diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 8316a6d..716d004 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.29.0"
+ ".": "0.30.0"
}
diff --git a/.stats.yml b/.stats.yml
index 470e498..710d8bd 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 97
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-46c8320dcd9f8fc596f469ef0dd1aafaca591ab36cf2a6f8a7297dc9136bdc71.yml
-openapi_spec_hash: 1be1e6589cd94c581b241720e01a65bc
-config_hash: b470456b217bb9502f5212311d395a6f
+configured_endpoints: 98
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-ccbe854895eb34a9562e33979f5f43cd6ad1f529d5924ee56e56f0c94dcf0454.yml
+openapi_spec_hash: 2fa4ecbe742fc46fdde481188c1d885e
+config_hash: dd218aae3f852dff79e77febc2077b8e
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c47d06e..e90f6ab 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,23 @@
# Changelog
+## 0.30.0 (2026-02-03)
+
+Full Changelog: [v0.29.0...v0.30.0](https://github.com/kernel/kernel-node-sdk/compare/v0.29.0...v0.30.0)
+
+### Features
+
+* Neil/kernel 872 templates v3 ([3a73c62](https://github.com/kernel/kernel-node-sdk/commit/3a73c6245590aeb784ab0c840728b486f42e7cd5))
+
+
+### Bug Fixes
+
+* **client:** avoid memory leak with abort signals ([8523597](https://github.com/kernel/kernel-node-sdk/commit/85235970dd1c13e0b1ce5086d74515439453b929))
+
+
+### Chores
+
+* **client:** do not parse responses with empty content-length ([ada489d](https://github.com/kernel/kernel-node-sdk/commit/ada489d63f91567c9740623ede272a5f0763484b))
+
## 0.29.0 (2026-01-29)
Full Changelog: [v0.28.0...v0.29.0](https://github.com/kernel/kernel-node-sdk/compare/v0.28.0...v0.29.0)
diff --git a/api.md b/api.md
index 3b123d3..dc172c8 100644
--- a/api.md
+++ b/api.md
@@ -49,6 +49,7 @@ Types:
- InvocationUpdateResponse
- InvocationListResponse
- InvocationFollowResponse
+- InvocationListBrowsersResponse
Methods:
@@ -58,6 +59,7 @@ Methods:
- client.invocations.list({ ...params }) -> InvocationListResponsesOffsetPagination
- client.invocations.deleteBrowsers(id) -> void
- client.invocations.follow(id, { ...params }) -> InvocationFollowResponse
+- client.invocations.listBrowsers(id) -> InvocationListBrowsersResponse
# Browsers
diff --git a/package.json b/package.json
index 605cedd..13178cd 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@onkernel/sdk",
- "version": "0.29.0",
+ "version": "0.30.0",
"description": "The official TypeScript library for the Kernel API",
"author": "Kernel <>",
"types": "dist/index.d.ts",
diff --git a/src/client.ts b/src/client.ts
index 63de439..6ace201 100644
--- a/src/client.ts
+++ b/src/client.ts
@@ -76,6 +76,7 @@ import {
InvocationCreateResponse,
InvocationFollowParams,
InvocationFollowResponse,
+ InvocationListBrowsersResponse,
InvocationListParams,
InvocationListResponse,
InvocationListResponsesOffsetPagination,
@@ -638,9 +639,10 @@ export class Kernel {
controller: AbortController,
): Promise {
const { signal, method, ...options } = init || {};
- if (signal) signal.addEventListener('abort', () => controller.abort());
+ const abort = controller.abort.bind(controller);
+ if (signal) signal.addEventListener('abort', abort, { once: true });
- const timeout = setTimeout(() => controller.abort(), ms);
+ const timeout = setTimeout(abort, ms);
const isReadableBody =
((globalThis as any).ReadableStream && options.body instanceof (globalThis as any).ReadableStream) ||
@@ -935,6 +937,7 @@ export declare namespace Kernel {
type InvocationUpdateResponse as InvocationUpdateResponse,
type InvocationListResponse as InvocationListResponse,
type InvocationFollowResponse as InvocationFollowResponse,
+ type InvocationListBrowsersResponse as InvocationListBrowsersResponse,
type InvocationListResponsesOffsetPagination as InvocationListResponsesOffsetPagination,
type InvocationCreateParams as InvocationCreateParams,
type InvocationUpdateParams as InvocationUpdateParams,
diff --git a/src/internal/parse.ts b/src/internal/parse.ts
index 7fe9be4..4796f69 100644
--- a/src/internal/parse.ts
+++ b/src/internal/parse.ts
@@ -43,6 +43,12 @@ export async function defaultParseResponse(client: Kernel, props: APIResponse
const mediaType = contentType?.split(';')[0]?.trim();
const isJSON = mediaType?.includes('application/json') || mediaType?.endsWith('+json');
if (isJSON) {
+ const contentLength = response.headers.get('content-length');
+ if (contentLength === '0') {
+ // if there is no content we can't do anything
+ return undefined as T;
+ }
+
const json = await response.json();
return json as T;
}
diff --git a/src/resources/index.ts b/src/resources/index.ts
index a3475dd..a2d913f 100644
--- a/src/resources/index.ts
+++ b/src/resources/index.ts
@@ -83,6 +83,7 @@ export {
type InvocationUpdateResponse,
type InvocationListResponse,
type InvocationFollowResponse,
+ type InvocationListBrowsersResponse,
type InvocationCreateParams,
type InvocationUpdateParams,
type InvocationListParams,
diff --git a/src/resources/invocations.ts b/src/resources/invocations.ts
index 5b9106d..d8883bb 100644
--- a/src/resources/invocations.ts
+++ b/src/resources/invocations.ts
@@ -2,6 +2,7 @@
import { APIResource } from '../core/resource';
import * as Shared from './shared';
+import * as BrowsersAPI from './browsers/browsers';
import { APIPromise } from '../core/api-promise';
import { OffsetPagination, type OffsetPaginationParams, PagePromise } from '../core/pagination';
import { Stream } from '../core/streaming';
@@ -118,6 +119,20 @@ export class Invocations extends APIResource {
stream: true,
}) as APIPromise>;
}
+
+ /**
+ * Returns all active browser sessions created within the specified invocation.
+ *
+ * @example
+ * ```ts
+ * const response = await client.invocations.listBrowsers(
+ * 'id',
+ * );
+ * ```
+ */
+ listBrowsers(id: string, options?: RequestOptions): APIPromise {
+ return this._client.get(path`/invocations/${id}/browsers`, options);
+ }
}
export type InvocationListResponsesOffsetPagination = OffsetPagination;
@@ -394,6 +409,88 @@ export type InvocationFollowResponse =
| Shared.ErrorEvent
| Shared.HeartbeatEvent;
+export interface InvocationListBrowsersResponse {
+ browsers: Array;
+}
+
+export namespace InvocationListBrowsersResponse {
+ export interface Browser {
+ /**
+ * Websocket URL for Chrome DevTools Protocol connections to the browser session
+ */
+ cdp_ws_url: string;
+
+ /**
+ * When the browser session was created.
+ */
+ created_at: string;
+
+ /**
+ * Whether the browser session is running in headless mode.
+ */
+ headless: boolean;
+
+ /**
+ * Unique identifier for the browser session
+ */
+ session_id: string;
+
+ /**
+ * Whether the browser session is running in stealth mode.
+ */
+ stealth: boolean;
+
+ /**
+ * The number of seconds of inactivity before the browser session is terminated.
+ */
+ timeout_seconds: number;
+
+ /**
+ * Remote URL for live viewing the browser session. Only available for non-headless
+ * browsers.
+ */
+ browser_live_view_url?: string;
+
+ /**
+ * When the browser session was soft-deleted. Only present for deleted sessions.
+ */
+ deleted_at?: string;
+
+ /**
+ * Whether the browser session is running in kiosk mode.
+ */
+ kiosk_mode?: boolean;
+
+ /**
+ * @deprecated DEPRECATED: Use timeout_seconds (up to 72 hours) and Profiles
+ * instead.
+ */
+ persistence?: BrowsersAPI.BrowserPersistence;
+
+ /**
+ * Browser profile metadata.
+ */
+ profile?: BrowsersAPI.Profile;
+
+ /**
+ * ID of the proxy associated with this browser session, if any.
+ */
+ proxy_id?: string;
+
+ /**
+ * Initial browser window size in pixels with optional refresh rate. If omitted,
+ * image defaults apply (1920x1080@25). Only specific viewport configurations are
+ * supported. The server will reject unsupported combinations. Supported
+ * resolutions are: 2560x1440@10, 1920x1080@25, 1920x1200@25, 1440x900@25,
+ * 1280x800@60, 1024x768@60, 1200x800@60 If refresh_rate is not provided, it will
+ * be automatically determined from the width and height if they match a supported
+ * configuration exactly. Note: Higher resolutions may affect the responsiveness of
+ * live view browser
+ */
+ viewport?: Shared.BrowserViewport;
+ }
+}
+
export interface InvocationCreateParams {
/**
* Name of the action to invoke
@@ -488,6 +585,7 @@ export declare namespace Invocations {
type InvocationUpdateResponse as InvocationUpdateResponse,
type InvocationListResponse as InvocationListResponse,
type InvocationFollowResponse as InvocationFollowResponse,
+ type InvocationListBrowsersResponse as InvocationListBrowsersResponse,
type InvocationListResponsesOffsetPagination as InvocationListResponsesOffsetPagination,
type InvocationCreateParams as InvocationCreateParams,
type InvocationUpdateParams as InvocationUpdateParams,
diff --git a/src/version.ts b/src/version.ts
index bef2b64..91a9bb6 100644
--- a/src/version.ts
+++ b/src/version.ts
@@ -1 +1 @@
-export const VERSION = '0.29.0'; // x-release-please-version
+export const VERSION = '0.30.0'; // x-release-please-version
diff --git a/tests/api-resources/invocations.test.ts b/tests/api-resources/invocations.test.ts
index b8c8024..cb7750a 100644
--- a/tests/api-resources/invocations.test.ts
+++ b/tests/api-resources/invocations.test.ts
@@ -132,4 +132,16 @@ describe('resource invocations', () => {
),
).rejects.toThrow(Kernel.NotFoundError);
});
+
+ // Prism tests are disabled
+ test.skip('listBrowsers', async () => {
+ const responsePromise = client.invocations.listBrowsers('id');
+ const rawResponse = await responsePromise.asResponse();
+ expect(rawResponse).toBeInstanceOf(Response);
+ const response = await responsePromise;
+ expect(response).not.toBeInstanceOf(Response);
+ const dataAndResponse = await responsePromise.withResponse();
+ expect(dataAndResponse.data).toBe(response);
+ expect(dataAndResponse.response).toBe(rawResponse);
+ });
});