@oya-ai/browser 1.0.85 → 1.0.87
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 +58 -1
- package/dist/index.cjs +68 -3
- package/dist/index.d.cts +101 -5
- package/dist/index.d.ts +101 -5
- package/dist/index.js +65 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -150,6 +150,28 @@ Use `{{name}}` references in prompts instead of interpolating values into the pr
|
|
|
150
150
|
|
|
151
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
152
|
|
|
153
|
+
### Files
|
|
154
|
+
|
|
155
|
+
`file()` puts a file in `data`. The agent attaches it with its upload tool: hand it the element id of whatever you can see — the "Choose file" button, the drop zone, the field itself — and the real `<input type="file">` is found from there, including the hidden ones most upload widgets use.
|
|
156
|
+
|
|
157
|
+
```js
|
|
158
|
+
import { Oya, file } from "@oya-ai/browser";
|
|
159
|
+
|
|
160
|
+
await browser.ask("Attach my resume to the application and submit it", {
|
|
161
|
+
data: { name: "Ada Lovelace", resume: await file("./cv.pdf") },
|
|
162
|
+
});
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
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.
|
|
166
|
+
|
|
167
|
+
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:
|
|
168
|
+
|
|
169
|
+
```js
|
|
170
|
+
await browser.play("job-application", { name: "Ada Lovelace", resume: await file("./other.pdf") });
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
The generated Playwright module calls `setInputFiles`, where the same variable is a plain path rather than a `file()` value.
|
|
174
|
+
|
|
153
175
|
### Review a repaired playbook
|
|
154
176
|
|
|
155
177
|
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.
|
|
@@ -179,12 +201,47 @@ const modelKey = process.env.GEMINI_API_KEY;
|
|
|
179
201
|
if (!modelKey) throw new Error("Set GEMINI_API_KEY first.");
|
|
180
202
|
const oya = new Oya();
|
|
181
203
|
await oya.config.set({
|
|
182
|
-
llm_provider: "gemini", // "openai" | "anthropic" | "gemini"
|
|
204
|
+
llm_provider: "gemini", // LlmProvider: "openai" | "anthropic" | "gemini" | "vertex"
|
|
183
205
|
openai_api_key: modelKey, // Shared field name for every supported provider.
|
|
184
206
|
// chat_model: process.env.OYA_CHAT_MODEL, // Optional provider model override.
|
|
185
207
|
});
|
|
186
208
|
```
|
|
187
209
|
|
|
210
|
+
`config.set` takes `ConfigUpdate` and `config.get()` returns `Config`, so an editor offers the valid providers and a typo fails to compile rather than silently falling back:
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
import { Oya, type LlmProvider } from "@oya-ai/browser";
|
|
214
|
+
|
|
215
|
+
await oya.config.set({ llm_provider: "vertx" });
|
|
216
|
+
// ~~~~~~~ Type '"vertx"' is not assignable to type
|
|
217
|
+
// 'LlmProvider'. Did you mean '"vertex"'?
|
|
218
|
+
|
|
219
|
+
const provider: LlmProvider = "vertex"; // for your own config plumbing
|
|
220
|
+
const { effective } = await oya.config.get();
|
|
221
|
+
console.log(effective.baseUrl, effective.model, effective.hasLlmKey);
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
`browser_provider` is typed as `Provider` and `captcha_solver` as `CaptchaSolver` the same way. The server enforces the same sets, so a non-TypeScript caller gets a 400 listing the valid values instead of a silent fallback. Pass `null` to clear a field.
|
|
225
|
+
|
|
226
|
+
### Gemini Enterprise (ex-Vertex AI)
|
|
227
|
+
|
|
228
|
+
`llm_provider: "vertex"` targets express mode, whose API keys work against a global endpoint with no GCP project or location:
|
|
229
|
+
|
|
230
|
+
```js
|
|
231
|
+
await oya.config.set({ llm_provider: "vertex", openai_api_key: process.env.VERTEX_EXPRESS_KEY });
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
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:
|
|
235
|
+
|
|
236
|
+
```js
|
|
237
|
+
await oya.config.set({
|
|
238
|
+
llm_provider: "vertex",
|
|
239
|
+
openai_base_url: "https://us-central1-aiplatform.googleapis.com/v1/projects/PROJECT/locations/us-central1/endpoints/openapi",
|
|
240
|
+
openai_api_key: accessToken, // gcloud auth print-access-token
|
|
241
|
+
chat_model: "google/gemini-2.5-flash", // this endpoint prefixes model ids
|
|
242
|
+
});
|
|
243
|
+
```
|
|
244
|
+
|
|
188
245
|
Omit `chat_model` to use the configured provider's default. `oya.config.get()` reads configuration. To return to the shared model configuration:
|
|
189
246
|
|
|
190
247
|
```js
|
package/dist/index.cjs
CHANGED
|
@@ -21,10 +21,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
Browser: () => Browser,
|
|
24
|
+
MAX_FILE_BYTES: () => MAX_FILE_BYTES,
|
|
24
25
|
Oya: () => Oya,
|
|
25
26
|
OyaError: () => OyaError,
|
|
26
27
|
Run: () => Run,
|
|
27
|
-
default: () => index_default
|
|
28
|
+
default: () => index_default,
|
|
29
|
+
file: () => file
|
|
28
30
|
});
|
|
29
31
|
module.exports = __toCommonJS(index_exports);
|
|
30
32
|
|
|
@@ -376,6 +378,63 @@ var Run = class {
|
|
|
376
378
|
}
|
|
377
379
|
};
|
|
378
380
|
|
|
381
|
+
// src/file.ts
|
|
382
|
+
var MAX_FILE_BYTES = 10 * 1024 * 1024;
|
|
383
|
+
var NODE_FS = "node:fs/promises";
|
|
384
|
+
var MIME = {
|
|
385
|
+
pdf: "application/pdf",
|
|
386
|
+
png: "image/png",
|
|
387
|
+
jpg: "image/jpeg",
|
|
388
|
+
jpeg: "image/jpeg",
|
|
389
|
+
gif: "image/gif",
|
|
390
|
+
webp: "image/webp",
|
|
391
|
+
svg: "image/svg+xml",
|
|
392
|
+
heic: "image/heic",
|
|
393
|
+
txt: "text/plain",
|
|
394
|
+
csv: "text/csv",
|
|
395
|
+
json: "application/json",
|
|
396
|
+
xml: "application/xml",
|
|
397
|
+
html: "text/html",
|
|
398
|
+
doc: "application/msword",
|
|
399
|
+
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
400
|
+
xls: "application/vnd.ms-excel",
|
|
401
|
+
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
402
|
+
ppt: "application/vnd.ms-powerpoint",
|
|
403
|
+
pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
404
|
+
zip: "application/zip"
|
|
405
|
+
};
|
|
406
|
+
var base64 = (bytes) => {
|
|
407
|
+
const buffer = globalThis.Buffer;
|
|
408
|
+
if (buffer) return buffer.from(bytes).toString("base64");
|
|
409
|
+
let binary = "";
|
|
410
|
+
for (let i = 0; i < bytes.length; i += 8192) binary += String.fromCharCode(...bytes.subarray(i, i + 8192));
|
|
411
|
+
return btoa(binary);
|
|
412
|
+
};
|
|
413
|
+
async function file(source, options = {}) {
|
|
414
|
+
let bytes;
|
|
415
|
+
let name = options.name;
|
|
416
|
+
if (typeof source === "string") {
|
|
417
|
+
const fs = await import(
|
|
418
|
+
/* webpackIgnore: true */
|
|
419
|
+
/* @vite-ignore */
|
|
420
|
+
NODE_FS
|
|
421
|
+
);
|
|
422
|
+
bytes = new Uint8Array(await fs.readFile(source));
|
|
423
|
+
name ||= source.split(/[\\/]/).pop() || "file";
|
|
424
|
+
} else if (source instanceof Uint8Array) {
|
|
425
|
+
bytes = source;
|
|
426
|
+
} else {
|
|
427
|
+
bytes = new Uint8Array(await source.arrayBuffer());
|
|
428
|
+
name ||= source.name;
|
|
429
|
+
}
|
|
430
|
+
name ||= "file";
|
|
431
|
+
if (bytes.length > MAX_FILE_BYTES) {
|
|
432
|
+
throw new Error(`${name} is ${Math.round(bytes.length / 1024 / 1024)}MB; the limit for a task file is ${MAX_FILE_BYTES / 1024 / 1024}MB.`);
|
|
433
|
+
}
|
|
434
|
+
const ext = name.includes(".") ? name.split(".").pop().toLowerCase() : "";
|
|
435
|
+
return { file: name, type: options.type || MIME[ext] || "application/octet-stream", b64: base64(bytes) };
|
|
436
|
+
}
|
|
437
|
+
|
|
379
438
|
// src/index.ts
|
|
380
439
|
var DEFAULT_BASE_URL = "https://browser.getoya.ai";
|
|
381
440
|
var READY_POLL_MS = 2e3;
|
|
@@ -506,7 +565,11 @@ var Oya = class {
|
|
|
506
565
|
*
|
|
507
566
|
* Bring your own LLM key (it pays for its own tokens, so no hourly chat quota applies):
|
|
508
567
|
* await oya.config.set({ llm_provider: 'gemini', openai_api_key: process.env.GEMINI_API_KEY });
|
|
509
|
-
* `llm_provider` is
|
|
568
|
+
* `llm_provider` is the {@link LlmProvider} union, so an editor offers the choices and a
|
|
569
|
+
* typo is a compile error; `chat_model` overrides its default model. 'vertex' is Gemini
|
|
570
|
+
* Enterprise (ex-Vertex AI) in express mode, which needs no GCP project; for a
|
|
571
|
+
* project-scoped endpoint, set `openai_base_url` to `.../endpoints/openapi` and pass an
|
|
572
|
+
* OAuth access token as `openai_api_key`. See {@link ConfigUpdate} for every field.
|
|
510
573
|
*/
|
|
511
574
|
config = {
|
|
512
575
|
get: () => this.http.request("GET", "/api/config"),
|
|
@@ -537,7 +600,9 @@ var index_default = Oya;
|
|
|
537
600
|
// Annotate the CommonJS export names for ESM import in node:
|
|
538
601
|
0 && (module.exports = {
|
|
539
602
|
Browser,
|
|
603
|
+
MAX_FILE_BYTES,
|
|
540
604
|
Oya,
|
|
541
605
|
OyaError,
|
|
542
|
-
Run
|
|
606
|
+
Run,
|
|
607
|
+
file
|
|
543
608
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -9,6 +9,60 @@ declare class Http {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
/** Everything the API returns or accepts, in one place. */
|
|
12
|
+
/** Which model drives `ask()` and the chat API. */
|
|
13
|
+
type LlmProvider = 'openai' | 'anthropic'
|
|
14
|
+
/** Gemini via AI Studio. */
|
|
15
|
+
| 'gemini'
|
|
16
|
+
/** Gemini Enterprise, ex-Vertex AI. Express mode by default; set `openai_base_url` to a
|
|
17
|
+
* project-scoped `.../endpoints/openapi` endpoint to use an enterprise project. */
|
|
18
|
+
| 'vertex';
|
|
19
|
+
/** Empty disables solving. */
|
|
20
|
+
type CaptchaSolver = 'capsolver' | '2captcha' | '';
|
|
21
|
+
/**
|
|
22
|
+
* What a key can configure. Mirrors the server's field allowlist exactly: a field not
|
|
23
|
+
* listed here is ignored rather than stored, so the type is the whole surface.
|
|
24
|
+
* `null` clears a field and falls back to the deployment default.
|
|
25
|
+
*/
|
|
26
|
+
interface ConfigUpdate {
|
|
27
|
+
llm_provider?: LlmProvider | null;
|
|
28
|
+
/** The credential for whichever `llm_provider` is set — the field name is shared. */
|
|
29
|
+
openai_api_key?: string | null;
|
|
30
|
+
/** Only honoured alongside this key's own `openai_api_key`. */
|
|
31
|
+
openai_base_url?: string | null;
|
|
32
|
+
/** Overrides the provider's default model. */
|
|
33
|
+
chat_model?: string | null;
|
|
34
|
+
browser_provider?: Provider | null;
|
|
35
|
+
anchor_api_key?: string | null;
|
|
36
|
+
browserbase_api_key?: string | null;
|
|
37
|
+
browserbase_project_id?: string | null;
|
|
38
|
+
steel_api_key?: string | null;
|
|
39
|
+
browseruse_api_key?: string | null;
|
|
40
|
+
cdp_ws_url?: string | null;
|
|
41
|
+
captcha_solver?: CaptchaSolver | null;
|
|
42
|
+
captcha_api_key?: string | null;
|
|
43
|
+
onboarded?: string | null;
|
|
44
|
+
}
|
|
45
|
+
/** What `config.get()` returns. Secrets read back masked, never in full. */
|
|
46
|
+
interface Config extends Omit<ConfigUpdate, 'llm_provider' | 'browser_provider' | 'captcha_solver'> {
|
|
47
|
+
llm_provider?: LlmProvider | '';
|
|
48
|
+
browser_provider?: Provider | '';
|
|
49
|
+
captcha_solver?: CaptchaSolver;
|
|
50
|
+
/** What this key would actually use right now, deployment defaults included. */
|
|
51
|
+
effective: {
|
|
52
|
+
baseUrl: string;
|
|
53
|
+
model: string;
|
|
54
|
+
hasLlmKey: boolean;
|
|
55
|
+
};
|
|
56
|
+
/** True when the LLM key in play belongs to the deployment, not this key. */
|
|
57
|
+
inherited: boolean;
|
|
58
|
+
has_openai_key: boolean;
|
|
59
|
+
providers: Array<{
|
|
60
|
+
id: Provider;
|
|
61
|
+
label: string;
|
|
62
|
+
needs: string[];
|
|
63
|
+
configured: boolean;
|
|
64
|
+
}>;
|
|
65
|
+
}
|
|
12
66
|
/** Where a browser comes from. Configuration, not something a caller must know. */
|
|
13
67
|
type Provider = 'oya-cloud' | 'oya-selfhosted' | 'browseruse' | 'browserbase' | 'steel' | 'anchor' | 'cdp';
|
|
14
68
|
interface StartOptions {
|
|
@@ -83,12 +137,28 @@ interface PlaybookSummary extends Playbook {
|
|
|
83
137
|
healedFrom: number;
|
|
84
138
|
}) | null;
|
|
85
139
|
}
|
|
140
|
+
/**
|
|
141
|
+
* A file attached to a task value. Build it with `file()`, never by hand. Only `data`
|
|
142
|
+
* takes one: a file is not typed through a placeholder, so `secrets` has nothing to hide
|
|
143
|
+
* and rejects it.
|
|
144
|
+
*/
|
|
145
|
+
interface FileValue {
|
|
146
|
+
/** The filename the site sees. */
|
|
147
|
+
file: string;
|
|
148
|
+
/** MIME type, guessed from the extension unless you pass one. */
|
|
149
|
+
type: string;
|
|
150
|
+
/** The bytes, base64. 10MB ceiling. */
|
|
151
|
+
b64: string;
|
|
152
|
+
}
|
|
86
153
|
/**
|
|
87
154
|
* Task values, referred to as `{{name}}` in prompts. As `data` the agent can read them
|
|
88
155
|
* (to split a name or pick the right option); as `secrets` it never sees them. Either
|
|
89
156
|
* way they are typed through placeholders, so playbooks store no values.
|
|
157
|
+
*
|
|
158
|
+
* A {@link FileValue} from `file()` is the exception: the agent attaches it with its
|
|
159
|
+
* upload tool rather than typing it.
|
|
90
160
|
*/
|
|
91
|
-
type RunData = Record<string, string | number>;
|
|
161
|
+
type RunData = Record<string, string | number | FileValue>;
|
|
92
162
|
interface AttentionRequest {
|
|
93
163
|
id: string;
|
|
94
164
|
/** captcha / mfa: finish it in the live view. agent: the agent's question. heal_failed: replay and the agent both gave up. */
|
|
@@ -535,6 +605,28 @@ declare class Run {
|
|
|
535
605
|
private watch;
|
|
536
606
|
}
|
|
537
607
|
|
|
608
|
+
/**
|
|
609
|
+
* Files as task values.
|
|
610
|
+
*
|
|
611
|
+
* await browser.ask('Attach my resume', { data: { resume: await file('./cv.pdf') } });
|
|
612
|
+
*
|
|
613
|
+
* The bytes ride inline in the run request, so the agent's `upload_file` tool can put
|
|
614
|
+
* them into a page's file input. `secrets` cannot hold one — a file is never typed
|
|
615
|
+
* through a placeholder, so there is nothing to hide.
|
|
616
|
+
*/
|
|
617
|
+
|
|
618
|
+
/** Bigger than this and the server would refuse the body anyway. */
|
|
619
|
+
declare const MAX_FILE_BYTES: number;
|
|
620
|
+
/**
|
|
621
|
+
* A file for `data`. A string is a path on disk (Node only); a Blob, a File or raw bytes
|
|
622
|
+
* work anywhere. `name` is what the site sees, and `type` overrides the MIME guessed
|
|
623
|
+
* from the extension.
|
|
624
|
+
*/
|
|
625
|
+
declare function file(source: string | Uint8Array | Blob, options?: {
|
|
626
|
+
name?: string;
|
|
627
|
+
type?: string;
|
|
628
|
+
}): Promise<FileValue>;
|
|
629
|
+
|
|
538
630
|
/**
|
|
539
631
|
* @oya-ai/browser — thousands of browsers, one API.
|
|
540
632
|
*
|
|
@@ -699,11 +791,15 @@ declare class Oya {
|
|
|
699
791
|
*
|
|
700
792
|
* Bring your own LLM key (it pays for its own tokens, so no hourly chat quota applies):
|
|
701
793
|
* await oya.config.set({ llm_provider: 'gemini', openai_api_key: process.env.GEMINI_API_KEY });
|
|
702
|
-
* `llm_provider` is
|
|
794
|
+
* `llm_provider` is the {@link LlmProvider} union, so an editor offers the choices and a
|
|
795
|
+
* typo is a compile error; `chat_model` overrides its default model. 'vertex' is Gemini
|
|
796
|
+
* Enterprise (ex-Vertex AI) in express mode, which needs no GCP project; for a
|
|
797
|
+
* project-scoped endpoint, set `openai_base_url` to `.../endpoints/openapi` and pass an
|
|
798
|
+
* OAuth access token as `openai_api_key`. See {@link ConfigUpdate} for every field.
|
|
703
799
|
*/
|
|
704
800
|
readonly config: {
|
|
705
|
-
get: <T =
|
|
706
|
-
set: <T =
|
|
801
|
+
get: <T = Config>() => Promise<T>;
|
|
802
|
+
set: <T = Config>(values: ConfigUpdate) => Promise<T>;
|
|
707
803
|
};
|
|
708
804
|
/** Saved profiles. `personas` is retained as an alias for existing clients. */
|
|
709
805
|
readonly profiles: {
|
|
@@ -761,4 +857,4 @@ declare class Oya {
|
|
|
761
857
|
private waitUntilConnected;
|
|
762
858
|
}
|
|
763
859
|
|
|
764
|
-
export { type Activity, type Analysis, type AttentionRequest, Browser, type BrowserDetail, type BrowserInfo, type CaptchaResult, type ControlCredential, type ControlEvent, type ControlOverview, type ControlRole, type ControlSession, type Element, type Fingerprint, type Health, type HumanInputAction, type MfaConfig, type MfaResult, Oya, OyaError, type OyaOptions, type PersonaInfo, type PersonaPrefs, type PlayResult, type Playbook, type PlaybookSummary, type ProjectSettings, type Provider, type ProxyCreate, type ProxyInfo, Run, type RunData, type RunInfo, type RunResult, type StartOptions, type StartResult, type StopResult, type SubmitOptions, Oya as default };
|
|
860
|
+
export { type Activity, type Analysis, type AttentionRequest, Browser, type BrowserDetail, type BrowserInfo, type CaptchaResult, type CaptchaSolver, type Config, type ConfigUpdate, type ControlCredential, type ControlEvent, type ControlOverview, type ControlRole, type ControlSession, type Element, type FileValue, type Fingerprint, type Health, type HumanInputAction, type LlmProvider, MAX_FILE_BYTES, type MfaConfig, type MfaResult, Oya, OyaError, type OyaOptions, type PersonaInfo, type PersonaPrefs, type PlayResult, type Playbook, type PlaybookSummary, type ProjectSettings, type Provider, type ProxyCreate, type ProxyInfo, Run, type RunData, type RunInfo, type RunResult, type StartOptions, type StartResult, type StopResult, type SubmitOptions, Oya as default, file };
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,60 @@ declare class Http {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
/** Everything the API returns or accepts, in one place. */
|
|
12
|
+
/** Which model drives `ask()` and the chat API. */
|
|
13
|
+
type LlmProvider = 'openai' | 'anthropic'
|
|
14
|
+
/** Gemini via AI Studio. */
|
|
15
|
+
| 'gemini'
|
|
16
|
+
/** Gemini Enterprise, ex-Vertex AI. Express mode by default; set `openai_base_url` to a
|
|
17
|
+
* project-scoped `.../endpoints/openapi` endpoint to use an enterprise project. */
|
|
18
|
+
| 'vertex';
|
|
19
|
+
/** Empty disables solving. */
|
|
20
|
+
type CaptchaSolver = 'capsolver' | '2captcha' | '';
|
|
21
|
+
/**
|
|
22
|
+
* What a key can configure. Mirrors the server's field allowlist exactly: a field not
|
|
23
|
+
* listed here is ignored rather than stored, so the type is the whole surface.
|
|
24
|
+
* `null` clears a field and falls back to the deployment default.
|
|
25
|
+
*/
|
|
26
|
+
interface ConfigUpdate {
|
|
27
|
+
llm_provider?: LlmProvider | null;
|
|
28
|
+
/** The credential for whichever `llm_provider` is set — the field name is shared. */
|
|
29
|
+
openai_api_key?: string | null;
|
|
30
|
+
/** Only honoured alongside this key's own `openai_api_key`. */
|
|
31
|
+
openai_base_url?: string | null;
|
|
32
|
+
/** Overrides the provider's default model. */
|
|
33
|
+
chat_model?: string | null;
|
|
34
|
+
browser_provider?: Provider | null;
|
|
35
|
+
anchor_api_key?: string | null;
|
|
36
|
+
browserbase_api_key?: string | null;
|
|
37
|
+
browserbase_project_id?: string | null;
|
|
38
|
+
steel_api_key?: string | null;
|
|
39
|
+
browseruse_api_key?: string | null;
|
|
40
|
+
cdp_ws_url?: string | null;
|
|
41
|
+
captcha_solver?: CaptchaSolver | null;
|
|
42
|
+
captcha_api_key?: string | null;
|
|
43
|
+
onboarded?: string | null;
|
|
44
|
+
}
|
|
45
|
+
/** What `config.get()` returns. Secrets read back masked, never in full. */
|
|
46
|
+
interface Config extends Omit<ConfigUpdate, 'llm_provider' | 'browser_provider' | 'captcha_solver'> {
|
|
47
|
+
llm_provider?: LlmProvider | '';
|
|
48
|
+
browser_provider?: Provider | '';
|
|
49
|
+
captcha_solver?: CaptchaSolver;
|
|
50
|
+
/** What this key would actually use right now, deployment defaults included. */
|
|
51
|
+
effective: {
|
|
52
|
+
baseUrl: string;
|
|
53
|
+
model: string;
|
|
54
|
+
hasLlmKey: boolean;
|
|
55
|
+
};
|
|
56
|
+
/** True when the LLM key in play belongs to the deployment, not this key. */
|
|
57
|
+
inherited: boolean;
|
|
58
|
+
has_openai_key: boolean;
|
|
59
|
+
providers: Array<{
|
|
60
|
+
id: Provider;
|
|
61
|
+
label: string;
|
|
62
|
+
needs: string[];
|
|
63
|
+
configured: boolean;
|
|
64
|
+
}>;
|
|
65
|
+
}
|
|
12
66
|
/** Where a browser comes from. Configuration, not something a caller must know. */
|
|
13
67
|
type Provider = 'oya-cloud' | 'oya-selfhosted' | 'browseruse' | 'browserbase' | 'steel' | 'anchor' | 'cdp';
|
|
14
68
|
interface StartOptions {
|
|
@@ -83,12 +137,28 @@ interface PlaybookSummary extends Playbook {
|
|
|
83
137
|
healedFrom: number;
|
|
84
138
|
}) | null;
|
|
85
139
|
}
|
|
140
|
+
/**
|
|
141
|
+
* A file attached to a task value. Build it with `file()`, never by hand. Only `data`
|
|
142
|
+
* takes one: a file is not typed through a placeholder, so `secrets` has nothing to hide
|
|
143
|
+
* and rejects it.
|
|
144
|
+
*/
|
|
145
|
+
interface FileValue {
|
|
146
|
+
/** The filename the site sees. */
|
|
147
|
+
file: string;
|
|
148
|
+
/** MIME type, guessed from the extension unless you pass one. */
|
|
149
|
+
type: string;
|
|
150
|
+
/** The bytes, base64. 10MB ceiling. */
|
|
151
|
+
b64: string;
|
|
152
|
+
}
|
|
86
153
|
/**
|
|
87
154
|
* Task values, referred to as `{{name}}` in prompts. As `data` the agent can read them
|
|
88
155
|
* (to split a name or pick the right option); as `secrets` it never sees them. Either
|
|
89
156
|
* way they are typed through placeholders, so playbooks store no values.
|
|
157
|
+
*
|
|
158
|
+
* A {@link FileValue} from `file()` is the exception: the agent attaches it with its
|
|
159
|
+
* upload tool rather than typing it.
|
|
90
160
|
*/
|
|
91
|
-
type RunData = Record<string, string | number>;
|
|
161
|
+
type RunData = Record<string, string | number | FileValue>;
|
|
92
162
|
interface AttentionRequest {
|
|
93
163
|
id: string;
|
|
94
164
|
/** captcha / mfa: finish it in the live view. agent: the agent's question. heal_failed: replay and the agent both gave up. */
|
|
@@ -535,6 +605,28 @@ declare class Run {
|
|
|
535
605
|
private watch;
|
|
536
606
|
}
|
|
537
607
|
|
|
608
|
+
/**
|
|
609
|
+
* Files as task values.
|
|
610
|
+
*
|
|
611
|
+
* await browser.ask('Attach my resume', { data: { resume: await file('./cv.pdf') } });
|
|
612
|
+
*
|
|
613
|
+
* The bytes ride inline in the run request, so the agent's `upload_file` tool can put
|
|
614
|
+
* them into a page's file input. `secrets` cannot hold one — a file is never typed
|
|
615
|
+
* through a placeholder, so there is nothing to hide.
|
|
616
|
+
*/
|
|
617
|
+
|
|
618
|
+
/** Bigger than this and the server would refuse the body anyway. */
|
|
619
|
+
declare const MAX_FILE_BYTES: number;
|
|
620
|
+
/**
|
|
621
|
+
* A file for `data`. A string is a path on disk (Node only); a Blob, a File or raw bytes
|
|
622
|
+
* work anywhere. `name` is what the site sees, and `type` overrides the MIME guessed
|
|
623
|
+
* from the extension.
|
|
624
|
+
*/
|
|
625
|
+
declare function file(source: string | Uint8Array | Blob, options?: {
|
|
626
|
+
name?: string;
|
|
627
|
+
type?: string;
|
|
628
|
+
}): Promise<FileValue>;
|
|
629
|
+
|
|
538
630
|
/**
|
|
539
631
|
* @oya-ai/browser — thousands of browsers, one API.
|
|
540
632
|
*
|
|
@@ -699,11 +791,15 @@ declare class Oya {
|
|
|
699
791
|
*
|
|
700
792
|
* Bring your own LLM key (it pays for its own tokens, so no hourly chat quota applies):
|
|
701
793
|
* await oya.config.set({ llm_provider: 'gemini', openai_api_key: process.env.GEMINI_API_KEY });
|
|
702
|
-
* `llm_provider` is
|
|
794
|
+
* `llm_provider` is the {@link LlmProvider} union, so an editor offers the choices and a
|
|
795
|
+
* typo is a compile error; `chat_model` overrides its default model. 'vertex' is Gemini
|
|
796
|
+
* Enterprise (ex-Vertex AI) in express mode, which needs no GCP project; for a
|
|
797
|
+
* project-scoped endpoint, set `openai_base_url` to `.../endpoints/openapi` and pass an
|
|
798
|
+
* OAuth access token as `openai_api_key`. See {@link ConfigUpdate} for every field.
|
|
703
799
|
*/
|
|
704
800
|
readonly config: {
|
|
705
|
-
get: <T =
|
|
706
|
-
set: <T =
|
|
801
|
+
get: <T = Config>() => Promise<T>;
|
|
802
|
+
set: <T = Config>(values: ConfigUpdate) => Promise<T>;
|
|
707
803
|
};
|
|
708
804
|
/** Saved profiles. `personas` is retained as an alias for existing clients. */
|
|
709
805
|
readonly profiles: {
|
|
@@ -761,4 +857,4 @@ declare class Oya {
|
|
|
761
857
|
private waitUntilConnected;
|
|
762
858
|
}
|
|
763
859
|
|
|
764
|
-
export { type Activity, type Analysis, type AttentionRequest, Browser, type BrowserDetail, type BrowserInfo, type CaptchaResult, type ControlCredential, type ControlEvent, type ControlOverview, type ControlRole, type ControlSession, type Element, type Fingerprint, type Health, type HumanInputAction, type MfaConfig, type MfaResult, Oya, OyaError, type OyaOptions, type PersonaInfo, type PersonaPrefs, type PlayResult, type Playbook, type PlaybookSummary, type ProjectSettings, type Provider, type ProxyCreate, type ProxyInfo, Run, type RunData, type RunInfo, type RunResult, type StartOptions, type StartResult, type StopResult, type SubmitOptions, Oya as default };
|
|
860
|
+
export { type Activity, type Analysis, type AttentionRequest, Browser, type BrowserDetail, type BrowserInfo, type CaptchaResult, type CaptchaSolver, type Config, type ConfigUpdate, type ControlCredential, type ControlEvent, type ControlOverview, type ControlRole, type ControlSession, type Element, type FileValue, type Fingerprint, type Health, type HumanInputAction, type LlmProvider, MAX_FILE_BYTES, type MfaConfig, type MfaResult, Oya, OyaError, type OyaOptions, type PersonaInfo, type PersonaPrefs, type PlayResult, type Playbook, type PlaybookSummary, type ProjectSettings, type Provider, type ProxyCreate, type ProxyInfo, Run, type RunData, type RunInfo, type RunResult, type StartOptions, type StartResult, type StopResult, type SubmitOptions, Oya as default, file };
|
package/dist/index.js
CHANGED
|
@@ -346,6 +346,63 @@ var Run = class {
|
|
|
346
346
|
}
|
|
347
347
|
};
|
|
348
348
|
|
|
349
|
+
// src/file.ts
|
|
350
|
+
var MAX_FILE_BYTES = 10 * 1024 * 1024;
|
|
351
|
+
var NODE_FS = "node:fs/promises";
|
|
352
|
+
var MIME = {
|
|
353
|
+
pdf: "application/pdf",
|
|
354
|
+
png: "image/png",
|
|
355
|
+
jpg: "image/jpeg",
|
|
356
|
+
jpeg: "image/jpeg",
|
|
357
|
+
gif: "image/gif",
|
|
358
|
+
webp: "image/webp",
|
|
359
|
+
svg: "image/svg+xml",
|
|
360
|
+
heic: "image/heic",
|
|
361
|
+
txt: "text/plain",
|
|
362
|
+
csv: "text/csv",
|
|
363
|
+
json: "application/json",
|
|
364
|
+
xml: "application/xml",
|
|
365
|
+
html: "text/html",
|
|
366
|
+
doc: "application/msword",
|
|
367
|
+
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
368
|
+
xls: "application/vnd.ms-excel",
|
|
369
|
+
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
370
|
+
ppt: "application/vnd.ms-powerpoint",
|
|
371
|
+
pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
372
|
+
zip: "application/zip"
|
|
373
|
+
};
|
|
374
|
+
var base64 = (bytes) => {
|
|
375
|
+
const buffer = globalThis.Buffer;
|
|
376
|
+
if (buffer) return buffer.from(bytes).toString("base64");
|
|
377
|
+
let binary = "";
|
|
378
|
+
for (let i = 0; i < bytes.length; i += 8192) binary += String.fromCharCode(...bytes.subarray(i, i + 8192));
|
|
379
|
+
return btoa(binary);
|
|
380
|
+
};
|
|
381
|
+
async function file(source, options = {}) {
|
|
382
|
+
let bytes;
|
|
383
|
+
let name = options.name;
|
|
384
|
+
if (typeof source === "string") {
|
|
385
|
+
const fs = await import(
|
|
386
|
+
/* webpackIgnore: true */
|
|
387
|
+
/* @vite-ignore */
|
|
388
|
+
NODE_FS
|
|
389
|
+
);
|
|
390
|
+
bytes = new Uint8Array(await fs.readFile(source));
|
|
391
|
+
name ||= source.split(/[\\/]/).pop() || "file";
|
|
392
|
+
} else if (source instanceof Uint8Array) {
|
|
393
|
+
bytes = source;
|
|
394
|
+
} else {
|
|
395
|
+
bytes = new Uint8Array(await source.arrayBuffer());
|
|
396
|
+
name ||= source.name;
|
|
397
|
+
}
|
|
398
|
+
name ||= "file";
|
|
399
|
+
if (bytes.length > MAX_FILE_BYTES) {
|
|
400
|
+
throw new Error(`${name} is ${Math.round(bytes.length / 1024 / 1024)}MB; the limit for a task file is ${MAX_FILE_BYTES / 1024 / 1024}MB.`);
|
|
401
|
+
}
|
|
402
|
+
const ext = name.includes(".") ? name.split(".").pop().toLowerCase() : "";
|
|
403
|
+
return { file: name, type: options.type || MIME[ext] || "application/octet-stream", b64: base64(bytes) };
|
|
404
|
+
}
|
|
405
|
+
|
|
349
406
|
// src/index.ts
|
|
350
407
|
var DEFAULT_BASE_URL = "https://browser.getoya.ai";
|
|
351
408
|
var READY_POLL_MS = 2e3;
|
|
@@ -476,7 +533,11 @@ var Oya = class {
|
|
|
476
533
|
*
|
|
477
534
|
* Bring your own LLM key (it pays for its own tokens, so no hourly chat quota applies):
|
|
478
535
|
* await oya.config.set({ llm_provider: 'gemini', openai_api_key: process.env.GEMINI_API_KEY });
|
|
479
|
-
* `llm_provider` is
|
|
536
|
+
* `llm_provider` is the {@link LlmProvider} union, so an editor offers the choices and a
|
|
537
|
+
* typo is a compile error; `chat_model` overrides its default model. 'vertex' is Gemini
|
|
538
|
+
* Enterprise (ex-Vertex AI) in express mode, which needs no GCP project; for a
|
|
539
|
+
* project-scoped endpoint, set `openai_base_url` to `.../endpoints/openapi` and pass an
|
|
540
|
+
* OAuth access token as `openai_api_key`. See {@link ConfigUpdate} for every field.
|
|
480
541
|
*/
|
|
481
542
|
config = {
|
|
482
543
|
get: () => this.http.request("GET", "/api/config"),
|
|
@@ -506,8 +567,10 @@ var Oya = class {
|
|
|
506
567
|
var index_default = Oya;
|
|
507
568
|
export {
|
|
508
569
|
Browser,
|
|
570
|
+
MAX_FILE_BYTES,
|
|
509
571
|
Oya,
|
|
510
572
|
OyaError,
|
|
511
573
|
Run,
|
|
512
|
-
index_default as default
|
|
574
|
+
index_default as default,
|
|
575
|
+
file
|
|
513
576
|
};
|
package/package.json
CHANGED