111 lines
3.6 KiB
Python
111 lines
3.6 KiB
Python
|
|
"""
|
||
|
|
Configuration for ctxd — context daemon.
|
||
|
|
"""
|
||
|
|
import os
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
# Default home directory (~/.ctx) — overridable via CTXD_HOME env var
|
||
|
|
DEFAULT_HOME = Path(os.environ.get("CTXD_HOME", Path.home() / ".ctx"))
|
||
|
|
|
||
|
|
# Defaults for ctxd.yaml
|
||
|
|
DEFAULT_CONFIG = {
|
||
|
|
"server": {
|
||
|
|
"host": "0.0.0.0",
|
||
|
|
"port": 9091,
|
||
|
|
},
|
||
|
|
"snapshots": {
|
||
|
|
"min_keep": 5,
|
||
|
|
"max_keep": 25,
|
||
|
|
},
|
||
|
|
"auth": {
|
||
|
|
"enabled": False,
|
||
|
|
"api_key": "",
|
||
|
|
},
|
||
|
|
"seed": {
|
||
|
|
"admin_user": "admin",
|
||
|
|
"admin_display": "Administrator",
|
||
|
|
"service_user": "hermes-gateway",
|
||
|
|
"service_display": "Hermes Agent",
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
class CtxConfig:
|
||
|
|
"""Holds resolved paths and config for a ctxd runtime."""
|
||
|
|
|
||
|
|
def __init__(self, home: Path | str | None = None, config: dict | None = None):
|
||
|
|
resolved = Path(home) if home else DEFAULT_HOME
|
||
|
|
self.home = resolved.resolve()
|
||
|
|
self._cfg = config or dict(DEFAULT_CONFIG)
|
||
|
|
|
||
|
|
# ── Directory layout ──────────────────────────────────────────
|
||
|
|
@property
|
||
|
|
def db_path(self) -> Path:
|
||
|
|
return self.home / "ctxd.db"
|
||
|
|
|
||
|
|
@property
|
||
|
|
def snapshots_dir(self) -> Path:
|
||
|
|
return self.home / "snapshots"
|
||
|
|
|
||
|
|
@property
|
||
|
|
def projects_dir(self) -> Path:
|
||
|
|
return self.home / "projects"
|
||
|
|
|
||
|
|
@property
|
||
|
|
def user_dir(self) -> Path:
|
||
|
|
return self.home / "users"
|
||
|
|
|
||
|
|
@property
|
||
|
|
def config_path(self) -> Path:
|
||
|
|
return self.home / "ctxd.yaml"
|
||
|
|
|
||
|
|
# ── Config accessors ──────────────────────────────────────────
|
||
|
|
@property
|
||
|
|
def host(self) -> str:
|
||
|
|
return self._cfg.get("server", {}).get("host", "127.0.0.1")
|
||
|
|
|
||
|
|
@property
|
||
|
|
def port(self) -> int:
|
||
|
|
return self._cfg.get("server", {}).get("port", 9091)
|
||
|
|
|
||
|
|
@property
|
||
|
|
def min_snapshots(self) -> int:
|
||
|
|
return self._cfg.get("snapshots", {}).get("min_keep", 5)
|
||
|
|
|
||
|
|
@property
|
||
|
|
def max_snapshots(self) -> int:
|
||
|
|
return self._cfg.get("snapshots", {}).get("max_keep", 25)
|
||
|
|
|
||
|
|
# ── Auth ──────────────────────────────────────────────────────
|
||
|
|
@property
|
||
|
|
def auth_enabled(self) -> bool:
|
||
|
|
return self._cfg.get("auth", {}).get("enabled", False)
|
||
|
|
|
||
|
|
@property
|
||
|
|
def api_key(self) -> str:
|
||
|
|
return self._cfg.get("auth", {}).get("api_key", "")
|
||
|
|
|
||
|
|
# ── Bootstrap ─────────────────────────────────────────────────
|
||
|
|
def ensure_dirs(self):
|
||
|
|
"""Create all required directories if they don't exist."""
|
||
|
|
for d in [self.home, self.snapshots_dir, self.projects_dir, self.user_dir]:
|
||
|
|
d.mkdir(parents=True, exist_ok=True)
|
||
|
|
|
||
|
|
@classmethod
|
||
|
|
def from_home(cls, home: Path | str | None = None) -> "CtxConfig":
|
||
|
|
"""Load from ctxd.yaml if it exists, otherwise use defaults."""
|
||
|
|
home = Path(home).resolve() if home else DEFAULT_HOME
|
||
|
|
cfg_path = home / "ctxd.yaml"
|
||
|
|
if cfg_path.exists():
|
||
|
|
import yaml
|
||
|
|
with open(cfg_path) as f:
|
||
|
|
data = yaml.safe_load(f) or {}
|
||
|
|
return cls(home=str(home), config=data)
|
||
|
|
return cls(home=str(home))
|
||
|
|
|
||
|
|
def save(self):
|
||
|
|
"""Write config to ctxd.yaml."""
|
||
|
|
import yaml
|
||
|
|
self.ensure_dirs()
|
||
|
|
with open(self.config_path, "w") as f:
|
||
|
|
yaml.dump(self._cfg, f, default_flow_style=False, sort_keys=False)
|