cdp-tunnel 3.4.0 → 3.6.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.
@@ -20,6 +20,15 @@ var SpecialHandler = (function() {
20
20
  return (wm && wm.config && wm.config.tag) || null;
21
21
  }
22
22
 
23
+ // 从 state 读取 key 名称(用作分组名),doGroup 等分组操作调用
24
+ function _getGroupName(clientId, context) {
25
+ var state = context ? context._state : null;
26
+ if (state && clientId && state.getGroupNameForClient) {
27
+ return state.getGroupNameForClient(clientId);
28
+ }
29
+ return null;
30
+ }
31
+
23
32
  function muteTabIfNeeded(tabId) {
24
33
  Config.getAutoMute(function(enabled) {
25
34
  if (!enabled) return;
@@ -297,7 +306,7 @@ var SpecialHandler = (function() {
297
306
  return;
298
307
  }
299
308
  }
300
- var baseName = CDPUtils.getGroupBaseName(groupClientId, _getConnectionTag(context), context ? context.mode : null);
309
+ var baseName = CDPUtils.getGroupBaseName(groupClientId, _getConnectionTag(context), context ? context.mode : null, _getGroupName(groupClientId, context));
301
310
 
302
311
  Logger.info('[TabGroup] Grouping tab immediately for:', baseName);
303
312
  doGroup(tabId, groupClientId, baseName, 0, callback, context);
@@ -465,7 +474,8 @@ var SpecialHandler = (function() {
465
474
  chrome.tabs.query({ groupId: groupId }, function(tabs) {
466
475
  if (chrome.runtime.lastError || !tabs) return;
467
476
 
468
- var baseName = CDPUtils.getGroupBaseName(clientId, connectionTag, mode);
477
+ var groupName = (state && state.getGroupNameForClient) ? state.getGroupNameForClient(clientId) : null;
478
+ var baseName = CDPUtils.getGroupBaseName(clientId, connectionTag, mode, groupName);
469
479
  var newName = baseName + ' (' + tabs.length + ')';
470
480
 
471
481
  if (chrome.runtime.lastError || tabs.length === 0) return;
@@ -295,6 +295,11 @@
295
295
  margin-bottom: 2px;
296
296
  }
297
297
 
298
+ .auth-badge {
299
+ font-size: 11px;
300
+ cursor: help;
301
+ }
302
+
298
303
  .conn-config-url {
299
304
  font-size: 11px;
300
305
  color: #6b7280;
@@ -103,14 +103,16 @@
103
103
  var isActive = conn.enabled && statusClass === 'connected';
104
104
  var cdpAddr = getCdpAddress(conn.url, conn.mode);
105
105
  var statusHtml = getStatusHtml(statusClass);
106
+ var hasKey = (conn.url || '').indexOf('key=') >= 0;
107
+ var maskedUrl = maskUrl(conn.url);
106
108
  html +=
107
109
  '<div class="conn-config-item' + (isActive ? ' active' : '') + '" data-id="' + conn.id + '">' +
108
110
  '<input type="checkbox" class="conn-toggle" data-id="' + conn.id + '"' + (conn.enabled ? ' checked' : '') + ' title="启用/禁用">' +
109
111
  '<span class="status-dot ' + statusClass + '" title="' + statusClass + '"></span>' +
110
112
  '<div class="conn-config-info">' +
111
- '<div class="conn-config-tag">' + (conn.mode === 'takeover' ? '🔗 ' : '🆕 ') + escapeHtml(conn.tag) + '</div>' +
112
- '<div class="conn-config-url" title="' + escapeAttr(conn.url) + '">WS: ' + escapeHtml(conn.url) + '</div>' +
113
- (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>' : '') +
113
+ '<div class="conn-config-tag">' + (conn.mode === 'takeover' ? '🔗 ' : '🆕 ') + escapeHtml(conn.tag) + (hasKey ? ' <span class="auth-badge" title="带 API Key 鉴权">🔑</span>' : '') + '</div>' +
114
+ '<div class="conn-config-url" title="' + escapeAttr(maskedUrl) + '">WS: ' + escapeHtml(maskedUrl) + '</div>' +
115
+ (cdpAddr ? '<div class="conn-config-cdp">CDP: <span class="cdp-addr" data-cdp="' + escapeAttr(cdpAddr) + '">' + escapeHtml(cdpAddr.replace(/(key=)(cdp_[a-f0-9]{8})[a-f0-9]*/, '$1$2••••')) + '</span> <button class="btn-copy-cdp" data-cdp="' + escapeAttr(cdpAddr) + '" title="复制 CDP 地址(含 key)">📋</button></div>' : '') +
114
116
  '<div class="conn-config-status">' + statusHtml + '</div>' +
115
117
  '</div>' +
116
118
  '<button class="btn-delete" data-id="' + conn.id + '" title="删除">删除</button>' +
@@ -313,7 +315,16 @@
313
315
  var host = match[1];
314
316
  var port = parseInt(match[2], 10);
315
317
  if (mode === 'takeover') port += 1;
316
- return 'http://' + host + ':' + port;
318
+ // 保留 key(如果 URL 里带了 ?key=xxx,CDP 客户端连接也需要带)
319
+ var keyMatch = (wsUrl || '').match(/[?&]key=([^&]+)/);
320
+ var keyParam = keyMatch ? '?key=' + keyMatch[1] : '';
321
+ return 'http://' + host + ':' + port + keyParam;
322
+ }
323
+
324
+ // 显示 URL 时隐藏 key 的明文(防截图泄露),但保留可识别性
325
+ function maskUrl(wsUrl) {
326
+ if (!wsUrl) return '';
327
+ return wsUrl.replace(/([?&]key=)(cdp_[a-f0-9]{8})[a-f0-9]*/, '$1$2••••');
317
328
  }
318
329
 
319
330
  function init() {
@@ -308,6 +308,15 @@ ConnectionState.prototype.getTagForClient = function(clientId) {
308
308
  return this.clientIdToTag.get(clientId) || null;
309
309
  };
310
310
 
311
+ ConnectionState.prototype.setGroupNameForClient = function(clientId, groupName) {
312
+ if (!this.clientIdToGroupName) this.clientIdToGroupName = new Map();
313
+ if (clientId && groupName) this.clientIdToGroupName.set(clientId, groupName);
314
+ };
315
+
316
+ ConnectionState.prototype.getGroupNameForClient = function(clientId) {
317
+ return (this.clientIdToGroupName && this.clientIdToGroupName.get(clientId)) || null;
318
+ };
319
+
311
320
  ConnectionState.prototype.addCDPClient = function(clientId, info) {
312
321
  var exists = false;
313
322
  for (var i = 0; i < this.cdpClients.length; i++) {
@@ -244,7 +244,12 @@ var WebSocketConnection = (function() {
244
244
  if (message.__connectionTag) {
245
245
  self.state.setTagForClient(message.clientId, message.__connectionTag);
246
246
  }
247
- self._createGroupForClient(message.clientId, message.__mode);
247
+ // 存 groupName,供 doGroup 等后续分组操作使用
248
+ if (message.__groupName) {
249
+ self.state.setGroupNameForClient(message.clientId, message.__groupName);
250
+ }
251
+ // __groupName 来自 API Key 名称,用作 Chrome 分组名(一眼看出是谁的浏览器)
252
+ self._createGroupForClient(message.clientId, message.__mode, message.__groupName);
248
253
  self._broadcastStateUpdate();
249
254
  break;
250
255
 
@@ -555,7 +560,7 @@ var WebSocketConnection = (function() {
555
560
  });
556
561
  };
557
562
 
558
- WebSocketConnection.prototype._createGroupForClient = function(clientId, mode) {
563
+ WebSocketConnection.prototype._createGroupForClient = function(clientId, mode, groupName) {
559
564
  var self = this;
560
565
  if (!clientId || !chrome.tabGroups) return;
561
566
  if (mode === 'takeover') {
@@ -581,7 +586,7 @@ var WebSocketConnection = (function() {
581
586
  self.state.setGroupCreationPromise(clientId, readyPromise);
582
587
 
583
588
  var tag = (self.state.getTagForClient ? self.state.getTagForClient(clientId) : null) || (self.config ? self.config.tag : null);
584
- var baseName = CDPUtils.getGroupBaseName(clientId, tag, mode);
589
+ var baseName = CDPUtils.getGroupBaseName(clientId, tag, mode, groupName);
585
590
  chrome.tabs.query({ currentWindow: true }, function(tabs) {
586
591
  if (!tabs || tabs.length === 0) {
587
592
  Logger.warn('[WS:' + self.connectionId + '] No tabs found for group creation');
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "CDP Bridge",
4
- "version": "3.4.0",
4
+ "version": "3.6.0",
5
5
  "description": "Chrome DevTools Protocol Bridge for Playwright/Puppeteer automation",
6
6
  "permissions": [
7
7
  "debugger",
@@ -1,5 +1,5 @@
1
1
  var Config = {
2
- WS_URL: 'ws://localhost:9221/plugin',
2
+ WS_URL: 'ws://localhost:14576/plugin',
3
3
  RECONNECT_DELAY: 3000,
4
4
  DEBUGGER_VERSION: '1.3',
5
5
  HEARTBEAT_INTERVAL: 25000,
@@ -42,8 +42,14 @@ var CDPUtils = (function() {
42
42
  return 0;
43
43
  }
44
44
 
45
- function buildGroupName(clientId, connectionTag, mode) {
45
+ function buildGroupName(clientId, connectionTag, mode, groupName) {
46
46
  if (!clientId) return 'CDP';
47
+ // 如果传了 groupName(来自 API Key 的名称),直接用它作为分组名
48
+ // Chrome 分组显示为 "CDP-张三的浏览器",一眼看出是谁的浏览器
49
+ if (groupName) {
50
+ var prefixG = (mode === 'takeover') ? 'TAKE-' : 'CDP-';
51
+ return prefixG + groupName;
52
+ }
47
53
  var hash = 0;
48
54
  for (var i = 0; i < clientId.length; i++) {
49
55
  var chr = clientId.charCodeAt(i);
@@ -56,8 +62,8 @@ var CDPUtils = (function() {
56
62
  return prefix + tag + suffix;
57
63
  }
58
64
 
59
- function getGroupBaseName(clientId, connectionTag, mode) {
60
- return buildGroupName(clientId, connectionTag, mode);
65
+ function getGroupBaseName(clientId, connectionTag, mode, groupName) {
66
+ return buildGroupName(clientId, connectionTag, mode, groupName);
61
67
  }
62
68
 
63
69
  function findGroupByName(allGroups, baseName) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cdp-tunnel",
3
- "version": "3.4.0",
3
+ "version": "3.6.0",
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",