bfi-cli 0.1.74 → 0.1.76

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 +491 -4
  2. package/package.json +4 -3
package/dist/cli.mjs CHANGED
@@ -6,6 +6,7 @@ import { require_picocolors } from "./_chunks/libs/picocolors.mjs";
6
6
  import { addSkillToLocalLock, addSkillToLock, computeSkillFolderHash, discoverSkills, dismissPrompt, filterSkills, getAllLockedSkills, getGitHubToken, getLastSelectedAgents, getSkillDisplayName, getSkillFromLock, isPromptDismissed, parseFrontmatter, parseSkillMd, readLocalLock, readSkillLock, removeSkillFromLock, sanitizeMetadata, saveSelectedAgents, stripTerminalEscapes } from "./_chunks/skills.mjs";
7
7
  import { esm_default } from "./_chunks/libs/simple-git.mjs";
8
8
  import { EVE_SUBAGENTS_DIR, agents, detectInstalledAgents, getEveSubagents, getNonUniversalAgents, getUniversalAgents, getVisibleUniversalAgents, isUniversalAgent } from "./_chunks/agents.mjs";
9
+ import { createRequire } from "node:module";
9
10
  import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "fs";
10
11
  import { basename, dirname, extname, isAbsolute, join, normalize, relative, resolve, sep } from "path";
11
12
  import { fileURLToPath } from "url";
@@ -8239,7 +8240,7 @@ async function runYmc(args, globalOpts) {
8239
8240
  const token = globalOpts.token;
8240
8241
  const api = new YmcApi(token ?? null, void 0, dryRun);
8241
8242
  if (!sub || sub === "--help" || sub === "-h") {
8242
- printHelp();
8243
+ printHelp$2();
8243
8244
  return;
8244
8245
  }
8245
8246
  if (sub === "help") {
@@ -8805,7 +8806,7 @@ async function runYmc(args, globalOpts) {
8805
8806
  }
8806
8807
  default:
8807
8808
  console.error(`未知子命令:${sub}`);
8808
- printHelp();
8809
+ printHelp$2();
8809
8810
  process.exit(1);
8810
8811
  }
8811
8812
  }
@@ -8936,11 +8937,11 @@ function printCommandHelp(cmd) {
8936
8937
  if (h) console.log(h);
8937
8938
  else {
8938
8939
  console.error(`未知子命令:${cmd}`);
8939
- printHelp();
8940
+ printHelp$2();
8940
8941
  process.exit(1);
8941
8942
  }
8942
8943
  }
8943
- function printHelp() {
8944
+ function printHelp$2() {
8944
8945
  console.log(`
8945
8946
  bfi ymc — 观测查询(v2 API:YMC/Kibana/SLS/trace)
8946
8947
 
@@ -8984,6 +8985,486 @@ bfi ymc — 观测查询(v2 API:YMC/Kibana/SLS/trace)
8984
8985
  详细帮助:bfi ymc help <命令>(或 bfi ymc <命令> -h)——含参数说明/默认值/示例。
8985
8986
  `);
8986
8987
  }
8988
+ const METRICS = [
8989
+ "entry",
8990
+ "fail",
8991
+ "costtime",
8992
+ "content"
8993
+ ];
8994
+ const RESOURCES = [
8995
+ "type-doc",
8996
+ "type-info",
8997
+ "listener-doc"
8998
+ ];
8999
+ function printHelp$1() {
9000
+ console.log(`bfi event — YonBIP 事件中心取数(诊断编排见 bfi-diagnose-event 技能)
9001
+
9002
+ 用法:
9003
+ bfi event monitor <metric> [过滤参数]
9004
+ metric: entry|fail|costtime|content
9005
+ 过滤: --log-date D --source-id S --event-type-code ET --node-code N --datacenter DC --size N
9006
+ bfi event master <resource> [action[:target]]
9007
+ resource: type-doc|type-info|listener-doc
9008
+ action[:target]:
9009
+ list | code:<编码> | id:<ID> | latest:<源ID,类型编码> | source-type:<源ID,类型编码>
9010
+ source-node:<源ID,节点编码> | event-type:<类型编码> | micro-service:<微服务编码>
9011
+ bfi event stat <dc> <date> [metric_type] [--page N] [--size N]
9012
+ date: yyyy-MM-dd 单日;metric_type: enter|fail|response_time_4s_|response_time_2s_4s|body_size_512k_1m|body_size_1m_
9013
+ 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]
9014
+ bfi event content <dc> <date> --event-id E --ds-code DS --tenant-code T
9015
+ bfi event dclist <dc>
9016
+ 全局: --json / --dry-run / --token <tok>
9017
+
9018
+ 示例:
9019
+ bfi event master type-doc code:PC_PRODUCT_STOP
9020
+ bfi event dclist core1
9021
+ bfi event stat core1 2026-09-14 fail
9022
+ bfi event record core1 2026-09-14 --source-id MULILANG --event-type MULILANGCHANGE --node-code N --ds-code YonBip_core1_2_service`);
9023
+ }
9024
+ function missing(name) {
9025
+ console.error(`缺少参数 ${name}。运行 bfi event --help 查看用法。`);
9026
+ process.exit(1);
9027
+ }
9028
+ async function runEvent(args, globalOpts) {
9029
+ const sub = args[0];
9030
+ const rest = args.slice(1);
9031
+ if (!sub || sub === "--help" || sub === "-h" || flag(rest, "--help") || flag(rest, "-h")) {
9032
+ printHelp$1();
9033
+ return;
9034
+ }
9035
+ const jsonMode = flag(rest, "--json");
9036
+ const dryRun = flag(rest, "--dry-run");
9037
+ const api = new YmcApi(globalOpts.token ?? null, void 0, dryRun);
9038
+ if (!dryRun && !api.token) {
9039
+ console.error("未认证。先运行:bfi auth login --token <sk-token>(或传 --token)");
9040
+ process.exit(1);
9041
+ }
9042
+ const reqs = positionals(rest);
9043
+ const call = async (path, body) => emit(await api.call(path, body), jsonMode);
9044
+ const num = (name, dflt) => Number(opt(rest, name) ?? dflt);
9045
+ const str = (name) => opt(rest, name);
9046
+ if (sub === "monitor") {
9047
+ const metric = reqs[0];
9048
+ if (!metric || !METRICS.includes(metric)) {
9049
+ console.error("缺少/无效 metric(entry|fail|costtime|content)。例:bfi event monitor fail");
9050
+ process.exit(1);
9051
+ }
9052
+ const body = {
9053
+ metric,
9054
+ size: num("--size", 200)
9055
+ };
9056
+ for (const [cliName, key] of Object.entries({
9057
+ "--log-date": "log_date",
9058
+ "--source-id": "source_id",
9059
+ "--event-type-code": "event_type_code",
9060
+ "--node-code": "node_code",
9061
+ "--datacenter": "datacenter"
9062
+ })) {
9063
+ const v = str(cliName);
9064
+ if (v) body[key] = v;
9065
+ }
9066
+ await call("event/monitor", body);
9067
+ return;
9068
+ }
9069
+ if (sub === "master") {
9070
+ const resource = reqs[0];
9071
+ if (!resource || !RESOURCES.includes(resource)) {
9072
+ console.error("缺少/无效 resource(type-doc|type-info|listener-doc)。例:bfi event master type-doc code:PC_PRODUCT_STOP");
9073
+ process.exit(1);
9074
+ }
9075
+ let action = "list";
9076
+ let target;
9077
+ const second = reqs[1];
9078
+ if (second && second.includes(":")) {
9079
+ const i = second.indexOf(":");
9080
+ action = second.slice(0, i);
9081
+ target = second.slice(i + 1);
9082
+ } else if (second) {
9083
+ action = "code";
9084
+ target = second;
9085
+ }
9086
+ await call("event/master", {
9087
+ resource,
9088
+ action,
9089
+ target,
9090
+ query: {
9091
+ page: num("--page", 0),
9092
+ size: num("--size", 200)
9093
+ }
9094
+ });
9095
+ return;
9096
+ }
9097
+ if (sub === "stat") {
9098
+ const dc = reqs[0];
9099
+ const date = reqs[1];
9100
+ if (!dc || !date) {
9101
+ console.error("用法: bfi event stat <dc> <date> [metric_type]");
9102
+ process.exit(1);
9103
+ }
9104
+ await call("event/stat", {
9105
+ dc,
9106
+ date,
9107
+ metric_type: reqs[2] ?? "fail",
9108
+ page: num("--page", 0),
9109
+ size: num("--size", 1e3)
9110
+ });
9111
+ return;
9112
+ }
9113
+ if (sub === "record") {
9114
+ const dc = reqs[0];
9115
+ const date = reqs[1];
9116
+ if (!dc || !date) {
9117
+ console.error("用法: bfi event record <dc> <date> --source-id S --event-type ET --node-code N --ds-code DS");
9118
+ process.exit(1);
9119
+ }
9120
+ const sourceId = str("--source-id");
9121
+ const eventType = str("--event-type");
9122
+ const nodeCode = str("--node-code");
9123
+ const dsCode = str("--ds-code");
9124
+ if (!sourceId) missing("--source-id");
9125
+ if (!eventType) missing("--event-type");
9126
+ if (!nodeCode) missing("--node-code");
9127
+ if (!dsCode) missing("--ds-code");
9128
+ const body = {
9129
+ dc,
9130
+ date,
9131
+ source_id: sourceId,
9132
+ event_type: eventType,
9133
+ node_code: nodeCode,
9134
+ ds_code: dsCode,
9135
+ record_status: str("--record-status") ?? "fail",
9136
+ size: num("--size", 20)
9137
+ };
9138
+ const rid = str("--record-id");
9139
+ if (rid) body.record_id = rid;
9140
+ const tc = str("--tenant-code");
9141
+ if (tc) body.tenant_code = tc;
9142
+ await call("event/record", body);
9143
+ return;
9144
+ }
9145
+ if (sub === "content") {
9146
+ const dc = reqs[0];
9147
+ const date = reqs[1];
9148
+ const eventId = str("--event-id");
9149
+ const dsCode = str("--ds-code");
9150
+ const tenantCode = str("--tenant-code");
9151
+ if (!dc || !date || !eventId || !dsCode || !tenantCode) {
9152
+ console.error("用法: bfi event content <dc> <date> --event-id E --ds-code DS --tenant-code T");
9153
+ process.exit(1);
9154
+ }
9155
+ await call("event/content", {
9156
+ dc,
9157
+ date,
9158
+ event_id: eventId,
9159
+ ds_code: dsCode,
9160
+ tenant_code: tenantCode
9161
+ });
9162
+ return;
9163
+ }
9164
+ if (sub === "dclist") {
9165
+ const dc = reqs[0];
9166
+ if (!dc) {
9167
+ console.error("用法: bfi event dclist <dc>");
9168
+ process.exit(1);
9169
+ }
9170
+ await call("event/dclist", { dc });
9171
+ return;
9172
+ }
9173
+ console.error(`未知子命令: ${sub}。运行 bfi event --help`);
9174
+ process.exit(1);
9175
+ }
9176
+ function loadSourceMapConsumer() {
9177
+ return createRequire(import.meta.url)("source-map").SourceMapConsumer;
9178
+ }
9179
+ function parseErrorLog(log) {
9180
+ const out = {
9181
+ mdf: null,
9182
+ errorType: "",
9183
+ errorMessage: "",
9184
+ frames: []
9185
+ };
9186
+ const code = log.match(/\b(E\d{5})\b/);
9187
+ const kind = log.match(/\[([^\]]{2,20}错误)\]/);
9188
+ const diwork = log.match(/diworkCode:\s*([^\]]+)\]/);
9189
+ const method = log.match(/\[([a-zA-Z_$][\w$]*(?:\.[\w$$.]+)+)\]\s*:/);
9190
+ const event = log.match(/execute\[([^\]]+)\]/);
9191
+ const bill = log.match(/\[([^\]]+)\]\s*\[[^\]]{2,20}错误\]/);
9192
+ if (code && log.includes("【MDF】")) out.mdf = {
9193
+ code: code[1],
9194
+ billName: bill?.[1] ?? "",
9195
+ errorKind: kind?.[1] ?? "",
9196
+ diworkCode: diwork?.[1]?.trim() ?? "",
9197
+ method: method?.[1] ?? "",
9198
+ event: event?.[1] ?? ""
9199
+ };
9200
+ const errMatches = [...log.matchAll(/(TypeError|ReferenceError|SyntaxError|RangeError)\s*:\s*([^\n\r]+)/g)];
9201
+ if (errMatches.length > 0) {
9202
+ const last = errMatches[errMatches.length - 1];
9203
+ out.errorType = last[1];
9204
+ out.errorMessage = `${last[1]}: ${last[2].trim()}`;
9205
+ }
9206
+ for (const m of log.matchAll(/(https?:\/\/[^\s()"']+?\.min\.js):(\d+):(\d+)/g)) out.frames.push({
9207
+ index: out.frames.length,
9208
+ url: m[1],
9209
+ line: Number(m[2]),
9210
+ col: Number(m[3])
9211
+ });
9212
+ return out;
9213
+ }
9214
+ function pickFrame(frames) {
9215
+ const ext = frames.findIndex((f) => /\/extend\.\d+\.[a-f0-9]+/.test(f.url));
9216
+ return ext !== -1 ? ext : 0;
9217
+ }
9218
+ function resolveSourceMappingUrl(jsUrl, comment) {
9219
+ if (/^https?:\/\//.test(comment)) return comment;
9220
+ return `${jsUrl.replace(/\/[^/]*$/, "")}/${comment.replace(/^\.?\//, "")}`;
9221
+ }
9222
+ function webpackSourceInfo(source) {
9223
+ const m = source.match(/^webpack:\/\/([^/]+)\/(.+)$/);
9224
+ if (!m) return {
9225
+ repo: "",
9226
+ filePath: source.replace(/^webpack:\/\//, "")
9227
+ };
9228
+ return {
9229
+ repo: m[1],
9230
+ filePath: m[2].replace(/^\.\//, "")
9231
+ };
9232
+ }
9233
+ function extractContext(content, line, ctx) {
9234
+ const lines = content.split("\n");
9235
+ const start = Math.max(1, line - ctx);
9236
+ const end = Math.min(lines.length, line + ctx);
9237
+ const width = String(end).length;
9238
+ const out = [];
9239
+ for (let i = start; i <= end; i++) {
9240
+ const mark = i === line ? " --> " : " ";
9241
+ out.push(`${mark}${String(i).padStart(width)} | ${lines[i - 1]}`);
9242
+ }
9243
+ return out.join("\n");
9244
+ }
9245
+ const UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36";
9246
+ function httpGet(url, redirects = 0) {
9247
+ return new Promise((resolve, reject) => {
9248
+ if (redirects > 5) {
9249
+ reject(/* @__PURE__ */ new Error(`重定向超过 5 次: ${url}`));
9250
+ return;
9251
+ }
9252
+ const req = (url.startsWith("https://") ? https : http).get(url, {
9253
+ headers: {
9254
+ "user-agent": UA,
9255
+ accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
9256
+ cookie: "debug=true"
9257
+ },
9258
+ timeout: 6e4
9259
+ }, (res) => {
9260
+ const status = res.statusCode ?? 0;
9261
+ const loc = res.headers.location;
9262
+ if (status >= 300 && status < 400 && loc) {
9263
+ res.resume();
9264
+ httpGet(new URL(loc, url).toString(), redirects + 1).then(resolve, reject);
9265
+ return;
9266
+ }
9267
+ const chunks = [];
9268
+ res.on("data", (c) => chunks.push(c));
9269
+ res.on("end", () => resolve({
9270
+ status,
9271
+ body: Buffer.concat(chunks)
9272
+ }));
9273
+ });
9274
+ req.on("timeout", () => req.destroy(/* @__PURE__ */ new Error(`下载超时(60s): ${url}`)));
9275
+ req.on("error", reject);
9276
+ });
9277
+ }
9278
+ async function downloadTo(url, file, what) {
9279
+ const { status, body } = await httpGet(url);
9280
+ if (status !== 200 || body.length === 0) throw new Error(`${what}下载失败(HTTP ${status}): ${url}\n若 sourcemap 被剥离,降级:按 JS 文件名走 bfi-code-search 内容搜索定位`);
9281
+ fs.writeFileSync(file, body);
9282
+ return body.length;
9283
+ }
9284
+ function cacheDirFor(jsUrl, root) {
9285
+ const u = new URL(jsUrl);
9286
+ return path$1.join(root || path$1.join(os$1.homedir(), ".bfi", "fe-locate", "cache"), u.host, path$1.dirname(u.pathname).replace(/^\/|\/$/g, ""));
9287
+ }
9288
+ function readStdin() {
9289
+ return new Promise((resolve, reject) => {
9290
+ let data = "";
9291
+ process.stdin.setEncoding("utf-8");
9292
+ process.stdin.on("data", (c) => {
9293
+ data += c;
9294
+ });
9295
+ process.stdin.on("end", () => resolve(data));
9296
+ process.stdin.on("error", reject);
9297
+ });
9298
+ }
9299
+ function parseSourceMappingComment(jsContent) {
9300
+ const m = jsContent.slice(-3e3).match(/\/\/# sourceMappingURL=(\S+)/);
9301
+ if (!m) throw new Error("JS 文件末尾未找到 //# sourceMappingURL= 注释(构建未产出 sourcemap)");
9302
+ return m[1];
9303
+ }
9304
+ async function locateInMap(mapFile, line, col) {
9305
+ const consumer = await new (loadSourceMapConsumer())(JSON.parse(fs.readFileSync(mapFile, "utf-8")));
9306
+ try {
9307
+ let pos = consumer.originalPositionFor({
9308
+ line,
9309
+ column: Math.max(0, col - 1)
9310
+ });
9311
+ if (!pos?.source) pos = consumer.originalPositionFor({
9312
+ line,
9313
+ column: col
9314
+ });
9315
+ if (!pos?.source) throw new Error(`sourcemap 中无该位置映射(line=${line}, col=${col})——JS 与 map 版本可能不一致`);
9316
+ const content = pos.source ? consumer.sourceContentFor(pos.source, true) : null;
9317
+ return {
9318
+ source: pos.source,
9319
+ line: pos.line ?? 0,
9320
+ column: pos.column ?? 0,
9321
+ name: pos.name ?? "",
9322
+ content
9323
+ };
9324
+ } finally {
9325
+ consumer.destroy?.();
9326
+ }
9327
+ }
9328
+ function printHelp() {
9329
+ console.log(`bfi fe-locate — 前端错误定位(sourcemap 还原)
9330
+
9331
+ 用法:
9332
+ bfi fe-locate "<错误日志>" # 单参数(控制台报错栈粘贴)
9333
+ cat error.log | bfi fe-locate --stdin # 长日志走 stdin(推荐,免转义)
9334
+
9335
+ 选项:
9336
+ --json 结构化 JSON 输出(定位结果+全部帧+上下文)
9337
+ --stdin 从标准输入读取错误日志
9338
+ --context <N> 源码上下文行数(默认 5)
9339
+ --frame <N> 指定第 N 帧定位(1-based;默认智能选帧:extend 业务帧优先)
9340
+ --no-cache 强制重新下载 JS/sourcemap
9341
+
9342
+ 示例:
9343
+ bfi fe-locate "TypeError: e.get(...).setValue is not a function at y (https://static-dc-core1.yonyoucloud.com/static/mdf/udinghuo/latest/javascripts/extend.23.xxx.min.js:1:100358)"`);
9344
+ }
9345
+ function printResult(r, ctxLines) {
9346
+ const err = r.error;
9347
+ const mdf = r.mdf;
9348
+ console.log("═══ bfi fe-locate — 前端错误定位 ═══\n");
9349
+ if (mdf) console.log(`【MDF 头】${mdf.code} | 单据:${mdf.billName} | 类型:${mdf.errorKind} | diworkCode:${mdf.diworkCode} | 方法:${mdf.method} | 事件:${mdf.event}`);
9350
+ console.log(`【错误】${err.type}: ${err.message}`);
9351
+ const frames = r.frames;
9352
+ const sel = r.selected;
9353
+ console.log(`【选帧】第 ${sel.index} 帧(${sel.reason}): ${path$1.basename(String(sel.url))}:${sel.line}:${sel.col}(共 ${frames.length} 帧)`);
9354
+ console.log(`【压缩 JS】${r.jsUrl}`);
9355
+ const resolved = r.resolved;
9356
+ if (resolved) {
9357
+ console.log("\n── sourcemap 还原 ──");
9358
+ console.log(`原始文件: ${resolved.source}`);
9359
+ const info = resolved.webpack;
9360
+ if (info.repo) console.log(`包内仓库: ${info.repo}(注:包内仓库名≠GitLab 仓库名,远程定位按文件名搜索)`);
9361
+ console.log(`文件路径: ${info.filePath}`);
9362
+ console.log(`位置: 行 ${resolved.line}, 列 ${resolved.column}`);
9363
+ if (resolved.name) console.log(`函数名: ${resolved.name}`);
9364
+ if (ctxLines) {
9365
+ console.log(`\n── 源码上下文(sourcemap sourcesContent,±${r.context} 行)──`);
9366
+ console.log(ctxLines);
9367
+ }
9368
+ console.log("\n── 下一步 ──");
9369
+ console.log(`· 远程仓库: SourceGraph 精确文件名搜索 "${path$1.basename(info.filePath)}"(bfi-code-search)`);
9370
+ console.log("· 归因分析: 对照 bfi-diagnose-engine references/fe-error-attribution.md");
9371
+ console.log("· 数据格式类根因(接口返回变化)→ deep 档联动 bfi ymc ingress/trace 取证");
9372
+ }
9373
+ console.log(`\n缓存: ${r.cacheDir}`);
9374
+ }
9375
+ async function runFeLocate(args, globalOpts) {
9376
+ if (!args.length || flag(args, "--help") || flag(args, "-h")) {
9377
+ printHelp();
9378
+ return;
9379
+ }
9380
+ const jsonMode = flag(args, "--json");
9381
+ const noCache = flag(args, "--no-cache");
9382
+ const ctxN = Number(opt(args, "--context") ?? 5) || 5;
9383
+ const frameN = opt(args, "--frame");
9384
+ let log = positionals(args)[0] ?? "";
9385
+ if (!log && flag(args, "--stdin")) log = await readStdin();
9386
+ if (!log && !process.stdin.isTTY) log = await readStdin();
9387
+ if (!log.trim()) {
9388
+ console.error("缺少错误日志参数。粘贴控制台报错栈,或 cat error.log | bfi fe-locate --stdin");
9389
+ process.exit(1);
9390
+ }
9391
+ const parsed = parseErrorLog(log);
9392
+ if (parsed.frames.length === 0) {
9393
+ console.error("未在日志中找到压缩 JS 帧(https://...xxx.min.js:行:列)。请确认粘贴的是浏览器控制台报错栈。");
9394
+ process.exit(1);
9395
+ }
9396
+ const selIdx = frameN ? Math.max(0, Number(frameN) - 1) : pickFrame(parsed.frames);
9397
+ const selReason = frameN ? "--frame 指定" : parsed.frames[selIdx] && /\/extend\.\d+\./.test(parsed.frames[selIdx].url) ? "extend 业务帧优先" : "默认第一帧";
9398
+ if (selIdx >= parsed.frames.length) {
9399
+ console.error(`--frame ${frameN} 超出范围(共 ${parsed.frames.length} 帧)`);
9400
+ process.exit(1);
9401
+ }
9402
+ const frame = parsed.frames[selIdx];
9403
+ const cDir = cacheDirFor(frame.url, globalOpts["fe-cache-dir"]);
9404
+ fs.mkdirSync(cDir, { recursive: true });
9405
+ const jsFile = path$1.join(cDir, "error.min.js");
9406
+ const mapFile = path$1.join(cDir, "error.min.js.map");
9407
+ const metaFile = path$1.join(cDir, "meta.json");
9408
+ let sourceMappingURL = "";
9409
+ if (!noCache && fs.existsSync(metaFile)) try {
9410
+ sourceMappingURL = JSON.parse(fs.readFileSync(metaFile, "utf-8")).sourceMappingUrl ?? "";
9411
+ } catch {}
9412
+ if (sourceMappingURL && fs.existsSync(mapFile)) console.error(`[缓存命中] ${cDir}`);
9413
+ else {
9414
+ const jsSize = await downloadTo(frame.url, jsFile, "压缩 JS");
9415
+ console.error(`[下载] 压缩 JS(${(jsSize / 1024).toFixed(0)} KB)`);
9416
+ const comment = parseSourceMappingComment(fs.readFileSync(jsFile, "utf-8"));
9417
+ sourceMappingURL = resolveSourceMappingUrl(frame.url, comment);
9418
+ console.error(`[解析] sourceMappingURL → ${sourceMappingURL}`);
9419
+ const mapSize = await downloadTo(sourceMappingURL, mapFile, "sourcemap");
9420
+ console.error(`[下载] sourcemap(${(mapSize / 1024).toFixed(0)} KB)`);
9421
+ fs.writeFileSync(metaFile, JSON.stringify({
9422
+ jsUrl: frame.url,
9423
+ sourceMappingUrl: sourceMappingURL,
9424
+ downloadedAt: (/* @__PURE__ */ new Date()).toISOString()
9425
+ }, null, 2));
9426
+ }
9427
+ const located = await locateInMap(mapFile, frame.line, frame.col);
9428
+ const webpack = webpackSourceInfo(located.source);
9429
+ const ctxLines = located.content ? extractContext(located.content, located.line, ctxN) : null;
9430
+ const result = {
9431
+ error: {
9432
+ type: parsed.errorType,
9433
+ message: parsed.errorMessage
9434
+ },
9435
+ mdf: parsed.mdf,
9436
+ frames: parsed.frames,
9437
+ selected: {
9438
+ ...frame,
9439
+ index: selIdx + 1,
9440
+ reason: selReason
9441
+ },
9442
+ jsUrl: frame.url,
9443
+ sourceMappingURL,
9444
+ resolved: {
9445
+ source: located.source,
9446
+ webpack,
9447
+ line: located.line,
9448
+ column: located.column,
9449
+ name: located.name
9450
+ },
9451
+ context: ctxN,
9452
+ contextText: ctxLines,
9453
+ cacheDir: cDir,
9454
+ locatedAt: (/* @__PURE__ */ new Date()).toISOString()
9455
+ };
9456
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14);
9457
+ const runDir = path$1.join(cDir, stamp);
9458
+ fs.mkdirSync(runDir, { recursive: true });
9459
+ fs.writeFileSync(path$1.join(runDir, "error.log"), log);
9460
+ fs.writeFileSync(path$1.join(runDir, "result.json"), JSON.stringify(result, null, 2));
9461
+ if (jsonMode) {
9462
+ console.log(JSON.stringify(result, null, 2));
9463
+ return;
9464
+ }
9465
+ printResult(result, ctxLines);
9466
+ console.log(`\n结果已存: ${path$1.join(runDir, "result.json")}`);
9467
+ }
8987
9468
  const __dirname = dirname(fileURLToPath(import.meta.url));
8988
9469
  function getVersion() {
8989
9470
  try {
@@ -9374,6 +9855,12 @@ async function main() {
9374
9855
  case "ymc":
9375
9856
  await runYmc(restArgs, globalOpts);
9376
9857
  break;
9858
+ case "fe-locate":
9859
+ await runFeLocate(restArgs, globalOpts);
9860
+ break;
9861
+ case "event":
9862
+ await runEvent(restArgs, globalOpts);
9863
+ break;
9377
9864
  case "auth":
9378
9865
  if (restArgs[0] === "logout") await runLogout();
9379
9866
  else if (restArgs[0] === "whoami") await runWhoami(globalOpts);
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "bfi-cli",
3
- "version": "0.1.74",
3
+ "version": "0.1.76",
4
4
  "description": "用友大业财BFI统一命令行工具,支持技能,技能包,元数据,UI元数据等",
5
5
  "type": "module",
6
6
  "bin": {
7
- "bfi": "./bin/bfi.js",
8
- "bfi-db-mcp": "./bin/bfi-db-mcp.js"
7
+ "bfi": "bin/bfi.js",
8
+ "bfi-db-mcp": "bin/bfi-db-mcp.js"
9
9
  },
10
10
  "files": [
11
11
  "dist",
@@ -50,6 +50,7 @@
50
50
  "@modelcontextprotocol/sdk": "^1.12.0",
51
51
  "@vercel/detect-agent": "^1.2.3",
52
52
  "mysql2": "^3.14.0",
53
+ "source-map": "^0.7.4",
53
54
  "yaml": "^2.8.3",
54
55
  "zod": "^3.25.0"
55
56
  }