@sfmc-bds/cli 0.5.3 → 0.7.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/dist/logs.d.ts +21 -0
- package/dist/logs.d.ts.map +1 -1
- package/dist/logs.js +67 -1
- package/dist/logs.js.map +2 -2
- package/dist/management/deployment.js +1 -1
- package/dist/management/deployment.js.map +2 -2
- package/dist/management/domain.d.ts +46 -2
- package/dist/management/domain.d.ts.map +1 -1
- package/dist/management/domain.js +112 -26
- package/dist/management/domain.js.map +2 -2
- package/dist/management/maintenance.d.ts +14 -1
- package/dist/management/maintenance.d.ts.map +1 -1
- package/dist/management/maintenance.js +46 -38
- package/dist/management/maintenance.js.map +2 -2
- package/dist/management/pack-import.d.ts +29 -0
- package/dist/management/pack-import.d.ts.map +1 -0
- package/dist/management/pack-import.js +108 -0
- package/dist/management/pack-import.js.map +7 -0
- package/dist/management/platform-worker.js +5 -6
- package/dist/management/platform-worker.js.map +2 -2
- package/dist/management/server.d.ts.map +1 -1
- package/dist/management/server.js +113 -33
- package/dist/management/server.js.map +2 -2
- package/dist/management/tasks.js +1 -1
- package/dist/management/tasks.js.map +2 -2
- package/dist/registry.d.ts +11 -5
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +35 -11
- package/dist/registry.js.map +2 -2
- package/dist/services.d.ts +2 -1
- package/dist/services.d.ts.map +1 -1
- package/dist/services.js +16 -3
- package/dist/services.js.map +2 -2
- package/dist/world-packs.d.ts +47 -0
- package/dist/world-packs.d.ts.map +1 -1
- package/dist/world-packs.js +38 -11
- package/dist/world-packs.js.map +2 -2
- package/package.json +6 -6
- package/scripts/module-install/lib/registry-index.mjs +50 -13
package/dist/management/tasks.js
CHANGED
|
@@ -22,7 +22,7 @@ class TaskStore extends EventEmitter {
|
|
|
22
22
|
const active = typeof pid === "number" && lockOwnerAlive(pid);
|
|
23
23
|
if (!active) {
|
|
24
24
|
record.status = "interrupted";
|
|
25
|
-
record.error = { code: "unavailable", message: "\u6267\u884C\u8FDB\u7A0B\u5F02\u5E38\u9000\u51FA\uFF1B\u8BF7\u68C0\u67E5\
|
|
25
|
+
record.error = { code: "unavailable", message: "\u6267\u884C\u8FDB\u7A0B\u5F02\u5E38\u9000\u51FA\uFF1B\u8BF7\u68C0\u67E5\u5B9E\u4F8B\u5B9E\u9645\u72B6\u6001\uFF0C\u4EFB\u52A1\u4E0D\u4F1A\u81EA\u52A8\u91CD\u53D1" };
|
|
26
26
|
this.save(record);
|
|
27
27
|
}
|
|
28
28
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/management/tasks.ts"],
|
|
4
|
-
"sourcesContent": ["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\nimport { EventEmitter } from \"node:events\";\nimport { lockOwnerAlive, withMaintenanceLock } from \"@sfmc-bds/management/node\";\nimport { toManagementError, type OperationRecord } from \"@sfmc-bds/management\";\n\nexport function atomicJson(file: string, value: unknown) {\n fs.mkdirSync(path.dirname(file), { recursive: true });\n const temp = `${file}.${randomUUID()}.tmp`;\n fs.writeFileSync(temp, JSON.stringify(value, null, 2) + \"\\n\", { mode: 0o600 });\n fs.renameSync(temp, file);\n}\nexport type TaskContext = { phase: (name: string, message?: string) => void; id: string };\n\n/** \u4EFB\u52A1\u5C5E\u4E8E\u5B88\u62A4\u8FDB\u7A0B\u800C\u975E SSH \u4F1A\u8BDD\uFF1B\u5F02\u5E38\u6062\u590D\u53EA\u6807\u8BB0\uFF0C\u4E0D\u91CD\u653E\u5199\u5165\u3002 */\nexport class TaskStore extends EventEmitter {\n private queue: Promise<unknown> = Promise.resolve();\n private directory: string;\n constructor(private root: string) {\n super();\n this.directory = path.join(root, \".sfmc\", \"operations\");\n fs.mkdirSync(this.directory, { recursive: true });\n for (const record of this.list()) {\n if (record.status === \"queued\" || record.status === \"running\") {\n const pid = (record as OperationRecord & { workerPid?: number }).workerPid;\n // \u4E0E\u7EF4\u62A4\u9501\u540C\u4E00\u5957\u63A2\u6D3B\uFF1A\u6743\u9650\u4E0D\u8DB3\u4E0D\u7B97\u9000\u51FA\uFF0C\u907F\u514D\u628A\u8FD8\u5728\u8DD1\u7684\u7EF4\u62A4\u8FDB\u7A0B\u6807\u6210\u4E2D\u65AD\u3002\n const active = typeof pid === \"number\" && lockOwnerAlive(pid);\n if (!active) { record.status = \"interrupted\"; record.error = { code: \"unavailable\", message: \"\u6267\u884C\u8FDB\u7A0B\u5F02\u5E38\u9000\u51FA\uFF1B\u8BF7\u68C0\u67E5\
|
|
5
|
-
"mappings": "AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,kBAAkB;AAC3B,SAAS,oBAAoB;AAC7B,SAAS,gBAAgB,2BAA2B;AACpD,SAAS,yBAA+C;AAEjD,SAAS,WAAW,MAAc,OAAgB;AACvD,KAAG,UAAU,KAAK,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AACpD,QAAM,OAAO,GAAG,IAAI,IAAI,WAAW,CAAC;AACpC,KAAG,cAAc,MAAM,KAAK,UAAU,OAAO,MAAM,CAAC,IAAI,MAAM,EAAE,MAAM,IAAM,CAAC;AAC7E,KAAG,WAAW,MAAM,IAAI;AAC1B;AAIO,MAAM,kBAAkB,aAAa;AAAA,EAG1C,YAAoB,MAAc;AAChC,UAAM;AADY;AAElB,SAAK,YAAY,KAAK,KAAK,MAAM,SAAS,YAAY;AACtD,OAAG,UAAU,KAAK,WAAW,EAAE,WAAW,KAAK,CAAC;AAChD,eAAW,UAAU,KAAK,KAAK,GAAG;AAChC,UAAI,OAAO,WAAW,YAAY,OAAO,WAAW,WAAW;AAC7D,cAAM,MAAO,OAAoD;AAEjE,cAAM,SAAS,OAAO,QAAQ,YAAY,eAAe,GAAG;AAC5D,YAAI,CAAC,QAAQ;AAAE,iBAAO,SAAS;AAAe,iBAAO,QAAQ,EAAE,MAAM,eAAe,SAAS,
|
|
4
|
+
"sourcesContent": ["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\nimport { EventEmitter } from \"node:events\";\nimport { lockOwnerAlive, withMaintenanceLock } from \"@sfmc-bds/management/node\";\nimport { toManagementError, type OperationRecord } from \"@sfmc-bds/management\";\n\nexport function atomicJson(file: string, value: unknown) {\n fs.mkdirSync(path.dirname(file), { recursive: true });\n const temp = `${file}.${randomUUID()}.tmp`;\n fs.writeFileSync(temp, JSON.stringify(value, null, 2) + \"\\n\", { mode: 0o600 });\n fs.renameSync(temp, file);\n}\nexport type TaskContext = { phase: (name: string, message?: string) => void; id: string };\n\n/** \u4EFB\u52A1\u5C5E\u4E8E\u5B88\u62A4\u8FDB\u7A0B\u800C\u975E SSH \u4F1A\u8BDD\uFF1B\u5F02\u5E38\u6062\u590D\u53EA\u6807\u8BB0\uFF0C\u4E0D\u91CD\u653E\u5199\u5165\u3002 */\nexport class TaskStore extends EventEmitter {\n private queue: Promise<unknown> = Promise.resolve();\n private directory: string;\n constructor(private root: string) {\n super();\n this.directory = path.join(root, \".sfmc\", \"operations\");\n fs.mkdirSync(this.directory, { recursive: true });\n for (const record of this.list()) {\n if (record.status === \"queued\" || record.status === \"running\") {\n const pid = (record as OperationRecord & { workerPid?: number }).workerPid;\n // \u4E0E\u7EF4\u62A4\u9501\u540C\u4E00\u5957\u63A2\u6D3B\uFF1A\u6743\u9650\u4E0D\u8DB3\u4E0D\u7B97\u9000\u51FA\uFF0C\u907F\u514D\u628A\u8FD8\u5728\u8DD1\u7684\u7EF4\u62A4\u8FDB\u7A0B\u6807\u6210\u4E2D\u65AD\u3002\n const active = typeof pid === \"number\" && lockOwnerAlive(pid);\n if (!active) { record.status = \"interrupted\"; record.error = { code: \"unavailable\", message: \"\u6267\u884C\u8FDB\u7A0B\u5F02\u5E38\u9000\u51FA\uFF1B\u8BF7\u68C0\u67E5\u5B9E\u4F8B\u5B9E\u9645\u72B6\u6001\uFF0C\u4EFB\u52A1\u4E0D\u4F1A\u81EA\u52A8\u91CD\u53D1\" }; this.save(record); }\n }\n }\n }\n get(id: string): OperationRecord {\n if (!/^[a-zA-Z0-9-]{1,80}$/.test(id)) throw new Error(\"\u975E\u6CD5\u4EFB\u52A1\u7F16\u53F7\");\n return JSON.parse(fs.readFileSync(path.join(this.directory, `${id}.json`), \"utf8\")) as OperationRecord;\n }\n list(): OperationRecord[] {\n return fs.readdirSync(this.directory).filter(name => /^[a-f0-9-]{36}\\.json$/.test(name)).flatMap(name => {\n try { const record = JSON.parse(fs.readFileSync(path.join(this.directory, name), \"utf8\")) as OperationRecord; return typeof record.createdAt === \"string\" && Array.isArray(record.phases) ? [record] : []; } catch { return []; }\n }).sort((a, b) => b.createdAt.localeCompare(a.createdAt)).slice(0, 200);\n }\n save(record: OperationRecord) {\n record.updatedAt = new Date().toISOString();\n atomicJson(path.join(this.directory, `${record.id}.json`), record);\n this.emit(\"operation\", record);\n }\n submit(kind: string, action: (context: TaskContext) => Promise<unknown>) {\n const record: OperationRecord = { id: randomUUID(), kind, title: kind, status: \"queued\", phases: [], createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() };\n this.save(record);\n this.queue = this.queue.catch(() => {}).then(async () => {\n try {\n await withMaintenanceLock(this.root, async () => {\n record.status = \"running\"; this.save(record);\n const phase = (name: string, message?: string) => {\n const previous = record.phases.at(-1);\n if (previous?.status === \"running\") { previous.status = \"done\"; previous.finishedAt = new Date().toISOString(); }\n record.phases.push({ name, status: \"running\", startedAt: new Date().toISOString(), ...(message ? { message } : {}) }); this.save(record);\n };\n record.result = await action({ phase, id: record.id });\n // \u72EC\u7ACB\u7EF4\u62A4\u8FDB\u7A0B\u63A5\u7BA1\u8BB0\u5F55\u4E4B\u540E\uFF0C\u5B88\u62A4\u8FDB\u7A0B\u4E0D\u518D\u8986\u76D6\u5176\u72B6\u6001\u3002\n if ((record.result as { detached?: boolean } | undefined)?.detached) return;\n const last = record.phases.at(-1); if (last) { last.status = \"done\"; last.finishedAt = new Date().toISOString(); }\n record.status = \"succeeded\"; this.save(record);\n });\n } catch (error) {\n record.status = \"failed\"; record.error = toManagementError(error);\n const last = record.phases.at(-1); if (last) last.status = \"failed\";\n this.save(record);\n }\n });\n return { operationId: record.id };\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/registry.d.ts
CHANGED
|
@@ -19,13 +19,19 @@ export interface RegistryEntry {
|
|
|
19
19
|
sdk?: string;
|
|
20
20
|
repo?: string;
|
|
21
21
|
tag?: string;
|
|
22
|
+
/** 索引里的展示名。旧缓存可能没有。 */
|
|
23
|
+
name?: string;
|
|
24
|
+
description?: string;
|
|
25
|
+
category?: string;
|
|
26
|
+
license?: string;
|
|
27
|
+
official?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* 安装前必须已经启用的模块 id。
|
|
30
|
+
* 字段缺失表示这份索引没有声明依赖,调用方应拒绝安装。
|
|
31
|
+
*/
|
|
32
|
+
requires?: string[];
|
|
22
33
|
}
|
|
23
34
|
export type RegistryIndex = Record<string, RegistryEntry>;
|
|
24
|
-
/**
|
|
25
|
-
* 解析 first-party index.json。
|
|
26
|
-
* 契约与 scripts/module-install/lib/registry-index.mjs#parseRegistryIndex 保持一致:
|
|
27
|
-
* `{ modules: { <id>: { npm? | repo+tag } } }`。忽略 `_` 前缀元数据键。
|
|
28
|
-
*/
|
|
29
35
|
export declare function parseRegistryIndex(json: unknown): RegistryIndex;
|
|
30
36
|
export interface RegistryResult {
|
|
31
37
|
index: RegistryIndex;
|
package/dist/registry.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAWH,QAAA,MAAM,qBAAqB,yBAAyB,CAAC;AACrD,QAAA,MAAM,oBAAoB,SAAS,CAAC;AAGpC,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAWH,QAAA,MAAM,qBAAqB,yBAAyB,CAAC;AACrD,QAAA,MAAM,oBAAoB,SAAS,CAAC;AAGpC,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,uBAAuB;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AACD,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AA8C1D,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,aAAa,CA2C/D;AAWD,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,aAAa,CAAC;IACrB,iFAAiF;IACjF,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,oBAAoB,CAAC,IAAI,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,cAAc,CAAC,CAmB9F;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,EAAE,CAIpD;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAQlF;AAED,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,CAAC"}
|
package/dist/registry.js
CHANGED
|
@@ -23,6 +23,18 @@ function writeCache(cache) {
|
|
|
23
23
|
} catch {
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
|
+
function optionalText(value) {
|
|
27
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
28
|
+
}
|
|
29
|
+
function optionalRequires(value) {
|
|
30
|
+
if (!Array.isArray(value)) return void 0;
|
|
31
|
+
const items = [];
|
|
32
|
+
for (const item of value) {
|
|
33
|
+
if (typeof item !== "string" || !item.trim()) return void 0;
|
|
34
|
+
items.push(item.trim());
|
|
35
|
+
}
|
|
36
|
+
return items;
|
|
37
|
+
}
|
|
26
38
|
function parseRegistryIndex(json) {
|
|
27
39
|
if (typeof json !== "object" || json === null || Array.isArray(json)) {
|
|
28
40
|
throw new Error("registry index must be a JSON object with a 'modules' field");
|
|
@@ -37,19 +49,31 @@ function parseRegistryIndex(json) {
|
|
|
37
49
|
if (k.startsWith("_")) continue;
|
|
38
50
|
if (typeof v !== "object" || v === null || Array.isArray(v)) continue;
|
|
39
51
|
const entry = v;
|
|
40
|
-
const
|
|
41
|
-
const
|
|
52
|
+
const npm = optionalText(entry.npm);
|
|
53
|
+
const repo = optionalText(entry.repo);
|
|
54
|
+
const tag = optionalText(entry.tag);
|
|
55
|
+
const hasNpm = Boolean(npm);
|
|
56
|
+
const hasGh = Boolean(repo && tag);
|
|
42
57
|
if (!hasNpm && !hasGh) continue;
|
|
43
58
|
const out = {};
|
|
44
|
-
if (
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
59
|
+
if (npm) out.npm = npm;
|
|
60
|
+
const version = optionalText(entry.version);
|
|
61
|
+
const sdk = optionalText(entry.sdk);
|
|
62
|
+
const name = optionalText(entry.name);
|
|
63
|
+
const description = optionalText(entry.description);
|
|
64
|
+
const category = optionalText(entry.category);
|
|
65
|
+
const license = optionalText(entry.license);
|
|
66
|
+
const requires = optionalRequires(entry.requires);
|
|
67
|
+
if (version) out.version = version;
|
|
68
|
+
if (sdk) out.sdk = sdk;
|
|
69
|
+
if (repo) out.repo = repo;
|
|
70
|
+
if (repo && tag) out.tag = tag;
|
|
71
|
+
if (name) out.name = name;
|
|
72
|
+
if (description) out.description = description;
|
|
73
|
+
if (category) out.category = category;
|
|
74
|
+
if (license) out.license = license;
|
|
75
|
+
if (typeof entry.official === "boolean") out.official = entry.official;
|
|
76
|
+
if (requires !== void 0) out.requires = requires;
|
|
53
77
|
filtered[k] = out;
|
|
54
78
|
}
|
|
55
79
|
return filtered;
|
package/dist/registry.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/registry.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * registry.ts \u2014 first-party sfmc-modules index resolution, shared between\n * the module CLI and the startup unknown-source warning.\n *\n * Talks to https://raw.githubusercontent.com/Tanya7z/sfmc-modules/main/index.json\n * (the registry lives in a separate repo to keep this one free of \"what\n * modules exist\" governance). Caches to <sfmc/src/.sfmc-registry-cache.json>\n * with a 1h TTL so a brief network blip doesn't degrade the CLI.\n *\n * Both helpers are best-effort: failures are surfaced as `null` and a\n * console warning, never an exception, because the CLI must still boot\n * when the registry is unreachable.\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport process from \"node:process\";\nimport { fileURLToPath } from \"node:url\";\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\nconst CACHE_PATH = path.join(__dirname, \".sfmc-registry-cache.json\");\nconst TTL_MS = 60 * 60 * 1000;\n\nconst DEFAULT_REGISTRY_REPO = \"Tanya7z/sfmc-modules\";\nconst DEFAULT_REGISTRY_TAG = \"main\";\nconst INDEX_URL = `https://raw.githubusercontent.com/${DEFAULT_REGISTRY_REPO}/${DEFAULT_REGISTRY_TAG}/index.json`;\n\nexport interface RegistryEntry {\n npm?: string;\n version?: string;\n sdk?: string;\n repo?: string;\n tag?: string;\n}\nexport type RegistryIndex = Record<string, RegistryEntry>;\n\ninterface RegistryCache {\n fetchedAt: number;\n index: RegistryIndex;\n}\n\nfunction readCache(): RegistryCache | null {\n try {\n if (!existsSync(CACHE_PATH)) return null;\n return JSON.parse(readFileSync(CACHE_PATH, \"utf8\")) as RegistryCache;\n } catch {\n return null;\n }\n}\n\nfunction writeCache(cache: RegistryCache): void {\n try {\n mkdirSync(path.dirname(CACHE_PATH), { recursive: true });\n writeFileSync(CACHE_PATH, JSON.stringify(cache, null, 2));\n } catch {\n /* cache write is best-effort */\n }\n}\n\n/**\n * \u89E3\u6790 first-party index.json\u3002\n * \u5951\u7EA6\u4E0E scripts/module-install/lib/registry-index.mjs#parseRegistryIndex \u4FDD\u6301\u4E00\u81F4\uFF1A\n * `{ modules: { <id>: { npm? | repo+tag } } }`\u3002\u5FFD\u7565 `_` \u524D\u7F00\u5143\u6570\u636E\u952E\u3002\n */\nexport function parseRegistryIndex(json: unknown): RegistryIndex {\n if (typeof json !== \"object\" || json === null || Array.isArray(json)) {\n throw new Error(\"registry index must be a JSON object with a 'modules' field\");\n }\n const root = json as Record<string, unknown>;\n const modulesRaw = root.modules;\n if (typeof modulesRaw !== \"object\" || modulesRaw === null || Array.isArray(modulesRaw)) {\n throw new Error(\"registry index must have a 'modules' object mapping id \u2192 entry\");\n }\n const filtered: RegistryIndex = {};\n for (const [k, v] of Object.entries(modulesRaw as Record<string, unknown>)) {\n if (k.startsWith(\"_\")) continue;\n if (typeof v !== \"object\" || v === null || Array.isArray(v)) continue;\n const entry = v as Record<string, unknown>;\n const
|
|
5
|
-
"mappings": "AAcA,SAAS,YAAY,WAAW,cAAc,qBAAqB;AACnE,OAAO,UAAU;AACjB,OAAO,aAAa;AACpB,SAAS,qBAAqB;AAE9B,MAAM,YAAY,KAAK,QAAQ,cAAc,YAAY,GAAG,CAAC;AAC7D,MAAM,aAAa,KAAK,KAAK,WAAW,2BAA2B;AACnE,MAAM,SAAS,KAAK,KAAK;AAEzB,MAAM,wBAAwB;AAC9B,MAAM,uBAAuB;AAC7B,MAAM,YAAY,qCAAqC,qBAAqB,IAAI,oBAAoB;
|
|
4
|
+
"sourcesContent": ["/**\n * registry.ts \u2014 first-party sfmc-modules index resolution, shared between\n * the module CLI and the startup unknown-source warning.\n *\n * Talks to https://raw.githubusercontent.com/Tanya7z/sfmc-modules/main/index.json\n * (the registry lives in a separate repo to keep this one free of \"what\n * modules exist\" governance). Caches to <sfmc/src/.sfmc-registry-cache.json>\n * with a 1h TTL so a brief network blip doesn't degrade the CLI.\n *\n * Both helpers are best-effort: failures are surfaced as `null` and a\n * console warning, never an exception, because the CLI must still boot\n * when the registry is unreachable.\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport process from \"node:process\";\nimport { fileURLToPath } from \"node:url\";\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\nconst CACHE_PATH = path.join(__dirname, \".sfmc-registry-cache.json\");\nconst TTL_MS = 60 * 60 * 1000;\n\nconst DEFAULT_REGISTRY_REPO = \"Tanya7z/sfmc-modules\";\nconst DEFAULT_REGISTRY_TAG = \"main\";\nconst INDEX_URL = `https://raw.githubusercontent.com/${DEFAULT_REGISTRY_REPO}/${DEFAULT_REGISTRY_TAG}/index.json`;\n\nexport interface RegistryEntry {\n npm?: string;\n version?: string;\n sdk?: string;\n repo?: string;\n tag?: string;\n /** \u7D22\u5F15\u91CC\u7684\u5C55\u793A\u540D\u3002\u65E7\u7F13\u5B58\u53EF\u80FD\u6CA1\u6709\u3002 */\n name?: string;\n description?: string;\n category?: string;\n license?: string;\n official?: boolean;\n /**\n * \u5B89\u88C5\u524D\u5FC5\u987B\u5DF2\u7ECF\u542F\u7528\u7684\u6A21\u5757 id\u3002\n * \u5B57\u6BB5\u7F3A\u5931\u8868\u793A\u8FD9\u4EFD\u7D22\u5F15\u6CA1\u6709\u58F0\u660E\u4F9D\u8D56\uFF0C\u8C03\u7528\u65B9\u5E94\u62D2\u7EDD\u5B89\u88C5\u3002\n */\n requires?: string[];\n}\nexport type RegistryIndex = Record<string, RegistryEntry>;\n\ninterface RegistryCache {\n fetchedAt: number;\n index: RegistryIndex;\n}\n\nfunction readCache(): RegistryCache | null {\n try {\n if (!existsSync(CACHE_PATH)) return null;\n return JSON.parse(readFileSync(CACHE_PATH, \"utf8\")) as RegistryCache;\n } catch {\n return null;\n }\n}\n\nfunction writeCache(cache: RegistryCache): void {\n try {\n mkdirSync(path.dirname(CACHE_PATH), { recursive: true });\n writeFileSync(CACHE_PATH, JSON.stringify(cache, null, 2));\n } catch {\n /* cache write is best-effort */\n }\n}\n\n/**\n * \u89E3\u6790 first-party index.json\u3002\n * \u5951\u7EA6\u4E0E scripts/module-install/lib/registry-index.mjs#parseRegistryIndex \u4FDD\u6301\u4E00\u81F4\uFF1A\n * `{ modules: { <id>: { npm? | repo+tag, name?, description?, requires? } } }`\u3002\u5FFD\u7565 `_` \u524D\u7F00\u5143\u6570\u636E\u952E\u3002\n * requires \u53EA\u6709\u5728\u5168\u90E8\u662F\u975E\u7A7A\u5B57\u7B26\u4E32\u65F6\u624D\u4FDD\u7559\uFF1B\u7F3A\u4E86\u8FD9\u4E2A\u5B57\u6BB5\u5C31\u8868\u793A\u7D22\u5F15\u6CA1\u6709\u7ED9\u51FA\u4F9D\u8D56\u58F0\u660E\u3002\n */\nfunction optionalText(value: unknown): string | undefined {\n return typeof value === \"string\" && value.trim() ? value.trim() : undefined;\n}\n\n/** \u7D22\u5F15 requires\u3002\u683C\u5F0F\u4E0D\u5BF9\u65F6\u8FD4\u56DE undefined\uFF0C\u8BA9\u5B89\u88C5\u65B9\u62D2\u7EDD\uFF0C\u800C\u4E0D\u662F\u5F53\u6210\u6CA1\u6709\u4F9D\u8D56\u3002 */\nfunction optionalRequires(value: unknown): string[] | undefined {\n if (!Array.isArray(value)) return undefined;\n const items: string[] = [];\n for (const item of value) {\n if (typeof item !== \"string\" || !item.trim()) return undefined;\n items.push(item.trim());\n }\n return items;\n}\n\nexport function parseRegistryIndex(json: unknown): RegistryIndex {\n if (typeof json !== \"object\" || json === null || Array.isArray(json)) {\n throw new Error(\"registry index must be a JSON object with a 'modules' field\");\n }\n const root = json as Record<string, unknown>;\n const modulesRaw = root.modules;\n if (typeof modulesRaw !== \"object\" || modulesRaw === null || Array.isArray(modulesRaw)) {\n throw new Error(\"registry index must have a 'modules' object mapping id \u2192 entry\");\n }\n const filtered: RegistryIndex = {};\n for (const [k, v] of Object.entries(modulesRaw as Record<string, unknown>)) {\n if (k.startsWith(\"_\")) continue;\n if (typeof v !== \"object\" || v === null || Array.isArray(v)) continue;\n const entry = v as Record<string, unknown>;\n const npm = optionalText(entry.npm);\n const repo = optionalText(entry.repo);\n const tag = optionalText(entry.tag);\n const hasNpm = Boolean(npm);\n const hasGh = Boolean(repo && tag);\n if (!hasNpm && !hasGh) continue;\n const out: RegistryEntry = {};\n if (npm) out.npm = npm;\n const version = optionalText(entry.version);\n const sdk = optionalText(entry.sdk);\n const name = optionalText(entry.name);\n const description = optionalText(entry.description);\n const category = optionalText(entry.category);\n const license = optionalText(entry.license);\n const requires = optionalRequires(entry.requires);\n if (version) out.version = version;\n if (sdk) out.sdk = sdk;\n if (repo) out.repo = repo;\n if (repo && tag) out.tag = tag;\n if (name) out.name = name;\n if (description) out.description = description;\n if (category) out.category = category;\n if (license) out.license = license;\n if (typeof entry.official === \"boolean\") out.official = entry.official;\n // \u7A7A\u6570\u7EC4\u8868\u793A\u6CA1\u6709\u989D\u5916\u4F9D\u8D56\uFF0C\u5FC5\u987B\u4FDD\u7559\uFF1B\u7F3A\u5B57\u6BB5\u624D\u8868\u793A\u7D22\u5F15\u6CA1\u58F0\u660E\u3002\n if (requires !== undefined) out.requires = requires;\n filtered[k] = out;\n }\n return filtered;\n}\n\nasync function fetchFresh(): Promise<RegistryIndex> {\n const res = await fetch(INDEX_URL, {\n headers: { \"User-Agent\": \"sfmc-cli\" },\n signal: AbortSignal.timeout(3000),\n });\n if (!res.ok) throw new Error(`HTTP ${res.status} for ${INDEX_URL}`);\n return parseRegistryIndex(await res.json());\n}\n\nexport interface RegistryResult {\n index: RegistryIndex;\n /** True when the index came from a stale cache because the live fetch failed. */\n stale: boolean;\n}\n\n/**\n * Resolve the first-party index.\n *\n * Order (default):\n * 1. If the cache is fresh (< TTL): return it without trying the network.\n * 2. Otherwise attempt a live fetch.\n * - success \u2192 write cache, return fresh.\n * - failure + cache exists \u2192 return cache with `stale: true` and a warning.\n * - failure + no cache \u2192 return `{ index: {}, stale: true }` and warn.\n *\n * `force: true` \u8DF3\u8FC7 TTL \u65E9\u9000,\u59CB\u7EC8\u5C1D\u8BD5\u62C9\u7F51(\u4F9B `mod search` \u4E3B\u52A8\u5237\u65B0\u5217\u8868)\u3002\n * \u5931\u8D25\u65F6\u4ECD\u56DE\u9000\u5230\u7F13\u5B58,\u6C38\u4E0D\u629B\u5F02\u5E38\u3002\n */\nexport async function resolveRegistryIndex(opts?: { force?: boolean }): Promise<RegistryResult> {\n const cache = readCache();\n if (!opts?.force && cache && Date.now() - cache.fetchedAt < TTL_MS) {\n return { index: cache.index, stale: false };\n }\n try {\n const fresh = await fetchFresh();\n writeCache({ fetchedAt: Date.now(), index: fresh });\n return { index: fresh, stale: false };\n } catch (err) {\n if (cache) {\n const msg = err instanceof Error ? err.message : String(err);\n process.stderr.write(`[sfmc] registry offline (${msg}); using cached index from ${new Date(cache.fetchedAt).toISOString()}\\n`);\n return { index: cache.index, stale: true };\n }\n const msg = err instanceof Error ? err.message : String(err);\n process.stderr.write(`[sfmc] registry unreachable and no cache: ${msg}\\n`);\n return { index: {}, stale: true };\n }\n}\n\n/**\n * \u4ECE\u672C\u5730\u7F13\u5B58\u540C\u6B65\u8BFB\u51FA registry \u6A21\u5757 id(\u4F9B Tab \u8865\u5168)\u3002\n * \u65E0\u7F13\u5B58\u65F6\u8FD4\u56DE\u7A7A\u6570\u7EC4,\u4E0D\u89E6\u53D1\u7F51\u7EDC\u3002\n */\nexport function listRegistryModuleIdsSync(): string[] {\n const cache = readCache();\n if (!cache) return [];\n return Object.keys(cache.index).sort((a, b) => a.localeCompare(b));\n}\n\n/**\n * Compare a set of installed module ids to the registry and return the\n * subset that isn't published in the first-party index. The \"best-effort\"\n * guarantee: this returns an empty array when the registry can't be reached\n * (we don't want a network outage to mark every local module as unknown).\n */\nexport async function findUnknownModules(installedIds: string[]): Promise<string[]> {\n const { index, stale } = await resolveRegistryIndex();\n if (stale && Object.keys(index).length === 0) {\n /* No cache either \u2014 silently treat every module as \"we couldn't tell\".\n * Otherwise offline mode would scream warnings at every boot. */\n return [];\n }\n return installedIds.filter((id) => !index[id]);\n}\n\nexport { DEFAULT_REGISTRY_REPO, DEFAULT_REGISTRY_TAG };"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["msg"]
|
|
7
7
|
}
|
package/dist/services.d.ts
CHANGED
|
@@ -143,7 +143,8 @@ declare class Service {
|
|
|
143
143
|
private cleanup;
|
|
144
144
|
}
|
|
145
145
|
export declare let services: Record<ServiceName, Service>;
|
|
146
|
-
|
|
146
|
+
/** 可只刷新即将启动的服务定义,保留其他运行中的服务及其探活参数。 */
|
|
147
|
+
export declare function refreshServices(names?: readonly ServiceName[]): void;
|
|
147
148
|
export declare const START_ORDER: ServiceName[];
|
|
148
149
|
export declare function startAll(): Promise<StartAllResult>;
|
|
149
150
|
/**
|
package/dist/services.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../src/services.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAA8B,SAAS,EAAkB,MAAM,2BAA2B,CAAC;AAuBvG,OAAO,EAAS,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAa3C,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAElE,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEpC,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,QAAQ,GAAG,QAAQ,CAAC;CAC7B;AAED,8CAA8C;AAC9C,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAE1D;AAED,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,IAAI,GAAG,QAAQ,GAAG,IAAI,GAAG,OAAO,CAAC;AACnE,eAAO,MAAM,aAAa,EAAE,WAAW,EAA2C,CAAC;AAEnF,yDAAyD;AACzD,MAAM,MAAM,YAAY,GAAG;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjH;;;;;;GAMG;AACH,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;AAEvD,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,WAAW,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtD,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,WAAW,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACtD;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;CACpC;AAOD,wBAAgB,gBAAgB,IAAI,SAAS,CAE5C;AAoED,UAAU,UAAU;IAClB,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;IAC/B,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,0BAA0B;IAC1B,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AAED,cAAM,OAAO;IACX,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,YAAY,GAAG,IAAI,CAAQ;IACjC,OAAO,UAAS;IAChB,GAAG,SAAK;IACR,SAAS,EAAE,IAAI,GAAG,IAAI,CAAQ;IAC9B,IAAI,EAAE,OAAO,EAAE,CAAM;IACrB,MAAM,oBAAsB;IAE5B,OAAO,CAAC,GAAG,CAAa;IACxB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,YAAY,CAA8C;IAElE,YAAY,GAAG,EAAE,UAAU,EAS1B;IAED,IAAI,MAAM,IAAI,MAAM,CAQnB;IAED,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,IAAI,CAUvD;IAEK,KAAK,IAAI,OAAO,CAAC,YAAY,CAAC,CA6GnC;IAED,OAAO,CAAC,eAAe;IAYvB,oCAAoC;IACpC,WAAW,IAAI,IAAI,CAQlB;IAED,SAAS,IAAI,IAAI,CAEhB;IAED;;;;;;;;OAQG;IACG,IAAI,CAAC,QAAQ,UAAQ,EAAE,cAAc,UAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAkCrE;IAED;;;OAGG;YACW,gBAAgB;IAkC9B;;;OAGG;IACH,OAAO,CAAC,eAAe;IAiBvB,SAAS,IAAI,IAAI,CAchB;IAED;;;;OAIG;IACG,OAAO,IAAI,OAAO,CAAC,YAAY,CAAC,CAGrC;IAED,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,EAAE,CAElC;IAED;;OAEG;IACH,oBAAoB,IAAI,IAAI,CAG3B;IAED,OAAO,CAAC,OAAO;CAmBhB;
|
|
1
|
+
{"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../src/services.ts"],"names":[],"mappings":"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"}
|
package/dist/services.js
CHANGED
|
@@ -548,10 +548,21 @@ function createServices() {
|
|
|
548
548
|
})
|
|
549
549
|
};
|
|
550
550
|
}
|
|
551
|
+
const serviceStateListeners = /* @__PURE__ */ new Set();
|
|
551
552
|
let services = createServices();
|
|
552
|
-
function refreshServices() {
|
|
553
|
-
|
|
554
|
-
|
|
553
|
+
function refreshServices(names = SERVICE_NAMES) {
|
|
554
|
+
const previousDbPort = dbHealthPort;
|
|
555
|
+
const previousQqBackend = qqBackendMode;
|
|
556
|
+
const latest = createServices();
|
|
557
|
+
if (!names.includes("db")) dbHealthPort = previousDbPort;
|
|
558
|
+
if (!names.includes("qq")) qqBackendMode = previousQqBackend;
|
|
559
|
+
for (const name of names) {
|
|
560
|
+
const previous = services[name];
|
|
561
|
+
previous.forceStop();
|
|
562
|
+
for (const listener of serviceStateListeners) previous.events.off("state", listener);
|
|
563
|
+
services[name] = latest[name];
|
|
564
|
+
for (const listener of serviceStateListeners) services[name].events.on("state", listener);
|
|
565
|
+
}
|
|
555
566
|
}
|
|
556
567
|
const START_ORDER = ["db", "tunnel", "qq", "llbot", "bds"];
|
|
557
568
|
async function startAll() {
|
|
@@ -589,10 +600,12 @@ function onServiceStateChange(fn) {
|
|
|
589
600
|
const handler = (ev) => {
|
|
590
601
|
fn(ev);
|
|
591
602
|
};
|
|
603
|
+
serviceStateListeners.add(handler);
|
|
592
604
|
for (const service of Object.values(services)) {
|
|
593
605
|
service.events.on("state", handler);
|
|
594
606
|
}
|
|
595
607
|
return () => {
|
|
608
|
+
serviceStateListeners.delete(handler);
|
|
596
609
|
for (const service of Object.values(services)) {
|
|
597
610
|
service.events.off("state", handler);
|
|
598
611
|
}
|
package/dist/services.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/services.ts"],
|
|
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 findBedrockServerPids,\n isProcessAlive,\n killBedrockServerByImage,\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 } from \"node:child_process\";\nimport { EventEmitter } from \"node:events\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { isDaemonServer } from \"./daemon/role.js\";\nimport { onRemoteServiceStateChange } from \"./daemon/client.js\";\nimport { t } from \"./i18n/index.js\";\nimport { resolveLlbotLaunch } from \"./llbot-launch.js\";\nimport { inferLevel, pushLog as pushUnifiedLog } from \"./logs.js\";\nimport { findNodeServicePids, killNodeServiceByScript } 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 { startModuleSidecars, stopModuleSidecars } from \"./module-sidecars.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/** \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\n/**\n * \u5355\u670D\u52A1 stop \u7ED3\u679C\uFF0C\u4F9B CLI \u533A\u5206\u6587\u6848\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A`stop <name>` / `restart <name>` \u4E0E `stop all` \u90FD\u8D70 Service.stop\uFF0C\u4F46\u53EA\u6709\u524D\u8005\u4F1A\u5F97\u5230 external\u3002\n * - managed\uFF1A\u505C\u7684\u662F\u672C\u8FDB\u7A0B\u62C9\u8D77\u7684\u5B50\u8FDB\u7A0B\n * - external\uFF1A\u6CA1\u6709\u6258\u7BA1\u53E5\u67C4\uFF0C\u4F46\u6E05\u6389\u4E86\u5916\u90E8\u5B9E\u4F8B\uFF08\u4EC5 external=true \u65F6\uFF09\n * - idle\uFF1A\u672C\u6765\u5C31\u6CA1\u5728\u8DD1\uFF0C\u6216\u6279\u91CF\u505C\u6B62\u65F6\u6545\u610F\u4E0D\u52A8\u5916\u90E8\u5B9E\u4F8B\n */\nexport type StopKind = \"managed\" | \"external\" | \"idle\";\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\n/** \u6E05\u6389 qq-bridge \u8FD0\u884C\u65F6\u6587\u4EF6\uFF0C\u907F\u514D status \u4ECD\u628A\u5DF2\u9000\u51FA\u7684\u5916\u90E8\u8FDB\u7A0B\u5F53\u6210\u5B58\u6D3B\u3002 */\nfunction clearQqRuntimeFile(): void {\n try {\n const file = qqRuntimeStatusPath(ROOT);\n if (fs.existsSync(file)) fs.unlinkSync(file);\n } catch {\n /* \u8FD0\u884C\u65F6\u6587\u4EF6\u53EF\u80FD\u5DF2\u88AB\u8FDB\u7A0B\u81EA\u5DF1\u5220\u6389 */\n }\n}\n\n/**\n * \u6309\u955C\u50CF\u540D\u7ED3\u675F\u5916\u90E8 BDS\uFF0C\u5E76\u7B49\u5230\u8FDB\u7A0B\u4ECE\u8FDB\u7A0B\u8868\u6D88\u5931\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A\u4EC5\u5355\u670D\u52A1 stop / restart\uFF08Service.stop \u7684 external=true\uFF09\u6E05\u6389\u975E\u672C\u8FDB\u7A0B\u62C9\u8D77\u7684 bedrock\u3002\n * stop all \u4E0E\u5B88\u62A4\u8FDB\u7A0B shutdown \u4E0D\u8C03\u7528\u8FD9\u91CC\uFF0C\u907F\u514D taskkill \u8BEF\u6740\u540C\u673A\u5176\u4ED6 Bedrock\u3002\n * taskkill \u8FD4\u56DE\u540E\u8FDB\u7A0B\u8868\u53EF\u80FD\u4ECD\u77ED\u6682\u53EF\u89C1\uFF0Crestart \u4F1A\u7ACB\u523B\u63A2\u6D3B\uFF0C\u56E0\u6B64\u8FD9\u91CC\u7B49\u5230\u6D88\u5931\u518D\u8FD4\u56DE\u3002\n */\nasync function killExternalBedrock(): Promise<void> {\n await killBedrockServerByImage();\n clearBdsPidFile(ROOT);\n const deadline = Date.now() + 8000;\n while (Date.now() < deadline) {\n if ((await findBedrockServerPids()).length === 0) return;\n await new Promise((r) => setTimeout(r, 200));\n }\n await killBedrockServerByImage();\n clearBdsPidFile(ROOT);\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 if (this.name === \"bds\") {\n await startModuleSidecars(ROOT, (line, stream) => this.pushLog(line, stream));\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 /* \u5B50\u8FDB\u7A0B\u59CB\u7EC8\u7531\u5B88\u62A4\u8FDB\u7A0B\u7528\u7BA1\u9053\u6301\u6709\uFF08stdin \u53D1 stop\u3001stdout \u65E5\u5FD7\u3001autoRestart\uFF09\uFF1B\n * \u4E0D\u518D\u5BF9\u5B50\u670D\u52A1\u505A detached/unref\u2014\u2014\u8131\u79BB\u7EC8\u7AEF\u7531 daemon \u8FDB\u7A0B\u81EA\u8EAB\u8D1F\u8D23\u3002\n * windowsHide\uFF1AWindows \u4E0A\u907F\u514D\u4E3A\u6BCF\u4E2A\u670D\u52A1\u5F39\u51FA\u7A7A\u767D\u63A7\u5236\u53F0\uFF08stdout \u5DF2 pipe\uFF0C\u7A97\u5185\u65E0\u8F93\u51FA\uFF09\u3002 */\n const spawnOpts = {\n cwd: this.def.cwd,\n stdio: [\"pipe\", \"pipe\", \"pipe\"] as Array<\"pipe\">,\n env: this.def.env ? { ...process.env, ...this.def.env } : process.env,\n windowsHide: true,\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 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 /**\n * \u505C\u6B62\u672C\u670D\u52A1\u3002\u6258\u7BA1\u5B50\u8FDB\u7A0B\u59CB\u7EC8\u8D70\u539F\u6765\u7684\u4F18\u96C5\u505C\u6B62\uFF08stdin stop / SIGTERM\uFF09\u3002\n * @param external \u662F\u5426\u989D\u5916\u6E05\u6389\u975E\u672C\u8FDB\u7A0B\u62C9\u8D77\u7684\u5916\u90E8\u5B9E\u4F8B\u3002\u9ED8\u8BA4 false\uFF0C\u53EA\u505C\u6258\u7BA1\u5B50\u8FDB\u7A0B\u3002\n * \u4E24\u79CD\u8C03\u7528\u573A\u666F\uFF1A\n * - true\uFF1A\u5355\u670D\u52A1 `stop <name>` / `restart <name>`\uFF08cmdStopLocal\uFF0C\u4EE5\u53CA\u4ECD\u5B58\u5728\u7684 Service.restart\uFF09\u3002\n * BDS \u5916\u90E8\u8D70 killExternalBedrock\uFF1Bdb/qq \u5916\u90E8\u8D70 killNodeServiceByScript\u3002\n * - false \u6216\u4E0D\u4F20\uFF1A`stop all`\u3001`restart -all`\u3001\u5B88\u62A4\u8FDB\u7A0B shutdown / SIGTERM\uFF08\u7ECF stopAll\uFF09\u3002\n * \u4E0D\u6309\u955C\u50CF\u540D\u6216\u811A\u672C\u540D\u6740\u5916\u90E8\u8FDB\u7A0B\uFF0C\u907F\u514D\u8BEF\u6740\u540C\u673A\u5176\u4ED6 Bedrock\u3001db\u3001qq\u3002\n */\n async stop(external = false, forceOnTimeout = true): Promise<StopKind> {\n this.manualStop = true;\n if (this.restartTimer) {\n clearTimeout(this.restartTimer);\n this.restartTimer = null;\n }\n\n /* \u4E0E\u539F\u5148 cmdStop \u4E00\u81F4\uFF1A\u4EC5\u5355\u670D\u52A1\u505C\u6B62\u65F6\uFF0CBDS \u88AB\u5224\u5B9A\u4E3A\u5916\u90E8\u624D\u6309\u955C\u50CF\u7ED3\u675F\uFF0C\u4E0D\u8D70 stdin stop */\n if (external && this.name === \"bds\") {\n const probe = await probeBdsStatus({\n managedPid: this.pid,\n hasStdin: Boolean(this.proc?.stdin),\n rootDir: ROOT,\n });\n if (probe.state === \"external\") {\n await killExternalBedrock();\n this.dropStaleHandle();\n return \"external\";\n }\n }\n\n const managed = Boolean(this.proc && this.running);\n if (managed) await this.stopManagedChild(forceOnTimeout);\n\n /* db / qq\uFF1A\u4EC5\u5355\u670D\u52A1\u505C\u6B62\u65F6\uFF0C\u6258\u7BA1\u505C\u5B8C\u540E\u4ECD\u6309\u811A\u672C\u518D\u626B\u4E00\u904D\uFF0C\u6E05\u6389\u540C\u673A\u4E0A\u7684\u5916\u90E8\u5B9E\u4F8B */\n if (external && (this.name === \"db\" || this.name === \"qq\")) {\n const killed = await killNodeServiceByScript(this.name);\n if (this.name === \"qq\") clearQqRuntimeFile();\n if (managed) return \"managed\";\n return killed.length > 0 ? \"external\" : \"idle\";\n }\n\n if (managed) return \"managed\";\n return \"idle\";\n }\n\n /**\n * \u4F18\u96C5\u505C\u6B62\u5F53\u524D\u6258\u7BA1\u5B50\u8FDB\u7A0B\uFF08stdin stop \u6216 SIGTERM\uFF0C\u8D85\u65F6\u540E SIGKILL\uFF09\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1AService.stop \u786E\u8BA4\u672C\u8FDB\u7A0B\u4ECD\u6301\u6709\u5B50\u8FDB\u7A0B\u65F6\u8C03\u7528\uFF1B\u903B\u8F91\u4E0E\u539F\u5148 stop \u4E3B\u4F53\u76F8\u540C\u3002\n */\n private async stopManagedChild(forceOnTimeout = true): Promise<void> {\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, reject) => {\n const timeout = setTimeout(() => {\n if (!forceOnTimeout && this.proc) {\n reject(new Error(`${this.name} \u672A\u5728\u8D85\u65F6\u5185\u4F18\u96C5\u9000\u51FA\uFF1B\u5DF2\u4E2D\u6B62\u7EF4\u62A4\uFF0C\u4E0D\u5F3A\u5236\u7ED3\u675F\u8FDB\u7A0B`));\n return;\n }\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 /**\n * \u5916\u90E8\u505C\u670D\u540E\u4E22\u6389\u5931\u6548\u7684\u6258\u7BA1\u53E5\u67C4\uFF0C\u907F\u514D\u7D27\u63A5\u7740\u7684 start/restart \u8BEF\u5224\u300C\u5DF2\u5728\u8FD0\u884C\u300D\u3002\n * \u4E0D\u8D70 cleanup\uFF1A\u90A3\u4F1A\u8FDE\u5E26\u505C\u6389\u6A21\u5757\u9644\u5C5E\u8FDB\u7A0B\uFF0C\u800C\u539F\u5148\u7684\u5916\u90E8 BDS stop \u5E76\u4E0D\u8FD9\u4E48\u505A\u3002\n */\n private dropStaleHandle(): void {\n if (!this.running && !this.proc) return;\n const stale = this.proc;\n this.proc = null;\n this.running = false;\n this.pid = 0;\n this.startTime = null;\n this.events.emit(\"state\", { name: this.name, running: false, pid: 0 });\n if (stale && stale.exitCode === null) {\n try {\n stale.kill(\"SIGKILL\");\n } catch {\n /* \u5916\u90E8\u955C\u50CF\u7ED3\u675F\u65F6\u8FD9\u4E2A\u53E5\u67C4\u53EF\u80FD\u5DF2\u7ECF\u9000\u51FA */\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 /**\n * \u5355\u670D\u52A1\u91CD\u542F\uFF1A\u5148\u505C\u518D\u8D77\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A\u82E5\u4ECD\u6709\u8C03\u7528\u65B9\u76F4\u63A5\u8D70 Service.restart\uFF08\u4E0D\u7ECF cmdStopLocal\uFF09\uFF0C\u4E0E\u5355\u670D\u52A1 stop \u4E00\u6837\u6E05\u5916\u90E8\u5B9E\u4F8B\uFF08external true\uFF09\u3002\n * \u6279\u91CF `restart -all` \u548C\u5B88\u62A4\u8FDB\u7A0B shutdown \u4E0D\u8D70\u8FD9\u91CC\uFF0C\u5B83\u4EEC\u7ECF stopAll \u8C03\u7528 stop(false)\uFF0C\u53EA\u505C\u6258\u7BA1\u5B50\u8FDB\u7A0B\u3002\n */\n async restart(): Promise<StartOutcome> {\n await this.stop(true);\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 stopModuleSidecars();\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\", \"qq_link\"]);\n ensurePackUpdateConfigFile();\n void import(\"./module-update/index.js\")\n .then((mod) => mod.ensureModuleUpdateConfigFile())\n .catch((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n pushUnifiedLog(message, \"module\", \"warn\");\n });\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 rawBdsPath = String(bdsCfg.bds_path ?? \"\").trim();\n if (process.platform !== \"win32\" && /^[A-Za-z]:[\\\\/]/.test(rawBdsPath)) {\n throw new Error(\n `configs/bds_updater.json \u7684 bds_path \u662F Windows \u8DEF\u5F84\uFF08${rawBdsPath}\uFF09\u3002` +\n `\u8BF7\u6539\u6210\u76F8\u5BF9 SFMC_ROOT \u7684\u8DEF\u5F84\uFF0C\u4F8B\u5982 \"BDS\"\u3002`\n );\n }\n const bdsPath = path.resolve(ROOT, rawBdsPath || \".\");\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(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 /* \u5D29\u6E83\u62C9\u8D77\u7531 sfmc daemon \u56FA\u5B9A\u5F00\u542F\uFF1B\u4E0D\u518D\u8BFB bds_updater.crash_restart */\n autoRestart: true,\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 /* \u4E1A\u52A1\u6A21\u5757\u66F4\u65B0\u53EA\u8D70\u663E\u5F0F\u547D\u4EE4\uFF0C\u4E0D\u8FDB\u5F00\u670D\u94A9\u5B50\u3002 */\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\n/**\n * \u6309\u542F\u52A8\u9006\u5E8F\u505C\u6B62\u672C\u8FDB\u7A0B\u62C9\u8D77\u7684\u6258\u7BA1\u5B50\u8FDB\u7A0B\uFF08\u4F18\u96C5 stop / stdin stop\uFF09\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A`stop all`\u3001`restart -all`\u3001\u5B88\u62A4\u8FDB\u7A0B shutdown / SIGTERM\u3002\n * \u663E\u5F0F\u4F20 external false\uFF1A\u4E0D\u6309\u955C\u50CF\u540D\u6216\u811A\u672C\u540D\u6740\u5916\u90E8 BDS / db / qq\u3002\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(false));\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 /* CLI \u8FDB\u7A0B\u4E0D\u6301\u6709 Service \u5B50\u8FDB\u7A0B\uFF1A\u6539\u8BA2\u5B88\u62A4\u8FDB\u7A0B\u63A8\u9001\u7684\u8FDC\u7A0B\u72B6\u6001\u4E8B\u4EF6 */\n if (!isDaemonServer()) {\n return onRemoteServiceStateChange(fn);\n }\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 * CLI \u8FDB\u7A0B\u7ECF\u5B88\u62A4\u8FDB\u7A0B RPC \u83B7\u53D6\uFF1B\u5B88\u62A4\u8FDB\u7A0B\u5185\u8D70\u672C\u5730\u63A2\u6D3B\u3002\n */\nexport async function queryServicesRuntime(): Promise<ServiceStatus[]> {\n if (!isDaemonServer()) {\n const { queryRuntimeViaDaemon } = await import(\"./daemon/client.js\");\n return queryRuntimeViaDaemon();\n }\n return queryServicesRuntimeLocal();\n}\n\n/** \u5B88\u62A4\u8FDB\u7A0B\u5185\u672C\u5730\u63A2\u6D3B\u5B9E\u73B0\uFF08\u4EA6\u4F9B server status \u7EC4\u88C5 rows\uFF09 */\nexport async function queryServicesRuntimeLocal(): 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": "AAiBA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAEP,SAAS,YAAY,kBAAkB,2BAA2B;AAClE;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,6BAA6B;AACtC,SAAS,aAAgC;AACzC,SAAS,oBAAoB;AAC7B,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,sBAAsB;AAC/B,SAAS,kCAAkC;AAC3C,SAAS,SAAS;AAClB,SAAS,0BAA0B;AACnC,SAAS,YAAY,WAAW,sBAAsB;AACtD,SAAS,qBAAqB,+BAA+B;AAC7D,SAAS,kCAAkC;AAC3C,SAAS,wBAAwB;AACjC,SAAS,8BAA8B;AACvC,SAAS,qBAAqB,0BAA0B;AACxD,SAAS,MAAM,oBAAoC;AAEnD,SAAS,QAAAA,aAAY;AASd,SAAS,oBAAoB,MAAwB;AAC1D,SAAO,KAAK,MAAM,OAAO,EAAE,OAAO,CAAC,SAAS,KAAK,KAAK,EAAE,SAAS,CAAC;AACpE;AAGO,MAAM,gBAA+B,CAAC,OAAO,MAAM,UAAU,MAAM,OAAO;AA8BjF,IAAI,eAAe;AAEnB,IAAI,gBAA2B;AAExB,SAAS,mBAA8B;AAC5C,SAAO;AACT;AAEA,eAAe,cAAc,MAAgC;AAC3D,MAAI;AACF,UAAM,MAAM,MAAM,MAAM,oBAAoB,IAAI,eAAe,EAAE,QAAQ,YAAY,QAAQ,GAAI,EAAE,CAAC;AACpG,WAAO,IAAI;AAAA,EACb,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,eAAe,iBAA2D;AACxE,QAAM,SAAS,SAA0B,oBAAoB,IAAI,CAAC;AAClE,MAAI,CAAC,QAAQ,OAAO,OAAO,OAAO,EAAG,QAAO,EAAE,OAAO,OAAO,KAAK,EAAE;AACnE,MAAI,CAAE,MAAM,eAAe,OAAO,GAAG,EAAI,QAAO,EAAE,OAAO,OAAO,KAAK,EAAE;AACvE,SAAO,EAAE,OAAO,MAAM,KAAK,OAAO,IAAI;AACxC;AAEA,MAAM,oBAAoB,KAAK,KAAK,MAAM,SAAS,qBAAqB;AAExE,eAAe,qBAAsC;AACnD,QAAM,MAAM,SAA2B,iBAAiB,GAAG;AAC3D,SAAO,OAAO,QAAQ,YAAY,MAAM,KAAM,MAAM,eAAe,GAAG,IAAK,MAAM;AACnF;AAEA,SAAS,mBAAmB,KAAmB;AAC7C,KAAG,UAAU,KAAK,QAAQ,iBAAiB,GAAG,EAAE,WAAW,KAAK,CAAC;AACjE,KAAG,cAAc,mBAAmB,KAAK,UAAU,EAAE,KAAK,WAAW,KAAK,IAAI,EAAE,CAAC,IAAI,IAAI;AAC3F;AAEA,SAAS,mBAAmB,KAAmB;AAC7C,MAAI,SAA2B,iBAAiB,GAAG,QAAQ,IAAK;AAChE,MAAI;AACF,OAAG,WAAW,iBAAiB;AAAA,EACjC,QAAQ;AAAA,EAER;AACF;AAGA,SAAS,qBAA2B;AAClC,MAAI;AACF,UAAM,OAAO,oBAAoB,IAAI;AACrC,QAAI,GAAG,WAAW,IAAI,EAAG,IAAG,WAAW,IAAI;AAAA,EAC7C,QAAQ;AAAA,EAER;AACF;AAQA,eAAe,sBAAqC;AAClD,QAAM,yBAAyB;AAC/B,kBAAgB,IAAI;AACpB,QAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,SAAO,KAAK,IAAI,IAAI,UAAU;AAC5B,SAAK,MAAM,sBAAsB,GAAG,WAAW,EAAG;AAClD,UAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,GAAG,CAAC;AAAA,EAC7C;AACA,QAAM,yBAAyB;AAC/B,kBAAgB,IAAI;AACtB;AA0BA,MAAM,QAAQ;AAAA,EACZ;AAAA,EACA;AAAA,EACA,OAA4B;AAAA,EAC5B,UAAU;AAAA,EACV,MAAM;AAAA,EACN,YAAyB;AAAA,EACzB,OAAkB,CAAC;AAAA,EACnB,SAAS,IAAI,aAAa;AAAA,EAElB;AAAA,EACA,aAAa;AAAA,EACb,mBAAmB;AAAA,EACnB,eAAqD;AAAA,EAE7D,YAAY,KAAiB;AAC3B,SAAK,OAAO,IAAI;AAChB,SAAK,QAAQ,IAAI;AACjB,SAAK,MAAM;AACX,QAAI,IAAI,SAAS,UAAU;AACzB,WAAK,OAAO,GAAG,UAAU,CAAC,MAAc,UAA4B;AAClE,uBAAe,MAAM,UAAU,KAAK;AAAA,MACtC,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,IAAI,SAAiB;AACnB,QAAI,CAAC,KAAK,aAAa,CAAC,KAAK,QAAS,QAAO;AAC7C,UAAM,KAAK,KAAK,IAAI,IAAI,KAAK,UAAU,QAAQ;AAC/C,UAAM,IAAI,KAAK,MAAM,KAAK,GAAK;AAC/B,UAAM,IAAI,KAAK,MAAM,IAAI,EAAE;AAC3B,QAAI,IAAI,EAAG,QAAO,GAAG,CAAC,KAAK,IAAI,EAAE;AACjC,QAAI,IAAI,EAAG,QAAO,GAAG,CAAC;AACtB,WAAO,GAAG,KAAK,MAAM,KAAK,GAAI,CAAC;AAAA,EACjC;AAAA,EAEA,QAAQ,MAAc,QAAmC;AACvD,UAAM,OAAgB,EAAE,MAAM,oBAAI,KAAK,GAAG,MAAM,OAAO;AACvD,SAAK,KAAK,KAAK,IAAI;AACnB,QAAI,KAAK,KAAK,SAAS,IAAM,MAAK,KAAK,OAAO,GAAG,KAAK,KAAK,SAAS,GAAI;AACxE,SAAK,OAAO,KAAK,OAAO,IAAI;AAG5B,UAAM,QAAQ,WAAW,WAAW,UAAU,WAAW,IAAI;AAC7D,mBAAe,MAAM,KAAK,MAAM,KAAK;AACrC,SAAK,IAAI,YAAY,IAAI;AAAA,EAC3B;AAAA,EAEA,MAAM,QAA+B;AACnC,QAAI,KAAK,iBAAkB,OAAM,IAAI,MAAM,kEAAgB;AAC3D,QAAI,KAAK,QAAS,QAAO,EAAE,QAAQ,UAAU;AAC7C,QAAI,KAAK,SAAS,OAAO;AACvB,YAAM,QAAQ,MAAM,eAAe,EAAE,SAAS,KAAK,CAAC;AACpD,UAAI,MAAM,UAAU,WAAW;AAC7B,cAAM,IAAI;AAAA,UACR,EAAE,yBAAyB;AAAA,YACzB,KAAK,OAAO,MAAM,GAAG;AAAA,YACrB,MAAM,EAAE,MAAM,UAAU,YAAY,gBAAgB,qBAAqB;AAAA,UAC3E,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AACA,QAAI,KAAK,SAAS,QAAQ,KAAK,SAAS,MAAM;AAC5C,YAAM,WAAW,MAAM,oBAAoB,KAAK,IAAI;AACpD,UAAI,SAAS,SAAS,GAAG;AACvB,eAAO,EAAE,QAAQ,UAAU;AAAA,MAC7B;AAAA,IACF;AACA,QAAI,KAAK,SAAS,YAAa,MAAM,mBAAmB,EAAI,QAAO,EAAE,QAAQ,UAAU;AACvF,QAAI,KAAK,IAAI,UAAU;AACrB,YAAM,IAAI,KAAK,IAAI,SAAS;AAC5B,UAAI,GAAG;AAEL,YAAI,KAAK,IAAI,SAAU,QAAO,EAAE,QAAQ,WAAW,QAAQ,EAAE;AAC7D,cAAM,IAAI,MAAM,CAAC;AAAA,MACnB;AAAA,IACF;AACA,QAAI,KAAK,IAAI,aAAa;AACxB,YAAM,KAAK,IAAI,YAAY;AAAA,IAC7B;AACA,QAAI,KAAK,SAAS,OAAO;AACvB,YAAM,oBAAoB,MAAM,CAAC,MAAM,WAAW,KAAK,QAAQ,MAAM,MAAM,CAAC;AAAA,IAC9E;AAEA,QAAI,KAAK,iBAAkB,OAAM,IAAI,MAAM,kEAAgB;AAC3D,SAAK,aAAa;AAIlB,UAAM,YAAY;AAAA,MAChB,KAAK,KAAK,IAAI;AAAA,MACd,OAAO,CAAC,QAAQ,QAAQ,MAAM;AAAA,MAC9B,KAAK,KAAK,IAAI,MAAM,EAAE,GAAG,QAAQ,KAAK,GAAG,KAAK,IAAI,IAAI,IAAI,QAAQ;AAAA,MAClE,aAAa;AAAA,IACf;AACA,UAAM,QAAQ,KAAK,IAAI,UACnB,aAAa,KAAK,IAAI,SAAS,KAAK,IAAI,QAAQ,CAAC,GAAG,SAAS,IAC7D,MAAM,KAAK,IAAI,KAAe,KAAK,IAAI,QAAQ,CAAC,GAAG,SAAS;AAChE,SAAK,OAAO;AACZ,SAAK,MAAM,MAAM,OAAO;AACxB,SAAK,UAAU;AACf,SAAK,YAAY,oBAAI,KAAK;AAC1B,QAAI,KAAK,SAAS,SAAS,KAAK,MAAM,GAAG;AACvC,sBAAgB,KAAK,KAAK,IAAI;AAAA,IAChC;AACA,QAAI,KAAK,SAAS,YAAY,KAAK,MAAM,EAAG,oBAAmB,KAAK,GAAG;AACvE,SAAK,OAAO,KAAK,UAAU,gBAAgB,KAAK,GAAG,KAAK,MAAM;AAC9D,SAAK,OAAO,KAAK,SAAS,EAAE,MAAM,KAAK,MAAM,SAAS,MAAM,KAAK,KAAK,IAAI,CAAC;AAE3E,QAAI,oBAAoB;AACxB,UAAM,aAAa,CAAC,SAAyB;AAC3C,UAAI,KAAK,SAAS,SAAS,kBAAmB;AAC9C,0BAAoB;AACpB,YAAM,UAAU,KAAK,cAAc,KAAK;AACxC,WAAK,sBAAsB,UAAU,SAAS,SAAS,UAAU,SAAY,QAAQ,QAAQ,GAAG,IAAI;AAAA,QAClG,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AACA,QAAI,KAAK,SAAS,OAAO;AACvB,YAAM,KAAK,SAAS,MAAM;AACxB,aAAK,sBAAsB,SAAS,QAAW,EAAE,MAAM,aAAa,CAAC;AAAA,MACvE,CAAC;AAAA,IACH;AAEA,UAAM,GAAG,SAAS,CAAC,MAAM;AACvB,WAAK,OAAO,KAAK,UAAU,kBAAkB,EAAE,OAAO,IAAI,OAAO;AACjE,iBAAW;AACX,WAAK,QAAQ;AACb,WAAK,gBAAgB;AAAA,IACvB,CAAC;AAED,QAAI,oBAAoB;AACxB,UAAM,QAAQ,GAAG,QAAQ,CAAC,MAAc;AACtC,UAAI,KAAK,SAAS,SAAS,KAAK,WAAW;AACzC,6BAAqB,EAAE,SAAS;AAChC,cAAM,eAAe,kBAAkB,MAAM,OAAO;AACpD,6BAAqB,aAAa,IAAI,KAAK,IAAI,MAAM,KAAK;AAC1D,mBAAW,eAAe,aAAc,kBAAiB,MAAM,aAAa,KAAK,KAAK,KAAK,UAAU,QAAQ,CAAC;AAAA,MAChH;AACA,iBAAW,QAAQ,oBAAoB,EAAE,SAAS,CAAC,GAAG;AACpD,aAAK,QAAQ,MAAM,QAAQ;AAAA,MAC7B;AAAA,IACF,CAAC;AACD,UAAM,QAAQ,GAAG,QAAQ,CAAC,MAAc;AACtC,iBAAW,QAAQ,oBAAoB,EAAE,SAAS,CAAC,GAAG;AACpD,aAAK,QAAQ,MAAM,QAAQ;AAAA,MAC7B;AAAA,IACF,CAAC;AAED,UAAM,GAAG,QAAQ,CAAC,SAAS;AACzB,UAAI,KAAK,QAAQ,KAAK,SAAS,MAAO;AACtC,WAAK,OAAO,KAAK,UAAU,iBAAiB,IAAI,KAAK,MAAM;AAC3D,iBAAW,IAAI;AACf,WAAK,QAAQ;AACb,WAAK,gBAAgB;AAAA,IACvB,CAAC;AACD,WAAO,EAAE,QAAQ,UAAU;AAAA,EAC7B;AAAA,EAEQ,kBAAwB;AAC9B,QAAI,KAAK,cAAc,KAAK,oBAAoB,CAAC,KAAK,IAAI,eAAe,KAAK,aAAc;AAC5F,SAAK,eAAe,WAAW,MAAM;AACnC,WAAK,eAAe;AACpB,UAAI,CAAC,KAAK,oBAAoB,CAAC,KAAK,cAAc,CAAC,KAAK,SAAS;AAC/D,aAAK,KAAK,MAAM,EAAE,MAAM,CAAC,MAAa;AACpC,eAAK,OAAO,KAAK,UAAU,mBAAmB,EAAE,OAAO,IAAI,OAAO;AAAA,QACpE,CAAC;AAAA,MACH;AAAA,IACF,GAAG,KAAK,IAAI,YAAY;AAAA,EAC1B;AAAA;AAAA,EAGA,cAAoB;AAClB,QAAI,KAAK,SAAS,MAAO;AACzB,QAAI,KAAK,iBAAkB,OAAM,IAAI,MAAM,gDAAa;AACxD,SAAK,mBAAmB;AACxB,QAAI,KAAK,cAAc;AACrB,mBAAa,KAAK,YAAY;AAC9B,WAAK,eAAe;AAAA,IACtB;AAAA,EACF;AAAA,EAEA,YAAkB;AAChB,SAAK,mBAAmB;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,KAAK,WAAW,OAAO,iBAAiB,MAAyB;AACrE,SAAK,aAAa;AAClB,QAAI,KAAK,cAAc;AACrB,mBAAa,KAAK,YAAY;AAC9B,WAAK,eAAe;AAAA,IACtB;AAGA,QAAI,YAAY,KAAK,SAAS,OAAO;AACnC,YAAM,QAAQ,MAAM,eAAe;AAAA,QACjC,YAAY,KAAK;AAAA,QACjB,UAAU,QAAQ,KAAK,MAAM,KAAK;AAAA,QAClC,SAAS;AAAA,MACX,CAAC;AACD,UAAI,MAAM,UAAU,YAAY;AAC9B,cAAM,oBAAoB;AAC1B,aAAK,gBAAgB;AACrB,eAAO;AAAA,MACT;AAAA,IACF;AAEA,UAAM,UAAU,QAAQ,KAAK,QAAQ,KAAK,OAAO;AACjD,QAAI,QAAS,OAAM,KAAK,iBAAiB,cAAc;AAGvD,QAAI,aAAa,KAAK,SAAS,QAAQ,KAAK,SAAS,OAAO;AAC1D,YAAM,SAAS,MAAM,wBAAwB,KAAK,IAAI;AACtD,UAAI,KAAK,SAAS,KAAM,oBAAmB;AAC3C,UAAI,QAAS,QAAO;AACpB,aAAO,OAAO,SAAS,IAAI,aAAa;AAAA,IAC1C;AAEA,QAAI,QAAS,QAAO;AACpB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,iBAAiB,iBAAiB,MAAqB;AACnE,QAAI,CAAC,KAAK,QAAQ,CAAC,KAAK,QAAS;AACjC,SAAK,OAAO,KAAK,UAAU,eAAe,MAAM;AAEhD,QAAI,KAAK,IAAI,eAAe,KAAK,KAAK,OAAO;AAC3C,WAAK,KAAK,MAAM,MAAM,KAAK,IAAI,cAAc,IAAI;AAAA,IACnD,OAAO;AACL,WAAK,KAAK,KAAK,SAAS;AAAA,IAC1B;AAEA,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,UAAU,WAAW,MAAM;AAC/B,YAAI,CAAC,kBAAkB,KAAK,MAAM;AAChC,iBAAO,IAAI,MAAM,GAAG,KAAK,IAAI,6IAA0B,CAAC;AACxD;AAAA,QACF;AACA,YAAI,KAAK,MAAM;AACb,eAAK,OAAO,KAAK,UAAU,cAAc,OAAO;AAChD,cAAI;AACF,iBAAK,KAAK,KAAK,SAAS;AAAA,UAC1B,QAAQ;AAAA,UAER;AAAA,QACF;AACA,gBAAQ;AAAA,MACV,GAAG,KAAK,IAAI,WAAW;AAEvB,WAAK,MAAM,GAAG,QAAQ,MAAM;AAC1B,qBAAa,OAAO;AACpB,gBAAQ;AAAA,MACV,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,kBAAwB;AAC9B,QAAI,CAAC,KAAK,WAAW,CAAC,KAAK,KAAM;AACjC,UAAM,QAAQ,KAAK;AACnB,SAAK,OAAO;AACZ,SAAK,UAAU;AACf,SAAK,MAAM;AACX,SAAK,YAAY;AACjB,SAAK,OAAO,KAAK,SAAS,EAAE,MAAM,KAAK,MAAM,SAAS,OAAO,KAAK,EAAE,CAAC;AACrE,QAAI,SAAS,MAAM,aAAa,MAAM;AACpC,UAAI;AACF,cAAM,KAAK,SAAS;AAAA,MACtB,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAAA,EAEA,YAAkB;AAChB,UAAM,QAAQ,KAAK;AACnB,SAAK,aAAa;AAClB,QAAI,KAAK,cAAc;AACrB,mBAAa,KAAK,YAAY;AAC9B,WAAK,eAAe;AAAA,IACtB;AACA,SAAK,QAAQ;AACb,QAAI,CAAC,MAAO;AACZ,QAAI;AACF,YAAM,KAAK,SAAS;AAAA,IACtB,QAAQ;AAAA,IAER;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,UAAiC;AACrC,UAAM,KAAK,KAAK,IAAI;AACpB,WAAO,KAAK,MAAM;AAAA,EACpB;AAAA,EAEA,cAAc,GAAsB;AAClC,WAAO,KAAK,KAAK,MAAM,CAAC,CAAC;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKA,uBAA6B;AAC3B,QAAI,CAAC,KAAK,WAAW,CAAC,KAAK,KAAM;AACjC,SAAK,QAAQ;AAAA,EACf;AAAA,EAEQ,UAAgB;AACtB,UAAM,aAAa,KAAK,WAAW,KAAK,SAAS;AACjD,UAAM,aAAa,KAAK;AACxB,SAAK,OAAO;AACZ,SAAK,UAAU;AACf,SAAK,MAAM;AACX,SAAK,YAAY;AACjB,QAAI,KAAK,SAAS,SAAS,aAAa,GAAG;AACzC,YAAM,UAAU,eAAe,IAAI;AACnC,UAAI,YAAY,YAAY;AAC1B,wBAAgB,IAAI;AAAA,MACtB;AACA,yBAAmB;AAAA,IACrB;AACA,QAAI,KAAK,SAAS,YAAY,aAAa,EAAG,oBAAmB,UAAU;AAC3E,QAAI,YAAY;AACd,WAAK,OAAO,KAAK,SAAS,EAAE,MAAM,KAAK,MAAM,SAAS,OAAO,KAAK,EAAE,CAAC;AAAA,IACvE;AAAA,EACF;AACF;AAEA,SAAS,iBAA+C;AAEtD,oBAAkB,MAAM,CAAC,eAAe,aAAa,aAAa,SAAS,CAAC;AAC5E,6BAA2B;AAC3B,OAAK,OAAO,0BAA0B,EACnC,KAAK,CAAC,QAAQ,IAAI,6BAA6B,CAAC,EAChD,MAAM,CAAC,UAAmB;AACzB,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,mBAAe,SAAS,UAAU,MAAM;AAAA,EAC1C,CAAC;AACH,QAAM,SAAS,kBAAkB,MAAM,oBAAoB,eAAe,EAAE,GAAG,2BAA2B,CAGzG;AACD,QAAM,QAAQ,kBAAkB,MAAM,kBAAkB,aAAa,EAAE,GAAG,kBAAkB,CAG3F;AACD,QAAM,QAAQ,kBAAkB,MAAM,kBAAkB,aAAa,EAAE,GAAG,kBAAkB,CAG3F;AACD,QAAM,aAAa,OAAO,OAAO,YAAY,EAAE,EAAE,KAAK;AACtD,MAAI,QAAQ,aAAa,WAAW,kBAAkB,KAAK,UAAU,GAAG;AACtE,UAAM,IAAI;AAAA,MACR,6EAAoD,UAAU;AAAA,IAEhE;AAAA,EACF;AACA,QAAM,UAAU,KAAK,QAAQ,MAAM,cAAc,GAAG;AACpD,QAAM,kBAAkB,MAAM,eAAe;AAC7C,QAAM,YAAY,MAAM,eAAe;AAEvC,QAAM,aAAa,CAAC,mBAAmB,MAAM,UAAU,cAAc;AACrE,QAAM,YAAY,MAAM,UAAU;AAClC,QAAM,gBAAgB,aAAa,cAAc,WAAW,YAAY;AACxE,QAAM,aAAa,OAAO,WAAW,QAAQ,EAAE,EAAE,KAAK;AACtD,QAAM,mBAAmB,WAAW,cAAc;AAClD,QAAM,eAAe,MAAM,OAAO,YAAY;AAC9C,QAAM,cAAc,mBAAmB,MAAM,OAAO,MAAM,MAAM,OAAO,GAAG;AAC1E,QAAM,YAAY,YAAY;AAC9B,QAAM,WAAW,YAAY;AAC7B,QAAM,SAAS,MAAM,WAAW;AAChC,iBAAe;AACf,kBAAgB,kBAAkB,UAAU;AAC5C,QAAM,SAAS,WAAW,OAAO;AACjC,QAAM,UAAU,kBAAkB,eAAe;AAEjD,SAAO;AAAA,IACL,KAAK,IAAI,QAAQ;AAAA,MACf,MAAM;AAAA,MACN,OAAO;AAAA,MACP,KAAK;AAAA,MACL,MAAM,CAAC;AAAA,MACP,KAAK;AAAA,MACL,KAAK,iBAAiB,OAAO;AAAA,MAC7B,aAAa;AAAA,MACb,aAAa;AAAA;AAAA,MAEb,aAAa;AAAA,MACb,cAAc;AAAA,MACd,UAAU,MAAM;AACd,YAAI,CAAC,GAAG,WAAW,MAAM,EAAG,QAAO,cAAc,MAAM;AACvD,eAAO;AAAA,MACT;AAAA,MACA,aAAa,YAAY;AACvB,4BAAoB,MAAM;AAG1B,cAAM,EAAE,oBAAoB,IAAI,MAAM,OAAO,kBAAkB;AAC/D,cAAM,oBAAoB,EAAE,aAAa,MAAM,CAAC;AAChD,cAAM,EAAE,yBAAyB,IAAI,MAAM,OAAO,wBAAwB;AAC1E,cAAM,yBAAyB;AAC/B,cAAM,EAAE,iBAAiB,IAAI,MAAM,OAAO,qBAAqB;AAC/D,cAAM,iBAAiB;AAAA,MACzB;AAAA,MACA,WAAW,CAAC,SACV,uBAAuB,MAAM;AAAA,QAC3B,MAAM;AAAA,QACN,GAAI,MAAM,YAAY,EAAE,WAAW,MAAM,UAAU,IAAI,CAAC;AAAA,MAC1D,CAAC;AAAA,IACL,CAAC;AAAA,IAED,IAAI,IAAI,QAAQ;AAAA,MACd,MAAM;AAAA,MACN,OAAO;AAAA,MACP,SAAS;AAAA,MACT,KAAK;AAAA,MACL,aAAa;AAAA,MACb,aAAa;AAAA,MACb,cAAc;AAAA,MACd,KAAK,EAAE,SAAS,OAAO,MAAM,EAAE;AAAA,IACjC,CAAC;AAAA,IAED,QAAQ,IAAI,QAAQ;AAAA,MAClB,MAAM;AAAA,MACN,OAAO;AAAA,MACP,KAAK;AAAA,MACL,MAAM;AAAA,QACJ;AAAA,QAAM;AAAA,QAAM;AAAA,QAAM;AAAA,QAAiB;AAAA,QAAM;AAAA,QACzC;AAAA,QAAM;AAAA,QAA4B;AAAA,QAAM;AAAA,QACxC;AAAA,QAAM;AAAA,QACN;AAAA,QAAM,aAAa,gBAAgB,cAAc,MAAM;AAAA,QACvD;AAAA,MACF;AAAA,MACA,KAAK;AAAA,MACL,aAAa;AAAA,MACb,aAAa;AAAA,MACb,cAAc;AAAA,MACd,UAAU,CAAC;AAAA,MACX,UAAU,MAAM;AACd,YAAI,CAAC,cAAe,QAAO;AAC3B,YAAI,CAAC,gCAAgC,KAAK,UAAU,EAAG,QAAO;AAC9D,YAAI,CAAC,OAAO,UAAU,gBAAgB,KAAK,mBAAmB,KAAK,mBAAmB,OAAO;AAC3F,iBAAO;AAAA,QACT;AACA,eAAO;AAAA,MACT;AAAA,IACF,CAAC;AAAA,IAED,IAAI,IAAI,QAAQ;AAAA,MACd,MAAM;AAAA,MACN,OAAO;AAAA,MACP,SAAS;AAAA,MACT,KAAK;AAAA,MACL,aAAa;AAAA;AAAA,MAEb,aAAa;AAAA,MACb,cAAc;AAAA,MACd,UAAU,CAAC,aAAa;AAAA,MACxB,UAAU,MAAM;AACd,YAAI,CAAC,UAAW,QAAO;AACvB,YAAI,WAAY,QAAO;AACvB,YAAI,CAAC,iBAAiB;AACpB,cAAI,CAAC,OAAO,MAAM,UAAU,UAAU,EAAE,EAAE,KAAK,KAAK,CAAC,OAAO,MAAM,UAAU,cAAc,EAAE,EAAE,KAAK,GAAG;AACpG,mBAAO;AAAA,UACT;AAAA,QACF;AACA,eAAO;AAAA,MACT;AAAA,IACF,CAAC;AAAA,IAED,OAAO,IAAI,QAAQ;AAAA,MACjB,MAAM;AAAA,MACN,OAAO;AAAA,MACP,KAAK;AAAA,MACL,MAAM,CAAC;AAAA,MACP,KAAK;AAAA,MACL,aAAa;AAAA,MACb,aAAa;AAAA,MACb,cAAc;AAAA;AAAA,MAEd,UAAU,CAAC,mBAAmB,CAAC;AAAA,MAC/B,UAAU,MAAM;AACd,YAAI,CAAC,gBAAiB,QAAO;AAC7B,YAAI,CAAC,aAAc,QAAO;AAC1B,YAAI,CAAC,GAAG,WAAW,SAAS,EAAG,QAAO,cAAc,SAAS;AAC7D,eAAO;AAAA,MACT;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAEO,IAAI,WAAyC,eAAe;AAE5D,SAAS,kBAAwB;AACtC,eAAa;AACb,aAAW,eAAe;AAC5B;AAEO,MAAM,cAA6B,CAAC,MAAM,UAAU,MAAM,SAAS,KAAK;AAE/E,eAAsB,WAAoC;AACxD,QAAM,SAAyB,EAAE,SAAS,CAAC,GAAG,SAAS,CAAC,GAAG,QAAQ,CAAC,EAAE;AACtE,aAAW,QAAQ,aAAa;AAC9B,UAAM,MAAM,SAAS,IAAI;AACzB,QAAI,CAAC,IAAK;AACV,QAAI;AACF,YAAM,UAAU,MAAM,IAAI,MAAM;AAChC,UAAI,QAAQ,WAAW,WAAW;AAChC,eAAO,QAAQ,KAAK,EAAE,MAAM,QAAQ,QAAQ,OAAO,CAAC;AACpD,YAAI,OAAO,KAAK,UAAU,YAAY,QAAQ,MAAM,IAAI,MAAM;AAAA,MAChE,OAAO;AACL,eAAO,QAAQ,KAAK,IAAI;AAAA,MAC1B;AAAA,IACF,SAAS,GAAG;AACV,YAAM,SAAU,EAAY;AAC5B,aAAO,OAAO,KAAK,EAAE,MAAM,OAAO,CAAC;AACnC,UAAI,OAAO,KAAK,UAAU,gBAAgB,MAAM,IAAI,OAAO;AAAA,IAC7D;AAAA,EACF;AACA,SAAO;AACT;AAOA,eAAsB,UAAyB;AAC7C,QAAM,UAAU,CAAC,GAAG,WAAW,EAC5B,QAAQ,EACR,IAAI,CAAC,SAAS,SAAS,IAAI,CAAC,EAC5B,OAAO,CAAC,YAAgC,QAAQ,OAAO,CAAC,EACxD,IAAI,CAAC,YAAY,QAAQ,KAAK,KAAK,CAAC;AACvC,QAAM,QAAQ,WAAW,OAAO;AAClC;AAEO,SAAS,eAAqB;AACnC,aAAW,WAAW,OAAO,OAAO,QAAQ,EAAG,SAAQ,UAAU;AACnE;AAKO,SAAS,qBAAqB,IAAiD;AAEpF,MAAI,CAAC,eAAe,GAAG;AACrB,WAAO,2BAA2B,EAAE;AAAA,EACtC;AACA,QAAM,UAAU,CAAC,OAAgC;AAC/C,OAAG,EAAE;AAAA,EACP;AACA,aAAW,WAAW,OAAO,OAAO,QAAQ,GAAG;AAC7C,YAAQ,OAAO,GAAG,SAAS,OAAO;AAAA,EACpC;AACA,SAAO,MAAM;AACX,eAAW,WAAW,OAAO,OAAO,QAAQ,GAAG;AAC7C,cAAQ,OAAO,IAAI,SAAS,OAAO;AAAA,IACrC;AAAA,EACF;AACF;AAGA,eAAe,sBAAsB,SAAoC;AACvE,MAAI,CAAC,QAAQ,QAAS,QAAO;AAC7B,MAAI,QAAQ,MAAM,KAAK,CAAE,MAAM,eAAe,QAAQ,GAAG,GAAI;AAC3D,YAAQ,qBAAqB;AAC7B,WAAO;AAAA,EACT;AACA,SAAO,QAAQ;AACjB;AAOA,eAAsB,uBAAiD;AACrE,MAAI,CAAC,eAAe,GAAG;AACrB,UAAM,EAAE,sBAAsB,IAAI,MAAM,OAAO,oBAAoB;AACnE,WAAO,sBAAsB;AAAA,EAC/B;AACA,SAAO,0BAA0B;AACnC;AAGA,eAAsB,4BAAsD;AAC1E,SAAO,QAAQ;AAAA,IACb,cAAc,IAAI,OAAO,SAAS;AAChC,YAAM,UAAU,SAAS,IAAI;AAC7B,UAAI,UAAU;AACd,UAAI,MAAM;AACV,UAAI,SAAS;AACb,UAAI;AAEJ,UAAI,SAAS,OAAO;AAClB,cAAM,QAAQ,MAAM,eAAe;AAAA,UACjC,YAAY,QAAQ;AAAA,UACpB,UAAU,QAAQ,QAAQ,MAAM,KAAK;AAAA,UACrC,SAAS;AAAA,QACX,CAAC;AACD,YAAI,MAAM,UAAU,WAAW;AAC7B,oBAAU;AACV,gBAAM,MAAM;AACZ,mBAAS,QAAQ;AACjB,sBAAY;AAAA,QAEd,WAAW,MAAM,UAAU,YAAY;AACrC,oBAAU;AACV,gBAAM,MAAM;AACZ,sBAAY;AAEZ,cAAI,QAAQ,WAAW,QAAQ,QAAQ,MAAM,KAAK;AAChD,oBAAQ,qBAAqB;AAAA,UAC/B,WAAW,QAAQ,WAAW,CAAE,MAAM,eAAe,QAAQ,GAAG,GAAI;AAClE,oBAAQ,qBAAqB;AAAA,UAC/B;AAAA,QACF,OAAO;AACL,oBAAU;AACV,cAAI,QAAQ,WAAW,QAAQ,MAAM;AACnC,oBAAQ,qBAAqB;AAAA,UAC/B;AAAA,QACF;AAAA,MACF,WAAW,SAAS,MAAM;AACxB,cAAM,UAAU,MAAM,sBAAsB,OAAO;AACnD,YAAI,SAAS;AACX,oBAAU;AACV,gBAAM,QAAQ;AACd,mBAAS,QAAQ;AACjB,sBAAY;AAAA,QACd,WAAW,MAAM,cAAc,YAAY,GAAG;AAC5C,oBAAU;AACV,sBAAY;AACZ,gBAAM,OAAO,MAAM,oBAAoB,IAAI;AAC3C,gBAAM,KAAK,CAAC,KAAK;AAAA,QACnB;AAAA,MACF,WAAW,SAAS,MAAM;AACxB,cAAM,UAAU,MAAM,sBAAsB,OAAO;AACnD,YAAI,SAAS;AACX,oBAAU;AACV,gBAAM,QAAQ;AACd,mBAAS,QAAQ;AACjB,sBAAY;AAAA,QACd,OAAO;AACL,gBAAM,QAAQ,MAAM,eAAe;AACnC,cAAI,MAAM,OAAO;AACf,sBAAU;AACV,kBAAM,MAAM;AACZ,wBAAY;AAAA,UACd,OAAO;AACL,kBAAM,OAAO,MAAM,oBAAoB,IAAI;AAC3C,gBAAI,KAAK,SAAS,GAAG;AACnB,wBAAU;AACV,oBAAM,KAAK,CAAC;AACZ,0BAAY;AAAA,YACd;AAAA,UACF;AAAA,QACF;AAAA,MACF,WAAW,SAAS,UAAU;AAC5B,cAAM,UAAU,MAAM,sBAAsB,OAAO;AACnD,YAAI,SAAS;AACX,oBAAU;AACV,gBAAM,QAAQ;AACd,mBAAS,QAAQ;AACjB,sBAAY;AAAA,QACd,OAAO;AACL,gBAAM,MAAM,mBAAmB;AAC/B,oBAAU,MAAM;AAChB,cAAI,QAAS,aAAY;AAAA,QAC3B;AAAA,MACF,OAAO;AACL,cAAM,UAAU,MAAM,sBAAsB,OAAO;AACnD,YAAI,SAAS;AACX,oBAAU;AACV,gBAAM,QAAQ;AACd,mBAAS,QAAQ;AACjB,sBAAY;AAAA,QACd;AAAA,MACF;AAEA,aAAO;AAAA,QACL;AAAA,QACA,OAAO,QAAQ;AAAA,QACf;AAAA,QACA;AAAA,QACA;AAAA,QACA,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;AAAA,MACnC;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAGA,eAAsB,iBAAiB,MAAqC;AAC1E,QAAM,OAAO,MAAM,qBAAqB;AACxC,SAAO,KAAK,KAAK,CAAC,MAAM,EAAE,SAAS,QAAQ,EAAE,OAAO;AACtD;",
|
|
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 findBedrockServerPids,\n isProcessAlive,\n killBedrockServerByImage,\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 } from \"node:child_process\";\nimport { EventEmitter } from \"node:events\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { isDaemonServer } from \"./daemon/role.js\";\nimport { onRemoteServiceStateChange } from \"./daemon/client.js\";\nimport { t } from \"./i18n/index.js\";\nimport { resolveLlbotLaunch } from \"./llbot-launch.js\";\nimport { inferLevel, pushLog as pushUnifiedLog } from \"./logs.js\";\nimport { findNodeServicePids, killNodeServiceByScript } 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 { startModuleSidecars, stopModuleSidecars } from \"./module-sidecars.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/** \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\n/**\n * \u5355\u670D\u52A1 stop \u7ED3\u679C\uFF0C\u4F9B CLI \u533A\u5206\u6587\u6848\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A`stop <name>` / `restart <name>` \u4E0E `stop all` \u90FD\u8D70 Service.stop\uFF0C\u4F46\u53EA\u6709\u524D\u8005\u4F1A\u5F97\u5230 external\u3002\n * - managed\uFF1A\u505C\u7684\u662F\u672C\u8FDB\u7A0B\u62C9\u8D77\u7684\u5B50\u8FDB\u7A0B\n * - external\uFF1A\u6CA1\u6709\u6258\u7BA1\u53E5\u67C4\uFF0C\u4F46\u6E05\u6389\u4E86\u5916\u90E8\u5B9E\u4F8B\uFF08\u4EC5 external=true \u65F6\uFF09\n * - idle\uFF1A\u672C\u6765\u5C31\u6CA1\u5728\u8DD1\uFF0C\u6216\u6279\u91CF\u505C\u6B62\u65F6\u6545\u610F\u4E0D\u52A8\u5916\u90E8\u5B9E\u4F8B\n */\nexport type StopKind = \"managed\" | \"external\" | \"idle\";\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\n/** \u6E05\u6389 qq-bridge \u8FD0\u884C\u65F6\u6587\u4EF6\uFF0C\u907F\u514D status \u4ECD\u628A\u5DF2\u9000\u51FA\u7684\u5916\u90E8\u8FDB\u7A0B\u5F53\u6210\u5B58\u6D3B\u3002 */\nfunction clearQqRuntimeFile(): void {\n try {\n const file = qqRuntimeStatusPath(ROOT);\n if (fs.existsSync(file)) fs.unlinkSync(file);\n } catch {\n /* \u8FD0\u884C\u65F6\u6587\u4EF6\u53EF\u80FD\u5DF2\u88AB\u8FDB\u7A0B\u81EA\u5DF1\u5220\u6389 */\n }\n}\n\n/**\n * \u6309\u955C\u50CF\u540D\u7ED3\u675F\u5916\u90E8 BDS\uFF0C\u5E76\u7B49\u5230\u8FDB\u7A0B\u4ECE\u8FDB\u7A0B\u8868\u6D88\u5931\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A\u4EC5\u5355\u670D\u52A1 stop / restart\uFF08Service.stop \u7684 external=true\uFF09\u6E05\u6389\u975E\u672C\u8FDB\u7A0B\u62C9\u8D77\u7684 bedrock\u3002\n * stop all \u4E0E\u5B88\u62A4\u8FDB\u7A0B shutdown \u4E0D\u8C03\u7528\u8FD9\u91CC\uFF0C\u907F\u514D taskkill \u8BEF\u6740\u540C\u673A\u5176\u4ED6 Bedrock\u3002\n * taskkill \u8FD4\u56DE\u540E\u8FDB\u7A0B\u8868\u53EF\u80FD\u4ECD\u77ED\u6682\u53EF\u89C1\uFF0Crestart \u4F1A\u7ACB\u523B\u63A2\u6D3B\uFF0C\u56E0\u6B64\u8FD9\u91CC\u7B49\u5230\u6D88\u5931\u518D\u8FD4\u56DE\u3002\n */\nasync function killExternalBedrock(): Promise<void> {\n await killBedrockServerByImage();\n clearBdsPidFile(ROOT);\n const deadline = Date.now() + 8000;\n while (Date.now() < deadline) {\n if ((await findBedrockServerPids()).length === 0) return;\n await new Promise((r) => setTimeout(r, 200));\n }\n await killBedrockServerByImage();\n clearBdsPidFile(ROOT);\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 if (this.name === \"bds\") {\n await startModuleSidecars(ROOT, (line, stream) => this.pushLog(line, stream));\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 /* \u5B50\u8FDB\u7A0B\u59CB\u7EC8\u7531\u5B88\u62A4\u8FDB\u7A0B\u7528\u7BA1\u9053\u6301\u6709\uFF08stdin \u53D1 stop\u3001stdout \u65E5\u5FD7\u3001autoRestart\uFF09\uFF1B\n * \u4E0D\u518D\u5BF9\u5B50\u670D\u52A1\u505A detached/unref\u2014\u2014\u8131\u79BB\u7EC8\u7AEF\u7531 daemon \u8FDB\u7A0B\u81EA\u8EAB\u8D1F\u8D23\u3002\n * windowsHide\uFF1AWindows \u4E0A\u907F\u514D\u4E3A\u6BCF\u4E2A\u670D\u52A1\u5F39\u51FA\u7A7A\u767D\u63A7\u5236\u53F0\uFF08stdout \u5DF2 pipe\uFF0C\u7A97\u5185\u65E0\u8F93\u51FA\uFF09\u3002 */\n const spawnOpts = {\n cwd: this.def.cwd,\n stdio: [\"pipe\", \"pipe\", \"pipe\"] as Array<\"pipe\">,\n env: this.def.env ? { ...process.env, ...this.def.env } : process.env,\n windowsHide: true,\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 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 /**\n * \u505C\u6B62\u672C\u670D\u52A1\u3002\u6258\u7BA1\u5B50\u8FDB\u7A0B\u59CB\u7EC8\u8D70\u539F\u6765\u7684\u4F18\u96C5\u505C\u6B62\uFF08stdin stop / SIGTERM\uFF09\u3002\n * @param external \u662F\u5426\u989D\u5916\u6E05\u6389\u975E\u672C\u8FDB\u7A0B\u62C9\u8D77\u7684\u5916\u90E8\u5B9E\u4F8B\u3002\u9ED8\u8BA4 false\uFF0C\u53EA\u505C\u6258\u7BA1\u5B50\u8FDB\u7A0B\u3002\n * \u4E24\u79CD\u8C03\u7528\u573A\u666F\uFF1A\n * - true\uFF1A\u5355\u670D\u52A1 `stop <name>` / `restart <name>`\uFF08cmdStopLocal\uFF0C\u4EE5\u53CA\u4ECD\u5B58\u5728\u7684 Service.restart\uFF09\u3002\n * BDS \u5916\u90E8\u8D70 killExternalBedrock\uFF1Bdb/qq \u5916\u90E8\u8D70 killNodeServiceByScript\u3002\n * - false \u6216\u4E0D\u4F20\uFF1A`stop all`\u3001`restart -all`\u3001\u5B88\u62A4\u8FDB\u7A0B shutdown / SIGTERM\uFF08\u7ECF stopAll\uFF09\u3002\n * \u4E0D\u6309\u955C\u50CF\u540D\u6216\u811A\u672C\u540D\u6740\u5916\u90E8\u8FDB\u7A0B\uFF0C\u907F\u514D\u8BEF\u6740\u540C\u673A\u5176\u4ED6 Bedrock\u3001db\u3001qq\u3002\n */\n async stop(external = false, forceOnTimeout = true): Promise<StopKind> {\n this.manualStop = true;\n if (this.restartTimer) {\n clearTimeout(this.restartTimer);\n this.restartTimer = null;\n }\n\n /* \u4E0E\u539F\u5148 cmdStop \u4E00\u81F4\uFF1A\u4EC5\u5355\u670D\u52A1\u505C\u6B62\u65F6\uFF0CBDS \u88AB\u5224\u5B9A\u4E3A\u5916\u90E8\u624D\u6309\u955C\u50CF\u7ED3\u675F\uFF0C\u4E0D\u8D70 stdin stop */\n if (external && this.name === \"bds\") {\n const probe = await probeBdsStatus({\n managedPid: this.pid,\n hasStdin: Boolean(this.proc?.stdin),\n rootDir: ROOT,\n });\n if (probe.state === \"external\") {\n await killExternalBedrock();\n this.dropStaleHandle();\n return \"external\";\n }\n }\n\n const managed = Boolean(this.proc && this.running);\n if (managed) await this.stopManagedChild(forceOnTimeout);\n\n /* db / qq\uFF1A\u4EC5\u5355\u670D\u52A1\u505C\u6B62\u65F6\uFF0C\u6258\u7BA1\u505C\u5B8C\u540E\u4ECD\u6309\u811A\u672C\u518D\u626B\u4E00\u904D\uFF0C\u6E05\u6389\u540C\u673A\u4E0A\u7684\u5916\u90E8\u5B9E\u4F8B */\n if (external && (this.name === \"db\" || this.name === \"qq\")) {\n const killed = await killNodeServiceByScript(this.name);\n if (this.name === \"qq\") clearQqRuntimeFile();\n if (managed) return \"managed\";\n return killed.length > 0 ? \"external\" : \"idle\";\n }\n\n if (managed) return \"managed\";\n return \"idle\";\n }\n\n /**\n * \u4F18\u96C5\u505C\u6B62\u5F53\u524D\u6258\u7BA1\u5B50\u8FDB\u7A0B\uFF08stdin stop \u6216 SIGTERM\uFF0C\u8D85\u65F6\u540E SIGKILL\uFF09\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1AService.stop \u786E\u8BA4\u672C\u8FDB\u7A0B\u4ECD\u6301\u6709\u5B50\u8FDB\u7A0B\u65F6\u8C03\u7528\uFF1B\u903B\u8F91\u4E0E\u539F\u5148 stop \u4E3B\u4F53\u76F8\u540C\u3002\n */\n private async stopManagedChild(forceOnTimeout = true): Promise<void> {\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, reject) => {\n const timeout = setTimeout(() => {\n if (!forceOnTimeout && this.proc) {\n reject(new Error(`${this.name} \u672A\u5728\u8D85\u65F6\u5185\u4F18\u96C5\u9000\u51FA\uFF1B\u5DF2\u4E2D\u6B62\u7EF4\u62A4\uFF0C\u4E0D\u5F3A\u5236\u7ED3\u675F\u8FDB\u7A0B`));\n return;\n }\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 /**\n * \u5916\u90E8\u505C\u670D\u540E\u4E22\u6389\u5931\u6548\u7684\u6258\u7BA1\u53E5\u67C4\uFF0C\u907F\u514D\u7D27\u63A5\u7740\u7684 start/restart \u8BEF\u5224\u300C\u5DF2\u5728\u8FD0\u884C\u300D\u3002\n * \u4E0D\u8D70 cleanup\uFF1A\u90A3\u4F1A\u8FDE\u5E26\u505C\u6389\u6A21\u5757\u9644\u5C5E\u8FDB\u7A0B\uFF0C\u800C\u539F\u5148\u7684\u5916\u90E8 BDS stop \u5E76\u4E0D\u8FD9\u4E48\u505A\u3002\n */\n private dropStaleHandle(): void {\n if (!this.running && !this.proc) return;\n const stale = this.proc;\n this.proc = null;\n this.running = false;\n this.pid = 0;\n this.startTime = null;\n this.events.emit(\"state\", { name: this.name, running: false, pid: 0 });\n if (stale && stale.exitCode === null) {\n try {\n stale.kill(\"SIGKILL\");\n } catch {\n /* \u5916\u90E8\u955C\u50CF\u7ED3\u675F\u65F6\u8FD9\u4E2A\u53E5\u67C4\u53EF\u80FD\u5DF2\u7ECF\u9000\u51FA */\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 /**\n * \u5355\u670D\u52A1\u91CD\u542F\uFF1A\u5148\u505C\u518D\u8D77\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A\u82E5\u4ECD\u6709\u8C03\u7528\u65B9\u76F4\u63A5\u8D70 Service.restart\uFF08\u4E0D\u7ECF cmdStopLocal\uFF09\uFF0C\u4E0E\u5355\u670D\u52A1 stop \u4E00\u6837\u6E05\u5916\u90E8\u5B9E\u4F8B\uFF08external true\uFF09\u3002\n * \u6279\u91CF `restart -all` \u548C\u5B88\u62A4\u8FDB\u7A0B shutdown \u4E0D\u8D70\u8FD9\u91CC\uFF0C\u5B83\u4EEC\u7ECF stopAll \u8C03\u7528 stop(false)\uFF0C\u53EA\u505C\u6258\u7BA1\u5B50\u8FDB\u7A0B\u3002\n */\n async restart(): Promise<StartOutcome> {\n await this.stop(true);\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 stopModuleSidecars();\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\", \"qq_link\"]);\n ensurePackUpdateConfigFile();\n void import(\"./module-update/index.js\")\n .then((mod) => mod.ensureModuleUpdateConfigFile())\n .catch((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n pushUnifiedLog(message, \"module\", \"warn\");\n });\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 rawBdsPath = String(bdsCfg.bds_path ?? \"\").trim();\n if (process.platform !== \"win32\" && /^[A-Za-z]:[\\\\/]/.test(rawBdsPath)) {\n throw new Error(\n `configs/bds_updater.json \u7684 bds_path \u662F Windows \u8DEF\u5F84\uFF08${rawBdsPath}\uFF09\u3002` +\n `\u8BF7\u6539\u6210\u76F8\u5BF9 SFMC_ROOT \u7684\u8DEF\u5F84\uFF0C\u4F8B\u5982 \"BDS\"\u3002`\n );\n }\n const bdsPath = path.resolve(ROOT, rawBdsPath || \".\");\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(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 /* \u5D29\u6E83\u62C9\u8D77\u7531 sfmc daemon \u56FA\u5B9A\u5F00\u542F\uFF1B\u4E0D\u518D\u8BFB bds_updater.crash_restart */\n autoRestart: true,\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 /* \u4E1A\u52A1\u6A21\u5757\u66F4\u65B0\u53EA\u8D70\u663E\u5F0F\u547D\u4EE4\uFF0C\u4E0D\u8FDB\u5F00\u670D\u94A9\u5B50\u3002 */\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\n/** \u914D\u7F6E\u5237\u65B0\u540E\uFF0C\u5C06\u5DF2\u6709\u8BA2\u9605\u91CD\u65B0\u7ED1\u5B9A\u5230\u65B0\u670D\u52A1\u5BF9\u8C61\u3002 */\nconst serviceStateListeners = new Set<(event: ServiceStateEvent) => void>();\nexport let services: Record<ServiceName, Service> = createServices();\n\n/** \u53EF\u53EA\u5237\u65B0\u5373\u5C06\u542F\u52A8\u7684\u670D\u52A1\u5B9A\u4E49\uFF0C\u4FDD\u7559\u5176\u4ED6\u8FD0\u884C\u4E2D\u7684\u670D\u52A1\u53CA\u5176\u63A2\u6D3B\u53C2\u6570\u3002 */\nexport function refreshServices(names: readonly ServiceName[] = SERVICE_NAMES): void {\n const previousDbPort = dbHealthPort;\n const previousQqBackend = qqBackendMode;\n const latest = createServices();\n if (!names.includes(\"db\")) dbHealthPort = previousDbPort;\n if (!names.includes(\"qq\")) qqBackendMode = previousQqBackend;\n for (const name of names) {\n const previous = services[name];\n previous.forceStop();\n for (const listener of serviceStateListeners) previous.events.off(\"state\", listener);\n services[name] = latest[name];\n for (const listener of serviceStateListeners) services[name].events.on(\"state\", listener);\n }\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\n/**\n * \u6309\u542F\u52A8\u9006\u5E8F\u505C\u6B62\u672C\u8FDB\u7A0B\u62C9\u8D77\u7684\u6258\u7BA1\u5B50\u8FDB\u7A0B\uFF08\u4F18\u96C5 stop / stdin stop\uFF09\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A`stop all`\u3001`restart -all`\u3001\u5B88\u62A4\u8FDB\u7A0B shutdown / SIGTERM\u3002\n * \u663E\u5F0F\u4F20 external false\uFF1A\u4E0D\u6309\u955C\u50CF\u540D\u6216\u811A\u672C\u540D\u6740\u5916\u90E8 BDS / db / qq\u3002\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(false));\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 /* CLI \u8FDB\u7A0B\u4E0D\u6301\u6709 Service \u5B50\u8FDB\u7A0B\uFF1A\u6539\u8BA2\u5B88\u62A4\u8FDB\u7A0B\u63A8\u9001\u7684\u8FDC\u7A0B\u72B6\u6001\u4E8B\u4EF6 */\n if (!isDaemonServer()) {\n return onRemoteServiceStateChange(fn);\n }\n const handler = (ev: ServiceStateEvent): void => {\n fn(ev);\n };\n serviceStateListeners.add(handler);\n for (const service of Object.values(services)) {\n service.events.on(\"state\", handler);\n }\n return () => {\n serviceStateListeners.delete(handler);\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 * CLI \u8FDB\u7A0B\u7ECF\u5B88\u62A4\u8FDB\u7A0B RPC \u83B7\u53D6\uFF1B\u5B88\u62A4\u8FDB\u7A0B\u5185\u8D70\u672C\u5730\u63A2\u6D3B\u3002\n */\nexport async function queryServicesRuntime(): Promise<ServiceStatus[]> {\n if (!isDaemonServer()) {\n const { queryRuntimeViaDaemon } = await import(\"./daemon/client.js\");\n return queryRuntimeViaDaemon();\n }\n return queryServicesRuntimeLocal();\n}\n\n/** \u5B88\u62A4\u8FDB\u7A0B\u5185\u672C\u5730\u63A2\u6D3B\u5B9E\u73B0\uFF08\u4EA6\u4F9B server status \u7EC4\u88C5 rows\uFF09 */\nexport async function queryServicesRuntimeLocal(): 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
|
}
|