From d12c7fae75a9ed3346dc423d21c8c19aa65c0b3f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 5 Feb 2026 03:52:51 +0000 Subject: [PATCH 1/6] chore(client): restructure abort controller binding --- src/client.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/client.ts b/src/client.ts index 6ace201..a0f8f1a 100644 --- a/src/client.ts +++ b/src/client.ts @@ -639,7 +639,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); @@ -665,6 +665,7 @@ export class Kernel { return await this.fetch.call(undefined, url, fetchOptions); } finally { clearTimeout(timeout); + if (signal) signal.removeEventListener('abort', abort); } } @@ -809,6 +810,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; From 4e4c9df0cf07d7f4e457b28c16431389b5cf7fd1 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 6 Feb 2026 04:40:41 +0000 Subject: [PATCH 2/6] fix(client): avoid removing abort listener too early --- src/client.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/client.ts b/src/client.ts index a0f8f1a..ba0ebf4 100644 --- a/src/client.ts +++ b/src/client.ts @@ -665,7 +665,6 @@ export class Kernel { return await this.fetch.call(undefined, url, fetchOptions); } finally { clearTimeout(timeout); - if (signal) signal.removeEventListener('abort', abort); } } From 5c521491092a3ea0386daa75d3552f9ddf88aad2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 6 Feb 2026 04:41:13 +0000 Subject: [PATCH 3/6] chore(internal): fix pagination internals not accepting option promises --- src/client.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/client.ts b/src/client.ts index ba0ebf4..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); From a35521f4745d58ed0e28554b4bc1a797e3d39f83 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 6 Feb 2026 12:38:43 +0000 Subject: [PATCH 4/6] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 710d8bd..28bf561 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 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-652441005cba60f2ada5328d8dfc60165f6ab0bb271d6b64feca250fe73dc197.yml +openapi_spec_hash: f50a08ba2578efd8b756bdd920510c50 config_hash: dd218aae3f852dff79e77febc2077b8e From 56f8c3303f7c1ac4082bffd756bc4b1ea4836ab1 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 6 Feb 2026 18:44:50 +0000 Subject: [PATCH 5/6] feat: add batch computer action proxy endpoint --- .stats.yml | 8 +- api.md | 3 + src/resources/browsers/browsers.ts | 4 + src/resources/browsers/computer.ts | 257 ++++++++++++++++++ src/resources/browsers/index.ts | 2 + tests/api-resources/browsers/computer.test.ts | 74 +++++ 6 files changed, 344 insertions(+), 4 deletions(-) diff --git a/.stats.yml b/.stats.yml index 28bf561..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-652441005cba60f2ada5328d8dfc60165f6ab0bb271d6b64feca250fe73dc197.yml -openapi_spec_hash: f50a08ba2578efd8b756bdd920510c50 -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/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/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/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 }); From b736823fc38ea65bf083091941392287497c5e2f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 6 Feb 2026 18:45:34 +0000 Subject: [PATCH 6/6] release: 0.31.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 19 +++++++++++++++++++ package.json | 2 +- src/version.ts | 2 +- 4 files changed, 22 insertions(+), 3 deletions(-) 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/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/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/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