Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions hermes_cli/gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,7 @@ def _matches_gateway_runtime(command: str) -> bool:
current_cmd = ""
else:
# Try /proc first (works in Docker without procps installed),
# fall back to ps -A eww.
# fall back to a platform-appropriate ps invocation.
_found_via_proc = False
if os.path.isdir("/proc"):
try:
Expand All @@ -501,8 +501,17 @@ def _matches_gateway_runtime(command: str) -> bool:
pass

if not _found_via_proc:
# macOS `ps` doesn't accept the BSD `e` flag the way Linux does,
# so we can't surface env vars there. As a result, gateways started
# on macOS with only `HERMES_HOME=<path>` (no `--profile` flag)
# won't be attributable to a named profile via `_matches_current_profile`.
ps_command = (
["ps", "-Aww", "-o", "pid=,command="]
if is_macos()
else ["ps", "-A", "eww", "-o", "pid=,command="]
)
result = subprocess.run(
["ps", "-A", "eww", "-o", "pid=,command="],
ps_command,
capture_output=True,
text=True, encoding='utf-8', errors='replace',
timeout=10,
Expand Down
49 changes: 49 additions & 0 deletions tests/hermes_cli/test_gateway_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -1551,6 +1551,55 @@ def fake_run(*args, **kwargs):

assert gateway_cli._is_service_running() is False

class TestScanGatewayPidsPsSelection:
"""The ps fallback in _scan_gateway_pids must use macOS-valid arguments.

macOS `ps` rejects the BSD `e` flag in the `-A eww` invocation, so when
/proc is unavailable (always true on macOS) the scan silently found
nothing and gateway/cron status reported a live gateway as down.
"""

def _force_ps_fallback(self, monkeypatch, macos, home):
monkeypatch.setattr(gateway_cli, "is_macos", lambda: macos)
monkeypatch.setattr(gateway_cli, "is_windows", lambda: False)
monkeypatch.setattr(gateway_cli, "get_hermes_home", lambda: Path(home))
monkeypatch.setattr(gateway_cli, "_get_ancestor_pids", lambda: set())

real_isdir = os.path.isdir
monkeypatch.setattr(
gateway_cli.os.path,
"isdir",
lambda path: False if path == "/proc" else real_isdir(path),
)

calls = []

def fake_run(cmd, capture_output=True, text=True, timeout=10, **kwargs):
calls.append(cmd)
return SimpleNamespace(
returncode=0,
stdout=(
f"12345 {home}/hermes-agent/venv/bin/python"
" -m hermes_cli.main gateway run --replace\n"
),
stderr="",
)

monkeypatch.setattr(gateway_cli.subprocess, "run", fake_run)
return calls

def test_scan_uses_macos_ps_args_when_proc_unavailable(self, monkeypatch):
calls = self._force_ps_fallback(monkeypatch, macos=True, home="/Users/test/.hermes")

assert gateway_cli._scan_gateway_pids(set()) == [12345]
assert calls == [["ps", "-Aww", "-o", "pid=,command="]]

def test_scan_keeps_linux_ps_args_when_proc_unavailable(self, monkeypatch):
calls = self._force_ps_fallback(monkeypatch, macos=False, home="/home/test/.hermes")

assert gateway_cli._scan_gateway_pids(set()) == [12345]
assert calls == [["ps", "-A", "eww", "-o", "pid=,command="]]

class TestGatewaySystemServiceRouting:
def test_systemd_restart_gracefully_restarts_running_service_and_waits(self, monkeypatch, capsys):
calls = []
Expand Down