mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-29 18:46:59 +00:00
fix(doctor): detect agent-browser in the Hermes-managed node bin so setup-browser installs aren't reported as missing (#53192)
`hermes acp --setup-browser` installs agent-browser into the Hermes-managed node prefix (~/.hermes/node/bin/agent-browser), which isn't necessarily on PATH. doctor only checked PROJECT_ROOT/node_modules and PATH (shutil.which), so it false-negatived with "agent-browser not installed" even though the binary was present and runnable. Mirror dep_ensure._has_hermes_agent_browser() by also checking HERMES_HOME/node/bin and the legacy HERMES_HOME/node_modules/.bin path, each gated by agent_browser_runnable(). Tested with tests/hermes_cli/test_doctor.py (added positive + not-runnable cases) and pytest tests/hermes_cli/test_doctor.py -q (66 passed).
This commit is contained in:
parent
7d3075d0d5
commit
1cec6afdfc
2 changed files with 79 additions and 0 deletions
|
|
@ -1813,12 +1813,24 @@ def run_doctor(args):
|
|||
agent_browser_path = PROJECT_ROOT / "node_modules" / "agent-browser"
|
||||
agent_browser_ok = False
|
||||
_which_ab = shutil.which("agent-browser")
|
||||
# `hermes acp --setup-browser` installs agent-browser into the
|
||||
# Hermes-managed node prefix, which isn't necessarily on PATH. Mirror
|
||||
# dep_ensure._has_hermes_agent_browser() so doctor and dep_ensure agree
|
||||
# on what "installed" means; otherwise doctor false-negatives (#53192).
|
||||
_managed_ab = HERMES_HOME / "node" / "bin" / "agent-browser"
|
||||
_legacy_ab = HERMES_HOME / "node_modules" / ".bin" / "agent-browser"
|
||||
if agent_browser_path.exists():
|
||||
check_ok("agent-browser (Node.js)", "(browser automation)")
|
||||
agent_browser_ok = True
|
||||
elif _which_ab and agent_browser_runnable(_which_ab):
|
||||
check_ok("agent-browser", "(browser automation)")
|
||||
agent_browser_ok = True
|
||||
elif _managed_ab.is_file() and agent_browser_runnable(str(_managed_ab)):
|
||||
check_ok("agent-browser", "(browser automation)")
|
||||
agent_browser_ok = True
|
||||
elif _legacy_ab.is_file() and agent_browser_runnable(str(_legacy_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
|
||||
|
|
|
|||
|
|
@ -742,6 +742,73 @@ def test_run_doctor_termux_does_not_mark_browser_available_without_agent_browser
|
|||
assert "npm install -g agent-browser && agent-browser install" in out
|
||||
|
||||
|
||||
def _run_doctor_with_managed_agent_browser(monkeypatch, tmp_path, runnable):
|
||||
"""Set up run_doctor with node present, agent-browser only in the
|
||||
Hermes-managed node bin (~/.hermes/node/bin), not on PATH or in
|
||||
PROJECT_ROOT/node_modules. Returns the captured stdout."""
|
||||
home = tmp_path / ".hermes"
|
||||
(home / "node" / "bin").mkdir(parents=True, exist_ok=True)
|
||||
(home / "config.yaml").write_text("memory: {}\n", encoding="utf-8")
|
||||
managed_ab = home / "node" / "bin" / "agent-browser"
|
||||
managed_ab.write_text("#!/bin/sh\n", encoding="utf-8")
|
||||
project = tmp_path / "project"
|
||||
project.mkdir(exist_ok=True) # no node_modules/agent-browser here
|
||||
|
||||
monkeypatch.delenv("TERMUX_VERSION", raising=False)
|
||||
monkeypatch.delenv("PREFIX", raising=False)
|
||||
monkeypatch.setattr(doctor_mod, "HERMES_HOME", home)
|
||||
monkeypatch.setattr(doctor_mod, "PROJECT_ROOT", project)
|
||||
monkeypatch.setattr(doctor_mod, "_DHH", str(home))
|
||||
# node on PATH, agent-browser is NOT on PATH (only in the managed bin)
|
||||
monkeypatch.setattr(
|
||||
doctor_mod.shutil,
|
||||
"which",
|
||||
lambda cmd: "/usr/bin/node" if cmd in {"node", "npm"} else None,
|
||||
)
|
||||
# agent_browser_runnable is imported into doctor's namespace
|
||||
monkeypatch.setattr(
|
||||
doctor_mod,
|
||||
"agent_browser_runnable",
|
||||
lambda path: runnable and str(path) == str(managed_ab),
|
||||
)
|
||||
|
||||
fake_model_tools = types.SimpleNamespace(
|
||||
check_tool_availability=lambda *a, **kw: ([], []),
|
||||
TOOLSET_REQUIREMENTS={},
|
||||
)
|
||||
monkeypatch.setitem(sys.modules, "model_tools", fake_model_tools)
|
||||
|
||||
try:
|
||||
from hermes_cli import auth as _auth_mod
|
||||
monkeypatch.setattr(_auth_mod, "get_nous_auth_status", lambda: {})
|
||||
monkeypatch.setattr(_auth_mod, "get_codex_auth_status", lambda: {})
|
||||
monkeypatch.setattr(_auth_mod, "get_xai_oauth_auth_status", lambda: {})
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
buf = io.StringIO()
|
||||
with contextlib.redirect_stdout(buf):
|
||||
doctor_mod.run_doctor(Namespace(fix=False))
|
||||
return buf.getvalue()
|
||||
|
||||
|
||||
def test_run_doctor_detects_agent_browser_in_managed_node_bin(monkeypatch, tmp_path):
|
||||
# Regression for #53192: `hermes acp --setup-browser` installs into
|
||||
# ~/.hermes/node/bin/agent-browser, which isn't on PATH; doctor must still
|
||||
# report it installed instead of "agent-browser not installed".
|
||||
out = _run_doctor_with_managed_agent_browser(monkeypatch, tmp_path, runnable=True)
|
||||
assert "agent-browser not installed" not in out
|
||||
assert "agent-browser found but not runnable" not in out
|
||||
assert "✓ agent-browser" in out
|
||||
|
||||
|
||||
def test_run_doctor_managed_agent_browser_not_runnable_still_warns(monkeypatch, tmp_path):
|
||||
# A present-but-unrunnable managed binary must fall through to the existing
|
||||
# warning, not be reported as OK.
|
||||
out = _run_doctor_with_managed_agent_browser(monkeypatch, tmp_path, runnable=False)
|
||||
assert "agent-browser not installed" in out
|
||||
|
||||
|
||||
def test_run_doctor_kimi_cn_env_is_detected_and_probe_is_null_safe(monkeypatch, tmp_path):
|
||||
home = tmp_path / ".hermes"
|
||||
home.mkdir(parents=True, exist_ok=True)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue