cdp-tunnel 2.3.0 → 2.4.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/cli/index.js CHANGED
@@ -456,8 +456,9 @@ program
456
456
 
457
457
  if (wasRunning) {
458
458
  log('cyan', '🔄 重启服务器...');
459
- startServer(savedPort, savedWatchdog, config.autoRestart);
460
- log('green', '✅ 服务器已重启');
459
+ const child = startServer(savedPort, false, config.autoRestart);
460
+ child.unref();
461
+ log('green', '✅ 服务器已重启 (PID: ' + child.pid + ')');
461
462
  } else {
462
463
  log('cyan', ' 运行 cdp-tunnel start 启动服务器');
463
464
  }
@@ -124,11 +124,11 @@ importScripts('features/automation-badge.js');
124
124
  var sessionId = State.findSessionByTabId(tabId);
125
125
  if (sessionId) {
126
126
  var targetId = State.getTargetIdBySession(sessionId);
127
- EventBuilder.send('Target.targetDestroyed', { targetId: targetId });
128
127
  EventBuilder.send('Target.detachedFromTarget', {
129
128
  sessionId: sessionId,
130
129
  targetId: targetId
131
130
  });
131
+ EventBuilder.send('Target.targetDestroyed', { targetId: targetId });
132
132
  State.unmapSession(sessionId);
133
133
  if (removedClientId) {
134
134
  SpecialHandler.updateTabGroupName(removedClientId);
@@ -138,6 +138,7 @@ importScripts('features/automation-badge.js');
138
138
  if (State.getCurrentTabId() === tabId) {
139
139
  State.persist(null, false);
140
140
  }
141
+ State.removeTabIdToClientId(tabId);
141
142
  });
142
143
 
143
144
  chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
@@ -9,12 +9,12 @@ var ForwardHandler = (function() {
9
9
 
10
10
  if (!tabId) {
11
11
  Logger.warn('[Forward] No tabId for command:', method);
12
- return Promise.resolve({});
12
+ return Promise.reject({ code: -32000, message: 'No target found for command: ' + method });
13
13
  }
14
14
 
15
15
  if (!State.isTabAttached(tabId)) {
16
16
  Logger.warn('[Forward] Tab not attached, skipping command:', method, 'tabId:', tabId);
17
- return Promise.resolve({});
17
+ return Promise.reject({ code: -32000, message: 'Target is not attached' });
18
18
  }
19
19
 
20
20
  Logger.debug('[Forward]', method, '-> tabId:', tabId);
@@ -228,16 +228,17 @@ var SpecialHandler = (function() {
228
228
  if (targetId) {
229
229
  var tabId = State.getTabIdByTargetId(targetId);
230
230
  if (tabId) {
231
- State.removeAttachedTab(tabId);
232
231
  var closeClientId = State.getClientIdByTabId(tabId);
233
232
  return new Promise(function(resolve) {
234
233
  chrome.tabs.remove(tabId, function() {
234
+ State.removeAttachedTab(tabId);
235
235
  if (closeClientId) {
236
236
  updateTabGroupName(closeClientId);
237
237
  }
238
238
  resolve({ success: true });
239
239
  });
240
240
  }).catch(function() {
241
+ State.removeAttachedTab(tabId);
241
242
  return { success: true };
242
243
  });
243
244
  }
@@ -82,8 +82,8 @@ function routeCDPCommand(message) {
82
82
  resolve({ result: result });
83
83
  })
84
84
  .catch(function(error) {
85
- Logger.error('[CDP] ERROR id=' + id + ' method=' + method + ' msg=' + error.message);
86
- resolve({ error: { message: error.message } });
85
+ Logger.error('[CDP] ERROR id=' + id + ' method=' + method + ' msg=' + (error.message || error));
86
+ resolve({ error: { code: error.code || -32000, message: error.message || String(error) } });
87
87
  });
88
88
  } else {
89
89
  ForwardHandler.execute({ id: id, method: method, params: params, sessionId: sessionId, clientId: clientId })
@@ -92,8 +92,8 @@ function routeCDPCommand(message) {
92
92
  resolve({ result: result });
93
93
  })
94
94
  .catch(function(error) {
95
- Logger.error('[CDP] ERROR id=' + id + ' method=' + method + ' msg=' + error.message + ' (forwarded)');
96
- resolve({ error: { message: error.message } });
95
+ Logger.error('[CDP] ERROR id=' + id + ' method=' + method + ' msg=' + (error.message || error) + ' (forwarded)');
96
+ resolve({ error: { code: error.code || -32000, message: error.message || String(error) } });
97
97
  });
98
98
  }
99
99
  }).then(function(response) {
@@ -169,6 +169,7 @@ var DebuggerManager = (function() {
169
169
  Logger.error('[Debugger] Failed to detach from tab', tabId, ':', error.message);
170
170
  State.removeAttachedTab(tabId);
171
171
  AutomationBadge.remove(tabId);
172
+ Screencast.stopPolling(tabId);
172
173
  });
173
174
  }
174
175
 
@@ -342,6 +342,10 @@ var State = (function() {
342
342
  _state.tabIdToClientId.set(tabId, clientId);
343
343
  }
344
344
 
345
+ function removeTabIdToClientId(tabId) {
346
+ _state.tabIdToClientId.delete(tabId);
347
+ }
348
+
345
349
  function getClientIdByTabId(tabId) {
346
350
  return _state.tabIdToClientId.get(tabId);
347
351
  }
@@ -481,6 +485,7 @@ var State = (function() {
481
485
  clearAllState: clearAllState,
482
486
  cleanupAllTabs: cleanupAllTabs,
483
487
  setTabIdToClientId: setTabIdToClientId,
488
+ removeTabIdToClientId: removeTabIdToClientId,
484
489
  getClientIdByTabId: getClientIdByTabId,
485
490
  setGroupIdForClient: setGroupIdForClient,
486
491
  getGroupIdForClient: getGroupIdForClient,
@@ -339,16 +339,20 @@ var WebSocketManager = (function() {
339
339
  return;
340
340
  }
341
341
  Logger.info('[WS] Closing ' + tabIds.length + ' attached tabs for clientId:', clientId);
342
+ var pending = tabIds.length;
342
343
  tabIds.forEach(function(tabId) {
343
344
  chrome.tabs.remove(tabId, function() {
344
345
  if (chrome.runtime.lastError) {
345
346
  Logger.info('[WS] Tab already closed:', tabId);
346
347
  }
348
+ chrome.debugger.detach({ tabId: tabId }).catch(function() {});
349
+ State.removeAttachedTab(tabId);
350
+ pending--;
351
+ if (pending === 0) {
352
+ resolve();
353
+ }
347
354
  });
348
- chrome.debugger.detach({ tabId: tabId }).catch(function() {});
349
- State.removeAttachedTab(tabId);
350
355
  });
351
- resolve();
352
356
  }
353
357
 
354
358
  function handleServerRestart() {
@@ -372,17 +376,27 @@ var WebSocketManager = (function() {
372
376
  Logger.info('[WS] Browser.close received, cleaning up... clientId:', clientId);
373
377
 
374
378
  closeTabGroupByClientId(clientId).then(function() {
375
- var attachedTabIds = State.getAttachedTabIds();
376
- var promises = attachedTabIds.map(function(tabId) {
377
- return chrome.debugger.detach({ tabId: tabId }).catch(function(e) {
378
- Logger.info('[WS] Detach failed for tab', tabId, ':', e.message);
379
- });
379
+ return new Promise(function(resolve) {
380
+ closeTabsByClientId(clientId, resolve);
381
+ });
382
+ }).then(function() {
383
+ var preExistingTabs = State.getPreExistingTabs();
384
+ var clientPreExisting = preExistingTabs.filter(function(tabId) {
385
+ return State.getClientIdByTabId(tabId) === clientId;
380
386
  });
381
- Promise.all(promises).then(function() {
387
+ clientPreExisting.forEach(function(tabId) {
388
+ chrome.debugger.detach({ tabId: tabId }).catch(function() {});
389
+ State.removeAttachedTab(tabId);
390
+ });
391
+ State.clearPreExistingTabsForClient(clientId);
392
+
393
+ State.removeCDPClient(clientId);
394
+ if (State.getCDPClients().length === 0) {
382
395
  State.clearAllState();
383
396
  State.persist(null, false);
384
- Logger.info('[WS] Browser.close cleanup complete');
385
- });
397
+ }
398
+ broadcastStateUpdate();
399
+ Logger.info('[WS] Browser.close cleanup complete for client:', clientId);
386
400
  });
387
401
  }
388
402
 
@@ -398,7 +412,18 @@ var WebSocketManager = (function() {
398
412
  var cdpClients = State.getCDPClients() || [];
399
413
  var attachedTabIds = State.getAttachedTabIds();
400
414
 
415
+ if (attachedTabIds.length === 0) {
416
+ chrome.runtime.sendMessage({
417
+ type: 'stateUpdate',
418
+ connected: isConnected,
419
+ cdpClients: cdpClients,
420
+ attachedPages: []
421
+ }).catch(function() {});
422
+ return;
423
+ }
424
+
401
425
  var attachedPages = [];
426
+ var pending = attachedTabIds.length;
402
427
  attachedTabIds.forEach(function(tabId) {
403
428
  chrome.tabs.get(tabId, function(tab) {
404
429
  if (tab && !chrome.runtime.lastError) {
@@ -408,15 +433,17 @@ var WebSocketManager = (function() {
408
433
  url: tab.url || ''
409
434
  });
410
435
  }
436
+ pending--;
437
+ if (pending === 0) {
438
+ chrome.runtime.sendMessage({
439
+ type: 'stateUpdate',
440
+ connected: isConnected,
441
+ cdpClients: cdpClients,
442
+ attachedPages: attachedPages
443
+ }).catch(function() {});
444
+ }
411
445
  });
412
446
  });
413
-
414
- chrome.runtime.sendMessage({
415
- type: 'stateUpdate',
416
- connected: isConnected,
417
- cdpClients: cdpClients,
418
- attachedPages: attachedPages
419
- }).catch(function() {});
420
447
  }
421
448
 
422
449
  function getQueueStats() {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "CDP Bridge",
4
- "version": "2.3.0",
4
+ "version": "2.4.1",
5
5
  "description": "Chrome DevTools Protocol Bridge for Playwright/Puppeteer automation",
6
6
  "permissions": [
7
7
  "debugger",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cdp-tunnel",
3
- "version": "2.3.0",
3
+ "version": "2.4.1",
4
4
  "description": "Bridge Chrome's debugger API to WebSocket — control your existing browser with Playwright/Puppeteer via CDP",
5
5
  "main": "server/proxy-server.js",
6
6
  "bin": "./cli/index.js",
@@ -1017,6 +1017,20 @@ function handleClientConnection(ws, clientInfo, customClientId = null) {
1017
1017
  // 广播更新后的客户端列表
1018
1018
  broadcastClientList();
1019
1019
 
1020
+ // 清理该 client 的所有映射
1021
+ for (const [tId, cId] of targetIdToClientId.entries()) {
1022
+ if (cId === id) targetIdToClientId.delete(tId);
1023
+ }
1024
+ for (const [bcId, cId] of browserContextToClientId.entries()) {
1025
+ if (cId === id) browserContextToClientId.delete(bcId);
1026
+ }
1027
+ if (clientIdToBrowserContext.has(id)) {
1028
+ clientIdToBrowserContext.delete(id);
1029
+ }
1030
+ for (const [gId, mapping] of globalRequestIdMap.entries()) {
1031
+ if (mapping.clientId === id) globalRequestIdMap.delete(gId);
1032
+ }
1033
+
1020
1034
  // 清理配对关系
1021
1035
  if (ws.pairedPlugin) {
1022
1036
  ws.pairedPlugin.pairedClientId = null;