@oya-ai/browser 1.0.111 → 1.0.113

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
@@ -166,6 +166,21 @@ await browser.ask('Attach my resume to the application and submit it', {
166
166
 
167
167
  A string argument is a path on disk (Node only); a `Blob`, a `File`, or a `Uint8Array` works anywhere. `name` sets the filename the site sees and `type` overrides the MIME guessed from the extension. The ceiling is 10MB per file, and the bytes travel inline with the run. Nothing is stored server-side after it ends.
168
168
 
169
+ ### Data instead of a sentence
170
+
171
+ `extract()` runs the same agent and answers in the shape you ask for, so you do not
172
+ parse prose:
173
+
174
+ ```ts
175
+ const { title, price } = await browser.extract('What is the product on this page?', {
176
+ type: 'object',
177
+ properties: { title: { type: 'string' }, price: { type: 'string' } },
178
+ required: ['title', 'price'],
179
+ });
180
+ ```
181
+
182
+ It throws when the agent reports it could not do the task.
183
+
169
184
  Files work in `data` for `ask()`, `submit()`, and `play()`; `secrets` rejects them, because a file is never typed through a placeholder. A run recorded with `toPlaybook()` keeps the upload as a variable, so the replay takes a different file:
170
185
 
171
186
  ```js
@@ -422,34 +437,35 @@ const oya = new Oya({
422
437
 
423
438
  ### Browser Instance Methods (`browser.*`)
424
439
 
425
- | Method | Returns | Description |
426
- | :---------------------------------- | :------------------------------------------- | :------------------------------------------------------- |
427
- | `goto(url)` | `Promise<void>` | Navigate to URL (with optional auto-CAPTCHA) |
428
- | `ask(prompt, { data?, secrets? }?)` | `Promise<string>` | Natural-language AI driving using key's configured model |
429
- | `analyze()` | `Promise<Analysis>` | Returns markdown representation and numbered elements |
430
- | `elements()` | `Promise<Element[]>` | Returns only visible interactable elements |
431
- | `click(elementId)` | `Promise<void>` | Click element by numeric ID from `analyze()` |
432
- | `type(elementId, text)` | `Promise<{ suggestions_visible?: boolean }>` | Type text into specified element |
433
- | `pressKey(key)` | `Promise<void>` | Dispatch keyboard key event (e.g. `'Enter'`) |
434
- | `scroll(dir, amount?, at?)` | `Promise<void>` | Scroll `'up' \| 'down' \| 'top' \| 'bottom'` |
435
- | `waitFor(selector, timeout?)` | `Promise<void>` | Wait for DOM selector |
436
- | `screenshot()` | `Promise<string>` | Capture page as base64 image data URL |
437
- | `url()` | `Promise<string>` | Current active tab URL |
438
- | `tabs()` | `Promise<Tab[]>` | List open tabs |
439
- | `openTab(url?)` | `Promise<string>` | Open a new tab |
440
- | `switchTab(tabId)` | `Promise<void>` | Switch active tab |
441
- | `closeTab(tabId)` | `Promise<void>` | Close target tab |
442
- | `solveCaptcha()` | `Promise<CaptchaResult>` | Detect and solve on-screen CAPTCHA |
443
- | `completeMfa()` | `Promise<MfaResult>` | Resolve TOTP/SMS MFA or return `liveViewUrl` |
444
- | `liveViewUrl()` | `string` | Dashboard link for this browser |
445
- | `liveStreamUrl()` | `Promise<string>` | SSE frame stream URL with a single-use ticket |
446
- | `shareUrl(options?)` | `Promise<{ url, id, expiresAt }>` | Expiring browser share link; optional control access |
447
- | `revokeShare(id)` | `Promise<void>` | Revoke a share link |
448
- | `submit(task, options?)` | `Promise<Run>` | Background prompt or playbook with callbacks |
449
- | `toPlaybook(name)` | `Promise<Playbook>` | Save the latest agent flow and export Playwright code |
450
- | `play(name, data?, { autoHeal? }?)` | `Promise<PlayResult>` | Replay a saved flow |
451
- | `status()` | `Promise<BrowserDetail>` | Instance metrics, health, and recent activity log |
452
- | `stop()` | `Promise<StopResult>` | Tear down sandbox and release CDP session |
440
+ | Method | Returns | Description |
441
+ | :---------------------------------------------- | :------------------------------------------- | :------------------------------------------------------- |
442
+ | `goto(url)` | `Promise<void>` | Navigate to URL (with optional auto-CAPTCHA) |
443
+ | `ask(prompt, { data?, secrets? }?)` | `Promise<string>` | Natural-language AI driving using key's configured model |
444
+ | `extract(prompt, schema, { data?, secrets? }?)` | `Promise<T>` | The same, answered as data in the shape of a JSON schema |
445
+ | `analyze()` | `Promise<Analysis>` | Returns markdown representation and numbered elements |
446
+ | `elements()` | `Promise<Element[]>` | Returns only visible interactable elements |
447
+ | `click(elementId)` | `Promise<void>` | Click element by numeric ID from `analyze()` |
448
+ | `type(elementId, text)` | `Promise<{ suggestions_visible?: boolean }>` | Type text into specified element |
449
+ | `pressKey(key)` | `Promise<void>` | Dispatch keyboard key event (e.g. `'Enter'`) |
450
+ | `scroll(dir, amount?, at?)` | `Promise<void>` | Scroll `'up' \| 'down' \| 'top' \| 'bottom'` |
451
+ | `waitFor(selector, timeout?)` | `Promise<void>` | Wait for DOM selector |
452
+ | `screenshot()` | `Promise<string>` | Capture page as base64 image data URL |
453
+ | `url()` | `Promise<string>` | Current active tab URL |
454
+ | `tabs()` | `Promise<Tab[]>` | List open tabs |
455
+ | `openTab(url?)` | `Promise<string>` | Open a new tab |
456
+ | `switchTab(tabId)` | `Promise<void>` | Switch active tab |
457
+ | `closeTab(tabId)` | `Promise<void>` | Close target tab |
458
+ | `solveCaptcha()` | `Promise<CaptchaResult>` | Detect and solve on-screen CAPTCHA |
459
+ | `completeMfa()` | `Promise<MfaResult>` | Resolve TOTP/SMS MFA or return `liveViewUrl` |
460
+ | `liveViewUrl()` | `string` | Dashboard link for this browser |
461
+ | `liveStreamUrl()` | `Promise<string>` | SSE frame stream URL with a single-use ticket |
462
+ | `shareUrl(options?)` | `Promise<{ url, id, expiresAt }>` | Expiring browser share link; optional control access |
463
+ | `revokeShare(id)` | `Promise<void>` | Revoke a share link |
464
+ | `submit(task, options?)` | `Promise<Run>` | Background prompt or playbook with callbacks |
465
+ | `toPlaybook(name)` | `Promise<Playbook>` | Save the latest agent flow and export Playwright code |
466
+ | `play(name, data?, { autoHeal? }?)` | `Promise<PlayResult>` | Replay a saved flow |
467
+ | `status()` | `Promise<BrowserDetail>` | Instance metrics, health, and recent activity log |
468
+ | `stop()` | `Promise<StopResult>` | Tear down sandbox and release CDP session |
453
469
 
454
470
  ### Profile and persona management (`oya.profiles`, `oya.personas`)
455
471
 
package/dist/index.cjs CHANGED
@@ -472,6 +472,17 @@ var Browser = class {
472
472
  const path = `/api/browsers/${this.id}/chat`;
473
473
  return agentAnswer(await this.http.request("POST", path, body, AGENT_TIMEOUT_MS)).text;
474
474
  }
475
+ /**
476
+ * `ask()` for data: the agent does the task and answers in the shape of `schema`
477
+ * (a JSON schema), instead of in words. Throws when the agent reports it could not.
478
+ */
479
+ async extract(prompt, schema, values = {}) {
480
+ const body = { messages: [{ role: "user", content: prompt }], ...values, schema };
481
+ const path = `/api/browsers/${this.id}/chat`;
482
+ const res = agentAnswer(await this.http.request("POST", path, body, AGENT_TIMEOUT_MS));
483
+ if (res.failed || res.data === void 0) throw new OyaError(res.text, Status.UNPROCESSABLE, res);
484
+ return res.data;
485
+ }
475
486
  /**
476
487
  * Save the last `ask()` on this browser as a named playbook. Every value that was
477
488
  * typed, picked or clicked becomes a variable, with what the run used kept in
package/dist/index.d.cts CHANGED
@@ -1229,6 +1229,11 @@ declare class Browser {
1229
1229
  * with filters like `{{name|first}}`, so a playbook saved from the run stores no values.
1230
1230
  */
1231
1231
  ask(prompt: string, { data, secrets }?: AskValues): Promise<string>;
1232
+ /**
1233
+ * `ask()` for data: the agent does the task and answers in the shape of `schema`
1234
+ * (a JSON schema), instead of in words. Throws when the agent reports it could not.
1235
+ */
1236
+ extract<T = unknown>(prompt: string, schema: Record<string, unknown>, values?: AskValues): Promise<T>;
1232
1237
  /**
1233
1238
  * Save the last `ask()` on this browser as a named playbook. Every value that was
1234
1239
  * typed, picked or clicked becomes a variable, with what the run used kept in
package/dist/index.d.ts CHANGED
@@ -1229,6 +1229,11 @@ declare class Browser {
1229
1229
  * with filters like `{{name|first}}`, so a playbook saved from the run stores no values.
1230
1230
  */
1231
1231
  ask(prompt: string, { data, secrets }?: AskValues): Promise<string>;
1232
+ /**
1233
+ * `ask()` for data: the agent does the task and answers in the shape of `schema`
1234
+ * (a JSON schema), instead of in words. Throws when the agent reports it could not.
1235
+ */
1236
+ extract<T = unknown>(prompt: string, schema: Record<string, unknown>, values?: AskValues): Promise<T>;
1232
1237
  /**
1233
1238
  * Save the last `ask()` on this browser as a named playbook. Every value that was
1234
1239
  * typed, picked or clicked becomes a variable, with what the run used kept in
package/dist/index.js CHANGED
@@ -440,6 +440,17 @@ var Browser = class {
440
440
  const path = `/api/browsers/${this.id}/chat`;
441
441
  return agentAnswer(await this.http.request("POST", path, body, AGENT_TIMEOUT_MS)).text;
442
442
  }
443
+ /**
444
+ * `ask()` for data: the agent does the task and answers in the shape of `schema`
445
+ * (a JSON schema), instead of in words. Throws when the agent reports it could not.
446
+ */
447
+ async extract(prompt, schema, values = {}) {
448
+ const body = { messages: [{ role: "user", content: prompt }], ...values, schema };
449
+ const path = `/api/browsers/${this.id}/chat`;
450
+ const res = agentAnswer(await this.http.request("POST", path, body, AGENT_TIMEOUT_MS));
451
+ if (res.failed || res.data === void 0) throw new OyaError(res.text, Status.UNPROCESSABLE, res);
452
+ return res.data;
453
+ }
443
454
  /**
444
455
  * Save the last `ask()` on this browser as a named playbook. Every value that was
445
456
  * typed, picked or clicked becomes a variable, with what the run used kept in
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oya-ai/browser",
3
- "version": "1.0.111",
3
+ "version": "1.0.113",
4
4
  "description": "Rotate thousands of browsers behind one API, personas, proxies, stealth, CAPTCHA and MFA.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://oyabrowser.com",