cdp-tunnel 2.5.20 → 2.5.22

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.
@@ -11,45 +11,51 @@ var WebSocketManager = (function() {
11
11
  }
12
12
 
13
13
  Config.getWsUrl(function(wsUrl) {
14
- Logger.info('[WS] Connecting to', wsUrl);
15
- setBadgeStatus('ON');
16
-
17
- try {
18
- ws = new WebSocket(wsUrl);
19
- State.setWs(ws);
20
-
21
- ws.onopen = function() {
22
- Logger.info('[WS] Connected');
23
- setBadgeStatus('ON');
24
- State.clearReconnectTimer();
25
- processQueue();
26
- broadcastStateUpdate();
27
- var extVersion = (typeof chrome !== 'undefined' && chrome.runtime && chrome.runtime.getManifest)
28
- ? chrome.runtime.getManifest().version : 'unknown';
29
- send({ type: 'plugin-hello', version: extVersion });
30
- };
31
-
32
- ws.onclose = function(event) {
33
- Logger.info('[WS] Closed:', event.code, event.reason);
34
- setBadgeStatus('OFF');
35
- scheduleReconnect();
36
- broadcastStateUpdate();
37
- };
14
+ Config.getPluginId(function(pluginId) {
15
+ if (pluginId) {
16
+ var sep = wsUrl.indexOf('?') >= 0 ? '&' : '?';
17
+ wsUrl += sep + 'pluginId=' + encodeURIComponent(pluginId);
18
+ }
19
+ Logger.info('[WS] Connecting to', wsUrl);
20
+ setBadgeStatus('ON');
38
21
 
39
- ws.onerror = function(error) {
40
- Logger.error('[WS] Error:', error);
22
+ try {
23
+ ws = new WebSocket(wsUrl);
24
+ State.setWs(ws);
25
+
26
+ ws.onopen = function() {
27
+ Logger.info('[WS] Connected');
28
+ setBadgeStatus('ON');
29
+ State.clearReconnectTimer();
30
+ processQueue();
31
+ broadcastStateUpdate();
32
+ var extVersion = (typeof chrome !== 'undefined' && chrome.runtime && chrome.runtime.getManifest)
33
+ ? chrome.runtime.getManifest().version : 'unknown';
34
+ send({ type: 'plugin-hello', version: extVersion });
35
+ };
36
+
37
+ ws.onclose = function(event) {
38
+ Logger.info('[WS] Closed:', event.code, event.reason);
39
+ setBadgeStatus('OFF');
40
+ scheduleReconnect();
41
+ broadcastStateUpdate();
42
+ };
43
+
44
+ ws.onerror = function(error) {
45
+ Logger.error('[WS] Error:', error);
46
+ setBadgeStatus('ERR');
47
+ broadcastStateUpdate();
48
+ };
49
+
50
+ ws.onmessage = function(event) {
51
+ handleRawMessage(event.data);
52
+ };
53
+ } catch (error) {
54
+ Logger.error('[WS] Failed to create:', error);
41
55
  setBadgeStatus('ERR');
42
- broadcastStateUpdate();
43
- };
44
-
45
- ws.onmessage = function(event) {
46
- handleRawMessage(event.data);
47
- };
48
- } catch (error) {
49
- Logger.error('[WS] Failed to create:', error);
50
- setBadgeStatus('ERR');
51
- scheduleReconnect();
52
- }
56
+ scheduleReconnect();
57
+ }
58
+ });
53
59
  });
54
60
  }
55
61
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "CDP Bridge",
4
- "version": "2.5.20",
4
+ "version": "2.5.22",
5
5
  "description": "Chrome DevTools Protocol Bridge for Playwright/Puppeteer automation",
6
6
  "permissions": [
7
7
  "debugger",
@@ -19,6 +19,7 @@
19
19
  "service_worker": "background.js"
20
20
  },
21
21
  "action": {
22
+ "default_popup": "popup.html",
22
23
  "default_title": "CDP Bridge",
23
24
  "default_icon": {
24
25
  "16": "icons/icon16.png",
@@ -42,4 +43,4 @@
42
43
  ]
43
44
  }
44
45
  ]
45
- }
46
+ }
@@ -0,0 +1,79 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <style>
6
+ * { margin: 0; padding: 0; box-sizing: border-box; }
7
+ body { width: 340px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #1a1a2e; color: #e0e0e0; font-size: 13px; }
8
+
9
+ .header { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px; border-bottom: 1px solid #2a2a4a; }
10
+ .header h1 { font-size: 15px; font-weight: 600; color: #fff; }
11
+ .status-badge { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 500; padding: 3px 10px; border-radius: 12px; }
12
+ .status-badge.on { background: rgba(76,175,80,0.15); color: #4CAF50; }
13
+ .status-badge.off { background: rgba(158,158,158,0.15); color: #9E9E9E; }
14
+ .status-badge.err { background: rgba(244,67,54,0.15); color: #F44336; }
15
+ .status-dot { width: 8px; height: 8px; border-radius: 50%; }
16
+ .status-badge.on .status-dot { background: #4CAF50; box-shadow: 0 0 6px #4CAF50; }
17
+ .status-badge.off .status-dot { background: #9E9E9E; }
18
+ .status-badge.err .status-dot { background: #F44336; }
19
+
20
+ .section { padding: 12px 16px; border-bottom: 1px solid #2a2a4a; }
21
+ .section-title { font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; color: #888; margin-bottom: 8px; }
22
+
23
+ .ws-address { display: flex; align-items: center; gap: 8px; }
24
+ .ws-address input { flex: 1; background: #16213e; border: 1px solid #2a2a4a; border-radius: 6px; padding: 7px 10px; color: #e0e0e0; font-size: 12px; font-family: monospace; outline: none; }
25
+ .ws-address input:focus { border-color: #4a9eff; }
26
+ .btn-sm { background: #2a2a4a; border: 1px solid #3a3a5a; border-radius: 6px; padding: 6px 12px; color: #e0e0e0; font-size: 12px; cursor: pointer; white-space: nowrap; }
27
+ .btn-sm:hover { background: #3a3a5a; }
28
+
29
+ .cdp-row { margin-bottom: 8px; }
30
+ .cdp-row:last-child { margin-bottom: 0; }
31
+ .cdp-label { font-size: 11px; color: #888; margin-bottom: 3px; }
32
+ .cdp-url { display: flex; align-items: center; gap: 6px; background: #16213e; border-radius: 6px; padding: 6px 10px; }
33
+ .cdp-url code { flex: 1; font-size: 11px; font-family: monospace; color: #4a9eff; word-break: break-all; line-height: 1.4; }
34
+ .copy-btn { background: none; border: 1px solid #3a3a5a; border-radius: 4px; padding: 3px 8px; color: #aaa; font-size: 11px; cursor: pointer; flex-shrink: 0; }
35
+ .copy-btn:hover { background: #3a3a5a; color: #fff; }
36
+ .copy-btn.copied { color: #4CAF50; border-color: #4CAF50; }
37
+
38
+ .clients-info { font-size: 12px; color: #aaa; }
39
+ .clients-info span { color: #4a9eff; }
40
+
41
+ .footer { padding: 8px 16px; text-align: center; }
42
+ .footer a { color: #666; font-size: 11px; text-decoration: none; }
43
+ .footer a:hover { color: #4a9eff; }
44
+ </style>
45
+ </head>
46
+ <body>
47
+ <div class="header">
48
+ <h1>CDP Bridge</h1>
49
+ <div class="status-badge off" id="statusBadge">
50
+ <div class="status-dot"></div>
51
+ <span id="statusText">未连接</span>
52
+ </div>
53
+ </div>
54
+
55
+ <div class="section">
56
+ <div class="section-title">WebSocket 地址</div>
57
+ <div class="ws-address">
58
+ <input type="text" id="wsInput" placeholder="ws://localhost:9221/plugin" spellcheck="false">
59
+ <button class="btn-sm" id="saveBtn">保存</button>
60
+ </div>
61
+ </div>
62
+
63
+ <div class="section" id="cdpSection" style="display:none;">
64
+ <div class="section-title">CDP 连接地址</div>
65
+ <div id="cdpAddresses"></div>
66
+ </div>
67
+
68
+ <div class="section" id="clientsSection" style="display:none;">
69
+ <div class="section-title">连接信息</div>
70
+ <div class="clients-info" id="clientsInfo"></div>
71
+ </div>
72
+
73
+ <div class="footer">
74
+ <a href="https://github.com" target="_blank">v2.5.21</a>
75
+ </div>
76
+
77
+ <script src="popup.js"></script>
78
+ </body>
79
+ </html>
@@ -0,0 +1,114 @@
1
+ (function() {
2
+ var $ = function(id) { return document.getElementById(id); };
3
+
4
+ var wsInput = $('wsInput');
5
+ var saveBtn = $('saveBtn');
6
+ var statusBadge = $('statusBadge');
7
+ var statusText = $('statusText');
8
+ var cdpSection = $('cdpSection');
9
+ var cdpAddresses = $('cdpAddresses');
10
+ var clientsSection = $('clientsSection');
11
+ var clientsInfo = $('clientsInfo');
12
+
13
+ function loadState() {
14
+ chrome.storage.local.get(['wsAddress'], function(result) {
15
+ wsInput.value = result.wsAddress || '';
16
+ });
17
+ chrome.runtime.sendMessage({ type: 'popup-query' }, function(state) {
18
+ if (!state) return;
19
+ updateStatus(state);
20
+ updateCDP(state);
21
+ updateClients(state);
22
+ });
23
+ }
24
+
25
+ function updateStatus(state) {
26
+ var cls = state.connected ? 'on' : 'off';
27
+ var text = state.connected ? '已连接' : '未连接';
28
+ if (!state.connected && state.lastError) {
29
+ cls = 'err';
30
+ text = '错误';
31
+ }
32
+ statusBadge.className = 'status-badge ' + cls;
33
+ statusText.textContent = text;
34
+ }
35
+
36
+ function updateCDP(state) {
37
+ if (!state.connected || !state.pluginId) {
38
+ cdpSection.style.display = 'none';
39
+ return;
40
+ }
41
+ cdpSection.style.display = '';
42
+
43
+ var wsUrl = wsInput.value || 'ws://localhost:9221/plugin';
44
+ var cdpBase = wsUrl.replace(/\/plugin(\?.*)?$/, '');
45
+ var pluginId = state.pluginId;
46
+ var cdpPath = '/devtools/browser/' + pluginId;
47
+
48
+ var urls = [];
49
+ var parsed = parseWsUrl(wsUrl);
50
+ if (parsed) {
51
+ urls.push({ label: 'CDP 地址', url: parsed.protocol + '://' + parsed.host + cdpPath });
52
+ }
53
+
54
+ var html = '';
55
+ urls.forEach(function(item) {
56
+ html += '<div class="cdp-row">';
57
+ html += '<div class="cdp-label">' + item.label + '</div>';
58
+ html += '<div class="cdp-url">';
59
+ html += '<code>' + escapeHtml(item.url) + '</code>';
60
+ html += '<button class="copy-btn" data-url="' + escapeAttr(item.url) + '">复制</button>';
61
+ html += '</div></div>';
62
+ });
63
+ cdpAddresses.innerHTML = html;
64
+ }
65
+
66
+ function updateClients(state) {
67
+ if (!state.connected) {
68
+ clientsSection.style.display = 'none';
69
+ return;
70
+ }
71
+ clientsSection.style.display = '';
72
+ var clients = state.cdpClients || [];
73
+ var attached = state.attachedPages || [];
74
+ clientsInfo.innerHTML = '活跃连接: <span>' + clients.length + '</span> | 已附加页面: <span>' + attached.length + '</span>';
75
+ }
76
+
77
+ function parseWsUrl(url) {
78
+ var m = url.match(/^(wss?):\/\/([^\/]+)/);
79
+ if (!m) return null;
80
+ return { protocol: m[1], host: m[2] };
81
+ }
82
+
83
+ function escapeHtml(s) {
84
+ return s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
85
+ }
86
+ function escapeAttr(s) {
87
+ return s.replace(/&/g,'&amp;').replace(/"/g,'&quot;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
88
+ }
89
+
90
+ saveBtn.addEventListener('click', function() {
91
+ var val = wsInput.value.trim();
92
+ chrome.storage.local.set({ wsAddress: val }, function() {
93
+ saveBtn.textContent = '已保存';
94
+ setTimeout(function() { saveBtn.textContent = '保存'; }, 1500);
95
+ chrome.runtime.sendMessage({ type: 'ws-reconnect' });
96
+ });
97
+ });
98
+
99
+ cdpAddresses.addEventListener('click', function(e) {
100
+ var btn = e.target.closest('.copy-btn');
101
+ if (!btn) return;
102
+ var url = btn.getAttribute('data-url');
103
+ navigator.clipboard.writeText(url).then(function() {
104
+ btn.textContent = '已复制';
105
+ btn.classList.add('copied');
106
+ setTimeout(function() {
107
+ btn.textContent = '复制';
108
+ btn.classList.remove('copied');
109
+ }, 2000);
110
+ });
111
+ });
112
+
113
+ loadState();
114
+ })();
@@ -1,5 +1,5 @@
1
1
  var Config = {
2
- WS_URL: 'ws://localhost:19221/plugin',
2
+ WS_URL: 'ws://localhost:9221/plugin',
3
3
  RECONNECT_DELAY: 3000,
4
4
  DEBUGGER_VERSION: '1.3',
5
5
  HEARTBEAT_INTERVAL: 25000,
@@ -17,6 +17,18 @@ var Config = {
17
17
  callback(result.wsAddress || Config.WS_URL);
18
18
  });
19
19
  },
20
+ getPluginId: function(callback) {
21
+ chrome.storage.local.get(['pluginId'], function(result) {
22
+ if (result.pluginId) {
23
+ callback(result.pluginId);
24
+ } else {
25
+ var id = 'browser_' + Date.now() + '_' + Math.random().toString(36).substr(2, 8);
26
+ chrome.storage.local.set({ pluginId: id }, function() {
27
+ callback(id);
28
+ });
29
+ }
30
+ });
31
+ },
20
32
  AUTO_MUTE: true,
21
33
  getAutoMute: function(callback) {
22
34
  chrome.storage.local.get(['autoMute'], function(result) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cdp-tunnel",
3
- "version": "2.5.20",
3
+ "version": "2.5.22",
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",
@@ -53,8 +53,13 @@
53
53
  "LICENSE"
54
54
  ],
55
55
  "dependencies": {
56
+ "bcryptjs": "^3.0.3",
57
+ "better-sqlite3": "^12.10.0",
56
58
  "cdp-tunnel": "^2.5.17",
57
59
  "commander": "^12.0.0",
60
+ "http-proxy": "^1.18.1",
61
+ "jsonwebtoken": "^9.0.3",
62
+ "uuid": "^14.0.0",
58
63
  "ws": "^8.16.0"
59
64
  },
60
65
  "devDependencies": {