package config import ( "github.com/spf13/viper" ) type NodeConfig struct { Transport TransportConf `mapstructure:"transport" yaml:"transport"` Logging LoggingConf `mapstructure:"logging" yaml:"logging"` Metrics MetricsConf `mapstructure:"metrics" yaml:"metrics"` } type EdgeConfig struct { Edge EdgeInfo `mapstructure:"edge" yaml:"edge"` Server EdgeServerConf `mapstructure:"server" yaml:"server"` OpenAI EdgeOpenAIConf `mapstructure:"openai" yaml:"openai"` A2A EdgeA2AConf `mapstructure:"a2a" yaml:"a2a"` TLS TLSConf `mapstructure:"tls" yaml:"tls"` Logging LoggingConf `mapstructure:"logging" yaml:"logging"` Metrics MetricsConf `mapstructure:"metrics" yaml:"metrics"` Console EdgeConsoleConf `mapstructure:"console" yaml:"console"` Nodes []NodeDefinition `mapstructure:"nodes" yaml:"nodes"` } // EdgeInfo carries this edge instance's stable identity for loading and logging. // It is not used for Control Plane registration. type EdgeInfo struct { ID string `mapstructure:"id" yaml:"id"` Name string `mapstructure:"name" yaml:"name"` } // NodeDefinition is the edge-side record for a pre-registered node. type NodeDefinition struct { ID string `mapstructure:"id" yaml:"id"` // stable node identity; if empty, a UUID v4 is auto-assigned (dev fallback only) Alias string `mapstructure:"alias" yaml:"alias"` Token string `mapstructure:"token" yaml:"token"` Adapters AdaptersConf `mapstructure:"adapters" yaml:"adapters"` Runtime RuntimeConf `mapstructure:"runtime" yaml:"runtime"` } type NodeInfo struct { ID string `mapstructure:"id" yaml:"id"` Name string `mapstructure:"name" yaml:"name"` } type EdgeServerConf struct { Listen string `mapstructure:"listen" yaml:"listen"` } type EdgeOpenAIConf struct { Enabled bool `mapstructure:"enabled" yaml:"enabled"` Listen string `mapstructure:"listen" yaml:"listen"` NodeRef string `mapstructure:"node" yaml:"node"` Adapter string `mapstructure:"adapter" yaml:"adapter"` Target string `mapstructure:"target" yaml:"target"` Models []string `mapstructure:"models" yaml:"models"` SessionID string `mapstructure:"session_id" yaml:"session_id"` TimeoutSec int `mapstructure:"timeout_sec" yaml:"timeout_sec"` StrictOutput bool `mapstructure:"strict_output" yaml:"strict_output"` StrictStreamBuffer bool `mapstructure:"strict_stream_buffer" yaml:"strict_stream_buffer"` } type EdgeA2AConf struct { Enabled bool `mapstructure:"enabled" yaml:"enabled"` Listen string `mapstructure:"listen" yaml:"listen"` Path string `mapstructure:"path" yaml:"path"` NodeRef string `mapstructure:"node" yaml:"node"` Adapter string `mapstructure:"adapter" yaml:"adapter"` Target string `mapstructure:"target" yaml:"target"` SessionID string `mapstructure:"session_id" yaml:"session_id"` TimeoutSec int `mapstructure:"timeout_sec" yaml:"timeout_sec"` BearerToken string `mapstructure:"bearer_token" yaml:"bearer_token"` } type EdgeConsoleConf struct { Adapter string `mapstructure:"adapter" yaml:"adapter"` Target string `mapstructure:"target" yaml:"target"` Agent string `mapstructure:"agent" yaml:"agent"` // legacy alias for target Model string `mapstructure:"model" yaml:"model"` // legacy alias for target SessionID string `mapstructure:"session_id" yaml:"session_id"` Background bool `mapstructure:"background" yaml:"background"` TimeoutSec int `mapstructure:"timeout_sec" yaml:"timeout_sec"` } func (c EdgeConsoleConf) ResolveTarget() string { if c.Target != "" { return c.Target } if c.Agent != "" { return c.Agent } return c.Model } // ResolveAgent is kept for callers still using the legacy console.agent name. func (c EdgeConsoleConf) ResolveAgent() string { return c.ResolveTarget() } type TransportConf struct { EdgeAddr string `mapstructure:"edge_addr" yaml:"edge_addr"` Token string `mapstructure:"token" yaml:"token"` } type TLSConf struct { Enabled bool `mapstructure:"enabled" yaml:"enabled"` Cert string `mapstructure:"cert" yaml:"cert"` Key string `mapstructure:"key" yaml:"key"` CA string `mapstructure:"ca" yaml:"ca"` } type RuntimeConf struct { Concurrency int `mapstructure:"concurrency" yaml:"concurrency"` WorkspaceRoot string `mapstructure:"workspace_root" yaml:"workspace_root"` } type SQLiteConf struct { DSN string `mapstructure:"dsn" yaml:"dsn"` } type LoggingConf struct { Level string `mapstructure:"level" yaml:"level"` Pretty bool `mapstructure:"pretty" yaml:"pretty"` } type MetricsConf struct { Port int `mapstructure:"port" yaml:"port"` } type AdaptersConf struct { Ollama OllamaConf `mapstructure:"ollama" yaml:"ollama"` Vllm VllmConf `mapstructure:"vllm" yaml:"vllm"` CLI CLIConf `mapstructure:"cli" yaml:"cli"` } type OllamaConf struct { Enabled bool `mapstructure:"enabled" yaml:"enabled"` BaseURL string `mapstructure:"base_url" yaml:"base_url"` ContextSize int `mapstructure:"context_size" yaml:"context_size"` } type VllmConf struct { Enabled bool `mapstructure:"enabled" yaml:"enabled"` Endpoint string `mapstructure:"endpoint" yaml:"endpoint"` } type CLIConf struct { Enabled bool `mapstructure:"enabled" yaml:"enabled"` Profiles map[string]CLIProfileConf `mapstructure:"profiles" yaml:"profiles"` } type CLIProfileConf struct { Command string `mapstructure:"command" yaml:"command"` Args []string `mapstructure:"args" yaml:"args"` Env []string `mapstructure:"env" yaml:"env"` Persistent bool `mapstructure:"persistent" yaml:"persistent"` Terminal bool `mapstructure:"terminal" yaml:"terminal"` ResponseIdleTimeoutMS int `mapstructure:"response_idle_timeout_ms" yaml:"response_idle_timeout_ms"` StartupIdleTimeoutMS int `mapstructure:"startup_idle_timeout_ms" yaml:"startup_idle_timeout_ms"` OutputFormat string `mapstructure:"output_format" yaml:"output_format"` CompletionMarker CompletionMarkerConf `mapstructure:"completion_marker" yaml:"completion_marker"` Mode string `mapstructure:"mode" yaml:"mode"` ResumeArgs []string `mapstructure:"resume_args" yaml:"resume_args"` } type CompletionMarkerConf struct { Line string `mapstructure:"line" yaml:"line"` Regex string `mapstructure:"regex" yaml:"regex"` } func (m CompletionMarkerConf) Empty() bool { return m.Line == "" && m.Regex == "" } func Load(cfgFile string) (*NodeConfig, error) { v := viper.New() v.SetConfigFile(cfgFile) setDefaults(v) if err := v.ReadInConfig(); err != nil { return nil, err } var cfg NodeConfig if err := v.Unmarshal(&cfg); err != nil { return nil, err } return &cfg, nil } func LoadEdge(cfgFile string) (*EdgeConfig, error) { v := viper.New() v.SetConfigFile(cfgFile) setEdgeDefaults(v) if err := v.ReadInConfig(); err != nil { return nil, err } var cfg EdgeConfig if err := v.Unmarshal(&cfg); err != nil { return nil, err } if !v.InConfig("console.target") { if v.InConfig("console.agent") { cfg.Console.Target = cfg.Console.Agent } else if v.InConfig("console.model") { cfg.Console.Target = cfg.Console.Model } } return &cfg, nil } func setDefaults(v *viper.Viper) { v.SetDefault("transport.edge_addr", "localhost:9090") v.SetDefault("logging.level", "info") v.SetDefault("metrics.port", 9091) } func setEdgeDefaults(v *viper.Viper) { v.SetDefault("server.listen", "0.0.0.0:9090") v.SetDefault("openai.enabled", false) v.SetDefault("openai.listen", "0.0.0.0:8080") v.SetDefault("openai.adapter", "ollama") v.SetDefault("openai.session_id", "openai") v.SetDefault("openai.timeout_sec", 120) v.SetDefault("openai.strict_output", true) v.SetDefault("openai.strict_stream_buffer", false) v.SetDefault("a2a.enabled", false) v.SetDefault("a2a.listen", "0.0.0.0:8081") v.SetDefault("a2a.path", "/a2a") v.SetDefault("a2a.adapter", "cli") v.SetDefault("a2a.session_id", "a2a") v.SetDefault("a2a.timeout_sec", 120) v.SetDefault("logging.level", "info") v.SetDefault("metrics.port", 9092) v.SetDefault("tls.enabled", false) v.SetDefault("console.adapter", "cli") v.SetDefault("console.target", "claude") v.SetDefault("console.session_id", "default") v.SetDefault("console.background", false) v.SetDefault("console.timeout_sec", 120) }