bfi-cli 0.1.58 → 0.1.65
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/dist/cli.mjs +1648 -304
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -22,7 +22,7 @@ import os$1 from "node:os";
|
|
|
22
22
|
import zlib, { gunzipSync, inflateRawSync } from "node:zlib";
|
|
23
23
|
import { promisify } from "util";
|
|
24
24
|
import { execFile, spawn, spawnSync } from "child_process";
|
|
25
|
-
import { access, chmod, cp, lstat, mkdir, mkdtemp, readFile, readdir, readlink, realpath, rm, stat, symlink, writeFile } from "fs/promises";
|
|
25
|
+
import { access, chmod, cp, lstat, mkdir, mkdtemp, readFile, readdir, readlink, realpath, rename, rm, stat, symlink, writeFile } from "fs/promises";
|
|
26
26
|
import { determineAgent } from "@vercel/detect-agent";
|
|
27
27
|
var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
|
28
28
|
const GIT_HOST = process.env.BFI_GIT_HOST || "git.yyrd.com";
|
|
@@ -506,7 +506,8 @@ var SkillHubAPI = class {
|
|
|
506
506
|
let bodyStr;
|
|
507
507
|
let bodyBuf;
|
|
508
508
|
if (options.body !== void 0) if (options.body instanceof Buffer) bodyBuf = options.body;
|
|
509
|
-
else if (
|
|
509
|
+
else if (typeof options.body === "string") bodyStr = options.body;
|
|
510
|
+
else if (typeof FormData !== "undefined" && options.body instanceof FormData) return this.fetchRequest(url, requestOptions, options.body);
|
|
510
511
|
else {
|
|
511
512
|
requestOptions.headers["Content-Type"] = "application/json";
|
|
512
513
|
bodyStr = JSON.stringify(options.body);
|
|
@@ -550,6 +551,36 @@ var SkillHubAPI = class {
|
|
|
550
551
|
req.end();
|
|
551
552
|
});
|
|
552
553
|
}
|
|
554
|
+
async fetchRequest(url, requestOptions, formData) {
|
|
555
|
+
const headers = {
|
|
556
|
+
...this.getHeaders(),
|
|
557
|
+
...requestOptions.headers
|
|
558
|
+
};
|
|
559
|
+
const response = await fetch(url, {
|
|
560
|
+
method: requestOptions.method.toUpperCase(),
|
|
561
|
+
headers,
|
|
562
|
+
body: formData
|
|
563
|
+
});
|
|
564
|
+
const responseHeaders = {};
|
|
565
|
+
response.headers.forEach((value, key) => {
|
|
566
|
+
responseHeaders[key] = value;
|
|
567
|
+
});
|
|
568
|
+
const text = await response.text();
|
|
569
|
+
let body = text;
|
|
570
|
+
try {
|
|
571
|
+
body = JSON.parse(text);
|
|
572
|
+
} catch {}
|
|
573
|
+
if (response.ok) return {
|
|
574
|
+
status: response.status,
|
|
575
|
+
body,
|
|
576
|
+
headers: responseHeaders
|
|
577
|
+
};
|
|
578
|
+
const msg = body?.message || body?.msg || body?.error || `HTTP ${response.status}`;
|
|
579
|
+
const error = new Error(msg);
|
|
580
|
+
error.status = response.status;
|
|
581
|
+
error.body = body;
|
|
582
|
+
throw error;
|
|
583
|
+
}
|
|
553
584
|
async downloadFile(urlPath, targetPath, followRedirect = true) {
|
|
554
585
|
const url = new URL(this.buildUrl(urlPath));
|
|
555
586
|
const isHttps = url.protocol === "https:";
|
|
@@ -650,6 +681,72 @@ var SkillHubAPI = class {
|
|
|
650
681
|
async removeSkillsFromPackage(namespace, slug, skills) {
|
|
651
682
|
return (await this.request("DELETE", `/namespaces/${namespace}/packages/${slug}/skills`, { body: { skills } })).body;
|
|
652
683
|
}
|
|
684
|
+
async listPlugins(params) {
|
|
685
|
+
const body = (await this.request("GET", "/plugins", { params })).body;
|
|
686
|
+
return body.data || body;
|
|
687
|
+
}
|
|
688
|
+
async getPlugin(pluginId) {
|
|
689
|
+
const body = (await this.request("GET", `/plugins/${pluginId}`)).body;
|
|
690
|
+
return body.data || body;
|
|
691
|
+
}
|
|
692
|
+
async createPlugin(data) {
|
|
693
|
+
return (await this.request("POST", "/plugins", { body: data })).body;
|
|
694
|
+
}
|
|
695
|
+
async updatePlugin(pluginId, data) {
|
|
696
|
+
return (await this.request("PUT", `/plugins/${pluginId}`, { body: data })).body;
|
|
697
|
+
}
|
|
698
|
+
async deletePlugin(pluginId) {
|
|
699
|
+
return (await this.request("DELETE", `/plugins/${pluginId}`)).body;
|
|
700
|
+
}
|
|
701
|
+
async listPluginVersions(pluginId) {
|
|
702
|
+
const body = (await this.request("GET", `/plugins/${pluginId}/versions`)).body;
|
|
703
|
+
return body.data || body;
|
|
704
|
+
}
|
|
705
|
+
async createPluginVersion(pluginId, data) {
|
|
706
|
+
return (await this.request("POST", `/plugins/${pluginId}/versions`, { body: data })).body;
|
|
707
|
+
}
|
|
708
|
+
async publishPluginVersion(pluginId, versionId) {
|
|
709
|
+
return (await this.request("POST", `/plugins/${pluginId}/versions/${versionId}/publish`)).body;
|
|
710
|
+
}
|
|
711
|
+
async yankPluginVersion(pluginId, versionId, reason) {
|
|
712
|
+
return (await this.request("POST", `/plugins/${pluginId}/versions/${versionId}/yank`, { body: reason ? { changelog: reason } : void 0 })).body;
|
|
713
|
+
}
|
|
714
|
+
async downloadPluginBundle(pluginId, versionId, targetPath) {
|
|
715
|
+
try {
|
|
716
|
+
return await this.downloadFile(`/plugins/${pluginId}/versions/${versionId}/download`, targetPath);
|
|
717
|
+
} catch (error) {
|
|
718
|
+
throw new Error(`Failed to download plugin bundle: ${error.message}`);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
async uploadPlugin(fileBuffer, filename, namespace, options) {
|
|
722
|
+
const form = new FormData();
|
|
723
|
+
form.append("file", new Blob([new Uint8Array(fileBuffer)]), filename);
|
|
724
|
+
form.append("namespace", namespace);
|
|
725
|
+
if (options?.visibility) form.append("visibility", options.visibility);
|
|
726
|
+
if (options?.summary) form.append("summary", options.summary);
|
|
727
|
+
if (options?.changelog) form.append("changelog", options.changelog);
|
|
728
|
+
return (await this.request("POST", "/plugins/upload", { body: form })).body;
|
|
729
|
+
}
|
|
730
|
+
async listMcpServers(params) {
|
|
731
|
+
const body = (await this.request("GET", "/mcp-servers", { params })).body;
|
|
732
|
+
return body.data || body;
|
|
733
|
+
}
|
|
734
|
+
async getMcpServer(mcpId) {
|
|
735
|
+
const body = (await this.request("GET", `/mcp-servers/${mcpId}`)).body;
|
|
736
|
+
return body.data || body;
|
|
737
|
+
}
|
|
738
|
+
async createMcpServer(data) {
|
|
739
|
+
return (await this.request("POST", "/mcp-servers", { body: data })).body;
|
|
740
|
+
}
|
|
741
|
+
async updateMcpServer(mcpId, data) {
|
|
742
|
+
return (await this.request("PUT", `/mcp-servers/${mcpId}`, { body: data })).body;
|
|
743
|
+
}
|
|
744
|
+
async deleteMcpServer(mcpId) {
|
|
745
|
+
return (await this.request("DELETE", `/mcp-servers/${mcpId}`)).body;
|
|
746
|
+
}
|
|
747
|
+
async getMcpConfig(mcpId) {
|
|
748
|
+
return (await this.request("GET", `/mcp-servers/${mcpId}/config`)).body.data?.config ?? "";
|
|
749
|
+
}
|
|
653
750
|
async gitParse(repositoryUrl, branch, directory) {
|
|
654
751
|
const body = {
|
|
655
752
|
repositoryUrl,
|
|
@@ -1352,6 +1449,54 @@ async function cleanAndCreateDirectory(path) {
|
|
|
1352
1449
|
force: true
|
|
1353
1450
|
});
|
|
1354
1451
|
} catch {}
|
|
1452
|
+
await ensureDirectory(path);
|
|
1453
|
+
}
|
|
1454
|
+
function isErrnoException(err) {
|
|
1455
|
+
return err instanceof Error && typeof err.code === "string";
|
|
1456
|
+
}
|
|
1457
|
+
async function classifyPathEntry(path) {
|
|
1458
|
+
try {
|
|
1459
|
+
return (await stat(path)).isDirectory() ? "dir" : "blocker";
|
|
1460
|
+
} catch {
|
|
1461
|
+
try {
|
|
1462
|
+
await lstat(path);
|
|
1463
|
+
return "blocker";
|
|
1464
|
+
} catch {
|
|
1465
|
+
return "missing";
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
async function findBlockingEntry(path) {
|
|
1470
|
+
let current = resolve(path);
|
|
1471
|
+
for (;;) {
|
|
1472
|
+
const kind = await classifyPathEntry(current);
|
|
1473
|
+
if (kind === "blocker") return current;
|
|
1474
|
+
if (kind === "dir") return null;
|
|
1475
|
+
const parent = dirname(current);
|
|
1476
|
+
if (parent === current) return null;
|
|
1477
|
+
current = parent;
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
async function ensureDirectory(path) {
|
|
1481
|
+
try {
|
|
1482
|
+
await mkdir(path, { recursive: true });
|
|
1483
|
+
return;
|
|
1484
|
+
} catch (err) {
|
|
1485
|
+
if (!isErrnoException(err) || err.code !== "ENOTDIR" && err.code !== "EEXIST" && err.code !== "ELOOP" && err.code !== "ENOENT") throw err;
|
|
1486
|
+
}
|
|
1487
|
+
const blockingEntry = await findBlockingEntry(path);
|
|
1488
|
+
if (!blockingEntry) {
|
|
1489
|
+
await mkdir(path, { recursive: true });
|
|
1490
|
+
return;
|
|
1491
|
+
}
|
|
1492
|
+
const backupPath = `${blockingEntry}.bak-${Date.now()}`;
|
|
1493
|
+
try {
|
|
1494
|
+
await rename(blockingEntry, backupPath);
|
|
1495
|
+
} catch (renameErr) {
|
|
1496
|
+
const reason = renameErr instanceof Error ? renameErr.message : String(renameErr);
|
|
1497
|
+
throw new Error(`无法创建目录 ${path}:${blockingEntry} 存在但不是可用目录(可能是文件或失效的链接),且自动移开失败(${reason})。请手动删除或重命名该条目后重试。`);
|
|
1498
|
+
}
|
|
1499
|
+
console.warn(`检测到 ${blockingEntry} 不是可用目录(可能是文件或失效的链接),已重命名为 ${backupPath}`);
|
|
1355
1500
|
await mkdir(path, { recursive: true });
|
|
1356
1501
|
}
|
|
1357
1502
|
async function resolveParentSymlinks(path) {
|
|
@@ -1680,7 +1825,7 @@ async function installBlobSkillForAgent(skill, agentType, options = {}) {
|
|
|
1680
1825
|
error: "无效的技能名称:检测到潜在的路径遍历攻击"
|
|
1681
1826
|
};
|
|
1682
1827
|
try {
|
|
1683
|
-
await
|
|
1828
|
+
await ensureDirectory(agentBase);
|
|
1684
1829
|
await rm(flatSkillPath, {
|
|
1685
1830
|
recursive: true,
|
|
1686
1831
|
force: true
|
|
@@ -4192,10 +4337,10 @@ function parseAddOptions(args) {
|
|
|
4192
4337
|
errors
|
|
4193
4338
|
};
|
|
4194
4339
|
}
|
|
4195
|
-
const RESET$
|
|
4196
|
-
const BOLD$
|
|
4197
|
-
const DIM$
|
|
4198
|
-
const TEXT$
|
|
4340
|
+
const RESET$13 = "\x1B[0m";
|
|
4341
|
+
const BOLD$10 = "\x1B[1m";
|
|
4342
|
+
const DIM$13 = "\x1B[38;5;102m";
|
|
4343
|
+
const TEXT$10 = "\x1B[38;5;145m";
|
|
4199
4344
|
const CYAN$2 = "\x1B[36m";
|
|
4200
4345
|
const SEARCH_API_BASE = process.env.SKILLS_API_URL || "https://skills.sh";
|
|
4201
4346
|
function formatInstalls$1(count) {
|
|
@@ -4284,28 +4429,28 @@ async function runSearchPrompt(initialQuery = "", owner) {
|
|
|
4284
4429
|
if (lastRenderedLines > 0) process.stdout.write(MOVE_UP(lastRenderedLines) + MOVE_TO_COL(1));
|
|
4285
4430
|
process.stdout.write(CLEAR_DOWN);
|
|
4286
4431
|
const lines = [];
|
|
4287
|
-
const cursor = `${BOLD$
|
|
4288
|
-
lines.push(`${TEXT$
|
|
4432
|
+
const cursor = `${BOLD$10}_${RESET$13}`;
|
|
4433
|
+
lines.push(`${TEXT$10}搜索技能:${RESET$13} ${query}${cursor}`);
|
|
4289
4434
|
lines.push("");
|
|
4290
|
-
if (!query || query.length < 2) lines.push(`${DIM$
|
|
4291
|
-
else if (results.length === 0 && loading) lines.push(`${DIM$
|
|
4292
|
-
else if (results.length === 0) lines.push(`${DIM$
|
|
4435
|
+
if (!query || query.length < 2) lines.push(`${DIM$13}输入关键词搜索(最少 2 个字符)${RESET$13}`);
|
|
4436
|
+
else if (results.length === 0 && loading) lines.push(`${DIM$13}搜索中...${RESET$13}`);
|
|
4437
|
+
else if (results.length === 0) lines.push(`${DIM$13}未找到技能${RESET$13}`);
|
|
4293
4438
|
else {
|
|
4294
4439
|
const visible = results.slice(0, 8);
|
|
4295
4440
|
for (let i = 0; i < visible.length; i++) {
|
|
4296
4441
|
const skill = visible[i];
|
|
4297
4442
|
const isSelected = i === selectedIndex;
|
|
4298
|
-
const arrow = isSelected ? `${BOLD$
|
|
4299
|
-
const name = isSelected ? `${BOLD$
|
|
4300
|
-
const source = skill.source ? ` ${DIM$
|
|
4443
|
+
const arrow = isSelected ? `${BOLD$10}>${RESET$13}` : " ";
|
|
4444
|
+
const name = isSelected ? `${BOLD$10}${skill.name}${RESET$13}` : `${TEXT$10}${skill.name}${RESET$13}`;
|
|
4445
|
+
const source = skill.source ? ` ${DIM$13}${skill.source}${RESET$13}` : "";
|
|
4301
4446
|
const installs = formatInstalls$1(skill.installs);
|
|
4302
|
-
const installsBadge = installs ? ` ${CYAN$2}${installs}${RESET$
|
|
4303
|
-
const loadingIndicator = loading && i === 0 ? ` ${DIM$
|
|
4447
|
+
const installsBadge = installs ? ` ${CYAN$2}${installs}${RESET$13}` : "";
|
|
4448
|
+
const loadingIndicator = loading && i === 0 ? ` ${DIM$13}...${RESET$13}` : "";
|
|
4304
4449
|
lines.push(` ${arrow} ${name}${source}${installsBadge}${loadingIndicator}`);
|
|
4305
4450
|
}
|
|
4306
4451
|
}
|
|
4307
4452
|
lines.push("");
|
|
4308
|
-
lines.push(`${DIM$
|
|
4453
|
+
lines.push(`${DIM$13}上下键导航 | 回车选择 | ESC 取消${RESET$13}`);
|
|
4309
4454
|
for (const line of lines) process.stdout.write(line + "\n");
|
|
4310
4455
|
lastRenderedLines = lines.length;
|
|
4311
4456
|
}
|
|
@@ -4402,9 +4547,9 @@ async function runFind(args) {
|
|
|
4402
4547
|
const { query, options: findOptions, errors } = parseFindOptions(args);
|
|
4403
4548
|
const owner = findOptions.owner;
|
|
4404
4549
|
const isNonInteractive = !process.stdin.isTTY;
|
|
4405
|
-
const agentTip = `${DIM$
|
|
4406
|
-
${DIM$
|
|
4407
|
-
${DIM$
|
|
4550
|
+
const agentTip = `${DIM$13}Tip: if running in a coding agent, follow these steps:${RESET$13}
|
|
4551
|
+
${DIM$13} 1) npx skills find [query] [--owner <owner>]${RESET$13}
|
|
4552
|
+
${DIM$13} 2) npx skills add <owner/repo@skill>${RESET$13}`;
|
|
4408
4553
|
if (errors.length > 0) {
|
|
4409
4554
|
for (const error of errors) console.error(error);
|
|
4410
4555
|
console.error("Usage: npx skills find <query> [--owner <owner>]");
|
|
@@ -4415,16 +4560,16 @@ ${DIM$11} 2) npx skills add <owner/repo@skill>${RESET$11}`;
|
|
|
4415
4560
|
String(results.length);
|
|
4416
4561
|
if (results.length === 0) {
|
|
4417
4562
|
const ownerSuffix = owner ? ` from owner "${owner}"` : "";
|
|
4418
|
-
console.log(`${DIM$
|
|
4563
|
+
console.log(`${DIM$13}未找到 "${query}" 的技能${ownerSuffix}${RESET$13}`);
|
|
4419
4564
|
return;
|
|
4420
4565
|
}
|
|
4421
|
-
console.log(`${DIM$
|
|
4566
|
+
console.log(`${DIM$13}安装:${RESET$13} npx skills add <owner/repo@skill>`);
|
|
4422
4567
|
console.log();
|
|
4423
4568
|
for (const skill of results.slice(0, 6)) {
|
|
4424
4569
|
const pkg = skill.source || skill.slug;
|
|
4425
4570
|
const installs = formatInstalls$1(skill.installs);
|
|
4426
|
-
console.log(`${TEXT$
|
|
4427
|
-
console.log(`${DIM$
|
|
4571
|
+
console.log(`${TEXT$10}${pkg}@${skill.name}${RESET$13}${installs ? ` ${CYAN$2}${installs}${RESET$13}` : ""}`);
|
|
4572
|
+
console.log(`${DIM$13}└ https://skills.sh/${skill.slug}${RESET$13}`);
|
|
4428
4573
|
console.log();
|
|
4429
4574
|
}
|
|
4430
4575
|
return;
|
|
@@ -4432,19 +4577,19 @@ ${DIM$11} 2) npx skills add <owner/repo@skill>${RESET$11}`;
|
|
|
4432
4577
|
if (isNonInteractive || await isRunningInAgent()) {
|
|
4433
4578
|
console.log(agentTip);
|
|
4434
4579
|
console.log();
|
|
4435
|
-
console.log(`${DIM$
|
|
4580
|
+
console.log(`${DIM$13}Usage: npx skills find <query> [--owner <owner>]${RESET$13}`);
|
|
4436
4581
|
return;
|
|
4437
4582
|
}
|
|
4438
4583
|
const selected = await runSearchPrompt("", owner);
|
|
4439
4584
|
if (!selected) {
|
|
4440
|
-
console.log(`${DIM$
|
|
4585
|
+
console.log(`${DIM$13}搜索已取消${RESET$13}`);
|
|
4441
4586
|
console.log();
|
|
4442
4587
|
return;
|
|
4443
4588
|
}
|
|
4444
4589
|
const pkg = selected.source || selected.slug;
|
|
4445
4590
|
const skillName = selected.name;
|
|
4446
4591
|
console.log();
|
|
4447
|
-
console.log(`${TEXT$
|
|
4592
|
+
console.log(`${TEXT$10}正在从 ${DIM$13}${pkg}${RESET$13} 安装 ${BOLD$10}${skillName}${RESET$13}...`);
|
|
4448
4593
|
console.log();
|
|
4449
4594
|
const { source, options: addOptions } = parseAddOptions([
|
|
4450
4595
|
pkg,
|
|
@@ -4454,13 +4599,13 @@ ${DIM$11} 2) npx skills add <owner/repo@skill>${RESET$11}`;
|
|
|
4454
4599
|
await runAdd(source, addOptions);
|
|
4455
4600
|
console.log();
|
|
4456
4601
|
const info = getOwnerRepoFromString(pkg);
|
|
4457
|
-
if (info && await isRepoPublic(info.owner, info.repo)) console.log(`${DIM$
|
|
4458
|
-
else console.log(`${DIM$
|
|
4602
|
+
if (info && await isRepoPublic(info.owner, info.repo)) console.log(`${DIM$13}查看技能:${RESET$13} ${TEXT$10}https://skills.sh/${selected.slug}${RESET$13}`);
|
|
4603
|
+
else console.log(`${DIM$13}发现更多技能:${RESET$13} ${TEXT$10}https://skills.sh${RESET$13}`);
|
|
4459
4604
|
console.log();
|
|
4460
4605
|
}
|
|
4461
|
-
const RESET$
|
|
4462
|
-
const BOLD$
|
|
4463
|
-
const DIM$
|
|
4606
|
+
const RESET$12 = "\x1B[0m";
|
|
4607
|
+
const BOLD$9 = "\x1B[1m";
|
|
4608
|
+
const DIM$12 = "\x1B[38;5;102m";
|
|
4464
4609
|
const CYAN$1 = "\x1B[36m";
|
|
4465
4610
|
const YELLOW$4 = "\x1B[33m";
|
|
4466
4611
|
function shortenPath(fullPath, cwd) {
|
|
@@ -4496,8 +4641,8 @@ async function runList(args) {
|
|
|
4496
4641
|
const validAgents = Object.keys(agents);
|
|
4497
4642
|
const invalidAgents = options.agent.filter((a) => !validAgents.includes(a));
|
|
4498
4643
|
if (invalidAgents.length > 0) {
|
|
4499
|
-
console.log(`${YELLOW$4}Invalid agents: ${invalidAgents.join(", ")}${RESET$
|
|
4500
|
-
console.log(`${DIM$
|
|
4644
|
+
console.log(`${YELLOW$4}Invalid agents: ${invalidAgents.join(", ")}${RESET$12}`);
|
|
4645
|
+
console.log(`${DIM$12}Valid agents: ${validAgents.join(", ")}${RESET$12}`);
|
|
4501
4646
|
process.exit(1);
|
|
4502
4647
|
}
|
|
4503
4648
|
agentFilter = options.agent;
|
|
@@ -4524,21 +4669,21 @@ async function runList(args) {
|
|
|
4524
4669
|
console.log("[]");
|
|
4525
4670
|
return;
|
|
4526
4671
|
}
|
|
4527
|
-
console.log(`${DIM$
|
|
4528
|
-
if (scope) console.log(`${DIM$
|
|
4529
|
-
else console.log(`${DIM$
|
|
4672
|
+
console.log(`${DIM$12}未找到${scopeLabel}技能。${RESET$12}`);
|
|
4673
|
+
if (scope) console.log(`${DIM$12}尝试不带 -g 参数列出项目技能${RESET$12}`);
|
|
4674
|
+
else console.log(`${DIM$12}尝试带 -g 参数列出全局技能${RESET$12}`);
|
|
4530
4675
|
return;
|
|
4531
4676
|
}
|
|
4532
4677
|
function printSkill(skill, indent = false, maxNameLength = 0, maxPathLength = 0) {
|
|
4533
4678
|
const prefix = indent ? " " : "";
|
|
4534
4679
|
const shortPath = shortenPath(skill.canonicalPath, cwd);
|
|
4535
4680
|
const agentNames = skill.agents.map((a) => agents[a].displayName);
|
|
4536
|
-
const agentInfo = skill.agents.length > 0 ? formatList$1(agentNames) : `${YELLOW$4}not linked${RESET$
|
|
4681
|
+
const agentInfo = skill.agents.length > 0 ? formatList$1(agentNames) : `${YELLOW$4}not linked${RESET$12}`;
|
|
4537
4682
|
const paddedName = sanitizeMetadata(skill.name).padEnd(maxNameLength);
|
|
4538
4683
|
const paddedPath = shortPath.padEnd(maxPathLength);
|
|
4539
|
-
console.log(`${prefix}${CYAN$1}${paddedName}${RESET$
|
|
4684
|
+
console.log(`${prefix}${CYAN$1}${paddedName}${RESET$12} ${DIM$12}${paddedPath}${RESET$12} ${DIM$12}Agents:${RESET$12} ${agentInfo}`);
|
|
4540
4685
|
}
|
|
4541
|
-
console.log(`${BOLD$
|
|
4686
|
+
console.log(`${BOLD$9}${scopeLabel}技能${RESET$12}`);
|
|
4542
4687
|
console.log();
|
|
4543
4688
|
const groupedSkills = {};
|
|
4544
4689
|
const ungroupedSkills = [];
|
|
@@ -4554,7 +4699,7 @@ async function runList(args) {
|
|
|
4554
4699
|
const sortedGroups = Object.keys(groupedSkills).sort();
|
|
4555
4700
|
for (const group of sortedGroups) {
|
|
4556
4701
|
const title = group.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
4557
|
-
console.log(`${BOLD$
|
|
4702
|
+
console.log(`${BOLD$9}${title}${RESET$12}`);
|
|
4558
4703
|
const skills = groupedSkills[group];
|
|
4559
4704
|
if (skills) {
|
|
4560
4705
|
let maxNameLength = 0;
|
|
@@ -4570,7 +4715,7 @@ async function runList(args) {
|
|
|
4570
4715
|
console.log();
|
|
4571
4716
|
}
|
|
4572
4717
|
if (ungroupedSkills.length > 0) {
|
|
4573
|
-
console.log(`${BOLD$
|
|
4718
|
+
console.log(`${BOLD$9}通用${RESET$12}`);
|
|
4574
4719
|
let maxNameLength = 0;
|
|
4575
4720
|
let maxPathLength = 0;
|
|
4576
4721
|
for (const skill of ungroupedSkills) {
|
|
@@ -4844,10 +4989,10 @@ function buildLocalUpdateSource(entry) {
|
|
|
4844
4989
|
return appendFolderAndRef(source, entry.skillPath, entry.ref);
|
|
4845
4990
|
}
|
|
4846
4991
|
const __dirname$1 = dirname(fileURLToPath(import.meta.url));
|
|
4847
|
-
const RESET$
|
|
4848
|
-
const BOLD$
|
|
4849
|
-
const DIM$
|
|
4850
|
-
const TEXT$
|
|
4992
|
+
const RESET$11 = "\x1B[0m";
|
|
4993
|
+
const BOLD$8 = "\x1B[1m";
|
|
4994
|
+
const DIM$11 = "\x1B[38;5;102m";
|
|
4995
|
+
const TEXT$9 = "\x1B[38;5;145m";
|
|
4851
4996
|
function parseUpdateOptions(args) {
|
|
4852
4997
|
const options = {};
|
|
4853
4998
|
const positional = [];
|
|
@@ -4940,7 +5085,7 @@ function getInstallSource(skill) {
|
|
|
4940
5085
|
function printSkippedSkills(skipped) {
|
|
4941
5086
|
if (skipped.length === 0) return;
|
|
4942
5087
|
console.log();
|
|
4943
|
-
console.log(`${DIM$
|
|
5088
|
+
console.log(`${DIM$11}${skipped.length} 个技能无法自动检查:${RESET$11}`);
|
|
4944
5089
|
const grouped = /* @__PURE__ */ new Map();
|
|
4945
5090
|
for (const skill of skipped) {
|
|
4946
5091
|
const source = getInstallSource(skill);
|
|
@@ -4951,13 +5096,13 @@ function printSkippedSkills(skipped) {
|
|
|
4951
5096
|
for (const [source, skills] of grouped) {
|
|
4952
5097
|
if (skills.length === 1) {
|
|
4953
5098
|
const skill = skills[0];
|
|
4954
|
-
console.log(` ${TEXT$
|
|
5099
|
+
console.log(` ${TEXT$9}•${RESET$11} ${sanitizeMetadata(skill.name)} ${DIM$11}(${skill.reason})${RESET$11}`);
|
|
4955
5100
|
} else {
|
|
4956
5101
|
const reason = skills[0].reason;
|
|
4957
5102
|
const names = skills.map((s) => sanitizeMetadata(s.name)).join(", ");
|
|
4958
|
-
console.log(` ${TEXT$
|
|
5103
|
+
console.log(` ${TEXT$9}•${RESET$11} ${names} ${DIM$11}(${reason})${RESET$11}`);
|
|
4959
5104
|
}
|
|
4960
|
-
console.log(` ${DIM$
|
|
5105
|
+
console.log(` ${DIM$11}更新方法:${TEXT$9}npx skills add ${source} -g -y${RESET$11}`);
|
|
4961
5106
|
}
|
|
4962
5107
|
}
|
|
4963
5108
|
async function getProjectSkillsForUpdate(skillFilter) {
|
|
@@ -4982,13 +5127,13 @@ async function checkAndPromptForDeletions(source, allLockedForSource, lockSkills
|
|
|
4982
5127
|
});
|
|
4983
5128
|
if (deletedSkills.length > 0) {
|
|
4984
5129
|
console.log();
|
|
4985
|
-
console.log(`${DIM$
|
|
4986
|
-
for (const s of deletedSkills) console.log(` ${DIM$
|
|
4987
|
-
if (options.yes || !process.stdin.isTTY) console.log(`${DIM$
|
|
5130
|
+
console.log(`${DIM$11}警告:${RESET$11} 以下来自 ${DIM$11}${source}${RESET$11} 的技能似乎已在源仓库中删除:`);
|
|
5131
|
+
for (const s of deletedSkills) console.log(` ${DIM$11}•${RESET$11} ${s}`);
|
|
5132
|
+
if (options.yes || !process.stdin.isTTY) console.log(`${DIM$11}非交互模式,跳过删除。${RESET$11}`);
|
|
4988
5133
|
else {
|
|
4989
5134
|
const confirmed = await confirm({ message: `是否删除这些已删除技能的本地副本?` });
|
|
4990
5135
|
if (confirmed && !isCancel(confirmed)) for (const s of deletedSkills) {
|
|
4991
|
-
console.log(`${DIM$
|
|
5136
|
+
console.log(`${DIM$11}正在卸载${RESET$11} ${s}...`);
|
|
4992
5137
|
await removeCommand([s], {
|
|
4993
5138
|
yes: true,
|
|
4994
5139
|
global: isGlobal
|
|
@@ -5005,8 +5150,8 @@ async function updateGlobalSkills(options = {}) {
|
|
|
5005
5150
|
let failCount = 0;
|
|
5006
5151
|
if (skillNames.length === 0) {
|
|
5007
5152
|
if (!options.skills) {
|
|
5008
|
-
console.log(`${DIM$
|
|
5009
|
-
console.log(`${DIM$
|
|
5153
|
+
console.log(`${DIM$11}锁文件中没有跟踪的全局技能。${RESET$11}`);
|
|
5154
|
+
console.log(`${DIM$11}安装技能:${RESET$11} ${TEXT$9}npx skills add <package> -g${RESET$11}`);
|
|
5010
5155
|
}
|
|
5011
5156
|
return {
|
|
5012
5157
|
successCount,
|
|
@@ -5047,7 +5192,7 @@ async function updateGlobalSkills(options = {}) {
|
|
|
5047
5192
|
const firstEntry = itemsForSource[0].entry;
|
|
5048
5193
|
const sourceUrl = firstEntry.sourceUrl || firstEntry.source;
|
|
5049
5194
|
let tempDir = null;
|
|
5050
|
-
process.stdout.write(`\r${DIM$
|
|
5195
|
+
process.stdout.write(`\r${DIM$11}Checking skills from source: ${source}${RESET$11}\x1b[K\n`);
|
|
5051
5196
|
if (firstEntry.sourceType === "skillhub") {
|
|
5052
5197
|
const skillhubRef = source.replace(/^skillhub:/, "");
|
|
5053
5198
|
const { parseSkillRef, toCanonicalSlug } = await Promise.resolve().then(() => skill_ref_exports);
|
|
@@ -5057,7 +5202,7 @@ async function updateGlobalSkills(options = {}) {
|
|
|
5057
5202
|
new SkillHubAPI(cfg.registry, cfg.token);
|
|
5058
5203
|
for (const item of itemsForSource) {
|
|
5059
5204
|
const safeName = sanitizeMetadata(item.name);
|
|
5060
|
-
console.log(`${TEXT$
|
|
5205
|
+
console.log(`${TEXT$9}正在更新 ${safeName}...${RESET$11}`);
|
|
5061
5206
|
try {
|
|
5062
5207
|
const parts = skillhubRef.split("/");
|
|
5063
5208
|
const ref = parts.length === 2 ? `@${parts[0]}/${parts[1]}` : skillhubRef;
|
|
@@ -5068,7 +5213,7 @@ async function updateGlobalSkills(options = {}) {
|
|
|
5068
5213
|
});
|
|
5069
5214
|
successCount++;
|
|
5070
5215
|
} catch (error) {
|
|
5071
|
-
console.log(` ${DIM$
|
|
5216
|
+
console.log(` ${DIM$11}✗ ${safeName}: ${error.message}${RESET$11}`);
|
|
5072
5217
|
failCount++;
|
|
5073
5218
|
}
|
|
5074
5219
|
}
|
|
@@ -5078,7 +5223,7 @@ async function updateGlobalSkills(options = {}) {
|
|
|
5078
5223
|
if (firstEntry.sourceType === "github") {
|
|
5079
5224
|
const tree = await fetchRepoTree(source, firstEntry.ref, getGitHubToken);
|
|
5080
5225
|
if (!tree) {
|
|
5081
|
-
console.log(` ${DIM$
|
|
5226
|
+
console.log(` ${DIM$11}✗ Failed to fetch tree for ${source}${RESET$11}`);
|
|
5082
5227
|
continue;
|
|
5083
5228
|
}
|
|
5084
5229
|
const discoveredPaths = findSkillMdPaths(tree);
|
|
@@ -5113,7 +5258,7 @@ async function updateGlobalSkills(options = {}) {
|
|
|
5113
5258
|
});
|
|
5114
5259
|
}
|
|
5115
5260
|
} catch (error) {
|
|
5116
|
-
console.log(` ${DIM$
|
|
5261
|
+
console.log(` ${DIM$11}✗ Failed to check skills from ${source}${RESET$11}`);
|
|
5117
5262
|
} finally {
|
|
5118
5263
|
if (tempDir) await cleanupTempDir(tempDir);
|
|
5119
5264
|
}
|
|
@@ -5121,7 +5266,7 @@ async function updateGlobalSkills(options = {}) {
|
|
|
5121
5266
|
if (checkable.length > 0) process.stdout.write("\r\x1B[K");
|
|
5122
5267
|
const checkedCount = checkable.length + skipped.length;
|
|
5123
5268
|
if (checkable.length === 0 && skipped.length === 0) {
|
|
5124
|
-
if (!options.skills) console.log(`${DIM$
|
|
5269
|
+
if (!options.skills) console.log(`${DIM$11}没有可检查的全局技能。${RESET$11}`);
|
|
5125
5270
|
return {
|
|
5126
5271
|
successCount,
|
|
5127
5272
|
failCount,
|
|
@@ -5137,28 +5282,28 @@ async function updateGlobalSkills(options = {}) {
|
|
|
5137
5282
|
};
|
|
5138
5283
|
}
|
|
5139
5284
|
if (updates.length === 0) {
|
|
5140
|
-
console.log(`${TEXT$
|
|
5285
|
+
console.log(`${TEXT$9}✓ 所有全局技能已是最新${RESET$11}`);
|
|
5141
5286
|
return {
|
|
5142
5287
|
successCount,
|
|
5143
5288
|
failCount,
|
|
5144
5289
|
checkedCount
|
|
5145
5290
|
};
|
|
5146
5291
|
}
|
|
5147
|
-
console.log(`${TEXT$
|
|
5292
|
+
console.log(`${TEXT$9}发现 ${updates.length} 个可更新全局技能${RESET$11}`);
|
|
5148
5293
|
console.log();
|
|
5149
5294
|
for (const update of updates) {
|
|
5150
5295
|
const safeName = sanitizeMetadata(update.name);
|
|
5151
|
-
console.log(`${TEXT$
|
|
5296
|
+
console.log(`${TEXT$9}正在更新 ${safeName}...${RESET$11}`);
|
|
5152
5297
|
const installUrl = buildUpdateInstallSource(update.entry);
|
|
5153
5298
|
if (!installUrl) {
|
|
5154
5299
|
failCount++;
|
|
5155
|
-
console.log(` ${DIM$
|
|
5300
|
+
console.log(` ${DIM$11}✗ Cannot update ${safeName}: lock file is missing sourceUrl for this generic Git source${RESET$11}`);
|
|
5156
5301
|
continue;
|
|
5157
5302
|
}
|
|
5158
5303
|
const cliEntry = join(__dirname$1, "..", "bin", "bfi.mjs");
|
|
5159
5304
|
if (!existsSync(cliEntry)) {
|
|
5160
5305
|
failCount++;
|
|
5161
|
-
console.log(` ${DIM$
|
|
5306
|
+
console.log(` ${DIM$11}✗ Failed to update ${safeName}: CLI entrypoint not found at ${cliEntry}${RESET$11}`);
|
|
5162
5307
|
continue;
|
|
5163
5308
|
}
|
|
5164
5309
|
if (spawnSync(process.execPath, [
|
|
@@ -5177,10 +5322,10 @@ async function updateGlobalSkills(options = {}) {
|
|
|
5177
5322
|
shell: false
|
|
5178
5323
|
}).status === 0) {
|
|
5179
5324
|
successCount++;
|
|
5180
|
-
console.log(` ${TEXT$
|
|
5325
|
+
console.log(` ${TEXT$9}✓${RESET$11} 已更新 ${safeName}`);
|
|
5181
5326
|
} else {
|
|
5182
5327
|
failCount++;
|
|
5183
|
-
console.log(` ${DIM$
|
|
5328
|
+
console.log(` ${DIM$11}✗ 更新 ${safeName} 失败${RESET$11}`);
|
|
5184
5329
|
}
|
|
5185
5330
|
}
|
|
5186
5331
|
printSkippedSkills(skipped);
|
|
@@ -5196,8 +5341,8 @@ async function updateProjectSkills(options = {}) {
|
|
|
5196
5341
|
let failCount = 0;
|
|
5197
5342
|
if (projectSkills.length === 0) {
|
|
5198
5343
|
if (!options.skills) {
|
|
5199
|
-
console.log(`${DIM$
|
|
5200
|
-
console.log(`${DIM$
|
|
5344
|
+
console.log(`${DIM$11}没有可更新的项目技能。${RESET$11}`);
|
|
5345
|
+
console.log(`${DIM$11}Install project skills with${RESET$11} ${TEXT$9}npx skills add <package>${RESET$11}`);
|
|
5201
5346
|
}
|
|
5202
5347
|
return {
|
|
5203
5348
|
successCount,
|
|
@@ -5208,7 +5353,7 @@ async function updateProjectSkills(options = {}) {
|
|
|
5208
5353
|
const updatable = projectSkills.filter((s) => s.entry.skillPath);
|
|
5209
5354
|
const legacy = projectSkills.filter((s) => !s.entry.skillPath);
|
|
5210
5355
|
if (updatable.length === 0) {
|
|
5211
|
-
console.log(`${DIM$
|
|
5356
|
+
console.log(`${DIM$11}No project skills can be updated in place.${RESET$11}`);
|
|
5212
5357
|
printLegacyProjectSkills(legacy);
|
|
5213
5358
|
return {
|
|
5214
5359
|
successCount,
|
|
@@ -5228,8 +5373,8 @@ async function updateProjectSkills(options = {}) {
|
|
|
5228
5373
|
const targetParts = [];
|
|
5229
5374
|
if (hasUniversal) targetParts.push("Universal");
|
|
5230
5375
|
targetParts.push(...targetAgentNames);
|
|
5231
|
-
if (targetParts.length > 0) console.log(`${TEXT$
|
|
5232
|
-
console.log(`${TEXT$
|
|
5376
|
+
if (targetParts.length > 0) console.log(`${TEXT$9}更新目标:${targetParts.join(", ")}${RESET$11}`);
|
|
5377
|
+
console.log(`${TEXT$9}正在刷新 ${updatable.length} 个技能...${RESET$11}`);
|
|
5233
5378
|
console.log();
|
|
5234
5379
|
const bySource = /* @__PURE__ */ new Map();
|
|
5235
5380
|
for (const skill of updatable) {
|
|
@@ -5241,7 +5386,7 @@ async function updateProjectSkills(options = {}) {
|
|
|
5241
5386
|
const localLock = await readLocalLock();
|
|
5242
5387
|
const cliEntry = join(__dirname$1, "..", "bin", "bfi.mjs");
|
|
5243
5388
|
if (!existsSync(cliEntry)) {
|
|
5244
|
-
console.log(`${DIM$
|
|
5389
|
+
console.log(`${DIM$11}✗ CLI 入口未找到 ${cliEntry}${RESET$11}`);
|
|
5245
5390
|
return {
|
|
5246
5391
|
successCount,
|
|
5247
5392
|
failCount: updatable.length,
|
|
@@ -5261,7 +5406,7 @@ async function updateProjectSkills(options = {}) {
|
|
|
5261
5406
|
new SkillHubAPI(cfg.registry, cfg.token);
|
|
5262
5407
|
for (const item of skillsForSource) {
|
|
5263
5408
|
const safeName = sanitizeMetadata(item.name);
|
|
5264
|
-
console.log(`${TEXT$
|
|
5409
|
+
console.log(`${TEXT$9}正在更新 ${safeName}...${RESET$11}`);
|
|
5265
5410
|
try {
|
|
5266
5411
|
const parts = skillhubRef.split("/");
|
|
5267
5412
|
const ref = parts.length === 2 ? `@${parts[0]}/${parts[1]}` : skillhubRef;
|
|
@@ -5272,7 +5417,7 @@ async function updateProjectSkills(options = {}) {
|
|
|
5272
5417
|
});
|
|
5273
5418
|
successCount++;
|
|
5274
5419
|
} catch (error) {
|
|
5275
|
-
console.log(` ${DIM$
|
|
5420
|
+
console.log(` ${DIM$11}✗ ${safeName}: ${error.message}${RESET$11}`);
|
|
5276
5421
|
failCount++;
|
|
5277
5422
|
}
|
|
5278
5423
|
}
|
|
@@ -5283,7 +5428,7 @@ async function updateProjectSkills(options = {}) {
|
|
|
5283
5428
|
let deletedSkills = [];
|
|
5284
5429
|
if (buildLocalUpdateSource(firstEntry) === null) {
|
|
5285
5430
|
failCount += skillsForSource.length;
|
|
5286
|
-
console.log(`${DIM$
|
|
5431
|
+
console.log(`${DIM$11}✗ Cannot update ${source}: skills-lock.json is missing sourceUrl for this generic Git source${RESET$11}`);
|
|
5287
5432
|
continue;
|
|
5288
5433
|
}
|
|
5289
5434
|
try {
|
|
@@ -5293,18 +5438,18 @@ async function updateProjectSkills(options = {}) {
|
|
|
5293
5438
|
});
|
|
5294
5439
|
deletedSkills = await checkAndPromptForDeletions(source, allLockedForSource, localLock.skills, false, options, discoveredPaths);
|
|
5295
5440
|
} catch (error) {
|
|
5296
|
-
console.log(`${DIM$
|
|
5441
|
+
console.log(`${DIM$11}✗ 检查 ${source} 已删除技能失败${RESET$11}`);
|
|
5297
5442
|
} finally {
|
|
5298
5443
|
if (tempDir) await cleanupTempDir(tempDir);
|
|
5299
5444
|
}
|
|
5300
5445
|
const remainingSkills = skillsForSource.filter((s) => !deletedSkills.includes(s.name));
|
|
5301
5446
|
for (const skill of remainingSkills) {
|
|
5302
5447
|
const safeName = sanitizeMetadata(skill.name);
|
|
5303
|
-
console.log(`${TEXT$
|
|
5448
|
+
console.log(`${TEXT$9}正在更新 ${safeName}...${RESET$11}`);
|
|
5304
5449
|
const installUrl = buildLocalUpdateSource(skill.entry);
|
|
5305
5450
|
if (!installUrl) {
|
|
5306
5451
|
failCount++;
|
|
5307
|
-
console.log(` ${DIM$
|
|
5452
|
+
console.log(` ${DIM$11}✗ Cannot update ${safeName}: skills-lock.json is missing sourceUrl for this generic Git source${RESET$11}`);
|
|
5308
5453
|
continue;
|
|
5309
5454
|
}
|
|
5310
5455
|
const subagentArgs = skill.entry.subagents?.length ? ["--subagent", ...skill.entry.subagents.map((s) => s === "" ? "root" : s)] : [];
|
|
@@ -5326,10 +5471,10 @@ async function updateProjectSkills(options = {}) {
|
|
|
5326
5471
|
shell: false
|
|
5327
5472
|
}).status === 0) {
|
|
5328
5473
|
successCount++;
|
|
5329
|
-
console.log(` ${TEXT$
|
|
5474
|
+
console.log(` ${TEXT$9}✓${RESET$11} 已更新 ${safeName}`);
|
|
5330
5475
|
} else {
|
|
5331
5476
|
failCount++;
|
|
5332
|
-
console.log(` ${DIM$
|
|
5477
|
+
console.log(` ${DIM$11}✗ 更新 ${safeName} 失败${RESET$11}`);
|
|
5333
5478
|
}
|
|
5334
5479
|
}
|
|
5335
5480
|
}
|
|
@@ -5343,25 +5488,25 @@ async function updateProjectSkills(options = {}) {
|
|
|
5343
5488
|
function printLegacyProjectSkills(legacy) {
|
|
5344
5489
|
if (legacy.length === 0) return;
|
|
5345
5490
|
console.log();
|
|
5346
|
-
console.log(`${DIM$
|
|
5491
|
+
console.log(`${DIM$11}${legacy.length} project skill(s) cannot be updated automatically (installed before skillPath tracking):${RESET$11}`);
|
|
5347
5492
|
for (const skill of legacy) {
|
|
5348
5493
|
const reinstall = buildLocalUpdateSource(skill.entry);
|
|
5349
|
-
console.log(` ${TEXT$
|
|
5350
|
-
if (reinstall) console.log(` ${DIM$
|
|
5351
|
-
else console.log(` ${DIM$
|
|
5494
|
+
console.log(` ${TEXT$9}•${RESET$11} ${sanitizeMetadata(skill.name)}`);
|
|
5495
|
+
if (reinstall) console.log(` ${DIM$11}刷新方法:${TEXT$9}npx skills add ${reinstall} -y${RESET$11}`);
|
|
5496
|
+
else console.log(` ${DIM$11}To refresh: reinstall using the original full Git URL; this lock entry only has an ambiguous shorthand.${RESET$11}`);
|
|
5352
5497
|
}
|
|
5353
5498
|
}
|
|
5354
5499
|
async function runUpdate(args = []) {
|
|
5355
5500
|
const options = parseUpdateOptions(args);
|
|
5356
5501
|
const scope = await resolveUpdateScope(options);
|
|
5357
|
-
if (options.skills) console.log(`${TEXT$
|
|
5358
|
-
else console.log(`${TEXT$
|
|
5502
|
+
if (options.skills) console.log(`${TEXT$9}正在更新 ${options.skills.join(", ")}...${RESET$11}`);
|
|
5503
|
+
else console.log(`${TEXT$9}正在检查技能更新...${RESET$11}`);
|
|
5359
5504
|
console.log();
|
|
5360
5505
|
let totalSuccess = 0;
|
|
5361
5506
|
let totalFail = 0;
|
|
5362
5507
|
let totalFound = 0;
|
|
5363
5508
|
if (scope === "global" || scope === "both") {
|
|
5364
|
-
if (scope === "both" && !options.skills) console.log(`${BOLD$
|
|
5509
|
+
if (scope === "both" && !options.skills) console.log(`${BOLD$8}全局技能${RESET$11}`);
|
|
5365
5510
|
const { successCount, failCount, checkedCount } = await updateGlobalSkills(options);
|
|
5366
5511
|
totalSuccess += successCount;
|
|
5367
5512
|
totalFail += failCount;
|
|
@@ -5369,17 +5514,17 @@ async function runUpdate(args = []) {
|
|
|
5369
5514
|
if (scope === "both" && !options.skills) console.log();
|
|
5370
5515
|
}
|
|
5371
5516
|
if (scope === "project" || scope === "both") {
|
|
5372
|
-
if (scope === "both" && !options.skills) console.log(`${BOLD$
|
|
5517
|
+
if (scope === "both" && !options.skills) console.log(`${BOLD$8}项目技能${RESET$11}`);
|
|
5373
5518
|
const { successCount, failCount, foundCount } = await updateProjectSkills(options);
|
|
5374
5519
|
totalSuccess += successCount;
|
|
5375
5520
|
totalFail += failCount;
|
|
5376
5521
|
totalFound += foundCount;
|
|
5377
5522
|
}
|
|
5378
|
-
if (options.skills && totalFound === 0) console.log(`${DIM$
|
|
5523
|
+
if (options.skills && totalFound === 0) console.log(`${DIM$11}未找到匹配的已安装技能:${options.skills.join(", ")}${RESET$11}`);
|
|
5379
5524
|
console.log();
|
|
5380
|
-
if (totalSuccess > 0) console.log(`${TEXT$
|
|
5525
|
+
if (totalSuccess > 0) console.log(`${TEXT$9}✓ 已更新 ${totalSuccess} 个技能${RESET$11}`);
|
|
5381
5526
|
if (totalFail > 0) {
|
|
5382
|
-
console.log(`${DIM$
|
|
5527
|
+
console.log(`${DIM$11}更新失败 ${totalFail} 个技能${RESET$11}`);
|
|
5383
5528
|
process.exitCode = 1;
|
|
5384
5529
|
}
|
|
5385
5530
|
String(totalSuccess + totalFail);
|
|
@@ -5831,10 +5976,10 @@ function getOptionValue$3(args, flag) {
|
|
|
5831
5976
|
const idx = args.indexOf(flag);
|
|
5832
5977
|
return idx !== -1 && args[idx + 1] ? args[idx + 1] : void 0;
|
|
5833
5978
|
}
|
|
5834
|
-
const RESET$
|
|
5835
|
-
const BOLD$
|
|
5836
|
-
const DIM$
|
|
5837
|
-
const TEXT$
|
|
5979
|
+
const RESET$10 = "\x1B[0m";
|
|
5980
|
+
const BOLD$7 = "\x1B[1m";
|
|
5981
|
+
const DIM$10 = "\x1B[38;5;102m";
|
|
5982
|
+
const TEXT$8 = "\x1B[38;5;145m";
|
|
5838
5983
|
const CYAN = "\x1B[36m";
|
|
5839
5984
|
const MARKETPLACE_API = process.env.SKILLS_API_URL || "https://skills.sh";
|
|
5840
5985
|
async function runSearch(args, globalOpts) {
|
|
@@ -5842,13 +5987,13 @@ async function runSearch(args, globalOpts) {
|
|
|
5842
5987
|
const namespace = args.includes("--namespace") ? getOptionValue$2(args, "--namespace") : void 0;
|
|
5843
5988
|
const json = args.includes("--json");
|
|
5844
5989
|
if (!query || query.trim() === "") {
|
|
5845
|
-
console.log(`${BOLD$
|
|
5990
|
+
console.log(`${BOLD$7}bfi skills search <关键词> [选项]${RESET$10}`);
|
|
5846
5991
|
console.log();
|
|
5847
|
-
console.log(`${DIM$
|
|
5848
|
-
console.log(` bfi skills search react ${DIM$
|
|
5849
|
-
console.log(` bfi skills search react --marketplace ${DIM$
|
|
5850
|
-
console.log(` bfi skills search react --all ${DIM$
|
|
5851
|
-
console.log(` bfi skills search react --json ${DIM$
|
|
5992
|
+
console.log(`${DIM$10}常用命令:${RESET$10}`);
|
|
5993
|
+
console.log(` bfi skills search react ${DIM$10}搜索 SkillHub${RESET$10}`);
|
|
5994
|
+
console.log(` bfi skills search react --marketplace ${DIM$10}搜索 skills.sh${RESET$10}`);
|
|
5995
|
+
console.log(` bfi skills search react --all ${DIM$10}搜索全部${RESET$10}`);
|
|
5996
|
+
console.log(` bfi skills search react --json ${DIM$10}JSON 输出${RESET$10}`);
|
|
5852
5997
|
return;
|
|
5853
5998
|
}
|
|
5854
5999
|
const useMarketplace = args.includes("--marketplace");
|
|
@@ -5858,9 +6003,9 @@ async function runSearch(args, globalOpts) {
|
|
|
5858
6003
|
return;
|
|
5859
6004
|
}
|
|
5860
6005
|
if (searchAll) {
|
|
5861
|
-
console.log(`${BOLD$
|
|
6006
|
+
console.log(`${BOLD$7}=== SkillHub 注册表 ===${RESET$10}\n`);
|
|
5862
6007
|
await searchSkillHub(query, namespace, globalOpts, json);
|
|
5863
|
-
console.log(`\n${BOLD$
|
|
6008
|
+
console.log(`\n${BOLD$7}=== skills.sh 市场 ===${RESET$10}\n`);
|
|
5864
6009
|
await searchMarketplace(query, json);
|
|
5865
6010
|
return;
|
|
5866
6011
|
}
|
|
@@ -5895,7 +6040,7 @@ async function searchSkillHub(query, namespace, globalOpts, json = false) {
|
|
|
5895
6040
|
count: 0,
|
|
5896
6041
|
results: []
|
|
5897
6042
|
}, null, 2));
|
|
5898
|
-
else console.log(`${DIM$
|
|
6043
|
+
else console.log(`${DIM$10}SkillHub 上未找到 "${query}" 的技能${RESET$10}`);
|
|
5899
6044
|
return;
|
|
5900
6045
|
}
|
|
5901
6046
|
if (json) {
|
|
@@ -5922,15 +6067,15 @@ async function searchSkillHub(query, namespace, globalOpts, json = false) {
|
|
|
5922
6067
|
const desc = skill.summary || skill.description || "";
|
|
5923
6068
|
const canonicalSlug = skill.slug || "";
|
|
5924
6069
|
const displaySlug = canonicalSlug.indexOf("--") > 0 ? canonicalSlug : `global--${canonicalSlug}`;
|
|
5925
|
-
console.log(` ${BOLD$
|
|
5926
|
-
if (desc) console.log(` ${DIM$
|
|
5927
|
-
console.log(` ${TEXT$
|
|
5928
|
-
if (skill.version) console.log(` ${DIM$
|
|
5929
|
-
console.log(` ${DIM$
|
|
6070
|
+
console.log(` ${BOLD$7}${name}${RESET$10}`);
|
|
6071
|
+
if (desc) console.log(` ${DIM$10}${truncate$2(desc, 100)}${RESET$10}`);
|
|
6072
|
+
console.log(` ${TEXT$8}slug: ${displaySlug}${RESET$10}`);
|
|
6073
|
+
if (skill.version) console.log(` ${DIM$10}version: ${skill.version}${RESET$10}`);
|
|
6074
|
+
console.log(` ${DIM$10}安装:${TEXT$8}bfi skills add ${displaySlug}${RESET$10}`);
|
|
5930
6075
|
console.log();
|
|
5931
6076
|
}
|
|
5932
6077
|
} catch (error) {
|
|
5933
|
-
console.error(`${DIM$
|
|
6078
|
+
console.error(`${DIM$10}SkillHub 搜索失败:${error.message}${RESET$10}`);
|
|
5934
6079
|
}
|
|
5935
6080
|
}
|
|
5936
6081
|
async function searchMarketplace(query, json = false) {
|
|
@@ -5947,7 +6092,7 @@ async function searchMarketplace(query, json = false) {
|
|
|
5947
6092
|
count: 0,
|
|
5948
6093
|
results: []
|
|
5949
6094
|
}, null, 2));
|
|
5950
|
-
else console.log(`${DIM$
|
|
6095
|
+
else console.log(`${DIM$10}skills.sh 市场无结果${RESET$10}`);
|
|
5951
6096
|
return;
|
|
5952
6097
|
}
|
|
5953
6098
|
const skills = (await res.json()).skills || [];
|
|
@@ -5957,7 +6102,7 @@ async function searchMarketplace(query, json = false) {
|
|
|
5957
6102
|
count: 0,
|
|
5958
6103
|
results: []
|
|
5959
6104
|
}, null, 2));
|
|
5960
|
-
else console.log(`${DIM$
|
|
6105
|
+
else console.log(`${DIM$10}skills.sh 上未找到 "${query}" 的技能${RESET$10}`);
|
|
5961
6106
|
return;
|
|
5962
6107
|
}
|
|
5963
6108
|
if (json) {
|
|
@@ -5978,15 +6123,15 @@ async function searchMarketplace(query, json = false) {
|
|
|
5978
6123
|
console.log(`skills.sh 上找到 ${skills.length} 个技能:\n`);
|
|
5979
6124
|
for (const skill of skills.slice(0, 12)) {
|
|
5980
6125
|
const installs = formatInstalls(skill.installs);
|
|
5981
|
-
console.log(` ${BOLD$
|
|
5982
|
-
if (skill.source) console.log(` ${DIM$
|
|
5983
|
-
if (installs) console.log(` ${CYAN}${installs}${RESET$
|
|
5984
|
-
console.log(` ${TEXT$
|
|
5985
|
-
if (skill.source) console.log(` ${DIM$
|
|
6126
|
+
console.log(` ${BOLD$7}${skill.name}${RESET$10}`);
|
|
6127
|
+
if (skill.source) console.log(` ${DIM$10}来源:${skill.source}${RESET$10}`);
|
|
6128
|
+
if (installs) console.log(` ${CYAN}${installs}${RESET$10}`);
|
|
6129
|
+
console.log(` ${TEXT$8}https://skills.sh/${skill.id}${RESET$10}`);
|
|
6130
|
+
if (skill.source) console.log(` ${DIM$10}安装:${TEXT$8}bfi skills add github:${skill.source}${RESET$10}`);
|
|
5986
6131
|
console.log();
|
|
5987
6132
|
}
|
|
5988
6133
|
} catch {
|
|
5989
|
-
console.log(`${DIM$
|
|
6134
|
+
console.log(`${DIM$10}skills.sh 市场不可用${RESET$10}`);
|
|
5990
6135
|
}
|
|
5991
6136
|
}
|
|
5992
6137
|
function formatInstalls(count) {
|
|
@@ -6003,13 +6148,13 @@ function getOptionValue$2(args, flag) {
|
|
|
6003
6148
|
const idx = args.indexOf(flag);
|
|
6004
6149
|
return idx !== -1 && args[idx + 1] ? args[idx + 1] : void 0;
|
|
6005
6150
|
}
|
|
6006
|
-
const RESET$
|
|
6007
|
-
const BOLD$
|
|
6008
|
-
const DIM$
|
|
6009
|
-
const TEXT$
|
|
6151
|
+
const RESET$9 = "\x1B[0m";
|
|
6152
|
+
const BOLD$6 = "\x1B[1m";
|
|
6153
|
+
const DIM$9 = "\x1B[38;5;102m";
|
|
6154
|
+
const TEXT$7 = "\x1B[38;5;145m";
|
|
6010
6155
|
const YELLOW$3 = "\x1B[33m";
|
|
6011
|
-
const RED$
|
|
6012
|
-
const GREEN$
|
|
6156
|
+
const RED$5 = "\x1B[31m";
|
|
6157
|
+
const GREEN$6 = "\x1B[32m";
|
|
6013
6158
|
async function runInfo(args, globalOpts) {
|
|
6014
6159
|
const ref = args[0];
|
|
6015
6160
|
if (!ref) {
|
|
@@ -6025,35 +6170,35 @@ async function runInfo(args, globalOpts) {
|
|
|
6025
6170
|
const result = await api.getSkill(canonical);
|
|
6026
6171
|
const skill = result.data || result.skill || result;
|
|
6027
6172
|
if (!skill || !skill.slug) {
|
|
6028
|
-
console.log(`${DIM$
|
|
6173
|
+
console.log(`${DIM$9}未找到技能:${ref}${RESET$9}`);
|
|
6029
6174
|
process.exit(1);
|
|
6030
6175
|
}
|
|
6031
6176
|
console.log();
|
|
6032
|
-
console.log(`${BOLD$
|
|
6033
|
-
console.log(`${DIM$
|
|
6034
|
-
console.log(` ${BOLD$
|
|
6035
|
-
if (skill.summary || skill.description) console.log(` ${BOLD$
|
|
6177
|
+
console.log(`${BOLD$6}${skill.displayName || skill.name || skill.slug}${RESET$9}`);
|
|
6178
|
+
console.log(`${DIM$9}${"─".repeat(48)}${RESET$9}`);
|
|
6179
|
+
console.log(` ${BOLD$6}Slug:${RESET$9} ${TEXT$7}${skill.slug}${RESET$9}`);
|
|
6180
|
+
if (skill.summary || skill.description) console.log(` ${BOLD$6}描述:${RESET$9} ${TEXT$7}${skill.summary || skill.description}${RESET$9}`);
|
|
6036
6181
|
const owner = skill.owner;
|
|
6037
6182
|
if (owner) {
|
|
6038
|
-
console.log(` ${BOLD$
|
|
6039
|
-
if (owner.handle) console.log(` ${BOLD$
|
|
6183
|
+
console.log(` ${BOLD$6}所有者:${RESET$9} ${TEXT$7}${owner.displayName || owner.handle}${RESET$9}`);
|
|
6184
|
+
if (owner.handle) console.log(` ${BOLD$6}句柄:${RESET$9} ${TEXT$7}${owner.handle}${RESET$9}`);
|
|
6040
6185
|
}
|
|
6041
6186
|
const latestVersion = skill.latestVersion;
|
|
6042
6187
|
if (latestVersion) {
|
|
6043
|
-
console.log(` ${BOLD$
|
|
6044
|
-
if (latestVersion.license) console.log(` ${BOLD$
|
|
6045
|
-
if (latestVersion.changelog) console.log(` ${BOLD$
|
|
6188
|
+
console.log(` ${BOLD$6}版本:${RESET$9} ${TEXT$7}${latestVersion.version || "N/A"}${RESET$9}`);
|
|
6189
|
+
if (latestVersion.license) console.log(` ${BOLD$6}许可证:${RESET$9} ${TEXT$7}${latestVersion.license}${RESET$9}`);
|
|
6190
|
+
if (latestVersion.changelog) console.log(` ${BOLD$6}变更日志:${RESET$9} ${DIM$9}${truncate$1(latestVersion.changelog, 80)}${RESET$9}`);
|
|
6046
6191
|
if (latestVersion.createdAt) {
|
|
6047
6192
|
const date = new Date(latestVersion.createdAt).toISOString().split("T")[0];
|
|
6048
|
-
console.log(` ${BOLD$
|
|
6193
|
+
console.log(` ${BOLD$6}发布时间:${RESET$9} ${TEXT$7}${date}${RESET$9}`);
|
|
6049
6194
|
}
|
|
6050
6195
|
}
|
|
6051
6196
|
const stats = skill.stats;
|
|
6052
6197
|
if (stats) {
|
|
6053
6198
|
console.log();
|
|
6054
|
-
console.log(` ${BOLD$
|
|
6055
|
-
if (stats.downloads !== void 0) console.log(` 下载量:${TEXT$
|
|
6056
|
-
if (stats.views !== void 0) console.log(` 浏览量:${TEXT$
|
|
6199
|
+
console.log(` ${BOLD$6}统计:${RESET$9}`);
|
|
6200
|
+
if (stats.downloads !== void 0) console.log(` 下载量:${TEXT$7}${stats.downloads}${RESET$9}`);
|
|
6201
|
+
if (stats.views !== void 0) console.log(` 浏览量:${TEXT$7}${stats.views}${RESET$9}`);
|
|
6057
6202
|
}
|
|
6058
6203
|
const tags = skill.tags;
|
|
6059
6204
|
if (Array.isArray(tags) && tags.length > 0) {
|
|
@@ -6061,28 +6206,28 @@ async function runInfo(args, globalOpts) {
|
|
|
6061
6206
|
const tag = t;
|
|
6062
6207
|
return tag.name || tag;
|
|
6063
6208
|
}).join(", ");
|
|
6064
|
-
console.log(` ${BOLD$
|
|
6209
|
+
console.log(` ${BOLD$6}标签:${RESET$9} ${TEXT$7}${tagList}${RESET$9}`);
|
|
6065
6210
|
}
|
|
6066
6211
|
const moderation = skill.moderation;
|
|
6067
6212
|
if (moderation) {
|
|
6068
6213
|
const verdict = moderation.verdict;
|
|
6069
6214
|
if (verdict === "MALWARE_BLOCKED" || moderation.isMalwareBlocked) {
|
|
6070
6215
|
console.log();
|
|
6071
|
-
console.log(` ${RED$
|
|
6072
|
-
if (moderation.summary) console.log(` ${RED$
|
|
6216
|
+
console.log(` ${RED$5}${BOLD$6}⚠ 已封禁:检测到恶意软件${RESET$9}`);
|
|
6217
|
+
if (moderation.summary) console.log(` ${RED$5}${moderation.summary}${RESET$9}`);
|
|
6073
6218
|
} else if (verdict === "SUSPICIOUS" || moderation.isSuspicious) {
|
|
6074
6219
|
console.log();
|
|
6075
|
-
console.log(` ${YELLOW$3}⚠ 可疑技能${RESET$
|
|
6076
|
-
if (moderation.summary) console.log(` ${YELLOW$3}${moderation.summary}${RESET$
|
|
6077
|
-
} else if (verdict === "SAFE") console.log(` ${BOLD$
|
|
6220
|
+
console.log(` ${YELLOW$3}⚠ 可疑技能${RESET$9}`);
|
|
6221
|
+
if (moderation.summary) console.log(` ${YELLOW$3}${moderation.summary}${RESET$9}`);
|
|
6222
|
+
} else if (verdict === "SAFE") console.log(` ${BOLD$6}安全:${RESET$9} ${GREEN$6}已验证安全${RESET$9}`);
|
|
6078
6223
|
}
|
|
6079
6224
|
if (skill.createdAt) {
|
|
6080
6225
|
const created = new Date(skill.createdAt).toISOString().split("T")[0];
|
|
6081
|
-
console.log(` ${BOLD$
|
|
6226
|
+
console.log(` ${BOLD$6}创建时间:${RESET$9} ${TEXT$7}${created}${RESET$9}`);
|
|
6082
6227
|
}
|
|
6083
6228
|
if (skill.updatedAt) {
|
|
6084
6229
|
const updated = new Date(skill.updatedAt).toISOString().split("T")[0];
|
|
6085
|
-
console.log(` ${BOLD$
|
|
6230
|
+
console.log(` ${BOLD$6}更新时间:${RESET$9} ${TEXT$7}${updated}${RESET$9}`);
|
|
6086
6231
|
}
|
|
6087
6232
|
console.log();
|
|
6088
6233
|
} catch (error) {
|
|
@@ -6095,8 +6240,8 @@ function truncate$1(text, maxLen) {
|
|
|
6095
6240
|
if (text.length <= maxLen) return text;
|
|
6096
6241
|
return text.slice(0, maxLen) + "...";
|
|
6097
6242
|
}
|
|
6098
|
-
const DIM$
|
|
6099
|
-
const RESET$
|
|
6243
|
+
const DIM$8 = "\x1B[38;5;102m";
|
|
6244
|
+
const RESET$8 = "\x1B[0m";
|
|
6100
6245
|
async function runConfig(args, globalOpts) {
|
|
6101
6246
|
switch (args[0]) {
|
|
6102
6247
|
case "get":
|
|
@@ -6191,12 +6336,12 @@ async function handleSet(args, _globalOpts) {
|
|
|
6191
6336
|
}
|
|
6192
6337
|
const isGlobal = args.includes("--global");
|
|
6193
6338
|
setConfigValue(key, value, isGlobal);
|
|
6194
|
-
console.log(`${DIM$
|
|
6339
|
+
console.log(`${DIM$8}✓${RESET$8} 已设置 ${key} = ${value}(${(isGlobal ? "global" : "project") === "global" ? "全局" : "项目"})`);
|
|
6195
6340
|
}
|
|
6196
|
-
const RESET$
|
|
6197
|
-
const BOLD$
|
|
6198
|
-
const DIM$
|
|
6199
|
-
const TEXT$
|
|
6341
|
+
const RESET$7 = "\x1B[0m";
|
|
6342
|
+
const BOLD$5 = "\x1B[1m";
|
|
6343
|
+
const DIM$7 = "\x1B[38;5;102m";
|
|
6344
|
+
const TEXT$6 = "\x1B[38;5;145m";
|
|
6200
6345
|
function formatFileSize(bytes) {
|
|
6201
6346
|
if (bytes < 1024) return `${bytes} B`;
|
|
6202
6347
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
@@ -6223,9 +6368,9 @@ function printDocsHelp() {
|
|
|
6223
6368
|
console.log(" bfi docs info <id> 查看文档处理状态");
|
|
6224
6369
|
console.log();
|
|
6225
6370
|
console.log("示例:");
|
|
6226
|
-
console.log(` ${DIM$
|
|
6227
|
-
console.log(` ${DIM$
|
|
6228
|
-
console.log(` ${DIM$
|
|
6371
|
+
console.log(` ${DIM$7}$ ${RESET$7}bfi docs upload resume.pdf`);
|
|
6372
|
+
console.log(` ${DIM$7}$ ${RESET$7}bfi docs list`);
|
|
6373
|
+
console.log(` ${DIM$7}$ ${RESET$7}bfi docs info 42`);
|
|
6229
6374
|
}
|
|
6230
6375
|
async function handleUpload(args, globalOpts) {
|
|
6231
6376
|
const filePath = args[0];
|
|
@@ -6248,18 +6393,18 @@ async function handleUpload(args, globalOpts) {
|
|
|
6248
6393
|
const api = new SkillHubAPI(registry, token);
|
|
6249
6394
|
const fileName = path$1.basename(resolvedPath);
|
|
6250
6395
|
const fileSizeStr = formatFileSize(stats.size);
|
|
6251
|
-
console.log(`${DIM$
|
|
6396
|
+
console.log(`${DIM$7}正在上传 ${fileName} (${fileSizeStr})...${RESET$7}`);
|
|
6252
6397
|
try {
|
|
6253
6398
|
const fileBuffer = fs.readFileSync(resolvedPath);
|
|
6254
6399
|
const result = await api.uploadDoc(fileBuffer, fileName);
|
|
6255
6400
|
const doc = result.data || result;
|
|
6256
|
-
console.log(`${TEXT$
|
|
6401
|
+
console.log(`${TEXT$6}✓ 上传成功${RESET$7}`);
|
|
6257
6402
|
console.log();
|
|
6258
|
-
if (doc.id) console.log(` ${BOLD$
|
|
6259
|
-
if (doc.status) console.log(` ${BOLD$
|
|
6260
|
-
if (doc.fileName) console.log(` ${BOLD$
|
|
6403
|
+
if (doc.id) console.log(` ${BOLD$5}ID:${RESET$7} ${TEXT$6}${doc.id}${RESET$7}`);
|
|
6404
|
+
if (doc.status) console.log(` ${BOLD$5}状态:${RESET$7} ${TEXT$6}${doc.status}${RESET$7}`);
|
|
6405
|
+
if (doc.fileName) console.log(` ${BOLD$5}文件:${RESET$7} ${TEXT$6}${doc.fileName}${RESET$7}`);
|
|
6261
6406
|
console.log();
|
|
6262
|
-
if (doc.id) console.log(`${DIM$
|
|
6407
|
+
if (doc.id) console.log(`${DIM$7}查看状态:bfi docs info ${doc.id}${RESET$7}`);
|
|
6263
6408
|
} catch (error) {
|
|
6264
6409
|
console.error(`上传失败:${error.message}`);
|
|
6265
6410
|
process.exit(1);
|
|
@@ -6275,14 +6420,14 @@ async function handleList(args, globalOpts) {
|
|
|
6275
6420
|
const data = result.data || result;
|
|
6276
6421
|
const docs = data.items || data.content || data.docs || (Array.isArray(data) ? data : []);
|
|
6277
6422
|
if (!Array.isArray(docs) || docs.length === 0) {
|
|
6278
|
-
console.log(`${DIM$
|
|
6423
|
+
console.log(`${DIM$7}未找到文档。${RESET$7}`);
|
|
6279
6424
|
return;
|
|
6280
6425
|
}
|
|
6281
6426
|
const total = data.total || data.totalElements || result.total || result.totalElements;
|
|
6282
6427
|
console.log();
|
|
6283
|
-
if (total) console.log(`${BOLD$
|
|
6284
|
-
else console.log(`${BOLD$
|
|
6285
|
-
console.log(`${DIM$
|
|
6428
|
+
if (total) console.log(`${BOLD$5}文档${RESET$7}(第 ${page + 1} 页,共 ${total} 条):`);
|
|
6429
|
+
else console.log(`${BOLD$5}文档${RESET$7}(第 ${page + 1} 页,共 ${docs.length} 条结果):`);
|
|
6430
|
+
console.log(`${DIM$7}${"─".repeat(64)}${RESET$7}`);
|
|
6286
6431
|
for (const doc of docs) {
|
|
6287
6432
|
const id = doc.id || "?";
|
|
6288
6433
|
const fileName = doc.fileName || doc.file_name || "unknown";
|
|
@@ -6292,11 +6437,11 @@ async function handleList(args, globalOpts) {
|
|
|
6292
6437
|
const ip = doc.clientIp || "N/A";
|
|
6293
6438
|
const createdAt = doc.createdAt || doc.created_at;
|
|
6294
6439
|
const dateStr = createdAt ? new Date(createdAt).toISOString().split("T")[0] : "N/A";
|
|
6295
|
-
console.log(` ${BOLD$
|
|
6296
|
-
console.log(` ${DIM$
|
|
6440
|
+
console.log(` ${BOLD$5}#${id}${RESET$7} ${TEXT$6}${fileName}${RESET$7}`);
|
|
6441
|
+
console.log(` ${DIM$7}${sizeStr} | ${user} | ${ip} | ${dateStr}${RESET$7}`);
|
|
6297
6442
|
console.log();
|
|
6298
6443
|
}
|
|
6299
|
-
if (total && Number(total) > (page + 1) * size) console.log(`${DIM$
|
|
6444
|
+
if (total && Number(total) > (page + 1) * size) console.log(`${DIM$7}下一页:bfi docs list ${page + 1}${RESET$7}`);
|
|
6300
6445
|
console.log();
|
|
6301
6446
|
} catch (error) {
|
|
6302
6447
|
console.error(`列出文档失败:${error.message}`);
|
|
@@ -6320,43 +6465,43 @@ async function handleInfo(args, globalOpts) {
|
|
|
6320
6465
|
const result = await api.getDoc(id);
|
|
6321
6466
|
const doc = result.data || result;
|
|
6322
6467
|
if (!doc || !doc.id) {
|
|
6323
|
-
console.log(`${DIM$
|
|
6468
|
+
console.log(`${DIM$7}未找到文档:${id}${RESET$7}`);
|
|
6324
6469
|
process.exit(1);
|
|
6325
6470
|
}
|
|
6326
6471
|
const content = doc.content;
|
|
6327
6472
|
const hasContent = typeof content === "string" && content.trim().length > 0;
|
|
6328
6473
|
console.log();
|
|
6329
|
-
console.log(`${BOLD$
|
|
6330
|
-
console.log(`${DIM$
|
|
6331
|
-
if (doc.fileName || doc.file_name) console.log(` ${BOLD$
|
|
6474
|
+
console.log(`${BOLD$5}文档 #${doc.id}${RESET$7}`);
|
|
6475
|
+
console.log(`${DIM$7}${"─".repeat(48)}${RESET$7}`);
|
|
6476
|
+
if (doc.fileName || doc.file_name) console.log(` ${BOLD$5}文件:${RESET$7} ${TEXT$6}${doc.fileName || doc.file_name}${RESET$7}`);
|
|
6332
6477
|
const fileSize = doc.fileSize || doc.file_size;
|
|
6333
|
-
if (fileSize) console.log(` ${BOLD$
|
|
6478
|
+
if (fileSize) console.log(` ${BOLD$5}大小:${RESET$7} ${TEXT$6}${formatFileSize(Number(fileSize))}${RESET$7}`);
|
|
6334
6479
|
const user = doc.userDisplayName || doc.userId;
|
|
6335
|
-
if (user) console.log(` ${BOLD$
|
|
6336
|
-
if (doc.clientIp) console.log(` ${BOLD$
|
|
6480
|
+
if (user) console.log(` ${BOLD$5}上传者:${RESET$7} ${TEXT$6}${user}${RESET$7}`);
|
|
6481
|
+
if (doc.clientIp) console.log(` ${BOLD$5}IP:${RESET$7} ${TEXT$6}${doc.clientIp}${RESET$7}`);
|
|
6337
6482
|
if (doc.status) {
|
|
6338
|
-
const statusColor = doc.status === "completed" || doc.status === "COMPLETED" ? "\x1B[32m" : doc.status === "failed" || doc.status === "FAILED" ? "\x1B[31m" : TEXT$
|
|
6339
|
-
console.log(` ${BOLD$
|
|
6483
|
+
const statusColor = doc.status === "completed" || doc.status === "COMPLETED" ? "\x1B[32m" : doc.status === "failed" || doc.status === "FAILED" ? "\x1B[31m" : TEXT$6;
|
|
6484
|
+
console.log(` ${BOLD$5}状态:${RESET$7} ${statusColor}${doc.status}${RESET$7}`);
|
|
6340
6485
|
}
|
|
6341
6486
|
if (doc.createdAt || doc.created_at) {
|
|
6342
6487
|
const date = new Date(doc.createdAt || doc.created_at).toISOString().replace("T", " ").slice(0, 19);
|
|
6343
|
-
console.log(` ${BOLD$
|
|
6488
|
+
console.log(` ${BOLD$5}创建时间:${RESET$7} ${TEXT$6}${date}${RESET$7}`);
|
|
6344
6489
|
}
|
|
6345
6490
|
if (doc.updatedAt || doc.updated_at) {
|
|
6346
6491
|
const date = new Date(doc.updatedAt || doc.updated_at).toISOString().replace("T", " ").slice(0, 19);
|
|
6347
|
-
console.log(` ${BOLD$
|
|
6492
|
+
console.log(` ${BOLD$5}更新时间:${RESET$7} ${TEXT$6}${date}${RESET$7}`);
|
|
6348
6493
|
}
|
|
6349
6494
|
if (doc.error || doc.errorMessage) {
|
|
6350
6495
|
console.log();
|
|
6351
|
-
console.log(` \x1b[31m${BOLD$
|
|
6352
|
-
console.log(` \x1b[31m${doc.error || doc.errorMessage}${RESET$
|
|
6496
|
+
console.log(` \x1b[31m${BOLD$5}错误:${RESET$7}`);
|
|
6497
|
+
console.log(` \x1b[31m${doc.error || doc.errorMessage}${RESET$7}`);
|
|
6353
6498
|
}
|
|
6354
6499
|
if (hasContent) {
|
|
6355
6500
|
console.log();
|
|
6356
|
-
console.log(` ${BOLD$
|
|
6357
|
-
console.log(`${DIM$
|
|
6358
|
-
for (const line of content.split("\n")) console.log(` ${TEXT$
|
|
6359
|
-
console.log(`${DIM$
|
|
6501
|
+
console.log(` ${BOLD$5}内容:${RESET$7}`);
|
|
6502
|
+
console.log(`${DIM$7}${"─".repeat(48)}${RESET$7}`);
|
|
6503
|
+
for (const line of content.split("\n")) console.log(` ${TEXT$6}${line}${RESET$7}`);
|
|
6504
|
+
console.log(`${DIM$7}${"─".repeat(48)}${RESET$7}`);
|
|
6360
6505
|
}
|
|
6361
6506
|
console.log();
|
|
6362
6507
|
} catch (error) {
|
|
@@ -6365,13 +6510,13 @@ async function handleInfo(args, globalOpts) {
|
|
|
6365
6510
|
process.exit(1);
|
|
6366
6511
|
}
|
|
6367
6512
|
}
|
|
6368
|
-
const RESET$
|
|
6369
|
-
const BOLD$
|
|
6370
|
-
const DIM$
|
|
6371
|
-
const TEXT$
|
|
6372
|
-
const GREEN$
|
|
6513
|
+
const RESET$6 = "\x1B[0m";
|
|
6514
|
+
const BOLD$4 = "\x1B[1m";
|
|
6515
|
+
const DIM$6 = "\x1B[38;5;102m";
|
|
6516
|
+
const TEXT$5 = "\x1B[38;5;145m";
|
|
6517
|
+
const GREEN$5 = "\x1B[32m";
|
|
6373
6518
|
const YELLOW$2 = "\x1B[33m";
|
|
6374
|
-
const RED$
|
|
6519
|
+
const RED$4 = "\x1B[31m";
|
|
6375
6520
|
async function runPublish(args, globalOpts) {
|
|
6376
6521
|
const fromIdx = args.indexOf("--from");
|
|
6377
6522
|
const mode = fromIdx !== -1 ? args[fromIdx + 1] : "dir";
|
|
@@ -6432,23 +6577,23 @@ async function publishFromDir(positionalArgs, api, namespace, visibility) {
|
|
|
6432
6577
|
}
|
|
6433
6578
|
const { data: frontmatter } = parseFrontmatter(fs.readFileSync(skillMdPath, "utf-8"));
|
|
6434
6579
|
const skillName = frontmatter.name || path$1.basename(resolvedDir);
|
|
6435
|
-
console.log(`${DIM$
|
|
6436
|
-
console.log(` ${BOLD$
|
|
6437
|
-
console.log(` ${BOLD$
|
|
6438
|
-
console.log(` ${BOLD$
|
|
6439
|
-
console.log(` ${BOLD$
|
|
6580
|
+
console.log(`${DIM$6}正在从目录发布技能...${RESET$6}`);
|
|
6581
|
+
console.log(` ${BOLD$4}名称:${RESET$6} ${TEXT$5}${skillName}${RESET$6}`);
|
|
6582
|
+
console.log(` ${BOLD$4}命名空间:${RESET$6} ${TEXT$5}${namespace}${RESET$6}`);
|
|
6583
|
+
console.log(` ${BOLD$4}可见性:${RESET$6} ${TEXT$5}${visibility}${RESET$6}`);
|
|
6584
|
+
console.log(` ${BOLD$4}目录:${RESET$6} ${TEXT$5}${resolvedDir}${RESET$6}`);
|
|
6440
6585
|
console.log();
|
|
6441
|
-
console.log(`${DIM$
|
|
6586
|
+
console.log(`${DIM$6}正在创建 zip...${RESET$6}`);
|
|
6442
6587
|
const zipBuffer = createZipFromDir(resolvedDir);
|
|
6443
|
-
console.log(` ${GREEN$
|
|
6444
|
-
console.log(`${DIM$
|
|
6588
|
+
console.log(` ${GREEN$5}Zip 已创建 (${formatSize(zipBuffer.length)})${RESET$6}`);
|
|
6589
|
+
console.log(`${DIM$6}正在上传到 SkillHub...${RESET$6}`);
|
|
6445
6590
|
try {
|
|
6446
6591
|
const res = await api.publishSkill(namespace, zipBuffer, visibility, true);
|
|
6447
|
-
console.log(` ${GREEN$
|
|
6448
|
-
if (res.slug) console.log(` ${BOLD$
|
|
6449
|
-
if (res.url) console.log(` ${BOLD$
|
|
6592
|
+
console.log(` ${GREEN$5}发布成功!${RESET$6}`);
|
|
6593
|
+
if (res.slug) console.log(` ${BOLD$4}Slug:${RESET$6} ${TEXT$5}${res.slug}${RESET$6}`);
|
|
6594
|
+
if (res.url) console.log(` ${BOLD$4}URL:${RESET$6} ${TEXT$5}${res.url}${RESET$6}`);
|
|
6450
6595
|
} catch (error) {
|
|
6451
|
-
console.error(` ${RED$
|
|
6596
|
+
console.error(` ${RED$4}发布失败:${error.message}${RESET$6}`);
|
|
6452
6597
|
process.exit(1);
|
|
6453
6598
|
}
|
|
6454
6599
|
console.log();
|
|
@@ -6465,34 +6610,34 @@ async function publishFromZip(positionalArgs, api, namespace, visibility) {
|
|
|
6465
6610
|
console.error(`文件未找到:${resolvedZip}`);
|
|
6466
6611
|
process.exit(1);
|
|
6467
6612
|
}
|
|
6468
|
-
console.log(`${DIM$
|
|
6613
|
+
console.log(`${DIM$6}正在解压 zip...${RESET$6}`);
|
|
6469
6614
|
const tmpDir = path$1.join(os$1.tmpdir(), `.bfi-publish-${Date.now()}`);
|
|
6470
6615
|
fs.mkdirSync(tmpDir, { recursive: true });
|
|
6471
6616
|
try {
|
|
6472
6617
|
await extractZip(resolvedZip, tmpDir);
|
|
6473
6618
|
const skillMdPath = findSkillMd(tmpDir);
|
|
6474
6619
|
if (!skillMdPath) {
|
|
6475
|
-
console.error(` ${RED$
|
|
6620
|
+
console.error(` ${RED$4}zip 文件中未找到 SKILL.md${RESET$6}`);
|
|
6476
6621
|
process.exit(1);
|
|
6477
6622
|
}
|
|
6478
6623
|
const skillDir = path$1.dirname(skillMdPath);
|
|
6479
|
-
console.log(` ${GREEN$
|
|
6624
|
+
console.log(` ${GREEN$5}在 ${path$1.relative(tmpDir, skillDir) || "."} 中找到 SKILL.md${RESET$6}`);
|
|
6480
6625
|
const { data: frontmatter } = parseFrontmatter(fs.readFileSync(skillMdPath, "utf-8"));
|
|
6481
6626
|
const skillName = frontmatter.name || path$1.basename(skillDir);
|
|
6482
|
-
console.log(` ${BOLD$
|
|
6483
|
-
console.log(` ${BOLD$
|
|
6484
|
-
console.log(` ${BOLD$
|
|
6627
|
+
console.log(` ${BOLD$4}名称:${RESET$6} ${TEXT$5}${skillName}${RESET$6}`);
|
|
6628
|
+
console.log(` ${BOLD$4}命名空间:${RESET$6} ${TEXT$5}${namespace}${RESET$6}`);
|
|
6629
|
+
console.log(` ${BOLD$4}可见性:${RESET$6} ${TEXT$5}${visibility}${RESET$6}`);
|
|
6485
6630
|
console.log();
|
|
6486
|
-
console.log(`${DIM$
|
|
6631
|
+
console.log(`${DIM$6}正在重新打包...${RESET$6}`);
|
|
6487
6632
|
const zipBuffer = createZipFromDir(skillDir);
|
|
6488
|
-
console.log(` ${GREEN$
|
|
6489
|
-
console.log(`${DIM$
|
|
6633
|
+
console.log(` ${GREEN$5}Zip 已创建 (${formatSize(zipBuffer.length)})${RESET$6}`);
|
|
6634
|
+
console.log(`${DIM$6}正在上传到 SkillHub...${RESET$6}`);
|
|
6490
6635
|
const res = await api.publishSkill(namespace, zipBuffer, visibility, true);
|
|
6491
|
-
console.log(` ${GREEN$
|
|
6492
|
-
if (res.slug) console.log(` ${BOLD$
|
|
6493
|
-
if (res.url) console.log(` ${BOLD$
|
|
6636
|
+
console.log(` ${GREEN$5}发布成功!${RESET$6}`);
|
|
6637
|
+
if (res.slug) console.log(` ${BOLD$4}Slug:${RESET$6} ${TEXT$5}${res.slug}${RESET$6}`);
|
|
6638
|
+
if (res.url) console.log(` ${BOLD$4}URL:${RESET$6} ${TEXT$5}${res.url}${RESET$6}`);
|
|
6494
6639
|
} catch (error) {
|
|
6495
|
-
if (error.message && !error.message.includes("Publish failed")) console.error(` ${RED$
|
|
6640
|
+
if (error.message && !error.message.includes("Publish failed")) console.error(` ${RED$4}错误:${error.message}${RESET$6}`);
|
|
6496
6641
|
process.exit(1);
|
|
6497
6642
|
} finally {
|
|
6498
6643
|
try {
|
|
@@ -6521,18 +6666,18 @@ async function publishFromGit(positionalArgs, api, namespace, visibility, yes, b
|
|
|
6521
6666
|
}
|
|
6522
6667
|
const effectiveBranch = branch || parsedRef || "main";
|
|
6523
6668
|
const effectiveSubpath = directory || null;
|
|
6524
|
-
console.log(`${DIM$
|
|
6525
|
-
console.log(` ${BOLD$
|
|
6526
|
-
console.log(` ${BOLD$
|
|
6669
|
+
console.log(`${DIM$6}正在从 Git 仓库发现技能...${RESET$6}`);
|
|
6670
|
+
console.log(` ${BOLD$4}仓库:${RESET$6} ${TEXT$5}${repoUrl}${RESET$6}`);
|
|
6671
|
+
console.log(` ${BOLD$4}分支:${RESET$6} ${TEXT$5}${effectiveBranch}${RESET$6}`);
|
|
6527
6672
|
console.log();
|
|
6528
6673
|
let tmpDir = null;
|
|
6529
6674
|
let skills = [];
|
|
6530
6675
|
try {
|
|
6531
|
-
console.log(`${DIM$
|
|
6676
|
+
console.log(`${DIM$6}正在克隆仓库...${RESET$6}`);
|
|
6532
6677
|
const { cloneRepo } = await Promise.resolve().then(() => git_exports);
|
|
6533
6678
|
tmpDir = await cloneRepo(repoUrl, effectiveBranch);
|
|
6534
|
-
console.log(` ${GREEN$
|
|
6535
|
-
console.log(`${DIM$
|
|
6679
|
+
console.log(` ${GREEN$5}仓库克隆完成${RESET$6}`);
|
|
6680
|
+
console.log(`${DIM$6}正在发现技能...${RESET$6}`);
|
|
6536
6681
|
const { discoverSkills } = await import("./_chunks/skills.mjs").then((n) => n.skills_exports);
|
|
6537
6682
|
skills = (await discoverSkills(tmpDir, effectiveSubpath || void 0)).map((s) => ({
|
|
6538
6683
|
path: s.path,
|
|
@@ -6540,7 +6685,7 @@ async function publishFromGit(positionalArgs, api, namespace, visibility, yes, b
|
|
|
6540
6685
|
description: s.description || ""
|
|
6541
6686
|
}));
|
|
6542
6687
|
} catch (error) {
|
|
6543
|
-
console.error(` ${RED$
|
|
6688
|
+
console.error(` ${RED$4}克隆或发现失败:${error.message}${RESET$6}`);
|
|
6544
6689
|
if (tmpDir) try {
|
|
6545
6690
|
const { cleanupTempDir } = await Promise.resolve().then(() => git_exports);
|
|
6546
6691
|
await cleanupTempDir(tmpDir);
|
|
@@ -6548,7 +6693,7 @@ async function publishFromGit(positionalArgs, api, namespace, visibility, yes, b
|
|
|
6548
6693
|
process.exit(1);
|
|
6549
6694
|
}
|
|
6550
6695
|
if (skills.length === 0) {
|
|
6551
|
-
console.log(`${YELLOW$2}仓库中未发现技能。${RESET$
|
|
6696
|
+
console.log(`${YELLOW$2}仓库中未发现技能。${RESET$6}`);
|
|
6552
6697
|
if (tmpDir) try {
|
|
6553
6698
|
const { cleanupTempDir } = await Promise.resolve().then(() => git_exports);
|
|
6554
6699
|
await cleanupTempDir(tmpDir);
|
|
@@ -6560,7 +6705,7 @@ async function publishFromGit(positionalArgs, api, namespace, visibility, yes, b
|
|
|
6560
6705
|
const filters = skillFilter.split(",").map((s) => s.trim().toLowerCase());
|
|
6561
6706
|
selectedSkills = skills.filter((s) => filters.some((f) => s.name.toLowerCase().includes(f)));
|
|
6562
6707
|
if (selectedSkills.length === 0) {
|
|
6563
|
-
console.log(`${YELLOW$2}未找到匹配 "${skillFilter}" 的技能${RESET$
|
|
6708
|
+
console.log(`${YELLOW$2}未找到匹配 "${skillFilter}" 的技能${RESET$6}`);
|
|
6564
6709
|
console.log(`可用:${skills.map((s) => s.name).join(", ")}`);
|
|
6565
6710
|
try {
|
|
6566
6711
|
fs.rmSync(tmpDir, {
|
|
@@ -6571,12 +6716,12 @@ async function publishFromGit(positionalArgs, api, namespace, visibility, yes, b
|
|
|
6571
6716
|
process.exit(1);
|
|
6572
6717
|
}
|
|
6573
6718
|
}
|
|
6574
|
-
console.log(`\n${BOLD$
|
|
6719
|
+
console.log(`\n${BOLD$4}发现 ${skills.length} 个技能,将发布 ${selectedSkills.length} 个:${RESET$6}\n`);
|
|
6575
6720
|
for (const skill of selectedSkills) {
|
|
6576
6721
|
const relPath = path$1.relative(tmpDir, skill.path) || ".";
|
|
6577
|
-
console.log(` ${BOLD$
|
|
6578
|
-
console.log(` ${DIM$
|
|
6579
|
-
if (skill.description) console.log(` ${DIM$
|
|
6722
|
+
console.log(` ${BOLD$4}${skill.name}${RESET$6}`);
|
|
6723
|
+
console.log(` ${DIM$6}📁 ${relPath.split("/").join(" ▸ ")}${RESET$6}`);
|
|
6724
|
+
if (skill.description) console.log(` ${DIM$6}${truncate(skill.description, 80)}${RESET$6}`);
|
|
6580
6725
|
console.log();
|
|
6581
6726
|
}
|
|
6582
6727
|
if (!yes) {
|
|
@@ -6595,17 +6740,17 @@ async function publishFromGit(positionalArgs, api, namespace, visibility, yes, b
|
|
|
6595
6740
|
const { createZipFromDir } = await Promise.resolve().then(() => zip_exports);
|
|
6596
6741
|
let successCount = 0;
|
|
6597
6742
|
for (const skill of selectedSkills) {
|
|
6598
|
-
console.log(`${DIM$
|
|
6743
|
+
console.log(`${DIM$6}正在发布 ${skill.name}...${RESET$6}`);
|
|
6599
6744
|
try {
|
|
6600
6745
|
const zipBuffer = createZipFromDir(skill.path);
|
|
6601
6746
|
const res = await api.publishSkill(namespace, zipBuffer, visibility, true);
|
|
6602
|
-
console.log(` ${GREEN$
|
|
6747
|
+
console.log(` ${GREEN$5}✓ 已发布:${namespace}--${res.slug || skill.name}${RESET$6}`);
|
|
6603
6748
|
successCount++;
|
|
6604
6749
|
} catch (error) {
|
|
6605
|
-
console.error(` ${RED$
|
|
6750
|
+
console.error(` ${RED$4}✗ ${skill.name}:${error.message}${RESET$6}`);
|
|
6606
6751
|
}
|
|
6607
6752
|
}
|
|
6608
|
-
console.log(`\n${GREEN$
|
|
6753
|
+
console.log(`\n${GREEN$5}发布完成:${successCount}/${selectedSkills.length} 个技能${RESET$6}`);
|
|
6609
6754
|
if (tmpDir) try {
|
|
6610
6755
|
const { cleanupTempDir } = await Promise.resolve().then(() => git_exports);
|
|
6611
6756
|
await cleanupTempDir(tmpDir);
|
|
@@ -6655,11 +6800,11 @@ function truncate(text, maxLen) {
|
|
|
6655
6800
|
return text.slice(0, maxLen) + "...";
|
|
6656
6801
|
}
|
|
6657
6802
|
var package_exports = /* @__PURE__ */ __exportAll({ runPackage: () => runPackage });
|
|
6658
|
-
const RESET$
|
|
6659
|
-
const BOLD$
|
|
6660
|
-
const DIM$
|
|
6661
|
-
const GREEN$
|
|
6662
|
-
const RED$
|
|
6803
|
+
const RESET$5 = "\x1B[0m";
|
|
6804
|
+
const BOLD$3 = "\x1B[1m";
|
|
6805
|
+
const DIM$5 = "\x1B[38;5;102m";
|
|
6806
|
+
const GREEN$4 = "\x1B[32m";
|
|
6807
|
+
const RED$3 = "\x1B[31m";
|
|
6663
6808
|
const YELLOW$1 = "\x1B[33m";
|
|
6664
6809
|
async function runPackage(args, globalOpts) {
|
|
6665
6810
|
const subcommand = args[0];
|
|
@@ -6705,21 +6850,21 @@ function getWorkdir(globalOpts, args) {
|
|
|
6705
6850
|
return globalOpts.workdir || process.cwd();
|
|
6706
6851
|
}
|
|
6707
6852
|
function printPackageHelp() {
|
|
6708
|
-
console.log(`${BOLD$
|
|
6853
|
+
console.log(`${BOLD$3}包命令:${RESET$5}`);
|
|
6709
6854
|
console.log();
|
|
6710
|
-
console.log(`${BOLD$
|
|
6711
|
-
console.log(` ${DIM$
|
|
6712
|
-
console.log(` ${DIM$
|
|
6713
|
-
console.log(` ${DIM$
|
|
6714
|
-
console.log(` ${DIM$
|
|
6715
|
-
console.log(` ${DIM$
|
|
6855
|
+
console.log(`${BOLD$3}本地操作:${RESET$5}`);
|
|
6856
|
+
console.log(` ${DIM$5}bfi skills package list${RESET$5} 列出已安装的包`);
|
|
6857
|
+
console.log(` ${DIM$5}bfi skills package install <ref>${RESET$5} 安装包(含全部技能)`);
|
|
6858
|
+
console.log(` ${DIM$5}bfi skills package uninstall <ref>${RESET$5} 卸载包(含全部技能)`);
|
|
6859
|
+
console.log(` ${DIM$5}bfi skills package update [ref]${RESET$5} 更新包中技能`);
|
|
6860
|
+
console.log(` ${DIM$5}bfi skills package search <query>${RESET$5} 搜索包`);
|
|
6716
6861
|
console.log();
|
|
6717
|
-
console.log(`${BOLD$
|
|
6718
|
-
console.log(` ${DIM$
|
|
6719
|
-
console.log(` ${DIM$
|
|
6720
|
-
console.log(` ${DIM$
|
|
6862
|
+
console.log(`${BOLD$3}服务端管理:${RESET$5}`);
|
|
6863
|
+
console.log(` ${DIM$5}bfi skills package create <slug>${RESET$5} 创建包`);
|
|
6864
|
+
console.log(` ${DIM$5}bfi skills package add-skills <ref> [skills...]${RESET$5} 添加技能到包`);
|
|
6865
|
+
console.log(` ${DIM$5}bfi skills package remove-skills <ref> [skills...]${RESET$5} 从包移除技能`);
|
|
6721
6866
|
console.log();
|
|
6722
|
-
console.log(`${BOLD$
|
|
6867
|
+
console.log(`${BOLD$3}选项:${RESET$5}`);
|
|
6723
6868
|
console.log(` --namespace <ns> 目标命名空间`);
|
|
6724
6869
|
console.log(` --name <name> 显示名称(create 时)`);
|
|
6725
6870
|
console.log(` --description <t> 描述(create 时)`);
|
|
@@ -6736,18 +6881,18 @@ async function handleLocalList(args, globalOpts) {
|
|
|
6736
6881
|
}
|
|
6737
6882
|
if (entries.length === 0) {
|
|
6738
6883
|
console.log("没有已安装的包。");
|
|
6739
|
-
console.log(`使用 ${DIM$
|
|
6884
|
+
console.log(`使用 ${DIM$5}bfi skills package install <ref>${RESET$5} 安装包。`);
|
|
6740
6885
|
return;
|
|
6741
6886
|
}
|
|
6742
6887
|
console.log();
|
|
6743
|
-
console.log(`${BOLD$
|
|
6888
|
+
console.log(`${BOLD$3}已安装的包(${entries.length} 个):${RESET$5}`);
|
|
6744
6889
|
console.log();
|
|
6745
6890
|
for (const [ref, info] of entries) {
|
|
6746
6891
|
const pkg = info;
|
|
6747
6892
|
const skills = lock.skills || {};
|
|
6748
6893
|
const pkgSkills = Object.entries(skills).filter(([, s]) => s.package?.slug === pkg.slug);
|
|
6749
|
-
console.log(` ${BOLD$
|
|
6750
|
-
console.log(` ${DIM$
|
|
6894
|
+
console.log(` ${BOLD$3}${pkg.name || pkg.slug}${RESET$5}`);
|
|
6895
|
+
console.log(` ${DIM$5}命名空间:@${pkg.namespace} | 版本:${pkg.version || "latest"} | 技能数:${pkgSkills.length}${RESET$5}`);
|
|
6751
6896
|
console.log();
|
|
6752
6897
|
}
|
|
6753
6898
|
}
|
|
@@ -6806,25 +6951,25 @@ async function handleInstall(args, api, globalOpts) {
|
|
|
6806
6951
|
const { namespace, slug } = parseSkillRef(pkgRef, globalOpts.namespace);
|
|
6807
6952
|
const isYes = args.includes("-y") || args.includes("--yes") || globalOpts.yes;
|
|
6808
6953
|
const isGlobal = args.includes("-g") || args.includes("--global");
|
|
6809
|
-
console.log(`${DIM$
|
|
6954
|
+
console.log(`${DIM$5}正在获取包信息...${RESET$5}`);
|
|
6810
6955
|
let pkgInfo;
|
|
6811
6956
|
try {
|
|
6812
6957
|
pkgInfo = await api.getPackage(namespace, slug);
|
|
6813
6958
|
pkgInfo = pkgInfo.data || pkgInfo;
|
|
6814
6959
|
} catch (error) {
|
|
6815
|
-
console.error(`${RED$
|
|
6960
|
+
console.error(`${RED$3}获取包信息失败:${error.message}${RESET$5}`);
|
|
6816
6961
|
process.exit(1);
|
|
6817
6962
|
}
|
|
6818
6963
|
const skills = pkgInfo.skills || [];
|
|
6819
6964
|
if (skills.length === 0) {
|
|
6820
|
-
console.log(`${YELLOW$1}包中没有技能。${RESET$
|
|
6965
|
+
console.log(`${YELLOW$1}包中没有技能。${RESET$5}`);
|
|
6821
6966
|
return;
|
|
6822
6967
|
}
|
|
6823
|
-
console.log(`${BOLD$
|
|
6968
|
+
console.log(`${BOLD$3}${pkgInfo.name || slug}${RESET$5}(${skills.length} 个技能):`);
|
|
6824
6969
|
console.log();
|
|
6825
6970
|
for (const skill of skills) {
|
|
6826
6971
|
const ref = `@${skill.namespace || namespace}/${skill.slug}`;
|
|
6827
|
-
console.log(` ${DIM$
|
|
6972
|
+
console.log(` ${DIM$5}• ${skill.displayName || skill.slug}${RESET$5} ${DIM$5}${ref}${RESET$5}`);
|
|
6828
6973
|
}
|
|
6829
6974
|
console.log();
|
|
6830
6975
|
let targetAgents;
|
|
@@ -6837,8 +6982,8 @@ async function handleInstall(args, api, globalOpts) {
|
|
|
6837
6982
|
targetAgents = ensureUniversalAgents(installed.length > 0 ? installed : validAgents);
|
|
6838
6983
|
installGlobally = isGlobal;
|
|
6839
6984
|
installMode = "symlink";
|
|
6840
|
-
console.log(`${DIM$
|
|
6841
|
-
console.log(`${DIM$
|
|
6985
|
+
console.log(`${DIM$5}安装到:${targetAgents.map((a) => agents[a].displayName).join(", ")}${RESET$5}`);
|
|
6986
|
+
console.log(`${DIM$5}范围:${installGlobally ? "全局" : "项目"} | 方式:${installMode === "symlink" ? "符号链接" : "复制"}${RESET$5}`);
|
|
6842
6987
|
console.log();
|
|
6843
6988
|
} else {
|
|
6844
6989
|
const spinner$1 = spinner();
|
|
@@ -6859,7 +7004,7 @@ async function handleInstall(args, api, globalOpts) {
|
|
|
6859
7004
|
}
|
|
6860
7005
|
if (useEve) {
|
|
6861
7006
|
targetAgents = ["eve"];
|
|
6862
|
-
console.log(`${DIM$
|
|
7007
|
+
console.log(`${DIM$5}安装到:eve agent${RESET$5}`);
|
|
6863
7008
|
} else {
|
|
6864
7009
|
const selected = await selectAgentsInteractive({ global: false });
|
|
6865
7010
|
if (isCancel(selected)) {
|
|
@@ -6881,7 +7026,7 @@ async function handleInstall(args, api, globalOpts) {
|
|
|
6881
7026
|
} else if (installedAgents.length === 1) {
|
|
6882
7027
|
targetAgents = ensureUniversalAgents(installedAgents);
|
|
6883
7028
|
const firstAgent = installedAgents[0];
|
|
6884
|
-
console.log(`${DIM$
|
|
7029
|
+
console.log(`${DIM$5}安装到:${agents[firstAgent].displayName}${RESET$5}`);
|
|
6885
7030
|
} else {
|
|
6886
7031
|
const selected = await selectAgentsInteractive({ global: false });
|
|
6887
7032
|
if (isCancel(selected)) {
|
|
@@ -6998,13 +7143,13 @@ async function handleInstall(args, api, globalOpts) {
|
|
|
6998
7143
|
if (targetAgents.includes("eve")) runAddBaseOpts.subagent = eveSubagentTargets.map((s) => s ?? "root");
|
|
6999
7144
|
for (const skill of skills) {
|
|
7000
7145
|
const skillRef = `@${skill.namespace || namespace}/${skill.slug}`;
|
|
7001
|
-
console.log(` ${DIM$
|
|
7146
|
+
console.log(` ${DIM$5}正在安装 ${skillRef}...${RESET$5}`);
|
|
7002
7147
|
try {
|
|
7003
7148
|
await runAdd([skillRef], runAddBaseOpts);
|
|
7004
|
-
console.log(` ${GREEN$
|
|
7149
|
+
console.log(` ${GREEN$4}✓ ${skill.slug}${RESET$5}`);
|
|
7005
7150
|
installedSkills.push(skill.slug);
|
|
7006
7151
|
} catch (error) {
|
|
7007
|
-
console.log(` ${RED$
|
|
7152
|
+
console.log(` ${RED$3}✗ ${skill.slug}:${error.message}${RESET$5}`);
|
|
7008
7153
|
}
|
|
7009
7154
|
}
|
|
7010
7155
|
const cwd = getWorkdir(globalOpts, args);
|
|
@@ -7031,7 +7176,7 @@ async function handleInstall(args, api, globalOpts) {
|
|
|
7031
7176
|
}
|
|
7032
7177
|
saveLockfile(cwd, lock);
|
|
7033
7178
|
console.log();
|
|
7034
|
-
console.log(`${GREEN$
|
|
7179
|
+
console.log(`${GREEN$4}✓ 包安装完成${RESET$5}`);
|
|
7035
7180
|
}
|
|
7036
7181
|
async function handleUninstall(args, globalOpts) {
|
|
7037
7182
|
const pkgRef = args[0];
|
|
@@ -7044,7 +7189,7 @@ async function handleUninstall(args, globalOpts) {
|
|
|
7044
7189
|
const cwd = getWorkdir(globalOpts, args);
|
|
7045
7190
|
const lock = loadLockfile(cwd);
|
|
7046
7191
|
if (!lock.packages || !lock.packages[pkgKey]) {
|
|
7047
|
-
console.log(`${YELLOW$1}包未安装:${pkgKey}${RESET$
|
|
7192
|
+
console.log(`${YELLOW$1}包未安装:${pkgKey}${RESET$5}`);
|
|
7048
7193
|
return;
|
|
7049
7194
|
}
|
|
7050
7195
|
const skills = lock.skills || {};
|
|
@@ -7065,7 +7210,7 @@ async function handleUninstall(args, globalOpts) {
|
|
|
7065
7210
|
delete lock.packages[pkgKey];
|
|
7066
7211
|
const { saveLockfile } = await Promise.resolve().then(() => config_exports);
|
|
7067
7212
|
saveLockfile(cwd, lock);
|
|
7068
|
-
console.log(`${GREEN$
|
|
7213
|
+
console.log(`${GREEN$4}✓ 已卸载包,移除 ${removed} 个技能${RESET$5}`);
|
|
7069
7214
|
}
|
|
7070
7215
|
async function handleUpdate(args, api, globalOpts) {
|
|
7071
7216
|
const pkgRef = args[0];
|
|
@@ -7075,7 +7220,7 @@ async function handleUpdate(args, api, globalOpts) {
|
|
|
7075
7220
|
if (pkgRef) {
|
|
7076
7221
|
const { namespace, slug } = parseSkillRef(pkgRef, globalOpts.namespace);
|
|
7077
7222
|
if (!pkgs[`@${namespace}/${slug}`]) {
|
|
7078
|
-
console.log(`${YELLOW$1}包未安装:@${namespace}/${slug}${RESET$
|
|
7223
|
+
console.log(`${YELLOW$1}包未安装:@${namespace}/${slug}${RESET$5}`);
|
|
7079
7224
|
return;
|
|
7080
7225
|
}
|
|
7081
7226
|
packagesToUpdate.push({
|
|
@@ -7090,11 +7235,11 @@ async function handleUpdate(args, api, globalOpts) {
|
|
|
7090
7235
|
console.log("没有已安装的包需要更新。");
|
|
7091
7236
|
return;
|
|
7092
7237
|
}
|
|
7093
|
-
console.log(`${DIM$
|
|
7238
|
+
console.log(`${DIM$5}正在更新 ${packagesToUpdate.length} 个包...${RESET$5}`);
|
|
7094
7239
|
for (const pkg of packagesToUpdate) try {
|
|
7095
7240
|
const pkgInfo = await api.getPackage(pkg.namespace, pkg.slug);
|
|
7096
7241
|
const skills = (pkgInfo.data || pkgInfo).skills || [];
|
|
7097
|
-
console.log(` ${DIM$
|
|
7242
|
+
console.log(` ${DIM$5}更新 @${pkg.namespace}/${pkg.slug}(${skills.length} 个技能)...${RESET$5}`);
|
|
7098
7243
|
for (const skill of skills) {
|
|
7099
7244
|
const skillRef = skill.namespace ? `@${skill.namespace}/${skill.slug}` : skill.slug;
|
|
7100
7245
|
const { runAdd } = await Promise.resolve().then(() => add_exports);
|
|
@@ -7106,9 +7251,9 @@ async function handleUpdate(args, api, globalOpts) {
|
|
|
7106
7251
|
} catch {}
|
|
7107
7252
|
}
|
|
7108
7253
|
} catch (error) {
|
|
7109
|
-
console.log(` ${RED$
|
|
7254
|
+
console.log(` ${RED$3}✗ @${pkg.namespace}/${pkg.slug}:${error.message}${RESET$5}`);
|
|
7110
7255
|
}
|
|
7111
|
-
console.log(`${GREEN$
|
|
7256
|
+
console.log(`${GREEN$4}✓ 包更新完成${RESET$5}`);
|
|
7112
7257
|
}
|
|
7113
7258
|
async function handleSearch(args, api, globalOpts) {
|
|
7114
7259
|
const query = args.filter((a) => !a.startsWith("--")).join(" ") || void 0;
|
|
@@ -7128,11 +7273,11 @@ async function handleSearch(args, api, globalOpts) {
|
|
|
7128
7273
|
}, null, 2));
|
|
7129
7274
|
return;
|
|
7130
7275
|
}
|
|
7131
|
-
console.log(`\n${BOLD$
|
|
7276
|
+
console.log(`\n${BOLD$3}找到 ${skills.length} 个相关技能(按命名空间分组):${RESET$5}\n`);
|
|
7132
7277
|
for (const [ns, sk] of namespaces) {
|
|
7133
|
-
console.log(` ${BOLD$
|
|
7134
|
-
for (const s of sk.slice(0, 5)) console.log(` ${DIM$
|
|
7135
|
-
if (sk.length > 5) console.log(` ${DIM$
|
|
7278
|
+
console.log(` ${BOLD$3}@${ns}${RESET$5}(${sk.length} 个技能)`);
|
|
7279
|
+
for (const s of sk.slice(0, 5)) console.log(` ${DIM$5}- ${s.displayName || s.name} (${s.slug})${RESET$5}`);
|
|
7280
|
+
if (sk.length > 5) console.log(` ${DIM$5}... 还有 ${sk.length - 5} 个${RESET$5}`);
|
|
7136
7281
|
console.log();
|
|
7137
7282
|
}
|
|
7138
7283
|
} else {
|
|
@@ -7171,11 +7316,11 @@ async function handleSearch(args, api, globalOpts) {
|
|
|
7171
7316
|
};
|
|
7172
7317
|
const maxName = Math.max(...allPackages.map((p) => dw(p.displayName || p.name || p.slug || "")), 4);
|
|
7173
7318
|
const maxSlug = Math.max(...allPackages.map((p) => dw(`@${p._namespace}/${p.slug}`)), 4);
|
|
7174
|
-
console.log(`\n${BOLD$
|
|
7319
|
+
console.log(`\n${BOLD$3}所有技能包(${allPackages.length} 个):${RESET$5}\n`);
|
|
7175
7320
|
for (const pkg of allPackages) {
|
|
7176
7321
|
const name = pad(pkg.displayName || pkg.name || pkg.slug || "", maxName);
|
|
7177
7322
|
const ref = pad(`@${pkg._namespace}/${pkg.slug}`, maxSlug);
|
|
7178
|
-
console.log(` ${BOLD$
|
|
7323
|
+
console.log(` ${BOLD$3}${name}${RESET$5} ${DIM$5}${ref} ${pkg.visibility === "PUBLIC" ? "公开" : "私有"}${RESET$5}`);
|
|
7179
7324
|
}
|
|
7180
7325
|
}
|
|
7181
7326
|
}
|
|
@@ -7196,11 +7341,11 @@ async function handleCreate(args, api, globalOpts) {
|
|
|
7196
7341
|
description: description || "",
|
|
7197
7342
|
visibility: visibility.toUpperCase()
|
|
7198
7343
|
});
|
|
7199
|
-
console.log(`${GREEN$
|
|
7344
|
+
console.log(`${GREEN$4}✓ 已创建包 ${toSkillRefString(namespace, slug)}${RESET$5}`);
|
|
7200
7345
|
const pkg = result.package || result.data || result;
|
|
7201
|
-
if (pkg) console.log(` ${DIM$
|
|
7346
|
+
if (pkg) console.log(` ${DIM$5}名称:${pkg.name || slug} | 可见性:${pkg.visibility || visibility}${RESET$5}`);
|
|
7202
7347
|
} catch (error) {
|
|
7203
|
-
console.error(`${RED$
|
|
7348
|
+
console.error(`${RED$3}创建包失败:${error.message}${RESET$5}`);
|
|
7204
7349
|
process.exit(1);
|
|
7205
7350
|
}
|
|
7206
7351
|
}
|
|
@@ -7226,11 +7371,11 @@ async function handleAddSkills(args, api, globalOpts) {
|
|
|
7226
7371
|
};
|
|
7227
7372
|
});
|
|
7228
7373
|
if (skills.length === 0) {
|
|
7229
|
-
console.log(`${YELLOW$1}命名空间 "${namespace}" 中没有技能。${RESET$
|
|
7374
|
+
console.log(`${YELLOW$1}命名空间 "${namespace}" 中没有技能。${RESET$5}`);
|
|
7230
7375
|
return;
|
|
7231
7376
|
}
|
|
7232
7377
|
} catch (error) {
|
|
7233
|
-
console.error(`${RED$
|
|
7378
|
+
console.error(`${RED$3}获取技能列表失败:${error.message}${RESET$5}`);
|
|
7234
7379
|
process.exit(1);
|
|
7235
7380
|
}
|
|
7236
7381
|
else skills = skillRefs.map((ref) => {
|
|
@@ -7242,9 +7387,9 @@ async function handleAddSkills(args, api, globalOpts) {
|
|
|
7242
7387
|
});
|
|
7243
7388
|
try {
|
|
7244
7389
|
await api.addSkillsToPackage(namespace, slug, skills);
|
|
7245
|
-
console.log(`${GREEN$
|
|
7390
|
+
console.log(`${GREEN$4}✓ 已添加 ${skills.length} 个技能到 ${toSkillRefString(namespace, slug)}${RESET$5}`);
|
|
7246
7391
|
} catch (error) {
|
|
7247
|
-
console.error(`${RED$
|
|
7392
|
+
console.error(`${RED$3}添加失败:${error.message}${RESET$5}`);
|
|
7248
7393
|
process.exit(1);
|
|
7249
7394
|
}
|
|
7250
7395
|
}
|
|
@@ -7269,7 +7414,7 @@ async function handleRemoveSkills(args, api, globalOpts) {
|
|
|
7269
7414
|
return;
|
|
7270
7415
|
}
|
|
7271
7416
|
} catch (error) {
|
|
7272
|
-
console.error(`${RED$
|
|
7417
|
+
console.error(`${RED$3}获取包信息失败:${error.message}${RESET$5}`);
|
|
7273
7418
|
process.exit(1);
|
|
7274
7419
|
}
|
|
7275
7420
|
else skills = skillRefs.map((ref) => {
|
|
@@ -7281,9 +7426,9 @@ async function handleRemoveSkills(args, api, globalOpts) {
|
|
|
7281
7426
|
});
|
|
7282
7427
|
try {
|
|
7283
7428
|
await api.removeSkillsFromPackage(namespace, slug, skills);
|
|
7284
|
-
console.log(`${GREEN$
|
|
7429
|
+
console.log(`${GREEN$4}✓ 已从包移除 ${skills.length} 个技能${RESET$5}`);
|
|
7285
7430
|
} catch (error) {
|
|
7286
|
-
console.error(`${RED$
|
|
7431
|
+
console.error(`${RED$3}移除失败:${error.message}${RESET$5}`);
|
|
7287
7432
|
process.exit(1);
|
|
7288
7433
|
}
|
|
7289
7434
|
}
|
|
@@ -7302,19 +7447,19 @@ async function handleServerInfo(args, api, globalOpts) {
|
|
|
7302
7447
|
console.log(JSON.stringify(pkg, null, 2));
|
|
7303
7448
|
return;
|
|
7304
7449
|
}
|
|
7305
|
-
console.log(`\n${BOLD$
|
|
7306
|
-
console.log(`${DIM$
|
|
7307
|
-
console.log(` ${BOLD$
|
|
7308
|
-
console.log(` ${BOLD$
|
|
7309
|
-
console.log(` ${BOLD$
|
|
7450
|
+
console.log(`\n${BOLD$3}${pkg.displayName || pkg.name || slug}${RESET$5}`);
|
|
7451
|
+
console.log(`${DIM$5}${"=".repeat(48)}${RESET$5}`);
|
|
7452
|
+
console.log(` ${BOLD$3}Slug:${RESET$5} ${toCanonicalSlug(namespace, slug)}`);
|
|
7453
|
+
console.log(` ${BOLD$3}命名空间:${RESET$5} @${namespace}`);
|
|
7454
|
+
console.log(` ${BOLD$3}可见性:${RESET$5} ${pkg.visibility || "PRIVATE"}`);
|
|
7310
7455
|
const skills = pkg.skills || [];
|
|
7311
7456
|
if (skills.length > 0) {
|
|
7312
|
-
console.log(`\n ${BOLD$
|
|
7313
|
-
for (const s of skills) console.log(` ${DIM$
|
|
7457
|
+
console.log(`\n ${BOLD$3}技能(${skills.length} 个):${RESET$5}`);
|
|
7458
|
+
for (const s of skills) console.log(` ${DIM$5}- ${s.displayName || s.slug} (${s.namespace || namespace}--${s.slug})${RESET$5}`);
|
|
7314
7459
|
}
|
|
7315
7460
|
console.log();
|
|
7316
7461
|
} catch (error) {
|
|
7317
|
-
console.error(`${RED$
|
|
7462
|
+
console.error(`${RED$3}获取包信息失败:${error.message}${RESET$5}`);
|
|
7318
7463
|
process.exit(1);
|
|
7319
7464
|
}
|
|
7320
7465
|
}
|
|
@@ -7322,6 +7467,418 @@ function getOptionValue(args, flag) {
|
|
|
7322
7467
|
const idx = args.indexOf(flag);
|
|
7323
7468
|
return idx !== -1 && args[idx + 1] ? args[idx + 1] : void 0;
|
|
7324
7469
|
}
|
|
7470
|
+
const RESET$4 = "\x1B[0m";
|
|
7471
|
+
const BOLD$2 = "\x1B[1m";
|
|
7472
|
+
const DIM$4 = "\x1B[38;5;102m";
|
|
7473
|
+
const TEXT$4 = "\x1B[38;5;145m";
|
|
7474
|
+
const GREEN$3 = "\x1B[32m";
|
|
7475
|
+
const RED$2 = "\x1B[31m";
|
|
7476
|
+
async function runPlugin(args, globalOpts) {
|
|
7477
|
+
const sub = args[0];
|
|
7478
|
+
const subArgs = args.slice(1);
|
|
7479
|
+
switch (sub) {
|
|
7480
|
+
case "create":
|
|
7481
|
+
case "new":
|
|
7482
|
+
await createScaffold(subArgs);
|
|
7483
|
+
break;
|
|
7484
|
+
case "validate":
|
|
7485
|
+
await validate(subArgs);
|
|
7486
|
+
break;
|
|
7487
|
+
case "publish":
|
|
7488
|
+
await publish(subArgs, globalOpts);
|
|
7489
|
+
break;
|
|
7490
|
+
case "list":
|
|
7491
|
+
case "ls":
|
|
7492
|
+
await list$1(subArgs, globalOpts);
|
|
7493
|
+
break;
|
|
7494
|
+
case "info":
|
|
7495
|
+
await info$1(subArgs, globalOpts);
|
|
7496
|
+
break;
|
|
7497
|
+
case "install":
|
|
7498
|
+
await install(subArgs, globalOpts);
|
|
7499
|
+
break;
|
|
7500
|
+
case "--help":
|
|
7501
|
+
case "-h":
|
|
7502
|
+
case void 0:
|
|
7503
|
+
showHelp$2();
|
|
7504
|
+
break;
|
|
7505
|
+
default:
|
|
7506
|
+
console.log(`未知 plugin 命令:${sub}`);
|
|
7507
|
+
showHelp$2();
|
|
7508
|
+
}
|
|
7509
|
+
}
|
|
7510
|
+
function showHelp$2() {
|
|
7511
|
+
console.log();
|
|
7512
|
+
console.log(`${BOLD$2}bfi plugin <command>${RESET$4} — Claude Code plugin 管理`);
|
|
7513
|
+
console.log();
|
|
7514
|
+
console.log(`${BOLD$2}Commands:${RESET$4}`);
|
|
7515
|
+
console.log(` create <name> Scaffold a plugin directory (skills/, agents/, hooks/, mcp/)`);
|
|
7516
|
+
console.log(` validate [path] Validate plugin.json and directory layout`);
|
|
7517
|
+
console.log(` publish <path> Publish a plugin to SkillHub`);
|
|
7518
|
+
console.log(` list, ls List plugins on SkillHub`);
|
|
7519
|
+
console.log(` info <id> Show plugin details`);
|
|
7520
|
+
console.log(` install <ref> Install a plugin (slug, plugin id, or local .zip)`);
|
|
7521
|
+
console.log();
|
|
7522
|
+
}
|
|
7523
|
+
async function createScaffold(args) {
|
|
7524
|
+
const name = args.find((a, i) => i === 0 || args[i - 1] !== "--with" && !a.startsWith("-"));
|
|
7525
|
+
if (!name || name.startsWith("-")) {
|
|
7526
|
+
console.error("用法:bfi plugin create <name> [--with skills agents hooks mcp]");
|
|
7527
|
+
process.exit(1);
|
|
7528
|
+
}
|
|
7529
|
+
const withIdx = args.indexOf("--with");
|
|
7530
|
+
const components = withIdx !== -1 ? args.slice(withIdx + 1) : [];
|
|
7531
|
+
const target = path$1.resolve(name);
|
|
7532
|
+
if (fs.existsSync(path$1.join(target, ".claude-plugin", "plugin.json"))) {
|
|
7533
|
+
console.error(`plugin 已存在:${target}`);
|
|
7534
|
+
process.exit(1);
|
|
7535
|
+
}
|
|
7536
|
+
fs.mkdirSync(path$1.join(target, ".claude-plugin"), { recursive: true });
|
|
7537
|
+
const manifest = {
|
|
7538
|
+
$schema: "https://anthropic.com/claude-code/plugin.schema.json",
|
|
7539
|
+
name: path$1.basename(target),
|
|
7540
|
+
version: "0.1.0",
|
|
7541
|
+
description: "",
|
|
7542
|
+
author: { name: "" },
|
|
7543
|
+
...components.includes("skills") ? { skills: ["./skills"] } : {}
|
|
7544
|
+
};
|
|
7545
|
+
fs.writeFileSync(path$1.join(target, ".claude-plugin", "plugin.json"), JSON.stringify(manifest, null, 2) + "\n");
|
|
7546
|
+
if (components.includes("skills")) fs.mkdirSync(path$1.join(target, "skills"), { recursive: true });
|
|
7547
|
+
if (components.includes("agents")) fs.mkdirSync(path$1.join(target, "agents"), { recursive: true });
|
|
7548
|
+
if (components.includes("hooks")) {
|
|
7549
|
+
fs.mkdirSync(path$1.join(target, "hooks"), { recursive: true });
|
|
7550
|
+
fs.writeFileSync(path$1.join(target, "hooks", "hooks.json"), JSON.stringify({ hooks: {} }, null, 2) + "\n");
|
|
7551
|
+
}
|
|
7552
|
+
if (components.includes("mcp")) fs.writeFileSync(path$1.join(target, ".mcp.json"), JSON.stringify({ mcpServers: {} }, null, 2) + "\n");
|
|
7553
|
+
console.log(`${GREEN$3}✓${RESET$4} Created plugin ${TEXT$4}${path$1.basename(target)}${RESET$4}`);
|
|
7554
|
+
console.log(` ${DIM$4}${target}${RESET$4}`);
|
|
7555
|
+
}
|
|
7556
|
+
async function validate(args) {
|
|
7557
|
+
const target = path$1.resolve(args[0] ?? ".");
|
|
7558
|
+
const standardManifest = path$1.join(target, ".claude-plugin", "plugin.json");
|
|
7559
|
+
const bfiManifest = path$1.join(target, "plugin.json");
|
|
7560
|
+
const manifestPath = fs.existsSync(standardManifest) ? standardManifest : fs.existsSync(bfiManifest) ? bfiManifest : standardManifest;
|
|
7561
|
+
const isBfiLayout = manifestPath === bfiManifest;
|
|
7562
|
+
const errors = [];
|
|
7563
|
+
const warnings = [];
|
|
7564
|
+
if (!fs.existsSync(manifestPath)) errors.push(`未找到 ${manifestPath}`);
|
|
7565
|
+
else try {
|
|
7566
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
|
|
7567
|
+
if (!manifest.name) errors.push("plugin.json 缺少 name 字段");
|
|
7568
|
+
if (!manifest.version) warnings.push("plugin.json 缺少 version 字段");
|
|
7569
|
+
if (!manifest.description) warnings.push("plugin.json 缺少 description 字段");
|
|
7570
|
+
const skillPaths = manifest.skills ?? manifest.provides?.skills ?? [];
|
|
7571
|
+
for (const skillPath of skillPaths) {
|
|
7572
|
+
const normalized = skillPath.startsWith("./") ? skillPath.slice(2) : skillPath;
|
|
7573
|
+
if (!isBfiLayout && !skillPath.startsWith("./")) errors.push(`skills 路径必须以 ./ 开头:${skillPath}`);
|
|
7574
|
+
else if (!fs.existsSync(path$1.join(target, normalized))) errors.push(`skills 路径不存在:${skillPath}`);
|
|
7575
|
+
}
|
|
7576
|
+
} catch (e) {
|
|
7577
|
+
errors.push(`plugin.json 解析失败:${e.message}`);
|
|
7578
|
+
}
|
|
7579
|
+
if (fs.existsSync(path$1.join(target, ".mcp.json"))) try {
|
|
7580
|
+
JSON.parse(fs.readFileSync(path$1.join(target, ".mcp.json"), "utf-8"));
|
|
7581
|
+
} catch (e) {
|
|
7582
|
+
errors.push(`.mcp.json 解析失败:${e.message}`);
|
|
7583
|
+
}
|
|
7584
|
+
if (fs.existsSync(path$1.join(target, "hooks", "hooks.json"))) try {
|
|
7585
|
+
JSON.parse(fs.readFileSync(path$1.join(target, "hooks", "hooks.json"), "utf-8"));
|
|
7586
|
+
} catch (e) {
|
|
7587
|
+
errors.push(`hooks/hooks.json 解析失败:${e.message}`);
|
|
7588
|
+
}
|
|
7589
|
+
for (const error of errors) console.log(`${RED$2}✗${RESET$4} ${error}`);
|
|
7590
|
+
for (const warning of warnings) console.log(`${DIM$4}⚠${RESET$4} ${warning}`);
|
|
7591
|
+
if (errors.length === 0) console.log(`${GREEN$3}✓${RESET$4} Validation passed${warnings.length > 0 ? ` with ${warnings.length} warning(s)` : ""}`);
|
|
7592
|
+
else process.exitCode = 1;
|
|
7593
|
+
}
|
|
7594
|
+
async function publish(args, globalOpts) {
|
|
7595
|
+
const target = args[0];
|
|
7596
|
+
if (!target) {
|
|
7597
|
+
console.error("用法:bfi plugin publish <directory|file.zip|file.tgz>");
|
|
7598
|
+
process.exit(1);
|
|
7599
|
+
}
|
|
7600
|
+
const resolved = path$1.resolve(target);
|
|
7601
|
+
const namespace = getOption$1(args, "--namespace") || globalOpts.namespace || "global";
|
|
7602
|
+
const visibility = getOption$1(args, "--visibility") || "public";
|
|
7603
|
+
const { registry, token } = resolveConfig(globalOpts);
|
|
7604
|
+
if (!token) {
|
|
7605
|
+
console.error("未登录。请先运行:bfi auth login --token <token>");
|
|
7606
|
+
process.exit(1);
|
|
7607
|
+
}
|
|
7608
|
+
const api = new SkillHubAPI(registry, token);
|
|
7609
|
+
if (/\.(tgz|tar\.gz|zip)$/i.test(resolved)) {
|
|
7610
|
+
if (!fs.existsSync(resolved)) {
|
|
7611
|
+
console.error(`文件未找到:${resolved}`);
|
|
7612
|
+
process.exit(1);
|
|
7613
|
+
}
|
|
7614
|
+
console.log(`上传 ${TEXT$4}${path$1.basename(resolved)}${RESET$4} → ${DIM$4}${registry}${RESET$4}`);
|
|
7615
|
+
try {
|
|
7616
|
+
const created = await api.uploadPlugin(fs.readFileSync(resolved), path$1.basename(resolved), namespace, { visibility: visibility.toUpperCase() });
|
|
7617
|
+
const plugin = created?.data ?? created;
|
|
7618
|
+
console.log(`${GREEN$3}✓${RESET$4} Published ${TEXT$4}${plugin.slug}${RESET$4} (id=${plugin.id})`);
|
|
7619
|
+
} catch (e) {
|
|
7620
|
+
console.error(`发布失败:${e.message}`);
|
|
7621
|
+
process.exit(1);
|
|
7622
|
+
}
|
|
7623
|
+
return;
|
|
7624
|
+
}
|
|
7625
|
+
const standardManifest = path$1.join(resolved, ".claude-plugin", "plugin.json");
|
|
7626
|
+
const bfiManifest = path$1.join(resolved, "plugin.json");
|
|
7627
|
+
const manifestPath = fs.existsSync(standardManifest) ? standardManifest : fs.existsSync(bfiManifest) ? bfiManifest : null;
|
|
7628
|
+
if (!manifestPath) {
|
|
7629
|
+
console.error(`不是有效 plugin 目录(缺少 .claude-plugin/plugin.json 或 plugin.json):${resolved}`);
|
|
7630
|
+
process.exit(1);
|
|
7631
|
+
}
|
|
7632
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
|
|
7633
|
+
const namespaces = await api.myNamespaces();
|
|
7634
|
+
const ns = (Array.isArray(namespaces) ? namespaces : namespaces?.data ?? []).find((n) => n.slug === namespace);
|
|
7635
|
+
if (!ns) {
|
|
7636
|
+
console.error(`命名空间不存在:${namespace}`);
|
|
7637
|
+
process.exit(1);
|
|
7638
|
+
}
|
|
7639
|
+
const zipBuffer = createZipFromDir(resolved);
|
|
7640
|
+
const versionMatch = manifest.version ?? "0.1.0";
|
|
7641
|
+
console.log(`发布 ${TEXT$4}${manifest.name}@${versionMatch}${RESET$4} → ${DIM$4}${registry}${RESET$4}`);
|
|
7642
|
+
const created = await api.createPlugin({
|
|
7643
|
+
namespaceId: ns.id,
|
|
7644
|
+
slug: manifest.name,
|
|
7645
|
+
displayName: manifest.name,
|
|
7646
|
+
summary: manifest.description ?? "",
|
|
7647
|
+
description: manifest.description ?? "",
|
|
7648
|
+
visibility: visibility.toUpperCase()
|
|
7649
|
+
});
|
|
7650
|
+
const plugin = created?.data ?? created;
|
|
7651
|
+
const version = await api.createPluginVersion(plugin.id, {
|
|
7652
|
+
version: versionMatch,
|
|
7653
|
+
changelog: "Published via bfi plugin publish"
|
|
7654
|
+
});
|
|
7655
|
+
const versionBody = version?.data ?? version;
|
|
7656
|
+
const tmpZip = path$1.join(os$1.tmpdir(), `${manifest.name}-${versionMatch}.zip`);
|
|
7657
|
+
fs.writeFileSync(tmpZip, zipBuffer);
|
|
7658
|
+
console.log(`${GREEN$3}✓${RESET$4} Plugin created (id=${plugin.id}, version=${versionBody.version})`);
|
|
7659
|
+
console.log(` ${DIM$4}zip: ${tmpZip}${RESET$4}`);
|
|
7660
|
+
}
|
|
7661
|
+
async function list$1(_args, globalOpts) {
|
|
7662
|
+
const { registry, token } = resolveConfig(globalOpts);
|
|
7663
|
+
const result = await new SkillHubAPI(registry, token).listPlugins();
|
|
7664
|
+
const plugins = Array.isArray(result) ? result : result?.content ?? result?.data ?? [];
|
|
7665
|
+
if (plugins.length === 0) {
|
|
7666
|
+
console.log("暂无 plugin。");
|
|
7667
|
+
return;
|
|
7668
|
+
}
|
|
7669
|
+
console.log();
|
|
7670
|
+
for (const p of plugins) {
|
|
7671
|
+
console.log(` ${TEXT$4}${p.slug}${RESET$4} ${DIM$4}${p.latestVersionId ? "v" + p.latestVersionId : ""}${RESET$4}`);
|
|
7672
|
+
console.log(` ${p.summary ?? ""}`);
|
|
7673
|
+
console.log(` ${DIM$4}下载 ${p.downloadCount ?? 0} · star ${p.starCount ?? 0}${RESET$4}`);
|
|
7674
|
+
}
|
|
7675
|
+
console.log();
|
|
7676
|
+
}
|
|
7677
|
+
async function info$1(args, globalOpts) {
|
|
7678
|
+
const idArg = args[0];
|
|
7679
|
+
if (!idArg) {
|
|
7680
|
+
console.error("用法:bfi plugin info <pluginId>");
|
|
7681
|
+
process.exit(1);
|
|
7682
|
+
}
|
|
7683
|
+
const { registry, token } = resolveConfig(globalOpts);
|
|
7684
|
+
const plugin = await new SkillHubAPI(registry, token).getPlugin(Number(idArg));
|
|
7685
|
+
console.log();
|
|
7686
|
+
console.log(`${BOLD$2}${plugin.displayName ?? plugin.slug}${RESET$4} (id=${plugin.id})`);
|
|
7687
|
+
console.log(` ${plugin.summary ?? ""}`);
|
|
7688
|
+
console.log(` ${DIM$4}visibility=${plugin.visibility} status=${plugin.status}${RESET$4}`);
|
|
7689
|
+
console.log(` ${DIM$4}下载 ${plugin.downloadCount ?? 0} · star ${plugin.starCount ?? 0}${RESET$4}`);
|
|
7690
|
+
console.log();
|
|
7691
|
+
}
|
|
7692
|
+
async function install(args, globalOpts) {
|
|
7693
|
+
const ref = args[0];
|
|
7694
|
+
if (!ref) {
|
|
7695
|
+
console.error("用法:bfi plugin install <slug|插件ID|./plugin.zip>");
|
|
7696
|
+
process.exit(1);
|
|
7697
|
+
}
|
|
7698
|
+
const homeSkills = path$1.join(os$1.homedir(), ".claude", "skills");
|
|
7699
|
+
if (ref.endsWith(".zip") || fs.existsSync(ref)) {
|
|
7700
|
+
const resolved = path$1.resolve(ref);
|
|
7701
|
+
extractZip(resolved, homeSkills);
|
|
7702
|
+
console.log(`${GREEN$3}✓${RESET$4} Installed from ${resolved}`);
|
|
7703
|
+
console.log(` ${DIM$4}${homeSkills}${RESET$4}`);
|
|
7704
|
+
console.log(` ${DIM$4}重启 Claude Code 会话后生效${RESET$4}`);
|
|
7705
|
+
return;
|
|
7706
|
+
}
|
|
7707
|
+
const { registry, token } = resolveConfig(globalOpts);
|
|
7708
|
+
const api = new SkillHubAPI(registry, token);
|
|
7709
|
+
let plugin;
|
|
7710
|
+
if (/^\d+$/.test(ref)) plugin = await api.getPlugin(Number(ref));
|
|
7711
|
+
else {
|
|
7712
|
+
const list = await api.listPlugins();
|
|
7713
|
+
const match = (Array.isArray(list) ? list : list?.content ?? list?.data ?? []).find((p) => p.slug === ref || p.displayName === ref);
|
|
7714
|
+
if (!match) {
|
|
7715
|
+
console.error(`未找到插件:${ref}(支持传 slug 或插件 ID)`);
|
|
7716
|
+
process.exit(1);
|
|
7717
|
+
}
|
|
7718
|
+
plugin = await api.getPlugin(match.id);
|
|
7719
|
+
}
|
|
7720
|
+
const slug = plugin.slug ?? `plugin-${plugin.id}`;
|
|
7721
|
+
if (!plugin.latestVersionId) {
|
|
7722
|
+
console.error(`插件 ${slug} 尚无已发布版本,无法安装`);
|
|
7723
|
+
process.exit(1);
|
|
7724
|
+
}
|
|
7725
|
+
const tmpZip = path$1.join(os$1.tmpdir(), `${slug}-install.zip`);
|
|
7726
|
+
console.log(`下载 ${TEXT$4}${slug}${RESET$4} ← ${DIM$4}${registry}${RESET$4}`);
|
|
7727
|
+
await api.downloadPluginBundle(plugin.id, plugin.latestVersionId, tmpZip);
|
|
7728
|
+
extractZip(tmpZip, homeSkills);
|
|
7729
|
+
fs.unlinkSync(tmpZip);
|
|
7730
|
+
console.log(`${GREEN$3}✓${RESET$4} Installed ${TEXT$4}${slug}${RESET$4}`);
|
|
7731
|
+
console.log(` ${DIM$4}${homeSkills}${RESET$4}`);
|
|
7732
|
+
console.log(` ${DIM$4}重启 Claude Code 会话后生效${RESET$4}`);
|
|
7733
|
+
}
|
|
7734
|
+
function getOption$1(args, flag) {
|
|
7735
|
+
const idx = args.indexOf(flag);
|
|
7736
|
+
return idx !== -1 && idx + 1 < args.length ? args[idx + 1] : void 0;
|
|
7737
|
+
}
|
|
7738
|
+
const RESET$3 = "\x1B[0m";
|
|
7739
|
+
const BOLD$1 = "\x1B[1m";
|
|
7740
|
+
const DIM$3 = "\x1B[38;5;102m";
|
|
7741
|
+
const TEXT$3 = "\x1B[38;5;145m";
|
|
7742
|
+
const GREEN$2 = "\x1B[32m";
|
|
7743
|
+
const RED$1 = "\x1B[31m";
|
|
7744
|
+
async function runMcp(args, globalOpts) {
|
|
7745
|
+
const sub = args[0];
|
|
7746
|
+
const subArgs = args.slice(1);
|
|
7747
|
+
switch (sub) {
|
|
7748
|
+
case "list":
|
|
7749
|
+
case "ls":
|
|
7750
|
+
await list(subArgs, globalOpts);
|
|
7751
|
+
break;
|
|
7752
|
+
case "info":
|
|
7753
|
+
await info(subArgs, globalOpts);
|
|
7754
|
+
break;
|
|
7755
|
+
case "config":
|
|
7756
|
+
await config(subArgs, globalOpts);
|
|
7757
|
+
break;
|
|
7758
|
+
case "add":
|
|
7759
|
+
await add(subArgs, globalOpts);
|
|
7760
|
+
break;
|
|
7761
|
+
case "remove":
|
|
7762
|
+
case "rm":
|
|
7763
|
+
await remove(subArgs);
|
|
7764
|
+
break;
|
|
7765
|
+
case "--help":
|
|
7766
|
+
case "-h":
|
|
7767
|
+
case void 0:
|
|
7768
|
+
showHelp$1();
|
|
7769
|
+
break;
|
|
7770
|
+
default:
|
|
7771
|
+
console.log(`未知 mcp 命令:${sub}`);
|
|
7772
|
+
showHelp$1();
|
|
7773
|
+
}
|
|
7774
|
+
}
|
|
7775
|
+
function showHelp$1() {
|
|
7776
|
+
console.log();
|
|
7777
|
+
console.log(`${BOLD$1}bfi mcp <command>${RESET$3} — MCP 服务器管理`);
|
|
7778
|
+
console.log();
|
|
7779
|
+
console.log(`${BOLD$1}Commands:${RESET$3}`);
|
|
7780
|
+
console.log(` list, ls List MCP servers on SkillHub`);
|
|
7781
|
+
console.log(` info <id> Show MCP server details`);
|
|
7782
|
+
console.log(` config <id> Print .mcp.json config fragment`);
|
|
7783
|
+
console.log(` add <id> Write server into local .mcp.json (user scope)`);
|
|
7784
|
+
console.log(` remove <name> Remove a server from local .mcp.json`);
|
|
7785
|
+
console.log();
|
|
7786
|
+
}
|
|
7787
|
+
async function list(_args, globalOpts) {
|
|
7788
|
+
const { registry, token } = resolveConfig(globalOpts);
|
|
7789
|
+
const result = await new SkillHubAPI(registry, token).listMcpServers();
|
|
7790
|
+
const servers = Array.isArray(result) ? result : result?.content ?? result?.data ?? [];
|
|
7791
|
+
if (servers.length === 0) {
|
|
7792
|
+
console.log("暂无 MCP 服务器。");
|
|
7793
|
+
return;
|
|
7794
|
+
}
|
|
7795
|
+
console.log();
|
|
7796
|
+
for (const s of servers) {
|
|
7797
|
+
console.log(` ${TEXT$3}${s.slug}${RESET$3} ${DIM$3}[${s.transportType}]${RESET$3}`);
|
|
7798
|
+
console.log(` ${s.summary ?? ""}`);
|
|
7799
|
+
console.log(` ${DIM$3}下载 ${s.downloadCount ?? 0} · star ${s.starCount ?? 0}${RESET$3}`);
|
|
7800
|
+
}
|
|
7801
|
+
console.log();
|
|
7802
|
+
}
|
|
7803
|
+
async function info(args, globalOpts) {
|
|
7804
|
+
const idArg = args[0];
|
|
7805
|
+
if (!idArg) {
|
|
7806
|
+
console.error("用法:bfi mcp info <mcpId>");
|
|
7807
|
+
process.exit(1);
|
|
7808
|
+
}
|
|
7809
|
+
const { registry, token } = resolveConfig(globalOpts);
|
|
7810
|
+
const server = await new SkillHubAPI(registry, token).getMcpServer(Number(idArg));
|
|
7811
|
+
console.log();
|
|
7812
|
+
console.log(`${BOLD$1}${server.displayName ?? server.slug}${RESET$3} (id=${server.id})`);
|
|
7813
|
+
console.log(` transport: ${server.transportType}`);
|
|
7814
|
+
if (server.serverUrl) console.log(` url: ${server.serverUrl}`);
|
|
7815
|
+
if (server.command) console.log(` command: ${server.command}`);
|
|
7816
|
+
console.log(` ${DIM$3}visibility=${server.visibility} status=${server.status}${RESET$3}`);
|
|
7817
|
+
console.log();
|
|
7818
|
+
}
|
|
7819
|
+
async function config(args, globalOpts) {
|
|
7820
|
+
const idArg = args[0];
|
|
7821
|
+
if (!idArg) {
|
|
7822
|
+
console.error("用法:bfi mcp config <mcpId>");
|
|
7823
|
+
process.exit(1);
|
|
7824
|
+
}
|
|
7825
|
+
const { registry, token } = resolveConfig(globalOpts);
|
|
7826
|
+
const configJson = await new SkillHubAPI(registry, token).getMcpConfig(Number(idArg));
|
|
7827
|
+
console.log(configJson);
|
|
7828
|
+
}
|
|
7829
|
+
async function add(args, globalOpts) {
|
|
7830
|
+
const idArg = args[0];
|
|
7831
|
+
if (!idArg) {
|
|
7832
|
+
console.error("用法:bfi mcp add <mcpId>");
|
|
7833
|
+
process.exit(1);
|
|
7834
|
+
}
|
|
7835
|
+
const { registry, token } = resolveConfig(globalOpts);
|
|
7836
|
+
const api = new SkillHubAPI(registry, token);
|
|
7837
|
+
const server = await api.getMcpServer(Number(idArg));
|
|
7838
|
+
const configJson = JSON.parse(await api.getMcpConfig(Number(idArg)));
|
|
7839
|
+
const mcpPath = resolveMcpPath(getOption(args, "--scope") ?? "user");
|
|
7840
|
+
let existing = { mcpServers: {} };
|
|
7841
|
+
if (fs.existsSync(mcpPath)) {
|
|
7842
|
+
existing = JSON.parse(fs.readFileSync(mcpPath, "utf-8"));
|
|
7843
|
+
if (!existing.mcpServers) existing.mcpServers = {};
|
|
7844
|
+
}
|
|
7845
|
+
const serverName = server.slug ?? `mcp-${server.id}`;
|
|
7846
|
+
existing.mcpServers[serverName] = configJson.mcpServers[Object.keys(configJson.mcpServers)[0]];
|
|
7847
|
+
fs.mkdirSync(path$1.dirname(mcpPath), { recursive: true });
|
|
7848
|
+
fs.writeFileSync(mcpPath, JSON.stringify(existing, null, 2) + "\n");
|
|
7849
|
+
console.log(`${GREEN$2}✓${RESET$3} Added ${TEXT$3}${serverName}${RESET$3} → ${DIM$3}${mcpPath}${RESET$3}`);
|
|
7850
|
+
}
|
|
7851
|
+
async function remove(args) {
|
|
7852
|
+
const name = args[0];
|
|
7853
|
+
if (!name) {
|
|
7854
|
+
console.error("用法:bfi mcp remove <name> [--scope user|project|local]");
|
|
7855
|
+
process.exit(1);
|
|
7856
|
+
}
|
|
7857
|
+
const mcpPath = resolveMcpPath(getOption(args, "--scope") ?? "user");
|
|
7858
|
+
if (!fs.existsSync(mcpPath)) {
|
|
7859
|
+
console.error(`配置文件不存在:${mcpPath}`);
|
|
7860
|
+
process.exit(1);
|
|
7861
|
+
}
|
|
7862
|
+
const existing = JSON.parse(fs.readFileSync(mcpPath, "utf-8"));
|
|
7863
|
+
if (!existing.mcpServers?.[name]) {
|
|
7864
|
+
console.error(`${RED$1}未找到服务器:${name}${RESET$3}`);
|
|
7865
|
+
process.exit(1);
|
|
7866
|
+
}
|
|
7867
|
+
delete existing.mcpServers[name];
|
|
7868
|
+
fs.writeFileSync(mcpPath, JSON.stringify(existing, null, 2) + "\n");
|
|
7869
|
+
console.log(`${GREEN$2}✓${RESET$3} Removed ${TEXT$3}${name}${RESET$3}`);
|
|
7870
|
+
}
|
|
7871
|
+
function resolveMcpPath(scope) {
|
|
7872
|
+
switch (scope) {
|
|
7873
|
+
case "project": return path$1.join(process.cwd(), ".mcp.json");
|
|
7874
|
+
case "local": return path$1.join(process.cwd(), ".claude", ".mcp.json.local");
|
|
7875
|
+
default: return path$1.join(os$1.homedir(), ".claude", ".mcp.json");
|
|
7876
|
+
}
|
|
7877
|
+
}
|
|
7878
|
+
function getOption(args, flag) {
|
|
7879
|
+
const idx = args.indexOf(flag);
|
|
7880
|
+
return idx !== -1 && idx + 1 < args.length ? args[idx + 1] : void 0;
|
|
7881
|
+
}
|
|
7325
7882
|
const RESET$2 = "\x1B[0m";
|
|
7326
7883
|
const DIM$2 = "\x1B[38;5;102m";
|
|
7327
7884
|
const TEXT$2 = "\x1B[38;5;145m";
|
|
@@ -7435,6 +7992,755 @@ async function runInstall(args, globalOpts) {
|
|
|
7435
7992
|
if (failedCount > 0) console.log(`${YELLOW}⚠ ${failedCount} 项恢复失败${RESET$1}`);
|
|
7436
7993
|
console.log();
|
|
7437
7994
|
}
|
|
7995
|
+
const YMC_DEFAULT_BASE = process.env.BFI_YMC_BASE || "http://172.20.35.178:8901/bficli/api/v2";
|
|
7996
|
+
var YmcApi = class {
|
|
7997
|
+
baseUrl;
|
|
7998
|
+
token;
|
|
7999
|
+
constructor(token = null, baseUrl) {
|
|
8000
|
+
this.baseUrl = (baseUrl || YMC_DEFAULT_BASE).replace(/\/$/, "");
|
|
8001
|
+
if (token) {
|
|
8002
|
+
this.token = token;
|
|
8003
|
+
return;
|
|
8004
|
+
}
|
|
8005
|
+
this.token = loadGlobalConfig()?.token ?? null;
|
|
8006
|
+
}
|
|
8007
|
+
async call(path, body = {}, timeoutMs = 9e4) {
|
|
8008
|
+
const url = `${this.baseUrl}/${path.replace(/^\//, "")}`;
|
|
8009
|
+
const payload = JSON.stringify(body);
|
|
8010
|
+
const mod = url.startsWith("https://") ? https : http;
|
|
8011
|
+
const raw = await new Promise((resolve, reject) => {
|
|
8012
|
+
const req = mod.request(url, {
|
|
8013
|
+
method: "POST",
|
|
8014
|
+
headers: {
|
|
8015
|
+
"Content-Type": "application/json",
|
|
8016
|
+
Accept: "application/json",
|
|
8017
|
+
...this.token ? { Authorization: `Bearer ${this.token}` } : {},
|
|
8018
|
+
"Content-Length": Buffer.byteLength(payload)
|
|
8019
|
+
},
|
|
8020
|
+
timeout: timeoutMs
|
|
8021
|
+
}, (res) => {
|
|
8022
|
+
const chunks = [];
|
|
8023
|
+
res.on("data", (c) => chunks.push(c));
|
|
8024
|
+
res.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
|
|
8025
|
+
});
|
|
8026
|
+
req.on("timeout", () => {
|
|
8027
|
+
req.destroy(/* @__PURE__ */ new Error(`请求超时(${timeoutMs}ms)`));
|
|
8028
|
+
});
|
|
8029
|
+
req.on("error", reject);
|
|
8030
|
+
req.write(payload);
|
|
8031
|
+
req.end();
|
|
8032
|
+
});
|
|
8033
|
+
let parsed;
|
|
8034
|
+
try {
|
|
8035
|
+
parsed = JSON.parse(raw);
|
|
8036
|
+
} catch {
|
|
8037
|
+
throw new Error(`非 JSON 响应(HTTP):${raw.slice(0, 200)}`);
|
|
8038
|
+
}
|
|
8039
|
+
if (parsed.detail && parsed.code === void 0) throw new Error(String(parsed.detail));
|
|
8040
|
+
return {
|
|
8041
|
+
code: typeof parsed.code === "number" ? parsed.code : -1,
|
|
8042
|
+
msg: String(parsed.msg ?? ""),
|
|
8043
|
+
data: parsed.data ?? null,
|
|
8044
|
+
raw: parsed
|
|
8045
|
+
};
|
|
8046
|
+
}
|
|
8047
|
+
async download(path, body, outFile, timeoutMs = 18e4) {
|
|
8048
|
+
const url = `${this.baseUrl}/${path.replace(/^\//, "")}`;
|
|
8049
|
+
const payload = JSON.stringify(body);
|
|
8050
|
+
const mod = url.startsWith("https://") ? https : http;
|
|
8051
|
+
const raw = await new Promise((resolve, reject) => {
|
|
8052
|
+
const req = mod.request(url, {
|
|
8053
|
+
method: "POST",
|
|
8054
|
+
headers: {
|
|
8055
|
+
"Content-Type": "application/json",
|
|
8056
|
+
Accept: "*/*",
|
|
8057
|
+
Authorization: `Bearer ${this.token}`,
|
|
8058
|
+
"Content-Length": Buffer.byteLength(payload)
|
|
8059
|
+
},
|
|
8060
|
+
timeout: timeoutMs
|
|
8061
|
+
}, (res) => {
|
|
8062
|
+
const chunks = [];
|
|
8063
|
+
res.on("data", (c) => chunks.push(c));
|
|
8064
|
+
res.on("end", () => resolve(Buffer.concat(chunks)));
|
|
8065
|
+
});
|
|
8066
|
+
req.on("timeout", () => {
|
|
8067
|
+
req.destroy(/* @__PURE__ */ new Error(`下载超时(${timeoutMs}ms)`));
|
|
8068
|
+
});
|
|
8069
|
+
req.on("error", reject);
|
|
8070
|
+
req.write(payload);
|
|
8071
|
+
req.end();
|
|
8072
|
+
});
|
|
8073
|
+
if (raw.length > 0 && raw[0] === 123) {
|
|
8074
|
+
const msg = JSON.parse(raw.toString("utf-8"));
|
|
8075
|
+
throw new Error(String(msg.msg ?? "下载失败"));
|
|
8076
|
+
}
|
|
8077
|
+
(await import("fs")).writeFileSync(outFile, raw);
|
|
8078
|
+
return raw.length;
|
|
8079
|
+
}
|
|
8080
|
+
async get(path, timeoutMs = 3e4) {
|
|
8081
|
+
const url = `${this.baseUrl}/${path.replace(/^\//, "")}`;
|
|
8082
|
+
const mod = url.startsWith("https://") ? https : http;
|
|
8083
|
+
const raw = await new Promise((resolve, reject) => {
|
|
8084
|
+
const req = mod.request(url, {
|
|
8085
|
+
method: "GET",
|
|
8086
|
+
headers: {
|
|
8087
|
+
Accept: "application/json",
|
|
8088
|
+
...this.token ? { Authorization: `Bearer ${this.token}` } : {}
|
|
8089
|
+
},
|
|
8090
|
+
timeout: timeoutMs
|
|
8091
|
+
}, (res) => {
|
|
8092
|
+
const chunks = [];
|
|
8093
|
+
res.on("data", (c) => chunks.push(c));
|
|
8094
|
+
res.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
|
|
8095
|
+
});
|
|
8096
|
+
req.on("timeout", () => {
|
|
8097
|
+
req.destroy(/* @__PURE__ */ new Error(`请求超时(${timeoutMs}ms)`));
|
|
8098
|
+
});
|
|
8099
|
+
req.on("error", reject);
|
|
8100
|
+
req.end();
|
|
8101
|
+
});
|
|
8102
|
+
const parsed = JSON.parse(raw);
|
|
8103
|
+
if (parsed.detail && parsed.code === void 0) throw new Error(String(parsed.detail));
|
|
8104
|
+
return {
|
|
8105
|
+
code: typeof parsed.code === "number" ? parsed.code : -1,
|
|
8106
|
+
msg: String(parsed.msg ?? ""),
|
|
8107
|
+
data: parsed.data ?? null,
|
|
8108
|
+
raw: parsed
|
|
8109
|
+
};
|
|
8110
|
+
}
|
|
8111
|
+
};
|
|
8112
|
+
function opt(args, ...names) {
|
|
8113
|
+
for (const name of names) {
|
|
8114
|
+
const idx = args.indexOf(name);
|
|
8115
|
+
if (idx !== -1 && idx + 1 < args.length) return args[idx + 1];
|
|
8116
|
+
const eq = args.find((a) => a.startsWith(`${name}=`));
|
|
8117
|
+
if (eq) return eq.slice(name.length + 1);
|
|
8118
|
+
}
|
|
8119
|
+
}
|
|
8120
|
+
function flag(args, ...names) {
|
|
8121
|
+
return names.some((n) => args.includes(n));
|
|
8122
|
+
}
|
|
8123
|
+
function positionals(args) {
|
|
8124
|
+
const out = [];
|
|
8125
|
+
for (let i = 0; i < args.length; i++) {
|
|
8126
|
+
if (args[i].startsWith("-")) {
|
|
8127
|
+
if (!args[i].includes("=") && i + 1 < args.length && !args[i + 1].startsWith("-")) i++;
|
|
8128
|
+
continue;
|
|
8129
|
+
}
|
|
8130
|
+
out.push(args[i]);
|
|
8131
|
+
}
|
|
8132
|
+
return out;
|
|
8133
|
+
}
|
|
8134
|
+
function emit(result, jsonMode, summarize) {
|
|
8135
|
+
if (jsonMode) {
|
|
8136
|
+
console.log(JSON.stringify(result.raw, null, 2));
|
|
8137
|
+
return;
|
|
8138
|
+
}
|
|
8139
|
+
if (result.code !== 0) {
|
|
8140
|
+
console.error(`✗ ${result.msg || "请求失败"}`);
|
|
8141
|
+
process.exit(1);
|
|
8142
|
+
}
|
|
8143
|
+
console.log(summarize ? summarize(result.data) : JSON.stringify(result.data, null, 2));
|
|
8144
|
+
}
|
|
8145
|
+
function raw0(text) {
|
|
8146
|
+
return text || "{}";
|
|
8147
|
+
}
|
|
8148
|
+
const ENV_DOC = "环境: test/daily/pre/core1-4/commldev";
|
|
8149
|
+
function requireEnv(args) {
|
|
8150
|
+
const env = opt(args, "-e", "--env", "--env-name");
|
|
8151
|
+
if (!env) {
|
|
8152
|
+
console.error(`缺少环境参数:-e <${ENV_DOC}>`);
|
|
8153
|
+
process.exit(1);
|
|
8154
|
+
}
|
|
8155
|
+
return { env_name: env };
|
|
8156
|
+
}
|
|
8157
|
+
function timeOf(args, required = true) {
|
|
8158
|
+
const t = opt(args, "-t", "--time");
|
|
8159
|
+
if (!t && required) {
|
|
8160
|
+
console.error("缺少时间参数:-t <简写>(如 1h/3d/10m/4w,表示过去 N 单位到现在)");
|
|
8161
|
+
process.exit(1);
|
|
8162
|
+
}
|
|
8163
|
+
return t ? { time: t } : {};
|
|
8164
|
+
}
|
|
8165
|
+
function timeOpts(args) {
|
|
8166
|
+
const out = {};
|
|
8167
|
+
const t = opt(args, "-t", "--time");
|
|
8168
|
+
if (t) out.time = t;
|
|
8169
|
+
const st = opt(args, "--start-time");
|
|
8170
|
+
if (st) out.start_time = st;
|
|
8171
|
+
const et = opt(args, "--end-time");
|
|
8172
|
+
if (et) out.end_time = et;
|
|
8173
|
+
return out;
|
|
8174
|
+
}
|
|
8175
|
+
function requireToken(api) {
|
|
8176
|
+
if (!api.token) {
|
|
8177
|
+
console.error("未认证。先运行:bfi auth login --token <sk-token>(或传 --token)");
|
|
8178
|
+
process.exit(1);
|
|
8179
|
+
}
|
|
8180
|
+
}
|
|
8181
|
+
function rowsSummary(label) {
|
|
8182
|
+
return (data) => {
|
|
8183
|
+
const d = data;
|
|
8184
|
+
const rows = d.rows ?? d;
|
|
8185
|
+
return `✓ ${label}: ${Array.isArray(rows) ? rows.length : 0} 条`;
|
|
8186
|
+
};
|
|
8187
|
+
}
|
|
8188
|
+
async function runYmc(args, globalOpts) {
|
|
8189
|
+
const sub = args[0];
|
|
8190
|
+
const rest = args.slice(1);
|
|
8191
|
+
const jsonMode = flag(rest, "--json");
|
|
8192
|
+
const token = globalOpts.token;
|
|
8193
|
+
const api = new YmcApi(token ?? null);
|
|
8194
|
+
requireToken(api);
|
|
8195
|
+
if (!sub || sub === "--help" || sub === "-h") {
|
|
8196
|
+
printHelp();
|
|
8197
|
+
return;
|
|
8198
|
+
}
|
|
8199
|
+
switch (sub) {
|
|
8200
|
+
case "status":
|
|
8201
|
+
emit(await api.get("ymc/auth/status"), jsonMode, (data) => {
|
|
8202
|
+
const s = data;
|
|
8203
|
+
const line = (g) => {
|
|
8204
|
+
const x = s[g];
|
|
8205
|
+
if (!x?.logged_in) return `${g}: 未登录`;
|
|
8206
|
+
return `${g}: 已登录(至 ${(/* @__PURE__ */ new Date(Number(x.expires_at) * 1e3)).toLocaleString("zh-CN")})`;
|
|
8207
|
+
};
|
|
8208
|
+
return `${line("dev")}\n${line("prod")}`;
|
|
8209
|
+
});
|
|
8210
|
+
break;
|
|
8211
|
+
case "trace": {
|
|
8212
|
+
const id = positionals(rest)[0];
|
|
8213
|
+
if (!id) {
|
|
8214
|
+
console.error("用法:bfi ymc trace <trace_id> -e <env>");
|
|
8215
|
+
process.exit(1);
|
|
8216
|
+
}
|
|
8217
|
+
emit(await api.call("trace", {
|
|
8218
|
+
trace_id: id,
|
|
8219
|
+
...requireEnv(rest)
|
|
8220
|
+
}), jsonMode, (data) => {
|
|
8221
|
+
const d = data;
|
|
8222
|
+
const lines = [];
|
|
8223
|
+
const wc = d.warning_chain;
|
|
8224
|
+
const s = wc?.Summary ?? {};
|
|
8225
|
+
lines.push(`✓ 三源查询完成 ts=${d.ts}(${d.ts_source})`);
|
|
8226
|
+
lines.push(`概览: span=${s.TraceCount ?? "?"} 异常=${s.AppExceptionCount ?? 0} 最大耗时=${s.MaxCostTime ?? "?"}ms 慢调用告警=${s.WarningCallCount ?? 0} 慢SQL告警=${s.WarningSqlCount ?? 0}`);
|
|
8227
|
+
const mw = wc?.MWSummary ?? [];
|
|
8228
|
+
if (Array.isArray(mw) && mw.length) lines.push("Top慢span: " + mw.slice().sort((a, b) => Number(b.CostTime ?? 0) - Number(a.CostTime ?? 0)).slice(0, 3).map((x) => `${x.CurAppCode}=${x.CostTime}ms(sql${x.SqlNum}/rpc${x.RpcCount})`).join(" | "));
|
|
8229
|
+
const ae = wc?.AppException ?? [];
|
|
8230
|
+
if (Array.isArray(ae) && ae.length) {
|
|
8231
|
+
const first = ae[0];
|
|
8232
|
+
const le = first.LogEntry ?? {};
|
|
8233
|
+
const msg = String(le.Message ?? "").replace(/\s+/g, " ").slice(0, 180);
|
|
8234
|
+
lines.push(`异常[0]: ${first.AppCode} ${le.Logger ?? ""}\n ${msg}`);
|
|
8235
|
+
}
|
|
8236
|
+
const ing = d.ingress_logs ?? [];
|
|
8237
|
+
if (Array.isArray(ing) && ing.length) {
|
|
8238
|
+
const st = ing.reduce((m, x) => {
|
|
8239
|
+
const k = String(x.status);
|
|
8240
|
+
m[k] = (m[k] ?? 0) + 1;
|
|
8241
|
+
return m;
|
|
8242
|
+
}, {});
|
|
8243
|
+
lines.push(`ingress: ${ing.length} 条 ${JSON.stringify(st)} 入口=${(ing[0].url ?? "?").toString().slice(0, 60)}`);
|
|
8244
|
+
}
|
|
8245
|
+
return lines.join("\n");
|
|
8246
|
+
});
|
|
8247
|
+
break;
|
|
8248
|
+
}
|
|
8249
|
+
case "chain": {
|
|
8250
|
+
const id = positionals(rest)[0];
|
|
8251
|
+
if (!id) {
|
|
8252
|
+
console.error("用法:bfi ymc chain <trace_id> -e <env>");
|
|
8253
|
+
process.exit(1);
|
|
8254
|
+
}
|
|
8255
|
+
emit(await api.call("chain", {
|
|
8256
|
+
trace_id: id,
|
|
8257
|
+
...requireEnv(rest)
|
|
8258
|
+
}), jsonMode, (data) => {
|
|
8259
|
+
const d = data;
|
|
8260
|
+
return `✓ 链路查询: ts=${d.ts}(${d.ts_source})chain=${d.warning_chain ? "有" : "无"}`;
|
|
8261
|
+
});
|
|
8262
|
+
break;
|
|
8263
|
+
}
|
|
8264
|
+
case "sls-raw": {
|
|
8265
|
+
const query = positionals(rest)[0];
|
|
8266
|
+
if (!query) {
|
|
8267
|
+
console.error("用法:bfi ymc sls-raw \"<查询语句>\" -e <env> [-t time | --start-time <s> --end-time <e> | --days N] [--line N]");
|
|
8268
|
+
console.error("例:bfi ymc sls-raw '* | SELECT applicationName, count(*) AS c GROUP BY applicationName' -e daily -t 3d");
|
|
8269
|
+
console.error("例:bfi ymc sls-raw '* | SELECT * WHERE level=''ERROR''' -e pre --start-time \"2026-08-31 15:00:00\" --end-time \"2026-08-31 16:00:00\"");
|
|
8270
|
+
console.error("说明:--start-time/--end-time(yyyy-MM-dd HH:mm:ss 北京时间)为精确窗口,优先于 -t/--days");
|
|
8271
|
+
process.exit(1);
|
|
8272
|
+
}
|
|
8273
|
+
const body = {
|
|
8274
|
+
query,
|
|
8275
|
+
...requireEnv(rest),
|
|
8276
|
+
...timeOpts(rest),
|
|
8277
|
+
...opt(rest, "--days") ? { days: Number(opt(rest, "--days")) } : {},
|
|
8278
|
+
...opt(rest, "--line") ? { line: Number(opt(rest, "--line")) } : {}
|
|
8279
|
+
};
|
|
8280
|
+
emit(await api.call("sls/raw", body), jsonMode, rowsSummary("SLS 原始查询"));
|
|
8281
|
+
break;
|
|
8282
|
+
}
|
|
8283
|
+
case "sls-grep": {
|
|
8284
|
+
const kw = positionals(rest)[0];
|
|
8285
|
+
if (!kw) {
|
|
8286
|
+
console.error("用法:bfi ymc sls-grep \"<关键词>\" -e <env> [-t time] [--group-by applicationName|message|requestURL]");
|
|
8287
|
+
console.error("例:bfi ymc sls-grep \"ClassCastException\" -e daily -t 3d");
|
|
8288
|
+
console.error("⚠️ 输出为聚合统计行(每组计数,计数上限 100,可能被封顶)——非明细。");
|
|
8289
|
+
console.error(" 需要真实条数/明细/精确时间窗时改用 sls-raw + GROUP BY + --start-time/--end-time");
|
|
8290
|
+
process.exit(1);
|
|
8291
|
+
}
|
|
8292
|
+
const groupBy = opt(rest, "--group-by") || "applicationName";
|
|
8293
|
+
const query = `"${kw.replace(/"/g, "")}" | SELECT ${groupBy}, count(*) AS count GROUP BY ${groupBy} ORDER BY count DESC`;
|
|
8294
|
+
emit(await api.call("sls/raw", {
|
|
8295
|
+
query,
|
|
8296
|
+
...requireEnv(rest),
|
|
8297
|
+
...timeOf(rest, false),
|
|
8298
|
+
line: Number(opt(rest, "--line") || 50)
|
|
8299
|
+
}), jsonMode, rowsSummary(`SLS 全文检索 "${kw}"`));
|
|
8300
|
+
break;
|
|
8301
|
+
}
|
|
8302
|
+
case "sls-logs": {
|
|
8303
|
+
const id = positionals(rest)[0];
|
|
8304
|
+
if (!id) {
|
|
8305
|
+
console.error("用法:bfi ymc sls-logs <trace_id> -e <env>");
|
|
8306
|
+
process.exit(1);
|
|
8307
|
+
}
|
|
8308
|
+
emit(await api.call("sls/logs", {
|
|
8309
|
+
trace_id: id,
|
|
8310
|
+
...requireEnv(rest)
|
|
8311
|
+
}), jsonMode, rowsSummary("SLS 日志"));
|
|
8312
|
+
break;
|
|
8313
|
+
}
|
|
8314
|
+
case "slowsql": {
|
|
8315
|
+
const body = {
|
|
8316
|
+
...requireEnv(rest),
|
|
8317
|
+
...timeOpts(rest),
|
|
8318
|
+
page: Number(opt(rest, "--page") || 1),
|
|
8319
|
+
pageSize: Number(opt(rest, "--page-size") || 20)
|
|
8320
|
+
};
|
|
8321
|
+
for (const [k, name] of [
|
|
8322
|
+
["sqlStr", "--sql-str"],
|
|
8323
|
+
["md5Sql", "--md5"],
|
|
8324
|
+
["traceId", "--trace-id"],
|
|
8325
|
+
["curAppCode", "--app"],
|
|
8326
|
+
["enableProfiler", "--profiler"]
|
|
8327
|
+
]) {
|
|
8328
|
+
const v = opt(rest, name);
|
|
8329
|
+
if (v) body[k] = v;
|
|
8330
|
+
}
|
|
8331
|
+
emit(await api.call("ymc/warningSql", body), jsonMode, (data) => {
|
|
8332
|
+
const d = data;
|
|
8333
|
+
const rows = Array.isArray(d) ? d : d.rows ?? d.data ?? [];
|
|
8334
|
+
const n = rows.length;
|
|
8335
|
+
const lines = [`✓ 慢 SQL: ${n} 条(耗时降序)`];
|
|
8336
|
+
for (const x of rows.slice(0, 5)) {
|
|
8337
|
+
const sql = String(x.sqlStr ?? "").replace(/\s+/g, " ").slice(0, 120);
|
|
8338
|
+
const stack1 = String(x.sqlStack ?? "").split("\n")[0]?.slice(0, 80) ?? "";
|
|
8339
|
+
lines.push(`- ${x.curAppCode} ${x.costTime}ms rows=${x.rowCount ?? "?"} md5=${String(x.md5Sql ?? "").slice(0, 8)} trace=${x.traceId ?? "?"}\n ${sql}\n stack: ${stack1}`);
|
|
8340
|
+
}
|
|
8341
|
+
if (n > 5) lines.push(`… 其余 ${n - 5} 条用 --json 获取`);
|
|
8342
|
+
return lines.join("\n");
|
|
8343
|
+
});
|
|
8344
|
+
break;
|
|
8345
|
+
}
|
|
8346
|
+
case "slowcall": {
|
|
8347
|
+
const body = {
|
|
8348
|
+
...requireEnv(rest),
|
|
8349
|
+
...timeOpts(rest),
|
|
8350
|
+
page: Number(opt(rest, "--page") || 1),
|
|
8351
|
+
pageSize: Number(opt(rest, "--page-size") || 20)
|
|
8352
|
+
};
|
|
8353
|
+
for (const [k, name] of [
|
|
8354
|
+
["traceId", "--trace-id"],
|
|
8355
|
+
["curAppCode", "--app"],
|
|
8356
|
+
["remoteCallMethod", "--method"],
|
|
8357
|
+
["enableProfiler", "--profiler"]
|
|
8358
|
+
]) {
|
|
8359
|
+
const v = opt(rest, name);
|
|
8360
|
+
if (v) body[k] = v;
|
|
8361
|
+
}
|
|
8362
|
+
const cost = opt(rest, "--min-cost");
|
|
8363
|
+
if (cost) body.costTime = [Number(cost), 999999];
|
|
8364
|
+
emit(await api.call("ymc/warningCall", body), jsonMode, (data) => {
|
|
8365
|
+
const d = data;
|
|
8366
|
+
const rows = Array.isArray(d) ? d : d.rows ?? d.data ?? [];
|
|
8367
|
+
const n = rows.length;
|
|
8368
|
+
const lines = [`✓ 慢调用: ${n} 条(耗时降序)`];
|
|
8369
|
+
for (const x of rows.slice(0, 5)) lines.push(`- ${x.curAppCode} ${x.costTime}ms (sql${x.sqlCostTime ?? 0}/rpc${x.rpcCostTime ?? 0}/redis${x.redisCostTime ?? 0})\n ${x.remoteCallMethod ?? "?"} trace=${x.traceId ?? "?"} pod=${x.podId ?? "?"}`);
|
|
8370
|
+
if (n > 5) lines.push(`… 其余 ${n - 5} 条用 --json 获取`);
|
|
8371
|
+
return lines.join("\n");
|
|
8372
|
+
});
|
|
8373
|
+
break;
|
|
8374
|
+
}
|
|
8375
|
+
case "exceptions": {
|
|
8376
|
+
const body = {
|
|
8377
|
+
...requireEnv(rest),
|
|
8378
|
+
...timeOpts(rest),
|
|
8379
|
+
appCode: opt(rest, "--app") || ""
|
|
8380
|
+
};
|
|
8381
|
+
emit(await api.call("ymc/exceptionHistory", body), jsonMode, rowsSummary("异常聚合"));
|
|
8382
|
+
break;
|
|
8383
|
+
}
|
|
8384
|
+
case "alerts": {
|
|
8385
|
+
const body = {
|
|
8386
|
+
...requireEnv(rest),
|
|
8387
|
+
...timeOpts(rest),
|
|
8388
|
+
pageSize: Number(opt(rest, "--page-size") || 10),
|
|
8389
|
+
...opt(rest, "--level") ? { level: opt(rest, "--level") } : {}
|
|
8390
|
+
};
|
|
8391
|
+
emit(await api.call("ymc/alert/detail", body), jsonMode, rowsSummary("告警"));
|
|
8392
|
+
break;
|
|
8393
|
+
}
|
|
8394
|
+
case "url-stats": {
|
|
8395
|
+
const uri = positionals(rest)[0];
|
|
8396
|
+
if (!uri) {
|
|
8397
|
+
console.error("用法:bfi ymc url-stats </uri> -e <env> [-t <time> | --start-time <t> [--end-time <t>]]");
|
|
8398
|
+
process.exit(1);
|
|
8399
|
+
}
|
|
8400
|
+
emit(await api.call("kibana/stats", {
|
|
8401
|
+
uri,
|
|
8402
|
+
...requireEnv(rest),
|
|
8403
|
+
...timeOpts(rest)
|
|
8404
|
+
}), jsonMode, (data) => {
|
|
8405
|
+
const d = data;
|
|
8406
|
+
const a = d.aggs;
|
|
8407
|
+
return `✓ URL 统计: QPS=${a.qps} avg=${a.avg_rt?.value ?? "-"} 慢请求Top=${Array.isArray(d.slow_top) ? d.slow_top.length : 0}`;
|
|
8408
|
+
});
|
|
8409
|
+
break;
|
|
8410
|
+
}
|
|
8411
|
+
case "ingress": {
|
|
8412
|
+
const body = {
|
|
8413
|
+
...requireEnv(rest),
|
|
8414
|
+
...timeOpts(rest),
|
|
8415
|
+
size: Number(opt(rest, "--size") || 100)
|
|
8416
|
+
};
|
|
8417
|
+
const id = opt(rest, "--trace-id");
|
|
8418
|
+
const uri = opt(rest, "--uri");
|
|
8419
|
+
if (id) body.trace_id = id;
|
|
8420
|
+
else if (uri) {
|
|
8421
|
+
body.uri = uri;
|
|
8422
|
+
body.fuzzy = !flag(rest, "--exact");
|
|
8423
|
+
} else {
|
|
8424
|
+
console.error("用法:bfi ymc ingress --trace-id <id> | --uri </path> -e <env> [-t time]");
|
|
8425
|
+
process.exit(1);
|
|
8426
|
+
}
|
|
8427
|
+
emit(await api.call("kibana/search", body), jsonMode, rowsSummary("ingress 日志"));
|
|
8428
|
+
break;
|
|
8429
|
+
}
|
|
8430
|
+
case "dump-list": {
|
|
8431
|
+
const body = {
|
|
8432
|
+
...requireEnv(rest),
|
|
8433
|
+
...timeOpts(rest),
|
|
8434
|
+
monitorType: opt(rest, "--type") || "profiler",
|
|
8435
|
+
appIdList: opt(rest, "--app") ? [opt(rest, "--app")] : []
|
|
8436
|
+
};
|
|
8437
|
+
emit(await api.call("ymc/tool/list", body), jsonMode, rowsSummary("诊断文件"));
|
|
8438
|
+
break;
|
|
8439
|
+
}
|
|
8440
|
+
case "cases": {
|
|
8441
|
+
const sub2 = rest[0] || "search";
|
|
8442
|
+
const rest2 = rest.slice(1);
|
|
8443
|
+
if (sub2 === "search") {
|
|
8444
|
+
const body = {};
|
|
8445
|
+
for (const [k, name] of [
|
|
8446
|
+
["type", "--type"],
|
|
8447
|
+
["fingerprint", "--finger"],
|
|
8448
|
+
["service", "--service"],
|
|
8449
|
+
["keyword", "--kw"]
|
|
8450
|
+
]) {
|
|
8451
|
+
const v = opt(rest2, name);
|
|
8452
|
+
if (v) body[k] = v;
|
|
8453
|
+
}
|
|
8454
|
+
if (opt(rest2, "--limit")) body.limit = Number(opt(rest2, "--limit"));
|
|
8455
|
+
emit(await api.call("cases/search", body), jsonMode, (data) => {
|
|
8456
|
+
const d = data;
|
|
8457
|
+
const list = d.cases || [];
|
|
8458
|
+
if (!list.length) return "(无命中案例)";
|
|
8459
|
+
return `✓ 命中 ${d.count} 条:\n` + list.map((x) => ` [${x.case_id}] (${x.type}/${x.confidence}) ${x.title}\n 指纹: ${JSON.stringify(x.fingerprint)} ${x.env || ""} ${x.service || ""} by ${x.author}`).join("\n");
|
|
8460
|
+
});
|
|
8461
|
+
} else if (sub2 === "show") {
|
|
8462
|
+
const id = positionals(rest2)[0];
|
|
8463
|
+
if (!id) {
|
|
8464
|
+
console.error("用法:bfi ymc cases show <case_id>");
|
|
8465
|
+
process.exit(1);
|
|
8466
|
+
}
|
|
8467
|
+
emit(await api.get(`cases/${id}`), jsonMode);
|
|
8468
|
+
} else if (sub2 === "upload") {
|
|
8469
|
+
const file = opt(rest2, "--file");
|
|
8470
|
+
if (!file) {
|
|
8471
|
+
console.error("用法:bfi ymc cases upload --file <case.md> [--type] [--finger \"a,b\"] [--env] [--service]");
|
|
8472
|
+
process.exit(1);
|
|
8473
|
+
}
|
|
8474
|
+
const { readFileSync } = await import("node:fs");
|
|
8475
|
+
const body = {
|
|
8476
|
+
type: opt(rest2, "--type") || "review",
|
|
8477
|
+
fingerprint: (opt(rest2, "--finger") || "").split(",").map((s) => s.trim()).filter(Boolean),
|
|
8478
|
+
content: readFileSync(file, "utf-8")
|
|
8479
|
+
};
|
|
8480
|
+
for (const [k, name] of [
|
|
8481
|
+
["env", "--env"],
|
|
8482
|
+
["service", "--service"],
|
|
8483
|
+
["confidence", "--confidence"],
|
|
8484
|
+
["title", "--title"]
|
|
8485
|
+
]) {
|
|
8486
|
+
const v = opt(rest2, name);
|
|
8487
|
+
if (v) body[k] = v;
|
|
8488
|
+
}
|
|
8489
|
+
emit(await api.call("cases", body), jsonMode, (data) => `✓ 案例已上传: ${data.case_id}`);
|
|
8490
|
+
} else if (sub2 === "index") emit(await api.get("cases/index/overview"), jsonMode, (data) => {
|
|
8491
|
+
const d = data;
|
|
8492
|
+
const byType = Object.entries(d.by_type || {}).map(([k, v]) => `${k}=${v}`).join(", ");
|
|
8493
|
+
const fps = (d.top_fingerprints || []).map((x) => `${x.fingerprint}(${x.count})`).join(", ");
|
|
8494
|
+
return `✓ 案例总数: ${d.total}\n 类型分布: ${byType}\n 高频指纹: ${fps}`;
|
|
8495
|
+
});
|
|
8496
|
+
else {
|
|
8497
|
+
console.error("用法:bfi ymc cases <search|show|upload|index>");
|
|
8498
|
+
process.exit(1);
|
|
8499
|
+
}
|
|
8500
|
+
break;
|
|
8501
|
+
}
|
|
8502
|
+
case "wiki": {
|
|
8503
|
+
const sub2 = rest[0] || "search";
|
|
8504
|
+
const rest2 = rest.slice(1);
|
|
8505
|
+
const searchBase = (process.env.BFI_WIKI_BASE || YMC_DEFAULT_BASE.replace(/\/api\/v2$/, "/api/v1")) + "/search/";
|
|
8506
|
+
if (sub2 === "search") {
|
|
8507
|
+
const kw = positionals(rest2)[0];
|
|
8508
|
+
if (!kw) {
|
|
8509
|
+
console.error("用法:bfi ymc wiki search \"<关键词>\" [--source confluence|jira|ecfile] [--limit N] [--titles]");
|
|
8510
|
+
process.exit(1);
|
|
8511
|
+
}
|
|
8512
|
+
const body = {
|
|
8513
|
+
query: kw,
|
|
8514
|
+
limit: Number(opt(rest2, "--limit") || 20)
|
|
8515
|
+
};
|
|
8516
|
+
const src = opt(rest2, "--source");
|
|
8517
|
+
if (src) body.sources = [src];
|
|
8518
|
+
if (flag(rest2, "--titles")) body.titles_only = true;
|
|
8519
|
+
const resp = await new Promise((resolve, reject) => {
|
|
8520
|
+
const mod = searchBase.startsWith("https://") ? https : http;
|
|
8521
|
+
const payload = JSON.stringify(body);
|
|
8522
|
+
const req = mod.request(searchBase + "unified", {
|
|
8523
|
+
method: "POST",
|
|
8524
|
+
headers: {
|
|
8525
|
+
"Content-Type": "application/json",
|
|
8526
|
+
Accept: "application/json",
|
|
8527
|
+
"Content-Length": Buffer.byteLength(payload)
|
|
8528
|
+
},
|
|
8529
|
+
timeout: 6e4
|
|
8530
|
+
}, (res) => {
|
|
8531
|
+
const chunks = [];
|
|
8532
|
+
res.on("data", (c) => chunks.push(c));
|
|
8533
|
+
res.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
|
|
8534
|
+
});
|
|
8535
|
+
req.on("timeout", () => req.destroy(/* @__PURE__ */ new Error("超时")));
|
|
8536
|
+
req.on("error", reject);
|
|
8537
|
+
req.write(payload);
|
|
8538
|
+
req.end();
|
|
8539
|
+
});
|
|
8540
|
+
if (jsonMode) {
|
|
8541
|
+
console.log(JSON.stringify(JSON.parse(raw0(resp)), null, 2));
|
|
8542
|
+
break;
|
|
8543
|
+
}
|
|
8544
|
+
const parsed = JSON.parse(raw0(resp));
|
|
8545
|
+
if (parsed.code !== 0) {
|
|
8546
|
+
console.error(`✗ ${parsed.msg}`);
|
|
8547
|
+
process.exit(1);
|
|
8548
|
+
}
|
|
8549
|
+
console.log(JSON.stringify(parsed.data, null, 2));
|
|
8550
|
+
} else if (sub2 === "show") {
|
|
8551
|
+
const id = positionals(rest2)[0];
|
|
8552
|
+
const sourceName = opt(rest2, "--source") || "gfjira";
|
|
8553
|
+
if (!id) {
|
|
8554
|
+
console.error("用法:bfi ymc wiki show <id> [--source wiki|uap-wiki|gfwiki|gfjira|ecfile]");
|
|
8555
|
+
process.exit(1);
|
|
8556
|
+
}
|
|
8557
|
+
const resp = await new Promise((resolve, reject) => {
|
|
8558
|
+
const mod = searchBase.startsWith("https://") ? https : http;
|
|
8559
|
+
const payload = JSON.stringify({
|
|
8560
|
+
source_name: sourceName,
|
|
8561
|
+
id,
|
|
8562
|
+
offset: 0
|
|
8563
|
+
});
|
|
8564
|
+
const req = mod.request(searchBase + "detail", {
|
|
8565
|
+
method: "POST",
|
|
8566
|
+
headers: {
|
|
8567
|
+
"Content-Type": "application/json",
|
|
8568
|
+
Accept: "application/json",
|
|
8569
|
+
"Content-Length": Buffer.byteLength(payload)
|
|
8570
|
+
},
|
|
8571
|
+
timeout: 6e4
|
|
8572
|
+
}, (res) => {
|
|
8573
|
+
const chunks = [];
|
|
8574
|
+
res.on("data", (c) => chunks.push(c));
|
|
8575
|
+
res.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
|
|
8576
|
+
});
|
|
8577
|
+
req.on("timeout", () => req.destroy(/* @__PURE__ */ new Error("超时")));
|
|
8578
|
+
req.on("error", reject);
|
|
8579
|
+
req.write(payload);
|
|
8580
|
+
req.end();
|
|
8581
|
+
});
|
|
8582
|
+
if (jsonMode) {
|
|
8583
|
+
console.log(JSON.stringify(JSON.parse(raw0(resp)), null, 2));
|
|
8584
|
+
break;
|
|
8585
|
+
}
|
|
8586
|
+
const parsed = JSON.parse(raw0(resp));
|
|
8587
|
+
if (parsed.code !== 0) {
|
|
8588
|
+
console.error(`✗ ${parsed.msg}`);
|
|
8589
|
+
process.exit(1);
|
|
8590
|
+
}
|
|
8591
|
+
const d = parsed.data;
|
|
8592
|
+
const text = String(d.content ?? d.text ?? JSON.stringify(d));
|
|
8593
|
+
console.log(text.length > 4e3 ? `${text.slice(0, 4e3)}\n…(截断,--json 看全量)` : text);
|
|
8594
|
+
} else {
|
|
8595
|
+
console.error("用法:bfi ymc wiki <search|show>");
|
|
8596
|
+
process.exit(1);
|
|
8597
|
+
}
|
|
8598
|
+
break;
|
|
8599
|
+
}
|
|
8600
|
+
case "callstat":
|
|
8601
|
+
emit(await api.call("ymc/warningCallstat", {
|
|
8602
|
+
...requireEnv(rest),
|
|
8603
|
+
...timeOpts(rest),
|
|
8604
|
+
page: Number(opt(rest, "--page") || 1),
|
|
8605
|
+
pageSize: Number(opt(rest, "--page-size") || 20)
|
|
8606
|
+
}), jsonMode, rowsSummary("慢调用统计"));
|
|
8607
|
+
break;
|
|
8608
|
+
case "sqlstat":
|
|
8609
|
+
emit(await api.call("ymc/warningSqlstat", {
|
|
8610
|
+
...requireEnv(rest),
|
|
8611
|
+
...timeOpts(rest),
|
|
8612
|
+
page: Number(opt(rest, "--page") || 1),
|
|
8613
|
+
pageSize: Number(opt(rest, "--page-size") || 20)
|
|
8614
|
+
}), jsonMode, rowsSummary("慢 SQL 统计"));
|
|
8615
|
+
break;
|
|
8616
|
+
case "events": {
|
|
8617
|
+
const body = {
|
|
8618
|
+
...requireEnv(rest),
|
|
8619
|
+
type: opt(rest, "--type") || "",
|
|
8620
|
+
startTime: opt(rest, "--start-time") || "",
|
|
8621
|
+
endTime: opt(rest, "--end-time") || "",
|
|
8622
|
+
page: Number(opt(rest, "--page") || 1),
|
|
8623
|
+
pageSize: Number(opt(rest, "--page-size") || 10),
|
|
8624
|
+
appCode: opt(rest, "--app") || "",
|
|
8625
|
+
pipelineCode: opt(rest, "--pipeline") || ""
|
|
8626
|
+
};
|
|
8627
|
+
const realPath = flag(rest, "--histogram") ? "ymc/eventHistograms" : "ymc/eventLogs";
|
|
8628
|
+
emit(await api.call(realPath, body), jsonMode, rowsSummary(flag(rest, "--histogram") ? "事件直方图" : "集群事件"));
|
|
8629
|
+
break;
|
|
8630
|
+
}
|
|
8631
|
+
case "log-url": {
|
|
8632
|
+
const body = {
|
|
8633
|
+
...requireEnv(rest),
|
|
8634
|
+
type: opt(rest, "--type") || "ingressLog",
|
|
8635
|
+
appId: opt(rest, "--app") || ""
|
|
8636
|
+
};
|
|
8637
|
+
const ids = opt(rest, "--filter-app");
|
|
8638
|
+
if (ids) body.filterCondition = { appIds: ids.split(",").map((s) => s.trim()) };
|
|
8639
|
+
emit(await api.call("ymc/aliyun-log/url", body), jsonMode, (data) => `✓ 日志直通 URL 已生成(data 内含 url,浏览器可直接打开)`);
|
|
8640
|
+
break;
|
|
8641
|
+
}
|
|
8642
|
+
case "sample-list":
|
|
8643
|
+
emit(await api.call("ymc/sample/list", {
|
|
8644
|
+
...requireEnv(rest),
|
|
8645
|
+
...timeOpts(rest)
|
|
8646
|
+
}), jsonMode, rowsSummary("采样数据"));
|
|
8647
|
+
break;
|
|
8648
|
+
case "rca": {
|
|
8649
|
+
const body = {
|
|
8650
|
+
...requireEnv(rest),
|
|
8651
|
+
...timeOpts(rest)
|
|
8652
|
+
};
|
|
8653
|
+
for (const [k, name] of [["appId", "--app"], ["appCode", "--app-code"]]) {
|
|
8654
|
+
const v = opt(rest, name);
|
|
8655
|
+
if (v) body[k] = v;
|
|
8656
|
+
}
|
|
8657
|
+
emit(await api.call("ymc/rca/full-report", body, 3e5), jsonMode, () => "✓ RCA 报告已生成(--json 查看全文)");
|
|
8658
|
+
break;
|
|
8659
|
+
}
|
|
8660
|
+
case "bpr-download": {
|
|
8661
|
+
const pk = positionals(rest)[0];
|
|
8662
|
+
if (!pk) {
|
|
8663
|
+
console.error("用法:bfi ymc bpr-download <pk> -e <env> [-o 输出文件]");
|
|
8664
|
+
console.error("例:bfi ymc bpr-download eopppmokijpjoflfmodjffojcohiflgm -e pre -o ~/bpr.html");
|
|
8665
|
+
console.error("pk 来源:bfi ymc call bpr/list --data '{\"page\":1,\"pageSize\":10}' -e <env>");
|
|
8666
|
+
process.exit(1);
|
|
8667
|
+
}
|
|
8668
|
+
const out = opt(rest, "-o", "--out") || `bpr_${pk.slice(0, 16)}.html`;
|
|
8669
|
+
try {
|
|
8670
|
+
const n = await api.download("ymc/bpr/download", {
|
|
8671
|
+
...requireEnv(rest),
|
|
8672
|
+
pk
|
|
8673
|
+
}, out);
|
|
8674
|
+
console.log(`✓ 已下载 ${n} 字节 → ${out}`);
|
|
8675
|
+
} catch (e) {
|
|
8676
|
+
console.error(`✗ ${e.message}`);
|
|
8677
|
+
process.exit(1);
|
|
8678
|
+
}
|
|
8679
|
+
break;
|
|
8680
|
+
}
|
|
8681
|
+
case "call": {
|
|
8682
|
+
const route = positionals(rest)[0];
|
|
8683
|
+
const dataStr = opt(rest, "--data") || "{}";
|
|
8684
|
+
if (!route) {
|
|
8685
|
+
console.error("用法:bfi ymc call <route> -e <env> [--data '{\"k\":\"v\"}'] [--timeout 秒] [-t time | --start-time <s> --end-time <e>]");
|
|
8686
|
+
console.error("route 例:warningCall/warningSql/exceptionHistory/bpr/list/alert/detail/uba/query/tool/list/environment ...");
|
|
8687
|
+
console.error("完整清单见 http://172.20.35.178:8901/docs");
|
|
8688
|
+
process.exit(1);
|
|
8689
|
+
}
|
|
8690
|
+
let extra;
|
|
8691
|
+
try {
|
|
8692
|
+
extra = JSON.parse(dataStr);
|
|
8693
|
+
} catch {
|
|
8694
|
+
console.error("--data 不是合法 JSON");
|
|
8695
|
+
process.exit(1);
|
|
8696
|
+
}
|
|
8697
|
+
const timeoutArg = opt(rest, "--timeout");
|
|
8698
|
+
const timeoutMs = timeoutArg ? Number(timeoutArg) * 1e3 : void 0;
|
|
8699
|
+
emit(await api.call(`ymc/${route}`, {
|
|
8700
|
+
...requireEnv(rest),
|
|
8701
|
+
...timeOpts(rest),
|
|
8702
|
+
...extra
|
|
8703
|
+
}, timeoutMs), jsonMode);
|
|
8704
|
+
break;
|
|
8705
|
+
}
|
|
8706
|
+
default:
|
|
8707
|
+
console.error(`未知子命令:${sub}`);
|
|
8708
|
+
printHelp();
|
|
8709
|
+
process.exit(1);
|
|
8710
|
+
}
|
|
8711
|
+
}
|
|
8712
|
+
function printHelp() {
|
|
8713
|
+
console.log(`
|
|
8714
|
+
bfi ymc — 观测查询(v2 API:YMC/Kibana/SLS/trace)
|
|
8715
|
+
|
|
8716
|
+
命令:
|
|
8717
|
+
status 登录会话状态(dev/prod)
|
|
8718
|
+
trace <trace_id> -e <env> 三源合一(SLS+链路+ingress)
|
|
8719
|
+
chain <trace_id> -e <env> 原子源:warningChain 链路
|
|
8720
|
+
sls-logs <trace_id> -e <env> 原子源:SLS 业务日志
|
|
8721
|
+
sls-raw "<查询>" -e <env> SLS 原始查询(完整查询语法透传)
|
|
8722
|
+
sls-grep "<关键词>" -e <env> SLS 全文检索聚合(异常历史分布取证)
|
|
8723
|
+
slowsql -t <time> [-e] [--sql-str] [--md5] [--trace-id] [--app] [--profiler]
|
|
8724
|
+
slowcall -t <time> [-e] [--trace-id] [--app] [--method] [--min-cost ms] [--profiler]
|
|
8725
|
+
exceptions -t <time> [-e] [--app]
|
|
8726
|
+
alerts -t <time> [-e] [--level error|warn|info]
|
|
8727
|
+
url-stats </uri> -t <time> -e <env>
|
|
8728
|
+
ingress --trace-id <id> | --uri </path> [-t <time>] -e <env> [--exact]
|
|
8729
|
+
dump-list [-t <time>] -e <env> [--app] [--type profiler]
|
|
8730
|
+
cases <search|show|upload|index> 案例库(KEDB)
|
|
8731
|
+
wiki search "<kw>" [--source] [--titles] 知识库统一检索(wiki/jira/文库)
|
|
8732
|
+
wiki show <id> [--source] 知识条目全文
|
|
8733
|
+
call <route> -e <env> [--data '<json>'] 通用透传(全部注册表端点)
|
|
8734
|
+
|
|
8735
|
+
通用参数:
|
|
8736
|
+
-e, --env 环境(${ENV_DOC})
|
|
8737
|
+
-t, --time 时间简写(10m/2h/3d/4w = 过去 N 单位到现在)
|
|
8738
|
+
--start-time <t> 开始时间 yyyy-MM-dd HH:mm:ss(与 -t 二选一;只传它=到当前)
|
|
8739
|
+
--end-time <t> 结束时间(须与 --start-time 同传)
|
|
8740
|
+
--json 输出原始 JSON
|
|
8741
|
+
--token 覆盖已登录 token(默认用 bfi auth login 的)
|
|
8742
|
+
`);
|
|
8743
|
+
}
|
|
7438
8744
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7439
8745
|
function getVersion() {
|
|
7440
8746
|
try {
|
|
@@ -7527,11 +8833,37 @@ ${BOLD}━━━ 技能包管理 ━━━${RESET}
|
|
|
7527
8833
|
skills package add <pkg> <skill...> 添加技能到包
|
|
7528
8834
|
skills package remove <pkg> <skill...> 从包移除技能
|
|
7529
8835
|
|
|
8836
|
+
${BOLD}━━━ 插件管理 ━━━${RESET}
|
|
8837
|
+
plugin create <name> [--with ...] 脚手架一个插件目录
|
|
8838
|
+
plugin validate [path] 校验插件结构
|
|
8839
|
+
plugin publish <dir|zip|tgz> 发布插件到 SkillHub
|
|
8840
|
+
plugin list 列出插件
|
|
8841
|
+
plugin info <id> 查看插件详情
|
|
8842
|
+
plugin install <slug|id|file.zip> 安装插件(名称 / ID / 本地压缩包)
|
|
8843
|
+
|
|
8844
|
+
${BOLD}━━━ MCP 服务器 ━━━${RESET}
|
|
8845
|
+
mcp list 列出 MCP 服务器
|
|
8846
|
+
mcp info <id> 查看详情
|
|
8847
|
+
mcp config <id> 打印 .mcp.json 配置
|
|
8848
|
+
mcp add <id> 写入本地 .mcp.json
|
|
8849
|
+
mcp remove <name> 从本地 .mcp.json 移除
|
|
8850
|
+
|
|
7530
8851
|
${BOLD}━━━ 文档 ━━━${RESET}
|
|
7531
8852
|
docs upload <file> 上传文档
|
|
7532
8853
|
docs list 列出文档
|
|
7533
8854
|
docs info <id> 查看文档详情
|
|
7534
8855
|
|
|
8856
|
+
${BOLD}━━━ 观测查询(ymc)━━━${RESET}
|
|
8857
|
+
ymc status 登录会话状态
|
|
8858
|
+
ymc trace <trace_id> -e <env> traceId 三源合一(SLS+链路+ingress)
|
|
8859
|
+
ymc chain / sls-logs <id> 原子源:链路 / SLS 日志
|
|
8860
|
+
ymc slowsql / slowcall 慢 SQL / 慢调用(-t 时间简写)
|
|
8861
|
+
ymc exceptions / alerts 异常聚合 / 告警明细
|
|
8862
|
+
ymc url-stats <uri> -t <time> URL 性能统计
|
|
8863
|
+
ymc ingress --trace-id|--uri ingress 访问日志
|
|
8864
|
+
ymc dump-list / call <route> 诊断文件 / 通用端点透传
|
|
8865
|
+
(-e env: test/daily/pre/core1-4/commldev;-t: 10m/2h/3d/4w)
|
|
8866
|
+
|
|
7535
8867
|
${BOLD}━━━ 配置 ━━━${RESET}
|
|
7536
8868
|
config get [key] 查看配置
|
|
7537
8869
|
config set <key> <value> [--global] 设置配置
|
|
@@ -7796,6 +9128,9 @@ async function main() {
|
|
|
7796
9128
|
setAgents(await resolveAgents(cfg.token ? new SkillHubAPI(cfg.registry, cfg.token) : void 0));
|
|
7797
9129
|
} catch {}
|
|
7798
9130
|
switch (command) {
|
|
9131
|
+
case "ymc":
|
|
9132
|
+
await runYmc(restArgs, globalOpts);
|
|
9133
|
+
break;
|
|
7799
9134
|
case "auth":
|
|
7800
9135
|
if (restArgs[0] === "logout") await runLogout();
|
|
7801
9136
|
else if (restArgs[0] === "whoami") await runWhoami(globalOpts);
|
|
@@ -7828,6 +9163,12 @@ async function main() {
|
|
|
7828
9163
|
case "pkg":
|
|
7829
9164
|
await runPackage(restArgs, globalOpts);
|
|
7830
9165
|
break;
|
|
9166
|
+
case "plugin":
|
|
9167
|
+
await runPlugin(restArgs, globalOpts);
|
|
9168
|
+
break;
|
|
9169
|
+
case "mcp":
|
|
9170
|
+
await runMcp(restArgs, globalOpts);
|
|
9171
|
+
break;
|
|
7831
9172
|
case "log":
|
|
7832
9173
|
await runLog(restArgs, globalOpts);
|
|
7833
9174
|
break;
|
|
@@ -7906,5 +9247,8 @@ async function main() {
|
|
|
7906
9247
|
console.log(`运行 ${BOLD}bfi --help${RESET} 查看用法。`);
|
|
7907
9248
|
}
|
|
7908
9249
|
}
|
|
7909
|
-
main().
|
|
9250
|
+
main().catch((e) => {
|
|
9251
|
+
console.error(`✗ ${e?.message || e}`);
|
|
9252
|
+
process.exit(1);
|
|
9253
|
+
});
|
|
7910
9254
|
export { loadGlobalConfig };
|