mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-25 05:52:34 +00:00
fix(install): skip browser download when system chromium exists
This commit is contained in:
parent
7f08cb5941
commit
52521c937a
4 changed files with 173 additions and 54 deletions
|
|
@ -64,6 +64,7 @@ NODE_VERSION="22"
|
||||||
# data still at /root/.hermes (HERMES_HOME). Matches Claude Code / Codex CLI
|
# data still at /root/.hermes (HERMES_HOME). Matches Claude Code / Codex CLI
|
||||||
# and keeps Docker bind-mounted /root/ volumes lean.
|
# and keeps Docker bind-mounted /root/ volumes lean.
|
||||||
ROOT_FHS_LAYOUT=false
|
ROOT_FHS_LAYOUT=false
|
||||||
|
DETECTED_BROWSER_EXECUTABLE=""
|
||||||
|
|
||||||
# Options
|
# Options
|
||||||
USE_VENV=true
|
USE_VENV=true
|
||||||
|
|
@ -1421,6 +1422,7 @@ copy_config_templates() {
|
||||||
else
|
else
|
||||||
log_info "~/.hermes/.env already exists, keeping it"
|
log_info "~/.hermes/.env already exists, keeping it"
|
||||||
fi
|
fi
|
||||||
|
configure_browser_env_from_system_browser
|
||||||
|
|
||||||
# Create config.yaml at ~/.hermes/config.yaml (top level, easy to find)
|
# Create config.yaml at ~/.hermes/config.yaml (top level, easy to find)
|
||||||
if [ ! -f "$HERMES_HOME/config.yaml" ]; then
|
if [ ! -f "$HERMES_HOME/config.yaml" ]; then
|
||||||
|
|
@ -1469,6 +1471,68 @@ SOUL_EOF
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
run_browser_install_with_timeout() {
|
||||||
|
local timeout_seconds="$1"
|
||||||
|
shift
|
||||||
|
|
||||||
|
if command -v timeout >/dev/null 2>&1; then
|
||||||
|
timeout "$timeout_seconds" "$@"
|
||||||
|
else
|
||||||
|
"$@"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
configure_browser_env_from_system_browser() {
|
||||||
|
local env_file="$HERMES_HOME/.env"
|
||||||
|
local browser_path="${DETECTED_BROWSER_EXECUTABLE:-}"
|
||||||
|
|
||||||
|
if [ -z "$browser_path" ]; then
|
||||||
|
browser_path="$(find_system_browser 2>/dev/null || true)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$browser_path" ] || [ ! -f "$env_file" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if grep -q '^AGENT_BROWSER_EXECUTABLE_PATH=' "$env_file" 2>/dev/null; then
|
||||||
|
log_info "AGENT_BROWSER_EXECUTABLE_PATH already configured"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
{
|
||||||
|
echo ""
|
||||||
|
echo "# Hermes Agent browser tools — use the system Chrome/Chromium binary."
|
||||||
|
echo "AGENT_BROWSER_EXECUTABLE_PATH=$browser_path"
|
||||||
|
} >> "$env_file"
|
||||||
|
log_success "Configured browser tools to use $browser_path"
|
||||||
|
}
|
||||||
|
|
||||||
install_node_deps() {
|
install_node_deps() {
|
||||||
if [ "$HAS_NODE" = false ]; then
|
if [ "$HAS_NODE" = false ]; then
|
||||||
log_info "Skipping Node.js dependencies (Node not installed)"
|
log_info "Skipping Node.js dependencies (Node not installed)"
|
||||||
|
|
@ -1495,57 +1559,63 @@ install_node_deps() {
|
||||||
# For Arch/Manjaro we install the system libs via pacman first.
|
# For Arch/Manjaro we install the system libs via pacman first.
|
||||||
# Other systems must install Chromium dependencies manually.
|
# Other systems must install Chromium dependencies manually.
|
||||||
log_info "Installing browser engine (Playwright Chromium)..."
|
log_info "Installing browser engine (Playwright Chromium)..."
|
||||||
case "$DISTRO" in
|
DETECTED_BROWSER_EXECUTABLE="$(find_system_browser 2>/dev/null || true)"
|
||||||
ubuntu|debian|raspbian|pop|linuxmint|elementary|zorin|kali|parrot)
|
if [ -n "$DETECTED_BROWSER_EXECUTABLE" ]; then
|
||||||
log_info "Playwright may request sudo to install browser system dependencies (shared libraries)."
|
log_success "Found system Chrome/Chromium at $DETECTED_BROWSER_EXECUTABLE"
|
||||||
log_info "This is standard Playwright setup — Hermes itself does not require root access."
|
log_info "Skipping Playwright browser download; Hermes will use the system browser."
|
||||||
cd "$INSTALL_DIR" && npx playwright install --with-deps chromium 2>/dev/null || {
|
else
|
||||||
log_warn "Playwright browser installation failed — browser tools will not work."
|
case "$DISTRO" in
|
||||||
log_warn "Try running manually: cd $INSTALL_DIR && npx playwright install --with-deps chromium"
|
ubuntu|debian|raspbian|pop|linuxmint|elementary|zorin|kali|parrot)
|
||||||
}
|
log_info "Playwright may request sudo to install browser system dependencies (shared libraries)."
|
||||||
;;
|
log_info "This is standard Playwright setup — Hermes itself does not require root access."
|
||||||
arch|manjaro)
|
cd "$INSTALL_DIR" && run_browser_install_with_timeout 600 npx playwright install --with-deps chromium 2>/dev/null || {
|
||||||
if command -v pacman &> /dev/null; then
|
log_warn "Playwright browser installation failed — browser tools will not work."
|
||||||
log_info "Arch/Manjaro detected — installing Chromium system dependencies via pacman..."
|
log_warn "Try running manually: cd $INSTALL_DIR && npx playwright install --with-deps chromium"
|
||||||
if command -v sudo &> /dev/null && sudo -n true 2>/dev/null; then
|
}
|
||||||
sudo NEEDRESTART_MODE=a pacman -S --noconfirm --needed \
|
;;
|
||||||
nss atk at-spi2-core cups libdrm libxkbcommon mesa pango cairo alsa-lib >/dev/null 2>&1 || true
|
arch|manjaro|cachyos|endeavouros|garuda)
|
||||||
elif [ "$(id -u)" -eq 0 ]; then
|
if command -v pacman &> /dev/null; then
|
||||||
pacman -S --noconfirm --needed \
|
log_info "Arch-family distro detected — installing Chromium system dependencies via pacman..."
|
||||||
nss atk at-spi2-core cups libdrm libxkbcommon mesa pango cairo alsa-lib >/dev/null 2>&1 || true
|
if command -v sudo &> /dev/null && sudo -n true 2>/dev/null; then
|
||||||
else
|
sudo NEEDRESTART_MODE=a pacman -S --noconfirm --needed \
|
||||||
log_warn "Cannot install browser deps without sudo. Run manually:"
|
nss atk at-spi2-core cups libdrm libxkbcommon mesa pango cairo alsa-lib >/dev/null 2>&1 || true
|
||||||
log_warn " sudo pacman -S nss atk at-spi2-core cups libdrm libxkbcommon mesa pango cairo alsa-lib"
|
elif [ "$(id -u)" -eq 0 ]; then
|
||||||
|
pacman -S --noconfirm --needed \
|
||||||
|
nss atk at-spi2-core cups libdrm libxkbcommon mesa pango cairo alsa-lib >/dev/null 2>&1 || true
|
||||||
|
else
|
||||||
|
log_warn "Cannot install browser deps without sudo. Run manually:"
|
||||||
|
log_warn " sudo pacman -S nss atk at-spi2-core cups libdrm libxkbcommon mesa pango cairo alsa-lib"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
cd "$INSTALL_DIR" && run_browser_install_with_timeout 600 npx playwright install chromium 2>/dev/null || {
|
||||||
cd "$INSTALL_DIR" && npx playwright install chromium 2>/dev/null || {
|
log_warn "Playwright browser installation failed — browser tools will not work."
|
||||||
log_warn "Playwright browser installation failed — browser tools will not work."
|
}
|
||||||
}
|
;;
|
||||||
;;
|
fedora|rhel|centos|rocky|alma)
|
||||||
fedora|rhel|centos|rocky|alma)
|
log_warn "Playwright does not support automatic dependency installation on RPM-based systems."
|
||||||
log_warn "Playwright does not support automatic dependency installation on RPM-based systems."
|
log_info "Install Chromium system dependencies manually before using browser tools:"
|
||||||
log_info "Install Chromium system dependencies manually before using browser tools:"
|
log_info " sudo dnf install nss atk at-spi2-core cups-libs libdrm libxkbcommon mesa-libgbm pango cairo alsa-lib"
|
||||||
log_info " sudo dnf install nss atk at-spi2-core cups-libs libdrm libxkbcommon mesa-libgbm pango cairo alsa-lib"
|
cd "$INSTALL_DIR" && run_browser_install_with_timeout 600 npx playwright install chromium 2>/dev/null || {
|
||||||
cd "$INSTALL_DIR" && npx playwright install chromium 2>/dev/null || {
|
log_warn "Playwright browser installation failed — install dependencies above and retry."
|
||||||
log_warn "Playwright browser installation failed — install dependencies above and retry."
|
}
|
||||||
}
|
;;
|
||||||
;;
|
opensuse*|sles)
|
||||||
opensuse*|sles)
|
log_warn "Playwright does not support automatic dependency installation on zypper-based systems."
|
||||||
log_warn "Playwright does not support automatic dependency installation on zypper-based systems."
|
log_info "Install Chromium system dependencies manually before using browser tools:"
|
||||||
log_info "Install Chromium system dependencies manually before using browser tools:"
|
log_info " sudo zypper install mozilla-nss libatk-1_0-0 at-spi2-core cups-libs libdrm2 libxkbcommon0 Mesa-libgbm1 pango cairo libasound2"
|
||||||
log_info " sudo zypper install mozilla-nss libatk-1_0-0 at-spi2-core cups-libs libdrm2 libxkbcommon0 Mesa-libgbm1 pango cairo libasound2"
|
cd "$INSTALL_DIR" && run_browser_install_with_timeout 600 npx playwright install chromium 2>/dev/null || {
|
||||||
cd "$INSTALL_DIR" && npx playwright install chromium 2>/dev/null || {
|
log_warn "Playwright browser installation failed — install dependencies above and retry."
|
||||||
log_warn "Playwright browser installation failed — install dependencies above and retry."
|
}
|
||||||
}
|
;;
|
||||||
;;
|
*)
|
||||||
*)
|
log_warn "Playwright does not support automatic dependency installation on $DISTRO."
|
||||||
log_warn "Playwright does not support automatic dependency installation on $DISTRO."
|
log_info "Install Chromium/browser system dependencies for your distribution, then run:"
|
||||||
log_info "Install Chromium/browser system dependencies for your distribution, then run:"
|
log_info " cd $INSTALL_DIR && npx playwright install chromium"
|
||||||
log_info " cd $INSTALL_DIR && npx playwright install chromium"
|
log_info "Browser tools will not work until dependencies are installed."
|
||||||
log_info "Browser tools will not work until dependencies are installed."
|
cd "$INSTALL_DIR" && run_browser_install_with_timeout 600 npx playwright install chromium 2>/dev/null || true
|
||||||
cd "$INSTALL_DIR" && npx playwright install chromium 2>/dev/null || true
|
;;
|
||||||
;;
|
esac
|
||||||
esac
|
fi
|
||||||
log_success "Browser engine setup complete"
|
log_success "Browser engine setup complete"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
||||||
35
tests/test_install_sh_browser_install.py
Normal file
35
tests/test_install_sh_browser_install.py
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
"""Regression tests for install.sh browser setup.
|
||||||
|
|
||||||
|
Browser automation is optional. The installer should not leave Hermes
|
||||||
|
half-installed just because Playwright's managed Chromium download hangs on an
|
||||||
|
unsupported distribution.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||||
|
INSTALL_SH = REPO_ROOT / "scripts" / "install.sh"
|
||||||
|
|
||||||
|
|
||||||
|
def test_install_script_skips_playwright_download_when_system_browser_exists() -> None:
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
def test_install_script_persists_system_browser_for_agent_browser() -> None:
|
||||||
|
text = INSTALL_SH.read_text()
|
||||||
|
|
||||||
|
assert "configure_browser_env_from_system_browser()" in text
|
||||||
|
assert "AGENT_BROWSER_EXECUTABLE_PATH=$browser_path" in text
|
||||||
|
|
||||||
|
|
||||||
|
def test_playwright_installs_are_timeout_guarded() -> None:
|
||||||
|
text = INSTALL_SH.read_text()
|
||||||
|
|
||||||
|
assert "run_browser_install_with_timeout()" in text
|
||||||
|
assert "run_browser_install_with_timeout 600 npx playwright install chromium" in text
|
||||||
|
assert "run_browser_install_with_timeout 600 npx playwright install --with-deps chromium" in text
|
||||||
|
|
@ -41,6 +41,16 @@ class TestChromiumSearchRoots:
|
||||||
|
|
||||||
|
|
||||||
class TestChromiumInstalled:
|
class TestChromiumInstalled:
|
||||||
|
def test_true_when_plain_chromium_on_path(self, monkeypatch):
|
||||||
|
monkeypatch.delenv("AGENT_BROWSER_EXECUTABLE_PATH", raising=False)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
bt.shutil,
|
||||||
|
"which",
|
||||||
|
lambda name: "/usr/bin/chromium" if name == "chromium" else None,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert bt._chromium_installed() is True
|
||||||
|
|
||||||
def test_true_when_chromium_dir_present(self, monkeypatch, tmp_path):
|
def test_true_when_chromium_dir_present(self, monkeypatch, tmp_path):
|
||||||
monkeypatch.setenv("PLAYWRIGHT_BROWSERS_PATH", str(tmp_path))
|
monkeypatch.setenv("PLAYWRIGHT_BROWSERS_PATH", str(tmp_path))
|
||||||
(tmp_path / "chromium-1208").mkdir()
|
(tmp_path / "chromium-1208").mkdir()
|
||||||
|
|
@ -108,4 +118,3 @@ class TestRunBrowserCommandChromiumGuard:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3381,8 +3381,8 @@ def _chromium_installed() -> bool:
|
||||||
|
|
||||||
1. ``AGENT_BROWSER_EXECUTABLE_PATH`` env var — the official way to point
|
1. ``AGENT_BROWSER_EXECUTABLE_PATH`` env var — the official way to point
|
||||||
agent-browser at a pre-installed Chrome/Chromium.
|
agent-browser at a pre-installed Chrome/Chromium.
|
||||||
2. System Chrome/Chromium in PATH (``google-chrome``, ``chromium-browser``,
|
2. System Chrome/Chromium in PATH (``google-chrome``, ``chromium``,
|
||||||
``chrome``).
|
``chromium-browser``, ``chrome``).
|
||||||
3. Playwright's browser cache (current logic) — directories containing
|
3. Playwright's browser cache (current logic) — directories containing
|
||||||
``chromium-*`` or ``chromium_headless_shell-*``.
|
``chromium-*`` or ``chromium_headless_shell-*``.
|
||||||
|
|
||||||
|
|
@ -3405,7 +3405,12 @@ def _chromium_installed() -> bool:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# 2. System Chrome/Chromium in PATH (common names)
|
# 2. System Chrome/Chromium in PATH (common names)
|
||||||
system_chrome = shutil.which("google-chrome") or shutil.which("chromium-browser") or shutil.which("chrome")
|
system_chrome = (
|
||||||
|
shutil.which("google-chrome")
|
||||||
|
or shutil.which("chromium")
|
||||||
|
or shutil.which("chromium-browser")
|
||||||
|
or shutil.which("chrome")
|
||||||
|
)
|
||||||
if system_chrome:
|
if system_chrome:
|
||||||
_cached_chromium_installed = True
|
_cached_chromium_installed = True
|
||||||
return True
|
return True
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue