@sfmc-bds/cli 0.5.3 → 0.7.0

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 (39) hide show
  1. package/dist/logs.d.ts +21 -0
  2. package/dist/logs.d.ts.map +1 -1
  3. package/dist/logs.js +67 -1
  4. package/dist/logs.js.map +2 -2
  5. package/dist/management/deployment.js +1 -1
  6. package/dist/management/deployment.js.map +2 -2
  7. package/dist/management/domain.d.ts +46 -2
  8. package/dist/management/domain.d.ts.map +1 -1
  9. package/dist/management/domain.js +112 -26
  10. package/dist/management/domain.js.map +2 -2
  11. package/dist/management/maintenance.d.ts +14 -1
  12. package/dist/management/maintenance.d.ts.map +1 -1
  13. package/dist/management/maintenance.js +46 -38
  14. package/dist/management/maintenance.js.map +2 -2
  15. package/dist/management/pack-import.d.ts +29 -0
  16. package/dist/management/pack-import.d.ts.map +1 -0
  17. package/dist/management/pack-import.js +108 -0
  18. package/dist/management/pack-import.js.map +7 -0
  19. package/dist/management/platform-worker.js +5 -6
  20. package/dist/management/platform-worker.js.map +2 -2
  21. package/dist/management/server.d.ts.map +1 -1
  22. package/dist/management/server.js +113 -33
  23. package/dist/management/server.js.map +2 -2
  24. package/dist/management/tasks.js +1 -1
  25. package/dist/management/tasks.js.map +2 -2
  26. package/dist/registry.d.ts +11 -5
  27. package/dist/registry.d.ts.map +1 -1
  28. package/dist/registry.js +35 -11
  29. package/dist/registry.js.map +2 -2
  30. package/dist/services.d.ts +2 -1
  31. package/dist/services.d.ts.map +1 -1
  32. package/dist/services.js +16 -3
  33. package/dist/services.js.map +2 -2
  34. package/dist/world-packs.d.ts +47 -0
  35. package/dist/world-packs.d.ts.map +1 -1
  36. package/dist/world-packs.js +38 -11
  37. package/dist/world-packs.js.map +2 -2
  38. package/package.json +6 -6
  39. package/scripts/module-install/lib/registry-index.mjs +50 -13
package/dist/logs.d.ts CHANGED
@@ -48,12 +48,33 @@ export type ReadDiskLogsOpts = {
48
48
  dir?: string;
49
49
  /** 仅保留时间序最后 N 条;省略则返回全部解析结果 */
50
50
  limit?: number;
51
+ /** 含此时间。设置后读取整个日志文件,不再只取文件末尾 */
52
+ since?: Date;
53
+ /** 含此时间 */
54
+ until?: Date;
55
+ };
56
+ /** 删除落盘日志时的筛选。使用场景:桌面 logs.delete。 */
57
+ export type DeleteDiskLogsOpts = {
58
+ since: Date;
59
+ until: Date;
60
+ source?: string;
61
+ text?: string;
62
+ /** 覆盖日志目录(测试用) */
63
+ dir?: string;
51
64
  };
52
65
  /**
53
66
  * 从 `.sfmc/logs/*.log` 读取并解析为 UnifiedLog(按时间升序)。
54
67
  * CLI 重连后主面板 /logs 的历史权威来源;内存缓冲仅承载本进程订阅到的实时增量。
55
68
  */
56
69
  export declare function readDiskLogs(opts?: ReadDiskLogsOpts): UnifiedLog[];
70
+ /**
71
+ * 按时间范围删除已知日志文件中的行。
72
+ * 使用场景:桌面 logs.delete。符号链接和非文件直接拒绝。
73
+ * 写入前会再读一次期间追加的内容;替换文件的瞬间新写入的行可能留下来。
74
+ */
75
+ export declare function deleteStoredLogs(opts: DeleteDiskLogsOpts): {
76
+ removed: number;
77
+ };
57
78
  export interface SourceMeta {
58
79
  value: string;
59
80
  /** 方括号内固定 3 字符,与 [INF] 对齐 */
@@ -1 +1 @@
1
- {"version":3,"file":"logs.d.ts","sourceRoot":"","sources":["../src/logs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAQL,KAAK,QAAQ,EACb,KAAK,QAAQ,IAAI,cAAc,EAChC,MAAM,oBAAoB,CAAC;AAa5B,MAAM,MAAM,QAAQ,GAAG,cAAc,CAAC;AACtC,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC;AAC/B,MAAM,WAAW,UAAW,SAAQ,QAAQ;CAAG;AAE/C,wDAAwD;AACxD,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAShG,yCAAyC;AACzC,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG/D;AAwBD,yBAAyB;AACzB,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,GAAG,IAAI,CA+C9E;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,GAAG,IAAI,CAEtF;AAED,uBAAuB;AACvB,wBAAgB,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,IAAI,GAAG,MAAM,IAAI,CAE/D;AAED,oDAAoD;AACpD,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,UAAU,EAAE,CAG/F;AAED,qDAAqD;AACrD,wBAAgB,UAAU,IAAI,UAAU,EAAE,CAGzC;AASD,6BAA6B;AAC7B,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAIxD;AAuBD,2BAA2B;AAC3B,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAS/D;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE,SAAS,EAAE,CAAC;IACtB,yCAAyC;IACzC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,GAAE,gBAAqB,GAAG,UAAU,EAAE,CA6CtE;AAMD,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;CAC9B;AAED,eAAO,MAAM,WAAW,EAAE,UAAU,EASnC,CAAC;AASF,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAKtD;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,UAAU,GAAG,QAAQ,CAM3D;AAED,oCAAoC;AACpC,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAEjD;AA+BD,4CAA4C;AAC5C,wBAAgB,SAAS,CAAC,CAAC,EAAE,UAAU,GAAG,MAAM,CAW/C;AAwBD,2CAA2C;AAC3C,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAK9C;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,UAAU,GAAG,MAAM,CAKpD;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAa7D"}
1
+ {"version":3,"file":"logs.d.ts","sourceRoot":"","sources":["../src/logs.ts"],"names":[],"mappings":"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"}
package/dist/logs.js CHANGED
@@ -134,6 +134,9 @@ function readDiskLogs(opts = {}) {
134
134
  const levels = opts.levels ?? [];
135
135
  const sources = opts.sources ?? [];
136
136
  const dir = opts.dir ?? logsDir(ROOT);
137
+ const sinceMs = opts.since?.getTime();
138
+ const untilMs = opts.until?.getTime();
139
+ const ranged = sinceMs !== void 0 || untilMs !== void 0;
137
140
  const fileNames = /* @__PURE__ */ new Set();
138
141
  if (sources.length === 0) {
139
142
  for (const n of DISK_LOG_FILES) fileNames.add(n);
@@ -145,7 +148,7 @@ function readDiskLogs(opts = {}) {
145
148
  const fp = path.join(dir, name.endsWith(".log") ? name : `${name}.log`);
146
149
  let text;
147
150
  try {
148
- if (opts.limit) {
151
+ if (opts.limit && !ranged) {
149
152
  const fd = fs.openSync(fp, "r");
150
153
  try {
151
154
  const size = fs.fstatSync(fd).size;
@@ -165,6 +168,9 @@ function readDiskLogs(opts = {}) {
165
168
  if (!raw) continue;
166
169
  const entry = parseDiskLogLine(raw);
167
170
  if (!entry) continue;
171
+ const time = entry.time.getTime();
172
+ if (sinceMs !== void 0 && time < sinceMs) continue;
173
+ if (untilMs !== void 0 && time > untilMs) continue;
168
174
  if (sources.length && !sources.includes(entry.source)) continue;
169
175
  if (levels.length && !levels.includes(entry.level)) continue;
170
176
  out.push(entry);
@@ -177,6 +183,65 @@ function readDiskLogs(opts = {}) {
177
183
  }
178
184
  return out;
179
185
  }
186
+ function diskLineMatchesRemoval(entry, opts) {
187
+ const time = entry.time.getTime();
188
+ if (time < opts.since.getTime() || time > opts.until.getTime()) return false;
189
+ if (opts.source !== void 0 && entry.source !== opts.source) return false;
190
+ if (opts.text !== void 0 && entry.text !== opts.text) return false;
191
+ return true;
192
+ }
193
+ function filterLogText(text, opts) {
194
+ if (!text) return { text: "", removed: 0 };
195
+ const newline = text.includes("\r\n") ? "\r\n" : "\n";
196
+ const ended = text.endsWith("\n");
197
+ const lines = text.split(/\r?\n/);
198
+ if (ended) lines.pop();
199
+ let removed = 0;
200
+ const kept = [];
201
+ for (const raw of lines) {
202
+ const entry = raw ? parseDiskLogLine(raw) : null;
203
+ if (entry && diskLineMatchesRemoval(entry, opts)) {
204
+ removed++;
205
+ continue;
206
+ }
207
+ kept.push(raw);
208
+ }
209
+ if (!kept.length) return { text: "", removed };
210
+ return { text: kept.join(newline) + (ended ? newline : ""), removed };
211
+ }
212
+ function deleteStoredLogs(opts) {
213
+ if (opts.until.getTime() < opts.since.getTime()) throw new Error("\u7ED3\u675F\u65F6\u95F4\u4E0D\u80FD\u65E9\u4E8E\u5F00\u59CB\u65F6\u95F4");
214
+ const dir = opts.dir ?? logsDir(ROOT);
215
+ let removed = 0;
216
+ for (const name of DISK_LOG_FILES) {
217
+ const file = path.join(dir, `${name}.log`);
218
+ removed += rewriteLogFile(file, opts);
219
+ }
220
+ return { removed };
221
+ }
222
+ function rewriteLogFile(file, opts) {
223
+ let stat;
224
+ try {
225
+ stat = fs.lstatSync(file);
226
+ } catch (error) {
227
+ if (error.code === "ENOENT") return 0;
228
+ throw error;
229
+ }
230
+ if (stat.isSymbolicLink()) throw new Error(`\u62D2\u7EDD\u6539\u5199\u7B26\u53F7\u94FE\u63A5\u65E5\u5FD7 ${path.basename(file)}`);
231
+ if (!stat.isFile()) throw new Error(`\u62D2\u7EDD\u6539\u5199\u975E\u6587\u4EF6\u65E5\u5FD7 ${path.basename(file)}`);
232
+ let text = fs.readFileSync(file, "utf8");
233
+ if (fs.statSync(file).size > Buffer.byteLength(text)) text = fs.readFileSync(file, "utf8");
234
+ const filtered = filterLogText(text, opts);
235
+ if (!filtered.removed) return 0;
236
+ const temp = `${file}.${process.pid}.filtering`;
237
+ fs.writeFileSync(temp, filtered.text, "utf8");
238
+ try {
239
+ fs.copyFileSync(temp, file);
240
+ } finally {
241
+ fs.rmSync(temp, { force: true });
242
+ }
243
+ return filtered.removed;
244
+ }
180
245
  const SOURCE_META = [
181
246
  { value: "bds", name: "BDS", paint: (s) => c.green(s) },
182
247
  { value: "db", name: "DB ", paint: (s) => c.blue(s) },
@@ -319,6 +384,7 @@ function wrapSegment(s, cols, startPad, hangIndent) {
319
384
  }
320
385
  export {
321
386
  SOURCE_META,
387
+ deleteStoredLogs,
322
388
  diskFileForSource,
323
389
  formatLog,
324
390
  formatSourceTag,
package/dist/logs.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/logs.ts"],
4
- "sourcesContent": ["/**\n * logs.ts \u2014 sfmc \u4E3B\u8FDB\u7A0B\u7EDF\u4E00\u65E5\u5FD7\u805A\u5408\u5C42\n *\n * \u5185\u90E8\u59D4\u6258 @sfmc-bds/logs \u5171\u4EAB\u5305\u7684 createMemoryBuffer + inferLevel,\n * \u4FDD\u6301\u5411\u540E\u517C\u5BB9\u7684 pushLog / onLog / getAllLogs / getRecentLogs API\n * (services.ts \u6355\u83B7\u5B50\u8FDB\u7A0B stdout \u540E\u8C03\u7528 pushLog \u6C47\u805A\u5230\u6B64)\u3002\n *\n * \u843D\u76D8\u7B56\u7565(\u4E0E\u5B50\u8FDB\u7A0B\u81EA\u5199\u6587\u4EF6\u5206\u5DE5,\u907F\u514D\u91CD\u590D):\n * - db / qq / update \u2192 \u5B50\u8FDB\u7A0B\u5DF2\u5199 `.sfmc/logs/{db,qq,bds-update}.log`,\u6B64\u5904\u8DF3\u8FC7\n * - bds / llbot \u2192 `.sfmc/logs/{bds,llbot}.log`(\u5916\u90E8\u8FDB\u7A0B\u65E0\u81EA\u5E26 file sink)\n * - \u5176\u4F59(system / pack / \u2026) \u2192 `.sfmc/logs/sfmc.log`\n *\n * CLI \u91CD\u8FDE\u540E\u5386\u53F2\u56DE\u653E\u4EE5\u843D\u76D8\u4E3A\u51C6\uFF08readDiskLogs\uFF09\uFF1B\u5185\u5B58\u7F13\u51B2\u53EA\u670D\u52A1\u672C\u8FDB\u7A0B\u5B9E\u65F6\u8BA2\u9605\u3002\n *\n * formatLog \u4FDD\u7559 theme.ts (chalk) \u914D\u8272,\u6BD4\u5171\u4EAB\u5305\u7684\u7EAF ANSI \u7248\u672C\u89C6\u89C9\u66F4\u4E30\u5BCC\u3002\n */\n\nimport {\n createFileSink,\n createMemoryBuffer,\n formatLogLine,\n inferLevel as sharedInferLevel,\n parseBdsEmbeddedLevel,\n stripBdsLogPrefix,\n type FileSink,\n type LogEntry,\n type LogLevel as SharedLogLevel,\n} from \"@sfmc-bds/sdk/logs\";\nimport { logFile, logsDir } from \"@sfmc-bds/sdk/node/config\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport {\n getCompiledLogFilter,\n shouldDropForDisk,\n shouldDropForDisplay,\n transformLogEntry,\n} from \"./log-filter.js\";\nimport { c, highlightLogLine } from \"./theme.js\";\nimport { ROOT } from \"./runtime.js\";\n\nexport type LogLevel = SharedLogLevel;\nexport type LogSource = string;\nexport interface UnifiedLog extends LogEntry {}\n\n/** \u518D\u5BFC\u51FA SDK BDS \u89E3\u6790\uFF08\u6D4B\u8BD5\u4E0E\u8C03\u7528\u65B9\u7EDF\u4E00\u5165\u53E3\uFF0C\u6743\u5A01\u5728 @sfmc-bds/sdk/logs\uFF09 */\nexport { mapBdsLevelToken, parseBdsEmbeddedLevel, stripBdsLogPrefix } from \"@sfmc-bds/sdk/logs\";\n\nconst buffer = createMemoryBuffer(5000);\n\n/** \u5B50\u8FDB\u7A0B\u5DF2\u7528 createNodeServiceLogger \u81EA\u884C\u843D\u76D8\u7684 source */\nconst CHILD_OWNED_SOURCES = new Set([\"db\", \"qq\", \"update\"]);\n\nconst fileSinks = new Map<string, FileSink>();\n\n/** \u5265\u79BB BDS \u81EA\u5E26\u524D\u7F00\uFF1B\u82E5\u524D\u7F00\u540E\u6CA1\u6709\u6B63\u6587\uFF0C\u5219\u5224\u5B9A\u4E3A\u4EC5\u7528\u4E8E\u6392\u7248\u7684\u7A7A\u884C\u3002 */\nexport function normalizeBdsLogBody(text: string): string | null {\n const body = stripBdsLogPrefix(text);\n return body.trim().length > 0 ? body : null;\n}\n\n/** \u89E3\u6790\u672C\u6761\u65E5\u5FD7\u5E94\u5199\u5165\u7684\u6587\u4EF6\u540D(\u4E0D\u542B .log);null \u8868\u793A\u8DF3\u8FC7(\u5B50\u8FDB\u7A0B\u5DF2\u5199) */\nfunction resolveDiskLogName(source: string): string | null {\n if (CHILD_OWNED_SOURCES.has(source)) return null;\n if (source === \"bds\" || source === \"llbot\" || source === \"tunnel\") return source;\n return \"sfmc\";\n}\n\nfunction sinkFor(source: string): FileSink | null {\n const name = resolveDiskLogName(source);\n if (!name) return null;\n let sink = fileSinks.get(name);\n if (!sink) {\n sink = createFileSink(logFile(ROOT, name));\n fileSinks.set(name, sink);\n }\n return sink;\n}\n\nprocess.on(\"exit\", () => {\n for (const sink of fileSinks.values()) sink.close();\n});\n\n/** \u63A8\u9001\u4E00\u6761\u65E5\u5FD7\u5230\u5185\u5B58\u7F13\u51B2,\u5E76\u6309\u7B56\u7565\u843D\u76D8 */\nexport function pushLog(text: string, source: LogSource, level: LogLevel): void {\n let body = text;\n let lvl = level;\n /* BDS\uFF1A\u5148\u63D0\u53D6\u5185\u5D4C\u7EA7\u522B\uFF0C\u518D\u5265\u6389\u81EA\u5E26\u65F6\u95F4\u6233+\u7B49\u7EA7\uFF0C\u907F\u514D\u5C55\u793A/\u843D\u76D8\u91CD\u590D */\n if (source === \"bds\") {\n const embedded = parseBdsEmbeddedLevel(text);\n if (embedded) lvl = embedded;\n const normalized = normalizeBdsLogBody(text);\n if (normalized === null) return;\n body = normalized;\n }\n\n const rawEntry: LogEntry = { time: new Date(), text: body, source, level: lvl };\n const filter = getCompiledLogFilter((src) => {\n /* \u907F\u514D\u9012\u5F52\uFF1A\u975E\u6CD5\u6B63\u5219\u53EA\u5199\u6587\u4EF6 sink\uFF0C\u4E0D\u7ECF pushLog */\n try {\n const sink = sinkFor(\"system\");\n if (!sink) return;\n const warn: LogEntry = {\n time: new Date(),\n text: `log-filter: invalid regex skipped: ${src}`,\n source: \"system\",\n level: \"warn\",\n };\n sink.write(warn, formatLogLine(warn, false));\n } catch {\n /* ignore */\n }\n });\n\n const dropDisplay = shouldDropForDisplay(rawEntry, filter);\n const dropDisk = shouldDropForDisk(rawEntry, filter);\n const { entry: transformedEntry } = transformLogEntry(rawEntry, filter);\n\n if (!dropDisplay) {\n buffer.pushDirect(transformedEntry.text, source, lvl);\n }\n\n if (dropDisk) return;\n const sink = sinkFor(source);\n if (!sink) return;\n try {\n const diskEntry: LogEntry = filter.applyTo === \"all\" ? { ...rawEntry, text: transformedEntry.text } : rawEntry;\n sink.write(diskEntry, formatLogLine(diskEntry, false));\n } catch {\n /* \u843D\u76D8\u5931\u8D25\u4E0D\u963B\u65AD\u4E3B\u6D41\u7A0B */\n }\n}\n\n/**\n * \u6444\u5165\u5B88\u62A4\u8FDB\u7A0B\u8F6C\u53D1\u7684\u8FDC\u7A0B\u65E5\u5FD7\uFF1A\u53EA\u5199\u5165\u672C\u8FDB\u7A0B\u5185\u5B58\u7F13\u51B2\u5E76\u901A\u77E5\u8BA2\u9605\u8005\uFF0C\u4E0D\u518D\u843D\u76D8\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1ACLI \u5BA2\u6237\u7AEF subscribe \u540E\u628A daemon \u63A8\u9001\u7684 log \u4E8B\u4EF6\u704C\u5165\u672C\u5730\uFF0C\u4F9B REPL \u4E3B\u9762\u677F\u5B9E\u65F6\u5C55\u793A\uFF1B\n * \u843D\u76D8\u5DF2\u7531\u5B88\u62A4\u8FDB\u7A0B\u5B8C\u6210\uFF0C\u6B64\u5904\u8DF3\u8FC7\u907F\u514D\u91CD\u590D\u5199 `.sfmc/logs/*`\u3002\n */\nexport function ingestRemoteLog(text: string, source: LogSource, level: LogLevel): void {\n buffer.pushDirect(text, source, level);\n}\n\n/** \u8BA2\u9605\u65B0\u65E5\u5FD7\u4E8B\u4EF6,\u8FD4\u56DE\u53D6\u6D88\u8BA2\u9605\u51FD\u6570 */\nexport function onLog(fn: (log: UnifiedLog) => void): () => void {\n return buffer.subscribe(fn);\n}\n\n/** \u83B7\u53D6\u6700\u8FD1 n \u6761,\u53EF\u6309 level / source \u8FC7\u6EE4\uFF1B\u518D\u5957\u914D\u7F6E\u8FC7\u6EE4\uFF08display\uFF09 */\nexport function getRecentLogs(n: number, levels: LogLevel[], sources: LogSource[]): UnifiedLog[] {\n const filter = getCompiledLogFilter();\n return buffer.getRecent(n, levels, sources).filter((l) => !shouldDropForDisplay(l, filter));\n}\n\n/** \u83B7\u53D6\u5168\u90E8\u65E5\u5FD7\uFF1B\u914D\u7F6E\u8FC7\u6EE4\u4EC5\u5F71\u54CD\u8FD4\u56DE\u89C6\u56FE\uFF08\u7F13\u51B2\u5185\u53EF\u80FD\u4ECD\u6709\u5168\u91CF\uFF0C\u89C6 push \u65F6 applyTo\uFF09 */\nexport function getAllLogs(): UnifiedLog[] {\n const filter = getCompiledLogFilter();\n return buffer.getAll().filter((l) => !shouldDropForDisplay(l, filter));\n}\n\n/* ==================================================================\n * \u843D\u76D8\u6587\u4EF6\u8BFB\u53D6\uFF08/logs \u7A97\u7528\uFF0C\u4E0D\u8D70\u5185\u5B58\u7F13\u51B2\uFF09\n * ================================================================== */\n\n/** \u4E0E\u843D\u76D8\u7B56\u7565\u5BF9\u5E94\u7684\u6587\u4EF6\u57FA\u540D\uFF08\u4E0D\u542B .log\uFF09 */\nconst DISK_LOG_FILES = [\"bds\", \"llbot\", \"tunnel\", \"db\", \"qq\", \"bds-update\", \"sfmc\"] as const;\n\n/** source \u2192 \u843D\u76D8\u6587\u4EF6\u540D\uFF08\u542B\u5B50\u8FDB\u7A0B\u81EA\u5199\uFF09 */\nexport function diskFileForSource(source: string): string {\n if (source === \"update\") return \"bds-update\";\n if (source === \"bds\" || source === \"llbot\" || source === \"tunnel\" || source === \"db\" || source === \"qq\") return source;\n return \"sfmc\";\n}\n\n/** formatLogLine \u65E0\u8272\u884C\uFF1A`YYYY-MM-DD HH:mm:ss [source] [LEVEL] text` */\nconst DISK_LINE_RE =\n /^(\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}) \\[([^\\]]+)\\] \\[(INFO|WARN|ERROR|OK|DEBUG|SUCCESS|FATAL)\\]\\s?(.*)$/;\n\nfunction mapDiskLevelTag(tag: string): LogLevel {\n switch (tag) {\n case \"ERROR\":\n case \"FATAL\":\n return \"error\";\n case \"WARN\":\n return \"warn\";\n case \"OK\":\n case \"SUCCESS\":\n return \"success\";\n case \"DEBUG\":\n return \"debug\";\n default:\n return \"info\";\n }\n}\n\n/** \u89E3\u6790\u5355\u884C\u843D\u76D8\u65E5\u5FD7\uFF1B\u4E0D\u7B26\u683C\u5F0F\u8FD4\u56DE null */\nexport function parseDiskLogLine(raw: string): UnifiedLog | null {\n const m = DISK_LINE_RE.exec(raw);\n if (!m) return null;\n const ts = m[1]!;\n const source = m[2]!.trim();\n const level = mapDiskLevelTag(m[3]!);\n const body = m[4] ?? \"\";\n const time = new Date(ts.replace(\" \", \"T\") + \"Z\");\n return { time, source, level, text: body };\n}\n\nexport type ReadDiskLogsOpts = {\n levels?: LogLevel[];\n sources?: LogSource[];\n /** \u8986\u76D6\u65E5\u5FD7\u76EE\u5F55\uFF08\u6D4B\u8BD5\u7528\uFF09\uFF1B\u9ED8\u8BA4 `<ROOT>/.sfmc/logs` */\n dir?: string;\n /** \u4EC5\u4FDD\u7559\u65F6\u95F4\u5E8F\u6700\u540E N \u6761\uFF1B\u7701\u7565\u5219\u8FD4\u56DE\u5168\u90E8\u89E3\u6790\u7ED3\u679C */\n limit?: number;\n};\n\n/**\n * \u4ECE `.sfmc/logs/*.log` \u8BFB\u53D6\u5E76\u89E3\u6790\u4E3A UnifiedLog\uFF08\u6309\u65F6\u95F4\u5347\u5E8F\uFF09\u3002\n * CLI \u91CD\u8FDE\u540E\u4E3B\u9762\u677F /logs \u7684\u5386\u53F2\u6743\u5A01\u6765\u6E90\uFF1B\u5185\u5B58\u7F13\u51B2\u4EC5\u627F\u8F7D\u672C\u8FDB\u7A0B\u8BA2\u9605\u5230\u7684\u5B9E\u65F6\u589E\u91CF\u3002\n */\nexport function readDiskLogs(opts: ReadDiskLogsOpts = {}): UnifiedLog[] {\n const levels = opts.levels ?? [];\n const sources = opts.sources ?? [];\n const dir = opts.dir ?? logsDir(ROOT);\n const fileNames = new Set<string>();\n if (sources.length === 0) {\n for (const n of DISK_LOG_FILES) fileNames.add(n);\n } else {\n for (const s of sources) fileNames.add(diskFileForSource(s));\n }\n\n const out: UnifiedLog[] = [];\n for (const name of fileNames) {\n const fp = path.join(dir, name.endsWith(\".log\") ? name : `${name}.log`);\n let text: string;\n try {\n if (opts.limit) {\n const fd = fs.openSync(fp, \"r\");\n try {\n const size = fs.fstatSync(fd).size;\n const start = Math.max(0, size - 4 * 1024 * 1024);\n const bytes = Buffer.alloc(size - start);\n fs.readSync(fd, bytes, 0, bytes.length, start);\n text = bytes.toString(\"utf8\");\n if (start > 0) text = text.slice(text.indexOf(\"\\n\") + 1);\n } finally { fs.closeSync(fd); }\n } else text = fs.readFileSync(fp, \"utf8\");\n } catch {\n continue;\n }\n for (const raw of text.split(/\\r?\\n/)) {\n if (!raw) continue;\n const entry = parseDiskLogLine(raw);\n if (!entry) continue;\n if (sources.length && !sources.includes(entry.source)) continue;\n if (levels.length && !levels.includes(entry.level)) continue;\n out.push(entry);\n }\n }\n out.sort((a, b) => a.time.getTime() - b.time.getTime());\n const limit = opts.limit;\n if (limit !== undefined && limit > 0 && out.length > limit) {\n return out.slice(out.length - limit);\n }\n return out;\n}\n\n/* ==================================================================\n * \u6765\u6E90\u6807\u7B7E\u5143\u6570\u636E (\u65B9\u62EC\u53F7\u4E0E\u6587\u5B57\u540C\u8272,\u5BF9\u9F50 levelTag)\n * ================================================================== */\n\nexport interface SourceMeta {\n value: string;\n /** \u65B9\u62EC\u53F7\u5185\u56FA\u5B9A 3 \u5B57\u7B26\uFF0C\u4E0E [INF] \u5BF9\u9F50 */\n name: string;\n paint: (s: string) => string;\n}\n\nexport const SOURCE_META: SourceMeta[] = [\n { value: \"bds\", name: \"BDS\", paint: (s) => c.green(s) },\n { value: \"db\", name: \"DB \", paint: (s) => c.blue(s) },\n { value: \"qq\", name: \"QQ \", paint: (s) => c.purple(s) },\n { value: \"llbot\", name: \"BOT\", paint: (s) => c.yellow(s) },\n { value: \"system\", name: \"SYS\", paint: (s) => c.cyan(s) },\n { value: \"update\", name: \"UPD\", paint: (s) => c.orange(s) },\n { value: \"pack\", name: \"PAK\", paint: (s) => c.purple(s) },\n { value: \"bds-tools\", name: \"TOL\", paint: (s) => c.red(s) },\n];\n\n/** \u6E90\u6807\u7B7E\u65E0\u8272\u6587\u672C\uFF08formatSourceTag / logPrefixWidth \u5171\u7528\uFF09 */\nfunction sourceTagPlain(source: string): string {\n const meta = SOURCE_META.find((m) => m.value === source);\n if (meta) return `[${meta.name}]`;\n return `[${source.toUpperCase().padEnd(3).slice(0, 3)}]`;\n}\n\nexport function formatSourceTag(source: string): string {\n const meta = SOURCE_META.find((m) => m.value === source);\n const plain = sourceTagPlain(source);\n if (meta) return meta.paint(plain);\n return c.bold(plain);\n}\n\n/**\n * \u5C55\u793A\u7528\u7EA7\u522B\uFF1ABDS \u82E5\u4ECD\u5E26\u65F6\u95F4\u6233\u524D\u7F00\u5219\u89E3\u6790\u5185\u5D4C\u7EA7\u522B\uFF1B\u5DF2\u5265\u79BB\uFF08pushLog \u5165\u5E93\uFF09\u5219\u4FE1\u4EFB entry.level\u3002\n * \u7981\u6B62\u5BF9\u6B63\u6587\u505A\u677E\u6563\u5173\u952E\u8BCD\u5339\u914D\uFF08\u4F1A\u628A `[Commands] Error on line` \u8BEF\u5224\u4E3A error\uFF09\u3002\n */\nexport function resolveDisplayLevel(l: UnifiedLog): LogLevel {\n if (l.source === \"bds\") {\n const embedded = parseBdsEmbeddedLevel(l.text);\n if (embedded) return embedded;\n }\n return l.level;\n}\n\n/** \u4ECE\u539F\u59CB\u6587\u672C\u63A8\u65AD\u7EA7\u522B\uFF1A\u59D4\u6258\u5171\u4EAB\u5305\uFF08\u5185\u542B BDS \u65F6\u95F4\u6233\u89E3\u6790\uFF09 */\nexport function inferLevel(text: string): LogLevel {\n return sharedInferLevel(text);\n}\n\n/** \u65E0\u8272\u7EA7\u522B\u6807\u7B7E\u6587\u672C\uFF08\u53EF\u89C1\u5BBD\u5EA6\u6743\u5A01\u6E90\uFF1B\u62EC\u53F7\u5185\u5747\u4E3A 3 \u5B57\u7B26\uFF09 */\nconst LEVEL_TAG_TEXT: Record<LogLevel, string> = {\n error: \"[ERR]\",\n warn: \"[WRN]\",\n success: \"[SUC]\",\n debug: \"[DBG]\",\n info: \"[INF]\",\n};\n\nfunction levelTagPlain(lvl: LogLevel): string {\n return LEVEL_TAG_TEXT[lvl] ?? LEVEL_TAG_TEXT.info;\n}\n\nfunction levelTag(lvl: LogLevel): string {\n const text = levelTagPlain(lvl);\n switch (lvl) {\n case \"error\":\n return c.red(text);\n case \"warn\":\n return c.yellow(text);\n case \"success\":\n return c.green(c.bold(text));\n case \"debug\":\n return c.dim(text);\n default:\n return c.blue(text);\n }\n}\n\n/** \u683C\u5F0F\u5316\u65E5\u5FD7\u7528\u4E8E REPL \u5C55\u793A (\u7528 theme.ts chalk \u914D\u8272) */\nexport function formatLog(l: UnifiedLog): string {\n const ts = c.dim(l.time.toLocaleTimeString());\n const level = resolveDisplayLevel(l);\n const lvl = levelTag(level);\n const src = formatSourceTag(l.source);\n /* BDS\uFF1A\u53BB\u6389\u81EA\u5E26\u65F6\u95F4\u6233+\u7B49\u7EA7\u524D\u7F00\u540E\u518D\u672C\u5730\u5316\u7FFB\u8BD1\u4E0E\u9AD8\u4EAE\u6B63\u6587\uFF08push \u65F6\u591A\u534A\u5DF2\u8F6C\u8FC7\uFF0C\u6B64\u5904\u5E42\u7B49\u4E14\u8986\u76D6\u56DE\u653E\uFF09 */\n const body = l.source === \"bds\" ? stripBdsLogPrefix(l.text) : l.text;\n const filter = getCompiledLogFilter();\n const { entry } = transformLogEntry({ text: body, source: l.source, level }, filter);\n const txt = highlightLogLine(entry.text);\n return `${ts} ${src} ${lvl} ${txt}`;\n}\n\n/* ==================================================================\n * \u8F6F\u6362\u884C: \u8D85\u7EC8\u7AEF\u5BBD\u5EA6\u65F6\u6362\u884C,\u540E\u7EED\u884C\u7F29\u8FDB\u5BF9\u9F50\n * ================================================================== */\n\n/** \u5355\u4E2A\u5B57\u7B26\u5728\u7EC8\u7AEF\u7684\u663E\u793A\u5BBD\u5EA6 (CJK \u5168\u89D2=2, \u5176\u4F59=1) */\nfunction charWidth(ch: string): number {\n const cp = ch.codePointAt(0) ?? 0;\n if (\n cp >= 0x1100 &&\n (cp <= 0x115f ||\n (cp >= 0x2e80 && cp <= 0xa4cf && cp !== 0x303f) ||\n (cp >= 0xac00 && cp <= 0xd7a3) ||\n (cp >= 0xf900 && cp <= 0xfaff) ||\n (cp >= 0xfe30 && cp <= 0xfe4f) ||\n (cp >= 0xff00 && cp <= 0xff60) ||\n (cp >= 0xffe0 && cp <= 0xffe6) ||\n (cp >= 0x20000 && cp <= 0x2fffd))\n )\n return 2;\n return 1;\n}\n\n/** \u5B57\u7B26\u4E32\u5728\u7EC8\u7AEF\u7684\u53EF\u89C1\u5BBD\u5EA6 (strip ANSI \u540E\u6309 CJK \u5BBD\u5EA6\u7D2F\u52A0) */\nexport function visibleWidth(s: string): number {\n const stripped = s.replace(/\\x1b\\[[0-9;]*m/g, \"\");\n let w = 0;\n for (const ch of stripped) w += charWidth(ch);\n return w;\n}\n\n/**\n * \u65E5\u5FD7\u524D\u7F00\u53EF\u89C1\u5BBD\u5EA6(\u65F6\u95F4 + \u6E90 + \u7EA7\u522B + \u5C3E\u7A7A\u683C),\u4F9B\u60AC\u6302\u7F29\u8FDB\u5BF9\u9F50\u6B63\u6587\u8D77\u70B9\u3002\n * \u4E0E formatLog \u62FC\u63A5\u987A\u5E8F\u4FDD\u6301\u4E00\u81F4\uFF08\u5171\u7528 resolveDisplayLevel / sourceTagPlain / levelTagPlain\uFF09\u3002\n */\nexport function logPrefixWidth(l: UnifiedLog): number {\n const ts = l.time.toLocaleTimeString();\n const src = sourceTagPlain(l.source);\n const level = resolveDisplayLevel(l);\n return visibleWidth(`${ts} ${src} ${levelTagPlain(level)} `);\n}\n\n/**\n * \u6309\u7EC8\u7AEF\u53EF\u89C1\u5BBD\u5EA6\u6362\u884C;\u663E\u5F0F \\\\n \u4E0E\u8F6F\u6362\u884C\u7684\u540E\u7EED\u884C\u4E00\u5F8B\u5DE6\u4FA7\u7F29\u8FDB indent \u5217\u3002\n * \u4FDD\u7559 ANSI:\u6362\u884C\u65F6 reset,\u65B0\u884C\u6062\u590D\u5F53\u524D\u989C\u8272\u72B6\u6001(\u53EF\u591A\u5C42\u53E0\u52A0)\u3002\n * \u5BBD\u5B57\u7B26(CJK)\u6309 2 \u5217;cols \u9884\u7559 1 \u5217,\u907F\u514D Windows Terminal \u8FB9\u754C\u518D\u8F6F\u6298\u5230\u7B2C 0 \u5217\u3002\n */\nexport function wrapLogLine(s: string, indent: number): string {\n const rawCols = process.stdout.columns || 80;\n /* \u81F3\u5C11\u7559\u7ED9\u6B63\u6587\u82E5\u5E72\u5217,\u907F\u514D indent >= cols \u65F6\u6B7B\u5FAA\u73AF */\n const cols = Math.max(rawCols - 1, indent + 4);\n const segments = s.split(/\\r?\\n/);\n const out: string[] = [];\n for (let si = 0; si < segments.length; si++) {\n const raw = segments[si]!;\n /* \u663E\u5F0F\u6362\u884C\u6BB5\u53BB\u6389\u6587\u6848\u81EA\u5E26\u7684\u5DE6\u7A7A\u683C,\u7EDF\u4E00\u7531 hang indent \u5BF9\u9F50\u6B63\u6587 */\n const piece = si === 0 ? raw : raw.replace(/^[ \\t]+/, \"\");\n out.push(wrapSegment(piece, cols, si === 0 ? 0 : indent, indent));\n }\n return out.join(\"\\n\");\n}\n\n/** \u5355\u6BB5(\u65E0\u5185\u5D4C \\\\n)\u8F6F\u6362\u884C;startPad>0 \u8868\u793A\u672C\u6BB5\u5F00\u5934\u5DF2\u5360\u7528\u7684\u5217(\u663E\u5F0F\u6362\u884C\u540E\u7684\u60AC\u6302) */\nfunction wrapSegment(s: string, cols: number, startPad: number, hangIndent: number): string {\n if (s.length === 0) return startPad > 0 ? \" \".repeat(startPad) : \"\";\n\n const lines: string[] = [];\n let cur = startPad > 0 ? \" \".repeat(startPad) : \"\";\n let w = startPad;\n /** \u5F53\u524D\u751F\u6548\u7684 ANSI \u5F00\u7801\u6808(\u4E0D\u542B reset),\u6362\u884C\u540E\u91CD\u653E */\n const ansiStack: string[] = [];\n\n const resetIfNeeded = (): void => {\n if (ansiStack.length) cur += \"\\x1b[0m\";\n };\n const replayAnsi = (): void => {\n for (const code of ansiStack) cur += code;\n };\n\n let i = 0;\n while (i < s.length) {\n const m = /^\\x1b\\[[0-9;]*m/.exec(s.slice(i));\n if (m) {\n const code = m[0]!;\n cur += code;\n if (code === \"\\x1b[0m\") ansiStack.length = 0;\n else ansiStack.push(code);\n i += code.length;\n continue;\n }\n\n const cp = s.codePointAt(i)!;\n const ch = String.fromCodePoint(cp);\n const cw = charWidth(ch);\n const advance = ch.length;\n\n /* w > startPad:\u5F53\u524D\u884C\u9664\u5DE6\u57AB\u5916\u5DF2\u6709\u5185\u5BB9,\u53EF\u4EE5\u62C6\u884C;\u5426\u5219\u5F3A\u5236\u585E\u5165\u907F\u514D\u6B7B\u5FAA\u73AF */\n if (w + cw > cols && w > startPad) {\n resetIfNeeded();\n lines.push(cur);\n cur = \" \".repeat(hangIndent);\n replayAnsi();\n w = hangIndent;\n startPad = hangIndent;\n }\n\n cur += ch;\n w += cw;\n i += advance;\n }\n\n if (ansiStack.length) cur += \"\\x1b[0m\";\n lines.push(cur);\n return lines.join(\"\\n\");\n}\n\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * logs.ts \u2014 sfmc \u4E3B\u8FDB\u7A0B\u7EDF\u4E00\u65E5\u5FD7\u805A\u5408\u5C42\n *\n * \u5185\u90E8\u59D4\u6258 @sfmc-bds/logs \u5171\u4EAB\u5305\u7684 createMemoryBuffer + inferLevel,\n * \u4FDD\u6301\u5411\u540E\u517C\u5BB9\u7684 pushLog / onLog / getAllLogs / getRecentLogs API\n * (services.ts \u6355\u83B7\u5B50\u8FDB\u7A0B stdout \u540E\u8C03\u7528 pushLog \u6C47\u805A\u5230\u6B64)\u3002\n *\n * \u843D\u76D8\u7B56\u7565(\u4E0E\u5B50\u8FDB\u7A0B\u81EA\u5199\u6587\u4EF6\u5206\u5DE5,\u907F\u514D\u91CD\u590D):\n * - db / qq / update \u2192 \u5B50\u8FDB\u7A0B\u5DF2\u5199 `.sfmc/logs/{db,qq,bds-update}.log`,\u6B64\u5904\u8DF3\u8FC7\n * - bds / llbot \u2192 `.sfmc/logs/{bds,llbot}.log`(\u5916\u90E8\u8FDB\u7A0B\u65E0\u81EA\u5E26 file sink)\n * - \u5176\u4F59(system / pack / \u2026) \u2192 `.sfmc/logs/sfmc.log`\n *\n * CLI \u91CD\u8FDE\u540E\u5386\u53F2\u56DE\u653E\u4EE5\u843D\u76D8\u4E3A\u51C6\uFF08readDiskLogs\uFF09\uFF1B\u5185\u5B58\u7F13\u51B2\u53EA\u670D\u52A1\u672C\u8FDB\u7A0B\u5B9E\u65F6\u8BA2\u9605\u3002\n *\n * formatLog \u4FDD\u7559 theme.ts (chalk) \u914D\u8272,\u6BD4\u5171\u4EAB\u5305\u7684\u7EAF ANSI \u7248\u672C\u89C6\u89C9\u66F4\u4E30\u5BCC\u3002\n */\n\nimport {\n createFileSink,\n createMemoryBuffer,\n formatLogLine,\n inferLevel as sharedInferLevel,\n parseBdsEmbeddedLevel,\n stripBdsLogPrefix,\n type FileSink,\n type LogEntry,\n type LogLevel as SharedLogLevel,\n} from \"@sfmc-bds/sdk/logs\";\nimport { logFile, logsDir } from \"@sfmc-bds/sdk/node/config\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport {\n getCompiledLogFilter,\n shouldDropForDisk,\n shouldDropForDisplay,\n transformLogEntry,\n} from \"./log-filter.js\";\nimport { c, highlightLogLine } from \"./theme.js\";\nimport { ROOT } from \"./runtime.js\";\n\nexport type LogLevel = SharedLogLevel;\nexport type LogSource = string;\nexport interface UnifiedLog extends LogEntry {}\n\n/** \u518D\u5BFC\u51FA SDK BDS \u89E3\u6790\uFF08\u6D4B\u8BD5\u4E0E\u8C03\u7528\u65B9\u7EDF\u4E00\u5165\u53E3\uFF0C\u6743\u5A01\u5728 @sfmc-bds/sdk/logs\uFF09 */\nexport { mapBdsLevelToken, parseBdsEmbeddedLevel, stripBdsLogPrefix } from \"@sfmc-bds/sdk/logs\";\n\nconst buffer = createMemoryBuffer(5000);\n\n/** \u5B50\u8FDB\u7A0B\u5DF2\u7528 createNodeServiceLogger \u81EA\u884C\u843D\u76D8\u7684 source */\nconst CHILD_OWNED_SOURCES = new Set([\"db\", \"qq\", \"update\"]);\n\nconst fileSinks = new Map<string, FileSink>();\n\n/** \u5265\u79BB BDS \u81EA\u5E26\u524D\u7F00\uFF1B\u82E5\u524D\u7F00\u540E\u6CA1\u6709\u6B63\u6587\uFF0C\u5219\u5224\u5B9A\u4E3A\u4EC5\u7528\u4E8E\u6392\u7248\u7684\u7A7A\u884C\u3002 */\nexport function normalizeBdsLogBody(text: string): string | null {\n const body = stripBdsLogPrefix(text);\n return body.trim().length > 0 ? body : null;\n}\n\n/** \u89E3\u6790\u672C\u6761\u65E5\u5FD7\u5E94\u5199\u5165\u7684\u6587\u4EF6\u540D(\u4E0D\u542B .log);null \u8868\u793A\u8DF3\u8FC7(\u5B50\u8FDB\u7A0B\u5DF2\u5199) */\nfunction resolveDiskLogName(source: string): string | null {\n if (CHILD_OWNED_SOURCES.has(source)) return null;\n if (source === \"bds\" || source === \"llbot\" || source === \"tunnel\") return source;\n return \"sfmc\";\n}\n\nfunction sinkFor(source: string): FileSink | null {\n const name = resolveDiskLogName(source);\n if (!name) return null;\n let sink = fileSinks.get(name);\n if (!sink) {\n sink = createFileSink(logFile(ROOT, name));\n fileSinks.set(name, sink);\n }\n return sink;\n}\n\nprocess.on(\"exit\", () => {\n for (const sink of fileSinks.values()) sink.close();\n});\n\n/** \u63A8\u9001\u4E00\u6761\u65E5\u5FD7\u5230\u5185\u5B58\u7F13\u51B2,\u5E76\u6309\u7B56\u7565\u843D\u76D8 */\nexport function pushLog(text: string, source: LogSource, level: LogLevel): void {\n let body = text;\n let lvl = level;\n /* BDS\uFF1A\u5148\u63D0\u53D6\u5185\u5D4C\u7EA7\u522B\uFF0C\u518D\u5265\u6389\u81EA\u5E26\u65F6\u95F4\u6233+\u7B49\u7EA7\uFF0C\u907F\u514D\u5C55\u793A/\u843D\u76D8\u91CD\u590D */\n if (source === \"bds\") {\n const embedded = parseBdsEmbeddedLevel(text);\n if (embedded) lvl = embedded;\n const normalized = normalizeBdsLogBody(text);\n if (normalized === null) return;\n body = normalized;\n }\n\n const rawEntry: LogEntry = { time: new Date(), text: body, source, level: lvl };\n const filter = getCompiledLogFilter((src) => {\n /* \u907F\u514D\u9012\u5F52\uFF1A\u975E\u6CD5\u6B63\u5219\u53EA\u5199\u6587\u4EF6 sink\uFF0C\u4E0D\u7ECF pushLog */\n try {\n const sink = sinkFor(\"system\");\n if (!sink) return;\n const warn: LogEntry = {\n time: new Date(),\n text: `log-filter: invalid regex skipped: ${src}`,\n source: \"system\",\n level: \"warn\",\n };\n sink.write(warn, formatLogLine(warn, false));\n } catch {\n /* ignore */\n }\n });\n\n const dropDisplay = shouldDropForDisplay(rawEntry, filter);\n const dropDisk = shouldDropForDisk(rawEntry, filter);\n const { entry: transformedEntry } = transformLogEntry(rawEntry, filter);\n\n if (!dropDisplay) {\n buffer.pushDirect(transformedEntry.text, source, lvl);\n }\n\n if (dropDisk) return;\n const sink = sinkFor(source);\n if (!sink) return;\n try {\n const diskEntry: LogEntry = filter.applyTo === \"all\" ? { ...rawEntry, text: transformedEntry.text } : rawEntry;\n sink.write(diskEntry, formatLogLine(diskEntry, false));\n } catch {\n /* \u843D\u76D8\u5931\u8D25\u4E0D\u963B\u65AD\u4E3B\u6D41\u7A0B */\n }\n}\n\n/**\n * \u6444\u5165\u5B88\u62A4\u8FDB\u7A0B\u8F6C\u53D1\u7684\u8FDC\u7A0B\u65E5\u5FD7\uFF1A\u53EA\u5199\u5165\u672C\u8FDB\u7A0B\u5185\u5B58\u7F13\u51B2\u5E76\u901A\u77E5\u8BA2\u9605\u8005\uFF0C\u4E0D\u518D\u843D\u76D8\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1ACLI \u5BA2\u6237\u7AEF subscribe \u540E\u628A daemon \u63A8\u9001\u7684 log \u4E8B\u4EF6\u704C\u5165\u672C\u5730\uFF0C\u4F9B REPL \u4E3B\u9762\u677F\u5B9E\u65F6\u5C55\u793A\uFF1B\n * \u843D\u76D8\u5DF2\u7531\u5B88\u62A4\u8FDB\u7A0B\u5B8C\u6210\uFF0C\u6B64\u5904\u8DF3\u8FC7\u907F\u514D\u91CD\u590D\u5199 `.sfmc/logs/*`\u3002\n */\nexport function ingestRemoteLog(text: string, source: LogSource, level: LogLevel): void {\n buffer.pushDirect(text, source, level);\n}\n\n/** \u8BA2\u9605\u65B0\u65E5\u5FD7\u4E8B\u4EF6,\u8FD4\u56DE\u53D6\u6D88\u8BA2\u9605\u51FD\u6570 */\nexport function onLog(fn: (log: UnifiedLog) => void): () => void {\n return buffer.subscribe(fn);\n}\n\n/** \u83B7\u53D6\u6700\u8FD1 n \u6761,\u53EF\u6309 level / source \u8FC7\u6EE4\uFF1B\u518D\u5957\u914D\u7F6E\u8FC7\u6EE4\uFF08display\uFF09 */\nexport function getRecentLogs(n: number, levels: LogLevel[], sources: LogSource[]): UnifiedLog[] {\n const filter = getCompiledLogFilter();\n return buffer.getRecent(n, levels, sources).filter((l) => !shouldDropForDisplay(l, filter));\n}\n\n/** \u83B7\u53D6\u5168\u90E8\u65E5\u5FD7\uFF1B\u914D\u7F6E\u8FC7\u6EE4\u4EC5\u5F71\u54CD\u8FD4\u56DE\u89C6\u56FE\uFF08\u7F13\u51B2\u5185\u53EF\u80FD\u4ECD\u6709\u5168\u91CF\uFF0C\u89C6 push \u65F6 applyTo\uFF09 */\nexport function getAllLogs(): UnifiedLog[] {\n const filter = getCompiledLogFilter();\n return buffer.getAll().filter((l) => !shouldDropForDisplay(l, filter));\n}\n\n/* ==================================================================\n * \u843D\u76D8\u6587\u4EF6\u8BFB\u53D6\uFF08/logs \u7A97\u7528\uFF0C\u4E0D\u8D70\u5185\u5B58\u7F13\u51B2\uFF09\n * ================================================================== */\n\n/** \u4E0E\u843D\u76D8\u7B56\u7565\u5BF9\u5E94\u7684\u6587\u4EF6\u57FA\u540D\uFF08\u4E0D\u542B .log\uFF09 */\nconst DISK_LOG_FILES = [\"bds\", \"llbot\", \"tunnel\", \"db\", \"qq\", \"bds-update\", \"sfmc\"] as const;\n\n/** source \u2192 \u843D\u76D8\u6587\u4EF6\u540D\uFF08\u542B\u5B50\u8FDB\u7A0B\u81EA\u5199\uFF09 */\nexport function diskFileForSource(source: string): string {\n if (source === \"update\") return \"bds-update\";\n if (source === \"bds\" || source === \"llbot\" || source === \"tunnel\" || source === \"db\" || source === \"qq\") return source;\n return \"sfmc\";\n}\n\n/** formatLogLine \u65E0\u8272\u884C\uFF1A`YYYY-MM-DD HH:mm:ss [source] [LEVEL] text` */\nconst DISK_LINE_RE =\n /^(\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}) \\[([^\\]]+)\\] \\[(INFO|WARN|ERROR|OK|DEBUG|SUCCESS|FATAL)\\]\\s?(.*)$/;\n\nfunction mapDiskLevelTag(tag: string): LogLevel {\n switch (tag) {\n case \"ERROR\":\n case \"FATAL\":\n return \"error\";\n case \"WARN\":\n return \"warn\";\n case \"OK\":\n case \"SUCCESS\":\n return \"success\";\n case \"DEBUG\":\n return \"debug\";\n default:\n return \"info\";\n }\n}\n\n/** \u89E3\u6790\u5355\u884C\u843D\u76D8\u65E5\u5FD7\uFF1B\u4E0D\u7B26\u683C\u5F0F\u8FD4\u56DE null */\nexport function parseDiskLogLine(raw: string): UnifiedLog | null {\n const m = DISK_LINE_RE.exec(raw);\n if (!m) return null;\n const ts = m[1]!;\n const source = m[2]!.trim();\n const level = mapDiskLevelTag(m[3]!);\n const body = m[4] ?? \"\";\n const time = new Date(ts.replace(\" \", \"T\") + \"Z\");\n return { time, source, level, text: body };\n}\n\nexport type ReadDiskLogsOpts = {\n levels?: LogLevel[];\n sources?: LogSource[];\n /** \u8986\u76D6\u65E5\u5FD7\u76EE\u5F55\uFF08\u6D4B\u8BD5\u7528\uFF09\uFF1B\u9ED8\u8BA4 `<ROOT>/.sfmc/logs` */\n dir?: string;\n /** \u4EC5\u4FDD\u7559\u65F6\u95F4\u5E8F\u6700\u540E N \u6761\uFF1B\u7701\u7565\u5219\u8FD4\u56DE\u5168\u90E8\u89E3\u6790\u7ED3\u679C */\n limit?: number;\n /** \u542B\u6B64\u65F6\u95F4\u3002\u8BBE\u7F6E\u540E\u8BFB\u53D6\u6574\u4E2A\u65E5\u5FD7\u6587\u4EF6\uFF0C\u4E0D\u518D\u53EA\u53D6\u6587\u4EF6\u672B\u5C3E */\n since?: Date;\n /** \u542B\u6B64\u65F6\u95F4 */\n until?: Date;\n};\n\n/** \u5220\u9664\u843D\u76D8\u65E5\u5FD7\u65F6\u7684\u7B5B\u9009\u3002\u4F7F\u7528\u573A\u666F\uFF1A\u684C\u9762 logs.delete\u3002 */\nexport type DeleteDiskLogsOpts = {\n since: Date;\n until: Date;\n source?: string;\n text?: string;\n /** \u8986\u76D6\u65E5\u5FD7\u76EE\u5F55\uFF08\u6D4B\u8BD5\u7528\uFF09 */\n dir?: string;\n};\n\n/**\n * \u4ECE `.sfmc/logs/*.log` \u8BFB\u53D6\u5E76\u89E3\u6790\u4E3A UnifiedLog\uFF08\u6309\u65F6\u95F4\u5347\u5E8F\uFF09\u3002\n * CLI \u91CD\u8FDE\u540E\u4E3B\u9762\u677F /logs \u7684\u5386\u53F2\u6743\u5A01\u6765\u6E90\uFF1B\u5185\u5B58\u7F13\u51B2\u4EC5\u627F\u8F7D\u672C\u8FDB\u7A0B\u8BA2\u9605\u5230\u7684\u5B9E\u65F6\u589E\u91CF\u3002\n */\nexport function readDiskLogs(opts: ReadDiskLogsOpts = {}): UnifiedLog[] {\n const levels = opts.levels ?? [];\n const sources = opts.sources ?? [];\n const dir = opts.dir ?? logsDir(ROOT);\n const sinceMs = opts.since?.getTime();\n const untilMs = opts.until?.getTime();\n const ranged = sinceMs !== undefined || untilMs !== undefined;\n const fileNames = new Set<string>();\n if (sources.length === 0) {\n for (const n of DISK_LOG_FILES) fileNames.add(n);\n } else {\n for (const s of sources) fileNames.add(diskFileForSource(s));\n }\n\n const out: UnifiedLog[] = [];\n for (const name of fileNames) {\n const fp = path.join(dir, name.endsWith(\".log\") ? name : `${name}.log`);\n let text: string;\n try {\n if (opts.limit && !ranged) {\n const fd = fs.openSync(fp, \"r\");\n try {\n const size = fs.fstatSync(fd).size;\n const start = Math.max(0, size - 4 * 1024 * 1024);\n const bytes = Buffer.alloc(size - start);\n fs.readSync(fd, bytes, 0, bytes.length, start);\n text = bytes.toString(\"utf8\");\n if (start > 0) text = text.slice(text.indexOf(\"\\n\") + 1);\n } finally { fs.closeSync(fd); }\n } else text = fs.readFileSync(fp, \"utf8\");\n } catch {\n continue;\n }\n for (const raw of text.split(/\\r?\\n/)) {\n if (!raw) continue;\n const entry = parseDiskLogLine(raw);\n if (!entry) continue;\n const time = entry.time.getTime();\n if (sinceMs !== undefined && time < sinceMs) continue;\n if (untilMs !== undefined && time > untilMs) continue;\n if (sources.length && !sources.includes(entry.source)) continue;\n if (levels.length && !levels.includes(entry.level)) continue;\n out.push(entry);\n }\n }\n out.sort((a, b) => a.time.getTime() - b.time.getTime());\n const limit = opts.limit;\n if (limit !== undefined && limit > 0 && out.length > limit) {\n return out.slice(out.length - limit);\n }\n return out;\n}\n\n/** \u8FD9\u4E00\u884C\u662F\u5426\u843D\u5728\u5220\u9664\u6761\u4EF6\u91CC\u3002\u89E3\u6790\u5931\u8D25\u7684\u884C\u4E0D\u5220\u3002 */\nfunction diskLineMatchesRemoval(entry: UnifiedLog, opts: DeleteDiskLogsOpts): boolean {\n const time = entry.time.getTime();\n if (time < opts.since.getTime() || time > opts.until.getTime()) return false;\n if (opts.source !== undefined && entry.source !== opts.source) return false;\n if (opts.text !== undefined && entry.text !== opts.text) return false;\n return true;\n}\n\n/**\n * \u4ECE\u4E00\u6BB5\u65E5\u5FD7\u6587\u672C\u91CC\u53BB\u6389\u547D\u4E2D\u7684\u884C\uFF0C\u4FDD\u7559\u65E0\u6CD5\u89E3\u6790\u7684\u884C\u548C\u539F\u6765\u7684\u6362\u884C\u98CE\u683C\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A\u6539\u5199 `.sfmc/logs/*.log` \u65F6\u4E0D\u628A\u574F\u884C\u4E00\u8D77\u4E22\u6389\u3002\n */\nfunction filterLogText(text: string, opts: DeleteDiskLogsOpts): { text: string; removed: number } {\n if (!text) return { text: \"\", removed: 0 };\n const newline = text.includes(\"\\r\\n\") ? \"\\r\\n\" : \"\\n\";\n const ended = text.endsWith(\"\\n\");\n const lines = text.split(/\\r?\\n/);\n if (ended) lines.pop();\n let removed = 0;\n const kept: string[] = [];\n for (const raw of lines) {\n const entry = raw ? parseDiskLogLine(raw) : null;\n if (entry && diskLineMatchesRemoval(entry, opts)) {\n removed++;\n continue;\n }\n kept.push(raw);\n }\n if (!kept.length) return { text: \"\", removed };\n return { text: kept.join(newline) + (ended ? newline : \"\"), removed };\n}\n\n/**\n * \u6309\u65F6\u95F4\u8303\u56F4\u5220\u9664\u5DF2\u77E5\u65E5\u5FD7\u6587\u4EF6\u4E2D\u7684\u884C\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A\u684C\u9762 logs.delete\u3002\u7B26\u53F7\u94FE\u63A5\u548C\u975E\u6587\u4EF6\u76F4\u63A5\u62D2\u7EDD\u3002\n * \u5199\u5165\u524D\u4F1A\u518D\u8BFB\u4E00\u6B21\u671F\u95F4\u8FFD\u52A0\u7684\u5185\u5BB9\uFF1B\u66FF\u6362\u6587\u4EF6\u7684\u77AC\u95F4\u65B0\u5199\u5165\u7684\u884C\u53EF\u80FD\u7559\u4E0B\u6765\u3002\n */\nexport function deleteStoredLogs(opts: DeleteDiskLogsOpts): { removed: number } {\n if (opts.until.getTime() < opts.since.getTime()) throw new Error(\"\u7ED3\u675F\u65F6\u95F4\u4E0D\u80FD\u65E9\u4E8E\u5F00\u59CB\u65F6\u95F4\");\n const dir = opts.dir ?? logsDir(ROOT);\n let removed = 0;\n for (const name of DISK_LOG_FILES) {\n const file = path.join(dir, `${name}.log`);\n removed += rewriteLogFile(file, opts);\n }\n return { removed };\n}\n\n/** \u6539\u5199\u4E00\u4E2A\u65E5\u5FD7\u6587\u4EF6\u3002\u6587\u4EF6\u4E0D\u5B58\u5728\u5219\u8DF3\u8FC7\u3002 */\nfunction rewriteLogFile(file: string, opts: DeleteDiskLogsOpts): number {\n let stat: fs.Stats;\n try {\n stat = fs.lstatSync(file);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return 0;\n throw error;\n }\n if (stat.isSymbolicLink()) throw new Error(`\u62D2\u7EDD\u6539\u5199\u7B26\u53F7\u94FE\u63A5\u65E5\u5FD7 ${path.basename(file)}`);\n if (!stat.isFile()) throw new Error(`\u62D2\u7EDD\u6539\u5199\u975E\u6587\u4EF6\u65E5\u5FD7 ${path.basename(file)}`);\n let text = fs.readFileSync(file, \"utf8\");\n if (fs.statSync(file).size > Buffer.byteLength(text)) text = fs.readFileSync(file, \"utf8\");\n const filtered = filterLogText(text, opts);\n if (!filtered.removed) return 0;\n const temp = `${file}.${process.pid}.filtering`;\n fs.writeFileSync(temp, filtered.text, \"utf8\");\n try {\n fs.copyFileSync(temp, file);\n } finally {\n fs.rmSync(temp, { force: true });\n }\n return filtered.removed;\n}\n\n/* ==================================================================\n * \u6765\u6E90\u6807\u7B7E\u5143\u6570\u636E (\u65B9\u62EC\u53F7\u4E0E\u6587\u5B57\u540C\u8272,\u5BF9\u9F50 levelTag)\n * ================================================================== */\n\nexport interface SourceMeta {\n value: string;\n /** \u65B9\u62EC\u53F7\u5185\u56FA\u5B9A 3 \u5B57\u7B26\uFF0C\u4E0E [INF] \u5BF9\u9F50 */\n name: string;\n paint: (s: string) => string;\n}\n\nexport const SOURCE_META: SourceMeta[] = [\n { value: \"bds\", name: \"BDS\", paint: (s) => c.green(s) },\n { value: \"db\", name: \"DB \", paint: (s) => c.blue(s) },\n { value: \"qq\", name: \"QQ \", paint: (s) => c.purple(s) },\n { value: \"llbot\", name: \"BOT\", paint: (s) => c.yellow(s) },\n { value: \"system\", name: \"SYS\", paint: (s) => c.cyan(s) },\n { value: \"update\", name: \"UPD\", paint: (s) => c.orange(s) },\n { value: \"pack\", name: \"PAK\", paint: (s) => c.purple(s) },\n { value: \"bds-tools\", name: \"TOL\", paint: (s) => c.red(s) },\n];\n\n/** \u6E90\u6807\u7B7E\u65E0\u8272\u6587\u672C\uFF08formatSourceTag / logPrefixWidth \u5171\u7528\uFF09 */\nfunction sourceTagPlain(source: string): string {\n const meta = SOURCE_META.find((m) => m.value === source);\n if (meta) return `[${meta.name}]`;\n return `[${source.toUpperCase().padEnd(3).slice(0, 3)}]`;\n}\n\nexport function formatSourceTag(source: string): string {\n const meta = SOURCE_META.find((m) => m.value === source);\n const plain = sourceTagPlain(source);\n if (meta) return meta.paint(plain);\n return c.bold(plain);\n}\n\n/**\n * \u5C55\u793A\u7528\u7EA7\u522B\uFF1ABDS \u82E5\u4ECD\u5E26\u65F6\u95F4\u6233\u524D\u7F00\u5219\u89E3\u6790\u5185\u5D4C\u7EA7\u522B\uFF1B\u5DF2\u5265\u79BB\uFF08pushLog \u5165\u5E93\uFF09\u5219\u4FE1\u4EFB entry.level\u3002\n * \u7981\u6B62\u5BF9\u6B63\u6587\u505A\u677E\u6563\u5173\u952E\u8BCD\u5339\u914D\uFF08\u4F1A\u628A `[Commands] Error on line` \u8BEF\u5224\u4E3A error\uFF09\u3002\n */\nexport function resolveDisplayLevel(l: UnifiedLog): LogLevel {\n if (l.source === \"bds\") {\n const embedded = parseBdsEmbeddedLevel(l.text);\n if (embedded) return embedded;\n }\n return l.level;\n}\n\n/** \u4ECE\u539F\u59CB\u6587\u672C\u63A8\u65AD\u7EA7\u522B\uFF1A\u59D4\u6258\u5171\u4EAB\u5305\uFF08\u5185\u542B BDS \u65F6\u95F4\u6233\u89E3\u6790\uFF09 */\nexport function inferLevel(text: string): LogLevel {\n return sharedInferLevel(text);\n}\n\n/** \u65E0\u8272\u7EA7\u522B\u6807\u7B7E\u6587\u672C\uFF08\u53EF\u89C1\u5BBD\u5EA6\u6743\u5A01\u6E90\uFF1B\u62EC\u53F7\u5185\u5747\u4E3A 3 \u5B57\u7B26\uFF09 */\nconst LEVEL_TAG_TEXT: Record<LogLevel, string> = {\n error: \"[ERR]\",\n warn: \"[WRN]\",\n success: \"[SUC]\",\n debug: \"[DBG]\",\n info: \"[INF]\",\n};\n\nfunction levelTagPlain(lvl: LogLevel): string {\n return LEVEL_TAG_TEXT[lvl] ?? LEVEL_TAG_TEXT.info;\n}\n\nfunction levelTag(lvl: LogLevel): string {\n const text = levelTagPlain(lvl);\n switch (lvl) {\n case \"error\":\n return c.red(text);\n case \"warn\":\n return c.yellow(text);\n case \"success\":\n return c.green(c.bold(text));\n case \"debug\":\n return c.dim(text);\n default:\n return c.blue(text);\n }\n}\n\n/** \u683C\u5F0F\u5316\u65E5\u5FD7\u7528\u4E8E REPL \u5C55\u793A (\u7528 theme.ts chalk \u914D\u8272) */\nexport function formatLog(l: UnifiedLog): string {\n const ts = c.dim(l.time.toLocaleTimeString());\n const level = resolveDisplayLevel(l);\n const lvl = levelTag(level);\n const src = formatSourceTag(l.source);\n /* BDS\uFF1A\u53BB\u6389\u81EA\u5E26\u65F6\u95F4\u6233+\u7B49\u7EA7\u524D\u7F00\u540E\u518D\u672C\u5730\u5316\u7FFB\u8BD1\u4E0E\u9AD8\u4EAE\u6B63\u6587\uFF08push \u65F6\u591A\u534A\u5DF2\u8F6C\u8FC7\uFF0C\u6B64\u5904\u5E42\u7B49\u4E14\u8986\u76D6\u56DE\u653E\uFF09 */\n const body = l.source === \"bds\" ? stripBdsLogPrefix(l.text) : l.text;\n const filter = getCompiledLogFilter();\n const { entry } = transformLogEntry({ text: body, source: l.source, level }, filter);\n const txt = highlightLogLine(entry.text);\n return `${ts} ${src} ${lvl} ${txt}`;\n}\n\n/* ==================================================================\n * \u8F6F\u6362\u884C: \u8D85\u7EC8\u7AEF\u5BBD\u5EA6\u65F6\u6362\u884C,\u540E\u7EED\u884C\u7F29\u8FDB\u5BF9\u9F50\n * ================================================================== */\n\n/** \u5355\u4E2A\u5B57\u7B26\u5728\u7EC8\u7AEF\u7684\u663E\u793A\u5BBD\u5EA6 (CJK \u5168\u89D2=2, \u5176\u4F59=1) */\nfunction charWidth(ch: string): number {\n const cp = ch.codePointAt(0) ?? 0;\n if (\n cp >= 0x1100 &&\n (cp <= 0x115f ||\n (cp >= 0x2e80 && cp <= 0xa4cf && cp !== 0x303f) ||\n (cp >= 0xac00 && cp <= 0xd7a3) ||\n (cp >= 0xf900 && cp <= 0xfaff) ||\n (cp >= 0xfe30 && cp <= 0xfe4f) ||\n (cp >= 0xff00 && cp <= 0xff60) ||\n (cp >= 0xffe0 && cp <= 0xffe6) ||\n (cp >= 0x20000 && cp <= 0x2fffd))\n )\n return 2;\n return 1;\n}\n\n/** \u5B57\u7B26\u4E32\u5728\u7EC8\u7AEF\u7684\u53EF\u89C1\u5BBD\u5EA6 (strip ANSI \u540E\u6309 CJK \u5BBD\u5EA6\u7D2F\u52A0) */\nexport function visibleWidth(s: string): number {\n const stripped = s.replace(/\\x1b\\[[0-9;]*m/g, \"\");\n let w = 0;\n for (const ch of stripped) w += charWidth(ch);\n return w;\n}\n\n/**\n * \u65E5\u5FD7\u524D\u7F00\u53EF\u89C1\u5BBD\u5EA6(\u65F6\u95F4 + \u6E90 + \u7EA7\u522B + \u5C3E\u7A7A\u683C),\u4F9B\u60AC\u6302\u7F29\u8FDB\u5BF9\u9F50\u6B63\u6587\u8D77\u70B9\u3002\n * \u4E0E formatLog \u62FC\u63A5\u987A\u5E8F\u4FDD\u6301\u4E00\u81F4\uFF08\u5171\u7528 resolveDisplayLevel / sourceTagPlain / levelTagPlain\uFF09\u3002\n */\nexport function logPrefixWidth(l: UnifiedLog): number {\n const ts = l.time.toLocaleTimeString();\n const src = sourceTagPlain(l.source);\n const level = resolveDisplayLevel(l);\n return visibleWidth(`${ts} ${src} ${levelTagPlain(level)} `);\n}\n\n/**\n * \u6309\u7EC8\u7AEF\u53EF\u89C1\u5BBD\u5EA6\u6362\u884C;\u663E\u5F0F \\\\n \u4E0E\u8F6F\u6362\u884C\u7684\u540E\u7EED\u884C\u4E00\u5F8B\u5DE6\u4FA7\u7F29\u8FDB indent \u5217\u3002\n * \u4FDD\u7559 ANSI:\u6362\u884C\u65F6 reset,\u65B0\u884C\u6062\u590D\u5F53\u524D\u989C\u8272\u72B6\u6001(\u53EF\u591A\u5C42\u53E0\u52A0)\u3002\n * \u5BBD\u5B57\u7B26(CJK)\u6309 2 \u5217;cols \u9884\u7559 1 \u5217,\u907F\u514D Windows Terminal \u8FB9\u754C\u518D\u8F6F\u6298\u5230\u7B2C 0 \u5217\u3002\n */\nexport function wrapLogLine(s: string, indent: number): string {\n const rawCols = process.stdout.columns || 80;\n /* \u81F3\u5C11\u7559\u7ED9\u6B63\u6587\u82E5\u5E72\u5217,\u907F\u514D indent >= cols \u65F6\u6B7B\u5FAA\u73AF */\n const cols = Math.max(rawCols - 1, indent + 4);\n const segments = s.split(/\\r?\\n/);\n const out: string[] = [];\n for (let si = 0; si < segments.length; si++) {\n const raw = segments[si]!;\n /* \u663E\u5F0F\u6362\u884C\u6BB5\u53BB\u6389\u6587\u6848\u81EA\u5E26\u7684\u5DE6\u7A7A\u683C,\u7EDF\u4E00\u7531 hang indent \u5BF9\u9F50\u6B63\u6587 */\n const piece = si === 0 ? raw : raw.replace(/^[ \\t]+/, \"\");\n out.push(wrapSegment(piece, cols, si === 0 ? 0 : indent, indent));\n }\n return out.join(\"\\n\");\n}\n\n/** \u5355\u6BB5(\u65E0\u5185\u5D4C \\\\n)\u8F6F\u6362\u884C;startPad>0 \u8868\u793A\u672C\u6BB5\u5F00\u5934\u5DF2\u5360\u7528\u7684\u5217(\u663E\u5F0F\u6362\u884C\u540E\u7684\u60AC\u6302) */\nfunction wrapSegment(s: string, cols: number, startPad: number, hangIndent: number): string {\n if (s.length === 0) return startPad > 0 ? \" \".repeat(startPad) : \"\";\n\n const lines: string[] = [];\n let cur = startPad > 0 ? \" \".repeat(startPad) : \"\";\n let w = startPad;\n /** \u5F53\u524D\u751F\u6548\u7684 ANSI \u5F00\u7801\u6808(\u4E0D\u542B reset),\u6362\u884C\u540E\u91CD\u653E */\n const ansiStack: string[] = [];\n\n const resetIfNeeded = (): void => {\n if (ansiStack.length) cur += \"\\x1b[0m\";\n };\n const replayAnsi = (): void => {\n for (const code of ansiStack) cur += code;\n };\n\n let i = 0;\n while (i < s.length) {\n const m = /^\\x1b\\[[0-9;]*m/.exec(s.slice(i));\n if (m) {\n const code = m[0]!;\n cur += code;\n if (code === \"\\x1b[0m\") ansiStack.length = 0;\n else ansiStack.push(code);\n i += code.length;\n continue;\n }\n\n const cp = s.codePointAt(i)!;\n const ch = String.fromCodePoint(cp);\n const cw = charWidth(ch);\n const advance = ch.length;\n\n /* w > startPad:\u5F53\u524D\u884C\u9664\u5DE6\u57AB\u5916\u5DF2\u6709\u5185\u5BB9,\u53EF\u4EE5\u62C6\u884C;\u5426\u5219\u5F3A\u5236\u585E\u5165\u907F\u514D\u6B7B\u5FAA\u73AF */\n if (w + cw > cols && w > startPad) {\n resetIfNeeded();\n lines.push(cur);\n cur = \" \".repeat(hangIndent);\n replayAnsi();\n w = hangIndent;\n startPad = hangIndent;\n }\n\n cur += ch;\n w += cw;\n i += advance;\n }\n\n if (ansiStack.length) cur += \"\\x1b[0m\";\n lines.push(cur);\n return lines.join(\"\\n\");\n}\n\n"],
5
+ "mappings": "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",
6
6
  "names": ["parseBdsEmbeddedLevel", "stripBdsLogPrefix", "sink"]
7
7
  }
@@ -12,7 +12,7 @@ async function deploymentPreflight(ports) {
12
12
  if (path.resolve(ROOT) === path.parse(ROOT).root) throw new Error("\u4E0D\u80FD\u5728\u78C1\u76D8\u6839\u76EE\u5F55\u521B\u5EFA\u90E8\u7F72");
13
13
  for (const file of ["BDS/bedrock_server.exe", "BDS/bedrock_server", "data/sfmc_data.db"]) if (fs.existsSync(path.join(ROOT, file))) throw new Error("\u5DF2\u6709 BDS \u6216\u6570\u636E\u5E93\uFF0C\u8BF7\u63A5\u5165\u73B0\u6709\u90E8\u7F72\uFF0C\u4E0D\u8986\u76D6\u6570\u636E");
14
14
  const space = fs.statfsSync(ROOT);
15
- if (space.bavail * space.bsize < 1024 ** 3) throw new Error("\u53EF\u7528\u7A7A\u95F4\u4E0D\u8DB3 1 GiB\uFF0C\u65E0\u6CD5\u51C6\u5907\u8FD0\u884C\u65F6\u3001BDS \u548C\u7EF4\u62A4\u5907\u4EFD");
15
+ if (space.bavail * space.bsize < 1024 ** 3) throw new Error("\u53EF\u7528\u7A7A\u95F4\u4E0D\u8DB3 1 GiB\uFF0C\u65E0\u6CD5\u51C6\u5907\u8FD0\u884C\u65F6\u548C BDS");
16
16
  await new Promise((resolve, reject) => {
17
17
  const server = net.createServer();
18
18
  server.once("error", (error) => reject(new Error(`DB \u7AEF\u53E3 ${ports.db} \u4E0D\u53EF\u7528: ${error.message}`)));
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/management/deployment.ts"],
4
- "sourcesContent": ["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport net from \"node:net\";\nimport dgram from \"node:dgram\";\nimport { ROOT } from \"../runtime.js\";\nexport function deploymentPort(value: unknown, fallback: number) {\n const port = value === undefined ? fallback : Number(value);\n if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error(\"\u7AEF\u53E3\u5FC5\u987B\u4E3A 1 \u5230 65535 \u7684\u6574\u6570\");\n return port;\n}\nexport async function deploymentPreflight(ports: { db: number; bds: number; bds6: number }) {\n if (path.resolve(ROOT) === path.parse(ROOT).root) throw new Error(\"\u4E0D\u80FD\u5728\u78C1\u76D8\u6839\u76EE\u5F55\u521B\u5EFA\u90E8\u7F72\");\n for (const file of [\"BDS/bedrock_server.exe\", \"BDS/bedrock_server\", \"data/sfmc_data.db\"]) if (fs.existsSync(path.join(ROOT, file))) throw new Error(\"\u5DF2\u6709 BDS \u6216\u6570\u636E\u5E93\uFF0C\u8BF7\u63A5\u5165\u73B0\u6709\u90E8\u7F72\uFF0C\u4E0D\u8986\u76D6\u6570\u636E\");\n const space = fs.statfsSync(ROOT);\n if (space.bavail * space.bsize < 1024 ** 3) throw new Error(\"\u53EF\u7528\u7A7A\u95F4\u4E0D\u8DB3 1 GiB\uFF0C\u65E0\u6CD5\u51C6\u5907\u8FD0\u884C\u65F6\u3001BDS \u548C\u7EF4\u62A4\u5907\u4EFD\");\n await new Promise<void>((resolve, reject) => {\n const server = net.createServer(); server.once(\"error\", error => reject(new Error(`DB \u7AEF\u53E3 ${ports.db} \u4E0D\u53EF\u7528: ${error.message}`)));\n server.listen(ports.db, \"0.0.0.0\", () => server.close(error => error ? reject(error) : resolve()));\n });\n for (const [port, type, address] of [[ports.bds, \"udp4\", \"0.0.0.0\"], [ports.bds6, \"udp6\", \"::\"]] as const) {\n await new Promise<void>((resolve, reject) => {\n const socket = dgram.createSocket(type);\n socket.once(\"error\", error => { socket.close(); reject(new Error(`BDS \u7AEF\u53E3 ${port} \u4E0D\u53EF\u7528: ${error.message}`)); });\n socket.bind(port, address, () => socket.close(() => resolve()));\n });\n }\n}\n"],
5
- "mappings": "AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,OAAO,SAAS;AAChB,OAAO,WAAW;AAClB,SAAS,YAAY;AACd,SAAS,eAAe,OAAgB,UAAkB;AAC/D,QAAM,OAAO,UAAU,SAAY,WAAW,OAAO,KAAK;AAC1D,MAAI,CAAC,OAAO,UAAU,IAAI,KAAK,OAAO,KAAK,OAAO,MAAO,OAAM,IAAI,MAAM,kEAAqB;AAC9F,SAAO;AACT;AACA,eAAsB,oBAAoB,OAAkD;AAC1F,MAAI,KAAK,QAAQ,IAAI,MAAM,KAAK,MAAM,IAAI,EAAE,KAAM,OAAM,IAAI,MAAM,0EAAc;AAChF,aAAW,QAAQ,CAAC,0BAA0B,sBAAsB,mBAAmB,EAAG,KAAI,GAAG,WAAW,KAAK,KAAK,MAAM,IAAI,CAAC,EAAG,OAAM,IAAI,MAAM,+HAA2B;AAC/K,QAAM,QAAQ,GAAG,WAAW,IAAI;AAChC,MAAI,MAAM,SAAS,MAAM,QAAQ,QAAQ,EAAG,OAAM,IAAI,MAAM,oIAAgC;AAC5F,QAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,UAAM,SAAS,IAAI,aAAa;AAAG,WAAO,KAAK,SAAS,WAAS,OAAO,IAAI,MAAM,mBAAS,MAAM,EAAE,wBAAS,MAAM,OAAO,EAAE,CAAC,CAAC;AAC7H,WAAO,OAAO,MAAM,IAAI,WAAW,MAAM,OAAO,MAAM,WAAS,QAAQ,OAAO,KAAK,IAAI,QAAQ,CAAC,CAAC;AAAA,EACnG,CAAC;AACD,aAAW,CAAC,MAAM,MAAM,OAAO,KAAK,CAAC,CAAC,MAAM,KAAK,QAAQ,SAAS,GAAG,CAAC,MAAM,MAAM,QAAQ,IAAI,CAAC,GAAY;AACzG,UAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,YAAM,SAAS,MAAM,aAAa,IAAI;AACtC,aAAO,KAAK,SAAS,WAAS;AAAE,eAAO,MAAM;AAAG,eAAO,IAAI,MAAM,oBAAU,IAAI,wBAAS,MAAM,OAAO,EAAE,CAAC;AAAA,MAAG,CAAC;AAC5G,aAAO,KAAK,MAAM,SAAS,MAAM,OAAO,MAAM,MAAM,QAAQ,CAAC,CAAC;AAAA,IAChE,CAAC;AAAA,EACH;AACF;",
4
+ "sourcesContent": ["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport net from \"node:net\";\nimport dgram from \"node:dgram\";\nimport { ROOT } from \"../runtime.js\";\nexport function deploymentPort(value: unknown, fallback: number) {\n const port = value === undefined ? fallback : Number(value);\n if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error(\"\u7AEF\u53E3\u5FC5\u987B\u4E3A 1 \u5230 65535 \u7684\u6574\u6570\");\n return port;\n}\nexport async function deploymentPreflight(ports: { db: number; bds: number; bds6: number }) {\n if (path.resolve(ROOT) === path.parse(ROOT).root) throw new Error(\"\u4E0D\u80FD\u5728\u78C1\u76D8\u6839\u76EE\u5F55\u521B\u5EFA\u90E8\u7F72\");\n for (const file of [\"BDS/bedrock_server.exe\", \"BDS/bedrock_server\", \"data/sfmc_data.db\"]) if (fs.existsSync(path.join(ROOT, file))) throw new Error(\"\u5DF2\u6709 BDS \u6216\u6570\u636E\u5E93\uFF0C\u8BF7\u63A5\u5165\u73B0\u6709\u90E8\u7F72\uFF0C\u4E0D\u8986\u76D6\u6570\u636E\");\n const space = fs.statfsSync(ROOT);\n if (space.bavail * space.bsize < 1024 ** 3) throw new Error(\"\u53EF\u7528\u7A7A\u95F4\u4E0D\u8DB3 1 GiB\uFF0C\u65E0\u6CD5\u51C6\u5907\u8FD0\u884C\u65F6\u548C BDS\");\n await new Promise<void>((resolve, reject) => {\n const server = net.createServer(); server.once(\"error\", error => reject(new Error(`DB \u7AEF\u53E3 ${ports.db} \u4E0D\u53EF\u7528: ${error.message}`)));\n server.listen(ports.db, \"0.0.0.0\", () => server.close(error => error ? reject(error) : resolve()));\n });\n for (const [port, type, address] of [[ports.bds, \"udp4\", \"0.0.0.0\"], [ports.bds6, \"udp6\", \"::\"]] as const) {\n await new Promise<void>((resolve, reject) => {\n const socket = dgram.createSocket(type);\n socket.once(\"error\", error => { socket.close(); reject(new Error(`BDS \u7AEF\u53E3 ${port} \u4E0D\u53EF\u7528: ${error.message}`)); });\n socket.bind(port, address, () => socket.close(() => resolve()));\n });\n }\n}\n"],
5
+ "mappings": "AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,OAAO,SAAS;AAChB,OAAO,WAAW;AAClB,SAAS,YAAY;AACd,SAAS,eAAe,OAAgB,UAAkB;AAC/D,QAAM,OAAO,UAAU,SAAY,WAAW,OAAO,KAAK;AAC1D,MAAI,CAAC,OAAO,UAAU,IAAI,KAAK,OAAO,KAAK,OAAO,MAAO,OAAM,IAAI,MAAM,kEAAqB;AAC9F,SAAO;AACT;AACA,eAAsB,oBAAoB,OAAkD;AAC1F,MAAI,KAAK,QAAQ,IAAI,MAAM,KAAK,MAAM,IAAI,EAAE,KAAM,OAAM,IAAI,MAAM,0EAAc;AAChF,aAAW,QAAQ,CAAC,0BAA0B,sBAAsB,mBAAmB,EAAG,KAAI,GAAG,WAAW,KAAK,KAAK,MAAM,IAAI,CAAC,EAAG,OAAM,IAAI,MAAM,+HAA2B;AAC/K,QAAM,QAAQ,GAAG,WAAW,IAAI;AAChC,MAAI,MAAM,SAAS,MAAM,QAAQ,QAAQ,EAAG,OAAM,IAAI,MAAM,sGAA2B;AACvF,QAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,UAAM,SAAS,IAAI,aAAa;AAAG,WAAO,KAAK,SAAS,WAAS,OAAO,IAAI,MAAM,mBAAS,MAAM,EAAE,wBAAS,MAAM,OAAO,EAAE,CAAC,CAAC;AAC7H,WAAO,OAAO,MAAM,IAAI,WAAW,MAAM,OAAO,MAAM,WAAS,QAAQ,OAAO,KAAK,IAAI,QAAQ,CAAC,CAAC;AAAA,EACnG,CAAC;AACD,aAAW,CAAC,MAAM,MAAM,OAAO,KAAK,CAAC,CAAC,MAAM,KAAK,QAAQ,SAAS,GAAG,CAAC,MAAM,MAAM,QAAQ,IAAI,CAAC,GAAY;AACzG,UAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,YAAM,SAAS,MAAM,aAAa,IAAI;AACtC,aAAO,KAAK,SAAS,WAAS;AAAE,eAAO,MAAM;AAAG,eAAO,IAAI,MAAM,oBAAU,IAAI,wBAAS,MAAM,OAAO,EAAE,CAAC;AAAA,MAAG,CAAC;AAC5G,aAAO,KAAK,MAAM,SAAS,MAAM,OAAO,MAAM,MAAM,QAAQ,CAAC,CAAC;AAAA,IAChE,CAAC;AAAA,EACH;AACF;",
6
6
  "names": []
7
7
  }
@@ -22,10 +22,22 @@ export declare function searchModules(query: string): Promise<{
22
22
  sdk?: string;
23
23
  repo?: string;
24
24
  tag?: string;
25
+ name?: string;
26
+ description?: string;
27
+ category?: string;
28
+ license?: string;
29
+ official?: boolean;
30
+ requires?: string[];
25
31
  id: string;
26
32
  }[];
27
33
  stale: boolean;
28
34
  }>;
35
+ /**
36
+ * 安装前核对索引里的 requires。
37
+ * 使用场景:modules.install 在创建下载任务之前调用。索引没有这个 id 时返回 null,继续原来的安装路径。
38
+ * 有条目但没有合法 requires,或依赖未安装、未启用时返回说明,调用方不得开始安装。
39
+ */
40
+ export declare function moduleInstallBlock(id: string): Promise<string | null>;
29
41
  export declare function runFetch(args: string[]): Promise<{
30
42
  changed: boolean;
31
43
  }>;
@@ -44,13 +56,45 @@ export declare function listPacks(): {
44
56
  kind: import("@sfmc-bds/bds-tools/world-packs").WorldPackKind;
45
57
  enabled: boolean;
46
58
  version: string;
59
+ iconStamp?: string;
60
+ protected?: boolean;
47
61
  }[];
48
62
  };
49
- export declare function togglePack(id: string, enabled: boolean): Promise<{
63
+ /**
64
+ * 读取某个已安装世界包的 pack_icon.png。
65
+ * 使用场景:桌面世界包列表按 iconStamp 按需拉取,不放进每 5 秒的 packs.list。
66
+ * 找不到包、文件缺失或内容不是 PNG 时返回空图标,不抛错。
67
+ */
68
+ export declare function readPackIcon(id: string): {
69
+ id: string;
70
+ mime: "image/png" | null;
71
+ data: string | null;
72
+ };
73
+ /**
74
+ * 卸载一个世界包,并沿用 CLI 的配对资源包与回收站规则。
75
+ * 使用场景:桌面 packs.uninstall。平台聚合包直接拒绝。
76
+ */
77
+ export declare function uninstallPack(id: string): Promise<{
50
78
  changed: boolean;
51
79
  restartRequired: boolean;
80
+ removed: import("../world-packs.js").PackUninstallOutcome[];
52
81
  }>;
53
- export declare function importPack(filename: string): Promise<{
82
+ /**
83
+ * 解析桌面传入的资源包版本。
84
+ * 使用场景:packs.version。只接受 major.minor.patch,每一段都是非负安全整数。
85
+ */
86
+ export declare function parsePackVersion(value: unknown): [number, number, number];
87
+ /**
88
+ * 修改一个已安装资源包的清单版本,并在已启用时同步世界启用列表。
89
+ * 使用场景:桌面 packs.version。行为包直接拒绝。
90
+ * 模块条目写成与 header 相同的完整版本;命令行 packs bump 仍只抬修订号。
91
+ */
92
+ export declare function setPackVersion(id: string, versionText: unknown): Promise<{
93
+ changed: boolean;
94
+ restartRequired: boolean;
95
+ version: string;
96
+ }>;
97
+ export declare function togglePack(id: string, enabled: boolean): Promise<{
54
98
  changed: boolean;
55
99
  restartRequired: boolean;
56
100
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"domain.d.ts","sourceRoot":"","sources":["../../src/management/domain.ts"],"names":[],"mappings":"AAWA;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7C;AACD,wBAAgB,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,CAGxD;AACD,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAGjD;AACD,wBAAsB,WAAW;;;;;;;;;GAMhC;AAED,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM;;;;;;;;;;GAGhD;AACD,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE;;GAG5C;AACD,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAOvH;AACD,wBAAsB,UAAU,CAAC,GAAG,GAAE,MAAM,EAAO,oBAIlD;AACD,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE;YAI+B;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE;GAQlH;AACD,wBAAgB,SAAS;IAEd,KAAK;;;;;;;EACf;AACD,wBAAsB,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO;;;GAO5D;AACD,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM;;;GAiBhD;AACD,wBAAgB,WAAW;IAEhB,SAAS;IAAoC,WAAW;IAAsC,eAAe;EACvH;AACD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO;IAWzD,KAAK;EACf"}
1
+ {"version":3,"file":"domain.d.ts","sourceRoot":"","sources":["../../src/management/domain.ts"],"names":[],"mappings":"AAaA;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7C;AACD,wBAAgB,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,CAGxD;AACD,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAGjD;AACD,wBAAsB,WAAW;;;;;;;;;GAMhC;AAED,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM;;;;;;;;;;;;;;;;GAehD;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAQ3E;AACD,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE;;GAG5C;AACD,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAOvH;AACD,wBAAsB,UAAU,CAAC,GAAG,GAAE,MAAM,EAAO,oBAIlD;AACD,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE;YAI+B;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE;GAQlH;AAkBD,wBAAgB,SAAS;IAEd,KAAK;;;;;;;;;EAYf;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAUtG;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,EAAE,EAAE,MAAM;;;;GAS7C;AACD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAezE;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO;;;;GAcpE;AAED,wBAAsB,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO;;;GAO5D;AACD,wBAAgB,WAAW;IAEhB,SAAS;IAAoC,WAAW;IAAsC,eAAe;EACvH;AACD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO;IAWzD,KAAK;EACf"}
@@ -5,8 +5,10 @@ import { spawn } from "node:child_process";
5
5
  import { resolveRegistryIndex } from "../registry.js";
6
6
  import { ROOT, resolveFetchModule } from "../runtime.js";
7
7
  import { resolveBdsContext, scanLocalModules } from "../pack-lifecycle.js";
8
- import { listInstalledWorldPacks, resolvePackRoots, readPackManifestInfo, installPackDirectory, enableInstalledPack, disableInstalledPack, worldPackParentDir } from "@sfmc-bds/bds-tools/world-packs";
8
+ import { listInstalledWorldPacks, enableInstalledPack, disableInstalledPack, writePackHeaderVersion } from "@sfmc-bds/bds-tools/world-packs";
9
+ import { buildPackUninstallQueue, isProtectedSfmcPack, resolveUninstallTargets, runPackUninstallQueue } from "../world-packs.js";
9
10
  import { atomicJson } from "./tasks.js";
11
+ import { assessModuleRequires, moduleRequireBlockMessage } from "@sfmc-bds/management";
10
12
  import { isRetiredPlatformModule } from "@sfmc-bds/sdk/contracts";
11
13
  function stripBom(text) {
12
14
  return text.charCodeAt(0) === 65279 ? text.slice(1) : text;
@@ -35,8 +37,28 @@ function fetchEntry() {
35
37
  return entry;
36
38
  }
37
39
  async function searchModules(query) {
38
- const result = await resolveRegistryIndex();
39
- return { modules: Object.entries(result.index).filter(([id]) => !isRetiredPlatformModule(id) && id.includes(query)).map(([id, metadata]) => ({ id, ...metadata })), stale: result.stale };
40
+ let result = await resolveRegistryIndex();
41
+ if (Object.values(result.index).some((entry) => !Array.isArray(entry.requires))) {
42
+ result = await resolveRegistryIndex({ force: true });
43
+ }
44
+ const needle = query.trim().toLowerCase();
45
+ return {
46
+ modules: Object.entries(result.index).filter(([id, metadata]) => {
47
+ if (isRetiredPlatformModule(id)) return false;
48
+ if (!needle) return true;
49
+ return [id, metadata.name, metadata.description, metadata.npm].some((value) => value?.toLowerCase().includes(needle));
50
+ }).map(([id, metadata]) => ({ id, ...metadata })),
51
+ stale: result.stale
52
+ };
53
+ }
54
+ async function moduleInstallBlock(id) {
55
+ let result = await resolveRegistryIndex();
56
+ const cached = result.index[id];
57
+ if (cached && !Array.isArray(cached.requires)) result = await resolveRegistryIndex({ force: true });
58
+ const entry = result.index[id];
59
+ if (!entry) return null;
60
+ const installed = (await listModules()).modules;
61
+ return moduleRequireBlockMessage(id, assessModuleRequires(entry.requires, installed));
40
62
  }
41
63
  async function runFetch(args) {
42
64
  await runProcess(process.execPath, [fetchEntry(), ...args], ROOT);
@@ -69,9 +91,89 @@ async function applyModulePlan(ids) {
69
91
  if (result.failed.length) throw new Error(JSON.stringify(result.failed));
70
92
  return result;
71
93
  }
94
+ const PACK_ICON_MAX_BYTES = 1572864;
95
+ const PNG_MAGIC = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
96
+ function packIconStamp(dir) {
97
+ const file = path.join(dir, "pack_icon.png");
98
+ let stat;
99
+ try {
100
+ stat = fs.lstatSync(file);
101
+ } catch {
102
+ return null;
103
+ }
104
+ if (!stat.isFile() || stat.isSymbolicLink() || stat.size <= 0 || stat.size > PACK_ICON_MAX_BYTES) return null;
105
+ return `${stat.size}:${Math.trunc(stat.mtimeMs)}`;
106
+ }
72
107
  function listPacks() {
73
108
  const context = resolveBdsContext();
74
- return { packs: listInstalledWorldPacks(context.bdsRoot, context.levelName).map((pack) => ({ id: pack.uuid, name: pack.name, kind: pack.kind, enabled: pack.enabled, version: pack.version.join(".") })) };
109
+ return { packs: listInstalledWorldPacks(context.bdsRoot, context.levelName).map((pack) => {
110
+ const iconStamp = packIconStamp(pack.dir);
111
+ return {
112
+ id: pack.uuid,
113
+ name: pack.name,
114
+ kind: pack.kind,
115
+ enabled: pack.enabled,
116
+ version: pack.version.join("."),
117
+ ...iconStamp ? { iconStamp } : {},
118
+ ...isProtectedSfmcPack(pack) ? { protected: true } : {}
119
+ };
120
+ }) };
121
+ }
122
+ function readPackIcon(id) {
123
+ const empty = { id, mime: null, data: null };
124
+ const context = resolveBdsContext();
125
+ const pack = listInstalledWorldPacks(context.bdsRoot, context.levelName).find((row) => row.uuid.toLowerCase() === id.toLowerCase());
126
+ if (!pack || !packIconStamp(pack.dir)) return empty;
127
+ const file = path.join(pack.dir, "pack_icon.png");
128
+ let data;
129
+ try {
130
+ data = fs.readFileSync(file);
131
+ } catch {
132
+ return empty;
133
+ }
134
+ if (data.length < PNG_MAGIC.length || !data.subarray(0, PNG_MAGIC.length).equals(PNG_MAGIC) || data.length > PACK_ICON_MAX_BYTES) return empty;
135
+ return { id: pack.uuid, mime: "image/png", data: data.toString("base64") };
136
+ }
137
+ async function uninstallPack(id) {
138
+ const context = resolveBdsContext();
139
+ const packs = listInstalledWorldPacks(context.bdsRoot, context.levelName);
140
+ const resolved = resolveUninstallTargets(packs, [id]);
141
+ if (resolved.status === "not_found") throw new Error("\u627E\u4E0D\u5230\u4E16\u754C\u5305");
142
+ if (resolved.status === "protected") throw new Error("\u5E73\u53F0\u805A\u5408\u5305\u4E0D\u53EF\u901A\u8FC7\u4E16\u754C\u5305\u5378\u8F7D\uFF0C\u8BF7\u4F7F\u7528\u6A21\u5757\u7BA1\u7406");
143
+ const queue = buildPackUninstallQueue(resolved.selected, packs, false);
144
+ const removed = await runPackUninstallQueue(queue, { bdsRoot: context.bdsRoot, levelName: context.levelName, purge: false });
145
+ return { changed: true, restartRequired: true, removed };
146
+ }
147
+ function parsePackVersion(value) {
148
+ if (typeof value !== "string") throw new Error("\u7248\u672C\u53F7\u5FC5\u987B\u4E3A major.minor.patch");
149
+ const parts = value.split(".");
150
+ if (parts.length !== 3) throw new Error("\u7248\u672C\u53F7\u5FC5\u987B\u4E3A major.minor.patch");
151
+ const numbers = parts.map((part) => {
152
+ if (!/^\d+$/.test(part)) throw new Error("\u7248\u672C\u53F7\u5FC5\u987B\u4E3A\u975E\u8D1F\u6574\u6570");
153
+ const number = Number(part);
154
+ if (!Number.isSafeInteger(number)) throw new Error("\u7248\u672C\u53F7\u8D85\u51FA\u53EF\u7CBE\u786E\u8868\u793A\u7684\u6574\u6570\u8303\u56F4");
155
+ return number;
156
+ });
157
+ const major = numbers[0];
158
+ const minor = numbers[1];
159
+ const patch = numbers[2];
160
+ if (major === void 0 || minor === void 0 || patch === void 0) throw new Error("\u7248\u672C\u53F7\u5FC5\u987B\u4E3A major.minor.patch");
161
+ return [major, minor, patch];
162
+ }
163
+ async function setPackVersion(id, versionText) {
164
+ const version = parsePackVersion(versionText);
165
+ const context = resolveBdsContext();
166
+ const pack = listInstalledWorldPacks(context.bdsRoot, context.levelName).find((row) => row.uuid === id);
167
+ if (!pack) throw new Error("\u627E\u4E0D\u5230\u4E16\u754C\u5305");
168
+ if (pack.kind !== "resource") throw new Error("\u53EA\u80FD\u4FEE\u6539\u8D44\u6E90\u5305\u7684\u7248\u672C\u53F7");
169
+ if (pack.version[0] === version[0] && pack.version[1] === version[1] && pack.version[2] === version[2]) {
170
+ return { changed: false, restartRequired: false, version: pack.version.join(".") };
171
+ }
172
+ const next = writePackHeaderVersion(pack.dir, version, "full");
173
+ if (pack.enabled) {
174
+ await enableInstalledPack({ ...context, info: { uuid: pack.uuid, version: next, kind: "resource", name: pack.name } });
175
+ }
176
+ return { changed: true, restartRequired: true, version: next.join(".") };
75
177
  }
76
178
  async function togglePack(id, enabled) {
77
179
  const context = resolveBdsContext();
@@ -81,26 +183,6 @@ async function togglePack(id, enabled) {
81
183
  else await disableInstalledPack({ ...context, kind: pack.kind, packUuid: pack.uuid, version: pack.version });
82
184
  return { changed: true, restartRequired: true };
83
185
  }
84
- async function importPack(filename) {
85
- const inbox = path.resolve(ROOT, "packs", "_desktop-inbox");
86
- const file = path.resolve(inbox, identifier(filename));
87
- if (!file.startsWith(inbox + path.sep) || !fs.existsSync(file)) throw new Error("\u4E16\u754C\u5305\u5C1A\u672A\u4E0A\u4F20\u5230\u5B9E\u4F8B\u6536\u4EF6\u7BB1");
88
- const context = resolveBdsContext();
89
- const resolved = await resolvePackRoots(file);
90
- try {
91
- if (!resolved.roots.length) throw new Error("\u5F52\u6863\u4E0D\u542B\u53EF\u8BC6\u522B\u7684\u4E16\u754C\u5305");
92
- for (const root of resolved.roots) {
93
- const info = readPackManifestInfo(root);
94
- if (!info) throw new Error("\u4E16\u754C\u5305 manifest \u65E0\u6548");
95
- const result = await installPackDirectory({ srcDir: root, destParent: worldPackParentDir(context.bdsRoot, context.levelName, info.kind) });
96
- if (!result.ok) throw new Error(result.reason ?? "\u5B89\u88C5\u5931\u8D25\uFF1B\u53EF\u80FD\u4E0E\u5DF2\u6709\u5305\u51B2\u7A81");
97
- await enableInstalledPack({ ...context, info });
98
- }
99
- } finally {
100
- resolved.dispose?.();
101
- }
102
- return { changed: true, restartRequired: true };
103
- }
104
186
  function playerFiles() {
105
187
  const bds = resolveBdsContext().bdsRoot;
106
188
  return { allowlist: path.join(bds, "allowlist.json"), permissions: path.join(bds, "permissions.json"), sfmcPermissions: path.join(ROOT, "configs", "permissions.json") };
@@ -121,17 +203,21 @@ function savePlayerPermissions(kind, entries) {
121
203
  export {
122
204
  applyModulePlan,
123
205
  identifier,
124
- importPack,
125
206
  listModules,
126
207
  listPacks,
208
+ moduleInstallBlock,
127
209
  modulePlan,
210
+ parsePackVersion,
128
211
  playerFiles,
129
212
  readJson,
213
+ readPackIcon,
130
214
  runFetch,
131
215
  runProcess,
132
216
  savePlayerPermissions,
133
217
  searchModules,
218
+ setPackVersion,
134
219
  stripBom,
135
- togglePack
220
+ togglePack,
221
+ uninstallPack
136
222
  };
137
223
  //# sourceMappingURL=domain.js.map