chrome-cdp-cli 1.5.0 → 1.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/README.md +12 -0
- package/dist/cli/CLIApplication.js +21 -0
- package/dist/cli/CLIInterface.js +65 -1
- package/dist/cli/CommandRouter.js +1 -0
- package/dist/client/ProxyClient.js +254 -0
- package/dist/client/index.js +1 -0
- package/dist/handlers/EvaluateScriptHandler.js +126 -1
- package/dist/handlers/GetConsoleMessageHandler.js +80 -10
- package/dist/handlers/GetNetworkRequestHandler.js +68 -10
- package/dist/handlers/ListConsoleMessagesHandler.js +83 -12
- package/dist/handlers/ListNetworkRequestsHandler.js +67 -11
- package/dist/monitors/ConsoleMonitor.js +47 -0
- package/dist/proxy/ProxyManager.js +267 -0
- package/dist/proxy/index.js +60 -0
- package/dist/proxy/server/CDPEventMonitor.js +263 -0
- package/dist/proxy/server/CDPProxyServer.js +436 -0
- package/dist/proxy/server/ConnectionPool.js +430 -0
- package/dist/proxy/server/FileSystemSecurity.js +358 -0
- package/dist/proxy/server/HealthMonitor.js +242 -0
- package/dist/proxy/server/MessageStore.js +360 -0
- package/dist/proxy/server/PerformanceMonitor.js +277 -0
- package/dist/proxy/server/ProxyAPIServer.js +909 -0
- package/dist/proxy/server/SecurityManager.js +337 -0
- package/dist/proxy/server/WSProxy.js +456 -0
- package/dist/proxy/types/ProxyTypes.js +2 -0
- package/dist/utils/logger.js +256 -18
- package/package.json +7 -1
package/README.md
CHANGED
|
@@ -390,6 +390,18 @@ chrome-cdp-cli list_console_messages
|
|
|
390
390
|
chrome-cdp-cli list_console_messages --filter '{"types":["error","warn"]}'
|
|
391
391
|
```
|
|
392
392
|
|
|
393
|
+
**Note**: Console monitoring only captures messages generated *after* monitoring starts. For historical messages or immediate console operations, use the eval-first approach:
|
|
394
|
+
|
|
395
|
+
```bash
|
|
396
|
+
# Generate and capture console messages in one command
|
|
397
|
+
chrome-cdp-cli eval "console.log('Test message'); console.warn('Warning'); 'Messages logged'"
|
|
398
|
+
|
|
399
|
+
# Check for existing console history (if page maintains it)
|
|
400
|
+
chrome-cdp-cli eval "window.consoleHistory || window._console_logs || 'No custom console history'"
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
See [Console Monitoring Documentation](docs/CONSOLE_MONITORING.md) for detailed solutions and workarounds.
|
|
404
|
+
|
|
393
405
|
#### Network Monitoring
|
|
394
406
|
```bash
|
|
395
407
|
# Get latest network request
|
|
@@ -6,11 +6,13 @@ const ConnectionManager_1 = require("../connection/ConnectionManager");
|
|
|
6
6
|
const handlers_1 = require("../handlers");
|
|
7
7
|
const logger_1 = require("../utils/logger");
|
|
8
8
|
const CommandRouter_1 = require("./CommandRouter");
|
|
9
|
+
const ProxyManager_1 = require("../proxy/ProxyManager");
|
|
9
10
|
class CLIApplication {
|
|
10
11
|
constructor() {
|
|
11
12
|
this.cli = new CLIInterface_1.CLIInterface();
|
|
12
13
|
this.connectionManager = new ConnectionManager_1.ConnectionManager();
|
|
13
14
|
this.logger = new logger_1.Logger();
|
|
15
|
+
this.proxyManager = ProxyManager_1.ProxyManager.getInstance();
|
|
14
16
|
this.setupHandlers();
|
|
15
17
|
}
|
|
16
18
|
setupHandlers() {
|
|
@@ -36,6 +38,10 @@ class CLIApplication {
|
|
|
36
38
|
async run(argv) {
|
|
37
39
|
try {
|
|
38
40
|
const command = this.cli.parseArgs(argv);
|
|
41
|
+
if (command.config.verbose) {
|
|
42
|
+
this.proxyManager.setLogging(true);
|
|
43
|
+
}
|
|
44
|
+
await this.ensureProxyReady();
|
|
39
45
|
if (this.needsConnection(command.name)) {
|
|
40
46
|
await this.ensureConnection(command);
|
|
41
47
|
}
|
|
@@ -49,6 +55,15 @@ class CLIApplication {
|
|
|
49
55
|
return CommandRouter_1.ExitCode.GENERAL_ERROR;
|
|
50
56
|
}
|
|
51
57
|
}
|
|
58
|
+
async ensureProxyReady() {
|
|
59
|
+
try {
|
|
60
|
+
const isReady = await this.proxyManager.ensureProxyReady();
|
|
61
|
+
if (!isReady) {
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
}
|
|
66
|
+
}
|
|
52
67
|
needsConnection(commandName) {
|
|
53
68
|
const noConnectionCommands = [
|
|
54
69
|
'help',
|
|
@@ -104,6 +119,12 @@ class CLIApplication {
|
|
|
104
119
|
this.logger.error('Error during shutdown:', error);
|
|
105
120
|
}
|
|
106
121
|
}
|
|
122
|
+
try {
|
|
123
|
+
await this.proxyManager.shutdown();
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
this.logger.error('Error shutting down proxy manager:', error);
|
|
127
|
+
}
|
|
107
128
|
}
|
|
108
129
|
getCLI() {
|
|
109
130
|
return this.cli;
|
package/dist/cli/CLIInterface.js
CHANGED
|
@@ -39,6 +39,7 @@ const path = __importStar(require("path"));
|
|
|
39
39
|
const CLIInterface_1 = require("../interfaces/CLIInterface");
|
|
40
40
|
const CommandRegistry_1 = require("./CommandRegistry");
|
|
41
41
|
const CommandRouter_1 = require("./CommandRouter");
|
|
42
|
+
const packageJson = require('../../package.json');
|
|
42
43
|
class CLIInterface {
|
|
43
44
|
constructor() {
|
|
44
45
|
this.program = new commander_1.Command();
|
|
@@ -50,7 +51,7 @@ class CLIInterface {
|
|
|
50
51
|
this.program
|
|
51
52
|
.name('chrome-cdp-cli')
|
|
52
53
|
.description('Command-line tool for controlling Chrome browser via DevTools Protocol')
|
|
53
|
-
.version(
|
|
54
|
+
.version(packageJson.version)
|
|
54
55
|
.allowUnknownOption(true)
|
|
55
56
|
.allowExcessArguments(true);
|
|
56
57
|
this.program
|
|
@@ -65,6 +66,10 @@ class CLIInterface {
|
|
|
65
66
|
parseArgs(argv) {
|
|
66
67
|
try {
|
|
67
68
|
const args = argv.slice(2);
|
|
69
|
+
if (args.includes('--version') || args.includes('-V')) {
|
|
70
|
+
console.log(packageJson.version);
|
|
71
|
+
process.exit(0);
|
|
72
|
+
}
|
|
68
73
|
const options = {};
|
|
69
74
|
const commandArgs = [];
|
|
70
75
|
let i = 0;
|
|
@@ -341,6 +346,65 @@ class CLIInterface {
|
|
|
341
346
|
if (result.data === undefined || result.data === null) {
|
|
342
347
|
return 'Success';
|
|
343
348
|
}
|
|
349
|
+
let output = '';
|
|
350
|
+
let dataSourceInfo = '';
|
|
351
|
+
if (result.dataSource === 'proxy' && result.hasHistoricalData) {
|
|
352
|
+
dataSourceInfo = '📊 Data from proxy server (includes historical data)\n';
|
|
353
|
+
}
|
|
354
|
+
else if (result.dataSource === 'direct' && result.hasHistoricalData === false) {
|
|
355
|
+
dataSourceInfo = '⚠️ Data from direct connection (new messages only, no historical data)\n';
|
|
356
|
+
}
|
|
357
|
+
if (result.data && typeof result.data === 'object') {
|
|
358
|
+
const data = result.data;
|
|
359
|
+
if (data.messages && Array.isArray(data.messages)) {
|
|
360
|
+
output += dataSourceInfo;
|
|
361
|
+
if (data.messages.length === 0) {
|
|
362
|
+
output += 'No console messages found.';
|
|
363
|
+
}
|
|
364
|
+
else {
|
|
365
|
+
output += `Found ${data.messages.length} console message(s):\n\n`;
|
|
366
|
+
data.messages.forEach((msg, index) => {
|
|
367
|
+
const timestamp = new Date(msg.timestamp).toISOString();
|
|
368
|
+
output += `[${index + 1}] ${timestamp} [${msg.type.toUpperCase()}] ${msg.text}\n`;
|
|
369
|
+
if (msg.args && msg.args.length > 0) {
|
|
370
|
+
output += ` Args: ${JSON.stringify(msg.args)}\n`;
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
return output.trim();
|
|
375
|
+
}
|
|
376
|
+
if (data.requests && Array.isArray(data.requests)) {
|
|
377
|
+
output += dataSourceInfo;
|
|
378
|
+
if (data.requests.length === 0) {
|
|
379
|
+
output += 'No network requests found.';
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
output += `Found ${data.requests.length} network request(s):\n\n`;
|
|
383
|
+
data.requests.forEach((req, index) => {
|
|
384
|
+
const timestamp = new Date(req.timestamp).toISOString();
|
|
385
|
+
const status = req.status ? ` [${req.status}]` : ' [pending]';
|
|
386
|
+
output += `[${index + 1}] ${timestamp} ${req.method} ${req.url}${status}\n`;
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
return output.trim();
|
|
390
|
+
}
|
|
391
|
+
if (data.type && data.text !== undefined && data.timestamp) {
|
|
392
|
+
output += dataSourceInfo;
|
|
393
|
+
const timestamp = new Date(data.timestamp).toISOString();
|
|
394
|
+
output += `${timestamp} [${data.type.toUpperCase()}] ${data.text}`;
|
|
395
|
+
if (data.args && data.args.length > 0) {
|
|
396
|
+
output += `\nArgs: ${JSON.stringify(data.args)}`;
|
|
397
|
+
}
|
|
398
|
+
return output;
|
|
399
|
+
}
|
|
400
|
+
if (data.requestId && data.url && data.method) {
|
|
401
|
+
output += dataSourceInfo;
|
|
402
|
+
const timestamp = new Date(data.timestamp).toISOString();
|
|
403
|
+
const status = data.status ? ` [${data.status}]` : ' [pending]';
|
|
404
|
+
output += `${timestamp} ${data.method} ${data.url}${status}`;
|
|
405
|
+
return output;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
344
408
|
if (typeof result.data === 'string') {
|
|
345
409
|
return result.data;
|
|
346
410
|
}
|
|
@@ -214,6 +214,7 @@ Global Options:
|
|
|
214
214
|
-q, --quiet Enable quiet mode
|
|
215
215
|
-t, --timeout <ms> Command timeout in milliseconds (default: 30000)
|
|
216
216
|
-c, --config <path> Configuration file path
|
|
217
|
+
-V, --version Show version number
|
|
217
218
|
|
|
218
219
|
Available Commands:
|
|
219
220
|
${commands.map(cmd => ` ${cmd.padEnd(20)} - ${this.getCommandDescription(cmd)}`).join('\n')}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ProxyClient = void 0;
|
|
7
|
+
const ws_1 = require("ws");
|
|
8
|
+
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
9
|
+
const ProxyManager_1 = require("../proxy/ProxyManager");
|
|
10
|
+
class ProxyClient {
|
|
11
|
+
constructor(config) {
|
|
12
|
+
this.config = {
|
|
13
|
+
proxyUrl: 'http://localhost:9223',
|
|
14
|
+
fallbackToDirect: true,
|
|
15
|
+
startProxyIfNeeded: true,
|
|
16
|
+
...config
|
|
17
|
+
};
|
|
18
|
+
this.proxyManager = ProxyManager_1.ProxyManager.getInstance();
|
|
19
|
+
}
|
|
20
|
+
async ensureProxyRunning() {
|
|
21
|
+
if (this.config.startProxyIfNeeded) {
|
|
22
|
+
return await this.proxyManager.ensureProxyReady();
|
|
23
|
+
}
|
|
24
|
+
return await this.isProxyRunning();
|
|
25
|
+
}
|
|
26
|
+
async isProxyAvailable() {
|
|
27
|
+
return await this.isProxyRunning();
|
|
28
|
+
}
|
|
29
|
+
async isProxyRunning() {
|
|
30
|
+
try {
|
|
31
|
+
const controller = new AbortController();
|
|
32
|
+
const timeout = setTimeout(() => controller.abort(), 2000);
|
|
33
|
+
const response = await (0, node_fetch_1.default)(`${this.config.proxyUrl}/api/health`, {
|
|
34
|
+
method: 'GET',
|
|
35
|
+
signal: controller.signal
|
|
36
|
+
});
|
|
37
|
+
clearTimeout(timeout);
|
|
38
|
+
return response.ok;
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async connect(host, port, targetId) {
|
|
45
|
+
const request = {
|
|
46
|
+
host,
|
|
47
|
+
port,
|
|
48
|
+
targetId
|
|
49
|
+
};
|
|
50
|
+
try {
|
|
51
|
+
const controller = new AbortController();
|
|
52
|
+
const timeout = setTimeout(() => controller.abort(), 30000);
|
|
53
|
+
const response = await (0, node_fetch_1.default)(`${this.config.proxyUrl}/api/connect`, {
|
|
54
|
+
method: 'POST',
|
|
55
|
+
headers: {
|
|
56
|
+
'Content-Type': 'application/json'
|
|
57
|
+
},
|
|
58
|
+
body: JSON.stringify(request),
|
|
59
|
+
signal: controller.signal
|
|
60
|
+
});
|
|
61
|
+
clearTimeout(timeout);
|
|
62
|
+
if (!response.ok) {
|
|
63
|
+
throw new Error(`Proxy connect failed: ${response.status} ${response.statusText}`);
|
|
64
|
+
}
|
|
65
|
+
const result = await response.json();
|
|
66
|
+
if (!result.success) {
|
|
67
|
+
throw new Error(`Proxy connect failed: ${result.error}`);
|
|
68
|
+
}
|
|
69
|
+
this.connectionId = result.data.connectionId;
|
|
70
|
+
return this.connectionId;
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
throw new Error(`Failed to connect through proxy: ${error instanceof Error ? error.message : error}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async getConsoleMessages(filter) {
|
|
77
|
+
if (!this.connectionId) {
|
|
78
|
+
throw new Error('No active connection. Call connect() first.');
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
const url = new URL(`${this.config.proxyUrl}/api/console/${this.connectionId}`);
|
|
82
|
+
if (filter) {
|
|
83
|
+
if (filter.types) {
|
|
84
|
+
url.searchParams.set('types', filter.types.join(','));
|
|
85
|
+
}
|
|
86
|
+
if (filter.textPattern) {
|
|
87
|
+
url.searchParams.set('textPattern', filter.textPattern);
|
|
88
|
+
}
|
|
89
|
+
if (filter.maxMessages) {
|
|
90
|
+
url.searchParams.set('maxMessages', filter.maxMessages.toString());
|
|
91
|
+
}
|
|
92
|
+
if (filter.startTime) {
|
|
93
|
+
url.searchParams.set('startTime', filter.startTime.toString());
|
|
94
|
+
}
|
|
95
|
+
if (filter.endTime) {
|
|
96
|
+
url.searchParams.set('endTime', filter.endTime.toString());
|
|
97
|
+
}
|
|
98
|
+
if (filter.source) {
|
|
99
|
+
url.searchParams.set('source', filter.source);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const controller = new AbortController();
|
|
103
|
+
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
104
|
+
const response = await (0, node_fetch_1.default)(url.toString(), {
|
|
105
|
+
method: 'GET',
|
|
106
|
+
signal: controller.signal
|
|
107
|
+
});
|
|
108
|
+
clearTimeout(timeout);
|
|
109
|
+
if (!response.ok) {
|
|
110
|
+
throw new Error(`Failed to get console messages: ${response.status} ${response.statusText}`);
|
|
111
|
+
}
|
|
112
|
+
const result = await response.json();
|
|
113
|
+
if (!result.success) {
|
|
114
|
+
throw new Error(`Failed to get console messages: ${result.error}`);
|
|
115
|
+
}
|
|
116
|
+
return result.data?.messages || [];
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
throw new Error(`Failed to get console messages: ${error instanceof Error ? error.message : error}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
async getNetworkRequests(filter) {
|
|
123
|
+
if (!this.connectionId) {
|
|
124
|
+
throw new Error('No active connection. Call connect() first.');
|
|
125
|
+
}
|
|
126
|
+
try {
|
|
127
|
+
const url = new URL(`${this.config.proxyUrl}/api/network/${this.connectionId}`);
|
|
128
|
+
if (filter) {
|
|
129
|
+
if (filter.methods) {
|
|
130
|
+
url.searchParams.set('methods', filter.methods.join(','));
|
|
131
|
+
}
|
|
132
|
+
if (filter.statusCodes) {
|
|
133
|
+
url.searchParams.set('statusCodes', filter.statusCodes.join(','));
|
|
134
|
+
}
|
|
135
|
+
if (filter.urlPattern) {
|
|
136
|
+
url.searchParams.set('urlPattern', filter.urlPattern);
|
|
137
|
+
}
|
|
138
|
+
if (filter.maxRequests) {
|
|
139
|
+
url.searchParams.set('maxRequests', filter.maxRequests.toString());
|
|
140
|
+
}
|
|
141
|
+
if (filter.startTime) {
|
|
142
|
+
url.searchParams.set('startTime', filter.startTime.toString());
|
|
143
|
+
}
|
|
144
|
+
if (filter.endTime) {
|
|
145
|
+
url.searchParams.set('endTime', filter.endTime.toString());
|
|
146
|
+
}
|
|
147
|
+
if (filter.includeResponseBody !== undefined) {
|
|
148
|
+
url.searchParams.set('includeResponseBody', filter.includeResponseBody.toString());
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const controller = new AbortController();
|
|
152
|
+
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
153
|
+
const response = await (0, node_fetch_1.default)(url.toString(), {
|
|
154
|
+
method: 'GET',
|
|
155
|
+
signal: controller.signal
|
|
156
|
+
});
|
|
157
|
+
clearTimeout(timeout);
|
|
158
|
+
if (!response.ok) {
|
|
159
|
+
throw new Error(`Failed to get network requests: ${response.status} ${response.statusText}`);
|
|
160
|
+
}
|
|
161
|
+
const result = await response.json();
|
|
162
|
+
if (!result.success) {
|
|
163
|
+
throw new Error(`Failed to get network requests: ${result.error}`);
|
|
164
|
+
}
|
|
165
|
+
return result.data?.requests || [];
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
throw new Error(`Failed to get network requests: ${error instanceof Error ? error.message : error}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
async createWebSocketProxy() {
|
|
172
|
+
if (!this.connectionId) {
|
|
173
|
+
throw new Error('No active connection. Call connect() first.');
|
|
174
|
+
}
|
|
175
|
+
try {
|
|
176
|
+
const wsUrl = this.config.proxyUrl.replace('http://', 'ws://').replace('https://', 'wss://');
|
|
177
|
+
const ws = new ws_1.WebSocket(`${wsUrl}/ws/${this.connectionId}`);
|
|
178
|
+
return new Promise((resolve, reject) => {
|
|
179
|
+
const timeout = setTimeout(() => {
|
|
180
|
+
reject(new Error('WebSocket connection timeout'));
|
|
181
|
+
}, 10000);
|
|
182
|
+
ws.on('open', () => {
|
|
183
|
+
clearTimeout(timeout);
|
|
184
|
+
this.wsConnection = ws;
|
|
185
|
+
resolve(ws);
|
|
186
|
+
});
|
|
187
|
+
ws.on('error', (error) => {
|
|
188
|
+
clearTimeout(timeout);
|
|
189
|
+
reject(error);
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
throw new Error(`Failed to create WebSocket proxy: ${error instanceof Error ? error.message : error}`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
async healthCheck() {
|
|
198
|
+
if (!this.connectionId) {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
try {
|
|
202
|
+
const controller = new AbortController();
|
|
203
|
+
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
204
|
+
const response = await (0, node_fetch_1.default)(`${this.config.proxyUrl}/api/health/${this.connectionId}`, {
|
|
205
|
+
method: 'GET',
|
|
206
|
+
signal: controller.signal
|
|
207
|
+
});
|
|
208
|
+
clearTimeout(timeout);
|
|
209
|
+
if (!response.ok) {
|
|
210
|
+
throw new Error(`Health check failed: ${response.status} ${response.statusText}`);
|
|
211
|
+
}
|
|
212
|
+
const result = await response.json();
|
|
213
|
+
if (!result.success) {
|
|
214
|
+
throw new Error(`Health check failed: ${result.error}`);
|
|
215
|
+
}
|
|
216
|
+
return result.data || null;
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
console.warn('Health check failed:', error instanceof Error ? error.message : error);
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
async disconnect() {
|
|
224
|
+
try {
|
|
225
|
+
if (this.wsConnection) {
|
|
226
|
+
this.wsConnection.close();
|
|
227
|
+
this.wsConnection = undefined;
|
|
228
|
+
}
|
|
229
|
+
if (this.connectionId) {
|
|
230
|
+
try {
|
|
231
|
+
const controller = new AbortController();
|
|
232
|
+
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
233
|
+
await (0, node_fetch_1.default)(`${this.config.proxyUrl}/api/connection/${this.connectionId}`, {
|
|
234
|
+
method: 'DELETE',
|
|
235
|
+
signal: controller.signal
|
|
236
|
+
});
|
|
237
|
+
clearTimeout(timeout);
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
}
|
|
241
|
+
this.connectionId = undefined;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
getConnectionId() {
|
|
248
|
+
return this.connectionId;
|
|
249
|
+
}
|
|
250
|
+
getConfig() {
|
|
251
|
+
return { ...this.config };
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
exports.ProxyClient = ProxyClient;
|
package/dist/client/index.js
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.EvaluateScriptHandler = void 0;
|
|
7
|
+
const ProxyClient_1 = require("../client/ProxyClient");
|
|
4
8
|
const fs_1 = require("fs");
|
|
9
|
+
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
5
10
|
class EvaluateScriptHandler {
|
|
6
|
-
constructor() {
|
|
11
|
+
constructor(useProxy = true) {
|
|
7
12
|
this.name = 'eval';
|
|
13
|
+
this.proxyClient = new ProxyClient_1.ProxyClient();
|
|
14
|
+
this.useProxy = useProxy;
|
|
8
15
|
}
|
|
9
16
|
async execute(client, args) {
|
|
10
17
|
const scriptArgs = args;
|
|
@@ -20,6 +27,124 @@ class EvaluateScriptHandler {
|
|
|
20
27
|
error: 'Cannot specify both "expression" and "file" arguments'
|
|
21
28
|
};
|
|
22
29
|
}
|
|
30
|
+
try {
|
|
31
|
+
if (this.useProxy) {
|
|
32
|
+
const proxyAvailable = await this.proxyClient.isProxyAvailable();
|
|
33
|
+
if (proxyAvailable) {
|
|
34
|
+
console.log('[INFO] Using proxy connection for script evaluation');
|
|
35
|
+
return await this.executeWithProxy(scriptArgs);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
console.warn('[WARN] Proxy not available, falling back to direct CDP connection');
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
console.warn('[WARN] Proxy execution failed, falling back to direct CDP:', error instanceof Error ? error.message : error);
|
|
44
|
+
}
|
|
45
|
+
return await this.executeWithDirectCDP(client, scriptArgs);
|
|
46
|
+
}
|
|
47
|
+
async executeWithProxy(scriptArgs) {
|
|
48
|
+
try {
|
|
49
|
+
let expression;
|
|
50
|
+
if (scriptArgs.file) {
|
|
51
|
+
expression = await this.readScriptFile(scriptArgs.file);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
expression = scriptArgs.expression;
|
|
55
|
+
}
|
|
56
|
+
const response = await (0, node_fetch_1.default)('http://localhost:9223/api/connections');
|
|
57
|
+
if (!response.ok) {
|
|
58
|
+
throw new Error('Failed to get proxy connections');
|
|
59
|
+
}
|
|
60
|
+
const result = await response.json();
|
|
61
|
+
if (!result.success || !result.data.connections || result.data.connections.length === 0) {
|
|
62
|
+
throw new Error('No active proxy connections found');
|
|
63
|
+
}
|
|
64
|
+
const connection = result.data.connections.find((conn) => conn.isHealthy);
|
|
65
|
+
if (!connection) {
|
|
66
|
+
throw new Error('No healthy proxy connections found');
|
|
67
|
+
}
|
|
68
|
+
console.log(`[INFO] Using existing proxy connection: ${connection.id}`);
|
|
69
|
+
this.proxyClient.connectionId = connection.id;
|
|
70
|
+
const ws = await this.proxyClient.createWebSocketProxy();
|
|
71
|
+
try {
|
|
72
|
+
const result = await this.executeScriptThroughProxy(ws, expression, scriptArgs);
|
|
73
|
+
return {
|
|
74
|
+
success: true,
|
|
75
|
+
data: result
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
ws.close();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
return {
|
|
84
|
+
success: false,
|
|
85
|
+
error: error instanceof Error ? error.message : String(error)
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async executeScriptThroughProxy(ws, expression, args) {
|
|
90
|
+
return new Promise((resolve, reject) => {
|
|
91
|
+
const commandId = Date.now();
|
|
92
|
+
const timeout = args.timeout || 30000;
|
|
93
|
+
const awaitPromise = args.awaitPromise ?? true;
|
|
94
|
+
const returnByValue = args.returnByValue ?? true;
|
|
95
|
+
const timeoutHandle = setTimeout(() => {
|
|
96
|
+
reject(new Error(`Script execution timeout after ${timeout}ms`));
|
|
97
|
+
}, timeout);
|
|
98
|
+
ws.on('message', (data) => {
|
|
99
|
+
try {
|
|
100
|
+
const response = JSON.parse(data.toString());
|
|
101
|
+
if (response.id === commandId) {
|
|
102
|
+
clearTimeout(timeoutHandle);
|
|
103
|
+
if (response.error) {
|
|
104
|
+
reject(new Error(`CDP Error: ${response.error.message}`));
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const result = response.result;
|
|
108
|
+
if (result.exceptionDetails) {
|
|
109
|
+
const error = new Error(result.result?.description || 'Script execution failed');
|
|
110
|
+
error.exceptionDetails = result.exceptionDetails;
|
|
111
|
+
reject(error);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
let value = result.result?.value;
|
|
115
|
+
if (result.result?.type === 'undefined') {
|
|
116
|
+
value = undefined;
|
|
117
|
+
}
|
|
118
|
+
else if (result.result?.unserializableValue) {
|
|
119
|
+
value = result.result.unserializableValue;
|
|
120
|
+
}
|
|
121
|
+
resolve(value);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
clearTimeout(timeoutHandle);
|
|
126
|
+
reject(new Error(`Failed to parse CDP response: ${error instanceof Error ? error.message : error}`));
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
ws.on('error', (error) => {
|
|
130
|
+
clearTimeout(timeoutHandle);
|
|
131
|
+
reject(new Error(`WebSocket error: ${error.message}`));
|
|
132
|
+
});
|
|
133
|
+
const command = {
|
|
134
|
+
id: commandId,
|
|
135
|
+
method: 'Runtime.evaluate',
|
|
136
|
+
params: {
|
|
137
|
+
expression: expression,
|
|
138
|
+
awaitPromise: awaitPromise,
|
|
139
|
+
returnByValue: returnByValue,
|
|
140
|
+
userGesture: true,
|
|
141
|
+
generatePreview: false
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
ws.send(JSON.stringify(command));
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
async executeWithDirectCDP(client, scriptArgs) {
|
|
23
148
|
try {
|
|
24
149
|
let expression;
|
|
25
150
|
if (scriptArgs.file) {
|