perf(frontend): v3-Umbau P0 — Ballast raus, Startbuendel halbiert
Erste Etappe des v3-Umbaus. Bewusst ohne jede Architektur-Aenderung: nur Entruempeln, damit der Rest des Umbaus auf einem messbar leichten Stand aufsetzt. Gemessen (vorher -> nachher): dist gesamt 27 375 720 B -> 1 477 852 B (-94,6 %) Dateien in dist 135 -> 35 Start-Chunk gzip 220 278 B -> 110 571 B (-49,8 %) CSS gzip 31 577 B -> 15 121 B (-52,1 %) Schrift-Dateien 112 -> 11 (WOFF 1: 0) Vier Eingriffe: 1) avatar.vrm (24,5 MB) entfernt. Lag in public/ und dist/, wurde von KEINER Zeile des Repos referenziert — der Renderer Avatar3D.tsx war schon vorher verschwunden. War 89,5 % der Nutzlast, die auf die Box ging. Damit fallen auch die .gitignore-Sonderregel und der Direkt-Deploy-Schritt weg. DISASTER_RECOVERY.md §3·D ehrlich auf "ausgebaut" gesetzt (7 Stellen). 2) Schriften: die 11 @fontsource-Sammelimporte zogen ALLE Subsets (latin-ext, griechisch, kyrillisch) und je eine WOFF-1-Fassung mit — 112 Dateien, 1,58 MB, davon 902 kB WOFF 1, das kein Browser dieser App je abruft. Jetzt stehen die @font-face-Regeln direkt in index.css: nur latin, nur WOFF 2, nur die Schnitte, die per grep ueber die font-*-Klassen wirklich belegt sind. Nebenbei behoben: JetBrains Mono 600/700 fehlten komplett — die 19 Stellen mit `font-mono font-bold/semibold` wurden vom Browser synthetisch fettgerechnet. Jetzt echte Schnitte; bei Monospace ist die Laufweite gleich, kein Layout-Versatz. 3) Recharts aus dem Startbuendel. Das Cockpit ist die Startseite und laedt daher NICHT lazy; ueber SystemStatusCard/TokenPerformanceCard zog es Recharts samt d3 in index-*.js. LiveAreaChart ist jetzt eine Lazy-Huelle (Suspense mit hoehengleichem Platzhalter, damit nichts springt), die Recharts-Umsetzung liegt in LiveAreaChartImpl.tsx und kommt als eigener Chunk nach (105 kB gzip). 4) index.css: height 100dvh mit 100% als Rueckfall. Auf Mobilbrowsern mit einfahrender Adressleiste ist 100 % nicht die sichtbare Hoehe. Dazu ein Buendel-Budget in beiden Ampel-Dateien (.gitea/ = MC2-Fassung, deploy/ = universelle Vorlage): Start-Chunk und dist-Gesamtgroesse werden am FRISCHEN Build im Runner gemessen. Bewusst kein Vergleich mit dem committeten dist — der waere ueber Node-Versionen hinweg flatterhaft und wuerde dauerhaft rot leuchten, was das Signal zerstoert. Verifiziert gegen die Box (Frontend-Dev mit MC_API_TARGET=192.168.178.151:9001): Cockpit rendert mit Live-Daten, keine Konsolenfehler, alle 11 Schriftschnitte registriert, LiveAreaChartImpl + recharts laden nachweislich als Nachlade-Chunk. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
3d1881f4bc
commit
8aa22e6591
@@ -42,7 +42,34 @@ jobs:
|
||||
rot=1
|
||||
else
|
||||
( cd frontend && npm ci --no-audit --no-fund && npm run build ) || rot=1
|
||||
|
||||
|
||||
# Bündel-Budget (v3-Umbau P0, 28.08.2026): Der Start-Chunk ist das, was der
|
||||
# Nutzer VOR dem ersten Bild lädt. Er lag bei 220 kB gzip, weil das eifrig
|
||||
# geladene Cockpit Recharts mitzog; dist lag bei 27 MB wegen eines verwaisten
|
||||
# Avatar-Modells. Ohne Deckel wächst beides unbemerkt zurück. Gemessen wird der
|
||||
# FRISCHE Build im Runner — kein Vergleich mit dem committeten dist (das wäre
|
||||
# über Node-Versionen hinweg flatterhaft und würde dauerhaft rot leuchten).
|
||||
if [ $rot -eq 0 ] && [ -d frontend/dist/assets ]; then
|
||||
budget_gz=125000 # Stand nach P0: 110 571 B gzip
|
||||
budget_dist=3145728 # Stand nach P0: 1 477 852 B
|
||||
haupt=$(ls frontend/dist/assets/index-*.js 2>/dev/null | head -1)
|
||||
if [ -n "$haupt" ]; then
|
||||
gz=$(gzip -c "$haupt" | wc -c)
|
||||
echo "-- Start-Chunk: $gz B gzip (Budget $budget_gz)"
|
||||
if [ "$gz" -gt "$budget_gz" ]; then
|
||||
echo "❌ Start-Bündel über Budget. Meist eine neue Bibliothek, die über"
|
||||
echo " das eifrig geladene Cockpit hereinkommt — hinter lazy() legen."
|
||||
rot=1
|
||||
fi
|
||||
fi
|
||||
gesamt=$(du -sb frontend/dist | cut -f1)
|
||||
echo "-- dist gesamt: $gesamt B (Budget $budget_dist)"
|
||||
if [ "$gesamt" -gt "$budget_dist" ]; then
|
||||
echo "❌ dist über Budget — Ballast? (Schrift-Subsets, Medien, WOFF 1)"
|
||||
rot=1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $rot -eq 0 ]; then
|
||||
echo "== Frontend: Push nach release-dist =="
|
||||
git config --global user.name "Gitea Actions"
|
||||
|
||||
+19
-24
@@ -1,24 +1,19 @@
|
||||
# Python
|
||||
backend/.venv/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Node / Vite
|
||||
frontend/node_modules/
|
||||
# frontend/dist wird committet (kein Node-Build auf der Box) — siehe deploy/
|
||||
|
||||
# Avatar-VRM (groß + lizenz-/redistributionssensibel) — liegt lokal + auf der Box, nicht in git.
|
||||
# Wird per Direkt-Deploy auf die Box gespielt (dist/avatar.vrm), nicht über git.
|
||||
frontend/public/avatar.vrm
|
||||
frontend/dist/avatar.vrm
|
||||
|
||||
# Env / local
|
||||
*.env
|
||||
.DS_Store
|
||||
|
||||
# Box-Recon-/Scratch-Skripte (lokale Diagnose, nicht fürs Repo)
|
||||
box_recon*
|
||||
gemma_swap*
|
||||
|
||||
# TypeScript-Inkrementalcache (reines Build-Artefakt, maschinenabhängig)
|
||||
frontend/tsconfig.tsbuildinfo
|
||||
# Python
|
||||
backend/.venv/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Node / Vite
|
||||
frontend/node_modules/
|
||||
# frontend/dist wird committet (kein Node-Build auf der Box) — siehe deploy/
|
||||
|
||||
# Env / local
|
||||
*.env
|
||||
.DS_Store
|
||||
|
||||
# Box-Recon-/Scratch-Skripte (lokale Diagnose, nicht fürs Repo)
|
||||
box_recon*
|
||||
gemma_swap*
|
||||
|
||||
# TypeScript-Inkrementalcache (reines Build-Artefakt, maschinenabhängig)
|
||||
frontend/tsconfig.tsbuildinfo
|
||||
|
||||
@@ -1,68 +1,67 @@
|
||||
# AGENTS.md — Mission Control 2.0
|
||||
|
||||
Projekt-Geschmack für Coding-Agenten (Kilo Code, Claude Code lesen diese Datei).
|
||||
Kurz gehalten, nur die Wahrheiten, die man sonst schmerzhaft lernt. Details: `README.md`, `docs/`.
|
||||
|
||||
## Was das ist
|
||||
Lokaler Local-AI-Stack für die Box (Bosgame M5, Strix Halo, Vulkan/RADV). Schichten:
|
||||
Engine (llama-swap) · Builtin-Routing-Gateway (`model: auto`) · MC2 (FastAPI + React/shadcn) ·
|
||||
**Hermes-Agent (das autonome Gehirn "Lucy")**. Backend Python (Box läuft Python 3.14), Frontend Vite/React/shadcn/Tailwind.
|
||||
|
||||
**Hardware-Spezifikationen der Box (WICHTIG für alle Agenten):**
|
||||
- **System:** Bosgame M5 (AMD Strix Halo APU)
|
||||
- **Arbeitsspeicher (RAM/VRAM):** 128 GB Shared Memory (ca. 122.7 GB nutzbar).
|
||||
- **Inference-Limit:** Modelle im GGUF-Format dürfen maximal ca. 100-110 GB groß sein (entspricht ca. 150B Parametern bei Q4_K_M). Größere Modelle (wie 200B+ oder 2T Parameter) **können lokal nicht ausgeführt werden** und sind auszuschließen, es sei denn, es handelt sich um stark quantisierte MoEs.
|
||||
**Gedächtnis & Dienste:** Hermes ist das autonome Agenten-Gehirn („Lucy") **und die alleinige Gedächtnis-Wahrheit** — es führt sein Gedächtnis selbst. Der frühere Sidecar auf `:8765` samt Memory-MCP-Server und MC2-Memory-Plugin wurde am 07.08.2026 abgelöst und am 27.08.2026 restlos ausgebaut (`docs/wissen/VERDIKTE.md`): MC2 hat **keine** `/api/memory`-Routen mehr, nichts darf mehr dorthin greifen. Governor, Zed-Workflows und alte Mittelschichten sind komplett gelöscht.
|
||||
|
||||
## Sprache (nicht verhandelbar)
|
||||
- **Alles User-Sichtbare ist Deutsch:** UI-Texte, Fehlermeldungen, Update-/Radar-Meldungen,
|
||||
Skills, LLM-Summaries, Commit-Messages, Code-Kommentare.
|
||||
- **Hermes' INTERNE System-Prompts bleiben Englisch** (fremde Software, wir forken sie nicht).
|
||||
Antwort-Sprache ≠ Prompt-Sprache — Deutsch kommt aus SOUL.md, nicht aus den Tool-Prompts.
|
||||
|
||||
## Build & Deploy (die häufigste Falle)
|
||||
- **`frontend/dist` WIRD committet.** Auf der Box läuft KEIN Node-Build; das Backend liefert die
|
||||
gebauten Assets direkt aus. Nach jeder Frontend-Änderung: `cd frontend && npm run build`, dann
|
||||
**das neue `frontend/dist` mit-committen**. Vergessen = Box zeigt alten Stand.
|
||||
(Ausnahme: `frontend/dist/avatar.vrm` ist bewusst nicht in git — siehe `.gitignore`.)
|
||||
- **Deploy macht `git reset --hard origin/main`** (`deploy/deploy.sh`). Heißt: **`main` muss vor
|
||||
dem Deploy auf Gitea liegen**, und uncommittete Box-Änderungen gehen verloren (Absicht).
|
||||
- **Nie direkt auf `main` arbeiten.** Immer Branch (`wartung/...`), Gate grün, dann Merge/Deploy.
|
||||
|
||||
## Agentic IDE & Vibe Coding
|
||||
- **Zero Middle-Layers:** Coding passiert zu 100% lokal auf dem Dev-PC in der **OpenCode Desktop IDE**.
|
||||
- Es gibt keinen Zed-Workflow, keine OpenCode-CLI-Mittelschicht und keinen Governor mehr.
|
||||
- Der Agent in OpenCode Desktop nutzt via MCP (`.agents/mcp_config.json`) die API der Box (`:9001/v1`), um autonom Projekte zu bauen.
|
||||
- **Mix-Ansatz beim Testen:** Der Agent testet lokal. Vor dem Push muss er prüfen, ob das Gitea-`VERIFY`-Skript fehlerfrei durchläuft.
|
||||
|
||||
## Zeit & Umgebung
|
||||
- **Box = Ubuntu, läuft in `Europe/Berlin`** (seit 03.07.2026; vorher UTC). Dev-PC = Windows.
|
||||
Naive/lokale Zeiten immer über `MC_LOCAL_TZ` (= `Europe/Berlin`) auflösen, nie `datetime.now()` ohne TZ annehmen
|
||||
(siehe `backend/services/reminders.py`).
|
||||
|
||||
## Backend-Konventionen
|
||||
- Router unter `backend/routers/` (`APIRouter(prefix="/api")`), Logik in `backend/services/`
|
||||
(Single Source of Truth — Router bleiben dünn). Beispiel-Lehre: Restart-Allowlist lebt NUR in
|
||||
`services.maintenance` (System-Dienste via `sudo -n`, User-Dienste via `systemctl --user`);
|
||||
keine zweite Allowlist in einem Router duplizieren.
|
||||
- **Gate vor Commit:** `python -m py_compile <geänderte .py>` muss durchlaufen.
|
||||
- Wartung ist **sudo-frei** gedacht (systemctl --user). Wo doch sudo nötig ist (llama-swap =
|
||||
System-Dienst), sauber über die NOPASSWD-Whitelist / `password_required`-Rückgabe, nie hart failen.
|
||||
- ‼️ **Die feingranularen sudoers.d-Regeln sind faktisch wirkungslos.** In `/etc/sudoers` steht
|
||||
`hitonabi ALL=(ALL) NOPASSWD: ALL` — der Nutzer, unter dem alle MC2-Dienste laufen, darf ohnehin
|
||||
alles passwortlos. `sudoers.d/mc2-autonomie` und `sudoers.d/mission-control` dokumentieren also,
|
||||
was gebraucht *würde*, schränken aber nichts ein. Das ist eine bewusste Entscheidung für die
|
||||
Single-User-Appliance; verlasse dich beim Bauen nicht darauf, dass eine Whitelist dich bremst.
|
||||
Wer das wirklich härten will, kommt um einen eigenen Service-User nicht herum — die Pauschalzeile
|
||||
einfach zu ziehen, bricht OS-Update, Config-Sync und den wöchentlichen Auto-Neustart still.
|
||||
(Geprüft 27.08.2026; die doppelte Zeile wurde damals entfernt, Sicherung `/root/sudoers.bak-*`.)
|
||||
- Lokal (Windows) müssen Box-Shell-Befehle **harmlos fehlschlagen** statt zu crashen.
|
||||
|
||||
## Grenzen (Verdikt, eingehalten)
|
||||
- **Hermes-Quellcode nie selbst patchen** (Fork verboten). Config/Deps ja, Code-Umbau nein.
|
||||
- **Security-Config** (approvals, Tokens, ufw, command_allowlist) **nie ohne explizites User-Ja.**
|
||||
- Alles reversibel halten: Branch + Backup + Pin.
|
||||
|
||||
## Gitea
|
||||
Remote = `Hitonabi/mission-control-v2`. Auth ist flatterhaft → **Push mit Retry**, nur über
|
||||
PowerShell/GCM. Neue Repos per API anlegen. Kein GitHub.
|
||||
# AGENTS.md — Mission Control 2.0
|
||||
|
||||
Projekt-Geschmack für Coding-Agenten (Kilo Code, Claude Code lesen diese Datei).
|
||||
Kurz gehalten, nur die Wahrheiten, die man sonst schmerzhaft lernt. Details: `README.md`, `docs/`.
|
||||
|
||||
## Was das ist
|
||||
Lokaler Local-AI-Stack für die Box (Bosgame M5, Strix Halo, Vulkan/RADV). Schichten:
|
||||
Engine (llama-swap) · Builtin-Routing-Gateway (`model: auto`) · MC2 (FastAPI + React/shadcn) ·
|
||||
**Hermes-Agent (das autonome Gehirn "Lucy")**. Backend Python (Box läuft Python 3.14), Frontend Vite/React/shadcn/Tailwind.
|
||||
|
||||
**Hardware-Spezifikationen der Box (WICHTIG für alle Agenten):**
|
||||
- **System:** Bosgame M5 (AMD Strix Halo APU)
|
||||
- **Arbeitsspeicher (RAM/VRAM):** 128 GB Shared Memory (ca. 122.7 GB nutzbar).
|
||||
- **Inference-Limit:** Modelle im GGUF-Format dürfen maximal ca. 100-110 GB groß sein (entspricht ca. 150B Parametern bei Q4_K_M). Größere Modelle (wie 200B+ oder 2T Parameter) **können lokal nicht ausgeführt werden** und sind auszuschließen, es sei denn, es handelt sich um stark quantisierte MoEs.
|
||||
**Gedächtnis & Dienste:** Hermes ist das autonome Agenten-Gehirn („Lucy") **und die alleinige Gedächtnis-Wahrheit** — es führt sein Gedächtnis selbst. Der frühere Sidecar auf `:8765` samt Memory-MCP-Server und MC2-Memory-Plugin wurde am 07.08.2026 abgelöst und am 27.08.2026 restlos ausgebaut (`docs/wissen/VERDIKTE.md`): MC2 hat **keine** `/api/memory`-Routen mehr, nichts darf mehr dorthin greifen. Governor, Zed-Workflows und alte Mittelschichten sind komplett gelöscht.
|
||||
|
||||
## Sprache (nicht verhandelbar)
|
||||
- **Alles User-Sichtbare ist Deutsch:** UI-Texte, Fehlermeldungen, Update-/Radar-Meldungen,
|
||||
Skills, LLM-Summaries, Commit-Messages, Code-Kommentare.
|
||||
- **Hermes' INTERNE System-Prompts bleiben Englisch** (fremde Software, wir forken sie nicht).
|
||||
Antwort-Sprache ≠ Prompt-Sprache — Deutsch kommt aus SOUL.md, nicht aus den Tool-Prompts.
|
||||
|
||||
## Build & Deploy (die häufigste Falle)
|
||||
- **`frontend/dist` WIRD committet.** Auf der Box läuft KEIN Node-Build; das Backend liefert die
|
||||
gebauten Assets direkt aus. Nach jeder Frontend-Änderung: `cd frontend && npm run build`, dann
|
||||
**das neue `frontend/dist` mit-committen**. Vergessen = Box zeigt alten Stand.
|
||||
- **Deploy macht `git reset --hard origin/main`** (`deploy/deploy.sh`). Heißt: **`main` muss vor
|
||||
dem Deploy auf Gitea liegen**, und uncommittete Box-Änderungen gehen verloren (Absicht).
|
||||
- **Nie direkt auf `main` arbeiten.** Immer Branch (`wartung/...`), Gate grün, dann Merge/Deploy.
|
||||
|
||||
## Agentic IDE & Vibe Coding
|
||||
- **Zero Middle-Layers:** Coding passiert zu 100% lokal auf dem Dev-PC in der **OpenCode Desktop IDE**.
|
||||
- Es gibt keinen Zed-Workflow, keine OpenCode-CLI-Mittelschicht und keinen Governor mehr.
|
||||
- Der Agent in OpenCode Desktop nutzt via MCP (`.agents/mcp_config.json`) die API der Box (`:9001/v1`), um autonom Projekte zu bauen.
|
||||
- **Mix-Ansatz beim Testen:** Der Agent testet lokal. Vor dem Push muss er prüfen, ob das Gitea-`VERIFY`-Skript fehlerfrei durchläuft.
|
||||
|
||||
## Zeit & Umgebung
|
||||
- **Box = Ubuntu, läuft in `Europe/Berlin`** (seit 03.07.2026; vorher UTC). Dev-PC = Windows.
|
||||
Naive/lokale Zeiten immer über `MC_LOCAL_TZ` (= `Europe/Berlin`) auflösen, nie `datetime.now()` ohne TZ annehmen
|
||||
(siehe `backend/services/reminders.py`).
|
||||
|
||||
## Backend-Konventionen
|
||||
- Router unter `backend/routers/` (`APIRouter(prefix="/api")`), Logik in `backend/services/`
|
||||
(Single Source of Truth — Router bleiben dünn). Beispiel-Lehre: Restart-Allowlist lebt NUR in
|
||||
`services.maintenance` (System-Dienste via `sudo -n`, User-Dienste via `systemctl --user`);
|
||||
keine zweite Allowlist in einem Router duplizieren.
|
||||
- **Gate vor Commit:** `python -m py_compile <geänderte .py>` muss durchlaufen.
|
||||
- Wartung ist **sudo-frei** gedacht (systemctl --user). Wo doch sudo nötig ist (llama-swap =
|
||||
System-Dienst), sauber über die NOPASSWD-Whitelist / `password_required`-Rückgabe, nie hart failen.
|
||||
- ‼️ **Die feingranularen sudoers.d-Regeln sind faktisch wirkungslos.** In `/etc/sudoers` steht
|
||||
`hitonabi ALL=(ALL) NOPASSWD: ALL` — der Nutzer, unter dem alle MC2-Dienste laufen, darf ohnehin
|
||||
alles passwortlos. `sudoers.d/mc2-autonomie` und `sudoers.d/mission-control` dokumentieren also,
|
||||
was gebraucht *würde*, schränken aber nichts ein. Das ist eine bewusste Entscheidung für die
|
||||
Single-User-Appliance; verlasse dich beim Bauen nicht darauf, dass eine Whitelist dich bremst.
|
||||
Wer das wirklich härten will, kommt um einen eigenen Service-User nicht herum — die Pauschalzeile
|
||||
einfach zu ziehen, bricht OS-Update, Config-Sync und den wöchentlichen Auto-Neustart still.
|
||||
(Geprüft 27.08.2026; die doppelte Zeile wurde damals entfernt, Sicherung `/root/sudoers.bak-*`.)
|
||||
- Lokal (Windows) müssen Box-Shell-Befehle **harmlos fehlschlagen** statt zu crashen.
|
||||
|
||||
## Grenzen (Verdikt, eingehalten)
|
||||
- **Hermes-Quellcode nie selbst patchen** (Fork verboten). Config/Deps ja, Code-Umbau nein.
|
||||
- **Security-Config** (approvals, Tokens, ufw, command_allowlist) **nie ohne explizites User-Ja.**
|
||||
- Alles reversibel halten: Branch + Backup + Pin.
|
||||
|
||||
## Gitea
|
||||
Remote = `Hitonabi/mission-control-v2`. Auth ist flatterhaft → **Push mit Retry**, nur über
|
||||
PowerShell/GCM. Neue Repos per API anlegen. Kein GitHub.
|
||||
|
||||
@@ -67,6 +67,18 @@ jobs:
|
||||
(cd "$d" && npm ci --no-audit --no-fund) || { rot=1; continue; }
|
||||
if grep -q '"build"' "$pkg"; then (cd "$d" && npm run build) || rot=1; fi
|
||||
if grep -q '"test"' "$pkg"; then (cd "$d" && npm test --silent) || rot=1; fi
|
||||
|
||||
# Buendel-Budget: das Start-Chunk ist, was der Nutzer VOR dem ersten Bild laedt.
|
||||
# Ohne Deckel waechst es unbemerkt zurueck (eine eifrig geladene Diagramm-Lib
|
||||
# reicht). Gemessen wird der frische Build, nicht das committete dist.
|
||||
if [ -d "$d/dist/assets" ]; then
|
||||
haupt=$(ls "$d"/dist/assets/index-*.js 2>/dev/null | head -1)
|
||||
if [ -n "$haupt" ]; then
|
||||
gz=$(gzip -c "$haupt" | wc -c)
|
||||
echo "-- Start-Chunk: $gz B gzip (Budget 200000)"
|
||||
[ "$gz" -gt 200000 ] && { echo "❌ Start-Buendel ueber Budget — hinter lazy() legen."; rot=1; }
|
||||
fi
|
||||
fi
|
||||
done <<< "$pkg_dateien"
|
||||
fi
|
||||
|
||||
|
||||
+325
-328
@@ -1,328 +1,325 @@
|
||||
# Konzept: Bare-Metal-Wiederaufbau + First-Run-Wizard (VOLLSTÄNDIG)
|
||||
|
||||
> **Zweck:** Plan für den „hard crash"-Fall — die Box (`tobisniceaiarbeitstier`, Ubuntu 26.04,
|
||||
> AMD Ryzen AI MAX+ 395 / gfx1151, 122 GB RAM) muss von **null** wiederherstellbar sein.
|
||||
> **Anspruch:** *ALLES* ist erfasst — Engine, Hermes-Konfig 1:1, 3D-Avatare, Voice/Klonstimme,
|
||||
> Memory, Browser, MCP, Skills, Secrets. Pro Komponente entscheidet der Nutzer im Wizard:
|
||||
> **1:1 zurück** · **neu/Default** · **weglassen**.
|
||||
>
|
||||
> Dieses Dokument ist die **Spezifikation für eine künftige Implementierungs-Session** — es baut
|
||||
> noch nichts. Stand: 2026-06-28.
|
||||
|
||||
---
|
||||
|
||||
## 1. Leitprinzip: 1:1 ODER modular — der Nutzer wählt
|
||||
|
||||
Der Wizard behandelt jede Komponente als **eigene Kachel mit drei Modi**:
|
||||
|
||||
| Modus | Bedeutung |
|
||||
|---|---|
|
||||
| 📦 **1:1 aus Backup** | Exakter alter Zustand wird zurückgespielt (Configs, Daten, Klonstimme, Avatare). |
|
||||
| 🆕 **Neu / Default** | Frische Installation mit sinnvollen Defaults (z.B. entfesseltes Hermes-Profil, Default-Avatar). |
|
||||
| ⏭️ **Weglassen** | Komponente wird (vorerst) nicht installiert. |
|
||||
|
||||
Ein „Alles 1:1"-Knopf wählt überall 📦 (wo ein Backup existiert), sonst 🆕. So bekommt der Nutzer
|
||||
entweder den exakten alten Stand oder kann gezielt entrümpeln.
|
||||
|
||||
---
|
||||
|
||||
## 2. Was es schon gibt (wiederverwenden)
|
||||
|
||||
| Asset | Datei | Deckt ab |
|
||||
|---|---|---|
|
||||
| Code-Deploy | `deploy/deploy.sh` | git pull + venvs + Units + Restart. **Setzt Engine/llama-swap/Dirs/venvs voraus.** |
|
||||
| Zustands-Backup | `deploy/backup.sh` + `mc2-backup.{service,timer}` | **Aktuell** (live): mem0, `~/.hermes/{config.yaml,.env,plugins}`, llama-swap-config. Retention 14. **Für echtes 1:1 zu erweitern** — fertiges Snippet in **Anhang B** (§5). |
|
||||
| Restore | `deploy/restore.sh` | Spielt Tarball zurück (mit Pre-Restore-Sicherung). |
|
||||
| Engine | `deploy/provision-engine.sh` | llama.cpp Vulkan/RADV-Build (root). |
|
||||
| Voice-Setup | `voice_service/install.sh` | venv + Piper-Binary + dt. Piper-Stimmen + Chatterbox (best-effort). |
|
||||
| Updates | `deploy/update-engine.sh`, `update-swap.sh` | Laufende Engine/Router-Updates. |
|
||||
| Postchecks | `deploy/stack-postcheck.sh`, `hermes-postcheck.sh` | Funktionsprüfung → Wizard-Verifikationsschritt. |
|
||||
|
||||
---
|
||||
|
||||
## 3. VOLLSTÄNDIGER Komponenten-Katalog
|
||||
|
||||
> **Scan-verifiziert (2026-06-28)** gegen `backend/config.py`, alle `backend/services/*` & `routers/*`,
|
||||
> `frontend/src/{nav.ts,views,components/voice}`, `voice_service/app.py`, `mem0_service/`, `deploy/*`.
|
||||
> Alle persistenten Schreibpfade, Dienste, Secrets und Env-Vars sind unten erfasst.
|
||||
|
||||
Legende Restore-Quelle: 📦 = aus Backup-Tarball · ⬇️ = Re-Download/Install (Skript) · 🌐 = Git ·
|
||||
🔑 = Secret (Nutzer/Generieren) · 🆕 = im Wizard neu gewählt.
|
||||
„Im Backup?" = deckt der **aktuelle** `backup.sh` es ab.
|
||||
|
||||
### A · OS & System (L0, root/sudo, einmalig)
|
||||
| Komponente | Ort | Quelle | Im Backup? |
|
||||
|---|---|---|---|
|
||||
| Ubuntu 26.04, User `hitonabi`, `enable-linger` | — | ⬇️ manuell | n/a |
|
||||
| System-Pakete: python3.14+venv, git, curl, jq, ttyd, uv | — | ⬇️ apt/curl | nein |
|
||||
| Vulkan-Stack: mesa-vulkan-drivers, libvulkan1, vulkan-tools | — | ⬇️ apt | nein |
|
||||
| Chrome-Libs (Browser): `agent-browser install --with-deps` | — | ⬇️ apt | nein |
|
||||
| Verzeichnis-Layout: `/srv/models/{,mem0,mc2-backups,drafts}`, `/etc/llama-swap/` | — | ⬇️ mkdir+chown | nein |
|
||||
|
||||
### B · Inferenz-Engine + Router (L1, root)
|
||||
| Komponente | Ort | Quelle | Im Backup? |
|
||||
|---|---|---|---|
|
||||
| llama.cpp (Vulkan) → `llama-server` | `/opt/llamacpp-vulkan`, `/usr/local/bin/llama-server` | ⬇️ provision-engine.sh / update-engine.sh (ggml-org Release) | nein (re-build) |
|
||||
| **llama-swap** Binary (Router `:8080`) | `/usr/local/bin/llama-swap` | ⬇️ **bekannt:** `mostlygeek/llama-swap`-Release (Rezept in `update-swap.sh`) | nein (re-download) |
|
||||
| **llama-swap systemd-System-Unit** | `/etc/systemd/system/llama-swap.service` (+ `.d/` Drop-ins) | ⬇️ Bootstrap legt sie an — **kompletter Unit-Inhalt in Anhang A** (von der Box abgegriffen; Drop-ins via provision-engine.sh) | nein |
|
||||
| llama-swap-Config (Modelle/Rollen) | `/etc/llama-swap/config.yaml` | 📦 | **ja** |
|
||||
| Draft-Modelle (Spec-Decoding) | `/srv/models/drafts/` | ⬇️ Re-Download | nein |
|
||||
|
||||
### C · MC2-App (L2, userspace)
|
||||
| Komponente | Ort | Quelle | Im Backup? |
|
||||
|---|---|---|---|
|
||||
| MC2-Code | `~/mission-control-v2` | 🌐 Git (Gitea) | n/a |
|
||||
| Backend-venv (Python 3.14) | `backend/.venv` | ⬇️ deploy.sh | nein (rebuild) |
|
||||
| Frontend (gebaut, inkl. **3D-Avatar `avatar.vrm`**) | `frontend/dist`, `frontend/public/avatar.vrm` | 🌐 Git | n/a |
|
||||
| systemd-User-Dienst `mission-control-2` (`:9001`) | `~/.config/systemd/user/` | ⬇️ deploy.sh | nein |
|
||||
|
||||
### D · 3D-Avatar (Sprechen-Tab)
|
||||
| Komponente | Ort | Quelle | Im Backup? |
|
||||
|---|---|---|---|
|
||||
| Default-Avatar (VRM) | `frontend/public/avatar.vrm` (→ dist) | 🌐 Git | n/a |
|
||||
| Renderer | `frontend/src/components/voice/Avatar3D.tsx` | 🌐 Git | n/a |
|
||||
| **Eigene/zusätzliche Avatare** (falls Nutzer welche ablegt) | **TODO: Ablageort definieren** (z.B. `/srv/models/avatars/` + DB-Verweis) | 📦/🆕 | **nein (Lücke)** |
|
||||
|
||||
> Heute ist der Avatar **fest** (`avatar.vrm`, kommt mit dem Git-Frontend zurück). Wenn künftig
|
||||
> Nutzer-Avatare hochgeladen werden, brauchen sie einen persistenten Ablageort, der ins Backup geht.
|
||||
|
||||
### E · Voice-Sidecar (STT + TTS + Klonstimme)
|
||||
| Komponente | Ort | Quelle | Im Backup? |
|
||||
|---|---|---|---|
|
||||
| Voice-venv (Python 3.12) | `~/.voice/venv` | ⬇️ install.sh | nein (rebuild) |
|
||||
| STT (faster-whisper Modell) | `~/.voice/` (Cache) | ⬇️ install.sh / 1. Start | nein |
|
||||
| Piper-Binary + dt. Stimmen (thorsten/kerstin) | `~/.voice/piper`, `~/.voice/voices` | ⬇️ install.sh | nein |
|
||||
| Chatterbox (Premium-TTS, CPU-torch) | venv | ⬇️ install.sh (best-effort) | nein |
|
||||
| **Klonstimme / Voice-Referenz-Audio** (Nutzer) | `~/.voice/refs/ref.wav` (`VOICE_REFS_DIR`, `/api/voice/reference`) | 📦 | **nein → Anhang B** |
|
||||
| ElevenLabs-Key | `~/.hermes/.env` | 🔑 | ja |
|
||||
| Stimm-/Lautstärke-Wahl | Browser-`localStorage` (pro Gerät) | 🆕 client | n/a |
|
||||
| Dienst `voice-service` (`:8650`) | systemd-User | ⬇️ deploy.sh | nein |
|
||||
|
||||
### F · Mem0 (Langzeitgedächtnis)
|
||||
| Komponente | Ort | Quelle | Im Backup? |
|
||||
|---|---|---|---|
|
||||
| Mem0-venv (Python 3.12, uv) | `~/.mem0/venv` | ⬇️ deploy.sh | nein (rebuild) |
|
||||
| **Gedächtnis-Daten (Chroma + history.db)** | `/srv/models/mem0/` | 📦 | **ja** |
|
||||
| Hermes-Memory-Plugin | `~/.hermes/plugins/mc2-memory/` | 📦/🌐 | ja |
|
||||
| Dienst `mem0-service` (`:8765`) | systemd-User | ⬇️ deploy.sh | nein |
|
||||
|
||||
### G · Hermes-Agent (das Herz)
|
||||
| Komponente | Ort | Quelle | Im Backup? |
|
||||
|---|---|---|---|
|
||||
| Hermes-Code | `~/.hermes/hermes-agent` | 🌐 Git (NousResearch) | nein (re-clone) |
|
||||
| Bundled Node v22 | `~/.hermes/node/bin` | ⬇️ (kommt mit Hermes) | nein |
|
||||
| Hermes-venv | `~/.hermes/hermes-agent/venv` | ⬇️ rebuild | nein |
|
||||
| **`config.yaml` 1:1** (Toolsets, MCP, Engine, Browser, Personalities, alles) | `~/.hermes/config.yaml` | 📦 | **ja** |
|
||||
| **`.env` (Secrets: TELEGRAM_BOT_TOKEN, API_SERVER_KEY, ElevenLabs …)** | `~/.hermes/.env` | 📦/🔑 | **ja** |
|
||||
| Plugins | `~/.hermes/plugins/` | 📦 | ja |
|
||||
| **Skills (eigene)** | `~/.hermes/skills/` (45M; `.hub`-Cache re-downloadbar) | 📦 | **nein → Anhang B (ohne .hub)** |
|
||||
| Sessions/History (optional) | `~/.hermes/sessions/` (856K) | 📦 | **nein → Anhang B** |
|
||||
| Checkpoints (optional) | `~/.hermes/checkpoints/` (existiert nicht) | ⏭️ skip | nein |
|
||||
| **Token-/Ersparnis-Statistik** | `~/.hermes/token_stats.json` | 📦 | **nein → Anhang B** |
|
||||
| Dienst `hermes-gateway` (`:8642`) | systemd-User | ⬇️ | nein |
|
||||
| Desktop-Gateway `hermes-builtin-ui` (`:9119`) | systemd-User | ⬇️ deploy.sh | nein |
|
||||
| Desktop-Gateway-Token-Drop-in | `~/.config/systemd/user/hermes-builtin-ui.service.d/session-token.conf` | 🔑 neu erzeugen (Wert auch in `~/.hermes/desktop-gateway-token`, chmod 600) | **nein** |
|
||||
| Desktop-Gateway-Token-Kopie | `~/.hermes/desktop-gateway-token` (chmod 600) | 🔑 | **nein** |
|
||||
|
||||
### H · MCP-Server (4)
|
||||
| Komponente | Ort | Quelle | Im Backup? |
|
||||
|---|---|---|---|
|
||||
| `mission-control-memory`, `-stack` (MC2-venv) | `~/mission-control-v2/mcp/*.py` | 🌐 Git | über config.yaml |
|
||||
| `hermes-pc-control`, `hermes-web-fetch` (Hermes-venv) | dito | 🌐 Git | über config.yaml |
|
||||
| MCP-Verdrahtung | `~/.hermes/config.yaml → mcp_servers` | 📦 | ja |
|
||||
|
||||
### I · Browser-Stack
|
||||
| Komponente | Ort | Quelle | Im Backup? |
|
||||
|---|---|---|---|
|
||||
| agent-browser (npm global) | `~/.local/...`, symlink `~/.hermes/node/bin` | ⬇️ npm i -g | nein |
|
||||
| Chrome (engine) + System-Libs | `~/.agent-browser/browsers/` + apt-Libs | ⬇️ install --with-deps | nein |
|
||||
| lightpanda (optional, leicht) | `~/.local/bin/lightpanda` | ⬇️ Download | nein |
|
||||
| Browser-Verdrahtung (engine=chrome, toolset) | `~/.hermes/config.yaml` | 📦 | ja |
|
||||
|
||||
### J · Modelle (GGUF — der große Brocken)
|
||||
| Komponente | Ort | Quelle | Im Backup? |
|
||||
|---|---|---|---|
|
||||
| GGUF-Modelle (Brain, fast, heavy, coder, vision, embedding …) | `/srv/models/` | ⬇️ Re-Download aus llama-swap-Manifest | **nein (zu groß, bewusst)** |
|
||||
|
||||
### K · Extern (anderer Rechner)
|
||||
| Komponente | Ort | Quelle | Im Backup? |
|
||||
|---|---|---|---|
|
||||
| PC-Executor (Windows) | `client/hermes-pc/` → Scheduled Task | 🌐 Git + Task | n/a (anderer Host) |
|
||||
| Hermes Desktop (Windows-App) | `%LOCALAPPDATA%\hermes` + `connection.json` in `%APPDATA%\Hermes\` | ⬇️ Hermes-Setup.exe + Remote-URL/Token neu eintragen | n/a (anderer Host) |
|
||||
|
||||
---
|
||||
|
||||
## 4. Ziel-Architektur
|
||||
|
||||
### 4.1 `deploy/bootstrap.sh` — orchestrierter From-Zero-Lauf
|
||||
- **Idempotent & resumierbar** (Schritt-Marker in `~/.mc2-bootstrap.state`).
|
||||
- **Getrennt nach sudo-Bedarf**: `bootstrap-root.sh` (L0+L1, bewusst mit sudo) + `bootstrap.sh`
|
||||
(L2+, sudo-frei = Kern ist `deploy.sh`). Passt zum Nordstern „Runtime ohne sudo".
|
||||
- **Mündet in den Wizard**: startet MC2 im First-Run-Modus, gibt Wizard-URL aus.
|
||||
|
||||
Phasen: `0 Vorflug → 1[sudo] System+Dirs → 2[sudo] Engine+llama-swap → 3 Code(MC2+Hermes+Node)
|
||||
→ 4 venvs(backend/mem0/voice/hermes) → 5 Browser → 6 deploy.sh(Units/enable/restart)
|
||||
→ 7 Restore(optional) → 8 Wizard hoch + postcheck`.
|
||||
|
||||
### 4.2 First-Run-Wizard (browserbasiert, von MC2 serviert)
|
||||
MC2 erkennt unkonfigurierten Zustand → Frontend-Route `/setup` statt Dashboard. Schritte:
|
||||
|
||||
1. **Systemcheck** — Live-Ampel je Komponente aus §3 (`GET /api/setup/status` + `…/system/services`).
|
||||
2. **Restore-Quelle wählen** — Backup-Tarball erkennen → globaler Modus „Alles 1:1" / „selektiv" / „frisch".
|
||||
3. **Komponenten-Auswahl (Kernstück)** — pro Katalog-Eintrag aus §3 eine Kachel mit
|
||||
📦/🆕/⏭️ (siehe §1). Zeigt Größe + ob Backup-Daten vorhanden.
|
||||
4. **Netzwerk & Identität** — Box-IP (→ `HERMES_TERMINAL_URL`, `PC_EXECUTOR_URL`), Hostname.
|
||||
5. **Secrets** 🔑 — `TELEGRAM_BOT_TOKEN`, erlaubte User-ID, `API_SERVER_KEY` (oder generieren),
|
||||
ElevenLabs-Key → `~/.hermes/.env` (chmod 600). Bei 📦 vorbefüllt aus Backup.
|
||||
6. **Hermes-Profil** — Brain-Alias + Toolset-Profil (Default = entfesselt: vision/tts/memory/browser
|
||||
an, image_gen/video aus — siehe [[project-hermes-setup]]). Bei 📦 = exakte alte `config.yaml`.
|
||||
7. **Avatar & Voice** — Avatar wählen (Default-VRM oder eigener), Stimme/Klonstimme
|
||||
(📦 Referenz-Audio zurück, oder neu aufnehmen/hochladen).
|
||||
8. **Modelle** — aus llama-swap-Manifest automatisch nachladen (`POST /api/models/install`,
|
||||
Fortschritt `GET /api/jobs`) ODER geführte Discover-Neuauswahl. Reihenfolge: Brain → heavy → Rest.
|
||||
9. **Externe Checkliste** — PC-Executor (Windows-Task), Hermes Desktop (App + Token) als Haken.
|
||||
10. **Verifikation & Abschluss** — `stack-postcheck.sh` + `hermes-postcheck.sh` → grün/rot-Liste → Dashboard.
|
||||
|
||||
**Technik:** neuer Router `backend/routers/setup.py`
|
||||
(`GET /api/setup/status`, `POST /api/setup/{secrets,network,hermes,components,finish}`).
|
||||
First-Run-Gate: MC2 prüft beim Boot Marker `~/.mc2-setup-done` bzw. Pflicht-Secrets → leitet auf `/setup`.
|
||||
|
||||
---
|
||||
|
||||
## 5. Backup-Scope für echtes 1:1 ERWEITERN (umzusetzen — Snippet in Anhang B)
|
||||
|
||||
Der **aktuelle** `backup.sh` (live auf der Box, unverändert) reicht für „1:1" nicht. Für die Bau-Session
|
||||
liegt das **fertige Erweiterungs-Snippet in Anhang B** — es ergänzt den Tarball um:
|
||||
|
||||
- **Voice-Klonstimme** → `~/.voice/refs/` (`ref.wav`)
|
||||
- **Token-/Ersparnis-Statistik** → `~/.hermes/token_stats.json`
|
||||
- **Hermes-Skills** → `~/.hermes/skills/` (re-downloadbarer `.hub`-Cache per `tar --exclude` ausgelassen)
|
||||
- **Hermes-Sessions/History** → `~/.hermes/sessions/`
|
||||
|
||||
Restore soll skills/sessions **mergen** (frisch geladenen `.hub` nicht überschreiben) und `voice-service`
|
||||
mit neu starten.
|
||||
|
||||
**Offen bleibt:** eigene Avatare (sobald Upload existiert — Ablageort heute undefiniert, siehe §3·D).
|
||||
|
||||
**Bewusst NICHT im Backup (re-downloadbar/rebuildbar):** Piper-Stimmen (install.sh), `.hub`-Skill-Cache,
|
||||
`/srv/models/mc2-discover.json` (Cache), `/srv/models/mc2-memory.db` (Legacy-Migration), alle venvs, GGUF-Modelle.
|
||||
|
||||
→ Aufgabe der Bau-Session: `backup.sh` + `restore.sh` um diese Pfade erweitern (mit klarer
|
||||
„opt-in für große/optionale Teile"-Logik), und den MANIFEST-Inhalt entsprechend.
|
||||
|
||||
---
|
||||
|
||||
## 6. Secrets-Strategie
|
||||
- Single Source: `~/.hermes/.env` (chmod 600), im Tarball (selbst 600).
|
||||
- Rebuild ohne Backup → Wizard-Schritt 5 erzeugt sie. `API_SERVER_KEY` generierbar; Telegram/ElevenLabs liefert Nutzer.
|
||||
- Nie ins Git, nie in Logs, nie in die MC2-DB. **Off-Box-Spiegelung des Tarballs noch offen** ([[mc2-backup-restore]]).
|
||||
|
||||
## 7. Modell-Strategie
|
||||
- **A (empfohlen):** `/etc/llama-swap/config.yaml` (im Backup) listet alle Modelle → Wizard leitet Repos/Quants ab und lädt automatisch (`/api/models/install`). „Ein Klick, lädt über Nacht."
|
||||
- **B:** geführte Discover-Neuauswahl je Rolle. Reihenfolge Brain → heavy → Rest, danach `warmup.sh`.
|
||||
|
||||
---
|
||||
|
||||
## 8. Implementierungs-Reihenfolge (neue Session)
|
||||
1. ✅ **Box-Verifikation erledigt (2026-06-28, nur gelesen — nichts verändert):** llama-swap.service-Inhalt
|
||||
in **Anhang A**; skills/sessions/refs/token_stats existieren (Pfade in §3 verifiziert); Backup-Erweiterung
|
||||
als fertiges Snippet in **Anhang B**. **Keine offenen Box-Fragen mehr** außer §10·3–6. → mit Schritt 2 starten.
|
||||
2. `backup.sh`/`restore.sh` um die 1:1-Lücken erweitern (§5).
|
||||
3. `deploy/bootstrap.sh` + `bootstrap-root.sh` (Phasen, State-Marker); llama-swap-Install skripten.
|
||||
4. `backend/routers/setup.py` + First-Run-Gate.
|
||||
5. Frontend `/setup`-Wizard (Schritte 1–10), Komponenten-Auswahl als Kernstück; bestehende Views
|
||||
(Cockpit/AgentView/Sprechen) wiederverwenden.
|
||||
6. Modell-Restore-aus-Manifest.
|
||||
7. **Abnahme in frischer VM** (§9).
|
||||
|
||||
Vorschlag: 2–4 zuerst (Skelett lauffähig), Wizard danach. Branch + PR.
|
||||
|
||||
## 9. Abnahmekriterien
|
||||
- Frisches Ubuntu 26.04 → `bootstrap-root.sh` + `bootstrap.sh` → laufender Stack, kein Spezialwissen.
|
||||
- Postchecks grün; Telegram, Voice (inkl. Klonstimme bei 📦), 3D-Avatar, Browser funktionieren.
|
||||
- „Alles 1:1" stellt mem0, Hermes-config.yaml, Secrets, Klonstimme, Skills exakt wieder her.
|
||||
- Selektiver Modus: einzelne Komponenten ⏭️ überspringbar, Stack läuft trotzdem.
|
||||
- Idempotenz: zweiter Lauf = No-op.
|
||||
|
||||
## 10. Offene Entscheidungen (vor dem Bau)
|
||||
1. ~~llama-swap systemd-Unit-Inhalt~~ → **geklärt: kompletter Unit in Anhang A** (in der Bau-Session anlegen).
|
||||
2. ~~Voice-Referenz-Audio-Ort~~ → **geklärt: `~/.voice/refs/`** (Backup-Snippet in Anhang B, in der Bau-Session umsetzen).
|
||||
3. **Eigene Avatare**: künftiger Upload-/Ablage-Mechanismus + Backup-Pfad (einziger offener 1:1-Daten-Punkt).
|
||||
4. **Off-Box-Backup-Ziel** (NAS/2. Platte/Cloud) — [[mc2-backup-restore]].
|
||||
5. **Python 3.14** auf frischem Ubuntu beschaffen (deadsnakes?).
|
||||
6. **Bootstrap-sudo-Modell**: getrenntes `bootstrap-root.sh` (empfohlen) vs. interaktives sudo.
|
||||
|
||||
## 11. Referenzen
|
||||
- `backend/config.py`, [[project-mc2-architecture]], [[project-stack-state]]
|
||||
- [[project-hermes-setup]], `docs/HERMES_SETUP.md` (entfesseltes Profil, Browser, PC-Executor)
|
||||
- [[voice-sprechen-feature]] (Voice + 3D-Avatar), [[mem0-memory-architecture]], [[mc2-backup-restore]]
|
||||
- `deploy/{deploy,backup,restore,provision-engine,stack-postcheck,warmup}.sh`, `voice_service/install.sh`
|
||||
|
||||
---
|
||||
|
||||
## Anhang A — `llama-swap.service` (1:1 von der Box abgegriffen, 2026-06-28)
|
||||
|
||||
Base-Unit nach `/etc/systemd/system/llama-swap.service` (root). User/GFX sind boxspezifisch
|
||||
(hitonabi, gfx1151 → HSA 11.5.1) — auf anderer Hardware anpassen. Die `.d/`-Drop-ins
|
||||
(`vulkan.conf`, `warmup.conf`) legt `provision-engine.sh` an.
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=llama-swap (lokaler LLM Router)
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=hitonabi
|
||||
Environment=HSA_OVERRIDE_GFX_VERSION=11.5.1
|
||||
Environment=PATH=/usr/local/bin:/usr/bin:/bin
|
||||
ExecStart=/usr/local/bin/llama-swap --config /etc/llama-swap/config.yaml --listen 0.0.0.0:8080 --watch-config
|
||||
Restart=on-failure
|
||||
RestartSec=3
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
```ini
|
||||
# /etc/systemd/system/llama-swap.service.d/vulkan.conf
|
||||
[Service]
|
||||
Environment=LD_LIBRARY_PATH=/opt/llamacpp-vulkan
|
||||
```
|
||||
```ini
|
||||
# /etc/systemd/system/llama-swap.service.d/warmup.conf
|
||||
[Service]
|
||||
ExecStartPost=-/usr/local/bin/llama-swap-warmup.sh
|
||||
```
|
||||
|
||||
**Erstinstall-Reihenfolge:** `bash deploy/update-swap.sh` (Binary) → Unit kopieren →
|
||||
`sudo bash deploy/provision-engine.sh` (Engine+Drop-ins) → `sudo systemctl enable --now llama-swap`.
|
||||
|
||||
---
|
||||
|
||||
## Anhang B — Backup-Scope-Erweiterung für echtes 1:1 (fertiges Snippet)
|
||||
|
||||
In `deploy/backup.sh` ergänzen (Variablen oben: `VOICE="${VOICE_HOME:-$HOME/.voice}"`,
|
||||
Stage zusätzlich `"$STAGE/voice"`):
|
||||
|
||||
```bash
|
||||
# 1:1-Erweiterung (scan-verifiziert 2026-06-28):
|
||||
[ -f "$HERMES/token_stats.json" ] && cp -a "$HERMES/token_stats.json" "$STAGE/hermes/" || true
|
||||
[ -d "$HERMES/skills" ] && cp -a "$HERMES/skills" "$STAGE/hermes/skills" || true
|
||||
[ -d "$HERMES/sessions" ] && cp -a "$HERMES/sessions" "$STAGE/hermes/sessions" || true
|
||||
[ -d "$VOICE/refs" ] && cp -a "$VOICE/refs" "$STAGE/voice/refs" || true
|
||||
```
|
||||
tar-Aufruf um den re-downloadbaren Skill-Cache erleichtern:
|
||||
```bash
|
||||
tar --exclude='./hermes/skills/.hub' -czf "$OUT" -C "$STAGE" .
|
||||
```
|
||||
In `deploy/restore.sh` spiegelbildlich (skills/sessions **mergen**, nicht ersetzen) und
|
||||
`voice-service` mit neustarten:
|
||||
```bash
|
||||
VOICE="${VOICE_HOME:-$HOME/.voice}"
|
||||
SERVICES="mem0-service voice-service mission-control-2 hermes-gateway"
|
||||
[ -f "$STAGE/hermes/token_stats.json" ] && cp -a "$STAGE/hermes/token_stats.json" "$HERMES/"
|
||||
[ -d "$STAGE/hermes/skills" ] && { mkdir -p "$HERMES/skills"; cp -a "$STAGE/hermes/skills/." "$HERMES/skills/"; }
|
||||
[ -d "$STAGE/hermes/sessions" ] && { mkdir -p "$HERMES/sessions"; cp -a "$STAGE/hermes/sessions/." "$HERMES/sessions/"; }
|
||||
[ -d "$STAGE/voice/refs" ] && { mkdir -p "$VOICE/refs"; cp -a "$STAGE/voice/refs/." "$VOICE/refs/"; }
|
||||
```
|
||||
# Konzept: Bare-Metal-Wiederaufbau + First-Run-Wizard (VOLLSTÄNDIG)
|
||||
|
||||
> **Zweck:** Plan für den „hard crash"-Fall — die Box (`tobisniceaiarbeitstier`, Ubuntu 26.04,
|
||||
> AMD Ryzen AI MAX+ 395 / gfx1151, 122 GB RAM) muss von **null** wiederherstellbar sein.
|
||||
> **Anspruch:** *ALLES* ist erfasst — Engine, Hermes-Konfig 1:1, Voice/Klonstimme,
|
||||
> Memory, Browser, MCP, Skills, Secrets. Pro Komponente entscheidet der Nutzer im Wizard:
|
||||
> **1:1 zurück** · **neu/Default** · **weglassen**.
|
||||
>
|
||||
> Dieses Dokument ist die **Spezifikation für eine künftige Implementierungs-Session** — es baut
|
||||
> noch nichts. Stand: 2026-06-28.
|
||||
|
||||
---
|
||||
|
||||
## 1. Leitprinzip: 1:1 ODER modular — der Nutzer wählt
|
||||
|
||||
Der Wizard behandelt jede Komponente als **eigene Kachel mit drei Modi**:
|
||||
|
||||
| Modus | Bedeutung |
|
||||
|---|---|
|
||||
| 📦 **1:1 aus Backup** | Exakter alter Zustand wird zurückgespielt (Configs, Daten, Klonstimme). |
|
||||
| 🆕 **Neu / Default** | Frische Installation mit sinnvollen Defaults (z.B. entfesseltes Hermes-Profil). |
|
||||
| ⏭️ **Weglassen** | Komponente wird (vorerst) nicht installiert. |
|
||||
|
||||
Ein „Alles 1:1"-Knopf wählt überall 📦 (wo ein Backup existiert), sonst 🆕. So bekommt der Nutzer
|
||||
entweder den exakten alten Stand oder kann gezielt entrümpeln.
|
||||
|
||||
---
|
||||
|
||||
## 2. Was es schon gibt (wiederverwenden)
|
||||
|
||||
| Asset | Datei | Deckt ab |
|
||||
|---|---|---|
|
||||
| Code-Deploy | `deploy/deploy.sh` | git pull + venvs + Units + Restart. **Setzt Engine/llama-swap/Dirs/venvs voraus.** |
|
||||
| Zustands-Backup | `deploy/backup.sh` + `mc2-backup.{service,timer}` | **Aktuell** (live): mem0, `~/.hermes/{config.yaml,.env,plugins}`, llama-swap-config. Retention 14. **Für echtes 1:1 zu erweitern** — fertiges Snippet in **Anhang B** (§5). |
|
||||
| Restore | `deploy/restore.sh` | Spielt Tarball zurück (mit Pre-Restore-Sicherung). |
|
||||
| Engine | `deploy/provision-engine.sh` | llama.cpp Vulkan/RADV-Build (root). |
|
||||
| Voice-Setup | `voice_service/install.sh` | venv + Piper-Binary + dt. Piper-Stimmen + Chatterbox (best-effort). |
|
||||
| Updates | `deploy/update-engine.sh`, `update-swap.sh` | Laufende Engine/Router-Updates. |
|
||||
| Postchecks | `deploy/stack-postcheck.sh`, `hermes-postcheck.sh` | Funktionsprüfung → Wizard-Verifikationsschritt. |
|
||||
|
||||
---
|
||||
|
||||
## 3. VOLLSTÄNDIGER Komponenten-Katalog
|
||||
|
||||
> **Scan-verifiziert (2026-06-28)** gegen `backend/config.py`, alle `backend/services/*` & `routers/*`,
|
||||
> `frontend/src/{nav.ts,views,components/voice}`, `voice_service/app.py`, `mem0_service/`, `deploy/*`.
|
||||
> Alle persistenten Schreibpfade, Dienste, Secrets und Env-Vars sind unten erfasst.
|
||||
|
||||
Legende Restore-Quelle: 📦 = aus Backup-Tarball · ⬇️ = Re-Download/Install (Skript) · 🌐 = Git ·
|
||||
🔑 = Secret (Nutzer/Generieren) · 🆕 = im Wizard neu gewählt.
|
||||
„Im Backup?" = deckt der **aktuelle** `backup.sh` es ab.
|
||||
|
||||
### A · OS & System (L0, root/sudo, einmalig)
|
||||
| Komponente | Ort | Quelle | Im Backup? |
|
||||
|---|---|---|---|
|
||||
| Ubuntu 26.04, User `hitonabi`, `enable-linger` | — | ⬇️ manuell | n/a |
|
||||
| System-Pakete: python3.14+venv, git, curl, jq, ttyd, uv | — | ⬇️ apt/curl | nein |
|
||||
| Vulkan-Stack: mesa-vulkan-drivers, libvulkan1, vulkan-tools | — | ⬇️ apt | nein |
|
||||
| Chrome-Libs (Browser): `agent-browser install --with-deps` | — | ⬇️ apt | nein |
|
||||
| Verzeichnis-Layout: `/srv/models/{,mem0,mc2-backups,drafts}`, `/etc/llama-swap/` | — | ⬇️ mkdir+chown | nein |
|
||||
|
||||
### B · Inferenz-Engine + Router (L1, root)
|
||||
| Komponente | Ort | Quelle | Im Backup? |
|
||||
|---|---|---|---|
|
||||
| llama.cpp (Vulkan) → `llama-server` | `/opt/llamacpp-vulkan`, `/usr/local/bin/llama-server` | ⬇️ provision-engine.sh / update-engine.sh (ggml-org Release) | nein (re-build) |
|
||||
| **llama-swap** Binary (Router `:8080`) | `/usr/local/bin/llama-swap` | ⬇️ **bekannt:** `mostlygeek/llama-swap`-Release (Rezept in `update-swap.sh`) | nein (re-download) |
|
||||
| **llama-swap systemd-System-Unit** | `/etc/systemd/system/llama-swap.service` (+ `.d/` Drop-ins) | ⬇️ Bootstrap legt sie an — **kompletter Unit-Inhalt in Anhang A** (von der Box abgegriffen; Drop-ins via provision-engine.sh) | nein |
|
||||
| llama-swap-Config (Modelle/Rollen) | `/etc/llama-swap/config.yaml` | 📦 | **ja** |
|
||||
| Draft-Modelle (Spec-Decoding) | `/srv/models/drafts/` | ⬇️ Re-Download | nein |
|
||||
|
||||
### C · MC2-App (L2, userspace)
|
||||
| Komponente | Ort | Quelle | Im Backup? |
|
||||
|---|---|---|---|
|
||||
| MC2-Code | `~/mission-control-v2` | 🌐 Git (Gitea) | n/a |
|
||||
| Backend-venv (Python 3.14) | `backend/.venv` | ⬇️ deploy.sh | nein (rebuild) |
|
||||
| Frontend (gebaut) | `frontend/dist` | 🌐 Git | n/a |
|
||||
| systemd-User-Dienst `mission-control-2` (`:9001`) | `~/.config/systemd/user/` | ⬇️ deploy.sh | nein |
|
||||
|
||||
### D · 3D-Avatar — **ausgebaut (28.08.2026)**
|
||||
|
||||
MC2 hat keinen Avatar mehr. `frontend/public/avatar.vrm` (24,5 MB) lag im Auslieferungsordner,
|
||||
wurde aber von keiner Zeile des Frontends referenziert — der Renderer `Avatar3D.tsx` war schon
|
||||
vorher verschwunden. Beides ist beim v3-Umbau (Etappe P0) entfernt worden; damit fällt auch die
|
||||
`.gitignore`-Sonderregel und der Direkt-Deploy-Schritt weg. **Nichts wiederherzustellen.**
|
||||
|
||||
### E · Voice-Sidecar (STT + TTS + Klonstimme)
|
||||
| Komponente | Ort | Quelle | Im Backup? |
|
||||
|---|---|---|---|
|
||||
| Voice-venv (Python 3.12) | `~/.voice/venv` | ⬇️ install.sh | nein (rebuild) |
|
||||
| STT (faster-whisper Modell) | `~/.voice/` (Cache) | ⬇️ install.sh / 1. Start | nein |
|
||||
| Piper-Binary + dt. Stimmen (thorsten/kerstin) | `~/.voice/piper`, `~/.voice/voices` | ⬇️ install.sh | nein |
|
||||
| Chatterbox (Premium-TTS, CPU-torch) | venv | ⬇️ install.sh (best-effort) | nein |
|
||||
| **Klonstimme / Voice-Referenz-Audio** (Nutzer) | `~/.voice/refs/ref.wav` (`VOICE_REFS_DIR`, `/api/voice/reference`) | 📦 | **nein → Anhang B** |
|
||||
| ElevenLabs-Key | `~/.hermes/.env` | 🔑 | ja |
|
||||
| Stimm-/Lautstärke-Wahl | Browser-`localStorage` (pro Gerät) | 🆕 client | n/a |
|
||||
| Dienst `voice-service` (`:8650`) | systemd-User | ⬇️ deploy.sh | nein |
|
||||
|
||||
### F · Mem0 (Langzeitgedächtnis)
|
||||
| Komponente | Ort | Quelle | Im Backup? |
|
||||
|---|---|---|---|
|
||||
| Mem0-venv (Python 3.12, uv) | `~/.mem0/venv` | ⬇️ deploy.sh | nein (rebuild) |
|
||||
| **Gedächtnis-Daten (Chroma + history.db)** | `/srv/models/mem0/` | 📦 | **ja** |
|
||||
| Hermes-Memory-Plugin | `~/.hermes/plugins/mc2-memory/` | 📦/🌐 | ja |
|
||||
| Dienst `mem0-service` (`:8765`) | systemd-User | ⬇️ deploy.sh | nein |
|
||||
|
||||
### G · Hermes-Agent (das Herz)
|
||||
| Komponente | Ort | Quelle | Im Backup? |
|
||||
|---|---|---|---|
|
||||
| Hermes-Code | `~/.hermes/hermes-agent` | 🌐 Git (NousResearch) | nein (re-clone) |
|
||||
| Bundled Node v22 | `~/.hermes/node/bin` | ⬇️ (kommt mit Hermes) | nein |
|
||||
| Hermes-venv | `~/.hermes/hermes-agent/venv` | ⬇️ rebuild | nein |
|
||||
| **`config.yaml` 1:1** (Toolsets, MCP, Engine, Browser, Personalities, alles) | `~/.hermes/config.yaml` | 📦 | **ja** |
|
||||
| **`.env` (Secrets: TELEGRAM_BOT_TOKEN, API_SERVER_KEY, ElevenLabs …)** | `~/.hermes/.env` | 📦/🔑 | **ja** |
|
||||
| Plugins | `~/.hermes/plugins/` | 📦 | ja |
|
||||
| **Skills (eigene)** | `~/.hermes/skills/` (45M; `.hub`-Cache re-downloadbar) | 📦 | **nein → Anhang B (ohne .hub)** |
|
||||
| Sessions/History (optional) | `~/.hermes/sessions/` (856K) | 📦 | **nein → Anhang B** |
|
||||
| Checkpoints (optional) | `~/.hermes/checkpoints/` (existiert nicht) | ⏭️ skip | nein |
|
||||
| **Token-/Ersparnis-Statistik** | `~/.hermes/token_stats.json` | 📦 | **nein → Anhang B** |
|
||||
| Dienst `hermes-gateway` (`:8642`) | systemd-User | ⬇️ | nein |
|
||||
| Desktop-Gateway `hermes-builtin-ui` (`:9119`) | systemd-User | ⬇️ deploy.sh | nein |
|
||||
| Desktop-Gateway-Token-Drop-in | `~/.config/systemd/user/hermes-builtin-ui.service.d/session-token.conf` | 🔑 neu erzeugen (Wert auch in `~/.hermes/desktop-gateway-token`, chmod 600) | **nein** |
|
||||
| Desktop-Gateway-Token-Kopie | `~/.hermes/desktop-gateway-token` (chmod 600) | 🔑 | **nein** |
|
||||
|
||||
### H · MCP-Server (4)
|
||||
| Komponente | Ort | Quelle | Im Backup? |
|
||||
|---|---|---|---|
|
||||
| `mission-control-memory`, `-stack` (MC2-venv) | `~/mission-control-v2/mcp/*.py` | 🌐 Git | über config.yaml |
|
||||
| `hermes-pc-control`, `hermes-web-fetch` (Hermes-venv) | dito | 🌐 Git | über config.yaml |
|
||||
| MCP-Verdrahtung | `~/.hermes/config.yaml → mcp_servers` | 📦 | ja |
|
||||
|
||||
### I · Browser-Stack
|
||||
| Komponente | Ort | Quelle | Im Backup? |
|
||||
|---|---|---|---|
|
||||
| agent-browser (npm global) | `~/.local/...`, symlink `~/.hermes/node/bin` | ⬇️ npm i -g | nein |
|
||||
| Chrome (engine) + System-Libs | `~/.agent-browser/browsers/` + apt-Libs | ⬇️ install --with-deps | nein |
|
||||
| lightpanda (optional, leicht) | `~/.local/bin/lightpanda` | ⬇️ Download | nein |
|
||||
| Browser-Verdrahtung (engine=chrome, toolset) | `~/.hermes/config.yaml` | 📦 | ja |
|
||||
|
||||
### J · Modelle (GGUF — der große Brocken)
|
||||
| Komponente | Ort | Quelle | Im Backup? |
|
||||
|---|---|---|---|
|
||||
| GGUF-Modelle (Brain, fast, heavy, coder, vision, embedding …) | `/srv/models/` | ⬇️ Re-Download aus llama-swap-Manifest | **nein (zu groß, bewusst)** |
|
||||
|
||||
### K · Extern (anderer Rechner)
|
||||
| Komponente | Ort | Quelle | Im Backup? |
|
||||
|---|---|---|---|
|
||||
| PC-Executor (Windows) | `client/hermes-pc/` → Scheduled Task | 🌐 Git + Task | n/a (anderer Host) |
|
||||
| Hermes Desktop (Windows-App) | `%LOCALAPPDATA%\hermes` + `connection.json` in `%APPDATA%\Hermes\` | ⬇️ Hermes-Setup.exe + Remote-URL/Token neu eintragen | n/a (anderer Host) |
|
||||
|
||||
---
|
||||
|
||||
## 4. Ziel-Architektur
|
||||
|
||||
### 4.1 `deploy/bootstrap.sh` — orchestrierter From-Zero-Lauf
|
||||
- **Idempotent & resumierbar** (Schritt-Marker in `~/.mc2-bootstrap.state`).
|
||||
- **Getrennt nach sudo-Bedarf**: `bootstrap-root.sh` (L0+L1, bewusst mit sudo) + `bootstrap.sh`
|
||||
(L2+, sudo-frei = Kern ist `deploy.sh`). Passt zum Nordstern „Runtime ohne sudo".
|
||||
- **Mündet in den Wizard**: startet MC2 im First-Run-Modus, gibt Wizard-URL aus.
|
||||
|
||||
Phasen: `0 Vorflug → 1[sudo] System+Dirs → 2[sudo] Engine+llama-swap → 3 Code(MC2+Hermes+Node)
|
||||
→ 4 venvs(backend/mem0/voice/hermes) → 5 Browser → 6 deploy.sh(Units/enable/restart)
|
||||
→ 7 Restore(optional) → 8 Wizard hoch + postcheck`.
|
||||
|
||||
### 4.2 First-Run-Wizard (browserbasiert, von MC2 serviert)
|
||||
MC2 erkennt unkonfigurierten Zustand → Frontend-Route `/setup` statt Dashboard. Schritte:
|
||||
|
||||
1. **Systemcheck** — Live-Ampel je Komponente aus §3 (`GET /api/setup/status` + `…/system/services`).
|
||||
2. **Restore-Quelle wählen** — Backup-Tarball erkennen → globaler Modus „Alles 1:1" / „selektiv" / „frisch".
|
||||
3. **Komponenten-Auswahl (Kernstück)** — pro Katalog-Eintrag aus §3 eine Kachel mit
|
||||
📦/🆕/⏭️ (siehe §1). Zeigt Größe + ob Backup-Daten vorhanden.
|
||||
4. **Netzwerk & Identität** — Box-IP (→ `HERMES_TERMINAL_URL`, `PC_EXECUTOR_URL`), Hostname.
|
||||
5. **Secrets** 🔑 — `TELEGRAM_BOT_TOKEN`, erlaubte User-ID, `API_SERVER_KEY` (oder generieren),
|
||||
ElevenLabs-Key → `~/.hermes/.env` (chmod 600). Bei 📦 vorbefüllt aus Backup.
|
||||
6. **Hermes-Profil** — Brain-Alias + Toolset-Profil (Default = entfesselt: vision/tts/memory/browser
|
||||
an, image_gen/video aus — siehe [[project-hermes-setup]]). Bei 📦 = exakte alte `config.yaml`.
|
||||
7. **Voice** — Stimme/Klonstimme
|
||||
(📦 Referenz-Audio zurück, oder neu aufnehmen/hochladen).
|
||||
8. **Modelle** — aus llama-swap-Manifest automatisch nachladen (`POST /api/models/install`,
|
||||
Fortschritt `GET /api/jobs`) ODER geführte Discover-Neuauswahl. Reihenfolge: Brain → heavy → Rest.
|
||||
9. **Externe Checkliste** — PC-Executor (Windows-Task), Hermes Desktop (App + Token) als Haken.
|
||||
10. **Verifikation & Abschluss** — `stack-postcheck.sh` + `hermes-postcheck.sh` → grün/rot-Liste → Dashboard.
|
||||
|
||||
**Technik:** neuer Router `backend/routers/setup.py`
|
||||
(`GET /api/setup/status`, `POST /api/setup/{secrets,network,hermes,components,finish}`).
|
||||
First-Run-Gate: MC2 prüft beim Boot Marker `~/.mc2-setup-done` bzw. Pflicht-Secrets → leitet auf `/setup`.
|
||||
|
||||
---
|
||||
|
||||
## 5. Backup-Scope für echtes 1:1 ERWEITERN (umzusetzen — Snippet in Anhang B)
|
||||
|
||||
Der **aktuelle** `backup.sh` (live auf der Box, unverändert) reicht für „1:1" nicht. Für die Bau-Session
|
||||
liegt das **fertige Erweiterungs-Snippet in Anhang B** — es ergänzt den Tarball um:
|
||||
|
||||
- **Voice-Klonstimme** → `~/.voice/refs/` (`ref.wav`)
|
||||
- **Token-/Ersparnis-Statistik** → `~/.hermes/token_stats.json`
|
||||
- **Hermes-Skills** → `~/.hermes/skills/` (re-downloadbarer `.hub`-Cache per `tar --exclude` ausgelassen)
|
||||
- **Hermes-Sessions/History** → `~/.hermes/sessions/`
|
||||
|
||||
Restore soll skills/sessions **mergen** (frisch geladenen `.hub` nicht überschreiben) und `voice-service`
|
||||
mit neu starten.
|
||||
|
||||
**Offen bleibt:** nichts — der letzte offene Punkt (eigene Avatare) ist mit dem Avatar-Ausbau erledigt (§3·D).
|
||||
|
||||
**Bewusst NICHT im Backup (re-downloadbar/rebuildbar):** Piper-Stimmen (install.sh), `.hub`-Skill-Cache,
|
||||
`/srv/models/mc2-discover.json` (Cache), `/srv/models/mc2-memory.db` (Legacy-Migration), alle venvs, GGUF-Modelle.
|
||||
|
||||
→ Aufgabe der Bau-Session: `backup.sh` + `restore.sh` um diese Pfade erweitern (mit klarer
|
||||
„opt-in für große/optionale Teile"-Logik), und den MANIFEST-Inhalt entsprechend.
|
||||
|
||||
---
|
||||
|
||||
## 6. Secrets-Strategie
|
||||
- Single Source: `~/.hermes/.env` (chmod 600), im Tarball (selbst 600).
|
||||
- Rebuild ohne Backup → Wizard-Schritt 5 erzeugt sie. `API_SERVER_KEY` generierbar; Telegram/ElevenLabs liefert Nutzer.
|
||||
- Nie ins Git, nie in Logs, nie in die MC2-DB. **Off-Box-Spiegelung des Tarballs noch offen** ([[mc2-backup-restore]]).
|
||||
|
||||
## 7. Modell-Strategie
|
||||
- **A (empfohlen):** `/etc/llama-swap/config.yaml` (im Backup) listet alle Modelle → Wizard leitet Repos/Quants ab und lädt automatisch (`/api/models/install`). „Ein Klick, lädt über Nacht."
|
||||
- **B:** geführte Discover-Neuauswahl je Rolle. Reihenfolge Brain → heavy → Rest, danach `warmup.sh`.
|
||||
|
||||
---
|
||||
|
||||
## 8. Implementierungs-Reihenfolge (neue Session)
|
||||
1. ✅ **Box-Verifikation erledigt (2026-06-28, nur gelesen — nichts verändert):** llama-swap.service-Inhalt
|
||||
in **Anhang A**; skills/sessions/refs/token_stats existieren (Pfade in §3 verifiziert); Backup-Erweiterung
|
||||
als fertiges Snippet in **Anhang B**. **Keine offenen Box-Fragen mehr** außer §10·3–6. → mit Schritt 2 starten.
|
||||
2. `backup.sh`/`restore.sh` um die 1:1-Lücken erweitern (§5).
|
||||
3. `deploy/bootstrap.sh` + `bootstrap-root.sh` (Phasen, State-Marker); llama-swap-Install skripten.
|
||||
4. `backend/routers/setup.py` + First-Run-Gate.
|
||||
5. Frontend `/setup`-Wizard (Schritte 1–10), Komponenten-Auswahl als Kernstück; bestehende Views
|
||||
(Cockpit/AgentView/Sprechen) wiederverwenden.
|
||||
6. Modell-Restore-aus-Manifest.
|
||||
7. **Abnahme in frischer VM** (§9).
|
||||
|
||||
Vorschlag: 2–4 zuerst (Skelett lauffähig), Wizard danach. Branch + PR.
|
||||
|
||||
## 9. Abnahmekriterien
|
||||
- Frisches Ubuntu 26.04 → `bootstrap-root.sh` + `bootstrap.sh` → laufender Stack, kein Spezialwissen.
|
||||
- Postchecks grün; Telegram, Voice (inkl. Klonstimme bei 📦), Browser funktionieren.
|
||||
- „Alles 1:1" stellt mem0, Hermes-config.yaml, Secrets, Klonstimme, Skills exakt wieder her.
|
||||
- Selektiver Modus: einzelne Komponenten ⏭️ überspringbar, Stack läuft trotzdem.
|
||||
- Idempotenz: zweiter Lauf = No-op.
|
||||
|
||||
## 10. Offene Entscheidungen (vor dem Bau)
|
||||
1. ~~llama-swap systemd-Unit-Inhalt~~ → **geklärt: kompletter Unit in Anhang A** (in der Bau-Session anlegen).
|
||||
2. ~~Voice-Referenz-Audio-Ort~~ → **geklärt: `~/.voice/refs/`** (Backup-Snippet in Anhang B, in der Bau-Session umsetzen).
|
||||
3. ~~**Eigene Avatare**~~ — entfallen: Avatar am 28.08.2026 ausgebaut (§3·D).
|
||||
4. **Off-Box-Backup-Ziel** (NAS/2. Platte/Cloud) — [[mc2-backup-restore]].
|
||||
5. **Python 3.14** auf frischem Ubuntu beschaffen (deadsnakes?).
|
||||
6. **Bootstrap-sudo-Modell**: getrenntes `bootstrap-root.sh` (empfohlen) vs. interaktives sudo.
|
||||
|
||||
## 11. Referenzen
|
||||
- `backend/config.py`, [[project-mc2-architecture]], [[project-stack-state]]
|
||||
- [[project-hermes-setup]], `docs/HERMES_SETUP.md` (entfesseltes Profil, Browser, PC-Executor)
|
||||
- [[voice-sprechen-feature]] (Voice + 3D-Avatar), [[mem0-memory-architecture]], [[mc2-backup-restore]]
|
||||
- `deploy/{deploy,backup,restore,provision-engine,stack-postcheck,warmup}.sh`, `voice_service/install.sh`
|
||||
|
||||
---
|
||||
|
||||
## Anhang A — `llama-swap.service` (1:1 von der Box abgegriffen, 2026-06-28)
|
||||
|
||||
Base-Unit nach `/etc/systemd/system/llama-swap.service` (root). User/GFX sind boxspezifisch
|
||||
(hitonabi, gfx1151 → HSA 11.5.1) — auf anderer Hardware anpassen. Die `.d/`-Drop-ins
|
||||
(`vulkan.conf`, `warmup.conf`) legt `provision-engine.sh` an.
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=llama-swap (lokaler LLM Router)
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=hitonabi
|
||||
Environment=HSA_OVERRIDE_GFX_VERSION=11.5.1
|
||||
Environment=PATH=/usr/local/bin:/usr/bin:/bin
|
||||
ExecStart=/usr/local/bin/llama-swap --config /etc/llama-swap/config.yaml --listen 0.0.0.0:8080 --watch-config
|
||||
Restart=on-failure
|
||||
RestartSec=3
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
```ini
|
||||
# /etc/systemd/system/llama-swap.service.d/vulkan.conf
|
||||
[Service]
|
||||
Environment=LD_LIBRARY_PATH=/opt/llamacpp-vulkan
|
||||
```
|
||||
```ini
|
||||
# /etc/systemd/system/llama-swap.service.d/warmup.conf
|
||||
[Service]
|
||||
ExecStartPost=-/usr/local/bin/llama-swap-warmup.sh
|
||||
```
|
||||
|
||||
**Erstinstall-Reihenfolge:** `bash deploy/update-swap.sh` (Binary) → Unit kopieren →
|
||||
`sudo bash deploy/provision-engine.sh` (Engine+Drop-ins) → `sudo systemctl enable --now llama-swap`.
|
||||
|
||||
---
|
||||
|
||||
## Anhang B — Backup-Scope-Erweiterung für echtes 1:1 (fertiges Snippet)
|
||||
|
||||
In `deploy/backup.sh` ergänzen (Variablen oben: `VOICE="${VOICE_HOME:-$HOME/.voice}"`,
|
||||
Stage zusätzlich `"$STAGE/voice"`):
|
||||
|
||||
```bash
|
||||
# 1:1-Erweiterung (scan-verifiziert 2026-06-28):
|
||||
[ -f "$HERMES/token_stats.json" ] && cp -a "$HERMES/token_stats.json" "$STAGE/hermes/" || true
|
||||
[ -d "$HERMES/skills" ] && cp -a "$HERMES/skills" "$STAGE/hermes/skills" || true
|
||||
[ -d "$HERMES/sessions" ] && cp -a "$HERMES/sessions" "$STAGE/hermes/sessions" || true
|
||||
[ -d "$VOICE/refs" ] && cp -a "$VOICE/refs" "$STAGE/voice/refs" || true
|
||||
```
|
||||
tar-Aufruf um den re-downloadbaren Skill-Cache erleichtern:
|
||||
```bash
|
||||
tar --exclude='./hermes/skills/.hub' -czf "$OUT" -C "$STAGE" .
|
||||
```
|
||||
In `deploy/restore.sh` spiegelbildlich (skills/sessions **mergen**, nicht ersetzen) und
|
||||
`voice-service` mit neustarten:
|
||||
```bash
|
||||
VOICE="${VOICE_HOME:-$HOME/.voice}"
|
||||
SERVICES="mem0-service voice-service mission-control-2 hermes-gateway"
|
||||
[ -f "$STAGE/hermes/token_stats.json" ] && cp -a "$STAGE/hermes/token_stats.json" "$HERMES/"
|
||||
[ -d "$STAGE/hermes/skills" ] && { mkdir -p "$HERMES/skills"; cp -a "$STAGE/hermes/skills/." "$HERMES/skills/"; }
|
||||
[ -d "$STAGE/hermes/sessions" ] && { mkdir -p "$HERMES/sessions"; cp -a "$STAGE/hermes/sessions/." "$HERMES/sessions/"; }
|
||||
[ -d "$STAGE/voice/refs" ] && { mkdir -p "$VOICE/refs"; cp -a "$STAGE/voice/refs/." "$VOICE/refs/"; }
|
||||
```
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import{c as A,I as P,f as M,k as B,u as D,r as z,j as e,J as T,b as a,K as g,M as b,N as W,t as f,W as j,O as G,X as I,C as N,n as y,q as i}from"./index-DNt66je4.js";/**
|
||||
import{c as A,I as P,f as M,k as B,u as D,r as z,j as e,J as T,b as a,K as g,M as b,N as W,t as f,W as j,O as G,X as I,C as N,n as y,q as i}from"./index-B57enqf6.js";/**
|
||||
* @license lucide-react v0.460.0 - ISC
|
||||
*
|
||||
* This source code is licensed under the ISC license.
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import{c as W,a1 as H,a2 as F,u as G,k as T,r as b,j as e,R as _,b as y,L as m,C as E,_ as O,D as P,a0 as z,a3 as R,a4 as V,X as D,T as C,F as K,n as $,a5 as q,q as U}from"./index-DNt66je4.js";import{S,H as Q}from"./SectionLabel-DKDWJ8hi.js";import{C as M}from"./chevron-down-BTSzJRag.js";/**
|
||||
import{c as W,a1 as H,a2 as F,u as G,k as T,r as b,j as e,R as _,b as y,L as m,C as E,_ as O,D as P,a0 as z,a3 as R,a4 as V,X as D,T as C,F as K,n as $,a5 as q,q as U}from"./index-B57enqf6.js";import{S,H as Q}from"./SectionLabel-C_SwIL8k.js";import{C as M}from"./chevron-down-C-RXUrpR.js";/**
|
||||
* @license lucide-react v0.460.0 - ISC
|
||||
*
|
||||
* This source code is licensed under the ISC license.
|
||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
||||
import{c as d,aj as S,r as h,j as e,ak as M,L as p,a0 as z,a5 as C,al as D,am as A,b as B,an as Z,u as E,k as L,n as k,q}from"./index-DNt66je4.js";import{R as j}from"./rotate-ccw-CGv9dXzB.js";/**
|
||||
import{c as d,aj as S,r as h,j as e,ak as M,L as p,a0 as z,a5 as C,al as D,am as A,b as B,an as Z,u as E,k as L,n as k,q}from"./index-B57enqf6.js";import{R as j}from"./rotate-ccw-Bz6oQ3_7.js";/**
|
||||
* @license lucide-react v0.460.0 - ISC
|
||||
*
|
||||
* This source code is licensed under the ISC license.
|
||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
||||
import{c as i,r as p,z as G,A as H,j as e,D as u,t as z,B as I,F as M,b as S,L as B,G as _,C as E}from"./index-DNt66je4.js";import{C as P}from"./chevron-down-BTSzJRag.js";/**
|
||||
import{c as i,r as p,z as G,A as H,j as e,D as u,t as z,B as I,F as M,b as S,L as B,G as _,C as E}from"./index-B57enqf6.js";import{C as P}from"./chevron-down-C-RXUrpR.js";/**
|
||||
* @license lucide-react v0.460.0 - ISC
|
||||
*
|
||||
* This source code is licensed under the ISC license.
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import{c as u,j as e,r as M,t as C,V as z,Y as k,Z as y,M as v,_ as L,O as G,$ as D,W as T,b as f,a0 as B}from"./index-DNt66je4.js";import{a as w,L as P,C as E,Z as R}from"./zap-cdYTHDpR.js";import{L as I}from"./layers-BakyhGhx.js";/**
|
||||
import{c as u,j as e,r as M,t as C,V as z,Y as k,Z as y,M as v,_ as L,O as G,$ as D,W as T,b as f,a0 as B}from"./index-B57enqf6.js";import{a as w,L as P,C as E,Z as R}from"./zap-0IkDqAhv.js";import{L as I}from"./layers-BVbGNbDq.js";/**
|
||||
* @license lucide-react v0.460.0 - ISC
|
||||
*
|
||||
* This source code is licensed under the ISC license.
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import{c as $,j as e,b as S,a2 as ve,u as Se,k as De,r as d,n as z,a6 as Ke,_ as O,L as D,X as Q,a7 as xe,F as T,a3 as he,C as Ce,T as ze,a4 as Ee,a8 as pe,q as Ae,R as fe}from"./index-DNt66je4.js";import{S as Le,H as X}from"./SectionLabel-DKDWJ8hi.js";import{C as ee}from"./code-xml-BiSOeCkP.js";import{L as Pe}from"./layers-BakyhGhx.js";import{C as U}from"./chevron-down-BTSzJRag.js";import{R as We}from"./rotate-ccw-CGv9dXzB.js";/**
|
||||
import{c as $,j as e,b as S,a2 as ve,u as Se,k as De,r as d,n as z,a6 as Ke,_ as O,L as D,X as Q,a7 as xe,F as T,a3 as he,C as Ce,T as ze,a4 as Ee,a8 as pe,q as Ae,R as fe}from"./index-B57enqf6.js";import{S as Le,H as X}from"./SectionLabel-C_SwIL8k.js";import{C as ee}from"./code-xml-ID_kOxxM.js";import{L as Pe}from"./layers-BVbGNbDq.js";import{C as U}from"./chevron-down-C-RXUrpR.js";import{R as We}from"./rotate-ccw-Bz6oQ3_7.js";/**
|
||||
* @license lucide-react v0.460.0 - ISC
|
||||
*
|
||||
* This source code is licensed under the ISC license.
|
||||
+1
-1
@@ -1 +1 @@
|
||||
import{u,ah as m,k as x,j as s,y as n,ai as b,b as p,n as f,q as h}from"./index-DNt66je4.js";function y(){const d=u(),{data:t=[]}=m(),{showAlert:l,dialogElement:o}=x();async function i(e){try{await f(`/api/jobs/${e}/cancel`,{method:"POST"}),d.invalidateQueries({queryKey:h.jobs})}catch(c){l("Fehler",c.message)}}const a=t.filter(e=>e.state==="running"||e.state==="queued"),r=t.filter(e=>e.state!=="running"&&e.state!=="queued").slice(-3);return a.length===0&&r.length===0?null:s.jsxs("div",{className:"space-y-3 mc-card p-4",children:[s.jsx("div",{className:"text-[11px] font-semibold text-muted-foreground uppercase tracking-wider",children:"Aktive Downloads"}),a.map(e=>s.jsxs("div",{className:"space-y-1.5 p-3 rounded-xl bg-background/20 border border-border/40",children:[s.jsxs("div",{className:"flex justify-between items-center text-xs",children:[s.jsx("span",{className:"font-semibold truncate max-w-[250px]",children:e.label}),s.jsxs("div",{className:"flex items-center gap-3",children:[s.jsxs("span",{className:"text-muted-foreground font-mono",children:[e.progress??0,"% • ",n(e.done_bytes),"/",n(e.total_bytes),e.eta_s?` • ETA ${b(e.eta_s)}`:""]}),s.jsx("button",{onClick:()=>i(e.id),className:"text-[10px] text-red-400 hover:text-red-300 font-semibold border border-red-500/25 bg-red-500/5 px-2 py-0.5 rounded transition-all cursor-pointer",children:"Abbrechen"})]})]}),s.jsx("div",{className:"h-1.5 overflow-hidden rounded-full bg-muted",children:s.jsx("div",{className:"h-full rounded-full bg-primary transition-all duration-500",style:{width:`${e.progress??0}%`}})})]},e.id)),r.map(e=>s.jsxs("div",{className:"flex justify-between items-center text-xs text-muted-foreground px-1",children:[s.jsx("span",{className:"truncate",children:e.label}),s.jsx("span",{className:p("font-semibold text-[10px] px-1.5 py-0.5 rounded uppercase font-mono",e.state==="done"?"bg-emerald-500/10 text-emerald-400":"bg-amber-500/10 text-amber-400"),children:e.state})]},e.id)),o]})}export{y as J};
|
||||
import{u,ah as m,k as x,j as s,y as n,ai as b,b as p,n as f,q as h}from"./index-B57enqf6.js";function y(){const d=u(),{data:t=[]}=m(),{showAlert:l,dialogElement:o}=x();async function i(e){try{await f(`/api/jobs/${e}/cancel`,{method:"POST"}),d.invalidateQueries({queryKey:h.jobs})}catch(c){l("Fehler",c.message)}}const a=t.filter(e=>e.state==="running"||e.state==="queued"),r=t.filter(e=>e.state!=="running"&&e.state!=="queued").slice(-3);return a.length===0&&r.length===0?null:s.jsxs("div",{className:"space-y-3 mc-card p-4",children:[s.jsx("div",{className:"text-[11px] font-semibold text-muted-foreground uppercase tracking-wider",children:"Aktive Downloads"}),a.map(e=>s.jsxs("div",{className:"space-y-1.5 p-3 rounded-xl bg-background/20 border border-border/40",children:[s.jsxs("div",{className:"flex justify-between items-center text-xs",children:[s.jsx("span",{className:"font-semibold truncate max-w-[250px]",children:e.label}),s.jsxs("div",{className:"flex items-center gap-3",children:[s.jsxs("span",{className:"text-muted-foreground font-mono",children:[e.progress??0,"% • ",n(e.done_bytes),"/",n(e.total_bytes),e.eta_s?` • ETA ${b(e.eta_s)}`:""]}),s.jsx("button",{onClick:()=>i(e.id),className:"text-[10px] text-red-400 hover:text-red-300 font-semibold border border-red-500/25 bg-red-500/5 px-2 py-0.5 rounded transition-all cursor-pointer",children:"Abbrechen"})]})]}),s.jsx("div",{className:"h-1.5 overflow-hidden rounded-full bg-muted",children:s.jsx("div",{className:"h-full rounded-full bg-primary transition-all duration-500",style:{width:`${e.progress??0}%`}})})]},e.id)),r.map(e=>s.jsxs("div",{className:"flex justify-between items-center text-xs text-muted-foreground px-1",children:[s.jsx("span",{className:"truncate",children:e.label}),s.jsx("span",{className:p("font-semibold text-[10px] px-1.5 py-0.5 rounded uppercase font-mono",e.state==="done"?"bg-emerald-500/10 text-emerald-400":"bg-amber-500/10 text-amber-400"),children:e.state})]},e.id)),o]})}export{y as J};
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
import{I as b,u as f,K as h,r as d,j as e,b as c,J as g,T as p,R as j,Q as N,n as v,U as w,q as x}from"./index-DNt66je4.js";function y(){const{data:s}=b(),m=f(),r=s!=null&&s.box_console_url?h(s.box_console_url):void 0,a=s==null?void 0:s.box_console_reachable,[l,o]=d.useState(!1),[i,n]=d.useState("");async function u(){o(!0),n("");try{const t=await v("/api/maintenance/restart",{method:"POST",body:JSON.stringify({service:"box-console"})});n(t.ok?"Neu gestartet — einen Moment, dann lädt das Terminal.":`Fehlgeschlagen: ${t.err||"Unbekannter Fehler"}`),w(m,x.agentStatus,x.services)}catch(t){n(`Fehlgeschlagen: ${(t==null?void 0:t.message)||t}`)}finally{o(!1)}}return e.jsxs("div",{className:"flex h-full flex-col gap-4",children:[e.jsxs("div",{className:"flex flex-wrap items-end justify-between gap-3",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"bg-gradient-to-r from-foreground via-foreground to-primary bg-clip-text font-space text-2xl font-bold tracking-tight text-transparent",children:"Konsole"}),e.jsx("p",{className:"flex items-center gap-2 text-sm text-muted-foreground",children:"Direkte Shell auf der Box — wie ein SSH-Fenster, mitten im Browser."})]}),e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsxs("span",{className:"flex items-center gap-1.5 text-[11px] font-medium text-muted-foreground",children:[e.jsx("span",{className:c("h-2 w-2 rounded-full",a?"bg-emerald-500 animate-pulse":"bg-amber-500")}),a?"online":"offline"]}),r&&e.jsxs("a",{href:r,target:"_blank",rel:"noopener",className:"flex h-8 items-center gap-1.5 rounded-lg border border-border/60 bg-background/20 px-3 text-xs font-semibold text-muted-foreground transition-all hover:border-primary/50 hover:text-foreground",children:[e.jsx(g,{className:"h-3.5 w-3.5"})," In neuem Tab"]})]})]}),r?e.jsxs("div",{className:"relative min-h-[58vh] flex-1 overflow-hidden rounded-2xl border border-border/60 bg-black/50 shadow-lg shadow-black/25",children:[a===!1&&e.jsxs("div",{className:"absolute inset-0 z-10 flex flex-col items-center justify-center gap-3 bg-black/70 text-center",children:[e.jsx(p,{className:"h-8 w-8 text-amber-400"}),e.jsx("div",{className:"text-sm font-semibold text-amber-300",children:"Konsole nicht erreichbar"}),e.jsxs("p",{className:"max-w-sm text-[11px] leading-normal text-muted-foreground",children:["Der Terminal-Dienst (",e.jsx("code",{className:"font-mono text-primary",children:"box-console"}),") läuft gerade nicht."]}),e.jsxs("button",{onClick:u,disabled:l,className:"flex h-9 items-center gap-1.5 rounded-lg border border-amber-500/40 bg-amber-500/10 px-4 text-[11px] font-bold uppercase tracking-wide text-amber-300 transition-all hover:bg-amber-500/20 cursor-pointer disabled:opacity-50",children:[e.jsx(j,{className:c("h-3.5 w-3.5",l&&"animate-spin")})," Dienst neu starten"]}),i&&e.jsx("p",{className:"max-w-sm text-[11px] text-muted-foreground",children:i})]}),e.jsx("iframe",{src:r,title:"Box-Konsole",className:"h-full w-full border-0",style:{minHeight:"58vh"}})]}):e.jsxs("div",{className:"flex min-h-[58vh] flex-1 items-center justify-center rounded-2xl border border-border/60 bg-background/20 text-xs text-muted-foreground",children:[e.jsx(N,{className:"mr-2 h-4 w-4"})," Lade Konsole…"]})]})}export{y as KonsoleView};
|
||||
import{I as b,u as f,K as h,r as d,j as e,b as c,J as g,T as p,R as j,Q as N,n as v,U as w,q as x}from"./index-B57enqf6.js";function y(){const{data:s}=b(),m=f(),r=s!=null&&s.box_console_url?h(s.box_console_url):void 0,a=s==null?void 0:s.box_console_reachable,[l,o]=d.useState(!1),[i,n]=d.useState("");async function u(){o(!0),n("");try{const t=await v("/api/maintenance/restart",{method:"POST",body:JSON.stringify({service:"box-console"})});n(t.ok?"Neu gestartet — einen Moment, dann lädt das Terminal.":`Fehlgeschlagen: ${t.err||"Unbekannter Fehler"}`),w(m,x.agentStatus,x.services)}catch(t){n(`Fehlgeschlagen: ${(t==null?void 0:t.message)||t}`)}finally{o(!1)}}return e.jsxs("div",{className:"flex h-full flex-col gap-4",children:[e.jsxs("div",{className:"flex flex-wrap items-end justify-between gap-3",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"bg-gradient-to-r from-foreground via-foreground to-primary bg-clip-text font-space text-2xl font-bold tracking-tight text-transparent",children:"Konsole"}),e.jsx("p",{className:"flex items-center gap-2 text-sm text-muted-foreground",children:"Direkte Shell auf der Box — wie ein SSH-Fenster, mitten im Browser."})]}),e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsxs("span",{className:"flex items-center gap-1.5 text-[11px] font-medium text-muted-foreground",children:[e.jsx("span",{className:c("h-2 w-2 rounded-full",a?"bg-emerald-500 animate-pulse":"bg-amber-500")}),a?"online":"offline"]}),r&&e.jsxs("a",{href:r,target:"_blank",rel:"noopener",className:"flex h-8 items-center gap-1.5 rounded-lg border border-border/60 bg-background/20 px-3 text-xs font-semibold text-muted-foreground transition-all hover:border-primary/50 hover:text-foreground",children:[e.jsx(g,{className:"h-3.5 w-3.5"})," In neuem Tab"]})]})]}),r?e.jsxs("div",{className:"relative min-h-[58vh] flex-1 overflow-hidden rounded-2xl border border-border/60 bg-black/50 shadow-lg shadow-black/25",children:[a===!1&&e.jsxs("div",{className:"absolute inset-0 z-10 flex flex-col items-center justify-center gap-3 bg-black/70 text-center",children:[e.jsx(p,{className:"h-8 w-8 text-amber-400"}),e.jsx("div",{className:"text-sm font-semibold text-amber-300",children:"Konsole nicht erreichbar"}),e.jsxs("p",{className:"max-w-sm text-[11px] leading-normal text-muted-foreground",children:["Der Terminal-Dienst (",e.jsx("code",{className:"font-mono text-primary",children:"box-console"}),") läuft gerade nicht."]}),e.jsxs("button",{onClick:u,disabled:l,className:"flex h-9 items-center gap-1.5 rounded-lg border border-amber-500/40 bg-amber-500/10 px-4 text-[11px] font-bold uppercase tracking-wide text-amber-300 transition-all hover:bg-amber-500/20 cursor-pointer disabled:opacity-50",children:[e.jsx(j,{className:c("h-3.5 w-3.5",l&&"animate-spin")})," Dienst neu starten"]}),i&&e.jsx("p",{className:"max-w-sm text-[11px] text-muted-foreground",children:i})]}),e.jsx("iframe",{src:r,title:"Box-Konsole",className:"h-full w-full border-0",style:{minHeight:"58vh"}})]}):e.jsxs("div",{className:"flex min-h-[58vh] flex-1 items-center justify-center rounded-2xl border border-border/60 bg-background/20 text-xs text-muted-foreground",children:[e.jsx(N,{className:"mr-2 h-4 w-4"})," Lade Konsole…"]})]})}export{y as KonsoleView};
|
||||
File diff suppressed because one or more lines are too long
+1
-1
@@ -1,4 +1,4 @@
|
||||
import{c as E,u as he,a as qe,r as j,j as e,C as Y,L as Fe,b as k,d as Re,q as V,B as re,e as Ie,E as ze,f as le,g as Be,h as Ae,i as Pe,k as Ge,l as ee,X as me,H as be,T as se,s as Oe,m as Te,n as q,o as U,p as He,S as ge,t as Qe,P as Ue,v as Ve,w as We,x as Ze,y as Je}from"./index-DNt66je4.js";import{J as Xe}from"./JobsBar-DG8VDiAV.js";import{C as Ye}from"./code-xml-BiSOeCkP.js";import{R as et}from"./rotate-ccw-CGv9dXzB.js";import{C as ue,Z as ie,L as tt,a as rt}from"./zap-cdYTHDpR.js";import{C as De}from"./chevron-down-BTSzJRag.js";import{L as st}from"./layers-BakyhGhx.js";/**
|
||||
import{c as E,u as he,a as qe,r as j,j as e,C as Y,L as Fe,b as k,d as Re,q as V,B as re,e as Ie,E as ze,f as le,g as Be,h as Ae,i as Pe,k as Ge,l as ee,X as me,H as be,T as se,s as Oe,m as Te,n as q,o as U,p as He,S as ge,t as Qe,P as Ue,v as Ve,w as We,x as Ze,y as Je}from"./index-B57enqf6.js";import{J as Xe}from"./JobsBar-D8URwRCr.js";import{C as Ye}from"./code-xml-ID_kOxxM.js";import{R as et}from"./rotate-ccw-Bz6oQ3_7.js";import{C as ue,Z as ie,L as tt,a as rt}from"./zap-0IkDqAhv.js";import{C as De}from"./chevron-down-C-RXUrpR.js";import{L as st}from"./layers-BVbGNbDq.js";/**
|
||||
* @license lucide-react v0.460.0 - ISC
|
||||
*
|
||||
* This source code is licensed under the ISC license.
|
||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
||||
import{c as s,j as e}from"./index-DNt66je4.js";/**
|
||||
import{c as s,j as e}from"./index-B57enqf6.js";/**
|
||||
* @license lucide-react v0.460.0 - ISC
|
||||
*
|
||||
* This source code is licensed under the ISC license.
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
var M=r=>{throw TypeError(r)};var O=(r,e,t)=>e.has(r)||M("Cannot "+t);var a=(r,e,t)=>(O(r,e,"read from private field"),t?t.call(r):e.get(r)),y=(r,e,t)=>e.has(r)?M("Cannot add the same private member more than once"):e instanceof WeakSet?e.add(r):e.set(r,t),v=(r,e,t,n)=>(O(r,e,"write to private field"),n?n.call(r,t):e.set(r,t),t),j=(r,e,t)=>(O(r,e,"access private method"),t);import{a9 as B,aa as F,ab as E,ac as I,ad as P,u as A,r as f,ae as J,af as Q,c as T,ag as U,j as s,G as D,T as H,b as K,L,M as G,q as V,n as R}from"./index-DNt66je4.js";import{J as _}from"./JobsBar-DG8VDiAV.js";var c,g,o,h,m,w,C,q,z=(q=class extends B{constructor(e,t){super();y(this,m);y(this,c);y(this,g);y(this,o);y(this,h);v(this,c,e),this.setOptions(t),this.bindMethods(),j(this,m,w).call(this)}bindMethods(){this.mutate=this.mutate.bind(this),this.reset=this.reset.bind(this)}setOptions(e){var n;const t=this.options;this.options=a(this,c).defaultMutationOptions(e),F(this.options,t)||a(this,c).getMutationCache().notify({type:"observerOptionsUpdated",mutation:a(this,o),observer:this}),t!=null&&t.mutationKey&&this.options.mutationKey&&E(t.mutationKey)!==E(this.options.mutationKey)?this.reset():((n=a(this,o))==null?void 0:n.state.status)==="pending"&&a(this,o).setOptions(this.options)}onUnsubscribe(){var e;this.hasListeners()||(e=a(this,o))==null||e.removeObserver(this)}onMutationUpdate(e){j(this,m,w).call(this),j(this,m,C).call(this,e)}getCurrentResult(){return a(this,g)}reset(){var e;(e=a(this,o))==null||e.removeObserver(this),v(this,o,void 0),j(this,m,w).call(this),j(this,m,C).call(this)}mutate(e,t){var n;return v(this,h,t),(n=a(this,o))==null||n.removeObserver(this),v(this,o,a(this,c).getMutationCache().build(a(this,c),this.options)),a(this,o).addObserver(this),a(this,o).execute(e)}},c=new WeakMap,g=new WeakMap,o=new WeakMap,h=new WeakMap,m=new WeakSet,w=function(){var t;const e=((t=a(this,o))==null?void 0:t.state)??I();v(this,g,{...e,isPending:e.status==="pending",isSuccess:e.status==="success",isError:e.status==="error",isIdle:e.status==="idle",mutate:this.mutate,reset:this.reset})},C=function(e){P.batch(()=>{var t,n,l,x,d,p,k,i;if(a(this,h)&&this.hasListeners()){const u=a(this,g).variables,N=a(this,g).context,S={client:a(this,c),meta:this.options.meta,mutationKey:this.options.mutationKey};if((e==null?void 0:e.type)==="success"){try{(n=(t=a(this,h)).onSuccess)==null||n.call(t,e.data,u,N,S)}catch(b){Promise.reject(b)}try{(x=(l=a(this,h)).onSettled)==null||x.call(l,e.data,null,u,N,S)}catch(b){Promise.reject(b)}}else if((e==null?void 0:e.type)==="error"){try{(p=(d=a(this,h)).onError)==null||p.call(d,e.error,u,N,S)}catch(b){Promise.reject(b)}try{(i=(k=a(this,h)).onSettled)==null||i.call(k,void 0,e.error,u,N,S)}catch(b){Promise.reject(b)}}}this.listeners.forEach(u=>{u(a(this,g))})})},q);function W(r,e){const t=A(),[n]=f.useState(()=>new z(t,r));f.useEffect(()=>{n.setOptions(r)},[n,r]);const l=f.useSyncExternalStore(f.useCallback(d=>n.subscribe(P.batchCalls(d)),[n]),()=>n.getCurrentResult(),()=>n.getCurrentResult()),x=f.useCallback((d,p)=>{n.mutate(d,p).catch(J)},[n]);if(l.error&&Q(n.options.throwOnError,[l.error]))throw l.error;return{...l,mutate:x,mutateAsync:l.mutate}}/**
|
||||
var M=r=>{throw TypeError(r)};var O=(r,e,t)=>e.has(r)||M("Cannot "+t);var a=(r,e,t)=>(O(r,e,"read from private field"),t?t.call(r):e.get(r)),y=(r,e,t)=>e.has(r)?M("Cannot add the same private member more than once"):e instanceof WeakSet?e.add(r):e.set(r,t),v=(r,e,t,n)=>(O(r,e,"write to private field"),n?n.call(r,t):e.set(r,t),t),j=(r,e,t)=>(O(r,e,"access private method"),t);import{a9 as B,aa as F,ab as E,ac as I,ad as P,u as A,r as f,ae as J,af as Q,c as T,ag as U,j as s,G as D,T as H,b as K,L,M as G,q as V,n as R}from"./index-B57enqf6.js";import{J as _}from"./JobsBar-D8URwRCr.js";var c,g,o,h,m,w,C,q,z=(q=class extends B{constructor(e,t){super();y(this,m);y(this,c);y(this,g);y(this,o);y(this,h);v(this,c,e),this.setOptions(t),this.bindMethods(),j(this,m,w).call(this)}bindMethods(){this.mutate=this.mutate.bind(this),this.reset=this.reset.bind(this)}setOptions(e){var n;const t=this.options;this.options=a(this,c).defaultMutationOptions(e),F(this.options,t)||a(this,c).getMutationCache().notify({type:"observerOptionsUpdated",mutation:a(this,o),observer:this}),t!=null&&t.mutationKey&&this.options.mutationKey&&E(t.mutationKey)!==E(this.options.mutationKey)?this.reset():((n=a(this,o))==null?void 0:n.state.status)==="pending"&&a(this,o).setOptions(this.options)}onUnsubscribe(){var e;this.hasListeners()||(e=a(this,o))==null||e.removeObserver(this)}onMutationUpdate(e){j(this,m,w).call(this),j(this,m,C).call(this,e)}getCurrentResult(){return a(this,g)}reset(){var e;(e=a(this,o))==null||e.removeObserver(this),v(this,o,void 0),j(this,m,w).call(this),j(this,m,C).call(this)}mutate(e,t){var n;return v(this,h,t),(n=a(this,o))==null||n.removeObserver(this),v(this,o,a(this,c).getMutationCache().build(a(this,c),this.options)),a(this,o).addObserver(this),a(this,o).execute(e)}},c=new WeakMap,g=new WeakMap,o=new WeakMap,h=new WeakMap,m=new WeakSet,w=function(){var t;const e=((t=a(this,o))==null?void 0:t.state)??I();v(this,g,{...e,isPending:e.status==="pending",isSuccess:e.status==="success",isError:e.status==="error",isIdle:e.status==="idle",mutate:this.mutate,reset:this.reset})},C=function(e){P.batch(()=>{var t,n,l,x,d,p,k,i;if(a(this,h)&&this.hasListeners()){const u=a(this,g).variables,N=a(this,g).context,S={client:a(this,c),meta:this.options.meta,mutationKey:this.options.mutationKey};if((e==null?void 0:e.type)==="success"){try{(n=(t=a(this,h)).onSuccess)==null||n.call(t,e.data,u,N,S)}catch(b){Promise.reject(b)}try{(x=(l=a(this,h)).onSettled)==null||x.call(l,e.data,null,u,N,S)}catch(b){Promise.reject(b)}}else if((e==null?void 0:e.type)==="error"){try{(p=(d=a(this,h)).onError)==null||p.call(d,e.error,u,N,S)}catch(b){Promise.reject(b)}try{(i=(k=a(this,h)).onSettled)==null||i.call(k,void 0,e.error,u,N,S)}catch(b){Promise.reject(b)}}}this.listeners.forEach(u=>{u(a(this,g))})})},q);function W(r,e){const t=A(),[n]=f.useState(()=>new z(t,r));f.useEffect(()=>{n.setOptions(r)},[n,r]);const l=f.useSyncExternalStore(f.useCallback(d=>n.subscribe(P.batchCalls(d)),[n]),()=>n.getCurrentResult(),()=>n.getCurrentResult()),x=f.useCallback((d,p)=>{n.mutate(d,p).catch(J)},[n]);if(l.error&&Q(n.options.throwOnError,[l.error]))throw l.error;return{...l,mutate:x,mutateAsync:l.mutate}}/**
|
||||
* @license lucide-react v0.460.0 - ISC
|
||||
*
|
||||
* This source code is licensed under the ISC license.
|
||||
+4
-4
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
||||
import{c as o}from"./index-DNt66je4.js";/**
|
||||
import{c as o}from"./index-B57enqf6.js";/**
|
||||
* @license lucide-react v0.460.0 - ISC
|
||||
*
|
||||
* This source code is licensed under the ISC license.
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import{c as e}from"./index-DNt66je4.js";/**
|
||||
import{c as e}from"./index-B57enqf6.js";/**
|
||||
* @license lucide-react v0.460.0 - ISC
|
||||
*
|
||||
* This source code is licensed under the ISC license.
|
||||
+359
File diff suppressed because one or more lines are too long
-435
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
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user