chrome-cdp-cli 2.1.1 → 2.1.3

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.
@@ -53,7 +53,7 @@ class Logger {
53
53
  maxFiles: 5,
54
54
  enableConsole: true,
55
55
  enableStructured: false,
56
- ...config
56
+ ...config,
57
57
  };
58
58
  if (this.config.file) {
59
59
  this.initializeLogFile();
@@ -74,13 +74,15 @@ class Logger {
74
74
  this.rotateLogFile();
75
75
  }
76
76
  }
77
- this.logFileHandle = fs.createWriteStream(this.config.file, { flags: 'a' });
78
- this.logFileHandle.on('error', (error) => {
79
- console.error('Log file write error:', error);
77
+ this.logFileHandle = fs.createWriteStream(this.config.file, {
78
+ flags: "a",
79
+ });
80
+ this.logFileHandle.on("error", (error) => {
81
+ console.error("Log file write error:", error);
80
82
  });
81
83
  }
82
84
  catch (error) {
83
- console.error('Failed to initialize log file:', error);
85
+ console.error("Failed to initialize log file:", error);
84
86
  }
85
87
  }
86
88
  rotateLogFile() {
@@ -104,25 +106,29 @@ class Logger {
104
106
  fs.renameSync(this.config.file, `${this.config.file}.1`);
105
107
  }
106
108
  this.currentFileSize = 0;
107
- this.logFileHandle = fs.createWriteStream(this.config.file, { flags: 'a' });
108
- this.logFileHandle.on('error', (error) => {
109
- console.error('Log file write error:', error);
109
+ this.logFileHandle = fs.createWriteStream(this.config.file, {
110
+ flags: "a",
111
+ });
112
+ this.logFileHandle.on("error", (error) => {
113
+ console.error("Log file write error:", error);
110
114
  });
111
115
  }
112
116
  catch (error) {
113
- console.error('Failed to rotate log file:', error);
117
+ console.error("Failed to rotate log file:", error);
114
118
  }
115
119
  }
116
120
  formatLogEntry(entry) {
117
121
  if (this.config.enableStructured) {
118
- return JSON.stringify(entry) + '\n';
122
+ return JSON.stringify(entry) + "\n";
119
123
  }
120
124
  else {
121
125
  const timestamp = entry.timestamp;
122
126
  const level = entry.level.padEnd(5);
123
- const component = entry.component ? `[${entry.component}]` : '';
124
- const connectionId = entry.connectionId ? `{conn:${entry.connectionId}}` : '';
125
- const clientId = entry.clientId ? `{client:${entry.clientId}}` : '';
127
+ const component = entry.component ? `[${entry.component}]` : "";
128
+ const connectionId = entry.connectionId
129
+ ? `{conn:${entry.connectionId}}`
130
+ : "";
131
+ const clientId = entry.clientId ? `{client:${entry.clientId}}` : "";
126
132
  let message = `${timestamp} ${level} ${component}${connectionId}${clientId} ${entry.message}`;
127
133
  if (entry.data) {
128
134
  message += ` | Data: ${JSON.stringify(entry.data)}`;
@@ -136,7 +142,7 @@ class Logger {
136
142
  if (entry.metrics) {
137
143
  message += ` | Metrics: ${JSON.stringify(entry.metrics)}`;
138
144
  }
139
- return message + '\n';
145
+ return message + "\n";
140
146
  }
141
147
  }
142
148
  writeLog(entry) {
@@ -145,24 +151,12 @@ class Logger {
145
151
  const consoleMessage = this.config.enableStructured
146
152
  ? formattedEntry.trim()
147
153
  : formattedEntry.trim();
148
- switch (entry.level) {
149
- case 'ERROR':
150
- console.error(consoleMessage);
151
- break;
152
- case 'WARN':
153
- console.warn(consoleMessage);
154
- break;
155
- case 'INFO':
156
- console.info(consoleMessage);
157
- break;
158
- case 'DEBUG':
159
- console.debug(consoleMessage);
160
- break;
161
- }
154
+ process.stderr.write(consoleMessage + "\n");
162
155
  }
163
156
  if (this.logFileHandle) {
164
- const entrySize = Buffer.byteLength(formattedEntry, 'utf8');
165
- if (this.currentFileSize + entrySize >= (this.config.maxFileSize || 10 * 1024 * 1024)) {
157
+ const entrySize = Buffer.byteLength(formattedEntry, "utf8");
158
+ if (this.currentFileSize + entrySize >=
159
+ (this.config.maxFileSize || 10 * 1024 * 1024)) {
166
160
  this.rotateLogFile();
167
161
  }
168
162
  this.logFileHandle.write(formattedEntry);
@@ -174,7 +168,7 @@ class Logger {
174
168
  timestamp: new Date().toISOString(),
175
169
  level,
176
170
  message,
177
- component: context?.component || this.config.component
171
+ component: context?.component || this.config.component,
178
172
  };
179
173
  if (context?.connectionId)
180
174
  entry.connectionId = context.connectionId;
@@ -188,7 +182,7 @@ class Logger {
188
182
  entry.error = {
189
183
  message: error.message,
190
184
  stack: error.stack,
191
- code: error.code
185
+ code: error.code,
192
186
  };
193
187
  }
194
188
  return entry;
@@ -214,57 +208,59 @@ class Logger {
214
208
  data = errorOrData;
215
209
  error = undefined;
216
210
  }
217
- const entry = this.createLogEntry('ERROR', message, data, error, context);
211
+ const entry = this.createLogEntry("ERROR", message, data, error, context);
218
212
  this.writeLog(entry);
219
213
  }
220
214
  }
221
215
  warn(message, data, context) {
222
216
  if (this.config.level >= LogLevel.WARN) {
223
- const entry = this.createLogEntry('WARN', message, data, undefined, context);
217
+ const entry = this.createLogEntry("WARN", message, data, undefined, context);
224
218
  this.writeLog(entry);
225
219
  }
226
220
  }
227
221
  info(message, data, context) {
228
222
  if (this.config.level >= LogLevel.INFO) {
229
- const entry = this.createLogEntry('INFO', message, data, undefined, context);
223
+ const entry = this.createLogEntry("INFO", message, data, undefined, context);
230
224
  this.writeLog(entry);
231
225
  }
232
226
  }
233
227
  debug(message, data, context) {
234
228
  if (this.config.level >= LogLevel.DEBUG) {
235
- const entry = this.createLogEntry('DEBUG', message, data, undefined, context);
229
+ const entry = this.createLogEntry("DEBUG", message, data, undefined, context);
236
230
  this.writeLog(entry);
237
231
  }
238
232
  }
239
233
  logServerEvent(event, message, data, error) {
240
- const metrics = event === 'startup' || event === 'shutdown' ? {
241
- memoryUsage: process.memoryUsage()
242
- } : undefined;
243
- const entry = this.createLogEntry(event === 'error' ? 'ERROR' : 'INFO', `[SERVER-${event.toUpperCase()}] ${message}`, data, error, { component: 'ProxyServer', metrics });
234
+ const metrics = event === "startup" || event === "shutdown"
235
+ ? {
236
+ memoryUsage: process.memoryUsage(),
237
+ }
238
+ : undefined;
239
+ const entry = this.createLogEntry(event === "error" ? "ERROR" : "INFO", `[SERVER-${event.toUpperCase()}] ${message}`, data, error, { component: "ProxyServer", metrics });
244
240
  this.writeLog(entry);
245
241
  }
246
242
  logConnectionEvent(event, connectionId, message, data, error) {
247
- const entry = this.createLogEntry(error ? 'ERROR' : 'INFO', `[CONNECTION-${event.toUpperCase()}] ${message}`, data, error, { component: 'ConnectionPool', connectionId });
243
+ const entry = this.createLogEntry(error ? "ERROR" : "INFO", `[CONNECTION-${event.toUpperCase()}] ${message}`, data, error, { component: "ConnectionPool", connectionId });
248
244
  this.writeLog(entry);
249
245
  }
250
246
  logClientEvent(event, clientId, message, data, error) {
251
- const entry = this.createLogEntry(error ? 'ERROR' : 'INFO', `[CLIENT-${event.toUpperCase()}] ${message}`, data, error, { component: 'WSProxy', clientId });
247
+ const entry = this.createLogEntry(error ? "ERROR" : "INFO", `[CLIENT-${event.toUpperCase()}] ${message}`, data, error, { component: "WSProxy", clientId });
252
248
  this.writeLog(entry);
253
249
  }
254
250
  logMemoryEvent(event, message, metrics) {
255
- const entry = this.createLogEntry('INFO', `[MEMORY-${event.toUpperCase()}] ${message}`, undefined, undefined, { component: 'MemoryManager', metrics });
251
+ const entry = this.createLogEntry("INFO", `[MEMORY-${event.toUpperCase()}] ${message}`, undefined, undefined, { component: "MemoryManager", metrics });
256
252
  this.writeLog(entry);
257
253
  }
258
254
  logAPIEvent(method, path, statusCode, duration, clientIP, error) {
259
- const entry = this.createLogEntry(error ? 'ERROR' : 'INFO', `[API] ${method} ${path} - ${statusCode} (${duration}ms)`, { clientIP, duration, statusCode }, error, { component: 'ProxyAPI' });
255
+ const entry = this.createLogEntry(error ? "ERROR" : "INFO", `[API] ${method} ${path} - ${statusCode} (${duration}ms)`, { clientIP, duration, statusCode }, error, { component: "ProxyAPI" });
260
256
  this.writeLog(entry);
261
257
  }
262
258
  logSecurityEvent(event, message, data, error) {
263
- const entry = this.createLogEntry(error ? 'ERROR' : 'WARN', `[SECURITY-${event.toUpperCase()}] ${message}`, data, error, { component: 'SecurityManager' });
259
+ const entry = this.createLogEntry(error ? "ERROR" : "WARN", `[SECURITY-${event.toUpperCase()}] ${message}`, data, error, { component: "SecurityManager" });
264
260
  this.writeLog(entry);
265
261
  }
266
262
  logPerformanceMetrics(component, metrics) {
267
- const entry = this.createLogEntry('INFO', `[PERFORMANCE] ${component} metrics`, undefined, undefined, { component, metrics });
263
+ const entry = this.createLogEntry("INFO", `[PERFORMANCE] ${component} metrics`, undefined, undefined, { component, metrics });
268
264
  this.writeLog(entry);
269
265
  }
270
266
  close() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chrome-cdp-cli",
3
- "version": "2.1.1",
3
+ "version": "2.1.3",
4
4
  "description": "Browser automation CLI via Chrome DevTools Protocol. Designed for developers and AI assistants - combines dedicated commands for common tasks with flexible JavaScript execution for complex scenarios. Features: element interaction, screenshots, DOM snapshots, console/network monitoring. Built-in IDE integration for Cursor and Claude.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -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;