Skip to content

Commit e58d036

Browse files
authored
Merge pull request #174 from kernel/hypeship/tier2-process-direct
feat: route process calls directly to browser VMs
2 parents 854ddfe + 9e1a053 commit e58d036

2 files changed

Lines changed: 19 additions & 7 deletions

File tree

‎src/lib/browser-routing.ts‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,13 @@ const BROWSER_ROUTING_SUBRESOURCES_ENV = 'KERNEL_BROWSER_ROUTING_SUBRESOURCES';
4141
// Path prefixes eligible for direct-to-VM routing. "telemetry/stream" is the live
4242
// SSE endpoint (served by the VM); "telemetry/events" is a historical read served
4343
// by the control plane (S2) and must NOT be here.
44-
const DEFAULT_BROWSER_ROUTING_SUBRESOURCES = ['curl', 'telemetry/stream', 'computer', 'playwright'];
44+
const DEFAULT_BROWSER_ROUTING_SUBRESOURCES = [
45+
'curl',
46+
'telemetry/stream',
47+
'computer',
48+
'playwright',
49+
'process',
50+
];
4551
const BROWSER_ROUTE_CACHEABLE_PATH = /^\/(?:v\d+\/)?browsers(?:\/[^/]+)?\/?$/;
4652
const BROWSER_POOL_ACQUIRE_PATH = /^\/(?:v\d+\/)?browser_pools\/[^/]+\/acquire\/?$/;
4753
const BROWSER_DELETE_BY_ID_PATH = /^\/(?:v\d+\/)?browsers\/([^/]+)\/?$/;

‎tests/lib/browser-routing.test.ts‎

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -451,12 +451,13 @@ describe('browser routing', () => {
451451
'telemetry/stream',
452452
'computer',
453453
'playwright',
454+
'process',
454455
]);
455456
});
456457
});
457458

458459
test('allowlist matching is segment-boundary aware (telemetry/events stays on the control plane)', () => {
459-
const prefixes = ['curl', 'telemetry/stream', 'computer', 'playwright'];
460+
const prefixes = ['curl', 'telemetry/stream', 'computer', 'playwright', 'process'];
460461
expect(matchesDirectVMPrefix('telemetry/stream', prefixes)).toBe(true);
461462
expect(matchesDirectVMPrefix('telemetry/stream/x', prefixes)).toBe(true);
462463
expect(matchesDirectVMPrefix('telemetry/events', prefixes)).toBe(false);
@@ -465,7 +466,8 @@ describe('browser routing', () => {
465466
expect(matchesDirectVMPrefix('curl/raw', prefixes)).toBe(true);
466467
expect(matchesDirectVMPrefix('computer/screenshot', prefixes)).toBe(true);
467468
expect(matchesDirectVMPrefix('playwright/execute', prefixes)).toBe(true);
468-
expect(matchesDirectVMPrefix('process/exec', prefixes)).toBe(false);
469+
expect(matchesDirectVMPrefix('process/exec', prefixes)).toBe(true);
470+
expect(matchesDirectVMPrefix('process/proc-1/stdout/stream', prefixes)).toBe(true);
469471
expect(matchesDirectVMPrefix('fs/read', prefixes)).toBe(false);
470472
});
471473

@@ -507,7 +509,7 @@ describe('browser routing', () => {
507509
});
508510
});
509511

510-
test('routes computer screenshot and playwright execute to the VM by default', async () => {
512+
test('routes default browser subresources to the VM', async () => {
511513
await withBrowserRoutingEnv(undefined, async () => {
512514
const calls: Array<{ url: string; headers: Headers }> = [];
513515
const kernel = new Kernel({
@@ -530,13 +532,17 @@ describe('browser routing', () => {
530532
headers: { 'content-type': 'image/png' },
531533
});
532534
}
535+
if (url.includes('/process/exec')) {
536+
return Response.json({ exit_code: 0, stdout_b64: '', stderr_b64: '' });
537+
}
533538
return Response.json({ success: true });
534539
},
535540
});
536541

537542
await kernel.browsers.create();
538543
await kernel.browsers.computer.captureScreenshot('sess-1');
539544
await kernel.browsers.playwright.execute('sess-1', { code: 'return 1' });
545+
await kernel.browsers.process.exec('sess-1', { command: 'echo' });
540546

541547
expect(calls[1]?.url).toBe(
542548
'http://browser-session.test/browser/kernel/computer/screenshot?jwt=token-abc',
@@ -546,10 +552,12 @@ describe('browser routing', () => {
546552
'http://browser-session.test/browser/kernel/playwright/execute?jwt=token-abc',
547553
);
548554
expect(calls[2]?.headers.get('authorization')).toBeNull();
555+
expect(calls[3]?.url).toBe('http://browser-session.test/browser/kernel/process/exec?jwt=token-abc');
556+
expect(calls[3]?.headers.get('authorization')).toBeNull();
549557
});
550558
});
551559

552-
test('keeps process, fs, and telemetry/events on the API origin by default', async () => {
560+
test('keeps fs and telemetry/events on the API origin by default', async () => {
553561
await withBrowserRoutingEnv(undefined, async () => {
554562
const calls: string[] = [];
555563
const kernel = new Kernel({
@@ -579,12 +587,10 @@ describe('browser routing', () => {
579587
});
580588

581589
await kernel.browsers.create();
582-
await kernel.browsers.process.exec('sess-1', { command: 'echo' });
583590
await kernel.browsers.fs.readFile('sess-1', { path: '/tmp/x' });
584591
await kernel.browsers.telemetry.events('sess-1');
585592

586593
expect(calls.slice(1)).toEqual([
587-
'https://api.example/browsers/sess-1/process/exec',
588594
'https://api.example/browsers/sess-1/fs/read_file?path=%2Ftmp%2Fx',
589595
'https://api.example/browsers/sess-1/telemetry/events',
590596
]);

0 commit comments

Comments
 (0)