feat: VM-Liste pro Projekt (Desktop-Panel) — Start/Stop/Verbinden

- Brain: project_vms.py (Registry /shared/config/project_vms.json pro Projekt) +
  Endpoints GET/POST/DELETE /projects/<id>/vms + boot/stop (via SSH aria-wohnung
  aria-vm auf dem Host; Status aus `aria-vm list`).
- ARIA-Tools vm_register/vm_list (aufs Request-Projekt) + Seed-Regel: nach dem
  VM-Bau registrieren, damit sie in Stefans Desktop-Panel auftaucht.
- App: brainApi VM-Methoden + ProjectVm-Typ. Neues DesktopTile — pro Projekt die
  VM-Liste (leer bis registriert), Start/Stop/Verbinden; Verbinden oeffnet noVNC
  (VncTile mit dem VNC-Port der VM). Deck rendert DesktopTile statt VncTile.

py/tsc clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-20 23:25:55 +02:00
co-authored by Claude Opus 4.8
parent 5fa5d79ad8
commit 0efb8d0848
8 changed files with 429 additions and 5 deletions
+56
View File
@@ -37,6 +37,7 @@ import triggers as triggers_mod
import watcher as watcher_mod
import oauth as oauth_mod
import projects as projects_mod
import project_vms as project_vms_mod
BRIDGE_URL = os.environ.get("BRIDGE_URL", "http://aria-bridge:8090")
# SearXNG (self-hosted Meta-Suche) — Backend fuers web_search-Tool (B1b).
@@ -1161,6 +1162,38 @@ META_TOOLS = [
},
},
},
{
"type": "function",
"function": {
"name": "vm_register",
"description": (
"Traegt eine QEMU-VM in die VM-Liste des AKTUELLEN Projekts ein, "
"damit sie in Stefans Desktop-Panel (Cockpit) erscheint und er sie "
"starten/stoppen/verbinden kann. Rufe das auf, NACHDEM Du mit aria-vm "
"eine VM gebaut/gebootet hast (z.B. bei einem OS-Bau-Projekt). "
"vnc_display bestimmt den VNC-Port (Port = 5900+display)."
),
"parameters": {
"type": "object",
"properties": {
"name": {"type": "string", "description": "VM-Name wie bei aria-vm (a-z0-9_-)."},
"arch": {"type": "string", "description": "z.B. i386, x86_64, aarch64, mips."},
"iso": {"type": "string", "description": "optional: Boot-ISO-Pfad auf dem Host."},
"vnc_display": {"type": "integer", "description": "VNC-Display (Default 1 → Port 5901)."},
"mem": {"type": "integer", "description": "RAM in MB (Default 1024)."},
},
"required": ["name", "arch"],
},
},
},
{
"type": "function",
"function": {
"name": "vm_list",
"description": "Listet die registrierten QEMU-VMs des aktuellen Projekts (mit Lauf-Status).",
"parameters": {"type": "object", "properties": {}},
},
},
]
@@ -2797,6 +2830,29 @@ class Agent:
return f"OK — Projekt '{updated['name']}' ist wieder ein normaler Chat."
if name in ("satellite_list", "satellite_devices", "satellite_command"):
return self._dispatch_satellite(name, arguments)
if name == "vm_register":
pid = (project_id or "").strip()
if not pid:
return "Kein aktives Projekt — VMs werden pro Projekt gefuehrt. Erst ein Projekt betreten."
try:
vm = project_vms_mod.add_vm(
pid, (arguments.get("name") or "").strip(),
(arguments.get("arch") or "i386").strip(),
(arguments.get("iso") or "").strip(),
int(arguments.get("vnc_display") or 1),
int(arguments.get("mem") or 1024),
)
except (ValueError, TypeError) as exc:
return f"FEHLER: {exc}"
return (f"OK — VM '{vm['name']}' ({vm['arch']}, Display :{vm['vnc_display']}) "
f"in Projekt '{pid}' registriert. Erscheint in Stefans Desktop-Panel.")
if name == "vm_list":
pid = (project_id or "").strip()
vms = project_vms_mod.list_vms(pid)
if not vms:
return f"Projekt '{pid or 'Hauptchat'}' hat noch keine registrierten VMs."
return "VMs:\n" + "\n".join(
f"- {v['name']} ({v.get('arch')}, Display :{v.get('vnc_display', 1)})" for v in vms)
return f"Unbekanntes Tool: {name}"
except Exception as exc:
logger.exception("Tool '%s' fehlgeschlagen", name)