@oya-ai/browser 1.0.56 โ†’ 1.0.58

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.
Files changed (3) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +251 -38
  3. package/package.json +22 -1
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Oya AI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,71 +1,284 @@
1
1
  # @oya-ai/browser
2
2
 
3
- Thousands of browsers behind one API: personas, proxies, stealth, CAPTCHA and MFA. The browser can run on Oya Cloud, Browserbase, Steel, Anchor, Browser Use, your own machines, or any CDP URL. Which one is a setting on your API key, so your code never changes.
3
+ <p align="center">
4
+ <strong>Thousands of browsers behind one API: personas, residential proxies, measured stealth, CAPTCHA, and MFA.</strong>
5
+ </p>
6
+
7
+ <p align="center">
8
+ <a href="https://www.npmjs.com/package/@oya-ai/browser"><img src="https://img.shields.io/npm/v/@oya-ai/browser?color=39ed35&label=@oya-ai/browser&logo=npm" alt="NPM Version"></a>
9
+ <a href="https://github.com/OyadotAI/oya-browser/blob/main/packages/sdk/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg?color=39ed35" alt="License: MIT"></a>
10
+ <a href="https://www.typescriptlang.org"><img src="https://img.shields.io/badge/TypeScript-Ready-3178C6.svg?logo=typescript&logoColor=white" alt="TypeScript"></a>
11
+ <a href="https://nodejs.org"><img src="https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen.svg" alt="Node Version"></a>
12
+ <a href="https://bundlephobia.com/package/@oya-ai/browser"><img src="https://img.shields.io/bundlephobia/minzip/@oya-ai/browser?color=39ed35" alt="Bundle Size"></a>
13
+ </p>
14
+
15
+ ---
16
+
17
+ Orchestrate browser instances running on **Oya Cloud sandboxes**, **Browserbase**, **Steel**, **Anchor**, **Browser Use**, or your own **private Chrome fleet**.
18
+
19
+ Oya does for browser vendors what OpenRouter does for LLM providers. The vendor behind a browser is a setting on your API key, or one `provider` parameter, so switching vendors never means rewriting your agent.
4
20
 
5
21
  ```bash
6
- npm i @oya-ai/browser
22
+ npm install @oya-ai/browser
7
23
  ```
8
24
 
9
- Get an API key at [browser.getoya.ai](https://browser.getoya.ai) and export it as `OYA_API_KEY`.
25
+ Get an API key at [browser.getoya.ai](https://browser.getoya.ai) or self-host your control plane, and set `OYA_API_KEY`.
10
26
 
11
- ## Quickstart
27
+ ---
28
+
29
+ ## โšก Quickstart
30
+
31
+ ### Natural Language Driving
12
32
 
13
33
  ```ts
14
- import { Oya } from '@oya-ai/browser';
34
+ import { Oya } from "@oya-ai/browser";
35
+
36
+ const oya = new Oya(); // Reads process.env.OYA_API_KEY
37
+
38
+ // Explicit resource management (Node 24+ / TS 5.2+)
39
+ // Stops the browser automatically when the scope exits, even on error
40
+ await using browser = await oya.browser.start({ persona: "auto", captcha: "auto" });
15
41
 
16
- const oya = new Oya(); // reads OYA_API_KEY
17
- await using browser = await oya.browser.start(); // stopped when the block exits, even on error
18
- await browser.goto('https://news.ycombinator.com');
19
- console.log(await browser.ask('What are the top 3 stories?'));
42
+ await browser.goto("https://news.ycombinator.com");
43
+ const answer = await browser.ask("What are the top 3 stories and their points?");
44
+ console.log(answer);
20
45
  ```
21
46
 
22
- `await using` needs Node 24+ or TypeScript 5.2+. Otherwise call `await browser.stop()` in a `finally` block.
47
+ > **Universal Lifecycle:** If you are not using `await using`, manage lifecycle with `try / finally`:
48
+ > ```ts
49
+ > const browser = await oya.browser.start();
50
+ > try {
51
+ > await browser.goto("https://example.com");
52
+ > } finally {
53
+ > await browser.stop();
54
+ > }
55
+ > ```
23
56
 
24
- ## Personas
57
+ ---
25
58
 
26
- A persona is one device: fingerprint, cookie jar and exit IP, the same on every run.
59
+ ## ๐Ÿ›ก๏ธ Deterministic Personas (Anti-Ban Identity)
60
+
61
+ A persona is a permanent, mathematically seeded device identity: **fingerprint + cookie jar + residential proxy**, identical on every run to eliminate bot-farm and device-farm flags.
27
62
 
28
63
  ```ts
64
+ import { Oya } from "@oya-ai/browser";
65
+
66
+ const oya = new Oya();
67
+
68
+ // Create a persistent persona
29
69
  const persona = await oya.personas.create({
30
- name: 'us-shopper',
31
- prefs: { platform: 'MacIntel', timezone: 'America/New_York', locale: 'en-US' }, // fixed for life
32
- proxy: { geo: 'US' },
70
+ name: "us-shopper",
71
+ prefs: { platform: "MacIntel", timezone: "America/New_York", locale: "en-US" },
72
+ proxy: { geo: "US" },
73
+ maxConcurrent: 2, // Concurrency cap prevents device-farm detection
33
74
  });
34
- await using browser = await oya.browser.start({ persona: persona.id }); // or 'auto' to rotate
75
+
76
+ // Launch a browser with this persona (or persona: 'auto' for least-recently-used)
77
+ await using browser = await oya.browser.start({ persona: persona.id });
78
+ await browser.goto("https://www.amazon.com");
79
+
80
+ // Hardware attributes remain byte-identical on subsequent sessions
81
+ console.log(persona.fingerprint.platform, persona.fingerprint.timezone);
82
+
83
+ // Need another device of the same class? Clone it with an empty cookie jar:
84
+ // const altDevice = await oya.personas.clone(persona.id, { name: "us-shopper-02" });
35
85
  ```
36
86
 
37
- ## CAPTCHA and MFA
87
+ ---
88
+
89
+ ## ๐Ÿง  Structured Agent Analysis & Interaction
90
+
91
+ Skip messy DOM traversal. Get clean markdown and numbered interactive elements:
38
92
 
39
93
  ```ts
40
- await using browser = await oya.browser.start({ captcha: 'auto' }); // solve on every goto()
41
- await browser.solveCaptcha(); // or on demand
94
+ import { Oya } from "@oya-ai/browser";
95
+
96
+ const oya = new Oya();
97
+ await using browser = await oya.browser.start();
98
+ await browser.goto("https://github.com/trending");
42
99
 
43
- await oya.personas.setMfa(persona.id, { type: 'totp', secret: process.env.TOTP_SECRET! });
44
- const mfa = await browser.completeMfa(); // totp, email, sms, or a human handoff
45
- if (!mfa.completed) console.log('Finish it here:', mfa.liveViewUrl);
100
+ // Analyze page: returns markdown and visible numbered elements
101
+ const { markdown, elements } = await browser.analyze();
102
+ console.log(markdown.slice(0, 300));
103
+
104
+ // Interact using numbered element IDs:
105
+ const firstRepo = elements.find((el) => el.tag === "a" && el.href?.includes("/stargazers"));
106
+ if (firstRepo) {
107
+ await browser.click(firstRepo.id); // clicks [data-ac-id="firstRepo.id"]
108
+ }
46
109
  ```
47
110
 
48
- ## Any vendor, any tool
111
+ ---
112
+
113
+ ## ๐Ÿงฉ Challenge Handling: Automated CAPTCHA & Sealed MFA
49
114
 
50
115
  ```ts
51
- import { chromium } from 'playwright-core';
116
+ import { Oya } from "@oya-ai/browser";
117
+
118
+ const oya = new Oya();
119
+
120
+ // 1. Seal a TOTP secret on a persona (encrypted with AES-256-GCM at rest, never exposed over API)
121
+ const persona = await oya.personas.create({ name: "finance-admin" });
122
+ await oya.personas.setMfa(persona.id, {
123
+ type: "totp",
124
+ secret: process.env.TOTP_SECRET!,
125
+ });
126
+
127
+ await using browser = await oya.browser.start({ persona: persona.id });
52
128
 
53
- await using browser = await oya.browser.start({ provider: 'browserbase' }); // one string per vendor
54
- const pw = await chromium.connectOverCDP(browser.cdpUrl!); // Playwright, Puppeteer, Stagehandโ€ฆ
129
+ // 2. Clear CAPTCHAs automatically (uses vendor solver or CapSolver/2Captcha fallback)
130
+ await browser.goto("https://www.google.com/recaptcha/api2/demo");
131
+ const captcha = await browser.solveCaptcha();
132
+ console.log("CAPTCHA Solved:", captcha.solved, "via", captcha.method);
133
+
134
+ // 3. Complete Two-Factor Authentication
135
+ await browser.goto(process.env.MFA_LOGIN_URL!);
136
+ const mfa = await browser.completeMfa();
137
+
138
+ if (!mfa.completed && mfa.liveViewUrl) {
139
+ // Hand off to human operator if interactive push notification or WebAuthn is needed
140
+ console.log("Interactive handoff required at:", mfa.liveViewUrl);
141
+ }
55
142
  ```
56
143
 
57
- ## API
144
+ ---
145
+
146
+ ## ๐Ÿ”Œ Universal CDP Gateway (Playwright & Puppeteer)
147
+
148
+ Every browser exposes an authenticated `browser.cdpUrl` routed through Oya's gateway. Connect standard Playwright, Puppeteer, or Stagehand:
149
+
150
+ ```ts
151
+ import { chromium } from "playwright-core";
152
+ import { Oya } from "@oya-ai/browser";
153
+
154
+ const oya = new Oya();
155
+
156
+ // Run on any underlying provider: browserbase, steel, anchor, browseruse, or oya-cloud
157
+ await using browser = await oya.browser.start({ provider: "browserbase" });
158
+
159
+ // Connect Playwright directly over Oya's gateway
160
+ const context = (await chromium.connectOverCDP(browser.cdpUrl!)).contexts()[0];
161
+ const page = context.pages()[0] ?? (await context.newPage());
162
+
163
+ await page.goto("https://news.ycombinator.com");
164
+ console.log("Page Title:", await page.title());
165
+ ```
166
+
167
+ ---
168
+
169
+ ## ๐Ÿ“š Complete API Reference
170
+
171
+ ### Initialization
172
+
173
+ ```ts
174
+ import { Oya } from "@oya-ai/browser";
175
+
176
+ const oya = new Oya({
177
+ apiKey: "oya_...", // default: process.env.OYA_API_KEY
178
+ baseUrl: "https://browser.getoya.ai", // default: OYA_BASE_URL, then the hosted service
179
+ timeoutMs: 60_000, // per request
180
+ // fetch: customFetch, // any fetch-compatible implementation
181
+ });
182
+ ```
183
+
184
+ ### Browser Operations (`oya.browser`)
185
+
186
+ | Method | Signature | Description |
187
+ |:---|:---|:---|
188
+ | `start(options)` | `(options?: StartOptions) => Promise<Browser>` | Start a browser and wait until it is ready for commands |
189
+ | `get(id)` | `(id: string) => Promise<Browser>` | Reattach to an existing running browser |
190
+ | `list()` | `() => Promise<BrowserInfo[]>` | List all active running browser sessions |
191
+ | `stop(ids \| 'all')` | `(ids: string[] \| 'all') => Promise<{ stopped: number; results: StopResult[] }>` | Stop target browsers or all browsers |
192
+ | `stopAll()` | `() => Promise<number>` | Terminate all active browser sessions |
193
+
194
+ #### `StartOptions`
195
+
196
+ - `persona?: 'default' | 'auto' | string` โ€” Assign persistent identity
197
+ - `provider?: 'oya-cloud' | 'oya-selfhosted' | 'browserbase' | 'steel' | 'anchor' | 'browseruse' | 'cdp'`
198
+ - `wsUrl?: string`: required only for the `'cdp'` provider
199
+ - `name?: string`: display name in the console and `oya ls`
200
+ - `captcha?: 'auto' | 'off'` โ€” Automatically solve CAPTCHAs on navigation
201
+ - `queueMs?: number` โ€” Wait duration for fleet capacity (ms)
202
+ - `budgetUsd?: number` โ€” Enforce budget limit for session
203
+ - `idempotencyKey?: string` โ€” Safe retry token
204
+ - `governed?: boolean` โ€” Enforce strict isolation policies
205
+
206
+ ### Browser Instance Methods (`browser.*`)
207
+
208
+ | Method | Returns | Description |
209
+ |:---|:---|:---|
210
+ | `goto(url)` | `Promise<void>` | Navigate to URL (with optional auto-CAPTCHA) |
211
+ | `ask(prompt)` | `Promise<string>` | Natural-language AI driving using key's configured model |
212
+ | `analyze()` | `Promise<Analysis>` | Returns markdown representation and numbered elements |
213
+ | `elements()` | `Promise<Element[]>` | Returns only visible interactable elements |
214
+ | `click(elementId)` | `Promise<void>` | Click element by numeric ID from `analyze()` |
215
+ | `type(elementId, text)` | `Promise<{ suggestions_visible?: boolean }>` | Type text into specified element |
216
+ | `pressKey(key)` | `Promise<void>` | Dispatch keyboard key event (e.g. `'Enter'`) |
217
+ | `scroll(dir, amount?, at?)` | `Promise<void>` | Scroll `'up' \| 'down' \| 'top' \| 'bottom'` |
218
+ | `waitFor(selector, timeout?)`| `Promise<void>` | Wait for DOM selector |
219
+ | `screenshot()` | `Promise<string>` | Capture page as base64 image data URL |
220
+ | `url()` | `Promise<string>` | Current active tab URL |
221
+ | `tabs()` | `Promise<Tab[]>` | List open tabs |
222
+ | `openTab(url?)` | `Promise<string>` | Open a new tab |
223
+ | `switchTab(tabId)` | `Promise<void>` | Switch active tab |
224
+ | `closeTab(tabId)` | `Promise<void>` | Close target tab |
225
+ | `solveCaptcha()` | `Promise<CaptchaResult>` | Detect and solve on-screen CAPTCHA |
226
+ | `completeMfa()` | `Promise<MfaResult>` | Resolve TOTP/SMS MFA or return `liveViewUrl` |
227
+ | `liveViewUrl()` | `string` | SSE JPEG stream URL for sub-second human takeover |
228
+ | `status()` | `Promise<BrowserDetail>` | Instance metrics, health, and recent activity log |
229
+ | `stop()` | `Promise<StopResult>` | Tear down sandbox and release CDP session |
230
+
231
+ ### Persona Management (`oya.personas`)
232
+
233
+ | Method | Description |
234
+ |:---|:---|
235
+ | `create({ name?, prefs?, proxy?, maxConcurrent? })` | Create new deterministic device identity |
236
+ | `list()` | List all saved personas and active concurrency |
237
+ | `get(id)` | Get persona profile details |
238
+ | `update(id, changes)` | Update name, concurrency limit, or proxy geo |
239
+ | `clone(id, options)` | Create fresh persona with same device traits but empty cookie jar |
240
+ | `preview(prefs)` | Preview generated hardware fingerprint before creating |
241
+ | `options()` | Available platforms, timezones, and valid locales |
242
+ | `pinProxy(id, proxyId)` | Bind persona permanently to a residential proxy exit node |
243
+ | `remove(id)` | Delete persona and associated cookie jar |
244
+ | `setMfa(id, config)` | Store TOTP secret (sealed at rest with AES-256-GCM) |
245
+ | `clearMfa(id)` | Remove MFA secret from persona |
246
+
247
+ ### Durable Governance & Control (`oya.control`)
248
+
249
+ | Method | Description |
250
+ |:---|:---|
251
+ | `overview()` | Fleet overview, spend, sessions, and active rate cards |
252
+ | `sessions()` | List all durable sessions (including cleanup-pending) |
253
+ | `session(id)` | Get detailed session execution state |
254
+ | `takeover(id, 'acquire' \| 'release' \| 'resume')` | Manage human control leases |
255
+ | `ticket(id)` | Generate single-use connection ticket for secure handoff |
256
+ | `events(after?)` | Stream append-only audit event log |
257
+ | `createCredential(options)` | Mint scoped service credential (`viewer` / `operator` / `administrator`) |
258
+ | `createWebhook(url, types)` | Register HMAC-signed webhook for fleet lifecycle events |
259
+
260
+ ---
261
+
262
+ ## ๐Ÿšจ Error Handling
263
+
264
+ All failed API and command operations throw an `OyaError`:
265
+
266
+ ```ts
267
+ import { Oya, OyaError } from "@oya-ai/browser";
268
+
269
+ try {
270
+ const oya = new Oya();
271
+ await oya.browser.start({ persona: "invalid-id" });
272
+ } catch (err) {
273
+ if (err instanceof OyaError) {
274
+ console.error(`Oya API Error (${err.status}):`, err.message);
275
+ console.error("Payload:", err.body);
276
+ }
277
+ }
278
+ ```
58
279
 
59
- | | |
60
- |---|---|
61
- | `new Oya({ apiKey?, baseUrl?, timeoutMs? })` | Defaults to `OYA_API_KEY` and `OYA_BASE_URL` |
62
- | `oya.browser` | `start(options)`, `get(id)`, `list()`, `stop(ids \| 'all')`, `stopAll()` |
63
- | `browser` | `goto`, `ask`, `analyze`, `elements`, `click`, `type`, `pressKey`, `scroll`, `waitFor`, `screenshot`, `url`, `tabs`, `openTab`, `switchTab`, `closeTab`, `solveCaptcha`, `completeMfa`, `liveViewUrl`, `status`, `stop`, `cdpUrl` |
64
- | `oya.personas` | `list`, `get`, `create`, `update`, `clone`, `preview`, `options`, `pinProxy`, `remove`, `setMfa`, `clearMfa` |
65
- | `oya.config` | `get()`, `set(values)`: model, provider and solver keys for this API key |
66
- | `oya.control` | Sessions, human takeover, events, members, credentials and webhooks |
67
- | `oya.usage()` | What this key has spent |
280
+ ---
68
281
 
69
- Errors are thrown as `OyaError` with `status` and `body`. ESM and CommonJS, fully typed, no runtime dependencies, Node 18+.
282
+ ## ๐Ÿ“„ License
70
283
 
71
- More: [examples](https://github.com/OyadotAI/AgentChrome/tree/main/examples) ยท [docs](https://browser.getoya.ai/docs) ยท [CLI](https://www.npmjs.com/package/@oya-ai/cli)
284
+ MIT ยฉ [Oya](https://getoya.ai)
package/package.json CHANGED
@@ -1,8 +1,29 @@
1
1
  {
2
2
  "name": "@oya-ai/browser",
3
- "version": "1.0.56",
3
+ "version": "1.0.58",
4
4
  "description": "Rotate thousands of browsers behind one API โ€” personas, proxies, stealth, CAPTCHA and MFA.",
5
5
  "license": "MIT",
6
+ "homepage": "https://browser.getoya.ai",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/OyadotAI/oya-browser.git",
10
+ "directory": "packages/sdk"
11
+ },
12
+ "bugs": "https://github.com/OyadotAI/oya-browser/issues",
13
+ "keywords": [
14
+ "browser",
15
+ "ai-agents",
16
+ "cdp",
17
+ "playwright",
18
+ "puppeteer",
19
+ "headless-chrome",
20
+ "browserbase",
21
+ "steel",
22
+ "browser-use",
23
+ "captcha",
24
+ "fingerprint",
25
+ "mcp"
26
+ ],
6
27
  "type": "module",
7
28
  "main": "./dist/index.cjs",
8
29
  "module": "./dist/index.js",