chrome-cdp-cli 2.0.4 → 2.1.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.
@@ -30,8 +30,10 @@ class CommandRouter {
30
30
  if (!this.client) {
31
31
  return {
32
32
  success: false,
33
- error: 'Not connected to Chrome. Use "connect" command first.',
34
- exitCode: ExitCode.CONNECTION_ERROR
33
+ error: "Not connected to Chrome.\n" +
34
+ "Launch Chrome with: google-chrome --remote-debugging-port=9222 --user-data-dir=/tmp/chrome-debug\n" +
35
+ "From Chrome 136, --user-data-dir is required. See: https://developer.chrome.com/blog/remote-debugging-port",
36
+ exitCode: ExitCode.CONNECTION_ERROR,
35
37
  };
36
38
  }
37
39
  const handler = this.registry.get(command.name);
@@ -39,14 +41,14 @@ class CommandRouter {
39
41
  return {
40
42
  success: false,
41
43
  error: `Unknown command: ${command.name}. Use "help" to see available commands.`,
42
- exitCode: ExitCode.INVALID_COMMAND
44
+ exitCode: ExitCode.INVALID_COMMAND,
43
45
  };
44
46
  }
45
47
  if (handler.validateArgs && !handler.validateArgs(command.args)) {
46
48
  return {
47
49
  success: false,
48
50
  error: `Invalid arguments for command "${command.name}". Use "help ${command.name}" for usage information.`,
49
- exitCode: ExitCode.VALIDATION_ERROR
51
+ exitCode: ExitCode.VALIDATION_ERROR,
50
52
  };
51
53
  }
52
54
  if (command.config.verbose) {
@@ -66,36 +68,36 @@ class CommandRouter {
66
68
  return {
67
69
  success: false,
68
70
  error: errorMessage,
69
- exitCode: this.getExitCodeForError(error)
71
+ exitCode: this.getExitCodeForError(error),
70
72
  };
71
73
  }
72
74
  }
73
75
  isSpecialCommand(commandName) {
74
76
  const specialCommands = [
75
- 'help',
76
- 'connect',
77
- 'disconnect',
78
- 'install_cursor_command',
79
- 'install_claude_skill'
77
+ "help",
78
+ "connect",
79
+ "disconnect",
80
+ "install_cursor_command",
81
+ "install_claude_skill",
80
82
  ];
81
83
  return specialCommands.includes(commandName);
82
84
  }
83
85
  async executeSpecialCommand(command) {
84
86
  switch (command.name) {
85
- case 'help':
87
+ case "help":
86
88
  return this.executeHelpCommand(command);
87
- case 'connect':
89
+ case "connect":
88
90
  return this.executeConnectCommand(command);
89
- case 'disconnect':
91
+ case "disconnect":
90
92
  return this.executeDisconnectCommand();
91
- case 'install_cursor_command':
92
- case 'install_claude_skill': {
93
+ case "install_cursor_command":
94
+ case "install_claude_skill": {
93
95
  const handler = this.registry.get(command.name);
94
96
  if (!handler) {
95
97
  return {
96
98
  success: false,
97
99
  error: `Handler not found for command: ${command.name}`,
98
- exitCode: ExitCode.INVALID_COMMAND
100
+ exitCode: ExitCode.INVALID_COMMAND,
99
101
  };
100
102
  }
101
103
  return await handler.execute(null, command.args);
@@ -104,26 +106,28 @@ class CommandRouter {
104
106
  return {
105
107
  success: false,
106
108
  error: `Unknown special command: ${command.name}`,
107
- exitCode: ExitCode.INVALID_COMMAND
109
+ exitCode: ExitCode.INVALID_COMMAND,
108
110
  };
109
111
  }
110
112
  }
111
113
  executeHelpCommand(command) {
112
114
  const commandName = command.args.command;
113
115
  if (commandName) {
114
- const normalizedCommandName = commandName.replace(/-/g, '_');
116
+ const normalizedCommandName = commandName.replace(/-/g, "_");
115
117
  const handler = this.registry.get(normalizedCommandName);
116
118
  if (!handler) {
117
119
  return {
118
120
  success: false,
119
121
  error: `Unknown command: ${commandName}`,
120
- exitCode: ExitCode.INVALID_COMMAND
122
+ exitCode: ExitCode.INVALID_COMMAND,
121
123
  };
122
124
  }
123
- const helpText = handler.getHelp ? handler.getHelp() : `No help available for command: ${commandName}`;
125
+ const helpText = handler.getHelp
126
+ ? handler.getHelp()
127
+ : `No help available for command: ${commandName}`;
124
128
  return {
125
129
  success: true,
126
- data: helpText
130
+ data: helpText,
127
131
  };
128
132
  }
129
133
  else {
@@ -131,7 +135,7 @@ class CommandRouter {
131
135
  const helpText = this.generateGeneralHelp(commands);
132
136
  return {
133
137
  success: true,
134
- data: helpText
138
+ data: helpText,
135
139
  };
136
140
  }
137
141
  }
@@ -139,14 +143,14 @@ class CommandRouter {
139
143
  try {
140
144
  return {
141
145
  success: true,
142
- data: `Connected to Chrome at ${command.config.host}:${command.config.port}`
146
+ data: `Connected to Chrome at ${command.config.host}:${command.config.port}`,
143
147
  };
144
148
  }
145
149
  catch (error) {
146
150
  return {
147
151
  success: false,
148
152
  error: `Failed to connect: ${error instanceof Error ? error.message : String(error)}`,
149
- exitCode: ExitCode.CONNECTION_ERROR
153
+ exitCode: ExitCode.CONNECTION_ERROR,
150
154
  };
151
155
  }
152
156
  }
@@ -158,14 +162,14 @@ class CommandRouter {
158
162
  }
159
163
  return {
160
164
  success: true,
161
- data: 'Disconnected from Chrome'
165
+ data: "Disconnected from Chrome",
162
166
  };
163
167
  }
164
168
  catch (error) {
165
169
  return {
166
170
  success: false,
167
171
  error: `Failed to disconnect: ${error instanceof Error ? error.message : String(error)}`,
168
- exitCode: ExitCode.CONNECTION_ERROR
172
+ exitCode: ExitCode.CONNECTION_ERROR,
169
173
  };
170
174
  }
171
175
  }
@@ -178,22 +182,41 @@ class CommandRouter {
178
182
  this.logger.setLevel(2);
179
183
  }
180
184
  this.logger.debug(`CommandRouter.executeWithTimeout called for command: ${command.name}, timeout: ${timeout}ms`);
185
+ this.logger.debug(`Starting handler execution for: ${command.name}`);
186
+ const executionPromise = handler.execute(this.client, command.args);
187
+ const longRunningCommands = ["log", "console", "network"];
188
+ const isLongRunning = longRunningCommands.includes(command.name) ||
189
+ command.args?.follow ||
190
+ command.args?.f;
191
+ if (isLongRunning) {
192
+ this.logger.debug(`Command ${command.name} is long-running, skipping timeout`);
193
+ try {
194
+ const result = await executionPromise;
195
+ if (result?.isLongRunning) {
196
+ return new Promise(() => {
197
+ });
198
+ }
199
+ return result;
200
+ }
201
+ catch (error) {
202
+ this.logger.debug(`Command execution error for: ${command.name}:`, error);
203
+ throw error;
204
+ }
205
+ }
181
206
  const timeoutPromise = new Promise((_, reject) => {
182
207
  setTimeout(() => {
183
208
  this.logger.debug(`Command timeout reached for: ${command.name} after ${timeout}ms`);
184
209
  reject(new Error(`Command timeout after ${timeout}ms`));
185
210
  }, timeout);
186
211
  });
187
- this.logger.debug(`Starting handler execution for: ${command.name}`);
188
- const executionPromise = handler.execute(this.client, command.args);
189
212
  try {
190
213
  const result = await Promise.race([executionPromise, timeoutPromise]);
191
214
  this.logger.debug(`Command completed successfully for: ${command.name}`);
192
- if (!result || typeof result !== 'object') {
215
+ if (!result || typeof result !== "object") {
193
216
  return {
194
217
  success: false,
195
- error: 'Invalid command result format',
196
- exitCode: ExitCode.GENERAL_ERROR
218
+ error: "Invalid command result format",
219
+ exitCode: ExitCode.GENERAL_ERROR,
197
220
  };
198
221
  }
199
222
  if (result.success && !result.exitCode) {
@@ -203,11 +226,11 @@ class CommandRouter {
203
226
  }
204
227
  catch (error) {
205
228
  this.logger.debug(`Command execution error for: ${command.name}:`, error);
206
- if (error instanceof Error && error.message.includes('timeout')) {
229
+ if (error instanceof Error && error.message.includes("timeout")) {
207
230
  return {
208
231
  success: false,
209
232
  error: error.message,
210
- exitCode: ExitCode.TIMEOUT_ERROR
233
+ exitCode: ExitCode.TIMEOUT_ERROR,
211
234
  };
212
235
  }
213
236
  throw error;
@@ -215,9 +238,9 @@ class CommandRouter {
215
238
  }
216
239
  generateGeneralHelp(commands) {
217
240
  return `
218
- Chrome DevTools CLI - Command-line tool for controlling Chrome browser
241
+ CDP - Chrome DevTools Protocol CLI
219
242
 
220
- Usage: chrome-cdp-cli [options] <command> [command-options]
243
+ Usage: cdp [options] <command> [command-options]
221
244
 
222
245
  Global Options:
223
246
  -h, --host <host> Chrome host address (default: localhost)
@@ -231,64 +254,65 @@ Global Options:
231
254
  -V, --version Show version number
232
255
 
233
256
  Available Commands:
234
- ${commands.map(cmd => ` ${cmd.padEnd(20)} - ${this.getCommandDescription(cmd)}`).join('\n')}
257
+ ${commands.map((cmd) => ` ${cmd.padEnd(20)} - ${this.getCommandDescription(cmd)}`).join("\n")}
235
258
 
236
259
  Examples:
237
- chrome-cdp-cli eval "document.title"
238
- chrome-cdp-cli eval --file script.js
239
- chrome-cdp-cli screenshot --filename page.png
240
- chrome-cdp-cli snapshot --format html --filename dom.html
241
- chrome-cdp-cli help <command>
260
+ cdp eval "document.title"
261
+ cdp eval --file script.js
262
+ cdp screenshot --filename page.png
263
+ cdp snapshot --filename dom.html
264
+ cdp log -f
265
+ cdp network --urlPattern "/api"
266
+ cdp help <command>
242
267
 
243
268
  For more information about a specific command, use:
244
- chrome-cdp-cli help <command>
269
+ cdp help <command>
245
270
  `;
246
271
  }
247
272
  getCommandDescription(commandName) {
248
273
  const descriptions = {
249
- 'connect': 'Connect to Chrome instance',
250
- 'disconnect': 'Disconnect from Chrome instance',
251
- 'navigate': 'Navigate to URL',
252
- 'new-page': 'Create new page/tab',
253
- 'close-page': 'Close current or specified page',
254
- 'list-pages': 'List all open pages',
255
- 'select-page': 'Select/focus page',
256
- 'resize-page': 'Resize browser viewport',
257
- 'eval': 'Execute JavaScript code',
258
- 'click': 'Click element',
259
- 'fill': 'Fill form field',
260
- 'fill_form': 'Fill multiple form fields in batch',
261
- 'hover': 'Hover over element',
262
- 'drag': 'Perform drag and drop operations',
263
- 'press_key': 'Simulate keyboard input with modifiers',
264
- 'upload_file': 'Upload files to file input elements',
265
- 'wait_for': 'Wait for elements to appear or meet conditions',
266
- 'handle_dialog': 'Handle browser dialogs (alert, confirm, prompt)',
267
- 'screenshot': 'Capture page screenshot',
268
- 'snapshot': 'Capture DOM snapshot with structure and styles',
269
- 'console-messages': 'Get console messages',
270
- 'network-requests': 'Get network requests',
271
- 'console': 'List console messages',
272
- 'network': 'List network requests',
273
- 'install_cursor_command': 'Install Cursor IDE commands for Chrome automation',
274
- 'install_claude_skill': 'Install Claude Code skill for Chrome automation',
275
- 'help': 'Show help information'
274
+ connect: "Connect to Chrome instance",
275
+ disconnect: "Disconnect from Chrome instance",
276
+ navigate: "Navigate to URL",
277
+ "new-page": "Create new page/tab",
278
+ "close-page": "Close current or specified page",
279
+ "list-pages": "List all open pages",
280
+ "select-page": "Select/focus page",
281
+ "resize-page": "Resize browser viewport",
282
+ eval: "Execute JavaScript code",
283
+ click: "Click element",
284
+ fill: "Fill form field",
285
+ fill_form: "Fill multiple form fields in batch",
286
+ hover: "Hover over element",
287
+ drag: "Perform drag and drop operations",
288
+ press_key: "Simulate keyboard input with modifiers",
289
+ upload_file: "Upload files to file input elements",
290
+ wait_for: "Wait for elements to appear or meet conditions",
291
+ handle_dialog: "Handle browser dialogs (alert, confirm, prompt)",
292
+ screenshot: "Capture page screenshot",
293
+ snapshot: "Capture DOM snapshot with structure and styles",
294
+ log: "Follow console messages in real-time",
295
+ console: "Follow console messages (alias for log)",
296
+ network: "Follow network requests in real-time",
297
+ install_cursor_command: "Install Cursor IDE commands for Chrome automation",
298
+ install_claude_skill: "Install Claude Code skill for Chrome automation",
299
+ help: "Show help information",
276
300
  };
277
- return descriptions[commandName] || 'No description available';
301
+ return descriptions[commandName] || "No description available";
278
302
  }
279
303
  getExitCodeForError(error) {
280
304
  if (error instanceof Error) {
281
305
  const message = error.message.toLowerCase();
282
- if (message.includes('timeout')) {
306
+ if (message.includes("timeout")) {
283
307
  return ExitCode.TIMEOUT_ERROR;
284
308
  }
285
- if (message.includes('connection') || message.includes('connect')) {
309
+ if (message.includes("connection") || message.includes("connect")) {
286
310
  return ExitCode.CONNECTION_ERROR;
287
311
  }
288
- if (message.includes('file') || message.includes('path')) {
312
+ if (message.includes("file") || message.includes("path")) {
289
313
  return ExitCode.FILE_ERROR;
290
314
  }
291
- if (message.includes('invalid') || message.includes('validation')) {
315
+ if (message.includes("invalid") || message.includes("validation")) {
292
316
  return ExitCode.VALIDATION_ERROR;
293
317
  }
294
318
  }