diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 716d004..8e3d955 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.30.0"
+ ".": "0.31.0"
}
diff --git a/.stats.yml b/.stats.yml
index 710d8bd..91fd3b6 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-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
+configured_endpoints: 100
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-a6d93dc291278035c96add38bb6150ec2b9ba8bbabb4676e3dbbb8444cf3b1e4.yml
+openapi_spec_hash: 694bcc56d94fd0ff0d1f7b0fc1dae8ba
+config_hash: 62e33cf2ed8fe0b4ceebba63367481ad
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e90f6ab..4eedd70 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,24 @@
# Changelog
+## 0.31.0 (2026-02-06)
+
+Full Changelog: [v0.30.0...v0.31.0](https://github.com/kernel/kernel-node-sdk/compare/v0.30.0...v0.31.0)
+
+### Features
+
+* add batch computer action proxy endpoint ([56f8c33](https://github.com/kernel/kernel-node-sdk/commit/56f8c3303f7c1ac4082bffd756bc4b1ea4836ab1))
+
+
+### Bug Fixes
+
+* **client:** avoid removing abort listener too early ([4e4c9df](https://github.com/kernel/kernel-node-sdk/commit/4e4c9df0cf07d7f4e457b28c16431389b5cf7fd1))
+
+
+### Chores
+
+* **client:** restructure abort controller binding ([d12c7fa](https://github.com/kernel/kernel-node-sdk/commit/d12c7fae75a9ed3346dc423d21c8c19aa65c0b3f))
+* **internal:** fix pagination internals not accepting option promises ([5c52149](https://github.com/kernel/kernel-node-sdk/commit/5c521491092a3ea0386daa75d3552f9ddf88aad2))
+
## 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)
diff --git a/api.md b/api.md
index dc172c8..1f78038 100644
--- a/api.md
+++ b/api.md
@@ -163,13 +163,16 @@ Methods:
Types:
+- ComputerGetMousePositionResponse
- ComputerSetCursorVisibilityResponse
Methods:
+- client.browsers.computer.batch(id, { ...params }) -> void
- client.browsers.computer.captureScreenshot(id, { ...params }) -> Response
- client.browsers.computer.clickMouse(id, { ...params }) -> void
- client.browsers.computer.dragMouse(id, { ...params }) -> void
+- client.browsers.computer.getMousePosition(id) -> ComputerGetMousePositionResponse
- client.browsers.computer.moveMouse(id, { ...params }) -> void
- client.browsers.computer.pressKey(id, { ...params }) -> void
- client.browsers.computer.scroll(id, { ...params }) -> void
diff --git a/package.json b/package.json
index 13178cd..53f4cbd 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@onkernel/sdk",
- "version": "0.30.0",
+ "version": "0.31.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 6ace201..90ca176 100644
--- a/src/client.ts
+++ b/src/client.ts
@@ -616,9 +616,14 @@ export class Kernel {
getAPIList- = Pagination.AbstractPage
- >(
path: string,
Page: new (...args: any[]) => PageClass,
- opts?: RequestOptions,
+ opts?: PromiseOrValue,
): Pagination.PagePromise {
- return this.requestAPIList(Page, { method: 'get', path, ...opts });
+ return this.requestAPIList(
+ Page,
+ opts && 'then' in opts ?
+ opts.then((opts) => ({ method: 'get', path, ...opts }))
+ : { method: 'get', path, ...opts },
+ );
}
requestAPIList<
@@ -626,7 +631,7 @@ export class Kernel {
PageClass extends Pagination.AbstractPage
- = Pagination.AbstractPage
- ,
>(
Page: new (...args: ConstructorParameters) => PageClass,
- options: FinalRequestOptions,
+ options: PromiseOrValue,
): Pagination.PagePromise {
const request = this.makeRequest(options, null, undefined);
return new Pagination.PagePromise(this as any as Kernel, request, Page);
@@ -639,7 +644,7 @@ export class Kernel {
controller: AbortController,
): Promise {
const { signal, method, ...options } = init || {};
- const abort = controller.abort.bind(controller);
+ const abort = this._makeAbort(controller);
if (signal) signal.addEventListener('abort', abort, { once: true });
const timeout = setTimeout(abort, ms);
@@ -809,6 +814,12 @@ export class Kernel {
return headers.values;
}
+ private _makeAbort(controller: AbortController) {
+ // note: we can't just inline this method inside `fetchWithTimeout()` because then the closure
+ // would capture all request options, and cause a memory leak.
+ return () => controller.abort();
+ }
+
private buildBody({ options: { body, headers: rawHeaders } }: { options: FinalRequestOptions }): {
bodyHeaders: HeadersLike;
body: BodyInit | undefined;
diff --git a/src/resources/browsers/browsers.ts b/src/resources/browsers/browsers.ts
index f9b244f..c335cac 100644
--- a/src/resources/browsers/browsers.ts
+++ b/src/resources/browsers/browsers.ts
@@ -5,9 +5,11 @@ import * as Shared from '../shared';
import * as ComputerAPI from './computer';
import {
Computer,
+ ComputerBatchParams,
ComputerCaptureScreenshotParams,
ComputerClickMouseParams,
ComputerDragMouseParams,
+ ComputerGetMousePositionResponse,
ComputerMoveMouseParams,
ComputerPressKeyParams,
ComputerScrollParams,
@@ -764,7 +766,9 @@ export declare namespace Browsers {
export {
Computer as Computer,
+ type ComputerGetMousePositionResponse as ComputerGetMousePositionResponse,
type ComputerSetCursorVisibilityResponse as ComputerSetCursorVisibilityResponse,
+ type ComputerBatchParams as ComputerBatchParams,
type ComputerCaptureScreenshotParams as ComputerCaptureScreenshotParams,
type ComputerClickMouseParams as ComputerClickMouseParams,
type ComputerDragMouseParams as ComputerDragMouseParams,
diff --git a/src/resources/browsers/computer.ts b/src/resources/browsers/computer.ts
index 575cfad..6b15782 100644
--- a/src/resources/browsers/computer.ts
+++ b/src/resources/browsers/computer.ts
@@ -7,6 +7,26 @@ import { RequestOptions } from '../../internal/request-options';
import { path } from '../../internal/utils/path';
export class Computer extends APIResource {
+ /**
+ * Send an array of computer actions to execute in order on the browser instance.
+ * Execution stops on the first error. This reduces network latency compared to
+ * sending individual action requests.
+ *
+ * @example
+ * ```ts
+ * await client.browsers.computer.batch('id', {
+ * actions: [{ type: 'click_mouse' }],
+ * });
+ * ```
+ */
+ batch(id: string, body: ComputerBatchParams, options?: RequestOptions): APIPromise {
+ return this._client.post(path`/browsers/${id}/computer/batch`, {
+ body,
+ ...options,
+ headers: buildHeaders([{ Accept: '*/*' }, options?.headers]),
+ });
+ }
+
/**
* Capture a screenshot of the browser instance
*
@@ -72,6 +92,19 @@ export class Computer extends APIResource {
});
}
+ /**
+ * Get the current mouse cursor position on the browser instance
+ *
+ * @example
+ * ```ts
+ * const response =
+ * await client.browsers.computer.getMousePosition('id');
+ * ```
+ */
+ getMousePosition(id: string, options?: RequestOptions): APIPromise {
+ return this._client.post(path`/browsers/${id}/computer/get_mouse_position`, options);
+ }
+
/**
* Move the mouse cursor to the specified coordinates on the browser instance
*
@@ -163,6 +196,18 @@ export class Computer extends APIResource {
}
}
+export interface ComputerGetMousePositionResponse {
+ /**
+ * X coordinate of the cursor
+ */
+ x: number;
+
+ /**
+ * Y coordinate of the cursor
+ */
+ y: number;
+}
+
/**
* Generic OK response.
*/
@@ -173,6 +218,216 @@ export interface ComputerSetCursorVisibilityResponse {
ok: boolean;
}
+export interface ComputerBatchParams {
+ /**
+ * Ordered list of actions to execute. Execution stops on the first error.
+ */
+ actions: Array;
+}
+
+export namespace ComputerBatchParams {
+ /**
+ * A single computer action to execute as part of a batch. The `type` field selects
+ * which action to perform, and the corresponding field contains the action
+ * parameters. Exactly one action field matching the type must be provided.
+ */
+ export interface Action {
+ /**
+ * The type of action to perform.
+ */
+ type:
+ | 'click_mouse'
+ | 'move_mouse'
+ | 'type_text'
+ | 'press_key'
+ | 'scroll'
+ | 'drag_mouse'
+ | 'set_cursor'
+ | 'sleep';
+
+ click_mouse?: Action.ClickMouse;
+
+ drag_mouse?: Action.DragMouse;
+
+ move_mouse?: Action.MoveMouse;
+
+ press_key?: Action.PressKey;
+
+ scroll?: Action.Scroll;
+
+ set_cursor?: Action.SetCursor;
+
+ /**
+ * Pause execution for a specified duration.
+ */
+ sleep?: Action.Sleep;
+
+ type_text?: Action.TypeText;
+ }
+
+ export namespace Action {
+ export interface ClickMouse {
+ /**
+ * X coordinate of the click position
+ */
+ x: number;
+
+ /**
+ * Y coordinate of the click position
+ */
+ y: number;
+
+ /**
+ * Mouse button to interact with
+ */
+ button?: 'left' | 'right' | 'middle' | 'back' | 'forward';
+
+ /**
+ * Type of click action
+ */
+ click_type?: 'down' | 'up' | 'click';
+
+ /**
+ * Modifier keys to hold during the click
+ */
+ hold_keys?: Array;
+
+ /**
+ * Number of times to repeat the click
+ */
+ num_clicks?: number;
+ }
+
+ export interface DragMouse {
+ /**
+ * Ordered list of [x, y] coordinate pairs to move through while dragging. Must
+ * contain at least 2 points.
+ */
+ path: Array>;
+
+ /**
+ * Mouse button to drag with
+ */
+ button?: 'left' | 'middle' | 'right';
+
+ /**
+ * Delay in milliseconds between button down and starting to move along the path.
+ */
+ delay?: number;
+
+ /**
+ * Modifier keys to hold during the drag
+ */
+ hold_keys?: Array;
+
+ /**
+ * Delay in milliseconds between relative steps while dragging (not the initial
+ * delay).
+ */
+ step_delay_ms?: number;
+
+ /**
+ * Number of relative move steps per segment in the path. Minimum 1.
+ */
+ steps_per_segment?: number;
+ }
+
+ export interface MoveMouse {
+ /**
+ * X coordinate to move the cursor to
+ */
+ x: number;
+
+ /**
+ * Y coordinate to move the cursor to
+ */
+ y: number;
+
+ /**
+ * Modifier keys to hold during the move
+ */
+ hold_keys?: Array;
+ }
+
+ export interface PressKey {
+ /**
+ * List of key symbols to press. Each item should be a key symbol supported by
+ * xdotool (see X11 keysym definitions). Examples include "Return", "Shift",
+ * "Ctrl", "Alt", "F5". Items in this list could also be combinations, e.g.
+ * "Ctrl+t" or "Ctrl+Shift+Tab".
+ */
+ keys: Array;
+
+ /**
+ * Duration to hold the keys down in milliseconds. If omitted or 0, keys are
+ * tapped.
+ */
+ duration?: number;
+
+ /**
+ * Optional modifier keys to hold during the key press sequence.
+ */
+ hold_keys?: Array;
+ }
+
+ export interface Scroll {
+ /**
+ * X coordinate at which to perform the scroll
+ */
+ x: number;
+
+ /**
+ * Y coordinate at which to perform the scroll
+ */
+ y: number;
+
+ /**
+ * Horizontal scroll amount. Positive scrolls right, negative scrolls left.
+ */
+ delta_x?: number;
+
+ /**
+ * Vertical scroll amount. Positive scrolls down, negative scrolls up.
+ */
+ delta_y?: number;
+
+ /**
+ * Modifier keys to hold during the scroll
+ */
+ hold_keys?: Array;
+ }
+
+ export interface SetCursor {
+ /**
+ * Whether the cursor should be hidden or visible
+ */
+ hidden: boolean;
+ }
+
+ /**
+ * Pause execution for a specified duration.
+ */
+ export interface Sleep {
+ /**
+ * Duration to sleep in milliseconds.
+ */
+ duration_ms: number;
+ }
+
+ export interface TypeText {
+ /**
+ * Text to type on the browser instance
+ */
+ text: string;
+
+ /**
+ * Delay in milliseconds between keystrokes
+ */
+ delay?: number;
+ }
+ }
+}
+
export interface ComputerCaptureScreenshotParams {
region?: ComputerCaptureScreenshotParams.Region;
}
@@ -353,7 +608,9 @@ export interface ComputerTypeTextParams {
export declare namespace Computer {
export {
+ type ComputerGetMousePositionResponse as ComputerGetMousePositionResponse,
type ComputerSetCursorVisibilityResponse as ComputerSetCursorVisibilityResponse,
+ type ComputerBatchParams as ComputerBatchParams,
type ComputerCaptureScreenshotParams as ComputerCaptureScreenshotParams,
type ComputerClickMouseParams as ComputerClickMouseParams,
type ComputerDragMouseParams as ComputerDragMouseParams,
diff --git a/src/resources/browsers/index.ts b/src/resources/browsers/index.ts
index 046c1b2..178d54f 100644
--- a/src/resources/browsers/index.ts
+++ b/src/resources/browsers/index.ts
@@ -18,7 +18,9 @@ export {
} from './browsers';
export {
Computer,
+ type ComputerGetMousePositionResponse,
type ComputerSetCursorVisibilityResponse,
+ type ComputerBatchParams,
type ComputerCaptureScreenshotParams,
type ComputerClickMouseParams,
type ComputerDragMouseParams,
diff --git a/src/version.ts b/src/version.ts
index 91a9bb6..b6314c2 100644
--- a/src/version.ts
+++ b/src/version.ts
@@ -1 +1 @@
-export const VERSION = '0.30.0'; // x-release-please-version
+export const VERSION = '0.31.0'; // x-release-please-version
diff --git a/tests/api-resources/browsers/computer.test.ts b/tests/api-resources/browsers/computer.test.ts
index b95f0fa..dbc7831 100644
--- a/tests/api-resources/browsers/computer.test.ts
+++ b/tests/api-resources/browsers/computer.test.ts
@@ -8,6 +8,68 @@ const client = new Kernel({
});
describe('resource computer', () => {
+ // Prism tests are disabled
+ test.skip('batch: only required params', async () => {
+ const responsePromise = client.browsers.computer.batch('id', { actions: [{ type: 'click_mouse' }] });
+ 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);
+ });
+
+ // Prism tests are disabled
+ test.skip('batch: required and optional params', async () => {
+ const response = await client.browsers.computer.batch('id', {
+ actions: [
+ {
+ type: 'click_mouse',
+ click_mouse: {
+ x: 0,
+ y: 0,
+ button: 'left',
+ click_type: 'down',
+ hold_keys: ['string'],
+ num_clicks: 0,
+ },
+ drag_mouse: {
+ path: [
+ [0, 0],
+ [0, 0],
+ ],
+ button: 'left',
+ delay: 0,
+ hold_keys: ['string'],
+ step_delay_ms: 0,
+ steps_per_segment: 1,
+ },
+ move_mouse: {
+ x: 0,
+ y: 0,
+ hold_keys: ['string'],
+ },
+ press_key: {
+ keys: ['string'],
+ duration: 0,
+ hold_keys: ['string'],
+ },
+ scroll: {
+ x: 0,
+ y: 0,
+ delta_x: 0,
+ delta_y: 0,
+ hold_keys: ['string'],
+ },
+ set_cursor: { hidden: true },
+ sleep: { duration_ms: 0 },
+ type_text: { text: 'text', delay: 0 },
+ },
+ ],
+ });
+ });
+
test('captureScreenshot: 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(
@@ -82,6 +144,18 @@ describe('resource computer', () => {
});
});
+ // Prism tests are disabled
+ test.skip('getMousePosition', async () => {
+ const responsePromise = client.browsers.computer.getMousePosition('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);
+ });
+
// Prism tests are disabled
test.skip('moveMouse: only required params', async () => {
const responsePromise = client.browsers.computer.moveMouse('id', { x: 0, y: 0 });