mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-29 11:42:04 +00:00
fix(browser): validate agent-browser is runnable, not just present (#51740)
After `hermes update`, a globally-installed agent-browser's npm postinstall (fixUnixSymlink) re-points the global symlink (e.g. /opt/homebrew/bin/agent-browser) at our local node_modules binary. The next update wipes node_modules, leaving a dangling symlink that `which` still reports but exec fails on with exit 127 — silently breaking every browser tool (#48521). Root cause is trust-on-presence: shutil.which/Path.exists accept a name that resolves but won't run. Add hermes_constants.agent_browser_runnable() (resolves the path + runs --version) and gate all four resolution sites on it: _find_agent_browser now skips a dead candidate and falls through to the next working one (extended PATH -> local .bin -> npx), self-healing the dangling link. dep_ensure/doctor/nous_subscription validate too; doctor warns on a broken link. Closes #48521.
This commit is contained in:
parent
a911bcda18
commit
3c75e11571
8 changed files with 143 additions and 26 deletions
|
|
@ -22,12 +22,14 @@ import subprocess
|
|||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from hermes_constants import agent_browser_runnable
|
||||
|
||||
_IS_WINDOWS = platform.system() == "Windows"
|
||||
|
||||
_DEP_CHECKS = {
|
||||
"node": lambda: shutil.which("node") is not None,
|
||||
"browser": lambda: (
|
||||
shutil.which("agent-browser") is not None
|
||||
agent_browser_runnable(shutil.which("agent-browser"))
|
||||
or _has_system_browser()
|
||||
or _has_hermes_agent_browser()
|
||||
),
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ from pathlib import Path
|
|||
from hermes_cli.config import get_project_root, get_hermes_home, get_env_path
|
||||
from hermes_cli.env_loader import load_hermes_dotenv
|
||||
from hermes_constants import display_hermes_home
|
||||
from hermes_constants import agent_browser_runnable
|
||||
|
||||
PROJECT_ROOT = get_project_root()
|
||||
HERMES_HOME = get_hermes_home()
|
||||
|
|
@ -1483,12 +1484,21 @@ def run_doctor(args):
|
|||
# Check if agent-browser is installed
|
||||
agent_browser_path = PROJECT_ROOT / "node_modules" / "agent-browser"
|
||||
agent_browser_ok = False
|
||||
_which_ab = shutil.which("agent-browser")
|
||||
if agent_browser_path.exists():
|
||||
check_ok("agent-browser (Node.js)", "(browser automation)")
|
||||
agent_browser_ok = True
|
||||
elif shutil.which("agent-browser"):
|
||||
elif _which_ab and agent_browser_runnable(_which_ab):
|
||||
check_ok("agent-browser", "(browser automation)")
|
||||
agent_browser_ok = True
|
||||
elif _which_ab:
|
||||
# Found on PATH but won't run — almost always a dangling global
|
||||
# symlink left behind by agent-browser's npm postinstall after a
|
||||
# `hermes update` wiped node_modules (issue #48521).
|
||||
check_warn(
|
||||
"agent-browser found but not runnable",
|
||||
f"(broken symlink at {_which_ab}? run: npm install)",
|
||||
)
|
||||
elif _is_termux():
|
||||
check_info("agent-browser is not installed (expected in the tested Termux path)")
|
||||
check_info("Install it manually later with: npm install -g agent-browser && agent-browser install")
|
||||
|
|
|
|||
|
|
@ -159,11 +159,17 @@ def _toolset_enabled(config: Dict[str, object], toolset_key: str) -> bool:
|
|||
def _has_agent_browser() -> bool:
|
||||
import shutil
|
||||
|
||||
agent_browser_bin = shutil.which("agent-browser")
|
||||
from hermes_constants import agent_browser_runnable
|
||||
|
||||
# Validate the resolved binary actually runs — a dangling global symlink
|
||||
# (issue #48521) is reported by ``which`` but fails at exec. Fall through to
|
||||
# the local node_modules copy, which the validator also checks.
|
||||
if agent_browser_runnable(shutil.which("agent-browser")):
|
||||
return True
|
||||
local_bin = (
|
||||
Path(__file__).parent.parent / "node_modules" / ".bin" / "agent-browser"
|
||||
)
|
||||
return bool(agent_browser_bin or local_bin.exists())
|
||||
return agent_browser_runnable(str(local_bin)) if local_bin.exists() else False
|
||||
|
||||
|
||||
def _local_browser_runnable() -> bool:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue