cdp-tunnel 2.8.2 → 2.8.4
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.
|
@@ -341,7 +341,7 @@ importScripts('features/automation-badge.js');
|
|
|
341
341
|
chrome.runtime.onInstalled.addListener(function(details) {
|
|
342
342
|
Logger.info('[Runtime] Extension installed/updated:', details.reason);
|
|
343
343
|
State.persist(null, false);
|
|
344
|
-
setBadgeStatus('
|
|
344
|
+
setBadgeStatus('OFF');
|
|
345
345
|
init();
|
|
346
346
|
});
|
|
347
347
|
|
|
@@ -453,21 +453,22 @@ importScripts('features/automation-badge.js');
|
|
|
453
453
|
sendResponse({ success: true });
|
|
454
454
|
} else if (message.type === 'get-connection-statuses') {
|
|
455
455
|
Config.getConnections(function(connections) {
|
|
456
|
-
var
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
} else {
|
|
456
|
+
var list = (connections || []).map(function(conn) {
|
|
457
|
+
var status = 'disabled';
|
|
458
|
+
var attachedCount = 0;
|
|
459
|
+
if (conn.enabled) {
|
|
461
460
|
var entry = ConnectionManager.getConnection(conn.id);
|
|
462
461
|
if (entry) {
|
|
463
462
|
var ws = entry.state.getWs();
|
|
464
|
-
|
|
463
|
+
status = (ws && ws.readyState === WebSocket.OPEN) ? 'connected' : 'error';
|
|
464
|
+
attachedCount = entry.state.getAttachedTabIds().length;
|
|
465
465
|
} else {
|
|
466
|
-
|
|
466
|
+
status = 'error';
|
|
467
467
|
}
|
|
468
468
|
}
|
|
469
|
+
return { id: conn.id, tag: conn.tag, url: conn.url, status: status, attachedCount: attachedCount };
|
|
469
470
|
});
|
|
470
|
-
sendResponse({
|
|
471
|
+
sendResponse({ connections: list });
|
|
471
472
|
});
|
|
472
473
|
return true;
|
|
473
474
|
} else if (message.type === 'add-connection') {
|
|
@@ -258,8 +258,12 @@
|
|
|
258
258
|
function loadConnectionStatuses() {
|
|
259
259
|
if (typeof chrome !== 'undefined' && chrome.runtime) {
|
|
260
260
|
chrome.runtime.sendMessage({ type: 'get-connection-statuses' }, function(response) {
|
|
261
|
-
if (response && response.
|
|
262
|
-
|
|
261
|
+
if (response && response.connections) {
|
|
262
|
+
var statuses = {};
|
|
263
|
+
response.connections.forEach(function(conn) {
|
|
264
|
+
statuses[conn.id] = conn.status;
|
|
265
|
+
});
|
|
266
|
+
state.connectionStatuses = statuses;
|
|
263
267
|
}
|
|
264
268
|
loadAndRenderConnections();
|
|
265
269
|
});
|
|
@@ -431,6 +435,8 @@
|
|
|
431
435
|
addLog(message.logType, message.message);
|
|
432
436
|
} else if (message.type === 'connections-updated') {
|
|
433
437
|
loadAndRenderConnections();
|
|
438
|
+
} else if (message.type === 'connection-status-changed') {
|
|
439
|
+
loadConnectionStatuses();
|
|
434
440
|
}
|
|
435
441
|
});
|
|
436
442
|
}
|
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
function updateBadgeFromAllConnections() {
|
|
2
|
+
var hasConnected = false;
|
|
3
|
+
var hasEnabled = false;
|
|
4
|
+
ConnectionManager.forEachConnection(function(entry) {
|
|
5
|
+
var ws = entry.state.getWs();
|
|
6
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
7
|
+
hasConnected = true;
|
|
8
|
+
}
|
|
9
|
+
if (entry.config && entry.config.enabled !== false) {
|
|
10
|
+
hasEnabled = true;
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
var status;
|
|
14
|
+
if (hasConnected) {
|
|
15
|
+
status = 'ON';
|
|
16
|
+
} else if (hasEnabled) {
|
|
17
|
+
status = 'ERR';
|
|
18
|
+
} else {
|
|
19
|
+
status = 'OFF';
|
|
20
|
+
}
|
|
21
|
+
setBadgeStatus(status);
|
|
22
|
+
chrome.runtime.sendMessage({ type: 'connection-status-changed' }).catch(function() {});
|
|
23
|
+
}
|
|
24
|
+
|
|
1
25
|
var WebSocketConnection = (function() {
|
|
2
26
|
function WebSocketConnection(connectionId, state, config) {
|
|
3
27
|
this.connectionId = connectionId;
|
|
@@ -24,7 +48,7 @@ var WebSocketConnection = (function() {
|
|
|
24
48
|
wsUrl += sep + 'pluginId=' + encodeURIComponent(pluginId);
|
|
25
49
|
}
|
|
26
50
|
Logger.info('[WS:' + self.connectionId + '] Connecting to', wsUrl);
|
|
27
|
-
|
|
51
|
+
updateBadgeFromAllConnections();
|
|
28
52
|
|
|
29
53
|
try {
|
|
30
54
|
ws = new WebSocket(wsUrl);
|
|
@@ -32,7 +56,7 @@ var WebSocketConnection = (function() {
|
|
|
32
56
|
|
|
33
57
|
ws.onopen = function() {
|
|
34
58
|
Logger.info('[WS:' + self.connectionId + '] Connected');
|
|
35
|
-
|
|
59
|
+
updateBadgeFromAllConnections();
|
|
36
60
|
self.state.clearReconnectTimer();
|
|
37
61
|
self._processQueue();
|
|
38
62
|
self._broadcastStateUpdate();
|
|
@@ -43,14 +67,14 @@ var WebSocketConnection = (function() {
|
|
|
43
67
|
|
|
44
68
|
ws.onclose = function(event) {
|
|
45
69
|
Logger.info('[WS:' + self.connectionId + '] Closed:', event.code, event.reason);
|
|
46
|
-
|
|
70
|
+
updateBadgeFromAllConnections();
|
|
47
71
|
self._scheduleReconnect();
|
|
48
72
|
self._broadcastStateUpdate();
|
|
49
73
|
};
|
|
50
74
|
|
|
51
75
|
ws.onerror = function(error) {
|
|
52
76
|
Logger.error('[WS:' + self.connectionId + '] Error:', error);
|
|
53
|
-
|
|
77
|
+
updateBadgeFromAllConnections();
|
|
54
78
|
self._broadcastStateUpdate();
|
|
55
79
|
};
|
|
56
80
|
|
|
@@ -58,9 +82,9 @@ var WebSocketConnection = (function() {
|
|
|
58
82
|
self._handleRawMessage(event.data);
|
|
59
83
|
};
|
|
60
84
|
} catch (error) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
85
|
+
Logger.error('[WS:' + self.connectionId + '] Failed to create:', error);
|
|
86
|
+
updateBadgeFromAllConnections();
|
|
87
|
+
self._scheduleReconnect();
|
|
64
88
|
}
|
|
65
89
|
});
|
|
66
90
|
};
|
package/extension-new/popup.html
CHANGED
|
@@ -4,76 +4,51 @@
|
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<style>
|
|
6
6
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
7
|
-
body { width:
|
|
7
|
+
body { width: 320px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #1a1a2e; color: #e0e0e0; font-size: 13px; }
|
|
8
8
|
|
|
9
|
-
.header { display: flex; align-items: center; justify-content: space-between; padding:
|
|
9
|
+
.header { display: flex; align-items: center; justify-content: space-between; padding: 10px 14px; border-bottom: 1px solid #2a2a4a; }
|
|
10
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
11
|
|
|
20
|
-
.
|
|
21
|
-
.section-title { font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; color: #888; margin-bottom: 8px; }
|
|
12
|
+
.body { padding: 10px 14px; }
|
|
22
13
|
|
|
23
|
-
|
|
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; }
|
|
14
|
+
#connectionList { background: #16213e; border-radius: 6px; overflow: hidden; }
|
|
28
15
|
|
|
29
|
-
.
|
|
30
|
-
.
|
|
31
|
-
.
|
|
32
|
-
.
|
|
33
|
-
.
|
|
34
|
-
.
|
|
35
|
-
.
|
|
36
|
-
.
|
|
16
|
+
.conn-item { padding: 8px 10px; border-bottom: 1px solid rgba(255,255,255,0.08); }
|
|
17
|
+
.conn-item:last-child { border-bottom: none; }
|
|
18
|
+
.conn-header { display: flex; align-items: center; gap: 6px; }
|
|
19
|
+
.conn-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
|
|
20
|
+
.conn-dot.connected { background: #4caf50; box-shadow: 0 0 4px #4caf50; }
|
|
21
|
+
.conn-dot.error { background: #f44336; box-shadow: 0 0 4px #f44336; }
|
|
22
|
+
.conn-dot.disabled { background: #9e9e9e; }
|
|
23
|
+
.conn-tag { font-weight: 600; font-size: 13px; color: #e0e0e0; }
|
|
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; }
|
|
37
25
|
|
|
38
|
-
.
|
|
39
|
-
.clients-info span { color: #4a9eff; }
|
|
26
|
+
.empty-hint { text-align: center; padding: 16px 10px; color: #777; font-size: 12px; }
|
|
40
27
|
|
|
41
|
-
.
|
|
28
|
+
.stats-row { display: flex; gap: 16px; padding: 8px 0; font-size: 12px; color: #aaa; }
|
|
29
|
+
.stats-row span b { color: #4a9eff; font-weight: 600; }
|
|
30
|
+
|
|
31
|
+
.btn-full { width: 100%; background: linear-gradient(135deg, #4a9eff, #357abd); border: none; border-radius: 6px; padding: 10px; color: #fff; font-size: 13px; font-weight: 600; cursor: pointer; transition: opacity 0.15s; margin-top: 6px; }
|
|
42
32
|
.btn-full:hover { opacity: 0.85; }
|
|
43
33
|
|
|
44
|
-
.footer { padding:
|
|
45
|
-
.footer a { color: #
|
|
34
|
+
.footer { padding: 6px 14px; text-align: right; }
|
|
35
|
+
.footer a { color: #555; font-size: 11px; text-decoration: none; }
|
|
46
36
|
.footer a:hover { color: #4a9eff; }
|
|
47
37
|
</style>
|
|
48
38
|
</head>
|
|
49
39
|
<body>
|
|
50
40
|
<div class="header">
|
|
51
41
|
<h1>CDP Bridge</h1>
|
|
52
|
-
<div class="status-badge off" id="statusBadge">
|
|
53
|
-
<div class="status-dot"></div>
|
|
54
|
-
<span id="statusText">未连接</span>
|
|
55
|
-
</div>
|
|
56
|
-
</div>
|
|
57
|
-
|
|
58
|
-
<div class="section">
|
|
59
|
-
<div class="section-title">WebSocket 地址</div>
|
|
60
|
-
<div class="ws-address">
|
|
61
|
-
<input type="text" id="wsInput" placeholder="ws://localhost:9221/plugin" spellcheck="false">
|
|
62
|
-
<button class="btn-sm" id="saveBtn">保存</button>
|
|
63
|
-
</div>
|
|
64
42
|
</div>
|
|
65
43
|
|
|
66
|
-
<div class="
|
|
67
|
-
<div
|
|
68
|
-
<div id="cdpAddresses"></div>
|
|
69
|
-
</div>
|
|
44
|
+
<div class="body">
|
|
45
|
+
<div id="connectionList"></div>
|
|
70
46
|
|
|
71
|
-
<div class="
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
</div>
|
|
47
|
+
<div class="stats-row" id="statsRow" style="display:none;">
|
|
48
|
+
<span>活跃连接: <b id="statClients">0</b></span>
|
|
49
|
+
<span>页面: <b id="statPages">0</b></span>
|
|
50
|
+
</div>
|
|
75
51
|
|
|
76
|
-
<div class="section">
|
|
77
52
|
<button class="btn-full" id="openConfigBtn">打开完整配置</button>
|
|
78
53
|
</div>
|
|
79
54
|
|
package/extension-new/popup.js
CHANGED
|
@@ -1,127 +1,85 @@
|
|
|
1
1
|
(function() {
|
|
2
2
|
var $ = function(id) { return document.getElementById(id); };
|
|
3
3
|
|
|
4
|
-
var
|
|
5
|
-
var
|
|
6
|
-
var
|
|
7
|
-
var
|
|
8
|
-
var cdpSection = $('cdpSection');
|
|
9
|
-
var cdpAddresses = $('cdpAddresses');
|
|
10
|
-
var clientsSection = $('clientsSection');
|
|
11
|
-
var clientsInfo = $('clientsInfo');
|
|
4
|
+
var connectionList = $('connectionList');
|
|
5
|
+
var statsRow = $('statsRow');
|
|
6
|
+
var statClients = $('statClients');
|
|
7
|
+
var statPages = $('statPages');
|
|
12
8
|
|
|
13
9
|
function loadState() {
|
|
14
|
-
chrome.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if (!state) return;
|
|
19
|
-
updateStatus(state);
|
|
20
|
-
updateCDP(state);
|
|
21
|
-
updateClients(state);
|
|
10
|
+
chrome.runtime.sendMessage({ type: 'get-connection-statuses' }, function(resp) {
|
|
11
|
+
if (!resp || !resp.connections) return;
|
|
12
|
+
renderConnectionList(resp.connections);
|
|
13
|
+
updateStats(resp.connections);
|
|
22
14
|
});
|
|
23
15
|
}
|
|
24
16
|
|
|
25
|
-
function
|
|
26
|
-
|
|
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
|
-
}
|
|
17
|
+
function renderConnectionList(connections) {
|
|
18
|
+
connectionList.innerHTML = '';
|
|
35
19
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
cdpSection.style.display = 'none';
|
|
20
|
+
if (!connections || connections.length === 0) {
|
|
21
|
+
connectionList.innerHTML = '<div class="empty-hint">尚未配置连接</div>';
|
|
39
22
|
return;
|
|
40
23
|
}
|
|
41
|
-
cdpSection.style.display = '';
|
|
42
24
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
var cdpPath = '/devtools/browser/' + pluginId;
|
|
25
|
+
connections.forEach(function(conn) {
|
|
26
|
+
var item = document.createElement('div');
|
|
27
|
+
item.className = 'conn-item';
|
|
47
28
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (parsed) {
|
|
51
|
-
urls.push({ label: 'CDP 地址', url: parsed.protocol + '://' + parsed.host + cdpPath });
|
|
52
|
-
}
|
|
29
|
+
var header = document.createElement('div');
|
|
30
|
+
header.className = 'conn-header';
|
|
53
31
|
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
}
|
|
32
|
+
var dot = document.createElement('span');
|
|
33
|
+
dot.className = 'conn-dot ' + conn.status;
|
|
65
34
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
}
|
|
35
|
+
var tag = document.createElement('span');
|
|
36
|
+
tag.className = 'conn-tag';
|
|
37
|
+
tag.textContent = conn.tag;
|
|
76
38
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (!m) return null;
|
|
80
|
-
return { protocol: m[1], host: m[2] };
|
|
81
|
-
}
|
|
39
|
+
header.appendChild(dot);
|
|
40
|
+
header.appendChild(tag);
|
|
82
41
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
function escapeAttr(s) {
|
|
87
|
-
return s.replace(/&/g,'&').replace(/"/g,'"').replace(/</g,'<').replace(/>/g,'>');
|
|
88
|
-
}
|
|
42
|
+
var url = document.createElement('div');
|
|
43
|
+
url.className = 'conn-url';
|
|
44
|
+
url.textContent = conn.url;
|
|
89
45
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
saveBtn.textContent = '已保存';
|
|
94
|
-
setTimeout(function() { saveBtn.textContent = '保存'; }, 1500);
|
|
95
|
-
chrome.runtime.sendMessage({ type: 'ws-reconnect' });
|
|
46
|
+
item.appendChild(header);
|
|
47
|
+
item.appendChild(url);
|
|
48
|
+
connectionList.appendChild(item);
|
|
96
49
|
});
|
|
97
|
-
}
|
|
50
|
+
}
|
|
98
51
|
|
|
99
|
-
|
|
100
|
-
var
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
btn.classList.add('copied');
|
|
106
|
-
setTimeout(function() {
|
|
107
|
-
btn.textContent = '复制';
|
|
108
|
-
btn.classList.remove('copied');
|
|
109
|
-
}, 2000);
|
|
52
|
+
function updateStats(connections) {
|
|
53
|
+
var active = 0;
|
|
54
|
+
var pages = 0;
|
|
55
|
+
connections.forEach(function(conn) {
|
|
56
|
+
if (conn.status === 'connected') active++;
|
|
57
|
+
pages += conn.attachedCount || 0;
|
|
110
58
|
});
|
|
111
|
-
});
|
|
112
59
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
60
|
+
if (active > 0 || pages > 0) {
|
|
61
|
+
statsRow.style.display = '';
|
|
62
|
+
} else {
|
|
63
|
+
statsRow.style.display = 'none';
|
|
64
|
+
}
|
|
65
|
+
statClients.textContent = active;
|
|
66
|
+
statPages.textContent = pages;
|
|
118
67
|
}
|
|
119
68
|
|
|
69
|
+
var openConfigBtn = $('openConfigBtn');
|
|
70
|
+
openConfigBtn.addEventListener('click', function() {
|
|
71
|
+
chrome.tabs.create({ url: chrome.runtime.getURL('config-page-preview.html') });
|
|
72
|
+
});
|
|
73
|
+
|
|
120
74
|
var versionLink = $('versionLink');
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
versionLink.textContent = 'v' + manifest.version;
|
|
124
|
-
}
|
|
75
|
+
var manifest = chrome.runtime.getManifest();
|
|
76
|
+
versionLink.textContent = 'v' + manifest.version;
|
|
125
77
|
|
|
126
78
|
loadState();
|
|
79
|
+
|
|
80
|
+
chrome.runtime.onMessage.addListener(function(message) {
|
|
81
|
+
if (message.type === 'connection-status-changed' || message.type === 'stateUpdate') {
|
|
82
|
+
loadState();
|
|
83
|
+
}
|
|
84
|
+
});
|
|
127
85
|
})();
|
package/package.json
CHANGED