diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c34dbad..535c4a7 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -21,10 +21,10 @@ jobs:
runs-on: ${{ github.repository == 'stainless-sdks/browserbase-node' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata')
steps:
- - uses: actions/checkout@v6
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up Node
- uses: actions/setup-node@v4
+ uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: '20'
@@ -43,10 +43,10 @@ jobs:
contents: read
id-token: write
steps:
- - uses: actions/checkout@v6
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up Node
- uses: actions/setup-node@v4
+ uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: '20'
@@ -61,7 +61,7 @@ jobs:
github.repository == 'stainless-sdks/browserbase-node' &&
!startsWith(github.ref, 'refs/heads/stl/')
id: github-oidc
- uses: actions/github-script@v8
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: core.setOutput('github_token', await core.getIDToken());
@@ -80,10 +80,10 @@ jobs:
runs-on: ${{ github.repository == 'stainless-sdks/browserbase-node' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
steps:
- - uses: actions/checkout@v6
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up Node
- uses: actions/setup-node@v4
+ uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: '20'
diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml
index d2c264b..38c849f 100644
--- a/.github/workflows/publish-npm.yml
+++ b/.github/workflows/publish-npm.yml
@@ -14,10 +14,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v6
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up Node
- uses: actions/setup-node@v3
+ uses: actions/setup-node@3235b876344d2a9aa001b8d1453c930bba69e610 # v3.9.1
with:
node-version: '20'
diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml
index 144f559..8b31abb 100644
--- a/.github/workflows/release-doctor.yml
+++ b/.github/workflows/release-doctor.yml
@@ -12,7 +12,7 @@ jobs:
if: github.repository == 'browserbase/sdk-node' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next')
steps:
- - uses: actions/checkout@v6
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Check release environment
run: |
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index f393718..a9b8e02 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "2.10.0"
+ ".": "2.11.0"
}
diff --git a/.stats.yml b/.stats.yml
index bf21668..4e2d03e 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 21
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase%2Fbrowserbase-921d3c61c7aa06269f74bee63cee993597944f913429caa2aa2e00dd51fab60f.yml
-openapi_spec_hash: d35b9613c41bf172fa2b28aceef10b39
-config_hash: cf04ecfb8dad5fbd8b85be25d6e9ec55
+configured_endpoints: 23
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase/browserbase-2118fd938d408dda6ed82d06c48b0785fad91fd54b5397acc3421a49a386c791.yml
+openapi_spec_hash: 8e48a39a55a11b128028b47747aea775
+config_hash: 40fbac80e24faaa0dc19e93368bcd821
diff --git a/CHANGELOG.md b/CHANGELOG.md
index abb6690..7075cbd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,23 @@
# Changelog
+## 2.11.0 (2026-05-13)
+
+Full Changelog: [v2.10.0...v2.11.0](https://github.com/browserbase/sdk-node/compare/v2.10.0...v2.11.0)
+
+### Features
+
+* [CORE-1928][apps/api] Add `PENDING` as a valid session state ([6c1dc59](https://github.com/browserbase/sdk-node/commit/6c1dc59e8ed581d88c26483a84efecd3903f4379))
+* [CORE-1979] [apps/api] Regenerate OpenAPI spec to match current routes ([90f048c](https://github.com/browserbase/sdk-node/commit/90f048c79e02ead952b73d6bd185f6d58fa25076))
+* **api:** add replays ([c1bcc8b](https://github.com/browserbase/sdk-node/commit/c1bcc8b9e6baff112a6672b24dccba0cec683c72))
+* support setting headers via env ([c4bce5e](https://github.com/browserbase/sdk-node/commit/c4bce5e45f6dc0d2f560313ea70fd9bb1ac28fa6))
+
+
+### Chores
+
+* **internal:** codegen related update ([e079c39](https://github.com/browserbase/sdk-node/commit/e079c39733d21312ee5d9e11fed35a2b8bba93be))
+* **internal:** more robust bootstrap script ([39adddf](https://github.com/browserbase/sdk-node/commit/39adddf3d60bcf94349cb9103a7f41a1b0bb9f9f))
+* **tests:** bump steady to v0.22.1 ([cd4bf1a](https://github.com/browserbase/sdk-node/commit/cd4bf1aad650b63d38638218919715a790c1c819))
+
## 2.10.0 (2026-04-06)
Full Changelog: [v2.9.0...v2.10.0](https://github.com/browserbase/sdk-node/compare/v2.9.0...v2.10.0)
diff --git a/api.md b/api.md
index eb38395..11f9afd 100644
--- a/api.md
+++ b/api.md
@@ -114,3 +114,14 @@ Types:
Methods:
- client.sessions.uploads.create(id, { ...params }) -> UploadCreateResponse
+
+## Replays
+
+Types:
+
+- ReplayRetrieveResponse
+
+Methods:
+
+- client.sessions.replays.retrieve(id) -> ReplayRetrieveResponse
+- client.sessions.replays.retrievePage(id, pageId) -> Response
diff --git a/package-lock.json b/package-lock.json
index a719b05..64a24c0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@browserbasehq/sdk",
- "version": "2.10.0",
+ "version": "2.11.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@browserbasehq/sdk",
- "version": "2.10.0",
+ "version": "2.11.0",
"dependencies": {
"@types/node": "^18.11.18",
"@types/node-fetch": "^2.6.4",
diff --git a/package.json b/package.json
index 095c2c4..d836657 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@browserbasehq/sdk",
- "version": "2.10.0",
+ "version": "2.11.0",
"description": "The official Node.js library for the Browserbase API",
"author": "Browserbase ",
"types": "dist/index.d.ts",
diff --git a/scripts/bootstrap b/scripts/bootstrap
index f68beda..28b5985 100755
--- a/scripts/bootstrap
+++ b/scripts/bootstrap
@@ -4,7 +4,7 @@ set -e
cd "$(dirname "$0")/.."
-if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ] && [ -t 0 ]; then
+if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "${SKIP_BREW:-}" != "1" ] && [ -t 0 ]; then
brew bundle check >/dev/null 2>&1 || {
echo -n "==> Install Homebrew dependencies? (y/N): "
read -r response
diff --git a/scripts/mock b/scripts/mock
index 5cd7c15..feebe5e 100755
--- a/scripts/mock
+++ b/scripts/mock
@@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}"
# Run steady mock on the given spec
if [ "$1" == "--daemon" ]; then
# Pre-install the package so the download doesn't eat into the startup timeout
- npm exec --package=@stdy/cli@0.20.2 -- steady --version
+ npm exec --package=@stdy/cli@0.22.1 -- steady --version
- npm exec --package=@stdy/cli@0.20.2 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log &
+ npm exec --package=@stdy/cli@0.22.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log &
# Wait for server to come online via health endpoint (max 30s)
echo -n "Waiting for server"
@@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then
echo
else
- npm exec --package=@stdy/cli@0.20.2 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL"
+ npm exec --package=@stdy/cli@0.22.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL"
fi
diff --git a/scripts/test b/scripts/test
index a9d718c..19b8d0c 100755
--- a/scripts/test
+++ b/scripts/test
@@ -43,7 +43,7 @@ elif ! steady_is_running ; then
echo -e "To run the server, pass in the path or url of your OpenAPI"
echo -e "spec to the steady command:"
echo
- echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.20.2 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}"
+ echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.22.1 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}"
echo
exit 1
diff --git a/src/core.ts b/src/core.ts
index 821e4e5..10b9d3f 100644
--- a/src/core.ts
+++ b/src/core.ts
@@ -1054,10 +1054,10 @@ export const ensurePresent = (value: T | null | undefined): T => {
*/
export const readEnv = (env: string): string | undefined => {
if (typeof process !== 'undefined') {
- return process.env?.[env]?.trim() ?? undefined;
+ return process.env?.[env]?.trim() || undefined;
}
if (typeof Deno !== 'undefined') {
- return Deno.env?.get?.(env)?.trim();
+ return Deno.env?.get?.(env)?.trim() || undefined;
}
return undefined;
};
diff --git a/src/index.ts b/src/index.ts
index 56c1535..19c7fb5 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -139,6 +139,18 @@ export class Browserbase extends Core.APIClient {
fetch: options.fetch,
});
+ const customHeadersEnv = Core.readEnv('BROWSERBASE_CUSTOM_HEADERS');
+ if (customHeadersEnv) {
+ const parsed: Record = {};
+ for (const line of customHeadersEnv.split('\n')) {
+ const colon = line.indexOf(':');
+ if (colon >= 0) {
+ parsed[line.substring(0, colon).trim()] = line.substring(colon + 1).trim();
+ }
+ }
+ options.defaultHeaders = { ...parsed, ...options.defaultHeaders };
+ }
+
this._options = options;
this.apiKey = apiKey;
diff --git a/src/resources/sessions/index.ts b/src/resources/sessions/index.ts
index 9c9a61e..b3c2a80 100644
--- a/src/resources/sessions/index.ts
+++ b/src/resources/sessions/index.ts
@@ -3,6 +3,7 @@
export { Downloads } from './downloads';
export { Logs, type SessionLog, type LogListResponse } from './logs';
export { Recording, type SessionRecording, type RecordingRetrieveResponse } from './recording';
+export { Replays, type ReplayRetrieveResponse } from './replays';
export {
Sessions,
type Session,
diff --git a/src/resources/sessions/replays.ts b/src/resources/sessions/replays.ts
new file mode 100644
index 0000000..70d1f3b
--- /dev/null
+++ b/src/resources/sessions/replays.ts
@@ -0,0 +1,49 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../../resource';
+import * as Core from '../../core';
+import { type Response } from '../../_shims/index';
+
+export class Replays extends APIResource {
+ /**
+ * Returns page metadata for a session replay, including timing information and the
+ * URL of each page's HLS playlist.
+ */
+ retrieve(id: string, options?: Core.RequestOptions): Core.APIPromise {
+ return this._client.get(`/v1/sessions/${id}/replays`, options);
+ }
+
+ /**
+ * Returns an HLS VOD media playlist (.m3u8) for a specific page of a session
+ * replay.
+ */
+ retrievePage(id: string, pageId: string, options?: Core.RequestOptions): Core.APIPromise {
+ return this._client.get(`/v1/sessions/${id}/replays/${pageId}`, {
+ ...options,
+ headers: { Accept: 'application/vnd.apple.mpegurl', ...options?.headers },
+ __binaryResponse: true,
+ });
+ }
+}
+
+export interface ReplayRetrieveResponse {
+ pageCount: number;
+
+ pages: Array;
+}
+
+export namespace ReplayRetrieveResponse {
+ export interface Page {
+ endTimeMs: number;
+
+ pageId: string;
+
+ startTimeMs: number;
+
+ url: string;
+ }
+}
+
+export declare namespace Replays {
+ export { type ReplayRetrieveResponse as ReplayRetrieveResponse };
+}
diff --git a/src/resources/sessions/sessions.ts b/src/resources/sessions/sessions.ts
index 183afb7..ac5b37d 100644
--- a/src/resources/sessions/sessions.ts
+++ b/src/resources/sessions/sessions.ts
@@ -9,6 +9,8 @@ import * as LogsAPI from './logs';
import { LogListResponse, Logs, SessionLog } from './logs';
import * as RecordingAPI from './recording';
import { Recording, RecordingRetrieveResponse, SessionRecording } from './recording';
+import * as ReplaysAPI from './replays';
+import { ReplayRetrieveResponse, Replays } from './replays';
import * as UploadsAPI from './uploads';
import { UploadCreateParams, UploadCreateResponse, Uploads } from './uploads';
@@ -17,6 +19,7 @@ export class Sessions extends APIResource {
logs: LogsAPI.Logs = new LogsAPI.Logs(this._client);
recording: RecordingAPI.Recording = new RecordingAPI.Recording(this._client);
uploads: UploadsAPI.Uploads = new UploadsAPI.Uploads(this._client);
+ replays: ReplaysAPI.Replays = new ReplaysAPI.Replays(this._client);
/**
* Create a Session
@@ -99,7 +102,7 @@ export interface Session {
startedAt: string;
- status: 'RUNNING' | 'ERROR' | 'TIMED_OUT' | 'COMPLETED';
+ status: 'PENDING' | 'RUNNING' | 'ERROR' | 'TIMED_OUT' | 'COMPLETED';
updatedAt: string;
@@ -263,6 +266,12 @@ export namespace SessionCreateParams {
*/
extensionId?: string;
+ /**
+ * Enable or disable ignoring of certificate errors in the browser. Defaults to
+ * `true`.
+ */
+ ignoreCertificateErrors?: boolean;
+
/**
* Enable or disable session logging. Defaults to `true`.
*/
@@ -424,13 +433,14 @@ export interface SessionListParams {
*/
q?: string;
- status?: 'RUNNING' | 'ERROR' | 'TIMED_OUT' | 'COMPLETED';
+ status?: 'PENDING' | 'RUNNING' | 'ERROR' | 'TIMED_OUT' | 'COMPLETED';
}
Sessions.Downloads = Downloads;
Sessions.Logs = Logs;
Sessions.Recording = Recording;
Sessions.Uploads = Uploads;
+Sessions.Replays = Replays;
export declare namespace Sessions {
export {
@@ -459,4 +469,6 @@ export declare namespace Sessions {
type UploadCreateResponse as UploadCreateResponse,
type UploadCreateParams as UploadCreateParams,
};
+
+ export { Replays as Replays, type ReplayRetrieveResponse as ReplayRetrieveResponse };
}
diff --git a/src/version.ts b/src/version.ts
index 7b16f63..e91ff8d 100644
--- a/src/version.ts
+++ b/src/version.ts
@@ -1 +1 @@
-export const VERSION = '2.10.0'; // x-release-please-version
+export const VERSION = '2.11.0'; // x-release-please-version
diff --git a/tests/api-resources/sessions/replays.test.ts b/tests/api-resources/sessions/replays.test.ts
new file mode 100644
index 0000000..4387aaa
--- /dev/null
+++ b/tests/api-resources/sessions/replays.test.ts
@@ -0,0 +1,40 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Browserbase from '@browserbasehq/sdk';
+import { Response } from 'node-fetch';
+
+const client = new Browserbase({
+ apiKey: 'My API Key',
+ baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource replays', () => {
+ test('retrieve', async () => {
+ const responsePromise = client.sessions.replays.retrieve('182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e');
+ 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);
+ });
+
+ test('retrieve: request options instead of 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.sessions.replays.retrieve('182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e', {
+ path: '/_stainless_unknown_path',
+ }),
+ ).rejects.toThrow(Browserbase.NotFoundError);
+ });
+
+ test('retrievePage: request options instead of 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.sessions.replays.retrievePage('182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e', '090', {
+ path: '/_stainless_unknown_path',
+ }),
+ ).rejects.toThrow(Browserbase.NotFoundError);
+ });
+});
diff --git a/tests/api-resources/sessions/sessions.test.ts b/tests/api-resources/sessions/sessions.test.ts
index 765cf69..d13d1be 100644
--- a/tests/api-resources/sessions/sessions.test.ts
+++ b/tests/api-resources/sessions/sessions.test.ts
@@ -39,6 +39,7 @@ describe('resource sessions', () => {
captchaInputSelector: 'captchaInputSelector',
context: { id: 'id', persist: true },
extensionId: 'extensionId',
+ ignoreCertificateErrors: true,
logSession: true,
os: 'windows',
recordSession: true,
@@ -126,7 +127,7 @@ describe('resource sessions', () => {
test('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.sessions.list({ q: 'q', status: 'RUNNING' }, { path: '/_stainless_unknown_path' }),
+ client.sessions.list({ q: 'q', status: 'PENDING' }, { path: '/_stainless_unknown_path' }),
).rejects.toThrow(Browserbase.NotFoundError);
});