From 8ba051755619596563a43c4068d286a56b1d4a62 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 22 Jul 2026 17:09:26 +0000 Subject: [PATCH 1/2] =?UTF-8?q?fix(ci):=20=E6=B8=85=E9=99=A4=20#33=20?= =?UTF-8?q?=E5=90=88=E5=B9=B6=E6=AE=8B=E7=95=99=E6=9C=AA=E7=94=A8=20MODULE?= =?UTF-8?q?=5FHELP=5FLABEL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #32+#33 并行合入后 repl.ts 仍保留 MODULE_HELP_LABEL,但 HELP 已改用 paintModuleCmdAlias,tsc noUnusedLocals → TS6133 打断 ootb。 同步:删死导出 MODULE_CMD_ALIAS_LABEL;configs/all banned_items 回 string[] 并对齐 helper;resolveModuleByKey 规范化 canDisable(LSP)。 Co-authored-by: Shiroha --- db-server/src/index.ts | 16 ++++- db-server/src/routes/config.ts | 58 +++++-------------- .../module-loader/internal/config-manager.ts | 14 ++++- sfmc/src/module-commands.ts | 3 - sfmc/src/repl.ts | 3 - 5 files changed, 40 insertions(+), 54 deletions(-) diff --git a/db-server/src/index.ts b/db-server/src/index.ts index d050cfb1..2dbb877f 100644 --- a/db-server/src/index.ts +++ b/db-server/src/index.ts @@ -183,14 +183,24 @@ function buildModuleList() { .filter(Boolean); } -function resolveModuleByKey(key: string) { +function resolveModuleByKey(key: string): { id: string; configKey: string; canDisable: boolean } | null { const k = String(key || "").trim(); const catalog = loadModuleCatalog(); - return catalog.find( + const raw = catalog.find( (m) => String((m as Record).id || "") === k || String((m as Record).configKey || (m as Record).config_key || "") === k - ) as { id: string; configKey: string; canDisable: boolean } | null; + ) as Record | undefined; + if (!raw) return null; + const id = String(raw.id || "").trim(); + const configKey = String(raw.configKey || raw.config_key || "").trim(); + if (!id || !configKey) return null; + /* 与 buildModuleList.can_disable 同源:缺省视为可禁用,避免 list/disable 契约漂移(LSP) */ + return { + id, + configKey, + canDisable: raw.canDisable !== false, + }; } function setModuleEnabled(mod: { id: string; canDisable: boolean }, enabled: boolean) { diff --git a/db-server/src/routes/config.ts b/db-server/src/routes/config.ts index 792b74e5..99b4f144 100644 --- a/db-server/src/routes/config.ts +++ b/db-server/src/routes/config.ts @@ -71,50 +71,21 @@ function createConfigRoutes({ json, projectRoot, listModules, getModuleTokens }: function getAllConfigs(): Record { const modules = typeof listModules === "function" ? listModules() : []; const module_tokens = typeof getModuleTokens === "function" ? getModuleTokens() : {}; + /* 各资源走与单资源 GET 相同的 helper,避免 configs/all 与专用路由形状漂移(DRY/LSP) */ return { // 与 /api/sfmc/modules 同源;ConfigManager.init 一次拉齐启停态(DRY) modules, // loopback-only 下发;SAPI 无 fs,靠此注入模块身份(DIP) module_tokens, settings: stripMeta(readJson(configPath(projectRoot, "settings.json")) as Record | null), - areas: (arrayOrEmpty(readJson(configPath(projectRoot, "areas.json"))) as Array>) - .filter((r) => r && r.module && r.dimension != null) - .map((r) => stripMetaDeep(r)), - permissions: ( - arrayOrEmpty(readJson(configPath(projectRoot, "permissions.json"))) as Array> - ) - .filter((r) => r && r.player_name) - .map((r) => stripMetaDeep(r)), - banned_items: (arrayOrEmpty(readJson(configPath(projectRoot, "banned_items.json"))) as Array) - .filter((i) => typeof i === "string" && i && !i.startsWith("_")) - .map((id) => ({ item_id: id })), - clean: stripMetaDeep(readJson(configPath(projectRoot, "clean.json")) ?? {}), - grids: (arrayOrEmpty(readJson(configPath(projectRoot, "grids.json"))) as Array>) - .filter((r) => r && r.name) - .map((r) => stripMetaDeep(r)), - peace_filters: ( - arrayOrEmpty(readJson(configPath(projectRoot, "peace_filters.json"))) as Array> - ) - .filter((r) => r && r.family) - .map((r) => stripMetaDeep(r)), - questions: (arrayOrEmpty(readJson(configPath(projectRoot, "questions.json"))) as Array>) - .filter((r) => r && r.question) - .map((r, idx: number) => { - const clean = stripMetaDeep(r) as Record; - return { - id: idx + 1, - weight: clean.weight ?? 1, - question: clean.question, - answers: clean.answers ?? [], - msg_right: clean.msg_right ?? "", - msg_wrong: clean.msg_wrong ?? "", - explanation: clean.explanation ?? "", - min_rank: clean.min_rank ?? null, - max_rank: clean.max_rank ?? null, - rewards: clean.rewards ?? [], - punishments: clean.punishments ?? [], - }; - }), + areas: getAreas(), + permissions: getPermissions(), + // ConfigManager.AllConfigs.banned_items: string[];勿再包成 {item_id} + banned_items: getBannedItems(), + clean: getClean(), + grids: getGrids(), + peace_filters: getPeaceFilters(), + questions: getQA(), }; } @@ -153,10 +124,11 @@ function createConfigRoutes({ json, projectRoot, listModules, getModuleTokens }: .map((r) => stripMetaDeep(r)); } - function getBannedItems(): Array<{ item_id: string }> { - return (arrayOrEmpty(readCfg("banned_items.json")) as Array) - .filter((i) => typeof i === "string" && i && !i.startsWith("_")) - .map((id) => ({ item_id: id })); + /** 返回 string[] —— 与 ConfigManager / GET /banned_items.items 契约一致。 */ + function getBannedItems(): string[] { + return (arrayOrEmpty(readCfg("banned_items.json")) as Array).filter( + (i) => typeof i === "string" && i && !i.startsWith("_") + ); } function getClean(): { item_max: number; poll_interval: number } { @@ -248,7 +220,7 @@ function createConfigRoutes({ json, projectRoot, listModules, getModuleTokens }: } if (requestPath === "/api/sfmc/banned_items") { if (method === "GET") { - json(res, { items: getBannedItems().map((x) => x.item_id) }); + json(res, { items: getBannedItems() }); return true; } } diff --git a/modules/sdk/@sfmc-sdk/src/module-loader/internal/config-manager.ts b/modules/sdk/@sfmc-sdk/src/module-loader/internal/config-manager.ts index 9aa1b79a..70112a9b 100644 --- a/modules/sdk/@sfmc-sdk/src/module-loader/internal/config-manager.ts +++ b/modules/sdk/@sfmc-sdk/src/module-loader/internal/config-manager.ts @@ -74,7 +74,8 @@ type AllConfigs = { end_z: number; }>; permissions: Array<{ player_name: string; level: number }>; - banned_items: string[]; + /** 权威为 string[];兼容过渡期 {item_id} 信封 */ + banned_items: Array; clean: { item_max?: number; poll_interval?: number }; grids: Array<{ name: string; @@ -275,7 +276,16 @@ export class ConfigManager { ConfigManager.cache.permissions[p.player_name] = p.level; } - ConfigManager.cache.bannedItems = (all.banned_items || []).filter((s) => typeof s === "string"); + /* 兼容旧信封 {item_id};权威形状为 string[](与 configs/all / GET banned_items 对齐) */ + ConfigManager.cache.bannedItems = (all.banned_items || []) + .map((s) => { + if (typeof s === "string") return s; + if (s && typeof s === "object" && typeof (s as { item_id?: unknown }).item_id === "string") { + return (s as { item_id: string }).item_id; + } + return null; + }) + .filter((s): s is string => typeof s === "string" && s.length > 0); if (all.clean) { ConfigManager.cache.clean = { diff --git a/sfmc/src/module-commands.ts b/sfmc/src/module-commands.ts index 778f4612..48672e3a 100644 --- a/sfmc/src/module-commands.ts +++ b/sfmc/src/module-commands.ts @@ -30,9 +30,6 @@ 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); diff --git a/sfmc/src/repl.ts b/sfmc/src/repl.ts index dffc1c7d..e0044ba4 100644 --- a/sfmc/src/repl.ts +++ b/sfmc/src/repl.ts @@ -21,9 +21,6 @@ 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(`⠄`)} From f87c7f470a584cbeffdd043979a802a66658eb36 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 22 Jul 2026 17:12:30 +0000 Subject: [PATCH 2/2] =?UTF-8?q?fix(sdk/ootb):=20service.get=20=E4=BF=9D?= =?UTF-8?q?=E7=95=99=20code=20+=20banned=5Fitems=20=E5=BD=A2=E7=8A=B6?= =?UTF-8?q?=E6=96=AD=E8=A8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 与并行 #35 对齐:service 客户端勿一律打成 internal(LSP); ootb 回归守卫 configs/all.banned_items 必须为 string[]。 Co-authored-by: Shiroha --- modules/sdk/@sfmc-sdk/src/sapi/service/client.ts | 4 +++- tools/check-ootb.mjs | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/modules/sdk/@sfmc-sdk/src/sapi/service/client.ts b/modules/sdk/@sfmc-sdk/src/sapi/service/client.ts index f852d2d4..acaff6d5 100644 --- a/modules/sdk/@sfmc-sdk/src/sapi/service/client.ts +++ b/modules/sdk/@sfmc-sdk/src/sapi/service/client.ts @@ -81,7 +81,9 @@ export const service = { authOpts() ); if (!res.ok) { - throw new ServiceError(res.error ?? "service_error", "internal", res.status); + // LSP:与 db 客户端一致,保留服务端 code,勿一律打成 internal + const data = res.data as { error?: string; code?: string } | undefined; + throw new ServiceError(data?.error ?? res.error ?? "service_error", data?.code || "internal", res.status); } return (res.data as { ok: true; result: T }).result; }, diff --git a/tools/check-ootb.mjs b/tools/check-ootb.mjs index 05589522..7cbba2dc 100755 --- a/tools/check-ootb.mjs +++ b/tools/check-ootb.mjs @@ -136,6 +136,13 @@ async function main() { if (!all.body.module_tokens || typeof all.body.module_tokens !== "object") { throw new Error("configs/all 缺少 module_tokens"); } + // LSP:configs/all.banned_items 须为 string[](与 /banned_items、ConfigManager 同源) + if (Array.isArray(all.body.banned_items)) { + const bad = all.body.banned_items.find((x) => typeof x !== "string"); + if (bad !== undefined) { + throw new Error(`configs/all.banned_items 须为 string[],收到 ${typeof bad}`); + } + } pass(`db-server 启动 + 平台 API (modules=${mods.body.modules.length})`); } catch (e) { fail("db-server 启动 + 平台 API", e.message);