browxai 0.7.0
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/LICENSE +21 -0
- package/README.md +139 -0
- package/THIRD_PARTY_NOTICES.md +45 -0
- package/dist/cli/chrome.d.ts +1 -0
- package/dist/cli/chrome.js +130 -0
- package/dist/cli/command-registry.d.ts +15 -0
- package/dist/cli/command-registry.js +35 -0
- package/dist/cli/doctor-plugins.d.ts +18 -0
- package/dist/cli/doctor-plugins.js +338 -0
- package/dist/cli/doctor.d.ts +9 -0
- package/dist/cli/doctor.js +407 -0
- package/dist/cli/init.d.ts +1 -0
- package/dist/cli/init.js +200 -0
- package/dist/cli/register-commands.d.ts +1 -0
- package/dist/cli/register-commands.js +22 -0
- package/dist/cli/serve.d.ts +14 -0
- package/dist/cli/serve.js +151 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +129 -0
- package/dist/engine/adapters/adb.d.ts +72 -0
- package/dist/engine/adapters/adb.js +200 -0
- package/dist/engine/adapters/android-cdp.d.ts +54 -0
- package/dist/engine/adapters/android-cdp.js +110 -0
- package/dist/engine/adapters/android.engine.d.ts +1 -0
- package/dist/engine/adapters/android.engine.js +31 -0
- package/dist/engine/adapters/chromium.engine.d.ts +1 -0
- package/dist/engine/adapters/chromium.engine.js +44 -0
- package/dist/engine/adapters/firefox.engine.d.ts +1 -0
- package/dist/engine/adapters/firefox.engine.js +43 -0
- package/dist/engine/adapters/playwright-chromium.d.ts +43 -0
- package/dist/engine/adapters/playwright-chromium.js +56 -0
- package/dist/engine/adapters/playwright-firefox.d.ts +52 -0
- package/dist/engine/adapters/playwright-firefox.js +97 -0
- package/dist/engine/adapters/playwright-webkit.d.ts +40 -0
- package/dist/engine/adapters/playwright-webkit.js +79 -0
- package/dist/engine/adapters/safari/bidi-client.d.ts +46 -0
- package/dist/engine/adapters/safari/bidi-client.js +130 -0
- package/dist/engine/adapters/safari/launch.d.ts +56 -0
- package/dist/engine/adapters/safari/launch.js +104 -0
- package/dist/engine/adapters/safari/webdriver-client.d.ts +102 -0
- package/dist/engine/adapters/safari/webdriver-client.js +175 -0
- package/dist/engine/adapters/safari.engine.d.ts +1 -0
- package/dist/engine/adapters/safari.engine.js +52 -0
- package/dist/engine/adapters/safaridriver-hybrid.d.ts +56 -0
- package/dist/engine/adapters/safaridriver-hybrid.js +127 -0
- package/dist/engine/adapters/webkit.engine.d.ts +1 -0
- package/dist/engine/adapters/webkit.engine.js +47 -0
- package/dist/engine/capabilities.d.ts +53 -0
- package/dist/engine/capabilities.js +122 -0
- package/dist/engine/capability-registry.d.ts +9 -0
- package/dist/engine/capability-registry.js +20 -0
- package/dist/engine/index.d.ts +18 -0
- package/dist/engine/index.js +14 -0
- package/dist/engine/register-engines.d.ts +5 -0
- package/dist/engine/register-engines.js +16 -0
- package/dist/engine/registry.d.ts +145 -0
- package/dist/engine/registry.js +67 -0
- package/dist/engine/select.d.ts +48 -0
- package/dist/engine/select.js +128 -0
- package/dist/engine/session-cdp.d.ts +13 -0
- package/dist/engine/session-cdp.js +22 -0
- package/dist/engine/tool-gate.d.ts +19 -0
- package/dist/engine/tool-gate.js +226 -0
- package/dist/engine/types.d.ts +71 -0
- package/dist/engine/types.js +16 -0
- package/dist/helper/bridge.d.ts +48 -0
- package/dist/helper/bridge.js +200 -0
- package/dist/helper/browx-page.d.ts +1 -0
- package/dist/helper/browx-page.js +47 -0
- package/dist/helper/overlay-hide.d.ts +9 -0
- package/dist/helper/overlay-hide.js +49 -0
- package/dist/helper/stealth.d.ts +10 -0
- package/dist/helper/stealth.js +88 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +15 -0
- package/dist/page/a11y.d.ts +81 -0
- package/dist/page/a11y.js +219 -0
- package/dist/page/action-substrate.d.ts +64 -0
- package/dist/page/action-substrate.js +118 -0
- package/dist/page/actionresult-blocks.d.ts +99 -0
- package/dist/page/actionresult-blocks.js +144 -0
- package/dist/page/actionresult-shape.d.ts +48 -0
- package/dist/page/actionresult-shape.js +155 -0
- package/dist/page/actionresult-types.d.ts +368 -0
- package/dist/page/actionresult-types.js +4 -0
- package/dist/page/actionresult.d.ts +4 -0
- package/dist/page/actionresult.js +299 -0
- package/dist/page/actions-probe.d.ts +32 -0
- package/dist/page/actions-probe.js +294 -0
- package/dist/page/actions-scroll.d.ts +40 -0
- package/dist/page/actions-scroll.js +53 -0
- package/dist/page/actions.d.ts +132 -0
- package/dist/page/actions.js +453 -0
- package/dist/page/archive-assets.d.ts +39 -0
- package/dist/page/archive-assets.js +187 -0
- package/dist/page/archive.d.ts +47 -0
- package/dist/page/archive.js +349 -0
- package/dist/page/asset-export.d.ts +122 -0
- package/dist/page/asset-export.js +376 -0
- package/dist/page/await_network.d.ts +16 -0
- package/dist/page/await_network.js +23 -0
- package/dist/page/bbox.d.ts +37 -0
- package/dist/page/bbox.js +115 -0
- package/dist/page/canvas-capture.d.ts +82 -0
- package/dist/page/canvas-capture.js +257 -0
- package/dist/page/canvas-diff.d.ts +51 -0
- package/dist/page/canvas-diff.js +131 -0
- package/dist/page/canvas-gesture.d.ts +53 -0
- package/dist/page/canvas-gesture.js +167 -0
- package/dist/page/canvas-transform.d.ts +96 -0
- package/dist/page/canvas-transform.js +150 -0
- package/dist/page/canvas.d.ts +8 -0
- package/dist/page/canvas.js +50 -0
- package/dist/page/capture-substrate.d.ts +111 -0
- package/dist/page/capture-substrate.js +139 -0
- package/dist/page/clipboard.d.ts +25 -0
- package/dist/page/clipboard.js +50 -0
- package/dist/page/clock.d.ts +36 -0
- package/dist/page/clock.js +167 -0
- package/dist/page/compose.d.ts +55 -0
- package/dist/page/compose.js +169 -0
- package/dist/page/console.d.ts +39 -0
- package/dist/page/console.js +73 -0
- package/dist/page/coverage.d.ts +97 -0
- package/dist/page/coverage.js +280 -0
- package/dist/page/dom-export.d.ts +41 -0
- package/dist/page/dom-export.js +193 -0
- package/dist/page/dom-walk.d.ts +91 -0
- package/dist/page/dom-walk.js +267 -0
- package/dist/page/dom_diff.d.ts +48 -0
- package/dist/page/dom_diff.js +121 -0
- package/dist/page/downloads.d.ts +80 -0
- package/dist/page/downloads.js +244 -0
- package/dist/page/drop-files.d.ts +78 -0
- package/dist/page/drop-files.js +310 -0
- package/dist/page/element-export-discovery.d.ts +64 -0
- package/dist/page/element-export-discovery.js +346 -0
- package/dist/page/element-export.d.ts +46 -0
- package/dist/page/element-export.js +251 -0
- package/dist/page/emulation-substrate.d.ts +53 -0
- package/dist/page/emulation-substrate.js +87 -0
- package/dist/page/emulation.d.ts +60 -0
- package/dist/page/emulation.js +162 -0
- package/dist/page/export-playwright-script.d.ts +47 -0
- package/dist/page/export-playwright-script.js +304 -0
- package/dist/page/extract-resolve.d.ts +22 -0
- package/dist/page/extract-resolve.js +341 -0
- package/dist/page/extract-schema.d.ts +20 -0
- package/dist/page/extract-schema.js +200 -0
- package/dist/page/extract-types.d.ts +127 -0
- package/dist/page/extract-types.js +8 -0
- package/dist/page/extract-warnings.d.ts +8 -0
- package/dist/page/extract-warnings.js +56 -0
- package/dist/page/extract.d.ts +9 -0
- package/dist/page/extract.js +174 -0
- package/dist/page/fill-form.d.ts +58 -0
- package/dist/page/fill-form.js +261 -0
- package/dist/page/find.d.ts +158 -0
- package/dist/page/find.js +470 -0
- package/dist/page/frames.d.ts +45 -0
- package/dist/page/frames.js +133 -0
- package/dist/page/generate-locator.d.ts +57 -0
- package/dist/page/generate-locator.js +136 -0
- package/dist/page/gestures.d.ts +128 -0
- package/dist/page/gestures.js +198 -0
- package/dist/page/har.d.ts +91 -0
- package/dist/page/har.js +174 -0
- package/dist/page/heap.d.ts +97 -0
- package/dist/page/heap.js +285 -0
- package/dist/page/inspect.d.ts +34 -0
- package/dist/page/inspect.js +75 -0
- package/dist/page/layout-thrash.d.ts +34 -0
- package/dist/page/layout-thrash.js +232 -0
- package/dist/page/learning.d.ts +21 -0
- package/dist/page/learning.js +84 -0
- package/dist/page/locator.d.ts +54 -0
- package/dist/page/locator.js +142 -0
- package/dist/page/memory-diff.d.ts +48 -0
- package/dist/page/memory-diff.js +105 -0
- package/dist/page/network-mask.d.ts +8 -0
- package/dist/page/network-mask.js +18 -0
- package/dist/page/network-playwright.d.ts +96 -0
- package/dist/page/network-playwright.js +353 -0
- package/dist/page/network-substrate-select.d.ts +18 -0
- package/dist/page/network-substrate-select.js +32 -0
- package/dist/page/network-substrate.d.ts +109 -0
- package/dist/page/network-substrate.js +161 -0
- package/dist/page/network-ws.d.ts +46 -0
- package/dist/page/network-ws.js +113 -0
- package/dist/page/network.d.ts +194 -0
- package/dist/page/network.js +415 -0
- package/dist/page/overflow-detect.d.ts +102 -0
- package/dist/page/overflow-detect.js +449 -0
- package/dist/page/pdf.d.ts +69 -0
- package/dist/page/pdf.js +109 -0
- package/dist/page/perf-audit-analysers.d.ts +40 -0
- package/dist/page/perf-audit-analysers.js +369 -0
- package/dist/page/perf-audit-runner.d.ts +20 -0
- package/dist/page/perf-audit-runner.js +195 -0
- package/dist/page/perf-audit-types.d.ts +41 -0
- package/dist/page/perf-audit-types.js +5 -0
- package/dist/page/perf-audit.d.ts +37 -0
- package/dist/page/perf-audit.js +377 -0
- package/dist/page/perf.d.ts +127 -0
- package/dist/page/perf.js +373 -0
- package/dist/page/plan.d.ts +192 -0
- package/dist/page/plan.js +308 -0
- package/dist/page/point_probe.d.ts +46 -0
- package/dist/page/point_probe.js +99 -0
- package/dist/page/recording.d.ts +67 -0
- package/dist/page/recording.js +172 -0
- package/dist/page/refs.d.ts +92 -0
- package/dist/page/refs.js +134 -0
- package/dist/page/regions.d.ts +23 -0
- package/dist/page/regions.js +32 -0
- package/dist/page/routes.d.ts +40 -0
- package/dist/page/routes.js +87 -0
- package/dist/page/safari-actions.d.ts +12 -0
- package/dist/page/safari-actions.js +144 -0
- package/dist/page/sample.d.ts +64 -0
- package/dist/page/sample.js +216 -0
- package/dist/page/screenshot-on.d.ts +51 -0
- package/dist/page/screenshot-on.js +150 -0
- package/dist/page/screenshot-save.d.ts +36 -0
- package/dist/page/screenshot-save.js +53 -0
- package/dist/page/screenshot-schedule.d.ts +50 -0
- package/dist/page/screenshot-schedule.js +155 -0
- package/dist/page/script-substrate.d.ts +32 -0
- package/dist/page/script-substrate.js +47 -0
- package/dist/page/seed-random.d.ts +45 -0
- package/dist/page/seed-random.js +144 -0
- package/dist/page/set-of-marks.d.ts +96 -0
- package/dist/page/set-of-marks.js +245 -0
- package/dist/page/shadow.d.ts +136 -0
- package/dist/page/shadow.js +400 -0
- package/dist/page/shortcut.d.ts +50 -0
- package/dist/page/shortcut.js +147 -0
- package/dist/page/snapshot-substrate-safari.d.ts +30 -0
- package/dist/page/snapshot-substrate-safari.js +84 -0
- package/dist/page/snapshot-substrate-select.d.ts +24 -0
- package/dist/page/snapshot-substrate-select.js +34 -0
- package/dist/page/snapshot-substrate.d.ts +58 -0
- package/dist/page/snapshot-substrate.js +135 -0
- package/dist/page/snapshot.d.ts +24 -0
- package/dist/page/snapshot.js +162 -0
- package/dist/page/solve-captcha.d.ts +76 -0
- package/dist/page/solve-captcha.js +286 -0
- package/dist/page/storage-substrate-types.d.ts +221 -0
- package/dist/page/storage-substrate-types.js +6 -0
- package/dist/page/storage-substrate.d.ts +215 -0
- package/dist/page/storage-substrate.js +280 -0
- package/dist/page/structural.d.ts +9 -0
- package/dist/page/structural.js +152 -0
- package/dist/page/substrate-bundle-safari.d.ts +8 -0
- package/dist/page/substrate-bundle-safari.js +42 -0
- package/dist/page/substrate-bundle.d.ts +6 -0
- package/dist/page/substrate-bundle.js +53 -0
- package/dist/page/text_search.d.ts +44 -0
- package/dist/page/text_search.js +90 -0
- package/dist/page/upload.d.ts +28 -0
- package/dist/page/upload.js +62 -0
- package/dist/page/verify.d.ts +63 -0
- package/dist/page/verify.js +451 -0
- package/dist/page/video.d.ts +115 -0
- package/dist/page/video.js +169 -0
- package/dist/page/visibility.d.ts +22 -0
- package/dist/page/visibility.js +94 -0
- package/dist/page/watch.d.ts +29 -0
- package/dist/page/watch.js +99 -0
- package/dist/page/workers.d.ts +126 -0
- package/dist/page/workers.js +490 -0
- package/dist/page/ws-interactive.d.ts +82 -0
- package/dist/page/ws-interactive.js +318 -0
- package/dist/plugin/cli.d.ts +45 -0
- package/dist/plugin/cli.js +496 -0
- package/dist/plugin/command-registry.d.ts +9 -0
- package/dist/plugin/command-registry.js +23 -0
- package/dist/plugin/depgraph.d.ts +37 -0
- package/dist/plugin/depgraph.js +186 -0
- package/dist/plugin/manifest.d.ts +182 -0
- package/dist/plugin/manifest.js +219 -0
- package/dist/plugin/package-manager.d.ts +22 -0
- package/dist/plugin/package-manager.js +40 -0
- package/dist/plugin/resolver.d.ts +85 -0
- package/dist/plugin/resolver.js +166 -0
- package/dist/plugin/runtime.d.ts +77 -0
- package/dist/plugin/runtime.js +402 -0
- package/dist/plugin/types.d.ts +113 -0
- package/dist/plugin/types.js +4 -0
- package/dist/policy/confirm.d.ts +76 -0
- package/dist/policy/confirm.js +162 -0
- package/dist/policy/origin.d.ts +17 -0
- package/dist/policy/origin.js +79 -0
- package/dist/sdk/client.d.ts +21 -0
- package/dist/sdk/client.js +174 -0
- package/dist/sdk/index.d.ts +32 -0
- package/dist/sdk/index.js +61 -0
- package/dist/sdk/plugin-types.d.ts +33 -0
- package/dist/sdk/plugin-types.js +22 -0
- package/dist/sdk/registry.d.ts +17 -0
- package/dist/sdk/registry.js +94 -0
- package/dist/sdk/socket-transport.d.ts +20 -0
- package/dist/sdk/socket-transport.js +90 -0
- package/dist/sdk/tool-types.d.ts +634 -0
- package/dist/sdk/tool-types.js +28 -0
- package/dist/sdk/transport-in-process.d.ts +21 -0
- package/dist/sdk/transport-in-process.js +44 -0
- package/dist/sdk/transport-registry.d.ts +19 -0
- package/dist/sdk/transport-registry.js +31 -0
- package/dist/sdk/transport-socket.d.ts +12 -0
- package/dist/sdk/transport-socket.js +77 -0
- package/dist/sdk/transport-stdio-child.d.ts +10 -0
- package/dist/sdk/transport-stdio-child.js +47 -0
- package/dist/sdk/transport.d.ts +10 -0
- package/dist/sdk/transport.js +35 -0
- package/dist/sdk/types.d.ts +176 -0
- package/dist/sdk/types.js +10 -0
- package/dist/server.d.ts +33 -0
- package/dist/server.js +327 -0
- package/dist/session/artifacts.d.ts +52 -0
- package/dist/session/artifacts.js +177 -0
- package/dist/session/byob-attach.d.ts +26 -0
- package/dist/session/byob-attach.js +187 -0
- package/dist/session/byob.d.ts +8 -0
- package/dist/session/byob.js +20 -0
- package/dist/session/cache-storage.d.ts +100 -0
- package/dist/session/cache-storage.js +166 -0
- package/dist/session/device-emu.d.ts +149 -0
- package/dist/session/device-emu.js +545 -0
- package/dist/session/device.d.ts +14 -0
- package/dist/session/device.js +44 -0
- package/dist/session/dialog.d.ts +62 -0
- package/dist/session/dialog.js +164 -0
- package/dist/session/emulation.d.ts +69 -0
- package/dist/session/emulation.js +168 -0
- package/dist/session/extensions.d.ts +113 -0
- package/dist/session/extensions.js +237 -0
- package/dist/session/fs-picker.d.ts +144 -0
- package/dist/session/fs-picker.js +666 -0
- package/dist/session/idb-storage.d.ts +86 -0
- package/dist/session/idb-storage.js +229 -0
- package/dist/session/incognito.d.ts +3 -0
- package/dist/session/incognito.js +20 -0
- package/dist/session/launch-options.d.ts +41 -0
- package/dist/session/launch-options.js +200 -0
- package/dist/session/managed.d.ts +3 -0
- package/dist/session/managed.js +16 -0
- package/dist/session/metrics.d.ts +45 -0
- package/dist/session/metrics.js +75 -0
- package/dist/session/notification.d.ts +122 -0
- package/dist/session/notification.js +426 -0
- package/dist/session/permission.d.ts +144 -0
- package/dist/session/permission.js +600 -0
- package/dist/session/playwright-post-wire.d.ts +8 -0
- package/dist/session/playwright-post-wire.js +148 -0
- package/dist/session/policy-buffer.d.ts +21 -0
- package/dist/session/policy-buffer.js +47 -0
- package/dist/session/profile-snapshot.d.ts +11 -0
- package/dist/session/profile-snapshot.js +53 -0
- package/dist/session/registry.d.ts +365 -0
- package/dist/session/registry.js +98 -0
- package/dist/session/safari-post-wire.d.ts +8 -0
- package/dist/session/safari-post-wire.js +28 -0
- package/dist/session/safari-session.d.ts +10 -0
- package/dist/session/safari-session.js +39 -0
- package/dist/session/storage.d.ts +148 -0
- package/dist/session/storage.js +350 -0
- package/dist/session/types.d.ts +113 -0
- package/dist/session/types.js +5 -0
- package/dist/session/wedge.d.ts +15 -0
- package/dist/session/wedge.js +41 -0
- package/dist/tools/action-core-tools.d.ts +13 -0
- package/dist/tools/action-core-tools.js +156 -0
- package/dist/tools/action-form-tools.d.ts +12 -0
- package/dist/tools/action-form-tools.js +179 -0
- package/dist/tools/action-gesture-tools.d.ts +9 -0
- package/dist/tools/action-gesture-tools.js +115 -0
- package/dist/tools/action-history-tools.d.ts +8 -0
- package/dist/tools/action-history-tools.js +67 -0
- package/dist/tools/action-tool.d.ts +42 -0
- package/dist/tools/action-tool.js +58 -0
- package/dist/tools/action-tools.d.ts +20 -0
- package/dist/tools/action-tools.js +28 -0
- package/dist/tools/batch-act-tools.d.ts +10 -0
- package/dist/tools/batch-act-tools.js +276 -0
- package/dist/tools/batch-human-tools.d.ts +8 -0
- package/dist/tools/batch-human-tools.js +148 -0
- package/dist/tools/canvas-tools.d.ts +40 -0
- package/dist/tools/canvas-tools.js +368 -0
- package/dist/tools/capture-report-diagnostics-tools.d.ts +7 -0
- package/dist/tools/capture-report-diagnostics-tools.js +318 -0
- package/dist/tools/capture-report-element-export-tools.d.ts +8 -0
- package/dist/tools/capture-report-element-export-tools.js +197 -0
- package/dist/tools/capture-report-export-tools.d.ts +8 -0
- package/dist/tools/capture-report-export-tools.js +246 -0
- package/dist/tools/capture-report-marks-tools.d.ts +9 -0
- package/dist/tools/capture-report-marks-tools.js +221 -0
- package/dist/tools/capture-report-upload-tools.d.ts +8 -0
- package/dist/tools/capture-report-upload-tools.js +277 -0
- package/dist/tools/config-approval-tools.d.ts +8 -0
- package/dist/tools/config-approval-tools.js +166 -0
- package/dist/tools/deep-coverage-tools.d.ts +8 -0
- package/dist/tools/deep-coverage-tools.js +325 -0
- package/dist/tools/deep-determinism-tools.d.ts +8 -0
- package/dist/tools/deep-determinism-tools.js +276 -0
- package/dist/tools/deep-perf-tools.d.ts +19 -0
- package/dist/tools/deep-perf-tools.js +324 -0
- package/dist/tools/device-emulation-tools.d.ts +9 -0
- package/dist/tools/device-emulation-tools.js +137 -0
- package/dist/tools/extensions-batch-tools.d.ts +18 -0
- package/dist/tools/extensions-batch-tools.js +24 -0
- package/dist/tools/extensions-rebuild.d.ts +22 -0
- package/dist/tools/extensions-rebuild.js +208 -0
- package/dist/tools/extensions-tools.d.ts +2 -0
- package/dist/tools/extensions-tools.js +331 -0
- package/dist/tools/forms-fill-tools.d.ts +8 -0
- package/dist/tools/forms-fill-tools.js +109 -0
- package/dist/tools/forms-plan-tools.d.ts +7 -0
- package/dist/tools/forms-plan-tools.js +159 -0
- package/dist/tools/forms-recording-mode-tools.d.ts +8 -0
- package/dist/tools/forms-recording-mode-tools.js +71 -0
- package/dist/tools/forms-recording-tools.d.ts +14 -0
- package/dist/tools/forms-recording-tools.js +22 -0
- package/dist/tools/forms-refs-tools.d.ts +8 -0
- package/dist/tools/forms-refs-tools.js +90 -0
- package/dist/tools/gesture-coord-tools.d.ts +8 -0
- package/dist/tools/gesture-coord-tools.js +168 -0
- package/dist/tools/gesture-emulation-tools.d.ts +8 -0
- package/dist/tools/gesture-emulation-tools.js +135 -0
- package/dist/tools/gesture-network-tools.d.ts +17 -0
- package/dist/tools/gesture-network-tools.js +27 -0
- package/dist/tools/gesture-route-tools.d.ts +8 -0
- package/dist/tools/gesture-route-tools.js +142 -0
- package/dist/tools/gesture-websocket-tools.d.ts +8 -0
- package/dist/tools/gesture-websocket-tools.js +122 -0
- package/dist/tools/gesture-worker-tools.d.ts +9 -0
- package/dist/tools/gesture-worker-tools.js +200 -0
- package/dist/tools/host-build.d.ts +76 -0
- package/dist/tools/host-build.js +516 -0
- package/dist/tools/host.d.ts +287 -0
- package/dist/tools/host.js +1 -0
- package/dist/tools/input-tools.d.ts +10 -0
- package/dist/tools/input-tools.js +176 -0
- package/dist/tools/live-emulation-tools.d.ts +9 -0
- package/dist/tools/live-emulation-tools.js +353 -0
- package/dist/tools/plugin-runtime.d.ts +36 -0
- package/dist/tools/plugin-runtime.js +274 -0
- package/dist/tools/read-observe-buffer-tools.d.ts +9 -0
- package/dist/tools/read-observe-buffer-tools.js +385 -0
- package/dist/tools/read-observe-capture-tools.d.ts +12 -0
- package/dist/tools/read-observe-capture-tools.js +376 -0
- package/dist/tools/read-observe-dom-tools.d.ts +8 -0
- package/dist/tools/read-observe-dom-tools.js +308 -0
- package/dist/tools/read-observe-extract-tools.d.ts +8 -0
- package/dist/tools/read-observe-extract-tools.js +232 -0
- package/dist/tools/read-observe-verify-tools.d.ts +8 -0
- package/dist/tools/read-observe-verify-tools.js +316 -0
- package/dist/tools/schemas.d.ts +29 -0
- package/dist/tools/schemas.js +58 -0
- package/dist/tools/secrets-captcha-tools.d.ts +9 -0
- package/dist/tools/secrets-captcha-tools.js +231 -0
- package/dist/tools/session-dialog-permission-tools.d.ts +9 -0
- package/dist/tools/session-dialog-permission-tools.js +287 -0
- package/dist/tools/session-lifecycle-tools.d.ts +8 -0
- package/dist/tools/session-lifecycle-tools.js +314 -0
- package/dist/tools/session-notification-device-tools.d.ts +9 -0
- package/dist/tools/session-notification-device-tools.js +156 -0
- package/dist/tools/session-policy-tools.d.ts +16 -0
- package/dist/tools/session-policy-tools.js +22 -0
- package/dist/tools/session-registry.d.ts +28 -0
- package/dist/tools/session-registry.js +427 -0
- package/dist/tools/storage-artifact-har-video-tools.d.ts +8 -0
- package/dist/tools/storage-artifact-har-video-tools.js +311 -0
- package/dist/tools/storage-cache-idb-tools.d.ts +8 -0
- package/dist/tools/storage-cache-idb-tools.js +347 -0
- package/dist/tools/storage-state-cookies-tools.d.ts +8 -0
- package/dist/tools/storage-state-cookies-tools.js +223 -0
- package/dist/tools/storage-tools.d.ts +17 -0
- package/dist/tools/storage-tools.js +25 -0
- package/dist/tools/storage-web-auth-tools.d.ts +10 -0
- package/dist/tools/storage-web-auth-tools.js +230 -0
- package/dist/tools/tool-metadata.d.ts +8 -0
- package/dist/tools/tool-metadata.js +185 -0
- package/dist/util/batch.d.ts +83 -0
- package/dist/util/batch.js +191 -0
- package/dist/util/capabilities.d.ts +504 -0
- package/dist/util/capabilities.js +254 -0
- package/dist/util/config-store.d.ts +103 -0
- package/dist/util/config-store.js +206 -0
- package/dist/util/config.d.ts +11 -0
- package/dist/util/config.js +28 -0
- package/dist/util/credentials.d.ts +136 -0
- package/dist/util/credentials.js +622 -0
- package/dist/util/deadline.d.ts +22 -0
- package/dist/util/deadline.js +62 -0
- package/dist/util/diagnostics.d.ts +161 -0
- package/dist/util/diagnostics.js +579 -0
- package/dist/util/egress-sanitiser.d.ts +29 -0
- package/dist/util/egress-sanitiser.js +52 -0
- package/dist/util/failure.d.ts +8 -0
- package/dist/util/failure.js +50 -0
- package/dist/util/flake-check.d.ts +109 -0
- package/dist/util/flake-check.js +342 -0
- package/dist/util/invariant.d.ts +25 -0
- package/dist/util/invariant.js +66 -0
- package/dist/util/logging.d.ts +6 -0
- package/dist/util/logging.js +12 -0
- package/dist/util/predicates.d.ts +62 -0
- package/dist/util/predicates.js +340 -0
- package/dist/util/secrets.d.ts +104 -0
- package/dist/util/secrets.js +219 -0
- package/dist/util/tokens.d.ts +6 -0
- package/dist/util/tokens.js +24 -0
- package/dist/util/url-sanitizer.d.ts +19 -0
- package/dist/util/url-sanitizer.js +70 -0
- package/dist/util/version.d.ts +2 -0
- package/dist/util/version.js +21 -0
- package/dist/util/workspace.d.ts +7 -0
- package/dist/util/workspace.js +22 -0
- package/package.json +120 -0
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
// find(query) — natural-language element description → ranked candidate locators
|
|
2
|
+
// with structured evidence. First-consumer : selectorHint follows a
|
|
3
|
+
// fixed preference order with a stability flag; bbox is the visible-rect.
|
|
4
|
+
import { walk } from "./a11y.js";
|
|
5
|
+
import { composeSnapshotForFrame } from "./compose.js";
|
|
6
|
+
import { visibleRect, locatorBoundingBox } from "./bbox.js";
|
|
7
|
+
import { findByRef } from "./snapshot.js";
|
|
8
|
+
const INTERACTIVE_ROLES = new Set([
|
|
9
|
+
"button",
|
|
10
|
+
"link",
|
|
11
|
+
"textbox",
|
|
12
|
+
"searchbox",
|
|
13
|
+
"combobox",
|
|
14
|
+
"checkbox",
|
|
15
|
+
"radio",
|
|
16
|
+
"switch",
|
|
17
|
+
"slider",
|
|
18
|
+
"spinbutton",
|
|
19
|
+
"menuitem",
|
|
20
|
+
"menuitemcheckbox",
|
|
21
|
+
"menuitemradio",
|
|
22
|
+
"option",
|
|
23
|
+
"tab",
|
|
24
|
+
"treeitem",
|
|
25
|
+
]);
|
|
26
|
+
/**
|
|
27
|
+
* Per-call cap for the auto-waiting Playwright probes (`boundingBox`,
|
|
28
|
+
* `isEnabled`) used in the candidate-evaluation loop. find() is a probe
|
|
29
|
+
* tool, not an action — a probe must fail fast when its hint doesn't match
|
|
30
|
+
* a live element. The default `actionTimeout` (30 s) is appropriate for
|
|
31
|
+
* acting on a known element; without a cap here, find() against N candidates
|
|
32
|
+
* whose hints don't resolve to a Playwright locator would burn N × 30 s of
|
|
33
|
+
* wall-clock waiting for nothing. 500 ms comfortably covers a real
|
|
34
|
+
* boundingBox resolution on a matched element (typically 1–50 ms) while
|
|
35
|
+
* keeping the per-candidate worst case bounded.
|
|
36
|
+
*/
|
|
37
|
+
const PROBE_TIMEOUT_MS = 500;
|
|
38
|
+
// Non-interactive structural / layout / landmark wrappers. These *enclose* the
|
|
39
|
+
// thing an agent wants to act on; they are never themselves the click target.
|
|
40
|
+
// When a query is phrased loosely (a product alias rather than the test-attr
|
|
41
|
+
// tokens) one of these can outscore the actual control it contains, so we
|
|
42
|
+
// demote them below an actionable interactive match. Deliberately
|
|
43
|
+
// conservative — list/listitem/article/section are omitted because they can
|
|
44
|
+
// legitimately be the intended target in some UIs.
|
|
45
|
+
const CONTAINER_ROLES = new Set([
|
|
46
|
+
"generic",
|
|
47
|
+
"group",
|
|
48
|
+
"region",
|
|
49
|
+
"toolbar",
|
|
50
|
+
"none",
|
|
51
|
+
"presentation",
|
|
52
|
+
"navigation",
|
|
53
|
+
"complementary",
|
|
54
|
+
"banner",
|
|
55
|
+
"contentinfo",
|
|
56
|
+
"main",
|
|
57
|
+
"application",
|
|
58
|
+
"document",
|
|
59
|
+
"form",
|
|
60
|
+
"search",
|
|
61
|
+
]);
|
|
62
|
+
/**
|
|
63
|
+
* Rank candidates against a natural-language query. The query is tokenised on
|
|
64
|
+
* whitespace; each token is matched (case-insensitively, as substring) against
|
|
65
|
+
* a haystack of role + name + testId. Scoring:
|
|
66
|
+
*
|
|
67
|
+
* - exact-name match: +10
|
|
68
|
+
* - name contains query: +5
|
|
69
|
+
* - testId contains query: +5
|
|
70
|
+
* - role contains query: +2
|
|
71
|
+
* - per-token hit anywhere: +1 each
|
|
72
|
+
* - interactive-role bonus: +2 (the agent's usually after a clickable thing)
|
|
73
|
+
*
|
|
74
|
+
* Candidates with score 0 are dropped. Top `maxCandidates` (default 5) returned.
|
|
75
|
+
*/
|
|
76
|
+
/** The compose layer's shadow-DOM warnings, surfaced only when `pierce` was
|
|
77
|
+
* explicitly opted into (so a pierce-less caller's find() envelope stays
|
|
78
|
+
* byte-identical to pre-v0.5.0). The `low-content` warning is always skipped —
|
|
79
|
+
* it pre-dates this path and was surfaced through snapshot only. */
|
|
80
|
+
function pierceWarnings(composedWarnings, pierce) {
|
|
81
|
+
if (pierce === undefined)
|
|
82
|
+
return [];
|
|
83
|
+
return composedWarnings.filter((w) => !w.startsWith("low-content"));
|
|
84
|
+
}
|
|
85
|
+
/** Walk the (scoped) tree, scoring each node and applying any feedback bonus;
|
|
86
|
+
* returns the score-descending candidate list. */
|
|
87
|
+
function scoreCandidates(walkRoot, q, qTokens, opts) {
|
|
88
|
+
const scored = [];
|
|
89
|
+
for (const { node } of walk(walkRoot)) {
|
|
90
|
+
let score = scoreNode(node, q, qTokens);
|
|
91
|
+
if (score > 0 && opts.feedback) {
|
|
92
|
+
score += opts.feedback.bonusFor(opts.query, {
|
|
93
|
+
testId: node.testId,
|
|
94
|
+
testIdAttr: node.testIdAttr,
|
|
95
|
+
role: node.role,
|
|
96
|
+
name: node.name,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
if (score > 0)
|
|
100
|
+
scored.push({ node, score });
|
|
101
|
+
}
|
|
102
|
+
scored.sort((a, b) => b.score - a.score);
|
|
103
|
+
return scored;
|
|
104
|
+
}
|
|
105
|
+
/** Probe one candidate against the live page (hint disambiguation → bbox →
|
|
106
|
+
* actionability) into a `FindCandidate`. Each probe is independent so the pool
|
|
107
|
+
* runs in parallel; the steps within stay ordered (actionable needs bbox). */
|
|
108
|
+
async function probeCandidate(node, score, ctx) {
|
|
109
|
+
const { locatorRoot, cdp, frame } = ctx;
|
|
110
|
+
const { hint: bareHint, tier, stability } = buildSelectorHint(node);
|
|
111
|
+
// disambiguate when the bare hint matches multiple DOM nodes (needs a locator
|
|
112
|
+
// root; on safari there is none, so use the bare hint as-is).
|
|
113
|
+
const hint = locatorRoot ? await disambiguateHint(locatorRoot, bareHint) : bareHint;
|
|
114
|
+
// Frame-scoped finds skip the CDP visible-rect path (its backendDOMNodeIds are
|
|
115
|
+
// rooted at the top target and don't resolve into OOPIFs); the portable
|
|
116
|
+
// locator-bounding-box path is identical-behaviour.
|
|
117
|
+
let bbox = frame === undefined && cdp !== undefined && node.backendDOMNodeId !== undefined
|
|
118
|
+
? await visibleRect(cdp, node.backendDOMNodeId)
|
|
119
|
+
: null;
|
|
120
|
+
// attached/BYOB: the CDP rect path can spuriously null out a rendered DOM-walk
|
|
121
|
+
// node → fall back to Playwright's locator box before a bad signal classifies a
|
|
122
|
+
// visible element off-screen (which `visibleOnly` would then drop entirely).
|
|
123
|
+
if (bbox === null && locatorRoot)
|
|
124
|
+
bbox = await locatorBoundingBox(locatorRoot, hint, { timeoutMs: PROBE_TIMEOUT_MS });
|
|
125
|
+
// No locator root (safari) → actionability can't be locator-probed, but the
|
|
126
|
+
// DOM-walk PAGE_SCRIPT already filtered to VISIBLE interactive elements, so the
|
|
127
|
+
// node is known-visible. Report `true` rather than fabricate a signal we can't
|
|
128
|
+
// measure. bbox stays null (no protocol rect on safari).
|
|
129
|
+
const actionable = locatorRoot ? await probeActionable(locatorRoot, hint, bbox) : true;
|
|
130
|
+
return {
|
|
131
|
+
ref: node.ref,
|
|
132
|
+
role: node.role,
|
|
133
|
+
name: node.name,
|
|
134
|
+
testId: node.testId,
|
|
135
|
+
stability,
|
|
136
|
+
selectorHint: hint,
|
|
137
|
+
selectorTier: tier,
|
|
138
|
+
bbox,
|
|
139
|
+
clipped: bbox === null,
|
|
140
|
+
actionable,
|
|
141
|
+
score,
|
|
142
|
+
...(node.context ? { context: node.context } : {}),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
export async function find(
|
|
146
|
+
// `null` on the safari engine — it has no Playwright Page, so the locator-based
|
|
147
|
+
// enrichment (disambiguation / bbox / actionability) is skipped and candidates
|
|
148
|
+
// are ranked from the substrate tree alone. Every other engine
|
|
149
|
+
// passes a real Page.
|
|
150
|
+
page, substrate, refs, opts,
|
|
151
|
+
/** Raw CDP handle for the visible-rect bbox fast path — present only on
|
|
152
|
+
* chromium (where a11y nodes carry `backendDOMNodeId`). Off Chromium the
|
|
153
|
+
* walker mints no `backendDOMNodeId`, so this is unused and the portable
|
|
154
|
+
* `locatorBoundingBox` fallback computes the box. Optional so the engine
|
|
155
|
+
* type never enters the find tool path. */
|
|
156
|
+
cdp) {
|
|
157
|
+
// Use the composed tree (a11y + DOM-walk fallback) so we can find candidates that
|
|
158
|
+
// only exist on the DOM-walk side — the #7 win on heavy-SPA targets.
|
|
159
|
+
// when `frame` is set, scope to that frame's DOM-walk-only compose
|
|
160
|
+
// path and bind refs to the frame on the registry so subsequent actions land
|
|
161
|
+
// inside the iframe.
|
|
162
|
+
// `pierce` propagates through to the dom-walk + (when "closed",
|
|
163
|
+
// main-frame only) the CDP pierce path. Omitting `pierce` preserves
|
|
164
|
+
// byte-identical pre-v0.5.0 output.
|
|
165
|
+
const composed = opts.frame && opts.frameId
|
|
166
|
+
? await composeSnapshotForFrame(opts.frame, refs, opts.testAttributes, opts.frameId, {
|
|
167
|
+
pierce: opts.pierce,
|
|
168
|
+
})
|
|
169
|
+
: await substrate.compose(refs, opts.testAttributes, { pierce: opts.pierce });
|
|
170
|
+
const { tree } = composed;
|
|
171
|
+
if (!tree) {
|
|
172
|
+
return { candidates: [], warnings: pierceWarnings(composed.warnings, opts.pierce) };
|
|
173
|
+
}
|
|
174
|
+
// The locator-resolution root: page for main-frame finds, frame for
|
|
175
|
+
// frame-scoped finds. Probes use this root so they exercise the correct DOM tree.
|
|
176
|
+
const locatorRoot = opts.frame ?? page;
|
|
177
|
+
const q = opts.query.toLowerCase();
|
|
178
|
+
const qTokens = q.split(/\s+/).filter(Boolean);
|
|
179
|
+
const max = opts.maxCandidates ?? 5;
|
|
180
|
+
const warnings = [...pierceWarnings(composed.warnings, opts.pierce)];
|
|
181
|
+
// limit walk to subtree rooted at contextRef.
|
|
182
|
+
let walkRoot = tree;
|
|
183
|
+
if (opts.contextRef) {
|
|
184
|
+
const sub = findByRef(tree, opts.contextRef);
|
|
185
|
+
if (sub)
|
|
186
|
+
walkRoot = sub;
|
|
187
|
+
else
|
|
188
|
+
warnings.push(`contextRef=${opts.contextRef} not found; ranking over the full tree instead.`);
|
|
189
|
+
}
|
|
190
|
+
const scored = scoreCandidates(walkRoot, q, qTokens, opts);
|
|
191
|
+
const top = scored.slice(0, max);
|
|
192
|
+
// Per-candidate probing is independent (each candidate's hint, bbox,
|
|
193
|
+
// and actionability are computed against the live page in isolation), so
|
|
194
|
+
// run the top-N pool in parallel. Sequential probing was the dominant
|
|
195
|
+
// find() cost: on a DOM-walk-sourced candidate whose role-locator doesn't
|
|
196
|
+
// resolve to a real Playwright role, every probe call would auto-wait the
|
|
197
|
+
// full `actionTimeout` window before returning. In default operation
|
|
198
|
+
// find() was already capped by the outer 5 s `actionTimeoutMs` anti-wedge
|
|
199
|
+
// but consumed it in full on pages with fall-through-role candidates;
|
|
200
|
+
// without the cap, the 60 s anti-wedge deadline would clip in pathological
|
|
201
|
+
// cases. The probe steps inside each task remain ordered (hint → bbox →
|
|
202
|
+
// actionable depends on bbox), and `PROBE_TIMEOUT_MS` caps any single
|
|
203
|
+
// probe call so a no-match hint fails fast instead of waiting on auto-wait.
|
|
204
|
+
const candidates = await Promise.all(top.map(({ node, score }) => probeCandidate(node, score, { locatorRoot, cdp, frame: opts.frame })));
|
|
205
|
+
// visibility-aware ranking. Stable-partition actionable candidates ahead of
|
|
206
|
+
// non-actionable ones, preserving score order within each tier.
|
|
207
|
+
const { ranked, visibleCount } = rankByVisibility(candidates, opts.visibleOnly === true);
|
|
208
|
+
appendRankingWarnings(warnings, ranked, candidates, visibleCount, opts);
|
|
209
|
+
return { candidates: ranked, warnings };
|
|
210
|
+
}
|
|
211
|
+
/** Append the confidence-floor + no-visible-candidate diagnostic warnings. */
|
|
212
|
+
function appendRankingWarnings(warnings, ranked, candidates, visibleCount, opts) {
|
|
213
|
+
const floor = opts.confidenceFloor ?? 0;
|
|
214
|
+
if (floor > 0 && (ranked.length === 0 || ranked[0].score < floor)) {
|
|
215
|
+
warnings.push(`no candidate scored confidently above ${floor} (top score: ${ranked[0]?.score ?? 0}). ` +
|
|
216
|
+
`Consider falling through to a snapshot scan + raw selector, or rephrasing the query against the element's accessible name / test-attribute value.`);
|
|
217
|
+
}
|
|
218
|
+
// When there are candidates but none visible, that's a strong "the match is
|
|
219
|
+
// wrong" signal — base it on the pre-filter match + visible count so it still
|
|
220
|
+
// fires under `visibleOnly` (where `ranked` is empty when nothing's visible).
|
|
221
|
+
if (visibleCount === 0 && candidates.length > 0) {
|
|
222
|
+
warnings.push(noVisibleCandidateWarning(candidates.length, opts.fallbackHints));
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Stable-partition candidates: actionable ones first (preserving score order),
|
|
227
|
+
* non-actionable (off-screen / clipped / covered / disabled) last — so a
|
|
228
|
+
* slightly-lower-scored *visible* match outranks a high-scored hidden modal.
|
|
229
|
+
* `visibleOnly` drops the hidden tier entirely: an empty result + the
|
|
230
|
+
* "no visible candidate" warning is safer than a confident hidden hit the
|
|
231
|
+
* agent will chase into a coordinate fallback.
|
|
232
|
+
*
|
|
233
|
+
* Within the actionable tier, a second stable partition demotes
|
|
234
|
+
* non-interactive structural/layout containers below interactive controls,
|
|
235
|
+
* but *only* when at least one actionable interactive candidate exists — an
|
|
236
|
+
* aliased query ("the X panel in the right rail") otherwise lets the
|
|
237
|
+
* enclosing wrapper outrank the button/tab the agent actually wants. If no
|
|
238
|
+
* actionable interactive candidate matched, containers are left in place
|
|
239
|
+
* (they may be the best available target). Pure; exported for tests.
|
|
240
|
+
*/
|
|
241
|
+
export function rankByVisibility(candidates, visibleOnly) {
|
|
242
|
+
let visible = candidates.filter((c) => c.actionable === true);
|
|
243
|
+
const hidden = candidates.filter((c) => c.actionable !== true);
|
|
244
|
+
const isContainer = (c) => CONTAINER_ROLES.has(c.role) && !INTERACTIVE_ROLES.has(c.role);
|
|
245
|
+
if (visible.some((c) => INTERACTIVE_ROLES.has(c.role))) {
|
|
246
|
+
const leaves = visible.filter((c) => !isContainer(c));
|
|
247
|
+
const containers = visible.filter(isContainer);
|
|
248
|
+
visible = [...leaves, ...containers];
|
|
249
|
+
}
|
|
250
|
+
return {
|
|
251
|
+
ranked: visibleOnly ? visible : [...visible, ...hidden],
|
|
252
|
+
visibleCount: visible.length,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* the "all candidates off-screen → probably the wrong match" warning.
|
|
257
|
+
* Capability-aware — only names a fallback tool the caller actually has
|
|
258
|
+
* enabled (`coords` ⇐ `action`, `eval_js` ⇐ `eval`). Pure; exported for tests.
|
|
259
|
+
*/
|
|
260
|
+
export function noVisibleCandidateWarning(count, fallbackHints) {
|
|
261
|
+
const suggestions = [];
|
|
262
|
+
if (fallbackHints?.coords)
|
|
263
|
+
suggestions.push("compute the element rect and use `coords` on click/hover");
|
|
264
|
+
if (fallbackHints?.evalJs)
|
|
265
|
+
suggestions.push("read state directly via `eval_js`");
|
|
266
|
+
const tail = suggestions.length ? ` You may want to: ${suggestions.join("; or ")}.` : "";
|
|
267
|
+
return (`no visible candidate — all ${count} match(es) are off-screen / clipped / covered ` +
|
|
268
|
+
`(actionable ≠ true). This usually means the query matched the wrong element ` +
|
|
269
|
+
`(e.g. a hidden modal).${tail}`);
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* . After find() produces a `selectorHint` for the visible candidate,
|
|
273
|
+
* check whether that bare hint matches multiple DOM nodes; if it does, append a
|
|
274
|
+
* disambiguator (`:visible` first, `:nth-match(..., 1)` last resort) so that a
|
|
275
|
+
* caller who transcribes the hint into a flow-file doesn't re-introduce the
|
|
276
|
+
* hidden-duplicate `boundingBox` hang. Best-effort: any error returns the bare hint.
|
|
277
|
+
*/
|
|
278
|
+
async function disambiguateHint(root, hint) {
|
|
279
|
+
try {
|
|
280
|
+
const count = await root.locator(hint).count();
|
|
281
|
+
if (count <= 1)
|
|
282
|
+
return hint;
|
|
283
|
+
const visibleHint = `${hint}:visible`;
|
|
284
|
+
const visibleCount = await root.locator(visibleHint).count();
|
|
285
|
+
if (visibleCount === 1)
|
|
286
|
+
return visibleHint;
|
|
287
|
+
if (visibleCount > 1)
|
|
288
|
+
return `:nth-match(${visibleHint}, 1)`;
|
|
289
|
+
return `:nth-match(${hint}, 1)`;
|
|
290
|
+
}
|
|
291
|
+
catch {
|
|
292
|
+
return hint;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* . Returns `true` iff the element is visible + enabled + on-screen.
|
|
297
|
+
* Else returns a single-word reason. Best-effort; on any error returns `true`
|
|
298
|
+
* (don't manufacture false-negatives).
|
|
299
|
+
*/
|
|
300
|
+
async function probeActionable(root, hint, bbox) {
|
|
301
|
+
if (bbox === null)
|
|
302
|
+
return "off-screen";
|
|
303
|
+
try {
|
|
304
|
+
const loc = root.locator(hint).first();
|
|
305
|
+
// isEnabled auto-waits to the action-timeout default (30 s) when the
|
|
306
|
+
// locator doesn't resolve; cap it. isVisible is documented as
|
|
307
|
+
// non-waiting (the option is deprecated/ignored) so it costs ~0.
|
|
308
|
+
const [isEnabled, isVisible] = await Promise.all([
|
|
309
|
+
loc.isEnabled({ timeout: PROBE_TIMEOUT_MS }).catch(() => true),
|
|
310
|
+
loc.isVisible().catch(() => true),
|
|
311
|
+
]);
|
|
312
|
+
if (!isEnabled)
|
|
313
|
+
return "disabled";
|
|
314
|
+
if (!isVisible)
|
|
315
|
+
return "off-screen";
|
|
316
|
+
// "covered" — requires `elementFromPoint` at the bbox center and an
|
|
317
|
+
// identity check. Skipped for now (~+10 LOC + a CDP call; not load-bearing
|
|
318
|
+
// for the headline cases). Leave the union member in place so callers
|
|
319
|
+
// handle it; the value is just never produced yet.
|
|
320
|
+
return true;
|
|
321
|
+
}
|
|
322
|
+
catch {
|
|
323
|
+
return true;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
/** Score a node's match against a tokenised query — also weights testId / testIdAttr
|
|
327
|
+
* hits high so a query like "feature-area language" lands on `data-testid="feature-panel-language-input"`
|
|
328
|
+
* even when the role tree doesn't surface a wrapper. */
|
|
329
|
+
const INPUT_LIKE_ROLES = new Set(["input", "textbox", "searchbox", "combobox", "spinbutton"]);
|
|
330
|
+
/** Direct name / testId / role match scoring (exact + substring). testId hits
|
|
331
|
+
* weigh heavier — `<input>`-shaped roles typically have an empty accessible
|
|
332
|
+
* name, so the testId is the load-bearing signal. */
|
|
333
|
+
function scoreDirect(nameLower, testIdLower, roleLower, q) {
|
|
334
|
+
let s = 0;
|
|
335
|
+
if (nameLower === q)
|
|
336
|
+
s += 10;
|
|
337
|
+
if (nameLower.includes(q))
|
|
338
|
+
s += 5;
|
|
339
|
+
if (testIdLower === q)
|
|
340
|
+
s += 15;
|
|
341
|
+
if (testIdLower.includes(q))
|
|
342
|
+
s += 10;
|
|
343
|
+
if (roleLower.includes(q))
|
|
344
|
+
s += 2;
|
|
345
|
+
return s;
|
|
346
|
+
}
|
|
347
|
+
/** Per-query-token substring scoring on name + testId. testId tokens are
|
|
348
|
+
* amplified for icon-only controls (no accessible name) where the testId is the
|
|
349
|
+
* only signal. */
|
|
350
|
+
function scoreTokens(nameLower, testIdLower, qTokens, isIconOnly) {
|
|
351
|
+
let s = 0;
|
|
352
|
+
for (const t of qTokens) {
|
|
353
|
+
if (t.length < 2)
|
|
354
|
+
continue;
|
|
355
|
+
if (nameLower.includes(t))
|
|
356
|
+
s += 1;
|
|
357
|
+
if (testIdLower.includes(t))
|
|
358
|
+
s += isIconOnly ? 3 : 2;
|
|
359
|
+
}
|
|
360
|
+
return s;
|
|
361
|
+
}
|
|
362
|
+
/** Input-shaped boost: +3 once when the node is input-like AND any testId token
|
|
363
|
+
* matched (the round-3 case). */
|
|
364
|
+
function scoreInputTestIdBoost(node, testIdLower, qTokens) {
|
|
365
|
+
if (!testIdLower || !INPUT_LIKE_ROLES.has(node.role))
|
|
366
|
+
return 0;
|
|
367
|
+
return qTokens.some((t) => t.length >= 2 && testIdLower.includes(t)) ? 3 : 0;
|
|
368
|
+
}
|
|
369
|
+
/** Trimmed text-content scoring (title tooltip / sr-only label / glyph-adjacent
|
|
370
|
+
* text) — often the only human-readable hint on an icon-only control. */
|
|
371
|
+
function scoreText(textLower, q, qTokens, isIconOnly) {
|
|
372
|
+
if (!textLower)
|
|
373
|
+
return 0;
|
|
374
|
+
let s = 0;
|
|
375
|
+
if (textLower === q)
|
|
376
|
+
s += 6;
|
|
377
|
+
else if (textLower.includes(q))
|
|
378
|
+
s += 3;
|
|
379
|
+
for (const t of qTokens) {
|
|
380
|
+
if (t.length < 2)
|
|
381
|
+
continue;
|
|
382
|
+
if (textLower.includes(t))
|
|
383
|
+
s += isIconOnly ? 2 : 1;
|
|
384
|
+
}
|
|
385
|
+
return s;
|
|
386
|
+
}
|
|
387
|
+
export function scoreNode(node, q, qTokens) {
|
|
388
|
+
const nameLower = (node.name ?? "").toLowerCase();
|
|
389
|
+
const testIdLower = (node.testId ?? "").toLowerCase();
|
|
390
|
+
const isIconOnly = !nameLower && !!testIdLower;
|
|
391
|
+
let s = scoreDirect(nameLower, testIdLower, node.role.toLowerCase(), q);
|
|
392
|
+
s += scoreTokens(nameLower, testIdLower, qTokens, isIconOnly);
|
|
393
|
+
if (s > 0 && INTERACTIVE_ROLES.has(node.role))
|
|
394
|
+
s += 2;
|
|
395
|
+
s += scoreInputTestIdBoost(node, testIdLower, qTokens);
|
|
396
|
+
s += scoreText((node.text ?? "").toLowerCase(), q, qTokens, isIconOnly);
|
|
397
|
+
// Active / selected state bonuses an existing match (the live feature area the
|
|
398
|
+
// agent means) — disambiguates the active side-panel tab from inert siblings.
|
|
399
|
+
const isActive = node.selected === true || node.pressed === true || node.checked === true;
|
|
400
|
+
if (s > 0 && isActive)
|
|
401
|
+
s += 3;
|
|
402
|
+
return s;
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* The five-tier preference order from :
|
|
406
|
+
* 1. `[<test-attr>="…"]` → stability "high" (any configured test-attribute)
|
|
407
|
+
* 2. role + accessible name → stability "medium"
|
|
408
|
+
* 3. stable text on stable role → covered by tier 2 (the DOM-walk's nameFor()
|
|
409
|
+
* computes name from aria-label / labelledby /
|
|
410
|
+
* textContent in that order, so a `<button>Submit</button>`
|
|
411
|
+
* already gets `role=button[name="Submit"]` via tier 2)
|
|
412
|
+
* 4. structural (#id, semantic) → stability "low" (id present + id-shaped stable)
|
|
413
|
+
* 5. positional (last resort) → stability "low"
|
|
414
|
+
*
|
|
415
|
+
* update: tier 4 now fires when the node has an HTML `id` attribute that
|
|
416
|
+
* looks stable (not a numeric/UUID content-keyed id). The id-stability heuristic:
|
|
417
|
+
* reject pure-numeric (`123`), short numeric+letter combos that look generated
|
|
418
|
+
* (e.g. `mui-1234`), or strings matching common content-keyed shapes. Anything
|
|
419
|
+
* with two or more `-`/`_`-separated word segments is treated as stable.
|
|
420
|
+
*/
|
|
421
|
+
export function buildSelectorHint(node) {
|
|
422
|
+
if (node.testId) {
|
|
423
|
+
const attr = node.testIdAttr ?? "data-testid";
|
|
424
|
+
return { hint: `[${attr}=${JSON.stringify(node.testId)}]`, tier: 1, stability: "high" };
|
|
425
|
+
}
|
|
426
|
+
if (node.name) {
|
|
427
|
+
return {
|
|
428
|
+
hint: `role=${node.role}[name=${JSON.stringify(node.name)}]`,
|
|
429
|
+
tier: 2,
|
|
430
|
+
stability: "medium",
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
if (node.id && isLikelyStableId(node.id)) {
|
|
434
|
+
return { hint: `#${cssEscape(node.id)}`, tier: 4, stability: "low" };
|
|
435
|
+
}
|
|
436
|
+
// Tier 5 fallback — role only. The agent should treat "low" as "ask a human
|
|
437
|
+
// or refuse to transcribe."
|
|
438
|
+
return { hint: `role=${node.role}`, tier: 5, stability: "low" };
|
|
439
|
+
}
|
|
440
|
+
/** Heuristic: is this HTML `id` value likely to survive across page reloads?
|
|
441
|
+
* Rejects content-keyed shapes (pure-numeric, MUI-generated `mui-N`, UUID-shaped).
|
|
442
|
+
* Accepts ids with two or more word segments separated by `-`/`_`/`:`. */
|
|
443
|
+
export function isLikelyStableId(id) {
|
|
444
|
+
// Pure numeric → content-keyed.
|
|
445
|
+
if (/^\d+$/.test(id))
|
|
446
|
+
return false;
|
|
447
|
+
// MUI / Radix / framework-generated short tags.
|
|
448
|
+
if (/^(mui|radix|headlessui|reach|react-aria)[-_]?[a-z0-9]+$/i.test(id))
|
|
449
|
+
return false;
|
|
450
|
+
// UUID-shaped.
|
|
451
|
+
if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id))
|
|
452
|
+
return false;
|
|
453
|
+
// 8+ hex chars only → likely a hash / content-keyed.
|
|
454
|
+
if (/^[0-9a-f]{8,}$/i.test(id) && id.length <= 32)
|
|
455
|
+
return false;
|
|
456
|
+
// Multi-segment (kebab/snake/colon-separated, ≥2 segments, each with letters) → stable.
|
|
457
|
+
const segments = id.split(/[-_:]/).filter((s) => s.length > 0);
|
|
458
|
+
if (segments.length >= 2 && segments.every((s) => /[a-z]/i.test(s)))
|
|
459
|
+
return true;
|
|
460
|
+
// Single-segment, ≥3 chars, has letters → probably stable.
|
|
461
|
+
if (id.length >= 3 && /[a-z]/i.test(id) && !/^\d/.test(id))
|
|
462
|
+
return true;
|
|
463
|
+
return false;
|
|
464
|
+
}
|
|
465
|
+
/** Minimal CSS escape for the id-selector value — covers the common cases
|
|
466
|
+
* (escapes leading digit, special chars). Doesn't aim to be a full CSS.escape() shim. */
|
|
467
|
+
function cssEscape(s) {
|
|
468
|
+
// Escape any character that isn't [A-Za-z0-9_-].
|
|
469
|
+
return s.replace(/([^a-zA-Z0-9_-])/g, "\\$1").replace(/^(\d)/, "\\3$1 ");
|
|
470
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { Frame, Page } from "playwright-core";
|
|
2
|
+
/** Stable sentinel for the page's top-level frame. */
|
|
3
|
+
export declare const MAIN_FRAME_ID = "f0";
|
|
4
|
+
export interface FrameInfo {
|
|
5
|
+
/** Stable per-session ID — pass back to snapshot/find/action via `frame`. */
|
|
6
|
+
frameId: string;
|
|
7
|
+
/** Parent frame's ID. Absent for the main frame. */
|
|
8
|
+
parentFrameId?: string;
|
|
9
|
+
url: string;
|
|
10
|
+
/** `<iframe name="…">` or empty when not set. */
|
|
11
|
+
name: string;
|
|
12
|
+
isMainFrame: boolean;
|
|
13
|
+
/** Origin parsed from `url` (`http://example.com`); empty for non-URL
|
|
14
|
+
* frames (`about:blank`, `data:` URLs). */
|
|
15
|
+
origin: string;
|
|
16
|
+
}
|
|
17
|
+
/** Per-session cache: structural fingerprint → stable frameId. */
|
|
18
|
+
export declare class FrameRegistry {
|
|
19
|
+
private idByFingerprint;
|
|
20
|
+
private frameByFingerprint;
|
|
21
|
+
private counter;
|
|
22
|
+
/** Assign (or look up) a stable ID for `frame`. The main frame always
|
|
23
|
+
* gets `MAIN_FRAME_ID`. Child frames get `f1`, `f2`, … by first-seen
|
|
24
|
+
* order; identical-fingerprint frames across calls keep their ID. */
|
|
25
|
+
idFor(frame: Frame, fingerprint: string): string;
|
|
26
|
+
/** Test/introspection only. */
|
|
27
|
+
size(): number;
|
|
28
|
+
}
|
|
29
|
+
/** Walk the page's frame tree and emit `FrameInfo[]`, depth-first. The
|
|
30
|
+
* main frame is always first. */
|
|
31
|
+
export declare function listFrames(page: Page, registry: FrameRegistry): FrameInfo[];
|
|
32
|
+
/** Structural fingerprint — short hex hash of the inputs that should keep
|
|
33
|
+
* a child frame's identity stable across calls within a session. Includes
|
|
34
|
+
* parent ID + sibling index + name + url origin. Not URL-path-sensitive so
|
|
35
|
+
* intra-iframe navigation doesn't reset the ID; not name-only so two
|
|
36
|
+
* identically-named iframes in the same parent still get distinct IDs via
|
|
37
|
+
* their sibling index. Exported for unit tests. */
|
|
38
|
+
export declare function fingerprintOf(frame: Frame, parentFrameId: string | undefined, siblingIndex: number): string;
|
|
39
|
+
/** Origin of a frame URL — `http://host` or empty for opaque schemes. */
|
|
40
|
+
export declare function originOf(url: string): string;
|
|
41
|
+
/** Resolve a `frameId` back to a Playwright `Frame` handle. Returns the
|
|
42
|
+
* main frame for `MAIN_FRAME_ID`; null for an unknown ID. The walk is
|
|
43
|
+
* cheap (frame trees are small) and avoids needing a reverse Map that
|
|
44
|
+
* could go stale when a frame is detached. */
|
|
45
|
+
export declare function resolveFrameById(page: Page, registry: FrameRegistry, frameId: string): Frame | null;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// Frame-scoped observation.
|
|
2
|
+
//
|
|
3
|
+
// Iframes are everywhere on real pages; today's CDP-rooted snapshot/find
|
|
4
|
+
// observe only the top frame. This module exposes the page's frame tree to
|
|
5
|
+
// agents and, for the consumers in this directory (snapshot/find/action),
|
|
6
|
+
// provides stable frame IDs that round-trip back into a Playwright `Frame`
|
|
7
|
+
// for scoped reads and ref-resolution.
|
|
8
|
+
//
|
|
9
|
+
// Frame ID scheme:
|
|
10
|
+
// - The main frame's ID is always `f0` (well-known sentinel — agents
|
|
11
|
+
// can address it explicitly when they want to be unambiguous about
|
|
12
|
+
// "the main frame", though omitting `frame` from snapshot/find keeps
|
|
13
|
+
// the existing main-frame-only behaviour byte-identical for back-compat).
|
|
14
|
+
// - Child frames mint a stable `fN` (N=1..) using a session-local
|
|
15
|
+
// monotonic counter keyed by a structural fingerprint
|
|
16
|
+
// (parent-id + index among siblings + name + url-origin). Same iframe
|
|
17
|
+
// across two `frames_list` calls keeps the same ID.
|
|
18
|
+
//
|
|
19
|
+
// Cross-origin caveat: Playwright's `Frame` handle works for both
|
|
20
|
+
// same-origin and cross-origin iframes — `frame.locator(...)`,
|
|
21
|
+
// `frame.evaluate(...)`, and the action surface all transparently cross
|
|
22
|
+
// the OOPIF boundary. The one observable gap is the CDP
|
|
23
|
+
// `Accessibility.getFullAXTree` path used by main-frame snapshots: per
|
|
24
|
+
// frame, the CDP session is rooted at the top target, so child frames
|
|
25
|
+
// fall back to the DOM-walk pass only (no a11y nodes from CDP). This is
|
|
26
|
+
// surfaced as a warning on frame-scoped snapshots.
|
|
27
|
+
import { createHash } from "node:crypto";
|
|
28
|
+
/** Stable sentinel for the page's top-level frame. */
|
|
29
|
+
export const MAIN_FRAME_ID = "f0";
|
|
30
|
+
/** Per-session cache: structural fingerprint → stable frameId. */
|
|
31
|
+
export class FrameRegistry {
|
|
32
|
+
idByFingerprint = new Map();
|
|
33
|
+
frameByFingerprint = new WeakMap();
|
|
34
|
+
counter = 0;
|
|
35
|
+
/** Assign (or look up) a stable ID for `frame`. The main frame always
|
|
36
|
+
* gets `MAIN_FRAME_ID`. Child frames get `f1`, `f2`, … by first-seen
|
|
37
|
+
* order; identical-fingerprint frames across calls keep their ID. */
|
|
38
|
+
idFor(frame, fingerprint) {
|
|
39
|
+
// Per-instance shortcut: a Playwright Frame handle that we've seen this
|
|
40
|
+
// session keeps the same ID — even if its URL changes mid-navigation.
|
|
41
|
+
const cached = this.frameByFingerprint.get(frame);
|
|
42
|
+
if (cached)
|
|
43
|
+
return cached;
|
|
44
|
+
let id = this.idByFingerprint.get(fingerprint);
|
|
45
|
+
if (!id) {
|
|
46
|
+
id = fingerprint === MAIN_FRAME_FINGERPRINT ? MAIN_FRAME_ID : `f${++this.counter}`;
|
|
47
|
+
this.idByFingerprint.set(fingerprint, id);
|
|
48
|
+
}
|
|
49
|
+
this.frameByFingerprint.set(frame, id);
|
|
50
|
+
return id;
|
|
51
|
+
}
|
|
52
|
+
/** Test/introspection only. */
|
|
53
|
+
size() {
|
|
54
|
+
return this.idByFingerprint.size;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/** Reserved fingerprint for the main frame. */
|
|
58
|
+
const MAIN_FRAME_FINGERPRINT = "__main__";
|
|
59
|
+
/** Walk the page's frame tree and emit `FrameInfo[]`, depth-first. The
|
|
60
|
+
* main frame is always first. */
|
|
61
|
+
export function listFrames(page, registry) {
|
|
62
|
+
const out = [];
|
|
63
|
+
const main = page.mainFrame();
|
|
64
|
+
visitFrame(main, undefined, undefined, registry, out);
|
|
65
|
+
return out;
|
|
66
|
+
}
|
|
67
|
+
function visitFrame(frame, parentFrameId, siblingIndex, registry, out) {
|
|
68
|
+
const isMain = frame.parentFrame() === null;
|
|
69
|
+
const fingerprint = isMain
|
|
70
|
+
? MAIN_FRAME_FINGERPRINT
|
|
71
|
+
: fingerprintOf(frame, parentFrameId, siblingIndex ?? 0);
|
|
72
|
+
const frameId = registry.idFor(frame, fingerprint);
|
|
73
|
+
out.push({
|
|
74
|
+
frameId,
|
|
75
|
+
...(parentFrameId ? { parentFrameId } : {}),
|
|
76
|
+
url: frame.url(),
|
|
77
|
+
name: frame.name(),
|
|
78
|
+
isMainFrame: isMain,
|
|
79
|
+
origin: originOf(frame.url()),
|
|
80
|
+
});
|
|
81
|
+
const children = frame.childFrames();
|
|
82
|
+
for (let i = 0; i < children.length; i++) {
|
|
83
|
+
visitFrame(children[i], frameId, i, registry, out);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/** Structural fingerprint — short hex hash of the inputs that should keep
|
|
87
|
+
* a child frame's identity stable across calls within a session. Includes
|
|
88
|
+
* parent ID + sibling index + name + url origin. Not URL-path-sensitive so
|
|
89
|
+
* intra-iframe navigation doesn't reset the ID; not name-only so two
|
|
90
|
+
* identically-named iframes in the same parent still get distinct IDs via
|
|
91
|
+
* their sibling index. Exported for unit tests. */
|
|
92
|
+
export function fingerprintOf(frame, parentFrameId, siblingIndex) {
|
|
93
|
+
const raw = [parentFrameId ?? "", String(siblingIndex), frame.name(), originOf(frame.url())].join("|");
|
|
94
|
+
return createHash("sha256").update(raw).digest("hex").slice(0, 12);
|
|
95
|
+
}
|
|
96
|
+
/** Origin of a frame URL — `http://host` or empty for opaque schemes. */
|
|
97
|
+
export function originOf(url) {
|
|
98
|
+
try {
|
|
99
|
+
const u = new URL(url);
|
|
100
|
+
if (u.protocol === "about:" || u.protocol === "data:" || u.protocol === "blob:") {
|
|
101
|
+
return "";
|
|
102
|
+
}
|
|
103
|
+
return u.origin;
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return "";
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/** Resolve a `frameId` back to a Playwright `Frame` handle. Returns the
|
|
110
|
+
* main frame for `MAIN_FRAME_ID`; null for an unknown ID. The walk is
|
|
111
|
+
* cheap (frame trees are small) and avoids needing a reverse Map that
|
|
112
|
+
* could go stale when a frame is detached. */
|
|
113
|
+
export function resolveFrameById(page, registry, frameId) {
|
|
114
|
+
if (frameId === MAIN_FRAME_ID)
|
|
115
|
+
return page.mainFrame();
|
|
116
|
+
return findFrame(page.mainFrame(), undefined, undefined, registry, frameId);
|
|
117
|
+
}
|
|
118
|
+
function findFrame(frame, parentFrameId, siblingIndex, registry, target) {
|
|
119
|
+
const isMain = frame.parentFrame() === null;
|
|
120
|
+
const fingerprint = isMain
|
|
121
|
+
? MAIN_FRAME_FINGERPRINT
|
|
122
|
+
: fingerprintOf(frame, parentFrameId, siblingIndex ?? 0);
|
|
123
|
+
const id = registry.idFor(frame, fingerprint);
|
|
124
|
+
if (id === target)
|
|
125
|
+
return frame;
|
|
126
|
+
const children = frame.childFrames();
|
|
127
|
+
for (let i = 0; i < children.length; i++) {
|
|
128
|
+
const hit = findFrame(children[i], id, i, registry, target);
|
|
129
|
+
if (hit)
|
|
130
|
+
return hit;
|
|
131
|
+
}
|
|
132
|
+
return null;
|
|
133
|
+
}
|