@agentrhq/webcmd 0.1.1 → 0.1.2
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/README.md +317 -24
- package/dist/src/adapter-shadow.d.ts +11 -0
- package/dist/src/adapter-shadow.js +72 -0
- package/dist/src/adapter-shadow.test.d.ts +1 -0
- package/dist/src/adapter-shadow.test.js +49 -0
- package/dist/src/adapter-source.d.ts +11 -0
- package/dist/src/adapter-source.js +24 -0
- package/dist/src/adapter-source.test.d.ts +1 -0
- package/dist/src/adapter-source.test.js +29 -0
- package/dist/src/brand.d.ts +9 -0
- package/dist/src/brand.js +9 -0
- package/dist/src/brand.test.d.ts +1 -0
- package/dist/src/brand.test.js +15 -0
- package/dist/src/browser/analyze.d.ts +118 -0
- package/dist/src/browser/analyze.js +405 -0
- package/dist/src/browser/analyze.test.d.ts +1 -0
- package/dist/src/browser/analyze.test.js +209 -0
- package/dist/src/browser/article-extract.d.ts +57 -0
- package/dist/src/browser/article-extract.e2e.test.d.ts +1 -0
- package/dist/src/browser/article-extract.e2e.test.js +105 -0
- package/dist/src/browser/article-extract.js +169 -0
- package/dist/src/browser/article-extract.test.d.ts +1 -0
- package/dist/src/browser/article-extract.test.js +94 -0
- package/dist/src/browser/ax-snapshot.d.ts +37 -0
- package/dist/src/browser/ax-snapshot.js +217 -0
- package/dist/src/browser/ax-snapshot.test.d.ts +1 -0
- package/dist/src/browser/ax-snapshot.test.js +91 -0
- package/dist/src/browser/base-page.d.ts +151 -0
- package/dist/src/browser/base-page.js +1060 -0
- package/dist/src/browser/base-page.test.d.ts +1 -0
- package/dist/src/browser/base-page.test.js +803 -0
- package/dist/src/browser/bridge-readiness.d.ts +14 -0
- package/dist/src/browser/bridge-readiness.js +24 -0
- package/dist/src/browser/bridge-readiness.test.d.ts +1 -0
- package/dist/src/browser/bridge-readiness.test.js +58 -0
- package/dist/src/browser/bridge.d.ts +26 -0
- package/dist/src/browser/bridge.js +58 -0
- package/dist/src/browser/cdp-click-fixture.test.d.ts +1 -0
- package/dist/src/browser/cdp-click-fixture.test.js +87 -0
- package/dist/src/browser/cdp.d.ts +46 -0
- package/dist/src/browser/cdp.js +536 -0
- package/dist/src/browser/cdp.test.d.ts +1 -0
- package/dist/src/browser/cdp.test.js +79 -0
- package/dist/src/browser/compound.d.ts +59 -0
- package/dist/src/browser/compound.js +112 -0
- package/dist/src/browser/compound.test.d.ts +1 -0
- package/dist/src/browser/compound.test.js +175 -0
- package/dist/src/browser/config.d.ts +2 -0
- package/dist/src/browser/config.js +14 -0
- package/dist/src/browser/daemon-client.d.ts +32 -0
- package/dist/src/browser/daemon-client.js +135 -0
- package/dist/src/browser/daemon-client.test.d.ts +1 -0
- package/dist/src/browser/daemon-client.test.js +326 -0
- package/dist/src/browser/daemon-lifecycle.d.ts +37 -0
- package/dist/src/browser/daemon-lifecycle.js +147 -0
- package/dist/src/browser/daemon-transport.d.ts +54 -0
- package/dist/src/browser/daemon-transport.js +68 -0
- package/dist/src/browser/daemon-version.d.ts +4 -0
- package/dist/src/browser/daemon-version.js +12 -0
- package/dist/src/browser/dom-helpers.d.ts +41 -0
- package/dist/src/browser/dom-helpers.js +239 -0
- package/dist/src/browser/dom-helpers.test.d.ts +1 -0
- package/dist/src/browser/dom-helpers.test.js +106 -0
- package/dist/src/browser/dom-snapshot.d.ts +93 -0
- package/dist/src/browser/dom-snapshot.js +894 -0
- package/dist/src/browser/dom-snapshot.test.d.ts +11 -0
- package/dist/src/browser/dom-snapshot.test.js +313 -0
- package/dist/src/browser/errors.d.ts +38 -0
- package/dist/src/browser/errors.js +79 -0
- package/dist/src/browser/errors.test.d.ts +1 -0
- package/dist/src/browser/errors.test.js +60 -0
- package/dist/src/browser/extract.d.ts +69 -0
- package/dist/src/browser/extract.js +132 -0
- package/dist/src/browser/extract.test.d.ts +1 -0
- package/dist/src/browser/extract.test.js +129 -0
- package/dist/src/browser/find.d.ts +84 -0
- package/dist/src/browser/find.js +398 -0
- package/dist/src/browser/find.test.d.ts +1 -0
- package/dist/src/browser/find.test.js +180 -0
- package/dist/src/browser/html-tree.d.ts +75 -0
- package/dist/src/browser/html-tree.js +112 -0
- package/dist/src/browser/html-tree.test.d.ts +1 -0
- package/dist/src/browser/html-tree.test.js +181 -0
- package/dist/src/browser/index.d.ts +14 -0
- package/dist/src/browser/index.js +12 -0
- package/dist/src/browser/network-cache.d.ts +49 -0
- package/dist/src/browser/network-cache.js +78 -0
- package/dist/src/browser/network-cache.test.d.ts +1 -0
- package/dist/src/browser/network-cache.test.js +75 -0
- package/dist/src/browser/network-interceptor.d.ts +11 -0
- package/dist/src/browser/network-interceptor.js +11 -0
- package/dist/src/browser/network-key.d.ts +22 -0
- package/dist/src/browser/network-key.js +66 -0
- package/dist/src/browser/network-key.test.d.ts +1 -0
- package/dist/src/browser/network-key.test.js +49 -0
- package/dist/src/browser/page.d.ts +83 -0
- package/dist/src/browser/page.js +453 -0
- package/dist/src/browser/page.test.d.ts +1 -0
- package/dist/src/browser/page.test.js +406 -0
- package/dist/src/browser/profile.d.ts +14 -0
- package/dist/src/browser/profile.js +86 -0
- package/dist/src/browser/protocol.d.ts +63 -0
- package/dist/src/browser/protocol.js +1 -0
- package/dist/src/browser/runtime/local-cloak/actions.d.ts +3 -0
- package/dist/src/browser/runtime/local-cloak/actions.js +240 -0
- package/dist/src/browser/runtime/local-cloak/downloads.d.ts +3 -0
- package/dist/src/browser/runtime/local-cloak/downloads.js +28 -0
- package/dist/src/browser/runtime/local-cloak/network.d.ts +28 -0
- package/dist/src/browser/runtime/local-cloak/network.js +120 -0
- package/dist/src/browser/runtime/local-cloak/network.test.d.ts +1 -0
- package/dist/src/browser/runtime/local-cloak/network.test.js +124 -0
- package/dist/src/browser/runtime/local-cloak/profiles.d.ts +6 -0
- package/dist/src/browser/runtime/local-cloak/profiles.js +17 -0
- package/dist/src/browser/runtime/local-cloak/profiles.test.d.ts +1 -0
- package/dist/src/browser/runtime/local-cloak/profiles.test.js +19 -0
- package/dist/src/browser/runtime/local-cloak/provider.d.ts +15 -0
- package/dist/src/browser/runtime/local-cloak/provider.js +27 -0
- package/dist/src/browser/runtime/local-cloak/provider.test.d.ts +1 -0
- package/dist/src/browser/runtime/local-cloak/provider.test.js +244 -0
- package/dist/src/browser/runtime/local-cloak/session-manager.d.ts +77 -0
- package/dist/src/browser/runtime/local-cloak/session-manager.js +280 -0
- package/dist/src/browser/runtime/local-cloak/session-manager.test.d.ts +1 -0
- package/dist/src/browser/runtime/local-cloak/session-manager.test.js +92 -0
- package/dist/src/browser/runtime/provider.d.ts +9 -0
- package/dist/src/browser/runtime/provider.js +1 -0
- package/dist/src/browser/runtime/provider.test.d.ts +2 -0
- package/dist/src/browser/runtime/provider.test.js +49 -0
- package/dist/src/browser/shape-filter.d.ts +52 -0
- package/dist/src/browser/shape-filter.js +101 -0
- package/dist/src/browser/shape-filter.test.d.ts +1 -0
- package/dist/src/browser/shape-filter.test.js +101 -0
- package/dist/src/browser/shape.d.ts +23 -0
- package/dist/src/browser/shape.js +95 -0
- package/dist/src/browser/shape.test.d.ts +1 -0
- package/dist/src/browser/shape.test.js +82 -0
- package/dist/src/browser/stealth.d.ts +11 -0
- package/dist/src/browser/stealth.js +359 -0
- package/dist/src/browser/stealth.test.d.ts +1 -0
- package/dist/src/browser/stealth.test.js +134 -0
- package/dist/src/browser/tabs.d.ts +13 -0
- package/dist/src/browser/tabs.js +70 -0
- package/dist/src/browser/target-errors.d.ts +39 -0
- package/dist/src/browser/target-errors.js +45 -0
- package/dist/src/browser/target-errors.test.d.ts +1 -0
- package/dist/src/browser/target-errors.test.js +94 -0
- package/dist/src/browser/target-resolver.d.ts +158 -0
- package/dist/src/browser/target-resolver.js +570 -0
- package/dist/src/browser/target-resolver.test.d.ts +1 -0
- package/dist/src/browser/target-resolver.test.js +118 -0
- package/dist/src/browser/utils.d.ts +20 -0
- package/dist/src/browser/utils.js +64 -0
- package/dist/src/browser/utils.test.d.ts +1 -0
- package/dist/src/browser/utils.test.js +29 -0
- package/dist/src/browser/verify-fixture.d.ts +65 -0
- package/dist/src/browser/verify-fixture.js +318 -0
- package/dist/src/browser/verify-fixture.test.d.ts +1 -0
- package/dist/src/browser/verify-fixture.test.js +219 -0
- package/dist/src/browser/visual-refs.d.ts +11 -0
- package/dist/src/browser/visual-refs.js +108 -0
- package/dist/src/browser.test.d.ts +1 -0
- package/dist/src/browser.test.js +297 -0
- package/dist/src/build-manifest.d.ts +103 -0
- package/dist/src/build-manifest.js +363 -0
- package/dist/src/build-manifest.test.d.ts +1 -0
- package/dist/src/build-manifest.test.js +341 -0
- package/dist/src/capabilityRouting.d.ts +19 -0
- package/dist/src/capabilityRouting.js +56 -0
- package/dist/src/capabilityRouting.test.d.ts +1 -0
- package/dist/src/capabilityRouting.test.js +63 -0
- package/dist/src/cli-argv-preprocess.d.ts +63 -0
- package/dist/src/cli-argv-preprocess.js +303 -0
- package/dist/src/cli-argv-preprocess.test.d.ts +1 -0
- package/dist/src/cli-argv-preprocess.test.js +304 -0
- package/dist/src/cli.d.ts +79 -0
- package/dist/src/cli.js +3350 -0
- package/dist/src/cli.test.d.ts +1 -0
- package/dist/src/cli.test.js +3041 -0
- package/dist/src/commanderAdapter.d.ts +21 -0
- package/dist/src/commanderAdapter.js +208 -0
- package/dist/src/commanderAdapter.test.d.ts +1 -0
- package/dist/src/commanderAdapter.test.js +352 -0
- package/dist/src/commands/auth.d.ts +40 -0
- package/dist/src/commands/auth.js +434 -0
- package/dist/src/commands/auth.test.d.ts +1 -0
- package/dist/src/commands/auth.test.js +252 -0
- package/dist/src/commands/daemon.d.ts +9 -0
- package/dist/src/commands/daemon.js +91 -0
- package/dist/src/commands/daemon.test.d.ts +1 -0
- package/dist/src/commands/daemon.test.js +293 -0
- package/dist/src/completion-fast.d.ts +22 -0
- package/dist/src/completion-fast.js +88 -0
- package/dist/src/completion-shared.d.ts +13 -0
- package/dist/src/completion-shared.js +61 -0
- package/dist/src/completion.d.ts +21 -0
- package/dist/src/completion.js +68 -0
- package/dist/src/completion.test.d.ts +1 -0
- package/dist/src/completion.test.js +24 -0
- package/dist/src/constants.d.ts +18 -0
- package/dist/src/constants.js +41 -0
- package/dist/src/convention-audit.d.ts +50 -0
- package/dist/src/convention-audit.js +553 -0
- package/dist/src/convention-audit.test.d.ts +1 -0
- package/dist/src/convention-audit.test.js +255 -0
- package/dist/src/daemon/server.d.ts +13 -0
- package/dist/src/daemon/server.js +185 -0
- package/dist/src/daemon/server.test.d.ts +1 -0
- package/dist/src/daemon/server.test.js +132 -0
- package/dist/src/daemon-utils.d.ts +18 -0
- package/dist/src/daemon-utils.js +37 -0
- package/dist/src/daemon.d.ts +1 -0
- package/dist/src/daemon.js +27 -0
- package/dist/src/daemon.test.d.ts +1 -0
- package/dist/src/daemon.test.js +60 -0
- package/dist/src/discovery.d.ts +46 -0
- package/dist/src/discovery.js +341 -0
- package/dist/src/doctor.d.ts +39 -0
- package/dist/src/doctor.js +168 -0
- package/dist/src/doctor.test.d.ts +1 -0
- package/dist/src/doctor.test.js +302 -0
- package/dist/src/download/article-download.d.ts +72 -0
- package/dist/src/download/article-download.js +305 -0
- package/dist/src/download/article-download.test.d.ts +1 -0
- package/dist/src/download/article-download.test.js +235 -0
- package/dist/src/download/index.d.ts +71 -0
- package/dist/src/download/index.js +363 -0
- package/dist/src/download/index.test.d.ts +1 -0
- package/dist/src/download/index.test.js +174 -0
- package/dist/src/download/media-download.d.ts +49 -0
- package/dist/src/download/media-download.js +127 -0
- package/dist/src/download/media-download.test.d.ts +1 -0
- package/dist/src/download/media-download.test.js +112 -0
- package/dist/src/download/progress.d.ts +36 -0
- package/dist/src/download/progress.js +120 -0
- package/dist/src/download/progress.test.d.ts +1 -0
- package/dist/src/download/progress.test.js +36 -0
- package/dist/src/electron-apps.d.ts +31 -0
- package/dist/src/electron-apps.js +91 -0
- package/dist/src/electron-apps.test.d.ts +1 -0
- package/dist/src/electron-apps.test.js +76 -0
- package/dist/src/engine.test.d.ts +1 -0
- package/dist/src/engine.test.js +312 -0
- package/dist/src/errors.d.ts +114 -0
- package/dist/src/errors.js +174 -0
- package/dist/src/errors.test.d.ts +1 -0
- package/dist/src/errors.test.js +109 -0
- package/dist/src/execution.d.ts +24 -0
- package/dist/src/execution.js +509 -0
- package/dist/src/execution.test.d.ts +1 -0
- package/dist/src/execution.test.js +645 -0
- package/dist/src/external.d.ts +49 -0
- package/dist/src/external.js +211 -0
- package/dist/src/external.test.d.ts +1 -0
- package/dist/src/external.test.js +110 -0
- package/dist/src/help.d.ts +88 -0
- package/dist/src/help.js +556 -0
- package/dist/src/help.test.d.ts +1 -0
- package/dist/src/help.test.js +58 -0
- package/dist/src/hooks.d.ts +46 -0
- package/dist/src/hooks.js +58 -0
- package/dist/src/hooks.test.d.ts +4 -0
- package/dist/src/hooks.test.js +92 -0
- package/dist/src/interceptor.d.ts +44 -0
- package/dist/src/interceptor.js +183 -0
- package/dist/src/interceptor.test.d.ts +4 -0
- package/dist/src/interceptor.test.js +81 -0
- package/dist/src/launcher.d.ts +41 -0
- package/dist/src/launcher.js +226 -0
- package/dist/src/launcher.test.d.ts +1 -0
- package/dist/src/launcher.test.js +153 -0
- package/dist/src/logger.d.ts +26 -0
- package/dist/src/logger.js +49 -0
- package/dist/src/main.d.ts +5 -0
- package/dist/src/main.js +177 -0
- package/dist/src/manifest-types.d.ts +43 -0
- package/dist/src/manifest-types.js +9 -0
- package/dist/src/node-network.d.ts +10 -0
- package/dist/src/node-network.js +174 -0
- package/dist/src/node-network.test.d.ts +1 -0
- package/dist/src/node-network.test.js +55 -0
- package/dist/src/observation/artifact.d.ts +16 -0
- package/dist/src/observation/artifact.js +260 -0
- package/dist/src/observation/artifact.test.d.ts +1 -0
- package/dist/src/observation/artifact.test.js +121 -0
- package/dist/src/observation/events.d.ts +89 -0
- package/dist/src/observation/events.js +1 -0
- package/dist/src/observation/index.d.ts +7 -0
- package/dist/src/observation/index.js +7 -0
- package/dist/src/observation/manager.d.ts +9 -0
- package/dist/src/observation/manager.js +27 -0
- package/dist/src/observation/manager.test.d.ts +1 -0
- package/dist/src/observation/manager.test.js +13 -0
- package/dist/src/observation/redaction.d.ts +11 -0
- package/dist/src/observation/redaction.js +81 -0
- package/dist/src/observation/redaction.test.d.ts +1 -0
- package/dist/src/observation/redaction.test.js +32 -0
- package/dist/src/observation/retention.d.ts +32 -0
- package/dist/src/observation/retention.js +160 -0
- package/dist/src/observation/retention.test.d.ts +1 -0
- package/dist/src/observation/retention.test.js +118 -0
- package/dist/src/observation/ring-buffer.d.ts +22 -0
- package/dist/src/observation/ring-buffer.js +45 -0
- package/dist/src/observation/ring-buffer.test.d.ts +1 -0
- package/dist/src/observation/ring-buffer.test.js +22 -0
- package/dist/src/observation/session.d.ts +25 -0
- package/dist/src/observation/session.js +50 -0
- package/dist/src/output.d.ts +14 -0
- package/dist/src/output.js +152 -0
- package/dist/src/output.test.d.ts +1 -0
- package/dist/src/output.test.js +53 -0
- package/dist/src/package-exports.test.d.ts +1 -0
- package/dist/src/package-exports.test.js +112 -0
- package/dist/src/package-paths.d.ts +8 -0
- package/dist/src/package-paths.js +41 -0
- package/dist/src/pipeline/executor.d.ts +11 -0
- package/dist/src/pipeline/executor.js +93 -0
- package/dist/src/pipeline/executor.test.d.ts +4 -0
- package/dist/src/pipeline/executor.test.js +182 -0
- package/dist/src/pipeline/index.d.ts +5 -0
- package/dist/src/pipeline/index.js +5 -0
- package/dist/src/pipeline/registry.d.ts +28 -0
- package/dist/src/pipeline/registry.js +55 -0
- package/dist/src/pipeline/steps/browser.d.ts +13 -0
- package/dist/src/pipeline/steps/browser.js +86 -0
- package/dist/src/pipeline/steps/download.d.ts +18 -0
- package/dist/src/pipeline/steps/download.js +252 -0
- package/dist/src/pipeline/steps/download.test.d.ts +1 -0
- package/dist/src/pipeline/steps/download.test.js +102 -0
- package/dist/src/pipeline/steps/fetch.d.ts +5 -0
- package/dist/src/pipeline/steps/fetch.js +122 -0
- package/dist/src/pipeline/steps/fetch.test.d.ts +1 -0
- package/dist/src/pipeline/steps/fetch.test.js +117 -0
- package/dist/src/pipeline/steps/intercept.d.ts +5 -0
- package/dist/src/pipeline/steps/intercept.js +50 -0
- package/dist/src/pipeline/steps/tap.d.ts +12 -0
- package/dist/src/pipeline/steps/tap.js +93 -0
- package/dist/src/pipeline/steps/transform.d.ts +9 -0
- package/dist/src/pipeline/steps/transform.js +70 -0
- package/dist/src/pipeline/template.d.ts +17 -0
- package/dist/src/pipeline/template.js +344 -0
- package/dist/src/pipeline/template.test.d.ts +4 -0
- package/dist/src/pipeline/template.test.js +197 -0
- package/dist/src/pipeline/transform.test.d.ts +4 -0
- package/dist/src/pipeline/transform.test.js +133 -0
- package/dist/src/plugin-manifest.d.ts +70 -0
- package/dist/src/plugin-manifest.js +160 -0
- package/dist/src/plugin-manifest.test.d.ts +4 -0
- package/dist/src/plugin-manifest.test.js +179 -0
- package/dist/src/plugin-scaffold.d.ts +28 -0
- package/dist/src/plugin-scaffold.js +143 -0
- package/dist/src/plugin-scaffold.test.d.ts +4 -0
- package/dist/src/plugin-scaffold.test.js +83 -0
- package/dist/src/plugin.d.ts +146 -0
- package/dist/src/plugin.js +1303 -0
- package/dist/src/plugin.test.d.ts +4 -0
- package/dist/src/plugin.test.js +1363 -0
- package/dist/src/registry-api.d.ts +13 -0
- package/dist/src/registry-api.js +10 -0
- package/dist/src/registry.d.ts +123 -0
- package/dist/src/registry.js +125 -0
- package/dist/src/registry.test.d.ts +4 -0
- package/dist/src/registry.test.js +209 -0
- package/dist/src/runtime-copy.test.d.ts +1 -0
- package/dist/src/runtime-copy.test.js +29 -0
- package/dist/src/runtime-detect.d.ts +31 -0
- package/dist/src/runtime-detect.js +47 -0
- package/dist/src/runtime-detect.test.d.ts +1 -0
- package/dist/src/runtime-detect.test.js +39 -0
- package/dist/src/runtime-identity.test.d.ts +1 -0
- package/dist/src/runtime-identity.test.js +18 -0
- package/dist/src/runtime.d.ts +47 -0
- package/dist/src/runtime.js +54 -0
- package/dist/src/scripts/framework.d.ts +7 -0
- package/dist/src/scripts/framework.js +25 -0
- package/dist/src/scripts/interact.d.ts +4 -0
- package/dist/src/scripts/interact.js +20 -0
- package/dist/src/scripts/store.d.ts +13 -0
- package/dist/src/scripts/store.js +48 -0
- package/dist/src/serialization.d.ts +42 -0
- package/dist/src/serialization.js +103 -0
- package/dist/src/serialization.test.d.ts +1 -0
- package/dist/src/serialization.test.js +78 -0
- package/dist/src/skills.d.ts +14 -0
- package/dist/src/skills.js +127 -0
- package/dist/src/skills.test.d.ts +1 -0
- package/dist/src/skills.test.js +71 -0
- package/dist/src/snapshotFormatter.d.ts +11 -0
- package/dist/src/snapshotFormatter.js +338 -0
- package/dist/src/snapshotFormatter.test.d.ts +7 -0
- package/dist/src/snapshotFormatter.test.js +521 -0
- package/dist/src/tui.d.ts +28 -0
- package/dist/src/tui.js +179 -0
- package/dist/src/tui.test.d.ts +1 -0
- package/dist/src/tui.test.js +19 -0
- package/dist/src/types.d.ts +236 -0
- package/dist/src/types.js +7 -0
- package/dist/src/update-check.d.ts +66 -0
- package/dist/src/update-check.js +206 -0
- package/dist/src/update-check.test.d.ts +1 -0
- package/dist/src/update-check.test.js +117 -0
- package/dist/src/utils.d.ts +57 -0
- package/dist/src/utils.js +152 -0
- package/dist/src/utils.test.d.ts +1 -0
- package/dist/src/utils.test.js +155 -0
- package/dist/src/validate.d.ts +21 -0
- package/dist/src/validate.js +130 -0
- package/dist/src/validate.test.d.ts +9 -0
- package/dist/src/validate.test.js +90 -0
- package/dist/src/verify.d.ts +26 -0
- package/dist/src/verify.js +67 -0
- package/dist/src/version.d.ts +4 -0
- package/dist/src/version.js +22 -0
- package/dist/src/weixin-download.test.d.ts +1 -0
- package/dist/src/weixin-download.test.js +45 -0
- package/package.json +4 -3
- package/scripts/check-package-bin.mjs +95 -0
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
const { sendCommandMock, sendCommandFullMock } = vi.hoisted(() => ({
|
|
3
|
+
sendCommandMock: vi.fn(),
|
|
4
|
+
sendCommandFullMock: vi.fn(),
|
|
5
|
+
}));
|
|
6
|
+
const { warnMock } = vi.hoisted(() => ({
|
|
7
|
+
warnMock: vi.fn(),
|
|
8
|
+
}));
|
|
9
|
+
vi.mock('./daemon-client.js', () => ({
|
|
10
|
+
sendCommand: sendCommandMock,
|
|
11
|
+
sendCommandFull: sendCommandFullMock,
|
|
12
|
+
}));
|
|
13
|
+
vi.mock('../logger.js', () => ({
|
|
14
|
+
log: {
|
|
15
|
+
warn: warnMock,
|
|
16
|
+
},
|
|
17
|
+
}));
|
|
18
|
+
import { Page } from './page.js';
|
|
19
|
+
describe('Page.getCurrentUrl', () => {
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
sendCommandMock.mockReset();
|
|
22
|
+
sendCommandFullMock.mockReset();
|
|
23
|
+
warnMock.mockReset();
|
|
24
|
+
});
|
|
25
|
+
it('reads the real browser URL when no local navigation cache exists', async () => {
|
|
26
|
+
sendCommandMock.mockResolvedValueOnce('https://notebooklm.google.com/notebook/nb-live');
|
|
27
|
+
const page = new Page('notebooklm', undefined, undefined, undefined, 'adapter');
|
|
28
|
+
const url = await page.getCurrentUrl();
|
|
29
|
+
expect(url).toBe('https://notebooklm.google.com/notebook/nb-live');
|
|
30
|
+
expect(sendCommandMock).toHaveBeenCalledTimes(1);
|
|
31
|
+
expect(sendCommandMock).toHaveBeenCalledWith('exec', expect.objectContaining({
|
|
32
|
+
session: 'notebooklm',
|
|
33
|
+
surface: 'adapter',
|
|
34
|
+
}));
|
|
35
|
+
});
|
|
36
|
+
it('caches the discovered browser URL for later reads', async () => {
|
|
37
|
+
sendCommandMock.mockResolvedValueOnce('https://notebooklm.google.com/notebook/nb-live');
|
|
38
|
+
const page = new Page('notebooklm', undefined, undefined, undefined, 'adapter');
|
|
39
|
+
expect(await page.getCurrentUrl()).toBe('https://notebooklm.google.com/notebook/nb-live');
|
|
40
|
+
expect(await page.getCurrentUrl()).toBe('https://notebooklm.google.com/notebook/nb-live');
|
|
41
|
+
expect(sendCommandMock).toHaveBeenCalledTimes(1);
|
|
42
|
+
});
|
|
43
|
+
it('passes adapter site session lifecycle through daemon commands', async () => {
|
|
44
|
+
sendCommandFullMock.mockResolvedValueOnce({ page: 'page-1', data: { url: 'https://chatgpt.com/' } });
|
|
45
|
+
sendCommandMock.mockResolvedValueOnce(null);
|
|
46
|
+
const page = new Page('site:chatgpt', undefined, undefined, undefined, 'adapter', 'persistent');
|
|
47
|
+
await page.goto('https://chatgpt.com/', { waitUntil: 'none' });
|
|
48
|
+
await page.evaluate('document.title');
|
|
49
|
+
expect(sendCommandFullMock).toHaveBeenCalledWith('navigate', expect.objectContaining({
|
|
50
|
+
session: 'site:chatgpt',
|
|
51
|
+
surface: 'adapter',
|
|
52
|
+
siteSession: 'persistent',
|
|
53
|
+
}));
|
|
54
|
+
expect(sendCommandMock).toHaveBeenCalledWith('exec', expect.objectContaining({
|
|
55
|
+
session: 'site:chatgpt',
|
|
56
|
+
surface: 'adapter',
|
|
57
|
+
siteSession: 'persistent',
|
|
58
|
+
page: 'page-1',
|
|
59
|
+
}));
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
describe('Page.evaluate', () => {
|
|
63
|
+
beforeEach(() => {
|
|
64
|
+
sendCommandMock.mockReset();
|
|
65
|
+
sendCommandFullMock.mockReset();
|
|
66
|
+
warnMock.mockReset();
|
|
67
|
+
});
|
|
68
|
+
it('retries once when the inspected target navigated during exec', async () => {
|
|
69
|
+
sendCommandMock
|
|
70
|
+
.mockRejectedValueOnce(new Error('{"code":-32000,"message":"Inspected target navigated or closed"}'))
|
|
71
|
+
.mockResolvedValueOnce(42);
|
|
72
|
+
const page = new Page('notebooklm', undefined, undefined, undefined, 'adapter');
|
|
73
|
+
const value = await page.evaluate('21 + 21');
|
|
74
|
+
expect(value).toBe(42);
|
|
75
|
+
expect(sendCommandMock).toHaveBeenCalledTimes(2);
|
|
76
|
+
});
|
|
77
|
+
it('serializes function-form evaluate calls with JSON args', async () => {
|
|
78
|
+
sendCommandMock.mockResolvedValueOnce('/webcmd');
|
|
79
|
+
const page = new Page('twitter', undefined, undefined, undefined, 'adapter');
|
|
80
|
+
const href = await page.evaluate((selector) => {
|
|
81
|
+
const link = document.querySelector(selector);
|
|
82
|
+
return link ? link.getAttribute('href') : null;
|
|
83
|
+
}, 'a[data-testid="AppTabBar_Profile_Link"]');
|
|
84
|
+
expect(href).toBe('/webcmd');
|
|
85
|
+
expect(sendCommandMock).toHaveBeenCalledWith('exec', expect.objectContaining({
|
|
86
|
+
session: 'twitter',
|
|
87
|
+
surface: 'adapter',
|
|
88
|
+
code: expect.stringContaining('(...["a[data-testid=\\"AppTabBar_Profile_Link\\"]"])'),
|
|
89
|
+
}));
|
|
90
|
+
const code = sendCommandMock.mock.calls[0]?.[1]?.code;
|
|
91
|
+
expect(code).toContain('document.querySelector(selector)');
|
|
92
|
+
});
|
|
93
|
+
it('rejects non-JSON-serializable evaluate args before sending to the daemon', async () => {
|
|
94
|
+
const page = new Page('default');
|
|
95
|
+
const circular = {};
|
|
96
|
+
circular.self = circular;
|
|
97
|
+
await expect(page.evaluate((value) => value, circular)).rejects.toThrow('JSON-serializable');
|
|
98
|
+
expect(sendCommandMock).not.toHaveBeenCalled();
|
|
99
|
+
});
|
|
100
|
+
it('keeps string evaluate behavior unchanged', async () => {
|
|
101
|
+
sendCommandMock.mockResolvedValueOnce(42);
|
|
102
|
+
const page = new Page('default');
|
|
103
|
+
await expect(page.evaluate('21 + 21')).resolves.toBe(42);
|
|
104
|
+
expect(sendCommandMock).toHaveBeenCalledWith('exec', expect.objectContaining({
|
|
105
|
+
code: '21 + 21',
|
|
106
|
+
}));
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
describe('Page network capture compatibility', () => {
|
|
110
|
+
beforeEach(() => {
|
|
111
|
+
sendCommandMock.mockReset();
|
|
112
|
+
sendCommandFullMock.mockReset();
|
|
113
|
+
warnMock.mockReset();
|
|
114
|
+
});
|
|
115
|
+
it('treats unknown network-capture-start as unsupported and memoizes it', async () => {
|
|
116
|
+
sendCommandMock.mockRejectedValueOnce(new Error('Unknown action: network-capture-start'));
|
|
117
|
+
const page = new Page('notebooklm', undefined, undefined, undefined, 'adapter');
|
|
118
|
+
await expect(page.startNetworkCapture()).resolves.toBe(false);
|
|
119
|
+
await expect(page.startNetworkCapture()).resolves.toBe(false);
|
|
120
|
+
expect(sendCommandMock).toHaveBeenCalledTimes(1);
|
|
121
|
+
expect(warnMock).toHaveBeenCalledTimes(1);
|
|
122
|
+
expect(warnMock).toHaveBeenCalledWith(expect.stringContaining('does not support network capture'));
|
|
123
|
+
expect(sendCommandMock).toHaveBeenCalledWith('network-capture-start', expect.objectContaining({
|
|
124
|
+
session: 'notebooklm',
|
|
125
|
+
surface: 'adapter',
|
|
126
|
+
}));
|
|
127
|
+
});
|
|
128
|
+
it('returns an empty capture when network-capture-read is unsupported', async () => {
|
|
129
|
+
sendCommandMock.mockRejectedValueOnce(new Error('Unknown action: network-capture-read'));
|
|
130
|
+
const page = new Page('notebooklm', undefined, undefined, undefined, 'adapter');
|
|
131
|
+
await expect(page.readNetworkCapture()).resolves.toEqual([]);
|
|
132
|
+
await expect(page.readNetworkCapture()).resolves.toEqual([]);
|
|
133
|
+
expect(sendCommandMock).toHaveBeenCalledTimes(1);
|
|
134
|
+
expect(warnMock).toHaveBeenCalledTimes(1);
|
|
135
|
+
expect(sendCommandMock).toHaveBeenCalledWith('network-capture-read', expect.objectContaining({
|
|
136
|
+
session: 'notebooklm',
|
|
137
|
+
surface: 'adapter',
|
|
138
|
+
}));
|
|
139
|
+
});
|
|
140
|
+
it('rethrows unrelated network capture failures', async () => {
|
|
141
|
+
sendCommandMock.mockRejectedValueOnce(new Error('Extension disconnected'));
|
|
142
|
+
const page = new Page('notebooklm', undefined, undefined, undefined, 'adapter');
|
|
143
|
+
await expect(page.startNetworkCapture()).rejects.toThrow('Extension disconnected');
|
|
144
|
+
expect(sendCommandMock).toHaveBeenCalledTimes(1);
|
|
145
|
+
expect(warnMock).not.toHaveBeenCalled();
|
|
146
|
+
});
|
|
147
|
+
it('warns only once even if both start and read hit the compatibility fallback', async () => {
|
|
148
|
+
sendCommandMock
|
|
149
|
+
.mockRejectedValueOnce(new Error('Unknown action: network-capture-start'))
|
|
150
|
+
.mockRejectedValueOnce(new Error('Unknown action: network-capture-read'));
|
|
151
|
+
const page = new Page('notebooklm', undefined, undefined, undefined, 'adapter');
|
|
152
|
+
await expect(page.startNetworkCapture()).resolves.toBe(false);
|
|
153
|
+
await expect(page.readNetworkCapture()).resolves.toEqual([]);
|
|
154
|
+
expect(warnMock).toHaveBeenCalledTimes(1);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
describe('Page download waits', () => {
|
|
158
|
+
beforeEach(() => {
|
|
159
|
+
sendCommandMock.mockReset();
|
|
160
|
+
sendCommandFullMock.mockReset();
|
|
161
|
+
warnMock.mockReset();
|
|
162
|
+
});
|
|
163
|
+
it('sends wait-download through the daemon with session and timeout', async () => {
|
|
164
|
+
sendCommandMock.mockResolvedValueOnce({
|
|
165
|
+
downloaded: true,
|
|
166
|
+
filename: '/tmp/receipt.pdf',
|
|
167
|
+
state: 'complete',
|
|
168
|
+
elapsedMs: 5,
|
|
169
|
+
});
|
|
170
|
+
const page = new Page('mercury', undefined, undefined, undefined, 'adapter');
|
|
171
|
+
const result = await page.waitForDownload('receipt', 1234);
|
|
172
|
+
expect(result).toEqual({
|
|
173
|
+
downloaded: true,
|
|
174
|
+
filename: '/tmp/receipt.pdf',
|
|
175
|
+
state: 'complete',
|
|
176
|
+
elapsedMs: 5,
|
|
177
|
+
});
|
|
178
|
+
expect(sendCommandMock).toHaveBeenCalledWith('wait-download', expect.objectContaining({
|
|
179
|
+
session: 'mercury',
|
|
180
|
+
surface: 'adapter',
|
|
181
|
+
pattern: 'receipt',
|
|
182
|
+
timeoutMs: 1234,
|
|
183
|
+
}));
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
describe('Page CDP helpers', () => {
|
|
187
|
+
beforeEach(() => {
|
|
188
|
+
sendCommandMock.mockReset();
|
|
189
|
+
sendCommandFullMock.mockReset();
|
|
190
|
+
warnMock.mockReset();
|
|
191
|
+
});
|
|
192
|
+
it('handles JavaScript dialogs through the CDP passthrough', async () => {
|
|
193
|
+
sendCommandMock.mockResolvedValueOnce({});
|
|
194
|
+
const page = new Page('default');
|
|
195
|
+
await page.handleJavaScriptDialog(true, 'confirm');
|
|
196
|
+
expect(sendCommandMock).toHaveBeenCalledWith('cdp', expect.objectContaining({
|
|
197
|
+
session: 'default',
|
|
198
|
+
surface: 'browser',
|
|
199
|
+
cdpMethod: 'Page.handleJavaScriptDialog',
|
|
200
|
+
cdpParams: { accept: true, promptText: 'confirm' },
|
|
201
|
+
}));
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
describe('Page active target tracking', () => {
|
|
205
|
+
beforeEach(() => {
|
|
206
|
+
sendCommandMock.mockReset();
|
|
207
|
+
sendCommandFullMock.mockReset();
|
|
208
|
+
warnMock.mockReset();
|
|
209
|
+
});
|
|
210
|
+
it('tracks only one active page identity at a time', async () => {
|
|
211
|
+
sendCommandFullMock
|
|
212
|
+
.mockResolvedValueOnce({ data: { url: 'https://first.example' }, page: 'page-1' })
|
|
213
|
+
.mockResolvedValueOnce({ data: { selected: true }, page: 'page-2' });
|
|
214
|
+
sendCommandMock.mockResolvedValue('ok');
|
|
215
|
+
const page = new Page('default');
|
|
216
|
+
await page.goto('https://first.example', { waitUntil: 'none' });
|
|
217
|
+
expect(page.getActivePage()).toBe('page-1');
|
|
218
|
+
await page.selectTab(1);
|
|
219
|
+
expect(page.getActivePage()).toBe('page-2');
|
|
220
|
+
await page.evaluate('1 + 1');
|
|
221
|
+
expect(sendCommandMock).toHaveBeenLastCalledWith('exec', expect.objectContaining({
|
|
222
|
+
session: 'default',
|
|
223
|
+
surface: 'browser',
|
|
224
|
+
page: 'page-2',
|
|
225
|
+
}));
|
|
226
|
+
});
|
|
227
|
+
it('allows the caller to bind a specific active page identity explicitly', async () => {
|
|
228
|
+
sendCommandMock.mockResolvedValue('bound');
|
|
229
|
+
const page = new Page('default');
|
|
230
|
+
page.setActivePage?.('page-explicit');
|
|
231
|
+
await page.evaluate('1 + 1');
|
|
232
|
+
expect(sendCommandMock).toHaveBeenCalledWith('exec', expect.objectContaining({
|
|
233
|
+
session: 'default',
|
|
234
|
+
surface: 'browser',
|
|
235
|
+
page: 'page-explicit',
|
|
236
|
+
}));
|
|
237
|
+
});
|
|
238
|
+
// Regression: a Page instance can keep re-sending a cached targetId after the tab
|
|
239
|
+
// has been closed externally, so the extension throws
|
|
240
|
+
// "Page not found: <id> — stale page identity" on follow-up navigation.
|
|
241
|
+
// goto() now drops the stale identity and retries once without it so the extension's
|
|
242
|
+
// session lease can resolve through to a live tab.
|
|
243
|
+
it('drops a stale page identity and retries navigate once', async () => {
|
|
244
|
+
sendCommandFullMock
|
|
245
|
+
.mockResolvedValueOnce({ data: { url: 'https://example.com/first' }, page: 'page-1' })
|
|
246
|
+
.mockRejectedValueOnce(new Error('Page not found: deadbeef — stale page identity'))
|
|
247
|
+
.mockResolvedValueOnce({ data: { url: 'https://example.com/second' }, page: 'page-2' });
|
|
248
|
+
const page = new Page('site:youtube', undefined, undefined, undefined, 'adapter', 'persistent');
|
|
249
|
+
await page.goto('https://example.com/first', { waitUntil: 'none' });
|
|
250
|
+
expect(page.getActivePage()).toBe('page-1');
|
|
251
|
+
await page.goto('https://example.com/second', { waitUntil: 'none' });
|
|
252
|
+
expect(page.getActivePage()).toBe('page-2');
|
|
253
|
+
expect(sendCommandFullMock).toHaveBeenCalledTimes(3);
|
|
254
|
+
// First retry attempt carried the stale page; the recovery call must drop it.
|
|
255
|
+
const retryCall = sendCommandFullMock.mock.calls[2];
|
|
256
|
+
expect(retryCall[0]).toBe('navigate');
|
|
257
|
+
expect(retryCall[1]).not.toHaveProperty('page');
|
|
258
|
+
});
|
|
259
|
+
it('retries on a bare "Page not found:" error without the stale-identity suffix', async () => {
|
|
260
|
+
// Under concurrent adapter calls the extension can reject with just
|
|
261
|
+
// "Page not found: <id>" (no "— stale page identity" suffix) when the cached
|
|
262
|
+
// targetId was evicted. That still means the identity is dead, so goto() must
|
|
263
|
+
// drop it and retry once instead of cascading failures.
|
|
264
|
+
sendCommandFullMock
|
|
265
|
+
.mockResolvedValueOnce({ data: { url: 'https://example.com/first' }, page: 'page-1' })
|
|
266
|
+
.mockRejectedValueOnce(new Error('Page not found: deadbeef'))
|
|
267
|
+
.mockResolvedValueOnce({ data: { url: 'https://example.com/second' }, page: 'page-2' });
|
|
268
|
+
const page = new Page('site:youtube', undefined, undefined, undefined, 'adapter', 'persistent');
|
|
269
|
+
await page.goto('https://example.com/first', { waitUntil: 'none' });
|
|
270
|
+
await page.goto('https://example.com/second', { waitUntil: 'none' });
|
|
271
|
+
expect(page.getActivePage()).toBe('page-2');
|
|
272
|
+
expect(sendCommandFullMock).toHaveBeenCalledTimes(3);
|
|
273
|
+
const retryCall = sendCommandFullMock.mock.calls[2];
|
|
274
|
+
expect(retryCall[0]).toBe('navigate');
|
|
275
|
+
expect(retryCall[1]).not.toHaveProperty('page');
|
|
276
|
+
});
|
|
277
|
+
it('does not retry stale page errors when no identity was cached', async () => {
|
|
278
|
+
// _page is undefined on a fresh Page — there's nothing to drop, so propagate the
|
|
279
|
+
// error instead of silently retrying with the same params.
|
|
280
|
+
sendCommandFullMock
|
|
281
|
+
.mockRejectedValueOnce(new Error('Page not found: deadbeef — stale page identity'));
|
|
282
|
+
const page = new Page('site:youtube', undefined, undefined, undefined, 'adapter', 'persistent');
|
|
283
|
+
await expect(page.goto('https://example.com', { waitUntil: 'none' }))
|
|
284
|
+
.rejects.toThrow('stale page identity');
|
|
285
|
+
expect(sendCommandFullMock).toHaveBeenCalledTimes(1);
|
|
286
|
+
});
|
|
287
|
+
it('propagates non-stale navigate errors unchanged', async () => {
|
|
288
|
+
sendCommandFullMock
|
|
289
|
+
.mockResolvedValueOnce({ data: { url: 'https://example.com/first' }, page: 'page-1' })
|
|
290
|
+
.mockRejectedValueOnce(new Error('Extension disconnected'));
|
|
291
|
+
const page = new Page('site:youtube', undefined, undefined, undefined, 'adapter', 'persistent');
|
|
292
|
+
await page.goto('https://example.com/first', { waitUntil: 'none' });
|
|
293
|
+
await expect(page.goto('https://example.com/second', { waitUntil: 'none' }))
|
|
294
|
+
.rejects.toThrow('Extension disconnected');
|
|
295
|
+
// No retry for unrelated errors — exactly two navigate calls total.
|
|
296
|
+
expect(sendCommandFullMock).toHaveBeenCalledTimes(2);
|
|
297
|
+
});
|
|
298
|
+
it('does not retry unrelated navigate errors that only mention Page not found in details', async () => {
|
|
299
|
+
sendCommandFullMock
|
|
300
|
+
.mockResolvedValueOnce({ data: { url: 'https://example.com/first' }, page: 'page-1' })
|
|
301
|
+
.mockRejectedValueOnce(new Error('Navigation failed: upstream says Page not found: /missing'));
|
|
302
|
+
const page = new Page('site:youtube', undefined, undefined, undefined, 'adapter', 'persistent');
|
|
303
|
+
await page.goto('https://example.com/first', { waitUntil: 'none' });
|
|
304
|
+
await expect(page.goto('https://example.com/missing', { waitUntil: 'none' }))
|
|
305
|
+
.rejects.toThrow('Navigation failed');
|
|
306
|
+
expect(sendCommandFullMock).toHaveBeenCalledTimes(2);
|
|
307
|
+
});
|
|
308
|
+
it('creates a new tab without changing the current active page binding', async () => {
|
|
309
|
+
sendCommandFullMock
|
|
310
|
+
.mockResolvedValueOnce({ data: { url: 'https://first.example' }, page: 'page-1' })
|
|
311
|
+
.mockResolvedValueOnce({
|
|
312
|
+
data: { url: 'https://second.example' },
|
|
313
|
+
page: 'page-2',
|
|
314
|
+
});
|
|
315
|
+
sendCommandMock.mockResolvedValue('ok');
|
|
316
|
+
const page = new Page('default');
|
|
317
|
+
await page.goto('https://first.example', { waitUntil: 'none' });
|
|
318
|
+
const created = await page.newTab?.('https://second.example');
|
|
319
|
+
expect(created).toBe('page-2');
|
|
320
|
+
expect(page.getActivePage()).toBe('page-1');
|
|
321
|
+
await page.evaluate('1 + 1');
|
|
322
|
+
expect(sendCommandMock).toHaveBeenLastCalledWith('exec', expect.objectContaining({
|
|
323
|
+
session: 'default',
|
|
324
|
+
surface: 'browser',
|
|
325
|
+
page: 'page-1',
|
|
326
|
+
}));
|
|
327
|
+
});
|
|
328
|
+
it('allows the caller to adopt a new tab explicitly after creation', async () => {
|
|
329
|
+
sendCommandFullMock.mockResolvedValueOnce({
|
|
330
|
+
data: { url: 'https://second.example' },
|
|
331
|
+
page: 'page-2',
|
|
332
|
+
});
|
|
333
|
+
const page = new Page('default');
|
|
334
|
+
const created = await page.newTab?.('https://second.example');
|
|
335
|
+
expect(created).toBe('page-2');
|
|
336
|
+
expect(page.getActivePage()).toBeUndefined();
|
|
337
|
+
page.setActivePage?.(created);
|
|
338
|
+
expect(page.getActivePage()).toBe('page-2');
|
|
339
|
+
expect(sendCommandFullMock).toHaveBeenCalledWith('tabs', expect.objectContaining({
|
|
340
|
+
op: 'new',
|
|
341
|
+
url: 'https://second.example',
|
|
342
|
+
session: 'default',
|
|
343
|
+
surface: 'browser',
|
|
344
|
+
}));
|
|
345
|
+
});
|
|
346
|
+
it('closes a tab by explicit page identity', async () => {
|
|
347
|
+
sendCommandMock.mockResolvedValueOnce({ closed: 'page-2' });
|
|
348
|
+
const page = new Page('default');
|
|
349
|
+
await page.closeTab?.('page-2');
|
|
350
|
+
expect(sendCommandMock).toHaveBeenCalledWith('tabs', expect.objectContaining({
|
|
351
|
+
op: 'close',
|
|
352
|
+
session: 'default',
|
|
353
|
+
surface: 'browser',
|
|
354
|
+
page: 'page-2',
|
|
355
|
+
}));
|
|
356
|
+
});
|
|
357
|
+
it('clears the active page binding when closing the selected tab by numeric index', async () => {
|
|
358
|
+
sendCommandFullMock.mockResolvedValueOnce({ data: { selected: true }, page: 'page-2' });
|
|
359
|
+
sendCommandMock
|
|
360
|
+
.mockResolvedValueOnce({ closed: 'page-2' })
|
|
361
|
+
.mockResolvedValueOnce('ok');
|
|
362
|
+
const page = new Page('default');
|
|
363
|
+
await page.selectTab(1);
|
|
364
|
+
expect(page.getActivePage()).toBe('page-2');
|
|
365
|
+
await page.closeTab?.(1);
|
|
366
|
+
expect(page.getActivePage()).toBeUndefined();
|
|
367
|
+
await page.evaluate('1 + 1');
|
|
368
|
+
const evalCall = sendCommandMock.mock.calls.at(-1);
|
|
369
|
+
expect(evalCall?.[0]).toBe('exec');
|
|
370
|
+
expect(evalCall?.[1]).toEqual(expect.objectContaining({
|
|
371
|
+
session: 'default',
|
|
372
|
+
surface: 'browser',
|
|
373
|
+
}));
|
|
374
|
+
expect(evalCall?.[1]).not.toHaveProperty('page');
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
describe('Page.screenshot', () => {
|
|
378
|
+
beforeEach(() => {
|
|
379
|
+
sendCommandMock.mockReset();
|
|
380
|
+
sendCommandFullMock.mockReset();
|
|
381
|
+
warnMock.mockReset();
|
|
382
|
+
});
|
|
383
|
+
it('forwards width / height / fullPage options to the bridge', async () => {
|
|
384
|
+
sendCommandMock.mockResolvedValueOnce('BASE64');
|
|
385
|
+
const page = new Page('default');
|
|
386
|
+
const data = await page.screenshot({ fullPage: true, width: 1080 });
|
|
387
|
+
expect(data).toBe('BASE64');
|
|
388
|
+
expect(sendCommandMock).toHaveBeenCalledWith('screenshot', expect.objectContaining({
|
|
389
|
+
session: 'default',
|
|
390
|
+
surface: 'browser',
|
|
391
|
+
fullPage: true,
|
|
392
|
+
width: 1080,
|
|
393
|
+
}));
|
|
394
|
+
});
|
|
395
|
+
it('omits viewport overrides when none are set', async () => {
|
|
396
|
+
sendCommandMock.mockResolvedValueOnce('BASE64');
|
|
397
|
+
const page = new Page('default');
|
|
398
|
+
await page.screenshot();
|
|
399
|
+
const call = sendCommandMock.mock.calls.at(-1);
|
|
400
|
+
expect(call?.[0]).toBe('screenshot');
|
|
401
|
+
const args = call?.[1];
|
|
402
|
+
expect(args.width).toBeUndefined();
|
|
403
|
+
expect(args.height).toBeUndefined();
|
|
404
|
+
expect(args.fullPage).toBeUndefined();
|
|
405
|
+
});
|
|
406
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const DEFAULT_CONTEXT_ID = "default";
|
|
2
|
+
export type ProfileConfig = {
|
|
3
|
+
version: 1;
|
|
4
|
+
defaultContextId?: string;
|
|
5
|
+
aliases: Record<string, string>;
|
|
6
|
+
};
|
|
7
|
+
export declare function normalizeContextId(value: string | undefined | null): string | undefined;
|
|
8
|
+
export declare function emptyProfileConfig(): ProfileConfig;
|
|
9
|
+
export declare function loadProfileConfig(): ProfileConfig;
|
|
10
|
+
export declare function saveProfileConfig(config: ProfileConfig): void;
|
|
11
|
+
export declare function resolveProfileContextId(profile?: string): string | undefined;
|
|
12
|
+
export declare function aliasForContextId(config: ProfileConfig, contextId: string): string | undefined;
|
|
13
|
+
export declare function renameProfile(contextId: string, alias: string): ProfileConfig;
|
|
14
|
+
export declare function setDefaultProfile(profile: string): ProfileConfig;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as os from 'node:os';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import { CONFIG_DIR_NAME, ENV_PREFIX } from '../brand.js';
|
|
5
|
+
export const DEFAULT_CONTEXT_ID = 'default';
|
|
6
|
+
function profileConfigPath() {
|
|
7
|
+
const baseDir = process.env[`${ENV_PREFIX}_CONFIG_DIR`] || path.join(os.homedir(), CONFIG_DIR_NAME);
|
|
8
|
+
return path.join(baseDir, 'browser-profiles.json');
|
|
9
|
+
}
|
|
10
|
+
export function normalizeContextId(value) {
|
|
11
|
+
const trimmed = value?.trim();
|
|
12
|
+
return trimmed || undefined;
|
|
13
|
+
}
|
|
14
|
+
export function emptyProfileConfig() {
|
|
15
|
+
return { version: 1, aliases: {} };
|
|
16
|
+
}
|
|
17
|
+
export function loadProfileConfig() {
|
|
18
|
+
try {
|
|
19
|
+
const raw = fs.readFileSync(profileConfigPath(), 'utf-8');
|
|
20
|
+
const parsed = JSON.parse(raw);
|
|
21
|
+
const aliases = parsed.aliases && typeof parsed.aliases === 'object'
|
|
22
|
+
? Object.fromEntries(Object.entries(parsed.aliases).filter((entry) => {
|
|
23
|
+
const [key, value] = entry;
|
|
24
|
+
return typeof key === 'string' && key.trim().length > 0
|
|
25
|
+
&& typeof value === 'string' && value.trim().length > 0;
|
|
26
|
+
}))
|
|
27
|
+
: {};
|
|
28
|
+
return {
|
|
29
|
+
version: 1,
|
|
30
|
+
aliases,
|
|
31
|
+
...(typeof parsed.defaultContextId === 'string' && parsed.defaultContextId.trim()
|
|
32
|
+
? { defaultContextId: parsed.defaultContextId.trim() }
|
|
33
|
+
: {}),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return emptyProfileConfig();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function saveProfileConfig(config) {
|
|
41
|
+
const target = profileConfigPath();
|
|
42
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
43
|
+
fs.writeFileSync(target, JSON.stringify(config, null, 2) + '\n', 'utf-8');
|
|
44
|
+
}
|
|
45
|
+
export function resolveProfileContextId(profile) {
|
|
46
|
+
const config = loadProfileConfig();
|
|
47
|
+
const requested = normalizeContextId(profile)
|
|
48
|
+
?? normalizeContextId(process.env[`${ENV_PREFIX}_PROFILE`])
|
|
49
|
+
?? normalizeContextId(config.defaultContextId);
|
|
50
|
+
if (!requested)
|
|
51
|
+
return undefined;
|
|
52
|
+
return config.aliases[requested] ?? requested;
|
|
53
|
+
}
|
|
54
|
+
export function aliasForContextId(config, contextId) {
|
|
55
|
+
for (const [alias, id] of Object.entries(config.aliases)) {
|
|
56
|
+
if (id === contextId)
|
|
57
|
+
return alias;
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
export function renameProfile(contextId, alias) {
|
|
62
|
+
const normalizedContextId = normalizeContextId(contextId);
|
|
63
|
+
const normalizedAlias = normalizeContextId(alias);
|
|
64
|
+
if (!normalizedContextId)
|
|
65
|
+
throw new Error('profile contextId is required');
|
|
66
|
+
if (!normalizedAlias)
|
|
67
|
+
throw new Error('profile alias is required');
|
|
68
|
+
const config = loadProfileConfig();
|
|
69
|
+
for (const [existingAlias, existingContextId] of Object.entries(config.aliases)) {
|
|
70
|
+
if (existingAlias !== normalizedAlias && existingContextId === normalizedContextId) {
|
|
71
|
+
delete config.aliases[existingAlias];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
config.aliases[normalizedAlias] = normalizedContextId;
|
|
75
|
+
saveProfileConfig(config);
|
|
76
|
+
return config;
|
|
77
|
+
}
|
|
78
|
+
export function setDefaultProfile(profile) {
|
|
79
|
+
const contextId = resolveProfileContextId(profile) ?? normalizeContextId(profile);
|
|
80
|
+
if (!contextId)
|
|
81
|
+
throw new Error('profile is required');
|
|
82
|
+
const config = loadProfileConfig();
|
|
83
|
+
config.defaultContextId = contextId;
|
|
84
|
+
saveProfileConfig(config);
|
|
85
|
+
return config;
|
|
86
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export type BrowserRuntimeAction = 'exec' | 'navigate' | 'tabs' | 'cookies' | 'screenshot' | 'close-window' | 'set-file-input' | 'insert-text' | 'bind' | 'network-capture-start' | 'network-capture-read' | 'wait-download' | 'cdp' | 'frames';
|
|
2
|
+
export type BrowserSurface = 'browser' | 'adapter';
|
|
3
|
+
export type SiteSessionMode = 'ephemeral' | 'persistent';
|
|
4
|
+
export type BrowserWindowMode = 'foreground' | 'background';
|
|
5
|
+
export interface BrowserRuntimeCommand {
|
|
6
|
+
id: string;
|
|
7
|
+
action: BrowserRuntimeAction;
|
|
8
|
+
page?: string;
|
|
9
|
+
code?: string;
|
|
10
|
+
session?: string;
|
|
11
|
+
surface?: BrowserSurface;
|
|
12
|
+
siteSession?: SiteSessionMode;
|
|
13
|
+
url?: string;
|
|
14
|
+
op?: string;
|
|
15
|
+
index?: number;
|
|
16
|
+
domain?: string;
|
|
17
|
+
format?: 'png' | 'jpeg';
|
|
18
|
+
quality?: number;
|
|
19
|
+
fullPage?: boolean;
|
|
20
|
+
width?: number;
|
|
21
|
+
height?: number;
|
|
22
|
+
files?: string[];
|
|
23
|
+
selector?: string;
|
|
24
|
+
text?: string;
|
|
25
|
+
pattern?: string;
|
|
26
|
+
timeoutMs?: number;
|
|
27
|
+
/** Daemon command timeout in seconds. Preserves the existing daemon protocol field. */
|
|
28
|
+
timeout?: number;
|
|
29
|
+
cdpMethod?: string;
|
|
30
|
+
cdpParams?: Record<string, unknown>;
|
|
31
|
+
windowMode?: BrowserWindowMode;
|
|
32
|
+
idleTimeout?: number;
|
|
33
|
+
frameIndex?: number;
|
|
34
|
+
contextId?: string;
|
|
35
|
+
profileId?: string;
|
|
36
|
+
}
|
|
37
|
+
export interface BrowserRuntimeResult {
|
|
38
|
+
id: string;
|
|
39
|
+
ok: boolean;
|
|
40
|
+
data?: unknown;
|
|
41
|
+
error?: string;
|
|
42
|
+
errorCode?: string;
|
|
43
|
+
errorHint?: string;
|
|
44
|
+
page?: string;
|
|
45
|
+
}
|
|
46
|
+
export interface BrowserRuntimeProfileStatus {
|
|
47
|
+
contextId: string;
|
|
48
|
+
runtimeConnected: boolean;
|
|
49
|
+
runtimeVersion?: string;
|
|
50
|
+
pending: number;
|
|
51
|
+
lastSeenAt?: number;
|
|
52
|
+
}
|
|
53
|
+
export interface BrowserRuntimeStatus {
|
|
54
|
+
runtimeConnected: boolean;
|
|
55
|
+
runtimeName: string;
|
|
56
|
+
runtimeVersion?: string;
|
|
57
|
+
contextId?: string;
|
|
58
|
+
profileRequired?: boolean;
|
|
59
|
+
profileDisconnected?: boolean;
|
|
60
|
+
profiles: BrowserRuntimeProfileStatus[];
|
|
61
|
+
pending: number;
|
|
62
|
+
commandResultUnknown?: number;
|
|
63
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { BrowserRuntimeCommand, BrowserRuntimeResult } from '../../protocol.js';
|
|
2
|
+
import type { CloakSessionManager } from './session-manager.js';
|
|
3
|
+
export declare function dispatchCloakAction(manager: CloakSessionManager, command: BrowserRuntimeCommand): Promise<BrowserRuntimeResult>;
|