tt-help-cli-ycl 1.4.154 → 1.4.155
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-daemon-whitelist.mjs +308 -0
- package/scripts/test-explore-captcha-wait.mjs +170 -0
- package/src/clash/daemon.js +125 -35
- package/src/cli/clash-daemon.js +1 -0
- package/src/cli/explore.js +67 -19
- package/src/lib/args.js +8 -0
- package/src/lib/constants.js +3 -0
- package/src/scraper/modules/captcha-handler.js +37 -21
- package/src/watch/public/app.js +97 -13
- package/src/watch/public/index.html +38 -1
- package/src/watch/watchdog-server.js +39 -1
package/src/watch/public/app.js
CHANGED
|
@@ -4159,6 +4159,7 @@ function cmdObj(cmd) {
|
|
|
4159
4159
|
enabled: true,
|
|
4160
4160
|
maxUptimeSeconds: 0,
|
|
4161
4161
|
maxMemoryMB: 0,
|
|
4162
|
+
resumeAt: 0,
|
|
4162
4163
|
};
|
|
4163
4164
|
return {
|
|
4164
4165
|
id: cmd.id || "",
|
|
@@ -4167,6 +4168,7 @@ function cmdObj(cmd) {
|
|
|
4167
4168
|
enabled: cmd.enabled !== false,
|
|
4168
4169
|
maxUptimeSeconds: cmd.maxUptimeSeconds || 0,
|
|
4169
4170
|
maxMemoryMB: cmd.maxMemoryMB || 0,
|
|
4171
|
+
resumeAt: cmd.resumeAt || 0,
|
|
4170
4172
|
};
|
|
4171
4173
|
}
|
|
4172
4174
|
|
|
@@ -4686,14 +4688,36 @@ function wdShowCountryModal(userId) {
|
|
|
4686
4688
|
cc +
|
|
4687
4689
|
'" ' +
|
|
4688
4690
|
checked +
|
|
4689
|
-
"/>
|
|
4691
|
+
' onchange="wdSyncAllCountriesCb()"/> ' +
|
|
4690
4692
|
cc +
|
|
4691
4693
|
"</label>";
|
|
4692
4694
|
}
|
|
4693
4695
|
document.getElementById("wdCountryList").innerHTML = html;
|
|
4696
|
+
wdSyncAllCountriesCb();
|
|
4694
4697
|
document.getElementById("wdCountryModal").style.display = "flex";
|
|
4695
4698
|
}
|
|
4696
4699
|
|
|
4700
|
+
// 全选/全不选所有国家
|
|
4701
|
+
function wdToggleAllCountries(cb) {
|
|
4702
|
+
var cbs = document.querySelectorAll(".wd-country-cb");
|
|
4703
|
+
for (var i = 0; i < cbs.length; i++) cbs[i].checked = cb.checked;
|
|
4704
|
+
}
|
|
4705
|
+
|
|
4706
|
+
// 根据国家勾选情况同步“全选”框状态(全部勾中→勾上,否则取消)
|
|
4707
|
+
function wdSyncAllCountriesCb() {
|
|
4708
|
+
var allCb = document.getElementById("wdCountryAllCb");
|
|
4709
|
+
if (!allCb) return;
|
|
4710
|
+
var cbs = document.querySelectorAll(".wd-country-cb");
|
|
4711
|
+
var all = cbs.length > 0;
|
|
4712
|
+
for (var i = 0; i < cbs.length; i++) {
|
|
4713
|
+
if (!cbs[i].checked) {
|
|
4714
|
+
all = false;
|
|
4715
|
+
break;
|
|
4716
|
+
}
|
|
4717
|
+
}
|
|
4718
|
+
allCb.checked = all;
|
|
4719
|
+
}
|
|
4720
|
+
|
|
4697
4721
|
async function wdSetCountries() {
|
|
4698
4722
|
var cbs = document.querySelectorAll(".wd-country-cb:checked");
|
|
4699
4723
|
var countries = [];
|
|
@@ -5462,6 +5486,27 @@ function renderCmdRows(myCmds, userId, procs, isOnline, llmAnalysis, clientId) {
|
|
|
5462
5486
|
var label =
|
|
5463
5487
|
c.name || (c.cmd.length > 40 ? c.cmd.substring(0, 37) + "..." : c.cmd);
|
|
5464
5488
|
var cmdTitle = c.name ? c.name + "\n" + c.cmd : c.cmd;
|
|
5489
|
+
// 禁用中的定时恢复徽标(点击可改时间)
|
|
5490
|
+
var resumeBadge = "";
|
|
5491
|
+
if (!c.enabled && c.resumeAt) {
|
|
5492
|
+
var rd = new Date(c.resumeAt);
|
|
5493
|
+
var rhh = ("0" + rd.getHours()).slice(-2);
|
|
5494
|
+
var rmm = ("0" + rd.getMinutes()).slice(-2);
|
|
5495
|
+
var rtxt =
|
|
5496
|
+
c.resumeAt > Date.now()
|
|
5497
|
+
? "⏰ " + rhh + ":" + rmm + " 自动启动"
|
|
5498
|
+
: "⏰ 待心跳恢复";
|
|
5499
|
+
resumeBadge = tag(
|
|
5500
|
+
"span",
|
|
5501
|
+
{
|
|
5502
|
+
onclick: "wdShowCmdOffModal('" + esc(userId) + "'," + idx + ")",
|
|
5503
|
+
style:
|
|
5504
|
+
"color:#fbbf24;font-size:10px;margin-left:6px;cursor:pointer;white-space:nowrap",
|
|
5505
|
+
title: "点击修改自动启动时间",
|
|
5506
|
+
},
|
|
5507
|
+
rtxt,
|
|
5508
|
+
);
|
|
5509
|
+
}
|
|
5465
5510
|
var pid = p ? p.pid : "-";
|
|
5466
5511
|
var mem = p && p.memoryMB != null ? p.memoryMB + "MB" : "-";
|
|
5467
5512
|
var uptime = p ? p.uptimeDisplay || p.uptimeSeconds + "s" : "-";
|
|
@@ -5493,7 +5538,8 @@ function renderCmdRows(myCmds, userId, procs, isOnline, llmAnalysis, clientId) {
|
|
|
5493
5538
|
"color:#555;font-size:10px;margin-left:4px;font-family:monospace",
|
|
5494
5539
|
c.cmd.substring(0, 30) + (c.cmd.length > 30 ? "..." : ""),
|
|
5495
5540
|
)
|
|
5496
|
-
: "")
|
|
5541
|
+
: "") +
|
|
5542
|
+
resumeBadge,
|
|
5497
5543
|
) +
|
|
5498
5544
|
tag(
|
|
5499
5545
|
"span",
|
|
@@ -5809,7 +5855,7 @@ function wdModalSave() {
|
|
|
5809
5855
|
var myCmds = (allCmds[_wdModalUserId] || []).slice();
|
|
5810
5856
|
|
|
5811
5857
|
if (_wdModalEditIdx >= 0) {
|
|
5812
|
-
// 编辑模式:保留已有 ID
|
|
5858
|
+
// 编辑模式:保留已有 ID 与定时恢复状态
|
|
5813
5859
|
if (_wdModalEditIdx < myCmds.length) {
|
|
5814
5860
|
var oldObj = cmdObj(myCmds[_wdModalEditIdx]);
|
|
5815
5861
|
myCmds[_wdModalEditIdx] = {
|
|
@@ -5819,6 +5865,7 @@ function wdModalSave() {
|
|
|
5819
5865
|
enabled: oldObj.enabled,
|
|
5820
5866
|
maxUptimeSeconds: maxUp,
|
|
5821
5867
|
maxMemoryMB: maxMem,
|
|
5868
|
+
resumeAt: oldObj.resumeAt,
|
|
5822
5869
|
};
|
|
5823
5870
|
}
|
|
5824
5871
|
} else {
|
|
@@ -5913,17 +5960,13 @@ function wdToggleClientCmd(userId, idx) {
|
|
|
5913
5960
|
var myCmds = (allCmds[userId] || []).slice();
|
|
5914
5961
|
if (idx >= myCmds.length) return;
|
|
5915
5962
|
var cmd = myCmds[idx];
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
enabled: false,
|
|
5921
|
-
maxUptimeSeconds: 0,
|
|
5922
|
-
maxMemoryMB: 0,
|
|
5923
|
-
};
|
|
5924
|
-
} else {
|
|
5925
|
-
cmd.enabled = !cmd.enabled;
|
|
5963
|
+
// 启用中(含老数据字符串命令)→ 弹关闭定时弹窗;已禁用 → 立即开启
|
|
5964
|
+
if (typeof cmd === "string" || cmdObj(cmd).enabled) {
|
|
5965
|
+
wdShowCmdOffModal(userId, idx);
|
|
5966
|
+
return;
|
|
5926
5967
|
}
|
|
5968
|
+
cmd.enabled = true;
|
|
5969
|
+
cmd.resumeAt = 0;
|
|
5927
5970
|
// 立即更新按钮为 loading 状态
|
|
5928
5971
|
var btn = event.target;
|
|
5929
5972
|
if (btn && btn.tagName === "BUTTON") {
|
|
@@ -5934,6 +5977,47 @@ function wdToggleClientCmd(userId, idx) {
|
|
|
5934
5977
|
wdSaveClientCmds(userId, myCmds);
|
|
5935
5978
|
}
|
|
5936
5979
|
|
|
5980
|
+
// ── 关闭命令定时弹窗 ──
|
|
5981
|
+
|
|
5982
|
+
var _wdOffUserId = "";
|
|
5983
|
+
var _wdOffIdx = -1;
|
|
5984
|
+
|
|
5985
|
+
function wdShowCmdOffModal(userId, idx) {
|
|
5986
|
+
if (!_wdData) return;
|
|
5987
|
+
var myCmds = (_wdData.clientCommands || {})[userId] || [];
|
|
5988
|
+
if (idx >= myCmds.length) return;
|
|
5989
|
+
var c = cmdObj(myCmds[idx]);
|
|
5990
|
+
_wdOffUserId = userId;
|
|
5991
|
+
_wdOffIdx = idx;
|
|
5992
|
+
document.getElementById("wdCmdOffName").textContent = c.name || c.cmd;
|
|
5993
|
+
document.getElementById("wdCmdOffMinutes").value = "";
|
|
5994
|
+
document.getElementById("wdCmdOffModal").style.display = "flex";
|
|
5995
|
+
}
|
|
5996
|
+
|
|
5997
|
+
function wdCmdOffCancel() {
|
|
5998
|
+
document.getElementById("wdCmdOffModal").style.display = "none";
|
|
5999
|
+
}
|
|
6000
|
+
|
|
6001
|
+
// minutes: 数字分钟;0=永久关闭;undefined=读自定义输入框。
|
|
6002
|
+
// 只把 resumeDelayMinutes 传给服务端,恢复时间由服务端按服务器时钟换算。
|
|
6003
|
+
function wdCmdOffConfirm(minutes) {
|
|
6004
|
+
var mins =
|
|
6005
|
+
minutes !== undefined
|
|
6006
|
+
? Number(minutes) || 0
|
|
6007
|
+
: parseInt(document.getElementById("wdCmdOffMinutes").value, 10) || 0;
|
|
6008
|
+
document.getElementById("wdCmdOffModal").style.display = "none";
|
|
6009
|
+
if (!_wdData) return;
|
|
6010
|
+
var allCmds = _wdData.clientCommands || {};
|
|
6011
|
+
var myCmds = (allCmds[_wdOffUserId] || []).slice();
|
|
6012
|
+
if (_wdOffIdx >= myCmds.length) return;
|
|
6013
|
+
var c = cmdObj(myCmds[_wdOffIdx]);
|
|
6014
|
+
c.enabled = false;
|
|
6015
|
+
c.resumeAt = 0;
|
|
6016
|
+
c.resumeDelayMinutes = mins;
|
|
6017
|
+
myCmds[_wdOffIdx] = c;
|
|
6018
|
+
wdSaveClientCmds(_wdOffUserId, myCmds);
|
|
6019
|
+
}
|
|
6020
|
+
|
|
5937
6021
|
var _wdSaving = false;
|
|
5938
6022
|
|
|
5939
6023
|
function wdShowSaving() {
|
|
@@ -690,6 +690,37 @@
|
|
|
690
690
|
</div>
|
|
691
691
|
</div>
|
|
692
692
|
|
|
693
|
+
<!-- 命令关闭定时弹窗 -->
|
|
694
|
+
<div id="wdCmdOffModal"
|
|
695
|
+
style="display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.6);z-index:9999;align-items:center;justify-content:center">
|
|
696
|
+
<div style="background:#1e1e2e;border:1px solid #333;border-radius:12px;padding:24px;width:90%;max-width:420px">
|
|
697
|
+
<div style="font-size:16px;color:#f59e0b;margin-bottom:4px">⏸ 关闭命令</div>
|
|
698
|
+
<div style="color:#888;font-size:12px;margin-bottom:12px"><span id="wdCmdOffName"
|
|
699
|
+
style="color:#e0e0e0;font-family:monospace"></span><br>关闭后经过所选时间自动重新启动(按服务器时钟计时,到点随心跳生效):</div>
|
|
700
|
+
<div style="display:flex;gap:8px;margin-bottom:12px;flex-wrap:wrap">
|
|
701
|
+
<button onclick="wdCmdOffConfirm(30)"
|
|
702
|
+
style="padding:8px 14px;background:#333;color:#e0e0e0;border:none;border-radius:6px;cursor:pointer;font-size:12px">30分钟</button>
|
|
703
|
+
<button onclick="wdCmdOffConfirm(60)"
|
|
704
|
+
style="padding:8px 14px;background:#333;color:#e0e0e0;border:none;border-radius:6px;cursor:pointer;font-size:12px">1小时</button>
|
|
705
|
+
<button onclick="wdCmdOffConfirm(120)"
|
|
706
|
+
style="padding:8px 14px;background:#333;color:#e0e0e0;border:none;border-radius:6px;cursor:pointer;font-size:12px">2小时</button>
|
|
707
|
+
<button onclick="wdCmdOffConfirm(0)"
|
|
708
|
+
style="padding:8px 14px;background:#7f1d1d;color:#fca5a5;border:none;border-radius:6px;cursor:pointer;font-size:12px">永久关闭</button>
|
|
709
|
+
</div>
|
|
710
|
+
<div style="display:flex;gap:8px;align-items:center;margin-bottom:16px">
|
|
711
|
+
<label style="color:#888;font-size:12px;white-space:nowrap">自定义(分钟)</label>
|
|
712
|
+
<input id="wdCmdOffMinutes" type="number" min="1"
|
|
713
|
+
style="width:90px;padding:6px;background:#111;border:1px solid #333;color:#e0e0e0;border-radius:6px;font-size:12px;box-sizing:border-box">
|
|
714
|
+
<button onclick="wdCmdOffConfirm()"
|
|
715
|
+
style="padding:8px 14px;background:#f59e0b;color:#000;border:none;border-radius:6px;cursor:pointer;font-size:12px;font-weight:600">确定</button>
|
|
716
|
+
</div>
|
|
717
|
+
<div style="display:flex;justify-content:flex-end">
|
|
718
|
+
<button onclick="wdCmdOffCancel()"
|
|
719
|
+
style="padding:8px 16px;background:#333;color:#ccc;border:none;border-radius:6px;cursor:pointer;font-size:12px">取消</button>
|
|
720
|
+
</div>
|
|
721
|
+
</div>
|
|
722
|
+
</div>
|
|
723
|
+
|
|
693
724
|
<!-- 客户端配置编辑弹窗 -->
|
|
694
725
|
<div id="wdConfigModal"
|
|
695
726
|
style="display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.6);z-index:9999;align-items:center;justify-content:center">
|
|
@@ -757,6 +788,12 @@
|
|
|
757
788
|
<div style="font-size:16px;color:#fbbf24;margin-bottom:4px">🌍 节点国家配置 — <span id="wdCountryUserId"></span></div>
|
|
758
789
|
<div style="color:#888;font-size:12px;margin-bottom:12px">选择该客户端可用的代理节点国家(可多选)。<br>未配置时默认使用 <b
|
|
759
790
|
style="color:#fbbf24">US</b>。修改后客户端将在下次心跳时生效。</div>
|
|
791
|
+
<div style="margin-bottom:8px">
|
|
792
|
+
<label style="display:inline-flex;align-items:center;gap:6px;color:#e0e0e0;font-size:12px;cursor:pointer">
|
|
793
|
+
<input type="checkbox" id="wdCountryAllCb" onchange="wdToggleAllCountries(this)"
|
|
794
|
+
style="width:14px;height:14px"> 全选
|
|
795
|
+
</label>
|
|
796
|
+
</div>
|
|
760
797
|
<div id="wdCountryList"
|
|
761
798
|
style="padding:10px;background:#16161e;border:1px solid #333;border-radius:8px;min-height:120px">加载中...</div>
|
|
762
799
|
<div style="display:flex;justify-content:flex-end;gap:8px;margin-top:16px">
|
|
@@ -882,7 +919,7 @@
|
|
|
882
919
|
</div>
|
|
883
920
|
</div>
|
|
884
921
|
|
|
885
|
-
<script src="app.js?v=
|
|
922
|
+
<script src="app.js?v=20260921a"></script>
|
|
886
923
|
</body>
|
|
887
924
|
|
|
888
925
|
</html>
|
|
@@ -195,10 +195,23 @@ function setClientCommands(userId, commands) {
|
|
|
195
195
|
enabled: true,
|
|
196
196
|
maxUptimeSeconds: 0,
|
|
197
197
|
maxMemoryMB: 0,
|
|
198
|
+
resumeAt: 0,
|
|
198
199
|
};
|
|
199
200
|
}
|
|
200
201
|
const obj = { ...c, cmd: (c.cmd || "").replace(/^[▶\n\r\s]+/, "").trim() };
|
|
201
202
|
if (!obj.id) obj.id = String(Date.now() + nextId++);
|
|
203
|
+
// 关闭延迟启动:resumeDelayMinutes 是前端显式信号(本次关闭操作指定的分钟数)。
|
|
204
|
+
// 恢复时间一律按服务器时钟换算,避免浏览器时钟偏差导致提前/延后恢复。
|
|
205
|
+
if (obj.resumeDelayMinutes !== undefined) {
|
|
206
|
+
const mins = Number(obj.resumeDelayMinutes) || 0;
|
|
207
|
+
obj.resumeAt =
|
|
208
|
+
obj.enabled === false && mins > 0 ? Date.now() + mins * 60000 : 0;
|
|
209
|
+
delete obj.resumeDelayMinutes;
|
|
210
|
+
} else if (obj.enabled !== false) {
|
|
211
|
+
obj.resumeAt = 0; // 启用的命令不允许残留恢复时间
|
|
212
|
+
} else {
|
|
213
|
+
obj.resumeAt = obj.resumeAt || 0; // 未改动的禁用命令保留原定时
|
|
214
|
+
}
|
|
202
215
|
return obj;
|
|
203
216
|
});
|
|
204
217
|
const cfg = loadConfig();
|
|
@@ -223,6 +236,30 @@ function getAllClientCommandsRaw() {
|
|
|
223
236
|
return cfg.clientCommands || {};
|
|
224
237
|
}
|
|
225
238
|
|
|
239
|
+
// 命令到点自动恢复:禁用且 resumeAt 已过 → 置回启用并写盘。
|
|
240
|
+
// 心跳前惰性触发(~15s 粒度足够);客户端零改动,恢复后随本次心跳正常下发。
|
|
241
|
+
function autoResumeCommands(userId) {
|
|
242
|
+
if (!userId) return;
|
|
243
|
+
const cfg = loadConfig();
|
|
244
|
+
const cmds = cfg.clientCommands && cfg.clientCommands[userId];
|
|
245
|
+
if (!cmds || !cmds.length) return;
|
|
246
|
+
const now = Date.now();
|
|
247
|
+
let resumed = [];
|
|
248
|
+
for (const c of cmds) {
|
|
249
|
+
if (c && c.enabled === false && c.resumeAt && c.resumeAt <= now) {
|
|
250
|
+
c.enabled = true;
|
|
251
|
+
c.resumeAt = 0;
|
|
252
|
+
resumed.push(c.id || c.cmd);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (resumed.length) {
|
|
256
|
+
saveConfig(cfg);
|
|
257
|
+
console.error(
|
|
258
|
+
`[watchdog ${new Date().toLocaleTimeString("zh-CN", { hour12: false })}] ${userId} 命令到点自动恢复: ${resumed.join(", ")}`,
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
226
263
|
// ── 客户端配置管理(用于远程下发 ~/.tt-help.json 配置) ──
|
|
227
264
|
|
|
228
265
|
function getClientConfigStored(userId) {
|
|
@@ -435,8 +472,9 @@ async function handleHeartbeat(params, body, res) {
|
|
|
435
472
|
loginReport: body.loginReport || null,
|
|
436
473
|
});
|
|
437
474
|
|
|
438
|
-
// 按 userId
|
|
475
|
+
// 按 userId 返回该客户端的专属命令列表(先处理到点自动恢复)
|
|
439
476
|
const userId = (body.config && body.config.userId) || "";
|
|
477
|
+
autoResumeCommands(userId);
|
|
440
478
|
const commands = getClientCommands(userId);
|
|
441
479
|
|
|
442
480
|
// 幂等下发节点国家配置(默认 US)
|