REVAMP : Firefox handling
All checks were successful
release / build (push) Successful in 25s
release / verify-windows (push) Successful in 1m15s

This commit is contained in:
jeanotx32
2026-08-12 01:46:14 +02:00
parent 3bd4d5f2ba
commit 77bfa0b8c5
14 changed files with 1223 additions and 108 deletions

View File

@@ -377,14 +377,27 @@ export interface WatchSettings {
*/
export interface BrowserSettings {
enabled: boolean;
/**
* Comment l'agent obtient la page.
*
* `launch` relance l'exécutable à chaque fois et lui confie l'URL : simple,
* mais l'agent ne sait rien de ce qui se passe ensuite, et le plein écran
* dépend alors de xdotool — donc d'une session X11.
*
* `bidi` ouvre Firefox une fois et garde un canal WebDriver BiDi. L'agent
* navigue, déclenche le plein écran et le *vérifie* dans la page, sans passer
* par le serveur d'affichage : c'est le seul mode qui fonctionne en session
* Wayland native.
*/
mode: BrowserControlMode;
/** Exécutable du navigateur. */
command: string;
/**
* Arguments placés avant l'URL. Vide par défaut, et ce n'est pas un oubli :
* `--new-window` créerait une fenêtre neuve à chaque capture, avec un nouvel
* identifiant X11 — la source « capture de fenêtre » d'OBS perdrait sa cible
* et il faudrait la repointer à la main. Sans argument, Firefox confie l'URL à
* la fenêtre déjà ouverte, qu'OBS continue de capturer.
* Arguments placés avant l'URL (mode `launch` seulement). Vide par défaut, et
* ce n'est pas un oubli : `--new-window` créerait une fenêtre neuve à chaque
* capture, avec un nouvel identifiant X11 — la source « capture de fenêtre »
* d'OBS perdrait sa cible et il faudrait la repointer à la main. Sans
* argument, Firefox confie l'URL à la fenêtre déjà ouverte, qu'OBS capture.
*/
args: string[];
/** Délai avant l'envoi du plein écran, le temps que le lecteur démarre. */
@@ -398,26 +411,75 @@ export interface BrowserSettings {
* le flux tourner.
*/
onStop: BrowserStopAction;
/**
* Port local du « remote agent » de Firefox (mode `bidi`).
*
* Il n'écoute que sur la boucle locale et n'est ouvert que par l'instance que
* l'agent lance lui-même. Le rendre configurable permet de faire cohabiter
* plusieurs profils sur une même machine.
*/
remotePort: number;
/**
* Profil Firefox dédié (mode `bidi`). Vide : l'agent en gère un sous son
* répertoire de données.
*
* Un profil séparé est nécessaire, pas cosmétique : le remote agent ne
* s'active qu'au démarrage du processus, et deux instances ne peuvent pas
* partager un profil — pointer sur celui de l'opérateur donnerait
* « Firefox est déjà ouvert ».
*/
profileDir: string;
}
/**
* Ce que l'agent sait du navigateur qu'il pilote.
*
* Le mode `launch` ne permet rien de tel : on lance un processus et on espère.
* Ce retour est l'autre bénéfice de BiDi, à côté du plein écran — savoir que la
* page est bien celle attendue, sans aller regarder l'écran de la VM.
*/
export interface BrowserState {
/** Canal BiDi établi avec une instance vivante. */
connected: boolean;
/** URL de l'onglet piloté. */
url?: string;
/** Un élément de la page est en plein écran. */
fullscreen?: boolean;
/** Dernier échec de pilotage, effacé à la reconnexion. */
lastError?: string;
}
export const BROWSER_CONTROL_MODES = ['launch', 'bidi'] as const;
export type BrowserControlMode = (typeof BROWSER_CONTROL_MODES)[number];
export const BROWSER_STOP_ACTIONS = ['blank', 'keep', 'close'] as const;
export type BrowserStopAction = (typeof BROWSER_STOP_ACTIONS)[number];
export const DEFAULT_BROWSER_SETTINGS: BrowserSettings = {
enabled: false,
// `launch` reste le défaut : basculer un agent existant en `bidi` ouvre une
// nouvelle fenêtre Firefox, que la source OBS doit être repointée sur une
// fois. C'est un choix d'opérateur, pas un effet de bord de mise à jour.
mode: 'launch',
command: 'firefox',
args: [],
readyDelayMs: 8000,
onStop: 'blank',
remotePort: 9222,
profileDir: '',
};
export function normalizeBrowserSettings(raw: unknown): BrowserSettings {
const input = (raw ?? {}) as Partial<BrowserSettings>;
const base = DEFAULT_BROWSER_SETTINGS;
const delay = Number(input.readyDelayMs);
const port = Number(input.remotePort);
return {
enabled: input.enabled === true,
mode: (BROWSER_CONTROL_MODES as readonly string[]).includes(input.mode as string)
? (input.mode as BrowserControlMode)
: base.mode,
command:
typeof input.command === 'string' && input.command.trim()
? input.command.trim()
@@ -429,6 +491,12 @@ export function normalizeBrowserSettings(raw: unknown): BrowserSettings {
? Math.min(Math.max(Math.round(delay), 0), 120_000)
: base.readyDelayMs,
onStop: readStopAction(raw),
// Bornes hautes des ports non privilégiés : sous 1024, l'agent tourne sans
// droit de liaison et Firefox échouerait au démarrage.
remotePort: Number.isFinite(port)
? Math.min(Math.max(Math.round(port), 1024), 65_535)
: base.remotePort,
profileDir: typeof input.profileDir === 'string' ? input.profileDir.trim() : base.profileDir,
};
}
@@ -560,6 +628,9 @@ export interface AgentStatus {
/** Surveillance du stream source, absente si elle n'est pas configurée. */
watch?: WatchState;
/** Navigateur piloté, absent hors du mode `bidi`. */
browser?: BrowserState;
/**
* Empreinte courte du binaire en cours d'exécution. Deux agents partageant
* cette valeur tournent sur le même build — c'est ce qui rend visible un