Pipeline replay visuel : - VLM-first : l'agent appelle Ollama directement pour trouver les éléments - Template matching en fallback (seuil strict 0.90) - Stop immédiat si élément non trouvé (pas de clic blind) - Replay depuis session brute (/replay-session) sans attendre le VLM - Vérification post-action (screenshot hash avant/après) - Gestion des popups (Enter/Escape/Tab+Enter) Worker VLM séparé : - run_worker.py : process distinct du serveur HTTP - Communication par fichiers (_worker_queue.txt + _replay_active.lock) - Le serveur HTTP ne fait plus jamais de VLM → toujours réactif - Service systemd rpa-worker.service Capture clavier : - raw_keys (vk + press/release) pour replay exact indépendant du layout - Fix AZERTY : ToUnicodeEx + AltGr detection - Enter capturé comme \n, Tab comme \t - Filtrage modificateurs seuls (Ctrl/Alt/Shift parasites) - Fusion text_input consécutifs, dédup key_combo Sécurité & Internet : - HTTPS Let's Encrypt (lea.labs + vwb.labs.laurinebazin.design) - Token API fixe dans .env.local - HTTP Basic Auth sur VWB - Security headers (HSTS, CSP, nosniff) - CORS domaines publics, plus de wildcard Infrastructure : - DPI awareness (SetProcessDpiAwareness) Python + Rust - Métadonnées système (dpi_scale, window_bounds, monitors, os_theme) - Template matching multi-scale [0.5, 2.0] - Résolution dynamique (plus de hardcode 1920x1080) - VLM prefill fix (47x speedup, 3.5s au lieu de 180s) Modules : - core/auth/ : credential vault (Fernet AES), TOTP (RFC 6238), auth handler - core/federation/ : LearningPack export/import anonymisé, FAISS global - deploy/ : package Léa (config.txt, Lea.bat, install.bat, LISEZMOI.txt) UX : - Filtrage OS (VWB + Chat montrent que les workflows de l'OS courant) - Bibliothèque persistante (cache local + SQLite) - Clustering hybride (titre fenêtre + DBSCAN) - EdgeConstraints + PostConditions peuplés - GraphBuilder compound actions (toutes les frappes) Agent Rust : - Token Bearer auth (network.rs) - sysinfo.rs (DPI, résolution, window bounds via Win32 API) - config.txt lu automatiquement - Support Chrome/Brave/Firefox (pas que Edge) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
116 lines
3.7 KiB
Rust
116 lines
3.7 KiB
Rust
//! Capture d'écran via xcap.
|
|
//!
|
|
//! Fournit la capture du moniteur principal, l'encodage JPEG en base64,
|
|
//! et un hash perceptuel rapide pour la déduplication des heartbeats.
|
|
|
|
use base64::Engine;
|
|
use image::codecs::jpeg::JpegEncoder;
|
|
use image::DynamicImage;
|
|
use std::io::Cursor;
|
|
|
|
/// Capture le moniteur principal et retourne un DynamicImage.
|
|
///
|
|
/// Utilise xcap pour la capture cross-platform (DXGI sur Windows, X11/Wayland sur Linux).
|
|
pub fn capture_screenshot() -> Option<DynamicImage> {
|
|
let monitors = match xcap::Monitor::all() {
|
|
Ok(m) => m,
|
|
Err(e) => {
|
|
eprintln!("[CAPTURE] Erreur enumeration moniteurs : {}", e);
|
|
return None;
|
|
}
|
|
};
|
|
|
|
let primary = monitors
|
|
.into_iter()
|
|
.find(|m| m.is_primary().unwrap_or(false));
|
|
let monitor = match primary {
|
|
Some(m) => m,
|
|
None => {
|
|
eprintln!("[CAPTURE] Aucun moniteur principal trouve");
|
|
return None;
|
|
}
|
|
};
|
|
|
|
match monitor.capture_image() {
|
|
Ok(rgba_image) => Some(DynamicImage::ImageRgba8(rgba_image)),
|
|
Err(e) => {
|
|
eprintln!("[CAPTURE] Erreur capture ecran : {}", e);
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Encode une image en JPEG et retourne le résultat en base64.
|
|
///
|
|
/// La qualité doit être entre 1 (mauvaise) et 100 (excellente).
|
|
/// 85 est un bon compromis taille/qualité pour le streaming réseau.
|
|
pub fn screenshot_to_jpeg_base64(img: &DynamicImage, quality: u8) -> String {
|
|
let rgb = img.to_rgb8();
|
|
let mut buffer = Cursor::new(Vec::new());
|
|
|
|
let mut encoder = JpegEncoder::new_with_quality(&mut buffer, quality);
|
|
if let Err(e) = encoder.encode(
|
|
rgb.as_raw(),
|
|
rgb.width(),
|
|
rgb.height(),
|
|
image::ExtendedColorType::Rgb8,
|
|
) {
|
|
eprintln!("[CAPTURE] Erreur encodage JPEG : {}", e);
|
|
return String::new();
|
|
}
|
|
|
|
base64::engine::general_purpose::STANDARD.encode(buffer.into_inner())
|
|
}
|
|
|
|
/// Encode une image en JPEG et retourne les bytes bruts.
|
|
pub fn screenshot_to_jpeg_bytes(img: &DynamicImage, quality: u8) -> Vec<u8> {
|
|
let rgb = img.to_rgb8();
|
|
let mut buffer = Cursor::new(Vec::new());
|
|
|
|
let mut encoder = JpegEncoder::new_with_quality(&mut buffer, quality);
|
|
if let Err(e) = encoder.encode(
|
|
rgb.as_raw(),
|
|
rgb.width(),
|
|
rgb.height(),
|
|
image::ExtendedColorType::Rgb8,
|
|
) {
|
|
eprintln!("[CAPTURE] Erreur encodage JPEG : {}", e);
|
|
return Vec::new();
|
|
}
|
|
|
|
buffer.into_inner()
|
|
}
|
|
|
|
/// Calcule un hash perceptuel rapide pour la déduplication.
|
|
///
|
|
/// Réduit l'image à 16x16 en niveaux de gris, puis calcule
|
|
/// un hash simple basé sur les pixels. Identique à la logique
|
|
/// Python (_quick_hash) dans agent_v1.
|
|
pub fn image_hash(img: &DynamicImage) -> u64 {
|
|
let small = img.resize_exact(16, 16, image::imageops::FilterType::Nearest);
|
|
let gray = small.to_luma8();
|
|
|
|
// Hash FNV-1a simple sur les pixels (rapide, pas besoin de crypto)
|
|
let mut hash: u64 = 0xcbf29ce484222325;
|
|
for pixel in gray.as_raw() {
|
|
hash ^= *pixel as u64;
|
|
hash = hash.wrapping_mul(0x100000001b3);
|
|
}
|
|
hash
|
|
}
|
|
|
|
/// Retourne les dimensions du moniteur principal (largeur, hauteur).
|
|
///
|
|
/// xcap utilise DXGI sur Windows qui retourne toujours les pixels physiques,
|
|
/// independamment du DPI awareness. Ceci est coherent avec les coordonnees
|
|
/// physiques d'enigo quand le process est DPI-aware.
|
|
pub fn screen_dimensions() -> Option<(u32, u32)> {
|
|
let monitors = xcap::Monitor::all().ok()?;
|
|
let primary = monitors
|
|
.into_iter()
|
|
.find(|m| m.is_primary().unwrap_or(false))?;
|
|
let w = primary.width().ok()?;
|
|
let h = primary.height().ok()?;
|
|
Some((w, h))
|
|
}
|