@oya-ai/browser 1.0.130 → 1.0.131

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
@@ -122,8 +122,8 @@ try {
122
122
  ...(exists ? { data: { ...data, ...secrets } } : { data, secrets }),
123
123
  onSuccess: () => console.log('Run succeeded.'),
124
124
  onFailure: (error) => console.error('Run failed with status:', error.status),
125
- onHealed: (result) => {
126
- console.log('A repair draft is ready for review:', result.draft);
125
+ onHealed: () => {
126
+ console.log('A step no longer fit the page; the agent finished and fixed the playbook.');
127
127
  },
128
128
  onHumanAttention: async (request) => {
129
129
  console.log('Attention needed:', request.reason);
@@ -201,22 +201,26 @@ await browser.play('job-application', { name: 'Ada Lovelace', resume: await file
201
201
 
202
202
  The generated Playwright module calls `setInputFiles`, where the same variable is a plain path rather than a `file()` value.
203
203
 
204
- ### Review a repaired playbook
204
+ ### When a replay heals
205
205
 
206
- 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.
206
+ Replay normally runs recorded steps without an LLM. With `autoHeal: true` (the default), a broken step hands over to the agent, which finishes the task; its steps replace the broken ones in the playbook, so the next replay runs without the model again. The result says so with `healed: true`.
207
207
 
208
- 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.
208
+ A replay that meets a sign-in page signs in with the profile's stored login and second factor (`oya.personas.setCredentials`, `setMfa`), returns to where the flow was, and carries on.
209
+
210
+ 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.
211
+
212
+ ### Free-text answers
213
+
214
+ A field the agent wrote itself (a comment, the reason for a request, a question's answer), rather than copying it from your prompt, is not replayed word for word. Each replay asks your model for fresh text, from the prompt and that run's data. The playbook lists these fields in `answers`; pass a field's `key` in `data` to type fixed text instead. The Playwright export calls `oya.llm.answer(question, vars)` for them, so run it as `run(page, vars, oya)` with an `Oya` client.
215
+
216
+ ### Move a playbook to another environment
209
217
 
210
218
  ```js
211
- const saved = (await oya.playbooks.list()).find((p) => p.name === playbookName);
212
- if (saved?.draft) {
213
- // Review saved.draft.code before executing it.
214
- await browser.play(`${playbookName}:draft`, { ...data, ...secrets }, { autoHeal: false });
215
- await oya.playbooks.promote(playbookName);
216
- }
219
+ const doc = await oya.playbooks.export('new-request'); // JSON: steps, defaults, secret names (never values)
220
+ await prodOya.playbooks.import(doc, { name: 'new-request', overwrite: false });
217
221
  ```
218
222
 
219
- 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.
223
+ The CLI does the same with `oya playbooks export <name> --out file.json` and `oya playbooks import file.json`.
220
224
 
221
225
  ## Use your own model key
222
226
 
package/dist/index.cjs CHANGED
@@ -510,9 +510,8 @@ var Browser = class {
510
510
  /**
511
511
  * Replay a playbook with no LLM in the loop. Variables left out reuse the
512
512
  * recorded values where there are any. If a step no longer fits the page and
513
- * `autoHeal` is on (the default), the agent finishes the task and its fix is
514
- * saved as a draft (`healed`, `draft`); off, the step's error is thrown.
515
- * Play `'<name>:draft'` to try a draft before promoting it.
513
+ * `autoHeal` is on (the default), the agent finishes the task and its fix
514
+ * replaces the broken steps in the playbook (`healed`); off, the step's error is thrown.
516
515
  */
517
516
  async play(name, data = {}, { autoHeal = true } = {}) {
518
517
  const path = `/api/browsers/${this.id}/playbooks/${segment(name, "name")}/play`;
@@ -738,7 +737,24 @@ var playbookApi = (http) => ({
738
737
  await http().request("DELETE", playbook(name));
739
738
  },
740
739
  /** Replace a playbook with the draft a healed replay saved. Try it first with `browser.play('<name>:draft')`. */
741
- promote: async (name) => http().request("POST", `${playbook(name)}/promote`, {})
740
+ promote: async (name) => http().request("POST", `${playbook(name)}/promote`, {}),
741
+ ...transferApi(http)
742
+ });
743
+ var transferApi = (http) => ({
744
+ /** The playbook as one JSON document, to move it to another environment with `import()`. Secrets travel by name only. */
745
+ export: async (name) => http().request("GET", `${playbook(name)}/export`),
746
+ /**
747
+ * Save an exported playbook here, as `name` or the name it was exported with. A name
748
+ * already taken is refused unless `overwrite` is true.
749
+ */
750
+ import: async (doc, options = {}) => http().request("POST", "/api/playbooks/import", { playbook: doc, ...options })
751
+ });
752
+ var llmApi = (http) => ({
753
+ /**
754
+ * The text for one free-text field, written by this key's model: what an exported
755
+ * playbook calls as `oya.llm.answer(question, vars)` for a comment or a question's answer.
756
+ */
757
+ answer: async (question, values = {}, task) => (await http().request("POST", "/api/playbooks/answer", { question, values, task })).answer
742
758
  });
743
759
 
744
760
  // src/api/proxies.ts
@@ -1042,6 +1058,8 @@ var Oya = class {
1042
1058
  control = controlApi(() => this.http);
1043
1059
  /** Playbooks saved with `browser.toPlaybook()`. */
1044
1060
  playbooks = playbookApi(() => this.http);
1061
+ /** This key's model for one-off text: `oya.llm.answer(question, vars)` fills a playbook's free-text field. */
1062
+ llm = llmApi(() => this.http);
1045
1063
  /**
1046
1064
  * Proxy exits for your personas. A persona takes one at first connect (by its
1047
1065
  * geo hint) or by `personas.pinProxy`, and keeps it.
package/dist/index.d.cts CHANGED
@@ -475,9 +475,36 @@ interface Playbook {
475
475
  defaults: Record<string, string>;
476
476
  /** How many steps it replays. */
477
477
  steps: number;
478
- /** The same flow as a Playwright module: `export default async function run(page, vars)`. */
478
+ /** Free-text fields the model writes fresh on each replay (pass the key in `play()` data to type fixed text). */
479
+ answers?: PlaybookAnswer[];
480
+ /** The same flow as a Playwright module: `export default async function run(page, vars, oya)`. */
479
481
  code: string;
480
482
  }
483
+ /** A free-text field a replay asks the model to fill: a comment, a reason, a question's answer. */
484
+ interface PlaybookAnswer {
485
+ /** The variable that overrides it with fixed text. */
486
+ key: string;
487
+ /** What the field asks, as its label says. */
488
+ question: string;
489
+ }
490
+ /** How `oya.playbooks.import()` saves an export. */
491
+ interface ImportOptions {
492
+ /** The name to save it as; the name it was exported with when left out. */
493
+ name?: string;
494
+ /** Replace a playbook that already has that name. */
495
+ overwrite?: boolean;
496
+ }
497
+ /** A playbook as one JSON document, to import into another Oya environment. */
498
+ interface PlaybookExport {
499
+ /** Always `'oya-playbook'`. */
500
+ format: 'oya-playbook';
501
+ /** The layout's version. */
502
+ version: number;
503
+ /** When it was exported. */
504
+ exportedAt: string;
505
+ /** The playbook: its prompt, steps, defaults and secret names (never secret values). */
506
+ playbook: Record<string, unknown>;
507
+ }
481
508
  /** What a `play()` did. */
482
509
  interface PlayResult {
483
510
  /** Steps replayed before finishing or handing over to the agent. */
@@ -486,9 +513,9 @@ interface PlayResult {
486
513
  total: number;
487
514
  /** A step no longer fit the page and the agent finished the task. */
488
515
  fellBack: boolean;
489
- /** The agent's fix was saved as `draft`; promote it with `oya.playbooks.promote(name)`. */
516
+ /** The agent's fix replaced the broken steps in the playbook, so the next replay runs it. */
490
517
  healed?: boolean;
491
- /** The draft's name, when one was saved. */
518
+ /** The draft's name, from a server that saved fixes as drafts to promote by hand. */
492
519
  draft?: string;
493
520
  /** The agent's summary, when it fell back. */
494
521
  text?: string;
@@ -1381,9 +1408,8 @@ declare class Browser {
1381
1408
  /**
1382
1409
  * Replay a playbook with no LLM in the loop. Variables left out reuse the
1383
1410
  * recorded values where there are any. If a step no longer fits the page and
1384
- * `autoHeal` is on (the default), the agent finishes the task and its fix is
1385
- * saved as a draft (`healed`, `draft`); off, the step's error is thrown.
1386
- * Play `'<name>:draft'` to try a draft before promoting it.
1411
+ * `autoHeal` is on (the default), the agent finishes the task and its fix
1412
+ * replaces the broken steps in the playbook (`healed`); off, the step's error is thrown.
1387
1413
  */
1388
1414
  play(name: string, data?: RunData, { autoHeal }?: PlayOptions): Promise<PlayResult>;
1389
1415
  /**
@@ -1502,10 +1528,16 @@ declare class Oya {
1502
1528
  };
1503
1529
  /** Playbooks saved with `browser.toPlaybook()`. */
1504
1530
  readonly playbooks: {
1531
+ export: (name: string) => Promise<PlaybookExport>;
1532
+ import: (doc: PlaybookExport, options?: ImportOptions) => Promise<Playbook>;
1505
1533
  list: () => Promise<PlaybookSummary[]>;
1506
1534
  remove: (name: string) => Promise<void>;
1507
1535
  promote: (name: string) => Promise<Playbook>;
1508
1536
  };
1537
+ /** This key's model for one-off text: `oya.llm.answer(question, vars)` fills a playbook's free-text field. */
1538
+ readonly llm: {
1539
+ answer: (question: string, values?: Record<string, unknown>, task?: string) => Promise<string>;
1540
+ };
1509
1541
  /**
1510
1542
  * Proxy exits for your personas. A persona takes one at first connect (by its
1511
1543
  * geo hint) or by `personas.pinProxy`, and keeps it.
@@ -1579,4 +1611,4 @@ declare class Oya {
1579
1611
  private waitUntilConnected;
1580
1612
  }
1581
1613
 
1582
- export { type Activity, type Analysis, type AnalyzeOptions, type AttentionRequest, type Block, Browser, type BrowserDetail, type BrowserInfo, type CaptchaResult, type CaptchaSolver, type Config, type ConfigUpdate, type ControlCredential, type ControlEvent, type ControlOverview, type ControlRole, type ControlSession, type Cookie, type CookieFormat, type DesktopOptions, type EcsAuth, type EcsConfig, type Element, type FileValue, type Fingerprint, type Health, type HumanInputAction, type LlmCatalogEntry, type LlmProvider, MAX_FILE_BYTES, type MfaConfig, type MfaResult, Oya, OyaError, type OyaOptions, type PageFormat, type PersonaInfo, type PersonaPrefs, type PlayResult, type Playbook, type PlaybookSummary, type ProjectSettings, type Provider, type ProxyCreate, type ProxyInfo, type RecoverOptions, Run, type RunData, type RunInfo, type RunResult, type SandboxRuntime, type Signup, type SignupOptions, type SiteCredentials, type StartOptions, type StartResult, type StopResult, type SubmitOptions, Oya as default, file };
1614
+ export { type Activity, type Analysis, type AnalyzeOptions, type AttentionRequest, type Block, Browser, type BrowserDetail, type BrowserInfo, type CaptchaResult, type CaptchaSolver, type Config, type ConfigUpdate, type ControlCredential, type ControlEvent, type ControlOverview, type ControlRole, type ControlSession, type Cookie, type CookieFormat, type DesktopOptions, type EcsAuth, type EcsConfig, type Element, type FileValue, type Fingerprint, type Health, type HumanInputAction, type ImportOptions, type LlmCatalogEntry, type LlmProvider, MAX_FILE_BYTES, type MfaConfig, type MfaResult, Oya, OyaError, type OyaOptions, type PageFormat, type PersonaInfo, type PersonaPrefs, type PlayResult, type Playbook, type PlaybookAnswer, type PlaybookExport, type PlaybookSummary, type ProjectSettings, type Provider, type ProxyCreate, type ProxyInfo, type RecoverOptions, Run, type RunData, type RunInfo, type RunResult, type SandboxRuntime, type Signup, type SignupOptions, type SiteCredentials, type StartOptions, type StartResult, type StopResult, type SubmitOptions, Oya as default, file };
package/dist/index.d.ts CHANGED
@@ -475,9 +475,36 @@ interface Playbook {
475
475
  defaults: Record<string, string>;
476
476
  /** How many steps it replays. */
477
477
  steps: number;
478
- /** The same flow as a Playwright module: `export default async function run(page, vars)`. */
478
+ /** Free-text fields the model writes fresh on each replay (pass the key in `play()` data to type fixed text). */
479
+ answers?: PlaybookAnswer[];
480
+ /** The same flow as a Playwright module: `export default async function run(page, vars, oya)`. */
479
481
  code: string;
480
482
  }
483
+ /** A free-text field a replay asks the model to fill: a comment, a reason, a question's answer. */
484
+ interface PlaybookAnswer {
485
+ /** The variable that overrides it with fixed text. */
486
+ key: string;
487
+ /** What the field asks, as its label says. */
488
+ question: string;
489
+ }
490
+ /** How `oya.playbooks.import()` saves an export. */
491
+ interface ImportOptions {
492
+ /** The name to save it as; the name it was exported with when left out. */
493
+ name?: string;
494
+ /** Replace a playbook that already has that name. */
495
+ overwrite?: boolean;
496
+ }
497
+ /** A playbook as one JSON document, to import into another Oya environment. */
498
+ interface PlaybookExport {
499
+ /** Always `'oya-playbook'`. */
500
+ format: 'oya-playbook';
501
+ /** The layout's version. */
502
+ version: number;
503
+ /** When it was exported. */
504
+ exportedAt: string;
505
+ /** The playbook: its prompt, steps, defaults and secret names (never secret values). */
506
+ playbook: Record<string, unknown>;
507
+ }
481
508
  /** What a `play()` did. */
482
509
  interface PlayResult {
483
510
  /** Steps replayed before finishing or handing over to the agent. */
@@ -486,9 +513,9 @@ interface PlayResult {
486
513
  total: number;
487
514
  /** A step no longer fit the page and the agent finished the task. */
488
515
  fellBack: boolean;
489
- /** The agent's fix was saved as `draft`; promote it with `oya.playbooks.promote(name)`. */
516
+ /** The agent's fix replaced the broken steps in the playbook, so the next replay runs it. */
490
517
  healed?: boolean;
491
- /** The draft's name, when one was saved. */
518
+ /** The draft's name, from a server that saved fixes as drafts to promote by hand. */
492
519
  draft?: string;
493
520
  /** The agent's summary, when it fell back. */
494
521
  text?: string;
@@ -1381,9 +1408,8 @@ declare class Browser {
1381
1408
  /**
1382
1409
  * Replay a playbook with no LLM in the loop. Variables left out reuse the
1383
1410
  * recorded values where there are any. If a step no longer fits the page and
1384
- * `autoHeal` is on (the default), the agent finishes the task and its fix is
1385
- * saved as a draft (`healed`, `draft`); off, the step's error is thrown.
1386
- * Play `'<name>:draft'` to try a draft before promoting it.
1411
+ * `autoHeal` is on (the default), the agent finishes the task and its fix
1412
+ * replaces the broken steps in the playbook (`healed`); off, the step's error is thrown.
1387
1413
  */
1388
1414
  play(name: string, data?: RunData, { autoHeal }?: PlayOptions): Promise<PlayResult>;
1389
1415
  /**
@@ -1502,10 +1528,16 @@ declare class Oya {
1502
1528
  };
1503
1529
  /** Playbooks saved with `browser.toPlaybook()`. */
1504
1530
  readonly playbooks: {
1531
+ export: (name: string) => Promise<PlaybookExport>;
1532
+ import: (doc: PlaybookExport, options?: ImportOptions) => Promise<Playbook>;
1505
1533
  list: () => Promise<PlaybookSummary[]>;
1506
1534
  remove: (name: string) => Promise<void>;
1507
1535
  promote: (name: string) => Promise<Playbook>;
1508
1536
  };
1537
+ /** This key's model for one-off text: `oya.llm.answer(question, vars)` fills a playbook's free-text field. */
1538
+ readonly llm: {
1539
+ answer: (question: string, values?: Record<string, unknown>, task?: string) => Promise<string>;
1540
+ };
1509
1541
  /**
1510
1542
  * Proxy exits for your personas. A persona takes one at first connect (by its
1511
1543
  * geo hint) or by `personas.pinProxy`, and keeps it.
@@ -1579,4 +1611,4 @@ declare class Oya {
1579
1611
  private waitUntilConnected;
1580
1612
  }
1581
1613
 
1582
- export { type Activity, type Analysis, type AnalyzeOptions, type AttentionRequest, type Block, Browser, type BrowserDetail, type BrowserInfo, type CaptchaResult, type CaptchaSolver, type Config, type ConfigUpdate, type ControlCredential, type ControlEvent, type ControlOverview, type ControlRole, type ControlSession, type Cookie, type CookieFormat, type DesktopOptions, type EcsAuth, type EcsConfig, type Element, type FileValue, type Fingerprint, type Health, type HumanInputAction, type LlmCatalogEntry, type LlmProvider, MAX_FILE_BYTES, type MfaConfig, type MfaResult, Oya, OyaError, type OyaOptions, type PageFormat, type PersonaInfo, type PersonaPrefs, type PlayResult, type Playbook, type PlaybookSummary, type ProjectSettings, type Provider, type ProxyCreate, type ProxyInfo, type RecoverOptions, Run, type RunData, type RunInfo, type RunResult, type SandboxRuntime, type Signup, type SignupOptions, type SiteCredentials, type StartOptions, type StartResult, type StopResult, type SubmitOptions, Oya as default, file };
1614
+ export { type Activity, type Analysis, type AnalyzeOptions, type AttentionRequest, type Block, Browser, type BrowserDetail, type BrowserInfo, type CaptchaResult, type CaptchaSolver, type Config, type ConfigUpdate, type ControlCredential, type ControlEvent, type ControlOverview, type ControlRole, type ControlSession, type Cookie, type CookieFormat, type DesktopOptions, type EcsAuth, type EcsConfig, type Element, type FileValue, type Fingerprint, type Health, type HumanInputAction, type ImportOptions, type LlmCatalogEntry, type LlmProvider, MAX_FILE_BYTES, type MfaConfig, type MfaResult, Oya, OyaError, type OyaOptions, type PageFormat, type PersonaInfo, type PersonaPrefs, type PlayResult, type Playbook, type PlaybookAnswer, type PlaybookExport, type PlaybookSummary, type ProjectSettings, type Provider, type ProxyCreate, type ProxyInfo, type RecoverOptions, Run, type RunData, type RunInfo, type RunResult, type SandboxRuntime, type Signup, type SignupOptions, type SiteCredentials, type StartOptions, type StartResult, type StopResult, type SubmitOptions, Oya as default, file };
package/dist/index.js CHANGED
@@ -478,9 +478,8 @@ var Browser = class {
478
478
  /**
479
479
  * Replay a playbook with no LLM in the loop. Variables left out reuse the
480
480
  * recorded values where there are any. If a step no longer fits the page and
481
- * `autoHeal` is on (the default), the agent finishes the task and its fix is
482
- * saved as a draft (`healed`, `draft`); off, the step's error is thrown.
483
- * Play `'<name>:draft'` to try a draft before promoting it.
481
+ * `autoHeal` is on (the default), the agent finishes the task and its fix
482
+ * replaces the broken steps in the playbook (`healed`); off, the step's error is thrown.
484
483
  */
485
484
  async play(name, data = {}, { autoHeal = true } = {}) {
486
485
  const path = `/api/browsers/${this.id}/playbooks/${segment(name, "name")}/play`;
@@ -706,7 +705,24 @@ var playbookApi = (http) => ({
706
705
  await http().request("DELETE", playbook(name));
707
706
  },
708
707
  /** Replace a playbook with the draft a healed replay saved. Try it first with `browser.play('<name>:draft')`. */
709
- promote: async (name) => http().request("POST", `${playbook(name)}/promote`, {})
708
+ promote: async (name) => http().request("POST", `${playbook(name)}/promote`, {}),
709
+ ...transferApi(http)
710
+ });
711
+ var transferApi = (http) => ({
712
+ /** The playbook as one JSON document, to move it to another environment with `import()`. Secrets travel by name only. */
713
+ export: async (name) => http().request("GET", `${playbook(name)}/export`),
714
+ /**
715
+ * Save an exported playbook here, as `name` or the name it was exported with. A name
716
+ * already taken is refused unless `overwrite` is true.
717
+ */
718
+ import: async (doc, options = {}) => http().request("POST", "/api/playbooks/import", { playbook: doc, ...options })
719
+ });
720
+ var llmApi = (http) => ({
721
+ /**
722
+ * The text for one free-text field, written by this key's model: what an exported
723
+ * playbook calls as `oya.llm.answer(question, vars)` for a comment or a question's answer.
724
+ */
725
+ answer: async (question, values = {}, task) => (await http().request("POST", "/api/playbooks/answer", { question, values, task })).answer
710
726
  });
711
727
 
712
728
  // src/api/proxies.ts
@@ -1010,6 +1026,8 @@ var Oya = class {
1010
1026
  control = controlApi(() => this.http);
1011
1027
  /** Playbooks saved with `browser.toPlaybook()`. */
1012
1028
  playbooks = playbookApi(() => this.http);
1029
+ /** This key's model for one-off text: `oya.llm.answer(question, vars)` fills a playbook's free-text field. */
1030
+ llm = llmApi(() => this.http);
1013
1031
  /**
1014
1032
  * Proxy exits for your personas. A persona takes one at first connect (by its
1015
1033
  * geo hint) or by `personas.pinProxy`, and keeps it.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oya-ai/browser",
3
- "version": "1.0.130",
3
+ "version": "1.0.131",
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",