This repository has been archived on 2026-07-22. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
mission-control/app.py
T
Hitonabi 09aabbb86e feat(v9): Phase 5 — Hermes-Web-Dashboard eingebettet, Eigenbau-Chat raus
Das Framework bringt eine fertige Web-UI mit (Chat mit Live-Tool-Aktivitaet,
Approval-Prompts, Settings, Sessions). Statt sie nachzubauen, betten wir sie ein:

- routers/hermes_ui.py: HTTP+WS-Reverse-Proxy auf das lokale Dashboard (:9119)
  unter /hermes-ui/ mit X-Forwarded-Prefix -> Dashboard rewritet Assets/Base-Path
  selbst, injiziert seinen Session-Token (kein zweiter Login). WS-Bruecke fuer
  pty/ws/pub/events.
- HermesPanel: Chat -> iframe auf /hermes-ui/; Eigenbau-Chat/Voice/WS entfernt.
  Cockpit + Setup bleiben. Loest damit Kontext-/Lern-/Tool-Sichtbarkeits-Themen,
  da die UI direkt mit dem Agent-Loop spricht (kein Proxy-Bug mehr).
- routers/hermes.py: Chat-WS + _proxy_chat entfernt (Cutover).
- config.py: HERMES_DASHBOARD_URL. requirements: websockets.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 16:47:52 +02:00

84 lines
2.9 KiB
Python

"""
Mission Control - eine schlanke Steuerzentrale fuer einen lokalen llama-swap Stack.
Dieser Einstieg haelt nur noch das Geruest zusammen: er baut die FastAPI-App,
haengt die Router ein und liefert das statische UI aus. Die eigentliche Logik
liegt nach Concern getrennt in:
- config.py Env-Vars / Konstanten
- auth.py optionale Token-Auth
- jobengine.py Hintergrund-Jobs mit Live-Log
- llamaswap.py Reden mit llama-swap + config.yaml lesen/schreiben
- routers/* ein Router je Bereich (models, jobs, maintenance, ...)
Bewusst KISS: kein Build-Schritt, kein Framework ueber FastAPI hinaus, keine DB.
Neue Bereiche kommen als routers/<bereich>.py + static/js/panels/<bereich>.js dazu.
"""
from pathlib import Path
from fastapi import FastAPI, HTTPException
from fastapi.responses import FileResponse, JSONResponse, Response
from fastapi.staticfiles import StaticFiles
from routers import jobs, hermes, hermes_ui, maintenance, memory, models, system, cookbook, integration, news
app = FastAPI(title="Mission Control")
@app.middleware("http")
async def _no_cache_static(request, call_next):
"""UI + statische Module immer revalidieren lassen (304 wenn unveraendert),
damit Aenderungen nach einem rsync sofort wirken und kein Stale-JS haengen bleibt."""
response = await call_next(request)
path = request.url.path
if path == "/" or path.startswith("/static"):
response.headers["Cache-Control"] = "no-cache"
return response
app.include_router(models.router)
app.include_router(jobs.router)
app.include_router(maintenance.router)
app.include_router(system.router)
app.include_router(cookbook.router)
app.include_router(integration.router)
app.include_router(news.router)
app.include_router(memory.router)
app.include_router(hermes.router)
app.include_router(hermes_ui.router)
_STATIC = Path(__file__).parent / "static"
@app.get("/")
def index():
return FileResponse(_STATIC / "index.html")
_FAVICON = (
b"<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'>"
b"<rect width='32' height='32' rx='7' fill='#0f1720'/>"
b"<circle cx='16' cy='16' r='8' fill='none' stroke='#2dd4bf' stroke-width='3'/>"
b"<circle cx='16' cy='16' r='2.5' fill='#2dd4bf'/></svg>"
)
@app.get("/favicon.ico")
def favicon():
return Response(content=_FAVICON, media_type="image/svg+xml")
app.mount("/static", StaticFiles(directory=_STATIC), name="static")
@app.exception_handler(HTTPException)
def _http_exc(_req, exc: HTTPException):
return JSONResponse(status_code=exc.status_code, content={"error": exc.detail})
@app.exception_handler(Exception)
def _any_exc(_req, exc: Exception):
"""Unerwartete Fehler als lesbare Meldung ans (vertrauenswuerdige LAN-)UI geben,
statt nur einen generischen 500 ohne Hinweis. Erleichtert Anfaengern die Diagnose."""
return JSONResponse(status_code=500, content={"error": str(exc) or exc.__class__.__name__})