browser-use-sdk 3.5.0 → 3.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -24,6 +24,18 @@ const result = await client.run("Find the top 3 trending repos on GitHub today")
24
24
  console.log(result.output);
25
25
  ```
26
26
 
27
+ ## v3 Bring Your Own LLM Key
28
+
29
+ Add your provider API key in Browser Use project settings, then enable BYOK for v3 agent runs:
30
+
31
+ ```typescript
32
+ import { BrowserUse } from "browser-use-sdk/v3";
33
+
34
+ const client = new BrowserUse({ useOwnKey: true });
35
+ const result = await client.run("Find the top 3 trending repos on GitHub today");
36
+ console.log(result.output);
37
+ ```
38
+
27
39
  ## Docs
28
40
 
29
41
  [docs.browser-use.com](https://docs.browser-use.com)
@@ -0,0 +1,175 @@
1
+ // src/core/errors.ts
2
+ var BrowserUseError = class extends Error {
3
+ statusCode;
4
+ detail;
5
+ constructor(statusCode, message, detail) {
6
+ super(message);
7
+ this.name = "BrowserUseError";
8
+ this.statusCode = statusCode;
9
+ this.detail = detail;
10
+ }
11
+ };
12
+
13
+ // src/core/x402.ts
14
+ var X402_BASE_URL_DEFAULT = "https://x402.api.browser-use.com/api/v3";
15
+ var X402_BASE_URL_DEFAULT_V2 = "https://x402.api.browser-use.com/api/v2";
16
+ var X402_BALANCE_BASE_URL_DEFAULT = "https://api.browser-use.com/api/v3";
17
+ function buildWalletAuthMessage(address, issuedAt, nonce) {
18
+ return `Browser Use x402 wallet authentication
19
+ Action: read credit balance
20
+ Wallet: ${address}
21
+ Issued At: ${issuedAt}
22
+ Nonce: ${nonce}`;
23
+ }
24
+ async function getWalletBalance(privateKey, opts = {}) {
25
+ let viem;
26
+ try {
27
+ viem = await import("viem/accounts");
28
+ } catch {
29
+ throw new Error(MISSING_X402);
30
+ }
31
+ const key = privateKey.startsWith("0x") ? privateKey : `0x${privateKey}`;
32
+ const account = viem.privateKeyToAccount(key);
33
+ const issuedAt = (/* @__PURE__ */ new Date()).toISOString();
34
+ const nonce = crypto.randomUUID().replace(/-/g, "");
35
+ const message = buildWalletAuthMessage(account.address, issuedAt, nonce);
36
+ const signature = await account.signMessage({ message });
37
+ const baseUrl = (opts.baseUrl ?? X402_BALANCE_BASE_URL_DEFAULT).replace(/\/$/, "");
38
+ const resp = await fetch(`${baseUrl}/x402/balance`, {
39
+ method: "POST",
40
+ headers: { "Content-Type": "application/json" },
41
+ body: JSON.stringify({ address: account.address, issued_at: issuedAt, nonce, signature, ...opts.extra })
42
+ });
43
+ if (!resp.ok) {
44
+ throw new Error(`x402 balance check failed: ${resp.status} ${await resp.text()}`);
45
+ }
46
+ return await resp.json();
47
+ }
48
+ var MISSING_X402 = "x402 mode requires the optional peer deps. Install them with: npm install @x402/fetch @x402/evm viem (or the equivalent for pnpm/yarn).";
49
+ async function x402ClientFromPrivateKey(privateKey) {
50
+ let viem;
51
+ let fetchPkg;
52
+ let evmPkg;
53
+ try {
54
+ viem = await import("viem/accounts");
55
+ fetchPkg = await import("@x402/fetch");
56
+ evmPkg = await import("@x402/evm");
57
+ } catch (e) {
58
+ throw new Error(MISSING_X402);
59
+ }
60
+ const key = privateKey.startsWith("0x") ? privateKey : `0x${privateKey}`;
61
+ const signer = viem.privateKeyToAccount(key);
62
+ const client = new fetchPkg.x402Client();
63
+ client.register("eip155:*", new evmPkg.ExactEvmScheme(signer));
64
+ return client;
65
+ }
66
+ async function wrapFetchWithX402(fetch2, x402Client) {
67
+ let fetchPkg;
68
+ try {
69
+ fetchPkg = await import("@x402/fetch");
70
+ } catch (e) {
71
+ throw new Error(MISSING_X402);
72
+ }
73
+ return fetchPkg.wrapFetchWithPayment(fetch2, x402Client);
74
+ }
75
+
76
+ // src/core/http.ts
77
+ var HttpClient = class {
78
+ apiKey;
79
+ baseUrl;
80
+ maxRetries;
81
+ timeout;
82
+ fetchPromise;
83
+ useApiKeyHeader;
84
+ constructor(options) {
85
+ this.apiKey = options.apiKey;
86
+ this.baseUrl = options.baseUrl.replace(/\/+$/, "");
87
+ this.maxRetries = options.maxRetries ?? 3;
88
+ this.timeout = options.timeout ?? 3e4;
89
+ this.fetchPromise = Promise.resolve(
90
+ options.fetch ?? ((input, init) => fetch(input, init))
91
+ );
92
+ this.useApiKeyHeader = options.apiKey !== "";
93
+ }
94
+ async request(method, path, options) {
95
+ const url = new URL(`${this.baseUrl}${path}`);
96
+ if (options?.query) {
97
+ for (const [key, value] of Object.entries(options.query)) {
98
+ if (value !== void 0 && value !== null) {
99
+ url.searchParams.set(key, String(value));
100
+ }
101
+ }
102
+ }
103
+ const headers = {};
104
+ if (this.useApiKeyHeader) {
105
+ headers["X-Browser-Use-API-Key"] = this.apiKey;
106
+ }
107
+ if (options?.body !== void 0) {
108
+ headers["Content-Type"] = "application/json";
109
+ }
110
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
111
+ if (attempt > 0) {
112
+ const delay = Math.min(1e3 * 2 ** (attempt - 1), 1e4);
113
+ await new Promise((resolve) => setTimeout(resolve, delay));
114
+ }
115
+ const controller = new AbortController();
116
+ const timeoutId = options?.signal ? void 0 : setTimeout(() => controller.abort(), this.timeout);
117
+ const signal = options?.signal ?? controller.signal;
118
+ try {
119
+ const fetchImpl = await this.fetchPromise;
120
+ const response = await fetchImpl(url.toString(), {
121
+ method,
122
+ headers,
123
+ body: options?.body !== void 0 ? JSON.stringify(options.body) : void 0,
124
+ signal
125
+ });
126
+ clearTimeout(timeoutId);
127
+ if (response.ok) {
128
+ if (response.status === 204) {
129
+ return void 0;
130
+ }
131
+ return await response.json();
132
+ }
133
+ const shouldRetry = response.status === 429 && attempt < this.maxRetries;
134
+ if (shouldRetry) {
135
+ continue;
136
+ }
137
+ let errorBody;
138
+ try {
139
+ errorBody = await response.json();
140
+ } catch {
141
+ }
142
+ const raw = typeof errorBody === "object" && errorBody !== null ? "message" in errorBody ? errorBody.message : "detail" in errorBody ? errorBody.detail : void 0 : void 0;
143
+ const message = raw === void 0 ? `HTTP ${response.status}` : typeof raw === "string" ? raw : JSON.stringify(raw);
144
+ throw new BrowserUseError(response.status, message, errorBody);
145
+ } catch (error) {
146
+ clearTimeout(timeoutId);
147
+ throw error;
148
+ }
149
+ }
150
+ throw new Error("Unreachable: retry loop exhausted");
151
+ }
152
+ get(path, query) {
153
+ return this.request("GET", path, { query });
154
+ }
155
+ post(path, body, query) {
156
+ return this.request("POST", path, { body, query });
157
+ }
158
+ patch(path, body, query) {
159
+ return this.request("PATCH", path, { body, query });
160
+ }
161
+ delete(path, query) {
162
+ return this.request("DELETE", path, { query });
163
+ }
164
+ };
165
+
166
+ export {
167
+ BrowserUseError,
168
+ HttpClient,
169
+ X402_BASE_URL_DEFAULT,
170
+ X402_BASE_URL_DEFAULT_V2,
171
+ getWalletBalance,
172
+ x402ClientFromPrivateKey,
173
+ wrapFetchWithX402
174
+ };
175
+ //# sourceMappingURL=chunk-ESJIWN3M.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/errors.ts","../src/core/x402.ts","../src/core/http.ts"],"sourcesContent":["export class BrowserUseError extends Error {\n readonly statusCode: number;\n readonly detail: unknown;\n\n constructor(statusCode: number, message: string, detail?: unknown) {\n super(message);\n this.name = \"BrowserUseError\";\n this.statusCode = statusCode;\n this.detail = detail;\n }\n}\n","/**\n * Helpers for the optional x402 (pay-per-request) integration.\n *\n * x402 is an HTTP payment protocol: instead of an API key, requests are\n * authenticated by signing a small USDC payment. See the docs at\n * `cloud/guides/x402` and https://www.x402.org for details.\n *\n * The peer deps `@x402/fetch`, `@x402/evm`, and `viem` are optional. They are\n * imported lazily so users who only use API-key auth never pay the cost.\n */\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type X402Client = any;\nexport type FetchLike = (\n input: RequestInfo | URL,\n init?: RequestInit,\n) => Promise<Response>;\n\nexport const X402_BASE_URL_DEFAULT = \"https://x402.api.browser-use.com/api/v3\";\nexport const X402_BASE_URL_DEFAULT_V2 = \"https://x402.api.browser-use.com/api/v2\";\nexport const X402_BALANCE_BASE_URL_DEFAULT = \"https://api.browser-use.com/api/v3\";\n\nexport interface X402WalletBalance {\n wallet: string;\n project_id: string;\n total_credits_usd: number;\n additional_credits_usd: number;\n}\n\nfunction buildWalletAuthMessage(address: string, issuedAt: string, nonce: string): string {\n return (\n \"Browser Use x402 wallet authentication\\n\" +\n \"Action: read credit balance\\n\" +\n `Wallet: ${address}\\n` +\n `Issued At: ${issuedAt}\\n` +\n `Nonce: ${nonce}`\n );\n}\n\n/**\n * Read a wallet-derived project's credit balance\n *\n * Authenticates with an off-chain wallet signature (EIP-191): signs a\n * message proving control of the address; the server resolves the wallet to its\n * project and returns the balance\n */\nexport async function getWalletBalance(\n privateKey: `0x${string}` | string,\n opts: { baseUrl?: string; extra?: Record<string, unknown> } = {},\n): Promise<X402WalletBalance> {\n let viem;\n try {\n // @ts-ignore - optional peer dep, may not be installed\n viem = await import(\"viem/accounts\");\n } catch {\n throw new Error(MISSING_X402);\n }\n const key = privateKey.startsWith(\"0x\") ? privateKey : `0x${privateKey}`;\n const account = viem.privateKeyToAccount(key as `0x${string}`);\n\n const issuedAt = new Date().toISOString();\n const nonce = crypto.randomUUID().replace(/-/g, \"\");\n const message = buildWalletAuthMessage(account.address, issuedAt, nonce);\n const signature = await account.signMessage({ message });\n\n const baseUrl = (opts.baseUrl ?? X402_BALANCE_BASE_URL_DEFAULT).replace(/\\/$/, \"\");\n const resp = await fetch(`${baseUrl}/x402/balance`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ address: account.address, issued_at: issuedAt, nonce, signature, ...opts.extra }),\n });\n if (!resp.ok) {\n throw new Error(`x402 balance check failed: ${resp.status} ${await resp.text()}`);\n }\n return (await resp.json()) as X402WalletBalance;\n}\n\nconst MISSING_X402 =\n \"x402 mode requires the optional peer deps. Install them with: \" +\n \"npm install @x402/fetch @x402/evm viem \" +\n \"(or the equivalent for pnpm/yarn).\";\n\n/** Build a ready-to-use x402 client from an EVM private key (`0x...`). */\nexport async function x402ClientFromPrivateKey(\n privateKey: `0x${string}` | string,\n): Promise<X402Client> {\n let viem;\n let fetchPkg;\n let evmPkg;\n try {\n // @ts-ignore - optional peer dep, may not be installed\n viem = await import(\"viem/accounts\");\n // @ts-ignore - optional peer dep, may not be installed\n fetchPkg = await import(\"@x402/fetch\");\n // @ts-ignore - optional peer dep, may not be installed\n evmPkg = await import(\"@x402/evm\");\n } catch (e) {\n throw new Error(MISSING_X402);\n }\n\n const key = privateKey.startsWith(\"0x\") ? privateKey : `0x${privateKey}`;\n const signer = viem.privateKeyToAccount(key as `0x${string}`);\n const client = new fetchPkg.x402Client();\n client.register(\"eip155:*\", new evmPkg.ExactEvmScheme(signer));\n return client;\n}\n\n/** Wrap a fetch with x402 payment handling. */\nexport async function wrapFetchWithX402(\n fetch: FetchLike,\n x402Client: X402Client,\n): Promise<FetchLike> {\n let fetchPkg;\n try {\n // @ts-ignore - optional peer dep, may not be installed\n fetchPkg = await import(\"@x402/fetch\");\n } catch (e) {\n throw new Error(MISSING_X402);\n }\n return fetchPkg.wrapFetchWithPayment(fetch, x402Client);\n}\n","import { BrowserUseError } from \"./errors.js\";\nimport type { FetchLike } from \"./x402.js\";\n\nexport interface HttpClientOptions {\n apiKey: string;\n baseUrl: string;\n maxRetries?: number;\n timeout?: number;\n /**\n * Optional custom fetch implementation. May be a sync value or a Promise\n * (for lazy resolution of e.g. x402-wrapped fetch). When set, replaces\n * `globalThis.fetch`. The `X-Browser-Use-API-Key` header is still sent if\n * `apiKey` is non-empty (top-up mode in x402); pass `apiKey: \"\"` to omit.\n */\n fetch?: FetchLike | Promise<FetchLike>;\n}\n\nexport class HttpClient {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly maxRetries: number;\n private readonly timeout: number;\n private readonly fetchPromise: Promise<FetchLike>;\n private readonly useApiKeyHeader: boolean;\n\n constructor(options: HttpClientOptions) {\n this.apiKey = options.apiKey;\n this.baseUrl = options.baseUrl.replace(/\\/+$/, \"\");\n this.maxRetries = options.maxRetries ?? 3;\n this.timeout = options.timeout ?? 30_000;\n this.fetchPromise = Promise.resolve(\n options.fetch ?? ((input, init) => fetch(input, init)),\n );\n // Send the API key header whenever apiKey is non-empty. In x402 mode\n // an empty apiKey means \"accountless\" (wallet is identity); a non-empty\n // apiKey means \"top up the existing key's project\".\n this.useApiKeyHeader = options.apiKey !== \"\";\n }\n\n async request<T>(\n method: string,\n path: string,\n options?: {\n body?: unknown;\n query?: Record<string, unknown>;\n signal?: AbortSignal;\n },\n ): Promise<T> {\n const url = new URL(`${this.baseUrl}${path}`);\n if (options?.query) {\n for (const [key, value] of Object.entries(options.query)) {\n if (value !== undefined && value !== null) {\n url.searchParams.set(key, String(value));\n }\n }\n }\n\n const headers: Record<string, string> = {};\n if (this.useApiKeyHeader) {\n headers[\"X-Browser-Use-API-Key\"] = this.apiKey;\n }\n if (options?.body !== undefined) {\n headers[\"Content-Type\"] = \"application/json\";\n }\n\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\n if (attempt > 0) {\n const delay = Math.min(1000 * 2 ** (attempt - 1), 10_000);\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n\n const controller = new AbortController();\n const timeoutId = options?.signal\n ? undefined\n : setTimeout(() => controller.abort(), this.timeout);\n\n // Combine user signal with internal timeout when both are present\n const signal = options?.signal ?? controller.signal;\n\n try {\n const fetchImpl = await this.fetchPromise;\n const response = await fetchImpl(url.toString(), {\n method,\n headers,\n body: options?.body !== undefined ? JSON.stringify(options.body) : undefined,\n signal,\n });\n\n clearTimeout(timeoutId);\n\n if (response.ok) {\n if (response.status === 204) {\n return undefined as T;\n }\n return (await response.json()) as T;\n }\n\n const shouldRetry =\n response.status === 429 &&\n attempt < this.maxRetries;\n\n if (shouldRetry) {\n continue;\n }\n\n let errorBody: unknown;\n try {\n errorBody = await response.json();\n } catch {\n /* ignore parse errors */\n }\n const raw =\n typeof errorBody === \"object\" && errorBody !== null\n ? \"message\" in errorBody\n ? (errorBody as Record<string, unknown>).message\n : \"detail\" in errorBody\n ? (errorBody as Record<string, unknown>).detail\n : undefined\n : undefined;\n const message =\n raw === undefined\n ? `HTTP ${response.status}`\n : typeof raw === \"string\"\n ? raw\n : JSON.stringify(raw);\n throw new BrowserUseError(response.status, message, errorBody);\n } catch (error) {\n clearTimeout(timeoutId);\n throw error;\n }\n }\n\n throw new Error(\"Unreachable: retry loop exhausted\");\n }\n\n get<T>(path: string, query?: Record<string, unknown>): Promise<T> {\n return this.request<T>(\"GET\", path, { query });\n }\n\n post<T>(path: string, body?: unknown, query?: Record<string, unknown>): Promise<T> {\n return this.request<T>(\"POST\", path, { body, query });\n }\n\n patch<T>(path: string, body?: unknown, query?: Record<string, unknown>): Promise<T> {\n return this.request<T>(\"PATCH\", path, { body, query });\n }\n\n delete<T>(path: string, query?: Record<string, unknown>): Promise<T> {\n return this.request<T>(\"DELETE\", path, { query });\n }\n}\n"],"mappings":"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","names":["fetch"]}
@@ -0,0 +1,175 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/core/errors.ts
2
+ var BrowserUseError = class extends Error {
3
+
4
+
5
+ constructor(statusCode, message, detail) {
6
+ super(message);
7
+ this.name = "BrowserUseError";
8
+ this.statusCode = statusCode;
9
+ this.detail = detail;
10
+ }
11
+ };
12
+
13
+ // src/core/x402.ts
14
+ var X402_BASE_URL_DEFAULT = "https://x402.api.browser-use.com/api/v3";
15
+ var X402_BASE_URL_DEFAULT_V2 = "https://x402.api.browser-use.com/api/v2";
16
+ var X402_BALANCE_BASE_URL_DEFAULT = "https://api.browser-use.com/api/v3";
17
+ function buildWalletAuthMessage(address, issuedAt, nonce) {
18
+ return `Browser Use x402 wallet authentication
19
+ Action: read credit balance
20
+ Wallet: ${address}
21
+ Issued At: ${issuedAt}
22
+ Nonce: ${nonce}`;
23
+ }
24
+ async function getWalletBalance(privateKey, opts = {}) {
25
+ let viem;
26
+ try {
27
+ viem = await Promise.resolve().then(() => _interopRequireWildcard(require("viem/accounts")));
28
+ } catch (e2) {
29
+ throw new Error(MISSING_X402);
30
+ }
31
+ const key = privateKey.startsWith("0x") ? privateKey : `0x${privateKey}`;
32
+ const account = viem.privateKeyToAccount(key);
33
+ const issuedAt = (/* @__PURE__ */ new Date()).toISOString();
34
+ const nonce = crypto.randomUUID().replace(/-/g, "");
35
+ const message = buildWalletAuthMessage(account.address, issuedAt, nonce);
36
+ const signature = await account.signMessage({ message });
37
+ const baseUrl = (_nullishCoalesce(opts.baseUrl, () => ( X402_BALANCE_BASE_URL_DEFAULT))).replace(/\/$/, "");
38
+ const resp = await fetch(`${baseUrl}/x402/balance`, {
39
+ method: "POST",
40
+ headers: { "Content-Type": "application/json" },
41
+ body: JSON.stringify({ address: account.address, issued_at: issuedAt, nonce, signature, ...opts.extra })
42
+ });
43
+ if (!resp.ok) {
44
+ throw new Error(`x402 balance check failed: ${resp.status} ${await resp.text()}`);
45
+ }
46
+ return await resp.json();
47
+ }
48
+ var MISSING_X402 = "x402 mode requires the optional peer deps. Install them with: npm install @x402/fetch @x402/evm viem (or the equivalent for pnpm/yarn).";
49
+ async function x402ClientFromPrivateKey(privateKey) {
50
+ let viem;
51
+ let fetchPkg;
52
+ let evmPkg;
53
+ try {
54
+ viem = await Promise.resolve().then(() => _interopRequireWildcard(require("viem/accounts")));
55
+ fetchPkg = await Promise.resolve().then(() => _interopRequireWildcard(require("@x402/fetch")));
56
+ evmPkg = await Promise.resolve().then(() => _interopRequireWildcard(require("@x402/evm")));
57
+ } catch (e) {
58
+ throw new Error(MISSING_X402);
59
+ }
60
+ const key = privateKey.startsWith("0x") ? privateKey : `0x${privateKey}`;
61
+ const signer = viem.privateKeyToAccount(key);
62
+ const client = new fetchPkg.x402Client();
63
+ client.register("eip155:*", new evmPkg.ExactEvmScheme(signer));
64
+ return client;
65
+ }
66
+ async function wrapFetchWithX402(fetch2, x402Client) {
67
+ let fetchPkg;
68
+ try {
69
+ fetchPkg = await Promise.resolve().then(() => _interopRequireWildcard(require("@x402/fetch")));
70
+ } catch (e) {
71
+ throw new Error(MISSING_X402);
72
+ }
73
+ return fetchPkg.wrapFetchWithPayment(fetch2, x402Client);
74
+ }
75
+
76
+ // src/core/http.ts
77
+ var HttpClient = class {
78
+
79
+
80
+
81
+
82
+
83
+
84
+ constructor(options) {
85
+ this.apiKey = options.apiKey;
86
+ this.baseUrl = options.baseUrl.replace(/\/+$/, "");
87
+ this.maxRetries = _nullishCoalesce(options.maxRetries, () => ( 3));
88
+ this.timeout = _nullishCoalesce(options.timeout, () => ( 3e4));
89
+ this.fetchPromise = Promise.resolve(
90
+ _nullishCoalesce(options.fetch, () => ( ((input, init) => fetch(input, init))))
91
+ );
92
+ this.useApiKeyHeader = options.apiKey !== "";
93
+ }
94
+ async request(method, path, options) {
95
+ const url = new URL(`${this.baseUrl}${path}`);
96
+ if (_optionalChain([options, 'optionalAccess', _ => _.query])) {
97
+ for (const [key, value] of Object.entries(options.query)) {
98
+ if (value !== void 0 && value !== null) {
99
+ url.searchParams.set(key, String(value));
100
+ }
101
+ }
102
+ }
103
+ const headers = {};
104
+ if (this.useApiKeyHeader) {
105
+ headers["X-Browser-Use-API-Key"] = this.apiKey;
106
+ }
107
+ if (_optionalChain([options, 'optionalAccess', _2 => _2.body]) !== void 0) {
108
+ headers["Content-Type"] = "application/json";
109
+ }
110
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
111
+ if (attempt > 0) {
112
+ const delay = Math.min(1e3 * 2 ** (attempt - 1), 1e4);
113
+ await new Promise((resolve) => setTimeout(resolve, delay));
114
+ }
115
+ const controller = new AbortController();
116
+ const timeoutId = _optionalChain([options, 'optionalAccess', _3 => _3.signal]) ? void 0 : setTimeout(() => controller.abort(), this.timeout);
117
+ const signal = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _4 => _4.signal]), () => ( controller.signal));
118
+ try {
119
+ const fetchImpl = await this.fetchPromise;
120
+ const response = await fetchImpl(url.toString(), {
121
+ method,
122
+ headers,
123
+ body: _optionalChain([options, 'optionalAccess', _5 => _5.body]) !== void 0 ? JSON.stringify(options.body) : void 0,
124
+ signal
125
+ });
126
+ clearTimeout(timeoutId);
127
+ if (response.ok) {
128
+ if (response.status === 204) {
129
+ return void 0;
130
+ }
131
+ return await response.json();
132
+ }
133
+ const shouldRetry = response.status === 429 && attempt < this.maxRetries;
134
+ if (shouldRetry) {
135
+ continue;
136
+ }
137
+ let errorBody;
138
+ try {
139
+ errorBody = await response.json();
140
+ } catch (e3) {
141
+ }
142
+ const raw = typeof errorBody === "object" && errorBody !== null ? "message" in errorBody ? errorBody.message : "detail" in errorBody ? errorBody.detail : void 0 : void 0;
143
+ const message = raw === void 0 ? `HTTP ${response.status}` : typeof raw === "string" ? raw : JSON.stringify(raw);
144
+ throw new BrowserUseError(response.status, message, errorBody);
145
+ } catch (error) {
146
+ clearTimeout(timeoutId);
147
+ throw error;
148
+ }
149
+ }
150
+ throw new Error("Unreachable: retry loop exhausted");
151
+ }
152
+ get(path, query) {
153
+ return this.request("GET", path, { query });
154
+ }
155
+ post(path, body, query) {
156
+ return this.request("POST", path, { body, query });
157
+ }
158
+ patch(path, body, query) {
159
+ return this.request("PATCH", path, { body, query });
160
+ }
161
+ delete(path, query) {
162
+ return this.request("DELETE", path, { query });
163
+ }
164
+ };
165
+
166
+
167
+
168
+
169
+
170
+
171
+
172
+
173
+
174
+ exports.BrowserUseError = BrowserUseError; exports.HttpClient = HttpClient; exports.X402_BASE_URL_DEFAULT = X402_BASE_URL_DEFAULT; exports.X402_BASE_URL_DEFAULT_V2 = X402_BASE_URL_DEFAULT_V2; exports.getWalletBalance = getWalletBalance; exports.x402ClientFromPrivateKey = x402ClientFromPrivateKey; exports.wrapFetchWithX402 = wrapFetchWithX402;
175
+ //# sourceMappingURL=chunk-QMK4AUMH.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/larsencundric/Documents/browser-use/sdk/browser-use-node/dist/chunk-QMK4AUMH.cjs","../src/core/errors.ts","../src/core/x402.ts","../src/core/http.ts"],"names":[],"mappings":"AAAA;ACAO,IAAM,gBAAA,EAAN,MAAA,QAA8B,MAAM;AAAA,EAChC;AAAA,EACA;AAAA,EAET,WAAA,CAAY,UAAA,EAAoB,OAAA,EAAiB,MAAA,EAAkB;AACjE,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,KAAA,EAAO,iBAAA;AACZ,IAAA,IAAA,CAAK,WAAA,EAAa,UAAA;AAClB,IAAA,IAAA,CAAK,OAAA,EAAS,MAAA;AAAA,EAChB;AACF,CAAA;ADCA;AACA;AEMO,IAAM,sBAAA,EAAwB,yCAAA;AAC9B,IAAM,yBAAA,EAA2B,yCAAA;AACjC,IAAM,8BAAA,EAAgC,oCAAA;AAS7C,SAAS,sBAAA,CAAuB,OAAA,EAAiB,QAAA,EAAkB,KAAA,EAAuB;AACxF,EAAA,OACE,CAAA;AAAA;AAAA,QAAA,EAEW,OAAO,CAAA;AAAA,WAAA,EACJ,QAAQ,CAAA;AAAA,OAAA,EACZ,KAAK,CAAA,CAAA;AAEnB;AASsB;AAIhB,EAAA;AACA,EAAA;AAEK,IAAA;AACD,EAAA;AACI,IAAA;AACZ,EAAA;AACY,EAAA;AACN,EAAA;AAEA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AAEA,EAAA;AACO,EAAA;AACH,IAAA;AACG,IAAA;AACA,IAAA;AACZ,EAAA;AACS,EAAA;AACE,IAAA;AACZ,EAAA;AACc,EAAA;AAChB;AAEM;AAMgB;AAGhB,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AAEK,IAAA;AAEI,IAAA;AAEF,IAAA;AACC,EAAA;AACA,IAAA;AACZ,EAAA;AAEY,EAAA;AACN,EAAA;AACA,EAAA;AACC,EAAA;AACA,EAAA;AACT;AAGsB;AAIhB,EAAA;AACA,EAAA;AAES,IAAA;AACD,EAAA;AACA,IAAA;AACZ,EAAA;AACO,EAAA;AACT;AF9Ce;AACA;AG1DF;AACM,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AAEL,EAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACK,uBAAA;AACV,IAAA;AAIK,IAAA;AACP,EAAA;AAEM,EAAA;AASE,IAAA;AACF,IAAA;AACF,MAAA;AACM,QAAA;AACE,UAAA;AACN,QAAA;AACF,MAAA;AACF,IAAA;AAEM,IAAA;AACG,IAAA;AACC,MAAA;AACV,IAAA;AACI,IAAA;AACM,MAAA;AACV,IAAA;AAES,IAAA;AACH,MAAA;AACI,QAAA;AACA,QAAA;AACR,MAAA;AAEM,MAAA;AACA,MAAA;AAKA,MAAA;AAEF,MAAA;AACI,QAAA;AACA,QAAA;AACJ,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACD,QAAA;AAED,QAAA;AAEI,QAAA;AACE,UAAA;AACF,YAAA;AACF,UAAA;AACA,UAAA;AACF,QAAA;AAEM,QAAA;AAIF,QAAA;AACF,UAAA;AACF,QAAA;AAEI,QAAA;AACA,QAAA;AACF,UAAA;AACF,QAAA;AAEA,QAAA;AACM,QAAA;AAQA,QAAA;AAMA,QAAA;AACC,MAAA;AACP,QAAA;AACM,QAAA;AACR,MAAA;AACF,IAAA;AAEU,IAAA;AACZ,EAAA;AAEqB,EAAA;AACZ,IAAA;AACT,EAAA;AAEsB,EAAA;AACb,IAAA;AACT,EAAA;AAEuB,EAAA;AACd,IAAA;AACT,EAAA;AAEwB,EAAA;AACf,IAAA;AACT,EAAA;AACF;AHce;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/Users/larsencundric/Documents/browser-use/sdk/browser-use-node/dist/chunk-QMK4AUMH.cjs","sourcesContent":[null,"export class BrowserUseError extends Error {\n readonly statusCode: number;\n readonly detail: unknown;\n\n constructor(statusCode: number, message: string, detail?: unknown) {\n super(message);\n this.name = \"BrowserUseError\";\n this.statusCode = statusCode;\n this.detail = detail;\n }\n}\n","/**\n * Helpers for the optional x402 (pay-per-request) integration.\n *\n * x402 is an HTTP payment protocol: instead of an API key, requests are\n * authenticated by signing a small USDC payment. See the docs at\n * `cloud/guides/x402` and https://www.x402.org for details.\n *\n * The peer deps `@x402/fetch`, `@x402/evm`, and `viem` are optional. They are\n * imported lazily so users who only use API-key auth never pay the cost.\n */\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type X402Client = any;\nexport type FetchLike = (\n input: RequestInfo | URL,\n init?: RequestInit,\n) => Promise<Response>;\n\nexport const X402_BASE_URL_DEFAULT = \"https://x402.api.browser-use.com/api/v3\";\nexport const X402_BASE_URL_DEFAULT_V2 = \"https://x402.api.browser-use.com/api/v2\";\nexport const X402_BALANCE_BASE_URL_DEFAULT = \"https://api.browser-use.com/api/v3\";\n\nexport interface X402WalletBalance {\n wallet: string;\n project_id: string;\n total_credits_usd: number;\n additional_credits_usd: number;\n}\n\nfunction buildWalletAuthMessage(address: string, issuedAt: string, nonce: string): string {\n return (\n \"Browser Use x402 wallet authentication\\n\" +\n \"Action: read credit balance\\n\" +\n `Wallet: ${address}\\n` +\n `Issued At: ${issuedAt}\\n` +\n `Nonce: ${nonce}`\n );\n}\n\n/**\n * Read a wallet-derived project's credit balance\n *\n * Authenticates with an off-chain wallet signature (EIP-191): signs a\n * message proving control of the address; the server resolves the wallet to its\n * project and returns the balance\n */\nexport async function getWalletBalance(\n privateKey: `0x${string}` | string,\n opts: { baseUrl?: string; extra?: Record<string, unknown> } = {},\n): Promise<X402WalletBalance> {\n let viem;\n try {\n // @ts-ignore - optional peer dep, may not be installed\n viem = await import(\"viem/accounts\");\n } catch {\n throw new Error(MISSING_X402);\n }\n const key = privateKey.startsWith(\"0x\") ? privateKey : `0x${privateKey}`;\n const account = viem.privateKeyToAccount(key as `0x${string}`);\n\n const issuedAt = new Date().toISOString();\n const nonce = crypto.randomUUID().replace(/-/g, \"\");\n const message = buildWalletAuthMessage(account.address, issuedAt, nonce);\n const signature = await account.signMessage({ message });\n\n const baseUrl = (opts.baseUrl ?? X402_BALANCE_BASE_URL_DEFAULT).replace(/\\/$/, \"\");\n const resp = await fetch(`${baseUrl}/x402/balance`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ address: account.address, issued_at: issuedAt, nonce, signature, ...opts.extra }),\n });\n if (!resp.ok) {\n throw new Error(`x402 balance check failed: ${resp.status} ${await resp.text()}`);\n }\n return (await resp.json()) as X402WalletBalance;\n}\n\nconst MISSING_X402 =\n \"x402 mode requires the optional peer deps. Install them with: \" +\n \"npm install @x402/fetch @x402/evm viem \" +\n \"(or the equivalent for pnpm/yarn).\";\n\n/** Build a ready-to-use x402 client from an EVM private key (`0x...`). */\nexport async function x402ClientFromPrivateKey(\n privateKey: `0x${string}` | string,\n): Promise<X402Client> {\n let viem;\n let fetchPkg;\n let evmPkg;\n try {\n // @ts-ignore - optional peer dep, may not be installed\n viem = await import(\"viem/accounts\");\n // @ts-ignore - optional peer dep, may not be installed\n fetchPkg = await import(\"@x402/fetch\");\n // @ts-ignore - optional peer dep, may not be installed\n evmPkg = await import(\"@x402/evm\");\n } catch (e) {\n throw new Error(MISSING_X402);\n }\n\n const key = privateKey.startsWith(\"0x\") ? privateKey : `0x${privateKey}`;\n const signer = viem.privateKeyToAccount(key as `0x${string}`);\n const client = new fetchPkg.x402Client();\n client.register(\"eip155:*\", new evmPkg.ExactEvmScheme(signer));\n return client;\n}\n\n/** Wrap a fetch with x402 payment handling. */\nexport async function wrapFetchWithX402(\n fetch: FetchLike,\n x402Client: X402Client,\n): Promise<FetchLike> {\n let fetchPkg;\n try {\n // @ts-ignore - optional peer dep, may not be installed\n fetchPkg = await import(\"@x402/fetch\");\n } catch (e) {\n throw new Error(MISSING_X402);\n }\n return fetchPkg.wrapFetchWithPayment(fetch, x402Client);\n}\n","import { BrowserUseError } from \"./errors.js\";\nimport type { FetchLike } from \"./x402.js\";\n\nexport interface HttpClientOptions {\n apiKey: string;\n baseUrl: string;\n maxRetries?: number;\n timeout?: number;\n /**\n * Optional custom fetch implementation. May be a sync value or a Promise\n * (for lazy resolution of e.g. x402-wrapped fetch). When set, replaces\n * `globalThis.fetch`. The `X-Browser-Use-API-Key` header is still sent if\n * `apiKey` is non-empty (top-up mode in x402); pass `apiKey: \"\"` to omit.\n */\n fetch?: FetchLike | Promise<FetchLike>;\n}\n\nexport class HttpClient {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly maxRetries: number;\n private readonly timeout: number;\n private readonly fetchPromise: Promise<FetchLike>;\n private readonly useApiKeyHeader: boolean;\n\n constructor(options: HttpClientOptions) {\n this.apiKey = options.apiKey;\n this.baseUrl = options.baseUrl.replace(/\\/+$/, \"\");\n this.maxRetries = options.maxRetries ?? 3;\n this.timeout = options.timeout ?? 30_000;\n this.fetchPromise = Promise.resolve(\n options.fetch ?? ((input, init) => fetch(input, init)),\n );\n // Send the API key header whenever apiKey is non-empty. In x402 mode\n // an empty apiKey means \"accountless\" (wallet is identity); a non-empty\n // apiKey means \"top up the existing key's project\".\n this.useApiKeyHeader = options.apiKey !== \"\";\n }\n\n async request<T>(\n method: string,\n path: string,\n options?: {\n body?: unknown;\n query?: Record<string, unknown>;\n signal?: AbortSignal;\n },\n ): Promise<T> {\n const url = new URL(`${this.baseUrl}${path}`);\n if (options?.query) {\n for (const [key, value] of Object.entries(options.query)) {\n if (value !== undefined && value !== null) {\n url.searchParams.set(key, String(value));\n }\n }\n }\n\n const headers: Record<string, string> = {};\n if (this.useApiKeyHeader) {\n headers[\"X-Browser-Use-API-Key\"] = this.apiKey;\n }\n if (options?.body !== undefined) {\n headers[\"Content-Type\"] = \"application/json\";\n }\n\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\n if (attempt > 0) {\n const delay = Math.min(1000 * 2 ** (attempt - 1), 10_000);\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n\n const controller = new AbortController();\n const timeoutId = options?.signal\n ? undefined\n : setTimeout(() => controller.abort(), this.timeout);\n\n // Combine user signal with internal timeout when both are present\n const signal = options?.signal ?? controller.signal;\n\n try {\n const fetchImpl = await this.fetchPromise;\n const response = await fetchImpl(url.toString(), {\n method,\n headers,\n body: options?.body !== undefined ? JSON.stringify(options.body) : undefined,\n signal,\n });\n\n clearTimeout(timeoutId);\n\n if (response.ok) {\n if (response.status === 204) {\n return undefined as T;\n }\n return (await response.json()) as T;\n }\n\n const shouldRetry =\n response.status === 429 &&\n attempt < this.maxRetries;\n\n if (shouldRetry) {\n continue;\n }\n\n let errorBody: unknown;\n try {\n errorBody = await response.json();\n } catch {\n /* ignore parse errors */\n }\n const raw =\n typeof errorBody === \"object\" && errorBody !== null\n ? \"message\" in errorBody\n ? (errorBody as Record<string, unknown>).message\n : \"detail\" in errorBody\n ? (errorBody as Record<string, unknown>).detail\n : undefined\n : undefined;\n const message =\n raw === undefined\n ? `HTTP ${response.status}`\n : typeof raw === \"string\"\n ? raw\n : JSON.stringify(raw);\n throw new BrowserUseError(response.status, message, errorBody);\n } catch (error) {\n clearTimeout(timeoutId);\n throw error;\n }\n }\n\n throw new Error(\"Unreachable: retry loop exhausted\");\n }\n\n get<T>(path: string, query?: Record<string, unknown>): Promise<T> {\n return this.request<T>(\"GET\", path, { query });\n }\n\n post<T>(path: string, body?: unknown, query?: Record<string, unknown>): Promise<T> {\n return this.request<T>(\"POST\", path, { body, query });\n }\n\n patch<T>(path: string, body?: unknown, query?: Record<string, unknown>): Promise<T> {\n return this.request<T>(\"PATCH\", path, { body, query });\n }\n\n delete<T>(path: string, query?: Record<string, unknown>): Promise<T> {\n return this.request<T>(\"DELETE\", path, { query });\n }\n}\n"]}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Helpers for the optional x402 (pay-per-request) integration.
3
+ *
4
+ * x402 is an HTTP payment protocol: instead of an API key, requests are
5
+ * authenticated by signing a small USDC payment. See the docs at
6
+ * `cloud/guides/x402` and https://www.x402.org for details.
7
+ *
8
+ * The peer deps `@x402/fetch`, `@x402/evm`, and `viem` are optional. They are
9
+ * imported lazily so users who only use API-key auth never pay the cost.
10
+ */
11
+ type X402Client = any;
12
+ type FetchLike = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
13
+ interface X402WalletBalance {
14
+ wallet: string;
15
+ project_id: string;
16
+ total_credits_usd: number;
17
+ additional_credits_usd: number;
18
+ }
19
+ /**
20
+ * Read a wallet-derived project's credit balance
21
+ *
22
+ * Authenticates with an off-chain wallet signature (EIP-191): signs a
23
+ * message proving control of the address; the server resolves the wallet to its
24
+ * project and returns the balance
25
+ */
26
+ declare function getWalletBalance(privateKey: `0x${string}` | string, opts?: {
27
+ baseUrl?: string;
28
+ extra?: Record<string, unknown>;
29
+ }): Promise<X402WalletBalance>;
30
+
31
+ interface HttpClientOptions {
32
+ apiKey: string;
33
+ baseUrl: string;
34
+ maxRetries?: number;
35
+ timeout?: number;
36
+ /**
37
+ * Optional custom fetch implementation. May be a sync value or a Promise
38
+ * (for lazy resolution of e.g. x402-wrapped fetch). When set, replaces
39
+ * `globalThis.fetch`. The `X-Browser-Use-API-Key` header is still sent if
40
+ * `apiKey` is non-empty (top-up mode in x402); pass `apiKey: ""` to omit.
41
+ */
42
+ fetch?: FetchLike | Promise<FetchLike>;
43
+ }
44
+ declare class HttpClient {
45
+ private readonly apiKey;
46
+ private readonly baseUrl;
47
+ private readonly maxRetries;
48
+ private readonly timeout;
49
+ private readonly fetchPromise;
50
+ private readonly useApiKeyHeader;
51
+ constructor(options: HttpClientOptions);
52
+ request<T>(method: string, path: string, options?: {
53
+ body?: unknown;
54
+ query?: Record<string, unknown>;
55
+ signal?: AbortSignal;
56
+ }): Promise<T>;
57
+ get<T>(path: string, query?: Record<string, unknown>): Promise<T>;
58
+ post<T>(path: string, body?: unknown, query?: Record<string, unknown>): Promise<T>;
59
+ patch<T>(path: string, body?: unknown, query?: Record<string, unknown>): Promise<T>;
60
+ delete<T>(path: string, query?: Record<string, unknown>): Promise<T>;
61
+ }
62
+
63
+ declare class BrowserUseError extends Error {
64
+ readonly statusCode: number;
65
+ readonly detail: unknown;
66
+ constructor(statusCode: number, message: string, detail?: unknown);
67
+ }
68
+
69
+ export { BrowserUseError as B, HttpClient as H, type X402Client as X, type X402WalletBalance as a, getWalletBalance as g };
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Helpers for the optional x402 (pay-per-request) integration.
3
+ *
4
+ * x402 is an HTTP payment protocol: instead of an API key, requests are
5
+ * authenticated by signing a small USDC payment. See the docs at
6
+ * `cloud/guides/x402` and https://www.x402.org for details.
7
+ *
8
+ * The peer deps `@x402/fetch`, `@x402/evm`, and `viem` are optional. They are
9
+ * imported lazily so users who only use API-key auth never pay the cost.
10
+ */
11
+ type X402Client = any;
12
+ type FetchLike = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
13
+ interface X402WalletBalance {
14
+ wallet: string;
15
+ project_id: string;
16
+ total_credits_usd: number;
17
+ additional_credits_usd: number;
18
+ }
19
+ /**
20
+ * Read a wallet-derived project's credit balance
21
+ *
22
+ * Authenticates with an off-chain wallet signature (EIP-191): signs a
23
+ * message proving control of the address; the server resolves the wallet to its
24
+ * project and returns the balance
25
+ */
26
+ declare function getWalletBalance(privateKey: `0x${string}` | string, opts?: {
27
+ baseUrl?: string;
28
+ extra?: Record<string, unknown>;
29
+ }): Promise<X402WalletBalance>;
30
+
31
+ interface HttpClientOptions {
32
+ apiKey: string;
33
+ baseUrl: string;
34
+ maxRetries?: number;
35
+ timeout?: number;
36
+ /**
37
+ * Optional custom fetch implementation. May be a sync value or a Promise
38
+ * (for lazy resolution of e.g. x402-wrapped fetch). When set, replaces
39
+ * `globalThis.fetch`. The `X-Browser-Use-API-Key` header is still sent if
40
+ * `apiKey` is non-empty (top-up mode in x402); pass `apiKey: ""` to omit.
41
+ */
42
+ fetch?: FetchLike | Promise<FetchLike>;
43
+ }
44
+ declare class HttpClient {
45
+ private readonly apiKey;
46
+ private readonly baseUrl;
47
+ private readonly maxRetries;
48
+ private readonly timeout;
49
+ private readonly fetchPromise;
50
+ private readonly useApiKeyHeader;
51
+ constructor(options: HttpClientOptions);
52
+ request<T>(method: string, path: string, options?: {
53
+ body?: unknown;
54
+ query?: Record<string, unknown>;
55
+ signal?: AbortSignal;
56
+ }): Promise<T>;
57
+ get<T>(path: string, query?: Record<string, unknown>): Promise<T>;
58
+ post<T>(path: string, body?: unknown, query?: Record<string, unknown>): Promise<T>;
59
+ patch<T>(path: string, body?: unknown, query?: Record<string, unknown>): Promise<T>;
60
+ delete<T>(path: string, query?: Record<string, unknown>): Promise<T>;
61
+ }
62
+
63
+ declare class BrowserUseError extends Error {
64
+ readonly statusCode: number;
65
+ readonly detail: unknown;
66
+ constructor(statusCode: number, message: string, detail?: unknown);
67
+ }
68
+
69
+ export { BrowserUseError as B, HttpClient as H, type X402Client as X, type X402WalletBalance as a, getWalletBalance as g };
package/dist/index.cjs CHANGED
@@ -1,7 +1,10 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class;
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } async function _asyncNullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return await rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class;
2
2
 
3
3
 
4
- var _chunkJT4IL3IZcjs = require('./chunk-JT4IL3IZ.cjs');
4
+
5
+
6
+
7
+ var _chunkQMK4AUMHcjs = require('./chunk-QMK4AUMH.cjs');
5
8
 
6
9
  // src/v2/client.ts
7
10
  var _zod = require('zod');
@@ -398,18 +401,36 @@ var BrowserUse = class {
398
401
 
399
402
 
400
403
  constructor(options = {}) {
401
- const apiKey = _nullishCoalesce(_nullishCoalesce(options.apiKey, () => ( process.env.BROWSER_USE_API_KEY)), () => ( ""));
402
- if (!apiKey) {
403
- throw new Error(
404
- "No API key provided. Pass apiKey or set BROWSER_USE_API_KEY."
405
- );
404
+ const x402PrivateKey = _nullishCoalesce(options.x402PrivateKey, () => ( process.env.BROWSER_USE_X402_PRIVATE_KEY));
405
+ if (options.x402 || x402PrivateKey) {
406
+ const topupKey = _nullishCoalesce(_nullishCoalesce(options.apiKey, () => ( process.env.BROWSER_USE_API_KEY)), () => ( ""));
407
+ const fetchPromise = (async () => {
408
+ const x402Client = await _asyncNullishCoalesce(options.x402, async () => ( await _chunkQMK4AUMHcjs.x402ClientFromPrivateKey.call(void 0, x402PrivateKey)));
409
+ return _chunkQMK4AUMHcjs.wrapFetchWithX402.call(void 0, globalThis.fetch, x402Client);
410
+ })();
411
+ fetchPromise.catch(() => {
412
+ });
413
+ this.http = new (0, _chunkQMK4AUMHcjs.HttpClient)({
414
+ apiKey: topupKey,
415
+ baseUrl: _nullishCoalesce(options.baseUrl, () => ( _chunkQMK4AUMHcjs.X402_BASE_URL_DEFAULT_V2)),
416
+ maxRetries: options.maxRetries,
417
+ timeout: options.timeout,
418
+ fetch: fetchPromise
419
+ });
420
+ } else {
421
+ const apiKey = _nullishCoalesce(_nullishCoalesce(options.apiKey, () => ( process.env.BROWSER_USE_API_KEY)), () => ( ""));
422
+ if (!apiKey) {
423
+ throw new Error(
424
+ "No credentials provided. Pass apiKey / set BROWSER_USE_API_KEY, or pass x402PrivateKey / set BROWSER_USE_X402_PRIVATE_KEY for pay-per-request access via USDC."
425
+ );
426
+ }
427
+ this.http = new (0, _chunkQMK4AUMHcjs.HttpClient)({
428
+ apiKey,
429
+ baseUrl: _nullishCoalesce(options.baseUrl, () => ( DEFAULT_BASE_URL)),
430
+ maxRetries: options.maxRetries,
431
+ timeout: options.timeout
432
+ });
406
433
  }
407
- this.http = new (0, _chunkJT4IL3IZcjs.HttpClient)({
408
- apiKey,
409
- baseUrl: _nullishCoalesce(options.baseUrl, () => ( DEFAULT_BASE_URL)),
410
- maxRetries: options.maxRetries,
411
- timeout: options.timeout
412
- });
413
434
  this.billing = new Billing(this.http);
414
435
  this.tasks = new Tasks(this.http);
415
436
  this.sessions = new Sessions(this.http);
@@ -441,5 +462,5 @@ var BrowserUse = class {
441
462
 
442
463
 
443
464
 
444
- exports.Billing = Billing; exports.BrowserUse = BrowserUse; exports.BrowserUseError = _chunkJT4IL3IZcjs.BrowserUseError; exports.Browsers = Browsers; exports.Files = Files; exports.Marketplace = Marketplace; exports.Profiles = Profiles; exports.Sessions = Sessions; exports.Skills = Skills; exports.TaskRun = TaskRun; exports.Tasks = Tasks;
465
+ exports.Billing = Billing; exports.BrowserUse = BrowserUse; exports.BrowserUseError = _chunkQMK4AUMHcjs.BrowserUseError; exports.Browsers = Browsers; exports.Files = Files; exports.Marketplace = Marketplace; exports.Profiles = Profiles; exports.Sessions = Sessions; exports.Skills = Skills; exports.TaskRun = TaskRun; exports.Tasks = Tasks;
445
466
  //# sourceMappingURL=index.cjs.map