package config import ( "os" "strconv" ) type Config struct { AppEnv string HTTPAddr string DatabaseURL string RedisURL string RedisKeyPrefix string AuthUsername string AuthPassword string ModelBaseURL string ModelAPIKey string ModelName string ModelContextSize int ModelTimeoutSec int A2AEdgeURL string A2AAgentURL string A2AToken string A2ATimeoutSec int MattermostBaseURL string MattermostToken string MattermostChannelID string PlaneBaseURL string PlaneToken string PlaneWebhookSecret string PlaneWorkspaceSlug string PlaneWorkspaceID string PlaneCreationBacklogStateID string PlaneAgentAssigneeID string PlaneSelfActorID string JiraBaseURL string JiraEmail string JiraAPIToken string WorkflowTaskTimeoutSec int ProtoSocketPath string ProtoSocketHeartbeatIntervalSec int ProtoSocketHeartbeatWaitSec int GitoProtoSocketURL string GitoRepoID string GitoBranch string GitoDevelopRepoPath string GitoRemoteName string RoadmapCreationTodoStateID string } // GitoBranchEventsEnabled reports whether the Gito branch event consumer should // run. It requires an endpoint, a repo id, a local develop checkout path to // scan against, and a Todo state id to project into. Any missing piece keeps the // consumer disabled so the rest of the Core server behaves exactly as before. func (c Config) GitoBranchEventsEnabled() bool { return c.GitoProtoSocketURL != "" && c.GitoRepoID != "" && c.GitoDevelopRepoPath != "" && c.RoadmapCreationTodoStateID != "" } func Load() Config { return Config{ AppEnv: getEnv("APP_ENV", "local"), HTTPAddr: getEnv("HTTP_ADDR", ":8080"), DatabaseURL: os.Getenv("DATABASE_URL"), RedisURL: os.Getenv("REDIS_URL"), RedisKeyPrefix: os.Getenv("REDIS_KEY_PREFIX"), AuthUsername: getEnv("AUTH_USERNAME", "nomadcode"), AuthPassword: os.Getenv("AUTH_PASSWORD"), ModelBaseURL: getEnv("MODEL_BASE_URL", "http://192.168.0.91:11434"), ModelAPIKey: getEnv("MODEL_API_KEY", "ollama"), ModelName: getEnv("MODEL_NAME", "qwen3.6:35b-a3b-bf16"), ModelContextSize: getEnvInt("MODEL_CONTEXT_SIZE", 262144), ModelTimeoutSec: getEnvInt("MODEL_TIMEOUT_SEC", 300), A2AEdgeURL: firstEnv("A2A_EDGE_URL", "A2A_AGENT_URL"), A2AAgentURL: firstEnv("A2A_AGENT_URL", "A2A_EDGE_URL"), A2AToken: os.Getenv("A2A_TOKEN"), A2ATimeoutSec: getEnvInt("A2A_TIMEOUT_SEC", 300), MattermostBaseURL: os.Getenv("MATTERMOST_BASE_URL"), MattermostToken: os.Getenv("MATTERMOST_TOKEN"), MattermostChannelID: os.Getenv("MATTERMOST_CHANNEL_ID"), PlaneBaseURL: os.Getenv("PLANE_BASE_URL"), PlaneToken: os.Getenv("PLANE_TOKEN"), PlaneWebhookSecret: os.Getenv("PLANE_WEBHOOK_SECRET"), PlaneWorkspaceSlug: os.Getenv("PLANE_WORKSPACE_SLUG"), PlaneWorkspaceID: os.Getenv("PLANE_WORKSPACE_ID"), PlaneCreationBacklogStateID: firstEnv("PLANE_CREATION_BACKLOG_STATE_ID", "PLANE_BACKLOG_STATE_ID"), PlaneAgentAssigneeID: os.Getenv("PLANE_AGENT_ASSIGNEE_ID"), PlaneSelfActorID: os.Getenv("PLANE_SELF_ACTOR_ID"), JiraBaseURL: os.Getenv("JIRA_BASE_URL"), JiraEmail: os.Getenv("JIRA_EMAIL"), JiraAPIToken: os.Getenv("JIRA_API_TOKEN"), WorkflowTaskTimeoutSec: getEnvInt("WORKFLOW_TASK_TIMEOUT_SEC", 300), ProtoSocketPath: getEnv("PROTO_SOCKET_PATH", "/proto-socket"), ProtoSocketHeartbeatIntervalSec: getEnvInt("PROTO_SOCKET_HEARTBEAT_INTERVAL_SEC", 30), ProtoSocketHeartbeatWaitSec: getEnvInt("PROTO_SOCKET_HEARTBEAT_WAIT_SEC", 10), GitoProtoSocketURL: os.Getenv("GITO_PROTO_SOCKET_URL"), GitoRepoID: os.Getenv("GITO_REPO_ID"), GitoBranch: getEnv("GITO_BRANCH", "develop"), GitoDevelopRepoPath: os.Getenv("GITO_DEVELOP_REPO_PATH"), GitoRemoteName: getEnv("GITO_REMOTE_NAME", "origin"), RoadmapCreationTodoStateID: firstEnv("ROADMAP_CREATION_TODO_STATE_ID", "PLANE_TODO_STATE_ID"), } } func getEnv(key, fallback string) string { value := os.Getenv(key) if value == "" { return fallback } return value } func firstEnv(keys ...string) string { for _, key := range keys { if value := os.Getenv(key); value != "" { return value } } return "" } func getEnvInt(key string, fallback int) int { value := os.Getenv(key) if value == "" { return fallback } parsed, err := strconv.Atoi(value) if err != nil { return fallback } return parsed }