cdp-tunnel 2.7.5 → 2.7.6
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.
|
@@ -65,8 +65,7 @@ var SpecialHandler = (function() {
|
|
|
65
65
|
|
|
66
66
|
if (State.isCDPCreatedTab(tabId)) {
|
|
67
67
|
addTabToAutomationGroup(tabId, clientId);
|
|
68
|
-
} else {
|
|
69
|
-
State.addPreExistingTab(tabId);
|
|
68
|
+
} else { State.addPreExistingTab(tabId);
|
|
70
69
|
Logger.info('[CDP] Target.attachToTarget: user tab not CDP-created, treating as pre-existing. tabId:', tabId);
|
|
71
70
|
}
|
|
72
71
|
|
|
@@ -141,12 +140,9 @@ var SpecialHandler = (function() {
|
|
|
141
140
|
|
|
142
141
|
function groupTabSilently(tabId, clientId) {
|
|
143
142
|
return new Promise(function(resolve) {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
Logger.error('[TabGroup] addTabToAutomationGroup threw:', e.message || e);
|
|
148
|
-
}
|
|
149
|
-
setTimeout(resolve, 100);
|
|
143
|
+
addTabToAutomationGroup(tabId, clientId, function(success) {
|
|
144
|
+
setTimeout(resolve, 50);
|
|
145
|
+
});
|
|
150
146
|
});
|
|
151
147
|
}
|
|
152
148
|
|
|
@@ -162,7 +158,7 @@ var SpecialHandler = (function() {
|
|
|
162
158
|
});
|
|
163
159
|
}
|
|
164
160
|
|
|
165
|
-
function addTabToAutomationGroup(tabId, clientId) {
|
|
161
|
+
function addTabToAutomationGroup(tabId, clientId, callback) {
|
|
166
162
|
Logger.info('[TabGroup] Starting addTabToAutomationGroup for tabId:', tabId, 'clientId:', clientId);
|
|
167
163
|
|
|
168
164
|
WebSocketManager.send({ type: 'tabgroup-debug', tabId: tabId, clientId: clientId, phase: 'start' });
|
|
@@ -183,21 +179,23 @@ var SpecialHandler = (function() {
|
|
|
183
179
|
Logger.warn('[TabGroup] No clientId for tab:', tabId, 'fallback to first client:', groupClientId);
|
|
184
180
|
} else {
|
|
185
181
|
Logger.warn('[TabGroup] No clientId for tab:', tabId, '— skipping group operation');
|
|
182
|
+
if (callback) callback(false);
|
|
186
183
|
return;
|
|
187
184
|
}
|
|
188
185
|
}
|
|
189
186
|
var baseName = CDPUtils.getGroupBaseName(groupClientId);
|
|
190
187
|
|
|
191
188
|
Logger.info('[TabGroup] Grouping tab immediately for:', baseName);
|
|
192
|
-
doGroup(tabId, groupClientId, baseName);
|
|
189
|
+
doGroup(tabId, groupClientId, baseName, 0, callback);
|
|
193
190
|
}
|
|
194
191
|
|
|
195
|
-
function doGroup(tabId, clientId, baseName, retries) {
|
|
192
|
+
function doGroup(tabId, clientId, baseName, retries, callback) {
|
|
196
193
|
retries = retries || 0;
|
|
197
194
|
Logger.info('[TabGroup] doGroup: tabId=' + tabId + ' clientId=' + (clientId || 'none') + ' baseName=' + baseName + ' retry=' + retries);
|
|
198
195
|
if (!chrome.tabGroups) {
|
|
199
196
|
Logger.warn('[TabGroup] chrome.tabGroups API not available (headless mode?), skipping grouping for tab:', tabId);
|
|
200
197
|
EventBuilder.send('CDPTunnel.debug', { source: 'doGroup', phase: 'skip', reason: 'tabGroups-unavailable', tabId: tabId });
|
|
198
|
+
if (callback) callback(false);
|
|
201
199
|
return;
|
|
202
200
|
}
|
|
203
201
|
chrome.tabGroups.query({}, function(allGroups) {
|
|
@@ -214,12 +212,15 @@ var SpecialHandler = (function() {
|
|
|
214
212
|
Logger.error('[TabGroup] Failed to add tab to group:', chrome.runtime.lastError.message, 'retries:', retries);
|
|
215
213
|
EventBuilder.send('CDPTunnel.debug', { source: 'doGroup', phase: 'addToExisting', error: chrome.runtime.lastError.message, tabId: tabId, groupId: existing.id });
|
|
216
214
|
if (retries < 3) {
|
|
217
|
-
setTimeout(function() { doGroup(tabId, clientId, baseName, retries + 1); }, 500);
|
|
215
|
+
setTimeout(function() { doGroup(tabId, clientId, baseName, retries + 1, callback); }, 500);
|
|
216
|
+
} else {
|
|
217
|
+
if (callback) callback(false);
|
|
218
218
|
}
|
|
219
219
|
} else {
|
|
220
220
|
State.setGroupIdForClient(clientId, existing.id);
|
|
221
221
|
updateTabGroupName(clientId);
|
|
222
222
|
Logger.info('[TabGroup] Tab', tabId, 'added to existing group:', existing.id);
|
|
223
|
+
if (callback) callback(true);
|
|
223
224
|
}
|
|
224
225
|
});
|
|
225
226
|
} else {
|
|
@@ -229,7 +230,9 @@ var SpecialHandler = (function() {
|
|
|
229
230
|
Logger.error('[TabGroup] Failed to create group:', chrome.runtime.lastError.message, 'retries:', retries);
|
|
230
231
|
EventBuilder.send('CDPTunnel.debug', { source: 'doGroup', phase: 'createGroup', error: chrome.runtime.lastError.message, tabId: tabId });
|
|
231
232
|
if (retries < 3) {
|
|
232
|
-
setTimeout(function() { doGroup(tabId, clientId, baseName, retries + 1); }, 500);
|
|
233
|
+
setTimeout(function() { doGroup(tabId, clientId, baseName, retries + 1, callback); }, 500);
|
|
234
|
+
} else {
|
|
235
|
+
if (callback) callback(false);
|
|
233
236
|
}
|
|
234
237
|
return;
|
|
235
238
|
}
|
|
@@ -250,13 +253,16 @@ var SpecialHandler = (function() {
|
|
|
250
253
|
updateTabGroupName(clientId);
|
|
251
254
|
Logger.info('[TabGroup] Group updated:', groupId, baseName);
|
|
252
255
|
}
|
|
256
|
+
if (callback) callback(true);
|
|
253
257
|
});
|
|
254
258
|
} else {
|
|
255
259
|
State.setGroupIdForClient(clientId, groupId);
|
|
256
260
|
Logger.info('[TabGroup] Group created but tabGroups.update unavailable (headless):', groupId);
|
|
261
|
+
if (callback) callback(true);
|
|
257
262
|
}
|
|
258
263
|
} else {
|
|
259
264
|
Logger.error('[TabGroup] chrome.tabs.group returned null groupId');
|
|
265
|
+
if (callback) callback(false);
|
|
260
266
|
}
|
|
261
267
|
});
|
|
262
268
|
}
|
|
@@ -281,22 +281,50 @@ var WebSocketManager = (function() {
|
|
|
281
281
|
Logger.info('[WS] Closing tab group for client:', clientId);
|
|
282
282
|
|
|
283
283
|
return new Promise(function(resolve) {
|
|
284
|
-
|
|
284
|
+
var timeoutId = setTimeout(function() {
|
|
285
|
+
Logger.warn('[WS] closeTabGroupByClientId timeout for client:', clientId, '— forcing cleanup');
|
|
286
|
+
cleanupStaleState(clientId);
|
|
287
|
+
resolve();
|
|
288
|
+
}, 5000);
|
|
285
289
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
290
|
+
var groupId = State.getGroupIdForClient(clientId);
|
|
291
|
+
|
|
292
|
+
if (groupId) {
|
|
293
|
+
closeGroupById(groupId, clientId, function() {
|
|
294
|
+
clearTimeout(timeoutId);
|
|
295
|
+
cleanupStaleState(clientId);
|
|
296
|
+
resolve();
|
|
297
|
+
});
|
|
298
|
+
} else {
|
|
299
|
+
var baseName = CDPUtils.getGroupBaseName(clientId);
|
|
300
|
+
chrome.tabGroups.query({}, function(allGroups) {
|
|
301
|
+
var match = CDPUtils.findGroupByName(allGroups, baseName);
|
|
302
|
+
if (match) {
|
|
303
|
+
closeGroupById(match.id, clientId, function() {
|
|
304
|
+
clearTimeout(timeoutId);
|
|
305
|
+
cleanupStaleState(clientId);
|
|
306
|
+
resolve();
|
|
298
307
|
});
|
|
299
|
-
|
|
308
|
+
} else {
|
|
309
|
+
Logger.info('[WS] No tab group found, closing tabs by clientId:', clientId);
|
|
310
|
+
closeTabsByClientId(clientId, function() {
|
|
311
|
+
clearTimeout(timeoutId);
|
|
312
|
+
cleanupStaleState(clientId);
|
|
313
|
+
resolve();
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function cleanupStaleState(clientId) {
|
|
322
|
+
if (!clientId) return;
|
|
323
|
+
var attachedTabs = State.getAttachedTabIds();
|
|
324
|
+
attachedTabs.forEach(function(tabId) {
|
|
325
|
+
if (State.getClientIdByTabId(tabId) === clientId) {
|
|
326
|
+
State.removeTabIdToClientId(tabId);
|
|
327
|
+
}
|
|
300
328
|
});
|
|
301
329
|
}
|
|
302
330
|
|
|
@@ -306,6 +334,7 @@ var WebSocketManager = (function() {
|
|
|
306
334
|
if (!tabs || tabs.length === 0) {
|
|
307
335
|
Logger.info('[WS] No tabs in group:', groupId);
|
|
308
336
|
State.removeGroupForClient(clientId);
|
|
337
|
+
removeEmptyGroup(groupId);
|
|
309
338
|
resolve();
|
|
310
339
|
return;
|
|
311
340
|
}
|
|
@@ -338,11 +367,33 @@ var WebSocketManager = (function() {
|
|
|
338
367
|
});
|
|
339
368
|
|
|
340
369
|
State.removeGroupForClient(clientId);
|
|
370
|
+
removeEmptyGroup(groupId);
|
|
341
371
|
resolve();
|
|
342
372
|
});
|
|
343
373
|
});
|
|
344
374
|
}
|
|
345
375
|
|
|
376
|
+
function removeEmptyGroup(groupId) {
|
|
377
|
+
if (!groupId || !chrome.tabGroups) return;
|
|
378
|
+
setTimeout(function() {
|
|
379
|
+
chrome.tabGroups.query({ groupId: groupId }, function(groups) {
|
|
380
|
+
if (chrome.runtime.lastError) return;
|
|
381
|
+
if (groups && groups.length > 0) {
|
|
382
|
+
chrome.tabs.query({ groupId: groupId }, function(tabs) {
|
|
383
|
+
if (chrome.runtime.lastError) return;
|
|
384
|
+
if (!tabs || tabs.length === 0) {
|
|
385
|
+
chrome.tabGroups.remove(groupId, function() {
|
|
386
|
+
if (!chrome.runtime.lastError) {
|
|
387
|
+
Logger.info('[WS] Removed empty group:', groupId);
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
}, 500);
|
|
395
|
+
}
|
|
396
|
+
|
|
346
397
|
function closeTabsByClientId(clientId, resolve) {
|
|
347
398
|
var attachedTabs = State.getAttachedTabIds();
|
|
348
399
|
var cdpCreatedTabs = State.getCDPCreatedTabIds();
|
|
@@ -44,7 +44,13 @@ var CDPUtils = (function() {
|
|
|
44
44
|
|
|
45
45
|
function buildGroupName(clientId) {
|
|
46
46
|
if (!clientId) return 'CDP';
|
|
47
|
-
var
|
|
47
|
+
var hash = 0;
|
|
48
|
+
for (var i = 0; i < clientId.length; i++) {
|
|
49
|
+
var chr = clientId.charCodeAt(i);
|
|
50
|
+
hash = ((hash << 5) - hash) + chr;
|
|
51
|
+
hash = hash | 0;
|
|
52
|
+
}
|
|
53
|
+
var suffix = Math.abs(hash).toString(16).substring(0, 8).padStart(8, '0');
|
|
48
54
|
return 'CDP-' + suffix;
|
|
49
55
|
}
|
|
50
56
|
|
|
@@ -55,7 +61,7 @@ var CDPUtils = (function() {
|
|
|
55
61
|
function findGroupByName(allGroups, baseName) {
|
|
56
62
|
for (var i = 0; i < allGroups.length; i++) {
|
|
57
63
|
var g = allGroups[i];
|
|
58
|
-
if (g.title && g.title.indexOf(baseName) === 0) return g;
|
|
64
|
+
if (g.title === baseName || (g.title && g.title.indexOf(baseName + ' (') === 0)) return g;
|
|
59
65
|
}
|
|
60
66
|
return null;
|
|
61
67
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cdp-tunnel",
|
|
3
|
-
"version": "2.7.
|
|
3
|
+
"version": "2.7.6",
|
|
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",
|
|
@@ -16,6 +16,10 @@
|
|
|
16
16
|
"benchmark:proxy": "node benchmark/run-all.js --proxy",
|
|
17
17
|
"demo": "node demo/server.js",
|
|
18
18
|
"test:compare": "node tests/cdp-compare/cdp-compare-runner.js",
|
|
19
|
+
"test": "node tests/e2e/run-all.js",
|
|
20
|
+
"test:e2e": "node tests/e2e/run-all.js",
|
|
21
|
+
"test:core": "SKIP_EXTENDED=1 node tests/e2e/run-all.js",
|
|
22
|
+
"test:smoke": "ONLY=test-default-page,test-strict-isolation,test-no-user-tab-grab node tests/e2e/run-all.js",
|
|
19
23
|
"prepare": "husky"
|
|
20
24
|
},
|
|
21
25
|
"keywords": [
|
package/server/proxy-server.js
CHANGED
|
@@ -487,11 +487,33 @@ function cleanupClient(ws, id, reason) {
|
|
|
487
487
|
}
|
|
488
488
|
|
|
489
489
|
if (ws.pairedPlugin) {
|
|
490
|
-
safeSend(ws.pairedPlugin, JSON.stringify({
|
|
490
|
+
const sendOk = safeSend(ws.pairedPlugin, JSON.stringify({
|
|
491
491
|
type: 'client-disconnected',
|
|
492
492
|
clientId: id,
|
|
493
493
|
sessions: []
|
|
494
494
|
}), 'plugin');
|
|
495
|
+
if (!sendOk) {
|
|
496
|
+
console.log(`[WARN] cleanupClient: failed to send client-disconnected for ${id} to plugin`);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
const pluginNs = getNamespace(ws.pairedPlugin);
|
|
500
|
+
if (pluginNs) {
|
|
501
|
+
const targetsToClose = [];
|
|
502
|
+
for (const [tId, cId] of pluginNs.targetIdToClientId.entries()) {
|
|
503
|
+
if (cId === id) {
|
|
504
|
+
targetsToClose.push(tId);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
targetsToClose.forEach(function(tId) {
|
|
508
|
+
const closeReq = JSON.stringify({
|
|
509
|
+
id: -1,
|
|
510
|
+
method: 'Target.closeTarget',
|
|
511
|
+
params: { targetId: tId },
|
|
512
|
+
__clientId: id
|
|
513
|
+
});
|
|
514
|
+
safeSend(ws.pairedPlugin, closeReq, 'plugin');
|
|
515
|
+
});
|
|
516
|
+
}
|
|
495
517
|
}
|
|
496
518
|
|
|
497
519
|
broadcastClientList();
|