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
+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>
)
}