@sfmc-bds/cli 0.2.2 → 0.2.4
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/command-palette.d.ts.map +1 -1
- package/dist/command-palette.js +11 -2
- package/dist/command-palette.js.map +2 -2
- package/dist/command-surface.d.ts +14 -2
- package/dist/command-surface.d.ts.map +1 -1
- package/dist/command-surface.js +120 -6
- package/dist/command-surface.js.map +2 -2
- package/dist/commands.d.ts.map +1 -1
- package/dist/commands.js +45 -38
- package/dist/commands.js.map +2 -2
- package/dist/daemon/client.d.ts +67 -0
- package/dist/daemon/client.d.ts.map +1 -0
- package/dist/daemon/client.js +272 -0
- package/dist/daemon/client.js.map +7 -0
- package/dist/daemon/paths.d.ts +35 -0
- package/dist/daemon/paths.d.ts.map +1 -0
- package/dist/daemon/paths.js +69 -0
- package/dist/daemon/paths.js.map +7 -0
- package/dist/daemon/protocol.d.ts +66 -0
- package/dist/daemon/protocol.d.ts.map +1 -0
- package/dist/daemon/protocol.js +16 -0
- package/dist/daemon/protocol.js.map +7 -0
- package/dist/daemon/role.d.ts +13 -0
- package/dist/daemon/role.d.ts.map +1 -0
- package/dist/daemon/role.js +12 -0
- package/dist/daemon/role.js.map +7 -0
- package/dist/daemon/server.d.ts +12 -0
- package/dist/daemon/server.d.ts.map +1 -0
- package/dist/daemon/server.js +221 -0
- package/dist/daemon/server.js.map +7 -0
- package/dist/help-text.d.ts.map +1 -1
- package/dist/help-text.js +3 -1
- package/dist/help-text.js.map +2 -2
- package/dist/i18n/locales/en.d.ts +25 -3
- package/dist/i18n/locales/en.d.ts.map +1 -1
- package/dist/i18n/locales/en.js +26 -3
- package/dist/i18n/locales/en.js.map +2 -2
- package/dist/i18n/locales/zh-CN.d.ts.map +1 -1
- package/dist/i18n/locales/zh-CN.js +26 -3
- package/dist/i18n/locales/zh-CN.js.map +2 -2
- package/dist/logs.d.ts +11 -1
- package/dist/logs.d.ts.map +1 -1
- package/dist/logs.js +8 -0
- package/dist/logs.js.map +2 -2
- package/dist/main.js +27 -4
- package/dist/main.js.map +2 -2
- package/dist/module-commands.d.ts.map +1 -1
- package/dist/module-commands.js +9 -5
- package/dist/module-commands.js.map +2 -2
- package/dist/module-fingerprint.d.ts +5 -0
- package/dist/module-fingerprint.d.ts.map +1 -1
- package/dist/module-fingerprint.js +13 -3
- package/dist/module-fingerprint.js.map +2 -2
- package/dist/module-sidecars.d.ts +16 -0
- package/dist/module-sidecars.d.ts.map +1 -0
- package/dist/module-sidecars.js +116 -0
- package/dist/module-sidecars.js.map +7 -0
- package/dist/repl.d.ts.map +1 -1
- package/dist/repl.js +51 -32
- package/dist/repl.js.map +3 -3
- package/dist/runtime.d.ts +1 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +2 -2
- package/dist/runtime.js.map +2 -2
- package/dist/services.d.ts +3 -1
- package/dist/services.d.ts.map +1 -1
- package/dist/services.js +22 -10
- package/dist/services.js.map +2 -2
- package/dist/world-packs.js.map +1 -1
- package/package.json +4 -4
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
function shouldSkipModuleEntry(parentDirName, name) {
|
|
5
|
+
if (name.startsWith(".") || name === "node_modules" || name === "dist") return true;
|
|
6
|
+
return name === "data" && parentDirName === "map-server";
|
|
7
|
+
}
|
|
4
8
|
async function dirFingerprint(rootDir) {
|
|
5
9
|
const hash = createHash("sha256");
|
|
6
10
|
const entries = [];
|
|
@@ -9,7 +13,7 @@ async function dirFingerprint(rootDir) {
|
|
|
9
13
|
const items = await fs.readdir(full, { withFileTypes: true });
|
|
10
14
|
const sorted = items.sort((a, b) => a.name.localeCompare(b.name));
|
|
11
15
|
for (const it of sorted) {
|
|
12
|
-
if (
|
|
16
|
+
if (shouldSkipModuleEntry(path.posix.basename(rel), it.name)) continue;
|
|
13
17
|
const child = rel ? `${rel}/${it.name}` : it.name;
|
|
14
18
|
if (it.isDirectory()) await walk(child);
|
|
15
19
|
else if (it.isFile()) entries.push(child);
|
|
@@ -18,7 +22,12 @@ async function dirFingerprint(rootDir) {
|
|
|
18
22
|
await walk("");
|
|
19
23
|
entries.sort();
|
|
20
24
|
for (const rel of entries) {
|
|
21
|
-
|
|
25
|
+
let data;
|
|
26
|
+
try {
|
|
27
|
+
data = await fs.readFile(path.join(rootDir, rel));
|
|
28
|
+
} catch {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
22
31
|
hash.update(rel.replaceAll("\\", "/"));
|
|
23
32
|
hash.update("\n");
|
|
24
33
|
hash.update(data);
|
|
@@ -27,6 +36,7 @@ async function dirFingerprint(rootDir) {
|
|
|
27
36
|
return hash.digest("hex");
|
|
28
37
|
}
|
|
29
38
|
export {
|
|
30
|
-
dirFingerprint
|
|
39
|
+
dirFingerprint,
|
|
40
|
+
shouldSkipModuleEntry
|
|
31
41
|
};
|
|
32
42
|
//# sourceMappingURL=module-fingerprint.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/module-fingerprint.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * module-fingerprint.ts \u2014 \u6A21\u5757\u76EE\u5F55 canonical SHA-256\n * (\u4E0E\u539F\u5148 module-commands \u5185 dirFingerprint \u7B97\u6CD5\u4E00\u81F4,\u4F9B catalog / verify \u5171\u7528)\u3002\n */\n\nimport { createHash } from \"node:crypto\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\n\n/** Compute the canonical SHA-256 of a module directory (POSIX `find ... | sha256sum` compatible). */\nexport async function dirFingerprint(rootDir: string): Promise<string> {\n const hash = createHash(\"sha256\");\n const entries: string[] = [];\n async function walk(rel: string): Promise<void> {\n const full = path.join(rootDir, rel);\n const items = await fs.readdir(full, { withFileTypes: true });\n const sorted = items.sort((a, b) => a.name.localeCompare(b.name));\n for (const it of sorted) {\n if (
|
|
5
|
-
"mappings": "AAKA,SAAS,kBAAkB;AAC3B,OAAO,QAAQ;AACf,OAAO,UAAU;
|
|
4
|
+
"sourcesContent": ["/**\n * module-fingerprint.ts \u2014 \u6A21\u5757\u76EE\u5F55 canonical SHA-256\n * (\u4E0E\u539F\u5148 module-commands \u5185 dirFingerprint \u7B97\u6CD5\u4E00\u81F4,\u4F9B catalog / verify \u5171\u7528)\u3002\n */\n\nimport { createHash } from \"node:crypto\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\n\n/**\n * \u6A21\u5757\u76EE\u5F55\u626B\u63CF\u65F6\u8981\u8DF3\u8FC7\u7684\u540D\u5B57\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A\u6307\u7EB9\u548C\u4F53\u79EF\u7EDF\u8BA1\u5171\u7528\u3002map-server/data \u662F\u5730\u56FE\u5FEB\u7167\uFF0C\u626B\u8FDB\u53BB\u4F1A\u8BA9 CLI \u542F\u52A8\u5361\u6B7B\u3002\n */\nexport function shouldSkipModuleEntry(parentDirName: string, name: string): boolean {\n if (name.startsWith(\".\") || name === \"node_modules\" || name === \"dist\") return true;\n return name === \"data\" && parentDirName === \"map-server\";\n}\n\n/** Compute the canonical SHA-256 of a module directory (POSIX `find ... | sha256sum` compatible). */\nexport async function dirFingerprint(rootDir: string): Promise<string> {\n const hash = createHash(\"sha256\");\n const entries: string[] = [];\n async function walk(rel: string): Promise<void> {\n const full = path.join(rootDir, rel);\n const items = await fs.readdir(full, { withFileTypes: true });\n const sorted = items.sort((a, b) => a.name.localeCompare(b.name));\n for (const it of sorted) {\n if (shouldSkipModuleEntry(path.posix.basename(rel), it.name)) continue;\n const child = rel ? `${rel}/${it.name}` : it.name;\n if (it.isDirectory()) await walk(child);\n else if (it.isFile()) entries.push(child);\n }\n }\n await walk(\"\");\n entries.sort();\n for (const rel of entries) {\n let data: Buffer;\n try {\n data = await fs.readFile(path.join(rootDir, rel));\n } catch {\n // \u5355\u4E2A\u6587\u4EF6\u6253\u4E0D\u5F00\u65F6\u8DF3\u8FC7\uFF0C\u907F\u514D\u6574\u6B21\u542F\u52A8\u5931\u8D25\n continue;\n }\n hash.update(rel.replaceAll(\"\\\\\", \"/\"));\n hash.update(\"\\n\");\n hash.update(data);\n hash.update(\"\\n\");\n }\n return hash.digest(\"hex\");\n}\n"],
|
|
5
|
+
"mappings": "AAKA,SAAS,kBAAkB;AAC3B,OAAO,QAAQ;AACf,OAAO,UAAU;AAMV,SAAS,sBAAsB,eAAuB,MAAuB;AAClF,MAAI,KAAK,WAAW,GAAG,KAAK,SAAS,kBAAkB,SAAS,OAAQ,QAAO;AAC/E,SAAO,SAAS,UAAU,kBAAkB;AAC9C;AAGA,eAAsB,eAAe,SAAkC;AACrE,QAAM,OAAO,WAAW,QAAQ;AAChC,QAAM,UAAoB,CAAC;AAC3B,iBAAe,KAAK,KAA4B;AAC9C,UAAM,OAAO,KAAK,KAAK,SAAS,GAAG;AACnC,UAAM,QAAQ,MAAM,GAAG,QAAQ,MAAM,EAAE,eAAe,KAAK,CAAC;AAC5D,UAAM,SAAS,MAAM,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAChE,eAAW,MAAM,QAAQ;AACvB,UAAI,sBAAsB,KAAK,MAAM,SAAS,GAAG,GAAG,GAAG,IAAI,EAAG;AAC9D,YAAM,QAAQ,MAAM,GAAG,GAAG,IAAI,GAAG,IAAI,KAAK,GAAG;AAC7C,UAAI,GAAG,YAAY,EAAG,OAAM,KAAK,KAAK;AAAA,eAC7B,GAAG,OAAO,EAAG,SAAQ,KAAK,KAAK;AAAA,IAC1C;AAAA,EACF;AACA,QAAM,KAAK,EAAE;AACb,UAAQ,KAAK;AACb,aAAW,OAAO,SAAS;AACzB,QAAI;AACJ,QAAI;AACF,aAAO,MAAM,GAAG,SAAS,KAAK,KAAK,SAAS,GAAG,CAAC;AAAA,IAClD,QAAQ;AAEN;AAAA,IACF;AACA,SAAK,OAAO,IAAI,WAAW,MAAM,GAAG,CAAC;AACrC,SAAK,OAAO,IAAI;AAChB,SAAK,OAAO,IAAI;AAChB,SAAK,OAAO,IAAI;AAAA,EAClB;AACA,SAAO,KAAK,OAAO,KAAK;AAC1B;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 模块随 BDS 启动的附属进程。
|
|
3
|
+
* 使用场景:开服前扫描 modules/packages/<id>/sidecar.json。
|
|
4
|
+
* 目前 Bluemap 用它拉起地图进程;日志正文由脚本打进 BDS,这里只报告启动和退出。
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* 在 BDS 进程拉起之前启动附属进程。
|
|
8
|
+
* 使用场景:services.ts 里 bds 的 start,在 beforeStart 之后调用。
|
|
9
|
+
*/
|
|
10
|
+
export declare function startModuleSidecars(root: string, log: (line: string, stream: "stdout" | "stderr") => void): Promise<void>;
|
|
11
|
+
/**
|
|
12
|
+
* 结束本轮由开服拉起的附属进程。
|
|
13
|
+
* 使用场景:BDS 服务 cleanup 时调用。用户自己另开的地图进程不会被关掉。
|
|
14
|
+
*/
|
|
15
|
+
export declare function stopModuleSidecars(): void;
|
|
16
|
+
//# sourceMappingURL=module-sidecars.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module-sidecars.d.ts","sourceRoot":"","sources":["../src/module-sidecars.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA+FH;;;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,CA8Bf;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CASzC"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import net from "node:net";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
const children = /* @__PURE__ */ new Map();
|
|
6
|
+
function readJson(file) {
|
|
7
|
+
try {
|
|
8
|
+
const parsed = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
9
|
+
return parsed && typeof parsed === "object" ? parsed : null;
|
|
10
|
+
} catch {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function readPath(source, dotted) {
|
|
15
|
+
let current = source;
|
|
16
|
+
for (const part of dotted.split(".")) {
|
|
17
|
+
if (!current || typeof current !== "object") return void 0;
|
|
18
|
+
current = current[part];
|
|
19
|
+
}
|
|
20
|
+
return current;
|
|
21
|
+
}
|
|
22
|
+
function isEnabled(root, spec) {
|
|
23
|
+
if (!spec.when || !spec.configFile) return true;
|
|
24
|
+
const config = readJson(path.join(root, spec.configFile));
|
|
25
|
+
if (!config) return true;
|
|
26
|
+
const flag = readPath(config, spec.when);
|
|
27
|
+
return flag !== false;
|
|
28
|
+
}
|
|
29
|
+
function configuredPort(root, spec) {
|
|
30
|
+
if (!spec.configFile) return 0;
|
|
31
|
+
const config = readJson(path.join(root, spec.configFile));
|
|
32
|
+
const port = config?.port;
|
|
33
|
+
return typeof port === "number" && port > 0 && port < 65536 ? port : 0;
|
|
34
|
+
}
|
|
35
|
+
function portOpen(port) {
|
|
36
|
+
if (!port) return Promise.resolve(false);
|
|
37
|
+
return new Promise((resolve) => {
|
|
38
|
+
const socket = net.connect({ host: "127.0.0.1", port });
|
|
39
|
+
const done = (open) => {
|
|
40
|
+
socket.removeAllListeners();
|
|
41
|
+
socket.destroy();
|
|
42
|
+
resolve(open);
|
|
43
|
+
};
|
|
44
|
+
socket.once("connect", () => done(true));
|
|
45
|
+
socket.once("error", () => done(false));
|
|
46
|
+
socket.setTimeout(500, () => done(false));
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function loadSpecs(root) {
|
|
50
|
+
const packagesDir = path.join(root, "modules", "packages");
|
|
51
|
+
if (!fs.existsSync(packagesDir)) return [];
|
|
52
|
+
const found = [];
|
|
53
|
+
for (const entry of fs.readdirSync(packagesDir, { withFileTypes: true })) {
|
|
54
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
55
|
+
const dir = path.join(packagesDir, entry.name);
|
|
56
|
+
const file = path.join(dir, "sidecar.json");
|
|
57
|
+
if (!fs.existsSync(file)) continue;
|
|
58
|
+
const raw = readJson(file);
|
|
59
|
+
if (!raw || typeof raw.id !== "string" || typeof raw.entry !== "string") continue;
|
|
60
|
+
const lock = readJson(path.join(root, "modules", "module-lock.json"));
|
|
61
|
+
const modules = lock?.modules && typeof lock.modules === "object" ? lock.modules : {};
|
|
62
|
+
const row = modules[raw.id];
|
|
63
|
+
const enabled = !row || typeof row !== "object" || row.enabled !== false;
|
|
64
|
+
if (!enabled) continue;
|
|
65
|
+
const spec = { id: raw.id, entry: raw.entry };
|
|
66
|
+
if (typeof raw.configFile === "string") spec.configFile = raw.configFile;
|
|
67
|
+
if (typeof raw.when === "string") spec.when = raw.when;
|
|
68
|
+
found.push({ spec, dir });
|
|
69
|
+
}
|
|
70
|
+
return found;
|
|
71
|
+
}
|
|
72
|
+
async function startModuleSidecars(root, log) {
|
|
73
|
+
for (const { spec, dir } of loadSpecs(root)) {
|
|
74
|
+
if (children.has(spec.id)) continue;
|
|
75
|
+
if (!isEnabled(root, spec)) continue;
|
|
76
|
+
const entry = path.join(dir, spec.entry);
|
|
77
|
+
if (!fs.existsSync(entry)) {
|
|
78
|
+
log(`[${spec.id}] \u672A\u627E\u5230\u9644\u5C5E\u8FDB\u7A0B\u5165\u53E3: ${entry}`, "stderr");
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const port = configuredPort(root, spec);
|
|
82
|
+
if (await portOpen(port)) {
|
|
83
|
+
log(`[${spec.id}] \u7AEF\u53E3 ${port} \u5DF2\u5728\u76D1\u542C\uFF0C\u8DF3\u8FC7\u542F\u52A8\u3002`, "stdout");
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const child = spawn(process.execPath, [entry], {
|
|
87
|
+
cwd: dir,
|
|
88
|
+
env: { ...process.env, SFMC_ROOT: root },
|
|
89
|
+
windowsHide: true,
|
|
90
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
91
|
+
});
|
|
92
|
+
children.set(spec.id, child);
|
|
93
|
+
log(`[${spec.id}] \u5DF2\u968F\u670D\u52A1\u5668\u542F\u52A8\u9644\u5C5E\u8FDB\u7A0B (PID ${child.pid ?? 0})`, "stdout");
|
|
94
|
+
child.on("exit", (code) => {
|
|
95
|
+
if (children.get(spec.id) === child) children.delete(spec.id);
|
|
96
|
+
log(`[${spec.id}] \u9644\u5C5E\u8FDB\u7A0B\u9000\u51FA (code ${code ?? "?"})`, code === 0 ? "stdout" : "stderr");
|
|
97
|
+
});
|
|
98
|
+
child.on("error", (err) => {
|
|
99
|
+
log(`[${spec.id}] \u9644\u5C5E\u8FDB\u7A0B\u542F\u52A8\u5931\u8D25: ${err.message}`, "stderr");
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function stopModuleSidecars() {
|
|
104
|
+
for (const [id, child] of children) {
|
|
105
|
+
children.delete(id);
|
|
106
|
+
try {
|
|
107
|
+
child.kill();
|
|
108
|
+
} catch {
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
export {
|
|
113
|
+
startModuleSidecars,
|
|
114
|
+
stopModuleSidecars
|
|
115
|
+
};
|
|
116
|
+
//# sourceMappingURL=module-sidecars.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 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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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/repl.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repl.d.ts","sourceRoot":"","sources":["../src/repl.ts"],"names":[],"mappings":"AAcA,OAAO,
|
|
1
|
+
{"version":3,"file":"repl.d.ts","sourceRoot":"","sources":["../src/repl.ts"],"names":[],"mappings":"AAcA,OAAO,EAKL,KAAK,WAAW,EACjB,MAAM,sBAAsB,CAAC;AA+M9B,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC,CAiD1D;AAcD,wBAAgB,OAAO,CAAC,IAAI,GAAE,WAAoB,GAAG,MAAM,CAE1D;AAufD,mCAAmC;AACnC,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAG9D,CAAC;AAmBJ,wBAAsB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAkS/C"}
|
package/dist/repl.js
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
resolvePaletteView
|
|
14
14
|
} from "./command-palette.js";
|
|
15
15
|
import {
|
|
16
|
+
listNextPaletteTokens,
|
|
16
17
|
listVisiblePacksSubs,
|
|
17
18
|
listVisibleTopLevelNames,
|
|
18
19
|
resolveModuleTopShorthand
|
|
@@ -24,7 +25,6 @@ import { t } from "./i18n/index.js";
|
|
|
24
25
|
import { cmdLocale } from "./locale-command.js";
|
|
25
26
|
import {
|
|
26
27
|
formatLog,
|
|
27
|
-
getAllLogs,
|
|
28
28
|
logPrefixWidth,
|
|
29
29
|
onLog,
|
|
30
30
|
readDiskLogs,
|
|
@@ -47,7 +47,7 @@ import {
|
|
|
47
47
|
WindowHost
|
|
48
48
|
} from "./repl-windows/index.js";
|
|
49
49
|
import { SEND_TARGET_ORDER, paintSendPrompt, paintSfmcPrompt, plainPrompt } from "./send-target.js";
|
|
50
|
-
import {
|
|
50
|
+
import { onServiceStateChange, queryServicesRuntime, SERVICE_NAMES } from "./services.js";
|
|
51
51
|
import { c, T } from "./theme.js";
|
|
52
52
|
import { dispatchPacksCommand, isPacksCommand } from "./world-packs.js";
|
|
53
53
|
const REPL_MODE = "repl";
|
|
@@ -264,6 +264,8 @@ function getCompletions(parsed) {
|
|
|
264
264
|
const cmds = getCommands().map((n) => n.startsWith("/") ? n : n);
|
|
265
265
|
return ["/", ...cmds].filter(sw);
|
|
266
266
|
}
|
|
267
|
+
const paletteNext = listNextPaletteTokens([cmd, ...words.slice(0, argIndex)], REPL_MODE);
|
|
268
|
+
if (paletteNext && paletteNext.length > 0) return paletteNext.filter(sw);
|
|
267
269
|
switch (cmd) {
|
|
268
270
|
case "logs":
|
|
269
271
|
case "log":
|
|
@@ -277,7 +279,7 @@ function getCompletions(parsed) {
|
|
|
277
279
|
if (argIndex === 0) return SERVICE_NAMES.filter(sw);
|
|
278
280
|
return [];
|
|
279
281
|
case "update":
|
|
280
|
-
return ["--check-only", "--force", "--channel=release", "--channel=preview"].filter(sw);
|
|
282
|
+
return ["--check-only", "--force", "--no-start", "--channel=release", "--channel=preview"].filter(sw);
|
|
281
283
|
case "packs":
|
|
282
284
|
case "addon":
|
|
283
285
|
if (argIndex === 0) return [...listVisiblePacksSubs(REPL_MODE)].filter(sw);
|
|
@@ -648,9 +650,11 @@ function writeLogLineToTty(wrapped) {
|
|
|
648
650
|
}
|
|
649
651
|
async function startRepl() {
|
|
650
652
|
let stopping = false;
|
|
653
|
+
let disconnectDaemon = null;
|
|
651
654
|
const onSigint = () => {
|
|
652
|
-
stdout.write(c.yellow("\n" + t("repl.
|
|
653
|
-
|
|
655
|
+
stdout.write(c.yellow("\n" + t("repl.detach") + "\n"));
|
|
656
|
+
disconnectDaemon?.();
|
|
657
|
+
disconnectDaemon = null;
|
|
654
658
|
process.exit(130);
|
|
655
659
|
};
|
|
656
660
|
const shutdown = async () => {
|
|
@@ -658,8 +662,9 @@ async function startRepl() {
|
|
|
658
662
|
stopping = true;
|
|
659
663
|
process.off("SIGINT", onSigint);
|
|
660
664
|
openLogsWindowHook = null;
|
|
661
|
-
stdout.write(c.dim(t("repl.
|
|
662
|
-
|
|
665
|
+
stdout.write(c.dim(t("repl.detach") + "\n"));
|
|
666
|
+
disconnectDaemon?.();
|
|
667
|
+
disconnectDaemon = null;
|
|
663
668
|
try {
|
|
664
669
|
setRaw(false);
|
|
665
670
|
stdin.pause();
|
|
@@ -669,6 +674,13 @@ async function startRepl() {
|
|
|
669
674
|
process.exit(0);
|
|
670
675
|
};
|
|
671
676
|
process.on("SIGINT", onSigint);
|
|
677
|
+
try {
|
|
678
|
+
const { ensureDaemonSubscription } = await import("./daemon/client.js");
|
|
679
|
+
disconnectDaemon = await ensureDaemonSubscription();
|
|
680
|
+
} catch (e) {
|
|
681
|
+
stdout.write(c.red(t("daemon.connectFailed", { message: e.message })) + "\n");
|
|
682
|
+
process.exit(1);
|
|
683
|
+
}
|
|
672
684
|
if (!stdin.isTTY) {
|
|
673
685
|
console.log(c.dim(t("repl.nonInteractive")));
|
|
674
686
|
for await (const line of (await import("node:readline/promises")).createInterface({
|
|
@@ -705,24 +717,17 @@ async function startRepl() {
|
|
|
705
717
|
stdout.write("\x1B[3J");
|
|
706
718
|
}
|
|
707
719
|
function seedWindowBuffer(w) {
|
|
720
|
+
const f = w.getReplayFilter?.() ?? { levels: [], sources: [] };
|
|
721
|
+
const disk = readDiskLogs({
|
|
722
|
+
levels: f.levels,
|
|
723
|
+
sources: f.sources,
|
|
724
|
+
limit: 5e3
|
|
725
|
+
});
|
|
708
726
|
const lines = [];
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
const
|
|
712
|
-
|
|
713
|
-
sources: f.sources
|
|
714
|
-
});
|
|
715
|
-
for (const log of disk) {
|
|
716
|
-
if (!w.acceptLog(log)) continue;
|
|
717
|
-
const { text, indent } = w.formatLogLine(log);
|
|
718
|
-
lines.push(wrapLogLine(text, indent));
|
|
719
|
-
}
|
|
720
|
-
} else {
|
|
721
|
-
for (const log of getAllLogs()) {
|
|
722
|
-
if (!w.acceptLog(log)) continue;
|
|
723
|
-
const { text, indent } = w.formatLogLine(log);
|
|
724
|
-
lines.push(wrapLogLine(text, indent));
|
|
725
|
-
}
|
|
727
|
+
for (const log of disk) {
|
|
728
|
+
if (!w.acceptLog(log)) continue;
|
|
729
|
+
const { text, indent } = w.formatLogLine(log);
|
|
730
|
+
lines.push(wrapLogLine(text, indent));
|
|
726
731
|
}
|
|
727
732
|
host.setBuffer(w.id, lines);
|
|
728
733
|
return lines;
|
|
@@ -734,14 +739,8 @@ async function startRepl() {
|
|
|
734
739
|
currentRedraw?.();
|
|
735
740
|
return;
|
|
736
741
|
}
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
if (opts?.reseed) host.invalidateBuffer(w.id);
|
|
740
|
-
lines = host.hasBuffer(w.id) ? host.getBuffer(w.id) : seedWindowBuffer(w);
|
|
741
|
-
} else {
|
|
742
|
-
host.invalidateBuffer(w.id);
|
|
743
|
-
lines = seedWindowBuffer(w);
|
|
744
|
-
}
|
|
742
|
+
if (opts?.reseed) host.invalidateBuffer(w.id);
|
|
743
|
+
const lines = host.hasBuffer(w.id) ? host.getBuffer(w.id) : seedWindowBuffer(w);
|
|
745
744
|
for (const l of lines) stdout.write(l + "\n");
|
|
746
745
|
currentRedraw?.();
|
|
747
746
|
}
|
|
@@ -967,6 +966,26 @@ async function execCmd(parts) {
|
|
|
967
966
|
stdout.write(await cmdUi(args, { block: false }) + "\n");
|
|
968
967
|
break;
|
|
969
968
|
}
|
|
969
|
+
case "daemon": {
|
|
970
|
+
const sub = args[0];
|
|
971
|
+
if (sub === "stop") {
|
|
972
|
+
const { shutdownDaemon } = await import("./daemon/client.js");
|
|
973
|
+
const err = await shutdownDaemon();
|
|
974
|
+
stdout.write((err ? c.red(err) : c.dim(t("daemon.stopped"))) + "\n");
|
|
975
|
+
} else if (sub === "status" || !sub) {
|
|
976
|
+
const { readDaemonMeta } = await import("./daemon/paths.js");
|
|
977
|
+
const { isProcessAlive } = await import("@sfmc-bds/bds-tools/process-probe");
|
|
978
|
+
const meta2 = readDaemonMeta();
|
|
979
|
+
if (!meta2 || !await isProcessAlive(meta2.pid)) {
|
|
980
|
+
stdout.write(c.dim(t("daemon.notRunning")) + "\n");
|
|
981
|
+
} else {
|
|
982
|
+
stdout.write(c.green(t("daemon.running", { pid: String(meta2.pid) })) + "\n");
|
|
983
|
+
}
|
|
984
|
+
} else {
|
|
985
|
+
stdout.write(c.yellow(t("daemon.usage") + "\n"));
|
|
986
|
+
}
|
|
987
|
+
break;
|
|
988
|
+
}
|
|
970
989
|
case "init": {
|
|
971
990
|
const wasRaw = stdin.isRaw ?? false;
|
|
972
991
|
setRaw(false);
|