@pyrokine/mcp-chrome 2.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +87 -43
- package/dist/core/browser-driver.d.ts +118 -1
- package/dist/core/browser-driver.d.ts.map +1 -1
- package/dist/core/browser-driver.js +11 -0
- package/dist/core/browser-driver.js.map +1 -1
- package/dist/core/error-sanitizer.js +2 -2
- package/dist/core/error-sanitizer.js.map +1 -1
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +17 -4
- package/dist/core/errors.js.map +1 -1
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/locator.d.ts.map +1 -1
- package/dist/core/locator.js +4 -3
- package/dist/core/locator.js.map +1 -1
- package/dist/core/session.d.ts +5 -2
- package/dist/core/session.d.ts.map +1 -1
- package/dist/core/session.js +107 -4
- package/dist/core/session.js.map +1 -1
- package/dist/core/types.d.ts +17 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +57 -4
- package/dist/core/types.js.map +1 -1
- package/dist/core/unified-session.d.ts +29 -9
- package/dist/core/unified-session.d.ts.map +1 -1
- package/dist/core/unified-session.js +226 -18
- package/dist/core/unified-session.js.map +1 -1
- package/dist/core/utils.d.ts +13 -0
- package/dist/core/utils.d.ts.map +1 -1
- package/dist/core/utils.js +121 -0
- package/dist/core/utils.js.map +1 -1
- package/dist/extension/bridge.d.ts +37 -22
- package/dist/extension/bridge.d.ts.map +1 -1
- package/dist/extension/bridge.js +256 -96
- package/dist/extension/bridge.js.map +1 -1
- package/dist/extension/http-server.d.ts.map +1 -1
- package/dist/extension/http-server.js +39 -10
- package/dist/extension/http-server.js.map +1 -1
- package/dist/tools/browse.d.ts.map +1 -1
- package/dist/tools/browse.js +160 -55
- package/dist/tools/browse.js.map +1 -1
- package/dist/tools/cookies.d.ts.map +1 -1
- package/dist/tools/cookies.js +8 -11
- package/dist/tools/cookies.js.map +1 -1
- package/dist/tools/evaluate.d.ts.map +1 -1
- package/dist/tools/evaluate.js +53 -30
- package/dist/tools/evaluate.js.map +1 -1
- package/dist/tools/extract.d.ts +1 -0
- package/dist/tools/extract.d.ts.map +1 -1
- package/dist/tools/extract.js +476 -244
- package/dist/tools/extract.js.map +1 -1
- package/dist/tools/input.d.ts.map +1 -1
- package/dist/tools/input.js +798 -441
- package/dist/tools/input.js.map +1 -1
- package/dist/tools/logs.d.ts.map +1 -1
- package/dist/tools/logs.js +79 -31
- package/dist/tools/logs.js.map +1 -1
- package/dist/tools/manage.d.ts +0 -3
- package/dist/tools/manage.d.ts.map +1 -1
- package/dist/tools/manage.js +339 -285
- package/dist/tools/manage.js.map +1 -1
- package/dist/tools/png.d.ts +16 -0
- package/dist/tools/png.d.ts.map +1 -0
- package/dist/tools/png.js +197 -0
- package/dist/tools/png.js.map +1 -0
- package/dist/tools/schema.d.ts +14 -112
- package/dist/tools/schema.d.ts.map +1 -1
- package/dist/tools/schema.js +21 -12
- package/dist/tools/schema.js.map +1 -1
- package/dist/tools/wait.d.ts.map +1 -1
- package/dist/tools/wait.js +73 -13
- package/dist/tools/wait.js.map +1 -1
- package/package.json +11 -11
package/dist/tools/manage.js
CHANGED
|
@@ -14,20 +14,78 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import { z } from 'zod';
|
|
16
16
|
import { devices, formatErrorResponse, formatResponse, getSession, getUnifiedSession } from '../core/index.js';
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
function invalidArgument(message, suggestion) {
|
|
18
|
+
return {
|
|
19
|
+
content: [
|
|
20
|
+
{
|
|
21
|
+
type: 'text',
|
|
22
|
+
text: JSON.stringify({
|
|
23
|
+
error: {
|
|
24
|
+
code: 'INVALID_ARGUMENT',
|
|
25
|
+
message,
|
|
26
|
+
...(suggestion ? { suggestion } : {}),
|
|
27
|
+
},
|
|
28
|
+
}),
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
isError: true,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function formatAffectedResponse(action, mode, affected) {
|
|
35
|
+
return formatResponse({
|
|
36
|
+
success: true,
|
|
37
|
+
action,
|
|
38
|
+
mode,
|
|
39
|
+
affected,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
20
42
|
const manageSchema = z.object({
|
|
21
43
|
action: z
|
|
22
|
-
.enum([
|
|
44
|
+
.enum([
|
|
45
|
+
'newPage',
|
|
46
|
+
'closePage',
|
|
47
|
+
'adoptPage',
|
|
48
|
+
'releasePage',
|
|
49
|
+
'movePage',
|
|
50
|
+
'reorderPage',
|
|
51
|
+
'pinPage',
|
|
52
|
+
'unpinPage',
|
|
53
|
+
'activatePage',
|
|
54
|
+
'focusWindow',
|
|
55
|
+
'resizeWindow',
|
|
56
|
+
'newWindow',
|
|
57
|
+
'closeWindow',
|
|
58
|
+
'clearCache',
|
|
59
|
+
'viewport',
|
|
60
|
+
'userAgent',
|
|
61
|
+
'emulate',
|
|
62
|
+
'inputMode',
|
|
63
|
+
'stealth',
|
|
64
|
+
'cdp',
|
|
65
|
+
])
|
|
23
66
|
.describe('管理操作'),
|
|
24
67
|
inputMode: z
|
|
25
68
|
.enum(['stealth', 'precise'])
|
|
26
69
|
.optional()
|
|
27
70
|
.describe('输入模式(inputMode),precise=debugger API(默认,可绕过 CSP 但显示调试提示);stealth=JS 事件模拟(不触发调试提示但受 CSP 限制,适用于反检测场景)'),
|
|
28
71
|
cdpMethod: z.string().optional().describe('CDP 方法名(cdp),如 Runtime.evaluate、Page.captureScreenshot'),
|
|
29
|
-
cdpParams: z.record(z.unknown()).optional().describe('CDP 方法参数(cdp)'),
|
|
30
|
-
targetId: z
|
|
72
|
+
cdpParams: z.record(z.string(), z.unknown()).optional().describe('CDP 方法参数(cdp)'),
|
|
73
|
+
targetId: z
|
|
74
|
+
.string()
|
|
75
|
+
.optional()
|
|
76
|
+
.describe('目标页面 ID(closePage/adoptPage/releasePage/movePage/reorderPage/pinPage/unpinPage/activatePage)'),
|
|
77
|
+
windowId: z.number().int().optional().describe('窗口 ID(movePage/focusWindow/resizeWindow/closeWindow)'),
|
|
78
|
+
index: z.number().int().nonnegative().optional().describe('tab 顺序 index(movePage/reorderPage)'),
|
|
79
|
+
activate: z.boolean().optional().describe('movePage 后是否激活目标 tab'),
|
|
80
|
+
focused: z.boolean().optional().describe('newWindow 是否聚焦新窗口,默认 false'),
|
|
81
|
+
incognito: z.boolean().optional().describe('newWindow 是否创建隐身窗口'),
|
|
82
|
+
left: z.number().int().optional().describe('窗口左上角 x 坐标(newWindow/resizeWindow)'),
|
|
83
|
+
top: z.number().int().optional().describe('窗口左上角 y 坐标(newWindow/resizeWindow)'),
|
|
84
|
+
windowState: z
|
|
85
|
+
.enum(['normal', 'minimized', 'maximized', 'fullscreen', 'locked-fullscreen'])
|
|
86
|
+
.optional()
|
|
87
|
+
.describe('窗口状态(newWindow/resizeWindow)'),
|
|
88
|
+
url: z.string().optional().describe('newWindow 初始 URL'),
|
|
31
89
|
cacheType: z
|
|
32
90
|
.enum(['all', 'storage', 'cache'])
|
|
33
91
|
.optional()
|
|
@@ -37,305 +95,301 @@ const manageSchema = z.object({
|
|
|
37
95
|
userAgent: z.string().optional().describe('User-Agent 字符串(userAgent)'),
|
|
38
96
|
device: z.string().optional().describe('设备名称(emulate),如 iPhone 13, iPad Pro'),
|
|
39
97
|
});
|
|
40
|
-
/**
|
|
41
|
-
* manage 工具处理器
|
|
42
|
-
*/
|
|
43
98
|
async function handleManage(args) {
|
|
44
99
|
try {
|
|
45
100
|
const unifiedSession = getUnifiedSession();
|
|
46
101
|
const mode = unifiedSession.getMode();
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
content: [
|
|
293
|
-
{
|
|
294
|
-
type: 'text',
|
|
295
|
-
text: JSON.stringify({
|
|
296
|
-
error: {
|
|
297
|
-
code: 'CDP_DOMAIN_NOT_SUPPORTED',
|
|
298
|
-
message: `Extension 模式不支持 ${domain} 域`,
|
|
299
|
-
suggestion: 'Extension 模式可用域:Page、Runtime、Emulation、DOM、Input、Network,' +
|
|
300
|
-
'如需完整 CDP 支持,请使用 CDP 模式(browse action="launch")',
|
|
301
|
-
},
|
|
302
|
-
}),
|
|
303
|
-
},
|
|
304
|
-
],
|
|
305
|
-
isError: true,
|
|
306
|
-
};
|
|
307
|
-
}
|
|
308
|
-
return formatErrorResponse(error);
|
|
309
|
-
}
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
default:
|
|
102
|
+
return await manageHandlers[args.action]({ unifiedSession, mode }, args);
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
return formatErrorResponse(error);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async function handleNewPage({ unifiedSession, mode }, args) {
|
|
109
|
+
const target = await unifiedSession.newPage(args.url);
|
|
110
|
+
return formatResponse({ success: true, action: 'newPage', target, mode });
|
|
111
|
+
}
|
|
112
|
+
async function handleClosePage({ unifiedSession, mode }, args) {
|
|
113
|
+
const affected = await unifiedSession.closePage(args.targetId);
|
|
114
|
+
return formatResponse({
|
|
115
|
+
success: true,
|
|
116
|
+
action: 'closePage',
|
|
117
|
+
targetId: args.targetId ?? 'current',
|
|
118
|
+
mode,
|
|
119
|
+
...(affected ? { affected } : {}),
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
async function handleAdoptPage({ unifiedSession, mode }, args) {
|
|
123
|
+
if (!args.targetId) {
|
|
124
|
+
return invalidArgument('adoptPage 需要 targetId 参数', '请先使用 browse(action="list") 获取目标 tab 的 targetId');
|
|
125
|
+
}
|
|
126
|
+
const result = await unifiedSession.adoptPage(args.targetId);
|
|
127
|
+
return formatResponse({ success: true, action: 'adoptPage', mode, targetId: args.targetId, ...result });
|
|
128
|
+
}
|
|
129
|
+
async function handleReleasePage({ unifiedSession, mode }, args) {
|
|
130
|
+
if (!args.targetId) {
|
|
131
|
+
return invalidArgument('releasePage 需要 targetId 参数', '请先使用 browse(action="list") 获取目标 tab 的 targetId');
|
|
132
|
+
}
|
|
133
|
+
const result = await unifiedSession.releasePage(args.targetId);
|
|
134
|
+
return formatResponse({ success: true, action: 'releasePage', mode, targetId: args.targetId, ...result });
|
|
135
|
+
}
|
|
136
|
+
async function handleMovePage({ unifiedSession, mode }, args) {
|
|
137
|
+
if (!args.targetId) {
|
|
138
|
+
return invalidArgument('movePage 需要 targetId 参数', '请先使用 browse(action="list") 获取目标 tab 的 targetId');
|
|
139
|
+
}
|
|
140
|
+
const affected = await unifiedSession.movePage(args.targetId, {
|
|
141
|
+
windowId: args.windowId,
|
|
142
|
+
index: args.index,
|
|
143
|
+
activate: args.activate,
|
|
144
|
+
});
|
|
145
|
+
return formatAffectedResponse('movePage', mode, affected);
|
|
146
|
+
}
|
|
147
|
+
async function handleReorderPage({ unifiedSession, mode }, args) {
|
|
148
|
+
if (!args.targetId) {
|
|
149
|
+
return invalidArgument('reorderPage 需要 targetId 参数', '请先使用 browse(action="list") 获取目标 tab 的 targetId');
|
|
150
|
+
}
|
|
151
|
+
if (args.index === undefined) {
|
|
152
|
+
return invalidArgument('reorderPage 需要 index 参数');
|
|
153
|
+
}
|
|
154
|
+
const affected = await unifiedSession.reorderPage(args.targetId, args.index);
|
|
155
|
+
return formatAffectedResponse('reorderPage', mode, affected);
|
|
156
|
+
}
|
|
157
|
+
async function handlePinPage({ unifiedSession, mode }, args) {
|
|
158
|
+
if (!args.targetId) {
|
|
159
|
+
return invalidArgument('pinPage 需要 targetId 参数', '请先使用 browse(action="list") 获取目标 tab 的 targetId');
|
|
160
|
+
}
|
|
161
|
+
const affected = await unifiedSession.pinPage(args.targetId, true);
|
|
162
|
+
return formatAffectedResponse('pinPage', mode, affected);
|
|
163
|
+
}
|
|
164
|
+
async function handleUnpinPage({ unifiedSession, mode }, args) {
|
|
165
|
+
if (!args.targetId) {
|
|
166
|
+
return invalidArgument('unpinPage 需要 targetId 参数', '请先使用 browse(action="list") 获取目标 tab 的 targetId');
|
|
167
|
+
}
|
|
168
|
+
const affected = await unifiedSession.pinPage(args.targetId, false);
|
|
169
|
+
return formatAffectedResponse('unpinPage', mode, affected);
|
|
170
|
+
}
|
|
171
|
+
async function handleActivatePage({ unifiedSession, mode }, args) {
|
|
172
|
+
if (!args.targetId) {
|
|
173
|
+
return invalidArgument('activatePage 需要 targetId 参数', '请先使用 browse(action="list") 获取目标 tab 的 targetId');
|
|
174
|
+
}
|
|
175
|
+
const affected = await unifiedSession.activatePageWithAffected(args.targetId);
|
|
176
|
+
return formatAffectedResponse('activatePage', mode, affected);
|
|
177
|
+
}
|
|
178
|
+
async function handleFocusWindow({ unifiedSession, mode }, args) {
|
|
179
|
+
if (args.windowId === undefined) {
|
|
180
|
+
return invalidArgument('focusWindow 需要 windowId 参数');
|
|
181
|
+
}
|
|
182
|
+
const affected = await unifiedSession.focusWindow(args.windowId);
|
|
183
|
+
return formatAffectedResponse('focusWindow', mode, affected);
|
|
184
|
+
}
|
|
185
|
+
async function handleResizeWindow({ unifiedSession, mode }, args) {
|
|
186
|
+
if (args.windowId === undefined) {
|
|
187
|
+
return invalidArgument('resizeWindow 需要 windowId 参数');
|
|
188
|
+
}
|
|
189
|
+
const affected = await unifiedSession.resizeWindow(args.windowId, {
|
|
190
|
+
left: args.left,
|
|
191
|
+
top: args.top,
|
|
192
|
+
width: args.width,
|
|
193
|
+
height: args.height,
|
|
194
|
+
state: args.windowState,
|
|
195
|
+
});
|
|
196
|
+
return formatAffectedResponse('resizeWindow', mode, affected);
|
|
197
|
+
}
|
|
198
|
+
async function handleNewWindow({ unifiedSession, mode }, args) {
|
|
199
|
+
const affected = await unifiedSession.newWindow({
|
|
200
|
+
url: args.url,
|
|
201
|
+
focused: args.focused,
|
|
202
|
+
incognito: args.incognito,
|
|
203
|
+
left: args.left,
|
|
204
|
+
top: args.top,
|
|
205
|
+
width: args.width,
|
|
206
|
+
height: args.height,
|
|
207
|
+
state: args.windowState,
|
|
208
|
+
});
|
|
209
|
+
return formatAffectedResponse('newWindow', mode, affected);
|
|
210
|
+
}
|
|
211
|
+
async function handleCloseWindow({ unifiedSession, mode }, args) {
|
|
212
|
+
if (args.windowId === undefined) {
|
|
213
|
+
return invalidArgument('closeWindow 需要 windowId 参数');
|
|
214
|
+
}
|
|
215
|
+
const affected = await unifiedSession.closeWindow(args.windowId);
|
|
216
|
+
return formatAffectedResponse('closeWindow', mode, affected);
|
|
217
|
+
}
|
|
218
|
+
async function handleClearCache({ unifiedSession, mode }, args) {
|
|
219
|
+
return unifiedSession.withTabId(undefined, async () => {
|
|
220
|
+
const cacheType = (args.cacheType ?? 'all');
|
|
221
|
+
if (mode === 'extension') {
|
|
222
|
+
return formatResponse({
|
|
223
|
+
success: true,
|
|
224
|
+
action: 'clearCache',
|
|
225
|
+
cacheType,
|
|
226
|
+
mode,
|
|
227
|
+
warning: 'Extension 模式不支持 clearCache,如需清除 cookies 请使用 cookies action=clear(必须带 name/domain/url 过滤),如需清除 storage/cache 请切换到 CDP 模式',
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
const session = getSession();
|
|
231
|
+
await session.clearCache(cacheType);
|
|
232
|
+
return formatResponse({ success: true, action: 'clearCache', cacheType, mode });
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
async function handleViewport({ unifiedSession, mode }, args) {
|
|
236
|
+
return unifiedSession.withTabId(undefined, async () => {
|
|
237
|
+
if (args.width === undefined || args.height === undefined) {
|
|
238
|
+
return invalidArgument('设置视口需要 width 和 height 参数');
|
|
239
|
+
}
|
|
240
|
+
if (mode === 'extension') {
|
|
241
|
+
await unifiedSession.sendCdpCommand('Emulation.setDeviceMetricsOverride', {
|
|
242
|
+
width: args.width,
|
|
243
|
+
height: args.height,
|
|
244
|
+
deviceScaleFactor: 1,
|
|
245
|
+
mobile: false,
|
|
246
|
+
});
|
|
247
|
+
await unifiedSession.sendCdpCommand('Emulation.setPageScaleFactor', { pageScaleFactor: 1 });
|
|
248
|
+
await unifiedSession.sendCdpCommand('Emulation.setTouchEmulationEnabled', { enabled: false });
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
const session = getSession();
|
|
252
|
+
await session.setViewport(args.width, args.height);
|
|
253
|
+
}
|
|
254
|
+
await unifiedSession.evaluate('window.dispatchEvent(new Event("resize"))');
|
|
255
|
+
return formatResponse({ success: true, action: 'viewport', width: args.width, height: args.height, mode });
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
async function handleUserAgent({ unifiedSession, mode }, args) {
|
|
259
|
+
return unifiedSession.withTabId(undefined, async () => {
|
|
260
|
+
if (!args.userAgent) {
|
|
261
|
+
return invalidArgument('设置 User-Agent 需要 userAgent 参数');
|
|
262
|
+
}
|
|
263
|
+
if (mode === 'extension') {
|
|
264
|
+
await unifiedSession.sendCdpCommand('Emulation.setUserAgentOverride', { userAgent: args.userAgent });
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
const session = getSession();
|
|
268
|
+
await session.setUserAgent(args.userAgent);
|
|
269
|
+
}
|
|
270
|
+
return formatResponse({ success: true, action: 'userAgent', userAgent: args.userAgent, mode });
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
function handleInputMode({ unifiedSession, mode }, args) {
|
|
274
|
+
if (!args.inputMode) {
|
|
275
|
+
return formatResponse({
|
|
276
|
+
success: true,
|
|
277
|
+
action: 'inputMode',
|
|
278
|
+
currentMode: unifiedSession.getInputMode(),
|
|
279
|
+
availableModes: ['stealth', 'precise'],
|
|
280
|
+
description: {
|
|
281
|
+
stealth: 'JS 事件模拟,不触发调试提示,但受 CSP 限制(evaluate 可能失败)',
|
|
282
|
+
precise: 'debugger API,可绕过 CSP,但显示"扩展程序正在调试此浏览器"',
|
|
283
|
+
},
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
unifiedSession.setInputMode(args.inputMode);
|
|
287
|
+
return formatResponse({ success: true, action: 'inputMode', inputMode: args.inputMode, mode });
|
|
288
|
+
}
|
|
289
|
+
async function handleEmulate({ unifiedSession, mode }, args) {
|
|
290
|
+
return unifiedSession.withTabId(undefined, async () => {
|
|
291
|
+
if (!args.device) {
|
|
292
|
+
return formatResponse({ success: true, action: 'emulate', availableDevices: Object.keys(devices) });
|
|
293
|
+
}
|
|
294
|
+
const device = devices[args.device];
|
|
295
|
+
if (!device) {
|
|
296
|
+
return invalidArgument(`未知设备: ${args.device}`, `可用设备: ${Object.keys(devices).join(', ')}`);
|
|
297
|
+
}
|
|
298
|
+
if (mode === 'extension') {
|
|
299
|
+
await unifiedSession.sendCdpCommand('Emulation.setDeviceMetricsOverride', {
|
|
300
|
+
width: device.viewport.width,
|
|
301
|
+
height: device.viewport.height,
|
|
302
|
+
deviceScaleFactor: device.viewport.deviceScaleFactor || 1,
|
|
303
|
+
mobile: device.viewport.isMobile || false,
|
|
304
|
+
});
|
|
305
|
+
await unifiedSession.sendCdpCommand('Emulation.setPageScaleFactor', { pageScaleFactor: 1 });
|
|
306
|
+
await unifiedSession.sendCdpCommand('Emulation.setTouchEmulationEnabled', {
|
|
307
|
+
enabled: device.viewport.hasTouch,
|
|
308
|
+
maxTouchPoints: device.viewport.hasTouch ? 1 : 0,
|
|
309
|
+
});
|
|
310
|
+
await unifiedSession.sendCdpCommand('Emulation.setUserAgentOverride', { userAgent: device.userAgent });
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
const session = getSession();
|
|
314
|
+
await session.setViewport(device.viewport.width, device.viewport.height);
|
|
315
|
+
await session.setUserAgent(device.userAgent);
|
|
316
|
+
}
|
|
317
|
+
await unifiedSession.evaluate('window.dispatchEvent(new Event("resize"))');
|
|
318
|
+
return formatResponse({
|
|
319
|
+
success: true,
|
|
320
|
+
action: 'emulate',
|
|
321
|
+
device: args.device,
|
|
322
|
+
viewport: device.viewport,
|
|
323
|
+
mode,
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
async function handleStealth({ unifiedSession, mode }) {
|
|
328
|
+
return unifiedSession.withTabId(undefined, async () => {
|
|
329
|
+
await unifiedSession.injectStealth();
|
|
330
|
+
return formatResponse({ success: true, action: 'stealth', mode, note: '已注入反检测脚本' });
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
async function handleCdp({ unifiedSession, mode }, args) {
|
|
334
|
+
return unifiedSession.withTabId(undefined, async () => {
|
|
335
|
+
if (!args.cdpMethod) {
|
|
336
|
+
return invalidArgument('缺少 cdpMethod 参数', '请指定 CDP 方法名,如 Runtime.evaluate、Page.captureScreenshot');
|
|
337
|
+
}
|
|
338
|
+
try {
|
|
339
|
+
const result = await unifiedSession.sendCdpCommand(args.cdpMethod, args.cdpParams);
|
|
340
|
+
return formatResponse({ success: true, action: 'cdp', method: args.cdpMethod, result, mode });
|
|
341
|
+
}
|
|
342
|
+
catch (error) {
|
|
343
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
344
|
+
if (mode === 'extension' &&
|
|
345
|
+
(errorMessage.includes('not supported') || errorMessage.includes('not found'))) {
|
|
346
|
+
const domain = args.cdpMethod.split('.')[0];
|
|
313
347
|
return {
|
|
314
348
|
content: [
|
|
315
349
|
{
|
|
316
350
|
type: 'text',
|
|
317
351
|
text: JSON.stringify({
|
|
318
352
|
error: {
|
|
319
|
-
code: '
|
|
320
|
-
message:
|
|
353
|
+
code: 'CDP_DOMAIN_NOT_SUPPORTED',
|
|
354
|
+
message: `Extension 模式不支持 ${domain} 域`,
|
|
355
|
+
suggestion: 'Extension 模式可用域:Page、Runtime、Emulation、DOM、Input、Network,' +
|
|
356
|
+
'如需完整 CDP 支持,请使用 CDP 模式(browse action="launch")',
|
|
321
357
|
},
|
|
322
358
|
}),
|
|
323
359
|
},
|
|
324
360
|
],
|
|
325
361
|
isError: true,
|
|
326
362
|
};
|
|
363
|
+
}
|
|
364
|
+
return formatErrorResponse(error);
|
|
327
365
|
}
|
|
328
|
-
}
|
|
329
|
-
catch (error) {
|
|
330
|
-
return formatErrorResponse(error);
|
|
331
|
-
}
|
|
366
|
+
});
|
|
332
367
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
368
|
+
const manageHandlers = {
|
|
369
|
+
newPage: handleNewPage,
|
|
370
|
+
closePage: handleClosePage,
|
|
371
|
+
adoptPage: handleAdoptPage,
|
|
372
|
+
releasePage: handleReleasePage,
|
|
373
|
+
movePage: handleMovePage,
|
|
374
|
+
reorderPage: handleReorderPage,
|
|
375
|
+
pinPage: handlePinPage,
|
|
376
|
+
unpinPage: handleUnpinPage,
|
|
377
|
+
activatePage: handleActivatePage,
|
|
378
|
+
focusWindow: handleFocusWindow,
|
|
379
|
+
resizeWindow: handleResizeWindow,
|
|
380
|
+
newWindow: handleNewWindow,
|
|
381
|
+
closeWindow: handleCloseWindow,
|
|
382
|
+
clearCache: handleClearCache,
|
|
383
|
+
viewport: handleViewport,
|
|
384
|
+
userAgent: handleUserAgent,
|
|
385
|
+
emulate: handleEmulate,
|
|
386
|
+
inputMode: handleInputMode,
|
|
387
|
+
stealth: handleStealth,
|
|
388
|
+
cdp: handleCdp,
|
|
389
|
+
};
|
|
336
390
|
export function registerManageTool(server) {
|
|
337
391
|
server.registerTool('manage', {
|
|
338
|
-
description: '
|
|
392
|
+
description: '页面与环境管理:新建页面、关闭页面、受控页 adopt/release、缓存、视口、UA、设备模拟',
|
|
339
393
|
inputSchema: manageSchema,
|
|
340
394
|
}, (args) => handleManage(args));
|
|
341
395
|
}
|