From cd226a4bfe6a4593556fa85c181ef7751956e5af Mon Sep 17 00:00:00 2001 From: nikhil Date: Tue, 13 Oct 2026 16:53:10 -0700 Subject: [PATCH 1/2] Add Web Bot Auth and proxy routes session options - CreateSessionParams.webBotAuth (true, or { onlyDomains }) signs the session's requests with Web Bot Auth. It works for sessions and agent tasks; SessionLaunchState.webBotAuth reports how a session signs. - CreateSessionParams.proxyRoutes chooses, per host, whether traffic goes through the session's proxy ("default") or straight out ("direct"). It only applies when creating a session. - README sections for both, and the version goes to 1.3.0. Co-Authored-By: Claude Opus 5.5 --- README.md | 39 ++++++++++ package.json | 2 +- src/types/index.ts | 3 + src/types/session.ts | 47 ++++++++++++ .../session-web-bot-auth-proxy-routes.test.ts | 74 +++++++++++++++++++ 5 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 tests/unit/session-web-bot-auth-proxy-routes.test.ts diff --git a/README.md b/README.md index 5799494..1bf438a 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,45 @@ status and truncation metadata before using output. Public types, including See the [WebMCP guide](https://hyperbrowser.ai/docs/sessions/webmcp) for complete examples, cancellation behavior, human submission, and limits. +## Web Bot Auth + +A session can sign its requests with Web Bot Auth so websites can verify that they +come from Hyperbrowser. Web Bot Auth is enabled for teams on request. + +```typescript +const session = await client.sessions.create({ webBotAuth: true }); +console.log(session.launchState?.webBotAuth?.signatureAgent); // "https://www.hyperbrowser.ai" + +// Sign only requests to these hosts and their subdomains: +await client.sessions.create({ webBotAuth: { onlyDomains: ["example.com"] } }); +``` + +Agent tasks accept `webBotAuth` in `sessionOptions`; scrape, crawl, and extract +jobs do not. It cannot be combined with `useUltraStealth` or `startFromSnapshot`. +See the [Web Bot Auth guide](https://hyperbrowser.ai/docs/sessions/web-bot-auth). + +## Proxy routes + +`proxyRoutes` sends some hosts' traffic straight out instead of through the +session's proxy, or the other way round. The first route that matches a host +decides; hosts that no route matches use the proxy. Proxy routes require +`useProxy` and are enabled for teams on request. + +```typescript +const session = await client.sessions.create({ + useProxy: true, + proxyRoutes: [ + { match: "api.example.com", proxy: "default" }, // stays on the proxy + { match: ["*.example.com", "cdn.example.net"], proxy: "direct" }, + ], +}); +``` + +A pattern is a hostname, `*.example.com` (its subdomains, not `example.com` itself), +or `*` (every host). Proxy routes only apply to `sessions.create` and cannot be +combined with `allowInternetAccess: false`, `allowOut`, or `denyOut`. See the +[proxy routes guide](https://hyperbrowser.ai/docs/sessions/proxy-routes). + ## Usage ### Playwright diff --git a/package.json b/package.json index 43a57af..106b6ca 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@hyperbrowser/sdk", - "version": "1.2.1", + "version": "1.3.0", "description": "Node SDK for Hyperbrowser API", "author": "", "repository": { diff --git a/src/types/index.ts b/src/types/index.ts index 6e5ea11..d18cece 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -156,8 +156,11 @@ export { SessionEventLog, SessionProfile, SessionLaunchState, + SessionWebBotAuth, SessionCreditBreakdown, StartSessionFromSnapshotParams, + WebBotAuthParams, + ProxyRoute, UpdateSessionProfileParams, UpdateSessionProxyLocationParams, UpdateSessionProxyParams, diff --git a/src/types/session.ts b/src/types/session.ts index 84c8ee4..6ff2a55 100644 --- a/src/types/session.ts +++ b/src/types/session.ts @@ -23,8 +23,18 @@ export interface SessionProfile { persistChanges?: boolean; } +/** How a session signs its requests with Web Bot Auth. */ +export interface SessionWebBotAuth { + /** The identity the session signs as, e.g. "https://www.hyperbrowser.ai". */ + signatureAgent: string; + /** Present when signing is limited to these hostnames and their subdomains. */ + onlyDomains?: string[]; +} + export interface SessionLaunchState { enableWebMcp?: boolean; + /** Present only for a session that signs its requests with Web Bot Auth. */ + webBotAuth?: SessionWebBotAuth; useUltraStealth?: boolean; useStealth?: boolean; useProxy?: boolean; @@ -165,9 +175,46 @@ export interface StartSessionFromSnapshotParams { snapshotId: string; } +export interface WebBotAuthParams { + /** + * Sign only requests to these hostnames (e.g. "example.com") and their + * subdomains. No scheme, port or path; at most 50. + */ + onlyDomains?: string[]; +} + +export interface ProxyRoute { + /** + * The hosts this route applies to: a hostname, "*.example.com" (subdomains, + * not example.com itself) or "*" (every host), or a list of them. + */ + match: string | string[]; + /** + * "direct" sends the traffic straight out of the browser's machine; + * "default" sends it through the session's proxy. + */ + proxy: "direct" | "default"; +} + export interface CreateSessionParams { /** Enable page-provided WebMCP tools. Defaults to false on the server. */ enableWebMcp?: boolean; + /** + * Sign the session's requests with Web Bot Auth so websites can verify they + * come from Hyperbrowser. Pass `{ onlyDomains }` to sign only for those + * hosts. Requires Web Bot Auth to be enabled for your team; works for + * sessions and agent tasks, and cannot be combined with `useUltraStealth` + * or `startFromSnapshot`. + */ + webBotAuth?: boolean | WebBotAuthParams; + /** + * Choose, per host, whether the session's traffic goes through its proxy or + * straight out. The first route that matches a host decides; hosts that no + * route matches use the proxy. Requires `useProxy` and proxy routes to be + * enabled for your team, only applies when creating a session, and cannot + * be combined with `allowInternetAccess: false`, `allowOut` or `denyOut`. + */ + proxyRoutes?: ProxyRoute[]; useUltraStealth?: boolean; useStealth?: boolean; useProxy?: boolean; diff --git a/tests/unit/session-web-bot-auth-proxy-routes.test.ts b/tests/unit/session-web-bot-auth-proxy-routes.test.ts new file mode 100644 index 0000000..07c31a8 --- /dev/null +++ b/tests/unit/session-web-bot-auth-proxy-routes.test.ts @@ -0,0 +1,74 @@ +import { afterEach, expect, expectTypeOf, test, vi } from "vitest"; +import { Response } from "node-fetch"; + +const fetchMock = vi.fn(); +vi.mock("node-fetch", async () => { + const actual = await vi.importActual("node-fetch"); + return { ...actual, default: (...args: unknown[]) => fetchMock(...args) }; +}); + +import { HyperbrowserClient as Hyperbrowser } from "../../src/client"; +import type { + CreateSessionParams, + ProxyRoute, + SessionLaunchState, + SessionWebBotAuth, + WebBotAuthParams, +} from "../../src/types"; + +const client = () => new Hyperbrowser({ apiKey: "test-key", baseUrl: "https://example.test" }); +const respond = (body: unknown, status = 200) => new Response(JSON.stringify(body), { status }); +const lastBody = () => JSON.parse(fetchMock.mock.lastCall![1].body); + +afterEach(() => fetchMock.mockReset()); + +test("session creation sends webBotAuth as given", async () => { + fetchMock.mockImplementation(() => Promise.resolve(respond({ id: "session" }))); + const sdk = client(); + for (const params of [ + {}, + { webBotAuth: true }, + { webBotAuth: false }, + { webBotAuth: { onlyDomains: ["example.com", "shop.example.org"] } }, + ] satisfies CreateSessionParams[]) { + await sdk.sessions.create(params); + expect(lastBody()).toEqual(params); + } +}); + +test("session creation sends proxyRoutes in order", async () => { + fetchMock.mockImplementation(() => Promise.resolve(respond({ id: "session" }))); + const params = { + useProxy: true, + proxyRoutes: [ + { match: "api.example.com", proxy: "default" }, + { match: ["*.example.com", "cdn.example.net"], proxy: "direct" }, + ], + } satisfies CreateSessionParams; + await client().sessions.create(params); + expect(lastBody()).toEqual(params); +}); + +test("agent tasks pass webBotAuth through sessionOptions", async () => { + fetchMock.mockImplementation(() => Promise.resolve(respond({ jobId: "job" }))); + await client().agents.browserUse.start({ + task: "Open example.com", + sessionOptions: { webBotAuth: { onlyDomains: ["example.com"] } }, + }); + expect(fetchMock.mock.lastCall![0]).toBe("https://example.test/api/task/browser-use"); + expect(lastBody().sessionOptions).toEqual({ webBotAuth: { onlyDomains: ["example.com"] } }); +}); + +test("public types describe the options and the signing launch state", () => { + expectTypeOf().toEqualTypeOf< + boolean | WebBotAuthParams | undefined + >(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf<"direct" | "default">(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf<{ + signatureAgent: string; + onlyDomains?: string[]; + }>(); +}); From 6df60f780fc8a7fe6fa7d1ecc7a8d39bde464976 Mon Sep 17 00:00:00 2001 From: nikhil Date: Wed, 14 Oct 2026 14:11:14 -0700 Subject: [PATCH 2/2] Leave the version bump for the release Co-Authored-By: Claude Opus 5.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 106b6ca..43a57af 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@hyperbrowser/sdk", - "version": "1.3.0", + "version": "1.2.1", "description": "Node SDK for Hyperbrowser API", "author": "", "repository": {