Hermes-GUI: eingebaute hermes serve-Weboberflaeche same-origin in MC2 einbetten (ersetzt CLI-Terminal-Tab)

Statt des ttyd-CLI-Terminals zeigt der Tab jetzt die reiche eingebaute Hermes-Web-GUI
(hermes serve/dashboard: Threads, sichtbare Tool-Calls, Sessions, Config) - same-origin,
ohne offenen Port, ohne Login (Loopback :9119 = LAN-Trust wie Terminal/Konsole).

- backend/routers/hermes_ui.py: generischer Reverse-Proxy /hermes-ui/* (alle Methoden + beliebige
  WebSockets) -> 127.0.0.1:9119; injiziert window.__HERMES_BASE_PATH__=/hermes-ui in die index.html,
  damit die SPA ihre API/WS-Calls unter dem Praefix absetzt (sonst Kollision mit MC2s /api).
- deploy/hermes-builtin-ui.service: hermes serve --skip-build auf Loopback :9119.
- deploy/deploy.sh: baut das GUI-Bundle mit Vite-base=/hermes-ui/ (Temp->Swap, ueberlebt Hermes-
  Updates) + installiert/startet den Dienst.
- backend/app.py + config.py + services/agent.py: Router registriert, Upstream/Pfad + Status-Feld.
- frontend: Terminal-Tab -> "Hermes GUI" (TerminalView iframe /hermes-ui/, nav-Label, vite-Dev-Proxy).

E2E im Browser verifiziert: GUI rendert eingebettet, /hermes-ui/api/* liefern echte Daten
(289 Sessions, verbundene Plattformen), Assets + Plugins laden, Basis-Pfad korrekt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-07-06 14:48:12 +02:00
parent 93f447327b
commit 3a6e3c5432
13 changed files with 326 additions and 123 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -7,7 +7,7 @@
<link rel="manifest" href="/manifest.webmanifest" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<title>Mission Control 2.0</title>
<script type="module" crossorigin src="/assets/index-BZIxDFd6.js"></script>
<script type="module" crossorigin src="/assets/index-_1h7akwb.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DfNbRJ2k.css">
</head>
<body>
+2
View File
@@ -373,9 +373,11 @@ export interface AgentStatus {
gateway_url: string
terminal_url: string
box_console_url?: string
hermes_ui_url?: string
gateway_reachable: boolean
terminal_reachable: boolean
box_console_reachable?: boolean
hermes_ui_reachable?: boolean
home_exists: boolean
brain_model?: string
has_config: boolean
+2 -2
View File
@@ -4,7 +4,7 @@ import {
Brain,
Plug,
Bot,
TerminalSquare,
AppWindow,
SquareTerminal,
HelpCircle,
type LucideIcon,
@@ -26,7 +26,7 @@ export const NAV: NavItem[] = [
{ id: "memory", label: "Gedächtnis", hint: "Geteiltes Memory verwalten", icon: Brain },
{ id: "connect", label: "Verbinden", hint: "IDE-/Agent-Configs erzeugen", icon: Plug },
{ id: "agent", label: "Hermes", hint: "Agent-Status & Verdrahtung", icon: Bot },
{ id: "terminal", label: "Hermes-Terminal", hint: "Interaktives Hermes-Agent-Terminal", icon: TerminalSquare },
{ id: "terminal", label: "Hermes GUI", hint: "Eingebaute Hermes-Weboberfläche (Threads & Tool-Calls)", icon: AppWindow },
{ id: "konsole", label: "Konsole", hint: "Direkte Box-Shell (SSH-artig)", icon: SquareTerminal },
{ id: "guide", label: "Anleitung", hint: "Einrichten & Vibe-Coding", icon: HelpCircle },
]
+10 -10
View File
@@ -1,21 +1,21 @@
import { TerminalSquare, ExternalLink, AlertTriangle } from "lucide-react"
import { Bot, ExternalLink, AlertTriangle } from "lucide-react"
import { useAgentStatus } from "@/lib/queries"
import { resolveExternalUrl } from "@/lib/utils"
export function TerminalView() {
const { data: agent } = useAgentStatus(5_000)
const url = agent?.terminal_url ? resolveExternalUrl(agent.terminal_url) : undefined
const ok = agent?.terminal_reachable
const url = agent?.hermes_ui_url ? resolveExternalUrl(agent.hermes_ui_url) : undefined
const ok = agent?.hermes_ui_reachable
return (
<div className="flex h-full flex-col gap-4">
<div className="flex flex-wrap items-end justify-between gap-3">
<div>
<h1 className="bg-gradient-to-r from-foreground via-foreground to-primary bg-clip-text font-space text-2xl font-bold tracking-tight text-transparent">
Hermes Terminal
Hermes GUI
</h1>
<p className="flex items-center gap-2 text-sm text-muted-foreground">
Interaktiver Agent (<code className="rounded bg-background/40 px-1 font-mono text-[11px] text-primary">hermes chat</code>) mit Tools &amp; PC-Steuerung mehr als Chatten".
Die eingebaute Hermes-Weboberfläche Threads, sichtbare Tool-Calls &amp; Chat, direkt im Cockpit statt im Terminal.
</p>
</div>
<div className="flex items-center gap-3">
@@ -37,18 +37,18 @@ export function TerminalView() {
{ok === false && (
<div className="absolute inset-0 z-10 flex flex-col items-center justify-center gap-3 bg-black/70 text-center">
<AlertTriangle className="h-8 w-8 text-amber-400" />
<div className="text-sm font-semibold text-amber-300">Terminal nicht erreichbar</div>
<div className="text-sm font-semibold text-amber-300">Hermes-GUI nicht erreichbar</div>
<p className="max-w-sm text-[11px] leading-normal text-muted-foreground">
Der <code className="font-mono text-primary">hermes-terminal</code>-Dienst (ttyd) läuft nicht.
Auf der Box: <code className="font-mono">systemctl --user restart hermes-terminal</code>.
Der <code className="font-mono text-primary">hermes-builtin-ui</code>-Dienst (<code className="font-mono">hermes serve</code>) läuft nicht.
Auf der Box: <code className="font-mono">systemctl --user restart hermes-builtin-ui</code>.
</p>
</div>
)}
<iframe src={url} title="Hermes Terminal" className="h-full w-full border-0" style={{ minHeight: "68vh" }} />
<iframe src={url} title="Hermes GUI" className="h-full w-full border-0" style={{ minHeight: "68vh" }} />
</div>
) : (
<div className="flex min-h-[68vh] flex-1 items-center justify-center rounded-2xl border border-border/60 bg-background/20 text-xs text-muted-foreground">
<TerminalSquare className="mr-2 h-4 w-4" /> Lade Terminal
<Bot className="mr-2 h-4 w-4" /> Lade Hermes-GUI
</div>
)}
</div>
+2
View File
@@ -15,6 +15,8 @@ export default defineConfig({
// Web-Terminals (ttyd) laufen same-origin über den MC2-Reverse-Proxy (/<name>/ + /<name>/ws).
"/console": { target: process.env.MC_API_TARGET || "http://127.0.0.1:9000", changeOrigin: true, ws: true },
"/hermes-terminal": { target: process.env.MC_API_TARGET || "http://127.0.0.1:9000", changeOrigin: true, ws: true },
// Eingebaute Hermes-Web-GUI (hermes serve) — same-origin über den MC2-Reverse-Proxy, mit WS.
"/hermes-ui": { target: process.env.MC_API_TARGET || "http://127.0.0.1:9000", changeOrigin: true, ws: true },
},
},
build: { outDir: "dist" },