From 771dd22f95951767c238fbb3801e3246d04f01ea Mon Sep 17 00:00:00 2001 From: Mateusz Kupczyk Date: Tue, 1 Sep 2026 13:51:37 +0200 Subject: [PATCH] fix(bridge): match headed mode on bridge discover --- src/bridge.ts | 1 + src/client.ts | 6 ++++-- src/identity.ts | 4 ++++ test/bridge-lifecycle.test.ts | 2 ++ test/identity.test.ts | 1 + 5 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/bridge.ts b/src/bridge.ts index 36244d9..7c7ac76 100644 --- a/src/bridge.ts +++ b/src/bridge.ts @@ -429,6 +429,7 @@ export function buildHealth(connected: boolean): BridgeHealth { startedAt: STARTED_AT, bootMinute: BOOT_MINUTE, browser: { connected }, + headed: shouldRunHeaded(), }; } diff --git a/src/client.ts b/src/client.ts index d137e4d..374a2b3 100644 --- a/src/client.ts +++ b/src/client.ts @@ -33,6 +33,7 @@ import { request } from "node:http"; import { AxiError } from "axi-sdk-js"; import { resolveBridgeLauncher, + shouldRunHeaded, type LastSnapshotCache, } from "./bridge.js"; import { @@ -378,19 +379,20 @@ async function probeAll(ports: number[]): Promise { */ export async function findUsableBridge(ports: number[]): Promise { const version = getPackageVersion(); + const wantHeaded = shouldRunHeaded(); // Fast path: the port in the PID file is nearly always the answer, and // checking it alone keeps the common case to a single round trip. const preferred = readPidFile()?.port; if (preferred !== undefined && ports.includes(preferred)) { const health = await probeHealth(preferred); - if (isUsableBridge(health, version)) return preferred; + if (isUsableBridge(health, version) && health.headed === wantHeaded) return preferred; if (isOurBridge(health)) await shutdownBridgeOnPort(preferred, health); } const probes = await probeAll(ports.filter((p) => p !== preferred)); for (const { port, health } of probes) { - if (isUsableBridge(health, version)) return port; + if (isUsableBridge(health, version) && health.headed === wantHeaded) return port; } for (const { port, health } of probes) { if (isOurBridge(health)) await shutdownBridgeOnPort(port, health); diff --git a/src/identity.ts b/src/identity.ts index 7b19428..dd58ec0 100644 --- a/src/identity.ts +++ b/src/identity.ts @@ -31,6 +31,8 @@ export interface BridgeHealth { startedAt: number; bootMinute: number; browser: { connected: boolean }; + /** Whether the bridge launched the browser in headed (visible) mode. */ + headed: boolean; } /** @@ -107,5 +109,7 @@ export function parseHealth(body: string): BridgeHealth | null { bootMinute: typeof record.bootMinute === "number" ? record.bootMinute : Number.NaN, browser: { connected: browser?.connected === true }, + // Pre-0.2.7 bridges omit headed; they launched headless by default. + headed: typeof record.headed === "boolean" ? record.headed : false, }; } diff --git a/test/bridge-lifecycle.test.ts b/test/bridge-lifecycle.test.ts index 3470b1a..a8d5dd8 100644 --- a/test/bridge-lifecycle.test.ts +++ b/test/bridge-lifecycle.test.ts @@ -15,6 +15,7 @@ import { join } from "node:path"; import { spawn, type ChildProcess } from "node:child_process"; import { BRIDGE_SERVER_NAME, computeBootMinute } from "../src/identity.js"; import { getPackageVersion } from "../src/version.js"; +import { shouldRunHeaded } from "../src/bridge.js"; type ClientModule = typeof import("../src/client.js"); @@ -47,6 +48,7 @@ function bridgeHealth(overrides: Record = {}): Record = {}): BridgeHealth { startedAt: 1_700_000_000_000, bootMinute: 28_000_000, browser: { connected: true }, + headed: true, ...overrides, }; }