Pipeline E2E complet validé : Capture VM → streaming → serveur → cleaner → replay → audit trail Mode apprentissage supervisé fonctionne (Léa échoue → humain → reprise) Dashboard : - Cleanup 14→10 onglets (RCE supprimée) - Fleet : enregistrer/révoquer agents, tokens, ZIP pré-configuré téléchargeable - Audit trail MVP (/audit) : filtres, tableau, export CSV, conformité AI Act/RGPD - Formulaire Fleet simplifié (nom + email, machine_id auto) VWB bridge Léa→VWB : - Compound décomposés en N steps (saisie + raccourci visibles) - Layout serpentin 3 colonnes (plus colonne verticale) - Badge OS 🪟/🐧, filtre OS retiré (admin Linux voit Windows) - Fix import SQLite readonly Cleaner intelligent : - Descriptions lisibles (UIA/C2) + détection doublons - Logique C2 : UIElement identifié = jamais parasite - Patterns parasites resserrés - Message Léa : "Je n'y arrive pas, montrez-moi comment faire" Config agent (INC-1 à INC-7) : - SERVER_URL + SERVER_BASE unifiés - RPA_OLLAMA_HOST séparé - allow_redirects=False sur POST - Middleware réécriture URL serveur CI Gitea : fix token + Flask-SocketIO + ruff propre Fleet endpoints : /agents/enroll|uninstall|fleet + agent_registry SQLite Backup : script quotidien workflows.db + audit Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
14 KiB
Architecture de Configuration Agent -- Serveur
Date : 2026-04-13 Auteur : Analyse automatique (Claude) Statut : Diagnostic + recommandation -- PAS de code modifie
1. Schema de la chaine de configuration
config.txt (fichier plat sur le poste Windows)
|
| Lea.bat (for /f "eol=# tokens=1,* delims==" => set)
v
Variables d'environnement du process Python
|
+---> agent_v1/config.py
| SERVER_URL = os.getenv("RPA_SERVER_URL", "http://localhost:5005/api/v1")
| STREAMING_ENDPOINT = f"{SERVER_URL}/traces/stream"
| API_TOKEN = os.environ.get("RPA_API_TOKEN", "")
|
+---> lea_ui/server_client.py
| _stream_base = RPA_SERVER_URL (si defini) OU http://{RPA_SERVER_HOST}:{5005}
| Utilise _stream_base + "/api/v1/traces/stream/..." (chemin COMPLET en dur)
|
+---> agent_v1/main.py
| _background_heartbeat_loop : utilise SERVER_URL DIRECTEMENT (pas STREAMING_ENDPOINT)
| _replay_poll_loop : passe SERVER_URL a executor.poll_and_execute()
|
+---> agent_v1/core/executor.py
poll_and_execute : construit f"{server_url}/traces/stream/replay/next"
_server_resolve_target : construit f"{server_url}/traces/stream/replay/resolve_target"
_observe_screen : construit f"{server_url}/traces/stream/replay/pre_analyze"
Cote serveur (generation du config.txt)
web_dashboard/app.py
|
+---> _RPA_PUBLIC_URL = RPA_PUBLIC_URL ou RPA_SERVER_URL ou "https://lea.labs.laurinebazin.design"
+---> _build_custom_config(machine_id, user_name, token)
| server_url = _RPA_PUBLIC_URL.rstrip("/")
| if not server_url.endswith("/api/v1"):
| server_url += "/api/v1" <=== CORRECTIF RECENT (fonctionne)
| -> RPA_SERVER_URL={server_url}
| -> RPA_SERVER_HOST={host sans schema ni path}
|
+---> deploy/lea_package/config.txt (template statique dans le repo)
+---> deploy/installer/config_template.txt (template pour installeur Inno Setup)
Cote serveur (routes FastAPI, port 5005)
agent_v0/server_v1/api_stream.py
|
+---> TOUTES les routes sous /api/v1/traces/stream/...
| /register, /event, /image, /finalize, /replay/next, etc.
|
+---> /health (public, a la racine)
|
+---> Middleware url_compat_rewrite :
/traces/stream/... => /api/v1/traces/stream/...
2. Inventaire des incoherences
INC-1 : Deux systemes paralleles de resolution d'URL (CRITIQUE)
Fichiers concernes :
agent_v0/agent_v1/config.py(ligne 43-45) :SERVER_URL= URL complete avec/api/v1;STREAMING_ENDPOINT = SERVER_URL + "/traces/stream"agent_v0/lea_ui/server_client.py(ligne 77-81) :_stream_base=RPA_SERVER_URLBRUTE (sans garantie de/api/v1) ; utilise ensuite_stream_base + "/api/v1/traces/stream/..."(chemin complet en dur)
Probleme : Si RPA_SERVER_URL=https://lea.labs.laurinebazin.design/api/v1 :
config.pyproduitSTREAMING_ENDPOINT = .../api/v1/traces/stream(CORRECT)server_client.pyproduit_stream_base/api/v1/traces/stream/...=.../api/v1/api/v1/traces/stream/...(DOUBLE/api/v1!)
Si RPA_SERVER_URL=https://lea.labs.laurinebazin.design :
config.pyproduitSTREAMING_ENDPOINT = .../traces/stream(MANQUE/api/v1)server_client.pyproduit_stream_base/api/v1/traces/stream/...(CORRECT)
Il n'existe aucune valeur de RPA_SERVER_URL qui fasse fonctionner les deux modules simultanement.
INC-2 : _background_heartbeat_loop utilise SERVER_URL au lieu de STREAMING_ENDPOINT
Fichier : agent_v0/agent_v1/main.py (ligne 370)
req.post(f"{SERVER_URL}/traces/stream/image", ...)
SERVER_URL = http://localhost:5005/api/v1 => URL finale = /api/v1/traces/stream/image (CORRECT).
Mais c'est un accident : le heartbeat bypasse STREAMING_ENDPOINT (SERVER_URL + "/traces/stream") et reconstruit son propre chemin. Le meme pattern se retrouve dans executor.py qui recoit server_url (= SERVER_URL) et construit f"{server_url}/traces/stream/replay/next".
Consequence : Deux conventions coexistent :
STREAMING_ENDPOINT + "/register"(streamer.py) -- attend queSERVER_URLcontienne/api/v1SERVER_URL + "/traces/stream/image"(main.py, executor.py) -- attend aussi queSERVER_URLcontienne/api/v1
Aujourd'hui les deux marchent parce que SERVER_URL inclut /api/v1. Mais server_client.py utilise une troisieme convention (chemin complet en dur), d'ou l'INC-1.
INC-3 : LeaServerClient.check_connection() appelle /health sur _stream_base
Fichier : agent_v0/lea_ui/server_client.py (ligne 161)
resp = requests.get(f"{self._stream_base}/health", ...)
Si _stream_base = "https://lea.labs.laurinebazin.design/api/v1", l'URL finale est /api/v1/health -- cette route n'existe pas. La route sante est GET /health (racine).
Si _stream_base = "https://lea.labs.laurinebazin.design", l'URL finale est /health -- OK.
Encore un conflit : server_client.py attend une URL sans /api/v1, config.py la fournit avec.
INC-4 : Template deploy/lea_package/config.txt contient de vrais secrets
Fichier : deploy/lea_package/config.txt (ligne 19)
RPA_API_TOKEN=86031addb338e449fccdb1a983f61807aec15d42d482b9c7748ad607dc23caab
Ce fichier est versionne dans Git. Le token reel est en clair dans le repo.
INC-5 : Copie ancienne non maintenue (agent_v0/deploy/windows_client/)
Fichier : agent_v0/deploy/windows_client/config.py (ligne 41)
SERVER_URL = os.getenv("RPA_SERVER_URL", "http://localhost:8000/api/traces/upload")
Cette copie pointe encore sur l'ancien serveur API (port 8000, endpoint /api/traces/upload). Completement obsolete. Idem pour agent_v0/config.py (ligne 41).
INC-6 : RPA_SERVER_HOST sert a deux choses incompatibles
server_client.py: utiliseRPA_SERVER_HOSTcomme hostname nu (ex:192.168.1.40oulea.labs.laurinebazin.design) pour construirehttp://{host}:5005executor.py: utiliseRPA_SERVER_HOSTpour construire des URLs Ollama (http://{host}:11434/api/chat)main.py(ligne 94-95) : passeRPA_SERVER_HOSTcommeserver_hostauLeaServerClientet auChatWindow
Le config.txt genere par Fleet met RPA_SERVER_HOST=lea.labs.laurinebazin.design. Cela provoque :
executor.pytentehttp://lea.labs.laurinebazin.design:11434/api/chat-- Ollama n'est pas expose sur Internet (echec silencieux)
INC-7 : Redirect POST -> GET (Bug 3, non resolu cote client)
La lib Python requests suit les redirections 301/302 en transformant les POST en GET (RFC 7231). Quand NPM redirige http:// vers https://, tous les POST streaming (register, event, image, finalize) deviennent des GET et recevront un 405.
Aucune protection cote client. Le middleware serveur url_compat_rewrite ne resout que le probleme de path (pas le probleme de schema HTTP/HTTPS).
3. Recommandation architecturale
Principe : une seule variable, deux composants
RPA_SERVER_URL = URL complete incluant le prefixe API
Exemples :
http://localhost:5005/api/v1 (dev local)
http://192.168.1.40:5005/api/v1 (LAN)
https://lea.labs.laurinebazin.design/api/v1 (Internet)
Toutes les URLs de l'agent sont construites par concatenation de SERVER_URL + suffixe de route :
# config.py
SERVER_URL = os.getenv("RPA_SERVER_URL", "http://localhost:5005/api/v1")
# Pour le health-check (route a la racine, pas sous /api/v1) :
SERVER_BASE = SERVER_URL.rsplit("/api/v1", 1)[0] # ex: "https://lea.labs.laurinebazin.design"
# streamer.py, executor.py, main.py : tous utilisent
STREAMING_ENDPOINT = f"{SERVER_URL}/traces/stream" # inchange
HEALTH_ENDPOINT = f"{SERVER_BASE}/health" # NOUVEAU
# server_client.py : supprimer le systeme parallele
# _stream_base = SERVER_URL (plus de re-concatenation de "/api/v1" en dur)
Supprimer RPA_SERVER_HOST
Cette variable est source de confusion (INC-6). Elle ne doit pas exister. Le hostname est derive de RPA_SERVER_URL si besoin (pour l'affichage dans la chat window, etc.).
L'acces Ollama doit avoir sa propre variable RPA_OLLAMA_HOST (defaut : localhost), car Ollama n'est JAMAIS accessible via le reverse proxy Internet.
Protection POST -> GET
Ajouter dans streamer.py et executor.py :
# Dans chaque session requests :
session = requests.Session()
session.max_redirects = 0 # Refuser les redirections (echouer bruyamment)
Ou forcer https:// cote client si le host est un domaine public (pas localhost/IP privee).
Template config.txt
RPA_SERVER_URL=CONFIGURE_ME
RPA_API_TOKEN=CONFIGURE_ME
RPA_MACHINE_ID=CONFIGURE_ME
Pas de token reel, pas de valeur par defaut fonctionnelle. L'agent doit refuser de demarrer si RPA_SERVER_URL contient "CONFIGURE_ME".
4. Matrice scenarios x configuration
| Scenario | RPA_SERVER_URL | RPA_API_TOKEN | Notes |
|---|---|---|---|
| Dev local (meme machine) | http://localhost:5005/api/v1 |
(vide ou token dev) | RPA_AUTH_DISABLED=true cote serveur |
| LAN interne (Dom <-> VM) | http://192.168.1.40:5005/api/v1 |
token prod | HTTP OK en LAN ferme |
| Internet via NPM (TIM) | https://lea.labs.laurinebazin.design/api/v1 |
token prod | HTTPS obligatoire, pas de redirect |
| Futur DGX on-premise | http://<ip_dgx>:5005/api/v1 ou https://... |
token prod | Selon reseau client |
5. Liste des fichiers a corriger
Priorite HAUTE (bloquant pour le deploiement TIM)
| Fichier | Ligne(s) | Action |
|---|---|---|
agent_v0/lea_ui/server_client.py |
77-81, 161, 230, 287, 321, 349 | Supprimer la double logique _stream_base. Utiliser SERVER_URL de config.py comme base, ne plus concatener /api/v1 en dur dans les appels. Pour /health, utiliser SERVER_BASE (sans /api/v1). |
agent_v0/agent_v1/main.py |
94-95 | Supprimer l'utilisation de RPA_SERVER_HOST pour construire le LeaServerClient. Passer SERVER_URL directement. |
agent_v0/agent_v1/main.py |
370 | Utiliser STREAMING_ENDPOINT au lieu de reconstruire le chemin manuellement. |
agent_v0/agent_v1/network/streamer.py |
34 | Aucun changement (utilise deja STREAMING_ENDPOINT correctement). |
deploy/lea_package/config.txt |
14, 19, 20 | Remplacer les valeurs par des placeholders CONFIGURE_ME. Supprimer le token reel. |
deploy/installer/config_template.txt |
26-27 | Idem, remplacer le token reel par un placeholder. |
Priorite MOYENNE (coherence du codebase)
| Fichier | Ligne(s) | Action |
|---|---|---|
agent_v0/agent_v1/config.py |
43-45 | Ajouter SERVER_BASE (URL sans /api/v1) pour le health-check. |
agent_v0/agent_v1/core/executor.py |
1144, 1280, 1595 | Remplacer RPA_SERVER_HOST par une nouvelle var RPA_OLLAMA_HOST (defaut localhost). |
web_dashboard/app.py |
2055-2060 | Renommer _RPA_PUBLIC_URL en _RPA_PUBLIC_SERVER_URL. S'assurer que le /api/v1 est toujours present dans le config.txt genere (deja fait, ligne 2097-2098). |
web_dashboard/app.py |
2119 | Supprimer la ligne RPA_SERVER_HOST= du config.txt genere. |
Priorite BASSE (nettoyage)
| Fichier | Action |
|---|---|
agent_v0/config.py |
Supprimer ou marquer deprecated (ancien agent V0, port 8000). |
agent_v0/deploy/windows_client/ |
Supprimer l'arborescence entiere (copie obsolete, remplacee par le ZIP Fleet). |
agent_v0/deploy/windows_client/config.py |
Port 8000, endpoint /api/traces/upload -- completement mort. |
Protection anti-redirect (Bug 3)
| Fichier | Action |
|---|---|
agent_v0/agent_v1/network/streamer.py |
Utiliser requests.Session() avec max_redirects=0 ou forcer HTTPS si domaine public. |
agent_v0/agent_v1/core/executor.py |
Idem pour les appels HTTP du replay (resolve_target, pre_analyze, replay/next, replay/result). |
6. Diagramme de flux (etat cible)
config.txt (genere par Fleet ou rempli a la main)
|
| RPA_SERVER_URL=https://lea.labs.laurinebazin.design/api/v1
| RPA_API_TOKEN=<token>
| RPA_MACHINE_ID=<id>
| (plus de RPA_SERVER_HOST)
|
v
Lea.bat -> set variables d'environnement
|
v
agent_v1/config.py
| SERVER_URL = "https://lea.labs.laurinebazin.design/api/v1"
| SERVER_BASE = "https://lea.labs.laurinebazin.design"
| STREAMING_ENDPOINT = "https://lea.labs.laurinebazin.design/api/v1/traces/stream"
| HEALTH_ENDPOINT = "https://lea.labs.laurinebazin.design/health"
|
+-------> streamer.py : STREAMING_ENDPOINT + "/register", "/event", "/image", "/finalize"
+-------> main.py : STREAMING_ENDPOINT + "/image" (heartbeat)
| SERVER_URL (passe a executor.poll_and_execute)
+-------> executor.py : SERVER_URL + "/traces/stream/replay/next", etc.
+-------> server_client.py : SERVER_URL + "/traces/stream/workflows", etc.
| HEALTH_ENDPOINT (pour check_connection)
|
v (HTTPS, port 443)
NPM Reverse Proxy
|
v (HTTP, port 5005)
FastAPI api_stream.py
| /health
| /api/v1/traces/stream/register
| /api/v1/traces/stream/event
| /api/v1/traces/stream/image
| /api/v1/traces/stream/replay/next
| ...
7. Resume des bugs originaux et leur resolution
| Bug | Cause racine | Correction |
|---|---|---|
| Bug 1 (URL obsolete dans config.txt) | Template deploy/lea_package/config.txt jamais mis a jour |
DEJA CORRIGE (le fichier actuel contient /api/v1). Mais le token reel est toujours en clair. |
| Bug 2 (mismatch /api/v1) | Deux modules (config.py vs server_client.py) avec des conventions incompatibles pour construire les URLs |
Unifier sur une seule convention : RPA_SERVER_URL inclut TOUJOURS /api/v1. server_client.py doit utiliser SERVER_URL de config.py au lieu de reimplementer sa propre logique. |
| Bug 3 (POST -> GET sur redirect) | requests suit les 301 en changeant la methode HTTP |
Forcer HTTPS cote client quand le domaine est public, OU desactiver les redirections (max_redirects=0) pour echouer explicitement. Le middleware serveur url_compat_rewrite est un filet de securite pour le path, pas pour le schema. |