@sfmc-bds/cli 0.7.0 → 0.8.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/management/config.d.ts.map +1 -1
- package/dist/management/config.js +5 -1
- package/dist/management/config.js.map +2 -2
- package/dist/module-sidecars.d.ts.map +1 -1
- package/dist/module-sidecars.js +23 -0
- package/dist/module-sidecars.js.map +2 -2
- package/dist/services.d.ts.map +1 -1
- package/dist/services.js +14 -0
- package/dist/services.js.map +2 -2
- package/package.json +7 -7
- package/scripts/module-install/.sfmc-registry-cache.json +151 -28
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/management/config.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/management/config.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAiB3D,wBAAgB,WAAW,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAsBjD;AACD,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAK9C;AACD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,UAA6D;AAClG,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAgBxD;AACD,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,QAqBtE;AACD,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,kBAQvE"}
|
|
@@ -3,6 +3,7 @@ import path from "node:path";
|
|
|
3
3
|
import { createHash, randomUUID } from "node:crypto";
|
|
4
4
|
import Ajv2020 from "ajv/dist/2020.js";
|
|
5
5
|
import { parse } from "jsonc-parser";
|
|
6
|
+
import { serverPropertyFormFields } from "@sfmc-bds/bds-tools/server-properties-fields";
|
|
6
7
|
import { resolveSdkPackageRoot, ROOT } from "../runtime.js";
|
|
7
8
|
import { resolveBdsContext } from "../pack-lifecycle.js";
|
|
8
9
|
import { readJson, stripBom } from "./domain.js";
|
|
@@ -12,6 +13,7 @@ const SCHEMA_STEM = {
|
|
|
12
13
|
"module-update.json": "module_update",
|
|
13
14
|
"pack-update.json": "pack_update"
|
|
14
15
|
};
|
|
16
|
+
const MANAGEMENT_UI_LOCALE = "zh-CN";
|
|
15
17
|
const ajv = new Ajv2020.default({ allErrors: true, strict: false });
|
|
16
18
|
const validators = /* @__PURE__ */ new Map();
|
|
17
19
|
function configFiles() {
|
|
@@ -57,7 +59,9 @@ function readDocument(key) {
|
|
|
57
59
|
const schemaFile = path.join(path.dirname(file), `${path.basename(file).replace(/\.jsonc?$/, "")}.schema.json`);
|
|
58
60
|
if (fs.existsSync(schemaFile)) schema = JSON.parse(fs.readFileSync(schemaFile, "utf8"));
|
|
59
61
|
}
|
|
60
|
-
|
|
62
|
+
const format = file.endsWith(".properties") ? "properties" : file.endsWith(".jsonc") ? "jsonc" : "json";
|
|
63
|
+
const fields = format === "properties" ? serverPropertyFormFields(MANAGEMENT_UI_LOCALE) : void 0;
|
|
64
|
+
return { key, text, revision: revision(text), ...schema ? { schema } : {}, format, affectedServices: ["db", "tunnel", "qq", "llbot", "bds"], ...fields ? { fields } : {} };
|
|
61
65
|
}
|
|
62
66
|
function validateDocument(document, text) {
|
|
63
67
|
if (Buffer.byteLength(text) > 2 * 1024 * 1024) throw new Error("\u914D\u7F6E\u6587\u4EF6\u8FC7\u5927");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/management/config.ts"],
|
|
4
|
-
"sourcesContent": ["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { createHash, randomUUID } from \"node:crypto\";\nimport Ajv2020 from \"ajv/dist/2020.js\";\nimport { parse, type ParseError } from \"jsonc-parser\";\nimport type { ConfigDocument } from \"@sfmc-bds/management\";\nimport { resolveSdkPackageRoot, ROOT } from \"../runtime.js\";\nimport { resolveBdsContext } from \"../pack-lifecycle.js\";\nimport { readJson, stripBom } from \"./domain.js\";\n\n/** \u78C1\u76D8\u4E0A\u7684\u771F\u5B9E\u6587\u4EF6\u540D\u3002schema \u6587\u4EF6\u4ECD\u7528\u4E0B\u5212\u7EBF\uFF08log_filter.schema.json\uFF09\u3002 */\nconst CORE_FILES = [\"runtime.json\", \"db_config.json\", \"qq_config.json\", \"bds_updater.json\", \"permissions.json\", \"log-filter.json\", \"module-update.json\", \"pack-update.json\"];\nconst SCHEMA_STEM: Record<string, string> = {\n \"log-filter.json\": \"log_filter\",\n \"module-update.json\": \"module_update\",\n \"pack-update.json\": \"pack_update\",\n};\nconst ajv = new Ajv2020.default({ allErrors: true, strict: false });\nconst validators = new Map<string, ReturnType<typeof ajv.compile>>();\nexport function configFiles(): Map<string, string> {\n const files = new Map(CORE_FILES.map(name => [`core/${name}`, path.join(ROOT, \"configs\", name)]));\n files.set(\"bds/server.properties\", path.join(resolveBdsContext().bdsRoot, \"server.properties\"));\n const modules = path.join(ROOT, \"modules\", \"packages\");\n if (fs.existsSync(modules)) for (const entry of fs.readdirSync(modules, { withFileTypes: true })) {\n if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;\n const configDir = path.join(modules, entry.name, \"configs\");\n if (!fs.existsSync(configDir)) continue;\n for (const filename of fs.readdirSync(configDir)) if (/^[\\w.-]+\\.jsonc?$/.test(filename)) files.set(`module/${entry.name}/${filename}`, path.join(configDir, filename));\n }\n // \u771F\u5B9E\u5B9E\u4F8B\u7684\u6A21\u5757\u914D\u7F6E\u5199\u5728 <SFMC_ROOT>/configs/<configKey>.json\uFF0C\u76EE\u5F55\u540D\u6765\u81EA catalog.configKey\u3002\n const catalog = readJson<{ modules?: { id?: string; configKey?: string }[] }>(path.join(ROOT, \"modules\", \"catalog.json\"), {});\n const taken = new Set([...files.values()].map(file => path.resolve(file)));\n for (const row of catalog.modules ?? []) {\n if (!row.id || !row.configKey || !/^[\\w.-]+$/.test(row.id) || !/^[\\w.-]+$/.test(row.configKey)) continue;\n const filename = `${row.configKey}.json`;\n const file = path.join(ROOT, \"configs\", filename);\n if (!fs.existsSync(file) || taken.has(path.resolve(file))) continue;\n files.set(`module/${row.id}/${filename}`, file);\n taken.add(path.resolve(file));\n }\n return files;\n}\nexport function configPath(key: string): string {\n const file = configFiles().get(key);\n if (!file) throw Object.assign(new Error(\"\u914D\u7F6E\u4E0D\u5728\u5141\u8BB8\u7F16\u8F91\u7684\u5217\u8868\u4E2D\"), { code: \"validation\" });\n if (fs.existsSync(file) && fs.lstatSync(file).isSymbolicLink()) throw new Error(\"\u4E0D\u80FD\u901A\u8FC7\u914D\u7F6E\u6587\u4EF6\u94FE\u63A5\u5199\u5165\u5176\u4ED6\u6587\u4EF6\");\n return file;\n}\nexport function revision(text: string) { return createHash(\"sha256\").update(text).digest(\"hex\"); }\nexport function readDocument(key: string): ConfigDocument {\n const file = configPath(key);\n const text = fs.existsSync(file) ? stripBom(fs.readFileSync(file, \"utf8\")) : \"\";\n let schema: object | undefined;\n if (key.startsWith(\"core/\")) {\n const name = SCHEMA_STEM[path.basename(file)] ?? path.basename(file, \".json\");\n const schemaFile = path.join(resolveSdkPackageRoot(), \"schemas\", `${name}.schema.json`);\n if (fs.existsSync(schemaFile)) schema = JSON.parse(fs.readFileSync(schemaFile, \"utf8\")) as object;\n } else if (key.startsWith(\"module/\")) {\n const schemaFile = path.join(path.dirname(file), `${path.basename(file).replace(/\\.jsonc?$/, \"\")}.schema.json`);\n if (fs.existsSync(schemaFile)) schema = JSON.parse(fs.readFileSync(schemaFile, \"utf8\")) as object;\n }\n
|
|
5
|
-
"mappings": "AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,YAAY,kBAAkB;AACvC,OAAO,aAAa;AACpB,SAAS,aAA8B;AAEvC,SAAS,uBAAuB,YAAY;AAC5C,SAAS,yBAAyB;AAClC,SAAS,UAAU,gBAAgB;AAGnC,MAAM,aAAa,CAAC,gBAAgB,kBAAkB,kBAAkB,oBAAoB,oBAAoB,mBAAmB,sBAAsB,kBAAkB;AAC3K,MAAM,cAAsC;AAAA,EAC1C,mBAAmB;AAAA,EACnB,sBAAsB;AAAA,EACtB,oBAAoB;AACtB;
|
|
4
|
+
"sourcesContent": ["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { createHash, randomUUID } from \"node:crypto\";\nimport Ajv2020 from \"ajv/dist/2020.js\";\nimport { parse, type ParseError } from \"jsonc-parser\";\nimport type { ConfigDocument } from \"@sfmc-bds/management\";\nimport { serverPropertyFormFields } from \"@sfmc-bds/bds-tools/server-properties-fields\";\nimport { resolveSdkPackageRoot, ROOT } from \"../runtime.js\";\nimport { resolveBdsContext } from \"../pack-lifecycle.js\";\nimport { readJson, stripBom } from \"./domain.js\";\n\n/** \u78C1\u76D8\u4E0A\u7684\u771F\u5B9E\u6587\u4EF6\u540D\u3002schema \u6587\u4EF6\u4ECD\u7528\u4E0B\u5212\u7EBF\uFF08log_filter.schema.json\uFF09\u3002 */\nconst CORE_FILES = [\"runtime.json\", \"db_config.json\", \"qq_config.json\", \"bds_updater.json\", \"permissions.json\", \"log-filter.json\", \"module-update.json\", \"pack-update.json\"];\nconst SCHEMA_STEM: Record<string, string> = {\n \"log-filter.json\": \"log_filter\",\n \"module-update.json\": \"module_update\",\n \"pack-update.json\": \"pack_update\",\n};\n/** \u7BA1\u7406\u754C\u9762\uFF08\u684C\u9762\u7AEF\u3001\u5C0F\u7A0B\u5E8F\uFF09\u7684\u8BED\u8A00\uFF1Bserver.properties \u8868\u5355\u5B57\u6BB5\u7684\u540D\u79F0\u4E0E\u8BF4\u660E\u6309\u6B64\u751F\u6210\u3002\u754C\u9762\u76EE\u524D\u53EA\u6709\u7B80\u4F53\u4E2D\u6587\u3002 */\nconst MANAGEMENT_UI_LOCALE = \"zh-CN\";\nconst ajv = new Ajv2020.default({ allErrors: true, strict: false });\nconst validators = new Map<string, ReturnType<typeof ajv.compile>>();\nexport function configFiles(): Map<string, string> {\n const files = new Map(CORE_FILES.map(name => [`core/${name}`, path.join(ROOT, \"configs\", name)]));\n files.set(\"bds/server.properties\", path.join(resolveBdsContext().bdsRoot, \"server.properties\"));\n const modules = path.join(ROOT, \"modules\", \"packages\");\n if (fs.existsSync(modules)) for (const entry of fs.readdirSync(modules, { withFileTypes: true })) {\n if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;\n const configDir = path.join(modules, entry.name, \"configs\");\n if (!fs.existsSync(configDir)) continue;\n for (const filename of fs.readdirSync(configDir)) if (/^[\\w.-]+\\.jsonc?$/.test(filename)) files.set(`module/${entry.name}/${filename}`, path.join(configDir, filename));\n }\n // \u771F\u5B9E\u5B9E\u4F8B\u7684\u6A21\u5757\u914D\u7F6E\u5199\u5728 <SFMC_ROOT>/configs/<configKey>.json\uFF0C\u76EE\u5F55\u540D\u6765\u81EA catalog.configKey\u3002\n const catalog = readJson<{ modules?: { id?: string; configKey?: string }[] }>(path.join(ROOT, \"modules\", \"catalog.json\"), {});\n const taken = new Set([...files.values()].map(file => path.resolve(file)));\n for (const row of catalog.modules ?? []) {\n if (!row.id || !row.configKey || !/^[\\w.-]+$/.test(row.id) || !/^[\\w.-]+$/.test(row.configKey)) continue;\n const filename = `${row.configKey}.json`;\n const file = path.join(ROOT, \"configs\", filename);\n if (!fs.existsSync(file) || taken.has(path.resolve(file))) continue;\n files.set(`module/${row.id}/${filename}`, file);\n taken.add(path.resolve(file));\n }\n return files;\n}\nexport function configPath(key: string): string {\n const file = configFiles().get(key);\n if (!file) throw Object.assign(new Error(\"\u914D\u7F6E\u4E0D\u5728\u5141\u8BB8\u7F16\u8F91\u7684\u5217\u8868\u4E2D\"), { code: \"validation\" });\n if (fs.existsSync(file) && fs.lstatSync(file).isSymbolicLink()) throw new Error(\"\u4E0D\u80FD\u901A\u8FC7\u914D\u7F6E\u6587\u4EF6\u94FE\u63A5\u5199\u5165\u5176\u4ED6\u6587\u4EF6\");\n return file;\n}\nexport function revision(text: string) { return createHash(\"sha256\").update(text).digest(\"hex\"); }\nexport function readDocument(key: string): ConfigDocument {\n const file = configPath(key);\n const text = fs.existsSync(file) ? stripBom(fs.readFileSync(file, \"utf8\")) : \"\";\n let schema: object | undefined;\n if (key.startsWith(\"core/\")) {\n const name = SCHEMA_STEM[path.basename(file)] ?? path.basename(file, \".json\");\n const schemaFile = path.join(resolveSdkPackageRoot(), \"schemas\", `${name}.schema.json`);\n if (fs.existsSync(schemaFile)) schema = JSON.parse(fs.readFileSync(schemaFile, \"utf8\")) as object;\n } else if (key.startsWith(\"module/\")) {\n const schemaFile = path.join(path.dirname(file), `${path.basename(file).replace(/\\.jsonc?$/, \"\")}.schema.json`);\n if (fs.existsSync(schemaFile)) schema = JSON.parse(fs.readFileSync(schemaFile, \"utf8\")) as object;\n }\n const format = file.endsWith(\".properties\") ? \"properties\" : file.endsWith(\".jsonc\") ? \"jsonc\" : \"json\";\n // server.properties \u6CA1\u6709 Schema\uFF0C\u9644\u5E26 bds-tools \u7EF4\u62A4\u7684\u5B8C\u6574\u5B57\u6BB5\u6E05\u5355\u4F9B\u8868\u5355\u89C6\u56FE\u4F7F\u7528\n const fields = format === \"properties\" ? serverPropertyFormFields(MANAGEMENT_UI_LOCALE) : undefined;\n return { key, text, revision: revision(text), ...(schema ? { schema } : {}), format, affectedServices: [\"db\", \"tunnel\", \"qq\", \"llbot\", \"bds\"], ...(fields ? { fields } : {}) };\n}\nexport function validateDocument(document: ConfigDocument, text: string) {\n if (Buffer.byteLength(text) > 2 * 1024 * 1024) throw new Error(\"\u914D\u7F6E\u6587\u4EF6\u8FC7\u5927\");\n if (document.format === \"properties\") {\n for (const line of text.split(/\\r?\\n/)) if (line.trim() && !line.trim().startsWith(\"#\") && !/^[^=\\r\\n]+=.*$/.test(line)) throw new Error(\"server.properties \u5305\u542B\u975E\u6CD5\u914D\u7F6E\u884C\");\n return;\n }\n const errors: ParseError[] = [];\n const value: unknown = parse(text, errors, { allowTrailingComma: document.format === \"jsonc\", disallowComments: document.format === \"json\" });\n if (errors.length) throw Object.assign(new Error(\"\u914D\u7F6E\u8BED\u6CD5\u9519\u8BEF\"), { code: \"validation\", details: errors });\n if (document.schema) {\n // \u4FDD\u7559\u5DF2\u6709\u672A\u77E5\u5B57\u6BB5\uFF1B\u53EA\u4ECE\u6821\u9A8C\u526F\u672C\u6392\u9664\u672A\u4FEE\u6539\u7684\u672A\u77E5\u5B57\u6BB5\uFF0C\u4FDD\u5B58\u7684\u539F\u6587\u4ECD\u5B8C\u5168\u4FDD\u7559\u3002\n const schemaObject = document.schema as { additionalProperties?: boolean; properties?: Record<string, unknown> };\n if (schemaObject.additionalProperties === false && value && typeof value === \"object\" && !Array.isArray(value)) {\n const previous = parse(document.text || \"{}\") as Record<string, unknown>;\n for (const key of Object.keys(value)) if (!(key in (schemaObject.properties ?? {})) && previous && key in previous && JSON.stringify(previous[key]) === JSON.stringify((value as Record<string, unknown>)[key])) delete (value as Record<string, unknown>)[key];\n }\n const hash = revision(JSON.stringify(document.schema));\n let validator = validators.get(hash);\n if (!validator) { const schema = { ...document.schema } as Record<string, unknown>; delete schema.$id; validator = ajv.compile(schema); validators.set(hash, validator); }\n if (!validator(value)) throw Object.assign(new Error(\"\u914D\u7F6E\u4E0D\u7B26\u5408 Schema\"), { code: \"validation\", details: validator.errors });\n }\n}\nexport function saveDocument(key: string, expected: string, text: string) {\n const document = readDocument(key);\n if (document.revision !== expected) throw Object.assign(new Error(\"\u914D\u7F6E\u5DF2\u88AB\u5176\u4ED6\u64CD\u4F5C\u4FEE\u6539\uFF0C\u8BF7\u91CD\u65B0\u8BFB\u53D6\"), { code: \"conflict\" });\n validateDocument(document, text);\n const file = configPath(key); const temp = `${file}.${randomUUID()}.tmp`;\n fs.mkdirSync(path.dirname(file), { recursive: true });\n fs.writeFileSync(temp, text, { mode: 0o600 }); fs.renameSync(temp, file);\n return document;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module-sidecars.d.ts","sourceRoot":"","sources":["../src/module-sidecars.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"module-sidecars.d.ts","sourceRoot":"","sources":["../src/module-sidecars.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAyHH;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,QAAQ,KAAK,IAAI,GACvD,OAAO,CAAC,IAAI,CAAC,CAiCf;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAUzC"}
|
package/dist/module-sidecars.js
CHANGED
|
@@ -3,6 +3,25 @@ import fs from "node:fs";
|
|
|
3
3
|
import net from "node:net";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
const children = /* @__PURE__ */ new Map();
|
|
6
|
+
let runtimeRoot = "";
|
|
7
|
+
function persistSidecars() {
|
|
8
|
+
if (!runtimeRoot) return;
|
|
9
|
+
const file = path.join(runtimeRoot, ".sfmc", "sidecars.runtime.json");
|
|
10
|
+
const processes = [...children.entries()].flatMap(([id, child]) => {
|
|
11
|
+
const pid = child.pid ?? 0;
|
|
12
|
+
if (!pid || child.exitCode !== null) return [];
|
|
13
|
+
return [{ id, pid }];
|
|
14
|
+
});
|
|
15
|
+
try {
|
|
16
|
+
if (!processes.length) {
|
|
17
|
+
fs.unlinkSync(file);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
21
|
+
fs.writeFileSync(file, JSON.stringify({ processes }) + "\n");
|
|
22
|
+
} catch {
|
|
23
|
+
}
|
|
24
|
+
}
|
|
6
25
|
function readJson(file) {
|
|
7
26
|
try {
|
|
8
27
|
const parsed = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
@@ -70,6 +89,7 @@ function loadSpecs(root) {
|
|
|
70
89
|
return found;
|
|
71
90
|
}
|
|
72
91
|
async function startModuleSidecars(root, log) {
|
|
92
|
+
runtimeRoot = root;
|
|
73
93
|
for (const { spec, dir } of loadSpecs(root)) {
|
|
74
94
|
if (children.has(spec.id)) continue;
|
|
75
95
|
if (!isEnabled(root, spec)) continue;
|
|
@@ -93,11 +113,13 @@ async function startModuleSidecars(root, log) {
|
|
|
93
113
|
log(`[${spec.id}] \u5DF2\u968F\u670D\u52A1\u5668\u542F\u52A8\u9644\u5C5E\u8FDB\u7A0B (PID ${child.pid ?? 0})`, "stdout");
|
|
94
114
|
child.on("exit", (code) => {
|
|
95
115
|
if (children.get(spec.id) === child) children.delete(spec.id);
|
|
116
|
+
persistSidecars();
|
|
96
117
|
log(`[${spec.id}] \u9644\u5C5E\u8FDB\u7A0B\u9000\u51FA (code ${code ?? "?"})`, code === 0 ? "stdout" : "stderr");
|
|
97
118
|
});
|
|
98
119
|
child.on("error", (err) => {
|
|
99
120
|
log(`[${spec.id}] \u9644\u5C5E\u8FDB\u7A0B\u542F\u52A8\u5931\u8D25: ${err.message}`, "stderr");
|
|
100
121
|
});
|
|
122
|
+
persistSidecars();
|
|
101
123
|
}
|
|
102
124
|
}
|
|
103
125
|
function stopModuleSidecars() {
|
|
@@ -108,6 +130,7 @@ function stopModuleSidecars() {
|
|
|
108
130
|
} catch {
|
|
109
131
|
}
|
|
110
132
|
}
|
|
133
|
+
persistSidecars();
|
|
111
134
|
}
|
|
112
135
|
export {
|
|
113
136
|
startModuleSidecars,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/module-sidecars.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * \u6A21\u5757\u968F BDS \u542F\u52A8\u7684\u9644\u5C5E\u8FDB\u7A0B\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A\u5F00\u670D\u524D\u626B\u63CF modules/packages/<id>/sidecar.json\u3002\n * \u76EE\u524D Bluemap \u7528\u5B83\u62C9\u8D77\u5730\u56FE\u8FDB\u7A0B\uFF1B\u65E5\u5FD7\u6B63\u6587\u7531\u811A\u672C\u6253\u8FDB BDS\uFF0C\u8FD9\u91CC\u53EA\u62A5\u544A\u542F\u52A8\u548C\u9000\u51FA\u3002\n */\n\nimport { spawn, type ChildProcess } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport net from \"node:net\";\nimport path from \"node:path\";\n\n/** \u6A21\u5757\u5305\u6839\u76EE\u5F55\u91CC\u7684 sidecar.json\u3002 */\ninterface SidecarSpec {\n id: string;\n /** \u76F8\u5BF9\u6A21\u5757\u5305\u6839\u76EE\u5F55\u7684 Node \u5165\u53E3\u3002 */\n entry: string;\n /** \u76F8\u5BF9 SFMC \u6839\u76EE\u5F55\u7684\u914D\u7F6E\u6587\u4EF6\u3002\u7F3A\u7701\u4E0D\u5224\u65AD\u5F00\u5173\u3002 */\n configFile?: string;\n /** \u914D\u7F6E\u91CC\u7684\u70B9\u8DEF\u5F84\u3002\u503C\u4E3A false \u65F6\u4E0D\u542F\u52A8\u3002\u7F3A\u7701\u89C6\u4E3A\u5F00\u542F\u3002 */\n when?: string;\n}\n\n/** \u672C\u8F6E BDS \u62C9\u8D77\u7684\u9644\u5C5E\u8FDB\u7A0B\u3002\u505C\u670D\u65F6\u53EA\u7ED3\u675F\u8FD9\u4E9B\uFF0C\u4E0D\u78B0\u7528\u6237\u53E6\u5916\u5F00\u7684\u8FDB\u7A0B\u3002 */\nconst children = new Map<string, ChildProcess>();\n\nfunction readJson(file: string): Record<string, unknown> | null {\n try {\n const parsed = JSON.parse(fs.readFileSync(file, \"utf8\")) as unknown;\n return parsed && typeof parsed === \"object\" ? (parsed as Record<string, unknown>) : null;\n } catch {\n return null;\n }\n}\n\n/** \u6CBF\u70B9\u8DEF\u5F84\u53D6\u503C\u3002\u4E2D\u9014\u4E0D\u662F\u5BF9\u8C61\u5219\u8FD4\u56DE undefined\u3002 */\nfunction readPath(source: Record<string, unknown>, dotted: string): unknown {\n let current: unknown = source;\n for (const part of dotted.split(\".\")) {\n if (!current || typeof current !== \"object\") return undefined;\n current = (current as Record<string, unknown>)[part];\n }\n return current;\n}\n\nfunction isEnabled(root: string, spec: SidecarSpec): boolean {\n if (!spec.when || !spec.configFile) return true;\n const config = readJson(path.join(root, spec.configFile));\n if (!config) return true;\n const flag = readPath(config, spec.when);\n return flag !== false;\n}\n\nfunction configuredPort(root: string, spec: SidecarSpec): number {\n if (!spec.configFile) return 0;\n const config = readJson(path.join(root, spec.configFile));\n const port = config?.port;\n return typeof port === \"number\" && port > 0 && port < 65536 ? port : 0;\n}\n\n/** \u7AEF\u53E3\u5DF2\u6709\u8FDB\u7A0B\u76D1\u542C\u65F6\u4E0D\u518D\u542F\u52A8\u7B2C\u4E8C\u4E2A\u3002 */\nfunction portOpen(port: number): Promise<boolean> {\n if (!port) return Promise.resolve(false);\n return new Promise((resolve) => {\n const socket = net.connect({ host: \"127.0.0.1\", port });\n const done = (open: boolean) => {\n socket.removeAllListeners();\n socket.destroy();\n resolve(open);\n };\n socket.once(\"connect\", () => done(true));\n socket.once(\"error\", () => done(false));\n socket.setTimeout(500, () => done(false));\n });\n}\n\nfunction loadSpecs(root: string): Array<{ spec: SidecarSpec; dir: string }> {\n const packagesDir = path.join(root, \"modules\", \"packages\");\n if (!fs.existsSync(packagesDir)) return [];\n const found: Array<{ spec: SidecarSpec; dir: string }> = [];\n for (const entry of fs.readdirSync(packagesDir, { withFileTypes: true })) {\n if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;\n const dir = path.join(packagesDir, entry.name);\n const file = path.join(dir, \"sidecar.json\");\n if (!fs.existsSync(file)) continue;\n const raw = readJson(file);\n if (!raw || typeof raw.id !== \"string\" || typeof raw.entry !== \"string\") continue;\n const lock = readJson(path.join(root, \"modules\", \"module-lock.json\"));\n const modules = lock?.modules && typeof lock.modules === \"object\" ? (lock.modules as Record<string, unknown>) : {};\n const row = modules[raw.id];\n const enabled = !row || typeof row !== \"object\" || (row as { enabled?: boolean }).enabled !== false;\n if (!enabled) continue;\n const spec: SidecarSpec = { id: raw.id, entry: raw.entry };\n if (typeof raw.configFile === \"string\") spec.configFile = raw.configFile;\n if (typeof raw.when === \"string\") spec.when = raw.when;\n found.push({ spec, dir });\n }\n return found;\n}\n\n/**\n * \u5728 BDS \u8FDB\u7A0B\u62C9\u8D77\u4E4B\u524D\u542F\u52A8\u9644\u5C5E\u8FDB\u7A0B\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1Aservices.ts \u91CC bds \u7684 start\uFF0C\u5728 beforeStart \u4E4B\u540E\u8C03\u7528\u3002\n */\nexport async function startModuleSidecars(\n root: string,\n log: (line: string, stream: \"stdout\" | \"stderr\") => void\n): Promise<void> {\n for (const { spec, dir } of loadSpecs(root)) {\n if (children.has(spec.id)) continue;\n if (!isEnabled(root, spec)) continue;\n const entry = path.join(dir, spec.entry);\n if (!fs.existsSync(entry)) {\n log(`[${spec.id}] \u672A\u627E\u5230\u9644\u5C5E\u8FDB\u7A0B\u5165\u53E3: ${entry}`, \"stderr\");\n continue;\n }\n const port = configuredPort(root, spec);\n if (await portOpen(port)) {\n log(`[${spec.id}] \u7AEF\u53E3 ${port} \u5DF2\u5728\u76D1\u542C\uFF0C\u8DF3\u8FC7\u542F\u52A8\u3002`, \"stdout\");\n continue;\n }\n const child = spawn(process.execPath, [entry], {\n cwd: dir,\n env: { ...process.env, SFMC_ROOT: root },\n windowsHide: true,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n children.set(spec.id, child);\n log(`[${spec.id}] \u5DF2\u968F\u670D\u52A1\u5668\u542F\u52A8\u9644\u5C5E\u8FDB\u7A0B (PID ${child.pid ?? 0})`, \"stdout\");\n child.on(\"exit\", (code) => {\n if (children.get(spec.id) === child) children.delete(spec.id);\n log(`[${spec.id}] \u9644\u5C5E\u8FDB\u7A0B\u9000\u51FA (code ${code ?? \"?\"})`, code === 0 ? \"stdout\" : \"stderr\");\n });\n child.on(\"error\", (err) => {\n log(`[${spec.id}] \u9644\u5C5E\u8FDB\u7A0B\u542F\u52A8\u5931\u8D25: ${err.message}`, \"stderr\");\n });\n }\n}\n\n/**\n * \u7ED3\u675F\u672C\u8F6E\u7531\u5F00\u670D\u62C9\u8D77\u7684\u9644\u5C5E\u8FDB\u7A0B\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1ABDS \u670D\u52A1 cleanup \u65F6\u8C03\u7528\u3002\u7528\u6237\u81EA\u5DF1\u53E6\u5F00\u7684\u5730\u56FE\u8FDB\u7A0B\u4E0D\u4F1A\u88AB\u5173\u6389\u3002\n */\nexport function stopModuleSidecars(): void {\n for (const [id, child] of children) {\n children.delete(id);\n try {\n child.kill();\n } catch {\n /* \u8FDB\u7A0B\u5DF2\u7ECF\u7ED3\u675F\u65F6\u5FFD\u7565\u3002 */\n }\n }\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["/**\n * \u6A21\u5757\u968F BDS \u542F\u52A8\u7684\u9644\u5C5E\u8FDB\u7A0B\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A\u5F00\u670D\u524D\u626B\u63CF modules/packages/<id>/sidecar.json\u3002\n * \u76EE\u524D Bluemap \u7528\u5B83\u62C9\u8D77\u5730\u56FE\u8FDB\u7A0B\uFF1B\u65E5\u5FD7\u6B63\u6587\u7531\u811A\u672C\u6253\u8FDB BDS\uFF0C\u8FD9\u91CC\u53EA\u62A5\u544A\u542F\u52A8\u548C\u9000\u51FA\u3002\n */\n\nimport { spawn, type ChildProcess } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport net from \"node:net\";\nimport path from \"node:path\";\n\n/** \u6A21\u5757\u5305\u6839\u76EE\u5F55\u91CC\u7684 sidecar.json\u3002 */\ninterface SidecarSpec {\n id: string;\n /** \u76F8\u5BF9\u6A21\u5757\u5305\u6839\u76EE\u5F55\u7684 Node \u5165\u53E3\u3002 */\n entry: string;\n /** \u76F8\u5BF9 SFMC \u6839\u76EE\u5F55\u7684\u914D\u7F6E\u6587\u4EF6\u3002\u7F3A\u7701\u4E0D\u5224\u65AD\u5F00\u5173\u3002 */\n configFile?: string;\n /** \u914D\u7F6E\u91CC\u7684\u70B9\u8DEF\u5F84\u3002\u503C\u4E3A false \u65F6\u4E0D\u542F\u52A8\u3002\u7F3A\u7701\u89C6\u4E3A\u5F00\u542F\u3002 */\n when?: string;\n}\n\n/** \u672C\u8F6E BDS \u62C9\u8D77\u7684\u9644\u5C5E\u8FDB\u7A0B\u3002\u505C\u670D\u65F6\u53EA\u7ED3\u675F\u8FD9\u4E9B\uFF0C\u4E0D\u78B0\u7528\u6237\u53E6\u5916\u5F00\u7684\u8FDB\u7A0B\u3002 */\nconst children = new Map<string, ChildProcess>();\n/** \u672C\u8F6E\u9644\u5C5E\u8FDB\u7A0B\u6240\u5C5E\u7684\u5B9E\u4F8B\u6839\uFF0C\u8FD0\u884C\u6001\u6587\u4EF6\u5199\u5230\u8BE5\u76EE\u5F55\u7684 .sfmc\u3002 */\nlet runtimeRoot = \"\";\n\n/**\n * \u628A\u5F53\u524D\u4ECD\u6D3B\u7740\u7684\u9644\u5C5E\u8FDB\u7A0B PID \u5199\u5230 .sfmc/sidecars.runtime.json\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A\u6570\u636E\u670D\u52A1\u636E\u6B64\u7EDF\u8BA1\u5404\u5730\u56FE\u7B49\u9644\u5C5E\u8FDB\u7A0B\u7684\u5185\u5B58\u3002\u8FDB\u7A0B\u90FD\u9000\u51FA\u540E\u5220\u6389\u6587\u4EF6\u3002\n */\nfunction persistSidecars(): void {\n if (!runtimeRoot) return;\n const file = path.join(runtimeRoot, \".sfmc\", \"sidecars.runtime.json\");\n const processes = [...children.entries()].flatMap(([id, child]) => {\n const pid = child.pid ?? 0;\n if (!pid || child.exitCode !== null) return [];\n return [{ id, pid }];\n });\n try {\n if (!processes.length) {\n fs.unlinkSync(file);\n return;\n }\n fs.mkdirSync(path.dirname(file), { recursive: true });\n fs.writeFileSync(file, JSON.stringify({ processes }) + \"\\n\");\n } catch {\n /* \u8FD0\u884C\u6001\u6587\u4EF6\u5199\u5931\u8D25\u65F6\u5185\u5B58\u9762\u677F\u4F1A\u5C11\u8FD9\u4E00\u9879\uFF0C\u4E0D\u5F71\u54CD\u9644\u5C5E\u8FDB\u7A0B\u672C\u8EAB\u3002 */\n }\n}\n\nfunction readJson(file: string): Record<string, unknown> | null {\n try {\n const parsed = JSON.parse(fs.readFileSync(file, \"utf8\")) as unknown;\n return parsed && typeof parsed === \"object\" ? (parsed as Record<string, unknown>) : null;\n } catch {\n return null;\n }\n}\n\n/** \u6CBF\u70B9\u8DEF\u5F84\u53D6\u503C\u3002\u4E2D\u9014\u4E0D\u662F\u5BF9\u8C61\u5219\u8FD4\u56DE undefined\u3002 */\nfunction readPath(source: Record<string, unknown>, dotted: string): unknown {\n let current: unknown = source;\n for (const part of dotted.split(\".\")) {\n if (!current || typeof current !== \"object\") return undefined;\n current = (current as Record<string, unknown>)[part];\n }\n return current;\n}\n\nfunction isEnabled(root: string, spec: SidecarSpec): boolean {\n if (!spec.when || !spec.configFile) return true;\n const config = readJson(path.join(root, spec.configFile));\n if (!config) return true;\n const flag = readPath(config, spec.when);\n return flag !== false;\n}\n\nfunction configuredPort(root: string, spec: SidecarSpec): number {\n if (!spec.configFile) return 0;\n const config = readJson(path.join(root, spec.configFile));\n const port = config?.port;\n return typeof port === \"number\" && port > 0 && port < 65536 ? port : 0;\n}\n\n/** \u7AEF\u53E3\u5DF2\u6709\u8FDB\u7A0B\u76D1\u542C\u65F6\u4E0D\u518D\u542F\u52A8\u7B2C\u4E8C\u4E2A\u3002 */\nfunction portOpen(port: number): Promise<boolean> {\n if (!port) return Promise.resolve(false);\n return new Promise((resolve) => {\n const socket = net.connect({ host: \"127.0.0.1\", port });\n const done = (open: boolean) => {\n socket.removeAllListeners();\n socket.destroy();\n resolve(open);\n };\n socket.once(\"connect\", () => done(true));\n socket.once(\"error\", () => done(false));\n socket.setTimeout(500, () => done(false));\n });\n}\n\nfunction loadSpecs(root: string): Array<{ spec: SidecarSpec; dir: string }> {\n const packagesDir = path.join(root, \"modules\", \"packages\");\n if (!fs.existsSync(packagesDir)) return [];\n const found: Array<{ spec: SidecarSpec; dir: string }> = [];\n for (const entry of fs.readdirSync(packagesDir, { withFileTypes: true })) {\n if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;\n const dir = path.join(packagesDir, entry.name);\n const file = path.join(dir, \"sidecar.json\");\n if (!fs.existsSync(file)) continue;\n const raw = readJson(file);\n if (!raw || typeof raw.id !== \"string\" || typeof raw.entry !== \"string\") continue;\n const lock = readJson(path.join(root, \"modules\", \"module-lock.json\"));\n const modules = lock?.modules && typeof lock.modules === \"object\" ? (lock.modules as Record<string, unknown>) : {};\n const row = modules[raw.id];\n const enabled = !row || typeof row !== \"object\" || (row as { enabled?: boolean }).enabled !== false;\n if (!enabled) continue;\n const spec: SidecarSpec = { id: raw.id, entry: raw.entry };\n if (typeof raw.configFile === \"string\") spec.configFile = raw.configFile;\n if (typeof raw.when === \"string\") spec.when = raw.when;\n found.push({ spec, dir });\n }\n return found;\n}\n\n/**\n * \u5728 BDS \u8FDB\u7A0B\u62C9\u8D77\u4E4B\u524D\u542F\u52A8\u9644\u5C5E\u8FDB\u7A0B\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1Aservices.ts \u91CC bds \u7684 start\uFF0C\u5728 beforeStart \u4E4B\u540E\u8C03\u7528\u3002\n */\nexport async function startModuleSidecars(\n root: string,\n log: (line: string, stream: \"stdout\" | \"stderr\") => void\n): Promise<void> {\n runtimeRoot = root;\n for (const { spec, dir } of loadSpecs(root)) {\n if (children.has(spec.id)) continue;\n if (!isEnabled(root, spec)) continue;\n const entry = path.join(dir, spec.entry);\n if (!fs.existsSync(entry)) {\n log(`[${spec.id}] \u672A\u627E\u5230\u9644\u5C5E\u8FDB\u7A0B\u5165\u53E3: ${entry}`, \"stderr\");\n continue;\n }\n const port = configuredPort(root, spec);\n if (await portOpen(port)) {\n log(`[${spec.id}] \u7AEF\u53E3 ${port} \u5DF2\u5728\u76D1\u542C\uFF0C\u8DF3\u8FC7\u542F\u52A8\u3002`, \"stdout\");\n continue;\n }\n const child = spawn(process.execPath, [entry], {\n cwd: dir,\n env: { ...process.env, SFMC_ROOT: root },\n windowsHide: true,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n children.set(spec.id, child);\n log(`[${spec.id}] \u5DF2\u968F\u670D\u52A1\u5668\u542F\u52A8\u9644\u5C5E\u8FDB\u7A0B (PID ${child.pid ?? 0})`, \"stdout\");\n child.on(\"exit\", (code) => {\n if (children.get(spec.id) === child) children.delete(spec.id);\n persistSidecars();\n log(`[${spec.id}] \u9644\u5C5E\u8FDB\u7A0B\u9000\u51FA (code ${code ?? \"?\"})`, code === 0 ? \"stdout\" : \"stderr\");\n });\n child.on(\"error\", (err) => {\n log(`[${spec.id}] \u9644\u5C5E\u8FDB\u7A0B\u542F\u52A8\u5931\u8D25: ${err.message}`, \"stderr\");\n });\n persistSidecars();\n }\n}\n\n/**\n * \u7ED3\u675F\u672C\u8F6E\u7531\u5F00\u670D\u62C9\u8D77\u7684\u9644\u5C5E\u8FDB\u7A0B\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1ABDS \u670D\u52A1 cleanup \u65F6\u8C03\u7528\u3002\u7528\u6237\u81EA\u5DF1\u53E6\u5F00\u7684\u5730\u56FE\u8FDB\u7A0B\u4E0D\u4F1A\u88AB\u5173\u6389\u3002\n */\nexport function stopModuleSidecars(): void {\n for (const [id, child] of children) {\n children.delete(id);\n try {\n child.kill();\n } catch {\n /* \u8FDB\u7A0B\u5DF2\u7ECF\u7ED3\u675F\u65F6\u5FFD\u7565\u3002 */\n }\n }\n persistSidecars();\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
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;
|
|
1
|
+
{"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../src/services.ts"],"names":[],"mappings":"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"}
|
package/dist/services.js
CHANGED
|
@@ -58,6 +58,7 @@ async function probeQqRuntime() {
|
|
|
58
58
|
return { alive: true, pid: status.pid };
|
|
59
59
|
}
|
|
60
60
|
const tunnelRuntimePath = path.join(ROOT, ".sfmc", "tunnel.runtime.json");
|
|
61
|
+
const llbotRuntimePath = path.join(ROOT, ".sfmc", "llbot.runtime.json");
|
|
61
62
|
async function probeTunnelRuntime() {
|
|
62
63
|
const pid = readJson(tunnelRuntimePath)?.pid;
|
|
63
64
|
return typeof pid === "number" && pid > 0 && await isProcessAlive(pid) ? pid : 0;
|
|
@@ -73,6 +74,17 @@ function clearTunnelRuntime(pid) {
|
|
|
73
74
|
} catch {
|
|
74
75
|
}
|
|
75
76
|
}
|
|
77
|
+
function writeLlbotRuntime(pid) {
|
|
78
|
+
fs.mkdirSync(path.dirname(llbotRuntimePath), { recursive: true });
|
|
79
|
+
fs.writeFileSync(llbotRuntimePath, JSON.stringify({ pid, startedAt: Date.now() }) + "\n");
|
|
80
|
+
}
|
|
81
|
+
function clearLlbotRuntime(pid) {
|
|
82
|
+
if (readJson(llbotRuntimePath)?.pid !== pid) return;
|
|
83
|
+
try {
|
|
84
|
+
fs.unlinkSync(llbotRuntimePath);
|
|
85
|
+
} catch {
|
|
86
|
+
}
|
|
87
|
+
}
|
|
76
88
|
function clearQqRuntimeFile() {
|
|
77
89
|
try {
|
|
78
90
|
const file = qqRuntimeStatusPath(ROOT);
|
|
@@ -183,6 +195,7 @@ class Service {
|
|
|
183
195
|
writeBdsPidFile(this.pid, ROOT);
|
|
184
196
|
}
|
|
185
197
|
if (this.name === "tunnel" && this.pid > 0) writeTunnelRuntime(this.pid);
|
|
198
|
+
if (this.name === "llbot" && this.pid > 0) writeLlbotRuntime(this.pid);
|
|
186
199
|
this.events.emit("output", `started (PID ${this.pid})`, "info");
|
|
187
200
|
this.events.emit("state", { name: this.name, running: true, pid: this.pid });
|
|
188
201
|
let lifecycleReported = false;
|
|
@@ -393,6 +406,7 @@ class Service {
|
|
|
393
406
|
stopModuleSidecars();
|
|
394
407
|
}
|
|
395
408
|
if (this.name === "tunnel" && exitingPid > 0) clearTunnelRuntime(exitingPid);
|
|
409
|
+
if (this.name === "llbot" && exitingPid > 0) clearLlbotRuntime(exitingPid);
|
|
396
410
|
if (wasRunning) {
|
|
397
411
|
this.events.emit("state", { name: this.name, running: false, pid: 0 });
|
|
398
412
|
}
|
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\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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",
|
|
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/** LLBot \u6CA1\u6709\u5355\u72EC\u7684\u5FC3\u8DF3\u6A21\u5757\uFF0C\u542F\u505C\u65F6\u7531\u8FD9\u91CC\u5199\u4E0B PID\uFF0C\u4F9B\u6570\u636E\u670D\u52A1\u7EDF\u8BA1\u5185\u5B58\u3002 */\nconst llbotRuntimePath = path.join(ROOT, \".sfmc\", \"llbot.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\nfunction writeLlbotRuntime(pid: number): void {\n fs.mkdirSync(path.dirname(llbotRuntimePath), { recursive: true });\n fs.writeFileSync(llbotRuntimePath, JSON.stringify({ pid, startedAt: Date.now() }) + \"\\n\");\n}\n\nfunction clearLlbotRuntime(pid: number): void {\n if (readJson<{ pid?: number }>(llbotRuntimePath)?.pid !== pid) return;\n try {\n fs.unlinkSync(llbotRuntimePath);\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 if (this.name === \"llbot\" && this.pid > 0) writeLlbotRuntime(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 (this.name === \"llbot\" && exitingPid > 0) clearLlbotRuntime(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": "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,MAAM,mBAAmB,KAAK,KAAK,MAAM,SAAS,oBAAoB;AAEtE,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;AAEA,SAAS,kBAAkB,KAAmB;AAC5C,KAAG,UAAU,KAAK,QAAQ,gBAAgB,GAAG,EAAE,WAAW,KAAK,CAAC;AAChE,KAAG,cAAc,kBAAkB,KAAK,UAAU,EAAE,KAAK,WAAW,KAAK,IAAI,EAAE,CAAC,IAAI,IAAI;AAC1F;AAEA,SAAS,kBAAkB,KAAmB;AAC5C,MAAI,SAA2B,gBAAgB,GAAG,QAAQ,IAAK;AAC/D,MAAI;AACF,OAAG,WAAW,gBAAgB;AAAA,EAChC,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,QAAI,KAAK,SAAS,WAAW,KAAK,MAAM,EAAG,mBAAkB,KAAK,GAAG;AACrE,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,KAAK,SAAS,WAAW,aAAa,EAAG,mBAAkB,UAAU;AACzE,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;AAGA,MAAM,wBAAwB,oBAAI,IAAwC;AACnE,IAAI,WAAyC,eAAe;AAG5D,SAAS,gBAAgB,QAAgC,eAAqB;AACnF,QAAM,iBAAiB;AACvB,QAAM,oBAAoB;AAC1B,QAAM,SAAS,eAAe;AAC9B,MAAI,CAAC,MAAM,SAAS,IAAI,EAAG,gBAAe;AAC1C,MAAI,CAAC,MAAM,SAAS,IAAI,EAAG,iBAAgB;AAC3C,aAAW,QAAQ,OAAO;AACxB,UAAM,WAAW,SAAS,IAAI;AAC9B,aAAS,UAAU;AACnB,eAAW,YAAY,sBAAuB,UAAS,OAAO,IAAI,SAAS,QAAQ;AACnF,aAAS,IAAI,IAAI,OAAO,IAAI;AAC5B,eAAW,YAAY,sBAAuB,UAAS,IAAI,EAAE,OAAO,GAAG,SAAS,QAAQ;AAAA,EAC1F;AACF;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,wBAAsB,IAAI,OAAO;AACjC,aAAW,WAAW,OAAO,OAAO,QAAQ,GAAG;AAC7C,YAAQ,OAAO,GAAG,SAAS,OAAO;AAAA,EACpC;AACA,SAAO,MAAM;AACX,0BAAsB,OAAO,OAAO;AACpC,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;",
|
|
6
6
|
"names": ["ROOT"]
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sfmc-bds/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "SFMC management CLI — REPL, service supervisor, module install, behavior-pack build/deploy",
|
|
5
5
|
"author": "Shiroha7z",
|
|
6
6
|
"license": "AGPL-3.0-only",
|
|
@@ -55,14 +55,14 @@
|
|
|
55
55
|
"esbuild": "^0.28.1",
|
|
56
56
|
"ajv": "8.20.0",
|
|
57
57
|
"jsonc-parser": "3.3.1",
|
|
58
|
-
"@sfmc-bds/bds-tools": "0.
|
|
59
|
-
"@sfmc-bds/
|
|
60
|
-
"@sfmc-bds/
|
|
61
|
-
"@sfmc-bds/management": "0.
|
|
62
|
-
"@sfmc-bds/db-server": "0.
|
|
58
|
+
"@sfmc-bds/bds-tools": "0.3.0",
|
|
59
|
+
"@sfmc-bds/devkit": "2.0.7",
|
|
60
|
+
"@sfmc-bds/sdk": "0.6.0",
|
|
61
|
+
"@sfmc-bds/management": "0.7.0",
|
|
62
|
+
"@sfmc-bds/db-server": "0.6.0"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
|
-
"@sfmc-bds/tools": "0.2.
|
|
65
|
+
"@sfmc-bds/tools": "0.2.11"
|
|
66
66
|
},
|
|
67
67
|
"publishConfig": {
|
|
68
68
|
"access": "public",
|
|
@@ -1,95 +1,218 @@
|
|
|
1
1
|
{
|
|
2
|
-
"fetchedAt":
|
|
2
|
+
"fetchedAt": 1792926615467,
|
|
3
3
|
"index": {
|
|
4
4
|
"activity-log": {
|
|
5
5
|
"npm": "@sfmc-bds/module-activity-log",
|
|
6
|
-
"version": "0.
|
|
7
|
-
"sdk": ">=0.2.0"
|
|
6
|
+
"version": "0.3.0",
|
|
7
|
+
"sdk": ">=0.2.0",
|
|
8
|
+
"repo": "https://github.com/DogeLakeDev/sfmc-module-activity-log",
|
|
9
|
+
"name": "行为日志",
|
|
10
|
+
"category": "system",
|
|
11
|
+
"license": "ISC",
|
|
12
|
+
"official": true,
|
|
13
|
+
"requires": []
|
|
8
14
|
},
|
|
9
15
|
"afk": {
|
|
10
16
|
"npm": "@sfmc-bds/module-afk",
|
|
11
|
-
"version": "0.
|
|
12
|
-
"sdk": ">=0.2.0"
|
|
17
|
+
"version": "0.3.0",
|
|
18
|
+
"sdk": ">=0.2.0",
|
|
19
|
+
"repo": "https://github.com/DogeLakeDev/sfmc-module-afk",
|
|
20
|
+
"name": "afk",
|
|
21
|
+
"category": "utility",
|
|
22
|
+
"license": "ISC",
|
|
23
|
+
"official": true,
|
|
24
|
+
"requires": []
|
|
13
25
|
},
|
|
14
26
|
"area": {
|
|
15
27
|
"npm": "@sfmc-bds/module-area",
|
|
16
28
|
"version": "0.2.1",
|
|
17
|
-
"sdk": ">=0.2.0"
|
|
29
|
+
"sdk": ">=0.2.0",
|
|
30
|
+
"repo": "https://github.com/Tanya7z/sfmc-module-area",
|
|
31
|
+
"name": "area",
|
|
32
|
+
"description": "区域特性调度引擎",
|
|
33
|
+
"category": "system",
|
|
34
|
+
"license": "ISC",
|
|
35
|
+
"official": true,
|
|
36
|
+
"requires": []
|
|
18
37
|
},
|
|
19
38
|
"chat": {
|
|
20
39
|
"npm": "@sfmc-bds/module-chat",
|
|
21
|
-
"version": "0.2.
|
|
22
|
-
"sdk": ">=0.2.
|
|
40
|
+
"version": "0.2.1",
|
|
41
|
+
"sdk": ">=0.2.0-beta.17",
|
|
42
|
+
"repo": "https://github.com/Tanya7z/sfmc-module-chat",
|
|
43
|
+
"name": "chat",
|
|
44
|
+
"category": "social",
|
|
45
|
+
"license": "ISC",
|
|
46
|
+
"official": true,
|
|
47
|
+
"requires": []
|
|
23
48
|
},
|
|
24
49
|
"chat-sounds": {
|
|
25
50
|
"npm": "@sfmc-bds/module-chat-sounds",
|
|
26
51
|
"version": "0.2.1",
|
|
27
|
-
"sdk": ">=0.2.0"
|
|
52
|
+
"sdk": ">=0.2.0",
|
|
53
|
+
"repo": "https://github.com/Tanya7z/sfmc-module-chat-sounds",
|
|
54
|
+
"name": "chat-sounds",
|
|
55
|
+
"category": "social",
|
|
56
|
+
"license": "ISC",
|
|
57
|
+
"official": true,
|
|
58
|
+
"requires": [
|
|
59
|
+
"chat"
|
|
60
|
+
]
|
|
28
61
|
},
|
|
29
62
|
"clean": {
|
|
30
63
|
"npm": "@sfmc-bds/module-clean",
|
|
31
64
|
"version": "0.2.1",
|
|
32
|
-
"sdk": ">=0.2.0"
|
|
65
|
+
"sdk": ">=0.2.0",
|
|
66
|
+
"repo": "https://github.com/Tanya7z/sfmc-module-clean",
|
|
67
|
+
"name": "掉落物清理",
|
|
68
|
+
"description": "区域与全服掉落物预警、倒计时广播与物理回收箱清理",
|
|
69
|
+
"category": "utility",
|
|
70
|
+
"license": "ISC",
|
|
71
|
+
"official": true,
|
|
72
|
+
"requires": [
|
|
73
|
+
"area"
|
|
74
|
+
]
|
|
33
75
|
},
|
|
34
76
|
"coop": {
|
|
35
77
|
"npm": "@sfmc-bds/module-coop",
|
|
36
78
|
"version": "0.2.2",
|
|
37
|
-
"sdk": ">=0.2.0"
|
|
79
|
+
"sdk": ">=0.2.0-beta.17",
|
|
80
|
+
"repo": "https://github.com/Tanya7z/sfmc-module-coop",
|
|
81
|
+
"name": "合作社",
|
|
82
|
+
"category": "social",
|
|
83
|
+
"license": "ISC",
|
|
84
|
+
"official": true,
|
|
85
|
+
"requires": [
|
|
86
|
+
"economy",
|
|
87
|
+
"activity-log"
|
|
88
|
+
]
|
|
38
89
|
},
|
|
39
90
|
"data-backup": {
|
|
40
91
|
"npm": "@sfmc-bds/module-data-backup",
|
|
41
92
|
"version": "0.2.2",
|
|
42
|
-
"sdk": ">=0.2.0"
|
|
93
|
+
"sdk": ">=0.2.0",
|
|
94
|
+
"repo": "https://github.com/Tanya7z/sfmc-module-data-backup",
|
|
95
|
+
"name": "数据备份",
|
|
96
|
+
"category": "system",
|
|
97
|
+
"license": "ISC",
|
|
98
|
+
"official": true,
|
|
99
|
+
"requires": []
|
|
43
100
|
},
|
|
44
101
|
"economy": {
|
|
45
102
|
"npm": "@sfmc-bds/module-economy",
|
|
46
103
|
"version": "0.2.1",
|
|
47
|
-
"sdk": ">=0.2.0"
|
|
104
|
+
"sdk": ">=0.2.0",
|
|
105
|
+
"repo": "https://github.com/Tanya7z/sfmc-module-economy",
|
|
106
|
+
"name": "经济系统",
|
|
107
|
+
"category": "economy",
|
|
108
|
+
"license": "ISC",
|
|
109
|
+
"official": true,
|
|
110
|
+
"requires": []
|
|
48
111
|
},
|
|
49
112
|
"fly-area": {
|
|
50
113
|
"npm": "@sfmc-bds/module-fly-area",
|
|
51
114
|
"version": "0.2.1",
|
|
52
|
-
"sdk": ">=0.2.0"
|
|
115
|
+
"sdk": ">=0.2.0",
|
|
116
|
+
"repo": "https://github.com/Tanya7z/sfmc-module-fly-area",
|
|
117
|
+
"name": "区域飞行",
|
|
118
|
+
"category": "gameplay",
|
|
119
|
+
"license": "ISC",
|
|
120
|
+
"official": true,
|
|
121
|
+
"requires": [
|
|
122
|
+
"area"
|
|
123
|
+
]
|
|
53
124
|
},
|
|
54
125
|
"gamemode-area": {
|
|
55
126
|
"npm": "@sfmc-bds/module-gamemode-area",
|
|
56
127
|
"version": "0.2.1",
|
|
57
|
-
"sdk": ">=0.2.0"
|
|
128
|
+
"sdk": ">=0.2.0",
|
|
129
|
+
"repo": "https://github.com/Tanya7z/sfmc-module-gamemode-area",
|
|
130
|
+
"name": "区域游戏模式",
|
|
131
|
+
"description": "区域游戏模式切换与背包隔离置换",
|
|
132
|
+
"category": "gameplay",
|
|
133
|
+
"license": "ISC",
|
|
134
|
+
"official": true,
|
|
135
|
+
"requires": [
|
|
136
|
+
"area",
|
|
137
|
+
"inventory-switcher"
|
|
138
|
+
]
|
|
58
139
|
},
|
|
59
140
|
"inventory-switcher": {
|
|
60
141
|
"npm": "@sfmc-bds/module-inventory-switcher",
|
|
61
142
|
"version": "0.2.1",
|
|
62
|
-
"sdk": ">=0.2.0"
|
|
143
|
+
"sdk": ">=0.2.0",
|
|
144
|
+
"repo": "https://github.com/Tanya7z/sfmc-module-inventory-switcher",
|
|
145
|
+
"name": "背包切换",
|
|
146
|
+
"description": "通用背包多槽位快照持久化与原子置换服务",
|
|
147
|
+
"category": "system",
|
|
148
|
+
"license": "ISC",
|
|
149
|
+
"official": true,
|
|
150
|
+
"requires": []
|
|
63
151
|
},
|
|
64
152
|
"land": {
|
|
65
153
|
"npm": "@sfmc-bds/module-land",
|
|
66
|
-
"version": "0.
|
|
67
|
-
"sdk": ">=0.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
154
|
+
"version": "1.0.0",
|
|
155
|
+
"sdk": ">=0.5.0",
|
|
156
|
+
"repo": "https://github.com/DogeLakeDev/sfmc-module-land",
|
|
157
|
+
"name": "领地",
|
|
158
|
+
"category": "gameplay",
|
|
159
|
+
"license": "ISC",
|
|
160
|
+
"official": true,
|
|
161
|
+
"requires": [
|
|
162
|
+
"economy",
|
|
163
|
+
"activity-log"
|
|
164
|
+
]
|
|
73
165
|
},
|
|
74
166
|
"online-time": {
|
|
75
167
|
"npm": "@sfmc-bds/module-online-time",
|
|
76
168
|
"version": "0.2.2",
|
|
77
|
-
"sdk": ">=0.2.0"
|
|
169
|
+
"sdk": ">=0.2.0-beta.17",
|
|
170
|
+
"repo": "https://github.com/Tanya7z/sfmc-module-online-time",
|
|
171
|
+
"name": "在线时长统计",
|
|
172
|
+
"description": "进服打点与心跳增量结转在线统计",
|
|
173
|
+
"category": "utility",
|
|
174
|
+
"license": "ISC",
|
|
175
|
+
"official": true,
|
|
176
|
+
"requires": []
|
|
78
177
|
},
|
|
79
178
|
"peace-area": {
|
|
80
179
|
"npm": "@sfmc-bds/module-peace-area",
|
|
81
|
-
"version": "0.
|
|
82
|
-
"sdk": ">=0.2.0"
|
|
180
|
+
"version": "1.0.0",
|
|
181
|
+
"sdk": ">=0.2.0",
|
|
182
|
+
"repo": "https://github.com/DogeLakeDev/sfmc-module-peace-area",
|
|
183
|
+
"name": "和平区域",
|
|
184
|
+
"description": "区域怪物生成拦截",
|
|
185
|
+
"category": "gameplay",
|
|
186
|
+
"license": "ISC",
|
|
187
|
+
"official": true,
|
|
188
|
+
"requires": [
|
|
189
|
+
"area"
|
|
190
|
+
]
|
|
83
191
|
},
|
|
84
192
|
"qa": {
|
|
85
193
|
"npm": "@sfmc-bds/module-qa",
|
|
86
194
|
"version": "0.2.1",
|
|
87
|
-
"sdk": ">=0.2.0"
|
|
195
|
+
"sdk": ">=0.2.0-beta.17",
|
|
196
|
+
"repo": "https://github.com/Tanya7z/sfmc-module-qa",
|
|
197
|
+
"name": "QA",
|
|
198
|
+
"category": "gameplay",
|
|
199
|
+
"license": "ISC",
|
|
200
|
+
"official": true,
|
|
201
|
+
"requires": [
|
|
202
|
+
"economy",
|
|
203
|
+
"chat"
|
|
204
|
+
]
|
|
88
205
|
},
|
|
89
206
|
"spawn-protect": {
|
|
90
207
|
"npm": "@sfmc-bds/module-spawn-protect",
|
|
91
208
|
"version": "0.2.1",
|
|
92
|
-
"sdk": ">=0.2.0"
|
|
209
|
+
"sdk": ">=0.2.0",
|
|
210
|
+
"repo": "https://github.com/Tanya7z/sfmc-module-spawn-protect",
|
|
211
|
+
"name": "出生保护",
|
|
212
|
+
"category": "utility",
|
|
213
|
+
"license": "ISC",
|
|
214
|
+
"official": true,
|
|
215
|
+
"requires": []
|
|
93
216
|
}
|
|
94
217
|
}
|
|
95
218
|
}
|