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"}
|
HF_DOWNLOAD_ENV = {"HF_HUB_DISABLE_XET": "1"}
|
||||||
|
|
||||||
# --- Routing-Gateway (LiteLLM, model: auto) ----------------------------------
|
# --- 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(
|
GATEWAY_CONFIG_PATH = Path(os.environ.get(
|
||||||
"MC_GATEWAY_CONFIG", str(Path(__file__).resolve().parent.parent / "gateway" / "config.yaml")))
|
"MC_GATEWAY_CONFIG", str(Path(__file__).resolve().parent.parent / "gateway" / "config.yaml")))
|
||||||
|
|
||||||
|
|||||||
@@ -4,3 +4,4 @@ httpx>=0.27
|
|||||||
ruamel.yaml>=0.18
|
ruamel.yaml>=0.18
|
||||||
psutil>=5.9
|
psutil>=5.9
|
||||||
huggingface_hub>=0.27
|
huggingface_hub>=0.27
|
||||||
|
mcp>=1.2.0
|
||||||
|
|||||||
@@ -8,5 +8,8 @@ router = APIRouter(prefix="/api")
|
|||||||
|
|
||||||
|
|
||||||
@router.get("/connect")
|
@router.get("/connect")
|
||||||
def connect(host: str = DEFAULT_HOST) -> dict:
|
def connect(host: str = DEFAULT_HOST, mcp_path: str | None = None) -> dict:
|
||||||
return build_snippets(host=host)
|
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")
|
router = APIRouter(prefix="/api")
|
||||||
|
|
||||||
# Nur diese User-Dienste dürfen neugestartet werden.
|
# 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).
|
# 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"))
|
SOURCE_DIR = os.path.expanduser(os.environ.get("MC2_SOURCE_DIR", "~/mission-control-v2"))
|
||||||
|
|
||||||
@@ -35,16 +35,17 @@ def status() -> dict:
|
|||||||
def services() -> dict:
|
def services() -> dict:
|
||||||
"""Aggregierte Erreichbarkeit aller Stack-Dienste (für die Health-Anzeige)."""
|
"""Aggregierte Erreichbarkeit aller Stack-Dienste (für die Health-Anzeige)."""
|
||||||
a = agent_status()
|
a = agent_status()
|
||||||
|
gw_url = f"{GATEWAY_URL}/v1"
|
||||||
return {
|
return {
|
||||||
"services": [
|
"services": [
|
||||||
{"name": "Engine (llama-swap)", "url": LLAMA_SWAP_URL, "ok": engine_reachable()},
|
{"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-Gateway", "url": HERMES_API_URL, "ok": a["gateway_reachable"]},
|
||||||
{"name": "Hermes-WebUI", "url": HERMES_WEBUI_URL, "ok": a["webui_reachable"]},
|
{"name": "Hermes-WebUI", "url": HERMES_WEBUI_URL, "ok": a["webui_reachable"]},
|
||||||
],
|
],
|
||||||
"links": {
|
"links": {
|
||||||
"engine_ui": f"{LLAMA_SWAP_URL}/ui",
|
"engine_ui": f"{LLAMA_SWAP_URL}/ui",
|
||||||
"gateway": GATEWAY_URL,
|
"gateway": gw_url,
|
||||||
"hermes_webui": HERMES_WEBUI_URL,
|
"hermes_webui": HERMES_WEBUI_URL,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ Hermes' eigener Config verdrahtet (siehe docs/HERMES_SETUP.md).
|
|||||||
|
|
||||||
import httpx
|
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:
|
def _reach(url: str, path: str = "") -> bool:
|
||||||
@@ -20,12 +20,24 @@ def _reach(url: str, path: str = "") -> bool:
|
|||||||
def agent_status() -> dict:
|
def agent_status() -> dict:
|
||||||
"""Erreichbarkeit von Gateway (:8642) + WebUI (:8787) + lokale Hinweise."""
|
"""Erreichbarkeit von Gateway (:8642) + WebUI (:8787) + lokale Hinweise."""
|
||||||
home = HERMES_HOME
|
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 {
|
return {
|
||||||
"gateway_url": HERMES_API_URL,
|
"gateway_url": HERMES_API_URL,
|
||||||
"webui_url": HERMES_WEBUI_URL,
|
"webui_url": HERMES_WEBUI_URL,
|
||||||
"gateway_reachable": _reach(HERMES_API_URL, "/v1/models"),
|
"gateway_reachable": _reach(HERMES_API_URL, "/v1/models"),
|
||||||
"webui_reachable": _reach(HERMES_WEBUI_URL),
|
"webui_reachable": _reach(HERMES_WEBUI_URL),
|
||||||
"home_exists": home.exists(),
|
"home_exists": home.exists(),
|
||||||
|
"brain_model": brain_model,
|
||||||
# Best-effort: welche Verdrahtung lokal sichtbar ist (auf der Box aussagekräftig).
|
# Best-effort: welche Verdrahtung lokal sichtbar ist (auf der Box aussagekräftig).
|
||||||
"has_config": (home / "config.yaml").exists() or (home / "config.json").exists(),
|
"has_config": (home / "config.yaml").exists() or (home / "config.json").exists(),
|
||||||
"has_skills": (home / "skills").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-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-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_UPDATE_CMD = os.environ.get("MC_ENGINE_UPDATE_CMD", "")
|
||||||
ENGINE_PATH = os.environ.get("MC_ENGINE_PATH", "/opt/llamacpp")
|
ENGINE_PATH = os.environ.get("MC_ENGINE_PATH", "/opt/llamacpp")
|
||||||
|
|||||||
@@ -69,9 +69,9 @@ delegation:
|
|||||||
`agent.disabled_toolsets` (gilt für ALLE Plattformen).
|
`agent.disabled_toolsets` (gilt für ALLE Plattformen).
|
||||||
- **Behalten:** terminal/shell, file, code_execution, skills, todo, session_search, clarify,
|
- **Behalten:** terminal/shell, file, code_execution, skills, todo, session_search, clarify,
|
||||||
delegation, cronjob, web. `approvals: auto` (rein lokal).
|
delegation, cronjob, web. `approvals: auto` (rein lokal).
|
||||||
- **MCP-Server** (`mcp_servers` in config) — laufen über die v1-venv (hat das `mcp`-Modul):
|
- **MCP-Server** (`mcp_servers` in config) — laufen über die v2-venv (hat das `mcp`-Modul nach pip install):
|
||||||
- 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`)
|
- 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/mcp/mcp_mc.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→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>`.
|
`~/.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" />
|
<meta name="theme-color" content="#0d1117" />
|
||||||
<link rel="manifest" href="/manifest.webmanifest" />
|
<link rel="manifest" href="/manifest.webmanifest" />
|
||||||
<title>Mission Control 2.0</title>
|
<title>Mission Control 2.0</title>
|
||||||
<script type="module" crossorigin src="/assets/index-S7P9XrdI.js"></script>
|
<script type="module" crossorigin src="/assets/index-BcRd449w.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-IpcnHTkp.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-D7pMz4id.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@@ -143,6 +143,7 @@ export interface AgentStatus {
|
|||||||
gateway_reachable: boolean
|
gateway_reachable: boolean
|
||||||
webui_reachable: boolean
|
webui_reachable: boolean
|
||||||
home_exists: boolean
|
home_exists: boolean
|
||||||
|
brain_model?: string
|
||||||
has_config: boolean
|
has_config: boolean
|
||||||
has_skills: boolean
|
has_skills: boolean
|
||||||
has_memories: boolean
|
has_memories: boolean
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ export function AgentView() {
|
|||||||
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-4">
|
<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="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="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 ? "✓" : "—"}`} />
|
<Tile label="Verdrahtung" ok={s.has_config} detail={`config ${s.has_config ? "✓" : "—"} · skills ${s.has_skills ? "✓" : "—"} · memories ${s.has_memories ? "✓" : "—"}`} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -5,22 +5,31 @@ import { cn } from "@/lib/utils"
|
|||||||
|
|
||||||
export function ConnectView() {
|
export function ConnectView() {
|
||||||
const [host, setHost] = useState(localStorage.getItem("mc_host") || "192.168.178.151")
|
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 [data, setData] = useState<ConnectResp | null>(null)
|
||||||
const [active, setActive] = useState("cline")
|
const [active, setActive] = useState("cline")
|
||||||
const [copied, setCopied] = useState(false)
|
const [copied, setCopied] = useState(false)
|
||||||
const [error, setError] = useState("")
|
const [error, setError] = useState("")
|
||||||
|
|
||||||
useEffect(() => {
|
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)
|
.then(setData)
|
||||||
.catch((e) => setError(String(e)))
|
.catch((e) => setError(String(e)))
|
||||||
}, [host])
|
}, [host, mcpPath])
|
||||||
|
|
||||||
function saveHost(v: string) {
|
function saveHost(v: string) {
|
||||||
setHost(v)
|
setHost(v)
|
||||||
if (v) localStorage.setItem("mc_host", 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]
|
const tool = data?.tools[active]
|
||||||
|
|
||||||
async function copy() {
|
async function copy() {
|
||||||
@@ -40,14 +49,24 @@ export function ConnectView() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</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>
|
<label className="text-muted-foreground">Box-LAN-IP:</label>
|
||||||
<input
|
<input
|
||||||
value={host}
|
value={host}
|
||||||
onChange={(e) => saveHost(e.target.value)}
|
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>
|
</div>
|
||||||
|
|
||||||
{error && (
|
{error && (
|
||||||
|
|||||||
Reference in New Issue
Block a user