bfi-cli 0.1.57 → 0.1.64
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/_chunks/agents2.mjs +6 -2
- package/dist/cli.mjs +2029 -497
- 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
|
|
@@ -2796,12 +2941,14 @@ async function tryBlobInstall(ownerRepo, options = {}) {
|
|
|
2796
2941
|
};
|
|
2797
2942
|
}
|
|
2798
2943
|
var add_exports = /* @__PURE__ */ __exportAll({
|
|
2944
|
+
ensureUniversalAgents: () => ensureUniversalAgents,
|
|
2799
2945
|
formatEveInstallPromptMessage: () => formatEveInstallPromptMessage,
|
|
2800
2946
|
getLockSource: () => getLockSource,
|
|
2801
2947
|
getProjectLockSourceUrl: () => getProjectLockSourceUrl,
|
|
2802
2948
|
parseAddOptions: () => parseAddOptions,
|
|
2803
2949
|
promptForAgents: () => promptForAgents,
|
|
2804
|
-
runAdd: () => runAdd
|
|
2950
|
+
runAdd: () => runAdd,
|
|
2951
|
+
selectAgentsInteractive: () => selectAgentsInteractive
|
|
2805
2952
|
});
|
|
2806
2953
|
const isCancelled = (value) => typeof value === "symbol";
|
|
2807
2954
|
const EVE_AGENT_LABEL = "eve agent";
|
|
@@ -2876,20 +3023,20 @@ function computeSingleFileSkillHash(contents) {
|
|
|
2876
3023
|
hash.update(contents);
|
|
2877
3024
|
return hash.digest("hex");
|
|
2878
3025
|
}
|
|
2879
|
-
function formatList$
|
|
3026
|
+
function formatList$2(items, maxShow = 5) {
|
|
2880
3027
|
if (items.length <= maxShow) return items.join(", ");
|
|
2881
3028
|
const shown = items.slice(0, maxShow);
|
|
2882
3029
|
const remaining = items.length - maxShow;
|
|
2883
3030
|
return `${shown.join(", ")} +${remaining} more`;
|
|
2884
3031
|
}
|
|
2885
3032
|
function formatSkillPromptSubject(skills) {
|
|
2886
|
-
const namedSubject = formatList$
|
|
3033
|
+
const namedSubject = formatList$2(skills.map((skill) => import_picocolors.default.cyan(getSkillDisplayName(skill))), 3);
|
|
2887
3034
|
return stripTerminalEscapes(namedSubject).length <= 80 ? namedSubject : `${skills.length} selected skills`;
|
|
2888
3035
|
}
|
|
2889
3036
|
function formatEveInstallPromptMessage(skills) {
|
|
2890
3037
|
return `Detected an eve project. Install ${formatSkillPromptSubject(skills)} for your ${EVE_AGENT_LABEL} to use?`;
|
|
2891
3038
|
}
|
|
2892
|
-
function splitAgentsByType(agentTypes) {
|
|
3039
|
+
function splitAgentsByType$1(agentTypes) {
|
|
2893
3040
|
const universal = [];
|
|
2894
3041
|
const symlinked = [];
|
|
2895
3042
|
for (const a of agentTypes) if (isUniversalAgent(a)) universal.push(agents[a].displayName);
|
|
@@ -2901,29 +3048,29 @@ function splitAgentsByType(agentTypes) {
|
|
|
2901
3048
|
}
|
|
2902
3049
|
function buildAgentSummaryLines(targetAgents, installMode, selectedAgents) {
|
|
2903
3050
|
const lines = [];
|
|
2904
|
-
const { universal, symlinked } = splitAgentsByType(targetAgents);
|
|
3051
|
+
const { universal, symlinked } = splitAgentsByType$1(targetAgents);
|
|
2905
3052
|
const selectedSet = new Set(selectedAgents || targetAgents);
|
|
2906
3053
|
if (installMode === "symlink") {
|
|
2907
3054
|
const selectedUniversal = universal.filter((a) => selectedSet.has(a));
|
|
2908
3055
|
const selectedSymlink = symlinked.filter((a) => selectedSet.has(a));
|
|
2909
|
-
if (selectedUniversal.length > 0) lines.push(` ${import_picocolors.default.green("共享目录:")} ${formatList$
|
|
2910
|
-
if (selectedSymlink.length > 0) lines.push(` ${import_picocolors.default.dim("符号链接 →")} ${formatList$
|
|
3056
|
+
if (selectedUniversal.length > 0) lines.push(` ${import_picocolors.default.green("共享目录:")} ${formatList$2(selectedUniversal)}`);
|
|
3057
|
+
if (selectedSymlink.length > 0) lines.push(` ${import_picocolors.default.dim("符号链接 →")} ${formatList$2(selectedSymlink)}`);
|
|
2911
3058
|
const otherUniversal = universal.filter((a) => !selectedSet.has(a));
|
|
2912
3059
|
if (otherUniversal.length > 0) lines.push(` ${import_picocolors.default.dim(`(另外 ${otherUniversal.length} 个使用共享目录的 Agent 也会自动获得此技能)`)}`);
|
|
2913
3060
|
} else {
|
|
2914
3061
|
const allNames = targetAgents.filter((a) => selectedSet.has(a)).map((a) => agents[a].displayName);
|
|
2915
|
-
lines.push(` ${import_picocolors.default.dim("复制 →")} ${formatList$
|
|
3062
|
+
lines.push(` ${import_picocolors.default.dim("复制 →")} ${formatList$2(allNames)}`);
|
|
2916
3063
|
}
|
|
2917
3064
|
return lines;
|
|
2918
3065
|
}
|
|
2919
|
-
function targetDisplayName(target) {
|
|
3066
|
+
function targetDisplayName$1(target) {
|
|
2920
3067
|
const base = agents[target.agent].displayName;
|
|
2921
3068
|
return target.subagent ? `${base} (${target.subagent})` : base;
|
|
2922
3069
|
}
|
|
2923
|
-
function targetKey(target) {
|
|
3070
|
+
function targetKey$1(target) {
|
|
2924
3071
|
return target.subagent ? `${target.agent}:${target.subagent}` : target.agent;
|
|
2925
3072
|
}
|
|
2926
|
-
function buildInstallTargets(targetAgents, eveSubagentTargets) {
|
|
3073
|
+
function buildInstallTargets$1(targetAgents, eveSubagentTargets) {
|
|
2927
3074
|
const targets = [];
|
|
2928
3075
|
for (const agent of targetAgents) if (agent === "eve") for (const subagent of eveSubagentTargets) targets.push({
|
|
2929
3076
|
agent,
|
|
@@ -2932,18 +3079,18 @@ function buildInstallTargets(targetAgents, eveSubagentTargets) {
|
|
|
2932
3079
|
else targets.push({ agent });
|
|
2933
3080
|
return targets;
|
|
2934
3081
|
}
|
|
2935
|
-
function buildTargetSummaryLines(targets, installMode) {
|
|
3082
|
+
function buildTargetSummaryLines$1(targets, installMode) {
|
|
2936
3083
|
const lines = [];
|
|
2937
3084
|
const rootAgents = targets.filter((t) => !t.subagent).map((t) => t.agent);
|
|
2938
|
-
const subagentNames = targets.filter((t) => t.subagent).map(targetDisplayName);
|
|
2939
|
-
const { universal, symlinked } = splitAgentsByType(rootAgents);
|
|
3085
|
+
const subagentNames = targets.filter((t) => t.subagent).map(targetDisplayName$1);
|
|
3086
|
+
const { universal, symlinked } = splitAgentsByType$1(rootAgents);
|
|
2940
3087
|
if (installMode === "symlink") {
|
|
2941
|
-
if (universal.length > 0) lines.push(` ${import_picocolors.default.green("共享目录:")} ${formatList$
|
|
2942
|
-
if (symlinked.length > 0) lines.push(` ${import_picocolors.default.dim("符号链接 →")} ${formatList$
|
|
2943
|
-
if (subagentNames.length > 0) lines.push(` ${import_picocolors.default.dim("copy →")} ${formatList$
|
|
3088
|
+
if (universal.length > 0) lines.push(` ${import_picocolors.default.green("共享目录:")} ${formatList$2(universal, 3)}`);
|
|
3089
|
+
if (symlinked.length > 0) lines.push(` ${import_picocolors.default.dim("符号链接 →")} ${formatList$2(symlinked)}`);
|
|
3090
|
+
if (subagentNames.length > 0) lines.push(` ${import_picocolors.default.dim("copy →")} ${formatList$2(subagentNames)}`);
|
|
2944
3091
|
} else {
|
|
2945
|
-
const allNames = targets.map(targetDisplayName);
|
|
2946
|
-
lines.push(` ${import_picocolors.default.dim("copy →")} ${formatList$
|
|
3092
|
+
const allNames = targets.map(targetDisplayName$1);
|
|
3093
|
+
lines.push(` ${import_picocolors.default.dim("copy →")} ${formatList$2(allNames)}`);
|
|
2947
3094
|
}
|
|
2948
3095
|
return lines;
|
|
2949
3096
|
}
|
|
@@ -2955,12 +3102,12 @@ function ensureUniversalAgents(targetAgents) {
|
|
|
2955
3102
|
}
|
|
2956
3103
|
function buildResultLines(results, targetAgents) {
|
|
2957
3104
|
const lines = [];
|
|
2958
|
-
const { universal, symlinked: symlinkAgents } = splitAgentsByType(targetAgents);
|
|
3105
|
+
const { universal, symlinked: symlinkAgents } = splitAgentsByType$1(targetAgents);
|
|
2959
3106
|
const successfulSymlinks = results.filter((r) => !r.symlinkFailed && !r.skipped && !universal.includes(r.agent)).map((r) => r.agent);
|
|
2960
3107
|
const failedSymlinks = results.filter((r) => r.symlinkFailed && !r.skipped).map((r) => r.agent);
|
|
2961
|
-
if (universal.length > 0) lines.push(` ${import_picocolors.default.green("共享目录:")} ${formatList$
|
|
2962
|
-
if (successfulSymlinks.length > 0) lines.push(` ${import_picocolors.default.dim("符号链接:")} ${formatList$
|
|
2963
|
-
if (failedSymlinks.length > 0) lines.push(` ${import_picocolors.default.yellow("copied:")} ${formatList$
|
|
3108
|
+
if (universal.length > 0) lines.push(` ${import_picocolors.default.green("共享目录:")} ${formatList$2(universal)}`);
|
|
3109
|
+
if (successfulSymlinks.length > 0) lines.push(` ${import_picocolors.default.dim("符号链接:")} ${formatList$2(successfulSymlinks)}`);
|
|
3110
|
+
if (failedSymlinks.length > 0) lines.push(` ${import_picocolors.default.yellow("copied:")} ${formatList$2(failedSymlinks)}`);
|
|
2964
3111
|
return lines;
|
|
2965
3112
|
}
|
|
2966
3113
|
function multiselect(opts) {
|
|
@@ -3215,7 +3362,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
3215
3362
|
if (skill.files.size > 1) summaryLines.push(` ${import_picocolors.default.dim("files:")} ${skill.files.size}`);
|
|
3216
3363
|
const skillOverwrites = overwriteStatus.get(skill.installName);
|
|
3217
3364
|
const overwriteAgents = targetAgents.filter((a) => skillOverwrites?.get(a)).map((a) => agents[a].displayName);
|
|
3218
|
-
if (overwriteAgents.length > 0) summaryLines.push(` ${import_picocolors.default.yellow("overwrites:")} ${formatList$
|
|
3365
|
+
if (overwriteAgents.length > 0) summaryLines.push(` ${import_picocolors.default.yellow("overwrites:")} ${formatList$2(overwriteAgents)}`);
|
|
3219
3366
|
}
|
|
3220
3367
|
console.log();
|
|
3221
3368
|
note(summaryLines.join("\n"), "安装摘要");
|
|
@@ -3304,7 +3451,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
3304
3451
|
const title = import_picocolors.default.green(`已安装 ${skillCount} 个技能`);
|
|
3305
3452
|
note(resultLines.join("\n"), title);
|
|
3306
3453
|
if (symlinkFailures.length > 0) {
|
|
3307
|
-
log.warn(import_picocolors.default.yellow(`符号链接失败: ${formatList$
|
|
3454
|
+
log.warn(import_picocolors.default.yellow(`符号链接失败: ${formatList$2(copiedAgents)}`));
|
|
3308
3455
|
log.message(import_picocolors.default.dim(" 已改为复制文件。在 Windows 上,请启用开发者模式以支持符号链接。"));
|
|
3309
3456
|
}
|
|
3310
3457
|
}
|
|
@@ -3500,10 +3647,10 @@ async function runAdd(args, options = {}) {
|
|
|
3500
3647
|
else if (!process.stdin.isTTY) showInstallTip();
|
|
3501
3648
|
let tempDir = null;
|
|
3502
3649
|
try {
|
|
3503
|
-
const spinner$
|
|
3504
|
-
spinner$
|
|
3650
|
+
const spinner$3 = spinner();
|
|
3651
|
+
spinner$3.start("正在解析源...");
|
|
3505
3652
|
const parsed = parseSource(source);
|
|
3506
|
-
spinner$
|
|
3653
|
+
spinner$3.stop(`源:${parsed.type === "local" ? parsed.localPath : parsed.url}${parsed.ref ? ` @ ${import_picocolors.default.yellow(parsed.ref)}` : ""}${parsed.subpath ? ` (${parsed.subpath})` : ""}${parsed.skillFilter ? ` ${import_picocolors.default.dim("@")}${import_picocolors.default.cyan(parsed.skillFilter)}` : ""}`);
|
|
3507
3654
|
const ownerRepoRaw = getOwnerRepo(parsed);
|
|
3508
3655
|
const repoPrivacyPromise = (() => {
|
|
3509
3656
|
if (!ownerRepoRaw) return Promise.resolve(null);
|
|
@@ -3512,7 +3659,7 @@ async function runAdd(args, options = {}) {
|
|
|
3512
3659
|
return isRepoPrivate(ownerRepo.owner, ownerRepo.repo).catch(() => null);
|
|
3513
3660
|
})();
|
|
3514
3661
|
if (parsed.type === "well-known") {
|
|
3515
|
-
await handleWellKnownSkills(source, parsed.url, options, spinner$
|
|
3662
|
+
await handleWellKnownSkills(source, parsed.url, options, spinner$3);
|
|
3516
3663
|
return;
|
|
3517
3664
|
}
|
|
3518
3665
|
if (parsed.skillFilter) {
|
|
@@ -3523,18 +3670,18 @@ async function runAdd(args, options = {}) {
|
|
|
3523
3670
|
let skills;
|
|
3524
3671
|
let blobResult = null;
|
|
3525
3672
|
if (parsed.type === "skillhub") {
|
|
3526
|
-
const result = await installFromSkillHub(parsed, options, spinner$
|
|
3673
|
+
const result = await installFromSkillHub(parsed, options, spinner$3);
|
|
3527
3674
|
skills = result.skills;
|
|
3528
3675
|
tempDir = result.tempDir;
|
|
3529
3676
|
} else if (parsed.type === "local") {
|
|
3530
|
-
spinner$
|
|
3677
|
+
spinner$3.start("正在验证本地路径...");
|
|
3531
3678
|
if (!existsSync(parsed.localPath)) {
|
|
3532
|
-
spinner$
|
|
3679
|
+
spinner$3.stop(import_picocolors.default.red("路径未找到"));
|
|
3533
3680
|
outro(import_picocolors.default.red(`本地路径不存在:${parsed.localPath}`));
|
|
3534
3681
|
process.exit(1);
|
|
3535
3682
|
}
|
|
3536
|
-
spinner$
|
|
3537
|
-
spinner$
|
|
3683
|
+
spinner$3.stop("本地路径验证通过");
|
|
3684
|
+
spinner$3.start("正在发现技能...");
|
|
3538
3685
|
skills = await discoverSkills(parsed.localPath, parsed.subpath, {
|
|
3539
3686
|
includeInternal,
|
|
3540
3687
|
fullDepth: options.fullDepth
|
|
@@ -3549,7 +3696,7 @@ async function runAdd(args, options = {}) {
|
|
|
3549
3696
|
const owner = ownerRepo?.split("/")[0]?.toLowerCase();
|
|
3550
3697
|
const isSelfHostedRepo = !!ownerRepo && Object.hasOwn(BLOB_ALLOWED_REPOS, ownerRepo.toLowerCase());
|
|
3551
3698
|
if (ownerRepo && owner && (isSelfHostedRepo || BLOB_ALLOWED_OWNERS.includes(owner))) {
|
|
3552
|
-
spinner$
|
|
3699
|
+
spinner$3.start("正在获取技能...");
|
|
3553
3700
|
blobResult = await tryBlobInstall(ownerRepo, {
|
|
3554
3701
|
subpath: parsed.subpath,
|
|
3555
3702
|
skillFilter: parsed.skillFilter,
|
|
@@ -3557,36 +3704,36 @@ async function runAdd(args, options = {}) {
|
|
|
3557
3704
|
getToken: getGitHubToken,
|
|
3558
3705
|
includeInternal
|
|
3559
3706
|
});
|
|
3560
|
-
if (!blobResult) spinner$
|
|
3707
|
+
if (!blobResult) spinner$3.stop(import_picocolors.default.dim("Falling back to clone..."));
|
|
3561
3708
|
}
|
|
3562
3709
|
if (blobResult) {
|
|
3563
3710
|
skills = blobResult.skills;
|
|
3564
|
-
spinner$
|
|
3711
|
+
spinner$3.stop(`Found ${import_picocolors.default.green(skills.length)} skill${skills.length > 1 ? "s" : ""}`);
|
|
3565
3712
|
} else {
|
|
3566
|
-
tempDir = await cloneWithGitHubFallback(parsed.url, options.branch || parsed.ref, spinner$
|
|
3567
|
-
spinner$
|
|
3568
|
-
spinner$
|
|
3713
|
+
tempDir = await cloneWithGitHubFallback(parsed.url, options.branch || parsed.ref, spinner$3);
|
|
3714
|
+
spinner$3.stop("仓库克隆完成");
|
|
3715
|
+
spinner$3.start("正在发现技能...");
|
|
3569
3716
|
skills = await discoverSkills(tempDir, parsed.subpath, {
|
|
3570
3717
|
includeInternal,
|
|
3571
3718
|
fullDepth: options.fullDepth
|
|
3572
3719
|
});
|
|
3573
3720
|
}
|
|
3574
3721
|
} else {
|
|
3575
|
-
tempDir = await cloneWithGitHubFallback(parsed.url, options.branch || parsed.ref, spinner$
|
|
3576
|
-
spinner$
|
|
3577
|
-
spinner$
|
|
3722
|
+
tempDir = await cloneWithGitHubFallback(parsed.url, options.branch || parsed.ref, spinner$3);
|
|
3723
|
+
spinner$3.stop("仓库克隆完成");
|
|
3724
|
+
spinner$3.start("正在发现技能...");
|
|
3578
3725
|
skills = await discoverSkills(tempDir, parsed.subpath, {
|
|
3579
3726
|
includeInternal,
|
|
3580
3727
|
fullDepth: options.fullDepth
|
|
3581
3728
|
});
|
|
3582
3729
|
}
|
|
3583
3730
|
if (skills.length === 0) {
|
|
3584
|
-
spinner$
|
|
3731
|
+
spinner$3.stop(import_picocolors.default.red("未找到技能"));
|
|
3585
3732
|
outro(import_picocolors.default.red("未找到有效技能。技能需要包含 name 和 description 的 SKILL.md 文件。"));
|
|
3586
3733
|
await cleanup(tempDir);
|
|
3587
3734
|
process.exit(1);
|
|
3588
3735
|
}
|
|
3589
|
-
if (!blobResult) spinner$
|
|
3736
|
+
if (!blobResult) spinner$3.stop(`Found ${import_picocolors.default.green(skills.length)} skill${skills.length > 1 ? "s" : ""}`);
|
|
3590
3737
|
if (options.list) {
|
|
3591
3738
|
console.log();
|
|
3592
3739
|
log.step(import_picocolors.default.bold("可用技能"));
|
|
@@ -3700,10 +3847,10 @@ async function runAdd(args, options = {}) {
|
|
|
3700
3847
|
}
|
|
3701
3848
|
targetAgents = options.agent;
|
|
3702
3849
|
} else {
|
|
3703
|
-
spinner$
|
|
3850
|
+
spinner$3.start("正在加载 Agent...");
|
|
3704
3851
|
const installedAgents = await detectInstalledAgents();
|
|
3705
3852
|
const totalAgents = Object.keys(agents).length;
|
|
3706
|
-
spinner$
|
|
3853
|
+
spinner$3.stop(`${totalAgents} agents`);
|
|
3707
3854
|
if (installedAgents.includes("eve") && (options.yes || !agentResult.isAgent)) {
|
|
3708
3855
|
const useEve = options.yes ? true : await confirm({
|
|
3709
3856
|
message: formatEveInstallPromptMessage(selectedSkills),
|
|
@@ -3786,7 +3933,7 @@ async function runAdd(args, options = {}) {
|
|
|
3786
3933
|
eveSubagentTargets = selectedSubagents.map((s) => s === "" ? void 0 : s);
|
|
3787
3934
|
}
|
|
3788
3935
|
}
|
|
3789
|
-
const installTargets = buildInstallTargets(targetAgents, eveSubagentTargets);
|
|
3936
|
+
const installTargets = buildInstallTargets$1(targetAgents, eveSubagentTargets);
|
|
3790
3937
|
let installGlobally = options.global ?? false;
|
|
3791
3938
|
const supportsGlobal = targetAgents.some((a) => agents[a].globalSkillsDir !== void 0);
|
|
3792
3939
|
if (options.global === void 0 && !options.yes && supportsGlobal) {
|
|
@@ -3845,7 +3992,7 @@ async function runAdd(args, options = {}) {
|
|
|
3845
3992
|
const overwriteStatus = /* @__PURE__ */ new Map();
|
|
3846
3993
|
for (const { skillName, target, installed } of overwriteChecks) {
|
|
3847
3994
|
if (!overwriteStatus.has(skillName)) overwriteStatus.set(skillName, /* @__PURE__ */ new Map());
|
|
3848
|
-
overwriteStatus.get(skillName).set(targetKey(target), installed);
|
|
3995
|
+
overwriteStatus.get(skillName).set(targetKey$1(target), installed);
|
|
3849
3996
|
}
|
|
3850
3997
|
const groupedSummary = {};
|
|
3851
3998
|
const ungroupedSummary = [];
|
|
@@ -3863,10 +4010,10 @@ async function runAdd(args, options = {}) {
|
|
|
3863
4010
|
eveSubagent: installTargets[0].subagent
|
|
3864
4011
|
}) : getCanonicalPath(skill.name, { global: installGlobally }), cwd);
|
|
3865
4012
|
summaryLines.push(`${import_picocolors.default.cyan(shortCanonical)}`);
|
|
3866
|
-
summaryLines.push(...buildTargetSummaryLines(installTargets, installMode));
|
|
4013
|
+
summaryLines.push(...buildTargetSummaryLines$1(installTargets, installMode));
|
|
3867
4014
|
const skillOverwrites = overwriteStatus.get(skill.name);
|
|
3868
|
-
const overwriteAgents = installTargets.filter((t) => skillOverwrites?.get(targetKey(t))).map(targetDisplayName);
|
|
3869
|
-
if (overwriteAgents.length > 0) summaryLines.push(` ${import_picocolors.default.yellow("overwrites:")} ${formatList$
|
|
4015
|
+
const overwriteAgents = installTargets.filter((t) => skillOverwrites?.get(targetKey$1(t))).map(targetDisplayName$1);
|
|
4016
|
+
if (overwriteAgents.length > 0) summaryLines.push(` ${import_picocolors.default.yellow("overwrites:")} ${formatList$2(overwriteAgents)}`);
|
|
3870
4017
|
}
|
|
3871
4018
|
};
|
|
3872
4019
|
const sortedGroups = Object.keys(groupedSummary).sort();
|
|
@@ -3903,7 +4050,7 @@ async function runAdd(args, options = {}) {
|
|
|
3903
4050
|
process.exit(0);
|
|
3904
4051
|
}
|
|
3905
4052
|
}
|
|
3906
|
-
spinner$
|
|
4053
|
+
spinner$3.start("正在安装技能...");
|
|
3907
4054
|
const results = [];
|
|
3908
4055
|
for (const skill of selectedSkills) for (const target of installTargets) {
|
|
3909
4056
|
const { agent, subagent } = target;
|
|
@@ -3935,12 +4082,12 @@ async function runAdd(args, options = {}) {
|
|
|
3935
4082
|
});
|
|
3936
4083
|
results.push({
|
|
3937
4084
|
skill: getSkillDisplayName(skill),
|
|
3938
|
-
agent: targetDisplayName(target),
|
|
4085
|
+
agent: targetDisplayName$1(target),
|
|
3939
4086
|
pluginName: skill.pluginName,
|
|
3940
4087
|
...result
|
|
3941
4088
|
});
|
|
3942
4089
|
}
|
|
3943
|
-
spinner$
|
|
4090
|
+
spinner$3.stop("安装完成");
|
|
3944
4091
|
console.log();
|
|
3945
4092
|
const successful = results.filter((r) => r.success);
|
|
3946
4093
|
const failed = results.filter((r) => !r.success);
|
|
@@ -4061,7 +4208,7 @@ async function runAdd(args, options = {}) {
|
|
|
4061
4208
|
const title = import_picocolors.default.green(`已安装 ${skillCount} 个技能`);
|
|
4062
4209
|
note(resultLines.join("\n"), title);
|
|
4063
4210
|
if (symlinkFailures.length > 0) {
|
|
4064
|
-
log.warn(import_picocolors.default.yellow(`符号链接失败: ${formatList$
|
|
4211
|
+
log.warn(import_picocolors.default.yellow(`符号链接失败: ${formatList$2(copiedAgents)}`));
|
|
4065
4212
|
log.message(import_picocolors.default.dim(" 已改为复制文件。在 Windows 上,请启用开发者模式以支持符号链接。"));
|
|
4066
4213
|
}
|
|
4067
4214
|
}
|
|
@@ -4190,10 +4337,10 @@ function parseAddOptions(args) {
|
|
|
4190
4337
|
errors
|
|
4191
4338
|
};
|
|
4192
4339
|
}
|
|
4193
|
-
const RESET$
|
|
4194
|
-
const BOLD$
|
|
4195
|
-
const DIM$
|
|
4196
|
-
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";
|
|
4197
4344
|
const CYAN$2 = "\x1B[36m";
|
|
4198
4345
|
const SEARCH_API_BASE = process.env.SKILLS_API_URL || "https://skills.sh";
|
|
4199
4346
|
function formatInstalls$1(count) {
|
|
@@ -4282,28 +4429,28 @@ async function runSearchPrompt(initialQuery = "", owner) {
|
|
|
4282
4429
|
if (lastRenderedLines > 0) process.stdout.write(MOVE_UP(lastRenderedLines) + MOVE_TO_COL(1));
|
|
4283
4430
|
process.stdout.write(CLEAR_DOWN);
|
|
4284
4431
|
const lines = [];
|
|
4285
|
-
const cursor = `${BOLD$
|
|
4286
|
-
lines.push(`${TEXT$
|
|
4432
|
+
const cursor = `${BOLD$10}_${RESET$13}`;
|
|
4433
|
+
lines.push(`${TEXT$10}搜索技能:${RESET$13} ${query}${cursor}`);
|
|
4287
4434
|
lines.push("");
|
|
4288
|
-
if (!query || query.length < 2) lines.push(`${DIM$
|
|
4289
|
-
else if (results.length === 0 && loading) lines.push(`${DIM$
|
|
4290
|
-
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}`);
|
|
4291
4438
|
else {
|
|
4292
4439
|
const visible = results.slice(0, 8);
|
|
4293
4440
|
for (let i = 0; i < visible.length; i++) {
|
|
4294
4441
|
const skill = visible[i];
|
|
4295
4442
|
const isSelected = i === selectedIndex;
|
|
4296
|
-
const arrow = isSelected ? `${BOLD$
|
|
4297
|
-
const name = isSelected ? `${BOLD$
|
|
4298
|
-
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}` : "";
|
|
4299
4446
|
const installs = formatInstalls$1(skill.installs);
|
|
4300
|
-
const installsBadge = installs ? ` ${CYAN$2}${installs}${RESET$
|
|
4301
|
-
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}` : "";
|
|
4302
4449
|
lines.push(` ${arrow} ${name}${source}${installsBadge}${loadingIndicator}`);
|
|
4303
4450
|
}
|
|
4304
4451
|
}
|
|
4305
4452
|
lines.push("");
|
|
4306
|
-
lines.push(`${DIM$
|
|
4453
|
+
lines.push(`${DIM$13}上下键导航 | 回车选择 | ESC 取消${RESET$13}`);
|
|
4307
4454
|
for (const line of lines) process.stdout.write(line + "\n");
|
|
4308
4455
|
lastRenderedLines = lines.length;
|
|
4309
4456
|
}
|
|
@@ -4400,9 +4547,9 @@ async function runFind(args) {
|
|
|
4400
4547
|
const { query, options: findOptions, errors } = parseFindOptions(args);
|
|
4401
4548
|
const owner = findOptions.owner;
|
|
4402
4549
|
const isNonInteractive = !process.stdin.isTTY;
|
|
4403
|
-
const agentTip = `${DIM$
|
|
4404
|
-
${DIM$
|
|
4405
|
-
${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}`;
|
|
4406
4553
|
if (errors.length > 0) {
|
|
4407
4554
|
for (const error of errors) console.error(error);
|
|
4408
4555
|
console.error("Usage: npx skills find <query> [--owner <owner>]");
|
|
@@ -4413,16 +4560,16 @@ ${DIM$11} 2) npx skills add <owner/repo@skill>${RESET$11}`;
|
|
|
4413
4560
|
String(results.length);
|
|
4414
4561
|
if (results.length === 0) {
|
|
4415
4562
|
const ownerSuffix = owner ? ` from owner "${owner}"` : "";
|
|
4416
|
-
console.log(`${DIM$
|
|
4563
|
+
console.log(`${DIM$13}未找到 "${query}" 的技能${ownerSuffix}${RESET$13}`);
|
|
4417
4564
|
return;
|
|
4418
4565
|
}
|
|
4419
|
-
console.log(`${DIM$
|
|
4566
|
+
console.log(`${DIM$13}安装:${RESET$13} npx skills add <owner/repo@skill>`);
|
|
4420
4567
|
console.log();
|
|
4421
4568
|
for (const skill of results.slice(0, 6)) {
|
|
4422
4569
|
const pkg = skill.source || skill.slug;
|
|
4423
4570
|
const installs = formatInstalls$1(skill.installs);
|
|
4424
|
-
console.log(`${TEXT$
|
|
4425
|
-
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}`);
|
|
4426
4573
|
console.log();
|
|
4427
4574
|
}
|
|
4428
4575
|
return;
|
|
@@ -4430,19 +4577,19 @@ ${DIM$11} 2) npx skills add <owner/repo@skill>${RESET$11}`;
|
|
|
4430
4577
|
if (isNonInteractive || await isRunningInAgent()) {
|
|
4431
4578
|
console.log(agentTip);
|
|
4432
4579
|
console.log();
|
|
4433
|
-
console.log(`${DIM$
|
|
4580
|
+
console.log(`${DIM$13}Usage: npx skills find <query> [--owner <owner>]${RESET$13}`);
|
|
4434
4581
|
return;
|
|
4435
4582
|
}
|
|
4436
4583
|
const selected = await runSearchPrompt("", owner);
|
|
4437
4584
|
if (!selected) {
|
|
4438
|
-
console.log(`${DIM$
|
|
4585
|
+
console.log(`${DIM$13}搜索已取消${RESET$13}`);
|
|
4439
4586
|
console.log();
|
|
4440
4587
|
return;
|
|
4441
4588
|
}
|
|
4442
4589
|
const pkg = selected.source || selected.slug;
|
|
4443
4590
|
const skillName = selected.name;
|
|
4444
4591
|
console.log();
|
|
4445
|
-
console.log(`${TEXT$
|
|
4592
|
+
console.log(`${TEXT$10}正在从 ${DIM$13}${pkg}${RESET$13} 安装 ${BOLD$10}${skillName}${RESET$13}...`);
|
|
4446
4593
|
console.log();
|
|
4447
4594
|
const { source, options: addOptions } = parseAddOptions([
|
|
4448
4595
|
pkg,
|
|
@@ -4452,13 +4599,13 @@ ${DIM$11} 2) npx skills add <owner/repo@skill>${RESET$11}`;
|
|
|
4452
4599
|
await runAdd(source, addOptions);
|
|
4453
4600
|
console.log();
|
|
4454
4601
|
const info = getOwnerRepoFromString(pkg);
|
|
4455
|
-
if (info && await isRepoPublic(info.owner, info.repo)) console.log(`${DIM$
|
|
4456
|
-
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}`);
|
|
4457
4604
|
console.log();
|
|
4458
4605
|
}
|
|
4459
|
-
const RESET$
|
|
4460
|
-
const BOLD$
|
|
4461
|
-
const DIM$
|
|
4606
|
+
const RESET$12 = "\x1B[0m";
|
|
4607
|
+
const BOLD$9 = "\x1B[1m";
|
|
4608
|
+
const DIM$12 = "\x1B[38;5;102m";
|
|
4462
4609
|
const CYAN$1 = "\x1B[36m";
|
|
4463
4610
|
const YELLOW$4 = "\x1B[33m";
|
|
4464
4611
|
function shortenPath(fullPath, cwd) {
|
|
@@ -4467,7 +4614,7 @@ function shortenPath(fullPath, cwd) {
|
|
|
4467
4614
|
if (fullPath.startsWith(cwd)) return "." + fullPath.slice(cwd.length);
|
|
4468
4615
|
return fullPath;
|
|
4469
4616
|
}
|
|
4470
|
-
function formatList(items, maxShow = 5) {
|
|
4617
|
+
function formatList$1(items, maxShow = 5) {
|
|
4471
4618
|
if (items.length <= maxShow) return items.join(", ");
|
|
4472
4619
|
const shown = items.slice(0, maxShow);
|
|
4473
4620
|
const remaining = items.length - maxShow;
|
|
@@ -4494,8 +4641,8 @@ async function runList(args) {
|
|
|
4494
4641
|
const validAgents = Object.keys(agents);
|
|
4495
4642
|
const invalidAgents = options.agent.filter((a) => !validAgents.includes(a));
|
|
4496
4643
|
if (invalidAgents.length > 0) {
|
|
4497
|
-
console.log(`${YELLOW$4}Invalid agents: ${invalidAgents.join(", ")}${RESET$
|
|
4498
|
-
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}`);
|
|
4499
4646
|
process.exit(1);
|
|
4500
4647
|
}
|
|
4501
4648
|
agentFilter = options.agent;
|
|
@@ -4522,21 +4669,21 @@ async function runList(args) {
|
|
|
4522
4669
|
console.log("[]");
|
|
4523
4670
|
return;
|
|
4524
4671
|
}
|
|
4525
|
-
console.log(`${DIM$
|
|
4526
|
-
if (scope) console.log(`${DIM$
|
|
4527
|
-
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}`);
|
|
4528
4675
|
return;
|
|
4529
4676
|
}
|
|
4530
4677
|
function printSkill(skill, indent = false, maxNameLength = 0, maxPathLength = 0) {
|
|
4531
4678
|
const prefix = indent ? " " : "";
|
|
4532
4679
|
const shortPath = shortenPath(skill.canonicalPath, cwd);
|
|
4533
4680
|
const agentNames = skill.agents.map((a) => agents[a].displayName);
|
|
4534
|
-
const agentInfo = skill.agents.length > 0 ? formatList(agentNames) : `${YELLOW$4}not linked${RESET$
|
|
4681
|
+
const agentInfo = skill.agents.length > 0 ? formatList$1(agentNames) : `${YELLOW$4}not linked${RESET$12}`;
|
|
4535
4682
|
const paddedName = sanitizeMetadata(skill.name).padEnd(maxNameLength);
|
|
4536
4683
|
const paddedPath = shortPath.padEnd(maxPathLength);
|
|
4537
|
-
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}`);
|
|
4538
4685
|
}
|
|
4539
|
-
console.log(`${BOLD$
|
|
4686
|
+
console.log(`${BOLD$9}${scopeLabel}技能${RESET$12}`);
|
|
4540
4687
|
console.log();
|
|
4541
4688
|
const groupedSkills = {};
|
|
4542
4689
|
const ungroupedSkills = [];
|
|
@@ -4552,7 +4699,7 @@ async function runList(args) {
|
|
|
4552
4699
|
const sortedGroups = Object.keys(groupedSkills).sort();
|
|
4553
4700
|
for (const group of sortedGroups) {
|
|
4554
4701
|
const title = group.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
4555
|
-
console.log(`${BOLD$
|
|
4702
|
+
console.log(`${BOLD$9}${title}${RESET$12}`);
|
|
4556
4703
|
const skills = groupedSkills[group];
|
|
4557
4704
|
if (skills) {
|
|
4558
4705
|
let maxNameLength = 0;
|
|
@@ -4568,7 +4715,7 @@ async function runList(args) {
|
|
|
4568
4715
|
console.log();
|
|
4569
4716
|
}
|
|
4570
4717
|
if (ungroupedSkills.length > 0) {
|
|
4571
|
-
console.log(`${BOLD$
|
|
4718
|
+
console.log(`${BOLD$9}通用${RESET$12}`);
|
|
4572
4719
|
let maxNameLength = 0;
|
|
4573
4720
|
let maxPathLength = 0;
|
|
4574
4721
|
for (const skill of ungroupedSkills) {
|
|
@@ -4605,8 +4752,8 @@ async function removeCommand(skillNames, options) {
|
|
|
4605
4752
|
}
|
|
4606
4753
|
const isGlobal = options.global ?? false;
|
|
4607
4754
|
const cwd = process.cwd();
|
|
4608
|
-
const spinner$
|
|
4609
|
-
spinner$
|
|
4755
|
+
const spinner$2 = spinner();
|
|
4756
|
+
spinner$2.start("正在扫描已安装的技能...");
|
|
4610
4757
|
const skillNamesSet = /* @__PURE__ */ new Set();
|
|
4611
4758
|
const scanDir = async (dir) => {
|
|
4612
4759
|
try {
|
|
@@ -4625,7 +4772,7 @@ async function removeCommand(skillNames, options) {
|
|
|
4625
4772
|
for (const subagent of getEveSubagents(cwd)) await scanDir(getEveSubagentSkillsDir(subagent, cwd));
|
|
4626
4773
|
}
|
|
4627
4774
|
const installedSkills = Array.from(skillNamesSet).sort();
|
|
4628
|
-
spinner$
|
|
4775
|
+
spinner$2.stop(`发现 ${installedSkills.length} 个已安装技能`);
|
|
4629
4776
|
if (installedSkills.length === 0) {
|
|
4630
4777
|
outro(import_picocolors.default.yellow("没有可卸载的技能。"));
|
|
4631
4778
|
return;
|
|
@@ -4667,7 +4814,7 @@ async function removeCommand(skillNames, options) {
|
|
|
4667
4814
|
if (options.agent && options.agent.length > 0) targetAgents = options.agent;
|
|
4668
4815
|
else {
|
|
4669
4816
|
targetAgents = Object.keys(agents);
|
|
4670
|
-
spinner$
|
|
4817
|
+
spinner$2.stop(`Targeting ${targetAgents.length} potential agent(s)`);
|
|
4671
4818
|
}
|
|
4672
4819
|
if (!options.yes) {
|
|
4673
4820
|
console.log();
|
|
@@ -4680,7 +4827,7 @@ async function removeCommand(skillNames, options) {
|
|
|
4680
4827
|
process.exit(0);
|
|
4681
4828
|
}
|
|
4682
4829
|
}
|
|
4683
|
-
spinner$
|
|
4830
|
+
spinner$2.start("正在卸载技能...");
|
|
4684
4831
|
const results = [];
|
|
4685
4832
|
for (const skillName of selectedSkills) try {
|
|
4686
4833
|
const canonicalPath = getCanonicalPath(skillName, {
|
|
@@ -4742,7 +4889,7 @@ async function removeCommand(skillNames, options) {
|
|
|
4742
4889
|
error: err instanceof Error ? err.message : String(err)
|
|
4743
4890
|
});
|
|
4744
4891
|
}
|
|
4745
|
-
spinner$
|
|
4892
|
+
spinner$2.stop("卸载完成");
|
|
4746
4893
|
const successful = results.filter((r) => r.success);
|
|
4747
4894
|
const failed = results.filter((r) => !r.success);
|
|
4748
4895
|
if (successful.length > 0) {
|
|
@@ -4842,10 +4989,10 @@ function buildLocalUpdateSource(entry) {
|
|
|
4842
4989
|
return appendFolderAndRef(source, entry.skillPath, entry.ref);
|
|
4843
4990
|
}
|
|
4844
4991
|
const __dirname$1 = dirname(fileURLToPath(import.meta.url));
|
|
4845
|
-
const RESET$
|
|
4846
|
-
const BOLD$
|
|
4847
|
-
const DIM$
|
|
4848
|
-
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";
|
|
4849
4996
|
function parseUpdateOptions(args) {
|
|
4850
4997
|
const options = {};
|
|
4851
4998
|
const positional = [];
|
|
@@ -4938,7 +5085,7 @@ function getInstallSource(skill) {
|
|
|
4938
5085
|
function printSkippedSkills(skipped) {
|
|
4939
5086
|
if (skipped.length === 0) return;
|
|
4940
5087
|
console.log();
|
|
4941
|
-
console.log(`${DIM$
|
|
5088
|
+
console.log(`${DIM$11}${skipped.length} 个技能无法自动检查:${RESET$11}`);
|
|
4942
5089
|
const grouped = /* @__PURE__ */ new Map();
|
|
4943
5090
|
for (const skill of skipped) {
|
|
4944
5091
|
const source = getInstallSource(skill);
|
|
@@ -4949,13 +5096,13 @@ function printSkippedSkills(skipped) {
|
|
|
4949
5096
|
for (const [source, skills] of grouped) {
|
|
4950
5097
|
if (skills.length === 1) {
|
|
4951
5098
|
const skill = skills[0];
|
|
4952
|
-
console.log(` ${TEXT$
|
|
5099
|
+
console.log(` ${TEXT$9}•${RESET$11} ${sanitizeMetadata(skill.name)} ${DIM$11}(${skill.reason})${RESET$11}`);
|
|
4953
5100
|
} else {
|
|
4954
5101
|
const reason = skills[0].reason;
|
|
4955
5102
|
const names = skills.map((s) => sanitizeMetadata(s.name)).join(", ");
|
|
4956
|
-
console.log(` ${TEXT$
|
|
5103
|
+
console.log(` ${TEXT$9}•${RESET$11} ${names} ${DIM$11}(${reason})${RESET$11}`);
|
|
4957
5104
|
}
|
|
4958
|
-
console.log(` ${DIM$
|
|
5105
|
+
console.log(` ${DIM$11}更新方法:${TEXT$9}npx skills add ${source} -g -y${RESET$11}`);
|
|
4959
5106
|
}
|
|
4960
5107
|
}
|
|
4961
5108
|
async function getProjectSkillsForUpdate(skillFilter) {
|
|
@@ -4980,13 +5127,13 @@ async function checkAndPromptForDeletions(source, allLockedForSource, lockSkills
|
|
|
4980
5127
|
});
|
|
4981
5128
|
if (deletedSkills.length > 0) {
|
|
4982
5129
|
console.log();
|
|
4983
|
-
console.log(`${DIM$
|
|
4984
|
-
for (const s of deletedSkills) console.log(` ${DIM$
|
|
4985
|
-
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}`);
|
|
4986
5133
|
else {
|
|
4987
5134
|
const confirmed = await confirm({ message: `是否删除这些已删除技能的本地副本?` });
|
|
4988
5135
|
if (confirmed && !isCancel(confirmed)) for (const s of deletedSkills) {
|
|
4989
|
-
console.log(`${DIM$
|
|
5136
|
+
console.log(`${DIM$11}正在卸载${RESET$11} ${s}...`);
|
|
4990
5137
|
await removeCommand([s], {
|
|
4991
5138
|
yes: true,
|
|
4992
5139
|
global: isGlobal
|
|
@@ -5003,8 +5150,8 @@ async function updateGlobalSkills(options = {}) {
|
|
|
5003
5150
|
let failCount = 0;
|
|
5004
5151
|
if (skillNames.length === 0) {
|
|
5005
5152
|
if (!options.skills) {
|
|
5006
|
-
console.log(`${DIM$
|
|
5007
|
-
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}`);
|
|
5008
5155
|
}
|
|
5009
5156
|
return {
|
|
5010
5157
|
successCount,
|
|
@@ -5045,7 +5192,7 @@ async function updateGlobalSkills(options = {}) {
|
|
|
5045
5192
|
const firstEntry = itemsForSource[0].entry;
|
|
5046
5193
|
const sourceUrl = firstEntry.sourceUrl || firstEntry.source;
|
|
5047
5194
|
let tempDir = null;
|
|
5048
|
-
process.stdout.write(`\r${DIM$
|
|
5195
|
+
process.stdout.write(`\r${DIM$11}Checking skills from source: ${source}${RESET$11}\x1b[K\n`);
|
|
5049
5196
|
if (firstEntry.sourceType === "skillhub") {
|
|
5050
5197
|
const skillhubRef = source.replace(/^skillhub:/, "");
|
|
5051
5198
|
const { parseSkillRef, toCanonicalSlug } = await Promise.resolve().then(() => skill_ref_exports);
|
|
@@ -5055,7 +5202,7 @@ async function updateGlobalSkills(options = {}) {
|
|
|
5055
5202
|
new SkillHubAPI(cfg.registry, cfg.token);
|
|
5056
5203
|
for (const item of itemsForSource) {
|
|
5057
5204
|
const safeName = sanitizeMetadata(item.name);
|
|
5058
|
-
console.log(`${TEXT$
|
|
5205
|
+
console.log(`${TEXT$9}正在更新 ${safeName}...${RESET$11}`);
|
|
5059
5206
|
try {
|
|
5060
5207
|
const parts = skillhubRef.split("/");
|
|
5061
5208
|
const ref = parts.length === 2 ? `@${parts[0]}/${parts[1]}` : skillhubRef;
|
|
@@ -5066,7 +5213,7 @@ async function updateGlobalSkills(options = {}) {
|
|
|
5066
5213
|
});
|
|
5067
5214
|
successCount++;
|
|
5068
5215
|
} catch (error) {
|
|
5069
|
-
console.log(` ${DIM$
|
|
5216
|
+
console.log(` ${DIM$11}✗ ${safeName}: ${error.message}${RESET$11}`);
|
|
5070
5217
|
failCount++;
|
|
5071
5218
|
}
|
|
5072
5219
|
}
|
|
@@ -5076,7 +5223,7 @@ async function updateGlobalSkills(options = {}) {
|
|
|
5076
5223
|
if (firstEntry.sourceType === "github") {
|
|
5077
5224
|
const tree = await fetchRepoTree(source, firstEntry.ref, getGitHubToken);
|
|
5078
5225
|
if (!tree) {
|
|
5079
|
-
console.log(` ${DIM$
|
|
5226
|
+
console.log(` ${DIM$11}✗ Failed to fetch tree for ${source}${RESET$11}`);
|
|
5080
5227
|
continue;
|
|
5081
5228
|
}
|
|
5082
5229
|
const discoveredPaths = findSkillMdPaths(tree);
|
|
@@ -5111,7 +5258,7 @@ async function updateGlobalSkills(options = {}) {
|
|
|
5111
5258
|
});
|
|
5112
5259
|
}
|
|
5113
5260
|
} catch (error) {
|
|
5114
|
-
console.log(` ${DIM$
|
|
5261
|
+
console.log(` ${DIM$11}✗ Failed to check skills from ${source}${RESET$11}`);
|
|
5115
5262
|
} finally {
|
|
5116
5263
|
if (tempDir) await cleanupTempDir(tempDir);
|
|
5117
5264
|
}
|
|
@@ -5119,7 +5266,7 @@ async function updateGlobalSkills(options = {}) {
|
|
|
5119
5266
|
if (checkable.length > 0) process.stdout.write("\r\x1B[K");
|
|
5120
5267
|
const checkedCount = checkable.length + skipped.length;
|
|
5121
5268
|
if (checkable.length === 0 && skipped.length === 0) {
|
|
5122
|
-
if (!options.skills) console.log(`${DIM$
|
|
5269
|
+
if (!options.skills) console.log(`${DIM$11}没有可检查的全局技能。${RESET$11}`);
|
|
5123
5270
|
return {
|
|
5124
5271
|
successCount,
|
|
5125
5272
|
failCount,
|
|
@@ -5135,28 +5282,28 @@ async function updateGlobalSkills(options = {}) {
|
|
|
5135
5282
|
};
|
|
5136
5283
|
}
|
|
5137
5284
|
if (updates.length === 0) {
|
|
5138
|
-
console.log(`${TEXT$
|
|
5285
|
+
console.log(`${TEXT$9}✓ 所有全局技能已是最新${RESET$11}`);
|
|
5139
5286
|
return {
|
|
5140
5287
|
successCount,
|
|
5141
5288
|
failCount,
|
|
5142
5289
|
checkedCount
|
|
5143
5290
|
};
|
|
5144
5291
|
}
|
|
5145
|
-
console.log(`${TEXT$
|
|
5292
|
+
console.log(`${TEXT$9}发现 ${updates.length} 个可更新全局技能${RESET$11}`);
|
|
5146
5293
|
console.log();
|
|
5147
5294
|
for (const update of updates) {
|
|
5148
5295
|
const safeName = sanitizeMetadata(update.name);
|
|
5149
|
-
console.log(`${TEXT$
|
|
5296
|
+
console.log(`${TEXT$9}正在更新 ${safeName}...${RESET$11}`);
|
|
5150
5297
|
const installUrl = buildUpdateInstallSource(update.entry);
|
|
5151
5298
|
if (!installUrl) {
|
|
5152
5299
|
failCount++;
|
|
5153
|
-
console.log(` ${DIM$
|
|
5300
|
+
console.log(` ${DIM$11}✗ Cannot update ${safeName}: lock file is missing sourceUrl for this generic Git source${RESET$11}`);
|
|
5154
5301
|
continue;
|
|
5155
5302
|
}
|
|
5156
5303
|
const cliEntry = join(__dirname$1, "..", "bin", "bfi.mjs");
|
|
5157
5304
|
if (!existsSync(cliEntry)) {
|
|
5158
5305
|
failCount++;
|
|
5159
|
-
console.log(` ${DIM$
|
|
5306
|
+
console.log(` ${DIM$11}✗ Failed to update ${safeName}: CLI entrypoint not found at ${cliEntry}${RESET$11}`);
|
|
5160
5307
|
continue;
|
|
5161
5308
|
}
|
|
5162
5309
|
if (spawnSync(process.execPath, [
|
|
@@ -5175,10 +5322,10 @@ async function updateGlobalSkills(options = {}) {
|
|
|
5175
5322
|
shell: false
|
|
5176
5323
|
}).status === 0) {
|
|
5177
5324
|
successCount++;
|
|
5178
|
-
console.log(` ${TEXT$
|
|
5325
|
+
console.log(` ${TEXT$9}✓${RESET$11} 已更新 ${safeName}`);
|
|
5179
5326
|
} else {
|
|
5180
5327
|
failCount++;
|
|
5181
|
-
console.log(` ${DIM$
|
|
5328
|
+
console.log(` ${DIM$11}✗ 更新 ${safeName} 失败${RESET$11}`);
|
|
5182
5329
|
}
|
|
5183
5330
|
}
|
|
5184
5331
|
printSkippedSkills(skipped);
|
|
@@ -5194,8 +5341,8 @@ async function updateProjectSkills(options = {}) {
|
|
|
5194
5341
|
let failCount = 0;
|
|
5195
5342
|
if (projectSkills.length === 0) {
|
|
5196
5343
|
if (!options.skills) {
|
|
5197
|
-
console.log(`${DIM$
|
|
5198
|
-
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}`);
|
|
5199
5346
|
}
|
|
5200
5347
|
return {
|
|
5201
5348
|
successCount,
|
|
@@ -5206,7 +5353,7 @@ async function updateProjectSkills(options = {}) {
|
|
|
5206
5353
|
const updatable = projectSkills.filter((s) => s.entry.skillPath);
|
|
5207
5354
|
const legacy = projectSkills.filter((s) => !s.entry.skillPath);
|
|
5208
5355
|
if (updatable.length === 0) {
|
|
5209
|
-
console.log(`${DIM$
|
|
5356
|
+
console.log(`${DIM$11}No project skills can be updated in place.${RESET$11}`);
|
|
5210
5357
|
printLegacyProjectSkills(legacy);
|
|
5211
5358
|
return {
|
|
5212
5359
|
successCount,
|
|
@@ -5226,8 +5373,8 @@ async function updateProjectSkills(options = {}) {
|
|
|
5226
5373
|
const targetParts = [];
|
|
5227
5374
|
if (hasUniversal) targetParts.push("Universal");
|
|
5228
5375
|
targetParts.push(...targetAgentNames);
|
|
5229
|
-
if (targetParts.length > 0) console.log(`${TEXT$
|
|
5230
|
-
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}`);
|
|
5231
5378
|
console.log();
|
|
5232
5379
|
const bySource = /* @__PURE__ */ new Map();
|
|
5233
5380
|
for (const skill of updatable) {
|
|
@@ -5239,7 +5386,7 @@ async function updateProjectSkills(options = {}) {
|
|
|
5239
5386
|
const localLock = await readLocalLock();
|
|
5240
5387
|
const cliEntry = join(__dirname$1, "..", "bin", "bfi.mjs");
|
|
5241
5388
|
if (!existsSync(cliEntry)) {
|
|
5242
|
-
console.log(`${DIM$
|
|
5389
|
+
console.log(`${DIM$11}✗ CLI 入口未找到 ${cliEntry}${RESET$11}`);
|
|
5243
5390
|
return {
|
|
5244
5391
|
successCount,
|
|
5245
5392
|
failCount: updatable.length,
|
|
@@ -5259,7 +5406,7 @@ async function updateProjectSkills(options = {}) {
|
|
|
5259
5406
|
new SkillHubAPI(cfg.registry, cfg.token);
|
|
5260
5407
|
for (const item of skillsForSource) {
|
|
5261
5408
|
const safeName = sanitizeMetadata(item.name);
|
|
5262
|
-
console.log(`${TEXT$
|
|
5409
|
+
console.log(`${TEXT$9}正在更新 ${safeName}...${RESET$11}`);
|
|
5263
5410
|
try {
|
|
5264
5411
|
const parts = skillhubRef.split("/");
|
|
5265
5412
|
const ref = parts.length === 2 ? `@${parts[0]}/${parts[1]}` : skillhubRef;
|
|
@@ -5270,7 +5417,7 @@ async function updateProjectSkills(options = {}) {
|
|
|
5270
5417
|
});
|
|
5271
5418
|
successCount++;
|
|
5272
5419
|
} catch (error) {
|
|
5273
|
-
console.log(` ${DIM$
|
|
5420
|
+
console.log(` ${DIM$11}✗ ${safeName}: ${error.message}${RESET$11}`);
|
|
5274
5421
|
failCount++;
|
|
5275
5422
|
}
|
|
5276
5423
|
}
|
|
@@ -5281,7 +5428,7 @@ async function updateProjectSkills(options = {}) {
|
|
|
5281
5428
|
let deletedSkills = [];
|
|
5282
5429
|
if (buildLocalUpdateSource(firstEntry) === null) {
|
|
5283
5430
|
failCount += skillsForSource.length;
|
|
5284
|
-
console.log(`${DIM$
|
|
5431
|
+
console.log(`${DIM$11}✗ Cannot update ${source}: skills-lock.json is missing sourceUrl for this generic Git source${RESET$11}`);
|
|
5285
5432
|
continue;
|
|
5286
5433
|
}
|
|
5287
5434
|
try {
|
|
@@ -5291,18 +5438,18 @@ async function updateProjectSkills(options = {}) {
|
|
|
5291
5438
|
});
|
|
5292
5439
|
deletedSkills = await checkAndPromptForDeletions(source, allLockedForSource, localLock.skills, false, options, discoveredPaths);
|
|
5293
5440
|
} catch (error) {
|
|
5294
|
-
console.log(`${DIM$
|
|
5441
|
+
console.log(`${DIM$11}✗ 检查 ${source} 已删除技能失败${RESET$11}`);
|
|
5295
5442
|
} finally {
|
|
5296
5443
|
if (tempDir) await cleanupTempDir(tempDir);
|
|
5297
5444
|
}
|
|
5298
5445
|
const remainingSkills = skillsForSource.filter((s) => !deletedSkills.includes(s.name));
|
|
5299
5446
|
for (const skill of remainingSkills) {
|
|
5300
5447
|
const safeName = sanitizeMetadata(skill.name);
|
|
5301
|
-
console.log(`${TEXT$
|
|
5448
|
+
console.log(`${TEXT$9}正在更新 ${safeName}...${RESET$11}`);
|
|
5302
5449
|
const installUrl = buildLocalUpdateSource(skill.entry);
|
|
5303
5450
|
if (!installUrl) {
|
|
5304
5451
|
failCount++;
|
|
5305
|
-
console.log(` ${DIM$
|
|
5452
|
+
console.log(` ${DIM$11}✗ Cannot update ${safeName}: skills-lock.json is missing sourceUrl for this generic Git source${RESET$11}`);
|
|
5306
5453
|
continue;
|
|
5307
5454
|
}
|
|
5308
5455
|
const subagentArgs = skill.entry.subagents?.length ? ["--subagent", ...skill.entry.subagents.map((s) => s === "" ? "root" : s)] : [];
|
|
@@ -5324,10 +5471,10 @@ async function updateProjectSkills(options = {}) {
|
|
|
5324
5471
|
shell: false
|
|
5325
5472
|
}).status === 0) {
|
|
5326
5473
|
successCount++;
|
|
5327
|
-
console.log(` ${TEXT$
|
|
5474
|
+
console.log(` ${TEXT$9}✓${RESET$11} 已更新 ${safeName}`);
|
|
5328
5475
|
} else {
|
|
5329
5476
|
failCount++;
|
|
5330
|
-
console.log(` ${DIM$
|
|
5477
|
+
console.log(` ${DIM$11}✗ 更新 ${safeName} 失败${RESET$11}`);
|
|
5331
5478
|
}
|
|
5332
5479
|
}
|
|
5333
5480
|
}
|
|
@@ -5341,25 +5488,25 @@ async function updateProjectSkills(options = {}) {
|
|
|
5341
5488
|
function printLegacyProjectSkills(legacy) {
|
|
5342
5489
|
if (legacy.length === 0) return;
|
|
5343
5490
|
console.log();
|
|
5344
|
-
console.log(`${DIM$
|
|
5491
|
+
console.log(`${DIM$11}${legacy.length} project skill(s) cannot be updated automatically (installed before skillPath tracking):${RESET$11}`);
|
|
5345
5492
|
for (const skill of legacy) {
|
|
5346
5493
|
const reinstall = buildLocalUpdateSource(skill.entry);
|
|
5347
|
-
console.log(` ${TEXT$
|
|
5348
|
-
if (reinstall) console.log(` ${DIM$
|
|
5349
|
-
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}`);
|
|
5350
5497
|
}
|
|
5351
5498
|
}
|
|
5352
5499
|
async function runUpdate(args = []) {
|
|
5353
5500
|
const options = parseUpdateOptions(args);
|
|
5354
5501
|
const scope = await resolveUpdateScope(options);
|
|
5355
|
-
if (options.skills) console.log(`${TEXT$
|
|
5356
|
-
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}`);
|
|
5357
5504
|
console.log();
|
|
5358
5505
|
let totalSuccess = 0;
|
|
5359
5506
|
let totalFail = 0;
|
|
5360
5507
|
let totalFound = 0;
|
|
5361
5508
|
if (scope === "global" || scope === "both") {
|
|
5362
|
-
if (scope === "both" && !options.skills) console.log(`${BOLD$
|
|
5509
|
+
if (scope === "both" && !options.skills) console.log(`${BOLD$8}全局技能${RESET$11}`);
|
|
5363
5510
|
const { successCount, failCount, checkedCount } = await updateGlobalSkills(options);
|
|
5364
5511
|
totalSuccess += successCount;
|
|
5365
5512
|
totalFail += failCount;
|
|
@@ -5367,17 +5514,17 @@ async function runUpdate(args = []) {
|
|
|
5367
5514
|
if (scope === "both" && !options.skills) console.log();
|
|
5368
5515
|
}
|
|
5369
5516
|
if (scope === "project" || scope === "both") {
|
|
5370
|
-
if (scope === "both" && !options.skills) console.log(`${BOLD$
|
|
5517
|
+
if (scope === "both" && !options.skills) console.log(`${BOLD$8}项目技能${RESET$11}`);
|
|
5371
5518
|
const { successCount, failCount, foundCount } = await updateProjectSkills(options);
|
|
5372
5519
|
totalSuccess += successCount;
|
|
5373
5520
|
totalFail += failCount;
|
|
5374
5521
|
totalFound += foundCount;
|
|
5375
5522
|
}
|
|
5376
|
-
if (options.skills && totalFound === 0) console.log(`${DIM$
|
|
5523
|
+
if (options.skills && totalFound === 0) console.log(`${DIM$11}未找到匹配的已安装技能:${options.skills.join(", ")}${RESET$11}`);
|
|
5377
5524
|
console.log();
|
|
5378
|
-
if (totalSuccess > 0) console.log(`${TEXT$
|
|
5525
|
+
if (totalSuccess > 0) console.log(`${TEXT$9}✓ 已更新 ${totalSuccess} 个技能${RESET$11}`);
|
|
5379
5526
|
if (totalFail > 0) {
|
|
5380
|
-
console.log(`${DIM$
|
|
5527
|
+
console.log(`${DIM$11}更新失败 ${totalFail} 个技能${RESET$11}`);
|
|
5381
5528
|
process.exitCode = 1;
|
|
5382
5529
|
}
|
|
5383
5530
|
String(totalSuccess + totalFail);
|
|
@@ -5829,10 +5976,10 @@ function getOptionValue$3(args, flag) {
|
|
|
5829
5976
|
const idx = args.indexOf(flag);
|
|
5830
5977
|
return idx !== -1 && args[idx + 1] ? args[idx + 1] : void 0;
|
|
5831
5978
|
}
|
|
5832
|
-
const RESET$
|
|
5833
|
-
const BOLD$
|
|
5834
|
-
const DIM$
|
|
5835
|
-
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";
|
|
5836
5983
|
const CYAN = "\x1B[36m";
|
|
5837
5984
|
const MARKETPLACE_API = process.env.SKILLS_API_URL || "https://skills.sh";
|
|
5838
5985
|
async function runSearch(args, globalOpts) {
|
|
@@ -5840,13 +5987,13 @@ async function runSearch(args, globalOpts) {
|
|
|
5840
5987
|
const namespace = args.includes("--namespace") ? getOptionValue$2(args, "--namespace") : void 0;
|
|
5841
5988
|
const json = args.includes("--json");
|
|
5842
5989
|
if (!query || query.trim() === "") {
|
|
5843
|
-
console.log(`${BOLD$
|
|
5990
|
+
console.log(`${BOLD$7}bfi skills search <关键词> [选项]${RESET$10}`);
|
|
5844
5991
|
console.log();
|
|
5845
|
-
console.log(`${DIM$
|
|
5846
|
-
console.log(` bfi skills search react ${DIM$
|
|
5847
|
-
console.log(` bfi skills search react --marketplace ${DIM$
|
|
5848
|
-
console.log(` bfi skills search react --all ${DIM$
|
|
5849
|
-
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}`);
|
|
5850
5997
|
return;
|
|
5851
5998
|
}
|
|
5852
5999
|
const useMarketplace = args.includes("--marketplace");
|
|
@@ -5856,9 +6003,9 @@ async function runSearch(args, globalOpts) {
|
|
|
5856
6003
|
return;
|
|
5857
6004
|
}
|
|
5858
6005
|
if (searchAll) {
|
|
5859
|
-
console.log(`${BOLD$
|
|
6006
|
+
console.log(`${BOLD$7}=== SkillHub 注册表 ===${RESET$10}\n`);
|
|
5860
6007
|
await searchSkillHub(query, namespace, globalOpts, json);
|
|
5861
|
-
console.log(`\n${BOLD$
|
|
6008
|
+
console.log(`\n${BOLD$7}=== skills.sh 市场 ===${RESET$10}\n`);
|
|
5862
6009
|
await searchMarketplace(query, json);
|
|
5863
6010
|
return;
|
|
5864
6011
|
}
|
|
@@ -5893,7 +6040,7 @@ async function searchSkillHub(query, namespace, globalOpts, json = false) {
|
|
|
5893
6040
|
count: 0,
|
|
5894
6041
|
results: []
|
|
5895
6042
|
}, null, 2));
|
|
5896
|
-
else console.log(`${DIM$
|
|
6043
|
+
else console.log(`${DIM$10}SkillHub 上未找到 "${query}" 的技能${RESET$10}`);
|
|
5897
6044
|
return;
|
|
5898
6045
|
}
|
|
5899
6046
|
if (json) {
|
|
@@ -5920,15 +6067,15 @@ async function searchSkillHub(query, namespace, globalOpts, json = false) {
|
|
|
5920
6067
|
const desc = skill.summary || skill.description || "";
|
|
5921
6068
|
const canonicalSlug = skill.slug || "";
|
|
5922
6069
|
const displaySlug = canonicalSlug.indexOf("--") > 0 ? canonicalSlug : `global--${canonicalSlug}`;
|
|
5923
|
-
console.log(` ${BOLD$
|
|
5924
|
-
if (desc) console.log(` ${DIM$
|
|
5925
|
-
console.log(` ${TEXT$
|
|
5926
|
-
if (skill.version) console.log(` ${DIM$
|
|
5927
|
-
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}`);
|
|
5928
6075
|
console.log();
|
|
5929
6076
|
}
|
|
5930
6077
|
} catch (error) {
|
|
5931
|
-
console.error(`${DIM$
|
|
6078
|
+
console.error(`${DIM$10}SkillHub 搜索失败:${error.message}${RESET$10}`);
|
|
5932
6079
|
}
|
|
5933
6080
|
}
|
|
5934
6081
|
async function searchMarketplace(query, json = false) {
|
|
@@ -5945,7 +6092,7 @@ async function searchMarketplace(query, json = false) {
|
|
|
5945
6092
|
count: 0,
|
|
5946
6093
|
results: []
|
|
5947
6094
|
}, null, 2));
|
|
5948
|
-
else console.log(`${DIM$
|
|
6095
|
+
else console.log(`${DIM$10}skills.sh 市场无结果${RESET$10}`);
|
|
5949
6096
|
return;
|
|
5950
6097
|
}
|
|
5951
6098
|
const skills = (await res.json()).skills || [];
|
|
@@ -5955,7 +6102,7 @@ async function searchMarketplace(query, json = false) {
|
|
|
5955
6102
|
count: 0,
|
|
5956
6103
|
results: []
|
|
5957
6104
|
}, null, 2));
|
|
5958
|
-
else console.log(`${DIM$
|
|
6105
|
+
else console.log(`${DIM$10}skills.sh 上未找到 "${query}" 的技能${RESET$10}`);
|
|
5959
6106
|
return;
|
|
5960
6107
|
}
|
|
5961
6108
|
if (json) {
|
|
@@ -5976,15 +6123,15 @@ async function searchMarketplace(query, json = false) {
|
|
|
5976
6123
|
console.log(`skills.sh 上找到 ${skills.length} 个技能:\n`);
|
|
5977
6124
|
for (const skill of skills.slice(0, 12)) {
|
|
5978
6125
|
const installs = formatInstalls(skill.installs);
|
|
5979
|
-
console.log(` ${BOLD$
|
|
5980
|
-
if (skill.source) console.log(` ${DIM$
|
|
5981
|
-
if (installs) console.log(` ${CYAN}${installs}${RESET$
|
|
5982
|
-
console.log(` ${TEXT$
|
|
5983
|
-
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}`);
|
|
5984
6131
|
console.log();
|
|
5985
6132
|
}
|
|
5986
6133
|
} catch {
|
|
5987
|
-
console.log(`${DIM$
|
|
6134
|
+
console.log(`${DIM$10}skills.sh 市场不可用${RESET$10}`);
|
|
5988
6135
|
}
|
|
5989
6136
|
}
|
|
5990
6137
|
function formatInstalls(count) {
|
|
@@ -6001,13 +6148,13 @@ function getOptionValue$2(args, flag) {
|
|
|
6001
6148
|
const idx = args.indexOf(flag);
|
|
6002
6149
|
return idx !== -1 && args[idx + 1] ? args[idx + 1] : void 0;
|
|
6003
6150
|
}
|
|
6004
|
-
const RESET$
|
|
6005
|
-
const BOLD$
|
|
6006
|
-
const DIM$
|
|
6007
|
-
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";
|
|
6008
6155
|
const YELLOW$3 = "\x1B[33m";
|
|
6009
|
-
const RED$
|
|
6010
|
-
const GREEN$
|
|
6156
|
+
const RED$5 = "\x1B[31m";
|
|
6157
|
+
const GREEN$6 = "\x1B[32m";
|
|
6011
6158
|
async function runInfo(args, globalOpts) {
|
|
6012
6159
|
const ref = args[0];
|
|
6013
6160
|
if (!ref) {
|
|
@@ -6023,35 +6170,35 @@ async function runInfo(args, globalOpts) {
|
|
|
6023
6170
|
const result = await api.getSkill(canonical);
|
|
6024
6171
|
const skill = result.data || result.skill || result;
|
|
6025
6172
|
if (!skill || !skill.slug) {
|
|
6026
|
-
console.log(`${DIM$
|
|
6173
|
+
console.log(`${DIM$9}未找到技能:${ref}${RESET$9}`);
|
|
6027
6174
|
process.exit(1);
|
|
6028
6175
|
}
|
|
6029
6176
|
console.log();
|
|
6030
|
-
console.log(`${BOLD$
|
|
6031
|
-
console.log(`${DIM$
|
|
6032
|
-
console.log(` ${BOLD$
|
|
6033
|
-
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}`);
|
|
6034
6181
|
const owner = skill.owner;
|
|
6035
6182
|
if (owner) {
|
|
6036
|
-
console.log(` ${BOLD$
|
|
6037
|
-
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}`);
|
|
6038
6185
|
}
|
|
6039
6186
|
const latestVersion = skill.latestVersion;
|
|
6040
6187
|
if (latestVersion) {
|
|
6041
|
-
console.log(` ${BOLD$
|
|
6042
|
-
if (latestVersion.license) console.log(` ${BOLD$
|
|
6043
|
-
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}`);
|
|
6044
6191
|
if (latestVersion.createdAt) {
|
|
6045
6192
|
const date = new Date(latestVersion.createdAt).toISOString().split("T")[0];
|
|
6046
|
-
console.log(` ${BOLD$
|
|
6193
|
+
console.log(` ${BOLD$6}发布时间:${RESET$9} ${TEXT$7}${date}${RESET$9}`);
|
|
6047
6194
|
}
|
|
6048
6195
|
}
|
|
6049
6196
|
const stats = skill.stats;
|
|
6050
6197
|
if (stats) {
|
|
6051
6198
|
console.log();
|
|
6052
|
-
console.log(` ${BOLD$
|
|
6053
|
-
if (stats.downloads !== void 0) console.log(` 下载量:${TEXT$
|
|
6054
|
-
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}`);
|
|
6055
6202
|
}
|
|
6056
6203
|
const tags = skill.tags;
|
|
6057
6204
|
if (Array.isArray(tags) && tags.length > 0) {
|
|
@@ -6059,28 +6206,28 @@ async function runInfo(args, globalOpts) {
|
|
|
6059
6206
|
const tag = t;
|
|
6060
6207
|
return tag.name || tag;
|
|
6061
6208
|
}).join(", ");
|
|
6062
|
-
console.log(` ${BOLD$
|
|
6209
|
+
console.log(` ${BOLD$6}标签:${RESET$9} ${TEXT$7}${tagList}${RESET$9}`);
|
|
6063
6210
|
}
|
|
6064
6211
|
const moderation = skill.moderation;
|
|
6065
6212
|
if (moderation) {
|
|
6066
6213
|
const verdict = moderation.verdict;
|
|
6067
6214
|
if (verdict === "MALWARE_BLOCKED" || moderation.isMalwareBlocked) {
|
|
6068
6215
|
console.log();
|
|
6069
|
-
console.log(` ${RED$
|
|
6070
|
-
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}`);
|
|
6071
6218
|
} else if (verdict === "SUSPICIOUS" || moderation.isSuspicious) {
|
|
6072
6219
|
console.log();
|
|
6073
|
-
console.log(` ${YELLOW$3}⚠ 可疑技能${RESET$
|
|
6074
|
-
if (moderation.summary) console.log(` ${YELLOW$3}${moderation.summary}${RESET$
|
|
6075
|
-
} 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}`);
|
|
6076
6223
|
}
|
|
6077
6224
|
if (skill.createdAt) {
|
|
6078
6225
|
const created = new Date(skill.createdAt).toISOString().split("T")[0];
|
|
6079
|
-
console.log(` ${BOLD$
|
|
6226
|
+
console.log(` ${BOLD$6}创建时间:${RESET$9} ${TEXT$7}${created}${RESET$9}`);
|
|
6080
6227
|
}
|
|
6081
6228
|
if (skill.updatedAt) {
|
|
6082
6229
|
const updated = new Date(skill.updatedAt).toISOString().split("T")[0];
|
|
6083
|
-
console.log(` ${BOLD$
|
|
6230
|
+
console.log(` ${BOLD$6}更新时间:${RESET$9} ${TEXT$7}${updated}${RESET$9}`);
|
|
6084
6231
|
}
|
|
6085
6232
|
console.log();
|
|
6086
6233
|
} catch (error) {
|
|
@@ -6093,8 +6240,8 @@ function truncate$1(text, maxLen) {
|
|
|
6093
6240
|
if (text.length <= maxLen) return text;
|
|
6094
6241
|
return text.slice(0, maxLen) + "...";
|
|
6095
6242
|
}
|
|
6096
|
-
const DIM$
|
|
6097
|
-
const RESET$
|
|
6243
|
+
const DIM$8 = "\x1B[38;5;102m";
|
|
6244
|
+
const RESET$8 = "\x1B[0m";
|
|
6098
6245
|
async function runConfig(args, globalOpts) {
|
|
6099
6246
|
switch (args[0]) {
|
|
6100
6247
|
case "get":
|
|
@@ -6189,12 +6336,12 @@ async function handleSet(args, _globalOpts) {
|
|
|
6189
6336
|
}
|
|
6190
6337
|
const isGlobal = args.includes("--global");
|
|
6191
6338
|
setConfigValue(key, value, isGlobal);
|
|
6192
|
-
console.log(`${DIM$
|
|
6339
|
+
console.log(`${DIM$8}✓${RESET$8} 已设置 ${key} = ${value}(${(isGlobal ? "global" : "project") === "global" ? "全局" : "项目"})`);
|
|
6193
6340
|
}
|
|
6194
|
-
const RESET$
|
|
6195
|
-
const BOLD$
|
|
6196
|
-
const DIM$
|
|
6197
|
-
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";
|
|
6198
6345
|
function formatFileSize(bytes) {
|
|
6199
6346
|
if (bytes < 1024) return `${bytes} B`;
|
|
6200
6347
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
@@ -6221,9 +6368,9 @@ function printDocsHelp() {
|
|
|
6221
6368
|
console.log(" bfi docs info <id> 查看文档处理状态");
|
|
6222
6369
|
console.log();
|
|
6223
6370
|
console.log("示例:");
|
|
6224
|
-
console.log(` ${DIM$
|
|
6225
|
-
console.log(` ${DIM$
|
|
6226
|
-
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`);
|
|
6227
6374
|
}
|
|
6228
6375
|
async function handleUpload(args, globalOpts) {
|
|
6229
6376
|
const filePath = args[0];
|
|
@@ -6246,18 +6393,18 @@ async function handleUpload(args, globalOpts) {
|
|
|
6246
6393
|
const api = new SkillHubAPI(registry, token);
|
|
6247
6394
|
const fileName = path$1.basename(resolvedPath);
|
|
6248
6395
|
const fileSizeStr = formatFileSize(stats.size);
|
|
6249
|
-
console.log(`${DIM$
|
|
6396
|
+
console.log(`${DIM$7}正在上传 ${fileName} (${fileSizeStr})...${RESET$7}`);
|
|
6250
6397
|
try {
|
|
6251
6398
|
const fileBuffer = fs.readFileSync(resolvedPath);
|
|
6252
6399
|
const result = await api.uploadDoc(fileBuffer, fileName);
|
|
6253
6400
|
const doc = result.data || result;
|
|
6254
|
-
console.log(`${TEXT$
|
|
6401
|
+
console.log(`${TEXT$6}✓ 上传成功${RESET$7}`);
|
|
6255
6402
|
console.log();
|
|
6256
|
-
if (doc.id) console.log(` ${BOLD$
|
|
6257
|
-
if (doc.status) console.log(` ${BOLD$
|
|
6258
|
-
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}`);
|
|
6259
6406
|
console.log();
|
|
6260
|
-
if (doc.id) console.log(`${DIM$
|
|
6407
|
+
if (doc.id) console.log(`${DIM$7}查看状态:bfi docs info ${doc.id}${RESET$7}`);
|
|
6261
6408
|
} catch (error) {
|
|
6262
6409
|
console.error(`上传失败:${error.message}`);
|
|
6263
6410
|
process.exit(1);
|
|
@@ -6273,14 +6420,14 @@ async function handleList(args, globalOpts) {
|
|
|
6273
6420
|
const data = result.data || result;
|
|
6274
6421
|
const docs = data.items || data.content || data.docs || (Array.isArray(data) ? data : []);
|
|
6275
6422
|
if (!Array.isArray(docs) || docs.length === 0) {
|
|
6276
|
-
console.log(`${DIM$
|
|
6423
|
+
console.log(`${DIM$7}未找到文档。${RESET$7}`);
|
|
6277
6424
|
return;
|
|
6278
6425
|
}
|
|
6279
6426
|
const total = data.total || data.totalElements || result.total || result.totalElements;
|
|
6280
6427
|
console.log();
|
|
6281
|
-
if (total) console.log(`${BOLD$
|
|
6282
|
-
else console.log(`${BOLD$
|
|
6283
|
-
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}`);
|
|
6284
6431
|
for (const doc of docs) {
|
|
6285
6432
|
const id = doc.id || "?";
|
|
6286
6433
|
const fileName = doc.fileName || doc.file_name || "unknown";
|
|
@@ -6290,11 +6437,11 @@ async function handleList(args, globalOpts) {
|
|
|
6290
6437
|
const ip = doc.clientIp || "N/A";
|
|
6291
6438
|
const createdAt = doc.createdAt || doc.created_at;
|
|
6292
6439
|
const dateStr = createdAt ? new Date(createdAt).toISOString().split("T")[0] : "N/A";
|
|
6293
|
-
console.log(` ${BOLD$
|
|
6294
|
-
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}`);
|
|
6295
6442
|
console.log();
|
|
6296
6443
|
}
|
|
6297
|
-
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}`);
|
|
6298
6445
|
console.log();
|
|
6299
6446
|
} catch (error) {
|
|
6300
6447
|
console.error(`列出文档失败:${error.message}`);
|
|
@@ -6318,43 +6465,43 @@ async function handleInfo(args, globalOpts) {
|
|
|
6318
6465
|
const result = await api.getDoc(id);
|
|
6319
6466
|
const doc = result.data || result;
|
|
6320
6467
|
if (!doc || !doc.id) {
|
|
6321
|
-
console.log(`${DIM$
|
|
6468
|
+
console.log(`${DIM$7}未找到文档:${id}${RESET$7}`);
|
|
6322
6469
|
process.exit(1);
|
|
6323
6470
|
}
|
|
6324
6471
|
const content = doc.content;
|
|
6325
6472
|
const hasContent = typeof content === "string" && content.trim().length > 0;
|
|
6326
6473
|
console.log();
|
|
6327
|
-
console.log(`${BOLD$
|
|
6328
|
-
console.log(`${DIM$
|
|
6329
|
-
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}`);
|
|
6330
6477
|
const fileSize = doc.fileSize || doc.file_size;
|
|
6331
|
-
if (fileSize) console.log(` ${BOLD$
|
|
6478
|
+
if (fileSize) console.log(` ${BOLD$5}大小:${RESET$7} ${TEXT$6}${formatFileSize(Number(fileSize))}${RESET$7}`);
|
|
6332
6479
|
const user = doc.userDisplayName || doc.userId;
|
|
6333
|
-
if (user) console.log(` ${BOLD$
|
|
6334
|
-
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}`);
|
|
6335
6482
|
if (doc.status) {
|
|
6336
|
-
const statusColor = doc.status === "completed" || doc.status === "COMPLETED" ? "\x1B[32m" : doc.status === "failed" || doc.status === "FAILED" ? "\x1B[31m" : TEXT$
|
|
6337
|
-
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}`);
|
|
6338
6485
|
}
|
|
6339
6486
|
if (doc.createdAt || doc.created_at) {
|
|
6340
6487
|
const date = new Date(doc.createdAt || doc.created_at).toISOString().replace("T", " ").slice(0, 19);
|
|
6341
|
-
console.log(` ${BOLD$
|
|
6488
|
+
console.log(` ${BOLD$5}创建时间:${RESET$7} ${TEXT$6}${date}${RESET$7}`);
|
|
6342
6489
|
}
|
|
6343
6490
|
if (doc.updatedAt || doc.updated_at) {
|
|
6344
6491
|
const date = new Date(doc.updatedAt || doc.updated_at).toISOString().replace("T", " ").slice(0, 19);
|
|
6345
|
-
console.log(` ${BOLD$
|
|
6492
|
+
console.log(` ${BOLD$5}更新时间:${RESET$7} ${TEXT$6}${date}${RESET$7}`);
|
|
6346
6493
|
}
|
|
6347
6494
|
if (doc.error || doc.errorMessage) {
|
|
6348
6495
|
console.log();
|
|
6349
|
-
console.log(` \x1b[31m${BOLD$
|
|
6350
|
-
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}`);
|
|
6351
6498
|
}
|
|
6352
6499
|
if (hasContent) {
|
|
6353
6500
|
console.log();
|
|
6354
|
-
console.log(` ${BOLD$
|
|
6355
|
-
console.log(`${DIM$
|
|
6356
|
-
for (const line of content.split("\n")) console.log(` ${TEXT$
|
|
6357
|
-
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}`);
|
|
6358
6505
|
}
|
|
6359
6506
|
console.log();
|
|
6360
6507
|
} catch (error) {
|
|
@@ -6363,13 +6510,13 @@ async function handleInfo(args, globalOpts) {
|
|
|
6363
6510
|
process.exit(1);
|
|
6364
6511
|
}
|
|
6365
6512
|
}
|
|
6366
|
-
const RESET$
|
|
6367
|
-
const BOLD$
|
|
6368
|
-
const DIM$
|
|
6369
|
-
const TEXT$
|
|
6370
|
-
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";
|
|
6371
6518
|
const YELLOW$2 = "\x1B[33m";
|
|
6372
|
-
const RED$
|
|
6519
|
+
const RED$4 = "\x1B[31m";
|
|
6373
6520
|
async function runPublish(args, globalOpts) {
|
|
6374
6521
|
const fromIdx = args.indexOf("--from");
|
|
6375
6522
|
const mode = fromIdx !== -1 ? args[fromIdx + 1] : "dir";
|
|
@@ -6430,23 +6577,23 @@ async function publishFromDir(positionalArgs, api, namespace, visibility) {
|
|
|
6430
6577
|
}
|
|
6431
6578
|
const { data: frontmatter } = parseFrontmatter(fs.readFileSync(skillMdPath, "utf-8"));
|
|
6432
6579
|
const skillName = frontmatter.name || path$1.basename(resolvedDir);
|
|
6433
|
-
console.log(`${DIM$
|
|
6434
|
-
console.log(` ${BOLD$
|
|
6435
|
-
console.log(` ${BOLD$
|
|
6436
|
-
console.log(` ${BOLD$
|
|
6437
|
-
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}`);
|
|
6438
6585
|
console.log();
|
|
6439
|
-
console.log(`${DIM$
|
|
6586
|
+
console.log(`${DIM$6}正在创建 zip...${RESET$6}`);
|
|
6440
6587
|
const zipBuffer = createZipFromDir(resolvedDir);
|
|
6441
|
-
console.log(` ${GREEN$
|
|
6442
|
-
console.log(`${DIM$
|
|
6588
|
+
console.log(` ${GREEN$5}Zip 已创建 (${formatSize(zipBuffer.length)})${RESET$6}`);
|
|
6589
|
+
console.log(`${DIM$6}正在上传到 SkillHub...${RESET$6}`);
|
|
6443
6590
|
try {
|
|
6444
6591
|
const res = await api.publishSkill(namespace, zipBuffer, visibility, true);
|
|
6445
|
-
console.log(` ${GREEN$
|
|
6446
|
-
if (res.slug) console.log(` ${BOLD$
|
|
6447
|
-
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}`);
|
|
6448
6595
|
} catch (error) {
|
|
6449
|
-
console.error(` ${RED$
|
|
6596
|
+
console.error(` ${RED$4}发布失败:${error.message}${RESET$6}`);
|
|
6450
6597
|
process.exit(1);
|
|
6451
6598
|
}
|
|
6452
6599
|
console.log();
|
|
@@ -6463,34 +6610,34 @@ async function publishFromZip(positionalArgs, api, namespace, visibility) {
|
|
|
6463
6610
|
console.error(`文件未找到:${resolvedZip}`);
|
|
6464
6611
|
process.exit(1);
|
|
6465
6612
|
}
|
|
6466
|
-
console.log(`${DIM$
|
|
6613
|
+
console.log(`${DIM$6}正在解压 zip...${RESET$6}`);
|
|
6467
6614
|
const tmpDir = path$1.join(os$1.tmpdir(), `.bfi-publish-${Date.now()}`);
|
|
6468
6615
|
fs.mkdirSync(tmpDir, { recursive: true });
|
|
6469
6616
|
try {
|
|
6470
6617
|
await extractZip(resolvedZip, tmpDir);
|
|
6471
6618
|
const skillMdPath = findSkillMd(tmpDir);
|
|
6472
6619
|
if (!skillMdPath) {
|
|
6473
|
-
console.error(` ${RED$
|
|
6620
|
+
console.error(` ${RED$4}zip 文件中未找到 SKILL.md${RESET$6}`);
|
|
6474
6621
|
process.exit(1);
|
|
6475
6622
|
}
|
|
6476
6623
|
const skillDir = path$1.dirname(skillMdPath);
|
|
6477
|
-
console.log(` ${GREEN$
|
|
6624
|
+
console.log(` ${GREEN$5}在 ${path$1.relative(tmpDir, skillDir) || "."} 中找到 SKILL.md${RESET$6}`);
|
|
6478
6625
|
const { data: frontmatter } = parseFrontmatter(fs.readFileSync(skillMdPath, "utf-8"));
|
|
6479
6626
|
const skillName = frontmatter.name || path$1.basename(skillDir);
|
|
6480
|
-
console.log(` ${BOLD$
|
|
6481
|
-
console.log(` ${BOLD$
|
|
6482
|
-
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}`);
|
|
6483
6630
|
console.log();
|
|
6484
|
-
console.log(`${DIM$
|
|
6631
|
+
console.log(`${DIM$6}正在重新打包...${RESET$6}`);
|
|
6485
6632
|
const zipBuffer = createZipFromDir(skillDir);
|
|
6486
|
-
console.log(` ${GREEN$
|
|
6487
|
-
console.log(`${DIM$
|
|
6633
|
+
console.log(` ${GREEN$5}Zip 已创建 (${formatSize(zipBuffer.length)})${RESET$6}`);
|
|
6634
|
+
console.log(`${DIM$6}正在上传到 SkillHub...${RESET$6}`);
|
|
6488
6635
|
const res = await api.publishSkill(namespace, zipBuffer, visibility, true);
|
|
6489
|
-
console.log(` ${GREEN$
|
|
6490
|
-
if (res.slug) console.log(` ${BOLD$
|
|
6491
|
-
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}`);
|
|
6492
6639
|
} catch (error) {
|
|
6493
|
-
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}`);
|
|
6494
6641
|
process.exit(1);
|
|
6495
6642
|
} finally {
|
|
6496
6643
|
try {
|
|
@@ -6519,18 +6666,18 @@ async function publishFromGit(positionalArgs, api, namespace, visibility, yes, b
|
|
|
6519
6666
|
}
|
|
6520
6667
|
const effectiveBranch = branch || parsedRef || "main";
|
|
6521
6668
|
const effectiveSubpath = directory || null;
|
|
6522
|
-
console.log(`${DIM$
|
|
6523
|
-
console.log(` ${BOLD$
|
|
6524
|
-
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}`);
|
|
6525
6672
|
console.log();
|
|
6526
6673
|
let tmpDir = null;
|
|
6527
6674
|
let skills = [];
|
|
6528
6675
|
try {
|
|
6529
|
-
console.log(`${DIM$
|
|
6676
|
+
console.log(`${DIM$6}正在克隆仓库...${RESET$6}`);
|
|
6530
6677
|
const { cloneRepo } = await Promise.resolve().then(() => git_exports);
|
|
6531
6678
|
tmpDir = await cloneRepo(repoUrl, effectiveBranch);
|
|
6532
|
-
console.log(` ${GREEN$
|
|
6533
|
-
console.log(`${DIM$
|
|
6679
|
+
console.log(` ${GREEN$5}仓库克隆完成${RESET$6}`);
|
|
6680
|
+
console.log(`${DIM$6}正在发现技能...${RESET$6}`);
|
|
6534
6681
|
const { discoverSkills } = await import("./_chunks/skills.mjs").then((n) => n.skills_exports);
|
|
6535
6682
|
skills = (await discoverSkills(tmpDir, effectiveSubpath || void 0)).map((s) => ({
|
|
6536
6683
|
path: s.path,
|
|
@@ -6538,7 +6685,7 @@ async function publishFromGit(positionalArgs, api, namespace, visibility, yes, b
|
|
|
6538
6685
|
description: s.description || ""
|
|
6539
6686
|
}));
|
|
6540
6687
|
} catch (error) {
|
|
6541
|
-
console.error(` ${RED$
|
|
6688
|
+
console.error(` ${RED$4}克隆或发现失败:${error.message}${RESET$6}`);
|
|
6542
6689
|
if (tmpDir) try {
|
|
6543
6690
|
const { cleanupTempDir } = await Promise.resolve().then(() => git_exports);
|
|
6544
6691
|
await cleanupTempDir(tmpDir);
|
|
@@ -6546,7 +6693,7 @@ async function publishFromGit(positionalArgs, api, namespace, visibility, yes, b
|
|
|
6546
6693
|
process.exit(1);
|
|
6547
6694
|
}
|
|
6548
6695
|
if (skills.length === 0) {
|
|
6549
|
-
console.log(`${YELLOW$2}仓库中未发现技能。${RESET$
|
|
6696
|
+
console.log(`${YELLOW$2}仓库中未发现技能。${RESET$6}`);
|
|
6550
6697
|
if (tmpDir) try {
|
|
6551
6698
|
const { cleanupTempDir } = await Promise.resolve().then(() => git_exports);
|
|
6552
6699
|
await cleanupTempDir(tmpDir);
|
|
@@ -6558,7 +6705,7 @@ async function publishFromGit(positionalArgs, api, namespace, visibility, yes, b
|
|
|
6558
6705
|
const filters = skillFilter.split(",").map((s) => s.trim().toLowerCase());
|
|
6559
6706
|
selectedSkills = skills.filter((s) => filters.some((f) => s.name.toLowerCase().includes(f)));
|
|
6560
6707
|
if (selectedSkills.length === 0) {
|
|
6561
|
-
console.log(`${YELLOW$2}未找到匹配 "${skillFilter}" 的技能${RESET$
|
|
6708
|
+
console.log(`${YELLOW$2}未找到匹配 "${skillFilter}" 的技能${RESET$6}`);
|
|
6562
6709
|
console.log(`可用:${skills.map((s) => s.name).join(", ")}`);
|
|
6563
6710
|
try {
|
|
6564
6711
|
fs.rmSync(tmpDir, {
|
|
@@ -6569,12 +6716,12 @@ async function publishFromGit(positionalArgs, api, namespace, visibility, yes, b
|
|
|
6569
6716
|
process.exit(1);
|
|
6570
6717
|
}
|
|
6571
6718
|
}
|
|
6572
|
-
console.log(`\n${BOLD$
|
|
6719
|
+
console.log(`\n${BOLD$4}发现 ${skills.length} 个技能,将发布 ${selectedSkills.length} 个:${RESET$6}\n`);
|
|
6573
6720
|
for (const skill of selectedSkills) {
|
|
6574
6721
|
const relPath = path$1.relative(tmpDir, skill.path) || ".";
|
|
6575
|
-
console.log(` ${BOLD$
|
|
6576
|
-
console.log(` ${DIM$
|
|
6577
|
-
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}`);
|
|
6578
6725
|
console.log();
|
|
6579
6726
|
}
|
|
6580
6727
|
if (!yes) {
|
|
@@ -6593,17 +6740,17 @@ async function publishFromGit(positionalArgs, api, namespace, visibility, yes, b
|
|
|
6593
6740
|
const { createZipFromDir } = await Promise.resolve().then(() => zip_exports);
|
|
6594
6741
|
let successCount = 0;
|
|
6595
6742
|
for (const skill of selectedSkills) {
|
|
6596
|
-
console.log(`${DIM$
|
|
6743
|
+
console.log(`${DIM$6}正在发布 ${skill.name}...${RESET$6}`);
|
|
6597
6744
|
try {
|
|
6598
6745
|
const zipBuffer = createZipFromDir(skill.path);
|
|
6599
6746
|
const res = await api.publishSkill(namespace, zipBuffer, visibility, true);
|
|
6600
|
-
console.log(` ${GREEN$
|
|
6747
|
+
console.log(` ${GREEN$5}✓ 已发布:${namespace}--${res.slug || skill.name}${RESET$6}`);
|
|
6601
6748
|
successCount++;
|
|
6602
6749
|
} catch (error) {
|
|
6603
|
-
console.error(` ${RED$
|
|
6750
|
+
console.error(` ${RED$4}✗ ${skill.name}:${error.message}${RESET$6}`);
|
|
6604
6751
|
}
|
|
6605
6752
|
}
|
|
6606
|
-
console.log(`\n${GREEN$
|
|
6753
|
+
console.log(`\n${GREEN$5}发布完成:${successCount}/${selectedSkills.length} 个技能${RESET$6}`);
|
|
6607
6754
|
if (tmpDir) try {
|
|
6608
6755
|
const { cleanupTempDir } = await Promise.resolve().then(() => git_exports);
|
|
6609
6756
|
await cleanupTempDir(tmpDir);
|
|
@@ -6653,11 +6800,11 @@ function truncate(text, maxLen) {
|
|
|
6653
6800
|
return text.slice(0, maxLen) + "...";
|
|
6654
6801
|
}
|
|
6655
6802
|
var package_exports = /* @__PURE__ */ __exportAll({ runPackage: () => runPackage });
|
|
6656
|
-
const RESET$
|
|
6657
|
-
const BOLD$
|
|
6658
|
-
const DIM$
|
|
6659
|
-
const GREEN$
|
|
6660
|
-
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";
|
|
6661
6808
|
const YELLOW$1 = "\x1B[33m";
|
|
6662
6809
|
async function runPackage(args, globalOpts) {
|
|
6663
6810
|
const subcommand = args[0];
|
|
@@ -6703,21 +6850,21 @@ function getWorkdir(globalOpts, args) {
|
|
|
6703
6850
|
return globalOpts.workdir || process.cwd();
|
|
6704
6851
|
}
|
|
6705
6852
|
function printPackageHelp() {
|
|
6706
|
-
console.log(`${BOLD$
|
|
6853
|
+
console.log(`${BOLD$3}包命令:${RESET$5}`);
|
|
6707
6854
|
console.log();
|
|
6708
|
-
console.log(`${BOLD$
|
|
6709
|
-
console.log(` ${DIM$
|
|
6710
|
-
console.log(` ${DIM$
|
|
6711
|
-
console.log(` ${DIM$
|
|
6712
|
-
console.log(` ${DIM$
|
|
6713
|
-
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} 搜索包`);
|
|
6714
6861
|
console.log();
|
|
6715
|
-
console.log(`${BOLD$
|
|
6716
|
-
console.log(` ${DIM$
|
|
6717
|
-
console.log(` ${DIM$
|
|
6718
|
-
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} 从包移除技能`);
|
|
6719
6866
|
console.log();
|
|
6720
|
-
console.log(`${BOLD$
|
|
6867
|
+
console.log(`${BOLD$3}选项:${RESET$5}`);
|
|
6721
6868
|
console.log(` --namespace <ns> 目标命名空间`);
|
|
6722
6869
|
console.log(` --name <name> 显示名称(create 时)`);
|
|
6723
6870
|
console.log(` --description <t> 描述(create 时)`);
|
|
@@ -6734,21 +6881,66 @@ async function handleLocalList(args, globalOpts) {
|
|
|
6734
6881
|
}
|
|
6735
6882
|
if (entries.length === 0) {
|
|
6736
6883
|
console.log("没有已安装的包。");
|
|
6737
|
-
console.log(`使用 ${DIM$
|
|
6884
|
+
console.log(`使用 ${DIM$5}bfi skills package install <ref>${RESET$5} 安装包。`);
|
|
6738
6885
|
return;
|
|
6739
6886
|
}
|
|
6740
6887
|
console.log();
|
|
6741
|
-
console.log(`${BOLD$
|
|
6888
|
+
console.log(`${BOLD$3}已安装的包(${entries.length} 个):${RESET$5}`);
|
|
6742
6889
|
console.log();
|
|
6743
6890
|
for (const [ref, info] of entries) {
|
|
6744
6891
|
const pkg = info;
|
|
6745
6892
|
const skills = lock.skills || {};
|
|
6746
6893
|
const pkgSkills = Object.entries(skills).filter(([, s]) => s.package?.slug === pkg.slug);
|
|
6747
|
-
console.log(` ${BOLD$
|
|
6748
|
-
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}`);
|
|
6749
6896
|
console.log();
|
|
6750
6897
|
}
|
|
6751
6898
|
}
|
|
6899
|
+
function targetDisplayName(target) {
|
|
6900
|
+
const base = agents[target.agent].displayName;
|
|
6901
|
+
return target.subagent ? `${base} (${target.subagent})` : base;
|
|
6902
|
+
}
|
|
6903
|
+
function targetKey(target) {
|
|
6904
|
+
return target.subagent ? `${target.agent}:${target.subagent}` : target.agent;
|
|
6905
|
+
}
|
|
6906
|
+
function buildInstallTargets(targetAgents, eveSubagentTargets) {
|
|
6907
|
+
const targets = [];
|
|
6908
|
+
for (const agent of targetAgents) if (agent === "eve") for (const subagent of eveSubagentTargets) targets.push({
|
|
6909
|
+
agent,
|
|
6910
|
+
subagent
|
|
6911
|
+
});
|
|
6912
|
+
else targets.push({ agent });
|
|
6913
|
+
return targets;
|
|
6914
|
+
}
|
|
6915
|
+
function splitAgentsByType(agentTypes) {
|
|
6916
|
+
const universal = [];
|
|
6917
|
+
const symlinked = [];
|
|
6918
|
+
for (const a of agentTypes) if (isUniversalAgent(a)) universal.push(agents[a].displayName);
|
|
6919
|
+
else symlinked.push(agents[a].displayName);
|
|
6920
|
+
return {
|
|
6921
|
+
universal,
|
|
6922
|
+
symlinked
|
|
6923
|
+
};
|
|
6924
|
+
}
|
|
6925
|
+
function formatList(items, maxShow = 5) {
|
|
6926
|
+
if (items.length <= maxShow) return items.join(", ");
|
|
6927
|
+
return `${items.slice(0, maxShow).join(", ")} +${items.length - maxShow} more`;
|
|
6928
|
+
}
|
|
6929
|
+
function buildTargetSummaryLines(targets, installMode) {
|
|
6930
|
+
const lines = [];
|
|
6931
|
+
const rootAgents = targets.filter((t) => !t.subagent).map((t) => t.agent);
|
|
6932
|
+
const subagentNames = targets.filter((t) => t.subagent).map(targetDisplayName);
|
|
6933
|
+
const { universal, symlinked } = splitAgentsByType(rootAgents);
|
|
6934
|
+
if (installMode === "symlink") {
|
|
6935
|
+
if (universal.length > 0) lines.push(` ${import_picocolors.default.green("共享目录:")} ${formatList(universal, 3)}`);
|
|
6936
|
+
if (symlinked.length > 0) lines.push(` ${import_picocolors.default.dim("符号链接 →")} ${formatList(symlinked)}`);
|
|
6937
|
+
if (subagentNames.length > 0) lines.push(` ${import_picocolors.default.dim("copy →")} ${formatList(subagentNames)}`);
|
|
6938
|
+
} else {
|
|
6939
|
+
const allNames = targets.map(targetDisplayName);
|
|
6940
|
+
lines.push(` ${import_picocolors.default.dim("copy →")} ${formatList(allNames)}`);
|
|
6941
|
+
}
|
|
6942
|
+
return lines;
|
|
6943
|
+
}
|
|
6752
6944
|
async function handleInstall(args, api, globalOpts) {
|
|
6753
6945
|
const pkgRef = args[0];
|
|
6754
6946
|
if (!pkgRef) {
|
|
@@ -6757,126 +6949,297 @@ async function handleInstall(args, api, globalOpts) {
|
|
|
6757
6949
|
process.exit(1);
|
|
6758
6950
|
}
|
|
6759
6951
|
const { namespace, slug } = parseSkillRef(pkgRef, globalOpts.namespace);
|
|
6760
|
-
|
|
6952
|
+
const isYes = args.includes("-y") || args.includes("--yes") || globalOpts.yes;
|
|
6953
|
+
const isGlobal = args.includes("-g") || args.includes("--global");
|
|
6954
|
+
console.log(`${DIM$5}正在获取包信息...${RESET$5}`);
|
|
6761
6955
|
let pkgInfo;
|
|
6762
6956
|
try {
|
|
6763
6957
|
pkgInfo = await api.getPackage(namespace, slug);
|
|
6764
6958
|
pkgInfo = pkgInfo.data || pkgInfo;
|
|
6765
6959
|
} catch (error) {
|
|
6766
|
-
console.error(`${RED$
|
|
6960
|
+
console.error(`${RED$3}获取包信息失败:${error.message}${RESET$5}`);
|
|
6767
6961
|
process.exit(1);
|
|
6768
6962
|
}
|
|
6769
6963
|
const skills = pkgInfo.skills || [];
|
|
6770
6964
|
if (skills.length === 0) {
|
|
6771
|
-
console.log(`${YELLOW$1}包中没有技能。${RESET$
|
|
6965
|
+
console.log(`${YELLOW$1}包中没有技能。${RESET$5}`);
|
|
6772
6966
|
return;
|
|
6773
6967
|
}
|
|
6774
|
-
console.log(`${BOLD$
|
|
6968
|
+
console.log(`${BOLD$3}${pkgInfo.name || slug}${RESET$5}(${skills.length} 个技能):`);
|
|
6775
6969
|
console.log();
|
|
6776
|
-
const { runAdd } = await Promise.resolve().then(() => add_exports);
|
|
6777
|
-
const installedSkills = [];
|
|
6778
6970
|
for (const skill of skills) {
|
|
6779
|
-
const
|
|
6780
|
-
console.log(` ${DIM$
|
|
6781
|
-
try {
|
|
6782
|
-
await runAdd([skillRef], {
|
|
6783
|
-
...globalOpts,
|
|
6784
|
-
yes: true
|
|
6785
|
-
});
|
|
6786
|
-
console.log(` ${GREEN$2}✓ ${skill.slug}${RESET$3}`);
|
|
6787
|
-
installedSkills.push(skill.slug);
|
|
6788
|
-
} catch (error) {
|
|
6789
|
-
console.log(` ${RED$1}✗ ${skill.slug}:${error.message}${RESET$3}`);
|
|
6790
|
-
}
|
|
6791
|
-
}
|
|
6792
|
-
const cwd = getWorkdir(globalOpts, args);
|
|
6793
|
-
const lock = loadLockfile(cwd);
|
|
6794
|
-
const pkgKey = `@${namespace}/${slug}`;
|
|
6795
|
-
if (!lock.packages) lock.packages = {};
|
|
6796
|
-
lock.packages[pkgKey] = {
|
|
6797
|
-
namespace,
|
|
6798
|
-
slug,
|
|
6799
|
-
name: pkgInfo.displayName || pkgInfo.name || slug,
|
|
6800
|
-
version: pkgInfo.version || "latest",
|
|
6801
|
-
skillCount: installedSkills.length
|
|
6802
|
-
};
|
|
6803
|
-
for (const skillSlug of installedSkills) {
|
|
6804
|
-
if (!lock.skills[skillSlug]) lock.skills[skillSlug] = {
|
|
6805
|
-
namespace,
|
|
6806
|
-
slug,
|
|
6807
|
-
version: "latest"
|
|
6808
|
-
};
|
|
6809
|
-
lock.skills[skillSlug].package = {
|
|
6810
|
-
namespace,
|
|
6811
|
-
slug
|
|
6812
|
-
};
|
|
6971
|
+
const ref = `@${skill.namespace || namespace}/${skill.slug}`;
|
|
6972
|
+
console.log(` ${DIM$5}• ${skill.displayName || skill.slug}${RESET$5} ${DIM$5}${ref}${RESET$5}`);
|
|
6813
6973
|
}
|
|
6814
|
-
saveLockfile(cwd, lock);
|
|
6815
6974
|
console.log();
|
|
6816
|
-
|
|
6817
|
-
|
|
6818
|
-
|
|
6819
|
-
|
|
6820
|
-
if (
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
|
|
6824
|
-
|
|
6825
|
-
|
|
6826
|
-
|
|
6827
|
-
|
|
6828
|
-
|
|
6829
|
-
|
|
6830
|
-
|
|
6831
|
-
|
|
6832
|
-
|
|
6833
|
-
|
|
6834
|
-
|
|
6835
|
-
const
|
|
6836
|
-
|
|
6837
|
-
|
|
6838
|
-
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6975
|
+
let targetAgents;
|
|
6976
|
+
let installGlobally;
|
|
6977
|
+
let installMode;
|
|
6978
|
+
let eveSubagentTargets = [void 0];
|
|
6979
|
+
if (isYes) {
|
|
6980
|
+
const installed = await detectInstalledAgents();
|
|
6981
|
+
const validAgents = Object.keys(agents);
|
|
6982
|
+
targetAgents = ensureUniversalAgents(installed.length > 0 ? installed : validAgents);
|
|
6983
|
+
installGlobally = isGlobal;
|
|
6984
|
+
installMode = "symlink";
|
|
6985
|
+
console.log(`${DIM$5}安装到:${targetAgents.map((a) => agents[a].displayName).join(", ")}${RESET$5}`);
|
|
6986
|
+
console.log(`${DIM$5}范围:${installGlobally ? "全局" : "项目"} | 方式:${installMode === "symlink" ? "符号链接" : "复制"}${RESET$5}`);
|
|
6987
|
+
console.log();
|
|
6988
|
+
} else {
|
|
6989
|
+
const spinner$1 = spinner();
|
|
6990
|
+
spinner$1.start("正在加载 Agent...");
|
|
6991
|
+
const installedAgents = await detectInstalledAgents();
|
|
6992
|
+
const totalAgents = Object.keys(agents).length;
|
|
6993
|
+
spinner$1.stop(`${totalAgents} agents`);
|
|
6994
|
+
const skillNames = skills.map((s) => s.displayName || s.slug);
|
|
6995
|
+
const skillSubject = skillNames.length <= 3 ? skillNames.map((n) => import_picocolors.default.cyan(n)).join(", ") : `${skills.length} 个技能`;
|
|
6996
|
+
if (installedAgents.includes("eve")) {
|
|
6997
|
+
const useEve = await confirm({
|
|
6998
|
+
message: `检测到 Eve 项目。为您的 eve agent 安装 ${skillSubject}?`,
|
|
6999
|
+
initialValue: true
|
|
7000
|
+
});
|
|
7001
|
+
if (isCancel(useEve)) {
|
|
7002
|
+
cancel("安装已取消");
|
|
7003
|
+
process.exit(0);
|
|
7004
|
+
}
|
|
7005
|
+
if (useEve) {
|
|
7006
|
+
targetAgents = ["eve"];
|
|
7007
|
+
console.log(`${DIM$5}安装到:eve agent${RESET$5}`);
|
|
7008
|
+
} else {
|
|
7009
|
+
const selected = await selectAgentsInteractive({ global: false });
|
|
7010
|
+
if (isCancel(selected)) {
|
|
7011
|
+
cancel("安装已取消");
|
|
7012
|
+
process.exit(0);
|
|
7013
|
+
}
|
|
7014
|
+
targetAgents = selected;
|
|
7015
|
+
}
|
|
7016
|
+
} else if (installedAgents.length === 0) {
|
|
7017
|
+
const selected = await promptForAgents("请选择要安装到的 Agent:", Object.entries(agents).filter(([key]) => key !== "eve").map(([key, config]) => ({
|
|
7018
|
+
value: key,
|
|
7019
|
+
label: config.displayName
|
|
7020
|
+
})));
|
|
7021
|
+
if (isCancel(selected)) {
|
|
7022
|
+
cancel("安装已取消");
|
|
7023
|
+
process.exit(0);
|
|
7024
|
+
}
|
|
7025
|
+
targetAgents = selected;
|
|
7026
|
+
} else if (installedAgents.length === 1) {
|
|
7027
|
+
targetAgents = ensureUniversalAgents(installedAgents);
|
|
7028
|
+
const firstAgent = installedAgents[0];
|
|
7029
|
+
console.log(`${DIM$5}安装到:${agents[firstAgent].displayName}${RESET$5}`);
|
|
7030
|
+
} else {
|
|
7031
|
+
const selected = await selectAgentsInteractive({ global: false });
|
|
7032
|
+
if (isCancel(selected)) {
|
|
7033
|
+
cancel("安装已取消");
|
|
7034
|
+
process.exit(0);
|
|
7035
|
+
}
|
|
7036
|
+
targetAgents = selected;
|
|
7037
|
+
}
|
|
7038
|
+
if (targetAgents.includes("eve")) {
|
|
7039
|
+
const availableSubagents = getEveSubagents(process.cwd());
|
|
7040
|
+
if (availableSubagents.length > 0) {
|
|
7041
|
+
const selectedSubagents = await multiselect$1({
|
|
7042
|
+
message: "Eve 技能安装位置?",
|
|
7043
|
+
options: [{
|
|
7044
|
+
value: "",
|
|
7045
|
+
label: "根 Agent",
|
|
7046
|
+
hint: "agent/skills"
|
|
7047
|
+
}, ...availableSubagents.map((name) => ({
|
|
7048
|
+
value: name,
|
|
7049
|
+
label: name,
|
|
7050
|
+
hint: `agent/subagents/${name}/skills`
|
|
7051
|
+
}))],
|
|
7052
|
+
initialValues: [""],
|
|
7053
|
+
required: true
|
|
7054
|
+
});
|
|
7055
|
+
if (isCancel(selectedSubagents)) {
|
|
7056
|
+
cancel("安装已取消");
|
|
7057
|
+
process.exit(0);
|
|
7058
|
+
}
|
|
7059
|
+
eveSubagentTargets = selectedSubagents.map((s) => s === "" ? void 0 : s);
|
|
7060
|
+
}
|
|
7061
|
+
}
|
|
7062
|
+
const installTargets = buildInstallTargets(targetAgents, eveSubagentTargets);
|
|
7063
|
+
installGlobally = isGlobal;
|
|
7064
|
+
const supportsGlobal = targetAgents.some((a) => agents[a].globalSkillsDir !== void 0);
|
|
7065
|
+
if (!isGlobal && supportsGlobal) {
|
|
7066
|
+
const scope = await select({
|
|
7067
|
+
message: "安装范围",
|
|
7068
|
+
options: [{
|
|
7069
|
+
value: false,
|
|
7070
|
+
label: "项目",
|
|
7071
|
+
hint: "安装到当前目录(随项目一起提交)"
|
|
7072
|
+
}, {
|
|
7073
|
+
value: true,
|
|
7074
|
+
label: "全局",
|
|
7075
|
+
hint: "安装到主目录(跨所有项目可用)"
|
|
7076
|
+
}]
|
|
7077
|
+
});
|
|
7078
|
+
if (isCancel(scope)) {
|
|
7079
|
+
cancel("安装已取消");
|
|
7080
|
+
process.exit(0);
|
|
7081
|
+
}
|
|
7082
|
+
installGlobally = scope;
|
|
7083
|
+
}
|
|
7084
|
+
const allEve = installTargets.every((t) => t.agent === "eve");
|
|
7085
|
+
if (new Set(installTargets.map((t) => t.subagent ? `eve:subagent:${t.subagent}` : agents[t.agent].skillsDir)).size > 1 && !allEve) {
|
|
7086
|
+
const modeChoice = await select({
|
|
7087
|
+
message: "安装方式",
|
|
7088
|
+
options: [{
|
|
7089
|
+
value: "symlink",
|
|
7090
|
+
label: "符号链接(推荐)",
|
|
7091
|
+
hint: "单一来源,易于更新"
|
|
7092
|
+
}, {
|
|
7093
|
+
value: "copy",
|
|
7094
|
+
label: "复制到所有 Agent",
|
|
7095
|
+
hint: "每个 Agent 独立副本"
|
|
7096
|
+
}]
|
|
7097
|
+
});
|
|
7098
|
+
if (isCancel(modeChoice)) {
|
|
7099
|
+
cancel("安装已取消");
|
|
7100
|
+
process.exit(0);
|
|
7101
|
+
}
|
|
7102
|
+
installMode = modeChoice;
|
|
7103
|
+
} else installMode = "copy";
|
|
7104
|
+
const overwriteChecks = await Promise.all(skills.flatMap((skill) => installTargets.map(async (target) => ({
|
|
7105
|
+
skillName: skill.slug,
|
|
7106
|
+
target,
|
|
7107
|
+
installed: await isSkillInstalled(skill.slug, target.agent, {
|
|
7108
|
+
global: installGlobally,
|
|
7109
|
+
eveSubagent: target.subagent
|
|
7110
|
+
})
|
|
7111
|
+
}))));
|
|
7112
|
+
const overwriteStatus = /* @__PURE__ */ new Map();
|
|
7113
|
+
for (const { skillName, target, installed } of overwriteChecks) {
|
|
7114
|
+
if (!overwriteStatus.has(skillName)) overwriteStatus.set(skillName, /* @__PURE__ */ new Map());
|
|
7115
|
+
overwriteStatus.get(skillName).set(targetKey(target), installed);
|
|
7116
|
+
}
|
|
7117
|
+
const summaryLines = [];
|
|
7118
|
+
for (const skill of skills) {
|
|
7119
|
+
if (summaryLines.length > 0) summaryLines.push("");
|
|
7120
|
+
const skillOverwrites = overwriteStatus.get(skill.slug);
|
|
7121
|
+
const overwriteAgents = installTargets.filter((t) => skillOverwrites?.get(targetKey(t))).map(targetDisplayName);
|
|
7122
|
+
summaryLines.push(`${import_picocolors.default.cyan(skill.displayName || skill.slug)}`);
|
|
7123
|
+
summaryLines.push(...buildTargetSummaryLines(installTargets, installMode));
|
|
7124
|
+
if (overwriteAgents.length > 0) summaryLines.push(` ${import_picocolors.default.yellow("覆盖:")} ${overwriteAgents.join(", ")}`);
|
|
7125
|
+
}
|
|
7126
|
+
console.log();
|
|
7127
|
+
note(summaryLines.join("\n"), "安装摘要");
|
|
7128
|
+
const confirmed = await confirm({ message: "确认安装?" });
|
|
7129
|
+
if (isCancel(confirmed) || !confirmed) {
|
|
7130
|
+
cancel("安装已取消");
|
|
7131
|
+
process.exit(0);
|
|
7132
|
+
}
|
|
7133
|
+
}
|
|
7134
|
+
const { runAdd } = await Promise.resolve().then(() => add_exports);
|
|
7135
|
+
const installedSkills = [];
|
|
7136
|
+
const nonEveAgents = targetAgents.filter((a) => a !== "eve");
|
|
7137
|
+
const runAddBaseOpts = {
|
|
7138
|
+
global: installGlobally,
|
|
7139
|
+
copy: installMode === "copy",
|
|
7140
|
+
yes: true
|
|
7141
|
+
};
|
|
7142
|
+
if (nonEveAgents.length > 0) runAddBaseOpts.agent = nonEveAgents;
|
|
7143
|
+
if (targetAgents.includes("eve")) runAddBaseOpts.subagent = eveSubagentTargets.map((s) => s ?? "root");
|
|
7144
|
+
for (const skill of skills) {
|
|
7145
|
+
const skillRef = `@${skill.namespace || namespace}/${skill.slug}`;
|
|
7146
|
+
console.log(` ${DIM$5}正在安装 ${skillRef}...${RESET$5}`);
|
|
7147
|
+
try {
|
|
7148
|
+
await runAdd([skillRef], runAddBaseOpts);
|
|
7149
|
+
console.log(` ${GREEN$4}✓ ${skill.slug}${RESET$5}`);
|
|
7150
|
+
installedSkills.push(skill.slug);
|
|
7151
|
+
} catch (error) {
|
|
7152
|
+
console.log(` ${RED$3}✗ ${skill.slug}:${error.message}${RESET$5}`);
|
|
7153
|
+
}
|
|
7154
|
+
}
|
|
7155
|
+
const cwd = getWorkdir(globalOpts, args);
|
|
7156
|
+
const lock = loadLockfile(cwd);
|
|
7157
|
+
const pkgKey = `@${namespace}/${slug}`;
|
|
7158
|
+
if (!lock.packages) lock.packages = {};
|
|
7159
|
+
lock.packages[pkgKey] = {
|
|
7160
|
+
namespace,
|
|
7161
|
+
slug,
|
|
7162
|
+
name: pkgInfo.displayName || pkgInfo.name || slug,
|
|
7163
|
+
version: pkgInfo.version || "latest",
|
|
7164
|
+
skillCount: installedSkills.length
|
|
7165
|
+
};
|
|
7166
|
+
for (const skillSlug of installedSkills) {
|
|
7167
|
+
if (!lock.skills[skillSlug]) lock.skills[skillSlug] = {
|
|
7168
|
+
namespace,
|
|
7169
|
+
slug,
|
|
7170
|
+
version: "latest"
|
|
7171
|
+
};
|
|
7172
|
+
lock.skills[skillSlug].package = {
|
|
7173
|
+
namespace,
|
|
7174
|
+
slug
|
|
7175
|
+
};
|
|
7176
|
+
}
|
|
7177
|
+
saveLockfile(cwd, lock);
|
|
7178
|
+
console.log();
|
|
7179
|
+
console.log(`${GREEN$4}✓ 包安装完成${RESET$5}`);
|
|
7180
|
+
}
|
|
7181
|
+
async function handleUninstall(args, globalOpts) {
|
|
7182
|
+
const pkgRef = args[0];
|
|
7183
|
+
if (!pkgRef) {
|
|
7184
|
+
console.error("用法:bfi skills package uninstall <package-ref>");
|
|
7185
|
+
process.exit(1);
|
|
7186
|
+
}
|
|
7187
|
+
const { namespace, slug } = parseSkillRef(pkgRef, globalOpts.namespace);
|
|
7188
|
+
const pkgKey = `@${namespace}/${slug}`;
|
|
7189
|
+
const cwd = getWorkdir(globalOpts, args);
|
|
7190
|
+
const lock = loadLockfile(cwd);
|
|
7191
|
+
if (!lock.packages || !lock.packages[pkgKey]) {
|
|
7192
|
+
console.log(`${YELLOW$1}包未安装:${pkgKey}${RESET$5}`);
|
|
7193
|
+
return;
|
|
7194
|
+
}
|
|
7195
|
+
const skills = lock.skills || {};
|
|
7196
|
+
let removed = 0;
|
|
7197
|
+
for (const [skillName, info] of Object.entries(skills)) {
|
|
7198
|
+
const skillInfo = info;
|
|
7199
|
+
if (skillInfo.package?.slug === slug && skillInfo.package?.namespace === namespace) {
|
|
7200
|
+
const { removeCommand } = await Promise.resolve().then(() => remove_exports);
|
|
7201
|
+
try {
|
|
7202
|
+
await removeCommand([skillName], {
|
|
7203
|
+
global: false,
|
|
7204
|
+
yes: true
|
|
7205
|
+
});
|
|
7206
|
+
removed++;
|
|
7207
|
+
} catch {}
|
|
7208
|
+
}
|
|
7209
|
+
}
|
|
7210
|
+
delete lock.packages[pkgKey];
|
|
7211
|
+
const { saveLockfile } = await Promise.resolve().then(() => config_exports);
|
|
7212
|
+
saveLockfile(cwd, lock);
|
|
7213
|
+
console.log(`${GREEN$4}✓ 已卸载包,移除 ${removed} 个技能${RESET$5}`);
|
|
7214
|
+
}
|
|
7215
|
+
async function handleUpdate(args, api, globalOpts) {
|
|
7216
|
+
const pkgRef = args[0];
|
|
7217
|
+
const lock = loadLockfile(getWorkdir(globalOpts, args));
|
|
7218
|
+
let packagesToUpdate = [];
|
|
7219
|
+
const pkgs = lock.packages || {};
|
|
7220
|
+
if (pkgRef) {
|
|
7221
|
+
const { namespace, slug } = parseSkillRef(pkgRef, globalOpts.namespace);
|
|
7222
|
+
if (!pkgs[`@${namespace}/${slug}`]) {
|
|
7223
|
+
console.log(`${YELLOW$1}包未安装:@${namespace}/${slug}${RESET$5}`);
|
|
7224
|
+
return;
|
|
7225
|
+
}
|
|
7226
|
+
packagesToUpdate.push({
|
|
7227
|
+
namespace,
|
|
7228
|
+
slug
|
|
7229
|
+
});
|
|
7230
|
+
} else packagesToUpdate = Object.values(pkgs).map((p) => ({
|
|
7231
|
+
namespace: p.namespace,
|
|
7232
|
+
slug: p.slug
|
|
7233
|
+
}));
|
|
6871
7234
|
if (packagesToUpdate.length === 0) {
|
|
6872
7235
|
console.log("没有已安装的包需要更新。");
|
|
6873
7236
|
return;
|
|
6874
7237
|
}
|
|
6875
|
-
console.log(`${DIM$
|
|
7238
|
+
console.log(`${DIM$5}正在更新 ${packagesToUpdate.length} 个包...${RESET$5}`);
|
|
6876
7239
|
for (const pkg of packagesToUpdate) try {
|
|
6877
7240
|
const pkgInfo = await api.getPackage(pkg.namespace, pkg.slug);
|
|
6878
7241
|
const skills = (pkgInfo.data || pkgInfo).skills || [];
|
|
6879
|
-
console.log(` ${DIM$
|
|
7242
|
+
console.log(` ${DIM$5}更新 @${pkg.namespace}/${pkg.slug}(${skills.length} 个技能)...${RESET$5}`);
|
|
6880
7243
|
for (const skill of skills) {
|
|
6881
7244
|
const skillRef = skill.namespace ? `@${skill.namespace}/${skill.slug}` : skill.slug;
|
|
6882
7245
|
const { runAdd } = await Promise.resolve().then(() => add_exports);
|
|
@@ -6888,9 +7251,9 @@ async function handleUpdate(args, api, globalOpts) {
|
|
|
6888
7251
|
} catch {}
|
|
6889
7252
|
}
|
|
6890
7253
|
} catch (error) {
|
|
6891
|
-
console.log(` ${RED$
|
|
7254
|
+
console.log(` ${RED$3}✗ @${pkg.namespace}/${pkg.slug}:${error.message}${RESET$5}`);
|
|
6892
7255
|
}
|
|
6893
|
-
console.log(`${GREEN$
|
|
7256
|
+
console.log(`${GREEN$4}✓ 包更新完成${RESET$5}`);
|
|
6894
7257
|
}
|
|
6895
7258
|
async function handleSearch(args, api, globalOpts) {
|
|
6896
7259
|
const query = args.filter((a) => !a.startsWith("--")).join(" ") || void 0;
|
|
@@ -6910,11 +7273,11 @@ async function handleSearch(args, api, globalOpts) {
|
|
|
6910
7273
|
}, null, 2));
|
|
6911
7274
|
return;
|
|
6912
7275
|
}
|
|
6913
|
-
console.log(`\n${BOLD$
|
|
7276
|
+
console.log(`\n${BOLD$3}找到 ${skills.length} 个相关技能(按命名空间分组):${RESET$5}\n`);
|
|
6914
7277
|
for (const [ns, sk] of namespaces) {
|
|
6915
|
-
console.log(` ${BOLD$
|
|
6916
|
-
for (const s of sk.slice(0, 5)) console.log(` ${DIM$
|
|
6917
|
-
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}`);
|
|
6918
7281
|
console.log();
|
|
6919
7282
|
}
|
|
6920
7283
|
} else {
|
|
@@ -6953,11 +7316,11 @@ async function handleSearch(args, api, globalOpts) {
|
|
|
6953
7316
|
};
|
|
6954
7317
|
const maxName = Math.max(...allPackages.map((p) => dw(p.displayName || p.name || p.slug || "")), 4);
|
|
6955
7318
|
const maxSlug = Math.max(...allPackages.map((p) => dw(`@${p._namespace}/${p.slug}`)), 4);
|
|
6956
|
-
console.log(`\n${BOLD$
|
|
7319
|
+
console.log(`\n${BOLD$3}所有技能包(${allPackages.length} 个):${RESET$5}\n`);
|
|
6957
7320
|
for (const pkg of allPackages) {
|
|
6958
7321
|
const name = pad(pkg.displayName || pkg.name || pkg.slug || "", maxName);
|
|
6959
7322
|
const ref = pad(`@${pkg._namespace}/${pkg.slug}`, maxSlug);
|
|
6960
|
-
console.log(` ${BOLD$
|
|
7323
|
+
console.log(` ${BOLD$3}${name}${RESET$5} ${DIM$5}${ref} ${pkg.visibility === "PUBLIC" ? "公开" : "私有"}${RESET$5}`);
|
|
6961
7324
|
}
|
|
6962
7325
|
}
|
|
6963
7326
|
}
|
|
@@ -6978,11 +7341,11 @@ async function handleCreate(args, api, globalOpts) {
|
|
|
6978
7341
|
description: description || "",
|
|
6979
7342
|
visibility: visibility.toUpperCase()
|
|
6980
7343
|
});
|
|
6981
|
-
console.log(`${GREEN$
|
|
7344
|
+
console.log(`${GREEN$4}✓ 已创建包 ${toSkillRefString(namespace, slug)}${RESET$5}`);
|
|
6982
7345
|
const pkg = result.package || result.data || result;
|
|
6983
|
-
if (pkg) console.log(` ${DIM$
|
|
7346
|
+
if (pkg) console.log(` ${DIM$5}名称:${pkg.name || slug} | 可见性:${pkg.visibility || visibility}${RESET$5}`);
|
|
6984
7347
|
} catch (error) {
|
|
6985
|
-
console.error(`${RED$
|
|
7348
|
+
console.error(`${RED$3}创建包失败:${error.message}${RESET$5}`);
|
|
6986
7349
|
process.exit(1);
|
|
6987
7350
|
}
|
|
6988
7351
|
}
|
|
@@ -7008,11 +7371,11 @@ async function handleAddSkills(args, api, globalOpts) {
|
|
|
7008
7371
|
};
|
|
7009
7372
|
});
|
|
7010
7373
|
if (skills.length === 0) {
|
|
7011
|
-
console.log(`${YELLOW$1}命名空间 "${namespace}" 中没有技能。${RESET$
|
|
7374
|
+
console.log(`${YELLOW$1}命名空间 "${namespace}" 中没有技能。${RESET$5}`);
|
|
7012
7375
|
return;
|
|
7013
7376
|
}
|
|
7014
7377
|
} catch (error) {
|
|
7015
|
-
console.error(`${RED$
|
|
7378
|
+
console.error(`${RED$3}获取技能列表失败:${error.message}${RESET$5}`);
|
|
7016
7379
|
process.exit(1);
|
|
7017
7380
|
}
|
|
7018
7381
|
else skills = skillRefs.map((ref) => {
|
|
@@ -7024,85 +7387,487 @@ async function handleAddSkills(args, api, globalOpts) {
|
|
|
7024
7387
|
});
|
|
7025
7388
|
try {
|
|
7026
7389
|
await api.addSkillsToPackage(namespace, slug, skills);
|
|
7027
|
-
console.log(`${GREEN$
|
|
7390
|
+
console.log(`${GREEN$4}✓ 已添加 ${skills.length} 个技能到 ${toSkillRefString(namespace, slug)}${RESET$5}`);
|
|
7028
7391
|
} catch (error) {
|
|
7029
|
-
console.error(`${RED$
|
|
7392
|
+
console.error(`${RED$3}添加失败:${error.message}${RESET$5}`);
|
|
7393
|
+
process.exit(1);
|
|
7394
|
+
}
|
|
7395
|
+
}
|
|
7396
|
+
async function handleRemoveSkills(args, api, globalOpts) {
|
|
7397
|
+
const pkgRef = args[0];
|
|
7398
|
+
const skillRefs = args.slice(1);
|
|
7399
|
+
if (!pkgRef) {
|
|
7400
|
+
console.error("用法:bfi skills package remove-skills <package-ref> [skill-ref...]");
|
|
7401
|
+
console.error(" 不指定技能时,移除包中全部技能");
|
|
7402
|
+
process.exit(1);
|
|
7403
|
+
}
|
|
7404
|
+
const { namespace, slug } = parseSkillRef(pkgRef, globalOpts.namespace);
|
|
7405
|
+
let skills;
|
|
7406
|
+
if (skillRefs.length === 0) try {
|
|
7407
|
+
const pkgInfo = await api.getPackage(namespace, slug);
|
|
7408
|
+
skills = ((pkgInfo.data || pkgInfo).skills || []).map((s) => ({
|
|
7409
|
+
namespace: s.namespace || namespace,
|
|
7410
|
+
slug: s.slug
|
|
7411
|
+
}));
|
|
7412
|
+
if (skills.length === 0) {
|
|
7413
|
+
console.log("包中没有技能。");
|
|
7414
|
+
return;
|
|
7415
|
+
}
|
|
7416
|
+
} catch (error) {
|
|
7417
|
+
console.error(`${RED$3}获取包信息失败:${error.message}${RESET$5}`);
|
|
7418
|
+
process.exit(1);
|
|
7419
|
+
}
|
|
7420
|
+
else skills = skillRefs.map((ref) => {
|
|
7421
|
+
const { namespace: ns, slug: s } = parseSkillRef(ref, namespace);
|
|
7422
|
+
return {
|
|
7423
|
+
namespace: ns,
|
|
7424
|
+
slug: s
|
|
7425
|
+
};
|
|
7426
|
+
});
|
|
7427
|
+
try {
|
|
7428
|
+
await api.removeSkillsFromPackage(namespace, slug, skills);
|
|
7429
|
+
console.log(`${GREEN$4}✓ 已从包移除 ${skills.length} 个技能${RESET$5}`);
|
|
7430
|
+
} catch (error) {
|
|
7431
|
+
console.error(`${RED$3}移除失败:${error.message}${RESET$5}`);
|
|
7432
|
+
process.exit(1);
|
|
7433
|
+
}
|
|
7434
|
+
}
|
|
7435
|
+
async function handleServerInfo(args, api, globalOpts) {
|
|
7436
|
+
const pkgRef = args[0];
|
|
7437
|
+
if (!pkgRef) {
|
|
7438
|
+
console.error("用法:bfi skills package info <package-ref>");
|
|
7439
|
+
process.exit(1);
|
|
7440
|
+
}
|
|
7441
|
+
const { namespace, slug } = parseSkillRef(pkgRef, globalOpts.namespace);
|
|
7442
|
+
const json = args.includes("--json");
|
|
7443
|
+
try {
|
|
7444
|
+
const result = await api.getPackage(namespace, slug);
|
|
7445
|
+
const pkg = result.data || result;
|
|
7446
|
+
if (json) {
|
|
7447
|
+
console.log(JSON.stringify(pkg, null, 2));
|
|
7448
|
+
return;
|
|
7449
|
+
}
|
|
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"}`);
|
|
7455
|
+
const skills = pkg.skills || [];
|
|
7456
|
+
if (skills.length > 0) {
|
|
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}`);
|
|
7459
|
+
}
|
|
7460
|
+
console.log();
|
|
7461
|
+
} catch (error) {
|
|
7462
|
+
console.error(`${RED$3}获取包信息失败:${error.message}${RESET$5}`);
|
|
7463
|
+
process.exit(1);
|
|
7464
|
+
}
|
|
7465
|
+
}
|
|
7466
|
+
function getOptionValue(args, flag) {
|
|
7467
|
+
const idx = args.indexOf(flag);
|
|
7468
|
+
return idx !== -1 && args[idx + 1] ? args[idx + 1] : void 0;
|
|
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 (SkillHub 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 <pluginId|./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
|
+
const plugin = await api.getPlugin(Number(ref));
|
|
7710
|
+
const slug = plugin.slug ?? `plugin-${plugin.id}`;
|
|
7711
|
+
if (!plugin.latestVersionId) {
|
|
7712
|
+
console.error(`插件 ${slug} 尚无已发布版本,无法安装`);
|
|
7713
|
+
process.exit(1);
|
|
7714
|
+
}
|
|
7715
|
+
const tmpZip = path$1.join(os$1.tmpdir(), `${slug}-install.zip`);
|
|
7716
|
+
console.log(`下载 ${TEXT$4}${slug}${RESET$4} ← ${DIM$4}${registry}${RESET$4}`);
|
|
7717
|
+
await api.downloadPluginBundle(plugin.id, plugin.latestVersionId, tmpZip);
|
|
7718
|
+
extractZip(tmpZip, homeSkills);
|
|
7719
|
+
fs.unlinkSync(tmpZip);
|
|
7720
|
+
console.log(`${GREEN$3}✓${RESET$4} Installed ${TEXT$4}${slug}${RESET$4}`);
|
|
7721
|
+
console.log(` ${DIM$4}${homeSkills}${RESET$4}`);
|
|
7722
|
+
console.log(` ${DIM$4}重启 Claude Code 会话后生效${RESET$4}`);
|
|
7723
|
+
}
|
|
7724
|
+
function getOption$1(args, flag) {
|
|
7725
|
+
const idx = args.indexOf(flag);
|
|
7726
|
+
return idx !== -1 && idx + 1 < args.length ? args[idx + 1] : void 0;
|
|
7727
|
+
}
|
|
7728
|
+
const RESET$3 = "\x1B[0m";
|
|
7729
|
+
const BOLD$1 = "\x1B[1m";
|
|
7730
|
+
const DIM$3 = "\x1B[38;5;102m";
|
|
7731
|
+
const TEXT$3 = "\x1B[38;5;145m";
|
|
7732
|
+
const GREEN$2 = "\x1B[32m";
|
|
7733
|
+
const RED$1 = "\x1B[31m";
|
|
7734
|
+
async function runMcp(args, globalOpts) {
|
|
7735
|
+
const sub = args[0];
|
|
7736
|
+
const subArgs = args.slice(1);
|
|
7737
|
+
switch (sub) {
|
|
7738
|
+
case "list":
|
|
7739
|
+
case "ls":
|
|
7740
|
+
await list(subArgs, globalOpts);
|
|
7741
|
+
break;
|
|
7742
|
+
case "info":
|
|
7743
|
+
await info(subArgs, globalOpts);
|
|
7744
|
+
break;
|
|
7745
|
+
case "config":
|
|
7746
|
+
await config(subArgs, globalOpts);
|
|
7747
|
+
break;
|
|
7748
|
+
case "add":
|
|
7749
|
+
await add(subArgs, globalOpts);
|
|
7750
|
+
break;
|
|
7751
|
+
case "remove":
|
|
7752
|
+
case "rm":
|
|
7753
|
+
await remove(subArgs);
|
|
7754
|
+
break;
|
|
7755
|
+
case "--help":
|
|
7756
|
+
case "-h":
|
|
7757
|
+
case void 0:
|
|
7758
|
+
showHelp$1();
|
|
7759
|
+
break;
|
|
7760
|
+
default:
|
|
7761
|
+
console.log(`未知 mcp 命令:${sub}`);
|
|
7762
|
+
showHelp$1();
|
|
7763
|
+
}
|
|
7764
|
+
}
|
|
7765
|
+
function showHelp$1() {
|
|
7766
|
+
console.log();
|
|
7767
|
+
console.log(`${BOLD$1}bfi mcp <command>${RESET$3} — MCP 服务器管理`);
|
|
7768
|
+
console.log();
|
|
7769
|
+
console.log(`${BOLD$1}Commands:${RESET$3}`);
|
|
7770
|
+
console.log(` list, ls List MCP servers on SkillHub`);
|
|
7771
|
+
console.log(` info <id> Show MCP server details`);
|
|
7772
|
+
console.log(` config <id> Print .mcp.json config fragment`);
|
|
7773
|
+
console.log(` add <id> Write server into local .mcp.json (user scope)`);
|
|
7774
|
+
console.log(` remove <name> Remove a server from local .mcp.json`);
|
|
7775
|
+
console.log();
|
|
7776
|
+
}
|
|
7777
|
+
async function list(_args, globalOpts) {
|
|
7778
|
+
const { registry, token } = resolveConfig(globalOpts);
|
|
7779
|
+
const result = await new SkillHubAPI(registry, token).listMcpServers();
|
|
7780
|
+
const servers = Array.isArray(result) ? result : result?.content ?? result?.data ?? [];
|
|
7781
|
+
if (servers.length === 0) {
|
|
7782
|
+
console.log("暂无 MCP 服务器。");
|
|
7783
|
+
return;
|
|
7784
|
+
}
|
|
7785
|
+
console.log();
|
|
7786
|
+
for (const s of servers) {
|
|
7787
|
+
console.log(` ${TEXT$3}${s.slug}${RESET$3} ${DIM$3}[${s.transportType}]${RESET$3}`);
|
|
7788
|
+
console.log(` ${s.summary ?? ""}`);
|
|
7789
|
+
console.log(` ${DIM$3}下载 ${s.downloadCount ?? 0} · star ${s.starCount ?? 0}${RESET$3}`);
|
|
7790
|
+
}
|
|
7791
|
+
console.log();
|
|
7792
|
+
}
|
|
7793
|
+
async function info(args, globalOpts) {
|
|
7794
|
+
const idArg = args[0];
|
|
7795
|
+
if (!idArg) {
|
|
7796
|
+
console.error("用法:bfi mcp info <mcpId>");
|
|
7030
7797
|
process.exit(1);
|
|
7031
7798
|
}
|
|
7799
|
+
const { registry, token } = resolveConfig(globalOpts);
|
|
7800
|
+
const server = await new SkillHubAPI(registry, token).getMcpServer(Number(idArg));
|
|
7801
|
+
console.log();
|
|
7802
|
+
console.log(`${BOLD$1}${server.displayName ?? server.slug}${RESET$3} (id=${server.id})`);
|
|
7803
|
+
console.log(` transport: ${server.transportType}`);
|
|
7804
|
+
if (server.serverUrl) console.log(` url: ${server.serverUrl}`);
|
|
7805
|
+
if (server.command) console.log(` command: ${server.command}`);
|
|
7806
|
+
console.log(` ${DIM$3}visibility=${server.visibility} status=${server.status}${RESET$3}`);
|
|
7807
|
+
console.log();
|
|
7032
7808
|
}
|
|
7033
|
-
async function
|
|
7034
|
-
const
|
|
7035
|
-
|
|
7036
|
-
|
|
7037
|
-
console.error("用法:bfi skills package remove-skills <package-ref> [skill-ref...]");
|
|
7038
|
-
console.error(" 不指定技能时,移除包中全部技能");
|
|
7809
|
+
async function config(args, globalOpts) {
|
|
7810
|
+
const idArg = args[0];
|
|
7811
|
+
if (!idArg) {
|
|
7812
|
+
console.error("用法:bfi mcp config <mcpId>");
|
|
7039
7813
|
process.exit(1);
|
|
7040
7814
|
}
|
|
7041
|
-
const {
|
|
7042
|
-
|
|
7043
|
-
|
|
7044
|
-
|
|
7045
|
-
|
|
7046
|
-
|
|
7047
|
-
|
|
7048
|
-
|
|
7049
|
-
if (skills.length === 0) {
|
|
7050
|
-
console.log("包中没有技能。");
|
|
7051
|
-
return;
|
|
7052
|
-
}
|
|
7053
|
-
} catch (error) {
|
|
7054
|
-
console.error(`${RED$1}获取包信息失败:${error.message}${RESET$3}`);
|
|
7815
|
+
const { registry, token } = resolveConfig(globalOpts);
|
|
7816
|
+
const configJson = await new SkillHubAPI(registry, token).getMcpConfig(Number(idArg));
|
|
7817
|
+
console.log(configJson);
|
|
7818
|
+
}
|
|
7819
|
+
async function add(args, globalOpts) {
|
|
7820
|
+
const idArg = args[0];
|
|
7821
|
+
if (!idArg) {
|
|
7822
|
+
console.error("用法:bfi mcp add <mcpId>");
|
|
7055
7823
|
process.exit(1);
|
|
7056
7824
|
}
|
|
7057
|
-
|
|
7058
|
-
|
|
7059
|
-
|
|
7060
|
-
|
|
7061
|
-
|
|
7062
|
-
|
|
7063
|
-
|
|
7064
|
-
|
|
7065
|
-
|
|
7066
|
-
|
|
7067
|
-
|
|
7068
|
-
|
|
7825
|
+
const { registry, token } = resolveConfig(globalOpts);
|
|
7826
|
+
const api = new SkillHubAPI(registry, token);
|
|
7827
|
+
const server = await api.getMcpServer(Number(idArg));
|
|
7828
|
+
const configJson = JSON.parse(await api.getMcpConfig(Number(idArg)));
|
|
7829
|
+
const mcpPath = resolveMcpPath(getOption(args, "--scope") ?? "user");
|
|
7830
|
+
let existing = { mcpServers: {} };
|
|
7831
|
+
if (fs.existsSync(mcpPath)) {
|
|
7832
|
+
existing = JSON.parse(fs.readFileSync(mcpPath, "utf-8"));
|
|
7833
|
+
if (!existing.mcpServers) existing.mcpServers = {};
|
|
7834
|
+
}
|
|
7835
|
+
const serverName = server.slug ?? `mcp-${server.id}`;
|
|
7836
|
+
existing.mcpServers[serverName] = configJson.mcpServers[Object.keys(configJson.mcpServers)[0]];
|
|
7837
|
+
fs.mkdirSync(path$1.dirname(mcpPath), { recursive: true });
|
|
7838
|
+
fs.writeFileSync(mcpPath, JSON.stringify(existing, null, 2) + "\n");
|
|
7839
|
+
console.log(`${GREEN$2}✓${RESET$3} Added ${TEXT$3}${serverName}${RESET$3} → ${DIM$3}${mcpPath}${RESET$3}`);
|
|
7840
|
+
}
|
|
7841
|
+
async function remove(args) {
|
|
7842
|
+
const name = args[0];
|
|
7843
|
+
if (!name) {
|
|
7844
|
+
console.error("用法:bfi mcp remove <name> [--scope user|project|local]");
|
|
7069
7845
|
process.exit(1);
|
|
7070
7846
|
}
|
|
7071
|
-
|
|
7072
|
-
|
|
7073
|
-
|
|
7074
|
-
if (!pkgRef) {
|
|
7075
|
-
console.error("用法:bfi skills package info <package-ref>");
|
|
7847
|
+
const mcpPath = resolveMcpPath(getOption(args, "--scope") ?? "user");
|
|
7848
|
+
if (!fs.existsSync(mcpPath)) {
|
|
7849
|
+
console.error(`配置文件不存在:${mcpPath}`);
|
|
7076
7850
|
process.exit(1);
|
|
7077
7851
|
}
|
|
7078
|
-
const
|
|
7079
|
-
|
|
7080
|
-
|
|
7081
|
-
const result = await api.getPackage(namespace, slug);
|
|
7082
|
-
const pkg = result.data || result;
|
|
7083
|
-
if (json) {
|
|
7084
|
-
console.log(JSON.stringify(pkg, null, 2));
|
|
7085
|
-
return;
|
|
7086
|
-
}
|
|
7087
|
-
console.log(`\n${BOLD$1}${pkg.displayName || pkg.name || slug}${RESET$3}`);
|
|
7088
|
-
console.log(`${DIM$3}${"=".repeat(48)}${RESET$3}`);
|
|
7089
|
-
console.log(` ${BOLD$1}Slug:${RESET$3} ${toCanonicalSlug(namespace, slug)}`);
|
|
7090
|
-
console.log(` ${BOLD$1}命名空间:${RESET$3} @${namespace}`);
|
|
7091
|
-
console.log(` ${BOLD$1}可见性:${RESET$3} ${pkg.visibility || "PRIVATE"}`);
|
|
7092
|
-
const skills = pkg.skills || [];
|
|
7093
|
-
if (skills.length > 0) {
|
|
7094
|
-
console.log(`\n ${BOLD$1}技能(${skills.length} 个):${RESET$3}`);
|
|
7095
|
-
for (const s of skills) console.log(` ${DIM$3}- ${s.displayName || s.slug} (${s.namespace || namespace}--${s.slug})${RESET$3}`);
|
|
7096
|
-
}
|
|
7097
|
-
console.log();
|
|
7098
|
-
} catch (error) {
|
|
7099
|
-
console.error(`${RED$1}获取包信息失败:${error.message}${RESET$3}`);
|
|
7852
|
+
const existing = JSON.parse(fs.readFileSync(mcpPath, "utf-8"));
|
|
7853
|
+
if (!existing.mcpServers?.[name]) {
|
|
7854
|
+
console.error(`${RED$1}未找到服务器:${name}${RESET$3}`);
|
|
7100
7855
|
process.exit(1);
|
|
7101
7856
|
}
|
|
7857
|
+
delete existing.mcpServers[name];
|
|
7858
|
+
fs.writeFileSync(mcpPath, JSON.stringify(existing, null, 2) + "\n");
|
|
7859
|
+
console.log(`${GREEN$2}✓${RESET$3} Removed ${TEXT$3}${name}${RESET$3}`);
|
|
7102
7860
|
}
|
|
7103
|
-
function
|
|
7861
|
+
function resolveMcpPath(scope) {
|
|
7862
|
+
switch (scope) {
|
|
7863
|
+
case "project": return path$1.join(process.cwd(), ".mcp.json");
|
|
7864
|
+
case "local": return path$1.join(process.cwd(), ".claude", ".mcp.json.local");
|
|
7865
|
+
default: return path$1.join(os$1.homedir(), ".claude", ".mcp.json");
|
|
7866
|
+
}
|
|
7867
|
+
}
|
|
7868
|
+
function getOption(args, flag) {
|
|
7104
7869
|
const idx = args.indexOf(flag);
|
|
7105
|
-
return idx !== -1 &&
|
|
7870
|
+
return idx !== -1 && idx + 1 < args.length ? args[idx + 1] : void 0;
|
|
7106
7871
|
}
|
|
7107
7872
|
const RESET$2 = "\x1B[0m";
|
|
7108
7873
|
const DIM$2 = "\x1B[38;5;102m";
|
|
@@ -7217,6 +7982,735 @@ async function runInstall(args, globalOpts) {
|
|
|
7217
7982
|
if (failedCount > 0) console.log(`${YELLOW}⚠ ${failedCount} 项恢复失败${RESET$1}`);
|
|
7218
7983
|
console.log();
|
|
7219
7984
|
}
|
|
7985
|
+
const YMC_DEFAULT_BASE = process.env.BFI_YMC_BASE || "http://172.20.35.178:8901/bficli/api/v2";
|
|
7986
|
+
var YmcApi = class {
|
|
7987
|
+
baseUrl;
|
|
7988
|
+
token;
|
|
7989
|
+
constructor(token = null, baseUrl) {
|
|
7990
|
+
this.baseUrl = (baseUrl || YMC_DEFAULT_BASE).replace(/\/$/, "");
|
|
7991
|
+
if (token) {
|
|
7992
|
+
this.token = token;
|
|
7993
|
+
return;
|
|
7994
|
+
}
|
|
7995
|
+
this.token = loadGlobalConfig()?.token ?? null;
|
|
7996
|
+
}
|
|
7997
|
+
async call(path, body = {}, timeoutMs = 9e4) {
|
|
7998
|
+
const url = `${this.baseUrl}/${path.replace(/^\//, "")}`;
|
|
7999
|
+
const payload = JSON.stringify(body);
|
|
8000
|
+
const mod = url.startsWith("https://") ? https : http;
|
|
8001
|
+
const raw = await new Promise((resolve, reject) => {
|
|
8002
|
+
const req = mod.request(url, {
|
|
8003
|
+
method: "POST",
|
|
8004
|
+
headers: {
|
|
8005
|
+
"Content-Type": "application/json",
|
|
8006
|
+
Accept: "application/json",
|
|
8007
|
+
...this.token ? { Authorization: `Bearer ${this.token}` } : {},
|
|
8008
|
+
"Content-Length": Buffer.byteLength(payload)
|
|
8009
|
+
},
|
|
8010
|
+
timeout: timeoutMs
|
|
8011
|
+
}, (res) => {
|
|
8012
|
+
const chunks = [];
|
|
8013
|
+
res.on("data", (c) => chunks.push(c));
|
|
8014
|
+
res.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
|
|
8015
|
+
});
|
|
8016
|
+
req.on("timeout", () => {
|
|
8017
|
+
req.destroy(/* @__PURE__ */ new Error(`请求超时(${timeoutMs}ms)`));
|
|
8018
|
+
});
|
|
8019
|
+
req.on("error", reject);
|
|
8020
|
+
req.write(payload);
|
|
8021
|
+
req.end();
|
|
8022
|
+
});
|
|
8023
|
+
let parsed;
|
|
8024
|
+
try {
|
|
8025
|
+
parsed = JSON.parse(raw);
|
|
8026
|
+
} catch {
|
|
8027
|
+
throw new Error(`非 JSON 响应(HTTP):${raw.slice(0, 200)}`);
|
|
8028
|
+
}
|
|
8029
|
+
if (parsed.detail && parsed.code === void 0) throw new Error(String(parsed.detail));
|
|
8030
|
+
return {
|
|
8031
|
+
code: typeof parsed.code === "number" ? parsed.code : -1,
|
|
8032
|
+
msg: String(parsed.msg ?? ""),
|
|
8033
|
+
data: parsed.data ?? null,
|
|
8034
|
+
raw: parsed
|
|
8035
|
+
};
|
|
8036
|
+
}
|
|
8037
|
+
async download(path, body, outFile, timeoutMs = 18e4) {
|
|
8038
|
+
const url = `${this.baseUrl}/${path.replace(/^\//, "")}`;
|
|
8039
|
+
const payload = JSON.stringify(body);
|
|
8040
|
+
const mod = url.startsWith("https://") ? https : http;
|
|
8041
|
+
const raw = await new Promise((resolve, reject) => {
|
|
8042
|
+
const req = mod.request(url, {
|
|
8043
|
+
method: "POST",
|
|
8044
|
+
headers: {
|
|
8045
|
+
"Content-Type": "application/json",
|
|
8046
|
+
Accept: "*/*",
|
|
8047
|
+
Authorization: `Bearer ${this.token}`,
|
|
8048
|
+
"Content-Length": Buffer.byteLength(payload)
|
|
8049
|
+
},
|
|
8050
|
+
timeout: timeoutMs
|
|
8051
|
+
}, (res) => {
|
|
8052
|
+
const chunks = [];
|
|
8053
|
+
res.on("data", (c) => chunks.push(c));
|
|
8054
|
+
res.on("end", () => resolve(Buffer.concat(chunks)));
|
|
8055
|
+
});
|
|
8056
|
+
req.on("timeout", () => {
|
|
8057
|
+
req.destroy(/* @__PURE__ */ new Error(`下载超时(${timeoutMs}ms)`));
|
|
8058
|
+
});
|
|
8059
|
+
req.on("error", reject);
|
|
8060
|
+
req.write(payload);
|
|
8061
|
+
req.end();
|
|
8062
|
+
});
|
|
8063
|
+
if (raw.length > 0 && raw[0] === 123) {
|
|
8064
|
+
const msg = JSON.parse(raw.toString("utf-8"));
|
|
8065
|
+
throw new Error(String(msg.msg ?? "下载失败"));
|
|
8066
|
+
}
|
|
8067
|
+
(await import("fs")).writeFileSync(outFile, raw);
|
|
8068
|
+
return raw.length;
|
|
8069
|
+
}
|
|
8070
|
+
async get(path, timeoutMs = 3e4) {
|
|
8071
|
+
const url = `${this.baseUrl}/${path.replace(/^\//, "")}`;
|
|
8072
|
+
const mod = url.startsWith("https://") ? https : http;
|
|
8073
|
+
const raw = await new Promise((resolve, reject) => {
|
|
8074
|
+
const req = mod.request(url, {
|
|
8075
|
+
method: "GET",
|
|
8076
|
+
headers: {
|
|
8077
|
+
Accept: "application/json",
|
|
8078
|
+
...this.token ? { Authorization: `Bearer ${this.token}` } : {}
|
|
8079
|
+
},
|
|
8080
|
+
timeout: timeoutMs
|
|
8081
|
+
}, (res) => {
|
|
8082
|
+
const chunks = [];
|
|
8083
|
+
res.on("data", (c) => chunks.push(c));
|
|
8084
|
+
res.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
|
|
8085
|
+
});
|
|
8086
|
+
req.on("timeout", () => {
|
|
8087
|
+
req.destroy(/* @__PURE__ */ new Error(`请求超时(${timeoutMs}ms)`));
|
|
8088
|
+
});
|
|
8089
|
+
req.on("error", reject);
|
|
8090
|
+
req.end();
|
|
8091
|
+
});
|
|
8092
|
+
const parsed = JSON.parse(raw);
|
|
8093
|
+
if (parsed.detail && parsed.code === void 0) throw new Error(String(parsed.detail));
|
|
8094
|
+
return {
|
|
8095
|
+
code: typeof parsed.code === "number" ? parsed.code : -1,
|
|
8096
|
+
msg: String(parsed.msg ?? ""),
|
|
8097
|
+
data: parsed.data ?? null,
|
|
8098
|
+
raw: parsed
|
|
8099
|
+
};
|
|
8100
|
+
}
|
|
8101
|
+
};
|
|
8102
|
+
function opt(args, ...names) {
|
|
8103
|
+
for (const name of names) {
|
|
8104
|
+
const idx = args.indexOf(name);
|
|
8105
|
+
if (idx !== -1 && idx + 1 < args.length) return args[idx + 1];
|
|
8106
|
+
const eq = args.find((a) => a.startsWith(`${name}=`));
|
|
8107
|
+
if (eq) return eq.slice(name.length + 1);
|
|
8108
|
+
}
|
|
8109
|
+
}
|
|
8110
|
+
function flag(args, ...names) {
|
|
8111
|
+
return names.some((n) => args.includes(n));
|
|
8112
|
+
}
|
|
8113
|
+
function positionals(args) {
|
|
8114
|
+
const out = [];
|
|
8115
|
+
for (let i = 0; i < args.length; i++) {
|
|
8116
|
+
if (args[i].startsWith("-")) {
|
|
8117
|
+
if (!args[i].includes("=") && i + 1 < args.length && !args[i + 1].startsWith("-")) i++;
|
|
8118
|
+
continue;
|
|
8119
|
+
}
|
|
8120
|
+
out.push(args[i]);
|
|
8121
|
+
}
|
|
8122
|
+
return out;
|
|
8123
|
+
}
|
|
8124
|
+
function emit(result, jsonMode, summarize) {
|
|
8125
|
+
if (jsonMode) {
|
|
8126
|
+
console.log(JSON.stringify(result.raw, null, 2));
|
|
8127
|
+
return;
|
|
8128
|
+
}
|
|
8129
|
+
if (result.code !== 0) {
|
|
8130
|
+
console.error(`✗ ${result.msg || "请求失败"}`);
|
|
8131
|
+
process.exit(1);
|
|
8132
|
+
}
|
|
8133
|
+
console.log(summarize ? summarize(result.data) : JSON.stringify(result.data, null, 2));
|
|
8134
|
+
}
|
|
8135
|
+
function raw0(text) {
|
|
8136
|
+
return text || "{}";
|
|
8137
|
+
}
|
|
8138
|
+
const ENV_DOC = "环境: test/daily/pre/core1-4/commldev";
|
|
8139
|
+
function requireEnv(args) {
|
|
8140
|
+
const env = opt(args, "-e", "--env", "--env-name");
|
|
8141
|
+
if (!env) {
|
|
8142
|
+
console.error(`缺少环境参数:-e <${ENV_DOC}>`);
|
|
8143
|
+
process.exit(1);
|
|
8144
|
+
}
|
|
8145
|
+
return { env_name: env };
|
|
8146
|
+
}
|
|
8147
|
+
function timeOf(args, required = true) {
|
|
8148
|
+
const t = opt(args, "-t", "--time");
|
|
8149
|
+
if (!t && required) {
|
|
8150
|
+
console.error("缺少时间参数:-t <简写>(如 1h/3d/10m/4w,表示过去 N 单位到现在)");
|
|
8151
|
+
process.exit(1);
|
|
8152
|
+
}
|
|
8153
|
+
return t ? { time: t } : {};
|
|
8154
|
+
}
|
|
8155
|
+
function timeOpts(args) {
|
|
8156
|
+
const out = {};
|
|
8157
|
+
const t = opt(args, "-t", "--time");
|
|
8158
|
+
if (t) out.time = t;
|
|
8159
|
+
const st = opt(args, "--start-time");
|
|
8160
|
+
if (st) out.start_time = st;
|
|
8161
|
+
const et = opt(args, "--end-time");
|
|
8162
|
+
if (et) out.end_time = et;
|
|
8163
|
+
return out;
|
|
8164
|
+
}
|
|
8165
|
+
function requireToken(api) {
|
|
8166
|
+
if (!api.token) {
|
|
8167
|
+
console.error("未认证。先运行:bfi auth login --token <sk-token>(或传 --token)");
|
|
8168
|
+
process.exit(1);
|
|
8169
|
+
}
|
|
8170
|
+
}
|
|
8171
|
+
function rowsSummary(label) {
|
|
8172
|
+
return (data) => {
|
|
8173
|
+
const d = data;
|
|
8174
|
+
const rows = d.rows ?? d;
|
|
8175
|
+
return `✓ ${label}: ${Array.isArray(rows) ? rows.length : 0} 条`;
|
|
8176
|
+
};
|
|
8177
|
+
}
|
|
8178
|
+
async function runYmc(args, globalOpts) {
|
|
8179
|
+
const sub = args[0];
|
|
8180
|
+
const rest = args.slice(1);
|
|
8181
|
+
const jsonMode = flag(rest, "--json");
|
|
8182
|
+
const token = globalOpts.token;
|
|
8183
|
+
const api = new YmcApi(token ?? null);
|
|
8184
|
+
requireToken(api);
|
|
8185
|
+
if (!sub || sub === "--help" || sub === "-h") {
|
|
8186
|
+
printHelp();
|
|
8187
|
+
return;
|
|
8188
|
+
}
|
|
8189
|
+
switch (sub) {
|
|
8190
|
+
case "status":
|
|
8191
|
+
emit(await api.get("ymc/auth/status"), jsonMode, (data) => {
|
|
8192
|
+
const s = data;
|
|
8193
|
+
const line = (g) => {
|
|
8194
|
+
const x = s[g];
|
|
8195
|
+
if (!x?.logged_in) return `${g}: 未登录`;
|
|
8196
|
+
return `${g}: 已登录(至 ${(/* @__PURE__ */ new Date(Number(x.expires_at) * 1e3)).toLocaleString("zh-CN")})`;
|
|
8197
|
+
};
|
|
8198
|
+
return `${line("dev")}\n${line("prod")}`;
|
|
8199
|
+
});
|
|
8200
|
+
break;
|
|
8201
|
+
case "trace": {
|
|
8202
|
+
const id = positionals(rest)[0];
|
|
8203
|
+
if (!id) {
|
|
8204
|
+
console.error("用法:bfi ymc trace <trace_id> -e <env>");
|
|
8205
|
+
process.exit(1);
|
|
8206
|
+
}
|
|
8207
|
+
emit(await api.call("trace", {
|
|
8208
|
+
trace_id: id,
|
|
8209
|
+
...requireEnv(rest)
|
|
8210
|
+
}), jsonMode, (data) => {
|
|
8211
|
+
const d = data;
|
|
8212
|
+
const lines = [];
|
|
8213
|
+
const wc = d.warning_chain;
|
|
8214
|
+
const s = wc?.Summary ?? {};
|
|
8215
|
+
lines.push(`✓ 三源查询完成 ts=${d.ts}(${d.ts_source})`);
|
|
8216
|
+
lines.push(`概览: span=${s.TraceCount ?? "?"} 异常=${s.AppExceptionCount ?? 0} 最大耗时=${s.MaxCostTime ?? "?"}ms 慢调用告警=${s.WarningCallCount ?? 0} 慢SQL告警=${s.WarningSqlCount ?? 0}`);
|
|
8217
|
+
const mw = wc?.MWSummary ?? [];
|
|
8218
|
+
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(" | "));
|
|
8219
|
+
const ae = wc?.AppException ?? [];
|
|
8220
|
+
if (Array.isArray(ae) && ae.length) {
|
|
8221
|
+
const first = ae[0];
|
|
8222
|
+
const le = first.LogEntry ?? {};
|
|
8223
|
+
const msg = String(le.Message ?? "").replace(/\s+/g, " ").slice(0, 180);
|
|
8224
|
+
lines.push(`异常[0]: ${first.AppCode} ${le.Logger ?? ""}\n ${msg}`);
|
|
8225
|
+
}
|
|
8226
|
+
const ing = d.ingress_logs ?? [];
|
|
8227
|
+
if (Array.isArray(ing) && ing.length) {
|
|
8228
|
+
const st = ing.reduce((m, x) => {
|
|
8229
|
+
const k = String(x.status);
|
|
8230
|
+
m[k] = (m[k] ?? 0) + 1;
|
|
8231
|
+
return m;
|
|
8232
|
+
}, {});
|
|
8233
|
+
lines.push(`ingress: ${ing.length} 条 ${JSON.stringify(st)} 入口=${(ing[0].url ?? "?").toString().slice(0, 60)}`);
|
|
8234
|
+
}
|
|
8235
|
+
return lines.join("\n");
|
|
8236
|
+
});
|
|
8237
|
+
break;
|
|
8238
|
+
}
|
|
8239
|
+
case "chain": {
|
|
8240
|
+
const id = positionals(rest)[0];
|
|
8241
|
+
if (!id) {
|
|
8242
|
+
console.error("用法:bfi ymc chain <trace_id> -e <env>");
|
|
8243
|
+
process.exit(1);
|
|
8244
|
+
}
|
|
8245
|
+
emit(await api.call("chain", {
|
|
8246
|
+
trace_id: id,
|
|
8247
|
+
...requireEnv(rest)
|
|
8248
|
+
}), jsonMode, (data) => {
|
|
8249
|
+
const d = data;
|
|
8250
|
+
return `✓ 链路查询: ts=${d.ts}(${d.ts_source})chain=${d.warning_chain ? "有" : "无"}`;
|
|
8251
|
+
});
|
|
8252
|
+
break;
|
|
8253
|
+
}
|
|
8254
|
+
case "sls-raw": {
|
|
8255
|
+
const query = positionals(rest)[0];
|
|
8256
|
+
if (!query) {
|
|
8257
|
+
console.error("用法:bfi ymc sls-raw \"<查询语句>\" -e <env> [-t time | --start-time <s> --end-time <e> | --days N] [--line N]");
|
|
8258
|
+
console.error("例:bfi ymc sls-raw '* | SELECT applicationName, count(*) AS c GROUP BY applicationName' -e daily -t 3d");
|
|
8259
|
+
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\"");
|
|
8260
|
+
console.error("说明:--start-time/--end-time(yyyy-MM-dd HH:mm:ss 北京时间)为精确窗口,优先于 -t/--days");
|
|
8261
|
+
process.exit(1);
|
|
8262
|
+
}
|
|
8263
|
+
const body = {
|
|
8264
|
+
query,
|
|
8265
|
+
...requireEnv(rest),
|
|
8266
|
+
...timeOpts(rest),
|
|
8267
|
+
...opt(rest, "--days") ? { days: Number(opt(rest, "--days")) } : {},
|
|
8268
|
+
...opt(rest, "--line") ? { line: Number(opt(rest, "--line")) } : {}
|
|
8269
|
+
};
|
|
8270
|
+
emit(await api.call("sls/raw", body), jsonMode, rowsSummary("SLS 原始查询"));
|
|
8271
|
+
break;
|
|
8272
|
+
}
|
|
8273
|
+
case "sls-grep": {
|
|
8274
|
+
const kw = positionals(rest)[0];
|
|
8275
|
+
if (!kw) {
|
|
8276
|
+
console.error("用法:bfi ymc sls-grep \"<关键词>\" -e <env> [-t time] [--group-by applicationName|message|requestURL]");
|
|
8277
|
+
console.error("例:bfi ymc sls-grep \"ClassCastException\" -e daily -t 3d");
|
|
8278
|
+
console.error("⚠️ 输出为聚合统计行(每组计数,计数上限 100,可能被封顶)——非明细。");
|
|
8279
|
+
console.error(" 需要真实条数/明细/精确时间窗时改用 sls-raw + GROUP BY + --start-time/--end-time");
|
|
8280
|
+
process.exit(1);
|
|
8281
|
+
}
|
|
8282
|
+
const groupBy = opt(rest, "--group-by") || "applicationName";
|
|
8283
|
+
const query = `"${kw.replace(/"/g, "")}" | SELECT ${groupBy}, count(*) AS count GROUP BY ${groupBy} ORDER BY count DESC`;
|
|
8284
|
+
emit(await api.call("sls/raw", {
|
|
8285
|
+
query,
|
|
8286
|
+
...requireEnv(rest),
|
|
8287
|
+
...timeOf(rest, false),
|
|
8288
|
+
line: Number(opt(rest, "--line") || 50)
|
|
8289
|
+
}), jsonMode, rowsSummary(`SLS 全文检索 "${kw}"`));
|
|
8290
|
+
break;
|
|
8291
|
+
}
|
|
8292
|
+
case "sls-logs": {
|
|
8293
|
+
const id = positionals(rest)[0];
|
|
8294
|
+
if (!id) {
|
|
8295
|
+
console.error("用法:bfi ymc sls-logs <trace_id> -e <env>");
|
|
8296
|
+
process.exit(1);
|
|
8297
|
+
}
|
|
8298
|
+
emit(await api.call("sls/logs", {
|
|
8299
|
+
trace_id: id,
|
|
8300
|
+
...requireEnv(rest)
|
|
8301
|
+
}), jsonMode, rowsSummary("SLS 日志"));
|
|
8302
|
+
break;
|
|
8303
|
+
}
|
|
8304
|
+
case "slowsql": {
|
|
8305
|
+
const body = {
|
|
8306
|
+
...requireEnv(rest),
|
|
8307
|
+
...timeOpts(rest),
|
|
8308
|
+
page: Number(opt(rest, "--page") || 1),
|
|
8309
|
+
pageSize: Number(opt(rest, "--page-size") || 20)
|
|
8310
|
+
};
|
|
8311
|
+
for (const [k, name] of [
|
|
8312
|
+
["sqlStr", "--sql-str"],
|
|
8313
|
+
["md5Sql", "--md5"],
|
|
8314
|
+
["traceId", "--trace-id"],
|
|
8315
|
+
["curAppCode", "--app"],
|
|
8316
|
+
["enableProfiler", "--profiler"]
|
|
8317
|
+
]) {
|
|
8318
|
+
const v = opt(rest, name);
|
|
8319
|
+
if (v) body[k] = v;
|
|
8320
|
+
}
|
|
8321
|
+
emit(await api.call("ymc/warningSql", body), jsonMode, rowsSummary("慢 SQL"));
|
|
8322
|
+
break;
|
|
8323
|
+
}
|
|
8324
|
+
case "slowcall": {
|
|
8325
|
+
const body = {
|
|
8326
|
+
...requireEnv(rest),
|
|
8327
|
+
...timeOpts(rest),
|
|
8328
|
+
page: Number(opt(rest, "--page") || 1),
|
|
8329
|
+
pageSize: Number(opt(rest, "--page-size") || 20)
|
|
8330
|
+
};
|
|
8331
|
+
for (const [k, name] of [
|
|
8332
|
+
["traceId", "--trace-id"],
|
|
8333
|
+
["curAppCode", "--app"],
|
|
8334
|
+
["remoteCallMethod", "--method"],
|
|
8335
|
+
["enableProfiler", "--profiler"]
|
|
8336
|
+
]) {
|
|
8337
|
+
const v = opt(rest, name);
|
|
8338
|
+
if (v) body[k] = v;
|
|
8339
|
+
}
|
|
8340
|
+
const cost = opt(rest, "--min-cost");
|
|
8341
|
+
if (cost) body.costTime = [Number(cost), 999999];
|
|
8342
|
+
emit(await api.call("ymc/warningCall", body), jsonMode, rowsSummary("慢调用"));
|
|
8343
|
+
break;
|
|
8344
|
+
}
|
|
8345
|
+
case "exceptions": {
|
|
8346
|
+
const body = {
|
|
8347
|
+
...requireEnv(rest),
|
|
8348
|
+
...timeOpts(rest),
|
|
8349
|
+
appCode: opt(rest, "--app") || ""
|
|
8350
|
+
};
|
|
8351
|
+
emit(await api.call("ymc/exceptionHistory", body), jsonMode, rowsSummary("异常聚合"));
|
|
8352
|
+
break;
|
|
8353
|
+
}
|
|
8354
|
+
case "alerts": {
|
|
8355
|
+
const body = {
|
|
8356
|
+
...requireEnv(rest),
|
|
8357
|
+
...timeOpts(rest),
|
|
8358
|
+
pageSize: Number(opt(rest, "--page-size") || 10),
|
|
8359
|
+
...opt(rest, "--level") ? { level: opt(rest, "--level") } : {}
|
|
8360
|
+
};
|
|
8361
|
+
emit(await api.call("ymc/alert/detail", body), jsonMode, rowsSummary("告警"));
|
|
8362
|
+
break;
|
|
8363
|
+
}
|
|
8364
|
+
case "url-stats": {
|
|
8365
|
+
const uri = positionals(rest)[0];
|
|
8366
|
+
if (!uri) {
|
|
8367
|
+
console.error("用法:bfi ymc url-stats </uri> -e <env> [-t <time> | --start-time <t> [--end-time <t>]]");
|
|
8368
|
+
process.exit(1);
|
|
8369
|
+
}
|
|
8370
|
+
emit(await api.call("kibana/stats", {
|
|
8371
|
+
uri,
|
|
8372
|
+
...requireEnv(rest),
|
|
8373
|
+
...timeOpts(rest)
|
|
8374
|
+
}), jsonMode, (data) => {
|
|
8375
|
+
const d = data;
|
|
8376
|
+
const a = d.aggs;
|
|
8377
|
+
return `✓ URL 统计: QPS=${a.qps} avg=${a.avg_rt?.value ?? "-"} 慢请求Top=${Array.isArray(d.slow_top) ? d.slow_top.length : 0}`;
|
|
8378
|
+
});
|
|
8379
|
+
break;
|
|
8380
|
+
}
|
|
8381
|
+
case "ingress": {
|
|
8382
|
+
const body = {
|
|
8383
|
+
...requireEnv(rest),
|
|
8384
|
+
...timeOpts(rest),
|
|
8385
|
+
size: Number(opt(rest, "--size") || 100)
|
|
8386
|
+
};
|
|
8387
|
+
const id = opt(rest, "--trace-id");
|
|
8388
|
+
const uri = opt(rest, "--uri");
|
|
8389
|
+
if (id) body.trace_id = id;
|
|
8390
|
+
else if (uri) {
|
|
8391
|
+
body.uri = uri;
|
|
8392
|
+
body.fuzzy = !flag(rest, "--exact");
|
|
8393
|
+
} else {
|
|
8394
|
+
console.error("用法:bfi ymc ingress --trace-id <id> | --uri </path> -e <env> [-t time]");
|
|
8395
|
+
process.exit(1);
|
|
8396
|
+
}
|
|
8397
|
+
emit(await api.call("kibana/search", body), jsonMode, rowsSummary("ingress 日志"));
|
|
8398
|
+
break;
|
|
8399
|
+
}
|
|
8400
|
+
case "dump-list": {
|
|
8401
|
+
const body = {
|
|
8402
|
+
...requireEnv(rest),
|
|
8403
|
+
...timeOpts(rest),
|
|
8404
|
+
monitorType: opt(rest, "--type") || "profiler",
|
|
8405
|
+
appIdList: opt(rest, "--app") ? [opt(rest, "--app")] : []
|
|
8406
|
+
};
|
|
8407
|
+
emit(await api.call("ymc/tool/list", body), jsonMode, rowsSummary("诊断文件"));
|
|
8408
|
+
break;
|
|
8409
|
+
}
|
|
8410
|
+
case "cases": {
|
|
8411
|
+
const sub2 = rest[0] || "search";
|
|
8412
|
+
const rest2 = rest.slice(1);
|
|
8413
|
+
if (sub2 === "search") {
|
|
8414
|
+
const body = {};
|
|
8415
|
+
for (const [k, name] of [
|
|
8416
|
+
["type", "--type"],
|
|
8417
|
+
["fingerprint", "--finger"],
|
|
8418
|
+
["service", "--service"],
|
|
8419
|
+
["keyword", "--kw"]
|
|
8420
|
+
]) {
|
|
8421
|
+
const v = opt(rest2, name);
|
|
8422
|
+
if (v) body[k] = v;
|
|
8423
|
+
}
|
|
8424
|
+
if (opt(rest2, "--limit")) body.limit = Number(opt(rest2, "--limit"));
|
|
8425
|
+
emit(await api.call("cases/search", body), jsonMode, (data) => {
|
|
8426
|
+
const d = data;
|
|
8427
|
+
const list = d.cases || [];
|
|
8428
|
+
if (!list.length) return "(无命中案例)";
|
|
8429
|
+
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");
|
|
8430
|
+
});
|
|
8431
|
+
} else if (sub2 === "show") {
|
|
8432
|
+
const id = positionals(rest2)[0];
|
|
8433
|
+
if (!id) {
|
|
8434
|
+
console.error("用法:bfi ymc cases show <case_id>");
|
|
8435
|
+
process.exit(1);
|
|
8436
|
+
}
|
|
8437
|
+
emit(await api.get(`cases/${id}`), jsonMode);
|
|
8438
|
+
} else if (sub2 === "upload") {
|
|
8439
|
+
const file = opt(rest2, "--file");
|
|
8440
|
+
if (!file) {
|
|
8441
|
+
console.error("用法:bfi ymc cases upload --file <case.md> [--type] [--finger \"a,b\"] [--env] [--service]");
|
|
8442
|
+
process.exit(1);
|
|
8443
|
+
}
|
|
8444
|
+
const { readFileSync } = await import("node:fs");
|
|
8445
|
+
const body = {
|
|
8446
|
+
type: opt(rest2, "--type") || "review",
|
|
8447
|
+
fingerprint: (opt(rest2, "--finger") || "").split(",").map((s) => s.trim()).filter(Boolean),
|
|
8448
|
+
content: readFileSync(file, "utf-8")
|
|
8449
|
+
};
|
|
8450
|
+
for (const [k, name] of [
|
|
8451
|
+
["env", "--env"],
|
|
8452
|
+
["service", "--service"],
|
|
8453
|
+
["confidence", "--confidence"],
|
|
8454
|
+
["title", "--title"]
|
|
8455
|
+
]) {
|
|
8456
|
+
const v = opt(rest2, name);
|
|
8457
|
+
if (v) body[k] = v;
|
|
8458
|
+
}
|
|
8459
|
+
emit(await api.call("cases", body), jsonMode, (data) => `✓ 案例已上传: ${data.case_id}`);
|
|
8460
|
+
} else if (sub2 === "index") emit(await api.get("cases/index/overview"), jsonMode, (data) => {
|
|
8461
|
+
const d = data;
|
|
8462
|
+
const byType = Object.entries(d.by_type || {}).map(([k, v]) => `${k}=${v}`).join(", ");
|
|
8463
|
+
const fps = (d.top_fingerprints || []).map((x) => `${x.fingerprint}(${x.count})`).join(", ");
|
|
8464
|
+
return `✓ 案例总数: ${d.total}\n 类型分布: ${byType}\n 高频指纹: ${fps}`;
|
|
8465
|
+
});
|
|
8466
|
+
else {
|
|
8467
|
+
console.error("用法:bfi ymc cases <search|show|upload|index>");
|
|
8468
|
+
process.exit(1);
|
|
8469
|
+
}
|
|
8470
|
+
break;
|
|
8471
|
+
}
|
|
8472
|
+
case "wiki": {
|
|
8473
|
+
const sub2 = rest[0] || "search";
|
|
8474
|
+
const rest2 = rest.slice(1);
|
|
8475
|
+
const searchBase = (process.env.BFI_WIKI_BASE || YMC_DEFAULT_BASE.replace(/\/api\/v2$/, "/api/v1")) + "/search/";
|
|
8476
|
+
if (sub2 === "search") {
|
|
8477
|
+
const kw = positionals(rest2)[0];
|
|
8478
|
+
if (!kw) {
|
|
8479
|
+
console.error("用法:bfi ymc wiki search \"<关键词>\" [--source confluence|jira|ecfile] [--limit N] [--titles]");
|
|
8480
|
+
process.exit(1);
|
|
8481
|
+
}
|
|
8482
|
+
const body = {
|
|
8483
|
+
query: kw,
|
|
8484
|
+
limit: Number(opt(rest2, "--limit") || 20)
|
|
8485
|
+
};
|
|
8486
|
+
const src = opt(rest2, "--source");
|
|
8487
|
+
if (src) body.sources = [src];
|
|
8488
|
+
if (flag(rest2, "--titles")) body.titles_only = true;
|
|
8489
|
+
const resp = await new Promise((resolve, reject) => {
|
|
8490
|
+
const mod = searchBase.startsWith("https://") ? https : http;
|
|
8491
|
+
const payload = JSON.stringify(body);
|
|
8492
|
+
const req = mod.request(searchBase + "unified", {
|
|
8493
|
+
method: "POST",
|
|
8494
|
+
headers: {
|
|
8495
|
+
"Content-Type": "application/json",
|
|
8496
|
+
Accept: "application/json",
|
|
8497
|
+
"Content-Length": Buffer.byteLength(payload)
|
|
8498
|
+
},
|
|
8499
|
+
timeout: 6e4
|
|
8500
|
+
}, (res) => {
|
|
8501
|
+
const chunks = [];
|
|
8502
|
+
res.on("data", (c) => chunks.push(c));
|
|
8503
|
+
res.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
|
|
8504
|
+
});
|
|
8505
|
+
req.on("timeout", () => req.destroy(/* @__PURE__ */ new Error("超时")));
|
|
8506
|
+
req.on("error", reject);
|
|
8507
|
+
req.write(payload);
|
|
8508
|
+
req.end();
|
|
8509
|
+
});
|
|
8510
|
+
if (jsonMode) {
|
|
8511
|
+
console.log(JSON.stringify(JSON.parse(raw0(resp)), null, 2));
|
|
8512
|
+
break;
|
|
8513
|
+
}
|
|
8514
|
+
const parsed = JSON.parse(raw0(resp));
|
|
8515
|
+
if (parsed.code !== 0) {
|
|
8516
|
+
console.error(`✗ ${parsed.msg}`);
|
|
8517
|
+
process.exit(1);
|
|
8518
|
+
}
|
|
8519
|
+
console.log(JSON.stringify(parsed.data, null, 2));
|
|
8520
|
+
} else if (sub2 === "show") {
|
|
8521
|
+
const id = positionals(rest2)[0];
|
|
8522
|
+
const sourceName = opt(rest2, "--source") || "gfjira";
|
|
8523
|
+
if (!id) {
|
|
8524
|
+
console.error("用法:bfi ymc wiki show <id> [--source wiki|uap-wiki|gfwiki|gfjira|ecfile]");
|
|
8525
|
+
process.exit(1);
|
|
8526
|
+
}
|
|
8527
|
+
const resp = await new Promise((resolve, reject) => {
|
|
8528
|
+
const mod = searchBase.startsWith("https://") ? https : http;
|
|
8529
|
+
const payload = JSON.stringify({
|
|
8530
|
+
source_name: sourceName,
|
|
8531
|
+
id,
|
|
8532
|
+
offset: 0
|
|
8533
|
+
});
|
|
8534
|
+
const req = mod.request(searchBase + "detail", {
|
|
8535
|
+
method: "POST",
|
|
8536
|
+
headers: {
|
|
8537
|
+
"Content-Type": "application/json",
|
|
8538
|
+
Accept: "application/json",
|
|
8539
|
+
"Content-Length": Buffer.byteLength(payload)
|
|
8540
|
+
},
|
|
8541
|
+
timeout: 6e4
|
|
8542
|
+
}, (res) => {
|
|
8543
|
+
const chunks = [];
|
|
8544
|
+
res.on("data", (c) => chunks.push(c));
|
|
8545
|
+
res.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
|
|
8546
|
+
});
|
|
8547
|
+
req.on("timeout", () => req.destroy(/* @__PURE__ */ new Error("超时")));
|
|
8548
|
+
req.on("error", reject);
|
|
8549
|
+
req.write(payload);
|
|
8550
|
+
req.end();
|
|
8551
|
+
});
|
|
8552
|
+
if (jsonMode) {
|
|
8553
|
+
console.log(JSON.stringify(JSON.parse(raw0(resp)), null, 2));
|
|
8554
|
+
break;
|
|
8555
|
+
}
|
|
8556
|
+
const parsed = JSON.parse(raw0(resp));
|
|
8557
|
+
if (parsed.code !== 0) {
|
|
8558
|
+
console.error(`✗ ${parsed.msg}`);
|
|
8559
|
+
process.exit(1);
|
|
8560
|
+
}
|
|
8561
|
+
const d = parsed.data;
|
|
8562
|
+
const text = String(d.content ?? d.text ?? JSON.stringify(d));
|
|
8563
|
+
console.log(text.length > 4e3 ? `${text.slice(0, 4e3)}\n…(截断,--json 看全量)` : text);
|
|
8564
|
+
} else {
|
|
8565
|
+
console.error("用法:bfi ymc wiki <search|show>");
|
|
8566
|
+
process.exit(1);
|
|
8567
|
+
}
|
|
8568
|
+
break;
|
|
8569
|
+
}
|
|
8570
|
+
case "callstat":
|
|
8571
|
+
emit(await api.call("ymc/warningCallstat", {
|
|
8572
|
+
...requireEnv(rest),
|
|
8573
|
+
...timeOpts(rest),
|
|
8574
|
+
page: Number(opt(rest, "--page") || 1),
|
|
8575
|
+
pageSize: Number(opt(rest, "--page-size") || 20)
|
|
8576
|
+
}), jsonMode, rowsSummary("慢调用统计"));
|
|
8577
|
+
break;
|
|
8578
|
+
case "sqlstat":
|
|
8579
|
+
emit(await api.call("ymc/warningSqlstat", {
|
|
8580
|
+
...requireEnv(rest),
|
|
8581
|
+
...timeOpts(rest),
|
|
8582
|
+
page: Number(opt(rest, "--page") || 1),
|
|
8583
|
+
pageSize: Number(opt(rest, "--page-size") || 20)
|
|
8584
|
+
}), jsonMode, rowsSummary("慢 SQL 统计"));
|
|
8585
|
+
break;
|
|
8586
|
+
case "events": {
|
|
8587
|
+
const body = {
|
|
8588
|
+
...requireEnv(rest),
|
|
8589
|
+
type: opt(rest, "--type") || "",
|
|
8590
|
+
startTime: opt(rest, "--start-time") || "",
|
|
8591
|
+
endTime: opt(rest, "--end-time") || "",
|
|
8592
|
+
page: Number(opt(rest, "--page") || 1),
|
|
8593
|
+
pageSize: Number(opt(rest, "--page-size") || 10),
|
|
8594
|
+
appCode: opt(rest, "--app") || "",
|
|
8595
|
+
pipelineCode: opt(rest, "--pipeline") || ""
|
|
8596
|
+
};
|
|
8597
|
+
const realPath = flag(rest, "--histogram") ? "ymc/eventHistograms" : "ymc/eventLogs";
|
|
8598
|
+
emit(await api.call(realPath, body), jsonMode, rowsSummary(flag(rest, "--histogram") ? "事件直方图" : "集群事件"));
|
|
8599
|
+
break;
|
|
8600
|
+
}
|
|
8601
|
+
case "log-url": {
|
|
8602
|
+
const body = {
|
|
8603
|
+
...requireEnv(rest),
|
|
8604
|
+
type: opt(rest, "--type") || "ingressLog",
|
|
8605
|
+
appId: opt(rest, "--app") || ""
|
|
8606
|
+
};
|
|
8607
|
+
const ids = opt(rest, "--filter-app");
|
|
8608
|
+
if (ids) body.filterCondition = { appIds: ids.split(",").map((s) => s.trim()) };
|
|
8609
|
+
emit(await api.call("ymc/aliyun-log/url", body), jsonMode, (data) => `✓ 日志直通 URL 已生成(data 内含 url,浏览器可直接打开)`);
|
|
8610
|
+
break;
|
|
8611
|
+
}
|
|
8612
|
+
case "sample-list":
|
|
8613
|
+
emit(await api.call("ymc/sample/list", {
|
|
8614
|
+
...requireEnv(rest),
|
|
8615
|
+
...timeOpts(rest)
|
|
8616
|
+
}), jsonMode, rowsSummary("采样数据"));
|
|
8617
|
+
break;
|
|
8618
|
+
case "rca": {
|
|
8619
|
+
const body = {
|
|
8620
|
+
...requireEnv(rest),
|
|
8621
|
+
...timeOpts(rest)
|
|
8622
|
+
};
|
|
8623
|
+
for (const [k, name] of [["appId", "--app"], ["appCode", "--app-code"]]) {
|
|
8624
|
+
const v = opt(rest, name);
|
|
8625
|
+
if (v) body[k] = v;
|
|
8626
|
+
}
|
|
8627
|
+
emit(await api.call("ymc/rca/full-report", body, 3e5), jsonMode, () => "✓ RCA 报告已生成(--json 查看全文)");
|
|
8628
|
+
break;
|
|
8629
|
+
}
|
|
8630
|
+
case "bpr-download": {
|
|
8631
|
+
const pk = positionals(rest)[0];
|
|
8632
|
+
if (!pk) {
|
|
8633
|
+
console.error("用法:bfi ymc bpr-download <pk> -e <env> [-o 输出文件]");
|
|
8634
|
+
console.error("例:bfi ymc bpr-download eopppmokijpjoflfmodjffojcohiflgm -e pre -o ~/bpr.html");
|
|
8635
|
+
console.error("pk 来源:bfi ymc call bpr/list --data '{\"page\":1,\"pageSize\":10}' -e <env>");
|
|
8636
|
+
process.exit(1);
|
|
8637
|
+
}
|
|
8638
|
+
const out = opt(rest, "-o", "--out") || `bpr_${pk.slice(0, 16)}.html`;
|
|
8639
|
+
try {
|
|
8640
|
+
const n = await api.download("ymc/bpr/download", {
|
|
8641
|
+
...requireEnv(rest),
|
|
8642
|
+
pk
|
|
8643
|
+
}, out);
|
|
8644
|
+
console.log(`✓ 已下载 ${n} 字节 → ${out}`);
|
|
8645
|
+
} catch (e) {
|
|
8646
|
+
console.error(`✗ ${e.message}`);
|
|
8647
|
+
process.exit(1);
|
|
8648
|
+
}
|
|
8649
|
+
break;
|
|
8650
|
+
}
|
|
8651
|
+
case "call": {
|
|
8652
|
+
const route = positionals(rest)[0];
|
|
8653
|
+
const dataStr = opt(rest, "--data") || "{}";
|
|
8654
|
+
if (!route) {
|
|
8655
|
+
console.error("用法:bfi ymc call <route> -e <env> [--data '{\"k\":\"v\"}'] [--timeout 秒] [-t time | --start-time <s> --end-time <e>]");
|
|
8656
|
+
console.error("route 例:warningCall/warningSql/exceptionHistory/bpr/list/alert/detail/uba/query/tool/list/environment ...");
|
|
8657
|
+
console.error("完整清单见 http://172.20.35.178:8901/docs");
|
|
8658
|
+
process.exit(1);
|
|
8659
|
+
}
|
|
8660
|
+
let extra;
|
|
8661
|
+
try {
|
|
8662
|
+
extra = JSON.parse(dataStr);
|
|
8663
|
+
} catch {
|
|
8664
|
+
console.error("--data 不是合法 JSON");
|
|
8665
|
+
process.exit(1);
|
|
8666
|
+
}
|
|
8667
|
+
const timeoutArg = opt(rest, "--timeout");
|
|
8668
|
+
const timeoutMs = timeoutArg ? Number(timeoutArg) * 1e3 : void 0;
|
|
8669
|
+
emit(await api.call(`ymc/${route}`, {
|
|
8670
|
+
...requireEnv(rest),
|
|
8671
|
+
...timeOpts(rest),
|
|
8672
|
+
...extra
|
|
8673
|
+
}, timeoutMs), jsonMode);
|
|
8674
|
+
break;
|
|
8675
|
+
}
|
|
8676
|
+
default:
|
|
8677
|
+
console.error(`未知子命令:${sub}`);
|
|
8678
|
+
printHelp();
|
|
8679
|
+
process.exit(1);
|
|
8680
|
+
}
|
|
8681
|
+
}
|
|
8682
|
+
function printHelp() {
|
|
8683
|
+
console.log(`
|
|
8684
|
+
bfi ymc — 观测查询(v2 API:YMC/Kibana/SLS/trace)
|
|
8685
|
+
|
|
8686
|
+
命令:
|
|
8687
|
+
status 登录会话状态(dev/prod)
|
|
8688
|
+
trace <trace_id> -e <env> 三源合一(SLS+链路+ingress)
|
|
8689
|
+
chain <trace_id> -e <env> 原子源:warningChain 链路
|
|
8690
|
+
sls-logs <trace_id> -e <env> 原子源:SLS 业务日志
|
|
8691
|
+
sls-raw "<查询>" -e <env> SLS 原始查询(完整查询语法透传)
|
|
8692
|
+
sls-grep "<关键词>" -e <env> SLS 全文检索聚合(异常历史分布取证)
|
|
8693
|
+
slowsql -t <time> [-e] [--sql-str] [--md5] [--trace-id] [--app] [--profiler]
|
|
8694
|
+
slowcall -t <time> [-e] [--trace-id] [--app] [--method] [--min-cost ms] [--profiler]
|
|
8695
|
+
exceptions -t <time> [-e] [--app]
|
|
8696
|
+
alerts -t <time> [-e] [--level error|warn|info]
|
|
8697
|
+
url-stats </uri> -t <time> -e <env>
|
|
8698
|
+
ingress --trace-id <id> | --uri </path> [-t <time>] -e <env> [--exact]
|
|
8699
|
+
dump-list [-t <time>] -e <env> [--app] [--type profiler]
|
|
8700
|
+
cases <search|show|upload|index> 案例库(KEDB)
|
|
8701
|
+
wiki search "<kw>" [--source] [--titles] 知识库统一检索(wiki/jira/文库)
|
|
8702
|
+
wiki show <id> [--source] 知识条目全文
|
|
8703
|
+
call <route> -e <env> [--data '<json>'] 通用透传(全部注册表端点)
|
|
8704
|
+
|
|
8705
|
+
通用参数:
|
|
8706
|
+
-e, --env 环境(${ENV_DOC})
|
|
8707
|
+
-t, --time 时间简写(10m/2h/3d/4w = 过去 N 单位到现在)
|
|
8708
|
+
--start-time <t> 开始时间 yyyy-MM-dd HH:mm:ss(与 -t 二选一;只传它=到当前)
|
|
8709
|
+
--end-time <t> 结束时间(须与 --start-time 同传)
|
|
8710
|
+
--json 输出原始 JSON
|
|
8711
|
+
--token 覆盖已登录 token(默认用 bfi auth login 的)
|
|
8712
|
+
`);
|
|
8713
|
+
}
|
|
7220
8714
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7221
8715
|
function getVersion() {
|
|
7222
8716
|
try {
|
|
@@ -7309,11 +8803,37 @@ ${BOLD}━━━ 技能包管理 ━━━${RESET}
|
|
|
7309
8803
|
skills package add <pkg> <skill...> 添加技能到包
|
|
7310
8804
|
skills package remove <pkg> <skill...> 从包移除技能
|
|
7311
8805
|
|
|
8806
|
+
${BOLD}━━━ 插件管理 ━━━${RESET}
|
|
8807
|
+
plugin create <name> [--with ...] 脚手架一个插件目录
|
|
8808
|
+
plugin validate [path] 校验插件结构
|
|
8809
|
+
plugin publish <dir|zip|tgz> 发布插件到 SkillHub
|
|
8810
|
+
plugin list 列出插件
|
|
8811
|
+
plugin info <id> 查看插件详情
|
|
8812
|
+
plugin install <id|file.zip> 安装插件(SkillHub id 或本地 zip)
|
|
8813
|
+
|
|
8814
|
+
${BOLD}━━━ MCP 服务器 ━━━${RESET}
|
|
8815
|
+
mcp list 列出 MCP 服务器
|
|
8816
|
+
mcp info <id> 查看详情
|
|
8817
|
+
mcp config <id> 打印 .mcp.json 配置
|
|
8818
|
+
mcp add <id> 写入本地 .mcp.json
|
|
8819
|
+
mcp remove <name> 从本地 .mcp.json 移除
|
|
8820
|
+
|
|
7312
8821
|
${BOLD}━━━ 文档 ━━━${RESET}
|
|
7313
8822
|
docs upload <file> 上传文档
|
|
7314
8823
|
docs list 列出文档
|
|
7315
8824
|
docs info <id> 查看文档详情
|
|
7316
8825
|
|
|
8826
|
+
${BOLD}━━━ 观测查询(ymc)━━━${RESET}
|
|
8827
|
+
ymc status 登录会话状态
|
|
8828
|
+
ymc trace <trace_id> -e <env> traceId 三源合一(SLS+链路+ingress)
|
|
8829
|
+
ymc chain / sls-logs <id> 原子源:链路 / SLS 日志
|
|
8830
|
+
ymc slowsql / slowcall 慢 SQL / 慢调用(-t 时间简写)
|
|
8831
|
+
ymc exceptions / alerts 异常聚合 / 告警明细
|
|
8832
|
+
ymc url-stats <uri> -t <time> URL 性能统计
|
|
8833
|
+
ymc ingress --trace-id|--uri ingress 访问日志
|
|
8834
|
+
ymc dump-list / call <route> 诊断文件 / 通用端点透传
|
|
8835
|
+
(-e env: test/daily/pre/core1-4/commldev;-t: 10m/2h/3d/4w)
|
|
8836
|
+
|
|
7317
8837
|
${BOLD}━━━ 配置 ━━━${RESET}
|
|
7318
8838
|
config get [key] 查看配置
|
|
7319
8839
|
config set <key> <value> [--global] 设置配置
|
|
@@ -7578,6 +9098,9 @@ async function main() {
|
|
|
7578
9098
|
setAgents(await resolveAgents(cfg.token ? new SkillHubAPI(cfg.registry, cfg.token) : void 0));
|
|
7579
9099
|
} catch {}
|
|
7580
9100
|
switch (command) {
|
|
9101
|
+
case "ymc":
|
|
9102
|
+
await runYmc(restArgs, globalOpts);
|
|
9103
|
+
break;
|
|
7581
9104
|
case "auth":
|
|
7582
9105
|
if (restArgs[0] === "logout") await runLogout();
|
|
7583
9106
|
else if (restArgs[0] === "whoami") await runWhoami(globalOpts);
|
|
@@ -7610,6 +9133,12 @@ async function main() {
|
|
|
7610
9133
|
case "pkg":
|
|
7611
9134
|
await runPackage(restArgs, globalOpts);
|
|
7612
9135
|
break;
|
|
9136
|
+
case "plugin":
|
|
9137
|
+
await runPlugin(restArgs, globalOpts);
|
|
9138
|
+
break;
|
|
9139
|
+
case "mcp":
|
|
9140
|
+
await runMcp(restArgs, globalOpts);
|
|
9141
|
+
break;
|
|
7613
9142
|
case "log":
|
|
7614
9143
|
await runLog(restArgs, globalOpts);
|
|
7615
9144
|
break;
|
|
@@ -7688,5 +9217,8 @@ async function main() {
|
|
|
7688
9217
|
console.log(`运行 ${BOLD}bfi --help${RESET} 查看用法。`);
|
|
7689
9218
|
}
|
|
7690
9219
|
}
|
|
7691
|
-
main().
|
|
9220
|
+
main().catch((e) => {
|
|
9221
|
+
console.error(`✗ ${e?.message || e}`);
|
|
9222
|
+
process.exit(1);
|
|
9223
|
+
});
|
|
7692
9224
|
export { loadGlobalConfig };
|