"""svrnty-hermes-webui-plugin — entry point. Called by hermes-webui's plugin loader at startup (after the env var HERMES_WEBUI_PYTHON_PLUGIN points the loader at this module). The loader passes a single `api` argument exposing 6 methods (see CLAUDE.md + protocol PRD §5.1). This module's job is to wire every route + static dir + asset injection that defines the Svrnty surface on hermes-webui. Keep this file thin. Route logic lives in `routes/.py`. Static lives in `static/`. The map of every upstream dependency is in CONNECTION-MAP.md (AST-generated by scripts/ast-connection-map.py). """ import os from pathlib import Path # Static + asset URL prefix (per protocol §12, decision Q5: /plugins/svrnty/) STATIC_PREFIX = "svrnty" STATIC_DIR = Path(__file__).resolve().parent / "static" def register(api): """Wire every Svrnty modification to hermes-webui. `api` is the loader-provided extension surface (6 methods). Treat it as the ONLY public contract. Touching anything else in hermes-webui requires a `CONNECTION-MAP.md` forced-internal entry with justification. """ log = api.logger("svrnty.plugin") log.info("svrnty-hermes-webui-plugin: registering") # Brand skin: serve static dir + inject CSS/JS into every page load. if STATIC_DIR.exists(): api.register_static(STATIC_PREFIX, str(STATIC_DIR)) api.inject_stylesheet(f"/plugins/{STATIC_PREFIX}/app.css") api.inject_script(f"/plugins/{STATIC_PREFIX}/app.js") # Sidebar nav glue — MUST load before tool panel scripts so its # switchPanel wrap is in place when panels listen for it. api.inject_script(f"/plugins/{STATIC_PREFIX}/svrnty_nav.js") # Adwright tool panel — content mounts into
, visibility keyed # off main.svrnty-showing-adwright (set by svrnty_nav.js wrapper). api.inject_stylesheet(f"/plugins/{STATIC_PREFIX}/adwright.css") api.inject_script(f"/plugins/{STATIC_PREFIX}/adwright.js") # BTE Command Center panel — same pattern (main.svrnty-showing-bte). api.inject_stylesheet(f"/plugins/{STATIC_PREFIX}/bte.css") api.inject_script(f"/plugins/{STATIC_PREFIX}/bte.js") # Sovereign Canvas panel — Stitch-like live design command surface. api.inject_stylesheet(f"/plugins/{STATIC_PREFIX}/canvas.css") api.inject_script(f"/plugins/{STATIC_PREFIX}/canvas.js") # Inline Umbrella graph for the Hermes Workspace right panel. api.inject_stylesheet(f"/plugins/{STATIC_PREFIX}/umbrella_inline.css") api.inject_script(f"/plugins/{STATIC_PREFIX}/umbrella_inline.js") # Cortex OS Runtime Health slice: read-only route status display. api.inject_stylesheet(f"/plugins/{STATIC_PREFIX}/cortex-os/runtime-health/runtime_health.css") api.inject_script(f"/plugins/{STATIC_PREFIX}/cortex-os/runtime-health/runtime_health.js") log.info("static + assets wired at /plugins/%s/", STATIC_PREFIX) # Routes — each feature lives in its own module under routes/. # Phase 2 will populate these. Import-and-register pattern; failures are # logged but don't take down the rest of the plugin. for route_module in _phase2_routes(): try: mod = __import__(f"routes.{route_module}", fromlist=["register"]) mod.register(api) log.info("route module loaded: %s", route_module) except ImportError as e: log.warning("route module %s not yet implemented (Phase 2): %s", route_module, e) except Exception as e: log.error("route module %s failed to register: %s", route_module, e) log.info("svrnty-hermes-webui-plugin: registration complete") def _phase2_routes(): """Routes to attempt loading. Returns module names under routes/. Phase 2 migrates the existing fork commits into these modules. Until then, ImportError is logged + swallowed so the plugin loads cleanly. """ return [ "transcribe", # P2.A — STT + voice-message audio processor ✓ "vault_status", # P2.B — vault connections status ✓ "adwright", # P2.C — Adwright tool panel routes (PRD §5+§6) ✓ "bte_proxy", # P2.D — BTE Command Center same-origin proxy (PRD §3) ✓ "canvas", # Sovereign Stitch-like design canvas proxy + event seed "umbrella", # P2.E — cortex-os umbrella graph viz (UMBRELLA-VIZ-PRD) ✓ "cortex_os_runtime_health", # S23.0-I4 — Cortex OS Runtime Health read-only slice ✓ ]