612ce127fa
- /api/alarm: Lucy-unabhaengiger Telegram-Weg (fuer den PC-Lucy-Watchdog, wenn die App haengt). - self-smoke.sh + mc2-selfsmoke.timer (taeglich 07:15): Gateway/Tools/Voice aktiv durchspielen, Meldung nur bei Rot. Ergaenzt den passiven Health-Waechter um echte Funktion. - self-repair.sh + autoupdate.sh: bei rotem Hermes-Update zieht die Box Config-Brueche selbst (nur eindeutige, nicht-sicherheitsrelevante Keys; Backup -> YAML-Check -> Gehirn-Check, sonst zurueck). Greift es nicht, haengt eine LLM-Diagnose an die Rollback-Meldung. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
100 lines
5.3 KiB
Bash
100 lines
5.3 KiB
Bash
#!/usr/bin/env bash
|
||
# Selbstreparatur-Modus (Autonomie 0g, Kür): Macht ein Hermes-Update den Gehirn-Check rot,
|
||
# WEIL sich Config-Schlüssel geändert haben (v0.18-Klasse: „Unknown key … → stiller Default →
|
||
# alle Tools hingen"), versucht die Box den Config-Bruch SELBST zu ziehen — bevor zurückgerollt
|
||
# wird, um die neue Version zu behalten. NUR Config, NIE Code, NIE Security-Schlüssel.
|
||
#
|
||
# Sicher durch Kapselung — jeder Schritt ist reversibel und hart gegatet:
|
||
# Backup → nur EINDEUTIGE (genau 1× vorkommende), nicht-sicherheitsrelevante Keys auskommentieren
|
||
# → YAML muss gültig bleiben → Gehirn-Check (hermes-postcheck.sh) muss GRÜN werden → sonst zurück.
|
||
# Schlimmster Fall bei Fehlgriff = exakt der heutige: Rollback. Der Aufrufer (autoupdate.sh) ruft
|
||
# dies VOR seinem Rollback; bei Rückgabe 2 rollt er wie gehabt zurück + pinnt.
|
||
#
|
||
# Rückgabe: 0 = repariert (Gehirn-Check danach grün) · 2 = nichts sicher reparierbar.
|
||
# Gibt bei Misserfolg eine Zeile „SELFREPAIR_SUGGESTION=<LLM-Diagnose>" auf stdout aus.
|
||
set -uo pipefail
|
||
|
||
SRC_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||
HERMES="${HERMES_HOME:-$HOME/.hermes}"
|
||
CFG="$HERMES/config.yaml"
|
||
WARN_FILE="${HERMES_CONFIG_WARNINGS:-/tmp/hermes-config-warnings.txt}"
|
||
LLAMA="${LLAMA_SWAP_URL:-http://127.0.0.1:8080}"
|
||
VENV_PY="$HERMES/hermes-agent/venv/bin/python"
|
||
export XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"
|
||
|
||
say(){ echo "[self-repair] $*"; }
|
||
|
||
# ── 1) Evidenz: Postcheck-Warnungen + frisches Gateway-Journal ────────────────
|
||
EVID="$(cat "$WARN_FILE" 2>/dev/null)"
|
||
JOURN="$(journalctl --user -u hermes-gateway --since '-6 minutes' --no-pager -o cat 2>/dev/null \
|
||
| grep -iE "Unknown [a-z._]+ '|deprecated .*(setting|option|config)|defaulting to|invalid config" \
|
||
| grep -v check_fn | head -10)"
|
||
[ -z "$EVID" ] && EVID="$JOURN"
|
||
if [ -z "$EVID" ]; then
|
||
say "Keine Config-Bruch-Hinweise gefunden — hier ist (sicher) nichts zu reparieren."
|
||
exit 2
|
||
fi
|
||
say "Config-Hinweise:"; echo "$EVID"
|
||
|
||
# LLM-Diagnose/Empfehlung (best-effort) — für die Meldung, egal ob wir reparieren.
|
||
SUGG="$(curl -sf -m 60 "$LLAMA/v1/chat/completions" -H 'Content-Type: application/json' \
|
||
-d "$(python3 - "$EVID" <<'PY'
|
||
import json, sys
|
||
p = ("Ein Hermes-Agent-Update hat den Selbsttest rot gemacht. Log-Hinweise:\n" + sys.argv[1] +
|
||
"\n\nNenne auf Deutsch in 1-2 kurzen Sätzen die wahrscheinliche Ursache und die konkrete "
|
||
"Config-Korrektur in ~/.hermes/config.yaml (Schlüssel → neuer Wert). Keine Einleitung.")
|
||
print(json.dumps({"model": "coder-lite", "max_tokens": 200, "temperature": 0.1,
|
||
"messages": [{"role": "user", "content": p}]}))
|
||
PY
|
||
)" 2>/dev/null | python3 -c 'import sys,json; print((json.load(sys.stdin).get("choices") or [{}])[0].get("message",{}).get("content","").strip())' 2>/dev/null)"
|
||
|
||
# ── 2) Offending Leaf-Keys ziehen ────────────────────────────────────────────
|
||
KEYS="$(echo "$EVID" | grep -oE "Unknown [a-zA-Z0-9_.]+|'[a-zA-Z0-9_.]+'" | sed -E "s/^Unknown //; s/'//g" | sort -u)"
|
||
LEAVES="$(for k in $KEYS; do echo "${k##*.}"; done | sort -u | grep -vE '^(true|false|null|)$')"
|
||
|
||
SEC_RX='token|secret|password|approval|api_server_key|ufw|sudo|allowlist|credential'
|
||
BAK="$CFG.selfrepair-bak-$(date +%Y%m%d-%H%M%S)"
|
||
cp -a "$CFG" "$BAK"
|
||
|
||
CHANGED=""; SKIPPED=""
|
||
for leaf in $LEAVES; do
|
||
if echo "$leaf" | grep -qiE "$SEC_RX"; then SKIPPED="$SKIPPED $leaf(security-tabu)"; continue; fi
|
||
# Nur EINDEUTIGE Schlüssel-Zeilen (genau 1×) anfassen — Mehrdeutige sind zu riskant.
|
||
n="$(grep -cE "^[[:space:]]*${leaf}:" "$CFG" 2>/dev/null || echo 0)"
|
||
if [ "$n" = "1" ]; then
|
||
sed -i -E "s|^([[:space:]]*)(${leaf}:.*)$|\1# [selbstrepariert $(date +%F)] \2|" "$CFG"
|
||
CHANGED="$CHANGED $leaf"; say "auskommentiert: $leaf (unbekannt/veraltet)"
|
||
else
|
||
SKIPPED="$SKIPPED $leaf(${n}x)"
|
||
fi
|
||
done
|
||
[ -n "$SKIPPED" ] && say "nicht automatisch angefasst:$SKIPPED"
|
||
|
||
if [ -z "$CHANGED" ]; then
|
||
say "Nichts sicher automatisch reparierbar."
|
||
cp -a "$BAK" "$CFG"
|
||
echo "SELFREPAIR_SUGGESTION=$SUGG"
|
||
exit 2
|
||
fi
|
||
|
||
# ── 3) YAML noch gültig? ──────────────────────────────────────────────────────
|
||
if [ -x "$VENV_PY" ] && ! "$VENV_PY" -c "import yaml; yaml.safe_load(open('$CFG'))" 2>/dev/null; then
|
||
say "YAML nach Fix ungültig → zurück zum Backup."
|
||
cp -a "$BAK" "$CFG"; echo "SELFREPAIR_SUGGESTION=$SUGG"; exit 2
|
||
fi
|
||
|
||
# ── 4) Neustart + Gehirn-Check ────────────────────────────────────────────────
|
||
systemctl --user restart hermes-gateway; sleep 6
|
||
if bash "$SRC_DIR/hermes-postcheck.sh" >/dev/null 2>&1; then
|
||
say "Gehirn-Check nach Selbstreparatur GRÜN."
|
||
bash "$SRC_DIR/notify.sh" -s "[Box-Update]" \
|
||
"Commander, nach dem Hermes-Update hakte der Selbsttest wegen geänderter Einstellungen — ich habe das selbst gerichtet (angepasst:${CHANGED// /, }). Der Test ist wieder grün, die neue Version läuft. Ein Rollback war nicht nötig." || true
|
||
exit 0
|
||
fi
|
||
|
||
say "Auch nach Selbstreparatur rot → zurück zum Backup (der Aufrufer rollt zurück)."
|
||
cp -a "$BAK" "$CFG"
|
||
systemctl --user restart hermes-gateway; sleep 6
|
||
echo "SELFREPAIR_SUGGESTION=$SUGG"
|
||
exit 2
|