feat(vwb): load palette from catalog

This commit is contained in:
Dom
2026-05-29 17:09:47 +02:00
parent 02211fddf2
commit 45b6da5e3f
6 changed files with 379 additions and 7 deletions

View File

@@ -1090,6 +1090,13 @@ def _load_lea_competence_actions() -> List[Dict[str, Any]]:
return []
def _truthy_query_arg(name: str) -> bool:
"""Return True for explicit opt-in query flags."""
return str(request.args.get(name, "")).strip().lower() in (
"1", "true", "yes", "on",
)
def _extract_competence_id(action_type: str, parameters: Dict[str, Any]) -> str:
competence_id = str(parameters.get("competence_id") or "").strip()
if competence_id:
@@ -1334,6 +1341,7 @@ def list_actions():
# Paramètres de requête
category_filter = request.args.get('category')
search_query = request.args.get('search', '').lower()
show_competences = _truthy_query_arg('show_competences')
# Définir les actions disponibles
available_actions = [
@@ -1443,6 +1451,36 @@ def list_actions():
}
]
},
{
"id": "keyboard_shortcut",
"name": "Raccourci Clavier",
"description": "Exécute une combinaison de touches normalisée",
"category": "keyboard",
"icon": "",
"parameters": {
"keys": {
"type": "array",
"required": True,
"description": "Touches à envoyer dans l'ordre, ex: ['ctrl', 's']"
},
"hold_duration_ms": {
"type": "number",
"required": False,
"default": 50,
"min": 0,
"description": "Durée de maintien optionnelle en millisecondes"
}
},
"examples": [
{
"name": "Ouvrir Exécuter",
"description": "Envoie Win+R pour ouvrir la boîte Exécuter",
"parameters": {
"keys": ["win", "r"]
}
}
]
},
{
"id": "wait_for_anchor",
"name": "Attente d'Ancre Visuelle",
@@ -1488,6 +1526,54 @@ def list_actions():
}
]
},
{
"id": "wait_for_state",
"name": "Attendre un État",
"description": "Attend un état sémantique d'écran, comme une fenêtre active ou un processus cible",
"category": "wait",
"icon": "⏱️",
"parameters": {
"expected_state": {
"type": "object",
"required": True,
"description": "Critères d'état attendus: window_title_in, window_title_contains, process_active"
},
"timeout_ms": {
"type": "number",
"required": False,
"default": 5000,
"min": 100,
"description": "Délai d'attente maximum en millisecondes"
},
"poll_interval_ms": {
"type": "number",
"required": False,
"default": 250,
"min": 50,
"description": "Intervalle de vérification en millisecondes"
},
"evidence_required": {
"type": "string",
"required": False,
"default": "window_or_process",
"options": ["window_or_process", "uia", "ocr", "screenshot_diff"],
"description": "Niveau de preuve requis"
}
},
"examples": [
{
"name": "Attendre Exécuter",
"description": "Attend que la fenêtre Exécuter soit au premier plan",
"parameters": {
"expected_state": {
"window_title_in": ["Exécuter"],
"process_active": "explorer.exe"
},
"timeout_ms": 5000
}
}
]
},
{
"id": "focus_anchor",
"name": "Donner le Focus à un Élément",
@@ -1533,6 +1619,42 @@ def list_actions():
}
]
},
{
"id": "pause_for_human",
"name": "Pause Supervisée",
"description": "Suspend l'exécution et demande une validation humaine avant de continuer",
"category": "logic",
"icon": "⏸️",
"parameters": {
"message": {
"type": "string",
"required": True,
"description": "Message affiché à l'opérateur"
},
"safety_level": {
"type": "string",
"required": False,
"default": "standard",
"options": ["standard", "medical_critical"],
"description": "Niveau de sécurité de la pause"
},
"safety_checks": {
"type": "array",
"required": False,
"default": [],
"description": "Liste de contrôles à acquitter avant reprise"
}
},
"examples": [
{
"name": "Validation humaine",
"description": "Demande à l'opérateur de valider l'état visible",
"parameters": {
"message": "Validez-vous que l'état attendu est visible ?"
}
}
]
},
{
"id": "type_secret",
"name": "Saisie Sécurisée",
@@ -1688,8 +1810,11 @@ def list_actions():
}
]
# Ajouter les compétences YAML Léa comme actions testables.
available_actions.extend(_load_lea_competence_actions())
# Les compétences Léa sont masquées par défaut : elles ne doivent pas
# ressembler à des actions ordinaires tant que le replay supervisé
# complet (verdict + write-back YAML) n'est pas branché.
if show_competences:
available_actions.extend(_load_lea_competence_actions())
# Filtrer par catégorie
if category_filter:
@@ -2879,7 +3004,8 @@ def list_categories():
}
"""
try:
competence_action_count = len(_load_lea_competence_actions())
show_competences = _truthy_query_arg('show_competences')
competence_action_count = len(_load_lea_competence_actions()) if show_competences else 0
# Définir les catégories disponibles avec métadonnées
available_categories = [
@@ -2892,6 +3018,15 @@ def list_categories():
"color": "#2196f3",
"isEnabled": True
},
{
"id": "keyboard",
"name": "Clavier",
"description": "Raccourcis clavier et actions de saisie",
"icon": "⌨️",
"actionCount": 1,
"color": "#607d8b",
"isEnabled": True
},
{
"id": "control",
"name": "Contrôle de Flux",
@@ -2901,6 +3036,24 @@ def list_categories():
"color": "#ff9800",
"isEnabled": True
},
{
"id": "wait",
"name": "Attente d'État",
"description": "Attentes sémantiques d'écran et de processus",
"icon": "⏱️",
"actionCount": 1,
"color": "#795548",
"isEnabled": True
},
{
"id": "logic",
"name": "Logique",
"description": "Pauses supervisées et contrôle humain",
"icon": "⏸️",
"actionCount": 1,
"color": "#9c27b0",
"isEnabled": True
},
{
"id": "data",
"name": "Données",
@@ -2962,7 +3115,7 @@ def catalog_health():
services_status = {
"screen_capturer": screen_capturer is not None,
"actions": 7 + competence_action_count,
"actions": 10,
"lea_competences": competence_action_count,
"screen_capturer_method": getattr(screen_capturer, 'method', 'unavailable') if screen_capturer else 'unavailable'
}