fix(cli): detect missing Chrome libs & ffmpeg on Linux/WSL in doctor (#1841)

WSL first-render success (34.7%) is dominated by a downloaded chrome-headless-shell that launches into `libnss3.so: cannot open shared object file` — doctor/preflight only checked the binary exists, never that it can load its libraries.

- New linuxDeps.ts: /etc/os-release distro detection (Debian/Fedora/Arch/Alpine) + WSL detection, per-distro Chrome dep set, ldd-based shared-lib probe.
- preflight.checkChrome downgrades a found-but-unlaunchable Chrome to a render-blocking error with the exact per-distro install command.
- Distro-aware ffmpeg hints; launch failures converted to actionable guidance pointing at `hyperframes doctor` (skipped on ARM64).
- Detect + print remediation (no auto-install).

Render-reliability workstream P1-4. Success measured on PostHog dashboard 1783183.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
James Russo
2026-07-01 18:49:48 -07:00
committed by GitHub
co-authored by Claude Opus 4.8
parent 1d4c5d5ec3
commit 180f368af1
6 changed files with 665 additions and 6 deletions
+39 -4
View File
@@ -9,6 +9,12 @@ import {
findFFprobe,
getFFmpegInstallHint,
} from "./ffmpeg.js";
import {
chromeDepsInstallCommand,
detectLinuxDistro,
distroLabel,
probeChromeSharedLibs,
} from "./linuxDeps.js";
import { getFreeDiskMb } from "../telemetry/system.js";
export type EnvironmentCheckLevel = "ok" | "warn" | "error";
@@ -118,16 +124,45 @@ function checkFFprobe(): EnvironmentCheckOutcome {
};
}
/**
* A Chrome binary can exist on disk yet be unlaunchable because its system
* shared libraries (libnss3, libatk, ...) aren't installed — the dominant WSL
* first-render failure. When that's the case, downgrade the "found" outcome to
* a render-blocking error carrying the exact per-distro install command, so the
* user hits it in `doctor`/pre-flight instead of a cryptic
* `Failed to launch the browser process` mid-render. No-op off Linux and when
* `ldd` can't run (probe inconclusive).
*/
function chromeSharedLibOutcome(
executablePath: string,
found: EnvironmentCheckOutcome,
): EnvironmentCheckOutcome {
if (process.platform !== "linux") return found;
const probe = probeChromeSharedLibs(executablePath);
if (probe.probeUnavailable || probe.ok) return found;
const distro = detectLinuxDistro();
return {
name: "Chrome",
ok: false,
level: "error",
title: "Chrome cannot launch (missing system libraries)",
detail: `Chrome at ${executablePath} is missing shared libraries on ${distroLabel(distro)}: ${probe.missing.join(", ")}`,
hint: chromeDepsInstallCommand(distro.family),
path: executablePath,
};
}
async function checkChrome(browserPath?: string): Promise<EnvironmentCheckOutcome> {
if (browserPath) {
if (existsSync(browserPath)) {
return {
return chromeSharedLibOutcome(browserPath, {
name: "Chrome",
ok: true,
level: "ok",
detail: `explicit: ${browserPath}`,
path: browserPath,
};
});
}
return {
name: "Chrome",
@@ -151,13 +186,13 @@ async function checkChrome(browserPath?: string): Promise<EnvironmentCheckOutcom
info = undefined;
}
if (info) {
return {
return chromeSharedLibOutcome(info.executablePath, {
name: "Chrome",
ok: true,
level: "ok",
detail: `${info.source}: ${info.executablePath}`,
path: info.executablePath,
};
});
}
return {