0c69c491e5
Backend (Wahrheit reparieren): - gateway_reachable() prueft jetzt den ECHTEN mc2-gateway (:9010 via V1_UPSTREAM) statt nur die Engine - toter Denkpfad war in Health/Cockpit unsichtbar - /api/system/services vollstaendig: + LLM-Gateway, + Steuerpult, + Waechter (mc2-steward via is-active); scope-Feld (user/system); mc2-gateway/mc2-steward in die Restart-Allowlist - Connect-Health Leitung 1 prueft den Client-Pfad (V1_UPSTREAM statt llama-swap) - agent_status liefert pc_executor_url (UI zeigte hartcodierte IP/Ports) - SSE-Endlosschleife gefixt: /api/auftragsbuch schrieb announce-branches bei JEDEM Aufruf neu -> mtime-Bump -> invalidate -> Refetch im 3-s-Takt je Client; jetzt nur noch bei echter Aenderung Frontend (ein Datenweg): - SystemDrawer komplett auf React-Query-Hooks (vorher eigenes 3-s-setInterval auf dieselben Endpunkte inkl. teurem Updates-Check); Dienste-Liste = Backend-Antwort (SERVICES-UI-Kopie geloescht); useDialog statt Eigenbau; fmtBytes statt Duplikat - useLucyHealth: Dienst-Matching per systemd-unit statt Namensfragment; Gateway- Reparatur zielt auf mc2-gateway; neuer Waechter-Check; Backend-tot => ehrliches rotes Verdikt statt eingefrorener letzter Stand (auch Sidebar) - Toter Code raus: views/models/Cockpit.tsx (908 Z.) + RoleAssignModal + Placeholder; LaneEditor (Routing-Policy) + WarmSetManager damit ZURUECK im UI als Tab "Routing & Warm-Set" im Modell-Manager - Cockpit: blockierte Ideen-Karten erscheinen in "Braucht dich" + Kachel-Hint; Verbinden-Kachel zeigt 3 Leitungen live statt hartem "Zed" - Maschinenraum: ehrliches "frei" (reale Belegung) + eigenes KV-Cache-Segment - AgentView: Ports/PC-Adresse aus der API; Guide lehrt Lanes chat/coding - queries.ts: useModels nutzt SSE-relax; Chronik-Limit im Query-Key; useJobs/useZeitmaschine mit enabled-Schalter Verifiziert: tsc+vite gruen, Backend-Smoke beide Gateway-Modi, UI live gegen die Box (Cockpit/Werkbank/Routing-Tab/Drawer rendern mit Echtdaten). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
162 lines
7.6 KiB
Python
162 lines
7.6 KiB
Python
"""
|
|
Connect: erzeugt saubere, getestete Konfig-Snippets für IDEs/Agenten auf dem
|
|
LOKALEN PC (separate Maschine im LAN). Alle zeigen auf den **Gateway** der Box
|
|
(reale Modelle coder/heavy/vision, Cockpit-Port :9001/v1) + den **Shared-Memory-MCP** (MC :9001).
|
|
|
|
Wichtig: Host ist die LAN-IP der Box (NICHT eine Proxy-Domain) — das war in v1
|
|
die häufigste Fehlerquelle. Der Aufrufer übergibt den Host explizit.
|
|
"""
|
|
|
|
import json
|
|
|
|
import httpx
|
|
|
|
from config import LLAMA_SWAP_URL, MEM0_SERVICE_URL, PORT, V1_UPSTREAM
|
|
|
|
DEFAULT_HOST = "192.168.178.151"
|
|
|
|
# IDEs/Agenten bekommen die ECHTEN Box-Modelle direkt (kein Lane-Routing): Coder (bauen), Planer (denken),
|
|
# Augen (Bilder, Vision). Der User wechselt im Modellwähler — spiegelt das reale Hermes-Desktop-Setup 1:1.
|
|
PRIMARY_MODEL = "coder"
|
|
|
|
|
|
def _caps(tools: bool = True, images: bool = False) -> dict:
|
|
"""Volle 7-Feld-Capabilities — braucht sie für die Modellwahl."""
|
|
return {"tools": tools, "images": images, "parallel_tool_calls": False,
|
|
"prompt_cache_key": False, "chat_completions": True,
|
|
"interleaved_reasoning": False, "max_tokens_parameter": False}
|
|
|
|
|
|
# Reale Box-Modelle für die IDE-Welt (getrennt von Lucy): bauen · denken · sehen.
|
|
IDE_MODELS = [
|
|
{"name": "coder", "display_name": "Box / Coder (bauen)", "max_tokens": 131072, "capabilities": _caps(True, False)},
|
|
{"name": "heavy", "display_name": "Box / Planer (denken)", "max_tokens": 32768, "capabilities": _caps(True, False)},
|
|
{"name": "vision", "display_name": "Box / Augen (Bilder)", "max_tokens": 32768, "capabilities": _caps(False, True)},
|
|
]
|
|
|
|
|
|
def _gw(host: str) -> str:
|
|
# Eingebauter Gateway: MC2 serviert /v1 selbst (gleicher Port wie das Cockpit).
|
|
return f"http://{host}:{PORT}/v1"
|
|
|
|
|
|
def build_snippets(host: str = DEFAULT_HOST,
|
|
mcp_script_path: str = r"F:\\Coding Stuff\\mission-control-2\\mcp\\mcp_memory.py",
|
|
mcp_python: str = "python") -> dict:
|
|
gw = _gw(host)
|
|
mc_url = f"http://{host}:{PORT}"
|
|
|
|
# Kilo Code = aktiver Nachfolger der Roo/Cline-Linie (Roo im April 2026 eingestellt).
|
|
# Gleiche Provider-Settings-Struktur; Multi-Agent kommt aus dem Tool (Orchestrator-Modus).
|
|
kilo = json.dumps({
|
|
"apiProvider": "openai",
|
|
"openAiBaseUrl": gw,
|
|
"openAiApiKey": "local",
|
|
"openAiModelId": PRIMARY_MODEL,
|
|
}, indent=2)
|
|
|
|
# Hermes Desktop = Remote-IDE, die auf dem Gateway läuft.
|
|
# Anleitung: Browser aufweisen → Token aus Datei laden → loslegen.
|
|
hermes_desktop = (
|
|
f"# Hermes Desktop — Remote-IDE auf der Box\n"\
|
|
f"#\n"\
|
|
f"# 1. Browser öffnen: http://{host}:9001/hermes-ui\n"\
|
|
f"# (MC2-Proxy auf dem Gateway)\n"\
|
|
f"#\n"\
|
|
f"# 2. Session-Token: auf der Box liegen in\n"\
|
|
f"# ~/.hermes/desktop-gateway-token\n"\
|
|
f"# (den Dateiname kopieren, den TOKEN-WERT NICHT hardcoden!)\n"\
|
|
f"#\n"\
|
|
f"# 3. Token im Browser-Login einfügen — fertig.\n"\
|
|
f"#\n"\
|
|
f"# Modelle im Hermes Desktop: {PRIMARY_MODEL} (bauen), heavy (denken), vision (Bilder)."
|
|
)
|
|
|
|
# Claude Code spricht das Anthropic-Format; der Gateway ist OpenAI-kompatibel und
|
|
# bietet KEIN /v1/messages (verifiziert). Daher braucht es einen kleinen Übersetzer
|
|
# (Anthropic ⇄ OpenAI) als Aufsatz. Die env-Vars sind Claude Codes echte Schnittstelle.
|
|
claude_code = (
|
|
f"# Claude Code spricht das Anthropic-Format — der Gateway ist OpenAI-kompatibel ({gw})\n"
|
|
f"# und hat kein /v1/messages. Dazwischen muss ein Übersetzer (Anthropic ⇄ OpenAI) laufen:\n"
|
|
f"# • claude-code-router (leichtgewichtig, npm)\n"
|
|
f"# • oder LiteLLM mit /v1/messages-Bridge\n"
|
|
f"# Den Übersetzer auf den Gateway zeigen lassen: baseURL={gw}, model=coder, apiKey=local.\n"
|
|
f"# Dann Claude Code auf den lokalen Übersetzer richten (Beispiel-Port 3456):\n"
|
|
f"\n"
|
|
f'export ANTHROPIC_BASE_URL="http://localhost:3456"\n'
|
|
f'export ANTHROPIC_AUTH_TOKEN="local"\n'
|
|
f'export ANTHROPIC_MODEL="coder"'
|
|
)
|
|
|
|
memory_mcp = json.dumps({
|
|
"mcpServers": {
|
|
"mission-control-memory": {
|
|
"command": mcp_python,
|
|
"args": [mcp_script_path],
|
|
"env": {"MC_URL": mc_url},
|
|
}
|
|
}
|
|
}, indent=2)
|
|
|
|
return {
|
|
"host": host,
|
|
"gateway_url": gw,
|
|
"mc_url": mc_url,
|
|
# Leitung 1 — das MODELL. Bewusst NUR 3 Tools (Verdikt 09.07.2026): Hermes Desktop
|
|
# (Remote-IDE im Browser), Kilo Code (VS-Code-Fallback mit Orchestrator-Modus),
|
|
# Claude Code (starke Sessions).
|
|
# Cursor/Continue/Roo/OpenCode entfernt — Roo eingestellt, Rest cloud-first, Terminal
|
|
# oder von Kilo abgedeckt. Das Evolution-Radar (AUTONOMIE_PLAN E4) überwacht die Kategorie.
|
|
"tools": {
|
|
"hermes_desktop": {"label": "Hermes Desktop (empfohlen)", "lang": "bash", "snippet": hermes_desktop,
|
|
"note": "Remote-IDE im Browser — Gateway-URL + Token aus Datei laden. Drei Modelle: "
|
|
"Coder (bauen) · Planer (denken) · Augen (Bilder) — oben im Wähler umschalten."},
|
|
"kilo": {"label": "Kilo Code", "lang": "json", "snippet": kilo,
|
|
"note": "VS Code/JetBrains (schwergewichtiger). OpenAI-Provider → Gateway. "
|
|
"API-Profile je Modus: Code→coder · Architect/Orchestrator→heavy · Ask/Debug→chat."},
|
|
"claude_code": {"label": "Claude Code", "lang": "bash", "snippet": claude_code,
|
|
"note": "Für die starken Sessions. Lokal-Betrieb bräuchte einen Anthropic⇄OpenAI-Übersetzer vor dem Gateway; Gedächtnis-Anbindung via Memory-MCP unten."},
|
|
},
|
|
# Leitung 2 — das GEDÄCHTNIS. Separater MCP-Server, gilt zusätzlich zu jedem Tool oben.
|
|
"memory": {"label": "Shared Memory (MCP)", "lang": "json", "snippet": memory_mcp,
|
|
"note": "Eigene Leitung: MCP-Block für jedes MCP-fähige Tool. mcp_memory.py muss lokal liegen."},
|
|
}
|
|
|
|
|
|
def check_health() -> dict:
|
|
"""Live-Erreichbarkeit der drei Leitungen, aus Sicht der Box:
|
|
Leitung 1 = der ECHTE Modell-Pfad, den Clients nutzen (mc2-gateway bei V1_UPSTREAM,
|
|
sonst llama-swap direkt), Leitung 2 = Gedächtnis-Sidecar (Mem0),
|
|
Leitung 3 = Desktop-Gateway (Hermes-Builtin-UI)."""
|
|
gateway = {"ok": False, "detail": "nicht erreichbar"}
|
|
try:
|
|
with httpx.Client(timeout=3.0) as c:
|
|
r = c.get(f"{V1_UPSTREAM or LLAMA_SWAP_URL}/v1/models")
|
|
if r.status_code == 200:
|
|
n = len(r.json().get("data", []))
|
|
gateway = {"ok": True, "detail": f"{n} Modelle verfügbar" if n else "bereit"}
|
|
else:
|
|
gateway = {"ok": False, "detail": f"HTTP {r.status_code}"}
|
|
except Exception: # noqa: BLE001
|
|
pass
|
|
|
|
memory = {"ok": False, "detail": "nicht erreichbar"}
|
|
try:
|
|
with httpx.Client(timeout=3.0) as c:
|
|
r = c.get(f"{MEM0_SERVICE_URL}/health")
|
|
memory = ({"ok": True, "detail": "bereit"} if r.status_code == 200
|
|
else {"ok": False, "detail": f"HTTP {r.status_code}"})
|
|
except Exception: # noqa: BLE001
|
|
pass
|
|
|
|
desktop_gateway = {"ok": False, "detail": "nicht erreichbar"}
|
|
try:
|
|
with httpx.Client(timeout=3.0) as c:
|
|
r = c.get("http://127.0.0.1:9119/api/status")
|
|
desktop_gateway = ({"ok": True, "detail": "bereit"} if r.status_code == 200
|
|
else {"ok": False, "detail": f"HTTP {r.status_code}"})
|
|
except Exception: # noqa: BLE001
|
|
pass
|
|
|
|
return {"gateway": gateway, "memory": memory, "desktop_gateway": desktop_gateway}
|