"""Cayce Ops — configuration. All configuration comes from environment variables prefixed with ``CAYCE_OPS_``. No config files, no defaults that touch the network. See .env.example for the full list with placeholder values. """ from __future__ import annotations import os from dataclasses import dataclass, field def _bool(name: str, default: bool = False) -> bool: raw = os.environ.get(name) if raw is None: return default return raw.strip().lower() in {"1", "true", "yes", "on"} def _int(name: str, default: int) -> int: raw = os.environ.get(name) if raw is None or not raw.strip(): return default return int(raw) def _list(name: str, default: list[str] | None = None) -> list[str]: raw = os.environ.get(name) if raw is None or not raw.strip(): return list(default or []) return [item.strip() for item in raw.split(",") if item.strip()] @dataclass(frozen=True) class Config: """Snapshot of the process environment. Constructed once per CLI invocation.""" # --- core --- database_url: str = field( default_factory=lambda: os.environ.get( "CAYCE_OPS_DATABASE_URL", "sqlite:///cayce-ops.db" ) ) autonomous_mode: bool = field( default_factory=lambda: _bool("CAYCE_OPS_AUTONOMOUS_MODE", False) ) log_level: str = field( default_factory=lambda: os.environ.get("CAYCE_OPS_LOG_LEVEL", "INFO") ) state_dir: str = field( default_factory=lambda: os.environ.get("CAYCE_OPS_STATE_DIR", ".") ) media_dir: str = field( default_factory=lambda: os.environ.get("CAYCE_OPS_MEDIA_DIR", "./media") ) # --- scanner --- scanner_sources: str = field( default_factory=lambda: os.environ.get("CAYCE_OPS_SCANNER_SOURCES", "[]") ) scanner_keywords: list[str] = field( default_factory=lambda: _list( "CAYCE_OPS_SCANNER_KEYWORDS", ["affiliate", "partnership", "bitcoin", "lightning", "travel"], ) ) scanner_user_agent: str = field( default_factory=lambda: os.environ.get( "CAYCE_OPS_SCANNER_USER_AGENT", "cayce-ops-scanner/0.1" ) ) # --- executor --- autonomous_kinds: list[str] = field( default_factory=lambda: _list("CAYCE_OPS_AUTONOMOUS_KINDS", []) ) autonomous_daily_cap: int = field( default_factory=lambda: _int("CAYCE_OPS_AUTONOMOUS_DAILY_CAP", 0) ) max_attempts: int = field( default_factory=lambda: _int("CAYCE_OPS_MAX_ATTEMPTS", 2) ) enable_http_actions: bool = field( default_factory=lambda: _bool("CAYCE_OPS_ENABLE_HTTP_ACTIONS", False) ) # --- mail --- imap_host: str = field( default_factory=lambda: os.environ.get("CAYCE_OPS_IMAP_HOST", "") ) imap_port: int = field(default_factory=lambda: _int("CAYCE_OPS_IMAP_PORT", 993)) imap_user: str = field( default_factory=lambda: os.environ.get("CAYCE_OPS_IMAP_USER", "") ) imap_password: str = field( default_factory=lambda: os.environ.get("CAYCE_OPS_IMAP_PASSWORD", "") ) imap_mailbox: str = field( default_factory=lambda: os.environ.get("CAYCE_OPS_IMAP_MAILBOX", "INBOX") ) smtp_host: str = field( default_factory=lambda: os.environ.get("CAYCE_OPS_SMTP_HOST", "") ) smtp_port: int = field(default_factory=lambda: _int("CAYCE_OPS_SMTP_PORT", 587)) smtp_user: str = field( default_factory=lambda: os.environ.get("CAYCE_OPS_SMTP_USER", "") ) smtp_password: str = field( default_factory=lambda: os.environ.get("CAYCE_OPS_SMTP_PASSWORD", "") ) mail_from: str = field( default_factory=lambda: os.environ.get("CAYCE_OPS_MAIL_FROM", "") ) # --- llm --- llm_provider: str = field( default_factory=lambda: os.environ.get("CAYCE_OPS_LLM_PROVIDER", "null") ) llm_model: str = field( default_factory=lambda: os.environ.get("CAYCE_OPS_LLM_MODEL", "") ) llm_base_url: str = field( default_factory=lambda: os.environ.get( "CAYCE_OPS_LLM_BASE_URL", "https://api.openai.com/v1" ) ) llm_api_key: str = field( default_factory=lambda: os.environ.get("CAYCE_OPS_LLM_API_KEY", "") ) llm_review_threshold: float = field( default_factory=lambda: float( os.environ.get("CAYCE_OPS_LLM_REVIEW_THRESHOLD", "0.7") ) ) # --- publisher / nostr --- nostr_private_key: str = field( default_factory=lambda: os.environ.get("CAYCE_OPS_NOSTR_PRIVATE_KEY", "") ) nostr_relays: list[str] = field( default_factory=lambda: _list("CAYCE_OPS_NOSTR_RELAYS", []) ) enable_facebook: bool = field( default_factory=lambda: _bool("CAYCE_OPS_ENABLE_FACEBOOK", False) ) facebook_page_token: str = field( default_factory=lambda: os.environ.get("CAYCE_OPS_FACEBOOK_PAGE_TOKEN", "") ) enable_instagram: bool = field( default_factory=lambda: _bool("CAYCE_OPS_ENABLE_INSTAGRAM", False) ) instagram_user_id: str = field( default_factory=lambda: os.environ.get("CAYCE_OPS_INSTAGRAM_USER_ID", "") ) # --- monitor --- monitor_output: str = field( default_factory=lambda: os.environ.get( "CAYCE_OPS_MONITOR_OUTPUT", "./status.html" ) ) def load() -> Config: """Build the process Config snapshot.""" return Config()