diff --git a/scripts/install.sh b/scripts/install.sh index 1ee5a31ec64..cf24912cc51 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -69,6 +69,7 @@ DETECTED_BROWSER_EXECUTABLE="" # Options USE_VENV=true RUN_SETUP=true +SKIP_BROWSER=false BRANCH="main" # Detect non-interactive mode (e.g. curl | bash) @@ -91,6 +92,10 @@ while [[ $# -gt 0 ]]; do RUN_SETUP=false shift ;; + --skip-browser|--no-playwright) + SKIP_BROWSER=true + shift + ;; --branch) BRANCH="$2" shift 2 @@ -112,6 +117,7 @@ while [[ $# -gt 0 ]]; do echo "Options:" echo " --no-venv Don't create virtual environment" echo " --skip-setup Skip interactive setup wizard" + echo " --skip-browser Skip Playwright/Chromium install (browser tools won't work)" echo " --branch NAME Git branch to install (default: main)" echo " --dir PATH Installation directory" echo " default (non-root): ~/.hermes/hermes-agent" @@ -1566,6 +1572,13 @@ install_node_deps() { # Playwright's --with-deps only supports apt-based systems natively. # For Arch/Manjaro we install the system libs via pacman first. # Other systems must install Chromium dependencies manually. + if [ "$SKIP_BROWSER" = true ]; then + log_info "Skipping Playwright/Chromium install (--skip-browser)" + log_info "Browser tools will be unavailable until you run manually:" + log_info " cd $INSTALL_DIR && npx playwright install chromium" + log_info "On apt-based systems, an admin also needs to run:" + log_info " sudo npx playwright install-deps chromium" + else log_info "Installing browser engine (Playwright Chromium)..." DETECTED_BROWSER_EXECUTABLE="$(find_system_browser 2>/dev/null || true)" if [ -n "$DETECTED_BROWSER_EXECUTABLE" ]; then @@ -1574,12 +1587,30 @@ install_node_deps() { else case "$DISTRO" in 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." - cd "$INSTALL_DIR" && run_browser_install_with_timeout 600 npx playwright install --with-deps chromium 2>/dev/null || { - log_warn "Playwright browser installation failed — browser tools will not work." - log_warn "Try running manually: cd $INSTALL_DIR && npx playwright install --with-deps chromium" - } + # Use --with-deps only when sudo is available non-interactively + # (root, or a user with passwordless sudo). Non-sudo users + # — typical for systemd service accounts and unprivileged + # operator users — would otherwise get blocked on an + # interactive sudo prompt that they can't satisfy. Fall back + # to the browser-only install in that case, and print the + # exact command the admin needs to run separately. + if [ "$(id -u)" -eq 0 ] || (command -v sudo >/dev/null 2>&1 && sudo -n true 2>/dev/null); then + log_info "Installing Playwright Chromium with system dependencies..." + cd "$INSTALL_DIR" && run_browser_install_with_timeout 600 npx playwright install --with-deps chromium 2>/dev/null || { + log_warn "Playwright browser installation failed — browser tools will not work." + log_warn "Try running manually: cd $INSTALL_DIR && npx playwright install --with-deps chromium" + } + else + log_warn "No sudo available — skipping system-library install (--with-deps)." + log_info "Ask an administrator to run, one time, as root:" + log_info " sudo npx playwright install-deps chromium" + log_info " (from $INSTALL_DIR, after Node.js deps are installed)" + log_info "Installing Chromium binary into this user's Playwright cache..." + cd "$INSTALL_DIR" && run_browser_install_with_timeout 600 npx playwright install chromium 2>/dev/null || { + log_warn "Playwright browser installation failed — browser tools will not work." + log_warn "Try running manually: cd $INSTALL_DIR && npx playwright install chromium" + } + fi ;; arch|manjaro|cachyos|endeavouros|garuda) if command -v pacman &> /dev/null; then @@ -1624,6 +1655,7 @@ install_node_deps() { ;; esac fi + fi log_success "Browser engine setup complete" fi diff --git a/tests/test_install_sh_browser_install.py b/tests/test_install_sh_browser_install.py index 4e1908e4294..6ec3b565384 100644 --- a/tests/test_install_sh_browser_install.py +++ b/tests/test_install_sh_browser_install.py @@ -32,4 +32,29 @@ def test_playwright_installs_are_timeout_guarded() -> None: assert "run_browser_install_with_timeout()" in text assert "run_browser_install_with_timeout 600 npx playwright install chromium" in text + # --with-deps is still invoked on apt-based systems, but only when sudo + # is available non-interactively (root or passwordless sudo). Non-sudo + # service users fall back to the browser-only install — see + # install_node_deps() in install.sh. assert "run_browser_install_with_timeout 600 npx playwright install --with-deps chromium" in text + + +def test_install_script_supports_skip_browser_flag() -> None: + """--skip-browser (and --no-playwright alias) skips the Playwright install.""" + text = INSTALL_SH.read_text() + + assert "--skip-browser|--no-playwright)" in text + assert "SKIP_BROWSER=true" in text + assert 'if [ "$SKIP_BROWSER" = true ]; then' in text + assert "--skip-browser Skip Playwright/Chromium install" in text + + +def test_install_script_skips_with_deps_when_no_sudo() -> None: + """Non-sudo users on apt distros must not block on an interactive sudo prompt.""" + text = INSTALL_SH.read_text() + + # The apt branch must gate --with-deps behind a sudo capability check + # (root or non-interactive sudo), otherwise the installer hangs for + # service-user installs (systemd accounts, operator users, etc.). + assert 'if [ "$(id -u)" -eq 0 ] || (command -v sudo >/dev/null 2>&1 && sudo -n true 2>/dev/null); then' in text + assert "sudo npx playwright install-deps chromium" in text diff --git a/website/docs/getting-started/installation.md b/website/docs/getting-started/installation.md index 102f044d501..c8db40a9137 100644 --- a/website/docs/getting-started/installation.md +++ b/website/docs/getting-started/installation.md @@ -132,6 +132,43 @@ If you want to clone the repo and install from source — for contributing, runn --- +## Non-Sudo / System Service User Installs + +Running Hermes as a dedicated unprivileged user (e.g. a `hermes` systemd service account, or any user without `sudo` access) is supported. The only thing on the install path that genuinely needs root is Playwright's `--with-deps` step, which `apt`-installs shared libraries (`libnss3`, `libxkbcommon`, etc.) used by Chromium. The installer detects whether sudo is available and gracefully degrades when it isn't — it will install the Chromium binary into the service user's own Playwright cache and print the exact command an administrator needs to run separately. + +**Recommended split (Debian/Ubuntu):** + +1. **One time, as an admin user with sudo**, install the system libraries Chromium needs: + ```bash + sudo npx playwright install-deps chromium + ``` + (You can run this from anywhere — `npx` will fetch Playwright on the fly.) + +2. **As the unprivileged service user**, run the regular installer. It will detect the missing sudo, skip `--with-deps`, and install Chromium into the user's local Playwright cache: + ```bash + curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash + ``` + + If you want to skip the Playwright step entirely — for example because you're running headless and don't need browser automation — pass `--skip-browser`: + ```bash + curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash -s -- --skip-browser + ``` + +3. **Make `hermes` available to the service user's shells.** The installer writes the launcher to `~/.local/bin/hermes`. System service accounts often have a minimal PATH that doesn't include `~/.local/bin`. Either add it to the user's environment, or symlink the launcher into a system location: + ```bash + # Option A — add to the service user's profile + echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc + + # Option B — symlink system-wide (run as an admin) + sudo ln -s /home/hermes/.hermes/hermes-agent/venv/bin/hermes /usr/local/bin/hermes + ``` + +4. **Verify:** `hermes doctor` should now run cleanly. If you get `ModuleNotFoundError: No module named 'dotenv'`, you're invoking the repo source `hermes` file (`~/.hermes/hermes-agent/hermes`) with system Python instead of the venv launcher (`~/.hermes/hermes-agent/venv/bin/hermes`) — fix step 3. + +The same pattern works on Arch (the installer uses pacman with the same sudo-detection logic), Fedora/RHEL, and openSUSE — those distros don't support `--with-deps` at all, so an administrator always installs the system libraries separately. The relevant `dnf`/`zypper` commands are printed by the installer. + +--- + ## Troubleshooting | Problem | Solution |