Files
mission-control-v2/deploy/self-smoke.sh
T
Hitonabi bc60c6fefc Betriebs-Luecken: Restore-Probe, Venv-Audit, Disk-Waechter, PC-Watch
Scope-Entscheid 17.07.: MC2 bleibt Box-Zentrale (KEINE Homelab-Zentrale) -
Infra-Radar (Proxmox/PBS/QNAP/Gitea) geht als eigenes Projekt an Lucys Queue.
- restore-probe.sh (Cron 1. d. M. 06:40, no-agent): stellt WIRKLICH wieder her -
  PBS-Canary aus hermes.pxar (selektiv via --pattern, sudo-frei ueber die
  User-Unit-Zugaenge) + Tarball-Probe + Frische-Check <36h; Erstlauf-Fallbacks
  (llamaswap.pxar / Kern-Datei) bis der Canary in den Sicherungen ankommt.
- venv-audit.sh (Cron 2. d. M. 06:40, no-agent): pip outdated + pip-audit-CVEs
  fuer mem0-/voice-venv ueber eigenes Audit-venv; Funde -> Kanban-Karte je Monat,
  Update bleibt Commander-Entscheid.
- self-smoke: Disk-Waechter (Check 7, Schwelle 85%, nennt die groessten Brocken).
- Trend-Radar-Watchlist: neue Typen github_release_major + pypi; Eintraege
  electron-major (PC-Shell auf 33!) und pocket-tts (PyPI-Versionswatch).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 16:50:48 +02:00

196 lines
11 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# Tägliche Selbst-Smoke-Tests der Box (Autonomie 0d): spielt die drei Kern-Pfade AKTIV durch —
# auch OHNE Update — und meldet NUR bei Rot (kein tägliches Grün-Rauschen). Ergänzt den passiven
# Health-Wächter (sentry.py prüft nur Erreichbarkeit) um echte Funktion:
# 1) Gateway/Hirn: der Agent liefert überhaupt eine Antwort.
# 2) Tools: ein harmloser terminal-Befehl durch den echten Agenten (fängt kaputte Approval-/
# Tool-Ketten — Antwort muss kommen und darf nicht in pending_approval hängen).
# 3) Voice: Lucys Sprech-Pfad (/api/voice/chat) streamt.
# Bei Fehlschlag → notify.sh (Telegram + Briefkasten) in Lucys Stimme. Läuft als User, kein sudo.
set -uo pipefail
SRC_DIR="$(cd "$(dirname "$0")" && pwd)"
MC_URL="${MC_URL:-http://127.0.0.1:9001}"
HERMES_URL="${HERMES_API_URL:-http://127.0.0.1:8642}"
HERMES="${HERMES_HOME:-$HOME/.hermes}"
export XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"
say(){ echo "[self-smoke] $*"; }
FAILED=()
HEALED=()
API_KEY="$(grep -E '^API_SERVER_KEY=' "$HERMES/.env" 2>/dev/null | cut -d= -f2- | tr -d '"' | tr -d "'")"
# ── 1) Gateway/Hirn: Agent antwortet ────────────────────────────────────────
if [ -n "$API_KEY" ]; then
RESP=$(curl -sf -m 90 -X POST "$HERMES_URL/v1/chat/completions" \
-H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
-H "X-Hermes-Session-Id: selfsmoke-gateway" \
-d '{"model":"hermes","stream":false,"chat_template_kwargs":{"enable_thinking":false},"messages":[{"role":"user","content":"Antworte NUR mit dem einen Wort: bereit"}]}' 2>/dev/null)
if echo "$RESP" | grep -qi "bereit"; then
say "PASS · Gateway/Hirn antwortet"
else
say "FAIL · Gateway/Hirn ohne verwertbare Antwort: $(echo "$RESP" | head -c 160)"
FAILED+=("Gehirn/Gateway (keine Antwort)")
fi
else
say "SKIP · Gateway (kein API_SERVER_KEY in $HERMES/.env)"
fi
# ── 2) Tools: terminal via Agent ─────────────────────────────────────────────
if [ -n "$API_KEY" ]; then
TOOL=$(curl -sf -m 90 -X POST "$HERMES_URL/v1/chat/completions" \
-H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
-H "X-Hermes-Session-Id: selfsmoke-tool" \
-d '{"model":"hermes","stream":false,"messages":[{"role":"user","content":"Führe im Terminal exakt den Befehl echo selfsmoke-ok aus und gib mir nur dessen Ausgabe zurück."}]}' 2>/dev/null)
if echo "$TOOL" | grep -qi "selfsmoke-ok"; then
say "PASS · Tools (terminal via Agent)"
elif echo "$TOOL" | grep -qi "pending_approval"; then
say "FAIL · Tools hängen in pending_approval (approvals.mode prüfen!)"
FAILED+=("Tools (hängen in Freigabe)")
else
say "FAIL · Tool-Smoke ohne Ergebnis: $(echo "$TOOL" | head -c 160)"
FAILED+=("Tools (kein Ergebnis)")
fi
fi
# ── 2b) Desktop-Gateway: hermes-builtin-ui antwortet ────────────────────────
# Seit dem Hermes-Desktop-Umzug (09.07.2026) hängt die Desktop-App an :9119
# (via MC2-Proxy /hermes-ui/). Stirbt der Dienst, ist die Arbeits-Oberfläche
# am PC tot — das muss der Selbst-Test melden, nicht erst der User.
if curl -sf -m 10 "http://127.0.0.1:9119/api/status" | grep -q '"gateway_running"'; then
say "PASS · Desktop-Gateway (:9119 /api/status)"
else
say "FAIL · Desktop-Gateway: :9119/api/status antwortet nicht (hermes-builtin-ui prüfen)"
FAILED+=("Desktop-Gateway (hermes-builtin-ui :9119)")
fi
# ── 3) Voice: Lucys Sprech-Stream ────────────────────────────────────────────
# Retry: der erste Turn nach Idle zahlt evtl. den Session-Kaltstart (Prefill).
# Subshell OHNE pipefail — head -c schließt die Pipe früh (curl endet mit 23/SIGPIPE),
# das würde einen bestandenen Check sonst als FAIL werten (Lehre aus hermes-postcheck.sh).
voice_ok=0
for try in 1 2 3; do
if ( set +o pipefail; curl -sf -m 45 -N -X POST "$MC_URL/api/voice/chat" -H "Content-Type: application/json" \
-d '{"text":"Sag nur ok.","session_id":"selfsmoke-voice"}' 2>/dev/null | head -c 50 | grep -q "data:" ); then
voice_ok=1; break
fi
sleep 8
done
if [ "$voice_ok" -eq 1 ]; then
say "PASS · Voice (/api/voice/chat streamt, Versuch $try)"
else
say "FAIL · Voice: /api/voice/chat streamt nicht (3 Versuche)"
FAILED+=("Voice (Lucys Sprech-Pfad)")
fi
# ── 4) Repo-Wächter: verlorene Merges (Vorfall 12.15.07.2026) ──────────────
# Der Annahme-Runner merged+pusht nach origin/main; klemmt der Push (Gitea-Auth
# flattert) oder überschreibt eine fremde Session origin/main, trägt Box-main
# Commits, die origin fehlen — der nächste deploy-Reset VERNICHTET sie still
# (so verlor die Box die angenommene Karte 'wartung/lucy-annahme-fixes' vom
# 12.07., bemerkt erst beim Review am 15.07.). Hier täglich gegenprüfen.
LIVE_CO="$HOME/mission-control-v2"
if git -C "$LIVE_CO" fetch -q origin 2>/dev/null; then
LOST=$(git -C "$LIVE_CO" log --oneline origin/main..main 2>/dev/null | head -3 | tr '\n' ' ')
if [ -n "$LOST" ]; then
say "FAIL · Box-main trägt ungepushte Commits (Deploy würde sie vernichten): $LOST"
FAILED+=("Repo (ungepushte Commits auf Box-main: $LOST— vor dem nächsten Deploy nach origin retten)")
else
say "PASS · Repo-Wächter (Box-main == origin/main)"
fi
else
say "SKIP · Repo-Wächter (git fetch fehlgeschlagen)"
fi
# ── 5) Bild-Weiche v2: Bild an coder → Vision beschreibt, Coder antwortet ──────
# Dieser Pfad brach am 15.07. ZWEIMAL still (toter Hermes-Patch; gepoolter Socket
# nach Modell-Swap) — deshalb täglich aktiv durchspielen. Nebeneffekt: wärmt
# coder+vision am Morgen vor. 256x256-Testbild (Winzbilder = VL-Halluzinationen).
BILD_JSON=$(python3 - <<'PYIN'
import struct, zlib, base64, json
w=h=256
raw=b"".join(b"\x00"+b"\xff\x00\x00"*w for _ in range(h))
def chunk(t,d):
c=t+d; return struct.pack(">I",len(d))+c+struct.pack(">I",zlib.crc32(c))
png=b"\x89PNG\r\n\x1a\n"+chunk(b"IHDR",struct.pack(">IIBBBBB",w,h,8,2,0,0,0))+chunk(b"IDAT",zlib.compress(raw))+chunk(b"IEND",b"")
url="data:image/png;base64,"+base64.b64encode(png).decode()
print(json.dumps({"model":"coder","stream":False,"max_tokens":40,"messages":[{"role":"user","content":[{"type":"image_url","image_url":{"url":url}},{"type":"text","text":"Welche Farbe hat das angehaengte Bild? Antworte nur mit der Farbe."}]}]}))
PYIN
)
BW_HDR=$(mktemp); BW_OUT=$(mktemp)
if curl -sf -m 420 -D "$BW_HDR" -X POST "$MC_URL/v1/chat/completions" \
-H "Content-Type: application/json" -d "$BILD_JSON" -o "$BW_OUT" 2>/dev/null \
&& grep -qi "bleibt beim Coder" "$BW_HDR" \
&& grep -qi "rot" "$BW_OUT"; then
say "PASS · Bild-Weiche v2 (Bild an coder → beschrieben → 'rot')"
else
say "FAIL · Bild-Weiche v2: $(grep -i 'x-mc-route-reason' "$BW_HDR" 2>/dev/null | head -1) Antwort: $(head -c 80 "$BW_OUT" 2>/dev/null)"
FAILED+=("Bild-Weiche v2 (Bild an coder — Umleitung statt Beschreibung oder falsche Antwort)")
fi
rm -f "$BW_HDR" "$BW_OUT"
# ── 6) Browser-Tool: agent-browser läuft (Vorfall 16.07.2026) ────────────────
# Hermes' browser_navigate ruft das agent-browser-CLI über die Kette
# ~/.hermes/node/bin → ~/.local/bin/agent-browser. Das Hermes-Update 15.07.
# 23:53 bog den Symlink per npm-postinstall auf node_modules/ um und löschte
# das Paket im selben Lauf wieder (npm ci --workspace) → toter Link, jedes
# „Website öffnen" scheiterte still als „Failed to open". Hier täglich prüfen
# und den bekannten Bruch SELBST heilen: Link aufs npm-global-Binary
# zurückbiegen + Gateway/Desktop-UI neu starten (browser_tool cached ein
# „nicht installiert" pro Prozess — ohne Restart bliebe es kaputt).
AB_LINK="$HOME/.local/bin/agent-browser"
AB_GOOD="$HOME/.local/lib/node_modules/agent-browser/bin/agent-browser-linux-x64"
if "$AB_LINK" --version >/dev/null 2>&1; then
say "PASS · Browser-Tool ($("$AB_LINK" --version 2>/dev/null))"
elif [ -x "$AB_GOOD" ] && "$AB_GOOD" --version >/dev/null 2>&1; then
ln -sfn "$AB_GOOD" "$AB_LINK"
if "$AB_LINK" --version >/dev/null 2>&1; then
systemctl --user try-restart hermes-gateway hermes-builtin-ui >/dev/null 2>&1 || true
say "GEHEILT · Browser-Tool: toter agent-browser-Link neu gesetzt, Gateway/Desktop-UI neu gestartet"
HEALED+=("Browser-Tool (agent-browser-Link war tot — selbst repariert, Gateway/Desktop-UI neu gestartet)")
else
say "FAIL · Browser-Tool: Link neu gesetzt, aber agent-browser startet nicht"
FAILED+=("Browser-Tool (agent-browser startet trotz Link-Reparatur nicht)")
fi
else
say "FAIL · Browser-Tool: agent-browser fehlt (auch kein heiles Binary unter ~/.local/lib)"
FAILED+=("Browser-Tool (agent-browser fehlt — npm install -g agent-browser && agent-browser install --with-deps)")
fi
# ── 7) Disk-Wächter (17.07.2026): /srv/models sammelt Modelle, Backups + Vault wachsen ──
# Schwelle 85 % — meldet mit den größten Brocken, damit die Aufräum-Entscheidung leicht fällt.
DISK_SCHWELLE="${DISK_SCHWELLE:-85}"
while read -r FS NUTZUNG ZIEL; do
PCT="${NUTZUNG%\%}"
if [ "${PCT:-0}" -ge "$DISK_SCHWELLE" ]; then
BROCKEN="$(du -s --block-size=1G /srv/models/*/ 2>/dev/null | sort -rn | head -3 \
| awk '{printf "%s GB %s · ", $1, $2}' || true)"
say "FAIL · Disk: $ZIEL ist zu $NUTZUNG voll (Schwelle ${DISK_SCHWELLE} %)"
FAILED+=("Disk ($ZIEL zu $NUTZUNG voll — größte Brocken: ${BROCKEN:-unbekannt}Kandidat: rollenlose Fallback-Modelle prüfen)")
else
say "OK · Disk: $ZIEL bei $NUTZUNG"
fi
done < <(df --output=source,pcent,target / /srv/models 2>/dev/null | tail -n +2 | sort -u -k1,1)
# ── Selbstheilungen nur melden, nicht alarmieren ─────────────────────────────
if [ "${#HEALED[@]}" -gt 0 ]; then
HEAL_MSG="Commander, mein Selbst-Test hat etwas gefunden und direkt selbst repariert:
$(printf '• %s\n' "${HEALED[@]}")
Nur zur Info — es ist schon wieder grün."
bash "$SRC_DIR/notify.sh" -s "[Selbst-Test]" "$HEAL_MSG" || true
fi
# ── Meldung nur bei Rot ──────────────────────────────────────────────────────
if [ "${#FAILED[@]}" -eq 0 ]; then
say "Alle Selbst-Tests grün ($(date '+%F %H:%M'))."
exit 0
fi
MSG="Commander, mein täglicher Selbst-Test hat etwas gefunden — das läuft gerade NICHT rund:
$(printf '• %s\n' "${FAILED[@]}")
Der Rest der Box kann trotzdem stehen; magst du kurz draufschauen?"
bash "$SRC_DIR/notify.sh" -s "[Selbst-Test]" "$MSG" || true
say "FEHLGESCHLAGEN: ${FAILED[*]}"
exit 1