feat(2.0): implement cockpit improvements, dynamic mcp path, v2 venv, and ko-residency
This commit is contained in:
+1
-1
@@ -34,7 +34,7 @@ DEFAULT_TTL = int(os.environ.get("MC_DEFAULT_TTL", "300"))
|
||||
HF_DOWNLOAD_ENV = {"HF_HUB_DISABLE_XET": "1"}
|
||||
|
||||
# --- Routing-Gateway (LiteLLM, model: auto) ----------------------------------
|
||||
GATEWAY_URL = os.environ.get("MC_GATEWAY_URL", "http://127.0.0.1:4000").rstrip("/")
|
||||
GATEWAY_URL = os.environ.get("MC_GATEWAY_URL", f"http://127.0.0.1:{os.environ.get('MC_PORT', '9000')}").rstrip("/")
|
||||
GATEWAY_CONFIG_PATH = Path(os.environ.get(
|
||||
"MC_GATEWAY_CONFIG", str(Path(__file__).resolve().parent.parent / "gateway" / "config.yaml")))
|
||||
|
||||
|
||||
@@ -4,3 +4,4 @@ httpx>=0.27
|
||||
ruamel.yaml>=0.18
|
||||
psutil>=5.9
|
||||
huggingface_hub>=0.27
|
||||
mcp>=1.2.0
|
||||
|
||||
@@ -8,5 +8,8 @@ router = APIRouter(prefix="/api")
|
||||
|
||||
|
||||
@router.get("/connect")
|
||||
def connect(host: str = DEFAULT_HOST) -> dict:
|
||||
return build_snippets(host=host)
|
||||
def connect(host: str = DEFAULT_HOST, mcp_path: str | None = None) -> dict:
|
||||
kwargs = {}
|
||||
if mcp_path:
|
||||
kwargs["mcp_script_path"] = mcp_path
|
||||
return build_snippets(host=host, **kwargs)
|
||||
|
||||
@@ -21,7 +21,7 @@ from services.system import system_status
|
||||
router = APIRouter(prefix="/api")
|
||||
|
||||
# Nur diese User-Dienste dürfen neugestartet werden.
|
||||
ALLOWED_SERVICES = {"mission-control-2", "litellm-gateway", "hermes-webui"}
|
||||
ALLOWED_SERVICES = {"mission-control-2", "hermes-gateway", "hermes-webui"}
|
||||
# Quelle für Self-Update (auf der Box ~/mission-control-v2).
|
||||
SOURCE_DIR = os.path.expanduser(os.environ.get("MC2_SOURCE_DIR", "~/mission-control-v2"))
|
||||
|
||||
@@ -35,16 +35,17 @@ def status() -> dict:
|
||||
def services() -> dict:
|
||||
"""Aggregierte Erreichbarkeit aller Stack-Dienste (für die Health-Anzeige)."""
|
||||
a = agent_status()
|
||||
gw_url = f"{GATEWAY_URL}/v1"
|
||||
return {
|
||||
"services": [
|
||||
{"name": "Engine (llama-swap)", "url": LLAMA_SWAP_URL, "ok": engine_reachable()},
|
||||
{"name": "Gateway (LiteLLM)", "url": GATEWAY_URL, "ok": gateway_reachable()},
|
||||
{"name": "Gateway (integriert)", "url": gw_url, "ok": gateway_reachable()},
|
||||
{"name": "Hermes-Gateway", "url": HERMES_API_URL, "ok": a["gateway_reachable"]},
|
||||
{"name": "Hermes-WebUI", "url": HERMES_WEBUI_URL, "ok": a["webui_reachable"]},
|
||||
],
|
||||
"links": {
|
||||
"engine_ui": f"{LLAMA_SWAP_URL}/ui",
|
||||
"gateway": GATEWAY_URL,
|
||||
"gateway": gw_url,
|
||||
"hermes_webui": HERMES_WEBUI_URL,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ Hermes' eigener Config verdrahtet (siehe docs/HERMES_SETUP.md).
|
||||
|
||||
import httpx
|
||||
|
||||
from config import HERMES_API_URL, HERMES_HOME, HERMES_WEBUI_URL
|
||||
from config import HERMES_API_URL, HERMES_HOME, HERMES_WEBUI_URL, yaml
|
||||
|
||||
|
||||
def _reach(url: str, path: str = "") -> bool:
|
||||
@@ -20,12 +20,24 @@ def _reach(url: str, path: str = "") -> bool:
|
||||
def agent_status() -> dict:
|
||||
"""Erreichbarkeit von Gateway (:8642) + WebUI (:8787) + lokale Hinweise."""
|
||||
home = HERMES_HOME
|
||||
brain_model = "auto"
|
||||
config_path = home / "config.yaml"
|
||||
if config_path.exists():
|
||||
try:
|
||||
with config_path.open("r", encoding="utf-8") as f:
|
||||
cfg = yaml.load(f) or {}
|
||||
if isinstance(cfg, dict):
|
||||
brain_model = cfg.get("model", {}).get("model", "auto")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return {
|
||||
"gateway_url": HERMES_API_URL,
|
||||
"webui_url": HERMES_WEBUI_URL,
|
||||
"gateway_reachable": _reach(HERMES_API_URL, "/v1/models"),
|
||||
"webui_reachable": _reach(HERMES_WEBUI_URL),
|
||||
"home_exists": home.exists(),
|
||||
"brain_model": brain_model,
|
||||
# Best-effort: welche Verdrahtung lokal sichtbar ist (auf der Box aussagekräftig).
|
||||
"has_config": (home / "config.yaml").exists() or (home / "config.json").exists(),
|
||||
"has_skills": (home / "skills").exists(),
|
||||
|
||||
@@ -18,7 +18,7 @@ from services import discover, jobengine, llamaswap
|
||||
|
||||
# System-Dienste (root, via sudo -n NOPASSWD) vs. User-Dienste (systemctl --user).
|
||||
SYSTEM_SERVICES = {"llama-swap"}
|
||||
USER_SERVICES = {"mission-control-2", "hermes-gateway", "hermes-dashboard", "hermes-webui", "litellm-gateway"}
|
||||
USER_SERVICES = {"mission-control-2", "hermes-gateway", "hermes-dashboard", "hermes-webui"}
|
||||
|
||||
ENGINE_UPDATE_CMD = os.environ.get("MC_ENGINE_UPDATE_CMD", "")
|
||||
ENGINE_PATH = os.environ.get("MC_ENGINE_PATH", "/opt/llamacpp")
|
||||
|
||||
@@ -69,9 +69,9 @@ delegation:
|
||||
`agent.disabled_toolsets` (gilt für ALLE Plattformen).
|
||||
- **Behalten:** terminal/shell, file, code_execution, skills, todo, session_search, clarify,
|
||||
delegation, cronjob, web. `approvals: auto` (rein lokal).
|
||||
- **MCP-Server** (`mcp_servers` in config) — laufen über die v1-venv (hat das `mcp`-Modul):
|
||||
- geteiltes Gedächtnis: `/opt/mission-control/.venv/bin/python ~/mission-control-v2/mcp/mcp_memory.py` (Env `MC_URL=http://127.0.0.1:9001`)
|
||||
- Stack-Management: `… ~/mission-control-v2/mcp/mcp_mc.py` (Env `MC_URL=http://127.0.0.1:9001`)
|
||||
- **MCP-Server** (`mcp_servers` in config) — laufen über die v2-venv (hat das `mcp`-Modul nach pip install):
|
||||
- geteiltes Gedächtnis: `~/mission-control-v2/backend/.venv/bin/python ~/mission-control-v2/mcp/mcp_memory.py` (Env `MC_URL=http://127.0.0.1:9001`)
|
||||
- Stack-Management: `~/mission-control-v2/backend/.venv/bin/python ~/mission-control-v2/mcp/mcp_mc.py` (Env `MC_URL=http://127.0.0.1:9001`)
|
||||
- **SSH→Windows-PC** (voller Zugriff): OpenSSH-Server auf Windows aktiv + Key
|
||||
`~/.ssh/id_ed25519_hermes_agent` autorisiert; Hermes nutzt sein terminal-Tool für `ssh TobisPC@<win-ip>`.
|
||||
|
||||
|
||||
+25
-25
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
-1
File diff suppressed because one or more lines are too long
Vendored
+2
-2
@@ -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-S7P9XrdI.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-IpcnHTkp.css">
|
||||
<script type="module" crossorigin src="/assets/index-BcRd449w.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-D7pMz4id.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -143,6 +143,7 @@ export interface AgentStatus {
|
||||
gateway_reachable: boolean
|
||||
webui_reachable: boolean
|
||||
home_exists: boolean
|
||||
brain_model?: string
|
||||
has_config: boolean
|
||||
has_skills: boolean
|
||||
has_memories: boolean
|
||||
|
||||
@@ -64,7 +64,7 @@ export function AgentView() {
|
||||
<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="Brain" ok={s.gateway_reachable} detail={`model: ${s.brain_model || "auto"}`} />
|
||||
<Tile label="Verdrahtung" ok={s.has_config} detail={`config ${s.has_config ? "✓" : "—"} · skills ${s.has_skills ? "✓" : "—"} · memories ${s.has_memories ? "✓" : "—"}`} />
|
||||
</div>
|
||||
|
||||
|
||||
@@ -5,22 +5,31 @@ import { cn } from "@/lib/utils"
|
||||
|
||||
export function ConnectView() {
|
||||
const [host, setHost] = useState(localStorage.getItem("mc_host") || "192.168.178.151")
|
||||
const [mcpPath, setMcpPath] = useState(localStorage.getItem("mc_mcp_path") || "")
|
||||
const [data, setData] = useState<ConnectResp | null>(null)
|
||||
const [active, setActive] = useState("cline")
|
||||
const [copied, setCopied] = useState(false)
|
||||
const [error, setError] = useState("")
|
||||
|
||||
useEffect(() => {
|
||||
api<ConnectResp>(`/api/connect?host=${encodeURIComponent(host)}`)
|
||||
const params = new URLSearchParams()
|
||||
params.set("host", host)
|
||||
if (mcpPath) params.set("mcp_path", mcpPath)
|
||||
api<ConnectResp>(`/api/connect?${params}`)
|
||||
.then(setData)
|
||||
.catch((e) => setError(String(e)))
|
||||
}, [host])
|
||||
}, [host, mcpPath])
|
||||
|
||||
function saveHost(v: string) {
|
||||
setHost(v)
|
||||
if (v) localStorage.setItem("mc_host", v)
|
||||
}
|
||||
|
||||
function saveMcpPath(v: string) {
|
||||
setMcpPath(v)
|
||||
localStorage.setItem("mc_mcp_path", v)
|
||||
}
|
||||
|
||||
const tool = data?.tools[active]
|
||||
|
||||
async function copy() {
|
||||
@@ -40,14 +49,24 @@ export function ConnectView() {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
<div className="flex flex-wrap items-center gap-4 text-sm bg-card/40 p-3 rounded-lg border border-border">
|
||||
<div className="flex items-center gap-2">
|
||||
<label className="text-muted-foreground">Box-LAN-IP:</label>
|
||||
<input
|
||||
value={host}
|
||||
onChange={(e) => saveHost(e.target.value)}
|
||||
className="rounded-md border border-border bg-card px-2 py-1 font-mono text-xs outline-none focus:ring-2 focus:ring-ring"
|
||||
className="w-36 rounded-md border border-border bg-background px-2 py-1 font-mono text-xs outline-none focus:ring-2 focus:ring-ring"
|
||||
/>
|
||||
{data && <span className="text-xs text-muted-foreground">→ {data.gateway_url}</span>}
|
||||
</div>
|
||||
<div className="flex items-center gap-2 flex-1 min-w-[280px]">
|
||||
<label className="text-muted-foreground whitespace-nowrap">Lokaler MCP-Pfad:</label>
|
||||
<input
|
||||
value={mcpPath}
|
||||
onChange={(e) => saveMcpPath(e.target.value)}
|
||||
placeholder="z.B. F:\Coding Stuff\mission-control-2\mcp\mcp_memory.py"
|
||||
className="flex-1 rounded-md border border-border bg-background px-2 py-1 font-mono text-xs outline-none focus:ring-2 focus:ring-ring"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
|
||||
Reference in New Issue
Block a user