@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,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Electron app launcher — auto-detect, confirm, launch, and connect.
|
|
3
|
+
*
|
|
4
|
+
* Flow:
|
|
5
|
+
* 1. Probe CDP port → already running with debug? connect directly
|
|
6
|
+
* 2. Detect process → running without CDP? prompt to restart
|
|
7
|
+
* 3. Discover app path → not installed? error
|
|
8
|
+
* 4. Launch with --remote-debugging-port
|
|
9
|
+
* 5. Poll /json until ready
|
|
10
|
+
*/
|
|
11
|
+
import { execFileSync, spawn } from 'node:child_process';
|
|
12
|
+
import { request as httpRequest } from 'node:http';
|
|
13
|
+
import * as path from 'node:path';
|
|
14
|
+
import { getElectronApp } from './electron-apps.js';
|
|
15
|
+
import { confirmPrompt } from './tui.js';
|
|
16
|
+
import { CommandExecutionError } from './errors.js';
|
|
17
|
+
import { log } from './logger.js';
|
|
18
|
+
const POLL_INTERVAL_MS = 500;
|
|
19
|
+
const POLL_TIMEOUT_MS = 15_000;
|
|
20
|
+
const PROBE_TIMEOUT_MS = 2_000;
|
|
21
|
+
const KILL_GRACE_MS = 3_000;
|
|
22
|
+
/**
|
|
23
|
+
* Probe whether a CDP endpoint is listening on the given port.
|
|
24
|
+
* Returns true if http://127.0.0.1:{port}/json responds successfully.
|
|
25
|
+
*/
|
|
26
|
+
export function probeCDP(port, timeoutMs = PROBE_TIMEOUT_MS) {
|
|
27
|
+
return new Promise((resolve) => {
|
|
28
|
+
const req = httpRequest({ hostname: '127.0.0.1', port, path: '/json', method: 'GET', timeout: timeoutMs }, (res) => {
|
|
29
|
+
res.resume();
|
|
30
|
+
resolve(res.statusCode !== undefined && res.statusCode >= 200 && res.statusCode < 300);
|
|
31
|
+
});
|
|
32
|
+
req.on('error', () => resolve(false));
|
|
33
|
+
req.on('timeout', () => { req.destroy(); resolve(false); });
|
|
34
|
+
req.end();
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Check if a process with the given name is running.
|
|
39
|
+
* Uses pgrep on macOS/Linux.
|
|
40
|
+
*/
|
|
41
|
+
export function detectProcess(processName) {
|
|
42
|
+
if (process.platform === 'win32')
|
|
43
|
+
return false; // pgrep not available on Windows
|
|
44
|
+
try {
|
|
45
|
+
execFileSync('pgrep', ['-x', processName], { encoding: 'utf-8', stdio: 'pipe' });
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Kill a process by name. Sends SIGTERM first, then SIGKILL after grace period.
|
|
54
|
+
*/
|
|
55
|
+
export async function killProcess(processName) {
|
|
56
|
+
if (process.platform === 'win32')
|
|
57
|
+
return; // pkill not available on Windows
|
|
58
|
+
try {
|
|
59
|
+
execFileSync('pkill', ['-x', processName], { stdio: 'pipe' });
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// Process may have already exited
|
|
63
|
+
}
|
|
64
|
+
const deadline = Date.now() + KILL_GRACE_MS;
|
|
65
|
+
while (Date.now() < deadline) {
|
|
66
|
+
if (!detectProcess(processName))
|
|
67
|
+
return;
|
|
68
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
execFileSync('pkill', ['-9', '-x', processName], { stdio: 'pipe' });
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// Ignore
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Discover the app installation path on macOS.
|
|
79
|
+
* Uses osascript to resolve the app name to a POSIX path.
|
|
80
|
+
* Returns null if the app is not installed.
|
|
81
|
+
*/
|
|
82
|
+
export function discoverAppPath(displayName) {
|
|
83
|
+
if (process.platform !== 'darwin') {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
const result = execFileSync('osascript', [
|
|
88
|
+
'-e', `POSIX path of (path to application "${displayName}")`,
|
|
89
|
+
], { encoding: 'utf-8', stdio: 'pipe', timeout: 5_000 });
|
|
90
|
+
return result.trim().replace(/\/$/, '');
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function resolveExecutable(appPath, processName) {
|
|
97
|
+
return `${appPath}/Contents/MacOS/${processName}`;
|
|
98
|
+
}
|
|
99
|
+
function isMissingExecutableError(err, label) {
|
|
100
|
+
return err instanceof CommandExecutionError
|
|
101
|
+
&& err.message.startsWith(`Could not launch ${label}: executable not found at `);
|
|
102
|
+
}
|
|
103
|
+
export function resolveExecutableCandidates(appPath, app) {
|
|
104
|
+
const executableNames = app.executableNames?.length ? app.executableNames : [app.processName];
|
|
105
|
+
return [...new Set(executableNames)].map((name) => resolveExecutable(appPath, name));
|
|
106
|
+
}
|
|
107
|
+
export async function launchDetachedApp(executable, args, label) {
|
|
108
|
+
await new Promise((resolve, reject) => {
|
|
109
|
+
const child = spawn(executable, args, {
|
|
110
|
+
detached: true,
|
|
111
|
+
stdio: 'ignore',
|
|
112
|
+
});
|
|
113
|
+
const onError = (err) => {
|
|
114
|
+
if (err.code === 'ENOENT') {
|
|
115
|
+
reject(new CommandExecutionError(`Could not launch ${label}: executable not found at ${executable}`, `Install ${label}, reinstall it, or register a custom app path in ~/.webcmd/apps.yaml`));
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
reject(new CommandExecutionError(`Failed to launch ${label}`, err.message));
|
|
119
|
+
};
|
|
120
|
+
child.once('error', onError);
|
|
121
|
+
child.once('spawn', () => {
|
|
122
|
+
child.off('error', onError);
|
|
123
|
+
child.unref();
|
|
124
|
+
resolve();
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
export async function launchElectronApp(appPath, app, args, label) {
|
|
129
|
+
const executables = resolveExecutableCandidates(appPath, app);
|
|
130
|
+
let lastMissingExecutableError;
|
|
131
|
+
for (const executable of executables) {
|
|
132
|
+
log.debug(`[launcher] Launching: ${executable} ${args.join(' ')}`);
|
|
133
|
+
try {
|
|
134
|
+
await launchDetachedApp(executable, args, label);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
if (isMissingExecutableError(err, label)) {
|
|
139
|
+
lastMissingExecutableError = err;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
throw err;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (executables.length > 1) {
|
|
146
|
+
throw new CommandExecutionError(`Could not launch ${label}: no compatible executable found in ${path.join(appPath, 'Contents', 'MacOS')}`, `Tried: ${executables.map((executable) => path.basename(executable)).join(', ')}. Install ${label}, reinstall it, or register a custom app path in ~/.webcmd/apps.yaml`);
|
|
147
|
+
}
|
|
148
|
+
throw lastMissingExecutableError ?? new CommandExecutionError(`Could not launch ${label}`, `Install ${label}, reinstall it, or register a custom app path in ~/.webcmd/apps.yaml`);
|
|
149
|
+
}
|
|
150
|
+
export function electronLaunchArgs(port, extraArgs = []) {
|
|
151
|
+
return [
|
|
152
|
+
`--remote-debugging-port=${port}`,
|
|
153
|
+
'--remote-allow-origins=*',
|
|
154
|
+
...extraArgs,
|
|
155
|
+
];
|
|
156
|
+
}
|
|
157
|
+
function manualElectronLaunchHint(label, port) {
|
|
158
|
+
return `Start ${label} manually with --remote-debugging-port=${port} --remote-allow-origins=*, then either:`;
|
|
159
|
+
}
|
|
160
|
+
async function pollForReady(port) {
|
|
161
|
+
const deadline = Date.now() + POLL_TIMEOUT_MS;
|
|
162
|
+
while (Date.now() < deadline) {
|
|
163
|
+
if (await probeCDP(port, 1_000))
|
|
164
|
+
return;
|
|
165
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
166
|
+
}
|
|
167
|
+
throw new CommandExecutionError(`App launched but CDP not available on port ${port} after ${POLL_TIMEOUT_MS / 1000}s`, 'The app may be slow to start. Try running the command again.');
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Main entry point: resolve an Electron app to a CDP endpoint URL.
|
|
171
|
+
*
|
|
172
|
+
* Returns the endpoint URL: http://127.0.0.1:{port}
|
|
173
|
+
*/
|
|
174
|
+
export async function resolveElectronEndpoint(site) {
|
|
175
|
+
const app = getElectronApp(site);
|
|
176
|
+
if (!app) {
|
|
177
|
+
throw new CommandExecutionError(`No Electron app registered for site "${site}"`, 'Register the app in ~/.webcmd/apps.yaml or check the site name.');
|
|
178
|
+
}
|
|
179
|
+
const { port, processName, displayName } = app;
|
|
180
|
+
const label = displayName ?? processName;
|
|
181
|
+
const endpoint = `http://127.0.0.1:${port}`;
|
|
182
|
+
// Step 1: Already running with CDP?
|
|
183
|
+
log.debug(`[launcher] Probing CDP on port ${port}...`);
|
|
184
|
+
if (await probeCDP(port)) {
|
|
185
|
+
log.debug(`[launcher] CDP already available on port ${port}`);
|
|
186
|
+
return endpoint;
|
|
187
|
+
}
|
|
188
|
+
// Step 2: Running without CDP? (process detection requires Unix tools)
|
|
189
|
+
if (process.platform !== 'darwin' && process.platform !== 'linux') {
|
|
190
|
+
throw new CommandExecutionError(`${label} is not reachable on CDP port ${port}.`, `Auto-launch is not yet supported on ${process.platform}.\n` +
|
|
191
|
+
`${manualElectronLaunchHint(label, port)}\n` +
|
|
192
|
+
` • Set WEBCMD_CDP_ENDPOINT=http://127.0.0.1:${port}\n` +
|
|
193
|
+
` • Or just re-run the command once ${label} is listening on port ${port}.`);
|
|
194
|
+
}
|
|
195
|
+
const isRunning = detectProcess(processName);
|
|
196
|
+
if (isRunning) {
|
|
197
|
+
log.debug(`[launcher] ${label} is running but CDP not available`);
|
|
198
|
+
const confirmed = await confirmPrompt(`${label} is running but CDP is not enabled. Restart with debug port?`, true);
|
|
199
|
+
if (!confirmed) {
|
|
200
|
+
throw new CommandExecutionError(`${label} needs to be restarted with CDP enabled.`, `Manually restart: kill the app and relaunch with --remote-debugging-port=${port} --remote-allow-origins=*`);
|
|
201
|
+
}
|
|
202
|
+
process.stderr.write(` Restarting ${label}...\n`);
|
|
203
|
+
await killProcess(processName);
|
|
204
|
+
}
|
|
205
|
+
// Step 3: Discover path
|
|
206
|
+
const appPath = discoverAppPath(label);
|
|
207
|
+
if (!appPath) {
|
|
208
|
+
throw new CommandExecutionError(`Could not find ${label} on this machine.`, `Install ${label} or register a custom path in ~/.webcmd/apps.yaml`);
|
|
209
|
+
}
|
|
210
|
+
// Step 4: Launch
|
|
211
|
+
//
|
|
212
|
+
// Chrome / Electron 142+ enforces an Origin allow-list on the CDP
|
|
213
|
+
// WebSocket upgrade (ws://127.0.0.1:<port>/devtools/page/<id>). Without
|
|
214
|
+
// --remote-allow-origins=* every ws client other than chrome://inspect
|
|
215
|
+
// gets HTTP 403 "Rejected an incoming WebSocket connection from the
|
|
216
|
+
// http://127.0.0.1:<port> origin". This affects every Electron app
|
|
217
|
+
// webcmd launches because they all bundle a recent Chromium. Same
|
|
218
|
+
// mitigation as Puppeteer / Playwright / chrome-devtools-mcp.
|
|
219
|
+
const args = electronLaunchArgs(port, app.extraArgs ?? []);
|
|
220
|
+
await launchElectronApp(appPath, app, args, label);
|
|
221
|
+
// Step 5: Poll for readiness
|
|
222
|
+
process.stderr.write(` Waiting for ${label} on port ${port}...\n`);
|
|
223
|
+
await pollForReady(port);
|
|
224
|
+
process.stderr.write(` Connected to ${label} on port ${port}.\n`);
|
|
225
|
+
return endpoint;
|
|
226
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { detectProcess, discoverAppPath, electronLaunchArgs, launchDetachedApp, launchElectronApp, probeCDP, resolveExecutableCandidates } from './launcher.js';
|
|
3
|
+
function createMockChildProcess() {
|
|
4
|
+
const listeners = new Map();
|
|
5
|
+
return {
|
|
6
|
+
once: vi.fn((event, handler) => {
|
|
7
|
+
listeners.set(event, [...(listeners.get(event) ?? []), handler]);
|
|
8
|
+
}),
|
|
9
|
+
off: vi.fn((event, handler) => {
|
|
10
|
+
listeners.set(event, (listeners.get(event) ?? []).filter((listener) => listener !== handler));
|
|
11
|
+
}),
|
|
12
|
+
unref: vi.fn(),
|
|
13
|
+
emit: (event, value) => {
|
|
14
|
+
for (const listener of listeners.get(event) ?? [])
|
|
15
|
+
listener(value);
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
vi.mock('node:child_process', () => ({
|
|
20
|
+
execFileSync: vi.fn(),
|
|
21
|
+
spawn: vi.fn(),
|
|
22
|
+
}));
|
|
23
|
+
const cp = vi.mocked(await import('node:child_process'));
|
|
24
|
+
describe('probeCDP', () => {
|
|
25
|
+
it('returns false when CDP endpoint is unreachable', async () => {
|
|
26
|
+
const result = await probeCDP(59999, 500);
|
|
27
|
+
expect(result).toBe(false);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
describe('detectProcess', () => {
|
|
31
|
+
beforeEach(() => {
|
|
32
|
+
vi.restoreAllMocks();
|
|
33
|
+
});
|
|
34
|
+
it('returns false when pgrep finds no process', () => {
|
|
35
|
+
cp.execFileSync.mockImplementation(() => {
|
|
36
|
+
const err = new Error('exit 1');
|
|
37
|
+
err.status = 1;
|
|
38
|
+
throw err;
|
|
39
|
+
});
|
|
40
|
+
const result = detectProcess('NonExistentApp');
|
|
41
|
+
expect(result).toBe(false);
|
|
42
|
+
});
|
|
43
|
+
it.skipIf(process.platform === 'win32')('returns true when pgrep finds a process', () => {
|
|
44
|
+
cp.execFileSync.mockReturnValue('12345\n');
|
|
45
|
+
const result = detectProcess('Cursor');
|
|
46
|
+
expect(result).toBe(true);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
describe('discoverAppPath', () => {
|
|
50
|
+
beforeEach(() => {
|
|
51
|
+
vi.restoreAllMocks();
|
|
52
|
+
});
|
|
53
|
+
it.skipIf(process.platform !== 'darwin')('returns path when osascript succeeds', () => {
|
|
54
|
+
cp.execFileSync.mockReturnValue('/Applications/Cursor.app/\n');
|
|
55
|
+
const result = discoverAppPath('Cursor');
|
|
56
|
+
expect(result).toBe('/Applications/Cursor.app');
|
|
57
|
+
});
|
|
58
|
+
it.skipIf(process.platform !== 'darwin')('returns null when osascript fails', () => {
|
|
59
|
+
cp.execFileSync.mockImplementation(() => {
|
|
60
|
+
throw new Error('app not found');
|
|
61
|
+
});
|
|
62
|
+
const result = discoverAppPath('NonExistent');
|
|
63
|
+
expect(result).toBeNull();
|
|
64
|
+
});
|
|
65
|
+
it.skipIf(process.platform === 'darwin')('returns null on non-darwin platform', () => {
|
|
66
|
+
const result = discoverAppPath('Cursor');
|
|
67
|
+
expect(result).toBeNull();
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe('launchDetachedApp', () => {
|
|
71
|
+
beforeEach(() => {
|
|
72
|
+
vi.restoreAllMocks();
|
|
73
|
+
cp.spawn.mockReset();
|
|
74
|
+
});
|
|
75
|
+
it('unrefs the process after spawn succeeds', async () => {
|
|
76
|
+
const child = createMockChildProcess();
|
|
77
|
+
cp.spawn.mockImplementation(() => {
|
|
78
|
+
queueMicrotask(() => child.emit('spawn'));
|
|
79
|
+
return child;
|
|
80
|
+
});
|
|
81
|
+
await expect(launchDetachedApp('/Applications/Antigravity.app/Contents/MacOS/Antigravity', ['--remote-debugging-port=9234'], 'Antigravity'))
|
|
82
|
+
.resolves
|
|
83
|
+
.toBeUndefined();
|
|
84
|
+
expect(child.unref).toHaveBeenCalledTimes(1);
|
|
85
|
+
});
|
|
86
|
+
it('converts ENOENT into a controlled launch error', async () => {
|
|
87
|
+
const child = createMockChildProcess();
|
|
88
|
+
cp.spawn.mockImplementation(() => {
|
|
89
|
+
queueMicrotask(() => child.emit('error', Object.assign(new Error('missing binary'), { code: 'ENOENT' })));
|
|
90
|
+
return child;
|
|
91
|
+
});
|
|
92
|
+
await expect(launchDetachedApp('/Applications/Antigravity.app/Contents/MacOS/Antigravity', ['--remote-debugging-port=9234'], 'Antigravity'))
|
|
93
|
+
.rejects
|
|
94
|
+
.toThrow('Could not launch Antigravity');
|
|
95
|
+
expect(child.unref).not.toHaveBeenCalled();
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
describe('resolveExecutableCandidates', () => {
|
|
99
|
+
it('prefers explicit executable candidates over processName', () => {
|
|
100
|
+
const app = {
|
|
101
|
+
port: 9234,
|
|
102
|
+
processName: 'Antigravity',
|
|
103
|
+
executableNames: ['Electron', 'Antigravity'],
|
|
104
|
+
};
|
|
105
|
+
expect(resolveExecutableCandidates('/Applications/Antigravity.app', app)).toEqual([
|
|
106
|
+
'/Applications/Antigravity.app/Contents/MacOS/Electron',
|
|
107
|
+
'/Applications/Antigravity.app/Contents/MacOS/Antigravity',
|
|
108
|
+
]);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
describe('electronLaunchArgs', () => {
|
|
112
|
+
it('includes Chromium 142 WebSocket origin allow-list for auto-launched Electron apps', () => {
|
|
113
|
+
expect(electronLaunchArgs(9234)).toEqual([
|
|
114
|
+
'--remote-debugging-port=9234',
|
|
115
|
+
'--remote-allow-origins=*',
|
|
116
|
+
]);
|
|
117
|
+
});
|
|
118
|
+
it('preserves app-specific extra launch args after the required CDP flags', () => {
|
|
119
|
+
expect(electronLaunchArgs(9234, ['--foo=bar'])).toEqual([
|
|
120
|
+
'--remote-debugging-port=9234',
|
|
121
|
+
'--remote-allow-origins=*',
|
|
122
|
+
'--foo=bar',
|
|
123
|
+
]);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
describe('launchElectronApp', () => {
|
|
127
|
+
beforeEach(() => {
|
|
128
|
+
vi.restoreAllMocks();
|
|
129
|
+
cp.spawn.mockReset();
|
|
130
|
+
});
|
|
131
|
+
it('falls back to the next executable candidate when the first is missing', async () => {
|
|
132
|
+
const firstChild = createMockChildProcess();
|
|
133
|
+
const secondChild = createMockChildProcess();
|
|
134
|
+
const app = {
|
|
135
|
+
port: 9234,
|
|
136
|
+
processName: 'Antigravity',
|
|
137
|
+
executableNames: ['Electron', 'Antigravity'],
|
|
138
|
+
};
|
|
139
|
+
cp.spawn
|
|
140
|
+
.mockImplementationOnce(() => {
|
|
141
|
+
queueMicrotask(() => firstChild.emit('error', Object.assign(new Error('missing binary'), { code: 'ENOENT' })));
|
|
142
|
+
return firstChild;
|
|
143
|
+
})
|
|
144
|
+
.mockImplementationOnce(() => {
|
|
145
|
+
queueMicrotask(() => secondChild.emit('spawn'));
|
|
146
|
+
return secondChild;
|
|
147
|
+
});
|
|
148
|
+
await expect(launchElectronApp('/Applications/Antigravity.app', app, ['--remote-debugging-port=9234'], 'Antigravity')).resolves.toBeUndefined();
|
|
149
|
+
expect(cp.spawn).toHaveBeenNthCalledWith(1, '/Applications/Antigravity.app/Contents/MacOS/Electron', ['--remote-debugging-port=9234'], { detached: true, stdio: 'ignore' });
|
|
150
|
+
expect(cp.spawn).toHaveBeenNthCalledWith(2, '/Applications/Antigravity.app/Contents/MacOS/Antigravity', ['--remote-debugging-port=9234'], { detached: true, stdio: 'ignore' });
|
|
151
|
+
expect(secondChild.unref).toHaveBeenCalledTimes(1);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified logging for webcmd.
|
|
3
|
+
*
|
|
4
|
+
* All framework output (warnings, debug info, errors) should go through
|
|
5
|
+
* this module so that verbosity levels are respected consistently.
|
|
6
|
+
*/
|
|
7
|
+
export declare const log: {
|
|
8
|
+
/** Informational message (always shown) */
|
|
9
|
+
info(msg: string): void;
|
|
10
|
+
/** Lightweight status line for adapter progress updates */
|
|
11
|
+
status(msg: string): void;
|
|
12
|
+
/** Positive completion/status line without the heavier info prefix */
|
|
13
|
+
success(msg: string): void;
|
|
14
|
+
/** Warning (always shown) */
|
|
15
|
+
warn(msg: string): void;
|
|
16
|
+
/** Error (always shown) */
|
|
17
|
+
error(msg: string): void;
|
|
18
|
+
/** Verbose output (shown when -v flag or WEBCMD_VERBOSE is set) */
|
|
19
|
+
verbose(msg: string): void;
|
|
20
|
+
/** Alias for verbose output. */
|
|
21
|
+
debug(msg: string): void;
|
|
22
|
+
/** Step-style debug (for pipeline steps, etc.) */
|
|
23
|
+
step(stepNum: number, total: number, op: string, preview?: string): void;
|
|
24
|
+
/** Step result summary */
|
|
25
|
+
stepResult(summary: string): void;
|
|
26
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified logging for webcmd.
|
|
3
|
+
*
|
|
4
|
+
* All framework output (warnings, debug info, errors) should go through
|
|
5
|
+
* this module so that verbosity levels are respected consistently.
|
|
6
|
+
*/
|
|
7
|
+
function isVerbose() {
|
|
8
|
+
return !!process.env.WEBCMD_VERBOSE;
|
|
9
|
+
}
|
|
10
|
+
export const log = {
|
|
11
|
+
/** Informational message (always shown) */
|
|
12
|
+
info(msg) {
|
|
13
|
+
process.stderr.write(`ℹ ${msg}\n`);
|
|
14
|
+
},
|
|
15
|
+
/** Lightweight status line for adapter progress updates */
|
|
16
|
+
status(msg) {
|
|
17
|
+
process.stderr.write(`${msg}\n`);
|
|
18
|
+
},
|
|
19
|
+
/** Positive completion/status line without the heavier info prefix */
|
|
20
|
+
success(msg) {
|
|
21
|
+
process.stderr.write(`${msg}\n`);
|
|
22
|
+
},
|
|
23
|
+
/** Warning (always shown) */
|
|
24
|
+
warn(msg) {
|
|
25
|
+
process.stderr.write(`⚠ ${msg}\n`);
|
|
26
|
+
},
|
|
27
|
+
/** Error (always shown) */
|
|
28
|
+
error(msg) {
|
|
29
|
+
process.stderr.write(`✖ ${msg}\n`);
|
|
30
|
+
},
|
|
31
|
+
/** Verbose output (shown when -v flag or WEBCMD_VERBOSE is set) */
|
|
32
|
+
verbose(msg) {
|
|
33
|
+
if (isVerbose()) {
|
|
34
|
+
process.stderr.write(`[verbose] ${msg}\n`);
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
/** Alias for verbose output. */
|
|
38
|
+
debug(msg) {
|
|
39
|
+
this.verbose(msg);
|
|
40
|
+
},
|
|
41
|
+
/** Step-style debug (for pipeline steps, etc.) */
|
|
42
|
+
step(stepNum, total, op, preview = '') {
|
|
43
|
+
process.stderr.write(` [${stepNum}/${total}] ${op}${preview}\n`);
|
|
44
|
+
},
|
|
45
|
+
/** Step result summary */
|
|
46
|
+
stepResult(summary) {
|
|
47
|
+
process.stderr.write(` → ${summary}\n`);
|
|
48
|
+
},
|
|
49
|
+
};
|
package/dist/src/main.js
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* webcmd — Make any website your CLI. AI-powered.
|
|
4
|
+
*/
|
|
5
|
+
// Ensure standard system paths are available for child processes.
|
|
6
|
+
// Some environments (GUI apps, cron, IDE terminals) launch with a minimal PATH
|
|
7
|
+
// that excludes /usr/local/bin, /usr/sbin, etc., causing external CLIs to fail.
|
|
8
|
+
if (process.platform !== 'win32') {
|
|
9
|
+
const std = ['/usr/local/bin', '/usr/bin', '/bin', '/usr/sbin', '/sbin'];
|
|
10
|
+
const cur = new Set((process.env.PATH ?? '').split(':').filter(Boolean));
|
|
11
|
+
for (const p of std)
|
|
12
|
+
cur.add(p);
|
|
13
|
+
process.env.PATH = [...cur].join(':');
|
|
14
|
+
}
|
|
15
|
+
import * as fs from 'node:fs';
|
|
16
|
+
import * as os from 'node:os';
|
|
17
|
+
import * as path from 'node:path';
|
|
18
|
+
import { fileURLToPath } from 'node:url';
|
|
19
|
+
import { getCompletionsFromManifest, hasAllManifests, printCompletionScriptFast } from './completion-fast.js';
|
|
20
|
+
import { findPackageRoot, getCliManifestPath } from './package-paths.js';
|
|
21
|
+
import { PKG_VERSION } from './version.js';
|
|
22
|
+
import { EXIT_CODES } from './errors.js';
|
|
23
|
+
import { isSupportedNodeVersion, MIN_SUPPORTED_NODE_MAJOR } from './runtime-detect.js';
|
|
24
|
+
import { unsupportedDaemonPortEnvMessage } from './constants.js';
|
|
25
|
+
import { CONFIG_DIR_NAME } from './brand.js';
|
|
26
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
27
|
+
const __dirname = path.dirname(__filename);
|
|
28
|
+
// Adapters are JS-first and live at <package-root>/clis/.
|
|
29
|
+
// Use findPackageRoot so the path works both in dev (src/main.ts) and prod (dist/src/main.js).
|
|
30
|
+
const BUILTIN_CLIS = path.join(findPackageRoot(__filename), 'clis');
|
|
31
|
+
const USER_CLIS = path.join(os.homedir(), CONFIG_DIR_NAME, 'clis');
|
|
32
|
+
// ── Ultra-fast path: lightweight commands bypass full discovery ──────────
|
|
33
|
+
// These are high-frequency or trivial paths that must not pay the startup tax.
|
|
34
|
+
const argv = process.argv.slice(2);
|
|
35
|
+
if (typeof globalThis.Bun === 'undefined' && !isSupportedNodeVersion(process.version)) {
|
|
36
|
+
process.stderr.write([
|
|
37
|
+
`Webcmd requires Node.js >= ${MIN_SUPPORTED_NODE_MAJOR}.0.0.`,
|
|
38
|
+
`Current runtime: ${process.version}`,
|
|
39
|
+
'Upgrade Node.js, then retry the same command.',
|
|
40
|
+
'',
|
|
41
|
+
].join('\n'));
|
|
42
|
+
process.exit(EXIT_CODES.CONFIG_ERROR);
|
|
43
|
+
}
|
|
44
|
+
if (process.env.WEBCMD_DAEMON_PORT) {
|
|
45
|
+
process.stderr.write(`error: ${unsupportedDaemonPortEnvMessage(process.env.WEBCMD_DAEMON_PORT)}\n`);
|
|
46
|
+
process.exit(EXIT_CODES.CONFIG_ERROR);
|
|
47
|
+
}
|
|
48
|
+
// Fast path: --version (only when it's the top-level intent, not passed to a subcommand)
|
|
49
|
+
// e.g. `webcmd --version` or `webcmd -V`, but NOT `webcmd gh --version`
|
|
50
|
+
if (argv[0] === '--version' || argv[0] === '-V') {
|
|
51
|
+
process.stdout.write(PKG_VERSION + '\n');
|
|
52
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
53
|
+
}
|
|
54
|
+
// Fast path: completion <shell> — print shell script without discovery
|
|
55
|
+
if (argv[0] === 'completion' && argv.length >= 2) {
|
|
56
|
+
if (printCompletionScriptFast(argv[1])) {
|
|
57
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
58
|
+
}
|
|
59
|
+
// Unknown shell — fall through to full path for proper error handling
|
|
60
|
+
}
|
|
61
|
+
// Fast path: --get-completions — read from manifest, skip discovery
|
|
62
|
+
const getCompIdx = process.argv.indexOf('--get-completions');
|
|
63
|
+
if (getCompIdx !== -1) {
|
|
64
|
+
// Only include manifests that actually exist on disk.
|
|
65
|
+
// With sparse override, the user clis dir may exist but have no manifest.
|
|
66
|
+
const manifestPaths = [getCliManifestPath(BUILTIN_CLIS)];
|
|
67
|
+
const userManifest = getCliManifestPath(USER_CLIS);
|
|
68
|
+
try {
|
|
69
|
+
fs.accessSync(userManifest);
|
|
70
|
+
manifestPaths.push(userManifest);
|
|
71
|
+
}
|
|
72
|
+
catch { /* no user manifest */ }
|
|
73
|
+
if (hasAllManifests(manifestPaths)) {
|
|
74
|
+
const rest = process.argv.slice(getCompIdx + 1);
|
|
75
|
+
let cursor;
|
|
76
|
+
const words = [];
|
|
77
|
+
for (let i = 0; i < rest.length; i++) {
|
|
78
|
+
if (rest[i] === '--cursor' && i + 1 < rest.length) {
|
|
79
|
+
cursor = parseInt(rest[i + 1], 10);
|
|
80
|
+
i++;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
words.push(rest[i]);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (cursor === undefined)
|
|
87
|
+
cursor = words.length;
|
|
88
|
+
const candidates = getCompletionsFromManifest(words, cursor, manifestPaths);
|
|
89
|
+
process.stdout.write(candidates.join('\n') + '\n');
|
|
90
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
91
|
+
}
|
|
92
|
+
// No manifest — fall through to full discovery path below
|
|
93
|
+
}
|
|
94
|
+
// ── Full startup path ───────────────────────────────────────────────────
|
|
95
|
+
// Dynamic imports: these are deferred so the fast path above never pays the cost.
|
|
96
|
+
const { discoverClis, discoverPlugins, ensureUserCliCompatShims, ensureUserAdapters } = await import('./discovery.js');
|
|
97
|
+
const { getCompletions } = await import('./completion.js');
|
|
98
|
+
const { runCli } = await import('./cli.js');
|
|
99
|
+
const { emitHook } = await import('./hooks.js');
|
|
100
|
+
const { installNodeNetwork } = await import('./node-network.js');
|
|
101
|
+
const { registerUpdateNoticeOnExit, checkForUpdateBackground } = await import('./update-check.js');
|
|
102
|
+
installNodeNetwork();
|
|
103
|
+
// Parallelise independent startup I/O:
|
|
104
|
+
// - Built-in adapter discovery has no dependency on user-dir setup.
|
|
105
|
+
// - ensureUserCliCompatShims and ensureUserAdapters operate on different paths
|
|
106
|
+
// (~/.webcmd/node_modules/ vs ~/.webcmd/clis/ + adapter-manifest.json).
|
|
107
|
+
// - registerCommand() overwrites on name collision (see registry.ts), so
|
|
108
|
+
// user-CLI discovery MUST run after built-in discovery to preserve the
|
|
109
|
+
// intended override order (user adapters override built-in ones).
|
|
110
|
+
// - discoverPlugins runs last: plugins may override both built-in and user CLIs.
|
|
111
|
+
const skipUserDiscovery = argv[0] === 'convention-audit';
|
|
112
|
+
if (skipUserDiscovery) {
|
|
113
|
+
await discoverClis(BUILTIN_CLIS);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
const [, ,] = await Promise.all([
|
|
117
|
+
ensureUserCliCompatShims(),
|
|
118
|
+
ensureUserAdapters(),
|
|
119
|
+
discoverClis(BUILTIN_CLIS),
|
|
120
|
+
]);
|
|
121
|
+
await discoverClis(USER_CLIS);
|
|
122
|
+
await discoverPlugins();
|
|
123
|
+
}
|
|
124
|
+
// Register exit hook: notice appears after command output (same as npm/gh/yarn)
|
|
125
|
+
registerUpdateNoticeOnExit();
|
|
126
|
+
// Kick off background fetch for next run (non-blocking)
|
|
127
|
+
checkForUpdateBackground();
|
|
128
|
+
// ── Fallback completion: manifest unavailable, use full registry ─────────
|
|
129
|
+
if (getCompIdx !== -1) {
|
|
130
|
+
const rest = process.argv.slice(getCompIdx + 1);
|
|
131
|
+
let cursor;
|
|
132
|
+
const words = [];
|
|
133
|
+
for (let i = 0; i < rest.length; i++) {
|
|
134
|
+
if (rest[i] === '--cursor' && i + 1 < rest.length) {
|
|
135
|
+
cursor = parseInt(rest[i + 1], 10);
|
|
136
|
+
i++;
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
words.push(rest[i]);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (cursor === undefined)
|
|
143
|
+
cursor = words.length;
|
|
144
|
+
const candidates = getCompletions(words, cursor);
|
|
145
|
+
process.stdout.write(candidates.join('\n') + '\n');
|
|
146
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
147
|
+
}
|
|
148
|
+
// Rewrite `webcmd browser <session> <subcommand> ...` so commander (which
|
|
149
|
+
// can't combine a parent positional with subcommand dispatch) sees the internal
|
|
150
|
+
// `--session <name>` flag form. Also refuses the retired `webcmd browser
|
|
151
|
+
// --session foo ...` user form with a friendly usage error.
|
|
152
|
+
const { rewriteBrowserArgv, BrowserSessionArgvError, escapeLeadingDashPositional } = await import('./cli-argv-preprocess.js');
|
|
153
|
+
try {
|
|
154
|
+
let rewritten = rewriteBrowserArgv(process.argv.slice(2));
|
|
155
|
+
// Insert a `--` separator before a required positional whose value starts
|
|
156
|
+
// with `-` (e.g. BOSS 直聘 securityId tokens; #1160). Skipped when the
|
|
157
|
+
// manifest is unavailable so the user-cli / dev paths still work.
|
|
158
|
+
try {
|
|
159
|
+
const manifestPath = getCliManifestPath(BUILTIN_CLIS);
|
|
160
|
+
if (fs.existsSync(manifestPath)) {
|
|
161
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
|
|
162
|
+
if (Array.isArray(manifest))
|
|
163
|
+
rewritten = escapeLeadingDashPositional(rewritten, manifest);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
catch { /* manifest unavailable; skip the dash escape */ }
|
|
167
|
+
process.argv.splice(2, process.argv.length - 2, ...rewritten);
|
|
168
|
+
}
|
|
169
|
+
catch (err) {
|
|
170
|
+
if (err instanceof BrowserSessionArgvError) {
|
|
171
|
+
process.stderr.write(`error: ${err.message}\n`);
|
|
172
|
+
process.exit(EXIT_CODES.GENERIC_ERROR);
|
|
173
|
+
}
|
|
174
|
+
throw err;
|
|
175
|
+
}
|
|
176
|
+
await emitHook('onStartup', { command: '__startup__', args: {} });
|
|
177
|
+
runCli(BUILTIN_CLIS, USER_CLIS);
|