ms-vite-plugin 1.1.7 → 1.1.8
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 +130 -37
- package/dist/project.d.ts +4 -3
- package/dist/project.js +14 -2
- package/package.json +1 -1
package/dist/mcp/tools.js
CHANGED
|
@@ -303,29 +303,62 @@ function registerRuntimeTools(server) {
|
|
|
303
303
|
return workspace;
|
|
304
304
|
}
|
|
305
305
|
/**
|
|
306
|
-
*
|
|
306
|
+
* 格式化运行期日志结果(供 run/run_ui 直接回传)
|
|
307
307
|
* @param ip 设备 IP
|
|
308
308
|
* @param port 设备端口
|
|
309
309
|
* @param durationSeconds 监听秒数
|
|
310
|
-
* @param
|
|
311
|
-
* @returns
|
|
310
|
+
* @param result 日志监听结果
|
|
311
|
+
* @returns 可拼接到 tool 响应的文本
|
|
312
312
|
* @example
|
|
313
|
-
* const
|
|
313
|
+
* const text = formatRunLogSection("192.168.1.10", 9800, 10, result)
|
|
314
314
|
*/
|
|
315
|
-
function
|
|
316
|
-
const
|
|
317
|
-
|
|
315
|
+
function formatRunLogSection(ip, port, durationSeconds, result) {
|
|
316
|
+
const logsPreview = result.logs
|
|
317
|
+
.slice(-20)
|
|
318
|
+
.map((log) => `[${log.timestamp}] [${log.level.toUpperCase()}] ${log.message}`)
|
|
319
|
+
.join("\n");
|
|
320
|
+
return [
|
|
321
|
+
`启动期日志窗口: ${durationSeconds}s(${ip}:${port})`,
|
|
322
|
+
`结束原因: ${result.stopReason},日志: ${result.logs.length} 条,runtime_status: ${result.runtimeStatus.length} 条`,
|
|
323
|
+
"最新日志(最多展示最后20条):",
|
|
324
|
+
logsPreview || "无日志",
|
|
325
|
+
].join("\n");
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* 提取最后一次 runtime_status 的 isRunning 值
|
|
329
|
+
* @param result 日志监听结果
|
|
330
|
+
* @returns 返回 true/false,无法判断时返回 undefined
|
|
331
|
+
* @example
|
|
332
|
+
* const running = getLatestIsRunning(result)
|
|
333
|
+
*/
|
|
334
|
+
function getLatestIsRunning(result) {
|
|
335
|
+
for (let i = result.runtimeStatus.length - 1; i >= 0; i -= 1) {
|
|
336
|
+
const raw = result.runtimeStatus[i].raw;
|
|
337
|
+
if (typeof raw.isRunning === "boolean") {
|
|
338
|
+
return raw.isRunning;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return undefined;
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* 启动持续日志监听(后台,不阻塞当前 tool 返回)
|
|
345
|
+
* @param ip 设备 IP
|
|
346
|
+
* @param port 设备端口
|
|
347
|
+
* @param maxLogs 日志环形缓冲上限
|
|
348
|
+
* @returns 返回提示文本
|
|
349
|
+
* @example
|
|
350
|
+
* const tip = startBackgroundContinuousLogWatch("192.168.1.10", 9800, 300)
|
|
351
|
+
*/
|
|
352
|
+
function startBackgroundContinuousLogWatch(ip, port, maxLogs) {
|
|
353
|
+
void (0, project_1.watchDeviceLogsBySse)(ip, port, 0, maxLogs, false)
|
|
318
354
|
.then((result) => {
|
|
319
|
-
console.log(`[mcp]
|
|
355
|
+
console.log(`[mcp] continuous logs ${ip}:${port} ended, reason=${result.stopReason}, logs=${result.logs.length}, runtime_status=${result.runtimeStatus.length}`);
|
|
320
356
|
})
|
|
321
357
|
.catch((error) => {
|
|
322
358
|
const message = error instanceof Error ? error.message : String(error);
|
|
323
|
-
console.warn(`[mcp]
|
|
359
|
+
console.warn(`[mcp] continuous logs ${ip}:${port} failed: ${message}`);
|
|
324
360
|
});
|
|
325
|
-
|
|
326
|
-
return `已在后台启动持续日志监听: ${ip}:${port}(直到连接断开,max ${maxLogs})`;
|
|
327
|
-
}
|
|
328
|
-
return `已在后台启动日志监听: ${ip}:${port}(${durationSeconds}s,max ${maxLogs})`;
|
|
361
|
+
return `已切换为持续日志监听: ${ip}:${port}(直到连接断开,max ${maxLogs})`;
|
|
329
362
|
}
|
|
330
363
|
/**
|
|
331
364
|
* 解析运行目标:
|
|
@@ -760,14 +793,19 @@ function registerRuntimeTools(server) {
|
|
|
760
793
|
.optional()
|
|
761
794
|
.default(true)
|
|
762
795
|
.describe("是否在运行前启动实时日志监听,默认 true"),
|
|
796
|
+
logMode: z
|
|
797
|
+
.enum(["auto", "single", "continuous"])
|
|
798
|
+
.optional()
|
|
799
|
+
.default("auto")
|
|
800
|
+
.describe("日志模式:auto=单次优先并自动判长任务,single=按 runtime_status 收敛,continuous=持续监听"),
|
|
763
801
|
logDurationSeconds: z
|
|
764
802
|
.number()
|
|
765
803
|
.int()
|
|
766
|
-
.min(
|
|
804
|
+
.min(1)
|
|
767
805
|
.max(300)
|
|
768
806
|
.optional()
|
|
769
|
-
.default(
|
|
770
|
-
.describe("日志监听时长(秒),默认
|
|
807
|
+
.default(10)
|
|
808
|
+
.describe("日志监听时长(秒),默认 10,用于回传 run 启动期实时输出"),
|
|
771
809
|
logMaxLogs: z
|
|
772
810
|
.number()
|
|
773
811
|
.int()
|
|
@@ -777,15 +815,25 @@ function registerRuntimeTools(server) {
|
|
|
777
815
|
.default(300)
|
|
778
816
|
.describe("日志最大收集条数,默认 300"),
|
|
779
817
|
},
|
|
780
|
-
}, async ({ transport, ip, port, wsPort, wsWaitMs, workspacePath, watchLogs, logDurationSeconds, logMaxLogs, }) => {
|
|
818
|
+
}, async ({ transport, ip, port, wsPort, wsWaitMs, workspacePath, watchLogs, logMode, logDurationSeconds, logMaxLogs, }) => {
|
|
781
819
|
const workspace = await ensureWorkspacePath(workspacePath);
|
|
782
|
-
let
|
|
820
|
+
let logPromise;
|
|
821
|
+
let logDeviceIp = "";
|
|
822
|
+
let logDevicePort = 0;
|
|
783
823
|
let logSetupWarning = "";
|
|
824
|
+
let logModeNotice = "";
|
|
784
825
|
if (watchLogs) {
|
|
785
826
|
try {
|
|
786
827
|
const logDevice = await (0, device_config_1.resolveDeviceConfig)(ip, port);
|
|
787
|
-
//
|
|
788
|
-
|
|
828
|
+
// 先挂监听,再执行 run,确保拿到启动期实时日志
|
|
829
|
+
logDeviceIp = logDevice.ip;
|
|
830
|
+
logDevicePort = logDevice.port;
|
|
831
|
+
if (logMode === "continuous") {
|
|
832
|
+
logModeNotice = startBackgroundContinuousLogWatch(logDevice.ip, logDevice.port, logMaxLogs);
|
|
833
|
+
}
|
|
834
|
+
else {
|
|
835
|
+
logPromise = (0, project_1.watchDeviceLogsBySse)(logDevice.ip, logDevice.port, logDurationSeconds * 1000, logMaxLogs, logMode === "single" || logMode === "auto");
|
|
836
|
+
}
|
|
789
837
|
}
|
|
790
838
|
catch (error) {
|
|
791
839
|
logSetupWarning = `日志监听未启动: ${error instanceof Error ? error.message : String(error)}`;
|
|
@@ -811,11 +859,26 @@ function registerRuntimeTools(server) {
|
|
|
811
859
|
transport: "http",
|
|
812
860
|
workspacePath: workspace,
|
|
813
861
|
});
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
862
|
+
let logSection = "";
|
|
863
|
+
if (logPromise) {
|
|
864
|
+
const result = await logPromise;
|
|
865
|
+
const sections = [
|
|
866
|
+
formatRunLogSection(logDeviceIp, logDevicePort, logDurationSeconds, result),
|
|
867
|
+
];
|
|
868
|
+
if (logMode === "auto") {
|
|
869
|
+
const latestIsRunning = getLatestIsRunning(result);
|
|
870
|
+
if (result.stopReason === "timeout" && latestIsRunning === true) {
|
|
871
|
+
sections.push(startBackgroundContinuousLogWatch(logDeviceIp, logDevicePort, logMaxLogs));
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
logSection = `\n\n${sections.join("\n")}`;
|
|
875
|
+
}
|
|
876
|
+
else if (logModeNotice) {
|
|
877
|
+
logSection = `\n\n${logModeNotice}`;
|
|
878
|
+
}
|
|
879
|
+
else if (logSetupWarning) {
|
|
880
|
+
logSection = `\n\n${logSetupWarning}`;
|
|
881
|
+
}
|
|
819
882
|
return {
|
|
820
883
|
content: [
|
|
821
884
|
{
|
|
@@ -869,14 +932,19 @@ function registerRuntimeTools(server) {
|
|
|
869
932
|
.optional()
|
|
870
933
|
.default(true)
|
|
871
934
|
.describe("是否在预览前启动实时日志监听,默认 true"),
|
|
935
|
+
logMode: z
|
|
936
|
+
.enum(["auto", "single", "continuous"])
|
|
937
|
+
.optional()
|
|
938
|
+
.default("auto")
|
|
939
|
+
.describe("日志模式:auto=单次优先并自动判长任务,single=按 runtime_status 收敛,continuous=持续监听"),
|
|
872
940
|
logDurationSeconds: z
|
|
873
941
|
.number()
|
|
874
942
|
.int()
|
|
875
|
-
.min(
|
|
943
|
+
.min(1)
|
|
876
944
|
.max(300)
|
|
877
945
|
.optional()
|
|
878
|
-
.default(
|
|
879
|
-
.describe("日志监听时长(秒),默认
|
|
946
|
+
.default(10)
|
|
947
|
+
.describe("日志监听时长(秒),默认 10,用于回传 run-ui 启动期实时输出"),
|
|
880
948
|
logMaxLogs: z
|
|
881
949
|
.number()
|
|
882
950
|
.int()
|
|
@@ -886,15 +954,25 @@ function registerRuntimeTools(server) {
|
|
|
886
954
|
.default(300)
|
|
887
955
|
.describe("日志最大收集条数,默认 300"),
|
|
888
956
|
},
|
|
889
|
-
}, async ({ transport, ip, port, wsPort, wsWaitMs, workspacePath, watchLogs, logDurationSeconds, logMaxLogs, }) => {
|
|
957
|
+
}, async ({ transport, ip, port, wsPort, wsWaitMs, workspacePath, watchLogs, logMode, logDurationSeconds, logMaxLogs, }) => {
|
|
890
958
|
const workspace = await ensureWorkspacePath(workspacePath);
|
|
891
|
-
let
|
|
959
|
+
let logPromise;
|
|
960
|
+
let logDeviceIp = "";
|
|
961
|
+
let logDevicePort = 0;
|
|
892
962
|
let logSetupWarning = "";
|
|
963
|
+
let logModeNotice = "";
|
|
893
964
|
if (watchLogs) {
|
|
894
965
|
try {
|
|
895
966
|
const logDevice = await (0, device_config_1.resolveDeviceConfig)(ip, port);
|
|
896
|
-
//
|
|
897
|
-
|
|
967
|
+
// 先挂监听,再执行 run-ui,确保拿到启动期实时日志
|
|
968
|
+
logDeviceIp = logDevice.ip;
|
|
969
|
+
logDevicePort = logDevice.port;
|
|
970
|
+
if (logMode === "continuous") {
|
|
971
|
+
logModeNotice = startBackgroundContinuousLogWatch(logDevice.ip, logDevice.port, logMaxLogs);
|
|
972
|
+
}
|
|
973
|
+
else {
|
|
974
|
+
logPromise = (0, project_1.watchDeviceLogsBySse)(logDevice.ip, logDevice.port, logDurationSeconds * 1000, logMaxLogs, logMode === "single" || logMode === "auto");
|
|
975
|
+
}
|
|
898
976
|
}
|
|
899
977
|
catch (error) {
|
|
900
978
|
logSetupWarning = `日志监听未启动: ${error instanceof Error ? error.message : String(error)}`;
|
|
@@ -920,11 +998,26 @@ function registerRuntimeTools(server) {
|
|
|
920
998
|
transport: "http",
|
|
921
999
|
workspacePath: workspace,
|
|
922
1000
|
});
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
1001
|
+
let logSection = "";
|
|
1002
|
+
if (logPromise) {
|
|
1003
|
+
const result = await logPromise;
|
|
1004
|
+
const sections = [
|
|
1005
|
+
formatRunLogSection(logDeviceIp, logDevicePort, logDurationSeconds, result),
|
|
1006
|
+
];
|
|
1007
|
+
if (logMode === "auto") {
|
|
1008
|
+
const latestIsRunning = getLatestIsRunning(result);
|
|
1009
|
+
if (result.stopReason === "timeout" && latestIsRunning === true) {
|
|
1010
|
+
sections.push(startBackgroundContinuousLogWatch(logDeviceIp, logDevicePort, logMaxLogs));
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
logSection = `\n\n${sections.join("\n")}`;
|
|
1014
|
+
}
|
|
1015
|
+
else if (logModeNotice) {
|
|
1016
|
+
logSection = `\n\n${logModeNotice}`;
|
|
1017
|
+
}
|
|
1018
|
+
else if (logSetupWarning) {
|
|
1019
|
+
logSection = `\n\n${logSetupWarning}`;
|
|
1020
|
+
}
|
|
928
1021
|
return {
|
|
929
1022
|
content: [
|
|
930
1023
|
{
|
package/dist/project.d.ts
CHANGED
|
@@ -38,7 +38,7 @@ export interface DeviceLogWatchResult {
|
|
|
38
38
|
/** 收集到的 runtime_status 条目 */
|
|
39
39
|
runtimeStatus: DeviceRuntimeStatusEntry[];
|
|
40
40
|
/** 监听结束原因 */
|
|
41
|
-
stopReason: "timeout" | "stream_closed" | "manual_abort" | "unknown";
|
|
41
|
+
stopReason: "timeout" | "runtime_stopped" | "stream_closed" | "manual_abort" | "unknown";
|
|
42
42
|
}
|
|
43
43
|
/**
|
|
44
44
|
* 在设备上获取截图 JPG 二进制数据(HTTP)
|
|
@@ -72,11 +72,12 @@ export declare function getSourceOnDevice(options: DeviceCliOptions, maxDepth?:
|
|
|
72
72
|
* @param port 设备端口
|
|
73
73
|
* @param durationMs 监听时长(毫秒),默认 15000;传 0 表示不设超时,持续监听
|
|
74
74
|
* @param maxLogs 最多收集日志条数,默认 200
|
|
75
|
+
* @param stopOnRuntimeStopped 若为 true,则在运行状态从 true 回落到 false 后结束监听
|
|
75
76
|
* @returns 返回日志与运行状态
|
|
76
77
|
* @example
|
|
77
|
-
* const result = await watchDeviceLogsBySse("192.168.1.10", 9800,
|
|
78
|
+
* const result = await watchDeviceLogsBySse("192.168.1.10", 9800, 30000, 300, true)
|
|
78
79
|
*/
|
|
79
|
-
export declare function watchDeviceLogsBySse(ip: string, port: number, durationMs?: number, maxLogs?: number): Promise<DeviceLogWatchResult>;
|
|
80
|
+
export declare function watchDeviceLogsBySse(ip: string, port: number, durationMs?: number, maxLogs?: number, stopOnRuntimeStopped?: boolean): Promise<DeviceLogWatchResult>;
|
|
80
81
|
/**
|
|
81
82
|
* 在设备上运行项目(先同步后运行)
|
|
82
83
|
* @param options 命令选项
|
package/dist/project.js
CHANGED
|
@@ -515,11 +515,12 @@ function normalizeLogEntry(payload) {
|
|
|
515
515
|
* @param port 设备端口
|
|
516
516
|
* @param durationMs 监听时长(毫秒),默认 15000;传 0 表示不设超时,持续监听
|
|
517
517
|
* @param maxLogs 最多收集日志条数,默认 200
|
|
518
|
+
* @param stopOnRuntimeStopped 若为 true,则在运行状态从 true 回落到 false 后结束监听
|
|
518
519
|
* @returns 返回日志与运行状态
|
|
519
520
|
* @example
|
|
520
|
-
* const result = await watchDeviceLogsBySse("192.168.1.10", 9800,
|
|
521
|
+
* const result = await watchDeviceLogsBySse("192.168.1.10", 9800, 30000, 300, true)
|
|
521
522
|
*/
|
|
522
|
-
async function watchDeviceLogsBySse(ip, port, durationMs = 15000, maxLogs = 200) {
|
|
523
|
+
async function watchDeviceLogsBySse(ip, port, durationMs = 15000, maxLogs = 200, stopOnRuntimeStopped = false) {
|
|
523
524
|
if (!ip.trim()) {
|
|
524
525
|
throw new Error("监听日志失败: 设备 IP 不能为空");
|
|
525
526
|
}
|
|
@@ -537,6 +538,7 @@ async function watchDeviceLogsBySse(ip, port, durationMs = 15000, maxLogs = 200)
|
|
|
537
538
|
const runtimeStatus = [];
|
|
538
539
|
const controller = new AbortController();
|
|
539
540
|
let stopReason = "unknown";
|
|
541
|
+
let seenRunningStatus = false;
|
|
540
542
|
const timeoutHandle = durationMs > 0
|
|
541
543
|
? setTimeout(() => {
|
|
542
544
|
stopReason = "timeout";
|
|
@@ -590,6 +592,16 @@ async function watchDeviceLogsBySse(ip, port, durationMs = 15000, maxLogs = 200)
|
|
|
590
592
|
raw: parsed,
|
|
591
593
|
timestamp: new Date().toISOString(),
|
|
592
594
|
});
|
|
595
|
+
if (parsed.isRunning === true) {
|
|
596
|
+
seenRunningStatus = true;
|
|
597
|
+
}
|
|
598
|
+
if (stopOnRuntimeStopped &&
|
|
599
|
+
seenRunningStatus &&
|
|
600
|
+
parsed.isRunning === false) {
|
|
601
|
+
stopReason = "runtime_stopped";
|
|
602
|
+
controller.abort();
|
|
603
|
+
break;
|
|
604
|
+
}
|
|
593
605
|
}
|
|
594
606
|
}
|
|
595
607
|
catch {
|