From af40fa1548cde7630a00aaa27c78d03c4524fa25 Mon Sep 17 00:00:00 2001 From: Michael Goberling Date: Thu, 4 Jun 2026 12:01:14 -0400 Subject: [PATCH 1/2] chore(sandbox): align with design for plural sandbox path --- src/aio_lib_sandbox/http.py | 2 +- src/aio_lib_sandbox/sandbox.py | 6 +++--- tests/test_sandbox.py | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/aio_lib_sandbox/http.py b/src/aio_lib_sandbox/http.py index 33ae29f..4bfc39c 100644 --- a/src/aio_lib_sandbox/http.py +++ b/src/aio_lib_sandbox/http.py @@ -46,7 +46,7 @@ def normalize_api_host(host: str) -> str: def build_ws_endpoint(api_host: str, namespace: str, sandbox_id: str) -> str: parsed = urlparse(api_host) ws_scheme = "ws" if parsed.scheme == "http" else "wss" - path = f"/ws/v1/namespaces/{namespace}/sandbox/{sandbox_id}/exec" + path = f"/ws/v1/namespaces/{namespace}/sandboxes/{sandbox_id}/exec" return urlunparse((ws_scheme, parsed.netloc, path, "", "", "")) diff --git a/src/aio_lib_sandbox/sandbox.py b/src/aio_lib_sandbox/sandbox.py index e4f142b..e9d4849 100644 --- a/src/aio_lib_sandbox/sandbox.py +++ b/src/aio_lib_sandbox/sandbox.py @@ -150,7 +150,7 @@ async def create( if ports is not None: body["ports"] = ports - url = f"{creds['api_host']}/api/v1/namespaces/{creds['namespace']}/sandbox" + url = f"{creds['api_host']}/api/v1/namespaces/{creds['namespace']}/sandboxes" payload = await api_request( "POST", url, @@ -211,7 +211,7 @@ async def get( This instance is **not** WebSocket-connected. """ creds = cls.resolve_credentials(api_host=api_host, namespace=namespace, auth=auth) - url = f"{creds['api_host']}/api/v1/namespaces/{creds['namespace']}/sandbox/{sandbox_id}" + url = f"{creds['api_host']}/api/v1/namespaces/{creds['namespace']}/sandboxes/{sandbox_id}" payload = await api_request( "GET", url, @@ -488,7 +488,7 @@ async def destroy(self) -> dict[str, Any]: The destroy response payload. """ base = self.management_endpoint or self.api_host - url = f"{base}/api/v1/namespaces/{self.namespace}/sandbox/{self.id}" + url = f"{base}/api/v1/namespaces/{self.namespace}/sandboxes/{self.id}" headers = {"Authorization": build_auth_header(self.api_key)} if self.session: self.session.begin_intentional_close() diff --git a/tests/test_sandbox.py b/tests/test_sandbox.py index a014b81..0d2230a 100644 --- a/tests/test_sandbox.py +++ b/tests/test_sandbox.py @@ -41,7 +41,7 @@ BASE_OPTS = dict( sandbox_id="sb-test", - endpoint="wss://runtime.example.net/ws/v1/namespaces/ns/sandbox/sb-test/exec", + endpoint="wss://runtime.example.net/ws/v1/namespaces/ns/sandboxes/sb-test/exec", status="ready", namespace="ns", api_host="https://runtime.example.net", @@ -189,7 +189,7 @@ class TestSandboxCreate: async def test_create_calls_api_and_connects(self, monkeypatch): payload = { "sandboxId": "sb-new", - "wsEndpoint": "wss://runtime.example.net/ws/v1/namespaces/ns/sandbox/sb-new/exec", + "wsEndpoint": "wss://runtime.example.net/ws/v1/namespaces/ns/sandboxes/sb-new/exec", "status": "ready", "token": "tok-new", "maxLifetime": 3600, @@ -220,7 +220,7 @@ async def test_create_forwards_policy(self, monkeypatch): policy = {"network": {"egress": [{"host": "api.github.com", "port": 443}]}} payload = { "sandboxId": "sb-pol", - "wsEndpoint": "wss://runtime.example.net/ws/v1/namespaces/ns/sandbox/sb-pol/exec", + "wsEndpoint": "wss://runtime.example.net/ws/v1/namespaces/ns/sandboxes/sb-pol/exec", "status": "ready", "token": "tok-pol", "maxLifetime": 3600, @@ -247,7 +247,7 @@ async def test_create_reads_env_vars(self, monkeypatch): payload = { "sandboxId": "sb-env", - "wsEndpoint": "wss://runtime.example.net/ws/v1/namespaces/ns/sandbox/sb-env/exec", + "wsEndpoint": "wss://runtime.example.net/ws/v1/namespaces/ns/sandboxes/sb-env/exec", "status": "ready", "token": "tok-env", "maxLifetime": 3600, @@ -290,7 +290,7 @@ async def test_create_builds_ws_endpoint_when_absent(self, monkeypatch): async def test_create_forwards_ports_and_parses_preview_urls(self): payload = { "sandboxId": "sb-ports", - "wsEndpoint": "wss://runtime.example.net/ws/v1/namespaces/ns/sandbox/sb-ports/exec", + "wsEndpoint": "wss://runtime.example.net/ws/v1/namespaces/ns/sandboxes/sb-ports/exec", "status": "ready", "token": "tok-ports", "maxLifetime": 3600, From 28db2f31a5721d1ac49455c15b4cb6511d678eb4 Mon Sep 17 00:00:00 2001 From: Michael Goberling Date: Thu, 4 Jun 2026 12:34:18 -0400 Subject: [PATCH 2/2] chore(sandbox): also move the ws endpoint under api/v1 for consistency --- src/aio_lib_sandbox/http.py | 2 +- tests/test_sandbox.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/aio_lib_sandbox/http.py b/src/aio_lib_sandbox/http.py index 4bfc39c..b9826c9 100644 --- a/src/aio_lib_sandbox/http.py +++ b/src/aio_lib_sandbox/http.py @@ -46,7 +46,7 @@ def normalize_api_host(host: str) -> str: def build_ws_endpoint(api_host: str, namespace: str, sandbox_id: str) -> str: parsed = urlparse(api_host) ws_scheme = "ws" if parsed.scheme == "http" else "wss" - path = f"/ws/v1/namespaces/{namespace}/sandboxes/{sandbox_id}/exec" + path = f"/api/v1/namespaces/{namespace}/sandboxes/{sandbox_id}/exec" return urlunparse((ws_scheme, parsed.netloc, path, "", "", "")) diff --git a/tests/test_sandbox.py b/tests/test_sandbox.py index 0d2230a..8611b71 100644 --- a/tests/test_sandbox.py +++ b/tests/test_sandbox.py @@ -41,7 +41,7 @@ BASE_OPTS = dict( sandbox_id="sb-test", - endpoint="wss://runtime.example.net/ws/v1/namespaces/ns/sandboxes/sb-test/exec", + endpoint="wss://runtime.example.net/api/v1/namespaces/ns/sandboxes/sb-test/exec", status="ready", namespace="ns", api_host="https://runtime.example.net", @@ -189,7 +189,7 @@ class TestSandboxCreate: async def test_create_calls_api_and_connects(self, monkeypatch): payload = { "sandboxId": "sb-new", - "wsEndpoint": "wss://runtime.example.net/ws/v1/namespaces/ns/sandboxes/sb-new/exec", + "wsEndpoint": "wss://runtime.example.net/api/v1/namespaces/ns/sandboxes/sb-new/exec", "status": "ready", "token": "tok-new", "maxLifetime": 3600, @@ -220,7 +220,7 @@ async def test_create_forwards_policy(self, monkeypatch): policy = {"network": {"egress": [{"host": "api.github.com", "port": 443}]}} payload = { "sandboxId": "sb-pol", - "wsEndpoint": "wss://runtime.example.net/ws/v1/namespaces/ns/sandboxes/sb-pol/exec", + "wsEndpoint": "wss://runtime.example.net/api/v1/namespaces/ns/sandboxes/sb-pol/exec", "status": "ready", "token": "tok-pol", "maxLifetime": 3600, @@ -247,7 +247,7 @@ async def test_create_reads_env_vars(self, monkeypatch): payload = { "sandboxId": "sb-env", - "wsEndpoint": "wss://runtime.example.net/ws/v1/namespaces/ns/sandboxes/sb-env/exec", + "wsEndpoint": "wss://runtime.example.net/api/v1/namespaces/ns/sandboxes/sb-env/exec", "status": "ready", "token": "tok-env", "maxLifetime": 3600, @@ -290,7 +290,7 @@ async def test_create_builds_ws_endpoint_when_absent(self, monkeypatch): async def test_create_forwards_ports_and_parses_preview_urls(self): payload = { "sandboxId": "sb-ports", - "wsEndpoint": "wss://runtime.example.net/ws/v1/namespaces/ns/sandboxes/sb-ports/exec", + "wsEndpoint": "wss://runtime.example.net/api/v1/namespaces/ns/sandboxes/sb-ports/exec", "status": "ready", "token": "tok-ports", "maxLifetime": 3600,