package config import ( "fmt" "os" "strconv" "strings" ) // Config represents the complete CHORUS configuration loaded from environment variables type Config struct { Agent AgentConfig `yaml:"agent"` Network NetworkConfig `yaml:"network"` License LicenseConfig `yaml:"license"` AI AIConfig `yaml:"ai"` Logging LoggingConfig `yaml:"logging"` } // AgentConfig defines agent-specific settings type AgentConfig struct { ID string `yaml:"id"` Specialization string `yaml:"specialization"` MaxTasks int `yaml:"max_tasks"` Capabilities []string `yaml:"capabilities"` } // NetworkConfig defines network and API settings type NetworkConfig struct { P2PPort int `yaml:"p2p_port"` APIPort int `yaml:"api_port"` HealthPort int `yaml:"health_port"` BindAddr string `yaml:"bind_address"` } // LicenseConfig defines licensing settings type LicenseConfig struct { Email string `yaml:"email"` LicenseKey string `yaml:"license_key"` ClusterID string `yaml:"cluster_id"` } // AIConfig defines AI service settings type AIConfig struct { OllamaEndpoint string `yaml:"ollama_endpoint"` DefaultModel string `yaml:"default_model"` } // LoggingConfig defines logging settings type LoggingConfig struct { Level string `yaml:"level"` Format string `yaml:"format"` } // LoadFromEnvironment loads configuration from environment variables // This is the primary configuration method for CHORUS (no config files) func LoadFromEnvironment() (*Config, error) { cfg := &Config{ Agent: AgentConfig{ ID: getEnvOrDefault("CHORUS_AGENT_ID", ""), Specialization: getEnvOrDefault("CHORUS_SPECIALIZATION", "general_developer"), MaxTasks: getEnvIntOrDefault("CHORUS_MAX_TASKS", 3), Capabilities: getEnvArrayOrDefault("CHORUS_CAPABILITIES", []string{"general_development", "task_coordination"}), }, Network: NetworkConfig{ P2PPort: getEnvIntOrDefault("CHORUS_P2P_PORT", 9000), APIPort: getEnvIntOrDefault("CHORUS_API_PORT", 8080), HealthPort: getEnvIntOrDefault("CHORUS_HEALTH_PORT", 8081), BindAddr: getEnvOrDefault("CHORUS_BIND_ADDRESS", "0.0.0.0"), }, License: LicenseConfig{ Email: os.Getenv("CHORUS_LICENSE_EMAIL"), LicenseKey: os.Getenv("CHORUS_LICENSE_KEY"), ClusterID: getEnvOrDefault("CHORUS_CLUSTER_ID", "default-cluster"), }, AI: AIConfig{ OllamaEndpoint: getEnvOrDefault("OLLAMA_ENDPOINT", "http://localhost:11434"), DefaultModel: getEnvOrDefault("CHORUS_DEFAULT_MODEL", "llama3.1:8b"), }, Logging: LoggingConfig{ Level: getEnvOrDefault("LOG_LEVEL", "info"), Format: getEnvOrDefault("LOG_FORMAT", "structured"), }, } // Validate required configuration if err := cfg.Validate(); err != nil { return nil, fmt.Errorf("configuration validation failed: %w", err) } return cfg, nil } // Validate ensures all required configuration is present func (c *Config) Validate() error { if c.License.Email == "" { return fmt.Errorf("CHORUS_LICENSE_EMAIL is required") } if c.License.LicenseKey == "" { return fmt.Errorf("CHORUS_LICENSE_KEY is required") } if c.Agent.ID == "" { // Auto-generate agent ID if not provided hostname, _ := os.Hostname() containerID := os.Getenv("HOSTNAME") // Docker sets this to container ID if containerID != "" && containerID != hostname { c.Agent.ID = fmt.Sprintf("chorus-%s", containerID[:12]) } else { c.Agent.ID = fmt.Sprintf("chorus-%s", hostname) } } return nil } // Helper functions for environment variable parsing func getEnvOrDefault(key, defaultValue string) string { if value := os.Getenv(key); value != "" { return value } return defaultValue } func getEnvIntOrDefault(key string, defaultValue int) int { if value := os.Getenv(key); value != "" { if parsed, err := strconv.Atoi(value); err == nil { return parsed } } return defaultValue } func getEnvArrayOrDefault(key string, defaultValue []string) []string { if value := os.Getenv(key); value != "" { return strings.Split(value, ",") } return defaultValue }