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 AuthoringStaleAfterSec int ProtoSocketPath string ProtoSocketHeartbeatIntervalSec int ProtoSocketHeartbeatWaitSec int GitoProtoSocketURL string GitoWebhookSecret string GitoRepoID string GitoBranch string GitoDevelopRepoPath string GitoRemoteName string RoadmapCreationTodoStateID string PlaneProjectID string ModelResponsesStream bool } // 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 != "" } // GitoWebhookConsumerEnabled reports whether the Gito HTTP webhook receiver // should be wired. Requires secret, repo id, develop repo path, and todo state // id. Branch defaults to "develop" and is not required. func (c Config) GitoWebhookConsumerEnabled() bool { return c.GitoWebhookSecret != "" && 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://toki-labs.com:18083/v1"), ModelAPIKey: os.Getenv("MODEL_API_KEY"), ModelName: getEnv("MODEL_NAME", "codex"), ModelContextSize: getEnvInt("MODEL_CONTEXT_SIZE", 0), ModelTimeoutSec: getEnvInt("MODEL_TIMEOUT_SEC", 900), 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), AuthoringStaleAfterSec: getEnvInt("AUTHORING_STALE_AFTER_SEC", 1200), 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"), GitoWebhookSecret: os.Getenv("GITO_WEBHOOK_SECRET"), 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"), PlaneProjectID: os.Getenv("PLANE_PROJECT_ID"), ModelResponsesStream: getEnvBool("MODEL_RESPONSES_STREAM", false), } } 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 } func getEnvBool(key string, fallback bool) bool { value := os.Getenv(key) if value == "" { return fallback } parsed, err := strconv.ParseBool(value) if err != nil { return fallback } return parsed }