@sfmc-bds/cli 0.2.0-beta.9 → 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 +4 -5
- package/dist/bds-runtime-version.d.ts +3 -0
- package/dist/bds-runtime-version.d.ts.map +1 -0
- package/dist/bds-runtime-version.js +15 -0
- package/dist/bds-runtime-version.js.map +7 -0
- package/dist/command-surface.d.ts.map +1 -1
- package/dist/command-surface.js +3 -1
- package/dist/command-surface.js.map +2 -2
- package/dist/commands.d.ts +57 -4
- package/dist/commands.d.ts.map +1 -1
- package/dist/commands.js +78 -75
- package/dist/commands.js.map +2 -2
- package/dist/directory-picker.d.ts +27 -0
- package/dist/directory-picker.d.ts.map +1 -0
- package/dist/directory-picker.js +112 -0
- package/dist/directory-picker.js.map +7 -0
- package/dist/i18n/locales/en.d.ts +25 -1
- package/dist/i18n/locales/en.d.ts.map +1 -1
- package/dist/i18n/locales/en.js +25 -1
- package/dist/i18n/locales/en.js.map +2 -2
- package/dist/i18n/locales/zh-CN.d.ts.map +1 -1
- package/dist/i18n/locales/zh-CN.js +26 -2
- package/dist/i18n/locales/zh-CN.js.map +2 -2
- package/dist/interactive-prompts.d.ts +1 -2
- package/dist/interactive-prompts.d.ts.map +1 -1
- package/dist/interactive-prompts.js +12 -23
- package/dist/interactive-prompts.js.map +3 -3
- package/dist/llbot-launch.d.ts +3 -3
- package/dist/llbot-launch.js.map +1 -1
- package/dist/log-filter.d.ts +18 -2
- package/dist/log-filter.d.ts.map +1 -1
- package/dist/log-filter.js +54 -4
- package/dist/log-filter.js.map +2 -2
- package/dist/log-translator.d.ts +36 -0
- package/dist/log-translator.d.ts.map +1 -0
- package/dist/log-translator.js +279 -0
- package/dist/log-translator.js.map +7 -0
- package/dist/logs.d.ts +2 -0
- package/dist/logs.d.ts.map +1 -1
- package/dist/logs.js +24 -11
- package/dist/logs.js.map +2 -2
- package/dist/main.js +5 -0
- package/dist/main.js.map +2 -2
- package/dist/module-commands.d.ts.map +1 -1
- package/dist/module-commands.js +22 -8
- package/dist/module-commands.js.map +2 -2
- package/dist/module-fingerprint.d.ts.map +1 -1
- package/dist/module-fingerprint.js +1 -0
- package/dist/module-fingerprint.js.map +2 -2
- package/dist/module-pack-build.d.ts +14 -4
- package/dist/module-pack-build.d.ts.map +1 -1
- package/dist/module-pack-build.js +81 -108
- package/dist/module-pack-build.js.map +2 -2
- package/dist/module-toggle.d.ts +1 -1
- package/dist/module-toggle.d.ts.map +1 -1
- package/dist/module-toggle.js.map +1 -1
- package/dist/pack-lifecycle.d.ts +15 -1
- package/dist/pack-lifecycle.d.ts.map +1 -1
- package/dist/pack-lifecycle.js +242 -15
- package/dist/pack-lifecycle.js.map +2 -2
- package/dist/player-session.d.ts +13 -0
- package/dist/player-session.d.ts.map +1 -0
- package/dist/player-session.js +26 -0
- package/dist/player-session.js.map +7 -0
- package/dist/registry.d.ts +1 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +4 -1
- package/dist/registry.js.map +2 -2
- package/dist/repl-windows/format-display.d.ts.map +1 -1
- package/dist/repl-windows/format-display.js +5 -1
- package/dist/repl-windows/format-display.js.map +2 -2
- package/dist/repl-windows/host.d.ts +6 -3
- package/dist/repl-windows/host.d.ts.map +1 -1
- package/dist/repl-windows/host.js +12 -6
- package/dist/repl-windows/host.js.map +2 -2
- package/dist/repl-windows/index.d.ts +1 -0
- package/dist/repl-windows/index.d.ts.map +1 -1
- package/dist/repl-windows/index.js +3 -0
- package/dist/repl-windows/index.js.map +2 -2
- package/dist/repl-windows/service-window.d.ts.map +1 -1
- package/dist/repl-windows/service-window.js +1 -0
- package/dist/repl-windows/service-window.js.map +2 -2
- package/dist/repl-windows/sfmc-window.d.ts +7 -0
- package/dist/repl-windows/sfmc-window.d.ts.map +1 -0
- package/dist/repl-windows/sfmc-window.js +28 -0
- package/dist/repl-windows/sfmc-window.js.map +7 -0
- package/dist/repl.d.ts.map +1 -1
- package/dist/repl.js +38 -13
- package/dist/repl.js.map +2 -2
- package/dist/runtime.d.ts +2 -2
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +15 -2
- package/dist/runtime.js.map +2 -2
- package/dist/send-target.d.ts +2 -0
- package/dist/send-target.d.ts.map +1 -1
- package/dist/send-target.js +6 -0
- package/dist/send-target.js.map +2 -2
- package/dist/services.d.ts +26 -1
- package/dist/services.d.ts.map +1 -1
- package/dist/services.js +179 -45
- package/dist/services.js.map +2 -2
- package/dist/sfmc-modules-root.d.ts.map +1 -1
- package/dist/sfmc-modules-root.js +6 -2
- package/dist/sfmc-modules-root.js.map +2 -2
- package/dist/ui-command.d.ts +6 -0
- package/dist/ui-command.d.ts.map +1 -0
- package/dist/ui-command.js +68 -0
- package/dist/ui-command.js.map +7 -0
- package/dist/wizard.d.ts.map +1 -1
- package/dist/wizard.js +36 -31
- package/dist/wizard.js.map +2 -2
- package/dist/world-packs.d.ts +2 -0
- package/dist/world-packs.d.ts.map +1 -1
- package/dist/world-packs.js +161 -18
- package/dist/world-packs.js.map +2 -2
- package/package.json +22 -15
- package/scripts/module-install/check-modules.mjs +178 -0
- package/scripts/module-install/fetch-module.mjs +1083 -0
- package/scripts/module-install/lib/catalog.mjs +96 -0
- package/scripts/module-install/lib/config-seeding.mjs +128 -0
- package/scripts/module-install/lib/io.mjs +37 -0
- package/scripts/module-install/lib/link-from.mjs +80 -0
- package/scripts/module-install/lib/lock.mjs +78 -0
- package/scripts/module-install/lib/npm-resolver.mjs +55 -0
- package/scripts/module-install/lib/packages.mjs +136 -0
- package/scripts/module-install/lib/paths.mjs +48 -0
- package/scripts/module-install/lib/registry-index.mjs +50 -0
package/dist/services.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../src/services.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA8B,SAAS,EAAkB,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../src/services.ts"],"names":[],"mappings":"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"}
|
package/dist/services.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
|
+
DEFAULT_BDS_UPDATER_CONFIG,
|
|
3
|
+
DEFAULT_DB_CONFIG,
|
|
4
|
+
DEFAULT_QQ_CONFIG,
|
|
2
5
|
ensureCoreConfigs,
|
|
3
6
|
loadEnsuredConfig,
|
|
4
7
|
qqRuntimeStatusPath,
|
|
5
|
-
readJson
|
|
6
|
-
DEFAULT_BDS_UPDATER_CONFIG,
|
|
7
|
-
DEFAULT_DB_CONFIG,
|
|
8
|
-
DEFAULT_QQ_CONFIG
|
|
8
|
+
readJson
|
|
9
9
|
} from "@sfmc-bds/sdk/node/config";
|
|
10
|
+
import { bdsExePath, bdsSpawnEnvExtra, ensureBdsExecutable } from "@sfmc-bds/bds-tools/host-platform";
|
|
10
11
|
import {
|
|
11
12
|
clearBdsPidFile,
|
|
12
13
|
isProcessAlive,
|
|
@@ -14,19 +15,24 @@ import {
|
|
|
14
15
|
readBdsPidFile,
|
|
15
16
|
writeBdsPidFile
|
|
16
17
|
} from "@sfmc-bds/bds-tools/process-probe";
|
|
17
|
-
import {
|
|
18
|
+
import { postBdsLifecycleEvent } from "@sfmc-bds/bds-tools/qq-events-notify";
|
|
18
19
|
import { spawn } from "node:child_process";
|
|
19
20
|
import { EventEmitter } from "node:events";
|
|
20
21
|
import fs from "node:fs";
|
|
21
22
|
import path from "node:path";
|
|
22
|
-
import {
|
|
23
|
+
import { t } from "./i18n/index.js";
|
|
23
24
|
import { resolveLlbotLaunch } from "./llbot-launch.js";
|
|
25
|
+
import { inferLevel, pushLog as pushUnifiedLog } from "./logs.js";
|
|
24
26
|
import { findNodeServicePids } from "./node-service-probe.js";
|
|
25
|
-
import { ROOT, spawnService } from "./runtime.js";
|
|
26
27
|
import { ensurePackUpdateConfigFile } from "./pack-update/index.js";
|
|
27
|
-
import {
|
|
28
|
+
import { recordBdsVersion } from "./bds-runtime-version.js";
|
|
29
|
+
import { reportBdsPlayerSession } from "./player-session.js";
|
|
30
|
+
import { ROOT, spawnService } from "./runtime.js";
|
|
28
31
|
import { ROOT as ROOT2 } from "./runtime.js";
|
|
29
|
-
|
|
32
|
+
function nonBlankOutputLines(text) {
|
|
33
|
+
return text.split(/\r?\n/).filter((line) => line.trim().length > 0);
|
|
34
|
+
}
|
|
35
|
+
const SERVICE_NAMES = ["bds", "db", "tunnel", "qq", "llbot"];
|
|
30
36
|
let argvDaemonize = false;
|
|
31
37
|
function setArgvDaemonize(on) {
|
|
32
38
|
argvDaemonize = on;
|
|
@@ -50,6 +56,22 @@ async function probeQqRuntime() {
|
|
|
50
56
|
if (!await isProcessAlive(status.pid)) return { alive: false, pid: 0 };
|
|
51
57
|
return { alive: true, pid: status.pid };
|
|
52
58
|
}
|
|
59
|
+
const tunnelRuntimePath = path.join(ROOT, ".sfmc", "tunnel.runtime.json");
|
|
60
|
+
async function probeTunnelRuntime() {
|
|
61
|
+
const pid = readJson(tunnelRuntimePath)?.pid;
|
|
62
|
+
return typeof pid === "number" && pid > 0 && await isProcessAlive(pid) ? pid : 0;
|
|
63
|
+
}
|
|
64
|
+
function writeTunnelRuntime(pid) {
|
|
65
|
+
fs.mkdirSync(path.dirname(tunnelRuntimePath), { recursive: true });
|
|
66
|
+
fs.writeFileSync(tunnelRuntimePath, JSON.stringify({ pid, startedAt: Date.now() }) + "\n");
|
|
67
|
+
}
|
|
68
|
+
function clearTunnelRuntime(pid) {
|
|
69
|
+
if (readJson(tunnelRuntimePath)?.pid !== pid) return;
|
|
70
|
+
try {
|
|
71
|
+
fs.unlinkSync(tunnelRuntimePath);
|
|
72
|
+
} catch {
|
|
73
|
+
}
|
|
74
|
+
}
|
|
53
75
|
class Service {
|
|
54
76
|
name;
|
|
55
77
|
title;
|
|
@@ -61,10 +83,17 @@ class Service {
|
|
|
61
83
|
events = new EventEmitter();
|
|
62
84
|
def;
|
|
63
85
|
manualStop = false;
|
|
86
|
+
updateInProgress = false;
|
|
87
|
+
restartTimer = null;
|
|
64
88
|
constructor(def) {
|
|
65
89
|
this.name = def.name;
|
|
66
90
|
this.title = def.title;
|
|
67
91
|
this.def = def;
|
|
92
|
+
if (def.name === "tunnel") {
|
|
93
|
+
this.events.on("output", (text, level) => {
|
|
94
|
+
pushUnifiedLog(text, "tunnel", level);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
68
97
|
}
|
|
69
98
|
get uptime() {
|
|
70
99
|
if (!this.startTime || !this.running) return "\u2014";
|
|
@@ -82,14 +111,19 @@ class Service {
|
|
|
82
111
|
this.events.emit("log", line);
|
|
83
112
|
const level = stream === "stderr" ? "error" : inferLevel(text);
|
|
84
113
|
pushUnifiedLog(text, this.name, level);
|
|
114
|
+
this.def.onLogLine?.(text);
|
|
85
115
|
}
|
|
86
116
|
async start() {
|
|
117
|
+
if (this.updateInProgress) throw new Error("BDS \u6B63\u5728\u66F4\u65B0\uFF0C\u6682\u4E0D\u53EF\u542F\u52A8");
|
|
87
118
|
if (this.running) return { status: "already" };
|
|
88
119
|
if (this.name === "bds") {
|
|
89
120
|
const probe = await probeBdsStatus({ rootDir: ROOT });
|
|
90
121
|
if (probe.state !== "stopped") {
|
|
91
122
|
throw new Error(
|
|
92
|
-
t("svc.bdsAlreadyRunning", {
|
|
123
|
+
t("svc.bdsAlreadyRunning", {
|
|
124
|
+
pid: String(probe.pid),
|
|
125
|
+
kind: t(probe.state === "managed" ? "svc.running" : "svc.runningExternal")
|
|
126
|
+
})
|
|
93
127
|
);
|
|
94
128
|
}
|
|
95
129
|
}
|
|
@@ -99,6 +133,7 @@ class Service {
|
|
|
99
133
|
return { status: "already" };
|
|
100
134
|
}
|
|
101
135
|
}
|
|
136
|
+
if (this.name === "tunnel" && await probeTunnelRuntime()) return { status: "already" };
|
|
102
137
|
if (this.def.validate) {
|
|
103
138
|
const v = this.def.validate();
|
|
104
139
|
if (v) {
|
|
@@ -109,6 +144,7 @@ class Service {
|
|
|
109
144
|
if (this.def.beforeStart) {
|
|
110
145
|
await this.def.beforeStart();
|
|
111
146
|
}
|
|
147
|
+
if (this.updateInProgress) throw new Error("BDS \u6B63\u5728\u66F4\u65B0\uFF0C\u6682\u4E0D\u53EF\u542F\u52A8");
|
|
112
148
|
this.manualStop = false;
|
|
113
149
|
const daemonize = argvDaemonize && process.platform !== "win32";
|
|
114
150
|
const spawnOpts = {
|
|
@@ -126,36 +162,86 @@ class Service {
|
|
|
126
162
|
if (this.name === "bds" && this.pid > 0) {
|
|
127
163
|
writeBdsPidFile(this.pid, ROOT);
|
|
128
164
|
}
|
|
165
|
+
if (this.name === "tunnel" && this.pid > 0) writeTunnelRuntime(this.pid);
|
|
129
166
|
this.events.emit("output", `started (PID ${this.pid})`, "info");
|
|
130
167
|
this.events.emit("state", { name: this.name, running: true, pid: this.pid });
|
|
168
|
+
let lifecycleReported = false;
|
|
169
|
+
const reportExit = (code) => {
|
|
170
|
+
if (this.name !== "bds" || lifecycleReported) return;
|
|
171
|
+
lifecycleReported = true;
|
|
172
|
+
const planned = this.manualStop || this.updateInProgress;
|
|
173
|
+
void postBdsLifecycleEvent(planned ? "stop" : "crash", planned ? void 0 : `code=${code ?? "?"}`, {
|
|
174
|
+
port: dbHealthPort
|
|
175
|
+
});
|
|
176
|
+
};
|
|
177
|
+
if (this.name === "bds") {
|
|
178
|
+
child.once("spawn", () => {
|
|
179
|
+
void postBdsLifecycleEvent("start", void 0, { port: dbHealthPort });
|
|
180
|
+
});
|
|
181
|
+
}
|
|
131
182
|
child.on("error", (e) => {
|
|
132
183
|
this.events.emit("output", `process error: ${e.message}`, "error");
|
|
184
|
+
reportExit();
|
|
133
185
|
this.cleanup();
|
|
186
|
+
this.scheduleRestart();
|
|
134
187
|
});
|
|
188
|
+
let versionLineBuffer = "";
|
|
135
189
|
child.stdout?.on("data", (d) => {
|
|
136
|
-
|
|
190
|
+
if (this.name === "bds" && this.startTime) {
|
|
191
|
+
versionLineBuffer += d.toString();
|
|
192
|
+
const versionLines = versionLineBuffer.split(/\r?\n/);
|
|
193
|
+
versionLineBuffer = (versionLines.pop() ?? "").slice(-4096);
|
|
194
|
+
for (const versionLine of versionLines) recordBdsVersion(ROOT, versionLine, this.pid, this.startTime.getTime());
|
|
195
|
+
}
|
|
196
|
+
for (const line of nonBlankOutputLines(d.toString())) {
|
|
137
197
|
this.pushLog(line, "stdout");
|
|
138
198
|
}
|
|
139
199
|
});
|
|
140
200
|
child.stderr?.on("data", (d) => {
|
|
141
|
-
for (const line of d.toString()
|
|
201
|
+
for (const line of nonBlankOutputLines(d.toString())) {
|
|
142
202
|
this.pushLog(line, "stderr");
|
|
143
203
|
}
|
|
144
204
|
});
|
|
145
205
|
child.on("exit", (code) => {
|
|
206
|
+
if (this.proc && this.proc !== child) return;
|
|
146
207
|
this.events.emit("output", `exited (code: ${code})`, "info");
|
|
208
|
+
reportExit(code);
|
|
147
209
|
this.cleanup();
|
|
148
|
-
|
|
149
|
-
setTimeout(() => {
|
|
150
|
-
void this.start();
|
|
151
|
-
}, this.def.restartDelay);
|
|
152
|
-
}
|
|
210
|
+
this.scheduleRestart();
|
|
153
211
|
});
|
|
154
212
|
return { status: "started" };
|
|
155
213
|
}
|
|
214
|
+
scheduleRestart() {
|
|
215
|
+
if (this.manualStop || this.updateInProgress || !this.def.autoRestart || this.restartTimer) return;
|
|
216
|
+
this.restartTimer = setTimeout(() => {
|
|
217
|
+
this.restartTimer = null;
|
|
218
|
+
if (!this.updateInProgress && !this.manualStop && !this.running) {
|
|
219
|
+
void this.start().catch((e) => {
|
|
220
|
+
this.events.emit("output", `restart failed: ${e.message}`, "error");
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}, this.def.restartDelay);
|
|
224
|
+
}
|
|
225
|
+
/** 更新器独立进程停服时,禁止将计划内退出当作崩溃并自动拉起。 */
|
|
226
|
+
beginUpdate() {
|
|
227
|
+
if (this.name !== "bds") return;
|
|
228
|
+
if (this.updateInProgress) throw new Error("BDS \u66F4\u65B0\u5DF2\u5728\u8FDB\u884C\u4E2D");
|
|
229
|
+
this.updateInProgress = true;
|
|
230
|
+
if (this.restartTimer) {
|
|
231
|
+
clearTimeout(this.restartTimer);
|
|
232
|
+
this.restartTimer = null;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
endUpdate() {
|
|
236
|
+
this.updateInProgress = false;
|
|
237
|
+
}
|
|
156
238
|
async stop() {
|
|
157
|
-
if (!this.proc || !this.running) return;
|
|
158
239
|
this.manualStop = true;
|
|
240
|
+
if (this.restartTimer) {
|
|
241
|
+
clearTimeout(this.restartTimer);
|
|
242
|
+
this.restartTimer = null;
|
|
243
|
+
}
|
|
244
|
+
if (!this.proc || !this.running) return;
|
|
159
245
|
this.events.emit("output", "stopping...", "info");
|
|
160
246
|
if (this.def.stopCommand && this.proc.stdin) {
|
|
161
247
|
this.proc.stdin.write(this.def.stopCommand + "\n");
|
|
@@ -182,6 +268,10 @@ class Service {
|
|
|
182
268
|
forceStop() {
|
|
183
269
|
const child = this.proc;
|
|
184
270
|
this.manualStop = true;
|
|
271
|
+
if (this.restartTimer) {
|
|
272
|
+
clearTimeout(this.restartTimer);
|
|
273
|
+
this.restartTimer = null;
|
|
274
|
+
}
|
|
185
275
|
this.cleanup();
|
|
186
276
|
if (!child) return;
|
|
187
277
|
try {
|
|
@@ -216,6 +306,7 @@ class Service {
|
|
|
216
306
|
clearBdsPidFile(ROOT);
|
|
217
307
|
}
|
|
218
308
|
}
|
|
309
|
+
if (this.name === "tunnel" && exitingPid > 0) clearTunnelRuntime(exitingPid);
|
|
219
310
|
if (wasRunning) {
|
|
220
311
|
this.events.emit("state", { name: this.name, running: false, pid: 0 });
|
|
221
312
|
}
|
|
@@ -224,29 +315,19 @@ class Service {
|
|
|
224
315
|
function createServices() {
|
|
225
316
|
ensureCoreConfigs(ROOT, ["bds_updater", "qq_config", "db_config"]);
|
|
226
317
|
ensurePackUpdateConfigFile();
|
|
227
|
-
const bdsCfg = loadEnsuredConfig(
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
"bds_updater",
|
|
231
|
-
{ ...DEFAULT_BDS_UPDATER_CONFIG }
|
|
232
|
-
);
|
|
233
|
-
const qqCfg = loadEnsuredConfig(
|
|
234
|
-
ROOT,
|
|
235
|
-
"qq_config.json",
|
|
236
|
-
"qq_config",
|
|
237
|
-
{ ...DEFAULT_QQ_CONFIG }
|
|
238
|
-
);
|
|
239
|
-
const dbCfg = loadEnsuredConfig(
|
|
240
|
-
ROOT,
|
|
241
|
-
"db_config.json",
|
|
242
|
-
"db_config",
|
|
243
|
-
{ ...DEFAULT_DB_CONFIG }
|
|
244
|
-
);
|
|
318
|
+
const bdsCfg = loadEnsuredConfig(ROOT, "bds_updater.json", "bds_updater", { ...DEFAULT_BDS_UPDATER_CONFIG });
|
|
319
|
+
const qqCfg = loadEnsuredConfig(ROOT, "qq_config.json", "qq_config", { ...DEFAULT_QQ_CONFIG });
|
|
320
|
+
const dbCfg = loadEnsuredConfig(ROOT, "db_config.json", "db_config", { ...DEFAULT_DB_CONFIG });
|
|
245
321
|
const bdsPath = bdsCfg.bds_path ?? ROOT;
|
|
246
322
|
const useLlbotBackend = qqCfg.qq_backend === "llbot";
|
|
247
323
|
const qqEnabled = qqCfg.qq_enabled !== false;
|
|
248
|
-
const
|
|
249
|
-
const
|
|
324
|
+
const qqExternal = !useLlbotBackend && qqCfg.official?.transport === "webhook";
|
|
325
|
+
const tunnelCfg = qqCfg.official?.tunnel;
|
|
326
|
+
const tunnelEnabled = qqEnabled && qqExternal && tunnelCfg?.enabled === true;
|
|
327
|
+
const tunnelHost = String(tunnelCfg?.host ?? "").trim();
|
|
328
|
+
const tunnelRemotePort = tunnelCfg?.remotePort ?? 13001;
|
|
329
|
+
const llbotEnabled = qqCfg.llbot?.enabled !== false;
|
|
330
|
+
const llbotLaunch = resolveLlbotLaunch(qqCfg.llbot?.path, qqCfg.llbot?.cwd);
|
|
250
331
|
const llbotPath = llbotLaunch.exe;
|
|
251
332
|
const llbotCwd = llbotLaunch.cwd;
|
|
252
333
|
const dbPort = dbCfg.db_port ?? 3001;
|
|
@@ -278,7 +359,11 @@ function createServices() {
|
|
|
278
359
|
await runPackUpdatesOnBdsStart();
|
|
279
360
|
const { ensurePacksReady } = await import("./pack-lifecycle.js");
|
|
280
361
|
await ensurePacksReady();
|
|
281
|
-
}
|
|
362
|
+
},
|
|
363
|
+
onLogLine: (line) => reportBdsPlayerSession(line, {
|
|
364
|
+
port: dbPort,
|
|
365
|
+
...dbCfg.http_auth ? { authToken: dbCfg.http_auth } : {}
|
|
366
|
+
})
|
|
282
367
|
}),
|
|
283
368
|
db: new Service({
|
|
284
369
|
name: "db",
|
|
@@ -290,6 +375,41 @@ function createServices() {
|
|
|
290
375
|
restartDelay: 3e3,
|
|
291
376
|
env: { DB_PORT: String(dbPort) }
|
|
292
377
|
}),
|
|
378
|
+
tunnel: new Service({
|
|
379
|
+
name: "tunnel",
|
|
380
|
+
title: "QQ SSH Tunnel",
|
|
381
|
+
cmd: "ssh",
|
|
382
|
+
args: [
|
|
383
|
+
"-N",
|
|
384
|
+
"-T",
|
|
385
|
+
"-o",
|
|
386
|
+
"BatchMode=yes",
|
|
387
|
+
"-o",
|
|
388
|
+
"ConnectTimeout=10",
|
|
389
|
+
"-o",
|
|
390
|
+
"ExitOnForwardFailure=yes",
|
|
391
|
+
"-o",
|
|
392
|
+
"ServerAliveInterval=30",
|
|
393
|
+
"-o",
|
|
394
|
+
"ServerAliveCountMax=3",
|
|
395
|
+
"-R",
|
|
396
|
+
`127.0.0.1:${tunnelRemotePort}:127.0.0.1:${dbPort}`,
|
|
397
|
+
tunnelHost
|
|
398
|
+
],
|
|
399
|
+
cwd: ROOT,
|
|
400
|
+
stopTimeout: 1e4,
|
|
401
|
+
autoRestart: true,
|
|
402
|
+
restartDelay: 5e3,
|
|
403
|
+
optional: !tunnelEnabled,
|
|
404
|
+
validate: () => {
|
|
405
|
+
if (!tunnelEnabled) return "QQ tunnel disabled (official webhook tunnel.enabled=false)";
|
|
406
|
+
if (!/^[A-Za-z0-9_][A-Za-z0-9_.-]*$/.test(tunnelHost)) return "invalid official.tunnel.host";
|
|
407
|
+
if (!Number.isInteger(tunnelRemotePort) || tunnelRemotePort < 1 || tunnelRemotePort > 65535) {
|
|
408
|
+
return "invalid official.tunnel.remotePort";
|
|
409
|
+
}
|
|
410
|
+
return null;
|
|
411
|
+
}
|
|
412
|
+
}),
|
|
293
413
|
qq: new Service({
|
|
294
414
|
name: "qq",
|
|
295
415
|
title: qqTitle,
|
|
@@ -299,12 +419,13 @@ function createServices() {
|
|
|
299
419
|
// 缺凭据时 validate 拦下,不会 spawn→exit(1)→autoRestart 死循环
|
|
300
420
|
autoRestart: true,
|
|
301
421
|
restartDelay: 3e3,
|
|
302
|
-
optional: !qqEnabled,
|
|
422
|
+
optional: !qqEnabled || qqExternal,
|
|
303
423
|
validate: () => {
|
|
304
424
|
if (!qqEnabled) return "QQ bridge disabled (qq_enabled=false)";
|
|
425
|
+
if (qqExternal) return "QQ bridge runs externally (official.transport=webhook)";
|
|
305
426
|
if (!useLlbotBackend) {
|
|
306
|
-
if (!String(qqCfg.
|
|
307
|
-
return "missing
|
|
427
|
+
if (!String(qqCfg.official?.app_id ?? "").trim() || !String(qqCfg.official?.app_secret ?? "").trim()) {
|
|
428
|
+
return "missing official.app_id / official.app_secret";
|
|
308
429
|
}
|
|
309
430
|
}
|
|
310
431
|
return null;
|
|
@@ -323,7 +444,7 @@ function createServices() {
|
|
|
323
444
|
optional: !useLlbotBackend || !llbotEnabled,
|
|
324
445
|
validate: () => {
|
|
325
446
|
if (!useLlbotBackend) return "LLBot skipped (qq_backend!=llbot)";
|
|
326
|
-
if (!llbotEnabled) return "LLBot disabled (
|
|
447
|
+
if (!llbotEnabled) return "LLBot disabled (llbot.enabled=false)";
|
|
327
448
|
if (!fs.existsSync(llbotPath)) return `not found: ${llbotPath}`;
|
|
328
449
|
return null;
|
|
329
450
|
}
|
|
@@ -335,7 +456,7 @@ function refreshServices() {
|
|
|
335
456
|
forceStopAll();
|
|
336
457
|
services = createServices();
|
|
337
458
|
}
|
|
338
|
-
const START_ORDER = ["db", "qq", "llbot", "bds"];
|
|
459
|
+
const START_ORDER = ["db", "tunnel", "qq", "llbot", "bds"];
|
|
339
460
|
async function startAll() {
|
|
340
461
|
const result = { started: [], skipped: [], failed: [] };
|
|
341
462
|
for (const name of START_ORDER) {
|
|
@@ -358,7 +479,7 @@ async function startAll() {
|
|
|
358
479
|
return result;
|
|
359
480
|
}
|
|
360
481
|
async function stopAll() {
|
|
361
|
-
const pending = [...START_ORDER].reverse().map((name) => services[name]).filter((service) => Boolean(service
|
|
482
|
+
const pending = [...START_ORDER].reverse().map((name) => services[name]).filter((service) => Boolean(service)).map((service) => service.stop());
|
|
362
483
|
await Promise.allSettled(pending);
|
|
363
484
|
}
|
|
364
485
|
function forceStopAll() {
|
|
@@ -454,6 +575,18 @@ async function queryServicesRuntime() {
|
|
|
454
575
|
}
|
|
455
576
|
}
|
|
456
577
|
}
|
|
578
|
+
} else if (name === "tunnel") {
|
|
579
|
+
const managed = await reconcileManagedAlive(service);
|
|
580
|
+
if (managed) {
|
|
581
|
+
running = true;
|
|
582
|
+
pid = service.pid;
|
|
583
|
+
uptime = service.uptime;
|
|
584
|
+
ownership = "managed";
|
|
585
|
+
} else {
|
|
586
|
+
pid = await probeTunnelRuntime();
|
|
587
|
+
running = pid > 0;
|
|
588
|
+
if (running) ownership = "external";
|
|
589
|
+
}
|
|
457
590
|
} else {
|
|
458
591
|
const managed = await reconcileManagedAlive(service);
|
|
459
592
|
if (managed) {
|
|
@@ -485,6 +618,7 @@ export {
|
|
|
485
618
|
forceStopAll,
|
|
486
619
|
getQqBackendMode,
|
|
487
620
|
isServiceRunning,
|
|
621
|
+
nonBlankOutputLines,
|
|
488
622
|
onServiceStateChange,
|
|
489
623
|
queryServicesRuntime,
|
|
490
624
|
refreshServices,
|
package/dist/services.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/services.ts"],
|
|
4
|
-
"sourcesContent": ["import type { BdsUpdaterConfig, DBConfig, QQBackend, QQBridgeConfig } from \"@sfmc-bds/sdk/node/config\";\nimport {\n ensureCoreConfigs,\n loadEnsuredConfig,\n qqRuntimeStatusPath,\n readJson,\n DEFAULT_BDS_UPDATER_CONFIG,\n DEFAULT_DB_CONFIG,\n DEFAULT_QQ_CONFIG,\n type QqRuntimeStatus,\n} from \"@sfmc-bds/sdk/node/config\";\nimport {\n clearBdsPidFile,\n isProcessAlive,\n probeBdsStatus,\n readBdsPidFile,\n writeBdsPidFile,\n} from \"@sfmc-bds/bds-tools/process-probe\";\nimport { bdsExePath, bdsSpawnEnvExtra, ensureBdsExecutable } from \"@sfmc-bds/bds-tools/host-platform\";\nimport { spawn, type ChildProcess, type IOType } from \"node:child_process\";\nimport { EventEmitter } from \"node:events\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { inferLevel, pushLog as pushUnifiedLog } from \"./logs.js\";\nimport { resolveLlbotLaunch } from \"./llbot-launch.js\";\nimport { findNodeServicePids } from \"./node-service-probe.js\";\nimport { ROOT, spawnService, type ServiceId } from \"./runtime.js\";\nimport { ensurePackUpdateConfigFile } from \"./pack-update/index.js\";\nimport { t } from \"./i18n/index.js\";\n\nexport { ROOT } from \"./runtime.js\";\n\nexport interface LogLine {\n time: Date;\n text: string;\n stream: \"stdout\" | \"stderr\";\n}\n\nexport type ServiceName = \"bds\" | \"db\" | \"qq\" | \"llbot\";\nexport const SERVICE_NAMES: ServiceName[] = [\"bds\", \"db\", \"qq\", \"llbot\"];\n\n/** argv \u4E00\u6B21\u6027 start \u5728 POSIX \u4E0A\u987B daemonize\uFF0C\u5426\u5219\u7236\u8FDB\u7A0B\u9000\u51FA\u4F1A\u5E26\u8D70\u5B50\u8FDB\u7A0B\uFF08Windows \u4E0D\u9700\u8981\uFF09 */\nlet argvDaemonize = false;\nexport function setArgvDaemonize(on: boolean): void {\n argvDaemonize = on;\n}\n\n/** \u5355\u670D\u52A1 start \u7ED3\u679C\uFF1Aoptional \u670D\u52A1 validate \u5931\u8D25\u8BB0\u4E3A skipped\uFF0C\u4E0D\u629B\u9519 */\nexport type StartOutcome =\n | { status: \"started\" }\n | { status: \"already\" }\n | { status: \"skipped\"; reason: string };\n\nexport interface StartAllResult {\n started: ServiceName[];\n skipped: Array<{ name: ServiceName; reason: string }>;\n failed: Array<{ name: ServiceName; reason: string }>;\n}\n\nexport interface ServiceStatus {\n name: ServiceName;\n title: string;\n running: boolean;\n pid: number;\n uptime: string;\n ownership?: \"managed\" | \"external\";\n}\n\n/** \u5F53\u524D db \u5065\u5EB7\u63A2\u6D4B\u7AEF\u53E3\uFF08\u4E0E createServices \u540C\u6B65\uFF09 */\nlet dbHealthPort = 3001;\n/** \u5F53\u524D QQ \u540E\u7AEF\uFF08\u4E0E createServices \u540C\u6B65\uFF0C\u4F9B Tab/\u7A97\u6807\u9898\uFF09 */\nlet qqBackendMode: QQBackend = \"official\";\n\nexport function getQqBackendMode(): QQBackend {\n return qqBackendMode;\n}\n\nasync function probeDbHealth(port: number): Promise<boolean> {\n try {\n const res = await fetch(`http://127.0.0.1:${port}/api/health`, { signal: AbortSignal.timeout(2000) });\n return res.ok;\n } catch {\n return false;\n }\n}\n\n/** \u8BFB `.sfmc/qq.runtime.json` + pid \u5B58\u6D3B\uFF0C\u63A2\u6D4B\u5916\u90E8/\u672C\u673A qq-bridge */\nasync function probeQqRuntime(): Promise<{ alive: boolean; pid: number }> {\n const status = readJson<QqRuntimeStatus>(qqRuntimeStatusPath(ROOT));\n if (!status?.pid || status.pid <= 0) return { alive: false, pid: 0 };\n if (!(await isProcessAlive(status.pid))) return { alive: false, pid: 0 };\n return { alive: true, pid: status.pid };\n}\n\ninterface ServiceDef {\n name: ServiceName;\n title: string;\n service?: ServiceId;\n cmd?: string;\n args?: string[];\n cwd: string;\n env?: Record<string, string>;\n stopCommand?: string;\n stopTimeout: number;\n autoRestart: boolean;\n restartDelay: number;\n /**\n * \u53EF\u9009\u670D\u52A1\uFF1Avalidate \u5931\u8D25\u65F6 start \u8FD4\u56DE skipped\uFF08startAll \u4E0D\u5F53\u4F5C\u5931\u8D25\uFF09\u3002\n * \u7528\u4E8E official \u4E0B\u8DF3\u8FC7 llbot\u3001\u4EE5\u53CA qq_enabled=false\u3002\n */\n optional?: boolean;\n validate?: () => string | null;\n /** \u542F\u52A8\u524D\u94A9\u5B50(\u5982 BDS \u88C5\u8F7D\u4E00\u81F4\u6027\u6821\u9A8C);\u5931\u8D25\u5219\u7981\u6B62 spawn */\n beforeStart?: () => Promise<void>;\n}\n\nclass Service {\n name: ServiceName;\n title: string;\n proc: ChildProcess | null = null;\n running = false;\n pid = 0;\n startTime: Date | null = null;\n logs: LogLine[] = [];\n events = new EventEmitter();\n\n private def: ServiceDef;\n private manualStop = false;\n\n constructor(def: ServiceDef) {\n this.name = def.name;\n this.title = def.title;\n this.def = def;\n }\n\n get uptime(): string {\n if (!this.startTime || !this.running) return \"\u2014\";\n const ms = Date.now() - this.startTime.getTime();\n const m = Math.floor(ms / 60000);\n const h = Math.floor(m / 60);\n if (h > 0) return `${h}h ${m % 60}m`;\n if (m > 0) return `${m}m`;\n return `${Math.floor(ms / 1000)}s`;\n }\n\n pushLog(text: string, stream: \"stdout\" | \"stderr\"): void {\n const line: LogLine = { time: new Date(), text, stream };\n this.logs.push(line);\n if (this.logs.length > 2000) this.logs.splice(0, this.logs.length - 2000);\n this.events.emit(\"log\", line);\n // stderr \u89C6\u4E3A error; stdout \u7528 inferLevel \u63A8\u65AD (bare \u5B50\u8FDB\u7A0B\u7EAF text \u65F6\u9ED8\u8BA4 info,\n // BDS \u7B49\u81EA\u5E26 [LEVEL] \u6807\u7B7E\u7684\u4ECD\u53EF\u6B63\u786E\u63A8\u65AD)\n const level = stream === \"stderr\" ? \"error\" : inferLevel(text);\n pushUnifiedLog(text, this.name, level);\n }\n\n async start(): Promise<StartOutcome> {\n if (this.running) return { status: \"already\" };\n if (this.name === \"bds\") {\n const probe = await probeBdsStatus({ rootDir: ROOT });\n if (probe.state !== \"stopped\") {\n throw new Error(\n t(\"svc.bdsAlreadyRunning\", { pid: String(probe.pid), kind: t(probe.state === \"managed\" ? \"svc.running\" : \"svc.runningExternal\") })\n );\n }\n }\n if (this.name === \"db\" || this.name === \"qq\") {\n const external = await findNodeServicePids(this.name);\n if (external.length > 0) {\n return { status: \"already\" };\n }\n }\n if (this.def.validate) {\n const v = this.def.validate();\n if (v) {\n // optional\uFF1A\u914D\u7F6E\u5C42\u9762\u8DF3\u8FC7\uFF08\u5982 official \u4E0B llbot\uFF09\uFF0C\u4E0D\u5F53\u5931\u8D25\u3001\u4E5F\u4E0D spawn\n if (this.def.optional) return { status: \"skipped\", reason: v };\n throw new Error(v);\n }\n }\n if (this.def.beforeStart) {\n await this.def.beforeStart();\n }\n this.manualStop = false;\n const daemonize = argvDaemonize && process.platform !== \"win32\";\n const spawnOpts = {\n cwd: this.def.cwd,\n stdio: (daemonize ? \"ignore\" : [\"pipe\", \"pipe\", \"pipe\"]) as \"ignore\" | Array<IOType>,\n env: this.def.env ? { ...process.env, ...this.def.env } : process.env,\n detached: daemonize,\n };\n const child = this.def.service\n ? spawnService(this.def.service, this.def.args ?? [], spawnOpts)\n : spawn(this.def.cmd as string, this.def.args ?? [], spawnOpts);\n if (daemonize) child.unref();\n this.proc = child;\n this.pid = child.pid ?? 0;\n this.running = true;\n this.startTime = new Date();\n if (this.name === \"bds\" && this.pid > 0) {\n writeBdsPidFile(this.pid, ROOT);\n }\n this.events.emit(\"output\", `started (PID ${this.pid})`, \"info\");\n this.events.emit(\"state\", { name: this.name, running: true, pid: this.pid });\n\n child.on(\"error\", (e) => {\n this.events.emit(\"output\", `process error: ${e.message}`, \"error\");\n this.cleanup();\n });\n\n child.stdout?.on(\"data\", (d: Buffer) => {\n for (const line of d.toString().split(\"\\n\").filter(Boolean)) {\n this.pushLog(line, \"stdout\");\n }\n });\n child.stderr?.on(\"data\", (d: Buffer) => {\n for (const line of d.toString().split(\"\\n\").filter(Boolean)) {\n this.pushLog(line, \"stderr\");\n }\n });\n\n child.on(\"exit\", (code) => {\n this.events.emit(\"output\", `exited (code: ${code})`, \"info\");\n this.cleanup();\n if (!this.manualStop && this.def.autoRestart) {\n setTimeout(() => {\n void this.start();\n }, this.def.restartDelay);\n }\n });\n return { status: \"started\" };\n }\n\n async stop(): Promise<void> {\n if (!this.proc || !this.running) return;\n this.manualStop = true;\n this.events.emit(\"output\", \"stopping...\", \"info\");\n\n if (this.def.stopCommand && this.proc.stdin) {\n this.proc.stdin.write(this.def.stopCommand + \"\\n\");\n } else {\n this.proc.kill(\"SIGTERM\");\n }\n\n return new Promise((resolve) => {\n const timeout = setTimeout(() => {\n if (this.proc) {\n this.events.emit(\"output\", \"force kill\", \"error\");\n try {\n this.proc.kill(\"SIGKILL\");\n } catch {\n /* ignore */\n }\n }\n resolve();\n }, this.def.stopTimeout);\n\n this.proc?.on(\"exit\", () => {\n clearTimeout(timeout);\n resolve();\n });\n });\n }\n\n forceStop(): void {\n const child = this.proc;\n this.manualStop = true;\n this.cleanup();\n if (!child) return;\n try {\n child.kill(\"SIGKILL\");\n } catch {\n /* ignore */\n }\n }\n\n async restart(): Promise<StartOutcome> {\n await this.stop();\n return this.start();\n }\n\n getRecentLogs(n: number): LogLine[] {\n return this.logs.slice(-n);\n }\n\n /**\n * \u63A2\u6D4B\u53D1\u73B0\u8FDB\u7A0B\u5DF2\u6B7B\u540E\u56DE\u5199\u672C\u5730\u72B6\u6001\uFF08\u4E0D\u8BBE manualStop\uFF0C\u4EE5\u4FBF exit \u56DE\u8C03\u4ECD\u53EF\u6309\u9700 autoRestart\uFF09\u3002\n */\n markStoppedFromProbe(): void {\n if (!this.running && !this.proc) return;\n this.cleanup();\n }\n\n private cleanup(): void {\n const wasRunning = this.running || this.proc !== null;\n const exitingPid = this.pid;\n this.proc = null;\n this.running = false;\n this.pid = 0;\n this.startTime = null;\n if (this.name === \"bds\" && exitingPid > 0) {\n const filePid = readBdsPidFile(ROOT);\n if (filePid === exitingPid) {\n clearBdsPidFile(ROOT);\n }\n }\n if (wasRunning) {\n this.events.emit(\"state\", { name: this.name, running: false, pid: 0 });\n }\n }\n}\n\nfunction createServices(): Record<ServiceName, Service> {\n /* \u5404\u670D\u52A1/CLI \u7528 SDK ensureCoreConfigs \u64AD\u79CD\uFF08\u542B $schema\uFF09\uFF0C\u4E0D\u518D\u4ECE configs-default \u62F7\u8D1D\u3002 */\n ensureCoreConfigs(ROOT, [\"bds_updater\", \"qq_config\", \"db_config\"]);\n ensurePackUpdateConfigFile();\n const bdsCfg = loadEnsuredConfig(\n ROOT,\n \"bds_updater.json\",\n \"bds_updater\",\n { ...DEFAULT_BDS_UPDATER_CONFIG } as Record<string, unknown>\n ) as BdsUpdaterConfig;\n const qqCfg = loadEnsuredConfig(\n ROOT,\n \"qq_config.json\",\n \"qq_config\",\n { ...DEFAULT_QQ_CONFIG } as Record<string, unknown>\n ) as QQBridgeConfig;\n const dbCfg = loadEnsuredConfig(\n ROOT,\n \"db_config.json\",\n \"db_config\",\n { ...DEFAULT_DB_CONFIG } as Record<string, unknown>\n ) as DBConfig;\n const bdsPath = bdsCfg.bds_path ?? ROOT;\n const useLlbotBackend = qqCfg.qq_backend === \"llbot\";\n const qqEnabled = qqCfg.qq_enabled !== false;\n const llbotEnabled = qqCfg.llbot_enabled !== false;\n const llbotLaunch = resolveLlbotLaunch(qqCfg.llbot_path, qqCfg.llbot_cwd);\n const llbotPath = llbotLaunch.exe;\n const llbotCwd = llbotLaunch.cwd;\n const dbPort = dbCfg.db_port ?? 3001;\n dbHealthPort = dbPort;\n qqBackendMode = useLlbotBackend ? \"llbot\" : \"official\";\n const bdsExe = bdsExePath(path.resolve(bdsPath));\n const qqTitle = useLlbotBackend ? \"QQ (llbot)\" : \"QQ (official)\";\n\n return {\n bds: new Service({\n name: \"bds\",\n title: \"BDS\",\n cmd: bdsExe,\n args: [],\n cwd: bdsPath,\n env: bdsSpawnEnvExtra(bdsPath),\n stopCommand: \"stop\",\n stopTimeout: 30000,\n autoRestart: bdsCfg.crash_restart !== false,\n restartDelay: 5000,\n validate: () => {\n if (!fs.existsSync(bdsExe)) return `not found: ${bdsExe}`;\n return null;\n },\n beforeStart: async () => {\n ensureBdsExecutable(bdsExe);\n /* \u5148\u88C5\u6536\u4EF6\u7BB1\u7B2C\u4E09\u65B9\u5305\uFF0C\u518D\u68C0\u67E5 CF \u66F4\u65B0\uFF0C\u518D\u8DD1\u6A21\u5757\u805A\u5408\u95F8\u95E8 */\n const { scanAndInstallInbox } = await import(\"./world-packs.js\");\n await scanAndInstallInbox({ interactive: false });\n const { runPackUpdatesOnBdsStart } = await import(\"./pack-update/index.js\");\n await runPackUpdatesOnBdsStart();\n const { ensurePacksReady } = await import(\"./pack-lifecycle.js\");\n await ensurePacksReady();\n },\n }),\n\n db: new Service({\n name: \"db\",\n title: \"DB Server\",\n service: \"db\",\n cwd: ROOT,\n stopTimeout: 10000,\n autoRestart: true,\n restartDelay: 3000,\n env: { DB_PORT: String(dbPort) },\n }),\n\n qq: new Service({\n name: \"qq\",\n title: qqTitle,\n service: \"qq\",\n cwd: ROOT,\n stopTimeout: 10000,\n // \u7F3A\u51ED\u636E\u65F6 validate \u62E6\u4E0B\uFF0C\u4E0D\u4F1A spawn\u2192exit(1)\u2192autoRestart \u6B7B\u5FAA\u73AF\n autoRestart: true,\n restartDelay: 3000,\n optional: !qqEnabled,\n validate: () => {\n if (!qqEnabled) return \"QQ bridge disabled (qq_enabled=false)\";\n if (!useLlbotBackend) {\n if (!String(qqCfg.qq_app_id ?? \"\").trim() || !String(qqCfg.qq_app_secret ?? \"\").trim()) {\n return \"missing qq_app_id / qq_app_secret (official backend)\";\n }\n }\n return null;\n },\n }),\n\n llbot: new Service({\n name: \"llbot\",\n title: \"LLBot\",\n cmd: llbotPath,\n args: [],\n cwd: llbotCwd,\n stopTimeout: 10000,\n autoRestart: false,\n restartDelay: 5000,\n // official \u6216\u672A\u542F\u7528\u65F6\u771F\u6B63 skip\uFF0C\u4E0D throw\n optional: !useLlbotBackend || !llbotEnabled,\n validate: () => {\n if (!useLlbotBackend) return \"LLBot skipped (qq_backend!=llbot)\";\n if (!llbotEnabled) return \"LLBot disabled (llbot_enabled=false)\";\n if (!fs.existsSync(llbotPath)) return `not found: ${llbotPath}`;\n return null;\n },\n }),\n };\n}\n\nexport let services: Record<ServiceName, Service> = createServices();\n\nexport function refreshServices(): void {\n forceStopAll();\n services = createServices();\n}\n\nexport const START_ORDER: ServiceName[] = [\"db\", \"qq\", \"llbot\", \"bds\"];\n\nexport async function startAll(): Promise<StartAllResult> {\n const result: StartAllResult = { started: [], skipped: [], failed: [] };\n for (const name of START_ORDER) {\n const svc = services[name];\n if (!svc) continue;\n try {\n const outcome = await svc.start();\n if (outcome.status === \"skipped\") {\n result.skipped.push({ name, reason: outcome.reason });\n svc.events.emit(\"output\", `skipped: ${outcome.reason}`, \"info\");\n } else {\n result.started.push(name);\n }\n } catch (e) {\n const reason = (e as Error).message;\n result.failed.push({ name, reason });\n svc.events.emit(\"output\", `start error: ${reason}`, \"error\");\n }\n }\n return result;\n}\n\nexport async function stopAll(): Promise<void> {\n const pending = [...START_ORDER]\n .reverse()\n .map((name) => services[name])\n .filter((service): service is Service => Boolean(service?.running))\n .map((service) => service.stop());\n await Promise.allSettled(pending);\n}\n\nexport function forceStopAll(): void {\n for (const service of Object.values(services)) service.forceStop();\n}\n\nexport type ServiceStateEvent = { name: ServiceName; running: boolean; pid: number };\n\n/** \u8BA2\u9605\u4EFB\u610F\u670D\u52A1\u542F\u505C\uFF08\u542B\u63A2\u6D4B\u56DE\u5199\uFF09\uFF1B\u8FD4\u56DE\u53D6\u6D88\u51FD\u6570 */\nexport function onServiceStateChange(fn: (ev: ServiceStateEvent) => void): () => void {\n const handler = (ev: ServiceStateEvent): void => {\n fn(ev);\n };\n for (const service of Object.values(services)) {\n service.events.on(\"state\", handler);\n }\n return () => {\n for (const service of Object.values(services)) {\n service.events.off(\"state\", handler);\n }\n };\n}\n\n/** \u82E5\u672C\u5730\u6807\u8BB0 running \u4F46 OS \u8FDB\u7A0B\u5DF2\u6B7B\uFF0C\u56DE\u6536\u5185\u5B58\u6807\u5FD7 */\nasync function reconcileManagedAlive(service: Service): Promise<boolean> {\n if (!service.running) return false;\n if (service.pid > 0 && !(await isProcessAlive(service.pid))) {\n service.markStoppedFromProbe();\n return false;\n }\n return service.running;\n}\n\n/**\n * \u7EDF\u4E00\u8FD0\u884C\u6001\u67E5\u8BE2\uFF08\u6743\u5A01\u5165\u53E3\uFF09\uFF1AOS/\u5065\u5EB7\u63A2\u6D4B + \u56DE\u5199 Service \u5185\u5B58\u6807\u5FD7\u3002\n * status / Tab \u53D1\u9001\u76EE\u6807 / reload \u7B49\u5747\u5E94\u8D70\u6B64\u63A5\u53E3\uFF0C\u52FF\u76F4\u63A5\u8BFB `service.running`\u3002\n */\nexport async function queryServicesRuntime(): Promise<ServiceStatus[]> {\n return Promise.all(\n SERVICE_NAMES.map(async (name) => {\n const service = services[name];\n let running = false;\n let pid = 0;\n let uptime = \"\u2014\";\n let ownership: \"managed\" | \"external\" | undefined;\n\n if (name === \"bds\") {\n const probe = await probeBdsStatus({\n managedPid: service.pid,\n hasStdin: Boolean(service.proc?.stdin),\n rootDir: ROOT,\n });\n if (probe.state === \"managed\") {\n running = true;\n pid = probe.pid;\n uptime = service.uptime;\n ownership = \"managed\";\n /* \u63A2\u6D4B\u4E3A managed \u4F46\u672C\u5730\u5DF2\u6807\u505C\uFF1A\u4FDD\u6301\u63A2\u6D4B\u7ED3\u679C\uFF0C\u4E0D\u5F3A\u5236\u6539\u5185\u5B58\uFF08stdin \u4ECD\u53EF\u7528\uFF09 */\n } else if (probe.state === \"external\") {\n running = true;\n pid = probe.pid;\n ownership = \"external\";\n /* \u5916\u90E8\u8FDB\u7A0B\uFF1A\u672C\u5730 managed \u53E5\u67C4\u5DF2\u5931\u6548\u5219\u56DE\u6536 */\n if (service.running && service.pid !== probe.pid) {\n service.markStoppedFromProbe();\n } else if (service.running && !(await isProcessAlive(service.pid))) {\n service.markStoppedFromProbe();\n }\n } else {\n running = false;\n if (service.running || service.proc) {\n service.markStoppedFromProbe();\n }\n }\n } else if (name === \"db\") {\n const managed = await reconcileManagedAlive(service);\n if (managed) {\n running = true;\n pid = service.pid;\n uptime = service.uptime;\n ownership = \"managed\";\n } else if (await probeDbHealth(dbHealthPort)) {\n running = true;\n ownership = \"external\";\n const pids = await findNodeServicePids(\"db\");\n pid = pids[0] ?? 0;\n }\n } else if (name === \"qq\") {\n const managed = await reconcileManagedAlive(service);\n if (managed) {\n running = true;\n pid = service.pid;\n uptime = service.uptime;\n ownership = \"managed\";\n } else {\n const probe = await probeQqRuntime();\n if (probe.alive) {\n running = true;\n pid = probe.pid;\n ownership = \"external\";\n } else {\n const pids = await findNodeServicePids(\"qq\");\n if (pids.length > 0) {\n running = true;\n pid = pids[0]!;\n ownership = \"external\";\n }\n }\n }\n } else {\n const managed = await reconcileManagedAlive(service);\n if (managed) {\n running = true;\n pid = service.pid;\n uptime = service.uptime;\n ownership = \"managed\";\n }\n }\n\n return {\n name,\n title: service.title,\n running,\n pid,\n uptime,\n ...(ownership ? { ownership } : {}),\n };\n })\n );\n}\n\n/** \u5355\u670D\u52A1\u662F\u5426\u5728\u8DD1\uFF08\u542B\u5916\u90E8\u5B9E\u4F8B\uFF09 */\nexport async function isServiceRunning(name: ServiceName): Promise<boolean> {\n const rows = await queryServicesRuntime();\n return rows.some((r) => r.name === name && r.running);\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/**\n * services.ts \u2014 CLI \u8FDB\u7A0B\u7F16\u6392\u4E0E\u670D\u52A1\u751F\u547D\u5468\u671F\u7BA1\u7406\n *\n * \u6838\u5FC3\u7BA1\u7406\u5BF9\u8C61\uFF08ServiceName\uFF09\uFF1A\n * - `bds`\uFF1AMinecraft \u57FA\u5CA9\u7248\u670D\u52A1\u7AEF\uFF08\u652F\u6301 stdin \u7BA1\u9053\u6307\u4EE4\u4EA4\u4E92\u4E0E\u4F18\u96C5\u505C\u670D\uFF09\n * - `db`\uFF1Adb-server SQLite HTTP \u670D\u52A1\uFF08\u7AEF\u53E3\u5065\u5EB7\u68C0\u67E5\u4E0E loopback \u63A2\u6D3B\uFF09\n * - `tunnel`\uFF1A\u4E91\u7AEF QQ Webhook \u5230\u672C\u673A db-server \u7684 SSH \u53CD\u5411\u96A7\u9053\n * - `qq`\uFF1Aqq-bridge \u6D88\u606F\u7F51\u6865\u670D\u52A1\uFF08\u652F\u6301\u5B98\u65B9 Bot \u4E0E LLBot \u53CC\u540E\u7AEF\uFF09\n * - `llbot`\uFF1ALLBot \u72EC\u7ACB\u673A\u5668\u4EBA\u8FDB\u7A0B\uFF08\u6761\u4EF6\u542F\u52A8\uFF09\n *\n * \u6838\u5FC3\u80FD\u529B\uFF1A\n * - \u8FDB\u7A0B\u751F\u547D\u5468\u671F\uFF1A\u6309\u4F9D\u8D56\u6B21\u5E8F\u7F16\u6392\u670D\u52A1\u542F\u52A8\uFF08db \u2192 qq \u2192 bds\uFF09\u3001\u4F18\u96C5\u505C\u6B62\u4E0E\u5F02\u5E38\u81EA\u52A8\u62C9\u8D77\n * - \u591A\u6E90\u63A2\u6D3B\u611F\u77E5\uFF1A\u81EA\u52A8\u63A2\u6D4B\u6258\u7BA1\u8FDB\u7A0B\u4E0E\u5916\u90E8\u72EC\u7ACB\u8FDB\u7A0B\u72B6\u6001\n * - \u65E5\u5FD7\u6D41\u7EDF\u4E00\u805A\u5408\uFF1A\u6355\u83B7\u5E76\u5F52\u4E00\u5316\u5404\u670D\u52A1\u7684 stdout / stderr \u6570\u636E\u6D41\n */\n\nimport type { BdsUpdaterConfig, DBConfig, QQBackend, QQBridgeConfig } from \"@sfmc-bds/sdk/node/config\";\nimport {\n DEFAULT_BDS_UPDATER_CONFIG,\n DEFAULT_DB_CONFIG,\n DEFAULT_QQ_CONFIG,\n ensureCoreConfigs,\n loadEnsuredConfig,\n qqRuntimeStatusPath,\n readJson,\n type QqRuntimeStatus,\n} from \"@sfmc-bds/sdk/node/config\";\n\nimport { bdsExePath, bdsSpawnEnvExtra, ensureBdsExecutable } from \"@sfmc-bds/bds-tools/host-platform\";\nimport {\n clearBdsPidFile,\n isProcessAlive,\n probeBdsStatus,\n readBdsPidFile,\n writeBdsPidFile,\n} from \"@sfmc-bds/bds-tools/process-probe\";\nimport { postBdsLifecycleEvent } from \"@sfmc-bds/bds-tools/qq-events-notify\";\nimport { spawn, type ChildProcess, type IOType } from \"node:child_process\";\nimport { EventEmitter } from \"node:events\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { t } from \"./i18n/index.js\";\nimport { resolveLlbotLaunch } from \"./llbot-launch.js\";\nimport { inferLevel, pushLog as pushUnifiedLog } from \"./logs.js\";\nimport { findNodeServicePids } from \"./node-service-probe.js\";\nimport { ensurePackUpdateConfigFile } from \"./pack-update/index.js\";\nimport { recordBdsVersion } from \"./bds-runtime-version.js\";\nimport { reportBdsPlayerSession } from \"./player-session.js\";\nimport { ROOT, spawnService, type ServiceId } from \"./runtime.js\";\n\nexport { ROOT } from \"./runtime.js\";\n\nexport interface LogLine {\n time: Date;\n text: string;\n stream: \"stdout\" | \"stderr\";\n}\n\n/** \u5B50\u8FDB\u7A0B\u8F93\u51FA\u5206\u884C\uFF1A\u8FC7\u6EE4\u7A7A\u4E32\u4EE5\u53CA Windows CRLF \u62C6\u5206\u540E\u6B8B\u7559\u7684\u7EAF\u7A7A\u767D\u884C\u3002 */\nexport function nonBlankOutputLines(text: string): string[] {\n return text.split(/\\r?\\n/).filter((line) => line.trim().length > 0);\n}\n\nexport type ServiceName = \"bds\" | \"db\" | \"tunnel\" | \"qq\" | \"llbot\";\nexport const SERVICE_NAMES: ServiceName[] = [\"bds\", \"db\", \"tunnel\", \"qq\", \"llbot\"];\n\n/** argv \u4E00\u6B21\u6027 start \u5728 POSIX \u4E0A\u987B daemonize\uFF0C\u5426\u5219\u7236\u8FDB\u7A0B\u9000\u51FA\u4F1A\u5E26\u8D70\u5B50\u8FDB\u7A0B\uFF08Windows \u4E0D\u9700\u8981\uFF09 */\nlet argvDaemonize = false;\nexport function setArgvDaemonize(on: boolean): void {\n argvDaemonize = on;\n}\n\n/** \u5355\u670D\u52A1 start \u7ED3\u679C\uFF1Aoptional \u670D\u52A1 validate \u5931\u8D25\u8BB0\u4E3A skipped\uFF0C\u4E0D\u629B\u9519 */\nexport type StartOutcome = { status: \"started\" } | { status: \"already\" } | { status: \"skipped\"; reason: string };\n\nexport interface StartAllResult {\n started: ServiceName[];\n skipped: Array<{ name: ServiceName; reason: string }>;\n failed: Array<{ name: ServiceName; reason: string }>;\n}\n\nexport interface ServiceStatus {\n name: ServiceName;\n title: string;\n running: boolean;\n pid: number;\n uptime: string;\n ownership?: \"managed\" | \"external\";\n}\n\n/** \u5F53\u524D db \u5065\u5EB7\u63A2\u6D4B\u7AEF\u53E3\uFF08\u4E0E createServices \u540C\u6B65\uFF09 */\nlet dbHealthPort = 3001;\n/** \u5F53\u524D QQ \u540E\u7AEF\uFF08\u4E0E createServices \u540C\u6B65\uFF0C\u4F9B Tab/\u7A97\u6807\u9898\uFF09 */\nlet qqBackendMode: QQBackend = \"official\";\n\nexport function getQqBackendMode(): QQBackend {\n return qqBackendMode;\n}\n\nasync function probeDbHealth(port: number): Promise<boolean> {\n try {\n const res = await fetch(`http://127.0.0.1:${port}/api/health`, { signal: AbortSignal.timeout(2000) });\n return res.ok;\n } catch {\n return false;\n }\n}\n\n/** \u8BFB `.sfmc/qq.runtime.json` + pid \u5B58\u6D3B\uFF0C\u63A2\u6D4B\u5916\u90E8/\u672C\u673A qq-bridge */\nasync function probeQqRuntime(): Promise<{ alive: boolean; pid: number }> {\n const status = readJson<QqRuntimeStatus>(qqRuntimeStatusPath(ROOT));\n if (!status?.pid || status.pid <= 0) return { alive: false, pid: 0 };\n if (!(await isProcessAlive(status.pid))) return { alive: false, pid: 0 };\n return { alive: true, pid: status.pid };\n}\n\nconst tunnelRuntimePath = path.join(ROOT, \".sfmc\", \"tunnel.runtime.json\");\n\nasync function probeTunnelRuntime(): Promise<number> {\n const pid = readJson<{ pid?: number }>(tunnelRuntimePath)?.pid;\n return typeof pid === \"number\" && pid > 0 && (await isProcessAlive(pid)) ? pid : 0;\n}\n\nfunction writeTunnelRuntime(pid: number): void {\n fs.mkdirSync(path.dirname(tunnelRuntimePath), { recursive: true });\n fs.writeFileSync(tunnelRuntimePath, JSON.stringify({ pid, startedAt: Date.now() }) + \"\\n\");\n}\n\nfunction clearTunnelRuntime(pid: number): void {\n if (readJson<{ pid?: number }>(tunnelRuntimePath)?.pid !== pid) return;\n try {\n fs.unlinkSync(tunnelRuntimePath);\n } catch {\n /* \u8FDB\u7A0B\u9000\u51FA\u65F6\u5141\u8BB8\u6587\u4EF6\u5DF2\u88AB\u79FB\u9664 */\n }\n}\n\ninterface ServiceDef {\n name: ServiceName;\n title: string;\n service?: ServiceId;\n cmd?: string;\n args?: string[];\n cwd: string;\n env?: Record<string, string>;\n stopCommand?: string;\n stopTimeout: number;\n autoRestart: boolean;\n restartDelay: number;\n /**\n * \u53EF\u9009\u670D\u52A1\uFF1Avalidate \u5931\u8D25\u65F6 start \u8FD4\u56DE skipped\uFF08startAll \u4E0D\u5F53\u4F5C\u5931\u8D25\uFF09\u3002\n * \u7528\u4E8E official \u4E0B\u8DF3\u8FC7 llbot\u3001\u4EE5\u53CA qq_enabled=false\u3002\n */\n optional?: boolean;\n validate?: () => string | null;\n /** \u542F\u52A8\u524D\u94A9\u5B50(\u5982 BDS \u88C5\u8F7D\u4E00\u81F4\u6027\u6821\u9A8C);\u5931\u8D25\u5219\u7981\u6B62 spawn */\n beforeStart?: () => Promise<void>;\n /** \u539F\u59CB\u65E5\u5FD7\u884C\u89C2\u5BDF\u5668\uFF0C\u4E0D\u5F97\u963B\u585E\u5B50\u8FDB\u7A0B\u8F93\u51FA\u3002 */\n onLogLine?: (line: string) => void;\n}\n\nclass Service {\n name: ServiceName;\n title: string;\n proc: ChildProcess | null = null;\n running = false;\n pid = 0;\n startTime: Date | null = null;\n logs: LogLine[] = [];\n events = new EventEmitter();\n\n private def: ServiceDef;\n private manualStop = false;\n private updateInProgress = false;\n private restartTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(def: ServiceDef) {\n this.name = def.name;\n this.title = def.title;\n this.def = def;\n if (def.name === \"tunnel\") {\n this.events.on(\"output\", (text: string, level: \"info\" | \"error\") => {\n pushUnifiedLog(text, \"tunnel\", level);\n });\n }\n }\n\n get uptime(): string {\n if (!this.startTime || !this.running) return \"\u2014\";\n const ms = Date.now() - this.startTime.getTime();\n const m = Math.floor(ms / 60000);\n const h = Math.floor(m / 60);\n if (h > 0) return `${h}h ${m % 60}m`;\n if (m > 0) return `${m}m`;\n return `${Math.floor(ms / 1000)}s`;\n }\n\n pushLog(text: string, stream: \"stdout\" | \"stderr\"): void {\n const line: LogLine = { time: new Date(), text, stream };\n this.logs.push(line);\n if (this.logs.length > 2000) this.logs.splice(0, this.logs.length - 2000);\n this.events.emit(\"log\", line);\n // stderr \u89C6\u4E3A error; stdout \u7528 inferLevel \u63A8\u65AD (bare \u5B50\u8FDB\u7A0B\u7EAF text \u65F6\u9ED8\u8BA4 info,\n // BDS \u7B49\u81EA\u5E26 [LEVEL] \u6807\u7B7E\u7684\u4ECD\u53EF\u6B63\u786E\u63A8\u65AD)\n const level = stream === \"stderr\" ? \"error\" : inferLevel(text);\n pushUnifiedLog(text, this.name, level);\n this.def.onLogLine?.(text);\n }\n\n async start(): Promise<StartOutcome> {\n if (this.updateInProgress) throw new Error(\"BDS \u6B63\u5728\u66F4\u65B0\uFF0C\u6682\u4E0D\u53EF\u542F\u52A8\");\n if (this.running) return { status: \"already\" };\n if (this.name === \"bds\") {\n const probe = await probeBdsStatus({ rootDir: ROOT });\n if (probe.state !== \"stopped\") {\n throw new Error(\n t(\"svc.bdsAlreadyRunning\", {\n pid: String(probe.pid),\n kind: t(probe.state === \"managed\" ? \"svc.running\" : \"svc.runningExternal\"),\n })\n );\n }\n }\n if (this.name === \"db\" || this.name === \"qq\") {\n const external = await findNodeServicePids(this.name);\n if (external.length > 0) {\n return { status: \"already\" };\n }\n }\n if (this.name === \"tunnel\" && (await probeTunnelRuntime())) return { status: \"already\" };\n if (this.def.validate) {\n const v = this.def.validate();\n if (v) {\n // optional\uFF1A\u914D\u7F6E\u5C42\u9762\u8DF3\u8FC7\uFF08\u5982 official \u4E0B llbot\uFF09\uFF0C\u4E0D\u5F53\u5931\u8D25\u3001\u4E5F\u4E0D spawn\n if (this.def.optional) return { status: \"skipped\", reason: v };\n throw new Error(v);\n }\n }\n if (this.def.beforeStart) {\n await this.def.beforeStart();\n }\n // beforeStart \u542B\u5F02\u6B65\u68C0\u67E5\uFF1B\u66F4\u65B0\u53EF\u80FD\u5728\u7B49\u5F85\u671F\u95F4\u5F00\u59CB\u3002\n if (this.updateInProgress) throw new Error(\"BDS \u6B63\u5728\u66F4\u65B0\uFF0C\u6682\u4E0D\u53EF\u542F\u52A8\");\n this.manualStop = false;\n const daemonize = argvDaemonize && process.platform !== \"win32\";\n const spawnOpts = {\n cwd: this.def.cwd,\n stdio: (daemonize ? \"ignore\" : [\"pipe\", \"pipe\", \"pipe\"]) as \"ignore\" | Array<IOType>,\n env: this.def.env ? { ...process.env, ...this.def.env } : process.env,\n detached: daemonize,\n };\n const child = this.def.service\n ? spawnService(this.def.service, this.def.args ?? [], spawnOpts)\n : spawn(this.def.cmd as string, this.def.args ?? [], spawnOpts);\n if (daemonize) child.unref();\n this.proc = child;\n this.pid = child.pid ?? 0;\n this.running = true;\n this.startTime = new Date();\n if (this.name === \"bds\" && this.pid > 0) {\n writeBdsPidFile(this.pid, ROOT);\n }\n if (this.name === \"tunnel\" && this.pid > 0) writeTunnelRuntime(this.pid);\n this.events.emit(\"output\", `started (PID ${this.pid})`, \"info\");\n this.events.emit(\"state\", { name: this.name, running: true, pid: this.pid });\n\n let lifecycleReported = false;\n const reportExit = (code?: number | null) => {\n if (this.name !== \"bds\" || lifecycleReported) return;\n lifecycleReported = true;\n const planned = this.manualStop || this.updateInProgress;\n void postBdsLifecycleEvent(planned ? \"stop\" : \"crash\", planned ? undefined : `code=${code ?? \"?\"}`, {\n port: dbHealthPort,\n });\n };\n if (this.name === \"bds\") {\n child.once(\"spawn\", () => {\n void postBdsLifecycleEvent(\"start\", undefined, { port: dbHealthPort });\n });\n }\n\n child.on(\"error\", (e) => {\n this.events.emit(\"output\", `process error: ${e.message}`, \"error\");\n reportExit();\n this.cleanup();\n this.scheduleRestart();\n });\n\n let versionLineBuffer = \"\";\n child.stdout?.on(\"data\", (d: Buffer) => {\n if (this.name === \"bds\" && this.startTime) {\n versionLineBuffer += d.toString();\n const versionLines = versionLineBuffer.split(/\\r?\\n/);\n versionLineBuffer = (versionLines.pop() ?? \"\").slice(-4096);\n for (const versionLine of versionLines) recordBdsVersion(ROOT, versionLine, this.pid, this.startTime.getTime());\n }\n for (const line of nonBlankOutputLines(d.toString())) {\n this.pushLog(line, \"stdout\");\n }\n });\n child.stderr?.on(\"data\", (d: Buffer) => {\n for (const line of nonBlankOutputLines(d.toString())) {\n this.pushLog(line, \"stderr\");\n }\n });\n\n child.on(\"exit\", (code) => {\n if (this.proc && this.proc !== child) return;\n this.events.emit(\"output\", `exited (code: ${code})`, \"info\");\n reportExit(code);\n this.cleanup();\n this.scheduleRestart();\n });\n return { status: \"started\" };\n }\n\n private scheduleRestart(): void {\n if (this.manualStop || this.updateInProgress || !this.def.autoRestart || this.restartTimer) return;\n this.restartTimer = setTimeout(() => {\n this.restartTimer = null;\n if (!this.updateInProgress && !this.manualStop && !this.running) {\n void this.start().catch((e: Error) => {\n this.events.emit(\"output\", `restart failed: ${e.message}`, \"error\");\n });\n }\n }, this.def.restartDelay);\n }\n\n /** \u66F4\u65B0\u5668\u72EC\u7ACB\u8FDB\u7A0B\u505C\u670D\u65F6\uFF0C\u7981\u6B62\u5C06\u8BA1\u5212\u5185\u9000\u51FA\u5F53\u4F5C\u5D29\u6E83\u5E76\u81EA\u52A8\u62C9\u8D77\u3002 */\n beginUpdate(): void {\n if (this.name !== \"bds\") return;\n if (this.updateInProgress) throw new Error(\"BDS \u66F4\u65B0\u5DF2\u5728\u8FDB\u884C\u4E2D\");\n this.updateInProgress = true;\n if (this.restartTimer) {\n clearTimeout(this.restartTimer);\n this.restartTimer = null;\n }\n }\n\n endUpdate(): void {\n this.updateInProgress = false;\n }\n\n async stop(): Promise<void> {\n this.manualStop = true;\n if (this.restartTimer) {\n clearTimeout(this.restartTimer);\n this.restartTimer = null;\n }\n if (!this.proc || !this.running) return;\n this.events.emit(\"output\", \"stopping...\", \"info\");\n\n if (this.def.stopCommand && this.proc.stdin) {\n this.proc.stdin.write(this.def.stopCommand + \"\\n\");\n } else {\n this.proc.kill(\"SIGTERM\");\n }\n\n return new Promise((resolve) => {\n const timeout = setTimeout(() => {\n if (this.proc) {\n this.events.emit(\"output\", \"force kill\", \"error\");\n try {\n this.proc.kill(\"SIGKILL\");\n } catch {\n /* ignore */\n }\n }\n resolve();\n }, this.def.stopTimeout);\n\n this.proc?.on(\"exit\", () => {\n clearTimeout(timeout);\n resolve();\n });\n });\n }\n\n forceStop(): void {\n const child = this.proc;\n this.manualStop = true;\n if (this.restartTimer) {\n clearTimeout(this.restartTimer);\n this.restartTimer = null;\n }\n this.cleanup();\n if (!child) return;\n try {\n child.kill(\"SIGKILL\");\n } catch {\n /* ignore */\n }\n }\n\n async restart(): Promise<StartOutcome> {\n await this.stop();\n return this.start();\n }\n\n getRecentLogs(n: number): LogLine[] {\n return this.logs.slice(-n);\n }\n\n /**\n * \u63A2\u6D4B\u53D1\u73B0\u8FDB\u7A0B\u5DF2\u6B7B\u540E\u56DE\u5199\u672C\u5730\u72B6\u6001\uFF08\u4E0D\u8BBE manualStop\uFF0C\u4EE5\u4FBF exit \u56DE\u8C03\u4ECD\u53EF\u6309\u9700 autoRestart\uFF09\u3002\n */\n markStoppedFromProbe(): void {\n if (!this.running && !this.proc) return;\n this.cleanup();\n }\n\n private cleanup(): void {\n const wasRunning = this.running || this.proc !== null;\n const exitingPid = this.pid;\n this.proc = null;\n this.running = false;\n this.pid = 0;\n this.startTime = null;\n if (this.name === \"bds\" && exitingPid > 0) {\n const filePid = readBdsPidFile(ROOT);\n if (filePid === exitingPid) {\n clearBdsPidFile(ROOT);\n }\n }\n if (this.name === \"tunnel\" && exitingPid > 0) clearTunnelRuntime(exitingPid);\n if (wasRunning) {\n this.events.emit(\"state\", { name: this.name, running: false, pid: 0 });\n }\n }\n}\n\nfunction createServices(): Record<ServiceName, Service> {\n /* \u5404\u670D\u52A1/CLI \u7528 SDK ensureCoreConfigs \u64AD\u79CD\uFF08\u542B $schema\uFF09\uFF0C\u4E0D\u518D\u4ECE configs-default \u62F7\u8D1D\u3002 */\n ensureCoreConfigs(ROOT, [\"bds_updater\", \"qq_config\", \"db_config\"]);\n ensurePackUpdateConfigFile();\n const bdsCfg = loadEnsuredConfig(ROOT, \"bds_updater.json\", \"bds_updater\", { ...DEFAULT_BDS_UPDATER_CONFIG } as Record<\n string,\n unknown\n >) as BdsUpdaterConfig;\n const qqCfg = loadEnsuredConfig(ROOT, \"qq_config.json\", \"qq_config\", { ...DEFAULT_QQ_CONFIG } as Record<\n string,\n unknown\n >) as QQBridgeConfig;\n const dbCfg = loadEnsuredConfig(ROOT, \"db_config.json\", \"db_config\", { ...DEFAULT_DB_CONFIG } as Record<\n string,\n unknown\n >) as DBConfig;\n const bdsPath = bdsCfg.bds_path ?? ROOT;\n const useLlbotBackend = qqCfg.qq_backend === \"llbot\";\n const qqEnabled = qqCfg.qq_enabled !== false;\n // \u5B98\u65B9 Webhook \u7531\u4E91\u7AEF\u63A5\u6536\uFF1B\u4EC5\u5207\u6362\u4F20\u8F93\u65B9\u5F0F\u5373\u53EF\u907F\u514D\u672C\u673A\u518D\u542F\u52A8\u4E00\u4E2A\u6865\u3002\n const qqExternal = !useLlbotBackend && qqCfg.official?.transport === \"webhook\";\n const tunnelCfg = qqCfg.official?.tunnel;\n const tunnelEnabled = qqEnabled && qqExternal && tunnelCfg?.enabled === true;\n const tunnelHost = String(tunnelCfg?.host ?? \"\").trim();\n const tunnelRemotePort = tunnelCfg?.remotePort ?? 13001;\n const llbotEnabled = qqCfg.llbot?.enabled !== false;\n const llbotLaunch = resolveLlbotLaunch(qqCfg.llbot?.path, qqCfg.llbot?.cwd);\n const llbotPath = llbotLaunch.exe;\n const llbotCwd = llbotLaunch.cwd;\n const dbPort = dbCfg.db_port ?? 3001;\n dbHealthPort = dbPort;\n qqBackendMode = useLlbotBackend ? \"llbot\" : \"official\";\n const bdsExe = bdsExePath(path.resolve(bdsPath));\n const qqTitle = useLlbotBackend ? \"QQ (llbot)\" : \"QQ (official)\";\n\n return {\n bds: new Service({\n name: \"bds\",\n title: \"BDS\",\n cmd: bdsExe,\n args: [],\n cwd: bdsPath,\n env: bdsSpawnEnvExtra(bdsPath),\n stopCommand: \"stop\",\n stopTimeout: 30000,\n autoRestart: bdsCfg.crash_restart !== false,\n restartDelay: 5000,\n validate: () => {\n if (!fs.existsSync(bdsExe)) return `not found: ${bdsExe}`;\n return null;\n },\n beforeStart: async () => {\n ensureBdsExecutable(bdsExe);\n /* \u5148\u88C5\u6536\u4EF6\u7BB1\u7B2C\u4E09\u65B9\u5305\uFF0C\u518D\u68C0\u67E5 CF \u66F4\u65B0\uFF0C\u518D\u8DD1\u6A21\u5757\u805A\u5408\u95F8\u95E8 */\n const { scanAndInstallInbox } = await import(\"./world-packs.js\");\n await scanAndInstallInbox({ interactive: false });\n const { runPackUpdatesOnBdsStart } = await import(\"./pack-update/index.js\");\n await runPackUpdatesOnBdsStart();\n const { ensurePacksReady } = await import(\"./pack-lifecycle.js\");\n await ensurePacksReady();\n },\n onLogLine: (line) =>\n reportBdsPlayerSession(line, {\n port: dbPort,\n ...(dbCfg.http_auth ? { authToken: dbCfg.http_auth } : {}),\n }),\n }),\n\n db: new Service({\n name: \"db\",\n title: \"DB Server\",\n service: \"db\",\n cwd: ROOT,\n stopTimeout: 10000,\n autoRestart: true,\n restartDelay: 3000,\n env: { DB_PORT: String(dbPort) },\n }),\n\n tunnel: new Service({\n name: \"tunnel\",\n title: \"QQ SSH Tunnel\",\n cmd: \"ssh\",\n args: [\n \"-N\", \"-T\", \"-o\", \"BatchMode=yes\", \"-o\", \"ConnectTimeout=10\",\n \"-o\", \"ExitOnForwardFailure=yes\", \"-o\", \"ServerAliveInterval=30\",\n \"-o\", \"ServerAliveCountMax=3\",\n \"-R\", `127.0.0.1:${tunnelRemotePort}:127.0.0.1:${dbPort}`,\n tunnelHost,\n ],\n cwd: ROOT,\n stopTimeout: 10000,\n autoRestart: true,\n restartDelay: 5000,\n optional: !tunnelEnabled,\n validate: () => {\n if (!tunnelEnabled) return \"QQ tunnel disabled (official webhook tunnel.enabled=false)\";\n if (!/^[A-Za-z0-9_][A-Za-z0-9_.-]*$/.test(tunnelHost)) return \"invalid official.tunnel.host\";\n if (!Number.isInteger(tunnelRemotePort) || tunnelRemotePort < 1 || tunnelRemotePort > 65535) {\n return \"invalid official.tunnel.remotePort\";\n }\n return null;\n },\n }),\n\n qq: new Service({\n name: \"qq\",\n title: qqTitle,\n service: \"qq\",\n cwd: ROOT,\n stopTimeout: 10000,\n // \u7F3A\u51ED\u636E\u65F6 validate \u62E6\u4E0B\uFF0C\u4E0D\u4F1A spawn\u2192exit(1)\u2192autoRestart \u6B7B\u5FAA\u73AF\n autoRestart: true,\n restartDelay: 3000,\n optional: !qqEnabled || qqExternal,\n validate: () => {\n if (!qqEnabled) return \"QQ bridge disabled (qq_enabled=false)\";\n if (qqExternal) return \"QQ bridge runs externally (official.transport=webhook)\";\n if (!useLlbotBackend) {\n if (!String(qqCfg.official?.app_id ?? \"\").trim() || !String(qqCfg.official?.app_secret ?? \"\").trim()) {\n return \"missing official.app_id / official.app_secret\";\n }\n }\n return null;\n },\n }),\n\n llbot: new Service({\n name: \"llbot\",\n title: \"LLBot\",\n cmd: llbotPath,\n args: [],\n cwd: llbotCwd,\n stopTimeout: 10000,\n autoRestart: false,\n restartDelay: 5000,\n // official \u6216\u672A\u542F\u7528\u65F6\u771F\u6B63 skip\uFF0C\u4E0D throw\n optional: !useLlbotBackend || !llbotEnabled,\n validate: () => {\n if (!useLlbotBackend) return \"LLBot skipped (qq_backend!=llbot)\";\n if (!llbotEnabled) return \"LLBot disabled (llbot.enabled=false)\";\n if (!fs.existsSync(llbotPath)) return `not found: ${llbotPath}`;\n return null;\n },\n }),\n };\n}\n\nexport let services: Record<ServiceName, Service> = createServices();\n\nexport function refreshServices(): void {\n forceStopAll();\n services = createServices();\n}\n\nexport const START_ORDER: ServiceName[] = [\"db\", \"tunnel\", \"qq\", \"llbot\", \"bds\"];\n\nexport async function startAll(): Promise<StartAllResult> {\n const result: StartAllResult = { started: [], skipped: [], failed: [] };\n for (const name of START_ORDER) {\n const svc = services[name];\n if (!svc) continue;\n try {\n const outcome = await svc.start();\n if (outcome.status === \"skipped\") {\n result.skipped.push({ name, reason: outcome.reason });\n svc.events.emit(\"output\", `skipped: ${outcome.reason}`, \"info\");\n } else {\n result.started.push(name);\n }\n } catch (e) {\n const reason = (e as Error).message;\n result.failed.push({ name, reason });\n svc.events.emit(\"output\", `start error: ${reason}`, \"error\");\n }\n }\n return result;\n}\n\nexport async function stopAll(): Promise<void> {\n const pending = [...START_ORDER]\n .reverse()\n .map((name) => services[name])\n .filter((service): service is Service => Boolean(service))\n .map((service) => service.stop());\n await Promise.allSettled(pending);\n}\n\nexport function forceStopAll(): void {\n for (const service of Object.values(services)) service.forceStop();\n}\n\nexport type ServiceStateEvent = { name: ServiceName; running: boolean; pid: number };\n\n/** \u8BA2\u9605\u4EFB\u610F\u670D\u52A1\u542F\u505C\uFF08\u542B\u63A2\u6D4B\u56DE\u5199\uFF09\uFF1B\u8FD4\u56DE\u53D6\u6D88\u51FD\u6570 */\nexport function onServiceStateChange(fn: (ev: ServiceStateEvent) => void): () => void {\n const handler = (ev: ServiceStateEvent): void => {\n fn(ev);\n };\n for (const service of Object.values(services)) {\n service.events.on(\"state\", handler);\n }\n return () => {\n for (const service of Object.values(services)) {\n service.events.off(\"state\", handler);\n }\n };\n}\n\n/** \u82E5\u672C\u5730\u6807\u8BB0 running \u4F46 OS \u8FDB\u7A0B\u5DF2\u6B7B\uFF0C\u56DE\u6536\u5185\u5B58\u6807\u5FD7 */\nasync function reconcileManagedAlive(service: Service): Promise<boolean> {\n if (!service.running) return false;\n if (service.pid > 0 && !(await isProcessAlive(service.pid))) {\n service.markStoppedFromProbe();\n return false;\n }\n return service.running;\n}\n\n/**\n * \u7EDF\u4E00\u8FD0\u884C\u6001\u67E5\u8BE2\uFF08\u6743\u5A01\u5165\u53E3\uFF09\uFF1AOS/\u5065\u5EB7\u63A2\u6D4B + \u56DE\u5199 Service \u5185\u5B58\u6807\u5FD7\u3002\n * status / Tab \u53D1\u9001\u76EE\u6807 / reload \u7B49\u5747\u5E94\u8D70\u6B64\u63A5\u53E3\uFF0C\u52FF\u76F4\u63A5\u8BFB `service.running`\u3002\n */\nexport async function queryServicesRuntime(): Promise<ServiceStatus[]> {\n return Promise.all(\n SERVICE_NAMES.map(async (name) => {\n const service = services[name];\n let running = false;\n let pid = 0;\n let uptime = \"\u2014\";\n let ownership: \"managed\" | \"external\" | undefined;\n\n if (name === \"bds\") {\n const probe = await probeBdsStatus({\n managedPid: service.pid,\n hasStdin: Boolean(service.proc?.stdin),\n rootDir: ROOT,\n });\n if (probe.state === \"managed\") {\n running = true;\n pid = probe.pid;\n uptime = service.uptime;\n ownership = \"managed\";\n /* \u63A2\u6D4B\u4E3A managed \u4F46\u672C\u5730\u5DF2\u6807\u505C\uFF1A\u4FDD\u6301\u63A2\u6D4B\u7ED3\u679C\uFF0C\u4E0D\u5F3A\u5236\u6539\u5185\u5B58\uFF08stdin \u4ECD\u53EF\u7528\uFF09 */\n } else if (probe.state === \"external\") {\n running = true;\n pid = probe.pid;\n ownership = \"external\";\n /* \u5916\u90E8\u8FDB\u7A0B\uFF1A\u672C\u5730 managed \u53E5\u67C4\u5DF2\u5931\u6548\u5219\u56DE\u6536 */\n if (service.running && service.pid !== probe.pid) {\n service.markStoppedFromProbe();\n } else if (service.running && !(await isProcessAlive(service.pid))) {\n service.markStoppedFromProbe();\n }\n } else {\n running = false;\n if (service.running || service.proc) {\n service.markStoppedFromProbe();\n }\n }\n } else if (name === \"db\") {\n const managed = await reconcileManagedAlive(service);\n if (managed) {\n running = true;\n pid = service.pid;\n uptime = service.uptime;\n ownership = \"managed\";\n } else if (await probeDbHealth(dbHealthPort)) {\n running = true;\n ownership = \"external\";\n const pids = await findNodeServicePids(\"db\");\n pid = pids[0] ?? 0;\n }\n } else if (name === \"qq\") {\n const managed = await reconcileManagedAlive(service);\n if (managed) {\n running = true;\n pid = service.pid;\n uptime = service.uptime;\n ownership = \"managed\";\n } else {\n const probe = await probeQqRuntime();\n if (probe.alive) {\n running = true;\n pid = probe.pid;\n ownership = \"external\";\n } else {\n const pids = await findNodeServicePids(\"qq\");\n if (pids.length > 0) {\n running = true;\n pid = pids[0]!;\n ownership = \"external\";\n }\n }\n }\n } else if (name === \"tunnel\") {\n const managed = await reconcileManagedAlive(service);\n if (managed) {\n running = true;\n pid = service.pid;\n uptime = service.uptime;\n ownership = \"managed\";\n } else {\n pid = await probeTunnelRuntime();\n running = pid > 0;\n if (running) ownership = \"external\";\n }\n } else {\n const managed = await reconcileManagedAlive(service);\n if (managed) {\n running = true;\n pid = service.pid;\n uptime = service.uptime;\n ownership = \"managed\";\n }\n }\n\n return {\n name,\n title: service.title,\n running,\n pid,\n uptime,\n ...(ownership ? { ownership } : {}),\n };\n })\n );\n}\n\n/** \u5355\u670D\u52A1\u662F\u5426\u5728\u8DD1\uFF08\u542B\u5916\u90E8\u5B9E\u4F8B\uFF09 */\nexport async function isServiceRunning(name: ServiceName): Promise<boolean> {\n const rows = await queryServicesRuntime();\n return rows.some((r) => r.name === name && r.running);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["ROOT"]
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sfmc-modules-root.d.ts","sourceRoot":"","sources":["../src/sfmc-modules-root.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,iBAAiB;IAChC,mCAAmC;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW;IACX,IAAI,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAOD,iDAAiD;AACjD,wBAAgB,4BAA4B,IAAI,MAAM,GAAG,IAAI,CAG5D;AAED,4CAA4C;AAC5C,wBAAgB,4BAA4B,CAAC,OAAO,GAAE,MAAa,GAAG,MAAM,GAAG,IAAI,CAMlF;AAED,8BAA8B;AAC9B,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,MAAa,GAAG,IAAI,CAGxF;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,MAAa,GAAG,MAAM,GAAG,IAAI,CAS5E;AAED,yCAAyC;AACzC,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,iBAAiB,EAAE,
|
|
1
|
+
{"version":3,"file":"sfmc-modules-root.d.ts","sourceRoot":"","sources":["../src/sfmc-modules-root.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,iBAAiB;IAChC,mCAAmC;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW;IACX,IAAI,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAOD,iDAAiD;AACjD,wBAAgB,4BAA4B,IAAI,MAAM,GAAG,IAAI,CAG5D;AAED,4CAA4C;AAC5C,wBAAgB,4BAA4B,CAAC,OAAO,GAAE,MAAa,GAAG,MAAM,GAAG,IAAI,CAMlF;AAED,8BAA8B;AAC9B,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,MAAa,GAAG,IAAI,CAGxF;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,MAAa,GAAG,MAAM,GAAG,IAAI,CAS5E;AAED,yCAAyC;AACzC,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,iBAAiB,EAAE,CA+B/E;AAED,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE7E"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { configPath, patchJson, readJson } from "@sfmc-bds/sdk/node/config";
|
|
2
|
-
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { ROOT } from "./runtime.js";
|
|
5
5
|
const PACKAGES_DIRNAME = "packages";
|
|
@@ -37,9 +37,13 @@ function listSfmcModulePackages(modulesRoot) {
|
|
|
37
37
|
if (!existsSync(packagesDir)) return [];
|
|
38
38
|
const out = [];
|
|
39
39
|
for (const entry of readdirSync(packagesDir, { withFileTypes: true })) {
|
|
40
|
-
if (!entry.isDirectory()) continue;
|
|
41
40
|
const id = entry.name;
|
|
42
41
|
const pkgPath = path.join(packagesDir, id);
|
|
42
|
+
try {
|
|
43
|
+
if (!statSync(pkgPath).isDirectory()) continue;
|
|
44
|
+
} catch {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
43
47
|
const manifestPath = path.join(pkgPath, "sapi", "manifest.json");
|
|
44
48
|
let logicalId = `feature-${id}`;
|
|
45
49
|
let name = id;
|