fix: harden browser provider activation

This commit is contained in:
Sinabina
2026-07-21 12:13:33 -07:00
parent e3effb3fc4
commit a84a6e233d
65 changed files with 1203 additions and 171 deletions
+23 -8
View File
@@ -143,7 +143,11 @@ export async function runDoctor(options = {}) {
add("specialist-tool:python", python.ok ? "pass" : "warn", python.message, python.details);
const selected = new Set(Array.isArray(manifest?.selectedCapabilities) ? manifest.selectedCapabilities : []);
const launchers = manifest?.capabilities ?? {};
for (const capability of OPTIONAL_RUNTIME_CAPABILITIES) {
const capabilitiesToInspect = [
...OPTIONAL_RUNTIME_CAPABILITIES,
...(selected.has("browser-visible") ? ["browser-visible"] : []),
];
for (const capability of capabilitiesToInspect) {
if (!selected.has(capability)) {
add(`capability:${capability}`, "warn", "not selected");
continue;
@@ -158,8 +162,11 @@ export async function runDoctor(options = {}) {
add(`capability:${capability}`, "fail", "selected but required launcher metadata is missing");
continue;
}
if (capability === "browser") {
const browser = runtimeConfig?.browser?.provider === "installed"
if (capability === "browser" || capability === "browser-visible") {
const visible = capability === "browser-visible";
const browser = visible && runtimeConfig?.browser?.provider !== "managed"
? { ok: false, message: "visible GStack Browser requires the managed Chromium provider" }
: runtimeConfig?.browser?.provider === "installed"
? await inspectInstalledChromium(
activeRoot,
options.nodeCommand ?? process.env.GSTACK_NODE ?? "node",
@@ -167,7 +174,11 @@ export async function runDoctor(options = {}) {
options,
)
: runtimeConfig?.browser?.provider === "managed"
? await inspectManagedChromium(activeRoot, options.nodeCommand ?? process.env.GSTACK_NODE ?? "node")
? await inspectManagedChromium(
activeRoot,
options.nodeCommand ?? process.env.GSTACK_NODE ?? "node",
{ visible },
)
: { ok: false, message: "browser capability is installed, but no browser provider was explicitly selected" };
add(`capability:${capability}`, browser.ok ? "pass" : "fail", browser.message, browser.details);
continue;
@@ -245,7 +256,7 @@ async function inspectPython(env) {
return { ok: false, message: "Python 3 is absent; only specialist flows that explicitly request it are unavailable" };
}
async function inspectManagedChromium(activeRoot, nodeCommand) {
async function inspectManagedChromium(activeRoot, nodeCommand, options = {}) {
const browserRoot = path.join(activeRoot, ".gstack-runtime-browsers");
const modulePath = path.join(activeRoot, "node_modules", "playwright", "index.mjs");
const [browserStat, moduleStat] = await Promise.all([
@@ -260,11 +271,15 @@ async function inspectManagedChromium(activeRoot, nodeCommand) {
const result = await captureCommand(nodeCommand, [
"--input-type=module",
"--eval",
`const { chromium } = await import(${JSON.stringify(moduleUrl)}); const browser = await chromium.launch({ headless: true }); try { process.stdout.write(browser.version()); } finally { await browser.close(); }`,
`const { chromium } = await import(${JSON.stringify(moduleUrl)}); const browser = await chromium.launch(${options.visible ? '{ headless: true, channel: "chromium" }' : "{ headless: true }"}); try { process.stdout.write(browser.version()); } finally { await browser.close(); }`,
], { env: { ...process.env, PLAYWRIGHT_BROWSERS_PATH: browserRoot } });
const version = result.stdout.trim();
if (!version) return { ok: false, message: "managed Chromium launched without reporting a browser version" };
return { ok: true, message: `managed headless Chromium ${version} launches and exits cleanly`, details: { browserRoot, version } };
return {
ok: true,
message: `managed ${options.visible ? "visible-capable" : "headless"} Chromium ${version} launches and exits cleanly`,
details: { browserRoot, version },
};
} catch (error) {
return { ok: false, message: `managed Chromium is not runnable: ${error.message}` };
}
@@ -311,7 +326,7 @@ async function inspectXcrun() {
}
function capabilityLaunchersReady(capability, launchers) {
if (capability === "browser") return typeof launchers.browse === "string";
if (capability === "browser" || capability === "browser-visible") return typeof launchers.browse === "string";
if (capability === "design") return typeof launchers["gstack-design"] === "string";
if (capability === "pdf") return typeof launchers["make-pdf"] === "string";
if (capability === "diagram") return true;