Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 12 additions & 13 deletions scripts/install.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -284,18 +284,17 @@ function Resolve-NpmCmd {
}

function Find-SystemBrowser {
$candidates = @(
"${env:ProgramFiles}\Google\Chrome\Application\chrome.exe",
"${env:ProgramFiles(x86)}\Google\Chrome\Application\chrome.exe",
"${env:LOCALAPPDATA}\Google\Chrome\Application\chrome.exe",
"${env:ProgramFiles}\Microsoft\Edge\Application\msedge.exe",
"${env:ProgramFiles(x86)}\Microsoft\Edge\Application\msedge.exe",
"${env:ProgramFiles}\Chromium\Application\chrome.exe",
"${env:LOCALAPPDATA}\Chromium\Application\chrome.exe"
)
foreach ($p in $candidates) {
if (Test-Path $p) { return $p }
}
# Honor ONLY an explicit, user-set AGENT_BROWSER_EXECUTABLE_PATH override.
#
# We no longer scan well-known install locations for a system browser.
# Auto-detection silently bound the install to an arbitrary binary instead
# of the bundled Playwright Chromium, which made the browser tool behave
# differently across hosts (and, on Linux, picked up a sandboxed Snap
# Chromium that hangs every browser_navigate). Every install now uses the
# bundled Chromium unless the user explicitly points elsewhere.
$override = $env:AGENT_BROWSER_EXECUTABLE_PATH
if ([string]::IsNullOrWhiteSpace($override)) { return $null }
if (Test-Path $override) { return $override }
return $null
}

Expand Down Expand Up @@ -346,7 +345,7 @@ function Install-AgentBrowser {
$sysBrowser = Find-SystemBrowser
if ($sysBrowser) {
Write-BrowserEnv -BrowserPath $sysBrowser
Write-Info "System browser detected -- skipping Chromium download"
Write-Info "Explicit browser override set -- skipping bundled Chromium download"
} else {
$abExe = Join-Path $prefixDir "agent-browser.cmd"
if (Test-Path $abExe) {
Expand Down
90 changes: 58 additions & 32 deletions scripts/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1777,42 +1777,66 @@ SOUL_EOF
}

find_system_browser() {
# Prefer a user-specified browser path, then common Linux/macOS Chrome and
# Chromium command names. Arch-family distributions commonly ship plain
# `chromium`, while Debian-family systems often use `chromium-browser`.
if [ -n "${AGENT_BROWSER_EXECUTABLE_PATH:-}" ]; then
if [ -x "$AGENT_BROWSER_EXECUTABLE_PATH" ]; then
echo "$AGENT_BROWSER_EXECUTABLE_PATH"
return 0
fi
if command -v "$AGENT_BROWSER_EXECUTABLE_PATH" >/dev/null 2>&1; then
command -v "$AGENT_BROWSER_EXECUTABLE_PATH"
return 0
fi
# Honor ONLY an explicit, user-set AGENT_BROWSER_EXECUTABLE_PATH override.
#
# We deliberately do NOT scan PATH or well-known app locations any more.
# Auto-detection silently bound the install to whatever `command -v chromium`
# resolved to — most damagingly a Snap Chromium (/snap/bin/chromium), whose
# sandbox blocks agent-browser's control socket under /tmp, so every
# browser_navigate hung until the 60s timeout fired ("opening web page
# failed"). Every install now uses the bundled Playwright Chromium unless the
# user explicitly points elsewhere.
local override="${AGENT_BROWSER_EXECUTABLE_PATH:-}"

if [ -z "$override" ]; then
return 1
fi

local candidate
for candidate in google-chrome google-chrome-stable chromium chromium-browser chrome; do
if command -v "$candidate" >/dev/null 2>&1; then
command -v "$candidate"
return 0
fi
done
# A Snap binary is never a valid target — its confinement is the very bug we
# are fixing — so reject it even when set explicitly.
case "$override" in
/snap/*) return 1 ;;
esac

if [ "$(uname)" = "Darwin" ]; then
for app in \
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
"/Applications/Chromium.app/Contents/MacOS/Chromium"; do
if [ -x "$app" ]; then
echo "$app"
return 0
fi
done
if [ -x "$override" ]; then
echo "$override"
return 0
fi
if command -v "$override" >/dev/null 2>&1; then
command -v "$override"
return 0
fi

return 1
}

strip_snap_browser_override() {
# Existing installs created before the system-browser fallback was dropped
# may carry an auto-written AGENT_BROWSER_EXECUTABLE_PATH pointing at a Snap
# Chromium (/snap/bin/chromium). That path is the root cause of the "opening
# web page failed" hang, and the runtime reads it straight from .env — so
# removing the fallback in the installer is not enough on its own. Strip any
# snap-pointing override here (and its auto-written comment) so the bundled
# Chromium download runs and the agent stops using the broken binary. A
# deliberately-set non-snap override is left untouched.
local env_file="$HERMES_HOME/.env"

[ -f "$env_file" ] || return 0
grep -Eq '^AGENT_BROWSER_EXECUTABLE_PATH=/snap/' "$env_file" 2>/dev/null || return 0

local tmp
tmp="$(mktemp)" || return 0
if grep -Ev '^AGENT_BROWSER_EXECUTABLE_PATH=/snap/|^# Hermes Agent browser tools' "$env_file" > "$tmp"; then
mv "$tmp" "$env_file"
log_warn "Removed stale Snap browser override (AGENT_BROWSER_EXECUTABLE_PATH=/snap/...) from $env_file"
log_info "Hermes will use the bundled Chromium instead."
# Drop it from this process too so the rest of the run doesn't re-detect it.
unset AGENT_BROWSER_EXECUTABLE_PATH
else
rm -f "$tmp"
fi
}

run_browser_install_with_timeout() {
local timeout_seconds="$1"
shift
Expand Down Expand Up @@ -1848,7 +1872,7 @@ configure_browser_env_from_system_browser() {

{
echo ""
echo "# Hermes Agent browser tools — use the system Chrome/Chromium binary."
echo "# Hermes Agent browser tools — explicit browser override."
echo "AGENT_BROWSER_EXECUTABLE_PATH=$browser_path"
} >> "$env_file"
log_success "Configured browser tools to use $browser_path"
Expand Down Expand Up @@ -1887,10 +1911,11 @@ install_node_deps() {
log_info " sudo npx playwright install-deps chromium"
else
log_info "Installing browser engine (Playwright Chromium)..."
strip_snap_browser_override
DETECTED_BROWSER_EXECUTABLE="$(find_system_browser 2>/dev/null || true)"
if [ -n "$DETECTED_BROWSER_EXECUTABLE" ]; then
log_success "Found system Chrome/Chromium at $DETECTED_BROWSER_EXECUTABLE"
log_info "Skipping Playwright browser download; Hermes will use the system browser."
log_success "Using explicit browser override: $DETECTED_BROWSER_EXECUTABLE"
log_info "Skipping bundled Chromium download (AGENT_BROWSER_EXECUTABLE_PATH is set)."
else
case "$DISTRO" in
ubuntu|debian|raspbian|pop|linuxmint|elementary|zorin|kali|parrot)
Expand Down Expand Up @@ -2225,11 +2250,12 @@ ensure_browser() {
rm -f "$log_file"
export PATH="$HERMES_HOME/node/bin:$PATH"

strip_snap_browser_override
local sys_browser
sys_browser="$(find_system_browser 2>/dev/null || true)"
if [ -n "$sys_browser" ]; then
configure_browser_env_from_system_browser "$sys_browser"
log_info "System browser detected -- skipping Chromium download"
log_info "Explicit browser override set -- skipping bundled Chromium download"
return 0
fi

Expand Down
40 changes: 34 additions & 6 deletions tests/test_install_sh_browser_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,47 @@
INSTALL_SH = REPO_ROOT / "scripts" / "install.sh"


def test_install_script_skips_playwright_download_when_system_browser_exists() -> None:
def test_install_script_does_not_autodetect_system_browser_on_path() -> None:
"""The installer must not scan PATH/well-known locations for a browser.

Auto-detection silently bound the install to whatever ``command -v
chromium`` resolved to — most damagingly a Snap Chromium, whose sandbox
blocks agent-browser's control socket and hangs every browser_navigate. The
fallback was dropped in favor of always using the bundled Playwright
Chromium, so the old PATH-scan and "use the system browser" path are gone.
"""
text = INSTALL_SH.read_text()

assert "find_system_browser()" in text
assert "google-chrome google-chrome-stable chromium chromium-browser chrome" in text
assert "Skipping Playwright browser download; Hermes will use the system browser." in text
assert "google-chrome google-chrome-stable chromium chromium-browser chrome" not in text
assert "Skipping Playwright browser download; Hermes will use the system browser." not in text


def test_install_script_persists_system_browser_for_agent_browser() -> None:
def test_install_script_honors_explicit_browser_override_only() -> None:
"""find_system_browser consults only an explicit AGENT_BROWSER_EXECUTABLE_PATH."""
text = INSTALL_SH.read_text()

assert "configure_browser_env_from_system_browser()" in text
assert "AGENT_BROWSER_EXECUTABLE_PATH=$browser_path" in text
assert 'override="${AGENT_BROWSER_EXECUTABLE_PATH:-}"' in text
# An explicit override still skips the bundled download (override, not fallback).
assert "Skipping bundled Chromium download" in text


def test_install_script_strips_stale_snap_browser_override() -> None:
"""Already-affected installs must auto-recover.

A pre-existing AGENT_BROWSER_EXECUTABLE_PATH pointing at a Snap Chromium is
the exact value that hangs the browser tool, and the runtime reads it from
.env — so the installer strips it (and a Snap override is rejected even when
set explicitly) so the bundled Chromium download runs on update.
"""
text = INSTALL_SH.read_text()

assert "strip_snap_browser_override()" in text
assert "^AGENT_BROWSER_EXECUTABLE_PATH=/snap/" in text
# Both install paths invoke the migration before resolving a browser.
assert text.count("strip_snap_browser_override") >= 3
# A snap path is rejected by find_system_browser itself.
assert "/snap/*) return 1 ;;" in text


def test_playwright_installs_are_timeout_guarded() -> None:
Expand Down
Loading