tt-help-cli-ycl 1.4.120 → 1.4.122
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/cli/attach.js +10 -2
- package/src/cli/precheck.js +10 -2
- package/src/watch/public/app.js +159 -63
- package/src/watch/public/index.html +1 -1
- package/src/watchdog/agent.js +12 -3
package/package.json
CHANGED
package/src/cli/attach.js
CHANGED
|
@@ -163,8 +163,16 @@ export async function handleAttach(options) {
|
|
|
163
163
|
) {
|
|
164
164
|
throw new CDNBlockedError(data.error);
|
|
165
165
|
}
|
|
166
|
-
//
|
|
167
|
-
|
|
166
|
+
// 其余失败(504 超时 / 503 / 502 / 500 等):抛出带状态码与错误详情的异常,
|
|
167
|
+
// 让外层日志显示真实原因(如 page.goto 超时)而非笼统"未获取到用户信息"。
|
|
168
|
+
// 行为不变:failCount++、不标记 needRefresh、服务端后续重新排队重试。
|
|
169
|
+
const detail =
|
|
170
|
+
(data && typeof data.error === "string" && data.error) ||
|
|
171
|
+
res.statusText ||
|
|
172
|
+
"unknown";
|
|
173
|
+
const err = new Error(`webserver ${res.status}: ${detail}`);
|
|
174
|
+
err.statusCode = res.status;
|
|
175
|
+
throw err;
|
|
168
176
|
}
|
|
169
177
|
// 业务结果:TikTok 明确 statusCode(209002 验证码等)→ 返回上层走"已记录"分支
|
|
170
178
|
if (data && data.error && data.tiktokStatusCode) {
|
package/src/cli/precheck.js
CHANGED
|
@@ -177,8 +177,16 @@ export async function handlePrecheck(options) {
|
|
|
177
177
|
) {
|
|
178
178
|
throw new CDNBlockedError(data.error);
|
|
179
179
|
}
|
|
180
|
-
//
|
|
181
|
-
|
|
180
|
+
// 其余失败(504 超时 / 503 / 502 / 500 等):抛出带状态码与错误详情的异常,
|
|
181
|
+
// 让外层日志显示真实原因(如 page.goto 超时)而非笼统"未获取到用户信息"。
|
|
182
|
+
// 行为不变:failCount++、不标记 needRefresh、服务端后续重新排队重试。
|
|
183
|
+
const detail =
|
|
184
|
+
(data && typeof data.error === "string" && data.error) ||
|
|
185
|
+
res.statusText ||
|
|
186
|
+
"unknown";
|
|
187
|
+
const err = new Error(`webserver ${res.status}: ${detail}`);
|
|
188
|
+
err.statusCode = res.status;
|
|
189
|
+
throw err;
|
|
182
190
|
}
|
|
183
191
|
// 业务结果:TikTok 明确 statusCode(209002 验证码等)→ 返回上层走"已记录"分支
|
|
184
192
|
if (data && data.error && data.tiktokStatusCode) {
|
package/src/watch/public/app.js
CHANGED
|
@@ -3989,86 +3989,182 @@ async function wdSetCountries() {
|
|
|
3989
3989
|
|
|
3990
3990
|
// ── 登录检测端口配置区域(看门狗面板中每个客户端卡片) ──
|
|
3991
3991
|
|
|
3992
|
+
// userId -> 详情展开状态(跨刷新保留)
|
|
3993
|
+
var _wdLoginExpanded = {};
|
|
3994
|
+
|
|
3995
|
+
function wdToggleLoginDetail(userId) {
|
|
3996
|
+
_wdLoginExpanded[userId] = !_wdLoginExpanded[userId];
|
|
3997
|
+
var el = document.getElementById("wdLoginDetail-" + wdEscId(userId));
|
|
3998
|
+
if (el) {
|
|
3999
|
+
el.style.display = _wdLoginExpanded[userId] ? "grid" : "none";
|
|
4000
|
+
}
|
|
4001
|
+
}
|
|
4002
|
+
|
|
4003
|
+
var _wdLoginStatusLabel = {
|
|
4004
|
+
logged_in: ["已登录", "#4ade80"],
|
|
4005
|
+
expired: ["已过期", "#fbbf24"],
|
|
4006
|
+
error: ["失败", "#f87171"],
|
|
4007
|
+
logged_out: ["未登录", "#f87171"],
|
|
4008
|
+
};
|
|
4009
|
+
|
|
4010
|
+
function wdLoginStatusLabel(s) {
|
|
4011
|
+
return _wdLoginStatusLabel[s] || ["未登录", "#888"];
|
|
4012
|
+
}
|
|
4013
|
+
|
|
3992
4014
|
function renderLoginSection(c, userId, loginPorts) {
|
|
3993
4015
|
if (!userId) return "";
|
|
3994
4016
|
var cfgEntry = (loginPorts && loginPorts[userId]) || {};
|
|
3995
4017
|
var configured =
|
|
3996
4018
|
cfgEntry.ports && cfgEntry.ports.length ? cfgEntry.ports : [];
|
|
3997
4019
|
var report = c.loginReport || null;
|
|
3998
|
-
|
|
4020
|
+
|
|
4021
|
+
// 配置按钮:只显示端口数量,完整列表放 title
|
|
4022
|
+
var cfgBtn = tag(
|
|
4023
|
+
"button",
|
|
4024
|
+
{
|
|
4025
|
+
onclick: "wdShowLoginPortsModal('" + esc(userId) + "')",
|
|
4026
|
+
style:
|
|
4027
|
+
"padding:2px 8px;font-size:11px;background:#333;color:#4ade80;" +
|
|
4028
|
+
"border:1px solid #444;border-radius:3px;cursor:pointer",
|
|
4029
|
+
title:
|
|
4030
|
+
"配置要定期检测登录状态的 CDP 端口(逗号分隔,留空清空)\n当前: " +
|
|
4031
|
+
(configured.length ? configured.join(",") : "无"),
|
|
4032
|
+
},
|
|
4033
|
+
(configured.length ? configured.length + "端口" : "未配置") + " ⚙️",
|
|
4034
|
+
);
|
|
4035
|
+
|
|
4036
|
+
var head =
|
|
4037
|
+
styled("span", "font-size:14px", "🔑") +
|
|
4038
|
+
styled("span", "color:#888", "登录检测:") +
|
|
4039
|
+
cfgBtn;
|
|
4040
|
+
|
|
4041
|
+
var wrapStyle =
|
|
4042
|
+
"margin-top:6px;padding:6px 8px;background:#111;border-radius:6px;" +
|
|
4043
|
+
"display:flex;align-items:center;gap:8px;font-size:12px;flex-wrap:wrap";
|
|
4044
|
+
|
|
3999
4045
|
if (!report) {
|
|
4000
|
-
|
|
4001
|
-
"
|
|
4002
|
-
|
|
4003
|
-
|
|
4046
|
+
return styled(
|
|
4047
|
+
"div",
|
|
4048
|
+
wrapStyle,
|
|
4049
|
+
head +
|
|
4050
|
+
styled(
|
|
4051
|
+
"span",
|
|
4052
|
+
"color:#555;margin-left:auto",
|
|
4053
|
+
configured.length ? "等待客户端上报..." : "",
|
|
4054
|
+
),
|
|
4004
4055
|
);
|
|
4005
|
-
}
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
? "#4ade80"
|
|
4021
|
-
: r.status === "expired"
|
|
4022
|
-
? "#fbbf24"
|
|
4023
|
-
: r.status === "error"
|
|
4024
|
-
? "#f87171"
|
|
4025
|
-
: "#888";
|
|
4026
|
-
var label =
|
|
4027
|
-
r.status === "logged_in"
|
|
4028
|
-
? "已登录"
|
|
4029
|
-
: r.status === "expired"
|
|
4030
|
-
? "已过期"
|
|
4031
|
-
: r.status === "error"
|
|
4032
|
-
? "失败"
|
|
4033
|
-
: "未登录";
|
|
4034
|
-
var txt =
|
|
4035
|
-
r.port +
|
|
4036
|
-
" " +
|
|
4037
|
-
label +
|
|
4038
|
-
(r.status === "logged_in" && r.account ? " @" + r.account : "");
|
|
4039
|
-
return styled(
|
|
4056
|
+
}
|
|
4057
|
+
|
|
4058
|
+
var et = report.checkedAt
|
|
4059
|
+
? new Date(report.checkedAt).toLocaleTimeString("zh-CN", {
|
|
4060
|
+
hour: "2-digit",
|
|
4061
|
+
minute: "2-digit",
|
|
4062
|
+
})
|
|
4063
|
+
: "?";
|
|
4064
|
+
|
|
4065
|
+
if (report.error) {
|
|
4066
|
+
return styled(
|
|
4067
|
+
"div",
|
|
4068
|
+
wrapStyle,
|
|
4069
|
+
head +
|
|
4070
|
+
styled(
|
|
4040
4071
|
"span",
|
|
4041
|
-
"color:"
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4072
|
+
"color:#f87171;margin-left:auto",
|
|
4073
|
+
"最近检测(" + et + ") 失败",
|
|
4074
|
+
),
|
|
4075
|
+
);
|
|
4076
|
+
}
|
|
4077
|
+
|
|
4078
|
+
var ports = Array.isArray(report.ports) ? report.ports : [];
|
|
4079
|
+
if (!ports.length) {
|
|
4080
|
+
return styled(
|
|
4081
|
+
"div",
|
|
4082
|
+
wrapStyle,
|
|
4083
|
+
head + styled("span", "color:#555;margin-left:auto", "无结果"),
|
|
4084
|
+
);
|
|
4085
|
+
}
|
|
4086
|
+
|
|
4087
|
+
// 汇总统计 + 异常端口内联提示
|
|
4088
|
+
var ok = 0;
|
|
4089
|
+
var abnormal = [];
|
|
4090
|
+
var hasError = false;
|
|
4091
|
+
for (var i = 0; i < ports.length; i++) {
|
|
4092
|
+
var r = ports[i];
|
|
4093
|
+
if (r.status === "logged_in") {
|
|
4094
|
+
ok++;
|
|
4095
|
+
} else {
|
|
4096
|
+
var lb = wdLoginStatusLabel(r.status);
|
|
4097
|
+
abnormal.push({ port: r.port, label: lb[0] });
|
|
4098
|
+
if (r.status === "error" || r.status === "logged_out") hasError = true;
|
|
4099
|
+
}
|
|
4100
|
+
}
|
|
4101
|
+
var sumColor = abnormal.length
|
|
4102
|
+
? hasError
|
|
4103
|
+
? "#f87171"
|
|
4104
|
+
: "#fbbf24"
|
|
4105
|
+
: "#4ade80";
|
|
4106
|
+
var summaryTxt =
|
|
4107
|
+
ok + "/" + ports.length + " 已登录" + (abnormal.length ? "" : " ✓");
|
|
4108
|
+
if (abnormal.length) {
|
|
4109
|
+
var shown = abnormal
|
|
4110
|
+
.slice(0, 3)
|
|
4111
|
+
.map(function (a) {
|
|
4112
|
+
return a.port + " " + a.label;
|
|
4046
4113
|
})
|
|
4047
|
-
.join(" ");
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
badges;
|
|
4051
|
-
} else {
|
|
4052
|
-
reportHtml = styled("span", "color:#555;margin-left:auto", "无结果");
|
|
4114
|
+
.join(", ");
|
|
4115
|
+
summaryTxt +=
|
|
4116
|
+
" ⚠ " + shown + (abnormal.length > 3 ? " +" + (abnormal.length - 3) : "");
|
|
4053
4117
|
}
|
|
4118
|
+
|
|
4119
|
+
// 明细(默认折叠,点汇总展开/收起)
|
|
4120
|
+
var detailHtml = "";
|
|
4121
|
+
for (var j = 0; j < ports.length; j++) {
|
|
4122
|
+
var r2 = ports[j];
|
|
4123
|
+
var lb2 = wdLoginStatusLabel(r2.status);
|
|
4124
|
+
detailHtml += styled(
|
|
4125
|
+
"div",
|
|
4126
|
+
"display:flex;gap:6px;align-items:center;font-size:11px",
|
|
4127
|
+
styled("span", "color:#888;min-width:42px", r2.port) +
|
|
4128
|
+
styled("span", "color:" + lb2[1], lb2[0]) +
|
|
4129
|
+
(r2.status === "logged_in" && r2.account
|
|
4130
|
+
? styled("span", "color:#4ade80", "@" + r2.account)
|
|
4131
|
+
: ""),
|
|
4132
|
+
);
|
|
4133
|
+
}
|
|
4134
|
+
|
|
4054
4135
|
return styled(
|
|
4055
4136
|
"div",
|
|
4056
|
-
"margin-top:6px;padding:6px 8px;background:#111;border-radius:6px;"
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4137
|
+
"margin-top:6px;padding:6px 8px;background:#111;border-radius:6px;font-size:12px",
|
|
4138
|
+
styled(
|
|
4139
|
+
"div",
|
|
4140
|
+
"display:flex;align-items:center;gap:8px;flex-wrap:wrap",
|
|
4141
|
+
head +
|
|
4142
|
+
tag(
|
|
4143
|
+
"span",
|
|
4144
|
+
{
|
|
4145
|
+
style:
|
|
4146
|
+
"color:" +
|
|
4147
|
+
sumColor +
|
|
4148
|
+
";margin-left:auto;cursor:pointer;text-decoration:underline dotted",
|
|
4149
|
+
onclick: "wdToggleLoginDetail('" + esc(userId) + "')",
|
|
4150
|
+
title: "点击展开/收起各端口明细",
|
|
4151
|
+
},
|
|
4152
|
+
summaryTxt,
|
|
4153
|
+
) +
|
|
4154
|
+
styled("span", "color:#555", "最近 " + et),
|
|
4155
|
+
) +
|
|
4060
4156
|
tag(
|
|
4061
|
-
"
|
|
4157
|
+
"div",
|
|
4062
4158
|
{
|
|
4063
|
-
|
|
4159
|
+
id: "wdLoginDetail-" + wdEscId(userId),
|
|
4064
4160
|
style:
|
|
4065
|
-
"
|
|
4066
|
-
"
|
|
4067
|
-
|
|
4161
|
+
"display:" +
|
|
4162
|
+
(_wdLoginExpanded[userId] ? "grid" : "none") +
|
|
4163
|
+
";grid-template-columns:repeat(auto-fill,minmax(170px,1fr));" +
|
|
4164
|
+
"gap:3px 16px;margin-top:6px;padding-top:6px;border-top:1px dashed #2a2a3a",
|
|
4068
4165
|
},
|
|
4069
|
-
|
|
4070
|
-
)
|
|
4071
|
-
reportHtml,
|
|
4166
|
+
detailHtml,
|
|
4167
|
+
),
|
|
4072
4168
|
);
|
|
4073
4169
|
}
|
|
4074
4170
|
|
package/src/watchdog/agent.js
CHANGED
|
@@ -30,6 +30,7 @@ import { fileURLToPath } from "url";
|
|
|
30
30
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
31
31
|
const homeDir = homedir() || process.env.HOME || process.env.USERPROFILE || "";
|
|
32
32
|
const TT_HELP_CONFIG = join(homeDir, ".tt-help.json");
|
|
33
|
+
const IS_WIN = process.platform === "win32";
|
|
33
34
|
|
|
34
35
|
// ── 业务日志错误统计 → 触发节点切换 ──
|
|
35
36
|
// 识别采集程序日志里已有的错误分类标签(explore 等已打标),连续错误达标则切换出口节点
|
|
@@ -612,9 +613,17 @@ export class WatchdogAgent {
|
|
|
612
613
|
if (cfg.proxy) args.push("--proxy", cfg.proxy);
|
|
613
614
|
|
|
614
615
|
const { stdout, stderr, code } = await new Promise((resolve) => {
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
616
|
+
// Windows 下 tt-help 是 .cmd 包装,必须 shell:true 才能被 cmd.exe 解析
|
|
617
|
+
// (与 process-manager.startCommand 的 Windows 处理保持一致)
|
|
618
|
+
const child = IS_WIN
|
|
619
|
+
? spawn([cmd, ...args].join(" "), {
|
|
620
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
621
|
+
shell: true,
|
|
622
|
+
windowsHide: true,
|
|
623
|
+
})
|
|
624
|
+
: spawn(cmd, args, {
|
|
625
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
626
|
+
});
|
|
618
627
|
let out = "",
|
|
619
628
|
err = "";
|
|
620
629
|
child.stdout.on("data", (d) => (out += d));
|