chrome-cdp-cli 2.0.5 → 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.
@@ -7,7 +7,6 @@ const ConnectionManager_1 = require("../connection/ConnectionManager");
7
7
  const handlers_1 = require("../handlers");
8
8
  const logger_1 = require("../utils/logger");
9
9
  const CommandRouter_1 = require("./CommandRouter");
10
- const ProxyManager_1 = require("../proxy/ProxyManager");
11
10
  const OutputManager_1 = require("./OutputManager");
12
11
  class CLIApplication {
13
12
  constructor() {
@@ -16,7 +15,6 @@ class CLIApplication {
16
15
  this.connectionManager = new ConnectionManager_1.ConnectionManager();
17
16
  this.outputManager = new OutputManager_1.OutputManager();
18
17
  this.logger = new logger_1.Logger();
19
- this.proxyManager = ProxyManager_1.ProxyManager.getInstance();
20
18
  this.setupHandlers();
21
19
  }
22
20
  setupHandlers() {
@@ -36,14 +34,13 @@ class CLIApplication {
36
34
  this.cli.registerHandler(new handlers_1.UploadFileHandler());
37
35
  this.cli.registerHandler(new handlers_1.WaitForHandler());
38
36
  this.cli.registerHandler(new handlers_1.HandleDialogHandler());
39
- this.cli.registerHandler(new handlers_1.RestartProxyHandler());
40
37
  }
41
38
  configureDebugMode(debug) {
42
39
  const registry = this.cli.getRegistry();
43
40
  const handlerNames = registry.getCommandNames();
44
41
  for (const handlerName of handlerNames) {
45
42
  const handler = registry.get(handlerName);
46
- if (handler && typeof handler.setDebug === 'function') {
43
+ if (handler && typeof handler.setDebug === "function") {
47
44
  handler.setDebug(debug);
48
45
  }
49
46
  }
@@ -56,10 +53,10 @@ class CLIApplication {
56
53
  }
57
54
  catch (parseError) {
58
55
  if (parseError instanceof Error) {
59
- if (parseError.message === 'VERSION_COMMAND_EXECUTED') {
56
+ if (parseError.message === "VERSION_COMMAND_EXECUTED") {
60
57
  return CommandRouter_1.ExitCode.SUCCESS;
61
58
  }
62
- if (parseError.message === 'HELP_COMMAND_EXECUTED') {
59
+ if (parseError.message === "HELP_COMMAND_EXECUTED") {
63
60
  return CommandRouter_1.ExitCode.SUCCESS;
64
61
  }
65
62
  }
@@ -85,73 +82,147 @@ class CLIApplication {
85
82
  this.logger.setLevel(2);
86
83
  }
87
84
  this.configureDebugMode(command.config.debug);
88
- this.logger.debug('CLIApplication.run called with argv:', argv);
89
- this.logger.debug('Parsed command:', command);
90
- if (command.config.verbose) {
91
- this.proxyManager.setLogging(true);
92
- }
93
- this.logger.debug('Ensuring proxy is ready...');
94
- await this.ensureProxyReady();
85
+ this.logger.debug("CLIApplication.run called with argv:", argv);
86
+ this.logger.debug("Parsed command:", command);
95
87
  if (this.needsConnection(command.name)) {
96
- this.logger.debug('Command needs connection, ensuring connection...');
88
+ this.logger.debug("Command needs connection, ensuring connection...");
97
89
  try {
98
90
  await this.ensureConnection(command);
99
91
  }
100
92
  catch (connectionError) {
101
- this.logger.debug('Connection failed, will be handled by command router:', connectionError);
93
+ const msg = connectionError instanceof Error
94
+ ? connectionError.message
95
+ : String(connectionError);
96
+ if (msg === "Cancelled") {
97
+ return CommandRouter_1.ExitCode.SUCCESS;
98
+ }
99
+ process.stderr.write(`\nError: ${msg}\n`);
100
+ process.stderr.write("\nTip: Launch Chrome with remote debugging enabled:\n" +
101
+ " chrome --remote-debugging-port=9222 --user-data-dir=/tmp/chrome-debug\n\n" +
102
+ "From Chrome 136, --user-data-dir is required. See:\n" +
103
+ " https://developer.chrome.com/blog/remote-debugging-port\n\n");
104
+ return CommandRouter_1.ExitCode.CONNECTION_ERROR;
102
105
  }
103
106
  }
104
- this.logger.debug('Executing command via CLI interface...');
107
+ this.logger.debug("Executing command via CLI interface...");
105
108
  const result = await this.cli.execute(command);
106
- this.logger.debug('Command execution result:', result);
109
+ this.logger.debug("Command execution result:", result);
107
110
  this.outputResult(result, command);
108
- return result.exitCode || (result.success ? CommandRouter_1.ExitCode.SUCCESS : CommandRouter_1.ExitCode.GENERAL_ERROR);
111
+ return (result.exitCode ||
112
+ (result.success ? CommandRouter_1.ExitCode.SUCCESS : CommandRouter_1.ExitCode.GENERAL_ERROR));
109
113
  }
110
114
  catch (error) {
111
- this.logger.debug('Error in CLIApplication.run:', error);
115
+ this.logger.debug("Error in CLIApplication.run:", error);
112
116
  const errorMessage = error instanceof Error ? error.message : String(error);
113
- console.error(this.formatError(errorMessage, { outputFormat: 'text', verbose: false, quiet: false, debug: false, host: 'localhost', port: 9222, timeout: 30000 }));
117
+ console.error(this.formatError(errorMessage, {
118
+ outputFormat: "text",
119
+ verbose: false,
120
+ quiet: false,
121
+ debug: false,
122
+ host: "localhost",
123
+ port: 9222,
124
+ timeout: 30000,
125
+ }));
114
126
  return CommandRouter_1.ExitCode.GENERAL_ERROR;
115
127
  }
116
128
  }
117
- async ensureProxyReady() {
118
- try {
119
- const isReady = await this.proxyManager.ensureProxyReady();
120
- if (!isReady) {
121
- }
122
- }
123
- catch (error) {
124
- }
125
- }
126
129
  needsConnection(commandName) {
127
130
  const noConnectionCommands = [
128
- 'help',
129
- 'connect',
130
- 'disconnect',
131
- 'install_cursor_command',
132
- 'install_claude_skill'
131
+ "help",
132
+ "connect",
133
+ "disconnect",
134
+ "install_cursor_command",
135
+ "install_claude_skill",
133
136
  ];
134
137
  return !noConnectionCommands.includes(commandName);
135
138
  }
136
139
  isDevToolsWindow(target) {
137
140
  const url = target.url.toLowerCase();
138
141
  const title = target.title.toLowerCase();
139
- return url.startsWith('chrome-devtools://') ||
140
- url.startsWith('devtools://') ||
141
- title.includes('devtools') ||
142
- title.includes('chrome devtools');
142
+ return (url.startsWith("chrome-devtools://") ||
143
+ url.startsWith("devtools://") ||
144
+ title.includes("devtools") ||
145
+ title.includes("chrome devtools"));
143
146
  }
144
- displayAvailableTargets(targets) {
145
- console.log('\nAvailable Chrome pages (excluding DevTools windows):');
146
- targets.forEach((target, index) => {
147
- const displayUrl = target.url.length > 60 ? target.url.substring(0, 57) + '...' : target.url;
148
- console.log(` [${index + 1}] ${target.title || '(Untitled)'}`);
149
- console.log(` ${displayUrl}`);
147
+ async selectTargetInteractive(targets) {
148
+ if (!process.stdin.isTTY || !process.stderr.isTTY) {
149
+ process.stderr.write("\nAvailable Chrome pages (excluding DevTools windows):\n");
150
+ targets.forEach((t, i) => {
151
+ const url = t.url.length > 70 ? t.url.substring(0, 67) + "..." : t.url;
152
+ process.stderr.write(` [${i + 1}] ${t.title || "(Untitled)"}\n ${url}\n`);
153
+ });
154
+ process.stderr.write(`\nTip: re-run with cdp -i <number> <command>\n\n`);
155
+ throw new Error(`Multiple Chrome pages found (${targets.length}). Use -i <number> to pick one.`);
156
+ }
157
+ return new Promise((resolve, reject) => {
158
+ let cursor = 0;
159
+ const RESET = "\x1b[0m";
160
+ const BOLD = "\x1b[1m";
161
+ const CYAN = "\x1b[36m";
162
+ const DIM = "\x1b[2m";
163
+ const CLEAR_LINE = "\x1b[2K\x1b[G";
164
+ const SAVE = "\x1b[s";
165
+ const RESTORE = "\x1b[u";
166
+ const truncate = (s, max) => s.length > max ? s.substring(0, max - 1) + "…" : s;
167
+ const render = (first) => {
168
+ if (first) {
169
+ process.stderr.write(SAVE);
170
+ }
171
+ else {
172
+ process.stderr.write(RESTORE);
173
+ }
174
+ process.stderr.write(`${CLEAR_LINE}${BOLD}Select a Chrome page${RESET} (↑↓ navigate, Enter select, q quit)\n`);
175
+ process.stderr.write(`${CLEAR_LINE}${"─".repeat(54)}\n`);
176
+ targets.forEach((t, i) => {
177
+ const num = `[${i + 1}]`;
178
+ const rawTitle = t.title || "(Untitled)";
179
+ const title = truncate(rawTitle, 52);
180
+ const url = truncate(t.url, 60);
181
+ const selected = i === cursor;
182
+ if (selected) {
183
+ process.stderr.write(`${CLEAR_LINE}${CYAN}${BOLD} ❯ ${num} ${title}${RESET}\n`);
184
+ process.stderr.write(`${CLEAR_LINE}${CYAN} ${url}${RESET}\n`);
185
+ }
186
+ else {
187
+ process.stderr.write(`${CLEAR_LINE} ${num} ${title}\n`);
188
+ process.stderr.write(`${CLEAR_LINE}${DIM} ${url}${RESET}\n`);
189
+ }
190
+ });
191
+ process.stderr.write(`${CLEAR_LINE}${"─".repeat(54)}\n`);
192
+ process.stderr.write(`${CLEAR_LINE}${DIM}Tip: skip this prompt with cdp -i <number> <command>${RESET}\n` +
193
+ `${CLEAR_LINE}${DIM} or close other tabs until only one remains.${RESET}\n`);
194
+ };
195
+ render(true);
196
+ process.stdin.setRawMode(true);
197
+ process.stdin.resume();
198
+ process.stdin.setEncoding("utf8");
199
+ const onKey = (key) => {
200
+ if (key === "\x1b[A") {
201
+ cursor = (cursor - 1 + targets.length) % targets.length;
202
+ render(false);
203
+ }
204
+ else if (key === "\x1b[B") {
205
+ cursor = (cursor + 1) % targets.length;
206
+ render(false);
207
+ }
208
+ else if (key === "\r" || key === "\n") {
209
+ cleanup();
210
+ process.stderr.write("\n");
211
+ resolve(targets[cursor]);
212
+ }
213
+ else if (key === "q" || key === "\x03" || key === "\x1b") {
214
+ cleanup();
215
+ process.stderr.write("\n");
216
+ reject(new Error("Cancelled"));
217
+ }
218
+ };
219
+ const cleanup = () => {
220
+ process.stdin.removeListener("data", onKey);
221
+ process.stdin.setRawMode(false);
222
+ process.stdin.pause();
223
+ };
224
+ process.stdin.on("data", onKey);
150
225
  });
151
- console.log('\nOptions:');
152
- console.log(' 1. Use --target-index <number> to select a specific page');
153
- console.log(' Example: chrome-cdp-cli --target-index 1 eval "document.title"');
154
- console.log(' 2. Close other pages until only one page remains\n');
155
226
  }
156
227
  async ensureConnection(command) {
157
228
  if (this.client) {
@@ -161,47 +232,55 @@ class CLIApplication {
161
232
  const targets = await this.connectionManager.discoverTargets(command.config.host, command.config.port);
162
233
  if (targets.length === 0) {
163
234
  throw new Error(`No Chrome targets found at ${command.config.host}:${command.config.port}. ` +
164
- 'Make sure Chrome is running with --remote-debugging-port=9222');
235
+ "Make sure Chrome is running with --remote-debugging-port=9222");
165
236
  }
166
- const pageTargets = targets.filter(target => target.type === 'page');
167
- const nonDevToolsTargets = pageTargets.filter(target => !this.isDevToolsWindow(target));
237
+ const pageTargets = targets.filter((target) => target.type === "page");
238
+ const nonDevToolsTargets = pageTargets.filter((target) => !this.isDevToolsWindow(target));
168
239
  if (nonDevToolsTargets.length === 0) {
169
- throw new Error('No page targets available (excluding DevTools windows). Open a tab in Chrome.');
240
+ throw new Error("No page targets available (excluding DevTools windows). Open a tab in Chrome.");
170
241
  }
171
242
  let selectedTarget;
172
243
  if (command.config.targetIndex !== undefined) {
173
244
  const index = command.config.targetIndex - 1;
174
245
  if (index < 0 || index >= nonDevToolsTargets.length) {
175
- this.displayAvailableTargets(nonDevToolsTargets);
176
- throw new Error(`Invalid target index: ${command.config.targetIndex}. ` +
177
- `Please choose a number between 1 and ${nonDevToolsTargets.length}.`);
246
+ process.stderr.write("\nAvailable Chrome pages:\n");
247
+ nonDevToolsTargets.forEach((t, i) => {
248
+ process.stderr.write(` [${i + 1}] ${t.title || "(Untitled)"}\n`);
249
+ });
250
+ throw new Error(`Invalid -i value: ${command.config.targetIndex}. ` +
251
+ `Choose a number between 1 and ${nonDevToolsTargets.length}.`);
178
252
  }
179
253
  selectedTarget = nonDevToolsTargets[index];
180
254
  }
255
+ else if (nonDevToolsTargets.length === 1) {
256
+ selectedTarget = nonDevToolsTargets[0];
257
+ }
181
258
  else {
182
- if (nonDevToolsTargets.length === 1) {
183
- selectedTarget = nonDevToolsTargets[0];
184
- }
185
- else {
186
- this.displayAvailableTargets(nonDevToolsTargets);
187
- throw new Error(`Multiple Chrome pages found (${nonDevToolsTargets.length}). ` +
188
- 'Please specify --target-index <number> to select a page, or close other pages until only one remains.');
189
- }
259
+ selectedTarget = await this.selectTargetInteractive(nonDevToolsTargets);
190
260
  }
191
- this.client = await this.connectionManager.connectToTarget(selectedTarget);
261
+ this.client = (await this.connectionManager.connectToTarget(selectedTarget));
192
262
  this.cli.setClient(this.client);
193
263
  if (command.config.verbose) {
194
264
  this.logger.info(`Connected to Chrome target: ${selectedTarget.title} (${selectedTarget.url})`);
195
265
  }
196
266
  }
197
267
  catch (error) {
198
- throw new Error(`Failed to connect to Chrome: ${error instanceof Error ? error.message : String(error)}`);
268
+ if (error instanceof Error) {
269
+ throw error;
270
+ }
271
+ throw new Error(String(error));
199
272
  }
200
273
  }
201
274
  outputResult(result, command) {
275
+ if (result._rawOutput === true) {
276
+ return;
277
+ }
202
278
  if (command.config.quiet && result.success) {
203
279
  return;
204
280
  }
281
+ if (result.isLongRunning === true) {
282
+ return;
283
+ }
205
284
  const output = this.cli.formatOutput(result, command.config.outputFormat);
206
285
  if (result.success) {
207
286
  console.log(output);
@@ -215,46 +294,46 @@ class CLIApplication {
215
294
  const args = argv.slice(2);
216
295
  for (let i = 0; i < args.length; i++) {
217
296
  const arg = args[i];
218
- if (arg === '--config' || arg === '-c') {
297
+ if (arg === "--config" || arg === "-c") {
219
298
  if (i + 1 < args.length) {
220
299
  options.configFile = args[i + 1];
221
300
  }
222
301
  }
223
- else if (arg === '--profile') {
302
+ else if (arg === "--profile") {
224
303
  if (i + 1 < args.length) {
225
304
  options.profile = args[i + 1];
226
305
  }
227
306
  }
228
- else if (arg === '--host' || arg === '-h') {
307
+ else if (arg === "--host" || arg === "-h") {
229
308
  if (i + 1 < args.length) {
230
309
  options.host = args[i + 1];
231
310
  }
232
311
  }
233
- else if (arg === '--port' || arg === '-p') {
312
+ else if (arg === "--port" || arg === "-p") {
234
313
  if (i + 1 < args.length) {
235
314
  options.port = parseInt(args[i + 1], 10);
236
315
  }
237
316
  }
238
- else if (arg === '--timeout' || arg === '-t') {
317
+ else if (arg === "--timeout" || arg === "-t") {
239
318
  if (i + 1 < args.length) {
240
319
  options.timeout = parseInt(args[i + 1], 10);
241
320
  }
242
321
  }
243
- else if (arg === '--format' || arg === '-f') {
322
+ else if (arg === "--format" || arg === "-f") {
244
323
  if (i + 1 < args.length) {
245
324
  options.outputFormat = args[i + 1];
246
325
  }
247
326
  }
248
- else if (arg === '--verbose' || arg === '-v') {
327
+ else if (arg === "--verbose" || arg === "-v") {
249
328
  options.verbose = true;
250
329
  }
251
- else if (arg === '--quiet' || arg === '-q') {
330
+ else if (arg === "--quiet" || arg === "-q") {
252
331
  options.quiet = true;
253
332
  }
254
- else if (arg === '--debug' || arg === '-d') {
333
+ else if (arg === "--debug" || arg === "-d") {
255
334
  options.debug = true;
256
335
  }
257
- else if (arg === '--target-index') {
336
+ else if (arg === "--target-index") {
258
337
  if (i + 1 < args.length) {
259
338
  options.targetIndex = parseInt(args[i + 1], 10);
260
339
  }
@@ -271,15 +350,9 @@ class CLIApplication {
271
350
  await this.client.disconnect();
272
351
  }
273
352
  catch (error) {
274
- this.logger.error('Error during shutdown:', error);
353
+ this.logger.error("Error during shutdown:", error);
275
354
  }
276
355
  }
277
- try {
278
- await this.proxyManager.shutdown();
279
- }
280
- catch (error) {
281
- this.logger.error('Error shutting down proxy manager:', error);
282
- }
283
356
  }
284
357
  getCLI() {
285
358
  return this.cli;