@sfmc-bds/bds-tools 0.2.0-beta.0 → 0.2.0-beta.10

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 (108) hide show
  1. package/LICENSE +663 -0
  2. package/dist/bds-manager.d.ts +16 -6
  3. package/dist/bds-manager.d.ts.map +1 -1
  4. package/dist/bds-manager.js +40 -65
  5. package/dist/bds-manager.js.map +2 -2
  6. package/dist/check-update.d.ts.map +1 -1
  7. package/dist/check-update.js +15 -17
  8. package/dist/check-update.js.map +2 -2
  9. package/dist/cli-pack-manager.js +7 -3
  10. package/dist/cli-pack-manager.js.map +2 -2
  11. package/dist/fsx.d.ts +6 -1
  12. package/dist/fsx.d.ts.map +1 -1
  13. package/dist/fsx.js +9 -1
  14. package/dist/fsx.js.map +2 -2
  15. package/dist/host-platform.d.ts +25 -0
  16. package/dist/host-platform.d.ts.map +1 -0
  17. package/dist/host-platform.js +50 -0
  18. package/dist/host-platform.js.map +7 -0
  19. package/dist/http.d.ts +49 -11
  20. package/dist/http.d.ts.map +1 -1
  21. package/dist/http.js.map +2 -2
  22. package/dist/log.d.ts +2 -3
  23. package/dist/log.d.ts.map +1 -1
  24. package/dist/log.js +0 -1
  25. package/dist/log.js.map +2 -2
  26. package/dist/pack-manager-lifecycle.test.js +252 -0
  27. package/dist/pack-manager-lifecycle.test.js.map +7 -0
  28. package/dist/pack-manager.d.ts +24 -29
  29. package/dist/pack-manager.d.ts.map +1 -1
  30. package/dist/pack-manager.js +11 -4
  31. package/dist/pack-manager.js.map +2 -2
  32. package/dist/pack-update/bindings.d.ts +16 -0
  33. package/dist/pack-update/bindings.d.ts.map +1 -0
  34. package/dist/pack-update/bindings.js +69 -0
  35. package/dist/pack-update/bindings.js.map +7 -0
  36. package/dist/pack-update/config.d.ts +16 -0
  37. package/dist/pack-update/config.d.ts.map +1 -0
  38. package/dist/pack-update/config.js +146 -0
  39. package/dist/pack-update/config.js.map +7 -0
  40. package/dist/pack-update/config.test.js +75 -0
  41. package/dist/pack-update/config.test.js.map +7 -0
  42. package/dist/pack-update/deps.d.ts +28 -0
  43. package/dist/pack-update/deps.d.ts.map +1 -0
  44. package/dist/pack-update/deps.js +20 -0
  45. package/dist/pack-update/deps.js.map +7 -0
  46. package/dist/pack-update/index.d.ts +55 -0
  47. package/dist/pack-update/index.d.ts.map +1 -0
  48. package/dist/pack-update/index.js +72 -0
  49. package/dist/pack-update/index.js.map +7 -0
  50. package/dist/pack-update/providers/curseforge.d.ts +23 -0
  51. package/dist/pack-update/providers/curseforge.d.ts.map +1 -0
  52. package/dist/pack-update/providers/curseforge.js +229 -0
  53. package/dist/pack-update/providers/curseforge.js.map +7 -0
  54. package/dist/pack-update/providers/index.d.ts +16 -0
  55. package/dist/pack-update/providers/index.d.ts.map +1 -0
  56. package/dist/pack-update/providers/index.js +37 -0
  57. package/dist/pack-update/providers/index.js.map +7 -0
  58. package/dist/pack-update/service.d.ts +28 -0
  59. package/dist/pack-update/service.d.ts.map +1 -0
  60. package/dist/pack-update/service.js +570 -0
  61. package/dist/pack-update/service.js.map +7 -0
  62. package/dist/pack-update/types.d.ts +126 -0
  63. package/dist/pack-update/types.d.ts.map +1 -0
  64. package/dist/pack-update/types.js +8 -0
  65. package/dist/pack-update/types.js.map +7 -0
  66. package/dist/pack-update/version-policy.d.ts +81 -0
  67. package/dist/pack-update/version-policy.d.ts.map +1 -0
  68. package/dist/pack-update/version-policy.js +210 -0
  69. package/dist/pack-update/version-policy.js.map +7 -0
  70. package/dist/pack-update/version-policy.test.js +137 -0
  71. package/dist/pack-update/version-policy.test.js.map +7 -0
  72. package/dist/paths.d.ts +1 -1
  73. package/dist/paths.d.ts.map +1 -1
  74. package/dist/paths.js +3 -5
  75. package/dist/paths.js.map +2 -2
  76. package/dist/process-probe.d.ts +34 -0
  77. package/dist/process-probe.d.ts.map +1 -0
  78. package/dist/process-probe.js +152 -0
  79. package/dist/process-probe.js.map +7 -0
  80. package/dist/process-probe.test.js +76 -0
  81. package/dist/process-probe.test.js.map +7 -0
  82. package/dist/qq-events-notify.d.ts +16 -0
  83. package/dist/qq-events-notify.d.ts.map +1 -0
  84. package/dist/qq-events-notify.js +70 -0
  85. package/dist/qq-events-notify.js.map +7 -0
  86. package/dist/qqutil.d.ts +10 -5
  87. package/dist/qqutil.d.ts.map +1 -1
  88. package/dist/qqutil.js +49 -8
  89. package/dist/qqutil.js.map +2 -2
  90. package/dist/update-result.d.ts +8 -2
  91. package/dist/update-result.d.ts.map +1 -1
  92. package/dist/update-result.js +7 -5
  93. package/dist/update-result.js.map +2 -2
  94. package/dist/upstream.d.ts +18 -9
  95. package/dist/upstream.d.ts.map +1 -1
  96. package/dist/upstream.js +8 -7
  97. package/dist/upstream.js.map +2 -2
  98. package/dist/version.js +2 -2
  99. package/dist/version.js.map +2 -2
  100. package/dist/world-packs.d.ts +158 -5
  101. package/dist/world-packs.d.ts.map +1 -1
  102. package/dist/world-packs.js +294 -48
  103. package/dist/world-packs.js.map +2 -2
  104. package/dist/world-packs.test.js +676 -0
  105. package/dist/world-packs.test.js.map +7 -0
  106. package/dist/zipx.test.js +36 -0
  107. package/dist/zipx.test.js.map +7 -0
  108. package/package.json +43 -39
package/dist/qqutil.js CHANGED
@@ -3,6 +3,7 @@ import {
3
3
  modulePath,
4
4
  readJson
5
5
  } from "@sfmc-bds/sdk/node/config";
6
+ import { sendGroupTextMessage } from "@sfmc-bds/sdk/node/qq-official";
6
7
  import http from "node:http";
7
8
  import { log } from "./log.js";
8
9
  import { ROOT_DIR } from "./paths.js";
@@ -19,15 +20,18 @@ function isQqBridgeEnabled() {
19
20
  const mod = catalog.modules?.find((m) => m.id === "qq-bridge" || m.configKey === "qq_bridge");
20
21
  return mod ? lock.modules?.[mod.id ?? ""]?.enabled === true : false;
21
22
  }
23
+ function isOfficialBackend(cfg) {
24
+ return (cfg.qq_backend ?? "official") === "official";
25
+ }
22
26
  function sendToLLBot(payload, timeoutMs = 5e3) {
23
27
  const cfg = getConfig();
24
- const url = new URL(cfg.llbot_http || "http://127.0.0.1:3000");
28
+ const url = new URL(cfg.llbot_http || "http://127.0.0.1:3004");
25
29
  const data = JSON.stringify(payload);
26
30
  return new Promise((resolve, reject) => {
27
31
  const req = http.request(
28
32
  {
29
33
  hostname: url.hostname,
30
- port: url.port || 3e3,
34
+ port: url.port || 3004,
31
35
  path: "/send_group_msg",
32
36
  method: "POST",
33
37
  headers: {
@@ -60,14 +64,36 @@ async function safeSend(label, fn) {
60
64
  log.warn(`[QQ] ${label} \u5931\u8D25: ${e.message}`);
61
65
  }
62
66
  }
67
+ async function sendOfficialText(text) {
68
+ const cfg = getConfig();
69
+ const appId = String(cfg.qq_app_id ?? "");
70
+ const appSecret = String(cfg.qq_app_secret ?? "");
71
+ const groupOpenid = String(cfg.qq_group_openid ?? "");
72
+ if (!appId || !appSecret || !groupOpenid) {
73
+ throw new Error("\u5B98\u65B9\u540E\u7AEF\u7F3A\u5C11 qq_app_id / qq_app_secret / qq_group_openid");
74
+ }
75
+ const result = await sendGroupTextMessage(
76
+ { appId, appSecret, sandbox: cfg.qq_sandbox === true },
77
+ { groupOpenid, content: text }
78
+ );
79
+ if (!result.ok) throw new Error(result.error);
80
+ }
63
81
  async function sendText(text) {
64
82
  const cfg = getConfig();
65
- if (!isQqBridgeEnabled() || !cfg.qq_group_id) {
66
- log.warn("[QQ] qq-bridge \u672A\u542F\u7528\u6216 qq_group_id \u7F3A\u5931");
83
+ if (!isQqBridgeEnabled()) {
84
+ log.warn("[QQ] qq-bridge \u672A\u542F\u7528");
85
+ return;
86
+ }
87
+ if (isOfficialBackend(cfg)) {
88
+ await safeSend("sendText(official)", () => sendOfficialText(text));
89
+ return;
90
+ }
91
+ if (!cfg.qq_group_id) {
92
+ log.warn("[QQ] qq_group_id \u7F3A\u5931");
67
93
  return;
68
94
  }
69
95
  await safeSend(
70
- "sendText",
96
+ "sendText(llbot)",
71
97
  () => sendToLLBot({
72
98
  group_id: parseInt(cfg.qq_group_id ?? "0", 10),
73
99
  message: [{ type: "text", data: { text } }]
@@ -76,12 +102,27 @@ async function sendText(text) {
76
102
  }
77
103
  async function sendMixed(segments) {
78
104
  const cfg = getConfig();
79
- if (!isQqBridgeEnabled() || !cfg.qq_group_id) {
80
- log.warn("[QQ] qq-bridge \u672A\u542F\u7528\u6216 qq_group_id \u7F3A\u5931");
105
+ if (!isQqBridgeEnabled()) {
106
+ log.warn("[QQ] qq-bridge \u672A\u542F\u7528");
107
+ return;
108
+ }
109
+ if (isOfficialBackend(cfg)) {
110
+ const text = segments.map((seg) => {
111
+ const s = seg;
112
+ if (s?.type === "text") return String(s.data?.text ?? "");
113
+ if (s?.type === "image") return "[\u56FE\u7247]";
114
+ return "";
115
+ }).join("").trim();
116
+ if (!text) return;
117
+ await safeSend("sendMixed(official)", () => sendOfficialText(text));
118
+ return;
119
+ }
120
+ if (!cfg.qq_group_id) {
121
+ log.warn("[QQ] qq_group_id \u7F3A\u5931");
81
122
  return;
82
123
  }
83
124
  await safeSend(
84
- "sendMixed",
125
+ "sendMixed(llbot)",
85
126
  () => sendToLLBot({
86
127
  group_id: parseInt(cfg.qq_group_id ?? "0", 10),
87
128
  message: segments
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/qqutil.ts"],
4
- "sourcesContent": ["/**\n * qqutil.ts \u2014 QQ \u901A\u77E5\u5DE5\u5177 (\u57FA\u4E8E LLBot HTTP OneBot 11)\n *\n * \u6539\u8FDB:\n * - sendTimeout \u63D0\u4F9B\u603B\u8D85\u65F6\uFF0C\u907F\u514D\u901A\u77E5\u53D1\u9001\u6302\u6B7B\u4E3B\u6D41\u7A0B\n * - \u9759\u9ED8\u6A21\u5F0F (\u5931\u8D25\u4E0D\u629B\u51FA)\uFF0C\u4FDD\u8BC1\u4E3B\u6D41\u7A0B\u4E0D\u88AB\u901A\u77E5\u5E72\u6270\n */\n\nimport {\n configPath,\n modulePath,\n readJson,\n type Catalog,\n type ModuleLock,\n type QQBridgeConfig,\n} from \"@sfmc-bds/sdk/node/config\";\nimport http from \"node:http\";\nimport { log } from \"./log.js\";\nimport { ROOT_DIR } from \"./paths.js\";\n\ntype QqConfig = Pick<QQBridgeConfig, \"llbot_http\" | \"qq_group_id\">;\n\nlet cachedCfg: QqConfig | null = null;\nfunction getConfig(): QqConfig {\n if (cachedCfg) return cachedCfg;\n cachedCfg = (readJson<QQBridgeConfig>(configPath(ROOT_DIR, \"qq_config.json\")) ?? {}) as QqConfig;\n return cachedCfg;\n}\n\n/** \u68C0\u67E5 qq-bridge \u6A21\u5757\u662F\u5426\u542F\u7528 */\nexport function isQqBridgeEnabled(): boolean {\n const catalog = readJson<Catalog>(modulePath(ROOT_DIR, \"catalog.json\"));\n const lock = readJson<ModuleLock>(modulePath(ROOT_DIR, \"module-lock.json\"));\n if (!catalog || !lock) return true; // \u6A21\u5757\u76EE\u5F55\u7F3A\u5931\u5219\u4FDD\u5B88\u89C6\u4E3A\u53EF\u7528\n const mod = catalog.modules?.find((m) => m.id === \"qq-bridge\" || m.configKey === \"qq_bridge\") as\n { id?: string } | undefined;\n return mod ? lock.modules?.[mod.id ?? \"\"]?.enabled === true : false;\n}\n\nfunction sendToLLBot(payload: unknown, timeoutMs = 5_000): Promise<void> {\n const cfg = getConfig();\n const url = new URL(cfg.llbot_http || \"http://127.0.0.1:3000\");\n const data = JSON.stringify(payload);\n return new Promise((resolve, reject) => {\n const req = http.request(\n {\n hostname: url.hostname,\n port: url.port || 3000,\n path: \"/send_group_msg\",\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Content-Length\": Buffer.byteLength(data),\n },\n timeout: timeoutMs,\n },\n (res) => {\n let body = \"\";\n res.on(\"data\", (c) => (body += c));\n res.on(\"end\", () => {\n if (res.statusCode === 200) resolve();\n else reject(new Error(`LLBot HTTP ${res.statusCode}: ${body.slice(0, 200)}`));\n });\n }\n );\n req.on(\"timeout\", () => {\n req.destroy(new Error(`LLBot \u8D85\u65F6 ${timeoutMs}ms`));\n });\n req.on(\"error\", reject);\n req.write(data);\n req.end();\n });\n}\n\nasync function safeSend(label: string, fn: () => Promise<void>): Promise<void> {\n try {\n await fn();\n } catch (e) {\n log.warn(`[QQ] ${label} \u5931\u8D25: ${(e as Error).message}`);\n }\n}\n\nexport async function sendText(text: string): Promise<void> {\n const cfg = getConfig();\n if (!isQqBridgeEnabled() || !cfg.qq_group_id) {\n log.warn(\"[QQ] qq-bridge \u672A\u542F\u7528\u6216 qq_group_id \u7F3A\u5931\");\n return;\n }\n await safeSend(\"sendText\", () =>\n sendToLLBot({\n group_id: parseInt(cfg.qq_group_id ?? \"0\", 10),\n message: [{ type: \"text\", data: { text } }],\n })\n );\n}\n\nexport async function sendMixed(segments: unknown[]): Promise<void> {\n const cfg = getConfig();\n if (!isQqBridgeEnabled() || !cfg.qq_group_id) {\n log.warn(\"[QQ] qq-bridge \u672A\u542F\u7528\u6216 qq_group_id \u7F3A\u5931\");\n return;\n }\n await safeSend(\"sendMixed\", () =>\n sendToLLBot({\n group_id: parseInt(cfg.qq_group_id ?? \"0\", 10),\n message: segments,\n })\n );\n}\n\nexport async function sendWithImage(text: string, base64Img: string): Promise<void> {\n const segments: unknown[] = [];\n if (text) segments.push({ type: \"text\", data: { text } });\n if (base64Img) segments.push({ type: \"image\", data: { file: `base64://${base64Img}` } });\n if (segments.length === 0) return;\n await sendMixed(segments);\n}\n"],
5
- "mappings": "AAQA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AACP,OAAO,UAAU;AACjB,SAAS,WAAW;AACpB,SAAS,gBAAgB;AAIzB,IAAI,YAA6B;AACjC,SAAS,YAAsB;AAC7B,MAAI,UAAW,QAAO;AACtB,cAAa,SAAyB,WAAW,UAAU,gBAAgB,CAAC,KAAK,CAAC;AAClF,SAAO;AACT;AAGO,SAAS,oBAA6B;AAC3C,QAAM,UAAU,SAAkB,WAAW,UAAU,cAAc,CAAC;AACtE,QAAM,OAAO,SAAqB,WAAW,UAAU,kBAAkB,CAAC;AAC1E,MAAI,CAAC,WAAW,CAAC,KAAM,QAAO;AAC9B,QAAM,MAAM,QAAQ,SAAS,KAAK,CAAC,MAAM,EAAE,OAAO,eAAe,EAAE,cAAc,WAAW;AAE5F,SAAO,MAAM,KAAK,UAAU,IAAI,MAAM,EAAE,GAAG,YAAY,OAAO;AAChE;AAEA,SAAS,YAAY,SAAkB,YAAY,KAAsB;AACvE,QAAM,MAAM,UAAU;AACtB,QAAM,MAAM,IAAI,IAAI,IAAI,cAAc,uBAAuB;AAC7D,QAAM,OAAO,KAAK,UAAU,OAAO;AACnC,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,MAAM,KAAK;AAAA,MACf;AAAA,QACE,UAAU,IAAI;AAAA,QACd,MAAM,IAAI,QAAQ;AAAA,QAClB,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,kBAAkB,OAAO,WAAW,IAAI;AAAA,QAC1C;AAAA,QACA,SAAS;AAAA,MACX;AAAA,MACA,CAAC,QAAQ;AACP,YAAI,OAAO;AACX,YAAI,GAAG,QAAQ,CAAC,MAAO,QAAQ,CAAE;AACjC,YAAI,GAAG,OAAO,MAAM;AAClB,cAAI,IAAI,eAAe,IAAK,SAAQ;AAAA,cAC/B,QAAO,IAAI,MAAM,cAAc,IAAI,UAAU,KAAK,KAAK,MAAM,GAAG,GAAG,CAAC,EAAE,CAAC;AAAA,QAC9E,CAAC;AAAA,MACH;AAAA,IACF;AACA,QAAI,GAAG,WAAW,MAAM;AACtB,UAAI,QAAQ,IAAI,MAAM,sBAAY,SAAS,IAAI,CAAC;AAAA,IAClD,CAAC;AACD,QAAI,GAAG,SAAS,MAAM;AACtB,QAAI,MAAM,IAAI;AACd,QAAI,IAAI;AAAA,EACV,CAAC;AACH;AAEA,eAAe,SAAS,OAAe,IAAwC;AAC7E,MAAI;AACF,UAAM,GAAG;AAAA,EACX,SAAS,GAAG;AACV,QAAI,KAAK,QAAQ,KAAK,kBAAS,EAAY,OAAO,EAAE;AAAA,EACtD;AACF;AAEA,eAAsB,SAAS,MAA6B;AAC1D,QAAM,MAAM,UAAU;AACtB,MAAI,CAAC,kBAAkB,KAAK,CAAC,IAAI,aAAa;AAC5C,QAAI,KAAK,kEAAoC;AAC7C;AAAA,EACF;AACA,QAAM;AAAA,IAAS;AAAA,IAAY,MACzB,YAAY;AAAA,MACV,UAAU,SAAS,IAAI,eAAe,KAAK,EAAE;AAAA,MAC7C,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,EAAE,KAAK,EAAE,CAAC;AAAA,IAC5C,CAAC;AAAA,EACH;AACF;AAEA,eAAsB,UAAU,UAAoC;AAClE,QAAM,MAAM,UAAU;AACtB,MAAI,CAAC,kBAAkB,KAAK,CAAC,IAAI,aAAa;AAC5C,QAAI,KAAK,kEAAoC;AAC7C;AAAA,EACF;AACA,QAAM;AAAA,IAAS;AAAA,IAAa,MAC1B,YAAY;AAAA,MACV,UAAU,SAAS,IAAI,eAAe,KAAK,EAAE;AAAA,MAC7C,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AACF;AAEA,eAAsB,cAAc,MAAc,WAAkC;AAClF,QAAM,WAAsB,CAAC;AAC7B,MAAI,KAAM,UAAS,KAAK,EAAE,MAAM,QAAQ,MAAM,EAAE,KAAK,EAAE,CAAC;AACxD,MAAI,UAAW,UAAS,KAAK,EAAE,MAAM,SAAS,MAAM,EAAE,MAAM,YAAY,SAAS,GAAG,EAAE,CAAC;AACvF,MAAI,SAAS,WAAW,EAAG;AAC3B,QAAM,UAAU,QAAQ;AAC1B;",
4
+ "sourcesContent": ["/**\n * qqutil.ts \u2014 BDS \u8FD0\u7EF4\u4E0E\u66F4\u65B0\u901A\u77E5\u63A8\u9001\u5DE5\u5177\uFF08\u652F\u6301\u5B98\u65B9 Bot \u4E0E LLBot \u53CC\u540E\u7AEF\uFF09\n *\n * \u6838\u5FC3\u673A\u5236\uFF1A\n * - \u53CC\u540E\u7AEF\u9002\u914D\uFF1A\u6839\u636E `qq_backend` \u914D\u7F6E\u81EA\u52A8\u5206\u53D1\u81F3\u5B98\u65B9\u5F00\u653E\u5E73\u53F0\u53D1\u7FA4\u63A5\u53E3\u6216 LLBot OneBot HTTP \u63A5\u53E3\n * - \u6545\u969C\u9694\u79BB\uFF1A\u901A\u77E5\u53D1\u9001\u5931\u8D25\u4EC5\u8BB0\u5F55\u544A\u8B66\u65E5\u5FD7\u5E76\u4E0D\u4E2D\u65AD\u4E3B\u6D41\u7A0B\uFF0C\u786E\u4FDD\u4E3B\u670D\u52A1\u8FD0\u7EF4\u6B63\u5E38\u6267\u884C\n * - \u53D1\u9001\u8D85\u65F6\u7194\u65AD\uFF1A\u8BBE\u7F6E\u8BF7\u6C42\u8D85\u65F6\u65F6\u95F4\uFF0C\u9632\u6B62\u5916\u90E8\u7F51\u7EDC\u54CD\u5E94\u5EF6\u8FDF\u5835\u585E BDS \u542F\u52A8\u4E0E\u66F4\u65B0\n */\n\nimport {\n configPath,\n modulePath,\n readJson,\n type Catalog,\n type ModuleLock,\n type QQBridgeConfig,\n} from \"@sfmc-bds/sdk/node/config\";\nimport { sendGroupTextMessage } from \"@sfmc-bds/sdk/node/qq-official\";\nimport http from \"node:http\";\nimport { log } from \"./log.js\";\nimport { ROOT_DIR } from \"./paths.js\";\n\ntype QqConfig = Pick<\n QQBridgeConfig,\n | \"llbot_http\"\n | \"qq_group_id\"\n | \"qq_backend\"\n | \"qq_app_id\"\n | \"qq_app_secret\"\n | \"qq_sandbox\"\n | \"qq_group_openid\"\n>;\n\nlet cachedCfg: QqConfig | null = null;\nfunction getConfig(): QqConfig {\n if (cachedCfg) return cachedCfg;\n cachedCfg = (readJson<QQBridgeConfig>(configPath(ROOT_DIR, \"qq_config.json\")) ?? {}) as QqConfig;\n return cachedCfg;\n}\n\n/**\n * \u68C0\u67E5\u5E73\u53F0\u4E2D `qq-bridge` \u6A21\u5757\u662F\u5426\u5904\u4E8E\u542F\u7528\u72B6\u6001\u3002\n *\n * @returns \u82E5\u542F\u7528\u8FD4\u56DE `true`\uFF0C\u5426\u5219\u8FD4\u56DE `false`\u3002\n */\nexport function isQqBridgeEnabled(): boolean {\n\n const catalog = readJson<Catalog>(modulePath(ROOT_DIR, \"catalog.json\"));\n const lock = readJson<ModuleLock>(modulePath(ROOT_DIR, \"module-lock.json\"));\n if (!catalog || !lock) return true; // \u6A21\u5757\u76EE\u5F55\u7F3A\u5931\u5219\u4FDD\u5B88\u89C6\u4E3A\u53EF\u7528\n const mod = catalog.modules?.find((m) => m.id === \"qq-bridge\" || m.configKey === \"qq_bridge\");\n return mod ? lock.modules?.[mod.id ?? \"\"]?.enabled === true : false;\n}\n\nfunction isOfficialBackend(cfg: QqConfig): boolean {\n return (cfg.qq_backend ?? \"official\") === \"official\";\n}\n\nfunction sendToLLBot(payload: unknown, timeoutMs = 5_000): Promise<void> {\n const cfg = getConfig();\n const url = new URL(cfg.llbot_http || \"http://127.0.0.1:3004\");\n const data = JSON.stringify(payload);\n return new Promise((resolve, reject) => {\n const req = http.request(\n {\n hostname: url.hostname,\n port: url.port || 3004,\n path: \"/send_group_msg\",\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Content-Length\": Buffer.byteLength(data),\n },\n timeout: timeoutMs,\n },\n (res) => {\n let body = \"\";\n res.on(\"data\", (c) => (body += c));\n res.on(\"end\", () => {\n if (res.statusCode === 200) resolve();\n else reject(new Error(`LLBot HTTP ${res.statusCode}: ${body.slice(0, 200)}`));\n });\n }\n );\n req.on(\"timeout\", () => {\n req.destroy(new Error(`LLBot \u8D85\u65F6 ${timeoutMs}ms`));\n });\n req.on(\"error\", reject);\n req.write(data);\n req.end();\n });\n}\n\nasync function safeSend(label: string, fn: () => Promise<void>): Promise<void> {\n try {\n await fn();\n } catch (e) {\n log.warn(`[QQ] ${label} \u5931\u8D25: ${(e as Error).message}`);\n }\n}\n\nasync function sendOfficialText(text: string): Promise<void> {\n const cfg = getConfig();\n const appId = String(cfg.qq_app_id ?? \"\");\n const appSecret = String(cfg.qq_app_secret ?? \"\");\n const groupOpenid = String(cfg.qq_group_openid ?? \"\");\n if (!appId || !appSecret || !groupOpenid) {\n throw new Error(\"\u5B98\u65B9\u540E\u7AEF\u7F3A\u5C11 qq_app_id / qq_app_secret / qq_group_openid\");\n }\n const result = await sendGroupTextMessage(\n { appId, appSecret, sandbox: cfg.qq_sandbox === true },\n { groupOpenid, content: text }\n );\n if (!result.ok) throw new Error(result.error);\n}\n\nexport async function sendText(text: string): Promise<void> {\n const cfg = getConfig();\n if (!isQqBridgeEnabled()) {\n log.warn(\"[QQ] qq-bridge \u672A\u542F\u7528\");\n return;\n }\n if (isOfficialBackend(cfg)) {\n await safeSend(\"sendText(official)\", () => sendOfficialText(text));\n return;\n }\n if (!cfg.qq_group_id) {\n log.warn(\"[QQ] qq_group_id \u7F3A\u5931\");\n return;\n }\n await safeSend(\"sendText(llbot)\", () =>\n sendToLLBot({\n group_id: parseInt(cfg.qq_group_id ?? \"0\", 10),\n message: [{ type: \"text\", data: { text } }],\n })\n );\n}\n\nexport async function sendMixed(segments: unknown[]): Promise<void> {\n const cfg = getConfig();\n if (!isQqBridgeEnabled()) {\n log.warn(\"[QQ] qq-bridge \u672A\u542F\u7528\");\n return;\n }\n if (isOfficialBackend(cfg)) {\n // \u5B98\u65B9\u8DEF\u5F84\u4EC5\u62FC\u7EAF\u6587\u672C\uFF1B\u56FE\u7247\u6BB5\u964D\u7EA7\u4E3A [\u56FE\u7247]\n const text = segments\n .map((seg) => {\n const s = seg as { type?: string; data?: { text?: string } };\n if (s?.type === \"text\") return String(s.data?.text ?? \"\");\n if (s?.type === \"image\") return \"[\u56FE\u7247]\";\n return \"\";\n })\n .join(\"\")\n .trim();\n if (!text) return;\n await safeSend(\"sendMixed(official)\", () => sendOfficialText(text));\n return;\n }\n if (!cfg.qq_group_id) {\n log.warn(\"[QQ] qq_group_id \u7F3A\u5931\");\n return;\n }\n await safeSend(\"sendMixed(llbot)\", () =>\n sendToLLBot({\n group_id: parseInt(cfg.qq_group_id ?? \"0\", 10),\n message: segments,\n })\n );\n}\n\nexport async function sendWithImage(text: string, base64Img: string): Promise<void> {\n const segments: unknown[] = [];\n if (text) segments.push({ type: \"text\", data: { text } });\n if (base64Img) segments.push({ type: \"image\", data: { file: `base64://${base64Img}` } });\n if (segments.length === 0) return;\n await sendMixed(segments);\n}\n\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -2,11 +2,17 @@
2
2
  * update-result.ts — BDS updater ↔ sfmc 监督器机器可读结果契约(DRY / DIP)
3
3
  *
4
4
  * 勿依赖本地化日志文案判断「是否真正部署」;一律解析本标记。
5
+ * 写出走统一 Logger(bare stdout),避免 console.log 旁路。
5
6
  */
7
+ import type { Logger } from "@sfmc-bds/sdk/logs";
6
8
  export declare const UPDATE_RESULT_PREFIX = "SFMC_UPDATE_RESULT=";
7
9
  export type UpdateResultKind = "uptodate" | "skipped" | "check-only" | "deployed";
8
- /** stdout 打印机器标记(供父进程捕获)。 */
9
- export declare function emitUpdateResult(kind: UpdateResultKind): void;
10
+ type InfoLogger = Pick<Logger, "info">;
11
+ /** logger 打印机器标记(bare stdout 下仍为可解析纯文本)。 */
12
+ export declare function emitUpdateResult(kind: UpdateResultKind, log: InfoLogger): void;
13
+ /** KEY=value 机器行(CURRENT/LATEST/CHANNEL/URLS 等) */
14
+ export declare function emitUpdateKv(key: string, value: string, log: InfoLogger): void;
10
15
  /** 从 updater 合并输出中解析是否完成部署。 */
11
16
  export declare function didUpdateDeploy(out: string): boolean;
17
+ export {};
12
18
  //# sourceMappingURL=update-result.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"update-result.d.ts","sourceRoot":"","sources":["../src/update-result.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,wBAAwB,CAAC;AAE1D,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,SAAS,GAAG,YAAY,GAAG,UAAU,CAAC;AAElF,+BAA+B;AAC/B,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI,CAE7D;AAED,+BAA+B;AAC/B,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAIpD"}
1
+ {"version":3,"file":"update-result.d.ts","sourceRoot":"","sources":["../src/update-result.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAEjD,eAAO,MAAM,oBAAoB,wBAAwB,CAAC;AAE1D,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,SAAS,GAAG,YAAY,GAAG,UAAU,CAAC;AAElF,KAAK,UAAU,GAAG,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEvC,8CAA8C;AAC9C,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,gBAAgB,EAAE,GAAG,EAAE,UAAU,GAAG,IAAI,CAE9E;AAED,mDAAmD;AACnD,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,GAAG,IAAI,CAE9E;AAED,+BAA+B;AAC/B,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEpD"}
@@ -1,15 +1,17 @@
1
1
  const UPDATE_RESULT_PREFIX = "SFMC_UPDATE_RESULT=";
2
- function emitUpdateResult(kind) {
3
- console.log(`${UPDATE_RESULT_PREFIX}${kind}`);
2
+ function emitUpdateResult(kind, log) {
3
+ log.info(`${UPDATE_RESULT_PREFIX}${kind}`);
4
+ }
5
+ function emitUpdateKv(key, value, log) {
6
+ log.info(`${key}=${value}`);
4
7
  }
5
8
  function didUpdateDeploy(out) {
6
- return new RegExp(
7
- `(?:^|\\n)${UPDATE_RESULT_PREFIX}deployed(?:\\r?\\n|$)`
8
- ).test(out);
9
+ return new RegExp(`(?:^|\\n)${UPDATE_RESULT_PREFIX}deployed(?:\\r?\\n|$)`).test(out);
9
10
  }
10
11
  export {
11
12
  UPDATE_RESULT_PREFIX,
12
13
  didUpdateDeploy,
14
+ emitUpdateKv,
13
15
  emitUpdateResult
14
16
  };
15
17
  //# sourceMappingURL=update-result.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/update-result.ts"],
4
- "sourcesContent": ["/**\n * update-result.ts \u2014 BDS updater \u2194 sfmc \u76D1\u7763\u5668\u673A\u5668\u53EF\u8BFB\u7ED3\u679C\u5951\u7EA6\uFF08DRY / DIP\uFF09\n *\n * \u52FF\u4F9D\u8D56\u672C\u5730\u5316\u65E5\u5FD7\u6587\u6848\u5224\u65AD\u300C\u662F\u5426\u771F\u6B63\u90E8\u7F72\u300D\uFF1B\u4E00\u5F8B\u89E3\u6790\u672C\u6807\u8BB0\u3002\n */\nexport const UPDATE_RESULT_PREFIX = \"SFMC_UPDATE_RESULT=\";\n\nexport type UpdateResultKind = \"uptodate\" | \"skipped\" | \"check-only\" | \"deployed\";\n\n/** \u5411 stdout \u6253\u5370\u673A\u5668\u6807\u8BB0\uFF08\u4F9B\u7236\u8FDB\u7A0B\u6355\u83B7\uFF09\u3002 */\nexport function emitUpdateResult(kind: UpdateResultKind): void {\n console.log(`${UPDATE_RESULT_PREFIX}${kind}`);\n}\n\n/** \u4ECE updater \u5408\u5E76\u8F93\u51FA\u4E2D\u89E3\u6790\u662F\u5426\u5B8C\u6210\u90E8\u7F72\u3002 */\nexport function didUpdateDeploy(out: string): boolean {\n return new RegExp(\n `(?:^|\\\\n)${UPDATE_RESULT_PREFIX}deployed(?:\\\\r?\\\\n|$)`\n ).test(out);\n}\n"],
5
- "mappings": "AAKO,MAAM,uBAAuB;AAK7B,SAAS,iBAAiB,MAA8B;AAC7D,UAAQ,IAAI,GAAG,oBAAoB,GAAG,IAAI,EAAE;AAC9C;AAGO,SAAS,gBAAgB,KAAsB;AACpD,SAAO,IAAI;AAAA,IACT,YAAY,oBAAoB;AAAA,EAClC,EAAE,KAAK,GAAG;AACZ;",
4
+ "sourcesContent": ["/**\n * update-result.ts \u2014 BDS updater \u2194 sfmc \u76D1\u7763\u5668\u673A\u5668\u53EF\u8BFB\u7ED3\u679C\u5951\u7EA6\uFF08DRY / DIP\uFF09\n *\n * \u52FF\u4F9D\u8D56\u672C\u5730\u5316\u65E5\u5FD7\u6587\u6848\u5224\u65AD\u300C\u662F\u5426\u771F\u6B63\u90E8\u7F72\u300D\uFF1B\u4E00\u5F8B\u89E3\u6790\u672C\u6807\u8BB0\u3002\n * \u5199\u51FA\u8D70\u7EDF\u4E00 Logger\uFF08bare stdout\uFF09\uFF0C\u907F\u514D console.log \u65C1\u8DEF\u3002\n */\nimport type { Logger } from \"@sfmc-bds/sdk/logs\";\n\nexport const UPDATE_RESULT_PREFIX = \"SFMC_UPDATE_RESULT=\";\n\nexport type UpdateResultKind = \"uptodate\" | \"skipped\" | \"check-only\" | \"deployed\";\n\ntype InfoLogger = Pick<Logger, \"info\">;\n\n/** \u5411 logger \u6253\u5370\u673A\u5668\u6807\u8BB0\uFF08bare stdout \u4E0B\u4ECD\u4E3A\u53EF\u89E3\u6790\u7EAF\u6587\u672C\uFF09\u3002 */\nexport function emitUpdateResult(kind: UpdateResultKind, log: InfoLogger): void {\n log.info(`${UPDATE_RESULT_PREFIX}${kind}`);\n}\n\n/** KEY=value \u673A\u5668\u884C\uFF08CURRENT/LATEST/CHANNEL/URLS \u7B49\uFF09 */\nexport function emitUpdateKv(key: string, value: string, log: InfoLogger): void {\n log.info(`${key}=${value}`);\n}\n\n/** \u4ECE updater \u5408\u5E76\u8F93\u51FA\u4E2D\u89E3\u6790\u662F\u5426\u5B8C\u6210\u90E8\u7F72\u3002 */\nexport function didUpdateDeploy(out: string): boolean {\n return new RegExp(`(?:^|\\\\n)${UPDATE_RESULT_PREFIX}deployed(?:\\\\r?\\\\n|$)`).test(out);\n}\n"],
5
+ "mappings": "AAQO,MAAM,uBAAuB;AAO7B,SAAS,iBAAiB,MAAwB,KAAuB;AAC9E,MAAI,KAAK,GAAG,oBAAoB,GAAG,IAAI,EAAE;AAC3C;AAGO,SAAS,aAAa,KAAa,OAAe,KAAuB;AAC9E,MAAI,KAAK,GAAG,GAAG,IAAI,KAAK,EAAE;AAC5B;AAGO,SAAS,gBAAgB,KAAsB;AACpD,SAAO,IAAI,OAAO,YAAY,oBAAoB,uBAAuB,EAAE,KAAK,GAAG;AACrF;",
6
6
  "names": []
7
7
  }
@@ -1,18 +1,27 @@
1
1
  /**
2
- * upstream.ts — 版本源 / 下载源 / 哈希校验
2
+ * upstream.ts — BDS 上游版本源、多镜像下载与完整性校验
3
3
  *
4
- * 改进:
5
- * - 3 次重试 + 指数退避
6
- * - sha1 / sha256 同时校验
7
- * - 流式校验 + 边下边校验
4
+ * 核心机制:
5
+ * - 多源容灾:官方源与镜像源并行竞争与多轮指数退避重试
6
+ * - 动态 URL 模板渲染:根据操作系统架构与更新通道计算下载地址
7
+ * - 流式哈希校验:支持 SHA-1 与 SHA-256 完整性流式计算校验
8
+ * - 版本白名单兼容门禁:防止自动升级至未验证版本
8
9
  */
10
+ import { type BdsHostOs } from "./host-platform.js";
9
11
  import type { BdsUpdaterConfig, VersionDetails, VersionInfo } from "./types.js";
10
- /** 获取最新版本号 (含 cdn_root),3 次重试 */
11
- export declare function getVersionInfo(cfg: BdsUpdaterConfig, channel: string): Promise<VersionInfo>;
12
+ /**
13
+ * 获取 BDS 最新可用版本信息(支持重试与指数退避)。
14
+ *
15
+ * @param cfg BDS 更新器配置。
16
+ * @param channel 更新通道(如 "release" 或 "preview")。
17
+ * @param hostOs 目标主机操作系统类型。
18
+ * @returns 包含最新版本号与 CDN 根路径的信息对象。
19
+ */
20
+ export declare function getVersionInfo(cfg: BdsUpdaterConfig, channel: string, hostOs?: BdsHostOs): Promise<VersionInfo>;
12
21
  /** 获取 per-version 的下载链接 + 哈希 (fallback GitHub → jsdelivr) */
13
- export declare function fetchVersionDetails(cfg: BdsUpdaterConfig, channel: string, version: string): Promise<VersionDetails>;
22
+ export declare function fetchVersionDetails(cfg: BdsUpdaterConfig, channel: string, version: string, hostOs?: BdsHostOs): Promise<VersionDetails>;
14
23
  /** 候选下载 URL (按优先级) */
15
- export declare function buildDownloadUrls(cfg: BdsUpdaterConfig, channel: string, version: string, details: VersionDetails): string[];
24
+ export declare function buildDownloadUrls(cfg: BdsUpdaterConfig, channel: string, version: string, details: VersionDetails, hostOs?: BdsHostOs): string[];
16
25
  /** 同步 / 异步校验文件哈希 */
17
26
  export declare function verifyFileHash(filePath: string, expectedSha1: string, expectedSha256: string): Promise<boolean>;
18
27
  /** 检查版本兼容性 (白名单) */
@@ -1 +1 @@
1
- {"version":3,"file":"upstream.d.ts","sourceRoot":"","sources":["../src/upstream.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAchF,iCAAiC;AACjC,wBAAsB,cAAc,CAAC,GAAG,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CA8BjG;AAED,6DAA6D;AAC7D,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,gBAAgB,EACrB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,cAAc,CAAC,CA2BzB;AAED,sBAAsB;AACtB,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,gBAAgB,EACrB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,cAAc,GACtB,MAAM,EAAE,CAeV;AAED,oBAAoB;AACpB,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAiBrH;AAED,oBAAoB;AACpB,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAInF"}
1
+ {"version":3,"file":"upstream.d.ts","sourceRoot":"","sources":["../src/upstream.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,EAAqD,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAGvG,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAchF;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAElC,GAAG,EAAE,gBAAgB,EACrB,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,SAAuB,GAC9B,OAAO,CAAC,WAAW,CAAC,CA8BtB;AAED,6DAA6D;AAC7D,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,gBAAgB,EACrB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,SAAuB,GAC9B,OAAO,CAAC,cAAc,CAAC,CA2BzB;AAED,sBAAsB;AACtB,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,gBAAgB,EACrB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,cAAc,EACvB,MAAM,GAAE,SAAuB,GAC9B,MAAM,EAAE,CAYV;AAED,oBAAoB;AACpB,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAiBrH;AAED,oBAAoB;AACpB,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAInF"}
package/dist/upstream.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import crypto from "node:crypto";
2
2
  import fs from "node:fs";
3
+ import { bdsCdnZipSuffix, bdsDetailsPlatformKey, bdsHostOs } from "./host-platform.js";
3
4
  import { fetchJsonWithFallback } from "./http.js";
4
5
  import { log } from "./log.js";
5
6
  import { toVer3 } from "./version.js";
@@ -12,7 +13,7 @@ function resolveTemplate(tpl, vars) {
12
13
  }
13
14
  return s;
14
15
  }
15
- async function getVersionInfo(cfg, channel) {
16
+ async function getVersionInfo(cfg, channel, hostOs = bdsHostOs()) {
16
17
  const api = cfg.version_versions || process.env["BDS_VERSIONS_API"] || DEFAULT_VERSIONS_API;
17
18
  const mirror = cfg.version_versions_mirror || process.env["BDS_VERSIONS_MIRROR"] || DEFAULT_VERSIONS_MIRROR;
18
19
  const sources = [api, mirror].filter(Boolean);
@@ -26,7 +27,7 @@ async function getVersionInfo(cfg, channel) {
26
27
  const entry = channel === "preview" ? json.preview : json.release;
27
28
  ver = entry?.latest;
28
29
  } else {
29
- const platform = json.windows || json.linux;
30
+ const platform = hostOs === "windows" ? json.windows || json.linux : json.linux || json.windows;
30
31
  ver = channel === "preview" ? platform?.preview : platform?.stable;
31
32
  }
32
33
  if (!ver) throw new Error("\u672A\u627E\u5230\u6700\u65B0\u7248\u672C\u53F7");
@@ -42,8 +43,8 @@ async function getVersionInfo(cfg, channel) {
42
43
  }
43
44
  throw lastErr instanceof Error ? lastErr : new Error("\u7248\u672C\u53F7\u83B7\u53D6\u5931\u8D25");
44
45
  }
45
- async function fetchVersionDetails(cfg, channel, version) {
46
- const platform = channel === "preview" ? "windows_preview" : "windows";
46
+ async function fetchVersionDetails(cfg, channel, version, hostOs = bdsHostOs()) {
47
+ const platform = bdsDetailsPlatformKey(channel, hostOs);
47
48
  const ch = channel === "preview" ? "preview" : "release";
48
49
  const vars = { version, ver3: toVer3(version), platform, channel: ch };
49
50
  const detailsTpl = cfg.version_details || "https://raw.githubusercontent.com/Bedrock-OSS/BDS-Versions/main/{platform}/{version}.json";
@@ -52,7 +53,7 @@ async function fetchVersionDetails(cfg, channel, version) {
52
53
  const json = await fetchJsonWithFallback(sources);
53
54
  const mode = cfg.version_mode || "bedrock-oss";
54
55
  if (mode === "endstone") {
55
- const bw = json.binary?.windows;
56
+ const bw = hostOs === "windows" ? json.binary?.windows : json.binary?.linux;
56
57
  return {
57
58
  downloadUrl: bw?.url ?? "",
58
59
  sha1: "",
@@ -67,7 +68,7 @@ async function fetchVersionDetails(cfg, channel, version) {
67
68
  size: json.size_in_bytes ?? 0
68
69
  };
69
70
  }
70
- function buildDownloadUrls(cfg, channel, version, details) {
71
+ function buildDownloadUrls(cfg, channel, version, details, hostOs = bdsHostOs()) {
71
72
  const urls = [];
72
73
  if (cfg.download_mirror) {
73
74
  const mirrorVer = channel === "preview" ? `${version}-preview` : version;
@@ -75,7 +76,7 @@ function buildDownloadUrls(cfg, channel, version, details) {
75
76
  }
76
77
  if (details.downloadUrl) urls.push(details.downloadUrl);
77
78
  const cdnRoot = cfg.cdn_root || "https://www.minecraft.net/bedrockdedicatedserver";
78
- const suffix = channel === "preview" ? `/bin-win-preview/bedrock-server-${version}-preview.zip` : `/bin-win/bedrock-server-${version}.zip`;
79
+ const suffix = bdsCdnZipSuffix(channel, version, hostOs);
79
80
  urls.push(cdnRoot + suffix);
80
81
  urls.push(`https://minecraft.azureedge.net${suffix}`);
81
82
  return [...new Set(urls)];
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/upstream.ts"],
4
- "sourcesContent": ["/**\n * upstream.ts \u2014 \u7248\u672C\u6E90 / \u4E0B\u8F7D\u6E90 / \u54C8\u5E0C\u6821\u9A8C\n *\n * \u6539\u8FDB:\n * - 3 \u6B21\u91CD\u8BD5 + \u6307\u6570\u9000\u907F\n * - sha1 / sha256 \u540C\u65F6\u6821\u9A8C\n * - \u6D41\u5F0F\u6821\u9A8C + \u8FB9\u4E0B\u8FB9\u6821\u9A8C\n */\n\nimport crypto from \"node:crypto\";\nimport fs from \"node:fs\";\nimport { fetchJsonWithFallback } from \"./http.js\";\nimport { log } from \"./log.js\";\nimport type { BdsUpdaterConfig, VersionDetails, VersionInfo } from \"./types.js\";\nimport { toVer3 } from \"./version.js\";\n\nconst DEFAULT_VERSIONS_API = \"https://raw.githubusercontent.com/Bedrock-OSS/BDS-Versions/main/versions.json\";\nconst DEFAULT_VERSIONS_MIRROR = \"https://cdn.jsdelivr.net/gh/Bedrock-OSS/BDS-Versions@main/versions.json\";\n\nfunction resolveTemplate(tpl: string, vars: Record<string, string>): string {\n let s = tpl;\n for (const [k, v] of Object.entries(vars)) {\n s = s.replace(new RegExp(`\\\\{${k}\\\\}`, \"g\"), v);\n }\n return s;\n}\n\n/** \u83B7\u53D6\u6700\u65B0\u7248\u672C\u53F7 (\u542B cdn_root)\uFF0C3 \u6B21\u91CD\u8BD5 */\nexport async function getVersionInfo(cfg: BdsUpdaterConfig, channel: string): Promise<VersionInfo> {\n const api = cfg.version_versions || process.env[\"BDS_VERSIONS_API\"] || DEFAULT_VERSIONS_API;\n const mirror = cfg.version_versions_mirror || process.env[\"BDS_VERSIONS_MIRROR\"] || DEFAULT_VERSIONS_MIRROR;\n const sources = [api, mirror].filter(Boolean);\n const versionMode = cfg.version_mode || \"bedrock-oss\";\n\n let lastErr: unknown;\n for (let attempt = 1; attempt <= 3; attempt++) {\n try {\n const json = await fetchJsonWithFallback<any>(sources);\n let ver: string | undefined;\n if (versionMode === \"endstone\") {\n const entry = channel === \"preview\" ? json.preview : json.release;\n ver = entry?.latest;\n } else {\n const platform = json.windows || json.linux;\n ver = channel === \"preview\" ? platform?.preview : platform?.stable;\n }\n if (!ver) throw new Error(\"\u672A\u627E\u5230\u6700\u65B0\u7248\u672C\u53F7\");\n return { version: ver, cdnRoot: json.cdn_root ?? \"\" };\n } catch (e) {\n lastErr = e;\n if (attempt < 3) {\n const wait = attempt * 3000;\n log.warn(`\u7248\u672C\u53F7\u83B7\u53D6\u5931\u8D25 (${attempt}/3): ${(e as Error).message}\uFF0C${wait / 1000}s \u540E\u91CD\u8BD5...`);\n await new Promise((r) => setTimeout(r, wait));\n }\n }\n }\n throw lastErr instanceof Error ? lastErr : new Error(\"\u7248\u672C\u53F7\u83B7\u53D6\u5931\u8D25\");\n}\n\n/** \u83B7\u53D6 per-version \u7684\u4E0B\u8F7D\u94FE\u63A5 + \u54C8\u5E0C (fallback GitHub \u2192 jsdelivr) */\nexport async function fetchVersionDetails(\n cfg: BdsUpdaterConfig,\n channel: string,\n version: string\n): Promise<VersionDetails> {\n const platform = channel === \"preview\" ? \"windows_preview\" : \"windows\";\n const ch = channel === \"preview\" ? \"preview\" : \"release\";\n const vars = { version, ver3: toVer3(version), platform, channel: ch };\n const detailsTpl =\n cfg.version_details || \"https://raw.githubusercontent.com/Bedrock-OSS/BDS-Versions/main/{platform}/{version}.json\";\n const mirrorTpl =\n cfg.version_details_mirror || \"https://cdn.jsdelivr.net/gh/Bedrock-OSS/BDS-Versions@main/{platform}/{version}.json\";\n const sources = [detailsTpl, mirrorTpl].filter(Boolean).map((t) => resolveTemplate(t, vars));\n\n const json = await fetchJsonWithFallback<any>(sources);\n const mode = cfg.version_mode || \"bedrock-oss\";\n if (mode === \"endstone\") {\n const bw = json.binary?.windows;\n return {\n downloadUrl: bw?.url ?? \"\",\n sha1: \"\",\n sha256: bw?.sha256 ?? \"\",\n size: bw?.size_in_bytes ?? 0,\n };\n }\n return {\n downloadUrl: json.download_url ?? \"\",\n sha1: json.sha1 ?? \"\",\n sha256: \"\",\n size: json.size_in_bytes ?? 0,\n };\n}\n\n/** \u5019\u9009\u4E0B\u8F7D URL (\u6309\u4F18\u5148\u7EA7) */\nexport function buildDownloadUrls(\n cfg: BdsUpdaterConfig,\n channel: string,\n version: string,\n details: VersionDetails\n): string[] {\n const urls: string[] = [];\n if (cfg.download_mirror) {\n const mirrorVer = channel === \"preview\" ? `${version}-preview` : version;\n urls.push(resolveTemplate(cfg.download_mirror, { version: mirrorVer }));\n }\n if (details.downloadUrl) urls.push(details.downloadUrl);\n const cdnRoot = cfg.cdn_root || \"https://www.minecraft.net/bedrockdedicatedserver\";\n const suffix =\n channel === \"preview\"\n ? `/bin-win-preview/bedrock-server-${version}-preview.zip`\n : `/bin-win/bedrock-server-${version}.zip`;\n urls.push(cdnRoot + suffix);\n urls.push(`https://minecraft.azureedge.net${suffix}`);\n return [...new Set(urls)];\n}\n\n/** \u540C\u6B65 / \u5F02\u6B65\u6821\u9A8C\u6587\u4EF6\u54C8\u5E0C */\nexport async function verifyFileHash(filePath: string, expectedSha1: string, expectedSha256: string): Promise<boolean> {\n const expected = expectedSha256 || expectedSha1;\n if (!expected) return true;\n const algo: \"sha1\" | \"sha256\" = expectedSha256 ? \"sha256\" : \"sha1\";\n\n return new Promise((resolve, reject) => {\n const h = crypto.createHash(algo);\n const stream = fs.createReadStream(filePath);\n stream.on(\"data\", (chunk: Buffer | string) => {\n h.update(typeof chunk === \"string\" ? Buffer.from(chunk) : chunk);\n });\n stream.on(\"end\", () => {\n const actual = h.digest(\"hex\").toLowerCase();\n resolve(actual === expected.toLowerCase());\n });\n stream.on(\"error\", reject);\n });\n}\n\n/** \u68C0\u67E5\u7248\u672C\u517C\u5BB9\u6027 (\u767D\u540D\u5355) */\nexport function isVersionCompatible(cfg: BdsUpdaterConfig, version: string): boolean {\n const allow = cfg.compatible_versions;\n if (!allow || allow.length === 0) return true;\n return allow.includes(version) || allow.includes(toVer3(version));\n}\n\n"],
5
- "mappings": "AASA,OAAO,YAAY;AACnB,OAAO,QAAQ;AACf,SAAS,6BAA6B;AACtC,SAAS,WAAW;AAEpB,SAAS,cAAc;AAEvB,MAAM,uBAAuB;AAC7B,MAAM,0BAA0B;AAEhC,SAAS,gBAAgB,KAAa,MAAsC;AAC1E,MAAI,IAAI;AACR,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,IAAI,GAAG;AACzC,QAAI,EAAE,QAAQ,IAAI,OAAO,MAAM,CAAC,OAAO,GAAG,GAAG,CAAC;AAAA,EAChD;AACA,SAAO;AACT;AAGA,eAAsB,eAAe,KAAuB,SAAuC;AACjG,QAAM,MAAM,IAAI,oBAAoB,QAAQ,IAAI,kBAAkB,KAAK;AACvE,QAAM,SAAS,IAAI,2BAA2B,QAAQ,IAAI,qBAAqB,KAAK;AACpF,QAAM,UAAU,CAAC,KAAK,MAAM,EAAE,OAAO,OAAO;AAC5C,QAAM,cAAc,IAAI,gBAAgB;AAExC,MAAI;AACJ,WAAS,UAAU,GAAG,WAAW,GAAG,WAAW;AAC7C,QAAI;AACF,YAAM,OAAO,MAAM,sBAA2B,OAAO;AACrD,UAAI;AACJ,UAAI,gBAAgB,YAAY;AAC9B,cAAM,QAAQ,YAAY,YAAY,KAAK,UAAU,KAAK;AAC1D,cAAM,OAAO;AAAA,MACf,OAAO;AACL,cAAM,WAAW,KAAK,WAAW,KAAK;AACtC,cAAM,YAAY,YAAY,UAAU,UAAU,UAAU;AAAA,MAC9D;AACA,UAAI,CAAC,IAAK,OAAM,IAAI,MAAM,kDAAU;AACpC,aAAO,EAAE,SAAS,KAAK,SAAS,KAAK,YAAY,GAAG;AAAA,IACtD,SAAS,GAAG;AACV,gBAAU;AACV,UAAI,UAAU,GAAG;AACf,cAAM,OAAO,UAAU;AACvB,YAAI,KAAK,+CAAY,OAAO,QAAS,EAAY,OAAO,SAAI,OAAO,GAAI,yBAAU;AACjF,cAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC;AAAA,MAC9C;AAAA,IACF;AAAA,EACF;AACA,QAAM,mBAAmB,QAAQ,UAAU,IAAI,MAAM,4CAAS;AAChE;AAGA,eAAsB,oBACpB,KACA,SACA,SACyB;AACzB,QAAM,WAAW,YAAY,YAAY,oBAAoB;AAC7D,QAAM,KAAK,YAAY,YAAY,YAAY;AAC/C,QAAM,OAAO,EAAE,SAAS,MAAM,OAAO,OAAO,GAAG,UAAU,SAAS,GAAG;AACrE,QAAM,aACJ,IAAI,mBAAmB;AACzB,QAAM,YACJ,IAAI,0BAA0B;AAChC,QAAM,UAAU,CAAC,YAAY,SAAS,EAAE,OAAO,OAAO,EAAE,IAAI,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAE3F,QAAM,OAAO,MAAM,sBAA2B,OAAO;AACrD,QAAM,OAAO,IAAI,gBAAgB;AACjC,MAAI,SAAS,YAAY;AACvB,UAAM,KAAK,KAAK,QAAQ;AACxB,WAAO;AAAA,MACL,aAAa,IAAI,OAAO;AAAA,MACxB,MAAM;AAAA,MACN,QAAQ,IAAI,UAAU;AAAA,MACtB,MAAM,IAAI,iBAAiB;AAAA,IAC7B;AAAA,EACF;AACA,SAAO;AAAA,IACL,aAAa,KAAK,gBAAgB;AAAA,IAClC,MAAM,KAAK,QAAQ;AAAA,IACnB,QAAQ;AAAA,IACR,MAAM,KAAK,iBAAiB;AAAA,EAC9B;AACF;AAGO,SAAS,kBACd,KACA,SACA,SACA,SACU;AACV,QAAM,OAAiB,CAAC;AACxB,MAAI,IAAI,iBAAiB;AACvB,UAAM,YAAY,YAAY,YAAY,GAAG,OAAO,aAAa;AACjE,SAAK,KAAK,gBAAgB,IAAI,iBAAiB,EAAE,SAAS,UAAU,CAAC,CAAC;AAAA,EACxE;AACA,MAAI,QAAQ,YAAa,MAAK,KAAK,QAAQ,WAAW;AACtD,QAAM,UAAU,IAAI,YAAY;AAChC,QAAM,SACJ,YAAY,YACR,mCAAmC,OAAO,iBAC1C,2BAA2B,OAAO;AACxC,OAAK,KAAK,UAAU,MAAM;AAC1B,OAAK,KAAK,kCAAkC,MAAM,EAAE;AACpD,SAAO,CAAC,GAAG,IAAI,IAAI,IAAI,CAAC;AAC1B;AAGA,eAAsB,eAAe,UAAkB,cAAsB,gBAA0C;AACrH,QAAM,WAAW,kBAAkB;AACnC,MAAI,CAAC,SAAU,QAAO;AACtB,QAAM,OAA0B,iBAAiB,WAAW;AAE5D,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,IAAI,OAAO,WAAW,IAAI;AAChC,UAAM,SAAS,GAAG,iBAAiB,QAAQ;AAC3C,WAAO,GAAG,QAAQ,CAAC,UAA2B;AAC5C,QAAE,OAAO,OAAO,UAAU,WAAW,OAAO,KAAK,KAAK,IAAI,KAAK;AAAA,IACjE,CAAC;AACD,WAAO,GAAG,OAAO,MAAM;AACrB,YAAM,SAAS,EAAE,OAAO,KAAK,EAAE,YAAY;AAC3C,cAAQ,WAAW,SAAS,YAAY,CAAC;AAAA,IAC3C,CAAC;AACD,WAAO,GAAG,SAAS,MAAM;AAAA,EAC3B,CAAC;AACH;AAGO,SAAS,oBAAoB,KAAuB,SAA0B;AACnF,QAAM,QAAQ,IAAI;AAClB,MAAI,CAAC,SAAS,MAAM,WAAW,EAAG,QAAO;AACzC,SAAO,MAAM,SAAS,OAAO,KAAK,MAAM,SAAS,OAAO,OAAO,CAAC;AAClE;",
4
+ "sourcesContent": ["/**\n * upstream.ts \u2014 BDS \u4E0A\u6E38\u7248\u672C\u6E90\u3001\u591A\u955C\u50CF\u4E0B\u8F7D\u4E0E\u5B8C\u6574\u6027\u6821\u9A8C\n *\n * \u6838\u5FC3\u673A\u5236\uFF1A\n * - \u591A\u6E90\u5BB9\u707E\uFF1A\u5B98\u65B9\u6E90\u4E0E\u955C\u50CF\u6E90\u5E76\u884C\u7ADE\u4E89\u4E0E\u591A\u8F6E\u6307\u6570\u9000\u907F\u91CD\u8BD5\n * - \u52A8\u6001 URL \u6A21\u677F\u6E32\u67D3\uFF1A\u6839\u636E\u64CD\u4F5C\u7CFB\u7EDF\u67B6\u6784\u4E0E\u66F4\u65B0\u901A\u9053\u8BA1\u7B97\u4E0B\u8F7D\u5730\u5740\n * - \u6D41\u5F0F\u54C8\u5E0C\u6821\u9A8C\uFF1A\u652F\u6301 SHA-1 \u4E0E SHA-256 \u5B8C\u6574\u6027\u6D41\u5F0F\u8BA1\u7B97\u6821\u9A8C\n * - \u7248\u672C\u767D\u540D\u5355\u517C\u5BB9\u95E8\u7981\uFF1A\u9632\u6B62\u81EA\u52A8\u5347\u7EA7\u81F3\u672A\u9A8C\u8BC1\u7248\u672C\n */\n\nimport crypto from \"node:crypto\";\nimport fs from \"node:fs\";\nimport { bdsCdnZipSuffix, bdsDetailsPlatformKey, bdsHostOs, type BdsHostOs } from \"./host-platform.js\";\nimport { fetchJsonWithFallback } from \"./http.js\";\nimport { log } from \"./log.js\";\nimport type { BdsUpdaterConfig, VersionDetails, VersionInfo } from \"./types.js\";\nimport { toVer3 } from \"./version.js\";\n\nconst DEFAULT_VERSIONS_API = \"https://raw.githubusercontent.com/Bedrock-OSS/BDS-Versions/main/versions.json\";\nconst DEFAULT_VERSIONS_MIRROR = \"https://cdn.jsdelivr.net/gh/Bedrock-OSS/BDS-Versions@main/versions.json\";\n\nfunction resolveTemplate(tpl: string, vars: Record<string, string>): string {\n let s = tpl;\n for (const [k, v] of Object.entries(vars)) {\n s = s.replace(new RegExp(`\\\\{${k}\\\\}`, \"g\"), v);\n }\n return s;\n}\n\n/**\n * \u83B7\u53D6 BDS \u6700\u65B0\u53EF\u7528\u7248\u672C\u4FE1\u606F\uFF08\u652F\u6301\u91CD\u8BD5\u4E0E\u6307\u6570\u9000\u907F\uFF09\u3002\n *\n * @param cfg BDS \u66F4\u65B0\u5668\u914D\u7F6E\u3002\n * @param channel \u66F4\u65B0\u901A\u9053\uFF08\u5982 \"release\" \u6216 \"preview\"\uFF09\u3002\n * @param hostOs \u76EE\u6807\u4E3B\u673A\u64CD\u4F5C\u7CFB\u7EDF\u7C7B\u578B\u3002\n * @returns \u5305\u542B\u6700\u65B0\u7248\u672C\u53F7\u4E0E CDN \u6839\u8DEF\u5F84\u7684\u4FE1\u606F\u5BF9\u8C61\u3002\n */\nexport async function getVersionInfo(\n\n cfg: BdsUpdaterConfig,\n channel: string,\n hostOs: BdsHostOs = bdsHostOs()\n): Promise<VersionInfo> {\n const api = cfg.version_versions || process.env[\"BDS_VERSIONS_API\"] || DEFAULT_VERSIONS_API;\n const mirror = cfg.version_versions_mirror || process.env[\"BDS_VERSIONS_MIRROR\"] || DEFAULT_VERSIONS_MIRROR;\n const sources = [api, mirror].filter(Boolean);\n const versionMode = cfg.version_mode || \"bedrock-oss\";\n\n let lastErr: unknown;\n for (let attempt = 1; attempt <= 3; attempt++) {\n try {\n const json = await fetchJsonWithFallback<any>(sources);\n let ver: string | undefined;\n if (versionMode === \"endstone\") {\n const entry = channel === \"preview\" ? json.preview : json.release;\n ver = entry?.latest;\n } else {\n const platform = hostOs === \"windows\" ? json.windows || json.linux : json.linux || json.windows;\n ver = channel === \"preview\" ? platform?.preview : platform?.stable;\n }\n if (!ver) throw new Error(\"\u672A\u627E\u5230\u6700\u65B0\u7248\u672C\u53F7\");\n return { version: ver, cdnRoot: json.cdn_root ?? \"\" };\n } catch (e) {\n lastErr = e;\n if (attempt < 3) {\n const wait = attempt * 3000;\n log.warn(`\u7248\u672C\u53F7\u83B7\u53D6\u5931\u8D25 (${attempt}/3): ${(e as Error).message}\uFF0C${wait / 1000}s \u540E\u91CD\u8BD5...`);\n await new Promise((r) => setTimeout(r, wait));\n }\n }\n }\n throw lastErr instanceof Error ? lastErr : new Error(\"\u7248\u672C\u53F7\u83B7\u53D6\u5931\u8D25\");\n}\n\n/** \u83B7\u53D6 per-version \u7684\u4E0B\u8F7D\u94FE\u63A5 + \u54C8\u5E0C (fallback GitHub \u2192 jsdelivr) */\nexport async function fetchVersionDetails(\n cfg: BdsUpdaterConfig,\n channel: string,\n version: string,\n hostOs: BdsHostOs = bdsHostOs()\n): Promise<VersionDetails> {\n const platform = bdsDetailsPlatformKey(channel, hostOs);\n const ch = channel === \"preview\" ? \"preview\" : \"release\";\n const vars = { version, ver3: toVer3(version), platform, channel: ch };\n const detailsTpl =\n cfg.version_details || \"https://raw.githubusercontent.com/Bedrock-OSS/BDS-Versions/main/{platform}/{version}.json\";\n const mirrorTpl =\n cfg.version_details_mirror || \"https://cdn.jsdelivr.net/gh/Bedrock-OSS/BDS-Versions@main/{platform}/{version}.json\";\n const sources = [detailsTpl, mirrorTpl].filter(Boolean).map((t) => resolveTemplate(t, vars));\n\n const json = await fetchJsonWithFallback<any>(sources);\n const mode = cfg.version_mode || \"bedrock-oss\";\n if (mode === \"endstone\") {\n const bw = hostOs === \"windows\" ? json.binary?.windows : json.binary?.linux;\n return {\n downloadUrl: bw?.url ?? \"\",\n sha1: \"\",\n sha256: bw?.sha256 ?? \"\",\n size: bw?.size_in_bytes ?? 0,\n };\n }\n return {\n downloadUrl: json.download_url ?? \"\",\n sha1: json.sha1 ?? \"\",\n sha256: \"\",\n size: json.size_in_bytes ?? 0,\n };\n}\n\n/** \u5019\u9009\u4E0B\u8F7D URL (\u6309\u4F18\u5148\u7EA7) */\nexport function buildDownloadUrls(\n cfg: BdsUpdaterConfig,\n channel: string,\n version: string,\n details: VersionDetails,\n hostOs: BdsHostOs = bdsHostOs()\n): string[] {\n const urls: string[] = [];\n if (cfg.download_mirror) {\n const mirrorVer = channel === \"preview\" ? `${version}-preview` : version;\n urls.push(resolveTemplate(cfg.download_mirror, { version: mirrorVer }));\n }\n if (details.downloadUrl) urls.push(details.downloadUrl);\n const cdnRoot = cfg.cdn_root || \"https://www.minecraft.net/bedrockdedicatedserver\";\n const suffix = bdsCdnZipSuffix(channel, version, hostOs);\n urls.push(cdnRoot + suffix);\n urls.push(`https://minecraft.azureedge.net${suffix}`);\n return [...new Set(urls)];\n}\n\n/** \u540C\u6B65 / \u5F02\u6B65\u6821\u9A8C\u6587\u4EF6\u54C8\u5E0C */\nexport async function verifyFileHash(filePath: string, expectedSha1: string, expectedSha256: string): Promise<boolean> {\n const expected = expectedSha256 || expectedSha1;\n if (!expected) return true;\n const algo: \"sha1\" | \"sha256\" = expectedSha256 ? \"sha256\" : \"sha1\";\n\n return new Promise((resolve, reject) => {\n const h = crypto.createHash(algo);\n const stream = fs.createReadStream(filePath);\n stream.on(\"data\", (chunk: Buffer | string) => {\n h.update(typeof chunk === \"string\" ? Buffer.from(chunk) : chunk);\n });\n stream.on(\"end\", () => {\n const actual = h.digest(\"hex\").toLowerCase();\n resolve(actual === expected.toLowerCase());\n });\n stream.on(\"error\", reject);\n });\n}\n\n/** \u68C0\u67E5\u7248\u672C\u517C\u5BB9\u6027 (\u767D\u540D\u5355) */\nexport function isVersionCompatible(cfg: BdsUpdaterConfig, version: string): boolean {\n const allow = cfg.compatible_versions;\n if (!allow || allow.length === 0) return true;\n return allow.includes(version) || allow.includes(toVer3(version));\n}\n\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/dist/version.js CHANGED
@@ -53,7 +53,7 @@ async function getCurrentVersionAsync(exePath) {
53
53
  for (const [ver, entry] of Object.entries(cache)) {
54
54
  if (entry.sha256 === actual) return ver;
55
55
  }
56
- log.warn("[BDSUpdater] bedrock_server.exe \u54C8\u5E0C\u672A\u5339\u914D\u7F13\u5B58\uFF0C\u7591\u4F3C\u88AB\u6539\u52A8\u6216\u9996\u6B21\u542F\u52A8");
56
+ log.warn("[BDSUpdater] BDS \u53EF\u6267\u884C\u6587\u4EF6\u54C8\u5E0C\u672A\u5339\u914D\u7F13\u5B58\uFF0C\u7591\u4F3C\u88AB\u6539\u52A8\u6216\u9996\u6B21\u542F\u52A8");
57
57
  return "0.0.0.0";
58
58
  }
59
59
  function getCurrentVersionSync(exePath) {
@@ -64,7 +64,7 @@ function getCurrentVersionSync(exePath) {
64
64
  for (const [ver, entry] of Object.entries(cache)) {
65
65
  if (entry.sha256 === actual) return ver;
66
66
  }
67
- log.warn("[BDSUpdater] bedrock_server.exe \u54C8\u5E0C\u672A\u5339\u914D\u7F13\u5B58");
67
+ log.warn("[BDSUpdater] BDS \u53EF\u6267\u884C\u6587\u4EF6\u54C8\u5E0C\u672A\u5339\u914D\u7F13\u5B58");
68
68
  return "0.0.0.0";
69
69
  }
70
70
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/version.ts"],
4
- "sourcesContent": ["/**\n * version.ts \u2014 \u7248\u672C\u53F7\u5DE5\u5177 / \u7248\u672C\u7F13\u5B58\n */\n\nimport { readJson, writeJson } from \"@sfmc-bds/sdk/node/config\";\nimport fs from \"node:fs\";\nimport { VERSION_CACHE } from \"./paths.js\";\nimport { hashFileSync, hashFileAsync } from \"./fsx.js\";\nimport { log } from \"./log.js\";\n\n/** 4\u6BB5 \u2192 3\u6BB5 (\u53BB\u5C3E) */\nexport function toVer3(v: string): string {\n return v.split(\".\").slice(0, 3).join(\".\");\n}\n\n/** 3\u6BB5 \u2192 4\u6BB5 (\u8865 0) */\nexport function toVer4(v: string): string {\n const p = v.split(\".\");\n while (p.length < 4) p.push(\"0\");\n return p.join(\".\");\n}\n\n/** \u7248\u672C\u6BD4\u8F83 (\u6309 4 \u6BB5\u6570\u5B57\u6392\u5E8F) */\nexport function compareVersions(a: string, b: string): number {\n const sa = a.replace(/-preview$/, \"\");\n const sb = b.replace(/-preview$/, \"\");\n const pa = sa.split(\".\").map(Number);\n const pb = sb.split(\".\").map(Number);\n for (let i = 0; i < 4; i++) {\n const ai = pa[i] ?? 0;\n const bi = pb[i] ?? 0;\n if (ai !== bi) return ai - bi;\n }\n // preview \u4F4E\u4E8E\u4E0D\u5E26 -preview \u7684\u540C\u4E00\u6570\u5B57\n if (a.endsWith(\"-preview\") && !b.endsWith(\"-preview\")) return -1;\n if (!a.endsWith(\"-preview\") && b.endsWith(\"-preview\")) return 1;\n return 0;\n}\n\n/** \u7B80\u5355\u7684\u771F\u503C\u6821\u9A8C (\u6309\u7248\u672C\u53F7\u5916\u5F62) */\nexport function isValidVersion(v: string): boolean {\n return /^\\d+\\.\\d+\\.\\d+(\\.\\d+)?(-preview)?$/.test(v);\n}\n\n/** \u7F13\u5B58\u6761\u76EE\u7C7B\u578B */\ninterface CacheEntry {\n sha256: string;\n verified_at: number;\n}\n\nfunction readCache(): Record<string, CacheEntry> {\n return readJson<Record<string, CacheEntry>>(VERSION_CACHE) ?? {};\n}\n\n/** \u4EC5\u4FDD\u7559\u6700\u8FD1 3 \u6761\u7248\u672C\u7F13\u5B58 (\u907F\u514D\u65E0\u9650\u589E\u957F) */\nfunction trimCache(cache: Record<string, CacheEntry>): Record<string, CacheEntry> {\n const keys = Object.keys(cache).sort(compareVersions).slice(-3);\n const trimmed: Record<string, CacheEntry> = {};\n for (const k of keys) trimmed[k] = cache[k]!;\n return trimmed;\n}\n\nexport function saveVersionCache(version: string, sha256: string): void {\n const cache = readCache();\n cache[version] = { sha256, verified_at: Date.now() };\n try {\n writeJson(VERSION_CACHE, trimCache(cache));\n } catch {\n /* ignore */\n }\n}\n\n/**\n * \u5F02\u6B65\u8BC6\u522B\u5F53\u524D BDS \u7248\u672C\n * \u4F18\u5148\u901A\u8FC7 SHA256 \u53CD\u67E5\u7F13\u5B58\uFF0C\u5931\u8D25\u5219\u56DE\u9000 `current_version.txt`\uFF08\u5982\u679C\u5B58\u5728\uFF09\n */\nexport async function getCurrentVersionAsync(exePath: string): Promise<string> {\n if (!fs.existsSync(exePath)) return \"0.0.0.0\";\n const actual = await hashFileAsync(exePath, \"sha256\");\n if (!actual) return \"0.0.0.0\";\n\n const cache = readCache();\n for (const [ver, entry] of Object.entries(cache)) {\n if (entry.sha256 === actual) return ver;\n }\n log.warn(\"[BDSUpdater] bedrock_server.exe \u54C8\u5E0C\u672A\u5339\u914D\u7F13\u5B58\uFF0C\u7591\u4F3C\u88AB\u6539\u52A8\u6216\u9996\u6B21\u542F\u52A8\");\n return \"0.0.0.0\";\n}\n\n/** \u540C\u6B65\u7248\u672C (\u517C\u5BB9\u65E7\u6D41\u7A0B) */\nexport function getCurrentVersionSync(exePath: string): string {\n if (!fs.existsSync(exePath)) return \"0.0.0.0\";\n const actual = hashFileSync(exePath, \"sha256\");\n if (!actual) return \"0.0.0.0\";\n const cache = readCache();\n for (const [ver, entry] of Object.entries(cache)) {\n if (entry.sha256 === actual) return ver;\n }\n log.warn(\"[BDSUpdater] bedrock_server.exe \u54C8\u5E0C\u672A\u5339\u914D\u7F13\u5B58\");\n return \"0.0.0.0\";\n}\n"],
5
- "mappings": "AAIA,SAAS,UAAU,iBAAiB;AACpC,OAAO,QAAQ;AACf,SAAS,qBAAqB;AAC9B,SAAS,cAAc,qBAAqB;AAC5C,SAAS,WAAW;AAGb,SAAS,OAAO,GAAmB;AACxC,SAAO,EAAE,MAAM,GAAG,EAAE,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG;AAC1C;AAGO,SAAS,OAAO,GAAmB;AACxC,QAAM,IAAI,EAAE,MAAM,GAAG;AACrB,SAAO,EAAE,SAAS,EAAG,GAAE,KAAK,GAAG;AAC/B,SAAO,EAAE,KAAK,GAAG;AACnB;AAGO,SAAS,gBAAgB,GAAW,GAAmB;AAC5D,QAAM,KAAK,EAAE,QAAQ,aAAa,EAAE;AACpC,QAAM,KAAK,EAAE,QAAQ,aAAa,EAAE;AACpC,QAAM,KAAK,GAAG,MAAM,GAAG,EAAE,IAAI,MAAM;AACnC,QAAM,KAAK,GAAG,MAAM,GAAG,EAAE,IAAI,MAAM;AACnC,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,UAAM,KAAK,GAAG,CAAC,KAAK;AACpB,UAAM,KAAK,GAAG,CAAC,KAAK;AACpB,QAAI,OAAO,GAAI,QAAO,KAAK;AAAA,EAC7B;AAEA,MAAI,EAAE,SAAS,UAAU,KAAK,CAAC,EAAE,SAAS,UAAU,EAAG,QAAO;AAC9D,MAAI,CAAC,EAAE,SAAS,UAAU,KAAK,EAAE,SAAS,UAAU,EAAG,QAAO;AAC9D,SAAO;AACT;AAGO,SAAS,eAAe,GAAoB;AACjD,SAAO,qCAAqC,KAAK,CAAC;AACpD;AAQA,SAAS,YAAwC;AAC/C,SAAO,SAAqC,aAAa,KAAK,CAAC;AACjE;AAGA,SAAS,UAAU,OAA+D;AAChF,QAAM,OAAO,OAAO,KAAK,KAAK,EAAE,KAAK,eAAe,EAAE,MAAM,EAAE;AAC9D,QAAM,UAAsC,CAAC;AAC7C,aAAW,KAAK,KAAM,SAAQ,CAAC,IAAI,MAAM,CAAC;AAC1C,SAAO;AACT;AAEO,SAAS,iBAAiB,SAAiB,QAAsB;AACtE,QAAM,QAAQ,UAAU;AACxB,QAAM,OAAO,IAAI,EAAE,QAAQ,aAAa,KAAK,IAAI,EAAE;AACnD,MAAI;AACF,cAAU,eAAe,UAAU,KAAK,CAAC;AAAA,EAC3C,QAAQ;AAAA,EAER;AACF;AAMA,eAAsB,uBAAuB,SAAkC;AAC7E,MAAI,CAAC,GAAG,WAAW,OAAO,EAAG,QAAO;AACpC,QAAM,SAAS,MAAM,cAAc,SAAS,QAAQ;AACpD,MAAI,CAAC,OAAQ,QAAO;AAEpB,QAAM,QAAQ,UAAU;AACxB,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,QAAI,MAAM,WAAW,OAAQ,QAAO;AAAA,EACtC;AACA,MAAI,KAAK,8IAAoD;AAC7D,SAAO;AACT;AAGO,SAAS,sBAAsB,SAAyB;AAC7D,MAAI,CAAC,GAAG,WAAW,OAAO,EAAG,QAAO;AACpC,QAAM,SAAS,aAAa,SAAS,QAAQ;AAC7C,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,QAAQ,UAAU;AACxB,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,QAAI,MAAM,WAAW,OAAQ,QAAO;AAAA,EACtC;AACA,MAAI,KAAK,4EAAyC;AAClD,SAAO;AACT;",
4
+ "sourcesContent": ["/**\n * version.ts \u2014 \u7248\u672C\u53F7\u5DE5\u5177 / \u7248\u672C\u7F13\u5B58\n */\n\nimport { readJson, writeJson } from \"@sfmc-bds/sdk/node/config\";\nimport fs from \"node:fs\";\nimport { VERSION_CACHE } from \"./paths.js\";\nimport { hashFileSync, hashFileAsync } from \"./fsx.js\";\nimport { log } from \"./log.js\";\n\n/** 4\u6BB5 \u2192 3\u6BB5 (\u53BB\u5C3E) */\nexport function toVer3(v: string): string {\n return v.split(\".\").slice(0, 3).join(\".\");\n}\n\n/** 3\u6BB5 \u2192 4\u6BB5 (\u8865 0) */\nexport function toVer4(v: string): string {\n const p = v.split(\".\");\n while (p.length < 4) p.push(\"0\");\n return p.join(\".\");\n}\n\n/** \u7248\u672C\u6BD4\u8F83 (\u6309 4 \u6BB5\u6570\u5B57\u6392\u5E8F) */\nexport function compareVersions(a: string, b: string): number {\n const sa = a.replace(/-preview$/, \"\");\n const sb = b.replace(/-preview$/, \"\");\n const pa = sa.split(\".\").map(Number);\n const pb = sb.split(\".\").map(Number);\n for (let i = 0; i < 4; i++) {\n const ai = pa[i] ?? 0;\n const bi = pb[i] ?? 0;\n if (ai !== bi) return ai - bi;\n }\n // preview \u4F4E\u4E8E\u4E0D\u5E26 -preview \u7684\u540C\u4E00\u6570\u5B57\n if (a.endsWith(\"-preview\") && !b.endsWith(\"-preview\")) return -1;\n if (!a.endsWith(\"-preview\") && b.endsWith(\"-preview\")) return 1;\n return 0;\n}\n\n/** \u7B80\u5355\u7684\u771F\u503C\u6821\u9A8C (\u6309\u7248\u672C\u53F7\u5916\u5F62) */\nexport function isValidVersion(v: string): boolean {\n return /^\\d+\\.\\d+\\.\\d+(\\.\\d+)?(-preview)?$/.test(v);\n}\n\n/** \u7F13\u5B58\u6761\u76EE\u7C7B\u578B */\ninterface CacheEntry {\n sha256: string;\n verified_at: number;\n}\n\nfunction readCache(): Record<string, CacheEntry> {\n return readJson<Record<string, CacheEntry>>(VERSION_CACHE) ?? {};\n}\n\n/** \u4EC5\u4FDD\u7559\u6700\u8FD1 3 \u6761\u7248\u672C\u7F13\u5B58 (\u907F\u514D\u65E0\u9650\u589E\u957F) */\nfunction trimCache(cache: Record<string, CacheEntry>): Record<string, CacheEntry> {\n const keys = Object.keys(cache).sort(compareVersions).slice(-3);\n const trimmed: Record<string, CacheEntry> = {};\n for (const k of keys) trimmed[k] = cache[k]!;\n return trimmed;\n}\n\nexport function saveVersionCache(version: string, sha256: string): void {\n const cache = readCache();\n cache[version] = { sha256, verified_at: Date.now() };\n try {\n writeJson(VERSION_CACHE, trimCache(cache));\n } catch {\n /* ignore */\n }\n}\n\n/**\n * \u5F02\u6B65\u8BC6\u522B\u5F53\u524D BDS \u7248\u672C\n * \u4F18\u5148\u901A\u8FC7 SHA256 \u53CD\u67E5\u7F13\u5B58\uFF0C\u5931\u8D25\u5219\u56DE\u9000 `current_version.txt`\uFF08\u5982\u679C\u5B58\u5728\uFF09\n */\nexport async function getCurrentVersionAsync(exePath: string): Promise<string> {\n if (!fs.existsSync(exePath)) return \"0.0.0.0\";\n const actual = await hashFileAsync(exePath, \"sha256\");\n if (!actual) return \"0.0.0.0\";\n\n const cache = readCache();\n for (const [ver, entry] of Object.entries(cache)) {\n if (entry.sha256 === actual) return ver;\n }\n log.warn(\"[BDSUpdater] BDS \u53EF\u6267\u884C\u6587\u4EF6\u54C8\u5E0C\u672A\u5339\u914D\u7F13\u5B58\uFF0C\u7591\u4F3C\u88AB\u6539\u52A8\u6216\u9996\u6B21\u542F\u52A8\");\n return \"0.0.0.0\";\n}\n\n/** \u540C\u6B65\u7248\u672C (\u517C\u5BB9\u65E7\u6D41\u7A0B) */\nexport function getCurrentVersionSync(exePath: string): string {\n if (!fs.existsSync(exePath)) return \"0.0.0.0\";\n const actual = hashFileSync(exePath, \"sha256\");\n if (!actual) return \"0.0.0.0\";\n const cache = readCache();\n for (const [ver, entry] of Object.entries(cache)) {\n if (entry.sha256 === actual) return ver;\n }\n log.warn(\"[BDSUpdater] BDS \u53EF\u6267\u884C\u6587\u4EF6\u54C8\u5E0C\u672A\u5339\u914D\u7F13\u5B58\");\n return \"0.0.0.0\";\n}\n"],
5
+ "mappings": "AAIA,SAAS,UAAU,iBAAiB;AACpC,OAAO,QAAQ;AACf,SAAS,qBAAqB;AAC9B,SAAS,cAAc,qBAAqB;AAC5C,SAAS,WAAW;AAGb,SAAS,OAAO,GAAmB;AACxC,SAAO,EAAE,MAAM,GAAG,EAAE,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG;AAC1C;AAGO,SAAS,OAAO,GAAmB;AACxC,QAAM,IAAI,EAAE,MAAM,GAAG;AACrB,SAAO,EAAE,SAAS,EAAG,GAAE,KAAK,GAAG;AAC/B,SAAO,EAAE,KAAK,GAAG;AACnB;AAGO,SAAS,gBAAgB,GAAW,GAAmB;AAC5D,QAAM,KAAK,EAAE,QAAQ,aAAa,EAAE;AACpC,QAAM,KAAK,EAAE,QAAQ,aAAa,EAAE;AACpC,QAAM,KAAK,GAAG,MAAM,GAAG,EAAE,IAAI,MAAM;AACnC,QAAM,KAAK,GAAG,MAAM,GAAG,EAAE,IAAI,MAAM;AACnC,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,UAAM,KAAK,GAAG,CAAC,KAAK;AACpB,UAAM,KAAK,GAAG,CAAC,KAAK;AACpB,QAAI,OAAO,GAAI,QAAO,KAAK;AAAA,EAC7B;AAEA,MAAI,EAAE,SAAS,UAAU,KAAK,CAAC,EAAE,SAAS,UAAU,EAAG,QAAO;AAC9D,MAAI,CAAC,EAAE,SAAS,UAAU,KAAK,EAAE,SAAS,UAAU,EAAG,QAAO;AAC9D,SAAO;AACT;AAGO,SAAS,eAAe,GAAoB;AACjD,SAAO,qCAAqC,KAAK,CAAC;AACpD;AAQA,SAAS,YAAwC;AAC/C,SAAO,SAAqC,aAAa,KAAK,CAAC;AACjE;AAGA,SAAS,UAAU,OAA+D;AAChF,QAAM,OAAO,OAAO,KAAK,KAAK,EAAE,KAAK,eAAe,EAAE,MAAM,EAAE;AAC9D,QAAM,UAAsC,CAAC;AAC7C,aAAW,KAAK,KAAM,SAAQ,CAAC,IAAI,MAAM,CAAC;AAC1C,SAAO;AACT;AAEO,SAAS,iBAAiB,SAAiB,QAAsB;AACtE,QAAM,QAAQ,UAAU;AACxB,QAAM,OAAO,IAAI,EAAE,QAAQ,aAAa,KAAK,IAAI,EAAE;AACnD,MAAI;AACF,cAAU,eAAe,UAAU,KAAK,CAAC;AAAA,EAC3C,QAAQ;AAAA,EAER;AACF;AAMA,eAAsB,uBAAuB,SAAkC;AAC7E,MAAI,CAAC,GAAG,WAAW,OAAO,EAAG,QAAO;AACpC,QAAM,SAAS,MAAM,cAAc,SAAS,QAAQ;AACpD,MAAI,CAAC,OAAQ,QAAO;AAEpB,QAAM,QAAQ,UAAU;AACxB,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,QAAI,MAAM,WAAW,OAAQ,QAAO;AAAA,EACtC;AACA,MAAI,KAAK,6JAA0C;AACnD,SAAO;AACT;AAGO,SAAS,sBAAsB,SAAyB;AAC7D,MAAI,CAAC,GAAG,WAAW,OAAO,EAAG,QAAO;AACpC,QAAM,SAAS,aAAa,SAAS,QAAQ;AAC7C,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,QAAQ,UAAU;AACxB,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,QAAI,MAAM,WAAW,OAAQ,QAAO;AAAA,EACtC;AACA,MAAI,KAAK,2FAA+B;AACxC,SAAO;AACT;",
6
6
  "names": []
7
7
  }