@oya-ai/browser 1.0.83 → 1.0.86

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
@@ -26,81 +26,210 @@ Get an API key at [browser.getoya.ai](https://browser.getoya.ai) or self-host yo
26
26
 
27
27
  ---
28
28
 
29
- ## ⚡ Quickstart
29
+ ## Quickstart
30
30
 
31
- ### Natural Language Driving
31
+ Requires Node.js 18+; examples use ES modules. Set `OYA_API_KEY` in your environment. `OYA_BASE_URL` optionally points to a self-hosted control plane.
32
32
 
33
- ```ts
33
+ ```js
34
+ import { Oya } from "@oya-ai/browser";
35
+
36
+ const oya = new Oya();
37
+ const browser = await oya.browser.start({ captcha: "auto" });
38
+ try {
39
+ await browser.goto("https://example.com");
40
+ console.log(await browser.ask("What is the main heading on this page?"));
41
+ } finally {
42
+ await browser.stop();
43
+ }
44
+ ```
45
+
46
+ On Node.js 24+, `await using browser = await oya.browser.start()` also stops the browser when its scope exits, including on error.
47
+
48
+ ## Portal automation: record once, replay with new inputs
49
+
50
+ This example adapts the portal-automation project's workflow: reuse a persona, attach to an existing browser or start one, run a prompt the first time, then replay its saved playbook. The portal, workflow names, and request values below are fictional. Supply your own test portal and credentials through environment variables; adapt the task to its actual pages.
51
+
52
+ Save as `portal.mjs` and run `node portal.mjs` after setting `OYA_API_KEY`, `PORTAL_URL`, `PORTAL_USERNAME`, and `PORTAL_PASSWORD`. Set `OYA_BROWSER_ID` only to reuse an already running browser.
53
+
54
+ ```js
55
+ import { createInterface } from "node:readline/promises";
34
56
  import { Oya } from "@oya-ai/browser";
35
57
 
36
- const oya = new Oya(); // Reads process.env.OYA_API_KEY
58
+ function requiredEnv(name) {
59
+ const value = process.env[name];
60
+ if (!value) throw new Error(`Set ${name} before running this example.`);
61
+ return value;
62
+ }
37
63
 
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" });
64
+ const oya = new Oya({ apiKey: requiredEnv("OYA_API_KEY") });
65
+ const portalUrl = requiredEnv("PORTAL_URL");
66
+ const playbookName = "portal-request-review";
67
+ const secrets = {
68
+ username: requiredEnv("PORTAL_USERNAME"),
69
+ password: requiredEnv("PORTAL_PASSWORD"),
70
+ };
71
+ // Fictional test inputs. data is visible to the agent.
72
+ const data = {
73
+ customerName: "Alex Example",
74
+ requestId: "DEMO-0001",
75
+ requestedDate: "2030-01-15",
76
+ };
77
+ const task = [
78
+ "If not logged in, log in with {{username}} and {{password}}.",
79
+ "Open New Request and enter {{requestId}} as the reference.",
80
+ "Fill first name {{customerName|first}} and last name {{customerName|last}}.",
81
+ "Set the requested date to {{requestedDate|date:MM/DD/YYYY}}.",
82
+ "If a field is already correct, do not type its value again.",
83
+ "If an action times out, inspect the page before retrying it.",
84
+ "If information is missing, ask the person instead of guessing.",
85
+ "Stop on the review page. Do not submit the request.",
86
+ ].join("\n");
87
+
88
+ const existingId = process.env.OYA_BROWSER_ID;
89
+ let browser;
90
+ if (existingId) {
91
+ browser = await oya.browser.get(existingId);
92
+ } else {
93
+ const persona = (await oya.personas.list())
94
+ .find((p) => p.name === "portal-demo")
95
+ ?? await oya.personas.create({ name: "portal-demo" });
96
+ browser = await oya.browser.start({ persona: persona.id, captcha: "auto" });
97
+ }
41
98
 
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);
99
+ try {
100
+ console.log("Watch in your Oya dashboard:", browser.liveViewUrl());
101
+ await browser.goto(portalUrl);
102
+ const exists = (await oya.playbooks.list()).some((p) => p.name === playbookName);
103
+ const run = await browser.submit(
104
+ exists ? { playbook: playbookName } : { prompt: task },
105
+ {
106
+ // Replay accepts all variables in data; the playbook remembers secret names.
107
+ ...(exists ? { data: { ...data, ...secrets } } : { data, secrets }),
108
+ onSuccess: () => console.log("Run succeeded."),
109
+ onFailure: (error) => console.error("Run failed with status:", error.status),
110
+ onHealed: (result) => {
111
+ console.log("A repair draft is ready for review:", result.draft);
112
+ },
113
+ onHumanAttention: async (request) => {
114
+ console.log("Attention needed:", request.reason);
115
+ console.log(request.message);
116
+ console.log("Open:", request.liveViewUrl ?? browser.liveViewUrl());
117
+ const terminal = createInterface({ input: process.stdin, output: process.stdout });
118
+ try {
119
+ const answer = await terminal.question(request.reason === "agent"
120
+ ? "Answer the agent: "
121
+ : "Handle this in the live view, then press Enter: ");
122
+ await request.respond(answer || "done");
123
+ } finally {
124
+ terminal.close();
125
+ }
126
+ },
127
+ },
128
+ );
129
+
130
+ await run.done; // Rejects on failure; do not save a failed run as a playbook.
131
+ const info = await run.status();
132
+ console.log("Run status:", info.status);
133
+ if (!exists) {
134
+ const playbook = await browser.toPlaybook(playbookName);
135
+ console.log("Saved:", playbook.name, "Steps:", playbook.steps);
136
+ console.log("Variables:", playbook.variables);
137
+ // playbook.code contains the flow as an exported Playwright module.
138
+ }
139
+ } finally {
140
+ // Leave an attached browser open; stop only the browser this script started.
141
+ if (!existingId) await browser.stop();
142
+ }
45
143
  ```
46
144
 
47
- ### Data and secrets
145
+ `submit()` starts a background run and returns a `Run` handle. The SDK polls every two seconds by default (`pollMs` overrides this). `run.done` resolves with the result or rejects with an error; `run.status()` reads the run record. Attention reasons are `captcha`, `mfa`, `agent`, and `heal_failed`. A run waits up to 30 minutes for `request.respond()` or `run.respond()`. Run records are held in server memory for one hour after completion; they do not survive a server restart.
48
146
 
49
- ```ts
50
- await browser.ask("Log in as {{user}} with {{password}}, then book {{patient}} born {{dob}}", {
51
- data: { patient: "John Smith", dob: "Jan 5, 1970" }, // the agent reads these
52
- secrets: { user: "ops", password: process.env.PORTAL_PASSWORD! }, // the agent never sees these
147
+ ### Data, secrets, and reusable placeholders
148
+
149
+ Use `{{name}}` references in prompts instead of interpolating values into the prompt text. `data` is available to the agent for reasoning; `secrets` supplies values for typing without including them as readable task inputs. Filters include `first`, `last`, `digits`, and `date:MM/DD/YYYY`.
150
+
151
+ For prompt runs, pass credentials in `secrets`. For replay, pass all variables in `data`: the saved playbook tracks which variables are secret, including during healing. Placeholder-based inputs remain variables in the saved flow. This is not a blanket redaction guarantee for page content, screenshots, agent replies, or application logs; inspect exported code before sharing it.
152
+
153
+ ### Review a repaired playbook
154
+
155
+ Replay normally runs recorded steps without an LLM. With `autoHeal: true` (the default), a broken step can hand over to the agent, which saves a repair as `<name>:draft`. Promotion replaces the saved playbook with that draft.
156
+
157
+ The following continues with an active `browser` and the inputs above. Replaying a draft performs its actions, so review its code and use test inputs before promoting it.
158
+
159
+ ```js
160
+ const saved = (await oya.playbooks.list())
161
+ .find((p) => p.name === playbookName);
162
+ if (saved?.draft) {
163
+ // Review saved.draft.code before executing it.
164
+ await browser.play(`${playbookName}:draft`, { ...data, ...secrets }, { autoHeal: false });
165
+ await oya.playbooks.promote(playbookName);
166
+ }
167
+ ```
168
+
169
+ Use `autoHeal: false` with `play()` or `submit({ playbook: name }, options)` to fail at a broken step without agent repair. `oya.playbooks.remove(name)` removes a playbook and its draft; `remove("<name>:draft")` removes only the draft.
170
+
171
+ ## Use your own model key
172
+
173
+ As in the portal-automation project's model setup script, configure the model on your Oya API key once for subsequent agent runs:
174
+
175
+ ```js
176
+ import { Oya } from "@oya-ai/browser";
177
+
178
+ const modelKey = process.env.GEMINI_API_KEY;
179
+ if (!modelKey) throw new Error("Set GEMINI_API_KEY first.");
180
+ const oya = new Oya();
181
+ await oya.config.set({
182
+ llm_provider: "gemini", // "openai" | "anthropic" | "gemini" | "vertex"
183
+ openai_api_key: modelKey, // Shared field name for every supported provider.
184
+ // chat_model: process.env.OYA_CHAT_MODEL, // Optional provider model override.
53
185
  });
54
186
  ```
55
187
 
56
- The agent types every value as a placeholder, transforming it with filters when a form needs a piece or another format: `{{patient|first}}`, `{{patient|last}}`, `{{dob|date:MM/DD/YYYY}}`, `{{dob|date:YYYY}}`, `{{phone|digits}}`. A playbook saved from the run stores the placeholders, never the values.
188
+ ### Gemini Enterprise (ex-Vertex AI)
57
189
 
58
- ### Playbooks: ask once, replay without the LLM
190
+ `llm_provider: "vertex"` targets express mode, whose API keys work against a global endpoint with no GCP project or location:
59
191
 
60
- ```ts
61
- const pb = await browser.toPlaybook("download-invoice");
62
- console.log(pb.variables); // ["orderNumber"]
63
- console.log(pb.code); // the same flow as Playwright
64
-
65
- // Later, on any browser. If the site changed, the agent finishes the task
66
- // and saves its fix as a draft ("download-invoice:draft").
67
- const result = await browser.play("download-invoice", { orderNumber: "2077" });
68
- if (result.healed) await oya.playbooks.promote("download-invoice"); // after reviewing it
69
- // autoHeal: false throws at the broken step instead.
192
+ ```js
193
+ await oya.config.set({ llm_provider: "vertex", openai_api_key: process.env.VERTEX_EXPRESS_KEY });
70
194
  ```
71
195
 
72
- ### Submit and get called back
196
+ For an enterprise project instead, point at its OpenAI-compatible endpoint. That path authenticates with a Google OAuth access token rather than an API key, and the token expires after about an hour, so it suits a one-off run rather than a long-lived deployment:
73
197
 
74
- ```ts
75
- const run = await browser.submit({ playbook: "download-invoice" }, {
76
- data: { orderNumber: "2077" },
77
- onSuccess: (result) => console.log("done", result),
78
- onFailure: (error) => console.error("failed", error.message),
79
- // CAPTCHA or MFA it could not clear, the agent asking a question, or a replay it could not heal.
80
- onHumanAttention: async (req) => {
81
- console.log(req.reason, req.message, req.liveViewUrl);
82
- await req.respond("done"); // or your answer, when req.reason === "agent"
83
- },
84
- onHealed: (result) => console.log("fix saved as", result.draft),
198
+ ```js
199
+ await oya.config.set({
200
+ llm_provider: "vertex",
201
+ openai_base_url: "https://us-central1-aiplatform.googleapis.com/v1/projects/PROJECT/locations/us-central1/endpoints/openapi",
202
+ openai_api_key: accessToken, // gcloud auth print-access-token
203
+ chat_model: "google/gemini-2.5-flash", // this endpoint prefixes model ids
85
204
  });
86
- await run.done;
87
205
  ```
88
206
 
89
- > **Universal Lifecycle:** If you are not using `await using`, manage lifecycle with `try / finally`:
90
- > ```ts
91
- > const browser = await oya.browser.start();
92
- > try {
93
- > await browser.goto("https://example.com");
94
- > } finally {
95
- > await browser.stop();
96
- > }
97
- > ```
207
+ Omit `chat_model` to use the configured provider's default. `oya.config.get()` reads configuration. To return to the shared model configuration:
208
+
209
+ ```js
210
+ await oya.config.set({ llm_provider: null, openai_api_key: null, chat_model: null });
211
+ ```
212
+
213
+ ## Live view, sharing, and embedded streams
214
+
215
+ `browser.liveViewUrl()` returns a dashboard link without an API key; the viewer signs into Oya. For a scoped handoff to someone else, create an expiring share link:
216
+
217
+ ```js
218
+ const share = await browser.shareUrl({ control: true, expiresInSeconds: 900 });
219
+ // Deliver share.url privately to the intended operator.
220
+ // Once the handoff is finished:
221
+ await browser.revokeShare(share.id);
222
+ ```
223
+
224
+ Share links are view-only by default. `control: true` permits browser interaction. Anyone holding the link has its access until expiry or revocation.
225
+
226
+ For an embedded SSE stream of JPEG frames, use `await browser.liveStreamUrl()`. It mints a single-use connection ticket valid for 60 seconds; request a new URL for each connection.
98
227
 
99
228
  ---
100
229
 
101
230
  ## 🛡️ Deterministic Personas (Anti-Ban Identity)
102
231
 
103
- 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.
232
+ A persona groups a stable device fingerprint, saved login cookies, and a proxy assignment for reuse across browser sessions.
104
233
 
105
234
  ```ts
106
235
  import { Oya } from "@oya-ai/browser";
@@ -112,7 +241,7 @@ const persona = await oya.personas.create({
112
241
  name: "us-shopper",
113
242
  prefs: { platform: "MacIntel", timezone: "America/New_York", locale: "en-US" },
114
243
  proxy: { geo: "US" },
115
- maxConcurrent: 2, // Concurrency cap prevents device-farm detection
244
+ maxConcurrent: 2, // Limit simultaneous sessions for this identity
116
245
  });
117
246
 
118
247
  // Launch a browser with this persona (or persona: 'auto' for least-recently-used)
@@ -208,7 +337,7 @@ console.log("Page Title:", await page.title());
208
337
 
209
338
  ---
210
339
 
211
- ## 📚 Complete API Reference
340
+ ## API reference
212
341
 
213
342
  ### Initialization
214
343
 
@@ -243,14 +372,18 @@ const oya = new Oya({
243
372
  - `queueMs?: number` — Wait duration for fleet capacity (ms)
244
373
  - `budgetUsd?: number` — Enforce budget limit for session
245
374
  - `idempotencyKey?: string` — Safe retry token
246
- - `governed?: boolean` — Enforce strict isolation policies
375
+ - `governed?: boolean` — Enable governed session controls
376
+ - `profile?: string` — Saved login profile (takes precedence over `persona`)
377
+ - `priority?: 'low' | 'normal' | 'high'` — Queue priority
378
+ - `policy?: { allowedHosts?, humanHosts?, region?, redactRecording? }` — Session policy
379
+ - `readyTimeoutMs?: number` — Wait budget for a starting browser to connect
247
380
 
248
381
  ### Browser Instance Methods (`browser.*`)
249
382
 
250
383
  | Method | Returns | Description |
251
384
  |:---|:---|:---|
252
385
  | `goto(url)` | `Promise<void>` | Navigate to URL (with optional auto-CAPTCHA) |
253
- | `ask(prompt)` | `Promise<string>` | Natural-language AI driving using key's configured model |
386
+ | `ask(prompt, { data?, secrets? }?)` | `Promise<string>` | Natural-language AI driving using key's configured model |
254
387
  | `analyze()` | `Promise<Analysis>` | Returns markdown representation and numbered elements |
255
388
  | `elements()` | `Promise<Element[]>` | Returns only visible interactable elements |
256
389
  | `click(elementId)` | `Promise<void>` | Click element by numeric ID from `analyze()` |
@@ -266,11 +399,19 @@ const oya = new Oya({
266
399
  | `closeTab(tabId)` | `Promise<void>` | Close target tab |
267
400
  | `solveCaptcha()` | `Promise<CaptchaResult>` | Detect and solve on-screen CAPTCHA |
268
401
  | `completeMfa()` | `Promise<MfaResult>` | Resolve TOTP/SMS MFA or return `liveViewUrl` |
269
- | `liveViewUrl()` | `string` | SSE JPEG stream URL for sub-second human takeover |
402
+ | `liveViewUrl()` | `string` | Dashboard link for this browser |
403
+ | `liveStreamUrl()` | `Promise<string>` | SSE frame stream URL with a single-use ticket |
404
+ | `shareUrl(options?)` | `Promise<{ url, id, expiresAt }>` | Expiring browser share link; optional control access |
405
+ | `revokeShare(id)` | `Promise<void>` | Revoke a share link |
406
+ | `submit(task, options?)` | `Promise<Run>` | Background prompt or playbook with callbacks |
407
+ | `toPlaybook(name)` | `Promise<Playbook>` | Save the latest agent flow and export Playwright code |
408
+ | `play(name, data?, { autoHeal? }?)` | `Promise<PlayResult>` | Replay a saved flow |
270
409
  | `status()` | `Promise<BrowserDetail>` | Instance metrics, health, and recent activity log |
271
410
  | `stop()` | `Promise<StopResult>` | Tear down sandbox and release CDP session |
272
411
 
273
- ### Persona Management (`oya.personas`)
412
+ ### Profile and persona management (`oya.profiles`, `oya.personas`)
413
+
414
+ `oya.profiles` exposes the same methods as `oya.personas`; the persona name remains available for existing integrations.
274
415
 
275
416
  | Method | Description |
276
417
  |:---|:---|
@@ -312,7 +453,7 @@ await oya.personas.pinProxy(persona.id, proxy.id);
312
453
  | `session(id)` | Get detailed session execution state |
313
454
  | `takeover(id, 'acquire' \| 'release' \| 'resume')` | Manage human control leases |
314
455
  | `ticket(id)` | Generate single-use connection ticket for secure handoff |
315
- | `events(after?)` | Stream append-only audit event log |
456
+ | `events(after?)` | Read audit events and a pagination cursor |
316
457
  | `createCredential(options)` | Mint scoped service credential (`viewer` / `operator` / `administrator`) |
317
458
  | `createWebhook(url, types)` | Register HMAC-signed webhook for fleet lifecycle events |
318
459
 
@@ -320,7 +461,7 @@ await oya.personas.pinProxy(persona.id, proxy.id);
320
461
 
321
462
  ## 🚨 Error Handling
322
463
 
323
- All failed API and command operations throw an `OyaError`:
464
+ API error responses and failed browser commands throw `OyaError`. Network failures, request timeouts, and configuration errors may throw other error types:
324
465
 
325
466
  ```ts
326
467
  import { Oya, OyaError } from "@oya-ai/browser";
@@ -331,7 +472,9 @@ try {
331
472
  } catch (err) {
332
473
  if (err instanceof OyaError) {
333
474
  console.error(`Oya API Error (${err.status}):`, err.message);
334
- console.error("Payload:", err.body);
475
+ // err.body contains response details; inspect privately if needed.
476
+ } else {
477
+ throw err;
335
478
  }
336
479
  }
337
480
  ```
package/dist/index.cjs CHANGED
@@ -171,7 +171,7 @@ var Browser = class {
171
171
  await this.command("close_tab", { tab_id: tabId });
172
172
  }
173
173
  /**
174
- * Detect and clear a CAPTCHA. Providers that solve natively are left to do
174
+ x * Detect and clear a CAPTCHA. Providers that solve natively are left to do
175
175
  * it; everything else goes to the configured solver.
176
176
  */
177
177
  solveCaptcha() {
@@ -506,7 +506,10 @@ var Oya = class {
506
506
  *
507
507
  * Bring your own LLM key (it pays for its own tokens, so no hourly chat quota applies):
508
508
  * await oya.config.set({ llm_provider: 'gemini', openai_api_key: process.env.GEMINI_API_KEY });
509
- * `llm_provider` is 'openai' | 'anthropic' | 'gemini'; `chat_model` overrides its default model.
509
+ * `llm_provider` is 'openai' | 'anthropic' | 'gemini' | 'vertex'; `chat_model` overrides
510
+ * its default model. 'vertex' is Gemini Enterprise (ex-Vertex AI) in express mode, which
511
+ * needs no GCP project; for a project-scoped endpoint, set `openai_base_url` to
512
+ * `.../endpoints/openapi` and pass an OAuth access token as `openai_api_key`.
510
513
  */
511
514
  config = {
512
515
  get: () => this.http.request("GET", "/api/config"),
package/dist/index.d.cts CHANGED
@@ -424,7 +424,7 @@ declare class Browser {
424
424
  switchTab(tabId: string): Promise<void>;
425
425
  closeTab(tabId: string): Promise<void>;
426
426
  /**
427
- * Detect and clear a CAPTCHA. Providers that solve natively are left to do
427
+ x * Detect and clear a CAPTCHA. Providers that solve natively are left to do
428
428
  * it; everything else goes to the configured solver.
429
429
  */
430
430
  solveCaptcha(): Promise<CaptchaResult>;
@@ -699,7 +699,10 @@ declare class Oya {
699
699
  *
700
700
  * Bring your own LLM key (it pays for its own tokens, so no hourly chat quota applies):
701
701
  * await oya.config.set({ llm_provider: 'gemini', openai_api_key: process.env.GEMINI_API_KEY });
702
- * `llm_provider` is 'openai' | 'anthropic' | 'gemini'; `chat_model` overrides its default model.
702
+ * `llm_provider` is 'openai' | 'anthropic' | 'gemini' | 'vertex'; `chat_model` overrides
703
+ * its default model. 'vertex' is Gemini Enterprise (ex-Vertex AI) in express mode, which
704
+ * needs no GCP project; for a project-scoped endpoint, set `openai_base_url` to
705
+ * `.../endpoints/openapi` and pass an OAuth access token as `openai_api_key`.
703
706
  */
704
707
  readonly config: {
705
708
  get: <T = Record<string, unknown>>() => Promise<T>;
package/dist/index.d.ts CHANGED
@@ -424,7 +424,7 @@ declare class Browser {
424
424
  switchTab(tabId: string): Promise<void>;
425
425
  closeTab(tabId: string): Promise<void>;
426
426
  /**
427
- * Detect and clear a CAPTCHA. Providers that solve natively are left to do
427
+ x * Detect and clear a CAPTCHA. Providers that solve natively are left to do
428
428
  * it; everything else goes to the configured solver.
429
429
  */
430
430
  solveCaptcha(): Promise<CaptchaResult>;
@@ -699,7 +699,10 @@ declare class Oya {
699
699
  *
700
700
  * Bring your own LLM key (it pays for its own tokens, so no hourly chat quota applies):
701
701
  * await oya.config.set({ llm_provider: 'gemini', openai_api_key: process.env.GEMINI_API_KEY });
702
- * `llm_provider` is 'openai' | 'anthropic' | 'gemini'; `chat_model` overrides its default model.
702
+ * `llm_provider` is 'openai' | 'anthropic' | 'gemini' | 'vertex'; `chat_model` overrides
703
+ * its default model. 'vertex' is Gemini Enterprise (ex-Vertex AI) in express mode, which
704
+ * needs no GCP project; for a project-scoped endpoint, set `openai_base_url` to
705
+ * `.../endpoints/openapi` and pass an OAuth access token as `openai_api_key`.
703
706
  */
704
707
  readonly config: {
705
708
  get: <T = Record<string, unknown>>() => Promise<T>;
package/dist/index.js CHANGED
@@ -141,7 +141,7 @@ var Browser = class {
141
141
  await this.command("close_tab", { tab_id: tabId });
142
142
  }
143
143
  /**
144
- * Detect and clear a CAPTCHA. Providers that solve natively are left to do
144
+ x * Detect and clear a CAPTCHA. Providers that solve natively are left to do
145
145
  * it; everything else goes to the configured solver.
146
146
  */
147
147
  solveCaptcha() {
@@ -476,7 +476,10 @@ var Oya = class {
476
476
  *
477
477
  * Bring your own LLM key (it pays for its own tokens, so no hourly chat quota applies):
478
478
  * await oya.config.set({ llm_provider: 'gemini', openai_api_key: process.env.GEMINI_API_KEY });
479
- * `llm_provider` is 'openai' | 'anthropic' | 'gemini'; `chat_model` overrides its default model.
479
+ * `llm_provider` is 'openai' | 'anthropic' | 'gemini' | 'vertex'; `chat_model` overrides
480
+ * its default model. 'vertex' is Gemini Enterprise (ex-Vertex AI) in express mode, which
481
+ * needs no GCP project; for a project-scoped endpoint, set `openai_base_url` to
482
+ * `.../endpoints/openapi` and pass an OAuth access token as `openai_api_key`.
480
483
  */
481
484
  config = {
482
485
  get: () => this.http.request("GET", "/api/config"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oya-ai/browser",
3
- "version": "1.0.83",
3
+ "version": "1.0.86",
4
4
  "description": "Rotate thousands of browsers behind one API — personas, proxies, stealth, CAPTCHA and MFA.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://browser.getoya.ai",