@orkestrel/browser 0.0.20 → 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 +963 -173
- 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 +957 -175
- 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 +5 -5
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,17 +1680,265 @@ 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}`];
|
|
1930
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
1931
|
+
for (const node of nodes) {
|
|
1932
|
+
let parents = sessions.get(node.session);
|
|
1933
|
+
if (parents === void 0) {
|
|
1934
|
+
parents = /* @__PURE__ */ new Map();
|
|
1935
|
+
sessions.set(node.session, parents);
|
|
1936
|
+
}
|
|
1937
|
+
if (!parents.has(node.id)) parents.set(node.id, node);
|
|
1938
|
+
}
|
|
1638
1939
|
let count = 0;
|
|
1639
1940
|
let total = 0;
|
|
1941
|
+
let focus;
|
|
1640
1942
|
for (const node of nodes) {
|
|
1641
1943
|
if (node.ignored || BROWSER_OUTLINE_OMITTED_ROLES.has(node.role ?? "")) continue;
|
|
1642
1944
|
const name = normalizeBrowserName(node.name ?? "");
|
|
@@ -1645,20 +1947,16 @@ function renderBrowserOutline(url, title, nodes, limit) {
|
|
|
1645
1947
|
continue;
|
|
1646
1948
|
}
|
|
1647
1949
|
if (node.role === "StaticText") {
|
|
1648
|
-
const parent =
|
|
1950
|
+
const parent = node.parent === void 0 ? void 0 : sessions.get(node.session)?.get(node.parent);
|
|
1649
1951
|
if (name !== "" && name !== normalizeBrowserName(parent?.name ?? "")) rows.push(name);
|
|
1650
1952
|
continue;
|
|
1651
1953
|
}
|
|
1652
1954
|
if (node.reference === void 0) continue;
|
|
1653
1955
|
total += 1;
|
|
1956
|
+
if (node.properties["focused"] === true) focus = node;
|
|
1654
1957
|
if (count >= limit) continue;
|
|
1655
1958
|
count += 1;
|
|
1656
|
-
|
|
1657
|
-
if (node.value !== void 0 && node.value !== "") row += ` value=${JSON.stringify(String(node.value))}`;
|
|
1658
|
-
if (node.properties["checked"] === true || node.properties["checked"] === "true") row += " [checked]";
|
|
1659
|
-
if (node.properties["disabled"] === true) row += " [disabled]";
|
|
1660
|
-
if (node.tool !== void 0) row += ` [tool=${node.tool}]`;
|
|
1661
|
-
rows.push(row);
|
|
1959
|
+
rows.push(renderBrowserOutlineRow(node));
|
|
1662
1960
|
}
|
|
1663
1961
|
rows.push(`(${count} of ${total} elements)`);
|
|
1664
1962
|
return {
|
|
@@ -1666,7 +1964,9 @@ function renderBrowserOutline(url, title, nodes, limit) {
|
|
|
1666
1964
|
title,
|
|
1667
1965
|
text: rows.join("\n"),
|
|
1668
1966
|
count,
|
|
1669
|
-
total
|
|
1967
|
+
total,
|
|
1968
|
+
matches: search === void 0 ? [] : scanBrowserOutline(nodes, search).map(renderBrowserOutlineRow),
|
|
1969
|
+
focus: focus === void 0 ? void 0 : renderBrowserOutlineRow(focus)
|
|
1670
1970
|
};
|
|
1671
1971
|
}
|
|
1672
1972
|
/**
|
|
@@ -1747,27 +2047,27 @@ function renderBrowserToolOutput(value) {
|
|
|
1747
2047
|
return (rendered.success ? rendered.value ?? String(value) : "[Unserializable tool output]").slice(0, BROWSER_REGISTRY_OUTPUT_LIMIT);
|
|
1748
2048
|
}
|
|
1749
2049
|
/**
|
|
1750
|
-
* 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.
|
|
1751
2051
|
* @param schema - Authored input schema, or undefined for a parameterless tool
|
|
1752
|
-
* @returns An advertised schema, or undefined when the authored `
|
|
1753
|
-
* @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.
|
|
1754
2054
|
*/
|
|
1755
2055
|
function deriveBrowserToolSchema(schema) {
|
|
1756
2056
|
const properties = isRecord(schema?.["properties"]) ? schema["properties"] : {};
|
|
1757
2057
|
const required = isArray(schema?.["required"]) ? schema["required"].filter(isString) : [];
|
|
1758
|
-
if (Object.hasOwn(properties, "
|
|
2058
|
+
if (Object.hasOwn(properties, "purpose") && !required.includes("purpose")) return void 0;
|
|
1759
2059
|
if (required.length > 0) return schema;
|
|
1760
2060
|
return {
|
|
1761
2061
|
...schema,
|
|
1762
2062
|
type: "object",
|
|
1763
2063
|
properties: {
|
|
1764
2064
|
...properties,
|
|
1765
|
-
|
|
2065
|
+
purpose: {
|
|
1766
2066
|
type: "string",
|
|
1767
2067
|
description: "Describe the purpose of this action."
|
|
1768
2068
|
}
|
|
1769
2069
|
},
|
|
1770
|
-
required: ["
|
|
2070
|
+
required: ["purpose"]
|
|
1771
2071
|
};
|
|
1772
2072
|
}
|
|
1773
2073
|
/**
|
|
@@ -1779,8 +2079,8 @@ function deriveBrowserToolSchema(schema) {
|
|
|
1779
2079
|
* units, one fewer when the last would split a surrogate pair (so a limit of 1 before a pair keeps
|
|
1780
2080
|
* nothing), followed by `\n[characters 0–END of TOTAL; FOOTER]`. The footer has no default, so
|
|
1781
2081
|
* every caller states what the cut result's reader does next: the toolset passes
|
|
1782
|
-
* `BROWSER_TOOL_VIEW_FOOTER` for
|
|
1783
|
-
* 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.
|
|
1784
2084
|
*
|
|
1785
2085
|
* @param text - The page-authored or composed string
|
|
1786
2086
|
* @param limit - The most characters kept before the footer, a positive integer
|
|
@@ -1817,8 +2117,8 @@ function boundBrowserText(text, limit, footer) {
|
|
|
1817
2117
|
* ```ts
|
|
1818
2118
|
* import { BROWSER_TOOL_COPY, validateBrowserToolArguments } from '@orkestrel/browser'
|
|
1819
2119
|
*
|
|
1820
|
-
* validateBrowserToolArguments(BROWSER_TOOL_COPY.look, {
|
|
1821
|
-
* // 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.'
|
|
1822
2122
|
* ```
|
|
1823
2123
|
*/
|
|
1824
2124
|
function validateBrowserToolArguments(definition, args) {
|
|
@@ -3598,7 +3898,7 @@ function renderBrowserJourney(journey) {
|
|
|
3598
3898
|
return `${step.id} type ${secret ? "(secret)" : JSON.stringify(text)}${suffix} into ${name}${step.arguments["submit"] === true ? ", submit" : ""}`;
|
|
3599
3899
|
}
|
|
3600
3900
|
case "press": return `${step.id} press ${text}${suffix}`;
|
|
3601
|
-
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" : ""}`;
|
|
3602
3902
|
case "dialog": return `${step.id} dialog ${step.arguments["accept"] === true ? "accept" : "dismiss"}${binding === void 0 ? "" : ` ${JSON.stringify(text)}${suffix}`}`;
|
|
3603
3903
|
case "switch": return `${step.id} switch ${JSON.stringify(step.tab?.title)} ${step.tab?.url}`;
|
|
3604
3904
|
case "unresolved": return `${step.id} unresolved: ${step.gap}`;
|
|
@@ -3906,46 +4206,55 @@ function buildBrowserJourney(steps, options) {
|
|
|
3906
4206
|
//#endregion
|
|
3907
4207
|
//#region src/core/compilers.ts
|
|
3908
4208
|
/**
|
|
3909
|
-
* 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.
|
|
3910
4214
|
* @param deadline - Maximum time in milliseconds
|
|
3911
4215
|
* @param key - Isolated-world property owning this observer
|
|
3912
|
-
* @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
|
|
3913
4217
|
* @returns Promise expression resolving true on a match or change, false at the deadline
|
|
3914
4218
|
*/
|
|
3915
4219
|
function compileQueryWaitExpression(deadline, key, predicate) {
|
|
3916
4220
|
return `new Promise((resolve) => {
|
|
3917
|
-
let
|
|
4221
|
+
let task
|
|
3918
4222
|
let timer
|
|
3919
4223
|
let observer
|
|
4224
|
+
const events = new AbortController()
|
|
3920
4225
|
const finish = (value) => {
|
|
3921
4226
|
observer?.disconnect()
|
|
3922
|
-
|
|
4227
|
+
events.abort()
|
|
4228
|
+
if (task !== undefined) clearTimeout(task)
|
|
3923
4229
|
clearTimeout(timer)
|
|
3924
4230
|
delete globalThis[${JSON.stringify(key)}]
|
|
3925
4231
|
resolve(value)
|
|
3926
4232
|
}
|
|
3927
4233
|
const check = () => {
|
|
3928
|
-
|
|
4234
|
+
task = undefined
|
|
3929
4235
|
if (${predicate ?? "true"}) finish(true)
|
|
3930
4236
|
}
|
|
3931
4237
|
globalThis[${JSON.stringify(key)}] = () => finish(false)
|
|
3932
|
-
|
|
3933
|
-
if (
|
|
3934
|
-
}
|
|
4238
|
+
const schedule = () => {
|
|
4239
|
+
if (task === undefined) task = setTimeout(check, 0)
|
|
4240
|
+
}
|
|
4241
|
+
observer = new MutationObserver(schedule)
|
|
3935
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 })
|
|
3936
4244
|
timer = setTimeout(() => finish(false), ${JSON.stringify(deadline)})
|
|
3937
4245
|
${predicate === void 0 ? "" : "check()"}
|
|
3938
4246
|
})`;
|
|
3939
4247
|
}
|
|
3940
4248
|
/**
|
|
3941
|
-
* Compiles a visible
|
|
4249
|
+
* Compiles a wait for the presence or absence of visible text, coalesced by tasks.
|
|
3942
4250
|
* @param text - Text to find in the main document body
|
|
3943
4251
|
* @param deadline - Maximum time in milliseconds
|
|
3944
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`
|
|
3945
4254
|
* @returns Mutation-driven promise expression with one deadline
|
|
3946
4255
|
*/
|
|
3947
|
-
function compileTextWaitExpression(text, deadline, key) {
|
|
3948
|
-
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)})`);
|
|
3949
4258
|
}
|
|
3950
4259
|
/**
|
|
3951
4260
|
* Compiles text selection or select-option assignment against the resolved element.
|
|
@@ -4257,20 +4566,337 @@ ${expression}
|
|
|
4257
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 })()`;
|
|
4258
4567
|
}
|
|
4259
4568
|
/**
|
|
4260
|
-
* 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.
|
|
4261
4570
|
*
|
|
4262
4571
|
* @remarks
|
|
4263
|
-
* The
|
|
4264
|
-
*
|
|
4265
|
-
*
|
|
4266
|
-
*
|
|
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}.
|
|
4267
4576
|
*
|
|
4268
4577
|
* @returns A function declaration source
|
|
4269
4578
|
*/
|
|
4270
4579
|
function compileReadFunction() {
|
|
4271
|
-
return `function() {
|
|
4272
|
-
const
|
|
4273
|
-
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
|
|
4274
4900
|
}`;
|
|
4275
4901
|
}
|
|
4276
4902
|
/**
|
|
@@ -4424,7 +5050,7 @@ function compileActionabilityFunction(options) {
|
|
|
4424
5050
|
if (${JSON.stringify(options.enabled === true)} && this.matches(':disabled')) throw new Error('Element is disabled')
|
|
4425
5051
|
if (${JSON.stringify(options.editable === true)} && (this.matches('[readonly]') || (!this.isContentEditable && !('value' in this)))) throw new Error('Element is not editable')
|
|
4426
5052
|
let previous
|
|
4427
|
-
for (let index = 0; index < 2; index += 1) {
|
|
5053
|
+
for (let index = 0; index < ${options.stable === true ? 2 : 0}; index += 1) {
|
|
4428
5054
|
await new Promise((resolve) => requestAnimationFrame(resolve))
|
|
4429
5055
|
const rect = this.getBoundingClientRect()
|
|
4430
5056
|
const current = [rect.x, rect.y, rect.width, rect.height]
|
|
@@ -5557,7 +6183,7 @@ var BrowserCodegen = class {
|
|
|
5557
6183
|
* title: 'Example',
|
|
5558
6184
|
* html: '<nav>Menu</nav><main><p>Body</p></main>',
|
|
5559
6185
|
* })
|
|
5560
|
-
* reading.markdown() // { text: 'Body', offset: 0, total: 4 }
|
|
6186
|
+
* reading.markdown({ distill: true }) // { text: 'Body', offset: 0, total: 4 }
|
|
5561
6187
|
* ```
|
|
5562
6188
|
*/
|
|
5563
6189
|
var BrowserReading = class {
|
|
@@ -5589,7 +6215,7 @@ var BrowserReading = class {
|
|
|
5589
6215
|
return this.#navigation !== void 0 && this.#navigation() !== this.#epoch;
|
|
5590
6216
|
}
|
|
5591
6217
|
markdown(options) {
|
|
5592
|
-
const distill = options?.distill ??
|
|
6218
|
+
const distill = options?.distill ?? false;
|
|
5593
6219
|
let text = this.#markdowns.get(distill);
|
|
5594
6220
|
if (text === void 0) {
|
|
5595
6221
|
text = renderMarkdown(htmlToMarkdown(this.#source(distill).document));
|
|
@@ -5598,7 +6224,7 @@ var BrowserReading = class {
|
|
|
5598
6224
|
return extractBrowserSlice(text, options?.offset, options?.limit);
|
|
5599
6225
|
}
|
|
5600
6226
|
text(options) {
|
|
5601
|
-
const distill = options?.distill ??
|
|
6227
|
+
const distill = options?.distill ?? false;
|
|
5602
6228
|
let text = this.#texts.get(distill);
|
|
5603
6229
|
if (text === void 0) {
|
|
5604
6230
|
text = renderText(this.#source(distill).document);
|
|
@@ -5607,7 +6233,10 @@ var BrowserReading = class {
|
|
|
5607
6233
|
return extractBrowserSlice(text, options?.offset, options?.limit);
|
|
5608
6234
|
}
|
|
5609
6235
|
#source(distill) {
|
|
5610
|
-
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);
|
|
5611
6240
|
this.#distilled ??= this.#html.distill({ base: this.#url });
|
|
5612
6241
|
return this.#distilled;
|
|
5613
6242
|
}
|
|
@@ -5678,8 +6307,7 @@ var BrowserPageElement = class {
|
|
|
5678
6307
|
async read(options) {
|
|
5679
6308
|
const epoch = this.#epoch();
|
|
5680
6309
|
const capture = await this.#call(`function() {
|
|
5681
|
-
const capture = (${compileReadFunction()})()
|
|
5682
|
-
capture.html = this.outerHTML
|
|
6310
|
+
const capture = (${compileReadFunction()})(this)
|
|
5683
6311
|
return ${compileGuardedEvaluateExpression("capture", BROWSER_RESULT_LIMIT)}
|
|
5684
6312
|
}`, options);
|
|
5685
6313
|
if (!isRecord(capture)) throw new BrowserElementError(this.reference, "UNKNOWN", "returned an invalid reading");
|
|
@@ -5880,6 +6508,7 @@ var BrowserPageElement = class {
|
|
|
5880
6508
|
}
|
|
5881
6509
|
async #pointer(options) {
|
|
5882
6510
|
this.#assert(options);
|
|
6511
|
+
await this.#input.page.send("Page.bringToFront", void 0, options);
|
|
5883
6512
|
await this.#input.client.send("DOM.scrollIntoViewIfNeeded", { backendNodeId: this.#input.backend }, {
|
|
5884
6513
|
session: this.#input.node.session,
|
|
5885
6514
|
...options
|
|
@@ -6000,7 +6629,7 @@ var BrowserElementManager = class {
|
|
|
6000
6629
|
...options
|
|
6001
6630
|
});
|
|
6002
6631
|
this.#assertCapture(this.#input.page.id, epoch);
|
|
6003
|
-
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);
|
|
6004
6633
|
} catch (error) {
|
|
6005
6634
|
if (options?.signal?.aborted === true || this.#changes === changes) throw error;
|
|
6006
6635
|
this.#lifetime.signal.throwIfAborted();
|
|
@@ -6090,34 +6719,52 @@ var BrowserElementManager = class {
|
|
|
6090
6719
|
const signal = options?.signal === void 0 ? this.#lifetime.signal : AbortSignal.any([options.signal, this.#lifetime.signal]);
|
|
6091
6720
|
while (true) {
|
|
6092
6721
|
signal.throwIfAborted();
|
|
6093
|
-
const remaining = Math.max(0, end - performance.now());
|
|
6094
|
-
const call = {
|
|
6095
|
-
timeout: remaining,
|
|
6096
|
-
signal
|
|
6097
|
-
};
|
|
6098
|
-
await this.#input.ready(call);
|
|
6099
|
-
const frame = this.#scopeFrame(query.within);
|
|
6100
|
-
const session = await this.#input.resolve(frame);
|
|
6101
|
-
const context = await this.#input.world(frame, session, call);
|
|
6102
6722
|
const key = `__browserQueryWait${++this.#sequence}`;
|
|
6103
|
-
|
|
6104
|
-
|
|
6105
|
-
|
|
6106
|
-
returnByValue: true,
|
|
6107
|
-
awaitPromise: true
|
|
6108
|
-
}, {
|
|
6109
|
-
session,
|
|
6110
|
-
timeout: remaining + 1e3,
|
|
6111
|
-
signal
|
|
6112
|
-
}).then((result) => ({ result }), (error) => ({ error }));
|
|
6723
|
+
let session;
|
|
6724
|
+
let context;
|
|
6725
|
+
const changes = this.#changes;
|
|
6113
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 }));
|
|
6114
6752
|
const found = await this.find(query, call);
|
|
6115
6753
|
if (options?.absent === true ? found.length === 0 : found.length > 0) return found;
|
|
6116
6754
|
const outcome = await pending;
|
|
6117
6755
|
if ("error" in outcome) throw outcome.error;
|
|
6118
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
|
+
});
|
|
6119
6766
|
} finally {
|
|
6120
|
-
await this.#input.client.send("Runtime.evaluate", {
|
|
6767
|
+
if (context !== void 0 && session !== void 0) await this.#input.client.send("Runtime.evaluate", {
|
|
6121
6768
|
expression: `globalThis[${JSON.stringify(key)}]?.()`,
|
|
6122
6769
|
contextId: context,
|
|
6123
6770
|
returnByValue: true
|
|
@@ -6128,6 +6775,11 @@ var BrowserElementManager = class {
|
|
|
6128
6775
|
}
|
|
6129
6776
|
}
|
|
6130
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
|
+
}
|
|
6131
6783
|
element(reference) {
|
|
6132
6784
|
const canonical = parseBrowserReference(reference);
|
|
6133
6785
|
return [...this.#records.values()].find((entry) => entry.node.reference === canonical)?.element;
|
|
@@ -6635,7 +7287,7 @@ var BrowserRegistry = class {
|
|
|
6635
7287
|
pending.reject(error);
|
|
6636
7288
|
}
|
|
6637
7289
|
async #executeAdopted(tool, synthetic, input, context) {
|
|
6638
|
-
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;
|
|
6639
7291
|
const result = await this.execute(tool, parameters, { signal: context.signal });
|
|
6640
7292
|
if (result.status !== "Completed") throw new BrowserError(`Browser tool ${result.status}: ${result.error ?? "No error description"}`.slice(0, BROWSER_REGISTRY_OUTPUT_LIMIT));
|
|
6641
7293
|
return renderBrowserToolOutput(result.output);
|
|
@@ -8952,6 +9604,8 @@ var BrowserPage = class BrowserPage extends BrowserFrame {
|
|
|
8952
9604
|
#epoch = 0;
|
|
8953
9605
|
#floor = 0;
|
|
8954
9606
|
#closed = false;
|
|
9607
|
+
#waitRelease = new AbortController();
|
|
9608
|
+
#textWaits = /* @__PURE__ */ new Map();
|
|
8955
9609
|
#codegen;
|
|
8956
9610
|
#registry;
|
|
8957
9611
|
#codegenStart = new BrowserTransition();
|
|
@@ -9002,6 +9656,7 @@ var BrowserPage = class BrowserPage extends BrowserFrame {
|
|
|
9002
9656
|
session: sessionId,
|
|
9003
9657
|
resolve: this.#resolveFrameSession.bind(this),
|
|
9004
9658
|
world: this.#world.bind(this),
|
|
9659
|
+
recover: this.#recoverReadiness.bind(this),
|
|
9005
9660
|
reference: this.#reference,
|
|
9006
9661
|
ready: this.#ready.bind(this)
|
|
9007
9662
|
});
|
|
@@ -9065,6 +9720,10 @@ var BrowserPage = class BrowserPage extends BrowserFrame {
|
|
|
9065
9720
|
}
|
|
9066
9721
|
async wait(text, options) {
|
|
9067
9722
|
this.assert();
|
|
9723
|
+
options = {
|
|
9724
|
+
...options,
|
|
9725
|
+
signal: options?.signal === void 0 ? this.#waitRelease.signal : AbortSignal.any([options.signal, this.#waitRelease.signal])
|
|
9726
|
+
};
|
|
9068
9727
|
const timeout = options?.timeout ?? 3e4;
|
|
9069
9728
|
validateBrowserTimeout(timeout);
|
|
9070
9729
|
const end = performance.now() + timeout;
|
|
@@ -9077,9 +9736,10 @@ var BrowserPage = class BrowserPage extends BrowserFrame {
|
|
|
9077
9736
|
});
|
|
9078
9737
|
const context = await this.#world(this.id, this.#sessionId, options);
|
|
9079
9738
|
const key = `__browserTextWait${++this.#waitSequence}`;
|
|
9739
|
+
this.#textWaits.set(key, context);
|
|
9080
9740
|
try {
|
|
9081
9741
|
if (readEvaluationResult(await this.send("Runtime.evaluate", {
|
|
9082
|
-
expression: compileTextWaitExpression(text, remaining, key),
|
|
9742
|
+
expression: compileTextWaitExpression(text, remaining, key, options?.absent === true),
|
|
9083
9743
|
contextId: context,
|
|
9084
9744
|
returnByValue: true,
|
|
9085
9745
|
awaitPromise: true
|
|
@@ -9093,23 +9753,20 @@ var BrowserPage = class BrowserPage extends BrowserFrame {
|
|
|
9093
9753
|
return;
|
|
9094
9754
|
} catch (error) {
|
|
9095
9755
|
if (options?.signal?.aborted === true) {
|
|
9096
|
-
await this
|
|
9097
|
-
expression: `globalThis[${JSON.stringify(key)}]?.()`,
|
|
9098
|
-
contextId: context,
|
|
9099
|
-
returnByValue: true
|
|
9100
|
-
}, { timeout: 1e3 }).catch(() => void 0);
|
|
9756
|
+
if (!this.#closed) await this.#releaseTextWait(key, context);
|
|
9101
9757
|
throw options.signal.reason;
|
|
9102
9758
|
}
|
|
9103
|
-
if (!isError(error) ||
|
|
9104
|
-
this.#dom = void 0;
|
|
9759
|
+
if (!isError(error) || !BROWSER_CONTEXT_LOSS_PATTERN.test(error.message)) throw error;
|
|
9105
9760
|
if (performance.now() >= end) throw new BrowserError("Browser text wait timed out", "BROWSER_WAIT_TIMEOUT", {
|
|
9106
9761
|
text,
|
|
9107
9762
|
timeout
|
|
9108
9763
|
});
|
|
9109
|
-
await this.#
|
|
9764
|
+
await this.#recoverReadiness({
|
|
9110
9765
|
...options,
|
|
9111
9766
|
timeout: Math.max(0, end - performance.now())
|
|
9112
9767
|
});
|
|
9768
|
+
} finally {
|
|
9769
|
+
this.#textWaits.delete(key);
|
|
9113
9770
|
}
|
|
9114
9771
|
}
|
|
9115
9772
|
}
|
|
@@ -9497,6 +10154,8 @@ var BrowserPage = class BrowserPage extends BrowserFrame {
|
|
|
9497
10154
|
return release;
|
|
9498
10155
|
}
|
|
9499
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)));
|
|
9500
10159
|
this.#unhold();
|
|
9501
10160
|
this.#announcement.reject(new BrowserError("Browser session ended"));
|
|
9502
10161
|
this.#client.unsubscribe("Page.lifecycleEvent", this.#lifecycleHandler, this.#sessionId);
|
|
@@ -9546,6 +10205,16 @@ var BrowserPage = class BrowserPage extends BrowserFrame {
|
|
|
9546
10205
|
}
|
|
9547
10206
|
this.#steps.destroy();
|
|
9548
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
|
+
}
|
|
9549
10218
|
#frame(frame) {
|
|
9550
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));
|
|
9551
10220
|
}
|
|
@@ -9589,6 +10258,10 @@ var BrowserPage = class BrowserPage extends BrowserFrame {
|
|
|
9589
10258
|
#rejectSeed(seed) {
|
|
9590
10259
|
if (this.#seed === seed) this.#seed = void 0;
|
|
9591
10260
|
}
|
|
10261
|
+
#recoverReadiness(options) {
|
|
10262
|
+
this.#dom = void 0;
|
|
10263
|
+
return this.#parkReadiness(options);
|
|
10264
|
+
}
|
|
9592
10265
|
async #seedReadiness(options) {
|
|
9593
10266
|
const document = this.#floor;
|
|
9594
10267
|
const context = await this.#world(this.id, this.#sessionId, options);
|
|
@@ -9636,24 +10309,44 @@ var BrowserPage = class BrowserPage extends BrowserFrame {
|
|
|
9636
10309
|
this.#dom = this.#loader ?? "initial";
|
|
9637
10310
|
for (const id of this.#readiness.keys()) this.#settleReadiness(id)?.resolve();
|
|
9638
10311
|
}
|
|
9639
|
-
#world(frame, session, options) {
|
|
10312
|
+
async #world(frame, session, options) {
|
|
10313
|
+
options?.signal?.throwIfAborted();
|
|
9640
10314
|
const cached = this.#worlds.get(frame);
|
|
9641
10315
|
if (cached !== void 0) return Promise.resolve(cached.context);
|
|
9642
10316
|
const existing = this.#creating.get(frame);
|
|
9643
|
-
if (existing !== void 0) return existing.promise;
|
|
10317
|
+
if (existing !== void 0) return await this.#awaitWorld(existing.promise, options);
|
|
9644
10318
|
const pending = this.#client.send("Page.createIsolatedWorld", {
|
|
9645
10319
|
frameId: frame,
|
|
9646
10320
|
worldName: BROWSER_FRAME_WORLD_NAME
|
|
9647
10321
|
}, {
|
|
9648
10322
|
session,
|
|
9649
|
-
|
|
10323
|
+
signal: this.#waitRelease.signal
|
|
9650
10324
|
}).then(this.#decodeWorld.bind(this, frame));
|
|
9651
10325
|
this.#creating.set(frame, {
|
|
9652
10326
|
session,
|
|
9653
10327
|
promise: pending
|
|
9654
10328
|
});
|
|
9655
10329
|
pending.then(this.#publishWorld.bind(this, frame, session, pending), this.#settleWorld.bind(this, frame, pending));
|
|
9656
|
-
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);
|
|
9657
10350
|
}
|
|
9658
10351
|
#decodeWorld(frame, world) {
|
|
9659
10352
|
return readBrowserWorld(world, frame);
|
|
@@ -11484,7 +12177,7 @@ function createBrowserReading(input) {
|
|
|
11484
12177
|
*
|
|
11485
12178
|
* const toolset = createBrowserToolset(page, { context })
|
|
11486
12179
|
* await toolset.start()
|
|
11487
|
-
* 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' } })
|
|
11488
12181
|
* ```
|
|
11489
12182
|
*
|
|
11490
12183
|
* @example Drive a page with a small model
|
|
@@ -11498,8 +12191,8 @@ function createBrowserReading(input) {
|
|
|
11498
12191
|
* const system =
|
|
11499
12192
|
* 'You control a web browser with tools and must call a tool before you answer. ' +
|
|
11500
12193
|
* 'The first message shows the page as look returns it; references such as e4 name its elements. ' +
|
|
11501
|
-
* 'To learn a fact, call read with
|
|
11502
|
-
*
|
|
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. " +
|
|
11503
12196
|
* 'To press a button or follow a link, call click with its reference from the latest result. Never invent a reference. ' +
|
|
11504
12197
|
* 'If text you expect has not appeared, call wait once. ' +
|
|
11505
12198
|
* 'When the task is done, answer in one short sentence.'
|
|
@@ -11509,11 +12202,11 @@ function createBrowserReading(input) {
|
|
|
11509
12202
|
* const page = await browser.create({ url: 'https://shop.example.test/' })
|
|
11510
12203
|
* const toolset = createBrowserToolset(page, { tools: createToolManager() })
|
|
11511
12204
|
* await toolset.start()
|
|
11512
|
-
* 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']
|
|
11513
12206
|
* const seeded = await toolset.tools.execute({
|
|
11514
12207
|
* id: 'seed',
|
|
11515
12208
|
* name: 'look',
|
|
11516
|
-
* arguments: {
|
|
12209
|
+
* arguments: { search: '' },
|
|
11517
12210
|
* })
|
|
11518
12211
|
* const view = seeded.success ? String(seeded.value) : seeded.error
|
|
11519
12212
|
* const agent = createAgent(createOllama({ model: 'qwen3.5:2b-q4_K_M' }), {
|
|
@@ -11733,6 +12426,10 @@ var BrowserJourneyToolset = class {
|
|
|
11733
12426
|
const offset = args["offset"] ?? 0;
|
|
11734
12427
|
if (!isInteger(offset) || offset < 0) throw new BrowserError("The offset parameter must be a non-negative integer.", "BROWSER_JOURNEY_ARGUMENT", { key: "offset" });
|
|
11735
12428
|
const listings = [];
|
|
12429
|
+
const headings = [];
|
|
12430
|
+
const offsets = /* @__PURE__ */ new Map();
|
|
12431
|
+
let headingOffset = 0;
|
|
12432
|
+
let listingOffset = 0;
|
|
11736
12433
|
const faults = /* @__PURE__ */ new Set();
|
|
11737
12434
|
let position = 0;
|
|
11738
12435
|
for (;;) {
|
|
@@ -11740,11 +12437,21 @@ var BrowserJourneyToolset = class {
|
|
|
11740
12437
|
signal,
|
|
11741
12438
|
offset: position
|
|
11742
12439
|
});
|
|
11743
|
-
|
|
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
|
+
}
|
|
11744
12449
|
for (const fault of page.faults) {
|
|
11745
12450
|
if (faults.has(fault.name)) continue;
|
|
11746
12451
|
faults.add(fault.name);
|
|
11747
|
-
|
|
12452
|
+
const listing = renderBrowserJourneyFault(fault);
|
|
12453
|
+
listings.push(listing);
|
|
12454
|
+
listingOffset += listing.length + 2;
|
|
11748
12455
|
}
|
|
11749
12456
|
const span = page.entries.length;
|
|
11750
12457
|
position += span;
|
|
@@ -11753,7 +12460,11 @@ var BrowserJourneyToolset = class {
|
|
|
11753
12460
|
if (listings.length === 0) return BROWSER_JOURNEY_EMPTY_LISTING;
|
|
11754
12461
|
const listing = listings.join("\n\n");
|
|
11755
12462
|
const start = offset < listing.length ? offset : 0;
|
|
11756
|
-
|
|
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);
|
|
11757
12468
|
const last = listing.charCodeAt(end - 1);
|
|
11758
12469
|
if (end < listing.length && last >= 55296 && last <= 56319) end -= 1;
|
|
11759
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", {
|
|
@@ -11761,8 +12472,8 @@ var BrowserJourneyToolset = class {
|
|
|
11761
12472
|
offset: start
|
|
11762
12473
|
});
|
|
11763
12474
|
const text = listing.slice(start, end);
|
|
11764
|
-
if (start === 0 && end === listing.length) return text
|
|
11765
|
-
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` : ""}]`;
|
|
11766
12477
|
}
|
|
11767
12478
|
async #edit(args, signal) {
|
|
11768
12479
|
if (this.#readonly) throw new BrowserError(BROWSER_JOURNEY_READONLY_REFUSAL, "BROWSER_JOURNEY_READONLY");
|
|
@@ -11928,7 +12639,7 @@ var BrowserJourneyToolset = class {
|
|
|
11928
12639
|
const performed = await this.#toolset.perform({
|
|
11929
12640
|
id: "journey",
|
|
11930
12641
|
name: "look",
|
|
11931
|
-
arguments: {
|
|
12642
|
+
arguments: { search: "" }
|
|
11932
12643
|
}, { signal });
|
|
11933
12644
|
return performed.result.success ? String(performed.result.value) : performed.result.error;
|
|
11934
12645
|
}
|
|
@@ -11956,7 +12667,7 @@ var BrowserJourneyToolset = class {
|
|
|
11956
12667
|
* view's own tools beside them.
|
|
11957
12668
|
*
|
|
11958
12669
|
* @remarks
|
|
11959
|
-
* 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
|
|
11960
12671
|
* `BrowserViewInterface` it is constructed over. With `options.page` it also advertises `press`
|
|
11961
12672
|
* and `navigate`, stages the `dialog` tool while a dialog is open on the current page, follows a
|
|
11962
12673
|
* popup the current page opens and returns to the opener when the popup closes, and adopts
|
|
@@ -11970,11 +12681,18 @@ var BrowserJourneyToolset = class {
|
|
|
11970
12681
|
* refuses every tool but `dialog` while a dialog is open, forwards `ToolContext.signal` into every
|
|
11971
12682
|
* protocol call, refuses a call to a toolset tool that carries a parameter the tool does not
|
|
11972
12683
|
* advertise, and cuts every returned string and every thrown message at `limit` characters plus
|
|
11973
|
-
* a footer; the footer of a cut
|
|
11974
|
-
*
|
|
11975
|
-
* the
|
|
11976
|
-
*
|
|
11977
|
-
* 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.
|
|
11978
12696
|
*
|
|
11979
12697
|
* `click`, `type`, `press`, `navigate`, and `switch` are actions: one runs at a time, in call
|
|
11980
12698
|
* order, and holds the queue until its receipt. A queued action whose signal aborts leaves
|
|
@@ -12005,7 +12723,7 @@ var BrowserJourneyToolset = class {
|
|
|
12005
12723
|
* page placement's `type` with `submit` ends its action with `and submitted the form` when a read
|
|
12006
12724
|
* recorded a submission, or when the navigation the receipt settled names `formSubmissionGet` or
|
|
12007
12725
|
* `formSubmissionPost` as its reason, whatever the read answered, and with `and pressed Enter`
|
|
12008
|
-
* otherwise. A `read`
|
|
12726
|
+
* otherwise. A `read` or `plain`
|
|
12009
12727
|
* at an offset at or past its retained reading's end restarts that reading at 0. A receipt that
|
|
12010
12728
|
* waits for a requested navigation shares one `BROWSER_TOOL_TIMEOUT_MS` deadline between that
|
|
12011
12729
|
* wait and its view capture, of which `BROWSER_TOOL_CAPTURE_MS` is reserved for the capture. The
|
|
@@ -12021,9 +12739,9 @@ var BrowserJourneyToolset = class {
|
|
|
12021
12739
|
* A page tool is skipped, with `skip` emitted, when its name is reserved (`unresolved` included), when the manager holds
|
|
12022
12740
|
* its name under a tool the toolset did not add (checked again immediately before each addition),
|
|
12023
12741
|
* when its name falls outside `BROWSER_TOOL_NAME_PATTERN`, when its parameters or, for a source
|
|
12024
|
-
* that supplies `tools()`, its selected registration declare `
|
|
12742
|
+
* that supplies `tools()`, its selected registration declare `purpose` optional, or when that
|
|
12025
12743
|
* registration is marked `debugging`. The selected registration is the main frame's, then the
|
|
12026
|
-
* 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
|
|
12027
12745
|
* is stripped before the tool runs. Every adopted tool is advertised `untrusted`. `destroy()`
|
|
12028
12746
|
* removes only the tools the manager still holds under the instances the toolset added, never
|
|
12029
12747
|
* closes a page, and ends only what `options.release` hands over, calling it one time last.
|
|
@@ -12034,7 +12752,7 @@ var BrowserJourneyToolset = class {
|
|
|
12034
12752
|
*
|
|
12035
12753
|
* const toolset = new BrowserToolset(page, { page })
|
|
12036
12754
|
* await toolset.start()
|
|
12037
|
-
* 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' } })
|
|
12038
12756
|
* await toolset.destroy()
|
|
12039
12757
|
* ```
|
|
12040
12758
|
*/
|
|
@@ -12098,20 +12816,23 @@ var BrowserToolset = class {
|
|
|
12098
12816
|
...options?.on === void 0 ? {} : { on: options.on },
|
|
12099
12817
|
...options?.error === void 0 ? {} : { error: options.error }
|
|
12100
12818
|
});
|
|
12101
|
-
const look = this.#create("look", this.#look.bind(this),
|
|
12102
|
-
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);
|
|
12103
12822
|
const click = this.#create("click", this.#click.bind(this), BROWSER_TOOL_VIEW_FOOTER);
|
|
12104
12823
|
const type = this.#create("type", this.#type.bind(this), BROWSER_TOOL_VIEW_FOOTER);
|
|
12105
12824
|
const wait = this.#create("wait", this.#wait.bind(this), BROWSER_TOOL_CUT_FOOTER);
|
|
12106
12825
|
this.#native = Object.freeze(options?.page === void 0 ? [
|
|
12107
12826
|
look,
|
|
12108
12827
|
read,
|
|
12828
|
+
plain,
|
|
12109
12829
|
click,
|
|
12110
12830
|
type,
|
|
12111
12831
|
wait
|
|
12112
12832
|
] : [
|
|
12113
12833
|
look,
|
|
12114
12834
|
read,
|
|
12835
|
+
plain,
|
|
12115
12836
|
click,
|
|
12116
12837
|
type,
|
|
12117
12838
|
this.#create("press", this.#press.bind(this), BROWSER_TOOL_VIEW_FOOTER),
|
|
@@ -12414,44 +13135,74 @@ var BrowserToolset = class {
|
|
|
12414
13135
|
if (isString(secret) && secret.length > 0) message = message.replaceAll(JSON.stringify(secret), "[redacted]").replaceAll(secret, "[redacted]");
|
|
12415
13136
|
return clause === void 0 ? message : boundBrowserText(message, this.#limit, clause);
|
|
12416
13137
|
}
|
|
12417
|
-
async #look(
|
|
12418
|
-
|
|
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);
|
|
12419
13159
|
}
|
|
12420
|
-
async #read(args, context) {
|
|
12421
|
-
const offset = args
|
|
12422
|
-
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);
|
|
12423
13162
|
const view = this.#cursor;
|
|
12424
13163
|
const retained = this.#reading;
|
|
12425
13164
|
let reading = retained?.reading;
|
|
12426
|
-
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) {
|
|
12427
13166
|
reading = await this.#race(view.read({ signal: context.signal }), "", context.signal);
|
|
12428
13167
|
this.#reading = {
|
|
12429
13168
|
view,
|
|
12430
|
-
reading
|
|
13169
|
+
reading,
|
|
13170
|
+
name
|
|
12431
13171
|
};
|
|
12432
13172
|
}
|
|
12433
|
-
const
|
|
12434
|
-
|
|
12435
|
-
|
|
12436
|
-
}).total ? offset : 0;
|
|
12437
|
-
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();
|
|
12438
13176
|
const room = this.#limit - note.length;
|
|
12439
|
-
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 ? {
|
|
12440
13183
|
text: "",
|
|
12441
13184
|
offset: start,
|
|
12442
|
-
total:
|
|
12443
|
-
} :
|
|
12444
|
-
|
|
12445
|
-
|
|
12446
|
-
|
|
12447
|
-
|
|
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", {
|
|
12448
13199
|
limit: this.#limit,
|
|
12449
|
-
offset:
|
|
13200
|
+
offset: slice.offset
|
|
12450
13201
|
});
|
|
12451
13202
|
const end = slice.offset + slice.text.length;
|
|
12452
13203
|
const more = end < slice.total;
|
|
12453
|
-
if (
|
|
12454
|
-
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` : ""}]`];
|
|
12455
13206
|
}
|
|
12456
13207
|
async #click(args, context) {
|
|
12457
13208
|
const turn = await this.#acquire(context.signal);
|
|
@@ -12554,7 +13305,7 @@ var BrowserToolset = class {
|
|
|
12554
13305
|
return [await this.#settle(page.keyboard.press(key), action, context.signal, true, record, popups, observation, key === "Enter" ? {
|
|
12555
13306
|
explicit: false,
|
|
12556
13307
|
action
|
|
12557
|
-
} : void 0), ""];
|
|
13308
|
+
} : void 0, true), ""];
|
|
12558
13309
|
} finally {
|
|
12559
13310
|
record?.destroy();
|
|
12560
13311
|
popups?.destroy();
|
|
@@ -12593,7 +13344,7 @@ var BrowserToolset = class {
|
|
|
12593
13344
|
const action = `Navigated to ${result.url}`;
|
|
12594
13345
|
return [renderBrowserReceipt({
|
|
12595
13346
|
action,
|
|
12596
|
-
view: await this.#capture(action, context.signal)
|
|
13347
|
+
view: (await this.#capture(action, context.signal)).view
|
|
12597
13348
|
}), ""];
|
|
12598
13349
|
} finally {
|
|
12599
13350
|
if (this.#navigating === page) this.#navigating = void 0;
|
|
@@ -12604,22 +13355,25 @@ var BrowserToolset = class {
|
|
|
12604
13355
|
async #wait(args, context) {
|
|
12605
13356
|
const text = readBrowserToolString(args, "text");
|
|
12606
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" });
|
|
12607
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" });
|
|
12608
13361
|
const timeout = Math.min(seconds === void 0 ? BROWSER_TOOL_TIMEOUT_MS : Math.ceil(seconds * 1e3), BROWSER_TOOL_TIMEOUT_LIMIT_MS);
|
|
12609
13362
|
const quoted = JSON.stringify(text);
|
|
12610
13363
|
try {
|
|
12611
13364
|
await this.#race(this.#cursor.wait(text, {
|
|
12612
13365
|
timeout,
|
|
12613
|
-
signal: context.signal
|
|
12614
|
-
|
|
12615
|
-
|
|
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.`, ""];
|
|
12616
13370
|
} catch (error) {
|
|
12617
13371
|
if (!context.signal.aborted && isBrowserError(error) && error.code === "BROWSER_WAIT_TIMEOUT") {
|
|
12618
13372
|
this.#actions.set(context.signal, {
|
|
12619
13373
|
...this.#actions.get(context.signal),
|
|
12620
13374
|
outcome: "timeout"
|
|
12621
13375
|
});
|
|
12622
|
-
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.`, ""];
|
|
12623
13377
|
}
|
|
12624
13378
|
throw error;
|
|
12625
13379
|
}
|
|
@@ -12642,14 +13396,19 @@ var BrowserToolset = class {
|
|
|
12642
13396
|
const action = `${accept ? "Accepted" : "Dismissed"} the ${dialog.category} dialog ${JSON.stringify(dialog.message)}`;
|
|
12643
13397
|
return [renderBrowserReceipt({
|
|
12644
13398
|
action,
|
|
12645
|
-
view: await this.#capture(action, context.signal)
|
|
13399
|
+
view: (await this.#capture(action, context.signal)).view
|
|
12646
13400
|
}), ""];
|
|
12647
13401
|
} finally {
|
|
12648
13402
|
turn.resolve();
|
|
12649
13403
|
}
|
|
12650
13404
|
}
|
|
12651
|
-
async #tabs(
|
|
12652
|
-
|
|
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}`, ""];
|
|
12653
13412
|
}
|
|
12654
13413
|
async #switch(args, context) {
|
|
12655
13414
|
const tab = readBrowserToolString(args, "tab");
|
|
@@ -12672,7 +13431,7 @@ var BrowserToolset = class {
|
|
|
12672
13431
|
await this.#command(front, action, front);
|
|
12673
13432
|
return [renderBrowserReceipt({
|
|
12674
13433
|
action,
|
|
12675
|
-
view: await this.#capture(action, context.signal)
|
|
13434
|
+
view: (await this.#capture(action, context.signal)).view
|
|
12676
13435
|
}), ""];
|
|
12677
13436
|
} finally {
|
|
12678
13437
|
turn.resolve();
|
|
@@ -12681,7 +13440,7 @@ var BrowserToolset = class {
|
|
|
12681
13440
|
async #invoke(tool, synthetic, args, context) {
|
|
12682
13441
|
const turn = await this.#acquire(context.signal);
|
|
12683
13442
|
try {
|
|
12684
|
-
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;
|
|
12685
13444
|
const command = Promise.resolve(tool.execute(input, context));
|
|
12686
13445
|
return [renderBrowserToolOutput(await this.#command(command, `Called ${tool.name}`, command, context.signal)), ""];
|
|
12687
13446
|
} finally {
|
|
@@ -12761,7 +13520,7 @@ var BrowserToolset = class {
|
|
|
12761
13520
|
}
|
|
12762
13521
|
return turn;
|
|
12763
13522
|
}
|
|
12764
|
-
async #settle(command, action, signal, trusted, record, popups, observation, enter) {
|
|
13523
|
+
async #settle(command, action, signal, trusted, record, popups, observation, enter, focus = false) {
|
|
12765
13524
|
let deadline;
|
|
12766
13525
|
try {
|
|
12767
13526
|
const started = record?.wait({ signal }).then(() => true, () => new Promise(() => void 0));
|
|
@@ -12797,11 +13556,13 @@ var BrowserToolset = class {
|
|
|
12797
13556
|
const unmoved = settled === void 0 && submissions.destinations.length === 0;
|
|
12798
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;
|
|
12799
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);
|
|
12800
13561
|
return renderBrowserReceipt({
|
|
12801
13562
|
action: line,
|
|
12802
13563
|
trusted,
|
|
12803
|
-
...
|
|
12804
|
-
view:
|
|
13564
|
+
...parts.length === 0 ? {} : { status: parts.join("; ") },
|
|
13565
|
+
view: captured.view
|
|
12805
13566
|
});
|
|
12806
13567
|
} finally {
|
|
12807
13568
|
record?.destroy();
|
|
@@ -12968,7 +13729,10 @@ var BrowserToolset = class {
|
|
|
12968
13729
|
}
|
|
12969
13730
|
}
|
|
12970
13731
|
async #capture(action, signal, deadline = performance.now() + BROWSER_TOOL_TIMEOUT_MS) {
|
|
12971
|
-
if (deadline - performance.now() < 1) return
|
|
13732
|
+
if (deadline - performance.now() < 1) return {
|
|
13733
|
+
view: BROWSER_TOOL_DEADLINE_NOTE,
|
|
13734
|
+
focus: void 0
|
|
13735
|
+
};
|
|
12972
13736
|
const receipt = new AbortController();
|
|
12973
13737
|
const options = {
|
|
12974
13738
|
signal: AbortSignal.any([signal, receipt.signal]),
|
|
@@ -12976,16 +13740,34 @@ var BrowserToolset = class {
|
|
|
12976
13740
|
};
|
|
12977
13741
|
try {
|
|
12978
13742
|
const outline = await this.#bounded(this.#cursor.elements.outline(options), deadline, action);
|
|
12979
|
-
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
|
+
};
|
|
12980
13750
|
} catch (error) {
|
|
12981
13751
|
if (signal.aborted || isBrowserError(error) && error.code === "BROWSER_TOOLSET_RECEIPT") throw error;
|
|
12982
|
-
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
|
+
};
|
|
12983
13756
|
try {
|
|
12984
13757
|
const outline = await this.#bounded(this.#cursor.elements.outline(options), deadline, action);
|
|
12985
|
-
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
|
+
};
|
|
12986
13765
|
} catch (retry) {
|
|
12987
13766
|
if (signal.aborted || isBrowserError(retry) && retry.code === "BROWSER_TOOLSET_RECEIPT") throw retry;
|
|
12988
|
-
return
|
|
13767
|
+
return {
|
|
13768
|
+
view: BROWSER_TOOL_CHANGED_NOTE,
|
|
13769
|
+
focus: void 0
|
|
13770
|
+
};
|
|
12989
13771
|
}
|
|
12990
13772
|
} finally {
|
|
12991
13773
|
receipt.abort(new BrowserError("the receipt settled", "BROWSER_TOOLSET_SETTLED"));
|
|
@@ -13219,6 +14001,6 @@ var BrowserToolset = class {
|
|
|
13219
14001
|
}
|
|
13220
14002
|
};
|
|
13221
14003
|
//#endregion
|
|
13222
|
-
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 };
|
|
13223
14005
|
|
|
13224
14006
|
//# sourceMappingURL=index.js.map
|