diff --git a/DEPLOY.md b/DEPLOY.md index 7481a24..c751df4 100644 --- a/DEPLOY.md +++ b/DEPLOY.md @@ -247,6 +247,7 @@ La rotation des jetons se fait alors depuis le dashboard, agent par agent. | Rappel plein écran sans effet (Linux) | Session Wayland au lieu de X11, ou `DISPLAY` inaccessible au service. Le diagnostic signale les deux | | Rappel plein écran sans effet (Windows) | La tâche ne tourne pas dans la session interactive : vérifie que le compte de `-RunAsUser` est bien celui ouvert sur la VM | | Agent en double après réinstallation | `agent.config.json` avait été supprimé : l'agent s'est ré-enrôlé. Supprime le doublon dans le dashboard | +| Firefox se ferme et rouvre à chaque mise à jour de l'agent (Linux) | `KillMode=process` manquant dans l'unité systemd — un agent installé avant son ajout garde l'ancienne unité. Relance `install-agent.sh` (même options, l'identité est conservée) pour la régénérer | | Workflow : *unauthorized* au push | `REGISTERYKEY` sans la portée **package**, ou expiré | ### Journaux diff --git a/deploy/install-agent.sh b/deploy/install-agent.sh index 65cd65c..f877cf7 100644 --- a/deploy/install-agent.sh +++ b/deploy/install-agent.sh @@ -275,6 +275,17 @@ Environment=AGENT_CONFIG=$CONFIG_FILE ExecStart=$(command -v node) $INSTALL_DIR/agent.cjs Restart=always RestartSec=5 +# Sans ceci, systemd tue par défaut tout le cgroup du service à chaque +# redémarrage — y compris Firefox, pourtant lancé "detached" par l'agent +# justement pour lui survivre (packages/agent/src/firefox.ts). "detached" +# côté Node ne fait que le sortir du groupe de processus ; ça ne le sort pas +# du cgroup que systemd surveille, lui. Résultat sans ce réglage : chaque +# "agent.update" (donc chaque process.exit(0) suivi du Restart=always) ferme +# la fenêtre que l'agent au redémarrage aurait dû retrouver via son +# rattachement au port de pilotage, et une nouvelle repart de zéro à la place. +# KillMode=process limite l'arrêt au seul PID suivi par systemd (celui de +# node) et laisse Firefox intact. +KillMode=process [Install] WantedBy=multi-user.target diff --git a/packages/agent/src/firefox.ts b/packages/agent/src/firefox.ts index e6a0ead..2a65627 100644 --- a/packages/agent/src/firefox.ts +++ b/packages/agent/src/firefox.ts @@ -261,6 +261,16 @@ export class FirefoxController { // Referme le menu sans rien changer. await this.pressEscape(); } + + // Le curseur reste sinon posé sur le bouton ou l'option cliquée : la barre + // de contrôles du lecteur, elle, se garde visible tant qu'une souris réelle + // la survole — un comportement voulu pour ne pas la faire disparaître sous + // le curseur d'un vrai spectateur, mais qui la laisse affichée en + // permanence ici puisque rien ne bouge plus ensuite. Un coin sûr : la barre + // du haut du site (pas celle du lecteur), toujours visible d'après le + // réglage plein écran plus haut, donc jamais partie du lecteur. + await this.moveAway(); + await this.send('input.releaseActions', { context: this.context }, 5000).catch(() => undefined); return { ok: true, label: best.label, already: best.active }; @@ -387,8 +397,13 @@ export class FirefoxController { let launched = false; this.starting = (async () => { // Rattachement d'abord : après un redémarrage de l'agent, la fenêtre est - // toujours là — et c'est celle qu'OBS capture. - if (await this.attach(2000)) return; + // toujours là — et c'est celle qu'OBS capture. Généreux à dessein : un + // faux négatif ici (le port répond, mais pas assez vite) déclenche + // launch(), qui tue le profil verrouillé — donc la fenêtre qu'on + // cherchait justement à retrouver. Un redémarrage de l'agent (mise à + // jour comprise) tombe souvent pendant que la VM est encore occupée à + // relancer Node ; mieux vaut patienter que rouvrir Firefox pour rien. + if (await this.attach(8000)) return; launched = true; await this.launch(); })(); @@ -638,6 +653,25 @@ export class FirefoxController { await delay(150); } + /** Déplace le curseur vers un coin hors du lecteur, sans cliquer. */ + private async moveAway(): Promise { + await this.send( + 'input.performActions', + { + context: this.context, + actions: [ + { + type: 'pointer', + id: 'stream-control-pointer', + parameters: { pointerType: 'mouse' }, + actions: [{ type: 'pointerMove', x: 2, y: 2, origin: 'viewport' }], + }, + ], + }, + 5000, + ).catch(() => undefined); + } + private async pressEscape(): Promise { await this.send( 'input.performActions',