@pyrokine/mcp-chrome 1.7.0 → 2.0.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 (109) hide show
  1. package/README.md +71 -31
  2. package/dist/anti-detection/behavior.d.ts.map +1 -1
  3. package/dist/anti-detection/behavior.js.map +1 -1
  4. package/dist/anti-detection/index.d.ts +1 -1
  5. package/dist/anti-detection/index.d.ts.map +1 -1
  6. package/dist/anti-detection/index.js +1 -1
  7. package/dist/anti-detection/index.js.map +1 -1
  8. package/dist/anti-detection/injection.d.ts +6 -2
  9. package/dist/anti-detection/injection.d.ts.map +1 -1
  10. package/dist/anti-detection/injection.js +34 -80
  11. package/dist/anti-detection/injection.js.map +1 -1
  12. package/dist/cdp/client.d.ts +2 -2
  13. package/dist/cdp/client.d.ts.map +1 -1
  14. package/dist/cdp/client.js +8 -10
  15. package/dist/cdp/client.js.map +1 -1
  16. package/dist/cdp/index.d.ts.map +1 -1
  17. package/dist/cdp/index.js.map +1 -1
  18. package/dist/cdp/launcher.d.ts.map +1 -1
  19. package/dist/cdp/launcher.js +40 -13
  20. package/dist/cdp/launcher.js.map +1 -1
  21. package/dist/core/auto-wait.d.ts +2 -2
  22. package/dist/core/auto-wait.d.ts.map +1 -1
  23. package/dist/core/auto-wait.js +2 -2
  24. package/dist/core/auto-wait.js.map +1 -1
  25. package/dist/core/browser-driver.d.ts +307 -0
  26. package/dist/core/browser-driver.d.ts.map +1 -0
  27. package/dist/core/browser-driver.js +21 -0
  28. package/dist/core/browser-driver.js.map +1 -0
  29. package/dist/core/error-sanitizer.d.ts +25 -0
  30. package/dist/core/error-sanitizer.d.ts.map +1 -0
  31. package/dist/core/error-sanitizer.js +66 -0
  32. package/dist/core/error-sanitizer.js.map +1 -0
  33. package/dist/core/errors.d.ts +10 -1
  34. package/dist/core/errors.d.ts.map +1 -1
  35. package/dist/core/errors.js +17 -4
  36. package/dist/core/errors.js.map +1 -1
  37. package/dist/core/extension-errors.d.ts +20 -0
  38. package/dist/core/extension-errors.d.ts.map +1 -0
  39. package/dist/core/extension-errors.js +40 -0
  40. package/dist/core/extension-errors.js.map +1 -0
  41. package/dist/core/index.d.ts.map +1 -1
  42. package/dist/core/index.js.map +1 -1
  43. package/dist/core/locator.d.ts +2 -2
  44. package/dist/core/locator.d.ts.map +1 -1
  45. package/dist/core/locator.js +25 -65
  46. package/dist/core/locator.js.map +1 -1
  47. package/dist/core/retry.d.ts +2 -2
  48. package/dist/core/retry.d.ts.map +1 -1
  49. package/dist/core/retry.js +2 -2
  50. package/dist/core/retry.js.map +1 -1
  51. package/dist/core/session.d.ts +149 -46
  52. package/dist/core/session.d.ts.map +1 -1
  53. package/dist/core/session.js +673 -181
  54. package/dist/core/session.js.map +1 -1
  55. package/dist/core/types.d.ts +9 -3
  56. package/dist/core/types.d.ts.map +1 -1
  57. package/dist/core/types.js +13 -6
  58. package/dist/core/types.js.map +1 -1
  59. package/dist/core/unified-session.d.ts +46 -85
  60. package/dist/core/unified-session.d.ts.map +1 -1
  61. package/dist/core/unified-session.js +341 -650
  62. package/dist/core/unified-session.js.map +1 -1
  63. package/dist/core/utils.d.ts +7 -0
  64. package/dist/core/utils.d.ts.map +1 -0
  65. package/dist/core/utils.js +33 -0
  66. package/dist/core/utils.js.map +1 -0
  67. package/dist/extension/bridge.d.ts +69 -52
  68. package/dist/extension/bridge.d.ts.map +1 -1
  69. package/dist/extension/bridge.js +242 -111
  70. package/dist/extension/bridge.js.map +1 -1
  71. package/dist/extension/http-server.d.ts +6 -4
  72. package/dist/extension/http-server.d.ts.map +1 -1
  73. package/dist/extension/http-server.js +45 -31
  74. package/dist/extension/http-server.js.map +1 -1
  75. package/dist/extension/index.d.ts.map +1 -1
  76. package/dist/extension/index.js.map +1 -1
  77. package/dist/index.js +3 -1
  78. package/dist/index.js.map +1 -1
  79. package/dist/tools/browse.d.ts.map +1 -1
  80. package/dist/tools/browse.js +32 -34
  81. package/dist/tools/browse.js.map +1 -1
  82. package/dist/tools/cookies.d.ts.map +1 -1
  83. package/dist/tools/cookies.js +38 -16
  84. package/dist/tools/cookies.js.map +1 -1
  85. package/dist/tools/evaluate.d.ts.map +1 -1
  86. package/dist/tools/evaluate.js +54 -23
  87. package/dist/tools/evaluate.js.map +1 -1
  88. package/dist/tools/extract.d.ts.map +1 -1
  89. package/dist/tools/extract.js +221 -153
  90. package/dist/tools/extract.js.map +1 -1
  91. package/dist/tools/index.d.ts.map +1 -1
  92. package/dist/tools/index.js.map +1 -1
  93. package/dist/tools/input.d.ts.map +1 -1
  94. package/dist/tools/input.js +281 -89
  95. package/dist/tools/input.js.map +1 -1
  96. package/dist/tools/logs.d.ts.map +1 -1
  97. package/dist/tools/logs.js +31 -17
  98. package/dist/tools/logs.js.map +1 -1
  99. package/dist/tools/manage.d.ts.map +1 -1
  100. package/dist/tools/manage.js +25 -28
  101. package/dist/tools/manage.js.map +1 -1
  102. package/dist/tools/schema.d.ts +1 -1
  103. package/dist/tools/schema.d.ts.map +1 -1
  104. package/dist/tools/schema.js +31 -55
  105. package/dist/tools/schema.js.map +1 -1
  106. package/dist/tools/wait.d.ts.map +1 -1
  107. package/dist/tools/wait.js +19 -16
  108. package/dist/tools/wait.js.map +1 -1
  109. package/package.json +48 -40
@@ -4,6 +4,7 @@
4
4
  * 封装与 Chrome Extension 的通信,提供与 CDP 类似的接口
5
5
  * 使用 HTTP + WebSocket 实现
6
6
  */
7
+ import { DriverCapabilityError, } from '../core/browser-driver.js';
7
8
  import { ExtensionHttpServer } from './http-server.js';
8
9
  /** RPC 传输余量(毫秒):给网络往返和 Extension 处理留出的额外时间 */
9
10
  const RPC_MARGIN = 5000;
@@ -26,8 +27,8 @@ export class ExtensionBridge {
26
27
  this.httpServer.on('connected', () => {
27
28
  if (this.currentTabId !== null) {
28
29
  const tabId = this.currentTabId;
29
- this.httpServer.sendCommand('debugger_attach', { tabId }).catch(() => {
30
- // tab 可能已关闭,忽略恢复失败
30
+ this.httpServer.sendCommand('debugger_attach', { tabId }).catch((err) => {
31
+ console.error('[Bridge] 重连后 debugger re-attach 失败:', err.message);
31
32
  });
32
33
  }
33
34
  });
@@ -53,6 +54,25 @@ export class ExtensionBridge {
53
54
  const result = await this.httpServer.sendCommand('tabs_list', {});
54
55
  return result;
55
56
  }
57
+ /** IBrowserDriver 接口:列出所有 tab,统一为 ListedTarget 形式(id 字符串化以保持跨 driver 兼容) */
58
+ async listTargets() {
59
+ const tabs = await this.listTabs();
60
+ return tabs.map((tab) => ({
61
+ id: tab.id,
62
+ targetId: String(tab.id),
63
+ url: tab.url,
64
+ title: tab.title,
65
+ type: 'page',
66
+ active: tab.active,
67
+ windowId: tab.windowId,
68
+ index: tab.index,
69
+ groupId: tab.groupId,
70
+ pinned: tab.pinned,
71
+ incognito: tab.incognito,
72
+ managed: tab.managed,
73
+ status: tab.status,
74
+ }));
75
+ }
56
76
  async createTab(url, timeout) {
57
77
  const rpcTimeout = timeout !== undefined ? timeout + RPC_MARGIN : undefined;
58
78
  const result = await this.httpServer.sendCommand('tabs_create', {
@@ -67,6 +87,16 @@ export class ExtensionBridge {
67
87
  this.updateState(tab.url, tab.title);
68
88
  return tab;
69
89
  }
90
+ /** IBrowserDriver 接口:新建页面(targetId 为字符串化的 chrome tab id) */
91
+ async newPage(url, timeout) {
92
+ const tab = await this.createTab(url, timeout);
93
+ return {
94
+ targetId: String(tab.id),
95
+ url: tab.url,
96
+ title: tab.title,
97
+ type: 'page',
98
+ };
99
+ }
70
100
  async closeTab(tabId) {
71
101
  await this.httpServer.sendCommand('tabs_close', { tabId });
72
102
  if (this.currentTabId === tabId) {
@@ -74,131 +104,164 @@ export class ExtensionBridge {
74
104
  this.state = null;
75
105
  }
76
106
  }
107
+ /** IBrowserDriver 接口:关闭页面(targetId 是 chrome tab id 的字符串形式,省略时关闭当前 tab) */
108
+ async closePage(targetId) {
109
+ const tabId = targetId !== undefined ? this.parseTargetId(targetId) : this.currentTabId;
110
+ if (tabId === null) {
111
+ throw new DriverCapabilityError('没有可关闭的页面,请指定 targetId');
112
+ }
113
+ await this.closeTab(tabId);
114
+ }
77
115
  async activateTab(tabId) {
78
116
  const result = await this.httpServer.sendCommand('tabs_activate', { tabId });
79
117
  const tab = result;
80
118
  this.currentTabId = tab.id;
81
119
  this.updateState(tab.url, tab.title);
82
120
  }
83
- // ==================== 导航操作 ====================
121
+ /** IBrowserDriver 接口:激活页面(切到前台) */
122
+ async activatePage(targetId) {
123
+ const tabId = this.parseTargetId(targetId);
124
+ await this.activateTab(tabId);
125
+ }
126
+ /** IBrowserDriver 接口:选择操作目标 tab(不切换前台,只设置当前 currentTabId) */
127
+ async selectPage(targetId) {
128
+ this.currentTabId = this.parseTargetId(targetId);
129
+ }
130
+ /** IBrowserDriver 接口:获取当前操作目标 ID(chrome tab id 的字符串形式) */
131
+ getCurrentTargetId() {
132
+ return this.currentTabId !== null ? String(this.currentTabId) : null;
133
+ }
134
+ /** IBrowserDriver 接口:设置当前操作目标 ID */
135
+ setCurrentTargetId(targetId) {
136
+ this.currentTabId = targetId !== null ? this.parseTargetId(targetId) : null;
137
+ }
84
138
  async navigate(url, options) {
85
139
  const rpcTimeout = options?.timeout !== undefined ? options.timeout + RPC_MARGIN : undefined;
86
- const result = await this.httpServer.sendCommand('navigate', {
87
- tabId: this.currentTabId,
140
+ const params = {
88
141
  url,
89
- waitUntil: options?.waitUntil ?? 'load',
142
+ waitUntil: options?.wait ?? 'load',
90
143
  timeout: options?.timeout,
91
- }, rpcTimeout);
144
+ };
145
+ if (this.currentTabId !== null) {
146
+ params.tabId = this.currentTabId;
147
+ }
148
+ const result = await this.httpServer.sendCommand('navigate', params, rpcTimeout);
92
149
  const tab = result;
150
+ this.currentTabId = tab.id;
93
151
  this.updateState(tab.url, tab.title);
94
152
  }
153
+ // ==================== 导航操作 ====================
95
154
  async goBack(timeout) {
96
155
  // 默认:NAV_SIGNAL_WINDOW + 导航等待(默认 30s)+ RPC_MARGIN = 40s
97
156
  // 调用方传 timeout 时:timeout 即导航超时 + 信号窗口 + 传输余量
98
- const rpcTimeout = timeout !== undefined ?
99
- timeout + NAV_SIGNAL_WINDOW + RPC_MARGIN :
100
- 30000 + NAV_SIGNAL_WINDOW + RPC_MARGIN;
101
- const result = await this.httpServer.sendCommand('go_back', {
102
- tabId: this.currentTabId,
157
+ const rpcTimeout = timeout !== undefined ? timeout + NAV_SIGNAL_WINDOW + RPC_MARGIN : 30000 + NAV_SIGNAL_WINDOW + RPC_MARGIN;
158
+ const params = {
103
159
  waitUntil: 'load',
104
160
  timeout,
105
- }, rpcTimeout);
161
+ };
162
+ if (this.currentTabId !== null) {
163
+ params.tabId = this.currentTabId;
164
+ }
165
+ const result = (await this.httpServer.sendCommand('go_back', params, rpcTimeout));
106
166
  this.updateState(result.url, result.title);
107
167
  return result;
108
168
  }
109
169
  async goForward(timeout) {
110
170
  // 默认:NAV_SIGNAL_WINDOW + 导航等待(默认 30s)+ RPC_MARGIN = 40s
111
171
  // 调用方传 timeout 时:timeout 即导航超时 + 信号窗口 + 传输余量
112
- const rpcTimeout = timeout !== undefined ?
113
- timeout + NAV_SIGNAL_WINDOW + RPC_MARGIN :
114
- 30000 + NAV_SIGNAL_WINDOW + RPC_MARGIN;
115
- const result = await this.httpServer.sendCommand('go_forward', {
116
- tabId: this.currentTabId,
172
+ const rpcTimeout = timeout !== undefined ? timeout + NAV_SIGNAL_WINDOW + RPC_MARGIN : 30000 + NAV_SIGNAL_WINDOW + RPC_MARGIN;
173
+ const params = {
117
174
  waitUntil: 'load',
118
175
  timeout,
119
- }, rpcTimeout);
176
+ };
177
+ if (this.currentTabId !== null) {
178
+ params.tabId = this.currentTabId;
179
+ }
180
+ const result = (await this.httpServer.sendCommand('go_forward', params, rpcTimeout));
120
181
  this.updateState(result.url, result.title);
121
182
  return result;
122
183
  }
123
184
  async reload(ignoreCache = false, waitUntil, timeout) {
124
185
  const rpcTimeout = timeout !== undefined ? timeout + RPC_MARGIN : undefined;
125
- const result = await this.httpServer.sendCommand('reload', {
126
- tabId: this.currentTabId,
186
+ const params = {
127
187
  ignoreCache,
128
188
  waitUntil: waitUntil ?? 'load',
129
189
  timeout,
130
- }, rpcTimeout);
190
+ };
191
+ if (this.currentTabId !== null) {
192
+ params.tabId = this.currentTabId;
193
+ }
194
+ const result = await this.httpServer.sendCommand('reload', params, rpcTimeout);
131
195
  const tab = result;
132
196
  this.updateState(tab.url, tab.title);
133
197
  }
134
- // ==================== 页面内容 ====================
135
198
  async readPage(options) {
136
- return await this.httpServer.sendCommand('read_page', {
199
+ return (await this.httpServer.sendCommand('read_page', {
137
200
  tabId: this.currentTabId,
138
201
  frameId: this.currentFrameId || undefined,
139
202
  ...options,
140
- });
141
- }
142
- async screenshot(options) {
143
- return await this.httpServer.sendCommand('screenshot', {
144
- tabId: this.currentTabId,
145
- ...options,
146
- });
203
+ }));
147
204
  }
148
- // ==================== DOM 操作 ====================
205
+ // ==================== 页面内容 ====================
149
206
  async click(refId) {
150
- const result = await this.httpServer.sendCommand('click', {
207
+ const result = (await this.httpServer.sendCommand('click', {
151
208
  tabId: this.currentTabId,
152
209
  frameId: this.currentFrameId || undefined,
153
210
  refId,
154
- });
211
+ }));
155
212
  if (!result.success) {
156
213
  throw new Error(result.error || 'Click failed');
157
214
  }
158
215
  }
216
+ // ==================== DOM 操作 ====================
159
217
  async actionableClick(refId, force) {
160
- return await this.httpServer.sendCommand('actionable_click', {
218
+ return (await this.httpServer.sendCommand('actionable_click', {
161
219
  tabId: this.currentTabId,
162
220
  frameId: this.currentFrameId || undefined,
163
221
  refId,
164
222
  force: force ?? false,
165
- });
223
+ }));
166
224
  }
167
- async checkActionability(refId) {
168
- return await this.httpServer.sendCommand('check_actionability', {
225
+ async dispatchInput(refId, text) {
226
+ return (await this.httpServer.sendCommand('dispatch_input', {
169
227
  tabId: this.currentTabId,
170
228
  frameId: this.currentFrameId || undefined,
171
229
  refId,
172
- });
230
+ text,
231
+ }));
173
232
  }
174
- async dispatchInput(refId, text) {
175
- return await this.httpServer.sendCommand('dispatch_input', {
233
+ async dragAndDrop(srcRefId, dstRefId) {
234
+ return (await this.httpServer.sendCommand('drag_and_drop', {
176
235
  tabId: this.currentTabId,
177
236
  frameId: this.currentFrameId || undefined,
178
- refId,
179
- text,
180
- });
237
+ srcRefId,
238
+ dstRefId,
239
+ }));
181
240
  }
182
241
  async getComputedStyle(refId, prop) {
183
- return await this.httpServer.sendCommand('get_computed_style', {
242
+ return (await this.httpServer.sendCommand('get_computed_style', {
184
243
  tabId: this.currentTabId,
185
244
  frameId: this.currentFrameId || undefined,
186
245
  refId,
187
246
  prop,
188
- });
247
+ }));
189
248
  }
190
249
  async type(refId, text, clear = false) {
191
- const result = await this.httpServer.sendCommand('type', {
250
+ const result = (await this.httpServer.sendCommand('type', {
192
251
  tabId: this.currentTabId,
193
252
  frameId: this.currentFrameId || undefined,
194
253
  refId,
195
254
  text,
196
255
  clear,
197
- });
256
+ }));
198
257
  if (!result.success) {
199
258
  throw new Error(result.error || 'Type failed');
200
259
  }
201
260
  }
261
+ /** IBrowserDriver 接口别名(避开与字段名冲突的命名歧义) */
262
+ typeRef(refId, text, clear = false) {
263
+ return this.type(refId, text, clear);
264
+ }
202
265
  async scroll(x, y, refId) {
203
266
  await this.httpServer.sendCommand('scroll', {
204
267
  tabId: this.currentTabId,
@@ -208,84 +271,122 @@ export class ExtensionBridge {
208
271
  refId,
209
272
  });
210
273
  }
211
- async evaluate(code, timeout, budget) {
212
- // budget 覆盖 rpcTimeout:轮询调用方传入端到端预算;一次性调用不传,保留 RPC_MARGIN
213
- const rpcTimeout = budget ?? (timeout !== undefined ? timeout + RPC_MARGIN : undefined);
214
- const result = await this.httpServer.sendCommand('evaluate', {
274
+ /** IBrowserDriver 接口别名 */
275
+ scrollAt(x, y, refId) {
276
+ return this.scroll(x, y, refId);
277
+ }
278
+ async evaluate(code, _args, timeout) {
279
+ // _args 参数仅满足 IBrowserDriver 接口签名;ExtensionBridge 不消费 args(stealth 路径上层已字符串拼接)
280
+ const rpcTimeout = timeout !== undefined ? timeout + RPC_MARGIN : undefined;
281
+ const result = (await this.httpServer.sendCommand('evaluate', {
215
282
  tabId: this.currentTabId,
216
283
  frameId: this.currentFrameId || undefined,
217
284
  code,
218
285
  timeout,
219
- }, rpcTimeout);
286
+ }, rpcTimeout));
220
287
  if (!result.success) {
221
288
  throw new Error(result.error || 'Evaluate failed');
222
289
  }
223
- return result.result ? JSON.parse(result.result) : undefined;
290
+ if (!result.result) {
291
+ return undefined;
292
+ }
293
+ try {
294
+ return JSON.parse(result.result);
295
+ }
296
+ catch (err) {
297
+ throw new Error(`evaluate 结果 JSON 解析失败: ${err}`);
298
+ }
224
299
  }
225
300
  async find(selector, text, xpath, timeout) {
226
- return await this.httpServer.sendCommand('find', {
301
+ return (await this.httpServer.sendCommand('find', {
227
302
  tabId: this.currentTabId,
228
303
  frameId: this.currentFrameId || undefined,
229
304
  selector,
230
305
  text,
231
306
  xpath,
232
- }, timeout);
307
+ }, timeout));
233
308
  }
234
309
  async getText(selector) {
235
- const result = await this.httpServer.sendCommand('get_text', {
310
+ const result = (await this.httpServer.sendCommand('get_text', {
236
311
  tabId: this.currentTabId,
237
312
  frameId: this.currentFrameId || undefined,
238
313
  selector,
239
- });
314
+ }));
240
315
  return result.text;
241
316
  }
242
317
  async getHtml(selector, outer = true) {
243
- const result = await this.httpServer.sendCommand('get_html', {
318
+ const result = (await this.httpServer.sendCommand('get_html', {
244
319
  tabId: this.currentTabId,
245
320
  frameId: this.currentFrameId || undefined,
246
321
  selector,
247
322
  outer,
248
- });
323
+ }));
249
324
  return result.html;
250
325
  }
326
+ getPageHtml(selector, outer = true) {
327
+ return this.getHtml(selector, outer);
328
+ }
329
+ getPageText(selector) {
330
+ return this.getText(selector);
331
+ }
332
+ async getConsoleLogs(options = {}) {
333
+ const messages = await this.consoleGet(options);
334
+ return messages;
335
+ }
336
+ async getNetworkRequests(options = {}) {
337
+ const requests = await this.networkGet(options);
338
+ return requests;
339
+ }
340
+ async screenshot(options) {
341
+ return (await this.httpServer.sendCommand('screenshot', {
342
+ tabId: this.currentTabId,
343
+ ...options,
344
+ }));
345
+ }
251
346
  async getHtmlWithImages(selector, outer = true) {
252
- return await this.httpServer.sendCommand('get_html_with_images', {
347
+ return (await this.httpServer.sendCommand('get_html_with_images', {
253
348
  tabId: this.currentTabId,
254
349
  frameId: this.currentFrameId || undefined,
255
350
  selector,
256
351
  outer,
257
- });
352
+ }));
258
353
  }
259
354
  async getAttribute(selector, refId, attribute) {
260
- const result = await this.httpServer.sendCommand('get_attribute', {
355
+ const result = (await this.httpServer.sendCommand('get_attribute', {
261
356
  tabId: this.currentTabId,
262
357
  frameId: this.currentFrameId || undefined,
263
358
  selector,
264
359
  refId,
265
360
  attribute,
266
- });
361
+ }));
267
362
  return result.value;
268
363
  }
269
364
  async getMetadata() {
270
- return await this.httpServer.sendCommand('get_metadata', {
365
+ return (await this.httpServer.sendCommand('get_metadata', {
271
366
  tabId: this.currentTabId,
272
367
  frameId: this.currentFrameId || undefined,
273
- });
368
+ }));
274
369
  }
275
- // ==================== Cookies ====================
276
370
  async getCookies(filter) {
277
- return await this.httpServer.sendCommand('cookies_get', filter ?? {});
371
+ return (await this.httpServer.sendCommand('cookies_get', filter ?? {}));
278
372
  }
373
+ // ==================== Cookies ====================
279
374
  async setCookie(params) {
280
- await this.httpServer.sendCommand('cookies_set', params);
375
+ const sameSiteMap = { None: 'no_restriction', Lax: 'lax', Strict: 'strict' };
376
+ const chromeSameSite = params.sameSite ? (sameSiteMap[params.sameSite] ?? params.sameSite) : undefined;
377
+ const url = params.url ?? this.state?.url ?? 'http://localhost';
378
+ await this.httpServer.sendCommand('cookies_set', {
379
+ ...params,
380
+ url,
381
+ ...(chromeSameSite !== undefined && { sameSite: chromeSameSite }),
382
+ });
281
383
  }
282
384
  async deleteCookie(url, name) {
283
385
  await this.httpServer.sendCommand('cookies_delete', { url, name });
284
386
  }
285
387
  async clearCookies(filter) {
286
- return await this.httpServer.sendCommand('cookies_clear', filter ?? {});
388
+ return (await this.httpServer.sendCommand('cookies_clear', filter ?? {}));
287
389
  }
288
- // ==================== Debugger (CDP via Extension) ====================
289
390
  async debuggerSend(method, params, tabId, timeout) {
290
391
  return await this.httpServer.sendCommand('debugger_send', {
291
392
  tabId: tabId ?? this.currentTabId,
@@ -293,7 +394,7 @@ export class ExtensionBridge {
293
394
  params,
294
395
  }, timeout);
295
396
  }
296
- // ==================== 输入事件(通过 CDP)====================
397
+ // ==================== Debugger (CDP via Extension) ====================
297
398
  async inputKey(type, options = {}) {
298
399
  await this.httpServer.sendCommand('input_key', {
299
400
  tabId: this.currentTabId,
@@ -301,6 +402,7 @@ export class ExtensionBridge {
301
402
  ...options,
302
403
  });
303
404
  }
405
+ // ==================== 输入事件(通过 CDP)====================
304
406
  async inputMouse(type, x, y, options = {}) {
305
407
  await this.httpServer.sendCommand('input_mouse', {
306
408
  tabId: this.currentTabId,
@@ -324,85 +426,107 @@ export class ExtensionBridge {
324
426
  delay,
325
427
  });
326
428
  }
327
- // ==================== 控制台日志 ====================
328
429
  async consoleEnable() {
329
430
  await this.httpServer.sendCommand('console_enable', {
330
431
  tabId: this.currentTabId,
331
432
  });
332
433
  }
434
+ // ==================== 控制台日志 ====================
333
435
  async consoleGet(options = {}) {
334
- const result = await this.httpServer.sendCommand('console_get', {
436
+ const result = (await this.httpServer.sendCommand('console_get', {
335
437
  tabId: this.currentTabId,
336
438
  ...options,
337
- });
439
+ }));
338
440
  return result.messages;
339
441
  }
340
- // ==================== 网络日志 ====================
341
442
  async networkEnable() {
342
443
  await this.httpServer.sendCommand('network_enable', {
343
444
  tabId: this.currentTabId,
344
445
  });
345
446
  }
447
+ // ==================== 网络日志 ====================
346
448
  async networkGet(options = {}) {
347
- const result = await this.httpServer.sendCommand('network_get', {
449
+ const result = (await this.httpServer.sendCommand('network_get', {
348
450
  tabId: this.currentTabId,
349
451
  ...options,
350
- });
452
+ }));
351
453
  return result.requests;
352
454
  }
353
- // ==================== Stealth 模式(JS 事件模拟,无 debugger)====================
354
455
  async stealthType(text, delay = 0) {
355
- await this.httpServer.sendCommand('stealth_type', {
356
- tabId: this.currentTabId,
357
- frameId: this.currentFrameId || undefined,
456
+ const params = {
358
457
  text,
359
458
  delay,
360
- });
459
+ };
460
+ if (this.currentTabId !== null) {
461
+ params.tabId = this.currentTabId;
462
+ }
463
+ if (this.currentFrameId !== null) {
464
+ params.frameId = this.currentFrameId;
465
+ }
466
+ await this.httpServer.sendCommand('stealth_type', params);
361
467
  }
468
+ // ==================== Stealth 模式(JS 事件模拟,无 debugger)====================
362
469
  async stealthKey(key, type = 'press', modifiers = []) {
363
- await this.httpServer.sendCommand('stealth_key', {
364
- tabId: this.currentTabId,
365
- frameId: this.currentFrameId || undefined,
470
+ const params = {
366
471
  key,
367
472
  type,
368
473
  modifiers,
369
- });
474
+ };
475
+ if (this.currentTabId !== null) {
476
+ params.tabId = this.currentTabId;
477
+ }
478
+ if (this.currentFrameId !== null) {
479
+ params.frameId = this.currentFrameId;
480
+ }
481
+ await this.httpServer.sendCommand('stealth_key', params);
370
482
  }
371
- async stealthClick(x, y, button = 'left') {
372
- await this.httpServer.sendCommand('stealth_click', {
373
- tabId: this.currentTabId,
374
- frameId: this.currentFrameId || undefined,
483
+ async stealthClick(x, y, button = 'left', clickCount = 1, refId) {
484
+ const params = {
375
485
  x,
376
486
  y,
377
487
  button,
378
- });
488
+ clickCount,
489
+ };
490
+ if (this.currentTabId !== null) {
491
+ params.tabId = this.currentTabId;
492
+ }
493
+ if (this.currentFrameId !== null) {
494
+ params.frameId = this.currentFrameId;
495
+ }
496
+ if (typeof refId === 'string') {
497
+ params.refId = refId;
498
+ }
499
+ await this.httpServer.sendCommand('stealth_click', params);
379
500
  }
380
501
  async stealthMouse(type, x, y, button = 'left') {
381
- await this.httpServer.sendCommand('stealth_mouse', {
382
- tabId: this.currentTabId,
383
- frameId: this.currentFrameId || undefined,
502
+ const params = {
384
503
  type,
385
504
  x,
386
505
  y,
387
506
  button,
388
- });
507
+ };
508
+ if (this.currentTabId !== null) {
509
+ params.tabId = this.currentTabId;
510
+ }
511
+ if (this.currentFrameId !== null) {
512
+ params.frameId = this.currentFrameId;
513
+ }
514
+ await this.httpServer.sendCommand('stealth_mouse', params);
389
515
  }
390
516
  async stealthInject() {
391
- await this.httpServer.sendCommand('stealth_inject', {
392
- tabId: this.currentTabId,
393
- frameId: this.currentFrameId || undefined,
394
- });
517
+ const params = {};
518
+ if (this.currentTabId !== null) {
519
+ params.tabId = this.currentTabId;
520
+ }
521
+ if (this.currentFrameId !== null) {
522
+ params.frameId = this.currentFrameId;
523
+ }
524
+ await this.httpServer.sendCommand('stealth_inject', params);
395
525
  }
396
- // ==================== 状态管理 ====================
397
526
  getState() {
398
527
  return this.state;
399
528
  }
400
- getCurrentTabId() {
401
- return this.currentTabId;
402
- }
403
- setCurrentTabId(tabId) {
404
- this.currentTabId = tabId;
405
- }
529
+ // ==================== 状态管理 ====================
406
530
  getCurrentFrameId() {
407
531
  return this.currentFrameId;
408
532
  }
@@ -413,23 +537,30 @@ export class ExtensionBridge {
413
537
  * 在指定 iframe 中以 precise 模式执行 JS(通过 contextId 绕过 CSP)
414
538
  */
415
539
  async evaluateInFrame(frameId, expression, timeout) {
416
- return await this.httpServer.sendCommand('evaluate_in_frame', {
540
+ return (await this.httpServer.sendCommand('evaluate_in_frame', {
417
541
  tabId: this.currentTabId,
418
542
  frameId,
419
543
  expression,
420
544
  returnByValue: true,
421
545
  awaitPromise: true,
422
546
  timeout,
423
- }, timeout);
547
+ }, timeout));
424
548
  }
425
549
  /**
426
550
  * 解析 iframe 选择器/索引 → frameId
427
551
  */
428
552
  async resolveFrame(frame) {
429
- return await this.httpServer.sendCommand('resolve_frame', {
553
+ return (await this.httpServer.sendCommand('resolve_frame', {
430
554
  tabId: this.currentTabId,
431
555
  frame,
432
- });
556
+ }));
557
+ }
558
+ parseTargetId(targetId) {
559
+ const parsed = parseInt(targetId, 10);
560
+ if (isNaN(parsed)) {
561
+ throw new DriverCapabilityError(`无效的 Tab ID: ${targetId}`);
562
+ }
563
+ return parsed;
433
564
  }
434
565
  updateState(url, title) {
435
566
  this.state = { url, title };