From 62cb7a0e629308c1061eb2c3cd9dab37a5e87086 Mon Sep 17 00:00:00 2001 From: roboomp Date: Tue, 4 Aug 2026 06:37:29 +0000 Subject: [PATCH] fix(browser): honored explicit Chromium executable override Selected PUPPETEER_EXECUTABLE_PATH before probing system browser installations so compatible headless-shell binaries remain usable by the shared daemon. Added an isolated Windows candidate-selection regression probe. Fixes #7601 --- packages/coding-agent/CHANGELOG.md | 4 +++ .../coding-agent/src/tools/browser/launch.ts | 10 +++--- .../test/fixtures/browser-executable-probe.ts | 7 ++++ .../test/tools/browser-launch.test.ts | 35 +++++++++++++++++++ 4 files changed, 51 insertions(+), 5 deletions(-) create mode 100644 packages/coding-agent/test/fixtures/browser-executable-probe.ts diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 84a743e43..8ca023f59 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Fixed `PUPPETEER_EXECUTABLE_PATH` being ignored when a system Chrome installation was detected, preventing Windows users from selecting a compatible headless browser for the shared browser daemon ([#7601](https://github.com/can1357/oh-my-pi/issues/7601)). + ## [17.2.7] - 2026-08-03 ### Changed diff --git a/packages/coding-agent/src/tools/browser/launch.ts b/packages/coding-agent/src/tools/browser/launch.ts index c3bb19b05..755a559ec 100644 --- a/packages/coding-agent/src/tools/browser/launch.ts +++ b/packages/coding-agent/src/tools/browser/launch.ts @@ -121,9 +121,9 @@ async function loadBrowsers(): Promise { } /** - * Resolve the Chromium executable puppeteer will launch, lazily downloading it - * on first use via @puppeteer/browsers. Skipped when a system Chromium (NixOS) - * or PUPPETEER_EXECUTABLE_PATH is set. The browser is cached under + * Resolve the Chromium executable puppeteer will launch, honoring + * PUPPETEER_EXECUTABLE_PATH before system browser detection and lazily + * downloading Chromium otherwise. The browser is cached under * ~/.omp/puppeteer (getPuppeteerDir). Returns undefined when platform * detection fails (puppeteer default resolution takes over). Exported so * real-browser tests can probe launchability and skip on hosts missing @@ -131,10 +131,10 @@ async function loadBrowsers(): Promise { */ let chromiumExecutablePromise: Promise | undefined; export async function ensureChromiumExecutable(): Promise { - const sysChrome = resolveSystemChromium(); - if (sysChrome) return sysChrome; const envPath = process.env.PUPPETEER_EXECUTABLE_PATH; if (envPath) return envPath; + const sysChrome = resolveSystemChromium(); + if (sysChrome) return sysChrome; if (chromiumExecutablePromise) return chromiumExecutablePromise; chromiumExecutablePromise = (async () => { diff --git a/packages/coding-agent/test/fixtures/browser-executable-probe.ts b/packages/coding-agent/test/fixtures/browser-executable-probe.ts new file mode 100644 index 000000000..d0f5641e7 --- /dev/null +++ b/packages/coding-agent/test/fixtures/browser-executable-probe.ts @@ -0,0 +1,7 @@ +import { ensureChromiumExecutable } from "@oh-my-pi/pi-coding-agent/tools/browser/launch"; + +const platform = process.env.OMP_BROWSER_PROBE_PLATFORM; +if (platform) Object.defineProperty(process, "platform", { value: platform }); + +const executable = await ensureChromiumExecutable(); +process.stdout.write(executable ?? ""); diff --git a/packages/coding-agent/test/tools/browser-launch.test.ts b/packages/coding-agent/test/tools/browser-launch.test.ts index 1d677507f..66d716378 100644 --- a/packages/coding-agent/test/tools/browser-launch.test.ts +++ b/packages/coding-agent/test/tools/browser-launch.test.ts @@ -1,5 +1,9 @@ import { describe, expect, it } from "bun:test"; +import * as path from "node:path"; import { stealthIgnoreDefaultArgsForTest } from "@oh-my-pi/pi-coding-agent/tools/browser/launch"; +import { TempDir } from "@oh-my-pi/pi-utils"; + +const EXECUTABLE_PROBE = path.resolve(import.meta.dir, "../fixtures/browser-executable-probe.ts"); const AUTOMATION_FLAG = "--enable-automation"; @@ -33,3 +37,34 @@ describe("browser launch stealth defaults", () => { } }); }); + +describe("browser executable selection", () => { + it("honors PUPPETEER_EXECUTABLE_PATH before a detected Windows system Chrome", async () => { + const tempDir = TempDir.createSync("@browser-executable-"); + try { + const override = path.join(tempDir.path(), "chrome-headless-shell.exe"); + const systemChrome = path.join(tempDir.path(), "Google\\Chrome\\Application\\chrome.exe"); + await Bun.write(override, "override"); + await Bun.write(systemChrome, "system"); + + const result = Bun.spawnSync([process.execPath, EXECUTABLE_PROBE], { + env: { + ...process.env, + OMP_BROWSER_PROBE_PLATFORM: "win32", + ProgramFiles: tempDir.path(), + "ProgramFiles(x86)": path.join(tempDir.path(), "missing-x86"), + LOCALAPPDATA: path.join(tempDir.path(), "missing-local"), + PUPPETEER_EXECUTABLE_PATH: override, + }, + stdout: "pipe", + stderr: "pipe", + }); + const stderr = new TextDecoder().decode(result.stderr); + + expect(result.exitCode, stderr).toBe(0); + expect(new TextDecoder().decode(result.stdout)).toBe(override); + } finally { + await tempDir.remove(); + } + }); +});