tt-help-cli-ycl 1.4.72 → 1.4.73

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tt-help-cli-ycl",
3
- "version": "1.4.72",
3
+ "version": "1.4.73",
4
4
  "description": "TikTok user & video data scraper - extract ttSeller, verified, locationCreated from HTML source",
5
5
  "type": "module",
6
6
  "bin": {
@@ -3448,6 +3448,7 @@ function styled(el, style, content) {
3448
3448
  function cmdObj(cmd) {
3449
3449
  if (typeof cmd === "string")
3450
3450
  return {
3451
+ id: "",
3451
3452
  name: "",
3452
3453
  cmd: cmd,
3453
3454
  enabled: true,
@@ -3455,6 +3456,7 @@ function cmdObj(cmd) {
3455
3456
  maxMemoryMB: 0,
3456
3457
  };
3457
3458
  return {
3459
+ id: cmd.id || "",
3458
3460
  name: cmd.name || "",
3459
3461
  cmd: cmd.cmd || "",
3460
3462
  enabled: cmd.enabled !== false,
@@ -3594,43 +3596,32 @@ function renderOfflineCard(userId, cmds) {
3594
3596
  );
3595
3597
  }
3596
3598
 
3597
- // 匹配进程到命令(按命令行前缀匹配)
3599
+ // 匹配进程到命令(按唯一 ID 精确匹配)
3598
3600
  function matchProcsToCmds(myCmds, procs) {
3599
3601
  var matched = {}; // cmdIdx -> proc
3600
3602
  var unmatchedProcs = [];
3603
+ // 建 ID → 命令索引 映射
3604
+ var idToIdx = {};
3605
+ myCmds.forEach(function (cmd, i) {
3606
+ var c = cmdObj(cmd);
3607
+ if (c.id) idToIdx[c.id] = i;
3608
+ });
3601
3609
  procs.forEach(function (p) {
3602
3610
  var found = false;
3603
- for (var i = 0; i < myCmds.length; i++) {
3604
- var c = cmdObj(myCmds[i]);
3605
- var pName = p.name || p.cmdId || "";
3606
- if (
3607
- c.cmd &&
3608
- pName &&
3609
- (c.cmd.indexOf(pName.substring(0, 30)) === 0 ||
3610
- pName.indexOf(c.cmd.substring(0, 30)) === 0)
3611
- ) {
3612
- matched[i] = p;
3613
- found = true;
3614
- break;
3615
- }
3611
+ if (p.cmdId && idToIdx[p.cmdId] !== undefined) {
3612
+ matched[idToIdx[p.cmdId]] = p;
3613
+ found = true;
3616
3614
  }
3617
3615
  if (!found) unmatchedProcs.push(p);
3618
3616
  });
3619
3617
  return { matched: matched, unmatched: unmatchedProcs };
3620
3618
  }
3621
3619
 
3622
- // 根据命令行匹配 LLM 分析结果
3623
- function findAnalysis(llmAnalysis, cmdLine) {
3624
- if (!llmAnalysis || !cmdLine) return null;
3620
+ // 按命令 ID 匹配 LLM 分析结果
3621
+ function findAnalysis(llmAnalysis, cmdId) {
3622
+ if (!llmAnalysis || !cmdId) return null;
3625
3623
  for (var i = 0; i < llmAnalysis.length; i++) {
3626
- var a = llmAnalysis[i];
3627
- if (
3628
- a.cmdId &&
3629
- (a.cmdId.indexOf(cmdLine.substring(0, 30)) === 0 ||
3630
- cmdLine.indexOf(a.cmdId.substring(0, 30)) === 0)
3631
- ) {
3632
- return a;
3633
- }
3624
+ if (llmAnalysis[i].cmdId === cmdId) return llmAnalysis[i];
3634
3625
  }
3635
3626
  return null;
3636
3627
  }
@@ -3848,7 +3839,7 @@ function renderCmdRows(myCmds, userId, procs, isOnline, llmAnalysis) {
3848
3839
  );
3849
3840
 
3850
3841
  // AI 分析结果
3851
- var ai = findAnalysis(llmAnalysis, c.cmd);
3842
+ var ai = findAnalysis(llmAnalysis, c.id);
3852
3843
  if (ai) {
3853
3844
  var sevColor =
3854
3845
  ai.severity === "error"
@@ -3956,19 +3947,22 @@ function wdModalSave() {
3956
3947
  var myCmds = (allCmds[_wdModalUserId] || []).slice();
3957
3948
 
3958
3949
  if (_wdModalEditIdx >= 0) {
3959
- // 编辑模式
3950
+ // 编辑模式:保留已有 ID
3960
3951
  if (_wdModalEditIdx < myCmds.length) {
3952
+ var oldObj = cmdObj(myCmds[_wdModalEditIdx]);
3961
3953
  myCmds[_wdModalEditIdx] = {
3954
+ id: oldObj.id,
3962
3955
  name: name,
3963
3956
  cmd: cmd,
3964
- enabled: cmdObj(myCmds[_wdModalEditIdx]).enabled,
3957
+ enabled: oldObj.enabled,
3965
3958
  maxUptimeSeconds: maxUp,
3966
3959
  maxMemoryMB: maxMem,
3967
3960
  };
3968
3961
  }
3969
3962
  } else {
3970
- // 添加模式
3963
+ // 添加模式:生成新 ID
3971
3964
  myCmds.push({
3965
+ id: String(Date.now()),
3972
3966
  name: name,
3973
3967
  cmd: cmd,
3974
3968
  enabled: true,
@@ -4059,6 +4053,7 @@ function wdToggleClientCmd(userId, idx) {
4059
4053
  var cmd = myCmds[idx];
4060
4054
  if (typeof cmd === "string") {
4061
4055
  myCmds[idx] = {
4056
+ id: String(Date.now()),
4062
4057
  cmd: cmd,
4063
4058
  enabled: false,
4064
4059
  maxUptimeSeconds: 0,
@@ -84,12 +84,14 @@ function getOnlineClients() {
84
84
  function normalizeCmd(c) {
85
85
  if (typeof c === "string")
86
86
  return {
87
+ id: "",
87
88
  cmd: c.replace(/^[▶\n\r\s]+/, "").trim(),
88
89
  enabled: true,
89
90
  maxUptimeSeconds: 0,
90
91
  maxMemoryMB: 0,
91
92
  };
92
93
  return {
94
+ id: c.id || "",
93
95
  cmd: (c.cmd || "").replace(/^[▶\n\r\s]+/, "").trim(),
94
96
  enabled: c.enabled !== false,
95
97
  maxUptimeSeconds: c.maxUptimeSeconds || 0,
@@ -105,6 +107,7 @@ function getClientCommands(userId) {
105
107
  .map(normalizeCmd)
106
108
  .filter((c) => c.enabled && c.cmd)
107
109
  .map((c) => ({
110
+ id: c.id,
108
111
  cmd: c.cmd,
109
112
  maxUptimeSeconds: c.maxUptimeSeconds,
110
113
  maxMemoryMB: c.maxMemoryMB,
@@ -113,10 +116,21 @@ function getClientCommands(userId) {
113
116
 
114
117
  function setClientCommands(userId, commands) {
115
118
  if (!Array.isArray(commands)) return;
116
- // 清洗每个命令中的非法前缀
117
- const cleaned = commands.map((c) => {
118
- if (typeof c === "string") return c.replace(/^[▶\n\r\s]+/, "").trim();
119
- return { ...c, cmd: (c.cmd || "").replace(/^[▶\n\r\s]+/, "").trim() };
119
+ let nextId = 1;
120
+ const cleaned = commands.map((c, idx) => {
121
+ if (typeof c === "string") {
122
+ const id = String(Date.now() + idx);
123
+ return {
124
+ id,
125
+ cmd: c.replace(/^[▶\n\r\s]+/, "").trim(),
126
+ enabled: true,
127
+ maxUptimeSeconds: 0,
128
+ maxMemoryMB: 0,
129
+ };
130
+ }
131
+ const obj = { ...c, cmd: (c.cmd || "").replace(/^[▶\n\r\s]+/, "").trim() };
132
+ if (!obj.id) obj.id = String(Date.now() + nextId++);
133
+ return obj;
120
134
  });
121
135
  const cfg = loadConfig();
122
136
  if (!cfg.clientCommands) cfg.clientCommands = {};
@@ -274,6 +288,29 @@ async function handleAnalyzeLogs(params, body, res) {
274
288
  export function registerWatchdogRoutes(httpServer) {
275
289
  console.error("[watchdog] 看门狗服务端模块已加载");
276
290
  console.error(`[watchdog] 配置文件: ${WATCHDOG_CONFIG_PATH}`);
291
+ // 自动迁移:为已有命令补充唯一 ID
292
+ migrateCommandIds();
293
+ }
294
+
295
+ // 为现有命令中未分配 ID 的自动补充 ID
296
+ function migrateCommandIds() {
297
+ const cfg = loadConfig();
298
+ if (!cfg.clientCommands) return;
299
+ let migrated = 0;
300
+ for (const userId of Object.keys(cfg.clientCommands)) {
301
+ const cmds = cfg.clientCommands[userId];
302
+ if (!Array.isArray(cmds)) continue;
303
+ for (const c of cmds) {
304
+ if (typeof c === "object" && !c.id) {
305
+ c.id = String(Date.now() + migrated);
306
+ migrated++;
307
+ }
308
+ }
309
+ }
310
+ if (migrated > 0) {
311
+ saveConfig(cfg);
312
+ console.error(`[watchdog] 已为 ${migrated} 条旧命令补充 ID`);
313
+ }
277
314
  }
278
315
 
279
316
  export async function tryHandleWatchdogRoute(
@@ -114,6 +114,19 @@ function getProcessStats(pid) {
114
114
  }
115
115
  }
116
116
 
117
+ // Windows: 从 cmd.exe 往下找真正的 node.exe PID(shell:true 时 cmd.exe 是中间层)
118
+ function getChildPid(parentPid) {
119
+ try {
120
+ const out = execSync(
121
+ `powershell -NoProfile -Command "Get-CimInstance Win32_Process -Filter 'ParentProcessId=${parentPid}' | Where-Object { $_.Name -notlike 'conhost*' } | Select-Object -First 1 -ExpandProperty ProcessId"`,
122
+ { encoding: "utf-8", timeout: 3000, stdio: ["ignore", "pipe", "ignore"] },
123
+ ).trim();
124
+ return parseInt(out, 10) || 0;
125
+ } catch {
126
+ return 0;
127
+ }
128
+ }
129
+
117
130
  export class ProcessManager extends EventEmitter {
118
131
  constructor() {
119
132
  super();
@@ -158,6 +171,19 @@ export class ProcessManager extends EventEmitter {
158
171
  stdio: ["ignore", "pipe", "pipe"],
159
172
  detached: false,
160
173
  });
174
+
175
+ // Windows: shell:true → child.pid 是 cmd.exe,延迟获取真实 node.exe PID(用于内存读取)
176
+ if (IS_WIN) {
177
+ setTimeout(() => {
178
+ const realPid = getChildPid(child.pid);
179
+ if (realPid > 0) {
180
+ const info = this.processes.get(id);
181
+ if (info && info.child === child) {
182
+ info._realPid = realPid;
183
+ }
184
+ }
185
+ }, 3000);
186
+ }
161
187
  } catch (e) {
162
188
  console.error(`[watchdog] 启动命令 ${id} 失败: ${e.message}`);
163
189
  this.processes.set(id, {
@@ -295,7 +321,10 @@ export class ProcessManager extends EventEmitter {
295
321
  const result = [];
296
322
  for (const [cmdId, info] of this.processes) {
297
323
  if (info.stopped) continue;
298
- const pid = info.child?.pid || 0;
324
+ // Windows: 壳是 cmd.exe,真实业务进程是 node.exe(_realPid)
325
+ const pid = IS_WIN
326
+ ? info._realPid || info.child?.pid || 0
327
+ : info.child?.pid || 0;
299
328
  const stats = pid ? getProcessStats(pid) : null;
300
329
  const uptimeSeconds = Math.round((Date.now() - info.startTime) / 1000);
301
330
 
@@ -361,16 +390,16 @@ export class ProcessManager extends EventEmitter {
361
390
  syncCommands(expectedCmds) {
362
391
  if (!Array.isArray(expectedCmds)) return;
363
392
 
364
- // 标准化:{cmd, maxUptimeSeconds, maxMemoryMB} → {id, name, command, args, enabled, thresholds}
393
+ // 标准化:使用服务端分配的 id,无 id 时用命令行截断兜底
365
394
  const normalized = expectedCmds
366
395
  .map((c) => {
367
396
  if (typeof c === "string")
368
397
  c = { cmd: c, maxUptimeSeconds: 0, maxMemoryMB: 0 };
369
- // 清洗:去除可能混入的控制字符、状态图标等
370
398
  const cmdLine = (c.cmd || "").replace(/^[▶\n\r\s]+/, "").trim();
371
399
  if (!cmdLine) return null;
372
400
  const id =
373
- cmdLine.length > 60 ? cmdLine.substring(0, 60) + "..." : cmdLine;
401
+ c.id ||
402
+ (cmdLine.length > 60 ? cmdLine.substring(0, 60) + "..." : cmdLine);
374
403
  return {
375
404
  id,
376
405
  name: id,
@@ -422,7 +451,8 @@ export class ProcessManager extends EventEmitter {
422
451
  if (info.stopped || !info.child) continue;
423
452
  const thresholds = info.thresholds || {};
424
453
  const uptimeSeconds = Math.round((now - info.startTime) / 1000);
425
- const pid = info.child.pid;
454
+ // Windows: 用真实 node.exe PID 读内存
455
+ const pid = IS_WIN ? info._realPid || info.child.pid : info.child.pid;
426
456
 
427
457
  let reason = "";
428
458
  // 检查运行时长