opendevbrowser 0.0.15 → 0.0.17
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 +1 -1
- package/README.md +262 -43
- package/dist/annotate/direct-annotator.d.ts +22 -0
- package/dist/annotate/direct-annotator.d.ts.map +1 -0
- package/dist/annotate/output.d.ts +10 -0
- package/dist/annotate/output.d.ts.map +1 -0
- package/dist/browser/annotation-manager.d.ts +33 -0
- package/dist/browser/annotation-manager.d.ts.map +1 -0
- package/dist/browser/browser-manager.d.ts +402 -0
- package/dist/browser/browser-manager.d.ts.map +1 -0
- package/dist/browser/canvas-client.d.ts +53 -0
- package/dist/browser/canvas-client.d.ts.map +1 -0
- package/dist/browser/canvas-code-sync-manager.d.ts +79 -0
- package/dist/browser/canvas-code-sync-manager.d.ts.map +1 -0
- package/dist/browser/canvas-manager.d.ts +94 -0
- package/dist/browser/canvas-manager.d.ts.map +1 -0
- package/dist/browser/canvas-runtime-preview-bridge.d.ts +20 -0
- package/dist/browser/canvas-runtime-preview-bridge.d.ts.map +1 -0
- package/dist/browser/canvas-session-sync-manager.d.ts +21 -0
- package/dist/browser/canvas-session-sync-manager.d.ts.map +1 -0
- package/dist/browser/fingerprint/adapters.d.ts +26 -0
- package/dist/browser/fingerprint/adapters.d.ts.map +1 -0
- package/dist/browser/fingerprint/canary.d.ts +25 -0
- package/dist/browser/fingerprint/canary.d.ts.map +1 -0
- package/dist/browser/fingerprint/profiles.d.ts +16 -0
- package/dist/browser/fingerprint/profiles.d.ts.map +1 -0
- package/dist/browser/fingerprint/tier1-coherence.d.ts +36 -0
- package/dist/browser/fingerprint/tier1-coherence.d.ts.map +1 -0
- package/dist/browser/fingerprint/tier2-runtime.d.ts +40 -0
- package/dist/browser/fingerprint/tier2-runtime.d.ts.map +1 -0
- package/dist/browser/fingerprint/tier3-adaptive.d.ts +30 -0
- package/dist/browser/fingerprint/tier3-adaptive.d.ts.map +1 -0
- package/dist/browser/manager-types.d.ts +15 -0
- package/dist/browser/manager-types.d.ts.map +1 -0
- package/dist/browser/ops-browser-manager.d.ts +141 -0
- package/dist/browser/ops-browser-manager.d.ts.map +1 -0
- package/dist/browser/ops-client.d.ts +56 -0
- package/dist/browser/ops-client.d.ts.map +1 -0
- package/dist/browser/parallelism-governor.d.ts +31 -0
- package/dist/browser/parallelism-governor.d.ts.map +1 -0
- package/dist/browser/script-runner.d.ts +23 -0
- package/dist/browser/script-runner.d.ts.map +1 -0
- package/dist/browser/session-store.d.ts +63 -0
- package/dist/browser/session-store.d.ts.map +1 -0
- package/dist/browser/target-manager.d.ts +36 -0
- package/dist/browser/target-manager.d.ts.map +1 -0
- package/dist/cache/chrome-locator.d.ts +2 -0
- package/dist/cache/chrome-locator.d.ts.map +1 -0
- package/dist/cache/downloader.d.ts +6 -0
- package/dist/cache/downloader.d.ts.map +1 -0
- package/dist/cache/paths.d.ts +9 -0
- package/dist/cache/paths.d.ts.map +1 -0
- package/dist/canvas/code-sync/apply-tsx.d.ts +23 -0
- package/dist/canvas/code-sync/apply-tsx.d.ts.map +1 -0
- package/dist/canvas/code-sync/graph.d.ts +5 -0
- package/dist/canvas/code-sync/graph.d.ts.map +1 -0
- package/dist/canvas/code-sync/hash.d.ts +3 -0
- package/dist/canvas/code-sync/hash.d.ts.map +1 -0
- package/dist/canvas/code-sync/import.d.ts +18 -0
- package/dist/canvas/code-sync/import.d.ts.map +1 -0
- package/dist/canvas/code-sync/manifest.d.ts +5 -0
- package/dist/canvas/code-sync/manifest.d.ts.map +1 -0
- package/dist/canvas/code-sync/tsx-adapter.d.ts +8 -0
- package/dist/canvas/code-sync/tsx-adapter.d.ts.map +1 -0
- package/dist/canvas/code-sync/types.d.ts +152 -0
- package/dist/canvas/code-sync/types.d.ts.map +1 -0
- package/dist/canvas/code-sync/write.d.ts +9 -0
- package/dist/canvas/code-sync/write.d.ts.map +1 -0
- package/dist/canvas/document-store.d.ts +81 -0
- package/dist/canvas/document-store.d.ts.map +1 -0
- package/dist/canvas/export.d.ts +12 -0
- package/dist/canvas/export.d.ts.map +1 -0
- package/dist/canvas/repo-store.d.ts +10 -0
- package/dist/canvas/repo-store.d.ts.map +1 -0
- package/dist/canvas/surface-palette.d.ts +15 -0
- package/dist/canvas/surface-palette.d.ts.map +1 -0
- package/dist/canvas/types.d.ts +255 -0
- package/dist/canvas/types.d.ts.map +1 -0
- package/dist/canvas-runtime-preview-bridge-HBEHXM4T.js +7 -0
- package/dist/canvas-runtime-preview-bridge-HBEHXM4T.js.map +1 -0
- package/dist/chunk-5J3IFL3X.js +16706 -0
- package/dist/chunk-5J3IFL3X.js.map +1 -0
- package/dist/chunk-D633UO34.js +8149 -0
- package/dist/chunk-D633UO34.js.map +1 -0
- package/dist/chunk-FUSXMW3G.js +169 -0
- package/dist/chunk-FUSXMW3G.js.map +1 -0
- package/dist/chunk-TBUCZX4A.js +34 -0
- package/dist/chunk-TBUCZX4A.js.map +1 -0
- package/dist/chunk-V7KUDHDG.js +276 -0
- package/dist/chunk-V7KUDHDG.js.map +1 -0
- package/dist/chunk-Y2KL55OG.js +59 -0
- package/dist/chunk-Y2KL55OG.js.map +1 -0
- package/dist/cli/args.d.ts +25 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/client.d.ts +2 -0
- package/dist/cli/client.d.ts.map +1 -0
- package/dist/cli/commands/annotate.d.ts +38 -0
- package/dist/cli/commands/annotate.d.ts.map +1 -0
- package/dist/cli/commands/artifacts.d.ts +24 -0
- package/dist/cli/commands/artifacts.d.ts.map +1 -0
- package/dist/cli/commands/canvas.d.ts +45 -0
- package/dist/cli/commands/canvas.d.ts.map +1 -0
- package/dist/cli/commands/daemon.d.ts +35 -0
- package/dist/cli/commands/daemon.d.ts.map +1 -0
- package/dist/cli/commands/devtools/console-poll.d.ts +7 -0
- package/dist/cli/commands/devtools/console-poll.d.ts.map +1 -0
- package/dist/cli/commands/devtools/debug-trace-snapshot.d.ts +20 -0
- package/dist/cli/commands/devtools/debug-trace-snapshot.d.ts.map +1 -0
- package/dist/cli/commands/devtools/network-poll.d.ts +7 -0
- package/dist/cli/commands/devtools/network-poll.d.ts.map +1 -0
- package/dist/cli/commands/devtools/perf.d.ts +7 -0
- package/dist/cli/commands/devtools/perf.d.ts.map +1 -0
- package/dist/cli/commands/devtools/screenshot.d.ts +18 -0
- package/dist/cli/commands/devtools/screenshot.d.ts.map +1 -0
- package/dist/cli/commands/dom/attr.d.ts +7 -0
- package/dist/cli/commands/dom/attr.d.ts.map +1 -0
- package/dist/cli/commands/dom/checked.d.ts +7 -0
- package/dist/cli/commands/dom/checked.d.ts.map +1 -0
- package/dist/cli/commands/dom/enabled.d.ts +7 -0
- package/dist/cli/commands/dom/enabled.d.ts.map +1 -0
- package/dist/cli/commands/dom/html.d.ts +7 -0
- package/dist/cli/commands/dom/html.d.ts.map +1 -0
- package/dist/cli/commands/dom/text.d.ts +7 -0
- package/dist/cli/commands/dom/text.d.ts.map +1 -0
- package/dist/cli/commands/dom/value.d.ts +7 -0
- package/dist/cli/commands/dom/value.d.ts.map +1 -0
- package/dist/cli/commands/dom/visible.d.ts +7 -0
- package/dist/cli/commands/dom/visible.d.ts.map +1 -0
- package/dist/cli/commands/export/clone-component.d.ts +16 -0
- package/dist/cli/commands/export/clone-component.d.ts.map +1 -0
- package/dist/cli/commands/export/clone-page.d.ts +15 -0
- package/dist/cli/commands/export/clone-page.d.ts.map +1 -0
- package/dist/cli/commands/interact/check.d.ts +7 -0
- package/dist/cli/commands/interact/check.d.ts.map +1 -0
- package/dist/cli/commands/interact/click.d.ts +7 -0
- package/dist/cli/commands/interact/click.d.ts.map +1 -0
- package/dist/cli/commands/interact/hover.d.ts +7 -0
- package/dist/cli/commands/interact/hover.d.ts.map +1 -0
- package/dist/cli/commands/interact/press.d.ts +7 -0
- package/dist/cli/commands/interact/press.d.ts.map +1 -0
- package/dist/cli/commands/interact/scroll-into-view.d.ts +7 -0
- package/dist/cli/commands/interact/scroll-into-view.d.ts.map +1 -0
- package/dist/cli/commands/interact/scroll.d.ts +7 -0
- package/dist/cli/commands/interact/scroll.d.ts.map +1 -0
- package/dist/cli/commands/interact/select.d.ts +7 -0
- package/dist/cli/commands/interact/select.d.ts.map +1 -0
- package/dist/cli/commands/interact/type.d.ts +7 -0
- package/dist/cli/commands/interact/type.d.ts.map +1 -0
- package/dist/cli/commands/interact/uncheck.d.ts +7 -0
- package/dist/cli/commands/interact/uncheck.d.ts.map +1 -0
- package/dist/cli/commands/macro-resolve.d.ts +18 -0
- package/dist/cli/commands/macro-resolve.d.ts.map +1 -0
- package/dist/cli/commands/native.d.ts +93 -0
- package/dist/cli/commands/native.d.ts.map +1 -0
- package/dist/cli/commands/nav/goto.d.ts +7 -0
- package/dist/cli/commands/nav/goto.d.ts.map +1 -0
- package/dist/cli/commands/nav/snapshot.d.ts +7 -0
- package/dist/cli/commands/nav/snapshot.d.ts.map +1 -0
- package/dist/cli/commands/nav/wait.d.ts +7 -0
- package/dist/cli/commands/nav/wait.d.ts.map +1 -0
- package/dist/cli/commands/pages/close.d.ts +6 -0
- package/dist/cli/commands/pages/close.d.ts.map +1 -0
- package/dist/cli/commands/pages/list.d.ts +7 -0
- package/dist/cli/commands/pages/list.d.ts.map +1 -0
- package/dist/cli/commands/pages/open.d.ts +7 -0
- package/dist/cli/commands/pages/open.d.ts.map +1 -0
- package/dist/cli/commands/product-video.d.ts +25 -0
- package/dist/cli/commands/product-video.d.ts.map +1 -0
- package/dist/cli/commands/registry.d.ts +5 -0
- package/dist/cli/commands/registry.d.ts.map +1 -0
- package/dist/cli/commands/research.d.ts +27 -0
- package/dist/cli/commands/research.d.ts.map +1 -0
- package/dist/cli/commands/rpc.d.ts +28 -0
- package/dist/cli/commands/rpc.d.ts.map +1 -0
- package/dist/cli/commands/run.d.ts +17 -0
- package/dist/cli/commands/run.d.ts.map +1 -0
- package/dist/cli/commands/serve.d.ts +64 -0
- package/dist/cli/commands/serve.d.ts.map +1 -0
- package/dist/cli/commands/session/connect.d.ts +9 -0
- package/dist/cli/commands/session/connect.d.ts.map +1 -0
- package/dist/cli/commands/session/cookie-import.d.ts +31 -0
- package/dist/cli/commands/session/cookie-import.d.ts.map +1 -0
- package/dist/cli/commands/session/cookie-list.d.ts +17 -0
- package/dist/cli/commands/session/cookie-list.d.ts.map +1 -0
- package/dist/cli/commands/session/disconnect.d.ts +6 -0
- package/dist/cli/commands/session/disconnect.d.ts.map +1 -0
- package/dist/cli/commands/session/launch.d.ts +29 -0
- package/dist/cli/commands/session/launch.d.ts.map +1 -0
- package/dist/cli/commands/session/status.d.ts +7 -0
- package/dist/cli/commands/session/status.d.ts.map +1 -0
- package/dist/cli/commands/shopping.d.ts +25 -0
- package/dist/cli/commands/shopping.d.ts.map +1 -0
- package/dist/cli/commands/status.d.ts +24 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/targets/close.d.ts +6 -0
- package/dist/cli/commands/targets/close.d.ts.map +1 -0
- package/dist/cli/commands/targets/list.d.ts +7 -0
- package/dist/cli/commands/targets/list.d.ts.map +1 -0
- package/dist/cli/commands/targets/new.d.ts +7 -0
- package/dist/cli/commands/targets/new.d.ts.map +1 -0
- package/dist/cli/commands/targets/use.d.ts +7 -0
- package/dist/cli/commands/targets/use.d.ts.map +1 -0
- package/dist/cli/commands/types.d.ts +13 -0
- package/dist/cli/commands/types.d.ts.map +1 -0
- package/dist/cli/commands/uninstall.d.ts +14 -0
- package/dist/cli/commands/uninstall.d.ts.map +1 -0
- package/dist/cli/commands/update.d.ts +7 -0
- package/dist/cli/commands/update.d.ts.map +1 -0
- package/dist/cli/daemon-autostart.d.ts +46 -0
- package/dist/cli/daemon-autostart.d.ts.map +1 -0
- package/dist/cli/daemon-client.d.ts +33 -0
- package/dist/cli/daemon-client.d.ts.map +1 -0
- package/dist/cli/daemon-commands.d.ts +7 -0
- package/dist/cli/daemon-commands.d.ts.map +1 -0
- package/dist/cli/daemon-state.d.ts +56 -0
- package/dist/cli/daemon-state.d.ts.map +1 -0
- package/dist/cli/daemon-status.d.ts +19 -0
- package/dist/cli/daemon-status.d.ts.map +1 -0
- package/dist/cli/daemon.d.ts +29 -0
- package/dist/cli/daemon.d.ts.map +1 -0
- package/dist/cli/errors.d.ts +20 -0
- package/dist/cli/errors.d.ts.map +1 -0
- package/dist/cli/help.d.ts +33 -0
- package/dist/cli/help.d.ts.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +2825 -326
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/installers/global.d.ts +9 -0
- package/dist/cli/installers/global.d.ts.map +1 -0
- package/dist/cli/installers/local.d.ts +9 -0
- package/dist/cli/installers/local.d.ts.map +1 -0
- package/dist/cli/installers/skills.d.ts +19 -0
- package/dist/cli/installers/skills.d.ts.map +1 -0
- package/dist/cli/output.d.ts +7 -0
- package/dist/cli/output.d.ts.map +1 -0
- package/dist/cli/remote-canvas-manager.d.ts +8 -0
- package/dist/cli/remote-canvas-manager.d.ts.map +1 -0
- package/dist/cli/remote-manager.d.ts +98 -0
- package/dist/cli/remote-manager.d.ts.map +1 -0
- package/dist/cli/remote-relay.d.ts +19 -0
- package/dist/cli/remote-relay.d.ts.map +1 -0
- package/dist/cli/templates/config.d.ts +7 -0
- package/dist/cli/templates/config.d.ts.map +1 -0
- package/dist/cli/utils/config.d.ts +20 -0
- package/dist/cli/utils/config.d.ts.map +1 -0
- package/dist/cli/utils/http.d.ts +5 -0
- package/dist/cli/utils/http.d.ts.map +1 -0
- package/dist/cli/utils/parse.d.ts +9 -0
- package/dist/cli/utils/parse.d.ts.map +1 -0
- package/dist/cli/utils/skills.d.ts +12 -0
- package/dist/cli/utils/skills.d.ts.map +1 -0
- package/dist/config.d.ts +208 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/core/bootstrap.d.ts +3 -0
- package/dist/core/bootstrap.d.ts.map +1 -0
- package/dist/core/index.d.ts +3 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/logging.d.ts +34 -0
- package/dist/core/logging.d.ts.map +1 -0
- package/dist/core/types.d.ts +36 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/devtools/console-tracker.d.ts +44 -0
- package/dist/devtools/console-tracker.d.ts.map +1 -0
- package/dist/devtools/exception-tracker.d.ts +42 -0
- package/dist/devtools/exception-tracker.d.ts.map +1 -0
- package/dist/devtools/network-tracker.d.ts +34 -0
- package/dist/devtools/network-tracker.d.ts.map +1 -0
- package/dist/export/css-extract.d.ts +5 -0
- package/dist/export/css-extract.d.ts.map +1 -0
- package/dist/export/dom-capture.d.ts +15 -0
- package/dist/export/dom-capture.d.ts.map +1 -0
- package/dist/export/react-emitter.d.ts +11 -0
- package/dist/export/react-emitter.d.ts.map +1 -0
- package/dist/extension-extractor.d.ts +3 -0
- package/dist/extension-extractor.d.ts.map +1 -0
- package/dist/fs-UMRKOBNN.js +7 -0
- package/dist/fs-UMRKOBNN.js.map +1 -0
- package/dist/index.d.ts +3 -4
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1028 -123
- package/dist/index.js.map +1 -1
- package/dist/macros/execute.d.ts +44 -0
- package/dist/macros/execute.d.ts.map +1 -0
- package/dist/macros/index.d.ts +9 -0
- package/dist/macros/index.d.ts.map +1 -0
- package/dist/macros/packs/core.d.ts +3 -0
- package/dist/macros/packs/core.d.ts.map +1 -0
- package/dist/macros/registry.d.ts +48 -0
- package/dist/macros/registry.d.ts.map +1 -0
- package/dist/macros-ND2M7LWU.js +399 -0
- package/dist/macros-ND2M7LWU.js.map +1 -0
- package/dist/opendevbrowser.d.ts +3 -4
- package/dist/opendevbrowser.d.ts.map +1 -0
- package/dist/opendevbrowser.js +1028 -123
- package/dist/opendevbrowser.js.map +1 -1
- package/dist/providers/adaptive-concurrency.d.ts +42 -0
- package/dist/providers/adaptive-concurrency.d.ts.map +1 -0
- package/dist/providers/artifacts.d.ts +34 -0
- package/dist/providers/artifacts.d.ts.map +1 -0
- package/dist/providers/blocker.d.ts +47 -0
- package/dist/providers/blocker.d.ts.map +1 -0
- package/dist/providers/community/index.d.ts +44 -0
- package/dist/providers/community/index.d.ts.map +1 -0
- package/dist/providers/enrichment.d.ts +33 -0
- package/dist/providers/enrichment.d.ts.map +1 -0
- package/dist/providers/errors.d.ts +41 -0
- package/dist/providers/errors.d.ts.map +1 -0
- package/dist/providers/index.d.ts +121 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/normalize.d.ts +39 -0
- package/dist/providers/normalize.d.ts.map +1 -0
- package/dist/providers/policy.d.ts +5 -0
- package/dist/providers/policy.d.ts.map +1 -0
- package/dist/providers/registry.d.ts +22 -0
- package/dist/providers/registry.d.ts.map +1 -0
- package/dist/providers/renderer.d.ts +49 -0
- package/dist/providers/renderer.d.ts.map +1 -0
- package/dist/providers/runtime-factory.d.ts +20 -0
- package/dist/providers/runtime-factory.d.ts.map +1 -0
- package/dist/providers/safety/prompt-guard.d.ts +34 -0
- package/dist/providers/safety/prompt-guard.d.ts.map +1 -0
- package/dist/providers/shared/anti-bot-policy.d.ts +51 -0
- package/dist/providers/shared/anti-bot-policy.d.ts.map +1 -0
- package/dist/providers/shared/post-policy.d.ts +31 -0
- package/dist/providers/shared/post-policy.d.ts.map +1 -0
- package/dist/providers/shared/request-headers.d.ts +5 -0
- package/dist/providers/shared/request-headers.d.ts.map +1 -0
- package/dist/providers/shared/traversal-url.d.ts +2 -0
- package/dist/providers/shared/traversal-url.d.ts.map +1 -0
- package/dist/providers/shopping/index.d.ts +63 -0
- package/dist/providers/shopping/index.d.ts.map +1 -0
- package/dist/providers/social/bluesky.d.ts +3 -0
- package/dist/providers/social/bluesky.d.ts.map +1 -0
- package/dist/providers/social/facebook.d.ts +3 -0
- package/dist/providers/social/facebook.d.ts.map +1 -0
- package/dist/providers/social/index.d.ts +31 -0
- package/dist/providers/social/index.d.ts.map +1 -0
- package/dist/providers/social/instagram.d.ts +3 -0
- package/dist/providers/social/instagram.d.ts.map +1 -0
- package/dist/providers/social/linkedin.d.ts +3 -0
- package/dist/providers/social/linkedin.d.ts.map +1 -0
- package/dist/providers/social/platform.d.ts +40 -0
- package/dist/providers/social/platform.d.ts.map +1 -0
- package/dist/providers/social/reddit.d.ts +3 -0
- package/dist/providers/social/reddit.d.ts.map +1 -0
- package/dist/providers/social/threads.d.ts +3 -0
- package/dist/providers/social/threads.d.ts.map +1 -0
- package/dist/providers/social/tiktok.d.ts +3 -0
- package/dist/providers/social/tiktok.d.ts.map +1 -0
- package/dist/providers/social/x.d.ts +3 -0
- package/dist/providers/social/x.d.ts.map +1 -0
- package/dist/providers/social/youtube-resolver.d.ts +78 -0
- package/dist/providers/social/youtube-resolver.d.ts.map +1 -0
- package/dist/providers/social/youtube.d.ts +34 -0
- package/dist/providers/social/youtube.d.ts.map +1 -0
- package/dist/providers/tier-router.d.ts +30 -0
- package/dist/providers/tier-router.d.ts.map +1 -0
- package/dist/providers/timebox.d.ts +20 -0
- package/dist/providers/timebox.d.ts.map +1 -0
- package/dist/providers/types.d.ts +344 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/web/crawl-worker.d.ts +36 -0
- package/dist/providers/web/crawl-worker.d.ts.map +1 -0
- package/dist/providers/web/crawler.d.ts +101 -0
- package/dist/providers/web/crawler.d.ts.map +1 -0
- package/dist/providers/web/extract.d.ts +11 -0
- package/dist/providers/web/extract.d.ts.map +1 -0
- package/dist/providers/web/index.d.ts +24 -0
- package/dist/providers/web/index.d.ts.map +1 -0
- package/dist/providers/web/policy.d.ts +14 -0
- package/dist/providers/web/policy.d.ts.map +1 -0
- package/dist/providers/workflows.d.ts +67 -0
- package/dist/providers/workflows.d.ts.map +1 -0
- package/dist/providers-G3LRHQXX.js +121 -0
- package/dist/providers-G3LRHQXX.js.map +1 -0
- package/dist/relay/protocol.d.ts +399 -0
- package/dist/relay/protocol.d.ts.map +1 -0
- package/dist/relay/relay-endpoints.d.ts +16 -0
- package/dist/relay/relay-endpoints.d.ts.map +1 -0
- package/dist/relay/relay-server.d.ts +124 -0
- package/dist/relay/relay-server.d.ts.map +1 -0
- package/dist/relay/relay-types.d.ts +12 -0
- package/dist/relay/relay-types.d.ts.map +1 -0
- package/dist/runtime-factory-BICHDPE7.js +13 -0
- package/dist/runtime-factory-BICHDPE7.js.map +1 -0
- package/dist/skills/continuity-nudge.d.ts +12 -0
- package/dist/skills/continuity-nudge.d.ts.map +1 -0
- package/dist/skills/skill-loader.d.ts +20 -0
- package/dist/skills/skill-loader.d.ts.map +1 -0
- package/dist/skills/skill-nudge.d.ts +18 -0
- package/dist/skills/skill-nudge.d.ts.map +1 -0
- package/dist/skills/types.d.ts +12 -0
- package/dist/skills/types.d.ts.map +1 -0
- package/dist/snapshot/ops-snapshot.d.ts +16 -0
- package/dist/snapshot/ops-snapshot.d.ts.map +1 -0
- package/dist/snapshot/refs.d.ts +23 -0
- package/dist/snapshot/refs.d.ts.map +1 -0
- package/dist/snapshot/snapshotter.d.ts +27 -0
- package/dist/snapshot/snapshotter.d.ts.map +1 -0
- package/dist/tools/annotate.d.ts +4 -0
- package/dist/tools/annotate.d.ts.map +1 -0
- package/dist/tools/canvas.d.ts +4 -0
- package/dist/tools/canvas.d.ts.map +1 -0
- package/dist/tools/check.d.ts +4 -0
- package/dist/tools/check.d.ts.map +1 -0
- package/dist/tools/click.d.ts +4 -0
- package/dist/tools/click.d.ts.map +1 -0
- package/dist/tools/clone_component.d.ts +4 -0
- package/dist/tools/clone_component.d.ts.map +1 -0
- package/dist/tools/clone_page.d.ts +4 -0
- package/dist/tools/clone_page.d.ts.map +1 -0
- package/dist/tools/close.d.ts +4 -0
- package/dist/tools/close.d.ts.map +1 -0
- package/dist/tools/connect.d.ts +4 -0
- package/dist/tools/connect.d.ts.map +1 -0
- package/dist/tools/console_poll.d.ts +4 -0
- package/dist/tools/console_poll.d.ts.map +1 -0
- package/dist/tools/cookie_import.d.ts +25 -0
- package/dist/tools/cookie_import.d.ts.map +1 -0
- package/dist/tools/cookie_list.d.ts +9 -0
- package/dist/tools/cookie_list.d.ts.map +1 -0
- package/dist/tools/debug_trace_snapshot.d.ts +4 -0
- package/dist/tools/debug_trace_snapshot.d.ts.map +1 -0
- package/dist/tools/deps.d.ts +28 -0
- package/dist/tools/deps.d.ts.map +1 -0
- package/dist/tools/disconnect.d.ts +4 -0
- package/dist/tools/disconnect.d.ts.map +1 -0
- package/dist/tools/dom_get_html.d.ts +4 -0
- package/dist/tools/dom_get_html.d.ts.map +1 -0
- package/dist/tools/dom_get_text.d.ts +4 -0
- package/dist/tools/dom_get_text.d.ts.map +1 -0
- package/dist/tools/get_attr.d.ts +4 -0
- package/dist/tools/get_attr.d.ts.map +1 -0
- package/dist/tools/get_value.d.ts +4 -0
- package/dist/tools/get_value.d.ts.map +1 -0
- package/dist/tools/goto.d.ts +4 -0
- package/dist/tools/goto.d.ts.map +1 -0
- package/dist/tools/hover.d.ts +4 -0
- package/dist/tools/hover.d.ts.map +1 -0
- package/dist/tools/index.d.ts +4 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/is_checked.d.ts +4 -0
- package/dist/tools/is_checked.d.ts.map +1 -0
- package/dist/tools/is_enabled.d.ts +4 -0
- package/dist/tools/is_enabled.d.ts.map +1 -0
- package/dist/tools/is_visible.d.ts +4 -0
- package/dist/tools/is_visible.d.ts.map +1 -0
- package/dist/tools/launch.d.ts +4 -0
- package/dist/tools/launch.d.ts.map +1 -0
- package/dist/tools/list.d.ts +4 -0
- package/dist/tools/list.d.ts.map +1 -0
- package/dist/tools/macro_resolve.d.ts +25 -0
- package/dist/tools/macro_resolve.d.ts.map +1 -0
- package/dist/tools/network_poll.d.ts +4 -0
- package/dist/tools/network_poll.d.ts.map +1 -0
- package/dist/tools/page.d.ts +4 -0
- package/dist/tools/page.d.ts.map +1 -0
- package/dist/tools/perf.d.ts +4 -0
- package/dist/tools/perf.d.ts.map +1 -0
- package/dist/tools/press.d.ts +4 -0
- package/dist/tools/press.d.ts.map +1 -0
- package/dist/tools/product_video_run.d.ts +4 -0
- package/dist/tools/product_video_run.d.ts.map +1 -0
- package/dist/tools/prompting_guide.d.ts +4 -0
- package/dist/tools/prompting_guide.d.ts.map +1 -0
- package/dist/tools/research_run.d.ts +4 -0
- package/dist/tools/research_run.d.ts.map +1 -0
- package/dist/tools/response.d.ts +19 -0
- package/dist/tools/response.d.ts.map +1 -0
- package/dist/tools/run.d.ts +4 -0
- package/dist/tools/run.d.ts.map +1 -0
- package/dist/tools/screenshot.d.ts +4 -0
- package/dist/tools/screenshot.d.ts.map +1 -0
- package/dist/tools/scroll.d.ts +4 -0
- package/dist/tools/scroll.d.ts.map +1 -0
- package/dist/tools/scroll_into_view.d.ts +4 -0
- package/dist/tools/scroll_into_view.d.ts.map +1 -0
- package/dist/tools/select.d.ts +4 -0
- package/dist/tools/select.d.ts.map +1 -0
- package/dist/tools/shopping_run.d.ts +4 -0
- package/dist/tools/shopping_run.d.ts.map +1 -0
- package/dist/tools/skill_list.d.ts +4 -0
- package/dist/tools/skill_list.d.ts.map +1 -0
- package/dist/tools/skill_load.d.ts +4 -0
- package/dist/tools/skill_load.d.ts.map +1 -0
- package/dist/tools/snapshot.d.ts +4 -0
- package/dist/tools/snapshot.d.ts.map +1 -0
- package/dist/tools/status.d.ts +4 -0
- package/dist/tools/status.d.ts.map +1 -0
- package/dist/tools/target_close.d.ts +4 -0
- package/dist/tools/target_close.d.ts.map +1 -0
- package/dist/tools/target_new.d.ts +4 -0
- package/dist/tools/target_new.d.ts.map +1 -0
- package/dist/tools/target_use.d.ts +4 -0
- package/dist/tools/target_use.d.ts.map +1 -0
- package/dist/tools/targets_list.d.ts +4 -0
- package/dist/tools/targets_list.d.ts.map +1 -0
- package/dist/tools/type.d.ts +4 -0
- package/dist/tools/type.d.ts.map +1 -0
- package/dist/tools/uncheck.d.ts +4 -0
- package/dist/tools/uncheck.d.ts.map +1 -0
- package/dist/tools/wait.d.ts +4 -0
- package/dist/tools/wait.d.ts.map +1 -0
- package/dist/tools/workflow-runtime.d.ts +3 -0
- package/dist/tools/workflow-runtime.d.ts.map +1 -0
- package/dist/utils/crypto.d.ts +2 -0
- package/dist/utils/crypto.d.ts.map +1 -0
- package/dist/utils/endpoint-validation.d.ts +2 -0
- package/dist/utils/endpoint-validation.d.ts.map +1 -0
- package/dist/utils/fs.d.ts +5 -0
- package/dist/utils/fs.d.ts.map +1 -0
- package/dist/utils/hub-enabled.d.ts +3 -0
- package/dist/utils/hub-enabled.d.ts.map +1 -0
- package/extension/canvas.html +636 -0
- package/extension/dist/annotate-content.css +15 -6
- package/extension/dist/annotate-content.js +119 -9
- package/extension/dist/annotation-payload.js +163 -0
- package/extension/dist/background.js +158 -22
- package/extension/dist/canvas/canvas-runtime.js +1061 -0
- package/extension/dist/canvas/model.js +213 -0
- package/extension/dist/canvas/viewport-fit.js +67 -0
- package/extension/dist/canvas-page.js +1801 -0
- package/extension/dist/ops/dom-bridge.js +116 -3
- package/extension/dist/ops/ops-runtime.js +1014 -48
- package/extension/dist/ops/ops-session-store.js +37 -116
- package/extension/dist/ops/parallelism-governor.js +117 -0
- package/extension/dist/ops/snapshot-shared.js +21 -5
- package/extension/dist/ops/target-session-coordinator.js +157 -0
- package/extension/dist/popup.js +155 -31
- package/extension/dist/services/CDPRouter.js +70 -5
- package/extension/dist/services/ConnectionManager.js +22 -3
- package/extension/dist/services/RelayClient.js +9 -0
- package/extension/dist/services/TabManager.js +35 -12
- package/extension/dist/types.js +2 -0
- package/extension/icons/icon128.png +0 -0
- package/extension/icons/icon16.png +0 -0
- package/extension/icons/icon32.png +0 -0
- package/extension/icons/icon48.png +0 -0
- package/extension/manifest.json +1 -1
- package/extension/popup.html +52 -0
- package/package.json +30 -19
- package/scripts/native/host.cjs +230 -0
- package/scripts/native/install.ps1 +73 -0
- package/scripts/native/install.sh +66 -0
- package/scripts/native/uninstall.ps1 +25 -0
- package/scripts/native/uninstall.sh +26 -0
- package/skills/AGENTS.md +20 -8
- package/skills/opendevbrowser-best-practices/SKILL.md +248 -74
- package/skills/opendevbrowser-best-practices/artifacts/browser-agent-known-issues-matrix.md +44 -0
- package/skills/opendevbrowser-best-practices/artifacts/canvas-governance-playbook.md +141 -0
- package/skills/opendevbrowser-best-practices/artifacts/command-channel-reference.md +191 -0
- package/skills/opendevbrowser-best-practices/artifacts/debug-trace-playbook.md +36 -0
- package/skills/opendevbrowser-best-practices/artifacts/fingerprint-tiers.md +36 -0
- package/skills/opendevbrowser-best-practices/artifacts/macro-workflows.md +43 -0
- package/skills/opendevbrowser-best-practices/artifacts/parity-gates.md +36 -0
- package/skills/opendevbrowser-best-practices/artifacts/provider-workflows.md +89 -0
- package/skills/opendevbrowser-best-practices/assets/templates/canvas-blocker-checklist.json +70 -0
- package/skills/opendevbrowser-best-practices/assets/templates/canvas-feedback-eval.json +73 -0
- package/skills/opendevbrowser-best-practices/assets/templates/canvas-generation-plan.v1.json +67 -0
- package/skills/opendevbrowser-best-practices/assets/templates/canvas-handshake-example.json +126 -0
- package/skills/opendevbrowser-best-practices/assets/templates/cdp-forward-envelope.json +11 -0
- package/skills/opendevbrowser-best-practices/assets/templates/mode-flag-matrix.json +56 -0
- package/skills/opendevbrowser-best-practices/assets/templates/ops-request-envelope.json +9 -0
- package/skills/opendevbrowser-best-practices/assets/templates/robustness-checklist.json +136 -0
- package/skills/opendevbrowser-best-practices/assets/templates/surface-audit-checklist.json +28 -0
- package/skills/opendevbrowser-best-practices/scripts/odb-workflow.sh +170 -0
- package/skills/opendevbrowser-best-practices/scripts/run-robustness-audit.sh +164 -0
- package/skills/opendevbrowser-best-practices/scripts/validate-skill-assets.sh +234 -0
- package/skills/opendevbrowser-continuity-ledger/SKILL.md +10 -0
- package/skills/opendevbrowser-data-extraction/SKILL.md +126 -0
- package/skills/opendevbrowser-data-extraction/artifacts/extraction-workflows.md +31 -0
- package/skills/opendevbrowser-data-extraction/assets/templates/compliance-checklist.md +7 -0
- package/skills/opendevbrowser-data-extraction/assets/templates/extraction-schema.json +17 -0
- package/skills/opendevbrowser-data-extraction/assets/templates/pagination-state.json +11 -0
- package/skills/opendevbrowser-data-extraction/assets/templates/quality-gates.json +10 -0
- package/skills/opendevbrowser-data-extraction/examples/sample-schema.json +19 -0
- package/skills/opendevbrowser-data-extraction/scripts/run-extraction-workflow.sh +83 -0
- package/skills/opendevbrowser-data-extraction/scripts/validate-skill-assets.sh +49 -0
- package/skills/opendevbrowser-form-testing/SKILL.md +143 -0
- package/skills/opendevbrowser-form-testing/artifacts/form-workflows.md +37 -0
- package/skills/opendevbrowser-form-testing/assets/templates/a11y-assertions.md +7 -0
- package/skills/opendevbrowser-form-testing/assets/templates/challenge-decision-tree.json +16 -0
- package/skills/opendevbrowser-form-testing/assets/templates/multi-step-state.json +11 -0
- package/skills/opendevbrowser-form-testing/assets/templates/validation-matrix.json +24 -0
- package/skills/opendevbrowser-form-testing/examples/sample-validation-matrix.json +29 -0
- package/skills/opendevbrowser-form-testing/scripts/run-form-workflow.sh +82 -0
- package/skills/opendevbrowser-form-testing/scripts/validate-skill-assets.sh +49 -0
- package/skills/opendevbrowser-login-automation/SKILL.md +159 -0
- package/skills/opendevbrowser-login-automation/artifacts/login-workflows.md +39 -0
- package/skills/opendevbrowser-login-automation/assets/templates/auth-signals.json +21 -0
- package/skills/opendevbrowser-login-automation/assets/templates/challenge-checkpoint.md +10 -0
- package/skills/opendevbrowser-login-automation/assets/templates/login-scenario-matrix.json +26 -0
- package/skills/opendevbrowser-login-automation/examples/sample-auth-signals.json +14 -0
- package/skills/opendevbrowser-login-automation/scripts/record-auth-signals.sh +18 -0
- package/skills/opendevbrowser-login-automation/scripts/run-login-workflow.sh +99 -0
- package/skills/opendevbrowser-login-automation/scripts/validate-skill-assets.sh +50 -0
- package/skills/opendevbrowser-product-presentation-asset/SKILL.md +98 -0
- package/skills/opendevbrowser-product-presentation-asset/artifacts/asset-pack-assembly.md +23 -0
- package/skills/opendevbrowser-product-presentation-asset/artifacts/ugc-creative-guide.md +21 -0
- package/skills/opendevbrowser-product-presentation-asset/assets/templates/claims-evidence-map.md +5 -0
- package/skills/opendevbrowser-product-presentation-asset/assets/templates/copy.md +5 -0
- package/skills/opendevbrowser-product-presentation-asset/assets/templates/features.md +4 -0
- package/skills/opendevbrowser-product-presentation-asset/assets/templates/manifest.schema.json +14 -0
- package/skills/opendevbrowser-product-presentation-asset/assets/templates/shot-list.md +7 -0
- package/skills/opendevbrowser-product-presentation-asset/assets/templates/ugc-concepts.md +17 -0
- package/skills/opendevbrowser-product-presentation-asset/assets/templates/user-actions.md +7 -0
- package/skills/opendevbrowser-product-presentation-asset/assets/templates/video-assembly.md +18 -0
- package/skills/opendevbrowser-product-presentation-asset/examples/sample-input.json +6 -0
- package/skills/opendevbrowser-product-presentation-asset/examples/sample-manifest.json +18 -0
- package/skills/opendevbrowser-product-presentation-asset/scripts/capture-screenshots.sh +9 -0
- package/skills/opendevbrowser-product-presentation-asset/scripts/collect-product.sh +14 -0
- package/skills/opendevbrowser-product-presentation-asset/scripts/download-images.sh +9 -0
- package/skills/opendevbrowser-product-presentation-asset/scripts/render-video-brief.sh +96 -0
- package/skills/opendevbrowser-product-presentation-asset/scripts/validate-skill-assets.sh +56 -0
- package/skills/opendevbrowser-product-presentation-asset/scripts/write-manifest.sh +43 -0
- package/skills/opendevbrowser-research/SKILL.md +73 -0
- package/skills/opendevbrowser-research/artifacts/research-workflows.md +29 -0
- package/skills/opendevbrowser-research/assets/templates/compact.md +7 -0
- package/skills/opendevbrowser-research/assets/templates/context.json +18 -0
- package/skills/opendevbrowser-research/assets/templates/report.md +9 -0
- package/skills/opendevbrowser-research/examples/sample-input.json +6 -0
- package/skills/opendevbrowser-research/examples/sample-output.md +4 -0
- package/skills/opendevbrowser-research/scripts/render-output.sh +12 -0
- package/skills/opendevbrowser-research/scripts/run-research.sh +23 -0
- package/skills/opendevbrowser-research/scripts/validate-skill-assets.sh +48 -0
- package/skills/opendevbrowser-research/scripts/write-artifacts.sh +29 -0
- package/skills/opendevbrowser-shopping/SKILL.md +118 -0
- package/skills/opendevbrowser-shopping/artifacts/deal-hunting-workflows.md +37 -0
- package/skills/opendevbrowser-shopping/assets/templates/deal-thresholds.json +8 -0
- package/skills/opendevbrowser-shopping/assets/templates/deals-context.json +9 -0
- package/skills/opendevbrowser-shopping/assets/templates/deals-table.md +4 -0
- package/skills/opendevbrowser-shopping/assets/templates/market-analysis.json +30 -0
- package/skills/opendevbrowser-shopping/examples/sample-deals.md +4 -0
- package/skills/opendevbrowser-shopping/examples/sample-query.json +5 -0
- package/skills/opendevbrowser-shopping/scripts/analyze-market.sh +307 -0
- package/skills/opendevbrowser-shopping/scripts/normalize-offers.sh +28 -0
- package/skills/opendevbrowser-shopping/scripts/render-deals.sh +13 -0
- package/skills/opendevbrowser-shopping/scripts/run-deal-hunt.sh +32 -0
- package/skills/opendevbrowser-shopping/scripts/run-shopping.sh +19 -0
- package/skills/opendevbrowser-shopping/scripts/validate-skill-assets.sh +53 -0
- package/dist/chunk-JVBMT2O5.js +0 -7173
- package/dist/chunk-JVBMT2O5.js.map +0 -1
- package/skills/data-extraction/SKILL.md +0 -128
- package/skills/form-testing/SKILL.md +0 -106
- package/skills/login-automation/SKILL.md +0 -108
|
@@ -5,24 +5,59 @@ import { logError } from "../logging.js";
|
|
|
5
5
|
import { DomBridge } from "./dom-bridge.js";
|
|
6
6
|
import { buildSnapshot } from "./snapshot-builder.js";
|
|
7
7
|
import { OpsSessionStore } from "./ops-session-store.js";
|
|
8
|
+
import { DEFAULT_OPS_PARALLELISM_POLICY, evaluateOpsGovernor } from "./parallelism-governor.js";
|
|
8
9
|
import { redactConsoleText, redactUrl } from "./redaction.js";
|
|
9
10
|
const MAX_CONSOLE_EVENTS = 200;
|
|
10
11
|
const MAX_NETWORK_EVENTS = 300;
|
|
11
12
|
const SESSION_TTL_MS = 20_000;
|
|
12
13
|
const SCREENSHOT_TIMEOUT_MS = 8000;
|
|
13
14
|
const TAB_CLOSE_TIMEOUT_MS = 5000;
|
|
15
|
+
const TARGET_SCOPED_COMMANDS = new Set([
|
|
16
|
+
"storage.setCookies",
|
|
17
|
+
"storage.getCookies",
|
|
18
|
+
"nav.goto",
|
|
19
|
+
"nav.wait",
|
|
20
|
+
"nav.snapshot",
|
|
21
|
+
"interact.click",
|
|
22
|
+
"interact.hover",
|
|
23
|
+
"interact.press",
|
|
24
|
+
"interact.check",
|
|
25
|
+
"interact.uncheck",
|
|
26
|
+
"interact.type",
|
|
27
|
+
"interact.select",
|
|
28
|
+
"interact.scroll",
|
|
29
|
+
"interact.scrollIntoView",
|
|
30
|
+
"dom.getHtml",
|
|
31
|
+
"dom.getText",
|
|
32
|
+
"dom.getAttr",
|
|
33
|
+
"dom.getValue",
|
|
34
|
+
"dom.isVisible",
|
|
35
|
+
"dom.isEnabled",
|
|
36
|
+
"dom.isChecked",
|
|
37
|
+
"canvas.applyRuntimePreviewBridge",
|
|
38
|
+
"export.clonePage",
|
|
39
|
+
"export.cloneComponent",
|
|
40
|
+
"devtools.perf",
|
|
41
|
+
"page.screenshot"
|
|
42
|
+
]);
|
|
14
43
|
export class OpsRuntime {
|
|
15
44
|
sendEnvelope;
|
|
16
45
|
cdp;
|
|
46
|
+
getCanvasPageState;
|
|
47
|
+
performCanvasPageAction;
|
|
17
48
|
tabs = new TabManager();
|
|
18
49
|
dom = new DomBridge();
|
|
19
50
|
sessions = new OpsSessionStore();
|
|
20
51
|
encoder = new TextEncoder();
|
|
21
52
|
closingTimers = new Map();
|
|
53
|
+
parallelWaiters = new Map();
|
|
22
54
|
constructor(options) {
|
|
23
55
|
this.sendEnvelope = options.send;
|
|
24
56
|
this.cdp = options.cdp;
|
|
57
|
+
this.getCanvasPageState = options.getCanvasPageState;
|
|
58
|
+
this.performCanvasPageAction = options.performCanvasPageAction;
|
|
25
59
|
chrome.tabs.onRemoved.addListener(this.handleTabRemoved);
|
|
60
|
+
chrome.tabs.onUpdated.addListener(this.handleTabUpdated);
|
|
26
61
|
chrome.debugger.onEvent.addListener(this.handleDebuggerEvent);
|
|
27
62
|
chrome.debugger.onDetach.addListener(this.handleDebuggerDetach);
|
|
28
63
|
}
|
|
@@ -95,6 +130,19 @@ export class OpsRuntime {
|
|
|
95
130
|
handleTabRemoved = (tabId) => {
|
|
96
131
|
this.handleClosedTarget(tabId, "ops_tab_closed");
|
|
97
132
|
};
|
|
133
|
+
handleTabUpdated = (tabId, changeInfo, tab) => {
|
|
134
|
+
const session = this.sessions.getByTabId(tabId);
|
|
135
|
+
if (!session)
|
|
136
|
+
return;
|
|
137
|
+
if (changeInfo.discarded === true || tab.discarded === true) {
|
|
138
|
+
session.discardedSignals += 1;
|
|
139
|
+
}
|
|
140
|
+
const frozenChange = changeInfo.frozen === true;
|
|
141
|
+
const frozenTab = tab.frozen === true;
|
|
142
|
+
if (frozenChange || frozenTab) {
|
|
143
|
+
session.frozenSignals += 1;
|
|
144
|
+
}
|
|
145
|
+
};
|
|
98
146
|
handleDebuggerDetach = (source) => {
|
|
99
147
|
if (typeof source.tabId !== "number")
|
|
100
148
|
return;
|
|
@@ -197,12 +245,21 @@ export class OpsRuntime {
|
|
|
197
245
|
case "session.status":
|
|
198
246
|
await this.handleSessionStatus(message, clientId);
|
|
199
247
|
return;
|
|
248
|
+
case "storage.setCookies":
|
|
249
|
+
await this.withSession(message, clientId, (session) => this.handleStorageSetCookies(message, session));
|
|
250
|
+
return;
|
|
251
|
+
case "storage.getCookies":
|
|
252
|
+
await this.withSession(message, clientId, (session) => this.handleStorageGetCookies(message, session));
|
|
253
|
+
return;
|
|
200
254
|
case "targets.list":
|
|
201
255
|
await this.withSession(message, clientId, (session) => this.handleTargetsList(message, session));
|
|
202
256
|
return;
|
|
203
257
|
case "targets.use":
|
|
204
258
|
await this.withSession(message, clientId, (session) => this.handleTargetsUse(message, session));
|
|
205
259
|
return;
|
|
260
|
+
case "targets.registerCanvas":
|
|
261
|
+
await this.withSession(message, clientId, (session) => this.handleTargetsRegisterCanvas(message, session));
|
|
262
|
+
return;
|
|
206
263
|
case "targets.new":
|
|
207
264
|
await this.withSession(message, clientId, (session) => this.handleTargetsNew(message, session));
|
|
208
265
|
return;
|
|
@@ -275,6 +332,9 @@ export class OpsRuntime {
|
|
|
275
332
|
case "dom.isChecked":
|
|
276
333
|
await this.withSession(message, clientId, (session) => this.handleDomIsChecked(message, session));
|
|
277
334
|
return;
|
|
335
|
+
case "canvas.applyRuntimePreviewBridge":
|
|
336
|
+
await this.withSession(message, clientId, (session) => this.handleCanvasRuntimePreviewBridge(message, session));
|
|
337
|
+
return;
|
|
278
338
|
case "export.clonePage":
|
|
279
339
|
await this.withSession(message, clientId, (session) => this.handleClonePage(message, session));
|
|
280
340
|
return;
|
|
@@ -299,6 +359,7 @@ export class OpsRuntime {
|
|
|
299
359
|
}
|
|
300
360
|
async handleSessionLaunch(message, clientId) {
|
|
301
361
|
const payload = isRecord(message.payload) ? message.payload : {};
|
|
362
|
+
const parallelismPolicy = parseParallelismPolicy(payload.parallelismPolicy);
|
|
302
363
|
const startUrl = typeof payload.startUrl === "string" ? payload.startUrl : undefined;
|
|
303
364
|
if (startUrl) {
|
|
304
365
|
try {
|
|
@@ -320,28 +381,38 @@ export class OpsRuntime {
|
|
|
320
381
|
this.sendError(message, buildError("ops_unavailable", "No active tab to attach.", true));
|
|
321
382
|
return;
|
|
322
383
|
}
|
|
323
|
-
|
|
324
|
-
|
|
384
|
+
const activeTabId = activeTab.id;
|
|
385
|
+
const resolvedTab = startUrl
|
|
386
|
+
? await this.tabs.waitForTabComplete(activeTabId)
|
|
387
|
+
.catch(() => undefined)
|
|
388
|
+
.then(async () => await this.tabs.getTab(activeTabId) ?? activeTab)
|
|
389
|
+
: activeTab;
|
|
390
|
+
if (resolvedTab.url) {
|
|
391
|
+
const restriction = isRestrictedUrl(resolvedTab.url);
|
|
325
392
|
if (restriction.restricted) {
|
|
326
393
|
this.sendError(message, buildError("restricted_url", restriction.message ?? "Restricted tab.", false));
|
|
327
394
|
return;
|
|
328
395
|
}
|
|
329
396
|
}
|
|
330
397
|
try {
|
|
331
|
-
await this.cdp.attach(
|
|
398
|
+
await this.cdp.attach(activeTabId);
|
|
332
399
|
}
|
|
333
400
|
catch (error) {
|
|
334
401
|
const detail = error instanceof Error ? error.message : "Debugger attach failed";
|
|
335
402
|
this.sendError(message, buildError("cdp_attach_failed", detail, false));
|
|
336
403
|
return;
|
|
337
404
|
}
|
|
338
|
-
|
|
405
|
+
if (!startUrl) {
|
|
406
|
+
await this.tabs.waitForTabComplete(activeTab.id).catch(() => undefined);
|
|
407
|
+
}
|
|
339
408
|
const leaseId = typeof message.leaseId === "string" && message.leaseId.trim().length > 0
|
|
340
409
|
? message.leaseId.trim()
|
|
341
410
|
: createId();
|
|
342
|
-
const session = this.sessions.createSession(clientId,
|
|
343
|
-
url:
|
|
344
|
-
title:
|
|
411
|
+
const session = this.sessions.createSession(clientId, activeTabId, leaseId, {
|
|
412
|
+
url: resolvedTab.url ?? undefined,
|
|
413
|
+
title: resolvedTab.title ?? undefined
|
|
414
|
+
}, {
|
|
415
|
+
parallelismPolicy
|
|
345
416
|
});
|
|
346
417
|
await this.enableSessionDomains(session);
|
|
347
418
|
this.sendEvent({
|
|
@@ -354,8 +425,8 @@ export class OpsRuntime {
|
|
|
354
425
|
this.sendResponse(message, {
|
|
355
426
|
opsSessionId: session.id,
|
|
356
427
|
activeTargetId: session.activeTargetId,
|
|
357
|
-
url:
|
|
358
|
-
title:
|
|
428
|
+
url: resolvedTab.url ?? undefined,
|
|
429
|
+
title: resolvedTab.title ?? undefined,
|
|
359
430
|
leaseId: session.leaseId
|
|
360
431
|
});
|
|
361
432
|
}
|
|
@@ -385,11 +456,12 @@ export class OpsRuntime {
|
|
|
385
456
|
const includeUrls = payload.includeUrls === true;
|
|
386
457
|
const targets = await Promise.all(Array.from(session.targets.values()).map(async (target) => {
|
|
387
458
|
const tab = await this.tabs.getTab(target.tabId);
|
|
459
|
+
const synthetic = session.syntheticTargets.get(target.targetId);
|
|
388
460
|
return {
|
|
389
461
|
targetId: target.targetId,
|
|
390
462
|
type: "page",
|
|
391
|
-
title: tab?.title
|
|
392
|
-
url: includeUrls ? tab?.url
|
|
463
|
+
title: resolveReportedTargetTitle(target, tab?.title, synthetic),
|
|
464
|
+
url: includeUrls ? resolveReportedTargetUrl(target, tab?.url, synthetic) : undefined
|
|
393
465
|
};
|
|
394
466
|
}));
|
|
395
467
|
this.sendResponse(message, { activeTargetId: session.activeTargetId || null, targets });
|
|
@@ -407,10 +479,66 @@ export class OpsRuntime {
|
|
|
407
479
|
await this.tabs.activateTab(target.tabId).catch(() => undefined);
|
|
408
480
|
}
|
|
409
481
|
const tab = target ? await this.tabs.getTab(target.tabId) : null;
|
|
482
|
+
const synthetic = target ? session.syntheticTargets.get(target.targetId) : undefined;
|
|
410
483
|
this.sendResponse(message, {
|
|
411
484
|
activeTargetId: targetId,
|
|
412
|
-
url:
|
|
413
|
-
title: tab?.title
|
|
485
|
+
url: target ? resolveReportedTargetUrl(target, tab?.url, synthetic) : undefined,
|
|
486
|
+
title: target ? resolveReportedTargetTitle(target, tab?.title, synthetic) : undefined
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
async handleTargetsRegisterCanvas(message, session) {
|
|
490
|
+
const payload = isRecord(message.payload) ? message.payload : {};
|
|
491
|
+
const targetId = typeof payload.targetId === "string" ? payload.targetId.trim() : "";
|
|
492
|
+
if (!targetId) {
|
|
493
|
+
this.sendError(message, buildError("invalid_request", "Missing targetId", false));
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
const tabId = parseTabTargetId(targetId);
|
|
497
|
+
if (tabId === null) {
|
|
498
|
+
this.sendError(message, buildError("invalid_request", "Canvas targetId must be tab-<id>.", false));
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
let tab = await this.tabs.getTab(tabId);
|
|
502
|
+
if (!tab) {
|
|
503
|
+
this.sendError(message, buildError("invalid_request", "Unknown targetId", false));
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
await this.tabs.waitForTabComplete(tabId, 5000).catch(() => undefined);
|
|
507
|
+
tab = await this.tabs.getTab(tabId) ?? tab;
|
|
508
|
+
if (!this.isAllowedCanvasTargetUrl(tab.url)) {
|
|
509
|
+
this.sendError(message, buildError("restricted_url", "Only the extension canvas tab can be registered.", false));
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
const existing = session.targets.get(targetId);
|
|
513
|
+
if (existing) {
|
|
514
|
+
existing.url = tab.url ?? existing.url;
|
|
515
|
+
existing.title = tab.title ?? existing.title;
|
|
516
|
+
session.activeTargetId = targetId;
|
|
517
|
+
this.sendResponse(message, {
|
|
518
|
+
targetId,
|
|
519
|
+
url: existing.url,
|
|
520
|
+
title: existing.title,
|
|
521
|
+
adopted: false
|
|
522
|
+
});
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
try {
|
|
526
|
+
await this.cdp.attach(tabId);
|
|
527
|
+
await this.enableTargetDomains(tabId);
|
|
528
|
+
}
|
|
529
|
+
catch (error) {
|
|
530
|
+
logError("ops.register_canvas_target", error, {
|
|
531
|
+
code: "canvas_target_attach_failed",
|
|
532
|
+
extra: { tabId, targetId }
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
const target = this.sessions.addTarget(session.id, tabId, { url: tab.url ?? undefined, title: tab.title ?? undefined });
|
|
536
|
+
session.activeTargetId = target.targetId;
|
|
537
|
+
this.sendResponse(message, {
|
|
538
|
+
targetId: target.targetId,
|
|
539
|
+
url: target.url,
|
|
540
|
+
title: target.title,
|
|
541
|
+
adopted: true
|
|
414
542
|
});
|
|
415
543
|
}
|
|
416
544
|
async handleTargetsNew(message, session) {
|
|
@@ -492,11 +620,12 @@ export class OpsRuntime {
|
|
|
492
620
|
const pages = await Promise.all(this.sessions.listNamedTargets(session.id).map(async ({ name, targetId }) => {
|
|
493
621
|
const target = session.targets.get(targetId);
|
|
494
622
|
const tab = target ? await this.tabs.getTab(target.tabId) : null;
|
|
623
|
+
const synthetic = session.syntheticTargets.get(targetId);
|
|
495
624
|
return {
|
|
496
625
|
name,
|
|
497
626
|
targetId,
|
|
498
|
-
url: tab?.url
|
|
499
|
-
title: tab?.title
|
|
627
|
+
url: resolveReportedTargetUrl(target, tab?.url, synthetic),
|
|
628
|
+
title: resolveReportedTargetTitle(target, tab?.title, synthetic)
|
|
500
629
|
};
|
|
501
630
|
}));
|
|
502
631
|
this.sendResponse(message, { pages });
|
|
@@ -549,6 +678,24 @@ export class OpsRuntime {
|
|
|
549
678
|
if (!target)
|
|
550
679
|
return;
|
|
551
680
|
await this.tabs.activateTab(target.tabId).catch(() => undefined);
|
|
681
|
+
const targetRecord = session.targets.get(target.targetId);
|
|
682
|
+
const syntheticHtml = decodeHtmlDataUrl(url);
|
|
683
|
+
if (syntheticHtml !== null) {
|
|
684
|
+
const result = await executeInTab(target.tabId, replaceDocumentWithHtmlScript, [{ html: syntheticHtml }]);
|
|
685
|
+
session.refStore.clearTarget(target.targetId);
|
|
686
|
+
session.syntheticTargets.set(target.targetId, {
|
|
687
|
+
url,
|
|
688
|
+
title: typeof result?.title === "string" && result.title.trim().length > 0
|
|
689
|
+
? result.title
|
|
690
|
+
: targetRecord?.title
|
|
691
|
+
});
|
|
692
|
+
this.sendResponse(message, {
|
|
693
|
+
finalUrl: url,
|
|
694
|
+
status: undefined,
|
|
695
|
+
timingMs: Date.now() - start
|
|
696
|
+
});
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
552
699
|
const updated = await new Promise((resolve) => {
|
|
553
700
|
chrome.tabs.update(target.tabId, { url }, (tab) => {
|
|
554
701
|
resolve(tab ?? null);
|
|
@@ -556,10 +703,13 @@ export class OpsRuntime {
|
|
|
556
703
|
});
|
|
557
704
|
await this.tabs.waitForTabComplete(target.tabId, timeoutMs).catch(() => undefined);
|
|
558
705
|
const refreshed = await this.tabs.getTab(target.tabId);
|
|
559
|
-
|
|
706
|
+
session.syntheticTargets.delete(target.targetId);
|
|
560
707
|
if (targetRecord) {
|
|
561
|
-
|
|
562
|
-
|
|
708
|
+
session.targets.set(target.targetId, {
|
|
709
|
+
...targetRecord,
|
|
710
|
+
url: refreshed?.url ?? updated?.url ?? url,
|
|
711
|
+
title: refreshed?.title ?? updated?.title ?? targetRecord.title
|
|
712
|
+
});
|
|
563
713
|
}
|
|
564
714
|
this.sendResponse(message, {
|
|
565
715
|
finalUrl: refreshed?.url ?? updated?.url ?? url,
|
|
@@ -580,7 +730,7 @@ export class OpsRuntime {
|
|
|
580
730
|
if (!selector)
|
|
581
731
|
return;
|
|
582
732
|
try {
|
|
583
|
-
await this.waitForSelector(target
|
|
733
|
+
await this.waitForSelector(target, selector, state, timeoutMs);
|
|
584
734
|
this.sendResponse(message, { timingMs: Date.now() - start });
|
|
585
735
|
}
|
|
586
736
|
catch (error) {
|
|
@@ -619,10 +769,12 @@ export class OpsRuntime {
|
|
|
619
769
|
return;
|
|
620
770
|
}
|
|
621
771
|
const tab = await this.tabs.getTab(target.tabId);
|
|
772
|
+
const targetRecord = session.targets.get(target.targetId);
|
|
773
|
+
const synthetic = session.syntheticTargets.get(target.targetId);
|
|
622
774
|
this.sendResponse(message, {
|
|
623
775
|
snapshotId: snapshot.snapshotId,
|
|
624
|
-
url: tab?.url
|
|
625
|
-
title: tab?.title
|
|
776
|
+
url: resolveReportedTargetUrl(targetRecord ?? null, tab?.url, synthetic),
|
|
777
|
+
title: resolveReportedTargetTitle(targetRecord ?? null, tab?.title, synthetic),
|
|
626
778
|
content,
|
|
627
779
|
truncated,
|
|
628
780
|
nextCursor,
|
|
@@ -640,7 +792,7 @@ export class OpsRuntime {
|
|
|
640
792
|
return;
|
|
641
793
|
const start = Date.now();
|
|
642
794
|
const before = await this.tabs.getTab(target.tabId);
|
|
643
|
-
await this.dom.click(target.tabId, selector);
|
|
795
|
+
await this.runElementAction(target, selector, { type: "click" }, () => this.dom.click(target.tabId, selector));
|
|
644
796
|
const after = await this.tabs.getTab(target.tabId);
|
|
645
797
|
const navigated = Boolean(before?.url && after?.url && before.url !== after.url);
|
|
646
798
|
this.sendResponse(message, { timingMs: Date.now() - start, navigated });
|
|
@@ -653,7 +805,7 @@ export class OpsRuntime {
|
|
|
653
805
|
if (!target)
|
|
654
806
|
return;
|
|
655
807
|
const start = Date.now();
|
|
656
|
-
await this.dom.hover(target.tabId, selector);
|
|
808
|
+
await this.runElementAction(target, selector, { type: "hover" }, () => this.dom.hover(target.tabId, selector));
|
|
657
809
|
this.sendResponse(message, { timingMs: Date.now() - start });
|
|
658
810
|
}
|
|
659
811
|
async handlePress(message, session) {
|
|
@@ -670,7 +822,7 @@ export class OpsRuntime {
|
|
|
670
822
|
if (payload.ref && !selector)
|
|
671
823
|
return;
|
|
672
824
|
const start = Date.now();
|
|
673
|
-
await this.dom.press(target.tabId, selector, key);
|
|
825
|
+
await this.runCanvasPageAction(target, { type: "press", key }, selector, () => this.dom.press(target.tabId, selector, key));
|
|
674
826
|
this.sendResponse(message, { timingMs: Date.now() - start });
|
|
675
827
|
}
|
|
676
828
|
async handleCheck(message, session, checked) {
|
|
@@ -681,7 +833,7 @@ export class OpsRuntime {
|
|
|
681
833
|
if (!target)
|
|
682
834
|
return;
|
|
683
835
|
const start = Date.now();
|
|
684
|
-
await this.dom.setChecked(target.tabId, selector, checked);
|
|
836
|
+
await this.runElementAction(target, selector, { type: "setChecked", checked }, () => this.dom.setChecked(target.tabId, selector, checked));
|
|
685
837
|
this.sendResponse(message, { timingMs: Date.now() - start });
|
|
686
838
|
}
|
|
687
839
|
async handleType(message, session) {
|
|
@@ -699,7 +851,7 @@ export class OpsRuntime {
|
|
|
699
851
|
if (!target)
|
|
700
852
|
return;
|
|
701
853
|
const start = Date.now();
|
|
702
|
-
await this.dom.type(target.tabId, selector, text, payload.clear === true, payload.submit === true);
|
|
854
|
+
await this.runElementAction(target, selector, { type: "type", value: text, clear: payload.clear === true, submit: payload.submit === true }, () => this.dom.type(target.tabId, selector, text, payload.clear === true, payload.submit === true));
|
|
703
855
|
this.sendResponse(message, { timingMs: Date.now() - start });
|
|
704
856
|
}
|
|
705
857
|
async handleSelect(message, session) {
|
|
@@ -716,7 +868,7 @@ export class OpsRuntime {
|
|
|
716
868
|
const target = this.requireActiveTarget(session, message);
|
|
717
869
|
if (!target)
|
|
718
870
|
return;
|
|
719
|
-
await this.dom.select(target.tabId, selector, values);
|
|
871
|
+
await this.runElementAction(target, selector, { type: "select", values: values }, () => this.dom.select(target.tabId, selector, values));
|
|
720
872
|
this.sendResponse(message, {});
|
|
721
873
|
}
|
|
722
874
|
async handleScroll(message, session) {
|
|
@@ -729,7 +881,7 @@ export class OpsRuntime {
|
|
|
729
881
|
const target = this.requireActiveTarget(session, message);
|
|
730
882
|
if (!target)
|
|
731
883
|
return;
|
|
732
|
-
await this.dom.scroll(target.tabId, dy, selector);
|
|
884
|
+
await this.runCanvasPageAction(target, { type: "scroll", dy }, selector ?? null, () => this.dom.scroll(target.tabId, dy, selector));
|
|
733
885
|
this.sendResponse(message, {});
|
|
734
886
|
}
|
|
735
887
|
async handleScrollIntoView(message, session) {
|
|
@@ -740,7 +892,7 @@ export class OpsRuntime {
|
|
|
740
892
|
if (!target)
|
|
741
893
|
return;
|
|
742
894
|
const start = Date.now();
|
|
743
|
-
await this.dom.scrollIntoView(target.tabId, selector);
|
|
895
|
+
await this.runElementAction(target, selector, { type: "scrollIntoView" }, () => this.dom.scrollIntoView(target.tabId, selector));
|
|
744
896
|
this.sendResponse(message, { timingMs: Date.now() - start });
|
|
745
897
|
}
|
|
746
898
|
async handleDomGetHtml(message, session) {
|
|
@@ -757,7 +909,7 @@ export class OpsRuntime {
|
|
|
757
909
|
const target = this.requireActiveTarget(session, message);
|
|
758
910
|
if (!target)
|
|
759
911
|
return;
|
|
760
|
-
const html = await this.dom.getOuterHtml(target.tabId, selector);
|
|
912
|
+
const html = await this.runElementAction(target, selector, { type: "outerHTML" }, () => this.dom.getOuterHtml(target.tabId, selector));
|
|
761
913
|
const truncated = html.length > maxChars;
|
|
762
914
|
const outerHTML = truncated ? html.slice(0, maxChars) : html;
|
|
763
915
|
this.sendResponse(message, { outerHTML, truncated });
|
|
@@ -776,7 +928,7 @@ export class OpsRuntime {
|
|
|
776
928
|
const target = this.requireActiveTarget(session, message);
|
|
777
929
|
if (!target)
|
|
778
930
|
return;
|
|
779
|
-
const text = await this.dom.getInnerText(target.tabId, selector);
|
|
931
|
+
const text = await this.runElementAction(target, selector, { type: "innerText" }, () => this.dom.getInnerText(target.tabId, selector));
|
|
780
932
|
const truncated = text.length > maxChars;
|
|
781
933
|
this.sendResponse(message, { text: truncated ? text.slice(0, maxChars) : text, truncated });
|
|
782
934
|
}
|
|
@@ -794,7 +946,7 @@ export class OpsRuntime {
|
|
|
794
946
|
const target = this.requireActiveTarget(session, message);
|
|
795
947
|
if (!target)
|
|
796
948
|
return;
|
|
797
|
-
const value = await this.dom.getAttr(target.tabId, selector, name);
|
|
949
|
+
const value = await this.runElementAction(target, selector, { type: "getAttr", name }, () => this.dom.getAttr(target.tabId, selector, name));
|
|
798
950
|
this.sendResponse(message, { value });
|
|
799
951
|
}
|
|
800
952
|
async handleDomGetValue(message, session) {
|
|
@@ -810,7 +962,7 @@ export class OpsRuntime {
|
|
|
810
962
|
const target = this.requireActiveTarget(session, message);
|
|
811
963
|
if (!target)
|
|
812
964
|
return;
|
|
813
|
-
const value = await this.dom.getValue(target.tabId, selector);
|
|
965
|
+
const value = await this.runElementAction(target, selector, { type: "getValue" }, () => this.dom.getValue(target.tabId, selector));
|
|
814
966
|
this.sendResponse(message, { value });
|
|
815
967
|
}
|
|
816
968
|
async handleDomIsVisible(message, session) {
|
|
@@ -820,8 +972,11 @@ export class OpsRuntime {
|
|
|
820
972
|
const target = this.requireActiveTarget(session, message);
|
|
821
973
|
if (!target)
|
|
822
974
|
return;
|
|
823
|
-
const visible = await this.dom.
|
|
824
|
-
|
|
975
|
+
const visible = await this.runElementAction(target, selector, { type: "getSelectorState" }, async () => await this.dom.getSelectorState(target.tabId, selector));
|
|
976
|
+
const isVisible = typeof visible === "object" && visible !== null && "visible" in visible
|
|
977
|
+
? Boolean(visible.visible)
|
|
978
|
+
: Boolean(visible);
|
|
979
|
+
this.sendResponse(message, { value: isVisible });
|
|
825
980
|
}
|
|
826
981
|
async handleDomIsEnabled(message, session) {
|
|
827
982
|
const selector = this.resolveSelector(session, message.payload, message);
|
|
@@ -830,7 +985,7 @@ export class OpsRuntime {
|
|
|
830
985
|
const target = this.requireActiveTarget(session, message);
|
|
831
986
|
if (!target)
|
|
832
987
|
return;
|
|
833
|
-
const enabled = await this.dom.isEnabled(target.tabId, selector);
|
|
988
|
+
const enabled = await this.runElementAction(target, selector, { type: "isEnabled" }, () => this.dom.isEnabled(target.tabId, selector));
|
|
834
989
|
this.sendResponse(message, { value: enabled });
|
|
835
990
|
}
|
|
836
991
|
async handleDomIsChecked(message, session) {
|
|
@@ -840,14 +995,34 @@ export class OpsRuntime {
|
|
|
840
995
|
const target = this.requireActiveTarget(session, message);
|
|
841
996
|
if (!target)
|
|
842
997
|
return;
|
|
843
|
-
const checked = await this.dom.isChecked(target.tabId, selector);
|
|
998
|
+
const checked = await this.runElementAction(target, selector, { type: "isChecked" }, () => this.dom.isChecked(target.tabId, selector));
|
|
844
999
|
this.sendResponse(message, { value: checked });
|
|
845
1000
|
}
|
|
1001
|
+
async handleCanvasRuntimePreviewBridge(message, session) {
|
|
1002
|
+
const payload = isRecord(message.payload) ? message.payload : {};
|
|
1003
|
+
const bindingId = typeof payload.bindingId === "string" ? payload.bindingId.trim() : "";
|
|
1004
|
+
const rootSelector = typeof payload.rootSelector === "string" ? payload.rootSelector.trim() : "";
|
|
1005
|
+
const html = typeof payload.html === "string" ? payload.html : "";
|
|
1006
|
+
if (!bindingId || !rootSelector) {
|
|
1007
|
+
this.sendError(message, buildError("invalid_request", "Missing bindingId or rootSelector", false));
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
1010
|
+
const target = this.requireActiveTarget(session, message);
|
|
1011
|
+
if (!target)
|
|
1012
|
+
return;
|
|
1013
|
+
const result = await this.dom.applyRuntimePreviewBridge(target.tabId, bindingId, rootSelector, html);
|
|
1014
|
+
this.sendResponse(message, result);
|
|
1015
|
+
}
|
|
846
1016
|
async handleClonePage(message, session) {
|
|
847
1017
|
const payload = isRecord(message.payload) ? message.payload : {};
|
|
848
1018
|
const target = this.requireActiveTarget(session, message);
|
|
849
1019
|
if (!target)
|
|
850
1020
|
return;
|
|
1021
|
+
const canvasCapture = await this.captureCanvasPage(target.tabId, target.targetId);
|
|
1022
|
+
if (canvasCapture) {
|
|
1023
|
+
this.sendResponse(message, { capture: canvasCapture });
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
851
1026
|
const capture = await this.dom.captureDom(target.tabId, "body", {
|
|
852
1027
|
sanitize: payload.sanitize !== false,
|
|
853
1028
|
maxNodes: typeof payload.maxNodes === "number" ? payload.maxNodes : undefined,
|
|
@@ -925,11 +1100,98 @@ export class OpsRuntime {
|
|
|
925
1100
|
const nextSeq = lastEvent ? lastEvent.seq : sinceSeq;
|
|
926
1101
|
this.sendResponse(message, { events, nextSeq });
|
|
927
1102
|
}
|
|
1103
|
+
async handleStorageSetCookies(message, session) {
|
|
1104
|
+
const payload = isRecord(message.payload) ? message.payload : {};
|
|
1105
|
+
const cookies = Array.isArray(payload.cookies) ? payload.cookies : null;
|
|
1106
|
+
if (!cookies) {
|
|
1107
|
+
this.sendError(message, buildError("invalid_request", "Missing cookies", false));
|
|
1108
|
+
return;
|
|
1109
|
+
}
|
|
1110
|
+
const strict = payload.strict !== false;
|
|
1111
|
+
const requestId = typeof payload.requestId === "string" && payload.requestId.trim().length > 0
|
|
1112
|
+
? payload.requestId
|
|
1113
|
+
: createId();
|
|
1114
|
+
const normalized = [];
|
|
1115
|
+
const rejected = [];
|
|
1116
|
+
cookies.forEach((entry, index) => {
|
|
1117
|
+
if (!isRecord(entry)) {
|
|
1118
|
+
rejected.push({ index, reason: "Invalid cookie entry: expected object." });
|
|
1119
|
+
return;
|
|
1120
|
+
}
|
|
1121
|
+
const validation = validateCookieRecord(entry);
|
|
1122
|
+
if (!validation.valid) {
|
|
1123
|
+
rejected.push({ index, reason: validation.reason });
|
|
1124
|
+
return;
|
|
1125
|
+
}
|
|
1126
|
+
normalized.push(validation.cookie);
|
|
1127
|
+
});
|
|
1128
|
+
if (strict && rejected.length > 0) {
|
|
1129
|
+
this.sendError(message, buildError("invalid_request", `Cookie import rejected ${rejected.length} entries.`, false));
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
if (normalized.length > 0) {
|
|
1133
|
+
const target = this.requireActiveTarget(session, message);
|
|
1134
|
+
if (!target)
|
|
1135
|
+
return;
|
|
1136
|
+
try {
|
|
1137
|
+
await this.cdp.sendCommand({ tabId: target.tabId }, "Network.setCookies", { cookies: normalized });
|
|
1138
|
+
}
|
|
1139
|
+
catch (error) {
|
|
1140
|
+
const detail = error instanceof Error ? error.message : "Cookie import failed";
|
|
1141
|
+
this.sendError(message, buildError("execution_failed", detail, false));
|
|
1142
|
+
return;
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
this.sendResponse(message, {
|
|
1146
|
+
requestId,
|
|
1147
|
+
imported: normalized.length,
|
|
1148
|
+
rejected
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
async handleStorageGetCookies(message, session) {
|
|
1152
|
+
const payload = isRecord(message.payload) ? message.payload : {};
|
|
1153
|
+
const requestId = typeof payload.requestId === "string" && payload.requestId.trim().length > 0
|
|
1154
|
+
? payload.requestId
|
|
1155
|
+
: createId();
|
|
1156
|
+
let urls;
|
|
1157
|
+
try {
|
|
1158
|
+
urls = parseCookieFilterUrls(payload.urls);
|
|
1159
|
+
}
|
|
1160
|
+
catch (error) {
|
|
1161
|
+
const detail = error instanceof Error ? error.message : "Invalid cookie url filter.";
|
|
1162
|
+
this.sendError(message, buildError("invalid_request", detail, false));
|
|
1163
|
+
return;
|
|
1164
|
+
}
|
|
1165
|
+
const target = this.requireActiveTarget(session, message);
|
|
1166
|
+
if (!target)
|
|
1167
|
+
return;
|
|
1168
|
+
let rawCookies = [];
|
|
1169
|
+
try {
|
|
1170
|
+
const response = await this.cdp.sendCommand({ tabId: target.tabId }, "Network.getCookies", urls ? { urls } : {});
|
|
1171
|
+
rawCookies = Array.isArray(response.cookies) ? response.cookies : [];
|
|
1172
|
+
}
|
|
1173
|
+
catch (error) {
|
|
1174
|
+
const detail = error instanceof Error ? error.message : "Cookie list failed";
|
|
1175
|
+
this.sendError(message, buildError("execution_failed", detail, false));
|
|
1176
|
+
return;
|
|
1177
|
+
}
|
|
1178
|
+
const cookies = rawCookies
|
|
1179
|
+
.map((entry) => toCookieListRecord(entry))
|
|
1180
|
+
.filter((entry) => entry !== null);
|
|
1181
|
+
this.sendResponse(message, {
|
|
1182
|
+
requestId,
|
|
1183
|
+
cookies,
|
|
1184
|
+
count: cookies.length
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
928
1187
|
async enableSessionDomains(session) {
|
|
1188
|
+
await this.enableTargetDomains(session.tabId);
|
|
1189
|
+
}
|
|
1190
|
+
async enableTargetDomains(tabId) {
|
|
929
1191
|
try {
|
|
930
|
-
await this.cdp.sendCommand({ tabId
|
|
931
|
-
await this.cdp.sendCommand({ tabId
|
|
932
|
-
await this.cdp.sendCommand({ tabId
|
|
1192
|
+
await this.cdp.sendCommand({ tabId }, "Runtime.enable", {});
|
|
1193
|
+
await this.cdp.sendCommand({ tabId }, "Network.enable", {});
|
|
1194
|
+
await this.cdp.sendCommand({ tabId }, "Performance.enable", {});
|
|
933
1195
|
}
|
|
934
1196
|
catch (error) {
|
|
935
1197
|
logError("ops.enable_domains", error, { code: "enable_domains_failed" });
|
|
@@ -939,8 +1201,175 @@ export class OpsRuntime {
|
|
|
939
1201
|
const session = this.getSessionForMessage(message, clientId);
|
|
940
1202
|
if (!session)
|
|
941
1203
|
return;
|
|
942
|
-
|
|
943
|
-
|
|
1204
|
+
if (!TARGET_SCOPED_COMMANDS.has(message.command)) {
|
|
1205
|
+
session.queue = session.queue.then(() => handler(session), () => handler(session));
|
|
1206
|
+
await session.queue;
|
|
1207
|
+
return;
|
|
1208
|
+
}
|
|
1209
|
+
try {
|
|
1210
|
+
await this.withTargetQueue(message, session, handler);
|
|
1211
|
+
}
|
|
1212
|
+
catch (error) {
|
|
1213
|
+
if (isParallelismBackpressureError(error)) {
|
|
1214
|
+
this.sendError(message, buildError("parallelism_backpressure", error.message, true, error.details));
|
|
1215
|
+
return;
|
|
1216
|
+
}
|
|
1217
|
+
throw error;
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
resolveTargetIdForQueue(session, message) {
|
|
1221
|
+
const payload = isRecord(message.payload) ? message.payload : {};
|
|
1222
|
+
const requested = typeof payload.targetId === "string" ? payload.targetId.trim() : "";
|
|
1223
|
+
return requested || session.activeTargetId || session.targetId;
|
|
1224
|
+
}
|
|
1225
|
+
sessionQueueAgeMs(session) {
|
|
1226
|
+
let oldest = null;
|
|
1227
|
+
for (const value of session.targetQueueOldestAt.values()) {
|
|
1228
|
+
if (oldest === null || value < oldest) {
|
|
1229
|
+
oldest = value;
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
return oldest === null ? 0 : Math.max(0, Date.now() - oldest);
|
|
1233
|
+
}
|
|
1234
|
+
sampleParallelism(session) {
|
|
1235
|
+
const now = Date.now();
|
|
1236
|
+
const policy = session.parallelismPolicy;
|
|
1237
|
+
if (session.parallelismState.lastSampleAt > 0
|
|
1238
|
+
&& now - session.parallelismState.lastSampleAt < policy.sampleIntervalMs) {
|
|
1239
|
+
return {
|
|
1240
|
+
state: session.parallelismState,
|
|
1241
|
+
pressure: session.parallelismState.lastPressure,
|
|
1242
|
+
targetCap: session.parallelismState.effectiveCap,
|
|
1243
|
+
waitQueueDepth: session.pendingParallel,
|
|
1244
|
+
waitQueueAgeMs: this.sessionQueueAgeMs(session)
|
|
1245
|
+
};
|
|
1246
|
+
}
|
|
1247
|
+
const snapshot = evaluateOpsGovernor(policy, session.parallelismState, {
|
|
1248
|
+
hostFreeMemPct: 100,
|
|
1249
|
+
rssUsagePct: 0,
|
|
1250
|
+
queueAgeMs: this.sessionQueueAgeMs(session),
|
|
1251
|
+
queueDepth: session.pendingParallel,
|
|
1252
|
+
discardedSignals: session.discardedSignals,
|
|
1253
|
+
frozenSignals: session.frozenSignals
|
|
1254
|
+
}, now);
|
|
1255
|
+
session.parallelismState = snapshot.state;
|
|
1256
|
+
session.discardedSignals = 0;
|
|
1257
|
+
session.frozenSignals = 0;
|
|
1258
|
+
return snapshot;
|
|
1259
|
+
}
|
|
1260
|
+
wakeParallelWaiters(session) {
|
|
1261
|
+
const waiters = this.parallelWaiters.get(session.id);
|
|
1262
|
+
if (!waiters || waiters.length === 0) {
|
|
1263
|
+
return;
|
|
1264
|
+
}
|
|
1265
|
+
this.sampleParallelism(session);
|
|
1266
|
+
while (waiters.length > 0 && session.parallelInFlight < session.parallelismState.effectiveCap) {
|
|
1267
|
+
const waiter = waiters.shift();
|
|
1268
|
+
if (!waiter)
|
|
1269
|
+
break;
|
|
1270
|
+
if (waiter.timer !== null) {
|
|
1271
|
+
clearTimeout(waiter.timer);
|
|
1272
|
+
waiter.timer = null;
|
|
1273
|
+
}
|
|
1274
|
+
session.parallelInFlight += 1;
|
|
1275
|
+
waiter.resolve();
|
|
1276
|
+
}
|
|
1277
|
+
if (waiters.length === 0) {
|
|
1278
|
+
this.parallelWaiters.delete(session.id);
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
createParallelismBackpressureError(session, targetId, timeoutMs) {
|
|
1282
|
+
const snapshot = this.sampleParallelism(session);
|
|
1283
|
+
const details = {
|
|
1284
|
+
sessionId: session.id,
|
|
1285
|
+
targetId,
|
|
1286
|
+
effectiveParallelCap: session.parallelismState.effectiveCap,
|
|
1287
|
+
inFlight: session.parallelInFlight,
|
|
1288
|
+
waitQueueDepth: snapshot.waitQueueDepth,
|
|
1289
|
+
waitQueueAgeMs: snapshot.waitQueueAgeMs,
|
|
1290
|
+
pressure: snapshot.pressure,
|
|
1291
|
+
timeoutMs
|
|
1292
|
+
};
|
|
1293
|
+
const error = new Error(`Parallelism cap reached for target ${targetId}; retry later.`);
|
|
1294
|
+
error.code = "parallelism_backpressure";
|
|
1295
|
+
error.details = details;
|
|
1296
|
+
return error;
|
|
1297
|
+
}
|
|
1298
|
+
async acquireParallelSlot(session, targetId, timeoutMs) {
|
|
1299
|
+
const waiters = this.parallelWaiters.get(session.id) ?? [];
|
|
1300
|
+
this.parallelWaiters.set(session.id, waiters);
|
|
1301
|
+
this.sampleParallelism(session);
|
|
1302
|
+
if (session.parallelInFlight < session.parallelismState.effectiveCap && waiters.length === 0) {
|
|
1303
|
+
session.parallelInFlight += 1;
|
|
1304
|
+
return;
|
|
1305
|
+
}
|
|
1306
|
+
await new Promise((resolve, reject) => {
|
|
1307
|
+
const waiter = {
|
|
1308
|
+
targetId,
|
|
1309
|
+
enqueuedAt: Date.now(),
|
|
1310
|
+
timeoutMs,
|
|
1311
|
+
resolve,
|
|
1312
|
+
reject,
|
|
1313
|
+
timer: null
|
|
1314
|
+
};
|
|
1315
|
+
waiter.timer = setTimeout(() => {
|
|
1316
|
+
const index = waiters.indexOf(waiter);
|
|
1317
|
+
if (index >= 0) {
|
|
1318
|
+
waiters.splice(index, 1);
|
|
1319
|
+
}
|
|
1320
|
+
if (waiters.length === 0) {
|
|
1321
|
+
this.parallelWaiters.delete(session.id);
|
|
1322
|
+
}
|
|
1323
|
+
reject(this.createParallelismBackpressureError(session, targetId, timeoutMs));
|
|
1324
|
+
}, timeoutMs);
|
|
1325
|
+
waiters.push(waiter);
|
|
1326
|
+
this.wakeParallelWaiters(session);
|
|
1327
|
+
});
|
|
1328
|
+
}
|
|
1329
|
+
releaseParallelSlot(session) {
|
|
1330
|
+
session.parallelInFlight = Math.max(0, session.parallelInFlight - 1);
|
|
1331
|
+
this.wakeParallelWaiters(session);
|
|
1332
|
+
}
|
|
1333
|
+
async withTargetQueue(message, session, handler) {
|
|
1334
|
+
const targetId = this.resolveTargetIdForQueue(session, message);
|
|
1335
|
+
const enqueuedAt = Date.now();
|
|
1336
|
+
const previous = session.targetQueues.get(targetId) ?? Promise.resolve();
|
|
1337
|
+
let releaseQueue = () => { };
|
|
1338
|
+
const gate = new Promise((resolve) => {
|
|
1339
|
+
releaseQueue = resolve;
|
|
1340
|
+
});
|
|
1341
|
+
const tail = previous.then(() => gate, () => gate);
|
|
1342
|
+
session.targetQueues.set(targetId, tail);
|
|
1343
|
+
session.pendingParallel += 1;
|
|
1344
|
+
session.targetQueueDepth.set(targetId, (session.targetQueueDepth.get(targetId) ?? 0) + 1);
|
|
1345
|
+
if (!session.targetQueueOldestAt.has(targetId)) {
|
|
1346
|
+
session.targetQueueOldestAt.set(targetId, enqueuedAt);
|
|
1347
|
+
}
|
|
1348
|
+
await previous;
|
|
1349
|
+
let acquired = false;
|
|
1350
|
+
try {
|
|
1351
|
+
await this.acquireParallelSlot(session, targetId, session.parallelismPolicy.backpressureTimeoutMs);
|
|
1352
|
+
acquired = true;
|
|
1353
|
+
await handler(session);
|
|
1354
|
+
}
|
|
1355
|
+
finally {
|
|
1356
|
+
if (acquired) {
|
|
1357
|
+
this.releaseParallelSlot(session);
|
|
1358
|
+
}
|
|
1359
|
+
releaseQueue();
|
|
1360
|
+
const depth = (session.targetQueueDepth.get(targetId) ?? 1) - 1;
|
|
1361
|
+
if (depth <= 0) {
|
|
1362
|
+
session.targetQueueDepth.delete(targetId);
|
|
1363
|
+
session.targetQueueOldestAt.delete(targetId);
|
|
1364
|
+
}
|
|
1365
|
+
else {
|
|
1366
|
+
session.targetQueueDepth.set(targetId, depth);
|
|
1367
|
+
}
|
|
1368
|
+
session.pendingParallel = Math.max(0, session.pendingParallel - 1);
|
|
1369
|
+
if (session.targetQueues.get(targetId) === tail) {
|
|
1370
|
+
session.targetQueues.delete(targetId);
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
944
1373
|
}
|
|
945
1374
|
getSessionForMessage(message, clientId) {
|
|
946
1375
|
const opsSessionId = message.opsSessionId;
|
|
@@ -974,8 +1403,15 @@ export class OpsRuntime {
|
|
|
974
1403
|
session.lastUsedAt = Date.now();
|
|
975
1404
|
return session;
|
|
976
1405
|
}
|
|
1406
|
+
requestedTargetId(session, message) {
|
|
1407
|
+
const payload = isRecord(message.payload) ? message.payload : {};
|
|
1408
|
+
if (typeof payload.targetId === "string" && payload.targetId.trim().length > 0) {
|
|
1409
|
+
return payload.targetId.trim();
|
|
1410
|
+
}
|
|
1411
|
+
return session.activeTargetId || null;
|
|
1412
|
+
}
|
|
977
1413
|
requireActiveTarget(session, message) {
|
|
978
|
-
const targetId = session
|
|
1414
|
+
const targetId = this.requestedTargetId(session, message);
|
|
979
1415
|
if (!targetId) {
|
|
980
1416
|
this.sendError(message, buildError("invalid_request", "No active target", false));
|
|
981
1417
|
return null;
|
|
@@ -987,12 +1423,99 @@ export class OpsRuntime {
|
|
|
987
1423
|
}
|
|
988
1424
|
if (target.url) {
|
|
989
1425
|
const restriction = isRestrictedUrl(target.url);
|
|
990
|
-
if (restriction.restricted) {
|
|
1426
|
+
if (restriction.restricted && !this.isAllowedCanvasTargetUrl(target.url)) {
|
|
991
1427
|
this.sendError(message, buildError("restricted_url", restriction.message ?? "Restricted tab.", false));
|
|
992
1428
|
return null;
|
|
993
1429
|
}
|
|
994
1430
|
}
|
|
995
|
-
return { tabId: target.tabId, targetId: target.targetId };
|
|
1431
|
+
return { tabId: target.tabId, targetId: target.targetId, url: target.url };
|
|
1432
|
+
}
|
|
1433
|
+
isAllowedCanvasTargetUrl(rawUrl) {
|
|
1434
|
+
if (typeof rawUrl !== "string" || rawUrl.length === 0) {
|
|
1435
|
+
return false;
|
|
1436
|
+
}
|
|
1437
|
+
try {
|
|
1438
|
+
const allowedUrl = chrome.runtime.getURL("canvas.html");
|
|
1439
|
+
return rawUrl === allowedUrl || rawUrl.startsWith(`${allowedUrl}#`) || rawUrl.startsWith(`${allowedUrl}?`);
|
|
1440
|
+
}
|
|
1441
|
+
catch {
|
|
1442
|
+
return false;
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
async captureCanvasPage(tabId, targetId) {
|
|
1446
|
+
if (!this.getCanvasPageState) {
|
|
1447
|
+
return null;
|
|
1448
|
+
}
|
|
1449
|
+
const state = this.getCanvasPageState(targetId);
|
|
1450
|
+
if (!state) {
|
|
1451
|
+
return null;
|
|
1452
|
+
}
|
|
1453
|
+
const previewHtml = typeof state.html === "string" && state.html.length > 0
|
|
1454
|
+
? extractBodyHtml(state.html)
|
|
1455
|
+
: null;
|
|
1456
|
+
const shouldProbeLiveStage = Boolean(state.pendingMutation)
|
|
1457
|
+
|| (canvasStateContainsRichMedia(state) && !htmlContainsRichMedia(previewHtml));
|
|
1458
|
+
if (shouldProbeLiveStage) {
|
|
1459
|
+
const liveStageCapture = await this.captureLiveCanvasStage(tabId);
|
|
1460
|
+
if (liveStageCapture) {
|
|
1461
|
+
return liveStageCapture;
|
|
1462
|
+
}
|
|
1463
|
+
const documentCapture = buildCanvasDocumentCapture(state);
|
|
1464
|
+
if (documentCapture) {
|
|
1465
|
+
return documentCapture;
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
if (!previewHtml) {
|
|
1469
|
+
return buildCanvasDocumentCapture(state);
|
|
1470
|
+
}
|
|
1471
|
+
return {
|
|
1472
|
+
html: previewHtml,
|
|
1473
|
+
styles: {},
|
|
1474
|
+
warnings: ["canvas_state_capture"],
|
|
1475
|
+
inlineStyles: false
|
|
1476
|
+
};
|
|
1477
|
+
}
|
|
1478
|
+
async runElementAction(target, selector, action, fallback) {
|
|
1479
|
+
return await this.runCanvasPageAction(target, action, selector, fallback);
|
|
1480
|
+
}
|
|
1481
|
+
async runCanvasPageAction(target, action, selector, fallback) {
|
|
1482
|
+
if (!this.isAllowedCanvasTargetUrl(target.url) || !this.performCanvasPageAction) {
|
|
1483
|
+
return await fallback();
|
|
1484
|
+
}
|
|
1485
|
+
return await this.performCanvasPageAction(target.targetId, action, selector ?? null);
|
|
1486
|
+
}
|
|
1487
|
+
async captureLiveCanvasStage(tabId) {
|
|
1488
|
+
try {
|
|
1489
|
+
const results = await chrome.scripting.executeScript({
|
|
1490
|
+
target: { tabId },
|
|
1491
|
+
func: () => {
|
|
1492
|
+
const stage = document.getElementById("canvas-stage-inner");
|
|
1493
|
+
if (!(stage instanceof HTMLElement)) {
|
|
1494
|
+
return null;
|
|
1495
|
+
}
|
|
1496
|
+
const html = stage.innerHTML.trim();
|
|
1497
|
+
if (!html) {
|
|
1498
|
+
return null;
|
|
1499
|
+
}
|
|
1500
|
+
const width = stage.style.width || `${Math.max(stage.scrollWidth, 320)}px`;
|
|
1501
|
+
const height = stage.style.height || `${Math.max(stage.scrollHeight, 240)}px`;
|
|
1502
|
+
return `<body><main data-surface="canvas" style="position:relative;width:${width};min-height:${height};">${html}</main></body>`;
|
|
1503
|
+
}
|
|
1504
|
+
});
|
|
1505
|
+
const html = typeof results[0]?.result === "string" ? results[0].result : null;
|
|
1506
|
+
if (!html) {
|
|
1507
|
+
return null;
|
|
1508
|
+
}
|
|
1509
|
+
return {
|
|
1510
|
+
html,
|
|
1511
|
+
styles: {},
|
|
1512
|
+
warnings: ["canvas_state_capture"],
|
|
1513
|
+
inlineStyles: true
|
|
1514
|
+
};
|
|
1515
|
+
}
|
|
1516
|
+
catch {
|
|
1517
|
+
return null;
|
|
1518
|
+
}
|
|
996
1519
|
}
|
|
997
1520
|
resolveSelector(session, refOrPayload, message) {
|
|
998
1521
|
const ref = typeof refOrPayload === "string"
|
|
@@ -1002,17 +1525,22 @@ export class OpsRuntime {
|
|
|
1002
1525
|
this.sendError(message, buildError("invalid_request", "Missing ref", false));
|
|
1003
1526
|
return null;
|
|
1004
1527
|
}
|
|
1005
|
-
const
|
|
1528
|
+
const targetId = this.requestedTargetId(session, message);
|
|
1529
|
+
if (!targetId) {
|
|
1530
|
+
this.sendError(message, buildError("invalid_request", "No active target", false));
|
|
1531
|
+
return null;
|
|
1532
|
+
}
|
|
1533
|
+
const entry = session.refStore.resolve(targetId, ref);
|
|
1006
1534
|
if (!entry) {
|
|
1007
1535
|
this.sendError(message, buildError("invalid_request", `Unknown ref: ${ref}`, false));
|
|
1008
1536
|
return null;
|
|
1009
1537
|
}
|
|
1010
1538
|
return entry.selector;
|
|
1011
1539
|
}
|
|
1012
|
-
async waitForSelector(
|
|
1540
|
+
async waitForSelector(target, selector, state, timeoutMs) {
|
|
1013
1541
|
const start = Date.now();
|
|
1014
1542
|
while (Date.now() - start < timeoutMs) {
|
|
1015
|
-
const snapshot = await this.dom.getSelectorState(tabId, selector);
|
|
1543
|
+
const snapshot = await this.runElementAction(target, selector, { type: "getSelectorState" }, () => this.dom.getSelectorState(target.tabId, selector));
|
|
1016
1544
|
if (state === "attached" && snapshot.attached)
|
|
1017
1545
|
return;
|
|
1018
1546
|
if (state === "visible" && snapshot.visible)
|
|
@@ -1025,6 +1553,17 @@ export class OpsRuntime {
|
|
|
1025
1553
|
}
|
|
1026
1554
|
cleanupSession(session, event) {
|
|
1027
1555
|
this.clearClosingTimer(session.id);
|
|
1556
|
+
const waiters = this.parallelWaiters.get(session.id);
|
|
1557
|
+
if (waiters) {
|
|
1558
|
+
for (const waiter of waiters) {
|
|
1559
|
+
if (waiter.timer !== null) {
|
|
1560
|
+
clearTimeout(waiter.timer);
|
|
1561
|
+
waiter.timer = null;
|
|
1562
|
+
}
|
|
1563
|
+
waiter.reject(new Error("Ops session closed while waiting for parallelism slot."));
|
|
1564
|
+
}
|
|
1565
|
+
this.parallelWaiters.delete(session.id);
|
|
1566
|
+
}
|
|
1028
1567
|
this.sessions.delete(session.id);
|
|
1029
1568
|
for (const target of session.targets.values()) {
|
|
1030
1569
|
void this.cdp.detachTab(target.tabId).catch(() => undefined);
|
|
@@ -1185,12 +1724,188 @@ export class OpsRuntime {
|
|
|
1185
1724
|
});
|
|
1186
1725
|
}
|
|
1187
1726
|
}
|
|
1727
|
+
const numberInRange = (value, fallback, min, max) => {
|
|
1728
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
1729
|
+
return fallback;
|
|
1730
|
+
}
|
|
1731
|
+
return Math.min(max, Math.max(min, value));
|
|
1732
|
+
};
|
|
1733
|
+
const parseParallelismPolicy = (value) => {
|
|
1734
|
+
if (!isRecord(value)) {
|
|
1735
|
+
return DEFAULT_OPS_PARALLELISM_POLICY;
|
|
1736
|
+
}
|
|
1737
|
+
const modeCapsInput = isRecord(value.modeCaps) ? value.modeCaps : {};
|
|
1738
|
+
return {
|
|
1739
|
+
floor: numberInRange(value.floor, DEFAULT_OPS_PARALLELISM_POLICY.floor, 1, 32),
|
|
1740
|
+
backpressureTimeoutMs: numberInRange(value.backpressureTimeoutMs, DEFAULT_OPS_PARALLELISM_POLICY.backpressureTimeoutMs, 100, 120000),
|
|
1741
|
+
sampleIntervalMs: numberInRange(value.sampleIntervalMs, DEFAULT_OPS_PARALLELISM_POLICY.sampleIntervalMs, 250, 60000),
|
|
1742
|
+
recoveryStableWindows: numberInRange(value.recoveryStableWindows, DEFAULT_OPS_PARALLELISM_POLICY.recoveryStableWindows, 1, 20),
|
|
1743
|
+
hostFreeMemMediumPct: numberInRange(value.hostFreeMemMediumPct, DEFAULT_OPS_PARALLELISM_POLICY.hostFreeMemMediumPct, 1, 99),
|
|
1744
|
+
hostFreeMemHighPct: numberInRange(value.hostFreeMemHighPct, DEFAULT_OPS_PARALLELISM_POLICY.hostFreeMemHighPct, 1, 99),
|
|
1745
|
+
hostFreeMemCriticalPct: numberInRange(value.hostFreeMemCriticalPct, DEFAULT_OPS_PARALLELISM_POLICY.hostFreeMemCriticalPct, 1, 99),
|
|
1746
|
+
rssBudgetMb: numberInRange(value.rssBudgetMb, DEFAULT_OPS_PARALLELISM_POLICY.rssBudgetMb, 64, 65536),
|
|
1747
|
+
rssSoftPct: numberInRange(value.rssSoftPct, DEFAULT_OPS_PARALLELISM_POLICY.rssSoftPct, 1, 99),
|
|
1748
|
+
rssHighPct: numberInRange(value.rssHighPct, DEFAULT_OPS_PARALLELISM_POLICY.rssHighPct, 1, 99),
|
|
1749
|
+
rssCriticalPct: numberInRange(value.rssCriticalPct, DEFAULT_OPS_PARALLELISM_POLICY.rssCriticalPct, 1, 99),
|
|
1750
|
+
queueAgeHighMs: numberInRange(value.queueAgeHighMs, DEFAULT_OPS_PARALLELISM_POLICY.queueAgeHighMs, 100, 120000),
|
|
1751
|
+
queueAgeCriticalMs: numberInRange(value.queueAgeCriticalMs, DEFAULT_OPS_PARALLELISM_POLICY.queueAgeCriticalMs, 100, 120000),
|
|
1752
|
+
modeCaps: {
|
|
1753
|
+
managedHeaded: numberInRange(modeCapsInput.managedHeaded, DEFAULT_OPS_PARALLELISM_POLICY.modeCaps.managedHeaded, 1, 64),
|
|
1754
|
+
managedHeadless: numberInRange(modeCapsInput.managedHeadless, DEFAULT_OPS_PARALLELISM_POLICY.modeCaps.managedHeadless, 1, 64),
|
|
1755
|
+
cdpConnectHeaded: numberInRange(modeCapsInput.cdpConnectHeaded, DEFAULT_OPS_PARALLELISM_POLICY.modeCaps.cdpConnectHeaded, 1, 64),
|
|
1756
|
+
cdpConnectHeadless: numberInRange(modeCapsInput.cdpConnectHeadless, DEFAULT_OPS_PARALLELISM_POLICY.modeCaps.cdpConnectHeadless, 1, 64),
|
|
1757
|
+
extensionOpsHeaded: numberInRange(modeCapsInput.extensionOpsHeaded, DEFAULT_OPS_PARALLELISM_POLICY.modeCaps.extensionOpsHeaded, 1, 64),
|
|
1758
|
+
extensionLegacyCdpHeaded: numberInRange(modeCapsInput.extensionLegacyCdpHeaded, DEFAULT_OPS_PARALLELISM_POLICY.modeCaps.extensionLegacyCdpHeaded, 1, 64)
|
|
1759
|
+
}
|
|
1760
|
+
};
|
|
1761
|
+
};
|
|
1762
|
+
const isParallelismBackpressureError = (error) => {
|
|
1763
|
+
if (!(error instanceof Error)) {
|
|
1764
|
+
return false;
|
|
1765
|
+
}
|
|
1766
|
+
const typed = error;
|
|
1767
|
+
return typed.code === "parallelism_backpressure" && typeof typed.details === "object" && typed.details !== null;
|
|
1768
|
+
};
|
|
1188
1769
|
const buildError = (code, message, retryable, details) => ({
|
|
1189
1770
|
code,
|
|
1190
1771
|
message,
|
|
1191
1772
|
retryable,
|
|
1192
1773
|
details
|
|
1193
1774
|
});
|
|
1775
|
+
const validateCookieRecord = (cookie) => {
|
|
1776
|
+
const name = cookie.name?.trim();
|
|
1777
|
+
if (!name) {
|
|
1778
|
+
return { valid: false, reason: "Cookie name is required.", cookie };
|
|
1779
|
+
}
|
|
1780
|
+
if (!/^[^\s;=]+$/.test(name)) {
|
|
1781
|
+
return { valid: false, reason: `Invalid cookie name: ${cookie.name}.`, cookie };
|
|
1782
|
+
}
|
|
1783
|
+
if (typeof cookie.value !== "string" || /\r|\n|;/.test(cookie.value)) {
|
|
1784
|
+
return { valid: false, reason: `Invalid cookie value for ${name}.`, cookie };
|
|
1785
|
+
}
|
|
1786
|
+
const hasUrl = typeof cookie.url === "string" && cookie.url.trim().length > 0;
|
|
1787
|
+
const hasDomain = typeof cookie.domain === "string" && cookie.domain.trim().length > 0;
|
|
1788
|
+
if (!hasUrl && !hasDomain) {
|
|
1789
|
+
return { valid: false, reason: `Cookie ${name} requires url or domain.`, cookie };
|
|
1790
|
+
}
|
|
1791
|
+
let normalizedUrl;
|
|
1792
|
+
if (hasUrl) {
|
|
1793
|
+
try {
|
|
1794
|
+
const parsedUrl = new URL(cookie.url);
|
|
1795
|
+
if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
|
|
1796
|
+
return { valid: false, reason: `Cookie ${name} url must be http(s).`, cookie };
|
|
1797
|
+
}
|
|
1798
|
+
normalizedUrl = parsedUrl.toString();
|
|
1799
|
+
}
|
|
1800
|
+
catch {
|
|
1801
|
+
return { valid: false, reason: `Cookie ${name} has invalid url.`, cookie };
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
let normalizedDomain;
|
|
1805
|
+
if (hasDomain) {
|
|
1806
|
+
normalizedDomain = String(cookie.domain).trim().toLowerCase();
|
|
1807
|
+
if (!/^\.?[a-z0-9.-]+$/.test(normalizedDomain) || normalizedDomain.includes("..")) {
|
|
1808
|
+
return { valid: false, reason: `Cookie ${name} has invalid domain.`, cookie };
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
const normalizedPath = typeof cookie.path === "string" ? cookie.path.trim() : undefined;
|
|
1812
|
+
if (typeof normalizedPath === "string" && !normalizedPath.startsWith("/")) {
|
|
1813
|
+
return { valid: false, reason: `Cookie ${name} path must start with '/'.`, cookie };
|
|
1814
|
+
}
|
|
1815
|
+
if (typeof cookie.expires !== "undefined") {
|
|
1816
|
+
if (!Number.isFinite(cookie.expires) || cookie.expires < -1) {
|
|
1817
|
+
return { valid: false, reason: `Cookie ${name} has invalid expires.`, cookie };
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
if (cookie.sameSite === "None" && cookie.secure !== true) {
|
|
1821
|
+
return { valid: false, reason: `Cookie ${name} with SameSite=None must set secure=true.`, cookie };
|
|
1822
|
+
}
|
|
1823
|
+
const normalizedCookie = {
|
|
1824
|
+
name,
|
|
1825
|
+
value: cookie.value,
|
|
1826
|
+
...(typeof cookie.expires === "number" ? { expires: cookie.expires } : {}),
|
|
1827
|
+
...(typeof cookie.httpOnly === "boolean" ? { httpOnly: cookie.httpOnly } : {}),
|
|
1828
|
+
...(typeof cookie.secure === "boolean" ? { secure: cookie.secure } : {}),
|
|
1829
|
+
...(cookie.sameSite ? { sameSite: cookie.sameSite } : {})
|
|
1830
|
+
};
|
|
1831
|
+
if (normalizedDomain) {
|
|
1832
|
+
normalizedCookie.domain = normalizedDomain;
|
|
1833
|
+
normalizedCookie.path = normalizedPath ?? "/";
|
|
1834
|
+
}
|
|
1835
|
+
else if (normalizedUrl) {
|
|
1836
|
+
normalizedCookie.url = normalizedUrl;
|
|
1837
|
+
}
|
|
1838
|
+
return {
|
|
1839
|
+
valid: true,
|
|
1840
|
+
reason: "",
|
|
1841
|
+
cookie: normalizedCookie
|
|
1842
|
+
};
|
|
1843
|
+
};
|
|
1844
|
+
const parseCookieFilterUrls = (value) => {
|
|
1845
|
+
if (typeof value === "undefined") {
|
|
1846
|
+
return undefined;
|
|
1847
|
+
}
|
|
1848
|
+
if (!Array.isArray(value)) {
|
|
1849
|
+
throw new Error("Cookie url filters must be an array of strings.");
|
|
1850
|
+
}
|
|
1851
|
+
const normalized = [];
|
|
1852
|
+
const seen = new Set();
|
|
1853
|
+
for (const entry of value) {
|
|
1854
|
+
if (typeof entry !== "string") {
|
|
1855
|
+
throw new Error("Cookie url filters must be an array of strings.");
|
|
1856
|
+
}
|
|
1857
|
+
const trimmed = entry.trim();
|
|
1858
|
+
if (!trimmed) {
|
|
1859
|
+
throw new Error("Cookie url filters must be non-empty strings.");
|
|
1860
|
+
}
|
|
1861
|
+
let parsedUrl;
|
|
1862
|
+
try {
|
|
1863
|
+
parsedUrl = new URL(trimmed);
|
|
1864
|
+
}
|
|
1865
|
+
catch {
|
|
1866
|
+
throw new Error(`Cookie url filter is invalid: ${trimmed}`);
|
|
1867
|
+
}
|
|
1868
|
+
if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
|
|
1869
|
+
throw new Error(`Cookie url filter must be http(s): ${trimmed}`);
|
|
1870
|
+
}
|
|
1871
|
+
const normalizedUrl = parsedUrl.toString();
|
|
1872
|
+
if (seen.has(normalizedUrl)) {
|
|
1873
|
+
continue;
|
|
1874
|
+
}
|
|
1875
|
+
seen.add(normalizedUrl);
|
|
1876
|
+
normalized.push(normalizedUrl);
|
|
1877
|
+
}
|
|
1878
|
+
return normalized.length > 0 ? normalized : undefined;
|
|
1879
|
+
};
|
|
1880
|
+
const toCookieListRecord = (entry) => {
|
|
1881
|
+
if (!isRecord(entry)) {
|
|
1882
|
+
return null;
|
|
1883
|
+
}
|
|
1884
|
+
const name = typeof entry.name === "string" ? entry.name : "";
|
|
1885
|
+
const value = typeof entry.value === "string" ? entry.value : "";
|
|
1886
|
+
const domain = typeof entry.domain === "string" ? entry.domain : "";
|
|
1887
|
+
const path = typeof entry.path === "string" ? entry.path : "";
|
|
1888
|
+
const expires = typeof entry.expires === "number" && Number.isFinite(entry.expires) ? entry.expires : -1;
|
|
1889
|
+
const httpOnly = entry.httpOnly === true;
|
|
1890
|
+
const secure = entry.secure === true;
|
|
1891
|
+
if (!name || !domain || !path) {
|
|
1892
|
+
return null;
|
|
1893
|
+
}
|
|
1894
|
+
const sameSiteRaw = entry.sameSite;
|
|
1895
|
+
const sameSite = sameSiteRaw === "Strict" || sameSiteRaw === "Lax" || sameSiteRaw === "None"
|
|
1896
|
+
? sameSiteRaw
|
|
1897
|
+
: undefined;
|
|
1898
|
+
return {
|
|
1899
|
+
name,
|
|
1900
|
+
value,
|
|
1901
|
+
domain,
|
|
1902
|
+
path,
|
|
1903
|
+
expires,
|
|
1904
|
+
httpOnly,
|
|
1905
|
+
secure,
|
|
1906
|
+
...(sameSite ? { sameSite } : {})
|
|
1907
|
+
};
|
|
1908
|
+
};
|
|
1194
1909
|
const isRecord = (value) => {
|
|
1195
1910
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
1196
1911
|
};
|
|
@@ -1227,6 +1942,257 @@ const paginate = (lines, startIndex, maxChars) => {
|
|
|
1227
1942
|
};
|
|
1228
1943
|
};
|
|
1229
1944
|
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
1945
|
+
const parseTabTargetId = (targetId) => {
|
|
1946
|
+
const raw = targetId.startsWith("tab-") ? targetId.slice(4) : targetId;
|
|
1947
|
+
const parsed = Number.parseInt(raw, 10);
|
|
1948
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
1949
|
+
return null;
|
|
1950
|
+
}
|
|
1951
|
+
return parsed;
|
|
1952
|
+
};
|
|
1953
|
+
const extractBodyHtml = (html) => {
|
|
1954
|
+
const bodyMatch = html.match(/<body\b[^>]*>[\s\S]*<\/body>/i);
|
|
1955
|
+
if (bodyMatch) {
|
|
1956
|
+
return bodyMatch[0];
|
|
1957
|
+
}
|
|
1958
|
+
return html;
|
|
1959
|
+
};
|
|
1960
|
+
const htmlContainsRichMedia = (html) => {
|
|
1961
|
+
return typeof html === "string" && /<(img|video|audio)\b/i.test(html);
|
|
1962
|
+
};
|
|
1963
|
+
const canvasStateContainsRichMedia = (state) => {
|
|
1964
|
+
const document = isRecord(state.document) ? state.document : null;
|
|
1965
|
+
const pages = Array.isArray(document?.pages) ? document.pages : [];
|
|
1966
|
+
const assets = Array.isArray(document?.assets) ? document.assets : [];
|
|
1967
|
+
const assetsById = new Map(assets.flatMap((asset) => typeof asset?.id === "string" ? [[asset.id, asset]] : []));
|
|
1968
|
+
return pages.some((page) => Array.isArray(page?.nodes) && page.nodes.some((node) => nodeContainsRichMedia(node, assetsById)));
|
|
1969
|
+
};
|
|
1970
|
+
const nodeContainsRichMedia = (node, assetsById) => {
|
|
1971
|
+
const tagName = readCanvasMediaTagName(node);
|
|
1972
|
+
if (tagName === "img" || tagName === "video" || tagName === "audio") {
|
|
1973
|
+
return true;
|
|
1974
|
+
}
|
|
1975
|
+
const assetIds = Array.isArray(node.metadata.assetIds)
|
|
1976
|
+
? node.metadata.assetIds.filter((entry) => typeof entry === "string" && entry.trim().length > 0)
|
|
1977
|
+
: [];
|
|
1978
|
+
return assetIds.some((assetId) => {
|
|
1979
|
+
const asset = assetsById.get(assetId);
|
|
1980
|
+
const kind = typeof asset?.kind === "string" ? asset.kind.toLowerCase() : "";
|
|
1981
|
+
const mime = typeof asset?.mime === "string" ? asset.mime.toLowerCase() : "";
|
|
1982
|
+
return kind === "image" || kind === "video" || kind === "audio" || mime.startsWith("image/") || mime.startsWith("video/") || mime.startsWith("audio/");
|
|
1983
|
+
});
|
|
1984
|
+
};
|
|
1985
|
+
const readCanvasMediaTagName = (node) => {
|
|
1986
|
+
if (typeof node.props.tagName === "string" && node.props.tagName.trim().length > 0) {
|
|
1987
|
+
return node.props.tagName.trim().toLowerCase();
|
|
1988
|
+
}
|
|
1989
|
+
const codeSync = isRecord(node.metadata.codeSync) ? node.metadata.codeSync : null;
|
|
1990
|
+
if (codeSync && typeof codeSync.tagName === "string" && codeSync.tagName.trim().length > 0) {
|
|
1991
|
+
return codeSync.tagName.trim().toLowerCase();
|
|
1992
|
+
}
|
|
1993
|
+
return null;
|
|
1994
|
+
};
|
|
1995
|
+
const buildCanvasDocumentCapture = (state) => {
|
|
1996
|
+
const page = Array.isArray(state.document.pages) ? state.document.pages[0] : null;
|
|
1997
|
+
if (!page || !Array.isArray(page.nodes) || page.nodes.length === 0) {
|
|
1998
|
+
return null;
|
|
1999
|
+
}
|
|
2000
|
+
const { width, height } = computeCanvasDocumentBounds(page.nodes);
|
|
2001
|
+
const nodes = [...page.nodes]
|
|
2002
|
+
.sort(compareCanvasCaptureNodes)
|
|
2003
|
+
.map((node) => renderCanvasDocumentNode(state.document, node))
|
|
2004
|
+
.join("");
|
|
2005
|
+
return {
|
|
2006
|
+
html: `<body><main data-surface="canvas" style="position:relative;width:${width}px;min-height:${height}px;">${nodes}</main></body>`,
|
|
2007
|
+
styles: {},
|
|
2008
|
+
warnings: ["canvas_state_capture"],
|
|
2009
|
+
inlineStyles: true
|
|
2010
|
+
};
|
|
2011
|
+
};
|
|
2012
|
+
const computeCanvasDocumentBounds = (nodes) => {
|
|
2013
|
+
if (nodes.length === 0) {
|
|
2014
|
+
return { width: 1600, height: 1200 };
|
|
2015
|
+
}
|
|
2016
|
+
const maxX = Math.max(...nodes.map((node) => node.rect.x + node.rect.width));
|
|
2017
|
+
const maxY = Math.max(...nodes.map((node) => node.rect.y + node.rect.height));
|
|
2018
|
+
return {
|
|
2019
|
+
width: Math.max(maxX + 240, 1600),
|
|
2020
|
+
height: Math.max(maxY + 240, 1200)
|
|
2021
|
+
};
|
|
2022
|
+
};
|
|
2023
|
+
const compareCanvasCaptureNodes = (left, right) => {
|
|
2024
|
+
const rootOrder = Number(left.parentId !== null) - Number(right.parentId !== null);
|
|
2025
|
+
if (rootOrder !== 0) {
|
|
2026
|
+
return rootOrder;
|
|
2027
|
+
}
|
|
2028
|
+
const areaOrder = (right.rect.width * right.rect.height) - (left.rect.width * left.rect.height);
|
|
2029
|
+
if (areaOrder !== 0) {
|
|
2030
|
+
return areaOrder;
|
|
2031
|
+
}
|
|
2032
|
+
const verticalOrder = left.rect.y - right.rect.y;
|
|
2033
|
+
return verticalOrder !== 0 ? verticalOrder : left.rect.x - right.rect.x;
|
|
2034
|
+
};
|
|
2035
|
+
const renderCanvasDocumentNode = (document, node) => {
|
|
2036
|
+
const media = resolveCanvasDocumentMedia(document, node);
|
|
2037
|
+
const text = escapeCanvasHtml(nodeTextForCapture(node) || node.name);
|
|
2038
|
+
const style = serializeCanvasCaptureStyle({
|
|
2039
|
+
position: "absolute",
|
|
2040
|
+
left: `${node.rect.x}px`,
|
|
2041
|
+
top: `${node.rect.y}px`,
|
|
2042
|
+
width: `${Math.max(node.rect.width, 40)}px`,
|
|
2043
|
+
minHeight: `${Math.max(node.rect.height, readCanvasMediaTagName(node) === "audio" ? 64 : 40)}px`,
|
|
2044
|
+
overflow: "hidden",
|
|
2045
|
+
...node.style
|
|
2046
|
+
});
|
|
2047
|
+
const title = escapeCanvasAttribute(`${node.kind} • ${node.name}`);
|
|
2048
|
+
if (media?.kind === "image" && media.src) {
|
|
2049
|
+
return `<div data-node-id="${escapeCanvasAttribute(node.id)}" title="${title}" style="${style}"><img src="${escapeCanvasAttribute(media.src)}" alt="${escapeCanvasAttribute(media.alt ?? node.name)}" loading="lazy" draggable="false" style="width:100%;height:100%;object-fit:cover;display:block;" /></div>`;
|
|
2050
|
+
}
|
|
2051
|
+
if (media?.kind === "video" && media.src) {
|
|
2052
|
+
const poster = media.poster ? ` poster="${escapeCanvasAttribute(media.poster)}"` : "";
|
|
2053
|
+
return `<div data-node-id="${escapeCanvasAttribute(node.id)}" title="${title}" style="${style}"><video src="${escapeCanvasAttribute(media.src)}"${poster} muted loop autoplay playsinline preload="metadata" style="width:100%;height:100%;object-fit:cover;display:block;"></video></div>`;
|
|
2054
|
+
}
|
|
2055
|
+
if (media?.kind === "audio" && media.src) {
|
|
2056
|
+
return `<div data-node-id="${escapeCanvasAttribute(node.id)}" title="${title}" style="${style}"><audio src="${escapeCanvasAttribute(media.src)}" controls preload="metadata" style="width:100%;display:block;"></audio>${text ? `<div style="margin-top:8px;font:500 12px/1.4 sans-serif;">${text}</div>` : ""}</div>`;
|
|
2057
|
+
}
|
|
2058
|
+
return `<div data-node-id="${escapeCanvasAttribute(node.id)}" title="${title}" style="${style}">${text}</div>`;
|
|
2059
|
+
};
|
|
2060
|
+
const nodeTextForCapture = (node) => {
|
|
2061
|
+
const raw = node.props.text ?? node.metadata.text;
|
|
2062
|
+
if (raw !== undefined && raw !== null) {
|
|
2063
|
+
return typeof raw === "string" ? raw : String(raw);
|
|
2064
|
+
}
|
|
2065
|
+
return node.kind === "text" || node.kind === "note" || node.kind === "component-instance"
|
|
2066
|
+
? node.name
|
|
2067
|
+
: "";
|
|
2068
|
+
};
|
|
2069
|
+
const resolveCanvasDocumentMedia = (document, node) => {
|
|
2070
|
+
const tagName = readCanvasMediaTagName(node);
|
|
2071
|
+
const attributes = isRecord(node.props.attributes) ? node.props.attributes : {};
|
|
2072
|
+
const assetIds = Array.isArray(node.metadata.assetIds)
|
|
2073
|
+
? node.metadata.assetIds.filter((entry) => typeof entry === "string" && entry.trim().length > 0)
|
|
2074
|
+
: [];
|
|
2075
|
+
const asset = assetIds.length > 0
|
|
2076
|
+
? document.assets.find((entry) => entry.id === assetIds[0])
|
|
2077
|
+
: null;
|
|
2078
|
+
const assetKind = typeof asset?.kind === "string" ? asset.kind.toLowerCase() : null;
|
|
2079
|
+
const assetMime = typeof asset?.mime === "string" ? asset.mime.toLowerCase() : null;
|
|
2080
|
+
const src = typeof node.props.src === "string"
|
|
2081
|
+
? node.props.src
|
|
2082
|
+
: typeof attributes.src === "string"
|
|
2083
|
+
? attributes.src
|
|
2084
|
+
: typeof asset?.url === "string"
|
|
2085
|
+
? asset.url
|
|
2086
|
+
: typeof asset?.repoPath === "string"
|
|
2087
|
+
? asset.repoPath
|
|
2088
|
+
: null;
|
|
2089
|
+
const poster = typeof node.props.poster === "string"
|
|
2090
|
+
? node.props.poster
|
|
2091
|
+
: typeof attributes.poster === "string"
|
|
2092
|
+
? attributes.poster
|
|
2093
|
+
: null;
|
|
2094
|
+
const alt = typeof node.props.alt === "string"
|
|
2095
|
+
? node.props.alt
|
|
2096
|
+
: typeof attributes.alt === "string"
|
|
2097
|
+
? attributes.alt
|
|
2098
|
+
: node.name;
|
|
2099
|
+
if (tagName === "img" || assetKind === "image" || assetMime?.startsWith("image/")) {
|
|
2100
|
+
return { kind: "image", src, poster: null, alt };
|
|
2101
|
+
}
|
|
2102
|
+
if (tagName === "video" || assetKind === "video" || assetMime?.startsWith("video/")) {
|
|
2103
|
+
return { kind: "video", src, poster, alt };
|
|
2104
|
+
}
|
|
2105
|
+
if (tagName === "audio" || assetKind === "audio" || assetMime?.startsWith("audio/")) {
|
|
2106
|
+
return { kind: "audio", src, poster: null, alt };
|
|
2107
|
+
}
|
|
2108
|
+
return null;
|
|
2109
|
+
};
|
|
2110
|
+
const serializeCanvasCaptureStyle = (style) => {
|
|
2111
|
+
return Object.entries(style)
|
|
2112
|
+
.flatMap(([key, value]) => {
|
|
2113
|
+
if (typeof value !== "string" && typeof value !== "number") {
|
|
2114
|
+
return [];
|
|
2115
|
+
}
|
|
2116
|
+
const cssKey = key.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
|
|
2117
|
+
const cssValue = typeof value === "number" && !CANVAS_CAPTURE_UNITLESS_STYLES.has(key) ? `${value}px` : String(value);
|
|
2118
|
+
return `${cssKey}:${escapeCanvasAttribute(cssValue)};`;
|
|
2119
|
+
})
|
|
2120
|
+
.join("");
|
|
2121
|
+
};
|
|
2122
|
+
const escapeCanvasHtml = (value) => {
|
|
2123
|
+
return value
|
|
2124
|
+
.replaceAll("&", "&")
|
|
2125
|
+
.replaceAll("<", "<")
|
|
2126
|
+
.replaceAll(">", ">");
|
|
2127
|
+
};
|
|
2128
|
+
const escapeCanvasAttribute = (value) => {
|
|
2129
|
+
return escapeCanvasHtml(value)
|
|
2130
|
+
.replaceAll("\"", """)
|
|
2131
|
+
.replaceAll("'", "'");
|
|
2132
|
+
};
|
|
2133
|
+
const CANVAS_CAPTURE_UNITLESS_STYLES = new Set(["fontWeight", "lineHeight", "opacity", "zIndex"]);
|
|
2134
|
+
const resolveReportedTargetUrl = (target, liveUrl, synthetic) => {
|
|
2135
|
+
if (typeof synthetic?.url === "string" && isHtmlDataUrl(synthetic.url)) {
|
|
2136
|
+
return synthetic.url;
|
|
2137
|
+
}
|
|
2138
|
+
if (typeof target?.url === "string" && isHtmlDataUrl(target.url)) {
|
|
2139
|
+
return target.url;
|
|
2140
|
+
}
|
|
2141
|
+
return liveUrl ?? target?.url;
|
|
2142
|
+
};
|
|
2143
|
+
const resolveReportedTargetTitle = (target, liveTitle, synthetic) => {
|
|
2144
|
+
if (typeof synthetic?.title === "string" && synthetic.title.length > 0) {
|
|
2145
|
+
return synthetic.title;
|
|
2146
|
+
}
|
|
2147
|
+
if (typeof target?.url === "string" && isHtmlDataUrl(target.url) && typeof target.title === "string" && target.title.length > 0) {
|
|
2148
|
+
return target.title;
|
|
2149
|
+
}
|
|
2150
|
+
return liveTitle ?? target?.title;
|
|
2151
|
+
};
|
|
2152
|
+
const isHtmlDataUrl = (url) => {
|
|
2153
|
+
return url.startsWith("data:text/html");
|
|
2154
|
+
};
|
|
2155
|
+
const decodeHtmlDataUrl = (url) => {
|
|
2156
|
+
if (!isHtmlDataUrl(url)) {
|
|
2157
|
+
return null;
|
|
2158
|
+
}
|
|
2159
|
+
const commaIndex = url.indexOf(",");
|
|
2160
|
+
if (commaIndex === -1) {
|
|
2161
|
+
return null;
|
|
2162
|
+
}
|
|
2163
|
+
const metadata = url.slice(0, commaIndex).toLowerCase();
|
|
2164
|
+
const payload = url.slice(commaIndex + 1);
|
|
2165
|
+
if (metadata.includes(";base64")) {
|
|
2166
|
+
const decoded = atob(payload);
|
|
2167
|
+
const bytes = Uint8Array.from(decoded, (char) => char.charCodeAt(0));
|
|
2168
|
+
return new TextDecoder().decode(bytes);
|
|
2169
|
+
}
|
|
2170
|
+
try {
|
|
2171
|
+
return decodeURIComponent(payload);
|
|
2172
|
+
}
|
|
2173
|
+
catch {
|
|
2174
|
+
return payload;
|
|
2175
|
+
}
|
|
2176
|
+
};
|
|
2177
|
+
const executeInTab = async (tabId, func, args) => {
|
|
2178
|
+
return await new Promise((resolve, reject) => {
|
|
2179
|
+
chrome.scripting.executeScript({ target: { tabId }, func: func, args }, (results) => {
|
|
2180
|
+
const lastError = chrome.runtime.lastError;
|
|
2181
|
+
if (lastError) {
|
|
2182
|
+
reject(new Error(lastError.message));
|
|
2183
|
+
return;
|
|
2184
|
+
}
|
|
2185
|
+
const [first] = results ?? [];
|
|
2186
|
+
resolve((first?.result ?? null));
|
|
2187
|
+
});
|
|
2188
|
+
});
|
|
2189
|
+
};
|
|
2190
|
+
function replaceDocumentWithHtmlScript(input) {
|
|
2191
|
+
document.open();
|
|
2192
|
+
document.write(input.html);
|
|
2193
|
+
document.close();
|
|
2194
|
+
return { title: document.title };
|
|
2195
|
+
}
|
|
1230
2196
|
const withTimeout = async (promise, timeoutMs, message) => {
|
|
1231
2197
|
return await new Promise((resolve, reject) => {
|
|
1232
2198
|
const timeoutId = setTimeout(() => {
|