cdp-tunnel 2.0.0 → 2.1.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 +12 -0
- package/extension-new/background.js +18 -30
- package/extension-new/cdp/handler/special.js +15 -25
- package/extension-new/core/state.js +33 -2
- package/extension-new/core/websocket.js +18 -20
- package/extension-new/utils/helpers.js +40 -1
- package/package.json +1 -1
- package/server/proxy-server.js +12 -1
package/cli/index.js
CHANGED
|
@@ -181,6 +181,18 @@ function startServer(port, watchdog, autoRestart) {
|
|
|
181
181
|
console.log(' 重启次数: ' + restartTimestamps.length + '/' + MAX_RESTARTS + ' (60秒内)');
|
|
182
182
|
console.log('');
|
|
183
183
|
|
|
184
|
+
// Kill any leftover process occupying the port before restarting
|
|
185
|
+
try {
|
|
186
|
+
const result = execSync(`lsof -ti:${port} 2>/dev/null || true`).toString().trim();
|
|
187
|
+
if (result) {
|
|
188
|
+
const pids = result.split('\n').filter(p => p && parseInt(p) !== process.pid);
|
|
189
|
+
pids.forEach(p => { try { process.kill(parseInt(p), 'SIGKILL'); } catch {} });
|
|
190
|
+
if (pids.length > 0) {
|
|
191
|
+
log('gray', ' 已清理占用端口 ' + port + ' 的残留进程: ' + pids.join(', '));
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
} catch {}
|
|
195
|
+
|
|
184
196
|
setTimeout(() => startServer(port, true, autoRestart), 3000);
|
|
185
197
|
});
|
|
186
198
|
|
|
@@ -219,66 +219,54 @@ importScripts('features/automation-badge.js');
|
|
|
219
219
|
|
|
220
220
|
// 将标签页添加到CDP组(添加延迟等待)
|
|
221
221
|
setTimeout(function() {
|
|
222
|
-
// 获取openerTabId对应的clientId
|
|
223
222
|
var openerClientId = openerTabId ? State.getClientIdByTabId(openerTabId) : null;
|
|
224
|
-
var
|
|
225
|
-
|
|
226
|
-
// 如果有指定的clientId,使用该clientId作为组名
|
|
227
|
-
if (openerClientId) {
|
|
228
|
-
groupName = 'CDP-' + openerClientId.substring(0, 8);
|
|
229
|
-
Logger.info('[TabGroup] Using opener clientId for group name:', groupName, 'openerTabId:', openerTabId);
|
|
230
|
-
} else {
|
|
231
|
-
// 回退到使用第一个CDP客户端的ID
|
|
223
|
+
var groupClientId = openerClientId;
|
|
224
|
+
if (!groupClientId) {
|
|
232
225
|
var cdpClients = State.getCDPClients() || [];
|
|
233
226
|
if (cdpClients.length > 0 && cdpClients[0] && cdpClients[0].id) {
|
|
234
|
-
|
|
235
|
-
} else {
|
|
236
|
-
// 如果没有CDP客户端,使用时间戳作为组名
|
|
237
|
-
groupName = 'CDP-' + Date.now().toString(36);
|
|
227
|
+
groupClientId = cdpClients[0].id;
|
|
238
228
|
}
|
|
239
|
-
Logger.info('[TabGroup] Using fallback clientId for group name:', groupName);
|
|
240
229
|
}
|
|
241
230
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
231
|
+
if (!groupClientId) return;
|
|
232
|
+
var baseName = CDPUtils.getGroupBaseName(groupClientId);
|
|
233
|
+
Logger.info('[TabGroup] background onCreated, baseName:', baseName);
|
|
234
|
+
|
|
235
|
+
chrome.tabGroups.query({}, function(allGroups) {
|
|
236
|
+
var existing = CDPUtils.findGroupByName(allGroups, baseName);
|
|
237
|
+
if (existing) {
|
|
238
|
+
chrome.tabs.group({ tabIds: tabId, groupId: existing.id }, function(groupId) {
|
|
246
239
|
if (chrome.runtime.lastError) {
|
|
247
240
|
Logger.error('[TabGroup] Failed to add tab to group:', chrome.runtime.lastError.message);
|
|
248
241
|
} else {
|
|
249
|
-
|
|
242
|
+
State.setGroupIdForClient(groupClientId, existing.id);
|
|
243
|
+
Logger.info('[TabGroup] Tab added to group:', groupId);
|
|
250
244
|
}
|
|
251
245
|
});
|
|
252
246
|
} else {
|
|
253
|
-
// 创建新组并添加标签页
|
|
254
247
|
chrome.tabs.group({ tabIds: tabId }, function(groupId) {
|
|
255
248
|
if (chrome.runtime.lastError) {
|
|
256
249
|
Logger.error('[TabGroup] Failed to create group:', chrome.runtime.lastError.message);
|
|
257
250
|
return;
|
|
258
251
|
}
|
|
259
|
-
// 更新组的标题和颜色
|
|
260
252
|
if (groupId) {
|
|
261
|
-
// 为不同的组使用不同的颜色
|
|
262
|
-
var colors = ['blue', 'red', 'yellow', 'green', 'pink', 'purple', 'cyan', 'orange'];
|
|
263
|
-
var colorIndex = Math.abs(CDPUtils.hashCode(groupName)) % colors.length;
|
|
264
|
-
var groupColor = colors[colorIndex];
|
|
265
|
-
|
|
266
253
|
chrome.tabGroups.update(groupId, {
|
|
267
|
-
title:
|
|
268
|
-
color:
|
|
254
|
+
title: baseName,
|
|
255
|
+
color: CDPUtils.getGroupColorForClient(groupClientId),
|
|
269
256
|
collapsed: true
|
|
270
257
|
}, function(group) {
|
|
271
258
|
if (chrome.runtime.lastError) {
|
|
272
259
|
Logger.error('[TabGroup] Failed to update group:', chrome.runtime.lastError.message);
|
|
273
260
|
} else {
|
|
274
|
-
|
|
261
|
+
State.setGroupIdForClient(groupClientId, groupId);
|
|
262
|
+
Logger.info('[TabGroup] Group created:', group);
|
|
275
263
|
}
|
|
276
264
|
});
|
|
277
265
|
}
|
|
278
266
|
});
|
|
279
267
|
}
|
|
280
268
|
});
|
|
281
|
-
}, 2000);
|
|
269
|
+
}, 2000);
|
|
282
270
|
|
|
283
271
|
Logger.info('[Tabs] Sending Target.attachedToTarget event');
|
|
284
272
|
|
|
@@ -132,34 +132,28 @@ var SpecialHandler = (function() {
|
|
|
132
132
|
muteTabIfNeeded(tabId);
|
|
133
133
|
}, 500);
|
|
134
134
|
|
|
135
|
-
var groupName;
|
|
136
135
|
var groupClientId = clientId;
|
|
137
|
-
|
|
138
|
-
if (clientId) {
|
|
139
|
-
groupName = 'CDP-' + clientId.substring(0, 8);
|
|
140
|
-
} else {
|
|
136
|
+
if (!groupClientId) {
|
|
141
137
|
var cdpClients = State.getCDPClients() || [];
|
|
142
138
|
if (cdpClients.length > 0 && cdpClients[0] && cdpClients[0].id) {
|
|
143
|
-
groupName = 'CDP-' + cdpClients[0].id.substring(0, 8);
|
|
144
139
|
groupClientId = cdpClients[0].id;
|
|
145
|
-
} else {
|
|
146
|
-
groupName = 'CDP-Automation';
|
|
147
140
|
}
|
|
148
141
|
}
|
|
142
|
+
|
|
143
|
+
if (!groupClientId) return;
|
|
144
|
+
var baseName = CDPUtils.getGroupBaseName(groupClientId);
|
|
149
145
|
|
|
150
146
|
setTimeout(function() {
|
|
151
|
-
Logger.info('[TabGroup] Executing group operation
|
|
147
|
+
Logger.info('[TabGroup] Executing group operation for:', baseName);
|
|
152
148
|
|
|
153
149
|
chrome.tabGroups.query({}, function(allGroups) {
|
|
154
|
-
var
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
if (groups.length > 0) {
|
|
158
|
-
chrome.tabs.group({ tabIds: tabId, groupId: groups[0].id }, function(result) {
|
|
150
|
+
var existing = CDPUtils.findGroupByName(allGroups, baseName);
|
|
151
|
+
if (existing) {
|
|
152
|
+
chrome.tabs.group({ tabIds: tabId, groupId: existing.id }, function(result) {
|
|
159
153
|
if (chrome.runtime.lastError) {
|
|
160
154
|
Logger.error('[TabGroup] Failed to add tab to group:', chrome.runtime.lastError.message);
|
|
161
155
|
} else {
|
|
162
|
-
State.setGroupIdForClient(groupClientId,
|
|
156
|
+
State.setGroupIdForClient(groupClientId, existing.id);
|
|
163
157
|
updateTabGroupName(groupClientId);
|
|
164
158
|
}
|
|
165
159
|
});
|
|
@@ -170,12 +164,9 @@ var SpecialHandler = (function() {
|
|
|
170
164
|
return;
|
|
171
165
|
}
|
|
172
166
|
if (groupId) {
|
|
173
|
-
var colors = ['blue', 'red', 'yellow', 'green', 'pink', 'purple', 'cyan', 'orange'];
|
|
174
|
-
var colorIndex = Math.abs(CDPUtils.hashCode(groupName)) % colors.length;
|
|
175
|
-
|
|
176
167
|
chrome.tabGroups.update(groupId, {
|
|
177
|
-
title:
|
|
178
|
-
color:
|
|
168
|
+
title: baseName,
|
|
169
|
+
color: CDPUtils.getGroupColorForClient(groupClientId),
|
|
179
170
|
collapsed: true
|
|
180
171
|
}, function() {
|
|
181
172
|
if (chrome.runtime.lastError) {
|
|
@@ -201,9 +192,8 @@ var SpecialHandler = (function() {
|
|
|
201
192
|
chrome.tabs.query({ groupId: groupId }, function(tabs) {
|
|
202
193
|
if (chrome.runtime.lastError || !tabs) return;
|
|
203
194
|
|
|
204
|
-
var
|
|
205
|
-
var
|
|
206
|
-
var newName = baseName + ' (' + count + ')';
|
|
195
|
+
var baseName = CDPUtils.getGroupBaseName(clientId);
|
|
196
|
+
var newName = baseName + ' (' + tabs.length + ')';
|
|
207
197
|
|
|
208
198
|
chrome.tabGroups.update(groupId, {
|
|
209
199
|
title: newName
|
|
@@ -388,7 +378,7 @@ function checkTabVisibility(tabId) {
|
|
|
388
378
|
if (clientId) {
|
|
389
379
|
State.setTabIdToClientId(target.tabId, clientId);
|
|
390
380
|
}
|
|
391
|
-
|
|
381
|
+
State.addPreExistingTab(target.tabId);
|
|
392
382
|
|
|
393
383
|
if (config.waitForDebuggerOnStart) {
|
|
394
384
|
State.addPendingDebuggerTab(target.tabId);
|
|
@@ -410,7 +400,7 @@ function checkTabVisibility(tabId) {
|
|
|
410
400
|
if (clientId) {
|
|
411
401
|
State.setTabIdToClientId(target.tabId, clientId);
|
|
412
402
|
}
|
|
413
|
-
|
|
403
|
+
State.addPreExistingTab(target.tabId);
|
|
414
404
|
|
|
415
405
|
if (config.waitForDebuggerOnStart) {
|
|
416
406
|
State.addPendingDebuggerTab(target.tabId);
|
|
@@ -24,7 +24,8 @@ var State = (function() {
|
|
|
24
24
|
clientIdToTabId: new Map(),
|
|
25
25
|
clientIdToSessionId: new Map(),
|
|
26
26
|
tabIdToClientId: new Map(),
|
|
27
|
-
clientIdToGroupId: new Map()
|
|
27
|
+
clientIdToGroupId: new Map(),
|
|
28
|
+
preExistingTabIds: new Set()
|
|
28
29
|
};
|
|
29
30
|
|
|
30
31
|
function mapSession(sessionId, tabId, targetId) {
|
|
@@ -264,6 +265,7 @@ var State = (function() {
|
|
|
264
265
|
_state.hasConnectedClient = false;
|
|
265
266
|
_state.tabIdToClientId.clear();
|
|
266
267
|
_state.clientIdToGroupId.clear();
|
|
268
|
+
_state.preExistingTabIds.clear();
|
|
267
269
|
}
|
|
268
270
|
|
|
269
271
|
function cleanupAllTabs() {
|
|
@@ -346,6 +348,30 @@ var State = (function() {
|
|
|
346
348
|
_state.clientIdToGroupId.delete(clientId);
|
|
347
349
|
}
|
|
348
350
|
|
|
351
|
+
function addPreExistingTab(tabId) {
|
|
352
|
+
_state.preExistingTabIds.add(tabId);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function isPreExistingTab(tabId) {
|
|
356
|
+
return _state.preExistingTabIds.has(tabId);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function getPreExistingTabs() {
|
|
360
|
+
return Array.from(_state.preExistingTabIds);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function removePreExistingTab(tabId) {
|
|
364
|
+
_state.preExistingTabIds.delete(tabId);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function clearPreExistingTabsForClient(clientId) {
|
|
368
|
+
_state.preExistingTabIds.forEach(function(tabId) {
|
|
369
|
+
if (State.getClientIdByTabId(tabId) === clientId) {
|
|
370
|
+
_state.preExistingTabIds.delete(tabId);
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
|
|
349
375
|
function getTabIdByClientId(clientId) {
|
|
350
376
|
return _state.clientIdToTabId.get(clientId);
|
|
351
377
|
}
|
|
@@ -448,6 +474,11 @@ var State = (function() {
|
|
|
448
474
|
getClientIdByTabId: getClientIdByTabId,
|
|
449
475
|
setGroupIdForClient: setGroupIdForClient,
|
|
450
476
|
getGroupIdForClient: getGroupIdForClient,
|
|
451
|
-
removeGroupForClient: removeGroupForClient
|
|
477
|
+
removeGroupForClient: removeGroupForClient,
|
|
478
|
+
addPreExistingTab: addPreExistingTab,
|
|
479
|
+
isPreExistingTab: isPreExistingTab,
|
|
480
|
+
getPreExistingTabs: getPreExistingTabs,
|
|
481
|
+
removePreExistingTab: removePreExistingTab,
|
|
482
|
+
clearPreExistingTabsForClient: clearPreExistingTabsForClient
|
|
452
483
|
};
|
|
453
484
|
})();
|
|
@@ -199,6 +199,16 @@ var WebSocketManager = (function() {
|
|
|
199
199
|
closeTabsByClientId(discClientId, resolve);
|
|
200
200
|
});
|
|
201
201
|
}).then(function() {
|
|
202
|
+
var preExistingTabs = State.getPreExistingTabs();
|
|
203
|
+
var clientPreExisting = preExistingTabs.filter(function(tabId) {
|
|
204
|
+
return State.getClientIdByTabId(tabId) === discClientId;
|
|
205
|
+
});
|
|
206
|
+
clientPreExisting.forEach(function(tabId) {
|
|
207
|
+
chrome.debugger.detach({ tabId: tabId }).catch(function() {});
|
|
208
|
+
State.removeAttachedTab(tabId);
|
|
209
|
+
});
|
|
210
|
+
State.clearPreExistingTabsForClient(discClientId);
|
|
211
|
+
|
|
202
212
|
State.removeCDPClient(discClientId);
|
|
203
213
|
if (State.getCDPClients().length === 0) {
|
|
204
214
|
State.setHasConnectedClient(false);
|
|
@@ -261,26 +271,14 @@ var WebSocketManager = (function() {
|
|
|
261
271
|
if (groupId) {
|
|
262
272
|
closeGroupById(groupId, clientId, resolve);
|
|
263
273
|
} else {
|
|
264
|
-
var
|
|
265
|
-
chrome.tabGroups.query({
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
var match = allGroups.find(function(g) {
|
|
270
|
-
return g.title && g.title.indexOf(groupName) === 0;
|
|
271
|
-
});
|
|
272
|
-
if (match) {
|
|
273
|
-
closeGroupById(match.id, clientId, resolve);
|
|
274
|
-
} else {
|
|
275
|
-
Logger.info('[WS] No tab group found, closing tabs by clientId:', clientId);
|
|
276
|
-
closeTabsByClientId(clientId, resolve);
|
|
277
|
-
}
|
|
278
|
-
} else {
|
|
279
|
-
resolve();
|
|
280
|
-
}
|
|
281
|
-
});
|
|
274
|
+
var baseName = CDPUtils.getGroupBaseName(clientId);
|
|
275
|
+
chrome.tabGroups.query({}, function(allGroups) {
|
|
276
|
+
var match = CDPUtils.findGroupByName(allGroups, baseName);
|
|
277
|
+
if (match) {
|
|
278
|
+
closeGroupById(match.id, clientId, resolve);
|
|
282
279
|
} else {
|
|
283
|
-
|
|
280
|
+
Logger.info('[WS] No tab group found, closing tabs by clientId:', clientId);
|
|
281
|
+
closeTabsByClientId(clientId, resolve);
|
|
284
282
|
}
|
|
285
283
|
});
|
|
286
284
|
}
|
|
@@ -321,7 +319,7 @@ var WebSocketManager = (function() {
|
|
|
321
319
|
var tabsToClose = [];
|
|
322
320
|
|
|
323
321
|
attachedTabs.forEach(function(tabId) {
|
|
324
|
-
if (State.getClientIdByTabId(tabId) === clientId) {
|
|
322
|
+
if (State.getClientIdByTabId(tabId) === clientId && !State.isPreExistingTab(tabId)) {
|
|
325
323
|
tabsToClose.push(tabId);
|
|
326
324
|
}
|
|
327
325
|
});
|
|
@@ -27,10 +27,49 @@ var CDPUtils = (function() {
|
|
|
27
27
|
return hash;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
var GROUP_COLORS = ['blue', 'cyan', 'green', 'yellow', 'orange', 'red', 'pink', 'purple'];
|
|
31
|
+
|
|
32
|
+
function getGroupColorForClient(clientId) {
|
|
33
|
+
var idx = Math.abs(hashCode(clientId)) % GROUP_COLORS.length;
|
|
34
|
+
return GROUP_COLORS[idx];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function getClientIndex(clientId) {
|
|
38
|
+
var clients = (typeof State !== 'undefined') ? (State.getCDPClients() || []) : [];
|
|
39
|
+
for (var i = 0; i < clients.length; i++) {
|
|
40
|
+
if (clients[i].id === clientId) return i + 1;
|
|
41
|
+
}
|
|
42
|
+
return 0;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function buildGroupName(clientId) {
|
|
46
|
+
if (!clientId) return 'CDP';
|
|
47
|
+
var idx = getClientIndex(clientId);
|
|
48
|
+
return idx > 0 ? ('CDP #' + idx) : ('CDP-' + clientId.substring(0, 6));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function getGroupBaseName(clientId) {
|
|
52
|
+
return buildGroupName(clientId);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function findGroupByName(allGroups, baseName) {
|
|
56
|
+
for (var i = 0; i < allGroups.length; i++) {
|
|
57
|
+
var g = allGroups[i];
|
|
58
|
+
if (g.title && g.title.indexOf(baseName) === 0) return g;
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
30
63
|
return {
|
|
31
64
|
generateSessionId: generateSessionId,
|
|
32
65
|
getChromeVersion: getChromeVersion,
|
|
33
66
|
sleep: sleep,
|
|
34
|
-
hashCode: hashCode
|
|
67
|
+
hashCode: hashCode,
|
|
68
|
+
GROUP_COLORS: GROUP_COLORS,
|
|
69
|
+
getGroupColorForClient: getGroupColorForClient,
|
|
70
|
+
getClientIndex: getClientIndex,
|
|
71
|
+
buildGroupName: buildGroupName,
|
|
72
|
+
getGroupBaseName: getGroupBaseName,
|
|
73
|
+
findGroupByName: findGroupByName
|
|
35
74
|
};
|
|
36
75
|
})();
|
package/package.json
CHANGED
package/server/proxy-server.js
CHANGED
|
@@ -22,6 +22,7 @@ const PORT = CONFIG.PORT;
|
|
|
22
22
|
const CONFIG_DIR = path.join(os.homedir(), '.cdp-tunnel');
|
|
23
23
|
const EXTENSION_STATE_FILE = path.join(CONFIG_DIR, 'extension-state.json');
|
|
24
24
|
const PLUGIN_EVER_CONNECTED_FILE = path.join(CONFIG_DIR, 'plugin-ever-connected');
|
|
25
|
+
const SERVER_START_TIME = Date.now();
|
|
25
26
|
|
|
26
27
|
let lastChromeRestartAttempt = 0;
|
|
27
28
|
const CHROME_RESTART_COOLDOWN = CONFIG.CHROME_RESTART_COOLDOWN;
|
|
@@ -739,7 +740,7 @@ function handlePluginConnection(ws, clientInfo) {
|
|
|
739
740
|
type: 'connected',
|
|
740
741
|
role: 'plugin',
|
|
741
742
|
id: id,
|
|
742
|
-
fresh:
|
|
743
|
+
fresh: (Date.now() - SERVER_START_TIME) < 5000,
|
|
743
744
|
timestamp: Date.now()
|
|
744
745
|
}));
|
|
745
746
|
}
|
|
@@ -1513,4 +1514,14 @@ process.on('SIGTERM', () => {
|
|
|
1513
1514
|
process.exit(0);
|
|
1514
1515
|
});
|
|
1515
1516
|
|
|
1517
|
+
server.on('error', (err) => {
|
|
1518
|
+
if (err.code === 'EADDRINUSE') {
|
|
1519
|
+
console.error(`[FATAL] Port ${PORT} is already in use. Is another cdp-tunnel running?`);
|
|
1520
|
+
console.error(` Run "cdp-tunnel stop" first, or kill the process on port ${PORT}.`);
|
|
1521
|
+
process.exit(2);
|
|
1522
|
+
}
|
|
1523
|
+
console.error('[FATAL] Server error:', err.message);
|
|
1524
|
+
process.exit(1);
|
|
1525
|
+
});
|
|
1526
|
+
|
|
1516
1527
|
server.listen(PORT, '0.0.0.0');
|