@sfmc-bds/bds-tools 0.2.0-beta.8 → 0.2.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.
- package/LICENSE +663 -0
- package/README.md +1 -1
- package/dist/bds-manager.d.ts +16 -6
- package/dist/bds-manager.d.ts.map +1 -1
- package/dist/bds-manager.js +59 -66
- package/dist/bds-manager.js.map +2 -2
- package/dist/changelog.d.ts +11 -7
- package/dist/changelog.d.ts.map +1 -1
- package/dist/changelog.js +60 -34
- package/dist/changelog.js.map +2 -2
- package/dist/check-update.d.ts.map +1 -1
- package/dist/check-update.js +59 -23
- package/dist/check-update.js.map +2 -2
- package/dist/cli-pack-manager.js +11 -1
- package/dist/cli-pack-manager.js.map +2 -2
- package/dist/dependency-negotiator.d.ts +73 -0
- package/dist/dependency-negotiator.d.ts.map +1 -0
- package/dist/dependency-negotiator.js +151 -0
- package/dist/dependency-negotiator.js.map +7 -0
- package/dist/fsx.d.ts +17 -14
- package/dist/fsx.d.ts.map +1 -1
- package/dist/fsx.js +67 -19
- package/dist/fsx.js.map +2 -2
- package/dist/host-platform.d.ts +25 -0
- package/dist/host-platform.d.ts.map +1 -0
- package/dist/host-platform.js +50 -0
- package/dist/host-platform.js.map +7 -0
- package/dist/http.d.ts +49 -11
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js.map +2 -2
- package/dist/level-dat.d.ts +105 -0
- package/dist/level-dat.d.ts.map +1 -0
- package/dist/level-dat.js +490 -0
- package/dist/level-dat.js.map +7 -0
- package/dist/pack-manager.d.ts +41 -34
- package/dist/pack-manager.d.ts.map +1 -1
- package/dist/pack-manager.js +79 -25
- package/dist/pack-manager.js.map +2 -2
- package/dist/pack-update/bindings.d.ts +16 -0
- package/dist/pack-update/bindings.d.ts.map +1 -0
- package/dist/pack-update/bindings.js +69 -0
- package/dist/pack-update/bindings.js.map +7 -0
- package/dist/pack-update/config.d.ts +16 -0
- package/dist/pack-update/config.d.ts.map +1 -0
- package/dist/pack-update/config.js +146 -0
- package/dist/pack-update/config.js.map +7 -0
- package/dist/pack-update/deps.d.ts +28 -0
- package/dist/pack-update/deps.d.ts.map +1 -0
- package/dist/pack-update/deps.js +20 -0
- package/dist/pack-update/deps.js.map +7 -0
- package/dist/pack-update/index.d.ts +55 -0
- package/dist/pack-update/index.d.ts.map +1 -0
- package/dist/pack-update/index.js +72 -0
- package/dist/pack-update/index.js.map +7 -0
- package/dist/pack-update/providers/curseforge.d.ts +23 -0
- package/dist/pack-update/providers/curseforge.d.ts.map +1 -0
- package/dist/pack-update/providers/curseforge.js +229 -0
- package/dist/pack-update/providers/curseforge.js.map +7 -0
- package/dist/pack-update/providers/index.d.ts +16 -0
- package/dist/pack-update/providers/index.d.ts.map +1 -0
- package/dist/pack-update/providers/index.js +37 -0
- package/dist/pack-update/providers/index.js.map +7 -0
- package/dist/pack-update/service.d.ts +28 -0
- package/dist/pack-update/service.d.ts.map +1 -0
- package/dist/pack-update/service.js +570 -0
- package/dist/pack-update/service.js.map +7 -0
- package/dist/pack-update/types.d.ts +126 -0
- package/dist/pack-update/types.d.ts.map +1 -0
- package/dist/pack-update/types.js +8 -0
- package/dist/pack-update/types.js.map +7 -0
- package/dist/pack-update/version-policy.d.ts +81 -0
- package/dist/pack-update/version-policy.d.ts.map +1 -0
- package/dist/pack-update/version-policy.js +210 -0
- package/dist/pack-update/version-policy.js.map +7 -0
- package/dist/paths.d.ts +1 -1
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +3 -5
- package/dist/paths.js.map +2 -2
- package/dist/process-probe.d.ts +34 -0
- package/dist/process-probe.d.ts.map +1 -0
- package/dist/process-probe.js +152 -0
- package/dist/process-probe.js.map +7 -0
- package/dist/qq-events-notify.d.ts +16 -0
- package/dist/qq-events-notify.d.ts.map +1 -0
- package/dist/qq-events-notify.js +70 -0
- package/dist/qq-events-notify.js.map +7 -0
- package/dist/qqutil.d.ts +10 -5
- package/dist/qqutil.d.ts.map +1 -1
- package/dist/qqutil.js +53 -10
- package/dist/qqutil.js.map +2 -2
- package/dist/rollback.d.ts.map +1 -1
- package/dist/rollback.js +2 -2
- package/dist/rollback.js.map +2 -2
- package/dist/server-properties-i18n.d.ts +17 -0
- package/dist/server-properties-i18n.d.ts.map +1 -0
- package/dist/server-properties-i18n.js +201 -0
- package/dist/server-properties-i18n.js.map +7 -0
- package/dist/server-properties.d.ts +25 -0
- package/dist/server-properties.d.ts.map +1 -1
- package/dist/server-properties.js +141 -1
- package/dist/server-properties.js.map +2 -2
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/upstream.d.ts +18 -9
- package/dist/upstream.d.ts.map +1 -1
- package/dist/upstream.js +8 -7
- package/dist/upstream.js.map +2 -2
- package/dist/version.js +2 -2
- package/dist/version.js.map +2 -2
- package/dist/world-packs.d.ts +108 -8
- package/dist/world-packs.d.ts.map +1 -1
- package/dist/world-packs.js +245 -12
- package/dist/world-packs.js.map +3 -3
- package/package.json +24 -17
package/dist/bds-manager.d.ts
CHANGED
|
@@ -1,23 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* bds-manager.ts — BDS
|
|
2
|
+
* bds-manager.ts — Bedrock Dedicated Server (BDS) 进程生命周期管理器
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* 核心职责:
|
|
5
|
+
* - 优雅停服:向标准输入发送 `stop` 命令等待自然退出,超时后逐步升级至 SIGTERM 与 SIGKILL
|
|
6
|
+
* - 守护监视(watch):监听进程退出事件并在异常退出时自动延时重启恢复
|
|
7
|
+
* - 生命周期事件广播:分发启动、停止、崩溃等事件,触发运维告警与状态同步
|
|
8
|
+
* - 管道交互与探活:检测进程存活状态并向 BDS stdin 投递控制台指令
|
|
9
9
|
*/
|
|
10
10
|
import { EventEmitter } from "node:events";
|
|
11
|
+
/** BDS 进程管理器统一操作接口。 */
|
|
11
12
|
export interface BdsManager {
|
|
13
|
+
/** 启动 BDS 服务进程。 */
|
|
12
14
|
start(): Promise<void>;
|
|
15
|
+
/** 优雅停止 BDS 服务进程。 */
|
|
13
16
|
stop(): Promise<void>;
|
|
17
|
+
/** 查询 BDS 进程当前是否处于运行状态。 */
|
|
14
18
|
status(): Promise<boolean>;
|
|
19
|
+
/** 向 BDS 控制台管道发送指令。 */
|
|
15
20
|
sendCommand(cmd: string): boolean;
|
|
21
|
+
/** 开启守护监视模式(异常退出时自动拉起)。 */
|
|
16
22
|
watch(): Promise<void>;
|
|
23
|
+
/** 进程生命周期事件发射器。 */
|
|
17
24
|
events: EventEmitter;
|
|
25
|
+
/** 当前是否属于主动调用停服。 */
|
|
18
26
|
isManualStop: boolean;
|
|
27
|
+
/** 获取当前运行中 BDS 进程的 PID。 */
|
|
19
28
|
getPid(): number;
|
|
20
29
|
}
|
|
30
|
+
/** BDS 进程启动选项。 */
|
|
21
31
|
export interface BdsManagerOptions {
|
|
22
32
|
detached?: boolean;
|
|
23
33
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bds-manager.d.ts","sourceRoot":"","sources":["../src/bds-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"bds-manager.d.ts","sourceRoot":"","sources":["../src/bds-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAiB3C,uBAAuB;AACvB,MAAM,WAAW,UAAU;IACzB,mBAAmB;IACnB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,qBAAqB;IACrB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,2BAA2B;IAC3B,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3B,uBAAuB;IACvB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAClC,2BAA2B;IAC3B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,mBAAmB;IACnB,MAAM,EAAE,YAAY,CAAC;IACrB,oBAAoB;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,2BAA2B;IAC3B,MAAM,IAAI,MAAM,CAAC;CAClB;AAED,kBAAkB;AAClB,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AA+BD,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,iBAAsB,GAAG,UAAU,CAkM5E;AAED,eAAO,MAAM,SAAS,EAAE,YAAiC,CAAC;AAC1D,eAAO,MAAM,iBAAiB,QAAO,IAEpC,CAAC"}
|
package/dist/bds-manager.js
CHANGED
|
@@ -1,52 +1,28 @@
|
|
|
1
|
-
import { spawn
|
|
2
|
-
import { promisify } from "node:util";
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
3
2
|
import fs from "node:fs";
|
|
4
3
|
import path from "node:path";
|
|
5
4
|
import { EventEmitter } from "node:events";
|
|
5
|
+
import { bdsExePath, bdsSpawnEnvExtra, ensureBdsExecutable } from "./host-platform.js";
|
|
6
6
|
import { isMainModule } from "./is-main.js";
|
|
7
|
-
import {
|
|
7
|
+
import { loadConfig, ROOT_DIR } from "./paths.js";
|
|
8
8
|
import { log } from "./log.js";
|
|
9
|
-
import {
|
|
10
|
-
|
|
9
|
+
import { postBdsLifecycleEvent } from "./qq-events-notify.js";
|
|
10
|
+
import { ensureEmitServerTelemetry, ensureNetherNetTransport, localizeServerProperties } from "./server-properties.js";
|
|
11
|
+
import {
|
|
12
|
+
clearBdsPidFile,
|
|
13
|
+
findBedrockServerPids,
|
|
14
|
+
isProcessAlive,
|
|
15
|
+
isProcessAliveSync,
|
|
16
|
+
killBedrockServerByImage,
|
|
17
|
+
readBdsPidFile,
|
|
18
|
+
writeBdsPidFile
|
|
19
|
+
} from "./process-probe.js";
|
|
11
20
|
let cached = null;
|
|
12
|
-
function readPid() {
|
|
13
|
-
try {
|
|
14
|
-
return parseInt(fs.readFileSync(PID_FILE, "utf-8").trim(), 10) || 0;
|
|
15
|
-
} catch {
|
|
16
|
-
return 0;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
function writePid(pid) {
|
|
20
|
-
try {
|
|
21
|
-
ensureStateDir();
|
|
22
|
-
fs.writeFileSync(PID_FILE, String(pid));
|
|
23
|
-
} catch {
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
function clearPid() {
|
|
27
|
-
try {
|
|
28
|
-
if (fs.existsSync(PID_FILE)) fs.unlinkSync(PID_FILE);
|
|
29
|
-
} catch {
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
async function isAlive(pid) {
|
|
33
|
-
if (!pid) return false;
|
|
34
|
-
try {
|
|
35
|
-
if (process.platform === "win32") {
|
|
36
|
-
const { stdout } = await execAsync(`tasklist /fi "PID eq ${pid}" /nh`, { windowsHide: true });
|
|
37
|
-
return stdout.includes(String(pid));
|
|
38
|
-
}
|
|
39
|
-
process.kill(pid, 0);
|
|
40
|
-
return true;
|
|
41
|
-
} catch {
|
|
42
|
-
return false;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
21
|
function ensureProc() {
|
|
46
22
|
if (cached) return cached;
|
|
47
23
|
const cfg = loadConfig();
|
|
48
24
|
const bds_path = path.resolve(cfg.bds_path || process.cwd());
|
|
49
|
-
const exePath =
|
|
25
|
+
const exePath = bdsExePath(bds_path);
|
|
50
26
|
cached = {
|
|
51
27
|
process: null,
|
|
52
28
|
isManualStop: false,
|
|
@@ -62,8 +38,8 @@ function createBdsManager(options = {}) {
|
|
|
62
38
|
events.setMaxListeners(100);
|
|
63
39
|
const sendCommand = (cmd) => {
|
|
64
40
|
const p = ensureProc();
|
|
65
|
-
const pid =
|
|
66
|
-
if (!pid || !
|
|
41
|
+
const pid = readBdsPidFile(ROOT_DIR);
|
|
42
|
+
if (!pid || !isProcessAliveSync(pid)) {
|
|
67
43
|
log.warn("BDS \u672A\u8FD0\u884C");
|
|
68
44
|
return false;
|
|
69
45
|
}
|
|
@@ -81,14 +57,25 @@ function createBdsManager(options = {}) {
|
|
|
81
57
|
};
|
|
82
58
|
const stop = async () => {
|
|
83
59
|
const p = ensureProc();
|
|
84
|
-
|
|
85
|
-
|
|
60
|
+
p.isManualStop = true;
|
|
61
|
+
const pid = readBdsPidFile(ROOT_DIR);
|
|
62
|
+
if (!pid || !await isProcessAlive(pid)) {
|
|
63
|
+
const externalPids = await findBedrockServerPids();
|
|
64
|
+
if (externalPids.length > 0) {
|
|
65
|
+
log.info("\u68C0\u6D4B\u5230\u5916\u90E8\u8FD0\u884C\u7684 BDS \u8FDB\u7A0B\uFF0C\u4F7F\u7528 taskkill \u7EC8\u6B62...");
|
|
66
|
+
await killBedrockServerByImage();
|
|
67
|
+
clearBdsPidFile(ROOT_DIR);
|
|
68
|
+
p.process = null;
|
|
69
|
+
log.info("BDS \u5DF2\u505C\u6B62");
|
|
70
|
+
void postBdsLifecycleEvent("stop");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
86
73
|
log.info("BDS \u672A\u8FD0\u884C");
|
|
87
|
-
|
|
74
|
+
clearBdsPidFile(ROOT_DIR);
|
|
75
|
+
p.isManualStop = false;
|
|
88
76
|
return;
|
|
89
77
|
}
|
|
90
78
|
if (p.process && p.process.pid === pid && p.process.stdin) {
|
|
91
|
-
p.isManualStop = true;
|
|
92
79
|
log.info("\u6B63\u5728\u5173\u95ED BDS...");
|
|
93
80
|
try {
|
|
94
81
|
p.process.stdin.write("stop\n");
|
|
@@ -117,42 +104,42 @@ function createBdsManager(options = {}) {
|
|
|
117
104
|
}
|
|
118
105
|
} else {
|
|
119
106
|
log.info("BDS \u7531\u5916\u90E8\u542F\u52A8\uFF0C\u4F7F\u7528 taskkill...");
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
await execAsync("taskkill /f /im bedrock_server.exe", { windowsHide: true });
|
|
123
|
-
} else {
|
|
124
|
-
await execAsync("pkill -f bedrock_server", { windowsHide: true });
|
|
125
|
-
}
|
|
126
|
-
} catch {
|
|
127
|
-
}
|
|
107
|
+
await killBedrockServerByImage();
|
|
108
|
+
void postBdsLifecycleEvent("stop");
|
|
128
109
|
}
|
|
129
|
-
|
|
110
|
+
clearBdsPidFile(ROOT_DIR);
|
|
130
111
|
p.process = null;
|
|
131
112
|
log.info("BDS \u5DF2\u505C\u6B62");
|
|
132
113
|
};
|
|
133
114
|
const start = async () => {
|
|
134
115
|
const p = ensureProc();
|
|
135
|
-
const existing =
|
|
136
|
-
if (existing && await
|
|
116
|
+
const existing = readBdsPidFile(ROOT_DIR);
|
|
117
|
+
if (existing && await isProcessAlive(existing)) {
|
|
137
118
|
log.info("BDS \u5DF2\u5728\u8FD0\u884C\u4E2D");
|
|
138
119
|
return;
|
|
139
120
|
}
|
|
140
121
|
if (!fs.existsSync(p.exePath)) {
|
|
141
122
|
log.error(`\u672A\u627E\u5230 ${p.exePath}`);
|
|
142
|
-
throw new Error(`
|
|
123
|
+
throw new Error(`BDS \u53EF\u6267\u884C\u6587\u4EF6\u4E0D\u5B58\u5728: ${p.exePath}`);
|
|
143
124
|
}
|
|
144
125
|
ensureEmitServerTelemetry(p.bdsPath, log);
|
|
126
|
+
ensureNetherNetTransport(p.bdsPath, log);
|
|
127
|
+
localizeServerProperties(p.bdsPath, { logger: log, rootDir: ROOT_DIR });
|
|
128
|
+
ensureBdsExecutable(p.exePath);
|
|
145
129
|
log.info("\u6B63\u5728\u542F\u52A8 BDS...");
|
|
130
|
+
p.isManualStop = false;
|
|
146
131
|
const child = spawn(p.exePath, [], {
|
|
147
132
|
cwd: p.bdsPath,
|
|
133
|
+
env: { ...process.env, ...bdsSpawnEnvExtra(p.bdsPath) },
|
|
148
134
|
stdio: options.detached ? "ignore" : ["pipe", "pipe", "pipe"],
|
|
149
135
|
detached: options.detached,
|
|
150
136
|
windowsHide: true
|
|
151
137
|
});
|
|
152
138
|
if (options.detached) child.unref();
|
|
153
139
|
p.process = child;
|
|
154
|
-
|
|
140
|
+
writeBdsPidFile(child.pid ?? 0, ROOT_DIR);
|
|
155
141
|
log.info(`BDS \u5DF2\u542F\u52A8 (PID: ${child.pid})`);
|
|
142
|
+
void postBdsLifecycleEvent("start", `pid=${child.pid ?? 0}`);
|
|
156
143
|
child.stdout?.on("data", (chunk) => {
|
|
157
144
|
const text = chunk.toString();
|
|
158
145
|
events.emit("output", text);
|
|
@@ -165,9 +152,15 @@ function createBdsManager(options = {}) {
|
|
|
165
152
|
});
|
|
166
153
|
child.on("exit", (code) => {
|
|
167
154
|
log.info(`BDS \u5DF2\u9000\u51FA (code: ${code})`);
|
|
168
|
-
|
|
155
|
+
clearBdsPidFile(ROOT_DIR);
|
|
169
156
|
p.process = null;
|
|
170
|
-
|
|
157
|
+
const wasManual = p.isManualStop;
|
|
158
|
+
if (!wasManual) {
|
|
159
|
+
void postBdsLifecycleEvent("crash", `code=${code ?? "?"}`);
|
|
160
|
+
} else {
|
|
161
|
+
void postBdsLifecycleEvent("stop");
|
|
162
|
+
}
|
|
163
|
+
if (!wasManual && p.crashRestart && isMain()) {
|
|
171
164
|
log.info(`BDS \u610F\u5916\u9000\u51FA\uFF0C${p.crashDelayMs / 1e3}s \u540E\u81EA\u52A8\u91CD\u542F...`);
|
|
172
165
|
setTimeout(() => {
|
|
173
166
|
start().catch((e) => log.error(`\u81EA\u52A8\u91CD\u542F\u5931\u8D25: ${e.message}`));
|
|
@@ -177,21 +170,21 @@ function createBdsManager(options = {}) {
|
|
|
177
170
|
});
|
|
178
171
|
};
|
|
179
172
|
const status = async () => {
|
|
180
|
-
const pid =
|
|
181
|
-
const alive = pid > 0 && await
|
|
173
|
+
const pid = readBdsPidFile(ROOT_DIR);
|
|
174
|
+
const alive = pid > 0 && await isProcessAlive(pid);
|
|
182
175
|
if (alive) {
|
|
183
176
|
console.log(`BDS \u8FD0\u884C\u4E2D (PID: ${pid})`);
|
|
184
177
|
} else {
|
|
185
178
|
console.log("BDS \u672A\u8FD0\u884C");
|
|
186
|
-
|
|
179
|
+
clearBdsPidFile(ROOT_DIR);
|
|
187
180
|
}
|
|
188
181
|
return alive;
|
|
189
182
|
};
|
|
190
183
|
const watch = async () => {
|
|
191
184
|
log.info("\u542F\u52A8\u76D1\u62A4\u6A21\u5F0F...");
|
|
192
185
|
while (true) {
|
|
193
|
-
const pid =
|
|
194
|
-
if (!pid || !await
|
|
186
|
+
const pid = readBdsPidFile(ROOT_DIR);
|
|
187
|
+
if (!pid || !await isProcessAlive(pid)) {
|
|
195
188
|
try {
|
|
196
189
|
await start();
|
|
197
190
|
} catch (e) {
|
|
@@ -203,7 +196,7 @@ function createBdsManager(options = {}) {
|
|
|
203
196
|
};
|
|
204
197
|
const getPid = () => {
|
|
205
198
|
const p = ensureProc();
|
|
206
|
-
return p.process?.pid ??
|
|
199
|
+
return p.process?.pid ?? readBdsPidFile(ROOT_DIR);
|
|
207
200
|
};
|
|
208
201
|
return {
|
|
209
202
|
start,
|
package/dist/bds-manager.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/bds-manager.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * bds-manager.ts \u2014 BDS \u8FDB\u7A0B\u7BA1\u7406\u5668\n *\n * \u6539\u8FDB:\n * - \u4F18\u96C5 stop (\u53D1\u9001 stop \u547D\u4EE4 \u2192 \u7B49\u5F85\u9000\u51FA \u2192 SIGTERM \u2192 SIGKILL)\n * - watchdog (\u5D29\u6E83\u81EA\u52A8\u91CD\u542F)\n * - \u5355\u4F8B\u4E8B\u4EF6\u53D1\u5C04\u5668\n * - \u5B8C\u5168\u5F02\u6B65 (fs/promises)\n */\n\nimport { spawn, exec } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { EventEmitter } from \"node:events\";\nimport { isMainModule } from \"./is-main.js\";\nimport { ensureStateDir, loadConfig, PID_FILE } from \"./paths.js\";\nimport { log } from \"./log.js\";\nimport { ensureEmitServerTelemetry } from \"./server-properties.js\";\n\nconst execAsync = promisify(exec);\n\nexport interface BdsManager {\n start(): Promise<void>;\n stop(): Promise<void>;\n status(): Promise<boolean>;\n sendCommand(cmd: string): boolean;\n watch(): Promise<void>;\n events: EventEmitter;\n isManualStop: boolean;\n getPid(): number;\n}\n\nexport interface BdsManagerOptions {\n detached?: boolean;\n}\n\ninterface CachedProc {\n process: ReturnType<typeof spawn> | null;\n isManualStop: boolean;\n crashRestart: boolean;\n crashDelayMs: number;\n exePath: string;\n bdsPath: string;\n}\n\nlet cached: CachedProc | null = null;\n\nfunction readPid(): number {\n try {\n return parseInt(fs.readFileSync(PID_FILE, \"utf-8\").trim(), 10) || 0;\n } catch {\n return 0;\n }\n}\n\nfunction writePid(pid: number): void {\n try {\n ensureStateDir();\n fs.writeFileSync(PID_FILE, String(pid));\n } catch {\n /* ignore */\n }\n}\n\nfunction clearPid(): void {\n try {\n if (fs.existsSync(PID_FILE)) fs.unlinkSync(PID_FILE);\n } catch {\n /* ignore */\n }\n}\n\nasync function isAlive(pid: number): Promise<boolean> {\n if (!pid) return false;\n try {\n if (process.platform === \"win32\") {\n const { stdout } = await execAsync(`tasklist /fi \"PID eq ${pid}\" /nh`, { windowsHide: true });\n return stdout.includes(String(pid));\n }\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction ensureProc(): CachedProc {\n if (cached) return cached;\n const cfg = loadConfig();\n const bds_path = path.resolve(cfg.bds_path || process.cwd());\n const exePath = path.join(bds_path, \"bedrock_server.exe\");\n\n cached = {\n process: null,\n isManualStop: false,\n crashRestart: cfg.crash_restart !== false,\n crashDelayMs: (cfg.crash_restart_delay ?? 5) * 1000,\n exePath,\n bdsPath: bds_path,\n };\n return cached;\n}\n\nexport function createBdsManager(options: BdsManagerOptions = {}): BdsManager {\n const events = new EventEmitter();\n events.setMaxListeners(100);\n\n const sendCommand = (cmd: string): boolean => {\n const p = ensureProc();\n const pid = readPid();\n if (!pid || !isAlive(pid)) {\n log.warn(\"BDS \u672A\u8FD0\u884C\");\n return false;\n }\n if (p.process && p.process.pid === pid && p.process.stdin) {\n try {\n p.process.stdin.write(cmd + \"\\n\");\n log.info(`\u5DF2\u53D1\u9001\u547D\u4EE4: ${cmd}`);\n return true;\n } catch {\n return false;\n }\n }\n log.warn(\"BDS \u7531\u5916\u90E8\u542F\u52A8\uFF0C\u65E0\u6CD5\u53D1\u9001\u547D\u4EE4\u5230 stdin\");\n return false;\n };\n\n const stop = async (): Promise<void> => {\n const p = ensureProc();\n const pid = readPid();\n if (!pid || !(await isAlive(pid))) {\n log.info(\"BDS \u672A\u8FD0\u884C\");\n clearPid();\n return;\n }\n\n if (p.process && p.process.pid === pid && p.process.stdin) {\n p.isManualStop = true;\n log.info(\"\u6B63\u5728\u5173\u95ED BDS...\");\n try {\n p.process.stdin.write(\"stop\\n\");\n } catch (e) {\n log.warn(`\u53D1\u9001 stop \u547D\u4EE4\u5931\u8D25: ${(e as Error).message}`);\n }\n\n await Promise.race([\n new Promise<void>((resolve) => p.process?.once(\"exit\", () => resolve())),\n new Promise<void>((resolve) =>\n setTimeout(() => {\n log.warn(\"30s \u8D85\u65F6\uFF0C\u5F3A\u5236\u7EC8\u6B62...\");\n try { p.process?.kill(\"SIGTERM\"); } catch {}\n setTimeout(resolve, 5000);\n }, 30_000)\n ),\n ]);\n\n if (p.process && p.process.exitCode === null) {\n log.warn(\"\u5F3A\u5236\u7ED3\u675F\u8FDB\u7A0B...\");\n try { p.process.kill(\"SIGKILL\"); } catch {}\n }\n } else {\n // \u5916\u90E8\u542F\u52A8\u7684 BDS \u2014 fallback \u4F7F\u7528 taskkill / pkill\n log.info(\"BDS \u7531\u5916\u90E8\u542F\u52A8\uFF0C\u4F7F\u7528 taskkill...\");\n try {\n if (process.platform === \"win32\") {\n await execAsync(\"taskkill /f /im bedrock_server.exe\", { windowsHide: true });\n } else {\n await execAsync(\"pkill -f bedrock_server\", { windowsHide: true });\n }\n } catch {\n /* ignore */\n }\n }\n\n clearPid();\n p.process = null;\n log.info(\"BDS \u5DF2\u505C\u6B62\");\n };\n\n const start = async (): Promise<void> => {\n const p = ensureProc();\n const existing = readPid();\n if (existing && (await isAlive(existing))) {\n log.info(\"BDS \u5DF2\u5728\u8FD0\u884C\u4E2D\");\n return;\n }\n\n if (!fs.existsSync(p.exePath)) {\n log.error(`\u672A\u627E\u5230 ${p.exePath}`);\n throw new Error(`bedrock_server.exe \u4E0D\u5B58\u5728: ${p.exePath}`);\n }\n\n // \u542F\u52A8\u524D\u5E42\u7B49\u786E\u4FDD\u9065\u6D4B\u5F00\u5173\uFF08\u5B89\u88C5\u9636\u6BB5\u4E5F\u4F1A\u5199\uFF1B\u65E7\u76EE\u5F55\u9996\u6B21\u542F\u52A8\u65F6\u8865\u4E0A\uFF09\n ensureEmitServerTelemetry(p.bdsPath, log);\n\n log.info(\"\u6B63\u5728\u542F\u52A8 BDS...\");\n const child = spawn(p.exePath, [], {\n cwd: p.bdsPath,\n stdio: options.detached ? \"ignore\" : [\"pipe\", \"pipe\", \"pipe\"],\n detached: options.detached,\n windowsHide: true,\n });\n\n if (options.detached) child.unref();\n\n p.process = child;\n writePid(child.pid ?? 0);\n log.info(`BDS \u5DF2\u542F\u52A8 (PID: ${child.pid})`);\n\n child.stdout?.on(\"data\", (chunk: Buffer) => {\n const text = chunk.toString();\n events.emit(\"output\", text);\n if (isMain()) process.stdout.write(text);\n });\n child.stderr?.on(\"data\", (chunk: Buffer) => {\n const text = `[STDERR] ${chunk.toString()}`;\n events.emit(\"output\", text);\n if (isMain()) process.stderr.write(text);\n });\n\n child.on(\"exit\", (code) => {\n log.info(`BDS \u5DF2\u9000\u51FA (code: ${code})`);\n clearPid();\n p.process = null;\n if (!p.isManualStop && p.crashRestart && isMain()) {\n log.info(`BDS \u610F\u5916\u9000\u51FA\uFF0C${p.crashDelayMs / 1000}s \u540E\u81EA\u52A8\u91CD\u542F...`);\n setTimeout(() => {\n start().catch((e) => log.error(`\u81EA\u52A8\u91CD\u542F\u5931\u8D25: ${e.message}`));\n }, p.crashDelayMs);\n }\n p.isManualStop = false;\n });\n };\n\n const status = async (): Promise<boolean> => {\n const pid = readPid();\n const alive = pid > 0 && (await isAlive(pid));\n if (alive) {\n console.log(`BDS \u8FD0\u884C\u4E2D (PID: ${pid})`);\n } else {\n console.log(\"BDS \u672A\u8FD0\u884C\");\n clearPid();\n }\n return alive;\n };\n\n const watch = async (): Promise<void> => {\n log.info(\"\u542F\u52A8\u76D1\u62A4\u6A21\u5F0F...\");\n while (true) {\n const pid = readPid();\n if (!pid || !(await isAlive(pid))) {\n try { await start(); }\n catch (e) { log.error(`\u81EA\u52A8\u542F\u52A8\u5931\u8D25: ${(e as Error).message}`); }\n }\n await new Promise((r) => setTimeout(r, 5_000));\n }\n };\n\n const getPid = (): number => {\n const p = ensureProc();\n return p.process?.pid ?? readPid();\n };\n\n return {\n start,\n stop,\n status,\n sendCommand,\n watch,\n events,\n isManualStop: false,\n getPid,\n };\n}\n\nexport const bdsEvents: EventEmitter = new EventEmitter();\nexport const bdsEvents_enabled = (): void => { bdsEvents.setMaxListeners(100); };\nbdsEvents_enabled();\n\n/** \u5224\u65AD\u5F53\u524D\u662F\u5426\u4F5C\u4E3A CLI \u4E3B\u5165\u53E3\u8FD0\u884C\uFF08\u88AB check-update \u7B49 import \u65F6\u4E3A false\uFF09 */\nfunction isMain(): boolean {\n // sfmc supervisor \u901A\u8FC7 SFMC_SERVICE \u62C9\u8D77\u5B50\u8FDB\u7A0B\u65F6\u4F18\u5148\u5224\u5B9A\uFF08\u4E0E check-update \u5BF9\u79F0\uFF09\n if (process.env.SFMC_SERVICE === \"manager\") return true;\n return isMainModule(import.meta.url);\n}\n\n// CLI \u5165\u53E3\nif (isMain()) {\n const cmd = process.argv[2];\n const bds = createBdsManager();\n const args = process.argv.slice(3);\n\n switch (cmd) {\n case \"start\":\n bds.start().catch((e) => log.error(`\u542F\u52A8\u5931\u8D25: ${e.message}`));\n break;\n case \"stop\":\n bds.stop().catch((e) => log.error(`\u505C\u6B62\u5931\u8D25: ${e.message}`));\n break;\n case \"restart\":\n bds.stop()\n .then(() => bds.start())\n .catch((e) => log.error(`\u91CD\u542F\u5931\u8D25: ${e.message}`));\n break;\n case \"status\":\n bds.status();\n break;\n case \"send\":\n if (args[0]) bds.sendCommand(args.join(\" \"));\n else console.log(\"\u7528\u6CD5: node bds-manager.js send <command>\");\n break;\n case \"watch\":\n bds.watch().catch((e) => log.error(`\u76D1\u62A4\u5F02\u5E38: ${e.message}`));\n break;\n default:\n console.log(`\u7528\u6CD5:\n start \u542F\u52A8 BDS\n stop \u505C\u6B62\n restart \u91CD\u542F\n status \u68C0\u67E5\u72B6\u6001\n send <cmd> \u53D1\u9001\u547D\u4EE4\n watch \u76D1\u62A4\u6A21\u5F0F\uFF08\u5D29\u6E83\u81EA\u52A8\u91CD\u542F\uFF09`);\n }\n}\n"],
|
|
5
|
-
"mappings": "AAUA,SAAS,
|
|
4
|
+
"sourcesContent": ["/**\n * bds-manager.ts \u2014 Bedrock Dedicated Server (BDS) \u8FDB\u7A0B\u751F\u547D\u5468\u671F\u7BA1\u7406\u5668\n *\n * \u6838\u5FC3\u804C\u8D23\uFF1A\n * - \u4F18\u96C5\u505C\u670D\uFF1A\u5411\u6807\u51C6\u8F93\u5165\u53D1\u9001 `stop` \u547D\u4EE4\u7B49\u5F85\u81EA\u7136\u9000\u51FA\uFF0C\u8D85\u65F6\u540E\u9010\u6B65\u5347\u7EA7\u81F3 SIGTERM \u4E0E SIGKILL\n * - \u5B88\u62A4\u76D1\u89C6\uFF08watch\uFF09\uFF1A\u76D1\u542C\u8FDB\u7A0B\u9000\u51FA\u4E8B\u4EF6\u5E76\u5728\u5F02\u5E38\u9000\u51FA\u65F6\u81EA\u52A8\u5EF6\u65F6\u91CD\u542F\u6062\u590D\n * - \u751F\u547D\u5468\u671F\u4E8B\u4EF6\u5E7F\u64AD\uFF1A\u5206\u53D1\u542F\u52A8\u3001\u505C\u6B62\u3001\u5D29\u6E83\u7B49\u4E8B\u4EF6\uFF0C\u89E6\u53D1\u8FD0\u7EF4\u544A\u8B66\u4E0E\u72B6\u6001\u540C\u6B65\n * - \u7BA1\u9053\u4EA4\u4E92\u4E0E\u63A2\u6D3B\uFF1A\u68C0\u6D4B\u8FDB\u7A0B\u5B58\u6D3B\u72B6\u6001\u5E76\u5411 BDS stdin \u6295\u9012\u63A7\u5236\u53F0\u6307\u4EE4\n */\n\nimport { spawn } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { EventEmitter } from \"node:events\";\nimport { bdsExePath, bdsSpawnEnvExtra, ensureBdsExecutable } from \"./host-platform.js\";\nimport { isMainModule } from \"./is-main.js\";\nimport { loadConfig, ROOT_DIR } from \"./paths.js\";\nimport { log } from \"./log.js\";\nimport { postBdsLifecycleEvent } from \"./qq-events-notify.js\";\nimport { ensureEmitServerTelemetry, ensureNetherNetTransport, localizeServerProperties } from \"./server-properties.js\";\nimport {\n clearBdsPidFile,\n findBedrockServerPids,\n isProcessAlive,\n isProcessAliveSync,\n killBedrockServerByImage,\n readBdsPidFile,\n writeBdsPidFile,\n} from \"./process-probe.js\";\n\n/** BDS \u8FDB\u7A0B\u7BA1\u7406\u5668\u7EDF\u4E00\u64CD\u4F5C\u63A5\u53E3\u3002 */\nexport interface BdsManager {\n /** \u542F\u52A8 BDS \u670D\u52A1\u8FDB\u7A0B\u3002 */\n start(): Promise<void>;\n /** \u4F18\u96C5\u505C\u6B62 BDS \u670D\u52A1\u8FDB\u7A0B\u3002 */\n stop(): Promise<void>;\n /** \u67E5\u8BE2 BDS \u8FDB\u7A0B\u5F53\u524D\u662F\u5426\u5904\u4E8E\u8FD0\u884C\u72B6\u6001\u3002 */\n status(): Promise<boolean>;\n /** \u5411 BDS \u63A7\u5236\u53F0\u7BA1\u9053\u53D1\u9001\u6307\u4EE4\u3002 */\n sendCommand(cmd: string): boolean;\n /** \u5F00\u542F\u5B88\u62A4\u76D1\u89C6\u6A21\u5F0F\uFF08\u5F02\u5E38\u9000\u51FA\u65F6\u81EA\u52A8\u62C9\u8D77\uFF09\u3002 */\n watch(): Promise<void>;\n /** \u8FDB\u7A0B\u751F\u547D\u5468\u671F\u4E8B\u4EF6\u53D1\u5C04\u5668\u3002 */\n events: EventEmitter;\n /** \u5F53\u524D\u662F\u5426\u5C5E\u4E8E\u4E3B\u52A8\u8C03\u7528\u505C\u670D\u3002 */\n isManualStop: boolean;\n /** \u83B7\u53D6\u5F53\u524D\u8FD0\u884C\u4E2D BDS \u8FDB\u7A0B\u7684 PID\u3002 */\n getPid(): number;\n}\n\n/** BDS \u8FDB\u7A0B\u542F\u52A8\u9009\u9879\u3002 */\nexport interface BdsManagerOptions {\n detached?: boolean;\n}\n\n\ninterface CachedProc {\n process: ReturnType<typeof spawn> | null;\n isManualStop: boolean;\n crashRestart: boolean;\n crashDelayMs: number;\n exePath: string;\n bdsPath: string;\n}\n\nlet cached: CachedProc | null = null;\n\nfunction ensureProc(): CachedProc {\n if (cached) return cached;\n const cfg = loadConfig();\n const bds_path = path.resolve(cfg.bds_path || process.cwd());\n const exePath = bdsExePath(bds_path);\n\n cached = {\n process: null,\n isManualStop: false,\n crashRestart: cfg.crash_restart !== false,\n crashDelayMs: (cfg.crash_restart_delay ?? 5) * 1000,\n exePath,\n bdsPath: bds_path,\n };\n return cached;\n}\n\nexport function createBdsManager(options: BdsManagerOptions = {}): BdsManager {\n const events = new EventEmitter();\n events.setMaxListeners(100);\n\n const sendCommand = (cmd: string): boolean => {\n const p = ensureProc();\n const pid = readBdsPidFile(ROOT_DIR);\n if (!pid || !isProcessAliveSync(pid)) {\n log.warn(\"BDS \u672A\u8FD0\u884C\");\n return false;\n }\n if (p.process && p.process.pid === pid && p.process.stdin) {\n try {\n p.process.stdin.write(cmd + \"\\n\");\n log.info(`\u5DF2\u53D1\u9001\u547D\u4EE4: ${cmd}`);\n return true;\n } catch {\n return false;\n }\n }\n log.warn(\"BDS \u7531\u5916\u90E8\u542F\u52A8\uFF0C\u65E0\u6CD5\u53D1\u9001\u547D\u4EE4\u5230 stdin\");\n return false;\n };\n\n const stop = async (): Promise<void> => {\n const p = ensureProc();\n // \u65E0\u8BBA\u81EA\u7BA1 / \u5916\u90E8\u8FDB\u7A0B\uFF0C\u624B\u52A8 stop \u90FD\u4E0D\u62A5 crash\n p.isManualStop = true;\n const pid = readBdsPidFile(ROOT_DIR);\n if (!pid || !(await isProcessAlive(pid))) {\n const externalPids = await findBedrockServerPids();\n if (externalPids.length > 0) {\n log.info(\"\u68C0\u6D4B\u5230\u5916\u90E8\u8FD0\u884C\u7684 BDS \u8FDB\u7A0B\uFF0C\u4F7F\u7528 taskkill \u7EC8\u6B62...\");\n await killBedrockServerByImage();\n clearBdsPidFile(ROOT_DIR);\n p.process = null;\n log.info(\"BDS \u5DF2\u505C\u6B62\");\n void postBdsLifecycleEvent(\"stop\");\n return;\n }\n log.info(\"BDS \u672A\u8FD0\u884C\");\n clearBdsPidFile(ROOT_DIR);\n p.isManualStop = false;\n return;\n }\n\n if (p.process && p.process.pid === pid && p.process.stdin) {\n log.info(\"\u6B63\u5728\u5173\u95ED BDS...\");\n try {\n p.process.stdin.write(\"stop\\n\");\n } catch (e) {\n log.warn(`\u53D1\u9001 stop \u547D\u4EE4\u5931\u8D25: ${(e as Error).message}`);\n }\n\n await Promise.race([\n new Promise<void>((resolve) => p.process?.once(\"exit\", () => resolve())),\n new Promise<void>((resolve) =>\n setTimeout(() => {\n log.warn(\"30s \u8D85\u65F6\uFF0C\u5F3A\u5236\u7EC8\u6B62...\");\n try {\n p.process?.kill(\"SIGTERM\");\n } catch {}\n setTimeout(resolve, 5000);\n }, 30_000)\n ),\n ]);\n\n if (p.process && p.process.exitCode === null) {\n log.warn(\"\u5F3A\u5236\u7ED3\u675F\u8FDB\u7A0B...\");\n try {\n p.process.kill(\"SIGKILL\");\n } catch {}\n }\n } else {\n // \u5916\u90E8\u542F\u52A8\u7684 BDS \u2014 fallback \u4F7F\u7528 taskkill / pkill\n log.info(\"BDS \u7531\u5916\u90E8\u542F\u52A8\uFF0C\u4F7F\u7528 taskkill...\");\n await killBedrockServerByImage();\n void postBdsLifecycleEvent(\"stop\");\n }\n\n clearBdsPidFile(ROOT_DIR);\n p.process = null;\n log.info(\"BDS \u5DF2\u505C\u6B62\");\n };\n\n const start = async (): Promise<void> => {\n const p = ensureProc();\n const existing = readBdsPidFile(ROOT_DIR);\n if (existing && (await isProcessAlive(existing))) {\n log.info(\"BDS \u5DF2\u5728\u8FD0\u884C\u4E2D\");\n return;\n }\n\n if (!fs.existsSync(p.exePath)) {\n log.error(`\u672A\u627E\u5230 ${p.exePath}`);\n throw new Error(`BDS \u53EF\u6267\u884C\u6587\u4EF6\u4E0D\u5B58\u5728: ${p.exePath}`);\n }\n\n // \u542F\u52A8\u524D\u8865\u9F50\u5FC5\u8981\u914D\u7F6E\u5E76\u672C\u5730\u5316\u6CE8\u91CA\uFF08\u5B89\u88C5\u9636\u6BB5\u4E5F\u4F1A\u5199\uFF1B\u65E7\u76EE\u5F55\u9996\u6B21\u542F\u52A8\u65F6\u8865\u4E0A\uFF09\n ensureEmitServerTelemetry(p.bdsPath, log);\n ensureNetherNetTransport(p.bdsPath, log);\n localizeServerProperties(p.bdsPath, { logger: log, rootDir: ROOT_DIR });\n ensureBdsExecutable(p.exePath);\n\n log.info(\"\u6B63\u5728\u542F\u52A8 BDS...\");\n p.isManualStop = false;\n const child = spawn(p.exePath, [], {\n cwd: p.bdsPath,\n env: { ...process.env, ...bdsSpawnEnvExtra(p.bdsPath) },\n stdio: options.detached ? \"ignore\" : [\"pipe\", \"pipe\", \"pipe\"],\n detached: options.detached,\n windowsHide: true,\n });\n\n if (options.detached) child.unref();\n\n p.process = child;\n writeBdsPidFile(child.pid ?? 0, ROOT_DIR);\n log.info(`BDS \u5DF2\u542F\u52A8 (PID: ${child.pid})`);\n void postBdsLifecycleEvent(\"start\", `pid=${child.pid ?? 0}`);\n\n child.stdout?.on(\"data\", (chunk: Buffer) => {\n const text = chunk.toString();\n events.emit(\"output\", text);\n if (isMain()) process.stdout.write(text);\n });\n child.stderr?.on(\"data\", (chunk: Buffer) => {\n const text = `[STDERR] ${chunk.toString()}`;\n events.emit(\"output\", text);\n if (isMain()) process.stderr.write(text);\n });\n\n child.on(\"exit\", (code) => {\n log.info(`BDS \u5DF2\u9000\u51FA (code: ${code})`);\n clearBdsPidFile(ROOT_DIR);\n p.process = null;\n const wasManual = p.isManualStop;\n if (!wasManual) {\n void postBdsLifecycleEvent(\"crash\", `code=${code ?? \"?\"}`);\n } else {\n void postBdsLifecycleEvent(\"stop\");\n }\n if (!wasManual && p.crashRestart && isMain()) {\n log.info(`BDS \u610F\u5916\u9000\u51FA\uFF0C${p.crashDelayMs / 1000}s \u540E\u81EA\u52A8\u91CD\u542F...`);\n setTimeout(() => {\n start().catch((e) => log.error(`\u81EA\u52A8\u91CD\u542F\u5931\u8D25: ${e.message}`));\n }, p.crashDelayMs);\n }\n p.isManualStop = false;\n });\n };\n\n const status = async (): Promise<boolean> => {\n const pid = readBdsPidFile(ROOT_DIR);\n const alive = pid > 0 && (await isProcessAlive(pid));\n if (alive) {\n console.log(`BDS \u8FD0\u884C\u4E2D (PID: ${pid})`);\n } else {\n console.log(\"BDS \u672A\u8FD0\u884C\");\n clearBdsPidFile(ROOT_DIR);\n }\n return alive;\n };\n\n const watch = async (): Promise<void> => {\n log.info(\"\u542F\u52A8\u76D1\u62A4\u6A21\u5F0F...\");\n while (true) {\n const pid = readBdsPidFile(ROOT_DIR);\n if (!pid || !(await isProcessAlive(pid))) {\n try {\n await start();\n } catch (e) {\n log.error(`\u81EA\u52A8\u542F\u52A8\u5931\u8D25: ${(e as Error).message}`);\n }\n }\n await new Promise((r) => setTimeout(r, 5_000));\n }\n };\n\n const getPid = (): number => {\n const p = ensureProc();\n return p.process?.pid ?? readBdsPidFile(ROOT_DIR);\n };\n\n return {\n start,\n stop,\n status,\n sendCommand,\n watch,\n events,\n isManualStop: false,\n getPid,\n };\n}\n\nexport const bdsEvents: EventEmitter = new EventEmitter();\nexport const bdsEvents_enabled = (): void => {\n bdsEvents.setMaxListeners(100);\n};\nbdsEvents_enabled();\n\n/** \u5224\u65AD\u5F53\u524D\u662F\u5426\u4F5C\u4E3A CLI \u4E3B\u5165\u53E3\u8FD0\u884C\uFF08\u88AB check-update \u7B49 import \u65F6\u4E3A false\uFF09 */\nfunction isMain(): boolean {\n // sfmc supervisor \u901A\u8FC7 SFMC_SERVICE \u62C9\u8D77\u5B50\u8FDB\u7A0B\u65F6\u4F18\u5148\u5224\u5B9A\uFF08\u4E0E check-update \u5BF9\u79F0\uFF09\n if (process.env.SFMC_SERVICE === \"manager\") return true;\n return isMainModule(import.meta.url);\n}\n\n// CLI \u5165\u53E3\nif (isMain()) {\n const cmd = process.argv[2];\n const bds = createBdsManager();\n const args = process.argv.slice(3);\n\n switch (cmd) {\n case \"start\":\n bds.start().catch((e) => log.error(`\u542F\u52A8\u5931\u8D25: ${e.message}`));\n break;\n case \"stop\":\n bds.stop().catch((e) => log.error(`\u505C\u6B62\u5931\u8D25: ${e.message}`));\n break;\n case \"restart\":\n bds\n .stop()\n .then(() => bds.start())\n .catch((e) => log.error(`\u91CD\u542F\u5931\u8D25: ${e.message}`));\n break;\n case \"status\":\n bds.status();\n break;\n case \"send\":\n if (args[0]) bds.sendCommand(args.join(\" \"));\n else console.log(\"\u7528\u6CD5: node bds-manager.js send <command>\");\n break;\n case \"watch\":\n bds.watch().catch((e) => log.error(`\u76D1\u62A4\u5F02\u5E38: ${e.message}`));\n break;\n default:\n console.log(`\u7528\u6CD5:\n start \u542F\u52A8 BDS\n stop \u505C\u6B62\n restart \u91CD\u542F\n status \u68C0\u67E5\u72B6\u6001\n send <cmd> \u53D1\u9001\u547D\u4EE4\n watch \u76D1\u62A4\u6A21\u5F0F\uFF08\u5D29\u6E83\u81EA\u52A8\u91CD\u542F\uFF09`);\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/changelog.d.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* changelog.ts — 更新日志抓取 (官方页面)
|
|
3
|
-
*/
|
|
1
|
+
/** changelog.ts — 按 BDS 版本查找 Minecraft 官方更新日志。 */
|
|
4
2
|
import type { ChangelogPayload } from "./types.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
interface FeedbackArticle {
|
|
4
|
+
title: string;
|
|
5
|
+
html_url: string;
|
|
6
|
+
body: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function changelogSectionUrl(channel: string): string;
|
|
9
|
+
export declare function findMatchingChangelogArticle(articles: FeedbackArticle[], channel: string, version: string): FeedbackArticle | null;
|
|
10
|
+
/** 只返回与目标版本匹配的文章;找不到时由调用方给出对应频道的目录。 */
|
|
11
|
+
export declare function fetchChangelog(channel: string, version: string): Promise<ChangelogPayload | null>;
|
|
12
|
+
export {};
|
|
9
13
|
//# sourceMappingURL=changelog.d.ts.map
|
package/dist/changelog.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"changelog.d.ts","sourceRoot":"","sources":["../src/changelog.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"changelog.d.ts","sourceRoot":"","sources":["../src/changelog.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAKlD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAMnD,UAAU,eAAe;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAOD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAG3D;AAuBD,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,eAAe,EAAE,EAC3B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,eAAe,GAAG,IAAI,CAYxB;AAED,uCAAuC;AACvC,wBAAsB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAoCvG"}
|
package/dist/changelog.js
CHANGED
|
@@ -1,48 +1,74 @@
|
|
|
1
1
|
import { parse as parseHtml } from "node-html-parser";
|
|
2
|
-
import {
|
|
2
|
+
import { httpGetJson, httpRequest } from "./http.js";
|
|
3
3
|
import { log } from "./log.js";
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
const FEEDBACK_BASE = "https://feedback.minecraft.net";
|
|
5
|
+
const RELEASE_SECTION = "360001186971";
|
|
6
|
+
const PREVIEW_SECTION = "360001185332";
|
|
7
|
+
function changelogSectionUrl(channel) {
|
|
8
|
+
const section = channel === "preview" ? PREVIEW_SECTION : RELEASE_SECTION;
|
|
9
|
+
return `${FEEDBACK_BASE}/hc/en-us/sections/${section}`;
|
|
7
10
|
}
|
|
8
|
-
|
|
11
|
+
function versionCandidates(version, channel) {
|
|
12
|
+
const parts = version.split(".");
|
|
13
|
+
if (parts[0] === "1" && Number(parts[1]) >= 26) parts.shift();
|
|
14
|
+
const full = parts.join(".");
|
|
15
|
+
if (channel === "preview") return [full];
|
|
16
|
+
return [full, parts.slice(0, 3).join("."), parts.slice(0, 2).join(".")];
|
|
17
|
+
}
|
|
18
|
+
function articleVersions(title) {
|
|
19
|
+
const found = [];
|
|
20
|
+
for (const match of title.matchAll(/\b(?:1\.)?\d+\.\d+(?:\.\d+)?(?:\/\d+)*\b/g)) {
|
|
21
|
+
const [first, ...rest] = match[0].split("/");
|
|
22
|
+
found.push(first);
|
|
23
|
+
const prefix = first.slice(0, first.lastIndexOf(".") + 1);
|
|
24
|
+
for (const suffix of rest) found.push(prefix + suffix);
|
|
25
|
+
}
|
|
26
|
+
return found;
|
|
27
|
+
}
|
|
28
|
+
function findMatchingChangelogArticle(articles, channel, version) {
|
|
29
|
+
const candidates = versionCandidates(version, channel);
|
|
30
|
+
return articles.find((article) => {
|
|
31
|
+
const title = article.title.toLowerCase();
|
|
32
|
+
const expectedChannel = channel === "preview" ? /\b(beta|preview)\b/.test(title) : /\bbedrock\b/.test(title) && !/\b(beta|preview)\b/.test(title);
|
|
33
|
+
return expectedChannel && articleVersions(title).some((v) => candidates.includes(v));
|
|
34
|
+
}) ?? null;
|
|
35
|
+
}
|
|
36
|
+
async function fetchChangelog(channel, version) {
|
|
37
|
+
const section = channel === "preview" ? PREVIEW_SECTION : RELEASE_SECTION;
|
|
9
38
|
try {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
void res;
|
|
30
|
-
const { httpRequest } = await import("./http.js");
|
|
31
|
-
const img = await httpRequest(imgUrl);
|
|
32
|
-
imageBase64 = img.body.toString("base64");
|
|
33
|
-
} catch (e) {
|
|
34
|
-
log.warn(`\u83B7\u53D6\u56FE\u7247\u5931\u8D25: ${e.message}`);
|
|
39
|
+
for (let page = 1; page <= 5; page++) {
|
|
40
|
+
const url = `${FEEDBACK_BASE}/api/v2/help_center/en-us/sections/${section}/articles.json?per_page=10&page=${page}`;
|
|
41
|
+
const result = await httpGetJson(url, { totalTimeoutMs: 15e3 });
|
|
42
|
+
const article = findMatchingChangelogArticle(result.articles, channel, version);
|
|
43
|
+
if (article) {
|
|
44
|
+
const root = parseHtml(article.body);
|
|
45
|
+
const paragraphs = root.querySelectorAll("p, h2, h3, li").map((el) => el.text.trim()).filter(Boolean);
|
|
46
|
+
const text = paragraphs.join("\n").slice(0, 2e3);
|
|
47
|
+
if (!text) return null;
|
|
48
|
+
let imageBase64 = null;
|
|
49
|
+
const imgSrc = root.querySelector("img")?.getAttribute("src");
|
|
50
|
+
if (imgSrc) {
|
|
51
|
+
try {
|
|
52
|
+
const imgUrl = new URL(imgSrc, article.html_url).href;
|
|
53
|
+
const image = await httpRequest(imgUrl, { totalTimeoutMs: 15e3 });
|
|
54
|
+
imageBase64 = image.body.toString("base64");
|
|
55
|
+
} catch (e) {
|
|
56
|
+
log.warn(`\u83B7\u53D6\u66F4\u65B0\u65E5\u5FD7\u56FE\u7247\u5931\u8D25: ${e.message}`);
|
|
57
|
+
}
|
|
35
58
|
}
|
|
59
|
+
return { text, imageBase64, url: article.html_url };
|
|
36
60
|
}
|
|
61
|
+
if (!result.next_page) break;
|
|
37
62
|
}
|
|
38
|
-
|
|
63
|
+
log.warn(`\u672A\u627E\u5230 BDS ${version} (${channel}) \u5BF9\u5E94\u7684\u5B98\u65B9\u66F4\u65B0\u65E5\u5FD7`);
|
|
39
64
|
} catch (e) {
|
|
40
65
|
log.warn(`\u83B7\u53D6\u66F4\u65B0\u65E5\u5FD7\u5931\u8D25: ${e.message}`);
|
|
41
|
-
return null;
|
|
42
66
|
}
|
|
67
|
+
return null;
|
|
43
68
|
}
|
|
44
69
|
export {
|
|
45
|
-
|
|
46
|
-
fetchChangelog
|
|
70
|
+
changelogSectionUrl,
|
|
71
|
+
fetchChangelog,
|
|
72
|
+
findMatchingChangelogArticle
|
|
47
73
|
};
|
|
48
74
|
//# sourceMappingURL=changelog.js.map
|
package/dist/changelog.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/changelog.ts"],
|
|
4
|
-
"sourcesContent": ["
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["/** changelog.ts \u2014 \u6309 BDS \u7248\u672C\u67E5\u627E Minecraft \u5B98\u65B9\u66F4\u65B0\u65E5\u5FD7\u3002 */\n\nimport { parse as parseHtml } from \"node-html-parser\";\nimport { httpGetJson, httpRequest } from \"./http.js\";\nimport { log } from \"./log.js\";\nimport type { ChangelogPayload } from \"./types.js\";\n\nconst FEEDBACK_BASE = \"https://feedback.minecraft.net\";\nconst RELEASE_SECTION = \"360001186971\";\nconst PREVIEW_SECTION = \"360001185332\";\n\ninterface FeedbackArticle {\n title: string;\n html_url: string;\n body: string;\n}\n\ninterface FeedbackPage {\n articles: FeedbackArticle[];\n next_page: string | null;\n}\n\nexport function changelogSectionUrl(channel: string): string {\n const section = channel === \"preview\" ? PREVIEW_SECTION : RELEASE_SECTION;\n return `${FEEDBACK_BASE}/hc/en-us/sections/${section}`;\n}\n\n/** BDS \u7248\u672C\u591A\u4E00\u4E2A\u6784\u5EFA\u53F7\uFF1B\u6B63\u5F0F\u7248\u6587\u7AE0\u901A\u5E38\u53EA\u5199\u524D\u4E09\u6BB5\uFF0C\u9884\u89C8\u7248\u9700\u4FDD\u7559\u6784\u5EFA\u53F7\u3002 */\nfunction versionCandidates(version: string, channel: string): string[] {\n const parts = version.split(\".\");\n if (parts[0] === \"1\" && Number(parts[1]) >= 26) parts.shift();\n const full = parts.join(\".\");\n if (channel === \"preview\") return [full];\n return [full, parts.slice(0, 3).join(\".\"), parts.slice(0, 2).join(\".\")];\n}\n\n/** \u517C\u5BB9 26.44/45 \u548C 1.21.93/94 \u8FD9\u7C7B\u5408\u5E76\u53D1\u5E03\u7684\u5B98\u65B9\u6807\u9898\u3002 */\nfunction articleVersions(title: string): string[] {\n const found: string[] = [];\n for (const match of title.matchAll(/\\b(?:1\\.)?\\d+\\.\\d+(?:\\.\\d+)?(?:\\/\\d+)*\\b/g)) {\n const [first, ...rest] = match[0].split(\"/\");\n found.push(first!);\n const prefix = first!.slice(0, first!.lastIndexOf(\".\") + 1);\n for (const suffix of rest) found.push(prefix + suffix);\n }\n return found;\n}\n\nexport function findMatchingChangelogArticle(\n articles: FeedbackArticle[],\n channel: string,\n version: string\n): FeedbackArticle | null {\n const candidates = versionCandidates(version, channel);\n return (\n articles.find((article) => {\n const title = article.title.toLowerCase();\n const expectedChannel =\n channel === \"preview\"\n ? /\\b(beta|preview)\\b/.test(title)\n : /\\bbedrock\\b/.test(title) && !/\\b(beta|preview)\\b/.test(title);\n return expectedChannel && articleVersions(title).some((v) => candidates.includes(v));\n }) ?? null\n );\n}\n\n/** \u53EA\u8FD4\u56DE\u4E0E\u76EE\u6807\u7248\u672C\u5339\u914D\u7684\u6587\u7AE0\uFF1B\u627E\u4E0D\u5230\u65F6\u7531\u8C03\u7528\u65B9\u7ED9\u51FA\u5BF9\u5E94\u9891\u9053\u7684\u76EE\u5F55\u3002 */\nexport async function fetchChangelog(channel: string, version: string): Promise<ChangelogPayload | null> {\n const section = channel === \"preview\" ? PREVIEW_SECTION : RELEASE_SECTION;\n try {\n for (let page = 1; page <= 5; page++) {\n const url = `${FEEDBACK_BASE}/api/v2/help_center/en-us/sections/${section}/articles.json?per_page=10&page=${page}`;\n const result = await httpGetJson<FeedbackPage>(url, { totalTimeoutMs: 15_000 });\n const article = findMatchingChangelogArticle(result.articles, channel, version);\n if (article) {\n const root = parseHtml(article.body);\n const paragraphs = root\n .querySelectorAll(\"p, h2, h3, li\")\n .map((el) => el.text.trim())\n .filter(Boolean);\n const text = paragraphs.join(\"\\n\").slice(0, 2000);\n if (!text) return null;\n\n let imageBase64: string | null = null;\n const imgSrc = root.querySelector(\"img\")?.getAttribute(\"src\");\n if (imgSrc) {\n try {\n const imgUrl = new URL(imgSrc, article.html_url).href;\n const image = await httpRequest(imgUrl, { totalTimeoutMs: 15_000 });\n imageBase64 = image.body.toString(\"base64\");\n } catch (e) {\n log.warn(`\u83B7\u53D6\u66F4\u65B0\u65E5\u5FD7\u56FE\u7247\u5931\u8D25: ${(e as Error).message}`);\n }\n }\n return { text, imageBase64, url: article.html_url };\n }\n if (!result.next_page) break;\n }\n log.warn(`\u672A\u627E\u5230 BDS ${version} (${channel}) \u5BF9\u5E94\u7684\u5B98\u65B9\u66F4\u65B0\u65E5\u5FD7`);\n } catch (e) {\n log.warn(`\u83B7\u53D6\u66F4\u65B0\u65E5\u5FD7\u5931\u8D25: ${(e as Error).message}`);\n }\n return null;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check-update.d.ts","sourceRoot":"","sources":["../src/check-update.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;
|
|
1
|
+
{"version":3,"file":"check-update.d.ts","sourceRoot":"","sources":["../src/check-update.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAkKH,wBAAsB,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,CA6SjD"}
|