@sfmc-bds/cli 0.1.0 → 0.2.0-beta.1

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 (142) hide show
  1. package/README.md +7 -1
  2. package/dist/cli-result.d.ts +12 -0
  3. package/dist/cli-result.d.ts.map +1 -0
  4. package/dist/cli-result.js +15 -0
  5. package/dist/cli-result.js.map +7 -0
  6. package/dist/commands-behavior-pack.d.ts +11 -42
  7. package/dist/commands-behavior-pack.d.ts.map +1 -1
  8. package/dist/commands-behavior-pack.js +40 -159
  9. package/dist/commands-behavior-pack.js.map +7 -1
  10. package/dist/commands-reload.d.ts +9 -0
  11. package/dist/commands-reload.d.ts.map +1 -0
  12. package/dist/commands-reload.js +30 -0
  13. package/dist/commands-reload.js.map +7 -0
  14. package/dist/commands.d.ts +7 -0
  15. package/dist/commands.d.ts.map +1 -1
  16. package/dist/commands.js +182 -172
  17. package/dist/commands.js.map +7 -1
  18. package/dist/i18n/index.d.ts +26 -0
  19. package/dist/i18n/index.d.ts.map +1 -0
  20. package/dist/i18n/index.js +107 -0
  21. package/dist/i18n/index.js.map +7 -0
  22. package/dist/i18n/locales/en.d.ts +381 -0
  23. package/dist/i18n/locales/en.d.ts.map +1 -0
  24. package/dist/i18n/locales/en.js +393 -0
  25. package/dist/i18n/locales/en.js.map +7 -0
  26. package/dist/i18n/locales/zh-CN.d.ts +4 -0
  27. package/dist/i18n/locales/zh-CN.d.ts.map +1 -0
  28. package/dist/i18n/locales/zh-CN.js +379 -0
  29. package/dist/i18n/locales/zh-CN.js.map +7 -0
  30. package/dist/i18n/types.d.ts +7 -0
  31. package/dist/i18n/types.d.ts.map +1 -0
  32. package/dist/i18n/types.js +17 -0
  33. package/dist/i18n/types.js.map +7 -0
  34. package/dist/index.d.ts +5 -11
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +51 -43
  37. package/dist/index.js.map +7 -1
  38. package/dist/interactive-prompts.d.ts +8 -0
  39. package/dist/interactive-prompts.d.ts.map +1 -0
  40. package/dist/interactive-prompts.js +57 -0
  41. package/dist/interactive-prompts.js.map +7 -0
  42. package/dist/locale-command.d.ts +3 -0
  43. package/dist/locale-command.d.ts.map +1 -0
  44. package/dist/locale-command.js +19 -0
  45. package/dist/locale-command.js.map +7 -0
  46. package/dist/log-filter.d.ts +69 -0
  47. package/dist/log-filter.d.ts.map +1 -0
  48. package/dist/log-filter.js +150 -0
  49. package/dist/log-filter.js.map +7 -0
  50. package/dist/logs.d.ts +33 -8
  51. package/dist/logs.d.ts.map +1 -1
  52. package/dist/logs.js +232 -194
  53. package/dist/logs.js.map +7 -1
  54. package/dist/main.js +135 -173
  55. package/dist/main.js.map +7 -1
  56. package/dist/module-commands.d.ts +43 -4
  57. package/dist/module-commands.d.ts.map +1 -1
  58. package/dist/module-commands.js +440 -327
  59. package/dist/module-commands.js.map +7 -1
  60. package/dist/module-fingerprint.d.ts +7 -0
  61. package/dist/module-fingerprint.d.ts.map +1 -0
  62. package/dist/module-fingerprint.js +31 -0
  63. package/dist/module-fingerprint.js.map +7 -0
  64. package/dist/module-wizard.d.ts +14 -0
  65. package/dist/module-wizard.d.ts.map +1 -0
  66. package/dist/module-wizard.js +313 -0
  67. package/dist/module-wizard.js.map +7 -0
  68. package/dist/pack-lifecycle.d.ts +95 -0
  69. package/dist/pack-lifecycle.d.ts.map +1 -0
  70. package/dist/pack-lifecycle.js +634 -0
  71. package/dist/pack-lifecycle.js.map +7 -0
  72. package/dist/pack-update/bindings.d.ts +12 -0
  73. package/dist/pack-update/bindings.d.ts.map +1 -0
  74. package/dist/pack-update/bindings.js +64 -0
  75. package/dist/pack-update/bindings.js.map +7 -0
  76. package/dist/pack-update/config.d.ts +22 -0
  77. package/dist/pack-update/config.d.ts.map +1 -0
  78. package/dist/pack-update/config.js +141 -0
  79. package/dist/pack-update/config.js.map +7 -0
  80. package/dist/pack-update/providers/curseforge.d.ts +23 -0
  81. package/dist/pack-update/providers/curseforge.d.ts.map +1 -0
  82. package/dist/pack-update/providers/curseforge.js +229 -0
  83. package/dist/pack-update/providers/curseforge.js.map +7 -0
  84. package/dist/pack-update/providers/index.d.ts +16 -0
  85. package/dist/pack-update/providers/index.d.ts.map +1 -0
  86. package/dist/pack-update/providers/index.js +37 -0
  87. package/dist/pack-update/providers/index.js.map +7 -0
  88. package/dist/pack-update/service.d.ts +21 -0
  89. package/dist/pack-update/service.d.ts.map +1 -0
  90. package/dist/pack-update/service.js +572 -0
  91. package/dist/pack-update/service.js.map +7 -0
  92. package/dist/pack-update/types.d.ts +126 -0
  93. package/dist/pack-update/types.d.ts.map +1 -0
  94. package/dist/pack-update/types.js +8 -0
  95. package/dist/pack-update/types.js.map +7 -0
  96. package/dist/pack-update/version-policy.d.ts +81 -0
  97. package/dist/pack-update/version-policy.d.ts.map +1 -0
  98. package/dist/pack-update/version-policy.js +210 -0
  99. package/dist/pack-update/version-policy.js.map +7 -0
  100. package/dist/registry.d.ts +12 -4
  101. package/dist/registry.d.ts.map +1 -1
  102. package/dist/registry.js +73 -106
  103. package/dist/registry.js.map +7 -1
  104. package/dist/remote-agent.d.ts.map +1 -1
  105. package/dist/remote-agent.js +160 -176
  106. package/dist/remote-agent.js.map +7 -1
  107. package/dist/repl.d.ts +4 -0
  108. package/dist/repl.d.ts.map +1 -1
  109. package/dist/repl.js +683 -605
  110. package/dist/repl.js.map +7 -1
  111. package/dist/runtime.d.ts +40 -14
  112. package/dist/runtime.d.ts.map +1 -1
  113. package/dist/runtime.js +185 -62
  114. package/dist/runtime.js.map +7 -1
  115. package/dist/services.d.ts +2 -0
  116. package/dist/services.d.ts.map +1 -1
  117. package/dist/services.js +251 -238
  118. package/dist/services.js.map +7 -1
  119. package/dist/sfmc-modules-root.d.ts +24 -0
  120. package/dist/sfmc-modules-root.d.ts.map +1 -0
  121. package/dist/sfmc-modules-root.js +70 -0
  122. package/dist/sfmc-modules-root.js.map +7 -0
  123. package/dist/theme.d.ts +4 -0
  124. package/dist/theme.d.ts.map +1 -1
  125. package/dist/theme.js +134 -48
  126. package/dist/theme.js.map +7 -1
  127. package/dist/wizard.d.ts.map +1 -1
  128. package/dist/wizard.js +363 -438
  129. package/dist/wizard.js.map +7 -1
  130. package/dist/world-packs.d.ts +37 -0
  131. package/dist/world-packs.d.ts.map +1 -0
  132. package/dist/world-packs.js +872 -0
  133. package/dist/world-packs.js.map +7 -0
  134. package/package.json +10 -10
  135. package/dist/dispatcher.d.ts +0 -13
  136. package/dist/dispatcher.d.ts.map +0 -1
  137. package/dist/dispatcher.js +0 -23
  138. package/dist/dispatcher.js.map +0 -1
  139. package/dist/terminal.d.ts +0 -3
  140. package/dist/terminal.d.ts.map +0 -1
  141. package/dist/terminal.js +0 -178
  142. package/dist/terminal.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,43 +1,51 @@
1
- /**
2
- * sfmc/ public runtime surface (Stage J — minimum entry design)
3
- *
4
- * This is the clean API that downstream callers (future sfmc-cil commands,
5
- * external scripts, CI tooling) consume to manage the runtime stack
6
- * without re-implementing process lifecycle logic.
7
- *
8
- * Design:
9
- * - Single process tree. sfmc/ does not spawn itself recursively — it spawns
10
- * the actual services (db-server, qq-bridge, bds-tools, BDS).
11
- * - `IS_SEA` / `ROOT` from runtime.ts describe the deployment shape so callers
12
- * can adapt (path lookup, config dir).
13
- * - Service lifecycle goes through ServiceRegistry (start/stop/restart/status).
14
- * - Module manifest from db-server is exposed via `readModuleManifest()` so
15
- * callers can show enabled modules + per-module state.
16
- *
17
- * Re-exports are deliberately thin: each item is a stable contract. Internal
18
- * helpers (LogLine buffers, child-process piping) are NOT re-exported.
19
- */
20
- export { ROOT, IS_SEA, spawnService, spawnServiceSync } from "./runtime.js";
21
- export { SERVICE_NAMES, refreshServices, startAll, stopAll, forceStopAll, serviceStatus, services, START_ORDER, } from "./services.js";
22
- export { pushLog, onLog, getAllLogs, getRecentLogs } from "./logs.js";
23
- /**
24
- * Reads the manifest emitted by the behavior pack build, if present.
25
- * Returns null when no manifest exists (e.g. behavior pack hasn't been built).
26
- */
27
- export async function readModuleManifest() {
28
- const fs = await import("node:fs/promises");
29
- const path = await import("node:path");
30
- const { ROOT } = await import("./runtime.js");
31
- const manifestPath = path.join(ROOT, "modules", "_manifests", "module-manifests.json");
32
- try {
33
- const text = await fs.readFile(manifestPath, "utf8");
34
- return JSON.parse(text);
35
- }
36
- catch {
37
- return null;
38
- }
39
- }
40
- export { startRepl, HELP } from "./repl.js";
41
- export { runWizard } from "./wizard.js";
42
- export { cmdModuleList, cmdModuleInstall, cmdModuleUninstall, cmdModuleVerify, cmdModuleInfo } from "./module-commands.js";
43
- //# sourceMappingURL=index.js.map
1
+ import { ROOT, isMonorepoLayout, isRuntimeInitialized, resolveServiceScript, resolveFetchModule, spawnService, spawnServiceSync } from "./runtime.js";
2
+ import {
3
+ SERVICE_NAMES,
4
+ refreshServices,
5
+ startAll,
6
+ stopAll,
7
+ forceStopAll,
8
+ serviceStatus,
9
+ services,
10
+ START_ORDER
11
+ } from "./services.js";
12
+ import { pushLog, onLog, getAllLogs, getRecentLogs } from "./logs.js";
13
+ import { startRepl, getHelp, HELP } from "./repl.js";
14
+ import { t, getLocale, setLocale, initLocale } from "./i18n/index.js";
15
+ import { runWizard } from "./wizard.js";
16
+ import { cmdModuleList, cmdModuleInstall, cmdModuleUninstall, cmdModuleVerify, cmdModuleInfo } from "./module-commands.js";
17
+ export {
18
+ HELP,
19
+ ROOT,
20
+ SERVICE_NAMES,
21
+ START_ORDER,
22
+ cmdModuleInfo,
23
+ cmdModuleInstall,
24
+ cmdModuleList,
25
+ cmdModuleUninstall,
26
+ cmdModuleVerify,
27
+ forceStopAll,
28
+ getAllLogs,
29
+ getHelp,
30
+ getLocale,
31
+ getRecentLogs,
32
+ initLocale,
33
+ isMonorepoLayout,
34
+ isRuntimeInitialized,
35
+ onLog,
36
+ pushLog,
37
+ refreshServices,
38
+ resolveFetchModule,
39
+ resolveServiceScript,
40
+ runWizard,
41
+ serviceStatus,
42
+ services,
43
+ setLocale,
44
+ spawnService,
45
+ spawnServiceSync,
46
+ startAll,
47
+ startRepl,
48
+ stopAll,
49
+ t
50
+ };
51
+ //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1,7 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,gBAAgB,EAAkB,MAAM,cAAc,CAAC;AAE5F,OAAO,EACL,aAAa,EAGb,eAAe,EACf,QAAQ,EACR,OAAO,EACP,YAAY,EACZ,aAAa,EACb,QAAQ,EACR,WAAW,GACZ,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,aAAa,EAAkD,MAAM,WAAW,CAAC;AAEtH;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;IAC5C,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;IACvC,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,CAAC;IAC9C,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,YAAY,EAAE,uBAAuB,CAAC,CAAC;IACvF,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;QACrD,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC"}
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index.ts"],
4
+ "sourcesContent": ["/**\n * sfmc/ public runtime surface (Stage J \u2014 minimum entry design)\n *\n * This is the clean API that downstream callers (CLI commands,\n * external scripts, CI tooling) consume to manage the runtime stack\n * without re-implementing process lifecycle logic.\n *\n * Design:\n * - Single process tree. sfmc/ does not spawn itself recursively \u2014 it spawns\n * the actual services (db-server, qq-bridge, bds-tools, BDS).\n * - `ROOT` from runtime.ts describes the deployment root so callers\n * can adapt (path lookup, config dir).\n * - Service lifecycle goes through ServiceRegistry (start/stop/restart/status).\n *\n * Re-exports are deliberately thin: each item is a stable contract. Internal\n * helpers (LogLine buffers, child-process piping) are NOT re-exported.\n */\n\nexport { ROOT, isMonorepoLayout, isRuntimeInitialized, resolveServiceScript, resolveFetchModule, spawnService, spawnServiceSync, type ServiceId } from \"./runtime.js\";\n\nexport {\n SERVICE_NAMES,\n type ServiceName,\n type ServiceStatus,\n refreshServices,\n startAll,\n stopAll,\n forceStopAll,\n serviceStatus,\n services,\n START_ORDER,\n} from \"./services.js\";\n\nexport { pushLog, onLog, getAllLogs, getRecentLogs, type UnifiedLog, type LogLevel, type LogSource } from \"./logs.js\";\n\nexport { startRepl, getHelp, HELP } from \"./repl.js\";\nexport { t, getLocale, setLocale, initLocale, type Locale } from \"./i18n/index.js\";\nexport { runWizard } from \"./wizard.js\";\nexport { cmdModuleList, cmdModuleInstall, cmdModuleUninstall, cmdModuleVerify, cmdModuleInfo } from \"./module-commands.js\";\n"],
5
+ "mappings": "AAkBA,SAAS,MAAM,kBAAkB,sBAAsB,sBAAsB,oBAAoB,cAAc,wBAAwC;AAEvJ;AAAA,EACE;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAS,SAAS,OAAO,YAAY,qBAAqE;AAE1G,SAAS,WAAW,SAAS,YAAY;AACzC,SAAS,GAAG,WAAW,WAAW,kBAA+B;AACjE,SAAS,iBAAiB;AAC1B,SAAS,eAAe,kBAAkB,oBAAoB,iBAAiB,qBAAqB;",
6
+ "names": []
7
+ }
@@ -0,0 +1,8 @@
1
+ /** 与 init 向导一致:文本输入或系统文件夹选择器。 */
2
+ export declare function pickDirectory(message: string, defaultDirectory: string): Promise<string>;
3
+ /** Windows 文件夹选择器;非 Windows 或失败时返回 null。 */
4
+ export declare function pickDirectoryDialog(title: string, defaultDirectory: string): string | null;
5
+ export declare function ensureDirectory(directory: string): boolean;
6
+ /** 将用户输入规范为绝对路径(兼容含 # 的 Windows 路径)。 */
7
+ export declare function resolveUserPath(input: string, base?: string): string;
8
+ //# sourceMappingURL=interactive-prompts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interactive-prompts.d.ts","sourceRoot":"","sources":["../src/interactive-prompts.ts"],"names":[],"mappings":"AAMA,iCAAiC;AACjC,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAa9F;AAED,4CAA4C;AAC5C,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAqB1F;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAO1D;AAED,wCAAwC;AACxC,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,SAAgB,GAAG,MAAM,CAE3E"}
@@ -0,0 +1,57 @@
1
+ import { isCancel, select, text } from "@clack/prompts";
2
+ import { execFileSync } from "node:child_process";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { t } from "./i18n/index.js";
6
+ async function pickDirectory(message, defaultDirectory) {
7
+ const method = await select({
8
+ message,
9
+ options: [
10
+ { value: "text", label: t("prompt.enterPath"), hint: defaultDirectory },
11
+ { value: "browse", label: t("prompt.browse"), hint: t("prompt.browseHint") }
12
+ ]
13
+ });
14
+ if (isCancel(method)) return defaultDirectory;
15
+ if (method === "browse") return pickDirectoryDialog(message, defaultDirectory) ?? defaultDirectory;
16
+ const selected = await text({ message, initialValue: defaultDirectory });
17
+ return isCancel(selected) || !selected ? defaultDirectory : selected;
18
+ }
19
+ function pickDirectoryDialog(title, defaultDirectory) {
20
+ if (process.platform !== "win32") return null;
21
+ const escape = (value) => value.replace(/'/g, "''");
22
+ const script = [
23
+ "Add-Type -AssemblyName System.Windows.Forms",
24
+ "$dialog = New-Object System.Windows.Forms.FolderBrowserDialog",
25
+ `$dialog.Description = '${escape(title)}'`,
26
+ `$dialog.SelectedPath = '${escape(defaultDirectory)}'`,
27
+ "if ($dialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { Write-Output $dialog.SelectedPath }"
28
+ ].join("; ");
29
+ try {
30
+ const output = execFileSync("powershell.exe", ["-NoProfile", "-STA", "-Command", script], {
31
+ encoding: "utf-8",
32
+ timeout: 3e4,
33
+ windowsHide: true
34
+ });
35
+ return output.trim() || null;
36
+ } catch {
37
+ return null;
38
+ }
39
+ }
40
+ function ensureDirectory(directory) {
41
+ try {
42
+ fs.mkdirSync(directory, { recursive: true });
43
+ return fs.statSync(directory).isDirectory();
44
+ } catch {
45
+ return false;
46
+ }
47
+ }
48
+ function resolveUserPath(input, base = process.cwd()) {
49
+ return path.resolve(base, input.trim());
50
+ }
51
+ export {
52
+ ensureDirectory,
53
+ pickDirectory,
54
+ pickDirectoryDialog,
55
+ resolveUserPath
56
+ };
57
+ //# sourceMappingURL=interactive-prompts.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/interactive-prompts.ts"],
4
+ "sourcesContent": ["import { isCancel, select, text } from \"@clack/prompts\";\nimport { execFileSync } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { t } from \"./i18n/index.js\";\n\n/** \u4E0E init \u5411\u5BFC\u4E00\u81F4\uFF1A\u6587\u672C\u8F93\u5165\u6216\u7CFB\u7EDF\u6587\u4EF6\u5939\u9009\u62E9\u5668\u3002 */\nexport async function pickDirectory(message: string, defaultDirectory: string): Promise<string> {\n const method = await select({\n message,\n options: [\n { value: \"text\", label: t(\"prompt.enterPath\"), hint: defaultDirectory },\n { value: \"browse\", label: t(\"prompt.browse\"), hint: t(\"prompt.browseHint\") },\n ],\n });\n if (isCancel(method)) return defaultDirectory;\n if (method === \"browse\") return pickDirectoryDialog(message, defaultDirectory) ?? defaultDirectory;\n\n const selected = await text({ message, initialValue: defaultDirectory });\n return isCancel(selected) || !selected ? defaultDirectory : selected;\n}\n\n/** Windows \u6587\u4EF6\u5939\u9009\u62E9\u5668\uFF1B\u975E Windows \u6216\u5931\u8D25\u65F6\u8FD4\u56DE null\u3002 */\nexport function pickDirectoryDialog(title: string, defaultDirectory: string): string | null {\n if (process.platform !== \"win32\") return null;\n const escape = (value: string): string => value.replace(/'/g, \"''\");\n const script = [\n \"Add-Type -AssemblyName System.Windows.Forms\",\n \"$dialog = New-Object System.Windows.Forms.FolderBrowserDialog\",\n `$dialog.Description = '${escape(title)}'`,\n `$dialog.SelectedPath = '${escape(defaultDirectory)}'`,\n \"if ($dialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { Write-Output $dialog.SelectedPath }\",\n ].join(\"; \");\n\n try {\n const output = execFileSync(\"powershell.exe\", [\"-NoProfile\", \"-STA\", \"-Command\", script], {\n encoding: \"utf-8\",\n timeout: 30_000,\n windowsHide: true,\n });\n return output.trim() || null;\n } catch {\n return null;\n }\n}\n\nexport function ensureDirectory(directory: string): boolean {\n try {\n fs.mkdirSync(directory, { recursive: true });\n return fs.statSync(directory).isDirectory();\n } catch {\n return false;\n }\n}\n\n/** \u5C06\u7528\u6237\u8F93\u5165\u89C4\u8303\u4E3A\u7EDD\u5BF9\u8DEF\u5F84\uFF08\u517C\u5BB9\u542B # \u7684 Windows \u8DEF\u5F84\uFF09\u3002 */\nexport function resolveUserPath(input: string, base = process.cwd()): string {\n return path.resolve(base, input.trim());\n}\n"],
5
+ "mappings": "AAAA,SAAS,UAAU,QAAQ,YAAY;AACvC,SAAS,oBAAoB;AAC7B,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,SAAS;AAGlB,eAAsB,cAAc,SAAiB,kBAA2C;AAC9F,QAAM,SAAS,MAAM,OAAO;AAAA,IAC1B;AAAA,IACA,SAAS;AAAA,MACP,EAAE,OAAO,QAAQ,OAAO,EAAE,kBAAkB,GAAG,MAAM,iBAAiB;AAAA,MACtE,EAAE,OAAO,UAAU,OAAO,EAAE,eAAe,GAAG,MAAM,EAAE,mBAAmB,EAAE;AAAA,IAC7E;AAAA,EACF,CAAC;AACD,MAAI,SAAS,MAAM,EAAG,QAAO;AAC7B,MAAI,WAAW,SAAU,QAAO,oBAAoB,SAAS,gBAAgB,KAAK;AAElF,QAAM,WAAW,MAAM,KAAK,EAAE,SAAS,cAAc,iBAAiB,CAAC;AACvE,SAAO,SAAS,QAAQ,KAAK,CAAC,WAAW,mBAAmB;AAC9D;AAGO,SAAS,oBAAoB,OAAe,kBAAyC;AAC1F,MAAI,QAAQ,aAAa,QAAS,QAAO;AACzC,QAAM,SAAS,CAAC,UAA0B,MAAM,QAAQ,MAAM,IAAI;AAClE,QAAM,SAAS;AAAA,IACb;AAAA,IACA;AAAA,IACA,0BAA0B,OAAO,KAAK,CAAC;AAAA,IACvC,2BAA2B,OAAO,gBAAgB,CAAC;AAAA,IACnD;AAAA,EACF,EAAE,KAAK,IAAI;AAEX,MAAI;AACF,UAAM,SAAS,aAAa,kBAAkB,CAAC,cAAc,QAAQ,YAAY,MAAM,GAAG;AAAA,MACxF,UAAU;AAAA,MACV,SAAS;AAAA,MACT,aAAa;AAAA,IACf,CAAC;AACD,WAAO,OAAO,KAAK,KAAK;AAAA,EAC1B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,gBAAgB,WAA4B;AAC1D,MAAI;AACF,OAAG,UAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AAC3C,WAAO,GAAG,SAAS,SAAS,EAAE,YAAY;AAAA,EAC5C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,gBAAgB,OAAe,OAAO,QAAQ,IAAI,GAAW;AAC3E,SAAO,KAAK,QAAQ,MAAM,MAAM,KAAK,CAAC;AACxC;",
6
+ "names": []
7
+ }
@@ -0,0 +1,3 @@
1
+ /** `sfmc locale [zh-CN|en]` — 查看或设置 CLI 语言并写入 runtime.json。 */
2
+ export declare function cmdLocale(args: string[]): string;
3
+ //# sourceMappingURL=locale-command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"locale-command.d.ts","sourceRoot":"","sources":["../src/locale-command.ts"],"names":[],"mappings":"AAIA,+DAA+D;AAC/D,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAWhD"}
@@ -0,0 +1,19 @@
1
+ import { getLocale, normalizeLocale, persistLocale, t } from "./i18n/index.js";
2
+ import { ROOT } from "./runtime.js";
3
+ import { c } from "./theme.js";
4
+ function cmdLocale(args) {
5
+ const raw = args[0];
6
+ if (!raw) {
7
+ return c.text(t("locale.current", { locale: getLocale() }));
8
+ }
9
+ const locale = normalizeLocale(raw);
10
+ if (!locale) {
11
+ return c.red(t("locale.invalid", { value: raw })) + "\n" + c.dim(t("locale.usage"));
12
+ }
13
+ persistLocale(ROOT, locale);
14
+ return c.green(t("locale.set", { locale }));
15
+ }
16
+ export {
17
+ cmdLocale
18
+ };
19
+ //# sourceMappingURL=locale-command.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/locale-command.ts"],
4
+ "sourcesContent": ["import { getLocale, normalizeLocale, persistLocale, t } from \"./i18n/index.js\";\nimport { ROOT } from \"./runtime.js\";\nimport { c } from \"./theme.js\";\n\n/** `sfmc locale [zh-CN|en]` \u2014 \u67E5\u770B\u6216\u8BBE\u7F6E CLI \u8BED\u8A00\u5E76\u5199\u5165 runtime.json\u3002 */\nexport function cmdLocale(args: string[]): string {\n const raw = args[0];\n if (!raw) {\n return c.text(t(\"locale.current\", { locale: getLocale() }));\n }\n const locale = normalizeLocale(raw);\n if (!locale) {\n return c.red(t(\"locale.invalid\", { value: raw })) + \"\\n\" + c.dim(t(\"locale.usage\"));\n }\n persistLocale(ROOT, locale);\n return c.green(t(\"locale.set\", { locale }));\n}\n"],
5
+ "mappings": "AAAA,SAAS,WAAW,iBAAiB,eAAe,SAAS;AAC7D,SAAS,YAAY;AACrB,SAAS,SAAS;AAGX,SAAS,UAAU,MAAwB;AAChD,QAAM,MAAM,KAAK,CAAC;AAClB,MAAI,CAAC,KAAK;AACR,WAAO,EAAE,KAAK,EAAE,kBAAkB,EAAE,QAAQ,UAAU,EAAE,CAAC,CAAC;AAAA,EAC5D;AACA,QAAM,SAAS,gBAAgB,GAAG;AAClC,MAAI,CAAC,QAAQ;AACX,WAAO,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,IAAI,CAAC,CAAC,IAAI,OAAO,EAAE,IAAI,EAAE,cAAc,CAAC;AAAA,EACpF;AACA,gBAAc,MAAM,MAAM;AAC1B,SAAO,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,CAAC,CAAC;AAC5C;",
6
+ "names": []
7
+ }
@@ -0,0 +1,69 @@
1
+ import type { LogLevel } from "@sfmc-bds/sdk/logs";
2
+ export type LogFilterMode = "drop" | "keep";
3
+ export type LogFilterApplyTo = "display" | "all";
4
+ export interface LogFilterRule {
5
+ enabled?: boolean;
6
+ sources?: string[];
7
+ levels?: LogLevel[];
8
+ contains?: string;
9
+ /** JS RegExp 源;支持前缀 (?i) 表示忽略大小写 */
10
+ regex?: string;
11
+ }
12
+ export interface LogFilterConfig {
13
+ enabled: boolean;
14
+ mode: LogFilterMode;
15
+ applyTo: LogFilterApplyTo;
16
+ rules: LogFilterRule[];
17
+ }
18
+ export type LogFilterEntry = {
19
+ text: string;
20
+ source: string;
21
+ level: LogLevel;
22
+ };
23
+ export declare function logFilterConfigPath(): string;
24
+ /** 确保 configs/log-filter.json 存在(委托 SDK ensureSchemaConfig,禁止手写 withConfigSchema) */
25
+ export declare function ensureLogFilterConfigFile(): string;
26
+ type CompiledRule = {
27
+ sources?: Set<string>;
28
+ levels?: Set<string>;
29
+ contains?: string;
30
+ regex?: RegExp;
31
+ };
32
+ type CompiledFilter = {
33
+ enabled: boolean;
34
+ mode: LogFilterMode;
35
+ applyTo: LogFilterApplyTo;
36
+ rules: CompiledRule[];
37
+ mtimeMs: number;
38
+ };
39
+ /** 将 (?i) 前缀转为 RegExp i 旗标(JS 无内联 (?i)) */
40
+ export declare function compileLogFilterRegex(src: string): RegExp | null;
41
+ /**
42
+ * 加载并编译过滤配置(按文件 mtime 热读)。
43
+ * onBadRegex:正则非法时回调(由 logs 层打 warn,避免循环依赖)。
44
+ */
45
+ export declare function getCompiledLogFilter(onBadRegex?: (src: string) => void): CompiledFilter;
46
+ /** 单条规则是否命中(同条 AND) */
47
+ export declare function ruleMatchesEntry(rule: CompiledRule, entry: LogFilterEntry): boolean;
48
+ /** 任一规则命中即为 matched */
49
+ export declare function entryMatchesAnyRule(entry: LogFilterEntry, rules: CompiledRule[]): boolean;
50
+ /**
51
+ * 是否应丢弃该条(对展示/内存而言)。
52
+ * enabled=false → 不丢;keep 模式未匹配 → 丢;drop 模式匹配 → 丢。
53
+ */
54
+ export declare function shouldDropForDisplay(entry: LogFilterEntry, filter: CompiledFilter): boolean;
55
+ /** 是否应跳过落盘(仅 applyTo=all 时与 display 一致) */
56
+ export declare function shouldDropForDisk(entry: LogFilterEntry, filter: CompiledFilter): boolean;
57
+ /** 测试用:清空缓存 */
58
+ export declare function resetLogFilterCacheForTests(): void;
59
+ /**
60
+ * 纯函数判定(不读盘):供单测与调用方表驱动。
61
+ * dropDisplay / dropDisk 语义与 pushLog 一致。
62
+ */
63
+ export declare function evaluateLogFilter(entry: LogFilterEntry, cfg: LogFilterConfig): {
64
+ dropDisplay: boolean;
65
+ dropDisk: boolean;
66
+ matched: boolean;
67
+ };
68
+ export {};
69
+ //# sourceMappingURL=log-filter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log-filter.d.ts","sourceRoot":"","sources":["../src/log-filter.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAKnD,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,CAAC;AAC5C,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,KAAK,CAAC;AAEjD,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,aAAa,CAAC;IACpB,OAAO,EAAE,gBAAgB,CAAC;IAC1B,KAAK,EAAE,aAAa,EAAE,CAAC;CACxB;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,QAAQ,CAAC;CACjB,CAAC;AASF,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED,qFAAqF;AACrF,wBAAgB,yBAAyB,IAAI,MAAM,CAGlD;AAED,KAAK,YAAY,GAAG;IAClB,OAAO,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACtB,MAAM,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,KAAK,cAAc,GAAG;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,aAAa,CAAC;IACpB,OAAO,EAAE,gBAAgB,CAAC;IAC1B,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAIF,2CAA2C;AAC3C,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAYhE;AAiDD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAAG,cAAc,CA8BvF;AAED,uBAAuB;AACvB,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,cAAc,GAAG,OAAO,CAMnF;AAED,uBAAuB;AACvB,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,EAAE,GAAG,OAAO,CAGzF;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAK3F;AAED,2CAA2C;AAC3C,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAGxF;AAED,eAAe;AACf,wBAAgB,2BAA2B,IAAI,IAAI,CAElD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,cAAc,EACrB,GAAG,EAAE,eAAe,GACnB;IAAE,WAAW,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAa/D"}
@@ -0,0 +1,150 @@
1
+ import { ensureSchemaConfig, readJson, stripConfigMeta } from "@sfmc-bds/sdk/node/config";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { ROOT } from "./runtime.js";
5
+ const DEFAULTS = {
6
+ enabled: false,
7
+ mode: "drop",
8
+ applyTo: "display",
9
+ rules: []
10
+ };
11
+ function logFilterConfigPath() {
12
+ return path.join(ROOT, "configs", "log-filter.json");
13
+ }
14
+ function ensureLogFilterConfigFile() {
15
+ ensureSchemaConfig(ROOT, "log-filter.json", "log_filter", { ...DEFAULTS });
16
+ return logFilterConfigPath();
17
+ }
18
+ let cache = null;
19
+ function compileLogFilterRegex(src) {
20
+ let pattern = String(src ?? "");
21
+ let flags = "";
22
+ if (pattern.startsWith("(?i)")) {
23
+ pattern = pattern.slice(4);
24
+ flags = "i";
25
+ }
26
+ try {
27
+ return new RegExp(pattern, flags);
28
+ } catch {
29
+ return null;
30
+ }
31
+ }
32
+ function compileRules(rules, onBadRegex) {
33
+ const out = [];
34
+ for (const r of rules) {
35
+ if (r.enabled === false) continue;
36
+ const hasAny = r.sources && r.sources.length > 0 || r.levels && r.levels.length > 0 || typeof r.contains === "string" && r.contains.length > 0 || typeof r.regex === "string" && r.regex.length > 0;
37
+ if (!hasAny) continue;
38
+ const compiled = {};
39
+ if (r.sources && r.sources.length > 0) {
40
+ compiled.sources = new Set(r.sources.map((s) => s.toLowerCase()));
41
+ }
42
+ if (r.levels && r.levels.length > 0) {
43
+ compiled.levels = new Set(r.levels.map((l) => l.toLowerCase()));
44
+ }
45
+ if (typeof r.contains === "string" && r.contains.length > 0) {
46
+ compiled.contains = r.contains;
47
+ }
48
+ if (typeof r.regex === "string" && r.regex.length > 0) {
49
+ const re = compileLogFilterRegex(r.regex);
50
+ if (!re) {
51
+ onBadRegex?.(r.regex);
52
+ continue;
53
+ }
54
+ compiled.regex = re;
55
+ }
56
+ out.push(compiled);
57
+ }
58
+ return out;
59
+ }
60
+ function normalizeConfig(raw) {
61
+ const stripped = stripConfigMeta(raw);
62
+ const mode = stripped.mode === "keep" ? "keep" : "drop";
63
+ const applyTo = stripped.applyTo === "all" ? "all" : "display";
64
+ const rules = Array.isArray(stripped.rules) ? stripped.rules : [];
65
+ return {
66
+ enabled: Boolean(stripped.enabled),
67
+ mode,
68
+ applyTo,
69
+ rules
70
+ };
71
+ }
72
+ function getCompiledLogFilter(onBadRegex) {
73
+ const p = ensureLogFilterConfigFile();
74
+ let mtimeMs = 0;
75
+ try {
76
+ mtimeMs = fs.statSync(p).mtimeMs;
77
+ } catch {
78
+ mtimeMs = 0;
79
+ }
80
+ if (cache && cache.mtimeMs === mtimeMs) return cache;
81
+ let cfg = { ...DEFAULTS };
82
+ const raw = readJson(p);
83
+ if (raw) {
84
+ try {
85
+ cfg = normalizeConfig(raw);
86
+ } catch {
87
+ cfg = { ...DEFAULTS };
88
+ }
89
+ }
90
+ const rules = compileRules(cfg.rules, onBadRegex);
91
+ cache = {
92
+ enabled: cfg.enabled,
93
+ mode: cfg.mode,
94
+ applyTo: cfg.applyTo,
95
+ rules,
96
+ mtimeMs
97
+ };
98
+ return cache;
99
+ }
100
+ function ruleMatchesEntry(rule, entry) {
101
+ if (rule.sources && !rule.sources.has(entry.source.toLowerCase())) return false;
102
+ if (rule.levels && !rule.levels.has(entry.level.toLowerCase())) return false;
103
+ if (rule.contains != null && !entry.text.includes(rule.contains)) return false;
104
+ if (rule.regex && !rule.regex.test(entry.text)) return false;
105
+ return true;
106
+ }
107
+ function entryMatchesAnyRule(entry, rules) {
108
+ if (rules.length === 0) return false;
109
+ return rules.some((r) => ruleMatchesEntry(r, entry));
110
+ }
111
+ function shouldDropForDisplay(entry, filter) {
112
+ if (!filter.enabled) return false;
113
+ const matched = entryMatchesAnyRule(entry, filter.rules);
114
+ if (filter.mode === "keep") return !matched;
115
+ return matched;
116
+ }
117
+ function shouldDropForDisk(entry, filter) {
118
+ if (filter.applyTo !== "all") return false;
119
+ return shouldDropForDisplay(entry, filter);
120
+ }
121
+ function resetLogFilterCacheForTests() {
122
+ cache = null;
123
+ }
124
+ function evaluateLogFilter(entry, cfg) {
125
+ const compiled = {
126
+ enabled: cfg.enabled,
127
+ mode: cfg.mode,
128
+ applyTo: cfg.applyTo,
129
+ rules: compileRules(cfg.rules),
130
+ mtimeMs: 0
131
+ };
132
+ return {
133
+ matched: entryMatchesAnyRule(entry, compiled.rules),
134
+ dropDisplay: shouldDropForDisplay(entry, compiled),
135
+ dropDisk: shouldDropForDisk(entry, compiled)
136
+ };
137
+ }
138
+ export {
139
+ compileLogFilterRegex,
140
+ ensureLogFilterConfigFile,
141
+ entryMatchesAnyRule,
142
+ evaluateLogFilter,
143
+ getCompiledLogFilter,
144
+ logFilterConfigPath,
145
+ resetLogFilterCacheForTests,
146
+ ruleMatchesEntry,
147
+ shouldDropForDisk,
148
+ shouldDropForDisplay
149
+ };
150
+ //# sourceMappingURL=log-filter.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/log-filter.ts"],
4
+ "sourcesContent": ["/**\n * log-filter.ts \u2014 configs/log-filter.json \u52A0\u8F7D\u4E0E\u5339\u914D\uFF08\u7EAF\u5224\u5B9A + \u70ED\u8BFB\u7F13\u5B58\uFF09\n */\nimport { ensureSchemaConfig, readJson, stripConfigMeta } from \"@sfmc-bds/sdk/node/config\";\nimport type { LogLevel } from \"@sfmc-bds/sdk/logs\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { ROOT } from \"./runtime.js\";\n\nexport type LogFilterMode = \"drop\" | \"keep\";\nexport type LogFilterApplyTo = \"display\" | \"all\";\n\nexport interface LogFilterRule {\n enabled?: boolean;\n sources?: string[];\n levels?: LogLevel[];\n contains?: string;\n /** JS RegExp \u6E90\uFF1B\u652F\u6301\u524D\u7F00 (?i) \u8868\u793A\u5FFD\u7565\u5927\u5C0F\u5199 */\n regex?: string;\n}\n\nexport interface LogFilterConfig {\n enabled: boolean;\n mode: LogFilterMode;\n applyTo: LogFilterApplyTo;\n rules: LogFilterRule[];\n}\n\nexport type LogFilterEntry = {\n text: string;\n source: string;\n level: LogLevel;\n};\n\nconst DEFAULTS: LogFilterConfig = {\n enabled: false,\n mode: \"drop\",\n applyTo: \"display\",\n rules: [],\n};\n\nexport function logFilterConfigPath(): string {\n return path.join(ROOT, \"configs\", \"log-filter.json\");\n}\n\n/** \u786E\u4FDD configs/log-filter.json \u5B58\u5728\uFF08\u59D4\u6258 SDK ensureSchemaConfig\uFF0C\u7981\u6B62\u624B\u5199 withConfigSchema\uFF09 */\nexport function ensureLogFilterConfigFile(): string {\n ensureSchemaConfig(ROOT, \"log-filter.json\", \"log_filter\", { ...DEFAULTS } as Record<string, unknown>);\n return logFilterConfigPath();\n}\n\ntype CompiledRule = {\n sources?: Set<string>;\n levels?: Set<string>;\n contains?: string;\n regex?: RegExp;\n};\n\ntype CompiledFilter = {\n enabled: boolean;\n mode: LogFilterMode;\n applyTo: LogFilterApplyTo;\n rules: CompiledRule[];\n mtimeMs: number;\n};\n\nlet cache: CompiledFilter | null = null;\n\n/** \u5C06 (?i) \u524D\u7F00\u8F6C\u4E3A RegExp i \u65D7\u6807\uFF08JS \u65E0\u5185\u8054 (?i)\uFF09 */\nexport function compileLogFilterRegex(src: string): RegExp | null {\n let pattern = String(src ?? \"\");\n let flags = \"\";\n if (pattern.startsWith(\"(?i)\")) {\n pattern = pattern.slice(4);\n flags = \"i\";\n }\n try {\n return new RegExp(pattern, flags);\n } catch {\n return null;\n }\n}\n\nfunction compileRules(rules: LogFilterRule[], onBadRegex?: (src: string) => void): CompiledRule[] {\n const out: CompiledRule[] = [];\n for (const r of rules) {\n if (r.enabled === false) continue;\n const hasAny =\n (r.sources && r.sources.length > 0) ||\n (r.levels && r.levels.length > 0) ||\n (typeof r.contains === \"string\" && r.contains.length > 0) ||\n (typeof r.regex === \"string\" && r.regex.length > 0);\n if (!hasAny) continue;\n\n const compiled: CompiledRule = {};\n if (r.sources && r.sources.length > 0) {\n compiled.sources = new Set(r.sources.map((s) => s.toLowerCase()));\n }\n if (r.levels && r.levels.length > 0) {\n compiled.levels = new Set(r.levels.map((l) => l.toLowerCase()));\n }\n if (typeof r.contains === \"string\" && r.contains.length > 0) {\n compiled.contains = r.contains;\n }\n if (typeof r.regex === \"string\" && r.regex.length > 0) {\n const re = compileLogFilterRegex(r.regex);\n if (!re) {\n onBadRegex?.(r.regex);\n continue;\n }\n compiled.regex = re;\n }\n out.push(compiled);\n }\n return out;\n}\n\nfunction normalizeConfig(raw: Record<string, unknown>): LogFilterConfig {\n const stripped = stripConfigMeta(raw);\n const mode = stripped.mode === \"keep\" ? \"keep\" : \"drop\";\n const applyTo = stripped.applyTo === \"all\" ? \"all\" : \"display\";\n const rules = Array.isArray(stripped.rules) ? (stripped.rules as LogFilterRule[]) : [];\n return {\n enabled: Boolean(stripped.enabled),\n mode,\n applyTo,\n rules,\n };\n}\n\n/**\n * \u52A0\u8F7D\u5E76\u7F16\u8BD1\u8FC7\u6EE4\u914D\u7F6E\uFF08\u6309\u6587\u4EF6 mtime \u70ED\u8BFB\uFF09\u3002\n * onBadRegex\uFF1A\u6B63\u5219\u975E\u6CD5\u65F6\u56DE\u8C03\uFF08\u7531 logs \u5C42\u6253 warn\uFF0C\u907F\u514D\u5FAA\u73AF\u4F9D\u8D56\uFF09\u3002\n */\nexport function getCompiledLogFilter(onBadRegex?: (src: string) => void): CompiledFilter {\n const p = ensureLogFilterConfigFile();\n let mtimeMs = 0;\n try {\n mtimeMs = fs.statSync(p).mtimeMs;\n } catch {\n mtimeMs = 0;\n }\n if (cache && cache.mtimeMs === mtimeMs) return cache;\n\n let cfg = { ...DEFAULTS };\n const raw = readJson<Record<string, unknown>>(p);\n if (raw) {\n try {\n cfg = normalizeConfig(raw);\n } catch {\n cfg = { ...DEFAULTS };\n }\n }\n\n const rules = compileRules(cfg.rules, onBadRegex);\n\n cache = {\n enabled: cfg.enabled,\n mode: cfg.mode,\n applyTo: cfg.applyTo,\n rules,\n mtimeMs,\n };\n return cache;\n}\n\n/** \u5355\u6761\u89C4\u5219\u662F\u5426\u547D\u4E2D\uFF08\u540C\u6761 AND\uFF09 */\nexport function ruleMatchesEntry(rule: CompiledRule, entry: LogFilterEntry): boolean {\n if (rule.sources && !rule.sources.has(entry.source.toLowerCase())) return false;\n if (rule.levels && !rule.levels.has(entry.level.toLowerCase())) return false;\n if (rule.contains != null && !entry.text.includes(rule.contains)) return false;\n if (rule.regex && !rule.regex.test(entry.text)) return false;\n return true;\n}\n\n/** \u4EFB\u4E00\u89C4\u5219\u547D\u4E2D\u5373\u4E3A matched */\nexport function entryMatchesAnyRule(entry: LogFilterEntry, rules: CompiledRule[]): boolean {\n if (rules.length === 0) return false;\n return rules.some((r) => ruleMatchesEntry(r, entry));\n}\n\n/**\n * \u662F\u5426\u5E94\u4E22\u5F03\u8BE5\u6761\uFF08\u5BF9\u5C55\u793A/\u5185\u5B58\u800C\u8A00\uFF09\u3002\n * enabled=false \u2192 \u4E0D\u4E22\uFF1Bkeep \u6A21\u5F0F\u672A\u5339\u914D \u2192 \u4E22\uFF1Bdrop \u6A21\u5F0F\u5339\u914D \u2192 \u4E22\u3002\n */\nexport function shouldDropForDisplay(entry: LogFilterEntry, filter: CompiledFilter): boolean {\n if (!filter.enabled) return false;\n const matched = entryMatchesAnyRule(entry, filter.rules);\n if (filter.mode === \"keep\") return !matched;\n return matched;\n}\n\n/** \u662F\u5426\u5E94\u8DF3\u8FC7\u843D\u76D8\uFF08\u4EC5 applyTo=all \u65F6\u4E0E display \u4E00\u81F4\uFF09 */\nexport function shouldDropForDisk(entry: LogFilterEntry, filter: CompiledFilter): boolean {\n if (filter.applyTo !== \"all\") return false;\n return shouldDropForDisplay(entry, filter);\n}\n\n/** \u6D4B\u8BD5\u7528\uFF1A\u6E05\u7A7A\u7F13\u5B58 */\nexport function resetLogFilterCacheForTests(): void {\n cache = null;\n}\n\n/**\n * \u7EAF\u51FD\u6570\u5224\u5B9A\uFF08\u4E0D\u8BFB\u76D8\uFF09\uFF1A\u4F9B\u5355\u6D4B\u4E0E\u8C03\u7528\u65B9\u8868\u9A71\u52A8\u3002\n * dropDisplay / dropDisk \u8BED\u4E49\u4E0E pushLog \u4E00\u81F4\u3002\n */\nexport function evaluateLogFilter(\n entry: LogFilterEntry,\n cfg: LogFilterConfig\n): { dropDisplay: boolean; dropDisk: boolean; matched: boolean } {\n const compiled = {\n enabled: cfg.enabled,\n mode: cfg.mode,\n applyTo: cfg.applyTo,\n rules: compileRules(cfg.rules),\n mtimeMs: 0,\n };\n return {\n matched: entryMatchesAnyRule(entry, compiled.rules),\n dropDisplay: shouldDropForDisplay(entry, compiled),\n dropDisk: shouldDropForDisk(entry, compiled),\n };\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
package/dist/logs.d.ts CHANGED
@@ -5,6 +5,11 @@
5
5
  * 保持向后兼容的 pushLog / onLog / getAllLogs / getRecentLogs API
6
6
  * (services.ts 捕获子进程 stdout 后调用 pushLog 汇聚到此)。
7
7
  *
8
+ * 落盘策略(与子进程自写文件分工,避免重复):
9
+ * - db / qq / update → 子进程已写 `.sfmc/logs/{db,qq,bds-update}.log`,此处跳过
10
+ * - bds / llbot → `.sfmc/logs/{bds,llbot}.log`(外部进程无自带 file sink)
11
+ * - 其余(system / pack / …) → `.sfmc/logs/sfmc.log`
12
+ *
8
13
  * formatLog 保留 theme.ts (chalk) 配色,比共享包的纯 ANSI 版本视觉更丰富。
9
14
  */
10
15
  import { type LogEntry, type LogLevel as SharedLogLevel } from "@sfmc-bds/sdk/logs";
@@ -12,24 +17,44 @@ export type LogLevel = SharedLogLevel;
12
17
  export type LogSource = string;
13
18
  export interface UnifiedLog extends LogEntry {
14
19
  }
15
- /** 推送一条日志到内存缓冲 (services.ts 捕获子进程 stdout 后调用) */
20
+ /** 再导出 SDK BDS 解析(测试与调用方统一入口,权威在 @sfmc-bds/sdk/logs) */
21
+ export { mapBdsLevelToken, parseBdsEmbeddedLevel, stripBdsLogPrefix } from "@sfmc-bds/sdk/logs";
22
+ /** 推送一条日志到内存缓冲,并按策略落盘 */
16
23
  export declare function pushLog(text: string, source: LogSource, level: LogLevel): void;
17
24
  /** 订阅新日志事件,返回取消订阅函数 */
18
25
  export declare function onLog(fn: (log: UnifiedLog) => void): () => void;
19
- /** 获取全部日志 (按时间顺序) */
20
- export declare function getAllLogs(): UnifiedLog[];
21
- /** 获取最近 n 条,可按 level / source 过滤 */
26
+ /** 获取最近 n 条,可按 level / source 过滤;再套配置过滤(display) */
22
27
  export declare function getRecentLogs(n: number, levels: LogLevel[], sources: LogSource[]): UnifiedLog[];
23
- /** 从原始文本推断日志级别 (委托共享包) */
28
+ /** 获取全部日志;配置过滤仅影响返回视图(缓冲内可能仍有全量,视 push 时 applyTo) */
29
+ export declare function getAllLogs(): UnifiedLog[];
30
+ export interface SourceMeta {
31
+ value: string;
32
+ /** 显示名,尽量 8 宽对齐 */
33
+ name: string;
34
+ paint: (s: string) => string;
35
+ }
36
+ export declare const SOURCE_META: SourceMeta[];
37
+ export declare function formatSourceTag(source: string): string;
38
+ /**
39
+ * 展示用级别:BDS 若仍带时间戳前缀则解析内嵌级别;已剥离(pushLog 入库)则信任 entry.level。
40
+ * 禁止对正文做松散关键词匹配(会把 `[Commands] Error on line` 误判为 error)。
41
+ */
42
+ export declare function resolveDisplayLevel(l: UnifiedLog): LogLevel;
43
+ /** 从原始文本推断级别:委托共享包(内含 BDS 时间戳解析) */
24
44
  export declare function inferLevel(text: string): LogLevel;
25
45
  /** 格式化日志用于 REPL 展示 (用 theme.ts chalk 配色) */
26
46
  export declare function formatLog(l: UnifiedLog): string;
27
47
  /** 字符串在终端的可见宽度 (strip ANSI 后按 CJK 宽度累加) */
28
48
  export declare function visibleWidth(s: string): number;
29
49
  /**
30
- * 按终端可见宽度换行,后续行左侧缩进 indent 个空格。
31
- * 保留 ANSI 颜色码:换行时先 reset 避免缩进空格带色,新行恢复颜色状态。
32
- * 宽字符(CJK)按 2 列计算,避免中文行换行位置偏后。
50
+ * 日志前缀可见宽度(时间 + 源 + 级别 + 尾空格),供悬挂缩进对齐正文起点。
51
+ * formatLog 拼接顺序保持一致(共用 resolveDisplayLevel / sourceTagPlain / levelTagPlain)。
52
+ */
53
+ export declare function logPrefixWidth(l: UnifiedLog): number;
54
+ /**
55
+ * 按终端可见宽度换行;显式 \\n 与软换行的后续行一律左侧缩进 indent 列。
56
+ * 保留 ANSI:换行时 reset,新行恢复当前颜色状态(可多层叠加)。
57
+ * 宽字符(CJK)按 2 列;cols 预留 1 列,避免 Windows Terminal 边界再软折到第 0 列。
33
58
  */
34
59
  export declare function wrapLogLine(s: string, indent: number): string;
35
60
  //# sourceMappingURL=logs.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"logs.d.ts","sourceRoot":"","sources":["../src/logs.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAGL,KAAK,QAAQ,EACb,KAAK,QAAQ,IAAI,cAAc,EAChC,MAAM,oBAAoB,CAAC;AAI5B,MAAM,MAAM,QAAQ,GAAG,cAAc,CAAC;AACtC,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC;AAC/B,MAAM,WAAW,UAAW,SAAQ,QAAQ;CAAG;AAI/C,iDAAiD;AACjD,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,GAAG,IAAI,CAE9E;AAED,uBAAuB;AACvB,wBAAgB,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,IAAI,GAAG,MAAM,IAAI,CAE/D;AAED,qBAAqB;AACrB,wBAAgB,UAAU,IAAI,UAAU,EAAE,CAEzC;AAED,oCAAoC;AACpC,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,UAAU,EAAE,CAE/F;AAED,0BAA0B;AAC1B,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAEjD;AAuCD,4CAA4C;AAC5C,wBAAgB,SAAS,CAAC,CAAC,EAAE,UAAU,GAAG,MAAM,CAgC/C;AAmDD,2CAA2C;AAC3C,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAK9C;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAoC7D"}
1
+ {"version":3,"file":"logs.d.ts","sourceRoot":"","sources":["../src/logs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAQL,KAAK,QAAQ,EACb,KAAK,QAAQ,IAAI,cAAc,EAChC,MAAM,oBAAoB,CAAC;AAU5B,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;AA+BhG,yBAAyB;AACzB,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,GAAG,IAAI,CA2C9E;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;AAMD,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,mBAAmB;IACnB,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,CAQ/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"}