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 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</h1>
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
- Logger.info('[WS] Closing ' + tabsToClose.length + ' attached tabs for clientId:', clientId);
334
-
335
- tabsToClose.forEach(function(tabId) {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cdp-tunnel",
3
- "version": "2.1.1",
3
+ "version": "2.2.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",
@@ -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
  };
@@ -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(' WebSocket CDP Proxy Server');
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) {