Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions examples/browser-telemetry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import Kernel from '@onkernel/sdk';

async function main() {
const kernel = new Kernel();

// Create a browser with telemetry enabled so it emits events while it runs.
const browser = await kernel.browsers.create({ telemetry: { enabled: true } });

try {
// Telemetry is a default routing subresource, so the stream goes directly to the VM automatically.
const stream = await kernel.browsers.telemetry.stream(browser.session_id);

// Make browser activity to generate telemetry. The "api" category emits an event per VM API call,
// so events arrive within ~1s.
for (let i = 0; i < 3; i++) {
await kernel.browsers.curl(browser.session_id, { url: 'https://example.com', method: 'GET' });
}

// Print a few events, then stop so the program terminates promptly.
let count = 0;
for await (const event of stream) {
console.log('telemetry event', event);
if (++count >= 3) break;
}
} finally {
await kernel.browsers.deleteByID(browser.session_id);
}
}

void main();
2 changes: 1 addition & 1 deletion src/lib/browser-routing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export class BrowserRouteCache {
}

const BROWSER_ROUTING_SUBRESOURCES_ENV = 'KERNEL_BROWSER_ROUTING_SUBRESOURCES';
const DEFAULT_BROWSER_ROUTING_SUBRESOURCES = ['curl'];
const DEFAULT_BROWSER_ROUTING_SUBRESOURCES = ['curl', 'telemetry'];
const BROWSER_ROUTE_CACHEABLE_PATH = /^\/(?:v\d+\/)?browsers(?:\/[^/]+)?\/?$/;
const BROWSER_POOL_ACQUIRE_PATH = /^\/(?:v\d+\/)?browser_pools\/[^/]+\/acquire\/?$/;
const BROWSER_DELETE_BY_ID_PATH = /^\/(?:v\d+\/)?browsers\/([^/]+)\/?$/;
Expand Down
36 changes: 34 additions & 2 deletions tests/lib/browser-routing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -381,9 +381,41 @@ describe('browser routing', () => {
).rejects.toThrow(/unsupported HTTP method/i);
});

test('defaults browser routing subresources to curl when env is unset', async () => {
test('defaults browser routing subresources to curl and telemetry when env is unset', async () => {
await withBrowserRoutingEnv(undefined, async () => {
expect(browserRoutingSubresourcesFromEnv()).toEqual(['curl']);
expect(browserRoutingSubresourcesFromEnv()).toEqual(['curl', 'telemetry']);
});
});

test('routes telemetry stream calls to the VM /telemetry/stream path by default', async () => {
await withBrowserRoutingEnv(undefined, async () => {
const calls: Array<{ url: string; headers: Headers }> = [];
const kernel = new Kernel({
apiKey: 'k',
baseURL: 'https://api.example/',
fetch: async (input, init?: RequestInit) => {
const url = normalizeURL(input);
const headers = input instanceof Request ? new Headers(input.headers) : new Headers(init?.headers);
calls.push({ url, headers });
if (url === 'https://api.example/browsers') {
return Response.json({
session_id: 'sess-1',
base_url: 'http://browser-session.test/browser/kernel',
cdp_ws_url: 'wss://browser-session.test/browser/cdp?jwt=token-abc',
});
}
return new Response('id: 1\ndata: {"seq":1}\n\n', {
status: 200,
headers: { 'content-type': 'text/event-stream' },
});
},
});

await kernel.browsers.create();
await kernel.browsers.telemetry.stream('sess-1');

expect(calls[1]?.url).toBe('http://browser-session.test/browser/kernel/telemetry/stream?jwt=token-abc');
expect(calls[1]?.headers.get('authorization')).toBeNull();
});
});

Expand Down
Loading