fix(vwb): execute-windows route vers la machine la plus active (pas alphabétique)
Quand le frontend ne passe pas de machine_id explicite, le backend VWB auto-sélectionne une machine Windows en interrogeant /api/v1/traces/ stream/machines. Le code prenait la première de la liste sans tri, donc l'ordre dépendait de l'ordre arbitraire renvoyé par le streaming server. Conséquence reproduite ce 2026-05-06 : un replay du workflow Urgence a été dispatché vers DESKTOP-ST3VBSD_windows alors que l'agent V1 actif polait depuis DESKTOP-58D5CAC_windows. /replay/next ne dispatchait aucune action puisque state.machine_id != polling_machine_id. Symptôme côté Dom : "rien ne se passe sur Windows". Correction : tri explicite par last_activity desc avant sélection. La machine retenue est désormais celle qui a heartbeaté le plus récemment (= celle qui POLLE actuellement le serveur). Le workflow.machine_id (machine d'origine d'enregistrement) reste distinct de la cible d'exécution : un workflow enregistré sur PC A peut être rejoué sur PC B grâce au pipeline 100% visuel qui recalcule anchors et coordonnées selon la résolution courante. C'était la vraie intention architecturale, masquée par le bug de tri.
This commit is contained in:
@@ -1082,8 +1082,13 @@ def execute_windows():
|
|||||||
if not data.get('session_id'):
|
if not data.get('session_id'):
|
||||||
data['session_id'] = 'agent_demo_user'
|
data['session_id'] = 'agent_demo_user'
|
||||||
|
|
||||||
# Injecter le machine_id pour le ciblage multi-machine
|
# Injecter le machine_id pour le ciblage multi-machine.
|
||||||
# Chercher la première machine Windows connectée si pas spécifié
|
# Cibler la machine Windows la plus récemment active (heartbeat last_activity)
|
||||||
|
# plutôt que la première dans l'ordre arbitraire renvoyé par /machines :
|
||||||
|
# un workflow enregistré sur PC A doit pouvoir être rejoué sur PC B (vision
|
||||||
|
# 100 % visuelle, recalcul anchors+coords selon la résolution courante).
|
||||||
|
# Le workflow.machine_id signale l'origine d'enregistrement, pas la cible
|
||||||
|
# d'exécution — la cible doit être l'agent qui POLLE actuellement.
|
||||||
if 'machine_id' not in data or not data.get('machine_id'):
|
if 'machine_id' not in data or not data.get('machine_id'):
|
||||||
try:
|
try:
|
||||||
machines_resp = req.get(
|
machines_resp = req.get(
|
||||||
@@ -1093,11 +1098,19 @@ def execute_windows():
|
|||||||
)
|
)
|
||||||
if machines_resp.ok:
|
if machines_resp.ok:
|
||||||
machines = machines_resp.json().get('machines', [])
|
machines = machines_resp.json().get('machines', [])
|
||||||
for m in machines:
|
# Filtrer Windows + non default, trier par last_activity desc
|
||||||
mid = m.get('machine_id', '')
|
windows_machines = [
|
||||||
if mid and mid != 'default' and 'windows' in mid.lower():
|
m for m in machines
|
||||||
data['machine_id'] = mid
|
if m.get('machine_id')
|
||||||
break
|
and m['machine_id'] != 'default'
|
||||||
|
and 'windows' in m['machine_id'].lower()
|
||||||
|
]
|
||||||
|
windows_machines.sort(
|
||||||
|
key=lambda m: m.get('last_activity', ''),
|
||||||
|
reverse=True,
|
||||||
|
)
|
||||||
|
if windows_machines:
|
||||||
|
data['machine_id'] = windows_machines[0]['machine_id']
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user