weixin-devtools-mcp 0.0.1 → 0.1.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.
- package/LICENSE +21 -0
- package/README.md +127 -128
- package/build/MiniProgramContext.d.ts +317 -0
- package/build/MiniProgramContext.d.ts.map +1 -0
- package/build/MiniProgramContext.js +696 -0
- package/build/MiniProgramContext.js.map +1 -0
- package/build/collectors/Collector.d.ts +127 -0
- package/build/collectors/Collector.d.ts.map +1 -0
- package/build/collectors/Collector.js +252 -0
- package/build/collectors/Collector.js.map +1 -0
- package/build/collectors/ConsoleCollector.d.ts +104 -0
- package/build/collectors/ConsoleCollector.d.ts.map +1 -0
- package/build/collectors/ConsoleCollector.js +157 -0
- package/build/collectors/ConsoleCollector.js.map +1 -0
- package/build/collectors/NetworkCollector.d.ts +167 -0
- package/build/collectors/NetworkCollector.d.ts.map +1 -0
- package/build/collectors/NetworkCollector.js +265 -0
- package/build/collectors/NetworkCollector.js.map +1 -0
- package/build/collectors/index.d.ts +13 -0
- package/build/collectors/index.d.ts.map +1 -0
- package/build/collectors/index.js +17 -0
- package/build/collectors/index.js.map +1 -0
- package/build/config/tool-profile.d.ts +30 -0
- package/build/config/tool-profile.d.ts.map +1 -0
- package/build/config/tool-profile.js +138 -0
- package/build/config/tool-profile.js.map +1 -0
- package/build/connection/adapters.d.ts +3 -0
- package/build/connection/adapters.d.ts.map +1 -0
- package/build/connection/adapters.js +134 -0
- package/build/connection/adapters.js.map +1 -0
- package/build/connection/errors.d.ts +34 -0
- package/build/connection/errors.d.ts.map +1 -0
- package/build/connection/errors.js +101 -0
- package/build/connection/errors.js.map +1 -0
- package/build/connection/health-probe.d.ts +4 -0
- package/build/connection/health-probe.d.ts.map +1 -0
- package/build/connection/health-probe.js +60 -0
- package/build/connection/health-probe.js.map +1 -0
- package/build/connection/index.d.ts +6 -0
- package/build/connection/index.d.ts.map +1 -0
- package/build/connection/index.js +6 -0
- package/build/connection/index.js.map +1 -0
- package/build/connection/manager.d.ts +19 -0
- package/build/connection/manager.d.ts.map +1 -0
- package/build/connection/manager.js +227 -0
- package/build/connection/manager.js.map +1 -0
- package/build/connection/resolver.d.ts +3 -0
- package/build/connection/resolver.d.ts.map +1 -0
- package/build/connection/resolver.js +99 -0
- package/build/connection/resolver.js.map +1 -0
- package/build/connection/types.d.ts +95 -0
- package/build/connection/types.d.ts.map +1 -0
- package/build/connection/types.js +16 -0
- package/build/connection/types.js.map +1 -0
- package/build/core/assertion.d.ts +22 -0
- package/build/core/assertion.d.ts.map +1 -0
- package/build/core/assertion.js +318 -0
- package/build/core/assertion.js.map +1 -0
- package/build/core/connection.d.ts +37 -0
- package/build/core/connection.d.ts.map +1 -0
- package/build/core/connection.js +745 -0
- package/build/core/connection.js.map +1 -0
- package/build/core/index.d.ts +13 -0
- package/build/core/index.d.ts.map +1 -0
- package/build/core/index.js +19 -0
- package/build/core/index.js.map +1 -0
- package/build/core/interaction.d.ts +22 -0
- package/build/core/interaction.d.ts.map +1 -0
- package/build/core/interaction.js +185 -0
- package/build/core/interaction.js.map +1 -0
- package/build/core/navigation.d.ts +26 -0
- package/build/core/navigation.d.ts.map +1 -0
- package/build/core/navigation.js +210 -0
- package/build/core/navigation.js.map +1 -0
- package/build/core/query.d.ts +14 -0
- package/build/core/query.d.ts.map +1 -0
- package/build/core/query.js +191 -0
- package/build/core/query.js.map +1 -0
- package/build/core/screenshot.d.ts +10 -0
- package/build/core/screenshot.d.ts.map +1 -0
- package/build/core/screenshot.js +93 -0
- package/build/core/screenshot.js.map +1 -0
- package/build/core/snapshot.d.ts +17 -0
- package/build/core/snapshot.d.ts.map +1 -0
- package/build/core/snapshot.js +225 -0
- package/build/core/snapshot.js.map +1 -0
- package/build/core/types.d.ts +250 -0
- package/build/core/types.d.ts.map +1 -0
- package/build/core/types.js +6 -0
- package/build/core/types.js.map +1 -0
- package/build/formatters/consoleFormatter.d.ts +50 -0
- package/build/formatters/consoleFormatter.d.ts.map +1 -0
- package/build/formatters/consoleFormatter.js +116 -0
- package/build/formatters/consoleFormatter.js.map +1 -0
- package/build/formatters/snapshotFormatter.d.ts +41 -0
- package/build/formatters/snapshotFormatter.d.ts.map +1 -0
- package/build/formatters/snapshotFormatter.js +156 -0
- package/build/formatters/snapshotFormatter.js.map +1 -0
- package/build/index.d.ts +11 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +45 -9
- package/build/index.js.map +1 -0
- package/build/server.d.ts +7 -0
- package/build/server.d.ts.map +1 -0
- package/build/server.js +88 -32
- package/build/server.js.map +1 -0
- package/build/tools/ToolDefinition.d.ts +265 -0
- package/build/tools/ToolDefinition.d.ts.map +1 -0
- package/build/tools/ToolDefinition.js +16 -7
- package/build/tools/ToolDefinition.js.map +1 -0
- package/build/tools/assert.d.ts +17 -0
- package/build/tools/assert.d.ts.map +1 -0
- package/build/tools/assert.js +63 -103
- package/build/tools/assert.js.map +1 -0
- package/build/tools/connection.d.ts +13 -0
- package/build/tools/connection.d.ts.map +1 -0
- package/build/tools/connection.js +338 -611
- package/build/tools/connection.js.map +1 -0
- package/build/tools/console.d.ts +20 -0
- package/build/tools/console.d.ts.map +1 -0
- package/build/tools/console.js +162 -152
- package/build/tools/console.js.map +1 -0
- package/build/tools/diagnose.d.ts +22 -0
- package/build/tools/diagnose.d.ts.map +1 -0
- package/build/tools/diagnose.js +406 -13
- package/build/tools/diagnose.js.map +1 -0
- package/build/tools/index.d.ts +6 -0
- package/build/tools/index.d.ts.map +1 -0
- package/build/tools/index.js +3 -77
- package/build/tools/index.js.map +1 -0
- package/build/tools/input.d.ts +21 -0
- package/build/tools/input.d.ts.map +1 -0
- package/build/tools/input.js +73 -139
- package/build/tools/input.js.map +1 -0
- package/build/tools/navigate.d.ts +21 -0
- package/build/tools/navigate.d.ts.map +1 -0
- package/build/tools/navigate.js +63 -126
- package/build/tools/navigate.js.map +1 -0
- package/build/tools/network.d.ts +21 -0
- package/build/tools/network.d.ts.map +1 -0
- package/build/tools/network.js +214 -1044
- package/build/tools/network.js.map +1 -0
- package/build/tools/page.d.ts +13 -0
- package/build/tools/page.d.ts.map +1 -0
- package/build/tools/page.js +6 -3
- package/build/tools/page.js.map +1 -0
- package/build/tools/screenshot.d.ts +9 -0
- package/build/tools/screenshot.d.ts.map +1 -0
- package/build/tools/screenshot.js +3 -1
- package/build/tools/screenshot.js.map +1 -0
- package/build/tools/script.d.ts +6 -0
- package/build/tools/script.d.ts.map +1 -0
- package/build/tools/script.js +92 -0
- package/build/tools/script.js.map +1 -0
- package/build/tools/snapshot.d.ts +9 -0
- package/build/tools/snapshot.d.ts.map +1 -0
- package/build/tools/snapshot.js +78 -12
- package/build/tools/snapshot.js.map +1 -0
- package/build/tools/tools.d.ts +15 -0
- package/build/tools/tools.d.ts.map +1 -0
- package/build/tools/tools.js +63 -0
- package/build/tools/tools.js.map +1 -0
- package/build/tools.d.ts +431 -0
- package/build/tools.d.ts.map +1 -0
- package/build/tools.js +258 -118
- package/build/tools.js.map +1 -0
- package/build/types/errors.d.ts +189 -0
- package/build/types/errors.d.ts.map +1 -0
- package/build/types/errors.js +257 -0
- package/build/types/errors.js.map +1 -0
- package/build/utils/error.d.ts +6 -0
- package/build/utils/error.d.ts.map +1 -0
- package/build/utils/error.js +11 -0
- package/build/utils/error.js.map +1 -0
- package/build/utils/idGenerator.d.ts +21 -0
- package/build/utils/idGenerator.d.ts.map +1 -0
- package/build/utils/idGenerator.js +23 -0
- package/build/utils/idGenerator.js.map +1 -0
- package/build/version.d.ts +7 -0
- package/build/version.d.ts.map +1 -0
- package/build/version.js +10 -0
- package/build/version.js.map +1 -0
- package/package.json +31 -9
package/build/tools.js
CHANGED
|
@@ -2,21 +2,40 @@
|
|
|
2
2
|
* 微信开发者工具 MCP 工具函数
|
|
3
3
|
* 提供可测试的纯函数实现
|
|
4
4
|
*/
|
|
5
|
-
import automator from "miniprogram-automator";
|
|
6
|
-
import path from "path";
|
|
7
|
-
import fs from "fs";
|
|
8
5
|
import { spawn } from "child_process";
|
|
6
|
+
import fs from "fs";
|
|
7
|
+
import path from "path";
|
|
9
8
|
import { promisify } from "util";
|
|
9
|
+
import automator from "miniprogram-automator";
|
|
10
10
|
const sleep = promisify(setTimeout);
|
|
11
11
|
/**
|
|
12
12
|
* 开发者工具连接错误类
|
|
13
13
|
*/
|
|
14
|
-
|
|
14
|
+
import { DevToolsError, ErrorCode, ErrorCategory } from './types/errors.js';
|
|
15
|
+
/**
|
|
16
|
+
* @deprecated 使用 DevToolsError 替代
|
|
17
|
+
* 保留此类以保持向后兼容性
|
|
18
|
+
*/
|
|
19
|
+
export class DevToolsConnectionError extends DevToolsError {
|
|
15
20
|
phase;
|
|
16
21
|
originalError;
|
|
17
22
|
details;
|
|
18
23
|
constructor(message, phase, originalError, details) {
|
|
19
|
-
|
|
24
|
+
// 根据阶段选择错误代码
|
|
25
|
+
const code = phase === 'startup'
|
|
26
|
+
? ErrorCode.CONNECTION_FAILED
|
|
27
|
+
: phase === 'connection'
|
|
28
|
+
? ErrorCode.CONNECTION_FAILED
|
|
29
|
+
: ErrorCode.CONNECTION_TIMEOUT;
|
|
30
|
+
const context = {
|
|
31
|
+
operation: phase,
|
|
32
|
+
details,
|
|
33
|
+
cause: originalError,
|
|
34
|
+
};
|
|
35
|
+
super(message, code, {
|
|
36
|
+
category: ErrorCategory.CONNECTION,
|
|
37
|
+
context,
|
|
38
|
+
});
|
|
20
39
|
this.phase = phase;
|
|
21
40
|
this.originalError = originalError;
|
|
22
41
|
this.details = details;
|
|
@@ -31,7 +50,7 @@ export class DevToolsConnectionError extends Error {
|
|
|
31
50
|
* @throws 连接失败时抛出错误
|
|
32
51
|
*/
|
|
33
52
|
export async function connectDevtools(options) {
|
|
34
|
-
const { projectPath, cliPath, port } = options;
|
|
53
|
+
const { projectPath, cliPath, port, autoAudits } = options;
|
|
35
54
|
if (!projectPath) {
|
|
36
55
|
throw new Error("项目路径是必需的");
|
|
37
56
|
}
|
|
@@ -57,6 +76,15 @@ export async function connectDevtools(options) {
|
|
|
57
76
|
launchOptions.cliPath = cliPath;
|
|
58
77
|
if (port)
|
|
59
78
|
launchOptions.port = port;
|
|
79
|
+
if (typeof autoAudits === 'boolean') {
|
|
80
|
+
launchOptions.projectConfig = {
|
|
81
|
+
...(launchOptions.projectConfig || {}),
|
|
82
|
+
setting: {
|
|
83
|
+
...(launchOptions.projectConfig?.setting || {}),
|
|
84
|
+
autoAudits
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
60
88
|
// 启动并连接微信开发者工具
|
|
61
89
|
const miniProgram = await automator.launch(launchOptions);
|
|
62
90
|
// 获取当前页面
|
|
@@ -218,12 +246,12 @@ export async function connectDevtools(options) {
|
|
|
218
246
|
hasInterceptors: !!(app && app.$xfetch && app.$xfetch.interceptors),
|
|
219
247
|
hasMpxFetch: hasMpxFetch
|
|
220
248
|
};
|
|
221
|
-
console.
|
|
249
|
+
console.error('[MCP-DEBUG] Mpx检测:', debugInfo);
|
|
222
250
|
// 强制安装 Mpx 拦截器(不检查标志,每次都重新安装以覆盖旧的)
|
|
223
251
|
// 这样可以解决小程序未重新加载导致标志残留的问题
|
|
224
252
|
// @ts-ignore
|
|
225
253
|
if (hasMpxFetch) {
|
|
226
|
-
console.
|
|
254
|
+
console.error('[MCP] 正在安装 Mpx $xfetch 拦截器(强制覆盖)...');
|
|
227
255
|
// 安装 Mpx 请求拦截器
|
|
228
256
|
// @ts-ignore
|
|
229
257
|
app.$xfetch.interceptors.request.use(function (config) {
|
|
@@ -282,12 +310,12 @@ export async function connectDevtools(options) {
|
|
|
282
310
|
});
|
|
283
311
|
throw error;
|
|
284
312
|
});
|
|
285
|
-
console.
|
|
313
|
+
console.error('[MCP] Mpx $xfetch 拦截器安装完成');
|
|
286
314
|
}
|
|
287
315
|
// @ts-ignore
|
|
288
316
|
wx.__networkInterceptorsInstalled = true;
|
|
289
317
|
});
|
|
290
|
-
console.
|
|
318
|
+
console.error('[connectDevtools] 网络监听已自动启动(包含 Mpx 框架支持)');
|
|
291
319
|
}
|
|
292
320
|
catch (err) {
|
|
293
321
|
console.warn('[connectDevtools] 网络监听启动失败:', err);
|
|
@@ -311,7 +339,7 @@ export async function connectDevtools(options) {
|
|
|
311
339
|
* @returns 详细连接结果
|
|
312
340
|
*/
|
|
313
341
|
export async function connectDevtoolsEnhanced(options) {
|
|
314
|
-
const { mode = 'auto',
|
|
342
|
+
const { mode = 'auto', verbose = false } = options;
|
|
315
343
|
const startTime = Date.now();
|
|
316
344
|
// 验证项目路径(在所有模式执行前统一验证)
|
|
317
345
|
if (!options.projectPath) {
|
|
@@ -330,8 +358,8 @@ export async function connectDevtoolsEnhanced(options) {
|
|
|
330
358
|
throw new Error(`Project path '${resolvedProjectPath}' doesn't exist`);
|
|
331
359
|
}
|
|
332
360
|
if (verbose) {
|
|
333
|
-
console.
|
|
334
|
-
console.
|
|
361
|
+
console.error(`开始连接微信开发者工具,模式: ${mode}`);
|
|
362
|
+
console.error(`项目路径: ${resolvedProjectPath}`);
|
|
335
363
|
}
|
|
336
364
|
try {
|
|
337
365
|
switch (mode) {
|
|
@@ -377,7 +405,7 @@ function isSessionConflictError(error) {
|
|
|
377
405
|
*/
|
|
378
406
|
async function intelligentConnect(options, startTime) {
|
|
379
407
|
if (options.verbose) {
|
|
380
|
-
console.
|
|
408
|
+
console.error('🎯 智能连接策略: 优先使用 launchMode(自动处理项目验证和会话复用)');
|
|
381
409
|
}
|
|
382
410
|
try {
|
|
383
411
|
// 默认使用 launchMode
|
|
@@ -389,12 +417,12 @@ async function intelligentConnect(options, startTime) {
|
|
|
389
417
|
}
|
|
390
418
|
catch (error) {
|
|
391
419
|
if (options.verbose) {
|
|
392
|
-
console.
|
|
420
|
+
console.error('⚠️ launchMode 失败,分析错误类型...');
|
|
393
421
|
}
|
|
394
422
|
// 仅在特定可恢复错误时回退到 connectMode
|
|
395
423
|
if (options.fallbackMode && isSessionConflictError(error)) {
|
|
396
424
|
if (options.verbose) {
|
|
397
|
-
console.
|
|
425
|
+
console.error('🔄 检测到会话冲突,尝试回退到 connectMode');
|
|
398
426
|
}
|
|
399
427
|
return await connectMode(options, startTime);
|
|
400
428
|
}
|
|
@@ -430,7 +458,7 @@ async function connectMode(options, startTime) {
|
|
|
430
458
|
error.phase === 'startup' &&
|
|
431
459
|
error.details?.reason === 'session_conflict') {
|
|
432
460
|
if (options.verbose) {
|
|
433
|
-
console.
|
|
461
|
+
console.error('🔄 检测到会话冲突,自动回退到传统连接模式(launch)...');
|
|
434
462
|
}
|
|
435
463
|
// 如果允许回退,自动使用launch模式
|
|
436
464
|
if (options.fallbackMode) {
|
|
@@ -448,7 +476,8 @@ async function launchMode(options, startTime) {
|
|
|
448
476
|
const connectOptions = {
|
|
449
477
|
projectPath: options.projectPath,
|
|
450
478
|
cliPath: options.cliPath,
|
|
451
|
-
port: options.autoPort || options.port
|
|
479
|
+
port: options.autoPort || options.port,
|
|
480
|
+
autoAudits: options.autoAudits
|
|
452
481
|
};
|
|
453
482
|
const result = await connectDevtools(connectOptions);
|
|
454
483
|
// 健康检查
|
|
@@ -470,7 +499,7 @@ async function startupPhase(options) {
|
|
|
470
499
|
const port = options.autoPort || 9420;
|
|
471
500
|
const cliCommand = buildCliCommand(options);
|
|
472
501
|
if (options.verbose) {
|
|
473
|
-
console.
|
|
502
|
+
console.error('执行CLI命令:', cliCommand.join(' '));
|
|
474
503
|
}
|
|
475
504
|
// 执行CLI命令
|
|
476
505
|
const process = await executeCliCommand(cliCommand);
|
|
@@ -490,7 +519,7 @@ async function startupPhase(options) {
|
|
|
490
519
|
async function connectionPhase(options, startupResult) {
|
|
491
520
|
const wsEndpoint = `ws://localhost:${startupResult.processInfo.port}`;
|
|
492
521
|
if (options.verbose) {
|
|
493
|
-
console.
|
|
522
|
+
console.error('连接WebSocket端点:', wsEndpoint);
|
|
494
523
|
}
|
|
495
524
|
// 连接到WebSocket端点
|
|
496
525
|
const miniProgram = await connectWithRetry(wsEndpoint, 3);
|
|
@@ -530,8 +559,19 @@ function buildCliCommand(options) {
|
|
|
530
559
|
}
|
|
531
560
|
/**
|
|
532
561
|
* 查找默认CLI路径
|
|
562
|
+
* 优先级:环境变量 > 默认路径
|
|
533
563
|
*/
|
|
534
564
|
function findDefaultCliPath() {
|
|
565
|
+
// 1. 优先使用环境变量
|
|
566
|
+
const envCliPath = process.env.WECHAT_DEVTOOLS_CLI;
|
|
567
|
+
if (envCliPath) {
|
|
568
|
+
if (envCliPath.startsWith('@playground/')) {
|
|
569
|
+
// @playground/ 格式需要转换为实际路径
|
|
570
|
+
const relativePath = envCliPath.replace('@playground/', 'playground/');
|
|
571
|
+
return path.resolve(process.cwd(), relativePath);
|
|
572
|
+
}
|
|
573
|
+
return path.resolve(process.cwd(), envCliPath);
|
|
574
|
+
}
|
|
535
575
|
const platform = process.platform;
|
|
536
576
|
if (platform === 'darwin') {
|
|
537
577
|
return '/Applications/wechatwebdevtools.app/Contents/MacOS/cli';
|
|
@@ -540,7 +580,18 @@ function findDefaultCliPath() {
|
|
|
540
580
|
return 'C:/Program Files (x86)/Tencent/微信web开发者工具/cli.bat';
|
|
541
581
|
}
|
|
542
582
|
else {
|
|
543
|
-
|
|
583
|
+
// Linux: 尝试常见的 Linux 版开发者工具路径
|
|
584
|
+
const linuxPaths = [
|
|
585
|
+
'/opt/apps/io.github.msojocs.wechat-devtools-linux/files/bin/bin/wechat-devtools-cli',
|
|
586
|
+
'/usr/share/wechat-devtools/bin/cli',
|
|
587
|
+
'/usr/local/bin/wechat-devtools-cli',
|
|
588
|
+
];
|
|
589
|
+
for (const p of linuxPaths) {
|
|
590
|
+
if (fs.existsSync(p)) {
|
|
591
|
+
return p;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
throw new Error(`不支持的平台: ${platform}。可在 Linux 上设置环境变量 WECHAT_DEVTOOLS_CLI 指定 CLI 路径`);
|
|
544
595
|
}
|
|
545
596
|
}
|
|
546
597
|
/**
|
|
@@ -572,14 +623,14 @@ async function executeCliCommand(command) {
|
|
|
572
623
|
process.stdout.on('data', (data) => {
|
|
573
624
|
const text = data.toString();
|
|
574
625
|
output += text;
|
|
575
|
-
console.
|
|
626
|
+
console.error('[CLI stdout]:', text.trim());
|
|
576
627
|
});
|
|
577
628
|
}
|
|
578
629
|
if (process.stderr) {
|
|
579
630
|
process.stderr.on('data', (data) => {
|
|
580
631
|
const text = data.toString();
|
|
581
632
|
errorOutput += text;
|
|
582
|
-
console.
|
|
633
|
+
console.error('[CLI stderr]:', text.trim());
|
|
583
634
|
// 检测端口冲突错误
|
|
584
635
|
if (text.includes('must be restarted on port')) {
|
|
585
636
|
const match = text.match(/started on .+:(\d+) and must be restarted on port (\d+)/);
|
|
@@ -619,7 +670,7 @@ async function executeCliCommand(command) {
|
|
|
619
670
|
// 检测 CLI 命令失败(通用)
|
|
620
671
|
if (text.includes('error') || text.includes('failed') || text.includes('失败')) {
|
|
621
672
|
if (!resolved && text.length > 10) { // 确保不是误报
|
|
622
|
-
console.
|
|
673
|
+
console.error('[CLI 警告] 检测到潜在错误:', text.trim());
|
|
623
674
|
}
|
|
624
675
|
}
|
|
625
676
|
});
|
|
@@ -661,22 +712,21 @@ async function executeCliCommand(command) {
|
|
|
661
712
|
export async function waitForWebSocketReady(port, timeout, verbose = false) {
|
|
662
713
|
const startTime = Date.now();
|
|
663
714
|
let attempt = 0;
|
|
664
|
-
const maxAttempts = Math.ceil(timeout / 1000); // 每秒检查一次
|
|
665
715
|
if (verbose) {
|
|
666
|
-
console.
|
|
716
|
+
console.error(`等待WebSocket服务启动,端口: ${port},超时: ${timeout}ms`);
|
|
667
717
|
}
|
|
668
718
|
while (Date.now() - startTime < timeout) {
|
|
669
719
|
attempt++;
|
|
670
720
|
if (verbose && attempt % 5 === 0) { // 每5秒显示一次进度
|
|
671
721
|
const elapsed = Date.now() - startTime;
|
|
672
|
-
console.
|
|
722
|
+
console.error(`WebSocket检测进度: ${Math.round(elapsed / 1000)}s / ${Math.round(timeout / 1000)}s`);
|
|
673
723
|
}
|
|
674
724
|
// 尝试多种检测方式
|
|
675
725
|
const isReady = await checkDevToolsRunning(port) || await checkWebSocketDirectly(port);
|
|
676
726
|
if (isReady) {
|
|
677
727
|
if (verbose) {
|
|
678
728
|
const elapsed = Date.now() - startTime;
|
|
679
|
-
console.
|
|
729
|
+
console.error(`WebSocket服务已启动,耗时: ${elapsed}ms`);
|
|
680
730
|
}
|
|
681
731
|
return;
|
|
682
732
|
}
|
|
@@ -737,16 +787,16 @@ export async function detectIDEPort(verbose = false) {
|
|
|
737
787
|
// 常用端口列表
|
|
738
788
|
const commonPorts = [9420, 9440, 9430, 9450, 9460];
|
|
739
789
|
if (verbose) {
|
|
740
|
-
console.
|
|
790
|
+
console.error('🔍 检测微信开发者工具运行端口...');
|
|
741
791
|
}
|
|
742
792
|
// 策略1: 尝试常用端口
|
|
743
793
|
for (const port of commonPorts) {
|
|
744
794
|
if (verbose) {
|
|
745
|
-
console.
|
|
795
|
+
console.error(` 检测端口 ${port}...`);
|
|
746
796
|
}
|
|
747
797
|
if (await checkDevToolsRunning(port)) {
|
|
748
798
|
if (verbose) {
|
|
749
|
-
console.
|
|
799
|
+
console.error(`✅ 检测到IDE运行在端口 ${port}`);
|
|
750
800
|
}
|
|
751
801
|
return port;
|
|
752
802
|
}
|
|
@@ -760,14 +810,14 @@ export async function detectIDEPort(verbose = false) {
|
|
|
760
810
|
if (output) {
|
|
761
811
|
const ports = output.split('\n').map((p) => parseInt(p, 10)).filter((p) => !isNaN(p));
|
|
762
812
|
if (verbose && ports.length > 0) {
|
|
763
|
-
console.
|
|
813
|
+
console.error(` lsof检测到端口: ${ports.join(', ')}`);
|
|
764
814
|
}
|
|
765
815
|
// 遍历检测到的端口,验证是否为有效的自动化端口
|
|
766
816
|
for (const port of ports) {
|
|
767
817
|
if (port >= 9400 && port <= 9500) {
|
|
768
818
|
if (await checkDevToolsRunning(port)) {
|
|
769
819
|
if (verbose) {
|
|
770
|
-
console.
|
|
820
|
+
console.error(`✅ 通过lsof检测到IDE运行在端口 ${port}`);
|
|
771
821
|
}
|
|
772
822
|
return port;
|
|
773
823
|
}
|
|
@@ -778,12 +828,12 @@ export async function detectIDEPort(verbose = false) {
|
|
|
778
828
|
catch (error) {
|
|
779
829
|
// lsof 失败,继续
|
|
780
830
|
if (verbose) {
|
|
781
|
-
console.
|
|
831
|
+
console.error(' lsof检测失败');
|
|
782
832
|
}
|
|
783
833
|
}
|
|
784
834
|
}
|
|
785
835
|
if (verbose) {
|
|
786
|
-
console.
|
|
836
|
+
console.error('❌ 未检测到IDE运行端口');
|
|
787
837
|
}
|
|
788
838
|
return null;
|
|
789
839
|
}
|
|
@@ -838,30 +888,69 @@ async function executeWithDetailedError(operation, phase) {
|
|
|
838
888
|
throw new DevToolsConnectionError(originalError.message, phase, originalError, { timestamp: new Date().toISOString() });
|
|
839
889
|
}
|
|
840
890
|
}
|
|
891
|
+
/**
|
|
892
|
+
* 生成简单的文本哈希(用于增强 UID 唯一性)
|
|
893
|
+
*/
|
|
894
|
+
function simpleTextHash(text) {
|
|
895
|
+
if (!text || text.length === 0)
|
|
896
|
+
return '';
|
|
897
|
+
// 取文本的前 8 个字符,过滤特殊字符
|
|
898
|
+
const sanitized = text.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, '').slice(0, 8);
|
|
899
|
+
if (sanitized.length === 0)
|
|
900
|
+
return '';
|
|
901
|
+
return `_${sanitized}`;
|
|
902
|
+
}
|
|
841
903
|
/**
|
|
842
904
|
* 生成元素的唯一标识符 (uid)
|
|
905
|
+
*
|
|
906
|
+
* 优先级顺序(稳定性从高到低):
|
|
907
|
+
* 1. data-testid(专门用于测试,最稳定)
|
|
908
|
+
* 2. id 属性
|
|
909
|
+
* 3. data-id(自定义数据属性)
|
|
910
|
+
* 4. class + 文本哈希(中等稳定性)
|
|
911
|
+
* 5. class:eq(index)
|
|
912
|
+
* 6. nth-child(兜底)
|
|
843
913
|
*/
|
|
844
914
|
export async function generateElementUid(element, index) {
|
|
845
915
|
try {
|
|
846
916
|
const tagName = element.tagName;
|
|
847
|
-
|
|
848
|
-
const id = await
|
|
849
|
-
|
|
917
|
+
// 并行获取所有可能的标识属性
|
|
918
|
+
const [className, id, testId, dataId, text] = await Promise.all([
|
|
919
|
+
element.attribute('class').catch(() => ''),
|
|
920
|
+
element.attribute('id').catch(() => ''),
|
|
921
|
+
element.attribute('data-testid').catch(() => ''),
|
|
922
|
+
element.attribute('data-id').catch(() => ''),
|
|
923
|
+
element.text().catch(() => '')
|
|
924
|
+
]);
|
|
925
|
+
console.error(`[generateElementUid] tagName=${tagName}, id="${id}", testId="${testId}", dataId="${dataId}", className="${className}", index=${index}`);
|
|
850
926
|
let selector = tagName;
|
|
851
|
-
|
|
927
|
+
// 优先级1: data-testid(最稳定)
|
|
928
|
+
if (testId) {
|
|
929
|
+
selector += `[data-testid="${testId}"]`;
|
|
930
|
+
}
|
|
931
|
+
// 优先级2: id 属性
|
|
932
|
+
else if (id) {
|
|
852
933
|
selector += `#${id}`;
|
|
853
934
|
}
|
|
935
|
+
// 优先级3: data-id
|
|
936
|
+
else if (dataId) {
|
|
937
|
+
selector += `[data-id="${dataId}"]`;
|
|
938
|
+
}
|
|
939
|
+
// 优先级4: class + 文本哈希
|
|
854
940
|
else if (className) {
|
|
855
|
-
|
|
941
|
+
const firstClass = className.split(' ')[0];
|
|
942
|
+
const textHash = simpleTextHash(text);
|
|
943
|
+
selector += `.${firstClass}${textHash}`;
|
|
856
944
|
}
|
|
945
|
+
// 优先级5: nth-child(兜底)
|
|
857
946
|
else {
|
|
858
947
|
selector += `:nth-child(${index + 1})`;
|
|
859
948
|
}
|
|
860
|
-
console.
|
|
949
|
+
console.error(`[generateElementUid] Generated UID: ${selector}`);
|
|
861
950
|
return selector;
|
|
862
951
|
}
|
|
863
952
|
catch (error) {
|
|
864
|
-
console.
|
|
953
|
+
console.error(`[generateElementUid] Error:`, error);
|
|
865
954
|
return `${element.tagName || 'unknown'}:nth-child(${index + 1})`;
|
|
866
955
|
}
|
|
867
956
|
}
|
|
@@ -882,16 +971,21 @@ export async function getPageSnapshot(page) {
|
|
|
882
971
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
883
972
|
// 尝试多种选择器策略获取元素
|
|
884
973
|
let childElements = [];
|
|
885
|
-
|
|
974
|
+
let usedStrategy = 'unknown';
|
|
975
|
+
// 策略1: 优先使用通配符(最快,一次API调用)
|
|
886
976
|
try {
|
|
887
977
|
childElements = await page.$$('*');
|
|
888
|
-
|
|
978
|
+
if (childElements.length > 0) {
|
|
979
|
+
usedStrategy = 'wildcard(*)';
|
|
980
|
+
console.error(`✅ 策略1成功: 通配符查询获取到 ${childElements.length} 个元素`);
|
|
981
|
+
}
|
|
889
982
|
}
|
|
890
983
|
catch (error) {
|
|
891
|
-
console.
|
|
984
|
+
console.warn('⚠️ 策略1失败 (*)', error);
|
|
892
985
|
}
|
|
893
|
-
// 策略2:
|
|
986
|
+
// 策略2: 降级到常用组件选择器(仅当策略1失败时)
|
|
894
987
|
if (childElements.length === 0) {
|
|
988
|
+
console.error('🔄 策略1无结果,降级到策略2(常用组件选择器)');
|
|
895
989
|
const commonSelectors = [
|
|
896
990
|
'view', 'text', 'button', 'image', 'input', 'textarea', 'picker', 'switch',
|
|
897
991
|
'slider', 'scroll-view', 'swiper', 'icon', 'rich-text', 'progress',
|
|
@@ -901,51 +995,108 @@ export async function getPageSnapshot(page) {
|
|
|
901
995
|
try {
|
|
902
996
|
const elements = await page.$$(selector);
|
|
903
997
|
childElements.push(...elements);
|
|
904
|
-
|
|
998
|
+
if (elements.length > 0) {
|
|
999
|
+
console.error(` - ${selector}: ${elements.length} 个元素`);
|
|
1000
|
+
}
|
|
905
1001
|
}
|
|
906
1002
|
catch (error) {
|
|
907
|
-
|
|
1003
|
+
// 忽略单个选择器失败
|
|
908
1004
|
}
|
|
909
1005
|
}
|
|
1006
|
+
if (childElements.length > 0) {
|
|
1007
|
+
usedStrategy = 'common-selectors';
|
|
1008
|
+
console.error(`✅ 策略2成功: 获取到 ${childElements.length} 个元素`);
|
|
1009
|
+
}
|
|
910
1010
|
}
|
|
911
|
-
// 策略3:
|
|
1011
|
+
// 策略3: 最后尝试层级选择器
|
|
912
1012
|
if (childElements.length === 0) {
|
|
1013
|
+
console.error('🔄 策略2无结果,降级到策略3(层级选择器)');
|
|
913
1014
|
try {
|
|
914
1015
|
const rootElements = await page.$$('page > *');
|
|
915
1016
|
childElements = rootElements;
|
|
916
|
-
|
|
1017
|
+
if (childElements.length > 0) {
|
|
1018
|
+
usedStrategy = 'hierarchical(page>*)';
|
|
1019
|
+
console.error(`✅ 策略3成功: 获取到 ${childElements.length} 个元素`);
|
|
1020
|
+
}
|
|
917
1021
|
}
|
|
918
1022
|
catch (error) {
|
|
919
|
-
console.
|
|
1023
|
+
console.warn('⚠️ 策略3失败 (page > *)', error);
|
|
920
1024
|
}
|
|
921
1025
|
}
|
|
922
|
-
|
|
1026
|
+
if (childElements.length === 0) {
|
|
1027
|
+
console.warn('❌ 所有策略均未获取到元素');
|
|
1028
|
+
return {
|
|
1029
|
+
snapshot: { path: await page.path, elements: [] },
|
|
1030
|
+
elementMap: new Map()
|
|
1031
|
+
};
|
|
1032
|
+
}
|
|
1033
|
+
console.error(`📊 最终获取到 ${childElements.length} 个元素(策略:${usedStrategy})`);
|
|
923
1034
|
// 用于跟踪每个基础选择器的元素计数
|
|
924
1035
|
const selectorIndexMap = new Map();
|
|
1036
|
+
// 优化:批量并行处理元素属性
|
|
1037
|
+
const startTime = Date.now();
|
|
925
1038
|
for (let i = 0; i < childElements.length; i++) {
|
|
926
1039
|
const element = childElements[i];
|
|
927
1040
|
try {
|
|
928
|
-
|
|
1041
|
+
// 🚀 优化点1: 使用 Promise.allSettled 并行获取所有元素属性
|
|
1042
|
+
// 减少API调用往返次数:从 6次串行 → 1次并行
|
|
1043
|
+
// 新增 data-testid 和 data-id 属性用于增强 UID 稳定性
|
|
1044
|
+
const [tagNameResult, textResult, classResult, idResult, testIdResult, dataIdResult, sizeResult, offsetResult] = await Promise.allSettled([
|
|
1045
|
+
Promise.resolve(element.tagName || 'unknown'),
|
|
1046
|
+
element.text().catch(() => ''),
|
|
1047
|
+
element.attribute('class').catch(() => ''),
|
|
1048
|
+
element.attribute('id').catch(() => ''),
|
|
1049
|
+
element.attribute('data-testid').catch(() => ''),
|
|
1050
|
+
element.attribute('data-id').catch(() => ''),
|
|
1051
|
+
element.size().catch(() => null),
|
|
1052
|
+
element.offset().catch(() => null)
|
|
1053
|
+
]);
|
|
1054
|
+
// 提取结果
|
|
1055
|
+
const tagName = tagNameResult.status === 'fulfilled' ? tagNameResult.value : 'unknown';
|
|
1056
|
+
const text = textResult.status === 'fulfilled' ? textResult.value : '';
|
|
1057
|
+
const className = classResult.status === 'fulfilled' ? classResult.value : '';
|
|
1058
|
+
const id = idResult.status === 'fulfilled' ? idResult.value : '';
|
|
1059
|
+
const testId = testIdResult.status === 'fulfilled' ? testIdResult.value : '';
|
|
1060
|
+
const dataId = dataIdResult.status === 'fulfilled' ? dataIdResult.value : '';
|
|
1061
|
+
const size = sizeResult.status === 'fulfilled' ? sizeResult.value : null;
|
|
1062
|
+
const offset = offsetResult.status === 'fulfilled' ? offsetResult.value : null;
|
|
1063
|
+
// 生成 UID(增强版优先级顺序)
|
|
1064
|
+
// 优先级:data-testid > id > data-id > class+文本哈希 > nth-child
|
|
1065
|
+
let selector = tagName;
|
|
1066
|
+
if (testId) {
|
|
1067
|
+
// 优先级1: data-testid(专门用于测试,最稳定)
|
|
1068
|
+
selector += `[data-testid="${testId}"]`;
|
|
1069
|
+
}
|
|
1070
|
+
else if (id) {
|
|
1071
|
+
// 优先级2: id 属性
|
|
1072
|
+
selector += `#${id}`;
|
|
1073
|
+
}
|
|
1074
|
+
else if (dataId) {
|
|
1075
|
+
// 优先级3: data-id
|
|
1076
|
+
selector += `[data-id="${dataId}"]`;
|
|
1077
|
+
}
|
|
1078
|
+
else if (className) {
|
|
1079
|
+
// 优先级4: class + 文本哈希(中等稳定性)
|
|
1080
|
+
const firstClass = className.split(' ')[0];
|
|
1081
|
+
const textHash = simpleTextHash(text);
|
|
1082
|
+
selector += `.${firstClass}${textHash}`;
|
|
1083
|
+
}
|
|
1084
|
+
else {
|
|
1085
|
+
// 优先级5: nth-child(兜底)
|
|
1086
|
+
selector += `:nth-child(${i + 1})`;
|
|
1087
|
+
}
|
|
1088
|
+
const uid = selector;
|
|
1089
|
+
// 构建快照
|
|
929
1090
|
const snapshot = {
|
|
930
1091
|
uid,
|
|
931
|
-
tagName
|
|
1092
|
+
tagName,
|
|
932
1093
|
};
|
|
933
|
-
//
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
if (text && text.trim()) {
|
|
937
|
-
snapshot.text = text.trim();
|
|
938
|
-
}
|
|
939
|
-
}
|
|
940
|
-
catch (error) {
|
|
941
|
-
// 忽略无法获取文本的元素
|
|
1094
|
+
// 添加文本内容
|
|
1095
|
+
if (text && text.trim()) {
|
|
1096
|
+
snapshot.text = text.trim();
|
|
942
1097
|
}
|
|
943
|
-
//
|
|
944
|
-
|
|
945
|
-
const [size, offset] = await Promise.all([
|
|
946
|
-
element.size(),
|
|
947
|
-
element.offset()
|
|
948
|
-
]);
|
|
1098
|
+
// 添加位置信息
|
|
1099
|
+
if (size && offset) {
|
|
949
1100
|
snapshot.position = {
|
|
950
1101
|
left: offset.left,
|
|
951
1102
|
top: offset.top,
|
|
@@ -953,56 +1104,38 @@ export async function getPageSnapshot(page) {
|
|
|
953
1104
|
height: size.height
|
|
954
1105
|
};
|
|
955
1106
|
}
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
}
|
|
959
|
-
// 获取常用属性
|
|
960
|
-
try {
|
|
961
|
-
const attributes = {};
|
|
962
|
-
const commonAttrs = ['class', 'id', 'data-*'];
|
|
963
|
-
for (const attr of commonAttrs) {
|
|
964
|
-
try {
|
|
965
|
-
const value = await element.attribute(attr);
|
|
966
|
-
if (value) {
|
|
967
|
-
attributes[attr] = value;
|
|
968
|
-
}
|
|
969
|
-
}
|
|
970
|
-
catch (error) {
|
|
971
|
-
// 忽略不存在的属性
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
if (Object.keys(attributes).length > 0) {
|
|
975
|
-
snapshot.attributes = attributes;
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
|
-
catch (error) {
|
|
979
|
-
// 忽略属性获取错误
|
|
980
|
-
}
|
|
1107
|
+
// 添加属性信息(可选,目前不收集)
|
|
1108
|
+
// 如果需要属性,可以在上面的 Promise.allSettled 中添加更多属性查询
|
|
981
1109
|
elements.push(snapshot);
|
|
982
|
-
//
|
|
983
|
-
const tagName = element.tagName;
|
|
984
|
-
const className = await element.attribute('class').catch(() => '');
|
|
985
|
-
const id = await element.attribute('id').catch(() => '');
|
|
1110
|
+
// 生成可查询的基础选择器(与 UID 优先级一致)
|
|
986
1111
|
let baseSelector = tagName;
|
|
987
|
-
if (
|
|
1112
|
+
if (testId) {
|
|
1113
|
+
baseSelector = `${tagName}[data-testid="${testId}"]`;
|
|
1114
|
+
}
|
|
1115
|
+
else if (id) {
|
|
988
1116
|
baseSelector = `${tagName}#${id}`;
|
|
989
1117
|
}
|
|
1118
|
+
else if (dataId) {
|
|
1119
|
+
baseSelector = `${tagName}[data-id="${dataId}"]`;
|
|
1120
|
+
}
|
|
990
1121
|
else if (className) {
|
|
991
1122
|
baseSelector = `${tagName}.${className.split(' ')[0]}`;
|
|
992
1123
|
}
|
|
993
1124
|
// 计算该选择器的元素索引(递增计数)
|
|
994
1125
|
const currentIndex = selectorIndexMap.get(baseSelector) || 0;
|
|
995
1126
|
selectorIndexMap.set(baseSelector, currentIndex + 1);
|
|
996
|
-
// 存储 ElementMapInfo
|
|
1127
|
+
// 存储 ElementMapInfo
|
|
997
1128
|
elementMap.set(uid, {
|
|
998
|
-
selector: baseSelector,
|
|
999
|
-
index: currentIndex
|
|
1129
|
+
selector: baseSelector,
|
|
1130
|
+
index: currentIndex
|
|
1000
1131
|
});
|
|
1001
1132
|
}
|
|
1002
1133
|
catch (error) {
|
|
1003
|
-
console.warn(
|
|
1134
|
+
console.warn(`⚠️ 处理元素 ${i} 时出错:`, error);
|
|
1004
1135
|
}
|
|
1005
1136
|
}
|
|
1137
|
+
const processingTime = Date.now() - startTime;
|
|
1138
|
+
console.error(`⏱️ 元素处理耗时: ${processingTime}ms (平均 ${(processingTime / childElements.length).toFixed(2)}ms/元素)`);
|
|
1006
1139
|
const pagePath = await page.path;
|
|
1007
1140
|
const snapshot = {
|
|
1008
1141
|
path: pagePath,
|
|
@@ -1036,7 +1169,7 @@ export async function clickElement(page, elementMap, options) {
|
|
|
1036
1169
|
if (!mapInfo) {
|
|
1037
1170
|
throw new Error(`找不到uid为 ${uid} 的元素,请先获取页面快照`);
|
|
1038
1171
|
}
|
|
1039
|
-
console.
|
|
1172
|
+
console.error(`[Click] 准备点击元素 - UID: ${uid}, Selector: ${mapInfo.selector}, Index: ${mapInfo.index}`);
|
|
1040
1173
|
// 使用选择器获取所有匹配元素
|
|
1041
1174
|
const elements = await page.$$(mapInfo.selector);
|
|
1042
1175
|
if (!elements || elements.length === 0) {
|
|
@@ -1053,27 +1186,27 @@ export async function clickElement(page, elementMap, options) {
|
|
|
1053
1186
|
}
|
|
1054
1187
|
// 记录点击前的页面路径
|
|
1055
1188
|
const beforePath = await page.path;
|
|
1056
|
-
console.
|
|
1189
|
+
console.error(`[Click] 点击前页面: ${beforePath}`);
|
|
1057
1190
|
// 执行点击操作
|
|
1058
1191
|
await element.tap();
|
|
1059
|
-
console.
|
|
1192
|
+
console.error(`[Click] 已执行 tap() 操作`);
|
|
1060
1193
|
// 如果是双击,再点击一次
|
|
1061
1194
|
if (dblClick) {
|
|
1062
1195
|
await new Promise(resolve => setTimeout(resolve, 100)); // 短暂延迟
|
|
1063
1196
|
await element.tap();
|
|
1064
|
-
console.
|
|
1197
|
+
console.error(`[Click] 已执行第二次 tap() (双击)`);
|
|
1065
1198
|
}
|
|
1066
1199
|
// 等待一小段时间,让页面有机会响应
|
|
1067
1200
|
await new Promise(resolve => setTimeout(resolve, 300));
|
|
1068
1201
|
// 记录点击后的页面路径
|
|
1069
1202
|
try {
|
|
1070
1203
|
const afterPath = await page.path;
|
|
1071
|
-
console.
|
|
1204
|
+
console.error(`[Click] 点击后页面: ${afterPath}`);
|
|
1072
1205
|
if (beforePath !== afterPath) {
|
|
1073
|
-
console.
|
|
1206
|
+
console.error(`[Click] ✅ 页面已切换: ${beforePath} → ${afterPath}`);
|
|
1074
1207
|
}
|
|
1075
1208
|
else {
|
|
1076
|
-
console.
|
|
1209
|
+
console.error(`[Click] ⚠️ 页面未切换,可能是同页面操作或导航延迟`);
|
|
1077
1210
|
}
|
|
1078
1211
|
}
|
|
1079
1212
|
catch (error) {
|
|
@@ -1101,12 +1234,12 @@ export async function takeScreenshot(miniProgram, options = {}) {
|
|
|
1101
1234
|
const { path } = options;
|
|
1102
1235
|
// 确保页面完全加载和稳定
|
|
1103
1236
|
try {
|
|
1104
|
-
console.
|
|
1237
|
+
console.error('获取当前页面并等待稳定...');
|
|
1105
1238
|
const currentPage = await miniProgram.currentPage();
|
|
1106
1239
|
if (currentPage && typeof currentPage.waitFor === 'function') {
|
|
1107
1240
|
// 等待页面稳定,增加等待时间
|
|
1108
1241
|
await currentPage.waitFor(1000);
|
|
1109
|
-
console.
|
|
1242
|
+
console.error('页面等待完成');
|
|
1110
1243
|
}
|
|
1111
1244
|
}
|
|
1112
1245
|
catch (waitError) {
|
|
@@ -1114,24 +1247,30 @@ export async function takeScreenshot(miniProgram, options = {}) {
|
|
|
1114
1247
|
}
|
|
1115
1248
|
// 重试机制执行截图
|
|
1116
1249
|
let result;
|
|
1250
|
+
let screenshotSucceeded = false;
|
|
1117
1251
|
let lastError;
|
|
1118
1252
|
for (let attempt = 1; attempt <= 3; attempt++) {
|
|
1119
1253
|
try {
|
|
1120
|
-
console.
|
|
1254
|
+
console.error(`截图尝试 ${attempt}/3`);
|
|
1121
1255
|
if (path) {
|
|
1122
1256
|
// 保存到指定路径
|
|
1123
1257
|
await miniProgram.screenshot({ path });
|
|
1258
|
+
if (!fs.existsSync(path)) {
|
|
1259
|
+
throw new Error(`截图命令返回成功,但目标文件不存在: ${path}`);
|
|
1260
|
+
}
|
|
1261
|
+
screenshotSucceeded = true;
|
|
1124
1262
|
result = undefined;
|
|
1125
|
-
console.
|
|
1263
|
+
console.error(`截图保存成功: ${path}`);
|
|
1126
1264
|
break;
|
|
1127
1265
|
}
|
|
1128
1266
|
else {
|
|
1129
1267
|
// 返回base64数据
|
|
1130
1268
|
const base64Data = await miniProgram.screenshot();
|
|
1131
|
-
console.
|
|
1269
|
+
console.error('截图API调用完成,检查返回数据...');
|
|
1132
1270
|
if (base64Data && typeof base64Data === 'string' && base64Data.length > 0) {
|
|
1271
|
+
screenshotSucceeded = true;
|
|
1133
1272
|
result = base64Data;
|
|
1134
|
-
console.
|
|
1273
|
+
console.error(`截图成功,数据长度: ${base64Data.length}`);
|
|
1135
1274
|
break;
|
|
1136
1275
|
}
|
|
1137
1276
|
else {
|
|
@@ -1144,12 +1283,12 @@ export async function takeScreenshot(miniProgram, options = {}) {
|
|
|
1144
1283
|
console.warn(`截图尝试 ${attempt} 失败:`, lastError.message);
|
|
1145
1284
|
if (attempt < 3) {
|
|
1146
1285
|
// 重试前等待更长时间,让页面稳定
|
|
1147
|
-
console.
|
|
1286
|
+
console.error(`等待 ${1000 + attempt * 500}ms 后重试...`);
|
|
1148
1287
|
await new Promise(resolve => setTimeout(resolve, 1000 + attempt * 500));
|
|
1149
1288
|
}
|
|
1150
1289
|
}
|
|
1151
1290
|
}
|
|
1152
|
-
if (!
|
|
1291
|
+
if (!screenshotSucceeded) {
|
|
1153
1292
|
const troubleshootingTips = `
|
|
1154
1293
|
|
|
1155
1294
|
⚠️ 截图功能故障排除建议:
|
|
@@ -2129,3 +2268,4 @@ export async function reLaunch(miniProgram, options) {
|
|
|
2129
2268
|
throw new Error(`重新启动失败: ${errorMessage}`);
|
|
2130
2269
|
}
|
|
2131
2270
|
}
|
|
2271
|
+
//# sourceMappingURL=tools.js.map
|