browser: add Windows Chrome executable detection

This commit is contained in:
Mourad Boustani
2026-01-08 02:22:06 +01:00
committed by Peter Steinberger
parent 92a62bc300
commit 7294ba037d
2 changed files with 97 additions and 1 deletions

View File

@@ -8,6 +8,7 @@ import { afterEach, describe, expect, it, vi } from "vitest";
import {
decorateClawdProfile,
findChromeExecutableMac,
findChromeExecutableWindows,
isChromeReachable,
stopClawdChrome,
} from "./chrome.js";
@@ -155,6 +156,34 @@ describe("browser chrome helpers", () => {
exists.mockRestore();
});
it("picks the first existing Chrome candidate on Windows", () => {
const exists = vi
.spyOn(fs, "existsSync")
.mockImplementation((p) => String(p).includes("Chrome SxS"));
const exe = findChromeExecutableWindows();
expect(exe?.kind).toBe("canary");
expect(exe?.path).toMatch(/Chrome SxS/);
exists.mockRestore();
});
it("finds Chrome in Program Files on Windows", () => {
// Use path.join to match how the function builds paths (cross-platform)
const marker = path.join("Program Files", "Google", "Chrome");
const exists = vi
.spyOn(fs, "existsSync")
.mockImplementation((p) => String(p).includes(marker));
const exe = findChromeExecutableWindows();
expect(exe?.kind).toBe("chrome");
expect(exe?.path).toMatch(/chrome\.exe$/);
exists.mockRestore();
});
it("returns null when no Chrome candidate exists on Windows", () => {
const exists = vi.spyOn(fs, "existsSync").mockReturnValue(false);
expect(findChromeExecutableWindows()).toBeNull();
exists.mockRestore();
});
it("reports reachability based on /json/version", async () => {
vi.stubGlobal(
"fetch",

View File

@@ -102,6 +102,72 @@ export function findChromeExecutableLinux(): BrowserExecutable | null {
return null;
}
export function findChromeExecutableWindows(): BrowserExecutable | null {
const localAppData = process.env.LOCALAPPDATA ?? "";
const programFiles = process.env.ProgramFiles ?? "C:\\Program Files";
// Must use bracket notation: variable name contains parentheses
const programFilesX86 =
process.env["ProgramFiles(x86)"] ?? "C:\\Program Files (x86)";
const candidates: Array<BrowserExecutable> = [
// Chrome Canary (user install)
{
kind: "canary",
path: path.join(
localAppData,
"Google",
"Chrome SxS",
"Application",
"chrome.exe",
),
},
// Chromium (user install)
{
kind: "chromium",
path: path.join(localAppData, "Chromium", "Application", "chrome.exe"),
},
// Chrome (user install)
{
kind: "chrome",
path: path.join(
localAppData,
"Google",
"Chrome",
"Application",
"chrome.exe",
),
},
// Chrome (system install, 64-bit)
{
kind: "chrome",
path: path.join(
programFiles,
"Google",
"Chrome",
"Application",
"chrome.exe",
),
},
// Chrome (system install, 32-bit on 64-bit Windows)
{
kind: "chrome",
path: path.join(
programFilesX86,
"Google",
"Chrome",
"Application",
"chrome.exe",
),
},
];
for (const candidate of candidates) {
if (exists(candidate.path)) return candidate;
}
return null;
}
function resolveBrowserExecutable(
resolved: ResolvedBrowserConfig,
): BrowserExecutable | null {
@@ -116,6 +182,7 @@ function resolveBrowserExecutable(
if (process.platform === "darwin") return findChromeExecutableMac();
if (process.platform === "linux") return findChromeExecutableLinux();
if (process.platform === "win32") return findChromeExecutableWindows();
return null;
}
@@ -445,7 +512,7 @@ export async function launchClawdChrome(
const exe = resolveBrowserExecutable(resolved);
if (!exe) {
throw new Error(
"No supported browser found (Chrome/Chromium on macOS or Linux).",
"No supported browser found (Chrome/Chromium on macOS, Linux, or Windows).",
);
}