@creatoria/miniapp-mcp 0.2.0 → 0.2.2
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/README.md +127 -73
- package/dist/capabilities/assert/handlers/assert-handlers.d.ts +98 -0
- package/dist/capabilities/assert/handlers/assert-handlers.d.ts.map +1 -0
- package/dist/capabilities/assert/handlers/assert-handlers.js +179 -0
- package/dist/capabilities/assert/handlers/assert-handlers.js.map +1 -0
- package/dist/capabilities/assert/handlers/index.d.ts +7 -0
- package/dist/capabilities/assert/handlers/index.d.ts.map +1 -0
- package/dist/capabilities/assert/handlers/index.js +7 -0
- package/dist/capabilities/assert/handlers/index.js.map +1 -0
- package/dist/capabilities/assert/index.d.ts +12 -2
- package/dist/capabilities/assert/index.d.ts.map +1 -1
- package/dist/capabilities/assert/index.js +87 -2
- package/dist/capabilities/assert/index.js.map +1 -1
- package/dist/capabilities/assert/schemas/index.d.ts +101 -0
- package/dist/capabilities/assert/schemas/index.d.ts.map +1 -0
- package/dist/capabilities/assert/schemas/index.js +62 -0
- package/dist/capabilities/assert/schemas/index.js.map +1 -0
- package/dist/capabilities/automator/handlers/close.d.ts +18 -0
- package/dist/capabilities/automator/handlers/close.d.ts.map +1 -0
- package/dist/capabilities/automator/handlers/close.js +45 -0
- package/dist/capabilities/automator/handlers/close.js.map +1 -0
- package/dist/capabilities/automator/handlers/connect.d.ts +24 -0
- package/dist/capabilities/automator/handlers/connect.d.ts.map +1 -0
- package/dist/capabilities/automator/handlers/connect.js +51 -0
- package/dist/capabilities/automator/handlers/connect.js.map +1 -0
- package/dist/capabilities/automator/handlers/disconnect.d.ts +16 -0
- package/dist/capabilities/automator/handlers/disconnect.d.ts.map +1 -0
- package/dist/capabilities/automator/handlers/disconnect.js +44 -0
- package/dist/capabilities/automator/handlers/disconnect.js.map +1 -0
- package/dist/capabilities/automator/handlers/index.d.ts +8 -0
- package/dist/capabilities/automator/handlers/index.d.ts.map +1 -0
- package/dist/capabilities/automator/handlers/index.js +8 -0
- package/dist/capabilities/automator/handlers/index.js.map +1 -0
- package/dist/capabilities/automator/handlers/launch.d.ts +25 -0
- package/dist/capabilities/automator/handlers/launch.d.ts.map +1 -0
- package/dist/capabilities/automator/handlers/launch.js +61 -0
- package/dist/capabilities/automator/handlers/launch.js.map +1 -0
- package/dist/capabilities/automator/index.d.ts +11 -2
- package/dist/capabilities/automator/index.d.ts.map +1 -1
- package/dist/capabilities/automator/index.js +51 -2
- package/dist/capabilities/automator/index.js.map +1 -1
- package/dist/capabilities/automator/schemas/index.d.ts +5 -0
- package/dist/capabilities/automator/schemas/index.d.ts.map +1 -1
- package/dist/capabilities/automator/schemas/index.js +6 -4
- package/dist/capabilities/automator/schemas/index.js.map +1 -1
- package/dist/capabilities/element/handlers/element-handlers.d.ts +212 -0
- package/dist/capabilities/element/handlers/element-handlers.d.ts.map +1 -0
- package/dist/capabilities/element/handlers/element-handlers.js +383 -0
- package/dist/capabilities/element/handlers/element-handlers.js.map +1 -0
- package/dist/capabilities/element/handlers/index.d.ts +7 -0
- package/dist/capabilities/element/handlers/index.d.ts.map +1 -0
- package/dist/capabilities/element/handlers/index.js +27 -0
- package/dist/capabilities/element/handlers/index.js.map +1 -0
- package/dist/capabilities/element/index.d.ts +17 -2
- package/dist/capabilities/element/index.d.ts.map +1 -1
- package/dist/capabilities/element/index.js +196 -2
- package/dist/capabilities/element/index.js.map +1 -1
- package/dist/capabilities/element/schemas/index.d.ts +325 -0
- package/dist/capabilities/element/schemas/index.d.ts.map +1 -0
- package/dist/capabilities/element/schemas/index.js +168 -0
- package/dist/capabilities/element/schemas/index.js.map +1 -0
- package/dist/capabilities/index.d.ts +23 -4
- package/dist/capabilities/index.d.ts.map +1 -1
- package/dist/capabilities/index.js +29 -3
- package/dist/capabilities/index.js.map +1 -1
- package/dist/capabilities/loader.d.ts +61 -0
- package/dist/capabilities/loader.d.ts.map +1 -0
- package/dist/capabilities/loader.js +164 -0
- package/dist/capabilities/loader.js.map +1 -0
- package/dist/capabilities/miniprogram/handlers/call-wx.d.ts +24 -0
- package/dist/capabilities/miniprogram/handlers/call-wx.d.ts.map +1 -0
- package/dist/capabilities/miniprogram/handlers/call-wx.js +37 -0
- package/dist/capabilities/miniprogram/handlers/call-wx.js.map +1 -0
- package/dist/capabilities/miniprogram/handlers/evaluate.d.ts +32 -0
- package/dist/capabilities/miniprogram/handlers/evaluate.d.ts.map +1 -0
- package/dist/capabilities/miniprogram/handlers/evaluate.js +51 -0
- package/dist/capabilities/miniprogram/handlers/evaluate.js.map +1 -0
- package/dist/capabilities/miniprogram/handlers/index.d.ts +12 -0
- package/dist/capabilities/miniprogram/handlers/index.d.ts.map +1 -0
- package/dist/capabilities/miniprogram/handlers/index.js +12 -0
- package/dist/capabilities/miniprogram/handlers/index.js.map +1 -0
- package/dist/capabilities/miniprogram/handlers/navigate.d.ts +26 -0
- package/dist/capabilities/miniprogram/handlers/navigate.d.ts.map +1 -0
- package/dist/capabilities/miniprogram/handlers/navigate.js +56 -0
- package/dist/capabilities/miniprogram/handlers/navigate.js.map +1 -0
- package/dist/capabilities/miniprogram/handlers/page-stack.d.ts +20 -0
- package/dist/capabilities/miniprogram/handlers/page-stack.d.ts.map +1 -0
- package/dist/capabilities/miniprogram/handlers/page-stack.js +37 -0
- package/dist/capabilities/miniprogram/handlers/page-stack.js.map +1 -0
- package/dist/capabilities/miniprogram/handlers/screenshot.d.ts +33 -0
- package/dist/capabilities/miniprogram/handlers/screenshot.d.ts.map +1 -0
- package/dist/capabilities/miniprogram/handlers/screenshot.js +132 -0
- package/dist/capabilities/miniprogram/handlers/screenshot.js.map +1 -0
- package/dist/capabilities/miniprogram/handlers/system-info.d.ts +17 -0
- package/dist/capabilities/miniprogram/handlers/system-info.d.ts.map +1 -0
- package/dist/capabilities/miniprogram/handlers/system-info.js +31 -0
- package/dist/capabilities/miniprogram/handlers/system-info.js.map +1 -0
- package/dist/capabilities/miniprogram/index.d.ts +12 -2
- package/dist/capabilities/miniprogram/index.d.ts.map +1 -1
- package/dist/capabilities/miniprogram/index.js +66 -2
- package/dist/capabilities/miniprogram/index.js.map +1 -1
- package/dist/capabilities/miniprogram/schemas/call-wx.d.ts +15 -0
- package/dist/capabilities/miniprogram/schemas/call-wx.d.ts.map +1 -0
- package/dist/capabilities/miniprogram/schemas/call-wx.js +11 -0
- package/dist/capabilities/miniprogram/schemas/call-wx.js.map +1 -0
- package/dist/capabilities/miniprogram/schemas/evaluate.d.ts +15 -0
- package/dist/capabilities/miniprogram/schemas/evaluate.d.ts.map +1 -0
- package/dist/capabilities/miniprogram/schemas/evaluate.js +11 -0
- package/dist/capabilities/miniprogram/schemas/evaluate.js.map +1 -0
- package/dist/capabilities/miniprogram/schemas/index.d.ts +10 -0
- package/dist/capabilities/miniprogram/schemas/index.d.ts.map +1 -0
- package/dist/capabilities/miniprogram/schemas/index.js +10 -0
- package/dist/capabilities/miniprogram/schemas/index.js.map +1 -0
- package/dist/capabilities/miniprogram/schemas/navigate.d.ts +18 -0
- package/dist/capabilities/miniprogram/schemas/navigate.d.ts.map +1 -0
- package/dist/capabilities/miniprogram/schemas/navigate.js +22 -0
- package/dist/capabilities/miniprogram/schemas/navigate.js.map +1 -0
- package/dist/capabilities/miniprogram/schemas/page-stack.d.ts +6 -0
- package/dist/capabilities/miniprogram/schemas/page-stack.d.ts.map +1 -0
- package/dist/capabilities/miniprogram/schemas/page-stack.js +6 -0
- package/dist/capabilities/miniprogram/schemas/page-stack.js.map +1 -0
- package/dist/capabilities/miniprogram/schemas/screenshot.d.ts +18 -0
- package/dist/capabilities/miniprogram/schemas/screenshot.d.ts.map +1 -0
- package/dist/capabilities/miniprogram/schemas/screenshot.js +21 -0
- package/dist/capabilities/miniprogram/schemas/screenshot.js.map +1 -0
- package/dist/capabilities/miniprogram/schemas/system-info.d.ts +6 -0
- package/dist/capabilities/miniprogram/schemas/system-info.d.ts.map +1 -0
- package/dist/capabilities/miniprogram/schemas/system-info.js +6 -0
- package/dist/capabilities/miniprogram/schemas/system-info.js.map +1 -0
- package/dist/capabilities/network/handlers/index.d.ts +7 -0
- package/dist/capabilities/network/handlers/index.d.ts.map +1 -0
- package/dist/capabilities/network/handlers/index.js +7 -0
- package/dist/capabilities/network/handlers/index.js.map +1 -0
- package/dist/capabilities/network/handlers/network-handlers.d.ts +53 -0
- package/dist/capabilities/network/handlers/network-handlers.d.ts.map +1 -0
- package/dist/capabilities/network/handlers/network-handlers.js +127 -0
- package/dist/capabilities/network/handlers/network-handlers.js.map +1 -0
- package/dist/capabilities/network/index.d.ts +12 -2
- package/dist/capabilities/network/index.d.ts.map +1 -1
- package/dist/capabilities/network/index.js +66 -2
- package/dist/capabilities/network/index.js.map +1 -1
- package/dist/capabilities/network/schemas/index.d.ts +53 -0
- package/dist/capabilities/network/schemas/index.d.ts.map +1 -0
- package/dist/capabilities/network/schemas/index.js +33 -0
- package/dist/capabilities/network/schemas/index.js.map +1 -0
- package/dist/capabilities/page/handlers/index.d.ts +7 -0
- package/dist/capabilities/page/handlers/index.d.ts.map +1 -0
- package/dist/capabilities/page/handlers/index.js +7 -0
- package/dist/capabilities/page/handlers/index.js.map +1 -0
- package/dist/capabilities/page/handlers/page-handlers.d.ts +165 -0
- package/dist/capabilities/page/handlers/page-handlers.d.ts.map +1 -0
- package/dist/capabilities/page/handlers/page-handlers.js +258 -0
- package/dist/capabilities/page/handlers/page-handlers.js.map +1 -0
- package/dist/capabilities/page/index.d.ts +12 -2
- package/dist/capabilities/page/index.d.ts.map +1 -1
- package/dist/capabilities/page/index.js +80 -2
- package/dist/capabilities/page/index.js.map +1 -1
- package/dist/capabilities/page/schemas/index.d.ts +91 -0
- package/dist/capabilities/page/schemas/index.d.ts.map +1 -0
- package/dist/capabilities/page/schemas/index.js +55 -0
- package/dist/capabilities/page/schemas/index.js.map +1 -0
- package/dist/capabilities/record/handlers/index.d.ts +7 -0
- package/dist/capabilities/record/handlers/index.d.ts.map +1 -0
- package/dist/capabilities/record/handlers/index.js +7 -0
- package/dist/capabilities/record/handlers/index.js.map +1 -0
- package/dist/capabilities/record/handlers/record-handlers.d.ts +76 -0
- package/dist/capabilities/record/handlers/record-handlers.d.ts.map +1 -0
- package/dist/capabilities/record/handlers/record-handlers.js +255 -0
- package/dist/capabilities/record/handlers/record-handlers.js.map +1 -0
- package/dist/capabilities/record/index.d.ts +12 -2
- package/dist/capabilities/record/index.d.ts.map +1 -1
- package/dist/capabilities/record/index.js +66 -2
- package/dist/capabilities/record/index.js.map +1 -1
- package/dist/capabilities/record/schemas/index.d.ts +47 -0
- package/dist/capabilities/record/schemas/index.d.ts.map +1 -0
- package/dist/capabilities/record/schemas/index.js +33 -0
- package/dist/capabilities/record/schemas/index.js.map +1 -0
- package/dist/capabilities/registry.d.ts +138 -0
- package/dist/capabilities/registry.d.ts.map +1 -0
- package/dist/capabilities/registry.js +261 -0
- package/dist/capabilities/registry.js.map +1 -0
- package/dist/capabilities/snapshot/handlers/index.d.ts +7 -0
- package/dist/capabilities/snapshot/handlers/index.d.ts.map +1 -0
- package/dist/capabilities/snapshot/handlers/index.js +7 -0
- package/dist/capabilities/snapshot/handlers/index.js.map +1 -0
- package/dist/capabilities/snapshot/handlers/snapshot-handlers.d.ts +75 -0
- package/dist/capabilities/snapshot/handlers/snapshot-handlers.d.ts.map +1 -0
- package/dist/capabilities/snapshot/handlers/snapshot-handlers.js +201 -0
- package/dist/capabilities/snapshot/handlers/snapshot-handlers.js.map +1 -0
- package/dist/capabilities/snapshot/index.d.ts +12 -2
- package/dist/capabilities/snapshot/index.d.ts.map +1 -1
- package/dist/capabilities/snapshot/index.js +45 -2
- package/dist/capabilities/snapshot/index.js.map +1 -1
- package/dist/capabilities/snapshot/schemas/index.d.ts +47 -0
- package/dist/capabilities/snapshot/schemas/index.d.ts.map +1 -0
- package/dist/capabilities/snapshot/schemas/index.js +28 -0
- package/dist/capabilities/snapshot/schemas/index.js.map +1 -0
- package/dist/runtime/index.d.ts +2 -1
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +2 -1
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/logging/tool-logger.js +2 -2
- package/dist/runtime/logging/tool-logger.js.map +1 -1
- package/dist/runtime/retry/index.d.ts +5 -0
- package/dist/runtime/retry/index.d.ts.map +1 -0
- package/dist/runtime/retry/index.js +5 -0
- package/dist/runtime/retry/index.js.map +1 -0
- package/dist/runtime/retry/retry.d.ts +61 -0
- package/dist/runtime/retry/retry.d.ts.map +1 -0
- package/dist/runtime/retry/retry.js +90 -0
- package/dist/runtime/retry/retry.js.map +1 -0
- package/dist/runtime/timeout/timeout.d.ts +10 -0
- package/dist/runtime/timeout/timeout.d.ts.map +1 -1
- package/dist/runtime/timeout/timeout.js +10 -0
- package/dist/runtime/timeout/timeout.js.map +1 -1
- package/dist/server.js +4 -4
- package/dist/server.js.map +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +7 -3
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/miniprogram.d.ts +11 -1
- package/dist/tools/miniprogram.d.ts.map +1 -1
- package/dist/tools/miniprogram.js +102 -20
- package/dist/tools/miniprogram.js.map +1 -1
- package/docs/current/01-architecture-migration.md +337 -0
- package/docs/current/02-screenshot-timeout-fix.md +589 -0
- package/docs/current/README.md +226 -0
- package/package.json +1 -1
|
@@ -0,0 +1,589 @@
|
|
|
1
|
+
# 截图超时问题修复方案
|
|
2
|
+
|
|
3
|
+
> 版本: 1.0.0
|
|
4
|
+
> 日期: 2025-12-27
|
|
5
|
+
> 状态: ✅ 已完成
|
|
6
|
+
|
|
7
|
+
## 1. 问题概述
|
|
8
|
+
|
|
9
|
+
### 1.1 问题现象
|
|
10
|
+
|
|
11
|
+
调用截图工具 (`miniprogram_screenshot`) 时经常出现超时卡住,导致:
|
|
12
|
+
- MCP 工具调用无响应
|
|
13
|
+
- 后续工具调用被阻塞
|
|
14
|
+
- 集成测试超时失败
|
|
15
|
+
|
|
16
|
+
### 1.2 影响范围
|
|
17
|
+
|
|
18
|
+
| 工具 | 受影响 | 原因 |
|
|
19
|
+
|------|--------|------|
|
|
20
|
+
| miniprogram_screenshot | ✅ 直接受影响 | 无超时保护 |
|
|
21
|
+
| snapshot_page | ✅ 级联受影响 | 调用 screenshot |
|
|
22
|
+
| snapshot_full | ✅ 级联受影响 | 调用 screenshot |
|
|
23
|
+
| snapshot_element | ✅ 级联受影响 | 调用 screenshot |
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 2. 根本原因分析
|
|
28
|
+
|
|
29
|
+
### 2.1 核心问题:缺少超时保护
|
|
30
|
+
|
|
31
|
+
**位置**:`src/tools/miniprogram.ts` 第 250-253 行
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
// ❌ 当前代码 - 无超时保护
|
|
35
|
+
const screenshotBuffer = await session.miniProgram.screenshot({
|
|
36
|
+
path: fullPath,
|
|
37
|
+
fullPage,
|
|
38
|
+
})
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**对比**:`evaluate()` 函数正确使用了超时保护
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
// ✅ evaluate 的正确做法 (第 174-177 行)
|
|
45
|
+
const result = await withTimeout(
|
|
46
|
+
session.miniProgram.evaluate(expression, ...evalArgs),
|
|
47
|
+
timeoutMs,
|
|
48
|
+
'Evaluate expression'
|
|
49
|
+
)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 2.2 超时保护对比
|
|
53
|
+
|
|
54
|
+
| 操作 | 超时保护 | 代码位置 |
|
|
55
|
+
|------|---------|---------|
|
|
56
|
+
| evaluate | ✅ 有 | miniprogram.ts:174-177 |
|
|
57
|
+
| navigate | ❌ 无 | miniprogram.ts:46,50 |
|
|
58
|
+
| callWx | ❌ 无 | miniprogram.ts:107 |
|
|
59
|
+
| **screenshot** | ❌ **无** | miniprogram.ts:250-253 |
|
|
60
|
+
| pageStack | ❌ 无 | miniprogram.ts:297 |
|
|
61
|
+
| systemInfo | ❌ 无 | miniprogram.ts:341 |
|
|
62
|
+
|
|
63
|
+
### 2.3 功能退化:base64 返回被删除
|
|
64
|
+
|
|
65
|
+
**历史**:commit `e8599ed` 添加了 base64 返回功能
|
|
66
|
+
**当前**:HEAD 版本删除了所有 base64 相关代码
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
// e8599ed 版本 - 有快速路径
|
|
70
|
+
if (!filename) {
|
|
71
|
+
const base64String = await session.miniProgram.screenshot()
|
|
72
|
+
return { success: true, message: '...', base64: base64String }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// 当前版本 - 强制必须有 OutputManager
|
|
76
|
+
if (!outputManager) {
|
|
77
|
+
throw new Error('OutputManager not available')
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### 2.4 可能导致长时间卡顿的原因
|
|
82
|
+
|
|
83
|
+
1. **fullPage 模式**:需要多次截图拼接,非常耗时
|
|
84
|
+
2. **WebSocket 连接中断**:DevTools 连接丢失
|
|
85
|
+
3. **图片编码**:大分辨率下编码耗时
|
|
86
|
+
4. **UI 线程阻塞**:开发者工具 UI 冻结
|
|
87
|
+
5. **内存压力**:大型 fullPage 截图导致 GC 暂停
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 3. 修复方案
|
|
92
|
+
|
|
93
|
+
### 3.1 P0:添加超时保护(必须)
|
|
94
|
+
|
|
95
|
+
**文件**:`src/tools/miniprogram.ts`
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
// 修改后的 screenshot 函数
|
|
99
|
+
export async function screenshot(
|
|
100
|
+
session: SessionState,
|
|
101
|
+
args: {
|
|
102
|
+
filename?: string
|
|
103
|
+
fullPage?: boolean
|
|
104
|
+
returnBase64?: boolean // 恢复 base64 选项
|
|
105
|
+
}
|
|
106
|
+
): Promise<{
|
|
107
|
+
success: boolean
|
|
108
|
+
message: string
|
|
109
|
+
path?: string
|
|
110
|
+
base64?: string
|
|
111
|
+
}> {
|
|
112
|
+
const { filename, fullPage = false, returnBase64 = false } = args
|
|
113
|
+
|
|
114
|
+
// 导入超时工具
|
|
115
|
+
const { withTimeout, getTimeout, DEFAULT_TIMEOUTS } = await import('../runtime/timeout/timeout.js')
|
|
116
|
+
|
|
117
|
+
// fullPage 模式使用更长的超时
|
|
118
|
+
const baseTimeout = getTimeout(
|
|
119
|
+
session.loggerConfig?.screenshotTimeout,
|
|
120
|
+
DEFAULT_TIMEOUTS.screenshot
|
|
121
|
+
)
|
|
122
|
+
const timeoutMs = fullPage ? baseTimeout * 2 : baseTimeout
|
|
123
|
+
|
|
124
|
+
session.logger?.info('Taking screenshot', { filename, fullPage, timeoutMs })
|
|
125
|
+
|
|
126
|
+
// 如果只需要 base64,走快速路径
|
|
127
|
+
if (returnBase64 && !filename) {
|
|
128
|
+
const buffer = await withTimeout(
|
|
129
|
+
session.miniProgram.screenshot({ fullPage }),
|
|
130
|
+
timeoutMs,
|
|
131
|
+
'Screenshot capture (base64)'
|
|
132
|
+
)
|
|
133
|
+
return {
|
|
134
|
+
success: true,
|
|
135
|
+
message: 'Screenshot captured successfully',
|
|
136
|
+
base64: buffer.toString('base64'),
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// 文件保存路径
|
|
141
|
+
const outputManager = session.outputManager
|
|
142
|
+
if (!outputManager) {
|
|
143
|
+
throw new Error('OutputManager not available')
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const resolvedFilename = filename || outputManager.generateFilename('screenshot', 'png')
|
|
147
|
+
const fullPath = join(outputManager.getOutputDir(), resolvedFilename)
|
|
148
|
+
|
|
149
|
+
await outputManager.ensureOutputDir()
|
|
150
|
+
|
|
151
|
+
// 🔧 关键修复:添加超时保护
|
|
152
|
+
const screenshotBuffer = await withTimeout(
|
|
153
|
+
session.miniProgram.screenshot({
|
|
154
|
+
path: fullPath,
|
|
155
|
+
fullPage,
|
|
156
|
+
}),
|
|
157
|
+
timeoutMs,
|
|
158
|
+
'Screenshot capture'
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
if (screenshotBuffer) {
|
|
162
|
+
await outputManager.writeFile(resolvedFilename, screenshotBuffer)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
session.logger?.info('Screenshot saved', { path: fullPath })
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
success: true,
|
|
169
|
+
message: `Screenshot saved to ${resolvedFilename}`,
|
|
170
|
+
path: fullPath,
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### 3.2 P0:恢复 base64 返回能力
|
|
176
|
+
|
|
177
|
+
**修改 schema**:`src/tools/index.ts` 中 screenshot 工具定义
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
// 添加 returnBase64 参数
|
|
181
|
+
{
|
|
182
|
+
name: 'miniprogram_screenshot',
|
|
183
|
+
description: 'Take a screenshot of the Mini Program',
|
|
184
|
+
inputSchema: {
|
|
185
|
+
type: 'object',
|
|
186
|
+
properties: {
|
|
187
|
+
filename: {
|
|
188
|
+
type: 'string',
|
|
189
|
+
description: 'Filename for screenshot (optional, auto-generated if not provided)',
|
|
190
|
+
},
|
|
191
|
+
fullPage: {
|
|
192
|
+
type: 'boolean',
|
|
193
|
+
description: 'Capture full page including scroll area',
|
|
194
|
+
default: false,
|
|
195
|
+
},
|
|
196
|
+
returnBase64: { // 🆕 新增
|
|
197
|
+
type: 'boolean',
|
|
198
|
+
description: 'Return screenshot as base64 string instead of saving to file',
|
|
199
|
+
default: false,
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**修改返回类型**:
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
// 更新 ScreenshotResult 类型
|
|
210
|
+
interface ScreenshotResult {
|
|
211
|
+
success: boolean
|
|
212
|
+
message: string
|
|
213
|
+
path?: string // 文件路径(保存时)
|
|
214
|
+
base64?: string // Base64 数据(returnBase64=true 时)
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### 3.3 P1:为其他操作添加超时保护
|
|
219
|
+
|
|
220
|
+
需要添加超时保护的函数:
|
|
221
|
+
|
|
222
|
+
| 函数 | 位置 | 超时值 |
|
|
223
|
+
|------|------|--------|
|
|
224
|
+
| navigate | miniprogram.ts:46 | 30s |
|
|
225
|
+
| callWx | miniprogram.ts:107 | 10s |
|
|
226
|
+
| getPageStack | miniprogram.ts:297 | 5s |
|
|
227
|
+
| getSystemInfo | miniprogram.ts:341 | 5s |
|
|
228
|
+
|
|
229
|
+
**示例:navigate 修复**
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
export async function navigate(
|
|
233
|
+
session: SessionState,
|
|
234
|
+
args: { url: string }
|
|
235
|
+
): Promise<{ success: boolean; message: string }> {
|
|
236
|
+
const { withTimeout, getTimeout, DEFAULT_TIMEOUTS } = await import('../runtime/timeout/timeout.js')
|
|
237
|
+
|
|
238
|
+
const timeoutMs = getTimeout(
|
|
239
|
+
session.loggerConfig?.navigationTimeout,
|
|
240
|
+
DEFAULT_TIMEOUTS.navigation
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
await withTimeout(
|
|
244
|
+
session.miniProgram.navigateTo({ url: args.url }),
|
|
245
|
+
timeoutMs,
|
|
246
|
+
'Page navigation'
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
return { success: true, message: `Navigated to ${args.url}` }
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### 3.4 P1:添加重试机制
|
|
254
|
+
|
|
255
|
+
**新建**:`src/runtime/retry/retry.ts`
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
export interface RetryOptions {
|
|
259
|
+
maxRetries: number
|
|
260
|
+
delayMs: number
|
|
261
|
+
backoffMultiplier?: number
|
|
262
|
+
shouldRetry?: (error: Error) => boolean
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export async function withRetry<T>(
|
|
266
|
+
operation: () => Promise<T>,
|
|
267
|
+
options: RetryOptions
|
|
268
|
+
): Promise<T> {
|
|
269
|
+
const {
|
|
270
|
+
maxRetries,
|
|
271
|
+
delayMs,
|
|
272
|
+
backoffMultiplier = 1.5,
|
|
273
|
+
shouldRetry = () => true,
|
|
274
|
+
} = options
|
|
275
|
+
|
|
276
|
+
let lastError: Error
|
|
277
|
+
|
|
278
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
279
|
+
try {
|
|
280
|
+
return await operation()
|
|
281
|
+
} catch (error) {
|
|
282
|
+
lastError = error as Error
|
|
283
|
+
|
|
284
|
+
if (attempt === maxRetries || !shouldRetry(lastError)) {
|
|
285
|
+
throw lastError
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const delay = delayMs * Math.pow(backoffMultiplier, attempt)
|
|
289
|
+
await new Promise(resolve => setTimeout(resolve, delay))
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
throw lastError!
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
**使用示例**:
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
const screenshotBuffer = await withRetry(
|
|
301
|
+
() => withTimeout(
|
|
302
|
+
session.miniProgram.screenshot({ fullPage }),
|
|
303
|
+
timeoutMs,
|
|
304
|
+
'Screenshot capture'
|
|
305
|
+
),
|
|
306
|
+
{
|
|
307
|
+
maxRetries: 2,
|
|
308
|
+
delayMs: 1000,
|
|
309
|
+
shouldRetry: (error) => error.message.includes('timeout'),
|
|
310
|
+
}
|
|
311
|
+
)
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### 3.5 P2:优化 fullPage 超时
|
|
315
|
+
|
|
316
|
+
**配置更新**:`src/config/defaults.ts`
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
export const DEFAULT_TIMEOUTS = {
|
|
320
|
+
screenshot: 10 * 1000, // 10s 普通截图
|
|
321
|
+
screenshotFullPage: 30 * 1000, // 30s 全页截图
|
|
322
|
+
navigation: 30 * 1000, // 30s 导航
|
|
323
|
+
evaluate: 5 * 1000, // 5s 脚本执行
|
|
324
|
+
callWx: 10 * 1000, // 10s wx API
|
|
325
|
+
pageStack: 5 * 1000, // 5s 页面栈
|
|
326
|
+
systemInfo: 5 * 1000, // 5s 系统信息
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### 3.6 P2:添加性能日志
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
export async function screenshot(session: SessionState, args: ScreenshotArgs) {
|
|
334
|
+
const startTime = Date.now()
|
|
335
|
+
|
|
336
|
+
try {
|
|
337
|
+
// ... 截图逻辑 ...
|
|
338
|
+
|
|
339
|
+
const duration = Date.now() - startTime
|
|
340
|
+
session.logger?.info('Screenshot performance', {
|
|
341
|
+
duration,
|
|
342
|
+
fullPage,
|
|
343
|
+
size: screenshotBuffer?.length,
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
return result
|
|
347
|
+
} catch (error) {
|
|
348
|
+
const duration = Date.now() - startTime
|
|
349
|
+
session.logger?.error('Screenshot failed', {
|
|
350
|
+
duration,
|
|
351
|
+
fullPage,
|
|
352
|
+
error: (error as Error).message,
|
|
353
|
+
})
|
|
354
|
+
throw error
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
## 4. 改动文件清单
|
|
362
|
+
|
|
363
|
+
### 4.1 修改文件
|
|
364
|
+
|
|
365
|
+
| 文件 | 改动描述 |
|
|
366
|
+
|------|---------|
|
|
367
|
+
| src/tools/miniprogram.ts | 添加超时保护、恢复 base64 |
|
|
368
|
+
| src/tools/index.ts | 更新 schema 定义 |
|
|
369
|
+
| src/config/defaults.ts | 添加新的超时配置 |
|
|
370
|
+
| src/types.ts | 更新返回类型定义 |
|
|
371
|
+
|
|
372
|
+
### 4.2 新建文件
|
|
373
|
+
|
|
374
|
+
| 文件 | 描述 |
|
|
375
|
+
|------|------|
|
|
376
|
+
| src/runtime/retry/retry.ts | 重试机制 |
|
|
377
|
+
| src/runtime/retry/index.ts | 导出入口 |
|
|
378
|
+
|
|
379
|
+
### 4.3 测试文件
|
|
380
|
+
|
|
381
|
+
| 文件 | 描述 |
|
|
382
|
+
|------|------|
|
|
383
|
+
| tests/unit/screenshot.test.ts | 截图超时测试 |
|
|
384
|
+
| tests/unit/retry.test.ts | 重试机制测试 |
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## 5. 代码改动示例
|
|
389
|
+
|
|
390
|
+
### 5.1 miniprogram.ts 完整修改
|
|
391
|
+
|
|
392
|
+
```diff
|
|
393
|
+
// src/tools/miniprogram.ts
|
|
394
|
+
|
|
395
|
+
+ import { withTimeout, getTimeout, DEFAULT_TIMEOUTS } from '../runtime/timeout/timeout.js'
|
|
396
|
+
|
|
397
|
+
export async function screenshot(
|
|
398
|
+
session: SessionState,
|
|
399
|
+
args: {
|
|
400
|
+
filename?: string
|
|
401
|
+
fullPage?: boolean
|
|
402
|
+
+ returnBase64?: boolean
|
|
403
|
+
}
|
|
404
|
+
): Promise<{
|
|
405
|
+
success: boolean
|
|
406
|
+
message: string
|
|
407
|
+
path?: string
|
|
408
|
+
+ base64?: string
|
|
409
|
+
}> {
|
|
410
|
+
- const { filename, fullPage = false } = args
|
|
411
|
+
+ const { filename, fullPage = false, returnBase64 = false } = args
|
|
412
|
+
|
|
413
|
+
+ // 计算超时时间
|
|
414
|
+
+ const baseTimeout = getTimeout(
|
|
415
|
+
+ session.loggerConfig?.screenshotTimeout,
|
|
416
|
+
+ DEFAULT_TIMEOUTS.screenshot
|
|
417
|
+
+ )
|
|
418
|
+
+ const timeoutMs = fullPage ? baseTimeout * 2 : baseTimeout
|
|
419
|
+
|
|
420
|
+
+ session.logger?.info('Taking screenshot', { filename, fullPage, timeoutMs })
|
|
421
|
+
|
|
422
|
+
+ // base64 快速路径
|
|
423
|
+
+ if (returnBase64 && !filename) {
|
|
424
|
+
+ const buffer = await withTimeout(
|
|
425
|
+
+ session.miniProgram.screenshot({ fullPage }),
|
|
426
|
+
+ timeoutMs,
|
|
427
|
+
+ 'Screenshot capture (base64)'
|
|
428
|
+
+ )
|
|
429
|
+
+ return {
|
|
430
|
+
+ success: true,
|
|
431
|
+
+ message: 'Screenshot captured successfully',
|
|
432
|
+
+ base64: buffer.toString('base64'),
|
|
433
|
+
+ }
|
|
434
|
+
+ }
|
|
435
|
+
|
|
436
|
+
const outputManager = session.outputManager
|
|
437
|
+
if (!outputManager) {
|
|
438
|
+
throw new Error('OutputManager not available')
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const resolvedFilename = filename || outputManager.generateFilename('screenshot', 'png')
|
|
442
|
+
const fullPath = join(outputManager.getOutputDir(), resolvedFilename)
|
|
443
|
+
|
|
444
|
+
await outputManager.ensureOutputDir()
|
|
445
|
+
|
|
446
|
+
- const screenshotBuffer = await session.miniProgram.screenshot({
|
|
447
|
+
- path: fullPath,
|
|
448
|
+
- fullPage,
|
|
449
|
+
- })
|
|
450
|
+
+ const screenshotBuffer = await withTimeout(
|
|
451
|
+
+ session.miniProgram.screenshot({
|
|
452
|
+
+ path: fullPath,
|
|
453
|
+
+ fullPage,
|
|
454
|
+
+ }),
|
|
455
|
+
+ timeoutMs,
|
|
456
|
+
+ 'Screenshot capture'
|
|
457
|
+
+ )
|
|
458
|
+
|
|
459
|
+
if (screenshotBuffer) {
|
|
460
|
+
await outputManager.writeFile(resolvedFilename, screenshotBuffer)
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
session.logger?.info('Screenshot saved', { path: fullPath })
|
|
464
|
+
|
|
465
|
+
return {
|
|
466
|
+
success: true,
|
|
467
|
+
message: `Screenshot saved to ${resolvedFilename}`,
|
|
468
|
+
path: fullPath,
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
### 5.2 defaults.ts 修改
|
|
474
|
+
|
|
475
|
+
```diff
|
|
476
|
+
// src/config/defaults.ts
|
|
477
|
+
|
|
478
|
+
export const DEFAULT_TIMEOUTS = {
|
|
479
|
+
screenshot: 10 * 1000,
|
|
480
|
+
+ screenshotFullPage: 30 * 1000,
|
|
481
|
+
evaluate: 5 * 1000,
|
|
482
|
+
launch: 60 * 1000,
|
|
483
|
+
+ navigation: 30 * 1000,
|
|
484
|
+
+ callWx: 10 * 1000,
|
|
485
|
+
+ pageStack: 5 * 1000,
|
|
486
|
+
+ systemInfo: 5 * 1000,
|
|
487
|
+
}
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
---
|
|
491
|
+
|
|
492
|
+
## 6. 测试计划
|
|
493
|
+
|
|
494
|
+
### 6.1 单元测试
|
|
495
|
+
|
|
496
|
+
```typescript
|
|
497
|
+
// tests/unit/screenshot.test.ts
|
|
498
|
+
describe('screenshot', () => {
|
|
499
|
+
it('should timeout after configured duration', async () => {
|
|
500
|
+
// Mock miniProgram.screenshot to never resolve
|
|
501
|
+
const mockScreenshot = jest.fn(() => new Promise(() => {}))
|
|
502
|
+
session.miniProgram = { screenshot: mockScreenshot }
|
|
503
|
+
|
|
504
|
+
await expect(screenshot(session, {}))
|
|
505
|
+
.rejects.toThrow('Screenshot capture timed out')
|
|
506
|
+
})
|
|
507
|
+
|
|
508
|
+
it('should return base64 when returnBase64 is true', async () => {
|
|
509
|
+
const mockBuffer = Buffer.from('test-image')
|
|
510
|
+
session.miniProgram = {
|
|
511
|
+
screenshot: jest.fn().mockResolvedValue(mockBuffer)
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const result = await screenshot(session, { returnBase64: true })
|
|
515
|
+
|
|
516
|
+
expect(result.base64).toBe(mockBuffer.toString('base64'))
|
|
517
|
+
expect(result.path).toBeUndefined()
|
|
518
|
+
})
|
|
519
|
+
|
|
520
|
+
it('should use longer timeout for fullPage', async () => {
|
|
521
|
+
// Verify timeout is doubled for fullPage
|
|
522
|
+
})
|
|
523
|
+
})
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
### 6.2 集成测试
|
|
527
|
+
|
|
528
|
+
```typescript
|
|
529
|
+
// tests/integration/screenshot.test.ts
|
|
530
|
+
describe('screenshot integration', () => {
|
|
531
|
+
it('should capture and return screenshot', async () => {
|
|
532
|
+
const result = await client.callTool('miniprogram_screenshot', {
|
|
533
|
+
returnBase64: true,
|
|
534
|
+
})
|
|
535
|
+
|
|
536
|
+
expect(result.success).toBe(true)
|
|
537
|
+
expect(result.base64).toMatch(/^[A-Za-z0-9+/=]+$/)
|
|
538
|
+
})
|
|
539
|
+
|
|
540
|
+
it('should not hang on fullPage screenshot', async () => {
|
|
541
|
+
const startTime = Date.now()
|
|
542
|
+
|
|
543
|
+
const result = await client.callTool('miniprogram_screenshot', {
|
|
544
|
+
fullPage: true,
|
|
545
|
+
})
|
|
546
|
+
|
|
547
|
+
const duration = Date.now() - startTime
|
|
548
|
+
expect(duration).toBeLessThan(35000) // 30s timeout + buffer
|
|
549
|
+
})
|
|
550
|
+
})
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
---
|
|
554
|
+
|
|
555
|
+
## 7. 时间估算
|
|
556
|
+
|
|
557
|
+
| 任务 | 预计时间 | 优先级 |
|
|
558
|
+
|------|---------|--------|
|
|
559
|
+
| 添加 screenshot 超时保护 | 1h | P0 |
|
|
560
|
+
| 恢复 base64 返回能力 | 1h | P0 |
|
|
561
|
+
| 其他操作添加超时保护 | 2h | P1 |
|
|
562
|
+
| 添加重试机制 | 1.5h | P1 |
|
|
563
|
+
| 优化 fullPage 超时配置 | 0.5h | P2 |
|
|
564
|
+
| 添加性能日志 | 0.5h | P2 |
|
|
565
|
+
| 编写测试 | 2h | P1 |
|
|
566
|
+
| **总计** | **8-9h** | - |
|
|
567
|
+
|
|
568
|
+
---
|
|
569
|
+
|
|
570
|
+
## 8. 验收标准
|
|
571
|
+
|
|
572
|
+
- [ ] 截图工具在 10 秒内必须返回结果或超时错误
|
|
573
|
+
- [ ] fullPage 截图在 30 秒内必须返回结果或超时错误
|
|
574
|
+
- [ ] returnBase64=true 时正确返回 base64 数据
|
|
575
|
+
- [ ] 超时错误信息清晰,包含操作名称和超时时长
|
|
576
|
+
- [ ] 所有 miniprogram 操作都有超时保护
|
|
577
|
+
- [ ] 单元测试覆盖超时场景
|
|
578
|
+
- [ ] 集成测试验证不会卡住
|
|
579
|
+
|
|
580
|
+
---
|
|
581
|
+
|
|
582
|
+
## 9. 风险与缓解
|
|
583
|
+
|
|
584
|
+
| 风险 | 影响 | 缓解措施 |
|
|
585
|
+
|------|------|---------|
|
|
586
|
+
| 超时时间设置过短 | 正常截图被误杀 | 添加配置项,允许用户调整 |
|
|
587
|
+
| fullPage 页面过长 | 30s 仍不够 | 添加进度回调,支持分段截图 |
|
|
588
|
+
| 重试导致更长等待 | 用户体验差 | 重试次数可配置,默认 2 次 |
|
|
589
|
+
| SDK 本身有 bug | 无法根本解决 | 添加详细日志,便于排查 |
|