@orkestrel/browser 0.0.21 → 0.0.22
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 +6 -3
- package/dist/src/browser/index.d.ts +77 -19
- package/dist/src/browser/index.js +361 -21
- package/dist/src/browser/index.js.map +1 -1
- package/dist/src/core/index.cjs +953 -172
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +354 -78
- package/dist/src/core/index.d.ts +354 -78
- package/dist/src/core/index.js +947 -174
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +36 -11
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +2 -2
- package/dist/src/server/index.d.ts +2 -2
- package/dist/src/server/index.js +36 -11
- package/dist/src/server/index.js.map +1 -1
- package/package.json +3 -3
package/dist/src/core/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { attempt, cloneJSONRecord, instanceOf, isArray, isBoolean, isError, isFiniteNumber, isFunction, isInstance, isInteger, isJSONValue, isNumber, isRecord, isString, parseArray, parseEnum, parseJSON, parseJSONAs } from "@orkestrel/contract";
|
|
2
2
|
import { Emitter, extractKeys } from "@orkestrel/emitter";
|
|
3
|
-
import { createHTML, renderText } from "@orkestrel/html";
|
|
3
|
+
import { createHTML, renderText, resolveAttributes } from "@orkestrel/html";
|
|
4
4
|
import { htmlToMarkdown, renderMarkdown } from "@orkestrel/markdown";
|
|
5
5
|
import { createTool, createToolManager } from "@orkestrel/tool";
|
|
6
6
|
//#region src/core/constants.ts
|
|
@@ -127,6 +127,16 @@ var BROWSER_SNAPSHOT_NODE_LIMIT = 1e5;
|
|
|
127
127
|
var BROWSER_FRAME_WORLD_NAME = "__orkestrelBrowserFrame";
|
|
128
128
|
/** Sets the number of animation frames whose element bounds must agree before trusted input. */
|
|
129
129
|
var BROWSER_STABLE_FRAME_COUNT = 2;
|
|
130
|
+
/**
|
|
131
|
+
* Names the finished transitions and animations that wake a parked wait.
|
|
132
|
+
*
|
|
133
|
+
* @remarks
|
|
134
|
+
* Chromium 154.0.4258.53 delivers these events in main and isolated worlds after a
|
|
135
|
+
* visibility or discrete display exit. Opacity alone leaves text in `innerText`.
|
|
136
|
+
*/
|
|
137
|
+
var BROWSER_WAIT_EVENTS = Object.freeze(["transitionend", "animationend"]);
|
|
138
|
+
/** Matches protocol errors caused by replacement or closure of an execution context. */
|
|
139
|
+
var BROWSER_CONTEXT_LOSS_PATTERN = /execution context was destroyed|cannot find context with specified id|inspected target navigated or closed/i;
|
|
130
140
|
/** Maps a canonical modifier name to its CDP Input modifier bit value. */
|
|
131
141
|
var BROWSER_KEY_MODIFIERS = Object.freeze({
|
|
132
142
|
Alt: 1,
|
|
@@ -152,7 +162,7 @@ var BROWSER_MOUSE_BUTTON_MASKS = Object.freeze({
|
|
|
152
162
|
*/
|
|
153
163
|
var BROWSER_HAR_CREATOR = Object.freeze({
|
|
154
164
|
name: "@orkestrel/browser",
|
|
155
|
-
version: "0.0.
|
|
165
|
+
version: "0.0.22"
|
|
156
166
|
});
|
|
157
167
|
/** Names the attribute that tags temporary screenshot styles and masks. */
|
|
158
168
|
var BROWSER_SCREENSHOT_ATTRIBUTE = "data-orkestrel-screenshot";
|
|
@@ -262,6 +272,15 @@ var BROWSER_REGISTRY_OUTPUT_LIMIT = 4096;
|
|
|
262
272
|
var BROWSER_REFERENCE_PREFIX = "e";
|
|
263
273
|
/** Bounds the default number of actionable elements in an outline. */
|
|
264
274
|
var BROWSER_OUTLINE_LIMIT = 150;
|
|
275
|
+
/**
|
|
276
|
+
* Matches one word of an outline search and of a row's role and name: a run of at least 3 letters
|
|
277
|
+
* or digits.
|
|
278
|
+
*
|
|
279
|
+
* @remarks
|
|
280
|
+
* The 3-character floor drops words such as `a`, `to`, and `of`, which would otherwise score a row
|
|
281
|
+
* whose name shares them with the search.
|
|
282
|
+
*/
|
|
283
|
+
var BROWSER_SEARCH_PATTERN = /[\p{L}\p{N}]{3,}/gu;
|
|
265
284
|
/** Names accessibility roles that receive actionable outline references. */
|
|
266
285
|
var BROWSER_INTERACTIVE_ROLES = Object.freeze(/* @__PURE__ */ new Set([
|
|
267
286
|
"button",
|
|
@@ -383,6 +402,7 @@ var BROWSER_TOOL_PENDING_NOTE = "An earlier input is still pending; call look.";
|
|
|
383
402
|
var BROWSER_OBSERVATION_TOOL_NAMES = Object.freeze([
|
|
384
403
|
"look",
|
|
385
404
|
"read",
|
|
405
|
+
"plain",
|
|
386
406
|
"tabs"
|
|
387
407
|
]);
|
|
388
408
|
/**
|
|
@@ -392,15 +412,15 @@ var BROWSER_OBSERVATION_TOOL_NAMES = Object.freeze([
|
|
|
392
412
|
*/
|
|
393
413
|
var BROWSER_TOOL_CHANGED_NOTE = "(The page changed before the view could be read; call look.)";
|
|
394
414
|
/**
|
|
395
|
-
* Holds the clause that ends the footer of a cut result
|
|
396
|
-
*
|
|
415
|
+
* Holds the clause that ends the footer of a cut result other than an action or `dialog` receipt
|
|
416
|
+
* that carries a view, including `look` and `read` results.
|
|
397
417
|
*/
|
|
398
418
|
var BROWSER_TOOL_CUT_FOOTER = "the rest was cut";
|
|
399
419
|
/**
|
|
400
|
-
* Holds the clause that ends the footer of a cut
|
|
401
|
-
*
|
|
420
|
+
* Holds the clause that ends the footer of a cut action or `dialog` receipt that carries a view,
|
|
421
|
+
* and names `look` as the call that finds an element the cut view leaves out.
|
|
402
422
|
*/
|
|
403
|
-
var BROWSER_TOOL_VIEW_FOOTER = "the rest was cut; call
|
|
423
|
+
var BROWSER_TOOL_VIEW_FOOTER = "the rest was cut; call look with words to find";
|
|
404
424
|
/**
|
|
405
425
|
* Names the accessibility roles the `type` tool writes to: `textbox`, `searchbox`, and
|
|
406
426
|
* `spinbutton` take typed text, and `combobox` and `listbox` take a select control's option
|
|
@@ -422,12 +442,13 @@ var BROWSER_TYPED_ROLES = Object.freeze(/* @__PURE__ */ new Set([
|
|
|
422
442
|
/** Caps the `wait` tool's `timeout` parameter at `30_000` milliseconds. */
|
|
423
443
|
var BROWSER_TOOL_TIMEOUT_LIMIT_MS = 3e4;
|
|
424
444
|
/**
|
|
425
|
-
* Names every tool the browser toolset reserves: `look`, `read`, `click`, `type`, `press`,
|
|
445
|
+
* Names every tool the browser toolset reserves: `look`, `read`, `plain`, `click`, `type`, `press`,
|
|
426
446
|
* `navigate`, `wait`, `dialog`, `tabs`, and `switch`.
|
|
427
447
|
*/
|
|
428
448
|
var BROWSER_TOOL_NAMES = Object.freeze([
|
|
429
449
|
"look",
|
|
430
450
|
"read",
|
|
451
|
+
"plain",
|
|
431
452
|
"click",
|
|
432
453
|
"type",
|
|
433
454
|
"press",
|
|
@@ -458,14 +479,15 @@ var BROWSER_SCHEMES = Object.freeze(["http:", "https:"]);
|
|
|
458
479
|
*
|
|
459
480
|
* @remarks
|
|
460
481
|
* Every tool description is at most 25 words and says what the tool shows or does, and for
|
|
461
|
-
* `look` and `
|
|
482
|
+
* `look`, `read`, and `plain` when to call it; every parameter description is at most 100 characters.
|
|
462
483
|
* Every tool declares at least one required parameter, because the streamed tool-call parser of
|
|
463
|
-
* Ollama 0.34.4 rejects a call to a tool that declares no parameter. `look`
|
|
464
|
-
*
|
|
465
|
-
*
|
|
484
|
+
* Ollama 0.34.4 rejects a call to a tool that declares no parameter. `look`, `read`, and `plain` take
|
|
485
|
+
* `search` and `offset`; an element's own reading is `BrowserElementInterface.read`.
|
|
486
|
+
* `look`, `read`, `plain`, and `journeys` annotate `pure` and
|
|
466
487
|
* `untrusted`, `wait` and `tabs` annotate `pure`, and the rest carry no annotation. `type` takes
|
|
467
488
|
* `secret` beside `ref`, `text`, and `submit`. The journey tools `record`, `save`, `journeys`,
|
|
468
489
|
* `edit`, and `replay` are advertised only by a toolset constructed with `journeys`.
|
|
490
|
+
* `wait` takes `absent` beside `text` and `timeout`.
|
|
469
491
|
*/
|
|
470
492
|
var BROWSER_TOOL_COPY = Object.freeze({
|
|
471
493
|
look: Object.freeze({
|
|
@@ -473,11 +495,17 @@ var BROWSER_TOOL_COPY = Object.freeze({
|
|
|
473
495
|
description: "Shows the page's text and the elements you can act on, each with a reference like e4. Call it first and after the page changes.",
|
|
474
496
|
parameters: Object.freeze({
|
|
475
497
|
type: "object",
|
|
476
|
-
properties: Object.freeze({
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
498
|
+
properties: Object.freeze({
|
|
499
|
+
search: Object.freeze({
|
|
500
|
+
type: "string",
|
|
501
|
+
description: "Words to find on this page; matching elements come first."
|
|
502
|
+
}),
|
|
503
|
+
offset: Object.freeze({
|
|
504
|
+
type: "integer",
|
|
505
|
+
description: "The character to continue from, as the last reply names. Default: 0."
|
|
506
|
+
})
|
|
507
|
+
}),
|
|
508
|
+
required: Object.freeze(["search"])
|
|
481
509
|
}),
|
|
482
510
|
annotations: Object.freeze({
|
|
483
511
|
pure: true,
|
|
@@ -486,20 +514,42 @@ var BROWSER_TOOL_COPY = Object.freeze({
|
|
|
486
514
|
}),
|
|
487
515
|
read: Object.freeze({
|
|
488
516
|
name: "read",
|
|
489
|
-
description: "Reads the page
|
|
517
|
+
description: "Reads the page as Markdown, with headings, tables, and link addresses. Call it to learn a fact; continue with the offset a cut result names.",
|
|
518
|
+
parameters: Object.freeze({
|
|
519
|
+
type: "object",
|
|
520
|
+
properties: Object.freeze({
|
|
521
|
+
search: Object.freeze({
|
|
522
|
+
type: "string",
|
|
523
|
+
description: "Words to find on this page; the lines that share them come first."
|
|
524
|
+
}),
|
|
525
|
+
offset: Object.freeze({
|
|
526
|
+
type: "integer",
|
|
527
|
+
description: "The character to continue from, as the last reply names. Default: 0."
|
|
528
|
+
})
|
|
529
|
+
}),
|
|
530
|
+
required: Object.freeze(["search"])
|
|
531
|
+
}),
|
|
532
|
+
annotations: Object.freeze({
|
|
533
|
+
pure: true,
|
|
534
|
+
untrusted: true
|
|
535
|
+
})
|
|
536
|
+
}),
|
|
537
|
+
plain: Object.freeze({
|
|
538
|
+
name: "plain",
|
|
539
|
+
description: "Reads the page as plain text, without Markdown, link addresses, or image text. Call it for words to pass to wait or type.",
|
|
490
540
|
parameters: Object.freeze({
|
|
491
541
|
type: "object",
|
|
492
542
|
properties: Object.freeze({
|
|
493
|
-
|
|
543
|
+
search: Object.freeze({
|
|
494
544
|
type: "string",
|
|
495
|
-
description: "
|
|
545
|
+
description: "Words to find on this page; the lines that share them come first."
|
|
496
546
|
}),
|
|
497
547
|
offset: Object.freeze({
|
|
498
548
|
type: "integer",
|
|
499
549
|
description: "The character to continue from, as the last reply names. Default: 0."
|
|
500
550
|
})
|
|
501
551
|
}),
|
|
502
|
-
required: Object.freeze(["
|
|
552
|
+
required: Object.freeze(["search"])
|
|
503
553
|
}),
|
|
504
554
|
annotations: Object.freeze({
|
|
505
555
|
pure: true,
|
|
@@ -570,7 +620,7 @@ var BROWSER_TOOL_COPY = Object.freeze({
|
|
|
570
620
|
}),
|
|
571
621
|
wait: Object.freeze({
|
|
572
622
|
name: "wait",
|
|
573
|
-
description: "Waits for
|
|
623
|
+
description: "Waits for text to appear; set absent to true to wait for it to leave.",
|
|
574
624
|
parameters: Object.freeze({
|
|
575
625
|
type: "object",
|
|
576
626
|
properties: Object.freeze({
|
|
@@ -578,6 +628,10 @@ var BROWSER_TOOL_COPY = Object.freeze({
|
|
|
578
628
|
type: "string",
|
|
579
629
|
description: "The text to wait for."
|
|
580
630
|
}),
|
|
631
|
+
absent: Object.freeze({
|
|
632
|
+
type: "boolean",
|
|
633
|
+
description: "True to wait for the text to leave the page."
|
|
634
|
+
}),
|
|
581
635
|
timeout: Object.freeze({
|
|
582
636
|
type: "integer",
|
|
583
637
|
description: "The most seconds to wait, at most 30. Default: 5."
|
|
@@ -610,11 +664,11 @@ var BROWSER_TOOL_COPY = Object.freeze({
|
|
|
610
664
|
description: "Lists the open tabs; the current one is marked.",
|
|
611
665
|
parameters: Object.freeze({
|
|
612
666
|
type: "object",
|
|
613
|
-
properties: Object.freeze({
|
|
667
|
+
properties: Object.freeze({ search: Object.freeze({
|
|
614
668
|
type: "string",
|
|
615
|
-
description: "
|
|
669
|
+
description: "Words to find; matching tabs come first."
|
|
616
670
|
}) }),
|
|
617
|
-
required: Object.freeze(["
|
|
671
|
+
required: Object.freeze(["search"])
|
|
618
672
|
}),
|
|
619
673
|
annotations: Object.freeze({ pure: true })
|
|
620
674
|
}),
|
|
@@ -660,16 +714,16 @@ var BROWSER_TOOL_COPY = Object.freeze({
|
|
|
660
714
|
parameters: Object.freeze({
|
|
661
715
|
type: "object",
|
|
662
716
|
properties: Object.freeze({
|
|
663
|
-
|
|
717
|
+
search: Object.freeze({
|
|
664
718
|
type: "string",
|
|
665
|
-
description: "
|
|
719
|
+
description: "Words to find; matching journeys come first."
|
|
666
720
|
}),
|
|
667
721
|
offset: Object.freeze({
|
|
668
722
|
type: "integer",
|
|
669
723
|
description: "The character to continue from, as the last reply names. Default: 0."
|
|
670
724
|
})
|
|
671
725
|
}),
|
|
672
|
-
required: Object.freeze(["
|
|
726
|
+
required: Object.freeze(["search"])
|
|
673
727
|
}),
|
|
674
728
|
annotations: Object.freeze({
|
|
675
729
|
pure: true,
|
|
@@ -1626,14 +1680,252 @@ function filterBrowserOutline(nodes, query) {
|
|
|
1626
1680
|
return nodes.filter((node) => !node.ignored && !BROWSER_OUTLINE_OMITTED_ROLES.has(node.role ?? "") && (query.role === void 0 || node.role === query.role) && (query.name === void 0 || (query.exact === true ? normalizeBrowserName(node.name ?? "") === normalizeBrowserName(query.name) : normalizeBrowserName(node.name ?? "").toLowerCase().includes(normalizeBrowserName(query.name).toLowerCase()))));
|
|
1627
1681
|
}
|
|
1628
1682
|
/**
|
|
1683
|
+
* Renders one outline row: the node's reference, its role, its quoted accessible name, and the
|
|
1684
|
+
* states it carries.
|
|
1685
|
+
*
|
|
1686
|
+
* @remarks
|
|
1687
|
+
* After the name the row appends `value="V"` when the node carries a non-empty value,
|
|
1688
|
+
* `pressed=true|false|mixed`, `expanded=true|false`, and `selected=true|false` when present,
|
|
1689
|
+
* `[checked]` when checked, `[disabled]` when disabled, and `[tool=NAME]` for a page tool's form,
|
|
1690
|
+
* in that order. A node without a reference renders without one.
|
|
1691
|
+
*
|
|
1692
|
+
* @param node - The outline node
|
|
1693
|
+
* @returns The rendered row
|
|
1694
|
+
*
|
|
1695
|
+
* @example
|
|
1696
|
+
* ```ts
|
|
1697
|
+
* import { renderBrowserOutlineRow } from '@orkestrel/browser'
|
|
1698
|
+
*
|
|
1699
|
+
* renderBrowserOutlineRow({
|
|
1700
|
+
* id: '4',
|
|
1701
|
+
* parent: undefined,
|
|
1702
|
+
* children: [],
|
|
1703
|
+
* backend: undefined,
|
|
1704
|
+
* frame: undefined,
|
|
1705
|
+
* ignored: false,
|
|
1706
|
+
* role: 'checkbox',
|
|
1707
|
+
* name: ' Gift wrap ',
|
|
1708
|
+
* description: undefined,
|
|
1709
|
+
* value: undefined,
|
|
1710
|
+
* properties: { checked: 'true' },
|
|
1711
|
+
* session: 'main',
|
|
1712
|
+
* reference: 'e4',
|
|
1713
|
+
* }) // 'e4 checkbox "Gift wrap" [checked]'
|
|
1714
|
+
* ```
|
|
1715
|
+
*/
|
|
1716
|
+
function renderBrowserOutlineRow(node) {
|
|
1717
|
+
const name = JSON.stringify(normalizeBrowserName(node.name ?? ""));
|
|
1718
|
+
const role = node.role ?? "unknown";
|
|
1719
|
+
let row = node.reference === void 0 ? `${role} ${name}` : `${node.reference} ${role} ${name}`;
|
|
1720
|
+
if (node.value !== void 0 && node.value !== "") row += ` value=${JSON.stringify(String(node.value))}`;
|
|
1721
|
+
for (const key of [
|
|
1722
|
+
"pressed",
|
|
1723
|
+
"expanded",
|
|
1724
|
+
"selected"
|
|
1725
|
+
]) {
|
|
1726
|
+
const value = node.properties[key];
|
|
1727
|
+
if (isString(value) || isBoolean(value)) row += ` ${key}=${String(value)}`;
|
|
1728
|
+
}
|
|
1729
|
+
if (node.properties["checked"] === true || node.properties["checked"] === "true") row += " [checked]";
|
|
1730
|
+
if (node.properties["disabled"] === true) row += " [disabled]";
|
|
1731
|
+
if (node.tool !== void 0) row += ` [tool=${node.tool}]`;
|
|
1732
|
+
return row;
|
|
1733
|
+
}
|
|
1734
|
+
/**
|
|
1735
|
+
* Returns the referenced outline nodes whose role and accessible name share the most words with a
|
|
1736
|
+
* search, in document order.
|
|
1737
|
+
*
|
|
1738
|
+
* @remarks
|
|
1739
|
+
* The search and each node's role and name are lowercased and split into whole words by
|
|
1740
|
+
* `BROWSER_SEARCH_PATTERN`, so a word shorter than 3 letters or digits never counts, and words
|
|
1741
|
+
* compare whole, with no substring or prefix match. A node scores the number of distinct search
|
|
1742
|
+
* words equal to one of its words, and every node with the highest score above 0 matches. Only a
|
|
1743
|
+
* node an outline renders as a referenced row takes part: one that is not ignored, carries a
|
|
1744
|
+
* reference, and whose role is not omitted, a heading, or text.
|
|
1745
|
+
*
|
|
1746
|
+
* @param nodes - Ordered accessibility rows
|
|
1747
|
+
* @param search - The words to match
|
|
1748
|
+
* @returns The best-matching nodes in document order; empty when no node shares a word
|
|
1749
|
+
*
|
|
1750
|
+
* @example
|
|
1751
|
+
* ```ts
|
|
1752
|
+
* import { scanBrowserOutline } from '@orkestrel/browser'
|
|
1753
|
+
*
|
|
1754
|
+
* const node = {
|
|
1755
|
+
* parent: undefined,
|
|
1756
|
+
* children: [],
|
|
1757
|
+
* backend: undefined,
|
|
1758
|
+
* frame: undefined,
|
|
1759
|
+
* ignored: false,
|
|
1760
|
+
* description: undefined,
|
|
1761
|
+
* value: undefined,
|
|
1762
|
+
* properties: {},
|
|
1763
|
+
* session: 'main',
|
|
1764
|
+
* }
|
|
1765
|
+
* const nodes = [
|
|
1766
|
+
* { ...node, id: '1', role: 'button', name: 'Close', reference: 'e1' },
|
|
1767
|
+
* { ...node, id: '2', role: 'button', name: 'Archive', reference: 'e2' },
|
|
1768
|
+
* ]
|
|
1769
|
+
* scanBrowserOutline(nodes, 'archive dialog button').map((match) => match.reference) // ['e2']
|
|
1770
|
+
* ```
|
|
1771
|
+
*/
|
|
1772
|
+
function scanBrowserOutline(nodes, search) {
|
|
1773
|
+
const words = collectBrowserWords(search);
|
|
1774
|
+
if (words.size === 0) return [];
|
|
1775
|
+
let best = 0;
|
|
1776
|
+
const scored = [];
|
|
1777
|
+
for (const node of nodes) {
|
|
1778
|
+
const role = node.role ?? "";
|
|
1779
|
+
if (node.ignored || node.reference === void 0 || BROWSER_OUTLINE_OMITTED_ROLES.has(role) || role === "heading" || role === "StaticText") continue;
|
|
1780
|
+
const own = collectBrowserWords(`${role} ${node.name ?? ""}`);
|
|
1781
|
+
let score = 0;
|
|
1782
|
+
for (const word of words) if (own.has(word)) score += 1;
|
|
1783
|
+
if (score === 0) continue;
|
|
1784
|
+
best = Math.max(best, score);
|
|
1785
|
+
scored.push({
|
|
1786
|
+
node,
|
|
1787
|
+
score
|
|
1788
|
+
});
|
|
1789
|
+
}
|
|
1790
|
+
return scored.filter((entry) => entry.score === best).map((entry) => entry.node);
|
|
1791
|
+
}
|
|
1792
|
+
/**
|
|
1793
|
+
* Collects distinct lowercase words of at least 3 letters or digits.
|
|
1794
|
+
*
|
|
1795
|
+
* @param text - Text to scan
|
|
1796
|
+
* @returns The searchable whole words
|
|
1797
|
+
*
|
|
1798
|
+
* @example
|
|
1799
|
+
* ```ts
|
|
1800
|
+
* import { collectBrowserWords } from '@orkestrel/browser'
|
|
1801
|
+
* [...collectBrowserWords('The cart, the bag')] // ['the', 'cart', 'bag']
|
|
1802
|
+
* ```
|
|
1803
|
+
*/
|
|
1804
|
+
function collectBrowserWords(text) {
|
|
1805
|
+
return new Set(Array.from(text.toLowerCase().matchAll(BROWSER_SEARCH_PATTERN), (match) => match[0]));
|
|
1806
|
+
}
|
|
1807
|
+
/**
|
|
1808
|
+
* Matches the lines sharing the most distinct search words, in document order.
|
|
1809
|
+
*
|
|
1810
|
+
* @param text - The complete projection
|
|
1811
|
+
* @param search - Words to match, using the outline's word rule
|
|
1812
|
+
* @returns The best-scoring lines and their original UTF-16 offsets; empty without a shared word
|
|
1813
|
+
*
|
|
1814
|
+
* @example
|
|
1815
|
+
* ```ts
|
|
1816
|
+
* import { scanBrowserText } from '@orkestrel/browser'
|
|
1817
|
+
* scanBrowserText('Cart\nBlue kettle', 'kettle') // [{ offset: 5, text: 'Blue kettle' }]
|
|
1818
|
+
* ```
|
|
1819
|
+
*/
|
|
1820
|
+
function scanBrowserText(text, search) {
|
|
1821
|
+
const words = collectBrowserWords(search);
|
|
1822
|
+
if (words.size === 0) return [];
|
|
1823
|
+
let best = 0;
|
|
1824
|
+
const scored = [];
|
|
1825
|
+
for (const line of text.matchAll(/[^\r\n]+/g)) {
|
|
1826
|
+
const own = collectBrowserWords(line[0]);
|
|
1827
|
+
let score = 0;
|
|
1828
|
+
for (const word of words) if (own.has(word)) score += 1;
|
|
1829
|
+
if (score === 0) continue;
|
|
1830
|
+
best = Math.max(best, score);
|
|
1831
|
+
scored.push({
|
|
1832
|
+
match: {
|
|
1833
|
+
offset: line.index,
|
|
1834
|
+
text: line[0]
|
|
1835
|
+
},
|
|
1836
|
+
score
|
|
1837
|
+
});
|
|
1838
|
+
}
|
|
1839
|
+
return scored.filter((entry) => entry.score === best).map((entry) => entry.match);
|
|
1840
|
+
}
|
|
1841
|
+
/**
|
|
1842
|
+
* Renders matching rows in at most half the available reply room.
|
|
1843
|
+
*
|
|
1844
|
+
* @remarks
|
|
1845
|
+
* An oversized first row is cut with an ellipsis without splitting a surrogate pair, reserving
|
|
1846
|
+
* room for the first later row that can fit beside it only when the cut keeps the leading token
|
|
1847
|
+
* through its first space. Otherwise it cuts without reserving. Later rows that do not fit are
|
|
1848
|
+
* skipped. An empty collection or a cut with no character before the ellipsis produces no block.
|
|
1849
|
+
*
|
|
1850
|
+
* @param heading - The match count and search, ending with a colon
|
|
1851
|
+
* @param rows - Matching rows in document order
|
|
1852
|
+
* @param room - Characters available in the reply
|
|
1853
|
+
* @returns The bounded block with a trailing blank line, or an empty string
|
|
1854
|
+
*
|
|
1855
|
+
* @example
|
|
1856
|
+
* ```ts
|
|
1857
|
+
* import { renderBrowserMatches } from '@orkestrel/browser'
|
|
1858
|
+
* renderBrowserMatches('Matches:', ['[5] Cart'], 100) // 'Matches:\n[5] Cart\n\n'
|
|
1859
|
+
* ```
|
|
1860
|
+
*/
|
|
1861
|
+
function renderBrowserMatches(heading, rows, room) {
|
|
1862
|
+
const half = Math.floor(room / 2);
|
|
1863
|
+
let text = `${heading}\n`;
|
|
1864
|
+
let count = 0;
|
|
1865
|
+
for (const row of rows) {
|
|
1866
|
+
const space = half - text.length - 2;
|
|
1867
|
+
if (space < 1) break;
|
|
1868
|
+
if (row.length > space) {
|
|
1869
|
+
if (count > 0) continue;
|
|
1870
|
+
let end = space - 1;
|
|
1871
|
+
const later = rows.slice(1).find((candidate) => candidate.length + 2 <= space);
|
|
1872
|
+
if (later !== void 0 && end - later.length - 1 >= row.indexOf(" ") + 1) end -= later.length + 1;
|
|
1873
|
+
const last = row.charCodeAt(end - 1);
|
|
1874
|
+
if (last >= 55296 && last <= 56319) end -= 1;
|
|
1875
|
+
if (end < 1) break;
|
|
1876
|
+
text += `${row.slice(0, end)}…\n`;
|
|
1877
|
+
} else text += `${row}\n`;
|
|
1878
|
+
count += 1;
|
|
1879
|
+
}
|
|
1880
|
+
return count === 0 ? "" : `${text}\n`;
|
|
1881
|
+
}
|
|
1882
|
+
/**
|
|
1629
1883
|
* Renders document-order text and referenced elements with a bounded element count.
|
|
1884
|
+
*
|
|
1885
|
+
* @remarks
|
|
1886
|
+
* Each referenced row renders through `renderBrowserOutlineRow`, and the closing
|
|
1887
|
+
* `(COUNT of TOTAL elements)` line counts the rows included and available. `matches` holds the
|
|
1888
|
+
* rows `scanBrowserOutline` returns for `search`, and `focus` the last referenced row whose node
|
|
1889
|
+
* carries a `focused` property of `true`; both reach past `limit`.
|
|
1890
|
+
*
|
|
1630
1891
|
* @param url - Document address
|
|
1631
1892
|
* @param title - Document title
|
|
1632
1893
|
* @param nodes - Ordered accessibility rows
|
|
1633
1894
|
* @param limit - Maximum referenced rows to include
|
|
1634
|
-
* @
|
|
1895
|
+
* @param search - The words whose best-matching rows `matches` lists. Default: none, which lists
|
|
1896
|
+
* none
|
|
1897
|
+
* @returns Outline text, element counts, matching rows, and the focused row
|
|
1898
|
+
*
|
|
1899
|
+
* @example
|
|
1900
|
+
* ```ts
|
|
1901
|
+
* import { renderBrowserOutline } from '@orkestrel/browser'
|
|
1902
|
+
*
|
|
1903
|
+
* const node = {
|
|
1904
|
+
* parent: undefined,
|
|
1905
|
+
* children: [],
|
|
1906
|
+
* backend: undefined,
|
|
1907
|
+
* frame: undefined,
|
|
1908
|
+
* ignored: false,
|
|
1909
|
+
* description: undefined,
|
|
1910
|
+
* value: undefined,
|
|
1911
|
+
* session: 'main',
|
|
1912
|
+
* }
|
|
1913
|
+
* const outline = renderBrowserOutline(
|
|
1914
|
+
* 'https://shop.example/cart',
|
|
1915
|
+
* 'Cart',
|
|
1916
|
+
* [
|
|
1917
|
+
* { ...node, id: '1', role: 'button', name: 'Close', properties: {}, reference: 'e1' },
|
|
1918
|
+
* { ...node, id: '2', role: 'button', name: 'Archive', properties: { focused: true }, reference: 'e2' },
|
|
1919
|
+
* ],
|
|
1920
|
+
* 1,
|
|
1921
|
+
* 'archive button',
|
|
1922
|
+
* )
|
|
1923
|
+
* outline.text // 'page "Cart" https://shop.example/cart\ne1 button "Close"\n(1 of 2 elements)'
|
|
1924
|
+
* outline.matches // ['e2 button "Archive"']
|
|
1925
|
+
* outline.focus // 'e2 button "Archive"'
|
|
1926
|
+
* ```
|
|
1635
1927
|
*/
|
|
1636
|
-
function renderBrowserOutline(url, title, nodes, limit) {
|
|
1928
|
+
function renderBrowserOutline(url, title, nodes, limit, search) {
|
|
1637
1929
|
const rows = [`page ${JSON.stringify(title)} ${url}`];
|
|
1638
1930
|
const sessions = /* @__PURE__ */ new Map();
|
|
1639
1931
|
for (const node of nodes) {
|
|
@@ -1646,6 +1938,7 @@ function renderBrowserOutline(url, title, nodes, limit) {
|
|
|
1646
1938
|
}
|
|
1647
1939
|
let count = 0;
|
|
1648
1940
|
let total = 0;
|
|
1941
|
+
let focus;
|
|
1649
1942
|
for (const node of nodes) {
|
|
1650
1943
|
if (node.ignored || BROWSER_OUTLINE_OMITTED_ROLES.has(node.role ?? "")) continue;
|
|
1651
1944
|
const name = normalizeBrowserName(node.name ?? "");
|
|
@@ -1660,14 +1953,10 @@ function renderBrowserOutline(url, title, nodes, limit) {
|
|
|
1660
1953
|
}
|
|
1661
1954
|
if (node.reference === void 0) continue;
|
|
1662
1955
|
total += 1;
|
|
1956
|
+
if (node.properties["focused"] === true) focus = node;
|
|
1663
1957
|
if (count >= limit) continue;
|
|
1664
1958
|
count += 1;
|
|
1665
|
-
|
|
1666
|
-
if (node.value !== void 0 && node.value !== "") row += ` value=${JSON.stringify(String(node.value))}`;
|
|
1667
|
-
if (node.properties["checked"] === true || node.properties["checked"] === "true") row += " [checked]";
|
|
1668
|
-
if (node.properties["disabled"] === true) row += " [disabled]";
|
|
1669
|
-
if (node.tool !== void 0) row += ` [tool=${node.tool}]`;
|
|
1670
|
-
rows.push(row);
|
|
1959
|
+
rows.push(renderBrowserOutlineRow(node));
|
|
1671
1960
|
}
|
|
1672
1961
|
rows.push(`(${count} of ${total} elements)`);
|
|
1673
1962
|
return {
|
|
@@ -1675,7 +1964,9 @@ function renderBrowserOutline(url, title, nodes, limit) {
|
|
|
1675
1964
|
title,
|
|
1676
1965
|
text: rows.join("\n"),
|
|
1677
1966
|
count,
|
|
1678
|
-
total
|
|
1967
|
+
total,
|
|
1968
|
+
matches: search === void 0 ? [] : scanBrowserOutline(nodes, search).map(renderBrowserOutlineRow),
|
|
1969
|
+
focus: focus === void 0 ? void 0 : renderBrowserOutlineRow(focus)
|
|
1679
1970
|
};
|
|
1680
1971
|
}
|
|
1681
1972
|
/**
|
|
@@ -1756,27 +2047,27 @@ function renderBrowserToolOutput(value) {
|
|
|
1756
2047
|
return (rendered.success ? rendered.value ?? String(value) : "[Unserializable tool output]").slice(0, BROWSER_REGISTRY_OUTPUT_LIMIT);
|
|
1757
2048
|
}
|
|
1758
2049
|
/**
|
|
1759
|
-
* Derives the advertised input schema from an authored one, adding a required `
|
|
2050
|
+
* Derives the advertised input schema from an authored one, adding a required `purpose` parameter when the schema requires nothing.
|
|
1760
2051
|
* @param schema - Authored input schema, or undefined for a parameterless tool
|
|
1761
|
-
* @returns An advertised schema, or undefined when the authored `
|
|
1762
|
-
* @remarks The caller strips only a synthetic `
|
|
2052
|
+
* @returns An advertised schema, or undefined when the authored `purpose` parameter is optional
|
|
2053
|
+
* @remarks The caller strips only a synthetic `purpose` parameter before invoking the page tool.
|
|
1763
2054
|
*/
|
|
1764
2055
|
function deriveBrowserToolSchema(schema) {
|
|
1765
2056
|
const properties = isRecord(schema?.["properties"]) ? schema["properties"] : {};
|
|
1766
2057
|
const required = isArray(schema?.["required"]) ? schema["required"].filter(isString) : [];
|
|
1767
|
-
if (Object.hasOwn(properties, "
|
|
2058
|
+
if (Object.hasOwn(properties, "purpose") && !required.includes("purpose")) return void 0;
|
|
1768
2059
|
if (required.length > 0) return schema;
|
|
1769
2060
|
return {
|
|
1770
2061
|
...schema,
|
|
1771
2062
|
type: "object",
|
|
1772
2063
|
properties: {
|
|
1773
2064
|
...properties,
|
|
1774
|
-
|
|
2065
|
+
purpose: {
|
|
1775
2066
|
type: "string",
|
|
1776
2067
|
description: "Describe the purpose of this action."
|
|
1777
2068
|
}
|
|
1778
2069
|
},
|
|
1779
|
-
required: ["
|
|
2070
|
+
required: ["purpose"]
|
|
1780
2071
|
};
|
|
1781
2072
|
}
|
|
1782
2073
|
/**
|
|
@@ -1788,8 +2079,8 @@ function deriveBrowserToolSchema(schema) {
|
|
|
1788
2079
|
* units, one fewer when the last would split a surrogate pair (so a limit of 1 before a pair keeps
|
|
1789
2080
|
* nothing), followed by `\n[characters 0–END of TOTAL; FOOTER]`. The footer has no default, so
|
|
1790
2081
|
* every caller states what the cut result's reader does next: the toolset passes
|
|
1791
|
-
* `BROWSER_TOOL_VIEW_FOOTER` for
|
|
1792
|
-
* any other.
|
|
2082
|
+
* `BROWSER_TOOL_VIEW_FOOTER` for an action or `dialog` receipt that carries a view and
|
|
2083
|
+
* `BROWSER_TOOL_CUT_FOOTER` for any other result, including `look` and `read` results.
|
|
1793
2084
|
*
|
|
1794
2085
|
* @param text - The page-authored or composed string
|
|
1795
2086
|
* @param limit - The most characters kept before the footer, a positive integer
|
|
@@ -1826,8 +2117,8 @@ function boundBrowserText(text, limit, footer) {
|
|
|
1826
2117
|
* ```ts
|
|
1827
2118
|
* import { BROWSER_TOOL_COPY, validateBrowserToolArguments } from '@orkestrel/browser'
|
|
1828
2119
|
*
|
|
1829
|
-
* validateBrowserToolArguments(BROWSER_TOOL_COPY.look, {
|
|
1830
|
-
* // throws 'The look tool takes no ref parameter; call look with
|
|
2120
|
+
* validateBrowserToolArguments(BROWSER_TOOL_COPY.look, { search: 'cart', ref: 'e1' })
|
|
2121
|
+
* // throws 'The look tool takes no ref parameter; call look with search and offset.'
|
|
1831
2122
|
* ```
|
|
1832
2123
|
*/
|
|
1833
2124
|
function validateBrowserToolArguments(definition, args) {
|
|
@@ -3607,7 +3898,7 @@ function renderBrowserJourney(journey) {
|
|
|
3607
3898
|
return `${step.id} type ${secret ? "(secret)" : JSON.stringify(text)}${suffix} into ${name}${step.arguments["submit"] === true ? ", submit" : ""}`;
|
|
3608
3899
|
}
|
|
3609
3900
|
case "press": return `${step.id} press ${text}${suffix}`;
|
|
3610
|
-
case "wait": return `${step.id} wait ${JSON.stringify(text)}${suffix}`;
|
|
3901
|
+
case "wait": return `${step.id} wait ${JSON.stringify(text)}${suffix}${step.arguments["absent"] === true ? ", absent" : ""}`;
|
|
3611
3902
|
case "dialog": return `${step.id} dialog ${step.arguments["accept"] === true ? "accept" : "dismiss"}${binding === void 0 ? "" : ` ${JSON.stringify(text)}${suffix}`}`;
|
|
3612
3903
|
case "switch": return `${step.id} switch ${JSON.stringify(step.tab?.title)} ${step.tab?.url}`;
|
|
3613
3904
|
case "unresolved": return `${step.id} unresolved: ${step.gap}`;
|
|
@@ -3915,46 +4206,55 @@ function buildBrowserJourney(steps, options) {
|
|
|
3915
4206
|
//#endregion
|
|
3916
4207
|
//#region src/core/compilers.ts
|
|
3917
4208
|
/**
|
|
3918
|
-
* Compiles a
|
|
4209
|
+
* Compiles a wait woken by mutations and finished transitions and animations, with one deadline and explicit disconnect ownership.
|
|
4210
|
+
* @remarks
|
|
4211
|
+
* `transitionend` and `animationend` inside a shadow root are not composed in Chromium
|
|
4212
|
+
* 154.0.4258.53, so this document listener does not see them. Mutations inside that root are
|
|
4213
|
+
* not observed either.
|
|
3919
4214
|
* @param deadline - Maximum time in milliseconds
|
|
3920
4215
|
* @param key - Isolated-world property owning this observer
|
|
3921
|
-
* @param predicate - Optional expression checked immediately and after
|
|
4216
|
+
* @param predicate - Optional expression checked immediately and on the task after a mutation batch or a `BROWSER_WAIT_EVENTS` event
|
|
3922
4217
|
* @returns Promise expression resolving true on a match or change, false at the deadline
|
|
3923
4218
|
*/
|
|
3924
4219
|
function compileQueryWaitExpression(deadline, key, predicate) {
|
|
3925
4220
|
return `new Promise((resolve) => {
|
|
3926
|
-
let
|
|
4221
|
+
let task
|
|
3927
4222
|
let timer
|
|
3928
4223
|
let observer
|
|
4224
|
+
const events = new AbortController()
|
|
3929
4225
|
const finish = (value) => {
|
|
3930
4226
|
observer?.disconnect()
|
|
3931
|
-
|
|
4227
|
+
events.abort()
|
|
4228
|
+
if (task !== undefined) clearTimeout(task)
|
|
3932
4229
|
clearTimeout(timer)
|
|
3933
4230
|
delete globalThis[${JSON.stringify(key)}]
|
|
3934
4231
|
resolve(value)
|
|
3935
4232
|
}
|
|
3936
4233
|
const check = () => {
|
|
3937
|
-
|
|
4234
|
+
task = undefined
|
|
3938
4235
|
if (${predicate ?? "true"}) finish(true)
|
|
3939
4236
|
}
|
|
3940
4237
|
globalThis[${JSON.stringify(key)}] = () => finish(false)
|
|
3941
|
-
|
|
3942
|
-
if (
|
|
3943
|
-
}
|
|
4238
|
+
const schedule = () => {
|
|
4239
|
+
if (task === undefined) task = setTimeout(check, 0)
|
|
4240
|
+
}
|
|
4241
|
+
observer = new MutationObserver(schedule)
|
|
3944
4242
|
observer.observe(document, { childList: true, attributes: true, characterData: true, subtree: true })
|
|
4243
|
+
for (const name of ${JSON.stringify(BROWSER_WAIT_EVENTS)}) document.addEventListener(name, schedule, { capture: true, signal: events.signal })
|
|
3945
4244
|
timer = setTimeout(() => finish(false), ${JSON.stringify(deadline)})
|
|
3946
4245
|
${predicate === void 0 ? "" : "check()"}
|
|
3947
4246
|
})`;
|
|
3948
4247
|
}
|
|
3949
4248
|
/**
|
|
3950
|
-
* Compiles a visible
|
|
4249
|
+
* Compiles a wait for the presence or absence of visible text, coalesced by tasks.
|
|
3951
4250
|
* @param text - Text to find in the main document body
|
|
3952
4251
|
* @param deadline - Maximum time in milliseconds
|
|
3953
4252
|
* @param key - Isolated-world property owning this observer
|
|
4253
|
+
* @param absent - If `true`, resolves when the body's `innerText` lacks `text`; if `false`, when it contains it. Default: `false`
|
|
3954
4254
|
* @returns Mutation-driven promise expression with one deadline
|
|
3955
4255
|
*/
|
|
3956
|
-
function compileTextWaitExpression(text, deadline, key) {
|
|
3957
|
-
return compileQueryWaitExpression(deadline, key,
|
|
4256
|
+
function compileTextWaitExpression(text, deadline, key, absent = false) {
|
|
4257
|
+
return compileQueryWaitExpression(deadline, key, `${absent ? "!" : ""}(document.body?.innerText ?? '').includes(${JSON.stringify(text)})`);
|
|
3958
4258
|
}
|
|
3959
4259
|
/**
|
|
3960
4260
|
* Compiles text selection or select-option assignment against the resolved element.
|
|
@@ -4266,20 +4566,337 @@ ${expression}
|
|
|
4266
4566
|
); const s = JSON.stringify(r); if (typeof s === 'string' && s.length > ${limit}) throw new Error(${JSON.stringify(BROWSER_RESULT_LIMIT_SENTINEL_PREFIX)} + s.length); return r })()`;
|
|
4267
4567
|
}
|
|
4268
4568
|
/**
|
|
4269
|
-
* Compiles the in-page function that reads the document's URL, title, and
|
|
4569
|
+
* Compiles the in-page function that reads the document's URL, title, and rendered HTML.
|
|
4270
4570
|
*
|
|
4271
4571
|
* @remarks
|
|
4272
|
-
* The
|
|
4273
|
-
*
|
|
4274
|
-
*
|
|
4275
|
-
*
|
|
4572
|
+
* The optional root defaults to `document.documentElement`. {@link BrowserReadingInput}
|
|
4573
|
+
* defines its inert capture, lowering, redaction, and limits. A missing root yields empty HTML.
|
|
4574
|
+
* Call it inside {@link compileGuardedEvaluateExpression} to bound the final serialized result
|
|
4575
|
+
* under {@link BROWSER_RESULT_LIMIT}.
|
|
4276
4576
|
*
|
|
4277
4577
|
* @returns A function declaration source
|
|
4278
4578
|
*/
|
|
4279
4579
|
function compileReadFunction() {
|
|
4280
|
-
return `function() {
|
|
4281
|
-
const
|
|
4282
|
-
return { url:
|
|
4580
|
+
return `function(node = document.documentElement) {
|
|
4581
|
+
const owner = node?.ownerDocument ?? document
|
|
4582
|
+
if (!node) return { url: owner.URL, title: owner.title, html: '' }
|
|
4583
|
+
const capture = { url: owner.URL, title: owner.title, html: '' }
|
|
4584
|
+
const view = owner.defaultView
|
|
4585
|
+
const rendered = view !== null && node.isConnected
|
|
4586
|
+
const inert = owner.implementation.createHTMLDocument('')
|
|
4587
|
+
let root = inert.importNode(node, true)
|
|
4588
|
+
const pending = [{ live: node, twin: root, visited: false }]
|
|
4589
|
+
if (rendered) {
|
|
4590
|
+
let child = node
|
|
4591
|
+
for (
|
|
4592
|
+
let parent = node;
|
|
4593
|
+
parent?.ownerDocument === owner;
|
|
4594
|
+
parent = parent.assignedSlot ?? parent.parentElement ?? (parent.parentNode instanceof view.ShadowRoot ? parent.parentNode.host : null)
|
|
4595
|
+
) {
|
|
4596
|
+
const style = view.getComputedStyle(parent)
|
|
4597
|
+
const outside =
|
|
4598
|
+
parent !== node &&
|
|
4599
|
+
(style.contentVisibility === 'hidden' ||
|
|
4600
|
+
['canvas', 'video', 'audio'].includes(parent.localName) ||
|
|
4601
|
+
parent instanceof view.HTMLInputElement ||
|
|
4602
|
+
parent instanceof view.HTMLTextAreaElement ||
|
|
4603
|
+
parent instanceof view.HTMLOptionElement ||
|
|
4604
|
+
(parent.namespaceURI === 'http://www.w3.org/2000/svg' &&
|
|
4605
|
+
parent.localName === 'switch' &&
|
|
4606
|
+
child !== (Array.from(parent.children).find((candidate) => candidate.getClientRects().length > 0) ?? parent.firstElementChild)) ||
|
|
4607
|
+
((parent instanceof view.HTMLSelectElement || parent instanceof view.HTMLOptGroupElement) &&
|
|
4608
|
+
!(node instanceof view.HTMLOptionElement || node instanceof view.HTMLOptGroupElement)) ||
|
|
4609
|
+
[
|
|
4610
|
+
'script',
|
|
4611
|
+
'style',
|
|
4612
|
+
'template',
|
|
4613
|
+
'frame',
|
|
4614
|
+
'frameset',
|
|
4615
|
+
'iframe',
|
|
4616
|
+
'object',
|
|
4617
|
+
'embed',
|
|
4618
|
+
'applet',
|
|
4619
|
+
'noscript',
|
|
4620
|
+
'meta',
|
|
4621
|
+
'link',
|
|
4622
|
+
'base',
|
|
4623
|
+
'math',
|
|
4624
|
+
].includes(parent.localName) ||
|
|
4625
|
+
(parent.localName === 'details' &&
|
|
4626
|
+
!parent.hasAttribute('open') &&
|
|
4627
|
+
child !== parent.querySelector(':scope > summary')))
|
|
4628
|
+
const unassigned =
|
|
4629
|
+
child.parentElement?.shadowRoot !== null &&
|
|
4630
|
+
child.parentElement?.shadowRoot !== undefined &&
|
|
4631
|
+
child.assignedSlot === null
|
|
4632
|
+
if (style.display === 'none' || outside || unassigned) {
|
|
4633
|
+
root = undefined
|
|
4634
|
+
pending.length = 0
|
|
4635
|
+
break
|
|
4636
|
+
}
|
|
4637
|
+
child = parent
|
|
4638
|
+
}
|
|
4639
|
+
}
|
|
4640
|
+
while (pending.length > 0) {
|
|
4641
|
+
const entry = pending.pop()
|
|
4642
|
+
if (entry === undefined) continue
|
|
4643
|
+
const { live, twin, visited } = entry
|
|
4644
|
+
const tag = live.localName
|
|
4645
|
+
const input = tag === 'input' ? (live.getAttribute('type')?.toLowerCase() ?? 'text') : ''
|
|
4646
|
+
if (
|
|
4647
|
+
[
|
|
4648
|
+
'script',
|
|
4649
|
+
'style',
|
|
4650
|
+
'template',
|
|
4651
|
+
'frame',
|
|
4652
|
+
'frameset',
|
|
4653
|
+
'iframe',
|
|
4654
|
+
'object',
|
|
4655
|
+
'embed',
|
|
4656
|
+
'applet',
|
|
4657
|
+
'noscript',
|
|
4658
|
+
'meta',
|
|
4659
|
+
'link',
|
|
4660
|
+
'base',
|
|
4661
|
+
'math',
|
|
4662
|
+
].includes(tag) ||
|
|
4663
|
+
(tag === 'input' && ['password', 'hidden'].includes(input))
|
|
4664
|
+
) {
|
|
4665
|
+
twin.remove()
|
|
4666
|
+
if (twin === root) root = undefined
|
|
4667
|
+
continue
|
|
4668
|
+
}
|
|
4669
|
+
const style = entry.style ?? (rendered ? view.getComputedStyle(live) : undefined)
|
|
4670
|
+
const invisible = style?.visibility === 'hidden' || style?.visibility === 'collapse'
|
|
4671
|
+
if (style?.display === 'none' || (invisible && !live.hasChildNodes())) {
|
|
4672
|
+
twin.remove()
|
|
4673
|
+
if (twin === root) root = undefined
|
|
4674
|
+
continue
|
|
4675
|
+
}
|
|
4676
|
+
if (!visited) {
|
|
4677
|
+
pending.push({ live, twin, style, visited: true })
|
|
4678
|
+
if (rendered) {
|
|
4679
|
+
twin.removeAttribute('hidden')
|
|
4680
|
+
twin.removeAttribute('aria-hidden')
|
|
4681
|
+
}
|
|
4682
|
+
if (
|
|
4683
|
+
rendered &&
|
|
4684
|
+
(live instanceof view.HTMLAnchorElement || live instanceof view.HTMLAreaElement) &&
|
|
4685
|
+
live.hasAttribute('href')
|
|
4686
|
+
)
|
|
4687
|
+
twin.setAttribute('href', live.href)
|
|
4688
|
+
if (style?.contentVisibility === 'hidden' || ['canvas', 'video', 'audio'].includes(tag)) {
|
|
4689
|
+
twin.replaceChildren()
|
|
4690
|
+
continue
|
|
4691
|
+
}
|
|
4692
|
+
if (
|
|
4693
|
+
rendered &&
|
|
4694
|
+
(live instanceof view.HTMLInputElement ||
|
|
4695
|
+
live instanceof view.HTMLTextAreaElement ||
|
|
4696
|
+
live instanceof view.HTMLSelectElement ||
|
|
4697
|
+
live instanceof view.HTMLOptionElement ||
|
|
4698
|
+
live instanceof view.HTMLOptGroupElement)
|
|
4699
|
+
)
|
|
4700
|
+
continue
|
|
4701
|
+
const summary =
|
|
4702
|
+
tag === 'details' && !live.hasAttribute('open')
|
|
4703
|
+
? live.querySelector(':scope > summary')
|
|
4704
|
+
: undefined
|
|
4705
|
+
|
|
4706
|
+
// Native layout selects the switch branch after its conditional processing tests.
|
|
4707
|
+
const conditional = tag === 'switch' && live.namespaceURI === 'http://www.w3.org/2000/svg'
|
|
4708
|
+
const branch = rendered && conditional
|
|
4709
|
+
? (Array.from(live.children).find((candidate) => candidate.getClientRects().length > 0) ?? live.firstElementChild)
|
|
4710
|
+
: undefined
|
|
4711
|
+
// Save sibling pointers before pruning invalidates the copied child collections.
|
|
4712
|
+
let element = live.lastElementChild
|
|
4713
|
+
let counterpart = twin.lastElementChild
|
|
4714
|
+
let child = live.lastChild
|
|
4715
|
+
let mirror = twin.lastChild
|
|
4716
|
+
while (child !== null && mirror !== null) {
|
|
4717
|
+
const previous = child.previousSibling
|
|
4718
|
+
const previousMirror = mirror.previousSibling
|
|
4719
|
+
const previousElement = element?.previousElementSibling ?? null
|
|
4720
|
+
const previousCounterpart = counterpart?.previousElementSibling ?? null
|
|
4721
|
+
let omitted = rendered && ((summary !== undefined && child !== summary) || (conditional && child !== branch))
|
|
4722
|
+
if (rendered && live.shadowRoot !== null) {
|
|
4723
|
+
const slot =
|
|
4724
|
+
child instanceof view.Element || child instanceof view.Text ? child.assignedSlot : null
|
|
4725
|
+
if (slot === null) omitted = true
|
|
4726
|
+
else {
|
|
4727
|
+
for (
|
|
4728
|
+
let parent = slot;
|
|
4729
|
+
parent !== null && parent !== live;
|
|
4730
|
+
parent = parent.assignedSlot ?? parent.parentElement ?? (parent.parentNode instanceof view.ShadowRoot ? parent.parentNode.host : null)
|
|
4731
|
+
) {
|
|
4732
|
+
if (view.getComputedStyle(parent).display === 'none') omitted = true
|
|
4733
|
+
}
|
|
4734
|
+
}
|
|
4735
|
+
}
|
|
4736
|
+
if (omitted || (child.nodeType === 3 && invisible)) mirror.parentNode?.removeChild(mirror)
|
|
4737
|
+
else if (child.nodeType === 1) {
|
|
4738
|
+
// Element sibling pointers retain their type across windowless owner realms.
|
|
4739
|
+
if (element !== null && counterpart !== null)
|
|
4740
|
+
pending.push({ live: element, twin: counterpart, visited: false })
|
|
4741
|
+
}
|
|
4742
|
+
if (child.nodeType === 1) {
|
|
4743
|
+
element = previousElement
|
|
4744
|
+
counterpart = previousCounterpart
|
|
4745
|
+
}
|
|
4746
|
+
child = previous
|
|
4747
|
+
mirror = previousMirror
|
|
4748
|
+
}
|
|
4749
|
+
continue
|
|
4750
|
+
}
|
|
4751
|
+
if (!rendered || style?.contentVisibility === 'hidden') continue
|
|
4752
|
+
if (
|
|
4753
|
+
['div', 'summary', 'details'].includes(tag) &&
|
|
4754
|
+
!style?.display.startsWith('inline') &&
|
|
4755
|
+
style?.display !== 'contents' &&
|
|
4756
|
+
twin.hasChildNodes()
|
|
4757
|
+
) {
|
|
4758
|
+
twin.prepend(inert.createElement('br'))
|
|
4759
|
+
twin.append(inert.createElement('br'))
|
|
4760
|
+
}
|
|
4761
|
+
let fragments
|
|
4762
|
+
let lines
|
|
4763
|
+
let container = false
|
|
4764
|
+
if (live instanceof view.HTMLInputElement) {
|
|
4765
|
+
lines = []
|
|
4766
|
+
if (['text', 'email', 'search', 'tel', 'url', 'number'].includes(live.type)) {
|
|
4767
|
+
if (live.type !== 'number' || !live.validity.badInput) {
|
|
4768
|
+
const placeholder = view.getComputedStyle(live, '::placeholder')
|
|
4769
|
+
lines = [
|
|
4770
|
+
live.value ||
|
|
4771
|
+
(live.matches(':placeholder-shown') &&
|
|
4772
|
+
placeholder.visibility === 'visible' &&
|
|
4773
|
+
placeholder.opacity !== '0' &&
|
|
4774
|
+
placeholder.color !== 'rgba(0, 0, 0, 0)'
|
|
4775
|
+
? live.placeholder
|
|
4776
|
+
: ''),
|
|
4777
|
+
]
|
|
4778
|
+
}
|
|
4779
|
+
} else if (['submit', 'reset', 'button'].includes(live.type)) lines = [live.value]
|
|
4780
|
+
else if (live.type === 'file' && live.files?.length === 1)
|
|
4781
|
+
lines = Array.from(live.files ?? [], (file) => file.name)
|
|
4782
|
+
else if (live.type === 'image' && live.getAttribute('aria-hidden') !== 'true')
|
|
4783
|
+
lines = [live.getAttribute('aria-label')?.trim() || live.alt]
|
|
4784
|
+
} else if (live instanceof view.HTMLTextAreaElement) {
|
|
4785
|
+
const placeholder = view.getComputedStyle(live, '::placeholder')
|
|
4786
|
+
const value =
|
|
4787
|
+
live.value ||
|
|
4788
|
+
(live.matches(':placeholder-shown') &&
|
|
4789
|
+
placeholder.visibility === 'visible' &&
|
|
4790
|
+
placeholder.opacity !== '0' &&
|
|
4791
|
+
placeholder.color !== 'rgba(0, 0, 0, 0)'
|
|
4792
|
+
? live.placeholder
|
|
4793
|
+
: '')
|
|
4794
|
+
lines = value.split(/\\r?\\n/)
|
|
4795
|
+
} else if (
|
|
4796
|
+
live instanceof view.HTMLSelectElement ||
|
|
4797
|
+
live instanceof view.HTMLOptionElement ||
|
|
4798
|
+
live instanceof view.HTMLOptGroupElement
|
|
4799
|
+
) {
|
|
4800
|
+
lines = []
|
|
4801
|
+
const select = live instanceof view.HTMLSelectElement ? live : live.closest('select')
|
|
4802
|
+
if (select !== null) {
|
|
4803
|
+
if (!select.multiple && select.size <= 1) {
|
|
4804
|
+
const option = select.selectedOptions[0]
|
|
4805
|
+
if (option !== undefined && (live === select || live === option || live.contains(option)))
|
|
4806
|
+
lines.push(option.label)
|
|
4807
|
+
} else {
|
|
4808
|
+
const box = select.getBoundingClientRect()
|
|
4809
|
+
const top = box.top + select.clientTop
|
|
4810
|
+
const bottom = top + select.clientHeight
|
|
4811
|
+
const rows =
|
|
4812
|
+
live === select
|
|
4813
|
+
? select.querySelectorAll('option,optgroup')
|
|
4814
|
+
: [live, ...live.querySelectorAll('option')]
|
|
4815
|
+
for (const row of rows) {
|
|
4816
|
+
const rowStyle = view.getComputedStyle(row)
|
|
4817
|
+
const groupStyle =
|
|
4818
|
+
row.parentElement instanceof view.HTMLOptGroupElement
|
|
4819
|
+
? view.getComputedStyle(row.parentElement)
|
|
4820
|
+
: undefined
|
|
4821
|
+
if (
|
|
4822
|
+
rowStyle.display === 'none' ||
|
|
4823
|
+
rowStyle.visibility !== 'visible' ||
|
|
4824
|
+
groupStyle?.display === 'none'
|
|
4825
|
+
)
|
|
4826
|
+
continue
|
|
4827
|
+
const rect = row.getBoundingClientRect()
|
|
4828
|
+
// The group box includes its options; its caption ends where the first row starts.
|
|
4829
|
+
const end =
|
|
4830
|
+
row instanceof view.HTMLOptGroupElement
|
|
4831
|
+
? (Array.from(row.querySelectorAll('option'))
|
|
4832
|
+
.find((option) => option.getClientRects().length > 0)
|
|
4833
|
+
?.getBoundingClientRect().top ?? rect.bottom)
|
|
4834
|
+
: rect.bottom
|
|
4835
|
+
if (
|
|
4836
|
+
end > top &&
|
|
4837
|
+
rect.top < bottom &&
|
|
4838
|
+
rect.right > box.left + select.clientLeft &&
|
|
4839
|
+
rect.left < box.left + select.clientLeft + select.clientWidth &&
|
|
4840
|
+
(row instanceof view.HTMLOptionElement || row instanceof view.HTMLOptGroupElement)
|
|
4841
|
+
)
|
|
4842
|
+
lines.push(row.label)
|
|
4843
|
+
}
|
|
4844
|
+
}
|
|
4845
|
+
}
|
|
4846
|
+
} else if (tag === 'svg') {
|
|
4847
|
+
if (twin.parentElement?.closest('svg, foreignObject')?.localName === 'svg') continue
|
|
4848
|
+
lines = []
|
|
4849
|
+
fragments = []
|
|
4850
|
+
for (const content of twin.querySelectorAll('text, foreignObject')) {
|
|
4851
|
+
if (content.parentElement?.closest('svg, foreignObject')?.localName === 'foreignObject') continue
|
|
4852
|
+
if (content.localName === 'text' && content.parentElement?.closest('text')?.closest('svg') === content.closest('svg')) continue
|
|
4853
|
+
if (content.localName === 'foreignObject') fragments.push(...content.childNodes)
|
|
4854
|
+
else if (content.textContent) fragments.push(inert.createTextNode(content.textContent))
|
|
4855
|
+
}
|
|
4856
|
+
if (fragments.length === 0) fragments = undefined
|
|
4857
|
+
if (!invisible && fragments === undefined && live.getAttribute('aria-hidden') !== 'true')
|
|
4858
|
+
lines.push(
|
|
4859
|
+
live.getAttribute('aria-label')?.trim() ||
|
|
4860
|
+
live.querySelector(':scope > title')?.textContent ||
|
|
4861
|
+
'',
|
|
4862
|
+
)
|
|
4863
|
+
} else if (['form', 'dialog', 'button'].includes(tag)) {
|
|
4864
|
+
container = true
|
|
4865
|
+
if (live instanceof view.HTMLButtonElement && !invisible && live.innerText.trim() === '') {
|
|
4866
|
+
const name = live.getAttribute('aria-label')?.trim()
|
|
4867
|
+
if (name) {
|
|
4868
|
+
lines = [name]
|
|
4869
|
+
container = false
|
|
4870
|
+
}
|
|
4871
|
+
}
|
|
4872
|
+
}
|
|
4873
|
+
if (lines === undefined && !container) continue
|
|
4874
|
+
if (invisible && !container && tag !== 'svg') lines = []
|
|
4875
|
+
const block = !style?.display.startsWith('inline') && style?.display !== 'contents'
|
|
4876
|
+
const carrier = inert.createElement(block ? 'div' : 'span')
|
|
4877
|
+
if (container) carrier.append(...twin.childNodes)
|
|
4878
|
+
else if (fragments !== undefined) {
|
|
4879
|
+
for (const fragment of fragments) {
|
|
4880
|
+
carrier.append(fragment, inert.createElement('br'))
|
|
4881
|
+
}
|
|
4882
|
+
} else
|
|
4883
|
+
for (const [index, line] of (lines ?? []).entries()) {
|
|
4884
|
+
if (index > 0) carrier.append(inert.createElement('br'))
|
|
4885
|
+
carrier.append(inert.createTextNode(line))
|
|
4886
|
+
}
|
|
4887
|
+
if (carrier.hasChildNodes()) {
|
|
4888
|
+
carrier.prepend(inert.createElement('br'))
|
|
4889
|
+
carrier.append(inert.createElement('br'))
|
|
4890
|
+
}
|
|
4891
|
+
twin.replaceWith(carrier)
|
|
4892
|
+
if (twin === root) root = carrier
|
|
4893
|
+
}
|
|
4894
|
+
// Guard a copy that has fallen out of step with the live tree.
|
|
4895
|
+
for (const input of root?.querySelectorAll('input[type="password" i],input[type="hidden" i]') ??
|
|
4896
|
+
[])
|
|
4897
|
+
input.remove()
|
|
4898
|
+
capture.html = root?.outerHTML ?? ''
|
|
4899
|
+
return capture
|
|
4283
4900
|
}`;
|
|
4284
4901
|
}
|
|
4285
4902
|
/**
|
|
@@ -4433,7 +5050,7 @@ function compileActionabilityFunction(options) {
|
|
|
4433
5050
|
if (${JSON.stringify(options.enabled === true)} && this.matches(':disabled')) throw new Error('Element is disabled')
|
|
4434
5051
|
if (${JSON.stringify(options.editable === true)} && (this.matches('[readonly]') || (!this.isContentEditable && !('value' in this)))) throw new Error('Element is not editable')
|
|
4435
5052
|
let previous
|
|
4436
|
-
for (let index = 0; index < 2; index += 1) {
|
|
5053
|
+
for (let index = 0; index < ${options.stable === true ? 2 : 0}; index += 1) {
|
|
4437
5054
|
await new Promise((resolve) => requestAnimationFrame(resolve))
|
|
4438
5055
|
const rect = this.getBoundingClientRect()
|
|
4439
5056
|
const current = [rect.x, rect.y, rect.width, rect.height]
|
|
@@ -5566,7 +6183,7 @@ var BrowserCodegen = class {
|
|
|
5566
6183
|
* title: 'Example',
|
|
5567
6184
|
* html: '<nav>Menu</nav><main><p>Body</p></main>',
|
|
5568
6185
|
* })
|
|
5569
|
-
* reading.markdown() // { text: 'Body', offset: 0, total: 4 }
|
|
6186
|
+
* reading.markdown({ distill: true }) // { text: 'Body', offset: 0, total: 4 }
|
|
5570
6187
|
* ```
|
|
5571
6188
|
*/
|
|
5572
6189
|
var BrowserReading = class {
|
|
@@ -5598,7 +6215,7 @@ var BrowserReading = class {
|
|
|
5598
6215
|
return this.#navigation !== void 0 && this.#navigation() !== this.#epoch;
|
|
5599
6216
|
}
|
|
5600
6217
|
markdown(options) {
|
|
5601
|
-
const distill = options?.distill ??
|
|
6218
|
+
const distill = options?.distill ?? false;
|
|
5602
6219
|
let text = this.#markdowns.get(distill);
|
|
5603
6220
|
if (text === void 0) {
|
|
5604
6221
|
text = renderMarkdown(htmlToMarkdown(this.#source(distill).document));
|
|
@@ -5607,7 +6224,7 @@ var BrowserReading = class {
|
|
|
5607
6224
|
return extractBrowserSlice(text, options?.offset, options?.limit);
|
|
5608
6225
|
}
|
|
5609
6226
|
text(options) {
|
|
5610
|
-
const distill = options?.distill ??
|
|
6227
|
+
const distill = options?.distill ?? false;
|
|
5611
6228
|
let text = this.#texts.get(distill);
|
|
5612
6229
|
if (text === void 0) {
|
|
5613
6230
|
text = renderText(this.#source(distill).document);
|
|
@@ -5616,7 +6233,10 @@ var BrowserReading = class {
|
|
|
5616
6233
|
return extractBrowserSlice(text, options?.offset, options?.limit);
|
|
5617
6234
|
}
|
|
5618
6235
|
#source(distill) {
|
|
5619
|
-
if (!distill) return this.#html
|
|
6236
|
+
if (!distill) return this.#html.map((node) => node.category === "element" ? {
|
|
6237
|
+
...node,
|
|
6238
|
+
attributes: resolveAttributes(node, this.#url)
|
|
6239
|
+
} : node);
|
|
5620
6240
|
this.#distilled ??= this.#html.distill({ base: this.#url });
|
|
5621
6241
|
return this.#distilled;
|
|
5622
6242
|
}
|
|
@@ -5687,8 +6307,7 @@ var BrowserPageElement = class {
|
|
|
5687
6307
|
async read(options) {
|
|
5688
6308
|
const epoch = this.#epoch();
|
|
5689
6309
|
const capture = await this.#call(`function() {
|
|
5690
|
-
const capture = (${compileReadFunction()})()
|
|
5691
|
-
capture.html = this.outerHTML
|
|
6310
|
+
const capture = (${compileReadFunction()})(this)
|
|
5692
6311
|
return ${compileGuardedEvaluateExpression("capture", BROWSER_RESULT_LIMIT)}
|
|
5693
6312
|
}`, options);
|
|
5694
6313
|
if (!isRecord(capture)) throw new BrowserElementError(this.reference, "UNKNOWN", "returned an invalid reading");
|
|
@@ -5889,6 +6508,7 @@ var BrowserPageElement = class {
|
|
|
5889
6508
|
}
|
|
5890
6509
|
async #pointer(options) {
|
|
5891
6510
|
this.#assert(options);
|
|
6511
|
+
await this.#input.page.send("Page.bringToFront", void 0, options);
|
|
5892
6512
|
await this.#input.client.send("DOM.scrollIntoViewIfNeeded", { backendNodeId: this.#input.backend }, {
|
|
5893
6513
|
session: this.#input.node.session,
|
|
5894
6514
|
...options
|
|
@@ -6009,7 +6629,7 @@ var BrowserElementManager = class {
|
|
|
6009
6629
|
...options
|
|
6010
6630
|
});
|
|
6011
6631
|
this.#assertCapture(this.#input.page.id, epoch);
|
|
6012
|
-
return renderBrowserOutline(this.#input.page.url, requireBrowserString(readEvaluationResult(result), "Document title"), this.#within(rows, options?.within), limit);
|
|
6632
|
+
return renderBrowserOutline(this.#input.page.url, requireBrowserString(readEvaluationResult(result), "Document title"), this.#within(rows, options?.within), limit, options?.search);
|
|
6013
6633
|
} catch (error) {
|
|
6014
6634
|
if (options?.signal?.aborted === true || this.#changes === changes) throw error;
|
|
6015
6635
|
this.#lifetime.signal.throwIfAborted();
|
|
@@ -6099,34 +6719,52 @@ var BrowserElementManager = class {
|
|
|
6099
6719
|
const signal = options?.signal === void 0 ? this.#lifetime.signal : AbortSignal.any([options.signal, this.#lifetime.signal]);
|
|
6100
6720
|
while (true) {
|
|
6101
6721
|
signal.throwIfAborted();
|
|
6102
|
-
const remaining = Math.max(0, end - performance.now());
|
|
6103
|
-
const call = {
|
|
6104
|
-
timeout: remaining,
|
|
6105
|
-
signal
|
|
6106
|
-
};
|
|
6107
|
-
await this.#input.ready(call);
|
|
6108
|
-
const frame = this.#scopeFrame(query.within);
|
|
6109
|
-
const session = await this.#input.resolve(frame);
|
|
6110
|
-
const context = await this.#input.world(frame, session, call);
|
|
6111
6722
|
const key = `__browserQueryWait${++this.#sequence}`;
|
|
6112
|
-
|
|
6113
|
-
|
|
6114
|
-
|
|
6115
|
-
returnByValue: true,
|
|
6116
|
-
awaitPromise: true
|
|
6117
|
-
}, {
|
|
6118
|
-
session,
|
|
6119
|
-
timeout: remaining + 1e3,
|
|
6120
|
-
signal
|
|
6121
|
-
}).then((result) => ({ result }), (error) => ({ error }));
|
|
6723
|
+
let session;
|
|
6724
|
+
let context;
|
|
6725
|
+
const changes = this.#changes;
|
|
6122
6726
|
try {
|
|
6727
|
+
await this.#input.ready({
|
|
6728
|
+
timeout: this.#remaining(end),
|
|
6729
|
+
signal
|
|
6730
|
+
});
|
|
6731
|
+
const frame = this.#scopeFrame(query.within);
|
|
6732
|
+
session = await this.#input.resolve(frame);
|
|
6733
|
+
context = await this.#input.world(frame, session, {
|
|
6734
|
+
timeout: this.#remaining(end),
|
|
6735
|
+
signal
|
|
6736
|
+
});
|
|
6737
|
+
const remaining = this.#remaining(end);
|
|
6738
|
+
const call = {
|
|
6739
|
+
timeout: remaining,
|
|
6740
|
+
signal
|
|
6741
|
+
};
|
|
6742
|
+
const pending = this.#input.client.send("Runtime.evaluate", {
|
|
6743
|
+
expression: compileQueryWaitExpression(remaining, key),
|
|
6744
|
+
contextId: context,
|
|
6745
|
+
returnByValue: true,
|
|
6746
|
+
awaitPromise: true
|
|
6747
|
+
}, {
|
|
6748
|
+
session,
|
|
6749
|
+
timeout: remaining + 1e3,
|
|
6750
|
+
signal
|
|
6751
|
+
}).then((result) => ({ result }), (error) => ({ error }));
|
|
6123
6752
|
const found = await this.find(query, call);
|
|
6124
6753
|
if (options?.absent === true ? found.length === 0 : found.length > 0) return found;
|
|
6125
6754
|
const outcome = await pending;
|
|
6126
6755
|
if ("error" in outcome) throw outcome.error;
|
|
6127
6756
|
if (readEvaluationResult(outcome.result) !== true || performance.now() >= end) throw new BrowserError("Element wait timed out", "BROWSER_WAIT_TIMEOUT");
|
|
6757
|
+
} catch (error) {
|
|
6758
|
+
signal.throwIfAborted();
|
|
6759
|
+
if (isCDPTimeoutError(error)) throw new BrowserError("Element wait timed out", "BROWSER_WAIT_TIMEOUT");
|
|
6760
|
+
if (this.#input.page.closed || this.#changes === changes && (!isError(error) || !BROWSER_CONTEXT_LOSS_PATTERN.test(error.message))) throw error;
|
|
6761
|
+
const remaining = this.#remaining(end);
|
|
6762
|
+
if (this.#changes === changes) await this.#input.recover({
|
|
6763
|
+
signal,
|
|
6764
|
+
timeout: remaining
|
|
6765
|
+
});
|
|
6128
6766
|
} finally {
|
|
6129
|
-
await this.#input.client.send("Runtime.evaluate", {
|
|
6767
|
+
if (context !== void 0 && session !== void 0) await this.#input.client.send("Runtime.evaluate", {
|
|
6130
6768
|
expression: `globalThis[${JSON.stringify(key)}]?.()`,
|
|
6131
6769
|
contextId: context,
|
|
6132
6770
|
returnByValue: true
|
|
@@ -6137,6 +6775,11 @@ var BrowserElementManager = class {
|
|
|
6137
6775
|
}
|
|
6138
6776
|
}
|
|
6139
6777
|
}
|
|
6778
|
+
#remaining(end) {
|
|
6779
|
+
const remaining = end - performance.now();
|
|
6780
|
+
if (remaining <= 0) throw new BrowserError("Element wait timed out", "BROWSER_WAIT_TIMEOUT");
|
|
6781
|
+
return remaining;
|
|
6782
|
+
}
|
|
6140
6783
|
element(reference) {
|
|
6141
6784
|
const canonical = parseBrowserReference(reference);
|
|
6142
6785
|
return [...this.#records.values()].find((entry) => entry.node.reference === canonical)?.element;
|
|
@@ -6644,7 +7287,7 @@ var BrowserRegistry = class {
|
|
|
6644
7287
|
pending.reject(error);
|
|
6645
7288
|
}
|
|
6646
7289
|
async #executeAdopted(tool, synthetic, input, context) {
|
|
6647
|
-
const parameters = synthetic ? Object.fromEntries(Object.entries(input).filter(([key]) => key !== "
|
|
7290
|
+
const parameters = synthetic ? Object.fromEntries(Object.entries(input).filter(([key]) => key !== "purpose")) : input;
|
|
6648
7291
|
const result = await this.execute(tool, parameters, { signal: context.signal });
|
|
6649
7292
|
if (result.status !== "Completed") throw new BrowserError(`Browser tool ${result.status}: ${result.error ?? "No error description"}`.slice(0, BROWSER_REGISTRY_OUTPUT_LIMIT));
|
|
6650
7293
|
return renderBrowserToolOutput(result.output);
|
|
@@ -8961,6 +9604,8 @@ var BrowserPage = class BrowserPage extends BrowserFrame {
|
|
|
8961
9604
|
#epoch = 0;
|
|
8962
9605
|
#floor = 0;
|
|
8963
9606
|
#closed = false;
|
|
9607
|
+
#waitRelease = new AbortController();
|
|
9608
|
+
#textWaits = /* @__PURE__ */ new Map();
|
|
8964
9609
|
#codegen;
|
|
8965
9610
|
#registry;
|
|
8966
9611
|
#codegenStart = new BrowserTransition();
|
|
@@ -9011,6 +9656,7 @@ var BrowserPage = class BrowserPage extends BrowserFrame {
|
|
|
9011
9656
|
session: sessionId,
|
|
9012
9657
|
resolve: this.#resolveFrameSession.bind(this),
|
|
9013
9658
|
world: this.#world.bind(this),
|
|
9659
|
+
recover: this.#recoverReadiness.bind(this),
|
|
9014
9660
|
reference: this.#reference,
|
|
9015
9661
|
ready: this.#ready.bind(this)
|
|
9016
9662
|
});
|
|
@@ -9074,6 +9720,10 @@ var BrowserPage = class BrowserPage extends BrowserFrame {
|
|
|
9074
9720
|
}
|
|
9075
9721
|
async wait(text, options) {
|
|
9076
9722
|
this.assert();
|
|
9723
|
+
options = {
|
|
9724
|
+
...options,
|
|
9725
|
+
signal: options?.signal === void 0 ? this.#waitRelease.signal : AbortSignal.any([options.signal, this.#waitRelease.signal])
|
|
9726
|
+
};
|
|
9077
9727
|
const timeout = options?.timeout ?? 3e4;
|
|
9078
9728
|
validateBrowserTimeout(timeout);
|
|
9079
9729
|
const end = performance.now() + timeout;
|
|
@@ -9086,9 +9736,10 @@ var BrowserPage = class BrowserPage extends BrowserFrame {
|
|
|
9086
9736
|
});
|
|
9087
9737
|
const context = await this.#world(this.id, this.#sessionId, options);
|
|
9088
9738
|
const key = `__browserTextWait${++this.#waitSequence}`;
|
|
9739
|
+
this.#textWaits.set(key, context);
|
|
9089
9740
|
try {
|
|
9090
9741
|
if (readEvaluationResult(await this.send("Runtime.evaluate", {
|
|
9091
|
-
expression: compileTextWaitExpression(text, remaining, key),
|
|
9742
|
+
expression: compileTextWaitExpression(text, remaining, key, options?.absent === true),
|
|
9092
9743
|
contextId: context,
|
|
9093
9744
|
returnByValue: true,
|
|
9094
9745
|
awaitPromise: true
|
|
@@ -9102,23 +9753,20 @@ var BrowserPage = class BrowserPage extends BrowserFrame {
|
|
|
9102
9753
|
return;
|
|
9103
9754
|
} catch (error) {
|
|
9104
9755
|
if (options?.signal?.aborted === true) {
|
|
9105
|
-
await this
|
|
9106
|
-
expression: `globalThis[${JSON.stringify(key)}]?.()`,
|
|
9107
|
-
contextId: context,
|
|
9108
|
-
returnByValue: true
|
|
9109
|
-
}, { timeout: 1e3 }).catch(() => void 0);
|
|
9756
|
+
if (!this.#closed) await this.#releaseTextWait(key, context);
|
|
9110
9757
|
throw options.signal.reason;
|
|
9111
9758
|
}
|
|
9112
|
-
if (!isError(error) ||
|
|
9113
|
-
this.#dom = void 0;
|
|
9759
|
+
if (!isError(error) || !BROWSER_CONTEXT_LOSS_PATTERN.test(error.message)) throw error;
|
|
9114
9760
|
if (performance.now() >= end) throw new BrowserError("Browser text wait timed out", "BROWSER_WAIT_TIMEOUT", {
|
|
9115
9761
|
text,
|
|
9116
9762
|
timeout
|
|
9117
9763
|
});
|
|
9118
|
-
await this.#
|
|
9764
|
+
await this.#recoverReadiness({
|
|
9119
9765
|
...options,
|
|
9120
9766
|
timeout: Math.max(0, end - performance.now())
|
|
9121
9767
|
});
|
|
9768
|
+
} finally {
|
|
9769
|
+
this.#textWaits.delete(key);
|
|
9122
9770
|
}
|
|
9123
9771
|
}
|
|
9124
9772
|
}
|
|
@@ -9506,6 +10154,8 @@ var BrowserPage = class BrowserPage extends BrowserFrame {
|
|
|
9506
10154
|
return release;
|
|
9507
10155
|
}
|
|
9508
10156
|
async #releaseResources() {
|
|
10157
|
+
this.#waitRelease.abort(new BrowserError("Browser page is closed"));
|
|
10158
|
+
await Promise.all([...this.#textWaits].map(([key, context]) => this.#releaseTextWait(key, context)));
|
|
9509
10159
|
this.#unhold();
|
|
9510
10160
|
this.#announcement.reject(new BrowserError("Browser session ended"));
|
|
9511
10161
|
this.#client.unsubscribe("Page.lifecycleEvent", this.#lifecycleHandler, this.#sessionId);
|
|
@@ -9555,6 +10205,16 @@ var BrowserPage = class BrowserPage extends BrowserFrame {
|
|
|
9555
10205
|
}
|
|
9556
10206
|
this.#steps.destroy();
|
|
9557
10207
|
}
|
|
10208
|
+
async #releaseTextWait(key, context) {
|
|
10209
|
+
await this.#client.send("Runtime.evaluate", {
|
|
10210
|
+
expression: `globalThis[${JSON.stringify(key)}]?.()`,
|
|
10211
|
+
contextId: context,
|
|
10212
|
+
returnByValue: true
|
|
10213
|
+
}, {
|
|
10214
|
+
session: this.#sessionId,
|
|
10215
|
+
timeout: 1e3
|
|
10216
|
+
}).catch(() => void 0);
|
|
10217
|
+
}
|
|
9558
10218
|
#frame(frame) {
|
|
9559
10219
|
return new BrowserFrame(this.#client, (id) => this.#resolveFrameSession(id), frame.id, frame.url, frame.parent, frame.name, true, () => this.#epochOf(frame.id), (session, call) => this.#world(frame.id, session, call));
|
|
9560
10220
|
}
|
|
@@ -9598,6 +10258,10 @@ var BrowserPage = class BrowserPage extends BrowserFrame {
|
|
|
9598
10258
|
#rejectSeed(seed) {
|
|
9599
10259
|
if (this.#seed === seed) this.#seed = void 0;
|
|
9600
10260
|
}
|
|
10261
|
+
#recoverReadiness(options) {
|
|
10262
|
+
this.#dom = void 0;
|
|
10263
|
+
return this.#parkReadiness(options);
|
|
10264
|
+
}
|
|
9601
10265
|
async #seedReadiness(options) {
|
|
9602
10266
|
const document = this.#floor;
|
|
9603
10267
|
const context = await this.#world(this.id, this.#sessionId, options);
|
|
@@ -9645,24 +10309,44 @@ var BrowserPage = class BrowserPage extends BrowserFrame {
|
|
|
9645
10309
|
this.#dom = this.#loader ?? "initial";
|
|
9646
10310
|
for (const id of this.#readiness.keys()) this.#settleReadiness(id)?.resolve();
|
|
9647
10311
|
}
|
|
9648
|
-
#world(frame, session, options) {
|
|
10312
|
+
async #world(frame, session, options) {
|
|
10313
|
+
options?.signal?.throwIfAborted();
|
|
9649
10314
|
const cached = this.#worlds.get(frame);
|
|
9650
10315
|
if (cached !== void 0) return Promise.resolve(cached.context);
|
|
9651
10316
|
const existing = this.#creating.get(frame);
|
|
9652
|
-
if (existing !== void 0) return existing.promise;
|
|
10317
|
+
if (existing !== void 0) return await this.#awaitWorld(existing.promise, options);
|
|
9653
10318
|
const pending = this.#client.send("Page.createIsolatedWorld", {
|
|
9654
10319
|
frameId: frame,
|
|
9655
10320
|
worldName: BROWSER_FRAME_WORLD_NAME
|
|
9656
10321
|
}, {
|
|
9657
10322
|
session,
|
|
9658
|
-
|
|
10323
|
+
signal: this.#waitRelease.signal
|
|
9659
10324
|
}).then(this.#decodeWorld.bind(this, frame));
|
|
9660
10325
|
this.#creating.set(frame, {
|
|
9661
10326
|
session,
|
|
9662
10327
|
promise: pending
|
|
9663
10328
|
});
|
|
9664
10329
|
pending.then(this.#publishWorld.bind(this, frame, session, pending), this.#settleWorld.bind(this, frame, pending));
|
|
9665
|
-
return pending;
|
|
10330
|
+
return await this.#awaitWorld(pending, options);
|
|
10331
|
+
}
|
|
10332
|
+
async #awaitWorld(pending, options) {
|
|
10333
|
+
const signal = options?.signal;
|
|
10334
|
+
signal?.throwIfAborted();
|
|
10335
|
+
const timeout = options?.timeout ?? 3e4;
|
|
10336
|
+
validateBrowserTimeout(timeout);
|
|
10337
|
+
const abandoned = Promise.withResolvers();
|
|
10338
|
+
const abort = this.#abandonWorld.bind(this, abandoned, signal);
|
|
10339
|
+
const timer = setTimeout(() => abandoned.reject(new CDPTimeoutError("Isolated world wait timed out")), timeout);
|
|
10340
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
10341
|
+
try {
|
|
10342
|
+
return await Promise.race([pending, abandoned.promise]);
|
|
10343
|
+
} finally {
|
|
10344
|
+
clearTimeout(timer);
|
|
10345
|
+
signal?.removeEventListener("abort", abort);
|
|
10346
|
+
}
|
|
10347
|
+
}
|
|
10348
|
+
#abandonWorld(pending, signal) {
|
|
10349
|
+
pending.reject(signal?.reason);
|
|
9666
10350
|
}
|
|
9667
10351
|
#decodeWorld(frame, world) {
|
|
9668
10352
|
return readBrowserWorld(world, frame);
|
|
@@ -11493,7 +12177,7 @@ function createBrowserReading(input) {
|
|
|
11493
12177
|
*
|
|
11494
12178
|
* const toolset = createBrowserToolset(page, { context })
|
|
11495
12179
|
* await toolset.start()
|
|
11496
|
-
* const result = await toolset.tools.execute({ id: '1', name: 'look', arguments: {
|
|
12180
|
+
* const result = await toolset.tools.execute({ id: '1', name: 'look', arguments: { search: 'cart' } })
|
|
11497
12181
|
* ```
|
|
11498
12182
|
*
|
|
11499
12183
|
* @example Drive a page with a small model
|
|
@@ -11507,8 +12191,8 @@ function createBrowserReading(input) {
|
|
|
11507
12191
|
* const system =
|
|
11508
12192
|
* 'You control a web browser with tools and must call a tool before you answer. ' +
|
|
11509
12193
|
* 'The first message shows the page as look returns it; references such as e4 name its elements. ' +
|
|
11510
|
-
* 'To learn a fact, call read with
|
|
11511
|
-
*
|
|
12194
|
+
* 'To learn a fact, call read with search set to words from your question; when its result ends by naming an offset, call read again with that offset. ' +
|
|
12195
|
+
* "To use the site's search box, call type with its reference, the words, and submit true. " +
|
|
11512
12196
|
* 'To press a button or follow a link, call click with its reference from the latest result. Never invent a reference. ' +
|
|
11513
12197
|
* 'If text you expect has not appeared, call wait once. ' +
|
|
11514
12198
|
* 'When the task is done, answer in one short sentence.'
|
|
@@ -11518,11 +12202,11 @@ function createBrowserReading(input) {
|
|
|
11518
12202
|
* const page = await browser.create({ url: 'https://shop.example.test/' })
|
|
11519
12203
|
* const toolset = createBrowserToolset(page, { tools: createToolManager() })
|
|
11520
12204
|
* await toolset.start()
|
|
11521
|
-
* toolset.tools.tools().map((tool) => tool.name) // ['look', 'read', 'click', 'type', 'press', 'navigate', 'wait']
|
|
12205
|
+
* toolset.tools.tools().map((tool) => tool.name) // ['look', 'read', 'plain', 'click', 'type', 'press', 'navigate', 'wait']
|
|
11522
12206
|
* const seeded = await toolset.tools.execute({
|
|
11523
12207
|
* id: 'seed',
|
|
11524
12208
|
* name: 'look',
|
|
11525
|
-
* arguments: {
|
|
12209
|
+
* arguments: { search: '' },
|
|
11526
12210
|
* })
|
|
11527
12211
|
* const view = seeded.success ? String(seeded.value) : seeded.error
|
|
11528
12212
|
* const agent = createAgent(createOllama({ model: 'qwen3.5:2b-q4_K_M' }), {
|
|
@@ -11742,6 +12426,10 @@ var BrowserJourneyToolset = class {
|
|
|
11742
12426
|
const offset = args["offset"] ?? 0;
|
|
11743
12427
|
if (!isInteger(offset) || offset < 0) throw new BrowserError("The offset parameter must be a non-negative integer.", "BROWSER_JOURNEY_ARGUMENT", { key: "offset" });
|
|
11744
12428
|
const listings = [];
|
|
12429
|
+
const headings = [];
|
|
12430
|
+
const offsets = /* @__PURE__ */ new Map();
|
|
12431
|
+
let headingOffset = 0;
|
|
12432
|
+
let listingOffset = 0;
|
|
11745
12433
|
const faults = /* @__PURE__ */ new Set();
|
|
11746
12434
|
let position = 0;
|
|
11747
12435
|
for (;;) {
|
|
@@ -11749,11 +12437,21 @@ var BrowserJourneyToolset = class {
|
|
|
11749
12437
|
signal,
|
|
11750
12438
|
offset: position
|
|
11751
12439
|
});
|
|
11752
|
-
|
|
12440
|
+
for (const entry of page.entries) {
|
|
12441
|
+
const listing = renderBrowserJourney(entry.journey);
|
|
12442
|
+
const heading = listing.split("\n", 1)[0] ?? "";
|
|
12443
|
+
headings.push(heading);
|
|
12444
|
+
offsets.set(headingOffset, listingOffset);
|
|
12445
|
+
headingOffset += heading.length + 1;
|
|
12446
|
+
listingOffset += listing.length + 2;
|
|
12447
|
+
listings.push(listing);
|
|
12448
|
+
}
|
|
11753
12449
|
for (const fault of page.faults) {
|
|
11754
12450
|
if (faults.has(fault.name)) continue;
|
|
11755
12451
|
faults.add(fault.name);
|
|
11756
|
-
|
|
12452
|
+
const listing = renderBrowserJourneyFault(fault);
|
|
12453
|
+
listings.push(listing);
|
|
12454
|
+
listingOffset += listing.length + 2;
|
|
11757
12455
|
}
|
|
11758
12456
|
const span = page.entries.length;
|
|
11759
12457
|
position += span;
|
|
@@ -11762,7 +12460,11 @@ var BrowserJourneyToolset = class {
|
|
|
11762
12460
|
if (listings.length === 0) return BROWSER_JOURNEY_EMPTY_LISTING;
|
|
11763
12461
|
const listing = listings.join("\n\n");
|
|
11764
12462
|
const start = offset < listing.length ? offset : 0;
|
|
11765
|
-
|
|
12463
|
+
const search = isString(args["search"]) ? args["search"] : "";
|
|
12464
|
+
const matches = start === 0 ? scanBrowserText(headings.join("\n"), search) : [];
|
|
12465
|
+
const count = matches.length;
|
|
12466
|
+
const block = renderBrowserMatches(`${count} ${count === 1 ? "journey matches" : "journeys match"} ${JSON.stringify(search)}:`, matches.map((match) => `[${offsets.get(match.offset)}] ${match.text}`), this.#limit);
|
|
12467
|
+
let end = Math.min(listing.length, start + this.#limit - block.length);
|
|
11766
12468
|
const last = listing.charCodeAt(end - 1);
|
|
11767
12469
|
if (end < listing.length && last >= 55296 && last <= 56319) end -= 1;
|
|
11768
12470
|
if (end === start) throw new BrowserError(`The journeys limit of ${this.#limit} characters cannot hold the next character at offset ${start}; raise the journeys limit.`, "BROWSER_TOOLSET_LIMIT", {
|
|
@@ -11770,8 +12472,8 @@ var BrowserJourneyToolset = class {
|
|
|
11770
12472
|
offset: start
|
|
11771
12473
|
});
|
|
11772
12474
|
const text = listing.slice(start, end);
|
|
11773
|
-
if (start === 0 && end === listing.length) return text
|
|
11774
|
-
return `${text}\n\n[characters ${start}–${end} of ${listing.length}${end < listing.length ? `; call journeys with offset ${end} for more` : ""}]`;
|
|
12475
|
+
if (start === 0 && end === listing.length) return `${block}${text}`;
|
|
12476
|
+
return `${block}${text}\n\n[characters ${start}–${end} of ${listing.length}${end < listing.length ? `; call journeys with offset ${end} for more` : ""}]`;
|
|
11775
12477
|
}
|
|
11776
12478
|
async #edit(args, signal) {
|
|
11777
12479
|
if (this.#readonly) throw new BrowserError(BROWSER_JOURNEY_READONLY_REFUSAL, "BROWSER_JOURNEY_READONLY");
|
|
@@ -11937,7 +12639,7 @@ var BrowserJourneyToolset = class {
|
|
|
11937
12639
|
const performed = await this.#toolset.perform({
|
|
11938
12640
|
id: "journey",
|
|
11939
12641
|
name: "look",
|
|
11940
|
-
arguments: {
|
|
12642
|
+
arguments: { search: "" }
|
|
11941
12643
|
}, { signal });
|
|
11942
12644
|
return performed.result.success ? String(performed.result.value) : performed.result.error;
|
|
11943
12645
|
}
|
|
@@ -11965,7 +12667,7 @@ var BrowserJourneyToolset = class {
|
|
|
11965
12667
|
* view's own tools beside them.
|
|
11966
12668
|
*
|
|
11967
12669
|
* @remarks
|
|
11968
|
-
* Every toolset advertises `look`, `read`, `click`, `type`, and `wait`, which need only the
|
|
12670
|
+
* Every toolset advertises `look`, `read`, `plain`, `click`, `type`, and `wait`, which need only the
|
|
11969
12671
|
* `BrowserViewInterface` it is constructed over. With `options.page` it also advertises `press`
|
|
11970
12672
|
* and `navigate`, stages the `dialog` tool while a dialog is open on the current page, follows a
|
|
11971
12673
|
* popup the current page opens and returns to the opener when the popup closes, and adopts
|
|
@@ -11979,11 +12681,18 @@ var BrowserJourneyToolset = class {
|
|
|
11979
12681
|
* refuses every tool but `dialog` while a dialog is open, forwards `ToolContext.signal` into every
|
|
11980
12682
|
* protocol call, refuses a call to a toolset tool that carries a parameter the tool does not
|
|
11981
12683
|
* advertise, and cuts every returned string and every thrown message at `limit` characters plus
|
|
11982
|
-
* a footer; the footer of a cut
|
|
11983
|
-
*
|
|
11984
|
-
* the
|
|
11985
|
-
*
|
|
11986
|
-
* the
|
|
12684
|
+
* a footer; the footer of a cut action or `dialog` receipt that carries a view names `look` as the next
|
|
12685
|
+
* call. `look`, `read`, and `plain` read the page and return pages of at most `limit`
|
|
12686
|
+
* characters and name the offset the next page starts at. A page ends after the last line break
|
|
12687
|
+
* in its window that lies past the page's start, or at the window's end when no such break fits,
|
|
12688
|
+
* without splitting a surrogate pair. The last page ends at the end of the outline or reading. A `look` whose
|
|
12689
|
+
* `search` matches referenced rows lists those rows first, and its pages reach every referenced row.
|
|
12690
|
+
* A `press` receipt names the referenced element that has focus after the key. A page tool's
|
|
12691
|
+
* error message and JSON output reach the toolset already cut at `BROWSER_REGISTRY_OUTPUT_LIMIT`
|
|
12692
|
+
* (4 096) by the registry, so a `limit` over that shows at most 4 096 characters of either; a page
|
|
12693
|
+
* tool's text output reaches the boundary whole.
|
|
12694
|
+
* `wait` with `absent` set to `true` settles when the text is not on the page, and a miss
|
|
12695
|
+
* returns the `timeout` outcome as an appearance miss does.
|
|
11987
12696
|
*
|
|
11988
12697
|
* `click`, `type`, `press`, `navigate`, and `switch` are actions: one runs at a time, in call
|
|
11989
12698
|
* order, and holds the queue until its receipt. A queued action whose signal aborts leaves
|
|
@@ -12014,7 +12723,7 @@ var BrowserJourneyToolset = class {
|
|
|
12014
12723
|
* page placement's `type` with `submit` ends its action with `and submitted the form` when a read
|
|
12015
12724
|
* recorded a submission, or when the navigation the receipt settled names `formSubmissionGet` or
|
|
12016
12725
|
* `formSubmissionPost` as its reason, whatever the read answered, and with `and pressed Enter`
|
|
12017
|
-
* otherwise. A `read`
|
|
12726
|
+
* otherwise. A `read` or `plain`
|
|
12018
12727
|
* at an offset at or past its retained reading's end restarts that reading at 0. A receipt that
|
|
12019
12728
|
* waits for a requested navigation shares one `BROWSER_TOOL_TIMEOUT_MS` deadline between that
|
|
12020
12729
|
* wait and its view capture, of which `BROWSER_TOOL_CAPTURE_MS` is reserved for the capture. The
|
|
@@ -12030,9 +12739,9 @@ var BrowserJourneyToolset = class {
|
|
|
12030
12739
|
* A page tool is skipped, with `skip` emitted, when its name is reserved (`unresolved` included), when the manager holds
|
|
12031
12740
|
* its name under a tool the toolset did not add (checked again immediately before each addition),
|
|
12032
12741
|
* when its name falls outside `BROWSER_TOOL_NAME_PATTERN`, when its parameters or, for a source
|
|
12033
|
-
* that supplies `tools()`, its selected registration declare `
|
|
12742
|
+
* that supplies `tools()`, its selected registration declare `purpose` optional, or when that
|
|
12034
12743
|
* registration is marked `debugging`. The selected registration is the main frame's, then the
|
|
12035
|
-
* earlier one. A page tool whose parameters require nothing advertises a required `
|
|
12744
|
+
* earlier one. A page tool whose parameters require nothing advertises a required `purpose`, which
|
|
12036
12745
|
* is stripped before the tool runs. Every adopted tool is advertised `untrusted`. `destroy()`
|
|
12037
12746
|
* removes only the tools the manager still holds under the instances the toolset added, never
|
|
12038
12747
|
* closes a page, and ends only what `options.release` hands over, calling it one time last.
|
|
@@ -12043,7 +12752,7 @@ var BrowserJourneyToolset = class {
|
|
|
12043
12752
|
*
|
|
12044
12753
|
* const toolset = new BrowserToolset(page, { page })
|
|
12045
12754
|
* await toolset.start()
|
|
12046
|
-
* const result = await toolset.tools.execute({ id: '1', name: 'look', arguments: {
|
|
12755
|
+
* const result = await toolset.tools.execute({ id: '1', name: 'look', arguments: { search: 'cart' } })
|
|
12047
12756
|
* await toolset.destroy()
|
|
12048
12757
|
* ```
|
|
12049
12758
|
*/
|
|
@@ -12107,20 +12816,23 @@ var BrowserToolset = class {
|
|
|
12107
12816
|
...options?.on === void 0 ? {} : { on: options.on },
|
|
12108
12817
|
...options?.error === void 0 ? {} : { error: options.error }
|
|
12109
12818
|
});
|
|
12110
|
-
const look = this.#create("look", this.#look.bind(this),
|
|
12111
|
-
const read = this.#create("read", this.#read.bind(this), BROWSER_TOOL_CUT_FOOTER);
|
|
12819
|
+
const look = this.#create("look", this.#look.bind(this), BROWSER_TOOL_CUT_FOOTER);
|
|
12820
|
+
const read = this.#create("read", this.#read.bind(this, "read", (reading) => reading.markdown()), BROWSER_TOOL_CUT_FOOTER);
|
|
12821
|
+
const plain = this.#create("plain", this.#read.bind(this, "plain", (reading) => reading.text()), BROWSER_TOOL_CUT_FOOTER);
|
|
12112
12822
|
const click = this.#create("click", this.#click.bind(this), BROWSER_TOOL_VIEW_FOOTER);
|
|
12113
12823
|
const type = this.#create("type", this.#type.bind(this), BROWSER_TOOL_VIEW_FOOTER);
|
|
12114
12824
|
const wait = this.#create("wait", this.#wait.bind(this), BROWSER_TOOL_CUT_FOOTER);
|
|
12115
12825
|
this.#native = Object.freeze(options?.page === void 0 ? [
|
|
12116
12826
|
look,
|
|
12117
12827
|
read,
|
|
12828
|
+
plain,
|
|
12118
12829
|
click,
|
|
12119
12830
|
type,
|
|
12120
12831
|
wait
|
|
12121
12832
|
] : [
|
|
12122
12833
|
look,
|
|
12123
12834
|
read,
|
|
12835
|
+
plain,
|
|
12124
12836
|
click,
|
|
12125
12837
|
type,
|
|
12126
12838
|
this.#create("press", this.#press.bind(this), BROWSER_TOOL_VIEW_FOOTER),
|
|
@@ -12423,44 +13135,74 @@ var BrowserToolset = class {
|
|
|
12423
13135
|
if (isString(secret) && secret.length > 0) message = message.replaceAll(JSON.stringify(secret), "[redacted]").replaceAll(secret, "[redacted]");
|
|
12424
13136
|
return clause === void 0 ? message : boundBrowserText(message, this.#limit, clause);
|
|
12425
13137
|
}
|
|
12426
|
-
async #look(
|
|
12427
|
-
|
|
13138
|
+
async #look(args, context) {
|
|
13139
|
+
const offset = this.#readOffset(args);
|
|
13140
|
+
const search = isString(args["search"]) ? args["search"] : "";
|
|
13141
|
+
const outline = await this.#race(this.#cursor.elements.outline({
|
|
13142
|
+
signal: context.signal,
|
|
13143
|
+
limit: Number.MAX_SAFE_INTEGER,
|
|
13144
|
+
search
|
|
13145
|
+
}), "");
|
|
13146
|
+
const total = outline.text.length;
|
|
13147
|
+
const start = offset < total ? offset : 0;
|
|
13148
|
+
const note = this.#drainNote();
|
|
13149
|
+
const room = this.#limit - note.length;
|
|
13150
|
+
const count = outline.matches.length;
|
|
13151
|
+
const block = start === 0 ? renderBrowserMatches(`${count} ${count === 1 ? "element matches" : "elements match"} ${JSON.stringify(search)}:`, outline.matches, room) : "";
|
|
13152
|
+
const space = room - block.length;
|
|
13153
|
+
const slice = space < 1 ? {
|
|
13154
|
+
text: "",
|
|
13155
|
+
offset: start,
|
|
13156
|
+
total
|
|
13157
|
+
} : extractBrowserSlice(outline.text, start, space);
|
|
13158
|
+
return this.#pageSlice("look", note, slice, block);
|
|
12428
13159
|
}
|
|
12429
|
-
async #read(args, context) {
|
|
12430
|
-
const offset = args
|
|
12431
|
-
if (!isInteger(offset) || offset < 0) throw new BrowserError("The offset parameter must be a non-negative integer.", "BROWSER_TOOLSET_ARGUMENT", { key: "offset" });
|
|
13160
|
+
async #read(name, project, args, context) {
|
|
13161
|
+
const offset = this.#readOffset(args);
|
|
12432
13162
|
const view = this.#cursor;
|
|
12433
13163
|
const retained = this.#reading;
|
|
12434
13164
|
let reading = retained?.reading;
|
|
12435
|
-
if (offset === 0 || retained === void 0 || reading === void 0 || retained.view !== view || reading.stale) {
|
|
13165
|
+
if (offset === 0 || retained === void 0 || reading === void 0 || retained.name !== name || retained.view !== view || reading.stale) {
|
|
12436
13166
|
reading = await this.#race(view.read({ signal: context.signal }), "", context.signal);
|
|
12437
13167
|
this.#reading = {
|
|
12438
13168
|
view,
|
|
12439
|
-
reading
|
|
13169
|
+
reading,
|
|
13170
|
+
name
|
|
12440
13171
|
};
|
|
12441
13172
|
}
|
|
12442
|
-
const
|
|
12443
|
-
|
|
12444
|
-
|
|
12445
|
-
}).total ? offset : 0;
|
|
12446
|
-
const note = boundBrowserText(this.#drain(), Math.max(1, this.#limit - 1), BROWSER_TOOL_CUT_FOOTER);
|
|
13173
|
+
const whole = project(reading);
|
|
13174
|
+
const start = reading === retained?.reading && offset < whole.total ? offset : 0;
|
|
13175
|
+
const note = this.#drainNote();
|
|
12447
13176
|
const room = this.#limit - note.length;
|
|
12448
|
-
const
|
|
13177
|
+
const search = isString(args["search"]) ? args["search"] : "";
|
|
13178
|
+
const matches = start === 0 ? scanBrowserText(whole.text, search) : [];
|
|
13179
|
+
const count = matches.length;
|
|
13180
|
+
const block = renderBrowserMatches(`${count} ${count === 1 ? "line matches" : "lines match"} ${JSON.stringify(search)}:`, matches.map((match) => `[${match.offset}] ${match.text}`), room);
|
|
13181
|
+
const space = room - block.length;
|
|
13182
|
+
const slice = space < 1 ? {
|
|
12449
13183
|
text: "",
|
|
12450
13184
|
offset: start,
|
|
12451
|
-
total:
|
|
12452
|
-
} :
|
|
12453
|
-
|
|
12454
|
-
|
|
12455
|
-
|
|
12456
|
-
|
|
13185
|
+
total: whole.total
|
|
13186
|
+
} : extractBrowserSlice(whole.text, start, space);
|
|
13187
|
+
return this.#pageSlice(name, note, slice, block);
|
|
13188
|
+
}
|
|
13189
|
+
#readOffset(args) {
|
|
13190
|
+
const offset = args["offset"] ?? 0;
|
|
13191
|
+
if (!isInteger(offset) || offset < 0) throw new BrowserError("The offset parameter must be a non-negative integer.", "BROWSER_TOOLSET_ARGUMENT", { key: "offset" });
|
|
13192
|
+
return offset;
|
|
13193
|
+
}
|
|
13194
|
+
#drainNote() {
|
|
13195
|
+
return boundBrowserText(this.#drain(), Math.max(1, this.#limit - 1), BROWSER_TOOL_CUT_FOOTER);
|
|
13196
|
+
}
|
|
13197
|
+
#pageSlice(name, note, slice, block = "") {
|
|
13198
|
+
if (note === "" && slice.text === "" && slice.offset < slice.total) throw new BrowserError(`The ${name} limit of ${this.#limit} characters cannot hold the next character at offset ${slice.offset}; raise the toolset limit.`, "BROWSER_TOOLSET_LIMIT", {
|
|
12457
13199
|
limit: this.#limit,
|
|
12458
|
-
offset:
|
|
13200
|
+
offset: slice.offset
|
|
12459
13201
|
});
|
|
12460
13202
|
const end = slice.offset + slice.text.length;
|
|
12461
13203
|
const more = end < slice.total;
|
|
12462
|
-
if (
|
|
12463
|
-
return [`${note}${slice.text}`, `\n\n[characters ${
|
|
13204
|
+
if (slice.offset === 0 && !more) return [`${note}${block}${slice.text}`, ""];
|
|
13205
|
+
return [`${note}${block}${slice.text}`, `\n\n[characters ${slice.offset}–${end} of ${slice.total}${more ? `; call ${name} with offset ${end} for more` : ""}]`];
|
|
12464
13206
|
}
|
|
12465
13207
|
async #click(args, context) {
|
|
12466
13208
|
const turn = await this.#acquire(context.signal);
|
|
@@ -12563,7 +13305,7 @@ var BrowserToolset = class {
|
|
|
12563
13305
|
return [await this.#settle(page.keyboard.press(key), action, context.signal, true, record, popups, observation, key === "Enter" ? {
|
|
12564
13306
|
explicit: false,
|
|
12565
13307
|
action
|
|
12566
|
-
} : void 0), ""];
|
|
13308
|
+
} : void 0, true), ""];
|
|
12567
13309
|
} finally {
|
|
12568
13310
|
record?.destroy();
|
|
12569
13311
|
popups?.destroy();
|
|
@@ -12602,7 +13344,7 @@ var BrowserToolset = class {
|
|
|
12602
13344
|
const action = `Navigated to ${result.url}`;
|
|
12603
13345
|
return [renderBrowserReceipt({
|
|
12604
13346
|
action,
|
|
12605
|
-
view: await this.#capture(action, context.signal)
|
|
13347
|
+
view: (await this.#capture(action, context.signal)).view
|
|
12606
13348
|
}), ""];
|
|
12607
13349
|
} finally {
|
|
12608
13350
|
if (this.#navigating === page) this.#navigating = void 0;
|
|
@@ -12613,22 +13355,25 @@ var BrowserToolset = class {
|
|
|
12613
13355
|
async #wait(args, context) {
|
|
12614
13356
|
const text = readBrowserToolString(args, "text");
|
|
12615
13357
|
const seconds = args["timeout"];
|
|
13358
|
+
const absent = args["absent"];
|
|
13359
|
+
if (absent !== void 0 && !isBoolean(absent)) throw new BrowserError("The absent parameter must be a boolean.", "BROWSER_TOOLSET_ARGUMENT", { key: "absent" });
|
|
12616
13360
|
if (seconds !== void 0 && (!isFiniteNumber(seconds) || seconds <= 0)) throw new BrowserError("The timeout parameter must be a positive number of seconds.", "BROWSER_TOOLSET_ARGUMENT", { key: "timeout" });
|
|
12617
13361
|
const timeout = Math.min(seconds === void 0 ? BROWSER_TOOL_TIMEOUT_MS : Math.ceil(seconds * 1e3), BROWSER_TOOL_TIMEOUT_LIMIT_MS);
|
|
12618
13362
|
const quoted = JSON.stringify(text);
|
|
12619
13363
|
try {
|
|
12620
13364
|
await this.#race(this.#cursor.wait(text, {
|
|
12621
13365
|
timeout,
|
|
12622
|
-
signal: context.signal
|
|
12623
|
-
|
|
12624
|
-
|
|
13366
|
+
signal: context.signal,
|
|
13367
|
+
absent: absent === true
|
|
13368
|
+
}), `Waited for ${quoted}${absent === true ? " to leave" : ""}`, context.signal);
|
|
13369
|
+
return [`${quoted} is ${absent === true ? "not " : ""}on the page.`, ""];
|
|
12625
13370
|
} catch (error) {
|
|
12626
13371
|
if (!context.signal.aborted && isBrowserError(error) && error.code === "BROWSER_WAIT_TIMEOUT") {
|
|
12627
13372
|
this.#actions.set(context.signal, {
|
|
12628
13373
|
...this.#actions.get(context.signal),
|
|
12629
13374
|
outcome: "timeout"
|
|
12630
13375
|
});
|
|
12631
|
-
return [`${quoted} did not appear within ${timeout / 1e3} s.`, ""];
|
|
13376
|
+
return [absent === true ? `${quoted} is still on the page after ${timeout / 1e3} s.` : `${quoted} did not appear within ${timeout / 1e3} s.`, ""];
|
|
12632
13377
|
}
|
|
12633
13378
|
throw error;
|
|
12634
13379
|
}
|
|
@@ -12651,14 +13396,19 @@ var BrowserToolset = class {
|
|
|
12651
13396
|
const action = `${accept ? "Accepted" : "Dismissed"} the ${dialog.category} dialog ${JSON.stringify(dialog.message)}`;
|
|
12652
13397
|
return [renderBrowserReceipt({
|
|
12653
13398
|
action,
|
|
12654
|
-
view: await this.#capture(action, context.signal)
|
|
13399
|
+
view: (await this.#capture(action, context.signal)).view
|
|
12655
13400
|
}), ""];
|
|
12656
13401
|
} finally {
|
|
12657
13402
|
turn.resolve();
|
|
12658
13403
|
}
|
|
12659
13404
|
}
|
|
12660
|
-
async #tabs(
|
|
12661
|
-
|
|
13405
|
+
async #tabs(args, context) {
|
|
13406
|
+
const text = (await this.#list(context.signal, true)).map((tab) => `${tab.id} ${JSON.stringify(tab.title)} ${tab.url}${tab.current ? " (current)" : ""}`).join("\n");
|
|
13407
|
+
const search = isString(args["search"]) ? args["search"] : "";
|
|
13408
|
+
const matches = scanBrowserText(text, search);
|
|
13409
|
+
const count = matches.length;
|
|
13410
|
+
const note = this.#drainNote();
|
|
13411
|
+
return [`${note}${renderBrowserMatches(`${count} ${count === 1 ? "tab matches" : "tabs match"} ${JSON.stringify(search)}:`, matches.map((match) => match.text), this.#limit - note.length)}${text}`, ""];
|
|
12662
13412
|
}
|
|
12663
13413
|
async #switch(args, context) {
|
|
12664
13414
|
const tab = readBrowserToolString(args, "tab");
|
|
@@ -12681,7 +13431,7 @@ var BrowserToolset = class {
|
|
|
12681
13431
|
await this.#command(front, action, front);
|
|
12682
13432
|
return [renderBrowserReceipt({
|
|
12683
13433
|
action,
|
|
12684
|
-
view: await this.#capture(action, context.signal)
|
|
13434
|
+
view: (await this.#capture(action, context.signal)).view
|
|
12685
13435
|
}), ""];
|
|
12686
13436
|
} finally {
|
|
12687
13437
|
turn.resolve();
|
|
@@ -12690,7 +13440,7 @@ var BrowserToolset = class {
|
|
|
12690
13440
|
async #invoke(tool, synthetic, args, context) {
|
|
12691
13441
|
const turn = await this.#acquire(context.signal);
|
|
12692
13442
|
try {
|
|
12693
|
-
const input = synthetic ? Object.fromEntries(Object.entries(args).filter(([key]) => key !== "
|
|
13443
|
+
const input = synthetic ? Object.fromEntries(Object.entries(args).filter(([key]) => key !== "purpose")) : args;
|
|
12694
13444
|
const command = Promise.resolve(tool.execute(input, context));
|
|
12695
13445
|
return [renderBrowserToolOutput(await this.#command(command, `Called ${tool.name}`, command, context.signal)), ""];
|
|
12696
13446
|
} finally {
|
|
@@ -12770,7 +13520,7 @@ var BrowserToolset = class {
|
|
|
12770
13520
|
}
|
|
12771
13521
|
return turn;
|
|
12772
13522
|
}
|
|
12773
|
-
async #settle(command, action, signal, trusted, record, popups, observation, enter) {
|
|
13523
|
+
async #settle(command, action, signal, trusted, record, popups, observation, enter, focus = false) {
|
|
12774
13524
|
let deadline;
|
|
12775
13525
|
try {
|
|
12776
13526
|
const started = record?.wait({ signal }).then(() => true, () => new Promise(() => void 0));
|
|
@@ -12806,11 +13556,13 @@ var BrowserToolset = class {
|
|
|
12806
13556
|
const unmoved = settled === void 0 && submissions.destinations.length === 0;
|
|
12807
13557
|
const status = settled?.stage === "committed" ? `the page is still loading ${settled.url}` : settled?.stage === "requested" ? `it requested ${settled.url} and the page did not change` : unmoved && submissions.prevented ? BROWSER_TOOL_HANDLED_STATUS : unmoved && submissions.submitted === false && (enter?.explicit === true || enter !== void 0 && submissions.implicit) ? "no form received the submission" : void 0;
|
|
12808
13558
|
const line = enter !== void 0 && (submissions.submitted === true || settled?.reason === "formSubmissionGet" || settled?.reason === "formSubmissionPost") ? enter.action : action;
|
|
13559
|
+
const captured = await this.#capture(line, signal, deadline);
|
|
13560
|
+
const parts = [status, focus && captured.focus !== void 0 ? `focus is on ${captured.focus}` : void 0].filter((part) => part !== void 0);
|
|
12809
13561
|
return renderBrowserReceipt({
|
|
12810
13562
|
action: line,
|
|
12811
13563
|
trusted,
|
|
12812
|
-
...
|
|
12813
|
-
view:
|
|
13564
|
+
...parts.length === 0 ? {} : { status: parts.join("; ") },
|
|
13565
|
+
view: captured.view
|
|
12814
13566
|
});
|
|
12815
13567
|
} finally {
|
|
12816
13568
|
record?.destroy();
|
|
@@ -12977,7 +13729,10 @@ var BrowserToolset = class {
|
|
|
12977
13729
|
}
|
|
12978
13730
|
}
|
|
12979
13731
|
async #capture(action, signal, deadline = performance.now() + BROWSER_TOOL_TIMEOUT_MS) {
|
|
12980
|
-
if (deadline - performance.now() < 1) return
|
|
13732
|
+
if (deadline - performance.now() < 1) return {
|
|
13733
|
+
view: BROWSER_TOOL_DEADLINE_NOTE,
|
|
13734
|
+
focus: void 0
|
|
13735
|
+
};
|
|
12981
13736
|
const receipt = new AbortController();
|
|
12982
13737
|
const options = {
|
|
12983
13738
|
signal: AbortSignal.any([signal, receipt.signal]),
|
|
@@ -12985,16 +13740,34 @@ var BrowserToolset = class {
|
|
|
12985
13740
|
};
|
|
12986
13741
|
try {
|
|
12987
13742
|
const outline = await this.#bounded(this.#cursor.elements.outline(options), deadline, action);
|
|
12988
|
-
return outline === void 0 ?
|
|
13743
|
+
return outline === void 0 ? {
|
|
13744
|
+
view: BROWSER_TOOL_DEADLINE_NOTE,
|
|
13745
|
+
focus: void 0
|
|
13746
|
+
} : {
|
|
13747
|
+
view: outline.text,
|
|
13748
|
+
focus: outline.focus
|
|
13749
|
+
};
|
|
12989
13750
|
} catch (error) {
|
|
12990
13751
|
if (signal.aborted || isBrowserError(error) && error.code === "BROWSER_TOOLSET_RECEIPT") throw error;
|
|
12991
|
-
if (!isBrowserElementError(error) || error.context?.["reason"] !== "GONE") return
|
|
13752
|
+
if (!isBrowserElementError(error) || error.context?.["reason"] !== "GONE") return {
|
|
13753
|
+
view: `(The view could not be read: ${isError(error) ? error.message : String(error)}; call look.)`,
|
|
13754
|
+
focus: void 0
|
|
13755
|
+
};
|
|
12992
13756
|
try {
|
|
12993
13757
|
const outline = await this.#bounded(this.#cursor.elements.outline(options), deadline, action);
|
|
12994
|
-
return outline === void 0 ?
|
|
13758
|
+
return outline === void 0 ? {
|
|
13759
|
+
view: BROWSER_TOOL_DEADLINE_NOTE,
|
|
13760
|
+
focus: void 0
|
|
13761
|
+
} : {
|
|
13762
|
+
view: outline.text,
|
|
13763
|
+
focus: outline.focus
|
|
13764
|
+
};
|
|
12995
13765
|
} catch (retry) {
|
|
12996
13766
|
if (signal.aborted || isBrowserError(retry) && retry.code === "BROWSER_TOOLSET_RECEIPT") throw retry;
|
|
12997
|
-
return
|
|
13767
|
+
return {
|
|
13768
|
+
view: BROWSER_TOOL_CHANGED_NOTE,
|
|
13769
|
+
focus: void 0
|
|
13770
|
+
};
|
|
12998
13771
|
}
|
|
12999
13772
|
} finally {
|
|
13000
13773
|
receipt.abort(new BrowserError("the receipt settled", "BROWSER_TOOLSET_SETTLED"));
|
|
@@ -13228,6 +14001,6 @@ var BrowserToolset = class {
|
|
|
13228
14001
|
}
|
|
13229
14002
|
};
|
|
13230
14003
|
//#endregion
|
|
13231
|
-
export { BASE64_CHARS, BASE64_LOOKUP, BROWSER_ACTION_OUTCOMES, BROWSER_ACTION_STAGES, BROWSER_CODEGEN_BINDING_NAME, BROWSER_CODEGEN_SOURCE, BROWSER_DEFAULT_TIMEOUT_MS, BROWSER_DEFAULT_VIEWPORT_HEIGHT, BROWSER_DEFAULT_VIEWPORT_WIDTH, BROWSER_ELEMENT_REFUSALS, BROWSER_FRAME_WORLD_NAME, BROWSER_HAR_CREATOR, BROWSER_INTERACTIVE_ROLES, BROWSER_JOURNEY_ACTIONS, BROWSER_JOURNEY_EMPTY_LISTING, BROWSER_JOURNEY_FORMAT_VERSION, BROWSER_JOURNEY_IDLE_REFUSAL, BROWSER_JOURNEY_NAME_PATTERN, BROWSER_JOURNEY_NON_STEP_TOOLS, BROWSER_JOURNEY_PARAMETER_PATTERN, BROWSER_JOURNEY_READONLY_REFUSAL, BROWSER_JOURNEY_RECORDING_REFUSAL, BROWSER_JOURNEY_STEP_KEYS, BROWSER_JOURNEY_TOOL_NAMES, BROWSER_KEY_MODIFIERS, BROWSER_MOUSE_BUTTON_MASKS, BROWSER_NAVIGATION_REASONS, BROWSER_OBSERVATION_TOOL_NAMES, BROWSER_OUTLINE_LIMIT, BROWSER_OUTLINE_OMITTED_ROLES, BROWSER_REFERENCE_PREFIX, BROWSER_REGISTRY_ABSENT_CODE, BROWSER_REGISTRY_OUTPUT_LIMIT, BROWSER_RELOAD_NAVIGATION_TYPES, BROWSER_RESULT_LIMIT, BROWSER_RESULT_LIMIT_PATTERN, BROWSER_RESULT_LIMIT_SENTINEL_PREFIX, BROWSER_RUN_ID_PATTERN, BROWSER_SCHEMES, BROWSER_SCREENSHOT_ATTRIBUTE, BROWSER_SNAPSHOT_NODE_LIMIT, BROWSER_STABLE_FRAME_COUNT, BROWSER_STOP_LOADING_TIMEOUT_MS, BROWSER_SUBMIT_KEY, BROWSER_TEXT_ROLES, BROWSER_TOOL_CAPTURE_MS, BROWSER_TOOL_CHANGED_NOTE, BROWSER_TOOL_COPY, BROWSER_TOOL_CUT_FOOTER, BROWSER_TOOL_DEADLINE_NOTE, BROWSER_TOOL_HANDLED_STATUS, BROWSER_TOOL_LIMIT, BROWSER_TOOL_NAMES, BROWSER_TOOL_NAME_PATTERN, BROWSER_TOOL_PENDING_NOTE, BROWSER_TOOL_TIMEOUT_LIMIT_MS, BROWSER_TOOL_TIMEOUT_MS, BROWSER_TOOL_VIEW_FOOTER, BROWSER_TYPED_ROLES, BrowserAccessibility, BrowserClock, BrowserCodegen, BrowserConnectionError, BrowserContext, BrowserCookieManager, BrowserCoverage, BrowserDiagnostics, BrowserElementError, BrowserElementManager, BrowserEmulationManager, BrowserError, BrowserFrame, BrowserHARManager, BrowserJourneyToolset, BrowserKeyboard, BrowserMouse, BrowserNavigationManager, BrowserNetworkManager, BrowserPage, BrowserPageElement, BrowserPerformance, BrowserPermissionManager, BrowserProfiler, BrowserReading, BrowserRecorder, BrowserReplay, BrowserResultLimitError, BrowserScriptManager, BrowserSnapshot, BrowserStepError, BrowserStorageManager, BrowserToolset, BrowserTouch, BrowserTracing, BrowserTransition, BrowserWebSocket, CDPClient, CDPConnectionError, CDPError, CDPTimeoutError, MemoryBrowserJourneyStore, MemoryBrowserRunStore, boundBrowserText, browserHARHeadersToRecord, browserHeadersToProtocol, browserPDFToParams, browserScreenshotToParams, buildBrowserJourney, bytesToText, collectBrowserJourneyBindings, collectBrowserJourneyTextBindings, compileActionabilityFunction, compileBrowserBindingCleanup, compileBrowserBindingResult, compileBrowserBindingSource, compileBrowserJourney, compileBrowserJourneyValue, compileGuardedEvaluateExpression, compileHitFunction, compileQueryWaitExpression, compileReadFunction, compileScreenshotCleanupExpression, compileScreenshotPreparationExpression, compileSelectFunction, compileStorageClearExpression, compileStorageReadExpression, compileStorageRestoreExpression, compileSubmitObserverExpression, compileSubmitReadExpression, compileTextWaitExpression, composeBrowserPoint, computeBrowserButtons, computeBrowserModifiers, concatBytes, cookieToProtocol, createBrowserHAREntry, createBrowserReading, createBrowserRecorder, createBrowserReplay, createBrowserSnapshot, createBrowserToolset, createCDPClient, createMemoryBrowserJourneyStore, createMemoryBrowserRunStore, decodeBase64, deriveBrowserJourneySecret, deriveBrowserJourneyTrigger, deriveBrowserToolSchema, editBrowserJourney, encodeBase64, extractBrowserChord, extractBrowserSlice, filterBrowserOutline, generateBrowserRunId, isBrowserConnectionError, isBrowserElementError, isBrowserError, isBrowserJourneyBinding, isBrowserJourneyTab, isBrowserJourneyTarget, isBrowserJourneyValidationContext, isBrowserNodeQuery, isBrowserNodeVisible, isBrowserResultLimitError, isBrowserSecretBinding, isBrowserStepError, isCDPConnectionError, isCDPError, isCDPTimeoutError, keyToBrowserInput, matchesBrowserCookieURL, matchesBrowserNode, matchesBrowserRoute, matchesBrowserURL, mediaToFeatures, normalizeBrowserJourneyReason, normalizeBrowserKey, normalizeBrowserName, parseBrowserAXString, parseBrowserBindingCall, parseBrowserConsoleMessage, parseBrowserCookiePartition, parseBrowserDownloadProgress, parseBrowserDownloadStart, parseBrowserInvocation, parseBrowserInvocationResult, parseBrowserJourney, parseBrowserJourneyEdit, parseBrowserPageError, parseBrowserRect, parseBrowserReference, parseBrowserRemoval, parseBrowserRequest, parseBrowserRequestFailure, parseBrowserResponse, parseBrowserResponseRecord, parseBrowserRun, parseBrowserSecurity, parseBrowserTiming, parseBrowserTimingRange, parseBrowserTool, parseBrowserWebSocketFrame, parseCodegenActionPayload, parseNumberArray, parseSnapshotString, readBrowserAXValue, readBrowserAccessibility, readBrowserAttributes, readBrowserCookie, readBrowserCookies, readBrowserCoverageRanges, readBrowserFrames, readBrowserHeaders, readBrowserMetrics, readBrowserProfile, readBrowserProfileFrame, readBrowserQuad, readBrowserRemoteValue, readBrowserScriptCoverage, readBrowserScriptIdentifier, readBrowserSnapshot, readBrowserStack, readBrowserStorageEntries, readBrowserStorageOrigin, readBrowserStreamChunk, readBrowserStyleCoverage, readBrowserToolString, readBrowserWorld, readEvaluationResult, readRareBooleanData, readRareIntegerData, readRareStringData, renderBrowserElement, renderBrowserJourney, renderBrowserJourneyFault, renderBrowserOutline, renderBrowserReceipt, renderBrowserRun, renderBrowserRunResult, renderBrowserToolOutput, requireBrowserReference, requireBrowserString, resolveBrowserJourneyBinding, settleBrowserTeardown, textToBytes, validateBrowserAccessibilityOptions, validateBrowserContextOptions, validateBrowserEmulationOptions, validateBrowserHAR, validateBrowserInputOptions, validateBrowserJourney, validateBrowserJourneyEdit, validateBrowserJourneyName, validateBrowserJourneyParameter, validateBrowserJourneyStep, validateBrowserPoint, validateBrowserRange, validateBrowserRun, validateBrowserStorePage, validateBrowserTimeout, validateBrowserToolArguments, validateBrowserViewport };
|
|
14004
|
+
export { BASE64_CHARS, BASE64_LOOKUP, BROWSER_ACTION_OUTCOMES, BROWSER_ACTION_STAGES, BROWSER_CODEGEN_BINDING_NAME, BROWSER_CODEGEN_SOURCE, BROWSER_CONTEXT_LOSS_PATTERN, BROWSER_DEFAULT_TIMEOUT_MS, BROWSER_DEFAULT_VIEWPORT_HEIGHT, BROWSER_DEFAULT_VIEWPORT_WIDTH, BROWSER_ELEMENT_REFUSALS, BROWSER_FRAME_WORLD_NAME, BROWSER_HAR_CREATOR, BROWSER_INTERACTIVE_ROLES, BROWSER_JOURNEY_ACTIONS, BROWSER_JOURNEY_EMPTY_LISTING, BROWSER_JOURNEY_FORMAT_VERSION, BROWSER_JOURNEY_IDLE_REFUSAL, BROWSER_JOURNEY_NAME_PATTERN, BROWSER_JOURNEY_NON_STEP_TOOLS, BROWSER_JOURNEY_PARAMETER_PATTERN, BROWSER_JOURNEY_READONLY_REFUSAL, BROWSER_JOURNEY_RECORDING_REFUSAL, BROWSER_JOURNEY_STEP_KEYS, BROWSER_JOURNEY_TOOL_NAMES, BROWSER_KEY_MODIFIERS, BROWSER_MOUSE_BUTTON_MASKS, BROWSER_NAVIGATION_REASONS, BROWSER_OBSERVATION_TOOL_NAMES, BROWSER_OUTLINE_LIMIT, BROWSER_OUTLINE_OMITTED_ROLES, BROWSER_REFERENCE_PREFIX, BROWSER_REGISTRY_ABSENT_CODE, BROWSER_REGISTRY_OUTPUT_LIMIT, BROWSER_RELOAD_NAVIGATION_TYPES, BROWSER_RESULT_LIMIT, BROWSER_RESULT_LIMIT_PATTERN, BROWSER_RESULT_LIMIT_SENTINEL_PREFIX, BROWSER_RUN_ID_PATTERN, BROWSER_SCHEMES, BROWSER_SCREENSHOT_ATTRIBUTE, BROWSER_SEARCH_PATTERN, BROWSER_SNAPSHOT_NODE_LIMIT, BROWSER_STABLE_FRAME_COUNT, BROWSER_STOP_LOADING_TIMEOUT_MS, BROWSER_SUBMIT_KEY, BROWSER_TEXT_ROLES, BROWSER_TOOL_CAPTURE_MS, BROWSER_TOOL_CHANGED_NOTE, BROWSER_TOOL_COPY, BROWSER_TOOL_CUT_FOOTER, BROWSER_TOOL_DEADLINE_NOTE, BROWSER_TOOL_HANDLED_STATUS, BROWSER_TOOL_LIMIT, BROWSER_TOOL_NAMES, BROWSER_TOOL_NAME_PATTERN, BROWSER_TOOL_PENDING_NOTE, BROWSER_TOOL_TIMEOUT_LIMIT_MS, BROWSER_TOOL_TIMEOUT_MS, BROWSER_TOOL_VIEW_FOOTER, BROWSER_TYPED_ROLES, BROWSER_WAIT_EVENTS, BrowserAccessibility, BrowserClock, BrowserCodegen, BrowserConnectionError, BrowserContext, BrowserCookieManager, BrowserCoverage, BrowserDiagnostics, BrowserElementError, BrowserElementManager, BrowserEmulationManager, BrowserError, BrowserFrame, BrowserHARManager, BrowserJourneyToolset, BrowserKeyboard, BrowserMouse, BrowserNavigationManager, BrowserNetworkManager, BrowserPage, BrowserPageElement, BrowserPerformance, BrowserPermissionManager, BrowserProfiler, BrowserReading, BrowserRecorder, BrowserReplay, BrowserResultLimitError, BrowserScriptManager, BrowserSnapshot, BrowserStepError, BrowserStorageManager, BrowserToolset, BrowserTouch, BrowserTracing, BrowserTransition, BrowserWebSocket, CDPClient, CDPConnectionError, CDPError, CDPTimeoutError, MemoryBrowserJourneyStore, MemoryBrowserRunStore, boundBrowserText, browserHARHeadersToRecord, browserHeadersToProtocol, browserPDFToParams, browserScreenshotToParams, buildBrowserJourney, bytesToText, collectBrowserJourneyBindings, collectBrowserJourneyTextBindings, collectBrowserWords, compileActionabilityFunction, compileBrowserBindingCleanup, compileBrowserBindingResult, compileBrowserBindingSource, compileBrowserJourney, compileBrowserJourneyValue, compileGuardedEvaluateExpression, compileHitFunction, compileQueryWaitExpression, compileReadFunction, compileScreenshotCleanupExpression, compileScreenshotPreparationExpression, compileSelectFunction, compileStorageClearExpression, compileStorageReadExpression, compileStorageRestoreExpression, compileSubmitObserverExpression, compileSubmitReadExpression, compileTextWaitExpression, composeBrowserPoint, computeBrowserButtons, computeBrowserModifiers, concatBytes, cookieToProtocol, createBrowserHAREntry, createBrowserReading, createBrowserRecorder, createBrowserReplay, createBrowserSnapshot, createBrowserToolset, createCDPClient, createMemoryBrowserJourneyStore, createMemoryBrowserRunStore, decodeBase64, deriveBrowserJourneySecret, deriveBrowserJourneyTrigger, deriveBrowserToolSchema, editBrowserJourney, encodeBase64, extractBrowserChord, extractBrowserSlice, filterBrowserOutline, generateBrowserRunId, isBrowserConnectionError, isBrowserElementError, isBrowserError, isBrowserJourneyBinding, isBrowserJourneyTab, isBrowserJourneyTarget, isBrowserJourneyValidationContext, isBrowserNodeQuery, isBrowserNodeVisible, isBrowserResultLimitError, isBrowserSecretBinding, isBrowserStepError, isCDPConnectionError, isCDPError, isCDPTimeoutError, keyToBrowserInput, matchesBrowserCookieURL, matchesBrowserNode, matchesBrowserRoute, matchesBrowserURL, mediaToFeatures, normalizeBrowserJourneyReason, normalizeBrowserKey, normalizeBrowserName, parseBrowserAXString, parseBrowserBindingCall, parseBrowserConsoleMessage, parseBrowserCookiePartition, parseBrowserDownloadProgress, parseBrowserDownloadStart, parseBrowserInvocation, parseBrowserInvocationResult, parseBrowserJourney, parseBrowserJourneyEdit, parseBrowserPageError, parseBrowserRect, parseBrowserReference, parseBrowserRemoval, parseBrowserRequest, parseBrowserRequestFailure, parseBrowserResponse, parseBrowserResponseRecord, parseBrowserRun, parseBrowserSecurity, parseBrowserTiming, parseBrowserTimingRange, parseBrowserTool, parseBrowserWebSocketFrame, parseCodegenActionPayload, parseNumberArray, parseSnapshotString, readBrowserAXValue, readBrowserAccessibility, readBrowserAttributes, readBrowserCookie, readBrowserCookies, readBrowserCoverageRanges, readBrowserFrames, readBrowserHeaders, readBrowserMetrics, readBrowserProfile, readBrowserProfileFrame, readBrowserQuad, readBrowserRemoteValue, readBrowserScriptCoverage, readBrowserScriptIdentifier, readBrowserSnapshot, readBrowserStack, readBrowserStorageEntries, readBrowserStorageOrigin, readBrowserStreamChunk, readBrowserStyleCoverage, readBrowserToolString, readBrowserWorld, readEvaluationResult, readRareBooleanData, readRareIntegerData, readRareStringData, renderBrowserElement, renderBrowserJourney, renderBrowserJourneyFault, renderBrowserMatches, renderBrowserOutline, renderBrowserOutlineRow, renderBrowserReceipt, renderBrowserRun, renderBrowserRunResult, renderBrowserToolOutput, requireBrowserReference, requireBrowserString, resolveBrowserJourneyBinding, scanBrowserOutline, scanBrowserText, settleBrowserTeardown, textToBytes, validateBrowserAccessibilityOptions, validateBrowserContextOptions, validateBrowserEmulationOptions, validateBrowserHAR, validateBrowserInputOptions, validateBrowserJourney, validateBrowserJourneyEdit, validateBrowserJourneyName, validateBrowserJourneyParameter, validateBrowserJourneyStep, validateBrowserPoint, validateBrowserRange, validateBrowserRun, validateBrowserStorePage, validateBrowserTimeout, validateBrowserToolArguments, validateBrowserViewport };
|
|
13232
14005
|
|
|
13233
14006
|
//# sourceMappingURL=index.js.map
|