@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,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Electron app registry — maps site names to launch metadata.
|
|
3
|
+
*
|
|
4
|
+
* Builtin apps are defined here. User-defined apps are loaded
|
|
5
|
+
* from ~/.webcmd/apps.yaml (additive only, does not override builtins).
|
|
6
|
+
*/
|
|
7
|
+
export interface ElectronAppEntry {
|
|
8
|
+
/** CDP debug port (unique per app) */
|
|
9
|
+
port: number;
|
|
10
|
+
/** macOS process name for detection via pgrep */
|
|
11
|
+
processName: string;
|
|
12
|
+
/** Candidate executable names inside Contents/MacOS/, tried in order */
|
|
13
|
+
executableNames?: string[];
|
|
14
|
+
/** macOS bundle ID for path discovery */
|
|
15
|
+
bundleId?: string;
|
|
16
|
+
/** Human-readable name for prompts */
|
|
17
|
+
displayName?: string;
|
|
18
|
+
/** Additional launch args beyond --remote-debugging-port */
|
|
19
|
+
extraArgs?: string[];
|
|
20
|
+
}
|
|
21
|
+
export declare const builtinApps: Record<string, ElectronAppEntry>;
|
|
22
|
+
/** Merge builtin + user-defined apps. User entries are additive only. */
|
|
23
|
+
export declare function loadApps(userApps?: Record<string, Omit<ElectronAppEntry, 'displayName'> & {
|
|
24
|
+
displayName?: string;
|
|
25
|
+
}>): Record<string, ElectronAppEntry>;
|
|
26
|
+
export declare function getElectronApp(site: string): ElectronAppEntry | undefined;
|
|
27
|
+
export declare function isElectronApp(site: string): boolean;
|
|
28
|
+
/** Get all registered apps (builtin + user-defined). */
|
|
29
|
+
export declare function getAllElectronApps(): Record<string, ElectronAppEntry>;
|
|
30
|
+
/** Reset loaded apps (for testing). */
|
|
31
|
+
export declare function _resetRegistry(): void;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Electron app registry — maps site names to launch metadata.
|
|
3
|
+
*
|
|
4
|
+
* Builtin apps are defined here. User-defined apps are loaded
|
|
5
|
+
* from ~/.webcmd/apps.yaml (additive only, does not override builtins).
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'node:fs';
|
|
8
|
+
import * as path from 'node:path';
|
|
9
|
+
import * as os from 'node:os';
|
|
10
|
+
import yaml from 'js-yaml';
|
|
11
|
+
export const builtinApps = {
|
|
12
|
+
cursor: { port: 9226, processName: 'Cursor', bundleId: 'com.todesktop.runtime.Cursor', displayName: 'Cursor' },
|
|
13
|
+
codex: { port: 9238, processName: 'Codex', bundleId: 'com.openai.codex', displayName: 'Codex' },
|
|
14
|
+
chatwise: { port: 9228, processName: 'ChatWise', bundleId: 'com.chatwise.app', displayName: 'ChatWise' },
|
|
15
|
+
'discord-app': { port: 9232, processName: 'Discord', bundleId: 'com.discord.app', displayName: 'Discord' },
|
|
16
|
+
'doubao-app': { port: 9225, processName: 'Doubao', bundleId: 'com.volcengine.doubao', displayName: 'Doubao' },
|
|
17
|
+
antigravity: {
|
|
18
|
+
port: 9234,
|
|
19
|
+
processName: 'Antigravity',
|
|
20
|
+
executableNames: ['Electron', 'Antigravity'],
|
|
21
|
+
bundleId: 'dev.antigravity.app',
|
|
22
|
+
displayName: 'Antigravity',
|
|
23
|
+
},
|
|
24
|
+
'chatgpt-app': { port: 9236, processName: 'ChatGPT', bundleId: 'com.openai.chat', displayName: 'ChatGPT' },
|
|
25
|
+
qoder: {
|
|
26
|
+
port: 9237,
|
|
27
|
+
processName: 'Qoder',
|
|
28
|
+
executableNames: ['Electron'],
|
|
29
|
+
bundleId: 'com.qoder.ide',
|
|
30
|
+
displayName: 'Qoder',
|
|
31
|
+
},
|
|
32
|
+
'trae-solo': {
|
|
33
|
+
port: 9235,
|
|
34
|
+
processName: 'TRAE SOLO',
|
|
35
|
+
executableNames: ['Electron', 'TRAE SOLO'],
|
|
36
|
+
bundleId: 'com.trae.solo.app',
|
|
37
|
+
displayName: 'Trae SOLO',
|
|
38
|
+
},
|
|
39
|
+
'trae-cn': {
|
|
40
|
+
port: 39240,
|
|
41
|
+
processName: 'Trae CN',
|
|
42
|
+
executableNames: ['Electron'],
|
|
43
|
+
bundleId: 'cn.trae.app',
|
|
44
|
+
displayName: 'Trae CN',
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
/** Merge builtin + user-defined apps. User entries are additive only. */
|
|
48
|
+
export function loadApps(userApps) {
|
|
49
|
+
const merged = { ...builtinApps };
|
|
50
|
+
if (userApps) {
|
|
51
|
+
for (const [name, entry] of Object.entries(userApps)) {
|
|
52
|
+
if (!(name in merged)) {
|
|
53
|
+
merged[name] = entry;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return merged;
|
|
58
|
+
}
|
|
59
|
+
let _apps = null;
|
|
60
|
+
function ensureLoaded() {
|
|
61
|
+
if (_apps)
|
|
62
|
+
return _apps;
|
|
63
|
+
let userApps;
|
|
64
|
+
try {
|
|
65
|
+
const yamlPath = path.join(os.homedir(), '.webcmd', 'apps.yaml');
|
|
66
|
+
if (fs.existsSync(yamlPath)) {
|
|
67
|
+
const content = fs.readFileSync(yamlPath, 'utf-8');
|
|
68
|
+
const parsed = yaml.load(content);
|
|
69
|
+
userApps = parsed?.apps;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// Silently ignore malformed user config
|
|
74
|
+
}
|
|
75
|
+
_apps = loadApps(userApps);
|
|
76
|
+
return _apps;
|
|
77
|
+
}
|
|
78
|
+
export function getElectronApp(site) {
|
|
79
|
+
return ensureLoaded()[site];
|
|
80
|
+
}
|
|
81
|
+
export function isElectronApp(site) {
|
|
82
|
+
return site in ensureLoaded();
|
|
83
|
+
}
|
|
84
|
+
/** Get all registered apps (builtin + user-defined). */
|
|
85
|
+
export function getAllElectronApps() {
|
|
86
|
+
return ensureLoaded();
|
|
87
|
+
}
|
|
88
|
+
/** Reset loaded apps (for testing). */
|
|
89
|
+
export function _resetRegistry() {
|
|
90
|
+
_apps = null;
|
|
91
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { builtinApps, getElectronApp, isElectronApp, loadApps } from './electron-apps.js';
|
|
3
|
+
describe('electron-apps registry', () => {
|
|
4
|
+
it('returns builtin app entry for cursor', () => {
|
|
5
|
+
const app = getElectronApp('cursor');
|
|
6
|
+
expect(app).toBeDefined();
|
|
7
|
+
expect(app.port).toBe(9226);
|
|
8
|
+
expect(app.processName).toBe('Cursor');
|
|
9
|
+
});
|
|
10
|
+
it('returns builtin app entry for codex', () => {
|
|
11
|
+
const app = getElectronApp('codex');
|
|
12
|
+
expect(app).toBeDefined();
|
|
13
|
+
expect(app.port).toBe(9238);
|
|
14
|
+
});
|
|
15
|
+
it('keeps builtin Electron app CDP ports unique and off the browser-bridge port', () => {
|
|
16
|
+
const ports = Object.values(builtinApps).map((app) => app.port);
|
|
17
|
+
expect(new Set(ports).size).toBe(ports.length);
|
|
18
|
+
expect(ports).not.toContain(9222);
|
|
19
|
+
});
|
|
20
|
+
it('returns builtin app entry for Trae CN', () => {
|
|
21
|
+
const app = getElectronApp('trae-cn');
|
|
22
|
+
expect(app).toBeDefined();
|
|
23
|
+
expect(app.port).toBe(39240);
|
|
24
|
+
expect(app.processName).toBe('Trae CN');
|
|
25
|
+
expect(app.bundleId).toBe('cn.trae.app');
|
|
26
|
+
expect(app.executableNames).toEqual(['Electron']);
|
|
27
|
+
});
|
|
28
|
+
it('returns undefined for non-Electron sites', () => {
|
|
29
|
+
expect(getElectronApp('bilibili')).toBeUndefined();
|
|
30
|
+
expect(getElectronApp('hackernews')).toBeUndefined();
|
|
31
|
+
});
|
|
32
|
+
it('isElectronApp returns true for registered apps', () => {
|
|
33
|
+
expect(isElectronApp('cursor')).toBe(true);
|
|
34
|
+
expect(isElectronApp('codex')).toBe(true);
|
|
35
|
+
expect(isElectronApp('chatwise')).toBe(true);
|
|
36
|
+
expect(isElectronApp('qoder')).toBe(true);
|
|
37
|
+
expect(isElectronApp('trae-solo')).toBe(true);
|
|
38
|
+
expect(isElectronApp('trae-cn')).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
it('registers Qoder on its own CDP port', () => {
|
|
41
|
+
const app = getElectronApp('qoder');
|
|
42
|
+
expect(app).toMatchObject({
|
|
43
|
+
port: 9237,
|
|
44
|
+
processName: 'Qoder',
|
|
45
|
+
bundleId: 'com.qoder.ide',
|
|
46
|
+
displayName: 'Qoder',
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
it('registers Trae SOLO with its own CDP port and process metadata', () => {
|
|
50
|
+
const app = getElectronApp('trae-solo');
|
|
51
|
+
expect(app).toBeDefined();
|
|
52
|
+
expect(app.port).toBe(9235);
|
|
53
|
+
expect(app.processName).toBe('TRAE SOLO');
|
|
54
|
+
expect(app.bundleId).toBe('com.trae.solo.app');
|
|
55
|
+
});
|
|
56
|
+
it('isElectronApp returns false for non-Electron sites', () => {
|
|
57
|
+
expect(isElectronApp('bilibili')).toBe(false);
|
|
58
|
+
expect(isElectronApp('notion')).toBe(false);
|
|
59
|
+
expect(isElectronApp('unknown-app')).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
it('loadApps merges user config additively', () => {
|
|
62
|
+
const apps = loadApps({
|
|
63
|
+
myapp: { port: 9234, processName: 'MyApp' },
|
|
64
|
+
});
|
|
65
|
+
expect(apps.myapp).toBeDefined();
|
|
66
|
+
expect(apps.myapp.port).toBe(9234);
|
|
67
|
+
// Builtins still present
|
|
68
|
+
expect(apps.cursor).toBeDefined();
|
|
69
|
+
});
|
|
70
|
+
it('loadApps does not override builtin entries', () => {
|
|
71
|
+
const apps = loadApps({
|
|
72
|
+
cursor: { port: 9999, processName: 'FakeCursor' },
|
|
73
|
+
});
|
|
74
|
+
expect(apps.cursor.port).toBe(9226); // Builtin wins
|
|
75
|
+
});
|
|
76
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { discoverClis, discoverPlugins, ensureUserCliCompatShims, ensureUserAdapters, PLUGINS_DIR } from './discovery.js';
|
|
3
|
+
import { executeCommand } from './execution.js';
|
|
4
|
+
import { getRegistry, cli, Strategy } from './registry.js';
|
|
5
|
+
import { clearAllHooks, onAfterExecute } from './hooks.js';
|
|
6
|
+
import * as fs from 'node:fs';
|
|
7
|
+
import * as os from 'node:os';
|
|
8
|
+
import * as path from 'node:path';
|
|
9
|
+
import { pathToFileURL } from 'node:url';
|
|
10
|
+
describe('discoverClis', () => {
|
|
11
|
+
it('handles non-existent directories gracefully', async () => {
|
|
12
|
+
// Should not throw for missing directories
|
|
13
|
+
await expect(discoverClis(path.join(os.tmpdir(), 'nonexistent-webcmd-test-dir'))).resolves.not.toThrow();
|
|
14
|
+
});
|
|
15
|
+
it('imports only CLI command modules during filesystem discovery', async () => {
|
|
16
|
+
const tempRoot = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'webcmd-discovery-'));
|
|
17
|
+
const siteDir = path.join(tempRoot, 'temp-site');
|
|
18
|
+
const helperPath = path.join(siteDir, 'helper.js');
|
|
19
|
+
const commandPath = path.join(siteDir, 'hello.js');
|
|
20
|
+
try {
|
|
21
|
+
await fs.promises.mkdir(siteDir, { recursive: true });
|
|
22
|
+
await fs.promises.writeFile(helperPath, `
|
|
23
|
+
globalThis.__webcmd_helper_loaded__ = true;
|
|
24
|
+
export const helper = true;
|
|
25
|
+
`);
|
|
26
|
+
await fs.promises.writeFile(commandPath, `
|
|
27
|
+
import { cli, Strategy } from '${pathToFileURL(path.join(process.cwd(), 'src', 'registry.ts')).href}';
|
|
28
|
+
cli({
|
|
29
|
+
site: 'temp-site',
|
|
30
|
+
name: 'hello', access: 'read',
|
|
31
|
+
description: 'hello command',
|
|
32
|
+
strategy: Strategy.PUBLIC,
|
|
33
|
+
browser: false,
|
|
34
|
+
func: async () => [{ ok: true }],
|
|
35
|
+
});
|
|
36
|
+
`);
|
|
37
|
+
delete globalThis.__webcmd_helper_loaded__;
|
|
38
|
+
await discoverClis(tempRoot);
|
|
39
|
+
expect(globalThis.__webcmd_helper_loaded__).toBeUndefined();
|
|
40
|
+
expect(getRegistry().get('temp-site/hello')).toBeDefined();
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
delete globalThis.__webcmd_helper_loaded__;
|
|
44
|
+
await fs.promises.rm(tempRoot, { recursive: true, force: true });
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
it('falls back to filesystem discovery when the manifest is invalid', async () => {
|
|
48
|
+
const tempBuildRoot = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'webcmd-manifest-fallback-'));
|
|
49
|
+
const distDir = path.join(tempBuildRoot, 'dist');
|
|
50
|
+
const siteDir = path.join(distDir, 'fallback-site');
|
|
51
|
+
const commandPath = path.join(siteDir, 'hello.js');
|
|
52
|
+
const manifestPath = path.join(tempBuildRoot, 'cli-manifest.json');
|
|
53
|
+
try {
|
|
54
|
+
await fs.promises.mkdir(siteDir, { recursive: true });
|
|
55
|
+
await fs.promises.writeFile(manifestPath, '{ invalid json');
|
|
56
|
+
await fs.promises.writeFile(commandPath, `
|
|
57
|
+
import { cli, Strategy } from '${pathToFileURL(path.join(process.cwd(), 'src', 'registry.ts')).href}';
|
|
58
|
+
cli({
|
|
59
|
+
site: 'fallback-site',
|
|
60
|
+
name: 'hello', access: 'read',
|
|
61
|
+
description: 'hello command',
|
|
62
|
+
strategy: Strategy.PUBLIC,
|
|
63
|
+
browser: false,
|
|
64
|
+
func: async () => [{ ok: true }],
|
|
65
|
+
});
|
|
66
|
+
`);
|
|
67
|
+
await discoverClis(distDir);
|
|
68
|
+
expect(getRegistry().get('fallback-site/hello')).toBeDefined();
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
await fs.promises.rm(tempBuildRoot, { recursive: true, force: true });
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
it('loads user CLI modules via package exports symlink', async () => {
|
|
75
|
+
const tempWebcmdRoot = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'webcmd-user-clis-'));
|
|
76
|
+
const userClisDir = path.join(tempWebcmdRoot, 'clis');
|
|
77
|
+
const siteDir = path.join(userClisDir, 'legacy-site');
|
|
78
|
+
const commandPath = path.join(siteDir, 'hello.js');
|
|
79
|
+
try {
|
|
80
|
+
await ensureUserCliCompatShims(tempWebcmdRoot);
|
|
81
|
+
await fs.promises.mkdir(siteDir, { recursive: true });
|
|
82
|
+
await fs.promises.writeFile(commandPath, `
|
|
83
|
+
import { cli, Strategy } from '@agentrhq/webcmd/registry';
|
|
84
|
+
import { CommandExecutionError } from '@agentrhq/webcmd/errors';
|
|
85
|
+
import { htmlToMarkdown } from '@agentrhq/webcmd/utils';
|
|
86
|
+
|
|
87
|
+
cli({
|
|
88
|
+
site: 'legacy-site',
|
|
89
|
+
name: 'hello', access: 'read',
|
|
90
|
+
description: 'hello command',
|
|
91
|
+
strategy: Strategy.PUBLIC,
|
|
92
|
+
browser: false,
|
|
93
|
+
func: async () => [{ ok: true, errorName: new CommandExecutionError('boom').name, markdown: htmlToMarkdown('<p>hello</p>') }],
|
|
94
|
+
});
|
|
95
|
+
`);
|
|
96
|
+
await discoverClis(userClisDir);
|
|
97
|
+
const cmd = getRegistry().get('legacy-site/hello');
|
|
98
|
+
expect(cmd).toBeDefined();
|
|
99
|
+
await expect(executeCommand(cmd, {})).resolves.toEqual([{ ok: true, errorName: 'CommandExecutionError', markdown: 'hello' }]);
|
|
100
|
+
}
|
|
101
|
+
finally {
|
|
102
|
+
await fs.promises.rm(tempWebcmdRoot, { recursive: true, force: true });
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
describe('ensureUserAdapters', () => {
|
|
107
|
+
it('creates user clis directory without triggering full copy', async () => {
|
|
108
|
+
const tempDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'webcmd-ensure-'));
|
|
109
|
+
const clisDir = path.join(tempDir, 'clis');
|
|
110
|
+
try {
|
|
111
|
+
// Patch USER_CLIS_DIR is not easy, so we test the function behavior indirectly:
|
|
112
|
+
// ensureUserAdapters should not throw and should be very fast (no fetch script)
|
|
113
|
+
const start = Date.now();
|
|
114
|
+
await ensureUserAdapters();
|
|
115
|
+
const elapsed = Date.now() - start;
|
|
116
|
+
// Should complete quickly (< 1s) since it only creates a directory
|
|
117
|
+
expect(elapsed).toBeLessThan(1000);
|
|
118
|
+
}
|
|
119
|
+
finally {
|
|
120
|
+
await fs.promises.rm(tempDir, { recursive: true, force: true });
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
it('discoverClis handles empty user directory gracefully', async () => {
|
|
124
|
+
const emptyDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'webcmd-empty-'));
|
|
125
|
+
try {
|
|
126
|
+
// Should not throw for an empty directory (no adapters to discover)
|
|
127
|
+
await expect(discoverClis(emptyDir)).resolves.not.toThrow();
|
|
128
|
+
}
|
|
129
|
+
finally {
|
|
130
|
+
await fs.promises.rm(emptyDir, { recursive: true, force: true });
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
describe('discoverPlugins', () => {
|
|
135
|
+
const testPluginDir = path.join(PLUGINS_DIR, '__test-plugin__');
|
|
136
|
+
const yamlPath = path.join(testPluginDir, 'greeting.yaml');
|
|
137
|
+
const symlinkTargetDir = path.join(os.tmpdir(), '__test-plugin-symlink-target__');
|
|
138
|
+
const symlinkPluginDir = path.join(PLUGINS_DIR, '__test-plugin-symlink__');
|
|
139
|
+
const brokenSymlinkDir = path.join(PLUGINS_DIR, '__test-plugin-broken__');
|
|
140
|
+
const dirSymlinkType = process.platform === 'win32' ? 'junction' : 'dir';
|
|
141
|
+
afterEach(async () => {
|
|
142
|
+
try {
|
|
143
|
+
await fs.promises.rm(testPluginDir, { recursive: true });
|
|
144
|
+
}
|
|
145
|
+
catch { }
|
|
146
|
+
try {
|
|
147
|
+
await fs.promises.rm(symlinkPluginDir, { recursive: true, force: true });
|
|
148
|
+
}
|
|
149
|
+
catch { }
|
|
150
|
+
try {
|
|
151
|
+
await fs.promises.rm(symlinkTargetDir, { recursive: true, force: true });
|
|
152
|
+
}
|
|
153
|
+
catch { }
|
|
154
|
+
try {
|
|
155
|
+
await fs.promises.rm(brokenSymlinkDir, { recursive: true, force: true });
|
|
156
|
+
}
|
|
157
|
+
catch { }
|
|
158
|
+
});
|
|
159
|
+
it('ignores YAML files in plugin directories (YAML format removed)', async () => {
|
|
160
|
+
await fs.promises.mkdir(testPluginDir, { recursive: true });
|
|
161
|
+
await fs.promises.writeFile(yamlPath, `
|
|
162
|
+
site: __test-plugin__
|
|
163
|
+
name: greeting
|
|
164
|
+
description: Test plugin greeting
|
|
165
|
+
strategy: public
|
|
166
|
+
browser: false
|
|
167
|
+
`);
|
|
168
|
+
await discoverPlugins();
|
|
169
|
+
const registry = getRegistry();
|
|
170
|
+
const cmd = registry.get('__test-plugin__/greeting');
|
|
171
|
+
expect(cmd).toBeUndefined();
|
|
172
|
+
});
|
|
173
|
+
it('handles non-existent plugins directory gracefully', async () => {
|
|
174
|
+
// discoverPlugins should not throw if ~/.webcmd/plugins/ does not exist
|
|
175
|
+
await expect(discoverPlugins()).resolves.not.toThrow();
|
|
176
|
+
});
|
|
177
|
+
it('ignores YAML files in symlinked plugin directories (YAML format removed)', async () => {
|
|
178
|
+
await fs.promises.mkdir(PLUGINS_DIR, { recursive: true });
|
|
179
|
+
await fs.promises.mkdir(symlinkTargetDir, { recursive: true });
|
|
180
|
+
await fs.promises.writeFile(path.join(symlinkTargetDir, 'hello.yaml'), `
|
|
181
|
+
site: __test-plugin-symlink__
|
|
182
|
+
name: hello
|
|
183
|
+
description: Test plugin greeting via symlink
|
|
184
|
+
strategy: public
|
|
185
|
+
browser: false
|
|
186
|
+
`);
|
|
187
|
+
await fs.promises.symlink(symlinkTargetDir, symlinkPluginDir, dirSymlinkType);
|
|
188
|
+
await discoverPlugins();
|
|
189
|
+
const cmd = getRegistry().get('__test-plugin-symlink__/hello');
|
|
190
|
+
expect(cmd).toBeUndefined();
|
|
191
|
+
});
|
|
192
|
+
it('skips broken plugin symlinks without throwing', async () => {
|
|
193
|
+
await fs.promises.mkdir(PLUGINS_DIR, { recursive: true });
|
|
194
|
+
await fs.promises.symlink(path.join(os.tmpdir(), '__missing-plugin-target__'), brokenSymlinkDir, dirSymlinkType);
|
|
195
|
+
await expect(discoverPlugins()).resolves.not.toThrow();
|
|
196
|
+
expect(getRegistry().get('__test-plugin-broken__/hello')).toBeUndefined();
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
describe('executeCommand', () => {
|
|
200
|
+
beforeEach(() => {
|
|
201
|
+
clearAllHooks();
|
|
202
|
+
vi.unstubAllEnvs();
|
|
203
|
+
});
|
|
204
|
+
it('accepts kebab-case option names after Commander camelCases them', async () => {
|
|
205
|
+
const cmd = cli({
|
|
206
|
+
site: 'test-engine',
|
|
207
|
+
name: 'kebab-arg-test', access: 'read',
|
|
208
|
+
description: 'test command with kebab-case arg',
|
|
209
|
+
browser: false,
|
|
210
|
+
strategy: Strategy.PUBLIC,
|
|
211
|
+
args: [
|
|
212
|
+
{ name: 'note-id', required: true, help: 'Note ID' },
|
|
213
|
+
],
|
|
214
|
+
func: async (kwargs) => [{ noteId: kwargs['note-id'] }],
|
|
215
|
+
});
|
|
216
|
+
const result = await executeCommand(cmd, { 'note-id': 'abc123' });
|
|
217
|
+
expect(result).toEqual([{ noteId: 'abc123' }]);
|
|
218
|
+
});
|
|
219
|
+
it('executes a command with func', async () => {
|
|
220
|
+
const cmd = cli({
|
|
221
|
+
site: 'test-engine',
|
|
222
|
+
name: 'func-test', access: 'read',
|
|
223
|
+
description: 'test command with func',
|
|
224
|
+
browser: false,
|
|
225
|
+
strategy: Strategy.PUBLIC,
|
|
226
|
+
func: async (kwargs) => {
|
|
227
|
+
return [{ title: kwargs.query ?? 'default' }];
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
const result = await executeCommand(cmd, { query: 'hello' });
|
|
231
|
+
expect(result).toEqual([{ title: 'hello' }]);
|
|
232
|
+
});
|
|
233
|
+
it('executes a command with pipeline', async () => {
|
|
234
|
+
const cmd = cli({
|
|
235
|
+
site: 'test-engine',
|
|
236
|
+
name: 'pipe-test', access: 'read',
|
|
237
|
+
description: 'test command with pipeline',
|
|
238
|
+
browser: false,
|
|
239
|
+
strategy: Strategy.PUBLIC,
|
|
240
|
+
pipeline: [
|
|
241
|
+
{ evaluate: '() => [{ n: 1 }, { n: 2 }, { n: 3 }]' },
|
|
242
|
+
{ limit: '2' },
|
|
243
|
+
],
|
|
244
|
+
});
|
|
245
|
+
// Pipeline commands require page for evaluate step, so we'll test the error path
|
|
246
|
+
await expect(executeCommand(cmd, {})).rejects.toThrow();
|
|
247
|
+
});
|
|
248
|
+
it('throws for command with no func or pipeline', async () => {
|
|
249
|
+
const cmd = cli({
|
|
250
|
+
site: 'test-engine',
|
|
251
|
+
name: 'empty-test', access: 'read',
|
|
252
|
+
description: 'empty command',
|
|
253
|
+
browser: false,
|
|
254
|
+
});
|
|
255
|
+
await expect(executeCommand(cmd, {})).rejects.toThrow('has no func or pipeline');
|
|
256
|
+
});
|
|
257
|
+
it('passes debug flag to func', async () => {
|
|
258
|
+
let receivedDebug = false;
|
|
259
|
+
const cmd = cli({
|
|
260
|
+
site: 'test-engine',
|
|
261
|
+
name: 'debug-test', access: 'read',
|
|
262
|
+
description: 'debug test',
|
|
263
|
+
browser: false,
|
|
264
|
+
func: async (_kwargs, debug) => {
|
|
265
|
+
receivedDebug = debug ?? false;
|
|
266
|
+
return [];
|
|
267
|
+
},
|
|
268
|
+
});
|
|
269
|
+
await executeCommand(cmd, {}, true);
|
|
270
|
+
expect(receivedDebug).toBe(true);
|
|
271
|
+
});
|
|
272
|
+
it('fires onAfterExecute even when command execution throws', async () => {
|
|
273
|
+
const seen = [];
|
|
274
|
+
onAfterExecute((ctx) => {
|
|
275
|
+
seen.push({ error: ctx.error, finishedAt: ctx.finishedAt });
|
|
276
|
+
});
|
|
277
|
+
const cmd = cli({
|
|
278
|
+
site: 'test-engine',
|
|
279
|
+
name: 'failing-test', access: 'read',
|
|
280
|
+
description: 'failing command',
|
|
281
|
+
browser: false,
|
|
282
|
+
strategy: Strategy.PUBLIC,
|
|
283
|
+
func: async () => {
|
|
284
|
+
throw new Error('boom');
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
await expect(executeCommand(cmd, {})).rejects.toThrow('boom');
|
|
288
|
+
expect(seen).toHaveLength(1);
|
|
289
|
+
expect(seen[0].error).toBeInstanceOf(Error);
|
|
290
|
+
expect(seen[0].error.message).toBe('boom');
|
|
291
|
+
expect(typeof seen[0].finishedAt).toBe('number');
|
|
292
|
+
});
|
|
293
|
+
it('uses launcher for registered Electron apps (chatwise)', async () => {
|
|
294
|
+
// Mock the launcher to return a fake endpoint (avoids real HTTP/process calls)
|
|
295
|
+
const launcher = await import('./launcher.js');
|
|
296
|
+
const spy = vi.spyOn(launcher, 'resolveElectronEndpoint')
|
|
297
|
+
.mockResolvedValue('http://127.0.0.1:9228');
|
|
298
|
+
const cmd = cli({
|
|
299
|
+
site: 'chatwise',
|
|
300
|
+
name: 'status', access: 'read',
|
|
301
|
+
description: 'chatwise status',
|
|
302
|
+
browser: true,
|
|
303
|
+
strategy: Strategy.PUBLIC,
|
|
304
|
+
func: async () => [{ ok: true }],
|
|
305
|
+
});
|
|
306
|
+
// CDPBridge.connect() will fail (no actual CDP server), but the launcher
|
|
307
|
+
// should have been called with 'chatwise'.
|
|
308
|
+
await expect(executeCommand(cmd, {})).rejects.toThrow();
|
|
309
|
+
expect(spy).toHaveBeenCalledWith('chatwise');
|
|
310
|
+
spy.mockRestore();
|
|
311
|
+
});
|
|
312
|
+
});
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified error types for webcmd.
|
|
3
|
+
*
|
|
4
|
+
* All errors thrown by the framework should extend CliError so that
|
|
5
|
+
* the top-level handler in commanderAdapter.ts can render consistent,
|
|
6
|
+
* helpful output with emoji-coded severity and actionable hints.
|
|
7
|
+
*
|
|
8
|
+
* ## Exit codes
|
|
9
|
+
*
|
|
10
|
+
* webcmd follows Unix conventions (sysexits.h) for process exit codes:
|
|
11
|
+
*
|
|
12
|
+
* 0 Success
|
|
13
|
+
* 1 Generic / unexpected error
|
|
14
|
+
* 2 Argument / usage error (ArgumentError)
|
|
15
|
+
* 66 No input / empty result (EmptyResultError)
|
|
16
|
+
* 69 Service unavailable (BrowserConnectError, adapter load failures)
|
|
17
|
+
* 75 Temporary failure, retry later (TimeoutError) EX_TEMPFAIL
|
|
18
|
+
* 77 Permission denied / auth needed (AuthRequiredError)
|
|
19
|
+
* 78 Configuration error (ConfigError)
|
|
20
|
+
* 130 Interrupted by Ctrl-C (set by tui.ts SIGINT handler)
|
|
21
|
+
*/
|
|
22
|
+
import type { ObservationTraceReceipt } from './observation/events.js';
|
|
23
|
+
export declare const EXIT_CODES: {
|
|
24
|
+
readonly SUCCESS: 0;
|
|
25
|
+
readonly GENERIC_ERROR: 1;
|
|
26
|
+
readonly USAGE_ERROR: 2;
|
|
27
|
+
readonly EMPTY_RESULT: 66;
|
|
28
|
+
readonly SERVICE_UNAVAIL: 69;
|
|
29
|
+
readonly TEMPFAIL: 75;
|
|
30
|
+
readonly NOPERM: 77;
|
|
31
|
+
readonly CONFIG_ERROR: 78;
|
|
32
|
+
readonly INTERRUPTED: 130;
|
|
33
|
+
};
|
|
34
|
+
export type ExitCode = typeof EXIT_CODES[keyof typeof EXIT_CODES];
|
|
35
|
+
export declare class CliError extends Error {
|
|
36
|
+
/** Machine-readable error code (e.g. 'BROWSER_CONNECT', 'AUTH_REQUIRED') */
|
|
37
|
+
readonly code: string;
|
|
38
|
+
/** Human-readable hint on how to fix the problem */
|
|
39
|
+
readonly hint?: string;
|
|
40
|
+
/** Unix process exit code — defaults to 1 (generic error) */
|
|
41
|
+
readonly exitCode: ExitCode;
|
|
42
|
+
constructor(code: string, message: string, hint?: string, exitCode?: ExitCode);
|
|
43
|
+
}
|
|
44
|
+
export declare function attachTraceReceipt(err: unknown, receipt: ObservationTraceReceipt): void;
|
|
45
|
+
export declare function getTraceReceipt(err: unknown): ObservationTraceReceipt | undefined;
|
|
46
|
+
export type BrowserConnectKind = 'daemon-not-running' | 'runtime-not-ready' | 'extension-not-connected' | 'profile-required' | 'profile-disconnected' | 'command-failed' | 'unknown';
|
|
47
|
+
export declare class BrowserConnectError extends CliError {
|
|
48
|
+
readonly kind: BrowserConnectKind;
|
|
49
|
+
constructor(message: string, hint?: string, kind?: BrowserConnectKind);
|
|
50
|
+
}
|
|
51
|
+
export declare class CommandExecutionError extends CliError {
|
|
52
|
+
constructor(message: string, hint?: string);
|
|
53
|
+
}
|
|
54
|
+
export declare class ConfigError extends CliError {
|
|
55
|
+
constructor(message: string, hint?: string);
|
|
56
|
+
}
|
|
57
|
+
export declare class AuthRequiredError extends CliError {
|
|
58
|
+
readonly domain: string;
|
|
59
|
+
constructor(domain: string, message?: string);
|
|
60
|
+
}
|
|
61
|
+
export declare class TimeoutError extends CliError {
|
|
62
|
+
constructor(label: string, seconds: number, hint?: string);
|
|
63
|
+
}
|
|
64
|
+
export declare class ArgumentError extends CliError {
|
|
65
|
+
constructor(message: string, hint?: string);
|
|
66
|
+
}
|
|
67
|
+
export declare class EmptyResultError extends CliError {
|
|
68
|
+
constructor(command: string, hint?: string);
|
|
69
|
+
}
|
|
70
|
+
export declare function adapterLoadError(message: string, hint?: string): CliError;
|
|
71
|
+
export declare function selectorError(selector: string, hint?: string): CliError;
|
|
72
|
+
export declare class PluginError extends CliError {
|
|
73
|
+
constructor(message: string, hint?: string);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Thrown when a JSON endpoint returns HTML instead of JSON — typically a login
|
|
77
|
+
* wall, rate-limit page, or WAF challenge. Surfaced as a structured error so
|
|
78
|
+
* callers can show "re-login or wait out the rate limit" guidance instead of
|
|
79
|
+
* the cryptic `SyntaxError: Unexpected token '<', "<!DOCTYPE "...` that a naive
|
|
80
|
+
* JSON.parse on an HTML body produces.
|
|
81
|
+
*
|
|
82
|
+
* `bodyPreview` is the first 100 chars of the response body (after trimming
|
|
83
|
+
* leading whitespace) — useful for logs / debugging without dumping the full
|
|
84
|
+
* page.
|
|
85
|
+
*/
|
|
86
|
+
export declare class LoginWallError extends CliError {
|
|
87
|
+
readonly status: number;
|
|
88
|
+
readonly url: string;
|
|
89
|
+
readonly bodyPreview: string;
|
|
90
|
+
constructor(message: string, status: number, url: string, bodyPreview: string, hint?: string);
|
|
91
|
+
}
|
|
92
|
+
/** Structured error output — unified contract for all consumers (AI agents, scripts, humans). */
|
|
93
|
+
export interface ErrorEnvelope {
|
|
94
|
+
ok: false;
|
|
95
|
+
error: {
|
|
96
|
+
code: string;
|
|
97
|
+
message: string;
|
|
98
|
+
help?: string;
|
|
99
|
+
exitCode: number;
|
|
100
|
+
stack?: string;
|
|
101
|
+
cause?: string;
|
|
102
|
+
};
|
|
103
|
+
trace?: {
|
|
104
|
+
traceId: string;
|
|
105
|
+
dir: string;
|
|
106
|
+
summaryPath: string;
|
|
107
|
+
receiptPath: string;
|
|
108
|
+
status: ObservationTraceReceipt['status'];
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/** Extract a human-readable message from an unknown caught value. */
|
|
112
|
+
export declare function getErrorMessage(error: unknown): string;
|
|
113
|
+
/** Build an ErrorEnvelope from any caught value. */
|
|
114
|
+
export declare function toEnvelope(err: unknown): ErrorEnvelope;
|