@sfmc-bds/cli 0.4.5 → 0.5.1
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/daemon/client.d.ts +2 -2
- package/dist/daemon/client.d.ts.map +1 -1
- package/dist/daemon/client.js +7 -15
- package/dist/daemon/client.js.map +2 -2
- package/dist/daemon/host.d.ts +27 -0
- package/dist/daemon/host.d.ts.map +1 -0
- package/dist/daemon/host.js +178 -0
- package/dist/daemon/host.js.map +7 -0
- package/dist/daemon/server.d.ts.map +1 -1
- package/dist/daemon/server.js +2 -0
- package/dist/daemon/server.js.map +2 -2
- package/dist/management/metrics-read.d.ts +7 -0
- package/dist/management/metrics-read.d.ts.map +1 -0
- package/dist/management/metrics-read.js +80 -0
- package/dist/management/metrics-read.js.map +7 -0
- package/dist/management/platform-worker.js +20 -2
- package/dist/management/platform-worker.js.map +2 -2
- package/dist/management/server.d.ts.map +1 -1
- package/dist/management/server.js +7 -16
- package/dist/management/server.js.map +2 -2
- package/dist/management/stdio.d.ts.map +1 -1
- package/dist/management/stdio.js +10 -0
- package/dist/management/stdio.js.map +3 -3
- package/dist/module-commands.d.ts.map +1 -1
- package/dist/module-commands.js +9 -3
- package/dist/module-commands.js.map +2 -2
- package/dist/services.js +1 -1
- package/dist/services.js.map +2 -2
- package/dist/wizard.js +1 -1
- package/dist/wizard.js.map +2 -2
- package/package.json +7 -6
- package/scripts/module-install/fetch-module.mjs +6 -2
package/dist/daemon/client.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* client.ts — CLI 侧守护进程客户端:ensureDaemon + RPC + 日志订阅
|
|
3
3
|
*
|
|
4
|
-
* 若 `.sfmc/daemon.json`
|
|
5
|
-
|
|
4
|
+
* 若 `.sfmc/daemon.json` 对应进程不在或管道不可连,则在登录会话之外启动 `sfmc --daemon`,
|
|
5
|
+
* 再轮询直到 ping 成功。管理连接退出只断开管道,不停止守护进程。
|
|
6
6
|
*/
|
|
7
7
|
import net from "node:net";
|
|
8
8
|
import type { ServiceStateEvent, ServiceStatus } from "../services.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/daemon/client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/daemon/client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,GAAG,MAAM,UAAU,CAAC;AAK3B,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACvE,OAAO,EAKL,KAAK,UAAU,EAChB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAGV,YAAY,EACZ,YAAY,EAEZ,YAAY,EACb,MAAM,eAAe,CAAC;AAKvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAG5D,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,GAAG,MAAM,IAAI,CAG9F;AACD,wBAAgB,sBAAsB,IAAI,IAAI,CAAgE;AAmB9G;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,iBAAiB,KAAK,IAAI,GAAG,MAAM,IAAI,CAK1F;AAED,4BAA4B;AAC5B,cAAM,gBAAgB;IACpB,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,OAAO,CAGX;IACJ,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,WAAW,CAA4B;IAC/C,MAAM,UAAS;IAEf,YAAY,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,UAAU,EAO7C;IAED,OAAO,CAAC,MAAM;IA4Bd,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,OAAO;IAWf,0BAA0B;IAC1B,IAAI,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,YAAY,EAAE,SAAS,SAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,CAsBpG;IAED,8BAA8B;IACxB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAQ/B;IAED,mBAAmB;IACnB,UAAU,IAAI,IAAI,CAQjB;CACF;AAiED;;;GAGG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAU9D;AA2CD;;;GAGG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAInG;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,MAAM,IAAI,CAAC,CAWpE;AAED;;GAEG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,CAqBtD;AAED;;GAEG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC,CAItE;AAED;;GAEG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,MAAM,CAAC,CAK3D;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAKhG"}
|
package/dist/daemon/client.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { isProcessAlive } from "@sfmc-bds/bds-tools/process-probe";
|
|
2
|
-
import { logsDir } from "@sfmc-bds/sdk/node/config";
|
|
3
|
-
import { spawn } from "node:child_process";
|
|
4
2
|
import { randomBytes } from "node:crypto";
|
|
5
3
|
import fs from "node:fs";
|
|
6
4
|
import path from "node:path";
|
|
@@ -17,6 +15,7 @@ import {
|
|
|
17
15
|
} from "./paths.js";
|
|
18
16
|
import { isLogPayload } from "./protocol.js";
|
|
19
17
|
import { isDaemonServer } from "./role.js";
|
|
18
|
+
import { launchDaemonHost } from "./host.js";
|
|
20
19
|
import { fileURLToPath } from "node:url";
|
|
21
20
|
import { lockOwnerAlive, maintenanceToken } from "@sfmc-bds/management/node";
|
|
22
21
|
function onDaemonManagementEvent(callback) {
|
|
@@ -196,20 +195,13 @@ function spawnDaemonProcess() {
|
|
|
196
195
|
}
|
|
197
196
|
const entry = process.env.SFMC_DAEMON_ENTRY ?? fileURLToPath(new URL("../main.js", import.meta.url));
|
|
198
197
|
if (!entry) throw new Error("cannot resolve sfmc entry for daemon spawn");
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
env: { ...process.env, SFMC_ROOT: ROOT },
|
|
206
|
-
cwd: ROOT
|
|
198
|
+
launchDaemonHost({
|
|
199
|
+
node: process.env.SFMC_NODE_BINARY ?? process.execPath,
|
|
200
|
+
entry,
|
|
201
|
+
root: ROOT,
|
|
202
|
+
logPath: daemonLogPath(),
|
|
203
|
+
...process.env.SFMC_PNPM_ENTRY ? { pnpm: process.env.SFMC_PNPM_ENTRY } : {}
|
|
207
204
|
});
|
|
208
|
-
child.unref();
|
|
209
|
-
try {
|
|
210
|
-
fs.closeSync(logFd);
|
|
211
|
-
} catch {
|
|
212
|
-
}
|
|
213
205
|
}
|
|
214
206
|
async function ensureDaemon() {
|
|
215
207
|
if (isDaemonServer()) throw new Error("ensureDaemon must not run inside daemon server");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/daemon/client.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * client.ts \u2014 CLI \u4FA7\u5B88\u62A4\u8FDB\u7A0B\u5BA2\u6237\u7AEF\uFF1AensureDaemon + RPC + \u65E5\u5FD7\u8BA2\u9605\n *\n * \u82E5 `.sfmc/daemon.json` \u5BF9\u5E94\u8FDB\u7A0B\u4E0D\u5728\u6216\u7BA1\u9053\u4E0D\u53EF\u8FDE\uFF0C\u5219 spawn `sfmc --daemon`\n *\uFF08detached + windowsHide\uFF09\uFF0C\u518D\u8F6E\u8BE2\u76F4\u5230 ping \u6210\u529F\u3002\n */\n\nimport { isProcessAlive } from \"@sfmc-bds/bds-tools/process-probe\";\nimport { logsDir } from \"@sfmc-bds/sdk/node/config\";\nimport { spawn } from \"node:child_process\";\nimport { randomBytes } from \"node:crypto\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport net from \"node:net\";\nimport process from \"node:process\";\nimport { EventEmitter } from \"node:events\";\nimport { ingestRemoteLog } from \"../logs.js\";\nimport { ROOT } from \"../runtime.js\";\nimport type { ServiceStateEvent, ServiceStatus } from \"../services.js\";\nimport {\n clearDaemonMeta,\n daemonLogPath,\n daemonMetaPath,\n readDaemonMeta,\n type DaemonMeta,\n} from \"./paths.js\";\nimport type {\n DaemonEvent,\n DaemonFrame,\n DaemonMethod,\n DaemonParams,\n DaemonRequest,\n DaemonResult,\n} from \"./protocol.js\";\nimport { isLogPayload } from \"./protocol.js\";\nimport { isDaemonServer } from \"./role.js\";\nimport { fileURLToPath } from \"node:url\";\nimport type { ManagementEvent } from \"@sfmc-bds/management\";\nimport { lockOwnerAlive, maintenanceToken } from \"@sfmc-bds/management/node\";\n\nexport function onDaemonManagementEvent(callback: (event: ManagementEvent) => void): () => void {\n remoteStateBus.on(\"management\", callback);\n return () => { remoteStateBus.off(\"management\", callback); };\n}\nexport function disconnectDaemonClient(): void { subscriptionCount = 0; shared?.disconnect(); shared = null; }\n\n/** \u5355\u6B21 RPC \u9ED8\u8BA4\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09 */\nconst CALL_TIMEOUT_MS = 120_000;\n/** ensureDaemon \u7B49\u5F85\u5B88\u62A4\u8FDB\u7A0B\u5C31\u7EEA\u7684\u603B\u65F6\u957F */\nconst READY_TIMEOUT_MS = 15_000;\n/** \u8F6E\u8BE2\u95F4\u9694 */\nconst READY_POLL_MS = 150;\n\n/** \u5171\u4EAB\u8FDE\u63A5\uFF08\u540C\u4E00 CLI \u8FDB\u7A0B\u590D\u7528\uFF09 */\nlet shared: DaemonConnection | null = null;\n/** \u91CD\u8FDE\u671F\u95F4\u590D\u7528\u540C\u4E00\u6B21\u5EFA\u8FDE\uFF0C\u907F\u514D\u5E76\u884C\u72B6\u6001\u67E5\u8BE2\u8986\u76D6\u5DF2\u8BA2\u9605\u7684\u8FDE\u63A5\u3002 */\nlet connecting: Promise<DaemonConnection> | null = null;\n/** \u8BA2\u9605\u610F\u56FE\u5C5E\u4E8E\u5BA2\u6237\u7AEF\u4F1A\u8BDD\uFF0C\u4E0D\u80FD\u968F\u7740\u65E7 socket \u5173\u95ED\u800C\u4E22\u5931\u3002 */\nlet subscriptionCount = 0;\n\n/** \u672C\u5730\u72B6\u6001\u4E8B\u4EF6\u603B\u7EBF\uFF1A\u628A daemon \u63A8\u9001\u7684 state \u8F6C\u7ED9 CLI \u4FA7 onServiceStateChange \u8BA2\u9605\u8005 */\nconst remoteStateBus = new EventEmitter();\n\n/**\n * \u8BA2\u9605\u5B88\u62A4\u8FDB\u7A0B\u63A8\u9001\u7684\u670D\u52A1\u542F\u505C\u4E8B\u4EF6\uFF08CLI \u8FDB\u7A0B\u7528\uFF09\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1AREPL \u6839\u636E running \u96C6\u5408\u5237\u65B0\u53D1\u9001\u76EE\u6807 / \u670D\u52A1\u7A97\uFF0C\u65E0\u9700\u672C\u5730\u6301\u6709 ChildProcess\u3002\n */\nexport function onRemoteServiceStateChange(fn: (ev: ServiceStateEvent) => void): () => void {\n remoteStateBus.on(\"state\", fn);\n return () => {\n remoteStateBus.off(\"state\", fn);\n };\n}\n\n/** \u5355\u6761\u8FDE\u63A5\uFF1A\u8BF7\u6C42/\u54CD\u5E94\u5339\u914D + \u53EF\u9009\u4E8B\u4EF6\u8BA2\u9605 */\nclass DaemonConnection {\n private sock: net.Socket;\n private meta: DaemonMeta;\n private buf = \"\";\n private pending = new Map<\n string,\n { resolve: (r: DaemonResult) => void; reject: (e: Error) => void; timer: ReturnType<typeof setTimeout> }\n >();\n private subscribed = false;\n private subscribing: Promise<void> | undefined;\n closed = false;\n\n constructor(sock: net.Socket, meta: DaemonMeta) {\n this.sock = sock;\n this.meta = meta;\n sock.setEncoding(\"utf8\");\n sock.on(\"data\", (chunk: string) => this.onData(chunk));\n sock.on(\"close\", () => this.onClose(new Error(\"daemon connection closed\")));\n sock.on(\"error\", (e) => this.onClose(e instanceof Error ? e : new Error(String(e))));\n }\n\n private onData(chunk: string): void {\n this.buf += chunk;\n let idx: number;\n while ((idx = this.buf.indexOf(\"\\n\")) >= 0) {\n const line = this.buf.slice(0, idx).trim();\n this.buf = this.buf.slice(idx + 1);\n if (!line) continue;\n let frame: DaemonFrame;\n try {\n frame = JSON.parse(line) as DaemonFrame;\n } catch {\n continue;\n }\n if (frame.type === \"event\") {\n this.handleEvent(frame);\n continue;\n }\n if (frame.type === \"res\") {\n const wait = this.pending.get(frame.id);\n if (!wait) continue;\n this.pending.delete(frame.id);\n clearTimeout(wait.timer);\n if (frame.ok && frame.result) wait.resolve(frame.result);\n else wait.reject(new Error(frame.error ?? \"daemon error\"));\n }\n }\n }\n\n private handleEvent(ev: DaemonEvent): void {\n if (ev.event === \"management\") { remoteStateBus.emit(\"management\", ev.payload); return; }\n if (ev.event === \"log\" && isLogPayload(ev.payload)) {\n ingestRemoteLog(ev.payload.text, ev.payload.source, ev.payload.level);\n return;\n }\n if (ev.event === \"state\") {\n remoteStateBus.emit(\"state\", ev.payload as ServiceStateEvent);\n }\n }\n\n private onClose(err: Error): void {\n if (this.closed) return;\n this.closed = true;\n for (const [, wait] of this.pending) {\n clearTimeout(wait.timer);\n wait.reject(err);\n }\n this.pending.clear();\n if (shared === this) shared = null;\n }\n\n /** \u53D1\u9001 RPC \u5E76\u7B49\u5F85\u5339\u914D id \u7684\u54CD\u5E94 */\n call(method: DaemonMethod, params?: DaemonParams, timeoutMs = CALL_TIMEOUT_MS): Promise<DaemonResult> {\n if (this.closed || this.sock.destroyed) {\n return Promise.reject(new Error(\"daemon not connected\"));\n }\n const id = randomBytes(8).toString(\"hex\");\n const req: DaemonRequest = params\n ? { id, token: this.meta.token, method, params }\n : { id, token: this.meta.token, method };\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pending.delete(id);\n reject(new Error(`daemon call timeout: ${method}`));\n }, timeoutMs);\n this.pending.set(id, { resolve, reject, timer });\n try {\n this.sock.write(JSON.stringify(req) + \"\\n\");\n } catch (e) {\n this.pending.delete(id);\n clearTimeout(timer);\n reject(e instanceof Error ? e : new Error(String(e)));\n }\n });\n }\n\n /** \u8BA2\u9605\u65E5\u5FD7\u4E0E\u72B6\u6001\u4E8B\u4EF6\uFF08\u540C\u4E00\u8FDE\u63A5\u4E0A\u7EE7\u7EED\u53D1 RPC\uFF09 */\n async subscribe(): Promise<void> {\n if (this.subscribed) return;\n if (!this.subscribing) {\n this.subscribing = this.call(\"subscribe\").then(() => {\n this.subscribed = true;\n }).finally(() => { this.subscribing = undefined; });\n }\n await this.subscribing;\n }\n\n /** \u65AD\u5F00\u8FDE\u63A5\uFF08\u4E0D\u505C\u5B88\u62A4\u8FDB\u7A0B\uFF09 */\n disconnect(): void {\n this.closed = true;\n try {\n this.sock.end();\n } catch {\n /* ignore */\n }\n if (shared === this) shared = null;\n }\n}\n\nfunction connectSocket(pipe: string, timeoutMs = 3000): Promise<net.Socket> {\n return new Promise((resolve, reject) => {\n const sock = net.createConnection(pipe);\n const timer = setTimeout(() => {\n sock.destroy();\n reject(new Error(\"connect timeout\"));\n }, timeoutMs);\n sock.once(\"connect\", () => {\n clearTimeout(timer);\n resolve(sock);\n });\n sock.once(\"error\", (e) => {\n clearTimeout(timer);\n reject(e);\n });\n });\n}\n\n/**\n * \u5C1D\u8BD5\u8FDE\u63A5\u5DF2\u6709 daemon\uFF1B\u6210\u529F\u5219\u8FD4\u56DE\u8FDE\u63A5\uFF0C\u5931\u8D25\u8FD4\u56DE null\u3002\n */\nasync function tryConnectExisting(): Promise<DaemonConnection | null> {\n const meta = readDaemonMeta();\n if (!meta) return null;\n if (!(await isProcessAlive(meta.pid))) {\n return null;\n }\n try {\n const sock = await connectSocket(meta.pipe);\n const conn = new DaemonConnection(sock, meta);\n await conn.call(\"ping\", undefined, 3000);\n return conn;\n } catch {\n return null;\n }\n}\n\n/**\n * \u62C9\u8D77\u5B88\u62A4\u8FDB\u7A0B\u5B50\u8FDB\u7A0B\uFF08detached\uFF0C\u65E5\u5FD7\u5199\u5165 daemon.log\uFF09\u3002\n * \u5165\u53E3\u811A\u672C\u590D\u7528\u5F53\u524D CLI \u7684 argv[1]\uFF08\u5373 sfmc \u4E3B\u5165\u53E3\uFF09\u3002\n */\nfunction spawnDaemonProcess(): void {\n const lockFile = path.join(ROOT, \".sfmc\", \"maintenance.lock\");\n if (fs.existsSync(lockFile)) {\n const owner = JSON.parse(fs.readFileSync(lockFile, \"utf8\")) as { pid?: number; nonce?: string };\n const token = maintenanceToken(ROOT) ?? process.env.SFMC_MAINTENANCE_TOKEN;\n const holder = token === owner.nonce;\n const pidKnown = Number.isSafeInteger(owner.pid) && (owner.pid ?? 0) > 0;\n // \u5F53\u524D\u7EF4\u62A4\u8FDB\u7A0B\u81EA\u5DF1\u62C9\u8D77\u66FF\u6362\u7528\u7684\u5B88\u62A4\u8FDB\u7A0B\u65F6 token \u76F8\u540C\uFF0C\u5141\u8BB8\u542F\u52A8\u3002\n // \u6301\u6709\u8005\u5DF2\u9000\u51FA\u5219\u4E0D\u518D\u6321\u4F4F\uFF1A\u5426\u5219\u754C\u9762\u65E0\u6CD5\u91CD\u8FDE\uFF0C\u4EFB\u52A1\u4F1A\u6C38\u8FDC\u505C\u5728\u300C\u8FDB\u884C\u4E2D\u300D\u3002\n if (!holder && (!pidKnown || lockOwnerAlive(owner.pid ?? 0))) throw new Error(\"\u5B9E\u4F8B\u6B63\u5728\u7EF4\u62A4\uFF1B\u540E\u53F0\u5207\u6362\u5B8C\u6210\u524D\u4E0D\u4F1A\u521B\u5EFA\u53E6\u4E00\u4E2A\u5B88\u62A4\u8FDB\u7A0B\");\n }\n const entry = process.env.SFMC_DAEMON_ENTRY ?? fileURLToPath(new URL(\"../main.js\", import.meta.url));\n if (!entry) throw new Error(\"cannot resolve sfmc entry for daemon spawn\");\n fs.mkdirSync(logsDir(ROOT), { recursive: true });\n const logFd = fs.openSync(daemonLogPath(), \"a\");\n const child = spawn(process.env.SFMC_NODE_BINARY ?? process.execPath, [entry, \"--daemon\"], {\n detached: true,\n windowsHide: true,\n stdio: [\"ignore\", logFd, logFd],\n env: { ...process.env, SFMC_ROOT: ROOT },\n cwd: ROOT,\n });\n child.unref();\n try {\n fs.closeSync(logFd);\n } catch {\n /* ignore */\n }\n}\n\n/**\n * \u786E\u4FDD\u5B88\u62A4\u8FDB\u7A0B\u53EF\u7528\u5E76\u8FD4\u56DE\u5171\u4EAB\u8FDE\u63A5\u3002\n * \u5B88\u62A4\u8FDB\u7A0B\u8FDB\u7A0B\u5185\u8C03\u7528\u4F1A\u629B\u9519\uFF08\u5E94\u76F4\u63A5\u8D70\u672C\u5730 cmd*\uFF09\u3002\n */\nexport async function ensureDaemon(): Promise<DaemonConnection> {\n if (isDaemonServer()) throw new Error(\"ensureDaemon must not run inside daemon server\");\n let conn = shared && !shared.closed ? shared : null;\n if (!conn) {\n connecting ??= connectDaemon().finally(() => { connecting = null; });\n conn = await connecting;\n }\n // \u7BA1\u7406\u8BF7\u6C42\u91CD\u8FDE\u6210\u529F\u540E\uFF0C\u5148\u6062\u590D\u65E5\u5FD7/\u72B6\u6001\u8BA2\u9605\uFF0C\u518D\u8FD4\u56DE\u65B0\u8FDE\u63A5\u3002\n if (subscriptionCount > 0) await conn.subscribe();\n return conn;\n}\n\nasync function connectDaemon(): Promise<DaemonConnection> {\n if (isDaemonServer()) {\n throw new Error(\"ensureDaemon must not run inside daemon server\");\n }\n if (shared && !shared.closed) return shared;\n\n let conn = await tryConnectExisting();\n if (conn) {\n shared = conn;\n return conn;\n }\n\n const existing = readDaemonMeta();\n if (existing && (await isProcessAlive(existing.pid))) {\n /* PID \u4ECD\u5728\u4F46\u6682\u65F6\u8FDE\u4E0D\u4E0A\uFF1A\u77ED\u6682\u91CD\u8BD5\uFF0C\u7981\u6B62\u518D spawn \u7B2C\u4E8C\u5957\u5B88\u62A4\u8FDB\u7A0B */\n const deadline = Date.now() + READY_TIMEOUT_MS;\n while (Date.now() < deadline) {\n await new Promise((r) => setTimeout(r, READY_POLL_MS));\n conn = await tryConnectExisting();\n if (conn) {\n shared = conn;\n return conn;\n }\n }\n throw new Error(`daemon pid ${existing.pid} is alive but pipe is unreachable`);\n }\n\n spawnDaemonProcess();\n const deadline = Date.now() + READY_TIMEOUT_MS;\n while (Date.now() < deadline) {\n await new Promise((r) => setTimeout(r, READY_POLL_MS));\n if (!fs.existsSync(daemonMetaPath())) continue;\n conn = await tryConnectExisting();\n if (conn) {\n shared = conn;\n return conn;\n }\n }\n throw new Error(\"failed to start or connect sfmc daemon\");\n}\n\n/**\n * \u8C03\u7528\u5B88\u62A4\u8FDB\u7A0B\u65B9\u6CD5\uFF1B\u81EA\u52A8 ensureDaemon\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1ACLI \u4FA7 status/start/stop/send/update \u7B49\u8FD0\u7EF4\u547D\u4EE4\u7684\u7EDF\u4E00\u51FA\u53E3\u3002\n */\nexport async function callDaemon(method: DaemonMethod, params?: DaemonParams): Promise<DaemonResult> {\n const conn = await ensureDaemon();\n const token = maintenanceToken(ROOT) ?? process.env.SFMC_MAINTENANCE_TOKEN;\n return conn.call(method, token ? { ...params, maintenanceToken: token } : params);\n}\n\n/**\n * \u786E\u4FDD\u5DF2\u8FDE\u63A5\u5E76\u8BA2\u9605\u8FDC\u7A0B\u65E5\u5FD7/\u72B6\u6001\uFF1B\u8FD4\u56DE\u65AD\u5F00\u51FD\u6570\uFF08\u4E0D\u505C\u5B88\u62A4\u8FDB\u7A0B\uFF09\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1AREPL \u542F\u52A8\u65F6\u6302\u4E0A\u5B9E\u65F6\u65E5\u5FD7\u6D41\uFF0C\u9000\u51FA\u65F6\u53EA disconnect\u3002\n */\nexport async function ensureDaemonSubscription(): Promise<() => void> {\n subscriptionCount++;\n try { await ensureDaemon(); }\n catch (error) { subscriptionCount = Math.max(0, subscriptionCount - 1); throw error; }\n let disposed = false;\n return () => {\n if (disposed) return;\n disposed = true;\n subscriptionCount = Math.max(0, subscriptionCount - 1);\n if (subscriptionCount === 0) shared?.disconnect();\n };\n}\n\n/**\n * \u8BF7\u6C42\u5B88\u62A4\u8FDB\u7A0B stopAll \u540E\u9000\u51FA\uFF08`sfmc daemon stop`\uFF09\u3002\n */\nexport async function shutdownDaemon(): Promise<string> {\n try {\n const conn = await ensureDaemon();\n const token = maintenanceToken(ROOT) ?? process.env.SFMC_MAINTENANCE_TOKEN;\n await conn.call(\"shutdown\", token ? { maintenanceToken: token } : undefined);\n conn.disconnect();\n } catch (e) {\n const meta = readDaemonMeta();\n if (!meta || !await isProcessAlive(meta.pid)) { clearDaemonMeta(); return \"\u5B88\u62A4\u8FDB\u7A0B\u5DF2\u505C\u6B62\"; }\n throw e;\n }\n /* \u77ED\u6682\u7B49\u5F85 meta \u6E05\u7406 */\n const deadline = Date.now() + 5000;\n while (Date.now() < deadline) {\n if (!readDaemonMeta()) break;\n await new Promise((r) => setTimeout(r, 100));\n }\n const remaining = readDaemonMeta();\n if (remaining && await isProcessAlive(remaining.pid)) throw new Error(\"\u5B88\u62A4\u8FDB\u7A0B\u5C1A\u672A\u9000\u51FA\uFF0C\u4FDD\u7559\u8FDE\u63A5\u4FE1\u606F\");\n clearDaemonMeta();\n return \"\";\n}\n\n/**\n * \u4ECE\u5B88\u62A4\u8FDB\u7A0B\u62C9\u53D6\u8FD0\u884C\u6001\u884C\uFF1B\u5931\u8D25\u65F6\u8FD4\u56DE\u7A7A\u6570\u7EC4\u7531\u8C03\u7528\u65B9\u964D\u7EA7\u3002\n */\nexport async function queryRuntimeViaDaemon(): Promise<ServiceStatus[]> {\n const result = await callDaemon(\"status\");\n if (result.kind === \"status\") return result.rows;\n return [];\n}\n\n/**\n * \u4ECE\u5B88\u62A4\u8FDB\u7A0B\u62C9\u53D6\u683C\u5F0F\u5316 status \u6587\u672C\u3002\n */\nexport async function statusTextViaDaemon(): Promise<string> {\n const result = await callDaemon(\"status\");\n if (result.kind === \"status\") return result.text;\n if (result.kind === \"text\") return result.text;\n return \"\";\n}\n\n/**\n * \u901A\u7528\u300C\u8FD4\u56DE text\u300D\u7C7B RPC \u8F85\u52A9\u3002\n */\nexport async function textViaDaemon(method: DaemonMethod, params?: DaemonParams): Promise<string> {\n const result = await callDaemon(method, params);\n if (result.kind === \"text\") return result.text;\n if (result.kind === \"status\") return result.text;\n return \"\";\n}\n"],
|
|
5
|
-
"mappings": "AAOA,SAAS,sBAAsB;AAC/B,SAAS,
|
|
4
|
+
"sourcesContent": ["/**\n * client.ts \u2014 CLI \u4FA7\u5B88\u62A4\u8FDB\u7A0B\u5BA2\u6237\u7AEF\uFF1AensureDaemon + RPC + \u65E5\u5FD7\u8BA2\u9605\n *\n * \u82E5 `.sfmc/daemon.json` \u5BF9\u5E94\u8FDB\u7A0B\u4E0D\u5728\u6216\u7BA1\u9053\u4E0D\u53EF\u8FDE\uFF0C\u5219\u5728\u767B\u5F55\u4F1A\u8BDD\u4E4B\u5916\u542F\u52A8 `sfmc --daemon`\uFF0C\n * \u518D\u8F6E\u8BE2\u76F4\u5230 ping \u6210\u529F\u3002\u7BA1\u7406\u8FDE\u63A5\u9000\u51FA\u53EA\u65AD\u5F00\u7BA1\u9053\uFF0C\u4E0D\u505C\u6B62\u5B88\u62A4\u8FDB\u7A0B\u3002\n */\n\nimport { isProcessAlive } from \"@sfmc-bds/bds-tools/process-probe\";\nimport { randomBytes } from \"node:crypto\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport net from \"node:net\";\nimport process from \"node:process\";\nimport { EventEmitter } from \"node:events\";\nimport { ingestRemoteLog } from \"../logs.js\";\nimport { ROOT } from \"../runtime.js\";\nimport type { ServiceStateEvent, ServiceStatus } from \"../services.js\";\nimport {\n clearDaemonMeta,\n daemonLogPath,\n daemonMetaPath,\n readDaemonMeta,\n type DaemonMeta,\n} from \"./paths.js\";\nimport type {\n DaemonEvent,\n DaemonFrame,\n DaemonMethod,\n DaemonParams,\n DaemonRequest,\n DaemonResult,\n} from \"./protocol.js\";\nimport { isLogPayload } from \"./protocol.js\";\nimport { isDaemonServer } from \"./role.js\";\nimport { launchDaemonHost } from \"./host.js\";\nimport { fileURLToPath } from \"node:url\";\nimport type { ManagementEvent } from \"@sfmc-bds/management\";\nimport { lockOwnerAlive, maintenanceToken } from \"@sfmc-bds/management/node\";\n\nexport function onDaemonManagementEvent(callback: (event: ManagementEvent) => void): () => void {\n remoteStateBus.on(\"management\", callback);\n return () => { remoteStateBus.off(\"management\", callback); };\n}\nexport function disconnectDaemonClient(): void { subscriptionCount = 0; shared?.disconnect(); shared = null; }\n\n/** \u5355\u6B21 RPC \u9ED8\u8BA4\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09 */\nconst CALL_TIMEOUT_MS = 120_000;\n/** ensureDaemon \u7B49\u5F85\u5B88\u62A4\u8FDB\u7A0B\u5C31\u7EEA\u7684\u603B\u65F6\u957F */\nconst READY_TIMEOUT_MS = 15_000;\n/** \u8F6E\u8BE2\u95F4\u9694 */\nconst READY_POLL_MS = 150;\n\n/** \u5171\u4EAB\u8FDE\u63A5\uFF08\u540C\u4E00 CLI \u8FDB\u7A0B\u590D\u7528\uFF09 */\nlet shared: DaemonConnection | null = null;\n/** \u91CD\u8FDE\u671F\u95F4\u590D\u7528\u540C\u4E00\u6B21\u5EFA\u8FDE\uFF0C\u907F\u514D\u5E76\u884C\u72B6\u6001\u67E5\u8BE2\u8986\u76D6\u5DF2\u8BA2\u9605\u7684\u8FDE\u63A5\u3002 */\nlet connecting: Promise<DaemonConnection> | null = null;\n/** \u8BA2\u9605\u610F\u56FE\u5C5E\u4E8E\u5BA2\u6237\u7AEF\u4F1A\u8BDD\uFF0C\u4E0D\u80FD\u968F\u7740\u65E7 socket \u5173\u95ED\u800C\u4E22\u5931\u3002 */\nlet subscriptionCount = 0;\n\n/** \u672C\u5730\u72B6\u6001\u4E8B\u4EF6\u603B\u7EBF\uFF1A\u628A daemon \u63A8\u9001\u7684 state \u8F6C\u7ED9 CLI \u4FA7 onServiceStateChange \u8BA2\u9605\u8005 */\nconst remoteStateBus = new EventEmitter();\n\n/**\n * \u8BA2\u9605\u5B88\u62A4\u8FDB\u7A0B\u63A8\u9001\u7684\u670D\u52A1\u542F\u505C\u4E8B\u4EF6\uFF08CLI \u8FDB\u7A0B\u7528\uFF09\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1AREPL \u6839\u636E running \u96C6\u5408\u5237\u65B0\u53D1\u9001\u76EE\u6807 / \u670D\u52A1\u7A97\uFF0C\u65E0\u9700\u672C\u5730\u6301\u6709 ChildProcess\u3002\n */\nexport function onRemoteServiceStateChange(fn: (ev: ServiceStateEvent) => void): () => void {\n remoteStateBus.on(\"state\", fn);\n return () => {\n remoteStateBus.off(\"state\", fn);\n };\n}\n\n/** \u5355\u6761\u8FDE\u63A5\uFF1A\u8BF7\u6C42/\u54CD\u5E94\u5339\u914D + \u53EF\u9009\u4E8B\u4EF6\u8BA2\u9605 */\nclass DaemonConnection {\n private sock: net.Socket;\n private meta: DaemonMeta;\n private buf = \"\";\n private pending = new Map<\n string,\n { resolve: (r: DaemonResult) => void; reject: (e: Error) => void; timer: ReturnType<typeof setTimeout> }\n >();\n private subscribed = false;\n private subscribing: Promise<void> | undefined;\n closed = false;\n\n constructor(sock: net.Socket, meta: DaemonMeta) {\n this.sock = sock;\n this.meta = meta;\n sock.setEncoding(\"utf8\");\n sock.on(\"data\", (chunk: string) => this.onData(chunk));\n sock.on(\"close\", () => this.onClose(new Error(\"daemon connection closed\")));\n sock.on(\"error\", (e) => this.onClose(e instanceof Error ? e : new Error(String(e))));\n }\n\n private onData(chunk: string): void {\n this.buf += chunk;\n let idx: number;\n while ((idx = this.buf.indexOf(\"\\n\")) >= 0) {\n const line = this.buf.slice(0, idx).trim();\n this.buf = this.buf.slice(idx + 1);\n if (!line) continue;\n let frame: DaemonFrame;\n try {\n frame = JSON.parse(line) as DaemonFrame;\n } catch {\n continue;\n }\n if (frame.type === \"event\") {\n this.handleEvent(frame);\n continue;\n }\n if (frame.type === \"res\") {\n const wait = this.pending.get(frame.id);\n if (!wait) continue;\n this.pending.delete(frame.id);\n clearTimeout(wait.timer);\n if (frame.ok && frame.result) wait.resolve(frame.result);\n else wait.reject(new Error(frame.error ?? \"daemon error\"));\n }\n }\n }\n\n private handleEvent(ev: DaemonEvent): void {\n if (ev.event === \"management\") { remoteStateBus.emit(\"management\", ev.payload); return; }\n if (ev.event === \"log\" && isLogPayload(ev.payload)) {\n ingestRemoteLog(ev.payload.text, ev.payload.source, ev.payload.level);\n return;\n }\n if (ev.event === \"state\") {\n remoteStateBus.emit(\"state\", ev.payload as ServiceStateEvent);\n }\n }\n\n private onClose(err: Error): void {\n if (this.closed) return;\n this.closed = true;\n for (const [, wait] of this.pending) {\n clearTimeout(wait.timer);\n wait.reject(err);\n }\n this.pending.clear();\n if (shared === this) shared = null;\n }\n\n /** \u53D1\u9001 RPC \u5E76\u7B49\u5F85\u5339\u914D id \u7684\u54CD\u5E94 */\n call(method: DaemonMethod, params?: DaemonParams, timeoutMs = CALL_TIMEOUT_MS): Promise<DaemonResult> {\n if (this.closed || this.sock.destroyed) {\n return Promise.reject(new Error(\"daemon not connected\"));\n }\n const id = randomBytes(8).toString(\"hex\");\n const req: DaemonRequest = params\n ? { id, token: this.meta.token, method, params }\n : { id, token: this.meta.token, method };\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pending.delete(id);\n reject(new Error(`daemon call timeout: ${method}`));\n }, timeoutMs);\n this.pending.set(id, { resolve, reject, timer });\n try {\n this.sock.write(JSON.stringify(req) + \"\\n\");\n } catch (e) {\n this.pending.delete(id);\n clearTimeout(timer);\n reject(e instanceof Error ? e : new Error(String(e)));\n }\n });\n }\n\n /** \u8BA2\u9605\u65E5\u5FD7\u4E0E\u72B6\u6001\u4E8B\u4EF6\uFF08\u540C\u4E00\u8FDE\u63A5\u4E0A\u7EE7\u7EED\u53D1 RPC\uFF09 */\n async subscribe(): Promise<void> {\n if (this.subscribed) return;\n if (!this.subscribing) {\n this.subscribing = this.call(\"subscribe\").then(() => {\n this.subscribed = true;\n }).finally(() => { this.subscribing = undefined; });\n }\n await this.subscribing;\n }\n\n /** \u65AD\u5F00\u8FDE\u63A5\uFF08\u4E0D\u505C\u5B88\u62A4\u8FDB\u7A0B\uFF09 */\n disconnect(): void {\n this.closed = true;\n try {\n this.sock.end();\n } catch {\n /* ignore */\n }\n if (shared === this) shared = null;\n }\n}\n\nfunction connectSocket(pipe: string, timeoutMs = 3000): Promise<net.Socket> {\n return new Promise((resolve, reject) => {\n const sock = net.createConnection(pipe);\n const timer = setTimeout(() => {\n sock.destroy();\n reject(new Error(\"connect timeout\"));\n }, timeoutMs);\n sock.once(\"connect\", () => {\n clearTimeout(timer);\n resolve(sock);\n });\n sock.once(\"error\", (e) => {\n clearTimeout(timer);\n reject(e);\n });\n });\n}\n\n/**\n * \u5C1D\u8BD5\u8FDE\u63A5\u5DF2\u6709 daemon\uFF1B\u6210\u529F\u5219\u8FD4\u56DE\u8FDE\u63A5\uFF0C\u5931\u8D25\u8FD4\u56DE null\u3002\n */\nasync function tryConnectExisting(): Promise<DaemonConnection | null> {\n const meta = readDaemonMeta();\n if (!meta) return null;\n if (!(await isProcessAlive(meta.pid))) {\n return null;\n }\n try {\n const sock = await connectSocket(meta.pipe);\n const conn = new DaemonConnection(sock, meta);\n await conn.call(\"ping\", undefined, 3000);\n return conn;\n } catch {\n return null;\n }\n}\n\n/**\n * \u62C9\u8D77\u5B88\u62A4\u8FDB\u7A0B\u3002\u5165\u53E3\u811A\u672C\u590D\u7528\u5F53\u524D CLI \u7684 argv[1]\uFF08\u5373 sfmc \u4E3B\u5165\u53E3\uFF09\u3002\n * \u5B9E\u9645\u542F\u52A8\u4EA4\u7ED9 host\uFF1ASSH \u4F1A\u8BDD\u91CC\u4E0D\u4F1A\u628A\u5B88\u62A4\u8FDB\u7A0B\u7559\u5728\u8FD9\u6B21\u767B\u5F55\u4E2D\u3002\n */\nfunction spawnDaemonProcess(): void {\n const lockFile = path.join(ROOT, \".sfmc\", \"maintenance.lock\");\n if (fs.existsSync(lockFile)) {\n const owner = JSON.parse(fs.readFileSync(lockFile, \"utf8\")) as { pid?: number; nonce?: string };\n const token = maintenanceToken(ROOT) ?? process.env.SFMC_MAINTENANCE_TOKEN;\n const holder = token === owner.nonce;\n const pidKnown = Number.isSafeInteger(owner.pid) && (owner.pid ?? 0) > 0;\n // \u5F53\u524D\u7EF4\u62A4\u8FDB\u7A0B\u81EA\u5DF1\u62C9\u8D77\u66FF\u6362\u7528\u7684\u5B88\u62A4\u8FDB\u7A0B\u65F6 token \u76F8\u540C\uFF0C\u5141\u8BB8\u542F\u52A8\u3002\n // \u6301\u6709\u8005\u5DF2\u9000\u51FA\u5219\u4E0D\u518D\u6321\u4F4F\uFF1A\u5426\u5219\u754C\u9762\u65E0\u6CD5\u91CD\u8FDE\uFF0C\u4EFB\u52A1\u4F1A\u6C38\u8FDC\u505C\u5728\u300C\u8FDB\u884C\u4E2D\u300D\u3002\n if (!holder && (!pidKnown || lockOwnerAlive(owner.pid ?? 0))) throw new Error(\"\u5B9E\u4F8B\u6B63\u5728\u7EF4\u62A4\uFF1B\u540E\u53F0\u5207\u6362\u5B8C\u6210\u524D\u4E0D\u4F1A\u521B\u5EFA\u53E6\u4E00\u4E2A\u5B88\u62A4\u8FDB\u7A0B\");\n }\n const entry = process.env.SFMC_DAEMON_ENTRY ?? fileURLToPath(new URL(\"../main.js\", import.meta.url));\n if (!entry) throw new Error(\"cannot resolve sfmc entry for daemon spawn\");\n launchDaemonHost({\n node: process.env.SFMC_NODE_BINARY ?? process.execPath,\n entry,\n root: ROOT,\n logPath: daemonLogPath(),\n ...(process.env.SFMC_PNPM_ENTRY ? { pnpm: process.env.SFMC_PNPM_ENTRY } : {}),\n });\n}\n\n/**\n * \u786E\u4FDD\u5B88\u62A4\u8FDB\u7A0B\u53EF\u7528\u5E76\u8FD4\u56DE\u5171\u4EAB\u8FDE\u63A5\u3002\n * \u5B88\u62A4\u8FDB\u7A0B\u8FDB\u7A0B\u5185\u8C03\u7528\u4F1A\u629B\u9519\uFF08\u5E94\u76F4\u63A5\u8D70\u672C\u5730 cmd*\uFF09\u3002\n */\nexport async function ensureDaemon(): Promise<DaemonConnection> {\n if (isDaemonServer()) throw new Error(\"ensureDaemon must not run inside daemon server\");\n let conn = shared && !shared.closed ? shared : null;\n if (!conn) {\n connecting ??= connectDaemon().finally(() => { connecting = null; });\n conn = await connecting;\n }\n // \u7BA1\u7406\u8BF7\u6C42\u91CD\u8FDE\u6210\u529F\u540E\uFF0C\u5148\u6062\u590D\u65E5\u5FD7/\u72B6\u6001\u8BA2\u9605\uFF0C\u518D\u8FD4\u56DE\u65B0\u8FDE\u63A5\u3002\n if (subscriptionCount > 0) await conn.subscribe();\n return conn;\n}\n\nasync function connectDaemon(): Promise<DaemonConnection> {\n if (isDaemonServer()) {\n throw new Error(\"ensureDaemon must not run inside daemon server\");\n }\n if (shared && !shared.closed) return shared;\n\n let conn = await tryConnectExisting();\n if (conn) {\n shared = conn;\n return conn;\n }\n\n const existing = readDaemonMeta();\n if (existing && (await isProcessAlive(existing.pid))) {\n /* PID \u4ECD\u5728\u4F46\u6682\u65F6\u8FDE\u4E0D\u4E0A\uFF1A\u77ED\u6682\u91CD\u8BD5\uFF0C\u7981\u6B62\u518D spawn \u7B2C\u4E8C\u5957\u5B88\u62A4\u8FDB\u7A0B */\n const deadline = Date.now() + READY_TIMEOUT_MS;\n while (Date.now() < deadline) {\n await new Promise((r) => setTimeout(r, READY_POLL_MS));\n conn = await tryConnectExisting();\n if (conn) {\n shared = conn;\n return conn;\n }\n }\n throw new Error(`daemon pid ${existing.pid} is alive but pipe is unreachable`);\n }\n\n spawnDaemonProcess();\n const deadline = Date.now() + READY_TIMEOUT_MS;\n while (Date.now() < deadline) {\n await new Promise((r) => setTimeout(r, READY_POLL_MS));\n if (!fs.existsSync(daemonMetaPath())) continue;\n conn = await tryConnectExisting();\n if (conn) {\n shared = conn;\n return conn;\n }\n }\n throw new Error(\"failed to start or connect sfmc daemon\");\n}\n\n/**\n * \u8C03\u7528\u5B88\u62A4\u8FDB\u7A0B\u65B9\u6CD5\uFF1B\u81EA\u52A8 ensureDaemon\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1ACLI \u4FA7 status/start/stop/send/update \u7B49\u8FD0\u7EF4\u547D\u4EE4\u7684\u7EDF\u4E00\u51FA\u53E3\u3002\n */\nexport async function callDaemon(method: DaemonMethod, params?: DaemonParams): Promise<DaemonResult> {\n const conn = await ensureDaemon();\n const token = maintenanceToken(ROOT) ?? process.env.SFMC_MAINTENANCE_TOKEN;\n return conn.call(method, token ? { ...params, maintenanceToken: token } : params);\n}\n\n/**\n * \u786E\u4FDD\u5DF2\u8FDE\u63A5\u5E76\u8BA2\u9605\u8FDC\u7A0B\u65E5\u5FD7/\u72B6\u6001\uFF1B\u8FD4\u56DE\u65AD\u5F00\u51FD\u6570\uFF08\u4E0D\u505C\u5B88\u62A4\u8FDB\u7A0B\uFF09\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1AREPL \u542F\u52A8\u65F6\u6302\u4E0A\u5B9E\u65F6\u65E5\u5FD7\u6D41\uFF0C\u9000\u51FA\u65F6\u53EA disconnect\u3002\n */\nexport async function ensureDaemonSubscription(): Promise<() => void> {\n subscriptionCount++;\n try { await ensureDaemon(); }\n catch (error) { subscriptionCount = Math.max(0, subscriptionCount - 1); throw error; }\n let disposed = false;\n return () => {\n if (disposed) return;\n disposed = true;\n subscriptionCount = Math.max(0, subscriptionCount - 1);\n if (subscriptionCount === 0) shared?.disconnect();\n };\n}\n\n/**\n * \u8BF7\u6C42\u5B88\u62A4\u8FDB\u7A0B stopAll \u540E\u9000\u51FA\uFF08`sfmc daemon stop`\uFF09\u3002\n */\nexport async function shutdownDaemon(): Promise<string> {\n try {\n const conn = await ensureDaemon();\n const token = maintenanceToken(ROOT) ?? process.env.SFMC_MAINTENANCE_TOKEN;\n await conn.call(\"shutdown\", token ? { maintenanceToken: token } : undefined);\n conn.disconnect();\n } catch (e) {\n const meta = readDaemonMeta();\n if (!meta || !await isProcessAlive(meta.pid)) { clearDaemonMeta(); return \"\u5B88\u62A4\u8FDB\u7A0B\u5DF2\u505C\u6B62\"; }\n throw e;\n }\n /* \u77ED\u6682\u7B49\u5F85 meta \u6E05\u7406 */\n const deadline = Date.now() + 5000;\n while (Date.now() < deadline) {\n if (!readDaemonMeta()) break;\n await new Promise((r) => setTimeout(r, 100));\n }\n const remaining = readDaemonMeta();\n if (remaining && await isProcessAlive(remaining.pid)) throw new Error(\"\u5B88\u62A4\u8FDB\u7A0B\u5C1A\u672A\u9000\u51FA\uFF0C\u4FDD\u7559\u8FDE\u63A5\u4FE1\u606F\");\n clearDaemonMeta();\n return \"\";\n}\n\n/**\n * \u4ECE\u5B88\u62A4\u8FDB\u7A0B\u62C9\u53D6\u8FD0\u884C\u6001\u884C\uFF1B\u5931\u8D25\u65F6\u8FD4\u56DE\u7A7A\u6570\u7EC4\u7531\u8C03\u7528\u65B9\u964D\u7EA7\u3002\n */\nexport async function queryRuntimeViaDaemon(): Promise<ServiceStatus[]> {\n const result = await callDaemon(\"status\");\n if (result.kind === \"status\") return result.rows;\n return [];\n}\n\n/**\n * \u4ECE\u5B88\u62A4\u8FDB\u7A0B\u62C9\u53D6\u683C\u5F0F\u5316 status \u6587\u672C\u3002\n */\nexport async function statusTextViaDaemon(): Promise<string> {\n const result = await callDaemon(\"status\");\n if (result.kind === \"status\") return result.text;\n if (result.kind === \"text\") return result.text;\n return \"\";\n}\n\n/**\n * \u901A\u7528\u300C\u8FD4\u56DE text\u300D\u7C7B RPC \u8F85\u52A9\u3002\n */\nexport async function textViaDaemon(method: DaemonMethod, params?: DaemonParams): Promise<string> {\n const result = await callDaemon(method, params);\n if (result.kind === \"text\") return result.text;\n if (result.kind === \"status\") return result.text;\n return \"\";\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["deadline"]
|
|
7
7
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* host.ts — 在登录会话之外启动守护进程
|
|
3
|
+
*
|
|
4
|
+
* 使用场景:桌面端通过 SSH 执行 `manage --stdio`,这条进程只是管理通道。
|
|
5
|
+
* Windows OpenSSH 会在会话结束时关掉作业里的全部进程;systemd 在登出时也会结束本次登录的进程。
|
|
6
|
+
* `detached` 仍留在该范围内,所以守护进程必须由会话外的服务创建(WMI 或 systemd --user)。
|
|
7
|
+
* 会话内启动只作为本机、且没有上述机制时的退路;SSH 会话里启动失败则直接报错,避免断开连接时停服。
|
|
8
|
+
*/
|
|
9
|
+
/** 交给会话外宿主的启动参数。路径都来自当前管理进程已经解析好的运行时。 */
|
|
10
|
+
export interface DaemonHostSpec {
|
|
11
|
+
/** Node 可执行文件绝对路径 */
|
|
12
|
+
node: string;
|
|
13
|
+
/** 守护进程入口(launcher 或 sfmc 主文件) */
|
|
14
|
+
entry: string;
|
|
15
|
+
/** 实例根目录,同时作为工作目录 */
|
|
16
|
+
root: string;
|
|
17
|
+
/** 守护进程标准输出和标准错误追加到这个文件 */
|
|
18
|
+
logPath: string;
|
|
19
|
+
/** 可选的 pnpm 入口,供之后的维护任务使用 */
|
|
20
|
+
pnpm?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* 启动守护进程,并尽量让它活在当前登录会话之外。
|
|
24
|
+
* 使用场景:管理连接第一次发现没有守护进程时调用。已有守护进程不会走到这里。
|
|
25
|
+
*/
|
|
26
|
+
export declare function launchDaemonHost(spec: DaemonHostSpec): void;
|
|
27
|
+
//# sourceMappingURL=host.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../../src/daemon/host.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH,yCAAyC;AACzC,MAAM,WAAW,cAAc;IAC7B,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,2BAA2B;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,6BAA6B;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAkLD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI,CAe3D"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
2
|
+
import { randomBytes, createHash } from "node:crypto";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
function insideSshSession() {
|
|
7
|
+
return Boolean(process.env.SSH_CONNECTION || process.env.SSH_CLIENT);
|
|
8
|
+
}
|
|
9
|
+
function currentUser() {
|
|
10
|
+
if (process.env.USER) return process.env.USER;
|
|
11
|
+
if (process.env.LOGNAME) return process.env.LOGNAME;
|
|
12
|
+
try {
|
|
13
|
+
return os.userInfo().username;
|
|
14
|
+
} catch {
|
|
15
|
+
return "";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function shellQuote(text) {
|
|
19
|
+
return `'${text.replace(/'/g, `'\\''`)}'`;
|
|
20
|
+
}
|
|
21
|
+
function psLiteral(text) {
|
|
22
|
+
return `'${text.replace(/'/g, "''")}'`;
|
|
23
|
+
}
|
|
24
|
+
function carriedEnv(spec, keys) {
|
|
25
|
+
const env = {
|
|
26
|
+
SFMC_ROOT: spec.root,
|
|
27
|
+
SFMC_NODE_BINARY: spec.node,
|
|
28
|
+
SFMC_DAEMON_ENTRY: spec.entry
|
|
29
|
+
};
|
|
30
|
+
if (spec.pnpm) env.SFMC_PNPM_ENTRY = spec.pnpm;
|
|
31
|
+
for (const key of keys) {
|
|
32
|
+
const value = process.env[key];
|
|
33
|
+
if (value) env[key] = value;
|
|
34
|
+
}
|
|
35
|
+
return env;
|
|
36
|
+
}
|
|
37
|
+
function failureText(result) {
|
|
38
|
+
const detail = `${result.stderr || ""}${result.stdout || ""}${result.error?.message || ""}`.trim();
|
|
39
|
+
return detail.slice(-500);
|
|
40
|
+
}
|
|
41
|
+
function launchDetached(spec) {
|
|
42
|
+
const logFd = fs.openSync(spec.logPath, "a");
|
|
43
|
+
const child = spawn(spec.node, [spec.entry, "--daemon"], {
|
|
44
|
+
detached: true,
|
|
45
|
+
windowsHide: true,
|
|
46
|
+
stdio: ["ignore", logFd, logFd],
|
|
47
|
+
cwd: spec.root,
|
|
48
|
+
env: { ...process.env, ...carriedEnv(spec, []) }
|
|
49
|
+
});
|
|
50
|
+
child.unref();
|
|
51
|
+
try {
|
|
52
|
+
fs.closeSync(logFd);
|
|
53
|
+
} catch {
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function launchWindowsOutsideSession(spec) {
|
|
57
|
+
const env = carriedEnv(spec, ["PATH", "PATHEXT", "SystemRoot", "USERNAME", "USERPROFILE", "APPDATA", "LOCALAPPDATA", "TEMP", "TMP"]);
|
|
58
|
+
const assignments = Object.entries(env).map(([key, value]) => `$env:${key} = ${psLiteral(value)}`);
|
|
59
|
+
const log = psLiteral(spec.logPath);
|
|
60
|
+
const script = [
|
|
61
|
+
"$ErrorActionPreference = 'Stop'",
|
|
62
|
+
`$env:SFMC_DAEMON_LOG = ${log}`,
|
|
63
|
+
...assignments,
|
|
64
|
+
"New-Item -ItemType Directory -Force -Path (Split-Path -Parent $env:SFMC_DAEMON_LOG) | Out-Null",
|
|
65
|
+
"Set-Location -LiteralPath $env:SFMC_ROOT",
|
|
66
|
+
// StreamWriter(path, append) 默认 FileShare.Read,会挡住下一次平台校验里的追加打开。
|
|
67
|
+
"$mode = [System.IO.FileMode]::Append",
|
|
68
|
+
"$access = [System.IO.FileAccess]::Write",
|
|
69
|
+
"$share = [System.IO.FileShare]::ReadWrite",
|
|
70
|
+
"$stream = New-Object System.IO.FileStream($env:SFMC_DAEMON_LOG, $mode, $access, $share)",
|
|
71
|
+
"$inner = New-Object System.IO.StreamWriter($stream)",
|
|
72
|
+
"$inner.AutoFlush = $true",
|
|
73
|
+
"$script:writer = [System.IO.TextWriter]::Synchronized($inner)",
|
|
74
|
+
"Remove-Item Env:SFMC_DAEMON_LOG",
|
|
75
|
+
"$psi = New-Object System.Diagnostics.ProcessStartInfo",
|
|
76
|
+
"$psi.FileName = $env:SFMC_NODE_BINARY",
|
|
77
|
+
"$psi.Arguments = ([char]34) + $env:SFMC_DAEMON_ENTRY + ([char]34) + ' --daemon'",
|
|
78
|
+
"$psi.WorkingDirectory = $env:SFMC_ROOT",
|
|
79
|
+
"$psi.UseShellExecute = $false",
|
|
80
|
+
"$psi.CreateNoWindow = $true",
|
|
81
|
+
"$psi.RedirectStandardOutput = $true",
|
|
82
|
+
"$psi.RedirectStandardError = $true",
|
|
83
|
+
"$proc = New-Object System.Diagnostics.Process",
|
|
84
|
+
"$proc.StartInfo = $psi",
|
|
85
|
+
"$onLine = { param($sender, $lineEvent) if ($null -ne $lineEvent.Data) { $script:writer.WriteLine($lineEvent.Data) } }",
|
|
86
|
+
"$proc.add_OutputDataReceived($onLine)",
|
|
87
|
+
"$proc.add_ErrorDataReceived({ param($sender, $lineEvent) if ($null -ne $lineEvent.Data) { $script:writer.WriteLine($lineEvent.Data) } })",
|
|
88
|
+
"[void]$proc.Start()",
|
|
89
|
+
"$proc.BeginOutputReadLine()",
|
|
90
|
+
"$proc.BeginErrorReadLine()",
|
|
91
|
+
"try { $proc.WaitForExit() } finally { if ($script:writer) { $script:writer.Dispose() } }"
|
|
92
|
+
];
|
|
93
|
+
const commandLine = `powershell.exe -NoLogo -NoProfile -NonInteractive -EncodedCommand ${Buffer.from(script.join("\n"), "utf16le").toString("base64")}`;
|
|
94
|
+
const run = (hide) => {
|
|
95
|
+
const startup = hide ? "$startup = New-CimInstance -ClassName Win32_ProcessStartup -ClientOnly -Property @{ ShowWindow = 0 }\n" : "";
|
|
96
|
+
const invoke = [
|
|
97
|
+
"$ErrorActionPreference = 'Stop'",
|
|
98
|
+
startup.trimEnd(),
|
|
99
|
+
`$result = Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{ CommandLine = ${psLiteral(commandLine)}; CurrentDirectory = ${psLiteral(spec.root)}${hide ? "; ProcessStartupInformation = $startup" : ""} }`,
|
|
100
|
+
'if ($result.ReturnValue -ne 0) { Write-Error "Win32_Process.Create $($result.ReturnValue)"; exit 1 }'
|
|
101
|
+
].filter(Boolean).join("\n");
|
|
102
|
+
return spawnSync("powershell.exe", ["-NoLogo", "-NoProfile", "-NonInteractive", "-EncodedCommand", Buffer.from(invoke, "utf16le").toString("base64")], {
|
|
103
|
+
encoding: "utf8",
|
|
104
|
+
timeout: 3e4,
|
|
105
|
+
windowsHide: true
|
|
106
|
+
});
|
|
107
|
+
};
|
|
108
|
+
const hidden = run(true);
|
|
109
|
+
if (hidden.status === 0) return null;
|
|
110
|
+
const visible = run(false);
|
|
111
|
+
if (visible.status === 0) return null;
|
|
112
|
+
return failureText(visible) || failureText(hidden) || "Win32_Process.Create \u5931\u8D25";
|
|
113
|
+
}
|
|
114
|
+
function userBusEnv() {
|
|
115
|
+
const env = { ...process.env, SYSTEMD_ASK_PASSWORD: "0" };
|
|
116
|
+
const uid = process.getuid?.();
|
|
117
|
+
if (uid !== void 0 && !env.XDG_RUNTIME_DIR) env.XDG_RUNTIME_DIR = `/run/user/${uid}`;
|
|
118
|
+
if (env.XDG_RUNTIME_DIR && !env.DBUS_SESSION_BUS_ADDRESS && fs.existsSync(`${env.XDG_RUNTIME_DIR}/bus`)) {
|
|
119
|
+
env.DBUS_SESSION_BUS_ADDRESS = `unix:path=${env.XDG_RUNTIME_DIR}/bus`;
|
|
120
|
+
}
|
|
121
|
+
return env;
|
|
122
|
+
}
|
|
123
|
+
function writePosixHost(spec) {
|
|
124
|
+
const file = path.join(spec.root, ".sfmc", "daemon-host.sh");
|
|
125
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
126
|
+
const exports = Object.entries(carriedEnv(spec, ["PATH", "HOME", "USER", "LANG", "LC_ALL", "TMPDIR"])).map(([key, value]) => `export ${key}=${shellQuote(value)}`);
|
|
127
|
+
const body = [
|
|
128
|
+
"#!/bin/sh",
|
|
129
|
+
"set -eu",
|
|
130
|
+
`cd ${shellQuote(spec.root)}`,
|
|
131
|
+
...exports,
|
|
132
|
+
`exec ${shellQuote(spec.node)} ${shellQuote(spec.entry)} --daemon >> ${shellQuote(spec.logPath)} 2>&1`,
|
|
133
|
+
""
|
|
134
|
+
].join("\n");
|
|
135
|
+
fs.writeFileSync(file, body, { encoding: "utf8", mode: 448 });
|
|
136
|
+
fs.chmodSync(file, 448);
|
|
137
|
+
return file;
|
|
138
|
+
}
|
|
139
|
+
function launchSystemdUser(spec) {
|
|
140
|
+
const user = currentUser();
|
|
141
|
+
if (!user) return "\u65E0\u6CD5\u786E\u5B9A\u5F53\u524D\u7528\u6237\uFF0C\u4E0D\u80FD\u653E\u5230 systemd \u7528\u6237\u670D\u52A1\u91CC";
|
|
142
|
+
const ask = { encoding: "utf8", timeout: 15e3, env: userBusEnv() };
|
|
143
|
+
const enable = spawnSync("loginctl", ["enable-linger", user], ask);
|
|
144
|
+
const show = spawnSync("loginctl", ["show-user", user, "-p", "Linger", "--value"], ask);
|
|
145
|
+
if (show.stdout?.trim() !== "yes") {
|
|
146
|
+
const detail = failureText(enable);
|
|
147
|
+
return `\u7528\u6237 ${user} \u6CA1\u6709 linger\uFF0C\u767B\u51FA\u65F6\u7CFB\u7EDF\u4F1A\u7ED3\u675F\u672C\u6B21\u767B\u5F55\u4E2D\u7684\u8FDB\u7A0B\u3002\u8BF7\u5148\u6267\u884C loginctl enable-linger ${user}\uFF0C\u6216\u5B89\u88C5\u5F00\u673A\u670D\u52A1\u3002${detail ? ` ${detail}` : ""}`;
|
|
148
|
+
}
|
|
149
|
+
const script = writePosixHost(spec);
|
|
150
|
+
const unit = `sfmc-${createHash("sha256").update(spec.root).digest("hex").slice(0, 8)}-${randomBytes(3).toString("hex")}`;
|
|
151
|
+
const result = spawnSync("systemd-run", ["--user", "--collect", `--unit=${unit}`, script], {
|
|
152
|
+
encoding: "utf8",
|
|
153
|
+
timeout: 2e4,
|
|
154
|
+
env: userBusEnv()
|
|
155
|
+
});
|
|
156
|
+
if (result.status === 0) return null;
|
|
157
|
+
return failureText(result) || "systemd-run --user \u5931\u8D25";
|
|
158
|
+
}
|
|
159
|
+
function launchDaemonHost(spec) {
|
|
160
|
+
fs.mkdirSync(path.dirname(spec.logPath), { recursive: true });
|
|
161
|
+
if (process.platform === "win32") {
|
|
162
|
+
const failure = launchWindowsOutsideSession(spec);
|
|
163
|
+
if (!failure) return;
|
|
164
|
+
if (insideSshSession()) throw new Error(`\u65E0\u6CD5\u5728 SSH \u4F1A\u8BDD\u4E4B\u5916\u542F\u52A8\u5B88\u62A4\u8FDB\u7A0B\uFF0C\u5DF2\u53D6\u6D88\u542F\u52A8\uFF0C\u907F\u514D\u65AD\u5F00\u8FDE\u63A5\u65F6\u505C\u670D\u3002${failure}`);
|
|
165
|
+
launchDetached(spec);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (fs.existsSync("/run/systemd/system")) {
|
|
169
|
+
const failure = launchSystemdUser(spec);
|
|
170
|
+
if (!failure) return;
|
|
171
|
+
if (insideSshSession()) throw new Error(`\u65E0\u6CD5\u5728 SSH \u4F1A\u8BDD\u4E4B\u5916\u542F\u52A8\u5B88\u62A4\u8FDB\u7A0B\uFF0C\u5DF2\u53D6\u6D88\u542F\u52A8\uFF0C\u907F\u514D\u65AD\u5F00\u8FDE\u63A5\u65F6\u505C\u670D\u3002${failure}`);
|
|
172
|
+
}
|
|
173
|
+
launchDetached(spec);
|
|
174
|
+
}
|
|
175
|
+
export {
|
|
176
|
+
launchDaemonHost
|
|
177
|
+
};
|
|
178
|
+
//# sourceMappingURL=host.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/daemon/host.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * host.ts \u2014 \u5728\u767B\u5F55\u4F1A\u8BDD\u4E4B\u5916\u542F\u52A8\u5B88\u62A4\u8FDB\u7A0B\n *\n * \u4F7F\u7528\u573A\u666F\uFF1A\u684C\u9762\u7AEF\u901A\u8FC7 SSH \u6267\u884C `manage --stdio`\uFF0C\u8FD9\u6761\u8FDB\u7A0B\u53EA\u662F\u7BA1\u7406\u901A\u9053\u3002\n * Windows OpenSSH \u4F1A\u5728\u4F1A\u8BDD\u7ED3\u675F\u65F6\u5173\u6389\u4F5C\u4E1A\u91CC\u7684\u5168\u90E8\u8FDB\u7A0B\uFF1Bsystemd \u5728\u767B\u51FA\u65F6\u4E5F\u4F1A\u7ED3\u675F\u672C\u6B21\u767B\u5F55\u7684\u8FDB\u7A0B\u3002\n * `detached` \u4ECD\u7559\u5728\u8BE5\u8303\u56F4\u5185\uFF0C\u6240\u4EE5\u5B88\u62A4\u8FDB\u7A0B\u5FC5\u987B\u7531\u4F1A\u8BDD\u5916\u7684\u670D\u52A1\u521B\u5EFA\uFF08WMI \u6216 systemd --user\uFF09\u3002\n * \u4F1A\u8BDD\u5185\u542F\u52A8\u53EA\u4F5C\u4E3A\u672C\u673A\u3001\u4E14\u6CA1\u6709\u4E0A\u8FF0\u673A\u5236\u65F6\u7684\u9000\u8DEF\uFF1BSSH \u4F1A\u8BDD\u91CC\u542F\u52A8\u5931\u8D25\u5219\u76F4\u63A5\u62A5\u9519\uFF0C\u907F\u514D\u65AD\u5F00\u8FDE\u63A5\u65F6\u505C\u670D\u3002\n */\n\nimport { spawn, spawnSync } from \"node:child_process\";\nimport { randomBytes, createHash } from \"node:crypto\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\n\n/** \u4EA4\u7ED9\u4F1A\u8BDD\u5916\u5BBF\u4E3B\u7684\u542F\u52A8\u53C2\u6570\u3002\u8DEF\u5F84\u90FD\u6765\u81EA\u5F53\u524D\u7BA1\u7406\u8FDB\u7A0B\u5DF2\u7ECF\u89E3\u6790\u597D\u7684\u8FD0\u884C\u65F6\u3002 */\nexport interface DaemonHostSpec {\n /** Node \u53EF\u6267\u884C\u6587\u4EF6\u7EDD\u5BF9\u8DEF\u5F84 */\n node: string;\n /** \u5B88\u62A4\u8FDB\u7A0B\u5165\u53E3\uFF08launcher \u6216 sfmc \u4E3B\u6587\u4EF6\uFF09 */\n entry: string;\n /** \u5B9E\u4F8B\u6839\u76EE\u5F55\uFF0C\u540C\u65F6\u4F5C\u4E3A\u5DE5\u4F5C\u76EE\u5F55 */\n root: string;\n /** \u5B88\u62A4\u8FDB\u7A0B\u6807\u51C6\u8F93\u51FA\u548C\u6807\u51C6\u9519\u8BEF\u8FFD\u52A0\u5230\u8FD9\u4E2A\u6587\u4EF6 */\n logPath: string;\n /** \u53EF\u9009\u7684 pnpm \u5165\u53E3\uFF0C\u4F9B\u4E4B\u540E\u7684\u7EF4\u62A4\u4EFB\u52A1\u4F7F\u7528 */\n pnpm?: string;\n}\n\n/** \u5F53\u524D\u8FDB\u7A0B\u662F\u5426\u7531 OpenSSH \u62C9\u8D77\u3002\u8FD9\u79CD\u4F1A\u8BDD\u7ED3\u675F\u65F6\uFF0C\u91CC\u9762\u7684\u8FDB\u7A0B\u4F1A\u88AB\u4E00\u8D77\u56DE\u6536\u3002 */\nfunction insideSshSession(): boolean {\n return Boolean(process.env.SSH_CONNECTION || process.env.SSH_CLIENT);\n}\n\nfunction currentUser(): string {\n if (process.env.USER) return process.env.USER;\n if (process.env.LOGNAME) return process.env.LOGNAME;\n try { return os.userInfo().username; } catch { return \"\"; }\n}\n\nfunction shellQuote(text: string): string {\n return `'${text.replace(/'/g, `'\\\\''`)}'`;\n}\n\nfunction psLiteral(text: string): string {\n return `'${text.replace(/'/g, \"''\")}'`;\n}\n\n/** \u4ECE\u5F53\u524D\u7BA1\u7406\u8FDB\u7A0B\u5E26\u4E0A\u7528\u6237\u73AF\u5883\uFF0C\u4E0D\u5E26 SSH_CONNECTION\uFF0C\u907F\u514D\u5B88\u62A4\u8FDB\u7A0B\u628A\u81EA\u5DF1\u5F53\u6210\u8FD9\u6B21\u767B\u5F55\u3002 */\nfunction carriedEnv(spec: DaemonHostSpec, keys: readonly string[]): Record<string, string> {\n const env: Record<string, string> = {\n SFMC_ROOT: spec.root,\n SFMC_NODE_BINARY: spec.node,\n SFMC_DAEMON_ENTRY: spec.entry,\n };\n if (spec.pnpm) env.SFMC_PNPM_ENTRY = spec.pnpm;\n for (const key of keys) {\n const value = process.env[key];\n if (value) env[key] = value;\n }\n return env;\n}\n\nfunction failureText(result: ReturnType<typeof spawnSync>): string {\n const detail = `${result.stderr || \"\"}${result.stdout || \"\"}${result.error?.message || \"\"}`.trim();\n return detail.slice(-500);\n}\n\n/**\n * \u672C\u673A\u9000\u8DEF\uFF1A\u7236\u8FDB\u7A0B\u9000\u51FA\u540E\u5B50\u8FDB\u7A0B\u8FD8\u80FD\u7559\u4E0B\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A\u6CA1\u6709 SSH \u4F1A\u8BDD\uFF0C\u4E14 WMI / systemd \u7528\u6237\u670D\u52A1\u4E0D\u53EF\u7528\u3002SSH \u91CC\u4E0D\u8D70\u8FD9\u91CC\u3002\n */\nfunction launchDetached(spec: DaemonHostSpec): void {\n const logFd = fs.openSync(spec.logPath, \"a\");\n const child = spawn(spec.node, [spec.entry, \"--daemon\"], {\n detached: true,\n windowsHide: true,\n stdio: [\"ignore\", logFd, logFd],\n cwd: spec.root,\n env: { ...process.env, ...carriedEnv(spec, []) },\n });\n child.unref();\n try { fs.closeSync(logFd); } catch { /* \u65E5\u5FD7\u53E5\u67C4\u5DF2\u4EA4\u7ED9\u5B50\u8FDB\u7A0B */ }\n}\n\n/**\n * \u7531 WMI \u521B\u5EFA\u8FDB\u7A0B\uFF0C\u4F7F\u5B88\u62A4\u8FDB\u7A0B\u4E0D\u5C5E\u4E8E OpenSSH \u7684\u4F5C\u4E1A\u5BF9\u8C61\u3002\n * \u8FD4\u56DE null \u8868\u793A\u5DF2\u7ECF\u542F\u52A8\uFF1B\u8FD4\u56DE\u5B57\u7B26\u4E32\u662F\u5931\u8D25\u539F\u56E0\u3002\n */\nfunction launchWindowsOutsideSession(spec: DaemonHostSpec): string | null {\n const env = carriedEnv(spec, [\"PATH\", \"PATHEXT\", \"SystemRoot\", \"USERNAME\", \"USERPROFILE\", \"APPDATA\", \"LOCALAPPDATA\", \"TEMP\", \"TMP\"]);\n const assignments = Object.entries(env).map(([key, value]) => `$env:${key} = ${psLiteral(value)}`);\n const log = psLiteral(spec.logPath);\n // \u4E8B\u4EF6\u56DE\u8C03\u8DD1\u5728\u522B\u7684\u7EBF\u7A0B\uFF0C\u7528\u811A\u672C\u4F5C\u7528\u57DF\u4FDD\u4F4F\u65E5\u5FD7\u5199\u5165\u5668\u3002\u5165\u53E3\u53C2\u6570\u7528\u5B57\u7B26\u7801\u62FC\u5F15\u53F7\uFF0C\u907F\u514D\u8F6C\u4E49\u628A\u8DEF\u5F84\u622A\u65AD\u3002\n const script = [\n \"$ErrorActionPreference = 'Stop'\",\n `$env:SFMC_DAEMON_LOG = ${log}`,\n ...assignments,\n \"New-Item -ItemType Directory -Force -Path (Split-Path -Parent $env:SFMC_DAEMON_LOG) | Out-Null\",\n \"Set-Location -LiteralPath $env:SFMC_ROOT\",\n // StreamWriter(path, append) \u9ED8\u8BA4 FileShare.Read\uFF0C\u4F1A\u6321\u4F4F\u4E0B\u4E00\u6B21\u5E73\u53F0\u6821\u9A8C\u91CC\u7684\u8FFD\u52A0\u6253\u5F00\u3002\n \"$mode = [System.IO.FileMode]::Append\",\n \"$access = [System.IO.FileAccess]::Write\",\n \"$share = [System.IO.FileShare]::ReadWrite\",\n \"$stream = New-Object System.IO.FileStream($env:SFMC_DAEMON_LOG, $mode, $access, $share)\",\n \"$inner = New-Object System.IO.StreamWriter($stream)\",\n \"$inner.AutoFlush = $true\",\n \"$script:writer = [System.IO.TextWriter]::Synchronized($inner)\",\n \"Remove-Item Env:SFMC_DAEMON_LOG\",\n \"$psi = New-Object System.Diagnostics.ProcessStartInfo\",\n \"$psi.FileName = $env:SFMC_NODE_BINARY\",\n \"$psi.Arguments = ([char]34) + $env:SFMC_DAEMON_ENTRY + ([char]34) + ' --daemon'\",\n \"$psi.WorkingDirectory = $env:SFMC_ROOT\",\n \"$psi.UseShellExecute = $false\",\n \"$psi.CreateNoWindow = $true\",\n \"$psi.RedirectStandardOutput = $true\",\n \"$psi.RedirectStandardError = $true\",\n \"$proc = New-Object System.Diagnostics.Process\",\n \"$proc.StartInfo = $psi\",\n \"$onLine = { param($sender, $lineEvent) if ($null -ne $lineEvent.Data) { $script:writer.WriteLine($lineEvent.Data) } }\",\n \"$proc.add_OutputDataReceived($onLine)\",\n \"$proc.add_ErrorDataReceived({ param($sender, $lineEvent) if ($null -ne $lineEvent.Data) { $script:writer.WriteLine($lineEvent.Data) } })\",\n \"[void]$proc.Start()\",\n \"$proc.BeginOutputReadLine()\",\n \"$proc.BeginErrorReadLine()\",\n \"try { $proc.WaitForExit() } finally { if ($script:writer) { $script:writer.Dispose() } }\",\n ];\n const commandLine = `powershell.exe -NoLogo -NoProfile -NonInteractive -EncodedCommand ${Buffer.from(script.join(\"\\n\"), \"utf16le\").toString(\"base64\")}`;\n const run = (hide: boolean) => {\n const startup = hide\n ? \"$startup = New-CimInstance -ClassName Win32_ProcessStartup -ClientOnly -Property @{ ShowWindow = 0 }\\n\"\n : \"\";\n const invoke = [\n \"$ErrorActionPreference = 'Stop'\",\n startup.trimEnd(),\n `$result = Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{ CommandLine = ${psLiteral(commandLine)}; CurrentDirectory = ${psLiteral(spec.root)}${hide ? \"; ProcessStartupInformation = $startup\" : \"\"} }`,\n \"if ($result.ReturnValue -ne 0) { Write-Error \\\"Win32_Process.Create $($result.ReturnValue)\\\"; exit 1 }\",\n ].filter(Boolean).join(\"\\n\");\n return spawnSync(\"powershell.exe\", [\"-NoLogo\", \"-NoProfile\", \"-NonInteractive\", \"-EncodedCommand\", Buffer.from(invoke, \"utf16le\").toString(\"base64\")], {\n encoding: \"utf8\",\n timeout: 30_000,\n windowsHide: true,\n });\n };\n const hidden = run(true);\n if (hidden.status === 0) return null;\n const visible = run(false);\n if (visible.status === 0) return null;\n return failureText(visible) || failureText(hidden) || \"Win32_Process.Create \u5931\u8D25\";\n}\n\nfunction userBusEnv(): NodeJS.ProcessEnv {\n const env: NodeJS.ProcessEnv = { ...process.env, SYSTEMD_ASK_PASSWORD: \"0\" };\n const uid = process.getuid?.();\n if (uid !== undefined && !env.XDG_RUNTIME_DIR) env.XDG_RUNTIME_DIR = `/run/user/${uid}`;\n if (env.XDG_RUNTIME_DIR && !env.DBUS_SESSION_BUS_ADDRESS && fs.existsSync(`${env.XDG_RUNTIME_DIR}/bus`)) {\n env.DBUS_SESSION_BUS_ADDRESS = `unix:path=${env.XDG_RUNTIME_DIR}/bus`;\n }\n return env;\n}\n\n/** \u5199\u6210\u53EF\u6267\u884C\u811A\u672C\uFF0C\u4EA4\u7ED9 systemd --user\u3002\u811A\u672C\u91CC\u5E26\u4E0A\u542F\u52A8\u5F53\u65F6\u7684\u7528\u6237\u73AF\u5883\u3002 */\nfunction writePosixHost(spec: DaemonHostSpec): string {\n const file = path.join(spec.root, \".sfmc\", \"daemon-host.sh\");\n fs.mkdirSync(path.dirname(file), { recursive: true });\n const exports = Object.entries(carriedEnv(spec, [\"PATH\", \"HOME\", \"USER\", \"LANG\", \"LC_ALL\", \"TMPDIR\"]))\n .map(([key, value]) => `export ${key}=${shellQuote(value)}`);\n const body = [\n \"#!/bin/sh\",\n \"set -eu\",\n `cd ${shellQuote(spec.root)}`,\n ...exports,\n `exec ${shellQuote(spec.node)} ${shellQuote(spec.entry)} --daemon >> ${shellQuote(spec.logPath)} 2>&1`,\n \"\",\n ].join(\"\\n\");\n fs.writeFileSync(file, body, { encoding: \"utf8\", mode: 0o700 });\n fs.chmodSync(file, 0o700);\n return file;\n}\n\n/**\n * \u542F\u7528 linger \u540E\uFF0C\u7528 systemd \u7528\u6237\u670D\u52A1\u542F\u52A8\u3002\u7528\u6237\u670D\u52A1\u4E0D\u5C5E\u4E8E\u8FD9\u6B21 SSH \u4F1A\u8BDD\uFF0C\u767B\u51FA\u4E0D\u4F1A\u628A\u5B83\u505C\u6389\u3002\n * \u8FD4\u56DE null \u8868\u793A\u5DF2\u7ECF\u542F\u52A8\u3002\n */\nfunction launchSystemdUser(spec: DaemonHostSpec): string | null {\n const user = currentUser();\n if (!user) return \"\u65E0\u6CD5\u786E\u5B9A\u5F53\u524D\u7528\u6237\uFF0C\u4E0D\u80FD\u653E\u5230 systemd \u7528\u6237\u670D\u52A1\u91CC\";\n const ask = { encoding: \"utf8\" as const, timeout: 15_000, env: userBusEnv() };\n const enable = spawnSync(\"loginctl\", [\"enable-linger\", user], ask);\n const show = spawnSync(\"loginctl\", [\"show-user\", user, \"-p\", \"Linger\", \"--value\"], ask);\n if (show.stdout?.trim() !== \"yes\") {\n const detail = failureText(enable);\n return `\u7528\u6237 ${user} \u6CA1\u6709 linger\uFF0C\u767B\u51FA\u65F6\u7CFB\u7EDF\u4F1A\u7ED3\u675F\u672C\u6B21\u767B\u5F55\u4E2D\u7684\u8FDB\u7A0B\u3002\u8BF7\u5148\u6267\u884C loginctl enable-linger ${user}\uFF0C\u6216\u5B89\u88C5\u5F00\u673A\u670D\u52A1\u3002${detail ? ` ${detail}` : \"\"}`;\n }\n const script = writePosixHost(spec);\n const unit = `sfmc-${createHash(\"sha256\").update(spec.root).digest(\"hex\").slice(0, 8)}-${randomBytes(3).toString(\"hex\")}`;\n const result = spawnSync(\"systemd-run\", [\"--user\", \"--collect\", `--unit=${unit}`, script], {\n encoding: \"utf8\",\n timeout: 20_000,\n env: userBusEnv(),\n });\n if (result.status === 0) return null;\n return failureText(result) || \"systemd-run --user \u5931\u8D25\";\n}\n\n/**\n * \u542F\u52A8\u5B88\u62A4\u8FDB\u7A0B\uFF0C\u5E76\u5C3D\u91CF\u8BA9\u5B83\u6D3B\u5728\u5F53\u524D\u767B\u5F55\u4F1A\u8BDD\u4E4B\u5916\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A\u7BA1\u7406\u8FDE\u63A5\u7B2C\u4E00\u6B21\u53D1\u73B0\u6CA1\u6709\u5B88\u62A4\u8FDB\u7A0B\u65F6\u8C03\u7528\u3002\u5DF2\u6709\u5B88\u62A4\u8FDB\u7A0B\u4E0D\u4F1A\u8D70\u5230\u8FD9\u91CC\u3002\n */\nexport function launchDaemonHost(spec: DaemonHostSpec): void {\n fs.mkdirSync(path.dirname(spec.logPath), { recursive: true });\n if (process.platform === \"win32\") {\n const failure = launchWindowsOutsideSession(spec);\n if (!failure) return;\n if (insideSshSession()) throw new Error(`\u65E0\u6CD5\u5728 SSH \u4F1A\u8BDD\u4E4B\u5916\u542F\u52A8\u5B88\u62A4\u8FDB\u7A0B\uFF0C\u5DF2\u53D6\u6D88\u542F\u52A8\uFF0C\u907F\u514D\u65AD\u5F00\u8FDE\u63A5\u65F6\u505C\u670D\u3002${failure}`);\n launchDetached(spec);\n return;\n }\n if (fs.existsSync(\"/run/systemd/system\")) {\n const failure = launchSystemdUser(spec);\n if (!failure) return;\n if (insideSshSession()) throw new Error(`\u65E0\u6CD5\u5728 SSH \u4F1A\u8BDD\u4E4B\u5916\u542F\u52A8\u5B88\u62A4\u8FDB\u7A0B\uFF0C\u5DF2\u53D6\u6D88\u542F\u52A8\uFF0C\u907F\u514D\u65AD\u5F00\u8FDE\u63A5\u65F6\u505C\u670D\u3002${failure}`);\n }\n launchDetached(spec);\n}\n"],
|
|
5
|
+
"mappings": "AASA,SAAS,OAAO,iBAAiB;AACjC,SAAS,aAAa,kBAAkB;AACxC,OAAO,QAAQ;AACf,OAAO,QAAQ;AACf,OAAO,UAAU;AAiBjB,SAAS,mBAA4B;AACnC,SAAO,QAAQ,QAAQ,IAAI,kBAAkB,QAAQ,IAAI,UAAU;AACrE;AAEA,SAAS,cAAsB;AAC7B,MAAI,QAAQ,IAAI,KAAM,QAAO,QAAQ,IAAI;AACzC,MAAI,QAAQ,IAAI,QAAS,QAAO,QAAQ,IAAI;AAC5C,MAAI;AAAE,WAAO,GAAG,SAAS,EAAE;AAAA,EAAU,QAAQ;AAAE,WAAO;AAAA,EAAI;AAC5D;AAEA,SAAS,WAAW,MAAsB;AACxC,SAAO,IAAI,KAAK,QAAQ,MAAM,OAAO,CAAC;AACxC;AAEA,SAAS,UAAU,MAAsB;AACvC,SAAO,IAAI,KAAK,QAAQ,MAAM,IAAI,CAAC;AACrC;AAGA,SAAS,WAAW,MAAsB,MAAiD;AACzF,QAAM,MAA8B;AAAA,IAClC,WAAW,KAAK;AAAA,IAChB,kBAAkB,KAAK;AAAA,IACvB,mBAAmB,KAAK;AAAA,EAC1B;AACA,MAAI,KAAK,KAAM,KAAI,kBAAkB,KAAK;AAC1C,aAAW,OAAO,MAAM;AACtB,UAAM,QAAQ,QAAQ,IAAI,GAAG;AAC7B,QAAI,MAAO,KAAI,GAAG,IAAI;AAAA,EACxB;AACA,SAAO;AACT;AAEA,SAAS,YAAY,QAA8C;AACjE,QAAM,SAAS,GAAG,OAAO,UAAU,EAAE,GAAG,OAAO,UAAU,EAAE,GAAG,OAAO,OAAO,WAAW,EAAE,GAAG,KAAK;AACjG,SAAO,OAAO,MAAM,IAAI;AAC1B;AAMA,SAAS,eAAe,MAA4B;AAClD,QAAM,QAAQ,GAAG,SAAS,KAAK,SAAS,GAAG;AAC3C,QAAM,QAAQ,MAAM,KAAK,MAAM,CAAC,KAAK,OAAO,UAAU,GAAG;AAAA,IACvD,UAAU;AAAA,IACV,aAAa;AAAA,IACb,OAAO,CAAC,UAAU,OAAO,KAAK;AAAA,IAC9B,KAAK,KAAK;AAAA,IACV,KAAK,EAAE,GAAG,QAAQ,KAAK,GAAG,WAAW,MAAM,CAAC,CAAC,EAAE;AAAA,EACjD,CAAC;AACD,QAAM,MAAM;AACZ,MAAI;AAAE,OAAG,UAAU,KAAK;AAAA,EAAG,QAAQ;AAAA,EAAmB;AACxD;AAMA,SAAS,4BAA4B,MAAqC;AACxE,QAAM,MAAM,WAAW,MAAM,CAAC,QAAQ,WAAW,cAAc,YAAY,eAAe,WAAW,gBAAgB,QAAQ,KAAK,CAAC;AACnI,QAAM,cAAc,OAAO,QAAQ,GAAG,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,QAAQ,GAAG,MAAM,UAAU,KAAK,CAAC,EAAE;AACjG,QAAM,MAAM,UAAU,KAAK,OAAO;AAElC,QAAM,SAAS;AAAA,IACb;AAAA,IACA,0BAA0B,GAAG;AAAA,IAC7B,GAAG;AAAA,IACH;AAAA,IACA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,QAAM,cAAc,qEAAqE,OAAO,KAAK,OAAO,KAAK,IAAI,GAAG,SAAS,EAAE,SAAS,QAAQ,CAAC;AACrJ,QAAM,MAAM,CAAC,SAAkB;AAC7B,UAAM,UAAU,OACZ,2GACA;AACJ,UAAM,SAAS;AAAA,MACb;AAAA,MACA,QAAQ,QAAQ;AAAA,MAChB,sGAAsG,UAAU,WAAW,CAAC,wBAAwB,UAAU,KAAK,IAAI,CAAC,GAAG,OAAO,2CAA2C,EAAE;AAAA,MAC/N;AAAA,IACF,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI;AAC3B,WAAO,UAAU,kBAAkB,CAAC,WAAW,cAAc,mBAAmB,mBAAmB,OAAO,KAAK,QAAQ,SAAS,EAAE,SAAS,QAAQ,CAAC,GAAG;AAAA,MACrJ,UAAU;AAAA,MACV,SAAS;AAAA,MACT,aAAa;AAAA,IACf,CAAC;AAAA,EACH;AACA,QAAM,SAAS,IAAI,IAAI;AACvB,MAAI,OAAO,WAAW,EAAG,QAAO;AAChC,QAAM,UAAU,IAAI,KAAK;AACzB,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,SAAO,YAAY,OAAO,KAAK,YAAY,MAAM,KAAK;AACxD;AAEA,SAAS,aAAgC;AACvC,QAAM,MAAyB,EAAE,GAAG,QAAQ,KAAK,sBAAsB,IAAI;AAC3E,QAAM,MAAM,QAAQ,SAAS;AAC7B,MAAI,QAAQ,UAAa,CAAC,IAAI,gBAAiB,KAAI,kBAAkB,aAAa,GAAG;AACrF,MAAI,IAAI,mBAAmB,CAAC,IAAI,4BAA4B,GAAG,WAAW,GAAG,IAAI,eAAe,MAAM,GAAG;AACvG,QAAI,2BAA2B,aAAa,IAAI,eAAe;AAAA,EACjE;AACA,SAAO;AACT;AAGA,SAAS,eAAe,MAA8B;AACpD,QAAM,OAAO,KAAK,KAAK,KAAK,MAAM,SAAS,gBAAgB;AAC3D,KAAG,UAAU,KAAK,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AACpD,QAAM,UAAU,OAAO,QAAQ,WAAW,MAAM,CAAC,QAAQ,QAAQ,QAAQ,QAAQ,UAAU,QAAQ,CAAC,CAAC,EAClG,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,UAAU,GAAG,IAAI,WAAW,KAAK,CAAC,EAAE;AAC7D,QAAM,OAAO;AAAA,IACX;AAAA,IACA;AAAA,IACA,MAAM,WAAW,KAAK,IAAI,CAAC;AAAA,IAC3B,GAAG;AAAA,IACH,QAAQ,WAAW,KAAK,IAAI,CAAC,IAAI,WAAW,KAAK,KAAK,CAAC,gBAAgB,WAAW,KAAK,OAAO,CAAC;AAAA,IAC/F;AAAA,EACF,EAAE,KAAK,IAAI;AACX,KAAG,cAAc,MAAM,MAAM,EAAE,UAAU,QAAQ,MAAM,IAAM,CAAC;AAC9D,KAAG,UAAU,MAAM,GAAK;AACxB,SAAO;AACT;AAMA,SAAS,kBAAkB,MAAqC;AAC9D,QAAM,OAAO,YAAY;AACzB,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,MAAM,EAAE,UAAU,QAAiB,SAAS,MAAQ,KAAK,WAAW,EAAE;AAC5E,QAAM,SAAS,UAAU,YAAY,CAAC,iBAAiB,IAAI,GAAG,GAAG;AACjE,QAAM,OAAO,UAAU,YAAY,CAAC,aAAa,MAAM,MAAM,UAAU,SAAS,GAAG,GAAG;AACtF,MAAI,KAAK,QAAQ,KAAK,MAAM,OAAO;AACjC,UAAM,SAAS,YAAY,MAAM;AACjC,WAAO,gBAAM,IAAI,mLAA2D,IAAI,yDAAY,SAAS,IAAI,MAAM,KAAK,EAAE;AAAA,EACxH;AACA,QAAM,SAAS,eAAe,IAAI;AAClC,QAAM,OAAO,QAAQ,WAAW,QAAQ,EAAE,OAAO,KAAK,IAAI,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,YAAY,CAAC,EAAE,SAAS,KAAK,CAAC;AACvH,QAAM,SAAS,UAAU,eAAe,CAAC,UAAU,aAAa,UAAU,IAAI,IAAI,MAAM,GAAG;AAAA,IACzF,UAAU;AAAA,IACV,SAAS;AAAA,IACT,KAAK,WAAW;AAAA,EAClB,CAAC;AACD,MAAI,OAAO,WAAW,EAAG,QAAO;AAChC,SAAO,YAAY,MAAM,KAAK;AAChC;AAMO,SAAS,iBAAiB,MAA4B;AAC3D,KAAG,UAAU,KAAK,QAAQ,KAAK,OAAO,GAAG,EAAE,WAAW,KAAK,CAAC;AAC5D,MAAI,QAAQ,aAAa,SAAS;AAChC,UAAM,UAAU,4BAA4B,IAAI;AAChD,QAAI,CAAC,QAAS;AACd,QAAI,iBAAiB,EAAG,OAAM,IAAI,MAAM,4LAAsC,OAAO,EAAE;AACvF,mBAAe,IAAI;AACnB;AAAA,EACF;AACA,MAAI,GAAG,WAAW,qBAAqB,GAAG;AACxC,UAAM,UAAU,kBAAkB,IAAI;AACtC,QAAI,CAAC,QAAS;AACd,QAAI,iBAAiB,EAAG,OAAM,IAAI,MAAM,4LAAsC,OAAO,EAAE;AAAA,EACzF;AACA,iBAAe,IAAI;AACrB;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/daemon/server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA2MH;;;GAGG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/daemon/server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA2MH;;;GAGG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CA2GrD"}
|
package/dist/daemon/server.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/daemon/server.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * server.ts \u2014 \u5B88\u62A4\u8FDB\u7A0B\u670D\u52A1\u7AEF\uFF1A\u672C\u673A\u7BA1\u9053 listen + \u5C06 RPC \u5206\u53D1\u7ED9\u73B0\u6709\u76D1\u7BA1\u903B\u8F91\n *\n * \u4EC5\u5728 `sfmc --daemon` \u8FDB\u7A0B\u5185\u8FD0\u884C\uFF1B\u6301\u6709 Service \u5B50\u8FDB\u7A0B\u3001\u81EA\u52A8\u91CD\u542F\u4E0E BDS stdin\u3002\n * CLI \u9000\u51FA\u4E0D\u65AD\u5F00\u672C\u8FDB\u7A0B\uFF1B`shutdown` \u65B9\u6CD5\u4F1A\u5148 stopAll \u518D\u9000\u51FA\u3002\n */\n\nimport net from \"node:net\";\nimport process from \"node:process\";\nimport {\n cmdRestart,\n cmdSend,\n cmdStart,\n cmdStartAll,\n cmdStatus,\n cmdStop,\n cmdStopAll,\n cmdUpdate,\n} from \"../commands.js\";\nimport { onLog } from \"../logs.js\";\nimport { onServiceStateChange, queryServicesRuntimeLocal, stopAll } from \"../services.js\";\nimport { startAll } from \"../services.js\";\nimport {\n clearDaemonMeta,\n createDaemonToken,\n daemonPipePath,\n writeDaemonMeta,\n type DaemonMeta,\n} from \"./paths.js\";\nimport type { DaemonEvent, DaemonRequest, DaemonResponse, DaemonResult } from \"./protocol.js\";\nimport { toLogPayload } from \"./protocol.js\";\nimport { markDaemonServer } from \"./role.js\";\nimport { dispatchManagement, managementTasks, onManagementEvent } from \"../management/server.js\";\nimport { toManagementError } from \"@sfmc-bds/management\";\nimport { isMaintenanceLocked, withMaintenanceLock } from \"@sfmc-bds/management/node\";\nimport { ROOT } from \"../runtime.js\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { stopForMaintenance } from \"../management/maintenance.js\";\nimport { START_ORDER } from \"../services.js\";\nimport { readDaemonMeta } from \"./paths.js\";\nimport { isProcessAlive } from \"@sfmc-bds/bds-tools/process-probe\";\nimport { randomUUID } from \"node:crypto\";\n\n/** \u5F53\u524D\u5DF2\u8BA2\u9605\u4E8B\u4EF6\u63A8\u9001\u7684 socket \u96C6\u5408 */\nconst subscribers = new Set<net.Socket>();\n\n/** \u662F\u5426\u5DF2\u5F00\u59CB\u4F18\u96C5\u9000\u51FA\uFF08\u907F\u514D\u91CD\u590D shutdown\uFF09 */\nlet shuttingDown = false;\n\n/**\n * \u5411\u6240\u6709\u8BA2\u9605\u8005\u5E7F\u64AD\u4E8B\u4EF6\u5E27\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A\u670D\u52A1\u65E5\u5FD7\u4E0E\u542F\u505C\u72B6\u6001\u9700\u5B9E\u65F6\u51FA\u73B0\u5728\u5DF2\u8FDE\u63A5\u7684 REPL\u3002\n */\nfunction broadcast(event: DaemonEvent): void {\n const line = JSON.stringify(event) + \"\\n\";\n for (const sock of subscribers) {\n if (sock.destroyed) {\n subscribers.delete(sock);\n continue;\n }\n if (sock.writableLength > 4 * 1024 * 1024) { sock.destroy(); subscribers.delete(sock); continue; }\n try {\n sock.write(line);\n } catch {\n subscribers.delete(sock);\n }\n }\n}\n\nfunction writeRes(sock: net.Socket, res: DaemonResponse): void {\n if (sock.destroyed) return;\n try {\n sock.write(JSON.stringify(res) + \"\\n\");\n } catch {\n /* ignore */\n }\n}\n\n/**\n * \u5206\u53D1\u5355\u6761 RPC \u5230\u73B0\u6709 cmd* / services API\uFF08\u672C\u8FDB\u7A0B\u5DF2\u662F daemon\uFF0Ccmd* \u8D70\u672C\u5730\u5B9E\u73B0\uFF09\u3002\n */\nasync function dispatch(req: DaemonRequest): Promise<DaemonResult> {\n const mutable = [\"start\", \"stop\", \"restart\", \"startAll\", \"stopAll\", \"send\", \"update\", \"shutdown\", \"maintenanceStop\"].includes(req.method);\n if (mutable) {\n const owner = fs.existsSync(path.join(ROOT, \".sfmc\", \"maintenance.lock\")) ? JSON.parse(fs.readFileSync(path.join(ROOT, \".sfmc\", \"maintenance.lock\"), \"utf8\")) as { nonce?: string } : null;\n if (!req.params?.maintenanceToken || req.params.maintenanceToken !== owner?.nonce) {\n return withMaintenanceLock(ROOT, () => dispatchUnlocked(req));\n }\n }\n return dispatchUnlocked(req);\n}\nasync function dispatchUnlocked(req: DaemonRequest): Promise<DaemonResult> {\n switch (req.method) {\n case \"maintenanceStop\": {\n await stopForMaintenance([...START_ORDER], () => {});\n return { kind: \"status\", text: \"\", rows: await queryServicesRuntimeLocal() };\n }\n case \"management\": {\n const request = req.params?.request;\n if (!request || typeof request.id !== \"string\" || typeof request.method !== \"string\") throw new Error(\"invalid management request\");\n try { return { kind: \"management\", response: { type: \"res\", id: request.id, ok: true, result: await dispatchManagement(request) } }; }\n catch (error) { return { kind: \"management\", response: { type: \"res\", id: request.id, ok: false, error: toManagementError(error) } }; }\n }\n case \"ping\":\n return { kind: \"pong\" };\n case \"status\": {\n const text = await cmdStatus();\n const rows = await queryServicesRuntimeLocal();\n return { kind: \"status\", text, rows };\n }\n case \"start\": {\n const name = req.params?.name;\n if (!name) throw new Error(\"missing params.name\");\n if (name === \"all\" || name === \"-all\" || name === \"--all\") {\n return { kind: \"text\", text: await cmdStartAll() };\n }\n return { kind: \"text\", text: await cmdStart(name) };\n }\n case \"stop\": {\n const name = req.params?.name;\n if (!name) throw new Error(\"missing params.name\");\n if (name === \"all\" || name === \"-all\" || name === \"--all\") {\n return { kind: \"text\", text: await cmdStopAll() };\n }\n return { kind: \"text\", text: await cmdStop(name) };\n }\n case \"restart\": {\n const name = req.params?.name;\n if (!name) throw new Error(\"missing params.name\");\n if (name === \"all\" || name === \"-all\" || name === \"--all\") {\n await cmdStopAll();\n return { kind: \"text\", text: await cmdStartAll() };\n }\n return { kind: \"text\", text: await cmdRestart(name) };\n }\n case \"startAll\":\n return { kind: \"text\", text: await cmdStartAll() };\n case \"stopAll\":\n return { kind: \"text\", text: await cmdStopAll() };\n case \"send\": {\n const name = req.params?.name;\n const message = req.params?.message;\n if (!name || message === undefined) throw new Error(\"missing params.name/message\");\n return { kind: \"text\", text: await cmdSend(name, message) };\n }\n case \"update\":\n return { kind: \"text\", text: await cmdUpdate(req.params?.args ?? []) };\n case \"subscribe\":\n return { kind: \"subscribed\" };\n case \"shutdown\": {\n if (!shuttingDown) {\n shuttingDown = true;\n try { await stopForMaintenance([...START_ORDER], () => {}); }\n catch (error) { shuttingDown = false; throw error; }\n setTimeout(() => {\n clearDaemonMeta();\n process.exit(0);\n }, 50).unref();\n }\n return { kind: \"shutdown\" };\n }\n default:\n throw new Error(`unknown method: ${(req as DaemonRequest).method}`);\n }\n}\n\n/**\n * \u5904\u7406\u5355\u6761\u8FDE\u63A5\u4E0A\u7684\u8BF7\u6C42\u884C\uFF1B\u9274\u6743\u5931\u8D25\u5219\u5173\u95ED\u3002\n */\nasync function handleLine(sock: net.Socket, line: string, expectedToken: string): Promise<void> {\n let req: DaemonRequest;\n try {\n req = JSON.parse(line) as DaemonRequest;\n } catch {\n writeRes(sock, { type: \"res\", id: \"?\", ok: false, error: \"invalid json\" });\n return;\n }\n if (!req?.id || !req.method) {\n writeRes(sock, { type: \"res\", id: req?.id ?? \"?\", ok: false, error: \"invalid request\" });\n return;\n }\n if (req.token !== expectedToken) {\n writeRes(sock, { type: \"res\", id: req.id, ok: false, error: \"unauthorized\" });\n sock.end();\n return;\n }\n\n if (req.method === \"subscribe\") {\n subscribers.add(sock);\n sock.once(\"close\", () => subscribers.delete(sock));\n writeRes(sock, { type: \"res\", id: req.id, ok: true, result: { kind: \"subscribed\" } });\n return;\n }\n\n try {\n const result = await dispatch(req);\n writeRes(sock, { type: \"res\", id: req.id, ok: true, result });\n } catch (e) {\n writeRes(sock, {\n type: \"res\",\n id: req.id,\n ok: false,\n error: e instanceof Error ? e.message : String(e),\n });\n }\n}\n\n/**\n * \u542F\u52A8\u5B88\u62A4\u8FDB\u7A0B\uFF1A\u6807\u8BB0\u89D2\u8272\u3001listen \u7BA1\u9053\u3001\u843D\u76D8 daemon.json\uFF0C\u5E76\u8F6C\u53D1\u65E5\u5FD7/\u72B6\u6001\u4E8B\u4EF6\u3002\n * \u672C\u51FD\u6570\u5728\u6210\u529F listen \u540E\u6C38\u4E0D\u8FD4\u56DE\uFF08\u8FDB\u7A0B\u5E38\u9A7B\uFF09\u3002\n */\nexport async function runDaemonServer(): Promise<void> {\n const startLock = path.join(ROOT, \".sfmc\", \"daemon-start.lock\");\n fs.mkdirSync(path.dirname(startLock), { recursive: true });\n const nonce = randomUUID();\n let handle: number;\n try { handle = fs.openSync(startLock, \"wx\", 0o600); }\n catch (error) { if ((error as NodeJS.ErrnoException).code === \"EEXIST\") throw new Error(\"\u5B88\u62A4\u8FDB\u7A0B\u6B63\u5728\u542F\u52A8\u6216\u542F\u52A8\u9501\u9057\u7559\uFF1B\u4E0D\u4F1A\u521B\u5EFA\u7B2C\u4E8C\u5957\u540E\u53F0\uFF0C\u8BF7\u68C0\u67E5 daemon-start.lock\"); throw error; }\n fs.writeFileSync(handle, JSON.stringify({ pid: process.pid, nonce })); fs.closeSync(handle);\n const releaseStartup = () => { try { const owner = JSON.parse(fs.readFileSync(startLock, \"utf8\")) as { nonce: string }; if (owner.nonce === nonce) fs.unlinkSync(startLock); } catch { /* \u5DF2\u91CA\u653E */ } };\n process.once(\"exit\", releaseStartup);\n const previous = readDaemonMeta();\n if (previous && await isProcessAlive(previous.pid)) { releaseStartup(); process.exit(0); }\n if (previous) clearDaemonMeta();\n markDaemonServer();\n const unsubManagement = onManagementEvent(event => broadcast({ type: \"event\", event: \"management\", payload: event }));\n\n const token = createDaemonToken();\n const pipe = daemonPipePath();\n const meta: DaemonMeta = {\n pid: process.pid,\n pipe,\n token,\n startedAt: new Date().toISOString(),\n };\n\n const unsubLog = onLog((log) => {\n broadcast({ type: \"event\", event: \"log\", payload: toLogPayload(log) });\n });\n const unsubState = onServiceStateChange((ev) => {\n broadcast({ type: \"event\", event: \"state\", payload: ev });\n });\n\n const server = net.createServer((sock) => {\n let buf = \"\";\n sock.setEncoding(\"utf8\");\n sock.on(\"data\", (chunk: string) => {\n buf += chunk;\n if (Buffer.byteLength(buf) > 8 * 1024 * 1024) { sock.destroy(); return; }\n let idx: number;\n while ((idx = buf.indexOf(\"\\n\")) >= 0) {\n const line = buf.slice(0, idx).trim();\n buf = buf.slice(idx + 1);\n if (!line) continue;\n void handleLine(sock, line, token);\n }\n });\n sock.on(\"error\", () => {\n subscribers.delete(sock);\n });\n sock.on(\"close\", () => {\n subscribers.delete(sock);\n });\n });\n\n await new Promise<void>((resolve, reject) => {\n const onErr = (err: NodeJS.ErrnoException): void => {\n /* \u53E6\u4E00\u5B9E\u4F8B\u5DF2\u5360\u7528\u7BA1\u9053\uFF1A\u5B89\u9759\u9000\u51FA\uFF0C\u907F\u514D\u53CC\u5B88\u62A4\u8FDB\u7A0B */\n if (err.code === \"EADDRINUSE\") {\n process.exit(0);\n }\n reject(err);\n };\n server.once(\"error\", onErr);\n server.listen(pipe, () => {\n server.off(\"error\", onErr);\n resolve();\n });\n });\n\n writeDaemonMeta(meta);\n releaseStartup();\n // \u542F\u52A8\u65F6\u5C31\u6838\u5BF9\u9057\u7559\u4EFB\u52A1\u3002\u7EF4\u62A4\u8FDB\u7A0B\u88AB\u6740\u6389\u540E\uFF0C\u684C\u9762\u4E0D\u7528\u5148\u53D1\u8BF7\u6C42\u4E5F\u80FD\u770B\u5230\u300C\u5DF2\u4E2D\u65AD\u300D\u3002\n managementTasks();\n if (process.argv.includes(\"--autostart\") && !isMaintenanceLocked(ROOT)) await startAll();\n\n const cleanup = (): void => {\n unsubManagement();\n unsubLog();\n unsubState();\n try {\n server.close();\n } catch {\n /* ignore */\n }\n clearDaemonMeta();\n };\n\n process.on(\"exit\", cleanup);\n process.on(\"SIGTERM\", () => {\n if (shuttingDown) return;\n shuttingDown = true;\n void stopAll().finally(() => {\n cleanup();\n process.exit(0);\n });\n });\n /* \u5B88\u62A4\u8FDB\u7A0B\u5FFD\u7565 SIGINT\uFF1A\u8131\u79BB\u7EC8\u7AEF\u540E\u4E0D\u5E94\u56E0\u4FE1\u53F7\u505C\u670D\uFF1B\u663E\u5F0F shutdown \u9000\u51FA */\n process.on(\"SIGINT\", () => {\n /* no-op */\n });\n\n await new Promise<never>(() => {\n /* park forever */\n });\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/**\n * server.ts \u2014 \u5B88\u62A4\u8FDB\u7A0B\u670D\u52A1\u7AEF\uFF1A\u672C\u673A\u7BA1\u9053 listen + \u5C06 RPC \u5206\u53D1\u7ED9\u73B0\u6709\u76D1\u7BA1\u903B\u8F91\n *\n * \u4EC5\u5728 `sfmc --daemon` \u8FDB\u7A0B\u5185\u8FD0\u884C\uFF1B\u6301\u6709 Service \u5B50\u8FDB\u7A0B\u3001\u81EA\u52A8\u91CD\u542F\u4E0E BDS stdin\u3002\n * CLI \u9000\u51FA\u4E0D\u65AD\u5F00\u672C\u8FDB\u7A0B\uFF1B`shutdown` \u65B9\u6CD5\u4F1A\u5148 stopAll \u518D\u9000\u51FA\u3002\n */\n\nimport net from \"node:net\";\nimport process from \"node:process\";\nimport {\n cmdRestart,\n cmdSend,\n cmdStart,\n cmdStartAll,\n cmdStatus,\n cmdStop,\n cmdStopAll,\n cmdUpdate,\n} from \"../commands.js\";\nimport { onLog } from \"../logs.js\";\nimport { onServiceStateChange, queryServicesRuntimeLocal, stopAll } from \"../services.js\";\nimport { startAll } from \"../services.js\";\nimport {\n clearDaemonMeta,\n createDaemonToken,\n daemonPipePath,\n writeDaemonMeta,\n type DaemonMeta,\n} from \"./paths.js\";\nimport type { DaemonEvent, DaemonRequest, DaemonResponse, DaemonResult } from \"./protocol.js\";\nimport { toLogPayload } from \"./protocol.js\";\nimport { markDaemonServer } from \"./role.js\";\nimport { dispatchManagement, managementTasks, onManagementEvent } from \"../management/server.js\";\nimport { toManagementError } from \"@sfmc-bds/management\";\nimport { isMaintenanceLocked, withMaintenanceLock } from \"@sfmc-bds/management/node\";\nimport { ROOT } from \"../runtime.js\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { stopForMaintenance } from \"../management/maintenance.js\";\nimport { START_ORDER } from \"../services.js\";\nimport { readDaemonMeta } from \"./paths.js\";\nimport { isProcessAlive } from \"@sfmc-bds/bds-tools/process-probe\";\nimport { randomUUID } from \"node:crypto\";\n\n/** \u5F53\u524D\u5DF2\u8BA2\u9605\u4E8B\u4EF6\u63A8\u9001\u7684 socket \u96C6\u5408 */\nconst subscribers = new Set<net.Socket>();\n\n/** \u662F\u5426\u5DF2\u5F00\u59CB\u4F18\u96C5\u9000\u51FA\uFF08\u907F\u514D\u91CD\u590D shutdown\uFF09 */\nlet shuttingDown = false;\n\n/**\n * \u5411\u6240\u6709\u8BA2\u9605\u8005\u5E7F\u64AD\u4E8B\u4EF6\u5E27\u3002\n * \u4F7F\u7528\u573A\u666F\uFF1A\u670D\u52A1\u65E5\u5FD7\u4E0E\u542F\u505C\u72B6\u6001\u9700\u5B9E\u65F6\u51FA\u73B0\u5728\u5DF2\u8FDE\u63A5\u7684 REPL\u3002\n */\nfunction broadcast(event: DaemonEvent): void {\n const line = JSON.stringify(event) + \"\\n\";\n for (const sock of subscribers) {\n if (sock.destroyed) {\n subscribers.delete(sock);\n continue;\n }\n if (sock.writableLength > 4 * 1024 * 1024) { sock.destroy(); subscribers.delete(sock); continue; }\n try {\n sock.write(line);\n } catch {\n subscribers.delete(sock);\n }\n }\n}\n\nfunction writeRes(sock: net.Socket, res: DaemonResponse): void {\n if (sock.destroyed) return;\n try {\n sock.write(JSON.stringify(res) + \"\\n\");\n } catch {\n /* ignore */\n }\n}\n\n/**\n * \u5206\u53D1\u5355\u6761 RPC \u5230\u73B0\u6709 cmd* / services API\uFF08\u672C\u8FDB\u7A0B\u5DF2\u662F daemon\uFF0Ccmd* \u8D70\u672C\u5730\u5B9E\u73B0\uFF09\u3002\n */\nasync function dispatch(req: DaemonRequest): Promise<DaemonResult> {\n const mutable = [\"start\", \"stop\", \"restart\", \"startAll\", \"stopAll\", \"send\", \"update\", \"shutdown\", \"maintenanceStop\"].includes(req.method);\n if (mutable) {\n const owner = fs.existsSync(path.join(ROOT, \".sfmc\", \"maintenance.lock\")) ? JSON.parse(fs.readFileSync(path.join(ROOT, \".sfmc\", \"maintenance.lock\"), \"utf8\")) as { nonce?: string } : null;\n if (!req.params?.maintenanceToken || req.params.maintenanceToken !== owner?.nonce) {\n return withMaintenanceLock(ROOT, () => dispatchUnlocked(req));\n }\n }\n return dispatchUnlocked(req);\n}\nasync function dispatchUnlocked(req: DaemonRequest): Promise<DaemonResult> {\n switch (req.method) {\n case \"maintenanceStop\": {\n await stopForMaintenance([...START_ORDER], () => {});\n return { kind: \"status\", text: \"\", rows: await queryServicesRuntimeLocal() };\n }\n case \"management\": {\n const request = req.params?.request;\n if (!request || typeof request.id !== \"string\" || typeof request.method !== \"string\") throw new Error(\"invalid management request\");\n try { return { kind: \"management\", response: { type: \"res\", id: request.id, ok: true, result: await dispatchManagement(request) } }; }\n catch (error) { return { kind: \"management\", response: { type: \"res\", id: request.id, ok: false, error: toManagementError(error) } }; }\n }\n case \"ping\":\n return { kind: \"pong\" };\n case \"status\": {\n const text = await cmdStatus();\n const rows = await queryServicesRuntimeLocal();\n return { kind: \"status\", text, rows };\n }\n case \"start\": {\n const name = req.params?.name;\n if (!name) throw new Error(\"missing params.name\");\n if (name === \"all\" || name === \"-all\" || name === \"--all\") {\n return { kind: \"text\", text: await cmdStartAll() };\n }\n return { kind: \"text\", text: await cmdStart(name) };\n }\n case \"stop\": {\n const name = req.params?.name;\n if (!name) throw new Error(\"missing params.name\");\n if (name === \"all\" || name === \"-all\" || name === \"--all\") {\n return { kind: \"text\", text: await cmdStopAll() };\n }\n return { kind: \"text\", text: await cmdStop(name) };\n }\n case \"restart\": {\n const name = req.params?.name;\n if (!name) throw new Error(\"missing params.name\");\n if (name === \"all\" || name === \"-all\" || name === \"--all\") {\n await cmdStopAll();\n return { kind: \"text\", text: await cmdStartAll() };\n }\n return { kind: \"text\", text: await cmdRestart(name) };\n }\n case \"startAll\":\n return { kind: \"text\", text: await cmdStartAll() };\n case \"stopAll\":\n return { kind: \"text\", text: await cmdStopAll() };\n case \"send\": {\n const name = req.params?.name;\n const message = req.params?.message;\n if (!name || message === undefined) throw new Error(\"missing params.name/message\");\n return { kind: \"text\", text: await cmdSend(name, message) };\n }\n case \"update\":\n return { kind: \"text\", text: await cmdUpdate(req.params?.args ?? []) };\n case \"subscribe\":\n return { kind: \"subscribed\" };\n case \"shutdown\": {\n if (!shuttingDown) {\n shuttingDown = true;\n try { await stopForMaintenance([...START_ORDER], () => {}); }\n catch (error) { shuttingDown = false; throw error; }\n setTimeout(() => {\n clearDaemonMeta();\n process.exit(0);\n }, 50).unref();\n }\n return { kind: \"shutdown\" };\n }\n default:\n throw new Error(`unknown method: ${(req as DaemonRequest).method}`);\n }\n}\n\n/**\n * \u5904\u7406\u5355\u6761\u8FDE\u63A5\u4E0A\u7684\u8BF7\u6C42\u884C\uFF1B\u9274\u6743\u5931\u8D25\u5219\u5173\u95ED\u3002\n */\nasync function handleLine(sock: net.Socket, line: string, expectedToken: string): Promise<void> {\n let req: DaemonRequest;\n try {\n req = JSON.parse(line) as DaemonRequest;\n } catch {\n writeRes(sock, { type: \"res\", id: \"?\", ok: false, error: \"invalid json\" });\n return;\n }\n if (!req?.id || !req.method) {\n writeRes(sock, { type: \"res\", id: req?.id ?? \"?\", ok: false, error: \"invalid request\" });\n return;\n }\n if (req.token !== expectedToken) {\n writeRes(sock, { type: \"res\", id: req.id, ok: false, error: \"unauthorized\" });\n sock.end();\n return;\n }\n\n if (req.method === \"subscribe\") {\n subscribers.add(sock);\n sock.once(\"close\", () => subscribers.delete(sock));\n writeRes(sock, { type: \"res\", id: req.id, ok: true, result: { kind: \"subscribed\" } });\n return;\n }\n\n try {\n const result = await dispatch(req);\n writeRes(sock, { type: \"res\", id: req.id, ok: true, result });\n } catch (e) {\n writeRes(sock, {\n type: \"res\",\n id: req.id,\n ok: false,\n error: e instanceof Error ? e.message : String(e),\n });\n }\n}\n\n/**\n * \u542F\u52A8\u5B88\u62A4\u8FDB\u7A0B\uFF1A\u6807\u8BB0\u89D2\u8272\u3001listen \u7BA1\u9053\u3001\u843D\u76D8 daemon.json\uFF0C\u5E76\u8F6C\u53D1\u65E5\u5FD7/\u72B6\u6001\u4E8B\u4EF6\u3002\n * \u672C\u51FD\u6570\u5728\u6210\u529F listen \u540E\u6C38\u4E0D\u8FD4\u56DE\uFF08\u8FDB\u7A0B\u5E38\u9A7B\uFF09\u3002\n */\nexport async function runDaemonServer(): Promise<void> {\n const startLock = path.join(ROOT, \".sfmc\", \"daemon-start.lock\");\n fs.mkdirSync(path.dirname(startLock), { recursive: true });\n const nonce = randomUUID();\n let handle: number;\n try { handle = fs.openSync(startLock, \"wx\", 0o600); }\n catch (error) { if ((error as NodeJS.ErrnoException).code === \"EEXIST\") throw new Error(\"\u5B88\u62A4\u8FDB\u7A0B\u6B63\u5728\u542F\u52A8\u6216\u542F\u52A8\u9501\u9057\u7559\uFF1B\u4E0D\u4F1A\u521B\u5EFA\u7B2C\u4E8C\u5957\u540E\u53F0\uFF0C\u8BF7\u68C0\u67E5 daemon-start.lock\"); throw error; }\n fs.writeFileSync(handle, JSON.stringify({ pid: process.pid, nonce })); fs.closeSync(handle);\n const releaseStartup = () => { try { const owner = JSON.parse(fs.readFileSync(startLock, \"utf8\")) as { nonce: string }; if (owner.nonce === nonce) fs.unlinkSync(startLock); } catch { /* \u5DF2\u91CA\u653E */ } };\n process.once(\"exit\", releaseStartup);\n const previous = readDaemonMeta();\n if (previous && await isProcessAlive(previous.pid)) { releaseStartup(); process.exit(0); }\n if (previous) clearDaemonMeta();\n markDaemonServer();\n const unsubManagement = onManagementEvent(event => broadcast({ type: \"event\", event: \"management\", payload: event }));\n\n const token = createDaemonToken();\n const pipe = daemonPipePath();\n const meta: DaemonMeta = {\n pid: process.pid,\n pipe,\n token,\n startedAt: new Date().toISOString(),\n };\n\n const unsubLog = onLog((log) => {\n broadcast({ type: \"event\", event: \"log\", payload: toLogPayload(log) });\n });\n const unsubState = onServiceStateChange((ev) => {\n broadcast({ type: \"event\", event: \"state\", payload: ev });\n });\n\n const server = net.createServer((sock) => {\n let buf = \"\";\n sock.setEncoding(\"utf8\");\n sock.on(\"data\", (chunk: string) => {\n buf += chunk;\n if (Buffer.byteLength(buf) > 8 * 1024 * 1024) { sock.destroy(); return; }\n let idx: number;\n while ((idx = buf.indexOf(\"\\n\")) >= 0) {\n const line = buf.slice(0, idx).trim();\n buf = buf.slice(idx + 1);\n if (!line) continue;\n void handleLine(sock, line, token);\n }\n });\n sock.on(\"error\", () => {\n subscribers.delete(sock);\n });\n sock.on(\"close\", () => {\n subscribers.delete(sock);\n });\n });\n\n await new Promise<void>((resolve, reject) => {\n const onErr = (err: NodeJS.ErrnoException): void => {\n /* \u53E6\u4E00\u5B9E\u4F8B\u5DF2\u5360\u7528\u7BA1\u9053\uFF1A\u5B89\u9759\u9000\u51FA\uFF0C\u907F\u514D\u53CC\u5B88\u62A4\u8FDB\u7A0B */\n if (err.code === \"EADDRINUSE\") {\n process.exit(0);\n }\n reject(err);\n };\n server.once(\"error\", onErr);\n server.listen(pipe, () => {\n server.off(\"error\", onErr);\n resolve();\n });\n });\n\n writeDaemonMeta(meta);\n releaseStartup();\n // \u542F\u52A8\u65F6\u5C31\u6838\u5BF9\u9057\u7559\u4EFB\u52A1\u3002\u7EF4\u62A4\u8FDB\u7A0B\u88AB\u6740\u6389\u540E\uFF0C\u684C\u9762\u4E0D\u7528\u5148\u53D1\u8BF7\u6C42\u4E5F\u80FD\u770B\u5230\u300C\u5DF2\u4E2D\u65AD\u300D\u3002\n managementTasks();\n if (process.argv.includes(\"--autostart\") && !isMaintenanceLocked(ROOT)) await startAll();\n\n const cleanup = (): void => {\n unsubManagement();\n unsubLog();\n unsubState();\n try {\n server.close();\n } catch {\n /* ignore */\n }\n clearDaemonMeta();\n };\n\n process.on(\"exit\", cleanup);\n process.on(\"SIGTERM\", () => {\n if (shuttingDown) return;\n shuttingDown = true;\n void stopAll().finally(() => {\n cleanup();\n process.exit(0);\n });\n });\n /* \u5B88\u62A4\u8FDB\u7A0B\u5FFD\u7565 SIGINT / SIGHUP\uFF1A\u7BA1\u7406\u8FDE\u63A5\u6216\u7EC8\u7AEF\u6302\u8D77\u4E0D\u8868\u793A\u505C\u670D\u3002\u505C\u670D\u53EA\u8D70\u663E\u5F0F shutdown\uFF0C\u6216\u670D\u52A1\u7BA1\u7406\u5668\u53D1\u6765\u7684 SIGTERM\u3002 */\n process.on(\"SIGINT\", () => {\n /* no-op */\n });\n process.on(\"SIGHUP\", () => {\n /* no-op */\n });\n\n await new Promise<never>(() => {\n /* park forever */\n });\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics-read.d.ts","sourceRoot":"","sources":["../../src/management/metrics-read.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAiC1D;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,CA2ChF"}
|