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,579 @@
|
|
|
1
|
+
// Diagnostics — structured per-call recording + agent self-feedback.
|
|
2
|
+
//
|
|
3
|
+
// . Off-by-default `diagnostics` capability; once enabled, every MCP
|
|
4
|
+
// tool call is recorded as a JSONL line in
|
|
5
|
+
// $BROWX_WORKSPACE/diagnostics/<sessionId>/<server-start-ISO>.jsonl
|
|
6
|
+
// and the three diagnostics_* tools surface a read-side query / report.
|
|
7
|
+
//
|
|
8
|
+
// Recorder posture (LOAD-BEARING):
|
|
9
|
+
// 1. ZERO observable side-effect when the capability is OFF — no allocations
|
|
10
|
+
// beyond a single boolean gate check, no file IO, no recorder method
|
|
11
|
+
// calls. The dispatch wrapper in server.ts short-circuits on
|
|
12
|
+
// `recorder.enabled` before doing anything else.
|
|
13
|
+
// 2. Runs DOWNSTREAM of the URL sanitiser + secrets-masking egress
|
|
14
|
+
// chokepoint — by the time the recorder sees a tool result, every
|
|
15
|
+
// egress sink has already rewritten registered secret values back to
|
|
16
|
+
// `<NAME>` aliases. The recorder additionally walks args through the
|
|
17
|
+
// same `applyMaskDeep` helper so a secret echoed in the call ARGS (e.g.
|
|
18
|
+
// a `fill({value:<PASSWORD>})` that materialised at dispatch) never
|
|
19
|
+
// lands raw in the JSONL.
|
|
20
|
+
// 3. Workspace-rooted by construction via `resolveWorkspacePath` — a
|
|
21
|
+
// session id of `../escape` is rejected at the path-resolution chokepoint
|
|
22
|
+
// and the dispatch path falls back to a no-op (the call still runs;
|
|
23
|
+
// only the recording is skipped).
|
|
24
|
+
//
|
|
25
|
+
// Storage shape (JSONL — one record per line, append-only):
|
|
26
|
+
// { kind:"call", ts, tool, sessionId, argsRedacted, resultMeta:{ok,
|
|
27
|
+
// sizeBytes, warningsCount, failureKind}, durationMs, capabilityDenials,
|
|
28
|
+
// agentId?, evalJs?:{exprSha, exprHead, returnType, returnSizeBytes,
|
|
29
|
+
// taxonomy} }
|
|
30
|
+
// { kind:"note", ts, sessionId, insight, category, severity, ref?, agentId? }
|
|
31
|
+
//
|
|
32
|
+
// Retention: env `BROWX_DIAGNOSTICS_RETENTION_DAYS` (default 30). Expired
|
|
33
|
+
// session directories are removed at server start AND on session close.
|
|
34
|
+
import { createHash } from "node:crypto";
|
|
35
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, appendFileSync, } from "node:fs";
|
|
36
|
+
import { join, sep as pathSep } from "node:path";
|
|
37
|
+
import { resolveWorkspacePath } from "../session/storage.js";
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
// Recorder
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
/** Default retention window in days. Mirrors the standing rule that
|
|
42
|
+
* diagnostics is a development-time aid, not a long-term log archive. */
|
|
43
|
+
export const DEFAULT_RETENTION_DAYS = 30;
|
|
44
|
+
/** Workspace subdir for diagnostics JSONL. */
|
|
45
|
+
const DIAG_DIR = "diagnostics";
|
|
46
|
+
/** Field names whose payload is opportunistically rewritten to sha256 +
|
|
47
|
+
* byteLength when the string is large (above the inline threshold). Small
|
|
48
|
+
* strings — a `fill({value:"hi"})` or a typed `<NAME>` alias — pass through
|
|
49
|
+
* the standard string-truncation path so the JSONL stays human-debuggable
|
|
50
|
+
* for the common short-value case. The threshold mirrors the structural
|
|
51
|
+
* intent: redact for content blobs (caches_put body, idb_put value,
|
|
52
|
+
* eval_js expr fragments), not for typed UI strings. */
|
|
53
|
+
const BIG_BLOB_FIELDS = new Set([
|
|
54
|
+
"body",
|
|
55
|
+
"contentBase64",
|
|
56
|
+
"value",
|
|
57
|
+
"expr",
|
|
58
|
+
"expression",
|
|
59
|
+
"data",
|
|
60
|
+
"contents",
|
|
61
|
+
"html",
|
|
62
|
+
"source",
|
|
63
|
+
]);
|
|
64
|
+
/** Threshold (bytes) above which a `BIG_BLOB_FIELDS` field is rewritten to
|
|
65
|
+
* sha256 + byteLength. Below this, the standard truncation path applies. */
|
|
66
|
+
const BIG_BLOB_REDACT_BYTES = 512;
|
|
67
|
+
/** Args fields the recorder rewrites to the `<NAME>` alias before writing,
|
|
68
|
+
* using the same `applyMaskDeep` the egress sinks do — so a secret echoed in
|
|
69
|
+
* args never reaches the JSONL raw. (The masking layer at egress catches it
|
|
70
|
+
* on result side; this catches it on args side.) */
|
|
71
|
+
function maskedArgs(args, secrets) {
|
|
72
|
+
if (!secrets)
|
|
73
|
+
return args;
|
|
74
|
+
return secrets.applyMaskDeep(args);
|
|
75
|
+
}
|
|
76
|
+
/** Resolve the diagnostics root path under the workspace. Rejects any
|
|
77
|
+
* session id that escapes the diagnostics subdir (`../escape`,
|
|
78
|
+
* absolute paths). The thrown error carries a stable prefix the dispatch
|
|
79
|
+
* wrapper recognises to fall back to no-op. */
|
|
80
|
+
export function resolveDiagnosticsPath(workspaceRoot, sessionId, serverStartIso) {
|
|
81
|
+
// `resolveWorkspacePath` rejects anything escaping the workspace root;
|
|
82
|
+
// we want stricter — the path must escape NEITHER the workspace nor the
|
|
83
|
+
// diagnostics subdir. Compose by resolving the full relative path and
|
|
84
|
+
// checking the prefix.
|
|
85
|
+
const rel = join(DIAG_DIR, sessionId, `${serverStartIso}.jsonl`);
|
|
86
|
+
const resolved = resolveWorkspacePath(workspaceRoot, rel, "diagnostics");
|
|
87
|
+
const diagRoot = resolveWorkspacePath(workspaceRoot, DIAG_DIR, "diagnostics");
|
|
88
|
+
if (resolved !== diagRoot && !resolved.startsWith(diagRoot + pathSep)) {
|
|
89
|
+
throw new Error(`diagnostics: sessionId "${sessionId}" must not escape the diagnostics ` +
|
|
90
|
+
`subdir — got resolved path "${resolved}"`);
|
|
91
|
+
}
|
|
92
|
+
return resolved;
|
|
93
|
+
}
|
|
94
|
+
/** Returns the diagnostics root dir (creating it if needed). The path is
|
|
95
|
+
* workspace.root-rooted by construction — caller passes `workspace.root`
|
|
96
|
+
* from `resolveWorkspace()`. */
|
|
97
|
+
export function ensureDiagnosticsRoot(workspaceRoot) {
|
|
98
|
+
// workspace.root-rooted by construction (see comment above).
|
|
99
|
+
const root = join(workspaceRoot, DIAG_DIR);
|
|
100
|
+
if (!existsSync(root))
|
|
101
|
+
mkdirSync(root, { recursive: true });
|
|
102
|
+
return root;
|
|
103
|
+
}
|
|
104
|
+
/** Resolve retention window from env. Defaults to 30 days; negative /
|
|
105
|
+
* non-numeric falls back to the default with no error. `0` disables the
|
|
106
|
+
* sweep (everything is kept). */
|
|
107
|
+
export function resolveRetentionDays(env = process.env) {
|
|
108
|
+
const raw = env.BROWX_DIAGNOSTICS_RETENTION_DAYS?.trim();
|
|
109
|
+
if (!raw)
|
|
110
|
+
return DEFAULT_RETENTION_DAYS;
|
|
111
|
+
const parsed = Number(raw);
|
|
112
|
+
if (!Number.isFinite(parsed) || parsed < 0)
|
|
113
|
+
return DEFAULT_RETENTION_DAYS;
|
|
114
|
+
return Math.floor(parsed);
|
|
115
|
+
}
|
|
116
|
+
/** Remove session directories whose newest JSONL file is older than the
|
|
117
|
+
* retention window. `0` disables the sweep entirely. Best-effort: a
|
|
118
|
+
* permission error on one session doesn't block the rest. */
|
|
119
|
+
export function sweepRetention(workspaceRoot, retentionDays, now = Date.now()) {
|
|
120
|
+
const root = join(workspaceRoot, DIAG_DIR);
|
|
121
|
+
if (!existsSync(root) || retentionDays <= 0)
|
|
122
|
+
return { removed: [], kept: [] };
|
|
123
|
+
const cutoff = now - retentionDays * 24 * 60 * 60 * 1000;
|
|
124
|
+
const removed = [];
|
|
125
|
+
const kept = [];
|
|
126
|
+
let entries = [];
|
|
127
|
+
try {
|
|
128
|
+
entries = readdirSync(root);
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return { removed, kept };
|
|
132
|
+
}
|
|
133
|
+
for (const id of entries) {
|
|
134
|
+
const sessionDir = join(root, id);
|
|
135
|
+
let st;
|
|
136
|
+
try {
|
|
137
|
+
st = statSync(sessionDir);
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (!st.isDirectory())
|
|
143
|
+
continue;
|
|
144
|
+
// Newest mtime across the session's JSONL files. If none, treat as
|
|
145
|
+
// ancient — the directory predates any retained run.
|
|
146
|
+
let newest = 0;
|
|
147
|
+
try {
|
|
148
|
+
for (const file of readdirSync(sessionDir)) {
|
|
149
|
+
const full = join(sessionDir, file);
|
|
150
|
+
const fst = statSync(full);
|
|
151
|
+
if (fst.mtimeMs > newest)
|
|
152
|
+
newest = fst.mtimeMs;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
/* best-effort */
|
|
157
|
+
}
|
|
158
|
+
if (newest === 0 || newest < cutoff) {
|
|
159
|
+
try {
|
|
160
|
+
rmSync(sessionDir, { recursive: true, force: true });
|
|
161
|
+
removed.push(id);
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
/* best-effort */
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
kept.push(id);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return { removed, kept };
|
|
172
|
+
}
|
|
173
|
+
/** Remove a single session's diagnostics directory. Used on session close. */
|
|
174
|
+
export function removeSessionDiagnostics(workspaceRoot, sessionId) {
|
|
175
|
+
const root = join(workspaceRoot, DIAG_DIR);
|
|
176
|
+
// Reject escape attempts the same way the write path does.
|
|
177
|
+
let dir;
|
|
178
|
+
try {
|
|
179
|
+
dir = resolveWorkspacePath(workspaceRoot, join(DIAG_DIR, sessionId), "diagnostics");
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
const diagRoot = root;
|
|
185
|
+
if (dir !== diagRoot && !dir.startsWith(diagRoot + pathSep))
|
|
186
|
+
return false;
|
|
187
|
+
if (!existsSync(dir))
|
|
188
|
+
return false;
|
|
189
|
+
try {
|
|
190
|
+
rmSync(dir, { recursive: true, force: true });
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* The recorder. One instance per server. The capability gate is captured at
|
|
199
|
+
* construction; `enabled` is the public hot-path flag the dispatch wrapper
|
|
200
|
+
* checks BEFORE allocating anything (zero-overhead off-path).
|
|
201
|
+
*/
|
|
202
|
+
export class DiagnosticsRecorder {
|
|
203
|
+
/** Hot-path gate — checked at the dispatch boundary. When false, every
|
|
204
|
+
* recorder method short-circuits to a no-op AND the dispatch wrapper
|
|
205
|
+
* is expected to NOT enter the recorder code path at all. */
|
|
206
|
+
enabled;
|
|
207
|
+
serverStartIso;
|
|
208
|
+
workspaceRoot;
|
|
209
|
+
retentionDays;
|
|
210
|
+
/** Capability denials accumulator (process-wide). Mirrors the snapshot the
|
|
211
|
+
* session-metrics module tracks per-session; diagnostics tracks it across
|
|
212
|
+
* every session to surface a single "how many gate hits did this server
|
|
213
|
+
* see" rollup in `diagnostics_report`. */
|
|
214
|
+
denials = 0;
|
|
215
|
+
constructor(opts) {
|
|
216
|
+
this.enabled = opts.enabled;
|
|
217
|
+
this.workspaceRoot = opts.workspaceRoot;
|
|
218
|
+
this.serverStartIso = opts.serverStartIso ?? new Date().toISOString().replace(/[:.]/g, "-");
|
|
219
|
+
this.retentionDays = opts.retentionDays ?? DEFAULT_RETENTION_DAYS;
|
|
220
|
+
}
|
|
221
|
+
/** Write one record. No-op when the recorder is disabled or the path
|
|
222
|
+
* resolution fails (workspace escape — the call still runs, only the
|
|
223
|
+
* recording is skipped). */
|
|
224
|
+
write(record) {
|
|
225
|
+
if (!this.enabled)
|
|
226
|
+
return;
|
|
227
|
+
let path;
|
|
228
|
+
try {
|
|
229
|
+
path = resolveDiagnosticsPath(this.workspaceRoot, record.sessionId, this.serverStartIso);
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
// Workspace-escape: skip silently. The session id was already
|
|
233
|
+
// validated upstream (session ids are agent-provided strings); a
|
|
234
|
+
// pathological id here means the agent passed `../escape` to a tool
|
|
235
|
+
// that didn't validate — the call still runs, just unrecorded.
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
// `dir` is workspace.root-rooted by construction — `resolveDiagnosticsPath`
|
|
239
|
+
// (above) rejects any path that escapes `workspace.root`, so a successful
|
|
240
|
+
// resolve guarantees `<workspace>/diagnostics/<sessionId>/` lives under
|
|
241
|
+
// BROWX_WORKSPACE.
|
|
242
|
+
const dir = join(this.workspaceRoot, DIAG_DIR, record.sessionId);
|
|
243
|
+
try {
|
|
244
|
+
// ws.sub-equivalent: parent dir creation rooted at workspace.root.
|
|
245
|
+
if (!existsSync(dir))
|
|
246
|
+
mkdirSync(dir, { recursive: true });
|
|
247
|
+
// workspace.root-rooted by construction (see comment above).
|
|
248
|
+
appendFileSync(path, JSON.stringify(record) + "\n", "utf8");
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
// Best-effort — a disk-full / permission error on the diagnostics
|
|
252
|
+
// path must NEVER take down a tool call.
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
/** Increment the capability-denials counter — surfaced on
|
|
256
|
+
* `diagnostics_report` so the agent's "tried to use a gated tool"
|
|
257
|
+
* pattern is one read. */
|
|
258
|
+
noteDenial() {
|
|
259
|
+
if (!this.enabled)
|
|
260
|
+
return;
|
|
261
|
+
this.denials += 1;
|
|
262
|
+
}
|
|
263
|
+
/** Read-back: enumerate every record in the workspace's diagnostics
|
|
264
|
+
* store. Order: by session id, then by file mtime (oldest first), then
|
|
265
|
+
* by line order within a file. Used by the read-side query tools.
|
|
266
|
+
* Best-effort — unreadable files are skipped. */
|
|
267
|
+
readAll() {
|
|
268
|
+
const out = [];
|
|
269
|
+
const root = join(this.workspaceRoot, DIAG_DIR);
|
|
270
|
+
if (!existsSync(root))
|
|
271
|
+
return out;
|
|
272
|
+
let sessionDirs = [];
|
|
273
|
+
try {
|
|
274
|
+
sessionDirs = readdirSync(root).sort();
|
|
275
|
+
}
|
|
276
|
+
catch {
|
|
277
|
+
return out;
|
|
278
|
+
}
|
|
279
|
+
for (const id of sessionDirs) {
|
|
280
|
+
const sd = join(root, id);
|
|
281
|
+
let st;
|
|
282
|
+
try {
|
|
283
|
+
st = statSync(sd);
|
|
284
|
+
}
|
|
285
|
+
catch {
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
if (!st.isDirectory())
|
|
289
|
+
continue;
|
|
290
|
+
let files = [];
|
|
291
|
+
try {
|
|
292
|
+
files = readdirSync(sd);
|
|
293
|
+
}
|
|
294
|
+
catch {
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
// Order files by mtime, oldest first.
|
|
298
|
+
const withMtime = [];
|
|
299
|
+
for (const f of files) {
|
|
300
|
+
try {
|
|
301
|
+
withMtime.push({ file: f, mtime: statSync(join(sd, f)).mtimeMs });
|
|
302
|
+
}
|
|
303
|
+
catch {
|
|
304
|
+
/* skip */
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
withMtime.sort((a, b) => a.mtime - b.mtime);
|
|
308
|
+
for (const { file } of withMtime) {
|
|
309
|
+
const full = join(sd, file);
|
|
310
|
+
let raw = "";
|
|
311
|
+
try {
|
|
312
|
+
raw = readFileSync(full, "utf8");
|
|
313
|
+
}
|
|
314
|
+
catch {
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
for (const line of raw.split("\n")) {
|
|
318
|
+
if (!line)
|
|
319
|
+
continue;
|
|
320
|
+
try {
|
|
321
|
+
const obj = JSON.parse(line);
|
|
322
|
+
out.push(obj);
|
|
323
|
+
}
|
|
324
|
+
catch {
|
|
325
|
+
/* skip malformed */
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return out;
|
|
331
|
+
}
|
|
332
|
+
/** Snapshot the cumulative capability-denials counter. */
|
|
333
|
+
denialsCount() {
|
|
334
|
+
return this.denials;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
// ---------------------------------------------------------------------------
|
|
338
|
+
// Args-redaction
|
|
339
|
+
// ---------------------------------------------------------------------------
|
|
340
|
+
/** Structural redaction — keep keys + types + sizes, drop raw values for
|
|
341
|
+
* known large / sensitive fields. Bounded depth. The output is small,
|
|
342
|
+
* diff-friendly, and never includes a registered secret value (the
|
|
343
|
+
* recorder applies the secrets mask on top of this before writing). */
|
|
344
|
+
export function redactArgs(args, depth = 0) {
|
|
345
|
+
if (depth > 6)
|
|
346
|
+
return { __truncated: true };
|
|
347
|
+
if (args == null)
|
|
348
|
+
return {};
|
|
349
|
+
if (typeof args !== "object" || Array.isArray(args)) {
|
|
350
|
+
return { __scalar: typeof args, __value: args };
|
|
351
|
+
}
|
|
352
|
+
const out = {};
|
|
353
|
+
for (const [k, v] of Object.entries(args)) {
|
|
354
|
+
if (BIG_BLOB_FIELDS.has(k) && typeof v === "string") {
|
|
355
|
+
const byteLen = Buffer.byteLength(v, "utf8");
|
|
356
|
+
// Only redact when the payload is genuinely large — small strings
|
|
357
|
+
// (typed `<NAME>` aliases, single-line CSS values, short URLs) stay
|
|
358
|
+
// readable so the JSONL keeps its diff-friendly debuggability.
|
|
359
|
+
if (byteLen >= BIG_BLOB_REDACT_BYTES) {
|
|
360
|
+
out[k] = {
|
|
361
|
+
__redacted: true,
|
|
362
|
+
sha256: createHash("sha256").update(v, "utf8").digest("hex"),
|
|
363
|
+
byteLength: byteLen,
|
|
364
|
+
};
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
if (typeof v === "string") {
|
|
369
|
+
out[k] = v.length > 256 ? `${v.slice(0, 256)}…[+${v.length - 256}]` : v;
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
if (typeof v === "number" || typeof v === "boolean" || v === null) {
|
|
373
|
+
out[k] = v;
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
376
|
+
if (Array.isArray(v)) {
|
|
377
|
+
out[k] = { __array: true, length: v.length };
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
if (typeof v === "object") {
|
|
381
|
+
out[k] = redactArgs(v, depth + 1);
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
384
|
+
out[k] = { __type: typeof v };
|
|
385
|
+
}
|
|
386
|
+
return out;
|
|
387
|
+
}
|
|
388
|
+
// ---------------------------------------------------------------------------
|
|
389
|
+
// eval_js taxonomy classifier
|
|
390
|
+
// ---------------------------------------------------------------------------
|
|
391
|
+
/** Heuristic substring classifier over the first 80 chars of an `eval_js`
|
|
392
|
+
* expression. Drives the "what curated primitive is missing?" inference
|
|
393
|
+
* downstream (high-count non-custom buckets → propose a primitive). */
|
|
394
|
+
export function classifyEvalExpr(exprHead) {
|
|
395
|
+
const s = exprHead;
|
|
396
|
+
// dom-query — querySelector / getElementBy* / closest / matches
|
|
397
|
+
if (/document\.querySelector|querySelectorAll|getElementBy|\.closest\(|\.matches\(/.test(s)) {
|
|
398
|
+
return "dom-query";
|
|
399
|
+
}
|
|
400
|
+
// storage-access — localStorage / sessionStorage / indexedDB / caches / cookies
|
|
401
|
+
if (/localStorage|sessionStorage|indexedDB|\bcaches\b|document\.cookie|\bcookies\b/.test(s)) {
|
|
402
|
+
return "storage-access";
|
|
403
|
+
}
|
|
404
|
+
// computed-style + layout-box measures
|
|
405
|
+
if (/getComputedStyle|getBoundingClientRect|offsetWidth|offsetHeight|clientWidth|clientHeight|scrollWidth|scrollHeight/.test(s)) {
|
|
406
|
+
return "computed-style";
|
|
407
|
+
}
|
|
408
|
+
// callback-trigger — .click() / .focus() / .blur() / .dispatchEvent( / .submit()
|
|
409
|
+
if (/\.click\(\)|\.focus\(\)|\.blur\(\)|\.dispatchEvent\(|\.submit\(\)/.test(s)) {
|
|
410
|
+
return "callback-trigger";
|
|
411
|
+
}
|
|
412
|
+
// feature-detect — typeof window./navigator. / 'X' in window/navigator
|
|
413
|
+
if (/typeof\s+window\.|typeof\s+navigator\.|['"][^'"]+['"]\s+in\s+(window|navigator)|window\.[A-Za-z_]+\s*!==\s*undefined/.test(s)) {
|
|
414
|
+
return "feature-detect";
|
|
415
|
+
}
|
|
416
|
+
return "custom";
|
|
417
|
+
}
|
|
418
|
+
// ---------------------------------------------------------------------------
|
|
419
|
+
// Result classification — drives the resultMeta + failureKind fields.
|
|
420
|
+
// ---------------------------------------------------------------------------
|
|
421
|
+
export function failureKindOf(parsed) {
|
|
422
|
+
if (Object.prototype.hasOwnProperty.call(parsed, "requiredCapability"))
|
|
423
|
+
return "capability-denied";
|
|
424
|
+
const err = typeof parsed.error === "string" ? parsed.error : "";
|
|
425
|
+
if (/anti-wedge timeout/i.test(err))
|
|
426
|
+
return "timeout";
|
|
427
|
+
if (/not found|no element matches|ref not found|locator did not resolve/i.test(err))
|
|
428
|
+
return "target-not-found";
|
|
429
|
+
if (/must |invalid |unknown |expected /i.test(err))
|
|
430
|
+
return "bad-arg";
|
|
431
|
+
return "internal";
|
|
432
|
+
}
|
|
433
|
+
/** Build the `resultMeta` field for a recorded tool call. `firstJsonObj` is
|
|
434
|
+
* the parsed first text item (when applicable); `sizeBytes` is the
|
|
435
|
+
* JSON-string length of the entire content envelope; `warningsCount` is
|
|
436
|
+
* pulled from the parsed object's `warnings` field when it's an array. */
|
|
437
|
+
export function buildResultMeta(firstJsonObj, sizeBytes) {
|
|
438
|
+
if (!firstJsonObj)
|
|
439
|
+
return { ok: true, sizeBytes, warningsCount: 0 };
|
|
440
|
+
const ok = firstJsonObj.ok !== false;
|
|
441
|
+
const warningsCount = Array.isArray(firstJsonObj.warnings) ? firstJsonObj.warnings.length : 0;
|
|
442
|
+
if (ok)
|
|
443
|
+
return { ok: true, sizeBytes, warningsCount };
|
|
444
|
+
return {
|
|
445
|
+
ok: false,
|
|
446
|
+
sizeBytes,
|
|
447
|
+
warningsCount,
|
|
448
|
+
failureKind: failureKindOf(firstJsonObj),
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
// ---------------------------------------------------------------------------
|
|
452
|
+
// eval_js deep-capture helpers
|
|
453
|
+
// ---------------------------------------------------------------------------
|
|
454
|
+
const EVAL_HEAD_LEN = 80;
|
|
455
|
+
/** Build the eval_js-specific deep-capture envelope from a tool call's
|
|
456
|
+
* args + result. Returns undefined when the tool isn't eval_js. */
|
|
457
|
+
export function buildEvalJsCapture(toolName, args, firstJsonObj) {
|
|
458
|
+
if (toolName !== "eval_js" && toolName !== "poll_eval")
|
|
459
|
+
return undefined;
|
|
460
|
+
const expr = args && typeof args === "object"
|
|
461
|
+
? (args.expr ?? args.expression)
|
|
462
|
+
: undefined;
|
|
463
|
+
if (typeof expr !== "string")
|
|
464
|
+
return undefined;
|
|
465
|
+
const exprHead = expr.slice(0, EVAL_HEAD_LEN);
|
|
466
|
+
const exprSha = createHash("sha256").update(expr, "utf8").digest("hex");
|
|
467
|
+
const taxonomy = classifyEvalExpr(exprHead);
|
|
468
|
+
let returnType = "unknown";
|
|
469
|
+
let returnSizeBytes = 0;
|
|
470
|
+
if (firstJsonObj) {
|
|
471
|
+
const v = firstJsonObj.value;
|
|
472
|
+
returnType = v === null ? "null" : Array.isArray(v) ? "array" : typeof v;
|
|
473
|
+
try {
|
|
474
|
+
returnSizeBytes = Buffer.byteLength(JSON.stringify(v ?? null), "utf8");
|
|
475
|
+
}
|
|
476
|
+
catch {
|
|
477
|
+
returnSizeBytes = 0;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
return { exprSha, exprHead, returnType, returnSizeBytes, taxonomy };
|
|
481
|
+
}
|
|
482
|
+
// ---------------------------------------------------------------------------
|
|
483
|
+
// Report
|
|
484
|
+
// ---------------------------------------------------------------------------
|
|
485
|
+
/** Compute the percentile (p50, p95) over an unsorted number array. */
|
|
486
|
+
function percentile(values, pct) {
|
|
487
|
+
if (values.length === 0)
|
|
488
|
+
return 0;
|
|
489
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
490
|
+
const idx = Math.min(sorted.length - 1, Math.max(0, Math.floor((pct / 100) * (sorted.length - 1))));
|
|
491
|
+
return sorted[idx] ?? 0;
|
|
492
|
+
}
|
|
493
|
+
export function buildReportSummary(records, opts = {}) {
|
|
494
|
+
const sinceMs = opts.since ? Date.parse(opts.since) : undefined;
|
|
495
|
+
const perTool = new Map();
|
|
496
|
+
const evalByPattern = new Map();
|
|
497
|
+
const capabilityDenials = {};
|
|
498
|
+
const notesByCategory = {};
|
|
499
|
+
const evalTaxonomyCounts = new Map();
|
|
500
|
+
for (const r of records) {
|
|
501
|
+
if (sinceMs !== undefined && Date.parse(r.ts) < sinceMs)
|
|
502
|
+
continue;
|
|
503
|
+
if (opts.session && r.sessionId !== opts.session)
|
|
504
|
+
continue;
|
|
505
|
+
if (r.kind === "note") {
|
|
506
|
+
notesByCategory[r.category] = (notesByCategory[r.category] ?? 0) + 1;
|
|
507
|
+
continue;
|
|
508
|
+
}
|
|
509
|
+
// call record
|
|
510
|
+
const row = perTool.get(r.tool) ?? { count: 0, failureCount: 0, durations: [] };
|
|
511
|
+
row.count += 1;
|
|
512
|
+
if (!r.resultMeta.ok)
|
|
513
|
+
row.failureCount += 1;
|
|
514
|
+
row.durations.push(r.durationMs);
|
|
515
|
+
perTool.set(r.tool, row);
|
|
516
|
+
if (r.resultMeta.failureKind === "capability-denied") {
|
|
517
|
+
// Pull the capability name from the tool's static map if available.
|
|
518
|
+
// We don't import the map here (avoid a cycle); the report tool injects
|
|
519
|
+
// a hint via the dispatcher. For now bucket by tool name — the report
|
|
520
|
+
// tool overlay rewrites this to capability where possible.
|
|
521
|
+
capabilityDenials[r.tool] = (capabilityDenials[r.tool] ?? 0) + 1;
|
|
522
|
+
}
|
|
523
|
+
if (r.evalJs) {
|
|
524
|
+
const e = evalByPattern.get(r.evalJs.exprSha) ?? {
|
|
525
|
+
count: 0,
|
|
526
|
+
exprHead: r.evalJs.exprHead,
|
|
527
|
+
taxonomy: r.evalJs.taxonomy,
|
|
528
|
+
};
|
|
529
|
+
e.count += 1;
|
|
530
|
+
evalByPattern.set(r.evalJs.exprSha, e);
|
|
531
|
+
const t = evalTaxonomyCounts.get(r.evalJs.taxonomy) ?? {
|
|
532
|
+
count: 0,
|
|
533
|
+
sampleHead: r.evalJs.exprHead,
|
|
534
|
+
};
|
|
535
|
+
t.count += 1;
|
|
536
|
+
evalTaxonomyCounts.set(r.evalJs.taxonomy, t);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
const perToolOut = {};
|
|
540
|
+
for (const [tool, row] of perTool) {
|
|
541
|
+
perToolOut[tool] = {
|
|
542
|
+
count: row.count,
|
|
543
|
+
failureCount: row.failureCount,
|
|
544
|
+
p50Duration: percentile(row.durations, 50),
|
|
545
|
+
p95Duration: percentile(row.durations, 95),
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
const topEvalJsPatterns = [];
|
|
549
|
+
for (const [exprSha, info] of evalByPattern) {
|
|
550
|
+
topEvalJsPatterns.push({
|
|
551
|
+
exprSha,
|
|
552
|
+
exprHead: info.exprHead,
|
|
553
|
+
count: info.count,
|
|
554
|
+
taxonomy: info.taxonomy,
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
topEvalJsPatterns.sort((a, b) => b.count - a.count);
|
|
558
|
+
topEvalJsPatterns.splice(10); // top 10
|
|
559
|
+
const missingPrimitiveHypotheses = [];
|
|
560
|
+
for (const [taxonomy, info] of evalTaxonomyCounts) {
|
|
561
|
+
// Heuristic: non-custom with count >= 3 OR custom with count >= 5.
|
|
562
|
+
const threshold = taxonomy === "custom" ? 5 : 3;
|
|
563
|
+
if (info.count >= threshold) {
|
|
564
|
+
missingPrimitiveHypotheses.push({ taxonomy, sampleHead: info.sampleHead, count: info.count });
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
missingPrimitiveHypotheses.sort((a, b) => b.count - a.count);
|
|
568
|
+
return {
|
|
569
|
+
perTool: perToolOut,
|
|
570
|
+
topEvalJsPatterns,
|
|
571
|
+
capabilityDenials,
|
|
572
|
+
notesByCategory,
|
|
573
|
+
missingPrimitiveHypotheses,
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
// ---------------------------------------------------------------------------
|
|
577
|
+
// Mask helper re-exported for the dispatch wrapper
|
|
578
|
+
// ---------------------------------------------------------------------------
|
|
579
|
+
export { maskedArgs };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { SecretRegistry } from "./secrets.js";
|
|
2
|
+
/** The single egress masking surface. Every client-facing output path masks
|
|
3
|
+
* through one of these. Constructed with the session's `SecretRegistry` when the
|
|
4
|
+
* `secrets` capability is on, or `null` when it is off — so the capability gate
|
|
5
|
+
* is an injection-time decision, not a per-sink inline check. URL-sanitisation
|
|
6
|
+
* applies regardless of the secrets registry (it is structural, not value-based). */
|
|
7
|
+
export declare class EgressSanitiser {
|
|
8
|
+
private readonly secrets;
|
|
9
|
+
constructor(secrets: SecretRegistry | null);
|
|
10
|
+
/** Whether a real secrets registry is attached (the `secrets` capability is on
|
|
11
|
+
* AND at least one secret is registered). Lets a sink skip an expensive
|
|
12
|
+
* side-channel sweep (e.g. the screenshot page-text probe) when there is
|
|
13
|
+
* nothing to find. */
|
|
14
|
+
get active(): boolean;
|
|
15
|
+
/** URL-sanitise then secrets-mask a single string, in the audited order. The
|
|
16
|
+
* composition `composeUrlAndSecretsInText` used to make every caller remember. */
|
|
17
|
+
maskText(text: string): string;
|
|
18
|
+
/** Deep-mask the string leaves of a structured payload (secrets only — the
|
|
19
|
+
* structured masking the verify / JSON families used `applyMaskDeep` for).
|
|
20
|
+
* No-op when the registry is null/empty. */
|
|
21
|
+
maskDeep<T>(value: T): T;
|
|
22
|
+
/** Best-effort detection: does `text` contain any registered real-value? The
|
|
23
|
+
* screenshot text-content sweep uses this to decide whether to warn. Returns
|
|
24
|
+
* no hit when no registry is attached. */
|
|
25
|
+
containsAnySecret(text: string): {
|
|
26
|
+
hit: boolean;
|
|
27
|
+
names: string[];
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// RFC 0004 P3 / D4 (DRY) — the egress-masking chokepoint.
|
|
2
|
+
//
|
|
3
|
+
// Secrets-masking + URL-sanitisation was a *discipline*, not a *guarantee*: every
|
|
4
|
+
// output sink hand-called `composeUrlAndSecretsInText` / `applyMaskDeep` /
|
|
5
|
+
// `containsAnySecret`, each first deciding `caps.enabled.has("secrets")` inline,
|
|
6
|
+
// and a sink that forgot the call leaked. `EgressSanitiser` is the one object
|
|
7
|
+
// every sink masks through. The capability decision is made ONCE, at
|
|
8
|
+
// construction (a sink injected with a secrets-off sanitiser holds a null
|
|
9
|
+
// registry — URL-sanitisation still applies), so a sink no longer inlines the
|
|
10
|
+
// gate; it just calls `maskText` / `maskDeep`.
|
|
11
|
+
//
|
|
12
|
+
// This wraps the existing primitives verbatim (`SecretRegistry.applyMaskInText` /
|
|
13
|
+
// `applyMaskDeep` / `containsAnySecret` and `sanitizeUrlsInText`), in the audited
|
|
14
|
+
// order (URL pass first, then secrets) — so the masking behaviour is byte-identical
|
|
15
|
+
// to the prior hand-calls; only the *who-decides-and-when* moves.
|
|
16
|
+
import { sanitizeUrlsInText } from "./url-sanitizer.js";
|
|
17
|
+
/** The single egress masking surface. Every client-facing output path masks
|
|
18
|
+
* through one of these. Constructed with the session's `SecretRegistry` when the
|
|
19
|
+
* `secrets` capability is on, or `null` when it is off — so the capability gate
|
|
20
|
+
* is an injection-time decision, not a per-sink inline check. URL-sanitisation
|
|
21
|
+
* applies regardless of the secrets registry (it is structural, not value-based). */
|
|
22
|
+
export class EgressSanitiser {
|
|
23
|
+
secrets;
|
|
24
|
+
constructor(secrets) {
|
|
25
|
+
this.secrets = secrets;
|
|
26
|
+
}
|
|
27
|
+
/** Whether a real secrets registry is attached (the `secrets` capability is on
|
|
28
|
+
* AND at least one secret is registered). Lets a sink skip an expensive
|
|
29
|
+
* side-channel sweep (e.g. the screenshot page-text probe) when there is
|
|
30
|
+
* nothing to find. */
|
|
31
|
+
get active() {
|
|
32
|
+
return this.secrets !== null && this.secrets.size() > 0;
|
|
33
|
+
}
|
|
34
|
+
/** URL-sanitise then secrets-mask a single string, in the audited order. The
|
|
35
|
+
* composition `composeUrlAndSecretsInText` used to make every caller remember. */
|
|
36
|
+
maskText(text) {
|
|
37
|
+
const afterUrl = sanitizeUrlsInText(text);
|
|
38
|
+
return this.secrets ? this.secrets.applyMaskInText(afterUrl) : afterUrl;
|
|
39
|
+
}
|
|
40
|
+
/** Deep-mask the string leaves of a structured payload (secrets only — the
|
|
41
|
+
* structured masking the verify / JSON families used `applyMaskDeep` for).
|
|
42
|
+
* No-op when the registry is null/empty. */
|
|
43
|
+
maskDeep(value) {
|
|
44
|
+
return this.secrets ? this.secrets.applyMaskDeep(value) : value;
|
|
45
|
+
}
|
|
46
|
+
/** Best-effort detection: does `text` contain any registered real-value? The
|
|
47
|
+
* screenshot text-content sweep uses this to decide whether to warn. Returns
|
|
48
|
+
* no hit when no registry is attached. */
|
|
49
|
+
containsAnySecret(text) {
|
|
50
|
+
return this.secrets ? this.secrets.containsAnySecret(text) : { hit: false, names: [] };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type FailureSource = "app" | "browxai" | "unknown";
|
|
2
|
+
export interface FailureClass {
|
|
3
|
+
source: FailureSource;
|
|
4
|
+
/** one-line, agent-facing: what this almost certainly is + what to do. */
|
|
5
|
+
hint: string;
|
|
6
|
+
}
|
|
7
|
+
/** Pure; exported for unit tests. */
|
|
8
|
+
export declare function classifyFailure(message: string): FailureClass;
|