cdp-tunnel 2.9.1 → 2.9.3

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.
@@ -338,6 +338,15 @@ importScripts('features/automation-badge.js');
338
338
  });
339
339
  });
340
340
 
341
+ function getCdpAddress(wsUrl, mode) {
342
+ var match = (wsUrl || '').match(/:\/\/([^\/]+):(\d+)/);
343
+ if (!match) return '';
344
+ var host = match[1];
345
+ var port = parseInt(match[2], 10);
346
+ if (mode === 'takeover') port += 1;
347
+ return 'http://' + host + ':' + port;
348
+ }
349
+
341
350
  chrome.runtime.onInstalled.addListener(function(details) {
342
351
  Logger.info('[Runtime] Extension installed/updated:', details.reason);
343
352
  State.persist(null, false);
@@ -466,7 +475,8 @@ importScripts('features/automation-badge.js');
466
475
  status = 'error';
467
476
  }
468
477
  }
469
- return { id: conn.id, tag: conn.tag, url: conn.url, mode: conn.mode || 'create', status: status, attachedCount: attachedCount };
478
+ var cdpAddress = getCdpAddress(conn.url, conn.mode || 'create');
479
+ return { id: conn.id, tag: conn.tag, url: conn.url, mode: conn.mode || 'create', status: status, attachedCount: attachedCount, cdpAddress: cdpAddress };
470
480
  });
471
481
  sendResponse({ connections: list });
472
482
  });
@@ -84,8 +84,7 @@ var SpecialHandler = (function() {
84
84
  addTabToAutomationGroup(tabId, clientId, null, context);
85
85
  } else if (context.mode === 'takeover') {
86
86
  state.addPreExistingTab(tabId);
87
- addTabToAutomationGroup(tabId, clientId, null, context);
88
- Logger.info('[CDP TAKEOVER] Target.attachToTarget: added to TAKE group. tabId:', tabId);
87
+ Logger.info('[CDP TAKEOVER] Target.attachToTarget: attached without grouping. tabId:', tabId);
89
88
  } else {
90
89
  state.addPreExistingTab(tabId);
91
90
  Logger.info('[CDP] Target.attachToTarget: user tab not CDP-created, treating as pre-existing. tabId:', tabId);
@@ -283,6 +283,37 @@
283
283
  font-family: 'Monaco', 'Menlo', monospace;
284
284
  }
285
285
 
286
+ .conn-config-cdp {
287
+ font-size: 11px;
288
+ color: #374151;
289
+ font-family: 'Monaco', 'Menlo', monospace;
290
+ margin-top: 2px;
291
+ display: flex;
292
+ align-items: center;
293
+ gap: 4px;
294
+ }
295
+
296
+ .conn-config-cdp .cdp-addr {
297
+ color: #2563eb;
298
+ font-weight: 600;
299
+ cursor: pointer;
300
+ }
301
+
302
+ .conn-config-cdp .btn-copy-cdp {
303
+ border: none;
304
+ background: transparent;
305
+ cursor: pointer;
306
+ font-size: 12px;
307
+ padding: 0 4px;
308
+ opacity: 0.5;
309
+ transition: opacity 0.2s;
310
+ line-height: 1;
311
+ }
312
+
313
+ .conn-config-cdp .btn-copy-cdp:hover {
314
+ opacity: 1;
315
+ }
316
+
286
317
  .conn-config-item .btn-delete {
287
318
  padding: 4px 10px;
288
319
  border: none;
@@ -95,13 +95,15 @@
95
95
  connections.forEach(function(conn) {
96
96
  var statusClass = getStatusClass(conn.id, conn.enabled);
97
97
  var isActive = conn.enabled && statusClass === 'connected';
98
+ var cdpAddr = getCdpAddress(conn.url, conn.mode);
98
99
  html +=
99
100
  '<div class="conn-config-item' + (isActive ? ' active' : '') + '" data-id="' + conn.id + '">' +
100
101
  '<input type="checkbox" class="conn-toggle" data-id="' + conn.id + '"' + (conn.enabled ? ' checked' : '') + ' title="启用/禁用">' +
101
102
  '<span class="status-dot ' + statusClass + '" title="' + statusClass + '"></span>' +
102
103
  '<div class="conn-config-info">' +
103
104
  '<div class="conn-config-tag">' + (conn.mode === 'takeover' ? '🔗 ' : '🆕 ') + escapeHtml(conn.tag) + '</div>' +
104
- '<div class="conn-config-url" title="' + escapeAttr(conn.url) + '">' + escapeHtml(conn.url) + '</div>' +
105
+ '<div class="conn-config-url" title="' + escapeAttr(conn.url) + '">WS: ' + escapeHtml(conn.url) + '</div>' +
106
+ (cdpAddr ? '<div class="conn-config-cdp">CDP: <span class="cdp-addr" data-cdp="' + escapeAttr(cdpAddr) + '">' + escapeHtml(cdpAddr) + '</span> <button class="btn-copy-cdp" data-cdp="' + escapeAttr(cdpAddr) + '" title="复制 CDP 地址">📋</button></div>' : '') +
105
107
  '</div>' +
106
108
  '<button class="btn-delete" data-id="' + conn.id + '" title="删除">删除</button>' +
107
109
  '</div>';
@@ -297,6 +299,15 @@
297
299
  return (s || '').replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
298
300
  }
299
301
 
302
+ function getCdpAddress(wsUrl, mode) {
303
+ var match = (wsUrl || '').match(/:\/\/([^\/]+):(\d+)/);
304
+ if (!match) return '';
305
+ var host = match[1];
306
+ var port = parseInt(match[2], 10);
307
+ if (mode === 'takeover') port += 1;
308
+ return 'http://' + host + ':' + port;
309
+ }
310
+
300
311
  function init() {
301
312
  if (typeof chrome !== 'undefined' && chrome.storage) {
302
313
  chrome.storage.local.get(['autoMute'], function(result) {
@@ -365,6 +376,17 @@
365
376
  });
366
377
 
367
378
  elements.connConfigList.addEventListener('click', function(e) {
379
+ var copyBtn = e.target.closest('.btn-copy-cdp');
380
+ if (copyBtn) {
381
+ var addr = copyBtn.dataset.cdp;
382
+ if (addr) {
383
+ navigator.clipboard.writeText(addr).then(function() {
384
+ showToast('已复制: ' + addr);
385
+ });
386
+ }
387
+ return;
388
+ }
389
+
368
390
  var toggleEl = e.target.closest('.conn-toggle');
369
391
  if (toggleEl) {
370
392
  var connId = toggleEl.dataset.id;
@@ -269,7 +269,6 @@ var WebSocketConnection = (function() {
269
269
  var sessions = self.state.findSessionsByTabId(tid);
270
270
  sessions.forEach(function(sid) { self.state.unmapSession(sid); });
271
271
  });
272
- self.state.removeGroupForClient(takeClientId);
273
272
  self.state.removeCDPClient(takeClientId);
274
273
  if (self.state.getCDPClients().length === 0) {
275
274
  self.state.setHasConnectedClient(false);
@@ -541,6 +540,10 @@ var WebSocketConnection = (function() {
541
540
  WebSocketConnection.prototype._createGroupForClient = function(clientId, mode) {
542
541
  var self = this;
543
542
  if (!clientId || !chrome.tabGroups) return;
543
+ if (mode === 'takeover') {
544
+ Logger.info('[WS:' + self.connectionId + '] Skipping group creation for takeover mode, clientId:', clientId);
545
+ return;
546
+ }
544
547
 
545
548
  if (self._groupCreationPending.has(clientId)) {
546
549
  Logger.info('[WS:' + self.connectionId + '] Group creation already pending for client:', clientId);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "CDP Bridge",
4
- "version": "2.9.1",
4
+ "version": "2.9.3",
5
5
  "description": "Chrome DevTools Protocol Bridge for Playwright/Puppeteer automation",
6
6
  "permissions": [
7
7
  "debugger",
@@ -23,6 +23,12 @@ body { width: 320px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI',
23
23
  .conn-tag { font-weight: 600; font-size: 13px; color: #e0e0e0; }
24
24
  .conn-url { color: #888; font-size: 11px; font-family: 'SF Mono', Menlo, monospace; word-break: break-all; margin-top: 3px; line-height: 1.4; }
25
25
 
26
+ .conn-cdp { color: #4a9eff; font-size: 11px; font-family: 'SF Mono', Menlo, monospace; margin-top: 2px; display: flex; align-items: center; gap: 2px; }
27
+ .conn-cdp-label { color: #888; }
28
+ .conn-cdp-value { font-weight: 600; }
29
+ .conn-cdp-copy { background: none; border: none; cursor: pointer; font-size: 11px; padding: 0 2px; opacity: 0.5; transition: opacity 0.15s; }
30
+ .conn-cdp-copy:hover { opacity: 1; }
31
+
26
32
  .empty-hint { text-align: center; padding: 16px 10px; color: #777; font-size: 12px; }
27
33
 
28
34
  .stats-row { display: flex; gap: 16px; padding: 8px 0; font-size: 12px; color: #aaa; }
@@ -14,6 +14,15 @@
14
14
  });
15
15
  }
16
16
 
17
+ function getCdpAddress(wsUrl, mode) {
18
+ var match = (wsUrl || '').match(/:\/\/([^\/]+):(\d+)/);
19
+ if (!match) return '';
20
+ var host = match[1];
21
+ var port = parseInt(match[2], 10);
22
+ if (mode === 'takeover') port += 1;
23
+ return 'http://' + host + ':' + port;
24
+ }
25
+
17
26
  function renderConnectionList(connections) {
18
27
  connectionList.innerHTML = '';
19
28
 
@@ -43,10 +52,42 @@
43
52
 
44
53
  var url = document.createElement('div');
45
54
  url.className = 'conn-url';
46
- url.textContent = conn.url;
55
+ url.textContent = 'WS: ' + conn.url;
47
56
 
48
57
  item.appendChild(header);
49
58
  item.appendChild(url);
59
+
60
+ var cdpAddr = conn.cdpAddress || getCdpAddress(conn.url, conn.mode);
61
+ if (cdpAddr) {
62
+ var cdpRow = document.createElement('div');
63
+ cdpRow.className = 'conn-cdp';
64
+
65
+ var cdpLabel = document.createElement('span');
66
+ cdpLabel.className = 'conn-cdp-label';
67
+ cdpLabel.textContent = 'CDP: ';
68
+
69
+ var cdpValue = document.createElement('span');
70
+ cdpValue.className = 'conn-cdp-value';
71
+ cdpValue.textContent = cdpAddr;
72
+
73
+ var copyBtn = document.createElement('button');
74
+ copyBtn.className = 'conn-cdp-copy';
75
+ copyBtn.textContent = '📋';
76
+ copyBtn.title = '复制';
77
+ copyBtn.addEventListener('click', function(ev) {
78
+ ev.stopPropagation();
79
+ navigator.clipboard.writeText(cdpAddr).then(function() {
80
+ copyBtn.textContent = '✓';
81
+ setTimeout(function() { copyBtn.textContent = '📋'; }, 1200);
82
+ });
83
+ });
84
+
85
+ cdpRow.appendChild(cdpLabel);
86
+ cdpRow.appendChild(cdpValue);
87
+ cdpRow.appendChild(copyBtn);
88
+ item.appendChild(cdpRow);
89
+ }
90
+
50
91
  connectionList.appendChild(item);
51
92
  });
52
93
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cdp-tunnel",
3
- "version": "2.9.1",
3
+ "version": "2.9.3",
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",