From 5cb6467e308c57cc5460e10c782592b0076ea718 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 22 Jul 2026 17:00:14 +0000 Subject: [PATCH 1/2] =?UTF-8?q?fix(ootb):=20=E6=8E=A5=E5=8F=97=20module/mo?= =?UTF-8?q?d=20HELP=20=E5=88=AB=E5=90=8D=E5=B9=B6=20DRY=20=E5=91=BD?= =?UTF-8?q?=E4=BB=A4=E5=88=86=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit feat@8fc0989 将 HELP 改为 module/mod 后,check-ootb 的 /module\s+(list|install)/ 误报失败。同步: - ootb 剥离 ANSI,匹配 module[/|]mod 展示 - MODULE_CMD_NAMES 作为 HELP/分发/USAGE 唯一来源 - 移除误提交的 npm pack *.tgz 并 gitignore Co-authored-by: Shiroha --- .gitignore | 1 + package-lock.json | 2 +- sfmc-bds-sfmc-0.1.0.tgz | Bin 3445 -> 0 bytes sfmc/src/main.ts | 13 ++++++------ sfmc/src/module-commands.ts | 17 ++++++++++++++- sfmc/src/repl.ts | 41 ++++++++++++++++++++---------------- tools/check-ootb.mjs | 7 ++++-- 7 files changed, 52 insertions(+), 29 deletions(-) delete mode 100644 sfmc-bds-sfmc-0.1.0.tgz diff --git a/.gitignore b/.gitignore index 09c4f200..54d2e74a 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ tools/.sfmc-registry-cache.json *.tmp *.tmp.* *.log +*.tgz # Proactive-agent credentials (security-audit.sh) .credentials/ diff --git a/package-lock.json b/package-lock.json index 8c0a5113..ed704950 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3171,7 +3171,7 @@ "sfmc-meta": { "name": "@sfmc-bds/sfmc", "version": "0.1.0", - "license": "ISC", + "license": "GPL-3.0-only", "dependencies": { "@sfmc-bds/bds-tools": "^0.1.0", "@sfmc-bds/cli": "^0.1.0", diff --git a/sfmc-bds-sfmc-0.1.0.tgz b/sfmc-bds-sfmc-0.1.0.tgz deleted file mode 100644 index 6534ac0384cee31f4fd8943658bc65d29d11fd1d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3445 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 XAQU*b4z7diPj>wWQW44405$*s47AbR diff --git a/sfmc/src/main.ts b/sfmc/src/main.ts index 492a844c..d350136d 100644 --- a/sfmc/src/main.ts +++ b/sfmc/src/main.ts @@ -3,7 +3,7 @@ import process from "node:process"; import pkg from "../package.json" with { type: "json" }; import { cmdLogs, cmdRestart, cmdStart, cmdStartAll, cmdStatus, cmdStop, cmdStopAll, cmdUpdate } from "./commands.js"; import { HELP, startRepl } from "./repl.js"; -import { dispatchModuleCommand, scanAndWarnUnknown } from "./module-commands.js"; +import { dispatchModuleCommand, isModuleCommand, scanAndWarnUnknown } from "./module-commands.js"; import { cmdBehaviorPackBuild, cmdBehaviorPackDeploy } from "./commands-behavior-pack.js"; import { disableRemoteAgent, enrollRemoteAgent, remoteStatus, startRemoteAgent, stopRemoteAgent } from "./remote-agent.js"; import { c } from "./theme.js"; @@ -138,13 +138,12 @@ async function main(): Promise { } break; } - case "module": - case "mod": { - const [sub, ...subRest] = rest; - console.log(await dispatchModuleCommand(sub, subRest)); - break; - } default: + if (isModuleCommand(cmd)) { + const [sub, ...subRest] = rest; + console.log(await dispatchModuleCommand(sub, subRest)); + break; + } console.log(c.red(`Unknown command: ${cmd}`)); printUsage(); process.exit(1); diff --git a/sfmc/src/module-commands.ts b/sfmc/src/module-commands.ts index 75c1118d..1fc0c5ae 100644 --- a/sfmc/src/module-commands.ts +++ b/sfmc/src/module-commands.ts @@ -28,6 +28,21 @@ /** 顶层命令名(主名 + 短别名),供 HELP / 补全 / 分发共用。 */ export const MODULE_CMD_NAMES = ["module", "mod"] as const; +export type ModuleCmdName = (typeof MODULE_CMD_NAMES)[number]; + +/** HELP 中主名+别名展示串(权威来源 MODULE_CMD_NAMES),如 "module/mod"。 */ +export const MODULE_CMD_ALIAS_LABEL = MODULE_CMD_NAMES.join("/"); + +/** 判断是否为 module 顶层命令(含别名);避免 main/repl 再硬编码 case。 */ +export function isModuleCommand(cmd: string | undefined): cmd is ModuleCmdName { + return !!cmd && (MODULE_CMD_NAMES as readonly string[]).includes(cmd); +} + +/** 染色后的 HELP 前缀,避免 HELP 硬编码 module/mod。 */ +export function paintModuleCmdAlias(paint: (name: string) => string): string { + return MODULE_CMD_NAMES.map(paint).join("/"); +} + /** 对外展示与 Tab 补全用的子命令列表(不含 remove 等同义别名)。 */ export const MODULE_SUBCOMMANDS = [ "list", @@ -41,7 +56,7 @@ export const MODULE_SUBCOMMANDS = [ ] as const; export const MODULE_USAGE = - "Usage: sfmc module|mod [args]"; + `Usage: sfmc ${MODULE_CMD_NAMES.join("|")} <${MODULE_SUBCOMMANDS.join("|")}> [args]`; import fs from "node:fs/promises"; import { existsSync, readdirSync } from "node:fs"; diff --git a/sfmc/src/repl.ts b/sfmc/src/repl.ts index 41dab0b2..e0044ba4 100644 --- a/sfmc/src/repl.ts +++ b/sfmc/src/repl.ts @@ -2,7 +2,14 @@ import process, { stdin, stdout } from "node:process"; import pkg from "../package.json" with { type: "json" }; import { cmdLogs, cmdRestart, cmdSend, cmdStart, cmdStartAll, cmdStatus, cmdStop, cmdStopAll, cmdUpdate } from "./commands.js"; import { formatLog, getAllLogs, onLog, wrapLogLine, type LogLevel, type LogSource, type UnifiedLog } from "./logs.js"; -import { dispatchModuleCommand, listInstalledModuleIdsSync, MODULE_CMD_NAMES, MODULE_SUBCOMMANDS } from "./module-commands.js"; +import { + dispatchModuleCommand, + isModuleCommand, + listInstalledModuleIdsSync, + MODULE_CMD_NAMES, + MODULE_SUBCOMMANDS, + paintModuleCmdAlias, +} from "./module-commands.js"; import { listRegistryModuleIdsSync } from "./registry.js"; import { disableRemoteAgent, enrollRemoteAgent, remoteStatus, startRemoteAgent } from "./remote-agent.js"; import { forceStopAll, SERVICE_NAMES, stopAll } from "./services.js"; @@ -43,19 +50,19 @@ ${c.bold("Commands")} ${c.green("remote enroll")} [name] Enroll this supervisor with a controller ${c.green("remote disable")} Disable + disconnect remote agent - ${c.green("module")}/${c.green("mod")} list + ${paintModuleCmdAlias(c.green)} list List installed modules - ${c.green("module")}/${c.green("mod")} search [id] + ${paintModuleCmdAlias(c.green)} search [id] Fetch registry list / show one module's registry info - ${c.green("module")}/${c.green("mod")} install [--from ] + ${paintModuleCmdAlias(c.green)} install [--from ] Fetch + install a module - ${c.green("module")}/${c.green("mod")} uninstall + ${paintModuleCmdAlias(c.green)} uninstall Remove an installed module - ${c.green("module")}/${c.green("mod")} verify [id] + ${paintModuleCmdAlias(c.green)} verify [id] Verify installed modules (SHA-256) - ${c.green("module")}/${c.green("mod")} info + ${paintModuleCmdAlias(c.green)} info Show one installed module's details - ${c.green("module")}/${c.green("mod")} enable|disable + ${paintModuleCmdAlias(c.green)} enable|disable Toggle module (needs db-server) ${c.green("version")} Show version ${c.green("help")} Show this @@ -148,8 +155,9 @@ function getCompletions(parsed: ParsedLine): string[] { case "remote": if (argIndex === 0) return ["status", "enroll", "disable"].filter(sw); return []; - case "module": - case "mod": { + default: { + /* 与 MODULE_CMD_NAMES 对齐,新增别名无需再改 case(OCP/DRY) */ + if (!isModuleCommand(cmd)) return []; if (argIndex === 0) return [...MODULE_SUBCOMMANDS].filter(sw); const verb = words[0] ?? ""; /* search:补全 registry 缓存中的 id;其余本地已装 id */ @@ -167,8 +175,6 @@ function getCompletions(parsed: ParsedLine): string[] { } return []; } - default: - return []; } } @@ -659,17 +665,16 @@ async function execCmd(parts: string[]): Promise { } break; } - case "module": - case "mod": { - const [sub, ...subRest] = args; - stdout.write((await dispatchModuleCommand(sub, subRest)) + "\n"); - break; - } case "quit": case "exit": case "q": throw "QUIT"; default: + if (isModuleCommand(cmd)) { + const [sub, ...subRest] = args; + stdout.write((await dispatchModuleCommand(sub, subRest)) + "\n"); + break; + } stdout.write(c.yellow(`Unknown: ${cmd} (try: help)\n`)); } } diff --git a/tools/check-ootb.mjs b/tools/check-ootb.mjs index a04ad526..98c7eacd 100755 --- a/tools/check-ootb.mjs +++ b/tools/check-ootb.mjs @@ -157,9 +157,12 @@ async function main() { console.log("[ootb] WARN: sfmc/dist/main.js 缺失 — 跳过 CLI 检查"); } else { const r = runSync(process.execPath, [SFMC_DIST, "--help"], { cwd: ROOT }); - const hasModule = /module\s+(list|install)/.test(r.stdout + r.stderr); + /* 剥离 ANSI;接受 module list / module/mod list / module|mod list + * (与 MODULE_CMD_NAMES / HELP 展示对齐,避免别名改动再次打红) */ + const helpText = (r.stdout + r.stderr).replace(/\u001b\[[0-9;]*m/g, ""); + const hasModule = /module(?:\s*[|/]\s*mod)?\s+(list|install)\b/.test(helpText); if (r.status === 0 && hasModule) pass("sfmc CLI module 子命令已注册"); - else fail("sfmc CLI module 子命令已注册", (r.stderr || r.stdout || `exit ${r.status}`).trim()); + else fail("sfmc CLI module 子命令已注册", helpText.trim().slice(0, 800)); } } From 56b38b84fa6ba1fb5576eceeddd6d08aa43a4927 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 22 Jul 2026 17:00:54 +0000 Subject: [PATCH 2/2] =?UTF-8?q?fix(cli):=20paintModuleCmdAlias=20=E9=81=BF?= =?UTF-8?q?=E5=85=8D=20chalk.map=20=E5=90=83=20index/array?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit .map(paint) 会把 (value,index,array) 传给 chalk,HELP 变成 「module 0 module,mod/...」乱码;改为显式 paint(name)。 Co-authored-by: Shiroha --- sfmc/src/module-commands.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sfmc/src/module-commands.ts b/sfmc/src/module-commands.ts index 1fc0c5ae..778f4612 100644 --- a/sfmc/src/module-commands.ts +++ b/sfmc/src/module-commands.ts @@ -40,7 +40,8 @@ export function isModuleCommand(cmd: string | undefined): cmd is ModuleCmdName { /** 染色后的 HELP 前缀,避免 HELP 硬编码 module/mod。 */ export function paintModuleCmdAlias(paint: (name: string) => string): string { - return MODULE_CMD_NAMES.map(paint).join("/"); + /* 不可写成 .map(paint):chalk 会吃到 (value,index,array) 并拼出乱文案 */ + return MODULE_CMD_NAMES.map((name) => paint(name)).join("/"); } /** 对外展示与 Tab 补全用的子命令列表(不含 remove 等同义别名)。 */