Skip to content

Commit d69eab1

Browse files
alt-glitchteknium1
authored andcommitted
fix(gateway): build service PATH from existing dirs only, include ~/.hermes/node_modules
Extract PATH building into _build_service_path_dirs() that skips directories which don't exist on disk (e.g. node_modules/.bin for pip installs) and also includes ~/.hermes/node/bin and ~/.hermes/node_modules/.bin for agent-browser.
1 parent c4bda3f commit d69eab1

2 files changed

Lines changed: 61 additions & 8 deletions

File tree

hermes_cli/gateway.py

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2103,15 +2103,41 @@ def _hermes_home_for_target_user(target_home_dir: str) -> str:
21032103
return str(current_hermes)
21042104

21052105

2106+
def _build_service_path_dirs(project_root: Path | None = None) -> list[str]:
2107+
"""Build PATH directory list for service units, excluding non-existent dirs."""
2108+
if project_root is None:
2109+
project_root = PROJECT_ROOT
2110+
2111+
candidates = []
2112+
2113+
venv_bin = project_root / "venv" / "bin"
2114+
if venv_bin.is_dir():
2115+
candidates.append(str(venv_bin))
2116+
elif sys.prefix != sys.base_prefix:
2117+
candidates.append(str(Path(sys.prefix) / "bin"))
2118+
2119+
node_bin = project_root / "node_modules" / ".bin"
2120+
if node_bin.is_dir():
2121+
candidates.append(str(node_bin))
2122+
2123+
hermes_home = get_hermes_home()
2124+
hermes_node = hermes_home / "node" / "bin"
2125+
if hermes_node.is_dir():
2126+
candidates.append(str(hermes_node))
2127+
hermes_nm = hermes_home / "node_modules" / ".bin"
2128+
if hermes_nm.is_dir():
2129+
candidates.append(str(hermes_nm))
2130+
2131+
return candidates
2132+
2133+
21062134
def generate_systemd_unit(system: bool = False, run_as_user: str | None = None) -> str:
21072135
python_path = get_python_path()
21082136
working_dir = str(PROJECT_ROOT)
21092137
detected_venv = _detect_venv_dir()
21102138
venv_dir = str(detected_venv) if detected_venv else str(PROJECT_ROOT / "venv")
2111-
venv_bin = str(detected_venv / "bin") if detected_venv else str(PROJECT_ROOT / "venv" / "bin")
2112-
node_bin = str(PROJECT_ROOT / "node_modules" / ".bin")
21132139

2114-
path_entries = [venv_bin, node_bin]
2140+
path_entries = _build_service_path_dirs()
21152141
resolved_node = shutil.which("node")
21162142
if resolved_node:
21172143
resolved_node_dir = str(Path(resolved_node).resolve().parent)
@@ -2138,8 +2164,6 @@ def generate_systemd_unit(system: bool = False, run_as_user: str | None = None)
21382164
python_path = _remap_path_for_user(python_path, home_dir)
21392165
working_dir = _remap_path_for_user(working_dir, home_dir)
21402166
venv_dir = _remap_path_for_user(venv_dir, home_dir)
2141-
venv_bin = _remap_path_for_user(venv_bin, home_dir)
2142-
node_bin = _remap_path_for_user(node_bin, home_dir)
21432167
path_entries = [_remap_path_for_user(p, home_dir) for p in path_entries]
21442168
path_entries.extend(_build_user_local_paths(Path(home_dir), path_entries))
21452169
path_entries.extend(_build_wsl_interop_paths(path_entries))
@@ -2754,12 +2778,10 @@ def generate_launchd_plist() -> str:
27542778
# the systemd unit), then capture the user's full shell PATH so every
27552779
# user-installed tool (node, ffmpeg, …) is reachable.
27562780
detected_venv = _detect_venv_dir()
2757-
venv_bin = str(detected_venv / "bin") if detected_venv else str(PROJECT_ROOT / "venv" / "bin")
27582781
venv_dir = str(detected_venv) if detected_venv else str(PROJECT_ROOT / "venv")
2759-
node_bin = str(PROJECT_ROOT / "node_modules" / ".bin")
27602782
# Resolve the directory containing the node binary (e.g. Homebrew, nvm)
27612783
# so it's explicitly in PATH even if the user's shell PATH changes later.
2762-
priority_dirs = [venv_bin, node_bin]
2784+
priority_dirs = _build_service_path_dirs()
27632785
resolved_node = shutil.which("node")
27642786
if resolved_node:
27652787
resolved_node_dir = str(Path(resolved_node).resolve().parent)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from pathlib import Path
2+
from unittest.mock import patch
3+
4+
5+
def test_service_path_skips_nonexistent_node_modules(tmp_path):
6+
"""Service PATH should not include node_modules/.bin if it doesn't exist."""
7+
from hermes_cli.gateway import _build_service_path_dirs
8+
with patch("hermes_cli.gateway.get_hermes_home", return_value=tmp_path / ".hermes"):
9+
dirs = _build_service_path_dirs(project_root=tmp_path)
10+
node_modules_bin = str(tmp_path / "node_modules" / ".bin")
11+
assert node_modules_bin not in dirs
12+
13+
14+
def test_service_path_includes_node_modules_when_present(tmp_path):
15+
"""Service PATH should include node_modules/.bin when it exists."""
16+
nm_bin = tmp_path / "node_modules" / ".bin"
17+
nm_bin.mkdir(parents=True)
18+
from hermes_cli.gateway import _build_service_path_dirs
19+
with patch("hermes_cli.gateway.get_hermes_home", return_value=tmp_path / ".hermes"):
20+
dirs = _build_service_path_dirs(project_root=tmp_path)
21+
assert str(nm_bin) in dirs
22+
23+
24+
def test_service_path_includes_hermes_home_node_modules(tmp_path):
25+
"""Service PATH should include ~/.hermes/node_modules/.bin when it exists."""
26+
hermes_nm = tmp_path / ".hermes" / "node_modules" / ".bin"
27+
hermes_nm.mkdir(parents=True)
28+
from hermes_cli.gateway import _build_service_path_dirs
29+
with patch("hermes_cli.gateway.get_hermes_home", return_value=tmp_path / ".hermes"):
30+
dirs = _build_service_path_dirs(project_root=tmp_path)
31+
assert str(hermes_nm) in dirs

0 commit comments

Comments
 (0)