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,244 @@
|
|
|
1
|
+
// Per-session download-capture pipeline. The reverse of `upload_file`:
|
|
2
|
+
// intercept Playwright `download` events, persist the artifact to a
|
|
3
|
+
// workspace-rooted slot, and surface it on `ActionResult.downloads[]` plus
|
|
4
|
+
// `download_get` for byte-level retrieval.
|
|
5
|
+
//
|
|
6
|
+
// Design notes:
|
|
7
|
+
// - **Off by default.** A session's listener is always attached at creation,
|
|
8
|
+
// but it only persists files when `DownloadsRegistry.captureOn` is true
|
|
9
|
+
// (toggled by the `downloads_capture` MCP tool). When off the listener
|
|
10
|
+
// deletes Playwright's temp artifact and records nothing — keeps the
|
|
11
|
+
// no-trace posture for sessions that never opted in.
|
|
12
|
+
// - **Workspace-rooted paths only.** Captured files land in
|
|
13
|
+
// `$BROWX_WORKSPACE/.downloads/<sessionId>/<id>-<sanitised-name>`. The
|
|
14
|
+
// suggested filename from the page is *sanitised* (no path separators, no
|
|
15
|
+
// traversal, no NULs, no control bytes, length-capped) before composing the
|
|
16
|
+
// on-disk name. Same posture as `upload.ts`'s workspace-escape rejection.
|
|
17
|
+
// - **Per-session.** Each `SessionEntry` owns a registry; entries don't
|
|
18
|
+
// cross sessions. Ids are session-local and monotonic.
|
|
19
|
+
// - Gated by the existing `file-io` capability (no new capability), same as
|
|
20
|
+
// `upload_file`.
|
|
21
|
+
import { mkdirSync, readFileSync, statSync, unlinkSync } from "node:fs";
|
|
22
|
+
import { join, resolve, sep } from "node:path";
|
|
23
|
+
import { log } from "../util/logging.js";
|
|
24
|
+
/** Per-session download registry. One instance per SessionEntry. */
|
|
25
|
+
export class DownloadsRegistry {
|
|
26
|
+
storageDir;
|
|
27
|
+
/** Toggled by the `downloads_capture` MCP tool. */
|
|
28
|
+
captureOn = false;
|
|
29
|
+
/** Active captures, keyed by id. Bounded to MAX_ENTRIES — oldest evicted. */
|
|
30
|
+
entries = new Map();
|
|
31
|
+
nextId = 1;
|
|
32
|
+
/** Captures that have fired and are not yet sliced into an ActionResult. */
|
|
33
|
+
pendingSince = [];
|
|
34
|
+
/** Max captures kept in memory + on disk per session before LRU-evicting
|
|
35
|
+
* the oldest. Prevents an unbounded download loop from filling the disk. */
|
|
36
|
+
static MAX_ENTRIES = 100;
|
|
37
|
+
constructor(
|
|
38
|
+
/** Per-session storage dir: `$BROWX_WORKSPACE/.downloads/<sessionId>/`. */
|
|
39
|
+
storageDir) {
|
|
40
|
+
this.storageDir = storageDir;
|
|
41
|
+
}
|
|
42
|
+
/** List all captured downloads for this session (most-recent first). */
|
|
43
|
+
list() {
|
|
44
|
+
return [...this.entries.values()].reverse();
|
|
45
|
+
}
|
|
46
|
+
/** Look up a capture by id; undefined if not present. */
|
|
47
|
+
get(id) {
|
|
48
|
+
return this.entries.get(id);
|
|
49
|
+
}
|
|
50
|
+
/** Slice captures that fired after `tsMs` (action-window slice). Returns a
|
|
51
|
+
* snapshot of the captures so the action-window can include them on the
|
|
52
|
+
* ActionResult without exposing the live registry. */
|
|
53
|
+
since(tsMs) {
|
|
54
|
+
return [...this.entries.values()].filter((d) => d.capturedAt >= tsMs);
|
|
55
|
+
}
|
|
56
|
+
/** Record a capture. Caller has already persisted the file at `path`. */
|
|
57
|
+
record(record) {
|
|
58
|
+
const id = `d${this.nextId++}`;
|
|
59
|
+
const entry = { id, ...record };
|
|
60
|
+
this.entries.set(id, entry);
|
|
61
|
+
this.pendingSince.push(entry);
|
|
62
|
+
// LRU evict oldest if over the cap.
|
|
63
|
+
while (this.entries.size > DownloadsRegistry.MAX_ENTRIES) {
|
|
64
|
+
const oldestKey = this.entries.keys().next().value;
|
|
65
|
+
if (oldestKey === undefined)
|
|
66
|
+
break;
|
|
67
|
+
const victim = this.entries.get(oldestKey);
|
|
68
|
+
this.entries.delete(oldestKey);
|
|
69
|
+
if (victim) {
|
|
70
|
+
// unlink under the workspace-rooted storageDir (BROWX_WORKSPACE).
|
|
71
|
+
try {
|
|
72
|
+
unlinkSync(victim.path);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
/* best-effort cleanup */
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return entry;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/** Sanitise a page-supplied filename for safe on-disk use. Rules (mirrors the
|
|
83
|
+
* workspace-escape posture in `upload.ts`):
|
|
84
|
+
* - strip path separators (`/`, `\`) and NUL/control bytes — collapses any
|
|
85
|
+
* traversal attempt to a flat filename.
|
|
86
|
+
* - reject leading dots so we never write to a hidden `.foo` file.
|
|
87
|
+
* - cap at 200 chars (leaves room for the `<id>-` prefix on filesystems
|
|
88
|
+
* with 255-byte name limits).
|
|
89
|
+
* - empty / all-stripped → fall back to `"download"`.
|
|
90
|
+
* Exported for unit tests. */
|
|
91
|
+
export function sanitiseFilename(raw) {
|
|
92
|
+
if (typeof raw !== "string")
|
|
93
|
+
return "download";
|
|
94
|
+
// strip NUL + control bytes (0x00-0x1f, 0x7f) and path separators.
|
|
95
|
+
// eslint-disable-next-line no-control-regex
|
|
96
|
+
let name = raw.replace(/[\x00-\x1f\x7f/\\]/g, "_");
|
|
97
|
+
// collapse runs of dots ("../.." → ".") so the literal substring `..`
|
|
98
|
+
// never survives — eliminates "looks-like-traversal" appearance even though
|
|
99
|
+
// the lack of separators already makes traversal impossible.
|
|
100
|
+
name = name.replace(/\.{2,}/g, ".");
|
|
101
|
+
// collapse repeated underscores from the strip pass.
|
|
102
|
+
name = name.replace(/_+/g, "_");
|
|
103
|
+
// strip leading dots so we don't write a hidden file.
|
|
104
|
+
name = name.replace(/^\.+/, "");
|
|
105
|
+
// strip leading/trailing whitespace, dots, underscores — these only exist
|
|
106
|
+
// because of the strip passes above; without them the filename "/" would
|
|
107
|
+
// remain as "_" which is unhelpful + tests as if it carried information.
|
|
108
|
+
name = name.replace(/^[._\s]+|[._\s]+$/g, "");
|
|
109
|
+
// cap length.
|
|
110
|
+
if (name.length > 200)
|
|
111
|
+
name = name.slice(0, 200);
|
|
112
|
+
if (!name)
|
|
113
|
+
return "download";
|
|
114
|
+
return name;
|
|
115
|
+
}
|
|
116
|
+
/** Best-effort MIME type from a filename extension. Tiny built-in table; this
|
|
117
|
+
* is metadata only (we never reject on it), so an unknown extension just
|
|
118
|
+
* yields `undefined`. */
|
|
119
|
+
export function mimeTypeFromName(name) {
|
|
120
|
+
const m = /\.([a-zA-Z0-9]+)$/.exec(name);
|
|
121
|
+
if (!m)
|
|
122
|
+
return undefined;
|
|
123
|
+
const ext = m[1].toLowerCase();
|
|
124
|
+
const map = {
|
|
125
|
+
pdf: "application/pdf",
|
|
126
|
+
csv: "text/csv",
|
|
127
|
+
tsv: "text/tab-separated-values",
|
|
128
|
+
txt: "text/plain",
|
|
129
|
+
json: "application/json",
|
|
130
|
+
xml: "application/xml",
|
|
131
|
+
html: "text/html",
|
|
132
|
+
htm: "text/html",
|
|
133
|
+
png: "image/png",
|
|
134
|
+
jpg: "image/jpeg",
|
|
135
|
+
jpeg: "image/jpeg",
|
|
136
|
+
gif: "image/gif",
|
|
137
|
+
webp: "image/webp",
|
|
138
|
+
svg: "image/svg+xml",
|
|
139
|
+
zip: "application/zip",
|
|
140
|
+
tar: "application/x-tar",
|
|
141
|
+
gz: "application/gzip",
|
|
142
|
+
mp3: "audio/mpeg",
|
|
143
|
+
mp4: "video/mp4",
|
|
144
|
+
webm: "video/webm",
|
|
145
|
+
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
146
|
+
xls: "application/vnd.ms-excel",
|
|
147
|
+
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
148
|
+
doc: "application/msword",
|
|
149
|
+
pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
150
|
+
ppt: "application/vnd.ms-powerpoint",
|
|
151
|
+
};
|
|
152
|
+
return map[ext];
|
|
153
|
+
}
|
|
154
|
+
/** Attach the Playwright `download` listener to a context. The listener fires
|
|
155
|
+
* for every download on every page in the context (now or later). When
|
|
156
|
+
* capture is OFF the artifact is silently deleted; when ON it's persisted and
|
|
157
|
+
* the registry records it. Errors during capture never propagate — they
|
|
158
|
+
* surface as warnings on the session's log only. */
|
|
159
|
+
export function attachDownloadCapture(context, registry) {
|
|
160
|
+
context.on("download", (download) => {
|
|
161
|
+
void handleDownload(download, registry).catch((err) => {
|
|
162
|
+
log.warn("downloads: capture failed", {
|
|
163
|
+
error: err instanceof Error ? err.message : String(err),
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
async function handleDownload(download, registry) {
|
|
169
|
+
if (!registry.captureOn) {
|
|
170
|
+
// Capture disabled for this session: discard the artifact and record
|
|
171
|
+
// nothing. Playwright still buffers downloads to a temp file when
|
|
172
|
+
// `acceptDownloads: true`; cancelling drops the temp file.
|
|
173
|
+
await download.cancel().catch(() => undefined);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const raw = download.suggestedFilename() ?? "download";
|
|
177
|
+
const safe = sanitiseFilename(raw);
|
|
178
|
+
// Ensure storage dir exists (created lazily; sessions that never opt in
|
|
179
|
+
// never create it). The dir is workspace-rooted (BROWX_WORKSPACE/.downloads
|
|
180
|
+
// via the SessionEntry factory in server.ts — never cwd).
|
|
181
|
+
mkdirSync(registry.storageDir, { recursive: true });
|
|
182
|
+
// Compose final on-disk path. We can't know the id yet (registry assigns
|
|
183
|
+
// it on `record`); use a millisecond+random prefix to disambiguate
|
|
184
|
+
// simultaneous downloads, then we'll record once persisted.
|
|
185
|
+
const prefix = `${Date.now()}-${Math.floor(Math.random() * 1e6).toString(36)}`;
|
|
186
|
+
const target = join(registry.storageDir, `${prefix}-${safe}`);
|
|
187
|
+
// Reject any composed path that doesn't resolve INSIDE storageDir. Defence
|
|
188
|
+
// in depth — sanitiseFilename already strips separators, but if a future
|
|
189
|
+
// change loosens it the workspace-escape guard catches it.
|
|
190
|
+
const resolved = resolve(target);
|
|
191
|
+
const root = resolve(registry.storageDir);
|
|
192
|
+
if (resolved !== root && !resolved.startsWith(root + sep)) {
|
|
193
|
+
await download.cancel().catch(() => undefined);
|
|
194
|
+
log.warn("downloads: refusing to persist outside storage dir", { resolved, root });
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
try {
|
|
198
|
+
await download.saveAs(resolved);
|
|
199
|
+
}
|
|
200
|
+
catch (err) {
|
|
201
|
+
log.warn("downloads: saveAs failed", {
|
|
202
|
+
error: err instanceof Error ? err.message : String(err),
|
|
203
|
+
});
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
let sizeBytes = 0;
|
|
207
|
+
try {
|
|
208
|
+
sizeBytes = statSync(resolved).size;
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
/* best-effort */
|
|
212
|
+
}
|
|
213
|
+
const rawDifferedFromSafe = raw !== safe;
|
|
214
|
+
registry.record({
|
|
215
|
+
suggestedFilename: safe,
|
|
216
|
+
...(rawDifferedFromSafe ? { rawSuggestedFilename: raw } : {}),
|
|
217
|
+
mimeType: mimeTypeFromName(safe),
|
|
218
|
+
sizeBytes,
|
|
219
|
+
path: resolved,
|
|
220
|
+
capturedAt: Date.now(),
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
/** Read a captured download's bytes. Returns base64. Throws if the id is
|
|
224
|
+
* unknown or the file vanished. */
|
|
225
|
+
export function readCapturedBytes(reg, id) {
|
|
226
|
+
const entry = reg.get(id);
|
|
227
|
+
if (!entry) {
|
|
228
|
+
throw new Error(`download_get: unknown id "${id}". Call downloads_capture({on:true}) before the action that triggers the download, then read the id from ActionResult.downloads[]`);
|
|
229
|
+
}
|
|
230
|
+
let buf;
|
|
231
|
+
try {
|
|
232
|
+
buf = readFileSync(entry.path);
|
|
233
|
+
}
|
|
234
|
+
catch (err) {
|
|
235
|
+
throw new Error(`download_get: file vanished for id "${id}" at ${entry.path} (${err instanceof Error ? err.message : String(err)})`);
|
|
236
|
+
}
|
|
237
|
+
return {
|
|
238
|
+
base64: buf.toString("base64"),
|
|
239
|
+
bytes: buf.length,
|
|
240
|
+
path: entry.path,
|
|
241
|
+
mimeType: entry.mimeType,
|
|
242
|
+
suggestedFilename: entry.suggestedFilename,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { Page } from "playwright-core";
|
|
2
|
+
import type { RefRegistry } from "./refs.js";
|
|
3
|
+
import { type ActionTarget } from "./locator.js";
|
|
4
|
+
export interface DropFileInputPath {
|
|
5
|
+
/** Workspace-rooted file path. Mutually exclusive with `contents`. */
|
|
6
|
+
path: string;
|
|
7
|
+
/** Filename presented to the page. Defaults to the basename of `path`. */
|
|
8
|
+
name?: string;
|
|
9
|
+
/** MIME type. Defaults to "application/octet-stream". */
|
|
10
|
+
mimeType?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface DropFileInputContents {
|
|
13
|
+
/** base64 file content. Mutually exclusive with `path`. */
|
|
14
|
+
contents: string;
|
|
15
|
+
/** Filename presented to the page. Required in `contents`-mode. */
|
|
16
|
+
name: string;
|
|
17
|
+
/** MIME type. Defaults to "application/octet-stream". */
|
|
18
|
+
mimeType?: string;
|
|
19
|
+
}
|
|
20
|
+
export type DropFileInput = DropFileInputPath | DropFileInputContents;
|
|
21
|
+
export interface DropFilesArgs {
|
|
22
|
+
target: ActionTarget;
|
|
23
|
+
files: DropFileInput[];
|
|
24
|
+
}
|
|
25
|
+
export interface DropFilesResult {
|
|
26
|
+
ok: boolean;
|
|
27
|
+
/** Resolved target description for debugging. */
|
|
28
|
+
target: string;
|
|
29
|
+
/** One entry per file with the resolved mode + bytes that were dropped. */
|
|
30
|
+
files: Array<{
|
|
31
|
+
name: string;
|
|
32
|
+
mode: "path" | "contents";
|
|
33
|
+
bytes: number;
|
|
34
|
+
mimeType: string;
|
|
35
|
+
}>;
|
|
36
|
+
/** Total bytes dispatched. */
|
|
37
|
+
totalBytes: number;
|
|
38
|
+
/** Number of files dropped. */
|
|
39
|
+
fileCount: number;
|
|
40
|
+
/** Which DOM events the page-side script actually fired. */
|
|
41
|
+
eventsFired: string[];
|
|
42
|
+
/** True when the `drop` event was dispatched (regardless of whether the
|
|
43
|
+
* page handler called preventDefault — we report the dispatch, not the
|
|
44
|
+
* app-side acceptance, since drop-zone apps vary wildly in how they
|
|
45
|
+
* signal "I took it"). */
|
|
46
|
+
dropDispatched: boolean;
|
|
47
|
+
}
|
|
48
|
+
export interface DropPayload {
|
|
49
|
+
files: Array<{
|
|
50
|
+
base64: string;
|
|
51
|
+
name: string;
|
|
52
|
+
mimeType: string;
|
|
53
|
+
}>;
|
|
54
|
+
/** Viewport-relative coords for the dispatched events. We compute these
|
|
55
|
+
* on the Node side (bounding-box centre for ref/selector; literal for
|
|
56
|
+
* coords). The page-side script writes them onto every event so apps
|
|
57
|
+
* that read `event.clientX`/`clientY` see consistent values. */
|
|
58
|
+
clientX: number;
|
|
59
|
+
clientY: number;
|
|
60
|
+
/** When the target was a coords target we have no Locator handle, so the
|
|
61
|
+
* page-side script does `document.elementFromPoint(clientX, clientY)`
|
|
62
|
+
* to find the drop target. For ref/selector targets we pass the
|
|
63
|
+
* pre-resolved element via the Locator's evaluate-handle. */
|
|
64
|
+
byCoords: boolean;
|
|
65
|
+
}
|
|
66
|
+
export interface DropEvalResult {
|
|
67
|
+
eventsFired: string[];
|
|
68
|
+
dropDispatched: boolean;
|
|
69
|
+
/** Tag of the element we landed on — surfaced for debugging. */
|
|
70
|
+
hitTag?: string;
|
|
71
|
+
/** Any error from the in-page side. */
|
|
72
|
+
error?: string;
|
|
73
|
+
}
|
|
74
|
+
export declare const dropFilesPageScript: (args: {
|
|
75
|
+
el: unknown;
|
|
76
|
+
payload: DropPayload;
|
|
77
|
+
}) => DropEvalResult;
|
|
78
|
+
export declare function dropFiles(page: Page, refs: RefRegistry, workspaceRoot: string, args: DropFilesArgs): Promise<DropFilesResult>;
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
2
|
+
// `drop_files` — synthesize an HTML5 drag-drop of one or more files onto a
|
|
3
|
+
// page element.
|
|
4
|
+
//
|
|
5
|
+
// Modern uploaders are no longer `<input type=file>` — they're drop zones
|
|
6
|
+
// listening for `dragenter` / `dragover` / `drop` with a populated
|
|
7
|
+
// `DataTransfer.files`. Playwright's `setInputFiles` only drives the
|
|
8
|
+
// `<input>` shape, so today's only path to drive a drop-zone uploader is
|
|
9
|
+
// `eval_js` with hand-rolled DataTransfer plumbing. This tool is the
|
|
10
|
+
// first-class alternative.
|
|
11
|
+
//
|
|
12
|
+
// Two file sources, same posture as `upload_file`:
|
|
13
|
+
// - `contents` — base64 inline; no filesystem read at all.
|
|
14
|
+
// - `path` — resolved **inside `$BROWX_WORKSPACE` only**; a path
|
|
15
|
+
// escaping the workspace is rejected.
|
|
16
|
+
//
|
|
17
|
+
// In-page construction approach: we route bytes to the page through a
|
|
18
|
+
// single `page.evaluate(fn, payload)` call at drop time. The page-side
|
|
19
|
+
// function (defined inline so it closes-over nothing) builds `File`
|
|
20
|
+
// objects from base64 payloads, populates a `DataTransfer`, then
|
|
21
|
+
// dispatches `dragenter` → `dragover` → `drop` on the target element. We
|
|
22
|
+
// deliberately do NOT use `addInitScript` to install a helper: each drop
|
|
23
|
+
// is one-shot, the data shape varies per call, and a boot-time injection
|
|
24
|
+
// would leak page-side identifiers.
|
|
25
|
+
//
|
|
26
|
+
// Why base64 over the Node→page boundary and not `Uint8Array`: Playwright's
|
|
27
|
+
// `evaluate` round-trips a `Uint8Array` arg as a per-byte object array
|
|
28
|
+
// (~10× the wire footprint of base64), and we'd still need to materialise
|
|
29
|
+
// it as a Uint8Array in-page anyway — `atob` once on the page side is
|
|
30
|
+
// cheaper than the structured-clone explosion.
|
|
31
|
+
import { readFileSync } from "node:fs";
|
|
32
|
+
import { resolve, sep } from "node:path";
|
|
33
|
+
import { resolveTarget } from "./locator.js";
|
|
34
|
+
function targetSummary(t) {
|
|
35
|
+
if (t.ref)
|
|
36
|
+
return `ref ${t.ref}`;
|
|
37
|
+
if (t.selector)
|
|
38
|
+
return `selector ${t.selector}`;
|
|
39
|
+
if (t.coords)
|
|
40
|
+
return `coords ${t.coords.x},${t.coords.y}`;
|
|
41
|
+
return "(unknown)";
|
|
42
|
+
}
|
|
43
|
+
function basename(p) {
|
|
44
|
+
const i = Math.max(p.lastIndexOf("/"), p.lastIndexOf("\\"));
|
|
45
|
+
return i >= 0 ? p.slice(i + 1) : p;
|
|
46
|
+
}
|
|
47
|
+
/** Prepare a path-mode file: workspace-escape guarded, read off disk, base64'd. */
|
|
48
|
+
function preparePathFile(workspaceRoot, f, i) {
|
|
49
|
+
const fp = f.path;
|
|
50
|
+
const resolved = resolve(workspaceRoot, fp);
|
|
51
|
+
if (resolved !== workspaceRoot && !resolved.startsWith(workspaceRoot + sep)) {
|
|
52
|
+
throw new Error(`drop_files: files[${i}].path must resolve inside $BROWX_WORKSPACE — stage the file there, or use \`contents\` (base64)`);
|
|
53
|
+
}
|
|
54
|
+
let buf;
|
|
55
|
+
try {
|
|
56
|
+
buf = readFileSync(resolved);
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
throw new Error(`drop_files: files[${i}].path: ${err.message}`);
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
base64: buf.toString("base64"),
|
|
63
|
+
name: f.name ?? basename(fp),
|
|
64
|
+
mimeType: f.mimeType ?? "application/octet-stream",
|
|
65
|
+
bytes: buf.length,
|
|
66
|
+
mode: "path",
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/** Prepare a contents-mode file (base64 inline; `name` required). */
|
|
70
|
+
function prepareContentsFile(f, i) {
|
|
71
|
+
if (!f.name || f.name.length === 0) {
|
|
72
|
+
throw new Error(`drop_files: files[${i}]: \`name\` is required in contents-mode`);
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
base64: f.contents,
|
|
76
|
+
name: f.name,
|
|
77
|
+
mimeType: f.mimeType ?? "application/octet-stream",
|
|
78
|
+
bytes: Buffer.from(f.contents, "base64").length,
|
|
79
|
+
mode: "contents",
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function prepareFiles(workspaceRoot, files) {
|
|
83
|
+
if (!Array.isArray(files) || files.length === 0) {
|
|
84
|
+
throw new Error("drop_files: `files` must be a non-empty array");
|
|
85
|
+
}
|
|
86
|
+
return files.map((f, i) => {
|
|
87
|
+
const hasPath = typeof f.path === "string" && f.path.length > 0;
|
|
88
|
+
const hasContents = typeof f.contents === "string";
|
|
89
|
+
if (hasPath && hasContents) {
|
|
90
|
+
throw new Error(`drop_files: files[${i}]: pass exactly one of \`path\` or \`contents\``);
|
|
91
|
+
}
|
|
92
|
+
if (!hasPath && !hasContents) {
|
|
93
|
+
throw new Error(`drop_files: files[${i}]: requires \`path\` or \`contents\``);
|
|
94
|
+
}
|
|
95
|
+
return hasPath
|
|
96
|
+
? preparePathFile(workspaceRoot, f, i)
|
|
97
|
+
: prepareContentsFile(f, i);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
export const dropFilesPageScript = function PAGE_DROP_FILES_FN(args) {
|
|
101
|
+
const g = globalThis;
|
|
102
|
+
const W = g.window ?? g;
|
|
103
|
+
const D = g.document ?? W.document;
|
|
104
|
+
if (!W || !D)
|
|
105
|
+
return { eventsFired: [], dropDispatched: false, error: "no window/document" };
|
|
106
|
+
// 1. Resolve the target element. For ref/selector mode the caller passed
|
|
107
|
+
// `el` directly via Locator.evaluate; for coords mode `el` is null
|
|
108
|
+
// and we look it up via elementFromPoint. The narrowing is inlined (NOT a
|
|
109
|
+
// module-level helper): this function is serialized to the page by
|
|
110
|
+
// Locator.evaluate, so any sibling-scope reference is a ReferenceError in
|
|
111
|
+
// the page realm.
|
|
112
|
+
const elCandidate = args.el;
|
|
113
|
+
let target = typeof elCandidate === "object" &&
|
|
114
|
+
elCandidate !== null &&
|
|
115
|
+
typeof elCandidate.dispatchEvent === "function"
|
|
116
|
+
? elCandidate
|
|
117
|
+
: null;
|
|
118
|
+
if (args.payload.byCoords || !target) {
|
|
119
|
+
target = D.elementFromPoint(args.payload.clientX, args.payload.clientY);
|
|
120
|
+
}
|
|
121
|
+
if (!target)
|
|
122
|
+
return {
|
|
123
|
+
eventsFired: [],
|
|
124
|
+
dropDispatched: false,
|
|
125
|
+
error: "no target element at the requested point",
|
|
126
|
+
};
|
|
127
|
+
// Bind a non-null alias so the closure below sees a narrowed element type.
|
|
128
|
+
const targetEl = target;
|
|
129
|
+
// 2. Materialise File objects from base64 payloads. `atob` is universal,
|
|
130
|
+
// Uint8Array → File is the canonical drop-zone construction.
|
|
131
|
+
const fileObjs = [];
|
|
132
|
+
const rawAtob = W.atob ?? g.atob;
|
|
133
|
+
// `atob` is a universal global (browser + Node); the captured lookups above
|
|
134
|
+
// only matter for unusual page contexts. Bind to `W` when present, else fall
|
|
135
|
+
// back to the ambient global — the same function the original `g.atob` path
|
|
136
|
+
// resolved to (never a silent identity map).
|
|
137
|
+
const atobFn = rawAtob ? rawAtob.bind(W) : atob;
|
|
138
|
+
const U8 = W.Uint8Array ?? g.Uint8Array ?? Uint8Array;
|
|
139
|
+
const FileCtor = W.File ?? g.File;
|
|
140
|
+
for (let i = 0; i < args.payload.files.length; i++) {
|
|
141
|
+
const f = args.payload.files[i];
|
|
142
|
+
const bin = atobFn(f.base64);
|
|
143
|
+
const len = bin.length;
|
|
144
|
+
const u8 = new U8(len);
|
|
145
|
+
for (let j = 0; j < len; j++)
|
|
146
|
+
u8[j] = bin.charCodeAt(j) & 0xff;
|
|
147
|
+
if (FileCtor)
|
|
148
|
+
fileObjs.push(new FileCtor([u8], f.name, { type: f.mimeType }));
|
|
149
|
+
}
|
|
150
|
+
// 3. Build the DataTransfer. `new DataTransfer()` is the public API
|
|
151
|
+
// listeners read `event.dataTransfer.files` from. `items.add(file)`
|
|
152
|
+
// implicitly registers the "Files" type, which apps gate on (React-
|
|
153
|
+
// DnD's NativeTypes.FILE, e.g.).
|
|
154
|
+
const DTCtor = W.DataTransfer ?? g.DataTransfer;
|
|
155
|
+
const dt = DTCtor ? new DTCtor() : undefined;
|
|
156
|
+
if (dt) {
|
|
157
|
+
for (const file of fileObjs) {
|
|
158
|
+
if (dt.items && typeof dt.items.add === "function") {
|
|
159
|
+
dt.items.add(file);
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
// Older shims expose `files` as a settable FileList-like. Fall back
|
|
163
|
+
// by reassigning via Object.defineProperty so the value is enumerable
|
|
164
|
+
// and `length`/index access both work — best-effort for ancient
|
|
165
|
+
// browsers; modern Chromium hits the `items.add` path above.
|
|
166
|
+
try {
|
|
167
|
+
const arr = Array.from(dt.files);
|
|
168
|
+
arr.push(file);
|
|
169
|
+
const fakeList = arr;
|
|
170
|
+
fakeList.item = (idx) => arr[idx] ?? null;
|
|
171
|
+
Object.defineProperty(dt, "files", { value: fakeList, configurable: true });
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
/* swallow — items.add path covers Chromium */
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
const eventsFired = [];
|
|
180
|
+
const DragEv = W.DragEvent ?? g.DragEvent;
|
|
181
|
+
const Ev = W.Event ?? g.Event ?? Event;
|
|
182
|
+
const fireOne = (kind) => {
|
|
183
|
+
// DragEvent is preferred (carries `dataTransfer` natively); some
|
|
184
|
+
// older browsers (jsdom in tests) don't expose it as a constructor.
|
|
185
|
+
// Fall back to a regular Event with dataTransfer assigned through a
|
|
186
|
+
// property descriptor — every real Chromium hits the DragEvent path.
|
|
187
|
+
let ev;
|
|
188
|
+
try {
|
|
189
|
+
if (!DragEv)
|
|
190
|
+
throw new Error("no DragEvent");
|
|
191
|
+
ev = new DragEv(kind, {
|
|
192
|
+
bubbles: true,
|
|
193
|
+
cancelable: true,
|
|
194
|
+
composed: true,
|
|
195
|
+
clientX: args.payload.clientX,
|
|
196
|
+
clientY: args.payload.clientY,
|
|
197
|
+
dataTransfer: dt,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
const base = new Ev(kind, { bubbles: true, cancelable: true, composed: true });
|
|
202
|
+
ev = base;
|
|
203
|
+
try {
|
|
204
|
+
Object.defineProperty(base, "dataTransfer", { value: dt, configurable: true });
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
/* best-effort */
|
|
208
|
+
}
|
|
209
|
+
base.clientX = args.payload.clientX;
|
|
210
|
+
base.clientY = args.payload.clientY;
|
|
211
|
+
}
|
|
212
|
+
// Modern Chromium DragEvent may have a read-only dataTransfer that
|
|
213
|
+
// ignores the constructor option. Defensive: if it didn't take, force
|
|
214
|
+
// it via descriptor.
|
|
215
|
+
try {
|
|
216
|
+
if (ev.dataTransfer !== dt) {
|
|
217
|
+
Object.defineProperty(ev, "dataTransfer", { value: dt, configurable: true });
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
/* best-effort */
|
|
222
|
+
}
|
|
223
|
+
targetEl.dispatchEvent(ev);
|
|
224
|
+
eventsFired.push(kind);
|
|
225
|
+
};
|
|
226
|
+
// 4. Dispatch the standard HTML5 drop sequence. `dragenter` → `dragover`
|
|
227
|
+
// → `drop`. We send `dragover` once; a real human drag would emit it
|
|
228
|
+
// continuously, but most drop-zone listeners only need one to set
|
|
229
|
+
// `event.dataTransfer.dropEffect` / call `preventDefault` so the
|
|
230
|
+
// subsequent `drop` is accepted.
|
|
231
|
+
fireOne("dragenter");
|
|
232
|
+
fireOne("dragover");
|
|
233
|
+
fireOne("drop");
|
|
234
|
+
const tag = target.tagName ? String(target.tagName).toLowerCase() : "?";
|
|
235
|
+
return {
|
|
236
|
+
eventsFired,
|
|
237
|
+
dropDispatched: eventsFired.indexOf("drop") >= 0,
|
|
238
|
+
hitTag: tag,
|
|
239
|
+
};
|
|
240
|
+
};
|
|
241
|
+
/** Resolve the viewport-relative drop point: coords mode uses the literal point;
|
|
242
|
+
* element mode uses the target's bounding-box centre (throwing when unrendered). */
|
|
243
|
+
async function computeDropPoint(resolved) {
|
|
244
|
+
if (resolved.kind === "coords") {
|
|
245
|
+
return { clientX: resolved.x, clientY: resolved.y, byCoords: true };
|
|
246
|
+
}
|
|
247
|
+
const box = await resolved.loc.boundingBox().catch(() => null);
|
|
248
|
+
if (!box || box.width <= 0 || box.height <= 0) {
|
|
249
|
+
throw new Error("drop_files: target element has no rendered box — scroll into view or wait for it to mount");
|
|
250
|
+
}
|
|
251
|
+
return { clientX: box.x + box.width / 2, clientY: box.y + box.height / 2, byCoords: false };
|
|
252
|
+
}
|
|
253
|
+
export async function dropFiles(page, refs, workspaceRoot, args) {
|
|
254
|
+
if (!args || !args.target) {
|
|
255
|
+
throw new Error("drop_files: requires `target` (ref/selector/named/coords)");
|
|
256
|
+
}
|
|
257
|
+
const prepared = prepareFiles(workspaceRoot, args.files);
|
|
258
|
+
// Compute the viewport-relative click point on the Node side so apps reading
|
|
259
|
+
// `event.clientX`/`clientY` see realistic values.
|
|
260
|
+
const resolved = resolveTarget(page, refs, args.target);
|
|
261
|
+
const { clientX, clientY, byCoords } = await computeDropPoint(resolved);
|
|
262
|
+
const payload = {
|
|
263
|
+
clientX,
|
|
264
|
+
clientY,
|
|
265
|
+
byCoords,
|
|
266
|
+
files: prepared.map((p) => ({ base64: p.base64, name: p.name, mimeType: p.mimeType })),
|
|
267
|
+
};
|
|
268
|
+
// Inline the page-side script as a string so the closure-over-
|
|
269
|
+
// `dropFilesPageScript` survives the CDP boundary. Playwright's
|
|
270
|
+
// `evaluate(fn, arg)` accepts the function source as a string, parses
|
|
271
|
+
// it in the page, and invokes it with the cloned argument bag — exactly
|
|
272
|
+
// what we want, no addInitScript indirection required.
|
|
273
|
+
const scriptSource = dropFilesPageScript.toString();
|
|
274
|
+
// Dispatch. For ref/selector targets we route through the Locator so
|
|
275
|
+
// the page-side script receives the element directly as `el`. For coords
|
|
276
|
+
// we run on `page` and the script re-resolves via `elementFromPoint`.
|
|
277
|
+
const result = resolved.kind === "coords"
|
|
278
|
+
? await page.evaluate((a) => {
|
|
279
|
+
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
|
280
|
+
const factory = new Function("return (" + a.src + ");");
|
|
281
|
+
const fn = factory();
|
|
282
|
+
return fn({ el: null, payload: a.payload });
|
|
283
|
+
}, { payload, src: scriptSource })
|
|
284
|
+
: await resolved.loc.evaluate(
|
|
285
|
+
// Locator.evaluate signature: (element, arg).
|
|
286
|
+
(el, a) => {
|
|
287
|
+
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
|
288
|
+
const factory = new Function("return (" + a.src + ");");
|
|
289
|
+
const fn = factory();
|
|
290
|
+
return fn({ el, payload: a.payload });
|
|
291
|
+
}, { payload, src: scriptSource });
|
|
292
|
+
if (result.error) {
|
|
293
|
+
throw new Error(`drop_files: in-page dispatch failed — ${result.error}`);
|
|
294
|
+
}
|
|
295
|
+
const totalBytes = prepared.reduce((acc, p) => acc + p.bytes, 0);
|
|
296
|
+
return {
|
|
297
|
+
ok: true,
|
|
298
|
+
target: targetSummary(args.target),
|
|
299
|
+
files: prepared.map((p) => ({
|
|
300
|
+
name: p.name,
|
|
301
|
+
mode: p.mode,
|
|
302
|
+
bytes: p.bytes,
|
|
303
|
+
mimeType: p.mimeType,
|
|
304
|
+
})),
|
|
305
|
+
totalBytes,
|
|
306
|
+
fileCount: prepared.length,
|
|
307
|
+
eventsFired: result.eventsFired,
|
|
308
|
+
dropDispatched: result.dropDispatched,
|
|
309
|
+
};
|
|
310
|
+
}
|