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,376 @@
|
|
|
1
|
+
// `asset_export` — filter the session's network ring and persist matching
|
|
2
|
+
// responses to a workspace-rooted directory.
|
|
3
|
+
//
|
|
4
|
+
// Design posture (mirrors `downloads.ts`):
|
|
5
|
+
// - **Workspace-rooted paths only.** Exports land under
|
|
6
|
+
// `$BROWX_WORKSPACE/assets/<sessionId>-<ISO>/` by default; an explicit
|
|
7
|
+
// `intoDir` is resolved INSIDE the workspace (escape rejected).
|
|
8
|
+
// - **Filename sanitisation** is the same posture as `downloads.sanitiseFilename`
|
|
9
|
+
// — no path separators, no NUL/control bytes, no leading dots, length-capped,
|
|
10
|
+
// all-stripped names fall back to `"asset"`. Collisions are resolved by
|
|
11
|
+
// appending `-N` to the stem so a second `logo.png` becomes `logo-1.png`.
|
|
12
|
+
// - **Bounded.** Caller can raise `maxCount` / `maxBytes` but never above a
|
|
13
|
+
// hard ceiling — a misconfigured filter on a long-running session must not
|
|
14
|
+
// fill the workspace. Defaults: 10000 files / 500 MiB.
|
|
15
|
+
// - **CORS tolerance.** When the response body isn't in the renderer's cache
|
|
16
|
+
// (CDP `Network.getResponseBody` returns "not available" — bodies are
|
|
17
|
+
// short-lived) we fall back to a same-origin in-page `fetch()` for the
|
|
18
|
+
// original URL. Cross-origin URLs without CORS headers may reject; we record
|
|
19
|
+
// the failure on `droppedCount` and keep going.
|
|
20
|
+
// - **No new capability.** Reuses `file-io` (same as `upload_file` /
|
|
21
|
+
// `downloads_capture` / `download_get`).
|
|
22
|
+
import { mkdirSync, writeFileSync, statSync } from "node:fs";
|
|
23
|
+
import { join, resolve, sep, basename } from "node:path";
|
|
24
|
+
import { log } from "../util/logging.js";
|
|
25
|
+
// ---------- caps & defaults --------------------------------------------------
|
|
26
|
+
/** Hard ceiling on the per-call file count cap — even a caller-supplied
|
|
27
|
+
* `maxCount` is clamped to this. Bounds a runaway export on a long session. */
|
|
28
|
+
export const ASSET_EXPORT_HARD_MAX_COUNT = 50_000;
|
|
29
|
+
/** Hard ceiling on the per-call byte cap. */
|
|
30
|
+
export const ASSET_EXPORT_HARD_MAX_BYTES = 2 * 1024 * 1024 * 1024; // 2 GiB
|
|
31
|
+
/** Default per-call file count cap. Caller can override up to the hard max. */
|
|
32
|
+
export const ASSET_EXPORT_DEFAULT_MAX_COUNT = 10_000;
|
|
33
|
+
/** Default per-call byte cap (500 MiB). */
|
|
34
|
+
export const ASSET_EXPORT_DEFAULT_MAX_BYTES = 500 * 1024 * 1024;
|
|
35
|
+
/** Maximum bytes for a single response body — same posture as
|
|
36
|
+
* `fetchResponseBody`'s default. Larger responses are skipped with a
|
|
37
|
+
* warning so one huge video can't blow the whole export. */
|
|
38
|
+
const SINGLE_BODY_MAX_BYTES = 64 * 1024 * 1024; // 64 MiB
|
|
39
|
+
/** Cap on the on-disk filename length, accounting for the `-N` collision
|
|
40
|
+
* suffix room and 255-byte filesystem name limits. */
|
|
41
|
+
const FILENAME_MAX_CHARS = 200;
|
|
42
|
+
// ---------- pure helpers (exported for unit tests) ---------------------------
|
|
43
|
+
/** Build the regex once at the boundary. Throws `Error` on invalid source so
|
|
44
|
+
* the tool layer surfaces a structured failure instead of a runtime crash
|
|
45
|
+
* deep inside the loop. */
|
|
46
|
+
export function compileUrlPattern(src) {
|
|
47
|
+
if (!src)
|
|
48
|
+
return null;
|
|
49
|
+
try {
|
|
50
|
+
return new RegExp(src, "i");
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
throw new Error(`asset_export: invalid \`filter.urlPattern\` — ${err instanceof Error ? err.message : String(err)}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/** Status gate: an explicit allow-list, else default 2xx. */
|
|
57
|
+
function statusMatches(entry, status) {
|
|
58
|
+
if (entry.status === undefined)
|
|
59
|
+
return false;
|
|
60
|
+
return status ? status.has(entry.status) : entry.status >= 200 && entry.status < 300;
|
|
61
|
+
}
|
|
62
|
+
/** MIME gate: substring on the captured Content-Type; no mime info → reject
|
|
63
|
+
* (an entry that never reported a Content-Type can't be classified). */
|
|
64
|
+
function mimeMatches(entry, mime) {
|
|
65
|
+
if (!mime || mime.length === 0)
|
|
66
|
+
return true;
|
|
67
|
+
if (!entry.mimeType)
|
|
68
|
+
return false;
|
|
69
|
+
const lower = entry.mimeType.toLowerCase();
|
|
70
|
+
return mime.some((m) => lower.includes(m.toLowerCase()));
|
|
71
|
+
}
|
|
72
|
+
/** Byte-bound gate: only enforced when bytes have landed (still-in-flight
|
|
73
|
+
* entries are admitted here; the fetch step counts actual bytes). */
|
|
74
|
+
function bytesMatch(entry, minBytes, maxBytes) {
|
|
75
|
+
if (typeof entry.bytes !== "number")
|
|
76
|
+
return true;
|
|
77
|
+
if (typeof minBytes === "number" && entry.bytes < minBytes)
|
|
78
|
+
return false;
|
|
79
|
+
if (typeof maxBytes === "number" && entry.bytes > maxBytes)
|
|
80
|
+
return false;
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
/** Filter a single `NetworkEntry` against a normalised filter. Pure — uses only
|
|
84
|
+
* the entry's captured metadata; no I/O. Exported so the test suite can pin
|
|
85
|
+
* every branch without spinning a NetworkBuffer. */
|
|
86
|
+
export function matchesFilter(entry, filter) {
|
|
87
|
+
if (!statusMatches(entry, filter.status))
|
|
88
|
+
return false;
|
|
89
|
+
if (!mimeMatches(entry, filter.mime))
|
|
90
|
+
return false;
|
|
91
|
+
if (filter.urlPattern && !filter.urlPattern.test(entry.url))
|
|
92
|
+
return false;
|
|
93
|
+
return bytesMatch(entry, filter.minBytes, filter.maxBytes);
|
|
94
|
+
}
|
|
95
|
+
/** Sanitise a URL-derived asset filename. Same posture as
|
|
96
|
+
* `downloads.sanitiseFilename`:
|
|
97
|
+
* - strip path separators (`/`, `\`) and NUL/control bytes — collapses any
|
|
98
|
+
* traversal attempt to a flat filename.
|
|
99
|
+
* - collapse runs of dots so the literal `..` substring never survives.
|
|
100
|
+
* - strip leading dots so we don't write a hidden file.
|
|
101
|
+
* - cap length (leaves room for a `-N` collision suffix).
|
|
102
|
+
* - empty / all-stripped → fall back to `"asset"`.
|
|
103
|
+
* Exported for unit tests. */
|
|
104
|
+
export function sanitiseAssetFilename(raw) {
|
|
105
|
+
if (typeof raw !== "string")
|
|
106
|
+
return "asset";
|
|
107
|
+
// strip NUL + control bytes (0x00-0x1f, 0x7f) and path separators.
|
|
108
|
+
// eslint-disable-next-line no-control-regex
|
|
109
|
+
let name = raw.replace(/[\x00-\x1f\x7f/\\]/g, "_");
|
|
110
|
+
// collapse runs of dots ("../.." → ".") so the literal substring `..`
|
|
111
|
+
// never survives — eliminates "looks-like-traversal" even though the
|
|
112
|
+
// lack of separators already makes traversal impossible.
|
|
113
|
+
name = name.replace(/\.{2,}/g, ".");
|
|
114
|
+
// collapse repeated underscores from the strip pass.
|
|
115
|
+
name = name.replace(/_+/g, "_");
|
|
116
|
+
// strip leading dots so we don't write a hidden file.
|
|
117
|
+
name = name.replace(/^\.+/, "");
|
|
118
|
+
// strip leading/trailing whitespace, dots, underscores — these only exist
|
|
119
|
+
// because of the strip passes above.
|
|
120
|
+
name = name.replace(/^[._\s]+|[._\s]+$/g, "");
|
|
121
|
+
if (name.length > FILENAME_MAX_CHARS)
|
|
122
|
+
name = name.slice(0, FILENAME_MAX_CHARS);
|
|
123
|
+
if (!name)
|
|
124
|
+
return "asset";
|
|
125
|
+
return name;
|
|
126
|
+
}
|
|
127
|
+
/** Derive a base filename from a URL: take the last path segment, decode
|
|
128
|
+
* percent-encoding, drop a query string. Falls back to `"asset"` when the
|
|
129
|
+
* URL has no usable basename (e.g. `https://example.com/`). Exported for
|
|
130
|
+
* unit tests. */
|
|
131
|
+
export function filenameFromUrl(url) {
|
|
132
|
+
let pathname;
|
|
133
|
+
try {
|
|
134
|
+
pathname = new URL(url).pathname;
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
pathname = url;
|
|
138
|
+
}
|
|
139
|
+
// basename strips the trailing slash; pathname like `/` → `""`.
|
|
140
|
+
let base = basename(pathname);
|
|
141
|
+
if (!base)
|
|
142
|
+
base = "asset";
|
|
143
|
+
try {
|
|
144
|
+
base = decodeURIComponent(base);
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
// malformed percent-encoding — keep the raw form, sanitisation handles it.
|
|
148
|
+
}
|
|
149
|
+
return sanitiseAssetFilename(base);
|
|
150
|
+
}
|
|
151
|
+
/** Resolve a collision by appending `-N` before the extension. Pure; uses
|
|
152
|
+
* the supplied `Set` of already-used names. Exported for unit tests. */
|
|
153
|
+
export function resolveCollision(name, used) {
|
|
154
|
+
if (!used.has(name))
|
|
155
|
+
return name;
|
|
156
|
+
const dot = name.lastIndexOf(".");
|
|
157
|
+
const stem = dot > 0 ? name.slice(0, dot) : name;
|
|
158
|
+
const ext = dot > 0 ? name.slice(dot) : "";
|
|
159
|
+
for (let i = 1; i < 1_000_000; i++) {
|
|
160
|
+
const candidate = `${stem}-${i}${ext}`;
|
|
161
|
+
if (!used.has(candidate))
|
|
162
|
+
return candidate;
|
|
163
|
+
}
|
|
164
|
+
// 1M collisions on the same stem is effectively impossible; if it happens,
|
|
165
|
+
// disambiguate with the current timestamp + a random tail.
|
|
166
|
+
return `${stem}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${ext}`;
|
|
167
|
+
}
|
|
168
|
+
/** ISO-8601 timestamp safe for use in a directory name (`:` replaced with `-`). */
|
|
169
|
+
export function timestampForDir(d = new Date()) {
|
|
170
|
+
return d.toISOString().replace(/[:.]/g, "-");
|
|
171
|
+
}
|
|
172
|
+
// ---------- workspace path safety -------------------------------------------
|
|
173
|
+
/** Resolve the export dir. Default: `assets/<sessionId>-<ISO>/`. Caller-
|
|
174
|
+
* supplied `intoDir` is resolved INSIDE `workspaceRoot`; any escape throws
|
|
175
|
+
* the same structured error as `upload_file` / `pdf_save`. */
|
|
176
|
+
export function resolveAssetExportDir(workspaceRoot, sessionId, intoDir) {
|
|
177
|
+
const rel = intoDir ?? `assets/${sessionId}-${timestampForDir()}`;
|
|
178
|
+
const resolved = resolve(workspaceRoot, rel);
|
|
179
|
+
if (resolved !== workspaceRoot && !resolved.startsWith(workspaceRoot + sep)) {
|
|
180
|
+
throw new Error(`asset_export: \`intoDir\` must resolve inside $BROWX_WORKSPACE — got "${rel}". ` +
|
|
181
|
+
`Use a workspace-relative path.`);
|
|
182
|
+
}
|
|
183
|
+
return resolved;
|
|
184
|
+
}
|
|
185
|
+
// ---------- body fetch -------------------------------------------------------
|
|
186
|
+
/** Try CDP `Network.getResponseBody` first; on "not available" fall back to
|
|
187
|
+
* an in-page `fetch()` against the original URL. Returns the bytes (decoded)
|
|
188
|
+
* + the effective content-type, or an `{ error }` envelope when both attempts
|
|
189
|
+
* fail. The CORS caveat: cross-origin URLs without permissive CORS headers
|
|
190
|
+
* will reject the in-page fetch; that's surfaced as `droppedCount` by the
|
|
191
|
+
* caller, not a crash. */
|
|
192
|
+
export async function fetchBodyBytes(cdp, page, entry) {
|
|
193
|
+
// First try the renderer's cached body — short-lived but free.
|
|
194
|
+
if (entry.requestId) {
|
|
195
|
+
try {
|
|
196
|
+
const { body, base64Encoded } = await cdp.send("Network.getResponseBody", {
|
|
197
|
+
requestId: entry.requestId,
|
|
198
|
+
});
|
|
199
|
+
const buf = base64Encoded ? Buffer.from(body, "base64") : Buffer.from(body, "utf8");
|
|
200
|
+
const out = { ok: true, bytes: buf };
|
|
201
|
+
if (entry.mimeType !== undefined)
|
|
202
|
+
out.mimeType = entry.mimeType;
|
|
203
|
+
return out;
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
// body discarded by the renderer — fall through to the in-page fetch.
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
// Fallback: in-page `fetch()`. CORS caveat — cross-origin URLs without
|
|
210
|
+
// permissive headers will throw; we report it as a drop, not a crash.
|
|
211
|
+
try {
|
|
212
|
+
const result = await page.evaluate(async (url) => {
|
|
213
|
+
try {
|
|
214
|
+
const res = await fetch(url);
|
|
215
|
+
const buf = await res.arrayBuffer();
|
|
216
|
+
const arr = new Uint8Array(buf);
|
|
217
|
+
// Encode as base64 inside the page so the bridge marshalling is
|
|
218
|
+
// string-only (avoids the structured-clone size cliff on big buffers).
|
|
219
|
+
let bin = "";
|
|
220
|
+
const chunk = 0x8000;
|
|
221
|
+
for (let i = 0; i < arr.length; i += chunk) {
|
|
222
|
+
bin += String.fromCharCode(...arr.subarray(i, Math.min(i + chunk, arr.length)));
|
|
223
|
+
}
|
|
224
|
+
return {
|
|
225
|
+
ok: true,
|
|
226
|
+
base64: btoa(bin),
|
|
227
|
+
mimeType: res.headers.get("content-type") ?? undefined,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
catch (e) {
|
|
231
|
+
return { ok: false, error: e instanceof Error ? e.message : String(e) };
|
|
232
|
+
}
|
|
233
|
+
}, entry.url);
|
|
234
|
+
if (!result.ok)
|
|
235
|
+
return { ok: false, error: `in-page fetch failed: ${result.error}` };
|
|
236
|
+
const out = {
|
|
237
|
+
ok: true,
|
|
238
|
+
bytes: Buffer.from(result.base64, "base64"),
|
|
239
|
+
};
|
|
240
|
+
const mt = result.mimeType ?? entry.mimeType;
|
|
241
|
+
if (mt !== undefined)
|
|
242
|
+
out.mimeType = mt;
|
|
243
|
+
return out;
|
|
244
|
+
}
|
|
245
|
+
catch (e) {
|
|
246
|
+
return { ok: false, error: e instanceof Error ? e.message : String(e) };
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
/** Persist one fetched body to disk, appending its manifest entry (or recording
|
|
250
|
+
* a drop / workspace-escape refusal). Returns "break" only on a maxBytes stop. */
|
|
251
|
+
function persistAssetBody(st, entry, fetched) {
|
|
252
|
+
if (st.runningBytes + fetched.bytes.length > st.maxBytes) {
|
|
253
|
+
st.warnings.push(`maxBytes (${st.maxBytes}) would be exceeded by ${entry.url} (${fetched.bytes.length} bytes); stopping export`);
|
|
254
|
+
return "break";
|
|
255
|
+
}
|
|
256
|
+
const finalName = resolveCollision(filenameFromUrl(entry.url), st.used);
|
|
257
|
+
st.used.add(finalName);
|
|
258
|
+
// Defence in depth — the sanitised name has no separators, so the join can't
|
|
259
|
+
// escape `intoDir`; verify anyway ($BROWX_WORKSPACE-rooted by construction).
|
|
260
|
+
const resolved = resolve(join(st.intoDir, finalName));
|
|
261
|
+
if (resolved !== st.intoDir && !resolved.startsWith(st.intoDir + sep)) {
|
|
262
|
+
st.droppedCount += 1;
|
|
263
|
+
st.warnings.push(`refused to write outside intoDir: ${entry.url}`);
|
|
264
|
+
return "continue";
|
|
265
|
+
}
|
|
266
|
+
try {
|
|
267
|
+
writeFileSync(resolved, fetched.bytes);
|
|
268
|
+
}
|
|
269
|
+
catch (err) {
|
|
270
|
+
st.droppedCount += 1;
|
|
271
|
+
st.warnings.push(`write failed for ${entry.url}: ${err instanceof Error ? err.message : String(err)}`);
|
|
272
|
+
return "continue";
|
|
273
|
+
}
|
|
274
|
+
let sizeBytes = fetched.bytes.length;
|
|
275
|
+
try {
|
|
276
|
+
sizeBytes = statSync(resolved).size;
|
|
277
|
+
}
|
|
278
|
+
catch {
|
|
279
|
+
/* best-effort */
|
|
280
|
+
}
|
|
281
|
+
st.runningBytes += sizeBytes;
|
|
282
|
+
const m = { url: entry.url, sizeBytes, savedAs: finalName };
|
|
283
|
+
const mt = fetched.mimeType ?? entry.mimeType;
|
|
284
|
+
if (mt !== undefined)
|
|
285
|
+
m.mime = mt;
|
|
286
|
+
if (entry.status !== undefined)
|
|
287
|
+
m.status = entry.status;
|
|
288
|
+
st.manifest.push(m);
|
|
289
|
+
return "continue";
|
|
290
|
+
}
|
|
291
|
+
/** Process one matched entry: cap checks → fetch → size checks → persist. */
|
|
292
|
+
async function processAssetEntry(deps, st, entry) {
|
|
293
|
+
st.matchedCount += 1;
|
|
294
|
+
if (st.manifest.length >= st.maxCount) {
|
|
295
|
+
st.warnings.push(`maxCount (${st.maxCount}) reached — ${st.matchedCount - st.manifest.length} additional matches were not persisted`);
|
|
296
|
+
return "break";
|
|
297
|
+
}
|
|
298
|
+
if (typeof entry.bytes === "number" && entry.bytes > SINGLE_BODY_MAX_BYTES) {
|
|
299
|
+
st.droppedCount += 1;
|
|
300
|
+
st.warnings.push(`skipped ${entry.url} — single-response size (${entry.bytes} bytes) exceeds SINGLE_BODY_MAX_BYTES (${SINGLE_BODY_MAX_BYTES})`);
|
|
301
|
+
return "continue";
|
|
302
|
+
}
|
|
303
|
+
const fetched = await fetchBodyBytes(deps.cdp, deps.page, entry);
|
|
304
|
+
if (!fetched.ok) {
|
|
305
|
+
st.droppedCount += 1;
|
|
306
|
+
st.warnings.push(`skipped ${entry.url} — ${fetched.error}`);
|
|
307
|
+
return "continue";
|
|
308
|
+
}
|
|
309
|
+
if (fetched.bytes.length > SINGLE_BODY_MAX_BYTES) {
|
|
310
|
+
st.droppedCount += 1;
|
|
311
|
+
st.warnings.push(`skipped ${entry.url} — fetched body (${fetched.bytes.length} bytes) exceeds SINGLE_BODY_MAX_BYTES (${SINGLE_BODY_MAX_BYTES})`);
|
|
312
|
+
return "continue";
|
|
313
|
+
}
|
|
314
|
+
return persistAssetBody(st, entry, fetched);
|
|
315
|
+
}
|
|
316
|
+
export async function assetExport(deps, args) {
|
|
317
|
+
const filter = args.filter ?? {};
|
|
318
|
+
const urlPattern = compileUrlPattern(filter.urlPattern);
|
|
319
|
+
const statusSet = filter.status && filter.status.length > 0 ? new Set(filter.status) : null;
|
|
320
|
+
const intoDir = resolveAssetExportDir(deps.workspaceRoot, deps.sessionId, args.intoDir);
|
|
321
|
+
// `intoDir` is workspace-rooted by construction: `resolveAssetExportDir`
|
|
322
|
+
// resolves it inside `workspaceRoot` ($BROWX_WORKSPACE) and rejects any escape.
|
|
323
|
+
// Every write below is under this dir, never cwd.
|
|
324
|
+
mkdirSync(intoDir, { recursive: true });
|
|
325
|
+
const all = deps.buffer.iter();
|
|
326
|
+
const totalCount = all.length;
|
|
327
|
+
const st = {
|
|
328
|
+
intoDir,
|
|
329
|
+
maxCount: Math.min(Math.max(1, args.maxCount ?? ASSET_EXPORT_DEFAULT_MAX_COUNT), ASSET_EXPORT_HARD_MAX_COUNT),
|
|
330
|
+
maxBytes: Math.min(Math.max(1, args.maxBytes ?? ASSET_EXPORT_DEFAULT_MAX_BYTES), ASSET_EXPORT_HARD_MAX_BYTES),
|
|
331
|
+
warnings: [],
|
|
332
|
+
manifest: [],
|
|
333
|
+
used: new Set(),
|
|
334
|
+
matchedCount: 0,
|
|
335
|
+
droppedCount: 0,
|
|
336
|
+
runningBytes: 0,
|
|
337
|
+
};
|
|
338
|
+
for (const entry of all) {
|
|
339
|
+
const matched = matchesFilter(entry, {
|
|
340
|
+
mime: filter.mime,
|
|
341
|
+
urlPattern,
|
|
342
|
+
minBytes: filter.minBytes,
|
|
343
|
+
maxBytes: filter.maxBytes,
|
|
344
|
+
status: statusSet,
|
|
345
|
+
});
|
|
346
|
+
if (!matched)
|
|
347
|
+
continue;
|
|
348
|
+
if ((await processAssetEntry(deps, st, entry)) === "break")
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
const { warnings, manifest, matchedCount, droppedCount } = st;
|
|
352
|
+
// Write `_manifest.json` last so a crash mid-export doesn't leave a
|
|
353
|
+
// misleading manifest pointing at files that weren't all written.
|
|
354
|
+
// `intoDir` is workspace-rooted ($BROWX_WORKSPACE / workspaceRoot) by
|
|
355
|
+
// construction — see `resolveAssetExportDir` above.
|
|
356
|
+
try {
|
|
357
|
+
const manifestPath = join(intoDir, "_manifest.json");
|
|
358
|
+
writeFileSync(manifestPath, JSON.stringify({ intoDir, manifest }, null, 2));
|
|
359
|
+
}
|
|
360
|
+
catch (err) {
|
|
361
|
+
log.warn("asset_export: writing _manifest.json failed", {
|
|
362
|
+
error: err instanceof Error ? err.message : String(err),
|
|
363
|
+
});
|
|
364
|
+
warnings.push(`writing _manifest.json failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
365
|
+
}
|
|
366
|
+
return {
|
|
367
|
+
ok: true,
|
|
368
|
+
intoDir,
|
|
369
|
+
totalCount,
|
|
370
|
+
matchedCount,
|
|
371
|
+
persistedCount: manifest.length,
|
|
372
|
+
droppedCount,
|
|
373
|
+
manifest,
|
|
374
|
+
warnings,
|
|
375
|
+
};
|
|
376
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface NetworkMatch {
|
|
2
|
+
/** case-insensitive substring of the request URL. */
|
|
3
|
+
urlPattern?: string;
|
|
4
|
+
/** exact HTTP method (case-insensitive). */
|
|
5
|
+
method?: string;
|
|
6
|
+
/** exact response status. */
|
|
7
|
+
status?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface ResponseLike {
|
|
10
|
+
url: string;
|
|
11
|
+
method: string;
|
|
12
|
+
status: number;
|
|
13
|
+
}
|
|
14
|
+
/** Pure predicate — does this response satisfy the match? An empty match
|
|
15
|
+
* matches nothing (refuse to "wait for anything"). Exported for tests. */
|
|
16
|
+
export declare function matchesResponse(resp: ResponseLike, match: NetworkMatch): boolean;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// act_and_wait_for_network matcher.
|
|
2
|
+
//
|
|
3
|
+
// `ActionResult.network` only sees requests inside the action window; async
|
|
4
|
+
// SPAs fire follow-up requests after it. This lets a caller drive an action
|
|
5
|
+
// and wait for a *specific* request to complete, with a precise match.
|
|
6
|
+
/** Pure predicate — does this response satisfy the match? An empty match
|
|
7
|
+
* matches nothing (refuse to "wait for anything"). Exported for tests. */
|
|
8
|
+
export function matchesResponse(resp, match) {
|
|
9
|
+
if (match.urlPattern === undefined && match.method === undefined && match.status === undefined) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
if (match.urlPattern !== undefined &&
|
|
13
|
+
!resp.url.toLowerCase().includes(match.urlPattern.toLowerCase())) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
if (match.method !== undefined && resp.method.toUpperCase() !== match.method.toUpperCase()) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
if (match.status !== undefined && resp.status !== match.status) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { CDPSession, Frame, Page } from "playwright-core";
|
|
2
|
+
export interface VisibleRect {
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
width: number;
|
|
6
|
+
height: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Compute the visible-rect bbox of a CDP-known DOM node. Returns null when the
|
|
10
|
+
* element is fully clipped (offscreen, inside a collapsed overflow container,
|
|
11
|
+
* detached). The caller surfaces null as `bbox: null, clipped: true`.
|
|
12
|
+
*/
|
|
13
|
+
export declare function visibleRect(cdp: CDPSession, backendDOMNodeId: number): Promise<VisibleRect | null>;
|
|
14
|
+
/**
|
|
15
|
+
* Playwright locator-based bounding box, used as a *fallback* when the CDP
|
|
16
|
+
* `visibleRect` path returns null. On attached/BYOB Chromes the CDP
|
|
17
|
+
* `DOM.resolveNode` → `Runtime.callFunctionOn` rect path can spuriously fail
|
|
18
|
+
* for DOM-walk-sourced nodes (no live backend node, cross-frame quirks),
|
|
19
|
+
* producing a bogus `bbox:null` → `off-screen` for an element that is in fact
|
|
20
|
+
* rendered. Playwright's own `boundingBox()` resolves the element through the
|
|
21
|
+
* locator engine and reports its real rendered box; a non-empty box means
|
|
22
|
+
* "this is on the page" regardless of what the CDP path said. Best-effort:
|
|
23
|
+
* any error / empty box → null (the caller then treats it as truly clipped).
|
|
24
|
+
*
|
|
25
|
+
* `opts.timeoutMs` caps Playwright's auto-wait so a probe call against an
|
|
26
|
+
* unmatched selector fails fast instead of pinning the default `actionTimeout`
|
|
27
|
+
* (30 s). This is the perf hot-path for `find()` candidate evaluation — find()
|
|
28
|
+
* emits locator hints derived from DOM-walk-sourced roles that don't always
|
|
29
|
+
* map to a real Playwright role selector (e.g. `role=a` when the tag is `<a>`),
|
|
30
|
+
* and the bounding-box probe on those mismatched hints would otherwise hang
|
|
31
|
+
* for the full action-timeout window per candidate. Default cap is 500 ms —
|
|
32
|
+
* the wedge class is now a known hazard on every call site; per-caller opt-in
|
|
33
|
+
* (e.g. `{ timeoutMs: 1000 }`) raises it when the caller can absorb the wait.
|
|
34
|
+
*/
|
|
35
|
+
export declare function locatorBoundingBox(root: Page | Frame, selector: string, opts?: {
|
|
36
|
+
timeoutMs?: number;
|
|
37
|
+
}): Promise<VisibleRect | null>;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// Visible-rect bbox computation — .
|
|
2
|
+
//
|
|
3
|
+
// `getBoundingClientRect()` intersected with each ancestor whose `overflow` isn't
|
|
4
|
+
// `visible`, then with the viewport. Returns null + clipped:true when the result
|
|
5
|
+
// is empty. Same definition site-docs's runtime computes, so calibration-time
|
|
6
|
+
// bbox == execution-time bbox for the same selector.
|
|
7
|
+
// Function source (runs in page context). Stringified so we can pass it to
|
|
8
|
+
// `Runtime.callFunctionOn` with the resolved DOM node as `this`.
|
|
9
|
+
//
|
|
10
|
+
// clipping is *only* triggered by `overflow: hidden` or `overflow: clip`.
|
|
11
|
+
// `overflow: auto` / `scroll` are scrollable, **not** clipping — the element's
|
|
12
|
+
// `getBoundingClientRect()` already accounts for the current scroll position,
|
|
13
|
+
// so the rect reflects the element's actual visible position. The previous
|
|
14
|
+
// check treated any non-`visible` overflow as clipping, which collapsed bboxes
|
|
15
|
+
// to zero on attached Chromes whose body/html or layout containers used
|
|
16
|
+
// `overflow: auto` (common pattern). Reported by the non-Claude verification
|
|
17
|
+
// run: visible elements returned `bbox: null` + `actionable: "off-screen"`.
|
|
18
|
+
const VISIBLE_RECT_FN = `function () {
|
|
19
|
+
function r(rect) { return { left: rect.left, top: rect.top, right: rect.right, bottom: rect.bottom }; }
|
|
20
|
+
function clips(cs) {
|
|
21
|
+
return cs.overflow === 'hidden' || cs.overflow === 'clip'
|
|
22
|
+
|| cs.overflowX === 'hidden' || cs.overflowX === 'clip'
|
|
23
|
+
|| cs.overflowY === 'hidden' || cs.overflowY === 'clip';
|
|
24
|
+
}
|
|
25
|
+
let b = r(this.getBoundingClientRect());
|
|
26
|
+
let n = this.parentElement;
|
|
27
|
+
while (n) {
|
|
28
|
+
const cs = getComputedStyle(n);
|
|
29
|
+
if (clips(cs)) {
|
|
30
|
+
const pr = r(n.getBoundingClientRect());
|
|
31
|
+
b = {
|
|
32
|
+
left: Math.max(b.left, pr.left),
|
|
33
|
+
top: Math.max(b.top, pr.top),
|
|
34
|
+
right: Math.min(b.right, pr.right),
|
|
35
|
+
bottom: Math.min(b.bottom, pr.bottom),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
n = n.parentElement;
|
|
39
|
+
}
|
|
40
|
+
// Viewport. Fall back to documentElement / document.body dims when innerWidth/
|
|
41
|
+
// innerHeight read as zero (some attached contexts report bogus window metrics
|
|
42
|
+
// before first paint).
|
|
43
|
+
var vw = window.innerWidth || (document.documentElement && document.documentElement.clientWidth) || 0;
|
|
44
|
+
var vh = window.innerHeight || (document.documentElement && document.documentElement.clientHeight) || 0;
|
|
45
|
+
if (!vw || !vh) {
|
|
46
|
+
// Last resort: skip viewport intersection — return the un-clipped client rect.
|
|
47
|
+
// Better to over-report bbox than to under-report (which currently produces
|
|
48
|
+
// bogus 'off-screen' actionability).
|
|
49
|
+
if (b.right <= b.left || b.bottom <= b.top) return null;
|
|
50
|
+
return { x: b.left, y: b.top, width: b.right - b.left, height: b.bottom - b.top };
|
|
51
|
+
}
|
|
52
|
+
b = {
|
|
53
|
+
left: Math.max(b.left, 0),
|
|
54
|
+
top: Math.max(b.top, 0),
|
|
55
|
+
right: Math.min(b.right, vw),
|
|
56
|
+
bottom: Math.min(b.bottom, vh),
|
|
57
|
+
};
|
|
58
|
+
if (b.right <= b.left || b.bottom <= b.top) return null;
|
|
59
|
+
return { x: b.left, y: b.top, width: b.right - b.left, height: b.bottom - b.top };
|
|
60
|
+
}`;
|
|
61
|
+
/**
|
|
62
|
+
* Compute the visible-rect bbox of a CDP-known DOM node. Returns null when the
|
|
63
|
+
* element is fully clipped (offscreen, inside a collapsed overflow container,
|
|
64
|
+
* detached). The caller surfaces null as `bbox: null, clipped: true`.
|
|
65
|
+
*/
|
|
66
|
+
export async function visibleRect(cdp, backendDOMNodeId) {
|
|
67
|
+
try {
|
|
68
|
+
const { object } = (await cdp.send("DOM.resolveNode", { backendNodeId: backendDOMNodeId }));
|
|
69
|
+
if (!object?.objectId)
|
|
70
|
+
return null;
|
|
71
|
+
const { result } = (await cdp.send("Runtime.callFunctionOn", {
|
|
72
|
+
objectId: object.objectId,
|
|
73
|
+
functionDeclaration: VISIBLE_RECT_FN,
|
|
74
|
+
returnByValue: true,
|
|
75
|
+
}));
|
|
76
|
+
return result.value ?? null;
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// Node detached, no live DOM, etc. Treat as clipped.
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Playwright locator-based bounding box, used as a *fallback* when the CDP
|
|
85
|
+
* `visibleRect` path returns null. On attached/BYOB Chromes the CDP
|
|
86
|
+
* `DOM.resolveNode` → `Runtime.callFunctionOn` rect path can spuriously fail
|
|
87
|
+
* for DOM-walk-sourced nodes (no live backend node, cross-frame quirks),
|
|
88
|
+
* producing a bogus `bbox:null` → `off-screen` for an element that is in fact
|
|
89
|
+
* rendered. Playwright's own `boundingBox()` resolves the element through the
|
|
90
|
+
* locator engine and reports its real rendered box; a non-empty box means
|
|
91
|
+
* "this is on the page" regardless of what the CDP path said. Best-effort:
|
|
92
|
+
* any error / empty box → null (the caller then treats it as truly clipped).
|
|
93
|
+
*
|
|
94
|
+
* `opts.timeoutMs` caps Playwright's auto-wait so a probe call against an
|
|
95
|
+
* unmatched selector fails fast instead of pinning the default `actionTimeout`
|
|
96
|
+
* (30 s). This is the perf hot-path for `find()` candidate evaluation — find()
|
|
97
|
+
* emits locator hints derived from DOM-walk-sourced roles that don't always
|
|
98
|
+
* map to a real Playwright role selector (e.g. `role=a` when the tag is `<a>`),
|
|
99
|
+
* and the bounding-box probe on those mismatched hints would otherwise hang
|
|
100
|
+
* for the full action-timeout window per candidate. Default cap is 500 ms —
|
|
101
|
+
* the wedge class is now a known hazard on every call site; per-caller opt-in
|
|
102
|
+
* (e.g. `{ timeoutMs: 1000 }`) raises it when the caller can absorb the wait.
|
|
103
|
+
*/
|
|
104
|
+
export async function locatorBoundingBox(root, selector, opts = {}) {
|
|
105
|
+
const timeoutMs = opts.timeoutMs ?? 500;
|
|
106
|
+
try {
|
|
107
|
+
const box = await root.locator(selector).first().boundingBox({ timeout: timeoutMs });
|
|
108
|
+
if (!box || box.width <= 0 || box.height <= 0)
|
|
109
|
+
return null;
|
|
110
|
+
return { x: box.x, y: box.y, width: box.width, height: box.height };
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
export type CanvasFormat = "png" | "webgl-framebuffer" | "2d-imagedata";
|
|
2
|
+
export interface CanvasCaptureArgs {
|
|
3
|
+
/** Stable ref of the target `<canvas>` element (from snapshot/find).
|
|
4
|
+
* Omit to capture the first `<canvas>` in the document. */
|
|
5
|
+
ref?: string;
|
|
6
|
+
/** Optional CSS selector used as a fallback when `ref` lookup fails
|
|
7
|
+
* (or for callers who want a raw selector path). Honoured by the
|
|
8
|
+
* page-side capture function. */
|
|
9
|
+
selector?: string;
|
|
10
|
+
/** Output format. */
|
|
11
|
+
format: CanvasFormat;
|
|
12
|
+
}
|
|
13
|
+
export interface CanvasCapturePngResult {
|
|
14
|
+
ok: true;
|
|
15
|
+
format: "png";
|
|
16
|
+
/** Base64-encoded PNG bytes. */
|
|
17
|
+
contentBase64: string;
|
|
18
|
+
byteLength: number;
|
|
19
|
+
width: number;
|
|
20
|
+
height: number;
|
|
21
|
+
}
|
|
22
|
+
export interface CanvasCaptureRgbaResult {
|
|
23
|
+
ok: true;
|
|
24
|
+
format: "2d-imagedata" | "webgl-framebuffer";
|
|
25
|
+
/** Base64-encoded RGBA byte array (row-major, top-left origin for
|
|
26
|
+
* 2d-imagedata; for webgl-framebuffer, the page-side capture flips
|
|
27
|
+
* the readPixels result into top-left order to match imagedata's
|
|
28
|
+
* convention, so downstream `canvas_diff` math is consistent). */
|
|
29
|
+
contentBase64: string;
|
|
30
|
+
width: number;
|
|
31
|
+
height: number;
|
|
32
|
+
channelCount: 4;
|
|
33
|
+
/** Only set for `webgl-framebuffer` so the caller can tell the two
|
|
34
|
+
* RGBA formats apart on result. */
|
|
35
|
+
isWebGL?: true;
|
|
36
|
+
}
|
|
37
|
+
export type CanvasCaptureResult = CanvasCapturePngResult | CanvasCaptureRgbaResult | {
|
|
38
|
+
ok: false;
|
|
39
|
+
error: string;
|
|
40
|
+
code?: string;
|
|
41
|
+
};
|
|
42
|
+
/** Max canvas dimensions accepted by `canvas_capture`. Larger canvases
|
|
43
|
+
* refuse with a structured error rather than allocating a giant byte
|
|
44
|
+
* payload. 16384 matches Chromium's `max_texture_size` for most
|
|
45
|
+
* hardware — a `<canvas>` larger than this would not paint correctly
|
|
46
|
+
* anyway. */
|
|
47
|
+
export declare const CANVAS_MAX_DIMENSION = 16384;
|
|
48
|
+
/** Page-side raw-capture return — the loose discriminated union the page
|
|
49
|
+
* function produces (every field optional so it serializes cleanly across
|
|
50
|
+
* the CDP boundary). The host side narrows it back into CanvasCaptureResult. */
|
|
51
|
+
export interface PageCaptureRaw {
|
|
52
|
+
ok: boolean;
|
|
53
|
+
format?: CanvasFormat;
|
|
54
|
+
contentBase64?: string;
|
|
55
|
+
byteLength?: number;
|
|
56
|
+
width?: number;
|
|
57
|
+
height?: number;
|
|
58
|
+
channelCount?: number;
|
|
59
|
+
isWebGL?: boolean;
|
|
60
|
+
error?: string;
|
|
61
|
+
code?: string;
|
|
62
|
+
}
|
|
63
|
+
/** Page-side capture function — REAL function literal (NOT stringified).
|
|
64
|
+
* Playwright's `page.evaluate(fn, arg)` serializes the source + invokes
|
|
65
|
+
* in-page with the arg. Mirror of the pattern used in `dom_export` /
|
|
66
|
+
* `element_export` / `overflow_detect` — a stringified arrow function
|
|
67
|
+
* evaluates to the function value uncalled, which CDP can't serialize.
|
|
68
|
+
*
|
|
69
|
+
* Returns a structured discriminated union (mirror of CanvasCaptureResult)
|
|
70
|
+
* so the host side can pass it straight back. */
|
|
71
|
+
export declare const PAGE_CAPTURE_FN: (args: {
|
|
72
|
+
ref?: string;
|
|
73
|
+
selector?: string;
|
|
74
|
+
format: CanvasFormat;
|
|
75
|
+
maxDimension: number;
|
|
76
|
+
}) => PageCaptureRaw;
|
|
77
|
+
/** Thin adapter so unit tests can stub `page.evaluate` without launching
|
|
78
|
+
* Chromium. */
|
|
79
|
+
export interface CanvasCapturePage {
|
|
80
|
+
evaluate<T, Arg>(fn: (arg: Arg) => T | Promise<T>, args?: Arg): Promise<T>;
|
|
81
|
+
}
|
|
82
|
+
export declare function canvasCapture(page: CanvasCapturePage, args: CanvasCaptureArgs): Promise<CanvasCaptureResult>;
|