"""Launcher for Odysseus (https://github.com/pewdiepie-archdaemon/odysseus). Odysseus is a self-hosted FastAPI/uvicorn app configured via a `.env` file and its in-app Settings UI (SQLite-backed) — not a CLI gateway with flags. This launcher seeds `.env` from odysseus.json on first boot (without touching an existing one, so in-app Settings changes persist across restarts), runs the one-time setup script, then execs uvicorn so supervisord can supervise it directly. """ import json import os import subprocess import sys from pathlib import Path CONFIG_PATH = Path(os.environ.get("ODYSSEUS_CONFIG_PATH", "/app/odysseus.json")) APP_DIR = Path(os.environ.get("ODYSSEUS_APP_DIR", "/app/odysseus")) DEFAULT_PORT = os.environ.get("ODYSSEUS_PORT", "18796") def _load_config() -> dict: try: return json.loads(CONFIG_PATH.read_text(encoding="utf-8")) except Exception: return {} def _seed_env_file(config: dict, port: str) -> None: env_path = APP_DIR / ".env" if env_path.exists(): return env_vars = dict(config.get("env", {})) env_vars.setdefault("APP_BIND", "127.0.0.1") env_vars.setdefault("APP_PORT", port) lines = [f"{key}={value}" for key, value in env_vars.items()] env_path.write_text("\n".join(lines) + "\n", encoding="utf-8") def main() -> None: config = _load_config() port = str(config.get("gateway", {}).get("port") or DEFAULT_PORT) _seed_env_file(config, port) os.chdir(APP_DIR) subprocess.run([sys.executable, "setup.py"], check=False) os.execvp( sys.executable, [sys.executable, "-m", "uvicorn", "app:app", "--host", "127.0.0.1", "--port", port], ) if __name__ == "__main__": main()