ms-vite-plugin 1.4.14 → 1.4.16

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.
@@ -33,13 +33,16 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.APPLE_OCR_LANGUAGES = void 0;
37
+ exports.formatOcrToolText = formatOcrToolText;
38
+ exports.runOcrRecognitionOnDevice = runOcrRecognitionOnDevice;
36
39
  exports.registerOcrTools = registerOcrTools;
37
40
  const fsExtra = __importStar(require("fs-extra"));
38
41
  const os = __importStar(require("os"));
39
42
  const path = __importStar(require("path"));
40
43
  const z = __importStar(require("zod/v4"));
41
44
  const tool_utils_1 = require("./tool-utils");
42
- const APPLE_OCR_LANGUAGES = [
45
+ exports.APPLE_OCR_LANGUAGES = [
43
46
  "en-US",
44
47
  "fr-FR",
45
48
  "it-IT",
@@ -82,21 +85,22 @@ function resolveOcrOutputPath(outputPath) {
82
85
  * @param ey 区域右下角 y
83
86
  * @param texts 查找文本数组
84
87
  * @param languages 识别语言数组
88
+ * @param exactMatch 是否完整匹配文本
85
89
  * @returns 返回可交给 runScript 的 JavaScript 脚本
86
90
  * @example
87
91
  * buildAppleOcrScript("recognize", "screen", 0, 0, 0, 0)
88
92
  */
89
- function buildAppleOcrScript(mode, input, x, y, ex, ey, texts, languages) {
93
+ function buildAppleOcrScript(mode, input, x, y, ex, ey, texts, languages, exactMatch = false) {
90
94
  if (mode === "findText") {
91
95
  if (!texts || texts.length === 0) {
92
96
  throw new Error("findText 模式必须传 texts。");
93
97
  }
94
- return `appleOcr.findText(${jsonLiteral(input)}, ${jsonLiteral(texts)}, ${x}, ${y}, ${ex}, ${ey}, ${jsonLiteral(languages)});`;
98
+ return `appleOcr.findTextAbs(${jsonLiteral(input)}, ${jsonLiteral(texts)}, ${x}, ${y}, ${ex}, ${ey}, ${jsonLiteral(languages)}, ${exactMatch});`;
95
99
  }
96
100
  if (mode === "numbers") {
97
- return `appleOcr.recognizeNumbers(${jsonLiteral(input)}, ${x}, ${y}, ${ex}, ${ey});`;
101
+ return `appleOcr.recognizeNumbersAbs(${jsonLiteral(input)}, ${x}, ${y}, ${ex}, ${ey});`;
98
102
  }
99
- return `appleOcr.recognize(${jsonLiteral(input)}, ${x}, ${y}, ${ex}, ${ey}, ${jsonLiteral(languages)});`;
103
+ return `appleOcr.recognizeAbs(${jsonLiteral(input)}, ${x}, ${y}, ${ex}, ${ey}, ${jsonLiteral(languages)});`;
100
104
  }
101
105
  /**
102
106
  * 构建 Paddle OCR 运行脚本
@@ -108,35 +112,22 @@ function buildAppleOcrScript(mode, input, x, y, ex, ey, texts, languages) {
108
112
  * @param ey 区域右下角 y
109
113
  * @param texts 查找文本数组
110
114
  * @param confidenceThreshold 置信度阈值
111
- * @param maxSideLen 模型最大边长
112
- * @param useGpu 是否使用 GPU
115
+ * @param exactMatch 是否完整匹配文本
113
116
  * @returns 返回可交给 runScript 的 JavaScript 脚本
114
117
  * @example
115
118
  * buildPaddleOcrScript("recognize", "screen", 0, 0, 0, 0)
116
119
  */
117
- function buildPaddleOcrScript(mode, input, x, y, ex, ey, texts, confidenceThreshold, maxSideLen, useGpu) {
120
+ function buildPaddleOcrScript(mode, input, x, y, ex, ey, texts, confidenceThreshold, exactMatch) {
118
121
  if (mode === "numbers") {
119
122
  throw new Error("PaddleOCR 文档未提供 numbers 模式,请使用 appleocr 引擎。");
120
123
  }
121
- const loadScript = `const __loaded = paddleOcr.loadV5(${maxSideLen}, ${useGpu});`;
122
- const loadFailedScript = 'let __ocrResult; if (!__loaded) { __ocrResult = { success: false, error: "PaddleOCR loadV5 failed" }; }';
123
124
  if (mode === "findText") {
124
125
  if (!texts || texts.length === 0) {
125
126
  throw new Error("findText 模式必须传 texts。");
126
127
  }
127
- return [
128
- loadScript,
129
- loadFailedScript,
130
- `if (__loaded) { __ocrResult = paddleOcr.findText(${jsonLiteral(input)}, ${jsonLiteral(texts)}, ${x}, ${y}, ${ex}, ${ey}, ${confidenceThreshold}); }`,
131
- "__ocrResult;",
132
- ].join("\n");
128
+ return `paddleOcr.findTextAbs(${jsonLiteral(input)}, ${jsonLiteral(texts)}, ${x}, ${y}, ${ex}, ${ey}, ${confidenceThreshold}, ${exactMatch});`;
133
129
  }
134
- return [
135
- loadScript,
136
- loadFailedScript,
137
- `if (__loaded) { __ocrResult = paddleOcr.recognize(${jsonLiteral(input)}, ${x}, ${y}, ${ex}, ${ey}, ${confidenceThreshold}); }`,
138
- "__ocrResult;",
139
- ].join("\n");
130
+ return `paddleOcr.recognizeAbs(${jsonLiteral(input)}, ${x}, ${y}, ${ex}, ${ey}, ${confidenceThreshold});`;
140
131
  }
141
132
  /**
142
133
  * 构建 OCR 运行脚本
@@ -150,17 +141,16 @@ function buildPaddleOcrScript(mode, input, x, y, ex, ey, texts, confidenceThresh
150
141
  * @param texts 查找文本数组
151
142
  * @param languages Apple OCR 识别语言数组
152
143
  * @param confidenceThreshold PaddleOCR 置信度阈值
153
- * @param paddleMaxSideLen PaddleOCR 模型最大边长
154
- * @param paddleUseGpu PaddleOCR 是否使用 GPU
144
+ * @param exactMatch 是否完整匹配文本
155
145
  * @returns 返回可交给 runScript 的 JavaScript 脚本
156
146
  * @example
157
147
  * buildOcrScript("appleocr", "recognize", "screen", 0, 0, 0, 0)
158
148
  */
159
- function buildOcrScript(engine, mode, input, x, y, ex, ey, texts, languages, confidenceThreshold, paddleMaxSideLen, paddleUseGpu) {
149
+ function buildOcrScript(engine, mode, input, x, y, ex, ey, texts, languages, confidenceThreshold, exactMatch) {
160
150
  if (engine === "paddleocr") {
161
- return buildPaddleOcrScript(mode, input, x, y, ex, ey, texts, confidenceThreshold, paddleMaxSideLen, paddleUseGpu);
151
+ return buildPaddleOcrScript(mode, input, x, y, ex, ey, texts, confidenceThreshold, exactMatch);
162
152
  }
163
- return buildAppleOcrScript(mode, input, x, y, ex, ey, texts, languages);
153
+ return buildAppleOcrScript(mode, input, x, y, ex, ey, texts, languages, exactMatch);
164
154
  }
165
155
  /**
166
156
  * 调用设备 runScript 接口
@@ -242,6 +232,24 @@ async function formatOcrToolText(response, outputPath) {
242
232
  responseText,
243
233
  ].join("\n");
244
234
  }
235
+ /**
236
+ * 在指定 HTTP 设备上执行 OCR 识别
237
+ * @param target 设备 HTTP 地址
238
+ * @param options OCR 参数
239
+ * @returns 返回 HTTP 状态码、原始结果与格式化文本
240
+ * @example
241
+ * await runOcrRecognitionOnDevice({ ip: "192.168.1.10", port: "9800" }, options)
242
+ */
243
+ async function runOcrRecognitionOnDevice(target, options) {
244
+ const script = buildOcrScript(options.engine, options.mode, options.input, options.x, options.y, options.ex, options.ey, options.texts, options.languages, options.confidenceThreshold, options.exactMatch);
245
+ const response = await callRunScript(target.ip, target.port, script, options.timeoutMs);
246
+ const text = await formatOcrToolText(response.body, options.outputPath);
247
+ return {
248
+ status: response.status,
249
+ body: response.body,
250
+ text,
251
+ };
252
+ }
245
253
  /**
246
254
  * 注册 OCR MCP 工具
247
255
  * @param server MCP 服务实例
@@ -252,7 +260,7 @@ async function formatOcrToolText(response, outputPath) {
252
260
  function registerOcrTools(server) {
253
261
  server.registerTool("ocr_recognize", {
254
262
  title: "OCR Recognize",
255
- description: "通过设备 POST /api/runScript 执行快点JS OCR。支持 appleocr 与 paddleocr,默认 appleocr;OCR 没有独立 HTTP 接口时使用本工具。",
263
+ description: "在当前默认设备执行 OCR。支持 appleocr 与 paddleocr,默认 appleocr;返回坐标使用绝对坐标。",
256
264
  inputSchema: {
257
265
  engine: z
258
266
  .enum(["appleocr", "paddleocr"])
@@ -291,9 +299,14 @@ function registerOcrTools(server) {
291
299
  .optional()
292
300
  .describe("findText 模式要查找的文本数组"),
293
301
  languages: z
294
- .array(z.enum(APPLE_OCR_LANGUAGES))
302
+ .array(z.enum(exports.APPLE_OCR_LANGUAGES))
295
303
  .optional()
296
304
  .describe('Apple OCR 识别语言数组,默认由运行时使用 ["zh-Hans", "en-US"]'),
305
+ exactMatch: z
306
+ .boolean()
307
+ .optional()
308
+ .default(false)
309
+ .describe("findText 模式是否要求整条识别文本完全匹配目标文本"),
297
310
  confidenceThreshold: z
298
311
  .number()
299
312
  .min(0)
@@ -301,19 +314,6 @@ function registerOcrTools(server) {
301
314
  .optional()
302
315
  .default(0.6)
303
316
  .describe("PaddleOCR 置信度阈值,默认 0.6"),
304
- paddleMaxSideLen: z
305
- .number()
306
- .int()
307
- .min(32)
308
- .max(4096)
309
- .optional()
310
- .default(640)
311
- .describe("PaddleOCR loadV5 最大边长,默认 640"),
312
- paddleUseGpu: z
313
- .boolean()
314
- .optional()
315
- .default(false)
316
- .describe("PaddleOCR loadV5 是否使用 GPU,默认 false"),
317
317
  outputPath: z
318
318
  .string()
319
319
  .min(1)
@@ -328,20 +328,31 @@ function registerOcrTools(server) {
328
328
  .default(60000)
329
329
  .describe("runScript 请求超时时间,默认 60000 毫秒"),
330
330
  },
331
- }, async ({ engine, mode, input, x, y, ex, ey, texts, languages, confidenceThreshold, paddleMaxSideLen, paddleUseGpu, outputPath, timeoutMs, }) => {
331
+ }, async ({ engine, mode, input, x, y, ex, ey, texts, languages, exactMatch, confidenceThreshold, outputPath, timeoutMs, }) => {
332
332
  const target = await (0, tool_utils_1.resolveRuntimeHttpTarget)();
333
- const script = buildOcrScript(engine, mode, input, x, y, ex, ey, texts, languages, confidenceThreshold, paddleMaxSideLen, paddleUseGpu);
334
- const response = await callRunScript(target.ip, target.port, script, timeoutMs);
335
- const text = await formatOcrToolText(response.body, outputPath);
333
+ const response = await runOcrRecognitionOnDevice(target, {
334
+ engine,
335
+ mode,
336
+ input,
337
+ x,
338
+ y,
339
+ ex,
340
+ ey,
341
+ texts,
342
+ languages,
343
+ confidenceThreshold,
344
+ exactMatch,
345
+ outputPath,
346
+ timeoutMs,
347
+ });
336
348
  return (0, tool_utils_1.createTextToolResult)([
337
- text,
349
+ response.text,
338
350
  "",
339
351
  `device: ${target.label}`,
340
352
  `engine: ${engine}`,
341
353
  `mode: ${mode}`,
342
354
  `input: ${input}`,
343
355
  `region: x=${x}, y=${y}, ex=${ex}, ey=${ey}`,
344
- `runScript: POST /api/runScript`,
345
356
  `status: ${response.status}`,
346
357
  ].join("\n"), response.body.success === false);
347
358
  });
@@ -142,13 +142,7 @@ function registerRuntimeTools(server) {
142
142
  },
143
143
  }, async ({ ip, port }) => {
144
144
  const config = await (0, device_config_1.setDeviceConfig)(ip, port);
145
- const reusedSubscription = (0, device_log_1.ensureDeviceLogSubscription)(config.ip, config.port);
146
- return (0, tool_utils_1.createTextToolResult)([
147
- `默认设备已设置为 ${config.ip}:${config.port}`,
148
- reusedSubscription
149
- ? "SSE 日志后台订阅已复用现有连接"
150
- : `SSE 日志后台订阅已启动,内存缓存上限 ${device_log_1.DEVICE_LOG_MEMORY_LIMIT} 条`,
151
- ].join("\n"));
145
+ return (0, tool_utils_1.createTextToolResult)(`默认设备已设置为 ${config.ip}:${config.port}`);
152
146
  });
153
147
  server.registerTool("get_device", {
154
148
  title: "Get Device",
@@ -191,7 +185,7 @@ function registerRuntimeTools(server) {
191
185
  });
192
186
  server.registerTool("get_node_source", {
193
187
  title: "Get Node Source",
194
- description: "获取当前默认设备页面节点 XML(/api/source),写入文件后返回路径,避免长文本响应被截断。",
188
+ description: "获取当前默认设备页面节点 XML,写入文件后返回路径,避免长文本响应被截断。",
195
189
  inputSchema: {
196
190
  maxDepth: z
197
191
  .number()
@@ -209,24 +203,32 @@ function registerRuntimeTools(server) {
209
203
  .optional()
210
204
  .default(120)
211
205
  .describe("设备端节点抓取超时秒数,默认 120"),
206
+ mode: z
207
+ .number()
208
+ .int()
209
+ .min(1)
210
+ .max(2)
211
+ .optional()
212
+ .default(1)
213
+ .describe("节点抓取模式:1=完整节点树,2=可见节点优化模式"),
212
214
  outputPath: z
213
215
  .string()
214
216
  .min(1)
215
217
  .optional()
216
218
  .describe("可选输出文件路径,不传则写入系统临时目录"),
217
219
  },
218
- }, async ({ maxDepth, timeout, outputPath }) => {
220
+ }, async ({ maxDepth, timeout, mode, outputPath }) => {
219
221
  const target = await (0, tool_utils_1.resolveRuntimeHttpTarget)();
220
222
  const requestOptions = (0, tool_utils_1.createRuntimeHttpRequestOptions)(target);
221
- const source = await (0, project_1.getSourceOnDevice)(requestOptions, maxDepth, timeout);
223
+ const source = await (0, project_1.getSourceOnDevice)(requestOptions, maxDepth, timeout, mode);
222
224
  const targetPath = resolveOutputPath(outputPath, "ms-mcp-node-source", "xml");
223
225
  await fsExtra.ensureDir(path.dirname(targetPath));
224
226
  await fsExtra.writeFile(targetPath, source, "utf8");
225
- return (0, tool_utils_1.createTextToolResult)(`节点获取成功: ${target.label}\nmaxDepth: ${maxDepth}\ntimeout: ${timeout}\npath: ${targetPath}\nsize: ${Buffer.byteLength(source, "utf8")} bytes`);
227
+ return (0, tool_utils_1.createTextToolResult)(`节点获取成功: ${target.label}\nmaxDepth: ${maxDepth}\ntimeout: ${timeout}\nmode: ${mode}\npath: ${targetPath}\nsize: ${Buffer.byteLength(source, "utf8")} bytes`);
226
228
  });
227
229
  server.registerTool("get_logs", {
228
230
  title: "Get Device Logs",
229
- description: "获取当前默认设备的日志缓存快照。调用 set_device 后会自动建立 SSE 后台订阅,此工具只返回当前已缓存的日志内容。",
231
+ description: "获取当前默认设备的最新日志行。",
230
232
  inputSchema: {
231
233
  limit: z
232
234
  .number()
@@ -236,19 +238,10 @@ function registerRuntimeTools(server) {
236
238
  .optional()
237
239
  .default(200)
238
240
  .describe("返回最近日志条数,默认 200,最大 5000"),
239
- runtimeStatusLimit: z
240
- .number()
241
- .int()
242
- .min(0)
243
- .max(200)
244
- .optional()
245
- .default(20)
246
- .describe("返回最近 runtime_status 条数,默认 20"),
247
241
  },
248
- }, async ({ limit, runtimeStatusLimit }) => {
242
+ }, async ({ limit }) => {
249
243
  const target = await (0, tool_utils_1.resolveRuntimeHttpTarget)();
250
- (0, device_log_1.ensureDeviceLogSubscription)(target.ip, Number.parseInt(target.port, 10));
251
- return (0, tool_utils_1.createTextToolResult)((0, device_log_1.buildDeviceLogSnapshotText)(limit, runtimeStatusLimit));
244
+ return (0, tool_utils_1.createTextToolResult)(await (0, device_log_1.buildDeviceLogSnapshotText)(target, limit));
252
245
  });
253
246
  server.registerTool("package_project", {
254
247
  title: "Package Project",
@@ -67,8 +67,9 @@ export declare function formatRuntimeJsonText(value: unknown): string;
67
67
  * @param title 文档标题
68
68
  * @param slug 文档 slug
69
69
  * @param index 列表序号
70
+ * @param filePath 文档文件绝对路径
70
71
  * @returns 返回用于列表展示的文本块
71
72
  * @example
72
- * formatApiDocSummary("js", "Tap", "tap", 0)
73
+ * formatApiDocSummary("js", "Tap", "tap", 0, "/tmp/tap.md")
73
74
  */
74
- export declare function formatApiDocSummary(language: ApiDocsLanguage, title: string, slug: string, index: number): string;
75
+ export declare function formatApiDocSummary(language: ApiDocsLanguage, title: string, slug: string, index: number, filePath?: string): string;
@@ -87,10 +87,16 @@ function formatRuntimeJsonText(value) {
87
87
  * @param title 文档标题
88
88
  * @param slug 文档 slug
89
89
  * @param index 列表序号
90
+ * @param filePath 文档文件绝对路径
90
91
  * @returns 返回用于列表展示的文本块
91
92
  * @example
92
- * formatApiDocSummary("js", "Tap", "tap", 0)
93
+ * formatApiDocSummary("js", "Tap", "tap", 0, "/tmp/tap.md")
93
94
  */
94
- function formatApiDocSummary(language, title, slug, index) {
95
- return `${index + 1}. ${title}\nslug: ${slug}\nuri: ${(0, docs_service_1.getDocUri)(language, slug)}`;
95
+ function formatApiDocSummary(language, title, slug, index, filePath) {
96
+ return [
97
+ `${index + 1}. ${title}`,
98
+ `slug: ${slug}`,
99
+ `uri: ${(0, docs_service_1.getDocUri)(language, slug)}`,
100
+ ...(filePath ? [`path: ${filePath}`] : []),
101
+ ].join("\n");
96
102
  }
package/dist/mcp/tools.js CHANGED
@@ -22,7 +22,6 @@ function createMcpServer() {
22
22
  name: "ms-vite-plugin-mcp",
23
23
  version: version_1.PACKAGE_VERSION,
24
24
  });
25
- (0, doc_tools_1.registerDocResources)(server);
26
25
  (0, doc_tools_1.registerDocTools)(server);
27
26
  (0, httpapi_tools_1.registerHttpApiTools)(server);
28
27
  (0, image_tools_1.registerImageTools)(server);
package/dist/project.d.ts CHANGED
@@ -10,35 +10,13 @@ export interface DeviceCliOptions {
10
10
  workspacePath?: string;
11
11
  }
12
12
  /**
13
- * 设备日志条目
13
+ * 当前日志最新行接口响应
14
14
  */
15
- export interface DeviceLogEntry {
16
- /** 日志级别,例如 debug/info/warn/error */
17
- level: string;
18
- /** 日志文本 */
19
- message: string;
20
- /** 日志时间戳(ISO 字符串) */
21
- timestamp: string;
22
- }
23
- /**
24
- * 运行时状态条目(来自 SSE runtime_status 事件)
25
- */
26
- export interface DeviceRuntimeStatusEntry {
27
- /** 原始 runtime_status 数据 */
28
- raw: Record<string, unknown>;
29
- /** 事件时间戳(本地接收时间) */
30
- timestamp: string;
31
- }
32
- /**
33
- * SSE 日志监听结果
34
- */
35
- export interface DeviceLogWatchResult {
36
- /** 收集到的日志条目 */
37
- logs: DeviceLogEntry[];
38
- /** 收集到的 runtime_status 条目 */
39
- runtimeStatus: DeviceRuntimeStatusEntry[];
40
- /** 监听结束原因 */
41
- stopReason: "timeout" | "runtime_continuous" | "runtime_stopped" | "stream_closed" | "manual_abort" | "unknown";
15
+ export interface CurrentLogLinesResponse {
16
+ /** 当前日志文件最新行,按日志原始输出顺序排列 */
17
+ lines: string[];
18
+ /** 本次返回范围前是否还有更早日志 */
19
+ hasMore: boolean;
42
20
  }
43
21
  /**
44
22
  * 在设备上获取截图 JPG 二进制数据(HTTP)
@@ -61,24 +39,21 @@ export declare function getScreenshotBase64OnDevice(options: DeviceCliOptions):
61
39
  * @param options 命令选项(transport 仅支持 http)
62
40
  * @param maxDepth 节点最大深度,默认 50
63
41
  * @param timeout 获取节点超时秒数,默认 120
42
+ * @param mode 节点抓取模式,1=完整节点树,2=可见节点优化模式
64
43
  * @returns 返回 XML 字符串
65
44
  * @example
66
- * const xml = await getSourceOnDevice({ ip: "192.168.1.10", port: "9800" }, 50, 120)
45
+ * const xml = await getSourceOnDevice({ ip: "192.168.1.10", port: "9800" }, 50, 120, 1)
67
46
  */
68
- export declare function getSourceOnDevice(options: DeviceCliOptions, maxDepth?: number, timeout?: number): Promise<string>;
47
+ export declare function getSourceOnDevice(options: DeviceCliOptions, maxDepth?: number, timeout?: number, mode?: number): Promise<string>;
69
48
  /**
70
- * 监听设备 SSE 日志并返回原始结果
71
- * @param ip 设备 IP 地址
72
- * @param port 设备端口
73
- * @param durationMs 监听时长(毫秒),默认 15000;传 0 表示不设超时,持续监听
74
- * @param maxLogs 最多收集日志条数,默认 200
75
- * @param stopOnRuntimeStopped 若为 true,则在运行状态从 true 回落到 false 后结束监听
76
- * @param stopOnRuntimeContinuous 若为 true,则在确认持续运行态后结束监听(用于 single 模式快速返回)
77
- * @returns 返回日志与运行状态
49
+ * 在设备上获取当前日志文件最新行
50
+ * @param options 命令选项(仅支持 http 传输)
51
+ * @param count 需要读取的最新日志行数,默认 200,最大 5000
52
+ * @returns 返回最新日志行和是否还有更早日志
78
53
  * @example
79
- * const result = await watchDeviceLogsBySse("192.168.1.10", 9800, 30000, 300, true, true)
54
+ * const result = await getCurrentLogLinesOnDevice({ ip: "192.168.1.10", port: "9800" }, 200)
80
55
  */
81
- export declare function watchDeviceLogsBySse(ip: string, port: number, durationMs?: number, maxLogs?: number, stopOnRuntimeStopped?: boolean, stopOnRuntimeContinuous?: boolean): Promise<DeviceLogWatchResult>;
56
+ export declare function getCurrentLogLinesOnDevice(options: DeviceCliOptions, count?: number): Promise<CurrentLogLinesResponse>;
82
57
  /**
83
58
  * 在设备上运行项目(先同步后运行)
84
59
  * @param options 命令选项