Skip to content

Commit 320b693

Browse files
rgarciaclaude
andcommitted
feat: route browser telemetry directly to the VM by default
Telemetry is now a default direct-to-VM routing subresource. The telemetry stream method path is changed from /browsers/{id}/telemetry to /browsers/{id}/telemetry/stream so it mirrors the browser VM endpoint: when the request is rewritten for direct routing it yields {base_url}/telemetry/stream, which is the SSE stream on the VM (the VM's /telemetry is a different, non-stream JSON endpoint). "telemetry" is added to the default KERNEL_BROWSER_ROUTING_SUBRESOURCES allowlist alongside "curl". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent efb45d6 commit 320b693

4 files changed

Lines changed: 27 additions & 5 deletions

File tree

api.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ from kernel.types.browsers import (
143143

144144
Methods:
145145

146-
- <code title="get /browsers/{id}/telemetry">client.browsers.telemetry.<a href="./src/kernel/resources/browsers/telemetry.py">stream</a>(id) -> <a href="./src/kernel/types/browsers/telemetry_stream_response.py">TelemetryStreamResponse</a></code>
146+
- <code title="get /browsers/{id}/telemetry/stream">client.browsers.telemetry.<a href="./src/kernel/resources/browsers/telemetry.py">stream</a>(id) -> <a href="./src/kernel/types/browsers/telemetry_stream_response.py">TelemetryStreamResponse</a></code>
147147

148148
## Replays
149149

src/kernel/lib/browser_routing/routing.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class BrowserRoutingConfig:
4141
def browser_routing_config_from_env() -> BrowserRoutingConfig:
4242
raw = os.environ.get("KERNEL_BROWSER_ROUTING_SUBRESOURCES")
4343
if raw is None:
44-
return BrowserRoutingConfig(subresources=("curl",))
44+
return BrowserRoutingConfig(subresources=("curl", "telemetry"))
4545
if raw.strip() == "":
4646
return BrowserRoutingConfig()
4747

src/kernel/resources/browsers/telemetry.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ def stream(
8080
extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})}
8181
extra_headers = {**strip_not_given({"Last-Event-ID": last_event_id}), **(extra_headers or {})}
8282
return self._get(
83-
path_template("/browsers/{id}/telemetry", id=id),
83+
path_template("/browsers/{id}/telemetry/stream", id=id),
8484
options=make_request_options(
8585
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
8686
),
@@ -149,7 +149,7 @@ async def stream(
149149
extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})}
150150
extra_headers = {**strip_not_given({"Last-Event-ID": last_event_id}), **(extra_headers or {})}
151151
return await self._get(
152-
path_template("/browsers/{id}/telemetry", id=id),
152+
path_template("/browsers/{id}/telemetry/stream", id=id),
153153
options=make_request_options(
154154
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
155155
),

tests/test_browser_routing.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,28 @@ def test_browser_request_uses_curl_raw() -> None:
9797
assert request.url.params.get("jwt") == "token-abc"
9898

9999

100+
@respx.mock
101+
def test_telemetry_stream_routes_directly_to_vm(monkeypatch: pytest.MonkeyPatch) -> None:
102+
monkeypatch.setenv("KERNEL_BROWSER_ROUTING_SUBRESOURCES", "telemetry")
103+
route = respx.get("http://browser-session.test/browser/kernel/telemetry/stream").mock(
104+
return_value=httpx.Response(
105+
200,
106+
headers={"content-type": "text/event-stream"},
107+
content=b'id: 1\ndata: {"category":"api"}\n\n',
108+
)
109+
)
110+
with Kernel(base_url=base_url, api_key=api_key, _strict_response_validation=True) as client:
111+
_cache_browser(client)
112+
stream = client.browsers.telemetry.stream("sess-1")
113+
stream.close()
114+
115+
assert route.called
116+
request = cast(httpx.Request, cast(Any, route.calls[0]).request)
117+
assert request.url.path == "/browser/kernel/telemetry/stream"
118+
assert request.url.params.get("jwt") == "token-abc"
119+
assert request.headers.get("Authorization") is None
120+
121+
100122
@respx.mock
101123
def test_browser_request_params_cannot_override_target_url_or_jwt() -> None:
102124
route = respx.get("http://browser-session.test/browser/kernel/curl/raw").mock(
@@ -315,7 +337,7 @@ def test_browser_route_from_browser_requires_base_url_and_jwt() -> None:
315337

316338
def test_browser_routing_config_from_env_defaults_to_curl(monkeypatch: pytest.MonkeyPatch) -> None:
317339
monkeypatch.delenv("KERNEL_BROWSER_ROUTING_SUBRESOURCES", raising=False)
318-
assert browser_routing_config_from_env().subresources == ("curl",)
340+
assert browser_routing_config_from_env().subresources == ("curl", "telemetry")
319341

320342

321343
def test_browser_routing_config_from_env_empty_string_disables_routing(monkeypatch: pytest.MonkeyPatch) -> None:

0 commit comments

Comments
 (0)