cdp-tunnel 2.9.0 → 2.9.2

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,14 +475,15 @@ importScripts('features/automation-badge.js');
466
475
  status = 'error';
467
476
  }
468
477
  }
469
- return { id: conn.id, tag: conn.tag, url: conn.url, 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
  });
473
483
  return true;
474
484
  } else if (message.type === 'add-connection') {
475
- Logger.info('[Runtime] Adding connection:', message.tag, message.url);
476
- Config.addConnection({ tag: message.tag, url: message.url }, function(conn) {
485
+ Logger.info('[Runtime] Adding connection:', message.tag, message.url, 'mode:', message.mode);
486
+ Config.addConnection({ tag: message.tag, url: message.url, mode: message.mode || 'create' }, function(conn) {
477
487
  if (conn && conn.enabled) {
478
488
  ConnectionManager.addConnection(conn);
479
489
  var entry = ConnectionManager.getConnection(conn.id);
@@ -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;
@@ -814,6 +845,10 @@
814
845
  <div class="add-conn-form">
815
846
  <input type="text" class="input-tag" id="newConnTag" placeholder="名称" value="local">
816
847
  <input type="text" class="input-url" id="newConnUrl" placeholder="ws://localhost:9221/plugin" value="ws://localhost:9221/plugin">
848
+ <select id="inputMode" style="padding:8px 10px;border:1px solid #d1d5db;border-radius:6px;font-size:13px;outline:none;transition:border-color 0.2s;flex-shrink:0;">
849
+ <option value="create">创建模式</option>
850
+ <option value="takeover">接管模式</option>
851
+ </select>
817
852
  <button class="btn btn-primary btn-sm" id="addConnBtn">添加连接</button>
818
853
  </div>
819
854
  </div>
@@ -27,6 +27,7 @@
27
27
  newConnTag: document.getElementById('newConnTag'),
28
28
  newConnUrl: document.getElementById('newConnUrl'),
29
29
  addConnBtn: document.getElementById('addConnBtn'),
30
+ inputMode: document.getElementById('inputMode'),
30
31
  autoMuteToggle: document.getElementById('autoMuteToggle'),
31
32
  pluginIdDisplay: document.getElementById('pluginIdDisplay')
32
33
  };
@@ -94,13 +95,15 @@
94
95
  connections.forEach(function(conn) {
95
96
  var statusClass = getStatusClass(conn.id, conn.enabled);
96
97
  var isActive = conn.enabled && statusClass === 'connected';
98
+ var cdpAddr = getCdpAddress(conn.url, conn.mode);
97
99
  html +=
98
100
  '<div class="conn-config-item' + (isActive ? ' active' : '') + '" data-id="' + conn.id + '">' +
99
101
  '<input type="checkbox" class="conn-toggle" data-id="' + conn.id + '"' + (conn.enabled ? ' checked' : '') + ' title="启用/禁用">' +
100
102
  '<span class="status-dot ' + statusClass + '" title="' + statusClass + '"></span>' +
101
103
  '<div class="conn-config-info">' +
102
- '<div class="conn-config-tag">' + escapeHtml(conn.tag) + '</div>' +
103
- '<div class="conn-config-url" title="' + escapeAttr(conn.url) + '">' + escapeHtml(conn.url) + '</div>' +
104
+ '<div class="conn-config-tag">' + (conn.mode === 'takeover' ? '🔗 ' : '🆕 ') + escapeHtml(conn.tag) + '</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>' : '') +
104
107
  '</div>' +
105
108
  '<button class="btn-delete" data-id="' + conn.id + '" title="删除">删除</button>' +
106
109
  '</div>';
@@ -296,6 +299,15 @@
296
299
  return (s || '').replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
297
300
  }
298
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
+
299
311
  function init() {
300
312
  if (typeof chrome !== 'undefined' && chrome.storage) {
301
313
  chrome.storage.local.get(['autoMute'], function(result) {
@@ -333,6 +345,7 @@
333
345
  elements.addConnBtn.addEventListener('click', function() {
334
346
  var tag = elements.newConnTag.value.trim();
335
347
  var url = elements.newConnUrl.value.trim();
348
+ var mode = elements.inputMode.value || 'create';
336
349
 
337
350
  if (!tag) {
338
351
  showToast('请输入连接名称', 'error');
@@ -344,16 +357,18 @@
344
357
  }
345
358
 
346
359
  if (typeof chrome !== 'undefined' && chrome.runtime) {
347
- chrome.runtime.sendMessage({ type: 'add-connection', tag: tag, url: url }, function() {
360
+ chrome.runtime.sendMessage({ type: 'add-connection', tag: tag, url: url, mode: mode }, function() {
348
361
  elements.newConnTag.value = '';
349
362
  elements.newConnUrl.value = '';
363
+ elements.inputMode.value = 'create';
350
364
  loadAndRenderConnections();
351
365
  showToast('连接已添加');
352
366
  });
353
367
  } else if (typeof Config !== 'undefined') {
354
- Config.addConnection({ tag: tag, url: url }, function() {
368
+ Config.addConnection({ tag: tag, url: url, mode: mode }, function() {
355
369
  elements.newConnTag.value = '';
356
370
  elements.newConnUrl.value = '';
371
+ elements.inputMode.value = 'create';
357
372
  loadAndRenderConnections();
358
373
  showToast('连接已添加');
359
374
  });
@@ -361,6 +376,17 @@
361
376
  });
362
377
 
363
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
+
364
390
  var toggleEl = e.target.closest('.conn-toggle');
365
391
  if (toggleEl) {
366
392
  var connId = toggleEl.dataset.id;
@@ -23,7 +23,7 @@ var ConnectionManager = (function() {
23
23
  return;
24
24
  }
25
25
 
26
- var state = new ConnectionState(config.id);
26
+ var state = new ConnectionState(config.id, config.mode);
27
27
  var wsManager = new WebSocketConnection(config.id, state, config);
28
28
 
29
29
  _connections.set(config.id, {
@@ -1,5 +1,6 @@
1
- function ConnectionState(connectionId) {
1
+ function ConnectionState(connectionId, mode) {
2
2
  this.connectionId = connectionId;
3
+ this.mode = mode || 'create';
3
4
  this.ws = null;
4
5
  this.reconnectTimer = null;
5
6
  this._hasConnectedClient = false;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "CDP Bridge",
4
- "version": "2.9.0",
4
+ "version": "2.9.2",
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
 
@@ -32,19 +41,53 @@
32
41
  var dot = document.createElement('span');
33
42
  dot.className = 'conn-dot ' + conn.status;
34
43
 
44
+ var modeIcon = conn.mode === 'takeover' ? '🔗 ' : '🆕 ';
45
+
35
46
  var tag = document.createElement('span');
36
47
  tag.className = 'conn-tag';
37
- tag.textContent = conn.tag;
48
+ tag.textContent = modeIcon + conn.tag;
38
49
 
39
50
  header.appendChild(dot);
40
51
  header.appendChild(tag);
41
52
 
42
53
  var url = document.createElement('div');
43
54
  url.className = 'conn-url';
44
- url.textContent = conn.url;
55
+ url.textContent = 'WS: ' + conn.url;
45
56
 
46
57
  item.appendChild(header);
47
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
+
48
91
  connectionList.appendChild(item);
49
92
  });
50
93
  }
@@ -58,6 +58,7 @@ var Config = {
58
58
  id: 'conn_' + Date.now() + '_' + Math.random().toString(36).substr(2, 6),
59
59
  tag: opts.tag || 'unnamed',
60
60
  url: opts.url || '',
61
+ mode: opts.mode || 'create',
61
62
  enabled: opts.enabled !== undefined ? opts.enabled : true
62
63
  };
63
64
  connections.push(conn);
@@ -91,6 +92,7 @@ var Config = {
91
92
  if (c.id === id) {
92
93
  if (updates.tag !== undefined) c.tag = updates.tag;
93
94
  if (updates.url !== undefined) c.url = updates.url;
95
+ if (updates.mode !== undefined) c.mode = updates.mode;
94
96
  }
95
97
  });
96
98
  Config.setConnections(connections, callback);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cdp-tunnel",
3
- "version": "2.9.0",
3
+ "version": "2.9.2",
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",