ms-vite-plugin 1.1.5 → 1.1.7
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/dist/mcp/tools.js +110 -16
- package/dist/project.d.ts +6 -27
- package/dist/project.js +30 -78
- package/package.json +1 -1
package/dist/mcp/tools.js
CHANGED
|
@@ -302,6 +302,31 @@ function registerRuntimeTools(server) {
|
|
|
302
302
|
await (0, project_2.ensureValidKuaiJSProject)(workspace);
|
|
303
303
|
return workspace;
|
|
304
304
|
}
|
|
305
|
+
/**
|
|
306
|
+
* 在后台启动一次日志监听,不阻塞 run/run_ui 调用返回
|
|
307
|
+
* @param ip 设备 IP
|
|
308
|
+
* @param port 设备端口
|
|
309
|
+
* @param durationSeconds 监听秒数
|
|
310
|
+
* @param maxLogs 最大日志条数
|
|
311
|
+
* @returns 返回监听目标说明文本
|
|
312
|
+
* @example
|
|
313
|
+
* const tip = startBackgroundLogWatch("192.168.1.10", 9800, 20, 300)
|
|
314
|
+
*/
|
|
315
|
+
function startBackgroundLogWatch(ip, port, durationSeconds, maxLogs) {
|
|
316
|
+
const durationMs = durationSeconds > 0 ? durationSeconds * 1000 : 0;
|
|
317
|
+
void (0, project_1.watchDeviceLogsBySse)(ip, port, durationMs, maxLogs)
|
|
318
|
+
.then((result) => {
|
|
319
|
+
console.log(`[mcp] background logs ${ip}:${port} done, reason=${result.stopReason}, logs=${result.logs.length}, runtime_status=${result.runtimeStatus.length}`);
|
|
320
|
+
})
|
|
321
|
+
.catch((error) => {
|
|
322
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
323
|
+
console.warn(`[mcp] background logs ${ip}:${port} failed: ${message}`);
|
|
324
|
+
});
|
|
325
|
+
if (durationSeconds === 0) {
|
|
326
|
+
return `已在后台启动持续日志监听: ${ip}:${port}(直到连接断开,max ${maxLogs})`;
|
|
327
|
+
}
|
|
328
|
+
return `已在后台启动日志监听: ${ip}:${port}(${durationSeconds}s,max ${maxLogs})`;
|
|
329
|
+
}
|
|
305
330
|
/**
|
|
306
331
|
* 解析运行目标:
|
|
307
332
|
* - 显式 transport=ws: 强制走 ws
|
|
@@ -591,7 +616,7 @@ function registerRuntimeTools(server) {
|
|
|
591
616
|
});
|
|
592
617
|
server.registerTool("watch_logs", {
|
|
593
618
|
title: "Watch Logs",
|
|
594
|
-
description: "通过 SSE
|
|
619
|
+
description: "通过 SSE 监听设备实时日志。首次可传 ip,也可复用 set_device 保存的默认设备。",
|
|
595
620
|
inputSchema: {
|
|
596
621
|
workspacePath: z
|
|
597
622
|
.string()
|
|
@@ -613,11 +638,11 @@ function registerRuntimeTools(server) {
|
|
|
613
638
|
durationSeconds: z
|
|
614
639
|
.number()
|
|
615
640
|
.int()
|
|
616
|
-
.min(
|
|
641
|
+
.min(0)
|
|
617
642
|
.max(300)
|
|
618
643
|
.optional()
|
|
619
644
|
.default(15)
|
|
620
|
-
.describe("监听时长(秒),默认 15
|
|
645
|
+
.describe("监听时长(秒),默认 15 秒;传 0 表示持续监听直到连接断开"),
|
|
621
646
|
maxLogs: z
|
|
622
647
|
.number()
|
|
623
648
|
.int()
|
|
@@ -631,10 +656,6 @@ function registerRuntimeTools(server) {
|
|
|
631
656
|
await ensureWorkspacePath(workspacePath);
|
|
632
657
|
const device = await (0, device_config_1.resolveDeviceConfig)(ip, port);
|
|
633
658
|
const result = await (0, project_1.watchDeviceLogsBySse)(device.ip, device.port, durationSeconds * 1000, maxLogs);
|
|
634
|
-
const findingsPreview = result.findings
|
|
635
|
-
.slice(0, 20)
|
|
636
|
-
.map((item, index) => `${index + 1}. [${item.severity}] ${item.rule} | ${item.log.timestamp} | ${item.log.message}`)
|
|
637
|
-
.join("\n");
|
|
638
659
|
const logsPreview = result.logs
|
|
639
660
|
.slice(-20)
|
|
640
661
|
.map((log) => `[${log.timestamp}] [${log.level.toUpperCase()}] ${log.message}`)
|
|
@@ -645,11 +666,8 @@ function registerRuntimeTools(server) {
|
|
|
645
666
|
type: "text",
|
|
646
667
|
text: [
|
|
647
668
|
`监听目标: ${device.ip}:${device.port}`,
|
|
648
|
-
|
|
649
|
-
result.
|
|
650
|
-
"",
|
|
651
|
-
"异常判定(最多展示前20条):",
|
|
652
|
-
findingsPreview || "未命中异常规则",
|
|
669
|
+
`监听模式: ${durationSeconds === 0 ? "持续直到连接断开" : `${durationSeconds}s`}`,
|
|
670
|
+
`结束原因: ${result.stopReason},收集日志: ${result.logs.length} 条,runtime_status: ${result.runtimeStatus.length} 条`,
|
|
653
671
|
"",
|
|
654
672
|
"最新日志(最多展示最后20条):",
|
|
655
673
|
logsPreview || "无日志",
|
|
@@ -737,9 +755,42 @@ function registerRuntimeTools(server) {
|
|
|
737
755
|
.min(1)
|
|
738
756
|
.optional()
|
|
739
757
|
.describe("可选工作目录;不传时使用 set_workspace 记录值或 MCP 启动目录"),
|
|
758
|
+
watchLogs: z
|
|
759
|
+
.boolean()
|
|
760
|
+
.optional()
|
|
761
|
+
.default(true)
|
|
762
|
+
.describe("是否在运行前启动实时日志监听,默认 true"),
|
|
763
|
+
logDurationSeconds: z
|
|
764
|
+
.number()
|
|
765
|
+
.int()
|
|
766
|
+
.min(0)
|
|
767
|
+
.max(300)
|
|
768
|
+
.optional()
|
|
769
|
+
.default(0)
|
|
770
|
+
.describe("日志监听时长(秒),默认 0;0 表示持续监听直到 runtime_status 失败或连接断开"),
|
|
771
|
+
logMaxLogs: z
|
|
772
|
+
.number()
|
|
773
|
+
.int()
|
|
774
|
+
.min(10)
|
|
775
|
+
.max(5000)
|
|
776
|
+
.optional()
|
|
777
|
+
.default(300)
|
|
778
|
+
.describe("日志最大收集条数,默认 300"),
|
|
740
779
|
},
|
|
741
|
-
}, async ({ transport, ip, port, wsPort, wsWaitMs, workspacePath }) => {
|
|
780
|
+
}, async ({ transport, ip, port, wsPort, wsWaitMs, workspacePath, watchLogs, logDurationSeconds, logMaxLogs, }) => {
|
|
742
781
|
const workspace = await ensureWorkspacePath(workspacePath);
|
|
782
|
+
let logNotice = "";
|
|
783
|
+
let logSetupWarning = "";
|
|
784
|
+
if (watchLogs) {
|
|
785
|
+
try {
|
|
786
|
+
const logDevice = await (0, device_config_1.resolveDeviceConfig)(ip, port);
|
|
787
|
+
// 先启动日志监听,再执行 run,且不等待日志结束,避免 run 被阻塞
|
|
788
|
+
logNotice = startBackgroundLogWatch(logDevice.ip, logDevice.port, logDurationSeconds, logMaxLogs);
|
|
789
|
+
}
|
|
790
|
+
catch (error) {
|
|
791
|
+
logSetupWarning = `日志监听未启动: ${error instanceof Error ? error.message : String(error)}`;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
743
794
|
const target = await resolvePreferredRuntimeTarget({
|
|
744
795
|
transport,
|
|
745
796
|
ip,
|
|
@@ -760,11 +811,16 @@ function registerRuntimeTools(server) {
|
|
|
760
811
|
transport: "http",
|
|
761
812
|
workspacePath: workspace,
|
|
762
813
|
});
|
|
814
|
+
const logSection = logNotice
|
|
815
|
+
? `\n\n${logNotice}`
|
|
816
|
+
: logSetupWarning
|
|
817
|
+
? `\n\n${logSetupWarning}`
|
|
818
|
+
: "";
|
|
763
819
|
return {
|
|
764
820
|
content: [
|
|
765
821
|
{
|
|
766
822
|
type: "text",
|
|
767
|
-
text: `运行请求已发送到 ${target.label}`,
|
|
823
|
+
text: `运行请求已发送到 ${target.label}${logSection}`,
|
|
768
824
|
},
|
|
769
825
|
],
|
|
770
826
|
};
|
|
@@ -808,9 +864,42 @@ function registerRuntimeTools(server) {
|
|
|
808
864
|
.min(1)
|
|
809
865
|
.optional()
|
|
810
866
|
.describe("可选工作目录;不传时使用 set_workspace 记录值或 MCP 启动目录"),
|
|
867
|
+
watchLogs: z
|
|
868
|
+
.boolean()
|
|
869
|
+
.optional()
|
|
870
|
+
.default(true)
|
|
871
|
+
.describe("是否在预览前启动实时日志监听,默认 true"),
|
|
872
|
+
logDurationSeconds: z
|
|
873
|
+
.number()
|
|
874
|
+
.int()
|
|
875
|
+
.min(0)
|
|
876
|
+
.max(300)
|
|
877
|
+
.optional()
|
|
878
|
+
.default(0)
|
|
879
|
+
.describe("日志监听时长(秒),默认 0;0 表示持续监听直到 runtime_status 失败或连接断开"),
|
|
880
|
+
logMaxLogs: z
|
|
881
|
+
.number()
|
|
882
|
+
.int()
|
|
883
|
+
.min(10)
|
|
884
|
+
.max(5000)
|
|
885
|
+
.optional()
|
|
886
|
+
.default(300)
|
|
887
|
+
.describe("日志最大收集条数,默认 300"),
|
|
811
888
|
},
|
|
812
|
-
}, async ({ transport, ip, port, wsPort, wsWaitMs, workspacePath }) => {
|
|
889
|
+
}, async ({ transport, ip, port, wsPort, wsWaitMs, workspacePath, watchLogs, logDurationSeconds, logMaxLogs, }) => {
|
|
813
890
|
const workspace = await ensureWorkspacePath(workspacePath);
|
|
891
|
+
let logNotice = "";
|
|
892
|
+
let logSetupWarning = "";
|
|
893
|
+
if (watchLogs) {
|
|
894
|
+
try {
|
|
895
|
+
const logDevice = await (0, device_config_1.resolveDeviceConfig)(ip, port);
|
|
896
|
+
// 先启动日志监听,再执行 run-ui,且不等待日志结束,避免 run-ui 被阻塞
|
|
897
|
+
logNotice = startBackgroundLogWatch(logDevice.ip, logDevice.port, logDurationSeconds, logMaxLogs);
|
|
898
|
+
}
|
|
899
|
+
catch (error) {
|
|
900
|
+
logSetupWarning = `日志监听未启动: ${error instanceof Error ? error.message : String(error)}`;
|
|
901
|
+
}
|
|
902
|
+
}
|
|
814
903
|
const target = await resolvePreferredRuntimeTarget({
|
|
815
904
|
transport,
|
|
816
905
|
ip,
|
|
@@ -831,11 +920,16 @@ function registerRuntimeTools(server) {
|
|
|
831
920
|
transport: "http",
|
|
832
921
|
workspacePath: workspace,
|
|
833
922
|
});
|
|
923
|
+
const logSection = logNotice
|
|
924
|
+
? `\n\n${logNotice}`
|
|
925
|
+
: logSetupWarning
|
|
926
|
+
? `\n\n${logSetupWarning}`
|
|
927
|
+
: "";
|
|
834
928
|
return {
|
|
835
929
|
content: [
|
|
836
930
|
{
|
|
837
931
|
type: "text",
|
|
838
|
-
text: `UI 预览请求已发送到 ${target.label}`,
|
|
932
|
+
text: `UI 预览请求已发送到 ${target.label}${logSection}`,
|
|
839
933
|
},
|
|
840
934
|
],
|
|
841
935
|
};
|
package/dist/project.d.ts
CHANGED
|
@@ -29,17 +29,6 @@ export interface DeviceRuntimeStatusEntry {
|
|
|
29
29
|
/** 事件时间戳(本地接收时间) */
|
|
30
30
|
timestamp: string;
|
|
31
31
|
}
|
|
32
|
-
/**
|
|
33
|
-
* 设备日志判定项
|
|
34
|
-
*/
|
|
35
|
-
export interface DeviceLogFinding {
|
|
36
|
-
/** 严重级别 */
|
|
37
|
-
severity: "error" | "warn";
|
|
38
|
-
/** 命中的判定规则 */
|
|
39
|
-
rule: string;
|
|
40
|
-
/** 证据日志 */
|
|
41
|
-
log: DeviceLogEntry;
|
|
42
|
-
}
|
|
43
32
|
/**
|
|
44
33
|
* SSE 日志监听结果
|
|
45
34
|
*/
|
|
@@ -48,10 +37,8 @@ export interface DeviceLogWatchResult {
|
|
|
48
37
|
logs: DeviceLogEntry[];
|
|
49
38
|
/** 收集到的 runtime_status 条目 */
|
|
50
39
|
runtimeStatus: DeviceRuntimeStatusEntry[];
|
|
51
|
-
/**
|
|
52
|
-
|
|
53
|
-
/** 结果摘要 */
|
|
54
|
-
summary: string;
|
|
40
|
+
/** 监听结束原因 */
|
|
41
|
+
stopReason: "timeout" | "stream_closed" | "manual_abort" | "unknown";
|
|
55
42
|
}
|
|
56
43
|
/**
|
|
57
44
|
* 在设备上获取截图 JPG 二进制数据(HTTP)
|
|
@@ -80,22 +67,14 @@ export declare function getScreenshotBase64OnDevice(options: DeviceCliOptions):
|
|
|
80
67
|
*/
|
|
81
68
|
export declare function getSourceOnDevice(options: DeviceCliOptions, maxDepth?: number, timeout?: number): Promise<string>;
|
|
82
69
|
/**
|
|
83
|
-
*
|
|
84
|
-
* @param logs 待判定日志列表
|
|
85
|
-
* @returns 返回判定命中项
|
|
86
|
-
* @example
|
|
87
|
-
* const findings = analyzeDeviceLogs([{ level: "error", message: "crash", timestamp: "..." }])
|
|
88
|
-
*/
|
|
89
|
-
export declare function analyzeDeviceLogs(logs: DeviceLogEntry[]): DeviceLogFinding[];
|
|
90
|
-
/**
|
|
91
|
-
* 监听设备 SSE 日志并返回自动判定结果
|
|
70
|
+
* 监听设备 SSE 日志并返回原始结果
|
|
92
71
|
* @param ip 设备 IP 地址
|
|
93
72
|
* @param port 设备端口
|
|
94
|
-
* @param durationMs 监听时长(毫秒),默认 15000
|
|
73
|
+
* @param durationMs 监听时长(毫秒),默认 15000;传 0 表示不设超时,持续监听
|
|
95
74
|
* @param maxLogs 最多收集日志条数,默认 200
|
|
96
|
-
* @returns
|
|
75
|
+
* @returns 返回日志与运行状态
|
|
97
76
|
* @example
|
|
98
|
-
* const result = await watchDeviceLogsBySse("192.168.1.10", 9800,
|
|
77
|
+
* const result = await watchDeviceLogsBySse("192.168.1.10", 9800, 0, 300)
|
|
99
78
|
*/
|
|
100
79
|
export declare function watchDeviceLogsBySse(ip: string, port: number, durationMs?: number, maxLogs?: number): Promise<DeviceLogWatchResult>;
|
|
101
80
|
/**
|
package/dist/project.js
CHANGED
|
@@ -39,7 +39,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
39
39
|
exports.getScreenshotOnDevice = getScreenshotOnDevice;
|
|
40
40
|
exports.getScreenshotBase64OnDevice = getScreenshotBase64OnDevice;
|
|
41
41
|
exports.getSourceOnDevice = getSourceOnDevice;
|
|
42
|
-
exports.analyzeDeviceLogs = analyzeDeviceLogs;
|
|
43
42
|
exports.watchDeviceLogsBySse = watchDeviceLogsBySse;
|
|
44
43
|
exports.runOnDevice = runOnDevice;
|
|
45
44
|
exports.runUIOnDevice = runUIOnDevice;
|
|
@@ -511,67 +510,14 @@ function normalizeLogEntry(payload) {
|
|
|
511
510
|
};
|
|
512
511
|
}
|
|
513
512
|
/**
|
|
514
|
-
*
|
|
515
|
-
* @param logs 待判定日志列表
|
|
516
|
-
* @returns 返回判定命中项
|
|
517
|
-
* @example
|
|
518
|
-
* const findings = analyzeDeviceLogs([{ level: "error", message: "crash", timestamp: "..." }])
|
|
519
|
-
*/
|
|
520
|
-
function analyzeDeviceLogs(logs) {
|
|
521
|
-
const findings = [];
|
|
522
|
-
const criticalPatterns = [
|
|
523
|
-
{ rule: "崩溃关键词", regex: /\b(crash|fatal|abort|sigabrt|segfault)\b/i },
|
|
524
|
-
{
|
|
525
|
-
rule: "异常关键词",
|
|
526
|
-
regex: /\b(exception|uncaught|assert|error:|failed|failure)\b/i,
|
|
527
|
-
},
|
|
528
|
-
{
|
|
529
|
-
rule: "内存风险关键词",
|
|
530
|
-
regex: /\b(out of memory|oom|memory leak|allocation failed)\b/i,
|
|
531
|
-
},
|
|
532
|
-
{
|
|
533
|
-
rule: "设备离线关键词",
|
|
534
|
-
regex: /\b(timeout|disconnected|connection reset|network error)\b/i,
|
|
535
|
-
},
|
|
536
|
-
];
|
|
537
|
-
for (const log of logs) {
|
|
538
|
-
const level = log.level.toLowerCase();
|
|
539
|
-
const message = log.message;
|
|
540
|
-
if (level === "error") {
|
|
541
|
-
findings.push({
|
|
542
|
-
severity: "error",
|
|
543
|
-
rule: "error 级别日志",
|
|
544
|
-
log,
|
|
545
|
-
});
|
|
546
|
-
}
|
|
547
|
-
else if (level === "warn") {
|
|
548
|
-
findings.push({
|
|
549
|
-
severity: "warn",
|
|
550
|
-
rule: "warn 级别日志",
|
|
551
|
-
log,
|
|
552
|
-
});
|
|
553
|
-
}
|
|
554
|
-
for (const pattern of criticalPatterns) {
|
|
555
|
-
if (pattern.regex.test(message)) {
|
|
556
|
-
findings.push({
|
|
557
|
-
severity: level === "error" ? "error" : "warn",
|
|
558
|
-
rule: pattern.rule,
|
|
559
|
-
log,
|
|
560
|
-
});
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
return findings;
|
|
565
|
-
}
|
|
566
|
-
/**
|
|
567
|
-
* 监听设备 SSE 日志并返回自动判定结果
|
|
513
|
+
* 监听设备 SSE 日志并返回原始结果
|
|
568
514
|
* @param ip 设备 IP 地址
|
|
569
515
|
* @param port 设备端口
|
|
570
|
-
* @param durationMs 监听时长(毫秒),默认 15000
|
|
516
|
+
* @param durationMs 监听时长(毫秒),默认 15000;传 0 表示不设超时,持续监听
|
|
571
517
|
* @param maxLogs 最多收集日志条数,默认 200
|
|
572
|
-
* @returns
|
|
518
|
+
* @returns 返回日志与运行状态
|
|
573
519
|
* @example
|
|
574
|
-
* const result = await watchDeviceLogsBySse("192.168.1.10", 9800,
|
|
520
|
+
* const result = await watchDeviceLogsBySse("192.168.1.10", 9800, 0, 300)
|
|
575
521
|
*/
|
|
576
522
|
async function watchDeviceLogsBySse(ip, port, durationMs = 15000, maxLogs = 200) {
|
|
577
523
|
if (!ip.trim()) {
|
|
@@ -580,7 +526,7 @@ async function watchDeviceLogsBySse(ip, port, durationMs = 15000, maxLogs = 200)
|
|
|
580
526
|
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
581
527
|
throw new Error(`监听日志失败: 无效端口 ${port}`);
|
|
582
528
|
}
|
|
583
|
-
if (!Number.isInteger(durationMs) || durationMs <
|
|
529
|
+
if (!Number.isInteger(durationMs) || durationMs < 0) {
|
|
584
530
|
throw new Error(`监听日志失败: 无效 durationMs ${durationMs}`);
|
|
585
531
|
}
|
|
586
532
|
if (!Number.isInteger(maxLogs) || maxLogs < 1) {
|
|
@@ -590,9 +536,13 @@ async function watchDeviceLogsBySse(ip, port, durationMs = 15000, maxLogs = 200)
|
|
|
590
536
|
const logs = [];
|
|
591
537
|
const runtimeStatus = [];
|
|
592
538
|
const controller = new AbortController();
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
539
|
+
let stopReason = "unknown";
|
|
540
|
+
const timeoutHandle = durationMs > 0
|
|
541
|
+
? setTimeout(() => {
|
|
542
|
+
stopReason = "timeout";
|
|
543
|
+
controller.abort();
|
|
544
|
+
}, durationMs)
|
|
545
|
+
: undefined;
|
|
596
546
|
try {
|
|
597
547
|
const response = await fetch(url, {
|
|
598
548
|
method: "GET",
|
|
@@ -610,6 +560,9 @@ async function watchDeviceLogsBySse(ip, port, durationMs = 15000, maxLogs = 200)
|
|
|
610
560
|
while (true) {
|
|
611
561
|
const { value, done } = await reader.read();
|
|
612
562
|
if (done) {
|
|
563
|
+
if (stopReason === "unknown") {
|
|
564
|
+
stopReason = "stream_closed";
|
|
565
|
+
}
|
|
613
566
|
break;
|
|
614
567
|
}
|
|
615
568
|
buffer += decoder.decode(value, { stream: true });
|
|
@@ -623,11 +576,16 @@ async function watchDeviceLogsBySse(ip, port, durationMs = 15000, maxLogs = 200)
|
|
|
623
576
|
try {
|
|
624
577
|
const parsed = JSON.parse(event.data);
|
|
625
578
|
if (event.event === "log") {
|
|
626
|
-
|
|
627
|
-
|
|
579
|
+
// 日志缓冲使用环形窗口,持续监听时不会无限增长
|
|
580
|
+
if (logs.length >= maxLogs) {
|
|
581
|
+
logs.shift();
|
|
628
582
|
}
|
|
583
|
+
logs.push(normalizeLogEntry(parsed));
|
|
629
584
|
}
|
|
630
585
|
else if (event.event === "runtime_status") {
|
|
586
|
+
if (runtimeStatus.length >= maxLogs) {
|
|
587
|
+
runtimeStatus.shift();
|
|
588
|
+
}
|
|
631
589
|
runtimeStatus.push({
|
|
632
590
|
raw: parsed,
|
|
633
591
|
timestamp: new Date().toISOString(),
|
|
@@ -638,10 +596,6 @@ async function watchDeviceLogsBySse(ip, port, durationMs = 15000, maxLogs = 200)
|
|
|
638
596
|
// 兼容非 JSON 数据:不抛错,继续监听后续事件
|
|
639
597
|
}
|
|
640
598
|
}
|
|
641
|
-
if (logs.length >= maxLogs) {
|
|
642
|
-
controller.abort();
|
|
643
|
-
break;
|
|
644
|
-
}
|
|
645
599
|
splitIndex = buffer.search(/\r?\n\r?\n/);
|
|
646
600
|
}
|
|
647
601
|
}
|
|
@@ -653,21 +607,19 @@ async function watchDeviceLogsBySse(ip, port, durationMs = 15000, maxLogs = 200)
|
|
|
653
607
|
error.message === "The operation was aborted."))) {
|
|
654
608
|
throw error;
|
|
655
609
|
}
|
|
610
|
+
if (stopReason === "unknown") {
|
|
611
|
+
stopReason = "manual_abort";
|
|
612
|
+
}
|
|
656
613
|
}
|
|
657
614
|
finally {
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
const warnCount = findings.filter((item) => item.severity === "warn").length;
|
|
663
|
-
const summary = findings.length === 0
|
|
664
|
-
? `日志监听完成:共 ${logs.length} 条日志,未发现明显异常。`
|
|
665
|
-
: `日志监听完成:共 ${logs.length} 条日志,发现 ${errorCount} 条 error 风险、${warnCount} 条 warn 风险。`;
|
|
615
|
+
if (timeoutHandle) {
|
|
616
|
+
clearTimeout(timeoutHandle);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
666
619
|
return {
|
|
667
620
|
logs,
|
|
668
621
|
runtimeStatus,
|
|
669
|
-
|
|
670
|
-
summary,
|
|
622
|
+
stopReason,
|
|
671
623
|
};
|
|
672
624
|
}
|
|
673
625
|
/**
|