package config import ( "fmt" "os" "gopkg.in/yaml.v3" ) // Load reads and parses an agent config file from the given path. func Load(path string) (*AgentConfig, error) { data, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("read config %s: %w", path, err) } // Expand environment variables in the raw YAML bytes. expanded := os.ExpandEnv(string(data)) var cfg AgentConfig if err := yaml.Unmarshal([]byte(expanded), &cfg); err != nil { return nil, fmt.Errorf("parse config %s: %w", path, err) } if err := validate(&cfg); err != nil { return nil, fmt.Errorf("invalid config %s: %w", path, err) } return &cfg, nil } // LoadMeta reads only the `agent:` block from a config file without expanding // env vars or running full validation. Used by agentctl list to show all // agents regardless of whether their env vars are configured. func LoadMeta(path string) (*AgentConfig, error) { data, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("read config %s: %w", path, err) } var cfg AgentConfig if err := yaml.Unmarshal(data, &cfg); err != nil { return nil, fmt.Errorf("parse config %s: %w", path, err) } if cfg.Agent.ID == "" { return nil, fmt.Errorf("agent.id is required") } return &cfg, nil } // LoadSpecial reads and parses a special agent config file. // Special agents have no Matrix identity so validation is lighter. func LoadSpecial(path string) (*SpecialConfig, error) { data, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("read special config %s: %w", path, err) } expanded := os.ExpandEnv(string(data)) var cfg SpecialConfig if err := yaml.Unmarshal([]byte(expanded), &cfg); err != nil { return nil, fmt.Errorf("parse special config %s: %w", path, err) } if err := validateSpecial(&cfg); err != nil { return nil, fmt.Errorf("invalid special config %s: %w", path, err) } return &cfg, nil } // validateSpecial applies sanity checks for special agent configs. func validateSpecial(cfg *SpecialConfig) error { if cfg.Special.ID == "" { return fmt.Errorf("special.id is required") } if cfg.Special.Type == "" { return fmt.Errorf("special.type is required") } if cfg.LLM.Primary.Provider == "" { return fmt.Errorf("llm.primary.provider is required") } return nil } // validate applies basic sanity checks. func validate(cfg *AgentConfig) error { if cfg.Agent.ID == "" { return fmt.Errorf("agent.id is required") } if cfg.Matrix.Homeserver == "" { return fmt.Errorf("matrix.homeserver is required") } if cfg.Matrix.UserID == "" { return fmt.Errorf("matrix.user_id is required") } if cfg.LLM.Primary.Provider == "" { return fmt.Errorf("llm.primary.provider is required") } return nil }