feat(config): add XDG platform path helpers
Add ConfigDir, DataDir, CacheDir, DefaultDBPath, DefaultConfigPath to pkg/config — following XDG Base Directory spec on Linux, and the stdlib os.UserConfigDir / os.UserCacheDir platform conventions on macOS/Windows. All four directory helpers call MkdirAll(0700) so callers never need to pre-create directories. DefaultDBPath returns <DataDir>/picoclaw.db and DefaultConfigPath returns <ConfigDir>/config.json. Wire into cmd/picoclaw: - getConfigPath() now delegates to config.DefaultConfigPath() with a legacy fallback (~/.picoclaw/config.json) for systems where XDG fails - setupSecureBus() resolves secrets.json via config.ConfigDir() with the same fallback, removing the hard-coded os.UserHomeDir + join pattern Extracted and extended from Budgetsmith internal/infrastructure/config/paths.go.
This commit is contained in:
parent
0db51c3b63
commit
f4ec7103fe
2 changed files with 86 additions and 4 deletions
|
|
@ -1533,6 +1533,10 @@ func daemonStatus() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func getConfigPath() string {
|
func getConfigPath() string {
|
||||||
|
if p, err := config.DefaultConfigPath(); err == nil {
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
// Fallback: legacy ~/.picoclaw/config.json for systems where XDG resolution fails.
|
||||||
home, _ := os.UserHomeDir()
|
home, _ := os.UserHomeDir()
|
||||||
return filepath.Join(home, ".picoclaw", "config.json")
|
return filepath.Join(home, ".picoclaw", "config.json")
|
||||||
}
|
}
|
||||||
|
|
@ -1568,13 +1572,18 @@ func loadConfig() (*config.Config, error) {
|
||||||
// returns without attaching the bus — the agent continues in direct-execution mode.
|
// returns without attaching the bus — the agent continues in direct-execution mode.
|
||||||
// The returned closer must be called on shutdown when the bus is non-nil.
|
// The returned closer must be called on shutdown when the bus is non-nil.
|
||||||
func setupSecureBus(agentLoop *agent.AgentLoop) (closer func()) {
|
func setupSecureBus(agentLoop *agent.AgentLoop) (closer func()) {
|
||||||
home, err := os.UserHomeDir()
|
cfgDir, err := config.ConfigDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.WarnC("itr", "SecureBus: cannot determine home dir — running without ITR")
|
// Fallback to ~/.picoclaw if XDG resolution fails.
|
||||||
|
home, herr := os.UserHomeDir()
|
||||||
|
if herr != nil {
|
||||||
|
logger.WarnC("itr", "SecureBus: cannot determine config dir — running without ITR")
|
||||||
return func() {}
|
return func() {}
|
||||||
}
|
}
|
||||||
|
cfgDir = filepath.Join(home, ".picoclaw")
|
||||||
|
}
|
||||||
|
|
||||||
secretsPath := filepath.Join(home, ".picoclaw", "secrets.json")
|
secretsPath := filepath.Join(cfgDir, "secrets.json")
|
||||||
|
|
||||||
// Use NoopKeyring by default; EnvKeyring when PICOCLAW_MASTER_KEY is set.
|
// Use NoopKeyring by default; EnvKeyring when PICOCLAW_MASTER_KEY is set.
|
||||||
var keyring security.KeyringProvider
|
var keyring security.KeyringProvider
|
||||||
|
|
|
||||||
|
|
@ -602,3 +602,76 @@ func expandHome(path string) string {
|
||||||
}
|
}
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── XDG / platform path helpers ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
const appName = "picoclaw"
|
||||||
|
|
||||||
|
// ConfigDir returns the platform-appropriate user configuration directory for
|
||||||
|
// picoclaw, following XDG Base Directory spec on Linux
|
||||||
|
// (~/.config/picoclaw), Library/Application Support on macOS, and
|
||||||
|
// %AppData%\picoclaw on Windows. The directory is created if it does not exist.
|
||||||
|
func ConfigDir() (string, error) {
|
||||||
|
base, err := os.UserConfigDir()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("resolve user config dir: %w", err)
|
||||||
|
}
|
||||||
|
dir := filepath.Join(base, appName)
|
||||||
|
if err := os.MkdirAll(dir, 0o700); err != nil {
|
||||||
|
return "", fmt.Errorf("create config dir %q: %w", dir, err)
|
||||||
|
}
|
||||||
|
return dir, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DataDir returns the platform-appropriate user data directory for picoclaw.
|
||||||
|
// On Linux this is ~/.local/share/picoclaw (XDG_DATA_HOME); on macOS and
|
||||||
|
// Windows it falls back to the same base as ConfigDir. The directory is
|
||||||
|
// created if it does not exist.
|
||||||
|
func DataDir() (string, error) {
|
||||||
|
// XDG_DATA_HOME is Linux-standard; os.UserHomeDir gives us the root we need.
|
||||||
|
home, err := os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("resolve home dir: %w", err)
|
||||||
|
}
|
||||||
|
dir := filepath.Join(home, ".local", "share", appName)
|
||||||
|
if err := os.MkdirAll(dir, 0o700); err != nil {
|
||||||
|
return "", fmt.Errorf("create data dir %q: %w", dir, err)
|
||||||
|
}
|
||||||
|
return dir, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CacheDir returns the platform-appropriate user cache directory for picoclaw
|
||||||
|
// (XDG_CACHE_HOME on Linux → ~/.cache/picoclaw). The directory is created if
|
||||||
|
// it does not exist.
|
||||||
|
func CacheDir() (string, error) {
|
||||||
|
base, err := os.UserCacheDir()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("resolve user cache dir: %w", err)
|
||||||
|
}
|
||||||
|
dir := filepath.Join(base, appName)
|
||||||
|
if err := os.MkdirAll(dir, 0o700); err != nil {
|
||||||
|
return "", fmt.Errorf("create cache dir %q: %w", dir, err)
|
||||||
|
}
|
||||||
|
return dir, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultDBPath returns the canonical SQLite database path inside DataDir.
|
||||||
|
// Callers that want to override this should check for a CLI flag or the
|
||||||
|
// PICOCLAW_DB_PATH environment variable before falling back to this value.
|
||||||
|
func DefaultDBPath() (string, error) {
|
||||||
|
dataDir, err := DataDir()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return filepath.Join(dataDir, appName+".db"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultConfigPath returns the path to the primary JSON config file inside
|
||||||
|
// ConfigDir (picoclaw/config.json).
|
||||||
|
func DefaultConfigPath() (string, error) {
|
||||||
|
cfgDir, err := ConfigDir()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return filepath.Join(cfgDir, "config.json"), nil
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue