bfi-cli 0.1.93 → 0.1.95

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.mjs +112 -4
  2. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -7162,6 +7162,7 @@ async function handleInstall(args, api, globalOpts) {
7162
7162
  console.error(`${RED$1}获取包信息失败:${error.message}${RESET$3}`);
7163
7163
  process.exit(1);
7164
7164
  }
7165
+ if (pkgInfo.kind === "PLUGIN") throw new Error("这是完整插件,不是技能组合;请使用 --as plugin --agent <目标Agent> 安装,插件可能包含 MCP 和 Hooks。");
7165
7166
  const skills = pkgInfo.skills || [];
7166
7167
  if (skills.length === 0) {
7167
7168
  console.log(`${YELLOW$1}包中没有可独立安装的技能。${RESET$3}`);
@@ -7484,7 +7485,9 @@ async function handleUpdate(args, api, globalOpts) {
7484
7485
  console.log(`${DIM$3}正在更新 ${packagesToUpdate.length} 个包...${RESET$3}`);
7485
7486
  for (const pkg of packagesToUpdate) try {
7486
7487
  const pkgInfo = await api.getPackage(pkg.namespace, pkg.slug);
7487
- const skills = (pkgInfo.data || pkgInfo).skills || [];
7488
+ const info = pkgInfo.data || pkgInfo;
7489
+ if (info.kind === "PLUGIN") throw new Error("这是完整插件,请使用 --as plugin --agent <目标Agent> 更新;不会按技能组合安装。");
7490
+ const skills = info.skills || [];
7488
7491
  console.log(` ${DIM$3}更新 @${pkg.namespace}/${pkg.slug}(${skills.length} 个技能)...${RESET$3}`);
7489
7492
  for (const skill of skills) {
7490
7493
  const skillRef = skill.namespace ? `@${skill.namespace}/${skill.slug}` : skill.slug;
@@ -8134,12 +8137,14 @@ const ALLOWED_FLAGS = {
8134
8137
  callstat: [
8135
8138
  ...F_ENV,
8136
8139
  ...F_RANGE,
8140
+ "--app",
8137
8141
  "--page",
8138
8142
  "--page-size"
8139
8143
  ],
8140
8144
  sqlstat: [
8141
8145
  ...F_ENV,
8142
8146
  ...F_RANGE,
8147
+ "--app",
8143
8148
  "--page",
8144
8149
  "--page-size"
8145
8150
  ],
@@ -8184,6 +8189,16 @@ const ALLOWED_FLAGS = {
8184
8189
  "--timeout"
8185
8190
  ]
8186
8191
  };
8192
+ const SUBCOMMAND_HINTS = {
8193
+ "db-tenant-locate": "租户定位在 db 命令组:bfi db tenant-locate <租户编码>",
8194
+ "tenant-locate": "租户定位在 db 命令组:bfi db tenant-locate <租户编码>",
8195
+ "tool-start": "call 透传路由:bfi ymc call action/tool/start --data '{\"type\":1,...}'",
8196
+ "eventHistograms": "call 透传路由:bfi ymc call eventHistograms ...(无同名子命令)",
8197
+ "exceptionDetail": "call 透传路由:bfi ymc call exceptionDetail ...(无同名子命令)",
8198
+ "app-list": "该端点无查询参数且不支持按服务过滤(列表型导航端点);appId 用推导式 <env>-<appCode>",
8199
+ "warningSqlstat": "用子命令:bfi ymc sqlstat",
8200
+ "warningCallstat": "用子命令:bfi ymc callstat"
8201
+ };
8187
8202
  function assertKnownFlags(sub, rest) {
8188
8203
  const allowed = /* @__PURE__ */ new Set([...COMMON_FLAGS, ...ALLOWED_FLAGS[sub] ?? []]);
8189
8204
  for (const a of rest) {
@@ -8244,6 +8259,21 @@ function rowsSummary(label) {
8244
8259
  return `✓ ${label}: ${Array.isArray(rows) ? rows.length : 0} 条`;
8245
8260
  };
8246
8261
  }
8262
+ function aggSummary(label) {
8263
+ return (data) => {
8264
+ const d = data;
8265
+ const rows = d.rows ?? d;
8266
+ if (!Array.isArray(rows) || !rows.length) return `✓ ${label}: 0 组`;
8267
+ const countCol = [
8268
+ "count",
8269
+ "cnt",
8270
+ "COUNT(*)",
8271
+ "c"
8272
+ ].find((k) => rows.every((r) => r[k] !== void 0));
8273
+ const total = countCol ? rows.reduce((acc, r) => acc + (Number(r[countCol]) || 0), 0) : NaN;
8274
+ return Number.isFinite(total) ? `✓ ${label}: ${rows.length} 组 / 总计 ${total} 条` : `✓ ${label}: ${rows.length} 组`;
8275
+ };
8276
+ }
8247
8277
  async function runYmc(args, globalOpts) {
8248
8278
  const sub = args[0];
8249
8279
  const rest = args.slice(1);
@@ -8393,7 +8423,7 @@ async function runYmc(args, globalOpts) {
8393
8423
  ...requireEnv(rest),
8394
8424
  ...timeOf(rest, false),
8395
8425
  line: Number(opt(rest, "--line") || 50)
8396
- }), jsonMode, rowsSummary(`SLS 全文检索 "${kw}"`));
8426
+ }), jsonMode, aggSummary(`SLS 全文检索 "${kw}"`));
8397
8427
  break;
8398
8428
  }
8399
8429
  case "sls-logs": {
@@ -8713,6 +8743,7 @@ async function runYmc(args, globalOpts) {
8713
8743
  emit(await api.call("ymc/warningCallstat", {
8714
8744
  ...requireEnv(rest),
8715
8745
  ...timeOpts(rest),
8746
+ curAppCode: opt(rest, "--app") || "",
8716
8747
  page: Number(opt(rest, "--page") || 1),
8717
8748
  pageSize: Number(opt(rest, "--page-size") || 20)
8718
8749
  }), jsonMode, rowsSummary("慢调用统计"));
@@ -8721,6 +8752,7 @@ async function runYmc(args, globalOpts) {
8721
8752
  emit(await api.call("ymc/warningSqlstat", {
8722
8753
  ...requireEnv(rest),
8723
8754
  ...timeOpts(rest),
8755
+ curAppCode: opt(rest, "--app") || "",
8724
8756
  page: Number(opt(rest, "--page") || 1),
8725
8757
  pageSize: Number(opt(rest, "--page-size") || 20)
8726
8758
  }), jsonMode, rowsSummary("慢 SQL 统计"));
@@ -8836,10 +8868,13 @@ async function runYmc(args, globalOpts) {
8836
8868
  }, timeoutMs), jsonMode);
8837
8869
  break;
8838
8870
  }
8839
- default:
8871
+ default: {
8840
8872
  console.error(`未知子命令:${sub}`);
8873
+ const hint = SUBCOMMAND_HINTS[sub];
8874
+ if (hint) console.error(`提示:${hint}`);
8841
8875
  printHelp$3();
8842
8876
  process.exit(1);
8877
+ }
8843
8878
  }
8844
8879
  }
8845
8880
  const HELP = {
@@ -8859,7 +8894,9 @@ const HELP = {
8859
8894
  chain: `bfi ymc chain <trace_id> -e <env> [--ts <毫秒>] [--json]
8860
8895
  原子源:warningChain 全量 Span 链路(耗时分解/调用树)。trace 三源合一的链路通道单独调用。`,
8861
8896
  "sls-logs": `bfi ymc sls-logs <trace_id> -e <env> [--ts <毫秒>] [--json]
8862
- 原子源:SLS 业务日志(异常全文/堆栈)。按 traceid 索引检索。`,
8897
+ 原子源:SLS 业务日志(异常全文/堆栈)。按 traceid 索引检索。
8898
+ --json 结构:data 为对象(非数组)——日志行在 data.rows,计数在 data.count。
8899
+ 直接把 data 当数组迭代会得到 0 条(2026-10-16 实战教训)。`,
8863
8900
  "sls-raw": `bfi ymc sls-raw "<查询>" -e <env> [-t <time> | --start-time <s> --end-time <e> | --days N] [--line N] [--json]
8864
8901
  SLS 原始查询(阿里云 SLS 完整语法透传:索引检索 + | SELECT 分析语句)。
8865
8902
  参数:
@@ -9054,6 +9091,9 @@ function printHelp$2() {
9054
9091
  bfi event stat <dc> <date> [metric_type] [--page N] [--size N]
9055
9092
  date: yyyy-MM-dd 单日;metric_type: enter|fail|response_time_4s_|response_time_2s_4s|body_size_512k_1m|body_size_1m_
9056
9093
  bfi event record <dc> <date> --source-id S --event-type ET --node-code N --ds-code DS [--record-status fail] [--record-id ID] [--tenant-code T] [--size ≤20]
9094
+ bfi event dc-scan <date> [--event-type ET] [--node-code N]
9095
+ 全 DC 矩阵扫描:C1-C4 逐数据中心查 fail 统计,本事件命中高亮。
9096
+ ⚠️ monitor fail(不带 DC)存在数据中心盲区(实测漏 C3)——失败现场定位必须用本命令
9057
9097
  bfi event content <dc> <date> --event-id E --ds-code DS --tenant-code T
9058
9098
  bfi event dclist <dc>
9059
9099
  bfi event analyze-alert <dc> <date> --event-type ET --node-code N [--source-id S] [--tenant-code T]
@@ -9117,6 +9157,74 @@ async function runEvent(args, globalOpts) {
9117
9157
  });
9118
9158
  return;
9119
9159
  }
9160
+ if (sub === "dc-scan") {
9161
+ const date = reqs[0] ?? missing("<date>");
9162
+ const eventType = str("--event-type") ?? "";
9163
+ const nodeCode = str("--node-code") ?? "";
9164
+ const dcs = [
9165
+ "core1",
9166
+ "core2",
9167
+ "core3",
9168
+ "core4"
9169
+ ];
9170
+ const statOnce = async (dc) => {
9171
+ return (await api.call("event/stat", {
9172
+ dc,
9173
+ date,
9174
+ metric_type: "fail",
9175
+ page: 0,
9176
+ size: 1e3
9177
+ }))?.data?.data?.content ?? [];
9178
+ };
9179
+ const results = await Promise.all(dcs.map(async (dc) => {
9180
+ try {
9181
+ let content = await statOnce(dc);
9182
+ if (!content.length) content = await statOnce(dc);
9183
+ const hit = eventType ? content.filter((x) => x.eventType === eventType && (!nodeCode || x.nodeCode === nodeCode)) : content;
9184
+ return {
9185
+ dc,
9186
+ rows: hit,
9187
+ failNum: hit.reduce((acc, x) => acc + Number(x.num ?? 0), 0),
9188
+ total: content.length
9189
+ };
9190
+ } catch (e) {
9191
+ return {
9192
+ dc,
9193
+ rows: [],
9194
+ failNum: 0,
9195
+ total: 0,
9196
+ error: e instanceof Error ? e.message : String(e)
9197
+ };
9198
+ }
9199
+ }));
9200
+ if (jsonMode) {
9201
+ console.log(JSON.stringify({
9202
+ code: 0,
9203
+ msg: "success",
9204
+ data: {
9205
+ date,
9206
+ event_type: eventType,
9207
+ node_code: nodeCode,
9208
+ results
9209
+ }
9210
+ }, null, 2));
9211
+ return;
9212
+ }
9213
+ const lines = [`全 DC fail 矩阵(${date}${eventType ? ` · ${eventType}${nodeCode ? ` / ${nodeCode}` : ""}` : ""}):`, "DC 失败数 失败率 判定"];
9214
+ for (const r of results) {
9215
+ if (r.error) {
9216
+ lines.push(`${r.dc.padEnd(8)} 通道错误:${r.error}`);
9217
+ continue;
9218
+ }
9219
+ const rate = r.rows.length ? String(r.rows[0].failRate ?? "-") : "-";
9220
+ const verdict = r.total === 0 ? "✓ 无记录(排除证据)" : r.failNum === 0 ? "✓ 有流量零失败" : `🔴 失败现场(${r.rows.length} 个监听行)`;
9221
+ lines.push(`${r.dc.padEnd(8)} ${String(r.failNum).padEnd(8)} ${rate.padEnd(11)} ${verdict}`);
9222
+ }
9223
+ const hot = results.filter((r) => r.failNum > 0).map((r) => r.dc);
9224
+ lines.push(hot.length ? `→ 失败现场:${hot.join(" / ")}(对命中 DC 逐一 event analyze-alert;零命中 DC 是报告的排除证据)` : "→ 全 DC 无失败记录");
9225
+ console.log(lines.join("\n"));
9226
+ return;
9227
+ }
9120
9228
  if (sub === "monitor") {
9121
9229
  const metric = reqs[0];
9122
9230
  if (!metric || !METRICS.includes(metric)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bfi-cli",
3
- "version": "0.1.93",
3
+ "version": "0.1.95",
4
4
  "description": "用友大业财BFI统一命令行工具,支持技能,技能包,元数据,UI元数据等",
5
5
  "type": "module",
6
6
  "bin": {