@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,146 @@
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 { platformForUrlAsync, } from '../../platform/index.js';
29
+ import { ClientPool } from '../client-pool.js';
30
+ import { NoDefaultSiteError, UnknownSiteError, } from '../client-pool.js';
31
+ import { probeSite } from '../probe.js';
32
+ import { SiteRegistry } from '../registry.js';
33
+ import { CompositeSecretResolver, } from '../secret-resolver.js';
34
+ import { loadSitesFile } from '../store.js';
35
+ export class TestSiteError extends Error {
36
+ code;
37
+ constructor(code, message) {
38
+ super(message);
39
+ this.code = code;
40
+ this.name = 'TestSiteError';
41
+ }
42
+ }
43
+ /**
44
+ * Build the runtime result lines for stdout — keeps the live CLI path
45
+ * + tests in sync without forcing tests to parse text.
46
+ */
47
+ export function renderTestSiteLines(result) {
48
+ if (result.unknownSite) {
49
+ const avail = result.available ?? [];
50
+ return [
51
+ `FAIL — Site "${result.siteId}" not found.`,
52
+ avail.length > 0
53
+ ? `Available: ${avail.join(', ')}`
54
+ : '(no sites configured)',
55
+ ];
56
+ }
57
+ const lines = [
58
+ `Site: ${result.siteId}`,
59
+ `URL: ${result.siteUrl}`,
60
+ `Platform: ${result.platform}`,
61
+ `Plugin reachable: ${result.pluginReachable ? 'yes' : 'NO'}`,
62
+ `Bearer valid: ${result.bearerValid ? 'yes' : 'NO'}`,
63
+ `ETag received: ${result.etagReceived ? 'yes' : 'NO'}`,
64
+ ];
65
+ if (result.pluginError !== undefined) {
66
+ lines.push(`Plugin error: ${result.pluginError}`);
67
+ }
68
+ if (result.bearerError !== undefined) {
69
+ lines.push(`Bearer error: ${result.bearerError}`);
70
+ }
71
+ lines.push('', result.summary);
72
+ return lines;
73
+ }
74
+ /**
75
+ * Probe one site and return a structured outcome. Never throws on a
76
+ * probe failure (those are surfaced inside the result); throws ONLY
77
+ * on file-IO failures the operator cannot recover from.
78
+ */
79
+ export async function testSiteCommand(siteId, path, deps) {
80
+ const file = await deps.load(path);
81
+ const registry = new SiteRegistry(file, {
82
+ ...(deps.platformForUrlAsync !== undefined
83
+ ? { platformForUrlAsync: deps.platformForUrlAsync }
84
+ : { platformForUrlAsync }),
85
+ });
86
+ const resolver = deps.secretResolver ?? new CompositeSecretResolver();
87
+ const pool = deps.buildPool !== undefined
88
+ ? deps.buildPool(registry, resolver)
89
+ : new ClientPool(registry, resolver);
90
+ let client;
91
+ let site;
92
+ try {
93
+ const resolution = await pool.resolve(siteId);
94
+ client = resolution.client;
95
+ site = resolution.site;
96
+ }
97
+ catch (e) {
98
+ if (e instanceof UnknownSiteError) {
99
+ const result = {
100
+ siteId,
101
+ siteUrl: '',
102
+ platform: 'unknown',
103
+ pluginReachable: false,
104
+ bearerValid: false,
105
+ etagReceived: false,
106
+ summary: `FAIL — Site "${siteId}" not found.`,
107
+ unknownSite: true,
108
+ available: e.available,
109
+ };
110
+ if (deps.log !== undefined) {
111
+ for (const line of renderTestSiteLines(result))
112
+ deps.log(line);
113
+ }
114
+ return result;
115
+ }
116
+ if (e instanceof NoDefaultSiteError) {
117
+ throw new TestSiteError('NO_DEFAULT_SITE', e.message);
118
+ }
119
+ throw e;
120
+ }
121
+ // W12-R1.2: probe lives in sites/probe.ts — single source of truth
122
+ // shared with the MCP tool surface so the two output channels never
123
+ // drift on edge cases (401 with /health rejected, 500 on /etag etc.).
124
+ const probe = await probeSite(client);
125
+ const result = {
126
+ siteId: site.id,
127
+ siteUrl: site.url,
128
+ platform: site.platform.kind,
129
+ pluginReachable: probe.plugin_reachable,
130
+ bearerValid: probe.bearer_valid,
131
+ etagReceived: probe.etag_received,
132
+ summary: probe.summary,
133
+ ...(probe.plugin_error !== undefined ? { pluginError: probe.plugin_error } : {}),
134
+ ...(probe.bearer_error !== undefined ? { bearerError: probe.bearer_error } : {}),
135
+ unknownSite: false,
136
+ };
137
+ if (deps.log !== undefined) {
138
+ for (const line of renderTestSiteLines(result))
139
+ deps.log(line);
140
+ }
141
+ return result;
142
+ }
143
+ export const DEFAULT_TEST_SITE_DEPS = {
144
+ load: loadSitesFile,
145
+ };
146
+ //# sourceMappingURL=test-site.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-site.js","sourceRoot":"","sources":["../../../src/sites/cli/test-site.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,124 @@
1
+ /**
2
+ * W4 — ClientPool: caches one {@link RestClient} per `site_id`.
3
+ *
4
+ * The pool sits between the W3 {@link SiteRegistry} (which maps a
5
+ * `site_id` → entry + memoised {@link Platform}) and the W2
6
+ * {@link SecretResolver} (which produces a bearer token at call time)
7
+ * and emits {@link PoolResolution} bundles ready for tool handlers.
8
+ *
9
+ * Design choices (per plan §W4, Z.562-644):
10
+ *
11
+ * - **One client per site_id.** The first `resolve(id)` runs bearer +
12
+ * platform resolve in parallel via `Promise.all` (saves one
13
+ * round-trip-time per cold site) and stores the constructed
14
+ * `RestClient`. All subsequent calls reuse the same instance —
15
+ * identity-equal across the process lifetime.
16
+ *
17
+ * - **Cold parallel calls are racey-but-safe.** When N callers arrive
18
+ * for the same uncached site_id simultaneously, the plan §W4
19
+ * explicitly accepts N first-time `secretResolver.resolve()` calls
20
+ * (Z.630): `op read` is idempotent + latency dominates so a
21
+ * hard-once Promise-cache would not materially help. The W3
22
+ * registry's Promise-cache handles the platform side. The last
23
+ * setter wins the `cache.set(id, client)` race; subsequent
24
+ * `resolve()` calls see a stable single instance. Identity is
25
+ * eventual, not instantaneous — but stable from call N+1 onwards.
26
+ *
27
+ * - **`invalidate(siteId)` for post-401 force-re-resolve.** When the
28
+ * auth layer detects a `401 invalid_kid`, it can drop the cached
29
+ * client so the next call re-fetches the bearer through `op` and
30
+ * constructs a fresh `RestClient`.
31
+ *
32
+ * - **Structured errors.** `NoDefaultSiteError` and `UnknownSiteError`
33
+ * carry `code` + (for unknown) `siteId` + `available[]` so the
34
+ * auth/tool layer can branch on `error.code` instead of substring
35
+ * matching the message.
36
+ *
37
+ * @license MIT
38
+ */
39
+ import { RestClient } from '../client.js';
40
+ import type { SiteRegistry, ResolvedSite } from './registry.js';
41
+ import type { SecretResolver } from './secret-resolver.js';
42
+ /**
43
+ * Thrown when `resolve()` is called without a `siteId` AND the
44
+ * registry has no default configured (neither `default_site_id` nor a
45
+ * flagged `is_default:true` entry). The auth layer surfaces this as a
46
+ * `no_default_site` MCP error with a hint to run
47
+ * `npx -y @wootsup/yt-builder-mcp add-site --default`.
48
+ */
49
+ export declare class NoDefaultSiteError extends Error {
50
+ readonly code: "no_default_site";
51
+ constructor(message?: string);
52
+ }
53
+ /**
54
+ * Thrown when `resolve('some-id')` cannot find `some-id` in the
55
+ * registry. Carries the requested `siteId` and the (insertion-order)
56
+ * list of configured ids so the caller can render a friendly
57
+ * "did-you-mean" hint.
58
+ */
59
+ export declare class UnknownSiteError extends Error {
60
+ readonly siteId: string;
61
+ readonly available: readonly string[];
62
+ readonly code: "unknown_site";
63
+ constructor(siteId: string, available: readonly string[]);
64
+ }
65
+ /**
66
+ * Bundle returned by {@link ClientPool.resolve}. Carries the cached
67
+ * REST client together with the fully-resolved site descriptor for
68
+ * downstream logging / display.
69
+ */
70
+ export interface PoolResolution {
71
+ readonly client: RestClient;
72
+ readonly site: ResolvedSite;
73
+ }
74
+ /**
75
+ * Process-local cache of {@link RestClient} instances keyed by
76
+ * `site_id`. Constructed once at server boot (W6) and shared across
77
+ * all tool handlers.
78
+ *
79
+ * The pool is NOT thread-safe in the strict sense (Node is
80
+ * single-threaded but `await` can interleave); the cold-parallel-call
81
+ * behaviour is documented at the top of this file.
82
+ */
83
+ export declare class ClientPool {
84
+ private readonly _registry;
85
+ private readonly secretResolver;
86
+ private readonly timeoutMs;
87
+ private readonly cache;
88
+ constructor(_registry: SiteRegistry, secretResolver: SecretResolver, timeoutMs?: number);
89
+ /**
90
+ * Read-only accessor for the underlying {@link SiteRegistry}. Used
91
+ * internally by the W7 `buildSitesTools(pool)` aggregator so the
92
+ * `sites_list` tool can render the configured-sites table without
93
+ * requiring callers to thread the registry separately. The registry
94
+ * itself is immutable from the outside (no mutator surface) so
95
+ * exposing it here is safe — `listForDisplay()` / `defaultSiteId()` /
96
+ * `listIds()` are all read-only operations.
97
+ *
98
+ * @internal Internal accessor for buildSitesTools; do not consume
99
+ * from tool handlers. Handlers should call `pool.resolve(siteId)`
100
+ * to get a `{client, site}` bundle instead.
101
+ */
102
+ get registry(): SiteRegistry;
103
+ /**
104
+ * Resolve a site_id to its cached {@link RestClient} + descriptor.
105
+ *
106
+ * @param siteId Explicit site_id from the tool param. When
107
+ * `undefined`, the registry's `defaultSiteId()` is used.
108
+ * @throws {NoDefaultSiteError} when `siteId` is omitted and no
109
+ * default is configured.
110
+ * @throws {UnknownSiteError} when the resolved id is not in the
111
+ * registry (either the explicit id is wrong, or the registry's
112
+ * `default_site_id` points at a stale entry).
113
+ */
114
+ resolve(siteId?: string): Promise<PoolResolution>;
115
+ /**
116
+ * Drop the cached client for `siteId` — used by the auth layer
117
+ * after a `401 invalid_kid` so the next call re-runs the bearer
118
+ * resolve and builds a fresh {@link RestClient}.
119
+ *
120
+ * No-op when `siteId` is not in the cache.
121
+ */
122
+ invalidate(siteId: string): void;
123
+ }
124
+ //# sourceMappingURL=client-pool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-pool.d.ts","sourceRoot":"","sources":["../../src/sites/client-pool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D;;;;;;GAMG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IACzC,SAAgB,IAAI,EAAG,iBAAiB,CAAU;gBAG9C,OAAO,SAG6E;CAK3F;AAED;;;;;GAKG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;aAInB,MAAM,EAAE,MAAM;aACd,SAAS,EAAE,SAAS,MAAM,EAAE;IAJhD,SAAgB,IAAI,EAAG,cAAc,CAAU;gBAG3B,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,SAAS,MAAM,EAAE;CAQnD;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC3B,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;CAC/B;AAED;;;;;;;;GAQG;AACH,qBAAa,UAAU;IAIf,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAL9B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiC;gBAGlC,SAAS,EAAE,YAAY,EACvB,cAAc,EAAE,cAAc,EAC9B,SAAS,GAAE,MAAe;IAG/C;;;;;;;;;;;;OAYG;IACH,IAAI,QAAQ,IAAI,YAAY,CAE3B;IAED;;;;;;;;;;OAUG;IACG,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IA8CvD;;;;;;OAMG;IACH,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;CAGnC"}
@@ -0,0 +1,172 @@
1
+ /**
2
+ * W4 — ClientPool: caches one {@link RestClient} per `site_id`.
3
+ *
4
+ * The pool sits between the W3 {@link SiteRegistry} (which maps a
5
+ * `site_id` → entry + memoised {@link Platform}) and the W2
6
+ * {@link SecretResolver} (which produces a bearer token at call time)
7
+ * and emits {@link PoolResolution} bundles ready for tool handlers.
8
+ *
9
+ * Design choices (per plan §W4, Z.562-644):
10
+ *
11
+ * - **One client per site_id.** The first `resolve(id)` runs bearer +
12
+ * platform resolve in parallel via `Promise.all` (saves one
13
+ * round-trip-time per cold site) and stores the constructed
14
+ * `RestClient`. All subsequent calls reuse the same instance —
15
+ * identity-equal across the process lifetime.
16
+ *
17
+ * - **Cold parallel calls are racey-but-safe.** When N callers arrive
18
+ * for the same uncached site_id simultaneously, the plan §W4
19
+ * explicitly accepts N first-time `secretResolver.resolve()` calls
20
+ * (Z.630): `op read` is idempotent + latency dominates so a
21
+ * hard-once Promise-cache would not materially help. The W3
22
+ * registry's Promise-cache handles the platform side. The last
23
+ * setter wins the `cache.set(id, client)` race; subsequent
24
+ * `resolve()` calls see a stable single instance. Identity is
25
+ * eventual, not instantaneous — but stable from call N+1 onwards.
26
+ *
27
+ * - **`invalidate(siteId)` for post-401 force-re-resolve.** When the
28
+ * auth layer detects a `401 invalid_kid`, it can drop the cached
29
+ * client so the next call re-fetches the bearer through `op` and
30
+ * constructs a fresh `RestClient`.
31
+ *
32
+ * - **Structured errors.** `NoDefaultSiteError` and `UnknownSiteError`
33
+ * carry `code` + (for unknown) `siteId` + `available[]` so the
34
+ * auth/tool layer can branch on `error.code` instead of substring
35
+ * matching the message.
36
+ *
37
+ * @license MIT
38
+ */
39
+ import { RestClient } from '../client.js';
40
+ /**
41
+ * Thrown when `resolve()` is called without a `siteId` AND the
42
+ * registry has no default configured (neither `default_site_id` nor a
43
+ * flagged `is_default:true` entry). The auth layer surfaces this as a
44
+ * `no_default_site` MCP error with a hint to run
45
+ * `npx -y @wootsup/yt-builder-mcp add-site --default`.
46
+ */
47
+ export class NoDefaultSiteError extends Error {
48
+ code = 'no_default_site';
49
+ constructor(message = 'No site_id supplied and no default site is configured. '
50
+ + 'Add a site via `npx -y @wootsup/yt-builder-mcp add-site --default --url ... --token ...` '
51
+ + 'or pass site_id explicitly. Use yootheme_builder_sites_list to discover IDs.') {
52
+ super(message);
53
+ this.name = 'NoDefaultSiteError';
54
+ }
55
+ }
56
+ /**
57
+ * Thrown when `resolve('some-id')` cannot find `some-id` in the
58
+ * registry. Carries the requested `siteId` and the (insertion-order)
59
+ * list of configured ids so the caller can render a friendly
60
+ * "did-you-mean" hint.
61
+ */
62
+ export class UnknownSiteError extends Error {
63
+ siteId;
64
+ available;
65
+ code = 'unknown_site';
66
+ constructor(siteId, available) {
67
+ super(`Site "${siteId}" not found. Available: `
68
+ + (available.length > 0 ? available.join(', ') : '(none configured)'));
69
+ this.siteId = siteId;
70
+ this.available = available;
71
+ this.name = 'UnknownSiteError';
72
+ }
73
+ }
74
+ /**
75
+ * Process-local cache of {@link RestClient} instances keyed by
76
+ * `site_id`. Constructed once at server boot (W6) and shared across
77
+ * all tool handlers.
78
+ *
79
+ * The pool is NOT thread-safe in the strict sense (Node is
80
+ * single-threaded but `await` can interleave); the cold-parallel-call
81
+ * behaviour is documented at the top of this file.
82
+ */
83
+ export class ClientPool {
84
+ _registry;
85
+ secretResolver;
86
+ timeoutMs;
87
+ cache = new Map();
88
+ constructor(_registry, secretResolver, timeoutMs = 15_000) {
89
+ this._registry = _registry;
90
+ this.secretResolver = secretResolver;
91
+ this.timeoutMs = timeoutMs;
92
+ }
93
+ /**
94
+ * Read-only accessor for the underlying {@link SiteRegistry}. Used
95
+ * internally by the W7 `buildSitesTools(pool)` aggregator so the
96
+ * `sites_list` tool can render the configured-sites table without
97
+ * requiring callers to thread the registry separately. The registry
98
+ * itself is immutable from the outside (no mutator surface) so
99
+ * exposing it here is safe — `listForDisplay()` / `defaultSiteId()` /
100
+ * `listIds()` are all read-only operations.
101
+ *
102
+ * @internal Internal accessor for buildSitesTools; do not consume
103
+ * from tool handlers. Handlers should call `pool.resolve(siteId)`
104
+ * to get a `{client, site}` bundle instead.
105
+ */
106
+ get registry() {
107
+ return this._registry;
108
+ }
109
+ /**
110
+ * Resolve a site_id to its cached {@link RestClient} + descriptor.
111
+ *
112
+ * @param siteId Explicit site_id from the tool param. When
113
+ * `undefined`, the registry's `defaultSiteId()` is used.
114
+ * @throws {NoDefaultSiteError} when `siteId` is omitted and no
115
+ * default is configured.
116
+ * @throws {UnknownSiteError} when the resolved id is not in the
117
+ * registry (either the explicit id is wrong, or the registry's
118
+ * `default_site_id` points at a stale entry).
119
+ */
120
+ async resolve(siteId) {
121
+ const id = siteId ?? this.registry.defaultSiteId();
122
+ if (id === null) {
123
+ throw new NoDefaultSiteError();
124
+ }
125
+ const entry = this.registry.get(id);
126
+ if (entry === null) {
127
+ throw new UnknownSiteError(id, this.registry.listIds());
128
+ }
129
+ let client = this.cache.get(id);
130
+ if (client === undefined) {
131
+ // Plan §W4 Z.604-608: parallelise bearer + platform on a
132
+ // cold call. The W3 registry's Promise-cache de-dupes the
133
+ // platform probe across N parallel callers; the bearer
134
+ // side is allowed N first-time calls (op read idempotent).
135
+ const [bearer, platform] = await Promise.all([
136
+ this.secretResolver.resolve(entry),
137
+ this.registry.platformFor(id),
138
+ ]);
139
+ client = new RestClient({
140
+ platform,
141
+ bearerToken: bearer.token,
142
+ timeoutMs: this.timeoutMs,
143
+ });
144
+ this.cache.set(id, client);
145
+ }
146
+ // `platformFor` is memoised in the registry; calling it a
147
+ // second time here is a O(1) Promise.resolve from the W3
148
+ // Promise-cache, NOT a fresh probe. Keeps the `ResolvedSite`
149
+ // synthesis self-contained even when the client was warm.
150
+ const platform = await this.registry.platformFor(id);
151
+ const resolved = {
152
+ id,
153
+ url: entry.url,
154
+ platform,
155
+ isDefault: entry.is_default,
156
+ bearerSource: entry.bearer_ref !== undefined ? 'op' : 'plain',
157
+ ...(entry.label !== undefined ? { label: entry.label } : {}),
158
+ };
159
+ return { client, site: resolved };
160
+ }
161
+ /**
162
+ * Drop the cached client for `siteId` — used by the auth layer
163
+ * after a `401 invalid_kid` so the next call re-runs the bearer
164
+ * resolve and builds a fresh {@link RestClient}.
165
+ *
166
+ * No-op when `siteId` is not in the cache.
167
+ */
168
+ invalidate(siteId) {
169
+ this.cache.delete(siteId);
170
+ }
171
+ }
172
+ //# sourceMappingURL=client-pool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-pool.js","sourceRoot":"","sources":["../../src/sites/client-pool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * W3 — Env-Bridge: synthesise a one-site {@link SitesFileT} from the
3
+ * legacy single-site env-vars when no `sites.json` exists on disk.
4
+ *
5
+ * Why: pre-multi-site customers configured the MCP server via
6
+ * `YTB_MCP_SITE_URL` (or the older `YTB_MCP_WP_URL`) + `YTB_MCP_BEARER_TOKEN`.
7
+ * Multi-site infra must NEVER break that experience. When no
8
+ * `sites.json` is found, the boot path consults this bridge; if both
9
+ * required env-vars are present we materialise an in-memory one-site
10
+ * file with `site_id: 'default'` and `is_default: true`. The rest of
11
+ * the system (registry, resolver, tool builder) then operates on
12
+ * `SitesFileT` uniformly — there is no second code path for the
13
+ * legacy single-site case.
14
+ *
15
+ * Returns `null` when either required env-var is missing or empty so
16
+ * the caller can fall through to "no sites configured" UX.
17
+ *
18
+ * W12-R1.1 consolidation:
19
+ * - Env-var names are imported from `../auth.js` (single source of
20
+ * truth) instead of duplicated as string-literals.
21
+ * - Raw URL + bearer are `.trim()`ed; whitespace-only values are
22
+ * treated as missing (return null), matching `loadConfig` behaviour.
23
+ * - Unknown `YTB_MCP_PLATFORM` values throw `ConfigError` so a typo
24
+ * (`woodpress`, `joomlaa`) surfaces immediately instead of silently
25
+ * downgrading to `'auto'` and confusing the runtime probe.
26
+ *
27
+ * @license MIT
28
+ */
29
+ import type { SitesFileT } from './schema.js';
30
+ /**
31
+ * Trim exactly one trailing `/` from a string. `""` stays `""`. A
32
+ * string with two trailing slashes keeps the inner one (`"a//"` →
33
+ * `"a/"`), preserving the operator's apparent intent — single-slash
34
+ * normalisation is a paste-error fix, not an aggressive cleanup.
35
+ */
36
+ export declare function stripTrailingSlash(s: string): string;
37
+ /**
38
+ * Build a one-site {@link SitesFileT} from `env`, or return `null` if
39
+ * neither URL nor bearer is configured. Order of URL precedence:
40
+ * 1. {@link ENV_SITE_URL} (new, multi-site-aware name)
41
+ * 2. {@link ENV_WP_URL} (legacy single-site name, still honoured)
42
+ *
43
+ * The platform hint comes from {@link ENV_PLATFORM} when set to
44
+ * `wordpress` / `joomla` / `auto`. An empty / unset value falls back to
45
+ * `'auto'` so the runtime probe (see `platformForUrlAsync`) decides.
46
+ * Any OTHER non-empty value throws `ConfigError` — a typo like
47
+ * `woodpress` is loud-failed at boot rather than silently degraded.
48
+ *
49
+ * Note: this function does NOT validate the URL or bearer-token shape
50
+ * beyond non-empty trimming — the W1 {@link SitesFile} schema does that
51
+ * the moment the caller hands the synthesised file to a registry
52
+ * constructor.
53
+ */
54
+ export declare function synthesiseFromEnv(env: NodeJS.ProcessEnv): SitesFileT | null;
55
+ //# sourceMappingURL=env-bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-bridge.d.ts","sourceRoot":"","sources":["../../src/sites/env-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAUH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAGpD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,CAC7B,GAAG,EAAE,MAAM,CAAC,UAAU,GACvB,UAAU,GAAG,IAAI,CAqCnB"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * W3 — Env-Bridge: synthesise a one-site {@link SitesFileT} from the
3
+ * legacy single-site env-vars when no `sites.json` exists on disk.
4
+ *
5
+ * Why: pre-multi-site customers configured the MCP server via
6
+ * `YTB_MCP_SITE_URL` (or the older `YTB_MCP_WP_URL`) + `YTB_MCP_BEARER_TOKEN`.
7
+ * Multi-site infra must NEVER break that experience. When no
8
+ * `sites.json` is found, the boot path consults this bridge; if both
9
+ * required env-vars are present we materialise an in-memory one-site
10
+ * file with `site_id: 'default'` and `is_default: true`. The rest of
11
+ * the system (registry, resolver, tool builder) then operates on
12
+ * `SitesFileT` uniformly — there is no second code path for the
13
+ * legacy single-site case.
14
+ *
15
+ * Returns `null` when either required env-var is missing or empty so
16
+ * the caller can fall through to "no sites configured" UX.
17
+ *
18
+ * W12-R1.1 consolidation:
19
+ * - Env-var names are imported from `../auth.js` (single source of
20
+ * truth) instead of duplicated as string-literals.
21
+ * - Raw URL + bearer are `.trim()`ed; whitespace-only values are
22
+ * treated as missing (return null), matching `loadConfig` behaviour.
23
+ * - Unknown `YTB_MCP_PLATFORM` values throw `ConfigError` so a typo
24
+ * (`woodpress`, `joomlaa`) surfaces immediately instead of silently
25
+ * downgrading to `'auto'` and confusing the runtime probe.
26
+ *
27
+ * @license MIT
28
+ */
29
+ import { ENV_BEARER, ENV_PLATFORM, ENV_SITE_URL, ENV_WP_URL, } from '../auth.js';
30
+ import { ConfigError } from '../errors.js';
31
+ /**
32
+ * Trim exactly one trailing `/` from a string. `""` stays `""`. A
33
+ * string with two trailing slashes keeps the inner one (`"a//"` →
34
+ * `"a/"`), preserving the operator's apparent intent — single-slash
35
+ * normalisation is a paste-error fix, not an aggressive cleanup.
36
+ */
37
+ export function stripTrailingSlash(s) {
38
+ if (s.length === 0)
39
+ return s;
40
+ return s.endsWith('/') ? s.slice(0, -1) : s;
41
+ }
42
+ /**
43
+ * Build a one-site {@link SitesFileT} from `env`, or return `null` if
44
+ * neither URL nor bearer is configured. Order of URL precedence:
45
+ * 1. {@link ENV_SITE_URL} (new, multi-site-aware name)
46
+ * 2. {@link ENV_WP_URL} (legacy single-site name, still honoured)
47
+ *
48
+ * The platform hint comes from {@link ENV_PLATFORM} when set to
49
+ * `wordpress` / `joomla` / `auto`. An empty / unset value falls back to
50
+ * `'auto'` so the runtime probe (see `platformForUrlAsync`) decides.
51
+ * Any OTHER non-empty value throws `ConfigError` — a typo like
52
+ * `woodpress` is loud-failed at boot rather than silently degraded.
53
+ *
54
+ * Note: this function does NOT validate the URL or bearer-token shape
55
+ * beyond non-empty trimming — the W1 {@link SitesFile} schema does that
56
+ * the moment the caller hands the synthesised file to a registry
57
+ * constructor.
58
+ */
59
+ export function synthesiseFromEnv(env) {
60
+ const rawUrl = (env[ENV_SITE_URL] ?? env[ENV_WP_URL] ?? '').trim();
61
+ const bearer = (env[ENV_BEARER] ?? '').trim();
62
+ if (rawUrl === '' || bearer === '')
63
+ return null;
64
+ const rawPlatform = env[ENV_PLATFORM];
65
+ const platformHint = rawPlatform === undefined ? '' : rawPlatform.trim();
66
+ let platform;
67
+ if (platformHint === '') {
68
+ platform = 'auto';
69
+ }
70
+ else if (platformHint === 'wordpress'
71
+ || platformHint === 'joomla'
72
+ || platformHint === 'auto') {
73
+ platform = platformHint;
74
+ }
75
+ else {
76
+ throw new ConfigError(`Invalid ${ENV_PLATFORM}: "${platformHint}". `
77
+ + 'Must be one of: wordpress, joomla, auto (or unset for auto-detect).');
78
+ }
79
+ return {
80
+ schema_version: 1,
81
+ default_site_id: 'default',
82
+ sites: [
83
+ {
84
+ site_id: 'default',
85
+ url: stripTrailingSlash(rawUrl),
86
+ platform,
87
+ bearer,
88
+ is_default: true,
89
+ label: 'Default site (env)',
90
+ },
91
+ ],
92
+ };
93
+ }
94
+ //# sourceMappingURL=env-bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-bridge.js","sourceRoot":"","sources":["../../src/sites/env-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EACH,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,UAAU,GACb,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAI3C;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,CAAS;IACxC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAC7B,OAAO,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAChD,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,iBAAiB,CAC7B,GAAsB;IAEtB,MAAM,MAAM,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACnE,MAAM,MAAM,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC9C,IAAI,MAAM,KAAK,EAAE,IAAI,MAAM,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAEhD,MAAM,WAAW,GAAG,GAAG,CAAC,YAAY,CAAC,CAAC;IACtC,MAAM,YAAY,GAAG,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;IACzE,IAAI,QAAyC,CAAC;IAC9C,IAAI,YAAY,KAAK,EAAE,EAAE,CAAC;QACtB,QAAQ,GAAG,MAAM,CAAC;IACtB,CAAC;SAAM,IACH,YAAY,KAAK,WAAW;WACzB,YAAY,KAAK,QAAQ;WACzB,YAAY,KAAK,MAAM,EAC5B,CAAC;QACC,QAAQ,GAAG,YAAY,CAAC;IAC5B,CAAC;SAAM,CAAC;QACJ,MAAM,IAAI,WAAW,CACjB,WAAW,YAAY,MAAM,YAAY,KAAK;cAC5C,qEAAqE,CAC1E,CAAC;IACN,CAAC;IAED,OAAO;QACH,cAAc,EAAE,CAAC;QACjB,eAAe,EAAE,SAAS;QAC1B,KAAK,EAAE;YACH;gBACI,OAAO,EAAE,SAAS;gBAClB,GAAG,EAAE,kBAAkB,CAAC,MAAM,CAAC;gBAC/B,QAAQ;gBACR,MAAM;gBACN,UAAU,EAAE,IAAI;gBAChB,KAAK,EAAE,oBAAoB;aAC9B;SACJ;KACJ,CAAC;AACN,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * W1 — Sites-Storage Foundation: default on-disk path resolver.
3
+ *
4
+ * Resolution order:
5
+ * 1. `$XDG_CONFIG_HOME/yt-builder-mcp/sites.json` (XDG Base-Dir spec)
6
+ * 2. `$HOME/.config/yt-builder-mcp/sites.json` (XDG fallback)
7
+ * 3. `os.tmpdir()/yt-builder-mcp/sites.json` + stderr warning (last
8
+ * resort — used in CI / containers where HOME may be unset).
9
+ *
10
+ * The W2/W3 layer never touches paths directly — callers ask this module
11
+ * for the canonical location and pass the resolved string to the store.
12
+ *
13
+ * @license MIT
14
+ */
15
+ /**
16
+ * Pure environment lookup so tests can inject a process-env-like object
17
+ * without mutating `process.env`. Returns the path that the load/save
18
+ * layer should target.
19
+ */
20
+ export declare function defaultSitesFilePath(env?: NodeJS.ProcessEnv, log?: (msg: string) => void): string;
21
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/sites/paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH;;;;GAIG;AACH,wBAAgB,oBAAoB,CAChC,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,GAAG,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAqD,GAC5E,MAAM,CAgBR"}