cdp-tunnel 2.5.21 → 2.6.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/cli/index.js +258 -92
- package/extension-new/background.js +19 -1
- package/extension-new/cdp/handler/special.js +28 -28
- package/extension-new/core/debugger.js +35 -36
- package/extension-new/core/websocket.js +43 -37
- package/extension-new/manifest.json +3 -2
- package/extension-new/popup.html +79 -0
- package/extension-new/popup.js +114 -0
- package/extension-new/utils/config.js +13 -1
- package/package.json +6 -1
- package/server/proxy-server.js +321 -163
- package/server/saas/auth.js +128 -0
- package/server/saas/cdp-proxy.js +36 -0
- package/server/saas/db.js +48 -0
- package/server/saas/index.js +147 -0
- package/server/saas/routes.js +184 -0
- package/server/saas/web/index.html +803 -0
|
@@ -33,25 +33,12 @@ var DebuggerManager = (function() {
|
|
|
33
33
|
if (tagName.toLowerCase() === 'iframe') {
|
|
34
34
|
var originalSetAttribute = element.setAttribute;
|
|
35
35
|
element.setAttribute = function(name, value) {
|
|
36
|
-
if (name.toLowerCase() === 'src' && isInternalUrl(value)) {
|
|
36
|
+
if (name.toLowerCase() === 'src' && isInternalUrl(String(value || ''))) {
|
|
37
37
|
console.warn('[CDP-BLOCK] Blocked iframe src:', value);
|
|
38
|
-
return;
|
|
38
|
+
return element;
|
|
39
39
|
}
|
|
40
40
|
return originalSetAttribute.call(this, name, value);
|
|
41
41
|
};
|
|
42
|
-
|
|
43
|
-
Object.defineProperty(element, 'src', {
|
|
44
|
-
set: function(value) {
|
|
45
|
-
if (isInternalUrl(value)) {
|
|
46
|
-
console.warn('[CDP-BLOCK] Blocked iframe src via property:', value);
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
originalSetAttribute.call(this, 'src', value);
|
|
50
|
-
},
|
|
51
|
-
get: function() {
|
|
52
|
-
return element.getAttribute('src');
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
42
|
}
|
|
56
43
|
return element;
|
|
57
44
|
};
|
|
@@ -68,18 +55,25 @@ var DebuggerManager = (function() {
|
|
|
68
55
|
|
|
69
56
|
// 拦截 location 修改
|
|
70
57
|
var locationDescriptor = Object.getOwnPropertyDescriptor(window, 'location');
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
58
|
+
if (locationDescriptor && typeof locationDescriptor.set === 'function') {
|
|
59
|
+
try {
|
|
60
|
+
Object.defineProperty(window, 'location', {
|
|
61
|
+
set: function(value) {
|
|
62
|
+
if (isInternalUrl(String(value))) {
|
|
63
|
+
console.warn('[CDP-BLOCK] Blocked location change:', value);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
locationDescriptor.set.call(window, value);
|
|
67
|
+
},
|
|
68
|
+
get: function() {
|
|
69
|
+
return locationDescriptor.get.call(window);
|
|
70
|
+
},
|
|
71
|
+
configurable: true
|
|
72
|
+
});
|
|
73
|
+
} catch(e) {
|
|
74
|
+
// location property cannot be redefined on some contexts, skip silently
|
|
81
75
|
}
|
|
82
|
-
}
|
|
76
|
+
}
|
|
83
77
|
|
|
84
78
|
console.log('[CDP-BLOCK] Internal URL block script injected');
|
|
85
79
|
})();
|
|
@@ -222,17 +216,22 @@ var DebuggerManager = (function() {
|
|
|
222
216
|
return;
|
|
223
217
|
}
|
|
224
218
|
|
|
225
|
-
//
|
|
219
|
+
// 在新的执行上下文中也注入拦截脚本(data: URL 上下文跳过,避免干扰导航)
|
|
226
220
|
if (isDefaultContext) {
|
|
227
|
-
chrome.
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
221
|
+
chrome.tabs.get(source.tabId, function(tab) {
|
|
222
|
+
if (chrome.runtime.lastError) return;
|
|
223
|
+
var tabUrl = tab ? (tab.url || tab.pendingUrl || '') : '';
|
|
224
|
+
if (tabUrl.startsWith('data:')) return;
|
|
225
|
+
chrome.debugger.sendCommand(
|
|
226
|
+
{ tabId: source.tabId },
|
|
227
|
+
'Runtime.evaluate',
|
|
228
|
+
{
|
|
229
|
+
expression: INTERNAL_URL_BLOCK_SCRIPT,
|
|
230
|
+
contextId: context.id,
|
|
231
|
+
runImmediately: true
|
|
232
|
+
}
|
|
233
|
+
).catch(function() {});
|
|
234
|
+
});
|
|
236
235
|
}
|
|
237
236
|
}
|
|
238
237
|
|
|
@@ -11,45 +11,51 @@ var WebSocketManager = (function() {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
Config.getWsUrl(function(wsUrl) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
ws.onopen = function() {
|
|
22
|
-
Logger.info('[WS] Connected');
|
|
23
|
-
setBadgeStatus('ON');
|
|
24
|
-
State.clearReconnectTimer();
|
|
25
|
-
processQueue();
|
|
26
|
-
broadcastStateUpdate();
|
|
27
|
-
var extVersion = (typeof chrome !== 'undefined' && chrome.runtime && chrome.runtime.getManifest)
|
|
28
|
-
? chrome.runtime.getManifest().version : 'unknown';
|
|
29
|
-
send({ type: 'plugin-hello', version: extVersion });
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
ws.onclose = function(event) {
|
|
33
|
-
Logger.info('[WS] Closed:', event.code, event.reason);
|
|
34
|
-
setBadgeStatus('OFF');
|
|
35
|
-
scheduleReconnect();
|
|
36
|
-
broadcastStateUpdate();
|
|
37
|
-
};
|
|
14
|
+
Config.getPluginId(function(pluginId) {
|
|
15
|
+
if (pluginId) {
|
|
16
|
+
var sep = wsUrl.indexOf('?') >= 0 ? '&' : '?';
|
|
17
|
+
wsUrl += sep + 'pluginId=' + encodeURIComponent(pluginId);
|
|
18
|
+
}
|
|
19
|
+
Logger.info('[WS] Connecting to', wsUrl);
|
|
20
|
+
setBadgeStatus('ON');
|
|
38
21
|
|
|
39
|
-
|
|
40
|
-
|
|
22
|
+
try {
|
|
23
|
+
ws = new WebSocket(wsUrl);
|
|
24
|
+
State.setWs(ws);
|
|
25
|
+
|
|
26
|
+
ws.onopen = function() {
|
|
27
|
+
Logger.info('[WS] Connected');
|
|
28
|
+
setBadgeStatus('ON');
|
|
29
|
+
State.clearReconnectTimer();
|
|
30
|
+
processQueue();
|
|
31
|
+
broadcastStateUpdate();
|
|
32
|
+
var extVersion = (typeof chrome !== 'undefined' && chrome.runtime && chrome.runtime.getManifest)
|
|
33
|
+
? chrome.runtime.getManifest().version : 'unknown';
|
|
34
|
+
send({ type: 'plugin-hello', version: extVersion });
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
ws.onclose = function(event) {
|
|
38
|
+
Logger.info('[WS] Closed:', event.code, event.reason);
|
|
39
|
+
setBadgeStatus('OFF');
|
|
40
|
+
scheduleReconnect();
|
|
41
|
+
broadcastStateUpdate();
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
ws.onerror = function(error) {
|
|
45
|
+
Logger.error('[WS] Error:', error);
|
|
46
|
+
setBadgeStatus('ERR');
|
|
47
|
+
broadcastStateUpdate();
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
ws.onmessage = function(event) {
|
|
51
|
+
handleRawMessage(event.data);
|
|
52
|
+
};
|
|
53
|
+
} catch (error) {
|
|
54
|
+
Logger.error('[WS] Failed to create:', error);
|
|
41
55
|
setBadgeStatus('ERR');
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
ws.onmessage = function(event) {
|
|
46
|
-
handleRawMessage(event.data);
|
|
47
|
-
};
|
|
48
|
-
} catch (error) {
|
|
49
|
-
Logger.error('[WS] Failed to create:', error);
|
|
50
|
-
setBadgeStatus('ERR');
|
|
51
|
-
scheduleReconnect();
|
|
52
|
-
}
|
|
56
|
+
scheduleReconnect();
|
|
57
|
+
}
|
|
58
|
+
});
|
|
53
59
|
});
|
|
54
60
|
}
|
|
55
61
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest_version": 3,
|
|
3
3
|
"name": "CDP Bridge",
|
|
4
|
-
"version": "2.5.
|
|
4
|
+
"version": "2.5.22",
|
|
5
5
|
"description": "Chrome DevTools Protocol Bridge for Playwright/Puppeteer automation",
|
|
6
6
|
"permissions": [
|
|
7
7
|
"debugger",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"service_worker": "background.js"
|
|
20
20
|
},
|
|
21
21
|
"action": {
|
|
22
|
+
"default_popup": "popup.html",
|
|
22
23
|
"default_title": "CDP Bridge",
|
|
23
24
|
"default_icon": {
|
|
24
25
|
"16": "icons/icon16.png",
|
|
@@ -42,4 +43,4 @@
|
|
|
42
43
|
]
|
|
43
44
|
}
|
|
44
45
|
]
|
|
45
|
-
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<style>
|
|
6
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
7
|
+
body { width: 340px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #1a1a2e; color: #e0e0e0; font-size: 13px; }
|
|
8
|
+
|
|
9
|
+
.header { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px; border-bottom: 1px solid #2a2a4a; }
|
|
10
|
+
.header h1 { font-size: 15px; font-weight: 600; color: #fff; }
|
|
11
|
+
.status-badge { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 500; padding: 3px 10px; border-radius: 12px; }
|
|
12
|
+
.status-badge.on { background: rgba(76,175,80,0.15); color: #4CAF50; }
|
|
13
|
+
.status-badge.off { background: rgba(158,158,158,0.15); color: #9E9E9E; }
|
|
14
|
+
.status-badge.err { background: rgba(244,67,54,0.15); color: #F44336; }
|
|
15
|
+
.status-dot { width: 8px; height: 8px; border-radius: 50%; }
|
|
16
|
+
.status-badge.on .status-dot { background: #4CAF50; box-shadow: 0 0 6px #4CAF50; }
|
|
17
|
+
.status-badge.off .status-dot { background: #9E9E9E; }
|
|
18
|
+
.status-badge.err .status-dot { background: #F44336; }
|
|
19
|
+
|
|
20
|
+
.section { padding: 12px 16px; border-bottom: 1px solid #2a2a4a; }
|
|
21
|
+
.section-title { font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; color: #888; margin-bottom: 8px; }
|
|
22
|
+
|
|
23
|
+
.ws-address { display: flex; align-items: center; gap: 8px; }
|
|
24
|
+
.ws-address input { flex: 1; background: #16213e; border: 1px solid #2a2a4a; border-radius: 6px; padding: 7px 10px; color: #e0e0e0; font-size: 12px; font-family: monospace; outline: none; }
|
|
25
|
+
.ws-address input:focus { border-color: #4a9eff; }
|
|
26
|
+
.btn-sm { background: #2a2a4a; border: 1px solid #3a3a5a; border-radius: 6px; padding: 6px 12px; color: #e0e0e0; font-size: 12px; cursor: pointer; white-space: nowrap; }
|
|
27
|
+
.btn-sm:hover { background: #3a3a5a; }
|
|
28
|
+
|
|
29
|
+
.cdp-row { margin-bottom: 8px; }
|
|
30
|
+
.cdp-row:last-child { margin-bottom: 0; }
|
|
31
|
+
.cdp-label { font-size: 11px; color: #888; margin-bottom: 3px; }
|
|
32
|
+
.cdp-url { display: flex; align-items: center; gap: 6px; background: #16213e; border-radius: 6px; padding: 6px 10px; }
|
|
33
|
+
.cdp-url code { flex: 1; font-size: 11px; font-family: monospace; color: #4a9eff; word-break: break-all; line-height: 1.4; }
|
|
34
|
+
.copy-btn { background: none; border: 1px solid #3a3a5a; border-radius: 4px; padding: 3px 8px; color: #aaa; font-size: 11px; cursor: pointer; flex-shrink: 0; }
|
|
35
|
+
.copy-btn:hover { background: #3a3a5a; color: #fff; }
|
|
36
|
+
.copy-btn.copied { color: #4CAF50; border-color: #4CAF50; }
|
|
37
|
+
|
|
38
|
+
.clients-info { font-size: 12px; color: #aaa; }
|
|
39
|
+
.clients-info span { color: #4a9eff; }
|
|
40
|
+
|
|
41
|
+
.footer { padding: 8px 16px; text-align: center; }
|
|
42
|
+
.footer a { color: #666; font-size: 11px; text-decoration: none; }
|
|
43
|
+
.footer a:hover { color: #4a9eff; }
|
|
44
|
+
</style>
|
|
45
|
+
</head>
|
|
46
|
+
<body>
|
|
47
|
+
<div class="header">
|
|
48
|
+
<h1>CDP Bridge</h1>
|
|
49
|
+
<div class="status-badge off" id="statusBadge">
|
|
50
|
+
<div class="status-dot"></div>
|
|
51
|
+
<span id="statusText">未连接</span>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<div class="section">
|
|
56
|
+
<div class="section-title">WebSocket 地址</div>
|
|
57
|
+
<div class="ws-address">
|
|
58
|
+
<input type="text" id="wsInput" placeholder="ws://localhost:9221/plugin" spellcheck="false">
|
|
59
|
+
<button class="btn-sm" id="saveBtn">保存</button>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<div class="section" id="cdpSection" style="display:none;">
|
|
64
|
+
<div class="section-title">CDP 连接地址</div>
|
|
65
|
+
<div id="cdpAddresses"></div>
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
<div class="section" id="clientsSection" style="display:none;">
|
|
69
|
+
<div class="section-title">连接信息</div>
|
|
70
|
+
<div class="clients-info" id="clientsInfo"></div>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<div class="footer">
|
|
74
|
+
<a href="https://github.com" target="_blank">v2.5.21</a>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<script src="popup.js"></script>
|
|
78
|
+
</body>
|
|
79
|
+
</html>
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
(function() {
|
|
2
|
+
var $ = function(id) { return document.getElementById(id); };
|
|
3
|
+
|
|
4
|
+
var wsInput = $('wsInput');
|
|
5
|
+
var saveBtn = $('saveBtn');
|
|
6
|
+
var statusBadge = $('statusBadge');
|
|
7
|
+
var statusText = $('statusText');
|
|
8
|
+
var cdpSection = $('cdpSection');
|
|
9
|
+
var cdpAddresses = $('cdpAddresses');
|
|
10
|
+
var clientsSection = $('clientsSection');
|
|
11
|
+
var clientsInfo = $('clientsInfo');
|
|
12
|
+
|
|
13
|
+
function loadState() {
|
|
14
|
+
chrome.storage.local.get(['wsAddress'], function(result) {
|
|
15
|
+
wsInput.value = result.wsAddress || '';
|
|
16
|
+
});
|
|
17
|
+
chrome.runtime.sendMessage({ type: 'popup-query' }, function(state) {
|
|
18
|
+
if (!state) return;
|
|
19
|
+
updateStatus(state);
|
|
20
|
+
updateCDP(state);
|
|
21
|
+
updateClients(state);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function updateStatus(state) {
|
|
26
|
+
var cls = state.connected ? 'on' : 'off';
|
|
27
|
+
var text = state.connected ? '已连接' : '未连接';
|
|
28
|
+
if (!state.connected && state.lastError) {
|
|
29
|
+
cls = 'err';
|
|
30
|
+
text = '错误';
|
|
31
|
+
}
|
|
32
|
+
statusBadge.className = 'status-badge ' + cls;
|
|
33
|
+
statusText.textContent = text;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function updateCDP(state) {
|
|
37
|
+
if (!state.connected || !state.pluginId) {
|
|
38
|
+
cdpSection.style.display = 'none';
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
cdpSection.style.display = '';
|
|
42
|
+
|
|
43
|
+
var wsUrl = wsInput.value || 'ws://localhost:9221/plugin';
|
|
44
|
+
var cdpBase = wsUrl.replace(/\/plugin(\?.*)?$/, '');
|
|
45
|
+
var pluginId = state.pluginId;
|
|
46
|
+
var cdpPath = '/devtools/browser/' + pluginId;
|
|
47
|
+
|
|
48
|
+
var urls = [];
|
|
49
|
+
var parsed = parseWsUrl(wsUrl);
|
|
50
|
+
if (parsed) {
|
|
51
|
+
urls.push({ label: 'CDP 地址', url: parsed.protocol + '://' + parsed.host + cdpPath });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
var html = '';
|
|
55
|
+
urls.forEach(function(item) {
|
|
56
|
+
html += '<div class="cdp-row">';
|
|
57
|
+
html += '<div class="cdp-label">' + item.label + '</div>';
|
|
58
|
+
html += '<div class="cdp-url">';
|
|
59
|
+
html += '<code>' + escapeHtml(item.url) + '</code>';
|
|
60
|
+
html += '<button class="copy-btn" data-url="' + escapeAttr(item.url) + '">复制</button>';
|
|
61
|
+
html += '</div></div>';
|
|
62
|
+
});
|
|
63
|
+
cdpAddresses.innerHTML = html;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function updateClients(state) {
|
|
67
|
+
if (!state.connected) {
|
|
68
|
+
clientsSection.style.display = 'none';
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
clientsSection.style.display = '';
|
|
72
|
+
var clients = state.cdpClients || [];
|
|
73
|
+
var attached = state.attachedPages || [];
|
|
74
|
+
clientsInfo.innerHTML = '活跃连接: <span>' + clients.length + '</span> | 已附加页面: <span>' + attached.length + '</span>';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function parseWsUrl(url) {
|
|
78
|
+
var m = url.match(/^(wss?):\/\/([^\/]+)/);
|
|
79
|
+
if (!m) return null;
|
|
80
|
+
return { protocol: m[1], host: m[2] };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function escapeHtml(s) {
|
|
84
|
+
return s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
|
85
|
+
}
|
|
86
|
+
function escapeAttr(s) {
|
|
87
|
+
return s.replace(/&/g,'&').replace(/"/g,'"').replace(/</g,'<').replace(/>/g,'>');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
saveBtn.addEventListener('click', function() {
|
|
91
|
+
var val = wsInput.value.trim();
|
|
92
|
+
chrome.storage.local.set({ wsAddress: val }, function() {
|
|
93
|
+
saveBtn.textContent = '已保存';
|
|
94
|
+
setTimeout(function() { saveBtn.textContent = '保存'; }, 1500);
|
|
95
|
+
chrome.runtime.sendMessage({ type: 'ws-reconnect' });
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
cdpAddresses.addEventListener('click', function(e) {
|
|
100
|
+
var btn = e.target.closest('.copy-btn');
|
|
101
|
+
if (!btn) return;
|
|
102
|
+
var url = btn.getAttribute('data-url');
|
|
103
|
+
navigator.clipboard.writeText(url).then(function() {
|
|
104
|
+
btn.textContent = '已复制';
|
|
105
|
+
btn.classList.add('copied');
|
|
106
|
+
setTimeout(function() {
|
|
107
|
+
btn.textContent = '复制';
|
|
108
|
+
btn.classList.remove('copied');
|
|
109
|
+
}, 2000);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
loadState();
|
|
114
|
+
})();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
var Config = {
|
|
2
|
-
WS_URL: 'ws://localhost:
|
|
2
|
+
WS_URL: 'ws://localhost:9221/plugin',
|
|
3
3
|
RECONNECT_DELAY: 3000,
|
|
4
4
|
DEBUGGER_VERSION: '1.3',
|
|
5
5
|
HEARTBEAT_INTERVAL: 25000,
|
|
@@ -17,6 +17,18 @@ var Config = {
|
|
|
17
17
|
callback(result.wsAddress || Config.WS_URL);
|
|
18
18
|
});
|
|
19
19
|
},
|
|
20
|
+
getPluginId: function(callback) {
|
|
21
|
+
chrome.storage.local.get(['pluginId'], function(result) {
|
|
22
|
+
if (result.pluginId) {
|
|
23
|
+
callback(result.pluginId);
|
|
24
|
+
} else {
|
|
25
|
+
var id = 'browser_' + Date.now() + '_' + Math.random().toString(36).substr(2, 8);
|
|
26
|
+
chrome.storage.local.set({ pluginId: id }, function() {
|
|
27
|
+
callback(id);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
},
|
|
20
32
|
AUTO_MUTE: true,
|
|
21
33
|
getAutoMute: function(callback) {
|
|
22
34
|
chrome.storage.local.get(['autoMute'], function(result) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cdp-tunnel",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.0",
|
|
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",
|
|
@@ -53,8 +53,13 @@
|
|
|
53
53
|
"LICENSE"
|
|
54
54
|
],
|
|
55
55
|
"dependencies": {
|
|
56
|
+
"bcryptjs": "^3.0.3",
|
|
57
|
+
"better-sqlite3": "^12.10.0",
|
|
56
58
|
"cdp-tunnel": "^2.5.17",
|
|
57
59
|
"commander": "^12.0.0",
|
|
60
|
+
"http-proxy": "^1.18.1",
|
|
61
|
+
"jsonwebtoken": "^9.0.3",
|
|
62
|
+
"uuid": "^14.0.0",
|
|
58
63
|
"ws": "^8.16.0"
|
|
59
64
|
},
|
|
60
65
|
"devDependencies": {
|