bfi-cli 0.1.102 → 0.1.104
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.
- package/README.md +26 -20
- package/dist/_chunks/command.mjs +1 -4
- package/dist/_chunks/intent.mjs +41 -10
- package/dist/_chunks/libs/@clack/core.mjs +28 -2
- package/dist/_chunks/libs/@clack/prompts.mjs +32 -2
- package/dist/_chunks/mcp.mjs +19 -87
- package/dist/_chunks/package-plugin.mjs +349 -72
- package/dist/_chunks/receipt.mjs +1 -1
- package/dist/_chunks/registry.mjs +83 -0
- package/dist/cli.mjs +987 -138
- package/package.json +1 -1
- package/dist/_chunks/client.mjs +0 -107
- package/dist/_chunks/files.mjs +0 -89
- package/dist/_chunks/manager.mjs +0 -436
- package/dist/_chunks/package-skill-identity.mjs +0 -18
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { normalizeConfig, object, validName } from "./model.mjs";
|
|
2
|
+
async function readCatalog(registry, query = "", fetcher = fetch) {
|
|
3
|
+
let base;
|
|
4
|
+
try {
|
|
5
|
+
base = new URL(registry);
|
|
6
|
+
} catch {
|
|
7
|
+
throw new Error("SkillHub 地址无效。");
|
|
8
|
+
}
|
|
9
|
+
if (!["https:", "http:"].includes(base.protocol) || base.username || base.password || base.search || base.hash) throw new Error("SkillHub 地址必须为无凭据的 HTTP(S) 地址。");
|
|
10
|
+
const entries = [];
|
|
11
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12
|
+
for (let page = 0; page < 100; page++) {
|
|
13
|
+
const url = new URL(`${base.toString().replace(/\/$/, "")}/api/web/mcps`);
|
|
14
|
+
url.search = new URLSearchParams({
|
|
15
|
+
query,
|
|
16
|
+
page: String(page),
|
|
17
|
+
size: "100"
|
|
18
|
+
}).toString();
|
|
19
|
+
const controller = new AbortController();
|
|
20
|
+
const timeout = setTimeout(() => controller.abort(), 15e3);
|
|
21
|
+
try {
|
|
22
|
+
const response = await fetcher(url, {
|
|
23
|
+
signal: controller.signal,
|
|
24
|
+
redirect: "error",
|
|
25
|
+
headers: { Accept: "application/json" }
|
|
26
|
+
});
|
|
27
|
+
if (!response.ok) throw new Error();
|
|
28
|
+
const reader = response.body?.getReader();
|
|
29
|
+
if (!reader) throw new Error();
|
|
30
|
+
const chunks = [];
|
|
31
|
+
let size = 0;
|
|
32
|
+
try {
|
|
33
|
+
while (true) {
|
|
34
|
+
const chunk = await reader.read();
|
|
35
|
+
if (chunk.done) break;
|
|
36
|
+
size += chunk.value.byteLength;
|
|
37
|
+
if (size > 2097152) throw new Error();
|
|
38
|
+
chunks.push(chunk.value);
|
|
39
|
+
}
|
|
40
|
+
} finally {
|
|
41
|
+
await reader.cancel().catch(() => {});
|
|
42
|
+
}
|
|
43
|
+
const envelope = object(JSON.parse(Buffer.concat(chunks).toString("utf8")));
|
|
44
|
+
if (envelope.code !== 0) throw new Error();
|
|
45
|
+
const data = object(envelope.data);
|
|
46
|
+
if (!Array.isArray(data.items) || typeof data.total !== "number" || data.total < 0) throw new Error();
|
|
47
|
+
for (const item of data.items) {
|
|
48
|
+
const row = object(item);
|
|
49
|
+
if (typeof row.registrationName !== "string") throw new Error();
|
|
50
|
+
const name = validName(row.registrationName);
|
|
51
|
+
if (seen.has(name)) throw new Error();
|
|
52
|
+
seen.add(name);
|
|
53
|
+
entries.push({
|
|
54
|
+
registrationName: name,
|
|
55
|
+
id: typeof row.id === "string" ? row.id : void 0,
|
|
56
|
+
title: typeof row.title === "string" ? row.title : void 0,
|
|
57
|
+
description: typeof row.description === "string" ? row.description : void 0,
|
|
58
|
+
revision: typeof row.revision === "number" ? row.revision : void 0,
|
|
59
|
+
config: normalizeConfig(row.config),
|
|
60
|
+
runtime: row.runtime ? { source: String(object(row.runtime).source) } : null
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
if (entries.length >= data.total) return entries;
|
|
64
|
+
if (!data.items.length) throw new Error();
|
|
65
|
+
} catch {
|
|
66
|
+
throw new Error("读取 MCP 市场失败:请核对 SkillHub 地址、网络及服务版本;未修改本地配置。");
|
|
67
|
+
} finally {
|
|
68
|
+
clearTimeout(timeout);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
throw new Error("MCP 市场分页超过安全上限,请缩小查询范围。");
|
|
72
|
+
}
|
|
73
|
+
async function catalogEntries(registry, names) {
|
|
74
|
+
const entries = [];
|
|
75
|
+
for (const name of names) {
|
|
76
|
+
validName(name);
|
|
77
|
+
const matches = (await readCatalog(registry, name)).filter((entry) => entry.registrationName === name);
|
|
78
|
+
if (matches.length !== 1) throw new Error(`市场中未找到唯一 MCP:${name}。请用 bfi mcp search 查询准确名称。`);
|
|
79
|
+
entries.push(matches[0]);
|
|
80
|
+
}
|
|
81
|
+
return entries;
|
|
82
|
+
}
|
|
83
|
+
export { catalogEntries, readCatalog };
|