@browserbasehq/convex-stagehand 0.0.2 → 0.1.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.
@@ -2,9 +2,33 @@
2
2
  * Stagehand REST API Client
3
3
  *
4
4
  * Wraps the Stagehand API at https://api.stagehand.browserbase.com
5
+ * Wire format matches the OpenAPI spec defined in stagehand/packages/core/lib/v3/types/public/api.ts
5
6
  */
6
7
 
7
- const API_BASE = "https://api.stagehand.browserbase.com/v1";
8
+ export type BrowserbaseRegion =
9
+ | "us-west-2"
10
+ | "us-east-1"
11
+ | "eu-central-1"
12
+ | "ap-southeast-1";
13
+
14
+ export interface BrowserbaseSessionCreateParams {
15
+ region?: BrowserbaseRegion;
16
+ }
17
+
18
+ /** Multi-region API URL mapping (matches official SDK) */
19
+ const REGION_API_URLS: Record<BrowserbaseRegion, string> = {
20
+ "us-west-2": "https://api.stagehand.browserbase.com",
21
+ "us-east-1": "https://api.use1.stagehand.browserbase.com",
22
+ "eu-central-1": "https://api.euc1.stagehand.browserbase.com",
23
+ "ap-southeast-1": "https://api.apse1.stagehand.browserbase.com",
24
+ };
25
+
26
+ export function getApiBase(region?: BrowserbaseRegion): string {
27
+ const baseUrl =
28
+ (region && REGION_API_URLS[region]) ||
29
+ REGION_API_URLS["us-west-2"];
30
+ return `${baseUrl}/v1`;
31
+ }
8
32
 
9
33
  export interface ApiConfig {
10
34
  browserbaseApiKey: string;
@@ -13,18 +37,22 @@ export interface ApiConfig {
13
37
  modelName?: string;
14
38
  }
15
39
 
40
+ /** Matches SessionStartResult from the OpenAPI spec */
16
41
  export interface SessionData {
17
42
  sessionId: string;
18
- browserbaseSessionId?: string;
19
- cdpUrl?: string;
43
+ cdpUrl?: string | null;
20
44
  available: boolean;
21
45
  }
22
46
 
23
47
  export interface StartSessionOptions {
24
- browserbaseSessionId?: string;
48
+ browserbaseSessionID?: string;
49
+ browserbaseSessionCreateParams?: BrowserbaseSessionCreateParams;
50
+ model?: unknown;
25
51
  domSettleTimeoutMs?: number;
26
52
  selfHeal?: boolean;
27
53
  systemPrompt?: string;
54
+ verbose?: 0 | 1 | 2;
55
+ experimental?: boolean;
28
56
  }
29
57
 
30
58
  export interface ApiResponse<T> {
@@ -55,85 +83,135 @@ async function handleResponse<T>(response: Response): Promise<T> {
55
83
  return json.data;
56
84
  }
57
85
 
86
+ /** POST /v1/sessions/start — matches SessionStartRequest schema */
58
87
  export async function startSession(
59
88
  config: ApiConfig,
60
89
  options?: StartSessionOptions,
61
90
  ): Promise<SessionData> {
62
- const response = await fetch(`${API_BASE}/sessions/start`, {
63
- method: "POST",
64
- headers: getHeaders(config),
65
- body: JSON.stringify({
66
- modelName: config.modelName || "openai/gpt-4o",
67
- browserbaseSessionId: options?.browserbaseSessionId,
68
- domSettleTimeoutMs: options?.domSettleTimeoutMs,
69
- selfHeal: options?.selfHeal,
70
- systemPrompt: options?.systemPrompt,
71
- }),
72
- });
91
+ const region = options?.browserbaseSessionCreateParams?.region;
92
+ const response = await fetch(
93
+ `${getApiBase(region)}/sessions/start`,
94
+ {
95
+ method: "POST",
96
+ headers: getHeaders(config),
97
+ body: JSON.stringify({
98
+ modelName: config.modelName || "openai/gpt-4o",
99
+ model: options?.model,
100
+ browserbaseSessionID: options?.browserbaseSessionID,
101
+ browserbaseSessionCreateParams:
102
+ options?.browserbaseSessionCreateParams,
103
+ domSettleTimeoutMs: options?.domSettleTimeoutMs,
104
+ selfHeal: options?.selfHeal,
105
+ systemPrompt: options?.systemPrompt,
106
+ verbose: options?.verbose,
107
+ experimental: options?.experimental,
108
+ }),
109
+ },
110
+ );
73
111
  return handleResponse<SessionData>(response);
74
112
  }
75
113
 
114
+ /** POST /v1/sessions/{id}/end */
76
115
  export async function endSession(
77
116
  sessionId: string,
78
117
  config: ApiConfig,
118
+ region?: BrowserbaseRegion,
79
119
  ): Promise<void> {
80
- try {
81
- await fetch(`${API_BASE}/sessions/${sessionId}/end`, {
82
- method: "POST",
83
- headers: getHeaders(config),
84
- });
85
- } catch {
86
- // Ignore errors when ending session - best effort cleanup
120
+ const response = await fetch(`${getApiBase(region)}/sessions/${sessionId}/end`, {
121
+ method: "POST",
122
+ headers: getHeaders(config),
123
+ });
124
+ if (!response.ok) {
125
+ const errorText = await response.text();
126
+ throw new Error(`Stagehand API error (${response.status}): ${errorText}`);
127
+ }
128
+ const text = await response.text();
129
+ if (text) {
130
+ const json = JSON.parse(text) as { success?: boolean };
131
+ if (json.success === false) {
132
+ throw new Error("Stagehand API returned success: false");
133
+ }
87
134
  }
88
135
  }
89
136
 
137
+ /** Matches NavigateOptions from the OpenAPI spec */
90
138
  export interface NavigateOptions {
91
139
  waitUntil?: "load" | "domcontentloaded" | "networkidle";
92
140
  timeout?: number;
141
+ referer?: string;
93
142
  }
94
143
 
144
+ /** POST /v1/sessions/{id}/navigate — matches NavigateRequest schema */
95
145
  export async function navigate(
96
146
  sessionId: string,
97
147
  url: string,
98
148
  config: ApiConfig,
99
149
  options?: NavigateOptions,
150
+ region?: BrowserbaseRegion,
100
151
  ): Promise<void> {
101
- const response = await fetch(`${API_BASE}/sessions/${sessionId}/navigate`, {
102
- method: "POST",
103
- headers: getHeaders(config),
104
- body: JSON.stringify({
105
- url,
106
- options: {
107
- waitUntil: options?.waitUntil || "networkidle",
108
- timeout: options?.timeout,
109
- },
110
- }),
111
- });
152
+ const response = await fetch(
153
+ `${getApiBase(region)}/sessions/${sessionId}/navigate`,
154
+ {
155
+ method: "POST",
156
+ headers: getHeaders(config),
157
+ body: JSON.stringify({
158
+ url,
159
+ options: {
160
+ waitUntil: options?.waitUntil || "networkidle",
161
+ timeout: options?.timeout,
162
+ referer: options?.referer,
163
+ },
164
+ }),
165
+ },
166
+ );
112
167
  await handleResponse(response);
113
168
  }
114
169
 
115
- export interface ExtractResult<T = any> {
170
+ export interface ExtractResult<T = unknown> {
116
171
  result: T;
117
- actionId: string;
172
+ actionId?: string;
173
+ }
174
+
175
+ /** Matches ExtractOptions from the OpenAPI spec */
176
+ export interface ExtractOperationOptions {
177
+ model?: unknown;
178
+ timeout?: number;
179
+ selector?: string;
118
180
  }
119
181
 
182
+ /** POST /v1/sessions/{id}/extract — matches ExtractRequest schema */
120
183
  export async function extract(
121
184
  sessionId: string,
122
185
  instruction: string,
123
- schema: any,
186
+ schema: unknown,
124
187
  config: ApiConfig,
188
+ operationOptions?: ExtractOperationOptions,
189
+ region?: BrowserbaseRegion,
125
190
  ): Promise<ExtractResult> {
126
- const response = await fetch(`${API_BASE}/sessions/${sessionId}/extract`, {
127
- method: "POST",
128
- headers: getHeaders(config),
129
- body: JSON.stringify({
130
- instruction,
131
- schema,
132
- }),
133
- });
191
+ const body: Record<string, unknown> = { instruction, schema };
192
+ if (
193
+ operationOptions?.model != null ||
194
+ operationOptions?.timeout != null ||
195
+ operationOptions?.selector != null
196
+ ) {
197
+ body.options = {
198
+ model: operationOptions?.model,
199
+ timeout: operationOptions?.timeout,
200
+ selector: operationOptions?.selector,
201
+ };
202
+ }
203
+ const response = await fetch(
204
+ `${getApiBase(region)}/sessions/${sessionId}/extract`,
205
+ {
206
+ method: "POST",
207
+ headers: getHeaders(config),
208
+ body: JSON.stringify(body),
209
+ },
210
+ );
134
211
  return handleResponse<ExtractResult>(response);
135
212
  }
136
213
 
214
+ /** Matches ActResultData from the OpenAPI spec */
137
215
  export interface ActResult {
138
216
  result: {
139
217
  actionDescription: string;
@@ -141,101 +219,182 @@ export interface ActResult {
141
219
  description: string;
142
220
  selector: string;
143
221
  arguments?: string[];
144
- method: string;
222
+ method?: string;
223
+ backendNodeId?: number;
145
224
  }>;
146
225
  message: string;
147
226
  success: boolean;
148
227
  };
149
- actionId: string;
228
+ actionId?: string;
150
229
  }
151
230
 
231
+ /** Matches ActOptions from the OpenAPI spec */
232
+ export interface ActOperationOptions {
233
+ model?: unknown;
234
+ variables?: Record<string, string>;
235
+ timeout?: number;
236
+ }
237
+
238
+ /** POST /v1/sessions/{id}/act — matches ActRequest schema */
152
239
  export async function act(
153
240
  sessionId: string,
154
241
  action: string,
155
242
  config: ApiConfig,
243
+ operationOptions?: ActOperationOptions,
244
+ region?: BrowserbaseRegion,
156
245
  ): Promise<ActResult> {
157
- const response = await fetch(`${API_BASE}/sessions/${sessionId}/act`, {
158
- method: "POST",
159
- headers: getHeaders(config),
160
- body: JSON.stringify({
161
- input: action,
162
- }),
163
- });
246
+ const body: Record<string, unknown> = { input: action };
247
+ if (
248
+ operationOptions?.model != null ||
249
+ operationOptions?.variables != null ||
250
+ operationOptions?.timeout != null
251
+ ) {
252
+ body.options = {
253
+ model: operationOptions?.model,
254
+ variables: operationOptions?.variables,
255
+ timeout: operationOptions?.timeout,
256
+ };
257
+ }
258
+ const response = await fetch(
259
+ `${getApiBase(region)}/sessions/${sessionId}/act`,
260
+ {
261
+ method: "POST",
262
+ headers: getHeaders(config),
263
+ body: JSON.stringify(body),
264
+ },
265
+ );
164
266
  return handleResponse<ActResult>(response);
165
267
  }
166
268
 
269
+ /** Matches ObserveResult from the OpenAPI spec (Action schema) */
167
270
  export interface ObserveResult {
168
271
  result: Array<{
169
272
  description: string;
170
273
  selector: string;
171
274
  arguments?: string[];
172
275
  backendNodeId?: number;
173
- method: string;
276
+ method?: string;
174
277
  }>;
175
- actionId: string;
278
+ actionId?: string;
279
+ }
280
+
281
+ /** Matches ObserveOptions from the OpenAPI spec */
282
+ export interface ObserveOperationOptions {
283
+ model?: unknown;
284
+ timeout?: number;
285
+ selector?: string;
176
286
  }
177
287
 
288
+ /** POST /v1/sessions/{id}/observe — matches ObserveRequest schema */
178
289
  export async function observe(
179
290
  sessionId: string,
180
291
  instruction: string,
181
292
  config: ApiConfig,
293
+ operationOptions?: ObserveOperationOptions,
294
+ region?: BrowserbaseRegion,
182
295
  ): Promise<ObserveResult> {
183
- const response = await fetch(`${API_BASE}/sessions/${sessionId}/observe`, {
184
- method: "POST",
185
- headers: getHeaders(config),
186
- body: JSON.stringify({
187
- instruction,
188
- }),
189
- });
296
+ const body: Record<string, unknown> = { instruction };
297
+ if (
298
+ operationOptions?.model != null ||
299
+ operationOptions?.timeout != null ||
300
+ operationOptions?.selector != null
301
+ ) {
302
+ body.options = {
303
+ model: operationOptions?.model,
304
+ timeout: operationOptions?.timeout,
305
+ selector: operationOptions?.selector,
306
+ };
307
+ }
308
+ const response = await fetch(
309
+ `${getApiBase(region)}/sessions/${sessionId}/observe`,
310
+ {
311
+ method: "POST",
312
+ headers: getHeaders(config),
313
+ body: JSON.stringify(body),
314
+ },
315
+ );
190
316
  return handleResponse<ObserveResult>(response);
191
317
  }
192
318
 
319
+ /** Matches AgentConfig from the OpenAPI spec */
193
320
  export interface AgentConfig {
194
321
  cua?: boolean;
195
- model?: string;
322
+ mode?: "dom" | "hybrid" | "cua";
323
+ model?: unknown;
196
324
  systemPrompt?: string;
325
+ executionModel?: unknown;
326
+ provider?: "openai" | "anthropic" | "google" | "microsoft";
197
327
  }
198
328
 
329
+ /** Matches AgentExecuteOptions from the OpenAPI spec */
199
330
  export interface AgentExecuteOptions {
200
331
  instruction: string;
201
332
  maxSteps?: number;
333
+ highlightCursor?: boolean;
202
334
  }
203
335
 
336
+ /** Matches AgentAction from the OpenAPI spec */
204
337
  export interface AgentAction {
205
338
  type: string;
206
339
  action?: string;
207
340
  reasoning?: string;
208
341
  timeMs?: number;
342
+ taskCompleted?: boolean;
343
+ pageText?: string;
344
+ pageUrl?: string;
345
+ instruction?: string;
346
+ }
347
+
348
+ /** Matches AgentUsage from the OpenAPI spec */
349
+ export interface AgentUsage {
350
+ input_tokens: number;
351
+ output_tokens: number;
352
+ reasoning_tokens?: number;
353
+ cached_input_tokens?: number;
354
+ inference_time_ms: number;
209
355
  }
210
356
 
357
+ /** Matches AgentExecuteResult from the OpenAPI spec */
211
358
  export interface AgentExecuteResult {
212
359
  result: {
213
360
  actions: AgentAction[];
214
361
  completed: boolean;
215
362
  message: string;
216
363
  success: boolean;
364
+ metadata?: Record<string, unknown>;
365
+ usage?: AgentUsage;
217
366
  };
218
367
  }
219
368
 
369
+ /** POST /v1/sessions/{id}/agentExecute — matches AgentExecuteRequest schema */
220
370
  export async function agentExecute(
221
371
  sessionId: string,
222
372
  agentConfig: AgentConfig,
223
373
  executeOptions: AgentExecuteOptions,
224
374
  config: ApiConfig,
375
+ shouldCache?: boolean,
376
+ region?: BrowserbaseRegion,
225
377
  ): Promise<AgentExecuteResult> {
226
378
  const response = await fetch(
227
- `${API_BASE}/sessions/${sessionId}/agentExecute`,
379
+ `${getApiBase(region)}/sessions/${sessionId}/agentExecute`,
228
380
  {
229
381
  method: "POST",
230
382
  headers: getHeaders(config),
231
383
  body: JSON.stringify({
232
384
  agentConfig: {
233
385
  cua: agentConfig.cua,
386
+ mode: agentConfig.mode,
234
387
  model: agentConfig.model,
235
388
  systemPrompt: agentConfig.systemPrompt,
389
+ executionModel: agentConfig.executionModel,
390
+ provider: agentConfig.provider,
391
+ },
392
+ executeOptions: {
393
+ instruction: executeOptions.instruction,
394
+ maxSteps: executeOptions.maxSteps,
395
+ highlightCursor: executeOptions.highlightCursor,
236
396
  },
237
- instruction: executeOptions.instruction,
238
- maxSteps: executeOptions.maxSteps,
397
+ shouldCache,
239
398
  }),
240
399
  },
241
400
  );