# scripts/lib/paths.py """Path resolution. Single source of truth — other modules import from here.""" from __future__ import annotations import json import os from pathlib import Path def default_paths_json_path() -> Path: """Return the default location of paths.json.""" if os.name == "nt": # Windows: %USERPROFILE%\.config\me-hub\paths.json return Path(os.environ["USERPROFILE"]) / ".config" / "me-hub" / "paths.json" return Path.home() / ".config" / "me-hub" / "paths.json" _PATHS_JSON_PATH: Path = default_paths_json_path() def repo_root() -> Path: """Return the repo root by walking up from cwd looking for pyproject.toml.""" p = Path.cwd().resolve() for candidate in [p, *p.parents]: if (candidate / "pyproject.toml").is_file(): return candidate raise RuntimeError(f"could not locate repo root from {p}") def load_paths_json() -> dict: """Load and return ~/.config/me-hub/paths.json as a dict.""" if not _PATHS_JSON_PATH.is_file(): raise FileNotFoundError( f"paths.json not found at {_PATHS_JSON_PATH}. " "Run ./bootstrap.{ps1,sh} to configure this machine." ) return json.loads(_PATHS_JSON_PATH.read_text()) def declared_role() -> str: """Return the declared role: 'maintainer' or 'faculty'.""" role = load_paths_json().get("declared_role") if role not in ("maintainer", "faculty"): raise ValueError(f"declared_role must be 'maintainer' or 'faculty', got {role!r}") return role def box_togo_path() -> Path | None: """Return the Box mirror path or None if not configured (faculty mode).""" raw = load_paths_json().get("box_togo_path") return Path(raw) if raw else None def tolk_technology_path() -> Path | None: """Return the tolk-technology repo path or None if not configured.""" raw = load_paths_json().get("tolk_technology_path") return Path(raw) if raw else None def ferpa_data_root() -> Path: """Return FERPA_DATA_ROOT (always configured).""" raw = load_paths_json().get("ferpa_data_root") if not raw: raise ValueError("ferpa_data_root not configured in paths.json") return Path(raw) def openai_api_key() -> str | None: """Read OPENAI_API_KEY from env. Returns None if unset. A per-machine key file fallback is supported but requires explicit opt-in via the build_derived --accept-key-file-risk flag; this reader does NOT read the file by default to avoid surprising the user. """ val = os.environ.get("OPENAI_API_KEY") return val if val else None