luckerr 0.41.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 (156) hide show
  1. package/README.md +267 -0
  2. package/README.zh-CN.md +237 -0
  3. package/dashboard/app.css +3022 -0
  4. package/dashboard/dist/app.js +30137 -0
  5. package/dashboard/dist/app.js.map +1 -0
  6. package/dashboard/dist/vendor-hljs.css +10 -0
  7. package/dashboard/dist/vendor-uplot.css +1 -0
  8. package/dashboard/index.html +19 -0
  9. package/data/deepseek-tokenizer.json.gz +0 -0
  10. package/dist/cli/acp-EOOAI4F5.js +712 -0
  11. package/dist/cli/acp-EOOAI4F5.js.map +1 -0
  12. package/dist/cli/chat-7J6GJXL2.js +51 -0
  13. package/dist/cli/chat-7J6GJXL2.js.map +1 -0
  14. package/dist/cli/chunk-2425HK6U.js +54 -0
  15. package/dist/cli/chunk-2425HK6U.js.map +1 -0
  16. package/dist/cli/chunk-25T6CVUP.js +172 -0
  17. package/dist/cli/chunk-25T6CVUP.js.map +1 -0
  18. package/dist/cli/chunk-2UQP6H6T.js +31 -0
  19. package/dist/cli/chunk-2UQP6H6T.js.map +1 -0
  20. package/dist/cli/chunk-56OAJILV.js +47 -0
  21. package/dist/cli/chunk-56OAJILV.js.map +1 -0
  22. package/dist/cli/chunk-5FTI4KXH.js +150 -0
  23. package/dist/cli/chunk-5FTI4KXH.js.map +1 -0
  24. package/dist/cli/chunk-5TWQD73O.js +2846 -0
  25. package/dist/cli/chunk-5TWQD73O.js.map +1 -0
  26. package/dist/cli/chunk-653BOCMK.js +40 -0
  27. package/dist/cli/chunk-653BOCMK.js.map +1 -0
  28. package/dist/cli/chunk-6ALJTWWQ.js +2663 -0
  29. package/dist/cli/chunk-6ALJTWWQ.js.map +1 -0
  30. package/dist/cli/chunk-6DRKA2IL.js +341 -0
  31. package/dist/cli/chunk-6DRKA2IL.js.map +1 -0
  32. package/dist/cli/chunk-6LV63NJV.js +634 -0
  33. package/dist/cli/chunk-6LV63NJV.js.map +1 -0
  34. package/dist/cli/chunk-74EX7SUH.js +25293 -0
  35. package/dist/cli/chunk-74EX7SUH.js.map +1 -0
  36. package/dist/cli/chunk-74U5RKTX.js +60611 -0
  37. package/dist/cli/chunk-74U5RKTX.js.map +1 -0
  38. package/dist/cli/chunk-ANJSUESV.js +143 -0
  39. package/dist/cli/chunk-ANJSUESV.js.map +1 -0
  40. package/dist/cli/chunk-DB2Z3DKZ.js +54 -0
  41. package/dist/cli/chunk-DB2Z3DKZ.js.map +1 -0
  42. package/dist/cli/chunk-DDIH3ZAA.js +400 -0
  43. package/dist/cli/chunk-DDIH3ZAA.js.map +1 -0
  44. package/dist/cli/chunk-ELN3Z3B2.js +621 -0
  45. package/dist/cli/chunk-ELN3Z3B2.js.map +1 -0
  46. package/dist/cli/chunk-F6BSQJGV.js +200 -0
  47. package/dist/cli/chunk-F6BSQJGV.js.map +1 -0
  48. package/dist/cli/chunk-FET2UAG5.js +246 -0
  49. package/dist/cli/chunk-FET2UAG5.js.map +1 -0
  50. package/dist/cli/chunk-FFJ342IJ.js +190 -0
  51. package/dist/cli/chunk-FFJ342IJ.js.map +1 -0
  52. package/dist/cli/chunk-GB3247B6.js +130 -0
  53. package/dist/cli/chunk-GB3247B6.js.map +1 -0
  54. package/dist/cli/chunk-HC2J4U3G.js +373 -0
  55. package/dist/cli/chunk-HC2J4U3G.js.map +1 -0
  56. package/dist/cli/chunk-HRUZAIHQ.js +42 -0
  57. package/dist/cli/chunk-HRUZAIHQ.js.map +1 -0
  58. package/dist/cli/chunk-J3ZJFUDL.js +308 -0
  59. package/dist/cli/chunk-J3ZJFUDL.js.map +1 -0
  60. package/dist/cli/chunk-J5XJHLWM.js +55 -0
  61. package/dist/cli/chunk-J5XJHLWM.js.map +1 -0
  62. package/dist/cli/chunk-JFGLMRZ6.js +160 -0
  63. package/dist/cli/chunk-JFGLMRZ6.js.map +1 -0
  64. package/dist/cli/chunk-JMBMLOBP.js +26 -0
  65. package/dist/cli/chunk-JMBMLOBP.js.map +1 -0
  66. package/dist/cli/chunk-JMWHXZEL.js +551 -0
  67. package/dist/cli/chunk-JMWHXZEL.js.map +1 -0
  68. package/dist/cli/chunk-KEQGPJBO.js +209 -0
  69. package/dist/cli/chunk-KEQGPJBO.js.map +1 -0
  70. package/dist/cli/chunk-M4K6U37F.js +232 -0
  71. package/dist/cli/chunk-M4K6U37F.js.map +1 -0
  72. package/dist/cli/chunk-MIJI2WMN.js +95 -0
  73. package/dist/cli/chunk-MIJI2WMN.js.map +1 -0
  74. package/dist/cli/chunk-MPAO3JNR.js +128 -0
  75. package/dist/cli/chunk-MPAO3JNR.js.map +1 -0
  76. package/dist/cli/chunk-PZOFBEDC.js +873 -0
  77. package/dist/cli/chunk-PZOFBEDC.js.map +1 -0
  78. package/dist/cli/chunk-RAILYQLN.js +46 -0
  79. package/dist/cli/chunk-RAILYQLN.js.map +1 -0
  80. package/dist/cli/chunk-RR35VQVT.js +90 -0
  81. package/dist/cli/chunk-RR35VQVT.js.map +1 -0
  82. package/dist/cli/chunk-RRA7VPW4.js +417 -0
  83. package/dist/cli/chunk-RRA7VPW4.js.map +1 -0
  84. package/dist/cli/chunk-RU36QVN3.js +452 -0
  85. package/dist/cli/chunk-RU36QVN3.js.map +1 -0
  86. package/dist/cli/chunk-RUBIINXR.js +1819 -0
  87. package/dist/cli/chunk-RUBIINXR.js.map +1 -0
  88. package/dist/cli/chunk-S4XVGLRW.js +499 -0
  89. package/dist/cli/chunk-S4XVGLRW.js.map +1 -0
  90. package/dist/cli/chunk-TUK7OWJA.js +51 -0
  91. package/dist/cli/chunk-TUK7OWJA.js.map +1 -0
  92. package/dist/cli/chunk-VALDDV76.js +580 -0
  93. package/dist/cli/chunk-VALDDV76.js.map +1 -0
  94. package/dist/cli/chunk-WQOGPYGN.js +11390 -0
  95. package/dist/cli/chunk-WQOGPYGN.js.map +1 -0
  96. package/dist/cli/chunk-WREKDFXT.js +34320 -0
  97. package/dist/cli/chunk-WREKDFXT.js.map +1 -0
  98. package/dist/cli/chunk-Y7XQU2EL.js +270 -0
  99. package/dist/cli/chunk-Y7XQU2EL.js.map +1 -0
  100. package/dist/cli/chunk-YBVCZJU4.js +54 -0
  101. package/dist/cli/chunk-YBVCZJU4.js.map +1 -0
  102. package/dist/cli/chunk-YLIHDXUQ.js +749 -0
  103. package/dist/cli/chunk-YLIHDXUQ.js.map +1 -0
  104. package/dist/cli/chunk-YV5XXFD7.js +767 -0
  105. package/dist/cli/chunk-YV5XXFD7.js.map +1 -0
  106. package/dist/cli/chunk-ZRCNIYRQ.js +101 -0
  107. package/dist/cli/chunk-ZRCNIYRQ.js.map +1 -0
  108. package/dist/cli/code-CRKVCMFZ.js +155 -0
  109. package/dist/cli/code-CRKVCMFZ.js.map +1 -0
  110. package/dist/cli/commands-QLMD3T7B.js +356 -0
  111. package/dist/cli/commands-QLMD3T7B.js.map +1 -0
  112. package/dist/cli/commit-53PP32NC.js +293 -0
  113. package/dist/cli/commit-53PP32NC.js.map +1 -0
  114. package/dist/cli/desktop-R6W5CLJ5.js +1046 -0
  115. package/dist/cli/desktop-R6W5CLJ5.js.map +1 -0
  116. package/dist/cli/devtools-YECO25QO.js +3719 -0
  117. package/dist/cli/devtools-YECO25QO.js.map +1 -0
  118. package/dist/cli/diff-LYNRCJZE.js +166 -0
  119. package/dist/cli/diff-LYNRCJZE.js.map +1 -0
  120. package/dist/cli/doctor-5IBP4R5J.js +28 -0
  121. package/dist/cli/doctor-5IBP4R5J.js.map +1 -0
  122. package/dist/cli/events-QN6KLN2V.js +340 -0
  123. package/dist/cli/events-QN6KLN2V.js.map +1 -0
  124. package/dist/cli/index.js +3500 -0
  125. package/dist/cli/index.js.map +1 -0
  126. package/dist/cli/mcp-FGKEH7RG.js +277 -0
  127. package/dist/cli/mcp-FGKEH7RG.js.map +1 -0
  128. package/dist/cli/mcp-browse-YCND4NWT.js +178 -0
  129. package/dist/cli/mcp-browse-YCND4NWT.js.map +1 -0
  130. package/dist/cli/mcp-inspect-V34J3VX5.js +143 -0
  131. package/dist/cli/mcp-inspect-V34J3VX5.js.map +1 -0
  132. package/dist/cli/package.json +3 -0
  133. package/dist/cli/prompt-I775PNKT.js +16 -0
  134. package/dist/cli/prompt-I775PNKT.js.map +1 -0
  135. package/dist/cli/prune-sessions-KGIIYD3P.js +44 -0
  136. package/dist/cli/prune-sessions-KGIIYD3P.js.map +1 -0
  137. package/dist/cli/replay-RDXLUAOE.js +292 -0
  138. package/dist/cli/replay-RDXLUAOE.js.map +1 -0
  139. package/dist/cli/run-RCAC2RYW.js +223 -0
  140. package/dist/cli/run-RCAC2RYW.js.map +1 -0
  141. package/dist/cli/server-FFU6TLYJ.js +3658 -0
  142. package/dist/cli/server-FFU6TLYJ.js.map +1 -0
  143. package/dist/cli/sessions-QT26MQAE.js +107 -0
  144. package/dist/cli/sessions-QT26MQAE.js.map +1 -0
  145. package/dist/cli/setup-VV4WKXHV.js +767 -0
  146. package/dist/cli/setup-VV4WKXHV.js.map +1 -0
  147. package/dist/cli/stats-JVZPQWAN.js +15 -0
  148. package/dist/cli/stats-JVZPQWAN.js.map +1 -0
  149. package/dist/cli/update-KYI3OVJP.js +15 -0
  150. package/dist/cli/update-KYI3OVJP.js.map +1 -0
  151. package/dist/cli/version-ANYORXTI.js +34 -0
  152. package/dist/cli/version-ANYORXTI.js.map +1 -0
  153. package/dist/index.d.ts +2557 -0
  154. package/dist/index.js +15000 -0
  155. package/dist/index.js.map +1 -0
  156. package/package.json +106 -0
@@ -0,0 +1,277 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire as __cr } from 'node:module'; if (typeof globalThis.require === 'undefined') { globalThis.require = __cr(import.meta.url); }
3
+ import {
4
+ fetchSmitheryDetail,
5
+ handleToFetchResult,
6
+ loadMorePages,
7
+ openRegistry,
8
+ specStringFor
9
+ } from "./chunk-6DRKA2IL.js";
10
+ import {
11
+ MCP_CATALOG,
12
+ mcpCommandFor
13
+ } from "./chunk-56OAJILV.js";
14
+ import {
15
+ defaultConfigPath,
16
+ readConfig,
17
+ writeConfig
18
+ } from "./chunk-6ALJTWWQ.js";
19
+ import "./chunk-TUK7OWJA.js";
20
+
21
+ // src/cli/commands/mcp.ts
22
+ var DEFAULT_LIST_LIMIT = 30;
23
+ var SEARCH_PAGE_CAP = 20;
24
+ var INSTALL_PAGE_CAP = 30;
25
+ var progressToStderr = ({ source, page, entries }) => {
26
+ if (page === 1 || page % 5 === 0) {
27
+ process.stderr.write(`\r\u25B8 fetching ${source} registry \xB7 page ${page} \xB7 ${entries} entries`);
28
+ }
29
+ };
30
+ function finishProgressLine() {
31
+ if (process.stderr.isTTY) process.stderr.write("\r\x1B[K");
32
+ else process.stderr.write("\n");
33
+ }
34
+ function rankEntries(entries) {
35
+ return [...entries].sort((a, b) => {
36
+ const ap = a.popularity ?? -1;
37
+ const bp = b.popularity ?? -1;
38
+ if (ap !== bp) return bp - ap;
39
+ return a.name.localeCompare(b.name);
40
+ });
41
+ }
42
+ function pad(s, width) {
43
+ return s.length >= width ? s : s + " ".repeat(width - s.length);
44
+ }
45
+ function fmtAge(ms) {
46
+ const sec = Math.floor(ms / 1e3);
47
+ if (sec < 60) return `${sec}s ago`;
48
+ if (sec < 3600) return `${Math.floor(sec / 60)}m ago`;
49
+ if (sec < 86400) return `${Math.floor(sec / 3600)}h ago`;
50
+ return `${Math.floor(sec / 86400)}d ago`;
51
+ }
52
+ function printEntry(e, indent = " ") {
53
+ const tag = e.source === "official" ? "[official]" : e.source === "smithery" ? "[smithery]" : "[local]";
54
+ const pop = e.popularity !== void 0 ? ` \xB7 ${e.popularity.toLocaleString()} uses` : "";
55
+ console.log(`${indent}${pad(e.name, 36)} ${tag}${pop}`);
56
+ if (e.description) console.log(`${indent} ${e.description}`);
57
+ if (e.install?.requiredEnv?.length) {
58
+ console.log(`${indent} needs: ${e.install.requiredEnv.join(", ")}`);
59
+ } else if (!e.install) {
60
+ console.log(`${indent} (smithery listing \u2014 install detail fetched lazily on install)`);
61
+ }
62
+ }
63
+ async function mcpListCommand(opts = {}) {
64
+ if (opts.local) {
65
+ if (opts.json) {
66
+ console.log(JSON.stringify(MCP_CATALOG, null, 2));
67
+ return;
68
+ }
69
+ console.log("Bundled MCP servers (offline catalog):");
70
+ console.log("");
71
+ for (const entry of MCP_CATALOG) {
72
+ console.log(` ${pad(entry.name, 12)} ${entry.summary}`);
73
+ console.log(` ${mcpCommandFor(entry)}`);
74
+ if (entry.note) console.log(` \xB7 ${entry.note}`);
75
+ console.log("");
76
+ }
77
+ return;
78
+ }
79
+ const handle = await openRegistry({ noCache: opts.refresh, onProgress: progressToStderr });
80
+ const wantedPages = opts.all ? Number.POSITIVE_INFINITY : opts.pages ?? 1;
81
+ const additional = Math.max(0, wantedPages - handle.cache.pagination.pagesLoaded);
82
+ if (additional > 0) {
83
+ await loadMorePages(handle, {
84
+ pages: additional,
85
+ onProgress: progressToStderr
86
+ });
87
+ }
88
+ finishProgressLine();
89
+ const result = handleToFetchResult(handle);
90
+ const ranked = rankEntries(result.entries);
91
+ const limit = opts.limit ?? DEFAULT_LIST_LIMIT;
92
+ const shown = ranked.slice(0, limit);
93
+ if (opts.json) {
94
+ console.log(
95
+ JSON.stringify(
96
+ {
97
+ source: result.source,
98
+ fromCache: result.fromCache,
99
+ fetchedAt: result.fetchedAt,
100
+ loaded: result.entries.length,
101
+ hasMore: result.hasMore,
102
+ entries: shown
103
+ },
104
+ null,
105
+ 2
106
+ )
107
+ );
108
+ return;
109
+ }
110
+ const ageStr = result.fromCache ? `cached, ${fmtAge(Date.now() - result.fetchedAt)}` : "just fetched";
111
+ const moreStr = result.hasMore ? "more available" : "all loaded";
112
+ console.log(
113
+ `MCP servers from ${result.source} registry (${result.entries.length} loaded, ${moreStr}, ${ageStr}):`
114
+ );
115
+ if (result.errors.length > 0) {
116
+ for (const e of result.errors) console.error(` warn: ${e}`);
117
+ }
118
+ console.log("");
119
+ for (const e of shown) printEntry(e);
120
+ if (ranked.length > limit) {
121
+ console.log(
122
+ ` \u2026 ${ranked.length - limit} more loaded \u2014 use \`luckerr mcp search <query>\` to filter`
123
+ );
124
+ }
125
+ if (result.hasMore) {
126
+ console.log(" \u25B8 more pages available \u2014 `luckerr mcp list --pages <n>` or --all");
127
+ }
128
+ console.log("");
129
+ console.log("Install: luckerr mcp install <name>");
130
+ }
131
+ function matchFilter(query) {
132
+ const q = query.toLowerCase();
133
+ return (e) => `${e.name} ${e.title} ${e.description}`.toLowerCase().includes(q);
134
+ }
135
+ async function mcpSearchCommand(query, opts = {}) {
136
+ const q = query.trim();
137
+ if (!q) {
138
+ console.error("usage: luckerr mcp search <query>");
139
+ process.exit(1);
140
+ }
141
+ const handle = await openRegistry({ noCache: opts.refresh, onProgress: progressToStderr });
142
+ const filter = matchFilter(q);
143
+ const limit = opts.limit ?? DEFAULT_LIST_LIMIT;
144
+ const cap = opts.maxPages ?? SEARCH_PAGE_CAP;
145
+ await loadMorePages(handle, {
146
+ pages: Math.max(0, cap - handle.cache.pagination.pagesLoaded),
147
+ matchTarget: limit,
148
+ filter,
149
+ onProgress: progressToStderr
150
+ });
151
+ finishProgressLine();
152
+ const result = handleToFetchResult(handle);
153
+ const matches = rankEntries(result.entries.filter(filter));
154
+ const shown = matches.slice(0, limit);
155
+ if (opts.json) {
156
+ console.log(
157
+ JSON.stringify(
158
+ {
159
+ query: q,
160
+ source: result.source,
161
+ loaded: result.entries.length,
162
+ hasMore: result.hasMore,
163
+ matches: matches.length,
164
+ entries: shown
165
+ },
166
+ null,
167
+ 2
168
+ )
169
+ );
170
+ return;
171
+ }
172
+ if (shown.length === 0) {
173
+ console.log(
174
+ `No matches for "${q}" across ${result.entries.length} loaded entries (${result.source}${result.hasMore ? ", more pages exist \u2014 try --refresh or `mcp list --all`" : ""}).`
175
+ );
176
+ return;
177
+ }
178
+ console.log(
179
+ `${matches.length} match(es) for "${q}" in ${result.source} registry (${result.entries.length} entries scanned):`
180
+ );
181
+ console.log("");
182
+ for (const e of shown) printEntry(e);
183
+ if (matches.length > limit) console.log(` \u2026 ${matches.length - limit} more matches`);
184
+ }
185
+ function findEntry(entries, name) {
186
+ const exact = entries.find((e) => e.name === name);
187
+ if (exact) return exact;
188
+ const lower = name.toLowerCase();
189
+ const ci = entries.find((e) => e.name.toLowerCase() === lower);
190
+ if (ci) return ci;
191
+ const tail = entries.find((e) => e.name.toLowerCase().endsWith(`/${lower}`));
192
+ if (tail) return tail;
193
+ return null;
194
+ }
195
+ async function mcpInstallCommand(name, opts = {}) {
196
+ const target = name.trim();
197
+ if (!target) {
198
+ console.error("usage: luckerr mcp install <name>");
199
+ process.exit(1);
200
+ }
201
+ const handle = await openRegistry({ noCache: opts.refresh, onProgress: progressToStderr });
202
+ const lower = target.toLowerCase();
203
+ const filter = (e) => {
204
+ const n = e.name.toLowerCase();
205
+ return n === lower || n.endsWith(`/${lower}`) || n.includes(lower);
206
+ };
207
+ const cap = opts.maxPages ?? INSTALL_PAGE_CAP;
208
+ await loadMorePages(handle, {
209
+ pages: Math.max(0, cap - handle.cache.pagination.pagesLoaded),
210
+ matchTarget: 1,
211
+ filter,
212
+ onProgress: progressToStderr
213
+ });
214
+ finishProgressLine();
215
+ const entry = findEntry(handle.cache.entries, target);
216
+ if (!entry) {
217
+ console.error(
218
+ `No MCP server named "${target}" found after walking ${handle.cache.pagination.pagesLoaded} page(s) of the ${handle.source} registry.`
219
+ );
220
+ if (handle.cache.pagination.nextCursor !== null) {
221
+ console.error(`Try: luckerr mcp install ${target} --max-pages 100`);
222
+ }
223
+ process.exit(1);
224
+ }
225
+ if (!entry.install && entry.source === "smithery") {
226
+ process.stderr.write(`\u25B8 fetching smithery install detail for ${entry.name}\u2026
227
+ `);
228
+ const fetched = await fetchSmitheryDetail(entry.name);
229
+ if (fetched) entry.install = fetched;
230
+ }
231
+ if (!entry.install) {
232
+ console.error(
233
+ `Could not derive install metadata for "${entry.name}" \u2014 try \`npx -y @smithery/cli install ${entry.name}\` directly.`
234
+ );
235
+ process.exit(1);
236
+ }
237
+ let spec;
238
+ try {
239
+ spec = specStringFor(entry.name, entry.install);
240
+ } catch (err) {
241
+ console.error(`Cannot build install spec for ${entry.name}: ${err.message}`);
242
+ process.exit(1);
243
+ }
244
+ const cfg = readConfig();
245
+ const existing = cfg.mcp ?? [];
246
+ if (existing.includes(spec)) {
247
+ console.log(`Already installed: ${spec}`);
248
+ return;
249
+ }
250
+ const next = { ...cfg, mcp: [...existing, spec] };
251
+ writeConfig(next);
252
+ console.log(`Installed: ${entry.name}`);
253
+ console.log(` spec: ${spec}`);
254
+ const installedName = parseInstalledName(spec);
255
+ if (entry.install.requiredEnv?.length) {
256
+ console.log(` needs: ${entry.install.requiredEnv.join(", ")}`);
257
+ console.log(" Either export these before launching, or add them to config:");
258
+ console.log(` mcpEnv.${installedName ?? entry.name} = { ... }`);
259
+ console.log(
260
+ ` (edit ${defaultConfigPath()} \u2014 values merge over process.env at spawn)`
261
+ );
262
+ }
263
+ console.log("");
264
+ console.log(
265
+ "Use it: luckerr chat (or `luckerr code`) \u2014 the server will be bridged automatically."
266
+ );
267
+ }
268
+ function parseInstalledName(spec) {
269
+ const match = /^([a-zA-Z_][a-zA-Z0-9_-]*)=/.exec(spec);
270
+ return match ? match[1] : null;
271
+ }
272
+ export {
273
+ mcpInstallCommand,
274
+ mcpListCommand,
275
+ mcpSearchCommand
276
+ };
277
+ //# sourceMappingURL=mcp-FGKEH7RG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/cli/commands/mcp.ts"],"sourcesContent":["import { defaultConfigPath, readConfig, writeConfig } from \"../../config.js\";\nimport { MCP_CATALOG, mcpCommandFor } from \"../../mcp/catalog.js\";\nimport {\n type FetchProgress,\n fetchSmitheryDetail,\n handleToFetchResult,\n loadMorePages,\n openRegistry,\n specStringFor,\n} from \"../../mcp/registry-fetch.js\";\nimport type { RegistryEntry } from \"../../mcp/registry-types.js\";\n\nconst DEFAULT_LIST_LIMIT = 30;\n/** Soft cap on how far `search` walks the registry on first run. */\nconst SEARCH_PAGE_CAP = 20;\n/** Soft cap on how far `install` walks looking for a name. */\nconst INSTALL_PAGE_CAP = 30;\n\nconst progressToStderr: FetchProgress = ({ source, page, entries }) => {\n if (page === 1 || page % 5 === 0) {\n process.stderr.write(`\\r▸ fetching ${source} registry · page ${page} · ${entries} entries`);\n }\n};\n\nfunction finishProgressLine(): void {\n if (process.stderr.isTTY) process.stderr.write(\"\\r\\x1b[K\");\n else process.stderr.write(\"\\n\");\n}\n\nexport interface McpListOptions {\n json?: boolean;\n /** Skip network — only show the bundled MCP_CATALOG entries. */\n local?: boolean;\n /** Bypass cache TTL. */\n refresh?: boolean;\n /** How many entries to show. Default 30. */\n limit?: number;\n /** Eagerly load this many pages before showing. Default 1. */\n pages?: number;\n /** Walk all pages of the registry (slow on first run). */\n all?: boolean;\n}\n\nexport interface McpSearchOptions {\n json?: boolean;\n refresh?: boolean;\n limit?: number;\n /** Cap how many pages to walk while searching. Default 20. */\n maxPages?: number;\n}\n\nexport interface McpInstallOptions {\n refresh?: boolean;\n /** Cap how many pages to walk while looking for the name. Default 30. */\n maxPages?: number;\n}\n\nfunction rankEntries(entries: RegistryEntry[]): RegistryEntry[] {\n return [...entries].sort((a, b) => {\n const ap = a.popularity ?? -1;\n const bp = b.popularity ?? -1;\n if (ap !== bp) return bp - ap;\n return a.name.localeCompare(b.name);\n });\n}\n\nfunction pad(s: string, width: number): string {\n return s.length >= width ? s : s + \" \".repeat(width - s.length);\n}\n\nfunction fmtAge(ms: number): string {\n const sec = Math.floor(ms / 1000);\n if (sec < 60) return `${sec}s ago`;\n if (sec < 3600) return `${Math.floor(sec / 60)}m ago`;\n if (sec < 86400) return `${Math.floor(sec / 3600)}h ago`;\n return `${Math.floor(sec / 86400)}d ago`;\n}\n\nfunction printEntry(e: RegistryEntry, indent = \" \"): void {\n const tag =\n e.source === \"official\" ? \"[official]\" : e.source === \"smithery\" ? \"[smithery]\" : \"[local]\";\n const pop = e.popularity !== undefined ? ` · ${e.popularity.toLocaleString()} uses` : \"\";\n console.log(`${indent}${pad(e.name, 36)} ${tag}${pop}`);\n if (e.description) console.log(`${indent} ${e.description}`);\n if (e.install?.requiredEnv?.length) {\n console.log(`${indent} needs: ${e.install.requiredEnv.join(\", \")}`);\n } else if (!e.install) {\n console.log(`${indent} (smithery listing — install detail fetched lazily on install)`);\n }\n}\n\nexport async function mcpListCommand(opts: McpListOptions = {}): Promise<void> {\n if (opts.local) {\n if (opts.json) {\n console.log(JSON.stringify(MCP_CATALOG, null, 2));\n return;\n }\n console.log(\"Bundled MCP servers (offline catalog):\");\n console.log(\"\");\n for (const entry of MCP_CATALOG) {\n console.log(` ${pad(entry.name, 12)} ${entry.summary}`);\n console.log(` ${mcpCommandFor(entry)}`);\n if (entry.note) console.log(` · ${entry.note}`);\n console.log(\"\");\n }\n return;\n }\n\n const handle = await openRegistry({ noCache: opts.refresh, onProgress: progressToStderr });\n const wantedPages = opts.all ? Number.POSITIVE_INFINITY : (opts.pages ?? 1);\n const additional = Math.max(0, wantedPages - handle.cache.pagination.pagesLoaded);\n if (additional > 0) {\n await loadMorePages(handle, {\n pages: additional,\n onProgress: progressToStderr,\n });\n }\n finishProgressLine();\n\n const result = handleToFetchResult(handle);\n const ranked = rankEntries(result.entries);\n const limit = opts.limit ?? DEFAULT_LIST_LIMIT;\n const shown = ranked.slice(0, limit);\n\n if (opts.json) {\n console.log(\n JSON.stringify(\n {\n source: result.source,\n fromCache: result.fromCache,\n fetchedAt: result.fetchedAt,\n loaded: result.entries.length,\n hasMore: result.hasMore,\n entries: shown,\n },\n null,\n 2,\n ),\n );\n return;\n }\n\n const ageStr = result.fromCache\n ? `cached, ${fmtAge(Date.now() - result.fetchedAt)}`\n : \"just fetched\";\n const moreStr = result.hasMore ? \"more available\" : \"all loaded\";\n console.log(\n `MCP servers from ${result.source} registry (${result.entries.length} loaded, ${moreStr}, ${ageStr}):`,\n );\n if (result.errors.length > 0) {\n for (const e of result.errors) console.error(` warn: ${e}`);\n }\n console.log(\"\");\n for (const e of shown) printEntry(e);\n if (ranked.length > limit) {\n console.log(\n ` … ${ranked.length - limit} more loaded — use \\`luckerr mcp search <query>\\` to filter`,\n );\n }\n if (result.hasMore) {\n console.log(\" ▸ more pages available — `luckerr mcp list --pages <n>` or --all\");\n }\n console.log(\"\");\n console.log(\"Install: luckerr mcp install <name>\");\n}\n\nfunction matchFilter(query: string): (e: RegistryEntry) => boolean {\n const q = query.toLowerCase();\n return (e) => `${e.name} ${e.title} ${e.description}`.toLowerCase().includes(q);\n}\n\nexport async function mcpSearchCommand(query: string, opts: McpSearchOptions = {}): Promise<void> {\n const q = query.trim();\n if (!q) {\n console.error(\"usage: luckerr mcp search <query>\");\n process.exit(1);\n }\n const handle = await openRegistry({ noCache: opts.refresh, onProgress: progressToStderr });\n const filter = matchFilter(q);\n const limit = opts.limit ?? DEFAULT_LIST_LIMIT;\n const cap = opts.maxPages ?? SEARCH_PAGE_CAP;\n\n await loadMorePages(handle, {\n pages: Math.max(0, cap - handle.cache.pagination.pagesLoaded),\n matchTarget: limit,\n filter,\n onProgress: progressToStderr,\n });\n finishProgressLine();\n\n const result = handleToFetchResult(handle);\n const matches = rankEntries(result.entries.filter(filter));\n const shown = matches.slice(0, limit);\n\n if (opts.json) {\n console.log(\n JSON.stringify(\n {\n query: q,\n source: result.source,\n loaded: result.entries.length,\n hasMore: result.hasMore,\n matches: matches.length,\n entries: shown,\n },\n null,\n 2,\n ),\n );\n return;\n }\n\n if (shown.length === 0) {\n console.log(\n `No matches for \"${q}\" across ${result.entries.length} loaded entries (${result.source}${\n result.hasMore ? \", more pages exist — try --refresh or `mcp list --all`\" : \"\"\n }).`,\n );\n return;\n }\n console.log(\n `${matches.length} match(es) for \"${q}\" in ${result.source} registry (${result.entries.length} entries scanned):`,\n );\n console.log(\"\");\n for (const e of shown) printEntry(e);\n if (matches.length > limit) console.log(` … ${matches.length - limit} more matches`);\n}\n\nfunction findEntry(entries: RegistryEntry[], name: string): RegistryEntry | null {\n const exact = entries.find((e) => e.name === name);\n if (exact) return exact;\n const lower = name.toLowerCase();\n const ci = entries.find((e) => e.name.toLowerCase() === lower);\n if (ci) return ci;\n const tail = entries.find((e) => e.name.toLowerCase().endsWith(`/${lower}`));\n if (tail) return tail;\n return null;\n}\n\nexport async function mcpInstallCommand(name: string, opts: McpInstallOptions = {}): Promise<void> {\n const target = name.trim();\n if (!target) {\n console.error(\"usage: luckerr mcp install <name>\");\n process.exit(1);\n }\n\n const handle = await openRegistry({ noCache: opts.refresh, onProgress: progressToStderr });\n const lower = target.toLowerCase();\n const filter = (e: RegistryEntry): boolean => {\n const n = e.name.toLowerCase();\n return n === lower || n.endsWith(`/${lower}`) || n.includes(lower);\n };\n const cap = opts.maxPages ?? INSTALL_PAGE_CAP;\n\n await loadMorePages(handle, {\n pages: Math.max(0, cap - handle.cache.pagination.pagesLoaded),\n matchTarget: 1,\n filter,\n onProgress: progressToStderr,\n });\n finishProgressLine();\n\n const entry = findEntry(handle.cache.entries, target);\n if (!entry) {\n console.error(\n `No MCP server named \"${target}\" found after walking ${handle.cache.pagination.pagesLoaded} page(s) of the ${handle.source} registry.`,\n );\n if (handle.cache.pagination.nextCursor !== null) {\n console.error(`Try: luckerr mcp install ${target} --max-pages 100`);\n }\n process.exit(1);\n }\n\n if (!entry.install && entry.source === \"smithery\") {\n process.stderr.write(`▸ fetching smithery install detail for ${entry.name}…\\n`);\n const fetched = await fetchSmitheryDetail(entry.name);\n if (fetched) entry.install = fetched;\n }\n\n if (!entry.install) {\n console.error(\n `Could not derive install metadata for \"${entry.name}\" — try \\`npx -y @smithery/cli install ${entry.name}\\` directly.`,\n );\n process.exit(1);\n }\n\n let spec: string;\n try {\n spec = specStringFor(entry.name, entry.install);\n } catch (err) {\n console.error(`Cannot build install spec for ${entry.name}: ${(err as Error).message}`);\n process.exit(1);\n }\n\n const cfg = readConfig();\n const existing = cfg.mcp ?? [];\n if (existing.includes(spec)) {\n console.log(`Already installed: ${spec}`);\n return;\n }\n const next = { ...cfg, mcp: [...existing, spec] };\n writeConfig(next);\n\n console.log(`Installed: ${entry.name}`);\n console.log(` spec: ${spec}`);\n const installedName = parseInstalledName(spec);\n if (entry.install.requiredEnv?.length) {\n console.log(` needs: ${entry.install.requiredEnv.join(\", \")}`);\n console.log(\" Either export these before launching, or add them to config:\");\n console.log(` mcpEnv.${installedName ?? entry.name} = { ... }`);\n console.log(\n ` (edit ${defaultConfigPath()} — values merge over process.env at spawn)`,\n );\n }\n console.log(\"\");\n console.log(\n \"Use it: luckerr chat (or `luckerr code`) — the server will be bridged automatically.\",\n );\n}\n\nfunction parseInstalledName(spec: string): string | null {\n const match = /^([a-zA-Z_][a-zA-Z0-9_-]*)=/.exec(spec);\n return match ? match[1]! : null;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,178 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire as __cr } from 'node:module'; if (typeof globalThis.require === 'undefined') { globalThis.require = __cr(import.meta.url); }
3
+ import {
4
+ loadOverlay
5
+ } from "./chunk-JMBMLOBP.js";
6
+ import {
7
+ Box_default,
8
+ Text,
9
+ render_default,
10
+ require_react,
11
+ use_app_default,
12
+ use_input_default
13
+ } from "./chunk-WREKDFXT.js";
14
+ import {
15
+ loadDotenv
16
+ } from "./chunk-2UQP6H6T.js";
17
+ import {
18
+ loadMorePages,
19
+ openRegistry,
20
+ specStringFor
21
+ } from "./chunk-6DRKA2IL.js";
22
+ import "./chunk-56OAJILV.js";
23
+ import {
24
+ readConfig,
25
+ writeConfig
26
+ } from "./chunk-6ALJTWWQ.js";
27
+ import {
28
+ __toESM
29
+ } from "./chunk-TUK7OWJA.js";
30
+
31
+ // src/cli/commands/mcp-browse.tsx
32
+ var import_react = __toESM(require_react(), 1);
33
+ var VISIBLE_ROWS = 12;
34
+ function rankAndFilter(entries, query) {
35
+ const q = query.trim().toLowerCase();
36
+ const list = q ? entries.filter((e) => `${e.name} ${e.title} ${e.description}`.toLowerCase().includes(q)) : entries;
37
+ return [...list].sort((a, b) => {
38
+ const ap = a.popularity ?? -1;
39
+ const bp = b.popularity ?? -1;
40
+ if (ap !== bp) return bp - ap;
41
+ return a.name.localeCompare(b.name);
42
+ });
43
+ }
44
+ function McpBrowseApp() {
45
+ const app = use_app_default();
46
+ const [state, setState] = (0, import_react.useState)({
47
+ handle: null,
48
+ loading: true,
49
+ query: "",
50
+ selected: 0,
51
+ status: "opening registry\u2026"
52
+ });
53
+ const setStatus = (0, import_react.useCallback)((status) => {
54
+ setState((s) => ({ ...s, status }));
55
+ }, []);
56
+ (0, import_react.useEffect)(() => {
57
+ let cancelled = false;
58
+ (async () => {
59
+ try {
60
+ const handle = await openRegistry({});
61
+ if (cancelled) return;
62
+ const ageMs = Date.now() - handle.fetchedAt;
63
+ const ageStr = ageMs < 6e4 ? `${Math.floor(ageMs / 1e3)}s` : `${Math.floor(ageMs / 6e4)}m`;
64
+ setState((s) => ({
65
+ ...s,
66
+ handle,
67
+ loading: false,
68
+ status: `${handle.source} \xB7 ${handle.cache.entries.length} entries${handle.fromCache ? ` \xB7 cached ${ageStr} ago` : ""}`
69
+ }));
70
+ } catch (err) {
71
+ if (cancelled) return;
72
+ setState((s) => ({ ...s, loading: false, status: `error: ${err.message}` }));
73
+ }
74
+ })();
75
+ return () => {
76
+ cancelled = true;
77
+ };
78
+ }, []);
79
+ const filtered = (0, import_react.useMemo)(() => {
80
+ if (!state.handle) return [];
81
+ return rankAndFilter(state.handle.cache.entries, state.query);
82
+ }, [state.handle, state.query]);
83
+ const selected = filtered[state.selected];
84
+ const fetchMore = (0, import_react.useCallback)(async () => {
85
+ if (!state.handle || state.loading) return;
86
+ if (state.handle.cache.pagination.nextCursor === null) {
87
+ setStatus("no more pages \u2014 registry exhausted");
88
+ return;
89
+ }
90
+ setState((s) => ({ ...s, loading: true, status: "loading more\u2026" }));
91
+ try {
92
+ const r = await loadMorePages(state.handle, { pages: 5 });
93
+ setState((s) => ({
94
+ ...s,
95
+ loading: false,
96
+ status: `+${r.newEntries} entries (${state.handle?.cache.entries.length ?? 0} total)${r.exhausted ? " \xB7 exhausted" : ""}`
97
+ }));
98
+ } catch (err) {
99
+ setState((s) => ({ ...s, loading: false, status: `error: ${err.message}` }));
100
+ }
101
+ }, [state.handle, state.loading, setStatus]);
102
+ const install = (0, import_react.useCallback)(
103
+ (entry) => {
104
+ if (!entry.install) {
105
+ setStatus(`${entry.name} has no install info (smithery listing)`);
106
+ return;
107
+ }
108
+ try {
109
+ const spec = specStringFor(entry.name, entry.install);
110
+ const cfg = readConfig();
111
+ const existing = cfg.mcp ?? [];
112
+ if (existing.includes(spec)) {
113
+ setStatus(`already installed: ${spec}`);
114
+ return;
115
+ }
116
+ writeConfig({ ...cfg, mcp: [...existing, spec] });
117
+ setStatus(`installed \u2192 ${spec}`);
118
+ } catch (err) {
119
+ setStatus(`install failed: ${err.message}`);
120
+ }
121
+ },
122
+ [setStatus]
123
+ );
124
+ use_input_default((input, key) => {
125
+ if (key.escape || key.ctrl && input === "c") {
126
+ app.exit();
127
+ return;
128
+ }
129
+ if (key.upArrow) {
130
+ setState((s) => ({ ...s, selected: Math.max(0, s.selected - 1) }));
131
+ return;
132
+ }
133
+ if (key.downArrow) {
134
+ setState((s) => ({ ...s, selected: Math.min(filtered.length - 1, s.selected + 1) }));
135
+ return;
136
+ }
137
+ if (key.return) {
138
+ if (selected) install(selected);
139
+ return;
140
+ }
141
+ if (key.tab || key.ctrl && input === "n") {
142
+ void fetchMore();
143
+ return;
144
+ }
145
+ if (key.backspace || key.delete) {
146
+ setState((s) => ({ ...s, query: s.query.slice(0, -1), selected: 0 }));
147
+ return;
148
+ }
149
+ if (input && !key.ctrl && !key.meta) {
150
+ setState((s) => ({ ...s, query: s.query + input, selected: 0 }));
151
+ }
152
+ });
153
+ const overlay = (0, import_react.useMemo)(() => loadOverlay("zh-CN"), []);
154
+ const start = Math.max(
155
+ 0,
156
+ Math.min(state.selected - Math.floor(VISIBLE_ROWS / 2), filtered.length - VISIBLE_ROWS)
157
+ );
158
+ const window = filtered.slice(Math.max(0, start), Math.max(0, start) + VISIBLE_ROWS);
159
+ return /* @__PURE__ */ import_react.default.createElement(Box_default, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ import_react.default.createElement(Box_default, null, /* @__PURE__ */ import_react.default.createElement(Text, { bold: true, color: "cyan" }, "\u25C8 MCP marketplace"), /* @__PURE__ */ import_react.default.createElement(Text, { dimColor: true }, ` \xB7 ${state.status}`)), /* @__PURE__ */ import_react.default.createElement(Box_default, { marginTop: 1 }, /* @__PURE__ */ import_react.default.createElement(Text, null, "search: "), /* @__PURE__ */ import_react.default.createElement(Text, { color: "white" }, state.query || "(type to filter)"), /* @__PURE__ */ import_react.default.createElement(Text, { dimColor: true }, ` ${filtered.length} match${filtered.length === 1 ? "" : "es"}`)), /* @__PURE__ */ import_react.default.createElement(Box_default, { marginTop: 1, flexDirection: "column" }, window.length === 0 ? /* @__PURE__ */ import_react.default.createElement(Text, { dimColor: true }, state.loading ? "loading\u2026" : "no entries") : window.map((e, i) => {
160
+ const idx = (start || 0) + i;
161
+ const active = idx === state.selected;
162
+ const tag = e.source === "official" ? "[off]" : e.source === "smithery" ? "[smt]" : "[loc]";
163
+ const pop = e.popularity !== void 0 ? ` \xB7 ${e.popularity.toLocaleString()}` : "";
164
+ return /* @__PURE__ */ import_react.default.createElement(Box_default, { key: e.name }, /* @__PURE__ */ import_react.default.createElement(Text, { color: active ? "cyan" : void 0 }, active ? "\u25B8 " : " "), /* @__PURE__ */ import_react.default.createElement(Text, { bold: active }, e.name.padEnd(40).slice(0, 40)), /* @__PURE__ */ import_react.default.createElement(Text, { dimColor: true }, ` ${tag}${pop}`));
165
+ })), selected ? /* @__PURE__ */ import_react.default.createElement(Box_default, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ import_react.default.createElement(Text, { bold: true, color: "white" }, overlay?.[selected.name]?.title ?? selected.title, overlay?.[selected.name] ? /* @__PURE__ */ import_react.default.createElement(Text, { dimColor: true }, ` \xB7 ${selected.title}`) : null), /* @__PURE__ */ import_react.default.createElement(Text, { dimColor: true }, overlay?.[selected.name]?.description ?? selected.description?.slice(0, 160) ?? null), selected.install ? /* @__PURE__ */ import_react.default.createElement(Text, { dimColor: true }, `spec: ${selected.install.runtime} ${selected.install.packageId ?? selected.install.url ?? "\u2014"} \xB7 ${selected.install.transport}`) : /* @__PURE__ */ import_react.default.createElement(Text, { dimColor: true }, "(smithery listing \u2014 install info not exposed)"), selected.install?.requiredEnv?.length ? /* @__PURE__ */ import_react.default.createElement(Text, { color: "yellow" }, `needs: ${selected.install.requiredEnv.join(", ")}`) : null) : null, /* @__PURE__ */ import_react.default.createElement(Box_default, { marginTop: 1 }, /* @__PURE__ */ import_react.default.createElement(Text, { dimColor: true }, "type to filter \xB7 \u2191\u2193 pick \xB7 enter install \xB7 tab load more \xB7 esc quit")));
166
+ }
167
+ async function mcpBrowseCommand(_opts = {}) {
168
+ loadDotenv();
169
+ const { waitUntilExit } = render_default(/* @__PURE__ */ import_react.default.createElement(McpBrowseApp, null), {
170
+ exitOnCtrlC: true,
171
+ patchConsole: false
172
+ });
173
+ await waitUntilExit();
174
+ }
175
+ export {
176
+ mcpBrowseCommand
177
+ };
178
+ //# sourceMappingURL=mcp-browse-YCND4NWT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/cli/commands/mcp-browse.tsx"],"sourcesContent":["/** `luckerr mcp browse` — Ink TUI for the MCP marketplace. Lazy-loads pages on scroll. */\n\nimport { Box, Text, render, useApp, useInput } from \"ink\";\nimport React, { useCallback, useEffect, useMemo, useState } from \"react\";\nimport { readConfig, writeConfig } from \"../../config.js\";\nimport { loadDotenv } from \"../../env.js\";\nimport { loadOverlay } from \"../../mcp/marketplace-overlay/loader.js\";\nimport {\n type RegistryHandle,\n loadMorePages,\n openRegistry,\n specStringFor,\n} from \"../../mcp/registry-fetch.js\";\nimport type { RegistryEntry } from \"../../mcp/registry-types.js\";\n\nconst VISIBLE_ROWS = 12;\n\ninterface State {\n handle: RegistryHandle | null;\n loading: boolean;\n query: string;\n selected: number;\n status: string;\n}\n\nfunction rankAndFilter(entries: RegistryEntry[], query: string): RegistryEntry[] {\n const q = query.trim().toLowerCase();\n const list = q\n ? entries.filter((e) => `${e.name} ${e.title} ${e.description}`.toLowerCase().includes(q))\n : entries;\n return [...list].sort((a, b) => {\n const ap = a.popularity ?? -1;\n const bp = b.popularity ?? -1;\n if (ap !== bp) return bp - ap;\n return a.name.localeCompare(b.name);\n });\n}\n\nfunction McpBrowseApp() {\n const app = useApp();\n const [state, setState] = useState<State>({\n handle: null,\n loading: true,\n query: \"\",\n selected: 0,\n status: \"opening registry…\",\n });\n\n const setStatus = useCallback((status: string) => {\n setState((s) => ({ ...s, status }));\n }, []);\n\n useEffect(() => {\n let cancelled = false;\n (async () => {\n try {\n const handle = await openRegistry({});\n if (cancelled) return;\n const ageMs = Date.now() - handle.fetchedAt;\n const ageStr =\n ageMs < 60_000 ? `${Math.floor(ageMs / 1000)}s` : `${Math.floor(ageMs / 60_000)}m`;\n setState((s) => ({\n ...s,\n handle,\n loading: false,\n status: `${handle.source} · ${handle.cache.entries.length} entries${\n handle.fromCache ? ` · cached ${ageStr} ago` : \"\"\n }`,\n }));\n } catch (err) {\n if (cancelled) return;\n setState((s) => ({ ...s, loading: false, status: `error: ${(err as Error).message}` }));\n }\n })();\n return () => {\n cancelled = true;\n };\n }, []);\n\n const filtered = useMemo(() => {\n if (!state.handle) return [];\n return rankAndFilter(state.handle.cache.entries, state.query);\n }, [state.handle, state.query]);\n\n const selected = filtered[state.selected];\n\n const fetchMore = useCallback(async () => {\n if (!state.handle || state.loading) return;\n if (state.handle.cache.pagination.nextCursor === null) {\n setStatus(\"no more pages — registry exhausted\");\n return;\n }\n setState((s) => ({ ...s, loading: true, status: \"loading more…\" }));\n try {\n const r = await loadMorePages(state.handle, { pages: 5 });\n setState((s) => ({\n ...s,\n loading: false,\n status: `+${r.newEntries} entries (${state.handle?.cache.entries.length ?? 0} total)${\n r.exhausted ? \" · exhausted\" : \"\"\n }`,\n }));\n } catch (err) {\n setState((s) => ({ ...s, loading: false, status: `error: ${(err as Error).message}` }));\n }\n }, [state.handle, state.loading, setStatus]);\n\n const install = useCallback(\n (entry: RegistryEntry) => {\n if (!entry.install) {\n setStatus(`${entry.name} has no install info (smithery listing)`);\n return;\n }\n try {\n const spec = specStringFor(entry.name, entry.install);\n const cfg = readConfig();\n const existing = cfg.mcp ?? [];\n if (existing.includes(spec)) {\n setStatus(`already installed: ${spec}`);\n return;\n }\n writeConfig({ ...cfg, mcp: [...existing, spec] });\n setStatus(`installed → ${spec}`);\n } catch (err) {\n setStatus(`install failed: ${(err as Error).message}`);\n }\n },\n [setStatus],\n );\n\n useInput((input, key) => {\n if (key.escape || (key.ctrl && input === \"c\")) {\n app.exit();\n return;\n }\n if (key.upArrow) {\n setState((s) => ({ ...s, selected: Math.max(0, s.selected - 1) }));\n return;\n }\n if (key.downArrow) {\n setState((s) => ({ ...s, selected: Math.min(filtered.length - 1, s.selected + 1) }));\n return;\n }\n if (key.return) {\n if (selected) install(selected);\n return;\n }\n if (key.tab || (key.ctrl && input === \"n\")) {\n void fetchMore();\n return;\n }\n if (key.backspace || key.delete) {\n setState((s) => ({ ...s, query: s.query.slice(0, -1), selected: 0 }));\n return;\n }\n if (input && !key.ctrl && !key.meta) {\n setState((s) => ({ ...s, query: s.query + input, selected: 0 }));\n }\n });\n\n const overlay = useMemo(() => loadOverlay(\"zh-CN\"), []);\n\n const start = Math.max(\n 0,\n Math.min(state.selected - Math.floor(VISIBLE_ROWS / 2), filtered.length - VISIBLE_ROWS),\n );\n const window = filtered.slice(Math.max(0, start), Math.max(0, start) + VISIBLE_ROWS);\n\n return (\n <Box flexDirection=\"column\" paddingX={1}>\n <Box>\n <Text bold color=\"cyan\">\n ◈ MCP marketplace\n </Text>\n <Text dimColor>{` · ${state.status}`}</Text>\n </Box>\n <Box marginTop={1}>\n <Text>search: </Text>\n <Text color=\"white\">{state.query || \"(type to filter)\"}</Text>\n <Text dimColor>{` ${filtered.length} match${filtered.length === 1 ? \"\" : \"es\"}`}</Text>\n </Box>\n <Box marginTop={1} flexDirection=\"column\">\n {window.length === 0 ? (\n <Text dimColor>{state.loading ? \"loading…\" : \"no entries\"}</Text>\n ) : (\n window.map((e, i) => {\n const idx = (start || 0) + i;\n const active = idx === state.selected;\n const tag =\n e.source === \"official\" ? \"[off]\" : e.source === \"smithery\" ? \"[smt]\" : \"[loc]\";\n const pop = e.popularity !== undefined ? ` · ${e.popularity.toLocaleString()}` : \"\";\n return (\n <Box key={e.name}>\n <Text color={active ? \"cyan\" : undefined}>{active ? \"▸ \" : \" \"}</Text>\n <Text bold={active}>{e.name.padEnd(40).slice(0, 40)}</Text>\n <Text dimColor>{` ${tag}${pop}`}</Text>\n </Box>\n );\n })\n )}\n </Box>\n {selected ? (\n <Box marginTop={1} flexDirection=\"column\">\n <Text bold color=\"white\">\n {overlay?.[selected.name]?.title ?? selected.title}\n {overlay?.[selected.name] ? (\n <Text dimColor>{` \\u00b7 ${selected.title}`}</Text>\n ) : null}\n </Text>\n <Text dimColor>\n {overlay?.[selected.name]?.description ?? selected.description?.slice(0, 160) ?? null}\n </Text>\n {selected.install ? (\n <Text dimColor>\n {`spec: ${selected.install.runtime} ${selected.install.packageId ?? selected.install.url ?? \"—\"} · ${selected.install.transport}`}\n </Text>\n ) : (\n <Text dimColor>(smithery listing — install info not exposed)</Text>\n )}\n {selected.install?.requiredEnv?.length ? (\n <Text color=\"yellow\">{`needs: ${selected.install.requiredEnv.join(\", \")}`}</Text>\n ) : null}\n </Box>\n ) : null}\n <Box marginTop={1}>\n <Text dimColor>type to filter · ↑↓ pick · enter install · tab load more · esc quit</Text>\n </Box>\n </Box>\n );\n}\n\nexport interface McpBrowseOptions {\n /** Reserved — currently unused, kept for symmetry with other commands. */\n _unused?: never;\n}\n\nexport async function mcpBrowseCommand(_opts: McpBrowseOptions = {}): Promise<void> {\n loadDotenv();\n const { waitUntilExit } = render(<McpBrowseApp />, {\n exitOnCtrlC: true,\n patchConsole: false,\n });\n await waitUntilExit();\n}\n"],"mappings":"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","names":["React"]}
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire as __cr } from 'node:module'; if (typeof globalThis.require === 'undefined') { globalThis.require = __cr(import.meta.url); }
3
+ import {
4
+ buildTransportFromSpec,
5
+ preflightStdioSpec
6
+ } from "./chunk-HRUZAIHQ.js";
7
+ import {
8
+ McpClient,
9
+ inspectMcpServer,
10
+ parseMcpSpec
11
+ } from "./chunk-YV5XXFD7.js";
12
+ import "./chunk-25T6CVUP.js";
13
+ import {
14
+ mcpEnvFor,
15
+ readConfig
16
+ } from "./chunk-6ALJTWWQ.js";
17
+ import "./chunk-MPAO3JNR.js";
18
+ import "./chunk-TUK7OWJA.js";
19
+
20
+ // src/cli/commands/mcp-inspect.ts
21
+ async function mcpInspectCommand(opts) {
22
+ const spec = parseMcpSpec(opts.spec);
23
+ if (spec.transport === "stdio") preflightStdioSpec(spec);
24
+ const transport = buildTransportFromSpec(spec, { env: mcpEnvFor(spec.name, readConfig()) });
25
+ const client = new McpClient({ transport });
26
+ try {
27
+ await client.initialize();
28
+ const report = await inspectMcpServer(client);
29
+ if (opts.json) {
30
+ console.log(JSON.stringify(report, null, 2));
31
+ } else {
32
+ console.log(formatReport(spec.name ?? "(anon)", report));
33
+ }
34
+ } finally {
35
+ await client.close();
36
+ }
37
+ }
38
+ function formatMcpInspectFailure(err) {
39
+ const error = err instanceof Error ? err : new Error(String(err));
40
+ const message = error.message;
41
+ const code = error.code;
42
+ if (code === "ENOENT") {
43
+ const command = message.match(/^spawn\s+([^\s]+)\s+ENOENT$/)?.[1] ?? "the command";
44
+ return `${message} \u2014 try: install or verify \`${command}\`, then check the MCP spec's command spelling`;
45
+ }
46
+ if (code === "ECONNREFUSED") {
47
+ const target = message.match(/\b(https?:\/\/\S+|\d+\.\d+\.\d+\.\d+:\d+|localhost:\d+)\b/i)?.[1];
48
+ return `${message} \u2014 try: confirm ${target ?? "the MCP server"} is running and the host/port match the spec`;
49
+ }
50
+ if (code === "ENOTFOUND" || code === "EAI_AGAIN") {
51
+ return `${message} \u2014 try: confirm the hostname is spelled correctly and DNS resolution is working (check your network/VPN)`;
52
+ }
53
+ if (code === "ECONNRESET") {
54
+ return `${message} \u2014 try: retry the request; if it keeps happening, check the server's logs for crashes or rate limits`;
55
+ }
56
+ if (code === "ETIMEDOUT") {
57
+ return `${message} \u2014 try: confirm the host is reachable and no firewall/proxy is blocking the port`;
58
+ }
59
+ if (code === "CERT_HAS_EXPIRED" || code === "DEPTH_ZERO_SELF_SIGNED_CERT" || code === "UNABLE_TO_VERIFY_LEAF_SIGNATURE" || code === "SELF_SIGNED_CERT_IN_CHAIN") {
60
+ return `${message} \u2014 try: renew or trust the server's TLS certificate, or point the spec at an endpoint with a valid cert`;
61
+ }
62
+ const httpStatus = matchTransportHttpStatus(message);
63
+ if (httpStatus !== null) {
64
+ return `${message}${hintForHttpStatus(httpStatus)}`;
65
+ }
66
+ if (/^MCP request initialize \(id=\d+\) timed out after \d+ms$/.test(message)) {
67
+ return `${message} \u2014 try: confirm the target speaks MCP and completes the handshake before the request timeout`;
68
+ }
69
+ if (/^(empty MCP spec|MCP spec ".*" has name but no command)/.test(message)) {
70
+ return `${message} \u2014 try: pass \`name=command args\` or an http(s):// URL`;
71
+ }
72
+ return message;
73
+ }
74
+ function matchTransportHttpStatus(message) {
75
+ const m = message.match(/^SSE handshake \S+ → (\d{3})\b/) ?? message.match(/^MCP SSE POST \S+ failed: (\d{3})\b/) ?? message.match(/^MCP Streamable HTTP POST \S+ → (\d{3})\b/);
76
+ return m ? Number(m[1]) : null;
77
+ }
78
+ function hintForHttpStatus(status) {
79
+ if (status === 401) {
80
+ return " \u2014 try: check the spec's auth header (e.g. `Authorization: Bearer \u2026`) or confirm the token isn't expired";
81
+ }
82
+ if (status === 403) {
83
+ return " \u2014 try: confirm the credentials have permission to reach this MCP endpoint";
84
+ }
85
+ if (status === 404) {
86
+ return " \u2014 try: confirm the endpoint path in the spec matches what the server actually exposes";
87
+ }
88
+ if (status >= 500 && status <= 599) {
89
+ return " \u2014 try: retry shortly; if the failure persists, check the MCP server's logs";
90
+ }
91
+ return "";
92
+ }
93
+ function formatReport(nsName, r) {
94
+ const lines = [];
95
+ lines.push(`MCP server [${nsName}]`);
96
+ lines.push(
97
+ ` server ${r.serverInfo.name || "(unknown)"}${r.serverInfo.version ? ` v${r.serverInfo.version}` : ""}`
98
+ );
99
+ lines.push(` protocol ${r.protocolVersion}`);
100
+ const capKeys = Object.keys(r.capabilities);
101
+ lines.push(` caps ${capKeys.length > 0 ? capKeys.join(", ") : "(none advertised)"}`);
102
+ if (r.instructions) {
103
+ lines.push(` notes ${r.instructions.trim().slice(0, 200)}`);
104
+ }
105
+ lines.push("");
106
+ lines.push(formatSection("Tools", r.tools, toolLine));
107
+ lines.push(formatSection("Resources", r.resources, resourceLine));
108
+ lines.push(formatSection("Prompts", r.prompts, promptLine));
109
+ return lines.join("\n");
110
+ }
111
+ function formatSection(title, section, render) {
112
+ if (!section.supported) {
113
+ return `${title}: (not supported \u2014 ${section.reason})`;
114
+ }
115
+ if (section.items.length === 0) {
116
+ return `${title}: (none)`;
117
+ }
118
+ const lines = [`${title} (${section.items.length}):`];
119
+ for (const item of section.items) lines.push(` ${render(item)}`);
120
+ return lines.join("\n");
121
+ }
122
+ function toolLine(t) {
123
+ const desc = t.description ? ` \u2014 ${oneLine(t.description, 80)}` : "";
124
+ return `\xB7 ${t.name}${desc}`;
125
+ }
126
+ function resourceLine(r) {
127
+ const mime = r.mimeType ? ` [${r.mimeType}]` : "";
128
+ return `\xB7 ${r.name}${mime} ${r.uri}`;
129
+ }
130
+ function promptLine(p) {
131
+ const argPart = p.arguments && p.arguments.length > 0 ? ` (${p.arguments.map((a) => a.required ? a.name : `${a.name}?`).join(", ")})` : "";
132
+ const desc = p.description ? ` \u2014 ${oneLine(p.description, 80)}` : "";
133
+ return `\xB7 ${p.name}${argPart}${desc}`;
134
+ }
135
+ function oneLine(s, max) {
136
+ const flat = s.replace(/\s+/g, " ").trim();
137
+ return flat.length <= max ? flat : `${flat.slice(0, max - 1)}\u2026`;
138
+ }
139
+ export {
140
+ formatMcpInspectFailure,
141
+ mcpInspectCommand
142
+ };
143
+ //# sourceMappingURL=mcp-inspect-V34J3VX5.js.map