@oya-ai/browser 1.0.97 → 1.0.99

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/dist/index.d.cts CHANGED
@@ -1,14 +1,232 @@
1
- /** The one HTTP path. Everything else in this package is a wrapper over it. */
2
- declare class Http {
3
- readonly baseUrl: string;
4
- readonly apiKey: string;
5
- private readonly timeoutMs;
6
- private readonly fetchImpl;
7
- constructor(baseUrl: string, apiKey: string, timeoutMs: number, fetchImpl: typeof globalThis.fetch);
8
- request<T>(method: string, path: string, body?: unknown, timeoutMs?: number, headers?: Record<string, string>): Promise<T>;
1
+ /**
2
+ * Types for browsers themselves: starting one, what a page looks like to the
3
+ * SDK, the results of CAPTCHA and MFA handling, and the fleet listing.
4
+ */
5
+ /** Where a browser comes from. Configuration, not something a caller must know. */
6
+ type Provider = 'oya-cloud' | 'oya-selfhosted' | 'browseruse' | 'browserbase' | 'steel' | 'anchor' | 'cdp';
7
+ /** How `oya.browser.start()` should start a browser. Every field is optional. */
8
+ interface StartOptions {
9
+ /** Reuse this value when retrying the same logical creation. */
10
+ idempotencyKey?: string;
11
+ /** Wait for capacity for up to five minutes; zero rejects immediately. */
12
+ queueMs?: number;
13
+ /** Spend ceiling for this browser, in US dollars. */
14
+ budgetUsd?: number;
15
+ /** Run under the project's governance: policies, budgets and recordings. */
16
+ governed?: boolean;
17
+ /** Egress and recording rules for a governed browser. */
18
+ policy?: {
19
+ /** Hosts the browser may reach; everything else is blocked. */
20
+ allowedHosts?: string[];
21
+ /** Hosts where a person, not the agent, must act. */
22
+ humanHosts?: string[];
23
+ /** Where the browser must run. */
24
+ region?: string;
25
+ /** Blur typed values out of the session recording. */
26
+ redactRecording?: boolean;
27
+ };
28
+ /** Place in the queue when capacity is short. */
29
+ priority?: 'low' | 'normal' | 'high';
30
+ /** Saved login profile. Defaults to the desktop's default profile. */
31
+ profile?: string;
32
+ /**
33
+ * Which identity to run as. A persona is one device: fingerprint, cookie jar
34
+ * and proxy bound together and stable for its life.
35
+ * 'default' — this key's own persona (the default)
36
+ * 'auto' — the least recently used persona under its concurrency cap
37
+ * <id> — a specific persona
38
+ */
39
+ persona?: 'default' | 'auto' | (string & {});
40
+ /** Solve CAPTCHAs as they appear rather than waiting to be asked. */
41
+ captcha?: 'auto' | 'off';
42
+ /** Override the key's configured provider for this browser only. */
43
+ provider?: Provider;
44
+ /** Required only for the 'cdp' provider. */
45
+ wsUrl?: string;
46
+ /** A label for the fleet listing. */
47
+ name?: string;
48
+ /** How long to wait for a cloud browser to dial in. Default 120s. */
49
+ readyTimeoutMs?: number;
50
+ }
51
+ /** What the server answers when a browser starts. */
52
+ interface StartResult {
53
+ /** The browser's id, for every later call. */
54
+ id: string;
55
+ /** Which provider runs it. */
56
+ provider: string;
57
+ /** Which persona it runs as. */
58
+ persona: string;
59
+ /** 'starting' while a cloud browser has yet to dial in. */
60
+ status: 'ready' | 'starting';
61
+ /** Point Playwright, Puppeteer or browser-use at this. */
62
+ cdpUrl?: string;
63
+ /** Anything the server wants the caller to know about this start. */
64
+ note?: string;
65
+ }
66
+ /** One element on the page, numbered by `analyze()`. */
67
+ interface Element {
68
+ /** The number to pass to `click()` and `type()`. Valid until the page changes. */
69
+ id: number;
70
+ /** What kind of control it is: link, button, input and so on. */
71
+ type: string;
72
+ /** Visible label, capped at 80 characters by the analyzer. */
73
+ text?: string;
74
+ /** Where a link goes. */
75
+ href?: string;
76
+ /** The current value of a field. */
77
+ value?: string;
78
+ /** Whether a checkbox or radio is ticked. */
79
+ checked?: boolean;
80
+ /** Whether it refuses input. */
81
+ disabled?: boolean;
82
+ /** Whether it is on screen. */
83
+ visible: boolean;
84
+ /** The HTML tag name. */
85
+ tag?: string;
86
+ /** The element's DOM `id`. Usually the most stable handle a site offers. */
87
+ domId?: string;
88
+ /** Its `aria-label`. */
89
+ ariaLabel?: string;
90
+ /** `data-testid`, when the site ships one. */
91
+ testId?: string;
92
+ /** Its `name` attribute. */
93
+ name?: string;
94
+ /** A field's placeholder text. */
95
+ placeholder?: string;
96
+ /** Action of the enclosing form. */
97
+ formName?: string;
98
+ }
99
+ /** The page as `analyze()` sees it. */
100
+ interface Analysis {
101
+ /** The page as markdown. */
102
+ markdown: string;
103
+ /** Every numbered element, visible or not. */
104
+ elements: Element[];
105
+ /** The window size, in CSS pixels. */
106
+ viewport?: {
107
+ /** Width in CSS pixels. */
108
+ width: number;
109
+ /** Height in CSS pixels. */
110
+ height: number;
111
+ };
112
+ /** How far the page is scrolled. */
113
+ scroll?: {
114
+ /** Horizontal offset in CSS pixels. */
115
+ x: number;
116
+ /** Vertical offset in CSS pixels. */
117
+ y: number;
118
+ };
119
+ /** True when the page was too long to send in full. */
120
+ truncated?: boolean;
121
+ }
122
+ /** What `solveCaptcha()` found and did. */
123
+ interface CaptchaResult {
124
+ /** Whether a CAPTCHA was on the page. */
125
+ present: boolean;
126
+ /** Whether it was cleared. */
127
+ solved: boolean;
128
+ /** 'provider' when the vendor solved it, 'solver' when we did, 'none' otherwise. */
129
+ method: 'provider' | 'solver' | 'none';
130
+ /** Which kind of CAPTCHA it was. */
131
+ type?: string;
132
+ /** Invisible reCAPTCHA v3 scores the visit passively; there is nothing on screen to clear. */
133
+ invisible?: boolean;
134
+ /** The site key the solver was given. */
135
+ sitekey?: string | null;
136
+ /** Why it was not solved. */
137
+ error?: string;
138
+ }
139
+ /** What `completeMfa()` found and did. */
140
+ interface MfaResult {
141
+ /** Whether an MFA prompt was on the page. */
142
+ present: boolean;
143
+ /** Whether it was answered and accepted. */
144
+ completed: boolean;
145
+ /** Which factor answered it, or 'handoff' when a person must. */
146
+ method?: 'totp' | 'email' | 'sms' | 'handoff' | 'none';
147
+ /** Whether the code was typed in. */
148
+ filled?: boolean;
149
+ /** Whether the form was submitted. */
150
+ submitted?: boolean;
151
+ /** Open this to finish by hand when nothing automated can. */
152
+ liveViewUrl?: string | null;
153
+ /** Why it could not be completed. */
154
+ error?: string;
155
+ }
156
+ /** How a browser is doing, judged from its recent commands and heartbeats. */
157
+ type Health = 'ok' | 'stale' | 'errors' | 'dead';
158
+ /** One thing a browser did. */
159
+ interface Activity {
160
+ /** When, as an ISO timestamp. */
161
+ ts: string;
162
+ /** The command's name. */
163
+ action: string;
164
+ /** A one-line account of it. */
165
+ summary: string;
166
+ /** Whether it succeeded. */
167
+ ok: boolean;
168
+ /** How long it took. */
169
+ ms: number;
170
+ /** Why it failed. */
171
+ error?: string;
172
+ }
173
+ /** The outcome of stopping one browser. */
174
+ interface StopResult {
175
+ /** The browser. */
176
+ id: string;
177
+ /** Whether it stopped. */
178
+ ok: boolean;
179
+ /** Which provider ran it. */
180
+ provider?: string | null;
181
+ /** For a cloud browser, whether its sandbox was destroyed (and billing ended). */
182
+ sandboxRemoved?: boolean | null;
183
+ /** Why it did not stop. */
184
+ error?: string;
185
+ }
186
+ /** One browser in the fleet listing. */
187
+ interface BrowserInfo {
188
+ /** The browser's id. */
189
+ id: string;
190
+ /** Its label. */
191
+ name: string;
192
+ /** An Oya Browser on the control socket, or a third-party browser over CDP. */
193
+ clientType: 'oya' | 'cdp';
194
+ /** Which provider runs it. */
195
+ provider: string | null;
196
+ /** The persona id it runs as. */
197
+ persona: string | null;
198
+ /** That persona's name. */
199
+ personaName: string | null;
200
+ /** How it is doing. */
201
+ health: Health;
202
+ /** When it connected. */
203
+ connectedAt: string;
204
+ /** When it was last heard from. */
205
+ lastSeen: string;
206
+ /** The page it is on. */
207
+ currentUrl: string;
208
+ /** Commands run so far. */
209
+ commands: number;
210
+ /** Commands that failed. */
211
+ errors: number;
212
+ /** Commands in flight. */
213
+ pending: number;
214
+ /** When it last ran a command. */
215
+ lastCommandAt: string | null;
216
+ /** The last failure's message. */
217
+ lastError: string | null;
218
+ }
219
+ /** A browser plus what it has been doing, from `browser.status()`. */
220
+ interface BrowserDetail extends BrowserInfo {
221
+ /** Its most recent commands, newest first. */
222
+ activity: Activity[];
9
223
  }
10
224
 
11
- /** Everything the API returns or accepts, in one place. */
225
+ /**
226
+ * Types for a key's settings: the LLM behind `ask()`, the browser provider and
227
+ * the CAPTCHA solver, as `config.set()` accepts them and `config.get()` returns them.
228
+ */
229
+
12
230
  /** Which model drives `ask()` and the chat API. */
13
231
  type LlmProvider = 'openai' | 'anthropic'
14
232
  /** Gemini via AI Studio. */
@@ -24,6 +242,7 @@ type CaptchaSolver = 'capsolver' | '2captcha' | '';
24
242
  * `null` clears a field and falls back to the deployment default.
25
243
  */
26
244
  interface ConfigUpdate {
245
+ /** Which LLM vendor `ask()` and the chat API call. */
27
246
  llm_provider?: LlmProvider | null;
28
247
  /** The credential for whichever `llm_provider` is set — the field name is shared. */
29
248
  openai_api_key?: string | null;
@@ -31,111 +250,119 @@ interface ConfigUpdate {
31
250
  openai_base_url?: string | null;
32
251
  /** Overrides the provider's default model. */
33
252
  chat_model?: string | null;
253
+ /** Where this key's browsers run unless `start()` names a provider. */
34
254
  browser_provider?: Provider | null;
255
+ /** Anchor's API key, for the 'anchor' provider. */
35
256
  anchor_api_key?: string | null;
257
+ /** Browserbase's API key, for the 'browserbase' provider. */
36
258
  browserbase_api_key?: string | null;
259
+ /** The Browserbase project browsers are created in. */
37
260
  browserbase_project_id?: string | null;
261
+ /** Steel's API key, for the 'steel' provider. */
38
262
  steel_api_key?: string | null;
263
+ /** Browser Use Cloud's API key, for the 'browseruse' provider. */
39
264
  browseruse_api_key?: string | null;
265
+ /** The DevTools WebSocket URL of your own Chrome, for the 'cdp' provider. */
40
266
  cdp_ws_url?: string | null;
267
+ /** Which CAPTCHA solving service to call. */
41
268
  captcha_solver?: CaptchaSolver | null;
269
+ /** The solving service's API key. */
42
270
  captcha_api_key?: string | null;
271
+ /** Set once onboarding has been completed, so the console stops offering it. */
43
272
  onboarded?: string | null;
44
273
  }
45
274
  /** What `config.get()` returns. Secrets read back masked, never in full. */
46
275
  interface Config extends Omit<ConfigUpdate, 'llm_provider' | 'browser_provider' | 'captcha_solver'> {
276
+ /** The configured LLM vendor, or empty for the deployment default. */
47
277
  llm_provider?: LlmProvider | '';
278
+ /** The configured browser provider, or empty for the deployment default. */
48
279
  browser_provider?: Provider | '';
280
+ /** The configured CAPTCHA solver; empty when solving is off. */
49
281
  captcha_solver?: CaptchaSolver;
50
282
  /** What this key would actually use right now, deployment defaults included. */
51
283
  effective: {
284
+ /** The LLM endpoint in use. */
52
285
  baseUrl: string;
286
+ /** The model in use. */
53
287
  model: string;
288
+ /** Whether any LLM credential is available. */
54
289
  hasLlmKey: boolean;
55
290
  };
56
291
  /** True when the LLM key in play belongs to the deployment, not this key. */
57
292
  inherited: boolean;
293
+ /** Whether this key has its own LLM credential stored. */
58
294
  has_openai_key: boolean;
295
+ /** Every browser provider, what it needs configured, and whether it is. */
59
296
  providers: Array<{
297
+ /** The provider. */
60
298
  id: Provider;
299
+ /** Its display name. */
61
300
  label: string;
301
+ /** The config fields it requires. */
62
302
  needs: string[];
303
+ /** Whether those fields are set. */
63
304
  configured: boolean;
64
305
  }>;
65
306
  }
66
- /** Where a browser comes from. Configuration, not something a caller must know. */
67
- type Provider = 'oya-cloud' | 'oya-selfhosted' | 'browseruse' | 'browserbase' | 'steel' | 'anchor' | 'cdp';
68
- interface StartOptions {
69
- /** Reuse this value when retrying the same logical creation. */
70
- idempotencyKey?: string;
71
- /** Wait for capacity for up to five minutes; zero rejects immediately. */
72
- queueMs?: number;
73
- budgetUsd?: number;
74
- governed?: boolean;
75
- policy?: {
76
- allowedHosts?: string[];
77
- humanHosts?: string[];
78
- region?: string;
79
- redactRecording?: boolean;
80
- };
81
- priority?: 'low' | 'normal' | 'high';
82
- /** Saved login profile. Defaults to the desktop's default profile. */
83
- profile?: string;
84
- /**
85
- * Which identity to run as. A persona is one device: fingerprint, cookie jar
86
- * and proxy bound together and stable for its life.
87
- * 'default' — this key's own persona (the default)
88
- * 'auto' — the least recently used persona under its concurrency cap
89
- * <id> — a specific persona
90
- */
91
- persona?: 'default' | 'auto' | (string & {});
92
- /** Solve CAPTCHAs as they appear rather than waiting to be asked. */
93
- captcha?: 'auto' | 'off';
94
- /** Override the key's configured provider for this browser only. */
95
- provider?: Provider;
96
- /** Required only for the 'cdp' provider. */
97
- wsUrl?: string;
98
- name?: string;
99
- /** How long to wait for a cloud browser to dial in. Default 120s. */
100
- readyTimeoutMs?: number;
101
- }
102
- interface StartResult {
103
- id: string;
104
- provider: string;
105
- persona: string;
106
- status: 'ready' | 'starting';
107
- /** Point Playwright, Puppeteer or browser-use at this. */
108
- cdpUrl?: string;
109
- note?: string;
307
+
308
+ /**
309
+ * OyaError: the one error type the SDK throws for a failed call, carrying the
310
+ * HTTP status and the server's answer so a caller can branch on either.
311
+ */
312
+ /** A failed API call or browser command. */
313
+ declare class OyaError extends Error {
314
+ /** The HTTP status, or the SDK's own status for errors it raises itself. */
315
+ readonly status: number;
316
+ /** The server's response body, parsed when it was JSON. */
317
+ readonly body: unknown;
318
+ /** Records the message, status and body. */
319
+ constructor(message: string, status: number, body: unknown);
110
320
  }
321
+
322
+ /**
323
+ * Types for agent work: task values and files, playbooks and their replays,
324
+ * and background runs with their attention requests.
325
+ */
326
+
327
+ /** A saved flow, replayable without an LLM. */
111
328
  interface Playbook {
329
+ /** The name it is played by. */
112
330
  name: string;
113
331
  /** Inputs `play()` accepts; any left out reuse the recorded value. */
114
332
  variables: string[];
115
333
  /** What each variable was recorded with. A secret has none — it never left the page. */
116
334
  defaults: Record<string, string>;
335
+ /** How many steps it replays. */
117
336
  steps: number;
118
337
  /** The same flow as a Playwright module: `export default async function run(page, vars)`. */
119
338
  code: string;
120
339
  }
340
+ /** What a `play()` did. */
121
341
  interface PlayResult {
122
342
  /** Steps replayed before finishing or handing over to the agent. */
123
343
  steps: number;
344
+ /** Steps in the playbook. */
124
345
  total: number;
125
346
  /** A step no longer fit the page and the agent finished the task. */
126
347
  fellBack: boolean;
127
348
  /** The agent's fix was saved as `draft`; promote it with `oya.playbooks.promote(name)`. */
128
349
  healed?: boolean;
350
+ /** The draft's name, when one was saved. */
129
351
  draft?: string;
130
352
  /** The agent's summary, when it fell back. */
131
353
  text?: string;
132
354
  }
355
+ /** A playbook in the listing, with its history and any pending fix. */
133
356
  interface PlaybookSummary extends Playbook {
357
+ /** When it was saved. */
134
358
  createdAt: string | null;
359
+ /** When a draft last replaced it. */
135
360
  promotedAt: string | null;
136
361
  /** A healed replay's fix, waiting for `promote()` or `remove('<name>:draft')`. */
137
362
  draft: (Playbook & {
363
+ /** When the replay was healed. */
138
364
  healedAt: string;
365
+ /** The step the replay broke at. */
139
366
  healedFrom: number;
140
367
  }) | null;
141
368
  }
@@ -161,29 +388,46 @@ interface FileValue {
161
388
  * upload tool rather than typing it.
162
389
  */
163
390
  type RunData = Record<string, string | number | FileValue>;
391
+ /** A run is waiting on a person. */
164
392
  interface AttentionRequest {
393
+ /** Identifies this request; a new one means a new problem. */
165
394
  id: string;
166
395
  /** captcha / login / mfa: finish it in the live view. agent: the agent's question. heal_failed: replay and the agent both gave up. */
167
396
  reason: 'captcha' | 'login' | 'mfa' | 'agent' | 'heal_failed';
397
+ /** What is needed, in words. */
168
398
  message: string;
399
+ /** Where to finish it by hand. */
169
400
  liveViewUrl?: string;
401
+ /** When it was raised, in epoch milliseconds. */
170
402
  at: number;
171
403
  }
404
+ /** What a finished run produced: a replay's result, an agent's answer, or both. */
172
405
  type RunResult = Partial<PlayResult> & {
406
+ /** The agent's answer. */
173
407
  text?: string;
174
408
  };
409
+ /** A background run's state. */
175
410
  interface RunInfo {
411
+ /** The run's id. */
176
412
  id: string;
413
+ /** The browser it runs on. */
177
414
  browserId: string;
415
+ /** Where it is. */
178
416
  status: 'running' | 'needs_attention' | 'succeeded' | 'failed';
417
+ /** When it started, in epoch milliseconds. */
179
418
  createdAt: number;
419
+ /** When it ended. */
180
420
  endedAt?: number;
421
+ /** The open request for a person, if any. */
181
422
  attention: AttentionRequest | null;
423
+ /** What it produced, once it succeeded. */
182
424
  result?: RunResult;
425
+ /** Why it failed. */
183
426
  error?: string;
184
427
  /** HTTP-style status of a failure: 429 when a quota stopped the run. */
185
428
  errorStatus?: number;
186
429
  }
430
+ /** The inputs and callbacks for `browser.submit()`. */
187
431
  interface SubmitOptions {
188
432
  /** Task values the agent can read; for a playbook, its variables (secret ones included). */
189
433
  data?: RunData;
@@ -191,7 +435,9 @@ interface SubmitOptions {
191
435
  secrets?: RunData;
192
436
  /** Playbooks only: let the agent finish a broken replay and save its fix as a draft. Default true. */
193
437
  autoHeal?: boolean;
438
+ /** Fires once with the result when the run succeeds. */
194
439
  onSuccess?: (result: RunResult) => unknown;
440
+ /** Fires once when the run fails, or when it can no longer be polled. */
195
441
  onFailure?: (error: OyaError) => unknown;
196
442
  /** Call `respond()` once it is handled: `'done'` after finishing by hand, or your answer to the agent. */
197
443
  onHumanAttention?: (request: AttentionRequest & {
@@ -202,165 +448,151 @@ interface SubmitOptions {
202
448
  /** How often to check on the run. Default 2000. */
203
449
  pollMs?: number;
204
450
  }
205
- interface Element {
206
- id: number;
207
- type: string;
208
- /** Visible label, capped at 80 characters by the analyzer. */
209
- text?: string;
210
- href?: string;
211
- value?: string;
212
- checked?: boolean;
213
- disabled?: boolean;
214
- visible: boolean;
215
- tag?: string;
216
- /** The element's DOM `id`. Usually the most stable handle a site offers. */
217
- domId?: string;
218
- ariaLabel?: string;
219
- /** `data-testid`, when the site ships one. */
220
- testId?: string;
221
- name?: string;
222
- placeholder?: string;
223
- /** Action of the enclosing form. */
224
- formName?: string;
225
- }
226
- interface Analysis {
227
- /** The page as markdown. */
228
- markdown: string;
229
- elements: Element[];
230
- viewport?: {
231
- width: number;
232
- height: number;
233
- };
234
- scroll?: {
235
- x: number;
236
- y: number;
237
- };
238
- truncated?: boolean;
239
- }
240
- interface CaptchaResult {
241
- present: boolean;
242
- solved: boolean;
243
- /** 'provider' when the vendor solved it, 'solver' when we did, 'none' otherwise. */
244
- method: 'provider' | 'solver' | 'none';
245
- type?: string;
246
- /** Invisible reCAPTCHA v3 scores the visit passively; there is nothing on screen to clear. */
247
- invisible?: boolean;
248
- sitekey?: string | null;
249
- error?: string;
250
- }
251
- interface MfaResult {
252
- present: boolean;
253
- completed: boolean;
254
- method?: 'totp' | 'email' | 'sms' | 'handoff' | 'none';
255
- filled?: boolean;
256
- submitted?: boolean;
257
- /** Open this to finish by hand when nothing automated can. */
258
- liveViewUrl?: string | null;
259
- error?: string;
260
- }
451
+
452
+ /**
453
+ * Types for identities: a persona's device and fingerprint, the proxies it
454
+ * exits through, and the second factors and site logins stored for it.
455
+ */
456
+ /** The device a persona presents. Fixed for its life. */
261
457
  interface Fingerprint {
458
+ /** `navigator.platform`. */
262
459
  platform: string;
460
+ /** IANA timezone. */
263
461
  timezone: string;
462
+ /** BCP 47 locale. */
264
463
  locale: string;
464
+ /** Screen size, as `WIDTHxHEIGHT`. */
265
465
  screen: string;
466
+ /** The GPU the WebGL renderer reports. */
266
467
  webgl: string;
468
+ /** Logical CPU cores reported. */
267
469
  hardwareConcurrency: number;
470
+ /** Device memory reported, in GB. */
268
471
  deviceMemory: number;
472
+ /** Seed for the persona's stable canvas noise. */
269
473
  canvasSeed: number;
270
474
  }
271
475
  /** Device choices made at creation. Fixed for the persona's life. */
272
476
  interface PersonaPrefs {
477
+ /** Which operating system to present. */
273
478
  platform?: 'Win32' | 'MacIntel' | 'Linux x86_64';
479
+ /** IANA timezone; must be one the platform can coherently claim. */
274
480
  timezone?: string;
481
+ /** BCP 47 locale; must be one the platform can coherently claim. */
275
482
  locale?: string;
276
483
  }
277
484
  /** A proxy exit. Credentials go in on create and never come back out. */
278
485
  interface ProxyInfo {
486
+ /** The proxy's id. */
279
487
  id: string;
488
+ /** Its display name. */
280
489
  label: string;
490
+ /** Residential IPs look like homes; datacenter ones are cheaper and easier to flag. */
281
491
  kind: 'residential' | 'datacenter';
282
492
  /** Two-letter country, optionally a region: "US", "US-CA". */
283
493
  geo: string | null;
284
494
  /** Provided by the host rather than this key. Cannot be removed. */
285
495
  shared: boolean;
496
+ /** Whether its last check passed. */
286
497
  healthy: boolean;
287
498
  /** Healthy and not cooling down after a failure. */
288
499
  available: boolean;
289
500
  /** Where traffic actually leaves, as of the last check. */
290
501
  exitIp: string | null;
502
+ /** When it was last checked. */
291
503
  lastCheckedAt: string | null;
292
504
  /** Personas on it now, out of `maxPersonas`. */
293
505
  assigned: number;
506
+ /** How many personas may share it. */
294
507
  maxPersonas: number;
508
+ /** How long until a failed proxy is tried again. */
295
509
  cooldownMsRemaining: number;
296
510
  }
511
+ /** A proxy to add. */
297
512
  interface ProxyCreate {
298
513
  /** http(s)://user:pass@host:port from your vendor. Chromium cannot use SOCKS5 with a password. */
299
514
  url: string;
515
+ /** A display name. */
300
516
  label?: string;
517
+ /** Two-letter country, optionally a region, used to match personas' geo hints. */
301
518
  geo?: string;
519
+ /** Residential or datacenter. */
302
520
  kind?: 'residential' | 'datacenter';
303
521
  /** Personas that may share it. Keep 1 for a sticky-session URL so each keeps its own IP. */
304
522
  maxPersonas?: number;
305
523
  }
524
+ /** A persona: one device, its cookie jar and its proxy. */
306
525
  interface PersonaInfo {
526
+ /** The persona's id. */
307
527
  id: string;
528
+ /** Its display name. */
308
529
  name: string;
530
+ /** Whether it is this key's own default persona. */
309
531
  isDefault: boolean;
532
+ /** Browsers running as it now. */
310
533
  activeBrowsers: number;
534
+ /** How many may run as it at once; null for no cap. */
311
535
  maxConcurrent: number | null;
536
+ /** Where its proxy should be, before one is assigned. */
312
537
  proxy: {
538
+ /** Two-letter country, optionally a region. */
313
539
  geo: string | null;
314
540
  } | null;
315
541
  /** The proxy it is actually on, once assigned or pinned. */
316
542
  exit: {
543
+ /** The proxy's id. */
317
544
  id: string;
545
+ /** Its display name. */
318
546
  label: string;
547
+ /** Its country and region. */
319
548
  geo: string | null;
549
+ /** Whether its last check passed. */
320
550
  healthy: boolean;
321
551
  } | null;
552
+ /** The device choices it was created with. */
322
553
  prefs: PersonaPrefs | null;
554
+ /** The device it presents. */
323
555
  fingerprint: Fingerprint;
556
+ /** Its persona-wide second factor. */
324
557
  mfa: {
558
+ /** Whether a factor is stored. */
325
559
  configured: boolean;
560
+ /** Which kind of factor. */
326
561
  type?: string;
562
+ /** The site it is filed against, if any. */
327
563
  domain?: string;
328
564
  };
329
565
  /** Per-site factors and stored logins. Usernames and types only, never secrets. */
330
566
  sites: {
567
+ /** Second factors filed against one site. */
331
568
  mfa: {
569
+ /** The site. */
332
570
  domain: string;
571
+ /** Which kind of factor. */
333
572
  type: string;
334
573
  }[];
574
+ /** Stored site logins. */
335
575
  credentials: {
576
+ /** The site. */
336
577
  domain: string;
578
+ /** The account name. */
337
579
  username: string;
338
580
  }[];
339
581
  };
582
+ /** What its cookie jar holds. */
340
583
  login: {
584
+ /** Cookies in the jar. */
341
585
  cookies: number;
586
+ /** Sites it holds a session for. */
342
587
  sites: string[];
588
+ /** When the jar last changed. */
343
589
  updatedAt: string | null;
344
590
  };
591
+ /** When it was created. */
345
592
  createdAt: string;
593
+ /** When a browser last ran as it. */
346
594
  lastUsedAt: string | null;
347
595
  }
348
- type Health = 'ok' | 'stale' | 'errors' | 'dead';
349
- interface Activity {
350
- ts: string;
351
- action: string;
352
- summary: string;
353
- ok: boolean;
354
- ms: number;
355
- error?: string;
356
- }
357
- interface StopResult {
358
- id: string;
359
- ok: boolean;
360
- provider?: string | null;
361
- sandboxRemoved?: boolean | null;
362
- error?: string;
363
- }
364
596
  /**
365
597
  * A second factor. `domain` files it against one site, because a persona driving
366
598
  * several portals meets several kinds of factor; without it the record is the
@@ -375,122 +607,452 @@ interface StopResult {
375
607
  * `pattern` is only the fallback for when no LLM key is set or the call fails.
376
608
  */
377
609
  type MfaConfig = {
610
+ /** The site this factor answers for; without it, the persona-wide default. */
378
611
  domain?: string;
379
612
  } & ({
613
+ /** An authenticator app's time-based codes. */
380
614
  type: 'totp';
615
+ /** The base32 seed. */
381
616
  secret: string;
382
617
  } | {
618
+ /** Codes delivered to an endpoint you host. */
383
619
  type: 'email' | 'sms';
620
+ /** The endpoint polled for the latest message. */
384
621
  url: string;
622
+ /** Headers sent with each poll. */
385
623
  headers?: Record<string, string>;
624
+ /** Fallback regex for the code, used when no LLM is available. */
386
625
  pattern?: string;
626
+ /** How long to wait for a code. */
387
627
  timeoutMs?: number;
388
628
  } | {
629
+ /** Codes read from a Gmail or Microsoft 365 mailbox. */
389
630
  type: 'gmail' | 'graph';
631
+ /** The mailbox's OAuth refresh token. */
390
632
  refreshToken: string;
633
+ /** The OAuth client the token was issued to. */
391
634
  clientId: string;
635
+ /** That client's secret, when it has one. */
392
636
  clientSecret?: string;
637
+ /** The Microsoft tenant, for `graph`. */
393
638
  tenant?: string;
639
+ /** A mailbox search narrowing which messages are read. */
394
640
  query?: string;
641
+ /** Fallback regex for the code, used when no LLM is available. */
395
642
  pattern?: string;
643
+ /** How long to wait for a code. */
396
644
  timeoutMs?: number;
397
645
  });
398
646
  /** A site login. The password is write-only: no API ever reads it back. */
399
647
  interface SiteCredentials {
648
+ /** The site it signs in to. */
400
649
  domain: string;
650
+ /** The account name. */
401
651
  username: string;
652
+ /** The password; stored sealed and never returned. */
402
653
  password: string;
403
654
  }
404
- interface BrowserInfo {
405
- id: string;
406
- name: string;
407
- clientType: 'oya' | 'cdp';
408
- provider: string | null;
409
- persona: string | null;
410
- personaName: string | null;
411
- health: Health;
412
- connectedAt: string;
413
- lastSeen: string;
414
- currentUrl: string;
415
- commands: number;
416
- errors: number;
417
- pending: number;
418
- lastCommandAt: string | null;
419
- lastError: string | null;
420
- }
421
- interface BrowserDetail extends BrowserInfo {
422
- activity: Activity[];
423
- }
424
- interface OyaOptions {
425
- /** Defaults to OYA_API_KEY. */
426
- apiKey?: string;
427
- /** Defaults to OYA_BASE_URL, then https://browser.getoya.ai. */
428
- baseUrl?: string;
429
- /** Per-request timeout. Navigation gets its own, longer budget. */
430
- timeoutMs?: number;
431
- fetch?: typeof globalThis.fetch;
432
- }
433
- declare class OyaError extends Error {
434
- readonly status: number;
435
- readonly body: unknown;
436
- constructor(message: string, status: number, body: unknown);
437
- }
655
+
656
+ /**
657
+ * Types for the durable control plane: sessions and their lifecycle, human
658
+ * takeover, project settings, lifecycle events and service credentials.
659
+ */
660
+ /** What a member or credential may do: read, operate browsers, or also administer the project. */
438
661
  type ControlRole = 'viewer' | 'operator' | 'administrator';
439
662
  /** What a human holding the control lease may send. Mirrors the server's allowlist. */
440
663
  type HumanInputAction = 'click' | 'type' | 'press_key' | 'scroll' | 'click_coordinates' | 'double_click' | 'drag' | 'mouse_move' | 'scroll_at' | 'type_text' | 'keyboard_type' | 'navigate' | 'back' | 'forward' | 'reload' | 'screenshot' | 'analyze' | 'read_page';
664
+ /** A browser session as the control plane records it, including ones no longer connected. */
441
665
  interface ControlSession {
666
+ /** The session's id, which is also the browser's. */
442
667
  id: string;
668
+ /** The project it belongs to. */
443
669
  project: string;
670
+ /** Which provider runs it. */
444
671
  provider: string;
672
+ /** The persona it runs as. */
445
673
  persona: string | null;
674
+ /** Where it is in its lifecycle. */
446
675
  state: 'queued' | 'provisioning' | 'ready' | 'disconnected' | 'stopping' | 'cleanup_pending' | 'stopped' | 'failed' | 'unknown_outcome';
676
+ /** Whether the control plane provisioned it (and so must clean it up). */
447
677
  managed: boolean;
678
+ /** When it was created, in epoch milliseconds. */
448
679
  createdAt: number;
680
+ /** When its state last changed. */
449
681
  updatedAt: number;
682
+ /** Estimated spend so far, in US dollars. */
450
683
  costUsd: number;
684
+ /** Who is driving it. */
451
685
  control: {
686
+ /** The agent, a person, or nobody while paused. */
452
687
  mode: 'agent' | 'human' | 'paused';
688
+ /** When a human lease runs out. */
453
689
  expiresAt?: number;
454
690
  };
691
+ /** Why cleanup has not finished. */
455
692
  cleanupError?: string;
456
693
  }
694
+ /** A project's limits, retention and rate cards. */
457
695
  interface ProjectSettings {
696
+ /** Days recordings are kept. */
458
697
  recordingDays: number;
698
+ /** Days audit events are kept. */
459
699
  auditDays: number;
700
+ /** Spend ceiling in US dollars; null for none. */
460
701
  budgetUsd: number | null;
702
+ /** Browsers that may run at once; null for no cap. */
461
703
  maxConcurrent: number | null;
704
+ /** Price per unit, by what is metered. */
462
705
  rates: Record<string, number>;
706
+ /** The default policy for governed browsers. */
463
707
  policy: Record<string, unknown>;
464
708
  }
709
+ /** One durable lifecycle event. */
465
710
  interface ControlEvent {
711
+ /** Increasing id, used as the read cursor. */
466
712
  id: number;
713
+ /** The project it belongs to. */
467
714
  project: string;
715
+ /** What happened. */
468
716
  type: string;
717
+ /** The session it concerns, if any. */
469
718
  sessionId: string | null;
719
+ /** When, in epoch milliseconds. */
470
720
  at: number;
721
+ /** Event-specific fields. */
471
722
  detail: Record<string, unknown>;
472
723
  }
724
+ /** A service credential. Its token is shown once, at creation. */
473
725
  interface ControlCredential {
726
+ /** The credential's id, for revoking it. */
474
727
  id: string;
728
+ /** What it is for. */
475
729
  label: string;
730
+ /** What it may do. */
476
731
  role: ControlRole;
732
+ /** When it stops working; null for never. */
477
733
  expiresAt: number | null;
734
+ /** When it was revoked, if it was. */
478
735
  revokedAt: number | null;
479
736
  }
737
+ /** The project at a glance: settings, sessions, recent events. */
480
738
  interface ControlOverview {
481
739
  /** costUsd: estimated lifetime spend, metered from rate cards. */
482
740
  project: {
741
+ /** The project's id. */
483
742
  id: string;
743
+ /** Its name. */
484
744
  name: string;
745
+ /** Its limits and retention. */
485
746
  settings: ProjectSettings;
747
+ /** Estimated lifetime spend, in US dollars. */
486
748
  costUsd?: number;
487
749
  };
750
+ /** Every session the control plane knows about. */
488
751
  sessions: ControlSession[];
752
+ /** Recent lifecycle events. */
489
753
  events: ControlEvent[];
754
+ /** True while the server is draining for a restart. */
490
755
  draining: boolean;
756
+ /** Service credentials, for administrators. */
491
757
  credentials?: ControlCredential[];
492
758
  }
493
759
 
760
+ /**
761
+ * The options the `Oya` client is constructed with.
762
+ */
763
+ /** How to reach the control plane, and as whom. */
764
+ interface OyaOptions {
765
+ /** Defaults to OYA_API_KEY. */
766
+ apiKey?: string;
767
+ /** Defaults to OYA_BASE_URL, then https://browser.getoya.ai. */
768
+ baseUrl?: string;
769
+ /** Per-request timeout. Navigation gets its own, longer budget. */
770
+ timeoutMs?: number;
771
+ /** A fetch to use instead of the global one, for tests, proxies or old runtimes. */
772
+ fetch?: typeof globalThis.fetch;
773
+ }
774
+
775
+ /** The one HTTP path. Everything else in this package is a wrapper over it. */
776
+ declare class Http {
777
+ readonly baseUrl: string;
778
+ readonly apiKey: string;
779
+ private readonly timeoutMs;
780
+ private readonly fetchImpl;
781
+ /** Stores where to call, with which key, how long to wait and which fetch to use. */
782
+ constructor(baseUrl: string, apiKey: string, timeoutMs: number, fetchImpl: typeof globalThis.fetch);
783
+ /** Sends one request and returns the parsed answer, or throws an OyaError when it failed. */
784
+ request<T>(method: string, path: string, body?: unknown, timeoutMs?: number, headers?: Record<string, string>): Promise<T>;
785
+ }
786
+
787
+ /**
788
+ * The shapes the client's namespaces (`oya.browser`, `oya.control`,
789
+ * `oya.personas` …) take and return that are not part of the exported types,
790
+ * named so each field can say what it is.
791
+ */
792
+
793
+ /** What stopping several browsers did. */
794
+ interface StopManyResult {
795
+ /** How many stopped. */
796
+ stopped: number;
797
+ /** Each browser's outcome. */
798
+ results: StopResult[];
799
+ }
800
+ /** A plain acknowledgement. */
801
+ interface Ack {
802
+ /** Whether it was done. */
803
+ ok: boolean;
804
+ }
805
+ /** A single-use live-stream ticket. */
806
+ interface Ticket {
807
+ /** The ticket. */
808
+ ticket: string;
809
+ /** Seconds until it expires. */
810
+ expiresIn: number;
811
+ }
812
+ /** A page of lifecycle events. */
813
+ interface EventPage {
814
+ /** Events after the cursor asked for. */
815
+ events: ControlEvent[];
816
+ /** Pass this as `after` to read on. */
817
+ cursor: number;
818
+ }
819
+ /** A service credential to mint. */
820
+ interface CredentialRequest {
821
+ /** What it may do. */
822
+ role: ControlRole;
823
+ /** What it is for. */
824
+ label?: string;
825
+ /** When it stops working, in epoch milliseconds. */
826
+ expiresAt?: number;
827
+ }
828
+ /** A new service credential, with its token: shown this once. */
829
+ interface NewCredential extends ControlCredential {
830
+ /** The secret to authenticate with. */
831
+ token: string;
832
+ }
833
+ /** One project member. */
834
+ interface Member {
835
+ /** The member's user id. */
836
+ userId: string;
837
+ /** What they may do. */
838
+ role: ControlRole;
839
+ }
840
+ /** The project's owner and members. */
841
+ interface MemberList {
842
+ /** The owner's user id. */
843
+ owner: string | null;
844
+ /** Everyone else. */
845
+ members: Member[];
846
+ }
847
+ /** An invitation code. */
848
+ interface Invite {
849
+ /** The code to hand to the invitee. */
850
+ code: string;
851
+ /** Seconds until it expires. */
852
+ expiresIn: number;
853
+ }
854
+ /** A registered webhook. */
855
+ interface Webhook {
856
+ /** Its id, for removing it. */
857
+ id: string;
858
+ /** The key its deliveries are signed with. */
859
+ secret: string;
860
+ }
861
+ /** One proxy's check. */
862
+ interface ProxyCheck {
863
+ /** The proxy. */
864
+ id: string;
865
+ /** Whether it answered. */
866
+ ok: boolean;
867
+ /** Where its traffic actually leaves. */
868
+ exitIp?: string | null;
869
+ /** Why it failed. */
870
+ error?: string;
871
+ }
872
+ /** Where a persona's proxy should be. */
873
+ interface GeoHint {
874
+ /** Two-letter country, optionally a region. */
875
+ geo?: string;
876
+ }
877
+ /** A persona to create. */
878
+ interface PersonaCreate {
879
+ /** Its display name. */
880
+ name?: string;
881
+ /** Device choices, fixed for its life. */
882
+ prefs?: PersonaPrefs;
883
+ /** Where its proxy should be. */
884
+ proxy?: GeoHint;
885
+ /** How many browsers may run as it at once; null for no cap. */
886
+ maxConcurrent?: number | null;
887
+ }
888
+ /** What may change on a persona. Never the device. */
889
+ interface PersonaUpdate {
890
+ /** Its display name. */
891
+ name?: string;
892
+ /** How many browsers may run as it at once; null for no cap. */
893
+ maxConcurrent?: number | null;
894
+ /** Where its proxy should be; null clears the hint. */
895
+ proxy?: GeoHint | null;
896
+ }
897
+ /** Options for a clone. */
898
+ interface CloneOptions {
899
+ /** The new persona's name. */
900
+ name?: string;
901
+ }
902
+ /** What a persona may claim, per platform. */
903
+ interface PersonaOptions {
904
+ /** The platforms on offer. */
905
+ platforms: string[];
906
+ /** Timezones each platform may coherently claim. */
907
+ timezones: Record<string, string[]>;
908
+ /** Locales each platform may coherently claim. */
909
+ locales: Record<string, string[]>;
910
+ }
911
+ /** The proxy a persona is pinned to. */
912
+ interface PinnedProxy {
913
+ /** The proxy's id. */
914
+ id: string;
915
+ /** Its display name. */
916
+ label: string;
917
+ }
918
+ /** The outcome of pinning a proxy. */
919
+ interface ProxyPin {
920
+ /** Whether it was saved. */
921
+ ok: boolean;
922
+ /** The proxy now pinned, or null when assignment is left to connect time. */
923
+ proxy: PinnedProxy | null;
924
+ }
925
+ /** A stored second factor, as confirmed. */
926
+ interface MfaSaved {
927
+ /** Whether it is stored. */
928
+ configured: boolean;
929
+ /** Which kind of factor. */
930
+ type: string;
931
+ }
932
+ /** A site login the persona can use. Never the password. */
933
+ interface SiteLogin {
934
+ /** The site. */
935
+ domain: string;
936
+ /** The account name. */
937
+ username: string;
938
+ }
939
+ /** A stored site login, as confirmed. */
940
+ interface CredentialsSaved extends SiteLogin {
941
+ /** Whether it is stored. */
942
+ configured: boolean;
943
+ }
944
+ /** The sites a persona can sign in to. */
945
+ interface SiteLogins {
946
+ /** Usernames only. */
947
+ credentials: SiteLogin[];
948
+ }
949
+
950
+ /**
951
+ * The shapes Browser's methods take and return that are not part of the
952
+ * exported types: tab listings, dialog answers, share links and the like.
953
+ * Named here so each field can say what it is.
954
+ */
955
+
956
+ /** What `type()` reports back. */
957
+ interface TypeResult {
958
+ /** An autocomplete list opened under the field; pick from it before moving on. */
959
+ suggestions_visible?: boolean;
960
+ }
961
+ /** The dialog `handleDialog()` answered. */
962
+ interface DialogResult {
963
+ /** alert, confirm, prompt or beforeunload. */
964
+ type: string;
965
+ /** The dialog's text. */
966
+ message: string;
967
+ /** Whether it was accepted. */
968
+ accepted: boolean;
969
+ }
970
+ /** A point on the page, in CSS pixels. */
971
+ interface Point {
972
+ /** From the left edge. */
973
+ x: number;
974
+ /** From the top edge. */
975
+ y: number;
976
+ }
977
+ /** One open tab. */
978
+ interface Tab {
979
+ /** The tab's id, for `switchTab()` and `closeTab()`. */
980
+ id: string;
981
+ /** The page it shows. */
982
+ url: string;
983
+ /** The page's title. */
984
+ title: string;
985
+ /** Whether it is the tab commands act on. */
986
+ active: boolean;
987
+ }
988
+ /** Task values for `ask()`. */
989
+ interface AskValues {
990
+ /** Values the agent can read. */
991
+ data?: RunData;
992
+ /** Values the agent never sees. */
993
+ secrets?: RunData;
994
+ }
995
+ /** How `play()` handles a step that no longer fits. */
996
+ interface PlayOptions {
997
+ /** Let the agent finish the task and save its fix as a draft. Default true. */
998
+ autoHeal?: boolean;
999
+ }
1000
+ /** What `submit()` runs: a prompt for the agent, or a saved playbook. */
1001
+ type Task = {
1002
+ /** A natural-language task for the agent. */
1003
+ prompt: string;
1004
+ } | {
1005
+ /** The name of a saved playbook. */
1006
+ playbook: string;
1007
+ };
1008
+ /** How a `shareUrl()` link works. */
1009
+ interface ShareOptions {
1010
+ /** Let whoever opens it act in the browser. Default view-only. */
1011
+ control?: boolean;
1012
+ /** How long it lasts. Default one hour. */
1013
+ expiresInSeconds?: number;
1014
+ }
1015
+ /** A link from `shareUrl()`. */
1016
+ interface ShareLink {
1017
+ /** The link to hand out. */
1018
+ url: string;
1019
+ /** Its credential's id, for `revokeShare()`. */
1020
+ id: string;
1021
+ /** When it stops working, in epoch milliseconds; null for never. */
1022
+ expiresAt: number | null;
1023
+ }
1024
+
1025
+ /** The callbacks a run can fire. */
1026
+ type RunCallbacks = Pick<SubmitOptions, 'onSuccess' | 'onFailure' | 'onHumanAttention' | 'onHealed'>;
1027
+
1028
+ /**
1029
+ * Run: a task submitted with `browser.submit()`, running in the background.
1030
+ * The polling itself lives in run-watch.ts.
1031
+ */
1032
+
1033
+ /** A submitted task. Callbacks fire as it changes; `done` settles when it ends. */
1034
+ declare class Run {
1035
+ private readonly http;
1036
+ readonly id: string;
1037
+ /** Settles with the result when the run succeeds, or rejects when it fails. */
1038
+ readonly done: Promise<RunResult>;
1039
+ /** Starts watching the run straight away. */
1040
+ constructor(http: Http, id: string, callbacks: RunCallbacks, pollMs?: number);
1041
+ /** The run's current state. */
1042
+ status(): Promise<RunInfo>;
1043
+ /** Answer the open attention request: `'done'` after handling it by hand, or your reply to the agent. */
1044
+ respond(response?: string): Promise<void>;
1045
+ /** Polls until the run ends, firing callbacks on the way. */
1046
+ private watch;
1047
+ }
1048
+
1049
+ /**
1050
+ * Browser: one running browser, driven through the API. Page actions go
1051
+ * through the command endpoint; CAPTCHA, MFA, agent runs, playbooks and live
1052
+ * view links have their own endpoints. The small rules the methods share
1053
+ * (element ids, aimed scrolls, agent errors) are the functions below the class.
1054
+ */
1055
+
494
1056
  /**
495
1057
  * One running browser.
496
1058
  *
@@ -501,54 +1063,56 @@ interface ControlOverview {
501
1063
  declare class Browser {
502
1064
  private readonly http;
503
1065
  private readonly autoCaptcha;
1066
+ /** The browser's id. */
504
1067
  readonly id: string;
1068
+ /** Which provider runs it. */
505
1069
  readonly provider: string;
1070
+ /** Which persona it runs as. */
506
1071
  readonly persona: string;
507
1072
  /** Point Playwright, Puppeteer or browser-use here. */
508
1073
  readonly cdpUrl?: string;
1074
+ /** Wraps a started browser; `autoCaptcha` solves CAPTCHAs after every `goto()`. */
509
1075
  constructor(http: Http, info: StartResult, autoCaptcha: boolean);
1076
+ /** Runs one browser command; a command that ran and failed throws. */
510
1077
  private command;
1078
+ /** Navigates, then clears any CAPTCHA when `captcha: 'auto'` was asked for. */
511
1079
  goto(url: string): Promise<void>;
512
1080
  /** The page as markdown plus numbered elements to act on. */
513
1081
  analyze(): Promise<Analysis>;
514
1082
  /** Only the visible elements, which is what an agent almost always wants. */
515
1083
  elements(): Promise<Element[]>;
1084
+ /** Clicks an element by its id from `analyze()`. */
516
1085
  click(elementId: number | string): Promise<void>;
517
- type(elementId: number | string, text: string): Promise<{
518
- suggestions_visible?: boolean;
519
- }>;
1086
+ /** Types into an element by its id from `analyze()`. */
1087
+ type(elementId: number | string, text: string): Promise<TypeResult>;
1088
+ /** A valid element id, or an OyaError saying where ids come from. */
520
1089
  private elementId;
1090
+ /** Presses one key, such as Enter or Escape. */
521
1091
  pressKey(key: string): Promise<void>;
522
1092
  /**
523
1093
  * Answer a native dialog holding the page. alert() and beforeunload are
524
1094
  * answered for you; a confirm() or prompt() waits for this, and every other
525
1095
  * command fails fast with the dialog's text until it is answered.
526
1096
  */
527
- handleDialog(accept: boolean, promptText?: string): Promise<{
528
- type: string;
529
- message: string;
530
- accepted: boolean;
531
- }>;
1097
+ handleDialog(accept: boolean, promptText?: string): Promise<DialogResult>;
532
1098
  /** `at` aims the wheel at an inner scroller (a results panel, a chat pane) instead of the page. */
533
- scroll(direction: 'up' | 'down' | 'top' | 'bottom', amount?: number, at?: {
534
- x: number;
535
- y: number;
536
- }): Promise<void>;
1099
+ scroll(direction: 'up' | 'down' | 'top' | 'bottom', amount?: number, at?: Point): Promise<void>;
1100
+ /** Waits until `selector` matches, up to `timeout` milliseconds. */
537
1101
  waitFor(selector: string, timeout?: number): Promise<void>;
538
1102
  /** A `data:image/…;base64,` URL. PNG or JPEG depending on the driver. */
539
1103
  screenshot(): Promise<string>;
1104
+ /** The active tab's URL, or empty when there is none. */
540
1105
  url(): Promise<string>;
541
- tabs(): Promise<Array<{
542
- id: string;
543
- url: string;
544
- title: string;
545
- active: boolean;
546
- }>>;
1106
+ /** Every open tab. */
1107
+ tabs(): Promise<Array<Tab>>;
1108
+ /** Opens a tab, optionally at `url`, and returns its id. */
547
1109
  openTab(url?: string): Promise<string>;
1110
+ /** Makes a tab the one commands act on. */
548
1111
  switchTab(tabId: string): Promise<void>;
1112
+ /** Closes a tab. */
549
1113
  closeTab(tabId: string): Promise<void>;
550
1114
  /**
551
- x * Detect and clear a CAPTCHA. Providers that solve natively are left to do
1115
+ * Detect and clear a CAPTCHA. Providers that solve natively are left to do
552
1116
  * it; everything else goes to the configured solver.
553
1117
  */
554
1118
  solveCaptcha(): Promise<CaptchaResult>;
@@ -563,10 +1127,7 @@ declare class Browser {
563
1127
  * the matching option; `secrets` it never sees. It types both through placeholders,
564
1128
  * with filters like `{{name|first}}`, so a playbook saved from the run stores no values.
565
1129
  */
566
- ask(prompt: string, { data, secrets }?: {
567
- data?: RunData;
568
- secrets?: RunData;
569
- }): Promise<string>;
1130
+ ask(prompt: string, { data, secrets }?: AskValues): Promise<string>;
570
1131
  /**
571
1132
  * Save the last `ask()` on this browser as a named playbook. Every value that was
572
1133
  * typed, picked or clicked becomes a variable, with what the run used kept in
@@ -581,31 +1142,17 @@ declare class Browser {
581
1142
  * saved as a draft (`healed`, `draft`); off, the step's error is thrown.
582
1143
  * Play `'<name>:draft'` to try a draft before promoting it.
583
1144
  */
584
- play(name: string, data?: RunData, { autoHeal }?: {
585
- autoHeal?: boolean;
586
- }): Promise<PlayResult>;
1145
+ play(name: string, data?: RunData, { autoHeal }?: PlayOptions): Promise<PlayResult>;
587
1146
  /**
588
1147
  * Start a prompt or playbook in the background and hear back through callbacks.
589
1148
  * `onHumanAttention` fires for an unsolved CAPTCHA, an unfinished MFA, the agent
590
1149
  * asking for help, or a replay the agent could not heal; the run waits (up to 30
591
1150
  * minutes) until you call `respond()`.
592
1151
  */
593
- submit(task: {
594
- prompt: string;
595
- } | {
596
- playbook: string;
597
- }, options?: SubmitOptions): Promise<Run>;
1152
+ submit(task: Task, options?: SubmitOptions): Promise<Run>;
598
1153
  /**
599
- * Watch it work: the console, opened on this browser.
600
- *
601
- * This used to return the raw frame stream with the project's API key in the
602
- * query string — a permanent credential in browser history, Referer headers
603
- * and every proxy log on the way, and a URL that renders as a wall of
604
- * text/event-stream if a person actually opens it. It is the console deep
605
- * link now, the same one the server hands back from `completeMfa()`, and it
606
- * carries no credential at all.
607
- *
608
- * For the frames themselves, use `liveStreamUrl()`.
1154
+ * Watch it work: the console, opened on this browser. It carries no
1155
+ * credential. For the frames themselves, use `liveStreamUrl()`.
609
1156
  */
610
1157
  liveViewUrl(): string;
611
1158
  /**
@@ -625,14 +1172,7 @@ declare class Browser {
625
1172
  * rest of your project. Anyone holding the link has that access until it
626
1173
  * expires or you revoke it, so treat it like a password.
627
1174
  */
628
- shareUrl({ control, expiresInSeconds }?: {
629
- control?: boolean;
630
- expiresInSeconds?: number;
631
- }): Promise<{
632
- url: string;
633
- id: string;
634
- expiresAt: number | null;
635
- }>;
1175
+ shareUrl({ control, expiresInSeconds }?: ShareOptions): Promise<ShareLink>;
636
1176
  /** Revoke a link from `shareUrl()` before it expires, by the id it returned. */
637
1177
  revokeShare(id: string): Promise<void>;
638
1178
  /** Counters, health and the last 50 things this browser did. */
@@ -647,18 +1187,6 @@ declare class Browser {
647
1187
  /** @deprecated use stop() — close() only dropped the socket, and a cloud browser redialled. */
648
1188
  close(): Promise<void>;
649
1189
  }
650
- type RunCallbacks = Pick<SubmitOptions, 'onSuccess' | 'onFailure' | 'onHumanAttention' | 'onHealed'>;
651
- /** A submitted task. Callbacks fire as it changes; `done` settles when it ends. */
652
- declare class Run {
653
- private readonly http;
654
- readonly id: string;
655
- readonly done: Promise<RunResult>;
656
- constructor(http: Http, id: string, callbacks: RunCallbacks, pollMs?: number);
657
- status(): Promise<RunInfo>;
658
- /** Answer the open attention request: `'done'` after handling it by hand, or your reply to the agent. */
659
- respond(response?: string): Promise<void>;
660
- private watch;
661
- }
662
1190
 
663
1191
  /**
664
1192
  * Files as task values.
@@ -678,100 +1206,52 @@ declare const MAX_FILE_BYTES: number;
678
1206
  * from the extension.
679
1207
  */
680
1208
  declare function file(source: string | Uint8Array | Blob, options?: {
1209
+ /** The filename the site sees. */
681
1210
  name?: string;
1211
+ /** The MIME type, instead of the one guessed from the extension. */
682
1212
  type?: string;
683
1213
  }): Promise<FileValue>;
684
1214
 
685
- /**
686
- * @oya-ai/browser — thousands of browsers, one API.
687
- *
688
- * import { Oya } from '@oya-ai/browser';
689
- *
690
- * const oya = new Oya(); // OYA_API_KEY
691
- * const browser = await oya.browser.start({ persona: 'auto', captcha: 'auto' });
692
- * await browser.goto('https://example.com');
693
- *
694
- * Which provider actually runs the browser — Oya Cloud, your own machines,
695
- * Browser Use, Browserbase, Steel, Anchor, or a CDP URL you hand us — is
696
- * configuration on your API key, not something this code has to know.
697
- */
698
-
1215
+ /** The client: one API key, every browser behind it. */
699
1216
  declare class Oya {
1217
+ /** The one HTTP path every call goes through. */
700
1218
  private readonly http;
1219
+ /** Reads the key and URL from `options`, then OYA_API_KEY and OYA_BASE_URL. Throws without a key. */
701
1220
  constructor(options?: OyaOptions);
1221
+ /** Start, reattach to, list and stop browsers. */
702
1222
  readonly browser: {
703
- /** Start a browser and wait until it can take commands. */
704
1223
  start: (options?: StartOptions) => Promise<Browser>;
705
- /** Reattach to a browser that is already running. */
706
1224
  get: (id: string) => Promise<Browser>;
707
1225
  list: () => Promise<BrowserInfo[]>;
708
- /** Stop some (`ids`) or every browser on this key. Each reports separately. */
709
- stop: (ids: string[] | "all") => Promise<{
710
- stopped: number;
711
- results: StopResult[];
712
- }>;
1226
+ stop: (ids: string[] | "all") => Promise<StopManyResult>;
713
1227
  stopAll: () => Promise<number>;
714
1228
  };
715
1229
  /** Durable operational controls, including disconnected and cleanup-pending sessions. */
716
1230
  readonly control: {
717
- overview: () => Promise<ControlOverview>;
718
- sessions: () => Promise<ControlSession[]>;
719
- session: (id: string) => Promise<ControlSession>;
720
- settings: (changes: Partial<ProjectSettings>) => Promise<ControlOverview["project"]>;
1231
+ createWebhook: (url: string, types?: string[]) => Promise<Webhook>;
1232
+ removeWebhook: (id: string) => Promise<Ack>;
1233
+ replayDelivery: (id: string) => Promise<Ack>;
1234
+ members: () => Promise<MemberList>;
1235
+ inviteMember: (role?: ControlRole) => Promise<Invite>;
1236
+ removeMember: (userId: string) => Promise<Ack>;
1237
+ createCredential: (options: CredentialRequest) => Promise<NewCredential>;
1238
+ revokeCredential: (id: string) => Promise<Ack>;
1239
+ recover: (id: string, replace?: boolean) => Promise<unknown>;
1240
+ ticket: (id: string) => Promise<Ticket>;
1241
+ events: (after?: number) => Promise<EventPage>;
721
1242
  cancel: (id: string) => Promise<ControlSession>;
722
1243
  stop: (id: string, force?: boolean) => Promise<StopResult>;
723
1244
  takeover: (id: string, action: "acquire" | "release" | "resume") => Promise<ControlSession["control"]>;
724
1245
  input: (id: string, action: HumanInputAction, params: Record<string, unknown>) => Promise<unknown>;
725
- recover: (id: string, replace?: boolean) => Promise<unknown>;
726
- ticket: (id: string) => Promise<{
727
- ticket: string;
728
- expiresIn: number;
729
- }>;
730
- events: (after?: number) => Promise<{
731
- events: ControlEvent[];
732
- cursor: number;
733
- }>;
734
- createCredential: (options: {
735
- role: ControlRole;
736
- label?: string;
737
- expiresAt?: number;
738
- }) => Promise<ControlCredential & {
739
- token: string;
740
- }>;
741
- revokeCredential: (id: string) => Promise<{
742
- ok: boolean;
743
- }>;
744
- members: () => Promise<{
745
- owner: string | null;
746
- members: {
747
- userId: string;
748
- role: ControlRole;
749
- }[];
750
- }>;
751
- inviteMember: (role?: ControlRole) => Promise<{
752
- code: string;
753
- expiresIn: number;
754
- }>;
755
- removeMember: (userId: string) => Promise<{
756
- ok: boolean;
757
- }>;
758
- createWebhook: (url: string, types?: string[]) => Promise<{
759
- id: string;
760
- secret: string;
761
- }>;
762
- removeWebhook: (id: string) => Promise<{
763
- ok: boolean;
764
- }>;
765
- replayDelivery: (id: string) => Promise<{
766
- ok: boolean;
767
- }>;
1246
+ overview: () => Promise<ControlOverview>;
1247
+ sessions: () => Promise<ControlSession[]>;
1248
+ session: (id: string) => Promise<ControlSession>;
1249
+ settings: (changes: Partial<ProjectSettings>) => Promise<ControlOverview["project"]>;
768
1250
  };
769
1251
  /** Playbooks saved with `browser.toPlaybook()`. */
770
1252
  readonly playbooks: {
771
1253
  list: () => Promise<PlaybookSummary[]>;
772
- /** Delete a playbook and its draft, or only the draft with `'<name>:draft'`. */
773
1254
  remove: (name: string) => Promise<void>;
774
- /** Replace a playbook with the draft a healed replay saved. Try it first with `browser.play('<name>:draft')`. */
775
1255
  promote: (name: string) => Promise<Playbook>;
776
1256
  };
777
1257
  /**
@@ -782,84 +1262,24 @@ declare class Oya {
782
1262
  list: () => Promise<ProxyInfo[]>;
783
1263
  create: (proxy: ProxyCreate) => Promise<ProxyInfo>;
784
1264
  remove: (id: string) => Promise<void>;
785
- /** Dial each proxy and learn its real exit IP. Failing ones cool down and are skipped. */
786
- check: () => Promise<Array<{
787
- id: string;
788
- ok: boolean;
789
- exitIp?: string | null;
790
- error?: string;
791
- }>>;
1265
+ check: () => Promise<Array<ProxyCheck>>;
792
1266
  };
1267
+ /** Identities: fingerprint, cookie jar and proxy bound together, plus their stored factors and logins. */
793
1268
  readonly personas: {
794
- list: () => Promise<PersonaInfo[]>;
795
- get: (id: string) => Promise<PersonaInfo>;
796
- /**
797
- * Create an identity. The device — platform, timezone, locale — is chosen
798
- * here and fixed for its life; `preview()` shows what a choice produces.
799
- */
800
- create: (options?: {
801
- name?: string;
802
- prefs?: PersonaPrefs;
803
- proxy?: {
804
- geo?: string;
805
- };
806
- maxConcurrent?: number | null;
807
- }) => Promise<PersonaInfo>;
808
- /** Name, concurrency cap and proxy hint. Never the device — clone for that. */
809
- update: (id: string, changes: {
810
- name?: string;
811
- maxConcurrent?: number | null;
812
- proxy?: {
813
- geo?: string;
814
- } | null;
815
- }) => Promise<PersonaInfo>;
816
- /** A new persona of the same kind of device: same choices, fresh identity, empty jar. */
817
- clone: (id: string, options?: {
818
- name?: string;
819
- }) => Promise<PersonaInfo>;
820
- /** The fingerprint these choices would produce. Persists nothing. */
1269
+ setCredentials: (id: string, config: SiteCredentials) => Promise<CredentialsSaved>;
1270
+ credentials: (id: string) => Promise<SiteLogins>;
1271
+ clearCredentials: (id: string, domain: string) => Promise<void>;
1272
+ setMfa: (id: string, config: MfaConfig) => Promise<MfaSaved>;
1273
+ clearMfa: (id: string, domain?: string) => Promise<void>;
821
1274
  preview: (prefs?: PersonaPrefs) => Promise<Fingerprint>;
822
- /** Platforms, and the timezones and locales each may coherently claim. */
823
- options: () => Promise<{
824
- platforms: string[];
825
- timezones: Record<string, string[]>;
826
- locales: Record<string, string[]>;
827
- }>;
828
- /** Pin the persona to one of your proxies, or `null` to let assignment happen at connect. */
829
- pinProxy: (id: string, proxyId: string | null) => Promise<{
830
- ok: boolean;
831
- proxy: {
832
- id: string;
833
- label: string;
834
- } | null;
835
- }>;
1275
+ options: () => Promise<PersonaOptions>;
1276
+ pinProxy: (id: string, proxyId: string | null) => Promise<ProxyPin>;
836
1277
  remove: (id: string) => Promise<void>;
837
- /** Store the second factor for this identity. Sealed at rest, never read back. */
838
- setMfa: (id: string, config: MfaConfig) => Promise<{
839
- configured: boolean;
840
- type: string;
841
- }>;
842
- clearMfa: (id: string, domain?: string) => Promise<void>;
843
- /**
844
- * Store a site login for this identity. Sealed at rest, never read back.
845
- *
846
- * Signing in once on the desktop and inheriting the cookies is still the
847
- * better path. This is for portals that expire a session server-side
848
- * between runs, where an unattended run has nothing else to recover with.
849
- */
850
- setCredentials: (id: string, config: SiteCredentials) => Promise<{
851
- configured: boolean;
852
- domain: string;
853
- username: string;
854
- }>;
855
- /** Which sites this identity can sign in to. Usernames only. */
856
- credentials: (id: string) => Promise<{
857
- credentials: {
858
- domain: string;
859
- username: string;
860
- }[];
861
- }>;
862
- clearCredentials: (id: string, domain: string) => Promise<void>;
1278
+ list: () => Promise<PersonaInfo[]>;
1279
+ get: (id: string) => Promise<PersonaInfo>;
1280
+ create: (options?: PersonaCreate) => Promise<PersonaInfo>;
1281
+ update: (id: string, changes: PersonaUpdate) => Promise<PersonaInfo>;
1282
+ clone: (id: string, options?: CloneOptions) => Promise<PersonaInfo>;
863
1283
  };
864
1284
  /**
865
1285
  * This key's settings: LLM credentials, browser provider, solver.
@@ -878,77 +1298,24 @@ declare class Oya {
878
1298
  };
879
1299
  /** Saved profiles. `personas` is retained as an alias for existing clients. */
880
1300
  readonly profiles: {
881
- list: () => Promise<PersonaInfo[]>;
882
- get: (id: string) => Promise<PersonaInfo>;
883
- /**
884
- * Create an identity. The device — platform, timezone, locale — is chosen
885
- * here and fixed for its life; `preview()` shows what a choice produces.
886
- */
887
- create: (options?: {
888
- name?: string;
889
- prefs?: PersonaPrefs;
890
- proxy?: {
891
- geo?: string;
892
- };
893
- maxConcurrent?: number | null;
894
- }) => Promise<PersonaInfo>;
895
- /** Name, concurrency cap and proxy hint. Never the device — clone for that. */
896
- update: (id: string, changes: {
897
- name?: string;
898
- maxConcurrent?: number | null;
899
- proxy?: {
900
- geo?: string;
901
- } | null;
902
- }) => Promise<PersonaInfo>;
903
- /** A new persona of the same kind of device: same choices, fresh identity, empty jar. */
904
- clone: (id: string, options?: {
905
- name?: string;
906
- }) => Promise<PersonaInfo>;
907
- /** The fingerprint these choices would produce. Persists nothing. */
1301
+ setCredentials: (id: string, config: SiteCredentials) => Promise<CredentialsSaved>;
1302
+ credentials: (id: string) => Promise<SiteLogins>;
1303
+ clearCredentials: (id: string, domain: string) => Promise<void>;
1304
+ setMfa: (id: string, config: MfaConfig) => Promise<MfaSaved>;
1305
+ clearMfa: (id: string, domain?: string) => Promise<void>;
908
1306
  preview: (prefs?: PersonaPrefs) => Promise<Fingerprint>;
909
- /** Platforms, and the timezones and locales each may coherently claim. */
910
- options: () => Promise<{
911
- platforms: string[];
912
- timezones: Record<string, string[]>;
913
- locales: Record<string, string[]>;
914
- }>;
915
- /** Pin the persona to one of your proxies, or `null` to let assignment happen at connect. */
916
- pinProxy: (id: string, proxyId: string | null) => Promise<{
917
- ok: boolean;
918
- proxy: {
919
- id: string;
920
- label: string;
921
- } | null;
922
- }>;
1307
+ options: () => Promise<PersonaOptions>;
1308
+ pinProxy: (id: string, proxyId: string | null) => Promise<ProxyPin>;
923
1309
  remove: (id: string) => Promise<void>;
924
- /** Store the second factor for this identity. Sealed at rest, never read back. */
925
- setMfa: (id: string, config: MfaConfig) => Promise<{
926
- configured: boolean;
927
- type: string;
928
- }>;
929
- clearMfa: (id: string, domain?: string) => Promise<void>;
930
- /**
931
- * Store a site login for this identity. Sealed at rest, never read back.
932
- *
933
- * Signing in once on the desktop and inheriting the cookies is still the
934
- * better path. This is for portals that expire a session server-side
935
- * between runs, where an unattended run has nothing else to recover with.
936
- */
937
- setCredentials: (id: string, config: SiteCredentials) => Promise<{
938
- configured: boolean;
939
- domain: string;
940
- username: string;
941
- }>;
942
- /** Which sites this identity can sign in to. Usernames only. */
943
- credentials: (id: string) => Promise<{
944
- credentials: {
945
- domain: string;
946
- username: string;
947
- }[];
948
- }>;
949
- clearCredentials: (id: string, domain: string) => Promise<void>;
1310
+ list: () => Promise<PersonaInfo[]>;
1311
+ get: (id: string) => Promise<PersonaInfo>;
1312
+ create: (options?: PersonaCreate) => Promise<PersonaInfo>;
1313
+ update: (id: string, changes: PersonaUpdate) => Promise<PersonaInfo>;
1314
+ clone: (id: string, options?: CloneOptions) => Promise<PersonaInfo>;
950
1315
  };
1316
+ /** What this key has spent. */
951
1317
  usage(): Promise<unknown>;
1318
+ /** Waits for a starting browser to dial in, through this client's own list and session calls. */
952
1319
  private waitUntilConnected;
953
1320
  }
954
1321