@wootsup/yt-builder-mcp 1.0.0 → 1.1.0

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.
Files changed (187) hide show
  1. package/README.md +18 -6
  2. package/bin/yt-builder-mcp.js +6 -0
  3. package/dist/auth.d.ts +86 -6
  4. package/dist/auth.d.ts.map +1 -1
  5. package/dist/auth.js +141 -8
  6. package/dist/auth.js.map +1 -1
  7. package/dist/errors/sanitize.d.ts.map +1 -1
  8. package/dist/errors/sanitize.js +10 -0
  9. package/dist/errors/sanitize.js.map +1 -1
  10. package/dist/gateway/essentials.d.ts +1 -1
  11. package/dist/gateway/essentials.d.ts.map +1 -1
  12. package/dist/gateway/essentials.js +15 -2
  13. package/dist/gateway/essentials.js.map +1 -1
  14. package/dist/index.d.ts +5 -1
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +52 -16
  17. package/dist/index.js.map +1 -1
  18. package/dist/install-skill.d.ts +10 -2
  19. package/dist/install-skill.d.ts.map +1 -1
  20. package/dist/install-skill.js +18 -7
  21. package/dist/install-skill.js.map +1 -1
  22. package/dist/platform/detect.d.ts +64 -0
  23. package/dist/platform/detect.d.ts.map +1 -0
  24. package/dist/platform/detect.js +98 -0
  25. package/dist/platform/detect.js.map +1 -0
  26. package/dist/platform/index.d.ts +72 -0
  27. package/dist/platform/index.d.ts.map +1 -1
  28. package/dist/platform/index.js +138 -0
  29. package/dist/platform/index.js.map +1 -1
  30. package/dist/platform/joomla.d.ts +44 -0
  31. package/dist/platform/joomla.d.ts.map +1 -0
  32. package/dist/platform/joomla.js +46 -0
  33. package/dist/platform/joomla.js.map +1 -0
  34. package/dist/server.d.ts +61 -2
  35. package/dist/server.d.ts.map +1 -1
  36. package/dist/server.js +86 -2
  37. package/dist/server.js.map +1 -1
  38. package/dist/setup-cli-confirm.d.ts +36 -0
  39. package/dist/setup-cli-confirm.d.ts.map +1 -0
  40. package/dist/setup-cli-confirm.js +59 -0
  41. package/dist/setup-cli-confirm.js.map +1 -0
  42. package/dist/setup-cli.d.ts +53 -1
  43. package/dist/setup-cli.d.ts.map +1 -1
  44. package/dist/setup-cli.js +525 -10
  45. package/dist/setup-cli.js.map +1 -1
  46. package/dist/setup-prompts.d.ts.map +1 -1
  47. package/dist/setup-prompts.js +12 -8
  48. package/dist/setup-prompts.js.map +1 -1
  49. package/dist/setup-wizard-types.d.ts +19 -3
  50. package/dist/setup-wizard-types.d.ts.map +1 -1
  51. package/dist/setup-wizard.d.ts.map +1 -1
  52. package/dist/setup-wizard.js +34 -1
  53. package/dist/setup-wizard.js.map +1 -1
  54. package/dist/sites/cli/add-site.d.ts +106 -0
  55. package/dist/sites/cli/add-site.d.ts.map +1 -0
  56. package/dist/sites/cli/add-site.js +185 -0
  57. package/dist/sites/cli/add-site.js.map +1 -0
  58. package/dist/sites/cli/list-sites.d.ts +40 -0
  59. package/dist/sites/cli/list-sites.d.ts.map +1 -0
  60. package/dist/sites/cli/list-sites.js +117 -0
  61. package/dist/sites/cli/list-sites.js.map +1 -0
  62. package/dist/sites/cli/remove-site.d.ts +60 -0
  63. package/dist/sites/cli/remove-site.d.ts.map +1 -0
  64. package/dist/sites/cli/remove-site.js +120 -0
  65. package/dist/sites/cli/remove-site.js.map +1 -0
  66. package/dist/sites/cli/set-default.d.ts +47 -0
  67. package/dist/sites/cli/set-default.d.ts.map +1 -0
  68. package/dist/sites/cli/set-default.js +69 -0
  69. package/dist/sites/cli/set-default.js.map +1 -0
  70. package/dist/sites/cli/test-site.d.ts +77 -0
  71. package/dist/sites/cli/test-site.d.ts.map +1 -0
  72. package/dist/sites/cli/test-site.js +146 -0
  73. package/dist/sites/cli/test-site.js.map +1 -0
  74. package/dist/sites/client-pool.d.ts +124 -0
  75. package/dist/sites/client-pool.d.ts.map +1 -0
  76. package/dist/sites/client-pool.js +172 -0
  77. package/dist/sites/client-pool.js.map +1 -0
  78. package/dist/sites/env-bridge.d.ts +55 -0
  79. package/dist/sites/env-bridge.d.ts.map +1 -0
  80. package/dist/sites/env-bridge.js +94 -0
  81. package/dist/sites/env-bridge.js.map +1 -0
  82. package/dist/sites/paths.d.ts +21 -0
  83. package/dist/sites/paths.d.ts.map +1 -0
  84. package/dist/sites/paths.js +37 -0
  85. package/dist/sites/paths.js.map +1 -0
  86. package/dist/sites/probe.d.ts +60 -0
  87. package/dist/sites/probe.d.ts.map +1 -0
  88. package/dist/sites/probe.js +112 -0
  89. package/dist/sites/probe.js.map +1 -0
  90. package/dist/sites/registry.d.ts +142 -0
  91. package/dist/sites/registry.d.ts.map +1 -0
  92. package/dist/sites/registry.js +163 -0
  93. package/dist/sites/registry.js.map +1 -0
  94. package/dist/sites/schema.d.ts +90 -0
  95. package/dist/sites/schema.d.ts.map +1 -0
  96. package/dist/sites/schema.js +71 -0
  97. package/dist/sites/schema.js.map +1 -0
  98. package/dist/sites/secret-resolver.d.ts +109 -0
  99. package/dist/sites/secret-resolver.d.ts.map +1 -0
  100. package/dist/sites/secret-resolver.js +189 -0
  101. package/dist/sites/secret-resolver.js.map +1 -0
  102. package/dist/sites/store.d.ts +82 -0
  103. package/dist/sites/store.d.ts.map +1 -0
  104. package/dist/sites/store.js +198 -0
  105. package/dist/sites/store.js.map +1 -0
  106. package/dist/sites/tools/index.d.ts +28 -0
  107. package/dist/sites/tools/index.d.ts.map +1 -0
  108. package/dist/sites/tools/index.js +30 -0
  109. package/dist/sites/tools/index.js.map +1 -0
  110. package/dist/sites/tools/sites-list.d.ts +48 -0
  111. package/dist/sites/tools/sites-list.d.ts.map +1 -0
  112. package/dist/sites/tools/sites-list.js +135 -0
  113. package/dist/sites/tools/sites-list.js.map +1 -0
  114. package/dist/sites/tools/sites-test.d.ts +47 -0
  115. package/dist/sites/tools/sites-test.d.ts.map +1 -0
  116. package/dist/sites/tools/sites-test.js +148 -0
  117. package/dist/sites/tools/sites-test.js.map +1 -0
  118. package/dist/skill-loader.d.ts +63 -3
  119. package/dist/skill-loader.d.ts.map +1 -1
  120. package/dist/skill-loader.js +123 -10
  121. package/dist/skill-loader.js.map +1 -1
  122. package/dist/tools/elements/builders.d.ts +2 -2
  123. package/dist/tools/elements/builders.d.ts.map +1 -1
  124. package/dist/tools/elements/builders.js +45 -23
  125. package/dist/tools/elements/builders.js.map +1 -1
  126. package/dist/tools/elements/handlers.d.ts +8 -1
  127. package/dist/tools/elements/handlers.d.ts.map +1 -1
  128. package/dist/tools/elements/handlers.js +37 -1
  129. package/dist/tools/elements/handlers.js.map +1 -1
  130. package/dist/tools/format/health-format.d.ts +12 -6
  131. package/dist/tools/format/health-format.d.ts.map +1 -1
  132. package/dist/tools/format/health-format.js +14 -11
  133. package/dist/tools/format/health-format.js.map +1 -1
  134. package/dist/tools/format/pages-format.d.ts +7 -1
  135. package/dist/tools/format/pages-format.d.ts.map +1 -1
  136. package/dist/tools/format/pages-format.js +25 -1
  137. package/dist/tools/format/pages-format.js.map +1 -1
  138. package/dist/tools/health.d.ts +2 -2
  139. package/dist/tools/health.d.ts.map +1 -1
  140. package/dist/tools/health.js +120 -47
  141. package/dist/tools/health.js.map +1 -1
  142. package/dist/tools/index.d.ts +9 -2
  143. package/dist/tools/index.d.ts.map +1 -1
  144. package/dist/tools/index.js +21 -7
  145. package/dist/tools/index.js.map +1 -1
  146. package/dist/tools/inspection.d.ts +2 -2
  147. package/dist/tools/inspection.d.ts.map +1 -1
  148. package/dist/tools/inspection.js +67 -22
  149. package/dist/tools/inspection.js.map +1 -1
  150. package/dist/tools/multi-items/builders.d.ts +2 -2
  151. package/dist/tools/multi-items/builders.d.ts.map +1 -1
  152. package/dist/tools/multi-items/builders.js +21 -8
  153. package/dist/tools/multi-items/builders.js.map +1 -1
  154. package/dist/tools/pages/builders.d.ts +2 -2
  155. package/dist/tools/pages/builders.d.ts.map +1 -1
  156. package/dist/tools/pages/builders.js +36 -24
  157. package/dist/tools/pages/builders.js.map +1 -1
  158. package/dist/tools/pages/handlers-read.d.ts.map +1 -1
  159. package/dist/tools/pages/handlers-read.js +10 -0
  160. package/dist/tools/pages/handlers-read.js.map +1 -1
  161. package/dist/tools/pages/schemas.d.ts.map +1 -1
  162. package/dist/tools/pages/schemas.js +18 -0
  163. package/dist/tools/pages/schemas.js.map +1 -1
  164. package/dist/tools/pool-resolve-helper.d.ts +83 -0
  165. package/dist/tools/pool-resolve-helper.d.ts.map +1 -0
  166. package/dist/tools/pool-resolve-helper.js +148 -0
  167. package/dist/tools/pool-resolve-helper.js.map +1 -0
  168. package/dist/tools/shared-schemas.d.ts +10 -0
  169. package/dist/tools/shared-schemas.d.ts.map +1 -1
  170. package/dist/tools/shared-schemas.js +20 -2
  171. package/dist/tools/shared-schemas.js.map +1 -1
  172. package/dist/tools/sources/builders.d.ts +2 -2
  173. package/dist/tools/sources/builders.d.ts.map +1 -1
  174. package/dist/tools/sources/builders.js +27 -15
  175. package/dist/tools/sources/builders.js.map +1 -1
  176. package/dist/tools/tool-builder/index.d.ts +1 -1
  177. package/dist/tools/tool-builder/index.d.ts.map +1 -1
  178. package/dist/tools/tool-builder/index.js +1 -1
  179. package/dist/tools/tool-builder/index.js.map +1 -1
  180. package/dist/tools/tool-builder/results.d.ts +34 -0
  181. package/dist/tools/tool-builder/results.d.ts.map +1 -1
  182. package/dist/tools/tool-builder/results.js +86 -0
  183. package/dist/tools/tool-builder/results.js.map +1 -1
  184. package/manifest.json +27 -10
  185. package/package.json +3 -1
  186. package/skills/yt-builder-mcp/SKILL.md +846 -0
  187. package/skills/yootheme-builder/SKILL.md +0 -587
@@ -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,CA+EpE;AAED,4CAA4C;AAC5C,eAAO,MAAM,uBAAuB,EAAE,aAErC,CAAC"}
@@ -0,0 +1,117 @@
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
+ // eslint-disable-next-line no-control-regex -- defense-in-depth strip
81
+ return text.replace(/[\x00-\x1f\x7f]/g, '?');
82
+ }
83
+ const header = COLUMNS.map((c) => pad(c.label, c.width)).join(' ');
84
+ const divider = COLUMNS.map((c) => '-'.repeat(c.width)).join(' ');
85
+ const dataLines = rows.map((row) => {
86
+ const platform = row.platform_resolved ?? row.platform_hint;
87
+ const def = row.is_default ? 'yes' : 'no';
88
+ const label = sanitiseForTerminal(row.label ?? '');
89
+ const cells = [
90
+ pad(row.site_id, COLUMNS[0].width),
91
+ pad(row.url, COLUMNS[1].width),
92
+ pad(platform, COLUMNS[2].width),
93
+ pad(def, COLUMNS[3].width),
94
+ pad(label, COLUMNS[4].width),
95
+ ];
96
+ return cells.join(' ');
97
+ });
98
+ const total = rows.length;
99
+ const summary = `${String(total)} configured site${total === 1 ? '' : 's'}` +
100
+ (defaultSiteId !== null
101
+ ? ` (default: ${defaultSiteId})`
102
+ : ' (no default configured)');
103
+ return [
104
+ summary,
105
+ '',
106
+ header,
107
+ divider,
108
+ ...dataLines,
109
+ '',
110
+ 'Use `yt-builder-mcp test-site <site_id>` to verify connectivity.',
111
+ ];
112
+ }
113
+ /** Default deps wired to the real store. */
114
+ export const DEFAULT_LIST_SITES_DEPS = {
115
+ load: loadSitesFile,
116
+ };
117
+ //# 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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remove-site.js","sourceRoot":"","sources":["../../../src/sites/cli/remove-site.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAoB3D,MAAM,OAAO,eAAgB,SAAQ,KAAK;IACV;IAA5B,YAA4B,IAAY,EAAE,OAAe;QACrD,KAAK,CAAC,OAAO,CAAC,CAAC;QADS,SAAI,GAAJ,IAAI,CAAQ;QAEpC,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAClC,CAAC;CACJ;AAaD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACnC,MAAc,EACd,IAAY,EACZ,IAAuB,EACvB,IAAoB;IAEpB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC;IAChE,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACZ,MAAM,IAAI,eAAe,CACrB,cAAc,EACd,SAAS,MAAM,kBAAkB,IAAI,IAAI;YACrC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;gBAClB,CAAC,CAAC,cAAc,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;gBAC9D,CAAC,CAAC,uBAAuB,CAAC,CACrC,CAAC;IACN,CAAC;IAED,gEAAgE;IAChE,iEAAiE;IACjE,+DAA+D;IAC/D,gEAAgE;IAChE,mEAAmE;IACnE,gEAAgE;IAChE,sDAAsD;IACtD,IAAI,IAAI,CAAC,GAAG,KAAK,IAAI,EAAE,CAAC;QACpB,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YAC7B,MAAM,IAAI,eAAe,CACrB,kBAAkB,EAClB,mBAAmB,MAAM,mCAAmC;kBACtD,2EAA2E,CACpF,CAAC;QACN,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAC3B,gBAAgB,MAAM,UAAU,IAAI,GAAG,CAC1C,CAAC;QACF,IAAI,CAAC,IAAI,EAAE,CAAC;YACR,OAAO;gBACH,MAAM;gBACN,IAAI;gBACJ,QAAQ,EAAE,KAAK;gBACf,SAAS,EAAE,IAAI;aAClB,CAAC;QACN,CAAC;IACL,CAAC;IAED,MAAM,UAAU,GACZ,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,UAAU,KAAK,IAAI;WACnC,IAAI,CAAC,eAAe,KAAK,MAAM,CAAC;IAEvC,qDAAqD;IACrD,MAAM,SAAS,GAAiB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC;IAEzE,IAAI,QAA4B,CAAC;IACjC,IAAI,SAAS,GAAiB,SAAS,CAAC;IACxC,IAAI,aAAa,GAAkB,IAAI,CAAC,eAAe,CAAC;IAExD,IAAI,UAAU,EAAE,CAAC;QACb,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,aAAa,GAAG,IAAI,CAAC;QACzB,CAAC;aAAM,CAAC;YACJ,MAAM,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;YAC7B,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;gBACxB,wDAAwD;gBACxD,aAAa,GAAG,IAAI,CAAC;YACzB,CAAC;iBAAM,CAAC;gBACJ,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;gBAC3B,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC;gBAChC,SAAS,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;oBACjC,GAAG,CAAC;oBACJ,UAAU,EAAE,CAAC,KAAK,CAAC;iBACtB,CAAC,CAAC,CAAC;YACR,CAAC;QACL,CAAC;IACL,CAAC;SAAM,CAAC;QACJ,6DAA6D;QAC7D,IACI,aAAa,KAAK,IAAI;eACnB,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,aAAa,CAAC,EACxD,CAAC;YACC,aAAa,GAAG,IAAI,CAAC;QACzB,CAAC;IACL,CAAC;IAED,MAAM,QAAQ,GAAe;QACzB,cAAc,EAAE,CAAC;QACjB,eAAe,EAAE,aAAa;QAC9B,KAAK,EAAE,SAAS;KACnB,CAAC;IAEF,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAEhC,OAAO;QACH,MAAM;QACN,IAAI;QACJ,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/C,QAAQ,EAAE,SAAS,CAAC,MAAM,KAAK,CAAC;QAChC,SAAS,EAAE,KAAK;KACnB,CAAC;AACN,CAAC;AAED,MAAM,CAAC,MAAM,wBAAwB,GAAmB;IACpD,IAAI,EAAE,aAAa;IACnB,IAAI,EAAE,aAAa;CACtB,CAAC"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * W9 — `set-default` CLI subcommand.
3
+ *
4
+ * Flips the `is_default` flag on the target entry to `true`, clears
5
+ * it on every other entry, and updates `default_site_id` to the
6
+ * target. Fails with code `'UNKNOWN_SITE'` when the site_id is not
7
+ * present.
8
+ *
9
+ * Idempotent: setting the already-default site as default is a
10
+ * successful no-op (the file is still rewritten so any drift between
11
+ * `default_site_id` and the per-entry `is_default` flag is healed in
12
+ * the same pass).
13
+ *
14
+ * @license MIT
15
+ */
16
+ import type { SitesFileT } from '../schema.js';
17
+ export interface SetDefaultOptions {
18
+ /** Skip the confirmation prompt when true. CLI passes through from --yes. */
19
+ readonly yes?: boolean;
20
+ }
21
+ export interface SetDefaultResult {
22
+ readonly siteId: string;
23
+ readonly path: string;
24
+ /** site_id that was the default BEFORE this call (null when none). */
25
+ readonly previousDefault: string | null;
26
+ /** True when no write happened because the confirm hook rejected. */
27
+ readonly cancelled: boolean;
28
+ }
29
+ export declare class SetDefaultError extends Error {
30
+ readonly code: string;
31
+ constructor(code: string, message: string);
32
+ }
33
+ export interface SetDefaultDeps {
34
+ readonly load: (path: string) => Promise<SitesFileT>;
35
+ readonly save: (path: string, data: SitesFileT) => Promise<void>;
36
+ /**
37
+ * Confirm hook. Production dispatcher wires this to a y/N readline
38
+ * prompt; tests pass `--yes` so this is never invoked. W12-R2A:
39
+ * `set-default` is destructive in the routing sense — every
40
+ * subsequent tool call routes to the new default — so the guard
41
+ * matches the remove-site shape.
42
+ */
43
+ readonly confirm?: (message: string) => Promise<boolean>;
44
+ }
45
+ export declare function setDefaultCommand(siteId: string, path: string, opts: SetDefaultOptions, deps: SetDefaultDeps): Promise<SetDefaultResult>;
46
+ export declare const DEFAULT_SET_DEFAULT_DEPS: SetDefaultDeps;
47
+ //# sourceMappingURL=set-default.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"set-default.d.ts","sourceRoot":"","sources":["../../../src/sites/cli/set-default.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAAc,UAAU,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,WAAW,iBAAiB;IAC9B,6EAA6E;IAC7E,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sEAAsE;IACtE,QAAQ,CAAC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,qEAAqE;IACrE,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,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;;;;;;OAMG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC5D;AAED,wBAAsB,iBAAiB,CACnC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,iBAAiB,EACvB,IAAI,EAAE,cAAc,GACrB,OAAO,CAAC,gBAAgB,CAAC,CAqD3B;AAED,eAAO,MAAM,wBAAwB,EAAE,cAGtC,CAAC"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * W9 — `set-default` CLI subcommand.
3
+ *
4
+ * Flips the `is_default` flag on the target entry to `true`, clears
5
+ * it on every other entry, and updates `default_site_id` to the
6
+ * target. Fails with code `'UNKNOWN_SITE'` when the site_id is not
7
+ * present.
8
+ *
9
+ * Idempotent: setting the already-default site as default is a
10
+ * successful no-op (the file is still rewritten so any drift between
11
+ * `default_site_id` and the per-entry `is_default` flag is healed in
12
+ * the same pass).
13
+ *
14
+ * @license MIT
15
+ */
16
+ import { loadSitesFile, saveSitesFile } from '../store.js';
17
+ export class SetDefaultError extends Error {
18
+ code;
19
+ constructor(code, message) {
20
+ super(message);
21
+ this.code = code;
22
+ this.name = 'SetDefaultError';
23
+ }
24
+ }
25
+ export async function setDefaultCommand(siteId, path, opts, deps) {
26
+ const file = await deps.load(path);
27
+ if (!file.sites.some((s) => s.site_id === siteId)) {
28
+ throw new SetDefaultError('UNKNOWN_SITE', `Site "${siteId}" not found in ${path}. ` +
29
+ (file.sites.length > 0
30
+ ? `Available: ${file.sites.map((s) => s.site_id).join(', ')}.`
31
+ : '(no sites configured)'));
32
+ }
33
+ const previousDefault = file.default_site_id;
34
+ // W12-R2A (A2-M1) — destructive-routing-op safeguard. Flipping the
35
+ // default redirects every subsequent tool call that omits `site_id`
36
+ // to a different connected site. That can silently land write-ops
37
+ // on the wrong CMS install. Fail-closed: if `--yes` is not set, a
38
+ // confirm hook is REQUIRED. Idempotent flip (already-default) is
39
+ // still gated — the file is rewritten even on no-op, and an
40
+ // operator confirming "yes" is the only reliable safe-belt.
41
+ if (opts.yes !== true) {
42
+ if (deps.confirm === undefined) {
43
+ throw new SetDefaultError('SET_DEFAULT_CONFIRM_REQUIRED', `Setting default to "${siteId}" requires --yes (no interactive `
44
+ + `confirm available in this context). Re-run with --yes to skip the prompt.`);
45
+ }
46
+ const priorLabel = previousDefault !== null ? `"${previousDefault}"` : '(none)';
47
+ const cont = await deps.confirm(`Switch default site from ${priorLabel} to "${siteId}" in ${path}? `
48
+ + `This changes which site every subsequent tool call routes to by default.`);
49
+ if (!cont) {
50
+ return { siteId, path, previousDefault, cancelled: true };
51
+ }
52
+ }
53
+ const nextSites = file.sites.map((s) => ({
54
+ ...s,
55
+ is_default: s.site_id === siteId,
56
+ }));
57
+ const nextFile = {
58
+ schema_version: 1,
59
+ default_site_id: siteId,
60
+ sites: nextSites,
61
+ };
62
+ await deps.save(path, nextFile);
63
+ return { siteId, path, previousDefault, cancelled: false };
64
+ }
65
+ export const DEFAULT_SET_DEFAULT_DEPS = {
66
+ load: loadSitesFile,
67
+ save: saveSitesFile,
68
+ };
69
+ //# sourceMappingURL=set-default.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"set-default.js","sourceRoot":"","sources":["../../../src/sites/cli/set-default.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAiB3D,MAAM,OAAO,eAAgB,SAAQ,KAAK;IACV;IAA5B,YAA4B,IAAY,EAAE,OAAe;QACrD,KAAK,CAAC,OAAO,CAAC,CAAC;QADS,SAAI,GAAJ,IAAI,CAAQ;QAEpC,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAClC,CAAC;CACJ;AAeD,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACnC,MAAc,EACd,IAAY,EACZ,IAAuB,EACvB,IAAoB;IAEpB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,MAAM,CAAC,EAAE,CAAC;QAChD,MAAM,IAAI,eAAe,CACrB,cAAc,EACd,SAAS,MAAM,kBAAkB,IAAI,IAAI;YACrC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;gBAClB,CAAC,CAAC,cAAc,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;gBAC9D,CAAC,CAAC,uBAAuB,CAAC,CACrC,CAAC;IACN,CAAC;IAED,MAAM,eAAe,GAAG,IAAI,CAAC,eAAe,CAAC;IAE7C,mEAAmE;IACnE,oEAAoE;IACpE,kEAAkE;IAClE,kEAAkE;IAClE,iEAAiE;IACjE,4DAA4D;IAC5D,4DAA4D;IAC5D,IAAI,IAAI,CAAC,GAAG,KAAK,IAAI,EAAE,CAAC;QACpB,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YAC7B,MAAM,IAAI,eAAe,CACrB,8BAA8B,EAC9B,uBAAuB,MAAM,mCAAmC;kBAC1D,2EAA2E,CACpF,CAAC;QACN,CAAC;QACD,MAAM,UAAU,GAAG,eAAe,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,eAAe,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC;QAChF,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAC3B,4BAA4B,UAAU,QAAQ,MAAM,QAAQ,IAAI,IAAI;cAC9D,0EAA0E,CACnF,CAAC;QACF,IAAI,CAAC,IAAI,EAAE,CAAC;YACR,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;QAC9D,CAAC;IACL,CAAC;IAED,MAAM,SAAS,GAAiB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACnD,GAAG,CAAC;QACJ,UAAU,EAAE,CAAC,CAAC,OAAO,KAAK,MAAM;KACnC,CAAC,CAAC,CAAC;IAEJ,MAAM,QAAQ,GAAe;QACzB,cAAc,EAAE,CAAC;QACjB,eAAe,EAAE,MAAM;QACvB,KAAK,EAAE,SAAS;KACnB,CAAC;IAEF,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAEhC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;AAC/D,CAAC;AAED,MAAM,CAAC,MAAM,wBAAwB,GAAmB;IACpD,IAAI,EAAE,aAAa;IACnB,IAAI,EAAE,aAAa;CACtB,CAAC"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * W9 — `test-site` CLI subcommand.
3
+ *
4
+ * Live connectivity probe for ONE site_id. Mirrors the runtime
5
+ * behaviour of the `yootheme_builder_sites_test` MCP tool, but from
6
+ * the CLI so operators can pre-flight a site without launching the
7
+ * stdio server.
8
+ *
9
+ * Pipeline (per plan §W9):
10
+ * 1. Load `sites.json` via the W1 store.
11
+ * 2. Build a {@link SiteRegistry} + {@link ClientPool} from the
12
+ * entries, wired to the supplied {@link SecretResolver}.
13
+ * 3. Call `pool.resolve(siteId)` → throws structured
14
+ * {@link UnknownSiteError} / {@link NoDefaultSiteError} which we
15
+ * map to a structured CLI result + a non-zero exit code.
16
+ * 4. Probe `/health` (no auth) + `/etag` (auth) in parallel via
17
+ * `Promise.allSettled` so a failure on one path never hides the
18
+ * other's outcome.
19
+ * 5. Print a green/red structured result and return it for the
20
+ * dispatcher / tests.
21
+ *
22
+ * Returns a {@link TestSiteResult} (not exit codes directly) so tests
23
+ * can assert deterministic outputs without re-implementing the
24
+ * probe-routing logic.
25
+ *
26
+ * @license MIT
27
+ */
28
+ import { type Platform, type PlatformKind } from '../../platform/index.js';
29
+ import { ClientPool } from '../client-pool.js';
30
+ import { SiteRegistry } from '../registry.js';
31
+ import type { SitesFileT } from '../schema.js';
32
+ import { type SecretResolver } from '../secret-resolver.js';
33
+ export interface TestSiteResult {
34
+ readonly siteId: string;
35
+ readonly siteUrl: string;
36
+ readonly platform: PlatformKind | 'unknown';
37
+ readonly pluginReachable: boolean;
38
+ readonly bearerValid: boolean;
39
+ readonly etagReceived: boolean;
40
+ readonly summary: string;
41
+ readonly pluginError?: string;
42
+ readonly bearerError?: string;
43
+ /** True when site_id was not in the registry. */
44
+ readonly unknownSite: boolean;
45
+ /** Available ids when unknownSite is true. */
46
+ readonly available?: readonly string[];
47
+ }
48
+ export interface TestSiteDeps {
49
+ readonly load: (path: string) => Promise<SitesFileT>;
50
+ readonly secretResolver?: SecretResolver;
51
+ /** Inject the platform probe for tests (no network). */
52
+ readonly platformForUrlAsync?: (url: string, hint?: PlatformKind) => Promise<Platform>;
53
+ /**
54
+ * Inject the pool factory. Tests pass a pool that does NOT call
55
+ * the real RestClient (they pre-stub the response shape).
56
+ * Production wires the default ClientPool below.
57
+ */
58
+ readonly buildPool?: (registry: SiteRegistry, resolver: SecretResolver) => ClientPool;
59
+ readonly log?: (line: string) => void;
60
+ }
61
+ export declare class TestSiteError extends Error {
62
+ readonly code: string;
63
+ constructor(code: string, message: string);
64
+ }
65
+ /**
66
+ * Build the runtime result lines for stdout — keeps the live CLI path
67
+ * + tests in sync without forcing tests to parse text.
68
+ */
69
+ export declare function renderTestSiteLines(result: TestSiteResult): readonly string[];
70
+ /**
71
+ * Probe one site and return a structured outcome. Never throws on a
72
+ * probe failure (those are surfaced inside the result); throws ONLY
73
+ * on file-IO failures the operator cannot recover from.
74
+ */
75
+ export declare function testSiteCommand(siteId: string, path: string, deps: TestSiteDeps): Promise<TestSiteResult>;
76
+ export declare const DEFAULT_TEST_SITE_DEPS: TestSiteDeps;
77
+ //# sourceMappingURL=test-site.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-site.d.ts","sourceRoot":"","sources":["../../../src/sites/cli/test-site.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAEH,KAAK,QAAQ,EACb,KAAK,YAAY,EACpB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAM/C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAEH,KAAK,cAAc,EACtB,MAAM,uBAAuB,CAAC;AAG/B,MAAM,WAAW,cAAc;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,YAAY,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;IAClC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,iDAAiD;IACjD,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,8CAA8C;IAC9C,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1C;AAED,MAAM,WAAW,YAAY;IACzB,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IACrD,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,CAAC;IACzC,wDAAwD;IACxD,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAC3B,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE,YAAY,KAClB,OAAO,CAAC,QAAQ,CAAC,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,CACjB,QAAQ,EAAE,YAAY,EACtB,QAAQ,EAAE,cAAc,KACvB,UAAU,CAAC;IAChB,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAED,qBAAa,aAAc,SAAQ,KAAK;aACR,IAAI,EAAE,MAAM;gBAAZ,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAI5D;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,cAAc,GAAG,SAAS,MAAM,EAAE,CA0B7E;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CACjC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,YAAY,GACnB,OAAO,CAAC,cAAc,CAAC,CAmEzB;AAED,eAAO,MAAM,sBAAsB,EAAE,YAEpC,CAAC"}