fix(tui): prepend Hermes venv and user-local bin to slash_worker PATH (#83845)
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
"""Regression test for slash_worker PATH construction (#83845).
|
||||
|
||||
When the gateway is launched by the Desktop/Dashboard app it can inherit a
|
||||
minimal PATH (/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:/sbin:/usr/sbin)
|
||||
that omits the Hermes venv bin dir and ~/.local/bin. The spawned
|
||||
tui_gateway.slash_worker then cannot resolve Hermes-managed CLIs such as
|
||||
browser-use/uvx via shutil.which, breaking browser_exec.
|
||||
|
||||
`tui_gateway.server._prepend_tool_paths` prepends those two directories to
|
||||
the worker env PATH while preserving the inherited PATH.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from tui_gateway import server as tui_server
|
||||
|
||||
|
||||
class TestPrependToolPaths:
|
||||
def test_prepends_venv_and_user_bin(self):
|
||||
env = {"PATH": "/usr/bin"}
|
||||
result = tui_server._prepend_tool_paths(env)
|
||||
|
||||
parts = result["PATH"].split(os.pathsep)
|
||||
# venv bin first, then user-local bin, then the original PATH preserved
|
||||
assert parts[0] == str(Path(sys.executable).parent)
|
||||
assert str(Path.home() / ".local" / "bin") in parts
|
||||
assert parts[-1] == "/usr/bin"
|
||||
|
||||
def test_preserves_existing_path_when_empty(self):
|
||||
env = {}
|
||||
result = tui_server._prepend_tool_paths(env)
|
||||
|
||||
parts = result["PATH"].split(os.pathsep)
|
||||
assert parts[0] == str(Path(sys.executable).parent)
|
||||
assert str(Path.home() / ".local" / "bin") in parts
|
||||
|
||||
def test_venv_bin_points_at_sys_executable_dir(self):
|
||||
env = {"PATH": "/bin"}
|
||||
result = tui_server._prepend_tool_paths(env)
|
||||
assert result["PATH"].split(os.pathsep)[0] == str(Path(sys.executable).parent)
|
||||
@@ -368,6 +368,20 @@ _stdio_transport = StdioTransport(lambda: _real_stdout, _stdout_lock)
|
||||
_detached_ws_transport = _DropTransport()
|
||||
|
||||
|
||||
def _prepend_tool_paths(env: dict[str, str]) -> dict[str, str]:
|
||||
"""Prepend the Hermes venv bin dir and the user-local bin dir to PATH
|
||||
so slash_worker child processes can resolve Hermes-managed CLIs
|
||||
(browser-use, uvx) even when the parent gateway was launched with a
|
||||
minimal PATH (e.g. by the Desktop/Dashboard app)."""
|
||||
venv_bin = str(Path(sys.executable).parent) # <venv>/bin (POSIX) or <venv>/Scripts (Windows)
|
||||
user_bin = str(Path.home() / ".local" / "bin")
|
||||
existing = env.get("PATH") or ""
|
||||
env["PATH"] = os.pathsep.join(
|
||||
[p for p in (venv_bin, user_bin) if p] + ([existing] if existing else [])
|
||||
)
|
||||
return env
|
||||
|
||||
|
||||
class _SlashWorker:
|
||||
"""Persistent HermesCLI subprocess for slash commands."""
|
||||
|
||||
@@ -404,6 +418,11 @@ class _SlashWorker:
|
||||
inherit_profile_home=False, # base already carries the HOME contract
|
||||
extra={"HERMES_HOME": str(profile_home)} if profile_home else None,
|
||||
)
|
||||
# Prepend the Hermes venv bin dir and the user-local bin dir to PATH so
|
||||
# slash_worker child processes can resolve Hermes-managed CLIs
|
||||
# (browser-use, uvx) even when the parent gateway was launched with a
|
||||
# minimal PATH (e.g. by the Desktop/Dashboard app). See #83845.
|
||||
env = _prepend_tool_paths(env)
|
||||
|
||||
# start_new_session=True detaches the slash worker into its own
|
||||
# process group / session. Without this, the worker inherits the
|
||||
|
||||
Reference in New Issue
Block a user