#!/usr/bin/env bash # Deploy AUF DER BOX als systemd-USER-Dienst — KEIN sudo, KEIN /opt, KEIN Passwort. # Erstinstallation + Updates in einem. Läuft als User hitonabi. # # Erstinstallation (einmalig): # git clone https://git.tobisniceshomelab.ddnsfree.com/Hitonabi/mission-control-v2 ~/mission-control-v2 # bash ~/mission-control-v2/deploy/deploy.sh set -euo pipefail SRC="${MC2_SRC:-$HOME/mission-control-v2}" cd "$SRC" git fetch -q origin && git reset -q --hard origin/main # ★ Selbst-Reset-Falle (15.07. beim P1-Deploy live gebissen): der reset oben ersetzt auch # DIESE Datei, und bash liest laufende Skripte häppchenweise von der Platte weiter — ab # hier liefe ein Zeilen-Mix aus alter+neuer Version (beim P1-Deploy fielen so daemon-reload # und der mc2-gateway-Start unter den Tisch). Darum: nach dem Reset genau EINMAL die # frische Version neu ausführen; der zweite Durchlauf überspringt dank Env-Marke nichts. if [ "${MC2_DEPLOY_REEXEC:-0}" != "1" ]; then export MC2_DEPLOY_REEXEC=1 exec bash "$SRC/deploy/deploy.sh" fi # venv + Abhängigkeiten if [ ! -d "$SRC/backend/.venv" ]; then python3 -m venv "$SRC/backend/.venv" fi "$SRC/backend/.venv/bin/python" -m pip install -q --upgrade pip "$SRC/backend/.venv/bin/python" -m pip install -q -r "$SRC/backend/requirements.txt" # Mem0-Sidecar (auto-lernendes Gedächtnis) — eigenes Python-3.12-venv (~/.mem0/venv), # weil mem0+chromadb unter dem 3.14-Backend-venv nicht laufen. mem0ai/chromadb sind dort # bereits installiert; hier nur den HTTP-Server nachziehen + Alt-DB einmalig migrieren. if [ -x "$HOME/.mem0/venv/bin/python" ]; then # ~/.mem0/venv ist uv-managed (kein pip) → uv pip nutzen. UV="$(command -v uv || echo "$HOME/.local/bin/uv")" "$UV" pip install -q --python "$HOME/.mem0/venv/bin/python" -r "$SRC/mem0_service/requirements.txt" ( cd "$SRC/mem0_service" && "$HOME/.mem0/venv/bin/python" migrate.py ) || true else echo "WARN: ~/.mem0/venv fehlt — Mem0-Sidecar wird nicht gestartet (siehe Plan A1)." fi # Voice-Sidecar (lokales STT + gestuftes TTS für „Mit Hermes reden") — eigenes Python-3.12-venv # (~/.voice/venv), weil torch/chatterbox/faster-whisper nicht ins 3.14-Backend-venv passen. # install.sh ist idempotent (venv + Deps + Piper-Stimmen). Best-effort: schlägt es fehl, läuft # der restliche Stack weiter (der Voice-Tab meldet den Sidecar dann als offline). bash "$SRC/voice_service/install.sh" || echo "WARN: Voice-Sidecar-Setup fehlgeschlagen — Voice-Tab bleibt offline." # Hermes-Memory-Provider-Plugin (auto-lernen/Recall via Mem0-Sidecar) nach ~/.hermes/plugins/ # spiegeln. Context-only (kein Tool-Loop). Aktivierung in ~/.hermes/config.yaml: # memory.memory_enabled: true + memory.provider: mc2-memory (einmalig, box-lokal). if [ -d "$HOME/.hermes" ]; then mkdir -p "$HOME/.hermes/plugins/mc2-memory" cp "$SRC/hermes/plugins/mc2-memory/__init__.py" "$SRC/hermes/plugins/mc2-memory/plugin.yaml" \ "$HOME/.hermes/plugins/mc2-memory/" fi # systemd-USER-Units installieren/aktualisieren mkdir -p "$HOME/.config/systemd/user" cp "$SRC/deploy/mission-control-2.service" "$HOME/.config/systemd/user/mission-control-2.service" # MC2-Diät (Hermes Desktop, 07/2026): das Hermes-Terminal-ttyd (:7681) und das standalone # hermes-webui (nesquena, Karteileiche seit dem Built-in-UI-Umbau) sind entfallen — die # Agent-Oberfläche ist die Desktop-App bzw. /hermes-ui/. Einmalige, idempotente Stilllegung: systemctl --user disable --now hermes-terminal 2>/dev/null || true systemctl --user disable --now hermes-webui 2>/dev/null || true rm -f "$HOME/.config/systemd/user/hermes-terminal.service" "$HOME/.config/systemd/user/hermes-webui.service" # Box-Konsole (ttyd -> Login-Shell auf :7682), in MC2 als Konsole-Seite eingebettet. cp "$SRC/deploy/box-console.service" "$HOME/.config/systemd/user/box-console.service" # MC2-Gateway (UMBAU v3 P1): der /v1-Datenpfad als eigener Prozess (Loopback :9010, # Restart=always) — Lucys Hirn + Nacht-Autonomie überleben damit jeden MC2-Neustart. cp "$SRC/deploy/mc2-gateway.service" "$HOME/.config/systemd/user/mc2-gateway.service" # MC2-Steward (UMBAU v3 P2): Wächter-Loops (Re-Warm/Sentry/Dedupe) als eigener Dienst — # überleben MC2-Neustarts und melden erstmals auch ein totes Steuerpult (Telegram). cp "$SRC/deploy/mc2-steward.service" "$HOME/.config/systemd/user/mc2-steward.service" # Mem0-Sidecar-Unit (nur wenn das venv existiert). [ -x "$HOME/.mem0/venv/bin/python" ] && cp "$SRC/deploy/mem0-service.service" "$HOME/.config/systemd/user/mem0-service.service" # Voice-Sidecar-Unit (nur wenn das venv existiert). [ -x "$HOME/.voice/venv/bin/python" ] && cp "$SRC/deploy/voice-service.service" "$HOME/.config/systemd/user/voice-service.service" # Tägliches Zustands-Backup (mem0 + Configs/Secrets) — Timer + oneshot-Service. Siehe docs/BACKUP.md. cp "$SRC/deploy/mc2-backup.service" "$HOME/.config/systemd/user/mc2-backup.service" cp "$SRC/deploy/mc2-backup.timer" "$HOME/.config/systemd/user/mc2-backup.timer" # Wöchentliches Auto-Update (Router/Engine/Hermes, Rollback+Pin+Telegram) — Autonomie E2. cp "$SRC/deploy/mc2-autoupdate.service" "$HOME/.config/systemd/user/mc2-autoupdate.service" cp "$SRC/deploy/mc2-autoupdate.timer" "$HOME/.config/systemd/user/mc2-autoupdate.timer" # Tägliche Selbst-Smoke-Tests (Gateway/Tools/Voice aktiv, Alarm bei Rot) — Autonomie 0d. cp "$SRC/deploy/mc2-selfsmoke.service" "$HOME/.config/systemd/user/mc2-selfsmoke.service" cp "$SRC/deploy/mc2-selfsmoke.timer" "$HOME/.config/systemd/user/mc2-selfsmoke.timer" # Bagatell-Annahme (Ideen-Queue, 10.07.2026): reine Doku-Vorschläge nachts ohne Klick einspielen. cp "$SRC/deploy/mc2-bagatell.service" "$HOME/.config/systemd/user/mc2-bagatell.service" cp "$SRC/deploy/mc2-bagatell.timer" "$HOME/.config/systemd/user/mc2-bagatell.timer" # Evolution-Radar-Feed (Autonomie E4): Hermes-cron speist ihn als Prompt ein. # Cron-Job selbst wird EINMALIG registriert (hermes cron create, siehe docs/RUNBOOK.md). mkdir -p "$HOME/.hermes/scripts" cp "$SRC/deploy/radar-feed.sh" "$HOME/.hermes/scripts/radar-feed.sh" # Selbstkritik-Cron (Faden 11b): Zwilling des Radars, Blick nach INNEN (Latenzen/Journal/Skills). cp "$SRC/deploy/selbstkritik-feed.sh" "$HOME/.hermes/scripts/selbstkritik-feed.sh" # Monats-Review-Wrapper (Cron 1. d. M. 09:00): Radar + Selbstkritik in EINEM Lauf. cp "$SRC/deploy/radar-selbstkritik-wrapper.sh" "$HOME/.hermes/scripts/radar-selbstkritik-wrapper.sh" # Traum-Cron (Dreaming, Phase 2): Blick auf das GELERNTE → Wissens-Vault. Bootstrappt ~/wissens-vault. cp "$SRC/deploy/dreaming-feed.sh" "$HOME/.hermes/scripts/dreaming-feed.sh" # Fremdblick-Helfer: Ein-Schuss-Zweitmeinung von einem anderen Modell (umgeht 64K-Delegations-Floor). cp "$SRC/deploy/fremdblick.sh" "$HOME/.hermes/scripts/fremdblick.sh" # Worker-Helfer (Orchestrator): Ein-Schuss-Arbeitsauftrag an ein Worker-Modell (Gegenstück zu fremdblick). cp "$SRC/deploy/worker.sh" "$HOME/.hermes/scripts/worker.sh" # Chef-Gutachter-Cron (Orchestrator-Finale): gpt-oss urteilt nachts über die autonome Arbeit. cp "$SRC/deploy/chef-gutachter-feed.sh" "$HOME/.hermes/scripts/chef-gutachter-feed.sh" # Release-Radar-Cron (10.07.2026): hält neue hermes-agent-Releases wöchentlich gegen die # Eigenbau-Landkarte im Wissens-Vault ("mehr Hermes nativ, weniger Eigenkreationen"). cp "$SRC/deploy/hermes-release-radar-feed.sh" "$HOME/.hermes/scripts/hermes-release-radar-feed.sh" # Idle-Radar (S4 "Zuendung", 12.07.2026): die Box gibt sich nachts selbst Arbeit — # Journal-Fehlermuster + Traum-Funde -> belegte rohe Ideen (triage) im nativen Kanban. cp "$SRC/deploy/idle-radar-feed.sh" "$HOME/.hermes/scripts/idle-radar-feed.sh" # "Lucy kennt sich" (12.07.2026): Selbst-Inventur generiert den Steckbrief + bemerkt # eigene Aenderungen; der Karten-Gutachter stempelt jede Karte mit einer Empfehlung. cp "$SRC/deploy/selbst-inventur.sh" "$HOME/.hermes/scripts/selbst-inventur.sh" cp "$SRC/deploy/karten-gutachter.sh" "$HOME/.hermes/scripts/karten-gutachter.sh" # Trend-Radar (17.07.2026, Sa 05:15): Live-Puls aller Rollen + Engine-A/B + Watch-Liste + # HF-Kandidaten-Suche → max. 1 Prüfstand-Kandidat/Woche. Prüfstand (So 01:00, --no-agent): # volles Programm (Coding/Agent/Recherche/Deutsch/Vision) gegen die Amtsinhaber-Baseline; # Harness + Suiten liegen im Repo ($SRC/deploy/pruefstand/), nur der Runner wird kopiert. cp "$SRC/deploy/trend-radar-feed.sh" "$HOME/.hermes/scripts/trend-radar-feed.sh" cp "$SRC/deploy/pruefstand/pruefstand.sh" "$HOME/.hermes/scripts/pruefstand.sh" # Restore-Probe + Venv-Audit (17.07.2026, monatliche --no-agent-Crons): echte # Wiederherstellungs-Drills (PBS-Canary + Tarball) und CVE-/Outdated-Audit der # Python-Nebendienst-venvs (mem0, voice) — melden nur, ändern nichts. cp "$SRC/deploy/restore-probe.sh" "$HOME/.hermes/scripts/restore-probe.sh" cp "$SRC/deploy/venv-audit.sh" "$HOME/.hermes/scripts/venv-audit.sh" # Gitea-Repo-Anlage (17.07.2026): schließt Lucys „Projekte selbst starten"-Lücke — # legt PRIVATE Projekt-Repos an (Token aus ~/.git-credentials, braucht write:user-Scope). cp "$SRC/deploy/gitea-repo-create.sh" "$HOME/.hermes/scripts/gitea-repo-create.sh" # Werkstatt-Kanban-Profil (Ideen-Queue, 10.07.2026): Leitplanken des Worker-Profils nachziehen # (Profil selbst wurde einmalig per `hermes profile create werkstatt --clone` angelegt). [ -d "$HOME/.hermes/profiles/werkstatt" ] && cp "$SRC/deploy/werkstatt-SOUL.md" "$HOME/.hermes/profiles/werkstatt/SOUL.md" # betrieb-Profil (Ideen-Queue Faden 7, 13.07.2026): Ops-/Mess-/Bench-Worker mit Box-Wissen # statt Lucy-default. Profil selbst wird einmalig per `hermes profile create betrieb # --clone-from werkstatt` angelegt (model coder→hermes, Description fürs Specifier-Routing). [ -d "$HOME/.hermes/profiles/betrieb" ] && cp "$SRC/deploy/betrieb-SOUL.md" "$HOME/.hermes/profiles/betrieb/SOUL.md" chmod +x "$HOME/.hermes/scripts/fremdblick.sh" "$HOME/.hermes/scripts/dreaming-feed.sh" "$HOME/.hermes/scripts/worker.sh" "$HOME/.hermes/scripts/chef-gutachter-feed.sh" "$HOME/.hermes/scripts/hermes-release-radar-feed.sh" "$HOME/.hermes/scripts/radar-selbstkritik-wrapper.sh" "$HOME/.hermes/scripts/idle-radar-feed.sh" "$HOME/.hermes/scripts/selbst-inventur.sh" "$HOME/.hermes/scripts/karten-gutachter.sh" "$HOME/.hermes/scripts/trend-radar-feed.sh" "$HOME/.hermes/scripts/pruefstand.sh" "$HOME/.hermes/scripts/restore-probe.sh" "$HOME/.hermes/scripts/venv-audit.sh" "$HOME/.hermes/scripts/gitea-repo-create.sh" # Werkstatt-Skill (Autonomie E6): Selbstwartungs-Kreislauf für Hermes. mkdir -p "$HOME/.hermes/skills/wartung" cp "$SRC/deploy/skills/wartung/SKILL.md" "$HOME/.hermes/skills/wartung/SKILL.md" # Orchestrator-Skill (Autonomie): Werkstatt verallgemeinert — zerlegen → an Worker delegieren → gaten. mkdir -p "$HOME/.hermes/skills/orchestrator" cp "$SRC/deploy/skills/orchestrator/SKILL.md" "$HOME/.hermes/skills/orchestrator/SKILL.md" # PC-Pfad-Cache-Skill (Traum-Entwurf 09.07.2026, aufbereitet): Cache lesen → bei Miss pc_shell-Tool → merken. mkdir -p "$HOME/.hermes/skills/pc-pfad-cache" cp "$SRC/deploy/skills/pc-pfad-cache/SKILL.md" "$HOME/.hermes/skills/pc-pfad-cache/SKILL.md" cp "$SRC/deploy/skills/pc-pfad-cache/pc_path_lookup.sh" "$HOME/.hermes/scripts/pc_path_lookup.sh" chmod +x "$HOME/.hermes/scripts/pc_path_lookup.sh" # Projekt-Start-Skill (Abloesung 13.07.2026): "erst denken, dann bauen" fuer neue Coding-Projekte # in Hermes Desktop — Plan-Riegel, Savepoints, AGENTS.md/SAVEPOINT.md-Ritual, Kontext-Waechter. # Portiert vom PC-Skill (F:\Coding Stuff\projekt-start), Zed-Verweise durch Hermes Desktop ersetzt. mkdir -p "$HOME/.hermes/skills/projekt-start" cp "$SRC/deploy/skills/projekt-start/SKILL.md" "$HOME/.hermes/skills/projekt-start/SKILL.md" # Konzept-Fliessband-Skill (14.07.2026): rohe Idee → wasserdichtes KONZEPT (kein Code), BEVOR # gebaut wird. heavy (gpt-oss-120b) waehlt den Rollen-Cast, mehrere Denk-Runden schaerfen ein # lebendes Dokument, ein Advocatus Diaboli (fremdblick/GLM) haertet bis wasserdicht (max 3 Runden). # Behebt das delegate_task=coder-Loch: Konzept-Arbeit lief bisher stumm auf dem Code-Modell. # Output = Konzept-Dokument (~/konzepte/-konzept.md) → Vorlage fuer projekt-start in Hermes Desktop. mkdir -p "$HOME/.hermes/skills/konzept-fliessband" cp "$SRC/deploy/skills/konzept-fliessband/SKILL.md" "$HOME/.hermes/skills/konzept-fliessband/SKILL.md" # Agent-Hooks (Faden 5, 13.07.2026): Skripte frisch halten (reproduzierbar, ueberlebt # Box-Neuaufbau). box-steckbrief-inject.sh = Box-Selbstwissen in JEDEN Kanban-Worker # (pre_llm_call, scoped auf task_id → Lucy-Chat unberuehrt); pre-verify-gates.sh = das # bestehende py_compile/dist-Gate. Die REGISTRIERUNG in ~/.hermes/config.yaml (hooks:) # ist EINMALIG von Hand (config.yaml ist Zwei-Schreiber-sensibel, kein Deploy-Schreiber). mkdir -p "$HOME/.hermes/agent-hooks" # Nur kopieren, wenn der Inhalt sich WIRKLICH geaendert hat — ein blindes cp aendert die # mtime auch bei identischem Inhalt und loest sonst bei JEDEM Deploy die harmlose # "script modified since approval"-Warnung von `hermes hooks doctor` aus. for _h in box-steckbrief-inject.sh pre-verify-gates.sh tabu-pfade-guard.py; do _src="$SRC/deploy/agent-hooks/$_h"; _dst="$HOME/.hermes/agent-hooks/$_h" cmp -s "$_src" "$_dst" 2>/dev/null || { cp "$_src" "$_dst"; chmod +x "$_dst"; } done # Worker-Profile lesen IHRE config.yaml, nicht die Top-Level — die worker-Hooks # (box-steckbrief = Box-Wissen, tabu-pfade-guard = Schreibschutz) muessen dort registriert # sein, sonst feuern sie fuer Worker nie (Bug-Fund 13.07.). Idempotent + Backup je Profil. for _prof in "$HOME"/.hermes/profiles/*/config.yaml; do [ -f "$_prof" ] && python3 "$SRC/deploy/ensure-profile-hooks.py" "$_prof" || true done # Eingebaute Hermes-Web-GUI (`hermes serve`) für die Einbettung in MC2 (routers/hermes_ui.py → # same-origin /hermes-ui/) vorbereiten: das SPA-Bundle mit Vite-base=/hermes-ui/ (neu) bauen, sonst # kollidieren seine absoluten Pfade (/assets, /api) mit MC2s eigenen. Läuft auch nach jedem # Hermes-Update mit → hält die Einbettung frisch. Best-effort + Build-zu-Temp-dann-Swap, damit ein # Fehlschlag die laufende web_dist NICHT leert (emptyOutDir). cp "$SRC/deploy/hermes-builtin-ui.service" "$HOME/.config/systemd/user/hermes-builtin-ui.service" _HUI_WEB="$HOME/.hermes/hermes-agent/web" _HUI_DIST="$HOME/.hermes/hermes-agent/hermes_cli/web_dist" if [ -d "$_HUI_WEB" ] && [ -x "$HOME/.hermes/node/bin/npx" ]; then if ( cd "$_HUI_WEB" && PATH="$HOME/.hermes/node/bin:$PATH" npx --no-install vite build \ --base=/hermes-ui/ --outDir /tmp/hermes-ui-build --emptyOutDir ) >/tmp/hermes-ui-build.log 2>&1; then # Dienst VOR dem Tausch stoppen — sonst crasht der laufende `hermes serve`, wenn er index.html # im Lösch-Fenster (rm→cp) liest (FileNotFoundError → Start-Limit → tot). Der reguläre restart # weiter unten bringt ihn sauber auf der neuen web_dist hoch. systemctl --user stop hermes-builtin-ui 2>/dev/null || true rm -rf "$_HUI_DIST" && cp -r /tmp/hermes-ui-build "$_HUI_DIST" && echo "Hermes-GUI (base=/hermes-ui/) gebaut." else echo "WARN: Hermes-GUI-Build fehlgeschlagen (siehe /tmp/hermes-ui-build.log) — bestehende web_dist bleibt." fi fi systemctl --user daemon-reload systemctl --user enable mission-control-2 >/dev/null 2>&1 || true systemctl --user enable mc2-gateway >/dev/null 2>&1 || true systemctl --user enable mc2-steward >/dev/null 2>&1 || true systemctl --user enable box-console >/dev/null 2>&1 || true systemctl --user enable mem0-service >/dev/null 2>&1 || true systemctl --user enable voice-service >/dev/null 2>&1 || true systemctl --user enable hermes-builtin-ui >/dev/null 2>&1 || true systemctl --user enable --now mc2-backup.timer >/dev/null 2>&1 || true # mc2-autoupdate.timer wird hier BEWUSST NICHT geschaltet — der Zustand wird nur von Hand # geändert (Lehre 09.07.: die frühere enable-Zeile hat einen User-Entscheid bei jedem Deploy # still rückgängig gemacht). Aktueller User-Entscheid (10.07.2026): Timer ist AN — # Router/Engine/Hermes dürfen So 04:30 automatisch (Fangnetz Backup→Postcheck→Rollback+Pin). systemctl --user enable --now mc2-selfsmoke.timer >/dev/null 2>&1 || true systemctl --user enable --now mc2-bagatell.timer >/dev/null 2>&1 || true loginctl enable-linger "$USER" >/dev/null 2>&1 || true # Mem0-Sidecar VOR dem Backend (re)starten, damit /api/memory sofort bedient wird. [ -x "$HOME/.mem0/venv/bin/python" ] && systemctl --user restart mem0-service 2>/dev/null || true # Voice-Sidecar (re)starten (best-effort; Erststart lädt das STT-Modell vor). [ -x "$HOME/.voice/venv/bin/python" ] && systemctl --user restart voice-service 2>/dev/null || true # Eingebaute Hermes-Web-GUI (Loopback :9119) (re)starten — MC2 bettet sie unter /hermes-ui/ ein. # reset-failed, damit ein zuvor am Start-Limit gestorbener Dienst wieder anläuft. systemctl --user reset-failed hermes-builtin-ui 2>/dev/null || true systemctl --user restart hermes-builtin-ui 2>/dev/null || true # Gateway VOR dem Steuerpult (re)starten — MC2s /v1-Weiterleiter braucht ihn sofort. # KEIN `|| true`: ohne Gateway ist der LLM-Datenpfad tot, das MUSS den Deploy stoppen. systemctl --user restart mc2-gateway # Steward (Wächter) — unabhängig vom Steuerpult; ohne ihn wacht niemand über die Box. systemctl --user restart mc2-steward systemctl --user restart mission-control-2 command -v ttyd >/dev/null 2>&1 && systemctl --user restart box-console 2>/dev/null || true sleep 2 echo "--- Health ---" # Gateway-Kaltstart-Fenster: bis ~12 s tolerieren, DANN hart scheitern (ohne Gateway # ist der LLM-Datenpfad tot — das muss den Karten-Runner rot machen). for _i in $(seq 1 10); do curl -sf -m 3 http://127.0.0.1:9010/gw/health >/dev/null 2>&1 && break [ "$_i" -eq 10 ] && { echo "FEHLER: mc2-gateway (:9010) antwortet nicht"; exit 1; } sleep 1 done curl -sf http://127.0.0.1:9010/gw/health && echo curl -sf http://127.0.0.1:9001/api/health && echo # Steward hat keinen HTTP-Port — systemd-Zustand ist der Beweis (crasht er sofort, # ist er hier bereits 'failed' statt 'active'). if systemctl --user is-active --quiet mc2-steward; then echo "mc2-steward aktiv (Wächter laufen)" else echo "FEHLER: mc2-steward läuft nicht (journalctl --user -u mc2-steward -n 30)"; exit 1 fi # Warm-Set (Hirn + Augen/vision + Gedächtnis/embed) nach dem Deploy nachladen — ein Deploy bzw. # watch-config-Reload verwirft es sonst und die erste Anfrage wäre kalt (D16d). warmup.sh ist # selbst-detachend (blockiert den Deploy nicht) und lädt jedes Modell über den richtigen Endpunkt. bash "$SRC/deploy/warmup.sh" || true echo "OK — Mission Control 2.0 läuft auf :9001 (User-Dienst, sudo-frei)."