fix(windows): auto-install Playwright Chromium + surface it in doctor

scripts/install.sh runs 'npx playwright install --with-deps chromium'
on every Linux distro after the npm-install step, which is why browser
tools Just Work on Linux.  scripts/install.ps1 never did the equivalent
step, so on native Windows installs check_browser_requirements() in
tools/browser_tool.py would return False (no Chromium under
%LOCALAPPDATA%\ms-playwright) and every browser_* tool got silently
filtered out of the agent's tool schema — no error, no log entry, user
just wondered why the tools didn't exist.

Two-part fix:

1. scripts/install.ps1: after 'npm install' in InstallDir succeeds, run
   'npx playwright install chromium'.  Resolves npx via the same
   execution-policy-aware logic already used for npm (prefer npx.cmd
   next to npmExe, fall back to Get-Command).  Surfaces a warning +
   manual-recovery hint when the install fails, matching install.sh
   behaviour for distros.

2. hermes_cli/doctor.py: after the agent-browser check, lazily import
   tools.browser_tool and reuse the exact same _chromium_installed()
   predicate check_browser_requirements() uses, so the doctor signal
   cannot drift from the runtime gate.  Skip the check when Camofox /
   CDP override / a cloud provider / Lightpanda is configured (those
   bypass local Chromium).  On missing Chromium, the hint is
   platform-correct: '--with-deps' on POSIX, plain 'install chromium'
   on win32.

Verified on Windows 10:
- 'npx playwright install chromium' completes successfully, drops
  Chrome Headless Shell under %LOCALAPPDATA%\ms-playwright
- check_browser_requirements() flips from False -> True
- 'hermes doctor' now prints either '✓ Playwright Chromium (browser
  engine)' or '⚠ Playwright Chromium not installed' + fix command
- tests/hermes_cli/test_doctor.py: 38/38 pass
- tests/tools/test_browser_chromium_check.py: 16/16 pass
This commit is contained in:
Teknium 2026-05-08 07:56:35 -07:00
parent b63f9645f0
commit 03566e5124
2 changed files with 111 additions and 1 deletions

View file

@ -1035,10 +1035,13 @@ def run_doctor(args):
check_ok("Node.js")
# Check if agent-browser is installed
agent_browser_path = PROJECT_ROOT / "node_modules" / "agent-browser"
agent_browser_ok = False
if agent_browser_path.exists():
check_ok("agent-browser (Node.js)", "(browser automation)")
agent_browser_ok = True
elif shutil.which("agent-browser"):
check_ok("agent-browser", "(browser automation)")
agent_browser_ok = True
else:
if _is_termux():
check_info("agent-browser is not installed (expected in the tested Termux path)")
@ -1048,6 +1051,56 @@ def run_doctor(args):
check_info(step)
else:
check_warn("agent-browser not installed", "(run: npm install)")
# Chromium presence — the browser tools silently fail to register when
# agent-browser is found but no Playwright-managed Chromium is on disk
# (tools/browser_tool.py::check_browser_requirements filters them out
# before the agent ever sees them). Reuse the exact predicate it uses
# so the two checks cannot diverge. Skip on Termux (not a tested
# path).
if agent_browser_ok and not _is_termux():
try:
# Lazy import: browser_tool is a ~150KB module we don't want
# to eagerly load in every `hermes doctor` invocation.
from tools.browser_tool import (
_chromium_installed,
_is_camofox_mode,
_get_cloud_provider,
_get_cdp_override,
_using_lightpanda_engine,
)
except Exception:
# If browser_tool can't even import, that's a separate bug
# surfaced elsewhere; don't crash doctor.
pass
else:
# Only warn about Chromium if the installed engine actually
# requires it: Camofox, CDP override, a cloud provider, or
# Lightpanda all bypass the local Chromium requirement.
skip_chromium_check = (
_is_camofox_mode()
or bool(_get_cdp_override())
or _get_cloud_provider() is not None
or _using_lightpanda_engine()
)
if not skip_chromium_check:
if _chromium_installed():
check_ok("Playwright Chromium", "(browser engine)")
else:
check_warn(
"Playwright Chromium not installed",
"(browser_* tools will be hidden from the agent)",
)
if sys.platform == "win32":
check_info(
f"Install with: cd {PROJECT_ROOT} && "
"npx playwright install chromium"
)
else:
check_info(
f"Install with: cd {PROJECT_ROOT} && "
"npx playwright install --with-deps chromium"
)
else:
if _is_termux():
check_info("Node.js not found (browser tools are optional in the tested Termux path)")

View file

@ -1040,7 +1040,64 @@ function Install-NodeDeps {
if (Test-Path "$InstallDir\package.json") {
Write-Info "Installing Node.js dependencies (browser tools)..."
$browserLog = "$env:TEMP\hermes-npm-browser-$(Get-Random).log"
[void](_Run-NpmInstall "Browser tools" $InstallDir $browserLog $npmExe)
$browserNpmOk = _Run-NpmInstall "Browser tools" $InstallDir $browserLog $npmExe
# Install Playwright Chromium (mirrors scripts/install.sh behaviour for
# Linux). Without this, tools/browser_tool.py::check_browser_requirements
# returns False (no Chromium under %LOCALAPPDATA%\ms-playwright), and the
# browser_* tools are silently filtered out of the agent's tool schema.
# System Chrome at "C:\Program Files\Google\Chrome\..." is NOT used by
# agent-browser — it expects a Playwright-managed Chromium.
if ($browserNpmOk) {
Write-Info "Installing browser engine (Playwright Chromium)..."
# npx lives next to npm in the same bin dir. Prefer .cmd to dodge
# the same execution-policy gotcha that affects npm.ps1 (see above).
$npmDir = Split-Path $npmExe -Parent
$npxExe = $null
foreach ($cand in @("npx.cmd", "npx.exe", "npx")) {
$try = Join-Path $npmDir $cand
if (Test-Path $try) { $npxExe = $try; break }
}
if (-not $npxExe) {
$npxCmd = Get-Command npx -ErrorAction SilentlyContinue
if ($npxCmd) { $npxExe = $npxCmd.Source }
}
if (-not $npxExe) {
Write-Warn "npx not found — cannot install Playwright Chromium."
Write-Info "Run manually later: cd `"$InstallDir`"; npx playwright install chromium"
} else {
$pwLog = "$env:TEMP\hermes-playwright-install-$(Get-Random).log"
Push-Location $InstallDir
try {
& $npxExe playwright install chromium *> $pwLog
$pwCode = $LASTEXITCODE
if ($pwCode -eq 0) {
Write-Success "Playwright Chromium installed (browser tools ready)"
Remove-Item -Force $pwLog -ErrorAction SilentlyContinue
} else {
Write-Warn "Playwright Chromium install failed — exit code $pwCode"
Write-Warn "Browser tools will not work until Chromium is installed."
if (Test-Path $pwLog) {
$pwErr = Get-Content $pwLog -Raw -ErrorAction SilentlyContinue
if ($pwErr) {
$snippet = if ($pwErr.Length -gt 1200) { $pwErr.Substring(0, 1200) + "..." } else { $pwErr }
Write-Info " playwright output:"
foreach ($line in $snippet -split "`n") {
Write-Host " $line" -ForegroundColor DarkGray
}
Write-Info " Full log: $pwLog"
}
}
Write-Info "Run manually later: cd `"$InstallDir`"; npx playwright install chromium"
}
} catch {
Write-Warn "Playwright Chromium install could not be launched: $_"
Write-Info "Run manually later: cd `"$InstallDir`"; npx playwright install chromium"
} finally {
Pop-Location
}
}
}
}
# TUI