tt-help-cli-ycl 1.4.73 → 1.4.74
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/watch/public/app.js +115 -15
- package/src/watch/public/index.html +3 -3
- package/src/watch/watchdog-server.js +41 -2
- package/src/watchdog/agent.js +15 -3
- package/src/watchdog/llm-analyzer.js +10 -60
package/package.json
CHANGED
package/src/watch/public/app.js
CHANGED
|
@@ -3510,13 +3510,17 @@ function renderWatchdogDashboard(clients, clientCommands) {
|
|
|
3510
3510
|
if (uid) userClients[uid] = c;
|
|
3511
3511
|
});
|
|
3512
3512
|
|
|
3513
|
-
//
|
|
3513
|
+
// 收集所有卡片:全部注册客户端 + 未上线但有命令配置的 userId
|
|
3514
3514
|
var allCards = [];
|
|
3515
3515
|
clients.forEach(function (c) {
|
|
3516
3516
|
allCards.push(renderClientCard(c, clientCommands));
|
|
3517
3517
|
});
|
|
3518
3518
|
for (var uid in clientCommands) {
|
|
3519
|
-
if (
|
|
3519
|
+
if (
|
|
3520
|
+
!userClients[uid] &&
|
|
3521
|
+
clientCommands[uid] &&
|
|
3522
|
+
clientCommands[uid].length > 0
|
|
3523
|
+
) {
|
|
3520
3524
|
allCards.push(renderOfflineCard(uid, clientCommands[uid]));
|
|
3521
3525
|
}
|
|
3522
3526
|
}
|
|
@@ -3526,11 +3530,40 @@ function renderWatchdogDashboard(clients, clientCommands) {
|
|
|
3526
3530
|
if (fingerprint === _wdLastRendered) return;
|
|
3527
3531
|
_wdLastRendered = fingerprint;
|
|
3528
3532
|
|
|
3533
|
+
// 注入动画样式(仅首次)
|
|
3534
|
+
if (!document.getElementById("wdAnimStyle")) {
|
|
3535
|
+
var style = document.createElement("style");
|
|
3536
|
+
style.id = "wdAnimStyle";
|
|
3537
|
+
style.textContent =
|
|
3538
|
+
"@keyframes wdFlash{0%{color:#4ade80}50%{color:#60a5fa}100%{color:inherit}}" +
|
|
3539
|
+
".wd-flash{animation:wdFlash 1.5s ease}";
|
|
3540
|
+
document.head.appendChild(style);
|
|
3541
|
+
}
|
|
3542
|
+
|
|
3543
|
+
// 重建前扫描旧值(按 data-wd-key 索引)
|
|
3544
|
+
var prevVals = {};
|
|
3545
|
+
var oldCells = el.querySelectorAll("[data-wd-key]");
|
|
3546
|
+
for (var i = 0; i < oldCells.length; i++) {
|
|
3547
|
+
prevVals[oldCells[i].getAttribute("data-wd-key")] = oldCells[i].textContent;
|
|
3548
|
+
}
|
|
3549
|
+
|
|
3529
3550
|
el.innerHTML = styled(
|
|
3530
3551
|
"div",
|
|
3531
3552
|
"display:grid;grid-template-columns:repeat(auto-fill,minmax(550px,1fr));gap:24px",
|
|
3532
3553
|
allCards.join(""),
|
|
3533
3554
|
);
|
|
3555
|
+
|
|
3556
|
+
// 只对变化的值加闪烁动画
|
|
3557
|
+
var newCells = el.querySelectorAll("[data-wd-key]");
|
|
3558
|
+
for (var i = 0; i < newCells.length; i++) {
|
|
3559
|
+
var key = newCells[i].getAttribute("data-wd-key");
|
|
3560
|
+
if (
|
|
3561
|
+
prevVals[key] !== undefined &&
|
|
3562
|
+
prevVals[key] !== newCells[i].textContent
|
|
3563
|
+
) {
|
|
3564
|
+
newCells[i].className = "wd-flash";
|
|
3565
|
+
}
|
|
3566
|
+
}
|
|
3534
3567
|
}
|
|
3535
3568
|
|
|
3536
3569
|
function renderOfflineCard(userId, cmds) {
|
|
@@ -3766,6 +3799,7 @@ function renderCmdRows(myCmds, userId, procs, isOnline, llmAnalysis) {
|
|
|
3766
3799
|
var uptime = p ? p.uptimeDisplay || p.uptimeSeconds + "s" : "-";
|
|
3767
3800
|
var restart = p && p.restartCount > 0 ? String(p.restartCount) : "0";
|
|
3768
3801
|
|
|
3802
|
+
var prefix = "wd-" + esc(userId) + "-" + idx;
|
|
3769
3803
|
html += styled(
|
|
3770
3804
|
"div",
|
|
3771
3805
|
"display:grid;grid-template-columns:1fr 50px 55px 55px 50px 80px;gap:4px;padding:4px 0;align-items:center;font-size:10px;border-top:1px solid #1a1a2a",
|
|
@@ -3789,12 +3823,27 @@ function renderCmdRows(myCmds, userId, procs, isOnline, llmAnalysis) {
|
|
|
3789
3823
|
)
|
|
3790
3824
|
: ""),
|
|
3791
3825
|
) +
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3826
|
+
tag(
|
|
3827
|
+
"span",
|
|
3828
|
+
{ style: "color:#888", "data-wd-key": prefix + "-pid" },
|
|
3829
|
+
pid,
|
|
3830
|
+
) +
|
|
3831
|
+
tag(
|
|
3832
|
+
"span",
|
|
3833
|
+
{ style: "color:#aaa", "data-wd-key": prefix + "-mem" },
|
|
3834
|
+
mem,
|
|
3835
|
+
) +
|
|
3836
|
+
tag(
|
|
3837
|
+
"span",
|
|
3838
|
+
{ style: "color:#888", "data-wd-key": prefix + "-up" },
|
|
3839
|
+
uptime,
|
|
3840
|
+
) +
|
|
3841
|
+
tag(
|
|
3796
3842
|
"span",
|
|
3797
|
-
|
|
3843
|
+
{
|
|
3844
|
+
style: "color:" + (p && p.restartCount > 0 ? "#f87171" : "#555"),
|
|
3845
|
+
"data-wd-key": prefix + "-rst",
|
|
3846
|
+
},
|
|
3798
3847
|
restart,
|
|
3799
3848
|
) +
|
|
3800
3849
|
styled(
|
|
@@ -3862,6 +3911,8 @@ function renderCmdRows(myCmds, userId, procs, isOnline, llmAnalysis) {
|
|
|
3862
3911
|
// 未匹配到的运行中进程(孤儿)
|
|
3863
3912
|
match.unmatched.forEach(function (p) {
|
|
3864
3913
|
var sc = p.status === "running" ? "#4ade80" : "#f87171";
|
|
3914
|
+
var oid = p.pid || p.cmdId || "?";
|
|
3915
|
+
var opre = "wd-" + esc(userId) + "-orphan-" + esc(String(oid));
|
|
3865
3916
|
html += styled(
|
|
3866
3917
|
"div",
|
|
3867
3918
|
"display:grid;grid-template-columns:1fr 50px 55px 55px 50px 80px;gap:4px;padding:4px 0;align-items:center;font-size:10px;border-top:1px solid #1a1a2a;opacity:0.5",
|
|
@@ -3870,14 +3921,26 @@ function renderCmdRows(myCmds, userId, procs, isOnline, llmAnalysis) {
|
|
|
3870
3921
|
"color:#666;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-family:monospace",
|
|
3871
3922
|
"⚡ " + (p.name || p.cmdId),
|
|
3872
3923
|
) +
|
|
3873
|
-
|
|
3874
|
-
styled(
|
|
3924
|
+
tag(
|
|
3875
3925
|
"span",
|
|
3876
|
-
"color:#
|
|
3926
|
+
{ style: "color:#888", "data-wd-key": opre + "-pid" },
|
|
3927
|
+
p.pid || "-",
|
|
3928
|
+
) +
|
|
3929
|
+
tag(
|
|
3930
|
+
"span",
|
|
3931
|
+
{ style: "color:#aaa", "data-wd-key": opre + "-mem" },
|
|
3877
3932
|
p.memoryMB != null ? p.memoryMB + "MB" : "-",
|
|
3878
3933
|
) +
|
|
3879
|
-
|
|
3880
|
-
|
|
3934
|
+
tag(
|
|
3935
|
+
"span",
|
|
3936
|
+
{ style: "color:#888", "data-wd-key": opre + "-up" },
|
|
3937
|
+
p.uptimeDisplay || p.uptimeSeconds + "s",
|
|
3938
|
+
) +
|
|
3939
|
+
tag(
|
|
3940
|
+
"span",
|
|
3941
|
+
{ style: "color:#555", "data-wd-key": opre + "-rst" },
|
|
3942
|
+
"0",
|
|
3943
|
+
) +
|
|
3881
3944
|
styled("span", "color:#555;font-size:9px", "孤立"),
|
|
3882
3945
|
);
|
|
3883
3946
|
});
|
|
@@ -4072,17 +4135,40 @@ function wdToggleClientCmd(userId, idx) {
|
|
|
4072
4135
|
wdSaveClientCmds(userId, myCmds);
|
|
4073
4136
|
}
|
|
4074
4137
|
|
|
4138
|
+
var _wdSaving = false;
|
|
4139
|
+
|
|
4140
|
+
function wdShowSaving() {
|
|
4141
|
+
_wdSaving = true;
|
|
4142
|
+
var el = document.getElementById("wdSavingTip");
|
|
4143
|
+
if (!el) {
|
|
4144
|
+
el = document.createElement("div");
|
|
4145
|
+
el.id = "wdSavingTip";
|
|
4146
|
+
el.style.cssText =
|
|
4147
|
+
"position:fixed;top:60px;right:20px;background:#f59e0b;color:#000;padding:8px 16px;border-radius:6px;font-size:12px;font-weight:600;z-index:9998;pointer-events:none";
|
|
4148
|
+
document.body.appendChild(el);
|
|
4149
|
+
}
|
|
4150
|
+
el.textContent = "⏳ 处理中...";
|
|
4151
|
+
el.style.display = "block";
|
|
4152
|
+
}
|
|
4153
|
+
function wdHideSaving() {
|
|
4154
|
+
_wdSaving = false;
|
|
4155
|
+
var el = document.getElementById("wdSavingTip");
|
|
4156
|
+
if (el) el.style.display = "none";
|
|
4157
|
+
}
|
|
4158
|
+
|
|
4075
4159
|
async function wdSaveClientCmds(userId, commands) {
|
|
4160
|
+
wdShowSaving();
|
|
4076
4161
|
try {
|
|
4077
4162
|
await fetch("/api/watchdog/client-commands", {
|
|
4078
4163
|
method: "POST",
|
|
4079
4164
|
headers: { "Content-Type": "application/json" },
|
|
4080
4165
|
body: JSON.stringify({ userId: userId, commands: commands }),
|
|
4081
4166
|
});
|
|
4167
|
+
wdHideSaving();
|
|
4082
4168
|
fetchWatchdogData();
|
|
4083
4169
|
} catch (e) {
|
|
4170
|
+
wdHideSaving();
|
|
4084
4171
|
alert("保存失败: " + e.message);
|
|
4085
|
-
// 失败时立即刷新恢复按钮状态
|
|
4086
4172
|
fetchWatchdogData();
|
|
4087
4173
|
}
|
|
4088
4174
|
}
|
|
@@ -4092,9 +4178,23 @@ function wdDeleteClient(userId) {
|
|
|
4092
4178
|
!confirm(
|
|
4093
4179
|
"确定删除 " +
|
|
4094
4180
|
userId +
|
|
4095
|
-
"
|
|
4181
|
+
" 及其所有命令配置?\n\n删除后该客户端将从列表中移除。",
|
|
4096
4182
|
)
|
|
4097
4183
|
)
|
|
4098
4184
|
return;
|
|
4099
|
-
|
|
4185
|
+
wdShowSaving();
|
|
4186
|
+
fetch("/api/watchdog/delete-client", {
|
|
4187
|
+
method: "POST",
|
|
4188
|
+
headers: { "Content-Type": "application/json" },
|
|
4189
|
+
body: JSON.stringify({ userId: userId }),
|
|
4190
|
+
})
|
|
4191
|
+
.then(function () {
|
|
4192
|
+
wdHideSaving();
|
|
4193
|
+
fetchWatchdogData();
|
|
4194
|
+
})
|
|
4195
|
+
.catch(function (e) {
|
|
4196
|
+
wdHideSaving();
|
|
4197
|
+
alert("删除失败: " + e.message);
|
|
4198
|
+
fetchWatchdogData();
|
|
4199
|
+
});
|
|
4100
4200
|
}
|
|
@@ -577,9 +577,9 @@
|
|
|
577
577
|
placeholder="默认">
|
|
578
578
|
</div>
|
|
579
579
|
<div>
|
|
580
|
-
<label style="display:block;color:#888;font-size:10px;margin-bottom:2px"
|
|
581
|
-
<input id="wdCfgUserId"
|
|
582
|
-
style="width:100%;padding:6px;background:#
|
|
580
|
+
<label style="display:block;color:#888;font-size:10px;margin-bottom:2px">用户编号(不可修改)</label>
|
|
581
|
+
<input id="wdCfgUserId" readonly
|
|
582
|
+
style="width:100%;padding:6px;background:#1a1a2a;border:1px solid #333;color:#666;border-radius:4px;font-size:11px;box-sizing:border-box;cursor:not-allowed"
|
|
583
583
|
placeholder="自动生成">
|
|
584
584
|
</div>
|
|
585
585
|
<div>
|
|
@@ -134,7 +134,17 @@ function setClientCommands(userId, commands) {
|
|
|
134
134
|
});
|
|
135
135
|
const cfg = loadConfig();
|
|
136
136
|
if (!cfg.clientCommands) cfg.clientCommands = {};
|
|
137
|
-
|
|
137
|
+
if (cleaned.length === 0) {
|
|
138
|
+
delete cfg.clientCommands[userId];
|
|
139
|
+
console.error(
|
|
140
|
+
`[watchdog ${new Date().toLocaleTimeString("zh-CN", { hour12: false })}] ${userId} 命令已清空,卡片已删除`,
|
|
141
|
+
);
|
|
142
|
+
} else {
|
|
143
|
+
cfg.clientCommands[userId] = cleaned;
|
|
144
|
+
console.error(
|
|
145
|
+
`[watchdog ${new Date().toLocaleTimeString("zh-CN", { hour12: false })}] ${userId} 命令已更新: ${cleaned.length} 条`,
|
|
146
|
+
);
|
|
147
|
+
}
|
|
138
148
|
saveConfig(cfg);
|
|
139
149
|
}
|
|
140
150
|
|
|
@@ -279,8 +289,33 @@ async function handleAnalyzeLogs(params, body, res) {
|
|
|
279
289
|
sendJSON(res, 200, { llmAnalysis });
|
|
280
290
|
} catch (e) {
|
|
281
291
|
console.error(`[watchdog] LLM 分析失败: ${e.message}`);
|
|
282
|
-
sendJSON(res,
|
|
292
|
+
sendJSON(res, 200, { llmAnalysis: [] });
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
async function handleDeleteClient(params, body, res) {
|
|
297
|
+
const userId = body.userId;
|
|
298
|
+
if (!userId) {
|
|
299
|
+
sendJSON(res, 400, { error: "缺少 userId" });
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
// 1. 删除命令配置
|
|
303
|
+
const cfg = loadConfig();
|
|
304
|
+
if (cfg.clientCommands) delete cfg.clientCommands[userId];
|
|
305
|
+
if (cfg.clientConfigs) delete cfg.clientConfigs[userId];
|
|
306
|
+
saveConfig(cfg);
|
|
307
|
+
// 2. 从内存客户端列表中移除所有匹配的客户端
|
|
308
|
+
let removed = 0;
|
|
309
|
+
for (const [clientId, info] of clients) {
|
|
310
|
+
if ((info.config && info.config.userId) === userId) {
|
|
311
|
+
clients.delete(clientId);
|
|
312
|
+
removed++;
|
|
313
|
+
}
|
|
283
314
|
}
|
|
315
|
+
console.error(
|
|
316
|
+
`[watchdog] ${userId} 已删除(${removed}个客户端,命令+配置已清除)`,
|
|
317
|
+
);
|
|
318
|
+
sendJSON(res, 200, { ok: true, removed });
|
|
284
319
|
}
|
|
285
320
|
|
|
286
321
|
// ── 公开 ──
|
|
@@ -340,5 +375,9 @@ export async function tryHandleWatchdogRoute(
|
|
|
340
375
|
await handleAnalyzeLogs(params, body, res);
|
|
341
376
|
return true;
|
|
342
377
|
}
|
|
378
|
+
if (method === "POST" && routePath === "/api/watchdog/delete-client") {
|
|
379
|
+
await handleDeleteClient(params, body, res);
|
|
380
|
+
return true;
|
|
381
|
+
}
|
|
343
382
|
return false;
|
|
344
383
|
}
|
package/src/watchdog/agent.js
CHANGED
|
@@ -42,9 +42,21 @@ function applyClientConfig(newCfg) {
|
|
|
42
42
|
];
|
|
43
43
|
let changed = false;
|
|
44
44
|
for (const f of fields) {
|
|
45
|
-
if (newCfg[f] !== undefined
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
if (newCfg[f] !== undefined) {
|
|
46
|
+
var newVal = newCfg[f],
|
|
47
|
+
oldVal = cfg[f];
|
|
48
|
+
// 忽略无意义的零值/空值(避免每次心跳都写文件)
|
|
49
|
+
if (
|
|
50
|
+
(typeof newVal === "number" &&
|
|
51
|
+
newVal === 0 &&
|
|
52
|
+
oldVal === undefined) ||
|
|
53
|
+
(typeof newVal === "string" && newVal === "" && oldVal === undefined)
|
|
54
|
+
)
|
|
55
|
+
continue;
|
|
56
|
+
if (newVal !== oldVal) {
|
|
57
|
+
cfg[f] = newVal;
|
|
58
|
+
changed = true;
|
|
59
|
+
}
|
|
48
60
|
}
|
|
49
61
|
}
|
|
50
62
|
if (changed) {
|
|
@@ -1,21 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* LLM 日志分析模块 — 供服务端调用(传入 llmFetch),或客户端独立使用(读环境变量)
|
|
3
|
-
*
|
|
4
|
-
* 分析结果通过心跳上报到 watch 服务端展示。
|
|
5
3
|
*/
|
|
6
4
|
|
|
7
5
|
// ── 分析入口 ──
|
|
8
6
|
|
|
9
|
-
/**
|
|
10
|
-
* 对所有进程的日志调用 LLM 分析
|
|
11
|
-
* @param {Array} processLogs — [{cmdId, logs[], uptimeSeconds, restartCount}]
|
|
12
|
-
* @param {Function} [llmFetch] — 可选,服务端传入的 llmFetch;不传则读环境变量
|
|
13
|
-
* @returns {Promise<Array<{cmdId, severity, summary, suggestions}>>}
|
|
14
|
-
*/
|
|
15
7
|
export async function analyzeProcessLogs(processLogs, llmFetch) {
|
|
16
8
|
if (!processLogs || processLogs.length === 0) return [];
|
|
17
9
|
|
|
18
|
-
// 如果没有传入 llmFetch,使用内置的轻量调用(客户端模式)
|
|
19
10
|
if (!llmFetch) {
|
|
20
11
|
llmFetch = await createStandaloneFetch();
|
|
21
12
|
if (!llmFetch) return [];
|
|
@@ -41,7 +32,7 @@ export async function analyzeProcessLogs(processLogs, llmFetch) {
|
|
|
41
32
|
return results;
|
|
42
33
|
}
|
|
43
34
|
|
|
44
|
-
// ──
|
|
35
|
+
// ── 客户端独立模式 ──
|
|
45
36
|
|
|
46
37
|
async function createStandaloneFetch() {
|
|
47
38
|
try {
|
|
@@ -59,8 +50,6 @@ async function createStandaloneFetch() {
|
|
|
59
50
|
const model = process.env.LLM_MODEL || fileCfg.llmModel || "auto";
|
|
60
51
|
if (!apiKey) { console.error("[llm-analyzer] 未配置 LLM_API_KEY"); return null; }
|
|
61
52
|
console.error(`[llm-analyzer] 客户端模式: ${model} @ ${url}`);
|
|
62
|
-
|
|
63
|
-
// 返回兼容 llmFetch 签名的函数
|
|
64
53
|
return async ({ messages, max_tokens = 256, temperature = 0.1 }) => {
|
|
65
54
|
const resp = await fetch(url, {
|
|
66
55
|
method: "POST",
|
|
@@ -79,34 +68,13 @@ async function createStandaloneFetch() {
|
|
|
79
68
|
return null;
|
|
80
69
|
}
|
|
81
70
|
}
|
|
82
|
-
},
|
|
83
|
-
],
|
|
84
|
-
256,
|
|
85
|
-
);
|
|
86
|
-
const parsed = parseResponse(content);
|
|
87
|
-
console.error(
|
|
88
|
-
`[llm-analyzer] ${proc.cmdId.substring(0, 30)} → ${parsed.severity}: ${parsed.summary}`,
|
|
89
|
-
);
|
|
90
|
-
results.push({ cmdId: proc.cmdId, ...parsed });
|
|
91
|
-
} catch (e) {
|
|
92
|
-
console.error(
|
|
93
|
-
`[llm-analyzer] ${proc.cmdId.substring(0, 30)} 失败: ${e.message}`,
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
return results;
|
|
98
|
-
} catch (e) {
|
|
99
|
-
console.error(`[llm-analyzer] 异常: ${e.message}`);
|
|
100
|
-
return [];
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
71
|
|
|
104
|
-
|
|
105
|
-
const uptime =
|
|
106
|
-
uptimeSeconds >= 3600
|
|
107
|
-
? Math.round(uptimeSeconds / 3600) + "h"
|
|
108
|
-
: uptimeSeconds + "s";
|
|
72
|
+
// ── Prompt ──
|
|
109
73
|
|
|
74
|
+
function buildPrompt(cmdId, logs, uptimeSeconds, restartCount) {
|
|
75
|
+
const uptime = uptimeSeconds >= 3600
|
|
76
|
+
? Math.round(uptimeSeconds / 3600) + "h"
|
|
77
|
+
: uptimeSeconds + "s";
|
|
110
78
|
return `你是 TikTok 数据采集工具的运维助手。以下是进程 "${cmdId}" 的最近 10 分钟日志,请分析是否有异常。
|
|
111
79
|
|
|
112
80
|
进程信息:
|
|
@@ -127,38 +95,20 @@ ${logs || "(无日志)"}
|
|
|
127
95
|
- 每个 suggestions 是简洁可操作的建议,不超过 3 条`;
|
|
128
96
|
}
|
|
129
97
|
|
|
130
|
-
// ──
|
|
98
|
+
// ── 解析 ──
|
|
131
99
|
|
|
132
100
|
function parseResponse(content) {
|
|
133
101
|
try {
|
|
134
102
|
const parsed = JSON.parse(content);
|
|
135
|
-
return {
|
|
136
|
-
severity: parsed.severity || "ok",
|
|
137
|
-
summary: parsed.summary || "",
|
|
138
|
-
suggestions: Array.isArray(parsed.suggestions)
|
|
139
|
-
? parsed.suggestions.slice(0, 3)
|
|
140
|
-
: [],
|
|
141
|
-
};
|
|
103
|
+
return { severity: parsed.severity || "ok", summary: parsed.summary || "", suggestions: Array.isArray(parsed.suggestions) ? parsed.suggestions.slice(0, 3) : [] };
|
|
142
104
|
} catch {
|
|
143
|
-
// 尝试提取 JSON 块
|
|
144
105
|
const match = content.match(/\{[\s\S]*\}/);
|
|
145
106
|
if (match) {
|
|
146
107
|
try {
|
|
147
108
|
const parsed = JSON.parse(match[0]);
|
|
148
|
-
return {
|
|
149
|
-
severity: parsed.severity || "ok",
|
|
150
|
-
summary: parsed.summary || "",
|
|
151
|
-
suggestions: Array.isArray(parsed.suggestions)
|
|
152
|
-
? parsed.suggestions.slice(0, 3)
|
|
153
|
-
: [],
|
|
154
|
-
};
|
|
109
|
+
return { severity: parsed.severity || "ok", summary: parsed.summary || "", suggestions: Array.isArray(parsed.suggestions) ? parsed.suggestions.slice(0, 3) : [] };
|
|
155
110
|
} catch {}
|
|
156
111
|
}
|
|
157
|
-
|
|
158
|
-
return {
|
|
159
|
-
severity: "warn",
|
|
160
|
-
summary: content.substring(0, 100),
|
|
161
|
-
suggestions: [],
|
|
162
|
-
};
|
|
112
|
+
return { severity: "warn", summary: content.substring(0, 100), suggestions: [] };
|
|
163
113
|
}
|
|
164
114
|
}
|