cdp-tunnel 2.8.3 → 2.9.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/extension-new/background.js +1 -1
- package/extension-new/cdp/handler/local.js +66 -1
- package/extension-new/cdp/handler/special.js +60 -8
- package/extension-new/cdp/index.js +1 -1
- package/extension-new/config-page.js +8 -2
- package/extension-new/core/websocket.js +86 -12
- package/extension-new/manifest.json +1 -1
- package/extension-new/popup.js +6 -0
- package/extension-new/utils/config.js +1 -1
- package/extension-new/utils/helpers.js +5 -4
- package/package.json +1 -1
- package/server/modules/config.js +2 -1
- package/server/proxy-server.js +137 -41
|
@@ -341,7 +341,7 @@ importScripts('features/automation-badge.js');
|
|
|
341
341
|
chrome.runtime.onInstalled.addListener(function(details) {
|
|
342
342
|
Logger.info('[Runtime] Extension installed/updated:', details.reason);
|
|
343
343
|
State.persist(null, false);
|
|
344
|
-
setBadgeStatus('
|
|
344
|
+
setBadgeStatus('OFF');
|
|
345
345
|
init();
|
|
346
346
|
});
|
|
347
347
|
|
|
@@ -42,9 +42,35 @@ var LocalHandler = (function() {
|
|
|
42
42
|
var state = _getState(context);
|
|
43
43
|
var params = context.params;
|
|
44
44
|
var wsManager = context._wsManager;
|
|
45
|
+
var mode = context ? context.mode : null;
|
|
45
46
|
state.setDiscoverTargets(!!(params && params.discover));
|
|
46
47
|
|
|
47
48
|
if (params && params.discover) {
|
|
49
|
+
if (mode === 'takeover') {
|
|
50
|
+
return chrome.debugger.getTargets().then(function(targets) {
|
|
51
|
+
return new Promise(function(resolve) {
|
|
52
|
+
var pageTargets = targets.filter(function(t) { return t.type === 'page' && t.tabId; });
|
|
53
|
+
var checked = 0;
|
|
54
|
+
if (pageTargets.length === 0) { resolve({}); return; }
|
|
55
|
+
pageTargets.forEach(function(target) {
|
|
56
|
+
var tabId = target.tabId;
|
|
57
|
+
var targetId = target.id;
|
|
58
|
+
chrome.tabs.get(tabId, function(tab) {
|
|
59
|
+
checked++;
|
|
60
|
+
if (chrome.runtime.lastError || !tab) { if (checked === pageTargets.length) resolve({}); return; }
|
|
61
|
+
var isGrouped = tab.groupId != null && tab.groupId !== -1;
|
|
62
|
+
var isCDPCreated = state.isCDPCreatedTab(tabId);
|
|
63
|
+
if (!isGrouped && !isCDPCreated && !state.hasEmittedTarget(targetId)) {
|
|
64
|
+
state.addEmittedTarget(targetId);
|
|
65
|
+
var targetInfo = mapToTargetInfo(target);
|
|
66
|
+
EventBuilder.send('Target.targetCreated', { targetInfo: targetInfo }, null, wsManager);
|
|
67
|
+
}
|
|
68
|
+
if (checked === pageTargets.length) resolve({});
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
}
|
|
48
74
|
return getTargetInfos().then(function(targets) {
|
|
49
75
|
targets.forEach(function(targetInfo) {
|
|
50
76
|
state.addEmittedTarget(targetInfo.targetId);
|
|
@@ -56,12 +82,51 @@ var LocalHandler = (function() {
|
|
|
56
82
|
return Promise.resolve({});
|
|
57
83
|
}
|
|
58
84
|
|
|
59
|
-
function targetGetTargets() {
|
|
85
|
+
function targetGetTargets(context) {
|
|
86
|
+
var mode = context ? context.mode : null;
|
|
87
|
+
var state = context ? context._state : null;
|
|
88
|
+
|
|
89
|
+
if (mode === 'takeover') {
|
|
90
|
+
return chrome.debugger.getTargets().then(function(targets) {
|
|
91
|
+
return filterTakeoverTargetsFromRaw(targets, state);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
60
94
|
return getTargetInfos().then(function(targetInfos) {
|
|
61
95
|
return { targetInfos: targetInfos };
|
|
62
96
|
});
|
|
63
97
|
}
|
|
64
98
|
|
|
99
|
+
function filterTakeoverTargetsFromRaw(targets, state) {
|
|
100
|
+
return new Promise(function(resolve) {
|
|
101
|
+
var pageTargets = targets.filter(function(t) { return t.type === 'page' && t.tabId; });
|
|
102
|
+
var nonPageTargets = targets.filter(function(t) { return t.type !== 'page'; }).map(mapToTargetInfo);
|
|
103
|
+
|
|
104
|
+
if (pageTargets.length === 0) {
|
|
105
|
+
resolve({ targetInfos: nonPageTargets });
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
var checked = 0;
|
|
110
|
+
var filtered = [];
|
|
111
|
+
pageTargets.forEach(function(target) {
|
|
112
|
+
var tabId = target.tabId;
|
|
113
|
+
chrome.tabs.get(tabId, function(tab) {
|
|
114
|
+
checked++;
|
|
115
|
+
if (!chrome.runtime.lastError && tab) {
|
|
116
|
+
var isGrouped = tab.groupId != null && tab.groupId !== -1;
|
|
117
|
+
var isCDPCreated = state ? state.isCDPCreatedTab(tabId) : false;
|
|
118
|
+
if (!isGrouped && !isCDPCreated) {
|
|
119
|
+
filtered.push(mapToTargetInfo(target));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (checked === pageTargets.length) {
|
|
123
|
+
resolve({ targetInfos: nonPageTargets.concat(filtered) });
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
65
130
|
function targetGetTargetInfo(params) {
|
|
66
131
|
return getFallbackTargetId().then(function(fallbackId) {
|
|
67
132
|
var targetId = (params && params.targetId) || fallbackId;
|
|
@@ -82,6 +82,10 @@ var SpecialHandler = (function() {
|
|
|
82
82
|
|
|
83
83
|
if (state.isCDPCreatedTab(tabId)) {
|
|
84
84
|
addTabToAutomationGroup(tabId, clientId, null, context);
|
|
85
|
+
} else if (context.mode === 'takeover') {
|
|
86
|
+
state.addPreExistingTab(tabId);
|
|
87
|
+
addTabToAutomationGroup(tabId, clientId, null, context);
|
|
88
|
+
Logger.info('[CDP TAKEOVER] Target.attachToTarget: added to TAKE group. tabId:', tabId);
|
|
85
89
|
} else {
|
|
86
90
|
state.addPreExistingTab(tabId);
|
|
87
91
|
Logger.info('[CDP] Target.attachToTarget: user tab not CDP-created, treating as pre-existing. tabId:', tabId);
|
|
@@ -236,7 +240,7 @@ var SpecialHandler = (function() {
|
|
|
236
240
|
return;
|
|
237
241
|
}
|
|
238
242
|
}
|
|
239
|
-
var baseName = CDPUtils.getGroupBaseName(groupClientId, _getConnectionTag(context));
|
|
243
|
+
var baseName = CDPUtils.getGroupBaseName(groupClientId, _getConnectionTag(context), context ? context.mode : null);
|
|
240
244
|
|
|
241
245
|
Logger.info('[TabGroup] Grouping tab immediately for:', baseName);
|
|
242
246
|
doGroup(tabId, groupClientId, baseName, 0, callback, context);
|
|
@@ -258,7 +262,7 @@ var SpecialHandler = (function() {
|
|
|
258
262
|
Logger.info('[TabGroup] Using cached groupId:', cachedGroupId, 'for client:', clientId);
|
|
259
263
|
chrome.tabs.group({ tabIds: tabId, groupId: cachedGroupId }, function(result) {
|
|
260
264
|
if (!chrome.runtime.lastError) {
|
|
261
|
-
updateTabGroupName(clientId, state, wsManager);
|
|
265
|
+
updateTabGroupName(clientId, state, wsManager, context ? context.mode : null);
|
|
262
266
|
Logger.info('[TabGroup] Tab', tabId, 'added to cached group:', cachedGroupId);
|
|
263
267
|
if (callback) callback(true);
|
|
264
268
|
return;
|
|
@@ -303,7 +307,7 @@ var SpecialHandler = (function() {
|
|
|
303
307
|
}
|
|
304
308
|
} else {
|
|
305
309
|
if (state) state.setGroupIdForClient(clientId, existing.id);
|
|
306
|
-
updateTabGroupName(clientId, state, wsManager);
|
|
310
|
+
updateTabGroupName(clientId, state, wsManager, context ? context.mode : null);
|
|
307
311
|
Logger.info('[TabGroup] Tab', tabId, 'added to existing group:', existing.id);
|
|
308
312
|
if (callback) callback(true);
|
|
309
313
|
}
|
|
@@ -335,7 +339,7 @@ var SpecialHandler = (function() {
|
|
|
335
339
|
EventBuilder.send('CDPTunnel.debug', { source: 'doGroup', phase: 'updateGroup', error: chrome.runtime.lastError.message, groupId: groupId }, null, wsManager);
|
|
336
340
|
} else {
|
|
337
341
|
if (state) state.setGroupIdForClient(clientId, groupId);
|
|
338
|
-
updateTabGroupName(clientId, state, wsManager);
|
|
342
|
+
updateTabGroupName(clientId, state, wsManager, context ? context.mode : null);
|
|
339
343
|
Logger.info('[TabGroup] Group updated:', groupId, baseName);
|
|
340
344
|
}
|
|
341
345
|
if (callback) callback(true);
|
|
@@ -354,7 +358,7 @@ var SpecialHandler = (function() {
|
|
|
354
358
|
});
|
|
355
359
|
}
|
|
356
360
|
|
|
357
|
-
function updateTabGroupName(clientId, state, wsManager) {
|
|
361
|
+
function updateTabGroupName(clientId, state, wsManager, mode) {
|
|
358
362
|
if (!clientId) return;
|
|
359
363
|
|
|
360
364
|
var groupId = state ? state.getGroupIdForClient(clientId) : null;
|
|
@@ -365,7 +369,7 @@ var SpecialHandler = (function() {
|
|
|
365
369
|
chrome.tabs.query({ groupId: groupId }, function(tabs) {
|
|
366
370
|
if (chrome.runtime.lastError || !tabs) return;
|
|
367
371
|
|
|
368
|
-
var baseName = CDPUtils.getGroupBaseName(clientId, connectionTag);
|
|
372
|
+
var baseName = CDPUtils.getGroupBaseName(clientId, connectionTag, mode);
|
|
369
373
|
var newName = baseName + ' (' + tabs.length + ')';
|
|
370
374
|
|
|
371
375
|
chrome.tabGroups.update(groupId, {
|
|
@@ -407,7 +411,7 @@ var SpecialHandler = (function() {
|
|
|
407
411
|
chrome.tabs.remove(tabId, function() {
|
|
408
412
|
state.removeAttachedTab(tabId);
|
|
409
413
|
if (closeClientId) {
|
|
410
|
-
updateTabGroupName(closeClientId, state, _getWSManager(context));
|
|
414
|
+
updateTabGroupName(closeClientId, state, _getWSManager(context), context ? context.mode : null);
|
|
411
415
|
}
|
|
412
416
|
resolve({ success: true });
|
|
413
417
|
});
|
|
@@ -516,12 +520,60 @@ function checkTabVisibility(tabId) {
|
|
|
516
520
|
var state = _getState(context);
|
|
517
521
|
var wsManager = _getWSManager(context);
|
|
518
522
|
var clientId = context ? context.clientId : null;
|
|
523
|
+
var mode = context ? context.mode : null;
|
|
519
524
|
var config = state.getAutoAttachConfig();
|
|
520
525
|
|
|
521
526
|
return chrome.debugger.getTargets().then(function(targets) {
|
|
522
527
|
var promises = [];
|
|
523
528
|
|
|
524
|
-
Logger.info('[CDP] emitAutoAttachForExistingTargets: checking', targets.length, 'targets, clientId:', clientId);
|
|
529
|
+
Logger.info('[CDP] emitAutoAttachForExistingTargets: checking', targets.length, 'targets, clientId:', clientId, 'mode:', mode);
|
|
530
|
+
|
|
531
|
+
if (mode === 'takeover') {
|
|
532
|
+
var takeoverPromises = [];
|
|
533
|
+
targets.forEach(function(target) {
|
|
534
|
+
if (target.type !== 'page' || !target.tabId) return;
|
|
535
|
+
var targetId = target.id;
|
|
536
|
+
var tabId = target.tabId;
|
|
537
|
+
if (state.hasEmittedTarget(targetId)) return;
|
|
538
|
+
|
|
539
|
+
takeoverPromises.push(new Promise(function(resolve) {
|
|
540
|
+
chrome.tabs.get(tabId, function(tab) {
|
|
541
|
+
if (chrome.runtime.lastError || !tab) { resolve(); return; }
|
|
542
|
+
var isGrouped = tab.groupId != null && tab.groupId !== -1;
|
|
543
|
+
var isCDPCreated = state.isCDPCreatedTab(tabId);
|
|
544
|
+
if (isGrouped || isCDPCreated) { resolve(); return; }
|
|
545
|
+
|
|
546
|
+
state.addEmittedTarget(targetId);
|
|
547
|
+
state.addPreExistingTab(tabId);
|
|
548
|
+
if (clientId) state.setTabIdToClientId(tabId, clientId);
|
|
549
|
+
var targetInfo = LocalHandler.mapToTargetInfo(target);
|
|
550
|
+
Logger.info('[CDP TAKEOVER] Emitting ungrouped target:', targetId, 'tabId:', tabId);
|
|
551
|
+
|
|
552
|
+
var attachLogic = function(attached) {
|
|
553
|
+
var sessionId = CDPUtils.generateSessionId();
|
|
554
|
+
state.mapSession(sessionId, tabId, targetId);
|
|
555
|
+
EventBuilder.send('Target.attachedToTarget', {
|
|
556
|
+
sessionId: sessionId,
|
|
557
|
+
targetInfo: Object.assign({}, targetInfo, { attached: true }),
|
|
558
|
+
waitingForDebugger: false
|
|
559
|
+
}, null, wsManager);
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
if (target.attached) {
|
|
563
|
+
attachLogic(true);
|
|
564
|
+
resolve();
|
|
565
|
+
} else {
|
|
566
|
+
DebuggerManager.attach(tabId, state).then(function(attached) {
|
|
567
|
+
if (!attached) { resolve(); return; }
|
|
568
|
+
attachLogic(attached);
|
|
569
|
+
resolve();
|
|
570
|
+
}).catch(resolve);
|
|
571
|
+
}
|
|
572
|
+
});
|
|
573
|
+
}));
|
|
574
|
+
});
|
|
575
|
+
return Promise.all(takeoverPromises);
|
|
576
|
+
}
|
|
525
577
|
|
|
526
578
|
targets.forEach(function(target) {
|
|
527
579
|
if (target.type !== 'page' && target.type !== 'background_page') return;
|
|
@@ -78,7 +78,7 @@ function routeCDPCommand(message, connState, wsManager) {
|
|
|
78
78
|
var logType = route ? route.type : 'FORWARD';
|
|
79
79
|
Logger.info('[CDP] RECV id=' + id + ' method=' + method + ' type=' + logType + ' sessionId=' + (sessionId || 'null') + ' clientId=' + (clientId || 'null'));
|
|
80
80
|
|
|
81
|
-
var ctx = { id: id, method: method, params: params, sessionId: sessionId, clientId: clientId, _state: state, _wsManager: wsManager };
|
|
81
|
+
var ctx = { id: id, method: method, params: params, sessionId: sessionId, clientId: clientId, mode: message.mode, _state: state, _wsManager: wsManager };
|
|
82
82
|
|
|
83
83
|
return new Promise(function(resolve) {
|
|
84
84
|
if (route) {
|
|
@@ -258,8 +258,12 @@
|
|
|
258
258
|
function loadConnectionStatuses() {
|
|
259
259
|
if (typeof chrome !== 'undefined' && chrome.runtime) {
|
|
260
260
|
chrome.runtime.sendMessage({ type: 'get-connection-statuses' }, function(response) {
|
|
261
|
-
if (response && response.
|
|
262
|
-
|
|
261
|
+
if (response && response.connections) {
|
|
262
|
+
var statuses = {};
|
|
263
|
+
response.connections.forEach(function(conn) {
|
|
264
|
+
statuses[conn.id] = conn.status;
|
|
265
|
+
});
|
|
266
|
+
state.connectionStatuses = statuses;
|
|
263
267
|
}
|
|
264
268
|
loadAndRenderConnections();
|
|
265
269
|
});
|
|
@@ -431,6 +435,8 @@
|
|
|
431
435
|
addLog(message.logType, message.message);
|
|
432
436
|
} else if (message.type === 'connections-updated') {
|
|
433
437
|
loadAndRenderConnections();
|
|
438
|
+
} else if (message.type === 'connection-status-changed') {
|
|
439
|
+
loadConnectionStatuses();
|
|
434
440
|
}
|
|
435
441
|
});
|
|
436
442
|
}
|
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
function updateBadgeFromAllConnections() {
|
|
2
|
+
var hasConnected = false;
|
|
3
|
+
var hasEnabled = false;
|
|
4
|
+
ConnectionManager.forEachConnection(function(entry) {
|
|
5
|
+
var ws = entry.state.getWs();
|
|
6
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
7
|
+
hasConnected = true;
|
|
8
|
+
}
|
|
9
|
+
if (entry.config && entry.config.enabled !== false) {
|
|
10
|
+
hasEnabled = true;
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
var status;
|
|
14
|
+
if (hasConnected) {
|
|
15
|
+
status = 'ON';
|
|
16
|
+
} else if (hasEnabled) {
|
|
17
|
+
status = 'ERR';
|
|
18
|
+
} else {
|
|
19
|
+
status = 'OFF';
|
|
20
|
+
}
|
|
21
|
+
setBadgeStatus(status);
|
|
22
|
+
chrome.runtime.sendMessage({ type: 'connection-status-changed' }).catch(function() {});
|
|
23
|
+
}
|
|
24
|
+
|
|
1
25
|
var WebSocketConnection = (function() {
|
|
2
26
|
function WebSocketConnection(connectionId, state, config) {
|
|
3
27
|
this.connectionId = connectionId;
|
|
@@ -24,7 +48,7 @@ var WebSocketConnection = (function() {
|
|
|
24
48
|
wsUrl += sep + 'pluginId=' + encodeURIComponent(pluginId);
|
|
25
49
|
}
|
|
26
50
|
Logger.info('[WS:' + self.connectionId + '] Connecting to', wsUrl);
|
|
27
|
-
|
|
51
|
+
updateBadgeFromAllConnections();
|
|
28
52
|
|
|
29
53
|
try {
|
|
30
54
|
ws = new WebSocket(wsUrl);
|
|
@@ -32,7 +56,7 @@ var WebSocketConnection = (function() {
|
|
|
32
56
|
|
|
33
57
|
ws.onopen = function() {
|
|
34
58
|
Logger.info('[WS:' + self.connectionId + '] Connected');
|
|
35
|
-
|
|
59
|
+
updateBadgeFromAllConnections();
|
|
36
60
|
self.state.clearReconnectTimer();
|
|
37
61
|
self._processQueue();
|
|
38
62
|
self._broadcastStateUpdate();
|
|
@@ -43,14 +67,14 @@ var WebSocketConnection = (function() {
|
|
|
43
67
|
|
|
44
68
|
ws.onclose = function(event) {
|
|
45
69
|
Logger.info('[WS:' + self.connectionId + '] Closed:', event.code, event.reason);
|
|
46
|
-
|
|
70
|
+
updateBadgeFromAllConnections();
|
|
47
71
|
self._scheduleReconnect();
|
|
48
72
|
self._broadcastStateUpdate();
|
|
49
73
|
};
|
|
50
74
|
|
|
51
75
|
ws.onerror = function(error) {
|
|
52
76
|
Logger.error('[WS:' + self.connectionId + '] Error:', error);
|
|
53
|
-
|
|
77
|
+
updateBadgeFromAllConnections();
|
|
54
78
|
self._broadcastStateUpdate();
|
|
55
79
|
};
|
|
56
80
|
|
|
@@ -58,9 +82,9 @@ var WebSocketConnection = (function() {
|
|
|
58
82
|
self._handleRawMessage(event.data);
|
|
59
83
|
};
|
|
60
84
|
} catch (error) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
85
|
+
Logger.error('[WS:' + self.connectionId + '] Failed to create:', error);
|
|
86
|
+
updateBadgeFromAllConnections();
|
|
87
|
+
self._scheduleReconnect();
|
|
64
88
|
}
|
|
65
89
|
});
|
|
66
90
|
};
|
|
@@ -203,7 +227,54 @@ var WebSocketConnection = (function() {
|
|
|
203
227
|
Logger.info('[WS:' + self.connectionId + '] Client connected, resuming event forwarding');
|
|
204
228
|
self.state.setHasConnectedClient(true);
|
|
205
229
|
self.state.addCDPClient(message.clientId, message.clientId);
|
|
206
|
-
self._createGroupForClient(message.clientId);
|
|
230
|
+
self._createGroupForClient(message.clientId, message.__mode);
|
|
231
|
+
self._broadcastStateUpdate();
|
|
232
|
+
break;
|
|
233
|
+
|
|
234
|
+
case 'takeover-disconnect':
|
|
235
|
+
Logger.info('[WS:' + self.connectionId + '] Takeover disconnect:', message.clientId);
|
|
236
|
+
var takeClientId = message.clientId;
|
|
237
|
+
self._groupCreationPending.delete(takeClientId);
|
|
238
|
+
var takeAttachedTabs = self.state.getAttachedTabIds();
|
|
239
|
+
var takeToDetach = takeAttachedTabs.filter(function(tid) {
|
|
240
|
+
return self.state.getClientIdByTabId(tid) === takeClientId;
|
|
241
|
+
});
|
|
242
|
+
var takeTargetIds = [];
|
|
243
|
+
self.state.sessionIdToTargetId.forEach(function(tTargetId, sessId) {
|
|
244
|
+
var tTabId = self.state.sessionIdToTabId.get(sessId);
|
|
245
|
+
if (tTabId && self.state.getClientIdByTabId(tTabId) === takeClientId) {
|
|
246
|
+
takeTargetIds.push(tTargetId);
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
takeToDetach.forEach(function(tid) {
|
|
250
|
+
chrome.debugger.detach({ tabId: tid }).catch(function() {});
|
|
251
|
+
self.state.removeAttachedTab(tid);
|
|
252
|
+
});
|
|
253
|
+
takeTargetIds.forEach(function(tTargetId) {
|
|
254
|
+
self.state.emittedTargets.delete(tTargetId);
|
|
255
|
+
});
|
|
256
|
+
self.state.clearPreExistingTabsForClient(takeClientId);
|
|
257
|
+
var takeSessions = self.state.sessionIdToTabId.entries();
|
|
258
|
+
var takeEntry = takeSessions.next();
|
|
259
|
+
while (!takeEntry.done) {
|
|
260
|
+
var sessId = takeEntry.value[0];
|
|
261
|
+
var tTabId = takeEntry.value[1];
|
|
262
|
+
if (self.state.getClientIdByTabId(tTabId) === takeClientId) {
|
|
263
|
+
self.state.unmapSession(sessId);
|
|
264
|
+
}
|
|
265
|
+
takeEntry = takeSessions.next();
|
|
266
|
+
}
|
|
267
|
+
takeToDetach.forEach(function(tid) {
|
|
268
|
+
self.state.removeTabIdToClientId(tid);
|
|
269
|
+
var sessions = self.state.findSessionsByTabId(tid);
|
|
270
|
+
sessions.forEach(function(sid) { self.state.unmapSession(sid); });
|
|
271
|
+
});
|
|
272
|
+
self.state.removeGroupForClient(takeClientId);
|
|
273
|
+
self.state.removeCDPClient(takeClientId);
|
|
274
|
+
if (self.state.getCDPClients().length === 0) {
|
|
275
|
+
self.state.setHasConnectedClient(false);
|
|
276
|
+
}
|
|
277
|
+
self._cleanupStaleState(takeClientId);
|
|
207
278
|
self._broadcastStateUpdate();
|
|
208
279
|
break;
|
|
209
280
|
|
|
@@ -258,6 +329,7 @@ var WebSocketConnection = (function() {
|
|
|
258
329
|
tabId: tabId,
|
|
259
330
|
sessionId: sessionId,
|
|
260
331
|
clientId: message.__clientId,
|
|
332
|
+
mode: message.__mode,
|
|
261
333
|
connectionId: self.connectionId
|
|
262
334
|
}, self.state, self);
|
|
263
335
|
}
|
|
@@ -272,6 +344,7 @@ var WebSocketConnection = (function() {
|
|
|
272
344
|
tabId: tabId,
|
|
273
345
|
sessionId: sessionId,
|
|
274
346
|
clientId: message.__clientId,
|
|
347
|
+
mode: message.__mode,
|
|
275
348
|
connectionId: self.connectionId
|
|
276
349
|
}, self.state, self);
|
|
277
350
|
}
|
|
@@ -300,9 +373,10 @@ var WebSocketConnection = (function() {
|
|
|
300
373
|
resolve();
|
|
301
374
|
});
|
|
302
375
|
} else {
|
|
303
|
-
var
|
|
376
|
+
var cdpBaseName = CDPUtils.getGroupBaseName(clientId, self.config ? self.config.tag : null, 'create');
|
|
377
|
+
var takeBaseName = CDPUtils.getGroupBaseName(clientId, self.config ? self.config.tag : null, 'takeover');
|
|
304
378
|
chrome.tabGroups.query({}, function(allGroups) {
|
|
305
|
-
var match = CDPUtils.findGroupByName(allGroups,
|
|
379
|
+
var match = CDPUtils.findGroupByName(allGroups, cdpBaseName) || CDPUtils.findGroupByName(allGroups, takeBaseName);
|
|
306
380
|
if (match) {
|
|
307
381
|
self._closeGroupById(match.id, clientId, function() {
|
|
308
382
|
clearTimeout(timeoutId);
|
|
@@ -464,7 +538,7 @@ var WebSocketConnection = (function() {
|
|
|
464
538
|
});
|
|
465
539
|
};
|
|
466
540
|
|
|
467
|
-
WebSocketConnection.prototype._createGroupForClient = function(clientId) {
|
|
541
|
+
WebSocketConnection.prototype._createGroupForClient = function(clientId, mode) {
|
|
468
542
|
var self = this;
|
|
469
543
|
if (!clientId || !chrome.tabGroups) return;
|
|
470
544
|
|
|
@@ -481,7 +555,7 @@ var WebSocketConnection = (function() {
|
|
|
481
555
|
|
|
482
556
|
self._groupCreationPending.add(clientId);
|
|
483
557
|
|
|
484
|
-
var baseName = CDPUtils.getGroupBaseName(clientId, self.config ? self.config.tag : null);
|
|
558
|
+
var baseName = CDPUtils.getGroupBaseName(clientId, self.config ? self.config.tag : null, mode);
|
|
485
559
|
chrome.tabs.query({ currentWindow: true }, function(tabs) {
|
|
486
560
|
if (!tabs || tabs.length === 0) {
|
|
487
561
|
Logger.warn('[WS:' + self.connectionId + '] No tabs found for group creation');
|
package/extension-new/popup.js
CHANGED
|
@@ -76,4 +76,10 @@
|
|
|
76
76
|
versionLink.textContent = 'v' + manifest.version;
|
|
77
77
|
|
|
78
78
|
loadState();
|
|
79
|
+
|
|
80
|
+
chrome.runtime.onMessage.addListener(function(message) {
|
|
81
|
+
if (message.type === 'connection-status-changed' || message.type === 'stateUpdate') {
|
|
82
|
+
loadState();
|
|
83
|
+
}
|
|
84
|
+
});
|
|
79
85
|
})();
|
|
@@ -42,7 +42,7 @@ var CDPUtils = (function() {
|
|
|
42
42
|
return 0;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
function buildGroupName(clientId, connectionTag) {
|
|
45
|
+
function buildGroupName(clientId, connectionTag, mode) {
|
|
46
46
|
if (!clientId) return 'CDP';
|
|
47
47
|
var hash = 0;
|
|
48
48
|
for (var i = 0; i < clientId.length; i++) {
|
|
@@ -52,11 +52,12 @@ var CDPUtils = (function() {
|
|
|
52
52
|
}
|
|
53
53
|
var suffix = Math.abs(hash).toString(16).substring(0, 8).padStart(8, '0');
|
|
54
54
|
var tag = (connectionTag && connectionTag !== 'default') ? connectionTag + '-' : '';
|
|
55
|
-
|
|
55
|
+
var prefix = (mode === 'takeover') ? 'TAKE-' : 'CDP-';
|
|
56
|
+
return prefix + tag + suffix;
|
|
56
57
|
}
|
|
57
58
|
|
|
58
|
-
function getGroupBaseName(clientId, connectionTag) {
|
|
59
|
-
return buildGroupName(clientId, connectionTag);
|
|
59
|
+
function getGroupBaseName(clientId, connectionTag, mode) {
|
|
60
|
+
return buildGroupName(clientId, connectionTag, mode);
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
function findGroupByName(allGroups, baseName) {
|
package/package.json
CHANGED
package/server/modules/config.js
CHANGED
|
@@ -10,7 +10,8 @@ const CONFIG = {
|
|
|
10
10
|
LOG_LEVEL: process.env.LOG_LEVEL || 'info',
|
|
11
11
|
AUTO_RESTART: process.env.AUTO_RESTART === 'true',
|
|
12
12
|
CHROME_RESTART_COOLDOWN: 30000,
|
|
13
|
-
PLUGIN_MAX_MISSED_PINGS: 3
|
|
13
|
+
PLUGIN_MAX_MISSED_PINGS: 3,
|
|
14
|
+
TAKEOVER_PORT: process.env.TAKEOVER_PORT ? parseInt(process.env.TAKEOVER_PORT) : (parseInt(process.env.PORT || '9221') + 1)
|
|
14
15
|
};
|
|
15
16
|
|
|
16
17
|
const LOG_LEVELS = {
|
package/server/proxy-server.js
CHANGED
|
@@ -16,6 +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 TAKEOVER_PORT = CONFIG.TAKEOVER_PORT;
|
|
19
20
|
const { logCDP, logEvent, clearLog, logStatus, logConnectionEvent, flushAllLogs, logDisconnect } = require('./modules/logger');
|
|
20
21
|
|
|
21
22
|
try {
|
|
@@ -195,6 +196,8 @@ console.log(` Server started on port ${PORT}`);
|
|
|
195
196
|
console.log(` - Plugin path: ws://localhost:${PORT}/plugin`);
|
|
196
197
|
console.log(` - Client path: ws://localhost:${PORT}/client`);
|
|
197
198
|
console.log(` - CDP endpoint: http://localhost:${PORT}`);
|
|
199
|
+
console.log(` - Takeover port: ${TAKEOVER_PORT} (mode=takeover)`);
|
|
200
|
+
console.log(` - Takeover CDP: http://localhost:${TAKEOVER_PORT}`);
|
|
198
201
|
console.log('='.repeat(60));
|
|
199
202
|
|
|
200
203
|
/**
|
|
@@ -436,7 +439,8 @@ wss.on('connection', (ws, req) => {
|
|
|
436
439
|
} else if (pathParts[0] === 'devtools' && pathParts[1] === 'browser' && pathParts[2]) {
|
|
437
440
|
targetPluginId = pathParts[2];
|
|
438
441
|
}
|
|
439
|
-
|
|
442
|
+
const mode = req._takeoverMode ? 'takeover' : 'create';
|
|
443
|
+
handleClientConnection(ws, clientInfo, customClientId, targetPluginId, mode);
|
|
440
444
|
} else if (path.startsWith('/devtools/page/')) {
|
|
441
445
|
const targetId = path.replace('/devtools/page/', '');
|
|
442
446
|
handlePageConnection(ws, clientInfo, targetId);
|
|
@@ -449,6 +453,7 @@ wss.on('connection', (ws, req) => {
|
|
|
449
453
|
function cleanupClient(ws, id, reason) {
|
|
450
454
|
const pluginWs = ws.pairedPlugin || clientIdToPlugin.get(id);
|
|
451
455
|
const ns = pluginWs ? getNamespace(pluginWs) : null;
|
|
456
|
+
const isTakeover = ws.mode === 'takeover';
|
|
452
457
|
|
|
453
458
|
if (ns) {
|
|
454
459
|
const sessionsToClean = [];
|
|
@@ -467,6 +472,7 @@ function cleanupClient(ws, id, reason) {
|
|
|
467
472
|
logConnectionEvent('CLIENT_DISCONNECTED', {
|
|
468
473
|
id,
|
|
469
474
|
reason,
|
|
475
|
+
mode: ws.mode || 'create',
|
|
470
476
|
totalPlugins: pluginConnections.size,
|
|
471
477
|
totalClients: clientConnections.size
|
|
472
478
|
});
|
|
@@ -474,6 +480,7 @@ function cleanupClient(ws, id, reason) {
|
|
|
474
480
|
logDisconnect('CLIENT_CLEANUP', {
|
|
475
481
|
clientId: id,
|
|
476
482
|
reason,
|
|
483
|
+
mode: ws.mode || 'create',
|
|
477
484
|
cdpMethodsUsed: ws.cdpTrace ? [...new Set(ws.cdpTrace)] : [],
|
|
478
485
|
uptime: ws.connectedAt ? `${((Date.now() - ws.connectedAt) / 1000).toFixed(0)}s` : 'unknown',
|
|
479
486
|
remainingClients: clientConnections.size,
|
|
@@ -487,32 +494,41 @@ function cleanupClient(ws, id, reason) {
|
|
|
487
494
|
}
|
|
488
495
|
|
|
489
496
|
if (ws.pairedPlugin) {
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
console.log(`[
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
}
|
|
497
|
+
if (isTakeover) {
|
|
498
|
+
safeSend(ws.pairedPlugin, JSON.stringify({
|
|
499
|
+
type: 'takeover-disconnect',
|
|
500
|
+
clientId: id,
|
|
501
|
+
sessions: []
|
|
502
|
+
}), 'plugin');
|
|
503
|
+
console.log(`[TAKEOVER DISCONNECT] client=${id} — detaching only, not closing tabs`);
|
|
504
|
+
} else {
|
|
505
|
+
const sendOk = safeSend(ws.pairedPlugin, JSON.stringify({
|
|
506
|
+
type: 'client-disconnected',
|
|
507
|
+
clientId: id,
|
|
508
|
+
sessions: []
|
|
509
|
+
}), 'plugin');
|
|
510
|
+
if (!sendOk) {
|
|
511
|
+
console.log(`[WARN] cleanupClient: failed to send client-disconnected for ${id} to plugin`);
|
|
506
512
|
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
+
|
|
514
|
+
const pluginNs = getNamespace(ws.pairedPlugin);
|
|
515
|
+
if (pluginNs) {
|
|
516
|
+
const targetsToClose = [];
|
|
517
|
+
for (const [tId, cId] of pluginNs.targetIdToClientId.entries()) {
|
|
518
|
+
if (cId === id) {
|
|
519
|
+
targetsToClose.push(tId);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
targetsToClose.forEach(function(tId) {
|
|
523
|
+
const closeReq = JSON.stringify({
|
|
524
|
+
id: -1,
|
|
525
|
+
method: 'Target.closeTarget',
|
|
526
|
+
params: { targetId: tId },
|
|
527
|
+
__clientId: id
|
|
528
|
+
});
|
|
529
|
+
safeSend(ws.pairedPlugin, closeReq, 'plugin');
|
|
513
530
|
});
|
|
514
|
-
|
|
515
|
-
});
|
|
531
|
+
}
|
|
516
532
|
}
|
|
517
533
|
}
|
|
518
534
|
|
|
@@ -819,7 +835,23 @@ function handlePluginConnection(ws, clientInfo, request) {
|
|
|
819
835
|
pendingMap.set(targetId, { parsed: JSON.parse(JSON.stringify(parsed)), cdpData });
|
|
820
836
|
console.log(`[TARGET EVENT PENDING] ${parsed.method} targetId=${targetId?.substring(0,8) || 'none'} (cached, waiting for createTarget response)`);
|
|
821
837
|
} else {
|
|
822
|
-
|
|
838
|
+
let takeoverRouted = false;
|
|
839
|
+
ns.discoveringClientIds.forEach((timestamp, discClientId) => {
|
|
840
|
+
if (takeoverRouted) return;
|
|
841
|
+
const discWs = clientById.get(discClientId);
|
|
842
|
+
if (discWs && discWs.mode === 'takeover' && discWs.readyState === WebSocket.OPEN) {
|
|
843
|
+
discWs.send(cdpData);
|
|
844
|
+
takeoverRouted = true;
|
|
845
|
+
console.log(`[TAKEOVER EVENT ROUTED] ${parsed.method} targetId=${targetId?.substring(0,8)} -> clientId=${discClientId}`);
|
|
846
|
+
if (parsed.params?.sessionId) {
|
|
847
|
+
ns.sessionToClientId.set(parsed.params.sessionId, discClientId);
|
|
848
|
+
}
|
|
849
|
+
ns.targetIdToClientId.set(targetId, discClientId);
|
|
850
|
+
}
|
|
851
|
+
});
|
|
852
|
+
if (!takeoverRouted) {
|
|
853
|
+
console.log(`[TARGET EVENT DROPPED] ${parsed.method} targetId=${targetId?.substring(0,8) || 'none'} (no owner, dropped for isolation)`);
|
|
854
|
+
}
|
|
823
855
|
}
|
|
824
856
|
} else {
|
|
825
857
|
console.log(`[TARGET EVENT DROPPED] ${parsed.method} targetId=${targetId?.substring(0,8) || 'none'} (no owner, dropped for isolation)`);
|
|
@@ -893,6 +925,10 @@ function handlePluginConnection(ws, clientInfo, request) {
|
|
|
893
925
|
if (parsed.result?.sessionId && mapping.method === 'Target.attachToTarget') {
|
|
894
926
|
ns.sessionToClientId.set(parsed.result.sessionId, mapping.clientId);
|
|
895
927
|
console.log(`[SESSION MAPPED from attach response] sessionId=${parsed.result.sessionId?.substring(0,8)} -> clientId=${mapping.clientId?.substring(0,8)}`);
|
|
928
|
+
if (mapping.attachTargetId && !ns.targetIdToClientId.has(mapping.attachTargetId)) {
|
|
929
|
+
ns.targetIdToClientId.set(mapping.attachTargetId, mapping.clientId);
|
|
930
|
+
console.log(`[TARGET MAPPED from attach] targetId=${mapping.attachTargetId?.substring(0,8)} -> clientId=${mapping.clientId}`);
|
|
931
|
+
}
|
|
896
932
|
}
|
|
897
933
|
|
|
898
934
|
if (mapping.isCreateTarget && parsed.result?.targetId) {
|
|
@@ -937,12 +973,16 @@ function handlePluginConnection(ws, clientInfo, request) {
|
|
|
937
973
|
}
|
|
938
974
|
if (mapping.isGetTargets && parsed.result && parsed.result.targetInfos) {
|
|
939
975
|
const clientId = mapping.clientId;
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
976
|
+
if (mapping.isTakeover) {
|
|
977
|
+
console.log(`[GET TARGETS TAKEOVER] client=${clientId} returning unfiltered targets (${parsed.result.targetInfos.length})`);
|
|
978
|
+
} else {
|
|
979
|
+
parsed.result.targetInfos = parsed.result.targetInfos.filter(t => {
|
|
980
|
+
if (t.type !== 'page') return true;
|
|
981
|
+
const ownerClient = ns.targetIdToClientId.get(t.targetId);
|
|
982
|
+
return ownerClient === clientId;
|
|
983
|
+
});
|
|
984
|
+
console.log(`[GET TARGETS FILTERED] client=${clientId} returned ${parsed.result.targetInfos.filter(t => t.type === 'page').length} page targets`);
|
|
985
|
+
}
|
|
946
986
|
}
|
|
947
987
|
if (parsed.result && parsed.result.success !== undefined && mapping.method === 'Target.closeTarget') {
|
|
948
988
|
if (mapping.closeTargetId) {
|
|
@@ -1123,11 +1163,12 @@ function forwardToPlugin(clientWs, data, clientId) {
|
|
|
1123
1163
|
/**
|
|
1124
1164
|
* 处理 CDP 客户端连接 (Playwright/Puppeteer)
|
|
1125
1165
|
*/
|
|
1126
|
-
function handleClientConnection(ws, clientInfo, customClientId = null, targetPluginId = null) {
|
|
1166
|
+
function handleClientConnection(ws, clientInfo, customClientId = null, targetPluginId = null, mode = 'create') {
|
|
1127
1167
|
clientConnections.add(ws);
|
|
1128
1168
|
const id = customClientId || generateId('client');
|
|
1169
|
+
ws.mode = mode;
|
|
1129
1170
|
if (shouldLog('info')) {
|
|
1130
|
-
console.log(`\n[CLIENT CONNECTED] ID: ${id}${customClientId ? ' (custom)' : ''}${targetPluginId ? ` targetPlugin=${targetPluginId}` : ''}`);
|
|
1171
|
+
console.log(`\n[CLIENT CONNECTED] ID: ${id}${customClientId ? ' (custom)' : ''}${targetPluginId ? ` targetPlugin=${targetPluginId}` : ''} mode=${mode}`);
|
|
1131
1172
|
console.log(` - Remote: ${clientInfo.ip}:${clientInfo.port}`);
|
|
1132
1173
|
console.log(` - Total client connections: ${clientConnections.size}`);
|
|
1133
1174
|
}
|
|
@@ -1296,6 +1337,10 @@ function handleClientConnection(ws, clientInfo, customClientId = null, targetPlu
|
|
|
1296
1337
|
safeSend(ws, errMsg, 'client');
|
|
1297
1338
|
return;
|
|
1298
1339
|
}
|
|
1340
|
+
const currentMapping = globalRequestIdMap.get(parsed.id);
|
|
1341
|
+
if (currentMapping && targetId) {
|
|
1342
|
+
currentMapping.attachTargetId = targetId;
|
|
1343
|
+
}
|
|
1299
1344
|
}
|
|
1300
1345
|
}
|
|
1301
1346
|
|
|
@@ -1311,6 +1356,9 @@ function handleClientConnection(ws, clientInfo, customClientId = null, targetPlu
|
|
|
1311
1356
|
const currentMapping = globalRequestIdMap.get(parsed.id);
|
|
1312
1357
|
if (currentMapping) {
|
|
1313
1358
|
currentMapping.isGetTargets = true;
|
|
1359
|
+
if (ws.mode === 'takeover') {
|
|
1360
|
+
currentMapping.isTakeover = true;
|
|
1361
|
+
}
|
|
1314
1362
|
}
|
|
1315
1363
|
}
|
|
1316
1364
|
|
|
@@ -1324,17 +1372,31 @@ function handleClientConnection(ws, clientInfo, customClientId = null, targetPlu
|
|
|
1324
1372
|
|
|
1325
1373
|
if (parsed && parsed.method === 'Target.setAutoAttach' && parsed.params?.autoAttach && !ws._autoDefaultPageSent) {
|
|
1326
1374
|
ws._autoDefaultPageSent = true;
|
|
1327
|
-
|
|
1375
|
+
if (ws.mode === 'takeover') {
|
|
1376
|
+
const takeoverMsg = { ...parsed, __clientId: id, __mode: 'takeover' };
|
|
1377
|
+
if (ws.pairedPlugin && ws.pairedPlugin.readyState === WebSocket.OPEN) {
|
|
1378
|
+
ws.pairedPlugin.send(JSON.stringify(takeoverMsg));
|
|
1379
|
+
}
|
|
1380
|
+
} else {
|
|
1381
|
+
autoCreateDefaultPageAndForward(ws, parsed, modifiedData, id, originalId);
|
|
1382
|
+
}
|
|
1328
1383
|
return;
|
|
1329
1384
|
}
|
|
1330
1385
|
|
|
1331
1386
|
if (parsed && parsed.method === 'Browser.close') {
|
|
1332
|
-
console.log(`[BROWSER CLOSE] Client ${id} requested Browser.close
|
|
1387
|
+
console.log(`[BROWSER CLOSE] Client ${id} mode=${ws.mode} requested Browser.close`);
|
|
1333
1388
|
if (ws.pairedPlugin) {
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1389
|
+
if (ws.mode === 'takeover') {
|
|
1390
|
+
safeSend(ws.pairedPlugin, JSON.stringify({
|
|
1391
|
+
type: 'takeover-disconnect',
|
|
1392
|
+
clientId: id
|
|
1393
|
+
}), 'plugin');
|
|
1394
|
+
} else {
|
|
1395
|
+
safeSend(ws.pairedPlugin, JSON.stringify({
|
|
1396
|
+
type: 'browser-close',
|
|
1397
|
+
clientId: id
|
|
1398
|
+
}), 'plugin');
|
|
1399
|
+
}
|
|
1338
1400
|
}
|
|
1339
1401
|
safeSend(ws, JSON.stringify({ id: originalId, result: {} }), 'client');
|
|
1340
1402
|
return;
|
|
@@ -1353,8 +1415,10 @@ function handleClientConnection(ws, clientInfo, customClientId = null, targetPlu
|
|
|
1353
1415
|
// 发送给配对的 plugin (或广播)
|
|
1354
1416
|
if (ws.pairedPlugin && ws.pairedPlugin.readyState === WebSocket.OPEN) {
|
|
1355
1417
|
console.log(`[SEND TO PLUGIN] id=${parsed?.id} method=${parsed?.method} sessionId=${parsed?.sessionId?.substring(0,8) || 'none'} clientId=${id}`);
|
|
1356
|
-
// 在消息中附加 clientId 信息
|
|
1357
1418
|
const pluginMsg = { ...parsed, __clientId: id };
|
|
1419
|
+
if (ws.mode === 'takeover') {
|
|
1420
|
+
pluginMsg.__mode = 'takeover';
|
|
1421
|
+
}
|
|
1358
1422
|
ws.pairedPlugin.send(JSON.stringify(pluginMsg));
|
|
1359
1423
|
} else {
|
|
1360
1424
|
broadcastToPlugins(modifiedData, ws);
|
|
@@ -1920,3 +1984,35 @@ server.on('error', (err) => {
|
|
|
1920
1984
|
});
|
|
1921
1985
|
|
|
1922
1986
|
server.listen(PORT, '0.0.0.0');
|
|
1987
|
+
|
|
1988
|
+
const takeoverServer = http.createServer((req, res) => handleHttpRequest(req, res));
|
|
1989
|
+
takeoverServer.on('upgrade', (req, socket, head) => {
|
|
1990
|
+
req._takeoverMode = true;
|
|
1991
|
+
const url = new URL(req.url, `http://localhost:${TAKEOVER_PORT}`);
|
|
1992
|
+
const path = url.pathname;
|
|
1993
|
+
const isPlugin = path === '/plugin';
|
|
1994
|
+
const isClient = path === '/client' ||
|
|
1995
|
+
path.startsWith('/client/') ||
|
|
1996
|
+
path.startsWith('/client-') ||
|
|
1997
|
+
path.startsWith('/devtools/browser/') ||
|
|
1998
|
+
path.startsWith('/devtools/page/');
|
|
1999
|
+
|
|
2000
|
+
if (!isPlugin && !isClient) {
|
|
2001
|
+
socket.destroy();
|
|
2002
|
+
return;
|
|
2003
|
+
}
|
|
2004
|
+
|
|
2005
|
+
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
2006
|
+
wss.emit('connection', ws, req);
|
|
2007
|
+
});
|
|
2008
|
+
});
|
|
2009
|
+
takeoverServer.on('error', (err) => {
|
|
2010
|
+
if (err.code === 'EADDRINUSE') {
|
|
2011
|
+
console.error(`[WARN] Takeover port ${TAKEOVER_PORT} is already in use. Takeover mode disabled.`);
|
|
2012
|
+
} else {
|
|
2013
|
+
console.error('[WARN] Takeover server error:', err.message);
|
|
2014
|
+
}
|
|
2015
|
+
});
|
|
2016
|
+
takeoverServer.listen(TAKEOVER_PORT, '0.0.0.0', () => {
|
|
2017
|
+
console.log(`[TAKEOVER] Listening on port ${TAKEOVER_PORT}`);
|
|
2018
|
+
});
|