cdp-tunnel 2.1.2 → 2.2.1

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',
@@ -158,10 +158,10 @@ importScripts('features/automation-badge.js');
158
158
  }
159
159
 
160
160
  var openerTabId = tab.openerTabId;
161
- var isOpenerControlled = openerTabId && State.isTabAttached(openerTabId);
161
+ var isOpenerControlled = openerTabId && State.isTabAttached(openerTabId) && !State.isPreExistingTab(openerTabId);
162
162
 
163
- // 只有当 opener CDP 控制时才跟踪新页面
164
- // 这样可以避免跟踪用户手动点击链接打开的页面
163
+ // 只有当 opener CDP 主动管理的 tab 时才跟踪新页面
164
+ // pre-existing tab 虽然也 attach 了 debugger,但属于用户 tab,不应继承 CDP 控制
165
165
  if (!openerTabId) {
166
166
  Logger.info('[Tabs] Tab has no opener, skipping. tabId:', tabId);
167
167
  return;
@@ -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
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "CDP Bridge",
4
- "version": "2.0.0",
4
+ "version": "2.2.1",
5
5
  "description": "Chrome DevTools Protocol Bridge for Playwright/Puppeteer automation",
6
6
  "permissions": [
7
7
  "debugger",
@@ -33,8 +33,13 @@
33
33
  },
34
34
  "web_accessible_resources": [
35
35
  {
36
- "resources": ["config-page-preview.html", "config-page.js"],
37
- "matches": ["<all_urls>"]
36
+ "resources": [
37
+ "config-page-preview.html",
38
+ "config-page.js"
39
+ ],
40
+ "matches": [
41
+ "<all_urls>"
42
+ ]
38
43
  }
39
44
  ]
40
45
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cdp-tunnel",
3
- "version": "2.1.2",
3
+ "version": "2.2.1",
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`);
@@ -697,6 +699,16 @@ function handlePluginConnection(ws, clientInfo) {
697
699
  }
698
700
  });
699
701
 
702
+ logDisconnect('PLUGIN_DISCONNECTED', {
703
+ pluginId: id,
704
+ code, reason: reason?.toString() || 'none',
705
+ remainingPlugins: pluginConnections.size,
706
+ affectedClients,
707
+ uptime: ws.connectedAt ? `${((Date.now() - ws.connectedAt) / 1000).toFixed(0)}s` : 'unknown',
708
+ activeSessions: sessionToClientId.size,
709
+ pendingRequests: pendingAttachRequests.size
710
+ });
711
+
700
712
  if (affectedClients.length > 0) {
701
713
  logConnectionEvent('PLUGIN_DISCONNECT_AFFECTED_CLIENTS', { pluginId: id, affectedClients });
702
714
  }
@@ -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) {