tt-help-cli-ycl 1.4.134 → 1.4.136
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/backup-db.mjs +73 -0
- package/scripts/check-queues.sh +37 -0
- package/scripts/migrate-jobs-done.mjs +124 -0
- package/scripts/migrate-tags-db.mjs +163 -0
- package/src/cli/tag.js +68 -9
- package/src/lib/tag-discover.js +21 -0
- package/src/watch/data-store.js +131 -77
- package/src/watch/db-crud.js +35 -10
- package/src/watch/db-schema.js +249 -100
- package/src/watch/db-stats.js +53 -23
- package/src/watch/db-tags.js +5 -2
- package/src/watch/llm-config.js +31 -1
- package/src/watch/public/app.js +162 -11
- package/src/watch/public/index.html +19 -1
- package/src/watch/server.js +338 -62
- package/src/watch/tag-selflearn.js +2 -1
- package/src/watch/watchdog-server.js +129 -1
- package/src/watchdog/agent.js +76 -0
- package/src/watchdog/process-manager.js +21 -0
- package/src/watchdog/self-log-buffer.js +25 -0
package/src/watch/public/app.js
CHANGED
|
@@ -1424,6 +1424,12 @@ async function fetchLlmConfig() {
|
|
|
1424
1424
|
// 根据当前 provider 渲染模型下拉
|
|
1425
1425
|
renderModelSelect(data.provider, data.model);
|
|
1426
1426
|
|
|
1427
|
+
// 如果选择了 none,则禁用相关输入并显示已禁用状态
|
|
1428
|
+
const isNone = data.provider === "none";
|
|
1429
|
+
if (modelSel) modelSel.disabled = !!isNone;
|
|
1430
|
+
if (urlInput) urlInput.disabled = !!isNone;
|
|
1431
|
+
if (keyInput) keyInput.disabled = !!isNone;
|
|
1432
|
+
|
|
1427
1433
|
if (urlInput) urlInput.value = data.url || "";
|
|
1428
1434
|
if (keyInput) {
|
|
1429
1435
|
keyInput.value = data.hasApiKey ? data.apiKeyHint : "";
|
|
@@ -1436,8 +1442,13 @@ async function fetchLlmConfig() {
|
|
|
1436
1442
|
}
|
|
1437
1443
|
if (status) {
|
|
1438
1444
|
const provider = _llmProvidersCache.find((p) => p.id === data.provider);
|
|
1439
|
-
|
|
1440
|
-
|
|
1445
|
+
if (isNone) {
|
|
1446
|
+
status.textContent = `当前: LLM 已禁用`;
|
|
1447
|
+
status.className = "settings-status warn";
|
|
1448
|
+
} else {
|
|
1449
|
+
status.textContent = `当前: ${provider?.name || data.provider} / ${data.model}`;
|
|
1450
|
+
status.className = "settings-status ok";
|
|
1451
|
+
}
|
|
1441
1452
|
}
|
|
1442
1453
|
} catch (e) {
|
|
1443
1454
|
console.error("fetchLlmConfig error:", e);
|
|
@@ -3824,7 +3835,22 @@ function renderClientCard(
|
|
|
3824
3835
|
"span",
|
|
3825
3836
|
"color:#555;font-size:12px;margin-left:8px",
|
|
3826
3837
|
c.hostname || "",
|
|
3827
|
-
)
|
|
3838
|
+
) +
|
|
3839
|
+
(online && c.clientId
|
|
3840
|
+
? tag(
|
|
3841
|
+
"button",
|
|
3842
|
+
{
|
|
3843
|
+
onclick:
|
|
3844
|
+
"wdShowLogsModal('" +
|
|
3845
|
+
esc(c.clientId) +
|
|
3846
|
+
"','__watchdog__','看门狗自身日志')",
|
|
3847
|
+
style:
|
|
3848
|
+
"padding:2px 8px;font-size:11px;background:#333;color:#a78bfa;border:1px solid #444;border-radius:3px;cursor:pointer;margin-left:8px",
|
|
3849
|
+
title: "查看看门狗自身最近日志(后台按需拉取)",
|
|
3850
|
+
},
|
|
3851
|
+
"📄 看门狗日志",
|
|
3852
|
+
)
|
|
3853
|
+
: ""),
|
|
3828
3854
|
) +
|
|
3829
3855
|
styled(
|
|
3830
3856
|
"div",
|
|
@@ -3859,7 +3885,7 @@ function renderClientCard(
|
|
|
3859
3885
|
renderErrorTriggerSection(c, userId, errorTriggers) +
|
|
3860
3886
|
renderLoginSection(c, userId, loginPorts) +
|
|
3861
3887
|
renderProxySection(c, userId) +
|
|
3862
|
-
renderCmdRows(myCmds, userId, procs, online, c.llmAnalysis),
|
|
3888
|
+
renderCmdRows(myCmds, userId, procs, online, c.llmAnalysis, c.clientId),
|
|
3863
3889
|
);
|
|
3864
3890
|
}
|
|
3865
3891
|
|
|
@@ -4503,7 +4529,7 @@ function wdSwitchNode(userId, node, port) {
|
|
|
4503
4529
|
}
|
|
4504
4530
|
|
|
4505
4531
|
// 统一的命令行视图:每条命令一行,含运行状态
|
|
4506
|
-
function renderCmdRows(myCmds, userId, procs, isOnline, llmAnalysis) {
|
|
4532
|
+
function renderCmdRows(myCmds, userId, procs, isOnline, llmAnalysis, clientId) {
|
|
4507
4533
|
var match = matchProcsToCmds(myCmds, procs);
|
|
4508
4534
|
var html = styled(
|
|
4509
4535
|
"div",
|
|
@@ -4531,8 +4557,8 @@ function renderCmdRows(myCmds, userId, procs, isOnline, llmAnalysis) {
|
|
|
4531
4557
|
// 表头
|
|
4532
4558
|
html += styled(
|
|
4533
4559
|
"div",
|
|
4534
|
-
"display:grid;grid-template-columns:1fr 50px 55px 55px 50px 80px;gap:4px;padding:4px 0;color:#666;font-size:11px;border-bottom:1px solid #222",
|
|
4535
|
-
"<span>命令行</span><span>PID</span><span>内存</span><span>运行</span><span>重启</span><span>操作</span>",
|
|
4560
|
+
"display:grid;grid-template-columns:1fr 50px 55px 55px 50px 80px 40px;gap:4px;padding:4px 0;color:#666;font-size:11px;border-bottom:1px solid #222",
|
|
4561
|
+
"<span>命令行</span><span>PID</span><span>内存</span><span>运行</span><span>重启</span><span>操作</span><span>日志</span>",
|
|
4536
4562
|
);
|
|
4537
4563
|
|
|
4538
4564
|
// 命令行
|
|
@@ -4554,7 +4580,7 @@ function renderCmdRows(myCmds, userId, procs, isOnline, llmAnalysis) {
|
|
|
4554
4580
|
var prefix = "wd-" + esc(userId) + "-" + idx;
|
|
4555
4581
|
html += styled(
|
|
4556
4582
|
"div",
|
|
4557
|
-
"display:grid;grid-template-columns:1fr 50px 55px 55px 50px 80px;gap:4px;padding:4px 0;align-items:center;font-size:12px;border-top:1px solid #1a1a2a",
|
|
4583
|
+
"display:grid;grid-template-columns:1fr 50px 55px 55px 50px 80px 40px;gap:4px;padding:4px 0;align-items:center;font-size:12px;border-top:1px solid #1a1a2a",
|
|
4558
4584
|
styled(
|
|
4559
4585
|
"span",
|
|
4560
4586
|
"color:" +
|
|
@@ -4636,7 +4662,26 @@ function renderCmdRows(myCmds, userId, procs, isOnline, llmAnalysis) {
|
|
|
4636
4662
|
},
|
|
4637
4663
|
"✕",
|
|
4638
4664
|
),
|
|
4639
|
-
)
|
|
4665
|
+
) +
|
|
4666
|
+
(clientId && isOnline && c.id
|
|
4667
|
+
? tag(
|
|
4668
|
+
"button",
|
|
4669
|
+
{
|
|
4670
|
+
onclick:
|
|
4671
|
+
"wdShowLogsModal('" +
|
|
4672
|
+
esc(clientId) +
|
|
4673
|
+
"','" +
|
|
4674
|
+
esc(c.id) +
|
|
4675
|
+
"','" +
|
|
4676
|
+
esc((c.name || c.id).substring(0, 50)) +
|
|
4677
|
+
"')",
|
|
4678
|
+
style:
|
|
4679
|
+
"padding:1px 5px;font-size:11px;background:#333;color:#a78bfa;border:1px solid #444;border-radius:2px;cursor:pointer",
|
|
4680
|
+
title: "查看该进程最近日志(后台按需拉取)",
|
|
4681
|
+
},
|
|
4682
|
+
"📄",
|
|
4683
|
+
)
|
|
4684
|
+
: ""),
|
|
4640
4685
|
);
|
|
4641
4686
|
|
|
4642
4687
|
// AI 分析结果
|
|
@@ -4667,7 +4712,7 @@ function renderCmdRows(myCmds, userId, procs, isOnline, llmAnalysis) {
|
|
|
4667
4712
|
var opre = "wd-" + esc(userId) + "-orphan-" + esc(String(oid));
|
|
4668
4713
|
html += styled(
|
|
4669
4714
|
"div",
|
|
4670
|
-
"display:grid;grid-template-columns:1fr 50px 55px 55px 50px 80px;gap:4px;padding:4px 0;align-items:center;font-size:12px;border-top:1px solid #1a1a2a;opacity:0.5",
|
|
4715
|
+
"display:grid;grid-template-columns:1fr 50px 55px 55px 50px 80px 40px;gap:4px;padding:4px 0;align-items:center;font-size:12px;border-top:1px solid #1a1a2a;opacity:0.5",
|
|
4671
4716
|
styled(
|
|
4672
4717
|
"span",
|
|
4673
4718
|
"color:#666;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-family:monospace",
|
|
@@ -4693,7 +4738,26 @@ function renderCmdRows(myCmds, userId, procs, isOnline, llmAnalysis) {
|
|
|
4693
4738
|
{ style: "color:#555", "data-wd-key": opre + "-rst" },
|
|
4694
4739
|
"0",
|
|
4695
4740
|
) +
|
|
4696
|
-
styled("span", "color:#555;font-size:11px", "孤立")
|
|
4741
|
+
styled("span", "color:#555;font-size:11px", "孤立") +
|
|
4742
|
+
(clientId && isOnline && p.cmdId
|
|
4743
|
+
? tag(
|
|
4744
|
+
"button",
|
|
4745
|
+
{
|
|
4746
|
+
onclick:
|
|
4747
|
+
"wdShowLogsModal('" +
|
|
4748
|
+
esc(clientId) +
|
|
4749
|
+
"','" +
|
|
4750
|
+
esc(String(p.cmdId)) +
|
|
4751
|
+
"','" +
|
|
4752
|
+
esc(String(p.name || p.cmdId).substring(0, 50)) +
|
|
4753
|
+
"')",
|
|
4754
|
+
style:
|
|
4755
|
+
"padding:1px 5px;font-size:11px;background:#333;color:#a78bfa;border:1px solid #444;border-radius:2px;cursor:pointer",
|
|
4756
|
+
title: "查看该进程最近日志(后台按需拉取)",
|
|
4757
|
+
},
|
|
4758
|
+
"📄",
|
|
4759
|
+
)
|
|
4760
|
+
: ""),
|
|
4697
4761
|
);
|
|
4698
4762
|
});
|
|
4699
4763
|
|
|
@@ -4708,6 +4772,93 @@ function renderCmdRows(myCmds, userId, procs, isOnline, llmAnalysis) {
|
|
|
4708
4772
|
return html;
|
|
4709
4773
|
}
|
|
4710
4774
|
|
|
4775
|
+
// ── 弹窗:日志查看(按需拉取:请求入队 → 心跳下发 → 客户端回传落盘 → 轮询读取) ──
|
|
4776
|
+
|
|
4777
|
+
var _wdLogs = {
|
|
4778
|
+
clientId: "",
|
|
4779
|
+
cmdId: "",
|
|
4780
|
+
mtime: 0,
|
|
4781
|
+
timer: null,
|
|
4782
|
+
deadline: 0,
|
|
4783
|
+
gotFirst: false,
|
|
4784
|
+
};
|
|
4785
|
+
|
|
4786
|
+
function wdShowLogsModal(clientId, cmdId, title) {
|
|
4787
|
+
if (!clientId || !cmdId) return;
|
|
4788
|
+
if (_wdLogs.timer) {
|
|
4789
|
+
clearInterval(_wdLogs.timer);
|
|
4790
|
+
_wdLogs.timer = null;
|
|
4791
|
+
}
|
|
4792
|
+
_wdLogs.clientId = clientId;
|
|
4793
|
+
_wdLogs.cmdId = cmdId;
|
|
4794
|
+
_wdLogs.mtime = 0;
|
|
4795
|
+
_wdLogs.deadline = Date.now() + 90000;
|
|
4796
|
+
_wdLogs.gotFirst = false;
|
|
4797
|
+
document.getElementById("wdLogsTitle").textContent = "📄 " + title;
|
|
4798
|
+
document.getElementById("wdLogsStatus").textContent =
|
|
4799
|
+
"⏳ 已请求客户端回传…";
|
|
4800
|
+
document.getElementById("wdLogsContent").textContent =
|
|
4801
|
+
"等待客户端心跳回传日志…\n(心跳周期 ≤15s,最长等待 90s;拿到后会实时刷新)";
|
|
4802
|
+
document.getElementById("wdLogsModal").style.display = "flex";
|
|
4803
|
+
|
|
4804
|
+
// 入队拉取请求(服务端等该客户端下次心跳下发)
|
|
4805
|
+
fetch("/api/watchdog/request-logs", {
|
|
4806
|
+
method: "POST",
|
|
4807
|
+
headers: { "Content-Type": "application/json" },
|
|
4808
|
+
body: JSON.stringify({ clientId: clientId, cmdId: cmdId, tail: 500 }),
|
|
4809
|
+
}).catch(function () {});
|
|
4810
|
+
|
|
4811
|
+
// 立即读一次(可能已有上次的日志),之后每 2s 轮询
|
|
4812
|
+
wdPollLogs();
|
|
4813
|
+
_wdLogs.timer = setInterval(wdPollLogs, 2000);
|
|
4814
|
+
}
|
|
4815
|
+
|
|
4816
|
+
async function wdPollLogs() {
|
|
4817
|
+
var s = _wdLogs;
|
|
4818
|
+
if (!s.clientId) return;
|
|
4819
|
+
try {
|
|
4820
|
+
var res = await fetch(
|
|
4821
|
+
"/api/watchdog/logs-file?clientId=" +
|
|
4822
|
+
encodeURIComponent(s.clientId) +
|
|
4823
|
+
"&cmdId=" +
|
|
4824
|
+
encodeURIComponent(s.cmdId),
|
|
4825
|
+
);
|
|
4826
|
+
if (!res.ok) return;
|
|
4827
|
+
var d = await res.json();
|
|
4828
|
+
if (d.ok && d.content != null) {
|
|
4829
|
+
if (d.mtimeMs !== s.mtime) {
|
|
4830
|
+
s.mtime = d.mtimeMs;
|
|
4831
|
+
s.gotFirst = true;
|
|
4832
|
+
document.getElementById("wdLogsContent").textContent = d.content;
|
|
4833
|
+
document.getElementById("wdLogsStatus").textContent =
|
|
4834
|
+
"✅ " +
|
|
4835
|
+
new Date(d.mtimeMs).toLocaleTimeString("zh-CN") +
|
|
4836
|
+
" · " +
|
|
4837
|
+
d.size +
|
|
4838
|
+
" 字节 · 每 2s 自动刷新";
|
|
4839
|
+
}
|
|
4840
|
+
} else if (!s.gotFirst && Date.now() > s.deadline) {
|
|
4841
|
+
document.getElementById("wdLogsStatus").textContent =
|
|
4842
|
+
"⚠️ 等待超时:客户端离线或未响应(可关闭后重试)";
|
|
4843
|
+
if (s.timer) {
|
|
4844
|
+
clearInterval(s.timer);
|
|
4845
|
+
s.timer = null;
|
|
4846
|
+
}
|
|
4847
|
+
}
|
|
4848
|
+
} catch (e) {
|
|
4849
|
+
/* 网络抖动忽略,下一轮再试 */
|
|
4850
|
+
}
|
|
4851
|
+
}
|
|
4852
|
+
|
|
4853
|
+
function wdCloseLogsModal() {
|
|
4854
|
+
if (_wdLogs.timer) {
|
|
4855
|
+
clearInterval(_wdLogs.timer);
|
|
4856
|
+
_wdLogs.timer = null;
|
|
4857
|
+
}
|
|
4858
|
+
_wdLogs.clientId = "";
|
|
4859
|
+
document.getElementById("wdLogsModal").style.display = "none";
|
|
4860
|
+
}
|
|
4861
|
+
|
|
4711
4862
|
// ── 弹窗:添加/编辑命令 ──
|
|
4712
4863
|
|
|
4713
4864
|
var _wdModalUserId = "";
|
|
@@ -686,7 +686,25 @@
|
|
|
686
686
|
</div>
|
|
687
687
|
</div>
|
|
688
688
|
|
|
689
|
-
|
|
689
|
+
<!-- 日志查看弹窗(按需拉取 + 轮询) -->
|
|
690
|
+
<div id="wdLogsModal"
|
|
691
|
+
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">
|
|
692
|
+
<div
|
|
693
|
+
style="background:#1e1e2e;border:1px solid #333;border-radius:12px;padding:20px;width:90%;max-width:960px;height:85vh;display:flex;flex-direction:column">
|
|
694
|
+
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px">
|
|
695
|
+
<div style="font-size:16px;color:#a78bfa" id="wdLogsTitle">📄 日志</div>
|
|
696
|
+
<div style="font-size:11px;color:#888" id="wdLogsStatus">⏳ 已请求客户端回传…</div>
|
|
697
|
+
</div>
|
|
698
|
+
<pre id="wdLogsContent"
|
|
699
|
+
style="flex:1;overflow:auto;background:#111;border:1px solid #333;border-radius:8px;padding:12px;color:#d4d4d4;font-family:monospace;font-size:12px;line-height:1.5;margin:0;white-space:pre-wrap;word-break:break-all">加载中...</pre>
|
|
700
|
+
<div style="display:flex;justify-content:flex-end;gap:8px;margin-top:12px">
|
|
701
|
+
<button onclick="wdCloseLogsModal()"
|
|
702
|
+
style="padding:8px 16px;background:#333;color:#ccc;border:none;border-radius:6px;cursor:pointer;font-size:12px">关闭</button>
|
|
703
|
+
</div>
|
|
704
|
+
</div>
|
|
705
|
+
</div>
|
|
706
|
+
|
|
707
|
+
<script src="app.js?v=20260826"></script>
|
|
690
708
|
</body>
|
|
691
709
|
|
|
692
710
|
</html>
|