cdp-tunnel 2.7.4 → 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();
|
|
@@ -797,25 +819,7 @@ function handlePluginConnection(ws, clientInfo, request) {
|
|
|
797
819
|
pendingMap.set(targetId, { parsed: JSON.parse(JSON.stringify(parsed)), cdpData });
|
|
798
820
|
console.log(`[TARGET EVENT PENDING] ${parsed.method} targetId=${targetId?.substring(0,8) || 'none'} (cached, waiting for createTarget response)`);
|
|
799
821
|
} else {
|
|
800
|
-
|
|
801
|
-
let routedToDiscoverer = false;
|
|
802
|
-
if (ns.discoveringClientIds.size > 0) {
|
|
803
|
-
for (const [discClientId, timestamp] of ns.discoveringClientIds) {
|
|
804
|
-
if (Date.now() - timestamp < 30000) {
|
|
805
|
-
const discWs = clientById.get(discClientId);
|
|
806
|
-
if (discWs && discWs.readyState === WebSocket.OPEN) {
|
|
807
|
-
discWs.send(cdpData);
|
|
808
|
-
routedToDiscoverer = true;
|
|
809
|
-
}
|
|
810
|
-
} else {
|
|
811
|
-
ns.discoveringClientIds.delete(discClientId);
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
if (!routedToDiscoverer) {
|
|
816
|
-
pendingMap.set(targetId, { parsed: JSON.parse(JSON.stringify(parsed)), cdpData });
|
|
817
|
-
console.log(`[TARGET EVENT PENDING] ${parsed.method} targetId=${targetId?.substring(0,8) || 'none'} (cached, no discoverer)`);
|
|
818
|
-
}
|
|
822
|
+
console.log(`[TARGET EVENT DROPPED] ${parsed.method} targetId=${targetId?.substring(0,8) || 'none'} (no owner, dropped for isolation)`);
|
|
819
823
|
}
|
|
820
824
|
} else {
|
|
821
825
|
console.log(`[TARGET EVENT DROPPED] ${parsed.method} targetId=${targetId?.substring(0,8) || 'none'} (no owner, dropped for isolation)`);
|
|
@@ -948,15 +952,33 @@ function handlePluginConnection(ws, clientInfo, request) {
|
|
|
948
952
|
invalidateTargetsCache(ws);
|
|
949
953
|
}
|
|
950
954
|
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
+
if (mapping.isAutoDefaultPage) {
|
|
956
|
+
console.log(`[AUTO DEFAULT PAGE] createTarget response received for client=${mapping.clientId}, targetId=${parsed.result?.targetId?.substring(0,8) || 'none'} — skipping response send to client`);
|
|
957
|
+
|
|
958
|
+
if (mapping.pendingSetAutoAttach) {
|
|
959
|
+
const pending = mapping.pendingSetAutoAttach;
|
|
960
|
+
const pendingParsed = pending.parsed;
|
|
961
|
+
const pendingClientId = pending.clientId;
|
|
962
|
+
|
|
963
|
+
console.log(`[AUTO DEFAULT PAGE] Now forwarding pending setAutoAttach for client=${pendingClientId}`);
|
|
964
|
+
|
|
965
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
966
|
+
const forwardMsg = { ...pendingParsed, __clientId: pendingClientId };
|
|
967
|
+
ws.send(JSON.stringify(forwardMsg));
|
|
968
|
+
console.log(`[SEND TO PLUGIN] Forwarding setAutoAttach for client=${pendingClientId}`);
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
} else {
|
|
972
|
+
const originalId = mapping.originalId;
|
|
973
|
+
parsed.id = originalId;
|
|
974
|
+
if (mapping.sessionId && !parsed.sessionId) {
|
|
975
|
+
parsed.sessionId = mapping.sessionId;
|
|
976
|
+
}
|
|
977
|
+
const responseStr = JSON.stringify(parsed);
|
|
978
|
+
console.log(`[SEND TO CLIENT] ${responseStr.substring(0, 300)}`);
|
|
979
|
+
clientWs.send(responseStr);
|
|
980
|
+
console.log(`[ROUTE] Response global=${globalId} -> original=${originalId} -> client=${mapping.clientId} sessionId=${parsed.sessionId?.substring(0,8) || 'none'}`);
|
|
955
981
|
}
|
|
956
|
-
const responseStr = JSON.stringify(parsed);
|
|
957
|
-
console.log(`[SEND TO CLIENT] ${responseStr.substring(0, 300)}`);
|
|
958
|
-
clientWs.send(responseStr);
|
|
959
|
-
console.log(`[ROUTE] Response global=${globalId} -> original=${originalId} -> client=${mapping.clientId} sessionId=${parsed.sessionId?.substring(0,8) || 'none'}`);
|
|
960
982
|
}
|
|
961
983
|
globalRequestIdMap.delete(globalId);
|
|
962
984
|
} else {
|
|
@@ -1052,6 +1074,52 @@ function handlePluginConnection(ws, clientInfo, request) {
|
|
|
1052
1074
|
}));
|
|
1053
1075
|
}
|
|
1054
1076
|
|
|
1077
|
+
function autoCreateDefaultPageAndForward(clientWs, setAutoAttachParsed, originalData, clientId, originalRequestId) {
|
|
1078
|
+
const pluginWs = clientWs.pairedPlugin;
|
|
1079
|
+
if (!pluginWs || pluginWs.readyState !== WebSocket.OPEN) {
|
|
1080
|
+
forwardToPlugin(clientWs, originalData, clientId);
|
|
1081
|
+
return;
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
globalRequestIdCounter++;
|
|
1085
|
+
const createGlobalId = globalRequestIdCounter;
|
|
1086
|
+
|
|
1087
|
+
globalRequestIdMap.set(createGlobalId, {
|
|
1088
|
+
clientId: clientId,
|
|
1089
|
+
originalId: -1,
|
|
1090
|
+
sessionId: null,
|
|
1091
|
+
method: 'Target.createTarget',
|
|
1092
|
+
isCreateTarget: true,
|
|
1093
|
+
isAutoDefaultPage: true,
|
|
1094
|
+
pendingSetAutoAttach: {
|
|
1095
|
+
parsed: setAutoAttachParsed,
|
|
1096
|
+
data: originalData,
|
|
1097
|
+
clientId: clientId,
|
|
1098
|
+
originalRequestId: originalRequestId
|
|
1099
|
+
}
|
|
1100
|
+
});
|
|
1101
|
+
|
|
1102
|
+
const request = {
|
|
1103
|
+
id: createGlobalId,
|
|
1104
|
+
method: 'Target.createTarget',
|
|
1105
|
+
params: { url: 'about:blank' },
|
|
1106
|
+
__clientId: clientId
|
|
1107
|
+
};
|
|
1108
|
+
|
|
1109
|
+
console.log(`[AUTO DEFAULT PAGE] Sending Target.createTarget for client=${clientId} globalId=${createGlobalId}, will forward setAutoAttach after`);
|
|
1110
|
+
pluginWs.send(JSON.stringify(request));
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
function forwardToPlugin(clientWs, data, clientId) {
|
|
1114
|
+
const pluginWs = clientWs.pairedPlugin;
|
|
1115
|
+
if (pluginWs && pluginWs.readyState === WebSocket.OPEN) {
|
|
1116
|
+
console.log(`[SEND TO PLUGIN] method=Target.setAutoAttach clientId=${clientId}`);
|
|
1117
|
+
pluginWs.send(data);
|
|
1118
|
+
} else {
|
|
1119
|
+
broadcastToPlugins(data, clientWs);
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1055
1123
|
/**
|
|
1056
1124
|
* 处理 CDP 客户端连接 (Playwright/Puppeteer)
|
|
1057
1125
|
*/
|
|
@@ -1254,6 +1322,12 @@ function handleClientConnection(ws, clientInfo, customClientId = null, targetPlu
|
|
|
1254
1322
|
}
|
|
1255
1323
|
}
|
|
1256
1324
|
|
|
1325
|
+
if (parsed && parsed.method === 'Target.setAutoAttach' && parsed.params?.autoAttach && !ws._autoDefaultPageSent) {
|
|
1326
|
+
ws._autoDefaultPageSent = true;
|
|
1327
|
+
autoCreateDefaultPageAndForward(ws, parsed, modifiedData, id, originalId);
|
|
1328
|
+
return;
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1257
1331
|
if (parsed && parsed.method === 'Browser.close') {
|
|
1258
1332
|
if (shouldLog('info')) {
|
|
1259
1333
|
console.log(`\n[BROWSER CLOSE] Client ${id} requested Browser.close, forwarding to plugin`);
|