From 792cc2aa9a50d132ab89dd6ef280eef009db14eb Mon Sep 17 00:00:00 2001 From: Dom Date: Wed, 18 Mar 2026 19:35:41 +0100 Subject: [PATCH] =?UTF-8?q?docs:=20plan=20de=20migration=20agent=20Windows?= =?UTF-8?q?=20Python=20=E2=86=92=20Rust?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Étude de faisabilité complète : 100% faisable, 0 bloqueur. Crates identifiées pour les 8 fonctionnalités clés. Migration en 5 phases sur 6-10 semaines. Gains : exe unique 10MB, démarrage 200ms, RAM 30MB. Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/PLAN_AGENT_RUST.md | 816 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 816 insertions(+) create mode 100644 docs/PLAN_AGENT_RUST.md diff --git a/docs/PLAN_AGENT_RUST.md b/docs/PLAN_AGENT_RUST.md new file mode 100644 index 000000000..743fe6b6b --- /dev/null +++ b/docs/PLAN_AGENT_RUST.md @@ -0,0 +1,816 @@ +# Plan de réécriture de l'Agent Windows en Rust + +**Date** : 18 mars 2026 +**Statut** : Proposition technique (phase recherche) +**Auteur** : Claude Code + Dom + +--- + +## 1. Contexte et motivation + +### Situation actuelle (Python) + +L'agent Windows (`agent_v0/agent_v1/`) est écrit en Python et nécessite : + +- Python 3.12 installé sur le PC cible +- Un virtualenv avec ~10 dépendances directes (mss, pynput, Pillow, requests, pystray, plyer, pywebview, pywin32, psutil) +- ~30 dépendances transitives +- Un script d'installation (`setup_v1.bat`) fragile +- ~200 Mo d'espace disque (Python + venv + site-packages) +- Temps de démarrage ~3-5 secondes + +### Objectif (Rust) + +Produire un **exécutable unique** (`lea-agent.exe`, ~5-10 Mo) avec : + +- Zéro dépendance externe (pas de runtime, pas de DLL, pas d'installation) +- Démarrage quasi-instantané (<500ms) +- Empreinte mémoire réduite (~15-30 Mo vs ~80-120 Mo en Python) +- Distribution par simple copie du .exe + +### Périmètre fonctionnel à réécrire + +| Module Python | Fichier | LOC | Responsabilité | +|---|---|---|---| +| `main.py` | Orchestrateur | ~390 | Boucles parallèles (heartbeat, replay, watchdog) | +| `config.py` | Configuration | ~55 | Paramètres, chemins, endpoints serveur | +| `core/captor.py` | Capture d'événements | ~330 | Souris, clavier, focus fenêtre, buffer texte | +| `core/executor.py` | Exécuteur d'actions | ~525 | Replay visuel et aveugle, polling serveur | +| `network/streamer.py` | Streaming réseau | ~400 | Queue, retry, health-check, compression JPEG | +| `vision/capturer.py` | Capture d'écran | ~95 | Screenshots, crops, dédup par hash | +| `vision/blur_sensitive.py` | Floutage données | ~205 | Détection champs de saisie, Gaussian blur | +| `ui/smart_tray.py` | Icône systray | ~780 | Menu, workflows, état, hotkey Ctrl+Shift+L | +| `ui/chat_window.py` | Fenêtre de chat | ~1150 | Chat Léa en tkinter, messages, file upload | +| `ui/notifications.py` | Notifications toast | ~220 | Toasts natifs Windows, rate limiting | +| `ui/shared_state.py` | État partagé | ~190 | Thread-safe state, observer pattern | +| `ui/capture_server.py` | Mini serveur HTTP | ~380 | Port 5006, screenshot à la demande, actions fichiers | +| `session/storage.py` | Stockage local | ~75 | Sessions, nettoyage par âge/taille | +| `window_info_crossplatform.py` | Info fenêtre | ~180 | Titre + process de la fenêtre active (Win32) | +| **Total** | | **~4975** | | + +--- + +## 2. Crates Rust pour chaque fonctionnalité + +### 2.1 Capture d'écran (remplace `mss`) + +| Crate | Version | Statut | Support Windows | Notes | +|---|---|---|---|---| +| **`xcap`** | 0.8.x | Actif | Oui (DXGI) | Cross-platform (Linux, macOS, Windows). Successeur de `screenshots`. Video recording WIP. Problèmes de build docs.rs sur dernières versions. | +| **`windows-capture`** | 1.5.x | Actif | Oui (Graphics Capture API) | Le plus performant sur Windows. Utilise l'API Microsoft moderne. Windows 10+ uniquement. | +| `win-screenshot` | 4.x | Mature | Oui | Windows only, simple, stable. | +| `scap` | - | Actif | Oui | Par CapSoftware, haute performance, cross-platform. | + +**Recommandation** : **`xcap`** pour la cross-compatibilité et la simplicité API. Fallback vers `windows-capture` si des performances extrêmes sont nécessaires (capture continue pour le heartbeat). + +**Justification** : Notre agent capture un screenshot toutes les 5 secondes (heartbeat) et à chaque clic. xcap suffit largement. windows-capture serait un over-engineering pour notre cas d'usage. + +### 2.2 Capture et simulation souris/clavier (remplace `pynput`) + +C'est la fonctionnalité la plus critique car elle couvre **deux besoins distincts** : +- **Écoute globale** (capture) : intercepter les clics, frappes et mouvements sans focus +- **Simulation** (replay) : générer des clics, frappes et combos de touches + +| Crate | Version | Rôle | Support Windows | Notes | +|---|---|---|---|---| +| **`rdev`** | 0.5.x | Écoute globale | Oui (Low-Level Hooks) | Listen + grab + simulate. Le plus complet pour notre usage. Cross-platform. | +| **`enigo`** | 0.6.x | Simulation | Oui (SendInput) | Mature, 62K downloads/mois. Mouse + Keyboard simulation. Serde support. | +| `willhook` | 0.4.x | Écoute (Windows) | Windows only | Lecture seule, pas de simulation. Safe hooks. | +| `inputbot` | 0.6.x | Hotkeys + simulation | Oui | Global hotkeys + simulation, mais moins mature. | +| `mki` | - | Listen + simulate | Windows + Linux | Registering global input hooks and simulating. | + +**Recommandation** : **`rdev`** pour la capture (listen) + **`enigo`** pour la simulation (replay). rdev fournit un listener global qui intercepte souris + clavier sans focus fenêtre, exactement comme pynput. enigo fournit une API propre pour simuler les clics et frappes pendant le replay. + +**Risque** : Le buffer de saisie texte (accumulation des frappes avec flush après 500ms d'inactivité) est une logique métier complexe. Elle devra être réimplémentée en Rust avec des timers async — faisable mais demande de l'attention. + +### 2.3 Icône systray (remplace `pystray`) + +| Crate | Version | Support Windows | Notes | +|---|---|---|---| +| **`tray-icon`** | 0.21.x | Oui | Par l'équipe Tauri. Sous-menus, événements, tooltip. Le standard actuel. | +| `tray-item` | 0.10.x | Oui | API minimaliste, cross-platform. | +| `trayicon` | 0.4.x | Oui | Windows + KDE. Menu popup, clic, double-clic. | + +**Recommandation** : **`tray-icon`** (crate Tauri). C'est le standard de l'écosystème Rust pour les icônes systray. Maintenu activement (v0.21 avec Tauri 2.10). Supporte les sous-menus (nécessaire pour « Mes tâches »), les icônes dynamiques (nécessaire pour les états idle/recording/replay), et les événements clic/double-clic. + +### 2.4 Client HTTP (remplace `requests`) + +| Crate | Version | Notes | +|---|---|---| +| **`reqwest`** | 0.12.x | Standard de facto. Async (tokio) ou blocking. Multipart uploads. JSON. TLS natif. | +| `ureq` | 2.x | Synchrone, léger, pas de tokio. Bon pour les cas simples. | + +**Recommandation** : **`reqwest`** avec le feature `blocking` pour les threads de polling, et le feature `multipart` pour l'upload d'images. reqwest est le standard absolu en Rust pour HTTP client. + +**Configuration Cargo.toml** : +```toml +reqwest = { version = "0.12", features = ["json", "multipart", "blocking"] } +``` + +### 2.5 Traitement d'images (remplace `Pillow`) + +| Crate | Version | Notes | +|---|---|---| +| **`image`** | 0.25.x | Standard Rust. Encodage/décodage PNG, JPEG. Resize, crop. Pure Rust. | +| `fast_image_resize` | - | Resize SIMD optimisé. | + +**Recommandation** : **`image`** (crate standard). Couvre tous nos besoins : lecture PNG des captures, conversion en JPEG pour le streaming, resize pour le hash perceptuel, crop pour la capture duale. + +### 2.6 Notifications toast (remplace `plyer`) + +| Crate | Version | Support Windows | Notes | +|---|---|---|---| +| **`winrt-notification`** | 0.5.x | Windows 8.1/10/11 | Wrapper WinRT toast API. Léger, stable. | +| `notify-rust` | 4.x | Cross-platform | Linux/macOS via D-Bus/NSNotification. Windows via winrt-notification. | +| `win-toast-notify` | 0.1.x | Windows 10/11 | Récent, testé Windows 11. | + +**Recommandation** : **`winrt-notification`** directement (Windows only pour l'agent). Si cross-platform souhaité plus tard, `notify-rust` qui utilise winrt-notification en backend. + +### 2.7 Fenêtre de chat (remplace `tkinter`) + +C'est la fonctionnalité la plus complexe à porter. L'agent Python utilise tkinter (stdlib) pour une fenêtre de chat complète avec : +- Messages (bulles Léa/utilisateur), scrollbar, historique +- Barre d'actions rapides (Apprenez-moi, Lancer, Données, Arrêter, Aide) +- Champ de saisie avec placeholder, upload de fichiers +- Indicateur de connexion serveur, drag & resize +- DPI awareness, ancrage bas-droite + +| Crate | Version | Approche | Notes | +|---|---|---|---| +| **`wry`** | 0.54.x | WebView2 (Edge) | Par Tauri. Charge du HTML/CSS/JS dans une fenêtre native. WebView2 est préinstallé sur Windows 10+. | +| `webview2` | - | WebView2 (direct) | Bindings Rust directs pour WebView2. Plus bas niveau que wry. | +| `iced` | 0.13.x | GUI native Rust | Elm-like, cross-platform. Pas de HTML/CSS. Courbe d'apprentissage. | +| `egui` | 0.30.x | GUI immediate mode | Très léger, rendu custom. Pas adapté pour un chat riche. | + +**Recommandation** : **`wry`** (WebView2). C'est l'approche optimale pour notre cas : + +1. **Le chat est essentiellement une interface web** — HTML/CSS est le meilleur outil pour rendre des bulles de chat, du scroll, des boutons d'actions rapides +2. **WebView2 est préinstallé** sur Windows 10 21H2+ et Windows 11 — pas de dépendance supplémentaire +3. **L'interface chat du VWB existe déjà** côté serveur (port 5004) — on peut charger l'URL directement dans le WebView +4. **Pas besoin de réécrire le frontend** — la fenêtre wry charge simplement `http://{server}:5004/chat` avec injection de l'identité machine + +**Taille supplémentaire** : ~0 octets (WebView2 est fourni par Windows, wry ne fait que l'instancier) + +**Fallback** : Si WebView2 n'est pas disponible (Windows 7/8), l'agent fonctionne sans la fenêtre de chat (systray + notifications seulement). + +### 2.8 Floutage données sensibles (remplace `OpenCV`) + +| Crate | Version | Notes | +|---|---|---| +| **`libblur`** | 0.19.x | Pure Rust, SIMD (SSE/AVX/NEON). Gaussian blur 100 FPS sur 4K. Pas de dépendance OpenCV. | +| `imageproc` | 0.25.x | Traitement d'images basé sur le crate `image`. Contours, seuillage, morphologie. | +| `opencv` (Rust) | 0.93.x | Bindings OpenCV. Lourds (~200 Mo de DLL), difficile à cross-compiler. | + +**Recommandation** : **`libblur`** pour le flou gaussien + **`imageproc`** pour la détection de contours et le seuillage. Cela remplace OpenCV sans aucune dépendance native. + +**Détail de l'implémentation** : Le module `blur_sensitive.py` actuel utilise OpenCV pour : +1. Conversion en niveaux de gris → `image` crate +2. Seuillage binaire → `imageproc` (threshold) +3. Morphologie (close) → `imageproc` (dilate/erode) +4. Détection de contours → `imageproc` (find_contours) +5. Gaussian blur sur les zones détectées → `libblur` + +Toutes ces opérations ont des équivalents purs Rust. Pas besoin d'OpenCV. + +### 2.9 Informations fenêtre active (remplace `pywin32`) + +| Crate | Version | Notes | +|---|---|---| +| **`windows`** | 0.61.x | Crate officiel Microsoft. Accès complet à Win32 API. | + +**Recommandation** : **`windows`** (crate officiel Microsoft). On utilisera : +- `GetForegroundWindow()` pour obtenir le handle de la fenêtre active +- `GetWindowTextW()` pour le titre +- `GetWindowThreadProcessId()` pour le PID +- `OpenProcess()` + `QueryFullProcessImageNameW()` pour le nom du processus + +Cela remplace pywin32 + psutil en une seule dépendance, sans DLL externe. + +### 2.10 Mini-serveur HTTP local (remplace `http.server`) + +| Crate | Version | Notes | +|---|---|---| +| **`axum`** | 0.8.x | Framework web async (tokio). Léger, performant. | +| `warp` | 0.3.x | Alternative plus légère. | +| `tiny_http` | 0.12.x | Synchrone, minimaliste. Parfait pour un mini-serveur. | + +**Recommandation** : **`tiny_http`** pour le serveur de capture (port 5006). C'est un serveur HTTP synchrone minimaliste qui suffit pour 2 endpoints (GET /capture, POST /file-action). Pas besoin d'un framework async pour ça. + +### 2.11 Runtime asynchrone + +| Crate | Version | Notes | +|---|---|---| +| **`tokio`** | 1.x | Runtime async standard. Nécessaire pour reqwest async. | + +**Note** : On peut éviter tokio en utilisant `reqwest` en mode `blocking` et des threads standards. Cela simplifie l'architecture et réduit la taille du binaire. L'agent actuel en Python utilise des threads daemon, pas de l'async — le portage direct en threads Rust est plus fidèle. + +**Recommandation** : **Pas de tokio**. Utiliser des threads standards (`std::thread`) + `reqwest` blocking. L'agent n'a pas besoin de milliers de connexions concurrentes — il a 4-5 boucles parallèles avec des `sleep` de 1-5 secondes. + +--- + +## 3. Architecture proposée + +``` +rpa_agent_rust/ +├── Cargo.toml — Dépendances et configuration build +├── build.rs — Embedding icône + manifeste Windows +├── assets/ +│ ├── lea-icon.ico — Icône de l'application +│ ├── manifest.xml — Manifeste Windows (DPI awareness, UAC) +│ └── chat/ +│ └── index.html — Fallback HTML si serveur chat indisponible +├── src/ +│ ├── main.rs — Point d'entrée, orchestrateur (équivalent main.py) +│ ├── config.rs — Configuration (SERVER_URL, MACHINE_ID, ports, chemins) +│ ├── state.rs — État partagé thread-safe (équivalent shared_state.py) +│ │ +│ ├── capture/ +│ │ ├── mod.rs +│ │ ├── screen.rs — Capture d'écran (xcap) — équivalent capturer.py +│ │ ├── events.rs — Capture souris/clavier (rdev) — équivalent captor.py +│ │ └── window_info.rs — Info fenêtre active (windows crate) — équivalent window_info.py +│ │ +│ ├── replay/ +│ │ ├── mod.rs +│ │ ├── executor.rs — Exécution d'actions (enigo) — équivalent executor.py +│ │ └── poller.rs — Polling serveur replay — partie de executor.py +│ │ +│ ├── network/ +│ │ ├── mod.rs +│ │ ├── streamer.rs — Streaming événements/images (reqwest) — équivalent streamer.py +│ │ └── http_server.rs — Mini-serveur capture (tiny_http) — équivalent capture_server.py +│ │ +│ ├── vision/ +│ │ ├── mod.rs +│ │ └── blur.rs — Floutage zones sensibles (libblur + imageproc) — équivalent blur_sensitive.py +│ │ +│ ├── ui/ +│ │ ├── mod.rs +│ │ ├── tray.rs — Icône systray (tray-icon) — équivalent smart_tray.py +│ │ ├── chat.rs — Fenêtre chat (wry/WebView2) — équivalent chat_window.py +│ │ └── notifications.rs — Toasts Windows (winrt-notification) — équivalent notifications.py +│ │ +│ ├── storage/ +│ │ ├── mod.rs +│ │ └── session.rs — Stockage local + nettoyage — équivalent storage.py +│ │ +│ └── file_actions/ +│ ├── mod.rs +│ └── handler.rs — Opérations fichiers (list, move, sort) — partie de capture_server.py +│ +└── tests/ + ├── integration/ + │ ├── test_streamer.rs + │ └── test_replay.rs + └── unit/ + ├── test_blur.rs + ├── test_events.rs + └── test_state.rs +``` + +### 3.1 Flux de données principal + +``` +┌──────────────┐ ┌──────────────┐ ┌──────────────┐ +│ EventCaptor │────→│ Orchestrator │────→│ Streamer │────→ Serveur :5005 +│ (rdev) │ │ (main.rs) │ │ (reqwest) │ +└──────────────┘ └──────────────┘ └──────────────┘ + │ + ┌──────┴──────┐ + │ScreenCapture│ + │ (xcap) │ + └─────────────┘ + +┌──────────────┐ ┌──────────────┐ +│ ReplayPoller │────→│ Executor │────→ Souris/Clavier (enigo) +│ (reqwest) │ │ (enigo) │ +└──────────────┘ └──────────────┘ + +┌──────────────┐ ┌──────────────┐ ┌──────────────┐ +│ TrayIcon │←───→│ State │←───→│ ChatWindow │ +│ (tray-icon) │ │ (Arc)│ │ (wry) │ +└──────────────┘ └──────────────┘ └──────────────┘ +``` + +### 3.2 Gestion des threads + +L'agent actuel utilise 8-10 threads daemon. Le portage Rust conserve cette architecture : + +| Thread | Rôle | Crate principal | +|---|---|---| +| **Main** | Boucle événements systray (tray-icon) | `tray-icon` | +| **Event Listener** | Capture souris/clavier | `rdev` | +| **Window Focus** | Surveillance focus fenêtre (500ms) | `windows` | +| **Heartbeat** | Screenshot + envoi toutes les 5s | `xcap` + `reqwest` | +| **Background HB** | Heartbeat sans session active | `xcap` + `reqwest` | +| **Replay Poller** | GET /replay/next (1-30s backoff) | `reqwest` | +| **Stream Worker** | Queue d'envoi événements/images | `reqwest` | +| **Health Check** | Vérification serveur (30s) | `reqwest` | +| **HTTP Server** | Serveur capture port 5006 | `tiny_http` | +| **Chat Window** | Fenêtre WebView2 (si visible) | `wry` | + +### 3.3 Communication inter-threads + +```rust +// État partagé — équivalent de shared_state.py +pub struct AgentState { + inner: Arc>, + // Channel pour notifier les listeners + notify: broadcast::Sender, +} + +struct AgentStateInner { + recording: bool, + recording_name: String, + actions_count: u32, + replay_active: bool, + server_connected: bool, +} + +// Queue de streaming — équivalent de la queue bornée Python +// crossbeam-channel est plus performant que std::sync::mpsc +type StreamQueue = crossbeam_channel::bounded(100); +``` + +--- + +## 4. Évaluation de faisabilité par fonctionnalité + +| Fonctionnalité | Difficulté | Crates | Commentaire | +|---|---|---|---| +| Capture d'écran (full + crop) | **Facile** | xcap, image | API directe, bien documentée | +| Encodage JPEG + resize | **Facile** | image | Standard Rust | +| Hash perceptuel (16x16 → MD5) | **Facile** | image, md5 | Trivial | +| Capture événements souris | **Facile** | rdev | Global listener, events bien typés | +| Capture événements clavier | **Facile** | rdev | Idem | +| Buffer de saisie texte (flush 500ms) | **Moyen** | rdev + timers | Logique métier spécifique, timers thread-safe | +| Détection double-clic | **Facile** | rdev | Logique pure, pas de dépendance | +| Simulation clic souris | **Facile** | enigo | API directe | +| Simulation frappe clavier | **Facile** | enigo | keyboard.type() + key combos | +| Simulation combo clavier | **Facile** | enigo | press/release des modifiers | +| Info fenêtre active (Win32) | **Facile** | windows | GetForegroundWindow, GetWindowTextW | +| Streaming HTTP événements | **Facile** | reqwest (blocking) | POST JSON | +| Upload image multipart | **Facile** | reqwest (multipart) | POST multipart/form-data | +| Retry + backoff exponentiel | **Facile** | Code pur | Boucle simple | +| Compression JPEG avant envoi | **Facile** | image | encode_to_jpeg | +| Queue bornée + backpressure | **Facile** | crossbeam-channel | bounded(100) + try_send | +| Health-check serveur | **Facile** | reqwest (blocking) | GET /stats | +| Polling replay | **Facile** | reqwest (blocking) | GET + POST JSON | +| Résolution visuelle (POST screenshot) | **Facile** | reqwest + base64 | POST JSON avec image b64 | +| Icône systray dynamique | **Facile** | tray-icon | Génération d'icônes cercle coloré | +| Menu systray dynamique | **Moyen** | tray-icon | Reconstruction du menu à chaque changement d'état | +| Sous-menu workflows | **Moyen** | tray-icon | Nécessite des sous-menus dynamiques (liste du serveur) | +| Notifications toast | **Facile** | winrt-notification | API simple, rate limiting en code pur | +| Hotkey global Ctrl+Shift+L | **Moyen** | rdev (grab) ou windows | Nécessite un hook clavier global | +| Fenêtre de chat (WebView2) | **Moyen** | wry | Création fenêtre + chargement URL serveur | +| Chat fallback (HTML local) | **Moyen** | wry | Embedding HTML/CSS/JS dans le binaire | +| Floutage champs de saisie | **Moyen** | imageproc + libblur | Réécriture de la détection de contours + seuillage | +| Dialogues tkinter (consentement, nom) | **Moyen** | windows (MessageBoxW) | Dialogues Win32 natifs au lieu de tkinter | +| Mini-serveur HTTP (port 5006) | **Facile** | tiny_http | 2-3 endpoints, synchrone | +| Actions fichiers (list, sort, move) | **Facile** | std::fs | API standard Rust | +| Stockage sessions + nettoyage | **Facile** | std::fs | Nettoyage par âge et taille, trivial | +| État partagé thread-safe | **Facile** | Arc> | Pattern standard Rust | + +### Résumé de faisabilité + +- **Facile** : 24 fonctionnalités (75%) +- **Moyen** : 8 fonctionnalités (25%) +- **Difficile** : 0 +- **Bloquant** : 0 + +**Verdict : 100% faisable. Aucun bloqueur identifié.** + +--- + +## 5. Stratégie de migration par phases + +### Phase 1 — Agent minimal fonctionnel (2-3 semaines) + +**Objectif** : Un .exe qui capture et envoie au serveur, et exécute les replays. + +**Fonctionnalités** : +- [x] Configuration (SERVER_URL, MACHINE_ID, ports) +- [x] Capture d'écran (xcap) — full + crop +- [x] Capture événements souris/clavier (rdev) — clics, combos, texte +- [x] Streaming réseau (reqwest) — événements + images + register/finalize +- [x] Polling replay (reqwest) — GET /replay/next + POST /result +- [x] Exécution d'actions (enigo) — clic, type, key_combo, scroll, wait +- [x] Heartbeat toutes les 5s avec dédup +- [x] État partagé (Arc) +- [x] Stockage local sessions + +**Pas encore** : UI, notifications, floutage, serveur capture, chat + +**Livrable** : `lea-agent.exe` (~3 Mo) qui s'exécute en mode headless (pas d'icône, logs en console). Test d'intégration avec le serveur streaming existant. + +**Critère de validation** : Enregistrer une session depuis l'agent Rust, vérifier qu'elle apparaît dans le VWB, lancer un replay. + +### Phase 2 — Systray + notifications (1-2 semaines) + +**Objectif** : L'agent a une présence visuelle dans la barre des tâches. + +**Fonctionnalités** : +- [x] Icône systray avec états (idle/recording/connected/replay) +- [x] Menu contextuel (Apprenez-moi, C'est terminé, Mes tâches, Quitter) +- [x] Sous-menu dynamique des workflows (fetch depuis serveur) +- [x] Notifications toast (winrt-notification) +- [x] Dialogue de consentement (MessageBoxW natif) +- [x] Dialogue de saisie du nom de tâche (InputBox Win32) +- [x] Arrêt d'urgence (Article 14, Règlement IA) +- [x] Hotkey global Ctrl+Shift+L + +**Livrable** : `lea-agent.exe` (~4 Mo) avec icône systray fonctionnelle. Même expérience utilisateur que l'agent Python (sans le chat). + +### Phase 3 — Fenêtre de chat (1-2 semaines) + +**Objectif** : Le chat Léa intégré dans l'agent. + +**Fonctionnalités** : +- [x] Fenêtre WebView2 (wry) chargeant `http://{server}:5004/chat` +- [x] Toggle via Ctrl+Shift+L ou menu systray +- [x] Ancrage bas-droite de l'écran +- [x] Fallback HTML local si serveur indisponible +- [x] Communication bidirectionnelle via JavaScript bridge (pour l'état agent) + +**Remarque** : Le chat actuel (tkinter) est une réécriture complète du frontend. Avec wry, on peut simplement charger l'interface chat existante du serveur, ce qui est **plus simple** et **plus maintenable** que l'approche Python. + +**Livrable** : `lea-agent.exe` (~5 Mo) avec chat intégré. + +### Phase 4 — Parité complète (1-2 semaines) + +**Objectif** : 100% des fonctionnalités de l'agent Python. + +**Fonctionnalités** : +- [x] Floutage des données sensibles (libblur + imageproc) +- [x] Mini-serveur HTTP port 5006 (capture à la demande, CORS) +- [x] Actions fichiers (list_dir, create_dir, move, copy, sort_by_ext) +- [x] Sécurité chemins (zones autorisées C:\Users, D:\, E:\) +- [x] Nettoyage automatique des sessions (par âge et taille) +- [x] Info fenêtre active (surveillance focus, Win32 API) +- [x] Divulgation IA (Article 50, Règlement IA) +- [x] Log rotation et rétention 180 jours (Article 12, Règlement IA) + +**Livrable** : `lea-agent.exe` (~6-8 Mo), parité fonctionnelle complète. Prêt pour déploiement en production. + +### Phase 5 — Optimisations et polish (optionnel) + +- Auto-update (téléchargement de la nouvelle version depuis le serveur) +- Installeur MSI/NSIS optionnel (pour l'inscription au démarrage Windows) +- Compression UPX du binaire (~3-4 Mo) +- Signature du code (certificat Authenticode) +- Télémétrie d'erreurs (panic handler → envoi au serveur) + +--- + +## 6. Build et distribution + +### 6.1 Cross-compilation Linux → Windows + +Deux approches possibles : + +**Option A : `cargo-xwin` (recommandée)** +```bash +# Installation une fois +cargo install cargo-xwin +rustup target add x86_64-pc-windows-msvc + +# Build +cargo xwin build --target x86_64-pc-windows-msvc --release +``` +Avantage : Produit un .exe compatible MSVC (le standard Windows). +Inconvénient : Télécharge le SDK Windows (~1 Go la première fois). + +**Option B : `cross` (plus simple, GNU)** +```bash +cargo install cross +cross build --target x86_64-pc-windows-gnu --release +``` +Avantage : Utilise un container Docker, rien à installer. +Inconvénient : Produit un .exe GNU (peut nécessiter mingw runtime). + +**Recommandation** : `cargo-xwin` pour la production (MSVC target), `cross` pour les tests rapides. + +### 6.2 Icône dans le .exe + +```rust +// build.rs +fn main() { + if std::env::var("CARGO_CFG_TARGET_OS").unwrap() == "windows" { + let mut res = tauri_winres::WindowsResource::new(); + res.set_icon("assets/lea-icon.ico"); + res.set("ProductName", "Léa Agent"); + res.set("FileDescription", "Agent RPA Vision - Léa"); + res.set("LegalCopyright", "Copyright 2026"); + res.compile().unwrap(); + } +} +``` + +Dépendance build : `tauri-winres` (fork maintenu de `winres`). + +### 6.3 Manifeste Windows (DPI + UAC) + +Le manifeste `assets/manifest.xml` déclare : +- DPI awareness (per-monitor V2) — nécessaire pour les captures d'écran correctes +- requestedExecutionLevel = asInvoker (pas besoin d'admin) +- Windows 10+ compatibility + +### 6.4 Signature du code + +Pour éviter les avertissements SmartScreen à l'exécution : + +```bash +# Avec signtool (Windows SDK) +signtool sign /f certificate.pfx /p password /tr http://timestamp.digicert.com /td sha256 /fd sha256 lea-agent.exe + +# Ou avec le crate rust-codesign depuis Linux +cargo install codesign +codesign --cert certificate.pfx --password password lea-agent.exe +``` + +**Coût** : Un certificat EV Code Signing coûte ~300-500 EUR/an. Un certificat OV standard ~100-200 EUR/an. Pour un usage interne (déploiement sur des machines contrôlées), la signature n'est pas obligatoire — on peut ajouter une exception Windows Defender. + +### 6.5 Estimation de taille du .exe + +| Configuration | Taille estimée | +|---|---| +| Debug build | ~30-50 Mo | +| Release build (défaut) | ~15-20 Mo | +| Release + LTO + strip | ~6-10 Mo | +| Release + LTO + strip + opt-level z | ~5-8 Mo | +| Release + LTO + strip + UPX | ~3-5 Mo | + +**Profil release optimisé** (Cargo.toml) : +```toml +[profile.release] +opt-level = "s" # Optimisation taille (z = encore plus petit mais plus lent) +lto = true # Link-Time Optimization +codegen-units = 1 # Optimisation max +panic = "abort" # Pas de stack unwinding +strip = true # Suppression des symboles +``` + +--- + +## 7. Comparaison de performances estimée + +| Métrique | Python (actuel) | Rust (estimé) | Amélioration | +|---|---|---|---| +| **Taille installation** | ~200 Mo (Python + venv) | ~6-10 Mo (.exe seul) | **20-30x plus petit** | +| **Temps de démarrage** | 3-5 secondes | 100-300 ms | **10-15x plus rapide** | +| **Mémoire au repos** | 80-120 Mo | 15-30 Mo | **4-5x moins** | +| **Mémoire en session** | 120-180 Mo | 25-50 Mo | **3-4x moins** | +| **Capture screenshot** | 30-80 ms (mss) | 5-15 ms (xcap/DXGI) | **3-5x plus rapide** | +| **Compression JPEG** | 15-30 ms (Pillow) | 5-10 ms (image crate) | **2-3x plus rapide** | +| **Envoi HTTP** | ~identique (TCP) | ~identique (TCP) | Négligeable | +| **Floutage** | 50-150 ms (OpenCV) | 10-30 ms (libblur SIMD) | **3-5x plus rapide** | +| **Utilisation CPU idle** | 1-3% | <0.5% | **3-5x moins** | +| **Complexité installation** | Script .bat + Python + pip | Copier 1 fichier .exe | **Incomparable** | + +**Notes** : +- L'envoi HTTP est dominé par la latence réseau, pas par le langage +- Les captures screenshot sont déjà rapides en Python (mss utilise DXGI) — le gain Rust est surtout sur le traitement post-capture +- Le gain le plus significatif est sur la **distribution** (1 fichier vs installation complexe) + +--- + +## 8. Risques et défis + +### 8.1 Risques techniques + +| Risque | Impact | Probabilité | Mitigation | +|---|---|---|---| +| `rdev` buggy sur certaines versions de Windows | Élevé | Faible | Tests sur Windows 10 LTSC, 11 23H2. Fallback vers `willhook` + `windows` crate direct. | +| WebView2 absent (Windows 7/8) | Faible | Très faible | PC cible = Windows 10+. Le chat est optionnel, le systray + notifications suffisent. | +| `tray-icon` ne supporte pas les sous-menus dynamiques | Moyen | Faible | Tester en amont. Fallback : menu plat avec numérotation. | +| DPI scaling incorrect pour les captures | Moyen | Moyen | Manifeste DPI-aware + tests multi-écran. Le problème existe déjà en Python. | +| Cross-compilation échoue avec certains crates (liens natifs) | Moyen | Moyen | Build natif sur une VM Windows si nécessaire. CI GitHub Actions avec Windows runner. | +| Bugs de threading (deadlocks, races) | Élevé | Faible | Le borrow checker Rust élimine les data races. Les deadlocks restent possibles — revue attentive de l'ordre de verrouillage. | + +### 8.2 Ce qui reste côté serveur + +Les fonctionnalités suivantes ne sont PAS portées en Rust car elles restent côté serveur (Python) : + +- **Analyse visuelle (CLIP, FAISS, ScreenAnalyzer)** — GPU server +- **VLM (Ollama, template matching)** — GPU server +- **Construction du workflow (GraphBuilder)** — serveur streaming +- **Chat LLM (Copilot, GestureCatalog)** — serveur chat port 5004 +- **Dashboard, VWB** — serveur web + +L'agent Rust est **strictement un client** : il capture, envoie, et exécute. Toute l'intelligence reste sur le serveur. + +### 8.3 Chat : stratégie WebView vs natif + +Le chat tkinter actuel (chat_window.py, 1150 LOC) est le module le plus gros de l'agent. Il implémente un frontend complet en tkinter : bulles de messages, scrollbar, boutons d'actions, upload fichier, indicateur de connexion. + +**L'approche WebView (wry) est supérieure** pour plusieurs raisons : + +1. **Pas de duplication** : L'interface chat existe déjà sur le serveur (port 5004). En la chargeant dans un WebView, on bénéficie de toutes les améliorations côté serveur sans rebuilder l'agent. + +2. **Rendu supérieur** : HTML/CSS produit une interface beaucoup plus riche que tkinter (animations, emojis, markdown, images inline). + +3. **Maintenance réduite** : Une seule codebase pour le chat (côté serveur), pas de duplication Python/Rust. + +4. **Taille binaire** : wry ajoute ~200 Ko au binaire (le rendu est fait par WebView2 déjà installé sur Windows). + +**Inconvénient** : L'agent nécessite une connexion au serveur pour afficher le chat. En mode offline, il faudrait un HTML de fallback embarqué dans le binaire (message « Connexion au serveur requise »). + +### 8.4 AZERTY et internationalisation + +Le clavier du PC cible est AZERTY. Points d'attention : + +- **rdev** renvoie les scancodes bruts — la conversion en caractères dépend du layout clavier Windows. Vérifier que rdev gère correctement AZERTY (les accents, les chiffres en Shift, etc.). +- **enigo** pour la simulation : `keyboard.type()` utilise SendInput avec les caractères Unicode, pas les scancodes — devrait fonctionner correctement en AZERTY. +- À tester en priorité lors de la Phase 1. + +--- + +## 9. Dépendances Cargo.toml prévisionnelles + +```toml +[package] +name = "lea-agent" +version = "1.0.0" +edition = "2021" +description = "Agent RPA Vision - Léa" +build = "build.rs" + +[dependencies] +# Capture d'écran +xcap = "0.8" + +# Capture événements souris/clavier +rdev = "0.5" + +# Simulation souris/clavier (replay) +enigo = { version = "0.6", features = ["serde"] } + +# Systray +tray-icon = "0.21" + +# HTTP client +reqwest = { version = "0.12", default-features = false, features = [ + "json", "multipart", "blocking", "rustls-tls" +] } + +# Image processing +image = "0.25" + +# Floutage +libblur = "0.19" +imageproc = "0.25" + +# Notifications Windows +winrt-notification = "0.5" + +# Chat window (WebView2) +wry = "0.54" + +# Mini-serveur HTTP +tiny_http = "0.12" + +# Sérialisation +serde = { version = "1", features = ["derive"] } +serde_json = "1" + +# Logging +log = "0.4" +env_logger = "0.11" + +# Hash (perceptuel screenshot) +md5 = "0.7" + +# Base64 (screenshots post-action) +base64 = "0.22" + +# UUID (session IDs) +uuid = { version = "1", features = ["v4"] } + +# Date/heure +chrono = "0.4" + +# Canaux inter-threads performants +crossbeam-channel = "0.5" + +# Win32 API (info fenêtre, dialogues) +[target.'cfg(windows)'.dependencies] +windows = { version = "0.61", features = [ + "Win32_UI_WindowsAndMessaging", + "Win32_System_Threading", + "Win32_Foundation", + "Win32_Security", +] } + +[build-dependencies] +tauri-winres = "0.2" + +[profile.release] +opt-level = "s" +lto = true +codegen-units = 1 +panic = "abort" +strip = true +``` + +**Nombre de dépendances directes** : 18 (vs ~10 en Python, mais toutes compilées statiquement dans le binaire final — pas de DLL, pas de pip, pas de virtualenv). + +--- + +## 10. Plan de tests + +### 10.1 Tests unitaires (Phase 1) + +| Module | Tests | +|---|---| +| `config.rs` | Lecture variables d'environnement, valeurs par défaut | +| `state.rs` | Transitions d'état, notifications listeners, thread-safety | +| `vision/blur.rs` | Détection champs de saisie, application du flou, image inchangée si pas de champ | +| `capture/events.rs` | Buffer texte : accumulation, flush après timeout, flush sur clic | +| `capture/events.rs` | Double-clic : détection, tolérance position, timeout | +| `replay/executor.rs` | Conversion coordonnées normalisées → pixels | +| `storage/session.rs` | Création dossier, nettoyage par âge, nettoyage par taille | +| `file_actions/handler.rs` | Validation chemins (sécurité anti-traversal) | + +### 10.2 Tests d'intégration (Phase 2-3) + +| Scénario | Description | +|---|---| +| Streaming | Envoyer événement + image au serveur :5005, vérifier réception | +| Replay | Simuler une action replay depuis le serveur, vérifier exécution | +| Heartbeat | Vérifier envoi périodique + dédup (même écran = pas d'envoi) | +| Tray menu | Vérifier affichage menu, changement d'icône sur enregistrement | +| Chat WebView | Vérifier ouverture fenêtre, chargement URL serveur | + +### 10.3 Tests manuels sur PC Windows cible + +| Test | Vérification | +|---|---| +| AZERTY | Taper « été » → le texte capturé est bien « été » | +| Multi-écran | Capture correcte sur l'écran principal | +| DPI 125%/150% | Coordonnées de clic correctes après scaling | +| Sleep/Reprise | L'agent se reconnecte après mise en veille | +| Replay complet | Workflow calculatrice : 3+5=8 | +| Arrêt d'urgence | Le systray stoppe immédiatement toute activité | + +--- + +## 11. Calendrier prévisionnel + +| Phase | Durée | Livrable | +|---|---|---| +| Phase 1 — Agent minimal | 2-3 semaines | lea-agent.exe headless, capture + replay | +| Phase 2 — Systray + notifications | 1-2 semaines | Icône, menu, toasts | +| Phase 3 — Chat WebView | 1-2 semaines | Fenêtre de chat intégrée | +| Phase 4 — Parité complète | 1-2 semaines | Floutage, serveur capture, actions fichiers | +| Phase 5 — Polish (optionnel) | 1 semaine | Auto-update, signature, installeur | +| **Total** | **6-10 semaines** | **Parité complète** | + +### Prérequis avant de commencer + +1. **Installer la toolchain Rust** sur la machine de dev : + ```bash + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + rustup target add x86_64-pc-windows-msvc + cargo install cargo-xwin + ``` + +2. **Préparer l'environnement de test** : + - VM Windows 10/11 ou accès au PC cible (192.168.1.11) + - Serveur streaming (:5005) accessible depuis le PC cible + +3. **Créer le repo** : + ```bash + cd ~/ai/rpa_vision_v3 + mkdir -p rpa_agent_rust/src rpa_agent_rust/assets + cd rpa_agent_rust + cargo init + ``` + +--- + +## 12. Conclusion + +La réécriture de l'agent Windows en Rust est **techniquement faisable à 100%** et apporterait des bénéfices majeurs : + +**Gains quantifiables** : +- Taille : 200 Mo → 6-10 Mo (**20x plus petit**) +- Démarrage : 5s → 300ms (**15x plus rapide**) +- Mémoire : 120 Mo → 30 Mo (**4x moins**) +- Installation : Script .bat + Python → Copier 1 fichier (**incomparable**) + +**Gains qualitatifs** : +- Plus de problèmes de version Python sur les PC cibles +- Plus de virtualenv cassé, plus de `pip install` qui échoue +- Distribution par email, clé USB, ou téléchargement direct +- Fiabilité accrue (borrow checker, pas de GIL, pas de GC) + +**Risques principaux** : +- Courbe d'apprentissage Rust (si pas de développeur Rust dans l'équipe) +- Temps de développement initial plus long qu'en Python (~6-10 semaines vs ~4 semaines) +- Maintenance de deux codebases pendant la transition + +**Recommandation** : Procéder par phases. La Phase 1 (agent minimal) permet de valider la faisabilité en conditions réelles avec un investissement limité. Si elle réussit, les phases suivantes sont incrémentales et relativement simples. + +--- + +*Document généré le 18 mars 2026.*