package config import ( "fmt" "io/ioutil" "os" "strings" "time" "gopkg.in/yaml.v2" ) // Config represents the complete configuration for a Bzzz agent type Config struct { HiveAPI HiveAPIConfig `yaml:"hive_api"` Agent AgentConfig `yaml:"agent"` GitHub GitHubConfig `yaml:"github"` P2P P2PConfig `yaml:"p2p"` Logging LoggingConfig `yaml:"logging"` HCFS HCFSConfig `yaml:"hcfs"` Slurp SlurpConfig `yaml:"slurp"` } // HiveAPIConfig holds Hive system integration settings type HiveAPIConfig struct { BaseURL string `yaml:"base_url"` APIKey string `yaml:"api_key"` Timeout time.Duration `yaml:"timeout"` RetryCount int `yaml:"retry_count"` } // CollaborationConfig holds role-based collaboration settings type CollaborationConfig struct { PreferredMessageTypes []string `yaml:"preferred_message_types"` AutoSubscribeToRoles []string `yaml:"auto_subscribe_to_roles"` AutoSubscribeToExpertise []string `yaml:"auto_subscribe_to_expertise"` ResponseTimeoutSeconds int `yaml:"response_timeout_seconds"` MaxCollaborationDepth int `yaml:"max_collaboration_depth"` EscalationThreshold int `yaml:"escalation_threshold"` CustomTopicSubscriptions []string `yaml:"custom_topic_subscriptions"` } // AgentConfig holds agent-specific configuration type AgentConfig struct { ID string `yaml:"id"` Capabilities []string `yaml:"capabilities"` PollInterval time.Duration `yaml:"poll_interval"` MaxTasks int `yaml:"max_tasks"` Models []string `yaml:"models"` Specialization string `yaml:"specialization"` ModelSelectionWebhook string `yaml:"model_selection_webhook"` DefaultReasoningModel string `yaml:"default_reasoning_model"` SandboxImage string `yaml:"sandbox_image"` // Role-based configuration from Bees-AgenticWorkers Role string `yaml:"role"` SystemPrompt string `yaml:"system_prompt"` ReportsTo []string `yaml:"reports_to"` Expertise []string `yaml:"expertise"` Deliverables []string `yaml:"deliverables"` // Role-based collaboration settings CollaborationSettings CollaborationConfig `yaml:"collaboration"` } // GitHubConfig holds GitHub integration settings type GitHubConfig struct { TokenFile string `yaml:"token_file"` UserAgent string `yaml:"user_agent"` Timeout time.Duration `yaml:"timeout"` RateLimit bool `yaml:"rate_limit"` Assignee string `yaml:"assignee"` } // P2PConfig holds P2P networking configuration type P2PConfig struct { ServiceTag string `yaml:"service_tag"` BzzzTopic string `yaml:"bzzz_topic"` HmmmTopic string `yaml:"hmmm_topic"` DiscoveryTimeout time.Duration `yaml:"discovery_timeout"` // Human escalation settings EscalationWebhook string `yaml:"escalation_webhook"` EscalationKeywords []string `yaml:"escalation_keywords"` ConversationLimit int `yaml:"conversation_limit"` } // LoggingConfig holds logging configuration type LoggingConfig struct { Level string `yaml:"level"` Format string `yaml:"format"` Output string `yaml:"output"` Structured bool `yaml:"structured"` } // HCFSConfig holds HCFS integration configuration type HCFSConfig struct { // API settings APIURL string `yaml:"api_url" json:"api_url"` APITimeout time.Duration `yaml:"api_timeout" json:"api_timeout"` // Workspace settings MountPath string `yaml:"mount_path" json:"mount_path"` WorkspaceTimeout time.Duration `yaml:"workspace_timeout" json:"workspace_timeout"` // FUSE settings FUSEEnabled bool `yaml:"fuse_enabled" json:"fuse_enabled"` FUSEMountPoint string `yaml:"fuse_mount_point" json:"fuse_mount_point"` // Cleanup settings IdleCleanupInterval time.Duration `yaml:"idle_cleanup_interval" json:"idle_cleanup_interval"` MaxIdleTime time.Duration `yaml:"max_idle_time" json:"max_idle_time"` // Storage settings StoreArtifacts bool `yaml:"store_artifacts" json:"store_artifacts"` CompressArtifacts bool `yaml:"compress_artifacts" json:"compress_artifacts"` // Enable/disable HCFS integration Enabled bool `yaml:"enabled" json:"enabled"` } // LoadConfig loads configuration from file, environment variables, and defaults func LoadConfig(configPath string) (*Config, error) { // Start with defaults config := getDefaultConfig() // Load from file if it exists if configPath != "" && fileExists(configPath) { if err := loadFromFile(config, configPath); err != nil { return nil, fmt.Errorf("failed to load config file: %w", err) } } // Override with environment variables if err := loadFromEnv(config); err != nil { return nil, fmt.Errorf("failed to load environment variables: %w", err) } // Validate configuration if err := validateConfig(config); err != nil { return nil, fmt.Errorf("invalid configuration: %w", err) } return config, nil } // getDefaultConfig returns the default configuration func getDefaultConfig() *Config { return &Config{ HiveAPI: HiveAPIConfig{ BaseURL: "https://hive.home.deepblack.cloud", Timeout: 30 * time.Second, RetryCount: 3, }, Agent: AgentConfig{ Capabilities: []string{"general", "reasoning", "task-coordination"}, PollInterval: 30 * time.Second, MaxTasks: 3, Models: []string{"phi3", "llama3.1"}, Specialization: "general_developer", ModelSelectionWebhook: "https://n8n.home.deepblack.cloud/webhook/model-selection", DefaultReasoningModel: "phi3", SandboxImage: "registry.home.deepblack.cloud/tony/bzzz-sandbox:latest", }, GitHub: GitHubConfig{ TokenFile: "/home/tony/chorus/business/secrets/gh-token", UserAgent: "Bzzz-P2P-Agent/1.0", Timeout: 30 * time.Second, RateLimit: true, Assignee: "anthonyrawlins", }, P2P: P2PConfig{ ServiceTag: "bzzz-peer-discovery", BzzzTopic: "bzzz/coordination/v1", HmmmTopic: "hmmm/meta-discussion/v1", DiscoveryTimeout: 10 * time.Second, EscalationWebhook: "https://n8n.home.deepblack.cloud/webhook-test/human-escalation", EscalationKeywords: []string{"stuck", "help", "human", "escalate", "clarification needed", "manual intervention"}, ConversationLimit: 10, }, Logging: LoggingConfig{ Level: "info", Format: "text", Output: "stdout", Structured: false, }, HCFS: HCFSConfig{ APIURL: "http://localhost:8000", APITimeout: 30 * time.Second, MountPath: "/tmp/hcfs-workspaces", WorkspaceTimeout: 2 * time.Hour, FUSEEnabled: false, FUSEMountPoint: "/mnt/hcfs", IdleCleanupInterval: 15 * time.Minute, MaxIdleTime: 1 * time.Hour, StoreArtifacts: true, CompressArtifacts: false, Enabled: true, }, Slurp: GetDefaultSlurpConfig(), } } // loadFromFile loads configuration from a YAML file func loadFromFile(config *Config, filePath string) error { data, err := ioutil.ReadFile(filePath) if err != nil { return fmt.Errorf("failed to read config file: %w", err) } if err := yaml.Unmarshal(data, config); err != nil { return fmt.Errorf("failed to parse YAML config: %w", err) } return nil } // loadFromEnv loads configuration from environment variables func loadFromEnv(config *Config) error { // Hive API configuration if url := os.Getenv("BZZZ_HIVE_API_URL"); url != "" { config.HiveAPI.BaseURL = url } if apiKey := os.Getenv("BZZZ_HIVE_API_KEY"); apiKey != "" { config.HiveAPI.APIKey = apiKey } // Agent configuration if agentID := os.Getenv("BZZZ_AGENT_ID"); agentID != "" { config.Agent.ID = agentID } if capabilities := os.Getenv("BZZZ_AGENT_CAPABILITIES"); capabilities != "" { config.Agent.Capabilities = strings.Split(capabilities, ",") } if specialization := os.Getenv("BZZZ_AGENT_SPECIALIZATION"); specialization != "" { config.Agent.Specialization = specialization } if modelWebhook := os.Getenv("BZZZ_MODEL_SELECTION_WEBHOOK"); modelWebhook != "" { config.Agent.ModelSelectionWebhook = modelWebhook } // GitHub configuration if tokenFile := os.Getenv("BZZZ_GITHUB_TOKEN_FILE"); tokenFile != "" { config.GitHub.TokenFile = tokenFile } // P2P configuration if webhook := os.Getenv("BZZZ_ESCALATION_WEBHOOK"); webhook != "" { config.P2P.EscalationWebhook = webhook } // Logging configuration if level := os.Getenv("BZZZ_LOG_LEVEL"); level != "" { config.Logging.Level = level } // SLURP configuration if slurpURL := os.Getenv("BZZZ_SLURP_URL"); slurpURL != "" { config.Slurp.BaseURL = slurpURL } if slurpKey := os.Getenv("BZZZ_SLURP_API_KEY"); slurpKey != "" { config.Slurp.APIKey = slurpKey } if slurpEnabled := os.Getenv("BZZZ_SLURP_ENABLED"); slurpEnabled == "true" { config.Slurp.Enabled = true } return nil } // validateConfig validates the configuration values func validateConfig(config *Config) error { // Validate required fields if config.HiveAPI.BaseURL == "" { return fmt.Errorf("hive_api.base_url is required") } // Note: Agent.ID can be empty - it will be auto-generated from node ID in main.go if len(config.Agent.Capabilities) == 0 { return fmt.Errorf("agent.capabilities cannot be empty") } if config.Agent.PollInterval <= 0 { return fmt.Errorf("agent.poll_interval must be positive") } if config.Agent.MaxTasks <= 0 { return fmt.Errorf("agent.max_tasks must be positive") } // Validate GitHub token file exists if specified if config.GitHub.TokenFile != "" && !fileExists(config.GitHub.TokenFile) { return fmt.Errorf("github token file does not exist: %s", config.GitHub.TokenFile) } // Validate SLURP configuration if err := ValidateSlurpConfig(config.Slurp); err != nil { return fmt.Errorf("slurp configuration invalid: %w", err) } return nil } // SaveConfig saves the configuration to a YAML file func SaveConfig(config *Config, filePath string) error { data, err := yaml.Marshal(config) if err != nil { return fmt.Errorf("failed to marshal config to YAML: %w", err) } if err := ioutil.WriteFile(filePath, data, 0644); err != nil { return fmt.Errorf("failed to write config file: %w", err) } return nil } // GetGitHubToken reads the GitHub token from the configured file func (c *Config) GetGitHubToken() (string, error) { if c.GitHub.TokenFile == "" { return "", fmt.Errorf("no GitHub token file configured") } tokenBytes, err := ioutil.ReadFile(c.GitHub.TokenFile) if err != nil { return "", fmt.Errorf("failed to read GitHub token: %w", err) } return strings.TrimSpace(string(tokenBytes)), nil } // fileExists checks if a file exists func fileExists(filePath string) bool { _, err := os.Stat(filePath) return err == nil } // GenerateDefaultConfigFile creates a default configuration file func GenerateDefaultConfigFile(filePath string) error { config := getDefaultConfig() return SaveConfig(config, filePath) }