diff --git a/sdk/src/flowmesh/ssh.py b/sdk/src/flowmesh/ssh.py index d67f30f..95f6258 100644 --- a/sdk/src/flowmesh/ssh.py +++ b/sdk/src/flowmesh/ssh.py @@ -8,6 +8,7 @@ import yaml +from ._constants import API_VERSION_PREFIX from .exceptions import FlowMeshError from .models.common import TaskStatus from .models.tasks import TaskInfo @@ -159,9 +160,8 @@ def ssh_proxy_url(base_url: str, task_id: str) -> str: """Build the websocket proxy URL for an SSH task.""" base = urlsplit(base_url) ws_scheme = "wss" if base.scheme == "https" else "ws" - return urlunsplit( - (ws_scheme, base.netloc, f"/api/v1/ssh/tasks/{task_id}/proxy", "", "") - ) + path = f"{base.path.rstrip('/')}{API_VERSION_PREFIX}/ssh/tasks/{task_id}/proxy" + return urlunsplit((ws_scheme, base.netloc, path, "", "")) def ssh_connection_commands( diff --git a/tests/sdk/test_resource_helpers.py b/tests/sdk/test_resource_helpers.py index 131f388..c3c2f22 100644 --- a/tests/sdk/test_resource_helpers.py +++ b/tests/sdk/test_resource_helpers.py @@ -317,6 +317,38 @@ def test_proxy_url_and_connection_commands(self) -> None: cmds = ssh_connection_commands("task-1", ssh_info, base_url=base_url) assert isinstance(cmds, list) + def test_proxy_url_root_mounted(self) -> None: + assert ( + ssh_proxy_url("https://example.com", "task-1") + == "wss://example.com/api/v1/ssh/tasks/task-1/proxy" + ) + + def test_proxy_url_preserves_base_path(self) -> None: + assert ( + ssh_proxy_url("https://example.com:8000/flowmesh", "task-1") + == "wss://example.com:8000/flowmesh/api/v1/ssh/tasks/task-1/proxy" + ) + + def test_proxy_url_scheme_conversion(self) -> None: + assert ssh_proxy_url("http://localhost:8000", "t").startswith("ws://") + assert ssh_proxy_url("https://localhost:8000", "t").startswith("wss://") + + def test_proxy_url_trailing_slash(self) -> None: + assert ( + ssh_proxy_url("https://example.com:8000/flowmesh/", "task-1") + == "wss://example.com:8000/flowmesh/api/v1/ssh/tasks/task-1/proxy" + ) + + def test_connection_commands_proxy_mode_embeds_base_path(self) -> None: + ssh_info = {"mode": "proxy", "username": "flowmesh", "host": "h", "port": 22} + cmds = ssh_connection_commands( + "task-1", ssh_info, base_url="https://example.com:8000/flowmesh" + ) + proxy_cmd = next(cmd for label, cmd in cmds if label == "ssh (proxy)") + assert ( + "wss://example.com:8000/flowmesh/api/v1/ssh/tasks/task-1/proxy" in proxy_cmd + ) + def test_detect_public_key_prefers_standard_keys(self, tmp_path: Path) -> None: ssh_dir = tmp_path / ".ssh" ssh_dir.mkdir()