From 066a1d8d40dbfac945afbea7b00afbfa29be01e0 Mon Sep 17 00:00:00 2001 From: kokokoXUY <13682395396@163.com> Date: Sun, 27 Sep 2026 10:39:38 +0800 Subject: [PATCH] fix(smoke): find the Windows headless shell in the browser smoke launcher `launchBrowser` could not start on Windows. Playwright ships the shell as `chrome-headless-shell.exe` there, and the launcher requires the bare name in two places: the candidate check compares `basename(candidate)` to `"chrome-headless-shell"`, and `findChromeHeadlessShell` matches the entry name exactly. It also searches only `~/.cache/hyperframes/chrome`, `~/.cache/ms-playwright` and `~/Library/Caches/ms-playwright`, while Windows installs the browser cache under `%LOCALAPPDATA%\ms-playwright`. The result is `chrome-headless-shell not found; set LOOPX_CHROME_HEADLESS_SHELL` on a machine where the shell is installed and the variable is not set. Accept both names through one shared set, and add the Windows cache root when `LOCALAPPDATA` is present. The POSIX roots and their order are unchanged, so a machine that already resolved a shell keeps the same choice. Validation: on Windows, `LOOPX_PERSONAL_WORKSPACE_SCENARIO=steward-model-settings LOOPX_PLAYWRIGHT_PACKAGE="$PWD/node_modules/playwright" node ../../../examples/personal-workspace-browser-smoke.mjs` from the dashboard package -> `ok`, after `npx playwright install chromium-headless-shell` and with `LOOPX_CHROME_HEADLESS_SHELL` unset. Before this change the same command failed with `Error: chrome-headless-shell not found`. Signed-off-by: kokokoXUY <13682395396@163.com> --- examples/dashboard-browser-smoke-support.mjs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/examples/dashboard-browser-smoke-support.mjs b/examples/dashboard-browser-smoke-support.mjs index 94468acc4d..f0a4b14e7f 100644 --- a/examples/dashboard-browser-smoke-support.mjs +++ b/examples/dashboard-browser-smoke-support.mjs @@ -34,6 +34,10 @@ export function loadPlaywright() { throw new Error("Playwright package not found; install playwright or set LOOPX_PLAYWRIGHT_PACKAGE"); } +// Playwright names the shell `chrome-headless-shell` on POSIX and +// `chrome-headless-shell.exe` on Windows. +const CHROME_HEADLESS_SHELL_NAMES = new Set(["chrome-headless-shell", "chrome-headless-shell.exe"]); + function findChromeHeadlessShell(root) { if (!existsSync(root)) { return null; @@ -46,7 +50,7 @@ function findChromeHeadlessShell(root) { const path = resolve(current, entry.name); if (entry.isDirectory()) { pending.push(path); - } else if (entry.isFile() && entry.name === "chrome-headless-shell") { + } else if (entry.isFile() && CHROME_HEADLESS_SHELL_NAMES.has(entry.name)) { matches.push(path); } } @@ -58,10 +62,14 @@ export async function launchBrowser(chromium) { const configuredPath = process.env.LOOPX_CHROME_HEADLESS_SHELL; const playwrightPath = chromium.executablePath(); const executablePath = [configuredPath, playwrightPath] - .find((candidate) => candidate && basename(candidate) === "chrome-headless-shell" && existsSync(candidate)) + .find((candidate) => candidate && CHROME_HEADLESS_SHELL_NAMES.has(basename(candidate)) && existsSync(candidate)) ?? findChromeHeadlessShell(resolve(homedir(), ".cache/hyperframes/chrome")) ?? findChromeHeadlessShell(resolve(homedir(), ".cache/ms-playwright")) - ?? findChromeHeadlessShell(resolve(homedir(), "Library/Caches/ms-playwright")); + ?? findChromeHeadlessShell(resolve(homedir(), "Library/Caches/ms-playwright")) + // Windows installs the browser cache under %LOCALAPPDATA% instead. + ?? (process.env.LOCALAPPDATA + ? findChromeHeadlessShell(resolve(process.env.LOCALAPPDATA, "ms-playwright")) + : null); if (!executablePath) { throw new Error("chrome-headless-shell not found; set LOOPX_CHROME_HEADLESS_SHELL"); }