@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,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Commander adapter: bridges Registry commands to Commander subcommands.
|
|
3
|
+
*
|
|
4
|
+
* This is a THIN adapter — it only handles:
|
|
5
|
+
* 1. Commander arg/option registration
|
|
6
|
+
* 2. Collecting kwargs from Commander's action args
|
|
7
|
+
* 3. Calling executeCommand (which handles browser sessions, validation, etc.)
|
|
8
|
+
* 4. Rendering output and errors
|
|
9
|
+
*
|
|
10
|
+
* All execution logic lives in execution.ts.
|
|
11
|
+
*/
|
|
12
|
+
import { Command } from 'commander';
|
|
13
|
+
import { type CliCommand } from './registry.js';
|
|
14
|
+
/**
|
|
15
|
+
* Register a single CliCommand as a Commander subcommand.
|
|
16
|
+
*/
|
|
17
|
+
export declare function registerCommandToProgram(siteCmd: Command, cmd: CliCommand): void;
|
|
18
|
+
/**
|
|
19
|
+
* Register all commands from the registry onto a Commander program.
|
|
20
|
+
*/
|
|
21
|
+
export declare function registerAllCommands(program: Command, siteGroups: Map<string, Command>): string[];
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Commander adapter: bridges Registry commands to Commander subcommands.
|
|
3
|
+
*
|
|
4
|
+
* This is a THIN adapter — it only handles:
|
|
5
|
+
* 1. Commander arg/option registration
|
|
6
|
+
* 2. Collecting kwargs from Commander's action args
|
|
7
|
+
* 3. Calling executeCommand (which handles browser sessions, validation, etc.)
|
|
8
|
+
* 4. Rendering output and errors
|
|
9
|
+
*
|
|
10
|
+
* All execution logic lives in execution.ts.
|
|
11
|
+
*/
|
|
12
|
+
import { log } from './logger.js';
|
|
13
|
+
import yaml from 'js-yaml';
|
|
14
|
+
import { fullName, getRegistry } from './registry.js';
|
|
15
|
+
import { render as renderOutput } from './output.js';
|
|
16
|
+
import { executeCommand, prepareCommandArgs } from './execution.js';
|
|
17
|
+
import { commandHelpData, formatCommandHelpText, formatCommandListTerm, formatSiteCommandDescription, formatSiteHelpText, getRequestedHelpFormat, renderStructuredHelp, siteHelpData, } from './help.js';
|
|
18
|
+
import { CliError, EXIT_CODES, toEnvelope, } from './errors.js';
|
|
19
|
+
/**
|
|
20
|
+
* Register a single CliCommand as a Commander subcommand.
|
|
21
|
+
*/
|
|
22
|
+
export function registerCommandToProgram(siteCmd, cmd) {
|
|
23
|
+
if (siteCmd.commands.some((c) => c.name() === cmd.name))
|
|
24
|
+
return;
|
|
25
|
+
const subCmd = siteCmd.command(cmd.name).description(formatSiteCommandDescription(cmd));
|
|
26
|
+
if (cmd.aliases?.length)
|
|
27
|
+
subCmd.aliases(cmd.aliases);
|
|
28
|
+
// Register positional args first, then named options
|
|
29
|
+
const positionalArgs = [];
|
|
30
|
+
for (const arg of cmd.args) {
|
|
31
|
+
if (arg.positional) {
|
|
32
|
+
const bracket = arg.required ? `<${arg.name}>` : `[${arg.name}]`;
|
|
33
|
+
subCmd.argument(bracket, arg.help ?? '');
|
|
34
|
+
positionalArgs.push(arg);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
const expectsValue = arg.required || arg.valueRequired;
|
|
38
|
+
const flag = expectsValue ? `--${arg.name} <value>` : `--${arg.name} [value]`;
|
|
39
|
+
if (arg.required)
|
|
40
|
+
subCmd.requiredOption(flag, arg.help ?? '');
|
|
41
|
+
else if (arg.default != null)
|
|
42
|
+
subCmd.option(flag, arg.help ?? '', String(arg.default));
|
|
43
|
+
else
|
|
44
|
+
subCmd.option(flag, arg.help ?? '');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
subCmd
|
|
48
|
+
.option('-f, --format <fmt>', 'Output format: table, plain, json, yaml, md, csv', 'table')
|
|
49
|
+
.option('--trace <mode>', 'Trace capture: off, on, retain-on-failure', 'off')
|
|
50
|
+
.option('-v, --verbose', 'Debug output', false);
|
|
51
|
+
if (cmd.browser) {
|
|
52
|
+
subCmd
|
|
53
|
+
.option('--window <mode>', 'Browser window mode: foreground or background')
|
|
54
|
+
.option('--site-session <mode>', 'Adapter site session lifecycle: ephemeral or persistent')
|
|
55
|
+
.option('--keep-tab <bool>', 'Keep the browser tab lease after the command finishes');
|
|
56
|
+
}
|
|
57
|
+
const originalHelpInformation = subCmd.helpInformation.bind(subCmd);
|
|
58
|
+
subCmd.helpInformation = ((contextOptions) => {
|
|
59
|
+
const format = getRequestedHelpFormat();
|
|
60
|
+
if (format)
|
|
61
|
+
return renderStructuredHelp(commandHelpData(cmd), format);
|
|
62
|
+
// Keep a fallback reference so future Commander upgrades still initialize
|
|
63
|
+
// internal help state before we render the cleaner grouped command help.
|
|
64
|
+
void originalHelpInformation(contextOptions);
|
|
65
|
+
return formatCommandHelpText(cmd);
|
|
66
|
+
});
|
|
67
|
+
subCmd.action(async (...actionArgs) => {
|
|
68
|
+
const actionOpts = actionArgs[positionalArgs.length] ?? {};
|
|
69
|
+
const optionsRecord = typeof actionOpts === 'object' && actionOpts !== null ? actionOpts : {};
|
|
70
|
+
const startTime = Date.now();
|
|
71
|
+
// ── Execute + render ────────────────────────────────────────────────
|
|
72
|
+
try {
|
|
73
|
+
// ── Collect kwargs ────────────────────────────────────────────────
|
|
74
|
+
const rawKwargs = {};
|
|
75
|
+
for (let i = 0; i < positionalArgs.length; i++) {
|
|
76
|
+
const v = actionArgs[i];
|
|
77
|
+
if (v !== undefined)
|
|
78
|
+
rawKwargs[positionalArgs[i].name] = v;
|
|
79
|
+
}
|
|
80
|
+
for (const arg of cmd.args) {
|
|
81
|
+
if (arg.positional)
|
|
82
|
+
continue;
|
|
83
|
+
const camelName = arg.name.replace(/-([a-z])/g, (_m, ch) => ch.toUpperCase());
|
|
84
|
+
const v = optionsRecord[arg.name] ?? optionsRecord[camelName];
|
|
85
|
+
if (v !== undefined)
|
|
86
|
+
rawKwargs[arg.name] = v;
|
|
87
|
+
}
|
|
88
|
+
const optionSources = {};
|
|
89
|
+
for (const arg of cmd.args) {
|
|
90
|
+
if (arg.positional)
|
|
91
|
+
continue;
|
|
92
|
+
const camelName = arg.name.replace(/-([a-z])/g, (_m, ch) => ch.toUpperCase());
|
|
93
|
+
const source = subCmd.getOptionValueSource(camelName) ?? subCmd.getOptionValueSource(arg.name);
|
|
94
|
+
if (source === 'cli')
|
|
95
|
+
optionSources[arg.name] = source;
|
|
96
|
+
}
|
|
97
|
+
if (Object.keys(optionSources).length > 0) {
|
|
98
|
+
rawKwargs.__webcmdOptionSources = optionSources;
|
|
99
|
+
}
|
|
100
|
+
const kwargs = prepareCommandArgs(cmd, rawKwargs);
|
|
101
|
+
const verbose = optionsRecord.verbose === true;
|
|
102
|
+
let format = typeof optionsRecord.format === 'string' ? optionsRecord.format : 'table';
|
|
103
|
+
const formatExplicit = subCmd.getOptionValueSource('format') === 'cli';
|
|
104
|
+
if (verbose)
|
|
105
|
+
process.env.WEBCMD_VERBOSE = '1';
|
|
106
|
+
const globals = typeof subCmd.optsWithGlobals === 'function' ? subCmd.optsWithGlobals() : {};
|
|
107
|
+
const result = await executeCommand(cmd, kwargs, verbose, {
|
|
108
|
+
prepared: true,
|
|
109
|
+
...(typeof globals.profile === 'string' && globals.profile.trim() ? { profile: globals.profile.trim() } : {}),
|
|
110
|
+
...(typeof optionsRecord.trace === 'string' && optionsRecord.trace !== 'off' ? { trace: optionsRecord.trace } : {}),
|
|
111
|
+
...(cmd.browser && typeof optionsRecord.window === 'string' ? { windowMode: optionsRecord.window } : {}),
|
|
112
|
+
...(cmd.browser && typeof optionsRecord.siteSession === 'string' ? { siteSession: optionsRecord.siteSession } : {}),
|
|
113
|
+
...(cmd.browser && typeof optionsRecord.keepTab === 'string' ? { keepTab: optionsRecord.keepTab } : {}),
|
|
114
|
+
});
|
|
115
|
+
if (result === null || result === undefined) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const resolved = getRegistry().get(fullName(cmd)) ?? cmd;
|
|
119
|
+
if (!formatExplicit && format === 'table' && resolved.defaultFormat) {
|
|
120
|
+
format = resolved.defaultFormat;
|
|
121
|
+
}
|
|
122
|
+
if (verbose && (!result || (Array.isArray(result) && result.length === 0))) {
|
|
123
|
+
log.warn('Command returned an empty result.');
|
|
124
|
+
}
|
|
125
|
+
renderOutput(result, {
|
|
126
|
+
fmt: format,
|
|
127
|
+
fmtExplicit: formatExplicit,
|
|
128
|
+
columns: resolved.columns,
|
|
129
|
+
title: `${resolved.site}/${resolved.name}`,
|
|
130
|
+
elapsed: (Date.now() - startTime) / 1000,
|
|
131
|
+
source: fullName(resolved),
|
|
132
|
+
footerExtra: resolved.footerExtra?.(kwargs),
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
catch (err) {
|
|
136
|
+
renderError(err, fullName(cmd), optionsRecord.verbose === true, optionsRecord.trace);
|
|
137
|
+
process.exitCode = resolveExitCode(err);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
// ── Exit code resolution ─────────────────────────────────────────────────────
|
|
142
|
+
function resolveExitCode(err) {
|
|
143
|
+
if (err instanceof CliError)
|
|
144
|
+
return err.exitCode;
|
|
145
|
+
return EXIT_CODES.GENERIC_ERROR;
|
|
146
|
+
}
|
|
147
|
+
// ── Error rendering ─────────────────────────────────────────────────────────
|
|
148
|
+
/** Emit AutoFix hint for repairable adapter errors (skipped if trace already exported). */
|
|
149
|
+
function emitAutoFixHint(envelope, cmdName, traceMode) {
|
|
150
|
+
if (traceMode === 'on' || traceMode === 'retain-on-failure')
|
|
151
|
+
return envelope;
|
|
152
|
+
const runnable = cmdName.replace('/', ' ');
|
|
153
|
+
return envelope
|
|
154
|
+
+ `# AutoFix: re-run with --trace=retain-on-failure for trace artifact\n`
|
|
155
|
+
+ `# webcmd ${runnable} --trace retain-on-failure\n`;
|
|
156
|
+
}
|
|
157
|
+
function renderError(err, cmdName, verbose, traceMode) {
|
|
158
|
+
const envelope = toEnvelope(err);
|
|
159
|
+
// In verbose mode, include stack trace for debugging
|
|
160
|
+
if (verbose && err instanceof Error && err.stack) {
|
|
161
|
+
envelope.error.stack = err.stack;
|
|
162
|
+
}
|
|
163
|
+
let output = yaml.dump(envelope, { sortKeys: false, lineWidth: 120, noRefs: true });
|
|
164
|
+
// Append AutoFix hint for repairable errors
|
|
165
|
+
const code = envelope.error.code;
|
|
166
|
+
if (code === 'SELECTOR' || code === 'EMPTY_RESULT' || code === 'ADAPTER_LOAD' || code === 'UNKNOWN') {
|
|
167
|
+
output = emitAutoFixHint(output, cmdName, traceMode);
|
|
168
|
+
}
|
|
169
|
+
process.stderr.write(output);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Register all commands from the registry onto a Commander program.
|
|
173
|
+
*/
|
|
174
|
+
export function registerAllCommands(program, siteGroups) {
|
|
175
|
+
const seen = new Set();
|
|
176
|
+
const commandsBySite = new Map();
|
|
177
|
+
for (const [, cmd] of getRegistry()) {
|
|
178
|
+
if (seen.has(cmd))
|
|
179
|
+
continue;
|
|
180
|
+
seen.add(cmd);
|
|
181
|
+
const commands = commandsBySite.get(cmd.site) ?? [];
|
|
182
|
+
commands.push(cmd);
|
|
183
|
+
commandsBySite.set(cmd.site, commands);
|
|
184
|
+
}
|
|
185
|
+
for (const [site, commands] of commandsBySite) {
|
|
186
|
+
let siteCmd = siteGroups.get(site);
|
|
187
|
+
if (!siteCmd) {
|
|
188
|
+
siteCmd = program.command(site);
|
|
189
|
+
siteGroups.set(site, siteCmd);
|
|
190
|
+
}
|
|
191
|
+
for (const cmd of commands) {
|
|
192
|
+
registerCommandToProgram(siteCmd, cmd);
|
|
193
|
+
}
|
|
194
|
+
const commandTerms = new Map(commands.map(cmd => [cmd.name, formatCommandListTerm(cmd)]));
|
|
195
|
+
siteCmd.configureHelp({
|
|
196
|
+
subcommandTerm: command => commandTerms.get(command.name()) ?? command.name(),
|
|
197
|
+
});
|
|
198
|
+
const originalSiteHelpInformation = siteCmd.helpInformation.bind(siteCmd);
|
|
199
|
+
siteCmd.helpInformation = ((contextOptions) => {
|
|
200
|
+
const format = getRequestedHelpFormat();
|
|
201
|
+
if (format)
|
|
202
|
+
return renderStructuredHelp(siteHelpData(site, commands), format);
|
|
203
|
+
void originalSiteHelpInformation(contextOptions);
|
|
204
|
+
return formatSiteHelpText(site, commands);
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
return [...commandsBySite.keys()].sort((a, b) => a.localeCompare(b));
|
|
208
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { attachTraceReceipt, EmptyResultError, selectorError } from './errors.js';
|
|
4
|
+
const { mockExecuteCommand, mockRenderOutput } = vi.hoisted(() => ({
|
|
5
|
+
mockExecuteCommand: vi.fn(),
|
|
6
|
+
mockRenderOutput: vi.fn(),
|
|
7
|
+
}));
|
|
8
|
+
vi.mock('./execution.js', async () => {
|
|
9
|
+
const actual = await vi.importActual('./execution.js');
|
|
10
|
+
return {
|
|
11
|
+
...actual,
|
|
12
|
+
executeCommand: mockExecuteCommand,
|
|
13
|
+
};
|
|
14
|
+
});
|
|
15
|
+
vi.mock('./output.js', () => ({
|
|
16
|
+
render: mockRenderOutput,
|
|
17
|
+
}));
|
|
18
|
+
import { registerCommandToProgram } from './commanderAdapter.js';
|
|
19
|
+
describe('commanderAdapter arg passing', () => {
|
|
20
|
+
const cmd = {
|
|
21
|
+
site: 'paperreview',
|
|
22
|
+
name: 'submit', access: 'read',
|
|
23
|
+
description: 'Submit a PDF',
|
|
24
|
+
browser: false,
|
|
25
|
+
args: [
|
|
26
|
+
{ name: 'pdf', positional: true, required: true, help: 'Path to the paper PDF' },
|
|
27
|
+
{ name: 'dry-run', type: 'bool', default: false, help: 'Validate only' },
|
|
28
|
+
{ name: 'prepare-only', type: 'bool', default: false, help: 'Prepare only' },
|
|
29
|
+
],
|
|
30
|
+
func: vi.fn(),
|
|
31
|
+
};
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
mockExecuteCommand.mockReset();
|
|
34
|
+
mockExecuteCommand.mockResolvedValue([]);
|
|
35
|
+
mockRenderOutput.mockReset();
|
|
36
|
+
delete process.env.WEBCMD_VERBOSE;
|
|
37
|
+
process.exitCode = undefined;
|
|
38
|
+
});
|
|
39
|
+
it('passes bool flag values through to executeCommand for coercion', async () => {
|
|
40
|
+
const program = new Command();
|
|
41
|
+
const siteCmd = program.command('paperreview');
|
|
42
|
+
registerCommandToProgram(siteCmd, cmd);
|
|
43
|
+
await program.parseAsync(['node', 'webcmd', 'paperreview', 'submit', './paper.pdf', '--dry-run', 'false']);
|
|
44
|
+
expect(mockExecuteCommand).toHaveBeenCalled();
|
|
45
|
+
const kwargs = mockExecuteCommand.mock.calls[0][1];
|
|
46
|
+
expect(kwargs.pdf).toBe('./paper.pdf');
|
|
47
|
+
expect(kwargs).toHaveProperty('dry-run');
|
|
48
|
+
});
|
|
49
|
+
it('passes valueless bool flags as true to executeCommand', async () => {
|
|
50
|
+
const program = new Command();
|
|
51
|
+
const siteCmd = program.command('paperreview');
|
|
52
|
+
registerCommandToProgram(siteCmd, cmd);
|
|
53
|
+
await program.parseAsync(['node', 'webcmd', 'paperreview', 'submit', './paper.pdf', '--prepare-only']);
|
|
54
|
+
expect(mockExecuteCommand).toHaveBeenCalled();
|
|
55
|
+
const kwargs = mockExecuteCommand.mock.calls[0][1];
|
|
56
|
+
expect(kwargs.pdf).toBe('./paper.pdf');
|
|
57
|
+
expect(kwargs['prepare-only']).toBe(true);
|
|
58
|
+
});
|
|
59
|
+
it('passes option value sources through for adapters that need explicit-vs-default semantics', async () => {
|
|
60
|
+
const program = new Command();
|
|
61
|
+
const siteCmd = program.command('paperreview');
|
|
62
|
+
registerCommandToProgram(siteCmd, cmd);
|
|
63
|
+
await program.parseAsync(['node', 'webcmd', 'paperreview', 'submit', './paper.pdf', '--prepare-only']);
|
|
64
|
+
expect(mockExecuteCommand).toHaveBeenCalled();
|
|
65
|
+
const kwargs = mockExecuteCommand.mock.calls[0][1];
|
|
66
|
+
expect(kwargs.__webcmdOptionSources).toMatchObject({
|
|
67
|
+
'prepare-only': 'cli',
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
it('passes explicit trace mode to executeCommand', async () => {
|
|
71
|
+
const program = new Command();
|
|
72
|
+
const siteCmd = program.command('paperreview');
|
|
73
|
+
registerCommandToProgram(siteCmd, cmd);
|
|
74
|
+
await program.parseAsync(['node', 'webcmd', 'paperreview', 'submit', './paper.pdf', '--trace', 'retain-on-failure']);
|
|
75
|
+
expect(mockExecuteCommand).toHaveBeenCalledWith(expect.objectContaining({ site: 'paperreview', name: 'submit' }), expect.objectContaining({ pdf: './paper.pdf' }), false, { prepared: true, trace: 'retain-on-failure' });
|
|
76
|
+
});
|
|
77
|
+
it('rejects invalid bool values before calling executeCommand', async () => {
|
|
78
|
+
const program = new Command();
|
|
79
|
+
const siteCmd = program.command('paperreview');
|
|
80
|
+
registerCommandToProgram(siteCmd, cmd);
|
|
81
|
+
await program.parseAsync(['node', 'webcmd', 'paperreview', 'submit', './paper.pdf', '--dry-run', 'maybe']);
|
|
82
|
+
// prepareCommandArgs validates bools before dispatch; executeCommand should not be reached
|
|
83
|
+
expect(mockExecuteCommand).not.toHaveBeenCalled();
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
describe('commanderAdapter boolean alias support', () => {
|
|
87
|
+
const cmd = {
|
|
88
|
+
site: 'reddit',
|
|
89
|
+
name: 'save', access: 'read',
|
|
90
|
+
description: 'Save a post',
|
|
91
|
+
browser: false,
|
|
92
|
+
args: [
|
|
93
|
+
{ name: 'post-id', positional: true, required: true, help: 'Post ID' },
|
|
94
|
+
{ name: 'undo', type: 'boolean', default: false, help: 'Unsave instead of save' },
|
|
95
|
+
],
|
|
96
|
+
func: vi.fn(),
|
|
97
|
+
};
|
|
98
|
+
beforeEach(() => {
|
|
99
|
+
mockExecuteCommand.mockReset();
|
|
100
|
+
mockExecuteCommand.mockResolvedValue([]);
|
|
101
|
+
mockRenderOutput.mockReset();
|
|
102
|
+
delete process.env.WEBCMD_VERBOSE;
|
|
103
|
+
process.exitCode = undefined;
|
|
104
|
+
});
|
|
105
|
+
it('coerces default false for boolean args to a real boolean', async () => {
|
|
106
|
+
const program = new Command();
|
|
107
|
+
const siteCmd = program.command('reddit');
|
|
108
|
+
registerCommandToProgram(siteCmd, cmd);
|
|
109
|
+
await program.parseAsync(['node', 'webcmd', 'reddit', 'save', 't3_abc123']);
|
|
110
|
+
expect(mockExecuteCommand).toHaveBeenCalled();
|
|
111
|
+
const kwargs = mockExecuteCommand.mock.calls[0][1];
|
|
112
|
+
expect(kwargs['post-id']).toBe('t3_abc123');
|
|
113
|
+
expect(kwargs.undo).toBe(false);
|
|
114
|
+
});
|
|
115
|
+
it('coerces explicit false for boolean args to a real boolean', async () => {
|
|
116
|
+
const program = new Command();
|
|
117
|
+
const siteCmd = program.command('reddit');
|
|
118
|
+
registerCommandToProgram(siteCmd, cmd);
|
|
119
|
+
await program.parseAsync(['node', 'webcmd', 'reddit', 'save', 't3_abc123', '--undo', 'false']);
|
|
120
|
+
expect(mockExecuteCommand).toHaveBeenCalled();
|
|
121
|
+
const kwargs = mockExecuteCommand.mock.calls[0][1];
|
|
122
|
+
expect(kwargs.undo).toBe(false);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
describe('commanderAdapter value-required optional options', () => {
|
|
126
|
+
const cmd = {
|
|
127
|
+
site: 'instagram',
|
|
128
|
+
name: 'post', access: 'read',
|
|
129
|
+
description: 'Post to Instagram',
|
|
130
|
+
browser: true,
|
|
131
|
+
args: [
|
|
132
|
+
{ name: 'image', valueRequired: true, help: 'Single image path' },
|
|
133
|
+
{ name: 'images', valueRequired: true, help: 'Comma-separated image paths' },
|
|
134
|
+
{ name: 'content', positional: true, required: false, help: 'Caption text' },
|
|
135
|
+
],
|
|
136
|
+
validateArgs: (kwargs) => {
|
|
137
|
+
if (!kwargs.image && !kwargs.images) {
|
|
138
|
+
throw new Error('media required');
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
func: vi.fn(),
|
|
142
|
+
};
|
|
143
|
+
beforeEach(() => {
|
|
144
|
+
mockExecuteCommand.mockReset();
|
|
145
|
+
mockExecuteCommand.mockResolvedValue([]);
|
|
146
|
+
mockRenderOutput.mockReset();
|
|
147
|
+
delete process.env.WEBCMD_VERBOSE;
|
|
148
|
+
process.exitCode = undefined;
|
|
149
|
+
});
|
|
150
|
+
it('requires a value when --image is present', async () => {
|
|
151
|
+
const program = new Command();
|
|
152
|
+
program.exitOverride();
|
|
153
|
+
const siteCmd = program.command('instagram');
|
|
154
|
+
registerCommandToProgram(siteCmd, cmd);
|
|
155
|
+
await expect(program.parseAsync(['node', 'webcmd', 'instagram', 'post', '--image'])).rejects.toMatchObject({ code: 'commander.optionMissingArgument' });
|
|
156
|
+
expect(mockExecuteCommand).not.toHaveBeenCalled();
|
|
157
|
+
});
|
|
158
|
+
it('runs validateArgs before executeCommand so missing media does not dispatch the browser command', async () => {
|
|
159
|
+
const program = new Command();
|
|
160
|
+
const siteCmd = program.command('instagram');
|
|
161
|
+
registerCommandToProgram(siteCmd, cmd);
|
|
162
|
+
await program.parseAsync(['node', 'webcmd', 'instagram', 'post', 'caption only']);
|
|
163
|
+
expect(mockExecuteCommand).not.toHaveBeenCalled();
|
|
164
|
+
expect(process.exitCode).toBeDefined();
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
describe('commanderAdapter command aliases', () => {
|
|
168
|
+
const cmd = {
|
|
169
|
+
site: 'notebooklm',
|
|
170
|
+
name: 'get',
|
|
171
|
+
access: 'read',
|
|
172
|
+
aliases: ['metadata'],
|
|
173
|
+
description: 'Get notebook metadata',
|
|
174
|
+
browser: false,
|
|
175
|
+
args: [],
|
|
176
|
+
func: vi.fn(),
|
|
177
|
+
};
|
|
178
|
+
beforeEach(() => {
|
|
179
|
+
mockExecuteCommand.mockReset();
|
|
180
|
+
mockExecuteCommand.mockResolvedValue([]);
|
|
181
|
+
mockRenderOutput.mockReset();
|
|
182
|
+
delete process.env.WEBCMD_VERBOSE;
|
|
183
|
+
process.exitCode = undefined;
|
|
184
|
+
});
|
|
185
|
+
it('registers aliases with Commander so compatibility names execute the same command', async () => {
|
|
186
|
+
const program = new Command();
|
|
187
|
+
const siteCmd = program.command('notebooklm');
|
|
188
|
+
registerCommandToProgram(siteCmd, cmd);
|
|
189
|
+
await program.parseAsync(['node', 'webcmd', 'notebooklm', 'metadata']);
|
|
190
|
+
expect(mockExecuteCommand).toHaveBeenCalledWith(cmd, {}, false, { prepared: true });
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
describe('commanderAdapter validation preparation', () => {
|
|
194
|
+
beforeEach(() => {
|
|
195
|
+
mockExecuteCommand.mockReset();
|
|
196
|
+
mockExecuteCommand.mockResolvedValue([]);
|
|
197
|
+
mockRenderOutput.mockReset();
|
|
198
|
+
delete process.env.WEBCMD_VERBOSE;
|
|
199
|
+
process.exitCode = undefined;
|
|
200
|
+
});
|
|
201
|
+
it('prepares args once before dispatching to executeCommand', async () => {
|
|
202
|
+
const validateArgs = vi.fn();
|
|
203
|
+
const program = new Command();
|
|
204
|
+
const siteCmd = program.command('test');
|
|
205
|
+
registerCommandToProgram(siteCmd, {
|
|
206
|
+
site: 'test',
|
|
207
|
+
name: 'run', access: 'read',
|
|
208
|
+
description: 'Run test command',
|
|
209
|
+
browser: false,
|
|
210
|
+
args: [{ name: 'count', default: '1', help: 'Count' }],
|
|
211
|
+
validateArgs,
|
|
212
|
+
func: vi.fn(),
|
|
213
|
+
});
|
|
214
|
+
await program.parseAsync(['node', 'webcmd', 'test', 'run']);
|
|
215
|
+
expect(validateArgs).toHaveBeenCalledTimes(1);
|
|
216
|
+
expect(mockExecuteCommand).toHaveBeenCalledWith(expect.objectContaining({ site: 'test', name: 'run' }), { count: '1' }, false, { prepared: true });
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
describe('commanderAdapter default formats', () => {
|
|
220
|
+
const cmd = {
|
|
221
|
+
site: 'gemini',
|
|
222
|
+
name: 'ask', access: 'read',
|
|
223
|
+
description: 'Ask Gemini',
|
|
224
|
+
browser: false,
|
|
225
|
+
args: [],
|
|
226
|
+
columns: ['response'],
|
|
227
|
+
defaultFormat: 'plain',
|
|
228
|
+
func: vi.fn(),
|
|
229
|
+
};
|
|
230
|
+
beforeEach(() => {
|
|
231
|
+
mockExecuteCommand.mockReset();
|
|
232
|
+
mockExecuteCommand.mockResolvedValue([{ response: 'hello' }]);
|
|
233
|
+
mockRenderOutput.mockReset();
|
|
234
|
+
delete process.env.WEBCMD_VERBOSE;
|
|
235
|
+
process.exitCode = undefined;
|
|
236
|
+
});
|
|
237
|
+
it('uses the command defaultFormat when the user keeps the default table format', async () => {
|
|
238
|
+
const program = new Command();
|
|
239
|
+
const siteCmd = program.command('gemini');
|
|
240
|
+
registerCommandToProgram(siteCmd, cmd);
|
|
241
|
+
await program.parseAsync(['node', 'webcmd', 'gemini', 'ask']);
|
|
242
|
+
expect(mockRenderOutput).toHaveBeenCalledWith([{ response: 'hello' }], expect.objectContaining({ fmt: 'plain' }));
|
|
243
|
+
});
|
|
244
|
+
it('respects an explicit user format over the command defaultFormat', async () => {
|
|
245
|
+
const program = new Command();
|
|
246
|
+
const siteCmd = program.command('gemini');
|
|
247
|
+
registerCommandToProgram(siteCmd, cmd);
|
|
248
|
+
await program.parseAsync(['node', 'webcmd', 'gemini', 'ask', '--format', 'json']);
|
|
249
|
+
expect(mockRenderOutput).toHaveBeenCalledWith([{ response: 'hello' }], expect.objectContaining({ fmt: 'json' }));
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
describe('commanderAdapter error envelope output', () => {
|
|
253
|
+
const cmd = {
|
|
254
|
+
site: 'xiaohongshu',
|
|
255
|
+
name: 'note', access: 'read',
|
|
256
|
+
description: 'Read one note',
|
|
257
|
+
browser: false,
|
|
258
|
+
args: [
|
|
259
|
+
{ name: 'note-id', positional: true, required: true, help: 'Note ID' },
|
|
260
|
+
],
|
|
261
|
+
func: vi.fn(),
|
|
262
|
+
};
|
|
263
|
+
beforeEach(() => {
|
|
264
|
+
mockExecuteCommand.mockReset();
|
|
265
|
+
mockRenderOutput.mockReset();
|
|
266
|
+
delete process.env.WEBCMD_VERBOSE;
|
|
267
|
+
process.exitCode = undefined;
|
|
268
|
+
});
|
|
269
|
+
it('outputs YAML error envelope with adapter hint to stderr', async () => {
|
|
270
|
+
const program = new Command();
|
|
271
|
+
const siteCmd = program.command('xiaohongshu');
|
|
272
|
+
registerCommandToProgram(siteCmd, cmd);
|
|
273
|
+
const stderrSpy = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
274
|
+
mockExecuteCommand.mockRejectedValueOnce(new EmptyResultError('xiaohongshu/note', 'Pass the full search_result URL with xsec_token instead of a bare note ID.'));
|
|
275
|
+
await program.parseAsync(['node', 'webcmd', 'xiaohongshu', 'note', '69ca3927000000001a020fd5']);
|
|
276
|
+
const output = stderrSpy.mock.calls.map(c => String(c[0])).join('');
|
|
277
|
+
expect(output).toContain('ok: false');
|
|
278
|
+
expect(output).toContain('code: EMPTY_RESULT');
|
|
279
|
+
expect(output).toContain('xsec_token');
|
|
280
|
+
expect(output).toContain('--trace=retain-on-failure');
|
|
281
|
+
expect(output).toContain('webcmd xiaohongshu note --trace retain-on-failure');
|
|
282
|
+
stderrSpy.mockRestore();
|
|
283
|
+
});
|
|
284
|
+
it('outputs YAML error envelope for selector errors', async () => {
|
|
285
|
+
const program = new Command();
|
|
286
|
+
const siteCmd = program.command('xiaohongshu');
|
|
287
|
+
registerCommandToProgram(siteCmd, cmd);
|
|
288
|
+
const stderrSpy = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
289
|
+
mockExecuteCommand.mockRejectedValueOnce(selectorError('.note-title', 'The note title selector no longer matches the current page.'));
|
|
290
|
+
await program.parseAsync(['node', 'webcmd', 'xiaohongshu', 'note', '69ca3927000000001a020fd5']);
|
|
291
|
+
const output = stderrSpy.mock.calls.map(c => String(c[0])).join('');
|
|
292
|
+
expect(output).toContain('ok: false');
|
|
293
|
+
expect(output).toContain('code: SELECTOR');
|
|
294
|
+
expect(output).toContain('selector no longer matches');
|
|
295
|
+
expect(output).toContain('--trace=retain-on-failure');
|
|
296
|
+
stderrSpy.mockRestore();
|
|
297
|
+
});
|
|
298
|
+
it('does not add an AutoFix rerun hint when trace is already enabled', async () => {
|
|
299
|
+
const program = new Command();
|
|
300
|
+
const siteCmd = program.command('xiaohongshu');
|
|
301
|
+
registerCommandToProgram(siteCmd, cmd);
|
|
302
|
+
const stderrSpy = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
303
|
+
mockExecuteCommand.mockRejectedValueOnce(selectorError('.note-title'));
|
|
304
|
+
await program.parseAsync([
|
|
305
|
+
'node',
|
|
306
|
+
'webcmd',
|
|
307
|
+
'xiaohongshu',
|
|
308
|
+
'note',
|
|
309
|
+
'69ca3927000000001a020fd5',
|
|
310
|
+
'--trace',
|
|
311
|
+
'retain-on-failure',
|
|
312
|
+
]);
|
|
313
|
+
const output = stderrSpy.mock.calls.map(c => String(c[0])).join('');
|
|
314
|
+
expect(output).toContain('code: SELECTOR');
|
|
315
|
+
expect(output).not.toContain('AutoFix: re-run');
|
|
316
|
+
stderrSpy.mockRestore();
|
|
317
|
+
});
|
|
318
|
+
it('includes trace metadata from the error envelope when execution attached it', async () => {
|
|
319
|
+
const program = new Command();
|
|
320
|
+
const siteCmd = program.command('xiaohongshu');
|
|
321
|
+
registerCommandToProgram(siteCmd, cmd);
|
|
322
|
+
const stderrSpy = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
323
|
+
const err = selectorError('.note-title');
|
|
324
|
+
attachTraceReceipt(err, {
|
|
325
|
+
schemaVersion: 1,
|
|
326
|
+
webcmdVersion: '1.7.8',
|
|
327
|
+
traceId: 'trace-1',
|
|
328
|
+
traceDir: '/tmp/webcmd/profiles/default/traces/trace-1',
|
|
329
|
+
summaryPath: '/tmp/webcmd/profiles/default/traces/trace-1/summary.md',
|
|
330
|
+
receiptPath: '/tmp/webcmd/profiles/default/traces/trace-1/receipt.json',
|
|
331
|
+
status: 'failure',
|
|
332
|
+
createdAt: '2026-05-03T00:00:00.000Z',
|
|
333
|
+
error: { code: 'SELECTOR', message: 'Could not find element: .note-title' },
|
|
334
|
+
});
|
|
335
|
+
mockExecuteCommand.mockRejectedValueOnce(err);
|
|
336
|
+
await program.parseAsync([
|
|
337
|
+
'node',
|
|
338
|
+
'webcmd',
|
|
339
|
+
'xiaohongshu',
|
|
340
|
+
'note',
|
|
341
|
+
'69ca3927000000001a020fd5',
|
|
342
|
+
'--trace',
|
|
343
|
+
'retain-on-failure',
|
|
344
|
+
]);
|
|
345
|
+
const output = stderrSpy.mock.calls.map(c => String(c[0])).join('');
|
|
346
|
+
expect(output).toContain('trace:');
|
|
347
|
+
expect(output).toContain('dir: /tmp/webcmd/profiles/default/traces/trace-1');
|
|
348
|
+
expect(output).toContain('summaryPath: /tmp/webcmd/profiles/default/traces/trace-1/summary.md');
|
|
349
|
+
expect(output).toContain('receiptPath: /tmp/webcmd/profiles/default/traces/trace-1/receipt.json');
|
|
350
|
+
stderrSpy.mockRestore();
|
|
351
|
+
});
|
|
352
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
type AuthStatus = 'logged_in' | 'not_logged_in' | 'unknown' | 'error';
|
|
3
|
+
type AuthStatusMode = 'quick' | 'full';
|
|
4
|
+
type AuthRefreshStatus = 'refreshed' | 'touched' | 'not_logged_in' | 'skipped' | 'unsupported' | 'error';
|
|
5
|
+
export interface AuthStatusRow {
|
|
6
|
+
site: string;
|
|
7
|
+
status: AuthStatus;
|
|
8
|
+
logged_in: boolean | '';
|
|
9
|
+
identity: string;
|
|
10
|
+
checked: AuthStatusMode | 'skipped';
|
|
11
|
+
error: string;
|
|
12
|
+
}
|
|
13
|
+
interface AuthStatusOptions {
|
|
14
|
+
sites?: string;
|
|
15
|
+
only?: string;
|
|
16
|
+
full?: boolean;
|
|
17
|
+
concurrency?: string | number;
|
|
18
|
+
timeout?: string | number;
|
|
19
|
+
profile?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface AuthRefreshRow {
|
|
22
|
+
site: string;
|
|
23
|
+
status: AuthRefreshStatus;
|
|
24
|
+
last_touched_at: string;
|
|
25
|
+
next_refresh_at: string;
|
|
26
|
+
error: string;
|
|
27
|
+
}
|
|
28
|
+
interface AuthRefreshOptions {
|
|
29
|
+
sites?: string;
|
|
30
|
+
all?: boolean;
|
|
31
|
+
concurrency?: string | number;
|
|
32
|
+
timeout?: string | number;
|
|
33
|
+
profile?: string;
|
|
34
|
+
statePath?: string;
|
|
35
|
+
now?: Date;
|
|
36
|
+
}
|
|
37
|
+
export declare function collectAuthStatus(options: AuthStatusOptions): Promise<AuthStatusRow[]>;
|
|
38
|
+
export declare function collectAuthRefresh(options: AuthRefreshOptions): Promise<AuthRefreshRow[]>;
|
|
39
|
+
export declare function registerAuthCommands(program: Command): Command;
|
|
40
|
+
export {};
|