tt-help-cli-ycl 1.4.183 → 1.4.184
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/scripts/test-crash-circuit-breaker.mjs +250 -0
- package/scripts/test-error-auto-reset.mjs +103 -0
- package/scripts/test-error-report.mjs +215 -0
- package/scripts/test-login-pool-faceban-e2e.mjs +131 -0
- package/scripts/test-login-pool-faceban.mjs +235 -0
- package/scripts/test-process-manager-label.mjs +145 -0
- package/src/cli/explore.js +99 -17
- package/src/cli/search.js +106 -15
- package/src/lib/browser/health-checker.js +19 -3
- package/src/lib/browser/page.js +23 -0
- package/src/lib/error-report.js +119 -0
- package/src/lib/login-pool-client.js +56 -0
- package/src/watch/data-store.js +25 -0
- package/src/watch/login-pool.js +118 -81
- package/src/watch/public/app.js +7 -2
- package/src/watch/public/index.html +2 -1
- package/src/watch/server.js +53 -8
- package/src/watchdog/process-manager.js +71 -6
- package/src/watchdog/reporter.js +2 -0
package/src/watch/server.js
CHANGED
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
prepareRelogin,
|
|
25
25
|
clearAccountHistory,
|
|
26
26
|
clearAllAccountHistory,
|
|
27
|
+
setAccountFace,
|
|
27
28
|
} from "./login-pool.js";
|
|
28
29
|
import { checkPoolAccounts } from "./login-health.js";
|
|
29
30
|
|
|
@@ -738,7 +739,6 @@ async function load() {
|
|
|
738
739
|
((b.lastUsedAt || b.createdAt || 0) - (a.lastUsedAt || a.createdAt || 0)));
|
|
739
740
|
const rows = list.map((a, idx) => {
|
|
740
741
|
const l = a.lease;
|
|
741
|
-
const biz = a.lastBusinessOkAt ? fmt(a.lastBusinessOkAt) : '无记录';
|
|
742
742
|
const expired = a.sessionExpiresAt && a.sessionExpiresAt < Date.now();
|
|
743
743
|
const st = ST[a.status] || [a.status, ''];
|
|
744
744
|
const who = l
|
|
@@ -748,16 +748,17 @@ async function load() {
|
|
|
748
748
|
const lastApp = l ? (a.prevAppName || null) : (a.lastAppName || null);
|
|
749
749
|
const recent = [];
|
|
750
750
|
if (lastApp) recent.push(esc(lastApp));
|
|
751
|
-
|
|
752
|
-
if (sw.length) recent.push('<span class="muted">' + sw.map(kv => (RS[kv[0]] || esc(kv[0])) + '×' + kv[1]).join(' · ') + '</span>');
|
|
751
|
+
if (a.lastSwitchReason) recent.push('<span class="muted">' + (RS[a.lastSwitchReason] || esc(a.lastSwitchReason)) + '×' + (a.switchStreak || 1) + '</span>');
|
|
753
752
|
const health = a.lastHealth === 'alive' ? '<span style="color:#16a34a">✓</span>'
|
|
754
753
|
: a.lastHealth === 'dead' ? '<span class="warn">✗</span>'
|
|
755
754
|
: a.lastHealth === 'error' ? '<span class="muted">?</span>'
|
|
756
755
|
: '<span class="muted">—</span>';
|
|
756
|
+
// (2026-11-03 faceBadge 面标注徽章随 explore/search 列一并移除,数据与 API 保留休眠)
|
|
757
757
|
const meta = [];
|
|
758
758
|
if (a.note) meta.push(esc(a.note));
|
|
759
759
|
if (a.proxyHint) meta.push('出口 ' + esc(a.proxyHint));
|
|
760
760
|
if (a.cookieCount) meta.push('cookies ' + a.cookieCount + ' 条');
|
|
761
|
+
if (a.errStreak > 0) meta.push('<span class="warn">连败×' + a.errStreak + '</span>');
|
|
761
762
|
return '<tr>' +
|
|
762
763
|
'<td class="muted">' + (idx + 1) + '</td>' +
|
|
763
764
|
'<td><b>' + esc(a.username) + '</b>' +
|
|
@@ -765,7 +766,7 @@ async function load() {
|
|
|
765
766
|
'<td><span class="badge ' + st[1] + '">' + st[0] + '</span></td>' +
|
|
766
767
|
'<td>' + who + '</td>' +
|
|
767
768
|
'<td>' + (recent.length ? recent.join('<br>') : '<span class="muted">—</span>') + '</td>' +
|
|
768
|
-
'<td title="最后使用: ' + fmt(a.lastUsedAt) + '
|
|
769
|
+
'<td title="最后使用: ' + fmt(a.lastUsedAt) + '">' + rel(a.lastUsedAt) + '</td>' +
|
|
769
770
|
'<td>' + (a.useCount || 0) + '</td>' +
|
|
770
771
|
'<td' + (expired ? ' class="warn"' : '') + ' title="sessionid 到期: ' + fmt(a.sessionExpiresAt) + '(cookie 承诺寿命,非实时有效性)">' + day(a.sessionExpiresAt) + (expired ? '(已过)' : '') + '</td>' +
|
|
771
772
|
'<td title="上次检测: ' + fmt(a.lastHealthAt) + (a.lastHealth === 'dead' ? '\\n死号已标疑似,可重登录复活' : a.lastHealth === 'alive' ? '\\n登录态存活' : '') + '">' + health + ' <span class="muted">' + rel(a.lastHealthAt) + '</span></td>' +
|
|
@@ -792,9 +793,10 @@ async function load() {
|
|
|
792
793
|
'<span class="chip">疑似 <b>' + nSus + '</b></span>' +
|
|
793
794
|
'<span class="chip">已停用 <b>' + nRet + '</b></span>';
|
|
794
795
|
document.getElementById('content').innerHTML =
|
|
795
|
-
'<table><tr><th>#</th><th>账号</th><th title="状态四态:使用中=租约在身 · 空闲=可被自动取号 · 疑似=检测判死暂不参与自动取号(可重登录复活)· 已停用=退出池">状态</th><th>使用者</th><th title="
|
|
796
|
+
'<table><tr><th>#</th><th>账号</th><th title="状态四态:使用中=租约在身 · 空闲=可被自动取号 · 疑似=检测判死暂不参与自动取号(可重登录复活)· 已停用=退出池">状态</th><th>使用者</th><th title="上次使用的程序:在租=当前取号的前一任(与使用者列对比看切换);空闲=最近一任。附最近换号原因×连续次数(仅显式上报才计数)">上次使用</th><th>最近使用</th><th>次数</th><th title="sessionid cookie 的承诺寿命,非实时有效性">有效期</th><th title="登录态探测(✓/✗/?/—);真实判据 = 业务出活">检测</th><th>操作</th><th>历史</th></tr>' + rows + '</table>' +
|
|
796
797
|
'<div style="padding: 10px 14px;"><p class="muted" style="margin:0">排序:使用中 → 空闲 → 疑似 → 已停用 · 每 15 秒自动刷新 · 有效期 = sessionid cookie 承诺寿命 · 检测 = 登录态探测(真实判据 = 业务出活)· cookies 明文不出服务端 · 「新增账号/重登录」一键拉起需本机装 tt-help 并执行过 <b>tt-help protocol register</b></p>' +
|
|
797
|
-
'<p class="muted" style="margin:6px 0 0">检测列图例:✓ 存活 · ✗ 已死(自动标疑似)· ? 探测异常(网络/超时,不代表号死)· — 未检测 · 时间 =
|
|
798
|
+
'<p class="muted" style="margin:6px 0 0">检测列图例:✓ 存活 · ✗ 已死(自动标疑似)· ? 探测异常(网络/超时,不代表号死)· — 未检测 · 时间 = 最近探测</p>' +
|
|
799
|
+
'<p class="muted" style="margin:6px 0 0">面标注图例:🚫 该面残缺禁用(派号跳过) · ✅ 验证通过 · — 未验证默认可用 · 角标「探」= 探针标注;「标挂/恢复」选面(explore/search),不影响状态与当前租约</p></div>';
|
|
798
800
|
} catch (e) { msg('加载失败: ' + e.message, true); }
|
|
799
801
|
}
|
|
800
802
|
function copyCmd(cmd) {
|
|
@@ -1773,11 +1775,19 @@ export function startWatchServer(
|
|
|
1773
1775
|
ret = updateAccount(body);
|
|
1774
1776
|
console.error(`[登录池 ${ts}] UPDATE: ${ret.username} status=${ret.status}`);
|
|
1775
1777
|
} else if (lpAction === "clear-history") {
|
|
1776
|
-
// 清空日志(账号中心页面按钮):history 清成一条留痕 + useCount
|
|
1778
|
+
// 清空日志(账号中心页面按钮):history 清成一条留痕 + useCount/换号原因清零
|
|
1777
1779
|
ret = clearAccountHistory(body);
|
|
1778
1780
|
console.error(`[登录池 ${ts}] CLEAR-LOGS: ${ret.username} (by ${body.by || "page"})`);
|
|
1781
|
+
} else if (lpAction === "face") {
|
|
1782
|
+
// 面标注(2026-11-02 faceBans):explore/search 按面标挂/恢复,探针与人工同入口;
|
|
1783
|
+
// leased 号也可标(不动租约,下次派发生效)
|
|
1784
|
+
ret = setAccountFace(body);
|
|
1785
|
+
const fb = ret.faceBans && ret.faceBans[body.face];
|
|
1786
|
+
console.error(
|
|
1787
|
+
`[登录池 ${ts}] FACE: ${ret.username} ${body.face}=${fb && fb.ban ? "禁用" : "恢复"} (${body.source || "manual"}${body.by ? " · " + body.by : ""})`,
|
|
1788
|
+
);
|
|
1779
1789
|
} else if (lpAction === "clear-history-all") {
|
|
1780
|
-
// 一键清空全部账号日志(页面批量按钮):各账号留痕一条 + useCount
|
|
1790
|
+
// 一键清空全部账号日志(页面批量按钮):各账号留痕一条 + useCount/换号原因清零
|
|
1781
1791
|
const sum = clearAllAccountHistory(body);
|
|
1782
1792
|
console.error(`[登录池 ${ts}] CLEAR-LOGS-ALL: ${sum.cleared} 个账号 (by ${body.by || "page"})`);
|
|
1783
1793
|
sendJSON(res, 200, { ok: true, cleared: sum.cleared });
|
|
@@ -2503,6 +2513,8 @@ export function startWatchServer(
|
|
|
2503
2513
|
body.errorStack || "",
|
|
2504
2514
|
body.hostname || req.headers["x-client-hostname"] || "",
|
|
2505
2515
|
body.clientId || req.headers["x-watchdog-client-id"] || "",
|
|
2516
|
+
body.poolUsername || "",
|
|
2517
|
+
body.proxyNode || "",
|
|
2506
2518
|
);
|
|
2507
2519
|
sendJSON(res, 200, { ok: true });
|
|
2508
2520
|
} else {
|
|
@@ -3655,6 +3667,39 @@ export function startWatchServer(
|
|
|
3655
3667
|
`[job] 无国家数据自动回炉已启动: 间隔 ${NO_COUNTRY_MOVE_INTERVAL_MS / 3600000} 小时`,
|
|
3656
3668
|
);
|
|
3657
3669
|
|
|
3670
|
+
// 错误任务自动恢复(B 方案:无次数上限全自动):
|
|
3671
|
+
// explore 失败任务 status='error' 躺平在 jobs 表,此前全靠页面「批量重新处理」人工重置。
|
|
3672
|
+
// 定时扫龄期超阈的 error 行复用 resetJob 回流(与按钮同语义,含三表迁移/缓存失效/统计置脏)。
|
|
3673
|
+
// 龄期防抖:避开错误风暴进行中 + 客户端可能仍在处理原任务(慢节点单用户可超 10 分钟)。
|
|
3674
|
+
const ERROR_AUTO_RESET_INTERVAL_MS = 5 * 60 * 1000; // 扫描间隔
|
|
3675
|
+
const ERROR_AUTO_RESET_AGE_MS = 10 * 60 * 1000; // error 龄期阈值(「恢复 10 分钟前的错误任务」)
|
|
3676
|
+
const ERROR_AUTO_RESET_BATCH = 500; // 每轮上限,积压大时分轮消化
|
|
3677
|
+
const autoResetStaleErrors = () => {
|
|
3678
|
+
try {
|
|
3679
|
+
const ids = store.listStaleErrorJobs(
|
|
3680
|
+
ERROR_AUTO_RESET_AGE_MS,
|
|
3681
|
+
ERROR_AUTO_RESET_BATCH,
|
|
3682
|
+
);
|
|
3683
|
+
if (ids.length === 0) return;
|
|
3684
|
+
let reset = 0;
|
|
3685
|
+
for (const uid of ids) {
|
|
3686
|
+
const ret = store.resetJob(uid);
|
|
3687
|
+
if (ret && ret.saved) reset++;
|
|
3688
|
+
}
|
|
3689
|
+
const ts = new Date().toLocaleTimeString("zh-CN", { hour12: false });
|
|
3690
|
+
console.error(
|
|
3691
|
+
`[JOB ${ts}] AUTO-RESET-ERROR: reset=${reset}/${ids.length}(龄期>${ERROR_AUTO_RESET_AGE_MS / 60000}min,无次数上限)`,
|
|
3692
|
+
);
|
|
3693
|
+
} catch (e) {
|
|
3694
|
+
console.error(`[JOB] AUTO-RESET-ERROR 定时任务异常: ${e.message}`);
|
|
3695
|
+
}
|
|
3696
|
+
};
|
|
3697
|
+
setInterval(autoResetStaleErrors, ERROR_AUTO_RESET_INTERVAL_MS);
|
|
3698
|
+
setTimeout(autoResetStaleErrors, 60 * 1000); // 启动 1 分钟后先清一轮积压
|
|
3699
|
+
console.error(
|
|
3700
|
+
`[job] 错误任务自动恢复已启动: 间隔 ${ERROR_AUTO_RESET_INTERVAL_MS / 60000} 分钟, 龄期 ${ERROR_AUTO_RESET_AGE_MS / 60000} 分钟, 每轮上限 ${ERROR_AUTO_RESET_BATCH}(重启后 1 分钟首扫)`,
|
|
3701
|
+
);
|
|
3702
|
+
|
|
3658
3703
|
_resolve({ server, port });
|
|
3659
3704
|
});
|
|
3660
3705
|
|
|
@@ -31,8 +31,10 @@ const RESTART_SPAWN_STAGGER_MS = 60000; // 启动错峰 60s
|
|
|
31
31
|
const STALE_LOG_TIMEOUT_MS = 15 * 60 * 1000; // 15 分钟无日志输出视为假死
|
|
32
32
|
|
|
33
33
|
// === 控制台日志格式化(仅显示层增强,不影响任何功能链路) ===
|
|
34
|
-
// 目标:多进程日志交错打印时,通过时间戳 + 固定宽度彩色 [
|
|
34
|
+
// 目标:多进程日志交错打印时,通过时间戳 + 固定宽度彩色 [程序名:端口] 前缀方便人眼分流。
|
|
35
35
|
// 兼容:Windows(Windows Terminal 彩色 / 旧 conhost 自动降级纯文本)与 macOS(原生 ANSI 彩色)。
|
|
36
|
+
// 前缀标签由 deriveDisplayLabel 从命令行派生(如 [explore:9223]/[explore:9223-9225]),
|
|
37
|
+
// 解析失败退回 cmdDef.name || id。内部 cmdId 完全不动(stop/toggle/日志拉取/LLM 分析按它索引)。
|
|
36
38
|
|
|
37
39
|
// 每进程固定一种颜色(按 cmdId 哈希取色),新增进程自动分配且不重复
|
|
38
40
|
const PROC_COLORS = [32, 36, 33, 35, 34, 92, 96, 93, 95, 94];
|
|
@@ -141,6 +143,61 @@ function splitCommand(cmdLine) {
|
|
|
141
143
|
return parts;
|
|
142
144
|
}
|
|
143
145
|
|
|
146
|
+
// === 显示层标签派生(仅显示层增强,不影响任何功能链路;2026-11-04 定稿) ===
|
|
147
|
+
// 规则:[程序名:端口],端口解析不到就只显示程序名,程序名也解析不到退回 fallbackId。
|
|
148
|
+
// 端口提取复用 extractCdpPorts 原样逻辑(多端口区间压缩为 首-尾,端口天然连续)。
|
|
149
|
+
|
|
150
|
+
// 启动器 token(basename 去掉 .exe/.cmd/.bat 后比对):其后的 token 才是程序名/子命令
|
|
151
|
+
const LAUNCHER_RE = /^(?:tt-help|node|npx)(?:\.(?:exe|cmd|bat))?$/i;
|
|
152
|
+
const SCRIPT_EXT_RE = /\.(?:mjs|cjs|js)$/i;
|
|
153
|
+
|
|
154
|
+
// 跨平台 basename(同时处理 / 与 Windows \)
|
|
155
|
+
function pathBaseName(p) {
|
|
156
|
+
const s = String(p || "");
|
|
157
|
+
const idx = Math.max(s.lastIndexOf("/"), s.lastIndexOf("\\"));
|
|
158
|
+
return idx >= 0 ? s.slice(idx + 1) : s;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* 从拆分后的 token 中解析程序名。
|
|
163
|
+
* token[0] 为启动器(tt-help/node/npx)时取 token[1];
|
|
164
|
+
* 取到的 token 是脚本文件路径(.js/.mjs/.cjs)时:
|
|
165
|
+
* - 后面还有非 flag token(子命令)→ 取子命令(node src/main.js watch → watch)
|
|
166
|
+
* - 否则取 basename 去扩展名(node scraper.js --port X → scraper)
|
|
167
|
+
* 解析不出返回 null(调用方退回 fallbackId)。
|
|
168
|
+
*/
|
|
169
|
+
function deriveProgramName(tokens) {
|
|
170
|
+
if (!Array.isArray(tokens) || tokens.length === 0) return null;
|
|
171
|
+
let idx = 0;
|
|
172
|
+
const firstBase = pathBaseName(tokens[0]).replace(/\.(?:exe|cmd|bat)$/i, "");
|
|
173
|
+
if (LAUNCHER_RE.test(firstBase) && tokens.length > 1) idx = 1;
|
|
174
|
+
const base = pathBaseName(tokens[idx]);
|
|
175
|
+
if (SCRIPT_EXT_RE.test(base)) {
|
|
176
|
+
const next = tokens[idx + 1];
|
|
177
|
+
if (next && !next.startsWith("-")) return next;
|
|
178
|
+
return base.replace(SCRIPT_EXT_RE, "") || null;
|
|
179
|
+
}
|
|
180
|
+
return base || null;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* 派生日志前缀显示标签:[程序名:端口](纯函数,导出供单测覆盖全部分支)。
|
|
185
|
+
* - tt-help explore --base-port 9223 --port-count 3 → "explore:9223-9225"
|
|
186
|
+
* - tt-help refresh-mini --port 5222 → "refresh-mini:5222"(单端口仅浏览器类命令提取)
|
|
187
|
+
* - tt-help comments(无端口参数) → "comments"(不猜默认端口,避免误导)
|
|
188
|
+
* - 全部解析不出 → fallbackId(即 cmdDef.name || id,兜底回现状)
|
|
189
|
+
*/
|
|
190
|
+
export function deriveDisplayLabel(cmdLine, fallbackId) {
|
|
191
|
+
const tokens = splitCommand(String(cmdLine || ""));
|
|
192
|
+
if (tokens.length === 0) return fallbackId;
|
|
193
|
+
const prog = deriveProgramName(tokens);
|
|
194
|
+
if (!prog) return fallbackId;
|
|
195
|
+
const ports = extractCdpPorts(String(cmdLine || ""));
|
|
196
|
+
if (ports.length === 1) return `${prog}:${ports[0]}`;
|
|
197
|
+
if (ports.length > 1) return `${prog}:${ports[0]}-${ports[ports.length - 1]}`;
|
|
198
|
+
return prog;
|
|
199
|
+
}
|
|
200
|
+
|
|
144
201
|
function getProcessStats(pid) {
|
|
145
202
|
try {
|
|
146
203
|
if (IS_WIN) {
|
|
@@ -244,7 +301,7 @@ function getChildPid(parentPid) {
|
|
|
244
301
|
export class ProcessManager extends EventEmitter {
|
|
245
302
|
constructor() {
|
|
246
303
|
super();
|
|
247
|
-
/** @type {Map<string, {cmdId:string, cmdDef:object, child:ChildProcess, startTime:number, restartCount:number, thresholds:object, stopped:boolean}>} */
|
|
304
|
+
/** @type {Map<string, {cmdId:string, cmdDef:object, displayLabel:string, child:ChildProcess, startTime:number, restartCount:number, thresholds:object, stopped:boolean}>} */
|
|
248
305
|
this.processes = new Map();
|
|
249
306
|
/** @type {Map<string, number>} cmdId → 连续异常重启次数(跨实例累计;稳定运行后清零)
|
|
250
307
|
* 2026-09-25 修复: 原先计数存在每个实例的 info 上,自动重启重建 info 后归零,
|
|
@@ -258,6 +315,12 @@ export class ProcessManager extends EventEmitter {
|
|
|
258
315
|
*/
|
|
259
316
|
startCommand(cmdDef) {
|
|
260
317
|
let { id, command, args = [] } = cmdDef;
|
|
318
|
+
// 显示层标签(仅显示用;cmdId 不动)。在 splitCommand 改写 command/args 前派生,
|
|
319
|
+
// 重启路径(自动重启/假死/阈值)都经 startCommand,标签随之重建。
|
|
320
|
+
const displayLabel = deriveDisplayLabel(
|
|
321
|
+
[command, ...(Array.isArray(args) ? args : [])].join(" "),
|
|
322
|
+
(cmdDef && cmdDef.name) || id,
|
|
323
|
+
);
|
|
261
324
|
if (this.processes.has(id)) {
|
|
262
325
|
// 已有进程在运行(或 stopCommand 还未清理),跳过
|
|
263
326
|
const existing = this.processes.get(id);
|
|
@@ -309,6 +372,7 @@ export class ProcessManager extends EventEmitter {
|
|
|
309
372
|
this.processes.set(id, {
|
|
310
373
|
cmdId: id,
|
|
311
374
|
cmdDef,
|
|
375
|
+
displayLabel,
|
|
312
376
|
child: null,
|
|
313
377
|
startTime,
|
|
314
378
|
restartCount: 0,
|
|
@@ -327,9 +391,8 @@ export class ProcessManager extends EventEmitter {
|
|
|
327
391
|
if (msg) {
|
|
328
392
|
const short = msg.substring(0, 200);
|
|
329
393
|
// 仅显示层格式化:时间戳 + 彩色固定宽度前缀,内容不变。
|
|
330
|
-
//
|
|
331
|
-
|
|
332
|
-
printProcessLine(displayId, short);
|
|
394
|
+
// 前缀用启动时派生的 [程序名:端口] 标签,解析失败已退回 name||id。
|
|
395
|
+
printProcessLine(displayLabel, short);
|
|
333
396
|
// 环形缓冲:保留最近 N 行供 LLM 分析
|
|
334
397
|
const info = this.processes.get(id);
|
|
335
398
|
if (info) {
|
|
@@ -388,6 +451,7 @@ export class ProcessManager extends EventEmitter {
|
|
|
388
451
|
this.processes.set(id, {
|
|
389
452
|
cmdId: id,
|
|
390
453
|
cmdDef,
|
|
454
|
+
displayLabel,
|
|
391
455
|
child,
|
|
392
456
|
startTime,
|
|
393
457
|
restartCount: 0,
|
|
@@ -466,7 +530,8 @@ export class ProcessManager extends EventEmitter {
|
|
|
466
530
|
|
|
467
531
|
result.push({
|
|
468
532
|
cmdId,
|
|
469
|
-
|
|
533
|
+
// 显示层 name 与日志前缀同源([程序名:端口]),web 页面进程列表与日志一致
|
|
534
|
+
name: info.displayLabel || info.cmdDef.name || cmdId,
|
|
470
535
|
pid,
|
|
471
536
|
status: info.child
|
|
472
537
|
? info.child.exitCode === null
|
package/src/watchdog/reporter.js
CHANGED
|
@@ -88,6 +88,8 @@ export function getClientConfig() {
|
|
|
88
88
|
maxComments: cfg.maxComments,
|
|
89
89
|
maxFollowing: cfg.maxFollowing,
|
|
90
90
|
maxFollowers: cfg.maxFollowers,
|
|
91
|
+
// 内存阈值覆盖(HealthChecker 用;顶层级键——cfg.browser 是 Edge 路径字符串,无法嵌套)
|
|
92
|
+
memoryThreshold: cfg.memoryThreshold,
|
|
91
93
|
};
|
|
92
94
|
}
|
|
93
95
|
} catch {
|