mirror of
https://github.com/hanzoai/agent.git
synced 2026-09-11 21:08:51 +00:00
- Add agent canvas with visual node editor, chat view, and collaboration cursors - Add real-time collaboration infrastructure (contexts, hooks, presence) - Add command palette, spaces, terminal panel, and side chat components - Add IAM auth middleware and route handlers for server - Add Hanzo branding (favicons, config, navigation updates) - Update AuthContext/AuthGuard with enhanced auth flow - Update Dockerfile and Makefile for control-plane builds - Rebrand UI references and update dependencies across web client
276 lines
13 KiB
Go
276 lines
13 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt" // Added for fmt.Errorf
|
|
"os" // Added for os.Stat, os.ReadFile
|
|
"path/filepath" // Added for filepath.Join
|
|
"strconv"
|
|
"time"
|
|
|
|
"gopkg.in/yaml.v3" // Added for yaml.Unmarshal
|
|
|
|
"github.com/hanzoai/agents/control-plane/internal/cloud"
|
|
"github.com/hanzoai/agents/control-plane/internal/storage"
|
|
)
|
|
|
|
// Config holds the entire configuration for the HanzoAgents server.
|
|
type Config struct {
|
|
HanzoAgents HanzoAgentsConfig `yaml:"hanzo-agents" mapstructure:"hanzo-agents"`
|
|
Features FeatureConfig `yaml:"features" mapstructure:"features"`
|
|
Storage StorageConfig `yaml:"storage" mapstructure:"storage"`
|
|
UI UIConfig `yaml:"ui" mapstructure:"ui"`
|
|
API APIConfig `yaml:"api" mapstructure:"api"`
|
|
Cloud CloudConfig `yaml:"cloud" mapstructure:"cloud"`
|
|
Billing BillingConfig `yaml:"billing" mapstructure:"billing"`
|
|
}
|
|
|
|
// BillingConfig holds prepaid credit billing configuration.
|
|
// Billing is always active — every execution is balance-gated and charged actual cost.
|
|
type BillingConfig struct {
|
|
CommerceURL string `yaml:"commerce_url" mapstructure:"commerce_url"` // BILLING_COMMERCE_URL
|
|
AdminToken string `yaml:"admin_token" mapstructure:"admin_token"` // BILLING_ADMIN_TOKEN
|
|
Currency string `yaml:"currency" mapstructure:"currency"` // default "usd"
|
|
}
|
|
|
|
// CloudConfig is an alias of the cloud package's configuration so callers can
|
|
// work with a single definition while keeping the canonical struct colocated
|
|
// with the implementation in the cloud package.
|
|
type CloudConfig = cloud.CloudConfig
|
|
|
|
// UIConfig holds configuration for the web UI.
|
|
type UIConfig struct {
|
|
Enabled bool `yaml:"enabled" mapstructure:"enabled"`
|
|
Mode string `yaml:"mode" mapstructure:"mode"` // "embedded", "dev", "separate"
|
|
SourcePath string `yaml:"source_path" mapstructure:"source_path"` // Path to UI source for building
|
|
DistPath string `yaml:"dist_path" mapstructure:"dist_path"` // Path to built UI assets for serving
|
|
DevPort int `yaml:"dev_port" mapstructure:"dev_port"` // Port for UI dev server
|
|
}
|
|
|
|
// HanzoAgentsConfig holds the core HanzoAgents server configuration.
|
|
type HanzoAgentsConfig struct {
|
|
Port int `yaml:"port"`
|
|
NodeHealth NodeHealthConfig `yaml:"node_health" mapstructure:"node_health"`
|
|
ExecutionCleanup ExecutionCleanupConfig `yaml:"execution_cleanup" mapstructure:"execution_cleanup"`
|
|
ExecutionQueue ExecutionQueueConfig `yaml:"execution_queue" mapstructure:"execution_queue"`
|
|
}
|
|
|
|
// NodeHealthConfig holds configuration for agent node health monitoring.
|
|
// Zero values are treated as "use default" — set explicitly to override.
|
|
type NodeHealthConfig struct {
|
|
CheckInterval time.Duration `yaml:"check_interval" mapstructure:"check_interval"` // How often to HTTP health check nodes (0 = default 10s)
|
|
CheckTimeout time.Duration `yaml:"check_timeout" mapstructure:"check_timeout"` // Timeout per HTTP health check (0 = default 5s)
|
|
ConsecutiveFailures int `yaml:"consecutive_failures" mapstructure:"consecutive_failures"` // Failures before marking inactive (0 = default 3; set 1 for instant)
|
|
RecoveryDebounce time.Duration `yaml:"recovery_debounce" mapstructure:"recovery_debounce"` // Wait before allowing inactive->active (0 = default 5s)
|
|
HeartbeatStaleThreshold time.Duration `yaml:"heartbeat_stale_threshold" mapstructure:"heartbeat_stale_threshold"` // Heartbeat age before marking stale (0 = default 60s)
|
|
}
|
|
|
|
// ExecutionCleanupConfig holds configuration for execution cleanup and garbage collection
|
|
type ExecutionCleanupConfig struct {
|
|
Enabled bool `yaml:"enabled" mapstructure:"enabled" default:"true"`
|
|
RetentionPeriod time.Duration `yaml:"retention_period" mapstructure:"retention_period" default:"24h"`
|
|
CleanupInterval time.Duration `yaml:"cleanup_interval" mapstructure:"cleanup_interval" default:"1h"`
|
|
BatchSize int `yaml:"batch_size" mapstructure:"batch_size" default:"100"`
|
|
PreserveRecentDuration time.Duration `yaml:"preserve_recent_duration" mapstructure:"preserve_recent_duration" default:"1h"`
|
|
StaleExecutionTimeout time.Duration `yaml:"stale_execution_timeout" mapstructure:"stale_execution_timeout" default:"30m"`
|
|
}
|
|
|
|
// ExecutionQueueConfig configures execution and webhook settings.
|
|
type ExecutionQueueConfig struct {
|
|
AgentCallTimeout time.Duration `yaml:"agent_call_timeout" mapstructure:"agent_call_timeout"`
|
|
WebhookTimeout time.Duration `yaml:"webhook_timeout" mapstructure:"webhook_timeout"`
|
|
WebhookMaxAttempts int `yaml:"webhook_max_attempts" mapstructure:"webhook_max_attempts"`
|
|
WebhookRetryBackoff time.Duration `yaml:"webhook_retry_backoff" mapstructure:"webhook_retry_backoff"`
|
|
WebhookMaxRetryBackoff time.Duration `yaml:"webhook_max_retry_backoff" mapstructure:"webhook_max_retry_backoff"`
|
|
}
|
|
|
|
// FeatureConfig holds configuration for enabling/disabling features.
|
|
type FeatureConfig struct {
|
|
DID DIDConfig `yaml:"did" mapstructure:"did"`
|
|
}
|
|
|
|
// DIDConfig holds configuration for DID identity system.
|
|
type DIDConfig struct {
|
|
Enabled bool `yaml:"enabled" mapstructure:"enabled" default:"true"`
|
|
Method string `yaml:"method" mapstructure:"method" default:"did:key"`
|
|
KeyAlgorithm string `yaml:"key_algorithm" mapstructure:"key_algorithm" default:"Ed25519"`
|
|
DerivationMethod string `yaml:"derivation_method" mapstructure:"derivation_method" default:"BIP32"`
|
|
KeyRotationDays int `yaml:"key_rotation_days" mapstructure:"key_rotation_days" default:"90"`
|
|
VCRequirements VCRequirements `yaml:"vc_requirements" mapstructure:"vc_requirements"`
|
|
Keystore KeystoreConfig `yaml:"keystore" mapstructure:"keystore"`
|
|
}
|
|
|
|
// VCRequirements holds VC generation requirements.
|
|
type VCRequirements struct {
|
|
RequireVCForRegistration bool `yaml:"require_vc_registration" mapstructure:"require_vc_registration" default:"true"`
|
|
RequireVCForExecution bool `yaml:"require_vc_execution" mapstructure:"require_vc_execution" default:"true"`
|
|
RequireVCForCrossAgent bool `yaml:"require_vc_cross_agent" mapstructure:"require_vc_cross_agent" default:"true"`
|
|
StoreInputOutput bool `yaml:"store_input_output" mapstructure:"store_input_output" default:"false"`
|
|
HashSensitiveData bool `yaml:"hash_sensitive_data" mapstructure:"hash_sensitive_data" default:"true"`
|
|
PersistExecutionVC bool `yaml:"persist_execution_vc" mapstructure:"persist_execution_vc" default:"true"`
|
|
StorageMode string `yaml:"storage_mode" mapstructure:"storage_mode" default:"inline"`
|
|
}
|
|
|
|
// KeystoreConfig holds keystore configuration.
|
|
type KeystoreConfig struct {
|
|
Type string `yaml:"type" mapstructure:"type" default:"local"`
|
|
Path string `yaml:"path" mapstructure:"path" default:"./data/keys"`
|
|
Encryption string `yaml:"encryption" mapstructure:"encryption" default:"AES-256-GCM"`
|
|
BackupEnabled bool `yaml:"backup_enabled" mapstructure:"backup_enabled" default:"true"`
|
|
BackupInterval string `yaml:"backup_interval" mapstructure:"backup_interval" default:"24h"`
|
|
}
|
|
|
|
// APIConfig holds configuration for API settings
|
|
type APIConfig struct {
|
|
CORS CORSConfig `yaml:"cors" mapstructure:"cors"`
|
|
Auth AuthConfig `yaml:"auth" mapstructure:"auth"`
|
|
}
|
|
|
|
// CORSConfig holds CORS configuration
|
|
type CORSConfig struct {
|
|
AllowedOrigins []string `yaml:"allowed_origins" mapstructure:"allowed_origins"`
|
|
AllowedMethods []string `yaml:"allowed_methods" mapstructure:"allowed_methods"`
|
|
AllowedHeaders []string `yaml:"allowed_headers" mapstructure:"allowed_headers"`
|
|
ExposedHeaders []string `yaml:"exposed_headers" mapstructure:"exposed_headers"`
|
|
AllowCredentials bool `yaml:"allow_credentials" mapstructure:"allow_credentials"`
|
|
}
|
|
|
|
// AuthConfig holds API authentication configuration.
|
|
type AuthConfig struct {
|
|
// APIKey is checked against headers or query params. Empty disables auth.
|
|
APIKey string `yaml:"api_key" mapstructure:"api_key"`
|
|
// SkipPaths allows bypassing auth for specific endpoints (e.g., health).
|
|
SkipPaths []string `yaml:"skip_paths" mapstructure:"skip_paths"`
|
|
|
|
// IAM (Casdoor) OAuth configuration
|
|
IAMEnabled bool `yaml:"iam_enabled" mapstructure:"iam_enabled"`
|
|
IAMEndpoint string `yaml:"iam_endpoint" mapstructure:"iam_endpoint"` // Internal endpoint (e.g., http://iam.hanzo.svc:8000)
|
|
IAMPublicEndpoint string `yaml:"iam_public_endpoint" mapstructure:"iam_public_endpoint"` // External endpoint (e.g., https://hanzo.id)
|
|
IAMClientID string `yaml:"iam_client_id" mapstructure:"iam_client_id"`
|
|
IAMClientSecret string `yaml:"iam_client_secret" mapstructure:"iam_client_secret"`
|
|
IAMOrganization string `yaml:"iam_organization" mapstructure:"iam_organization"`
|
|
IAMApplication string `yaml:"iam_application" mapstructure:"iam_application"`
|
|
}
|
|
|
|
// StorageConfig is an alias of the storage layer's configuration so callers can
|
|
// work with a single definition while keeping the canonical struct colocated
|
|
// with the implementation in the storage package.
|
|
type StorageConfig = storage.StorageConfig
|
|
|
|
// DefaultConfigPath is the default path for the af configuration file.
|
|
const DefaultConfigPath = "hanzo-agents.yaml" // Or "./hanzo-agents.yaml", "config/hanzo-agents.yaml" depending on convention
|
|
|
|
// LoadConfig reads the configuration from the given path or default paths.
|
|
func LoadConfig(configPath string) (*Config, error) {
|
|
if configPath == "" {
|
|
configPath = DefaultConfigPath
|
|
}
|
|
|
|
// Check if the specific path exists
|
|
if _, err := os.Stat(configPath); os.IsNotExist(err) {
|
|
// Fallback: try to find it in common locations relative to executable or CWD
|
|
// This part might need more sophisticated logic depending on project structure
|
|
// For now, let's assume configPath is either absolute or relative to CWD.
|
|
// If not found, try a common "config/" subdirectory
|
|
altPath := filepath.Join("config", "hanzo-agents.yaml")
|
|
if _, err2 := os.Stat(altPath); err2 == nil {
|
|
configPath = altPath
|
|
} else {
|
|
// If still not found, return the original error for the specified/default path
|
|
return nil, fmt.Errorf("configuration file not found at %s or default locations: %w", configPath, err)
|
|
}
|
|
}
|
|
|
|
data, err := os.ReadFile(configPath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read configuration file %s: %w", configPath, err)
|
|
}
|
|
|
|
var cfg Config
|
|
if err := yaml.Unmarshal(data, &cfg); err != nil {
|
|
return nil, fmt.Errorf("failed to parse configuration file %s: %w", configPath, err)
|
|
}
|
|
|
|
// Apply environment variable overrides
|
|
applyEnvOverrides(&cfg)
|
|
|
|
// Apply cloud config defaults and env overrides
|
|
cfg.Cloud.Defaults()
|
|
cfg.Cloud.ApplyEnvOverrides()
|
|
|
|
return &cfg, nil
|
|
}
|
|
|
|
// applyEnvOverrides applies environment variable overrides to the config.
|
|
// Environment variables take precedence over YAML config values.
|
|
func applyEnvOverrides(cfg *Config) {
|
|
// API Authentication
|
|
if apiKey := os.Getenv("HANZO_AGENTS_API_KEY"); apiKey != "" {
|
|
cfg.API.Auth.APIKey = apiKey
|
|
}
|
|
// Also support the nested path format for consistency
|
|
if apiKey := os.Getenv("HANZO_AGENTS_API_AUTH_API_KEY"); apiKey != "" {
|
|
cfg.API.Auth.APIKey = apiKey
|
|
}
|
|
|
|
// IAM Authentication overrides
|
|
if val := os.Getenv("HANZO_AGENTS_IAM_ENABLED"); val != "" {
|
|
cfg.API.Auth.IAMEnabled = val == "true" || val == "1"
|
|
}
|
|
if val := os.Getenv("HANZO_AGENTS_IAM_ENDPOINT"); val != "" {
|
|
cfg.API.Auth.IAMEndpoint = val
|
|
}
|
|
if val := os.Getenv("HANZO_AGENTS_IAM_PUBLIC_ENDPOINT"); val != "" {
|
|
cfg.API.Auth.IAMPublicEndpoint = val
|
|
}
|
|
if val := os.Getenv("HANZO_AGENTS_IAM_CLIENT_ID"); val != "" {
|
|
cfg.API.Auth.IAMClientID = val
|
|
}
|
|
if val := os.Getenv("HANZO_AGENTS_IAM_CLIENT_SECRET"); val != "" {
|
|
cfg.API.Auth.IAMClientSecret = val
|
|
}
|
|
if val := os.Getenv("HANZO_AGENTS_IAM_ORGANIZATION"); val != "" {
|
|
cfg.API.Auth.IAMOrganization = val
|
|
}
|
|
if val := os.Getenv("HANZO_AGENTS_IAM_APPLICATION"); val != "" {
|
|
cfg.API.Auth.IAMApplication = val
|
|
}
|
|
|
|
// Node health monitoring overrides
|
|
if val := os.Getenv("HANZO_AGENTS_HEALTH_CHECK_INTERVAL"); val != "" {
|
|
if d, err := time.ParseDuration(val); err == nil {
|
|
cfg.HanzoAgents.NodeHealth.CheckInterval = d
|
|
}
|
|
}
|
|
if val := os.Getenv("HANZO_AGENTS_HEALTH_CHECK_TIMEOUT"); val != "" {
|
|
if d, err := time.ParseDuration(val); err == nil {
|
|
cfg.HanzoAgents.NodeHealth.CheckTimeout = d
|
|
}
|
|
}
|
|
if val := os.Getenv("HANZO_AGENTS_HEALTH_CONSECUTIVE_FAILURES"); val != "" {
|
|
if i, err := strconv.Atoi(val); err == nil {
|
|
cfg.HanzoAgents.NodeHealth.ConsecutiveFailures = i
|
|
}
|
|
}
|
|
if val := os.Getenv("HANZO_AGENTS_HEALTH_RECOVERY_DEBOUNCE"); val != "" {
|
|
if d, err := time.ParseDuration(val); err == nil {
|
|
cfg.HanzoAgents.NodeHealth.RecoveryDebounce = d
|
|
}
|
|
}
|
|
if val := os.Getenv("HANZO_AGENTS_HEARTBEAT_STALE_THRESHOLD"); val != "" {
|
|
if d, err := time.ParseDuration(val); err == nil {
|
|
cfg.HanzoAgents.NodeHealth.HeartbeatStaleThreshold = d
|
|
}
|
|
}
|
|
|
|
// Billing overrides
|
|
if val := os.Getenv("BILLING_COMMERCE_URL"); val != "" {
|
|
cfg.Billing.CommerceURL = val
|
|
}
|
|
if val := os.Getenv("BILLING_ADMIN_TOKEN"); val != "" {
|
|
cfg.Billing.AdminToken = val
|
|
}
|
|
if val := os.Getenv("BILLING_CURRENCY"); val != "" {
|
|
cfg.Billing.Currency = val
|
|
}
|
|
}
|