"""Environment-based configuration for the service.""" from __future__ import annotations import os from dataclasses import dataclass, field from pathlib import Path def load_private_key() -> str: """Load private key from env var (raw PEM) or file path.""" if raw := os.environ.get("GITHUB_PRIVATE_KEY"): return raw key_path = os.environ.get("GITHUB_PRIVATE_KEY_PATH", "") if key_path: return Path(key_path).read_text() msg = "Set GITHUB_PRIVATE_KEY or GITHUB_PRIVATE_KEY_PATH" raise ValueError(msg) def default_plugin_dir() -> Path: """Default plugin dir is dist/ (assembled plugin) at the repo root.""" env = os.environ.get("PLUGIN_DIR") if env: return Path(env) return Path(__file__).resolve().parents[3] / "dist" @dataclass(frozen=True) class Config: """Immutable configuration loaded from environment variables.""" # GitHub App credentials app_id: int = field( default_factory=lambda: int(os.environ["GITHUB_APP_ID"]), ) private_key: str = field(default_factory=load_private_key) webhook_secret: str = field( default_factory=lambda: os.environ["GITHUB_WEBHOOK_SECRET"], ) # Claude CLI claude_cli: str = field( default_factory=lambda: os.environ.get("CLAUDE_CLI", "claude"), ) claude_model: str = field( default_factory=lambda: os.environ.get( "CLAUDE_MODEL", "us.anthropic.claude-sonnet-4-5-20250929-v1:0", ), ) plugin_dir: Path = field(default_factory=default_plugin_dir) # Codex CLI codex_cli: str = field( default_factory=lambda: os.environ.get("CODEX_CLI", "codex"), ) codex_model: str = field( default_factory=lambda: os.environ.get( "CODEX_MODEL", "gpt-5.4", ), ) # Backend selection lead_backend: str = field( default_factory=lambda: os.environ.get("LEAD_BACKEND", "claude"), ) # Server host: str = field( default_factory=lambda: os.environ.get("HOST", "0.0.0.0"), ) port: int = field( default_factory=lambda: int(os.environ.get("PORT", "8000")), ) # Repo workspace workspace_dir: Path = field( default_factory=lambda: Path( os.environ.get( "WORKSPACE_DIR", "/tmp/codeflash-workspaces", ), ), ) def cli_for_backend(self, name: str) -> str: """Return the CLI binary path for a backend name.""" return {"claude": self.claude_cli, "codex": self.codex_cli}[name] def model_for_backend(self, name: str) -> str: """Return the model name for a backend name.""" return {"claude": self.claude_model, "codex": self.codex_model}[name]