Merge PR #7604: fix(browser): honor explicit Chromium executable override (@roboomp)

This commit is contained in:
can1357
2026-08-05 01:11:57 +02:00
4 changed files with 50 additions and 5 deletions
+3
View File
@@ -21,6 +21,9 @@
### Fixed
- Fixed legacy Pi extension validation rejecting plugins such as `remote-pi` that import the package-root `convertToPng` image helper. ([#7610](https://github.com/can1357/oh-my-pi/issues/7610))
### 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
@@ -121,9 +121,9 @@ async function loadBrowsers(): Promise<typeof BrowsersNs> {
}
/**
* 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<typeof BrowsersNs> {
*/
let chromiumExecutablePromise: Promise<string | undefined> | undefined;
export async function ensureChromiumExecutable(): Promise<string | undefined> {
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 () => {
@@ -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 ?? "");
@@ -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();
}
});
});