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:
@@ -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
@@ -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 },
|
||||
]
|
||||
|
||||
@@ -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 & PC-Steuerung — „mehr als Chatten".
|
||||
Die eingebaute Hermes-Weboberfläche — Threads, sichtbare Tool-Calls & 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>
|
||||
|
||||
Reference in New Issue
Block a user