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
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "0.59.0"
".": "0.60.0"
}
4 changes: 2 additions & 2 deletions .stats.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
configured_endpoints: 117
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel/kernel-1acd8f0b76ab00e36b53cc3ca90b72b2199f3388b3e307890adb464b87f9a2d8.yml
openapi_spec_hash: 82003125c1c2c5d82d19270bafb4a6ca
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel/kernel-3a1db7f11a92b28681929255ada59d2317ee4db98b5ff5aa6ce142a0664058b3.yml
openapi_spec_hash: b3064eaa589ae2a84993686ad1a3ee43
config_hash: ede72e4ae65cc5a6d6927938b3455c46
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
# Changelog

## 0.60.0 (2026-06-03)

Full Changelog: [v0.59.0...v0.60.0](https://github.com/kernel/kernel-node-sdk/compare/v0.59.0...v0.60.0)

### Features

* Add API-backed API key management endpoints ([1e0f035](https://github.com/kernel/kernel-node-sdk/commit/1e0f035be5c62191399afa4843eb5b15f32001e6))
* Fix browser pool update schema ([a0c377d](https://github.com/kernel/kernel-node-sdk/commit/a0c377d9a05c8f3f81c763be868b60d53663f772))
* route browser telemetry directly to the VM by default ([2c4d153](https://github.com/kernel/kernel-node-sdk/commit/2c4d153399f48723d5eb4680379426029356e44c))


### Refactors

* **examples:** rename telemetry example to browser-telemetry ([2030547](https://github.com/kernel/kernel-node-sdk/commit/203054736d39a77e3e55895668130655a680b0c5))

## 0.59.0 (2026-06-03)

Full Changelog: [v0.58.0...v0.59.0](https://github.com/kernel/kernel-node-sdk/compare/v0.58.0...v0.59.0)
Expand Down
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 package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@onkernel/sdk",
"version": "0.59.0",
"version": "0.60.0",
"description": "The official TypeScript library for the Kernel API",
"author": "Kernel <>",
"types": "dist/index.d.ts",
Expand Down
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
18 changes: 17 additions & 1 deletion src/resources/api-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,23 @@ export interface APIKeyUpdateParams {
name: string;
}

export interface APIKeyListParams extends OffsetPaginationParams {}
export interface APIKeyListParams extends OffsetPaginationParams {
/**
* Case-insensitive substring match against API key name, creator, and project. API
* key identifiers and masked keys match by exact value or prefix.
*/
query?: string;

/**
* Field to sort API keys by.
*/
sort_by?: 'created_at' | 'name' | 'expires_at';

/**
* Sort direction for API keys.
*/
sort_direction?: 'asc' | 'desc';
}

export declare namespace APIKeys {
export {
Expand Down
15 changes: 7 additions & 8 deletions src/resources/browser-pools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ export class BrowserPools extends APIResource {
* ```ts
* const browserPool = await client.browserPools.update(
* 'id_or_name',
* { size: 10 },
* );
* ```
*/
Expand Down Expand Up @@ -494,13 +493,6 @@ export interface BrowserPoolCreateParams {
}

export interface BrowserPoolUpdateParams {
/**
* Number of browsers to maintain in the pool. The maximum size is determined by
* your organization's pooled sessions limit (the sum of all pool sizes cannot
* exceed your limit).
*/
size: number;

/**
* Custom Chrome enterprise policy overrides applied to all browsers in this pool.
* Keys are Chrome enterprise policy names; values must match their expected types.
Expand Down Expand Up @@ -554,6 +546,13 @@ export interface BrowserPoolUpdateParams {
*/
proxy_id?: string;

/**
* Number of browsers to maintain in the pool. The maximum size is determined by
* your organization's pooled sessions limit (the sum of all pool sizes cannot
* exceed your limit).
*/
size?: number;

/**
* Optional URL to navigate to when a new browser is warmed into the pool.
* Best-effort: failures to navigate do not fail pool fill. Only applied to
Expand Down
2 changes: 1 addition & 1 deletion src/version.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export const VERSION = '0.59.0'; // x-release-please-version
export const VERSION = '0.60.0'; // x-release-please-version
11 changes: 10 additions & 1 deletion tests/api-resources/api-keys.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,16 @@ describe('resource apiKeys', () => {
test.skip('list: request options and params are passed correctly', async () => {
// ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error
await expect(
client.apiKeys.list({ limit: 100, offset: 0 }, { path: '/_stainless_unknown_path' }),
client.apiKeys.list(
{
limit: 100,
offset: 0,
query: 'query',
sort_by: 'created_at',
sort_direction: 'asc',
},
{ path: '/_stainless_unknown_path' },
),
).rejects.toThrow(Kernel.NotFoundError);
});

Expand Down
32 changes: 2 additions & 30 deletions tests/api-resources/browser-pools.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ describe('resource browserPools', () => {
});

// Mock server tests are disabled
test.skip('update: only required params', async () => {
const responsePromise = client.browserPools.update('id_or_name', { size: 10 });
test.skip('update', async () => {
const responsePromise = client.browserPools.update('id_or_name', {});
const rawResponse = await responsePromise.asResponse();
expect(rawResponse).toBeInstanceOf(Response);
const response = await responsePromise;
Expand All @@ -71,34 +71,6 @@ describe('resource browserPools', () => {
expect(dataAndResponse.response).toBe(rawResponse);
});

// Mock server tests are disabled
test.skip('update: required and optional params', async () => {
const response = await client.browserPools.update('id_or_name', {
size: 10,
chrome_policy: { foo: 'bar' },
discard_all_idle: false,
extensions: [{ id: 'id', name: 'name' }],
fill_rate_per_minute: 0,
headless: false,
kiosk_mode: true,
name: 'my-pool',
profile: {
id: 'id',
name: 'name',
save_changes: true,
},
proxy_id: 'proxy_id',
start_url: 'https://example.com',
stealth: true,
timeout_seconds: 60,
viewport: {
height: 800,
width: 1280,
refresh_rate: 60,
},
});
});

// Mock server tests are disabled
test.skip('list', async () => {
const responsePromise = client.browserPools.list();
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