Skip to content

Commit 7486cd4

Browse files
committed
fix: require base_url for browser-scoped routing
Fail fast when browser-scoped clients do not have a session base_url, route subresource calls through the browser session base directly, and clean up browser-vm wording. Made-with: Cursor
1 parent fc7c6f9 commit 7486cd4

9 files changed

Lines changed: 101 additions & 94 deletions

File tree

examples/browser-scoped.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Browser-scoped client: call metro-routed browser APIs without repeating the
2+
* Browser-scoped client: call browser VM-routed browser APIs without repeating the
33
* session id, and run `fetch`-style HTTP through the browser network stack.
44
*
55
* Run after `yarn build` so `dist/` matches sources, or import from `src/` via

src/client.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -881,9 +881,9 @@ export class Kernel {
881881
}
882882

883883
/**
884-
* Returns a browser-scoped client: subresource calls omit the session id and,
885-
* when the browser response includes base_url, requests are routed through the
886-
* metro HTTP base for that session.
884+
* Returns a browser-scoped client: subresource calls omit the session id and
885+
* are routed through {@link BrowserCreateResponse.base_url} (browser session
886+
* HTTP base URL for the browser VM edge). Requires base_url on the browser object.
887887
*/
888888
public forBrowser(browser: KernelBrowserInput): KernelBrowserSession {
889889
return new KernelBrowserSession(this, browser);

src/lib/browser-transport.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import type { RequestOptions } from '../internal/request-options';
22

33
/**
4-
* Resolved HTTP routing for a browser session. Metro requests use defaultBaseURL
5-
* plus a per-request jwt query param. A future client-wide browser-id → base_url
6-
* cache can plug in by supplying an alternate resolver before constructing
7-
* {@link KernelBrowserSession}.
4+
* Resolved HTTP routing for a browser session. When {@link ResolvedBrowserTransport.defaultBaseURL}
5+
* is set, requests use that browser session base URL plus a per-request jwt query param.
6+
* A future client-wide browser-id → base_url cache can plug in by supplying an alternate
7+
* resolver before constructing {@link KernelBrowserSession}.
88
*/
99
export type ResolvedBrowserTransport = {
1010
sessionId: string;

src/lib/kernel-browser-session.ts

Lines changed: 66 additions & 61 deletions
Large diffs are not rendered by default.

src/resources/browser-pools.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ export interface BrowserPoolAcquireResponse {
306306
webdriver_ws_url: string;
307307

308308
/**
309-
* Metro-API HTTP base URL for this browser session.
309+
* HTTP base URL for routing browser subresource requests to this session's browser VM.
310310
*/
311311
base_url?: string;
312312

src/resources/browsers/browsers.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ export interface BrowserCreateResponse {
319319
webdriver_ws_url: string;
320320

321321
/**
322-
* Metro-API HTTP base URL for this browser session.
322+
* HTTP base URL for routing browser subresource requests to this session's browser VM.
323323
*/
324324
base_url?: string;
325325

@@ -425,7 +425,7 @@ export interface BrowserRetrieveResponse {
425425
webdriver_ws_url: string;
426426

427427
/**
428-
* Metro-API HTTP base URL for this browser session.
428+
* HTTP base URL for routing browser subresource requests to this session's browser VM.
429429
*/
430430
base_url?: string;
431431

@@ -531,7 +531,7 @@ export interface BrowserUpdateResponse {
531531
webdriver_ws_url: string;
532532

533533
/**
534-
* Metro-API HTTP base URL for this browser session.
534+
* HTTP base URL for routing browser subresource requests to this session's browser VM.
535535
*/
536536
base_url?: string;
537537

@@ -637,7 +637,7 @@ export interface BrowserListResponse {
637637
webdriver_ws_url: string;
638638

639639
/**
640-
* Metro-API HTTP base URL for this browser session.
640+
* HTTP base URL for routing browser subresource requests to this session's browser VM.
641641
*/
642642
base_url?: string;
643643

src/resources/invocations.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ export namespace InvocationListBrowsersResponse {
454454
webdriver_ws_url: string;
455455

456456
/**
457-
* Metro-API HTTP base URL for this browser session.
457+
* HTTP base URL for routing browser subresource requests to this session's browser VM.
458458
*/
459459
base_url?: string;
460460

tests/lib/browser-transport.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,26 @@ import {
66

77
describe('browser transport', () => {
88
test('parseJwtFromCdpWsUrl reads jwt query param', () => {
9-
const jwt = parseJwtFromCdpWsUrl('wss://metro.example/browser/cdp?jwt=abc%2B123&x=1');
9+
const jwt = parseJwtFromCdpWsUrl('wss://browser-session.test/browser/cdp?jwt=abc%2B123&x=1');
1010
expect(jwt).toBe('abc+123');
1111
});
1212

1313
test('resolveBrowserTransport prefers explicit jwt', () => {
1414
const t = resolveBrowserTransport({
1515
session_id: 'sess',
16-
base_url: 'https://metro/browser/kernel',
16+
base_url: 'https://vm.browser-session.test/browser/kernel',
1717
cdp_ws_url: 'wss://x/cdp?jwt=fromcdp',
1818
jwt: 'explicit',
1919
});
2020
expect(t.sessionId).toBe('sess');
21-
expect(t.defaultBaseURL).toBe('https://metro/browser/kernel');
21+
expect(t.defaultBaseURL).toBe('https://vm.browser-session.test/browser/kernel');
2222
expect(t.jwt).toBe('explicit');
2323
});
2424

2525
test('resolveBrowserTransport falls back to cdp_ws_url jwt', () => {
2626
const t = resolveBrowserTransport({
2727
session_id: 'sess',
28-
base_url: 'https://metro/browser/kernel',
28+
base_url: 'https://vm.browser-session.test/browser/kernel',
2929
cdp_ws_url: 'wss://x/cdp?jwt=fromcdp',
3030
});
3131
expect(t.jwt).toBe('fromcdp');
@@ -40,7 +40,7 @@ describe('browser transport', () => {
4040
expect(merged?.query).toEqual({ a: '1', jwt: 'j' });
4141
});
4242

43-
test('mergeBrowserScopedRequestOptions is noop without metro base', () => {
43+
test('mergeBrowserScopedRequestOptions is noop without browser session base URL', () => {
4444
const opts = { query: { a: '1' } };
4545
const merged = mergeBrowserScopedRequestOptions({ sessionId: 's' }, opts);
4646
expect(merged).toBe(opts);

tests/lib/kernel-browser-session.test.ts

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,29 @@
11
import Kernel from '@onkernel/sdk';
22
import { KernelBrowserSession } from '../../src/lib/kernel-browser-session';
33

4-
describe('KernelBrowserSession.fetch', () => {
5-
test('throws when base_url is missing', async () => {
4+
describe('KernelBrowserSession', () => {
5+
test('throws when base_url is missing', () => {
66
const kernel = new Kernel({ apiKey: 'k', baseURL: 'https://api.example/' });
7-
const browser = new KernelBrowserSession(kernel, {
8-
session_id: 'abc',
9-
cdp_ws_url: 'wss://x/browser/cdp?jwt=j',
10-
});
11-
await expect(browser.fetch('https://example.com')).rejects.toThrow(/base_url/);
7+
expect(
8+
() =>
9+
new KernelBrowserSession(kernel, {
10+
session_id: 'abc',
11+
cdp_ws_url: 'wss://x/browser/cdp?jwt=j',
12+
}),
13+
).toThrow(/base_url/);
1214
});
1315

1416
test('throws when jwt cannot be resolved', async () => {
1517
const kernel = new Kernel({ apiKey: 'k', baseURL: 'https://api.example/' });
1618
const browser = new KernelBrowserSession(kernel, {
1719
session_id: 'abc',
18-
base_url: 'https://metro/browser/kernel',
20+
base_url: 'https://vm.browser-session.test/browser/kernel',
1921
cdp_ws_url: 'wss://x/browser/cdp',
2022
});
2123
await expect(browser.fetch('https://example.com')).rejects.toThrow(/jwt/);
2224
});
2325

24-
test('issues /curl/raw against metro base with jwt query', async () => {
26+
test('issues /curl/raw against browser session base URL with jwt query', async () => {
2527
const fetchCalls: Array<{ url: string; init: RequestInit | undefined }> = [];
2628
const kernel = new Kernel({ apiKey: 'k', baseURL: 'https://api.example/' });
2729
(kernel as any).fetch = async (url: string, init?: RequestInit) => {
@@ -34,7 +36,7 @@ describe('KernelBrowserSession.fetch', () => {
3436

3537
const browser = new KernelBrowserSession(kernel, {
3638
session_id: 'abc',
37-
base_url: 'https://metro/browser/kernel',
39+
base_url: 'https://vm.browser-session.test/browser/kernel',
3840
cdp_ws_url: 'wss://x/browser/cdp?jwt=tok',
3941
});
4042

@@ -45,13 +47,13 @@ describe('KernelBrowserSession.fetch', () => {
4547
expect(res.status).toBe(200);
4648
expect(fetchCalls.length).toBe(1);
4749
const call = fetchCalls[0]!;
48-
expect(call.url).toContain('https://metro/browser/kernel/curl/raw?');
50+
expect(call.url).toContain('https://vm.browser-session.test/browser/kernel/curl/raw?');
4951
expect(call.url).toContain('url=https%3A%2F%2Fexample.com%2Fhello');
5052
expect(call.url).toContain('jwt=tok');
5153
expect((call.init?.headers as Headers).get('authorization')).toBeNull();
5254
});
5355

54-
test('rewrites browser subresource paths through metro base', async () => {
56+
test('rewrites browser subresource paths through browser session base URL', async () => {
5557
const fetchCalls: Array<{ url: string; init: RequestInit | undefined }> = [];
5658
const kernel = new Kernel({ apiKey: 'k', baseURL: 'https://api.example/' });
5759
(kernel as any).fetch = async (url: string, init?: RequestInit) => {
@@ -64,15 +66,15 @@ describe('KernelBrowserSession.fetch', () => {
6466

6567
const browser = new KernelBrowserSession(kernel, {
6668
session_id: 'abc',
67-
base_url: 'https://metro/browser/kernel',
69+
base_url: 'https://vm.browser-session.test/browser/kernel',
6870
cdp_ws_url: 'wss://x/browser/cdp?jwt=tok',
6971
});
7072

7173
await browser.computer.clickMouse({ x: 1, y: 2 });
7274

7375
expect(fetchCalls.length).toBe(1);
7476
const call = fetchCalls[0]!;
75-
expect(call.url).toContain('https://metro/browser/kernel/computer/click_mouse?');
77+
expect(call.url).toContain('https://vm.browser-session.test/browser/kernel/computer/click_mouse?');
7678
expect(call.url).toContain('jwt=tok');
7779
expect(call.url).not.toContain('/browsers/abc/');
7880
expect((call.init?.headers as Headers).get('authorization')).toBeNull();

0 commit comments

Comments
 (0)