bfi-cli 0.1.97 → 0.1.99

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -451,10 +451,8 @@ async function searchMultiselect(options) {
451
451
  }
452
452
  if (key.name === "space") {
453
453
  const item = filtered[cursor];
454
- if (item) {
455
- if (selected.has(item.value)) selected.delete(item.value);
456
- else selected.add(item.value);
457
- }
454
+ if (item) if (selected.has(item.value)) selected.delete(item.value);
455
+ else selected.add(item.value);
458
456
  render();
459
457
  return;
460
458
  }
@@ -514,14 +512,12 @@ var SkillHubAPI = class {
514
512
  if (url.protocol === "https:") requestOptions.rejectUnauthorized = this.rejectUnauthorized;
515
513
  let bodyStr;
516
514
  let bodyBuf;
517
- if (options.body !== void 0) {
518
- if (options.body instanceof Buffer) bodyBuf = options.body;
519
- else if (typeof options.body === "string") bodyStr = options.body;
520
- else if (typeof FormData !== "undefined" && options.body instanceof FormData) return this.fetchRequest(url, requestOptions, options.body);
521
- else {
522
- requestOptions.headers["Content-Type"] = "application/json";
523
- bodyStr = JSON.stringify(options.body);
524
- }
515
+ if (options.body !== void 0) if (options.body instanceof Buffer) bodyBuf = options.body;
516
+ else if (typeof options.body === "string") bodyStr = options.body;
517
+ else if (typeof FormData !== "undefined" && options.body instanceof FormData) return this.fetchRequest(url, requestOptions, options.body);
518
+ else {
519
+ requestOptions.headers["Content-Type"] = "application/json";
520
+ bodyStr = JSON.stringify(options.body);
525
521
  }
526
522
  return new Promise((resolve, reject) => {
527
523
  const req = (url.protocol === "https:" ? https : http).request(url, requestOptions, (res) => {
@@ -870,7 +866,12 @@ var SkillHubAPI = class {
870
866
  try {
871
867
  const parsed = JSON.parse(data);
872
868
  if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) resolve(parsed);
873
- else reject(new Error(parsed.msg || parsed.message || `HTTP ${res.statusCode}`));
869
+ else {
870
+ const error = new Error(parsed.msg || parsed.message || `HTTP ${res.statusCode}`);
871
+ error.status = res.statusCode;
872
+ error.body = parsed;
873
+ reject(error);
874
+ }
874
875
  } catch {
875
876
  reject(new Error(data || `HTTP ${res.statusCode}`));
876
877
  }
@@ -1300,12 +1301,10 @@ async function isDirEntryOrSymlinkToDir(entry, entryPath) {
1300
1301
  }
1301
1302
  }
1302
1303
  function getCanonicalSkillsDir(global, cwd) {
1303
- const baseDir = global ? homedir() : cwd || process.cwd();
1304
- return join(baseDir, AGENTS_DIR, SKILLS_SUBDIR);
1304
+ return join(global ? homedir() : cwd || process.cwd(), AGENTS_DIR, SKILLS_SUBDIR);
1305
1305
  }
1306
1306
  function getEveSubagentSkillsDir(subagent, cwd) {
1307
- const baseDir = cwd || process.cwd();
1308
- return join(baseDir, EVE_SUBAGENTS_DIR, sanitizeName(subagent), "skills");
1307
+ return join(cwd || process.cwd(), EVE_SUBAGENTS_DIR, sanitizeName(subagent), "skills");
1309
1308
  }
1310
1309
  function getAgentBaseDir(agentType, global, cwd, eveSubagent) {
1311
1310
  if (isUniversalAgent(agentType)) return getCanonicalSkillsDir(global, cwd);
@@ -1383,8 +1382,7 @@ async function resolveParentSymlinks(path) {
1383
1382
  const dir = dirname(resolved);
1384
1383
  const base = basename(resolved);
1385
1384
  try {
1386
- const realDir = await realpath(dir);
1387
- return join(realDir, base);
1385
+ return join(await realpath(dir), base);
1388
1386
  } catch {
1389
1387
  return resolved;
1390
1388
  }
@@ -1408,8 +1406,7 @@ async function createSymlink(target, linkPath) {
1408
1406
  }
1409
1407
  const linkDir = dirname(linkPath);
1410
1408
  await mkdir(linkDir, { recursive: true });
1411
- const realLinkDir = await resolveParentSymlinks(linkDir);
1412
- const relativePath = relative(realLinkDir, target);
1409
+ const relativePath = relative(await resolveParentSymlinks(linkDir), target);
1413
1410
  const symlinkType = platform() === "win32" ? "junction" : void 0;
1414
1411
  await symlink(symlinkType === "junction" ? resolvedTarget : relativePath, linkPath, symlinkType);
1415
1412
  return true;
@@ -1478,8 +1475,7 @@ async function installSkillForAgent(skill, agentType, options = {}) {
1478
1475
  mode: "symlink"
1479
1476
  };
1480
1477
  if (!isGlobal && !isUniversalAgent(agentType)) {
1481
- const agentRootDir = join(cwd, agents[agentType].skillsDir.split("/")[0]);
1482
- if (!existsSync(agentRootDir) && agentType !== "claude-code") return {
1478
+ if (!existsSync(join(cwd, agents[agentType].skillsDir.split("/")[0])) && agentType !== "claude-code") return {
1483
1479
  success: true,
1484
1480
  path: canonicalDir,
1485
1481
  canonicalPath: canonicalDir,
@@ -1553,8 +1549,7 @@ async function copyDirectory(src, dest, agentType) {
1553
1549
  dereference: true,
1554
1550
  recursive: true
1555
1551
  });
1556
- const sourceStats = await stat(srcPath);
1557
- await chmod(destPath, sourceStats.mode & 511);
1552
+ await chmod(destPath, (await stat(srcPath)).mode & 511);
1558
1553
  } catch (err) {
1559
1554
  if (err instanceof Error && "code" in err && err.code === "ENOENT" && entry.isSymbolicLink()) console.warn(`Skipping broken symlink: ${srcPath}`);
1560
1555
  else throw err;
@@ -1771,8 +1766,7 @@ async function installBlobSkillForAgent(skill, agentType, options = {}) {
1771
1766
  mode: "symlink"
1772
1767
  };
1773
1768
  if (!isGlobal && !isUniversalAgent(agentType)) {
1774
- const agentRootDir = join(cwd, agents[agentType].skillsDir.split("/")[0]);
1775
- if (!existsSync(agentRootDir)) return {
1769
+ if (!existsSync(join(cwd, agents[agentType].skillsDir.split("/")[0]))) return {
1776
1770
  success: true,
1777
1771
  path: canonicalDir,
1778
1772
  canonicalPath: canonicalDir,
@@ -2000,7 +1994,7 @@ var ProviderRegistryImpl = class {
2000
1994
  };
2001
1995
  new ProviderRegistryImpl();
2002
1996
  const DISCOVERY_SCHEMA_V2 = "https://schemas.agentskills.io/discovery/0.2.0/schema.json";
2003
- const MAX_ARCHIVE_UNPACKED_BYTES = 52428800;
1997
+ const MAX_ARCHIVE_UNPACKED_BYTES = 50 * 1024 * 1024;
2004
1998
  const MAX_ARCHIVE_FILES = 1e3;
2005
1999
  var WellKnownProvider = class {
2006
2000
  id = "well-known";
@@ -3153,23 +3147,22 @@ async function handleWellKnownSkills(source, url, options, spinner) {
3153
3147
  const installedAgents = await detectInstalledAgents();
3154
3148
  const totalAgents = Object.keys(agents).length;
3155
3149
  spinner.stop(`${totalAgents} agents`);
3156
- if (installedAgents.length === 0) {
3157
- if (options.yes) {
3158
- targetAgents = validAgents;
3159
- log.info("正在安装到全部 Agent");
3160
- } else {
3161
- log.info("请选择要安装技能的 Agent");
3162
- const selected = await promptForAgents("请选择要安装到的 Agent:", Object.entries(agents).map(([key, config]) => ({
3163
- value: key,
3164
- label: config.displayName
3165
- })));
3166
- if (isCancel(selected)) {
3167
- cancel("安装已取消");
3168
- process.exit(0);
3169
- }
3170
- targetAgents = selected;
3150
+ if (installedAgents.length === 0) if (options.yes) {
3151
+ targetAgents = validAgents;
3152
+ log.info("正在安装到全部 Agent");
3153
+ } else {
3154
+ log.info("请选择要安装技能的 Agent");
3155
+ const selected = await promptForAgents("请选择要安装到的 Agent:", Object.entries(agents).map(([key, config]) => ({
3156
+ value: key,
3157
+ label: config.displayName
3158
+ })));
3159
+ if (isCancel(selected)) {
3160
+ cancel("安装已取消");
3161
+ process.exit(0);
3171
3162
  }
3172
- } else if (installedAgents.length === 1 || options.yes) {
3163
+ targetAgents = selected;
3164
+ }
3165
+ else if (installedAgents.length === 1 || options.yes) {
3173
3166
  targetAgents = ensureUniversalAgents(installedAgents);
3174
3167
  if (installedAgents.length === 1) {
3175
3168
  const firstAgent = installedAgents[0];
@@ -3404,14 +3397,13 @@ async function installFromSkillHub(parsed, options, spinner) {
3404
3397
  slug = hit.slug;
3405
3398
  log.info(`自动选择:${import_picocolors.default.cyan(`@${namespace}/${slug}`)} — ${hit.description || hit.name}`);
3406
3399
  } else {
3407
- const choices = hits.map((h, idx) => ({
3408
- value: idx,
3409
- label: `@${h.namespace}/${h.slug}`,
3410
- hint: h.description || h.name
3411
- }));
3412
3400
  const idx = await select({
3413
3401
  message: "匹配到多个技能 — 请选择一个",
3414
- options: choices
3402
+ options: hits.map((h, idx) => ({
3403
+ value: idx,
3404
+ label: `@${h.namespace}/${h.slug}`,
3405
+ hint: h.description || h.name
3406
+ }))
3415
3407
  });
3416
3408
  if (isCancel(idx)) {
3417
3409
  cancel("安装已取消");
@@ -3438,8 +3430,7 @@ async function installFromSkillHub(parsed, options, spinner) {
3438
3430
  }
3439
3431
  const meta = metadata;
3440
3432
  spinner.stop(`Skill: ${import_picocolors.default.cyan(`@${namespace}/${slug}`)}${meta.description ? ` — ${import_picocolors.default.dim(meta.description)}` : ""}`);
3441
- const tmpRoot = tmpdir();
3442
- tempDir = join(tmpRoot, `.skillhub-skillshub-${canonicalSlug.replace(/[/:@]/g, "-")}-${Date.now()}`);
3433
+ tempDir = join(tmpdir(), `.skillhub-skillshub-${canonicalSlug.replace(/[/:@]/g, "-")}-${Date.now()}`);
3443
3434
  mkdirSync(tempDir, { recursive: true });
3444
3435
  const zipPath = join(tempDir, "skill.zip");
3445
3436
  spinner.start("正在下载技能...");
@@ -3775,24 +3766,23 @@ async function runAdd(args, options = {}) {
3775
3766
  }
3776
3767
  targetAgents = selected;
3777
3768
  }
3778
- } else if (installedAgents.length === 0) {
3779
- if (options.yes) {
3780
- targetAgents = validAgents;
3781
- log.info("正在安装到全部 Agent");
3782
- } else {
3783
- log.info("请选择要安装技能的 Agent");
3784
- const selected = await promptForAgents("请选择要安装到的 Agent:", Object.entries(agents).filter(([key]) => key !== "eve").map(([key, config]) => ({
3785
- value: key,
3786
- label: config.displayName
3787
- })));
3788
- if (isCancel(selected)) {
3789
- cancel("安装已取消");
3790
- await cleanup(tempDir);
3791
- process.exit(0);
3792
- }
3793
- targetAgents = selected;
3769
+ } else if (installedAgents.length === 0) if (options.yes) {
3770
+ targetAgents = validAgents;
3771
+ log.info("正在安装到全部 Agent");
3772
+ } else {
3773
+ log.info("请选择要安装技能的 Agent");
3774
+ const selected = await promptForAgents("请选择要安装到的 Agent:", Object.entries(agents).filter(([key]) => key !== "eve").map(([key, config]) => ({
3775
+ value: key,
3776
+ label: config.displayName
3777
+ })));
3778
+ if (isCancel(selected)) {
3779
+ cancel("安装已取消");
3780
+ await cleanup(tempDir);
3781
+ process.exit(0);
3794
3782
  }
3795
- } else if (installedAgents.length === 1 || options.yes) {
3783
+ targetAgents = selected;
3784
+ }
3785
+ else if (installedAgents.length === 1 || options.yes) {
3796
3786
  targetAgents = ensureUniversalAgents(installedAgents);
3797
3787
  if (installedAgents.length === 1) {
3798
3788
  const firstAgent = installedAgents[0];
@@ -3814,18 +3804,17 @@ async function runAdd(args, options = {}) {
3814
3804
  const availableSubagents = getEveSubagents(process.cwd());
3815
3805
  if (options.subagent && options.subagent.length > 0) eveSubagentTargets = options.subagent.map((s) => s === "root" || s === "." ? void 0 : s);
3816
3806
  else if (availableSubagents.length > 0 && !options.yes) {
3817
- const subagentChoices = [{
3818
- value: "",
3819
- label: "根 Agent",
3820
- hint: "agent/skills"
3821
- }, ...availableSubagents.map((name) => ({
3822
- value: name,
3823
- label: name,
3824
- hint: `agent/subagents/${name}/skills`
3825
- }))];
3826
3807
  const selectedSubagents = await multiselect$1({
3827
3808
  message: "Eve 技能安装位置?",
3828
- options: subagentChoices,
3809
+ options: [{
3810
+ value: "",
3811
+ label: "根 Agent",
3812
+ hint: "agent/skills"
3813
+ }, ...availableSubagents.map((name) => ({
3814
+ value: name,
3815
+ label: name,
3816
+ hint: `agent/subagents/${name}/skills`
3817
+ }))],
3829
3818
  initialValues: [""],
3830
3819
  required: true
3831
3820
  });
@@ -4003,11 +3992,9 @@ async function runAdd(args, options = {}) {
4003
3992
  const normalizedSource = getOwnerRepo(parsed);
4004
3993
  const lockSource = getLockSource(parsed.url, normalizedSource);
4005
3994
  const projectLockSourceUrl = getProjectLockSourceUrl(parsed.type, parsed.url);
4006
- if (normalizedSource) {
4007
- if (parseOwnerRepo(normalizedSource)) {
4008
- if (await repoPrivacyPromise === false) selectedSkills.map((s) => s.name).join(","), targetAgents.join(","), { ...installGlobally && { global: "1" } }, JSON.stringify(skillFiles), options.metadata;
4009
- } else selectedSkills.map((s) => s.name).join(","), targetAgents.join(","), { ...installGlobally && { global: "1" } }, JSON.stringify(skillFiles), options.metadata;
4010
- }
3995
+ if (normalizedSource) if (parseOwnerRepo(normalizedSource)) {
3996
+ if (await repoPrivacyPromise === false) selectedSkills.map((s) => s.name).join(","), targetAgents.join(","), { ...installGlobally && { global: "1" } }, JSON.stringify(skillFiles), options.metadata;
3997
+ } else selectedSkills.map((s) => s.name).join(","), targetAgents.join(","), { ...installGlobally && { global: "1" } }, JSON.stringify(skillFiles), options.metadata;
4011
3998
  if (successful.length > 0 && installGlobally && (normalizedSource || parsed.type === "skillhub")) {
4012
3999
  const successfulSkillNames = new Set(successful.map((r) => r.skill));
4013
4000
  let cachedTree;
@@ -4024,8 +4011,7 @@ async function runAdd(args, options = {}) {
4024
4011
  const hash = getSkillFolderHashFromTree(cachedTree, skillPathValue);
4025
4012
  if (hash) skillFolderHash = hash;
4026
4013
  } else if (skillPathValue && tempDir) {
4027
- const skillDir = join(tempDir, dirname(skillPathValue));
4028
- const hash = await computeSkillFolderHash(skillDir);
4014
+ const hash = await computeSkillFolderHash(join(tempDir, dirname(skillPathValue)));
4029
4015
  if (hash) skillFolderHash = hash;
4030
4016
  }
4031
4017
  await addSkillToLock(skill.name, {
@@ -4069,13 +4055,11 @@ async function runAdd(args, options = {}) {
4069
4055
  const skillResults = bySkill.get(r.skill) || [];
4070
4056
  skillResults.push(r);
4071
4057
  bySkill.set(r.skill, skillResults);
4072
- if (skillResults.length === 1) {
4073
- if (r.pluginName) {
4074
- const group = r.pluginName;
4075
- if (!groupedResults[group]) groupedResults[group] = [];
4076
- groupedResults[group].push(r);
4077
- } else ungroupedResults.push(r);
4078
- }
4058
+ if (skillResults.length === 1) if (r.pluginName) {
4059
+ const group = r.pluginName;
4060
+ if (!groupedResults[group]) groupedResults[group] = [];
4061
+ groupedResults[group].push(r);
4062
+ } else ungroupedResults.push(r);
4079
4063
  }
4080
4064
  const skillCount = bySkill.size;
4081
4065
  const symlinkFailures = successful.filter((r) => r.mode === "symlink" && r.symlinkFailed);
@@ -4771,15 +4755,12 @@ async function removeCommand(skillNames, options) {
4771
4755
  }
4772
4756
  const remainingAgents = (await detectInstalledAgents()).filter((a) => !targetAgents.includes(a));
4773
4757
  let isStillUsed = false;
4774
- for (const agentKey of remainingAgents) {
4775
- const path = getInstallPath(skillName, agentKey, {
4776
- global: isGlobal,
4777
- cwd
4778
- });
4779
- if (await lstat(path).catch(() => null)) {
4780
- isStillUsed = true;
4781
- break;
4782
- }
4758
+ for (const agentKey of remainingAgents) if (await lstat(getInstallPath(skillName, agentKey, {
4759
+ global: isGlobal,
4760
+ cwd
4761
+ })).catch(() => null)) {
4762
+ isStillUsed = true;
4763
+ break;
4783
4764
  }
4784
4765
  if (!isStillUsed) await rm(canonicalPath, {
4785
4766
  recursive: true,
@@ -4919,14 +4900,12 @@ function parseUpdateOptions(args) {
4919
4900
  }
4920
4901
  function hasProjectSkills(cwd) {
4921
4902
  const dir = cwd || process.cwd();
4922
- const lockPath = join(dir, "skills-lock.json");
4923
- if (existsSync(lockPath)) return true;
4903
+ if (existsSync(join(dir, "skills-lock.json"))) return true;
4924
4904
  const skillsDir = join(dir, ".agents", "skills");
4925
4905
  try {
4926
4906
  const entries = readdirSync(skillsDir, { withFileTypes: true });
4927
4907
  for (const entry of entries) if (entry.isDirectory()) {
4928
- const skillMd = join(skillsDir, entry.name, "SKILL.md");
4929
- if (existsSync(skillMd)) return true;
4908
+ if (existsSync(join(skillsDir, entry.name, "SKILL.md"))) return true;
4930
4909
  }
4931
4910
  } catch {}
4932
4911
  return false;
@@ -5350,8 +5329,7 @@ async function updateProjectSkills(options = {}) {
5350
5329
  try {
5351
5330
  tempDir = await cloneRepo(sourceUrl, ref);
5352
5331
  const discoveredPaths = (await discoverSkills(tempDir)).map((s) => {
5353
- const relPath = relative(tempDir, s.path);
5354
- return join(relPath, "SKILL.md").split(sep).join("/");
5332
+ return join(relative(tempDir, s.path), "SKILL.md").split(sep).join("/");
5355
5333
  });
5356
5334
  deletedSkills = await checkAndPromptForDeletions(source, allLockedForSource, localLock.skills, false, options, discoveredPaths);
5357
5335
  } catch (error) {
@@ -5810,12 +5788,8 @@ function fail(message) {
5810
5788
  }
5811
5789
  var UseCommandError = class extends Error {};
5812
5790
  async function runLogin(args, globalOpts) {
5813
- const token = getOptionValue$3(args, "--token");
5791
+ const token = getOptionValue$3(args, "--token") || await promptForToken();
5814
5792
  const { registry } = resolveConfig(globalOpts);
5815
- if (!token) {
5816
- console.error("用法:bfi auth login --token <token>");
5817
- process.exit(1);
5818
- }
5819
5793
  const api = new SkillHubAPI(registry, token);
5820
5794
  try {
5821
5795
  const response = await api.whoami();
@@ -5848,7 +5822,7 @@ async function runLogout() {
5848
5822
  async function runWhoami(globalOpts) {
5849
5823
  const { registry, token } = resolveConfig(globalOpts);
5850
5824
  if (!token) {
5851
- console.error("未认证。运行:bfi auth login --token <token>");
5825
+ console.error("未认证。运行:bfi auth login(在隐藏提示中输入 Token)");
5852
5826
  process.exit(1);
5853
5827
  }
5854
5828
  const response = await new SkillHubAPI(registry, token).whoami();
@@ -5889,6 +5863,47 @@ async function runWhoami(globalOpts) {
5889
5863
  }
5890
5864
  console.log();
5891
5865
  }
5866
+ function promptForToken() {
5867
+ const input = process.stdin;
5868
+ if (!input.isTTY || typeof input.setRawMode !== "function") throw new Error("请在交互式终端运行 `bfi auth login`,Token 将以隐藏方式输入。");
5869
+ process.stderr.write("请输入 SkillHub Token(输入内容隐藏): ");
5870
+ return new Promise((resolve, reject) => {
5871
+ let token = "";
5872
+ let finished = false;
5873
+ const wasRaw = input.isRaw;
5874
+ const cleanup = () => {
5875
+ if (finished) return;
5876
+ finished = true;
5877
+ input.off("data", onData);
5878
+ input.setRawMode(Boolean(wasRaw));
5879
+ input.pause();
5880
+ process.stderr.write("\n");
5881
+ };
5882
+ const onData = (chunk) => {
5883
+ for (const character of chunk.toString("utf8")) {
5884
+ const code = character.charCodeAt(0);
5885
+ if (code === 3 || code === 4) {
5886
+ cleanup();
5887
+ reject(/* @__PURE__ */ new Error("登录已取消。"));
5888
+ return;
5889
+ }
5890
+ if (character === "\r" || character === "\n") {
5891
+ cleanup();
5892
+ const value = token.trim();
5893
+ if (value) resolve(value);
5894
+ else reject(/* @__PURE__ */ new Error("Token 不能为空。"));
5895
+ return;
5896
+ }
5897
+ if (code === 8 || code === 127) token = token.slice(0, -1);
5898
+ else if (code >= 32 && code <= 126) token += character;
5899
+ }
5900
+ };
5901
+ input.setRawMode(true);
5902
+ input.setEncoding("utf8");
5903
+ input.resume();
5904
+ input.on("data", onData);
5905
+ });
5906
+ }
5892
5907
  function getOptionValue$3(args, flag) {
5893
5908
  const idx = args.indexOf(flag);
5894
5909
  return idx !== -1 && args[idx + 1] ? args[idx + 1] : void 0;
@@ -6285,8 +6300,8 @@ const DIM$5 = "\x1B[38;5;102m";
6285
6300
  const TEXT$4 = "\x1B[38;5;145m";
6286
6301
  function formatFileSize(bytes) {
6287
6302
  if (bytes < 1024) return `${bytes} B`;
6288
- if (bytes < 1048576) return `${(bytes / 1024).toFixed(1)} KB`;
6289
- return `${(bytes / 1048576).toFixed(1)} MB`;
6303
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
6304
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
6290
6305
  }
6291
6306
  async function runDocs(args, globalOpts) {
6292
6307
  switch (args[0]) {
@@ -6550,8 +6565,7 @@ async function publishFromDir(positionalArgs, api, namespace, visibility) {
6550
6565
  console.log("需要包含 frontmatter(name, description)的 SKILL.md 文件。");
6551
6566
  process.exit(1);
6552
6567
  }
6553
- const skillMdContent = fs.readFileSync(skillMdPath, "utf-8");
6554
- const { data: frontmatter } = parseFrontmatter(skillMdContent);
6568
+ const { data: frontmatter } = parseFrontmatter(fs.readFileSync(skillMdPath, "utf-8"));
6555
6569
  const skillName = frontmatter.name || path$1.basename(resolvedDir);
6556
6570
  console.log(`${DIM$4}正在从目录发布技能...${RESET$4}`);
6557
6571
  console.log(` ${BOLD$2}名称:${RESET$4} ${TEXT$3}${skillName}${RESET$4}`);
@@ -6569,7 +6583,7 @@ async function publishFromDir(positionalArgs, api, namespace, visibility) {
6569
6583
  if (res.slug) console.log(` ${BOLD$2}Slug:${RESET$4} ${TEXT$3}${res.slug}${RESET$4}`);
6570
6584
  if (res.url) console.log(` ${BOLD$2}URL:${RESET$4} ${TEXT$3}${res.url}${RESET$4}`);
6571
6585
  } catch (error) {
6572
- console.error(` ${RED$2}发布失败:${error.message}${RESET$4}`);
6586
+ printPublishFailure(error, api.registry);
6573
6587
  process.exit(1);
6574
6588
  }
6575
6589
  console.log();
@@ -6598,8 +6612,7 @@ async function publishFromZip(positionalArgs, api, namespace, visibility) {
6598
6612
  }
6599
6613
  const skillDir = path$1.dirname(skillMdPath);
6600
6614
  console.log(` ${GREEN$3}在 ${path$1.relative(tmpDir, skillDir) || "."} 中找到 SKILL.md${RESET$4}`);
6601
- const skillMdContent = fs.readFileSync(skillMdPath, "utf-8");
6602
- const { data: frontmatter } = parseFrontmatter(skillMdContent);
6615
+ const { data: frontmatter } = parseFrontmatter(fs.readFileSync(skillMdPath, "utf-8"));
6603
6616
  const skillName = frontmatter.name || path$1.basename(skillDir);
6604
6617
  console.log(` ${BOLD$2}名称:${RESET$4} ${TEXT$3}${skillName}${RESET$4}`);
6605
6618
  console.log(` ${BOLD$2}命名空间:${RESET$4} ${TEXT$3}${namespace}${RESET$4}`);
@@ -6614,7 +6627,7 @@ async function publishFromZip(positionalArgs, api, namespace, visibility) {
6614
6627
  if (res.slug) console.log(` ${BOLD$2}Slug:${RESET$4} ${TEXT$3}${res.slug}${RESET$4}`);
6615
6628
  if (res.url) console.log(` ${BOLD$2}URL:${RESET$4} ${TEXT$3}${res.url}${RESET$4}`);
6616
6629
  } catch (error) {
6617
- if (error.message && !error.message.includes("Publish failed")) console.error(` ${RED$2}错误:${error.message}${RESET$4}`);
6630
+ if (error.message && !error.message.includes("Publish failed")) printPublishFailure(error, api.registry);
6618
6631
  process.exit(1);
6619
6632
  } finally {
6620
6633
  try {
@@ -6724,7 +6737,7 @@ async function publishFromGit(positionalArgs, api, namespace, visibility, yes, b
6724
6737
  console.log(` ${GREEN$3}✓ 已发布:${namespace}--${res.slug || skill.name}${RESET$4}`);
6725
6738
  successCount++;
6726
6739
  } catch (error) {
6727
- console.error(` ${RED$2}✗ ${skill.name}:${error.message}${RESET$4}`);
6740
+ printPublishFailure(error, api.registry, `✗ ${skill.name}:`);
6728
6741
  }
6729
6742
  }
6730
6743
  console.log(`\n${GREEN$3}发布完成:${successCount}/${selectedSkills.length} 个技能${RESET$4}`);
@@ -6733,6 +6746,17 @@ async function publishFromGit(positionalArgs, api, namespace, visibility, yes, b
6733
6746
  await cleanupTempDir(tmpDir);
6734
6747
  } catch {}
6735
6748
  }
6749
+ function printPublishFailure(error, registry, label = "发布失败:") {
6750
+ const requiredScope = error?.body?.data?.requiredScope;
6751
+ if (error?.status === 403 && typeof requiredScope === "string" && requiredScope) {
6752
+ const settingsUrl = `${registry}/dashboard/tokens`;
6753
+ console.error(` ${RED$2}${label}当前 API Token 缺少「${requiredScope}」权限。${RESET$4}`);
6754
+ console.error(` ${DIM$4}修复:打开 ${settingsUrl} 新建 Token 并勾选「技能发布」,${RESET$4}`);
6755
+ console.error(` ${DIM$4}然后执行 bfi auth login --token <新 Token> 后重试。${RESET$4}`);
6756
+ return;
6757
+ }
6758
+ console.error(` ${RED$2}${label}${error.message}${RESET$4}`);
6759
+ }
6736
6760
  function getOptionValue$1(args, flag) {
6737
6761
  const idx = args.indexOf(flag);
6738
6762
  return idx !== -1 && idx < args.length - 1 ? args[idx + 1] : void 0;
@@ -6769,8 +6793,8 @@ function findSkillMd(dir) {
6769
6793
  }
6770
6794
  function formatSize(bytes) {
6771
6795
  if (bytes < 1024) return `${bytes} B`;
6772
- if (bytes < 1048576) return `${(bytes / 1024).toFixed(1)} KB`;
6773
- return `${(bytes / 1048576).toFixed(1)} MB`;
6796
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
6797
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
6774
6798
  }
6775
6799
  function truncate(text, maxLen) {
6776
6800
  if (text.length <= maxLen) return text;
@@ -7055,8 +7079,7 @@ function printPackageHelp() {
7055
7079
  console.log();
7056
7080
  }
7057
7081
  async function handleLocalList(args, globalOpts) {
7058
- const cwd = getWorkdir(globalOpts, args);
7059
- const lock = loadLockfile(cwd);
7082
+ const lock = loadLockfile(getWorkdir(globalOpts, args));
7060
7083
  args.includes("-g") || args.includes("--global");
7061
7084
  const pkgs = lock.packages || {};
7062
7085
  const entries = Object.entries(pkgs);
@@ -7253,18 +7276,17 @@ async function handleInstall(args, api, globalOpts) {
7253
7276
  if (targetAgents.includes("eve")) {
7254
7277
  const availableSubagents = getEveSubagents(process.cwd());
7255
7278
  if (availableSubagents.length > 0) {
7256
- const subagentChoices = [{
7257
- value: "",
7258
- label: "根 Agent",
7259
- hint: "agent/skills"
7260
- }, ...availableSubagents.map((name) => ({
7261
- value: name,
7262
- label: name,
7263
- hint: `agent/subagents/${name}/skills`
7264
- }))];
7265
7279
  const selectedSubagents = await multiselect$1({
7266
7280
  message: "Eve 技能安装位置?",
7267
- options: subagentChoices,
7281
+ options: [{
7282
+ value: "",
7283
+ label: "根 Agent",
7284
+ hint: "agent/skills"
7285
+ }, ...availableSubagents.map((name) => ({
7286
+ value: name,
7287
+ label: name,
7288
+ hint: `agent/subagents/${name}/skills`
7289
+ }))],
7268
7290
  initialValues: [""],
7269
7291
  required: true
7270
7292
  });
@@ -7459,8 +7481,7 @@ async function handleUninstall(args, globalOpts) {
7459
7481
  }
7460
7482
  async function handleUpdate(args, api, globalOpts) {
7461
7483
  const pkgRef = args[0];
7462
- const cwd = getWorkdir(globalOpts, args);
7463
- const lock = loadLockfile(cwd);
7484
+ const lock = loadLockfile(getWorkdir(globalOpts, args));
7464
7485
  let packagesToUpdate = [];
7465
7486
  const pkgs = lock.packages || {};
7466
7487
  if (pkgRef) {
@@ -9751,7 +9772,7 @@ ${BOLD}bfi — 用友大业财BFI统一命令行工具${RESET}
9751
9772
  ${BOLD}Usage:${RESET} bfi <command> [options]
9752
9773
 
9753
9774
  ${BOLD}━━━ 认证 ━━━${RESET}
9754
- auth login --token <token> 登录 SkillHub
9775
+ auth login [--token <token>] 登录 SkillHub(不带参数时隐藏输入 Token)
9755
9776
  auth logout 登出
9756
9777
  auth whoami 查看当前用户
9757
9778
 
@@ -9826,7 +9847,7 @@ ${BOLD}━━━ 技能引用格式 ━━━${RESET}
9826
9847
  owner/repo 从 GitHub 安装
9827
9848
 
9828
9849
  ${BOLD}━━━ 示例 ━━━${RESET}
9829
- ${DIM}$ bfi auth login --token sk-xxx${RESET}
9850
+ ${DIM}$ bfi auth login${RESET}
9830
9851
  ${DIM}$ bfi skills search react${RESET}
9831
9852
  ${DIM}$ bfi skills add @myteam/db-explore${RESET}
9832
9853
  ${DIM}$ bfi skills add vercel-labs/agent-skills --skill pr-review${RESET}
@@ -9880,7 +9901,7 @@ function runInit(args) {
9880
9901
  return;
9881
9902
  }
9882
9903
  if (hasName) mkdirSync(skillDir, { recursive: true });
9883
- const skillContent = `---
9904
+ writeFileSync(skillFile, `---
9884
9905
  name: ${skillName}
9885
9906
  description: A brief description of what this skill does
9886
9907
  ---
@@ -9898,8 +9919,7 @@ Describe when this skill should be used.
9898
9919
  1. First step
9899
9920
  2. Second step
9900
9921
  3. Additional steps as needed
9901
- `;
9902
- writeFileSync(skillFile, skillContent);
9922
+ `);
9903
9923
  console.log(`${TEXT}已初始化技能:${DIM}${skillName}${RESET}`);
9904
9924
  console.log();
9905
9925
  console.log(`${DIM}已创建:${RESET}`);
@@ -1 +1 @@
1
- export {}
1
+ export { };
@@ -3,6 +3,7 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
3
  import { CallToolRequestSchema, CallToolResultSchema, ListToolsRequestSchema, ListToolsResultSchema } from "@modelcontextprotocol/sdk/types.js";
4
4
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
5
5
  import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
6
+ import JSONbig from "json-bigint";
6
7
  import fs from "node:fs";
7
8
  import path from "node:path";
8
9
  import os from "node:os";
@@ -90,6 +91,7 @@ function loadGlobalConfig() {
90
91
  }
91
92
  return null;
92
93
  }
94
+ JSONbig({ storeAsString: true });
93
95
  function resolveApiBase() {
94
96
  return process.env.BFI_API_BASE || "https://bfi.yyuap.com/bfi-api";
95
97
  }
@@ -1 +1 @@
1
- export {}
1
+ export { };
@@ -2,6 +2,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
2
2
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import { z } from "zod";
4
4
  import mysql from "mysql2/promise";
5
+ import JSONbig from "json-bigint";
5
6
  import fs from "node:fs";
6
7
  import path from "node:path";
7
8
  import os from "node:os";
@@ -98,7 +99,9 @@ async function directExecute(dsn, sql, maxRows = DEFAULT_MAX_ROWS) {
98
99
  try {
99
100
  conn = await mysql.createConnection({
100
101
  uri: dsn,
101
- connectTimeout: 8e3
102
+ connectTimeout: 8e3,
103
+ supportBigNumbers: true,
104
+ bigNumberStrings: true
102
105
  });
103
106
  } catch (e) {
104
107
  return {
@@ -150,6 +153,7 @@ function loadGlobalConfig() {
150
153
  }
151
154
  return null;
152
155
  }
156
+ const JSONBigString = JSONbig({ storeAsString: true });
153
157
  function resolveApiBase() {
154
158
  return process.env.BFI_API_BASE || "https://bfi.yyuap.com/bfi-api";
155
159
  }
@@ -185,7 +189,13 @@ async function post(path, payload, fetchImpl = fetch) {
185
189
  throw new RestError("upstream_unreachable", `bfi-api(${resolveApiBase()})不可达:${e instanceof Error ? e.message : String(e)}`);
186
190
  }
187
191
  if (resp.status === 401) throw new RestError("unauthorized", "v2 token 无效或过期:请重新 `bfi auth login --token <token>`");
188
- const body = await resp.json().catch(() => ({}));
192
+ const rawText = await resp.text().catch(() => "");
193
+ let body = {};
194
+ try {
195
+ body = JSONBigString.parse(rawText);
196
+ } catch {
197
+ body = {};
198
+ }
189
199
  if (body.code === 0) return body.data;
190
200
  throw new RestError(body.data?.error_kind || "upstream_error", body.msg || "bfi-api 返回错误");
191
201
  }
@@ -1 +1 @@
1
- export {}
1
+ export { };
@@ -3,6 +3,7 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
3
  import { CallToolRequestSchema, CallToolResultSchema, ListToolsRequestSchema, ListToolsResultSchema } from "@modelcontextprotocol/sdk/types.js";
4
4
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
5
5
  import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
6
+ import JSONbig from "json-bigint";
6
7
  import fs from "node:fs";
7
8
  import path from "node:path";
8
9
  import os from "node:os";
@@ -87,6 +88,7 @@ function loadGlobalConfig() {
87
88
  }
88
89
  return null;
89
90
  }
91
+ JSONbig({ storeAsString: true });
90
92
  function resolveToken() {
91
93
  if (process.env.BFI_TOKEN) return process.env.BFI_TOKEN;
92
94
  return loadGlobalConfig()?.token ?? null;