tt-help-cli-ycl 1.4.63 → 1.4.65
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/package.json +1 -1
- package/src/watchdog/process-manager.js +90 -22
package/package.json
CHANGED
|
@@ -15,10 +15,52 @@ import { EventEmitter } from "events";
|
|
|
15
15
|
const MAX_RESTART = 3;
|
|
16
16
|
const IS_WIN = process.platform === "win32";
|
|
17
17
|
|
|
18
|
+
// 引号感知的命令拆分:"a b" "c d" → ["a b", "c d"]
|
|
19
|
+
function splitCommand(cmdLine) {
|
|
20
|
+
const parts = [];
|
|
21
|
+
let cur = "",
|
|
22
|
+
inDq = false,
|
|
23
|
+
inSq = false;
|
|
24
|
+
for (let i = 0; i < cmdLine.length; i++) {
|
|
25
|
+
const ch = cmdLine[i];
|
|
26
|
+
if (ch === '"' && !inSq) {
|
|
27
|
+
inDq = !inDq;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (ch === "'" && !inDq) {
|
|
31
|
+
inSq = !inSq;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (ch === " " && !inDq && !inSq) {
|
|
35
|
+
if (cur) {
|
|
36
|
+
parts.push(cur);
|
|
37
|
+
cur = "";
|
|
38
|
+
}
|
|
39
|
+
} else {
|
|
40
|
+
cur += ch;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (cur) parts.push(cur);
|
|
44
|
+
return parts;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Windows: 从 cmd.exe 往下找真正的子进程 PID(跳过 conhost)
|
|
48
|
+
function getChildPid(parentPid) {
|
|
49
|
+
try {
|
|
50
|
+
const out = execSync(
|
|
51
|
+
`powershell -NoProfile -Command "Get-CimInstance Win32_Process -Filter 'ParentProcessId=${parentPid}' | Where-Object { $_.Name -ne 'conhost.exe' } | Select-Object -First 1 -ExpandProperty ProcessId"`,
|
|
52
|
+
{ encoding: "utf-8", timeout: 3000, stdio: ["ignore", "pipe", "ignore"] },
|
|
53
|
+
).trim();
|
|
54
|
+
return parseInt(out, 10) || 0;
|
|
55
|
+
} catch {
|
|
56
|
+
return 0;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
18
60
|
function getProcessStats(pid) {
|
|
19
61
|
try {
|
|
20
62
|
const cmd = IS_WIN
|
|
21
|
-
? `
|
|
63
|
+
? `powershell -NoProfile -Command "(Get-Process -Id ${pid} -ErrorAction SilentlyContinue).WorkingSet64"`
|
|
22
64
|
: `ps -p ${pid} -o rss=,etime=`;
|
|
23
65
|
const stdout = execSync(cmd, {
|
|
24
66
|
encoding: "utf-8",
|
|
@@ -28,13 +70,13 @@ function getProcessStats(pid) {
|
|
|
28
70
|
if (!stdout) return null;
|
|
29
71
|
|
|
30
72
|
if (IS_WIN) {
|
|
31
|
-
//
|
|
32
|
-
const
|
|
33
|
-
const memStr = (cols[4] || "").replace(/[^0-9.]/g, "");
|
|
34
|
-
const memKB = parseInt(memStr, 10);
|
|
73
|
+
// PowerShell 返回字节数,运行时长从内部 startTime 计算
|
|
74
|
+
const memBytes = parseInt(stdout, 10);
|
|
35
75
|
return {
|
|
36
|
-
memoryMB: isNaN(
|
|
37
|
-
|
|
76
|
+
memoryMB: isNaN(memBytes)
|
|
77
|
+
? 0
|
|
78
|
+
: Math.round((memBytes / 1048576) * 10) / 10,
|
|
79
|
+
elapsed: null,
|
|
38
80
|
};
|
|
39
81
|
}
|
|
40
82
|
|
|
@@ -77,18 +119,29 @@ export class ProcessManager extends EventEmitter {
|
|
|
77
119
|
let child;
|
|
78
120
|
const startTime = Date.now();
|
|
79
121
|
try {
|
|
80
|
-
//
|
|
81
|
-
if (args.length
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
122
|
+
// 拆分为 program + args;Windows .cmd 需 shell 模式启动
|
|
123
|
+
if (args.length === 0) {
|
|
124
|
+
const parts = splitCommand(command);
|
|
125
|
+
command = parts[0] || command;
|
|
126
|
+
args = parts.slice(1);
|
|
127
|
+
}
|
|
128
|
+
child = spawn(command, args, {
|
|
129
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
130
|
+
detached: false,
|
|
131
|
+
shell: IS_WIN,
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Windows: cmd.exe 不是真实进程,延迟获取子进程 PID
|
|
135
|
+
if (IS_WIN) {
|
|
136
|
+
setTimeout(() => {
|
|
137
|
+
const realPid = getChildPid(child.pid);
|
|
138
|
+
if (realPid > 0) {
|
|
139
|
+
const info = this.processes.get(id);
|
|
140
|
+
if (info && info.child === child) {
|
|
141
|
+
info._realPid = realPid;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}, 2000);
|
|
92
145
|
}
|
|
93
146
|
} catch (e) {
|
|
94
147
|
console.error(`[watchdog] 启动命令 ${id} 失败: ${e.message}`);
|
|
@@ -203,7 +256,8 @@ export class ProcessManager extends EventEmitter {
|
|
|
203
256
|
const result = [];
|
|
204
257
|
for (const [cmdId, info] of this.processes) {
|
|
205
258
|
if (info.stopped) continue;
|
|
206
|
-
|
|
259
|
+
// Windows: 优先用真实子进程 PID(否则拿到的是 cmd.exe 的内存)
|
|
260
|
+
const pid = info._realPid || info.child?.pid || 0;
|
|
207
261
|
const stats = pid ? getProcessStats(pid) : null;
|
|
208
262
|
const uptimeSeconds = Math.round((Date.now() - info.startTime) / 1000);
|
|
209
263
|
|
|
@@ -218,7 +272,21 @@ export class ProcessManager extends EventEmitter {
|
|
|
218
272
|
: "error",
|
|
219
273
|
memoryMB: stats?.memoryMB ?? 0,
|
|
220
274
|
uptimeSeconds,
|
|
221
|
-
uptimeDisplay:
|
|
275
|
+
uptimeDisplay:
|
|
276
|
+
stats?.elapsed ||
|
|
277
|
+
(function (s) {
|
|
278
|
+
var m = Math.floor(s / 60);
|
|
279
|
+
var h = Math.floor(m / 60);
|
|
280
|
+
m = m % 60;
|
|
281
|
+
s = s % 60;
|
|
282
|
+
return h > 0
|
|
283
|
+
? h +
|
|
284
|
+
":" +
|
|
285
|
+
String(m).padStart(2, "0") +
|
|
286
|
+
":" +
|
|
287
|
+
String(s).padStart(2, "0")
|
|
288
|
+
: m + ":" + String(s).padStart(2, "0");
|
|
289
|
+
})(uptimeSeconds),
|
|
222
290
|
restartCount: info.restartCount,
|
|
223
291
|
error: info.spawnError || null,
|
|
224
292
|
});
|
|
@@ -296,7 +364,7 @@ export class ProcessManager extends EventEmitter {
|
|
|
296
364
|
if (info.stopped || !info.child) continue;
|
|
297
365
|
const thresholds = info.thresholds || {};
|
|
298
366
|
const uptimeSeconds = Math.round((now - info.startTime) / 1000);
|
|
299
|
-
const pid = info.child.pid;
|
|
367
|
+
const pid = info._realPid || info.child.pid;
|
|
300
368
|
|
|
301
369
|
let reason = "";
|
|
302
370
|
// 检查运行时长
|