mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
fix(claw): address Copilot review on Windows detection and non-interactive prompt
- Use PowerShell to inspect node.exe command lines on Windows, since tasklist output does not include them. - Also check for dedicated openclaw.exe/clawd.exe processes. - Skip the interactive prompt in non-interactive sessions so the preview-only behavior is preserved. - Update tests accordingly. Relates to #7907
This commit is contained in:
parent
5af9614f6d
commit
9fb36738a7
2 changed files with 62 additions and 17 deletions
|
|
@ -57,12 +57,27 @@ def _is_openclaw_running() -> bool:
|
|||
"""Check whether an OpenClaw process appears to be running."""
|
||||
if sys.platform == "win32":
|
||||
try:
|
||||
# First check for dedicated executables
|
||||
for exe in ("openclaw.exe", "clawd.exe"):
|
||||
result = subprocess.run(
|
||||
["tasklist", "/FI", f"IMAGENAME eq {exe}"],
|
||||
capture_output=True, text=True, timeout=5
|
||||
)
|
||||
if exe in result.stdout.lower():
|
||||
return True
|
||||
|
||||
# Check node.exe processes for openclaw/clawd in command line.
|
||||
# tasklist does not include command lines, so we use PowerShell.
|
||||
ps_cmd = (
|
||||
'Get-CimInstance Win32_Process -Filter "Name = \'node.exe\'" | '
|
||||
'Where-Object { $_.CommandLine -match "openclaw|clawd" } | '
|
||||
'Select-Object -First 1 ProcessId'
|
||||
)
|
||||
result = subprocess.run(
|
||||
["tasklist", "/FI", "IMAGENAME eq node.exe"],
|
||||
["powershell", "-NoProfile", "-Command", ps_cmd],
|
||||
capture_output=True, text=True, timeout=5
|
||||
)
|
||||
output = result.stdout.lower()
|
||||
return "openclaw" in output or "clawd" in output
|
||||
return bool(result.stdout.strip())
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
|
@ -95,7 +110,12 @@ def _warn_if_openclaw_running(auto_yes: bool) -> None:
|
|||
)
|
||||
print_info("Recommendation: stop OpenClaw before migrating.")
|
||||
print()
|
||||
if not auto_yes and not prompt_yes_no("Continue anyway?", default=False):
|
||||
if auto_yes:
|
||||
return
|
||||
if not sys.stdin.isatty():
|
||||
print_info("Non-interactive session — continuing to preview only.")
|
||||
return
|
||||
if not prompt_yes_no("Continue anyway?", default=False):
|
||||
print_info("Migration cancelled. Stop OpenClaw and try again.")
|
||||
sys.exit(0)
|
||||
|
||||
|
|
|
|||
|
|
@ -663,24 +663,39 @@ class TestIsOpenclawRunning:
|
|||
]
|
||||
assert claw_mod._is_openclaw_running() is False
|
||||
|
||||
def test_returns_true_on_windows_tasklist(self):
|
||||
def test_returns_true_on_windows_when_openclaw_exe_running(self):
|
||||
with patch.object(claw_mod, "sys") as mock_sys:
|
||||
mock_sys.platform = "win32"
|
||||
with patch.object(claw_mod, "subprocess") as mock_subprocess:
|
||||
mock_subprocess.run.return_value = MagicMock(
|
||||
returncode=0,
|
||||
stdout="node.exe openclaw-gateway",
|
||||
)
|
||||
# First tasklist (openclaw.exe) matches
|
||||
mock_subprocess.run.side_effect = [
|
||||
MagicMock(returncode=0, stdout="openclaw.exe 1234 Console 1 45,056 K\n"),
|
||||
]
|
||||
assert claw_mod._is_openclaw_running() is True
|
||||
|
||||
def test_returns_false_on_windows_when_not_found(self):
|
||||
def test_returns_true_on_windows_when_node_exe_has_openclaw_in_cmdline(self):
|
||||
with patch.object(claw_mod, "sys") as mock_sys:
|
||||
mock_sys.platform = "win32"
|
||||
with patch.object(claw_mod, "subprocess") as mock_subprocess:
|
||||
mock_subprocess.run.return_value = MagicMock(
|
||||
returncode=0,
|
||||
stdout="node.exe some-other-app",
|
||||
)
|
||||
# tasklist for openclaw.exe and clawd.exe both miss,
|
||||
# PowerShell finds a matching node.exe process.
|
||||
mock_subprocess.run.side_effect = [
|
||||
MagicMock(returncode=0, stdout=""),
|
||||
MagicMock(returncode=0, stdout=""),
|
||||
MagicMock(returncode=0, stdout="1234\n"),
|
||||
]
|
||||
assert claw_mod._is_openclaw_running() is True
|
||||
|
||||
def test_returns_false_on_windows_when_node_exe_has_no_openclaw_in_cmdline(self):
|
||||
with patch.object(claw_mod, "sys") as mock_sys:
|
||||
mock_sys.platform = "win32"
|
||||
with patch.object(claw_mod, "subprocess") as mock_subprocess:
|
||||
# Neither dedicated exe nor PowerShell find anything.
|
||||
mock_subprocess.run.side_effect = [
|
||||
MagicMock(returncode=0, stdout=""),
|
||||
MagicMock(returncode=0, stdout=""),
|
||||
MagicMock(returncode=0, stdout=""),
|
||||
]
|
||||
assert claw_mod._is_openclaw_running() is False
|
||||
|
||||
|
||||
|
|
@ -694,8 +709,9 @@ class TestWarnIfOpenclawRunning:
|
|||
def test_warns_and_exits_when_running_and_user_declines(self, capsys):
|
||||
with patch.object(claw_mod, "_is_openclaw_running", return_value=True):
|
||||
with patch.object(claw_mod, "prompt_yes_no", return_value=False):
|
||||
with pytest.raises(SystemExit) as exc_info:
|
||||
claw_mod._warn_if_openclaw_running(auto_yes=False)
|
||||
with patch.object(claw_mod.sys.stdin, "isatty", return_value=True):
|
||||
with pytest.raises(SystemExit) as exc_info:
|
||||
claw_mod._warn_if_openclaw_running(auto_yes=False)
|
||||
assert exc_info.value.code == 0
|
||||
captured = capsys.readouterr()
|
||||
assert "OpenClaw appears to be running" in captured.out
|
||||
|
|
@ -703,7 +719,8 @@ class TestWarnIfOpenclawRunning:
|
|||
def test_warns_and_continues_when_running_and_user_accepts(self, capsys):
|
||||
with patch.object(claw_mod, "_is_openclaw_running", return_value=True):
|
||||
with patch.object(claw_mod, "prompt_yes_no", return_value=True):
|
||||
claw_mod._warn_if_openclaw_running(auto_yes=False)
|
||||
with patch.object(claw_mod.sys.stdin, "isatty", return_value=True):
|
||||
claw_mod._warn_if_openclaw_running(auto_yes=False)
|
||||
captured = capsys.readouterr()
|
||||
assert "OpenClaw appears to be running" in captured.out
|
||||
|
||||
|
|
@ -712,3 +729,11 @@ class TestWarnIfOpenclawRunning:
|
|||
claw_mod._warn_if_openclaw_running(auto_yes=True)
|
||||
captured = capsys.readouterr()
|
||||
assert "OpenClaw appears to be running" in captured.out
|
||||
|
||||
def test_warns_and_continues_in_non_interactive_session(self, capsys):
|
||||
with patch.object(claw_mod, "_is_openclaw_running", return_value=True):
|
||||
with patch.object(claw_mod.sys.stdin, "isatty", return_value=False):
|
||||
claw_mod._warn_if_openclaw_running(auto_yes=False)
|
||||
captured = capsys.readouterr()
|
||||
assert "OpenClaw appears to be running" in captured.out
|
||||
assert "Non-interactive session" in captured.out
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue