From 11fcac48826766ab535a447bebc3baf2f658c0e4 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 22 Jul 2026 13:39:08 +0000 Subject: [PATCH 1/3] =?UTF-8?q?fix(ci):=20=E9=98=B2=E6=AD=A2=20npm-publish?= =?UTF-8?q?=20=E5=9C=A8=E9=9D=9E=20tag=20push=20=E4=B8=8A=E7=A9=BA=20jobs?= =?UTF-8?q?=20=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit workflow_dispatch.inputs.tag 改为非 required,publish job 增加 ref_type==tag 门控,避免改 workflow 的 branch push 被 GHA 误评估后 出现空 jobs + "workflow file issue"(如 run 29922880511)。 Co-authored-by: Shiroha --- .github/workflows/npm-publish.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index e4cd5b82..03d7b8c9 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -14,14 +14,18 @@ on: inputs: tag: description: "要发布的 tag(如 @sfmc-bds/sdk@v0.1.0)" - required: true + # 勿 required:true:改本 workflow 的 branch push 会被 GHA 误评估, + # 缺 inputs 时出现空 jobs + "workflow file issue" failure(见 run 29922880511)。 + required: false type: string jobs: publish: + # dispatch 放行;push 仅真实 tag 且 @sfmc-bds/ 前缀才跑。 + # ref_type==tag 防御误触的 branch push(skipped 绿,而非空 jobs 红)。 if: >- github.event_name == 'workflow_dispatch' || - startsWith(github.ref_name, '@sfmc-bds/') + (github.ref_type == 'tag' && startsWith(github.ref_name, '@sfmc-bds/')) runs-on: ubuntu-latest permissions: contents: read From 9c8690ce22f60cf05c4ce490bf7735043745e96e Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 22 Jul 2026 13:39:08 +0000 Subject: [PATCH 2/3] =?UTF-8?q?fix(solid):=20HttpDB=20DataAdapter(DIP)=20+?= =?UTF-8?q?=20db-routes=20v2=20=E5=A4=B1=E8=B4=A5=E4=BF=A1=E5=B0=81(LSP)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - installHostBootstrap 默认注入 createHttpDataAdapter,可经 dataAdapter / dbServerUrl 替换;HttpDB.configure 覆盖基址 - db-routes 错误路径统一 jsonV2Fail({ok:false}),支持 step extra Co-authored-by: Shiroha --- db-server/src/routes/_shared.ts | 7 +-- db-server/src/routes/db-routes.ts | 16 +++---- db-server/src/runtime.test.ts | 20 +++++++++ .../src/module-loader/http-data-adapter.ts | 22 +++++++++ .../sdk/@sfmc-sdk/src/module-loader/index.ts | 1 + .../@sfmc-sdk/src/module-loader/install.ts | 45 +++++++++---------- .../sdk/@sfmc-sdk/src/sapi/runtime/httpdb.ts | 24 +++++++--- 7 files changed, 93 insertions(+), 42 deletions(-) create mode 100644 modules/sdk/@sfmc-sdk/src/module-loader/http-data-adapter.ts diff --git a/db-server/src/routes/_shared.ts b/db-server/src/routes/_shared.ts index 5e9bb322..b94848e4 100644 --- a/db-server/src/routes/_shared.ts +++ b/db-server/src/routes/_shared.ts @@ -59,16 +59,17 @@ export const body = sharedBody; /** * v2 失败信封权威形态(DRY + LSP): - * 一律 `{ ok:false, error, code? }`,勿再混用 `{ success:false }`。 + * 一律 `{ ok:false, error, code? }`(+ 可选 extra),勿再混用 `{ success:false }`。 * service-routes / db / module-config / index 模块鉴权门应走此助手。 */ export function jsonV2Fail( res: ServerResponse, error: string, status: number, - code?: string + code?: string, + extra?: Record ): void { - const payload: Record = { ok: false, error }; + const payload: Record = { ...(extra ?? {}), ok: false, error }; if (code) payload.code = code; sharedJson(res, payload, status); } diff --git a/db-server/src/routes/db-routes.ts b/db-server/src/routes/db-routes.ts index 8eb1fe68..7184bf40 100644 --- a/db-server/src/routes/db-routes.ts +++ b/db-server/src/routes/db-routes.ts @@ -73,7 +73,7 @@ export function createDbRoutes(depsIn: Partial) { const result = deps.schemaRegistry!.define(moduleId, req2); json(res, { success: true, table: result.table, created: result.created }, 200); } catch (e) { - json(res, { success: false, error: (e as Error).message }, 400); + jsonV2Fail(res, (e as Error).message, 400); } return true; } @@ -89,7 +89,7 @@ export function createDbRoutes(depsIn: Partial) { const status = result.ok ? 200 : 400; json(res, result as unknown as Record, status); } catch (e) { - json(res, { success: false, error: (e as Error).message }, 500); + jsonV2Fail(res, (e as Error).message, 500); } return true; } @@ -109,17 +109,17 @@ export function createDbRoutes(depsIn: Partial) { const result = await deps.txRunner!.run({ moduleId, steps: [step] }); if (!result.ok) { const code = result.code === "permission_denied" ? 403 : 400; - json(res, { success: false, error: result.error, step: result.step, code: result.code }, code); + jsonV2Fail(res, result.error, code, result.code, { step: result.step }); return true; } const first = result.results[0]; json(res, { success: true, ...unwrapResult(first, cand.op) }, 200); } catch (e) { if (e instanceof PermissionDeniedError) { - json(res, { success: false, error: e.message, code: "permission_denied" }, 403); + jsonV2Fail(res, e.message, 403, "permission_denied"); return true; } - json(res, { success: false, error: (e as Error).message }, 500); + jsonV2Fail(res, (e as Error).message, 500); } return true; } @@ -136,12 +136,12 @@ export function createDbRoutes(depsIn: Partial) { }; const result = await deps.txRunner!.run({ moduleId, steps: [step] }); if (!result.ok) { - json(res, { success: false, error: result.error }, 400); + jsonV2Fail(res, result.error, 400, result.code); return true; } json(res, { success: true }); } catch (e) { - json(res, { success: false, error: (e as Error).message }, 500); + jsonV2Fail(res, (e as Error).message, 500); } return true; } @@ -166,7 +166,7 @@ export function createDbRoutes(depsIn: Partial) { json(res, { success: r.ok }); } } catch (e) { - json(res, { success: false, error: (e as Error).message }, 500); + jsonV2Fail(res, (e as Error).message, 500); } return true; } diff --git a/db-server/src/runtime.test.ts b/db-server/src/runtime.test.ts index 41000dfb..3d3c3fac 100644 --- a/db-server/src/runtime.test.ts +++ b/db-server/src/runtime.test.ts @@ -76,3 +76,23 @@ test("ServiceRegistry: missing service → no_such_service (not not_in_requires) (e: unknown) => e instanceof DispatchError && e.code === "no_such_service" ); }); + +test("jsonV2Fail: ok 方言 + extra(step) 合并(LSP/DRY)", async () => { + const { jsonV2Fail } = await import("./routes/_shared.js"); + const chunks: Buffer[] = []; + let statusCode = 0; + const res = { + writeHead(code: number) { + statusCode = code; + }, + end(body?: string) { + if (body) chunks.push(Buffer.from(body)); + }, + setHeader() {}, + } as unknown as import("node:http").ServerResponse; + + jsonV2Fail(res, "boom", 400, "bad_step", { step: 2 }); + const payload = JSON.parse(Buffer.concat(chunks).toString("utf8")); + equal(statusCode, 400); + deepEqual(payload, { ok: false, error: "boom", code: "bad_step", step: 2 }); +}); diff --git a/modules/sdk/@sfmc-sdk/src/module-loader/http-data-adapter.ts b/modules/sdk/@sfmc-sdk/src/module-loader/http-data-adapter.ts new file mode 100644 index 00000000..89891e4d --- /dev/null +++ b/modules/sdk/@sfmc-sdk/src/module-loader/http-data-adapter.ts @@ -0,0 +1,22 @@ +/** + * HttpDB → ConfigManager.DataAdapter 适配器(DIP)。 + * ConfigManager 只依赖 DataAdapter 抽象;本文件是 module-loader 侧唯一 HttpDB 装配点。 + */ + +import { HttpDB } from "../sapi/runtime/httpdb.js"; +import type { DataAdapter } from "./internal/config-manager.js"; + +/** 用 HttpDB 实现 DataAdapter;可选覆盖 db-server 基址。 */ +export function createHttpDataAdapter(opts?: { baseUrl?: string }): DataAdapter { + if (opts?.baseUrl) HttpDB.configure({ baseUrl: opts.baseUrl }); + return { + checkHealth: async () => { + await HttpDB.checkHealth(); + }, + getAllConfigs: async () => HttpDB.get("/api/sfmc/configs/all"), + getModules: async () => HttpDB.get("/api/sfmc/modules"), + setAuthToken: (token: string) => { + HttpDB.setAuthToken(token); + }, + }; +} diff --git a/modules/sdk/@sfmc-sdk/src/module-loader/index.ts b/modules/sdk/@sfmc-sdk/src/module-loader/index.ts index b9b186ca..76e66ff4 100644 --- a/modules/sdk/@sfmc-sdk/src/module-loader/index.ts +++ b/modules/sdk/@sfmc-sdk/src/module-loader/index.ts @@ -14,4 +14,5 @@ export type { ModuleLifecycle, ModuleDescriptor } from "./runtime.js"; export { guardEvent, announceLoaded } from "./runtime.js"; export { installHostBootstrap } from "./install.js"; export type { InstallOptions, HostBackend, ModuleSurface } from "./install.js"; +export { createHttpDataAdapter } from "./http-data-adapter.js"; export const SFMC_MODULE_LOADER_VERSION = "0.1.0" as const; diff --git a/modules/sdk/@sfmc-sdk/src/module-loader/install.ts b/modules/sdk/@sfmc-sdk/src/module-loader/install.ts index c87af554..c6e4a731 100644 --- a/modules/sdk/@sfmc-sdk/src/module-loader/install.ts +++ b/modules/sdk/@sfmc-sdk/src/module-loader/install.ts @@ -9,20 +9,19 @@ * 1) system.beforeEvents.startup.subscribe:ConfigManager.init() + bootAll + snapshot * 2) world.afterEvents.worldLoad.subscribe:bootAfterWorldLoad * 3) system.beforeEvents.shutdown.subscribe:teardown - * 4) bindDataAdapter():注入 db-server HTTP 适配器 + * 4) bindDataAdapter():注入 db-server HTTP 适配器(DIP:经 DataAdapter) * 5) 注册 setModuleGuard 给 Command.trigger 使用 - * - * 本批 (Stage A+B):占位实现,host adapters (Command/Permission/HttpDB 等) 在 Stage F - * (core-* 模块迁移)完成后实装。 */ import { system, world } from "@minecraft/server"; +import { createHttpDataAdapter } from "./http-data-adapter.js"; +import type { DataAdapter } from "./internal/config-manager.js"; import { ConfigManager } from "./internal/config-manager.js"; import { ModuleRegistry } from "./runtime.js"; export interface HostBackend { /** 注入 db-server 数据适配器 */ - bindDataAdapter(adapter: unknown): void; + bindDataAdapter(adapter: DataAdapter): void; /** 关闭 db-server HTTP 客户端 */ dispose(): void; } @@ -38,6 +37,11 @@ export interface InstallOptions { dbServerUrl?: string; /** 可选注入自定义 HostBackend(测试用) */ hostBackend?: HostBackend; + /** + * 测试/离线可注入自定义 DataAdapter;默认走 HttpDB 实现。 + * 高层只依赖 DataAdapter 抽象(DIP),不直接依赖 HttpDB。 + */ + dataAdapter?: DataAdapter; /** 模块 id 列表(默认 undefined = 全部装载,从 catalog 读取) */ enabledModuleIds?: readonly string[]; } @@ -45,16 +49,16 @@ export interface InstallOptions { let _installed = false; export function installHostBootstrap(options: InstallOptions = {}): HostBackend { - if (_installed) return _bootstrapStubBackend(); + if (_installed) return _bootstrapBackend(); _installed = true; - // 1) 占位 data adapter:Stage F 之前 ConsoleData 把 readAll 抛 NOT_IMPLEMENTED; - // installHostBootstrap 仍然注册 hooks,只有 ConfigManager.init() 会失败 — - // 这是允许的中间形态。modules/src/index.ts 还可以装运行。 - ConfigManager.bindDataAdapter(stubDataAdapter()); - if (options.hostBackend) options.hostBackend.bindDataAdapter(undefined); + // DIP:ConfigManager ← DataAdapter;默认 HttpDB,可被 options 替换(测试/自定义 host) + const adapter = + options.dataAdapter ?? createHttpDataAdapter(options.dbServerUrl ? { baseUrl: options.dbServerUrl } : undefined); + ConfigManager.bindDataAdapter(adapter); + if (options.hostBackend) options.hostBackend.bindDataAdapter(adapter); - // 2) 装配 system.events + // 装配 system.events system.beforeEvents.startup.subscribe(async () => { try { await ConfigManager.init(); @@ -77,25 +81,18 @@ export function installHostBootstrap(options: InstallOptions = {}): HostBackend } catch {} }); - return _bootstrapStubBackend(); + return _bootstrapBackend(); } -function _bootstrapStubBackend(): HostBackend { +function _bootstrapBackend(): HostBackend { return { - bindDataAdapter: () => undefined, + bindDataAdapter: (adapter: DataAdapter) => { + ConfigManager.bindDataAdapter(adapter); + }, dispose: () => undefined, }; } -function stubDataAdapter() { - return { - checkHealth: async () => undefined, - getAllConfigs: async () => null, - getModules: async () => null, - setAuthToken: () => undefined, - }; -} - function announceLoaded() { // runtime.ts 内的同名 export;此处仅为了不让 TS 报 unused 警告 const _ref = announceLoadedExported; diff --git a/modules/sdk/@sfmc-sdk/src/sapi/runtime/httpdb.ts b/modules/sdk/@sfmc-sdk/src/sapi/runtime/httpdb.ts index 30bbf4db..330d3a12 100644 --- a/modules/sdk/@sfmc-sdk/src/sapi/runtime/httpdb.ts +++ b/modules/sdk/@sfmc-sdk/src/sapi/runtime/httpdb.ts @@ -11,9 +11,8 @@ import { system } from "@minecraft/server"; import { http, HttpRequest, HttpRequestMethod } from "@minecraft/server-net"; import { isSuccessfulHttpEnvelope } from "./http-envelope.js"; -const HOST = "127.0.0.1"; -const PORT = 3001; -const BASE_URL = `http://${HOST}:${PORT}`; +/** 默认连本机 db-server;可通过 configure / InstallOptions.dbServerUrl 覆盖(DIP)。 */ +let baseUrl = "http://127.0.0.1:3001"; const TIMEOUT = 3; export class HttpDB { @@ -21,6 +20,17 @@ export class HttpDB { private static _lastErrorLog = 0; private static authToken = ""; + /** 注入 db-server 基址(如 http://127.0.0.1:4000),去掉末尾 /。 */ + static configure(opts: { baseUrl?: string }): void { + if (opts.baseUrl) { + baseUrl = opts.baseUrl.replace(/\/+$/, ""); + } + } + + static getBaseUrl(): string { + return baseUrl; + } + static setAuthToken(token: string): void { this.authToken = token.trim(); } @@ -51,10 +61,10 @@ export class HttpDB { static async checkHealth(): Promise { for (let i = 0; i < 5; i++) { try { - const res = await http.get(`${BASE_URL}/api/health`); + const res = await http.get(`${baseUrl}/api/health`); this.available = res.status === 200; if (this.available) { - console.info(`[HttpDB] 数据库服务连接成功 (${BASE_URL}/api/health)`); + console.info(`[HttpDB] 数据库服务连接成功 (${baseUrl}/api/health)`); return true; } console.error(`[HttpDB] 数据库服务返回异常状态 ${res.status}`); @@ -64,7 +74,7 @@ export class HttpDB { console.info(`[HttpDB] 连接失败,2s 后重试 (${i + 1}/5)...`); await system.waitTicks(40); } else { - console.error(`[HttpDB] 连接失败 (${BASE_URL}): ${err}`); + console.error(`[HttpDB] 连接失败 (${baseUrl}): ${err}`); } } } @@ -89,7 +99,7 @@ export class HttpDB { bodyData?: Record ): Promise<{ status: number; body: string }> { try { - const req = new HttpRequest(`${BASE_URL}${path}`); + const req = new HttpRequest(`${baseUrl}${path}`); req.timeout = TIMEOUT; req.method = method; From c985ff71fa8bfa7637557b48b6ff5db460b9bbc2 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 22 Jul 2026 14:02:49 +0000 Subject: [PATCH 3/3] =?UTF-8?q?fix(tools):=20=E7=BA=B3=E5=85=A5=20npm-publ?= =?UTF-8?q?ish=20=E6=B8=85=E5=8D=95=E5=B9=B6=E4=BF=AE=E5=A5=BD=20@sfmc-bds?= =?UTF-8?q?/tools=20=E5=8F=91=E5=8C=85=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit push@d52f8b5 打了 @sfmc-bds/tools@v0.1.0,但 NPM_PUBLISH_PACKAGES 未收录、 tools 未进 workspaces,导致 run 29926464473 在 parse tag 失败。 - 清单 + root workspaces + pack:verify 对齐(DRY) - main 改为 check-ootb.mjs;补 bin / files;noop build - npm-publish build 加 --if-present(OCP) - paths.ROOT 尊重 SFMC_ROOT(DIP) Co-authored-by: Shiroha --- .github/workflows/npm-publish.yml | 3 ++- package-lock.json | 20 +++++++++++++++++++- package.json | 5 +++-- tools/README.md | 14 ++++++++++++++ tools/catalog-sync.mjs | 0 tools/check-modules.mjs | 0 tools/check-ootb.mjs | 0 tools/fetch-module.mjs | 0 tools/lib/npm-publish-packages.mjs | 1 + tools/lib/paths.mjs | 10 ++++++++-- tools/package.json | 16 ++++++++++++++-- tools/sim-new-user.mjs | 0 tools/smoke-modules.mjs | 0 13 files changed, 61 insertions(+), 8 deletions(-) mode change 100644 => 100755 tools/catalog-sync.mjs mode change 100644 => 100755 tools/check-modules.mjs mode change 100644 => 100755 tools/check-ootb.mjs mode change 100644 => 100755 tools/fetch-module.mjs mode change 100644 => 100755 tools/sim-new-user.mjs mode change 100644 => 100755 tools/smoke-modules.mjs diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 03d7b8c9..6e43fab0 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -58,9 +58,10 @@ jobs: if: steps.meta.outputs.already_published != 'true' && steps.meta.outputs.workspace != '@sfmc-bds/sdk' run: npm run build --workspace @sfmc-bds/sdk + # --if-present:纯脚本包(如 @sfmc-bds/tools)无编译步骤,勿强制 build(OCP) - name: build target workspace if: steps.meta.outputs.already_published != 'true' - run: npm run build --workspace "${{ steps.meta.outputs.workspace }}" + run: npm run build --workspace "${{ steps.meta.outputs.workspace }}" --if-present - name: publish to npm if: steps.meta.outputs.already_published != 'true' diff --git a/package-lock.json b/package-lock.json index 5710a430..a5cdac5c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,8 @@ "qq-bridge", "bds-tools", "sfmc", - "remote-controller" + "remote-controller", + "tools" ], "bin": { "sfmc": "sfmc/dist/main.js" @@ -864,6 +865,10 @@ "resolved": "modules/sdk/@sfmc-sdk", "link": true }, + "node_modules/@sfmc-bds/tools": { + "resolved": "tools", + "link": true + }, "node_modules/@tsconfig/node10": { "version": "1.0.12", "resolved": "https://registry.npmmirror.com/@tsconfig/node10/-/node10-1.0.12.tgz", @@ -3157,6 +3162,19 @@ "engines": { "node": ">=22.13.0" } + }, + "tools": { + "name": "@sfmc-bds/tools", + "version": "0.1.0", + "license": "AGPL-3.0-only", + "bin": { + "sfmc-catalog-sync": "catalog-sync.mjs", + "sfmc-check-modules": "check-modules.mjs", + "sfmc-check-ootb": "check-ootb.mjs", + "sfmc-fetch-module": "fetch-module.mjs", + "sfmc-sim-new-user": "sim-new-user.mjs", + "sfmc-smoke-modules": "smoke-modules.mjs" + } } } } diff --git a/package.json b/package.json index b2f1c329..33e660ca 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "module-core:typecheck": "npm run sdk:typecheck", "module-core:test": "echo \"no sdk tests yet\"", "module-core:verify": "npm run sdk:verify", - "pack:verify": "npm run build --workspaces --if-present && npm pack -w @sfmc-bds/sdk && npm pack -w @sfmc-bds/cli && npm pack -w @sfmc-bds/db-server && npm pack -w @sfmc-bds/qq-bridge && npm pack -w @sfmc-bds/bds-tools", + "pack:verify": "npm run build --workspaces --if-present && npm pack -w @sfmc-bds/sdk && npm pack -w @sfmc-bds/cli && npm pack -w @sfmc-bds/db-server && npm pack -w @sfmc-bds/qq-bridge && npm pack -w @sfmc-bds/bds-tools && npm pack -w @sfmc-bds/tools", "check-modules": "node tools/check-modules.mjs", "catalog-sync": "node tools/catalog-sync.mjs", "check-ootb": "node tools/check-ootb.mjs", @@ -56,7 +56,8 @@ "qq-bridge", "bds-tools", "sfmc", - "remote-controller" + "remote-controller", + "tools" ], "engines": { "node": ">=22.13.0" diff --git a/tools/README.md b/tools/README.md index bef3da2b..08df0650 100644 --- a/tools/README.md +++ b/tools/README.md @@ -1,11 +1,25 @@ # SFMC dev tools - testing before build +开箱自检、模块安装、冒烟等脚本。面向 monorepo / 已部署的 SFMC 根目录使用。 + ## 安装 ```bash npm install @sfmc-bds/tools ``` +从 npm 安装到非 monorepo `tools/` 路径时,请设置 `SFMC_ROOT` 指向 SFMC 根目录。 + +## 常用命令 + +```bash +npx sfmc-check-ootb +npx sfmc-fetch-module search +npx sfmc-catalog-sync +``` + +或在仓库根:`npm run check-ootb` / `node tools/check-ootb.mjs`。 + ## 仓库 diff --git a/tools/catalog-sync.mjs b/tools/catalog-sync.mjs old mode 100644 new mode 100755 diff --git a/tools/check-modules.mjs b/tools/check-modules.mjs old mode 100644 new mode 100755 diff --git a/tools/check-ootb.mjs b/tools/check-ootb.mjs old mode 100644 new mode 100755 diff --git a/tools/fetch-module.mjs b/tools/fetch-module.mjs old mode 100644 new mode 100755 diff --git a/tools/lib/npm-publish-packages.mjs b/tools/lib/npm-publish-packages.mjs index 028682ca..8a6dbf19 100644 --- a/tools/lib/npm-publish-packages.mjs +++ b/tools/lib/npm-publish-packages.mjs @@ -10,6 +10,7 @@ export const NPM_PUBLISH_PACKAGES = { "@sfmc-bds/db-server": "db-server/package.json", "@sfmc-bds/qq-bridge": "qq-bridge/package.json", "@sfmc-bds/bds-tools": "bds-tools/package.json", + "@sfmc-bds/tools": "tools/package.json", }; /** @returns {keyof typeof NPM_PUBLISH_PACKAGES | null} */ diff --git a/tools/lib/paths.mjs b/tools/lib/paths.mjs index 1314a87a..e4b260f1 100644 --- a/tools/lib/paths.mjs +++ b/tools/lib/paths.mjs @@ -6,8 +6,14 @@ import { fileURLToPath } from "node:url"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); -/** 主仓根目录 */ -export const ROOT = path.resolve(__dirname, "..", ".."); +/** + * 主仓根目录。 + * 优先 SFMC_ROOT(与 db-server / 冒烟脚本一致);否则按 tools/lib 相对路径回退到 monorepo 根。 + * 从 npm 安装到 node_modules 时务必设置 SFMC_ROOT。 + */ +export const ROOT = process.env.SFMC_ROOT + ? path.resolve(process.env.SFMC_ROOT) + : path.resolve(__dirname, "..", ".."); export const MODULES_DIR = path.join(ROOT, "modules"); export const PACKAGES_DIR = path.join(MODULES_DIR, "packages"); diff --git a/tools/package.json b/tools/package.json index ec905870..a7fc45b9 100644 --- a/tools/package.json +++ b/tools/package.json @@ -20,11 +20,23 @@ "tools", "scriptsforminecraftserver" ], - "main": "check-ootb.js", + "main": "./check-ootb.mjs", + "bin": { + "sfmc-check-ootb": "./check-ootb.mjs", + "sfmc-catalog-sync": "./catalog-sync.mjs", + "sfmc-check-modules": "./check-modules.mjs", + "sfmc-fetch-module": "./fetch-module.mjs", + "sfmc-smoke-modules": "./smoke-modules.mjs", + "sfmc-sim-new-user": "./sim-new-user.mjs" + }, "files": [ - "./*", + "*.mjs", + "lib", "README.md" ], + "scripts": { + "build": "node -e \"process.exit(0)\"" + }, "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/" diff --git a/tools/sim-new-user.mjs b/tools/sim-new-user.mjs old mode 100644 new mode 100755 diff --git a/tools/smoke-modules.mjs b/tools/smoke-modules.mjs old mode 100644 new mode 100755