cdp-tunnel 1.0.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.
Files changed (46) hide show
  1. package/.github/workflows/publish.yml +92 -0
  2. package/.github/workflows/release-assets.yml +50 -0
  3. package/LICENSE +81 -0
  4. package/PUBLISH.md +65 -0
  5. package/README.md +228 -0
  6. package/cli/guide.html +753 -0
  7. package/cli/icon.svg +13 -0
  8. package/cli/icon128.png +0 -0
  9. package/cli/index.js +357 -0
  10. package/docs/README_CN.md +204 -0
  11. package/docs/config-page-screenshot.png +0 -0
  12. package/extension-new/background.js +294 -0
  13. package/extension-new/cdp/handler/forward.js +44 -0
  14. package/extension-new/cdp/handler/local.js +233 -0
  15. package/extension-new/cdp/handler/special.js +442 -0
  16. package/extension-new/cdp/index.js +104 -0
  17. package/extension-new/cdp/response.js +49 -0
  18. package/extension-new/config-page-preview.html +769 -0
  19. package/extension-new/config-page.js +318 -0
  20. package/extension-new/core/debugger.js +310 -0
  21. package/extension-new/core/state.js +384 -0
  22. package/extension-new/core/websocket.js +326 -0
  23. package/extension-new/features/automation-badge.js +113 -0
  24. package/extension-new/features/screencast.js +221 -0
  25. package/extension-new/icons/icon128.png +0 -0
  26. package/extension-new/icons/icon16.png +0 -0
  27. package/extension-new/icons/icon48.png +0 -0
  28. package/extension-new/manifest.json +39 -0
  29. package/extension-new/popup.html +72 -0
  30. package/extension-new/popup.js +34 -0
  31. package/extension-new/utils/config.js +20 -0
  32. package/extension-new/utils/diagnostics.js +560 -0
  33. package/extension-new/utils/helpers.js +25 -0
  34. package/extension-new/utils/logger.js +64 -0
  35. package/package.json +42 -0
  36. package/server/modules/config.js +28 -0
  37. package/server/modules/logger.js +197 -0
  38. package/server/proxy-server.js +1431 -0
  39. package/tests/playwright-demo.js +45 -0
  40. package/tests/playwright-interactive.js +261 -0
  41. package/tests/playwright-multi-demo.js +60 -0
  42. package/tests/playwright-multi.js +85 -0
  43. package/tests/playwright-single.js +41 -0
  44. package/tests/screenshot-config.js +35 -0
  45. package/tests/test-client.js +89 -0
  46. package/tests/test-multi-client.js +129 -0
@@ -0,0 +1,560 @@
1
+ var Diagnostics = (function() {
2
+ var _stats = {
3
+ startTime: Date.now(),
4
+ messagesReceived: 0,
5
+ messagesSent: 0,
6
+ bytesReceived: 0,
7
+ bytesSent: 0,
8
+ blobMessages: 0,
9
+ pendingBlobs: 0,
10
+ blobErrors: 0,
11
+ largeMessages: 0,
12
+ bufferedAmountPeaks: [],
13
+ errors: [],
14
+ disconnectReasons: [],
15
+ chromeDebuggerEvents: 0,
16
+ chromeDebuggerDropped: 0,
17
+ messageRateHistory: [],
18
+ lastRateCheck: Date.now(),
19
+ messagesInLastSecond: 0,
20
+ tabEvents: [],
21
+ debuggerEvents: [],
22
+ sessionMappings: []
23
+ };
24
+
25
+ var _config = {
26
+ largeMessageThreshold: 100000,
27
+ bufferedAmountThreshold: 50000,
28
+ logInterval: 5000,
29
+ rateCheckInterval: 1000,
30
+ maxEventsToKeep: 50
31
+ };
32
+
33
+ var _intervals = [];
34
+ var _originalWebSocketSend = null;
35
+ var _isMonitoring = false;
36
+
37
+ function startMonitoring() {
38
+ if (_isMonitoring) {
39
+ console.log('[DIAG] Already monitoring');
40
+ return;
41
+ }
42
+ _isMonitoring = true;
43
+ _stats.startTime = Date.now();
44
+
45
+ console.log('%c[DIAG] ========== 诊断监控已启动 ==========',
46
+ 'background: #4CAF50; color: white; font-size: 14px; padding: 5px;');
47
+ console.log('[DIAG] 配置:', _config);
48
+
49
+ monitorWebSocket();
50
+ monitorRate();
51
+ monitorChromeDebugger();
52
+ monitorTabs();
53
+ startPeriodicLog();
54
+
55
+ console.log('[DIAG] 可用命令:');
56
+ console.log(' - Diagnostics.getReport() // 获取完整报告');
57
+ console.log(' - Diagnostics.getTimeline() // 获取事件时间线');
58
+ console.log(' - Diagnostics.reset() // 重置统计');
59
+ console.log(' - Diagnostics.stop() // 停止监控');
60
+ }
61
+
62
+ function monitorWebSocket() {
63
+ var ws = State.getWs();
64
+ if (!ws) {
65
+ console.log('[DIAG] WebSocket未初始化,等待连接...');
66
+ setTimeout(monitorWebSocket, 1000);
67
+ return;
68
+ }
69
+
70
+ console.log('[DIAG] WebSocket已找到,开始监控');
71
+
72
+ var originalOnMessage = ws.onmessage;
73
+ ws.onmessage = function(event) {
74
+ var data = event.data;
75
+ var size = data.size || data.length || 0;
76
+
77
+ _stats.messagesReceived++;
78
+ _stats.bytesReceived += size;
79
+ _stats.messagesInLastSecond++;
80
+
81
+ if (size > _config.largeMessageThreshold) {
82
+ _stats.largeMessages++;
83
+ console.warn('[DIAG] 大消息:', size, 'bytes');
84
+ }
85
+
86
+ if (data instanceof Blob) {
87
+ _stats.blobMessages++;
88
+ _stats.pendingBlobs++;
89
+ console.log('[DIAG] Blob消息, size:', data.size, 'pending:', _stats.pendingBlobs);
90
+
91
+ data.text().then(function(text) {
92
+ _stats.pendingBlobs--;
93
+ }).catch(function(e) {
94
+ _stats.pendingBlobs--;
95
+ _stats.blobErrors++;
96
+ console.error('[DIAG] Blob处理错误:', e);
97
+ });
98
+ }
99
+
100
+ if (originalOnMessage) {
101
+ originalOnMessage.call(ws, event);
102
+ }
103
+ };
104
+
105
+ var originalOnClose = ws.onclose;
106
+ ws.onclose = function(event) {
107
+ var reason = {
108
+ code: event.code,
109
+ reason: event.reason,
110
+ wasClean: event.wasClean,
111
+ time: Date.now(),
112
+ uptime: Date.now() - _stats.startTime,
113
+ stats: getStats()
114
+ };
115
+ _stats.disconnectReasons.push(reason);
116
+
117
+ console.error('%c[DIAG] WebSocket断开!', 'background: #F44336; color: white; font-size: 14px;');
118
+ console.error('[DIAG] 断开详情:', reason);
119
+ console.error('[DIAG] 断开代码含义:', getCloseCodeMeaning(event.code));
120
+
121
+ addTimelineEvent('WS_CLOSE', {
122
+ code: event.code,
123
+ reason: event.reason,
124
+ meaning: getCloseCodeMeaning(event.code)
125
+ });
126
+
127
+ if (originalOnClose) {
128
+ originalOnClose.call(ws, event);
129
+ }
130
+ };
131
+
132
+ var originalOnError = ws.onerror;
133
+ ws.onerror = function(event) {
134
+ _stats.errors.push({
135
+ time: Date.now(),
136
+ type: 'WebSocket error',
137
+ event: event
138
+ });
139
+ console.error('[DIAG] WebSocket错误:', event);
140
+
141
+ addTimelineEvent('WS_ERROR', { event: event });
142
+
143
+ if (originalOnError) {
144
+ originalOnError.call(ws, event);
145
+ }
146
+ };
147
+
148
+ _originalWebSocketSend = ws.send.bind(ws);
149
+ ws.send = function(data) {
150
+ var size = data.length || data.size || 0;
151
+ _stats.messagesSent++;
152
+ _stats.bytesSent += size;
153
+
154
+ var bufferedAmount = ws.bufferedAmount;
155
+ if (bufferedAmount > _config.bufferedAmountThreshold) {
156
+ _stats.bufferedAmountPeaks.push({
157
+ time: Date.now(),
158
+ amount: bufferedAmount,
159
+ messageSize: size
160
+ });
161
+ console.warn('[DIAG] 缓冲区警告:', bufferedAmount, 'bytes');
162
+ addTimelineEvent('BUFFER_HIGH', {
163
+ bufferedAmount: bufferedAmount,
164
+ messageSize: size
165
+ });
166
+ }
167
+
168
+ try {
169
+ _originalWebSocketSend(data);
170
+ } catch (e) {
171
+ _stats.errors.push({
172
+ time: Date.now(),
173
+ type: 'Send error',
174
+ error: e.message
175
+ });
176
+ console.error('[DIAG] 发送错误:', e);
177
+ addTimelineEvent('SEND_ERROR', { error: e.message });
178
+ }
179
+ };
180
+ }
181
+
182
+ function monitorChromeDebugger() {
183
+ var originalListener = null;
184
+
185
+ chrome.debugger.onEvent.addListener(function(source, method, params) {
186
+ _stats.chromeDebuggerEvents++;
187
+
188
+ var eventInfo = {
189
+ time: Date.now(),
190
+ tabId: source.tabId,
191
+ method: method,
192
+ paramsSize: params ? JSON.stringify(params).length : 0
193
+ };
194
+
195
+ _stats.debuggerEvents.push(eventInfo);
196
+ if (_stats.debuggerEvents.length > _config.maxEventsToKeep) {
197
+ _stats.debuggerEvents.shift();
198
+ }
199
+
200
+ if (method.startsWith('Network.') || method.startsWith('Log.')) {
201
+ // 网络和日志事件可能很多,只记录简要信息
202
+ if (params && JSON.stringify(params).length > 10000) {
203
+ console.warn('[DIAG] 大量调试器事件数据:', method, 'size:', JSON.stringify(params).length);
204
+ }
205
+ } else {
206
+ addTimelineEvent('DEBUGGER_EVENT', {
207
+ tabId: source.tabId,
208
+ method: method,
209
+ paramsSize: eventInfo.paramsSize
210
+ });
211
+ }
212
+ });
213
+
214
+ chrome.debugger.onDetach.addListener(function(source, reason) {
215
+ console.warn('%c[DIAG] chrome.debugger 断开!', 'background: #FF5722; color: white; font-size: 12px;');
216
+ console.warn('[DIAG] Tab ID:', source.tabId, '原因:', reason);
217
+
218
+ addTimelineEvent('DEBUGGER_DETACH', {
219
+ tabId: source.tabId,
220
+ reason: reason
221
+ });
222
+
223
+ var session = State.findSessionByTabId(source.tabId);
224
+ if (session) {
225
+ console.warn('[DIAG] 受影响的Session:', session);
226
+ }
227
+ });
228
+ }
229
+
230
+ function monitorTabs() {
231
+ chrome.tabs.onRemoved.addListener(function(tabId, removeInfo) {
232
+ console.warn('%c[DIAG] Tab被关闭!', 'background: #E91E63; color: white; font-size: 12px;');
233
+ console.warn('[DIAG] Tab ID:', tabId, '窗口ID:', removeInfo.windowId);
234
+
235
+ addTimelineEvent('TAB_REMOVED', {
236
+ tabId: tabId,
237
+ windowId: removeInfo.windowId,
238
+ isWindowClosing: removeInfo.isWindowClosing
239
+ });
240
+
241
+ var session = State.findSessionByTabId(tabId);
242
+ if (session) {
243
+ console.warn('[DIAG] Tab关闭将影响Session:', session);
244
+ }
245
+ });
246
+
247
+ chrome.tabs.onReplaced.addListener(function(addedTabId, removedTabId) {
248
+ console.warn('[DIAG] Tab被替换:', removedTabId, '->', addedTabId);
249
+ addTimelineEvent('TAB_REPLACED', {
250
+ addedTabId: addedTabId,
251
+ removedTabId: removedTabId
252
+ });
253
+ });
254
+ }
255
+
256
+ function addTimelineEvent(type, data) {
257
+ var event = {
258
+ time: Date.now(),
259
+ timestamp: new Date().toISOString(),
260
+ type: type,
261
+ data: data
262
+ };
263
+
264
+ _stats.tabEvents.push(event);
265
+ if (_stats.tabEvents.length > _config.maxEventsToKeep * 2) {
266
+ _stats.tabEvents.shift();
267
+ }
268
+ }
269
+
270
+ function getTimeline() {
271
+ console.log('%c[DIAG] ========== 事件时间线 ==========',
272
+ 'background: #673AB7; color: white; font-size: 14px; padding: 5px;');
273
+
274
+ if (_stats.tabEvents.length === 0) {
275
+ console.log('暂无事件记录');
276
+ return [];
277
+ }
278
+
279
+ console.table(_stats.tabEvents.map(function(e) {
280
+ return {
281
+ '时间': e.timestamp,
282
+ '类型': e.type,
283
+ '详情': JSON.stringify(e.data).substring(0, 100)
284
+ };
285
+ }));
286
+
287
+ console.log('\n📊 事件类型统计:');
288
+ var typeCounts = {};
289
+ _stats.tabEvents.forEach(function(e) {
290
+ typeCounts[e.type] = (typeCounts[e.type] || 0) + 1;
291
+ });
292
+ console.table(typeCounts);
293
+
294
+ return _stats.tabEvents;
295
+ }
296
+
297
+ function monitorRate() {
298
+ var interval = setInterval(function() {
299
+ var now = Date.now();
300
+ var elapsed = now - _stats.lastRateCheck;
301
+
302
+ if (elapsed >= _config.rateCheckInterval) {
303
+ var rate = _stats.messagesInLastSecond / (elapsed / 1000);
304
+ _stats.messageRateHistory.push({
305
+ time: now,
306
+ rate: rate,
307
+ pendingBlobs: _stats.pendingBlobs
308
+ });
309
+
310
+ if (_stats.messageRateHistory.length > 60) {
311
+ _stats.messageRateHistory.shift();
312
+ }
313
+
314
+ _stats.messagesInLastSecond = 0;
315
+ _stats.lastRateCheck = now;
316
+ }
317
+ }, _config.rateCheckInterval);
318
+ _intervals.push(interval);
319
+ }
320
+
321
+ function startPeriodicLog() {
322
+ var interval = setInterval(function() {
323
+ var ws = State.getWs();
324
+ var bufferedAmount = ws ? ws.bufferedAmount : 0;
325
+ var readyState = ws ? ws.readyState : -1;
326
+
327
+ console.log('%c[DIAG] 状态报告', 'background: #2196F3; color: white;');
328
+ console.log(' 运行时间:', Math.round((Date.now() - _stats.startTime) / 1000), '秒');
329
+ console.log(' WebSocket状态:', getReadyStateText(readyState));
330
+ console.log(' 缓冲区:', bufferedAmount, 'bytes');
331
+ console.log(' 消息统计:');
332
+ console.log(' - 接收:', _stats.messagesReceived, '条', formatBytes(_stats.bytesReceived));
333
+ console.log(' - 发送:', _stats.messagesSent, '条', formatBytes(_stats.bytesSent));
334
+ console.log(' - 大消息:', _stats.largeMessages, '条');
335
+ console.log(' - Blob消息:', _stats.blobMessages, '条');
336
+ console.log(' - 待处理Blob:', _stats.pendingBlobs);
337
+ console.log(' - Blob错误:', _stats.blobErrors);
338
+ console.log(' 调试器事件:', _stats.chromeDebuggerEvents, '条');
339
+ console.log(' Tab事件:', _stats.tabEvents.length, '条');
340
+
341
+ var attachedTabs = State.getAttachedTabIds();
342
+ console.log(' 已附加的Tab:', attachedTabs.length, '个');
343
+
344
+ if (_stats.messageRateHistory.length > 0) {
345
+ var lastRate = _stats.messageRateHistory[_stats.messageRateHistory.length - 1];
346
+ console.log(' 消息速率:', lastRate.rate.toFixed(1), '条/秒');
347
+ }
348
+
349
+ if (_stats.bufferedAmountPeaks.length > 0) {
350
+ var lastPeak = _stats.bufferedAmountPeaks[_stats.bufferedAmountPeaks.length - 1];
351
+ console.log(' 最近缓冲区峰值:', lastPeak.amount, 'bytes');
352
+ }
353
+ }, _config.logInterval);
354
+ _intervals.push(interval);
355
+ }
356
+
357
+ function getStats() {
358
+ var ws = State.getWs();
359
+ return {
360
+ uptime: Date.now() - _stats.startTime,
361
+ webSocket: {
362
+ readyState: ws ? ws.readyState : -1,
363
+ bufferedAmount: ws ? ws.bufferedAmount : 0
364
+ },
365
+ messages: {
366
+ received: _stats.messagesReceived,
367
+ sent: _stats.messagesSent,
368
+ bytesReceived: _stats.bytesReceived,
369
+ bytesSent: _stats.bytesSent,
370
+ largeMessages: _stats.largeMessages,
371
+ blobMessages: _stats.blobMessages,
372
+ pendingBlobs: _stats.pendingBlobs,
373
+ blobErrors: _stats.blobErrors
374
+ },
375
+ debugger: {
376
+ events: _stats.chromeDebuggerEvents,
377
+ recentEvents: _stats.debuggerEvents.slice(-10)
378
+ },
379
+ tabs: {
380
+ events: _stats.tabEvents.length,
381
+ recentEvents: _stats.tabEvents.slice(-10)
382
+ },
383
+ peaks: _stats.bufferedAmountPeaks.slice(-10),
384
+ errors: _stats.errors.slice(-10),
385
+ disconnects: _stats.disconnectReasons,
386
+ rateHistory: _stats.messageRateHistory.slice(-10)
387
+ };
388
+ }
389
+
390
+ function getReport() {
391
+ var stats = getStats();
392
+
393
+ console.log('%c[DIAG] ========== 完整诊断报告 ==========',
394
+ 'background: #9C27B0; color: white; font-size: 16px; padding: 10px;');
395
+
396
+ console.log('\n📊 基本统计:');
397
+ console.table({
398
+ '运行时间(秒)': Math.round(stats.uptime / 1000),
399
+ '接收消息数': stats.messages.received,
400
+ '发送消息数': stats.messages.sent,
401
+ '接收字节数': formatBytes(stats.messages.bytesReceived),
402
+ '发送字节数': formatBytes(stats.messages.bytesSent),
403
+ '大消息数': stats.messages.largeMessages,
404
+ 'Blob消息数': stats.messages.blobMessages,
405
+ '待处理Blob': stats.messages.pendingBlobs,
406
+ 'Blob错误数': stats.messages.blobErrors,
407
+ '调试器事件数': stats.debugger.events,
408
+ 'Tab事件数': stats.tabs.events
409
+ });
410
+
411
+ console.log('\n🔌 WebSocket状态:');
412
+ console.table({
413
+ '状态': getReadyStateText(stats.webSocket.readyState),
414
+ '缓冲区': formatBytes(stats.webSocket.bufferedAmount)
415
+ });
416
+
417
+ if (stats.peaks.length > 0) {
418
+ console.log('\n📈 缓冲区峰值 (最近10次):');
419
+ console.table(stats.peaks.map(function(p) {
420
+ return {
421
+ '时间': new Date(p.time).toLocaleTimeString(),
422
+ '缓冲区': formatBytes(p.amount),
423
+ '消息大小': formatBytes(p.messageSize)
424
+ };
425
+ }));
426
+ }
427
+
428
+ if (stats.rateHistory.length > 0) {
429
+ console.log('\n⚡ 消息速率历史 (最近10次):');
430
+ console.table(stats.rateHistory.map(function(r) {
431
+ return {
432
+ '时间': new Date(r.time).toLocaleTimeString(),
433
+ '速率(条/秒)': r.rate.toFixed(1),
434
+ '待处理Blob': r.pendingBlobs
435
+ };
436
+ }));
437
+ }
438
+
439
+ if (stats.errors.length > 0) {
440
+ console.log('\n❌ 错误记录:');
441
+ console.table(stats.errors.map(function(e) {
442
+ return {
443
+ '时间': new Date(e.time).toLocaleTimeString(),
444
+ '类型': e.type,
445
+ '详情': e.error || e.event || '-'
446
+ };
447
+ }));
448
+ }
449
+
450
+ if (stats.disconnects.length > 0) {
451
+ console.log('\n🔌 断开记录:');
452
+ stats.disconnects.forEach(function(d, i) {
453
+ console.log('断开 #' + (i + 1) + ':');
454
+ console.log(' 代码:', d.code, '-', getCloseCodeMeaning(d.code));
455
+ console.log(' 原因:', d.reason || '无');
456
+ console.log(' 正常关闭:', d.wasClean);
457
+ console.log(' 运行时间:', Math.round(d.uptime / 1000), '秒');
458
+ });
459
+ }
460
+
461
+ if (stats.tabs.recentEvents.length > 0) {
462
+ console.log('\n📋 最近的Tab/调试器事件:');
463
+ console.table(stats.tabs.recentEvents.map(function(e) {
464
+ return {
465
+ '时间': e.timestamp || new Date(e.time).toLocaleTimeString(),
466
+ '类型': e.type,
467
+ '详情': JSON.stringify(e.data).substring(0, 80)
468
+ };
469
+ }));
470
+ }
471
+
472
+ return stats;
473
+ }
474
+
475
+ function getCloseCodeMeaning(code) {
476
+ var meanings = {
477
+ 1000: '正常关闭',
478
+ 1001: '端点离开',
479
+ 1002: '协议错误',
480
+ 1003: '不支持的数据类型',
481
+ 1005: '无状态码',
482
+ 1006: '连接异常断开 (可能是网络问题或缓冲区溢出)',
483
+ 1007: '数据类型不一致',
484
+ 1008: '违反策略',
485
+ 1009: '消息过大',
486
+ 1010: '扩展协商失败',
487
+ 1011: '内部错误',
488
+ 1012: '服务重启',
489
+ 1013: '稍后重试',
490
+ 1014: '网关错误',
491
+ 1015: 'TLS握手失败'
492
+ };
493
+ return meanings[code] || '未知代码';
494
+ }
495
+
496
+ function getReadyStateText(state) {
497
+ var states = {
498
+ 0: 'CONNECTING',
499
+ 1: 'OPEN',
500
+ 2: 'CLOSING',
501
+ 3: 'CLOSED'
502
+ };
503
+ return states[state] || 'UNKNOWN';
504
+ }
505
+
506
+ function formatBytes(bytes) {
507
+ if (bytes === 0) return '0 B';
508
+ var k = 1024;
509
+ var sizes = ['B', 'KB', 'MB', 'GB'];
510
+ var i = Math.floor(Math.log(bytes) / Math.log(k));
511
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
512
+ }
513
+
514
+ function reset() {
515
+ _stats = {
516
+ startTime: Date.now(),
517
+ messagesReceived: 0,
518
+ messagesSent: 0,
519
+ bytesReceived: 0,
520
+ bytesSent: 0,
521
+ blobMessages: 0,
522
+ pendingBlobs: 0,
523
+ blobErrors: 0,
524
+ largeMessages: 0,
525
+ bufferedAmountPeaks: [],
526
+ errors: [],
527
+ disconnectReasons: [],
528
+ chromeDebuggerEvents: 0,
529
+ chromeDebuggerDropped: 0,
530
+ messageRateHistory: [],
531
+ lastRateCheck: Date.now(),
532
+ messagesInLastSecond: 0,
533
+ tabEvents: [],
534
+ debuggerEvents: [],
535
+ sessionMappings: []
536
+ };
537
+ console.log('[DIAG] 统计已重置');
538
+ }
539
+
540
+ function stop() {
541
+ _intervals.forEach(function(interval) {
542
+ clearInterval(interval);
543
+ });
544
+ _intervals = [];
545
+ _isMonitoring = false;
546
+ console.log('[DIAG] 监控已停止');
547
+ }
548
+
549
+ return {
550
+ start: startMonitoring,
551
+ stop: stop,
552
+ reset: reset,
553
+ getReport: getReport,
554
+ getStats: getStats,
555
+ getTimeline: getTimeline
556
+ };
557
+ })();
558
+
559
+ console.log('%c[DIAG] 诊断模块已加载', 'background: #FF9800; color: white; padding: 3px;');
560
+ console.log('[DIAG] 运行 Diagnostics.start() 开始监控');
@@ -0,0 +1,25 @@
1
+ var CDPUtils = (function() {
2
+ function generateSessionId() {
3
+ return Array.from({ length: 32 }, function() {
4
+ return Math.floor(Math.random() * 16).toString(16);
5
+ }).join('').toUpperCase();
6
+ }
7
+
8
+ function getChromeVersion(userAgent) {
9
+ if (!userAgent) return '';
10
+ var match = userAgent.match(/Chrome\/([0-9.]+)/);
11
+ return match ? match[1] : '';
12
+ }
13
+
14
+ function sleep(ms) {
15
+ return new Promise(function(resolve) {
16
+ setTimeout(resolve, ms);
17
+ });
18
+ }
19
+
20
+ return {
21
+ generateSessionId: generateSessionId,
22
+ getChromeVersion: getChromeVersion,
23
+ sleep: sleep
24
+ };
25
+ })();
@@ -0,0 +1,64 @@
1
+ var Logger = (function() {
2
+ var PREFIX = '[CDP Bridge]';
3
+ var DEBUG = true;
4
+
5
+ function formatArgs(args) {
6
+ return Array.from(args).map(function(arg) {
7
+ if (typeof arg === 'object') {
8
+ try {
9
+ return JSON.stringify(arg);
10
+ } catch (e) {
11
+ return String(arg);
12
+ }
13
+ }
14
+ return String(arg);
15
+ }).join(' ');
16
+ }
17
+
18
+ function log(level) {
19
+ var args = Array.from(arguments).slice(1);
20
+ var timestamp = new Date().toISOString();
21
+ var prefix = '[' + timestamp + '] ' + PREFIX + ' [' + level + ']';
22
+ var message = prefix + ' ' + formatArgs(args);
23
+
24
+ switch(level) {
25
+ case 'ERROR':
26
+ console.error(message);
27
+ break;
28
+ case 'WARN':
29
+ console.warn(message);
30
+ break;
31
+ default:
32
+ console.log(message);
33
+ }
34
+ }
35
+
36
+ function info() {
37
+ var args = ['INFO'].concat(Array.from(arguments));
38
+ log.apply(null, args);
39
+ }
40
+
41
+ function warn() {
42
+ var args = ['WARN'].concat(Array.from(arguments));
43
+ log.apply(null, args);
44
+ }
45
+
46
+ function error() {
47
+ var args = ['ERROR'].concat(Array.from(arguments));
48
+ log.apply(null, args);
49
+ }
50
+
51
+ function debug() {
52
+ if (!DEBUG) return;
53
+ var args = ['DEBUG'].concat(Array.from(arguments));
54
+ log.apply(null, args);
55
+ }
56
+
57
+ return {
58
+ info: info,
59
+ warn: warn,
60
+ error: error,
61
+ debug: debug,
62
+ log: info
63
+ };
64
+ })();
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "cdp-tunnel",
3
+ "version": "1.0.0",
4
+ "description": "Chrome Extension CDP Proxy - 通过 Chrome 扩展将 chrome.debugger API 暴露为 WebSocket 端点",
5
+ "main": "server/proxy-server.js",
6
+ "bin": {
7
+ "cdp-tunnel": "./cli/index.js"
8
+ },
9
+ "scripts": {
10
+ "start": "node server/proxy-server.js",
11
+ "client": "node client/playwright-client.js",
12
+ "client:puppeteer": "node client/puppeteer-client.js",
13
+ "test:compat": "node client/test-compat-client.js",
14
+ "test:newpage": "node client/test-newpage-sequence.js",
15
+ "record": "node cdp-recorder/record.js",
16
+ "benchmark": "node benchmark/run-all.js",
17
+ "benchmark:native": "node benchmark/run-all.js --native",
18
+ "benchmark:proxy": "node benchmark/run-all.js --proxy",
19
+ "demo": "node demo/server.js"
20
+ },
21
+ "keywords": [
22
+ "cdp",
23
+ "chrome-devtools-protocol",
24
+ "playwright",
25
+ "puppeteer",
26
+ "chrome-extension"
27
+ ],
28
+ "author": "dyyz1993",
29
+ "license": "Apache-2.0",
30
+ "dependencies": {
31
+ "commander": "^12.0.0",
32
+ "playwright": "^1.58.2",
33
+ "ws": "^8.16.0"
34
+ },
35
+ "devDependencies": {
36
+ "lz-string": "^1.5.0",
37
+ "pako": "^2.1.0",
38
+ "playwright-core": "^1.58.2",
39
+ "puppeteer-core": "^22.0.0",
40
+ "sharp": "^0.34.0"
41
+ }
42
+ }