feat: harden and extend the CLI (security, UX, robustness, tests)

Sieben Verbesserungen; die Dateien überschneiden sich thematisch, daher ein
Commit (jeder Commit bleibt grün: 115 Tests).

- #1 --check ist scriptbar: Exit 0 wenn Container läuft und erreichbar,
  sonst 5 (check_exit_code / CheckResult).
- #2 --force implementiert: Bypass eines belegten Locks mit Warnung
  (_container_lock) und stop_container(force=…) schluckt Inkonsistenzen.
- #3 stille Trunkierung behoben: chat_completion_text liefert ChatReply
  (content + finish_reason); bei finish_reason=length Hinweis auf stderr,
  --chat gibt Exit 1 bei leerem Content zurück.
- #4 keine vermeidbare Downtime: --change validiert den Modellpfad VOR dem
  Entfernen des laufenden Containers.
- #5 Netzwerk dicht: Port-Publish standardmäßig nur auf 127.0.0.1
  (--expose/expose für alle Interfaces), optionaler --api-key/api_key
  (Server --api-key + Bearer-Token auf allen Requests).
- #6 --stream: Chat-Reply token-weise via SSE auf stdout (stream_chat).
- #7 tests/test_actions.py: Orchestrierungs-Ebene (dry-run-Nebenwirkungen,
  Lock, validate-before-remove, chat/stream/exit-codes).

Doku aktualisiert (Manpage, README, llama.cpp.config.example).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-07-06 16:57:47 +02:00
commit 24202feee6
14 changed files with 485 additions and 32 deletions

View file

@ -98,6 +98,10 @@ llamacppctl --change --config llama.cpp.config --profile deepseek
llamacppctl --chat --config llama.cpp.config --profile qwen35 \
-s "Du antwortest kurz." -p "Was ist 2+2?"
# Chat mit Live-Streaming und großem Antwortbudget (lange Texte)
llamacppctl --chat --config llama.cpp.config --profile qwen35 \
--stream --max-tokens 8000 -p "Schreibe eine kurze Rede zum Jahreswechsel."
# System-Prompt aus einer lokalen Datei, User-Prompt als Literal
llamacppctl --start --config llama.cpp.config --profile qwen35 \
--system-file ./prompts/coding.md -p "Refaktoriere diese Funktion."
@ -125,6 +129,11 @@ Für Details siehe [`docs/SECURITY_AND_OPERATIONS.md`](docs/SECURITY_AND_OPERATI
der URL, erlauben standardmäßig keine Redirects (jeder Hop wird bei
Aktivierung erneut validiert), erzwingen eine Content-Type-Allowlist und ein
hartes Größenlimit sowohl über `Content-Length` als auch beim Streaming.
- Der Port wird standardmäßig **nur auf `127.0.0.1`** veröffentlicht — der
ungeauthentifizierte OpenAI-Endpoint ist also nicht im LAN erreichbar.
`--expose`/`expose = true` bindet auf alle Interfaces; dann sollte per
`--api-key`/`api_key` ein Schlüssel gesetzt werden (Server verlangt ihn, das
Tool sendet ihn als Bearer-Token).
- `container_name` ist verpflichtend und muss pro parallel betriebenem
Profil eindeutig sein, um Docker-Namenskollisionen und ungewollte
Container-Übernahmen zu verhindern.