@springbrand/cli 0.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 (222) hide show
  1. package/README.md +167 -0
  2. package/dist/commands/auth.d.ts +4 -0
  3. package/dist/commands/auth.d.ts.map +1 -0
  4. package/dist/commands/auth.js +241 -0
  5. package/dist/commands/auth.js.map +1 -0
  6. package/dist/commands/check-version.d.ts +3 -0
  7. package/dist/commands/check-version.d.ts.map +1 -0
  8. package/dist/commands/check-version.js +112 -0
  9. package/dist/commands/check-version.js.map +1 -0
  10. package/dist/commands/config-claude.d.ts +4 -0
  11. package/dist/commands/config-claude.d.ts.map +1 -0
  12. package/dist/commands/config-claude.js +78 -0
  13. package/dist/commands/config-claude.js.map +1 -0
  14. package/dist/commands/connect.d.ts +41 -0
  15. package/dist/commands/connect.d.ts.map +1 -0
  16. package/dist/commands/connect.js +901 -0
  17. package/dist/commands/connect.js.map +1 -0
  18. package/dist/commands/doctor.d.ts +6 -0
  19. package/dist/commands/doctor.d.ts.map +1 -0
  20. package/dist/commands/doctor.js +124 -0
  21. package/dist/commands/doctor.js.map +1 -0
  22. package/dist/commands/upgrade.d.ts +9 -0
  23. package/dist/commands/upgrade.d.ts.map +1 -0
  24. package/dist/commands/upgrade.js +133 -0
  25. package/dist/commands/upgrade.js.map +1 -0
  26. package/dist/index.d.ts +4 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +15 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/lib/agent-detect.d.ts +33 -0
  31. package/dist/lib/agent-detect.d.ts.map +1 -0
  32. package/dist/lib/agent-detect.js +81 -0
  33. package/dist/lib/agent-detect.js.map +1 -0
  34. package/dist/lib/agent-launcher.d.ts +22 -0
  35. package/dist/lib/agent-launcher.d.ts.map +1 -0
  36. package/dist/lib/agent-launcher.js +67 -0
  37. package/dist/lib/agent-launcher.js.map +1 -0
  38. package/dist/lib/auth.d.ts +90 -0
  39. package/dist/lib/auth.d.ts.map +1 -0
  40. package/dist/lib/auth.js +327 -0
  41. package/dist/lib/auth.js.map +1 -0
  42. package/dist/lib/bundled-skill.d.ts +5 -0
  43. package/dist/lib/bundled-skill.d.ts.map +1 -0
  44. package/dist/lib/bundled-skill.js +13 -0
  45. package/dist/lib/bundled-skill.js.map +1 -0
  46. package/dist/lib/claude-config.d.ts +19 -0
  47. package/dist/lib/claude-config.d.ts.map +1 -0
  48. package/dist/lib/claude-config.js +202 -0
  49. package/dist/lib/claude-config.js.map +1 -0
  50. package/dist/lib/cli-install.d.ts +40 -0
  51. package/dist/lib/cli-install.d.ts.map +1 -0
  52. package/dist/lib/cli-install.js +42 -0
  53. package/dist/lib/cli-install.js.map +1 -0
  54. package/dist/lib/command-code-config.d.ts +5 -0
  55. package/dist/lib/command-code-config.d.ts.map +1 -0
  56. package/dist/lib/command-code-config.js +16 -0
  57. package/dist/lib/command-code-config.js.map +1 -0
  58. package/dist/lib/connect-all-auth.d.ts +21 -0
  59. package/dist/lib/connect-all-auth.d.ts.map +1 -0
  60. package/dist/lib/connect-all-auth.js +113 -0
  61. package/dist/lib/connect-all-auth.js.map +1 -0
  62. package/dist/lib/connect-all-summary.d.ts +10 -0
  63. package/dist/lib/connect-all-summary.d.ts.map +1 -0
  64. package/dist/lib/connect-all-summary.js +153 -0
  65. package/dist/lib/connect-all-summary.js.map +1 -0
  66. package/dist/lib/connect-all-uninstall.d.ts +7 -0
  67. package/dist/lib/connect-all-uninstall.d.ts.map +1 -0
  68. package/dist/lib/connect-all-uninstall.js +249 -0
  69. package/dist/lib/connect-all-uninstall.js.map +1 -0
  70. package/dist/lib/connect-all.d.ts +42 -0
  71. package/dist/lib/connect-all.d.ts.map +1 -0
  72. package/dist/lib/connect-all.js +438 -0
  73. package/dist/lib/connect-all.js.map +1 -0
  74. package/dist/lib/connect-auth.d.ts +7 -0
  75. package/dist/lib/connect-auth.d.ts.map +1 -0
  76. package/dist/lib/connect-auth.js +50 -0
  77. package/dist/lib/connect-auth.js.map +1 -0
  78. package/dist/lib/connect-clients.d.ts +108 -0
  79. package/dist/lib/connect-clients.d.ts.map +1 -0
  80. package/dist/lib/connect-clients.js +358 -0
  81. package/dist/lib/connect-clients.js.map +1 -0
  82. package/dist/lib/connect-fallback.d.ts +27 -0
  83. package/dist/lib/connect-fallback.d.ts.map +1 -0
  84. package/dist/lib/connect-fallback.js +166 -0
  85. package/dist/lib/connect-fallback.js.map +1 -0
  86. package/dist/lib/connect-picker.d.ts +19 -0
  87. package/dist/lib/connect-picker.d.ts.map +1 -0
  88. package/dist/lib/connect-picker.js +155 -0
  89. package/dist/lib/connect-picker.js.map +1 -0
  90. package/dist/lib/connect-preflight.d.ts +17 -0
  91. package/dist/lib/connect-preflight.d.ts.map +1 -0
  92. package/dist/lib/connect-preflight.js +81 -0
  93. package/dist/lib/connect-preflight.js.map +1 -0
  94. package/dist/lib/connect-runtime.d.ts +109 -0
  95. package/dist/lib/connect-runtime.d.ts.map +1 -0
  96. package/dist/lib/connect-runtime.js +360 -0
  97. package/dist/lib/connect-runtime.js.map +1 -0
  98. package/dist/lib/constants.d.ts +23 -0
  99. package/dist/lib/constants.d.ts.map +1 -0
  100. package/dist/lib/constants.js +87 -0
  101. package/dist/lib/constants.js.map +1 -0
  102. package/dist/lib/cursor-config.d.ts +16 -0
  103. package/dist/lib/cursor-config.d.ts.map +1 -0
  104. package/dist/lib/cursor-config.js +34 -0
  105. package/dist/lib/cursor-config.js.map +1 -0
  106. package/dist/lib/doctor-checks.d.ts +21 -0
  107. package/dist/lib/doctor-checks.d.ts.map +1 -0
  108. package/dist/lib/doctor-checks.js +312 -0
  109. package/dist/lib/doctor-checks.js.map +1 -0
  110. package/dist/lib/doctor-repairs.d.ts +11 -0
  111. package/dist/lib/doctor-repairs.d.ts.map +1 -0
  112. package/dist/lib/doctor-repairs.js +130 -0
  113. package/dist/lib/doctor-repairs.js.map +1 -0
  114. package/dist/lib/doctor-report.d.ts +195 -0
  115. package/dist/lib/doctor-report.d.ts.map +1 -0
  116. package/dist/lib/doctor-report.js +102 -0
  117. package/dist/lib/doctor-report.js.map +1 -0
  118. package/dist/lib/doctor-telemetry.d.ts +9 -0
  119. package/dist/lib/doctor-telemetry.d.ts.map +1 -0
  120. package/dist/lib/doctor-telemetry.js +63 -0
  121. package/dist/lib/doctor-telemetry.js.map +1 -0
  122. package/dist/lib/harness-detect.d.ts +33 -0
  123. package/dist/lib/harness-detect.d.ts.map +1 -0
  124. package/dist/lib/harness-detect.js +70 -0
  125. package/dist/lib/harness-detect.js.map +1 -0
  126. package/dist/lib/harness-spec.d.ts +285 -0
  127. package/dist/lib/harness-spec.d.ts.map +1 -0
  128. package/dist/lib/harness-spec.js +268 -0
  129. package/dist/lib/harness-spec.js.map +1 -0
  130. package/dist/lib/harness.d.ts +27 -0
  131. package/dist/lib/harness.d.ts.map +1 -0
  132. package/dist/lib/harness.js +125 -0
  133. package/dist/lib/harness.js.map +1 -0
  134. package/dist/lib/hermes-config.d.ts +22 -0
  135. package/dist/lib/hermes-config.d.ts.map +1 -0
  136. package/dist/lib/hermes-config.js +76 -0
  137. package/dist/lib/hermes-config.js.map +1 -0
  138. package/dist/lib/hermes-env.d.ts +19 -0
  139. package/dist/lib/hermes-env.d.ts.map +1 -0
  140. package/dist/lib/hermes-env.js +169 -0
  141. package/dist/lib/hermes-env.js.map +1 -0
  142. package/dist/lib/hermes-plugin.d.ts +40 -0
  143. package/dist/lib/hermes-plugin.d.ts.map +1 -0
  144. package/dist/lib/hermes-plugin.js +168 -0
  145. package/dist/lib/hermes-plugin.js.map +1 -0
  146. package/dist/lib/install-root.d.ts +18 -0
  147. package/dist/lib/install-root.d.ts.map +1 -0
  148. package/dist/lib/install-root.js +55 -0
  149. package/dist/lib/install-root.js.map +1 -0
  150. package/dist/lib/mcp-config.d.ts +36 -0
  151. package/dist/lib/mcp-config.d.ts.map +1 -0
  152. package/dist/lib/mcp-config.js +90 -0
  153. package/dist/lib/mcp-config.js.map +1 -0
  154. package/dist/lib/mcp-endpoint.d.ts +16 -0
  155. package/dist/lib/mcp-endpoint.d.ts.map +1 -0
  156. package/dist/lib/mcp-endpoint.js +41 -0
  157. package/dist/lib/mcp-endpoint.js.map +1 -0
  158. package/dist/lib/mcp-json-config.d.ts +43 -0
  159. package/dist/lib/mcp-json-config.d.ts.map +1 -0
  160. package/dist/lib/mcp-json-config.js +164 -0
  161. package/dist/lib/mcp-json-config.js.map +1 -0
  162. package/dist/lib/notice.d.ts +13 -0
  163. package/dist/lib/notice.d.ts.map +1 -0
  164. package/dist/lib/notice.js +73 -0
  165. package/dist/lib/notice.js.map +1 -0
  166. package/dist/lib/omp-config.d.ts +18 -0
  167. package/dist/lib/omp-config.d.ts.map +1 -0
  168. package/dist/lib/omp-config.js +73 -0
  169. package/dist/lib/omp-config.js.map +1 -0
  170. package/dist/lib/output.d.ts +22 -0
  171. package/dist/lib/output.d.ts.map +1 -0
  172. package/dist/lib/output.js +92 -0
  173. package/dist/lib/output.js.map +1 -0
  174. package/dist/lib/pi-config.d.ts +27 -0
  175. package/dist/lib/pi-config.d.ts.map +1 -0
  176. package/dist/lib/pi-config.js +113 -0
  177. package/dist/lib/pi-config.js.map +1 -0
  178. package/dist/lib/registration-detect.d.ts +28 -0
  179. package/dist/lib/registration-detect.d.ts.map +1 -0
  180. package/dist/lib/registration-detect.js +563 -0
  181. package/dist/lib/registration-detect.js.map +1 -0
  182. package/dist/lib/setup-telemetry.d.ts +273 -0
  183. package/dist/lib/setup-telemetry.d.ts.map +1 -0
  184. package/dist/lib/setup-telemetry.js +309 -0
  185. package/dist/lib/setup-telemetry.js.map +1 -0
  186. package/dist/lib/signals.d.ts +14 -0
  187. package/dist/lib/signals.d.ts.map +1 -0
  188. package/dist/lib/signals.js +29 -0
  189. package/dist/lib/signals.js.map +1 -0
  190. package/dist/lib/skill-install.d.ts +10 -0
  191. package/dist/lib/skill-install.d.ts.map +1 -0
  192. package/dist/lib/skill-install.js +94 -0
  193. package/dist/lib/skill-install.js.map +1 -0
  194. package/dist/lib/skill-paths.d.ts +7 -0
  195. package/dist/lib/skill-paths.d.ts.map +1 -0
  196. package/dist/lib/skill-paths.js +37 -0
  197. package/dist/lib/skill-paths.js.map +1 -0
  198. package/dist/lib/skill-vendor.d.ts +20 -0
  199. package/dist/lib/skill-vendor.d.ts.map +1 -0
  200. package/dist/lib/skill-vendor.js +215 -0
  201. package/dist/lib/skill-vendor.js.map +1 -0
  202. package/dist/lib/skill.d.ts +14 -0
  203. package/dist/lib/skill.d.ts.map +1 -0
  204. package/dist/lib/skill.js +87 -0
  205. package/dist/lib/skill.js.map +1 -0
  206. package/dist/lib/verify.d.ts +35 -0
  207. package/dist/lib/verify.d.ts.map +1 -0
  208. package/dist/lib/verify.js +169 -0
  209. package/dist/lib/verify.js.map +1 -0
  210. package/dist/program.d.ts +8 -0
  211. package/dist/program.d.ts.map +1 -0
  212. package/dist/program.js +54 -0
  213. package/dist/program.js.map +1 -0
  214. package/package.json +55 -0
  215. package/skill/springbrand-action-api/SKILL.md +49 -0
  216. package/skill/springbrand-action-api/references/action-aliases.md +156 -0
  217. package/skill/springbrand-action-api/references/action-discovery.md +95 -0
  218. package/skill/springbrand-connector/SKILL.md +49 -0
  219. package/skill/springbrand-gtm/SKILL.md +34 -0
  220. package/skill/springbrand-platform/SKILL.md +55 -0
  221. package/skill/springbrand-platform/references/plugin-discovery.md +94 -0
  222. package/skill/use-springbrand/SKILL.md +70 -0
@@ -0,0 +1,168 @@
1
+ /** 复刻自 tiny-fish CLI 编译产物:hermes-plugin.d.ts + hermes-plugin.impl.js */
2
+ import * as fs from 'node:fs';
3
+ import * as os from 'node:os';
4
+ import * as path from 'node:path';
5
+ import { pathToFileURL } from 'node:url';
6
+ import spawn from 'cross-spawn';
7
+ import { z } from 'zod';
8
+ import { capturedOutput, replay, SKILL_INSTALL_TIMEOUT_MS, STEP_MAX_BUFFER, } from './cli-install.js';
9
+ import { commandNotFound, ConnectStepError, spawnStepError, throwIfInterrupted, } from './connect-runtime.js';
10
+ import { HARNESS_PROBE_TIMEOUT_MS } from './constants.js';
11
+ import { HERMES_HEADER_TEMPLATE, HERMES_MCP_SERVER_KEY } from './hermes-config.js';
12
+ import { errLine, parseJson } from './output.js';
13
+ // Bump per CLI release.
14
+ export const HERMES_PLUGIN_SHA = '496cd63fefd982bbaa8a85ce78ef2270d700984f';
15
+ // Bump with the SHA; the source is `hermes/plugin.yaml` at that commit.
16
+ export const HERMES_PLUGIN_VERSION = '0.1.0';
17
+ // Manifest `name:`, not the repo subdir; `plugins uninstall` keys on it.
18
+ const PLUGIN_SOURCE = 'springbrand-lab/springbrand-web-agent-integrations/hermes';
19
+ const PLUGIN_REPO_URL = 'https://github.com/springbrand-lab/springbrand-web-agent-integrations.git';
20
+ export const HERMES_PLUGIN_MANUAL_INSTALL = `hermes plugins install ${PLUGIN_SOURCE} --ref ${HERMES_PLUGIN_SHA} --enable`;
21
+ // Anchored to --ref: another rejected flag would fail the clone path too.
22
+ const REF_UNSUPPORTED_PATTERN = /unrecognized arguments:[^\n]*--ref/;
23
+ function installSpawnEnv(home, apiKey) {
24
+ // Hermes' install-time requires_env check reads os.environ, not our .env seed.
25
+ return { ...process.env, HERMES_HOME: home, SPRINGBRAND_API_KEY: apiKey };
26
+ }
27
+ function runPluginInstall(source, env) {
28
+ // Captured even under --verbose: the fallback matches on stderr.
29
+ return spawn.sync('hermes', ['plugins', 'install', ...source, '--enable', '--force'], {
30
+ encoding: 'utf8',
31
+ maxBuffer: STEP_MAX_BUFFER,
32
+ timeout: SKILL_INSTALL_TIMEOUT_MS,
33
+ env,
34
+ });
35
+ }
36
+ function refUnsupported(result) {
37
+ return result.status !== 0 && REF_UNSUPPORTED_PATTERN.test(String(result.stderr ?? ''));
38
+ }
39
+ function runGit(args) {
40
+ const result = spawn.sync('git', args, {
41
+ encoding: 'utf8',
42
+ maxBuffer: STEP_MAX_BUFFER,
43
+ timeout: SKILL_INSTALL_TIMEOUT_MS,
44
+ });
45
+ if (!result.error && result.status === 0)
46
+ return;
47
+ if (commandNotFound(result.error)) {
48
+ throw new ConnectStepError('git is required to stage the pinned plugin; install git and run connect again', 'command_not_found');
49
+ }
50
+ // Built first: its interrupt check must run before any replay.
51
+ const error = spawnStepError('Could not stage the SpringBrand Hermes plugin', result);
52
+ replay(capturedOutput(result));
53
+ throw error;
54
+ }
55
+ // Pre-`--ref` Hermes: stage the pin ourselves, mirroring `--ref` internals.
56
+ function installFromClone(env) {
57
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'springbrand-hermes-plugin-'));
58
+ try {
59
+ runGit(['clone', '--depth', '1', PLUGIN_REPO_URL, tmp]);
60
+ runGit(['-C', tmp, 'fetch', '--depth', '1', 'origin', HERMES_PLUGIN_SHA]);
61
+ runGit(['-C', tmp, 'checkout', '--detach', HERMES_PLUGIN_SHA]);
62
+ return runPluginInstall([`${pathToFileURL(tmp).href}#hermes`], env);
63
+ }
64
+ finally {
65
+ fs.rmSync(tmp, { recursive: true, force: true });
66
+ }
67
+ }
68
+ export function installHermesPlugin(home, apiKey, { verbose }) {
69
+ errLine('Installing the SpringBrand web plugin in Hermes...');
70
+ const env = installSpawnEnv(home, apiKey);
71
+ // --ref always: a same-source reinstall without it keeps the previous pin.
72
+ const primary = runPluginInstall([PLUGIN_SOURCE, '--ref', HERMES_PLUGIN_SHA], env);
73
+ const result = refUnsupported(primary) ? installFromClone(env) : primary;
74
+ if (result.error || result.status !== 0) {
75
+ // A declined security scan exits 1 here, and reconnecting repeats it.
76
+ const error = spawnStepError(`Could not install the SpringBrand web plugin in Hermes; to install it yourself run: ${HERMES_PLUGIN_MANUAL_INSTALL}`, result);
77
+ replay(capturedOutput(result));
78
+ throw error;
79
+ }
80
+ if (verbose)
81
+ replay(capturedOutput(result));
82
+ }
83
+ function hermesConfig(args, home) {
84
+ return spawn.sync('hermes', ['config', ...args], {
85
+ encoding: 'utf8',
86
+ maxBuffer: STEP_MAX_BUFFER,
87
+ timeout: HARNESS_PROBE_TIMEOUT_MS,
88
+ env: { ...process.env, HERMES_HOME: home },
89
+ });
90
+ }
91
+ function configWrite(home, args) {
92
+ const result = hermesConfig(args, home);
93
+ if (!result.error && result.status === 0)
94
+ return;
95
+ // Built first: its interrupt check must run before any replay.
96
+ const error = spawnStepError(`Could not run \`hermes config ${args.join(' ')}\``, result);
97
+ replay(capturedOutput(result));
98
+ throw error;
99
+ }
100
+ /** Absent `enabled` reads as enabled, so never write this field-by-field. */
101
+ export function writeHermesMcpEntry(home, mcpUrl) {
102
+ const entry = {
103
+ url: mcpUrl,
104
+ headers: { Authorization: HERMES_HEADER_TEMPLATE },
105
+ enabled: true,
106
+ };
107
+ const result = hermesConfig(['set', `mcp_servers.${HERMES_MCP_SERVER_KEY}`, JSON.stringify(entry)], home);
108
+ // `config set` exits 0 on a bad key, so only an interrupt is worth raising here.
109
+ throwIfInterrupted(result);
110
+ }
111
+ /** Retracts our row; an absent `enabled` reads as enabled, so a partial one is live. */
112
+ export function removeHermesMcpEntry(home) {
113
+ configWrite(home, ['unset', `mcp_servers.${HERMES_MCP_SERVER_KEY}`]);
114
+ }
115
+ const HERMES_BACKEND_KEYS = ['web.search_backend', 'web.extract_backend'];
116
+ // Dispatch falls back to web.backend, and the plugin's own setup can set it.
117
+ const HERMES_OWNED_BACKEND_KEYS = [...HERMES_BACKEND_KEYS, 'web.backend'];
118
+ /** After install only: backends must never name an absent provider. */
119
+ export function setHermesWebBackends(home) {
120
+ for (const key of HERMES_BACKEND_KEYS)
121
+ configWrite(home, ['set', key, 'springbrand']);
122
+ }
123
+ /** Unset prints empty and exits 0, so a failed read must not read as unset. */
124
+ function configGet(home, key) {
125
+ const result = hermesConfig(['get', key], home);
126
+ throwIfInterrupted(result);
127
+ if (result.error || result.status !== 0) {
128
+ throw spawnStepError(`Could not read \`hermes config get ${key}\``, result);
129
+ }
130
+ return String(result.stdout ?? '').trim() || undefined;
131
+ }
132
+ /** Ours-only unsets: a user-chosen backend value survives the uninstall. */
133
+ export function clearHermesWebBackends(home) {
134
+ for (const key of HERMES_OWNED_BACKEND_KEYS) {
135
+ if (configGet(home, key) === 'springbrand')
136
+ configWrite(home, ['unset', key]);
137
+ }
138
+ }
139
+ const hermesPluginStatusSchema = z.looseObject({
140
+ ok: z.boolean(),
141
+ api_key_configured: z.boolean(),
142
+ api_key_env_var: z.string().nullish(),
143
+ plugin_version: z.string().nullish(),
144
+ provider_available: z.boolean().nullish(),
145
+ web_backend_configured: z.boolean(),
146
+ });
147
+ /** `status --json` always exits 0; a non-zero exit means the subcommand is absent. */
148
+ export function readHermesPluginStatus(home) {
149
+ const result = spawn.sync('hermes', ['springbrand', 'status', '--json'], {
150
+ encoding: 'utf8',
151
+ maxBuffer: STEP_MAX_BUFFER,
152
+ timeout: HARNESS_PROBE_TIMEOUT_MS,
153
+ env: { ...process.env, HERMES_HOME: home, FORCE_COLOR: '0', NO_COLOR: '1' },
154
+ });
155
+ // A timeout also lands in result.error, where "not installed" would be a lie.
156
+ if (result.error) {
157
+ return commandNotFound(result.error) ? { state: 'not_installed' } : { state: 'unreachable' };
158
+ }
159
+ if (result.status !== 0)
160
+ return { state: 'not_installed' };
161
+ const parsed = hermesPluginStatusSchema.safeParse(parseJson(String(result.stdout ?? '')));
162
+ return parsed.success ? { state: 'ok', status: parsed.data } : { state: 'unparseable' };
163
+ }
164
+ /** Fallback verdict input when the plugin CLI cannot answer. */
165
+ export function hermesWebBackendsOurs(home) {
166
+ return HERMES_OWNED_BACKEND_KEYS.some((key) => configGet(home, key) === 'springbrand');
167
+ }
168
+ //# sourceMappingURL=hermes-plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hermes-plugin.js","sourceRoot":"","sources":["../../src/lib/hermes-plugin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /** 复刻自 tiny-fish CLI 编译产物:install-root.d.ts + install-root.impl.js */
2
+ /**
3
+ * The prefix to upgrade in place, or null when the layout is not npm's.
4
+ *
5
+ * Guarded on purpose: npx, pnpm and bun all nest the package under a bare `node_modules`, and
6
+ * installing into their trees would report a success the PATH binary never sees.
7
+ */
8
+ export declare function resolveInstallRoot(packageRoot: string, platform: typeof process.platform, onDisk?: (candidate: string) => boolean): InstallRoot | null;
9
+ export declare function installRoot(): InstallRoot | null;
10
+ /** This package's own dir under a prefix's node_modules. */
11
+ export declare function installedPackageDir(nodeModules: string): string;
12
+ /** The version on disk under a prefix, which is the only account of what `@latest` resolved to. */
13
+ export declare function readInstalledVersion(nodeModules: string): string | null;
14
+ export type InstallRoot = {
15
+ prefix: string;
16
+ nodeModules: string;
17
+ };
18
+ //# sourceMappingURL=install-root.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-root.d.ts","sourceRoot":"","sources":["../../src/lib/install-root.ts"],"names":[],"mappings":"AAAA,sEAAsE;AActE;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,GAAG,WAAW,GAAG,IAAI,CAetJ;AACD,wBAAgB,WAAW,IAAI,WAAW,GAAG,IAAI,CAIhD;AACD,4DAA4D;AAC5D,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE/D;AACD,mGAAmG;AACnG,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQvE;AAGD,MAAM,MAAM,WAAW,GAAG;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACvB,CAAC"}
@@ -0,0 +1,55 @@
1
+ /** 复刻自 tiny-fish CLI 编译产物:install-root.d.ts + install-root.impl.js */
2
+ import * as fs from 'node:fs';
3
+ import * as path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { SPRINGBRAND_CLI_PACKAGE } from './constants.js';
6
+ function isDirectory(candidate) {
7
+ try {
8
+ return fs.statSync(candidate).isDirectory();
9
+ }
10
+ catch {
11
+ return false;
12
+ }
13
+ }
14
+ /**
15
+ * The prefix to upgrade in place, or null when the layout is not npm's.
16
+ *
17
+ * Guarded on purpose: npx, pnpm and bun all nest the package under a bare `node_modules`, and
18
+ * installing into their trees would report a success the PATH binary never sees.
19
+ */
20
+ export function resolveInstallRoot(packageRoot, platform, onDisk) {
21
+ const p = platform === 'win32' ? path.win32 : path.posix;
22
+ // npx's win32 cache uses the same bare layout as a global install.
23
+ if (packageRoot.split(p.sep).includes('_npx'))
24
+ return null;
25
+ const nodeModules = p.resolve(packageRoot, '..', '..');
26
+ if (p.basename(nodeModules) !== 'node_modules')
27
+ return null;
28
+ const container = p.dirname(nodeModules);
29
+ // Only win32 puts node_modules directly under the prefix.
30
+ if (platform !== 'win32' && p.basename(container) !== 'lib')
31
+ return null;
32
+ if (!onDisk?.(nodeModules))
33
+ return null;
34
+ return { prefix: platform === 'win32' ? container : p.dirname(container), nodeModules };
35
+ }
36
+ export function installRoot() {
37
+ // Two levels up from dist/lib/ is the package root, and tsc mirrors src/ into dist/.
38
+ const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
39
+ return resolveInstallRoot(packageRoot, process.platform);
40
+ }
41
+ /** This package's own dir under a prefix's node_modules. */
42
+ export function installedPackageDir(nodeModules) {
43
+ return path.join(nodeModules, ...SPRINGBRAND_CLI_PACKAGE.split('/'));
44
+ }
45
+ /** The version on disk under a prefix, which is the only account of what `@latest` resolved to. */
46
+ export function readInstalledVersion(nodeModules) {
47
+ try {
48
+ const manifest = fs.readFileSync(path.join(installedPackageDir(nodeModules), 'package.json'), 'utf8');
49
+ return JSON.parse(manifest).version ?? null;
50
+ }
51
+ catch {
52
+ return null;
53
+ }
54
+ }
55
+ //# sourceMappingURL=install-root.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-root.js","sourceRoot":"","sources":["../../src/lib/install-root.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAEtE,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AACzD,SAAS,WAAW,CAAC,SAAiB;IAClC,IAAI,CAAC;QACD,OAAO,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,CAAC;IAChD,CAAC;IACD,MAAM,CAAC;QACH,OAAO,KAAK,CAAC;IACjB,CAAC;AACL,CAAC;AACD;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,WAAmB,EAAE,QAAiC,EAAE,MAAuC;IAC9H,MAAM,CAAC,GAAG,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;IACzD,mEAAmE;IACnE,IAAI,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;QACzC,OAAO,IAAI,CAAC;IAChB,MAAM,WAAW,GAAG,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IACvD,IAAI,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,KAAK,cAAc;QAC1C,OAAO,IAAI,CAAC;IAChB,MAAM,SAAS,GAAG,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IACzC,0DAA0D;IAC1D,IAAI,QAAQ,KAAK,OAAO,IAAI,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,KAAK,KAAK;QACvD,OAAO,IAAI,CAAC;IAChB,IAAI,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC;QACtB,OAAO,IAAI,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,WAAW,EAAE,CAAC;AAC5F,CAAC;AACD,MAAM,UAAU,WAAW;IACvB,qFAAqF;IACrF,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC3F,OAAO,kBAAkB,CAAC,WAAW,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;AAC7D,CAAC;AACD,4DAA4D;AAC5D,MAAM,UAAU,mBAAmB,CAAC,WAAmB;IACnD,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,uBAAuB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;AACzE,CAAC;AACD,mGAAmG;AACnG,MAAM,UAAU,oBAAoB,CAAC,WAAmB;IACpD,IAAI,CAAC;QACD,MAAM,QAAQ,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,WAAW,CAAC,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC;QACtG,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,OAAO,IAAI,IAAI,CAAC;IAChD,CAAC;IACD,MAAM,CAAC;QACH,OAAO,IAAI,CAAC;IAChB,CAAC;AACL,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * MCP server configuration for agent config files
3
+ */
4
+ export interface McpServerConfig {
5
+ command: string;
6
+ args: string[];
7
+ env?: Record<string, string>;
8
+ }
9
+ export interface McpConfig {
10
+ mcpServers?: Record<string, McpServerConfig>;
11
+ }
12
+ /**
13
+ * Read existing MCP config from agent
14
+ */
15
+ export declare function readMcpConfig(configPath: string): McpConfig;
16
+ /**
17
+ * Write MCP config to agent (stage-then-swap for atomicity)
18
+ */
19
+ export declare function writeMcpConfig(configPath: string, config: McpConfig): void;
20
+ /**
21
+ * Add or update MCP Gateway server entry
22
+ */
23
+ export declare function upsertMcpGatewayServer(configPath: string, serverName: string, config: McpServerConfig): void;
24
+ /**
25
+ * Check if MCP Gateway is already configured
26
+ */
27
+ export declare function isMcpGatewayConfigured(configPath: string, serverName: string): boolean;
28
+ /**
29
+ * Generate MCP Gateway server config
30
+ */
31
+ export declare function generateMcpGatewayConfig(cliPath: string, accessToken: string, mcpUrl: string): McpServerConfig;
32
+ /**
33
+ * Remove MCP Gateway server entry
34
+ */
35
+ export declare function removeMcpGatewayServer(configPath: string, serverName: string): void;
36
+ //# sourceMappingURL=mcp-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-config.d.ts","sourceRoot":"","sources":["../../src/lib/mcp-config.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,SAAS;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;CAC9C;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAW3D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,IAAI,CAuB1E;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,eAAe,GACtB,IAAI,CASN;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAOtF;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,GACb,eAAe,CASjB;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAUnF"}
@@ -0,0 +1,90 @@
1
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';
2
+ import { dirname } from 'path';
3
+ /**
4
+ * Read existing MCP config from agent
5
+ */
6
+ export function readMcpConfig(configPath) {
7
+ if (!existsSync(configPath)) {
8
+ return { mcpServers: {} };
9
+ }
10
+ try {
11
+ const content = readFileSync(configPath, 'utf-8');
12
+ return JSON.parse(content);
13
+ }
14
+ catch (error) {
15
+ throw new Error(`Failed to read MCP config from ${configPath}: ${error}`);
16
+ }
17
+ }
18
+ /**
19
+ * Write MCP config to agent (stage-then-swap for atomicity)
20
+ */
21
+ export function writeMcpConfig(configPath, config) {
22
+ try {
23
+ // Ensure directory exists
24
+ const dir = dirname(configPath);
25
+ if (!existsSync(dir)) {
26
+ mkdirSync(dir, { recursive: true });
27
+ }
28
+ // Stage to temp file first
29
+ const tmpPath = `${configPath}.tmp`;
30
+ const content = JSON.stringify(config, null, 2);
31
+ writeFileSync(tmpPath, content, { encoding: 'utf-8', mode: 0o600 });
32
+ // Atomic swap
33
+ writeFileSync(configPath, content, { encoding: 'utf-8', mode: 0o600 });
34
+ // Clean up temp
35
+ if (existsSync(tmpPath)) {
36
+ require('fs').unlinkSync(tmpPath);
37
+ }
38
+ }
39
+ catch (error) {
40
+ throw new Error(`Failed to write MCP config to ${configPath}: ${error}`);
41
+ }
42
+ }
43
+ /**
44
+ * Add or update MCP Gateway server entry
45
+ */
46
+ export function upsertMcpGatewayServer(configPath, serverName, config) {
47
+ const mcpConfig = readMcpConfig(configPath);
48
+ if (!mcpConfig.mcpServers) {
49
+ mcpConfig.mcpServers = {};
50
+ }
51
+ mcpConfig.mcpServers[serverName] = config;
52
+ writeMcpConfig(configPath, mcpConfig);
53
+ }
54
+ /**
55
+ * Check if MCP Gateway is already configured
56
+ */
57
+ export function isMcpGatewayConfigured(configPath, serverName) {
58
+ if (!existsSync(configPath)) {
59
+ return false;
60
+ }
61
+ const config = readMcpConfig(configPath);
62
+ return !!config.mcpServers?.[serverName];
63
+ }
64
+ /**
65
+ * Generate MCP Gateway server config
66
+ */
67
+ export function generateMcpGatewayConfig(cliPath, accessToken, mcpUrl) {
68
+ return {
69
+ command: 'node',
70
+ args: [cliPath, 'mcp-server'],
71
+ env: {
72
+ MCP_GATEWAY_TOKEN: accessToken,
73
+ MCP_GATEWAY_URL: mcpUrl,
74
+ },
75
+ };
76
+ }
77
+ /**
78
+ * Remove MCP Gateway server entry
79
+ */
80
+ export function removeMcpGatewayServer(configPath, serverName) {
81
+ if (!existsSync(configPath)) {
82
+ return;
83
+ }
84
+ const config = readMcpConfig(configPath);
85
+ if (config.mcpServers?.[serverName]) {
86
+ delete config.mcpServers[serverName];
87
+ writeMcpConfig(configPath, config);
88
+ }
89
+ }
90
+ //# sourceMappingURL=mcp-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-config.js","sourceRoot":"","sources":["../../src/lib/mcp-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AACxE,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAgB/B;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,UAAkB;IAC9C,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,OAAO,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;IAC5B,CAAC;IAED,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAClD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,kCAAkC,UAAU,KAAK,KAAK,EAAE,CAAC,CAAC;IAC5E,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,UAAkB,EAAE,MAAiB;IAClE,IAAI,CAAC;QACH,0BAA0B;QAC1B,MAAM,GAAG,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;QAChC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACrB,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtC,CAAC;QAED,2BAA2B;QAC3B,MAAM,OAAO,GAAG,GAAG,UAAU,MAAM,CAAC;QACpC,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAChD,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAEpE,cAAc;QACd,aAAa,CAAC,UAAU,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAEvE,gBAAgB;QAChB,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YACxB,OAAO,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,iCAAiC,UAAU,KAAK,KAAK,EAAE,CAAC,CAAC;IAC3E,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,sBAAsB,CACpC,UAAkB,EAClB,UAAkB,EAClB,MAAuB;IAEvB,MAAM,SAAS,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC;IAE5C,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC;QAC1B,SAAS,CAAC,UAAU,GAAG,EAAE,CAAC;IAC5B,CAAC;IAED,SAAS,CAAC,UAAU,CAAC,UAAU,CAAC,GAAG,MAAM,CAAC;IAC1C,cAAc,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;AACxC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,sBAAsB,CAAC,UAAkB,EAAE,UAAkB;IAC3E,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,MAAM,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC;IACzC,OAAO,CAAC,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,UAAU,CAAC,CAAC;AAC3C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,wBAAwB,CACtC,OAAe,EACf,WAAmB,EACnB,MAAc;IAEd,OAAO;QACL,OAAO,EAAE,MAAM;QACf,IAAI,EAAE,CAAC,OAAO,EAAE,YAAY,CAAC;QAC7B,GAAG,EAAE;YACH,iBAAiB,EAAE,WAAW;YAC9B,eAAe,EAAE,MAAM;SACxB;KACF,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,sBAAsB,CAAC,UAAkB,EAAE,UAAkB;IAC3E,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,OAAO;IACT,CAAC;IAED,MAAM,MAAM,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC;IACzC,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC;QACpC,OAAO,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;QACrC,cAAc,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IACrC,CAAC;AACH,CAAC"}
@@ -0,0 +1,16 @@
1
+ /** 复刻自 tiny-fish CLI 编译产物:mcp-endpoint.d.ts + mcp-endpoint.impl.js */
2
+ import type { KeyEnvironment } from './auth.js';
3
+ export declare const DEFAULT_MCP_URL = "https://connector.springbrand.ai/mcp";
4
+ /**
5
+ * Which environment's keys an endpoint accepts. Mirrors the Gateway's `mcpWireEnvironment`:
6
+ * the production host serves production keys and every other host — dev, local — serves dev.
7
+ * A key from the other environment is refused with 401, so this comparison is worth making
8
+ * before anything is written.
9
+ */
10
+ export declare function environmentOf(mcpUrl: string): KeyEnvironment | undefined;
11
+ export declare function endpointOf(url: string): string;
12
+ /** One host serves `/mcp` and `/v1`, so origin is the API base. */
13
+ export declare function apiBaseFromMcpUrl(mcpUrl: string): string | undefined;
14
+ /** doctor reuses the CLI's own BASE_URL check; off-default endpoints cannot. */
15
+ export declare function isDefaultEndpoint(mcpUrl: string): boolean;
16
+ //# sourceMappingURL=mcp-endpoint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-endpoint.d.ts","sourceRoot":"","sources":["../../src/lib/mcp-endpoint.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAEtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,eAAO,MAAM,eAAe,yCAAyC,CAAC;AAEtE;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAOxE;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAG9C;AACD,mEAAmE;AACnE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAOpE;AACD,gFAAgF;AAChF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAOzD"}
@@ -0,0 +1,41 @@
1
+ /** 复刻自 tiny-fish CLI 编译产物:mcp-endpoint.d.ts + mcp-endpoint.impl.js */
2
+ export const DEFAULT_MCP_URL = 'https://connector.springbrand.ai/mcp';
3
+ const PRODUCTION_HOST = 'connector.springbrand.ai';
4
+ /**
5
+ * Which environment's keys an endpoint accepts. Mirrors the Gateway's `mcpWireEnvironment`:
6
+ * the production host serves production keys and every other host — dev, local — serves dev.
7
+ * A key from the other environment is refused with 401, so this comparison is worth making
8
+ * before anything is written.
9
+ */
10
+ export function environmentOf(mcpUrl) {
11
+ try {
12
+ return new URL(mcpUrl).hostname.toLowerCase() === PRODUCTION_HOST ? 'production' : 'dev';
13
+ }
14
+ catch {
15
+ return undefined;
16
+ }
17
+ }
18
+ // Query strings carry the connect attempt id, so they differ on every healthy install.
19
+ export function endpointOf(url) {
20
+ const parsed = new URL(url);
21
+ return `${parsed.origin}${parsed.pathname.replace(/\/$/, '')}`;
22
+ }
23
+ /** One host serves `/mcp` and `/v1`, so origin is the API base. */
24
+ export function apiBaseFromMcpUrl(mcpUrl) {
25
+ try {
26
+ return new URL(mcpUrl).origin;
27
+ }
28
+ catch {
29
+ return undefined;
30
+ }
31
+ }
32
+ /** doctor reuses the CLI's own BASE_URL check; off-default endpoints cannot. */
33
+ export function isDefaultEndpoint(mcpUrl) {
34
+ try {
35
+ return endpointOf(mcpUrl) === endpointOf(DEFAULT_MCP_URL);
36
+ }
37
+ catch {
38
+ return false;
39
+ }
40
+ }
41
+ //# sourceMappingURL=mcp-endpoint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-endpoint.js","sourceRoot":"","sources":["../../src/lib/mcp-endpoint.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAGtE,MAAM,CAAC,MAAM,eAAe,GAAG,sCAAsC,CAAC;AACtE,MAAM,eAAe,GAAG,0BAA0B,CAAC;AACnD;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,MAAc;IACxC,IAAI,CAAC;QACD,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,eAAe,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC;IAC7F,CAAC;IACD,MAAM,CAAC;QACH,OAAO,SAAS,CAAC;IACrB,CAAC;AACL,CAAC;AACD,uFAAuF;AACvF,MAAM,UAAU,UAAU,CAAC,GAAW;IAClC,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IAC5B,OAAO,GAAG,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC;AACnE,CAAC;AACD,mEAAmE;AACnE,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC5C,IAAI,CAAC;QACD,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC;IAClC,CAAC;IACD,MAAM,CAAC;QACH,OAAO,SAAS,CAAC;IACrB,CAAC;AACL,CAAC;AACD,gFAAgF;AAChF,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC5C,IAAI,CAAC;QACD,OAAO,UAAU,CAAC,MAAM,CAAC,KAAK,UAAU,CAAC,eAAe,CAAC,CAAC;IAC9D,CAAC;IACD,MAAM,CAAC;QACH,OAAO,KAAK,CAAC;IACjB,CAAC;AACL,CAAC"}
@@ -0,0 +1,43 @@
1
+ /** 复刻自 tiny-fish CLI 编译产物:mcp-json-config.d.ts + mcp-json-config.impl.js */
2
+ export declare function buildSpringbrandServerEntry(mcpUrl: string, apiKey?: string): Record<string, unknown>;
3
+ /** Dry-run description of the pending write; touches nothing. */
4
+ export declare function planWrite(target: McpJsonTarget, mcpUrl: string, apiKey?: string): string;
5
+ /** Reports the header's shape, never its value. */
6
+ export declare function readSpringbrandEntry(target: McpJsonTarget): McpJsonServerEntry;
7
+ /** Merges only the target's key; skips unreadable/corrupt files rather than clobber. */
8
+ export declare function writeMcpConfig(target: McpJsonTarget, mcpUrl: string, apiKey?: string): McpJsonWriteResult;
9
+ /** Removes only the target's key. */
10
+ export declare function removeServer(target: McpJsonTarget): McpJsonWriteResult;
11
+ export interface McpJsonTarget {
12
+ serverKey: string;
13
+ /** Functions so paths resolve at call time. */
14
+ dir(): string;
15
+ file(): string;
16
+ /** Read-side only; defaults to what the write path emits (Command Code writes its own). */
17
+ keyHeader?: {
18
+ name: string;
19
+ valuePrefix?: string;
20
+ };
21
+ }
22
+ export interface McpJsonWriteResult {
23
+ status: 'written' | 'unchanged' | 'corrupt_skip';
24
+ backupPath?: string;
25
+ error?: string;
26
+ /** A pre-existing entry was replaced — a repair, not a fresh install. */
27
+ repaired?: boolean;
28
+ }
29
+ export interface McpJsonServerEntry {
30
+ present: boolean;
31
+ hasApiKeyHeader: boolean;
32
+ /** Only ever false: harnesses with a disable toggle write it, the rest omit it. */
33
+ enabled?: false;
34
+ /** The key rides the pre-Bearer `X-API-Key` header, which the Gateway does not read. */
35
+ legacyKeyHeader?: true;
36
+ /** Registered endpoint, so a caller can tell "registered" from "registered at the right place". */
37
+ url?: string;
38
+ keyMatchesCliKey?: boolean;
39
+ /** Whole-value `${VAR}` template: the variable name, never a key. */
40
+ keyTemplateVar?: string;
41
+ error?: string;
42
+ }
43
+ //# sourceMappingURL=mcp-json-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-json-config.d.ts","sourceRoot":"","sources":["../../src/lib/mcp-json-config.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAqB5E,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEpG;AA+BD,iEAAiE;AACjE,wBAAgB,SAAS,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAiBxF;AAGD,mDAAmD;AACnD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,aAAa,GAAG,kBAAkB,CAmB9E;AAiDD,wFAAwF;AACxF,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,kBAAkB,CAczG;AACD,qCAAqC;AACrC,wBAAgB,YAAY,CAAC,MAAM,EAAE,aAAa,GAAG,kBAAkB,CAatE;AAGD,MAAM,WAAW,aAAa;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,+CAA+C;IAC/C,GAAG,IAAI,MAAM,CAAC;IACd,IAAI,IAAI,MAAM,CAAC;IACf,2FAA2F;IAC3F,SAAS,CAAC,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;CACL;AAED,MAAM,WAAW,kBAAkB;IAC/B,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,cAAc,CAAC;IACjD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,OAAO,CAAC;IACzB,mFAAmF;IACnF,OAAO,CAAC,EAAE,KAAK,CAAC;IAChB,wFAAwF;IACxF,eAAe,CAAC,EAAE,IAAI,CAAC;IACvB,mGAAmG;IACnG,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,qEAAqE;IACrE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB"}