weapp-ide-cli 5.2.11 → 5.3.1

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.
@@ -0,0 +1,434 @@
1
+ import { o as withMiniProgram$1 } from "./automator-session-BZzODsJi.js";
2
+ import { Buffer } from "node:buffer";
3
+ import fs from "node:fs/promises";
4
+ import path from "node:path";
5
+ import process from "node:process";
6
+ import { readDevtoolsElementSnapshot as readElementSnapshot, resolveDevtoolsProjectPath as resolveProjectPath, toDevtoolsSerializableValue as toSerializableValue } from "@weapp-vite/devtools-runtime";
7
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
8
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
9
+ import { z } from "zod";
10
+ //#region src/mcp/server.ts
11
+ function createResolvedProjectPath(workspaceRoot, projectPath) {
12
+ return path.isAbsolute(projectPath) ? path.normalize(projectPath) : path.resolve(workspaceRoot ?? process.cwd(), projectPath);
13
+ }
14
+ function createResolvedOutputPath(workspaceRoot, outputPath) {
15
+ return path.isAbsolute(outputPath) ? path.normalize(outputPath) : path.resolve(workspaceRoot ?? process.cwd(), outputPath);
16
+ }
17
+ async function withConnectedMiniProgram(runtimeHooks, workspaceRoot, input, runner) {
18
+ return await runtimeHooks.withMiniProgram({
19
+ preferOpenedSession: input.preferOpenedSession,
20
+ projectPath: createResolvedProjectPath(workspaceRoot, input.projectPath),
21
+ sharedSession: true,
22
+ timeout: input.timeout
23
+ }, runner);
24
+ }
25
+ async function withConnectedPage(runtimeHooks, workspaceRoot, input, runner) {
26
+ return await withConnectedMiniProgram(runtimeHooks, workspaceRoot, input, async (miniProgram) => {
27
+ return await runner(await miniProgram.currentPage(), miniProgram);
28
+ });
29
+ }
30
+ function defineConnectionSchema() {
31
+ return {
32
+ projectPath: z.string().trim().min(1).describe("小程序项目路径,支持 workspaceRoot 相对路径"),
33
+ timeout: z.number().int().positive().optional(),
34
+ preferOpenedSession: z.boolean().optional()
35
+ };
36
+ }
37
+ function defineElementSchema() {
38
+ return {
39
+ ...defineConnectionSchema(),
40
+ selector: z.string().trim().min(1)
41
+ };
42
+ }
43
+ function definePageSchema() {
44
+ return {
45
+ ...defineConnectionSchema(),
46
+ withData: z.boolean().optional()
47
+ };
48
+ }
49
+ function registerWeappIdeMcpTools(server, options) {
50
+ const workspaceRoot = options.workspaceRoot;
51
+ server.registerTool("weapp_devtools_connect", {
52
+ title: "Connect DevTools",
53
+ description: "连接当前项目的微信开发者工具并返回页面信息。",
54
+ inputSchema: defineConnectionSchema()
55
+ }, async (input) => {
56
+ try {
57
+ const result = await withConnectedMiniProgram(options.runtimeHooks, workspaceRoot, input, async (miniProgram) => {
58
+ const page = await miniProgram.currentPage().catch(() => null);
59
+ const systemInfo = await miniProgram.systemInfo().catch(() => null);
60
+ return {
61
+ connected: true,
62
+ currentPage: page ? {
63
+ path: page.path,
64
+ query: toSerializableValue(page.query)
65
+ } : null,
66
+ projectPath: input.projectPath,
67
+ systemInfo: toSerializableValue(systemInfo)
68
+ };
69
+ });
70
+ return {
71
+ content: [{
72
+ type: "text",
73
+ text: JSON.stringify(result, null, 2)
74
+ }],
75
+ structuredContent: { result }
76
+ };
77
+ } catch (error) {
78
+ return {
79
+ isError: true,
80
+ content: [{
81
+ type: "text",
82
+ text: error instanceof Error ? error.message : String(error)
83
+ }]
84
+ };
85
+ }
86
+ });
87
+ server.registerTool("weapp_devtools_active_page", {
88
+ title: "Current Page",
89
+ description: "读取当前页面路径、query、尺寸、滚动位置和页面 data。",
90
+ inputSchema: definePageSchema()
91
+ }, async (input) => {
92
+ try {
93
+ const result = await withConnectedPage(options.runtimeHooks, workspaceRoot, input, async (page) => {
94
+ const [size, scrollTop, data] = await Promise.all([
95
+ page.size().catch(() => null),
96
+ page.scrollTop().catch(() => null),
97
+ input.withData ? page.data().catch(() => null) : Promise.resolve(void 0)
98
+ ]);
99
+ return {
100
+ data: toSerializableValue(data),
101
+ path: page.path,
102
+ query: toSerializableValue(page.query),
103
+ scrollTop: toSerializableValue(scrollTop),
104
+ size: toSerializableValue(size)
105
+ };
106
+ });
107
+ return {
108
+ content: [{
109
+ type: "text",
110
+ text: JSON.stringify(result, null, 2)
111
+ }],
112
+ structuredContent: { result }
113
+ };
114
+ } catch (error) {
115
+ return {
116
+ isError: true,
117
+ content: [{
118
+ type: "text",
119
+ text: error instanceof Error ? error.message : String(error)
120
+ }]
121
+ };
122
+ }
123
+ });
124
+ server.registerTool("weapp_devtools_page_stack", {
125
+ title: "Page Stack",
126
+ description: "读取当前小程序页面栈。",
127
+ inputSchema: defineConnectionSchema()
128
+ }, async (input) => {
129
+ try {
130
+ const result = await withConnectedMiniProgram(options.runtimeHooks, workspaceRoot, input, async (miniProgram) => {
131
+ return (await miniProgram.pageStack()).map((page) => ({
132
+ path: page.path,
133
+ query: toSerializableValue(page.query)
134
+ }));
135
+ });
136
+ return {
137
+ content: [{
138
+ type: "text",
139
+ text: JSON.stringify(result, null, 2)
140
+ }],
141
+ structuredContent: { result }
142
+ };
143
+ } catch (error) {
144
+ return {
145
+ isError: true,
146
+ content: [{
147
+ type: "text",
148
+ text: error instanceof Error ? error.message : String(error)
149
+ }]
150
+ };
151
+ }
152
+ });
153
+ server.registerTool("weapp_runtime_find_node", {
154
+ title: "Find Node",
155
+ description: "通过 selector 查找单个节点,并返回节点快照。",
156
+ inputSchema: {
157
+ ...defineElementSchema(),
158
+ attributes: z.array(z.string().trim().min(1)).optional(),
159
+ styles: z.array(z.string().trim().min(1)).optional()
160
+ }
161
+ }, async (input) => {
162
+ try {
163
+ const result = await withConnectedPage(options.runtimeHooks, workspaceRoot, input, async (page) => {
164
+ const element = await page.$(input.selector);
165
+ if (!element) throw new Error(`Element not found: ${input.selector}`);
166
+ return await readElementSnapshot(element, input.selector, input.attributes ?? [], input.styles ?? []);
167
+ });
168
+ return {
169
+ content: [{
170
+ type: "text",
171
+ text: JSON.stringify(result, null, 2)
172
+ }],
173
+ structuredContent: { result }
174
+ };
175
+ } catch (error) {
176
+ return {
177
+ isError: true,
178
+ content: [{
179
+ type: "text",
180
+ text: error instanceof Error ? error.message : String(error)
181
+ }]
182
+ };
183
+ }
184
+ });
185
+ server.registerTool("weapp_runtime_find_nodes", {
186
+ title: "Find Nodes",
187
+ description: "通过 selector 查找多个节点,并返回节点快照列表。",
188
+ inputSchema: {
189
+ ...defineElementSchema(),
190
+ limit: z.number().int().positive().max(100).optional()
191
+ }
192
+ }, async (input) => {
193
+ try {
194
+ const result = await withConnectedPage(options.runtimeHooks, workspaceRoot, input, async (page) => {
195
+ const elements = await page.$$(input.selector);
196
+ const limited = elements.slice(0, input.limit ?? elements.length);
197
+ return await Promise.all(limited.map(async (element) => await readElementSnapshot(element, input.selector)));
198
+ });
199
+ return {
200
+ content: [{
201
+ type: "text",
202
+ text: JSON.stringify(result, null, 2)
203
+ }],
204
+ structuredContent: { result }
205
+ };
206
+ } catch (error) {
207
+ return {
208
+ isError: true,
209
+ content: [{
210
+ type: "text",
211
+ text: error instanceof Error ? error.message : String(error)
212
+ }]
213
+ };
214
+ }
215
+ });
216
+ server.registerTool("weapp_runtime_tap_node", {
217
+ title: "Tap Node",
218
+ description: "点击指定节点。",
219
+ inputSchema: defineElementSchema()
220
+ }, async (input) => {
221
+ try {
222
+ const result = await withConnectedPage(options.runtimeHooks, workspaceRoot, input, async (page) => {
223
+ const element = await page.$(input.selector);
224
+ if (!element) throw new Error(`Element not found: ${input.selector}`);
225
+ await element.tap();
226
+ return {
227
+ tapped: true,
228
+ selector: input.selector
229
+ };
230
+ });
231
+ return {
232
+ content: [{
233
+ type: "text",
234
+ text: JSON.stringify(result, null, 2)
235
+ }],
236
+ structuredContent: { result }
237
+ };
238
+ } catch (error) {
239
+ return {
240
+ isError: true,
241
+ content: [{
242
+ type: "text",
243
+ text: error instanceof Error ? error.message : String(error)
244
+ }]
245
+ };
246
+ }
247
+ });
248
+ server.registerTool("weapp_runtime_input_node", {
249
+ title: "Input Node",
250
+ description: "向指定节点输入文本。",
251
+ inputSchema: {
252
+ ...defineElementSchema(),
253
+ value: z.string()
254
+ }
255
+ }, async (input) => {
256
+ try {
257
+ const result = await withConnectedPage(options.runtimeHooks, workspaceRoot, input, async (page) => {
258
+ const element = await page.$(input.selector);
259
+ if (!element) throw new Error(`Element not found: ${input.selector}`);
260
+ const candidate = element;
261
+ if (typeof candidate.input !== "function") throw new TypeError(`Element does not support input: ${input.selector}`);
262
+ await candidate.input(input.value);
263
+ return {
264
+ selector: input.selector,
265
+ value: input.value,
266
+ updated: true
267
+ };
268
+ });
269
+ return {
270
+ content: [{
271
+ type: "text",
272
+ text: JSON.stringify(result, null, 2)
273
+ }],
274
+ structuredContent: { result }
275
+ };
276
+ } catch (error) {
277
+ return {
278
+ isError: true,
279
+ content: [{
280
+ type: "text",
281
+ text: error instanceof Error ? error.message : String(error)
282
+ }]
283
+ };
284
+ }
285
+ });
286
+ server.registerTool("weapp_devtools_capture", {
287
+ title: "Capture Screenshot",
288
+ description: "截取当前小程序视口并返回 base64,支持保存到文件。",
289
+ inputSchema: {
290
+ ...defineConnectionSchema(),
291
+ outputPath: z.string().trim().min(1).optional()
292
+ }
293
+ }, async (input) => {
294
+ try {
295
+ const result = await withConnectedMiniProgram(options.runtimeHooks, workspaceRoot, input, async (miniProgram) => {
296
+ const screenshot = await miniProgram.screenshot();
297
+ const base64 = typeof screenshot === "string" ? screenshot : Buffer.from(screenshot).toString("base64");
298
+ const buffer = Buffer.from(base64, "base64");
299
+ if (input.outputPath) {
300
+ const resolvedOutputPath = createResolvedOutputPath(workspaceRoot, input.outputPath);
301
+ await fs.mkdir(path.dirname(resolvedOutputPath), { recursive: true });
302
+ await fs.writeFile(resolvedOutputPath, buffer);
303
+ return {
304
+ bytes: buffer.byteLength,
305
+ path: resolvedOutputPath
306
+ };
307
+ }
308
+ return {
309
+ base64,
310
+ bytes: buffer.byteLength
311
+ };
312
+ });
313
+ return {
314
+ content: [{
315
+ type: "text",
316
+ text: JSON.stringify(result, null, 2)
317
+ }],
318
+ structuredContent: { result }
319
+ };
320
+ } catch (error) {
321
+ return {
322
+ isError: true,
323
+ content: [{
324
+ type: "text",
325
+ text: error instanceof Error ? error.message : String(error)
326
+ }]
327
+ };
328
+ }
329
+ });
330
+ server.registerTool("weapp_devtools_host_api", {
331
+ title: "Call wx Method",
332
+ description: "调用小程序 wx API。",
333
+ inputSchema: {
334
+ ...defineConnectionSchema(),
335
+ method: z.string().trim().min(1),
336
+ args: z.array(z.unknown()).optional()
337
+ }
338
+ }, async (input) => {
339
+ try {
340
+ const result = await withConnectedMiniProgram(options.runtimeHooks, workspaceRoot, input, async (miniProgram) => {
341
+ return {
342
+ args: toSerializableValue(input.args ?? []),
343
+ method: input.method,
344
+ result: toSerializableValue(await miniProgram.callWxMethod(input.method, ...input.args ?? []))
345
+ };
346
+ });
347
+ return {
348
+ content: [{
349
+ type: "text",
350
+ text: JSON.stringify(result, null, 2)
351
+ }],
352
+ structuredContent: { result }
353
+ };
354
+ } catch (error) {
355
+ return {
356
+ isError: true,
357
+ content: [{
358
+ type: "text",
359
+ text: error instanceof Error ? error.message : String(error)
360
+ }]
361
+ };
362
+ }
363
+ });
364
+ }
365
+ async function createWeappIdeMcpServer(options) {
366
+ const server = new McpServer({
367
+ name: "weapp-ide-cli",
368
+ version: "1.0.0"
369
+ });
370
+ registerWeappIdeMcpTools(server, options);
371
+ return {
372
+ close: async () => void 0,
373
+ server
374
+ };
375
+ }
376
+ //#endregion
377
+ //#region src/mcp/runtime.ts
378
+ async function startWeappIdeMcpServer(options = {}) {
379
+ const { server } = await createWeappIdeMcpServer({
380
+ runtimeHooks: { withMiniProgram: withMiniProgram$1 },
381
+ workspaceRoot: options.workspaceRoot
382
+ });
383
+ const transport = new StdioServerTransport();
384
+ await server.connect(transport);
385
+ return { close: async () => {
386
+ await transport.close();
387
+ } };
388
+ }
389
+ //#endregion
390
+ //#region src/cli/run-mcp.ts
391
+ function readOptionValue(argv, optionName) {
392
+ const optionWithEqual = `${optionName}=`;
393
+ for (let index = 0; index < argv.length; index += 1) {
394
+ const token = argv[index];
395
+ if (!token) continue;
396
+ if (token === optionName) {
397
+ const value = argv[index + 1];
398
+ return typeof value === "string" ? value : void 0;
399
+ }
400
+ if (token.startsWith(optionWithEqual)) return token.slice(optionWithEqual.length);
401
+ }
402
+ }
403
+ function shouldPrintHelp(argv) {
404
+ return argv.includes("-h") || argv.includes("--help");
405
+ }
406
+ function printMcpHelp() {
407
+ process.stdout.write(`Usage: weapp mcp [options]
408
+
409
+ Start the weapp-ide-cli MCP server over stdio.
410
+
411
+ Options:
412
+ --workspace-root <path> Resolve relative project/output paths from this root
413
+ -h, --help Show this help
414
+
415
+ AI client stdio config:
416
+ {
417
+ "mcpServers": {
418
+ "weapp-ide-cli": {
419
+ "command": "weapp",
420
+ "args": ["mcp", "--workspace-root", "<repo-root>"]
421
+ }
422
+ }
423
+ }
424
+ `);
425
+ }
426
+ async function runMcpCommand(argv) {
427
+ if (shouldPrintHelp(argv)) {
428
+ printMcpHelp();
429
+ return;
430
+ }
431
+ await startWeappIdeMcpServer({ workspaceRoot: readOptionValue(argv, "--workspace-root") });
432
+ }
433
+ //#endregion
434
+ export { readElementSnapshot as a, registerWeappIdeMcpTools as i, startWeappIdeMcpServer as n, resolveProjectPath as o, createWeappIdeMcpServer as r, toSerializableValue as s, runMcpCommand as t };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "weapp-ide-cli",
3
3
  "type": "module",
4
- "version": "5.2.11",
4
+ "version": "5.3.1",
5
5
  "description": "让微信开发者工具,用起来更加方便!",
6
6
  "author": "ice breaker <1324318532@qq.com>",
7
7
  "license": "MIT",
@@ -62,15 +62,17 @@
62
62
  "registry": "https://registry.npmjs.org/"
63
63
  },
64
64
  "dependencies": {
65
+ "@modelcontextprotocol/sdk": "^1.29.0",
65
66
  "cac": "^7.0.0",
66
67
  "execa": "9.6.1",
67
68
  "pathe": "^2.0.3",
68
69
  "pixelmatch": "^7.2.0",
69
70
  "pngjs": "^7.0.0",
71
+ "zod": "^4.4.3",
70
72
  "@weapp-core/logger": "3.1.1",
71
73
  "@weapp-core/shared": "3.0.4",
72
- "@weapp-vite/devtools-runtime": "0.2.3",
73
- "@weapp-vite/miniprogram-automator": "1.1.1"
74
+ "@weapp-vite/devtools-runtime": "0.3.1",
75
+ "@weapp-vite/miniprogram-automator": "1.1.2"
74
76
  },
75
77
  "scripts": {
76
78
  "dev": "tsdown -w --sourcemap",
@@ -1,2 +0,0 @@
1
- import { h as takeScreenshot } from "./commands-5QkUqOoO.js";
2
- export { takeScreenshot };