MC2-Diät: Hermes-Fenster-Rolle an Hermes Desktop abgegeben
Der "Hermes GUI"-Tab (iframe auf /hermes-ui/) und das Hermes-Terminal-ttyd (:7681) entfallen — die Agent-Oberfläche ist jetzt die Hermes-Desktop-App, die über den BESTEHENDEN MC2-Proxy /hermes-ui/ (WS inklusive) an die Box andockt. hermes-builtin-ui (:9119) bleibt als Desktop-Gateway erhalten und wird im Dienste-Drawer + /api/system/services erstklassig geführt (statt hermes-terminal); Wartungs-Allowlist kann ihn jetzt neu starten. - Frontend: Tab/View raus (nav.ts, App.tsx, TerminalView.tsx gelöscht), AgentView + (Legacy-)AgentStatusCard zeigen Desktop-Gateway statt ttyd, Dienste-Zeile hermes-terminal → hermes-builtin-ui, Versions-Zeile "Hermes UI" (nesquena-Karteileiche ~/hermes-webui) entfernt. - Backend: terminal_url/terminal_reachable + HERMES_TERMINAL_* raus, console.py proxyt nur noch die Box-Konsole, /api/system/services führt hermes-builtin-ui, USER_SERVICES getauscht, hermes-webui-GitInfo raus. - deploy.sh: legt hermes-terminal + hermes-webui idempotent still (disable --now + Unit-Löschung). deploy/hermes-terminal.service bleibt EINEN Zyklus im Repo (das laufende alte deploy.sh kopiert es noch — Selbst-Reset-Falle); Aufräum-Commit folgt nach dem nächsten Deploy. - frontend/dist mit-committet (Box baut nicht selbst). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+1
-6
@@ -94,12 +94,7 @@ HERMES_API_MODEL = os.environ.get("HERMES_API_MODEL", "hermes")
|
|||||||
# --- Voice-Sidecar (STT faster-whisper + TTS Piper/Chatterbox) ---------------
|
# --- Voice-Sidecar (STT faster-whisper + TTS Piper/Chatterbox) ---------------
|
||||||
# Eigenes Python-3.12-venv (~/.voice/venv), analog Mem0-Sidecar. MC2 proxyt nach außen.
|
# Eigenes Python-3.12-venv (~/.voice/venv), analog Mem0-Sidecar. MC2 proxyt nach außen.
|
||||||
VOICE_SERVICE_URL = os.environ.get("MC_VOICE_SERVICE_URL", "http://127.0.0.1:8650").rstrip("/")
|
VOICE_SERVICE_URL = os.environ.get("MC_VOICE_SERVICE_URL", "http://127.0.0.1:8650").rstrip("/")
|
||||||
# Hermes-Terminal: ttyd-Web-Terminal der interaktiven Agent-CLI. Wie die Box-Konsole bindet
|
# Box-Konsole: ttyd-Web-Terminal (echte Login-Shell). Bindet NUR an Loopback
|
||||||
# es NUR an Loopback (127.0.0.1:7681, base-path /hermes-terminal) und wird von MC2 same-origin
|
|
||||||
# durchgereicht (routers/console.py → /hermes-terminal/). Login per `su` (Box-Passwort).
|
|
||||||
HERMES_TERMINAL_UPSTREAM = os.environ.get("MC_HERMES_TERMINAL_UPSTREAM", "http://127.0.0.1:7681").rstrip("/")
|
|
||||||
HERMES_TERMINAL_PATH = "/hermes-terminal/"
|
|
||||||
# Box-Konsole: zweites ttyd-Web-Terminal (echte Login-Shell). Bindet NUR an Loopback
|
|
||||||
# (127.0.0.1:7682, base-path /console) und wird von MC2 über den ohnehin offenen Port 9001
|
# (127.0.0.1:7682, base-path /console) und wird von MC2 über den ohnehin offenen Port 9001
|
||||||
# rückwärts geproxyt (routers/console.py → same-origin /console/). So braucht die Konsole
|
# rückwärts geproxyt (routers/console.py → same-origin /console/). So braucht die Konsole
|
||||||
# KEINE eigene Firewall-Freigabe (Port 7682 ist von außen dicht) und keinen sudo-Eingriff.
|
# KEINE eigene Firewall-Freigabe (Port 7682 ist von außen dicht) und keinen sudo-Eingriff.
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import websockets
|
|||||||
from fastapi import APIRouter, Request, WebSocket
|
from fastapi import APIRouter, Request, WebSocket
|
||||||
from starlette.responses import Response
|
from starlette.responses import Response
|
||||||
|
|
||||||
from config import BOX_CONSOLE_UPSTREAM, HERMES_TERMINAL_UPSTREAM
|
from config import BOX_CONSOLE_UPSTREAM
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
@@ -83,6 +83,6 @@ def _register(base: str, upstream: str) -> None:
|
|||||||
name=f"{base}_path")
|
name=f"{base}_path")
|
||||||
|
|
||||||
|
|
||||||
# Box-Konsole (Login-Shell) und Hermes-Terminal (Agent-CLI) — beide Loopback-ttyd.
|
# Box-Konsole (Login-Shell) — Loopback-ttyd. (Das Hermes-Terminal-ttyd ist mit dem
|
||||||
|
# Umzug auf Hermes Desktop entfallen; die Agent-Oberfläche ist die Desktop-App bzw. /hermes-ui/.)
|
||||||
_register("console", BOX_CONSOLE_UPSTREAM)
|
_register("console", BOX_CONSOLE_UPSTREAM)
|
||||||
_register("hermes-terminal", HERMES_TERMINAL_UPSTREAM)
|
|
||||||
|
|||||||
@@ -61,14 +61,14 @@ def services() -> dict:
|
|||||||
{"name": "Engine (llama-swap)", "unit": "llama-swap", "url": LLAMA_SWAP_URL, "ok": engine_reachable()},
|
{"name": "Engine (llama-swap)", "unit": "llama-swap", "url": LLAMA_SWAP_URL, "ok": engine_reachable()},
|
||||||
{"name": "Gateway (integriert)", "unit": "mission-control-2", "url": gw_url, "ok": gateway_reachable()},
|
{"name": "Gateway (integriert)", "unit": "mission-control-2", "url": gw_url, "ok": gateway_reachable()},
|
||||||
{"name": "Hermes-Gateway", "unit": "hermes-gateway", "url": HERMES_API_URL, "ok": a["gateway_reachable"]},
|
{"name": "Hermes-Gateway", "unit": "hermes-gateway", "url": HERMES_API_URL, "ok": a["gateway_reachable"]},
|
||||||
{"name": "Hermes-Terminal", "unit": "hermes-terminal", "url": a["terminal_url"], "ok": a["terminal_reachable"]},
|
{"name": "Hermes-GUI (Desktop-Gateway)", "unit": "hermes-builtin-ui", "url": a["hermes_ui_url"], "ok": a["hermes_ui_reachable"]},
|
||||||
{"name": "Mem0 (Gedächtnis)", "unit": "mem0-service", "url": MEM0_SERVICE_URL, "ok": _mem0_reachable()},
|
{"name": "Mem0 (Gedächtnis)", "unit": "mem0-service", "url": MEM0_SERVICE_URL, "ok": _mem0_reachable()},
|
||||||
{"name": "Voice (STT/TTS)", "unit": "voice-service", "url": VOICE_SERVICE_URL, "ok": _voice_reachable()},
|
{"name": "Voice (STT/TTS)", "unit": "voice-service", "url": VOICE_SERVICE_URL, "ok": _voice_reachable()},
|
||||||
],
|
],
|
||||||
"links": {
|
"links": {
|
||||||
"engine_ui": f"{LLAMA_SWAP_URL}/ui",
|
"engine_ui": f"{LLAMA_SWAP_URL}/ui",
|
||||||
"gateway": gw_url,
|
"gateway": gw_url,
|
||||||
"hermes_terminal": a["terminal_url"],
|
"hermes_ui": a["hermes_ui_url"],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import re
|
|||||||
import httpx
|
import httpx
|
||||||
import psutil
|
import psutil
|
||||||
|
|
||||||
from config import (HERMES_TERMINAL_UPSTREAM, HERMES_TERMINAL_PATH, BOX_CONSOLE_UPSTREAM,
|
from config import (BOX_CONSOLE_UPSTREAM,
|
||||||
BOX_CONSOLE_PATH, HERMES_BUILTIN_UI_UPSTREAM, HERMES_BUILTIN_UI_PATH,
|
BOX_CONSOLE_PATH, HERMES_BUILTIN_UI_UPSTREAM, HERMES_BUILTIN_UI_PATH,
|
||||||
HERMES_API_URL, HERMES_HOME, PC_EXECUTOR_URL)
|
HERMES_API_URL, HERMES_HOME, PC_EXECUTOR_URL)
|
||||||
|
|
||||||
@@ -141,17 +141,15 @@ def agent_status() -> dict:
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
"gateway_url": HERMES_API_URL,
|
"gateway_url": HERMES_API_URL,
|
||||||
# Interaktives Web-Terminal (ttyd → `hermes chat`): same-origin über MC2 geproxyt.
|
|
||||||
"terminal_url": HERMES_TERMINAL_PATH,
|
|
||||||
# Box-Konsole (ttyd → Login-Shell): same-origin über MC2 geproxyt (/console/).
|
# Box-Konsole (ttyd → Login-Shell): same-origin über MC2 geproxyt (/console/).
|
||||||
"box_console_url": BOX_CONSOLE_PATH,
|
"box_console_url": BOX_CONSOLE_PATH,
|
||||||
# Eingebaute Hermes-Web-GUI (hermes serve): same-origin über MC2 geproxyt (/hermes-ui/).
|
# Eingebaute Hermes-Web-GUI (hermes serve): same-origin über MC2 geproxyt (/hermes-ui/).
|
||||||
|
# Seit dem Umzug auf Hermes Desktop ist :9119 zugleich das Desktop-Gateway.
|
||||||
"hermes_ui_url": HERMES_BUILTIN_UI_PATH,
|
"hermes_ui_url": HERMES_BUILTIN_UI_PATH,
|
||||||
"gateway_reachable": _reach(HERMES_API_URL, "/health"),
|
"gateway_reachable": _reach(HERMES_API_URL, "/health"),
|
||||||
# Erreichbarkeit der lokalen ttyd-Upstreams (Loopback, je base-path).
|
# Erreichbarkeit des lokalen ttyd-Upstreams (Loopback, base-path /console).
|
||||||
"terminal_reachable": _reach(HERMES_TERMINAL_UPSTREAM, "/hermes-terminal/"),
|
|
||||||
"box_console_reachable": _reach(BOX_CONSOLE_UPSTREAM, "/console/"),
|
"box_console_reachable": _reach(BOX_CONSOLE_UPSTREAM, "/console/"),
|
||||||
# Erreichbarkeit der eingebauten Hermes-GUI (Loopback :9119).
|
# Erreichbarkeit der eingebauten Hermes-GUI / des Desktop-Gateways (Loopback :9119).
|
||||||
"hermes_ui_reachable": _reach(HERMES_BUILTIN_UI_UPSTREAM, "/"),
|
"hermes_ui_reachable": _reach(HERMES_BUILTIN_UI_UPSTREAM, "/"),
|
||||||
"home_exists": home.exists(),
|
"home_exists": home.exists(),
|
||||||
"brain_model": brain_model,
|
"brain_model": brain_model,
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ from services import catalog, discover, jobengine, llamaswap, system
|
|||||||
|
|
||||||
# System-Dienste (root, via sudo -n NOPASSWD) vs. User-Dienste (systemctl --user).
|
# System-Dienste (root, via sudo -n NOPASSWD) vs. User-Dienste (systemctl --user).
|
||||||
SYSTEM_SERVICES = {"llama-swap"}
|
SYSTEM_SERVICES = {"llama-swap"}
|
||||||
USER_SERVICES = {"mission-control-2", "hermes-gateway", "hermes-terminal", "mem0-service", "voice-service"}
|
USER_SERVICES = {"mission-control-2", "hermes-gateway", "hermes-builtin-ui", "mem0-service", "voice-service"}
|
||||||
|
|
||||||
# Engine-Update: lädt den neuesten Vulkan-Build (deploy/update-engine.sh, läuft als root).
|
# Engine-Update: lädt den neuesten Vulkan-Build (deploy/update-engine.sh, läuft als root).
|
||||||
_REPO_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
|
_REPO_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
|
||||||
|
|||||||
@@ -117,7 +117,6 @@ def find_hermes_agent_git() -> dict | None:
|
|||||||
candidates = [
|
candidates = [
|
||||||
"~/hermes-agent",
|
"~/hermes-agent",
|
||||||
"~/.hermes/hermes-agent",
|
"~/.hermes/hermes-agent",
|
||||||
"~/hermes-webui/hermes-agent",
|
|
||||||
"~/.hermes"
|
"~/.hermes"
|
||||||
]
|
]
|
||||||
for c in candidates:
|
for c in candidates:
|
||||||
@@ -179,7 +178,6 @@ def check_versions_cached() -> dict:
|
|||||||
data = {
|
data = {
|
||||||
"mc2": get_git_info(mc2_path),
|
"mc2": get_git_info(mc2_path),
|
||||||
"engine": get_engine_version(),
|
"engine": get_engine_version(),
|
||||||
"hermes_ui": get_git_info("~/hermes-webui"),
|
|
||||||
"hermes_agent": find_hermes_agent_git()
|
"hermes_agent": find_hermes_agent_git()
|
||||||
}
|
}
|
||||||
_VERSION_CACHE["ts"] = now
|
_VERSION_CACHE["ts"] = now
|
||||||
|
|||||||
+6
-5
@@ -49,9 +49,12 @@ fi
|
|||||||
# systemd-USER-Units installieren/aktualisieren
|
# systemd-USER-Units installieren/aktualisieren
|
||||||
mkdir -p "$HOME/.config/systemd/user"
|
mkdir -p "$HOME/.config/systemd/user"
|
||||||
cp "$SRC/deploy/mission-control-2.service" "$HOME/.config/systemd/user/mission-control-2.service"
|
cp "$SRC/deploy/mission-control-2.service" "$HOME/.config/systemd/user/mission-control-2.service"
|
||||||
# Hermes-Terminal (ttyd -> `hermes chat`), in MC2 als Terminal-Seite eingebettet.
|
# MC2-Diät (Hermes Desktop, 07/2026): das Hermes-Terminal-ttyd (:7681) und das standalone
|
||||||
# Einmalig manuell noetig: `sudo apt install -y ttyd` + `sudo systemctl disable --now ttyd` (apt-Default-Dienst).
|
# hermes-webui (nesquena, Karteileiche seit dem Built-in-UI-Umbau) sind entfallen — die
|
||||||
cp "$SRC/deploy/hermes-terminal.service" "$HOME/.config/systemd/user/hermes-terminal.service"
|
# Agent-Oberfläche ist die Desktop-App bzw. /hermes-ui/. Einmalige, idempotente Stilllegung:
|
||||||
|
systemctl --user disable --now hermes-terminal 2>/dev/null || true
|
||||||
|
systemctl --user disable --now hermes-webui 2>/dev/null || true
|
||||||
|
rm -f "$HOME/.config/systemd/user/hermes-terminal.service" "$HOME/.config/systemd/user/hermes-webui.service"
|
||||||
# Box-Konsole (ttyd -> Login-Shell auf :7682), in MC2 als Konsole-Seite eingebettet.
|
# Box-Konsole (ttyd -> Login-Shell auf :7682), in MC2 als Konsole-Seite eingebettet.
|
||||||
cp "$SRC/deploy/box-console.service" "$HOME/.config/systemd/user/box-console.service"
|
cp "$SRC/deploy/box-console.service" "$HOME/.config/systemd/user/box-console.service"
|
||||||
# Mem0-Sidecar-Unit (nur wenn das venv existiert).
|
# Mem0-Sidecar-Unit (nur wenn das venv existiert).
|
||||||
@@ -110,7 +113,6 @@ if [ -d "$_HUI_WEB" ] && [ -x "$HOME/.hermes/node/bin/npx" ]; then
|
|||||||
fi
|
fi
|
||||||
systemctl --user daemon-reload
|
systemctl --user daemon-reload
|
||||||
systemctl --user enable mission-control-2 >/dev/null 2>&1 || true
|
systemctl --user enable mission-control-2 >/dev/null 2>&1 || true
|
||||||
systemctl --user enable hermes-terminal >/dev/null 2>&1 || true
|
|
||||||
systemctl --user enable box-console >/dev/null 2>&1 || true
|
systemctl --user enable box-console >/dev/null 2>&1 || true
|
||||||
systemctl --user enable mem0-service >/dev/null 2>&1 || true
|
systemctl --user enable mem0-service >/dev/null 2>&1 || true
|
||||||
systemctl --user enable voice-service >/dev/null 2>&1 || true
|
systemctl --user enable voice-service >/dev/null 2>&1 || true
|
||||||
@@ -128,7 +130,6 @@ loginctl enable-linger "$USER" >/dev/null 2>&1 || true
|
|||||||
systemctl --user reset-failed hermes-builtin-ui 2>/dev/null || true
|
systemctl --user reset-failed hermes-builtin-ui 2>/dev/null || true
|
||||||
systemctl --user restart hermes-builtin-ui 2>/dev/null || true
|
systemctl --user restart hermes-builtin-ui 2>/dev/null || true
|
||||||
systemctl --user restart mission-control-2
|
systemctl --user restart mission-control-2
|
||||||
command -v ttyd >/dev/null 2>&1 && systemctl --user restart hermes-terminal 2>/dev/null || true
|
|
||||||
command -v ttyd >/dev/null 2>&1 && systemctl --user restart box-console 2>/dev/null || true
|
command -v ttyd >/dev/null 2>&1 && systemctl --user restart box-console 2>/dev/null || true
|
||||||
|
|
||||||
sleep 2
|
sleep 2
|
||||||
|
|||||||
+1
-1
File diff suppressed because one or more lines are too long
+169
-174
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -7,7 +7,7 @@
|
|||||||
<link rel="manifest" href="/manifest.webmanifest" />
|
<link rel="manifest" href="/manifest.webmanifest" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
<title>Mission Control 2.0</title>
|
<title>Mission Control 2.0</title>
|
||||||
<script type="module" crossorigin src="/assets/index-CcL1k1kt.js"></script>
|
<script type="module" crossorigin src="/assets/index-1tNxUTCg.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-B7apENt0.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-B7apENt0.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import { ModelsView } from "@/views/ModelsView"
|
|||||||
import { ConnectView } from "@/views/ConnectView"
|
import { ConnectView } from "@/views/ConnectView"
|
||||||
import { MemoryView } from "@/views/MemoryView"
|
import { MemoryView } from "@/views/MemoryView"
|
||||||
import { AgentView } from "@/views/AgentView"
|
import { AgentView } from "@/views/AgentView"
|
||||||
import { TerminalView } from "@/views/TerminalView"
|
|
||||||
import { KonsoleView } from "@/views/KonsoleView"
|
import { KonsoleView } from "@/views/KonsoleView"
|
||||||
import { GuideView } from "@/views/GuideView"
|
import { GuideView } from "@/views/GuideView"
|
||||||
import { Placeholder } from "@/views/Placeholder"
|
import { Placeholder } from "@/views/Placeholder"
|
||||||
@@ -188,9 +187,6 @@ export default function App() {
|
|||||||
<div className="truncate" title={sysStatus.versions.engine?.type === "git" ? `${sysStatus.versions.engine.branch}-${sysStatus.versions.engine.hash}${sysStatus.versions.engine.dirty ? "*" : ""} (${sysStatus.versions.engine.date})` : sysStatus.versions.engine?.version_text || "unbekannt"}>
|
<div className="truncate" title={sysStatus.versions.engine?.type === "git" ? `${sysStatus.versions.engine.branch}-${sysStatus.versions.engine.hash}${sysStatus.versions.engine.dirty ? "*" : ""} (${sysStatus.versions.engine.date})` : sysStatus.versions.engine?.version_text || "unbekannt"}>
|
||||||
<strong>Engine:</strong> {sysStatus.versions.engine?.type === "git" ? `${sysStatus.versions.engine.hash}${sysStatus.versions.engine.dirty ? "*" : ""}` : (sysStatus.versions.engine?.version_text?.split(" ").pop() || "—")}
|
<strong>Engine:</strong> {sysStatus.versions.engine?.type === "git" ? `${sysStatus.versions.engine.hash}${sysStatus.versions.engine.dirty ? "*" : ""}` : (sysStatus.versions.engine?.version_text?.split(" ").pop() || "—")}
|
||||||
</div>
|
</div>
|
||||||
<div className="truncate" title={sysStatus.versions.hermes_ui ? `${sysStatus.versions.hermes_ui.branch}-${sysStatus.versions.hermes_ui.hash}${sysStatus.versions.hermes_ui.dirty ? "*" : ""} (${sysStatus.versions.hermes_ui.date})` : "nicht gefunden"}>
|
|
||||||
<strong>Hermes UI:</strong> {sysStatus.versions.hermes_ui ? `${sysStatus.versions.hermes_ui.hash}${sysStatus.versions.hermes_ui.dirty ? "*" : ""}` : "—"}
|
|
||||||
</div>
|
|
||||||
<div className="truncate" title={sysStatus.versions.hermes_agent ? `${sysStatus.versions.hermes_agent.branch}-${sysStatus.versions.hermes_agent.hash}${sysStatus.versions.hermes_agent.dirty ? "*" : ""} (${sysStatus.versions.hermes_agent.date})` : "nicht gefunden"}>
|
<div className="truncate" title={sysStatus.versions.hermes_agent ? `${sysStatus.versions.hermes_agent.branch}-${sysStatus.versions.hermes_agent.hash}${sysStatus.versions.hermes_agent.dirty ? "*" : ""} (${sysStatus.versions.hermes_agent.date})` : "nicht gefunden"}>
|
||||||
<strong>Hermes Agent:</strong> {sysStatus.versions.hermes_agent ? `${sysStatus.versions.hermes_agent.hash}${sysStatus.versions.hermes_agent.dirty ? "*" : ""}` : "—"}
|
<strong>Hermes Agent:</strong> {sysStatus.versions.hermes_agent ? `${sysStatus.versions.hermes_agent.hash}${sysStatus.versions.hermes_agent.dirty ? "*" : ""}` : "—"}
|
||||||
</div>
|
</div>
|
||||||
@@ -240,10 +236,9 @@ export default function App() {
|
|||||||
{view === "wissen" && <WissenView />}
|
{view === "wissen" && <WissenView />}
|
||||||
{view === "chronik" && <ChronikView />}
|
{view === "chronik" && <ChronikView />}
|
||||||
{view === "agent" && <AgentView />}
|
{view === "agent" && <AgentView />}
|
||||||
{view === "terminal" && <TerminalView />}
|
|
||||||
{view === "konsole" && <KonsoleView />}
|
{view === "konsole" && <KonsoleView />}
|
||||||
{view === "guide" && <GuideView />}
|
{view === "guide" && <GuideView />}
|
||||||
{!["dashboard", "auftraege", "models", "connect", "memory", "wissen", "chronik", "agent", "terminal", "konsole", "guide"].includes(view) && (
|
{!["dashboard", "auftraege", "models", "connect", "memory", "wissen", "chronik", "agent", "konsole", "guide"].includes(view) && (
|
||||||
<Placeholder title={active.label} hint={active.hint} />
|
<Placeholder title={active.label} hint={active.hint} />
|
||||||
)}
|
)}
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -33,19 +33,19 @@ export function AgentStatusCard() {
|
|||||||
<Bot className="h-4.5 w-4.5 text-primary" />
|
<Bot className="h-4.5 w-4.5 text-primary" />
|
||||||
<h2 className="text-sm font-semibold tracking-wide uppercase text-foreground">Hermes Agent</h2>
|
<h2 className="text-sm font-semibold tracking-wide uppercase text-foreground">Hermes Agent</h2>
|
||||||
</div>
|
</div>
|
||||||
{agent?.terminal_url && (
|
{agent?.hermes_ui_url && (
|
||||||
<a
|
<a
|
||||||
href={resolveExternalUrl(agent.terminal_url)}
|
href={resolveExternalUrl(agent.hermes_ui_url)}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener"
|
rel="noopener"
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex items-center gap-1 px-3 py-1 rounded-lg text-xs font-medium transition-all",
|
"flex items-center gap-1 px-3 py-1 rounded-lg text-xs font-medium transition-all",
|
||||||
agent.terminal_reachable
|
agent.hermes_ui_reachable
|
||||||
? "bg-primary text-primary-foreground hover:opacity-90 shadow-md shadow-primary/20"
|
? "bg-primary text-primary-foreground hover:opacity-90 shadow-md shadow-primary/20"
|
||||||
: "border border-border text-muted-foreground"
|
: "border border-border text-muted-foreground"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<ExternalLink className="h-3 w-3" /> Terminal öffnen
|
<ExternalLink className="h-3 w-3" /> GUI öffnen
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -60,10 +60,10 @@ export function AgentStatusCard() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-3 bg-background/20 rounded-xl border border-border/40">
|
<div className="p-3 bg-background/20 rounded-xl border border-border/40">
|
||||||
<div className="text-[10px] text-muted-foreground uppercase font-semibold">Terminal</div>
|
<div className="text-[10px] text-muted-foreground uppercase font-semibold">Desktop-Gateway</div>
|
||||||
<div className="flex items-center gap-2 mt-1">
|
<div className="flex items-center gap-2 mt-1">
|
||||||
<span className={cn("h-2 w-2 rounded-full", agent.terminal_reachable ? "bg-emerald-500 animate-pulse" : "bg-amber-500")} />
|
<span className={cn("h-2 w-2 rounded-full", agent.hermes_ui_reachable ? "bg-emerald-500 animate-pulse" : "bg-amber-500")} />
|
||||||
<span className="text-xs font-medium">{agent.terminal_reachable ? "Online" : "Offline"}</span>
|
<span className="text-xs font-medium">{agent.hermes_ui_reachable ? "Online" : "Offline"}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export const SERVICES = [
|
|||||||
{ id: "hermes-gateway", label: "Hermes Gateway", type: "user", reach: "hermes-gateway" },
|
{ id: "hermes-gateway", label: "Hermes Gateway", type: "user", reach: "hermes-gateway" },
|
||||||
{ id: "mem0-service", label: "Mem0 (Gedächtnis)", type: "user", reach: "mem0" },
|
{ id: "mem0-service", label: "Mem0 (Gedächtnis)", type: "user", reach: "mem0" },
|
||||||
{ id: "voice-service", label: "Voice (Sprache)", type: "user", reach: "voice" },
|
{ id: "voice-service", label: "Voice (Sprache)", type: "user", reach: "voice" },
|
||||||
{ id: "hermes-terminal", label: "Hermes Terminal", type: "user", reach: "hermes-terminal" },
|
{ id: "hermes-builtin-ui", label: "Hermes GUI (Desktop-Gateway)", type: "user", reach: "hermes-gui" },
|
||||||
{ id: "llama-swap", label: "Llama Swap", type: "system", reach: "llama-swap" },
|
{ id: "llama-swap", label: "Llama Swap", type: "system", reach: "llama-swap" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -249,7 +249,6 @@ export interface ComponentVersion {
|
|||||||
export interface Versions {
|
export interface Versions {
|
||||||
mc2: GitInfo | null
|
mc2: GitInfo | null
|
||||||
engine: ComponentVersion
|
engine: ComponentVersion
|
||||||
hermes_ui: GitInfo | null
|
|
||||||
hermes_agent: GitInfo | null
|
hermes_agent: GitInfo | null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,7 +269,7 @@ export interface SystemStatus {
|
|||||||
|
|
||||||
export interface ServicesResp {
|
export interface ServicesResp {
|
||||||
services: { name: string; unit: string; url: string; ok: boolean }[]
|
services: { name: string; unit: string; url: string; ok: boolean }[]
|
||||||
links: { engine_ui: string; gateway: string; hermes_terminal: string }
|
links: { engine_ui: string; gateway: string; hermes_ui: string }
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ComponentUpdate {
|
export interface ComponentUpdate {
|
||||||
@@ -371,11 +370,9 @@ export interface DedupeResult {
|
|||||||
|
|
||||||
export interface AgentStatus {
|
export interface AgentStatus {
|
||||||
gateway_url: string
|
gateway_url: string
|
||||||
terminal_url: string
|
|
||||||
box_console_url?: string
|
box_console_url?: string
|
||||||
hermes_ui_url?: string
|
hermes_ui_url?: string
|
||||||
gateway_reachable: boolean
|
gateway_reachable: boolean
|
||||||
terminal_reachable: boolean
|
|
||||||
box_console_reachable?: boolean
|
box_console_reachable?: boolean
|
||||||
hermes_ui_reachable?: boolean
|
hermes_ui_reachable?: boolean
|
||||||
home_exists: boolean
|
home_exists: boolean
|
||||||
|
|||||||
+1
-3
@@ -7,13 +7,12 @@ import {
|
|||||||
Library,
|
Library,
|
||||||
Plug,
|
Plug,
|
||||||
Bot,
|
Bot,
|
||||||
AppWindow,
|
|
||||||
SquareTerminal,
|
SquareTerminal,
|
||||||
HelpCircle,
|
HelpCircle,
|
||||||
type LucideIcon,
|
type LucideIcon,
|
||||||
} from "lucide-react"
|
} from "lucide-react"
|
||||||
|
|
||||||
export type ViewId = "dashboard" | "auftraege" | "models" | "memory" | "wissen" | "chronik" | "connect" | "agent" | "terminal" | "konsole" | "guide"
|
export type ViewId = "dashboard" | "auftraege" | "models" | "memory" | "wissen" | "chronik" | "connect" | "agent" | "konsole" | "guide"
|
||||||
|
|
||||||
export interface NavItem {
|
export interface NavItem {
|
||||||
id: ViewId
|
id: ViewId
|
||||||
@@ -32,7 +31,6 @@ export const NAV: NavItem[] = [
|
|||||||
{ id: "chronik", label: "Chronik", hint: "Was die Box von allein getan hat + Zeitmaschine", icon: History },
|
{ id: "chronik", label: "Chronik", hint: "Was die Box von allein getan hat + Zeitmaschine", icon: History },
|
||||||
{ id: "connect", label: "Verbinden", hint: "IDE-/Agent-Configs erzeugen", icon: Plug },
|
{ id: "connect", label: "Verbinden", hint: "IDE-/Agent-Configs erzeugen", icon: Plug },
|
||||||
{ id: "agent", label: "Hermes", hint: "Agent-Status & Verdrahtung", icon: Bot },
|
{ id: "agent", label: "Hermes", hint: "Agent-Status & Verdrahtung", icon: Bot },
|
||||||
{ 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: "konsole", label: "Konsole", hint: "Direkte Box-Shell (SSH-artig)", icon: SquareTerminal },
|
||||||
{ id: "guide", label: "Anleitung", hint: "Einrichten & Vibe-Coding", icon: HelpCircle },
|
{ id: "guide", label: "Anleitung", hint: "Einrichten & Vibe-Coding", icon: HelpCircle },
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -56,20 +56,20 @@ export function AgentView() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{s?.terminal_url && (
|
{s?.hermes_ui_url && (
|
||||||
<a
|
<a
|
||||||
href={resolveExternalUrl(s.terminal_url)}
|
href={resolveExternalUrl(s.hermes_ui_url)}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener"
|
rel="noopener"
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex h-9 px-4 items-center gap-1.5 text-xs font-semibold rounded-lg transition-all cursor-pointer shadow-md shrink-0 self-start",
|
"flex h-9 px-4 items-center gap-1.5 text-xs font-semibold rounded-lg transition-all cursor-pointer shadow-md shrink-0 self-start",
|
||||||
s.terminal_reachable
|
s.hermes_ui_reachable
|
||||||
? "bg-primary text-primary-foreground hover:opacity-90 shadow-primary/10"
|
? "bg-primary text-primary-foreground hover:opacity-90 shadow-primary/10"
|
||||||
: "border border-border/60 text-muted-foreground bg-background/20"
|
: "border border-border/60 text-muted-foreground bg-background/20"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<ExternalLink className="h-4 w-4" />
|
<ExternalLink className="h-4 w-4" />
|
||||||
<span>Terminal öffnen</span>
|
<span>Hermes-GUI öffnen</span>
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -92,16 +92,16 @@ export function AgentView() {
|
|||||||
<div className="flex flex-col lg:flex-row lg:items-stretch gap-3">
|
<div className="flex flex-col lg:flex-row lg:items-stretch gap-3">
|
||||||
{/* Quelle: Terminal/Clients */}
|
{/* Quelle: Terminal/Clients */}
|
||||||
<button
|
<button
|
||||||
onClick={() => s.terminal_reachable && window.open(resolveExternalUrl(s.terminal_url), "_blank")}
|
onClick={() => s.hermes_ui_reachable && window.open(resolveExternalUrl(s.hermes_ui_url), "_blank")}
|
||||||
title={s.terminal_reachable ? "Hermes-Terminal öffnen" : "Terminal offline"}
|
title={s.hermes_ui_reachable ? "Hermes-GUI öffnen" : "Desktop-Gateway offline"}
|
||||||
className="lg:w-40 shrink-0 rounded-xl border border-border/60 bg-background/30 p-3 text-left hover:border-primary/40 transition-all cursor-pointer flex flex-col justify-center"
|
className="lg:w-40 shrink-0 rounded-xl border border-border/60 bg-background/30 p-3 text-left hover:border-primary/40 transition-all cursor-pointer flex flex-col justify-center"
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-1.5">
|
<div className="flex items-center gap-1.5">
|
||||||
<Activity className={cn("h-3.5 w-3.5", s.terminal_reachable ? "text-emerald-400" : "text-amber-500")} />
|
<Activity className={cn("h-3.5 w-3.5", s.hermes_ui_reachable ? "text-emerald-400" : "text-amber-500")} />
|
||||||
<span className="text-xs font-semibold text-foreground">Terminal</span>
|
<span className="text-xs font-semibold text-foreground">Desktop & GUI</span>
|
||||||
<span className={cn("ml-auto h-1.5 w-1.5 rounded-full animate-pulse", s.terminal_reachable ? "bg-emerald-500" : "bg-amber-500")} />
|
<span className={cn("ml-auto h-1.5 w-1.5 rounded-full animate-pulse", s.hermes_ui_reachable ? "bg-emerald-500" : "bg-amber-500")} />
|
||||||
</div>
|
</div>
|
||||||
<span className="text-[10px] text-muted-foreground font-mono mt-0.5">hermes chat · Clients</span>
|
<span className="text-[10px] text-muted-foreground font-mono mt-0.5">Hermes Desktop · Clients</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div className="hidden lg:flex items-center text-muted-foreground text-lg select-none">→</div>
|
<div className="hidden lg:flex items-center text-muted-foreground text-lg select-none">→</div>
|
||||||
@@ -235,7 +235,7 @@ export function AgentView() {
|
|||||||
<div className="p-3.5 bg-background/25 rounded-xl border border-border/30 font-mono text-[10px] space-y-1">
|
<div className="p-3.5 bg-background/25 rounded-xl border border-border/30 font-mono text-[10px] space-y-1">
|
||||||
<div className="text-muted-foreground/60"># Dienste manuell auf der Box prüfen:</div>
|
<div className="text-muted-foreground/60"># Dienste manuell auf der Box prüfen:</div>
|
||||||
<div className="text-primary">systemctl --user status hermes-gateway</div>
|
<div className="text-primary">systemctl --user status hermes-gateway</div>
|
||||||
<div className="text-primary">systemctl --user status hermes-terminal</div>
|
<div className="text-primary">systemctl --user status hermes-builtin-ui</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
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?.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 GUI
|
|
||||||
</h1>
|
|
||||||
<p className="flex items-center gap-2 text-sm text-muted-foreground">
|
|
||||||
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">
|
|
||||||
<span className="flex items-center gap-1.5 text-[11px] font-medium text-muted-foreground">
|
|
||||||
<span className={`h-2 w-2 rounded-full ${ok ? "bg-emerald-500 animate-pulse" : "bg-amber-500"}`} />
|
|
||||||
{ok ? "online" : "offline"}
|
|
||||||
</span>
|
|
||||||
{url && (
|
|
||||||
<a href={url} target="_blank" rel="noopener"
|
|
||||||
className="flex h-8 items-center gap-1.5 rounded-lg border border-border/60 bg-background/20 px-3 text-xs font-semibold text-muted-foreground transition-all hover:border-primary/50 hover:text-foreground">
|
|
||||||
<ExternalLink className="h-3.5 w-3.5" /> In neuem Tab
|
|
||||||
</a>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{url ? (
|
|
||||||
<div className="relative min-h-[68vh] flex-1 overflow-hidden rounded-2xl border border-border/60 bg-black/50 shadow-lg shadow-black/25">
|
|
||||||
{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">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-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 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">
|
|
||||||
<Bot className="mr-2 h-4 w-4" /> Lade Hermes-GUI…
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user