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

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 (197) hide show
  1. package/LICENSE +663 -0
  2. package/dist/argv-parse.d.ts +11 -0
  3. package/dist/argv-parse.d.ts.map +1 -0
  4. package/dist/argv-parse.js +28 -0
  5. package/dist/argv-parse.js.map +7 -0
  6. package/dist/cli-gate.d.ts +11 -0
  7. package/dist/cli-gate.d.ts.map +1 -0
  8. package/dist/cli-gate.js +46 -0
  9. package/dist/cli-gate.js.map +7 -0
  10. package/dist/command-palette.d.ts +61 -0
  11. package/dist/command-palette.d.ts.map +1 -0
  12. package/dist/command-palette.js +352 -0
  13. package/dist/command-palette.js.map +7 -0
  14. package/dist/command-surface.d.ts +85 -0
  15. package/dist/command-surface.d.ts.map +1 -0
  16. package/dist/command-surface.js +292 -0
  17. package/dist/command-surface.js.map +7 -0
  18. package/dist/command-surface.test.js +92 -0
  19. package/dist/command-surface.test.js.map +7 -0
  20. package/dist/commands-reload.js +2 -2
  21. package/dist/commands-reload.js.map +2 -2
  22. package/dist/commands.d.ts +1 -1
  23. package/dist/commands.d.ts.map +1 -1
  24. package/dist/commands.js +196 -21
  25. package/dist/commands.js.map +2 -2
  26. package/dist/debug-command.d.ts +5 -0
  27. package/dist/debug-command.d.ts.map +1 -0
  28. package/dist/debug-command.js +118 -0
  29. package/dist/debug-command.js.map +7 -0
  30. package/dist/debug-command.test.js +76 -0
  31. package/dist/debug-command.test.js.map +7 -0
  32. package/dist/directory-picker.d.ts +27 -0
  33. package/dist/directory-picker.d.ts.map +1 -0
  34. package/dist/directory-picker.js +112 -0
  35. package/dist/directory-picker.js.map +7 -0
  36. package/dist/help-text.d.ts +3 -0
  37. package/dist/help-text.d.ts.map +1 -0
  38. package/dist/help-text.js +160 -0
  39. package/dist/help-text.js.map +7 -0
  40. package/dist/i18n/locales/en.d.ts +94 -79
  41. package/dist/i18n/locales/en.d.ts.map +1 -1
  42. package/dist/i18n/locales/en.js +96 -81
  43. package/dist/i18n/locales/en.js.map +2 -2
  44. package/dist/i18n/locales/zh-CN.d.ts.map +1 -1
  45. package/dist/i18n/locales/zh-CN.js +130 -109
  46. package/dist/i18n/locales/zh-CN.js.map +2 -2
  47. package/dist/index.d.ts +2 -2
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +9 -4
  50. package/dist/index.js.map +2 -2
  51. package/dist/interactive-prompts.d.ts +1 -2
  52. package/dist/interactive-prompts.d.ts.map +1 -1
  53. package/dist/interactive-prompts.js +12 -23
  54. package/dist/interactive-prompts.js.map +3 -3
  55. package/dist/llbot-launch.d.ts +20 -0
  56. package/dist/llbot-launch.d.ts.map +1 -0
  57. package/dist/llbot-launch.js +40 -0
  58. package/dist/llbot-launch.js.map +7 -0
  59. package/dist/log-filter.test.js +119 -0
  60. package/dist/log-filter.test.js.map +7 -0
  61. package/dist/logs.d.ts +16 -1
  62. package/dist/logs.d.ts.map +1 -1
  63. package/dist/logs.js +80 -11
  64. package/dist/logs.js.map +2 -2
  65. package/dist/main.js +55 -46
  66. package/dist/main.js.map +2 -2
  67. package/dist/module-commands.d.ts +17 -40
  68. package/dist/module-commands.d.ts.map +1 -1
  69. package/dist/module-commands.js +129 -85
  70. package/dist/module-commands.js.map +2 -2
  71. package/dist/module-pack-build.d.ts +10 -0
  72. package/dist/module-pack-build.d.ts.map +1 -0
  73. package/dist/module-pack-build.js +434 -0
  74. package/dist/module-pack-build.js.map +7 -0
  75. package/dist/module-toggle.d.ts +33 -0
  76. package/dist/module-toggle.d.ts.map +1 -0
  77. package/dist/module-toggle.js +28 -0
  78. package/dist/module-toggle.js.map +7 -0
  79. package/dist/module-toggle.test.js +37 -0
  80. package/dist/module-toggle.test.js.map +7 -0
  81. package/dist/node-service-probe.d.ts +18 -0
  82. package/dist/node-service-probe.d.ts.map +1 -0
  83. package/dist/node-service-probe.js +97 -0
  84. package/dist/node-service-probe.js.map +7 -0
  85. package/dist/pack-lifecycle.d.ts +1 -1
  86. package/dist/pack-lifecycle.d.ts.map +1 -1
  87. package/dist/pack-lifecycle.js +14 -4
  88. package/dist/pack-lifecycle.js.map +2 -2
  89. package/dist/pack-update/index.d.ts +18 -0
  90. package/dist/pack-update/index.d.ts.map +1 -0
  91. package/dist/pack-update/index.js +113 -0
  92. package/dist/pack-update/index.js.map +7 -0
  93. package/dist/registry.d.ts +7 -4
  94. package/dist/registry.d.ts.map +1 -1
  95. package/dist/registry.js +15 -3
  96. package/dist/registry.js.map +2 -2
  97. package/dist/repl-windows/format-display.d.ts +10 -0
  98. package/dist/repl-windows/format-display.d.ts.map +1 -0
  99. package/dist/repl-windows/format-display.js +57 -0
  100. package/dist/repl-windows/format-display.js.map +7 -0
  101. package/dist/repl-windows/host.d.ts +60 -0
  102. package/dist/repl-windows/host.d.ts.map +1 -0
  103. package/dist/repl-windows/host.js +179 -0
  104. package/dist/repl-windows/host.js.map +7 -0
  105. package/dist/repl-windows/index.d.ts +9 -0
  106. package/dist/repl-windows/index.d.ts.map +1 -0
  107. package/dist/repl-windows/index.js +13 -0
  108. package/dist/repl-windows/index.js.map +7 -0
  109. package/dist/repl-windows/logs-filter-window.d.ts +15 -0
  110. package/dist/repl-windows/logs-filter-window.d.ts.map +1 -0
  111. package/dist/repl-windows/logs-filter-window.js +77 -0
  112. package/dist/repl-windows/logs-filter-window.js.map +7 -0
  113. package/dist/repl-windows/service-window.d.ts +4 -0
  114. package/dist/repl-windows/service-window.d.ts.map +1 -0
  115. package/dist/repl-windows/service-window.js +35 -0
  116. package/dist/repl-windows/service-window.js.map +7 -0
  117. package/dist/repl-windows/types.d.ts +56 -0
  118. package/dist/repl-windows/types.d.ts.map +1 -0
  119. package/dist/repl-windows/types.js +1 -0
  120. package/dist/repl-windows/types.js.map +7 -0
  121. package/dist/repl.d.ts +6 -9
  122. package/dist/repl.d.ts.map +1 -1
  123. package/dist/repl.js +698 -427
  124. package/dist/repl.js.map +3 -3
  125. package/dist/runtime.d.ts +6 -13
  126. package/dist/runtime.d.ts.map +1 -1
  127. package/dist/runtime.js +23 -28
  128. package/dist/runtime.js.map +2 -2
  129. package/dist/send-target.d.ts +11 -0
  130. package/dist/send-target.d.ts.map +1 -0
  131. package/dist/send-target.js +42 -0
  132. package/dist/send-target.js.map +7 -0
  133. package/dist/services.d.ts +50 -4
  134. package/dist/services.d.ts.map +1 -1
  135. package/dist/services.js +244 -20
  136. package/dist/services.js.map +2 -2
  137. package/dist/theme.js +1 -1
  138. package/dist/theme.js.map +2 -2
  139. package/dist/wizard.d.ts.map +1 -1
  140. package/dist/wizard.js +76 -16
  141. package/dist/wizard.js.map +2 -2
  142. package/dist/world-packs-uninstall.test.js +54 -0
  143. package/dist/world-packs-uninstall.test.js.map +7 -0
  144. package/dist/world-packs.d.ts.map +1 -1
  145. package/dist/world-packs.js +6 -3
  146. package/dist/world-packs.js.map +2 -2
  147. package/package.json +29 -24
  148. package/scripts/module-install/check-modules.mjs +204 -0
  149. package/scripts/module-install/fetch-module.mjs +1007 -0
  150. package/scripts/module-install/lib/catalog.mjs +96 -0
  151. package/scripts/module-install/lib/io.mjs +37 -0
  152. package/scripts/module-install/lib/link-from.mjs +80 -0
  153. package/scripts/module-install/lib/lock.mjs +78 -0
  154. package/scripts/module-install/lib/npm-resolver.mjs +55 -0
  155. package/scripts/module-install/lib/packages.mjs +142 -0
  156. package/scripts/module-install/lib/paths.mjs +48 -0
  157. package/scripts/module-install/lib/registry-index.mjs +50 -0
  158. package/scripts/module-install/test/install-resolver.test.ts +47 -0
  159. package/scripts/module-install/test/link-local.test.ts +50 -0
  160. package/scripts/module-install/test/local-resolver.test.ts +44 -0
  161. package/scripts/module-install/test/registry-index.test.ts +27 -0
  162. package/dist/module-wizard.d.ts +0 -14
  163. package/dist/module-wizard.d.ts.map +0 -1
  164. package/dist/module-wizard.js +0 -313
  165. package/dist/module-wizard.js.map +0 -7
  166. package/dist/pack-update/bindings.d.ts +0 -12
  167. package/dist/pack-update/bindings.d.ts.map +0 -1
  168. package/dist/pack-update/bindings.js +0 -64
  169. package/dist/pack-update/bindings.js.map +0 -7
  170. package/dist/pack-update/config.d.ts +0 -22
  171. package/dist/pack-update/config.d.ts.map +0 -1
  172. package/dist/pack-update/config.js +0 -141
  173. package/dist/pack-update/config.js.map +0 -7
  174. package/dist/pack-update/providers/curseforge.d.ts +0 -23
  175. package/dist/pack-update/providers/curseforge.d.ts.map +0 -1
  176. package/dist/pack-update/providers/curseforge.js +0 -229
  177. package/dist/pack-update/providers/curseforge.js.map +0 -7
  178. package/dist/pack-update/providers/index.d.ts +0 -16
  179. package/dist/pack-update/providers/index.d.ts.map +0 -1
  180. package/dist/pack-update/providers/index.js +0 -37
  181. package/dist/pack-update/providers/index.js.map +0 -7
  182. package/dist/pack-update/service.d.ts +0 -21
  183. package/dist/pack-update/service.d.ts.map +0 -1
  184. package/dist/pack-update/service.js +0 -572
  185. package/dist/pack-update/service.js.map +0 -7
  186. package/dist/pack-update/types.d.ts +0 -126
  187. package/dist/pack-update/types.d.ts.map +0 -1
  188. package/dist/pack-update/types.js +0 -8
  189. package/dist/pack-update/types.js.map +0 -7
  190. package/dist/pack-update/version-policy.d.ts +0 -81
  191. package/dist/pack-update/version-policy.d.ts.map +0 -1
  192. package/dist/pack-update/version-policy.js +0 -210
  193. package/dist/pack-update/version-policy.js.map +0 -7
  194. package/dist/remote-agent.d.ts +0 -18
  195. package/dist/remote-agent.d.ts.map +0 -1
  196. package/dist/remote-agent.js +0 -196
  197. package/dist/remote-agent.js.map +0 -7
package/dist/repl.js CHANGED
@@ -1,152 +1,238 @@
1
+ import { pauseAllProgress, resumeAllProgress } from "@sfmc-bds/sdk/logs";
2
+ import chalk from "chalk";
1
3
  import process, { stdin, stdout } from "node:process";
2
4
  import pkg from "../package.json" with { type: "json" };
5
+ import { gateModuleSub, gatePacksSub, gateTopLevel } from "./cli-gate.js";
6
+ import {
7
+ activeNode,
8
+ clampPaletteSelection,
9
+ commitSelection,
10
+ formatPaletteCascadeLines,
11
+ paletteGhost,
12
+ promptSlashColumn,
13
+ resolvePaletteView
14
+ } from "./command-palette.js";
3
15
  import {
4
- cmdLogs,
5
- cmdRestart,
6
- cmdSend,
7
- cmdStart,
8
- cmdStartAll,
9
- cmdStatus,
10
- cmdStop,
11
- cmdStopAll,
12
- cmdUpdate
13
- } from "./commands.js";
16
+ listVisiblePacksSubs,
17
+ listVisibleTopLevelNames,
18
+ resolveModuleTopShorthand
19
+ } from "./command-surface.js";
20
+ import { cmdRestart, cmdSend, cmdStart, cmdStartAll, cmdStatus, cmdStop, cmdStopAll, cmdUpdate } from "./commands.js";
21
+ import { cmdDebug } from "./debug-command.js";
22
+ import { getHelp as buildHelp } from "./help-text.js";
23
+ import { t } from "./i18n/index.js";
24
+ import { cmdLocale } from "./locale-command.js";
14
25
  import {
15
26
  formatLog,
16
27
  getAllLogs,
17
- getRecentLogs,
18
28
  logPrefixWidth,
19
29
  onLog,
30
+ readDiskLogs,
20
31
  SOURCE_META,
21
32
  wrapLogLine
22
33
  } from "./logs.js";
23
- import { pauseAllProgress, resumeAllProgress } from "@sfmc-bds/sdk/logs";
24
34
  import {
25
35
  dispatchModuleCommand,
36
+ getVisibleModuleSubcommands,
26
37
  isModuleCommand,
27
- listInstalledModuleIdsSync,
28
- MODULE_CMD_NAMES,
29
- MODULE_SUBCOMMANDS
38
+ listInstalledModuleIdsSync
30
39
  } from "./module-commands.js";
31
40
  import { listRegistryModuleIdsSync } from "./registry.js";
32
- import { disableRemoteAgent, enrollRemoteAgent, remoteStatus, startRemoteAgent } from "./remote-agent.js";
33
- import { forceStopAll, SERVICE_NAMES, stopAll } from "./services.js";
34
- import { cmdLocale } from "./locale-command.js";
35
- import { t } from "./i18n/index.js";
36
- import { listSfmcModulePackages, resolveSfmcModulesRoot } from "./sfmc-modules-root.js";
37
- import { c } from "./theme.js";
38
- import { dispatchPacksCommand, isPacksCommand, PACKS_SUBCOMMANDS } from "./world-packs.js";
41
+ import {
42
+ createLogsFilterWindow,
43
+ createServiceWindow,
44
+ serviceWindowId,
45
+ WindowHost
46
+ } from "./repl-windows/index.js";
47
+ import { listActiveSendTargets, paintSendPrompt, plainPrompt } from "./send-target.js";
48
+ import { forceStopAll, onServiceStateChange, SERVICE_NAMES, stopAll } from "./services.js";
49
+ import { c, T } from "./theme.js";
50
+ import { dispatchPacksCommand, isPacksCommand } from "./world-packs.js";
51
+ const REPL_MODE = "repl";
52
+ function getDisplayWidth(str) {
53
+ const plain = str.replace(/\x1b\[[0-9;]*m/g, "");
54
+ let width = 0;
55
+ for (const ch of plain) {
56
+ if (ch.codePointAt(0) > 127) {
57
+ width += 2;
58
+ } else {
59
+ width += 1;
60
+ }
61
+ }
62
+ return width;
63
+ }
64
+ function charDisplayWidth(ch) {
65
+ return ch.codePointAt(0) > 127 ? 2 : 1;
66
+ }
39
67
  function setRaw(v) {
40
68
  try {
41
69
  if (stdin.isTTY && typeof stdin.setRawMode === "function") stdin.setRawMode(v);
42
70
  } catch {
43
71
  }
44
72
  }
45
- const welcome = `
46
-
47
- ${c.text(`\u282A\u2841\u286F\u2801`)}
48
- ${c.text(`\u2812\u2801\u2803`)}${c.purple(`\u2804`)}
49
- ${c.text(`\u2877\u2847\u284E\u2801`)} ${c.text(`S`)}${c.dim(`cripts`)} ${c.text(`F`)}${c.dim(`or`)} ${c.text(`M`)}${c.dim(`ine`)}${c.text(`c`)}${c.dim(`raft Server`)} v${pkg.version}
50
- ${c.text(`\u2803\u2803\u2811\u2802`)} ${c.dim(`help \xB7 Ctrl+L \xB7 \u21B9 \xB7 \u2192 \xB7 \u2191\u2193`)}
51
-
52
- `;
53
- const version = `
54
-
55
- ${c.text(`\u282A\u2841\u286F\u2801`)}
56
- ${c.text(`\u2812\u2801\u2803`)}${c.purple(`\u2804`)}
57
- ${c.text(`\u2877\u2847\u284E\u2801`)} ${c.dim(`https://github.com/DogeLakeDev/ScriptsForMinecraftServer`)}
58
- ${c.text(`\u2803\u2803\u2811\u2802`)} ${c.text(`S`)}${c.dim(`cripts`)} ${c.text(`F`)}${c.dim(`or`)} ${c.text(`M`)}${c.dim(`ine`)}${c.text(`c`)}${c.dim(`raft Server`)} v${pkg.version}
59
-
60
- `;
61
- function getHelp() {
62
- return `
63
- ${c.bold("\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E")}
64
- ${c.bold("\u2502")} ${c.green(t("help.title"))}${" ".repeat(Math.max(1, 52 - t("help.title").length))}${c.bold("\u2502")}
65
- ${c.bold("\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F")}
66
-
67
- ${c.bold(t("help.section.service"))}
68
- ${c.green("status")} ${t("help.status")}
69
- ${c.green("logs")} <svc> [-n N] [-f] ${t("help.logs")}
70
- ${c.green("start")} <svc>|-all ${t("help.start")}
71
- ${c.green("stop")} <svc>|-all ${t("help.stop")}
72
- ${c.green("restart")}<svc>|-all ${t("help.restart")}
73
- ${c.green("send")} <svc> <msg> ${t("help.send")}
74
-
75
- ${c.bold(t("help.section.update"))}
76
- ${c.green("update")} [--check-only] [--channel=release|preview]
77
- ${t("help.update")}
78
-
79
- ${c.bold(t("help.section.remote"))} ${c.dim("[beta]")}
80
- ${c.green("remote")} status ${t("help.remote.status")}
81
- ${c.green("remote")} enroll <url> <token> [name]
82
- ${t("help.remote.enroll")}
83
- ${c.green("remote")} disable ${t("help.remote.disable")}
84
-
85
- ${c.bold(t("help.section.module"))}
86
- ${c.green("module")}/${c.green("mod")} list
87
- ${t("help.module.list")}
88
- ${c.green("module")} search [id] ${t("help.module.search")}
89
- ${c.green("module")} install <id> [--from <source>] [--link]
90
- ${t("help.module.install")}
91
- ${c.green("module")} uninstall <id> ${t("help.module.uninstall")}
92
- ${c.green("module")} verify [id] ${t("help.module.verify")}
93
- ${c.green("module")} info <id> ${t("help.module.info")}
94
- ${c.green("module")} enable|disable <id>
95
- ${t("help.module.toggle")}
96
- ${c.green("module")} create ${t("help.module.create")}
97
- ${c.green("module")} link [id] ${t("help.module.link")}
98
- ${c.green("module")} dev ${t("help.module.dev")}
99
- ${c.green("module")} build ${t("help.module.build")}
100
- ${c.green("module")} reload [--build-only]
101
- ${t("help.module.reload")}
102
-
103
- ${c.bold(t("help.section.addon"))}
104
- ${c.green("addon")}/${c.green("packs")} list|search|enable|disable|bump|install|scan|doctor|path
105
- ${t("help.addon")}
106
-
107
- ${c.bold(t("help.section.general"))}
108
- ${c.green("init")} ${t("help.init")}
109
- ${c.green("locale")} [zh|en] ${t("help.locale")}
110
- ${c.green("version")} ${t("help.version")}
111
- ${c.green("help")} ${t("help.help")}
112
- ${c.green("quit")} / ${c.green("exit")} ${t("help.quit")}
113
-
114
- ${c.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")}
115
- ${c.dim(t("help.shortcuts"))}
116
- ${c.dim("Tab")} ${t("help.shortcut.tab")}
117
- ${c.dim("\u2192")} ${t("help.shortcut.right")}
118
- ${c.dim("Ctrl+L")} ${t("help.shortcut.ctrll")}
119
- ${c.dim("\u2191\u2193")} ${t("help.shortcut.history")}
120
- `;
73
+ const LOGO_ROWS = ["\u256D\u2500\u256E\u256D\u2500\u2574\u256D\u252C\u256E\u256D\u2500\u2574", "\u2570\u2500\u256E\u251C\u2574 \u2502\u2502\u2502\u2502 ", "\u2570\u2500\u256F\u2575 \u2575 \u2575\u2570\u2500\u2500\u2500"];
74
+ const LOGO_PAD = " ";
75
+ const LOGO_TOP_GAP = " ";
76
+ const LOGO_MID_GAP = " ";
77
+ const SHORTCUT_HINT = ` / \xB7 Tab \xB7 Ctrl+P \xB7 Ctrl+L \xB7 \u2190\u2192 \xB7 \u2191\u2193`;
78
+ const LOGO_STROKE_RAW = [
79
+ // S
80
+ [0, 1],
81
+ [0, 2],
82
+ [0, 0],
83
+ [1, 0],
84
+ [1, 1],
85
+ [1, 2],
86
+ [2, 2],
87
+ [2, 1],
88
+ [2, 0],
89
+ // F
90
+ [0, 3],
91
+ [1, 3],
92
+ [2, 3],
93
+ [0, 4],
94
+ [0, 5],
95
+ [1, 4],
96
+ // M
97
+ [0, 6],
98
+ [1, 6],
99
+ [2, 6],
100
+ [0, 7],
101
+ [1, 7],
102
+ [0, 8],
103
+ [1, 8],
104
+ [2, 8],
105
+ // C(末笔落到 ╰───,随后无缝接横线延长)
106
+ [0, 10],
107
+ [0, 11],
108
+ [0, 9],
109
+ [1, 9],
110
+ [2, 9],
111
+ [2, 10],
112
+ [2, 11],
113
+ [2, 12]
114
+ ];
115
+ const LOGO_STROKES = LOGO_STROKE_RAW.filter(([r, col]) => {
116
+ const ch = LOGO_ROWS[r]?.[col];
117
+ return ch !== void 0 && ch !== " ";
118
+ });
119
+ const metaPlain = `Scripts For Minecraft Server v${pkg.version}`;
120
+ const META_ACCENT_INDEX = /* @__PURE__ */ new Set([0, 8, 12, 16]);
121
+ const metaUnlit = chalk.hex(T.subtle);
122
+ const meta = `${c.text(`S`)}${c.dim(`cripts`)} ${c.text(`F`)}${c.dim(`or`)} ${c.text(`M`)}${c.dim(`ine`)}${c.text(`c`)}${c.dim(`raft Server`)} v${pkg.version}`;
123
+ const metaWidth = getDisplayWidth(metaPlain);
124
+ const lineLength = metaWidth + 5;
125
+ function buildTextAtProgress(plain, litColumns, paintLit) {
126
+ let cols = 0;
127
+ let out = "";
128
+ for (let i = 0; i < plain.length; i++) {
129
+ const ch = plain[i];
130
+ const w = charDisplayWidth(ch);
131
+ if (cols + w <= litColumns) {
132
+ out += paintLit(ch, i);
133
+ } else {
134
+ out += metaUnlit(ch);
135
+ }
136
+ cols += w;
137
+ }
138
+ return out;
121
139
  }
122
- const HELP = {
123
- toString() {
124
- return getHelp();
125
- },
126
- valueOf() {
127
- return getHelp();
140
+ function buildMetaAtProgress(litColumns) {
141
+ return buildTextAtProgress(metaPlain, litColumns, (ch, i) => META_ACCENT_INDEX.has(i) ? c.text(ch) : c.dim(ch));
142
+ }
143
+ function buildShortcutsAtProgress(litColumns) {
144
+ return buildTextAtProgress(SHORTCUT_HINT, litColumns, (ch) => c.dim(ch));
145
+ }
146
+ function buildLogoRows(strokeCount) {
147
+ const lit = /* @__PURE__ */ new Set();
148
+ const n = Math.min(Math.max(0, strokeCount), LOGO_STROKES.length);
149
+ for (let i = 0; i < n; i++) {
150
+ const [r, col] = LOGO_STROKES[i];
151
+ lit.add(`${r},${col}`);
128
152
  }
129
- };
130
- const COMMANDS = [
131
- "status",
132
- "logs",
133
- "start",
134
- "stop",
135
- "restart",
136
- "send",
137
- "init",
138
- "locale",
139
- "lang",
140
- "update",
141
- "remote",
142
- ...MODULE_CMD_NAMES,
143
- "packs",
144
- "addon",
145
- "version",
146
- "help",
147
- "quit",
148
- "exit"
149
- ];
153
+ return LOGO_ROWS.map((row, r) => {
154
+ let out = "";
155
+ for (let col = 0; col < row.length; col++) {
156
+ const ch = row[col];
157
+ if (ch === " ") {
158
+ out += " ";
159
+ } else if (lit.has(`${r},${col}`)) {
160
+ out += c.text(ch);
161
+ } else {
162
+ out += " ";
163
+ }
164
+ }
165
+ return out;
166
+ });
167
+ }
168
+ function buildWelcomeLines(strokeCount, lineProgress, metaContent, shortcutContent) {
169
+ const [topLogo, midLogo, botLogo] = buildLogoRows(strokeCount);
170
+ const line = "\u2500".repeat(Math.max(0, lineProgress));
171
+ return [
172
+ `
173
+ `,
174
+ `${LOGO_PAD}${topLogo}${LOGO_TOP_GAP}${shortcutContent}`,
175
+ `${LOGO_PAD}${midLogo}${LOGO_MID_GAP}${metaContent}`,
176
+ `${LOGO_PAD}${botLogo}${line}`
177
+ ];
178
+ }
179
+ function paintWelcomeLines(lines, firstFrame) {
180
+ if (!firstFrame) {
181
+ process.stdout.write(`\x1B[${lines.length}A`);
182
+ }
183
+ for (let i = 0; i < lines.length; i++) {
184
+ process.stdout.write(`\x1B[2K\r${lines[i]}`);
185
+ if (i < lines.length - 1) process.stdout.write("\n");
186
+ }
187
+ }
188
+ async function playWelcomeAnimation() {
189
+ const strokeTotal = LOGO_STROKES.length;
190
+ const totalSteps = strokeTotal + lineLength;
191
+ const finalShortcuts = c.dim(SHORTCUT_HINT);
192
+ if (!process.stdout.isTTY) {
193
+ const lines = buildWelcomeLines(strokeTotal, lineLength, c.bold(meta), finalShortcuts);
194
+ process.stdout.write(`
195
+ ${lines.join("\n")}
196
+
197
+ `);
198
+ return;
199
+ }
200
+ let step = 0;
201
+ let firstFrame = true;
202
+ process.stdout.write("\x1B[?25l");
203
+ process.stdout.write("\n");
204
+ return new Promise((resolve) => {
205
+ const done = () => {
206
+ const finalLines = buildWelcomeLines(strokeTotal, lineLength, c.bold(meta), finalShortcuts);
207
+ paintWelcomeLines(finalLines, firstFrame);
208
+ process.stdout.write("\x1B[?25h\n\n");
209
+ resolve();
210
+ };
211
+ const tick = () => {
212
+ const inLogo = step < strokeTotal;
213
+ const strokeCount = Math.min(step + 1, strokeTotal);
214
+ const lineProgress = inLogo ? 0 : Math.min(step - strokeTotal + 1, lineLength);
215
+ const metaContent = buildMetaAtProgress(Math.min(lineProgress, metaWidth));
216
+ const shortcutContent = buildShortcutsAtProgress(lineProgress);
217
+ const lines = buildWelcomeLines(strokeCount, lineProgress, metaContent, shortcutContent);
218
+ paintWelcomeLines(lines, firstFrame);
219
+ firstFrame = false;
220
+ if (step >= totalSteps - 1) {
221
+ done();
222
+ return;
223
+ }
224
+ step += 1;
225
+ setTimeout(tick, step < strokeTotal ? 22 : 5);
226
+ };
227
+ setTimeout(tick, 22);
228
+ });
229
+ }
230
+ function getHelp(mode = "argv") {
231
+ return buildHelp(mode);
232
+ }
233
+ function getCommands() {
234
+ return listVisibleTopLevelNames(REPL_MODE);
235
+ }
150
236
  function parseLine(line) {
151
237
  const endsWithSpace = line.length > 0 && /\s$/.test(line);
152
238
  const trimmed = line.trim();
@@ -169,14 +255,16 @@ function parseLine(line) {
169
255
  };
170
256
  }
171
257
  function getCompletions(parsed) {
172
- const { cmd, words, argIndex, current } = parsed;
258
+ const { words, argIndex, current } = parsed;
259
+ const cmd = parsed.cmd.startsWith("/") ? parsed.cmd.slice(1) : parsed.cmd;
173
260
  const sw = (s) => s.startsWith(current);
174
- if (!cmd) return COMMANDS.filter(sw);
261
+ if (!cmd) {
262
+ const cmds = getCommands().map((n) => n.startsWith("/") ? n : n);
263
+ return ["/", ...cmds].filter(sw);
264
+ }
175
265
  switch (cmd) {
176
266
  case "logs":
177
267
  case "log":
178
- if (argIndex === 0) return SERVICE_NAMES.filter(sw);
179
- if (argIndex === 1) return ["-n", "-f"].filter(sw);
180
268
  return [];
181
269
  case "start":
182
270
  case "stop":
@@ -188,19 +276,16 @@ function getCompletions(parsed) {
188
276
  return [];
189
277
  case "update":
190
278
  return ["--check-only", "--force", "--channel=release", "--channel=preview"].filter(sw);
191
- case "remote":
192
- if (argIndex === 0) return ["status", "enroll", "disable"].filter(sw);
193
- return [];
194
279
  case "packs":
195
280
  case "addon":
196
- if (argIndex === 0) return [...PACKS_SUBCOMMANDS].filter(sw);
281
+ if (argIndex === 0) return [...listVisiblePacksSubs(REPL_MODE)].filter(sw);
197
282
  if (argIndex === 1 && ["list"].includes(words[0] ?? "")) {
198
283
  return ["--kind", "--search"].filter(sw);
199
284
  }
200
285
  return [];
201
286
  default: {
202
287
  if (!isModuleCommand(cmd)) return [];
203
- if (argIndex === 0) return [...MODULE_SUBCOMMANDS].filter(sw);
288
+ if (argIndex === 0) return [...getVisibleModuleSubcommands(REPL_MODE)].filter(sw);
204
289
  const verb = words[0] ?? "";
205
290
  if (argIndex === 1 && verb === "search") {
206
291
  return listRegistryModuleIdsSync().filter(sw);
@@ -211,20 +296,9 @@ function getCompletions(parsed) {
211
296
  if (argIndex === 1 && verb === "reload") {
212
297
  return ["--build-only"].filter(sw);
213
298
  }
214
- if (argIndex === 1 && verb === "link") {
215
- const root = resolveSfmcModulesRoot();
216
- if (root) {
217
- const ids = listSfmcModulePackages(root).map((p) => p.id).filter(sw);
218
- if (ids.length) return ids;
219
- }
220
- return ["--from"].filter(sw);
221
- }
222
299
  if (argIndex >= 1 && verb === "install") {
223
300
  return ["--from", "--sha256", "--link"].filter(sw);
224
301
  }
225
- if (argIndex >= 2 && verb === "link") {
226
- return ["--from"].filter(sw);
227
- }
228
302
  if (argIndex >= 1 && verb === "list") {
229
303
  return ["--from", "--sha256"].filter(sw);
230
304
  }
@@ -246,107 +320,124 @@ function consumeEscapeSeq(chunk, i) {
246
320
  if (rem >= 1) return i + 2;
247
321
  return i + 1;
248
322
  }
249
- async function simpleSelect(items) {
250
- const wasRaw = stdin.isRaw ?? false;
251
- setRaw(true);
252
- stdin.resume();
253
- let selected = 0;
254
- const h = Math.min(items.length, 8);
255
- let lastLines = h;
256
- function render(first) {
257
- if (!first) {
258
- stdout.write(`\x1B[${lastLines}A\x1B[J`);
259
- } else {
260
- stdout.write("\x1B[J");
261
- }
262
- lastLines = h;
263
- let out = "";
264
- for (let i = 0; i < h; i++) {
265
- const cur = i === selected ? `\u25C9 ${c.text(items[i].label)}` : `\u25CB ${c.text(items[i].label)}`;
266
- out += `${cur}
267
- `;
268
- }
269
- stdout.write(out);
270
- }
271
- function clear() {
272
- stdout.write(`\x1B[${lastLines}A\x1B[J`);
273
- }
274
- render(true);
275
- return new Promise((resolve) => {
276
- const handler = (chunk) => {
277
- let i = 0;
278
- while (i < chunk.length) {
279
- if (chunk[i] === 27) {
280
- const rem = chunk.length - i - 1;
281
- if (rem === 0) {
282
- clear();
283
- stdin.removeListener("data", handler);
284
- setRaw(wasRaw);
285
- resolve(null);
286
- return;
287
- }
288
- const next = consumeEscapeSeq(chunk, i);
289
- if (next !== null) {
290
- const c2 = next - i;
291
- if (c2 === 3 && chunk[i + 1] === 91) {
292
- if (chunk[i + 2] === 65 && selected > 0) {
293
- selected--;
294
- render(false);
295
- }
296
- if (chunk[i + 2] === 66 && selected < items.length - 1) {
297
- selected++;
298
- render(false);
299
- }
300
- }
301
- i = next;
302
- } else i++;
303
- continue;
304
- }
305
- const byte = chunk[i];
306
- i++;
307
- if (byte === 13 || byte === 10) {
308
- clear();
309
- stdin.removeListener("data", handler);
310
- setRaw(wasRaw);
311
- resolve(items[selected]?.value ?? null);
312
- return;
313
- }
314
- if (byte === 3) {
315
- clear();
316
- stdin.removeListener("data", handler);
317
- setRaw(wasRaw);
318
- resolve(null);
319
- return;
320
- }
321
- }
322
- };
323
- stdin.on("data", handler);
324
- });
325
- }
326
323
  const history = [];
327
324
  let historyIdx = -1;
328
325
  let currentRedraw = null;
329
- async function readLine(prompt, initial = "") {
326
+ async function readLine(opts) {
330
327
  const wasRaw = stdin.isRaw ?? false;
331
328
  setRaw(true);
332
329
  stdin.resume();
333
- let line = initial;
330
+ let line = opts.initial ?? "";
331
+ let cursor = line.length;
334
332
  let suggestion = "";
335
- let tabState = null;
333
+ let palSels = [0];
334
+ let palScrolls = [0];
335
+ let overlayRows = 0;
336
+ function prompt() {
337
+ return opts.getPrompt();
338
+ }
339
+ function showsInput() {
340
+ return opts.getChrome?.().showsInput !== false;
341
+ }
342
+ function slashMode() {
343
+ return showsInput() && line.startsWith("/");
344
+ }
345
+ function paletteView() {
346
+ return resolvePaletteView(line, palSels, palScrolls);
347
+ }
348
+ function syncPaletteState(view) {
349
+ for (let i = 0; i < view.columns.length; i++) {
350
+ palSels[i] = view.columns[i].selected;
351
+ palScrolls[i] = view.columns[i].scroll;
352
+ }
353
+ }
354
+ function resetPalette() {
355
+ palSels = [0];
356
+ palScrolls = [0];
357
+ }
358
+ function clearOverlay() {
359
+ stdout.write("\r\x1B[J");
360
+ overlayRows = 0;
361
+ }
362
+ function applyWindowKey(ev) {
363
+ if (!opts.onWindowKey) return false;
364
+ const r = opts.onWindowKey(ev);
365
+ if (r.action === "none") return false;
366
+ if (r.action === "redraw") redraw();
367
+ return true;
368
+ }
336
369
  function redraw() {
337
- const parsed = parseLine(line);
338
- const candidates = getCompletions(parsed);
339
- const first = candidates[0];
340
- suggestion = parsed.current && first && first !== parsed.current && first.startsWith(parsed.current) ? first.slice(parsed.current.length) : "";
341
- stdout.write("\r\x1B[K" + prompt + line);
342
- if (suggestion) {
343
- stdout.write(c.dim(suggestion));
344
- stdout.write("\x1B[" + suggestion.length + "D");
370
+ const chrome = opts.getChrome?.();
371
+ if (chrome && !chrome.showsInput) {
372
+ clearOverlay();
373
+ const foot = chrome.footerShortcuts || "";
374
+ stdout.write(foot);
375
+ stdout.write("\r");
376
+ return;
377
+ }
378
+ const p = prompt();
379
+ suggestion = "";
380
+ if (slashMode()) {
381
+ const view = paletteView();
382
+ syncPaletteState(view);
383
+ if (cursor === line.length) {
384
+ suggestion = paletteGhost(view);
385
+ }
386
+ } else {
387
+ const parsed = parseLine(line);
388
+ const candidates = getCompletions(parsed);
389
+ const first = candidates[0];
390
+ if (cursor === line.length && parsed.current && first && first !== parsed.current && first.startsWith(parsed.current)) {
391
+ suggestion = first.slice(parsed.current.length);
392
+ }
393
+ }
394
+ clearOverlay();
395
+ stdout.write(p + line);
396
+ if (suggestion) stdout.write(c.dim(suggestion));
397
+ if (slashMode()) {
398
+ const view = paletteView();
399
+ const indent = promptSlashColumn(p);
400
+ const list = formatPaletteCascadeLines(view, indent);
401
+ if (list.length > 0) {
402
+ stdout.write("\n" + list.join("\n"));
403
+ overlayRows = list.length;
404
+ stdout.write(`\x1B[${overlayRows}A`);
405
+ }
345
406
  }
407
+ const col = promptSlashColumn(p) + cursor + 1;
408
+ stdout.write(`\r\x1B[${col}G`);
346
409
  }
347
410
  redraw();
348
411
  currentRedraw = redraw;
349
412
  return new Promise((resolve) => {
413
+ const done = (value) => {
414
+ stdin.removeListener("data", handler);
415
+ setRaw(wasRaw);
416
+ currentRedraw = null;
417
+ resolve(value);
418
+ };
419
+ const finishLine = (value) => {
420
+ clearOverlay();
421
+ stdout.write(prompt() + value + "\r\n");
422
+ if (value.length > 0) {
423
+ history.push(value);
424
+ if (history.length > 100) history.shift();
425
+ }
426
+ historyIdx = history.length;
427
+ done({ kind: "line", value });
428
+ };
429
+ const movePalSel = (delta) => {
430
+ const view = paletteView();
431
+ if (view.hidden) return false;
432
+ const col = view.columns[view.active];
433
+ if (!col || col.items.length === 0) return false;
434
+ const len = col.items.length;
435
+ const cur = clampPaletteSelection(col.selected, len);
436
+ const next = ((cur + delta) % len + len) % len;
437
+ palSels[view.active] = next;
438
+ redraw();
439
+ return true;
440
+ };
350
441
  const handler = (chunk) => {
351
442
  let i = 0;
352
443
  while (i < chunk.length) {
@@ -354,28 +445,69 @@ async function readLine(prompt, initial = "") {
354
445
  const next = consumeEscapeSeq(chunk, i);
355
446
  if (next !== null) {
356
447
  const len = next - i;
448
+ if (len === 1) {
449
+ if (applyWindowKey({ type: "escape" })) {
450
+ i = next;
451
+ continue;
452
+ }
453
+ i = next;
454
+ continue;
455
+ }
456
+ if (!showsInput()) {
457
+ i = next;
458
+ continue;
459
+ }
357
460
  if (len === 3 && chunk[i + 1] === 91) {
358
461
  const fin = chunk[i + 2];
359
- if (fin === 65 && historyIdx > 0) {
360
- historyIdx--;
361
- line = history[historyIdx] ?? "";
362
- tabState = null;
363
- redraw();
462
+ if (fin === 65) {
463
+ if (slashMode() && movePalSel(-1)) {
464
+ i = next;
465
+ continue;
466
+ }
467
+ if (historyIdx > 0) {
468
+ historyIdx--;
469
+ line = history[historyIdx] ?? "";
470
+ cursor = line.length;
471
+ resetPalette();
472
+ redraw();
473
+ }
364
474
  } else if (fin === 66) {
475
+ if (slashMode() && movePalSel(1)) {
476
+ i = next;
477
+ continue;
478
+ }
365
479
  if (historyIdx < history.length - 1) {
366
480
  historyIdx++;
367
481
  line = history[historyIdx] ?? "";
368
- tabState = null;
482
+ cursor = line.length;
483
+ resetPalette();
369
484
  redraw();
370
485
  } else if (historyIdx === history.length - 1) {
371
486
  historyIdx = history.length;
372
487
  line = "";
373
- tabState = null;
488
+ cursor = 0;
489
+ resetPalette();
490
+ redraw();
491
+ }
492
+ } else if (fin === 67) {
493
+ if (cursor < line.length) {
494
+ cursor++;
495
+ redraw();
496
+ } else if (suggestion) {
497
+ line += suggestion;
498
+ cursor = line.length;
499
+ redraw();
500
+ }
501
+ } else if (fin === 68) {
502
+ if (cursor > 0) {
503
+ cursor--;
374
504
  redraw();
375
505
  }
376
- } else if (fin === 67 && suggestion) {
377
- line += suggestion;
378
- tabState = null;
506
+ } else if (fin === 72) {
507
+ cursor = 0;
508
+ redraw();
509
+ } else if (fin === 70) {
510
+ cursor = line.length;
379
511
  redraw();
380
512
  }
381
513
  }
@@ -386,133 +518,125 @@ async function readLine(prompt, initial = "") {
386
518
  const byte = chunk[i];
387
519
  i++;
388
520
  if (byte === 13 || byte === 10) {
389
- stdin.removeListener("data", handler);
390
- setRaw(wasRaw);
391
- stdout.write("\r\x1B[K" + prompt + line + "\r\n");
392
- if (line.length > 0) {
393
- history.push(line);
394
- if (history.length > 100) history.shift();
521
+ if (!showsInput()) continue;
522
+ if (slashMode()) {
523
+ const view = paletteView();
524
+ syncPaletteState(view);
525
+ if (!view.hidden && view.columns.some((col) => col.items.length > 0) && activeNode(view)) {
526
+ const acc = commitSelection(line, view);
527
+ if (acc.submit) {
528
+ finishLine(acc.line);
529
+ return;
530
+ }
531
+ line = acc.line;
532
+ cursor = line.length;
533
+ const depth = Math.max(0, acc.line.trimEnd().split(/\s+/).length - 1);
534
+ while (palSels.length <= depth + 1) palSels.push(0);
535
+ while (palScrolls.length <= depth + 1) palScrolls.push(0);
536
+ palSels[depth + 1] = 0;
537
+ palScrolls[depth + 1] = 0;
538
+ redraw();
539
+ continue;
540
+ }
395
541
  }
396
- historyIdx = history.length;
397
- resolve(line);
542
+ finishLine(line);
398
543
  return;
399
544
  }
400
545
  if (byte === 3) {
401
- stdin.removeListener("data", handler);
402
- setRaw(wasRaw);
546
+ if (!showsInput()) {
547
+ if (applyWindowKey({ type: "escape" })) continue;
548
+ clearOverlay();
549
+ stdout.write("\r\n");
550
+ done({ kind: "cancel" });
551
+ return;
552
+ }
403
553
  if (line.length > 0) {
404
554
  line = "";
405
- tabState = null;
406
- stdout.write("\r\x1B[K" + prompt);
555
+ cursor = 0;
556
+ resetPalette();
557
+ redraw();
407
558
  continue;
408
559
  }
560
+ clearOverlay();
409
561
  stdout.write("\r\n");
410
- resolve(null);
562
+ done({ kind: "cancel" });
411
563
  return;
412
564
  }
413
- if (byte === 9) {
414
- if (tabState && line === tabState.completedLine) {
415
- tabState.idx = (tabState.idx + 1) % tabState.candidates.length;
416
- } else {
417
- const parsed = parseLine(line);
418
- const candidates = getCompletions(parsed);
419
- if (candidates.length === 0) {
420
- tabState = null;
421
- redraw();
422
- continue;
423
- }
424
- tabState = {
425
- candidates,
426
- idx: 0,
427
- wordStart: line.length - parsed.current.length,
428
- completedLine: ""
429
- };
565
+ if (byte === 16) {
566
+ if (!showsInput()) continue;
567
+ if (!line.startsWith("/")) {
568
+ line = "/" + line;
569
+ cursor = Math.min(cursor + 1, line.length);
430
570
  }
431
- const match = tabState.candidates[tabState.idx];
432
- line = line.slice(0, tabState.wordStart) + match;
433
- tabState.completedLine = line;
571
+ resetPalette();
434
572
  redraw();
435
573
  continue;
436
574
  }
575
+ if (byte === 9) {
576
+ if (slashMode()) {
577
+ movePalSel(1);
578
+ continue;
579
+ }
580
+ if (opts.cycleSendTarget()) {
581
+ redraw();
582
+ continue;
583
+ }
584
+ continue;
585
+ }
437
586
  if (byte === 12) {
438
- stdin.removeListener("data", handler);
439
- setRaw(wasRaw);
440
- resolve("__CTRLL__" + line);
587
+ clearOverlay();
588
+ done({ kind: "open-logs", pending: showsInput() ? line : "" });
441
589
  return;
442
590
  }
443
591
  if (byte === 127 || byte === 8) {
444
- if (line.length > 0) {
445
- line = line.slice(0, -1);
592
+ if (!showsInput()) continue;
593
+ if (cursor > 0) {
594
+ line = line.slice(0, cursor - 1) + line.slice(cursor);
595
+ cursor--;
596
+ }
597
+ if (slashMode()) {
598
+ const view = paletteView();
599
+ palSels[view.active] = 0;
600
+ palScrolls[view.active] = 0;
446
601
  }
447
- tabState = null;
448
602
  redraw();
449
603
  continue;
450
604
  }
451
605
  if (byte >= 32 && byte <= 126) {
452
- line += String.fromCharCode(byte);
453
- tabState = null;
606
+ const ch = String.fromCharCode(byte);
607
+ if (!showsInput()) {
608
+ applyWindowKey({ type: "char", ch });
609
+ continue;
610
+ }
611
+ let insert = ch;
612
+ const autoSlash = typeof opts.autoSlash === "function" ? opts.autoSlash() : opts.autoSlash;
613
+ if (autoSlash && line.length === 0 && insert !== "/") {
614
+ insert = "/" + insert;
615
+ }
616
+ line = line.slice(0, cursor) + insert + line.slice(cursor);
617
+ cursor += insert.length;
618
+ if (slashMode()) {
619
+ const view = paletteView();
620
+ palSels[view.active] = 0;
621
+ palScrolls[view.active] = 0;
622
+ }
454
623
  redraw();
455
624
  continue;
456
625
  }
457
626
  }
458
627
  };
459
628
  stdin.on("data", handler);
460
- }).finally(() => {
461
- currentRedraw = null;
462
629
  });
463
630
  }
464
- const LEVEL_ITEMS = [
465
- { label: c.blue("INFO"), value: "info" },
466
- { label: c.yellow("WARN"), value: "warn" },
467
- { label: c.red("ERROR"), value: "error" },
468
- { label: c.dim("DEBUG"), value: "debug" },
469
- { label: c.green("SUCCESS"), value: "success" }
470
- ];
471
631
  const SOURCE_ITEMS = SOURCE_META.map((m) => ({
472
- label: m.paint(m.name),
632
+ label: m.paint(`[${m.name}]`),
473
633
  value: m.value
474
634
  }));
475
- function historyItems() {
476
- return [
477
- { label: t("repl.history.live"), value: "none" },
478
- { label: t("repl.history.last50"), value: "count:50" },
479
- { label: t("repl.history.last100"), value: "count:100" },
480
- { label: t("repl.history.last500"), value: "count:500" },
481
- { label: t("repl.history.last1000"), value: "count:1000" },
482
- { label: t("repl.history.last1min"), value: "time:60000" },
483
- { label: t("repl.history.last5min"), value: "time:300000" },
484
- { label: t("repl.history.last15min"), value: "time:900000" },
485
- { label: t("repl.history.last1hour"), value: "time:3600000" },
486
- { label: t("repl.history.all"), value: "all" }
487
- ];
488
- }
489
- function queryHistory(filter, window) {
490
- if (!window || window === "none") return [];
491
- const match = (l) => {
492
- if (filter.levels.length && !filter.levels.includes(l.level)) return false;
493
- if (filter.sources.length && !filter.sources.includes(l.source)) return false;
494
- return true;
495
- };
496
- if (window === "all") return getAllLogs().filter(match);
497
- if (window.startsWith("count:")) {
498
- const n = Number(window.slice("count:".length));
499
- if (!Number.isFinite(n) || n <= 0) return [];
500
- return getRecentLogs(n, filter.levels, filter.sources);
501
- }
502
- if (window.startsWith("time:")) {
503
- const ms = Number(window.slice("time:".length));
504
- if (!Number.isFinite(ms) || ms <= 0) return [];
505
- const since = Date.now() - ms;
506
- return getAllLogs().filter((l) => match(l) && l.time.getTime() >= since);
507
- }
508
- return [];
509
- }
510
- function pushAndRender(log, filter) {
511
- if (filter.levels.length && !filter.levels.includes(log.level)) return;
512
- if (filter.sources.length && !filter.sources.includes(log.source)) return;
635
+ let openLogsWindowHook = null;
636
+ function writeLogLineToTty(wrapped) {
513
637
  pauseAllProgress();
514
638
  try {
515
- stdout.write(`\r\x1B[K${wrapLogLine(formatLog(log), logPrefixWidth(log))}
639
+ stdout.write(`\r\x1B[K${wrapped}
516
640
  `);
517
641
  } finally {
518
642
  resumeAllProgress();
@@ -521,17 +645,25 @@ function pushAndRender(log, filter) {
521
645
  }
522
646
  async function startRepl() {
523
647
  let stopping = false;
648
+ const onSigint = () => {
649
+ stdout.write(c.yellow("\n" + t("repl.forceStop") + "\n"));
650
+ forceStopAll();
651
+ process.exit(130);
652
+ };
524
653
  const shutdown = async () => {
525
654
  if (stopping) return;
526
655
  stopping = true;
656
+ process.off("SIGINT", onSigint);
657
+ openLogsWindowHook = null;
527
658
  stdout.write(c.dim(t("repl.stopping") + "\n"));
528
659
  await stopAll();
660
+ try {
661
+ setRaw(false);
662
+ stdin.pause();
663
+ } catch {
664
+ }
529
665
  stdout.write(c.dim(t("repl.bye") + "\n"));
530
- };
531
- const onSigint = () => {
532
- stdout.write(c.yellow("\n" + t("repl.forceStop") + "\n"));
533
- forceStopAll();
534
- process.exit(130);
666
+ process.exit(0);
535
667
  };
536
668
  process.on("SIGINT", onSigint);
537
669
  if (!stdin.isTTY) {
@@ -552,107 +684,226 @@ async function startRepl() {
552
684
  await execCmd(p);
553
685
  }
554
686
  await shutdown();
555
- process.off("SIGINT", onSigint);
556
687
  return;
557
688
  }
558
689
  console.clear();
559
- stdout.write(welcome);
560
- let filter = { levels: [], sources: [] };
561
- const unsub = onLog((log) => pushAndRender(log, filter));
690
+ await playWelcomeAnimation();
691
+ let logsFilterState = { levels: [], sources: [] };
692
+ let preferredTarget = null;
693
+ let activeTargets = [];
694
+ let pendingInput = "";
695
+ const host = new WindowHost({
696
+ writeLog: writeLogLineToTty,
697
+ onChromeChange: () => currentRedraw?.()
698
+ });
699
+ function clearTerminal() {
700
+ console.clear();
701
+ stdout.write("\x1B[3J");
702
+ }
703
+ function seedWindowBuffer(w) {
704
+ const lines = [];
705
+ if (w.id === "logs") {
706
+ const f = w.getReplayFilter?.() ?? { levels: [], sources: [] };
707
+ const disk = readDiskLogs({
708
+ levels: f.levels,
709
+ sources: f.sources
710
+ });
711
+ for (const log of disk) {
712
+ if (!w.acceptLog(log)) continue;
713
+ const { text, indent } = w.formatLogLine(log);
714
+ lines.push(wrapLogLine(text, indent));
715
+ }
716
+ } else {
717
+ for (const log of getAllLogs()) {
718
+ if (!w.acceptLog(log)) continue;
719
+ const { text, indent } = w.formatLogLine(log);
720
+ lines.push(wrapLogLine(text, indent));
721
+ }
722
+ }
723
+ host.setBuffer(w.id, lines);
724
+ return lines;
725
+ }
726
+ function showActiveWindow(opts) {
727
+ clearTerminal();
728
+ const w = host.getActive();
729
+ if (!w) {
730
+ currentRedraw?.();
731
+ return;
732
+ }
733
+ let lines;
734
+ if (w.id === "logs") {
735
+ if (opts?.reseed) host.invalidateBuffer(w.id);
736
+ lines = host.hasBuffer(w.id) ? host.getBuffer(w.id) : seedWindowBuffer(w);
737
+ } else {
738
+ host.invalidateBuffer(w.id);
739
+ lines = seedWindowBuffer(w);
740
+ }
741
+ for (const l of lines) stdout.write(l + "\n");
742
+ currentRedraw?.();
743
+ }
744
+ host.register(
745
+ createLogsFilterWindow({
746
+ getState: () => logsFilterState,
747
+ setState: (next) => {
748
+ logsFilterState = next;
749
+ },
750
+ /* 筛选变更:作废 /logs 缓存并重新读盘一次 */
751
+ onFilterChanged: () => showActiveWindow({ reseed: true })
752
+ })
753
+ );
754
+ function syncServiceWindows(targets) {
755
+ for (const name of targets) {
756
+ const id = serviceWindowId(name);
757
+ if (!host.has(id)) host.register(createServiceWindow(name));
758
+ }
759
+ host.setServiceOrder(targets.map(serviceWindowId));
760
+ const svc = host.getActive()?.serviceName;
761
+ if (svc) preferredTarget = svc;
762
+ }
763
+ function openLogsWindow() {
764
+ host.open("logs");
765
+ showActiveWindow();
766
+ }
767
+ openLogsWindowHook = openLogsWindow;
768
+ const unsub = onLog((log) => {
769
+ if (host.getActive()) {
770
+ host.routeLog(log);
771
+ return;
772
+ }
773
+ writeLogLineToTty(wrapLogLine(formatLog(log), logPrefixWidth(log)));
774
+ });
775
+ async function refreshTargetsFromRuntime() {
776
+ const prevId = host.getActiveId();
777
+ activeTargets = await listActiveSendTargets();
778
+ syncServiceWindows(activeTargets);
779
+ if (preferredTarget && !activeTargets.includes(preferredTarget)) {
780
+ preferredTarget = activeTargets[0] ?? null;
781
+ }
782
+ const nextId = host.getActiveId();
783
+ if (nextId && (nextId !== prevId || !host.hasBuffer(nextId))) {
784
+ showActiveWindow();
785
+ } else if (!nextId && prevId) {
786
+ clearTerminal();
787
+ currentRedraw?.();
788
+ } else {
789
+ currentRedraw?.();
790
+ }
791
+ }
792
+ const unsubState = onServiceStateChange(() => {
793
+ void refreshTargetsFromRuntime();
794
+ });
562
795
  function onResize() {
563
796
  if (!currentRedraw) return;
564
- const rows = process.stdout.rows || 24;
565
- stdout.write("\x1B[H\x1B[2J");
566
- const all = getAllLogs();
567
- const out = [];
568
- let usedRows = 0;
569
- for (let i = all.length - 1; i >= 0; i--) {
570
- const log = all[i];
571
- if (filter.levels.length && !filter.levels.includes(log.level)) continue;
572
- if (filter.sources.length && !filter.sources.includes(log.source)) continue;
573
- const wrapped = wrapLogLine(formatLog(log), logPrefixWidth(log));
574
- const logRows = wrapped.split("\n").length;
575
- if (usedRows + logRows > rows - 2) break;
576
- out.unshift(wrapped);
577
- usedRows += logRows;
578
- }
579
- for (const l of out) stdout.write(l + "\n");
580
- currentRedraw();
797
+ const id = host.getActiveId();
798
+ if (id) host.invalidateBuffer(id);
799
+ showActiveWindow();
581
800
  }
582
801
  process.stdout.on("resize", onResize);
583
- let pendingInput = "";
802
+ function currentTarget() {
803
+ if (activeTargets.length === 0) return null;
804
+ const fromWin = host.getActive()?.serviceName;
805
+ if (fromWin && activeTargets.includes(fromWin)) {
806
+ preferredTarget = fromWin;
807
+ return fromWin;
808
+ }
809
+ if (preferredTarget && activeTargets.includes(preferredTarget)) return preferredTarget;
810
+ preferredTarget = activeTargets[0];
811
+ return preferredTarget;
812
+ }
813
+ function buildPrompt() {
814
+ const tgt = currentTarget();
815
+ return tgt ? paintSendPrompt(tgt) : plainPrompt();
816
+ }
584
817
  while (true) {
585
- const raw = await readLine(c.text(" \u276F "), pendingInput);
818
+ await refreshTargetsFromRuntime();
819
+ const result = await readLine({
820
+ getPrompt: buildPrompt,
821
+ /* 无服务时默认加 /(与窗口系统前行为一致);用 getter 以便进程退出后即时生效 */
822
+ autoSlash: () => activeTargets.length === 0,
823
+ getChrome: () => host.getChrome(),
824
+ onWindowKey: (ev) => {
825
+ const r = host.onKey(ev);
826
+ if (r.action === "redraw") showActiveWindow();
827
+ return r;
828
+ },
829
+ cycleSendTarget: () => {
830
+ const name = host.cycleServiceWindows();
831
+ if (!name) return false;
832
+ preferredTarget = name;
833
+ showActiveWindow();
834
+ return true;
835
+ },
836
+ initial: pendingInput
837
+ });
586
838
  pendingInput = "";
587
- if (raw === null) break;
588
- if (raw.startsWith("__CTRLL__")) {
589
- stdout.write(c.dim(`
590
- LEVEL\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500SOURCE\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500HISTORY
591
- `));
592
- const histChoices = historyItems();
593
- const lvl = await simpleSelect([{ label: t("common.all"), value: "" }, ...LEVEL_ITEMS]);
594
- if (lvl === null) continue;
595
- const src = await simpleSelect([{ label: t("common.all"), value: "" }, ...SOURCE_ITEMS]);
596
- if (src === null) continue;
597
- const hist = await simpleSelect(histChoices);
598
- if (hist === null) continue;
599
- filter = { levels: lvl ? [lvl] : [], sources: src ? [src] : [] };
600
- const replay = queryHistory(filter, hist);
601
- if (replay.length > 0) {
602
- stdout.write(c.dim(t("repl.historyHeader", { count: replay.length }) + "\n"));
603
- for (const log of replay) {
604
- stdout.write(`${wrapLogLine(formatLog(log), logPrefixWidth(log))}
605
- `);
606
- }
839
+ if (result.kind === "cancel") break;
840
+ if (result.kind === "open-logs") {
841
+ pendingInput = result.pending;
842
+ openLogsWindow();
843
+ continue;
844
+ }
845
+ if (result.kind === "run") {
846
+ try {
847
+ await execCmd(result.parts);
848
+ } catch (e) {
849
+ if (e === "QUIT") break;
850
+ console.log(c.red(t("common.error", { message: e.message })));
607
851
  }
608
- const histLabel = histChoices.find((i) => i.value === hist)?.label ?? hist;
609
- stdout.write(
610
- c.dim(t("repl.filter", { level: lvl || "*", source: src || "*", history: histLabel }) + "\n")
611
- );
612
852
  continue;
613
853
  }
614
- const trimmed = raw.trim();
854
+ const trimmed = result.value.trim();
615
855
  if (!trimmed) continue;
616
856
  try {
617
- await execCmd(trimmed.split(/\s+/));
857
+ if (trimmed.startsWith("/")) {
858
+ await execCmd(trimmed.split(/\s+/));
859
+ } else {
860
+ const tgt = currentTarget();
861
+ if (!tgt) {
862
+ await execCmd(("/" + trimmed).split(/\s+/));
863
+ } else {
864
+ const sent = await cmdSend(tgt, trimmed);
865
+ if (sent) stdout.write(sent + "\n");
866
+ }
867
+ }
618
868
  } catch (e) {
619
869
  if (e === "QUIT") break;
620
870
  console.log(c.red(t("common.error", { message: e.message })));
621
871
  }
622
872
  }
623
873
  process.stdout.off("resize", onResize);
874
+ unsubState();
624
875
  unsub();
625
876
  await shutdown();
626
- process.off("SIGINT", onSigint);
627
877
  }
628
878
  async function execCmd(parts) {
629
- const [cmd, ...args] = parts;
879
+ const normalized = parts.map((p, i) => i === 0 && p.startsWith("/") ? p.slice(1) : p);
880
+ const [cmd, ...args] = normalized;
881
+ if (cmd && !["help", "h", "?", "version"].includes(cmd)) {
882
+ const topGate = gateTopLevel(cmd, REPL_MODE);
883
+ if (topGate) {
884
+ stdout.write(topGate + "\n");
885
+ return;
886
+ }
887
+ }
630
888
  switch (cmd) {
631
889
  case "help":
632
890
  case "h":
633
891
  case "?":
634
- stdout.write(getHelp());
892
+ stdout.write(getHelp(REPL_MODE));
635
893
  break;
636
894
  case "version":
637
- stdout.write(`${version}
638
- `);
639
- break;
640
- case "locale":
641
- case "lang":
642
- stdout.write(cmdLocale(args) + "\n");
895
+ await playWelcomeAnimation();
643
896
  break;
644
897
  case "status":
645
- stdout.write(cmdStatus() + "\n");
898
+ stdout.write(await cmdStatus() + "\n");
646
899
  break;
647
900
  case "logs":
648
901
  case "log": {
649
- const out = cmdLogs(args, () => {
650
- if (!stdin.isTTY) {
651
- stdout.write(c.yellow(t("repl.followRequiresTty") + "\n"));
652
- return;
653
- }
654
- });
655
- if (out) stdout.write(out + "\n");
902
+ if (openLogsWindowHook) {
903
+ openLogsWindowHook();
904
+ break;
905
+ }
906
+ stdout.write(c.yellow(t("repl.logsReplOnly") + "\n"));
656
907
  break;
657
908
  }
658
909
  case "start":
@@ -675,31 +926,30 @@ async function execCmd(parts) {
675
926
  case "send": {
676
927
  const svc = args[0] ?? "";
677
928
  const msg = args.slice(1).join(" ");
678
- stdout.write(await cmdSend(svc, msg) + "\n");
679
- break;
680
- }
681
- case "init": {
682
- const { runWizard } = await import("./wizard.js");
683
- await runWizard();
929
+ {
930
+ const sent = await cmdSend(svc, msg);
931
+ if (sent) stdout.write(sent + "\n");
932
+ }
684
933
  break;
685
934
  }
686
935
  case "update":
687
- await cmdUpdate(args);
936
+ stdout.write(await cmdUpdate(args) + "\n");
688
937
  break;
689
- case "remote": {
690
- const [subcommand, controllerUrl, enrollmentToken, name] = args;
691
- if (subcommand === "status") {
692
- stdout.write(JSON.stringify(remoteStatus(), null, 2) + "\n");
693
- } else if (subcommand === "enroll" && controllerUrl && enrollmentToken) {
694
- const agentName = name ?? process.env.COMPUTERNAME ?? "sfmc-agent";
695
- const id = await enrollRemoteAgent(controllerUrl, enrollmentToken, agentName);
696
- stdout.write(t("remote.enrolled", { id }) + "\n");
697
- startRemoteAgent();
698
- } else if (subcommand === "disable") {
699
- disableRemoteAgent();
700
- stdout.write(c.dim(t("remote.disabled") + "\n"));
701
- } else {
702
- stdout.write(t("remote.usageShort") + "\n");
938
+ case "locale":
939
+ case "lang":
940
+ stdout.write(cmdLocale(args) + "\n");
941
+ break;
942
+ case "debug":
943
+ stdout.write(await cmdDebug(args) + "\n");
944
+ break;
945
+ case "init": {
946
+ const wasRaw = stdin.isRaw ?? false;
947
+ setRaw(false);
948
+ try {
949
+ const { runWizard } = await import("./wizard.js");
950
+ await runWizard();
951
+ } finally {
952
+ setRaw(wasRaw);
703
953
  }
704
954
  break;
705
955
  }
@@ -707,24 +957,45 @@ async function execCmd(parts) {
707
957
  case "exit":
708
958
  case "q":
709
959
  throw "QUIT";
710
- default:
960
+ default: {
961
+ const modShort = resolveModuleTopShorthand(cmd);
962
+ if (modShort) {
963
+ const g = gateModuleSub(modShort, REPL_MODE);
964
+ if (g) {
965
+ stdout.write(g + "\n");
966
+ break;
967
+ }
968
+ stdout.write(await dispatchModuleCommand(modShort, args) + "\n");
969
+ break;
970
+ }
711
971
  if (isModuleCommand(cmd)) {
712
972
  const [sub, ...subRest] = args;
973
+ const g = gateModuleSub(sub, REPL_MODE);
974
+ if (g) {
975
+ stdout.write(g + "\n");
976
+ break;
977
+ }
713
978
  stdout.write(await dispatchModuleCommand(sub, subRest) + "\n");
714
979
  break;
715
980
  }
716
981
  if (isPacksCommand(cmd)) {
717
982
  const [sub, ...subRest] = args;
983
+ const g = gatePacksSub(sub, REPL_MODE);
984
+ if (g) {
985
+ stdout.write(g + "\n");
986
+ break;
987
+ }
718
988
  stdout.write(await dispatchPacksCommand(sub, subRest) + "\n");
719
989
  break;
720
990
  }
721
991
  stdout.write(c.yellow(t("common.unknownShort", { cmd: cmd ?? "" }) + "\n"));
992
+ }
722
993
  }
723
994
  }
724
995
  export {
725
- HELP,
726
996
  SOURCE_ITEMS,
727
997
  getHelp,
998
+ playWelcomeAnimation,
728
999
  startRepl
729
1000
  };
730
1001
  //# sourceMappingURL=repl.js.map