From 5f9ee0b67829ac1430ae04be6011edb355ef4ac5 Mon Sep 17 00:00:00 2001 From: Vance Ingalls Date: Wed, 8 Jul 2026 15:10:18 -0700 Subject: [PATCH] fix(cli,engine): close review gaps in Chrome resolution fix Address PR #2082 review feedback: - Route studio thumbnail + render call sites through preferManagedChrome so studio renders no longer silently fall back to whatever system Chrome happens to be installed. - `hyperframes browser ensure` now resolves through the same preferManagedChrome path render uses, so it reports what render will actually pick instead of any system Chrome it happens to find. - Point the unsupported-Chrome fallback log at `browser ensure --force` instead of `doctor`, which doesn't check Chrome/drawElement capability. - Fix stale findFromCache comment: the HF pin is now a Dev-channel build that can be newer than a user's puppeteer-cache Stable install. --- packages/cli/src/browser/manager.ts | 10 +++-- packages/cli/src/commands/browser.ts | 40 +++++++++++++------- packages/cli/src/server/studioServer.ts | 4 +- packages/engine/src/services/frameCapture.ts | 4 +- 4 files changed, 37 insertions(+), 21 deletions(-) diff --git a/packages/cli/src/browser/manager.ts b/packages/cli/src/browser/manager.ts index 1134e4a46..5acf32556 100644 --- a/packages/cli/src/browser/manager.ts +++ b/packages/cli/src/browser/manager.ts @@ -247,10 +247,12 @@ async function findFromCache(): Promise { // engine an older binary than the engine itself would pick. // // We intentionally check puppeteer BEFORE the hyperframes-managed cache: - // the HF cache is pinned to `CHROME_VERSION` (above) which lags behind - // upstream Chrome by many releases. If a user installed chrome-headless-shell - // separately (via `@puppeteer/browsers install`) we want to use that - // newer binary, not the pinned-stale fallback. + // this is the non-`preferManagedChrome` path, which exists so a user who + // installed chrome-headless-shell separately (via `@puppeteer/browsers + // install`) keeps using that binary instead of being silently switched to + // the HF-pinned one. Note `CHROME_VERSION` (above) is a Dev-channel pin + // that may be NEWER than a user's puppeteer-cache Stable build — this is + // about respecting an explicit prior choice, not "newest wins". const fromPuppeteer = findFromPuppeteerCache(); if (fromPuppeteer) { return { result: fromPuppeteer }; diff --git a/packages/cli/src/commands/browser.ts b/packages/cli/src/commands/browser.ts index d9391cc10..2d0c74a23 100644 --- a/packages/cli/src/commands/browser.ts +++ b/packages/cli/src/commands/browser.ts @@ -63,24 +63,38 @@ async function runEnsure(options?: { force?: boolean }): Promise { const s = clack.spinner(); if (!options?.force) { + // Resolve with `preferManagedChrome` so this reports what `render` + // actually uses — a system Chrome without our pinned HF cache still + // downloads on the next render, so it shouldn't be reported as "found". s.start("Looking for an existing browser..."); - const existing = await findBrowser(); - if (existing) { - s.stop(c.success("Browser found")); - console.log(); - console.log(` ${c.dim("Source:")} ${c.bold(existing.source)}`); - console.log(` ${c.dim("Path:")} ${c.bold(existing.executablePath)}`); - console.log(); - clack.outro(c.success("Ready to render.")); - return; - } + let lastPct = -1; + const existing = await ensureBrowser({ + preferManagedChrome: true, + onProgress: (downloaded, total) => { + if (total <= 0) return; + const pct = Math.floor((downloaded / total) * 100); + if (pct > lastPct) { + lastPct = pct; + s.message( + `Downloading Chrome Headless Shell ${c.dim("v" + CHROME_VERSION)} — ${c.progress(pct + "%")} ${c.dim("(" + formatBytes(downloaded) + " / " + formatBytes(total) + ")")}`, + ); + } + }, + }); - s.stop("No browser found — downloading"); - } else { - s.start("Purging cached download and re-downloading..."); + if (existing.source === "download") trackBrowserInstall(); + s.stop(c.success(existing.source === "download" ? "Download complete" : "Browser found")); + console.log(); + console.log(` ${c.dim("Source:")} ${c.bold(existing.source)}`); + console.log(` ${c.dim("Path:")} ${c.bold(existing.executablePath)}`); + console.log(); + clack.outro(c.success("Ready to render.")); + return; } + s.start("Purging cached download and re-downloading..."); + const downloadSpinner = clack.spinner(); downloadSpinner.start(`Downloading Chrome Headless Shell ${c.dim("v" + CHROME_VERSION)}...`); diff --git a/packages/cli/src/server/studioServer.ts b/packages/cli/src/server/studioServer.ts index d6d839a06..28b58f4e8 100644 --- a/packages/cli/src/server/studioServer.ts +++ b/packages/cli/src/server/studioServer.ts @@ -179,7 +179,7 @@ async function getThumbnailBrowser(): Promise