mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-28 11:32:22 +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
|
|
@ -65,7 +65,7 @@ import requests
|
|||
from typing import Dict, Any, Optional, List, Tuple, Union
|
||||
from pathlib import Path
|
||||
from agent.auxiliary_client import call_llm
|
||||
from hermes_constants import get_hermes_home
|
||||
from hermes_constants import agent_browser_runnable, get_hermes_home
|
||||
from utils import env_int, is_truthy_value
|
||||
from hermes_cli.config import DEFAULT_CONFIG, cfg_get
|
||||
|
||||
|
|
@ -1904,10 +1904,19 @@ def _find_agent_browser() -> str:
|
|||
# Note: _agent_browser_resolved is set at each return site below
|
||||
# (not before the search) to prevent a race where a concurrent thread
|
||||
# sees resolved=True but _cached_agent_browser is still None.
|
||||
#
|
||||
# Every candidate below is validated with ``agent_browser_runnable`` before
|
||||
# it is cached. A bare ``shutil.which`` hit is NOT trusted: agent-browser's
|
||||
# npm postinstall re-points a global install symlink at our local
|
||||
# node_modules binary, which disappears on the next ``hermes update`` and
|
||||
# leaves a dangling link that ``which`` still reports but exec fails on with
|
||||
# exit 127 (issue #48521). Validating lets a dead candidate fall through to
|
||||
# the next working resolution (extended PATH → local .bin → npx) instead of
|
||||
# caching the broken one and silently killing every browser tool.
|
||||
|
||||
# Check if it's in PATH (global install)
|
||||
which_result = shutil.which("agent-browser")
|
||||
if which_result:
|
||||
if which_result and agent_browser_runnable(which_result):
|
||||
_cached_agent_browser = which_result
|
||||
_agent_browser_resolved = True
|
||||
return which_result
|
||||
|
|
@ -1917,7 +1926,7 @@ def _find_agent_browser() -> str:
|
|||
extended_path = _merge_browser_path("")
|
||||
if extended_path:
|
||||
which_result = shutil.which("agent-browser", path=extended_path)
|
||||
if which_result:
|
||||
if which_result and agent_browser_runnable(which_result):
|
||||
_cached_agent_browser = which_result
|
||||
_agent_browser_resolved = True
|
||||
return which_result
|
||||
|
|
@ -1934,7 +1943,7 @@ def _find_agent_browser() -> str:
|
|||
local_bin_dir = repo_root / "node_modules" / ".bin"
|
||||
if local_bin_dir.is_dir():
|
||||
local_which = shutil.which("agent-browser", path=str(local_bin_dir))
|
||||
if local_which:
|
||||
if local_which and agent_browser_runnable(local_which):
|
||||
_cached_agent_browser = local_which
|
||||
_agent_browser_resolved = True
|
||||
return _cached_agent_browser
|
||||
|
|
@ -1952,22 +1961,18 @@ def _find_agent_browser() -> str:
|
|||
try:
|
||||
from hermes_cli.dep_ensure import ensure_dependency
|
||||
if ensure_dependency("browser"):
|
||||
recheck = shutil.which("agent-browser")
|
||||
if not recheck and extended_path:
|
||||
recheck = shutil.which("agent-browser", path=extended_path)
|
||||
if not recheck:
|
||||
hermes_nm = str(get_hermes_home() / "node_modules" / ".bin")
|
||||
recheck = shutil.which("agent-browser", path=hermes_nm)
|
||||
if not recheck:
|
||||
hermes_node_bin = str(get_hermes_home() / "node" / "bin")
|
||||
recheck = shutil.which("agent-browser", path=hermes_node_bin)
|
||||
if not recheck:
|
||||
hermes_node_root = str(get_hermes_home() / "node")
|
||||
recheck = shutil.which("agent-browser", path=hermes_node_root)
|
||||
if recheck:
|
||||
_cached_agent_browser = recheck
|
||||
_agent_browser_resolved = True
|
||||
return recheck
|
||||
candidates = [
|
||||
shutil.which("agent-browser"),
|
||||
shutil.which("agent-browser", path=extended_path) if extended_path else None,
|
||||
shutil.which("agent-browser", path=str(get_hermes_home() / "node_modules" / ".bin")),
|
||||
shutil.which("agent-browser", path=str(get_hermes_home() / "node" / "bin")),
|
||||
shutil.which("agent-browser", path=str(get_hermes_home() / "node")),
|
||||
]
|
||||
for recheck in candidates:
|
||||
if recheck and agent_browser_runnable(recheck):
|
||||
_cached_agent_browser = recheck
|
||||
_agent_browser_resolved = True
|
||||
return recheck
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue