feat(android): provider selection, stop button, adaptive icon, release signing; docs + previews

This commit is contained in:
OpenClaw Dev 2026-10-25 04:52:27 +01:00
parent 49fdc5a189
commit 6ff31589be
38 changed files with 468 additions and 41 deletions

View file

@ -26,7 +26,7 @@ Abhängigkeiten des Backends: `gopkg.in/yaml.v3`, `modernc.org/sqlite`, `github.
- TLS-Zertifikatsprüfung ist überall an (LLM-Provider, `web_fetch`); es gibt keinen Schalter zum Abschalten.
- `web_fetch`: nur http(s), Timeout, Größenlimit, max. 5 Redirects, blockiert Loopback/Private/Link-Local/CGNAT-Adressen beim Verbindungsaufbau (schützt auch gegen DNS-Rebinding und Redirect-Tricks). Abschaltbar per `allow_private`.
- Datei-Tools sind per `os.Root` auf den Workspace beschränkt (kein `..`, keine absoluten Pfade, keine Symlink-Ausbrüche) und größenbegrenzt.
- Kein Shell-Tool.
- Shell-Tool standardmäßig AUS (`tools.shell.enabled`). Wenn an: nur Befehle aus `allowed_commands` (Interpreter/Netzwerk-/Shell-Programme wie `sh`, `bash`, `env`, `xargs`, `python`, `curl`, `git` sind nie erlaubt), jedes Glied von `|`, `&&`, `||`, `;` wird geprüft, `$`, Backticks, `<( )`, Umleitungen, `&`, Klammern werden abgelehnt, keine Pfade außerhalb des Workspace (`/`, `~`, `..`), Ausführung ohne `sh -c` direkt per exec im Workspace, Timeout, Output-Limit, bereinigte Umgebung (nur `PATH`, `HOME`=Workspace, `LANG`, `TERM` – keine API-Keys/Token).
- Docker: distroless, User `65532` (nonroot), Go-Version fixiert.
- Android: Release-Builds erlauben nur HTTPS/WSS; Klartext (http/ws) nur im Debug-Build für lokale Entwicklung. Token/URL verschlüsselt per Android Keystore.
@ -79,10 +79,26 @@ curl -H "Authorization: Bearer $BYTECLAW_AUTH_TOKEN" -d '{"text":"Wie spät ist
| GET | /api/sessions/{id}/messages | Verlauf |
| POST | /api/chat | Nicht-streamend `{"session_id"?, "text"}`|
| GET | /api/ws | WebSocket-Streaming |
| POST | /api/sessions/{id}/cancel | Laufende Antwort abbrechen |
| POST | /api/sessions/{id}/summarize | Zusammenfassung manuell auslösen |
| GET | /api/sessions/{id}/summary | Letzte Zusammenfassung |
| GET | /api/providers | Provider-Liste + aktiv (ohne Keys) |
| PUT | /api/providers/active | Aktiven Provider/Modell setzen `{"provider","model"?}` |
| GET | /api/providers/{name}/models | Modellliste vom Provider (`/v1/models`) |
| PUT | /api/sessions/{id}/provider | Provider nur für diese Session (`{"provider":""}` = Standard) |
Alle `/api/*` brauchen `Authorization: Bearer <token>`.
WebSocket: Client sendet `{"type":"message","session_id":"<optional>","text":"..."}`; Server sendet Events
`session`, `delta`, `tool_call`, `tool_result`, `done` oder `error` (`{"type","text","tool","args","call_id","session_id"}`). `{"type":"ping"}` → `pong`.
`session`, `summarized`, `delta`, `tool_call`, `tool_result`, `done`, `cancelled` oder `error` (`{"type","text","tool","args","call_id","session_id"}`). `{"type":"ping"}` → `pong`.
Abbrechen: `{"type":"cancel","session_id":"…"}` über denselben WebSocket oder `POST /api/sessions/{id}/cancel`. Der Abbruch läuft per Context bis in den HTTP-Request zum Provider; die bis dahin gestreamte Teilantwort wird als Assistant-Nachricht mit `"cancelled":true` gespeichert.
### Provider umschalten
Alle Presets (openai, openrouter, ollama, dahl, gonkarouter) plus der in `llm.*` konfigurierte Provider stehen zur Auswahl. Keys kommen nur aus Env (`DAHL_API_KEY`, `GONKAROUTER_API_KEY`, …) oder `llm.providers.<name>.api_key` und verlassen den Server nie; die App sieht nur `configured: true/false`. Die Auswahl wird in SQLite gespeichert und überlebt Neustarts; pro Session kann ein anderer Provider gesetzt werden.
### Lange Sessions
`agent.summarize`: ab `max_messages` (Standard 40) oder ~`max_tokens` (12000, Zeichen/4) noch nicht zusammengefasster Nachrichten fasst das aktive LLM die älteren Nachrichten zusammen (die neuesten `keep_recent` bleiben wörtlich, Schnitt immer vor einer User-Nachricht). Die Zusammenfassung liegt in der Tabelle `summaries`, die Originalnachrichten bleiben erhalten.
### Build, Tests, Cross-Builds
@ -124,18 +140,37 @@ Build (JDK 17 + Android SDK, `ANDROID_HOME` oder `local.properties` mit `sdk.dir
cd android
./gradlew assembleDebug # app/build/outputs/apk/debug/app-debug.apk
./gradlew testDebugUnitTest
./gradlew assembleRelease # unsigniert; Signing-Config fehlt noch
./gradlew assembleRelease # signiert, wenn Keystore-Daten vorhanden sind (sonst unsigniert)
```
### Release-Signing
Die `signingConfig` liest Keystore-Daten aus Umgebungsvariablen (`BYTECLAW_KEYSTORE_FILE`, `BYTECLAW_KEYSTORE_PASSWORD`, `BYTECLAW_KEY_ALIAS`, `BYTECLAW_KEY_PASSWORD`) oder aus `android/keystore.properties` (Vorlage: `keystore.properties.example`). `keystore.properties`, `*.jks` und `*.keystore` sind gitignored – Keystore niemals ins Repo.
```bash
mkdir -p ~/byteclaw-keys && chmod 700 ~/byteclaw-keys
keytool -genkeypair -keystore ~/byteclaw-keys/byteclaw-release.jks -storetype PKCS12 \
-alias byteclaw -keyalg RSA -keysize 3072 -validity 10000 -dname "CN=ByteClaw, C=DE"
cp android/keystore.properties.example android/keystore.properties # Passwörter eintragen
cd android && ./gradlew assembleRelease
$ANDROID_HOME/build-tools/35.0.0/apksigner verify --print-certs app/build/outputs/apk/release/app-release.apk
```
Keystore und Passwörter sicher sichern: Ohne denselben Schlüssel lassen sich Updates nicht installieren.
### App-Icon
Adaptive Icon als Vektor (`res/drawable/ic_launcher_{background,foreground,monochrome}.xml`, `mipmap-anydpi-v26`): drei Krallen-Kratzer in Claw-Orange plus Byte-Bits in Türkis auf dunklem Navy – dieselben Markenfarben wie im Compose-Theme (`ui/Theme.kt`, `res/values/colors.xml`). Da minSdk 26 ist, deckt der Vektor alle Dichten ab; die Monochrom-Ebene sorgt für Themed Icons ab Android 13. Vorschau: `preview/icon.png`.
In der App unter Einstellungen Server-URL (`https://…`; im Debug-Build auch `http://10.0.2.2:8787` für den Emulator) und Token eintragen, „Verbindung testen", speichern.
## Nächste Schritte
- Release-Signing, App-Icon, CI (GitHub Actions) für Go + Android
- Session-Kontext zusammenfassen statt nur Fenster der letzten N Nachrichten
- Weitere Provider (Anthropic nativ), Abbrechen laufender Antworten über WebSocket
- CI (GitHub Actions) für Go + Android
- Weitere Provider (Anthropic nativ)
- App: Provider pro Session wählen (API existiert schon)
- Optional: Einrichtung per QR-Code (URL + Token)
## Vorschau
Screenshots mit Demo-Daten (Paparazzi, ohne Emulator) liegen in `preview/` (hell/dunkel). Neu erzeugen: `cd android && ./gradlew recordPaparazziDebug`.
Screenshots mit Demo-Daten (Paparazzi, ohne Emulator) liegen in `preview/` (hell/dunkel): Chat mit Stopp-Button, Sessions, Einstellungen, Provider-Auswahl, `icon.png`. Neu erzeugen: `cd android && ./gradlew recordPaparazziDebug`.