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.
Files changed (183) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +127 -128
  3. package/build/MiniProgramContext.d.ts +317 -0
  4. package/build/MiniProgramContext.d.ts.map +1 -0
  5. package/build/MiniProgramContext.js +696 -0
  6. package/build/MiniProgramContext.js.map +1 -0
  7. package/build/collectors/Collector.d.ts +127 -0
  8. package/build/collectors/Collector.d.ts.map +1 -0
  9. package/build/collectors/Collector.js +252 -0
  10. package/build/collectors/Collector.js.map +1 -0
  11. package/build/collectors/ConsoleCollector.d.ts +104 -0
  12. package/build/collectors/ConsoleCollector.d.ts.map +1 -0
  13. package/build/collectors/ConsoleCollector.js +157 -0
  14. package/build/collectors/ConsoleCollector.js.map +1 -0
  15. package/build/collectors/NetworkCollector.d.ts +167 -0
  16. package/build/collectors/NetworkCollector.d.ts.map +1 -0
  17. package/build/collectors/NetworkCollector.js +265 -0
  18. package/build/collectors/NetworkCollector.js.map +1 -0
  19. package/build/collectors/index.d.ts +13 -0
  20. package/build/collectors/index.d.ts.map +1 -0
  21. package/build/collectors/index.js +17 -0
  22. package/build/collectors/index.js.map +1 -0
  23. package/build/config/tool-profile.d.ts +30 -0
  24. package/build/config/tool-profile.d.ts.map +1 -0
  25. package/build/config/tool-profile.js +138 -0
  26. package/build/config/tool-profile.js.map +1 -0
  27. package/build/connection/adapters.d.ts +3 -0
  28. package/build/connection/adapters.d.ts.map +1 -0
  29. package/build/connection/adapters.js +134 -0
  30. package/build/connection/adapters.js.map +1 -0
  31. package/build/connection/errors.d.ts +34 -0
  32. package/build/connection/errors.d.ts.map +1 -0
  33. package/build/connection/errors.js +101 -0
  34. package/build/connection/errors.js.map +1 -0
  35. package/build/connection/health-probe.d.ts +4 -0
  36. package/build/connection/health-probe.d.ts.map +1 -0
  37. package/build/connection/health-probe.js +60 -0
  38. package/build/connection/health-probe.js.map +1 -0
  39. package/build/connection/index.d.ts +6 -0
  40. package/build/connection/index.d.ts.map +1 -0
  41. package/build/connection/index.js +6 -0
  42. package/build/connection/index.js.map +1 -0
  43. package/build/connection/manager.d.ts +19 -0
  44. package/build/connection/manager.d.ts.map +1 -0
  45. package/build/connection/manager.js +227 -0
  46. package/build/connection/manager.js.map +1 -0
  47. package/build/connection/resolver.d.ts +3 -0
  48. package/build/connection/resolver.d.ts.map +1 -0
  49. package/build/connection/resolver.js +99 -0
  50. package/build/connection/resolver.js.map +1 -0
  51. package/build/connection/types.d.ts +95 -0
  52. package/build/connection/types.d.ts.map +1 -0
  53. package/build/connection/types.js +16 -0
  54. package/build/connection/types.js.map +1 -0
  55. package/build/core/assertion.d.ts +22 -0
  56. package/build/core/assertion.d.ts.map +1 -0
  57. package/build/core/assertion.js +318 -0
  58. package/build/core/assertion.js.map +1 -0
  59. package/build/core/connection.d.ts +37 -0
  60. package/build/core/connection.d.ts.map +1 -0
  61. package/build/core/connection.js +745 -0
  62. package/build/core/connection.js.map +1 -0
  63. package/build/core/index.d.ts +13 -0
  64. package/build/core/index.d.ts.map +1 -0
  65. package/build/core/index.js +19 -0
  66. package/build/core/index.js.map +1 -0
  67. package/build/core/interaction.d.ts +22 -0
  68. package/build/core/interaction.d.ts.map +1 -0
  69. package/build/core/interaction.js +185 -0
  70. package/build/core/interaction.js.map +1 -0
  71. package/build/core/navigation.d.ts +26 -0
  72. package/build/core/navigation.d.ts.map +1 -0
  73. package/build/core/navigation.js +210 -0
  74. package/build/core/navigation.js.map +1 -0
  75. package/build/core/query.d.ts +14 -0
  76. package/build/core/query.d.ts.map +1 -0
  77. package/build/core/query.js +191 -0
  78. package/build/core/query.js.map +1 -0
  79. package/build/core/screenshot.d.ts +10 -0
  80. package/build/core/screenshot.d.ts.map +1 -0
  81. package/build/core/screenshot.js +93 -0
  82. package/build/core/screenshot.js.map +1 -0
  83. package/build/core/snapshot.d.ts +17 -0
  84. package/build/core/snapshot.d.ts.map +1 -0
  85. package/build/core/snapshot.js +225 -0
  86. package/build/core/snapshot.js.map +1 -0
  87. package/build/core/types.d.ts +250 -0
  88. package/build/core/types.d.ts.map +1 -0
  89. package/build/core/types.js +6 -0
  90. package/build/core/types.js.map +1 -0
  91. package/build/formatters/consoleFormatter.d.ts +50 -0
  92. package/build/formatters/consoleFormatter.d.ts.map +1 -0
  93. package/build/formatters/consoleFormatter.js +116 -0
  94. package/build/formatters/consoleFormatter.js.map +1 -0
  95. package/build/formatters/snapshotFormatter.d.ts +41 -0
  96. package/build/formatters/snapshotFormatter.d.ts.map +1 -0
  97. package/build/formatters/snapshotFormatter.js +156 -0
  98. package/build/formatters/snapshotFormatter.js.map +1 -0
  99. package/build/index.d.ts +11 -0
  100. package/build/index.d.ts.map +1 -0
  101. package/build/index.js +45 -9
  102. package/build/index.js.map +1 -0
  103. package/build/server.d.ts +7 -0
  104. package/build/server.d.ts.map +1 -0
  105. package/build/server.js +88 -32
  106. package/build/server.js.map +1 -0
  107. package/build/tools/ToolDefinition.d.ts +265 -0
  108. package/build/tools/ToolDefinition.d.ts.map +1 -0
  109. package/build/tools/ToolDefinition.js +16 -7
  110. package/build/tools/ToolDefinition.js.map +1 -0
  111. package/build/tools/assert.d.ts +17 -0
  112. package/build/tools/assert.d.ts.map +1 -0
  113. package/build/tools/assert.js +63 -103
  114. package/build/tools/assert.js.map +1 -0
  115. package/build/tools/connection.d.ts +13 -0
  116. package/build/tools/connection.d.ts.map +1 -0
  117. package/build/tools/connection.js +338 -611
  118. package/build/tools/connection.js.map +1 -0
  119. package/build/tools/console.d.ts +20 -0
  120. package/build/tools/console.d.ts.map +1 -0
  121. package/build/tools/console.js +162 -152
  122. package/build/tools/console.js.map +1 -0
  123. package/build/tools/diagnose.d.ts +22 -0
  124. package/build/tools/diagnose.d.ts.map +1 -0
  125. package/build/tools/diagnose.js +406 -13
  126. package/build/tools/diagnose.js.map +1 -0
  127. package/build/tools/index.d.ts +6 -0
  128. package/build/tools/index.d.ts.map +1 -0
  129. package/build/tools/index.js +3 -77
  130. package/build/tools/index.js.map +1 -0
  131. package/build/tools/input.d.ts +21 -0
  132. package/build/tools/input.d.ts.map +1 -0
  133. package/build/tools/input.js +73 -139
  134. package/build/tools/input.js.map +1 -0
  135. package/build/tools/navigate.d.ts +21 -0
  136. package/build/tools/navigate.d.ts.map +1 -0
  137. package/build/tools/navigate.js +63 -126
  138. package/build/tools/navigate.js.map +1 -0
  139. package/build/tools/network.d.ts +21 -0
  140. package/build/tools/network.d.ts.map +1 -0
  141. package/build/tools/network.js +214 -1044
  142. package/build/tools/network.js.map +1 -0
  143. package/build/tools/page.d.ts +13 -0
  144. package/build/tools/page.d.ts.map +1 -0
  145. package/build/tools/page.js +6 -3
  146. package/build/tools/page.js.map +1 -0
  147. package/build/tools/screenshot.d.ts +9 -0
  148. package/build/tools/screenshot.d.ts.map +1 -0
  149. package/build/tools/screenshot.js +3 -1
  150. package/build/tools/screenshot.js.map +1 -0
  151. package/build/tools/script.d.ts +6 -0
  152. package/build/tools/script.d.ts.map +1 -0
  153. package/build/tools/script.js +92 -0
  154. package/build/tools/script.js.map +1 -0
  155. package/build/tools/snapshot.d.ts +9 -0
  156. package/build/tools/snapshot.d.ts.map +1 -0
  157. package/build/tools/snapshot.js +78 -12
  158. package/build/tools/snapshot.js.map +1 -0
  159. package/build/tools/tools.d.ts +15 -0
  160. package/build/tools/tools.d.ts.map +1 -0
  161. package/build/tools/tools.js +63 -0
  162. package/build/tools/tools.js.map +1 -0
  163. package/build/tools.d.ts +431 -0
  164. package/build/tools.d.ts.map +1 -0
  165. package/build/tools.js +258 -118
  166. package/build/tools.js.map +1 -0
  167. package/build/types/errors.d.ts +189 -0
  168. package/build/types/errors.d.ts.map +1 -0
  169. package/build/types/errors.js +257 -0
  170. package/build/types/errors.js.map +1 -0
  171. package/build/utils/error.d.ts +6 -0
  172. package/build/utils/error.d.ts.map +1 -0
  173. package/build/utils/error.js +11 -0
  174. package/build/utils/error.js.map +1 -0
  175. package/build/utils/idGenerator.d.ts +21 -0
  176. package/build/utils/idGenerator.d.ts.map +1 -0
  177. package/build/utils/idGenerator.js +23 -0
  178. package/build/utils/idGenerator.js.map +1 -0
  179. package/build/version.d.ts +7 -0
  180. package/build/version.d.ts.map +1 -0
  181. package/build/version.js +10 -0
  182. package/build/version.js.map +1 -0
  183. package/package.json +31 -9
@@ -1,1111 +1,281 @@
1
1
  /**
2
2
  * 网络请求监听工具
3
- * 通过拦截 wx.request, wx.uploadFile, wx.downloadFile 实现网络监控
3
+ * 采用两阶段查询:list -> get detail
4
4
  */
5
+ /* eslint-disable @typescript-eslint/ban-ts-comment -- wx 运行时对象在 evaluate 上下文中动态注入,需保持现有注释抑制。 */
5
6
  import { z } from 'zod';
6
- import { defineTool } from './ToolDefinition.js';
7
- /**
8
- * 创建请求拦截器函数
9
- * 注意: 这个函数会被序列化后在小程序环境执行,不能使用闭包变量
10
- * 保持函数简单,只记录信息然后调用原始方法
11
- */
12
- function createRequestInterceptor() {
13
- return function (options) {
14
- // 初始化全局存储
15
- // 关键修复: 在小程序环境中直接访问 wx 对象,不通过 globalThis
16
- // wx 是小程序提供的全局对象,直接可用
17
- // @ts-ignore - wx is available in WeChat miniprogram environment
18
- const wxObj = (typeof wx !== 'undefined' ? wx : null);
19
- if (!wxObj) {
20
- // wx 对象不存在,无法记录,直接调用原始方法
21
- return this.origin(options);
7
+ import { defineTool, ToolCategory } from './ToolDefinition.js';
8
+ function sanitizeNetworkRequests(logs) {
9
+ const deduped = new Map();
10
+ for (const request of logs) {
11
+ if (!request || typeof request !== 'object') {
12
+ continue;
22
13
  }
23
- if (!wxObj.__networkLogs) {
24
- wxObj.__networkLogs = [];
14
+ if (!request.id || request.id === 'N/A') {
15
+ continue;
25
16
  }
26
- const requestId = 'req_' + Date.now() + '_' + Math.random().toString(36).substring(2, 9);
27
- const startTime = Date.now();
28
- // 包装 success 回调
29
- const originalSuccess = options.success;
30
- options.success = function (res) {
31
- wxObj.__networkLogs.push({
32
- id: requestId,
33
- type: 'request',
34
- url: options.url,
35
- method: options.method || 'GET',
36
- headers: options.header,
37
- data: options.data,
38
- statusCode: res.statusCode,
39
- response: res.data,
40
- duration: Date.now() - startTime,
41
- timestamp: new Date().toISOString(),
42
- success: true
43
- });
44
- if (originalSuccess)
45
- originalSuccess(res);
46
- };
47
- // 包装 fail 回调
48
- const originalFail = options.fail;
49
- options.fail = function (err) {
50
- wxObj.__networkLogs.push({
51
- id: requestId,
52
- type: 'request',
53
- url: options.url,
54
- method: options.method || 'GET',
55
- headers: options.header,
56
- data: options.data,
57
- error: err.errMsg || String(err),
58
- duration: Date.now() - startTime,
59
- timestamp: new Date().toISOString(),
60
- success: false
61
- });
62
- if (originalFail)
63
- originalFail(err);
64
- };
65
- // 调用原始方法
66
- return this.origin(options);
67
- };
68
- }
69
- /**
70
- * 创建 uploadFile 拦截器函数
71
- */
72
- function createUploadFileInterceptor() {
73
- return function (options) {
74
- // @ts-ignore - wx is available in WeChat miniprogram environment
75
- const wxObj = (typeof wx !== 'undefined' ? wx : null);
76
- if (!wxObj) {
77
- return this.origin(options);
17
+ if (!request.url || request.url === 'undefined') {
18
+ continue;
78
19
  }
79
- if (!wxObj.__networkLogs) {
80
- wxObj.__networkLogs = [];
20
+ if (request.type !== 'request' && request.type !== 'uploadFile' && request.type !== 'downloadFile') {
21
+ continue;
81
22
  }
82
- const requestId = 'req_' + Date.now() + '_' + Math.random().toString(36).substring(2, 9);
83
- const startTime = Date.now();
84
- const originalSuccess = options.success;
85
- options.success = function (res) {
86
- wxObj.__networkLogs.push({
87
- id: requestId,
88
- type: 'uploadFile',
89
- url: options.url,
90
- headers: options.header,
91
- data: {
92
- filePath: options.filePath,
93
- name: options.name,
94
- formData: options.formData
95
- },
96
- statusCode: res.statusCode,
97
- response: res.data,
98
- duration: Date.now() - startTime,
99
- timestamp: new Date().toISOString(),
100
- success: true
101
- });
102
- if (originalSuccess)
103
- originalSuccess(res);
104
- };
105
- const originalFail = options.fail;
106
- options.fail = function (err) {
107
- wxObj.__networkLogs.push({
108
- id: requestId,
109
- type: 'uploadFile',
110
- url: options.url,
111
- headers: options.header,
112
- data: {
113
- filePath: options.filePath,
114
- name: options.name,
115
- formData: options.formData
116
- },
117
- error: err.errMsg || String(err),
118
- duration: Date.now() - startTime,
119
- timestamp: new Date().toISOString(),
120
- success: false
121
- });
122
- if (originalFail)
123
- originalFail(err);
124
- };
125
- return this.origin(options);
126
- };
23
+ deduped.set(request.id, request);
24
+ }
25
+ return Array.from(deduped.values()).sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
127
26
  }
128
- /**
129
- * 创建 downloadFile 拦截器函数
130
- */
131
- function createDownloadFileInterceptor() {
132
- return function (options) {
133
- // @ts-ignore - wx is available in WeChat miniprogram environment
134
- const wxObj = (typeof wx !== 'undefined' ? wx : null);
135
- if (!wxObj) {
136
- return this.origin(options);
137
- }
138
- if (!wxObj.__networkLogs) {
139
- wxObj.__networkLogs = [];
140
- }
141
- const requestId = 'req_' + Date.now() + '_' + Math.random().toString(36).substring(2, 9);
142
- const startTime = Date.now();
143
- const originalSuccess = options.success;
144
- options.success = function (res) {
145
- wxObj.__networkLogs.push({
146
- id: requestId,
147
- type: 'downloadFile',
148
- url: options.url,
149
- headers: options.header,
150
- statusCode: res.statusCode,
151
- response: {
152
- tempFilePath: res.tempFilePath,
153
- filePath: res.filePath
154
- },
155
- duration: Date.now() - startTime,
156
- timestamp: new Date().toISOString(),
157
- success: true
158
- });
159
- if (originalSuccess)
160
- originalSuccess(res);
161
- };
162
- const originalFail = options.fail;
163
- options.fail = function (err) {
164
- wxObj.__networkLogs.push({
165
- id: requestId,
166
- type: 'downloadFile',
167
- url: options.url,
168
- headers: options.header,
169
- error: err.errMsg || String(err),
170
- duration: Date.now() - startTime,
171
- timestamp: new Date().toISOString(),
172
- success: false
173
- });
174
- if (originalFail)
175
- originalFail(err);
176
- };
177
- return this.origin(options);
27
+ function toRequestStatus(request) {
28
+ if (request.pending === true) {
29
+ return 'pending';
30
+ }
31
+ return request.success ? 'success' : 'failed';
32
+ }
33
+ function toSummary(request) {
34
+ return {
35
+ reqid: request.id,
36
+ type: request.type,
37
+ method: request.method ?? 'GET',
38
+ url: request.url,
39
+ status: toRequestStatus(request),
40
+ statusCode: request.statusCode ?? null,
41
+ durationMs: request.duration ?? null,
42
+ timestamp: request.timestamp,
178
43
  };
179
44
  }
45
+ function ensureConnected(context) {
46
+ if (!context.miniProgram) {
47
+ throw new Error('请先连接到微信开发者工具');
48
+ }
49
+ }
50
+ const requestTypeSchema = z.enum(['request', 'uploadFile', 'downloadFile']);
51
+ const listNetworkRequestsSchema = z.object({
52
+ pageSize: z.number().int().positive().optional().default(50).describe('每页条数'),
53
+ pageIdx: z.number().int().min(0).optional().default(0).describe('页码(从 0 开始)'),
54
+ resourceTypes: z.array(requestTypeSchema).optional().describe('按请求类型过滤'),
55
+ includePreservedRequests: z.boolean().optional().default(false).describe('是否包含历史请求(最近 3 次会话)'),
56
+ urlPattern: z.string().optional().describe('URL 匹配模式(支持正则)'),
57
+ successOnly: z.boolean().optional().default(false).describe('仅返回成功请求'),
58
+ failedOnly: z.boolean().optional().default(false).describe('仅返回失败请求'),
59
+ since: z.string().optional().describe('仅返回指定时间后的请求(ISO 8601)'),
60
+ });
61
+ const getNetworkRequestSchema = z.object({
62
+ reqid: z.string().min(1).describe('请求 ID(从 list_network_requests 获取)'),
63
+ });
64
+ const stopNetworkMonitoringSchema = z.object({
65
+ clearLogs: z.boolean().optional().default(false).describe('是否同时清空已收集的日志'),
66
+ });
67
+ const clearNetworkRequestsSchema = z.object({
68
+ clearRemote: z.boolean().optional().default(true).describe('是否同时清空小程序端日志'),
69
+ });
70
+ // 注意: start_network_monitoring 已移除,监听在连接成功后自动启动
180
71
  /**
181
- * 启动网络监听工具
182
- *
183
- * 使用evaluate()直接在小程序环境注入拦截代码
184
- * 这种方式可以绕过Mpx等框架的API缓存问题
72
+ * 第一阶段:列表查询网络请求(短格式)
185
73
  */
186
- export const startNetworkMonitoringTool = defineTool({
187
- name: 'start_network_monitoring',
188
- description: '启动对微信小程序网络请求的监听,拦截 wx.request、wx.uploadFile、wx.downloadFile',
189
- schema: z.object({
190
- clearExisting: z.boolean().optional().default(false).describe('是否清除已有的网络请求记录'),
191
- }),
74
+ export const listNetworkRequestsTool = defineTool({
75
+ name: 'list_network_requests',
76
+ description: '列表查询网络请求(短格式,支持分页和过滤),用于获取 reqid 后再查询详情',
77
+ schema: listNetworkRequestsSchema,
192
78
  annotations: {
79
+ category: ToolCategory.NETWORK,
193
80
  audience: ['developers'],
194
81
  },
195
82
  handler: async (request, response, context) => {
196
- const { clearExisting } = request.params;
197
- if (!context.miniProgram) {
198
- throw new Error('请先连接到微信开发者工具');
83
+ ensureConnected(context);
84
+ const { pageSize, pageIdx, resourceTypes, includePreservedRequests, urlPattern, successOnly, failedOnly, since, } = request.params;
85
+ if (successOnly && failedOnly) {
86
+ throw new Error('successOnly 与 failedOnly 不能同时为 true');
199
87
  }
200
- if (context.networkStorage.isMonitoring) {
201
- response.appendResponseLine('网络监听已在运行中');
202
- response.appendResponseLine(`当前已记录 ${context.networkStorage.requests.length} 个网络请求`);
203
- return;
88
+ const syncedCount = await context.getNetworkCollector().syncFromRemote(true);
89
+ const allRequests = context.getNetworkCollector().getRequests({
90
+ includePreserved: includePreservedRequests,
91
+ });
92
+ let filteredRequests = sanitizeNetworkRequests(allRequests);
93
+ if (resourceTypes && resourceTypes.length > 0) {
94
+ const typeSet = new Set(resourceTypes);
95
+ filteredRequests = filteredRequests.filter(req => typeSet.has(req.type));
204
96
  }
205
- // 清除现有记录
206
- if (clearExisting) {
207
- context.networkStorage.requests = [];
97
+ if (urlPattern) {
98
+ try {
99
+ const regex = new RegExp(urlPattern);
100
+ filteredRequests = filteredRequests.filter(req => regex.test(req.url));
101
+ }
102
+ catch {
103
+ filteredRequests = filteredRequests.filter(req => req.url.includes(urlPattern));
104
+ }
208
105
  }
209
- try {
210
- // 使用evaluate()方式在小程序环境中直接注入拦截代码
211
- // 支持双模式:Mpx框架拦截器 + wx.request回退方案
212
- await context.miniProgram.evaluate(function (shouldClear) {
213
- // @ts-ignore - wx在小程序环境中可用
214
- if (typeof wx === 'undefined') {
215
- throw new Error('wx对象不可用');
216
- }
217
- // 初始化或清除存储
218
- // @ts-ignore
219
- if (!wx.__networkLogs || shouldClear) {
220
- // @ts-ignore
221
- wx.__networkLogs = [];
222
- }
223
- // 检查是否已经注入过拦截器
224
- // @ts-ignore
225
- if (wx.__networkInterceptorsInstalled && !shouldClear) {
226
- console.log('[MCP-DEBUG] 拦截器已安装,跳过重复安装');
227
- return; // 已安装,跳过
228
- }
229
- // 如果需要清除,先删除旧的标记
230
- if (shouldClear) {
231
- console.log('[MCP-DEBUG] 强制重装:清除旧的安装标记');
232
- // @ts-ignore
233
- delete wx.__networkInterceptorsInstalled;
234
- // 同时清空pending队列和config缓存
235
- // @ts-ignore
236
- wx.__pendingQueue = [];
237
- // @ts-ignore
238
- wx.__requestConfigMap = {};
239
- }
240
- // ===== 模式1:检测并使用Mpx框架拦截器 =====
241
- console.log('[MCP-DEBUG] 开始检测Mpx框架...');
242
- // @ts-ignore - getApp is available in WeChat miniprogram environment
243
- const app = getApp();
244
- console.log('[MCP-DEBUG] getApp() 结果:', {
245
- hasApp: !!app,
246
- appType: typeof app,
247
- hasXfetch: !!(app && app.$xfetch),
248
- xfetchType: app && app.$xfetch ? typeof app.$xfetch : 'undefined'
249
- });
250
- const hasMpxFetch = app &&
251
- app.$xfetch &&
252
- app.$xfetch.interceptors &&
253
- typeof app.$xfetch.interceptors.request.use === 'function';
254
- console.log('[MCP-DEBUG] Mpx检测结果:', {
255
- hasMpxFetch: hasMpxFetch,
256
- hasInterceptors: !!(app && app.$xfetch && app.$xfetch.interceptors),
257
- hasRequestUse: !!(app && app.$xfetch && app.$xfetch.interceptors && app.$xfetch.interceptors.request),
258
- hasResponseUse: !!(app && app.$xfetch && app.$xfetch.interceptors && app.$xfetch.interceptors.response)
259
- });
260
- if (hasMpxFetch) {
261
- console.log('[MCP] ✅ 检测到Mpx框架,使用getApp().$xfetch拦截器模式');
262
- console.log('[MCP] 📝 使用Pending队列方案解决业务拦截器改变响应结构的问题');
263
- // 初始化pending队列和config缓存
264
- // @ts-ignore
265
- if (!wx.__pendingQueue) {
266
- // @ts-ignore
267
- wx.__pendingQueue = [];
268
- }
269
- // @ts-ignore
270
- if (!wx.__requestConfigMap) {
271
- // @ts-ignore
272
- wx.__requestConfigMap = {};
273
- }
274
- // 如果需要重装,清空旧的Mpx拦截器handlers(防止累加)
275
- if (shouldClear) {
276
- console.log('[MCP-DEBUG] 准备清空handlers, shouldClear=', shouldClear);
277
- console.log('[MCP-DEBUG] request拦截器结构:', {
278
- hasInterceptors: !!app.$xfetch.interceptors.request,
279
- hasHandlers: !!app.$xfetch.interceptors.request.handlers,
280
- handlersType: typeof app.$xfetch.interceptors.request.handlers,
281
- handlersIsArray: Array.isArray(app.$xfetch.interceptors.request.handlers)
282
- });
283
- // @ts-ignore
284
- if (app.$xfetch.interceptors.request && app.$xfetch.interceptors.request.handlers) {
285
- // @ts-ignore
286
- app.$xfetch.interceptors.request.handlers = [];
287
- console.log('[MCP-DEBUG] ✅ 已清空旧的request拦截器handlers');
288
- }
289
- else {
290
- console.log('[MCP-DEBUG] ⚠️ request.handlers不存在或不是数组');
291
- }
292
- // @ts-ignore
293
- if (app.$xfetch.interceptors.response && app.$xfetch.interceptors.response.handlers) {
294
- // @ts-ignore
295
- app.$xfetch.interceptors.response.handlers = [];
296
- console.log('[MCP-DEBUG] ✅ 已清空旧的response拦截器handlers');
297
- }
298
- else {
299
- console.log('[MCP-DEBUG] ⚠️ response.handlers不存在或不是数组');
300
- }
301
- }
302
- // 请求拦截器 - 记录请求开始并缓存config
303
- // @ts-ignore
304
- getApp().$xfetch.interceptors.request.use(function (config) {
305
- const requestId = 'mpx_' + Date.now() + '_' + Math.random().toString(36).substring(2, 9);
306
- const startTime = Date.now();
307
- console.log('[MCP-DEBUG] 🔵 请求拦截器被触发:', {
308
- requestId: requestId,
309
- method: config.method,
310
- url: config.url,
311
- hasData: !!config.data,
312
- hasParams: !!config.params,
313
- timestamp: new Date().toISOString()
314
- });
315
- // 保存完整的config到缓存(因为响应拦截器可能拿不到requestConfig)
316
- // @ts-ignore
317
- wx.__requestConfigMap[requestId] = {
318
- url: config.url,
319
- method: config.method || 'GET',
320
- header: config.header || config.headers,
321
- data: config.data,
322
- params: config.params,
323
- timeout: config.timeout || 30000
324
- };
325
- // 添加到pending队列(FIFO)
326
- // @ts-ignore
327
- wx.__pendingQueue.push({
328
- id: requestId,
329
- url: config.url,
330
- method: config.method || 'GET',
331
- startTime: startTime
332
- });
333
- // 清理超时的pending请求(避免队列堆积)
334
- const timeout = config.timeout || 30000;
335
- // @ts-ignore
336
- wx.__pendingQueue = wx.__pendingQueue.filter((item) => Date.now() - item.startTime < timeout + 5000 // 额外5秒容错
337
- );
338
- // @ts-ignore - wx is available in WeChat miniprogram environment
339
- wx.__networkLogs.push({
340
- id: requestId,
341
- type: 'request',
342
- method: config.method || 'GET',
343
- url: config.url,
344
- headers: config.header || config.headers,
345
- data: config.data,
346
- params: config.params,
347
- timestamp: new Date(startTime).toISOString(),
348
- source: 'getApp().$xfetch',
349
- pending: true, // 标记为待完成状态
350
- success: undefined // 初始化success字段,避免状态判断问题
351
- });
352
- // @ts-ignore - wx在小程序环境可用
353
- console.log('[MCP-DEBUG] ✅ 请求已记录, pending队列:', wx.__pendingQueue.length, ', 日志数:', wx.__networkLogs.length);
354
- return config; // 必须返回config继续请求链
355
- });
356
- // 响应拦截器 - 使用Pending队列匹配请求/响应
357
- // @ts-ignore
358
- getApp().$xfetch.interceptors.response.use(function onSuccess(data) {
359
- try {
360
- // 注意: data可能只是业务数据(如{goodsList, tripId}),而不是完整的response对象
361
- // 因为业务拦截器(commonResInterceptor)改变了响应结构
362
- console.log('[MCP-DEBUG] 🟢 响应拦截器被触发(成功)');
363
- console.log('[MCP-DEBUG] 🔍 响应数据类型:', typeof data, ', 键:', Object.keys(data || {}));
364
- // 从Pending队列获取最早的请求(FIFO匹配)
365
- // @ts-ignore
366
- const requestInfo = wx.__pendingQueue.shift();
367
- if (!requestInfo) {
368
- console.log('[MCP-DEBUG] ⚠️ Pending队列为空,无法匹配请求');
369
- return data;
370
- }
371
- const duration = Date.now() - requestInfo.startTime;
372
- console.log('[MCP-DEBUG] 📦 从队列取出请求:', {
373
- requestId: requestInfo.id,
374
- url: requestInfo.url,
375
- method: requestInfo.method,
376
- duration: duration + 'ms'
377
- });
378
- // 从缓存获取完整的请求配置
379
- // @ts-ignore
380
- const savedConfig = wx.__requestConfigMap[requestInfo.id];
381
- if (!savedConfig) {
382
- console.log('[MCP-DEBUG] ⚠️ 未找到缓存的config');
383
- }
384
- // @ts-ignore
385
- // 找到对应的日志记录并更新
386
- let logIndex = wx.__networkLogs.findIndex((log) => log.id === requestInfo.id);
387
- // 增强:如果按ID找不到,尝试按URL和时间窗口匹配(fallback策略)
388
- if (logIndex === -1) {
389
- console.log('[MCP-DEBUG] ⚠️ 按ID未找到日志,尝试URL匹配...');
390
- // @ts-ignore
391
- logIndex = wx.__networkLogs.findIndex((log) => log.url === requestInfo.url &&
392
- log.pending === true &&
393
- Math.abs(new Date(log.timestamp).getTime() - requestInfo.startTime) < 10000 // 10秒窗口
394
- );
395
- if (logIndex !== -1) {
396
- console.log('[MCP-DEBUG] ✅ 通过URL匹配找到日志, 索引:', logIndex);
397
- }
398
- }
399
- if (logIndex !== -1) {
400
- // @ts-ignore
401
- const existingLog = wx.__networkLogs[logIndex];
402
- // @ts-ignore
403
- wx.__networkLogs[logIndex] = {
404
- ...existingLog,
405
- statusCode: 200, // 能到这里说明成功
406
- response: data, // 只能拿到业务数据
407
- duration: duration,
408
- completedAt: new Date().toISOString(),
409
- pending: false,
410
- success: true
411
- };
412
- console.log('[MCP-DEBUG] ✅ 请求记录已更新 (合并响应), 索引:', logIndex);
413
- }
414
- else {
415
- console.log('[MCP-DEBUG] ❌ 完全未找到匹配的日志记录, requestId:', requestInfo.id, ', url:', requestInfo.url);
416
- }
417
- // 清理config缓存
418
- // @ts-ignore
419
- if (savedConfig) {
420
- // @ts-ignore
421
- delete wx.__requestConfigMap[requestInfo.id];
422
- }
423
- // @ts-ignore - wx在小程序环境可用
424
- console.log('[MCP-DEBUG] 📊 状态 - 日志:', wx.__networkLogs.length, ', pending:', wx.__pendingQueue.length, ', config缓存:', Object.keys(wx.__requestConfigMap || {}).length);
425
- return data; // 必须返回data继续拦截器链
426
- }
427
- catch (error) {
428
- console.log('[MCP-DEBUG] ❌ 响应拦截器异常:', error);
429
- return data; // 即使出错也要返回data,不能中断业务逻辑
430
- }
431
- }, function onError(error) {
432
- try {
433
- console.log('[MCP-DEBUG] 🔴 响应拦截器被触发(错误)');
434
- console.log('[MCP-DEBUG] 🔍 错误对象:', error);
435
- // 从Pending队列获取最早的请求(FIFO匹配)
436
- // @ts-ignore
437
- const requestInfo = wx.__pendingQueue.shift();
438
- if (!requestInfo) {
439
- console.log('[MCP-DEBUG] ⚠️ Pending队列为空,无法匹配错误请求');
440
- return Promise.reject(error);
441
- }
442
- const duration = Date.now() - requestInfo.startTime;
443
- console.log('[MCP-DEBUG] 📦 从队列取出请求(错误):', {
444
- requestId: requestInfo.id,
445
- url: requestInfo.url,
446
- error: error.errMsg || error.msg || error.message || String(error),
447
- duration: duration + 'ms'
448
- });
449
- // @ts-ignore
450
- // 找到对应的日志记录并更新
451
- let logIndex = wx.__networkLogs.findIndex((log) => log.id === requestInfo.id);
452
- // 增强:如果按ID找不到,尝试按URL和时间窗口匹配(fallback策略)
453
- if (logIndex === -1) {
454
- console.log('[MCP-DEBUG] ⚠️ 按ID未找到日志(错误场景),尝试URL匹配...');
455
- // @ts-ignore
456
- logIndex = wx.__networkLogs.findIndex((log) => log.url === requestInfo.url &&
457
- log.pending === true &&
458
- Math.abs(new Date(log.timestamp).getTime() - requestInfo.startTime) < 10000 // 10秒窗口
459
- );
460
- if (logIndex !== -1) {
461
- console.log('[MCP-DEBUG] ✅ 通过URL匹配找到日志(错误场景), 索引:', logIndex);
462
- }
463
- }
464
- if (logIndex !== -1) {
465
- // @ts-ignore
466
- const existingLog = wx.__networkLogs[logIndex];
467
- // @ts-ignore
468
- wx.__networkLogs[logIndex] = {
469
- ...existingLog,
470
- error: error.errMsg || error.msg || error.message || String(error),
471
- statusCode: error.status || error.statusCode,
472
- duration: duration,
473
- completedAt: new Date().toISOString(),
474
- pending: false,
475
- success: false
476
- };
477
- console.log('[MCP-DEBUG] ✅ 请求记录已更新 (合并错误), 索引:', logIndex);
478
- }
479
- else {
480
- console.log('[MCP-DEBUG] ❌ 完全未找到匹配的日志记录(错误场景), requestId:', requestInfo.id, ', url:', requestInfo.url);
481
- }
482
- // 清理config缓存
483
- // @ts-ignore
484
- if (wx.__requestConfigMap && wx.__requestConfigMap[requestInfo.id]) {
485
- // @ts-ignore
486
- delete wx.__requestConfigMap[requestInfo.id];
487
- }
488
- // @ts-ignore - wx在小程序环境可用
489
- console.log('[MCP-DEBUG] 📊 状态 - 日志:', wx.__networkLogs.length, ', pending:', wx.__pendingQueue.length);
490
- return Promise.reject(error); // 保持错误传播
491
- }
492
- catch (innerError) {
493
- console.log('[MCP-DEBUG] ❌ 错误拦截器异常:', innerError);
494
- return Promise.reject(error); // 即使出错也要传播原始错误,不能中断业务逻辑
495
- }
496
- });
497
- // @ts-ignore - wx is available in WeChat miniprogram environment
498
- wx.__networkInterceptorsInstalled = 'mpx';
499
- console.log('[MCP] ✅ Mpx拦截器安装完成');
500
- // @ts-ignore - wx is available in WeChat miniprogram environment
501
- console.log('[MCP-DEBUG] 拦截器已标记为已安装: wx.__networkInterceptorsInstalled =', wx.__networkInterceptorsInstalled);
502
- }
503
- else {
504
- console.log('[MCP] ⚠️ 未检测到Mpx框架或$xfetch不可用');
505
- }
506
- // ===== 模式2:wx.request回退方案(用于非Mpx框架或直接调用wx API的场景) =====
507
- if (!hasMpxFetch) {
508
- console.log('[MCP] ⚠️ 未检测到Mpx框架,使用wx.request拦截模式');
509
- }
510
- else {
511
- console.log('[MCP-DEBUG] Mpx模式下,同时安装wx.request回退拦截器(双保险)');
512
- }
513
- // 保存原始方法引用(通过getter获取)
514
- // @ts-ignore
515
- const _originalRequest = wx.request;
516
- // @ts-ignore
517
- const _originalUploadFile = wx.uploadFile;
518
- // @ts-ignore
519
- const _originalDownloadFile = wx.downloadFile;
520
- console.log('[MCP-DEBUG] 原始方法类型:', {
521
- requestType: typeof _originalRequest,
522
- uploadFileType: typeof _originalUploadFile,
523
- downloadFileType: typeof _originalDownloadFile
524
- });
525
- // 拦截 wx.request
526
- // 关键:先删除getter属性,然后重新定义为普通属性
527
- // @ts-ignore
528
- delete wx.request;
529
- // @ts-ignore
530
- Object.defineProperty(wx, 'request', {
531
- configurable: true,
532
- enumerable: true,
533
- writable: true,
534
- value: function (options) {
535
- const requestId = 'req_' + Date.now() + '_' + Math.random().toString(36).substring(2, 9);
536
- const startTime = Date.now();
537
- console.log('[MCP-DEBUG] 🔵 wx.request 被调用:', {
538
- requestId: requestId,
539
- method: options.method || 'GET',
540
- url: options.url,
541
- hasData: !!options.data,
542
- timestamp: new Date().toISOString()
543
- });
544
- // 包装success回调
545
- const originalSuccess = options.success;
546
- options.success = function (res) {
547
- console.log('[MCP-DEBUG] 🟢 wx.request 成功回调:', {
548
- requestId: requestId,
549
- statusCode: res.statusCode,
550
- duration: Date.now() - startTime
551
- });
552
- // @ts-ignore
553
- wx.__networkLogs.push({
554
- id: requestId,
555
- type: 'request',
556
- url: options.url,
557
- method: options.method || 'GET',
558
- headers: options.header,
559
- data: options.data,
560
- statusCode: res.statusCode,
561
- response: res.data,
562
- duration: Date.now() - startTime,
563
- timestamp: new Date().toISOString(),
564
- source: 'wx.request',
565
- success: true
566
- });
567
- // @ts-ignore - wx is available in WeChat miniprogram environment
568
- console.log('[MCP-DEBUG] ✅ wx.request 已记录, 当前总数:', wx.__networkLogs.length);
569
- if (originalSuccess)
570
- originalSuccess.call(this, res);
571
- };
572
- // 包装fail回调
573
- const originalFail = options.fail;
574
- options.fail = function (err) {
575
- console.log('[MCP-DEBUG] 🔴 wx.request 失败回调:', {
576
- requestId: requestId,
577
- error: err.errMsg,
578
- duration: Date.now() - startTime
579
- });
580
- // @ts-ignore
581
- wx.__networkLogs.push({
582
- id: requestId,
583
- type: 'request',
584
- url: options.url,
585
- method: options.method || 'GET',
586
- headers: options.header,
587
- data: options.data,
588
- error: err.errMsg || String(err),
589
- duration: Date.now() - startTime,
590
- timestamp: new Date().toISOString(),
591
- source: 'wx.request',
592
- success: false
593
- });
594
- // @ts-ignore - wx is available in WeChat miniprogram environment
595
- console.log('[MCP-DEBUG] ✅ wx.request 错误已记录, 当前总数:', wx.__networkLogs.length);
596
- if (originalFail)
597
- originalFail.call(this, err);
598
- };
599
- // 调用原始方法
600
- return _originalRequest.call(this, options);
601
- }
602
- });
603
- console.log('[MCP-DEBUG] ✅ wx.request 拦截器已安装');
604
- // 拦截 wx.uploadFile
605
- // 关键:先删除getter属性
606
- // @ts-ignore
607
- delete wx.uploadFile;
608
- // @ts-ignore
609
- Object.defineProperty(wx, 'uploadFile', {
610
- configurable: true,
611
- enumerable: true,
612
- writable: true,
613
- value: function (options) {
614
- const requestId = 'req_' + Date.now() + '_' + Math.random().toString(36).substring(2, 9);
615
- const startTime = Date.now();
616
- const originalSuccess = options.success;
617
- options.success = function (res) {
618
- // @ts-ignore
619
- wx.__networkLogs.push({
620
- id: requestId,
621
- type: 'uploadFile',
622
- url: options.url,
623
- headers: options.header,
624
- data: {
625
- filePath: options.filePath,
626
- name: options.name,
627
- formData: options.formData
628
- },
629
- statusCode: res.statusCode,
630
- response: res.data,
631
- duration: Date.now() - startTime,
632
- timestamp: new Date().toISOString(),
633
- source: 'wx.uploadFile',
634
- success: true
635
- });
636
- if (originalSuccess)
637
- originalSuccess.call(this, res);
638
- };
639
- const originalFail = options.fail;
640
- options.fail = function (err) {
641
- // @ts-ignore
642
- wx.__networkLogs.push({
643
- id: requestId,
644
- type: 'uploadFile',
645
- url: options.url,
646
- headers: options.header,
647
- data: {
648
- filePath: options.filePath,
649
- name: options.name,
650
- formData: options.formData
651
- },
652
- error: err.errMsg || String(err),
653
- duration: Date.now() - startTime,
654
- timestamp: new Date().toISOString(),
655
- source: 'wx.uploadFile',
656
- success: false
657
- });
658
- if (originalFail)
659
- originalFail.call(this, err);
660
- };
661
- return _originalUploadFile.call(this, options);
662
- }
663
- });
664
- // 拦截 wx.downloadFile
665
- // 关键:先删除getter属性
666
- // @ts-ignore
667
- delete wx.downloadFile;
668
- // @ts-ignore
669
- Object.defineProperty(wx, 'downloadFile', {
670
- configurable: true,
671
- enumerable: true,
672
- writable: true,
673
- value: function (options) {
674
- const requestId = 'req_' + Date.now() + '_' + Math.random().toString(36).substring(2, 9);
675
- const startTime = Date.now();
676
- const originalSuccess = options.success;
677
- options.success = function (res) {
678
- // @ts-ignore
679
- wx.__networkLogs.push({
680
- id: requestId,
681
- type: 'downloadFile',
682
- url: options.url,
683
- headers: options.header,
684
- statusCode: res.statusCode,
685
- response: {
686
- tempFilePath: res.tempFilePath,
687
- filePath: res.filePath
688
- },
689
- duration: Date.now() - startTime,
690
- timestamp: new Date().toISOString(),
691
- source: 'wx.downloadFile',
692
- success: true
693
- });
694
- if (originalSuccess)
695
- originalSuccess.call(this, res);
696
- };
697
- const originalFail = options.fail;
698
- options.fail = function (err) {
699
- // @ts-ignore
700
- wx.__networkLogs.push({
701
- id: requestId,
702
- type: 'downloadFile',
703
- url: options.url,
704
- headers: options.header,
705
- error: err.errMsg || String(err),
706
- duration: Date.now() - startTime,
707
- timestamp: new Date().toISOString(),
708
- source: 'wx.downloadFile',
709
- success: false
710
- });
711
- if (originalFail)
712
- originalFail.call(this, err);
713
- };
714
- return _originalDownloadFile.call(this, options);
715
- }
716
- });
717
- // 标记拦截器已安装
718
- // @ts-ignore
719
- wx.__networkInterceptorsInstalled = true;
720
- }, clearExisting);
721
- // 设置监听状态
722
- context.networkStorage.isMonitoring = true;
723
- context.networkStorage.startTime = new Date().toISOString();
724
- response.appendResponseLine('✅ 网络监听已启动(使用增强型拦截)');
725
- response.appendResponseLine(`监听开始时间: ${context.networkStorage.startTime}`);
726
- response.appendResponseLine(`清除历史记录: ${clearExisting ? '是' : '否'}`);
727
- response.appendResponseLine('');
728
- response.appendResponseLine('已拦截以下方法:');
729
- response.appendResponseLine(' - wx.request');
730
- response.appendResponseLine(' - wx.uploadFile');
731
- response.appendResponseLine(' - wx.downloadFile');
732
- response.appendResponseLine('');
733
- response.appendResponseLine('💡 使用 evaluate() 方式注入,可绕过 Mpx 等框架限制');
734
- response.appendResponseLine(' 所有网络请求都将被捕获,使用 get_network_requests 查看');
106
+ if (successOnly) {
107
+ filteredRequests = filteredRequests.filter(req => req.success === true);
735
108
  }
736
- catch (error) {
737
- const errorMessage = error instanceof Error ? error.message : String(error);
738
- throw new Error(`启动网络监听失败: ${errorMessage}`);
109
+ if (failedOnly) {
110
+ filteredRequests = filteredRequests.filter(req => req.success === false);
739
111
  }
740
- },
741
- });
742
- /**
743
- * 停止网络监听工具
744
- *
745
- * 注意:使用evaluate()注入的拦截器无法完全恢复
746
- * 只能清除标记,实际拦截器会继续工作
747
- */
748
- export const stopNetworkMonitoringTool = defineTool({
749
- name: 'stop_network_monitoring',
750
- description: '停止对微信小程序网络请求的监听,恢复原始的网络方法',
751
- schema: z.object({}),
752
- annotations: {
753
- audience: ['developers'],
754
- },
755
- handler: async (request, response, context) => {
756
- if (!context.miniProgram) {
757
- throw new Error('请先连接到微信开发者工具');
112
+ if (since) {
113
+ const sinceTime = new Date(since).getTime();
114
+ if (Number.isNaN(sinceTime)) {
115
+ throw new Error('since 参数必须是有效的 ISO 8601 时间字符串');
116
+ }
117
+ filteredRequests = filteredRequests.filter(req => new Date(req.timestamp).getTime() >= sinceTime);
758
118
  }
759
- if (!context.networkStorage.isMonitoring) {
760
- response.appendResponseLine('网络监听未在运行');
119
+ const total = filteredRequests.length;
120
+ const start = pageIdx * pageSize;
121
+ const end = Math.min(start + pageSize, total);
122
+ const pageRequests = filteredRequests.slice(start, end);
123
+ response.appendResponseLine('## Network Requests (List View)');
124
+ response.appendResponseLine(`监听状态: ${context.networkStorage.isMonitoring ? '运行中' : '已停止'}`);
125
+ response.appendResponseLine(`监听开始时间: ${context.networkStorage.startTime || '未设置'}`);
126
+ response.appendResponseLine(`本次同步新增: ${syncedCount}`);
127
+ response.appendResponseLine(`总数: ${total} 条`);
128
+ response.appendResponseLine(`显示: ${total === 0 ? 0 : start + 1}-${end}`);
129
+ response.appendResponseLine('');
130
+ if (pageRequests.length === 0) {
131
+ response.appendResponseLine('<no requests found>');
761
132
  return;
762
133
  }
763
- try {
764
- // 从小程序环境读取最终的请求数据并清除标记
765
- const result = await context.miniProgram.evaluate(function () {
766
- // @ts-ignore
767
- const wxObj = typeof wx !== 'undefined' ? wx : null;
768
- if (!wxObj) {
769
- return { logs: [], success: false };
770
- }
771
- const logs = wxObj.__networkLogs || [];
772
- // 清除安装标记(允许重新安装)
773
- // 注意:实际的拦截器无法恢复,因为我们使用了Object.defineProperty
774
- // 这是evaluate()方式的一个限制,但好处是可以绕过框架缓存
775
- wxObj.__networkInterceptorsInstalled = false;
776
- return { logs, success: true };
777
- });
778
- if (!result.success) {
779
- throw new Error('无法访问wx对象');
780
- }
781
- const logs = result.logs;
782
- // 更新监听状态
783
- context.networkStorage.isMonitoring = false;
784
- response.appendResponseLine('✅ 网络监听已停止');
785
- response.appendResponseLine(`监听期间收集到 ${logs.length} 个网络请求`);
786
- // 统计各类型请求数量
787
- const stats = logs.reduce((acc, req) => {
788
- acc[req.type] = (acc[req.type] || 0) + 1;
789
- return acc;
790
- }, {});
791
- response.appendResponseLine('');
792
- response.appendResponseLine('请求类型统计:');
793
- if (stats.request)
794
- response.appendResponseLine(` - request: ${stats.request}`);
795
- if (stats.uploadFile)
796
- response.appendResponseLine(` - uploadFile: ${stats.uploadFile}`);
797
- if (stats.downloadFile)
798
- response.appendResponseLine(` - downloadFile: ${stats.downloadFile}`);
799
- response.appendResponseLine('');
800
- response.appendResponseLine('⚠️ 注意: 拦截器将继续工作(evaluate方式的特性)');
801
- response.appendResponseLine(' 使用 clear_network_requests 清除数据');
802
- response.appendResponseLine(' 使用 start_network_monitoring 重新开始记录');
803
- }
804
- catch (error) {
805
- const errorMessage = error instanceof Error ? error.message : String(error);
806
- throw new Error(`停止网络监听失败: ${errorMessage}`);
134
+ for (const item of pageRequests.map(toSummary)) {
135
+ response.appendResponseLine(`reqid=${item.reqid} [${item.type}] ${item.method} ${item.url} status=${item.status}`);
807
136
  }
137
+ response.appendResponseLine('');
138
+ response.appendResponseLine('提示: 使用 get_network_request 结合 reqid 查看完整详情');
808
139
  },
809
140
  });
810
141
  /**
811
- * 获取网络请求工具
142
+ * 第二阶段:按 reqid 查询请求详情
812
143
  */
813
- export const getNetworkRequestsTool = defineTool({
814
- name: 'get_network_requests',
815
- description: '获取收集到的网络请求记录,支持按类型、URL、状态过滤',
816
- schema: z.object({
817
- type: z.enum(['all', 'request', 'uploadFile', 'downloadFile']).optional().default('all').describe('请求类型过滤'),
818
- urlPattern: z.string().optional().describe('URL 匹配模式(支持正则表达式)'),
819
- successOnly: z.boolean().optional().default(false).describe('仅返回成功的请求'),
820
- limit: z.number().optional().default(50).describe('限制返回条数'),
821
- since: z.string().optional().describe('获取指定时间之后的记录,格式:ISO 8601'),
822
- }),
144
+ export const getNetworkRequestTool = defineTool({
145
+ name: 'get_network_request',
146
+ description: '通过 reqid 获取单条网络请求完整详情',
147
+ schema: getNetworkRequestSchema,
823
148
  annotations: {
149
+ category: ToolCategory.NETWORK,
824
150
  audience: ['developers'],
825
151
  },
826
152
  handler: async (request, response, context) => {
827
- const { type, urlPattern, successOnly, limit, since } = request.params;
828
- if (!context.miniProgram) {
829
- throw new Error('请先连接到微信开发者工具');
153
+ ensureConnected(context);
154
+ await context.getNetworkCollector().syncFromRemote(true);
155
+ const requests = sanitizeNetworkRequests(context.getNetworkCollector().getRequests({ includePreserved: true }));
156
+ const matched = requests.find(item => item.id === request.params.reqid);
157
+ if (!matched) {
158
+ throw new Error(`未找到 reqid=${request.params.reqid} 的请求,请先调用 list_network_requests 获取可用 reqid`);
830
159
  }
831
- if (!context.networkStorage) {
832
- throw new Error('网络存储未初始化');
160
+ response.appendResponseLine('## Network Request (Detail View)');
161
+ response.appendResponseLine(`ID: ${matched.id}`);
162
+ response.appendResponseLine(`类型: ${matched.type}`);
163
+ response.appendResponseLine(`URL: ${matched.url}`);
164
+ response.appendResponseLine(`方法: ${matched.method ?? 'GET'}`);
165
+ response.appendResponseLine(`状态: ${toRequestStatus(matched)}`);
166
+ response.appendResponseLine(`状态码: ${matched.statusCode ?? 'N/A'}`);
167
+ response.appendResponseLine(`耗时: ${matched.duration ?? 'N/A'}ms`);
168
+ response.appendResponseLine(`时间: ${matched.timestamp}`);
169
+ if (matched.headers && Object.keys(matched.headers).length > 0) {
170
+ response.appendResponseLine(`请求头: ${JSON.stringify(matched.headers)}`);
833
171
  }
834
- try {
835
- // 从小程序环境读取网络请求数据
836
- const logs = await context.miniProgram.evaluate(function () {
837
- // @ts-ignore - wx is available in WeChat miniprogram environment
838
- const wxObj = typeof wx !== 'undefined' ? wx : null;
839
- return wxObj?.__networkLogs || [];
840
- });
841
- const sinceTime = since ? new Date(since) : null;
842
- const urlRegex = urlPattern ? new RegExp(urlPattern) : null;
843
- // 过滤函数
844
- const filters = [
845
- // 过滤无效记录(type='response' 或 url为空/undefined)
846
- (req) => {
847
- // 过滤掉 type='response' 的记录(不应该存在)
848
- if (req.type === 'response') {
849
- return false;
850
- }
851
- // 过滤掉 URL 为空或 'undefined' 的记录
852
- if (!req.url || req.url === 'undefined') {
853
- return false;
854
- }
855
- // 过滤掉 ID 为空或 'N/A' 的记录
856
- if (!req.id || req.id === 'N/A') {
857
- return false;
858
- }
859
- return true;
860
- },
861
- // 类型过滤
862
- (req) => type === 'all' || req.type === type,
863
- // 时间过滤
864
- (req) => !sinceTime || new Date(req.timestamp) >= sinceTime,
865
- // URL 过滤
866
- (req) => !urlRegex || urlRegex.test(req.url),
867
- // 成功状态过滤
868
- (req) => !successOnly || req.success,
869
- ];
870
- const filteredRequests = logs
871
- .filter(req => filters.every(filter => filter(req)))
872
- .slice(-limit);
873
- // 生成响应
874
- response.appendResponseLine('=== 网络请求记录 ===');
875
- response.appendResponseLine(`监听状态: ${context.networkStorage.isMonitoring ? '运行中' : '已停止'}`);
876
- response.appendResponseLine(`监听开始时间: ${context.networkStorage.startTime || '未设置'}`);
877
- response.appendResponseLine(`总请求数: ${logs.length}`);
878
- response.appendResponseLine(`过滤后: ${filteredRequests.length} 条`);
879
- response.appendResponseLine('');
880
- if (filteredRequests.length === 0) {
881
- response.appendResponseLine('暂无符合条件的网络请求记录');
882
- return;
883
- }
884
- filteredRequests.forEach((req, index) => {
885
- response.appendResponseLine(`--- 请求 ${index + 1} ---`);
886
- response.appendResponseLine(`ID: ${req.id || 'N/A'}`);
887
- response.appendResponseLine(`类型: ${req.type}`);
888
- // 过滤掉旧的、无效的记录
889
- if (!req.url || req.url === 'undefined') {
890
- response.appendResponseLine(`⚠️ 无效记录(可能是旧数据)`);
891
- response.appendResponseLine('');
892
- return;
893
- }
894
- response.appendResponseLine(`URL: ${req.url}`);
895
- if (req.method) {
896
- response.appendResponseLine(`方法: ${req.method}`);
897
- }
898
- // 优化的状态判断逻辑
899
- const isPending = req.pending === true;
900
- const isCompleted = req.pending === false;
901
- const isSuccess = req.success === true;
902
- const isFailed = req.success === false;
903
- if (isPending) {
904
- response.appendResponseLine(`状态: ⏳ 请求中(未收到响应)`);
905
- }
906
- else if (isCompleted) {
907
- if (isSuccess) {
908
- response.appendResponseLine(`状态: ✅ 成功`);
909
- }
910
- else if (isFailed) {
911
- response.appendResponseLine(`状态: ❌ 失败`);
912
- }
913
- else {
914
- response.appendResponseLine(`状态: ⚠️ 未知(success=${req.success})`);
915
- }
916
- }
917
- else {
918
- // 兼容旧格式(wx.request等,没有pending字段)
919
- if (isSuccess) {
920
- response.appendResponseLine(`状态: ✅ 成功`);
921
- }
922
- else if (isFailed) {
923
- response.appendResponseLine(`状态: ❌ 失败`);
924
- }
925
- else {
926
- response.appendResponseLine(`状态: ⚠️ 未知状态`);
927
- }
928
- }
929
- if (req.statusCode) {
930
- response.appendResponseLine(`状态码: ${req.statusCode}`);
931
- }
932
- if (req.duration !== undefined) {
933
- response.appendResponseLine(`耗时: ${req.duration}ms`);
934
- }
935
- response.appendResponseLine(`时间: ${req.timestamp}`);
936
- if (req.source) {
937
- response.appendResponseLine(`来源: ${req.source}`);
938
- }
939
- // === 请求信息 ===
940
- if (req.headers && Object.keys(req.headers).length > 0) {
941
- response.appendResponseLine(`请求头: ${JSON.stringify(req.headers)}`);
942
- }
943
- if (req.data) {
944
- const dataStr = typeof req.data === 'string'
945
- ? req.data
946
- : JSON.stringify(req.data);
947
- const truncatedData = dataStr.length > 200
948
- ? dataStr.substring(0, 200) + '...'
949
- : dataStr;
950
- response.appendResponseLine(`请求数据: ${truncatedData}`);
951
- }
952
- if (req.params) {
953
- response.appendResponseLine(`请求参数: ${JSON.stringify(req.params)}`);
954
- }
955
- // === 响应信息 ===
956
- if (req.response) {
957
- const respStr = typeof req.response === 'string'
958
- ? req.response
959
- : JSON.stringify(req.response);
960
- const truncatedResp = respStr.length > 200
961
- ? respStr.substring(0, 200) + '...'
962
- : respStr;
963
- response.appendResponseLine(`响应数据: ${truncatedResp}`);
964
- }
965
- if (req.responseHeaders && Object.keys(req.responseHeaders).length > 0) {
966
- response.appendResponseLine(`响应头: ${JSON.stringify(req.responseHeaders)}`);
967
- }
968
- if (req.error) {
969
- response.appendResponseLine(`错误信息: ${req.error}`);
970
- }
971
- if (req.completedAt) {
972
- response.appendResponseLine(`完成时间: ${req.completedAt}`);
973
- }
974
- response.appendResponseLine('');
975
- });
976
- response.appendResponseLine('=== 获取完成 ===');
172
+ if (matched.data !== undefined) {
173
+ response.appendResponseLine(`请求数据: ${JSON.stringify(matched.data)}`);
174
+ }
175
+ if (matched.params && Object.keys(matched.params).length > 0) {
176
+ response.appendResponseLine(`请求参数: ${JSON.stringify(matched.params)}`);
177
+ }
178
+ if (matched.response !== undefined) {
179
+ response.appendResponseLine(`响应数据: ${JSON.stringify(matched.response)}`);
180
+ }
181
+ if (matched.responseHeaders && Object.keys(matched.responseHeaders).length > 0) {
182
+ response.appendResponseLine(`响应头: ${JSON.stringify(matched.responseHeaders)}`);
183
+ }
184
+ if (matched.error) {
185
+ response.appendResponseLine(`错误信息: ${matched.error}`);
977
186
  }
978
- catch (error) {
979
- const errorMessage = error instanceof Error ? error.message : String(error);
980
- throw new Error(`获取网络请求失败: ${errorMessage}`);
187
+ if (matched.completedAt) {
188
+ response.appendResponseLine(`完成时间: ${matched.completedAt}`);
981
189
  }
982
190
  },
983
191
  });
984
192
  /**
985
- * 诊断拦截器状态工具 - 用于调试
193
+ * 停止网络监听
986
194
  */
987
- export const diagnoseInterceptorTool = defineTool({
988
- name: 'diagnose_interceptor',
989
- description: '诊断网络拦截器安装状态和运行情况',
990
- schema: z.object({}),
195
+ export const stopNetworkMonitoringTool = defineTool({
196
+ name: 'stop_network_monitoring',
197
+ description: '停止网络监听并禁用拦截器',
198
+ schema: stopNetworkMonitoringSchema,
991
199
  annotations: {
200
+ category: ToolCategory.NETWORK,
992
201
  audience: ['developers'],
993
202
  },
994
203
  handler: async (request, response, context) => {
995
- if (!context.miniProgram) {
996
- throw new Error('请先连接到微信开发者工具');
997
- }
998
- try {
999
- const result = await context.miniProgram.evaluate(() => {
1000
- // @ts-ignore - wx is available in WeChat miniprogram environment
204
+ ensureConnected(context);
205
+ const { clearLogs } = request.params;
206
+ await context.miniProgram.evaluate(function () {
207
+ // @ts-ignore - wx is available in WeChat miniprogram environment
208
+ const wxObj = typeof wx !== 'undefined' ? wx : null;
209
+ if (wxObj) {
210
+ // @ts-ignore
211
+ wxObj.__networkInterceptorsDisabled = true;
212
+ }
213
+ });
214
+ const storage = context.networkStorage;
215
+ storage.isMonitoring = false;
216
+ context.networkStorage = storage;
217
+ let clearedCount = 0;
218
+ if (clearLogs) {
219
+ clearedCount = await context.miniProgram.evaluate(function () {
220
+ // @ts-ignore
1001
221
  const wxObj = typeof wx !== 'undefined' ? wx : null;
1002
- // 测试console.log
1003
- console.log('[INTERCEPTOR-DIAGNOSE] === 开始诊断拦截器 ===');
1004
- console.log('[INTERCEPTOR-DIAGNOSE] wx对象存在:', !!wxObj);
1005
- // @ts-ignore - getApp is available in WeChat miniprogram environment
1006
- const hasGetApp = typeof getApp !== 'undefined';
1007
- // @ts-ignore - getApp is available in WeChat miniprogram environment
1008
- const app = hasGetApp ? getApp() : null;
1009
- const diagnosticInfo = {
1010
- environment: {
1011
- hasWx: !!wxObj,
1012
- hasGetApp: hasGetApp,
1013
- },
1014
- interceptor: {
1015
- installed: !!(wxObj && wxObj.__networkInterceptorsInstalled),
1016
- hasNetworkLogs: !!(wxObj && wxObj.__networkLogs),
1017
- networkLogsLength: wxObj && wxObj.__networkLogs ? wxObj.__networkLogs.length : 0,
1018
- },
1019
- mpx: {
1020
- hasGetApp: hasGetApp,
1021
- hasApp: !!app,
1022
- has$xfetch: !!(app && app.$xfetch),
1023
- },
1024
- networkLogs: wxObj && wxObj.__networkLogs ? wxObj.__networkLogs.slice(-5) : [],
1025
- };
1026
- console.log('[INTERCEPTOR-DIAGNOSE] 诊断信息:', JSON.stringify(diagnosticInfo, null, 2));
1027
- console.log('[INTERCEPTOR-DIAGNOSE] === 诊断完成 ===');
1028
- return diagnosticInfo;
222
+ if (wxObj && wxObj.__networkLogs) {
223
+ // @ts-ignore
224
+ const count = wxObj.__networkLogs.length;
225
+ // @ts-ignore
226
+ wxObj.__networkLogs = [];
227
+ return count;
228
+ }
229
+ return 0;
1029
230
  });
1030
- response.appendResponseLine('=== 拦截器诊断结果 ===\n');
1031
- response.appendResponseLine(`环境检查:`);
1032
- response.appendResponseLine(` wx对象: ${result.environment.hasWx ? '✅' : '❌'}`);
1033
- response.appendResponseLine(` getApp: ${result.environment.hasGetApp ? '✅' : '❌'}`);
1034
- response.appendResponseLine('');
1035
- response.appendResponseLine(`拦截器状态:`);
1036
- response.appendResponseLine(` 已安装: ${result.interceptor.installed ? '✅' : '❌'}`);
1037
- response.appendResponseLine(` 日志数组: ${result.interceptor.hasNetworkLogs ? '✅' : '❌'}`);
1038
- response.appendResponseLine(` 记录数量: ${result.interceptor.networkLogsLength}`);
1039
- response.appendResponseLine('');
1040
- response.appendResponseLine(`Mpx框架:`);
1041
- response.appendResponseLine(` getApp可用: ${result.mpx.hasGetApp ? '✅' : '❌'}`);
1042
- response.appendResponseLine(` App实例: ${result.mpx.hasApp ? '✅' : '❌'}`);
1043
- response.appendResponseLine(` $xfetch: ${result.mpx.has$xfetch ? '✅' : '❌'}`);
1044
- response.appendResponseLine('');
1045
- if (result.networkLogs && result.networkLogs.length > 0) {
1046
- response.appendResponseLine(`最近${result.networkLogs.length}条网络日志:`);
1047
- result.networkLogs.forEach((log, index) => {
1048
- response.appendResponseLine(` ${index + 1}. [${log.type}] ${log.url || log.method}`);
1049
- });
1050
- }
1051
231
  }
1052
- catch (error) {
1053
- const errorMessage = error instanceof Error ? error.message : String(error);
1054
- throw new Error(`诊断失败: ${errorMessage}`);
232
+ response.appendResponseLine('=== 网络监听已停止 ===');
233
+ response.appendResponseLine('监听状态: 已停止');
234
+ if (clearLogs) {
235
+ response.appendResponseLine(`已清空日志: ${clearedCount} 条`);
1055
236
  }
237
+ response.appendResponseLine('');
238
+ response.appendResponseLine('提示: 使用 reconnect_devtools 重新连接可恢复监听');
1056
239
  },
1057
240
  });
1058
241
  /**
1059
- * 清除网络请求工具
242
+ * 清空网络请求记录
1060
243
  */
1061
244
  export const clearNetworkRequestsTool = defineTool({
1062
245
  name: 'clear_network_requests',
1063
- description: '清除已收集的网络请求记录',
1064
- schema: z.object({
1065
- type: z.enum(['all', 'request', 'uploadFile', 'downloadFile']).optional().default('all').describe('清除的请求类型'),
1066
- }),
246
+ description: '清空已收集的网络请求记录',
247
+ schema: clearNetworkRequestsSchema,
1067
248
  annotations: {
249
+ category: ToolCategory.NETWORK,
1068
250
  audience: ['developers'],
1069
251
  },
1070
252
  handler: async (request, response, context) => {
1071
- const { type } = request.params;
1072
- if (!context.miniProgram) {
1073
- throw new Error('请先连接到微信开发者工具');
1074
- }
1075
- if (!context.networkStorage) {
1076
- throw new Error('网络存储未初始化');
1077
- }
1078
- try {
1079
- // 获取当前数量
1080
- const beforeCount = await context.miniProgram.evaluate(function () {
1081
- // @ts-ignore - wx is available in WeChat miniprogram environment
1082
- const wxObj = typeof wx !== 'undefined' ? wx : null;
1083
- return (wxObj?.__networkLogs || []).length;
1084
- });
1085
- // 在小程序环境清除数据
1086
- const afterCount = await context.miniProgram.evaluate(function (typeToDelete) {
1087
- // @ts-ignore - wx is available in WeChat miniprogram environment
253
+ ensureConnected(context);
254
+ const { clearRemote } = request.params;
255
+ const localCountBefore = context.getNetworkCollector().getCurrentCount();
256
+ context.clearNetworkRequests();
257
+ let remoteCount = 0;
258
+ if (clearRemote) {
259
+ remoteCount = await context.miniProgram.evaluate(function () {
260
+ // @ts-ignore
1088
261
  const wxObj = typeof wx !== 'undefined' ? wx : null;
1089
- if (!wxObj || !wxObj.__networkLogs) {
1090
- return 0;
1091
- }
1092
- if (typeToDelete === 'all') {
262
+ if (wxObj && wxObj.__networkLogs) {
263
+ // @ts-ignore
264
+ const count = wxObj.__networkLogs.length;
265
+ // @ts-ignore
1093
266
  wxObj.__networkLogs = [];
267
+ return count;
1094
268
  }
1095
- else {
1096
- wxObj.__networkLogs = wxObj.__networkLogs.filter((req) => req.type !== typeToDelete);
1097
- }
1098
- return wxObj.__networkLogs.length;
1099
- }, type);
1100
- const clearedCount = beforeCount - afterCount;
1101
- response.appendResponseLine('✅ 网络请求记录清除完成');
1102
- response.appendResponseLine(`清除类型: ${type}`);
1103
- response.appendResponseLine(`清除数量: ${clearedCount} 条`);
1104
- response.appendResponseLine(`剩余数量: ${afterCount} 条`);
269
+ return 0;
270
+ });
1105
271
  }
1106
- catch (error) {
1107
- const errorMessage = error instanceof Error ? error.message : String(error);
1108
- throw new Error(`清除网络请求失败: ${errorMessage}`);
272
+ response.appendResponseLine('=== 网络请求记录已清空 ===');
273
+ response.appendResponseLine(`本地清空: ${localCountBefore} 条`);
274
+ if (clearRemote) {
275
+ response.appendResponseLine(`远程清空: ${remoteCount} 条`);
1109
276
  }
277
+ response.appendResponseLine('');
278
+ response.appendResponseLine('提示: 网络监听仍在运行,新的请求会继续被收集');
1110
279
  },
1111
280
  });
281
+ //# sourceMappingURL=network.js.map