112 lines
2.9 KiB
Go
112 lines
2.9 KiB
Go
// Package client implements the employee side: config loading, work-repo
|
|||
|
|
// pull, inventory, manifest and deployment into the OpenCode target.
|
||
|
|
package client
|
||
|
|
|
||
|
|
import (
|
||
|
|
"encoding/json"
|
||
|
|
"fmt"
|
||
|
|
"os"
|
||
|
|
"path/filepath"
|
||
|
|
"runtime"
|
||
|
|
)
|
||
|
|
|
||
|
|
// ClientConfig is the admin-protected fleet configuration for sync.
|
||
|
|
type ClientConfig struct {
|
||
|
|
RepoURL string `json:"repo_url"`
|
||
|
|
Ref string `json:"ref,omitempty"`
|
||
|
|
Token string `json:"token,omitempty"`
|
||
|
|
TokenUser string `json:"token_user,omitempty"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// ConfigEnv names the override environment variables.
|
||
|
|
const (
|
||
|
|
ConfigEnv = "AGENT_LIB_CONFIG"
|
||
|
|
StateDirEnv = "AGENT_LIB_STATE_DIR"
|
||
|
|
defaultConfig = `{
|
||
|
|
"repo_url": "",
|
||
|
|
"ref": "",
|
||
|
|
"token": "",
|
||
|
|
"token_user": ""
|
||
|
|
}`
|
||
|
|
)
|
||
|
|
|
||
|
|
// DefaultConfigPath returns the admin-protected config location per platform:
|
||
|
|
// ProgramData on Windows, /etc on Linux/macOS.
|
||
|
|
func DefaultConfigPath() string {
|
||
|
|
if runtime.GOOS == "windows" {
|
||
|
|
if pd := os.Getenv("ProgramData"); pd != "" {
|
||
|
|
return filepath.Join(pd, "agent-lib", "config.json")
|
||
|
|
}
|
||
|
|
return `C:\ProgramData\agent-lib\config.json`
|
||
|
|
}
|
||
|
|
return "/etc/agent-lib/config.json"
|
||
|
|
}
|
||
|
|
|
||
|
|
// LoadClientConfig reads the config from flagPath, the override env, or the
|
||
|
|
// platform default — in that order.
|
||
|
|
func LoadClientConfig(flagPath string) (*ClientConfig, error) {
|
||
|
|
path := flagPath
|
||
|
|
if path == "" {
|
||
|
|
path = os.Getenv(ConfigEnv)
|
||
|
|
}
|
||
|
|
if path == "" {
|
||
|
|
path = DefaultConfigPath()
|
||
|
|
}
|
||
|
|
data, err := os.ReadFile(path)
|
||
|
|
if err != nil {
|
||
|
|
return nil, fmt.Errorf("reading client config %s: %w", path, err)
|
||
|
|
}
|
||
|
|
var cfg ClientConfig
|
||
|
|
if err := json.Unmarshal(data, &cfg); err != nil {
|
||
|
|
return nil, fmt.Errorf("client config %s is not valid JSON: %w", path, err)
|
||
|
|
}
|
||
|
|
if cfg.RepoURL == "" {
|
||
|
|
return nil, fmt.Errorf("client config %s: repo_url is required", path)
|
||
|
|
}
|
||
|
|
return &cfg, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// StateDir returns the client state directory: the override env, then the
|
||
|
|
// platform default (LocalAppData on Windows, XDG state elsewhere).
|
||
|
|
func StateDir() (string, error) {
|
||
|
|
if d := os.Getenv(StateDirEnv); d != "" {
|
||
|
|
return d, nil
|
||
|
|
}
|
||
|
|
if runtime.GOOS == "windows" {
|
||
|
|
base := os.Getenv("LocalAppData")
|
||
|
|
if base == "" {
|
||
|
|
return "", fmt.Errorf("LocalAppData is not set")
|
||
|
|
}
|
||
|
|
return filepath.Join(base, "agent-lib"), nil
|
||
|
|
}
|
||
|
|
if xdg := os.Getenv("XDG_STATE_HOME"); xdg != "" {
|
||
|
|
return filepath.Join(xdg, "agent-lib"), nil
|
||
|
|
}
|
||
|
|
home, err := os.UserHomeDir()
|
||
|
|
if err != nil {
|
||
|
|
return "", err
|
||
|
|
}
|
||
|
|
return filepath.Join(home, ".local", "state", "agent-lib"), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// StatePaths bundles the well-known files inside the state dir.
|
||
|
|
type StatePaths struct {
|
||
|
|
Root string
|
||
|
|
CacheDir string
|
||
|
|
Manifest string
|
||
|
|
LogFile string
|
||
|
|
}
|
||
|
|
|
||
|
|
func ResolveStatePaths() (StatePaths, error) {
|
||
|
|
root, err := StateDir()
|
||
|
|
if err != nil {
|
||
|
|
return StatePaths{}, err
|
||
|
|
}
|
||
|
|
return StatePaths{
|
||
|
|
Root: root,
|
||
|
|
CacheDir: filepath.Join(root, "work-repo"),
|
||
|
|
Manifest: filepath.Join(root, "manifest.json"),
|
||
|
|
LogFile: filepath.Join(root, "sync.log"),
|
||
|
|
}, nil
|
||
|
|
}
|