diff --git a/monitoring/cody_watchdog.py b/monitoring/cody_watchdog.py new file mode 100644 index 0000000..69a8561 --- /dev/null +++ b/monitoring/cody_watchdog.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 +"""Cody-Watchdog — Prüft ob das Cody-Profil läuft (Hermes Daemon + Profil intakt). + +Ausgabe: LEER wenn alles OK. Nur bei Problemen wird ein Alarm-Text ausgegeben. +Läuft als Hermes-Cronjob (no_agent=True, alle 5 Minuten). +Kostet $0.00 — kein LLM-Aufruf. +""" +import os +import subprocess +from datetime import datetime, timezone + +CODY_PROFILE = os.path.expanduser("~/.hermes/profiles/cody") +CONFIG_FILE = os.path.join(CODY_PROFILE, "config.yaml") + + +def check_hermes_daemon(): + """Prüfe ob mindestens ein Hermes-Prozess läuft.""" + try: + result = subprocess.run( + ["pgrep", "-f", "hermes"], + capture_output=True, text=True, timeout=5 + ) + pids = [p for p in result.stdout.strip().split("\n") if p] + return len(pids) > 0, pids + except Exception as e: + return False, [] + + +def check_profile(): + """Prüfe ob Cody-Profil existiert und Config lesbar ist.""" + if not os.path.isdir(CODY_PROFILE): + return False, f"Profil-Verzeichnis fehlt: {CODY_PROFILE}" + if not os.path.isfile(CONFIG_FILE): + return False, f"Config fehlt: {CONFIG_FILE}" + try: + with open(CONFIG_FILE, "r") as f: + content = f.read() + if not content.strip(): + return False, "Config ist leer" + # Minimaler YAML-Check: muss "profile" oder "model" enthalten + if "cody" not in content.lower() and "model" not in content.lower(): + return False, "Config scheint nicht für Cody-Profil" + return True, None + except Exception as e: + return False, f"Config nicht lesbar: {e}" + + +def main(): + now = datetime.now(timezone.utc).strftime("%d.%m.%Y %H:%M UTC") + alerts = [] + + # 1. Hermes-Daemon + daemon_ok, pids = check_hermes_daemon() + if not daemon_ok: + alerts.append("❌ Kein Hermes-Prozess gefunden — Daemon down") + + # 2. Cody-Profil + profile_ok, profile_err = check_profile() + if not profile_ok: + alerts.append(f"❌ Cody-Profil fehlerhaft: {profile_err}") + + # 3. Gateway (optional: nur prüfen wenn Daemon läuft) + if daemon_ok: + try: + gw = subprocess.run( + ["pgrep", "-f", "hermes.*gateway.*cody"], + capture_output=True, text=True, timeout=5 + ) + if not gw.stdout.strip(): + # Gateway nicht aktiv — bei on-demand normal, nur warnen wenn Profil da ist + pass # Cody ist on-demand, kein Alarm + except Exception: + pass + + if alerts: + print(f"🚨 Cody-Watchdog-Alarm {now}\n" + "\n".join(alerts)) + + +if __name__ == "__main__": + main()