chrome-cdp-cli 2.0.2 → 2.0.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.
@@ -459,6 +459,17 @@ class CommandSchemaRegistry {
459
459
  description: 'Include text index information',
460
460
  type: 'boolean',
461
461
  default: false
462
+ },
463
+ {
464
+ name: 'color',
465
+ description: 'Enable color output (default: auto-detect)',
466
+ type: 'boolean'
467
+ },
468
+ {
469
+ name: 'no-color',
470
+ description: 'Disable color output',
471
+ type: 'boolean',
472
+ default: false
462
473
  }
463
474
  ],
464
475
  arguments: []
@@ -141,7 +141,8 @@ class CDPClient {
141
141
  }
142
142
  }
143
143
  async discoverTargets(host, port) {
144
- const response = await (0, node_fetch_1.default)(`http://${host}:${port}/json/list`);
144
+ const normalizedHost = host === 'localhost' ? '127.0.0.1' : host;
145
+ const response = await (0, node_fetch_1.default)(`http://${normalizedHost}:${port}/json/list`);
145
146
  if (!response.ok) {
146
147
  throw new Error(`Failed to discover targets: ${response.statusText}`);
147
148
  }
@@ -44,7 +44,8 @@ class ConnectionManager {
44
44
  this.logger = logger || new logger_1.Logger();
45
45
  }
46
46
  async discoverTargets(host, port) {
47
- const url = `http://${host}:${port}/json/list`;
47
+ const normalizedHost = host === 'localhost' ? '127.0.0.1' : host;
48
+ const url = `http://${normalizedHost}:${port}/json/list`;
48
49
  try {
49
50
  this.logger.debug(`Discovering targets at ${url}`);
50
51
  const http = await Promise.resolve().then(() => __importStar(require('http')));
@@ -6,9 +6,93 @@ const path_1 = require("path");
6
6
  class TakeSnapshotHandler {
7
7
  constructor() {
8
8
  this.name = 'snapshot';
9
+ this.colors = {
10
+ reset: '\x1b[0m',
11
+ bright: '\x1b[1m',
12
+ dim: '\x1b[2m',
13
+ red: '\x1b[31m',
14
+ green: '\x1b[32m',
15
+ yellow: '\x1b[33m',
16
+ blue: '\x1b[34m',
17
+ magenta: '\x1b[35m',
18
+ cyan: '\x1b[36m',
19
+ white: '\x1b[37m',
20
+ gray: '\x1b[90m',
21
+ };
22
+ }
23
+ shouldUseColors(args) {
24
+ if (args.color === false) {
25
+ return false;
26
+ }
27
+ if (args.color === true) {
28
+ return true;
29
+ }
30
+ if (process.env.NO_COLOR) {
31
+ return false;
32
+ }
33
+ if (process.env.FORCE_COLOR === '0') {
34
+ return false;
35
+ }
36
+ if (process.env.FORCE_COLOR === '1' || process.env.FORCE_COLOR === 'true') {
37
+ return true;
38
+ }
39
+ if (args.filename) {
40
+ return false;
41
+ }
42
+ return process.stdout.isTTY === true;
43
+ }
44
+ colorize(text, color, useColors) {
45
+ if (!useColors)
46
+ return text;
47
+ return `${color}${text}${this.colors.reset}`;
48
+ }
49
+ colorTag(tagName, useColors) {
50
+ if (['button', 'a', 'input', 'textarea', 'select'].includes(tagName.toLowerCase())) {
51
+ return this.colorize(tagName.toUpperCase(), this.colors.green + this.colors.bright, useColors);
52
+ }
53
+ if (['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(tagName.toLowerCase())) {
54
+ return this.colorize(tagName.toUpperCase(), this.colors.cyan + this.colors.bright, useColors);
55
+ }
56
+ if (tagName.toLowerCase() === 'img') {
57
+ return this.colorize(tagName.toUpperCase(), this.colors.magenta, useColors);
58
+ }
59
+ return this.colorize(tagName.toUpperCase(), this.colors.cyan, useColors);
60
+ }
61
+ colorId(id, useColors) {
62
+ return this.colorize(`#${id}`, this.colors.green, useColors);
63
+ }
64
+ colorClass(className, useColors) {
65
+ return this.colorize(`.${className}`, this.colors.yellow, useColors);
66
+ }
67
+ colorAttribute(name, value, useColors) {
68
+ if (name === 'type') {
69
+ return this.colorize(`[${value}]`, this.colors.magenta, useColors);
70
+ }
71
+ if (name === 'name') {
72
+ return this.colorize(`name="${value}"`, this.colors.magenta, useColors);
73
+ }
74
+ return `[${name}="${value}"]`;
75
+ }
76
+ colorText(text, useColors) {
77
+ return this.colorize(`"${text}"`, this.colors.white, useColors);
78
+ }
79
+ colorUrl(url, useColors) {
80
+ return this.colorize(`"${url}"`, this.colors.blue, useColors);
81
+ }
82
+ colorSpecial(text, useColors) {
83
+ return this.colorize(text, this.colors.yellow, useColors);
84
+ }
85
+ colorTreeSymbol(symbol, useColors) {
86
+ return this.colorize(symbol, this.colors.gray, useColors);
87
+ }
88
+ colorPageTitle(title, useColors) {
89
+ return this.colorize(`PAGE: ${title}`, this.colors.cyan + this.colors.bright, useColors);
9
90
  }
10
91
  async execute(client, args) {
11
92
  const snapshotArgs = args;
93
+ if (snapshotArgs['no-color']) {
94
+ snapshotArgs.color = false;
95
+ }
12
96
  try {
13
97
  await client.send('DOM.enable');
14
98
  await client.send('CSS.enable');
@@ -89,7 +173,8 @@ class TakeSnapshotHandler {
89
173
  processedSnapshot = htmlResponse.outerHTML;
90
174
  }
91
175
  else {
92
- const textSnapshot = this.buildTextFromDOMNode(docResponse.root, url, title);
176
+ const useColors = this.shouldUseColors(snapshotArgs);
177
+ const textSnapshot = this.buildTextFromDOMNode(docResponse.root, url, title, useColors);
93
178
  processedSnapshot = {
94
179
  url,
95
180
  title,
@@ -168,7 +253,8 @@ class TakeSnapshotHandler {
168
253
  error: 'No documents found'
169
254
  };
170
255
  }
171
- const textSnapshot = this.createTextSnapshot(doc, response.strings);
256
+ const useColors = this.shouldUseColors(args);
257
+ const textSnapshot = this.createTextSnapshot(doc, response.strings, useColors);
172
258
  const result = {
173
259
  url: doc.documentURL,
174
260
  title: doc.title,
@@ -176,21 +262,21 @@ class TakeSnapshotHandler {
176
262
  };
177
263
  return result;
178
264
  }
179
- createTextSnapshot(doc, strings) {
265
+ createTextSnapshot(doc, strings, useColors = true) {
180
266
  const nodes = doc.nodes;
181
267
  if (!nodes.nodeName || !nodes.nodeType) {
182
268
  return 'Empty document';
183
269
  }
184
270
  const nodeTree = this.buildNodeTree(doc, strings);
185
- let output = `PAGE: ${doc.title || 'Untitled'}\n`;
271
+ let output = this.colorPageTitle(doc.title || 'Untitled', useColors) + '\n';
186
272
  const bodyNode = this.findBodyNode(nodeTree);
187
273
  if (bodyNode) {
188
- output += this.renderNodeAsText(bodyNode, 0);
274
+ output += this.renderNodeAsText(bodyNode, 0, false, [], useColors);
189
275
  }
190
276
  else {
191
277
  for (const node of nodeTree) {
192
278
  if (this.shouldIncludeNode(node)) {
193
- output += this.renderNodeAsText(node, 0);
279
+ output += this.renderNodeAsText(node, 0, false, [], useColors);
194
280
  }
195
281
  }
196
282
  }
@@ -288,72 +374,78 @@ class TakeSnapshotHandler {
288
374
  }
289
375
  return true;
290
376
  }
291
- renderNodeAsText(node, depth, isLast = false, parentIsLast = []) {
377
+ renderNodeAsText(node, depth, isLast = false, parentIsLast = [], useColors = true) {
292
378
  if (!this.shouldIncludeNode(node)) {
293
379
  return '';
294
380
  }
295
381
  let indent = '';
296
382
  for (let i = 0; i < parentIsLast.length; i++) {
297
- indent += parentIsLast[i] ? ' ' : '│ ';
383
+ const symbol = parentIsLast[i] ? ' ' : '│ ';
384
+ indent += this.colorTreeSymbol(symbol, useColors);
298
385
  }
299
- const prefix = depth > 0 ? (isLast ? '└── ' : '├── ') : '';
386
+ const prefixSymbol = depth > 0 ? (isLast ? '└── ' : '├── ') : '';
387
+ const prefix = this.colorTreeSymbol(prefixSymbol, useColors);
300
388
  let output = '';
301
389
  if (node.nodeType === 3) {
302
390
  if (node.textContent) {
303
391
  const truncatedText = this.truncateText(node.textContent.trim(), 40);
304
- output += `${indent}${prefix}"${truncatedText}"\n`;
392
+ output += `${indent}${prefix}${this.colorText(truncatedText, useColors)}\n`;
305
393
  }
306
394
  }
307
395
  else if (node.nodeType === 1) {
308
- const tag = node.nodeName.toUpperCase();
396
+ const tag = this.colorTag(node.nodeName, useColors);
309
397
  let description = tag;
310
398
  const attrs = [];
311
- if (node.attributes.id)
312
- attrs.push(`#${node.attributes.id}`);
399
+ if (node.attributes.id) {
400
+ attrs.push(this.colorId(node.attributes.id, useColors));
401
+ }
313
402
  if (node.attributes.class) {
314
403
  const classes = node.attributes.class.split(/\s+/).filter((c) => c.trim().length > 0);
315
404
  classes.forEach((cls) => {
316
- attrs.push(`.${cls.trim()}`);
405
+ attrs.push(this.colorClass(cls.trim(), useColors));
317
406
  });
318
407
  }
319
- if (node.attributes.type)
320
- attrs.push(`[${node.attributes.type}]`);
321
- if (node.attributes.name)
322
- attrs.push(`name="${node.attributes.name}"`);
408
+ if (node.attributes.type) {
409
+ attrs.push(this.colorAttribute('type', node.attributes.type, useColors));
410
+ }
411
+ if (node.attributes.name) {
412
+ attrs.push(this.colorAttribute('name', node.attributes.name, useColors));
413
+ }
323
414
  if (attrs.length > 0) {
324
415
  description += `(${attrs.join(' ')})`;
325
416
  }
326
417
  if (node.nodeName === 'img' && node.attributes.alt) {
327
418
  const altText = this.truncateText(node.attributes.alt, 40);
328
- description += `: "${altText}"`;
419
+ description += `: ${this.colorText(altText, useColors)}`;
329
420
  }
330
421
  else if (node.nodeName === 'a' && node.attributes.href) {
331
- description += `: "${node.attributes.href}"`;
422
+ description += `: ${this.colorUrl(node.attributes.href, useColors)}`;
332
423
  }
333
424
  else if (['input', 'textarea'].includes(node.nodeName)) {
334
425
  if (node.attributes.placeholder) {
335
426
  const placeholderText = this.truncateText(node.attributes.placeholder, 40);
336
- description += `: "${placeholderText}"`;
427
+ description += `: ${this.colorText(placeholderText, useColors)}`;
337
428
  }
338
429
  else if (node.inputValue) {
339
430
  const inputText = this.truncateText(node.inputValue, 40);
340
- description += `: "${inputText}"`;
431
+ description += `: ${this.colorText(inputText, useColors)}`;
341
432
  }
342
433
  else if (node.nodeName === 'textarea') {
343
434
  const textContent = this.extractTextContent(node);
344
435
  if (textContent) {
345
436
  const truncatedText = this.truncateText(textContent, 40);
346
- description += `: "${truncatedText}"`;
437
+ description += `: ${this.colorText(truncatedText, useColors)}`;
347
438
  }
348
439
  }
349
- if (node.checked)
350
- description += ' [checked]';
440
+ if (node.checked) {
441
+ description += ` ${this.colorSpecial('[checked]', useColors)}`;
442
+ }
351
443
  }
352
444
  else if (node.nodeName === 'button' || ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(node.nodeName)) {
353
445
  const textContent = this.extractTextContent(node);
354
446
  if (textContent) {
355
447
  const truncatedText = this.truncateText(textContent, 40);
356
- description += `: "${truncatedText}"`;
448
+ description += `: ${this.colorText(truncatedText, useColors)}`;
357
449
  }
358
450
  }
359
451
  output += `${indent}${prefix}${description}\n`;
@@ -365,7 +457,8 @@ class TakeSnapshotHandler {
365
457
  const textContent = this.extractTextContent(node);
366
458
  if (textContent && textContent.trim().length > 0) {
367
459
  const truncatedText = this.truncateText(textContent.trim(), 40);
368
- output += `${indent}│ └── "${truncatedText}"\n`;
460
+ const treeSymbol = this.colorTreeSymbol('│ └── ', useColors);
461
+ output += `${indent}${treeSymbol}${this.colorText(truncatedText, useColors)}\n`;
369
462
  return output;
370
463
  }
371
464
  return output;
@@ -375,7 +468,7 @@ class TakeSnapshotHandler {
375
468
  for (let i = 0; i < meaningfulChildren.length; i++) {
376
469
  const child = meaningfulChildren[i];
377
470
  const childIsLast = i === meaningfulChildren.length - 1;
378
- output += this.renderNodeAsText(child, depth + 1, childIsLast, newParentIsLast);
471
+ output += this.renderNodeAsText(child, depth + 1, childIsLast, newParentIsLast, useColors);
379
472
  }
380
473
  }
381
474
  }
@@ -401,17 +494,17 @@ class TakeSnapshotHandler {
401
494
  return text;
402
495
  return text.substring(0, maxLength) + '...';
403
496
  }
404
- buildTextFromDOMNode(root, _url, title) {
405
- let output = `PAGE: ${title || 'Untitled'}\n`;
497
+ buildTextFromDOMNode(root, _url, title, useColors = true) {
498
+ let output = this.colorPageTitle(title || 'Untitled', useColors) + '\n';
406
499
  const bodyNode = this.findBodyInDOMTree(root);
407
500
  if (bodyNode) {
408
- output += this.renderDOMNodeAsText(bodyNode, 0);
501
+ output += this.renderDOMNodeAsText(bodyNode, 0, false, [], useColors);
409
502
  }
410
503
  else {
411
504
  if (root.children) {
412
505
  for (const child of root.children) {
413
506
  if (this.shouldIncludeDOMNode(child)) {
414
- output += this.renderDOMNodeAsText(child, 0);
507
+ output += this.renderDOMNodeAsText(child, 0, false, [], useColors);
415
508
  }
416
509
  }
417
510
  }
@@ -448,25 +541,28 @@ class TakeSnapshotHandler {
448
541
  }
449
542
  return true;
450
543
  }
451
- renderDOMNodeAsText(node, depth, isLast = false, parentIsLast = []) {
544
+ renderDOMNodeAsText(node, depth, isLast = false, parentIsLast = [], useColors = true) {
452
545
  if (!this.shouldIncludeDOMNode(node)) {
453
546
  return '';
454
547
  }
455
548
  let indent = '';
456
549
  for (let i = 0; i < parentIsLast.length; i++) {
457
- indent += parentIsLast[i] ? ' ' : '│ ';
550
+ const symbol = parentIsLast[i] ? ' ' : '│ ';
551
+ indent += this.colorTreeSymbol(symbol, useColors);
458
552
  }
459
- const prefix = depth > 0 ? (isLast ? '└── ' : '├── ') : '';
553
+ const prefixSymbol = depth > 0 ? (isLast ? '└── ' : '├── ') : '';
554
+ const prefix = this.colorTreeSymbol(prefixSymbol, useColors);
460
555
  let output = '';
461
556
  if (node.nodeType === 3) {
462
557
  const text = (node.nodeValue || '').trim();
463
558
  if (text) {
464
559
  const truncatedText = this.truncateText(text, 40);
465
- output += `${indent}${prefix}"${truncatedText}"\n`;
560
+ output += `${indent}${prefix}${this.colorText(truncatedText, useColors)}\n`;
466
561
  }
467
562
  }
468
563
  else if (node.nodeType === 1) {
469
- const tag = (node.nodeName || '').toUpperCase();
564
+ const nodeName = (node.nodeName || '').toLowerCase();
565
+ const tag = this.colorTag(nodeName, useColors);
470
566
  let description = tag;
471
567
  const attrs = [];
472
568
  if (node.attributes) {
@@ -474,36 +570,38 @@ class TakeSnapshotHandler {
474
570
  const name = node.attributes[i];
475
571
  const value = node.attributes[i + 1];
476
572
  if (['id', 'class', 'type', 'name', 'href', 'src', 'alt', 'placeholder', 'value', 'title'].includes(name)) {
477
- if (name === 'id')
478
- attrs.push(`#${value}`);
573
+ if (name === 'id') {
574
+ attrs.push(this.colorId(value, useColors));
575
+ }
479
576
  else if (name === 'class') {
480
577
  const classes = value.split(/\s+/).filter((c) => c.trim().length > 0);
481
578
  classes.forEach((cls) => {
482
- attrs.push(`.${cls.trim()}`);
579
+ attrs.push(this.colorClass(cls.trim(), useColors));
483
580
  });
484
581
  }
485
- else if (name === 'type')
486
- attrs.push(`[${value}]`);
487
- else
488
- attrs.push(`${name}="${value}"`);
582
+ else if (name === 'type') {
583
+ attrs.push(this.colorAttribute('type', value, useColors));
584
+ }
585
+ else {
586
+ attrs.push(this.colorAttribute(name, value, useColors));
587
+ }
489
588
  }
490
589
  }
491
590
  }
492
591
  if (attrs.length > 0) {
493
592
  description += `(${attrs.join(' ')})`;
494
593
  }
495
- const nodeName = (node.nodeName || '').toLowerCase();
496
594
  if (nodeName === 'img' && node.attributes) {
497
595
  const altIndex = node.attributes.indexOf('alt');
498
596
  if (altIndex >= 0 && altIndex + 1 < node.attributes.length) {
499
597
  const altText = this.truncateText(node.attributes[altIndex + 1], 40);
500
- description += `: "${altText}"`;
598
+ description += `: ${this.colorText(altText, useColors)}`;
501
599
  }
502
600
  }
503
601
  else if (nodeName === 'a' && node.attributes) {
504
602
  const hrefIndex = node.attributes.indexOf('href');
505
603
  if (hrefIndex >= 0 && hrefIndex + 1 < node.attributes.length) {
506
- description += `: "${node.attributes[hrefIndex + 1]}"`;
604
+ description += `: ${this.colorUrl(node.attributes[hrefIndex + 1], useColors)}`;
507
605
  }
508
606
  }
509
607
  else if (['input', 'textarea'].includes(nodeName)) {
@@ -511,14 +609,14 @@ class TakeSnapshotHandler {
511
609
  const placeholderIndex = node.attributes.indexOf('placeholder');
512
610
  if (placeholderIndex >= 0 && placeholderIndex + 1 < node.attributes.length) {
513
611
  const placeholderText = this.truncateText(node.attributes[placeholderIndex + 1], 40);
514
- description += `: "${placeholderText}"`;
612
+ description += `: ${this.colorText(placeholderText, useColors)}`;
515
613
  }
516
614
  }
517
615
  if (nodeName === 'textarea') {
518
616
  const textContent = this.extractTextFromDOMNode(node);
519
617
  if (textContent) {
520
618
  const truncatedText = this.truncateText(textContent, 40);
521
- description += `: "${truncatedText}"`;
619
+ description += `: ${this.colorText(truncatedText, useColors)}`;
522
620
  }
523
621
  }
524
622
  }
@@ -526,7 +624,7 @@ class TakeSnapshotHandler {
526
624
  const textContent = this.extractTextFromDOMNode(node);
527
625
  if (textContent) {
528
626
  const truncatedText = this.truncateText(textContent, 40);
529
- description += `: "${truncatedText}"`;
627
+ description += `: ${this.colorText(truncatedText, useColors)}`;
530
628
  }
531
629
  }
532
630
  output += `${indent}${prefix}${description}\n`;
@@ -539,7 +637,8 @@ class TakeSnapshotHandler {
539
637
  const textContent = this.extractTextFromDOMNode(node);
540
638
  if (textContent && textContent.trim().length > 0) {
541
639
  const truncatedText = this.truncateText(textContent.trim(), 40);
542
- output += `${indent}│ └── "${truncatedText}"\n`;
640
+ const treeSymbol = this.colorTreeSymbol('│ └── ', useColors);
641
+ output += `${indent}${treeSymbol}${this.colorText(truncatedText, useColors)}\n`;
543
642
  return output;
544
643
  }
545
644
  return output;
@@ -549,7 +648,7 @@ class TakeSnapshotHandler {
549
648
  for (let i = 0; i < meaningfulChildren.length; i++) {
550
649
  const child = meaningfulChildren[i];
551
650
  const childIsLast = i === meaningfulChildren.length - 1;
552
- output += this.renderDOMNodeAsText(child, depth + 1, childIsLast, newParentIsLast);
651
+ output += this.renderDOMNodeAsText(child, depth + 1, childIsLast, newParentIsLast, useColors);
553
652
  }
554
653
  }
555
654
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chrome-cdp-cli",
3
- "version": "2.0.2",
3
+ "version": "2.0.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",