diff --git a/.release-please-manifest.json b/.release-please-manifest.json index bfeb4872..85b1765b 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.59.0" + ".": "0.60.0" } diff --git a/.stats.yml b/.stats.yml index 5a5e5632..9f173dc5 100644 --- a/.stats.yml +++ b/.stats.yml @@ -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 diff --git a/CHANGELOG.md b/CHANGELOG.md index 37b5c5c5..ea88ea69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/examples/browser-telemetry.ts b/examples/browser-telemetry.ts new file mode 100644 index 00000000..e2dbe438 --- /dev/null +++ b/examples/browser-telemetry.ts @@ -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(); diff --git a/package.json b/package.json index df4d823a..f9a6290a 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/lib/browser-routing.ts b/src/lib/browser-routing.ts index 6580da6c..bbedec29 100644 --- a/src/lib/browser-routing.ts +++ b/src/lib/browser-routing.ts @@ -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\/([^/]+)\/?$/; diff --git a/src/resources/api-keys.ts b/src/resources/api-keys.ts index cad02c32..a855c962 100644 --- a/src/resources/api-keys.ts +++ b/src/resources/api-keys.ts @@ -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 { diff --git a/src/resources/browser-pools.ts b/src/resources/browser-pools.ts index dc1af440..c6e78492 100644 --- a/src/resources/browser-pools.ts +++ b/src/resources/browser-pools.ts @@ -48,7 +48,6 @@ export class BrowserPools extends APIResource { * ```ts * const browserPool = await client.browserPools.update( * 'id_or_name', - * { size: 10 }, * ); * ``` */ @@ -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. @@ -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 diff --git a/src/version.ts b/src/version.ts index 7fd65e71..3c00a63b 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export const VERSION = '0.59.0'; // x-release-please-version +export const VERSION = '0.60.0'; // x-release-please-version diff --git a/tests/api-resources/api-keys.test.ts b/tests/api-resources/api-keys.test.ts index c39c2228..14a45a0d 100644 --- a/tests/api-resources/api-keys.test.ts +++ b/tests/api-resources/api-keys.test.ts @@ -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); }); diff --git a/tests/api-resources/browser-pools.test.ts b/tests/api-resources/browser-pools.test.ts index 7a473ba2..dff2eeda 100644 --- a/tests/api-resources/browser-pools.test.ts +++ b/tests/api-resources/browser-pools.test.ts @@ -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; @@ -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(); diff --git a/tests/lib/browser-routing.test.ts b/tests/lib/browser-routing.test.ts index 1ca285cc..da87ad6e 100644 --- a/tests/lib/browser-routing.test.ts +++ b/tests/lib/browser-routing.test.ts @@ -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(); }); });