Initial: Mesh-Skills, Configs, Scripts
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
---
|
||||
name: mobile-apk-flutter-build
|
||||
description: "Flutter-APK auf headless VPS bauen: Setup + AGP-Pitfalls."
|
||||
version: 1.0.0
|
||||
author: mehlzoerwer
|
||||
platforms: [linux]
|
||||
---
|
||||
|
||||
# Mobile APK bauen (Flutter, headless Linux-VPS)
|
||||
|
||||
Klasse: Eine Flutter-App als Android-APK auf einem headless Linux-VPS bauen und zum Download bereitstellen. Verifiziert 2026-08-07 für PersoEnt (VPS netcup, Ubuntu 24.04).
|
||||
|
||||
Hardware-Notiz: `flutter doctor` meldet "Android Studio not installed" und fehlendes Chrome/Linux-Toolchain — **beide sind für APK-Build nicht nötig**. Android toolchain ✓ reicht.
|
||||
|
||||
## 1. Flutter SDK installieren
|
||||
|
||||
```bash
|
||||
cd /opt
|
||||
curl -sL -o /tmp/flutter.tar.xz "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_3.24.5-stable.tar.xz"
|
||||
tar xf /tmp/flutter.tar.xz # → /opt/flutter
|
||||
echo 'export PATH="$PATH:/opt/flutter/bin"' >> /root/.bashrc
|
||||
export PATH="$PATH:/opt/flutter/bin"
|
||||
flutter --version # prüft Dart-Version (3.5.x)
|
||||
```
|
||||
|
||||
## 2. 🔥 git safe.directory — DER "0.0.0-unknown"-Fix (PITFALL)
|
||||
|
||||
Wenn `flutter --version` / `flutter pub` meldet **"Flutter SDK version 0.0.0-unknown"** oder `fatal: detected dubious ownership in repository at '/opt/flutter'`, dann ist Flutters git-Versionserkennung gebrochen. Fix:
|
||||
```bash
|
||||
git config --global --add safe.directory /opt/flutter
|
||||
```
|
||||
Ohne diesen Schritt schlägt **jeder** `pub get` an Flutters eigener Versionsprüfung fehl ("The current Flutter SDK version is 0.0.0-unknown ... version solving failed"). Das ist NICHT ein echtes Versionsproblem — nur ownership. Nach dem Fix zeigt `flutter --version` wieder `3.24.5 • stable`.
|
||||
|
||||
## 3. Android SDK + Java
|
||||
|
||||
```bash
|
||||
apt-get install -y openjdk-17-jdk unzip
|
||||
|
||||
# cmdline-tools:
|
||||
mkdir -p /opt/android-sdk/cmdline-tools
|
||||
curl -sL -o /tmp/cmdtools.zip "https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip"
|
||||
unzip -q cmdtools.zip -d /opt/android-sdk/cmdline-tools/
|
||||
mv /opt/android-sdk/cmdline-tools/cmdline-tools /opt/android-sdk/cmdline-tools/latest # 'latest' zwingend!
|
||||
|
||||
export ANDROID_HOME=/opt/android-sdk
|
||||
export PATH="$PATH:/opt/android-sdk/cmdline-tools/latest/bin"
|
||||
yes | sdkmanager --licenses
|
||||
sdkmanager "platforms;android-34" "build-tools;34.0.0" "platform-tools"
|
||||
```
|
||||
Gradle lädt fehlende Build-Tools (z.B. 33.0.1) automatisch nach. Android-Studio NICHT nötig.
|
||||
|
||||
## 4. Projekt anlegen + Dependencies
|
||||
|
||||
```bash
|
||||
flutter create --platforms=android --org de.persoent --project-name persoent_app persoent_app
|
||||
cd persoent_app
|
||||
```
|
||||
Statt manuelle Versionen in pubspec zu erraten → `flutter pub add <pkg>` (löst passende Version selbst, inkl. `:^0.0.0` bei 0.x-Paketen):
|
||||
```bash
|
||||
flutter pub add pocketbase google_sign_in http shared_preferences
|
||||
```
|
||||
|
||||
## 5. 🔥 AGP `namespace`-Konflikt mit älteren Plugins (PITFALL)
|
||||
|
||||
Symptom: `flutter build apk` → *"Could not create ... LibraryVariantBuilderImpl. > Namespace not specified. Specify a namespace in the module's build file."* (im Plugin, z.B. `:health_connect`).
|
||||
|
||||
Ursache: AGP 8.1 (im Projekt, `android/settings.gradle`) **erzwingt `namespace`**, aber ältere Plugin-Pakete (z.B. `health_connect ^0.x`) haben keinen. Optionen:
|
||||
- **Einfachster robuster Weg:** das brechende Plugin `flutter pub remove <pkg>` **vorerst entfernen**, das Grundgerüst (Login+Dashboard) bauen → **erst funktionierendes APK, dann Health-Integration gezielt lösen**.
|
||||
- Oder AGP 8.1 → 7.3 zurückstufen (matcht Flutters Gradle), riskant.
|
||||
|
||||
**Bau-Strategie (empfohlen):** erst minimales APK, das kompiliert; native Plugins (Health Connect usw.) danach einzeln, um Fehlerursachen zu isolieren.
|
||||
|
||||
## 5b. 🔥 `health_connect` auf pub.dev ist ein LEERES 0.0.0-Paket (PITFALL)
|
||||
|
||||
Symptom: `flutter pub add health_connect` liefert `health_connect-0.0.0` (im `~/.pub-cache/.../health_connect-0.0.0` nur ein leeres Verzeichnis), und der Build bricht mit dem AGP-`namespace`-Fehler.
|
||||
|
||||
Fakt: **Es existiert KEIN offizielles, funktionsfähiges `health_connect`-Flutter-Paket auf pub.dev.** Googles Health-Connect-Integration ist ein **GitHub-Beispiel**, das das Android-SDK nativ bundelt — nicht als veröffentlichtes pub-Paket gepflegt. `health_connect`/`health_connect_android` auf pub.dev sind leere Platzhalter.
|
||||
|
||||
Konsequenz für PersoEnt: Google-Gesundheitsdaten sind NICHT über ein magisches pub.dev-Paket abrufbar. Der echte Weg erfordert nativen Android-Health-Connect-Code (SDK-Binding) — das skizzierte der Nutzer als langfristige "PersoEnt als App"-Entscheidung; vorerst liefert die APK Login+Dashboard, Health-Import ist separat zu bauen. Siehe auch `references/persoent-google-health-strategy.md` (Google-Fit-REST deprecated, Health Connect nur Android-nativ, kein Server-REST-Sync).
|
||||
|
||||
## 6. Gate + Build
|
||||
|
||||
```bash
|
||||
flutter analyze # vorher: fängt unbenutzte Imports / falsche Klassennamen
|
||||
flutter build apk --release
|
||||
```
|
||||
- APK: `build/app/outputs/flutter-apk/app-release.apk` (~22MB)
|
||||
- Erster Gradle-Build dauert lange (Downloads). Nutze langen Timeout.
|
||||
- `test/widget_test.dart` muss zum neuen Code passen (Default referenziert `MyApp`).
|
||||
|
||||
## 7. Zum Download bereitstellen (VPS)
|
||||
|
||||
```bash
|
||||
mkdir -p /var/www/<projekt>/html/app
|
||||
cp build/app/outputs/flutter-apk/app-release.apk /var/www/<projekt>/html/app/<Name>-Android.apk
|
||||
chown -R www-data:www-data /var/www/<projekt>/html/app/
|
||||
# Test:
|
||||
curl -s -o /dev/null -w "%{http_code}" "https://<domain>/app/<Name>-Android.apk" # 200
|
||||
```
|
||||
|
||||
## 8. 🔥 Google-Sign-In Fehlercode 10 (PITFALL)
|
||||
|
||||
Symptom in der App (Nutzerscreenshot): `PlatformException(sign_in_failed, [google_sign_in] 10: null, null)`. Fehlercode **10 = OAuth-Client-Konfiguration fehlt/unverifiziert**:
|
||||
- Dem Google-Cloud-Projekt fehlt der **Android-OAuth-Client**, oder
|
||||
- der **SHA-1-Fingerprint** der App ist dort nicht hinterlegt.
|
||||
|
||||
Fix (Nutzer-Einbindung nötig, nur deren Konto kann das):
|
||||
1. SHA-1 aus der gebauten APK extrahieren und eintragen:
|
||||
```bash
|
||||
keytool -printcert -jarfile app-release.apk | grep SHA1
|
||||
```
|
||||
2. Google-Cloud → Credentials → OAuth-Client "Android" mit diesem SHA-1 anlegen.
|
||||
3. Die generierte **Client-ID** in `android/app/src/main/AndroidManifest.xml` als `<meta-data android:name="com.google.android.gms.auth.API_KEY" .../>`-Block eintragen, APK neu bauen.
|
||||
|
||||
## 9. 📸 Nutzer-Screenshots auswerten (Handy-Foto) ohne vision-Tool
|
||||
|
||||
Wenn der Nutzer einen Screenshot schickt und **kein vision_analyze-Tool** verfügbar ist, lässt sich der Text (Fehlermeldungen) per **Tesseract-OCR** auslesen:
|
||||
```bash
|
||||
tesseract /root/.hermes/cache/images/<img>.jpg - 2>/dev/null # Text auf stdout
|
||||
```
|
||||
Weg: `browser_get_images` liefert keine lokalen Dateien → stattdessen `file <pfad>` (Maße bestätigen) + `tesseract` zum Extrahieren von Fehlertext. Liefert z.B. `PlatformException(...)`-Zeilen — genug, um die Diagnose (Code 10, etc.) zu stellen. Hinweis: tesseract ist auf Ubuntu oft vorinstalliert (`/usr/bin/tesseract`).
|
||||
|
||||
## Referenzen
|
||||
- `references/persoent-apk-2026-08-07.md` — Session-Transcript: voller Verlauf, Versionen, Fehlerbilder
|
||||
- `references/persoent-google-health-strategy.md` — PersoEnt: Google-Gesundheitsdaten-Strategie (Fit-REST deprecated, Health-Connect nur nativ; Detektiv-Korrelations-Analytik)
|
||||
- `references/persoent-cost-tracking.md` — **PersoEnt/Mesh-Kosten-Tracking + Budget-BREMSE (OpenRouter)**: mesh-cost-track.sh (stündlich, je Agent) + mesh-cost-break.py (kill_switches bei Deckel) + Agent-Poller-Guard (PAUSED → keine Task-Übernahme), E2E-getestet. PITFALLs: DREI OpenRouter-Kostenquellen unterscheiden (`/auth/key usage`≠Credits, `/credits total_usage`=USD verbindlich, Body-`usage.cost`); Usage KUMULATIV → **tag-übergreifendes Delta** (letzter-heute − letzter-gestern, max(0,·) gegen Reset), sonst endlos PAUSED; DeepSeek V4 Pro = 64% (Bremsen auf Modell-Ebene via `hermes config set model.providers.openrouter.blocked_models`, Singular `model.`); Master-exempt von PAUSED. **FINALER NUTZER-COST-MODEL (ab 08-08 ersetzt Monats-Cap): 1$/Tag/Agent hart, solange Credits da, KEIN Team-Monats-Stopp, Guthaben-0→Stopp.**
|
||||
- `references/hermes-config-ops.md` — **Hermes-System-Operation (VPS/Master)**: Config NIE via patch — immer `hermes config set`; `approvals.mode=auto` (allowlist bleibt hart); **BACKUP-ONLY-Regel** (`.bak` vor jeder Änderung → keine extra Freigabe); Memory-Limits (4400/2750, verdoppelt); **„Wir, nicht du" Team-Pflicht** bei Kernsystemen.
|
||||
@@ -0,0 +1,48 @@
|
||||
# Hermes-System-Operation (VPS/Master) — durable Regeln für Config-/Budget-Management
|
||||
|
||||
> Session 2026-08-08: Nutzer legte fest, wie der Master sein eigenes System verwaltet.
|
||||
> Diese Regeln sind klasse-weit gültig und ausdrucklich gewollt — NIE ignorieren.
|
||||
|
||||
## 1. Hermes-Config NIE via patch/editor ändern — IMMER `hermes config set`
|
||||
|
||||
`patch`/`write_file` auf `/root/.hermes/config.yaml` wird abgelehnt („Refusing to write to Hermes config file"). Der Weg ist ausschließlich:
|
||||
|
||||
```bash
|
||||
hermes config set <pfad.zum.key> <wert> # z.B. approvals.mode auto
|
||||
hermes config get <pfad.zum.key> # rücklesen/verifizieren
|
||||
```
|
||||
|
||||
Beispiel (NUTZER-TROTZ): die falsche Schlüsselnotiz `models.` → richtige `model.` (Singular) bei OpenRouter; beide werden von `hermes config set` geschrieben, aber nur der echte Key wirkt.
|
||||
|
||||
## 2. approvals.mode = auto (NUTZERFREIGABE 2026-08-08)
|
||||
|
||||
- `hermes config set approvals.mode auto` — reduziert „allow?"-Rückfragen bei normaler Systemarbeit.
|
||||
- **Sicherheitsnetz bleibt:** die `command_allowlist` (recursive delete, overwrite system config, sudo-Eskalation, …) ist eine **harte Sperre** und greift auch in auto — die kann kein Agent freigeben, bewusst.
|
||||
- `mcp_reload_confirm` + `destructive_slash_confirm`: **bleiben aktiv** (Nutzer: „Auch ok" — als Schutz behalten).
|
||||
|
||||
## 3. BACKUP-ONLY-REGEL (NUTZER, nicht verhandelbar)
|
||||
|
||||
> „Wenn also Backup und Rückkehr Optionen da sind, brauchen wir kein extra allow."
|
||||
|
||||
Vor **JEDER** System-/Config-/Skript-Änderung zuerst ein Backup + Rückkehr-Option anlegen:
|
||||
|
||||
```bash
|
||||
cp <datei> <datei>.bak-$(date +%s)
|
||||
```
|
||||
|
||||
- Vor systemd-Unit-Änderung → aktuelle Unit-Datei sichern.
|
||||
- Vor Firewall/UFW → Regelstand exportieren.
|
||||
- Solange ein Rollback existiert, ist keine weitere Freigabe nötig.
|
||||
- Hermes legt bei `hermes config set` selbst `.bak.*`-Dateien an — zusätzliche `.bak` bei eigenen Skripten trotzdem setzen.
|
||||
|
||||
## 4. Memory-Limits (konfigurierbar, verdoppelt 2026-08-08)
|
||||
|
||||
Der Nutzer wählte „Memory verdoppeln" statt bestehende Einträge zu streichen (beides per `hermes config set`):
|
||||
- `memory.memory_char_limit`: **4400** (war 2200)
|
||||
- `memory.user_char_limit`: **2750** (war 1375)
|
||||
|
||||
Vorher „Memory voll"-Fehler: NICHT bestehende Einträge löschen, sondern Limit erhöhen (Nutzerpräferenz) und/oder kurz verdichten.
|
||||
|
||||
## 5. „Wir, nicht du" — Team-Pflicht (Nutzer-Frust aus 2026-08-08)
|
||||
|
||||
Der Nutzer korrigierte mehrfach: Master soll bei den Kernsystemen NICHT allein bauen, sondern **Team einspannen** (Brainstorming + gegenseitiges Code-Review), auch wenn das mehr Tokens kostet (4 günstige Agents > 1 teurer Agent). Merke: Der Funktions-Review (z.B. Monats-Delta-Logik der Bremse) wurde von Unraid/Switch echte Fehler finden lassen (row[0]-Basis-Wahl fehleranfällig → Vormonats-Basis-Fix). Allein hätte der Master diese Lücken evtl. nicht gesehen. PITFALL: „Es war Solo schneller" ist keine Ausrede — die Team-Gegenprüfung ist der vereinbarte Standard.
|
||||
@@ -0,0 +1,61 @@
|
||||
# PersoEnt APK Build — Session-Transcript (2026-08-07)
|
||||
|
||||
Voller Verlauf des ersten Flutter-APK-Builds auf dem Headless-VPS (netcup, Ubuntu 24.04). Konkrete Fehlerbilder + Versionen, die beim Wiederholen helfen.
|
||||
|
||||
## Versionen (funktionierender Stack)
|
||||
- Flutter 3.24.5 stable · Dart 3.5.4
|
||||
- Java: OpenJDK 17.0.19
|
||||
- Android SDK: platforms;android-34, build-tools;34.0.0 (+ auto-nachgeladen 33.0.1)
|
||||
- AGP 8.1.0 (im Projekt `android/settings.gradle`), Kotlin 1.8.22
|
||||
|
||||
## Fehlerbilder & Fixes
|
||||
|
||||
### 1. "The current Flutter SDK version is 0.0.0-unknown" beim pub get
|
||||
```
|
||||
fatal: detected dubious ownership in repository at '/opt/flutter'
|
||||
Because persoent_app depends on health_connect any which requires Flutter SDK version >=2.5.0, version solving failed.
|
||||
```
|
||||
Ursache: `/opt/flutter` einem anderen Eigentümer (root beim Entpacken) als git-owner erwartet → Flutters git-Versionserkennung bricht. NICHT das health_connect-Paket, das war Red Herring.
|
||||
Fix: `git config --global --add safe.directory /opt/flutter`. Danach `flutter --version` → `3.24.5 • stable • https://github.com/flutter/flutter.git`.
|
||||
|
||||
### 2. `flutter pub add health_connect` half nicht, bis safe.directory gesetzt war
|
||||
Erst safe.directory, DANN wiederholte `pub add` erfolgreich. Reihenfolge zählt: ohne Versionserkennung kann pub nicht lösen.
|
||||
|
||||
### 3. AGP 8.1 `namespace`-Konflikt (health_connect entfernt)
|
||||
```
|
||||
FAILURE: A problem occurred configuring project ':health_connect'.
|
||||
> Could not create an instance of type ... LibraryVariantBuilderImpl.
|
||||
> Namespace not specified. Specify a namespace in the module's build file.
|
||||
```
|
||||
AGP 8.1 erzwingt `namespace` in jedem Modul; älteres `health_connect`-Plugin (version ^0.0.0) hat keins. Fix: `flutter pub remove health_connect` und Grund-APK (ohne natives Health-Plugin) bauen. Health-Integration separat lösen (z.B. AGP-Angleich oder anderes Plugin).
|
||||
|
||||
### 4. `test/widget_test.dart` Default bricht analyze
|
||||
Default-Test referenziert `MyApp` (alte Template-Klasse). Nach App-Umbau: Test auf echten App-Widget-Namen updaten (hier `PersoEntApp`, `expect(find.text('PersoEnt'), findsWidgets)`).
|
||||
|
||||
## Build-Befehle
|
||||
```bash
|
||||
export PATH="$PATH:/opt/flutter/bin"
|
||||
export ANDROID_HOME=/opt/android-sdk
|
||||
cd /opt/persoent_app
|
||||
flutter analyze # sauber machen
|
||||
flutter build apk --release # erstmal lang (Gradle-Downloads); ~2min danach
|
||||
# Ergebnis: build/app/outputs/flutter-apk/app-release.apk (22.2MB)
|
||||
```
|
||||
|
||||
## Deploy zum Download
|
||||
```bash
|
||||
mkdir -p /var/www/persoent.perfekt-ohne-en.de/html/app
|
||||
cp build/app/outputs/flutter-apk/app-release.apk /var/www/persoent.perfekt-ohne-en.de/html/app/PersoEnt-Android.apk
|
||||
chown -R www-data:www-data /var/www/persoent.perfekt-ohne-en.de/html/app/
|
||||
# Zugriff: https://persoent.perfekt-ohne-en.de/app/PersoEnt-Android.apk → HTTP 200
|
||||
```
|
||||
|
||||
## PocketBase aus App heraus (CORS/Vorbereitung)
|
||||
- App nutzt `PocketBase('https://persoent.perfekt-ohne-en.de/pb')` (nginx `/pb/` → 127.0.0.1:8090)
|
||||
- **Offen (noch nicht gelöst):** CORS für den mobilen Client muss in PocketBase freigegeben werden, sonst schlägt `authWithPassword` aus der App fehl. `test/widget_test.dart` vorhanden — nur Grundgerüst ohne Health-Connect.
|
||||
|
||||
## Status
|
||||
- ✅ Login-Screen (E-Mail/Passwort via PocketBase) + Dashboard-UI (Schritte/Puls/Schlaf/Gewicht-Platzhalter)
|
||||
- ⏳ Health-Connect-Plugin + echtem Datenabgleich: noch offen (AGP-Konflikt)
|
||||
- ⏳ Google-Sign-In: Dependencies vorhanden, Flow noch nicht verdrahtet
|
||||
- ⏳ PocketBase-CORS für App-Client
|
||||
@@ -0,0 +1,149 @@
|
||||
# PersoEnt-Kosten-Tracking + Budget-Bremse (OpenRouter) — Verlauf & Referenzwerte
|
||||
|
||||
> Session 2026-08-07: Nutzer wollte Kostenübersicht für Statistik + künftige Mesh-Bremse. Es wurde festgestellt, dass **keine Kosten im Ereignis-Log erfasst** waren (zeigt 0€), obwohl OpenRouter Budget verbraucht war. Danach wurde das komplette **Bremse-System gebaut und E2E-getestet**.
|
||||
|
||||
## Das gebaute System (Master/VPS)
|
||||
|
||||
| Komponente | Pfad | Zweck |
|
||||
|---|---|---|
|
||||
| `mesh-cost-track.sh` | `/usr/local/bin/` | Stündlich OpenRouter-Usage aller 4 Agenten (VPS+3 Sub) via API abfragen, in Zeitreihe schreiben |
|
||||
| Zeitreihe | `/srv/work/costs/costs.jsonl` | wachsende Statistik je Agent (ts, master, unraid, switch, laptop) |
|
||||
| `mesh-cost-report.sh` | `/usr/local/bin/` | erzeugt HTML-Kosten-Übersicht (Dashboard-USD + pro-Agent + Bremse-Status) |
|
||||
| `budget.yaml` | `/srv/work/costs/` | Deckel: `limits.agent.*`, `limits.gesamt_pro_tag`, `modelle.erlaubt/blockiert`, `bremse.kill_switch_file` |
|
||||
| `mesh-cost-break.py` | `/usr/local/bin/` | vergleicht Tages-Usage vs. budgurations → schreibt `kill_switches.json` |
|
||||
| `kill_switches.json` | `/srv/work/costs/` | zentral, **chmod 0666** — welcher Agent PAUSED |
|
||||
| Agent-Poller-Guard | in `mesh-agent-poller.sh` | liest kill_switches per SSH; bei PAUSED → keine Task-Bearbeitung |
|
||||
| Cron (stündlich) | `0 * * * *` | `mesh-cost-track.sh && python3 mesh-cost-break.py` |
|
||||
| Doku-Integration | master-report.sh | `$(bash /usr/local/bin/mesh-cost-report.sh)` vor Betriebsregeln |
|
||||
|
||||
## 🔥 PITFALL 1: OpenRouter-Usage-Einheit ≠ Dollar
|
||||
|
||||
- `GET https://openrouter.ai/api/v1/auth/key` mit `Authorization: Bearer <KEY>` → `data.usage` = **OpenRouter-Credits je Key**.
|
||||
- Für die Agenten (je eigener Key in `~/.hermes/.env`) ergeben sich z.B.: master 39.5, unraid 0.33, switch 0.64, laptop 0.87.
|
||||
- **Summiert (≈41) DIFFERIERT vom OpenRouter-Dashboard ($20.51)** — die API-Credits sind nicht 1:1 USD, und es können verschiedene Key-Pools/Konten sein.
|
||||
- **Verbindlicher Maßstab ist der Dashboard-USD-Wert** (Nutzer liest ihn in der OpenRouter-UI ab). Den als `nutzer_dashboard_usd` mit Quelle in die Zeitreihe schreiben.
|
||||
|
||||
## 🔥 PITFALL 2: Usage ist KUMULATIV — Tages-Delta berechnen
|
||||
|
||||
Ohne Korrektur wäre **jeder Agent dauerhaft PAUSED**, weil die kumulierte Gesamt-Usage (seit Key-Start) jeden Deckel sprengt. Fix im Brems-Skript: **Tages-Delta = letzter Wert minus erster Wert desselben Kalendertags**:
|
||||
|
||||
```python
|
||||
today = last["ts"][:10]
|
||||
today_rows = [r for r in rows if r["ts"][:10] == today]
|
||||
first = today_rows[0] if today_rows else last
|
||||
delta = max(0.0, float(last[agent]) - float(first[agent]))
|
||||
```
|
||||
|
||||
## Bremsen (GEBAUT + E2E-getestet 2026-08-07)
|
||||
|
||||
Nutzerwunsch: Master überschaut Kosten pro+gesamt über ein wachsendes Mesh (mehr Agents, mehrere Server) und kann alles bremsen. Umsetzung:
|
||||
|
||||
1. `mesh-cost-break.py` liest letzte Usage-Zeile (Tages-Delta), vergleicht mit `budget.yaml`-Deckeln.
|
||||
2. Bei Deckel-Überschreitung (je Agent ODER Gesamt) schreibt es `kill_switches.json` mit `PAUSED`-Status + Grund.
|
||||
3. Jeder Sub-Agent hat im `mesh-agent-poller.sh` einen **Guard** am Anfang (vor Task-pull), der per SSH prüft: "bin ich PAUSED?" → wenn ja: `exit 0` (keine Task-Übernahme, keine weiteren Kosten).
|
||||
|
||||
```bash
|
||||
# Guard in mesh-agent-poller.sh (nach SSH_CMD, vor Task-pull)
|
||||
KILL_CHECK=$($SSH_CMD "python3 -c \"
|
||||
import json
|
||||
try: d=json.load(open('/srv/work/costs/kill_switches.json'))
|
||||
except Exception: d={}
|
||||
sw=d.get('kill_switches',{}).get('<MEINE_ID>',{})
|
||||
print('PAUSED' if sw and sw.get('status')=='PAUSED' else 'OK')
|
||||
\" 2>/dev/null")
|
||||
if [ "$KILL_CHECK" = "PAUSED" ]; then log "⏸️ Budget-BREMSE aktiv"; exit 0; fi
|
||||
```
|
||||
|
||||
**E2E-Test:** UnraidHermes auf PAUSED gesetzt → sein Task blieb in `offen/` (Bremse griff), Poller-Log zeigt "⏸️ Budget-BREMSE aktiv ... pausiert". Nach Reset (leere kill_switches) wieder normal. Der aktualisierte Poller-Guard muss an alle 3 Sub-Agenten verteilt werden (je passender Key-Pfad: `unraid_mesh`/`switch_mesh`/`laptop_mesh`).
|
||||
|
||||
## Kostenfakt (OpenRouter-Analyse 07.08, 1 Monat)
|
||||
|
||||
- **DeepSeek V4 Pro = 64% aller Kosten (~$26.80)** — teures Top-Modell, nicht Standard lt. Model-Allocation; der Haupt-Kostentreiber.
|
||||
- Andere: V4 Flash 0731 22,5%, V3 5,9%, Nemotron 2,4%, GLM 1,5%.
|
||||
- **→ Bremsen muss auf Modell-Ebene ansetzen** (teure Modelle blockieren/deckeln), nicht nur auf Agent-Tages-Summe. Aktuell `modelle.blockiert` in budget.yaml als Konfig/Log; harter Blocker (Modell gar nicht erst laden) ist offen.
|
||||
|
||||
## Abfrage-Snippets
|
||||
|
||||
### Master (lokal)
|
||||
```bash
|
||||
python3 << 'PYEOF'
|
||||
import json,re,urllib.request
|
||||
e=open('/root/.hermes/.env').read()
|
||||
m=re.search(r'OPENROUTER_API_KEY=([A-Za-z0-9_\-\\.]+)',e)
|
||||
req=urllib.request.Request("https://openrouter.ai/api/v1/auth/key",
|
||||
headers={"Authorization":f"Bearer {m.group(1)}"})
|
||||
print(json.load(urllib.request.urlopen(req,timeout=15))["data"]["usage"])
|
||||
PYEOF
|
||||
```
|
||||
|
||||
### Sub-Agent per SSH (Key bleibt lokal, nur Zahl kommt zurück)
|
||||
```bash
|
||||
u=$(sshpass -p 'Bp3lskt0' ssh -o StrictHostKeyChecking=no -o ConnectTimeout=12 mehlzoerwer@<IP> '
|
||||
python3 -c "
|
||||
import json,re,urllib.request
|
||||
e=open(\"/home/mehlzoerwer/.hermes/.env\").read()
|
||||
m=re.search(r\"OPENROUTER_API_KEY=([A-Za-z0-9_.\-]+)\", e)
|
||||
req=urllib.request.Request(\"https://openrouter.ai/api/v1/auth/key\", headers={\"Authorization\":\"Bearer \"+m.group(1)})
|
||||
print(json.load(urllib.request.urlopen(req, timeout=15))[\"data\"].get(\"usage\",0))
|
||||
"')
|
||||
```
|
||||
|
||||
## Erweiterungen/POLISH 2026-08-08 (Team-geprüft: Unraid+Switch+Laptop)
|
||||
|
||||
Das System wurde im Team geschliffen (Gegenprüfung fand echte Fehler). Ergänzungen:
|
||||
|
||||
### 🔥 PITFALL 3: Es gibt DREI OpenRouter-Kostenquellen — verwechsle sie nicht
|
||||
|
||||
| Endpoint | Feld | Einheit | Zweck |
|
||||
|---|---|---|---|
|
||||
| `GET /api/v1/auth/key` | `usage` | OpenRouter-Credits (je Key) | pro-Key-Verteilung, **grobe Relation nur** |
|
||||
| `GET /api/v1/credits` | `total_usage` | **USD, konto-weit** | **VERBINDLICH fürs Gesamtbudget** |
|
||||
| `POST chat/completions` | `usage.cost` | USD (im Body) | Kosten je Antwort (Header `X-OR-Usage-Cost-USD` oft NICHT da — Body bevorzugen) |
|
||||
|
||||
**Merke:** `/auth/key usage` ≠ USD und summiert anders als `/credits`. Beispiel: `/auth/key`-Werte (41.3) vs `/credits total_usage` (81.45 USD) vs Dashboard ($20.51) — drei verschiedene Zahlen. Für Gesamt-Deckel IMMER `/api/v1/credits` `total_usage` (USD) nehmen; pro-Key-`usage` nur als Aktivitäts-Relation.
|
||||
|
||||
### Tages-Delta KORRIGIERT (tag-übergreifend statt innerhalb des Tages)
|
||||
|
||||
Der naiv "letzter minus erster Wert des Kalendertags" hat Lücken: Mitternachts-Lücke zwischen Tag N und N+1, Ein-Eintrag-Tag → 0, Counter-Reset → negatives Delta (falsche "Ersparnis"). **Korrekt:**
|
||||
```python
|
||||
# Delta für Tag = letzter Wert heute − letzter Wert VORTAG; max(0,…) gegen Reset
|
||||
vorher = [d for d in daten if d < heute]
|
||||
base = tvals[vorher[-1]] if vorher else cur
|
||||
delta = max(0.0, cur - base)
|
||||
```
|
||||
Zusätzlich: **7-Tage-Kumulativ-Summe** (fängt "täglich unter Deckel, aber Monat explodiert") + **80%-Warnschwelle** vor hartem PAUSED.
|
||||
|
||||
### Master-exempt (Design-Entscheidung)
|
||||
Der **Master (VPSHermes) wird NIE hart pausiert** — er muss sein eigenes System + den Nutzer pflegen. Bei Monats-USD-Cap-Überschreitung werden NUR Sub-Agenten PAUSED. So bleibt das Kun-den-System bedienbar, auch wenn gespart wird.
|
||||
|
||||
### Modell hart blockieren (nicht nur Konfig/Log)
|
||||
Umsetzung des "Bremsen auf Modell-Ebene": Hermes selbst sperrt das Modell, damit es gar nicht erst geladen wird.
|
||||
```bash
|
||||
hermes config set model.providers.openrouter.blocked_models '["deepseek/deepseek-v4-pro"]'
|
||||
```
|
||||
- **Achtung Singular:** Schlüssel ist `model.providers.…` NICHT `models.` (sonst nur notiert, nicht gelesen).
|
||||
- Verifikation: Modell taucht in `hermes models list` nicht mehr auf; Fallback greift aufs nächste.
|
||||
- Allowlist (`allowed_models` nur günstige) ist noch strikter — blockt automatisch neue teure Modelle.
|
||||
|
||||
### Referenzwerte 2026-08-08
|
||||
- `/credits total_usage` (konto-weiter USD-Verbrauch): **$81.53**
|
||||
- Nutzer-Entscheidung: **strikt sparen** — Monats-Cap auf $80 gesetzt → Sub-Agenten aktuell PAUSED (Master aktiv).
|
||||
- Nach dem Setzen von Deckeln IMMER `mesh-cost-break.py` nochmal laufen lassen + prüfen, dass Master-exempt korrekt greift (kein Alle-Pausiert-Dauerschleife).
|
||||
|
||||
## 🔥 FINALES NUTZER-KOSTEN-MODELL (verbindlich ab 2026-08-08/09) — ersetzt obiges Monats-Cap-Gedächtnis
|
||||
|
||||
**Achtung: Der Nutzer hat das Budgetmodell zuletzt klar und EINFACH gemacht. Folge NUR diesem, nicht den komplexen $-Caps oben:**
|
||||
|
||||
- **Harte Grenze = 1 USD pro Tag und Agent** (`limits.tageslimit_agent_usd: 1.0`).
|
||||
- **Solange OpenRouter-Credits (Guthaben) vorhanden sind, wird gearbeitet.**
|
||||
- **KEIN Team-weiter Monats-Stopp** — nur pro-Agent-Tageslimit + technischer Stopp, wenn das Konto-Guthaben 0 erreicht.
|
||||
- Auflade-Rhythmus des Nutzers: ~20$ am 1. des Monats, evtl. noch ~20$ am 15. (nicht garantiert). Das ist nur die Auflade-Geschichte, NICHT der Brems-Heebel.
|
||||
- Master (VPSHermes) bleibt immer aktiv; nur Sub-Agenten werden pausiert.
|
||||
- `budget.yaml` entsprechend: `tageslimit_agent_usd: 1.0`, `tageslimit_gesamt_usd: 4.0` (4 Agents × 1$), keine `gesamt_usd_monat`-Logik.
|
||||
- Guthaben-Check im break: `guthaben = total_credits - total_usage`; wenn ≤ 0 → Sub-Agenten pausieren (Master nicht).
|
||||
|
||||
**PITFALL Vermeidung:** Nicht wieder ein komplexes „Monats-Cap in $" / Team-Stopp-Schema einführen — der Nutzer hat das aktiv zurückgenommen („Du oder wir?"-Korrekur + „1$ pro Tag und Agent … solange Credits da, kein Monats-Stopp"). Der Fehler aus dem Tag: Tages-Delta/`gesamt_pro_tag`-Key-Bruch und Monats-Cap-Überkomplexität.
|
||||
|
||||
## Referenzwerte 2026-08-07 (historisch)
|
||||
- OpenRouter-Dashboard-Gesamt: **$20.51**
|
||||
- API-Usage je Agent: master 39.48, unraid 0.33, switch 0.64, laptop 0.87
|
||||
@@ -0,0 +1,29 @@
|
||||
# PersoEnt: Google-Gesundheitsdaten-Strategie (2026-08-07)
|
||||
|
||||
Fachliche Entscheidung für die PersoEnt-App (Flutter-Android-Prototyp). Verhindert, dass eine künftige Session in die tote Server-REST-Sackgasse läuft.
|
||||
|
||||
## 🔑 Kern-Fakt: Es gibt KEINE Server-REST-API mehr für Google-Gesundheitsdaten
|
||||
|
||||
- **Google Fit REST API ist deprecated** und wird abgeschaltet — eine Neuentwicklung darauf ist ein totes Pferd.
|
||||
- **Health Connect** (der Nachfolger) hat **keine öffentliche REST-Schnittstelle für Server-Backend-Sync** — nur die native Android-API (SDK).
|
||||
- Konsequenz: Echte Google-Gesundheitsdaten (Schritte, Puls, Schlaf, Gewicht) lassen sich serverseitig (VPS) **nicht** per REST-API automatisch pullen.
|
||||
|
||||
## Der funktionierende Weg (nativ, zukunftssicher)
|
||||
|
||||
PersoEnt muss eine **echte App** sein (nicht Web) → dann nativ:
|
||||
- **Android:** Health-Connect-SDK direkt in der App → Felder wie `derived:com.google.step_count.delta`, `com.google.weight`, `com.google.heart_rate.bpm`, `com.google.sleep.segment` lesen.
|
||||
- **iOS (später):** HealthKit (Schritte, Puls, Schlaf, Gewicht).
|
||||
- Die App synchronisiert mit PocketBase-Backend (VPS, `health_records`).
|
||||
|
||||
## Wichtige agile Kompatibilität
|
||||
|
||||
- Das pub.dev-Paket **`health_connect` = leer (Version 0.0.0, kein echter Code)** — NICHT nutzbar. Googles offizielle Flutter-Health-Connect-Integration ist ein GitHub-Beispiel, das das Android-SDK nativ bundelt; keine einsatzbereite pub.dev-Bibliothek.
|
||||
- 2026 gesundet/Weg: natives Android-Health-Connect-SDK im bestehenden Flutter-Projekt (MethodChannel) oder natives Android-Modul. Kein toter publicum REST.
|
||||
|
||||
## Die "Detektiv"-Analytik (Produktkern)
|
||||
|
||||
Wenn Einträge im `health_records` vorliegen (≥3 pro Nutzer), zeigt die Korrelations-Analyse Zusammenhänge — aber **immer als Korrelation, nie als Kausal-Behauptung** (DSGVO-/Haftungs-sicher; "kein Heil-/Diagnoseversprechen"). PocketBase `sort=-id` (nicht `sort=-created`, das 400 gibt) für "neueste zuerst".
|
||||
|
||||
## Test-Zugang PersoEnt (Stand 2026-08-07)
|
||||
- PocketBase-Superuser + users-Collection; Test-Login `mehlzoerwer@perfekt-ohne-en.de`.
|
||||
- APK-Build-Mechanik: siehe übergeordneter Skill `mobile-apk-flutter-build`.
|
||||
Reference in New Issue
Block a user