Compare commits
4 Commits
cf81ce4c7b
...
d686c3ac22
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d686c3ac22 | ||
|
|
e212f4141c | ||
|
|
33ddb51c3c | ||
|
|
1d6efdb1b7 |
249
deploy/build_package_full.sh
Executable file
249
deploy/build_package_full.sh
Executable file
@@ -0,0 +1,249 @@
|
||||
#!/bin/bash
|
||||
# ============================================================
|
||||
# build_package_full.sh — Construit le ZIP Lea COMPLET autoportant
|
||||
# ------------------------------------------------------------
|
||||
#
|
||||
# Produit : deploy/build/Lea_full_v<version>.zip
|
||||
#
|
||||
# Ce ZIP est destine a etre servi par le dashboard Fleet
|
||||
# (web_dashboard/app.py -> /api/fleet/download/<machine_id>).
|
||||
# Contrairement a deploy/Lea_v1.0.0.zip (sources seules, suppose
|
||||
# Python systeme), ce ZIP est 100% autonome :
|
||||
#
|
||||
# - Code source Lea A JOUR (working tree courant du repo,
|
||||
# via build_package.sh : agent_v0/agent_v1, lea_ui, run_agent_v1)
|
||||
# - Runtime Python 3.12 embedded complet (python-embed/)
|
||||
# avec toutes les dependances pre-installees (mss, pynput,
|
||||
# pystray, plyer, requests, PIL, pywin32, socketio...)
|
||||
# - Lea.bat pointant directement sur python-embed\pythonw.exe
|
||||
# (version embedded de configure_embed.ps1 : ni venv, ni pip,
|
||||
# ni reseau, ni Python systeme)
|
||||
# - python312._pth patche (import site active)
|
||||
# - Lea/config.txt placeholder (CONFIGURE_ME) que le dashboard
|
||||
# remplace a la volee par la config de l'agent
|
||||
# - PAS de install.bat (plus aucune etape d'installation Python)
|
||||
#
|
||||
# Experience utilisateur cible (non-IT) :
|
||||
# dezipper -> double-clic Lea.bat -> Lea demarre dans le systray.
|
||||
# Aucune installation de Python, aucun UAC.
|
||||
#
|
||||
# Usage :
|
||||
# ./deploy/build_package_full.sh # Build complet
|
||||
# ./deploy/build_package_full.sh --clean # Nettoyer avant
|
||||
#
|
||||
# Pre-requis :
|
||||
# - bash, rsync, zip
|
||||
# - deploy/installer/python-3.12-embed/ (runtime embedded, ~80 Mo,
|
||||
# non versionne — restaure depuis lea_python_embed_working.tgz si absent)
|
||||
# ============================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # deploy/
|
||||
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" # racine repo
|
||||
INSTALLER_DIR="$SCRIPT_DIR/installer"
|
||||
STAGING_DIR="$SCRIPT_DIR/build/installer_staging"
|
||||
BUILD_DIR="$SCRIPT_DIR/build"
|
||||
ASSEMBLY_DIR="$BUILD_DIR/Lea_full_assembly" # arborescence Lea/ temporaire
|
||||
|
||||
# Version lue depuis la source courante.
|
||||
# NB : la ligne peut etre soit AGENT_VERSION = "1.0.1" soit
|
||||
# AGENT_VERSION = os.environ.get("RPA_AGENT_VERSION", "1.0.1").
|
||||
# La regex de build_package.sh/build_installer.sh ne gere QUE la 1ere forme
|
||||
# (et retombe sur 1.0.0 pour la 2e). Ici on prend le DERNIER litteral entre
|
||||
# guillemets de la ligne AGENT_VERSION (= la valeur par defaut effective),
|
||||
# pour nommer le ZIP de maniere stable quelle que soit la forme.
|
||||
VERSION=$(grep -m1 'AGENT_VERSION' "$PROJECT_ROOT/agent_v0/agent_v1/config.py" \
|
||||
| grep -oP '"[^"]+"' | tr -d '"' | tail -1)
|
||||
VERSION="${VERSION:-1.0.0}"
|
||||
OUTPUT_ZIP="$BUILD_DIR/Lea_full_v${VERSION}.zip"
|
||||
|
||||
echo -e "${GREEN}============================================================${NC}"
|
||||
echo -e "${GREEN} Build ZIP Lea COMPLET autoportant v${VERSION}${NC}"
|
||||
echo -e "${GREEN}============================================================${NC}"
|
||||
echo ""
|
||||
|
||||
CLEAN=0
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--clean) CLEAN=1 ;;
|
||||
*) echo "Argument inconnu : $arg" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# 1. Regenerer le staging depuis la SOURCE COURANTE du repo
|
||||
# build_installer.sh --stage-only appelle build_package.sh
|
||||
# (qui copie agent_v0/agent_v1, lea_ui, run_agent_v1.py courants)
|
||||
# puis ajoute python-3.12-embed/ + helpers, et exclut install.bat.
|
||||
#
|
||||
# --clean est TOUJOURS force : sans lui, build_installer.sh reutilise
|
||||
# un deploy/build/Lea/ deja present (cache du build precedent) et ne
|
||||
# re-execute PAS build_package.sh -> la source embarquee serait perimee.
|
||||
# On veut au contraire garantir le working tree COURANT du repo.
|
||||
# ---------------------------------------------------------------
|
||||
echo "[1/6] Regeneration du staging depuis la source courante (--clean force)..."
|
||||
bash "$INSTALLER_DIR/build_installer.sh" --stage-only --clean
|
||||
if [[ ! -d "$STAGING_DIR" ]]; then
|
||||
echo -e "${RED} ERREUR : staging $STAGING_DIR absent apres build_installer.sh${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo " Staging pret : $STAGING_DIR"
|
||||
echo ""
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# 2. Assembler l'arborescence Lea/ (prefixe attendu par le dashboard
|
||||
# qui remplace exactement 'Lea/config.txt').
|
||||
# ---------------------------------------------------------------
|
||||
echo "[2/6] Assemblage de l'arborescence Lea/..."
|
||||
rm -rf "$ASSEMBLY_DIR"
|
||||
mkdir -p "$ASSEMBLY_DIR/Lea"
|
||||
|
||||
# Copier le staging, en renommant python-3.12-embed -> python-embed
|
||||
# (chemin attendu par le Lea.bat embedded : %~dp0python-embed\pythonw.exe)
|
||||
rsync -a \
|
||||
--exclude='python-3.12-embed' \
|
||||
--exclude='install.bat' \
|
||||
--exclude='config.txt' \
|
||||
"$STAGING_DIR/" \
|
||||
"$ASSEMBLY_DIR/Lea/"
|
||||
|
||||
rsync -a "$STAGING_DIR/python-3.12-embed/" "$ASSEMBLY_DIR/Lea/python-embed/"
|
||||
echo " Source + python-embed/ assembles"
|
||||
echo ""
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# 3. Lea.bat embedded : extraire le bloc canonique de configure_embed.ps1
|
||||
# (le here-string $NewLeaBat). C'est la SEULE source de verite du
|
||||
# Lea.bat embedded ; on ne le duplique pas dans ce script.
|
||||
# ---------------------------------------------------------------
|
||||
echo "[3/6] Generation de Lea.bat (runtime embedded)..."
|
||||
LEA_BAT_OUT="$ASSEMBLY_DIR/Lea/Lea.bat"
|
||||
python3 - "$INSTALLER_DIR/configure_embed.ps1" "$LEA_BAT_OUT" <<'PYEOF'
|
||||
import sys, re
|
||||
ps1_path, out_path = sys.argv[1], sys.argv[2]
|
||||
text = open(ps1_path, encoding="utf-8").read()
|
||||
# Extrait le here-string PowerShell : $NewLeaBat = @" ... "@
|
||||
m = re.search(r'\$NewLeaBat\s*=\s*@"\r?\n(.*?)\r?\n"@', text, re.DOTALL)
|
||||
if not m:
|
||||
sys.exit("ERREUR : bloc $NewLeaBat introuvable dans configure_embed.ps1")
|
||||
content = m.group(1)
|
||||
# CRLF pour un .bat Windows
|
||||
content = content.replace("\r\n", "\n").replace("\n", "\r\n")
|
||||
if not content.endswith("\r\n"):
|
||||
content += "\r\n"
|
||||
open(out_path, "wb").write(content.encode("ascii"))
|
||||
print(f" Lea.bat genere depuis configure_embed.ps1 ({len(content)} octets)")
|
||||
PYEOF
|
||||
|
||||
# Installateur 1-clic non-IT (raccourci Bureau + Demarrage automatique,
|
||||
# per-user, sans admin). Asset statique CRLF/ASCII copie tel quel dans Lea/.
|
||||
INSTALLER_BAT_SRC="$INSTALLER_DIR/Installer-Lea.bat"
|
||||
if [[ ! -f "$INSTALLER_BAT_SRC" ]]; then
|
||||
echo -e "${RED} ERREUR : $INSTALLER_BAT_SRC introuvable${NC}"
|
||||
exit 1
|
||||
fi
|
||||
cp "$INSTALLER_BAT_SRC" "$ASSEMBLY_DIR/Lea/Installer-Lea.bat"
|
||||
echo " Installer-Lea.bat (installation 1-clic) ajoute"
|
||||
|
||||
# Notice utilisateur dediee a l'install autonome (remplace la LISEZMOI legacy
|
||||
# du staging, qui decrit l'ancien flux install.bat + Python systeme).
|
||||
LISEZMOI_SRC="$INSTALLER_DIR/LISEZMOI-autonome.txt"
|
||||
if [[ -f "$LISEZMOI_SRC" ]]; then
|
||||
cp "$LISEZMOI_SRC" "$ASSEMBLY_DIR/Lea/LISEZMOI.txt"
|
||||
echo " LISEZMOI.txt (version install autonome) pose"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# 4. Patcher python312._pth (import site active) — idempotent.
|
||||
# Necessaire pour que l'embed charge site-packages.
|
||||
# ---------------------------------------------------------------
|
||||
echo "[4/6] Patch python312._pth (import site)..."
|
||||
PTH_FILE=$(find "$ASSEMBLY_DIR/Lea/python-embed" -name "python*._pth" | head -1)
|
||||
if [[ -z "$PTH_FILE" ]]; then
|
||||
echo -e "${RED} ERREUR : python*._pth introuvable dans python-embed/${NC}"
|
||||
exit 1
|
||||
fi
|
||||
# Decommente '#import site' s'il est commente ; sinon laisse tel quel.
|
||||
sed -i 's/^#import site/import site/' "$PTH_FILE"
|
||||
if ! grep -q '^import site' "$PTH_FILE"; then
|
||||
printf 'import site\r\n' >> "$PTH_FILE"
|
||||
fi
|
||||
echo " $(basename "$PTH_FILE") : import site actif"
|
||||
echo ""
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# 5. config.txt placeholder (CONFIGURE_ME) — cible de l'injection
|
||||
# dashboard (app.py remplace 'Lea/config.txt').
|
||||
# ---------------------------------------------------------------
|
||||
echo "[5/6] Pose du config.txt placeholder..."
|
||||
cp "$INSTALLER_DIR/../lea_package/config.txt" "$ASSEMBLY_DIR/Lea/config.txt"
|
||||
if ! grep -q 'CONFIGURE_ME' "$ASSEMBLY_DIR/Lea/config.txt"; then
|
||||
echo -e "${YELLOW} AVERTISSEMENT : config.txt ne contient pas CONFIGURE_ME (placeholder inattendu)${NC}"
|
||||
fi
|
||||
echo " Lea/config.txt (placeholder) pose"
|
||||
echo ""
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# 6. Validation de completude AVANT zip (un ZIP incomplet = install
|
||||
# cassee chez le client non-IT).
|
||||
# ---------------------------------------------------------------
|
||||
echo "[6/6] Validation + creation du ZIP..."
|
||||
REQUIRED=(
|
||||
"Lea/run_agent_v1.py"
|
||||
"Lea/agent_v1/config.py"
|
||||
"Lea/agent_v1/main.py"
|
||||
"Lea/lea_ui/server_client.py"
|
||||
"Lea/Lea.bat"
|
||||
"Lea/Installer-Lea.bat"
|
||||
"Lea/config.txt"
|
||||
"Lea/python-embed/python.exe"
|
||||
"Lea/python-embed/pythonw.exe"
|
||||
"Lea/python-embed/Lib/site-packages/mss"
|
||||
"Lea/python-embed/Lib/site-packages/win32"
|
||||
"Lea/python-embed/Lib/site-packages/socketio"
|
||||
)
|
||||
MISSING=()
|
||||
for f in "${REQUIRED[@]}"; do
|
||||
[[ -e "$ASSEMBLY_DIR/$f" ]] || MISSING+=("$f")
|
||||
done
|
||||
# install.bat NE DOIT PAS etre present
|
||||
if [[ -e "$ASSEMBLY_DIR/Lea/install.bat" ]]; then
|
||||
echo -e "${RED} ERREUR : install.bat present dans l'assemblage (doit etre absent).${NC}"
|
||||
exit 1
|
||||
fi
|
||||
if [[ ${#MISSING[@]} -gt 0 ]]; then
|
||||
echo -e "${RED} ERREUR : assemblage incomplet. Manquants :${NC}"
|
||||
printf ' - %s\n' "${MISSING[@]}"
|
||||
exit 1
|
||||
fi
|
||||
echo " Completude verifiee (${#REQUIRED[@]} elements, install.bat absent)"
|
||||
|
||||
# Verif source A JOUR : le config.py embarque doit etre identique au repo
|
||||
if ! diff -q "$PROJECT_ROOT/agent_v0/agent_v1/config.py" "$ASSEMBLY_DIR/Lea/agent_v1/config.py" >/dev/null; then
|
||||
echo -e "${RED} ERREUR : agent_v1/config.py embarque DIFFERE de la source repo !${NC}"
|
||||
echo " Le ZIP n'embarque pas la source a jour — build interrompu."
|
||||
exit 1
|
||||
fi
|
||||
echo " Source a jour confirmee (agent_v1/config.py == repo)"
|
||||
|
||||
rm -f "$OUTPUT_ZIP"
|
||||
( cd "$ASSEMBLY_DIR" && zip -q -r -X "$OUTPUT_ZIP" Lea )
|
||||
ZIP_SIZE=$(du -h "$OUTPUT_ZIP" | cut -f1)
|
||||
echo ""
|
||||
echo -e "${GREEN}============================================================${NC}"
|
||||
echo -e "${GREEN} ZIP complet produit !${NC}"
|
||||
echo -e "${GREEN}============================================================${NC}"
|
||||
echo ""
|
||||
echo " Fichier : $OUTPUT_ZIP"
|
||||
echo " Taille : $ZIP_SIZE"
|
||||
echo ""
|
||||
echo " Servi par le dashboard via web_dashboard/app.py (_LEA_ZIP_TEMPLATE)."
|
||||
echo " L'utilisateur : dezippe -> double-clic Lea.bat (aucun Python systeme requis)."
|
||||
echo ""
|
||||
152
deploy/installer/Installer-Lea.bat
Normal file
152
deploy/installer/Installer-Lea.bat
Normal file
@@ -0,0 +1,152 @@
|
||||
@echo off
|
||||
chcp 65001 >nul 2>&1
|
||||
title Lea - Installation 1-clic
|
||||
setlocal EnableDelayedExpansion
|
||||
|
||||
:: ============================================================
|
||||
:: Installer-Lea.bat - Installation 1-clic per-user (sans admin)
|
||||
:: ------------------------------------------------------------
|
||||
:: - Copie le paquet Lea (y compris python-embed) vers
|
||||
:: %LOCALAPPDATA%\Lea (emplacement stable per-user).
|
||||
:: - Cree un raccourci sur le Bureau.
|
||||
:: - Cree un raccourci dans le dossier Demarrage (lancement
|
||||
:: automatique a chaque ouverture de session Windows).
|
||||
:: - Lance Lea une premiere fois (pythonw, sans console).
|
||||
::
|
||||
:: Aucun droit administrateur requis. Aucun service Windows
|
||||
:: (Lea est une application systray, doit tourner dans la
|
||||
:: session utilisateur).
|
||||
:: ============================================================
|
||||
|
||||
echo.
|
||||
echo ============================================================
|
||||
echo Lea - Installation
|
||||
echo ============================================================
|
||||
echo.
|
||||
|
||||
:: --- Emplacement source (dossier de ce script) -------------
|
||||
set "SRC=%~dp0"
|
||||
:: Retirer l'antislash final eventuel
|
||||
if "%SRC:~-1%"=="\" set "SRC=%SRC:~0,-1%"
|
||||
|
||||
:: --- Emplacement cible per-user ----------------------------
|
||||
set "DEST=%LOCALAPPDATA%\Lea"
|
||||
|
||||
echo Installation vers : %DEST%
|
||||
echo (copie du runtime embarque, cela prend quelques secondes)
|
||||
echo.
|
||||
|
||||
:: --- Verification du runtime embarque dans la source -------
|
||||
if not exist "%SRC%\python-embed\pythonw.exe" (
|
||||
echo ERREUR : python-embed\pythonw.exe introuvable dans le paquet.
|
||||
echo Le paquet semble incomplet. Re-telechargez Lea depuis le tableau de bord.
|
||||
echo.
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
:: --- Si Lea tourne deja depuis la cible, l'arreter ----------
|
||||
if exist "%DEST%\lea_agent.lock" (
|
||||
for /f "usebackq tokens=* delims=" %%i in ("%DEST%\lea_agent.lock") do (
|
||||
taskkill /F /PID %%i >nul 2>&1
|
||||
)
|
||||
del /f /q "%DEST%\lea_agent.lock" >nul 2>&1
|
||||
timeout /t 1 >nul
|
||||
)
|
||||
|
||||
:: --- Copie du paquet vers la cible -------------------------
|
||||
:: robocopy : robuste pour la grosse arborescence python-embed.
|
||||
:: /E sous-dossiers (vides inclus), /NFL /NDL /NJH /NJS /NP silencieux.
|
||||
:: Codes de sortie robocopy < 8 = succes ; >= 8 = echec.
|
||||
if not exist "%DEST%" mkdir "%DEST%" >nul 2>&1
|
||||
robocopy "%SRC%" "%DEST%" /E /NFL /NDL /NJH /NJS /NP >nul
|
||||
if %ERRORLEVEL% GEQ 8 (
|
||||
echo robocopy a echoue, tentative avec xcopy...
|
||||
xcopy "%SRC%\*" "%DEST%\" /E /I /H /Y >nul
|
||||
if errorlevel 1 (
|
||||
echo.
|
||||
echo ERREUR : la copie vers %DEST% a echoue.
|
||||
echo Verifiez l'espace disque et les droits sur votre profil.
|
||||
echo.
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
)
|
||||
echo Copie terminee - OK
|
||||
echo.
|
||||
|
||||
:: --- Ne pas laisser l'installeur se relancer en boucle -----
|
||||
:: (on supprime la copie de l'installeur dans la cible : inutile une fois installe)
|
||||
del /f /q "%DEST%\Installer-Lea.bat" >nul 2>&1
|
||||
|
||||
:: --- Detection d'une icone optionnelle ---------------------
|
||||
:: Cherche un .ico dans le paquet installe (best-effort).
|
||||
set "ICON="
|
||||
for /f "delims=" %%f in ('dir /b /s "%DEST%\*.ico" 2^>nul') do (
|
||||
if not defined ICON set "ICON=%%f"
|
||||
)
|
||||
|
||||
:: --- Cibles des raccourcis ---------------------------------
|
||||
set "TARGET=%DEST%\python-embed\pythonw.exe"
|
||||
set "ARGS=run_agent_v1.py"
|
||||
set "WORKDIR=%DEST%"
|
||||
set "DESKTOP_LNK=%USERPROFILE%\Desktop\Lea.lnk"
|
||||
set "STARTUP_LNK=%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\Lea.lnk"
|
||||
|
||||
:: --- Creation des raccourcis via PowerShell (WScript.Shell) -
|
||||
echo Creation des raccourcis (Bureau + Demarrage automatique)...
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -Command ^
|
||||
"$ws = New-Object -ComObject WScript.Shell;" ^
|
||||
"foreach ($p in @('%DESKTOP_LNK%','%STARTUP_LNK%')) {" ^
|
||||
" $dir = Split-Path $p -Parent;" ^
|
||||
" if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null }" ^
|
||||
" $s = $ws.CreateShortcut($p);" ^
|
||||
" $s.TargetPath = '%TARGET%';" ^
|
||||
" $s.Arguments = '%ARGS%';" ^
|
||||
" $s.WorkingDirectory = '%WORKDIR%';" ^
|
||||
" $s.Description = 'Lea - Assistante IA';" ^
|
||||
" if ('%ICON%' -ne '' -and (Test-Path '%ICON%')) { $s.IconLocation = '%ICON%' }" ^
|
||||
" $s.Save();" ^
|
||||
"}"
|
||||
if errorlevel 1 (
|
||||
echo ATTENTION : la creation des raccourcis a partiellement echoue.
|
||||
echo Vous pourrez tout de meme lancer Lea via %TARGET%.
|
||||
) else (
|
||||
echo Raccourcis crees - OK
|
||||
)
|
||||
echo.
|
||||
|
||||
:: --- Premier lancement de Lea (sans console) ---------------
|
||||
echo Demarrage de Lea...
|
||||
pushd "%DEST%"
|
||||
start "" /b "%TARGET%" %ARGS%
|
||||
popd
|
||||
|
||||
:: --- Verification rapide (via le lock PID) -----------------
|
||||
timeout /t 3 >nul
|
||||
set "LEA_ALIVE=0"
|
||||
if exist "%DEST%\lea_agent.lock" (
|
||||
for /f "usebackq tokens=* delims=" %%i in ("%DEST%\lea_agent.lock") do (
|
||||
tasklist /FI "PID eq %%i" /NH 2>nul | findstr /I "pythonw" >nul && set "LEA_ALIVE=1"
|
||||
)
|
||||
)
|
||||
|
||||
echo.
|
||||
echo ============================================================
|
||||
if "%LEA_ALIVE%"=="1" (
|
||||
echo Lea est installee et demarree !
|
||||
) else (
|
||||
echo Lea est installee.
|
||||
)
|
||||
echo ============================================================
|
||||
echo.
|
||||
echo - Lea apparait en bas a droite, dans la barre des taches
|
||||
echo (petite icone ronde, a cote de l'horloge).
|
||||
echo - Lea demarrera AUTOMATIQUEMENT a chaque ouverture de session.
|
||||
echo - Un raccourci "Lea" a ete ajoute sur votre Bureau.
|
||||
echo.
|
||||
echo Vous pouvez fermer cette fenetre.
|
||||
echo.
|
||||
pause
|
||||
endlocal
|
||||
exit /b 0
|
||||
105
deploy/installer/LISEZMOI-autonome.txt
Normal file
105
deploy/installer/LISEZMOI-autonome.txt
Normal file
@@ -0,0 +1,105 @@
|
||||
============================================================
|
||||
Lea - Votre assistante intelligente
|
||||
============================================================
|
||||
|
||||
Bienvenue ! Lea est une assistante qui apprend vos taches
|
||||
repetitives sur l'ordinateur pour pouvoir vous aider.
|
||||
|
||||
Cette version est 100% autonome : aucun Python a installer,
|
||||
aucun droit administrateur necessaire.
|
||||
|
||||
|
||||
INSTALLATION (une seule fois)
|
||||
-----------------------------
|
||||
|
||||
1. Si Lea est dans un fichier ZIP, faites un clic droit
|
||||
dessus puis "Extraire tout..." (ne lancez pas Lea
|
||||
directement depuis le ZIP).
|
||||
|
||||
2. Ouvrez le dossier extrait et double-cliquez sur
|
||||
"Installer-Lea.bat".
|
||||
|
||||
3. Patientez quelques secondes (copie du programme).
|
||||
A la fin, le message "Lea est installee et demarree"
|
||||
s'affiche.
|
||||
|
||||
C'est tout. Lea est installee dans votre profil utilisateur
|
||||
et :
|
||||
|
||||
- un raccourci "Lea" est ajoute sur votre Bureau ;
|
||||
- Lea demarrera AUTOMATIQUEMENT a chaque fois que vous
|
||||
ouvrez votre session Windows.
|
||||
|
||||
Vous pouvez ensuite supprimer le dossier extrait et le ZIP :
|
||||
Lea continue de fonctionner (elle a ete copiee a part).
|
||||
|
||||
|
||||
LANCER LEA MANUELLEMENT
|
||||
-----------------------
|
||||
|
||||
Si besoin, double-cliquez sur le raccourci "Lea" du Bureau.
|
||||
|
||||
Lea apparait en bas a droite de votre ecran, dans la barre
|
||||
des taches (petite icone ronde, a cote de l'horloge).
|
||||
|
||||
Clic droit sur l'icone pour ouvrir le menu :
|
||||
|
||||
- "Apprenez-moi une tache" : Lea observe ce que vous faites
|
||||
et memorise les etapes. Travaillez normalement, Lea
|
||||
apprend en vous regardant.
|
||||
|
||||
- "C'est termine" : Arrete l'enregistrement quand vous
|
||||
avez fini la tache. Si vous oubliez, Lea s'arrete
|
||||
automatiquement apres 1 heure.
|
||||
|
||||
- "Discuter avec Lea" : Ouvre une fenetre de discussion
|
||||
pour poser des questions.
|
||||
|
||||
- "ARRET D'URGENCE" : Arrete immediatement tout ce que
|
||||
Lea est en train de faire.
|
||||
|
||||
- "Quitter Lea" : Ferme le programme.
|
||||
|
||||
|
||||
INFORMATIONS IMPORTANTES
|
||||
------------------------
|
||||
|
||||
Quand Lea enregistre vos actions, elle capture votre ecran,
|
||||
vos clics et vos frappes clavier.
|
||||
|
||||
- Lea vous previent AVANT chaque enregistrement
|
||||
- Les donnees sensibles (mots de passe, informations
|
||||
medicales) sont automatiquement floutees
|
||||
- L'enregistrement s'arrete automatiquement apres 1 heure
|
||||
- Vous pouvez arreter a tout moment via le menu
|
||||
|
||||
Lea est un systeme base sur l'intelligence artificielle
|
||||
(Article 50, Reglement europeen sur l'IA).
|
||||
|
||||
|
||||
CONFIGURATION
|
||||
-------------
|
||||
|
||||
Si vous devez modifier l'adresse du serveur, ouvrez le fichier
|
||||
"config.txt" (dans le dossier d'installation de Lea) avec le
|
||||
Bloc-notes et changez les valeurs.
|
||||
|
||||
Ne modifiez rien d'autre sans l'accord de votre administrateur.
|
||||
|
||||
|
||||
EN CAS DE PROBLEME
|
||||
-------------------
|
||||
|
||||
- Lea ne demarre pas : double-cliquez a nouveau sur le
|
||||
raccourci "Lea" du Bureau, ou relancez "Installer-Lea.bat".
|
||||
|
||||
- Lea est deconnectee : Verifiez votre connexion
|
||||
reseau. Le serveur est peut-etre en maintenance.
|
||||
|
||||
- Pour desinstaller : supprimez le dossier "Lea" dans
|
||||
votre profil (dossier %LOCALAPPDATA%\Lea) ainsi que les
|
||||
raccourcis "Lea" du Bureau et du Demarrage.
|
||||
|
||||
- En cas de doute, contactez votre administrateur.
|
||||
|
||||
============================================================
|
||||
44
tests/test_dashboard_server_url.py
Normal file
44
tests/test_dashboard_server_url.py
Normal file
@@ -0,0 +1,44 @@
|
||||
"""TDD — résolution de l'URL serveur d'enrôlement depuis system_config.json.
|
||||
|
||||
Câble l'éditeur adresses/ports du dashboard (`services.streaming`) vers le
|
||||
`RPA_SERVER_URL` généré pour chaque agent Léa.
|
||||
|
||||
Priorité : config (`system_config.json`) > variable d'env > défaut.
|
||||
Un host loopback/vide dans la config = « non configuré » → fallback env, pour
|
||||
ne PAS régresser le déploiement actuel où l'URL vient de l'environnement.
|
||||
"""
|
||||
import os
|
||||
|
||||
# L'import du dashboard est fail-closed sur l'auth → escape dev/test documenté.
|
||||
os.environ.setdefault("DASHBOARD_AUTH_DISABLED", "true")
|
||||
|
||||
from web_dashboard import app as dash # noqa: E402
|
||||
|
||||
|
||||
def test_resolve_url_from_config_streaming_host(monkeypatch):
|
||||
"""La config (host streaming édité dans l'UI) prime, même si l'env existe."""
|
||||
monkeypatch.setattr(
|
||||
dash, "load_system_config",
|
||||
lambda: {"services": {"streaming": {"host": "192.168.1.178", "port": 5005}}},
|
||||
)
|
||||
monkeypatch.setenv("RPA_SERVER_URL", "http://192.168.1.45:5005/api/v1")
|
||||
assert dash._resolve_public_server_url() == "http://192.168.1.178:5005/api/v1"
|
||||
|
||||
|
||||
def test_resolve_url_loopback_in_config_falls_back_to_env(monkeypatch):
|
||||
"""host=localhost dans la config = non configuré → on garde l'env (pas de régression)."""
|
||||
monkeypatch.setattr(
|
||||
dash, "load_system_config",
|
||||
lambda: {"services": {"streaming": {"host": "localhost", "port": 5005}}},
|
||||
)
|
||||
monkeypatch.delenv("RPA_PUBLIC_URL", raising=False)
|
||||
monkeypatch.setenv("RPA_SERVER_URL", "http://192.168.1.45:5005/api/v1")
|
||||
assert dash._resolve_public_server_url() == "http://192.168.1.45:5005/api/v1"
|
||||
|
||||
|
||||
def test_resolve_url_no_config_uses_env(monkeypatch):
|
||||
"""Aucune section streaming → fallback env, normalisé en /api/v1."""
|
||||
monkeypatch.setattr(dash, "load_system_config", lambda: {"services": {}})
|
||||
monkeypatch.delenv("RPA_PUBLIC_URL", raising=False)
|
||||
monkeypatch.setenv("RPA_SERVER_URL", "http://10.0.0.5:5005/api/v1")
|
||||
assert dash._resolve_public_server_url() == "http://10.0.0.5:5005/api/v1"
|
||||
@@ -2219,8 +2219,33 @@ def proxy_fleet(endpoint):
|
||||
# Fleet — Téléchargement du ZIP installeur pré-configuré
|
||||
# =============================================================================
|
||||
|
||||
# Chemin du ZIP template Léa
|
||||
_LEA_ZIP_TEMPLATE = BASE_PATH / "deploy" / "Lea_v1.0.0.zip"
|
||||
# Chemin du ZIP template Léa.
|
||||
# ZIP COMPLET autoportant (runtime Python embedded inclus + source à jour) :
|
||||
# l'utilisateur dézippe puis double-clique Lea.bat, sans Python système ni UAC.
|
||||
# Construit par deploy/build_package_full.sh. Le placeholder Lea/config.txt
|
||||
# (CONFIGURE_ME) est remplacé à la volée par download_agent_package().
|
||||
# Fallback historique vers l'ancien ZIP léger (sources seules, suppose Python
|
||||
# système) uniquement s'il existe et que le complet est absent — pour ne pas
|
||||
# casser un environnement où le complet n'a pas encore été buildé.
|
||||
_LEA_ZIP_TEMPLATE_FULL = BASE_PATH / "deploy" / "build" / "Lea_full_v1.0.1.zip"
|
||||
_LEA_ZIP_TEMPLATE_LEGACY = BASE_PATH / "deploy" / "Lea_v1.0.0.zip"
|
||||
|
||||
|
||||
def _resolve_lea_zip_template():
|
||||
"""Résout le ZIP à servir, à la volée (le complet peut être buildé
|
||||
après le démarrage du dashboard). Préfère le ZIP complet autoportant ;
|
||||
retombe sur l'ancien ZIP léger uniquement s'il existe.
|
||||
Retourne None si aucun template n'est présent.
|
||||
"""
|
||||
if _LEA_ZIP_TEMPLATE_FULL.exists():
|
||||
return _LEA_ZIP_TEMPLATE_FULL
|
||||
if _LEA_ZIP_TEMPLATE_LEGACY.exists():
|
||||
return _LEA_ZIP_TEMPLATE_LEGACY
|
||||
return None
|
||||
|
||||
|
||||
# Compat : référence statique conservée pour le code/log historique.
|
||||
_LEA_ZIP_TEMPLATE = _resolve_lea_zip_template() or _LEA_ZIP_TEMPLATE_FULL
|
||||
|
||||
# URL publique du serveur (env ou fallback)
|
||||
_RPA_PUBLIC_URL = os.getenv(
|
||||
@@ -2248,6 +2273,33 @@ def _extract_host(url: str) -> str:
|
||||
_RPA_PUBLIC_HOST = _extract_host(_RPA_PUBLIC_URL)
|
||||
|
||||
|
||||
def _resolve_public_server_url() -> str:
|
||||
"""URL publique du serveur Léa pour l'enrôlement (config.txt des agents).
|
||||
|
||||
Priorité :
|
||||
1. system_config.json → services.streaming.{host,port} (édité dans le dashboard)
|
||||
2. variables d'env RPA_PUBLIC_URL / RPA_SERVER_URL
|
||||
3. défaut historique
|
||||
|
||||
Un host vide ou loopback dans la config = « non configuré » : on retombe sur
|
||||
l'env pour ne pas régresser un déploiement qui pilote l'URL par l'environnement.
|
||||
Toujours normalisée pour se terminer par /api/v1.
|
||||
"""
|
||||
_NON_ROUTABLE = {"", "localhost", "127.0.0.1", "0.0.0.0", "configure_me"}
|
||||
try:
|
||||
streaming = (load_system_config().get("services") or {}).get("streaming") or {}
|
||||
host = str(streaming.get("host") or "").strip()
|
||||
if host.lower() not in _NON_ROUTABLE:
|
||||
port = streaming.get("port") or 5005
|
||||
return _normalize_server_url(f"http://{host}:{port}")
|
||||
except Exception as exc: # config illisible → ne jamais casser l'enrôlement
|
||||
api_logger.warning(f"Résolution URL via system_config échouée: {exc}")
|
||||
env_url = os.getenv("RPA_PUBLIC_URL") or os.getenv("RPA_SERVER_URL")
|
||||
if env_url:
|
||||
return _normalize_server_url(env_url)
|
||||
return _normalize_server_url("https://lea.labs.laurinebazin.design")
|
||||
|
||||
|
||||
def _fetch_fleet_agent(machine_id: str):
|
||||
"""Récupère un agent depuis le serveur streaming (5005).
|
||||
|
||||
@@ -2284,7 +2336,7 @@ def _build_custom_config(machine_id: str, user_name: str, token: str) -> str:
|
||||
Le host est extrait proprement via urlparse (sans schema/port/path).
|
||||
"""
|
||||
now = datetime.now().strftime("%Y-%m-%d %H:%M")
|
||||
server_url = _normalize_server_url(_RPA_PUBLIC_URL)
|
||||
server_url = _resolve_public_server_url()
|
||||
|
||||
return f"""\
|
||||
# ============================================================
|
||||
@@ -2329,17 +2381,23 @@ def download_agent_package(machine_id):
|
||||
"""Génère et sert un ZIP Léa pré-configuré pour ce machine_id.
|
||||
|
||||
- Vérifie que le machine_id est enregistré et actif dans la fleet.
|
||||
- Lit le ZIP template (deploy/Lea_v1.0.0.zip).
|
||||
- Remplace config.txt par une version personnalisée.
|
||||
- Lit le ZIP template complet autoportant (deploy/build/Lea_full_v*.zip),
|
||||
avec fallback sur l'ancien ZIP léger (deploy/Lea_v1.0.0.zip) s'il est seul.
|
||||
- Remplace Lea/config.txt par une version personnalisée.
|
||||
- Renvoie le ZIP modifié en téléchargement (tout en mémoire).
|
||||
"""
|
||||
# Sécurité : l'auth Basic est déjà gérée par before_request
|
||||
|
||||
# 1. Vérifier que le ZIP template existe
|
||||
if not _LEA_ZIP_TEMPLATE.exists():
|
||||
# 1. Résoudre + vérifier que le ZIP template existe (à la volée)
|
||||
zip_template = _resolve_lea_zip_template()
|
||||
if zip_template is None:
|
||||
return jsonify({
|
||||
'error': 'ZIP template introuvable',
|
||||
'detail': f'{_LEA_ZIP_TEMPLATE} absent — exécuter deploy/build_package.sh',
|
||||
'detail': (
|
||||
f'Ni {_LEA_ZIP_TEMPLATE_FULL} ni {_LEA_ZIP_TEMPLATE_LEGACY} '
|
||||
'présents — exécuter deploy/build_package_full.sh (ZIP complet '
|
||||
'autoportant) ou deploy/build_package.sh (ZIP léger).'
|
||||
),
|
||||
}), 500
|
||||
|
||||
# 2. Vérifier que le machine_id est enregistré
|
||||
@@ -2360,7 +2418,7 @@ def download_agent_package(machine_id):
|
||||
# 5. Créer le ZIP personnalisé en mémoire
|
||||
output_buffer = io.BytesIO()
|
||||
try:
|
||||
with zipfile.ZipFile(_LEA_ZIP_TEMPLATE, 'r') as src_zip:
|
||||
with zipfile.ZipFile(zip_template, 'r') as src_zip:
|
||||
with zipfile.ZipFile(output_buffer, 'w', zipfile.ZIP_DEFLATED) as dst_zip:
|
||||
for item in src_zip.infolist():
|
||||
if item.filename == 'Lea/config.txt':
|
||||
@@ -2372,7 +2430,7 @@ def download_agent_package(machine_id):
|
||||
except zipfile.BadZipFile:
|
||||
return jsonify({
|
||||
'error': 'ZIP template corrompu',
|
||||
'detail': f'{_LEA_ZIP_TEMPLATE} n\'est pas un ZIP valide.',
|
||||
'detail': f'{zip_template} n\'est pas un ZIP valide.',
|
||||
}), 500
|
||||
|
||||
output_buffer.seek(0)
|
||||
|
||||
@@ -2290,7 +2290,7 @@
|
||||
: '<span style="display:inline-block;padding:3px 10px;border-radius:12px;font-size:11px;font-weight:600;background:rgba(100,116,139,0.15);color:#64748b;">révoqué</span>';
|
||||
|
||||
const downloadBtn = isActive
|
||||
? `<a href="/api/fleet/download/${encodeURIComponent(agent.machine_id)}" class="btn btn-primary btn-small" style="text-decoration:none;font-size:11px;" title="Télécharger l'installeur pré-configuré">📥</a>`
|
||||
? `<a href="/api/fleet/download/${encodeURIComponent(agent.machine_id)}" class="btn btn-primary btn-small" style="text-decoration:none;font-size:11px;" title="Télécharger Léa (installation autonome, sans Python — dézipper puis double-cliquer Lea.bat)">📥</a>`
|
||||
: `<span style="font-size:16px;color:#475569;cursor:not-allowed;opacity:0.4;" title="Agent révoqué — installeur indisponible">📥</span>`;
|
||||
|
||||
return `<tr style="border-bottom:1px solid #334155;">
|
||||
|
||||
Reference in New Issue
Block a user