From 2b685f08fad9f1191e8c0e3696a6b4319797ab49 Mon Sep 17 00:00:00 2001 From: teknium1 <127238744+teknium1@users.noreply.github.com> Date: Sat, 12 Sep 2026 08:11:30 -0700 Subject: [PATCH] fix(claw): detect the gateway's process title `openclaw-gateway` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Upstream sets process.title="openclaw-gateway" in the gateway run loop, so the real daemon has comm "openclaw-gatewa" (15-char truncation) and no `node … openclaw` argv for the script probe to match. Add the exact comm probe; substring matching stays out. --- hermes_cli/claw.py | 5 ++++- tests/hermes_cli/test_claw.py | 10 +++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/hermes_cli/claw.py b/hermes_cli/claw.py index a05320f52e..465417cc32 100644 --- a/hermes_cli/claw.py +++ b/hermes_cli/claw.py @@ -130,7 +130,10 @@ def _detect_openclaw_processes() -> list[str]: # ~/.openclaw/config.json, ``tail -f openclaw.log``) and aborted cleanup on idle hosts (#12648). # Mirror the Windows branch: exact binary names, plus node processes whose script mentions it. pids: list[str] = [] - for probe in (["pgrep", "-x", "openclaw"], ["pgrep", "-x", "clawd"], ["pgrep", "-f", _OPENCLAW_NODE_CMDLINE_RE]): + # ``-x`` matches the 15-char comm: the gateway sets process.title="openclaw-gateway", which + # the kernel truncates to "openclaw-gatewa". + for probe in (["pgrep", "-x", "openclaw"], ["pgrep", "-x", "openclaw-gatewa"], ["pgrep", "-x", "clawd"], + ["pgrep", "-f", _OPENCLAW_NODE_CMDLINE_RE]): result = _posix_probe(probe, 3) if result is not None and result.returncode == 0: pids.extend(result.stdout.split()) diff --git a/tests/hermes_cli/test_claw.py b/tests/hermes_cli/test_claw.py index 1cacde20cf..01af25c2dc 100644 --- a/tests/hermes_cli/test_claw.py +++ b/tests/hermes_cli/test_claw.py @@ -355,6 +355,7 @@ class TestDetectOpenclawProcesses: mock_subprocess.run.side_effect = [ MagicMock(returncode=1, stdout=""), # systemctl MagicMock(returncode=0, stdout="1234\n"), # pgrep -x openclaw + MagicMock(returncode=1, stdout=""), # pgrep -x openclaw-gatewa MagicMock(returncode=1, stdout=""), # pgrep -x clawd MagicMock(returncode=0, stdout="1234\n5678\n"), # node cmdline probe ] @@ -374,6 +375,12 @@ class TestDetectOpenclawProcesses: bystander = subprocess.Popen(["bash", "-c", f"exec {idle} {tmp_path}/openclaw-notes.txt"]) # argv[0] renamed to ``node`` running an openclaw script: the real launch shape. node_like = subprocess.Popen(["bash", "-c", f"exec -a node {idle} {tmp_path}/openclaw/entry.js"]) + # The gateway sets process.title="openclaw-gateway" (comm truncates to 15 chars); a + # copied interpreter with that file name yields the same comm. + import shutil + titled_bin = tmp_path / "openclaw-gateway" + shutil.copy2(sys.executable, titled_bin) + titled = subprocess.Popen([str(titled_bin), "-c", "import time; time.sleep(30)"]) try: time.sleep(0.3) with patch.object(claw_mod, "_posix_probe", wraps=claw_mod._posix_probe) as probe: @@ -382,9 +389,10 @@ class TestDetectOpenclawProcesses: assert len(result) == 1 pids = result[0].split("PIDs: ")[1].rstrip(")").split(", ") assert str(node_like.pid) in pids + assert str(titled.pid) in pids assert str(bystander.pid) not in pids finally: - for proc in (bystander, node_like): + for proc in (bystander, node_like, titled): proc.kill() proc.wait()