chrome-cdp-cli 2.1.1 → 2.1.2
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/dist/cli/EnhancedCLIInterface.js +48 -38
- package/dist/cli/OutputFormatter.js +77 -75
- package/dist/cli/OutputManager.js +20 -20
- package/dist/client/index.js +0 -1
- package/dist/handlers/EvaluateScriptHandler.js +74 -270
- package/package.json +1 -1
- package/dist/client/ProxyClient.js +0 -278
- package/dist/handlers/RestartProxyHandler.js +0 -97
- package/dist/proxy/ProxyManager.js +0 -270
- package/dist/proxy/index.js +0 -60
- package/dist/proxy/server/CDPEventMonitor.js +0 -263
- package/dist/proxy/server/CDPProxyServer.js +0 -441
- package/dist/proxy/server/CommandExecutionService.js +0 -297
- package/dist/proxy/server/ConnectionPool.js +0 -437
- package/dist/proxy/server/FileSystemSecurity.js +0 -358
- package/dist/proxy/server/HealthMonitor.js +0 -242
- package/dist/proxy/server/MessageStore.js +0 -291
- package/dist/proxy/server/PerformanceMonitor.js +0 -277
- package/dist/proxy/server/ProxyAPIServer.js +0 -1125
- package/dist/proxy/server/SecurityManager.js +0 -337
- package/dist/proxy/server/WSProxy.js +0 -468
- package/dist/proxy/types/ProxyTypes.js +0 -2
|
@@ -1,278 +0,0 @@
|
|
|
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
|
-
console.log(`[DEBUG] Creating WebSocket proxy for connection: ${this.connectionId}`);
|
|
176
|
-
try {
|
|
177
|
-
const wsUrl = this.config.proxyUrl.replace('http://', 'ws://').replace('https://', 'wss://');
|
|
178
|
-
const fullWsUrl = `${wsUrl}/ws/${this.connectionId}`;
|
|
179
|
-
console.log(`[DEBUG] WebSocket URL: ${fullWsUrl}`);
|
|
180
|
-
const ws = new ws_1.WebSocket(fullWsUrl);
|
|
181
|
-
return new Promise((resolve, reject) => {
|
|
182
|
-
const timeout = setTimeout(() => {
|
|
183
|
-
console.log(`[DEBUG] WebSocket connection timeout for ${this.connectionId}`);
|
|
184
|
-
reject(new Error('WebSocket connection timeout'));
|
|
185
|
-
}, 10000);
|
|
186
|
-
ws.on('open', () => {
|
|
187
|
-
console.log(`[DEBUG] WebSocket connection opened for ${this.connectionId}`);
|
|
188
|
-
clearTimeout(timeout);
|
|
189
|
-
this.wsConnection = ws;
|
|
190
|
-
resolve(ws);
|
|
191
|
-
});
|
|
192
|
-
ws.on('error', (error) => {
|
|
193
|
-
console.log(`[DEBUG] WebSocket connection error for ${this.connectionId}:`, error);
|
|
194
|
-
clearTimeout(timeout);
|
|
195
|
-
reject(error);
|
|
196
|
-
});
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
catch (error) {
|
|
200
|
-
console.log(`[DEBUG] Failed to create WebSocket proxy for ${this.connectionId}:`, error);
|
|
201
|
-
throw new Error(`Failed to create WebSocket proxy: ${error instanceof Error ? error.message : error}`);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
async healthCheck() {
|
|
205
|
-
if (!this.connectionId) {
|
|
206
|
-
return null;
|
|
207
|
-
}
|
|
208
|
-
try {
|
|
209
|
-
const controller = new AbortController();
|
|
210
|
-
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
211
|
-
const response = await (0, node_fetch_1.default)(`${this.config.proxyUrl}/api/health/${this.connectionId}`, {
|
|
212
|
-
method: 'GET',
|
|
213
|
-
signal: controller.signal
|
|
214
|
-
});
|
|
215
|
-
clearTimeout(timeout);
|
|
216
|
-
if (!response.ok) {
|
|
217
|
-
throw new Error(`Health check failed: ${response.status} ${response.statusText}`);
|
|
218
|
-
}
|
|
219
|
-
const result = await response.json();
|
|
220
|
-
if (!result.success) {
|
|
221
|
-
throw new Error(`Health check failed: ${result.error}`);
|
|
222
|
-
}
|
|
223
|
-
return result.data || null;
|
|
224
|
-
}
|
|
225
|
-
catch (error) {
|
|
226
|
-
console.warn('Health check failed:', error instanceof Error ? error.message : error);
|
|
227
|
-
return null;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
async disconnect() {
|
|
231
|
-
try {
|
|
232
|
-
if (this.connectionId) {
|
|
233
|
-
try {
|
|
234
|
-
const controller = new AbortController();
|
|
235
|
-
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
236
|
-
await (0, node_fetch_1.default)(`${this.config.proxyUrl}/api/client/release`, {
|
|
237
|
-
method: 'POST',
|
|
238
|
-
headers: {
|
|
239
|
-
'Content-Type': 'application/json',
|
|
240
|
-
'x-client-id': `proxy_client_${Date.now()}`
|
|
241
|
-
},
|
|
242
|
-
signal: controller.signal
|
|
243
|
-
});
|
|
244
|
-
clearTimeout(timeout);
|
|
245
|
-
}
|
|
246
|
-
catch (error) {
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
if (this.wsConnection) {
|
|
250
|
-
this.wsConnection.close();
|
|
251
|
-
this.wsConnection = undefined;
|
|
252
|
-
}
|
|
253
|
-
if (this.connectionId) {
|
|
254
|
-
try {
|
|
255
|
-
const controller = new AbortController();
|
|
256
|
-
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
257
|
-
await (0, node_fetch_1.default)(`${this.config.proxyUrl}/api/connection/${this.connectionId}`, {
|
|
258
|
-
method: 'DELETE',
|
|
259
|
-
signal: controller.signal
|
|
260
|
-
});
|
|
261
|
-
clearTimeout(timeout);
|
|
262
|
-
}
|
|
263
|
-
catch (error) {
|
|
264
|
-
}
|
|
265
|
-
this.connectionId = undefined;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
catch (error) {
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
getConnectionId() {
|
|
272
|
-
return this.connectionId;
|
|
273
|
-
}
|
|
274
|
-
getConfig() {
|
|
275
|
-
return { ...this.config };
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
exports.ProxyClient = ProxyClient;
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RestartProxyHandler = void 0;
|
|
4
|
-
const ProxyManager_1 = require("../proxy/ProxyManager");
|
|
5
|
-
class RestartProxyHandler {
|
|
6
|
-
constructor() {
|
|
7
|
-
this.name = 'restart';
|
|
8
|
-
}
|
|
9
|
-
async execute(_client, args) {
|
|
10
|
-
try {
|
|
11
|
-
const params = args;
|
|
12
|
-
const proxyManager = ProxyManager_1.ProxyManager.getInstance();
|
|
13
|
-
const status = await proxyManager.getStatus();
|
|
14
|
-
if (status.isHealthy && !params.force) {
|
|
15
|
-
return {
|
|
16
|
-
success: true,
|
|
17
|
-
data: {
|
|
18
|
-
message: 'Proxy server is already running and healthy',
|
|
19
|
-
status: status
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
const success = await proxyManager.restart();
|
|
24
|
-
if (success) {
|
|
25
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
26
|
-
const newStatus = await proxyManager.getStatus();
|
|
27
|
-
return {
|
|
28
|
-
success: true,
|
|
29
|
-
data: {
|
|
30
|
-
message: 'Proxy server restarted successfully',
|
|
31
|
-
status: newStatus
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
else {
|
|
36
|
-
return {
|
|
37
|
-
success: false,
|
|
38
|
-
error: 'Failed to restart proxy server. Check logs for details.'
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
catch (error) {
|
|
43
|
-
return {
|
|
44
|
-
success: false,
|
|
45
|
-
error: error instanceof Error ? error.message : 'Unknown error occurred while restarting proxy'
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
validateArgs(args) {
|
|
50
|
-
if (typeof args !== 'object' || args === null) {
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
const params = args;
|
|
54
|
-
if ('force' in params && typeof params.force !== 'boolean') {
|
|
55
|
-
return false;
|
|
56
|
-
}
|
|
57
|
-
return true;
|
|
58
|
-
}
|
|
59
|
-
getHelp() {
|
|
60
|
-
return `
|
|
61
|
-
restart - Restart the proxy server process
|
|
62
|
-
|
|
63
|
-
Usage:
|
|
64
|
-
cdp restart
|
|
65
|
-
cdp restart --force
|
|
66
|
-
|
|
67
|
-
Description:
|
|
68
|
-
Restarts the proxy server process. This will:
|
|
69
|
-
- Stop the current proxy server process
|
|
70
|
-
- Clear all stored console messages and network requests
|
|
71
|
-
- Start a new proxy server process
|
|
72
|
-
|
|
73
|
-
When to use:
|
|
74
|
-
Use this command when console or network command output becomes stale:
|
|
75
|
-
- Console messages are not refreshing or showing old data
|
|
76
|
-
- Network requests are not updating or displaying outdated information
|
|
77
|
-
- Logs appear to be stuck or not reflecting current browser state
|
|
78
|
-
Restarting the proxy will clear the message store and start fresh monitoring.
|
|
79
|
-
|
|
80
|
-
Options:
|
|
81
|
-
--force Force restart even if proxy is healthy
|
|
82
|
-
|
|
83
|
-
Examples:
|
|
84
|
-
# Restart the proxy server
|
|
85
|
-
cdp restart
|
|
86
|
-
|
|
87
|
-
# Force restart even if proxy is healthy
|
|
88
|
-
cdp restart --force
|
|
89
|
-
|
|
90
|
-
Note:
|
|
91
|
-
- Restarting the proxy will clear all stored console messages and network requests
|
|
92
|
-
- The proxy server must be restarted if logs are not refreshing properly
|
|
93
|
-
- After restart, you may need to reconnect to Chrome DevTools
|
|
94
|
-
`;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
exports.RestartProxyHandler = RestartProxyHandler;
|
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.ProxyManager = void 0;
|
|
40
|
-
const ProxyClient_1 = require("../client/ProxyClient");
|
|
41
|
-
const child_process_1 = require("child_process");
|
|
42
|
-
const path = __importStar(require("path"));
|
|
43
|
-
const fs = __importStar(require("fs"));
|
|
44
|
-
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
45
|
-
class ProxyManager {
|
|
46
|
-
constructor(config) {
|
|
47
|
-
this.isStarting = false;
|
|
48
|
-
this.lastHealthCheck = 0;
|
|
49
|
-
this.healthCheckCacheMs = 5000;
|
|
50
|
-
this.config = {
|
|
51
|
-
proxyUrl: 'http://localhost:9223',
|
|
52
|
-
maxRetries: 3,
|
|
53
|
-
retryDelayMs: 1000,
|
|
54
|
-
healthCheckTimeoutMs: 2000,
|
|
55
|
-
startupTimeoutMs: 10000,
|
|
56
|
-
enableLogging: false,
|
|
57
|
-
...config
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
static getInstance(config) {
|
|
61
|
-
if (!ProxyManager.instance) {
|
|
62
|
-
ProxyManager.instance = new ProxyManager(config);
|
|
63
|
-
}
|
|
64
|
-
return ProxyManager.instance;
|
|
65
|
-
}
|
|
66
|
-
async ensureProxyReady() {
|
|
67
|
-
try {
|
|
68
|
-
if (await this.isProxyHealthy()) {
|
|
69
|
-
return true;
|
|
70
|
-
}
|
|
71
|
-
return await this.startOrRestartProxy();
|
|
72
|
-
}
|
|
73
|
-
catch (error) {
|
|
74
|
-
this.log(`Failed to ensure proxy ready: ${error instanceof Error ? error.message : error}`);
|
|
75
|
-
return false;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
async isProxyHealthy() {
|
|
79
|
-
const now = Date.now();
|
|
80
|
-
if (now - this.lastHealthCheck < this.healthCheckCacheMs) {
|
|
81
|
-
return true;
|
|
82
|
-
}
|
|
83
|
-
try {
|
|
84
|
-
const controller = new AbortController();
|
|
85
|
-
const timeout = setTimeout(() => controller.abort(), this.config.healthCheckTimeoutMs);
|
|
86
|
-
const response = await (0, node_fetch_1.default)(`${this.config.proxyUrl}/api/health`, {
|
|
87
|
-
method: 'GET',
|
|
88
|
-
signal: controller.signal
|
|
89
|
-
});
|
|
90
|
-
clearTimeout(timeout);
|
|
91
|
-
if (response.ok) {
|
|
92
|
-
this.lastHealthCheck = now;
|
|
93
|
-
return true;
|
|
94
|
-
}
|
|
95
|
-
return false;
|
|
96
|
-
}
|
|
97
|
-
catch (error) {
|
|
98
|
-
return false;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
async restart() {
|
|
102
|
-
return await this.startOrRestartProxy();
|
|
103
|
-
}
|
|
104
|
-
async startOrRestartProxy() {
|
|
105
|
-
if (this.isStarting) {
|
|
106
|
-
return await this.waitForStartup();
|
|
107
|
-
}
|
|
108
|
-
this.isStarting = true;
|
|
109
|
-
try {
|
|
110
|
-
await this.killExistingProxy();
|
|
111
|
-
const success = await this.startProxyProcess();
|
|
112
|
-
if (success) {
|
|
113
|
-
this.log('Proxy server started successfully');
|
|
114
|
-
}
|
|
115
|
-
else {
|
|
116
|
-
this.log('Failed to start proxy server');
|
|
117
|
-
}
|
|
118
|
-
return success;
|
|
119
|
-
}
|
|
120
|
-
finally {
|
|
121
|
-
this.isStarting = false;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
async killExistingProxy() {
|
|
125
|
-
if (this.proxyProcess) {
|
|
126
|
-
try {
|
|
127
|
-
this.proxyProcess.kill('SIGTERM');
|
|
128
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
129
|
-
if (!this.proxyProcess.killed) {
|
|
130
|
-
this.proxyProcess.kill('SIGKILL');
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
catch (error) {
|
|
134
|
-
}
|
|
135
|
-
this.proxyProcess = undefined;
|
|
136
|
-
}
|
|
137
|
-
try {
|
|
138
|
-
const { spawn } = require('child_process');
|
|
139
|
-
const port = new URL(this.config.proxyUrl).port || '9223';
|
|
140
|
-
const lsof = spawn('lsof', ['-ti', `:${port}`]);
|
|
141
|
-
let pids = '';
|
|
142
|
-
lsof.stdout.on('data', (data) => {
|
|
143
|
-
pids += data.toString();
|
|
144
|
-
});
|
|
145
|
-
lsof.on('close', (code) => {
|
|
146
|
-
if (code === 0 && pids.trim()) {
|
|
147
|
-
const pidList = pids.trim().split('\n');
|
|
148
|
-
pidList.forEach(pid => {
|
|
149
|
-
try {
|
|
150
|
-
process.kill(parseInt(pid), 'SIGTERM');
|
|
151
|
-
}
|
|
152
|
-
catch (error) {
|
|
153
|
-
}
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
catch (error) {
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
async startProxyProcess() {
|
|
162
|
-
try {
|
|
163
|
-
const proxyServerPath = this.findProxyServerExecutable();
|
|
164
|
-
if (!proxyServerPath) {
|
|
165
|
-
this.log('Proxy server executable not found');
|
|
166
|
-
return false;
|
|
167
|
-
}
|
|
168
|
-
this.log(`Starting proxy server: ${proxyServerPath}`);
|
|
169
|
-
this.proxyProcess = (0, child_process_1.spawn)('node', [proxyServerPath], {
|
|
170
|
-
detached: true,
|
|
171
|
-
stdio: this.config.enableLogging ? 'inherit' : 'ignore'
|
|
172
|
-
});
|
|
173
|
-
this.proxyProcess.unref();
|
|
174
|
-
this.proxyProcess.on('error', (error) => {
|
|
175
|
-
this.log(`Proxy process error: ${error.message}`);
|
|
176
|
-
this.proxyProcess = undefined;
|
|
177
|
-
});
|
|
178
|
-
this.proxyProcess.on('exit', (code, signal) => {
|
|
179
|
-
this.log(`Proxy process exited with code ${code}, signal ${signal}`);
|
|
180
|
-
this.proxyProcess = undefined;
|
|
181
|
-
});
|
|
182
|
-
return await this.waitForProxyStartup();
|
|
183
|
-
}
|
|
184
|
-
catch (error) {
|
|
185
|
-
this.log(`Failed to start proxy process: ${error instanceof Error ? error.message : error}`);
|
|
186
|
-
return false;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
async waitForProxyStartup() {
|
|
190
|
-
const startTime = Date.now();
|
|
191
|
-
const maxWaitTime = this.config.startupTimeoutMs;
|
|
192
|
-
while (Date.now() - startTime < maxWaitTime) {
|
|
193
|
-
this.lastHealthCheck = 0;
|
|
194
|
-
if (await this.isProxyHealthy()) {
|
|
195
|
-
return true;
|
|
196
|
-
}
|
|
197
|
-
await new Promise(resolve => setTimeout(resolve, 500));
|
|
198
|
-
}
|
|
199
|
-
this.log(`Proxy startup timeout after ${maxWaitTime}ms`);
|
|
200
|
-
return false;
|
|
201
|
-
}
|
|
202
|
-
async waitForStartup() {
|
|
203
|
-
const maxWait = this.config.startupTimeoutMs;
|
|
204
|
-
const startTime = Date.now();
|
|
205
|
-
while (this.isStarting && Date.now() - startTime < maxWait) {
|
|
206
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
207
|
-
}
|
|
208
|
-
return !this.isStarting && await this.isProxyHealthy();
|
|
209
|
-
}
|
|
210
|
-
findProxyServerExecutable() {
|
|
211
|
-
const distPath = path.join(__dirname, '../../dist/proxy/index.js');
|
|
212
|
-
if (fs.existsSync(distPath)) {
|
|
213
|
-
return distPath;
|
|
214
|
-
}
|
|
215
|
-
const srcPath = path.join(__dirname, '../proxy/index.js');
|
|
216
|
-
if (fs.existsSync(srcPath)) {
|
|
217
|
-
return srcPath;
|
|
218
|
-
}
|
|
219
|
-
const currentDistPath = path.join(process.cwd(), 'dist/proxy/index.js');
|
|
220
|
-
if (fs.existsSync(currentDistPath)) {
|
|
221
|
-
return currentDistPath;
|
|
222
|
-
}
|
|
223
|
-
const nodeModulesPath = path.join(__dirname, '../../../node_modules/chrome-cdp-cli/dist/proxy/index.js');
|
|
224
|
-
if (fs.existsSync(nodeModulesPath)) {
|
|
225
|
-
return nodeModulesPath;
|
|
226
|
-
}
|
|
227
|
-
return null;
|
|
228
|
-
}
|
|
229
|
-
async getProxyClient() {
|
|
230
|
-
if (await this.ensureProxyReady()) {
|
|
231
|
-
return new ProxyClient_1.ProxyClient({
|
|
232
|
-
proxyUrl: this.config.proxyUrl,
|
|
233
|
-
fallbackToDirect: true,
|
|
234
|
-
startProxyIfNeeded: false
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
return null;
|
|
238
|
-
}
|
|
239
|
-
async shutdown() {
|
|
240
|
-
if (this.proxyProcess) {
|
|
241
|
-
try {
|
|
242
|
-
this.proxyProcess.kill('SIGTERM');
|
|
243
|
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
244
|
-
if (!this.proxyProcess.killed) {
|
|
245
|
-
this.proxyProcess.kill('SIGKILL');
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
catch (error) {
|
|
249
|
-
}
|
|
250
|
-
this.proxyProcess = undefined;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
setLogging(enabled) {
|
|
254
|
-
this.config.enableLogging = enabled;
|
|
255
|
-
}
|
|
256
|
-
log(message) {
|
|
257
|
-
if (this.config.enableLogging) {
|
|
258
|
-
console.log(`[ProxyManager] ${message}`);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
async getStatus() {
|
|
262
|
-
return {
|
|
263
|
-
isHealthy: await this.isProxyHealthy(),
|
|
264
|
-
isStarting: this.isStarting,
|
|
265
|
-
hasProcess: !!this.proxyProcess,
|
|
266
|
-
lastHealthCheck: this.lastHealthCheck
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
exports.ProxyManager = ProxyManager;
|