cdp-tunnel 2.1.1 → 2.2.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.
- package/cli/index.js +18 -0
- package/extension-new/config-page-preview.html +1 -1
- package/extension-new/config-page.js +6 -0
- package/extension-new/core/state.js +1 -1
- package/extension-new/core/websocket.js +30 -5
- package/package.json +1 -1
- package/server/modules/logger.js +26 -1
- package/server/proxy-server.js +42 -2
package/cli/index.js
CHANGED
|
@@ -20,6 +20,21 @@ program
|
|
|
20
20
|
.description('Chrome DevTools Protocol Tunnel')
|
|
21
21
|
.version(require(path.join(__dirname, '..', 'package.json')).version);
|
|
22
22
|
|
|
23
|
+
function syncExtensionVersion() {
|
|
24
|
+
try {
|
|
25
|
+
const pkgVersion = require(path.join(__dirname, '..', 'package.json')).version;
|
|
26
|
+
const manifestPath = path.join(__dirname, '..', 'extension-new', 'manifest.json');
|
|
27
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
28
|
+
if (manifest.version !== pkgVersion) {
|
|
29
|
+
manifest.version = pkgVersion;
|
|
30
|
+
manifest.description = `CDP Tunnel v${pkgVersion} — ${manifest.description.split('—')[1] || manifest.description}`.trim();
|
|
31
|
+
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
|
|
32
|
+
}
|
|
33
|
+
} catch {}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
syncExtensionVersion();
|
|
37
|
+
|
|
23
38
|
function log(color, ...args) {
|
|
24
39
|
const colors = {
|
|
25
40
|
green: '\x1b[32m',
|
|
@@ -104,6 +119,9 @@ function checkChromeExtension() {
|
|
|
104
119
|
}
|
|
105
120
|
|
|
106
121
|
function getExtensionPath() {
|
|
122
|
+
if (process.env.CDP_TUNNEL_DEV_EXT) {
|
|
123
|
+
return process.env.CDP_TUNNEL_DEV_EXT;
|
|
124
|
+
}
|
|
107
125
|
const cliDir = __dirname;
|
|
108
126
|
return path.join(cliDir, '..', 'extension-new');
|
|
109
127
|
}
|
|
@@ -657,7 +657,7 @@
|
|
|
657
657
|
<nav class="navbar">
|
|
658
658
|
<div class="navbar-brand">
|
|
659
659
|
<div class="logo">🔌</div>
|
|
660
|
-
<h1>CDP Bridge
|
|
660
|
+
<h1>CDP Bridge <small id="versionBadge" style="font-size:12px;color:#888;font-weight:normal"></small></h1>
|
|
661
661
|
</div>
|
|
662
662
|
<div class="status-badge disconnected" id="statusBadge">
|
|
663
663
|
<div class="dot"></div>
|
|
@@ -267,6 +267,12 @@
|
|
|
267
267
|
chrome.storage.local.set({ autoMute: e.target.checked });
|
|
268
268
|
addLog(e.target.checked ? '🔇 自动静音已开启' : '🔊 自动静音已关闭', 'action');
|
|
269
269
|
});
|
|
270
|
+
|
|
271
|
+
var versionBadge = document.getElementById('versionBadge');
|
|
272
|
+
if (versionBadge) {
|
|
273
|
+
var manifest = chrome.runtime.getManifest();
|
|
274
|
+
versionBadge.textContent = 'v' + manifest.version;
|
|
275
|
+
}
|
|
270
276
|
|
|
271
277
|
fetchState();
|
|
272
278
|
|
|
@@ -285,7 +285,7 @@ var State = (function() {
|
|
|
285
285
|
}
|
|
286
286
|
|
|
287
287
|
var cdpGroups = groups.filter(function(g) {
|
|
288
|
-
return g.title && g.title.indexOf('CDP-') === 0;
|
|
288
|
+
return g.title && (g.title.indexOf('CDP-') === 0 || g.title.indexOf('CDP #') === 0);
|
|
289
289
|
});
|
|
290
290
|
|
|
291
291
|
if (cdpGroups.length === 0) {
|
|
@@ -316,6 +316,7 @@ var WebSocketManager = (function() {
|
|
|
316
316
|
|
|
317
317
|
function closeTabsByClientId(clientId, resolve) {
|
|
318
318
|
var attachedTabs = State.getAttachedTabIds();
|
|
319
|
+
var groupId = State.getGroupIdForClient(clientId);
|
|
319
320
|
var tabsToClose = [];
|
|
320
321
|
|
|
321
322
|
attachedTabs.forEach(function(tabId) {
|
|
@@ -329,10 +330,35 @@ var WebSocketManager = (function() {
|
|
|
329
330
|
resolve();
|
|
330
331
|
return;
|
|
331
332
|
}
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
333
|
+
|
|
334
|
+
if (groupId) {
|
|
335
|
+
chrome.tabs.query({ groupId: groupId }, function(groupTabs) {
|
|
336
|
+
if (chrome.runtime.lastError || !groupTabs) {
|
|
337
|
+
resolve();
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
var groupTabIds = new Set(groupTabs.map(function(t) { return t.id; }));
|
|
341
|
+
var safeToClose = tabsToClose.filter(function(tabId) {
|
|
342
|
+
return groupTabIds.has(tabId);
|
|
343
|
+
});
|
|
344
|
+
var unsafeCount = tabsToClose.length - safeToClose.length;
|
|
345
|
+
if (unsafeCount > 0) {
|
|
346
|
+
Logger.info('[WS] Protecting ' + unsafeCount + ' tabs outside group from deletion');
|
|
347
|
+
}
|
|
348
|
+
doCloseTabs(safeToClose, clientId, resolve);
|
|
349
|
+
});
|
|
350
|
+
} else {
|
|
351
|
+
doCloseTabs(tabsToClose, clientId, resolve);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function doCloseTabs(tabIds, clientId, resolve) {
|
|
356
|
+
if (tabIds.length === 0) {
|
|
357
|
+
resolve();
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
Logger.info('[WS] Closing ' + tabIds.length + ' attached tabs for clientId:', clientId);
|
|
361
|
+
tabIds.forEach(function(tabId) {
|
|
336
362
|
chrome.tabs.remove(tabId, function() {
|
|
337
363
|
if (chrome.runtime.lastError) {
|
|
338
364
|
Logger.info('[WS] Tab already closed:', tabId);
|
|
@@ -341,7 +367,6 @@ var WebSocketManager = (function() {
|
|
|
341
367
|
chrome.debugger.detach({ tabId: tabId }).catch(function() {});
|
|
342
368
|
State.removeAttachedTab(tabId);
|
|
343
369
|
});
|
|
344
|
-
|
|
345
370
|
resolve();
|
|
346
371
|
}
|
|
347
372
|
|
package/package.json
CHANGED
package/server/modules/logger.js
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
3
4
|
|
|
4
5
|
const logDir = path.join(__dirname, '../../logs');
|
|
5
6
|
if (!fs.existsSync(logDir)) {
|
|
6
7
|
fs.mkdirSync(logDir, { recursive: true });
|
|
7
8
|
}
|
|
8
9
|
|
|
10
|
+
const cdpDir = path.join(os.homedir(), '.cdp-tunnel');
|
|
11
|
+
|
|
9
12
|
const logFile = path.join(logDir, 'cdp-debug.log');
|
|
10
13
|
const statusLogFile = path.join(logDir, 'server-status.log');
|
|
14
|
+
const disconnectLogFile = path.join(cdpDir, 'disconnect.log');
|
|
11
15
|
|
|
12
16
|
const MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
13
17
|
const MAX_LOG_FILES = 5;
|
|
@@ -117,6 +121,26 @@ function clearLog() {
|
|
|
117
121
|
}
|
|
118
122
|
}
|
|
119
123
|
|
|
124
|
+
function logDisconnect(event, details) {
|
|
125
|
+
const timestamp = new Date().toISOString();
|
|
126
|
+
const separator = '='.repeat(70);
|
|
127
|
+
const lines = [
|
|
128
|
+
separator,
|
|
129
|
+
`[${timestamp}] ${event}`,
|
|
130
|
+
JSON.stringify(details, null, 2),
|
|
131
|
+
''
|
|
132
|
+
].join('\n');
|
|
133
|
+
try {
|
|
134
|
+
const dir = path.dirname(disconnectLogFile);
|
|
135
|
+
if (!fs.existsSync(dir)) {
|
|
136
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
137
|
+
}
|
|
138
|
+
fs.appendFileSync(disconnectLogFile, lines);
|
|
139
|
+
} catch (e) {
|
|
140
|
+
console.error('[LOGGER] Error writing disconnect log:', e.message);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
120
144
|
const NOISY_METHODS = [
|
|
121
145
|
'Runtime.consoleAPICalled',
|
|
122
146
|
'Network.requestWillBeSent',
|
|
@@ -249,5 +273,6 @@ module.exports = {
|
|
|
249
273
|
logFile,
|
|
250
274
|
logStatus,
|
|
251
275
|
logConnectionEvent,
|
|
252
|
-
flushAllLogs
|
|
276
|
+
flushAllLogs,
|
|
277
|
+
logDisconnect
|
|
253
278
|
};
|
package/server/proxy-server.js
CHANGED
|
@@ -16,7 +16,7 @@ const path = require('path');
|
|
|
16
16
|
const os = require('os');
|
|
17
17
|
const { execSync, spawn: spawnProcess } = require('child_process');
|
|
18
18
|
const { CONFIG, BROWSER_ID, shouldLog } = require('./modules/config');
|
|
19
|
-
const { logCDP, logEvent, clearLog, logStatus, logConnectionEvent, flushAllLogs } = require('./modules/logger');
|
|
19
|
+
const { logCDP, logEvent, clearLog, logStatus, logConnectionEvent, flushAllLogs, logDisconnect } = require('./modules/logger');
|
|
20
20
|
|
|
21
21
|
const PORT = CONFIG.PORT;
|
|
22
22
|
const CONFIG_DIR = path.join(os.homedir(), '.cdp-tunnel');
|
|
@@ -154,11 +154,13 @@ const browserContextToClientId = new Map();
|
|
|
154
154
|
const clientIdToBrowserContext = new Map();
|
|
155
155
|
let globalRequestIdCounter = 0;
|
|
156
156
|
|
|
157
|
+
const { version: PKG_VERSION } = require('../package.json');
|
|
158
|
+
|
|
157
159
|
let cachedTargets = [];
|
|
158
160
|
let lastTargetsUpdate = 0;
|
|
159
161
|
|
|
160
162
|
console.log('='.repeat(60));
|
|
161
|
-
console.log(
|
|
163
|
+
console.log(` WebSocket CDP Proxy Server v${PKG_VERSION}`);
|
|
162
164
|
console.log('='.repeat(60));
|
|
163
165
|
console.log(` Server started on port ${PORT}`);
|
|
164
166
|
console.log(` - Plugin path: ws://localhost:${PORT}/plugin`);
|
|
@@ -670,6 +672,16 @@ function handlePluginConnection(ws, clientInfo) {
|
|
|
670
672
|
totalClients: clientConnections.size
|
|
671
673
|
});
|
|
672
674
|
|
|
675
|
+
logDisconnect('PLUGIN_DISCONNECTED', {
|
|
676
|
+
pluginId: id,
|
|
677
|
+
code, reason: reason?.toString() || 'none',
|
|
678
|
+
remainingPlugins: pluginConnections.size,
|
|
679
|
+
affectedClients: affectedClients.map(c => c),
|
|
680
|
+
uptime: ws.connectedAt ? `${((Date.now() - ws.connectedAt) / 1000).toFixed(0)}s` : 'unknown',
|
|
681
|
+
activeSessions: sessionToClientId.size,
|
|
682
|
+
pendingRequests: pendingAttachRequests.size
|
|
683
|
+
});
|
|
684
|
+
|
|
673
685
|
if (pluginConnections.size === 0) {
|
|
674
686
|
updateExtensionState(false);
|
|
675
687
|
}
|
|
@@ -963,6 +975,20 @@ function handleClientConnection(ws, clientInfo, customClientId = null) {
|
|
|
963
975
|
totalPlugins: pluginConnections.size,
|
|
964
976
|
totalClients: clientConnections.size
|
|
965
977
|
});
|
|
978
|
+
|
|
979
|
+
const isUnexpected = code !== 1000 && code !== 1001;
|
|
980
|
+
if (isUnexpected) {
|
|
981
|
+
logDisconnect('CLIENT_DISCONNECTED_UNEXPECTED', {
|
|
982
|
+
clientId: id,
|
|
983
|
+
code, reason: reason?.toString() || 'none',
|
|
984
|
+
sessionsLost: sessionsToClean.length,
|
|
985
|
+
cdpMethodsUsed: ws.cdpTrace ? [...new Set(ws.cdpTrace)] : [],
|
|
986
|
+
uptime: ws.connectedAt ? `${((Date.now() - ws.connectedAt) / 1000).toFixed(0)}s` : 'unknown',
|
|
987
|
+
remainingClients: clientConnections.size,
|
|
988
|
+
pluginAlive: pluginConnections.size > 0,
|
|
989
|
+
pairedPluginId: ws.pairedPlugin?.id || null
|
|
990
|
+
});
|
|
991
|
+
}
|
|
966
992
|
|
|
967
993
|
if (ws.cdpTrace && ws.cdpTrace.length && shouldLog('debug')) {
|
|
968
994
|
const unique = [...new Set(ws.cdpTrace)];
|
|
@@ -1343,6 +1369,14 @@ const heartbeatInterval = setInterval(() => {
|
|
|
1343
1369
|
console.log(`[${now}] Plugin ${ws.id} not responding, terminating...`);
|
|
1344
1370
|
}
|
|
1345
1371
|
logConnectionEvent('HEARTBEAT_TIMEOUT', { type: 'plugin', id: ws.id });
|
|
1372
|
+
logDisconnect('HEARTBEAT_TIMEOUT_PLUGIN', {
|
|
1373
|
+
pluginId: ws.id,
|
|
1374
|
+
pairedClientId: ws.pairedClientId || null,
|
|
1375
|
+
uptime: ws.connectedAt ? `${((Date.now() - ws.connectedAt) / 1000).toFixed(0)}s` : 'unknown',
|
|
1376
|
+
remainingPlugins: pluginConnections.size,
|
|
1377
|
+
activeClients: clientConnections.size,
|
|
1378
|
+
activeSessions: sessionToClientId.size
|
|
1379
|
+
});
|
|
1346
1380
|
pluginConnections.delete(ws);
|
|
1347
1381
|
if (ws.pairedClientId) {
|
|
1348
1382
|
connectionPairs.delete(ws.pairedClientId);
|
|
@@ -1365,6 +1399,12 @@ const heartbeatInterval = setInterval(() => {
|
|
|
1365
1399
|
console.log(`[${now}] Client ${ws.id} not responding, terminating...`);
|
|
1366
1400
|
}
|
|
1367
1401
|
logConnectionEvent('HEARTBEAT_TIMEOUT', { type: 'client', id: ws.id });
|
|
1402
|
+
logDisconnect('HEARTBEAT_TIMEOUT_CLIENT', {
|
|
1403
|
+
clientId: ws.id,
|
|
1404
|
+
uptime: ws.connectedAt ? `${((Date.now() - ws.connectedAt) / 1000).toFixed(0)}s` : 'unknown',
|
|
1405
|
+
remainingClients: clientConnections.size,
|
|
1406
|
+
pluginAlive: pluginConnections.size > 0
|
|
1407
|
+
});
|
|
1368
1408
|
clientConnections.delete(ws);
|
|
1369
1409
|
clientById.delete(ws.id);
|
|
1370
1410
|
if (ws.pairedPlugin) {
|