feat: Workspace-Umbau Schritt 6 — QEMU fuer alle Architekturen + set_project_kind

QEMU auf dem Host + ARIA-Anbindung:
- host-provisioning/qemu-setup.sh: installiert qemu-system-* (x86/arm/mips/ppc/
  sparc/misc), qemu-utils, Firmware, socat, imagemagick + den aria-vm-Helper.
- host-provisioning/aria-vm: VM-Verwaltung fuer JEDE Architektur (create/boot/
  screenshot/list/stop/rm). VNC bindet nur 127.0.0.1:<display> (Tunnel via
  Bridge), KVM nur fuer x86-Gaeste, sonst TCG.
- Brain: Projekt-Modell bekommt kind ('chat'|'code'); neues Tool
  set_project_kind markiert das aktive Projekt (blendet Editor/Desktop in der
  App ein) + feuert project_changed.
- Seed-Regel: ARIA weiss jetzt, dass Code unter /shared/projects/<id>/ gehoert
  und wie sie per `ssh aria-wohnung aria-vm ...` VMs baut/testet; VNC landet
  automatisch in der Desktop-Kachel.

py-compile clean, Host-Skripte bash -n OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-16 23:57:57 +02:00
co-authored by Claude Opus 4.8
parent a6cb152f55
commit a082c8398e
5 changed files with 320 additions and 1 deletions
+185
View File
@@ -0,0 +1,185 @@
#!/usr/bin/env bash
#
# aria-vm — ARIAs QEMU-VM-Verwaltung fuer alle Architekturen (laeuft auf dem
# Host). ARIA ruft das per SSH (aria-wohnung) ueber den qemu-vm-Skill auf.
#
# Unterkommandos:
# aria-vm create <name> <arch> [size] Disk anlegen (qcow2)
# aria-vm boot <name> [optionen] VM starten (VNC 127.0.0.1:<display>)
# aria-vm screenshot <name> PNG-Screenshot → Shared-Uploads
# aria-vm list laufende/vorhandene VMs
# aria-vm stop <name> VM beenden
# aria-vm rm <name> VM + Disk loeschen
#
# boot-Optionen:
# --iso <pfad> Boot-ISO (setzt Boot-Reihenfolge auf CD)
# --disk-boot von der Festplatte booten (Default nach Installation)
# --vnc-display <N> VNC-Display (Port = 5900+N, Default 1)
# --mem <MB> RAM (Default 1024)
# --machine <typ> QEMU-Maschine ueberschreiben
#
# VNC bindet immer nur an 127.0.0.1 — von aussen erreichbar ausschliesslich
# ueber den RVS-Tunnel der Bridge (host.docker.internal:<port>).
set -euo pipefail
VM_ROOT="${ARIA_VM_ROOT:-/var/lib/aria-vms}"
# Wohin Screenshots geschrieben werden — Host-Pfad des /shared-Volumes, damit
# Bridge/App sie sehen. Ueberschreibbar via ARIA_VM_SHOT_DIR.
SHOT_DIR="${ARIA_VM_SHOT_DIR:-/root/ARIA-AGENT/aria-shared/uploads}"
die() { echo "aria-vm: $*" >&2; exit 1; }
qemu_bin_for() {
case "$1" in
x86_64|amd64) echo qemu-system-x86_64 ;;
i386|i686|x86) echo qemu-system-i386 ;;
arm|armv7) echo qemu-system-arm ;;
aarch64|arm64) echo qemu-system-aarch64 ;;
mips) echo qemu-system-mips ;;
mipsel) echo qemu-system-mipsel ;;
mips64) echo qemu-system-mips64 ;;
ppc) echo qemu-system-ppc ;;
ppc64) echo qemu-system-ppc64 ;;
riscv64) echo qemu-system-riscv64 ;;
sparc) echo qemu-system-sparc ;;
*) echo "" ;;
esac
}
vm_dir() { echo "${VM_ROOT}/$1"; }
vm_pid() { local d; d="$(vm_dir "$1")"; [[ -f "${d}/pid" ]] && cat "${d}/pid" || echo ""; }
vm_running() {
local p; p="$(vm_pid "$1")"
[[ -n "${p}" ]] && kill -0 "${p}" 2>/dev/null
}
cmd_create() {
local name="${1:?name}" arch="${2:?arch}" size="${3:-10G}"
local bin; bin="$(qemu_bin_for "${arch}")"
[[ -n "${bin}" ]] || die "unbekannte Architektur: ${arch}"
command -v "${bin}" >/dev/null || die "${bin} nicht installiert (qemu-setup.sh?)"
local d; d="$(vm_dir "${name}")"
[[ -e "${d}/disk.qcow2" ]] && die "VM '${name}' existiert schon"
mkdir -p "${d}"
echo "${arch}" > "${d}/arch"
qemu-img create -f qcow2 "${d}/disk.qcow2" "${size}" >/dev/null
echo "VM '${name}' angelegt (${arch}, ${size})."
}
cmd_boot() {
local name="${1:?name}"; shift || true
local d; d="$(vm_dir "${name}")"
[[ -f "${d}/disk.qcow2" ]] || die "VM '${name}' nicht gefunden (erst 'create')"
vm_running "${name}" && die "VM '${name}' laeuft bereits"
local arch; arch="$(cat "${d}/arch" 2>/dev/null || echo x86_64)"
local bin; bin="$(qemu_bin_for "${arch}")"
local iso="" bootdev="c" display=1 mem=1024 machine=""
while [[ $# -gt 0 ]]; do
case "$1" in
--iso) iso="${2:?}"; bootdev="d"; shift 2 ;;
--disk-boot) bootdev="c"; shift ;;
--vnc-display) display="${2:?}"; shift 2 ;;
--mem) mem="${2:?}"; shift 2 ;;
--machine) machine="${2:?}"; shift 2 ;;
*) die "unbekannte Option: $1" ;;
esac
done
local args=(-name "${name}" -m "${mem}"
-drive "file=${d}/disk.qcow2,format=qcow2"
-vnc "127.0.0.1:${display}"
-monitor "unix:${d}/monitor.sock,server,nowait"
-pidfile "${d}/pid" -daemonize)
# KVM nur fuer x86 auf x86-Host.
case "${arch}" in
x86_64|amd64|i386|i686|x86)
[[ -e /dev/kvm ]] && args+=(-enable-kvm) ;;
esac
# ARM/AArch64 brauchen eine Maschine (kein Default).
if [[ -z "${machine}" ]]; then
case "${arch}" in
arm|armv7|aarch64|arm64) machine="virt" ;;
esac
fi
[[ -n "${machine}" ]] && args+=(-M "${machine}")
[[ -n "${iso}" ]] && args+=(-cdrom "${iso}")
args+=(-boot "${bootdev}")
"${bin}" "${args[@]}"
echo "VM '${name}' gestartet (${arch}) — VNC 127.0.0.1:${display} (Port $((5900+display)))."
echo "vnc_display=${display} vnc_port=$((5900+display))"
}
cmd_screenshot() {
local name="${1:?name}"
local d; d="$(vm_dir "${name}")"
vm_running "${name}" || die "VM '${name}' laeuft nicht"
command -v socat >/dev/null || die "socat fehlt (qemu-setup.sh?)"
mkdir -p "${SHOT_DIR}"
local ts; ts="$(date +%s)"
local ppm="${d}/shot-${ts}.ppm"
printf 'screendump %s\n' "${ppm}" | socat - "unix-connect:${d}/monitor.sock" >/dev/null
sleep 0.3
local out="${SHOT_DIR}/${name}-${ts}.png"
if command -v convert >/dev/null; then
convert "${ppm}" "${out}" && rm -f "${ppm}"
else
out="${SHOT_DIR}/${name}-${ts}.ppm"; mv "${ppm}" "${out}"
fi
echo "screenshot=${out}"
}
cmd_list() {
[[ -d "${VM_ROOT}" ]] || { echo "(keine VMs)"; return; }
local any=0
for d in "${VM_ROOT}"/*/; do
[[ -d "${d}" ]] || continue
any=1
local name arch state
name="$(basename "${d}")"
arch="$(cat "${d}/arch" 2>/dev/null || echo '?')"
if vm_running "${name}"; then state="laeuft (pid $(vm_pid "${name}"))"; else state="gestoppt"; fi
echo "${name} [${arch}] ${state}"
done
[[ "${any}" -eq 1 ]] || echo "(keine VMs)"
}
cmd_stop() {
local name="${1:?name}"
local d; d="$(vm_dir "${name}")"
if vm_running "${name}"; then
printf 'quit\n' | socat - "unix-connect:${d}/monitor.sock" >/dev/null 2>&1 || true
sleep 0.5
vm_running "${name}" && kill "$(vm_pid "${name}")" 2>/dev/null || true
echo "VM '${name}' gestoppt."
else
echo "VM '${name}' lief nicht."
fi
rm -f "${d}/pid" "${d}/monitor.sock"
}
cmd_rm() {
local name="${1:?name}"
vm_running "${name}" && cmd_stop "${name}"
rm -rf "$(vm_dir "${name}")"
echo "VM '${name}' geloescht."
}
main() {
local sub="${1:-}"; shift || true
case "${sub}" in
create) cmd_create "$@" ;;
boot) cmd_boot "$@" ;;
screenshot) cmd_screenshot "$@" ;;
list) cmd_list "$@" ;;
stop) cmd_stop "$@" ;;
rm) cmd_rm "$@" ;;
""|-h|--help)
sed -n '2,40p' "$0" | sed 's/^# \{0,1\}//' ;;
*) die "unbekanntes Kommando: ${sub} (siehe --help)" ;;
esac
}
main "$@"
+53
View File
@@ -0,0 +1,53 @@
#!/usr/bin/env bash
#
# qemu-setup.sh — installiert QEMU fuer ALLE Architekturen auf dem ARIA-Host
# (172.0.2.33) plus den aria-vm-Helper. Einmalig als root ausfuehren.
#
# sudo bash host-provisioning/qemu-setup.sh
#
# KVM-Beschleunigung gibt es nur fuer x86-Gaeste auf einem x86-Host; ARM/MIPS/
# PPC/RISC-V laufen unter TCG (voll emuliert, langsamer, aber alle Architekturen
# baubar). websockify/noVNC werden NICHT installiert — der VNC-Stream wird als
# RFB-Bytes durch die Bridge/RVS getunnelt (siehe aria_bridge.py VNC-Bruecke).
set -euo pipefail
if [[ "${EUID}" -ne 0 ]]; then
echo "Bitte als root ausfuehren (sudo)." >&2
exit 1
fi
echo "[qemu-setup] apt update ..."
apt-get update -qq
echo "[qemu-setup] Installiere QEMU (alle Architekturen) + Werkzeuge ..."
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
qemu-system \
qemu-system-x86 \
qemu-system-arm \
qemu-system-mips \
qemu-system-ppc \
qemu-system-sparc \
qemu-system-misc \
qemu-utils \
seabios \
ovmf \
ipxe-qemu \
socat \
imagemagick
echo "[qemu-setup] KVM-Status:"
if [[ -e /dev/kvm ]]; then
echo " /dev/kvm vorhanden → x86-Gaeste mit KVM-Beschleunigung."
else
echo " /dev/kvm FEHLT → alle Gaeste laufen unter TCG (emuliert, langsamer)."
fi
# aria-vm-Helper installieren (liegt neben diesem Skript).
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
install -m 0755 "${SCRIPT_DIR}/aria-vm" /usr/local/bin/aria-vm
echo "[qemu-setup] /usr/local/bin/aria-vm installiert."
mkdir -p /var/lib/aria-vms
echo "[qemu-setup] VM-Verzeichnis: /var/lib/aria-vms"
echo "[qemu-setup] Fertig. Test: aria-vm list"