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/network.js
CHANGED
|
@@ -1,1111 +1,281 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 网络请求监听工具
|
|
3
|
-
*
|
|
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 (!
|
|
24
|
-
|
|
14
|
+
if (!request.id || request.id === 'N/A') {
|
|
15
|
+
continue;
|
|
25
16
|
}
|
|
26
|
-
|
|
27
|
-
|
|
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 (
|
|
80
|
-
|
|
20
|
+
if (request.type !== 'request' && request.type !== 'uploadFile' && request.type !== 'downloadFile') {
|
|
21
|
+
continue;
|
|
81
22
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
return
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
|
187
|
-
name: '
|
|
188
|
-
description: '
|
|
189
|
-
schema:
|
|
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
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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
|
-
|
|
207
|
-
|
|
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
|
-
|
|
210
|
-
|
|
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
|
-
|
|
737
|
-
|
|
738
|
-
throw new Error(`启动网络监听失败: ${errorMessage}`);
|
|
109
|
+
if (failedOnly) {
|
|
110
|
+
filteredRequests = filteredRequests.filter(req => req.success === false);
|
|
739
111
|
}
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
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
|
-
|
|
760
|
-
|
|
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
|
-
|
|
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
|
|
814
|
-
name: '
|
|
815
|
-
description: '
|
|
816
|
-
schema:
|
|
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
|
-
|
|
828
|
-
|
|
829
|
-
|
|
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
|
-
|
|
832
|
-
|
|
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
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
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
|
-
|
|
979
|
-
|
|
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
|
|
988
|
-
name: '
|
|
989
|
-
description: '
|
|
990
|
-
schema:
|
|
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
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
const
|
|
1000
|
-
|
|
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
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
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
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
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:
|
|
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
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
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 (
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
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
|
-
|
|
1096
|
-
|
|
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
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
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
|