From 5f1e34dad9eafd2d1763eeb797345ffe6ef668b3 Mon Sep 17 00:00:00 2001 From: ALI YESILKAYA Date: Fri, 19 Jun 2026 00:05:02 +0200 Subject: [PATCH] =?UTF-8?q?feat(stt):=20lancement=20&=20cycle=20de=20vie?= =?UTF-8?q?=20du=20client=20=E2=80=94=20auto-start,=20app=20de=20bureau,?= =?UTF-8?q?=20auto-update=20(#18)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(stt): auto-start via `stt --install-service` (phase 6) Ajoute `stt --install-service` / `--uninstall-service` : génère et active un service systemd --user (lié à graphical-session.target) qui lance le HUD + voix à l'ouverture de session. Le kiosk existant (ui.kiosk) fournit l'« écran Jarvis ». - cli : _service_unit (génération du unit, fonction pure testable), _cmd_install_service (écrit le unit + import-environment DISPLAY/WAYLAND + daemon-reload/enable/start), _cmd_uninstall_service. - Docs : admin/ia/stt.md (phase 6 → ✅, section Auto-start + caveat DISPLAY), stt/README. - Bump client 0.2.1 → 0.3.0. Phase 6 de la roadmap STT. Reste : test sur poste, outils Hermes (7). Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_017Qjq5jHiqNepnobJpHYpCa * feat(stt): app de bureau (fenêtre type Discord) + auto-update Rend STT lançable comme une application de bureau et ajoute la mise à jour intégrée. - Mode fenêtre « app » : _open_browser ouvre le HUD en fenêtre chromeless (--app=, type Discord) en plus du kiosk plein écran ; support Brave ajouté. Sélection via --window {app,kiosk,none} ou [ui].window_mode. - stt --install-desktop / --uninstall-desktop : entrée .desktop + icône (stt/hud/icon.svg, aussi favicon du HUD) → STT dans le menu/dock, lance --window app. - stt --update : pipx reinstall depuis la source git (via clé SSH), affiche « ancienne → nouvelle » ; stt --version. - Docs (admin/ia/stt.md, stt/README) + config (window_mode) + bump 0.3.0 → 0.4.0. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_017Qjq5jHiqNepnobJpHYpCa * fix(stt): détection navigateur cross-distro (AlmaLinux/Fedora + Debian/Ubuntu) _open_browser tente les binaires natifs (rpm + deb : brave-browser / chromium / chromium-browser / google-chrome…) puis Flatpak (com.brave.Browser, org.chromium.Chromium, com.google.Chrome…), sinon repli sur le navigateur par défaut. systemd --user, lanceurs .desktop et pipx sont déjà identiques sur les deux familles. Doc : note de compatibilité distros. Bump 0.4.0 → 0.4.1. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_017Qjq5jHiqNepnobJpHYpCa * docs(stt): journal 2026-06-18 + PROGRESS + ligne STT du CLAUDE.md - progress/2026-06-18.md : HUD avancé, auto-start, app de bureau, auto-update, cross-distro - PROGRESS.md : entrée du jour - CLAUDE.md : ligne STT enrichie (HUD, auto-start, app de bureau, auto-update) Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_017Qjq5jHiqNepnobJpHYpCa * feat(stt): fenêtre app autonome (profil + WM class dédiés, hors session Brave) Le mode app s'attachait à la session Brave de l'utilisateur (même profil/icône) → « ça sent le Brave ». On lance désormais une instance séparée : --class=STT-Funk + --user-data-dir dédié (~/.local/share/stt/app-profile), et le .desktop déclare StartupWMClass=STT-Funk → fenêtre autonome avec sa propre icône de barre des tâches. (Flatpak : --class seul, sandbox.) Une vraie fenêtre native (pywebview) reste une option ultérieure. Bump 0.4.1 → 0.4.2. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_017Qjq5jHiqNepnobJpHYpCa --------- Co-authored-by: Claude --- CLAUDE.md | 2 +- PROGRESS.md | 1 + admin/ia/stt.md | 83 +++++++++-- progress/2026-06-18.md | 80 +++++++++++ stt/README.md | 4 + stt/client/config/stt.example.toml | 3 +- stt/client/pyproject.toml | 2 +- stt/client/stt/cli.py | 218 +++++++++++++++++++++++++++++ stt/client/stt/config.py | 3 + stt/client/stt/hud/icon.svg | 13 ++ stt/client/stt/hud/index.html | 1 + stt/client/stt/ui/app.py | 51 ++++++- 12 files changed, 444 insertions(+), 17 deletions(-) create mode 100644 progress/2026-06-18.md create mode 100644 stt/client/stt/hud/icon.svg diff --git a/CLAUDE.md b/CLAUDE.md index b713e3b..34891d8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -17,7 +17,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co - ✅ postfix_relay — SMTP sortant via Gmail SMTP + App Password (aliyesilkaya93@gmail.com) - ✅ Open WebUI opérationnel — `openwebui.lab.local`, namespace `ai`, backend LiteLLM - ✅ n8n opérationnel — `n8n.lab.local`, namespace `ai`, 3 workflows actifs (alertes, rapport horaire, Hermes Doc Worker) -- ✅ STT (assistant vocal « Jarvis ») — STT-server in-cluster (`stt.lab.local`, namespace `ai`) + client `stt` (pipx) ; mémoire long-terme Qdrant `stt-memory` validée 2026-06-17 (voir `admin/ia/stt.md`) +- ✅ STT (assistant vocal « Jarvis ») — STT-server in-cluster (`stt.lab.local`, namespace `ai`) + client `stt` (pipx) : HUD avancé (design Claude Design), auto-start systemd `--user`, app de bureau (`stt --install-desktop`, fenêtre type Discord) + auto-update (`stt --update`) ; mémoire long-terme Qdrant `stt-memory` validée 2026-06-17 (voir `admin/ia/stt.md`) - ✅ Ghostfolio opérationnel — `ghostfolio.lab.local`, namespace `ai`, suivi de portefeuille (PostgreSQL s01 + Redis in-cluster) - ✅ hermes-auto-improve — analyse quotidienne de `admin/` par Hermes à 22h (n8n → trigger :9095), PR sur `hermes/daily-work` - ⏳ WOL compute nodes non configuré (BIOS) — allumage manuel requis diff --git a/PROGRESS.md b/PROGRESS.md index 834b0d8..c9f4a82 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -10,3 +10,4 @@ | [2026-05-13](progress/2026-05-13.md) | Stabilisation multi-modèles — OOM fixes, Qwen3-1.7B sur 14 CPUs (21 tok/s), profils system/monitor finalisés, funk-ai par défaut | | [2026-05-14](progress/2026-05-14.md) | Monitoring complet + fix webhook AlertManager→Hermes (BrokenPipeError). RAG opérationnel : Qdrant 284 chunks, rag-ingest/rag-query, skill funk-ai. Souls révisés (4 profils). Docs : alertmanager-webhook, rag, hermes-souls | | [2026-05-29](progress/2026-05-29.md) | Migration `/srv/data` RAID5 dégradé → NVMe (sdb ATA errors). Tous les services sur NVMe, bind-mount `/home/data` | +| [2026-06-18](progress/2026-06-18.md) | STT : HUD avancé (design Claude Design, #17), auto-start systemd, app de bureau (`.desktop` + mode fenêtre), auto-update (`stt --update`), compat AlmaLinux/Debian. Passe de cohérence doc (#15, #16) | diff --git a/admin/ia/stt.md b/admin/ia/stt.md index 2e839ba..0bd7b29 100644 --- a/admin/ia/stt.md +++ b/admin/ia/stt.md @@ -12,10 +12,11 @@ Objectif ultérieur : « agir sur le homelab » via les outils de Hermes (phase > ✅ **2026-06-17** — déployé in-cluster (ArgoCD), mémoire court-terme (5a) + long-terme (5b) > **validées sur cible**, embeddings dédiés nomic `:1238` (5d) + fix thinking Qwen3 `/no_think` > (5e). -> ✅ **2026-06-18** — HUD avancé (4) implémenté (design **Claude Design**) : avatar portrait -> réactif (anneau/ping/spinner), transcript à bulles, drawer Réglages **câblé au backend** -> (reset / mode cerveau / mot de réveil à chaud). Reste : test audio bout-en-bout sur le poste -> (phase 1), portraits réels + test HUD sur cible, auto-start (6), outils Hermes (7). +> ✅ **2026-06-18** — HUD avancé (4) + auto-start (6). HUD : design **Claude Design** (avatar +> portrait réactif, transcript à bulles, drawer Réglages **câblé au backend** : reset / mode +> cerveau / mot de réveil à chaud). Auto-start : `stt --install-service` (systemd --user). +> App de bureau : `stt --install-desktop` (fenêtre type Discord) ; auto-update : `stt --update`. +> Reste : test audio bout-en-bout (phase 1), portraits réels + test sur cible, outils Hermes (7). --- @@ -29,7 +30,7 @@ Objectif ultérieur : « agir sur le homelab » via les outils de Hermes (phase | Modèle local **ou** Claude | LiteLLM `:4000` + `hermes-default` + `hermes-switch qwen\|claude` | ✅ opérationnel | | Agir sur le homelab | Hermes Agent (`:8080`, profils funk-ai/system/monitor/brain) | ✅ opérationnel | | Voix (STT + TTS + wake word) | `tools/hermes-voice/` — faster-whisper + Piper + webrtcvad | ✅ existe (CLI only) | -| Démarrage auto au boot | pattern `systemd --user` (`tools/hermes-voice/install-service.sh`) | ✅ éprouvé | +| Démarrage auto au boot | `stt --install-service` (systemd --user) — repris du pattern `tools/hermes-voice/` | ✅ | | Mémoire sémantique | Qdrant `:6333` + RAG (`rag-query`/`rag-ingest`) sur s01 | ✅ Qdrant réparé 17/06 (RAG `funk-docs` à re-ingérer) | Maillon réellement manquant : **l'interface graphique HUD** + le packaging + la mémoire perso. @@ -157,6 +158,71 @@ Côté client, pilotable depuis `stt/client/config/` + l'écran de réglages du --- +## Auto-start (service systemd --user) + +Lancer le HUD + voix automatiquement à l'ouverture de session (écran « Jarvis ») : + +```bash +stt --install-service # écrit ~/.config/systemd/user/stt.service, enable + start +stt --uninstall-service # retire le service +``` + +Le unit est lié à `graphical-session.target` (démarre avec la session graphique, s'arrête avec). +Suivi : `systemctl --user status|restart stt`, `journalctl --user -u stt -f`. + +**Caveat DISPLAY** : un service `systemd --user` n'hérite de `DISPLAY`/`WAYLAND_DISPLAY` que si la +session graphique les a importés dans le gestionnaire user. `--install-service` exécute un +`systemctl --user import-environment DISPLAY WAYLAND_DISPLAY XAUTHORITY` (best-effort). Si le kiosk +ne s'ouvre pas : vérifier que le bureau peuple `graphical-session.target` (GNOME/KDE le font), ou +lancer `stt` via les « applications au démarrage » du bureau. Le kiosk est piloté par `[ui].kiosk` +(défaut `true`). + +**Boot sans login** (borne dédiée) : `sudo loginctl enable-linger "$USER"` + auto-login du bureau — +la session graphique démarre au boot et tire le service. + +--- + +## Application de bureau & mise à jour + +**Lancer STT comme une appli** (fenêtre dédiée type Discord, pas un onglet) : + +```bash +stt --install-desktop # entrée de menu (.desktop) + icône → épinglable au dock +stt --uninstall-desktop # la retirer +``` + +Le lanceur exécute `stt --window app` : le HUD s'ouvre en fenêtre *chromeless* (`--app=` de +Chromium/Brave). Modes via `--window` ou `[ui].window_mode` : `app` (fenêtre), `kiosk` (plein +écran, défaut du service), `none` (n'ouvre rien). L'icône est `stt/hud/icon.svg` (aussi favicon). +La fenêtre tourne dans un **profil + une WM class dédiés** (`--class=STT-Funk`, `--user-data-dir` +sous `~/.local/share/stt/app-profile`) → **instance autonome** avec sa propre icône/entrée de barre +des tâches (associée au `.desktop` via `StartupWMClass`), séparée de ta session Brave. (Une vraie +fenêtre native sans navigateur — via `pywebview` — reste possible plus tard si besoin.) + +> ⚠️ Le lanceur de bureau et le service systemd (kiosk) utilisent les **mêmes ports** (9300/9301) : +> n'en faire tourner qu'un à la fois. Borne dédiée → service kiosk ; usage à la demande → lanceur +> de bureau (et `stt --uninstall-service`). + +**Mettre à jour** (sans désinstaller/réinstaller à la main) : + +```bash +stt --update # pipx reinstall depuis la source git (via ta clé SSH) — affiche ancienne → nouvelle +stt --version # version installée +``` + +`--update` re-récupère la dernière version depuis la branche/source d'installation. Pré-requis : +avoir installé via `pipx` depuis git. Astuce : toujours bumper la version du paquet à chaque +release, sinon le cache de build pipx/uv peut resservir l'ancienne (cf. `pyproject.toml`). + +**Compatibilité distros** : tout le client repose sur des standards communs — `systemd --user`, +lanceurs `.desktop` (XDG `~/.local/share/applications/`), `pipx` — donc **identique sur +AlmaLinux/Fedora (RHEL) et Debian/Ubuntu**. Seul le navigateur diffère : `_open_browser` teste +les binaires natifs (rpm + deb) puis **Flatpak** (`com.brave.Browser`, `org.chromium.Chromium`…), +sinon repli sur le navigateur par défaut. `update-desktop-database` (`desktop-file-utils`) est +optionnel — son absence n'empêche rien. + +--- + ## Roadmap | Phase | Objectif | État | @@ -171,7 +237,7 @@ Côté client, pilotable depuis `stt/client/config/` + l'écran de réglages du | **5c — Réparation Qdrant** | drop `funk-docs` corrompu + restart (s01) | ✅ (17/06) | | **5d — Embeddings dédiés** | `nomic-embed-text` (dim 768) sur gpu-01 `:1238` → recherche plus précise | ✅ (rôle llama_server + migration auto collection) | | **5e — Fix thinking Qwen3** | `/no_think` (content vide / timeout 502) + fallback reasoning_content | ✅ (17/06) | -| **6 — Auto-start client** | `install-service.sh` (systemd --user) + kiosk | ⏳ | +| **6 — Auto-start client** | `stt --install-service` (systemd --user, `graphical-session.target`) + kiosk | ✅ (à tester sur poste) | | **7 — Outils Hermes** | « agir sur Funk » via gateway `:8080` (API à spécifier) | ⏳ | ### État (validé sur cible) @@ -179,8 +245,9 @@ Côté client, pilotable depuis `stt/client/config/` + l'écran de réglages du Serveur déployé in-cluster via ArgoCD (image `sha-` gérée par CI). Mémoire **5a + 5b validées en prod** : court-terme (historique de session) et long-terme (rappel cross-session « Felix », `points_count` confirmé via `/v1/memory/health`). HUD avancé (4) **implémenté** -(design Claude Design, câblé au websocket backend). Reste côté poste : test audio bout-en-bout -(micro → STT → serveur → TTS) et test du HUD avec portraits réels. +(design Claude Design, câblé au websocket backend) ; auto-start (6) via `stt --install-service`. +Reste côté poste : test audio bout-en-bout (micro → STT → serveur → TTS) et test du HUD/kiosk +avec portraits réels. --- diff --git a/progress/2026-06-18.md b/progress/2026-06-18.md new file mode 100644 index 0000000..9f8620b --- /dev/null +++ b/progress/2026-06-18.md @@ -0,0 +1,80 @@ +# 2026-06-18 — STT : HUD avancé, app de bureau & auto-update + +## Résumé + +Grosse itération sur le **client STT** (`stt/client/`) : intégration du HUD « design Claude +Design » câblé au backend (phase 4, mergé #17), puis auto-start systemd, application de bureau +(lanceur `.desktop` + mode fenêtre), auto-update intégrée et compatibilité cross-distro +(AlmaLinux/Fedora + Debian/Ubuntu). En préambule, une passe de cohérence documentaire (#15, #16). + +--- + +## HUD avancé (phase 4) — mergé (#17) + +Remplacement du HUD minimal (cercle CSS bleu) par le design Claude Design : avatar portrait +réactif (anneau + ping d'écoute + spinner de réflexion), transcript à bulles avec tag du modèle, +indicateur de connexion + reconnexion auto, drawer Réglages. + +- HTML/CSS/JS **sans dépendance externe** (livrable par pipx, offline). `DEMO=false` branché + sur le websocket existant (`state` / `user` / `assistant` / `error` sur `:9300`). +- Réglages **câblés au backend** (`_apply_settings`) : `reset`, mode cerveau (→ modèle LiteLLM), + mot de réveil — appliqués à chaud. Visuels (intensité, avatar) en localStorage. +- Portraits dans `hud/avatars/`, repli sur l'initiale (« あ ») si absents. +- Fix « ancien HUD persistant » : `Cache-Control: no-store` + URL kiosk cache-bustée (`?v=`). + +> Piège du jour : après réinstall, Brave resservait l'ancien HUD depuis son cache, et pipx/uv +> réutilisait l'ancien build car la version n'avait pas changé → d'où la discipline **bump de +> version à chaque release** + en-têtes no-cache. + +--- + +## Auto-start (phase 6) + +`stt --install-service` / `--uninstall-service` : génère un service `systemd --user` lié à +`graphical-session.target` (lance HUD + voix à l'ouverture de session, kiosk), avec +`import-environment DISPLAY/WAYLAND` pour le kiosk. Caveat DISPLAY documenté dans `admin/ia/stt.md`. + +--- + +## Application de bureau + auto-update + +- **Mode fenêtre « app »** : `_open_browser` ouvre le HUD en fenêtre chromeless (`--app=`, type + Discord) en plus du kiosk. Sélection via `--window {app,kiosk,none}` ou `[ui].window_mode`. +- **Lanceur de bureau** : `stt --install-desktop` → entrée `.desktop` + icône (`hud/icon.svg`, + aussi favicon) dans le menu d'applis, épinglable au dock. Exécute `stt --window app`. +- **Auto-update** : `stt --update` (pipx reinstall depuis git via SSH, affiche `ancienne → nouvelle`) + + `stt --version`. Plus de désinstall/réinstall manuel. + +> ⚠️ Service kiosk et appli de bureau partagent les ports 9300/9301 → un seul à la fois. + +--- + +## Compatibilité cross-distro + +Tout le client repose sur des standards communs (systemd --user, `.desktop` XDG, pipx) → identique +sur **AlmaLinux/Fedora (RHEL)** et **Debian/Ubuntu**. Seul le navigateur diffère : `_open_browser` +tente les binaires natifs (rpm + deb) puis **Flatpak**, sinon repli sur le navigateur par défaut. + +--- + +## Passe de cohérence documentaire (#15, #16) + +- **#15** : cohérence `admin/` + README/CLAUDE (STT, Ghostfolio, embeddings nomic `:1238`, Qdrant + réparé / `funk-docs` à re-ingérer), archivage de `Funk/` legacy (LM Studio / Goose). +- **#16** : ajout du **switch réseau GS308EV4** au schéma d'architecture du README (+ table de + câblage, ports/VLAN, mode Access). + +--- + +## Versions client + +`0.1.0 → 0.4.1` : **0.2.0** (HUD) · **0.3.0** (auto-start) · **0.4.0** (app + update) · **0.4.1** (cross-distro). + +--- + +## Prochaines étapes + +- Tester le HUD/kiosk + audio bout-en-bout (micro → STT → serveur → TTS) sur le poste. +- Déposer les vrais portraits dans `hud/avatars/`. +- **Phase 7** — outils Hermes (« agir sur Funk » via gateway `:8080`, API à spécifier). +- Re-`rag-ingest` de la collection `funk-docs` (toujours en attente). diff --git a/stt/README.md b/stt/README.md index 5369915..849b2a5 100644 --- a/stt/README.md +++ b/stt/README.md @@ -30,6 +30,10 @@ stt --setup # génère ~/.config/stt/stt.toml (dont [server] url) stt --text # chat texte simple (sans micro ni HUD) — idéal pour tester stt # voix + HUD ; dis "Ok Hermès …" stt --model claude # choisir le modèle : hermes (défaut) | claude | qwen | opus +stt --install-service # auto-start au login (systemd --user, kiosk « écran Jarvis ») +stt --install-desktop # ajoute STT au menu des applis → fenêtre dédiée (type Discord) +stt --window app # ouvre le HUD en fenêtre app (sinon : kiosk plein écran) +stt --update # met à jour depuis git (plus de désinstall/réinstall manuel) ``` En mode texte : commandes `/model `, `/models`, `/quit`. Prérequis voix : micro, Piper + voix dans `~/.local/share/piper/`, `aplay` (alsa-utils). diff --git a/stt/client/config/stt.example.toml b/stt/client/config/stt.example.toml index f93b116..9d2e0d5 100644 --- a/stt/client/config/stt.example.toml +++ b/stt/client/config/stt.example.toml @@ -23,7 +23,8 @@ chat_timeout_sec = 60 [ui] theme = "arc-reactor" avatar = "default" -kiosk = true # ouvre le HUD en plein écran (kiosk) au lancement +kiosk = true # (compat) plein écran si window_mode absent +window_mode = "kiosk" # "app" (fenêtre, type Discord) | "kiosk" (plein écran) | "none" ws_host = "127.0.0.1" ws_port = 9300 # websocket états ↔ HUD (doit matcher hud/index.html) http_port = 9301 # HTTP statique qui sert le HUD diff --git a/stt/client/pyproject.toml b/stt/client/pyproject.toml index f9be9fb..08273a1 100644 --- a/stt/client/pyproject.toml +++ b/stt/client/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "stt" -version = "0.2.1" +version = "0.4.2" description = "STT — client vocal Jarvis du homelab Funk (voix + HUD, parle au STT-server)" requires-python = ">=3.11" readme = "README.md" diff --git a/stt/client/stt/cli.py b/stt/client/stt/cli.py index 8a590ed..1902d6e 100644 --- a/stt/client/stt/cli.py +++ b/stt/client/stt/cli.py @@ -4,6 +4,7 @@ from __future__ import annotations import argparse import asyncio +import os import sys @@ -22,6 +23,193 @@ def _cmd_setup() -> int: return 0 +SERVICE_NAME = "stt.service" + + +def _service_unit(stt_bin: str) -> str: + """Contenu du unit systemd --user : lance `stt` (HUD + voix) avec la session graphique.""" + return f"""[Unit] +Description=STT — Assistant vocal Jarvis (HUD + voix) +After=graphical-session.target +PartOf=graphical-session.target + +[Service] +Type=simple +ExecStart={stt_bin} +Restart=on-failure +RestartSec=5 +Environment=PYTHONUNBUFFERED=1 +StandardOutput=journal +StandardError=journal +SyslogIdentifier=stt + +[Install] +WantedBy=graphical-session.target +""" + + +def _cmd_install_service() -> int: + """Installe et démarre le service systemd --user (auto-start au login + kiosk).""" + import shutil + import subprocess + from pathlib import Path + + stt_bin = shutil.which("stt") or os.path.realpath(sys.argv[0]) + unit_dir = Path.home() / ".config" / "systemd" / "user" + unit_dir.mkdir(parents=True, exist_ok=True) + unit_path = unit_dir / SERVICE_NAME + unit_path.write_text(_service_unit(stt_bin)) + print(f"✓ Unit écrit : {unit_path}") + print(f" ExecStart = {stt_bin}") + + def sc(*args): + subprocess.run(["systemctl", "--user", *args], check=False) + + # Rendre DISPLAY/WAYLAND dispo au gestionnaire systemd --user (pour le kiosk) + subprocess.run( + ["systemctl", "--user", "import-environment", + "DISPLAY", "WAYLAND_DISPLAY", "XAUTHORITY"], + check=False, + ) + sc("daemon-reload") + sc("enable", SERVICE_NAME) + sc("start", SERVICE_NAME) + + print("\n✓ Service activé et démarré (systemd --user).") + print("\nCommandes utiles :") + print(" systemctl --user status stt") + print(" journalctl --user -u stt -f") + print(" systemctl --user restart stt") + print(" stt --uninstall-service") + print("\nDémarrer dès le boot (kiosk dédié, sans login manuel) :") + print(' sudo loginctl enable-linger "$USER" # + auto-login du bureau') + print("\nLe HUD s'ouvre en kiosk si [ui].kiosk = true (défaut).") + print("Si l'écran ne s'ouvre pas : voir le caveat DISPLAY dans admin/ia/stt.md.") + return 0 + + +def _cmd_uninstall_service() -> int: + """Arrête, désactive et supprime le service systemd --user.""" + import subprocess + from pathlib import Path + + def sc(*args): + subprocess.run(["systemctl", "--user", *args], check=False) + + sc("stop", SERVICE_NAME) + sc("disable", SERVICE_NAME) + unit_path = Path.home() / ".config" / "systemd" / "user" / SERVICE_NAME + if unit_path.exists(): + unit_path.unlink() + print(f"✓ Unit supprimé : {unit_path}") + else: + print("(unit déjà absent)") + sc("daemon-reload") + print("✓ Service STT désinstallé.") + return 0 + + +def _desktop_entry(stt_bin: str, icon_path: str) -> str: + """Contenu du lanceur .desktop : STT comme application de bureau (fenêtre app).""" + return f"""[Desktop Entry] +Type=Application +Name=STT — Assistant vocal +GenericName=Assistant vocal +Comment=Assistant vocal Jarvis du homelab Funk +Exec={stt_bin} --window app +Icon={icon_path} +Terminal=false +Categories=Utility;AudioVideo; +Keywords=jarvis;assistant;voix;hermes; +StartupNotify=true +StartupWMClass=STT-Funk +""" + + +def _cmd_install_desktop() -> int: + """Crée l'entrée de menu (.desktop) + icône → STT lançable comme une appli (type Discord).""" + import shutil + import subprocess + from pathlib import Path + + stt_bin = shutil.which("stt") or os.path.realpath(sys.argv[0]) + icon_path = Path(__file__).resolve().parent / "hud" / "icon.svg" + apps_dir = Path.home() / ".local" / "share" / "applications" + apps_dir.mkdir(parents=True, exist_ok=True) + entry = apps_dir / "stt.desktop" + entry.write_text(_desktop_entry(stt_bin, str(icon_path))) + entry.chmod(0o755) + subprocess.run(["update-desktop-database", str(apps_dir)], check=False) + + print(f"✓ Lanceur : {entry}") + print(f" Icône : {icon_path}") + print(f" Exec : {stt_bin} --window app") + print("\nSTT apparaît dans le menu des applications (épinglable au dock).") + print("Il s'ouvre en fenêtre « app » (sans onglets), comme Discord.") + print("Retirer : stt --uninstall-desktop") + return 0 + + +def _cmd_uninstall_desktop() -> int: + """Supprime le lanceur de bureau.""" + import subprocess + from pathlib import Path + + entry = Path.home() / ".local" / "share" / "applications" / "stt.desktop" + if entry.exists(): + entry.unlink() + print(f"✓ Lanceur supprimé : {entry}") + else: + print("(lanceur déjà absent)") + subprocess.run(["update-desktop-database", str(entry.parent)], check=False) + return 0 + + +def _installed_version() -> str | None: + from importlib.metadata import PackageNotFoundError, version + + try: + return version("stt") + except PackageNotFoundError: + return None + + +def _cmd_version() -> int: + print(_installed_version() or "version inconnue (pas installé via pip/pipx)") + return 0 + + +def _cmd_update() -> int: + """Met à jour STT depuis sa source git (pipx reinstall) — sans désinstall/réinstall manuel.""" + import re + import shutil + import subprocess + + pipx = shutil.which("pipx") + if not pipx: + print("pipx introuvable — mise à jour automatique impossible.") + print("Réinstalle manuellement depuis la source git.") + return 1 + before = _installed_version() + if before: + print(f"Version installée : {before}") + print("Récupération de la dernière version depuis git (pipx reinstall)…\n") + res = subprocess.run([pipx, "reinstall", "stt"], capture_output=True, text=True) + out = ((res.stdout or "") + (res.stderr or "")).strip() + if out: + print(out) + m = re.search(r"installed package stt ([0-9][^\s,]*)", out) + after = m.group(1) if m else None + print() + if after and before and after != before: + print(f"✓ Mis à jour : {before} → {after}") + elif after: + print(f"✓ Déjà à jour ({after}).") + else: + print("✓ Réinstallation terminée — vérifie avec : stt --version") + return res.returncode + + def _make_client(args): from stt.api import ServerClient from stt.config import load_config, resolve_model @@ -85,6 +273,11 @@ def _cmd_voice(args) -> int: from stt.voice import VoiceEngine cfg, client = _make_client(args) + # mode d'ouverture du HUD : --window > config window_mode > kiosk (compat) > none + ui = cfg["ui"] + ui["window_mode"] = ( + args.window or ui.get("window_mode") or ("kiosk" if ui.get("kiosk") else "none") + ) print(f"STT-server : {client.url} — modèle '{client.model}'") if not client.healthy(): print(" ⚠️ serveur injoignable — vérifie [server] url et le réseau") @@ -116,10 +309,35 @@ def main(argv: list[str] | None = None) -> int: p.add_argument("--model", help="Modèle : hermes, claude, qwen, opus (ou alias LiteLLM)") p.add_argument("--server", help="Force l'URL du STT-server (sinon config)") p.add_argument("--no-tts", action="store_true", help="Réponses texte seulement (voix)") + p.add_argument("--window", choices=["app", "kiosk", "none"], + help="Ouverture du HUD : app (fenêtre, type Discord) | kiosk (plein écran) | none") + p.add_argument("--install-service", action="store_true", + help="Installe le service systemd --user (auto-start au login + kiosk)") + p.add_argument("--uninstall-service", action="store_true", + help="Désinstalle le service systemd --user") + p.add_argument("--install-desktop", action="store_true", + help="Crée le lanceur de bureau (.desktop + icône) — appli type Discord") + p.add_argument("--uninstall-desktop", action="store_true", + help="Supprime le lanceur de bureau") + p.add_argument("--update", action="store_true", + help="Met à jour STT depuis git (pipx reinstall)") + p.add_argument("--version", action="store_true", help="Affiche la version installée") args = p.parse_args(argv) + if args.version: + return _cmd_version() if args.setup: return _cmd_setup() + if args.update: + return _cmd_update() + if args.install_service: + return _cmd_install_service() + if args.uninstall_service: + return _cmd_uninstall_service() + if args.install_desktop: + return _cmd_install_desktop() + if args.uninstall_desktop: + return _cmd_uninstall_desktop() if args.text: return _cmd_text(args) return _cmd_voice(args) diff --git a/stt/client/stt/config.py b/stt/client/stt/config.py index 2d79f8e..dfde4e5 100644 --- a/stt/client/stt/config.py +++ b/stt/client/stt/config.py @@ -52,6 +52,9 @@ DEFAULT_CONFIG: dict[str, Any] = { "theme": "arc-reactor", "avatar": "default", "kiosk": True, + # window_mode : "app" (fenêtre type Discord) | "kiosk" (plein écran) | "none". + # Si absent, repli sur kiosk ci-dessus (compat). + "window_mode": "kiosk", "ws_host": "127.0.0.1", "ws_port": 9300, "http_port": 9301, diff --git a/stt/client/stt/hud/icon.svg b/stt/client/stt/hud/icon.svg new file mode 100644 index 0000000..cc47662 --- /dev/null +++ b/stt/client/stt/hud/icon.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/stt/client/stt/hud/index.html b/stt/client/stt/hud/index.html index 177da6a..b8f2820 100644 --- a/stt/client/stt/hud/index.html +++ b/stt/client/stt/hud/index.html @@ -4,6 +4,7 @@ STT — Assistant vocal +