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.
This commit is contained in:
Vance Ingalls
2026-07-08 15:25:59 -07:00
parent 8854bad8f9
commit 5f9ee0b678
4 changed files with 37 additions and 21 deletions
+6 -4
View File
@@ -247,10 +247,12 @@ async function findFromCache(): Promise<CacheLookupResult> {
// 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 };
+27 -13
View File
@@ -63,24 +63,38 @@ async function runEnsure(options?: { force?: boolean }): Promise<void> {
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)}...`);
+2 -2
View File
@@ -179,7 +179,7 @@ async function getThumbnailBrowser(): Promise<import("puppeteer-core").Browser |
const { acquireBrowser, buildChromeArgs } = await import("@hyperframes/engine");
try {
const b = await ensureBrowser();
const b = await ensureBrowser({ preferManagedChrome: true });
if (b.executablePath && !process.env.PRODUCER_HEADLESS_SHELL_PATH) {
process.env.PRODUCER_HEADLESS_SHELL_PATH = b.executablePath;
}
@@ -394,7 +394,7 @@ export function createStudioServer(options: StudioServerOptions): StudioServer {
const { ensureBrowser } = await import("../browser/manager.js");
try {
const browser = await ensureBrowser();
const browser = await ensureBrowser({ preferManagedChrome: true });
if (browser.executablePath && !process.env.PRODUCER_HEADLESS_SHELL_PATH) {
process.env.PRODUCER_HEADLESS_SHELL_PATH = browser.executablePath;
}
+2 -2
View File
@@ -549,8 +549,8 @@ async function initDrawElementOrTransparentBackground(
console.log(
`[engine] fast capture: falling back to ${session.launchCaptureMode} capture — ` +
"this Chrome build does not implement canvas.drawElementImage (Dev/Canary-only " +
"feature, ~151+); run `hyperframes doctor` or set HYPERFRAMES_BROWSER_PATH to a " +
"build that supports it.",
"feature, ~151+); run `hyperframes browser ensure --force` to fetch a supported " +
"build, or set HYPERFRAMES_BROWSER_PATH to one.",
);
await routeToFallback();
return;