mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-07 02:51:50 +00:00
fix(gateway): preserve WSL interop PATH in systemd units
This commit is contained in:
parent
d90f73bcec
commit
8ab9f61dcf
2 changed files with 79 additions and 0 deletions
|
|
@ -1608,6 +1608,46 @@ def _build_user_local_paths(home: Path, path_entries: list[str]) -> list[str]:
|
||||||
return [p for p in candidates if p not in path_entries and Path(p).exists()]
|
return [p for p in candidates if p not in path_entries and Path(p).exists()]
|
||||||
|
|
||||||
|
|
||||||
|
def _build_wsl_interop_paths(path_entries: list[str]) -> list[str]:
|
||||||
|
"""Return WSL Windows interop PATH entries for generated systemd units.
|
||||||
|
|
||||||
|
WSL shells normally inherit Windows PATH entries such as
|
||||||
|
``/mnt/c/WINDOWS/System32``. systemd user services do not, so gateway tools
|
||||||
|
that call ``powershell.exe``/``cmd.exe`` work in a terminal but fail in the
|
||||||
|
background service unless we persist the relevant entries at install time.
|
||||||
|
"""
|
||||||
|
if not is_wsl():
|
||||||
|
return []
|
||||||
|
|
||||||
|
candidates: list[str] = []
|
||||||
|
for entry in os.environ.get("PATH", "").split(os.pathsep):
|
||||||
|
if entry.startswith("/mnt/"):
|
||||||
|
candidates.append(entry)
|
||||||
|
|
||||||
|
for executable in ("powershell.exe", "cmd.exe", "explorer.exe", "wsl.exe"):
|
||||||
|
resolved = shutil.which(executable)
|
||||||
|
if resolved:
|
||||||
|
candidates.append(str(Path(resolved).parent))
|
||||||
|
|
||||||
|
for entry in (
|
||||||
|
"/mnt/c/WINDOWS/system32",
|
||||||
|
"/mnt/c/WINDOWS",
|
||||||
|
"/mnt/c/WINDOWS/System32/Wbem",
|
||||||
|
"/mnt/c/WINDOWS/System32/WindowsPowerShell/v1.0/",
|
||||||
|
"/mnt/c/WINDOWS/System32/OpenSSH/",
|
||||||
|
):
|
||||||
|
if Path(entry).exists():
|
||||||
|
candidates.append(entry)
|
||||||
|
|
||||||
|
result: list[str] = []
|
||||||
|
seen = set(path_entries)
|
||||||
|
for entry in candidates:
|
||||||
|
if entry and entry not in seen:
|
||||||
|
seen.add(entry)
|
||||||
|
result.append(entry)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
def _remap_path_for_user(path: str, target_home_dir: str) -> str:
|
def _remap_path_for_user(path: str, target_home_dir: str) -> str:
|
||||||
"""Remap *path* from the current user's home to *target_home_dir*.
|
"""Remap *path* from the current user's home to *target_home_dir*.
|
||||||
|
|
||||||
|
|
@ -1699,6 +1739,7 @@ def generate_systemd_unit(system: bool = False, run_as_user: str | None = None)
|
||||||
node_bin = _remap_path_for_user(node_bin, home_dir)
|
node_bin = _remap_path_for_user(node_bin, home_dir)
|
||||||
path_entries = [_remap_path_for_user(p, home_dir) for p in path_entries]
|
path_entries = [_remap_path_for_user(p, home_dir) for p in path_entries]
|
||||||
path_entries.extend(_build_user_local_paths(Path(home_dir), path_entries))
|
path_entries.extend(_build_user_local_paths(Path(home_dir), path_entries))
|
||||||
|
path_entries.extend(_build_wsl_interop_paths(path_entries))
|
||||||
path_entries.extend(common_bin_paths)
|
path_entries.extend(common_bin_paths)
|
||||||
sane_path = ":".join(path_entries)
|
sane_path = ":".join(path_entries)
|
||||||
return f"""[Unit]
|
return f"""[Unit]
|
||||||
|
|
@ -1738,6 +1779,7 @@ WantedBy=multi-user.target
|
||||||
hermes_home = str(get_hermes_home().resolve())
|
hermes_home = str(get_hermes_home().resolve())
|
||||||
profile_arg = _profile_arg(hermes_home)
|
profile_arg = _profile_arg(hermes_home)
|
||||||
path_entries.extend(_build_user_local_paths(Path.home(), path_entries))
|
path_entries.extend(_build_user_local_paths(Path.home(), path_entries))
|
||||||
|
path_entries.extend(_build_wsl_interop_paths(path_entries))
|
||||||
path_entries.extend(common_bin_paths)
|
path_entries.extend(common_bin_paths)
|
||||||
sane_path = ":".join(path_entries)
|
sane_path = ":".join(path_entries)
|
||||||
return f"""[Unit]
|
return f"""[Unit]
|
||||||
|
|
|
||||||
|
|
@ -182,6 +182,43 @@ class TestGeneratedSystemdUnits:
|
||||||
|
|
||||||
assert "/home/test/.nvm/versions/node/v24.14.0/bin" in unit
|
assert "/home/test/.nvm/versions/node/v24.14.0/bin" in unit
|
||||||
|
|
||||||
|
def test_user_unit_includes_wsl_windows_interop_paths(self, monkeypatch):
|
||||||
|
monkeypatch.setattr(gateway_cli, "is_wsl", lambda: True)
|
||||||
|
monkeypatch.setenv(
|
||||||
|
"PATH",
|
||||||
|
"/usr/local/bin:/mnt/c/WINDOWS/system32:/mnt/c/WINDOWS/System32/WindowsPowerShell/v1.0/",
|
||||||
|
)
|
||||||
|
monkeypatch.setattr(gateway_cli.shutil, "which", lambda cmd: None)
|
||||||
|
|
||||||
|
unit = gateway_cli.generate_systemd_unit(system=False)
|
||||||
|
|
||||||
|
assert "/mnt/c/WINDOWS/system32" in unit
|
||||||
|
assert "/mnt/c/WINDOWS/System32/WindowsPowerShell/v1.0/" in unit
|
||||||
|
|
||||||
|
def test_user_unit_omits_windows_interop_paths_outside_wsl(self, monkeypatch):
|
||||||
|
monkeypatch.setattr(gateway_cli, "is_wsl", lambda: False)
|
||||||
|
monkeypatch.setenv("PATH", "/usr/local/bin:/mnt/c/WINDOWS/system32")
|
||||||
|
monkeypatch.setattr(gateway_cli.shutil, "which", lambda cmd: None)
|
||||||
|
|
||||||
|
unit = gateway_cli.generate_systemd_unit(system=False)
|
||||||
|
|
||||||
|
assert "/mnt/c/WINDOWS/system32" not in unit
|
||||||
|
|
||||||
|
def test_system_unit_includes_wsl_windows_interop_paths(self, monkeypatch):
|
||||||
|
monkeypatch.setattr(gateway_cli, "is_wsl", lambda: True)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
gateway_cli,
|
||||||
|
"_system_service_identity",
|
||||||
|
lambda run_as_user=None: ("alice", "alice", "/home/alice"),
|
||||||
|
)
|
||||||
|
monkeypatch.setattr(gateway_cli, "_hermes_home_for_target_user", lambda home: "/home/alice/.hermes")
|
||||||
|
monkeypatch.setenv("PATH", "/usr/local/bin:/mnt/c/WINDOWS/system32")
|
||||||
|
monkeypatch.setattr(gateway_cli.shutil, "which", lambda cmd: None)
|
||||||
|
|
||||||
|
unit = gateway_cli.generate_systemd_unit(system=True, run_as_user="alice")
|
||||||
|
|
||||||
|
assert "/mnt/c/WINDOWS/system32" in unit
|
||||||
|
|
||||||
def test_system_unit_avoids_recursive_execstop_and_uses_extended_stop_timeout(self):
|
def test_system_unit_avoids_recursive_execstop_and_uses_extended_stop_timeout(self):
|
||||||
unit = gateway_cli.generate_systemd_unit(system=True)
|
unit = gateway_cli.generate_systemd_unit(system=True)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue