feat(2.0): Phase 4 — Hermes-Schicht (MC-Seite + Runbook)

MC-Seite: services/agent.py + routers/agent.py (GET /api/agent/status:
Gateway/WebUI-Reachability + Verdrahtungs-Hinweise), AgentView (Status-
Tiles, Hermes-oeffnen-Button, Offline-Hinweis). deploy/hermes-webui.service
(systemd-USER, :8787). Box-Runbook docs/HERMES_SETUP.md (hermes-webui
installieren, Brain=model:auto via Gateway, Tools/MCP verdrahten inkl.
mcp_mc+mcp_memory, SSH-Windows, LiteLLM-Caveat #26489).

Lokal verifiziert: agent/status + Frontend-Build + Browser (Hermes-View).
Box-Ausfuehrung der Verdrahtung steht aus (Runbook). Docs aktualisiert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-06-25 08:22:28 +02:00
parent 81468df9c0
commit c773dd7eae
16 changed files with 428 additions and 174 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -6,8 +6,8 @@
<meta name="theme-color" content="#0d1117" />
<link rel="manifest" href="/manifest.webmanifest" />
<title>Mission Control 2.0</title>
<script type="module" crossorigin src="/assets/index-ByXSUESS.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CE_qxW4X.css">
<script type="module" crossorigin src="/assets/index-umMcKqw6.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-nVsgoG2p.css">
</head>
<body>
<div id="root"></div>
+3 -1
View File
@@ -7,6 +7,7 @@ import { RoutingView } from "@/views/RoutingView"
import { SystemView } from "@/views/SystemView"
import { ConnectView } from "@/views/ConnectView"
import { MemoryView } from "@/views/MemoryView"
import { AgentView } from "@/views/AgentView"
import { Placeholder } from "@/views/Placeholder"
import { api, type Health } from "@/lib/api"
import { cn } from "@/lib/utils"
@@ -96,7 +97,8 @@ export default function App() {
{view === "system" && <SystemView />}
{view === "connect" && <ConnectView />}
{view === "memory" && <MemoryView />}
{!["models", "routing", "system", "connect", "memory"].includes(view) && (
{view === "agent" && <AgentView />}
{!["models", "routing", "system", "connect", "memory", "agent"].includes(view) && (
<Placeholder title={active.label} hint={active.hint} />
)}
</main>
+11
View File
@@ -116,6 +116,17 @@ export interface DedupeResult {
applied: boolean
}
export interface AgentStatus {
gateway_url: string
webui_url: string
gateway_reachable: boolean
webui_reachable: boolean
home_exists: boolean
has_config: boolean
has_skills: boolean
has_memories: boolean
}
export interface Health {
status: string
version: string
+86
View File
@@ -0,0 +1,86 @@
import { useEffect, useState } from "react"
import { ExternalLink } from "lucide-react"
import { api, type AgentStatus } from "@/lib/api"
import { cn } from "@/lib/utils"
function Dot({ ok }: { ok: boolean }) {
return <span className={cn("h-2 w-2 rounded-full", ok ? "bg-emerald-500" : "bg-amber-500")} />
}
function Tile({ label, ok, detail }: { label: string; ok: boolean; detail?: string }) {
return (
<div className="rounded-lg border border-border bg-card p-4">
<div className="flex items-center gap-2">
<Dot ok={ok} />
<span className="text-sm font-medium">{label}</span>
</div>
{detail && <div className="mt-1 text-xs text-muted-foreground">{detail}</div>}
</div>
)
}
export function AgentView() {
const [s, setS] = useState<AgentStatus | null>(null)
const [error, setError] = useState("")
useEffect(() => {
const load = () => api<AgentStatus>("/api/agent/status").then(setS).catch((e) => setError(String(e)))
load()
const t = setInterval(load, 8000)
return () => clearInterval(t)
}, [])
return (
<div className="space-y-4">
<div className="flex items-start justify-between">
<div>
<h1 className="text-xl font-semibold">Hermes</h1>
<p className="text-sm text-muted-foreground">
Der autonome Agent läuft eigenständig (Gateway :8642) und hat seine eigene Oberfläche
(hermes-webui :8787). Mission Control verlinkt nur Chat &amp; Steuerung leben dort.
</p>
</div>
{s?.webui_url && (
<a
href={s.webui_url}
target="_blank"
rel="noopener"
className={cn(
"flex items-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-medium",
s.webui_reachable
? "bg-primary text-primary-foreground hover:opacity-90"
: "border border-border text-muted-foreground",
)}
>
<ExternalLink className="h-4 w-4" /> Hermes öffnen
</a>
)}
</div>
{error && <div className="text-sm text-muted-foreground">Status nicht lesbar ({error}).</div>}
{s && (
<>
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-4">
<Tile label="Gateway (:8642)" ok={s.gateway_reachable} detail={s.gateway_reachable ? "erreichbar" : "offline"} />
<Tile label="WebUI (:8787)" ok={s.webui_reachable} detail={s.webui_reachable ? "erreichbar" : "offline"} />
<Tile label="Brain" ok={s.gateway_reachable} detail="model: auto (über Gateway)" />
<Tile label="Verdrahtung" ok={s.has_config} detail={`config ${s.has_config ? "✓" : "—"} · skills ${s.has_skills ? "✓" : "—"} · memories ${s.has_memories ? "✓" : "—"}`} />
</div>
{!s.gateway_reachable && (
<div className="rounded-xl border border-dashed border-border bg-card/50 p-5 text-sm">
<div className="mb-2 font-medium">Hermes ist (hier) offline</div>
<p className="text-muted-foreground">
Der Agent + hermes-webui laufen auf der Box. Einrichtung & volle Verdrahtung
(Brain = <code>model: auto</code>, Tools/MCP inkl. <code>mcp_mc</code> + <code>mcp_memory</code>,
SSHWindows, lokale Browser-/Such-MCP) sind im Runbook beschrieben:
<code className="ml-1">docs/HERMES_SETUP.md</code>.
</p>
</div>
)}
</>
)}
</div>
)
}