from __future__ import annotations import os from dataclasses import dataclass from pathlib import Path @dataclass(frozen=True) class ConnectionSettings: host: str port: int user: str password: str login_prompt: str exit_command: str @dataclass(frozen=True) class ToolSettings: tool_mode: bool tool_spec: str sideload_specs: list[str] def load_env_file(path: str = ".env") -> None: """Populate ``os.environ`` with key/value pairs from a dotenv file.""" env_path = Path(path) if not env_path.exists(): return for line in env_path.read_text().splitlines(): stripped = line.strip() if not stripped or stripped.startswith("#"): continue if "=" not in stripped: continue key, value = stripped.split("=", 1) key = key.strip() value = value.strip().strip('"').strip("'") if key and key not in os.environ: os.environ[key] = value def require_env(key: str) -> str: value = os.environ.get(key) if value is None: raise RuntimeError(f"Missing required environment variable: {key}") return value def parse_bool(raw: str) -> bool: return raw.strip().lower() in {"1", "true", "yes", "on"} def parse_port(raw: str) -> int: try: return int(raw) except ValueError as exc: raise RuntimeError("MISTLE_PORT must be an integer") from exc def parse_csv(raw: str) -> list[str]: return [value.strip() for value in raw.split(",") if value.strip()] def read_connection_settings() -> ConnectionSettings: host = require_env("MISTLE_HOST") port = parse_port(require_env("MISTLE_PORT")) user = os.environ.get("MISTLE_USER", "") password = os.environ.get("MISTLE_PASSWORD", "") login_prompt = os.environ.get("MISTLE_LOGIN_PROMPT", "") exit_command = os.environ.get("MISTLE_EXIT_COMMAND", "") return ConnectionSettings( host=host, port=port, user=user, password=password, login_prompt=login_prompt, exit_command=exit_command, ) def read_tool_settings() -> ToolSettings: tool_mode = parse_bool(os.environ.get("MISTLE_TOOL_MODE", "")) tool_spec = os.environ.get("MISTLE_TOOL", "") sideload_specs = parse_csv(os.environ.get("MISTLE_SIDELOAD_TOOL", "")) return ToolSettings( tool_mode=tool_mode, tool_spec=tool_spec, sideload_specs=sideload_specs, )