@wootsup/yt-builder-mcp 1.1.5 → 1.1.7
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 +56 -55
- package/dist/auth.d.ts +119 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +226 -0
- package/dist/auth.js.map +1 -0
- package/dist/client.d.ts +84 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +151 -0
- package/dist/client.js.map +1 -0
- package/dist/clients/claude-code.d.ts +18 -0
- package/dist/clients/claude-code.d.ts.map +1 -0
- package/dist/clients/claude-code.js +53 -0
- package/dist/clients/claude-code.js.map +1 -0
- package/dist/clients/claude-desktop.d.ts +19 -0
- package/dist/clients/claude-desktop.d.ts.map +1 -0
- package/dist/clients/claude-desktop.js +56 -0
- package/dist/clients/claude-desktop.js.map +1 -0
- package/dist/clients/cline.d.ts +26 -0
- package/dist/clients/cline.d.ts.map +1 -0
- package/dist/clients/cline.js +80 -0
- package/dist/clients/cline.js.map +1 -0
- package/dist/clients/codex-cli.d.ts +42 -0
- package/dist/clients/codex-cli.d.ts.map +1 -0
- package/dist/clients/codex-cli.js +194 -0
- package/dist/clients/codex-cli.js.map +1 -0
- package/dist/clients/continue.d.ts +13 -0
- package/dist/clients/continue.d.ts.map +1 -0
- package/dist/clients/continue.js +52 -0
- package/dist/clients/continue.js.map +1 -0
- package/dist/clients/cursor.d.ts +12 -0
- package/dist/clients/cursor.d.ts.map +1 -0
- package/dist/clients/cursor.js +38 -0
- package/dist/clients/cursor.js.map +1 -0
- package/dist/clients/gemini-cli.d.ts +18 -0
- package/dist/clients/gemini-cli.d.ts.map +1 -0
- package/dist/clients/gemini-cli.js +44 -0
- package/dist/clients/gemini-cli.js.map +1 -0
- package/dist/clients/home.d.ts +14 -0
- package/dist/clients/home.d.ts.map +1 -0
- package/dist/clients/home.js +20 -0
- package/dist/clients/home.js.map +1 -0
- package/dist/clients/index.d.ts +52 -0
- package/dist/clients/index.d.ts.map +1 -0
- package/dist/clients/index.js +72 -0
- package/dist/clients/index.js.map +1 -0
- package/dist/clients/roo-code.d.ts +23 -0
- package/dist/clients/roo-code.d.ts.map +1 -0
- package/dist/clients/roo-code.js +69 -0
- package/dist/clients/roo-code.js.map +1 -0
- package/dist/clients/zed.d.ts +12 -0
- package/dist/clients/zed.d.ts.map +1 -0
- package/dist/clients/zed.js +41 -0
- package/dist/clients/zed.js.map +1 -0
- package/dist/errors/hints.d.ts +51 -0
- package/dist/errors/hints.d.ts.map +1 -0
- package/dist/errors/hints.js +95 -0
- package/dist/errors/hints.js.map +1 -0
- package/dist/errors/mask.d.ts +35 -0
- package/dist/errors/mask.d.ts.map +1 -0
- package/dist/errors/mask.js +49 -0
- package/dist/errors/mask.js.map +1 -0
- package/dist/errors/sanitize.d.ts +31 -0
- package/dist/errors/sanitize.d.ts.map +1 -0
- package/dist/errors/sanitize.js +100 -0
- package/dist/errors/sanitize.js.map +1 -0
- package/dist/errors.d.ts +42 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +61 -0
- package/dist/errors.js.map +1 -0
- package/dist/gateway/advanced-tool/discovery.d.ts +19 -0
- package/dist/gateway/advanced-tool/discovery.d.ts.map +1 -0
- package/dist/gateway/advanced-tool/discovery.js +53 -0
- package/dist/gateway/advanced-tool/discovery.js.map +1 -0
- package/dist/gateway/advanced-tool/domains.d.ts +47 -0
- package/dist/gateway/advanced-tool/domains.d.ts.map +1 -0
- package/dist/gateway/advanced-tool/domains.js +94 -0
- package/dist/gateway/advanced-tool/domains.js.map +1 -0
- package/dist/gateway/advanced-tool/execute.d.ts +29 -0
- package/dist/gateway/advanced-tool/execute.d.ts.map +1 -0
- package/dist/gateway/advanced-tool/execute.js +54 -0
- package/dist/gateway/advanced-tool/execute.js.map +1 -0
- package/dist/gateway/advanced-tool/index.d.ts +36 -0
- package/dist/gateway/advanced-tool/index.d.ts.map +1 -0
- package/dist/gateway/advanced-tool/index.js +39 -0
- package/dist/gateway/advanced-tool/index.js.map +1 -0
- package/dist/gateway/advanced-tool/register.d.ts +18 -0
- package/dist/gateway/advanced-tool/register.d.ts.map +1 -0
- package/dist/gateway/advanced-tool/register.js +69 -0
- package/dist/gateway/advanced-tool/register.js.map +1 -0
- package/dist/gateway/advanced-tool.d.ts +13 -0
- package/dist/gateway/advanced-tool.d.ts.map +1 -0
- package/dist/gateway/advanced-tool.js +13 -0
- package/dist/gateway/advanced-tool.js.map +1 -0
- package/dist/gateway/capturing-server.d.ts +117 -0
- package/dist/gateway/capturing-server.d.ts.map +1 -0
- package/dist/gateway/capturing-server.js +103 -0
- package/dist/gateway/capturing-server.js.map +1 -0
- package/dist/gateway/essentials.d.ts +50 -0
- package/dist/gateway/essentials.d.ts.map +1 -0
- package/dist/gateway/essentials.js +94 -0
- package/dist/gateway/essentials.js.map +1 -0
- package/dist/gateway/test-support.d.ts +41 -0
- package/dist/gateway/test-support.d.ts.map +1 -0
- package/dist/gateway/test-support.js +60 -0
- package/dist/gateway/test-support.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +111 -0
- package/dist/index.js.map +1 -0
- package/dist/install-skill.d.ts +43 -0
- package/dist/install-skill.d.ts.map +1 -0
- package/dist/install-skill.js +118 -0
- package/dist/install-skill.js.map +1 -0
- package/dist/platform/detect.d.ts +64 -0
- package/dist/platform/detect.d.ts.map +1 -0
- package/dist/platform/detect.js +98 -0
- package/dist/platform/detect.js.map +1 -0
- package/dist/platform/index.d.ts +121 -0
- package/dist/platform/index.d.ts.map +1 -0
- package/dist/platform/index.js +176 -0
- package/dist/platform/index.js.map +1 -0
- package/dist/platform/joomla.d.ts +44 -0
- package/dist/platform/joomla.d.ts.map +1 -0
- package/dist/platform/joomla.js +46 -0
- package/dist/platform/joomla.js.map +1 -0
- package/dist/server.d.ts +110 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +274 -0
- package/dist/server.js.map +1 -0
- package/dist/setup-cli-confirm.d.ts +36 -0
- package/dist/setup-cli-confirm.d.ts.map +1 -0
- package/dist/setup-cli-confirm.js +59 -0
- package/dist/setup-cli-confirm.js.map +1 -0
- package/dist/setup-cli.d.ts +152 -0
- package/dist/setup-cli.d.ts.map +1 -0
- package/dist/setup-cli.js +870 -0
- package/dist/setup-cli.js.map +1 -0
- package/dist/setup-prompts.d.ts +41 -0
- package/dist/setup-prompts.d.ts.map +1 -0
- package/dist/setup-prompts.js +146 -0
- package/dist/setup-prompts.js.map +1 -0
- package/dist/setup-token.d.ts +38 -0
- package/dist/setup-token.d.ts.map +1 -0
- package/dist/setup-token.js +106 -0
- package/dist/setup-token.js.map +1 -0
- package/dist/setup-wizard-defaults.d.ts +43 -0
- package/dist/setup-wizard-defaults.d.ts.map +1 -0
- package/dist/setup-wizard-defaults.js +160 -0
- package/dist/setup-wizard-defaults.js.map +1 -0
- package/dist/setup-wizard-handshake.d.ts +25 -0
- package/dist/setup-wizard-handshake.d.ts.map +1 -0
- package/dist/setup-wizard-handshake.js +103 -0
- package/dist/setup-wizard-handshake.js.map +1 -0
- package/dist/setup-wizard-types.d.ts +164 -0
- package/dist/setup-wizard-types.d.ts.map +1 -0
- package/dist/setup-wizard-types.js +11 -0
- package/dist/setup-wizard-types.js.map +1 -0
- package/dist/setup-wizard.d.ts +33 -0
- package/dist/setup-wizard.d.ts.map +1 -0
- package/dist/setup-wizard.js +199 -0
- package/dist/setup-wizard.js.map +1 -0
- package/dist/setup.d.ts +17 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +33 -0
- package/dist/setup.js.map +1 -0
- package/dist/sites/cli/add-site.d.ts +106 -0
- package/dist/sites/cli/add-site.d.ts.map +1 -0
- package/dist/sites/cli/add-site.js +185 -0
- package/dist/sites/cli/add-site.js.map +1 -0
- package/dist/sites/cli/list-sites.d.ts +40 -0
- package/dist/sites/cli/list-sites.d.ts.map +1 -0
- package/dist/sites/cli/list-sites.js +116 -0
- package/dist/sites/cli/list-sites.js.map +1 -0
- package/dist/sites/cli/remove-site.d.ts +60 -0
- package/dist/sites/cli/remove-site.d.ts.map +1 -0
- package/dist/sites/cli/remove-site.js +120 -0
- package/dist/sites/cli/remove-site.js.map +1 -0
- package/dist/sites/cli/set-default.d.ts +47 -0
- package/dist/sites/cli/set-default.d.ts.map +1 -0
- package/dist/sites/cli/set-default.js +69 -0
- package/dist/sites/cli/set-default.js.map +1 -0
- package/dist/sites/cli/test-site.d.ts +77 -0
- package/dist/sites/cli/test-site.d.ts.map +1 -0
- package/dist/sites/cli/test-site.js +146 -0
- package/dist/sites/cli/test-site.js.map +1 -0
- package/dist/sites/client-pool.d.ts +124 -0
- package/dist/sites/client-pool.d.ts.map +1 -0
- package/dist/sites/client-pool.js +172 -0
- package/dist/sites/client-pool.js.map +1 -0
- package/dist/sites/env-bridge.d.ts +55 -0
- package/dist/sites/env-bridge.d.ts.map +1 -0
- package/dist/sites/env-bridge.js +94 -0
- package/dist/sites/env-bridge.js.map +1 -0
- package/dist/sites/paths.d.ts +21 -0
- package/dist/sites/paths.d.ts.map +1 -0
- package/dist/sites/paths.js +37 -0
- package/dist/sites/paths.js.map +1 -0
- package/dist/sites/probe.d.ts +60 -0
- package/dist/sites/probe.d.ts.map +1 -0
- package/dist/sites/probe.js +112 -0
- package/dist/sites/probe.js.map +1 -0
- package/dist/sites/registry.d.ts +142 -0
- package/dist/sites/registry.d.ts.map +1 -0
- package/dist/sites/registry.js +163 -0
- package/dist/sites/registry.js.map +1 -0
- package/dist/sites/schema.d.ts +90 -0
- package/dist/sites/schema.d.ts.map +1 -0
- package/dist/sites/schema.js +71 -0
- package/dist/sites/schema.js.map +1 -0
- package/dist/sites/secret-resolver.d.ts +115 -0
- package/dist/sites/secret-resolver.d.ts.map +1 -0
- package/dist/sites/secret-resolver.js +225 -0
- package/dist/sites/secret-resolver.js.map +1 -0
- package/dist/sites/store.d.ts +82 -0
- package/dist/sites/store.d.ts.map +1 -0
- package/dist/sites/store.js +198 -0
- package/dist/sites/store.js.map +1 -0
- package/dist/sites/tools/index.d.ts +28 -0
- package/dist/sites/tools/index.d.ts.map +1 -0
- package/dist/sites/tools/index.js +30 -0
- package/dist/sites/tools/index.js.map +1 -0
- package/dist/sites/tools/sites-list.d.ts +48 -0
- package/dist/sites/tools/sites-list.d.ts.map +1 -0
- package/dist/sites/tools/sites-list.js +137 -0
- package/dist/sites/tools/sites-list.js.map +1 -0
- package/dist/sites/tools/sites-test.d.ts +47 -0
- package/dist/sites/tools/sites-test.d.ts.map +1 -0
- package/dist/sites/tools/sites-test.js +148 -0
- package/dist/sites/tools/sites-test.js.map +1 -0
- package/dist/skill-loader.d.ts +111 -0
- package/dist/skill-loader.d.ts.map +1 -0
- package/dist/skill-loader.js +191 -0
- package/dist/skill-loader.js.map +1 -0
- package/dist/tools/elements/builders.d.ts +24 -0
- package/dist/tools/elements/builders.d.ts.map +1 -0
- package/dist/tools/elements/builders.js +199 -0
- package/dist/tools/elements/builders.js.map +1 -0
- package/dist/tools/elements/handlers-write.d.ts +49 -0
- package/dist/tools/elements/handlers-write.d.ts.map +1 -0
- package/dist/tools/elements/handlers-write.js +148 -0
- package/dist/tools/elements/handlers-write.js.map +1 -0
- package/dist/tools/elements/handlers.d.ts +70 -0
- package/dist/tools/elements/handlers.d.ts.map +1 -0
- package/dist/tools/elements/handlers.js +292 -0
- package/dist/tools/elements/handlers.js.map +1 -0
- package/dist/tools/elements/index.d.ts +28 -0
- package/dist/tools/elements/index.d.ts.map +1 -0
- package/dist/tools/elements/index.js +27 -0
- package/dist/tools/elements/index.js.map +1 -0
- package/dist/tools/elements.d.ts +13 -0
- package/dist/tools/elements.d.ts.map +1 -0
- package/dist/tools/elements.js +13 -0
- package/dist/tools/elements.js.map +1 -0
- package/dist/tools/elicitation.d.ts +87 -0
- package/dist/tools/elicitation.d.ts.map +1 -0
- package/dist/tools/elicitation.js +100 -0
- package/dist/tools/elicitation.js.map +1 -0
- package/dist/tools/format/elements-format.d.ts +40 -0
- package/dist/tools/format/elements-format.d.ts.map +1 -0
- package/dist/tools/format/elements-format.js +194 -0
- package/dist/tools/format/elements-format.js.map +1 -0
- package/dist/tools/format/health-format.d.ts +79 -0
- package/dist/tools/format/health-format.d.ts.map +1 -0
- package/dist/tools/format/health-format.js +181 -0
- package/dist/tools/format/health-format.js.map +1 -0
- package/dist/tools/format/inspection-format.d.ts +59 -0
- package/dist/tools/format/inspection-format.d.ts.map +1 -0
- package/dist/tools/format/inspection-format.js +303 -0
- package/dist/tools/format/inspection-format.js.map +1 -0
- package/dist/tools/format/pages-format.d.ts +57 -0
- package/dist/tools/format/pages-format.d.ts.map +1 -0
- package/dist/tools/format/pages-format.js +157 -0
- package/dist/tools/format/pages-format.js.map +1 -0
- package/dist/tools/format/sources-format.d.ts +44 -0
- package/dist/tools/format/sources-format.d.ts.map +1 -0
- package/dist/tools/format/sources-format.js +242 -0
- package/dist/tools/format/sources-format.js.map +1 -0
- package/dist/tools/health.d.ts +22 -0
- package/dist/tools/health.d.ts.map +1 -0
- package/dist/tools/health.js +220 -0
- package/dist/tools/health.js.map +1 -0
- package/dist/tools/index.d.ts +30 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +39 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/inspection.d.ts +14 -0
- package/dist/tools/inspection.d.ts.map +1 -0
- package/dist/tools/inspection.js +208 -0
- package/dist/tools/inspection.js.map +1 -0
- package/dist/tools/layout-flatten.d.ts +75 -0
- package/dist/tools/layout-flatten.d.ts.map +1 -0
- package/dist/tools/layout-flatten.js +107 -0
- package/dist/tools/layout-flatten.js.map +1 -0
- package/dist/tools/multi-items/builders.d.ts +23 -0
- package/dist/tools/multi-items/builders.d.ts.map +1 -0
- package/dist/tools/multi-items/builders.js +78 -0
- package/dist/tools/multi-items/builders.js.map +1 -0
- package/dist/tools/multi-items/clean-implode-handler.d.ts +36 -0
- package/dist/tools/multi-items/clean-implode-handler.d.ts.map +1 -0
- package/dist/tools/multi-items/clean-implode-handler.js +57 -0
- package/dist/tools/multi-items/clean-implode-handler.js.map +1 -0
- package/dist/tools/multi-items/index.d.ts +18 -0
- package/dist/tools/multi-items/index.d.ts.map +1 -0
- package/dist/tools/multi-items/index.js +18 -0
- package/dist/tools/multi-items/index.js.map +1 -0
- package/dist/tools/multi-items/inspect-handler.d.ts +41 -0
- package/dist/tools/multi-items/inspect-handler.d.ts.map +1 -0
- package/dist/tools/multi-items/inspect-handler.js +50 -0
- package/dist/tools/multi-items/inspect-handler.js.map +1 -0
- package/dist/tools/multi-items/item-container-map.d.ts +41 -0
- package/dist/tools/multi-items/item-container-map.d.ts.map +1 -0
- package/dist/tools/multi-items/item-container-map.js +73 -0
- package/dist/tools/multi-items/item-container-map.js.map +1 -0
- package/dist/tools/pages/builders.d.ts +14 -0
- package/dist/tools/pages/builders.d.ts.map +1 -0
- package/dist/tools/pages/builders.js +136 -0
- package/dist/tools/pages/builders.js.map +1 -0
- package/dist/tools/pages/handlers-read.d.ts +33 -0
- package/dist/tools/pages/handlers-read.d.ts.map +1 -0
- package/dist/tools/pages/handlers-read.js +203 -0
- package/dist/tools/pages/handlers-read.js.map +1 -0
- package/dist/tools/pages/handlers-write.d.ts +21 -0
- package/dist/tools/pages/handlers-write.d.ts.map +1 -0
- package/dist/tools/pages/handlers-write.js +52 -0
- package/dist/tools/pages/handlers-write.js.map +1 -0
- package/dist/tools/pages/index.d.ts +17 -0
- package/dist/tools/pages/index.d.ts.map +1 -0
- package/dist/tools/pages/index.js +17 -0
- package/dist/tools/pages/index.js.map +1 -0
- package/dist/tools/pages/schemas.d.ts +45 -0
- package/dist/tools/pages/schemas.d.ts.map +1 -0
- package/dist/tools/pages/schemas.js +82 -0
- package/dist/tools/pages/schemas.js.map +1 -0
- package/dist/tools/pages.d.ts +13 -0
- package/dist/tools/pages.d.ts.map +1 -0
- package/dist/tools/pages.js +13 -0
- package/dist/tools/pages.js.map +1 -0
- package/dist/tools/pool-resolve-helper.d.ts +83 -0
- package/dist/tools/pool-resolve-helper.d.ts.map +1 -0
- package/dist/tools/pool-resolve-helper.js +148 -0
- package/dist/tools/pool-resolve-helper.js.map +1 -0
- package/dist/tools/progress-phases.d.ts +46 -0
- package/dist/tools/progress-phases.d.ts.map +1 -0
- package/dist/tools/progress-phases.js +48 -0
- package/dist/tools/progress-phases.js.map +1 -0
- package/dist/tools/shared-schemas.d.ts +25 -0
- package/dist/tools/shared-schemas.d.ts.map +1 -0
- package/dist/tools/shared-schemas.js +59 -0
- package/dist/tools/shared-schemas.js.map +1 -0
- package/dist/tools/sources/builders.d.ts +13 -0
- package/dist/tools/sources/builders.d.ts.map +1 -0
- package/dist/tools/sources/builders.js +118 -0
- package/dist/tools/sources/builders.js.map +1 -0
- package/dist/tools/sources/handlers-bind.d.ts +28 -0
- package/dist/tools/sources/handlers-bind.d.ts.map +1 -0
- package/dist/tools/sources/handlers-bind.js +129 -0
- package/dist/tools/sources/handlers-bind.js.map +1 -0
- package/dist/tools/sources/handlers.d.ts +47 -0
- package/dist/tools/sources/handlers.d.ts.map +1 -0
- package/dist/tools/sources/handlers.js +108 -0
- package/dist/tools/sources/handlers.js.map +1 -0
- package/dist/tools/sources/index.d.ts +19 -0
- package/dist/tools/sources/index.d.ts.map +1 -0
- package/dist/tools/sources/index.js +18 -0
- package/dist/tools/sources/index.js.map +1 -0
- package/dist/tools/sources.d.ts +14 -0
- package/dist/tools/sources.d.ts.map +1 -0
- package/dist/tools/sources.js +14 -0
- package/dist/tools/sources.js.map +1 -0
- package/dist/tools/sparse-fields.d.ts +106 -0
- package/dist/tools/sparse-fields.d.ts.map +1 -0
- package/dist/tools/sparse-fields.js +165 -0
- package/dist/tools/sparse-fields.js.map +1 -0
- package/dist/tools/tool-builder/annotations.d.ts +55 -0
- package/dist/tools/tool-builder/annotations.d.ts.map +1 -0
- package/dist/tools/tool-builder/annotations.js +86 -0
- package/dist/tools/tool-builder/annotations.js.map +1 -0
- package/dist/tools/tool-builder/define.d.ts +31 -0
- package/dist/tools/tool-builder/define.d.ts.map +1 -0
- package/dist/tools/tool-builder/define.js +31 -0
- package/dist/tools/tool-builder/define.js.map +1 -0
- package/dist/tools/tool-builder/index.d.ts +28 -0
- package/dist/tools/tool-builder/index.d.ts.map +1 -0
- package/dist/tools/tool-builder/index.js +27 -0
- package/dist/tools/tool-builder/index.js.map +1 -0
- package/dist/tools/tool-builder/results.d.ts +104 -0
- package/dist/tools/tool-builder/results.d.ts.map +1 -0
- package/dist/tools/tool-builder/results.js +224 -0
- package/dist/tools/tool-builder/results.js.map +1 -0
- package/dist/tools/tool-builder/types.d.ts +97 -0
- package/dist/tools/tool-builder/types.d.ts.map +1 -0
- package/dist/tools/tool-builder/types.js +9 -0
- package/dist/tools/tool-builder/types.js.map +1 -0
- package/dist/tools/tool-builder.d.ts +16 -0
- package/dist/tools/tool-builder.d.ts.map +1 -0
- package/dist/tools/tool-builder.js +16 -0
- package/dist/tools/tool-builder.js.map +1 -0
- package/manifest.json +1 -1
- package/package.json +2 -1
- package/skills/yt-builder-mcp/SKILL.md +108 -107
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* W9 — `add-site` CLI subcommand.
|
|
3
|
+
*
|
|
4
|
+
* Adds (or, with `--yes`, overwrites) a single site entry in the
|
|
5
|
+
* on-disk `sites.json` registry. Pure file-IO surface: loads, mutates
|
|
6
|
+
* in memory, and atomic-saves through {@link saveSitesFile}. NEVER
|
|
7
|
+
* touches the network — the optional connectivity probe lives in
|
|
8
|
+
* `test-site` so callers can compose the two.
|
|
9
|
+
*
|
|
10
|
+
* Design choices (per plan §W9):
|
|
11
|
+
* - **Mutual-exclusion `--token` vs `--token-ref`.** Exactly one
|
|
12
|
+
* credential field must be set; reuses the W1 SiteEntry refine() as
|
|
13
|
+
* the on-disk guard but also pre-checks here for an immediate CLI
|
|
14
|
+
* error rather than a Zod surprise at save time.
|
|
15
|
+
* - **Auto-default on first site.** If `sites.json` is empty (or did
|
|
16
|
+
* not exist), the first added entry gets `is_default: true` AND
|
|
17
|
+
* `default_site_id` is updated, regardless of whether `--default`
|
|
18
|
+
* was passed. The plan calls this out explicitly so users don't
|
|
19
|
+
* end up with a registry that has sites but no default.
|
|
20
|
+
* - **Idempotent + safe overwrite.** Re-adding the same `site_id`
|
|
21
|
+
* fails with exit code 2 unless `--yes` is passed; with `--yes` the
|
|
22
|
+
* existing entry is replaced AND its `is_default` flag is preserved
|
|
23
|
+
* so an overwrite of the default site stays the default.
|
|
24
|
+
* - **Defence-in-depth platform default.** When `--platform` is
|
|
25
|
+
* omitted the entry stores `'auto'`, which the W3 registry resolves
|
|
26
|
+
* at first use via the runtime probe.
|
|
27
|
+
*
|
|
28
|
+
* @license MIT
|
|
29
|
+
*/
|
|
30
|
+
import { type SitesFileT } from '../schema.js';
|
|
31
|
+
/**
|
|
32
|
+
* Parsed shape consumed by {@link addSiteCommand}. The dispatcher
|
|
33
|
+
* (`setup-cli.ts:runCli`) translates argv flags into this object and
|
|
34
|
+
* the public test surface drives it directly so flag parsing is
|
|
35
|
+
* exercised separately from the file-IO and registry-mutation logic.
|
|
36
|
+
*/
|
|
37
|
+
export interface AddSiteArgs {
|
|
38
|
+
readonly url: string;
|
|
39
|
+
/** Inline plain bearer; mutually exclusive with `tokenRef`. */
|
|
40
|
+
readonly token?: string;
|
|
41
|
+
/** 1Password Secret Reference; mutually exclusive with `token`. */
|
|
42
|
+
readonly tokenRef?: string;
|
|
43
|
+
/** Optional explicit platform hint; defaults to `'auto'`. */
|
|
44
|
+
readonly platform?: 'auto' | 'wordpress' | 'joomla';
|
|
45
|
+
/** Optional human-readable label (≤120 chars). */
|
|
46
|
+
readonly label?: string;
|
|
47
|
+
/** When true, mark the new site as the default. Ignored if it's
|
|
48
|
+
* the first site (auto-default applies). */
|
|
49
|
+
readonly default?: boolean;
|
|
50
|
+
/** Override the site_id slug; defaults to `'default'`. */
|
|
51
|
+
readonly siteId?: string;
|
|
52
|
+
/** Skip "this site_id exists" guard and overwrite instead. */
|
|
53
|
+
readonly yes?: boolean;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Outcome of a successful add. The CLI dispatcher uses this to render
|
|
57
|
+
* the "Site '<id>' added (default)." line + the restart hint. Returned
|
|
58
|
+
* as a value (not stdout-side) so tests can assert without parsing
|
|
59
|
+
* captured stdout.
|
|
60
|
+
*/
|
|
61
|
+
export interface AddSiteResult {
|
|
62
|
+
/** site_id that was written. */
|
|
63
|
+
readonly siteId: string;
|
|
64
|
+
/** Path of the sites.json that was written. */
|
|
65
|
+
readonly path: string;
|
|
66
|
+
/** True when this site was first AND auto-promoted to default. */
|
|
67
|
+
readonly becameDefault: boolean;
|
|
68
|
+
/** True when an existing entry with the same site_id was replaced. */
|
|
69
|
+
readonly overwritten: boolean;
|
|
70
|
+
/** True when --default flag was honoured (and not just auto-default). */
|
|
71
|
+
readonly defaultRequested: boolean;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Domain-tagged CLI failure. The dispatcher maps these to exit codes
|
|
75
|
+
* (typically 2 for "input invalid" + a friendly stderr line). Carries
|
|
76
|
+
* a `code` so tests / callers can branch without substring matching.
|
|
77
|
+
*/
|
|
78
|
+
export declare class AddSiteError extends Error {
|
|
79
|
+
readonly code: string;
|
|
80
|
+
constructor(code: string, message: string);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Test/IO injection seam. Production callers use the default
|
|
84
|
+
* implementations imported above; tests inject in-memory store
|
|
85
|
+
* helpers and a frozen `now()`.
|
|
86
|
+
*/
|
|
87
|
+
export interface AddSiteDeps {
|
|
88
|
+
readonly load: (path: string) => Promise<SitesFileT>;
|
|
89
|
+
readonly save: (path: string, data: SitesFileT) => Promise<void>;
|
|
90
|
+
readonly now?: () => string;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Add (or overwrite-with-`--yes`) a single site entry. See file header
|
|
94
|
+
* for the auto-default / mutual-exclusion / idempotency contract.
|
|
95
|
+
*
|
|
96
|
+
* @throws {AddSiteError} on any input-validation failure or when the
|
|
97
|
+
* site_id already exists and `--yes` was not passed.
|
|
98
|
+
*/
|
|
99
|
+
export declare function addSiteCommand(args: AddSiteArgs, path: string, deps: AddSiteDeps): Promise<AddSiteResult>;
|
|
100
|
+
/**
|
|
101
|
+
* Default IO deps wired to the real store. Exported so the dispatcher
|
|
102
|
+
* + the W9 wizard-internal call-site can both share them without
|
|
103
|
+
* duplicating the import surface.
|
|
104
|
+
*/
|
|
105
|
+
export declare const DEFAULT_ADD_SITE_DEPS: AddSiteDeps;
|
|
106
|
+
//# sourceMappingURL=add-site.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"add-site.d.ts","sourceRoot":"","sources":["../../../src/sites/cli/add-site.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAGH,OAAO,EAKH,KAAK,UAAU,EAClB,MAAM,cAAc,CAAC;AAEtB;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,+DAA+D;IAC/D,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,mEAAmE;IACnE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,6DAA6D;IAC7D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAC;IACpD,kDAAkD;IAClD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;iDAC6C;IAC7C,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,0DAA0D;IAC1D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,8DAA8D;IAC9D,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC1B,gCAAgC;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,+CAA+C;IAC/C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,kEAAkE;IAClE,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,sEAAsE;IACtE,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,yEAAyE;IACzE,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;CACtC;AAED;;;;GAIG;AACH,qBAAa,YAAa,SAAQ,KAAK;aACP,IAAI,EAAE,MAAM;gBAAZ,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAI5D;AA0GD;;;;GAIG;AACH,MAAM,WAAW,WAAW;IACxB,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IACrD,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CAC/B;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAChC,IAAI,EAAE,WAAW,EACjB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,WAAW,GAClB,OAAO,CAAC,aAAa,CAAC,CAoExB;AAED;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,EAAE,WAQnC,CAAC"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* W9 — `add-site` CLI subcommand.
|
|
3
|
+
*
|
|
4
|
+
* Adds (or, with `--yes`, overwrites) a single site entry in the
|
|
5
|
+
* on-disk `sites.json` registry. Pure file-IO surface: loads, mutates
|
|
6
|
+
* in memory, and atomic-saves through {@link saveSitesFile}. NEVER
|
|
7
|
+
* touches the network — the optional connectivity probe lives in
|
|
8
|
+
* `test-site` so callers can compose the two.
|
|
9
|
+
*
|
|
10
|
+
* Design choices (per plan §W9):
|
|
11
|
+
* - **Mutual-exclusion `--token` vs `--token-ref`.** Exactly one
|
|
12
|
+
* credential field must be set; reuses the W1 SiteEntry refine() as
|
|
13
|
+
* the on-disk guard but also pre-checks here for an immediate CLI
|
|
14
|
+
* error rather than a Zod surprise at save time.
|
|
15
|
+
* - **Auto-default on first site.** If `sites.json` is empty (or did
|
|
16
|
+
* not exist), the first added entry gets `is_default: true` AND
|
|
17
|
+
* `default_site_id` is updated, regardless of whether `--default`
|
|
18
|
+
* was passed. The plan calls this out explicitly so users don't
|
|
19
|
+
* end up with a registry that has sites but no default.
|
|
20
|
+
* - **Idempotent + safe overwrite.** Re-adding the same `site_id`
|
|
21
|
+
* fails with exit code 2 unless `--yes` is passed; with `--yes` the
|
|
22
|
+
* existing entry is replaced AND its `is_default` flag is preserved
|
|
23
|
+
* so an overwrite of the default site stays the default.
|
|
24
|
+
* - **Defence-in-depth platform default.** When `--platform` is
|
|
25
|
+
* omitted the entry stores `'auto'`, which the W3 registry resolves
|
|
26
|
+
* at first use via the runtime probe.
|
|
27
|
+
*
|
|
28
|
+
* @license MIT
|
|
29
|
+
*/
|
|
30
|
+
import { loadSitesFile, saveSitesFile } from '../store.js';
|
|
31
|
+
import { BEARER_REGEX, OP_REF_REGEX, SITE_ID_REGEX, } from '../schema.js';
|
|
32
|
+
/**
|
|
33
|
+
* Domain-tagged CLI failure. The dispatcher maps these to exit codes
|
|
34
|
+
* (typically 2 for "input invalid" + a friendly stderr line). Carries
|
|
35
|
+
* a `code` so tests / callers can branch without substring matching.
|
|
36
|
+
*/
|
|
37
|
+
export class AddSiteError extends Error {
|
|
38
|
+
code;
|
|
39
|
+
constructor(code, message) {
|
|
40
|
+
super(message);
|
|
41
|
+
this.code = code;
|
|
42
|
+
this.name = 'AddSiteError';
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Build a new {@link SiteEntryT} from validated CLI args. Pure +
|
|
47
|
+
* deterministic so tests can drive it directly. `added_at` uses the
|
|
48
|
+
* supplied `now` so snapshot tests don't drift on clock.
|
|
49
|
+
*/
|
|
50
|
+
function buildEntry(args, siteId, isDefault, now) {
|
|
51
|
+
const base = {
|
|
52
|
+
site_id: siteId,
|
|
53
|
+
url: args.url,
|
|
54
|
+
platform: args.platform ?? 'auto',
|
|
55
|
+
is_default: isDefault,
|
|
56
|
+
added_at: now(),
|
|
57
|
+
...(args.label !== undefined && args.label.length > 0
|
|
58
|
+
? { label: args.label }
|
|
59
|
+
: {}),
|
|
60
|
+
};
|
|
61
|
+
if (args.token !== undefined && args.token.length > 0) {
|
|
62
|
+
return { ...base, bearer: args.token };
|
|
63
|
+
}
|
|
64
|
+
// token-ref guaranteed by validateArgs.
|
|
65
|
+
return { ...base, bearer_ref: args.tokenRef };
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Validate the CLI arg combination before any file IO. Throws
|
|
69
|
+
* {@link AddSiteError} with a stable `code` on the first violation.
|
|
70
|
+
*/
|
|
71
|
+
function validateArgs(args) {
|
|
72
|
+
if (args.url === undefined || args.url.length === 0) {
|
|
73
|
+
throw new AddSiteError('URL_REQUIRED', 'Flag --url is required.');
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
void new URL(args.url);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
throw new AddSiteError('URL_INVALID', `--url is not a valid web address: ${args.url}`);
|
|
80
|
+
}
|
|
81
|
+
const hasToken = args.token !== undefined && args.token.length > 0;
|
|
82
|
+
const hasRef = args.tokenRef !== undefined && args.tokenRef.length > 0;
|
|
83
|
+
if (!hasToken && !hasRef) {
|
|
84
|
+
throw new AddSiteError('BEARER_REQUIRED', 'Either --token <bearer> or --token-ref <op://...> is required.');
|
|
85
|
+
}
|
|
86
|
+
if (hasToken && hasRef) {
|
|
87
|
+
throw new AddSiteError('BEARER_MUTUAL_EXCLUSION', '--token and --token-ref are mutually exclusive. Pick one.');
|
|
88
|
+
}
|
|
89
|
+
if (hasToken && !BEARER_REGEX.test(args.token)) {
|
|
90
|
+
throw new AddSiteError('BEARER_INVALID', '--token does not match the expected Bearer shape ' +
|
|
91
|
+
'(ytb_(live|test)_<payload>.<signature>).');
|
|
92
|
+
}
|
|
93
|
+
if (hasRef && !OP_REF_REGEX.test(args.tokenRef)) {
|
|
94
|
+
throw new AddSiteError('TOKEN_REF_INVALID', '--token-ref does not match the expected 1Password ref shape ' +
|
|
95
|
+
'(op://<vault>/<item>/<field>).');
|
|
96
|
+
}
|
|
97
|
+
const siteId = args.siteId ?? 'default';
|
|
98
|
+
if (!SITE_ID_REGEX.test(siteId)) {
|
|
99
|
+
throw new AddSiteError('SITE_ID_INVALID', `--site-id "${siteId}" must match letters / digits / dash / underscore only.`);
|
|
100
|
+
}
|
|
101
|
+
if (args.platform !== undefined
|
|
102
|
+
&& args.platform !== 'auto'
|
|
103
|
+
&& args.platform !== 'wordpress'
|
|
104
|
+
&& args.platform !== 'joomla') {
|
|
105
|
+
throw new AddSiteError('PLATFORM_INVALID', `--platform must be one of: auto, wordpress, joomla (got "${String(args.platform)}").`);
|
|
106
|
+
}
|
|
107
|
+
if (args.label !== undefined && args.label.length > 120) {
|
|
108
|
+
throw new AddSiteError('LABEL_TOO_LONG', `--label exceeds 120 characters (got ${String(args.label.length)}).`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Add (or overwrite-with-`--yes`) a single site entry. See file header
|
|
113
|
+
* for the auto-default / mutual-exclusion / idempotency contract.
|
|
114
|
+
*
|
|
115
|
+
* @throws {AddSiteError} on any input-validation failure or when the
|
|
116
|
+
* site_id already exists and `--yes` was not passed.
|
|
117
|
+
*/
|
|
118
|
+
export async function addSiteCommand(args, path, deps) {
|
|
119
|
+
validateArgs(args);
|
|
120
|
+
const siteId = args.siteId ?? 'default';
|
|
121
|
+
const now = deps.now ?? (() => new Date().toISOString());
|
|
122
|
+
const file = await deps.load(path);
|
|
123
|
+
const existingIndex = file.sites.findIndex((s) => s.site_id === siteId);
|
|
124
|
+
const exists = existingIndex >= 0;
|
|
125
|
+
if (exists && args.yes !== true) {
|
|
126
|
+
throw new AddSiteError('SITE_ID_EXISTS', `Site "${siteId}" already exists in ${path}. ` +
|
|
127
|
+
`Pass --yes to overwrite or pick a different --site-id.`);
|
|
128
|
+
}
|
|
129
|
+
// Auto-default policy:
|
|
130
|
+
// - first ever site → always default (regardless of --default).
|
|
131
|
+
// - subsequent sites with --default → flip is_default + update
|
|
132
|
+
// file.default_site_id.
|
|
133
|
+
// - overwrite of an existing site → preserve its prior is_default
|
|
134
|
+
// UNLESS --default was explicitly passed (then we promote it).
|
|
135
|
+
const isFirstEver = !exists && file.sites.length === 0;
|
|
136
|
+
const wantsDefault = args.default === true || isFirstEver;
|
|
137
|
+
let isDefault = wantsDefault;
|
|
138
|
+
if (exists && !wantsDefault) {
|
|
139
|
+
const prior = file.sites[existingIndex];
|
|
140
|
+
if (prior !== undefined)
|
|
141
|
+
isDefault = prior.is_default;
|
|
142
|
+
}
|
|
143
|
+
const entry = buildEntry(args, siteId, isDefault, now);
|
|
144
|
+
// Build the new sites array — replace in place on overwrite to
|
|
145
|
+
// preserve insertion order, append otherwise.
|
|
146
|
+
const nextSites = exists
|
|
147
|
+
? file.sites.map((s, i) => (i === existingIndex ? entry : s))
|
|
148
|
+
: [...file.sites, entry];
|
|
149
|
+
// When this entry becomes the default, clear any other site's
|
|
150
|
+
// is_default flag so the W1 invariant (at most one default) holds.
|
|
151
|
+
const sitesWithDefaultFixed = isDefault
|
|
152
|
+
? nextSites.map((s) => s.site_id === siteId ? s : { ...s, is_default: false })
|
|
153
|
+
: nextSites;
|
|
154
|
+
const defaultSiteId = isDefault
|
|
155
|
+
? siteId
|
|
156
|
+
: file.default_site_id;
|
|
157
|
+
const nextFile = {
|
|
158
|
+
schema_version: 1,
|
|
159
|
+
default_site_id: defaultSiteId,
|
|
160
|
+
sites: sitesWithDefaultFixed,
|
|
161
|
+
};
|
|
162
|
+
await deps.save(path, nextFile);
|
|
163
|
+
return {
|
|
164
|
+
siteId,
|
|
165
|
+
path,
|
|
166
|
+
becameDefault: isFirstEver,
|
|
167
|
+
overwritten: exists,
|
|
168
|
+
defaultRequested: args.default === true,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Default IO deps wired to the real store. Exported so the dispatcher
|
|
173
|
+
* + the W9 wizard-internal call-site can both share them without
|
|
174
|
+
* duplicating the import surface.
|
|
175
|
+
*/
|
|
176
|
+
export const DEFAULT_ADD_SITE_DEPS = {
|
|
177
|
+
// W12-R1.3 (A1-N-01): saveSitesFile is already a static import
|
|
178
|
+
// above; switch load to the same static binding so the runtime
|
|
179
|
+
// dependency graph matches the import graph and bundlers/tree-
|
|
180
|
+
// shakers don't have to chase a dynamic import for a path that's
|
|
181
|
+
// always reached.
|
|
182
|
+
load: loadSitesFile,
|
|
183
|
+
save: saveSitesFile,
|
|
184
|
+
};
|
|
185
|
+
//# sourceMappingURL=add-site.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"add-site.js","sourceRoot":"","sources":["../../../src/sites/cli/add-site.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* W9 — `list-sites` CLI subcommand.
|
|
3
|
+
*
|
|
4
|
+
* Reads `sites.json`, prints a tabular view matching the structure of
|
|
5
|
+
* the `yootheme_builder_sites_list` MCP tool. NEVER reaches for the
|
|
6
|
+
* secret-resolver — the table only exposes `bearer_source: 'plain' |
|
|
7
|
+
* 'op'` so an in-progress 1Password unlock cannot wedge the CLI.
|
|
8
|
+
*
|
|
9
|
+
* Output contract (verbatim columns, in order):
|
|
10
|
+
* SITE_ID URL PLATFORM DEFAULT LABEL BEARER
|
|
11
|
+
*
|
|
12
|
+
* Empty registry prints "(no sites configured)" + a discovery hint
|
|
13
|
+
* pointing at `add-site`. The exit code is always 0 on a successful
|
|
14
|
+
* read — even an empty registry is a normal state, not an error.
|
|
15
|
+
*
|
|
16
|
+
* @license MIT
|
|
17
|
+
*/
|
|
18
|
+
import type { SitesFileT } from '../schema.js';
|
|
19
|
+
/** Test-injection seam — production callers use the defaults below. */
|
|
20
|
+
export interface ListSitesDeps {
|
|
21
|
+
readonly load: (path: string) => Promise<SitesFileT>;
|
|
22
|
+
readonly log?: (line: string) => void;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Render the sites registry to stdout. Returns the rendered lines so
|
|
26
|
+
* tests can assert without parsing captured stdout; the caller is
|
|
27
|
+
* still expected to print them via `deps.log` for the live CLI path.
|
|
28
|
+
*/
|
|
29
|
+
export declare function listSitesCommand(path: string, deps: ListSitesDeps): Promise<readonly string[]>;
|
|
30
|
+
/**
|
|
31
|
+
* Pure renderer — exported for direct test coverage of the layout.
|
|
32
|
+
* Layout: fixed-width left-padded columns, header on row 0, divider on
|
|
33
|
+
* row 1, one row per site. Last line always carries the discovery hint
|
|
34
|
+
* (or empty-registry hint) so a Maria-class user always sees the next
|
|
35
|
+
* step.
|
|
36
|
+
*/
|
|
37
|
+
export declare function renderSitesTable(file: SitesFileT): readonly string[];
|
|
38
|
+
/** Default deps wired to the real store. */
|
|
39
|
+
export declare const DEFAULT_LIST_SITES_DEPS: ListSitesDeps;
|
|
40
|
+
//# sourceMappingURL=list-sites.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-sites.d.ts","sourceRoot":"","sources":["../../../src/sites/cli/list-sites.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,uEAAuE;AACvE,MAAM,WAAW,aAAa;IAC1B,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IACrD,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CAClC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,aAAa,GACpB,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAO5B;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS,MAAM,EAAE,CA8EpE;AAED,4CAA4C;AAC5C,eAAO,MAAM,uBAAuB,EAAE,aAErC,CAAC"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* W9 — `list-sites` CLI subcommand.
|
|
3
|
+
*
|
|
4
|
+
* Reads `sites.json`, prints a tabular view matching the structure of
|
|
5
|
+
* the `yootheme_builder_sites_list` MCP tool. NEVER reaches for the
|
|
6
|
+
* secret-resolver — the table only exposes `bearer_source: 'plain' |
|
|
7
|
+
* 'op'` so an in-progress 1Password unlock cannot wedge the CLI.
|
|
8
|
+
*
|
|
9
|
+
* Output contract (verbatim columns, in order):
|
|
10
|
+
* SITE_ID URL PLATFORM DEFAULT LABEL BEARER
|
|
11
|
+
*
|
|
12
|
+
* Empty registry prints "(no sites configured)" + a discovery hint
|
|
13
|
+
* pointing at `add-site`. The exit code is always 0 on a successful
|
|
14
|
+
* read — even an empty registry is a normal state, not an error.
|
|
15
|
+
*
|
|
16
|
+
* @license MIT
|
|
17
|
+
*/
|
|
18
|
+
import { SiteRegistry } from '../registry.js';
|
|
19
|
+
import { loadSitesFile } from '../store.js';
|
|
20
|
+
/**
|
|
21
|
+
* Render the sites registry to stdout. Returns the rendered lines so
|
|
22
|
+
* tests can assert without parsing captured stdout; the caller is
|
|
23
|
+
* still expected to print them via `deps.log` for the live CLI path.
|
|
24
|
+
*/
|
|
25
|
+
export async function listSitesCommand(path, deps) {
|
|
26
|
+
const file = await deps.load(path);
|
|
27
|
+
const lines = renderSitesTable(file);
|
|
28
|
+
if (deps.log !== undefined) {
|
|
29
|
+
for (const line of lines)
|
|
30
|
+
deps.log(line);
|
|
31
|
+
}
|
|
32
|
+
return lines;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Pure renderer — exported for direct test coverage of the layout.
|
|
36
|
+
* Layout: fixed-width left-padded columns, header on row 0, divider on
|
|
37
|
+
* row 1, one row per site. Last line always carries the discovery hint
|
|
38
|
+
* (or empty-registry hint) so a Maria-class user always sees the next
|
|
39
|
+
* step.
|
|
40
|
+
*/
|
|
41
|
+
export function renderSitesTable(file) {
|
|
42
|
+
if (file.sites.length === 0) {
|
|
43
|
+
return [
|
|
44
|
+
'(no sites configured)',
|
|
45
|
+
'',
|
|
46
|
+
'Add one with: yt-builder-mcp add-site --url https://example.com --token <bearer>',
|
|
47
|
+
];
|
|
48
|
+
}
|
|
49
|
+
// Build a registry purely so we can reuse listForDisplay's shape.
|
|
50
|
+
// The platform probe is never triggered because this CLI never
|
|
51
|
+
// calls platformFor().
|
|
52
|
+
const registry = new SiteRegistry(file);
|
|
53
|
+
const rows = registry.listForDisplay();
|
|
54
|
+
const defaultSiteId = registry.defaultSiteId();
|
|
55
|
+
// W12-R1.3 — bearer_source column dropped for consistency with the
|
|
56
|
+
// MCP `sites_list` tool + the `sites://current` resource. The CLI
|
|
57
|
+
// never needed it (the operator already knows which form they
|
|
58
|
+
// chose); keeping it here would be a divergence surface only.
|
|
59
|
+
const COLUMNS = [
|
|
60
|
+
{ key: 'site_id', label: 'SITE_ID', width: 20 },
|
|
61
|
+
{ key: 'url', label: 'URL', width: 36 },
|
|
62
|
+
{ key: 'platform', label: 'PLATFORM', width: 11 },
|
|
63
|
+
{ key: 'default', label: 'DEFAULT', width: 9 },
|
|
64
|
+
{ key: 'label', label: 'LABEL', width: 24 },
|
|
65
|
+
];
|
|
66
|
+
function pad(text, width) {
|
|
67
|
+
if (text.length >= width)
|
|
68
|
+
return text.slice(0, width);
|
|
69
|
+
return text + ' '.repeat(width - text.length);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* W12-R1.3 (A2-L2): strip ANSI escape codes + control characters
|
|
73
|
+
* from operator-provided strings before printing. A malicious or
|
|
74
|
+
* accidentally-pasted label could otherwise repaint the terminal
|
|
75
|
+
* ("\x1b[2J\x1b[H", "\r" cursor reset, etc.) and hide other rows.
|
|
76
|
+
* Replaces all ESC + C0 control bytes with `?` so the cell stays
|
|
77
|
+
* fixed-width.
|
|
78
|
+
*/
|
|
79
|
+
function sanitiseForTerminal(text) {
|
|
80
|
+
return text.replace(/[\x00-\x1f\x7f]/g, '?');
|
|
81
|
+
}
|
|
82
|
+
const header = COLUMNS.map((c) => pad(c.label, c.width)).join(' ');
|
|
83
|
+
const divider = COLUMNS.map((c) => '-'.repeat(c.width)).join(' ');
|
|
84
|
+
const dataLines = rows.map((row) => {
|
|
85
|
+
const platform = row.platform_resolved ?? row.platform_hint;
|
|
86
|
+
const def = row.is_default ? 'yes' : 'no';
|
|
87
|
+
const label = sanitiseForTerminal(row.label ?? '');
|
|
88
|
+
const cells = [
|
|
89
|
+
pad(row.site_id, COLUMNS[0].width),
|
|
90
|
+
pad(row.url, COLUMNS[1].width),
|
|
91
|
+
pad(platform, COLUMNS[2].width),
|
|
92
|
+
pad(def, COLUMNS[3].width),
|
|
93
|
+
pad(label, COLUMNS[4].width),
|
|
94
|
+
];
|
|
95
|
+
return cells.join(' ');
|
|
96
|
+
});
|
|
97
|
+
const total = rows.length;
|
|
98
|
+
const summary = `${String(total)} configured site${total === 1 ? '' : 's'}` +
|
|
99
|
+
(defaultSiteId !== null
|
|
100
|
+
? ` (default: ${defaultSiteId})`
|
|
101
|
+
: ' (no default configured)');
|
|
102
|
+
return [
|
|
103
|
+
summary,
|
|
104
|
+
'',
|
|
105
|
+
header,
|
|
106
|
+
divider,
|
|
107
|
+
...dataLines,
|
|
108
|
+
'',
|
|
109
|
+
'Use `yt-builder-mcp test-site <site_id>` to verify connectivity.',
|
|
110
|
+
];
|
|
111
|
+
}
|
|
112
|
+
/** Default deps wired to the real store. */
|
|
113
|
+
export const DEFAULT_LIST_SITES_DEPS = {
|
|
114
|
+
load: loadSitesFile,
|
|
115
|
+
};
|
|
116
|
+
//# sourceMappingURL=list-sites.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-sites.js","sourceRoot":"","sources":["../../../src/sites/cli/list-sites.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* W9 — `remove-site` CLI subcommand.
|
|
3
|
+
*
|
|
4
|
+
* Removes ONE site entry by `site_id`. Auto-promotion policy (per
|
|
5
|
+
* plan §C.3):
|
|
6
|
+
* - Removing a non-default site → plain delete, default_site_id and
|
|
7
|
+
* the other entries' is_default flags are untouched.
|
|
8
|
+
* - Removing the default AND there is at least one other site →
|
|
9
|
+
* promote the next-in-insertion-order site to default
|
|
10
|
+
* (`is_default:true` + `default_site_id` update).
|
|
11
|
+
* - Removing the only site → `default_site_id: null`, empty array.
|
|
12
|
+
*
|
|
13
|
+
* Returns a structured {@link RemoveSiteResult} so the dispatcher can
|
|
14
|
+
* render the "(default → promoted X)" hint and tests can assert
|
|
15
|
+
* promotion behaviour without parsing stdout.
|
|
16
|
+
*
|
|
17
|
+
* `--yes` skips the confirmation hook supplied by the dispatcher.
|
|
18
|
+
* Production callers always wire a confirm hook; tests pass yes=true
|
|
19
|
+
* to bypass it.
|
|
20
|
+
*
|
|
21
|
+
* @license MIT
|
|
22
|
+
*/
|
|
23
|
+
import type { SitesFileT } from '../schema.js';
|
|
24
|
+
export interface RemoveSiteOptions {
|
|
25
|
+
/** Skip confirmation when true. CLI passes through from --yes. */
|
|
26
|
+
readonly yes?: boolean;
|
|
27
|
+
}
|
|
28
|
+
export interface RemoveSiteResult {
|
|
29
|
+
readonly siteId: string;
|
|
30
|
+
/** Path of the sites.json that was written. */
|
|
31
|
+
readonly path: string;
|
|
32
|
+
/** site_id that was promoted to default (only when removing default + ≥1 other). */
|
|
33
|
+
readonly promoted?: string;
|
|
34
|
+
/** True when the registry became empty after the remove. */
|
|
35
|
+
readonly nowEmpty: boolean;
|
|
36
|
+
/** True when no write happened because --yes was missing on confirm. */
|
|
37
|
+
readonly cancelled: boolean;
|
|
38
|
+
}
|
|
39
|
+
export declare class RemoveSiteError extends Error {
|
|
40
|
+
readonly code: string;
|
|
41
|
+
constructor(code: string, message: string);
|
|
42
|
+
}
|
|
43
|
+
/** Test/IO injection seam. */
|
|
44
|
+
export interface RemoveSiteDeps {
|
|
45
|
+
readonly load: (path: string) => Promise<SitesFileT>;
|
|
46
|
+
readonly save: (path: string, data: SitesFileT) => Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Confirm hook. Production dispatcher wires this to a clack
|
|
49
|
+
* prompt; tests pass `--yes` so this is never invoked.
|
|
50
|
+
*/
|
|
51
|
+
readonly confirm?: (message: string) => Promise<boolean>;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Remove a site by `site_id`. See file header for the auto-promotion
|
|
55
|
+
* policy. Throws {@link RemoveSiteError} with code `'UNKNOWN_SITE'`
|
|
56
|
+
* when `siteId` is not in the registry.
|
|
57
|
+
*/
|
|
58
|
+
export declare function removeSiteCommand(siteId: string, path: string, opts: RemoveSiteOptions, deps: RemoveSiteDeps): Promise<RemoveSiteResult>;
|
|
59
|
+
export declare const DEFAULT_REMOVE_SITE_DEPS: RemoveSiteDeps;
|
|
60
|
+
//# sourceMappingURL=remove-site.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remove-site.d.ts","sourceRoot":"","sources":["../../../src/sites/cli/remove-site.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,KAAK,EAAc,UAAU,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,WAAW,iBAAiB;IAC9B,kEAAkE;IAClE,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,+CAA+C;IAC/C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,oFAAoF;IACpF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,4DAA4D;IAC5D,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,wEAAwE;IACxE,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC/B;AAED,qBAAa,eAAgB,SAAQ,KAAK;aACV,IAAI,EAAE,MAAM;gBAAZ,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAI5D;AAED,8BAA8B;AAC9B,MAAM,WAAW,cAAc;IAC3B,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IACrD,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC5D;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACnC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,iBAAiB,EACvB,IAAI,EAAE,cAAc,GACrB,OAAO,CAAC,gBAAgB,CAAC,CA8F3B;AAED,eAAO,MAAM,wBAAwB,EAAE,cAGtC,CAAC"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* W9 — `remove-site` CLI subcommand.
|
|
3
|
+
*
|
|
4
|
+
* Removes ONE site entry by `site_id`. Auto-promotion policy (per
|
|
5
|
+
* plan §C.3):
|
|
6
|
+
* - Removing a non-default site → plain delete, default_site_id and
|
|
7
|
+
* the other entries' is_default flags are untouched.
|
|
8
|
+
* - Removing the default AND there is at least one other site →
|
|
9
|
+
* promote the next-in-insertion-order site to default
|
|
10
|
+
* (`is_default:true` + `default_site_id` update).
|
|
11
|
+
* - Removing the only site → `default_site_id: null`, empty array.
|
|
12
|
+
*
|
|
13
|
+
* Returns a structured {@link RemoveSiteResult} so the dispatcher can
|
|
14
|
+
* render the "(default → promoted X)" hint and tests can assert
|
|
15
|
+
* promotion behaviour without parsing stdout.
|
|
16
|
+
*
|
|
17
|
+
* `--yes` skips the confirmation hook supplied by the dispatcher.
|
|
18
|
+
* Production callers always wire a confirm hook; tests pass yes=true
|
|
19
|
+
* to bypass it.
|
|
20
|
+
*
|
|
21
|
+
* @license MIT
|
|
22
|
+
*/
|
|
23
|
+
import { saveSitesFile, loadSitesFile } from '../store.js';
|
|
24
|
+
export class RemoveSiteError extends Error {
|
|
25
|
+
code;
|
|
26
|
+
constructor(code, message) {
|
|
27
|
+
super(message);
|
|
28
|
+
this.code = code;
|
|
29
|
+
this.name = 'RemoveSiteError';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Remove a site by `site_id`. See file header for the auto-promotion
|
|
34
|
+
* policy. Throws {@link RemoveSiteError} with code `'UNKNOWN_SITE'`
|
|
35
|
+
* when `siteId` is not in the registry.
|
|
36
|
+
*/
|
|
37
|
+
export async function removeSiteCommand(siteId, path, opts, deps) {
|
|
38
|
+
const file = await deps.load(path);
|
|
39
|
+
const index = file.sites.findIndex((s) => s.site_id === siteId);
|
|
40
|
+
if (index < 0) {
|
|
41
|
+
throw new RemoveSiteError('UNKNOWN_SITE', `Site "${siteId}" not found in ${path}. ` +
|
|
42
|
+
(file.sites.length > 0
|
|
43
|
+
? `Available: ${file.sites.map((s) => s.site_id).join(', ')}.`
|
|
44
|
+
: '(no sites configured)'));
|
|
45
|
+
}
|
|
46
|
+
// W12-R2A (A2-H1) — fail-closed guard. Round 1's check silently
|
|
47
|
+
// proceeded when neither `--yes` NOR a confirm hook was supplied
|
|
48
|
+
// (deps.confirm === undefined). That made the destructive op a
|
|
49
|
+
// no-prompt delete in any non-TTY context (CI, piped stdin, MCP
|
|
50
|
+
// server stdio). Tighten to: if `--yes` is not set, a confirm hook
|
|
51
|
+
// is REQUIRED — otherwise throw a typed error so the dispatcher
|
|
52
|
+
// can surface "pass --yes" with a non-zero exit code.
|
|
53
|
+
if (opts.yes !== true) {
|
|
54
|
+
if (deps.confirm === undefined) {
|
|
55
|
+
throw new RemoveSiteError('CONFIRM_REQUIRED', `Remove of site "${siteId}" requires --yes (no interactive `
|
|
56
|
+
+ `confirm available in this context). Re-run with --yes to skip the prompt.`);
|
|
57
|
+
}
|
|
58
|
+
const cont = await deps.confirm(`Remove site "${siteId}" from ${path}?`);
|
|
59
|
+
if (!cont) {
|
|
60
|
+
return {
|
|
61
|
+
siteId,
|
|
62
|
+
path,
|
|
63
|
+
nowEmpty: false,
|
|
64
|
+
cancelled: true,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const wasDefault = file.sites[index]?.is_default === true
|
|
69
|
+
|| file.default_site_id === siteId;
|
|
70
|
+
// Drop the target entry, preserving insertion order.
|
|
71
|
+
const remaining = file.sites.filter((_, i) => i !== index);
|
|
72
|
+
let promoted;
|
|
73
|
+
let nextSites = remaining;
|
|
74
|
+
let nextDefaultId = file.default_site_id;
|
|
75
|
+
if (wasDefault) {
|
|
76
|
+
if (remaining.length === 0) {
|
|
77
|
+
nextDefaultId = null;
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
const promote = remaining[0];
|
|
81
|
+
if (promote === undefined) {
|
|
82
|
+
// unreachable due to length check, but keeps TS strict.
|
|
83
|
+
nextDefaultId = null;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
promoted = promote.site_id;
|
|
87
|
+
nextDefaultId = promote.site_id;
|
|
88
|
+
nextSites = remaining.map((s, i) => ({
|
|
89
|
+
...s,
|
|
90
|
+
is_default: i === 0,
|
|
91
|
+
}));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
// Make sure default_site_id still points at a present entry.
|
|
97
|
+
if (nextDefaultId !== null
|
|
98
|
+
&& !remaining.some((s) => s.site_id === nextDefaultId)) {
|
|
99
|
+
nextDefaultId = null;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const nextFile = {
|
|
103
|
+
schema_version: 1,
|
|
104
|
+
default_site_id: nextDefaultId,
|
|
105
|
+
sites: nextSites,
|
|
106
|
+
};
|
|
107
|
+
await deps.save(path, nextFile);
|
|
108
|
+
return {
|
|
109
|
+
siteId,
|
|
110
|
+
path,
|
|
111
|
+
...(promoted !== undefined ? { promoted } : {}),
|
|
112
|
+
nowEmpty: nextSites.length === 0,
|
|
113
|
+
cancelled: false,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
export const DEFAULT_REMOVE_SITE_DEPS = {
|
|
117
|
+
load: loadSitesFile,
|
|
118
|
+
save: saveSitesFile,
|
|
119
|
+
};
|
|
120
|
+
//# sourceMappingURL=remove-site.js.map
|