@wootsup/yt-builder-mcp 1.3.0 → 1.4.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 (133) hide show
  1. package/README.md +1 -1
  2. package/dist/cli/doctor-command.d.ts +21 -0
  3. package/dist/cli/doctor-command.d.ts.map +1 -0
  4. package/dist/cli/doctor-command.js +111 -0
  5. package/dist/cli/doctor-command.js.map +1 -0
  6. package/dist/cli/invocation-decision.d.ts.map +1 -1
  7. package/dist/cli/invocation-decision.js +2 -0
  8. package/dist/cli/invocation-decision.js.map +1 -1
  9. package/dist/client.d.ts +14 -0
  10. package/dist/client.d.ts.map +1 -1
  11. package/dist/client.js +9 -0
  12. package/dist/client.js.map +1 -1
  13. package/dist/diagnostics/ca-trust.d.ts +16 -0
  14. package/dist/diagnostics/ca-trust.d.ts.map +1 -0
  15. package/dist/diagnostics/ca-trust.js +54 -0
  16. package/dist/diagnostics/ca-trust.js.map +1 -0
  17. package/dist/diagnostics/diagnose-network-error.d.ts +37 -0
  18. package/dist/diagnostics/diagnose-network-error.d.ts.map +1 -0
  19. package/dist/diagnostics/diagnose-network-error.js +158 -0
  20. package/dist/diagnostics/diagnose-network-error.js.map +1 -0
  21. package/dist/diagnostics/doctor.d.ts +62 -0
  22. package/dist/diagnostics/doctor.d.ts.map +1 -0
  23. package/dist/diagnostics/doctor.js +165 -0
  24. package/dist/diagnostics/doctor.js.map +1 -0
  25. package/dist/gateway/essentials.d.ts +1 -1
  26. package/dist/gateway/essentials.d.ts.map +1 -1
  27. package/dist/gateway/essentials.js +5 -0
  28. package/dist/gateway/essentials.js.map +1 -1
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +35 -0
  31. package/dist/index.js.map +1 -1
  32. package/dist/install-skill.d.ts +1 -1
  33. package/dist/install-skill.js +1 -1
  34. package/dist/net/extra-headers.d.ts +66 -0
  35. package/dist/net/extra-headers.d.ts.map +1 -0
  36. package/dist/net/extra-headers.js +164 -0
  37. package/dist/net/extra-headers.js.map +1 -0
  38. package/dist/net/internal-host.d.ts +45 -0
  39. package/dist/net/internal-host.d.ts.map +1 -0
  40. package/dist/net/internal-host.js +164 -0
  41. package/dist/net/internal-host.js.map +1 -0
  42. package/dist/net/proxy-dispatcher.d.ts +40 -0
  43. package/dist/net/proxy-dispatcher.d.ts.map +1 -0
  44. package/dist/net/proxy-dispatcher.js +65 -0
  45. package/dist/net/proxy-dispatcher.js.map +1 -0
  46. package/dist/proxy/bridge.d.ts +18 -11
  47. package/dist/proxy/bridge.d.ts.map +1 -1
  48. package/dist/proxy/bridge.js +30 -4
  49. package/dist/proxy/bridge.js.map +1 -1
  50. package/dist/setup-ca-env.d.ts +40 -0
  51. package/dist/setup-ca-env.d.ts.map +1 -0
  52. package/dist/setup-ca-env.js +58 -0
  53. package/dist/setup-ca-env.js.map +1 -0
  54. package/dist/setup-ca-reprobe.d.ts +38 -0
  55. package/dist/setup-ca-reprobe.d.ts.map +1 -0
  56. package/dist/setup-ca-reprobe.js +68 -0
  57. package/dist/setup-ca-reprobe.js.map +1 -0
  58. package/dist/setup-cli.d.ts +37 -2
  59. package/dist/setup-cli.d.ts.map +1 -1
  60. package/dist/setup-cli.js +178 -4
  61. package/dist/setup-cli.js.map +1 -1
  62. package/dist/setup-npx-spec.d.ts +44 -0
  63. package/dist/setup-npx-spec.d.ts.map +1 -0
  64. package/dist/setup-npx-spec.js +66 -0
  65. package/dist/setup-npx-spec.js.map +1 -0
  66. package/dist/setup-prompts.d.ts +14 -1
  67. package/dist/setup-prompts.d.ts.map +1 -1
  68. package/dist/setup-prompts.js +53 -1
  69. package/dist/setup-prompts.js.map +1 -1
  70. package/dist/setup-wizard-defaults.d.ts.map +1 -1
  71. package/dist/setup-wizard-defaults.js +29 -5
  72. package/dist/setup-wizard-defaults.js.map +1 -1
  73. package/dist/setup-wizard-handshake.d.ts +4 -4
  74. package/dist/setup-wizard-handshake.d.ts.map +1 -1
  75. package/dist/setup-wizard-handshake.js +46 -12
  76. package/dist/setup-wizard-handshake.js.map +1 -1
  77. package/dist/setup-wizard-types.d.ts +81 -0
  78. package/dist/setup-wizard-types.d.ts.map +1 -1
  79. package/dist/setup-wizard.d.ts +2 -2
  80. package/dist/setup-wizard.d.ts.map +1 -1
  81. package/dist/setup-wizard.js +133 -18
  82. package/dist/setup-wizard.js.map +1 -1
  83. package/dist/sites/client-pool.d.ts +8 -1
  84. package/dist/sites/client-pool.d.ts.map +1 -1
  85. package/dist/sites/client-pool.js +13 -1
  86. package/dist/sites/client-pool.js.map +1 -1
  87. package/dist/tools/elements/builders.d.ts.map +1 -1
  88. package/dist/tools/elements/builders.js +4 -2
  89. package/dist/tools/elements/builders.js.map +1 -1
  90. package/dist/tools/elements/handlers.d.ts +2 -1
  91. package/dist/tools/elements/handlers.d.ts.map +1 -1
  92. package/dist/tools/elements/handlers.js +3 -22
  93. package/dist/tools/elements/handlers.js.map +1 -1
  94. package/dist/tools/format/health-format.d.ts +3 -0
  95. package/dist/tools/format/health-format.d.ts.map +1 -1
  96. package/dist/tools/format/health-format.js +21 -4
  97. package/dist/tools/format/health-format.js.map +1 -1
  98. package/dist/tools/health.d.ts.map +1 -1
  99. package/dist/tools/health.js +32 -0
  100. package/dist/tools/health.js.map +1 -1
  101. package/dist/tools/pages/builders.d.ts.map +1 -1
  102. package/dist/tools/pages/builders.js +78 -27
  103. package/dist/tools/pages/builders.js.map +1 -1
  104. package/dist/tools/pages/handlers-read.d.ts +36 -5
  105. package/dist/tools/pages/handlers-read.d.ts.map +1 -1
  106. package/dist/tools/pages/handlers-read.js +217 -34
  107. package/dist/tools/pages/handlers-read.js.map +1 -1
  108. package/dist/tools/pages/schemas.d.ts +20 -5
  109. package/dist/tools/pages/schemas.d.ts.map +1 -1
  110. package/dist/tools/pages/schemas.js +44 -6
  111. package/dist/tools/pages/schemas.js.map +1 -1
  112. package/dist/tools/shared-schemas.d.ts +3 -0
  113. package/dist/tools/shared-schemas.d.ts.map +1 -1
  114. package/dist/tools/shared-schemas.js +28 -0
  115. package/dist/tools/shared-schemas.js.map +1 -1
  116. package/dist/tools/sources/builders.d.ts.map +1 -1
  117. package/dist/tools/sources/builders.js +30 -5
  118. package/dist/tools/sources/builders.js.map +1 -1
  119. package/dist/tools/sources/handlers.d.ts +9 -1
  120. package/dist/tools/sources/handlers.d.ts.map +1 -1
  121. package/dist/tools/sources/handlers.js +62 -12
  122. package/dist/tools/sources/handlers.js.map +1 -1
  123. package/dist/tools/sparse-fields.d.ts +50 -0
  124. package/dist/tools/sparse-fields.d.ts.map +1 -1
  125. package/dist/tools/sparse-fields.js +107 -0
  126. package/dist/tools/sparse-fields.js.map +1 -1
  127. package/dist/update/update-check.d.ts +56 -0
  128. package/dist/update/update-check.d.ts.map +1 -0
  129. package/dist/update/update-check.js +155 -0
  130. package/dist/update/update-check.js.map +1 -0
  131. package/manifest.json +1 -1
  132. package/package.json +2 -1
  133. package/skills/yt-builder-mcp/SKILL.md +13 -12
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Classify a host as internal. A host is internal when ANY of:
3
+ * - it is `localhost`,
4
+ * - it is a private/loopback/link-local IPv4 or IPv6 literal,
5
+ * - it ends in an internal DNS suffix (`.local`, `.internal`, …),
6
+ * - it is a bare single-label hostname (no dot — an intranet short name).
7
+ *
8
+ * A normal public FQDN (`example.com`, `acme.co.uk`) is NOT internal.
9
+ */
10
+ export declare function isInternalHost(rawHost: string): boolean;
11
+ /** Extract a lowercased, bracket-stripped host from a URL string, or null. */
12
+ export declare function hostFromUrl(rawUrl: string): string | null;
13
+ export interface NoProxyDerivation {
14
+ /** True when this call would change NO_PROXY (host added). */
15
+ readonly changed: boolean;
16
+ /**
17
+ * The effective NO_PROXY value to use. When `changed` is false this is the
18
+ * input value verbatim (or undefined when it was unset and stays unset).
19
+ */
20
+ readonly noProxy: string | undefined;
21
+ /** The internal host that was added, or null when nothing was added. */
22
+ readonly addedHost: string | null;
23
+ /** Non-fatal reason when nothing was added (for an optional debug log). */
24
+ readonly reason?: string;
25
+ }
26
+ export interface DeriveNoProxyInput {
27
+ /** Source of env vars. */
28
+ readonly env: NodeJS.ProcessEnv;
29
+ /** The configured site URL (typically `YTB_MCP_SITE_URL`). */
30
+ readonly siteUrl: string | undefined;
31
+ }
32
+ /**
33
+ * Compute the NO_PROXY value to apply so an internal site host bypasses a
34
+ * configured corporate proxy. Idempotent + non-clobbering:
35
+ *
36
+ * - No proxy configured → no change (the agent isn't installed anyway).
37
+ * - `siteUrl` missing / unparseable / clearly public → no change.
38
+ * - Host already present in NO_PROXY (case-insensitive) → no change.
39
+ * - Otherwise → append the host to the existing operator NO_PROXY (preserved
40
+ * verbatim, comma-joined), or seed NO_PROXY with just the host when unset.
41
+ *
42
+ * Pure: returns the value to write; the caller mutates `process.env`.
43
+ */
44
+ export declare function deriveNoProxyForInternalSite(input: DeriveNoProxyInput): NoProxyDerivation;
45
+ //# sourceMappingURL=internal-host.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"internal-host.d.ts","sourceRoot":"","sources":["../../src/net/internal-host.ts"],"names":[],"mappings":"AAmEA;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAWvD;AAED,8EAA8E;AAC9E,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CASzD;AAeD,MAAM,WAAW,iBAAiB;IAC9B,8DAA8D;IAC9D,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,wEAAwE;IACxE,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAkB;IAC/B,0BAA0B;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IAChC,8DAA8D;IAC9D,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;CACxC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,kBAAkB,GAAG,iBAAiB,CAwBzF"}
@@ -0,0 +1,164 @@
1
+ // packages/mcp/src/net/internal-host.ts
2
+ /**
3
+ * Classify a site host as internal vs public, and derive a NO_PROXY addition
4
+ * for an internal host when a forward proxy is configured (Glöckle gap #9).
5
+ *
6
+ * Why this exists: when a corporate `HTTP(S)_PROXY` is set, undici's
7
+ * `EnvHttpProxyAgent` routes EVERYTHING through it — including traffic to an
8
+ * intranet WordPress/Joomla that the proxy can't (or shouldn't) reach. The
9
+ * `NO_PROXY` exception list fixes that, but only if the operator pre-set it.
10
+ * A common Glöckle-class failure: the proxy reaches the public internet fine,
11
+ * but the internal CMS is direct-only (or vice-versa), and outbound MCP traffic
12
+ * silently dies.
13
+ *
14
+ * We therefore classify the configured `YTB_MCP_SITE_URL` host and, when a
15
+ * proxy is set AND the host is clearly internal, add it to the effective
16
+ * NO_PROXY the agent reads. **Conservative by construction:** only RFC1918 /
17
+ * loopback / link-local IPs, `.local` / `.internal` / `.intranet` suffixes, and
18
+ * bare single-label hostnames (no dot) are treated as internal. A normal
19
+ * public FQDN is NEVER routed to NO_PROXY.
20
+ *
21
+ * Pure + env-injectable so it is unit-testable offline. Never mutates input.
22
+ *
23
+ * @license MIT
24
+ */
25
+ /** Internal-by-convention DNS suffixes (RFC 6762 + common intranet TLDs). */
26
+ const INTERNAL_SUFFIXES = ['.local', '.internal', '.intranet', '.lan', '.home.arpa'];
27
+ /** Strip an `[…]` IPv6 bracket wrapper and a trailing `.` if present. */
28
+ function normalizeHost(host) {
29
+ let h = host.trim().toLowerCase();
30
+ if (h.startsWith('[') && h.endsWith(']'))
31
+ h = h.slice(1, -1);
32
+ if (h.endsWith('.'))
33
+ h = h.slice(0, -1);
34
+ return h;
35
+ }
36
+ /** Is `host` an IPv4 literal in a private/loopback/link-local range? */
37
+ function isPrivateIpv4(host) {
38
+ const m = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(host);
39
+ if (m === null)
40
+ return false;
41
+ const octets = [m[1], m[2], m[3], m[4]].map((o) => Number.parseInt(o, 10));
42
+ if (octets.some((o) => o > 255))
43
+ return false;
44
+ const [a, b] = octets;
45
+ if (a === 10)
46
+ return true; // 10.0.0.0/8
47
+ if (a === 127)
48
+ return true; // 127.0.0.0/8 loopback
49
+ if (a === 192 && b === 168)
50
+ return true; // 192.168.0.0/16
51
+ if (a === 172 && b >= 16 && b <= 31)
52
+ return true; // 172.16.0.0/12
53
+ if (a === 169 && b === 254)
54
+ return true; // 169.254.0.0/16 link-local
55
+ return false;
56
+ }
57
+ /** Is `host` an IPv6 loopback / unique-local (fc00::/7) / link-local (fe80::/10)? */
58
+ function isPrivateIpv6(host) {
59
+ if (!host.includes(':'))
60
+ return false;
61
+ if (host === '::1')
62
+ return true; // loopback
63
+ const first = host.split(':')[0] ?? '';
64
+ if (first === '')
65
+ return false; // e.g. "::" unspecified — not a real site host
66
+ const prefix = Number.parseInt(first, 16);
67
+ if (Number.isNaN(prefix))
68
+ return false;
69
+ // fc00::/7 (unique-local) → first hextet 0xfc00–0xfdff.
70
+ if (prefix >= 0xfc00 && prefix <= 0xfdff)
71
+ return true;
72
+ // fe80::/10 (link-local) → first hextet 0xfe80–0xfebf.
73
+ if (prefix >= 0xfe80 && prefix <= 0xfebf)
74
+ return true;
75
+ return false;
76
+ }
77
+ /**
78
+ * Classify a host as internal. A host is internal when ANY of:
79
+ * - it is `localhost`,
80
+ * - it is a private/loopback/link-local IPv4 or IPv6 literal,
81
+ * - it ends in an internal DNS suffix (`.local`, `.internal`, …),
82
+ * - it is a bare single-label hostname (no dot — an intranet short name).
83
+ *
84
+ * A normal public FQDN (`example.com`, `acme.co.uk`) is NOT internal.
85
+ */
86
+ export function isInternalHost(rawHost) {
87
+ const host = normalizeHost(rawHost);
88
+ if (host === '')
89
+ return false;
90
+ if (host === 'localhost')
91
+ return true;
92
+ if (isPrivateIpv4(host))
93
+ return true;
94
+ if (isPrivateIpv6(host))
95
+ return true;
96
+ if (INTERNAL_SUFFIXES.some((s) => host.endsWith(s)))
97
+ return true;
98
+ // A bare hostname with no dot is an intranet short name (e.g. "wpserver").
99
+ // IPv6 literals contain ":" and are handled above; guard against them here.
100
+ if (!host.includes('.') && !host.includes(':'))
101
+ return true;
102
+ return false;
103
+ }
104
+ /** Extract a lowercased, bracket-stripped host from a URL string, or null. */
105
+ export function hostFromUrl(rawUrl) {
106
+ const url = rawUrl.trim();
107
+ if (url === '')
108
+ return null;
109
+ try {
110
+ const parsed = new URL(url);
111
+ return normalizeHost(parsed.hostname);
112
+ }
113
+ catch {
114
+ return null;
115
+ }
116
+ }
117
+ /** Is any forward-proxy configured in `env`? */
118
+ function hasProxyConfigured(env) {
119
+ return Boolean(env.HTTPS_PROXY ?? env.https_proxy ?? env.HTTP_PROXY ?? env.http_proxy);
120
+ }
121
+ /** Split a NO_PROXY string into trimmed, lowercased, non-empty entries. */
122
+ function splitNoProxy(value) {
123
+ return value
124
+ .split(',')
125
+ .map((e) => e.trim().toLowerCase())
126
+ .filter((e) => e !== '');
127
+ }
128
+ /**
129
+ * Compute the NO_PROXY value to apply so an internal site host bypasses a
130
+ * configured corporate proxy. Idempotent + non-clobbering:
131
+ *
132
+ * - No proxy configured → no change (the agent isn't installed anyway).
133
+ * - `siteUrl` missing / unparseable / clearly public → no change.
134
+ * - Host already present in NO_PROXY (case-insensitive) → no change.
135
+ * - Otherwise → append the host to the existing operator NO_PROXY (preserved
136
+ * verbatim, comma-joined), or seed NO_PROXY with just the host when unset.
137
+ *
138
+ * Pure: returns the value to write; the caller mutates `process.env`.
139
+ */
140
+ export function deriveNoProxyForInternalSite(input) {
141
+ const { env, siteUrl } = input;
142
+ const current = env.NO_PROXY ?? env.no_proxy;
143
+ if (!hasProxyConfigured(env)) {
144
+ return { changed: false, noProxy: current, addedHost: null, reason: 'no proxy configured' };
145
+ }
146
+ const host = siteUrl !== undefined ? hostFromUrl(siteUrl) : null;
147
+ if (host === null) {
148
+ return { changed: false, noProxy: current, addedHost: null, reason: 'no parseable site URL' };
149
+ }
150
+ if (!isInternalHost(host)) {
151
+ return { changed: false, noProxy: current, addedHost: null, reason: 'site host is public' };
152
+ }
153
+ const existing = current !== undefined ? splitNoProxy(current) : [];
154
+ if (existing.includes(host)) {
155
+ return { changed: false, noProxy: current, addedHost: null, reason: 'host already in NO_PROXY' };
156
+ }
157
+ // Append, preserving the operator's existing entries verbatim (original
158
+ // casing/order) — we only add the host at the end.
159
+ const next = current !== undefined && current.trim() !== ''
160
+ ? `${current},${host}`
161
+ : host;
162
+ return { changed: true, noProxy: next, addedHost: host };
163
+ }
164
+ //# sourceMappingURL=internal-host.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"internal-host.js","sourceRoot":"","sources":["../../src/net/internal-host.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import { type Dispatcher } from 'undici';
2
+ /**
3
+ * Injection seam for tests — production uses the real undici functions + env.
4
+ */
5
+ export interface ProxyDispatcherDeps {
6
+ /** Defaults to `process.env`. Read for HTTP(S)_PROXY presence. */
7
+ env?: NodeJS.ProcessEnv;
8
+ /** Defaults to undici's {@link undiciSetGlobalDispatcher}. */
9
+ setGlobalDispatcher?: (dispatcher: Dispatcher) => void;
10
+ /** Defaults to `() => new EnvHttpProxyAgent()`. */
11
+ makeEnvHttpProxyAgent?: () => Dispatcher;
12
+ /** Defaults to `console.error`. */
13
+ stderr?: (message: string) => void;
14
+ }
15
+ /**
16
+ * Install a process-global undici dispatcher that honors the enterprise
17
+ * `HTTP_PROXY` / `HTTPS_PROXY` / `NO_PROXY` environment variables at request
18
+ * time (undici's {@link EnvHttpProxyAgent}).
19
+ *
20
+ * Why this exists: Node's global `fetch` only auto-honors proxy env vars from
21
+ * **Node 24** (`NODE_USE_ENV_PROXY`). On the Node 18–23 floor this package
22
+ * supports, an enterprise forward/auth proxy is otherwise ignored and outbound
23
+ * MCP traffic silently fails. Node's global `fetch` reads the global dispatcher,
24
+ * and so does the MCP SDK's `StreamableHTTPClientTransport` (proxy mode) and the
25
+ * fat-mode `RestClient` — so one global dispatcher covers every outbound call.
26
+ *
27
+ * Installed ONLY when a forward proxy is actually configured
28
+ * (`HTTP_PROXY`/`HTTPS_PROXY`). With no proxy env, the default dispatcher (a
29
+ * direct connection) is already correct, so we skip — this also avoids undici's
30
+ * `EnvHttpProxyAgent is experimental` startup warning for the common, direct
31
+ * case. Corporate TLS-inspection root CAs are handled separately by Node's
32
+ * native `NODE_EXTRA_CA_CERTS` (undici's default connector honors it).
33
+ *
34
+ * Never throws: a failure to install (exotic runtime, frozen global) degrades
35
+ * to a stderr warning and a direct connection, returning `false`.
36
+ *
37
+ * @returns `true` if the dispatcher was installed, `false` if skipped/degraded.
38
+ */
39
+ export declare function installGlobalProxyDispatcher(deps?: ProxyDispatcherDeps): boolean;
40
+ //# sourceMappingURL=proxy-dispatcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy-dispatcher.d.ts","sourceRoot":"","sources":["../../src/net/proxy-dispatcher.ts"],"names":[],"mappings":"AACA,OAAO,EAAuE,KAAK,UAAU,EAAE,MAAM,QAAQ,CAAC;AAI9G;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAChC,kEAAkE;IAClE,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,8DAA8D;IAC9D,mBAAmB,CAAC,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;IACvD,mDAAmD;IACnD,qBAAqB,CAAC,EAAE,MAAM,UAAU,CAAC;IACzC,mCAAmC;IACnC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC;AAOD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,4BAA4B,CAAC,IAAI,GAAE,mBAAwB,GAAG,OAAO,CAkCpF"}
@@ -0,0 +1,65 @@
1
+ // packages/mcp/src/net/proxy-dispatcher.ts
2
+ import { EnvHttpProxyAgent, setGlobalDispatcher as undiciSetGlobalDispatcher } from 'undici';
3
+ import { deriveNoProxyForInternalSite } from './internal-host.js';
4
+ /** Is any forward-proxy configured in the environment? */
5
+ function hasProxyConfigured(env) {
6
+ return Boolean(env.HTTPS_PROXY ?? env.https_proxy ?? env.HTTP_PROXY ?? env.http_proxy);
7
+ }
8
+ /**
9
+ * Install a process-global undici dispatcher that honors the enterprise
10
+ * `HTTP_PROXY` / `HTTPS_PROXY` / `NO_PROXY` environment variables at request
11
+ * time (undici's {@link EnvHttpProxyAgent}).
12
+ *
13
+ * Why this exists: Node's global `fetch` only auto-honors proxy env vars from
14
+ * **Node 24** (`NODE_USE_ENV_PROXY`). On the Node 18–23 floor this package
15
+ * supports, an enterprise forward/auth proxy is otherwise ignored and outbound
16
+ * MCP traffic silently fails. Node's global `fetch` reads the global dispatcher,
17
+ * and so does the MCP SDK's `StreamableHTTPClientTransport` (proxy mode) and the
18
+ * fat-mode `RestClient` — so one global dispatcher covers every outbound call.
19
+ *
20
+ * Installed ONLY when a forward proxy is actually configured
21
+ * (`HTTP_PROXY`/`HTTPS_PROXY`). With no proxy env, the default dispatcher (a
22
+ * direct connection) is already correct, so we skip — this also avoids undici's
23
+ * `EnvHttpProxyAgent is experimental` startup warning for the common, direct
24
+ * case. Corporate TLS-inspection root CAs are handled separately by Node's
25
+ * native `NODE_EXTRA_CA_CERTS` (undici's default connector honors it).
26
+ *
27
+ * Never throws: a failure to install (exotic runtime, frozen global) degrades
28
+ * to a stderr warning and a direct connection, returning `false`.
29
+ *
30
+ * @returns `true` if the dispatcher was installed, `false` if skipped/degraded.
31
+ */
32
+ export function installGlobalProxyDispatcher(deps = {}) {
33
+ const env = deps.env ?? process.env;
34
+ if (!hasProxyConfigured(env)) {
35
+ // No proxy to honor — leave the default (direct) dispatcher in place.
36
+ return false;
37
+ }
38
+ const setDispatcher = deps.setGlobalDispatcher ?? undiciSetGlobalDispatcher;
39
+ const makeAgent = deps.makeEnvHttpProxyAgent ?? (() => new EnvHttpProxyAgent());
40
+ const warn = deps.stderr ?? ((message) => console.error(message));
41
+ // Glöckle gap #9 — when the configured site is INTERNAL (RFC1918 / .local /
42
+ // bare hostname / loopback), make sure its host is in NO_PROXY so intranet
43
+ // traffic bypasses the corporate proxy. Idempotent + non-clobbering: the
44
+ // operator's NO_PROXY is preserved verbatim, the host is only appended.
45
+ // We mutate `env` BEFORE constructing the agent because EnvHttpProxyAgent
46
+ // snapshots NO_PROXY at construction. Public hosts are never touched.
47
+ const noProxy = deriveNoProxyForInternalSite({ env, siteUrl: env.YTB_MCP_SITE_URL });
48
+ if (noProxy.changed && noProxy.noProxy !== undefined) {
49
+ // Set BOTH cases so undici reads it regardless of which it prefers.
50
+ env.NO_PROXY = noProxy.noProxy;
51
+ env.no_proxy = noProxy.noProxy;
52
+ warn(`[yt-builder-mcp] internal site host "${noProxy.addedHost ?? ''}" added to NO_PROXY so it bypasses the configured proxy.`);
53
+ }
54
+ try {
55
+ setDispatcher(makeAgent());
56
+ return true;
57
+ }
58
+ catch (err) {
59
+ const message = err instanceof Error ? err.message : String(err);
60
+ // stderr only — stdout is the JSON-RPC channel in stdio transport.
61
+ warn(`[yt-builder-mcp] proxy dispatcher init failed (continuing with a direct connection): ${message}`);
62
+ return false;
63
+ }
64
+ }
65
+ //# sourceMappingURL=proxy-dispatcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy-dispatcher.js","sourceRoot":"","sources":["../../src/net/proxy-dispatcher.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,IAAI,yBAAyB,EAAmB,MAAM,QAAQ,CAAC;AAE9G,OAAO,EAAE,4BAA4B,EAAE,MAAM,oBAAoB,CAAC;AAgBlE,0DAA0D;AAC1D,SAAS,kBAAkB,CAAC,GAAsB;IAC9C,OAAO,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC;AAC3F,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,4BAA4B,CAAC,OAA4B,EAAE;IACvE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IACpC,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3B,sEAAsE;QACtE,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,MAAM,aAAa,GAAG,IAAI,CAAC,mBAAmB,IAAI,yBAAyB,CAAC;IAC5E,MAAM,SAAS,GAAG,IAAI,CAAC,qBAAqB,IAAI,CAAC,GAAe,EAAE,CAAC,IAAI,iBAAiB,EAAE,CAAC,CAAC;IAC5F,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,OAAe,EAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;IAEhF,4EAA4E;IAC5E,2EAA2E;IAC3E,yEAAyE;IACzE,wEAAwE;IACxE,0EAA0E;IAC1E,sEAAsE;IACtE,MAAM,OAAO,GAAG,4BAA4B,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,CAAC,gBAAgB,EAAE,CAAC,CAAC;IACrF,IAAI,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QACnD,oEAAoE;QACpE,GAAG,CAAC,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;QAC/B,GAAG,CAAC,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;QAC/B,IAAI,CAAC,wCAAwC,OAAO,CAAC,SAAS,IAAI,EAAE,0DAA0D,CAAC,CAAC;IACpI,CAAC;IAED,IAAI,CAAC;QACD,aAAa,CAAC,SAAS,EAAE,CAAC,CAAC;QAC3B,OAAO,IAAI,CAAC;IAChB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACX,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,mEAAmE;QACnE,IAAI,CAAC,wFAAwF,OAAO,EAAE,CAAC,CAAC;QACxG,OAAO,KAAK,CAAC;IACjB,CAAC;AACL,CAAC"}
@@ -13,24 +13,31 @@ import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
13
13
  * therefore `.catch()`es its `send()`.
14
14
  */
15
15
  export declare function bridgeTransports(local: Transport, remote: Transport): void;
16
- /** Build the real transports + bridge them; resolves when EITHER side closes. */
17
- export declare function runProxy(config: {
16
+ /** Config the proxy run needs: the upstream endpoint, the Bearer, and any
17
+ * corporate extra headers (Glöckle gap #14) to attach to every upstream
18
+ * request. The Bearer always wins on collision. */
19
+ interface ProxyConfig {
18
20
  endpointUrl: URL;
19
21
  token: string;
20
- }, deps?: {
21
- makeTransports?: (c: {
22
- endpointUrl: URL;
23
- token: string;
24
- }) => {
22
+ /** Extra headers from `YTB_MCP_EXTRA_HEADERS` (Basic-Auth / WAF / gateway). */
23
+ extraHeaders?: Record<string, string>;
24
+ }
25
+ /**
26
+ * Build the upstream `requestInit.headers` for proxy mode: the Bearer is
27
+ * authoritative; corporate extra headers (Glöckle gap #14) only fill gaps —
28
+ * `Authorization` can NEVER be overwritten. Exported for unit tests.
29
+ */
30
+ export declare function buildProxyRequestHeaders(config: ProxyConfig): Record<string, string>;
31
+ /** Build the real transports + bridge them; resolves when EITHER side closes. */
32
+ export declare function runProxy(config: ProxyConfig, deps?: {
33
+ makeTransports?: (c: ProxyConfig) => {
25
34
  local: Transport;
26
35
  remote: Transport;
27
36
  };
28
37
  }): Promise<void>;
29
38
  /** Returns true if proxy mode handled the run; false → caller continues with fat. */
30
39
  export declare function maybeRunProxy(env: NodeJS.ProcessEnv, deps?: {
31
- runProxy?: (c: {
32
- endpointUrl: URL;
33
- token: string;
34
- }) => Promise<void>;
40
+ runProxy?: (c: ProxyConfig) => Promise<void>;
35
41
  }): Promise<boolean>;
42
+ export {};
36
43
  //# sourceMappingURL=bridge.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../../src/proxy/bridge.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+CAA+C,CAAC;AA6B/E;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,GAAG,IAAI,CA2C1E;AAcD,iFAAiF;AACjF,wBAAsB,QAAQ,CAC5B,MAAM,EAAE;IAAE,WAAW,EAAE,GAAG,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAC3C,IAAI,GAAE;IACJ,cAAc,CAAC,EAAE,CAAC,CAAC,EAAE;QAAE,WAAW,EAAE,GAAG,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK;QAC3D,KAAK,EAAE,SAAS,CAAC;QACjB,MAAM,EAAE,SAAS,CAAC;KACnB,CAAC;CACE,GACL,OAAO,CAAC,IAAI,CAAC,CAyBf;AAED,qFAAqF;AACrF,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE;QAAE,WAAW,EAAE,GAAG,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAAO,GAClF,OAAO,CAAC,OAAO,CAAC,CAMlB"}
1
+ {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../../src/proxy/bridge.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+CAA+C,CAAC;AA+B/E;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,GAAG,IAAI,CAoD1E;AAED;;mDAEmD;AACnD,UAAU,WAAW;IACnB,WAAW,EAAE,GAAG,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,+EAA+E;IAC/E,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACvC;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAKpF;AAcD,iFAAiF;AACjF,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,WAAW,EACnB,IAAI,GAAE;IACJ,cAAc,CAAC,EAAE,CAAC,CAAC,EAAE,WAAW,KAAK;QACnC,KAAK,EAAE,SAAS,CAAC;QACjB,MAAM,EAAE,SAAS,CAAC;KACnB,CAAC;CACE,GACL,OAAO,CAAC,IAAI,CAAC,CAyBf;AAED,qFAAqF;AACrF,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAAO,GAC1D,OAAO,CAAC,OAAO,CAAC,CAalB"}
@@ -1,6 +1,8 @@
1
1
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
2
2
  import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
3
3
  import { resolveMode } from './mode.js';
4
+ import { diagnoseNetworkError } from '../diagnostics/diagnose-network-error.js';
5
+ import { mergeExtraHeaders, parseExtraHeaders } from '../net/extra-headers.js';
4
6
  /** Narrow an unknown caught value to a human-readable message (no `any`). */
5
7
  function errorMessage(err) {
6
8
  if (err instanceof Error)
@@ -43,8 +45,14 @@ export function bridgeTransports(local, remote) {
43
45
  local.onmessage = (msg) => {
44
46
  remote.send(msg).catch((err) => {
45
47
  const message = errorMessage(err);
48
+ // Turn an opaque transport failure (TLS-inspection CA, proxy, DNS,
49
+ // timeout) into an actionable hint — the Glöckle stress-test died with a
50
+ // 30s timeout that looked like an auth problem. Falls back to the generic
51
+ // config hint when the cause is not recognised.
52
+ const diag = diagnoseNetworkError(err);
53
+ const hint = diag !== null ? `${diag.summary} Fix: ${diag.fix}` : 'Check YTB_MCP_SITE_URL / YTB_MCP_BEARER_TOKEN.';
46
54
  // stderr only — stdout is the JSON-RPC channel.
47
- console.error('[yt-builder-proxy] upstream send failed:', message);
55
+ console.error('[yt-builder-proxy] upstream send failed:', message + (diag !== null ? `\n → ${diag.fix}` : ''));
48
56
  const id = requestId(msg);
49
57
  if (id !== null) {
50
58
  // Hand the client an actionable error instead of a dead pipe.
@@ -53,7 +61,7 @@ export function bridgeTransports(local, remote) {
53
61
  id,
54
62
  error: {
55
63
  code: -32000,
56
- message: `Proxy upstream error: ${message}. Check YTB_MCP_SITE_URL / YTB_MCP_BEARER_TOKEN.`,
64
+ message: `Proxy upstream error: ${message}. ${hint}`,
57
65
  },
58
66
  };
59
67
  local.send(frame).catch((sendErr) => {
@@ -84,10 +92,21 @@ export function bridgeTransports(local, remote) {
84
92
  console.error('[yt-builder-proxy] remote transport error:', err.message);
85
93
  };
86
94
  }
95
+ /**
96
+ * Build the upstream `requestInit.headers` for proxy mode: the Bearer is
97
+ * authoritative; corporate extra headers (Glöckle gap #14) only fill gaps —
98
+ * `Authorization` can NEVER be overwritten. Exported for unit tests.
99
+ */
100
+ export function buildProxyRequestHeaders(config) {
101
+ const base = { Authorization: `Bearer ${config.token}` };
102
+ return config.extraHeaders !== undefined && Object.keys(config.extraHeaders).length > 0
103
+ ? mergeExtraHeaders(base, config.extraHeaders)
104
+ : base;
105
+ }
87
106
  /** Injection seam for tests — production builds the real SDK transports. */
88
107
  function defaultTransports(config) {
89
108
  const remote = new StreamableHTTPClientTransport(config.endpointUrl, {
90
- requestInit: { headers: { Authorization: `Bearer ${config.token}` } },
109
+ requestInit: { headers: buildProxyRequestHeaders(config) },
91
110
  });
92
111
  const local = new StdioServerTransport();
93
112
  return { local, remote };
@@ -123,7 +142,14 @@ export async function maybeRunProxy(env, deps = {}) {
123
142
  if (resolved.mode !== 'proxy')
124
143
  return false;
125
144
  const run = deps.runProxy ?? runProxy;
126
- await run({ endpointUrl: resolved.endpointUrl, token: resolved.token });
145
+ // Glöckle gap #14 — attach corporate extra headers (Basic-Auth / WAF / gateway)
146
+ // to every upstream request. parseExtraHeaders reads YTB_MCP_EXTRA_HEADERS.
147
+ const { headers: extraHeaders } = parseExtraHeaders(env);
148
+ await run({
149
+ endpointUrl: resolved.endpointUrl,
150
+ token: resolved.token,
151
+ ...(Object.keys(extraHeaders).length > 0 ? { extraHeaders } : {}),
152
+ });
127
153
  return true;
128
154
  }
129
155
  //# sourceMappingURL=bridge.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"bridge.js","sourceRoot":"","sources":["../../src/proxy/bridge.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bridge.js","sourceRoot":"","sources":["../../src/proxy/bridge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * `setup-ca-env` — translate a wizard/CLI CA choice into MCP-server env keys.
3
+ *
4
+ * G-D #7: corporate TLS-inspection has two trust strategies, and exactly which
5
+ * env the server entry needs depends on the running Node:
6
+ *
7
+ * - **file mode** (`caCertPath`): write `NODE_EXTRA_CA_CERTS=<path>`. Works on
8
+ * every supported Node; the only option below the system-CA floor.
9
+ * - **system mode** (`caMode: 'system'`): write `NODE_OPTIONS=--use-system-ca`
10
+ * so Node reads the OS trust store (Windows CryptoAPI / macOS Keychain) —
11
+ * the zero-export path, but only on Node ≥ 22.15. Below that floor we emit
12
+ * NOTHING and warn to supply a `.pem` instead, so we never write a flag the
13
+ * target Node would reject at startup.
14
+ *
15
+ * Pure + Node-version-injectable so it is unit-testable offline. Never disables
16
+ * TLS verification.
17
+ *
18
+ * @license MIT
19
+ */
20
+ /** The CA-trust portion of a {@link WizardAnswers}. */
21
+ export interface CaChoice {
22
+ readonly caCertPath?: string;
23
+ readonly caMode?: 'system';
24
+ }
25
+ export interface CaEnvResult {
26
+ /** Env keys to merge into the MCP server entry's env block. */
27
+ readonly env: Record<string, string>;
28
+ /** Non-fatal warnings (e.g. system-CA requested on too-old a Node). */
29
+ readonly warnings: readonly string[];
30
+ }
31
+ /**
32
+ * Resolve the CA env-block keys for `choice` under `nodeVersion` (default
33
+ * `process.version`). `currentEnv` (default `process.env`) is only read to
34
+ * preserve a pre-existing `NODE_OPTIONS` when appending the system-CA flag.
35
+ *
36
+ * File mode wins deterministically if both are set (an explicit `.pem` is the
37
+ * stronger signal). Pure — returns a fresh object, mutates nothing.
38
+ */
39
+ export declare function buildCaEnv(choice: CaChoice, nodeVersion?: string, currentEnv?: NodeJS.ProcessEnv): CaEnvResult;
40
+ //# sourceMappingURL=setup-ca-env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup-ca-env.d.ts","sourceRoot":"","sources":["../src/setup-ca-env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,uDAAuD;AACvD,MAAM,WAAW,QAAQ;IACrB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC;CAC9B;AAED,MAAM,WAAW,WAAW;IACxB,+DAA+D;IAC/D,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,uEAAuE;IACvE,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC;AAYD;;;;;;;GAOG;AACH,wBAAgB,UAAU,CACtB,MAAM,EAAE,QAAQ,EAChB,WAAW,GAAE,MAAwB,EACrC,UAAU,GAAE,MAAM,CAAC,UAAwB,GAC5C,WAAW,CAoBb"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * `setup-ca-env` — translate a wizard/CLI CA choice into MCP-server env keys.
3
+ *
4
+ * G-D #7: corporate TLS-inspection has two trust strategies, and exactly which
5
+ * env the server entry needs depends on the running Node:
6
+ *
7
+ * - **file mode** (`caCertPath`): write `NODE_EXTRA_CA_CERTS=<path>`. Works on
8
+ * every supported Node; the only option below the system-CA floor.
9
+ * - **system mode** (`caMode: 'system'`): write `NODE_OPTIONS=--use-system-ca`
10
+ * so Node reads the OS trust store (Windows CryptoAPI / macOS Keychain) —
11
+ * the zero-export path, but only on Node ≥ 22.15. Below that floor we emit
12
+ * NOTHING and warn to supply a `.pem` instead, so we never write a flag the
13
+ * target Node would reject at startup.
14
+ *
15
+ * Pure + Node-version-injectable so it is unit-testable offline. Never disables
16
+ * TLS verification.
17
+ *
18
+ * @license MIT
19
+ */
20
+ import { supportsSystemCa } from './diagnostics/ca-trust.js';
21
+ const SYSTEM_CA_FLAG = '--use-system-ca';
22
+ /** Append `--use-system-ca` to an existing NODE_OPTIONS without duplicating it. */
23
+ function withSystemCaFlag(existing) {
24
+ const trimmed = (existing ?? '').trim();
25
+ if (trimmed === '')
26
+ return SYSTEM_CA_FLAG;
27
+ if (trimmed.split(/\s+/).includes(SYSTEM_CA_FLAG))
28
+ return trimmed;
29
+ return `${trimmed} ${SYSTEM_CA_FLAG}`;
30
+ }
31
+ /**
32
+ * Resolve the CA env-block keys for `choice` under `nodeVersion` (default
33
+ * `process.version`). `currentEnv` (default `process.env`) is only read to
34
+ * preserve a pre-existing `NODE_OPTIONS` when appending the system-CA flag.
35
+ *
36
+ * File mode wins deterministically if both are set (an explicit `.pem` is the
37
+ * stronger signal). Pure — returns a fresh object, mutates nothing.
38
+ */
39
+ export function buildCaEnv(choice, nodeVersion = process.version, currentEnv = process.env) {
40
+ const path = choice.caCertPath?.trim();
41
+ if (path !== undefined && path !== '') {
42
+ return { env: { NODE_EXTRA_CA_CERTS: path }, warnings: [] };
43
+ }
44
+ if (choice.caMode === 'system') {
45
+ if (supportsSystemCa(nodeVersion)) {
46
+ return { env: { NODE_OPTIONS: withSystemCaFlag(currentEnv.NODE_OPTIONS) }, warnings: [] };
47
+ }
48
+ return {
49
+ env: {},
50
+ warnings: [
51
+ `OS trust store (--use-system-ca) needs Node ≥ 22.15 — this Node is ${nodeVersion}. ` +
52
+ 'Re-run setup with `--ca <path-to-corp-root-ca.pem>` (export your corporate root CA to a .pem) instead. Never disable TLS verification.',
53
+ ],
54
+ };
55
+ }
56
+ return { env: {}, warnings: [] };
57
+ }
58
+ //# sourceMappingURL=setup-ca-env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup-ca-env.js","sourceRoot":"","sources":["../src/setup-ca-env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAe7D,MAAM,cAAc,GAAG,iBAAiB,CAAC;AAEzC,mFAAmF;AACnF,SAAS,gBAAgB,CAAC,QAA4B;IAClD,MAAM,OAAO,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACxC,IAAI,OAAO,KAAK,EAAE;QAAE,OAAO,cAAc,CAAC;IAC1C,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,cAAc,CAAC;QAAE,OAAO,OAAO,CAAC;IAClE,OAAO,GAAG,OAAO,IAAI,cAAc,EAAE,CAAC;AAC1C,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CACtB,MAAgB,EAChB,cAAsB,OAAO,CAAC,OAAO,EACrC,aAAgC,OAAO,CAAC,GAAG;IAE3C,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC;IACvC,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;QACpC,OAAO,EAAE,GAAG,EAAE,EAAE,mBAAmB,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IAChE,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC7B,IAAI,gBAAgB,CAAC,WAAW,CAAC,EAAE,CAAC;YAChC,OAAO,EAAE,GAAG,EAAE,EAAE,YAAY,EAAE,gBAAgB,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;QAC9F,CAAC;QACD,OAAO;YACH,GAAG,EAAE,EAAE;YACP,QAAQ,EAAE;gBACN,sEAAsE,WAAW,IAAI;oBACjF,wIAAwI;aAC/I;SACJ,CAAC;IACN,CAAC;IAED,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;AACrC,CAAC"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * `setup-ca-reprobe` — default `WizardDeps.applyAndReProbe` (G-D #6/#8).
3
+ *
4
+ * After the user picks a corporate-CA strategy, RE-PROBE the connection so the
5
+ * wizard can print ✅/❌ now instead of leaving a silent first-tool-call
6
+ * timeout for after the restart:
7
+ *
8
+ * - **file mode**: read the `.pem` and run the probes through an undici
9
+ * `Agent` whose connector trusts that CA — this validates in-process,
10
+ * immediately, without a restart.
11
+ * - **system mode**: the OS trust store is only consulted when Node is
12
+ * (re)started with `--use-system-ca`, which the written config now does —
13
+ * so we re-run the ordinary probes (best-effort) and report the truthful
14
+ * current state; the config is already correct for the next launch.
15
+ *
16
+ * Never disables TLS verification.
17
+ *
18
+ * @license MIT
19
+ */
20
+ import type { AuthProbeResult, CaPromptChoice, HealthProbeResult } from './setup-wizard-types.js';
21
+ /**
22
+ * Probe health + auth for `wpUrl`/`bearer` under the chosen CA. The probes are
23
+ * injected (default to the real handshake probes) so this stays testable; the
24
+ * file-mode CA wiring is the only real-I/O part and is exercised by an
25
+ * integration probe, never a unit mock.
26
+ */
27
+ export declare function defaultApplyAndReProbe(input: {
28
+ readonly wpUrl: string;
29
+ readonly bearer: string;
30
+ readonly choice: CaPromptChoice;
31
+ }, probes?: {
32
+ probeHealth?: (wpUrl: string, fetchImpl?: typeof fetch) => Promise<HealthProbeResult>;
33
+ probeAuth?: (wpUrl: string, bearer: string, fetchImpl?: typeof fetch) => Promise<AuthProbeResult>;
34
+ }): Promise<{
35
+ health: HealthProbeResult;
36
+ auth: AuthProbeResult;
37
+ }>;
38
+ //# sourceMappingURL=setup-ca-reprobe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup-ca-reprobe.d.ts","sourceRoot":"","sources":["../src/setup-ca-reprobe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAOH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAElG;;;;;GAKG;AACH,wBAAsB,sBAAsB,CACxC,KAAK,EAAE;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAA;CAAE,EAC3F,MAAM,GAAE;IACJ,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,OAAO,KAAK,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACtF,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,OAAO,KAAK,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;CAChG,GACP,OAAO,CAAC;IAAE,MAAM,EAAE,iBAAiB,CAAC;IAAC,IAAI,EAAE,eAAe,CAAA;CAAE,CAAC,CAkB/D"}