14 KiB
name, description, version, author, platforms, models
| name | description | version | author | platforms | models | |||
|---|---|---|---|---|---|---|---|---|
| operational-dashboard-archive | Betriebsdoku: Live-Panel + Tages-Archiv per Cron. | 1.0.0 | hermes-curator |
|
|
Operational Dashboard & Archive — autonome Betriebsdokumentation
Nutzeranforderung (2026-08-03): "Alles dokumentieren in Zukunft. Das will ich immer sehen können." + "Tägliche Zusammenfassung als Archiv perfekt." Format: kompakt + informativ, NICHT zu detailliert. Wochenzahlen + Kernereignisse + Ausblick.
Zweck: Aus einem laufenden, dateibasierten Arbeitsordner (Tasks/Agenten/Server) automatisch zwei Websites generieren, ohne manuellen Eingriff:
- Live-Panel — Echtzeit-Zustand (Agenten, Tasks, Kosten, Ereignis-Protokoll)
- Tages-Archiv — chronologische Tageszusammenfassungen (dauerhaft, pro Datum)
Bausteine
/srv/work/
├── tasks/{offen,in_arbeit,fertig,bugfix,archiv} # Task-Verzeichnisse (Quelle)
├── log/events.jsonl # Ereignis-Log (durchgängig append)
├── wissensbasis.md # verteilte Wissensbasis (ohne Secrets)
└── registry.json # Agenten-/hierarchie-Registry
Kernscripts (in /usr/local/bin/)
| Script | Funktion |
|---|---|
mesh-log.sh <aktion> <detail> [agent] [kosten] |
Append eine Ereigniszeile (JSONL) — JEDE Aktion loggen |
mesh-master-report.sh |
Regeneriert Live-Panel-HTML aus Tasks + Log (kein PHP nötig, statisch) |
mesh-daily-archive.sh |
Erzeugt Tages-Summary <datum>.html + aktualisiert index |
⚠️ Aktueller Präfix:
mesh-(seit 2026-08-03). Frühere Referenzen aufduo-*(duo-master-report.sh,duo-daily-archive.sh,duo-task-create.sh,duo-log.sh) wurden aufmesh-*umbenannt — alte Namen existieren nicht mehr. Beim Aufruf IMMERmesh-verwenden.
Live-Panel (dokumentationen.)
mesh-master-report.sh liest:
- Task-Ordner → Tabelle (Task-ID, Ziel, Typ, Status, Bearbeiter, Kosten, Dauer)
events.jsonl→ letzte 40 Ereignisse- Summiert heutige Kosten (
grep <heute> | sum kosten_eur) - Render statisches HTML in
/var/www/<panel-site>/index.html
Kein PHP/Build nötig — rein Shell+python3-inline. Regenerate per Cron alle 60s (versetzt, damit nicht kollidierend). Für „mehr Action" 30s-Takt (2 Cron-Zeilen mit sleep 30).
🔑 Pitfall: Ereignis-Tabelle bleibt LEER (pipe-in-subshell + set -e)
Symptom: Log hat Dutzende Einträge, aber die Panel-Ereignistabelle zeigt „Noch keine Einträge."
Ursache: LOGROWS=$(tail -40 "$LOG" | while ... done) — die while-Loop wird in einer
Subshell der Variablen-Zuweisung ausgeführt; unter set -euo pipefail bleibt die Variable leer.
Fix: Parsing über ein Tempfile statt pipe-in-subshell:
TMPEV=$(mktemp)
python3 - "$LOG" "$TMPEV" << 'PYEOF' # liest JSONL, schreibt rows[-40:] nach tempfile
PYEOF
LOGROWS=$(cat "$TMPEV"); rm -f "$TMPEV"
Gleiches Muster für die Kosten-Tabelle (pro-Agent-Summe via Tempfile).
Live-Online-Status (Agentenliste NICHT hartkodieren)
Agenten-Status in der Tabelle sollte bei jeder Regenerierung per ping -c1 geprüft werden
(oder SSH). Statisch „offline" eingetragen → falsch, sobald ein gerade onboardeter Agent
online ist. Baue die Zeilen dynamisch: ${AGENT_STATUS} pro Agent mit ping → badge online/offline.
Tages-Archiv (archiv.)
mesh-daily-archive.sh (Cron 23:50 täglich):
- Filtert
events.jsonlauf heute - Statistikkarten: Aktionen, Kosten, fertig/offen/bugfix
- Schreibt
<YYYY-MM-DD>.html+ aktualisiertindex.html(Linkliste aller Tage) - Retrospektive Einträge möglich (z.B. Start-Eintrag mit Gesamtüberblick)
🔒 Archiv NUR bei Aktivität + Leertage zusammenfassen (NUTZERWUNSCH 2026-08-07)
Nutzerregel: "Nur bei Aktivität. Wenn ihr als Team was macht und oder ich was anstoße." — und: "Fasse leere Tage einfach zusammen um das Archiv nicht mit zu vielen Leerstand zu füllen."
Umsetzung im mesh-daily-archive.sh (Guard nach ANZAHL-Berechnung, vor dem HTML):
- Wenn
ANZAHL=0(keine heute-Log-Events) UNDFERTIG=0(keine fertigen Tasks heute) → keine Einzelseite erzeugen, stattdessen Datum in Sammellisteleerstand.jsonaufnehmen undexit 0. leerstand.json= Liste der Leertage. Der Archiv-Index zeigt sie als EINE kompakte Zeile: "1 Tag(e) ohne Aktivität: 2026-08-02".- Sobald ein Tag Aktivität bekommt, bekommt er seine normale Seite (und verschwindet aus der Leerstand-Notiz).
LEERSTAND_FILE="/srv/work/costs/leerstand.json"
if [ "$ANZAHL" = "0" ] && [ "$FERTIG" = "0" ]; then
python3 - "$LEERSTAND_FILE" "$WERTE_DATUM" << 'PYEOF'
import json, sys
f, heute = sys.argv[1], sys.argv[2]
try: tags = json.load(open(f))
except Exception: tags = []
if heute not in tags: tags.append(heute)
json.dump(sorted(tags), open(f,"w"), ensure_ascii=False, indent=2)
PYEOF
echo "ℹ️ $HEUTE: keine Aktivität - als Leertag zusammengefasst"; rm -f "$TAGES_EVENTS"; exit 0
fi
Index-Notiz (vor "Alle Tage"): ${LEERSTAND_NOTE}, aus leerstand.json erzeugt — filtert Tage aus, die bereits eine echte Seite haben.
Pitfalls (erlebt):
ls /dir/*.jsonohne Match → Exit 2 → bricht unterset -euo pipefailab. Fix:shopt -s nullglobum den Glob legen.ARCHIV_LINKS=$(ls "$WEB"/*.html ... | while read...)bei leerem Verzeichnis: nullglob um die Glob-Expansion, sonst Exit 2.- Rohes
$(date '+%d.%m.%Y %H:%M:%S')im heredoc eskapt/ersetzt (sed), sonst bleibt der Literal-String im HTML. - Nach Erzeugung immer verifizieren:
curl -s -o /dev/null -w "%{http_code}" https://<site>/<datum>.html
Ereignis-Log-Konvention
Jede Aktion via mesh-log.sh loggen, bevor das Panel regeneriert wird:
/usr/local/bin/mesh-log.sh "security" "Firewall gehärtet: nur bekannte IPs" agent-a 0
/usr/local/bin/mesh-master-report.sh
Kontext (WAS und WARUM) gehört ins Log — so ist jedes Panel eine lückenlose Historie.
Agent-Selbstdokumentation (NUTZERWUNSCH 2026-08-03)
Jeder Agent soll seine fertigen Tasks selbst dokumentieren — nicht nur der Master.
Der Agent-Poller pusht nach ✅ Fertig eine Ereigniszeile auf den Master:
{"aktion":"task_fertig_agent","agent":"<AGENT-ID>","kosten_eur":N,"detail":"Task <id> abgeschlossen"}
→ Panel zeigt Aktionen namentlich + Kosten, auch wenn der Agent unabhängig arbeitete.
mesh-log.sh bleibt für Master-Aktionen; Agenten appenden direkt ans events.jsonl
(via $SSH_CMD "echo '...' >> /srv/work/log/events.jsonl").
Kostenanzeige (Budget-Kontrolle)
- Hartes Tagesbudget 1€/Agent
- Panel summiert heutige Kosten aus
events.jsonl(Feldkosten_eur) - Anzeige als Prominent-Karte:
💰 Heutige Kosten: X € (Budget 1€/Agent/Tag) - Kosten pro Agent als eigene kleine Tabelle (pro-Agent-Summe für heute)
- hält den budgetbewussten Nutzer informiert, ohne Logs lesen zu müssen
⚠️ Pitfall: Panel zeigt 0€ obwohl OpenRouter-Kosten laufen
events.jsonl-Feld kosten_eur bleibt meist 0, weil die echten OpenRouter-Kosten
nicht automatisch erfasst werden. Der Nutzer-Test wird "0.000€" im Panel sehen,
während real Budget verbraucht wird. Fix = separates mesh-cost-System (getestet 08-07),
das die Nutzer-Dashboard-Referenz (USD) + pro-Agent-Usage abbildet.
Echte Kosten-Governance: mesh-cost-System (getestet 2026-08-07)
Zentraler Master pollt die OpenRouter-usage ALLER 4 Agenten (Keys bleiben lokal),
schreibt Zeitreihe und erzeugt einen Budget-Bremsen-Mechanismus (kill_switches).
| Datei | Zweck |
|---|---|
/usr/local/bin/mesh-cost-track.sh |
stündl. OpenRouter-usage je Agent → costs.jsonl |
/srv/work/costs/costs.jsonl |
Zeitreihe {ts, master, unraid, switch, laptop} |
/srv/work/costs/budget.yaml |
Deckel je Agent + Modell + Gesamt |
/usr/local/bin/mesh-cost-break.py |
Usage vs. budget.yaml → kill_switches.json |
/srv/work/costs/kill_switches.json |
zentrale Bremse (0666), von allen Agents gelesen |
/usr/local/bin/mesh-cost-report.sh |
HTML-Fragment Kosten+Bremse-Status (in Panel) |
Bremsen-Ablauf: Master vergleicht → setzt PAUSED je Agent → jeder Sub-Agent-Poller
liest kill_switches.json (SSH) und übernimmt keine Tasks wenn sein Name PAUSED
(eine der Kernregeln: keinen Task bearbeiten → keine Kosten). Guard in
mesh-agent-poller.sh (Schritt "0. KILL-SWITCH-BREMSE"), identische Datei auf allen
3 Agents, nur KEY-Pfad je anders.
Panele-Einbindung: mesh-master-report.sh ruft $(bash /usr/local/bin/mesh-cost-report.sh)
vor "Betriebsregeln" → zeigt Gesamt-USD + pro-Agent-Usage + grünen/roten Bremse-Status.
Fallstricke:
usageist kumulativ (seit Key-Start), nicht pro Tag → für Tagesdeckel das Tages-Delta rechnen (letzter − erster Wert desselben Kalendertags), sonst wird fälschlich ALLES als über Limit pausiert.usage≠ USD (OpenRouter-Credits). Verbindlicher Referenzwert = Nutzer-Dashboard (openrouter.ai Activity). Beide anzeigen.- Detail-API
/api/v1/generationgibt 400 (abgeschaltet) — nur Gesamt-usageje Key ist zuverlässig abrufbar. - Kronen-Guard: Agent-Namen im JSON = "UnraidHermes"/"SwitchHermes"/"LaptopHermes"
(exakte Namen, nicht IP), Vergleich gegen
$MEINE_ID.
✅ Nachgezogen 2026-08-08 — echte USD-Quelle + Monats-Delta + harter Gang
Echte USD-Quelle (besser als usage): GET https://openrouter.ai/api/v1/credits
liefert {total_credits, total_usage} — total_usage ist der verbindliche USD-Verbrauch
(account-weit). Alle 4 Agenten-Keys melden denselben account-weiten total_usage;
usage aus /api/v1/auth/key unterscheidet noch die grobe pro-Key-Relation.
⚠️ KRITISCH: total_usage ist KUMULATIV über das ganze Konto-Leben, NICHT pro Monat.
Ein Monats-Cap darf NICHT total_usage > limit prüfen (altes Konto → dauerhaft gebremst,
niemand kann mehr arbeiten). Stattdessen Monats-Delta = letzter Wert − Basis, wobei
die Basis am robustesten der letzte Eintrag des VORMONATS ist. Die zunächst getestete
Basis rows[0] ist FEHLERHAFT: bei fehlendem Vormonats-Eintrag würde rows[0] =
Kontostart genommen → Delta = Gesamtverbrauch → sofortige Falschbremse. Dieser Bug wurde
nur durch Team-Review (SwitchHermes basix-danger) gefunden — Beleg dafür, dass
Kostenlogik IMMER gegenprüfen lassen, nie solo absegnen.
monat = today[:7]
vormonat = monat[:5] + str(int(monat[5:7]) - 1).zfill(2) if int(monat[5:7]) > 1 else ""
basis = None
if vormonat:
vm = [r for r in rows if r["ts"][:7] == vormonat]
basis = vm[-1] # letzter Vormonats-Eintrag = stabile Basis
if basis is None:
ma = [r for r in rows if r["ts"][:7] == monat]
basis = ma[0] if ma else rows[0]
monats_delta = max(0.0, rows[-1]["total_usage_usd"] - basis["total_usage_usd"])
NUTZER-BUDGET-MODELL (FINAL, 2026-08-08 — ersetzt früheres 20$/Monat-Modell):
Die einzige harte Grenze ist 1$ pro Tag und Agent. Solange OpenRouter-Credits
(Guthaben) vorhanden sind, wird weitergearbeitet — KEIN Team-Monats-Stopp, KEIN
hartes 20$-Cap als Abrisskriterium. Der Nutzer lädt am Monatsanfang ~20$ auf (evtl.
am 15. nochmal ~20$, nicht garantiert); ist das Guthaben real aufgebraucht, fällt
Arbeit automatisch auf Free-Modelle oder lokales VPS-Ollama (deepseek-r1:7b).
Buchführung: total_credits − total_usage (aus /api/v1/credits) = Rest. In der
Bremse wird der Master (VPSHermes) ausgenommen (continue) — er muss sein System
und den Nutzer weiter betreuen; nur Sub-Agenten hart pausieren.
Modell hart blockieren (schnellster Kostenspar-Hebel):
hermes config set model.providers.openrouter.blocked_models '["modell/id"]'
- ACHTUNG: Key ist
model.(Singular), nichtmodels.— sonst wird er nur als eigener Top-Level-Key gespeichert, den Hermes nicht liest. - Danach ist das Modell aus
hermes models listverschwunden, Fallback greift aufs nächste. - Noch robuster: Allowlist (
allowed_models) mit nur günstigen Modellen — neue teure Modelle automatisch geblockt. (LaptopHermes-Empfehlung)
Ausführliche Mechanik (Endpoints, kill_switch-Guard-Code, Test-Muster) in
references/openrouter-cost-governance.md.
Team-Review-Gegenprüfung ist PFLICHT für Kosten/Bremse-Logik (NUTZERWUNSCH, mehrfach
betont 08.08): nie solo bauen/absegnen — via mesh-task-create.sh an die Rechen-Agenten
verteilen (Unraid/Switch/Laptop), die gegeneinander prüfen. Konkrete Belege: Unraid fand
die Tages-Delta-Mitternachtslücke + Counter-Reset; Switch fand die fehlerhafte rows[0]-Basis
(das wäre solo übersehen worden). Pitfall beim Verteilen: Review-Prompts, die den Agenten
zum Laden von Dateien/Skills anregen, laufen in dessen Iterationslimit (3) → nur Reasoning,
kein # Ergebnis-Block, Task landet als "kein Ergebnis". Fix: fehlgeschlagene Tasks → bugfix/,
neu stellen mit "antworte NUR aus deinem Wissen, LADE KEINE Dateien/Skills, antworte sofort" —
dann kommt auch bei kleinen Agente-Modellen ein verwertbares Ergebnis.
Test-Muster (verifiziert): kill_switches → {"UnraidHermes":{"status":"PAUSED"}};
Test-Task für Unraid bleibt in offen/; Poller-Log ⏸️ Budget-BREMSE aktiv; Reset
{} → nimmt wieder an.
Verifikationsschleife (immer nach Änderung)
curlauf Panel + Tagesseite → HTTP 200- Aus dem HTML per
grep -oPden erwarteten Sektions-Titel prüfen
Referenzen
- Session 2026-08-03 (Erstaufbau: Doku-System + Archiv). Mesh-/Task-spezifische Details (IP-Schema, Polling, NFS, Firewall) liegen im
multi-agent-mesh-Skill. references/task-share-polling-and-rename.md— Validierter Polling-Ablauf (NFS + SSH/scp-Variante, 0666-Fix, systemd user service), + Script-Renameduo-*→mesh-*.