@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.
Files changed (75) hide show
  1. package/README.md +87 -43
  2. package/dist/core/browser-driver.d.ts +118 -1
  3. package/dist/core/browser-driver.d.ts.map +1 -1
  4. package/dist/core/browser-driver.js +11 -0
  5. package/dist/core/browser-driver.js.map +1 -1
  6. package/dist/core/error-sanitizer.js +2 -2
  7. package/dist/core/error-sanitizer.js.map +1 -1
  8. package/dist/core/errors.d.ts.map +1 -1
  9. package/dist/core/errors.js +17 -4
  10. package/dist/core/errors.js.map +1 -1
  11. package/dist/core/index.d.ts +1 -0
  12. package/dist/core/index.d.ts.map +1 -1
  13. package/dist/core/index.js +1 -0
  14. package/dist/core/index.js.map +1 -1
  15. package/dist/core/locator.d.ts.map +1 -1
  16. package/dist/core/locator.js +4 -3
  17. package/dist/core/locator.js.map +1 -1
  18. package/dist/core/session.d.ts +5 -2
  19. package/dist/core/session.d.ts.map +1 -1
  20. package/dist/core/session.js +107 -4
  21. package/dist/core/session.js.map +1 -1
  22. package/dist/core/types.d.ts +17 -1
  23. package/dist/core/types.d.ts.map +1 -1
  24. package/dist/core/types.js +57 -4
  25. package/dist/core/types.js.map +1 -1
  26. package/dist/core/unified-session.d.ts +29 -9
  27. package/dist/core/unified-session.d.ts.map +1 -1
  28. package/dist/core/unified-session.js +226 -18
  29. package/dist/core/unified-session.js.map +1 -1
  30. package/dist/core/utils.d.ts +13 -0
  31. package/dist/core/utils.d.ts.map +1 -1
  32. package/dist/core/utils.js +121 -0
  33. package/dist/core/utils.js.map +1 -1
  34. package/dist/extension/bridge.d.ts +37 -22
  35. package/dist/extension/bridge.d.ts.map +1 -1
  36. package/dist/extension/bridge.js +256 -96
  37. package/dist/extension/bridge.js.map +1 -1
  38. package/dist/extension/http-server.d.ts.map +1 -1
  39. package/dist/extension/http-server.js +39 -10
  40. package/dist/extension/http-server.js.map +1 -1
  41. package/dist/tools/browse.d.ts.map +1 -1
  42. package/dist/tools/browse.js +160 -55
  43. package/dist/tools/browse.js.map +1 -1
  44. package/dist/tools/cookies.d.ts.map +1 -1
  45. package/dist/tools/cookies.js +8 -11
  46. package/dist/tools/cookies.js.map +1 -1
  47. package/dist/tools/evaluate.d.ts.map +1 -1
  48. package/dist/tools/evaluate.js +53 -30
  49. package/dist/tools/evaluate.js.map +1 -1
  50. package/dist/tools/extract.d.ts +1 -0
  51. package/dist/tools/extract.d.ts.map +1 -1
  52. package/dist/tools/extract.js +476 -244
  53. package/dist/tools/extract.js.map +1 -1
  54. package/dist/tools/input.d.ts.map +1 -1
  55. package/dist/tools/input.js +798 -441
  56. package/dist/tools/input.js.map +1 -1
  57. package/dist/tools/logs.d.ts.map +1 -1
  58. package/dist/tools/logs.js +79 -31
  59. package/dist/tools/logs.js.map +1 -1
  60. package/dist/tools/manage.d.ts +0 -3
  61. package/dist/tools/manage.d.ts.map +1 -1
  62. package/dist/tools/manage.js +339 -285
  63. package/dist/tools/manage.js.map +1 -1
  64. package/dist/tools/png.d.ts +16 -0
  65. package/dist/tools/png.d.ts.map +1 -0
  66. package/dist/tools/png.js +197 -0
  67. package/dist/tools/png.js.map +1 -0
  68. package/dist/tools/schema.d.ts +14 -112
  69. package/dist/tools/schema.d.ts.map +1 -1
  70. package/dist/tools/schema.js +21 -12
  71. package/dist/tools/schema.js.map +1 -1
  72. package/dist/tools/wait.d.ts.map +1 -1
  73. package/dist/tools/wait.js +73 -13
  74. package/dist/tools/wait.js.map +1 -1
  75. package/package.json +11 -11
@@ -54,24 +54,25 @@ export class ExtensionBridge {
54
54
  const result = await this.httpServer.sendCommand('tabs_list', {});
55
55
  return result;
56
56
  }
57
+ async listWindows() {
58
+ const result = await this.httpServer.sendCommand('tabs_topology', {});
59
+ const topology = result;
60
+ return {
61
+ ...topology,
62
+ windows: topology.windows.map((window) => ({
63
+ ...window,
64
+ tabs: window.tabs.map((tab) => ({
65
+ ...tab,
66
+ targetId: String(tab.id),
67
+ type: tab.type ?? 'page',
68
+ })),
69
+ })),
70
+ };
71
+ }
57
72
  /** IBrowserDriver 接口:列出所有 tab,统一为 ListedTarget 形式(id 字符串化以保持跨 driver 兼容) */
58
73
  async listTargets() {
59
74
  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
+ return tabs.map((tab) => this.tabToTarget(tab));
75
76
  }
76
77
  async createTab(url, timeout) {
77
78
  const rpcTimeout = timeout !== undefined ? timeout + RPC_MARGIN : undefined;
@@ -84,7 +85,7 @@ export class ExtensionBridge {
84
85
  const tab = result;
85
86
  // 自动切换到新创建的 tab,后续操作立即生效
86
87
  this.currentTabId = tab.id;
87
- this.updateState(tab.url, tab.title);
88
+ this.updateState(tab);
88
89
  return tab;
89
90
  }
90
91
  /** IBrowserDriver 接口:新建页面(targetId 为字符串化的 chrome tab id) */
@@ -95,14 +96,16 @@ export class ExtensionBridge {
95
96
  url: tab.url,
96
97
  title: tab.title,
97
98
  type: 'page',
99
+ managed: tab.managed,
98
100
  };
99
101
  }
100
102
  async closeTab(tabId) {
101
- await this.httpServer.sendCommand('tabs_close', { tabId });
103
+ const result = (await this.httpServer.sendCommand('tabs_close', { tabId }));
102
104
  if (this.currentTabId === tabId) {
103
105
  this.currentTabId = null;
104
106
  this.state = null;
105
107
  }
108
+ return this.normalizePageChange(result);
106
109
  }
107
110
  /** IBrowserDriver 接口:关闭页面(targetId 是 chrome tab id 的字符串形式,省略时关闭当前 tab) */
108
111
  async closePage(targetId) {
@@ -110,13 +113,104 @@ export class ExtensionBridge {
110
113
  if (tabId === null) {
111
114
  throw new DriverCapabilityError('没有可关闭的页面,请指定 targetId');
112
115
  }
113
- await this.closeTab(tabId);
116
+ return this.closeTab(tabId);
117
+ }
118
+ async adoptPage(targetId) {
119
+ const tabId = this.parseTargetId(targetId);
120
+ const result = (await this.httpServer.sendCommand('tabs_adopt', { tabId }));
121
+ this.currentTabId = result.tab.id;
122
+ this.updateState(result.tab);
123
+ return {
124
+ target: this.tabToTarget(result.tab),
125
+ managedBefore: result.managedBefore,
126
+ managedAfter: result.managedAfter,
127
+ };
128
+ }
129
+ async releasePage(targetId) {
130
+ const tabId = this.parseTargetId(targetId);
131
+ const result = (await this.httpServer.sendCommand('tabs_release', { tabId }));
132
+ if (this.currentTabId === result.tab.id) {
133
+ this.currentTabId = null;
134
+ this.state = null;
135
+ }
136
+ return {
137
+ target: this.tabToTarget(result.tab),
138
+ managedBefore: result.managedBefore,
139
+ managedAfter: result.managedAfter,
140
+ };
141
+ }
142
+ async movePage(targetId, options) {
143
+ const tabId = this.parseTargetId(targetId);
144
+ const result = (await this.httpServer.sendCommand('tabs_move', {
145
+ tabId,
146
+ windowId: options.windowId,
147
+ index: options.index,
148
+ active: options.activate,
149
+ }));
150
+ return this.normalizePageChange(result);
151
+ }
152
+ async reorderPage(targetId, index) {
153
+ const tabId = this.parseTargetId(targetId);
154
+ const result = (await this.httpServer.sendCommand('tabs_reorder', {
155
+ tabId,
156
+ index,
157
+ }));
158
+ return this.normalizePageChange(result);
159
+ }
160
+ async pinPage(targetId, pinned) {
161
+ const tabId = this.parseTargetId(targetId);
162
+ const result = (await this.httpServer.sendCommand('tabs_pin', { tabId, pinned }));
163
+ return this.normalizePageChange(result);
164
+ }
165
+ async focusWindow(windowId) {
166
+ const result = (await this.httpServer.sendCommand('window_focus', { windowId }));
167
+ return this.normalizePageChange(result);
168
+ }
169
+ async resizeWindow(windowId, options) {
170
+ const result = (await this.httpServer.sendCommand('window_resize', {
171
+ windowId,
172
+ ...options,
173
+ }));
174
+ return this.normalizePageChange(result);
175
+ }
176
+ async newWindow(options) {
177
+ const result = (await this.httpServer.sendCommand('window_create', options));
178
+ if (result.targetId) {
179
+ this.currentTabId = this.parseTargetId(result.targetId);
180
+ const after = result.after;
181
+ const tab = after?.tabs.find((item) => String(item.id) === result.targetId);
182
+ if (tab) {
183
+ this.updateState(tab);
184
+ }
185
+ }
186
+ return this.normalizePageChange(result);
187
+ }
188
+ async closeWindow(windowId) {
189
+ const result = (await this.httpServer.sendCommand('window_close', { windowId }));
190
+ const before = result.before;
191
+ if (before?.tabs.some((tab) => tab.id === this.currentTabId)) {
192
+ this.currentTabId = null;
193
+ this.state = null;
194
+ }
195
+ return this.normalizePageChange(result);
196
+ }
197
+ async activatePageWithAffected(targetId) {
198
+ const tabId = this.parseTargetId(targetId);
199
+ const result = (await this.httpServer.sendCommand('tabs_activate_managed', {
200
+ tabId,
201
+ }));
202
+ this.currentTabId = tabId;
203
+ const after = result.after;
204
+ if (after) {
205
+ this.updateState(after);
206
+ }
207
+ return this.normalizePageChange(result);
114
208
  }
115
209
  async activateTab(tabId) {
116
210
  const result = await this.httpServer.sendCommand('tabs_activate', { tabId });
117
211
  const tab = result;
118
212
  this.currentTabId = tab.id;
119
- this.updateState(tab.url, tab.title);
213
+ this.updateState(tab);
120
214
  }
121
215
  /** IBrowserDriver 接口:激活页面(切到前台) */
122
216
  async activatePage(targetId) {
@@ -125,7 +219,14 @@ export class ExtensionBridge {
125
219
  }
126
220
  /** IBrowserDriver 接口:选择操作目标 tab(不切换前台,只设置当前 currentTabId) */
127
221
  async selectPage(targetId) {
128
- this.currentTabId = this.parseTargetId(targetId);
222
+ const tabId = this.parseTargetId(targetId);
223
+ const tabs = await this.listTabs();
224
+ const tab = tabs.find((item) => item.id === tabId);
225
+ if (!tab) {
226
+ throw new DriverCapabilityError(`Tab ${targetId} 不存在,请先 browse(action="list") 查看可用页面`);
227
+ }
228
+ this.currentTabId = tab.id;
229
+ this.updateState(tab);
129
230
  }
130
231
  /** IBrowserDriver 接口:获取当前操作目标 ID(chrome tab id 的字符串形式) */
131
232
  getCurrentTargetId() {
@@ -134,70 +235,77 @@ export class ExtensionBridge {
134
235
  /** IBrowserDriver 接口:设置当前操作目标 ID */
135
236
  setCurrentTargetId(targetId) {
136
237
  this.currentTabId = targetId !== null ? this.parseTargetId(targetId) : null;
238
+ if (targetId === null) {
239
+ this.state = null;
240
+ }
137
241
  }
138
242
  async navigate(url, options) {
243
+ if (this.currentTabId === null) {
244
+ throw new DriverCapabilityError('没有当前页面,请先 browse attach 或先 browse open 创建受控页面');
245
+ }
139
246
  const rpcTimeout = options?.timeout !== undefined ? options.timeout + RPC_MARGIN : undefined;
140
247
  const params = {
248
+ tabId: this.requireCurrentTabId(),
141
249
  url,
142
250
  waitUntil: options?.wait ?? 'load',
143
251
  timeout: options?.timeout,
144
252
  };
145
- if (this.currentTabId !== null) {
146
- params.tabId = this.currentTabId;
147
- }
148
253
  const result = await this.httpServer.sendCommand('navigate', params, rpcTimeout);
149
254
  const tab = result;
150
255
  this.currentTabId = tab.id;
151
- this.updateState(tab.url, tab.title);
256
+ this.updateState(tab);
152
257
  }
153
258
  // ==================== 导航操作 ====================
154
259
  async goBack(timeout) {
260
+ if (this.currentTabId === null) {
261
+ throw new DriverCapabilityError('没有当前页面,请先 browse attach 或先 browse open 创建受控页面');
262
+ }
155
263
  // 默认:NAV_SIGNAL_WINDOW + 导航等待(默认 30s)+ RPC_MARGIN = 40s
156
264
  // 调用方传 timeout 时:timeout 即导航超时 + 信号窗口 + 传输余量
157
265
  const rpcTimeout = timeout !== undefined ? timeout + NAV_SIGNAL_WINDOW + RPC_MARGIN : 30000 + NAV_SIGNAL_WINDOW + RPC_MARGIN;
158
266
  const params = {
267
+ tabId: this.requireCurrentTabId(),
159
268
  waitUntil: 'load',
160
269
  timeout,
161
270
  };
162
- if (this.currentTabId !== null) {
163
- params.tabId = this.currentTabId;
164
- }
165
271
  const result = (await this.httpServer.sendCommand('go_back', params, rpcTimeout));
166
- this.updateState(result.url, result.title);
272
+ this.updateState(result);
167
273
  return result;
168
274
  }
169
275
  async goForward(timeout) {
276
+ if (this.currentTabId === null) {
277
+ throw new DriverCapabilityError('没有当前页面,请先 browse attach 或先 browse open 创建受控页面');
278
+ }
170
279
  // 默认:NAV_SIGNAL_WINDOW + 导航等待(默认 30s)+ RPC_MARGIN = 40s
171
280
  // 调用方传 timeout 时:timeout 即导航超时 + 信号窗口 + 传输余量
172
281
  const rpcTimeout = timeout !== undefined ? timeout + NAV_SIGNAL_WINDOW + RPC_MARGIN : 30000 + NAV_SIGNAL_WINDOW + RPC_MARGIN;
173
282
  const params = {
283
+ tabId: this.requireCurrentTabId(),
174
284
  waitUntil: 'load',
175
285
  timeout,
176
286
  };
177
- if (this.currentTabId !== null) {
178
- params.tabId = this.currentTabId;
179
- }
180
287
  const result = (await this.httpServer.sendCommand('go_forward', params, rpcTimeout));
181
- this.updateState(result.url, result.title);
288
+ this.updateState(result);
182
289
  return result;
183
290
  }
184
291
  async reload(ignoreCache = false, waitUntil, timeout) {
292
+ if (this.currentTabId === null) {
293
+ throw new DriverCapabilityError('没有当前页面,请先 browse attach 或先 browse open 创建受控页面');
294
+ }
185
295
  const rpcTimeout = timeout !== undefined ? timeout + RPC_MARGIN : undefined;
186
296
  const params = {
297
+ tabId: this.requireCurrentTabId(),
187
298
  ignoreCache,
188
299
  waitUntil: waitUntil ?? 'load',
189
300
  timeout,
190
301
  };
191
- if (this.currentTabId !== null) {
192
- params.tabId = this.currentTabId;
193
- }
194
302
  const result = await this.httpServer.sendCommand('reload', params, rpcTimeout);
195
303
  const tab = result;
196
- this.updateState(tab.url, tab.title);
304
+ this.updateState(tab);
197
305
  }
198
306
  async readPage(options) {
199
307
  return (await this.httpServer.sendCommand('read_page', {
200
- tabId: this.currentTabId,
308
+ tabId: this.requireCurrentTabId(),
201
309
  frameId: this.currentFrameId || undefined,
202
310
  ...options,
203
311
  }));
@@ -205,7 +313,7 @@ export class ExtensionBridge {
205
313
  // ==================== 页面内容 ====================
206
314
  async click(refId) {
207
315
  const result = (await this.httpServer.sendCommand('click', {
208
- tabId: this.currentTabId,
316
+ tabId: this.requireCurrentTabId(),
209
317
  frameId: this.currentFrameId || undefined,
210
318
  refId,
211
319
  }));
@@ -216,15 +324,23 @@ export class ExtensionBridge {
216
324
  // ==================== DOM 操作 ====================
217
325
  async actionableClick(refId, force) {
218
326
  return (await this.httpServer.sendCommand('actionable_click', {
219
- tabId: this.currentTabId,
327
+ tabId: this.requireCurrentTabId(),
220
328
  frameId: this.currentFrameId || undefined,
221
329
  refId,
222
330
  force: force ?? false,
223
331
  }));
224
332
  }
333
+ async checkActionability(refId) {
334
+ const result = (await this.httpServer.sendCommand('check_actionability', {
335
+ tabId: this.requireCurrentTabId(),
336
+ frameId: this.currentFrameId || undefined,
337
+ refId,
338
+ }));
339
+ return { ...result, success: result.success ?? result.actionable === true };
340
+ }
225
341
  async dispatchInput(refId, text) {
226
342
  return (await this.httpServer.sendCommand('dispatch_input', {
227
- tabId: this.currentTabId,
343
+ tabId: this.requireCurrentTabId(),
228
344
  frameId: this.currentFrameId || undefined,
229
345
  refId,
230
346
  text,
@@ -232,7 +348,7 @@ export class ExtensionBridge {
232
348
  }
233
349
  async dragAndDrop(srcRefId, dstRefId) {
234
350
  return (await this.httpServer.sendCommand('drag_and_drop', {
235
- tabId: this.currentTabId,
351
+ tabId: this.requireCurrentTabId(),
236
352
  frameId: this.currentFrameId || undefined,
237
353
  srcRefId,
238
354
  dstRefId,
@@ -240,7 +356,7 @@ export class ExtensionBridge {
240
356
  }
241
357
  async getComputedStyle(refId, prop) {
242
358
  return (await this.httpServer.sendCommand('get_computed_style', {
243
- tabId: this.currentTabId,
359
+ tabId: this.requireCurrentTabId(),
244
360
  frameId: this.currentFrameId || undefined,
245
361
  refId,
246
362
  prop,
@@ -248,7 +364,7 @@ export class ExtensionBridge {
248
364
  }
249
365
  async type(refId, text, clear = false) {
250
366
  const result = (await this.httpServer.sendCommand('type', {
251
- tabId: this.currentTabId,
367
+ tabId: this.requireCurrentTabId(),
252
368
  frameId: this.currentFrameId || undefined,
253
369
  refId,
254
370
  text,
@@ -264,7 +380,7 @@ export class ExtensionBridge {
264
380
  }
265
381
  async scroll(x, y, refId) {
266
382
  await this.httpServer.sendCommand('scroll', {
267
- tabId: this.currentTabId,
383
+ tabId: this.requireCurrentTabId(),
268
384
  frameId: this.currentFrameId || undefined,
269
385
  x,
270
386
  y,
@@ -279,7 +395,7 @@ export class ExtensionBridge {
279
395
  // _args 参数仅满足 IBrowserDriver 接口签名;ExtensionBridge 不消费 args(stealth 路径上层已字符串拼接)
280
396
  const rpcTimeout = timeout !== undefined ? timeout + RPC_MARGIN : undefined;
281
397
  const result = (await this.httpServer.sendCommand('evaluate', {
282
- tabId: this.currentTabId,
398
+ tabId: this.requireCurrentTabId(),
283
399
  frameId: this.currentFrameId || undefined,
284
400
  code,
285
401
  timeout,
@@ -294,12 +410,12 @@ export class ExtensionBridge {
294
410
  return JSON.parse(result.result);
295
411
  }
296
412
  catch (err) {
297
- throw new Error(`evaluate 结果 JSON 解析失败: ${err}`);
413
+ throw new Error(`evaluate 结果 JSON 解析失败: ${err}`, { cause: err });
298
414
  }
299
415
  }
300
416
  async find(selector, text, xpath, timeout) {
301
417
  return (await this.httpServer.sendCommand('find', {
302
- tabId: this.currentTabId,
418
+ tabId: this.requireCurrentTabId(),
303
419
  frameId: this.currentFrameId || undefined,
304
420
  selector,
305
421
  text,
@@ -308,7 +424,7 @@ export class ExtensionBridge {
308
424
  }
309
425
  async getText(selector) {
310
426
  const result = (await this.httpServer.sendCommand('get_text', {
311
- tabId: this.currentTabId,
427
+ tabId: this.requireCurrentTabId(),
312
428
  frameId: this.currentFrameId || undefined,
313
429
  selector,
314
430
  }));
@@ -316,7 +432,7 @@ export class ExtensionBridge {
316
432
  }
317
433
  async getHtml(selector, outer = true) {
318
434
  const result = (await this.httpServer.sendCommand('get_html', {
319
- tabId: this.currentTabId,
435
+ tabId: this.requireCurrentTabId(),
320
436
  frameId: this.currentFrameId || undefined,
321
437
  selector,
322
438
  outer,
@@ -339,13 +455,13 @@ export class ExtensionBridge {
339
455
  }
340
456
  async screenshot(options) {
341
457
  return (await this.httpServer.sendCommand('screenshot', {
342
- tabId: this.currentTabId,
458
+ tabId: this.requireCurrentTabId(),
343
459
  ...options,
344
460
  }));
345
461
  }
346
462
  async getHtmlWithImages(selector, outer = true) {
347
463
  return (await this.httpServer.sendCommand('get_html_with_images', {
348
- tabId: this.currentTabId,
464
+ tabId: this.requireCurrentTabId(),
349
465
  frameId: this.currentFrameId || undefined,
350
466
  selector,
351
467
  outer,
@@ -353,7 +469,7 @@ export class ExtensionBridge {
353
469
  }
354
470
  async getAttribute(selector, refId, attribute) {
355
471
  const result = (await this.httpServer.sendCommand('get_attribute', {
356
- tabId: this.currentTabId,
472
+ tabId: this.requireCurrentTabId(),
357
473
  frameId: this.currentFrameId || undefined,
358
474
  selector,
359
475
  refId,
@@ -363,10 +479,15 @@ export class ExtensionBridge {
363
479
  }
364
480
  async getMetadata() {
365
481
  return (await this.httpServer.sendCommand('get_metadata', {
366
- tabId: this.currentTabId,
482
+ tabId: this.requireCurrentTabId(),
367
483
  frameId: this.currentFrameId || undefined,
368
484
  }));
369
485
  }
486
+ async getFrames() {
487
+ return (await this.httpServer.sendCommand('get_all_frames', {
488
+ tabId: this.requireCurrentTabId(),
489
+ }));
490
+ }
370
491
  async getCookies(filter) {
371
492
  return (await this.httpServer.sendCommand('cookies_get', filter ?? {}));
372
493
  }
@@ -389,7 +510,7 @@ export class ExtensionBridge {
389
510
  }
390
511
  async debuggerSend(method, params, tabId, timeout) {
391
512
  return await this.httpServer.sendCommand('debugger_send', {
392
- tabId: tabId ?? this.currentTabId,
513
+ tabId: tabId ?? this.requireCurrentTabId(),
393
514
  method,
394
515
  params,
395
516
  }, timeout);
@@ -397,7 +518,7 @@ export class ExtensionBridge {
397
518
  // ==================== Debugger (CDP via Extension) ====================
398
519
  async inputKey(type, options = {}) {
399
520
  await this.httpServer.sendCommand('input_key', {
400
- tabId: this.currentTabId,
521
+ tabId: this.requireCurrentTabId(),
401
522
  type,
402
523
  ...options,
403
524
  });
@@ -405,7 +526,7 @@ export class ExtensionBridge {
405
526
  // ==================== 输入事件(通过 CDP)====================
406
527
  async inputMouse(type, x, y, options = {}) {
407
528
  await this.httpServer.sendCommand('input_mouse', {
408
- tabId: this.currentTabId,
529
+ tabId: this.requireCurrentTabId(),
409
530
  type,
410
531
  x,
411
532
  y,
@@ -414,113 +535,109 @@ export class ExtensionBridge {
414
535
  }
415
536
  async inputTouch(type, touchPoints) {
416
537
  await this.httpServer.sendCommand('input_touch', {
417
- tabId: this.currentTabId,
538
+ tabId: this.requireCurrentTabId(),
418
539
  type,
419
540
  touchPoints,
420
541
  });
421
542
  }
422
543
  async inputType(text, delay = 0) {
423
544
  await this.httpServer.sendCommand('input_type', {
424
- tabId: this.currentTabId,
545
+ tabId: this.requireCurrentTabId(),
425
546
  text,
426
547
  delay,
427
548
  });
428
549
  }
429
550
  async consoleEnable() {
430
551
  await this.httpServer.sendCommand('console_enable', {
431
- tabId: this.currentTabId,
552
+ tabId: this.requireCurrentTabId(),
432
553
  });
433
554
  }
434
555
  // ==================== 控制台日志 ====================
435
556
  async consoleGet(options = {}) {
436
557
  const result = (await this.httpServer.sendCommand('console_get', {
437
- tabId: this.currentTabId,
558
+ tabId: this.requireCurrentTabId(),
438
559
  ...options,
439
560
  }));
440
561
  return result.messages;
441
562
  }
442
563
  async networkEnable() {
443
564
  await this.httpServer.sendCommand('network_enable', {
444
- tabId: this.currentTabId,
565
+ tabId: this.requireCurrentTabId(),
445
566
  });
446
567
  }
447
568
  // ==================== 网络日志 ====================
448
569
  async networkGet(options = {}) {
449
570
  const result = (await this.httpServer.sendCommand('network_get', {
450
- tabId: this.currentTabId,
571
+ tabId: this.requireCurrentTabId(),
451
572
  ...options,
452
573
  }));
453
574
  return result.requests;
454
575
  }
455
576
  async stealthType(text, delay = 0) {
577
+ if (this.currentTabId === null) {
578
+ throw new DriverCapabilityError('没有当前页面,请先 browse attach 或先 browse open 创建受控页面');
579
+ }
456
580
  const params = {
581
+ tabId: this.requireCurrentTabId(),
582
+ frameId: this.currentFrameId !== null ? this.currentFrameId : undefined,
457
583
  text,
458
584
  delay,
459
585
  };
460
- if (this.currentTabId !== null) {
461
- params.tabId = this.currentTabId;
462
- }
463
- if (this.currentFrameId !== null) {
464
- params.frameId = this.currentFrameId;
465
- }
466
586
  await this.httpServer.sendCommand('stealth_type', params);
467
587
  }
468
588
  // ==================== Stealth 模式(JS 事件模拟,无 debugger)====================
469
589
  async stealthKey(key, type = 'press', modifiers = []) {
590
+ if (this.currentTabId === null) {
591
+ throw new DriverCapabilityError('没有当前页面,请先 browse attach 或先 browse open 创建受控页面');
592
+ }
470
593
  const params = {
594
+ tabId: this.requireCurrentTabId(),
595
+ frameId: this.currentFrameId !== null ? this.currentFrameId : undefined,
471
596
  key,
472
597
  type,
473
598
  modifiers,
474
599
  };
475
- if (this.currentTabId !== null) {
476
- params.tabId = this.currentTabId;
477
- }
478
- if (this.currentFrameId !== null) {
479
- params.frameId = this.currentFrameId;
480
- }
481
600
  await this.httpServer.sendCommand('stealth_key', params);
482
601
  }
483
602
  async stealthClick(x, y, button = 'left', clickCount = 1, refId) {
603
+ if (this.currentTabId === null) {
604
+ throw new DriverCapabilityError('没有当前页面,请先 browse attach 或先 browse open 创建受控页面');
605
+ }
484
606
  const params = {
607
+ tabId: this.requireCurrentTabId(),
608
+ frameId: this.currentFrameId !== null ? this.currentFrameId : undefined,
485
609
  x,
486
610
  y,
487
611
  button,
488
612
  clickCount,
489
613
  };
490
- if (this.currentTabId !== null) {
491
- params.tabId = this.currentTabId;
492
- }
493
- if (this.currentFrameId !== null) {
494
- params.frameId = this.currentFrameId;
495
- }
496
614
  if (typeof refId === 'string') {
497
615
  params.refId = refId;
498
616
  }
499
617
  await this.httpServer.sendCommand('stealth_click', params);
500
618
  }
501
619
  async stealthMouse(type, x, y, button = 'left') {
620
+ if (this.currentTabId === null) {
621
+ throw new DriverCapabilityError('没有当前页面,请先 browse attach 或先 browse open 创建受控页面');
622
+ }
502
623
  const params = {
624
+ tabId: this.requireCurrentTabId(),
625
+ frameId: this.currentFrameId !== null ? this.currentFrameId : undefined,
503
626
  type,
504
627
  x,
505
628
  y,
506
629
  button,
507
630
  };
508
- if (this.currentTabId !== null) {
509
- params.tabId = this.currentTabId;
510
- }
511
- if (this.currentFrameId !== null) {
512
- params.frameId = this.currentFrameId;
513
- }
514
631
  await this.httpServer.sendCommand('stealth_mouse', params);
515
632
  }
516
633
  async stealthInject() {
517
- const params = {};
518
- if (this.currentTabId !== null) {
519
- params.tabId = this.currentTabId;
520
- }
521
- if (this.currentFrameId !== null) {
522
- params.frameId = this.currentFrameId;
634
+ if (this.currentTabId === null) {
635
+ throw new DriverCapabilityError('没有当前页面,请先 browse attach 或先 browse open 创建受控页面');
523
636
  }
637
+ const params = {
638
+ tabId: this.requireCurrentTabId(),
639
+ frameId: this.currentFrameId !== null ? this.currentFrameId : undefined,
640
+ };
524
641
  await this.httpServer.sendCommand('stealth_inject', params);
525
642
  }
526
643
  getState() {
@@ -538,7 +655,7 @@ export class ExtensionBridge {
538
655
  */
539
656
  async evaluateInFrame(frameId, expression, timeout) {
540
657
  return (await this.httpServer.sendCommand('evaluate_in_frame', {
541
- tabId: this.currentTabId,
658
+ tabId: this.requireCurrentTabId(),
542
659
  frameId,
543
660
  expression,
544
661
  returnByValue: true,
@@ -551,10 +668,36 @@ export class ExtensionBridge {
551
668
  */
552
669
  async resolveFrame(frame) {
553
670
  return (await this.httpServer.sendCommand('resolve_frame', {
554
- tabId: this.currentTabId,
671
+ tabId: this.requireCurrentTabId(),
555
672
  frame,
556
673
  }));
557
674
  }
675
+ requireCurrentTabId() {
676
+ if (this.currentTabId === null) {
677
+ throw new DriverCapabilityError('没有当前页面,请先 browse attach 或先 browse open 创建受控页面');
678
+ }
679
+ return this.currentTabId;
680
+ }
681
+ normalizePageChange(result) {
682
+ return {
683
+ targetId: result.targetId,
684
+ windowId: result.windowId,
685
+ before: this.normalizeAffected(result.before),
686
+ after: this.normalizeAffected(result.after),
687
+ };
688
+ }
689
+ normalizeAffected(value) {
690
+ if (!value) {
691
+ return value;
692
+ }
693
+ if ('url' in value) {
694
+ return this.tabToTarget(value);
695
+ }
696
+ return {
697
+ ...value,
698
+ tabs: value.tabs.map((tab) => this.tabToTarget(tab)),
699
+ };
700
+ }
558
701
  parseTargetId(targetId) {
559
702
  const parsed = parseInt(targetId, 10);
560
703
  if (isNaN(parsed)) {
@@ -562,8 +705,25 @@ export class ExtensionBridge {
562
705
  }
563
706
  return parsed;
564
707
  }
565
- updateState(url, title) {
566
- this.state = { url, title };
708
+ tabToTarget(tab) {
709
+ return {
710
+ id: tab.id,
711
+ targetId: String(tab.id),
712
+ url: tab.url,
713
+ title: tab.title,
714
+ type: 'page',
715
+ active: tab.active,
716
+ windowId: tab.windowId,
717
+ index: tab.index,
718
+ groupId: tab.groupId,
719
+ pinned: tab.pinned,
720
+ incognito: tab.incognito,
721
+ managed: tab.managed,
722
+ status: tab.status,
723
+ };
724
+ }
725
+ updateState(page) {
726
+ this.state = { url: page.url, title: page.title, managed: page.managed };
567
727
  }
568
728
  }
569
729
  //# sourceMappingURL=bridge.js.map