From 67ad77193f149e3b61c3b2e2841f5d1e7c58d778 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 22 Jul 2026 17:00:12 +0000 Subject: [PATCH] =?UTF-8?q?fix(wf@67069bf):=20ootb=20module/mod=20HELP=20?= =?UTF-8?q?=E6=96=AD=E8=A8=80=20+=20printVersion=20+=20=E8=AF=AF=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=20tgz?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ootb 因 HELP 改为 `module/mod list` 后仍用 `/module\s+(list|install)/` 失败。 同步收紧 DRY/OCP:MODULE_CMD_NAMES 驱动 HELP/分发;修复 --version 空输出; 删除仓根误提交的 npm pack 产物并 ignore *.tgz。 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 | 19 ++++++++-------- sfmc/src/module-commands.ts | 10 ++++++++- sfmc/src/repl.ts | 43 +++++++++++++++++++++--------------- sfmc/src/runtime.ts | 26 ++++++++++------------ tools/check-ootb.mjs | 5 ++++- 8 files changed, 62 insertions(+), 44 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..1fd7fab9 100644 --- a/sfmc/src/main.ts +++ b/sfmc/src/main.ts @@ -3,16 +3,17 @@ 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"; function printVersion(): void { - `${c.text(`⠪⡁⡯⠁`)} + /* 此前模板字符串未写入 stdout,导致 `sfmc --version` 无输出。 */ + console.log(`${c.text(`⠪⡁⡯⠁`)} ${c.text(`⠒⠁⠃`)}${c.purple(`⠄`)} ${c.text(`⡷⡇⡎⠁`)} ${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} - ${c.text(`⠃⠃⠑⠂`)} ${c.dim(`https://github.com/DogeLakeDev/ScriptsForMinecraftServer`)}\n`; + ${c.text(`⠃⠃⠑⠂`)} ${c.dim(`https://github.com/DogeLakeDev/ScriptsForMinecraftServer`)}`); } function printUsage(): void { @@ -138,13 +139,13 @@ async function main(): Promise { } break; } - case "module": - case "mod": { - const [sub, ...subRest] = rest; - console.log(await dispatchModuleCommand(sub, subRest)); - break; - } default: + /* module/mod 别名只维护 MODULE_CMD_NAMES,避免 main/repl case 链漂移(OCP)。 */ + 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..3f3c0954 100644 --- a/sfmc/src/module-commands.ts +++ b/sfmc/src/module-commands.ts @@ -28,6 +28,14 @@ /** 顶层命令名(主名 + 短别名),供 HELP / 补全 / 分发共用。 */ export const MODULE_CMD_NAMES = ["module", "mod"] as const; +/** HELP 行首标签,如 `module/mod`(与 MODULE_CMD_NAMES 同源,避免两处漂移)。 */ +export const MODULE_CMD_LABEL = MODULE_CMD_NAMES.join("/"); + +/** 是否为 module 顶层命令(含短别名)。新增别名只改 MODULE_CMD_NAMES。 */ +export function isModuleCommand(name: string | undefined): boolean { + return !!name && (MODULE_CMD_NAMES as readonly string[]).includes(name); +} + /** 对外展示与 Tab 补全用的子命令列表(不含 remove 等同义别名)。 */ export const MODULE_SUBCOMMANDS = [ "list", @@ -41,7 +49,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..34685c12 100644 --- a/sfmc/src/repl.ts +++ b/sfmc/src/repl.ts @@ -2,7 +2,13 @@ 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, +} 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"; @@ -14,6 +20,9 @@ function setRaw(v: boolean): void { } catch {} } +/** HELP 行首:把 MODULE_CMD_NAMES 着色后用 / 拼接(如 module/mod)。 */ +const MODULE_HELP_LABEL = MODULE_CMD_NAMES.map((n) => c.green(n)).join("/"); + const welcome = `\n ${c.text(`⠪⡁⡯⠁`)} ${c.text(`⠒⠁⠃`)}${c.purple(`⠄`)} @@ -43,19 +52,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 + ${MODULE_HELP_LABEL} list List installed modules - ${c.green("module")}/${c.green("mod")} search [id] + ${MODULE_HELP_LABEL} search [id] Fetch registry list / show one module's registry info - ${c.green("module")}/${c.green("mod")} install [--from ] + ${MODULE_HELP_LABEL} install [--from ] Fetch + install a module - ${c.green("module")}/${c.green("mod")} uninstall + ${MODULE_HELP_LABEL} uninstall Remove an installed module - ${c.green("module")}/${c.green("mod")} verify [id] + ${MODULE_HELP_LABEL} verify [id] Verify installed modules (SHA-256) - ${c.green("module")}/${c.green("mod")} info + ${MODULE_HELP_LABEL} info Show one installed module's details - ${c.green("module")}/${c.green("mod")} enable|disable + ${MODULE_HELP_LABEL} enable|disable Toggle module (needs db-server) ${c.green("version")} Show version ${c.green("help")} Show this @@ -148,8 +157,9 @@ function getCompletions(parsed: ParsedLine): string[] { case "remote": if (argIndex === 0) return ["status", "enroll", "disable"].filter(sw); return []; - case "module": - case "mod": { + default: { + /* module/mod 等别名统一走 MODULE_CMD_NAMES,避免 case 链与权威源漂移。 */ + if (!isModuleCommand(cmd)) return []; if (argIndex === 0) return [...MODULE_SUBCOMMANDS].filter(sw); const verb = words[0] ?? ""; /* search:补全 registry 缓存中的 id;其余本地已装 id */ @@ -167,8 +177,6 @@ function getCompletions(parsed: ParsedLine): string[] { } return []; } - default: - return []; } } @@ -659,17 +667,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/sfmc/src/runtime.ts b/sfmc/src/runtime.ts index aa774bfe..09c175ae 100644 --- a/sfmc/src/runtime.ts +++ b/sfmc/src/runtime.ts @@ -164,6 +164,16 @@ function nodeBinary(): string { return IS_SEA ? "node" : process.execPath; } +function serviceChildEnv(service: ServiceId, optsEnv?: NodeJS.ProcessEnv | null) { + return { + ...process.env, + ...optsEnv, + SFMC_SERVICE: service, + SFMC_ROOT: ROOT, + SFMC_PACKAGES_DIR: PACKAGES_DIR, + }; +} + /** * 启动一个子服务。 * @@ -171,26 +181,14 @@ function nodeBinary(): string { * - SFMC_SERVICE / SFMC_ROOT / SFMC_PACKAGES_DIR */ export function spawnService(service: ServiceId, args: string[] = [], opts: SpawnOptions = {}) { - const env = { - ...process.env, - ...opts.env, - SFMC_SERVICE: service, - SFMC_ROOT: ROOT, - SFMC_PACKAGES_DIR: PACKAGES_DIR, - }; + const env = serviceChildEnv(service, opts.env as NodeJS.ProcessEnv | undefined); const script = resolveServiceScript(service); return spawn(nodeBinary(), [script, ...args], { ...opts, env }); } /** spawnService 的同步版本 */ export function spawnServiceSync(service: ServiceId, args: string[] = [], opts: SpawnSyncOptions = {}) { - const env = { - ...process.env, - ...opts.env, - SFMC_SERVICE: service, - SFMC_ROOT: ROOT, - SFMC_PACKAGES_DIR: PACKAGES_DIR, - }; + const env = serviceChildEnv(service, opts.env as NodeJS.ProcessEnv | undefined); const script = resolveServiceScript(service); return spawnSync(nodeBinary(), [script, ...args], { ...opts, env }); } diff --git a/tools/check-ootb.mjs b/tools/check-ootb.mjs index a04ad526..6dce578c 100755 --- a/tools/check-ootb.mjs +++ b/tools/check-ootb.mjs @@ -157,7 +157,10 @@ 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); + /* HELP 现为 "module/mod list"(别名同行);兼容旧 "module list" / 裸 "mod list"。 */ + const helpText = String(r.stdout || "") + String(r.stderr || ""); + const hasModule = + /\bmodule(\/\S+)?\s+(list|install)\b/.test(helpText) || /\bmod\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()); }