@sfmc-bds/cli 0.3.1 → 0.4.0

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 (106) hide show
  1. package/dist/command-surface.d.ts.map +1 -1
  2. package/dist/command-surface.js +5 -6
  3. package/dist/command-surface.js.map +2 -2
  4. package/dist/commands.d.ts.map +1 -1
  5. package/dist/commands.js +1 -1
  6. package/dist/commands.js.map +2 -2
  7. package/dist/daemon/client.d.ts +3 -0
  8. package/dist/daemon/client.d.ts.map +1 -1
  9. package/dist/daemon/client.js +38 -8
  10. package/dist/daemon/client.js.map +2 -2
  11. package/dist/daemon/protocol.d.ts +9 -3
  12. package/dist/daemon/protocol.d.ts.map +1 -1
  13. package/dist/daemon/protocol.js.map +2 -2
  14. package/dist/daemon/server.d.ts.map +1 -1
  15. package/dist/daemon/server.js +82 -2
  16. package/dist/daemon/server.js.map +2 -2
  17. package/dist/i18n/locales/en.d.ts +12 -5
  18. package/dist/i18n/locales/en.d.ts.map +1 -1
  19. package/dist/i18n/locales/en.js +12 -5
  20. package/dist/i18n/locales/en.js.map +2 -2
  21. package/dist/i18n/locales/zh-CN.d.ts.map +1 -1
  22. package/dist/i18n/locales/zh-CN.js +12 -5
  23. package/dist/i18n/locales/zh-CN.js.map +2 -2
  24. package/dist/interactive-prompts.d.ts +1 -1
  25. package/dist/interactive-prompts.d.ts.map +1 -1
  26. package/dist/interactive-prompts.js +4 -2
  27. package/dist/interactive-prompts.js.map +2 -2
  28. package/dist/logs.d.ts.map +1 -1
  29. package/dist/logs.js +13 -1
  30. package/dist/logs.js.map +2 -2
  31. package/dist/main.js +14 -184
  32. package/dist/main.js.map +2 -2
  33. package/dist/management/config.d.ts +8 -0
  34. package/dist/management/config.d.ts.map +1 -0
  35. package/dist/management/config.js +107 -0
  36. package/dist/management/config.js.map +7 -0
  37. package/dist/management/deployment.d.ts +7 -0
  38. package/dist/management/deployment.d.ts.map +1 -0
  39. package/dist/management/deployment.js +36 -0
  40. package/dist/management/deployment.js.map +7 -0
  41. package/dist/management/domain.d.ts +65 -0
  42. package/dist/management/domain.d.ts.map +1 -0
  43. package/dist/management/domain.js +137 -0
  44. package/dist/management/domain.js.map +7 -0
  45. package/dist/management/maintenance.d.ts +18 -0
  46. package/dist/management/maintenance.d.ts.map +1 -0
  47. package/dist/management/maintenance.js +214 -0
  48. package/dist/management/maintenance.js.map +7 -0
  49. package/dist/management/platform-worker.d.ts +2 -0
  50. package/dist/management/platform-worker.d.ts.map +1 -0
  51. package/dist/management/platform-worker.js +155 -0
  52. package/dist/management/platform-worker.js.map +7 -0
  53. package/dist/management/server.d.ts +7 -0
  54. package/dist/management/server.d.ts.map +1 -0
  55. package/dist/management/server.js +291 -0
  56. package/dist/management/server.js.map +7 -0
  57. package/dist/management/startup.d.ts +6 -0
  58. package/dist/management/startup.d.ts.map +1 -0
  59. package/dist/management/startup.js +67 -0
  60. package/dist/management/startup.js.map +7 -0
  61. package/dist/management/stdio.d.ts +3 -0
  62. package/dist/management/stdio.d.ts.map +1 -0
  63. package/dist/management/stdio.js +93 -0
  64. package/dist/management/stdio.js.map +7 -0
  65. package/dist/management/tasks.d.ts +21 -0
  66. package/dist/management/tasks.d.ts.map +1 -0
  67. package/dist/management/tasks.js +103 -0
  68. package/dist/management/tasks.js.map +7 -0
  69. package/dist/module-commands.d.ts +2 -2
  70. package/dist/module-commands.d.ts.map +1 -1
  71. package/dist/module-commands.js +28 -24
  72. package/dist/module-commands.js.map +2 -2
  73. package/dist/module-update/index.d.ts +2 -5
  74. package/dist/module-update/index.d.ts.map +1 -1
  75. package/dist/module-update/index.js +5 -29
  76. package/dist/module-update/index.js.map +2 -2
  77. package/dist/pack-lifecycle.d.ts.map +1 -1
  78. package/dist/pack-lifecycle.js +2 -0
  79. package/dist/pack-lifecycle.js.map +2 -2
  80. package/dist/runtime-main.d.ts +3 -0
  81. package/dist/runtime-main.d.ts.map +1 -0
  82. package/dist/runtime-main.js +188 -0
  83. package/dist/runtime-main.js.map +7 -0
  84. package/dist/runtime.d.ts +6 -0
  85. package/dist/runtime.d.ts.map +1 -1
  86. package/dist/runtime.js +6 -1
  87. package/dist/runtime.js.map +2 -2
  88. package/dist/services.d.ts +19 -10
  89. package/dist/services.d.ts.map +1 -1
  90. package/dist/services.js +24 -13
  91. package/dist/services.js.map +2 -2
  92. package/dist/wizard.d.ts.map +1 -1
  93. package/dist/wizard.js +213 -142
  94. package/dist/wizard.js.map +3 -3
  95. package/dist/world-packs.d.ts.map +1 -1
  96. package/dist/world-packs.js +7 -0
  97. package/dist/world-packs.js.map +2 -2
  98. package/package.json +8 -4
  99. package/scripts/module-install/.sfmc-registry-cache.json +95 -0
  100. package/scripts/module-install/fetch-module.mjs +9 -3
  101. package/scripts/module-install/lib/module-update-config.mjs +2 -2
  102. package/scripts/module-install/lib/module-update.mjs +94 -6
  103. package/scripts/module-install/lib/packages.mjs +3 -6
  104. package/scripts/module-install/lib/pins.mjs +15 -4
  105. package/scripts/module-install/lib/registry-cache.mjs +5 -1
  106. package/scripts/module-install/test/module-update.test.mjs +349 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/management/config.ts"],
4
+ "sourcesContent": ["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { createHash, randomUUID } from \"node:crypto\";\nimport Ajv2020 from \"ajv/dist/2020.js\";\nimport { parse, type ParseError } from \"jsonc-parser\";\nimport type { ConfigDocument } from \"@sfmc-bds/management\";\nimport { resolveSdkPackageRoot, ROOT } from \"../runtime.js\";\nimport { resolveBdsContext } from \"../pack-lifecycle.js\";\nimport { readJson, stripBom } from \"./domain.js\";\n\n/** \u78C1\u76D8\u4E0A\u7684\u771F\u5B9E\u6587\u4EF6\u540D\u3002schema \u6587\u4EF6\u4ECD\u7528\u4E0B\u5212\u7EBF\uFF08log_filter.schema.json\uFF09\u3002 */\nconst CORE_FILES = [\"runtime.json\", \"db_config.json\", \"qq_config.json\", \"bds_updater.json\", \"permissions.json\", \"log-filter.json\", \"module-update.json\", \"pack-update.json\"];\nconst SCHEMA_STEM: Record<string, string> = {\n \"log-filter.json\": \"log_filter\",\n \"module-update.json\": \"module_update\",\n \"pack-update.json\": \"pack_update\",\n};\nconst ajv = new Ajv2020.default({ allErrors: true, strict: false });\nconst validators = new Map<string, ReturnType<typeof ajv.compile>>();\nexport function configFiles(): Map<string, string> {\n const files = new Map(CORE_FILES.map(name => [`core/${name}`, path.join(ROOT, \"configs\", name)]));\n files.set(\"bds/server.properties\", path.join(resolveBdsContext().bdsRoot, \"server.properties\"));\n const modules = path.join(ROOT, \"modules\", \"packages\");\n if (fs.existsSync(modules)) for (const entry of fs.readdirSync(modules, { withFileTypes: true })) {\n if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;\n const configDir = path.join(modules, entry.name, \"configs\");\n if (!fs.existsSync(configDir)) continue;\n for (const filename of fs.readdirSync(configDir)) if (/^[\\w.-]+\\.jsonc?$/.test(filename)) files.set(`module/${entry.name}/${filename}`, path.join(configDir, filename));\n }\n // \u771F\u5B9E\u5B9E\u4F8B\u7684\u6A21\u5757\u914D\u7F6E\u5199\u5728 <SFMC_ROOT>/configs/<configKey>.json\uFF0C\u76EE\u5F55\u540D\u6765\u81EA catalog.configKey\u3002\n const catalog = readJson<{ modules?: { id?: string; configKey?: string }[] }>(path.join(ROOT, \"modules\", \"catalog.json\"), {});\n const taken = new Set([...files.values()].map(file => path.resolve(file)));\n for (const row of catalog.modules ?? []) {\n if (!row.id || !row.configKey || !/^[\\w.-]+$/.test(row.id) || !/^[\\w.-]+$/.test(row.configKey)) continue;\n const filename = `${row.configKey}.json`;\n const file = path.join(ROOT, \"configs\", filename);\n if (!fs.existsSync(file) || taken.has(path.resolve(file))) continue;\n files.set(`module/${row.id}/${filename}`, file);\n taken.add(path.resolve(file));\n }\n return files;\n}\nexport function configPath(key: string): string {\n const file = configFiles().get(key);\n if (!file) throw Object.assign(new Error(\"\u914D\u7F6E\u4E0D\u5728\u5141\u8BB8\u7F16\u8F91\u7684\u5217\u8868\u4E2D\"), { code: \"validation\" });\n if (fs.existsSync(file) && fs.lstatSync(file).isSymbolicLink()) throw new Error(\"\u4E0D\u80FD\u901A\u8FC7\u914D\u7F6E\u6587\u4EF6\u94FE\u63A5\u5199\u5165\u5176\u4ED6\u6587\u4EF6\");\n return file;\n}\nexport function revision(text: string) { return createHash(\"sha256\").update(text).digest(\"hex\"); }\nexport function readDocument(key: string): ConfigDocument {\n const file = configPath(key);\n const text = fs.existsSync(file) ? stripBom(fs.readFileSync(file, \"utf8\")) : \"\";\n let schema: object | undefined;\n if (key.startsWith(\"core/\")) {\n const name = SCHEMA_STEM[path.basename(file)] ?? path.basename(file, \".json\");\n const schemaFile = path.join(resolveSdkPackageRoot(), \"schemas\", `${name}.schema.json`);\n if (fs.existsSync(schemaFile)) schema = JSON.parse(fs.readFileSync(schemaFile, \"utf8\")) as object;\n } else if (key.startsWith(\"module/\")) {\n const schemaFile = path.join(path.dirname(file), `${path.basename(file).replace(/\\.jsonc?$/, \"\")}.schema.json`);\n if (fs.existsSync(schemaFile)) schema = JSON.parse(fs.readFileSync(schemaFile, \"utf8\")) as object;\n }\n return { key, text, revision: revision(text), ...(schema ? { schema } : {}), format: file.endsWith(\".properties\") ? \"properties\" : file.endsWith(\".jsonc\") ? \"jsonc\" : \"json\", affectedServices: [\"db\", \"tunnel\", \"qq\", \"llbot\", \"bds\"] };\n}\nexport function validateDocument(document: ConfigDocument, text: string) {\n if (Buffer.byteLength(text) > 2 * 1024 * 1024) throw new Error(\"\u914D\u7F6E\u6587\u4EF6\u8FC7\u5927\");\n if (document.format === \"properties\") {\n for (const line of text.split(/\\r?\\n/)) if (line.trim() && !line.trim().startsWith(\"#\") && !/^[^=\\r\\n]+=.*$/.test(line)) throw new Error(\"server.properties \u5305\u542B\u975E\u6CD5\u914D\u7F6E\u884C\");\n return;\n }\n const errors: ParseError[] = [];\n const value: unknown = parse(text, errors, { allowTrailingComma: document.format === \"jsonc\", disallowComments: document.format === \"json\" });\n if (errors.length) throw Object.assign(new Error(\"\u914D\u7F6E\u8BED\u6CD5\u9519\u8BEF\"), { code: \"validation\", details: errors });\n if (document.schema) {\n // \u4FDD\u7559\u5DF2\u6709\u672A\u77E5\u5B57\u6BB5\uFF1B\u53EA\u4ECE\u6821\u9A8C\u526F\u672C\u6392\u9664\u672A\u4FEE\u6539\u7684\u672A\u77E5\u5B57\u6BB5\uFF0C\u4FDD\u5B58\u7684\u539F\u6587\u4ECD\u5B8C\u5168\u4FDD\u7559\u3002\n const schemaObject = document.schema as { additionalProperties?: boolean; properties?: Record<string, unknown> };\n if (schemaObject.additionalProperties === false && value && typeof value === \"object\" && !Array.isArray(value)) {\n const previous = parse(document.text || \"{}\") as Record<string, unknown>;\n for (const key of Object.keys(value)) if (!(key in (schemaObject.properties ?? {})) && previous && key in previous && JSON.stringify(previous[key]) === JSON.stringify((value as Record<string, unknown>)[key])) delete (value as Record<string, unknown>)[key];\n }\n const hash = revision(JSON.stringify(document.schema));\n let validator = validators.get(hash);\n if (!validator) { const schema = { ...document.schema } as Record<string, unknown>; delete schema.$id; validator = ajv.compile(schema); validators.set(hash, validator); }\n if (!validator(value)) throw Object.assign(new Error(\"\u914D\u7F6E\u4E0D\u7B26\u5408 Schema\"), { code: \"validation\", details: validator.errors });\n }\n}\nexport function saveDocument(key: string, expected: string, text: string) {\n const document = readDocument(key);\n if (document.revision !== expected) throw Object.assign(new Error(\"\u914D\u7F6E\u5DF2\u88AB\u5176\u4ED6\u64CD\u4F5C\u4FEE\u6539\uFF0C\u8BF7\u91CD\u65B0\u8BFB\u53D6\"), { code: \"conflict\" });\n validateDocument(document, text);\n const file = configPath(key); const temp = `${file}.${randomUUID()}.tmp`;\n fs.mkdirSync(path.dirname(file), { recursive: true });\n fs.writeFileSync(temp, text, { mode: 0o600 }); fs.renameSync(temp, file);\n return document;\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,7 @@
1
+ export declare function deploymentPort(value: unknown, fallback: number): number;
2
+ export declare function deploymentPreflight(ports: {
3
+ db: number;
4
+ bds: number;
5
+ bds6: number;
6
+ }): Promise<void>;
7
+ //# sourceMappingURL=deployment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deployment.d.ts","sourceRoot":"","sources":["../../src/management/deployment.ts"],"names":[],"mappings":"AAKA,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,UAI9D;AACD,wBAAsB,mBAAmB,CAAC,KAAK,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,iBAgBzF"}
@@ -0,0 +1,36 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import net from "node:net";
4
+ import dgram from "node:dgram";
5
+ import { ROOT } from "../runtime.js";
6
+ function deploymentPort(value, fallback) {
7
+ const port = value === void 0 ? fallback : Number(value);
8
+ if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error("\u7AEF\u53E3\u5FC5\u987B\u4E3A 1 \u5230 65535 \u7684\u6574\u6570");
9
+ return port;
10
+ }
11
+ async function deploymentPreflight(ports) {
12
+ if (path.resolve(ROOT) === path.parse(ROOT).root) throw new Error("\u4E0D\u80FD\u5728\u78C1\u76D8\u6839\u76EE\u5F55\u521B\u5EFA\u90E8\u7F72");
13
+ for (const file of ["BDS/bedrock_server.exe", "BDS/bedrock_server", "data/sfmc_data.db"]) if (fs.existsSync(path.join(ROOT, file))) throw new Error("\u5DF2\u6709 BDS \u6216\u6570\u636E\u5E93\uFF0C\u8BF7\u63A5\u5165\u73B0\u6709\u90E8\u7F72\uFF0C\u4E0D\u8986\u76D6\u6570\u636E");
14
+ const space = fs.statfsSync(ROOT);
15
+ if (space.bavail * space.bsize < 1024 ** 3) throw new Error("\u53EF\u7528\u7A7A\u95F4\u4E0D\u8DB3 1 GiB\uFF0C\u65E0\u6CD5\u51C6\u5907\u8FD0\u884C\u65F6\u3001BDS \u548C\u7EF4\u62A4\u5907\u4EFD");
16
+ await new Promise((resolve, reject) => {
17
+ const server = net.createServer();
18
+ server.once("error", (error) => reject(new Error(`DB \u7AEF\u53E3 ${ports.db} \u4E0D\u53EF\u7528: ${error.message}`)));
19
+ server.listen(ports.db, "0.0.0.0", () => server.close((error) => error ? reject(error) : resolve()));
20
+ });
21
+ for (const [port, type, address] of [[ports.bds, "udp4", "0.0.0.0"], [ports.bds6, "udp6", "::"]]) {
22
+ await new Promise((resolve, reject) => {
23
+ const socket = dgram.createSocket(type);
24
+ socket.once("error", (error) => {
25
+ socket.close();
26
+ reject(new Error(`BDS \u7AEF\u53E3 ${port} \u4E0D\u53EF\u7528: ${error.message}`));
27
+ });
28
+ socket.bind(port, address, () => socket.close(() => resolve()));
29
+ });
30
+ }
31
+ }
32
+ export {
33
+ deploymentPort,
34
+ deploymentPreflight
35
+ };
36
+ //# sourceMappingURL=deployment.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/management/deployment.ts"],
4
+ "sourcesContent": ["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport net from \"node:net\";\nimport dgram from \"node:dgram\";\nimport { ROOT } from \"../runtime.js\";\nexport function deploymentPort(value: unknown, fallback: number) {\n const port = value === undefined ? fallback : Number(value);\n if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error(\"\u7AEF\u53E3\u5FC5\u987B\u4E3A 1 \u5230 65535 \u7684\u6574\u6570\");\n return port;\n}\nexport async function deploymentPreflight(ports: { db: number; bds: number; bds6: number }) {\n if (path.resolve(ROOT) === path.parse(ROOT).root) throw new Error(\"\u4E0D\u80FD\u5728\u78C1\u76D8\u6839\u76EE\u5F55\u521B\u5EFA\u90E8\u7F72\");\n for (const file of [\"BDS/bedrock_server.exe\", \"BDS/bedrock_server\", \"data/sfmc_data.db\"]) if (fs.existsSync(path.join(ROOT, file))) throw new Error(\"\u5DF2\u6709 BDS \u6216\u6570\u636E\u5E93\uFF0C\u8BF7\u63A5\u5165\u73B0\u6709\u90E8\u7F72\uFF0C\u4E0D\u8986\u76D6\u6570\u636E\");\n const space = fs.statfsSync(ROOT);\n if (space.bavail * space.bsize < 1024 ** 3) throw new Error(\"\u53EF\u7528\u7A7A\u95F4\u4E0D\u8DB3 1 GiB\uFF0C\u65E0\u6CD5\u51C6\u5907\u8FD0\u884C\u65F6\u3001BDS \u548C\u7EF4\u62A4\u5907\u4EFD\");\n await new Promise<void>((resolve, reject) => {\n const server = net.createServer(); server.once(\"error\", error => reject(new Error(`DB \u7AEF\u53E3 ${ports.db} \u4E0D\u53EF\u7528: ${error.message}`)));\n server.listen(ports.db, \"0.0.0.0\", () => server.close(error => error ? reject(error) : resolve()));\n });\n for (const [port, type, address] of [[ports.bds, \"udp4\", \"0.0.0.0\"], [ports.bds6, \"udp6\", \"::\"]] as const) {\n await new Promise<void>((resolve, reject) => {\n const socket = dgram.createSocket(type);\n socket.once(\"error\", error => { socket.close(); reject(new Error(`BDS \u7AEF\u53E3 ${port} \u4E0D\u53EF\u7528: ${error.message}`)); });\n socket.bind(port, address, () => socket.close(() => resolve()));\n });\n }\n}\n"],
5
+ "mappings": "AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,OAAO,SAAS;AAChB,OAAO,WAAW;AAClB,SAAS,YAAY;AACd,SAAS,eAAe,OAAgB,UAAkB;AAC/D,QAAM,OAAO,UAAU,SAAY,WAAW,OAAO,KAAK;AAC1D,MAAI,CAAC,OAAO,UAAU,IAAI,KAAK,OAAO,KAAK,OAAO,MAAO,OAAM,IAAI,MAAM,kEAAqB;AAC9F,SAAO;AACT;AACA,eAAsB,oBAAoB,OAAkD;AAC1F,MAAI,KAAK,QAAQ,IAAI,MAAM,KAAK,MAAM,IAAI,EAAE,KAAM,OAAM,IAAI,MAAM,0EAAc;AAChF,aAAW,QAAQ,CAAC,0BAA0B,sBAAsB,mBAAmB,EAAG,KAAI,GAAG,WAAW,KAAK,KAAK,MAAM,IAAI,CAAC,EAAG,OAAM,IAAI,MAAM,+HAA2B;AAC/K,QAAM,QAAQ,GAAG,WAAW,IAAI;AAChC,MAAI,MAAM,SAAS,MAAM,QAAQ,QAAQ,EAAG,OAAM,IAAI,MAAM,oIAAgC;AAC5F,QAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,UAAM,SAAS,IAAI,aAAa;AAAG,WAAO,KAAK,SAAS,WAAS,OAAO,IAAI,MAAM,mBAAS,MAAM,EAAE,wBAAS,MAAM,OAAO,EAAE,CAAC,CAAC;AAC7H,WAAO,OAAO,MAAM,IAAI,WAAW,MAAM,OAAO,MAAM,WAAS,QAAQ,OAAO,KAAK,IAAI,QAAQ,CAAC,CAAC;AAAA,EACnG,CAAC;AACD,aAAW,CAAC,MAAM,MAAM,OAAO,KAAK,CAAC,CAAC,MAAM,KAAK,QAAQ,SAAS,GAAG,CAAC,MAAM,MAAM,QAAQ,IAAI,CAAC,GAAY;AACzG,UAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,YAAM,SAAS,MAAM,aAAa,IAAI;AACtC,aAAO,KAAK,SAAS,WAAS;AAAE,eAAO,MAAM;AAAG,eAAO,IAAI,MAAM,oBAAU,IAAI,wBAAS,MAAM,OAAO,EAAE,CAAC;AAAA,MAAG,CAAC;AAC5G,aAAO,KAAK,MAAM,SAAS,MAAM,OAAO,MAAM,MAAM,QAAQ,CAAC,CAAC;AAAA,IAChE,CAAC;AAAA,EACH;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * 去掉 UTF-8 BOM。
3
+ * 使用场景:编辑器或 BDS 写出的 JSON(例如 configs/permissions.json)常带 BOM,直接 JSON.parse 会失败。
4
+ */
5
+ export declare function stripBom(text: string): string;
6
+ export declare function readJson<T>(file: string, fallback: T): T;
7
+ export declare function identifier(value: unknown): string;
8
+ export declare function listModules(): Promise<{
9
+ modules: {
10
+ id: string;
11
+ folder: string;
12
+ name: string;
13
+ version: string;
14
+ enabled: boolean;
15
+ linked: boolean;
16
+ }[];
17
+ }>;
18
+ export declare function searchModules(query: string): Promise<{
19
+ modules: {
20
+ npm?: string;
21
+ version?: string;
22
+ sdk?: string;
23
+ repo?: string;
24
+ tag?: string;
25
+ id: string;
26
+ }[];
27
+ stale: boolean;
28
+ }>;
29
+ export declare function runFetch(args: string[]): Promise<{
30
+ changed: boolean;
31
+ }>;
32
+ export declare function runProcess(command: string, args: string[], cwd: string, onLine?: (text: string) => void): Promise<void>;
33
+ export declare function modulePlan(ids?: string[]): Promise<unknown>;
34
+ export declare function applyModulePlan(ids: string[]): Promise<{
35
+ failed: {
36
+ id: string;
37
+ message: string;
38
+ }[];
39
+ }>;
40
+ export declare function listPacks(): {
41
+ packs: {
42
+ id: string;
43
+ name: string;
44
+ kind: import("@sfmc-bds/bds-tools/world-packs").WorldPackKind;
45
+ enabled: boolean;
46
+ version: string;
47
+ }[];
48
+ };
49
+ export declare function togglePack(id: string, enabled: boolean): Promise<{
50
+ changed: boolean;
51
+ restartRequired: boolean;
52
+ }>;
53
+ export declare function importPack(filename: string): Promise<{
54
+ changed: boolean;
55
+ restartRequired: boolean;
56
+ }>;
57
+ export declare function playerFiles(): {
58
+ allowlist: string;
59
+ permissions: string;
60
+ sfmcPermissions: string;
61
+ };
62
+ export declare function savePlayerPermissions(kind: string, entries: unknown): {
63
+ saved: boolean;
64
+ };
65
+ //# sourceMappingURL=domain.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domain.d.ts","sourceRoot":"","sources":["../../src/management/domain.ts"],"names":[],"mappings":"AAWA;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7C;AACD,wBAAgB,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,CAGxD;AACD,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAGjD;AACD,wBAAsB,WAAW;;;;;;;;;GAMhC;AAED,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM;;;;;;;;;;GAGhD;AACD,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE;;GAG5C;AACD,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAOvH;AACD,wBAAsB,UAAU,CAAC,GAAG,GAAE,MAAM,EAAO,oBAIlD;AACD,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE;YAI+B;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE;GAQlH;AACD,wBAAgB,SAAS;IAEd,KAAK;;;;;;;EACf;AACD,wBAAsB,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO;;;GAO5D;AACD,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM;;;GAiBhD;AACD,wBAAgB,WAAW;IAEhB,SAAS;IAAoC,WAAW;IAAsC,eAAe;EACvH;AACD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO;IAWzD,KAAK;EACf"}
@@ -0,0 +1,137 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { pathToFileURL } from "node:url";
4
+ import { spawn } from "node:child_process";
5
+ import { resolveRegistryIndex } from "../registry.js";
6
+ import { ROOT, resolveFetchModule } from "../runtime.js";
7
+ import { resolveBdsContext, scanLocalModules } from "../pack-lifecycle.js";
8
+ import { listInstalledWorldPacks, resolvePackRoots, readPackManifestInfo, installPackDirectory, enableInstalledPack, disableInstalledPack, worldPackParentDir } from "@sfmc-bds/bds-tools/world-packs";
9
+ import { atomicJson } from "./tasks.js";
10
+ import { isRetiredPlatformModule } from "@sfmc-bds/sdk/contracts";
11
+ function stripBom(text) {
12
+ return text.charCodeAt(0) === 65279 ? text.slice(1) : text;
13
+ }
14
+ function readJson(file, fallback) {
15
+ try {
16
+ return JSON.parse(stripBom(fs.readFileSync(file, "utf8")));
17
+ } catch (error) {
18
+ if (error.code === "ENOENT") return fallback;
19
+ throw error;
20
+ }
21
+ }
22
+ function identifier(value) {
23
+ if (typeof value !== "string" || !/^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,127}$/.test(value) || value.includes("..")) throw new Error("\u975E\u6CD5\u6807\u8BC6\u7B26");
24
+ return value;
25
+ }
26
+ async function listModules() {
27
+ const rows = (await scanLocalModules()).filter((row) => !isRetiredPlatformModule(row.logicalId));
28
+ const catalog = readJson(path.join(ROOT, "modules", "catalog.json"), {});
29
+ const titles = new Map((catalog.modules ?? []).filter((row) => row.id && row.name).map((row) => [row.id, row.name]));
30
+ return { modules: rows.map((row) => ({ id: row.logicalId, folder: row.folderId, name: titles.get(row.logicalId) || row.logicalId, version: row.version ?? "unknown", enabled: row.enabled, linked: fs.lstatSync(path.join(ROOT, "modules", "packages", row.folderId)).isSymbolicLink() })) };
31
+ }
32
+ function fetchEntry() {
33
+ const entry = resolveFetchModule();
34
+ if (!entry) throw new Error("\u627E\u4E0D\u5230\u6A21\u5757\u5B89\u88C5\u5668");
35
+ return entry;
36
+ }
37
+ async function searchModules(query) {
38
+ const result = await resolveRegistryIndex();
39
+ return { modules: Object.entries(result.index).filter(([id]) => !isRetiredPlatformModule(id) && id.includes(query)).map(([id, metadata]) => ({ id, ...metadata })), stale: result.stale };
40
+ }
41
+ async function runFetch(args) {
42
+ await runProcess(process.execPath, [fetchEntry(), ...args], ROOT);
43
+ return { changed: true };
44
+ }
45
+ function runProcess(command, args, cwd, onLine) {
46
+ return new Promise((resolve, reject) => {
47
+ const child = spawn(command, args, { cwd, env: { ...process.env, SFMC_ROOT: ROOT, FORCE_COLOR: "0" }, windowsHide: true, stdio: ["ignore", "pipe", "pipe"] });
48
+ let diagnostic = "";
49
+ for (const stream of [child.stdout, child.stderr]) stream.on("data", (chunk) => {
50
+ const text = chunk.toString();
51
+ diagnostic = (diagnostic + text).slice(-6e3);
52
+ onLine?.(text);
53
+ });
54
+ child.on("error", reject);
55
+ child.on("close", (code) => code === 0 ? resolve() : reject(new Error(`\u5B50\u8FDB\u7A0B\u5931\u8D25 (${code}): ${diagnostic}`)));
56
+ });
57
+ }
58
+ async function modulePlan(ids = []) {
59
+ const url = pathToFileURL(path.join(path.dirname(fetchEntry()), "lib", "module-update.mjs")).href;
60
+ const api = await import(url);
61
+ return api.planModuleUpdates({ root: ROOT, ids });
62
+ }
63
+ async function applyModulePlan(ids) {
64
+ const directory = path.dirname(fetchEntry());
65
+ const api = await import(pathToFileURL(path.join(directory, "lib", "module-update.mjs")).href);
66
+ const fetchApi = await import(pathToFileURL(fetchEntry()).href);
67
+ const plan = await api.planModuleUpdates({ root: ROOT, ids });
68
+ const result = await api.applyModuleUpgrades(plan.upgrades, { root: ROOT, failMode: plan.failMode, install: fetchApi.installPreservingLock, resync: api.resyncInstalledCatalog });
69
+ if (result.failed.length) throw new Error(JSON.stringify(result.failed));
70
+ return result;
71
+ }
72
+ function listPacks() {
73
+ const context = resolveBdsContext();
74
+ return { packs: listInstalledWorldPacks(context.bdsRoot, context.levelName).map((pack) => ({ id: pack.uuid, name: pack.name, kind: pack.kind, enabled: pack.enabled, version: pack.version.join(".") })) };
75
+ }
76
+ async function togglePack(id, enabled) {
77
+ const context = resolveBdsContext();
78
+ const pack = listInstalledWorldPacks(context.bdsRoot, context.levelName).find((row) => row.uuid === id);
79
+ if (!pack) throw new Error("\u627E\u4E0D\u5230\u4E16\u754C\u5305");
80
+ if (enabled) await enableInstalledPack({ ...context, info: { uuid: pack.uuid, version: pack.version, kind: pack.kind, name: pack.name } });
81
+ else await disableInstalledPack({ ...context, kind: pack.kind, packUuid: pack.uuid, version: pack.version });
82
+ return { changed: true, restartRequired: true };
83
+ }
84
+ async function importPack(filename) {
85
+ const inbox = path.resolve(ROOT, "packs", "_desktop-inbox");
86
+ const file = path.resolve(inbox, identifier(filename));
87
+ if (!file.startsWith(inbox + path.sep) || !fs.existsSync(file)) throw new Error("\u4E16\u754C\u5305\u5C1A\u672A\u4E0A\u4F20\u5230\u5B9E\u4F8B\u6536\u4EF6\u7BB1");
88
+ const context = resolveBdsContext();
89
+ const resolved = await resolvePackRoots(file);
90
+ try {
91
+ if (!resolved.roots.length) throw new Error("\u5F52\u6863\u4E0D\u542B\u53EF\u8BC6\u522B\u7684\u4E16\u754C\u5305");
92
+ for (const root of resolved.roots) {
93
+ const info = readPackManifestInfo(root);
94
+ if (!info) throw new Error("\u4E16\u754C\u5305 manifest \u65E0\u6548");
95
+ const result = await installPackDirectory({ srcDir: root, destParent: worldPackParentDir(context.bdsRoot, context.levelName, info.kind) });
96
+ if (!result.ok) throw new Error(result.reason ?? "\u5B89\u88C5\u5931\u8D25\uFF1B\u53EF\u80FD\u4E0E\u5DF2\u6709\u5305\u51B2\u7A81");
97
+ await enableInstalledPack({ ...context, info });
98
+ }
99
+ } finally {
100
+ resolved.dispose?.();
101
+ }
102
+ return { changed: true, restartRequired: true };
103
+ }
104
+ function playerFiles() {
105
+ const bds = resolveBdsContext().bdsRoot;
106
+ return { allowlist: path.join(bds, "allowlist.json"), permissions: path.join(bds, "permissions.json"), sfmcPermissions: path.join(ROOT, "configs", "permissions.json") };
107
+ }
108
+ function savePlayerPermissions(kind, entries) {
109
+ const files = playerFiles();
110
+ if (!(kind in files) || !Array.isArray(entries)) throw new Error("\u975E\u6CD5\u73A9\u5BB6\u6743\u9650\u7C7B\u578B\u6216\u5185\u5BB9");
111
+ for (const entry of entries) {
112
+ if (!entry || typeof entry !== "object") throw new Error("\u6743\u9650\u6761\u76EE\u5FC5\u987B\u4E3A\u5BF9\u8C61");
113
+ const row = entry;
114
+ if (kind === "allowlist" && (typeof row.name !== "string" || /[\r\n]/.test(row.name))) throw new Error("\u5141\u8BB8\u540D\u5355\u9700\u63D0\u4F9B\u5408\u6CD5\u73A9\u5BB6\u540D\u79F0");
115
+ if (kind === "permissions" && (typeof row.xuid !== "string" || !/^\d+$/.test(row.xuid) || !["visitor", "member", "operator"].includes(String(row.permission)))) throw new Error("BDS \u6743\u9650\u9700\u6709\u6548 XUID \u548C\u6743\u9650\u7B49\u7EA7");
116
+ if (kind === "sfmcPermissions" && (typeof row.player_name !== "string" || !row.player_name.trim() || /[\r\n\0]/.test(row.player_name) || !Number.isInteger(row.level) || Number(row.level) < 0 || Number(row.level) > 3)) throw new Error("SFMC \u6743\u9650\u9700\u5408\u6CD5\u73A9\u5BB6\u540D\u53CA 0 \u5230 3 \u7684\u6574\u6570\u7B49\u7EA7");
117
+ }
118
+ atomicJson(files[kind], entries);
119
+ return { saved: true };
120
+ }
121
+ export {
122
+ applyModulePlan,
123
+ identifier,
124
+ importPack,
125
+ listModules,
126
+ listPacks,
127
+ modulePlan,
128
+ playerFiles,
129
+ readJson,
130
+ runFetch,
131
+ runProcess,
132
+ savePlayerPermissions,
133
+ searchModules,
134
+ stripBom,
135
+ togglePack
136
+ };
137
+ //# sourceMappingURL=domain.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/management/domain.ts"],
4
+ "sourcesContent": ["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { spawn } from \"node:child_process\";\nimport { resolveRegistryIndex } from \"../registry.js\";\nimport { ROOT, resolveFetchModule } from \"../runtime.js\";\nimport { resolveBdsContext, scanLocalModules } from \"../pack-lifecycle.js\";\nimport { listInstalledWorldPacks, resolvePackRoots, readPackManifestInfo, installPackDirectory, enableInstalledPack, disableInstalledPack, worldPackParentDir } from \"@sfmc-bds/bds-tools/world-packs\";\nimport { atomicJson } from \"./tasks.js\";\nimport { isRetiredPlatformModule } from \"@sfmc-bds/sdk/contracts\";\n\n/**\n * \u53BB\u6389 UTF-8 BOM\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A\u7F16\u8F91\u5668\u6216 BDS \u5199\u51FA\u7684 JSON\uFF08\u4F8B\u5982 configs/permissions.json\uFF09\u5E38\u5E26 BOM\uFF0C\u76F4\u63A5 JSON.parse \u4F1A\u5931\u8D25\u3002\n */\nexport function stripBom(text: string): string {\n return text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;\n}\nexport function readJson<T>(file: string, fallback: T): T {\n try { return JSON.parse(stripBom(fs.readFileSync(file, \"utf8\"))) as T; }\n catch (error) { if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return fallback; throw error; }\n}\nexport function identifier(value: unknown): string {\n if (typeof value !== \"string\" || !/^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,127}$/.test(value) || value.includes(\"..\")) throw new Error(\"\u975E\u6CD5\u6807\u8BC6\u7B26\");\n return value;\n}\nexport async function listModules() {\n const rows = (await scanLocalModules()).filter(row => !isRetiredPlatformModule(row.logicalId));\n // \u76EE\u5F55\u91CC\u7684\u5C55\u793A\u540D\u6765\u81EA modules/catalog.json\uFF1B\u6CA1\u6709\u4E2D\u6587\u540D\u65F6\u4ECD\u7528\u903B\u8F91 id\uFF0C\u907F\u514D\u754C\u9762\u53EA\u5269\u6587\u4EF6\u5939\u540D\u3002\n const catalog = readJson<{ modules?: { id?: string; name?: string }[] }>(path.join(ROOT, \"modules\", \"catalog.json\"), {});\n const titles = new Map((catalog.modules ?? []).filter(row => row.id && row.name).map(row => [row.id!, row.name!]));\n return { modules: rows.map(row => ({ id: row.logicalId, folder: row.folderId, name: titles.get(row.logicalId) || row.logicalId, version: row.version ?? \"unknown\", enabled: row.enabled, linked: fs.lstatSync(path.join(ROOT, \"modules\", \"packages\", row.folderId)).isSymbolicLink() })) };\n}\nfunction fetchEntry() { const entry = resolveFetchModule(); if (!entry) throw new Error(\"\u627E\u4E0D\u5230\u6A21\u5757\u5B89\u88C5\u5668\"); return entry; }\nexport async function searchModules(query: string) {\n const result = await resolveRegistryIndex();\n return { modules: Object.entries(result.index).filter(([id]) => !isRetiredPlatformModule(id) && id.includes(query)).map(([id, metadata]) => ({ id, ...metadata })), stale: result.stale };\n}\nexport async function runFetch(args: string[]) {\n await runProcess(process.execPath, [fetchEntry(), ...args], ROOT);\n return { changed: true };\n}\nexport function runProcess(command: string, args: string[], cwd: string, onLine?: (text: string) => void): Promise<void> {\n return new Promise((resolve, reject) => {\n const child = spawn(command, args, { cwd, env: { ...process.env, SFMC_ROOT: ROOT, FORCE_COLOR: \"0\" }, windowsHide: true, stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n let diagnostic = \"\";\n for (const stream of [child.stdout, child.stderr]) stream.on(\"data\", (chunk: Buffer) => { const text = chunk.toString(); diagnostic = (diagnostic + text).slice(-6000); onLine?.(text); });\n child.on(\"error\", reject); child.on(\"close\", code => code === 0 ? resolve() : reject(new Error(`\u5B50\u8FDB\u7A0B\u5931\u8D25 (${code}): ${diagnostic}`)));\n });\n}\nexport async function modulePlan(ids: string[] = []) {\n const url = pathToFileURL(path.join(path.dirname(fetchEntry()), \"lib\", \"module-update.mjs\")).href;\n const api = await import(url) as { planModuleUpdates: (options: { root: string; ids: string[] }) => Promise<unknown> };\n return api.planModuleUpdates({ root: ROOT, ids });\n}\nexport async function applyModulePlan(ids: string[]) {\n const directory = path.dirname(fetchEntry());\n const api = await import(pathToFileURL(path.join(directory, \"lib\", \"module-update.mjs\")).href) as {\n planModuleUpdates: (options: { root: string; ids: string[] }) => Promise<{ upgrades: { id: string; spec: string }[]; failMode: \"abort\" | \"continue\" }>;\n applyModuleUpgrades: (upgrades: unknown[], deps: object) => Promise<{ failed: { id: string; message: string }[] }>;\n resyncInstalledCatalog: (id: string) => void;\n };\n const fetchApi = await import(pathToFileURL(fetchEntry()).href) as { installPreservingLock: (id: string, spec: string) => Promise<void> };\n const plan = await api.planModuleUpdates({ root: ROOT, ids });\n const result = await api.applyModuleUpgrades(plan.upgrades, { root: ROOT, failMode: plan.failMode, install: fetchApi.installPreservingLock, resync: api.resyncInstalledCatalog });\n if (result.failed.length) throw new Error(JSON.stringify(result.failed));\n return result;\n}\nexport function listPacks() {\n const context = resolveBdsContext();\n return { packs: listInstalledWorldPacks(context.bdsRoot, context.levelName).map(pack => ({ id: pack.uuid, name: pack.name, kind: pack.kind, enabled: pack.enabled, version: pack.version.join(\".\") })) };\n}\nexport async function togglePack(id: string, enabled: boolean) {\n const context = resolveBdsContext();\n const pack = listInstalledWorldPacks(context.bdsRoot, context.levelName).find(row => row.uuid === id);\n if (!pack) throw new Error(\"\u627E\u4E0D\u5230\u4E16\u754C\u5305\");\n if (enabled) await enableInstalledPack({ ...context, info: { uuid: pack.uuid, version: pack.version, kind: pack.kind, name: pack.name } });\n else await disableInstalledPack({ ...context, kind: pack.kind, packUuid: pack.uuid, version: pack.version });\n return { changed: true, restartRequired: true };\n}\nexport async function importPack(filename: string) {\n const inbox = path.resolve(ROOT, \"packs\", \"_desktop-inbox\");\n const file = path.resolve(inbox, identifier(filename));\n if (!file.startsWith(inbox + path.sep) || !fs.existsSync(file)) throw new Error(\"\u4E16\u754C\u5305\u5C1A\u672A\u4E0A\u4F20\u5230\u5B9E\u4F8B\u6536\u4EF6\u7BB1\");\n const context = resolveBdsContext();\n const resolved = await resolvePackRoots(file);\n try {\n if (!resolved.roots.length) throw new Error(\"\u5F52\u6863\u4E0D\u542B\u53EF\u8BC6\u522B\u7684\u4E16\u754C\u5305\");\n for (const root of resolved.roots) {\n const info = readPackManifestInfo(root);\n if (!info) throw new Error(\"\u4E16\u754C\u5305 manifest \u65E0\u6548\");\n const result = await installPackDirectory({ srcDir: root, destParent: worldPackParentDir(context.bdsRoot, context.levelName, info.kind) });\n if (!result.ok) throw new Error(result.reason ?? \"\u5B89\u88C5\u5931\u8D25\uFF1B\u53EF\u80FD\u4E0E\u5DF2\u6709\u5305\u51B2\u7A81\");\n await enableInstalledPack({ ...context, info });\n }\n } finally { resolved.dispose?.(); }\n return { changed: true, restartRequired: true };\n}\nexport function playerFiles() {\n const bds = resolveBdsContext().bdsRoot;\n return { allowlist: path.join(bds, \"allowlist.json\"), permissions: path.join(bds, \"permissions.json\"), sfmcPermissions: path.join(ROOT, \"configs\", \"permissions.json\") };\n}\nexport function savePlayerPermissions(kind: string, entries: unknown) {\n const files = playerFiles();\n if (!(kind in files) || !Array.isArray(entries)) throw new Error(\"\u975E\u6CD5\u73A9\u5BB6\u6743\u9650\u7C7B\u578B\u6216\u5185\u5BB9\");\n for (const entry of entries) {\n if (!entry || typeof entry !== \"object\") throw new Error(\"\u6743\u9650\u6761\u76EE\u5FC5\u987B\u4E3A\u5BF9\u8C61\");\n const row = entry as Record<string, unknown>;\n if (kind === \"allowlist\" && (typeof row.name !== \"string\" || /[\\r\\n]/.test(row.name))) throw new Error(\"\u5141\u8BB8\u540D\u5355\u9700\u63D0\u4F9B\u5408\u6CD5\u73A9\u5BB6\u540D\u79F0\");\n if (kind === \"permissions\" && (typeof row.xuid !== \"string\" || !/^\\d+$/.test(row.xuid) || ![\"visitor\", \"member\", \"operator\"].includes(String(row.permission)))) throw new Error(\"BDS \u6743\u9650\u9700\u6709\u6548 XUID \u548C\u6743\u9650\u7B49\u7EA7\");\n if (kind === \"sfmcPermissions\" && (typeof row.player_name !== \"string\" || !row.player_name.trim() || /[\\r\\n\\0]/.test(row.player_name) || !Number.isInteger(row.level) || Number(row.level) < 0 || Number(row.level) > 3)) throw new Error(\"SFMC \u6743\u9650\u9700\u5408\u6CD5\u73A9\u5BB6\u540D\u53CA 0 \u5230 3 \u7684\u6574\u6570\u7B49\u7EA7\");\n }\n atomicJson(files[kind as keyof typeof files], entries);\n return { saved: true };\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,18 @@
1
+ import { type ServiceName } from "../services.js";
2
+ import { type TaskContext, type TaskStore } from "./tasks.js";
3
+ import type { AttachmentPlan } from "@sfmc-bds/management";
4
+ export declare function platformVersion(): string;
5
+ export declare function attachmentPlan(): Promise<AttachmentPlan>;
6
+ export declare function stopForMaintenance(names: ServiceName[], phase: TaskContext["phase"]): Promise<ServiceName[]>;
7
+ export declare function restoreRunning(names: ServiceName[], phase: TaskContext["phase"]): Promise<void>;
8
+ export declare function verifyRunning(names: ServiceName[]): Promise<void>;
9
+ export declare function createSnapshot(id: string, phase: TaskContext["phase"]): Promise<string>;
10
+ export declare function restoreSnapshot(id: string, phase: TaskContext["phase"]): Promise<{
11
+ restored: string;
12
+ }>;
13
+ export declare function withStoppedServices<T>(context: TaskContext, names: ServiceName[], action: () => Promise<T>, backup?: boolean, rollbackPrograms?: "modules" | "bds"): Promise<T>;
14
+ export declare function launchPlatformUpdate(store: TaskStore, context: TaskContext, target: string): Promise<{
15
+ detached: boolean;
16
+ }>;
17
+ export declare function runPnpm(args: string[], cwd: string): Promise<void>;
18
+ //# sourceMappingURL=maintenance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"maintenance.d.ts","sourceRoot":"","sources":["../../src/management/maintenance.ts"],"names":[],"mappings":"AAMA,OAAO,EAAoD,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAEpG,OAAO,EAAc,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAE1E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAY3D,wBAAgB,eAAe,IAAI,MAAM,CASxC;AACD,wBAAsB,cAAc,IAAI,OAAO,CAAC,cAAc,CAAC,CAkB9D;AACD,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAUlH;AACD,wBAAsB,cAAc,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,iBAOrF;AACD,wBAAsB,aAAa,CAAC,KAAK,EAAE,WAAW,EAAE,iBAWvD;AAMD,wBAAsB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,mBAc3E;AACD,wBAAsB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC;;GAiB5E;AAuBD,wBAAsB,mBAAmB,CAAC,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,UAAO,EAAE,gBAAgB,CAAC,EAAE,SAAS,GAAG,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,CAqBlL;AACD,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM;;GAmBhG;AACD,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,iBASxD"}
@@ -0,0 +1,214 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { createRequire } from "node:module";
4
+ import { spawn } from "node:child_process";
5
+ import { fileURLToPath } from "node:url";
6
+ import { ROOT, isMonorepoLayout, isSourceCheckout } from "../runtime.js";
7
+ import { queryServicesRuntimeLocal, services, START_ORDER } from "../services.js";
8
+ import { resolveBdsContext } from "../pack-lifecycle.js";
9
+ import { atomicJson } from "./tasks.js";
10
+ import { readJson, runProcess } from "./domain.js";
11
+ import { isDaemonServer } from "../daemon/role.js";
12
+ import { callDaemon } from "../daemon/client.js";
13
+ async function runtimeRows() {
14
+ if (isDaemonServer()) return queryServicesRuntimeLocal();
15
+ const result = await callDaemon("status");
16
+ if (result.kind !== "status") throw new Error("\u65E0\u6CD5\u8BFB\u53D6\u5B88\u62A4\u8FDB\u7A0B\u670D\u52A1\u72B6\u6001");
17
+ return result.rows;
18
+ }
19
+ const require2 = createRequire(import.meta.url);
20
+ function platformVersion() {
21
+ if (isMonorepoLayout(ROOT)) return readJson(path.join(ROOT, "packages", "meta", "package.json"), { version: "unknown" }).version;
22
+ if (process.env.SFMC_PLATFORM_VERSION) return process.env.SFMC_PLATFORM_VERSION;
23
+ const active = readJson(path.join(ROOT, ".sfmc", "runtime", "active.json"), {});
24
+ if (active.version) return active.version;
25
+ const deployed = readJson(path.join(ROOT, "node_modules", "@sfmc-bds", "sfmc", "package.json"), {});
26
+ if (deployed.version) return deployed.version;
27
+ try {
28
+ return require2("@sfmc-bds/sfmc/package.json").version;
29
+ } catch {
30
+ return readJson(path.join(fileURLToPath(new URL("../../..", import.meta.url)), "package.json"), { version: "unknown" }).version;
31
+ }
32
+ }
33
+ async function attachmentPlan() {
34
+ const development = isMonorepoLayout(ROOT) || isSourceCheckout();
35
+ const currentVersion = platformVersion();
36
+ let targetVersion = currentVersion;
37
+ if (!development) {
38
+ const response = await fetch("https://registry.npmjs.org/@sfmc-bds%2fsfmc/latest", { signal: AbortSignal.timeout(15e3) });
39
+ if (!response.ok) throw new Error(`\u5E73\u53F0\u7248\u672C\u68C0\u67E5\u5931\u8D25 (${response.status})`);
40
+ const metadata = await response.json();
41
+ if (!metadata.version || !/^\d+\.\d+\.\d+$/.test(metadata.version)) throw new Error("latest \u6CA1\u6709\u6307\u5411\u6709\u6548\u7684\u7A33\u5B9A\u5E73\u53F0\u7248\u672C");
42
+ const currentBase = /^(\d+)\.(\d+)\.(\d+)(?:-[\w.-]+)?$/.exec(currentVersion);
43
+ const latest = metadata.version.split(".").map(Number);
44
+ const current = currentBase?.slice(1).map(Number);
45
+ const difference = current && latest.map((value, index) => value - current[index]).find((value) => value !== 0);
46
+ if (!current || difference === void 0 || difference >= 0) targetVersion = metadata.version;
47
+ }
48
+ const rows = await runtimeRows();
49
+ return { currentVersion, targetVersion, development, upgradeRequired: !development && targetVersion !== currentVersion, externalServices: rows.filter((row) => row.running && row.ownership === "external").map((row) => row.name), steps: development ? ["\u4FDD\u7559\u6E90\u7801\u5DE5\u4F5C\u533A\uFF0C\u8FDE\u63A5\u5F53\u524D\u5F00\u53D1\u90E8\u7F72"] : ["\u4E0B\u8F7D\u56FA\u5B9A\u7248\u672C\u7684\u5E73\u53F0\u4F9D\u8D56\u7EC4\u5408", "\u505C\u6B62\u53D7\u5F71\u54CD\u670D\u52A1\u5E76\u786E\u8BA4\u8FDB\u7A0B\u9000\u51FA", "\u5907\u4EFD\u914D\u7F6E\u3001\u6A21\u5757\u3001\u6570\u636E\u5E93\u4E0E\u4E16\u754C", "\u5207\u6362\u5E73\u53F0\u5E76\u9A8C\u8BC1", "\u6062\u590D\u6B64\u524D\u8FD0\u884C\u7684\u670D\u52A1"] };
50
+ }
51
+ async function stopForMaintenance(names, phase) {
52
+ const before = await queryServicesRuntimeLocal();
53
+ const affected = before.filter((row) => names.includes(row.name) && row.running);
54
+ if (affected.some((row) => row.ownership === "external")) throw Object.assign(new Error("\u5B58\u5728\u65E0\u6CD5\u901A\u8FC7\u5F53\u524D\u5B88\u62A4\u8FDB\u7A0B\u4F18\u96C5\u505C\u6B62\u7684\u5916\u90E8\u670D\u52A1\uFF1B\u8BF7\u5148\u901A\u8FC7\u539F\u7BA1\u7406\u5668\u505C\u6B62\u540E\u91CD\u65B0\u63A5\u5165"), { code: "conflict" });
55
+ phase("stop-services", "\u4F18\u96C5\u505C\u6B62\u5E76\u786E\u8BA4\u8FDB\u7A0B\u9000\u51FA");
56
+ const running = affected.map((row) => row.name);
57
+ for (const name of [...START_ORDER].reverse().filter((name2) => names.includes(name2))) await services[name].stop(false, false);
58
+ const after = await queryServicesRuntimeLocal();
59
+ if (after.some((row) => names.includes(row.name) && row.running)) throw new Error("\u670D\u52A1\u672A\u5B8C\u5168\u9000\u51FA\uFF0C\u5DF2\u4E2D\u6B62\u6587\u4EF6\u64CD\u4F5C");
60
+ return running;
61
+ }
62
+ async function restoreRunning(names, phase) {
63
+ phase("restore", "\u6062\u590D\u7EF4\u62A4\u524D\u8FD0\u884C\u7684\u670D\u52A1");
64
+ for (const name of START_ORDER.filter((name2) => names.includes(name2))) {
65
+ const result = await services[name].start();
66
+ if (result.status === "skipped") throw new Error(`\u6062\u590D ${name} \u5931\u8D25: ${result.reason}`);
67
+ }
68
+ await verifyRunning(names);
69
+ }
70
+ async function verifyRunning(names) {
71
+ if (!names.length) return;
72
+ await new Promise((resolve) => setTimeout(resolve, 2e3));
73
+ for (const name of names) {
74
+ if (!services[name].running || !services[name].pid) throw new Error(`${name} \u542F\u52A8\u540E\u5DF2\u9000\u51FA`);
75
+ }
76
+ if (names.includes("db")) {
77
+ const cfg = readJson(path.join(ROOT, "configs", "db_config.json"), {});
78
+ const response = await fetch(`http://127.0.0.1:${cfg.db_port ?? 3001}/api/sfmc/status`, { headers: cfg.http_auth ? { authorization: `Bearer ${cfg.http_auth}` } : {}, signal: AbortSignal.timeout(5e3) });
79
+ if (!response.ok) throw new Error(`DB \u5065\u5EB7\u68C0\u67E5\u5931\u8D25 (${response.status})`);
80
+ }
81
+ }
82
+ function snapshotTargets() {
83
+ const db = readJson(path.join(ROOT, "configs", "db_config.json"), {});
84
+ const bds = resolveBdsContext().bdsRoot;
85
+ return { configs: path.join(ROOT, "configs"), modules: path.join(ROOT, "modules"), data: path.dirname(path.resolve(ROOT, db.dbDir ?? "data/sfmc_data.db")), bds };
86
+ }
87
+ async function createSnapshot(id, phase) {
88
+ phase("backup", "\u521B\u5EFA\u5DF2\u505C\u670D\u7684\u4E00\u81F4\u5907\u4EFD");
89
+ const destination = path.join(ROOT, ".sfmc", "backups", id);
90
+ fs.mkdirSync(destination, { recursive: true });
91
+ const targets = snapshotTargets();
92
+ for (const target of Object.values(targets)) {
93
+ const absolute = path.resolve(target);
94
+ if (absolute === path.parse(absolute).root || absolute === path.resolve(ROOT) || destination.startsWith(absolute + path.sep)) throw new Error("\u5907\u4EFD\u76EE\u6807\u5305\u542B\u5B9E\u4F8B\u6839\u76EE\u5F55\u6216\u5907\u4EFD\u76EE\u5F55\uFF0C\u5DF2\u4E2D\u6B62");
95
+ }
96
+ for (const [key, target] of Object.entries(targets)) {
97
+ if (fs.existsSync(target)) await fs.promises.cp(target, path.join(destination, key), { recursive: true, dereference: false });
98
+ }
99
+ atomicJson(path.join(destination, "manifest.json"), { createdAt: (/* @__PURE__ */ new Date()).toISOString(), targets, complete: true });
100
+ return destination;
101
+ }
102
+ async function restoreSnapshot(id, phase) {
103
+ if (!/^[a-zA-Z0-9-]{1,80}$/.test(id)) throw new Error("\u975E\u6CD5\u5907\u4EFD\u7F16\u53F7");
104
+ const source = path.join(ROOT, ".sfmc", "backups", id);
105
+ const manifest = readJson(path.join(source, "manifest.json"), { complete: false, targets: {} });
106
+ if (!manifest.complete) throw new Error("\u5907\u4EFD\u4E0D\u5B8C\u6574\uFF0C\u7981\u6B62\u6062\u590D");
107
+ const targets = snapshotTargets();
108
+ phase("restore-data", "\u6309\u7528\u6237\u660E\u786E\u9009\u62E9\u6062\u590D\u914D\u7F6E\u3001\u6A21\u5757\u3001\u6570\u636E\u5E93\u548C\u4E16\u754C");
109
+ for (const [key, target] of Object.entries(targets)) {
110
+ if (path.resolve(manifest.targets[key] ?? "") !== path.resolve(target)) throw new Error("\u5907\u4EFD\u76EE\u6807\u4E0E\u5F53\u524D\u90E8\u7F72\u4E0D\u4E00\u81F4");
111
+ const saved = path.join(source, key);
112
+ if (!fs.existsSync(saved)) continue;
113
+ const absolute = path.resolve(target);
114
+ if (absolute === path.parse(absolute).root || absolute === path.resolve(ROOT) || fs.lstatSync(saved).isSymbolicLink()) throw new Error("\u4E0D\u5B89\u5168\u7684\u6062\u590D\u76EE\u6807");
115
+ await fs.promises.rm(absolute, { recursive: true, force: true });
116
+ await fs.promises.cp(saved, absolute, { recursive: true, dereference: false });
117
+ }
118
+ return { restored: id };
119
+ }
120
+ async function restoreProgramSnapshot(id, kind) {
121
+ const source = path.join(ROOT, ".sfmc", "backups", id);
122
+ const manifest = readJson(path.join(source, "manifest.json"), { complete: false, targets: {} });
123
+ const targets = snapshotTargets();
124
+ if (!manifest.complete || path.resolve(manifest.targets[kind] ?? "") !== path.resolve(targets[kind])) throw new Error("\u7A0B\u5E8F\u5907\u4EFD\u4E0D\u5B8C\u6574\u6216\u76EE\u6807\u4E0D\u5339\u914D");
125
+ const saved = path.join(source, kind);
126
+ if (kind === "modules") {
127
+ if (!fs.existsSync(saved)) return;
128
+ await fs.promises.rm(targets.modules, { recursive: true, force: true });
129
+ await fs.promises.cp(saved, targets.modules, { recursive: true, dereference: false });
130
+ return;
131
+ }
132
+ const cfg = readJson(path.join(ROOT, "configs", "bds_updater.json"), {});
133
+ const preserved = /* @__PURE__ */ new Set(["worlds", "config", "server.properties", "allowlist.json", "whitelist.json", "permissions.json", ...(cfg.preserve ?? []).map((name) => name.split(/[\\/]/)[0])]);
134
+ if (!fs.existsSync(saved)) throw new Error("\u7F3A\u5C11\u539F BDS \u7A0B\u5E8F\u5907\u4EFD");
135
+ const target = path.resolve(targets.bds);
136
+ if (target === path.parse(target).root || target === path.resolve(ROOT)) throw new Error("\u4E0D\u5B89\u5168\u7684\u7A0B\u5E8F\u56DE\u9000\u76EE\u6807");
137
+ fs.mkdirSync(target, { recursive: true });
138
+ for (const entry of fs.readdirSync(target)) if (!preserved.has(entry)) await fs.promises.rm(path.join(target, entry), { recursive: true, force: true });
139
+ for (const entry of fs.readdirSync(saved)) if (!preserved.has(entry)) await fs.promises.cp(path.join(saved, entry), path.join(target, entry), { recursive: true, dereference: false });
140
+ }
141
+ async function withStoppedServices(context, names, action, backup = true, rollbackPrograms) {
142
+ const running = await stopForMaintenance(names, context.phase);
143
+ try {
144
+ if (backup) await createSnapshot(context.id, context.phase);
145
+ context.phase("execute");
146
+ const result = await action();
147
+ context.phase("verify");
148
+ await restoreRunning(running, context.phase);
149
+ return result;
150
+ } catch (error) {
151
+ for (const name of [...START_ORDER].reverse().filter((name2) => running.includes(name2))) await services[name].stop(false, false).catch(() => {
152
+ });
153
+ let programRolledBack = false;
154
+ let rollbackError;
155
+ if (rollbackPrograms) {
156
+ context.phase("rollback-program", "\u56DE\u9000\u7A0B\u5E8F\u7248\u672C\uFF0C\u4FDD\u7559\u5F53\u524D\u73A9\u5BB6\u6570\u636E");
157
+ try {
158
+ await restoreProgramSnapshot(context.id, rollbackPrograms);
159
+ programRolledBack = true;
160
+ } catch (failure) {
161
+ rollbackError = String(failure);
162
+ }
163
+ }
164
+ throw Object.assign(new Error(`\u7EF4\u62A4\u5931\u8D25\uFF0C\u670D\u52A1\u4FDD\u6301\u505C\u6B62\uFF1B\u5907\u4EFD\u7F16\u53F7 ${context.id}: ${error instanceof Error ? error.message : String(error)}`), { code: "io", details: { backupId: context.id, programRolledBack, dataRestoreRequired: Boolean(rollbackPrograms), ...rollbackError ? { rollbackError } : {}, ...error.details ?? {} } });
165
+ }
166
+ }
167
+ async function launchPlatformUpdate(store, context, target) {
168
+ if (isMonorepoLayout(ROOT)) throw new Error("\u6E90\u7801\u90E8\u7F72\u4E0D\u8986\u76D6\u5DE5\u4F5C\u533A\uFF1B\u8BF7\u4F7F\u7528\u6B63\u5E38\u5F00\u53D1\u53D1\u5E03\u6D41\u7A0B");
169
+ if (!/^\d+\.\d+\.\d+$/.test(target)) throw new Error("\u9700\u8981\u56FA\u5B9A\u7A33\u5B9A\u7248\u672C");
170
+ const rows = await runtimeRows();
171
+ if (rows.some((row) => row.running && row.ownership === "external")) throw new Error("\u5916\u90E8\u670D\u52A1\u5FC5\u987B\u5148\u901A\u8FC7\u539F\u7BA1\u7406\u5668\u4F18\u96C5\u505C\u6B62");
172
+ const request = path.join(ROOT, ".sfmc", "operations", `${context.id}.request.json`);
173
+ const previous = readJson(path.join(ROOT, ".sfmc", "runtime", "active.json"), { entry: fs.existsSync(path.join(ROOT, "node_modules", "@sfmc-bds", "sfmc", "bin", "sfmc.mjs")) ? path.join(ROOT, "node_modules", "@sfmc-bds", "sfmc", "bin", "sfmc.mjs") : process.env.SFMC_DAEMON_ENTRY, version: platformVersion() });
174
+ atomicJson(request, { root: ROOT, id: context.id, target, running: rows.filter((row) => row.running).map((row) => row.name), previous });
175
+ const entry = fileURLToPath(new URL("./platform-worker.js", import.meta.url));
176
+ fs.mkdirSync(path.join(ROOT, ".sfmc", "logs"), { recursive: true });
177
+ const fd = fs.openSync(path.join(ROOT, ".sfmc", "logs", "maintenance.log"), "a");
178
+ const child = spawn(process.execPath, [entry, request], { cwd: ROOT, env: { ...process.env, SFMC_ROOT: ROOT }, detached: true, windowsHide: true, stdio: ["ignore", fd, fd] });
179
+ fs.closeSync(fd);
180
+ await new Promise((resolve, reject) => {
181
+ child.once("spawn", resolve);
182
+ child.once("error", reject);
183
+ });
184
+ child.unref();
185
+ const record = store.get(context.id);
186
+ record.status = "running";
187
+ if (child.pid) record.workerPid = child.pid;
188
+ store.save(record);
189
+ return { detached: true };
190
+ }
191
+ async function runPnpm(args, cwd) {
192
+ const script = process.env.SFMC_PNPM_ENTRY;
193
+ if (script) return runProcess(process.execPath, [script, ...args], cwd);
194
+ const command = process.platform === "win32" ? "pnpm.cmd" : "pnpm";
195
+ const crossSpawn = require2("cross-spawn");
196
+ await new Promise((resolve, reject) => {
197
+ const child = crossSpawn(command, args, { cwd, env: process.env, windowsHide: true, stdio: "ignore" });
198
+ child.on("error", reject);
199
+ child.on("close", (code) => code === 0 ? resolve() : reject(new Error(`pnpm \u6267\u884C\u5931\u8D25 (${code})`)));
200
+ });
201
+ }
202
+ export {
203
+ attachmentPlan,
204
+ createSnapshot,
205
+ launchPlatformUpdate,
206
+ platformVersion,
207
+ restoreRunning,
208
+ restoreSnapshot,
209
+ runPnpm,
210
+ stopForMaintenance,
211
+ verifyRunning,
212
+ withStoppedServices
213
+ };
214
+ //# sourceMappingURL=maintenance.js.map