chrome-cdp-cli 1.0.1 → 1.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.
package/README.md CHANGED
@@ -2,17 +2,46 @@
2
2
 
3
3
  A powerful command-line tool for controlling Chrome browser instances via the Chrome DevTools Protocol (CDP). This tool provides programmatic access to browser automation, debugging, and inspection capabilities without requiring a graphical interface.
4
4
 
5
+ ## Implementation Status
6
+
7
+ ### ✅ Fully Implemented Features
8
+
9
+ - 🔗 **Connection Management**: Connect to local or remote Chrome instances with auto-discovery
10
+ - ⚡ **JavaScript Execution**: Execute JavaScript code in browser context with full async support and file execution
11
+ - 📸 **Visual Capture**: Take screenshots and capture complete DOM snapshots with layout information
12
+ - 📊 **Console Monitoring**: Real-time console message capture with filtering and storage
13
+ - 🌐 **Network Monitoring**: Real-time network request/response monitoring with comprehensive filtering
14
+ - 🔧 **CLI Interface**: Full command-line interface with argument parsing and routing
15
+ - 📦 **Build System**: Complete TypeScript build pipeline with testing framework
16
+
17
+ ### 🚧 Eval Workaround Available
18
+
19
+ These features are not directly implemented but can be achieved using the `eval` command:
20
+
21
+ - 📄 **Page Navigation**: `eval "window.location.href = 'https://example.com'"`
22
+ - 🖱️ **Element Interaction**: `eval "document.querySelector('#btn').click()"`
23
+ - 📝 **Form Filling**: `eval "document.querySelector('#input').value = 'text'"`
24
+ - 📄 **HTML Content**: `eval "document.documentElement.outerHTML"`
25
+ - 🚀 **Performance Data**: `eval "performance.now()"` or `eval "performance.getEntriesByType('navigation')"`
26
+ - 📱 **User Agent**: `eval "navigator.userAgent"`
27
+ - 🌐 **Network Requests**: `eval "fetch('/api').then(r => r.json())"`
28
+
29
+ ### ⏳ Not Yet Implemented
30
+
31
+ - 📄 **Direct Page Management**: Native commands for creating, closing, listing, and selecting tabs
32
+ - 🖱️ **Direct Element Interaction**: Native click, hover, drag, and form filling commands
33
+ - 🚀 **Performance Analysis**: Native performance profiling and metrics collection
34
+ - 📱 **Device Emulation**: Native device and network condition simulation
35
+ - 📊 **Output Formatting**: Advanced JSON/text formatting with quiet/verbose modes
36
+
5
37
  ## Features
6
38
 
7
39
  - 🔗 **Connection Management**: Connect to local or remote Chrome instances
8
- - 📄 **Page Management**: Navigate, create, close, and manage browser tabs
9
40
  - ⚡ **JavaScript Execution**: Execute JavaScript code in browser context with full async support
10
41
  - 📸 **Visual Capture**: Take screenshots and capture HTML content
11
- - 🖱️ **Element Interaction**: Click, hover, fill forms, and interact with page elements
12
42
  - 📊 **Monitoring**: Monitor console messages and network requests in real-time
13
- - 🚀 **Performance Analysis**: Profile page performance and analyze metrics
14
- - 📱 **Device Emulation**: Simulate different devices and network conditions
15
43
  - 🔧 **Flexible Output**: Support for JSON and human-readable text output formats
44
+ - 🚧 **Eval Workarounds**: Many advanced features available through JavaScript execution
16
45
 
17
46
  ## Installation
18
47
 
@@ -77,17 +106,26 @@ chrome-cdp-cli eval "document.title"
77
106
  # Or use with npx (no installation needed)
78
107
  npx chrome-cdp-cli eval "document.title"
79
108
 
80
- # Navigate to a website
81
- chrome-cdp-cli navigate_page "https://example.com"
109
+ # Navigate to a website (via eval)
110
+ chrome-cdp-cli eval "window.location.href = 'https://example.com'"
82
111
 
83
112
  # Take a screenshot
84
113
  chrome-cdp-cli screenshot --filename screenshot.png
85
114
 
86
- # Click an element
87
- chrome-cdp-cli click "#submit-button"
115
+ # Capture DOM snapshot
116
+ chrome-cdp-cli snapshot --filename dom-snapshot.json
117
+
118
+ # Click an element (via eval)
119
+ chrome-cdp-cli eval "document.querySelector('#submit-button').click()"
88
120
 
89
- # Fill a form field
90
- chrome-cdp-cli fill "#email" "user@example.com"
121
+ # Fill a form field (via eval)
122
+ chrome-cdp-cli eval "document.querySelector('#email').value = 'user@example.com'"
123
+
124
+ # Monitor console messages
125
+ chrome-cdp-cli get_console_message
126
+
127
+ # Monitor network requests
128
+ chrome-cdp-cli get_network_request
91
129
 
92
130
  # Get help for all commands
93
131
  chrome-cdp-cli --help
@@ -112,7 +150,23 @@ All commands support these connection options:
112
150
  - `--quiet`: Suppress non-essential output
113
151
  - `--verbose`: Enable detailed logging
114
152
 
115
- ### Core Commands
153
+ ## Command Reference
154
+
155
+ ### Connection Options
156
+
157
+ All commands support these connection options:
158
+
159
+ - `--host <host>`: Chrome host (default: localhost)
160
+ - `--port <port>`: DevTools port (default: 9222)
161
+ - `--timeout <ms>`: Command timeout in milliseconds (default: 30000)
162
+
163
+ ### Output Options
164
+
165
+ - `--format <format>`: Output format - 'json' or 'text' (default: text)
166
+ - `--quiet`: Suppress non-essential output
167
+ - `--verbose`: Enable detailed logging
168
+
169
+ ### ✅ Implemented Commands
116
170
 
117
171
  #### JavaScript Execution
118
172
  ```bash
@@ -130,57 +184,299 @@ npx chrome-cdp-cli eval "document.title"
130
184
  npx chrome-cdp-cli eval --file script.js
131
185
  ```
132
186
 
187
+ #### Visual Capture
188
+ ```bash
189
+ # Take screenshot
190
+ chrome-cdp-cli screenshot --filename screenshot.png
191
+
192
+ # Full page screenshot
193
+ chrome-cdp-cli screenshot --full-page --filename fullpage.png
194
+
195
+ # DOM snapshot with complete layout information
196
+ chrome-cdp-cli snapshot --filename dom-snapshot.json
197
+
198
+ # Custom dimensions
199
+ chrome-cdp-cli screenshot --width 1920 --height 1080 --filename custom.png
200
+ ```
201
+
202
+ #### Console Monitoring
203
+ ```bash
204
+ # Get latest console message
205
+ chrome-cdp-cli get_console_message
206
+
207
+ # List all console messages
208
+ chrome-cdp-cli list_console_messages
209
+
210
+ # Filter console messages
211
+ chrome-cdp-cli list_console_messages --filter '{"types":["error","warn"]}'
212
+ ```
213
+
214
+ #### Network Monitoring
215
+ ```bash
216
+ # Get latest network request
217
+ chrome-cdp-cli get_network_request
218
+
219
+ # List all network requests
220
+ chrome-cdp-cli list_network_requests
221
+
222
+ # Filter network requests
223
+ chrome-cdp-cli list_network_requests --filter '{"methods":["POST"],"statusCodes":[200,201]}'
224
+ ```
225
+
226
+ ### 🚧 Available via Eval Workarounds
227
+
133
228
  #### Page Management
134
229
  ```bash
135
230
  # Navigate to URL
136
- chrome-cdp-cli navigate_page "https://example.com"
231
+ chrome-cdp-cli eval "window.location.href = 'https://example.com'"
137
232
 
138
- # Create new tab
139
- chrome-cdp-cli new_page
233
+ # Get current URL
234
+ chrome-cdp-cli eval "window.location.href"
140
235
 
141
- # List all pages
142
- chrome-cdp-cli list_pages
236
+ # Reload page
237
+ chrome-cdp-cli eval "window.location.reload()"
143
238
 
144
- # Close current page
145
- chrome-cdp-cli close_page
239
+ # Go back
240
+ chrome-cdp-cli eval "window.history.back()"
146
241
 
147
- # Switch to specific page
148
- chrome-cdp-cli select_page --id "page-id"
242
+ # Go forward
243
+ chrome-cdp-cli eval "window.history.forward()"
149
244
  ```
150
245
 
151
246
  #### Element Interaction
152
247
  ```bash
153
248
  # Click element
154
- chrome-cdp-cli click "#button"
249
+ chrome-cdp-cli eval "document.querySelector('#button').click()"
155
250
 
156
251
  # Fill input field
157
- chrome-cdp-cli fill "#email" "user@example.com"
252
+ chrome-cdp-cli eval "document.querySelector('#email').value = 'user@example.com'"
253
+
254
+ # Hover over element (trigger mouseover event)
255
+ chrome-cdp-cli eval "document.querySelector('.menu-item').dispatchEvent(new MouseEvent('mouseover'))"
158
256
 
159
- # Hover over element
160
- chrome-cdp-cli hover ".menu-item"
257
+ # Check if element exists
258
+ chrome-cdp-cli eval "!!document.querySelector('#element')"
161
259
 
162
- # Wait for element
163
- chrome-cdp-cli wait_for "#loading" --timeout 5000
260
+ # Get element text
261
+ chrome-cdp-cli eval "document.querySelector('#element').textContent"
262
+
263
+ # Get element attributes
264
+ chrome-cdp-cli eval "document.querySelector('#element').getAttribute('class')"
164
265
  ```
165
266
 
166
- #### Visual Capture
267
+ #### Form Handling
167
268
  ```bash
168
- # Take screenshot
169
- chrome-cdp-cli screenshot --filename screenshot.png
269
+ # Fill multiple form fields
270
+ chrome-cdp-cli eval "
271
+ document.querySelector('#name').value = 'John Doe';
272
+ document.querySelector('#email').value = 'john@example.com';
273
+ document.querySelector('#phone').value = '123-456-7890';
274
+ "
170
275
 
171
- # Full page screenshot
172
- chrome-cdp-cli screenshot --full-page --filename fullpage.png
276
+ # Submit form
277
+ chrome-cdp-cli eval "document.querySelector('#myform').submit()"
173
278
 
174
- # DOM snapshot
175
- chrome-cdp-cli snapshot --filename dom-snapshot.json
279
+ # Select dropdown option
280
+ chrome-cdp-cli eval "document.querySelector('#dropdown').value = 'option1'"
176
281
 
177
- # Custom dimensions
178
- chrome-cdp-cli screenshot --width 1920 --height 1080 --filename custom.png
282
+ # Check checkbox
283
+ chrome-cdp-cli eval "document.querySelector('#checkbox').checked = true"
284
+ ```
285
+
286
+ #### Content Extraction
287
+ ```bash
288
+ # Get page HTML
289
+ chrome-cdp-cli eval "document.documentElement.outerHTML"
290
+
291
+ # Get page title
292
+ chrome-cdp-cli eval "document.title"
293
+
294
+ # Get all links
295
+ chrome-cdp-cli eval "Array.from(document.querySelectorAll('a')).map(a => a.href)"
296
+
297
+ # Get all images
298
+ chrome-cdp-cli eval "Array.from(document.querySelectorAll('img')).map(img => img.src)"
299
+
300
+ # Extract table data
301
+ chrome-cdp-cli eval "Array.from(document.querySelectorAll('table tr')).map(row => Array.from(row.cells).map(cell => cell.textContent))"
302
+ ```
303
+
304
+ #### Performance Monitoring
305
+ ```bash
306
+ # Get performance timing
307
+ chrome-cdp-cli eval "performance.timing"
308
+
309
+ # Get navigation entries
310
+ chrome-cdp-cli eval "performance.getEntriesByType('navigation')"
311
+
312
+ # Get resource entries
313
+ chrome-cdp-cli eval "performance.getEntriesByType('resource')"
314
+
315
+ # Get current timestamp
316
+ chrome-cdp-cli eval "performance.now()"
317
+
318
+ # Measure performance
319
+ chrome-cdp-cli eval "
320
+ performance.mark('start');
321
+ // ... some operation ...
322
+ performance.mark('end');
323
+ performance.measure('operation', 'start', 'end');
324
+ performance.getEntriesByName('operation')[0].duration;
325
+ "
326
+ ```
327
+
328
+ #### Network Operations
329
+ ```bash
330
+ # Make HTTP request
331
+ chrome-cdp-cli eval "fetch('/api/data').then(r => r.json())"
332
+
333
+ # POST data
334
+ chrome-cdp-cli eval "
335
+ fetch('/api/users', {
336
+ method: 'POST',
337
+ headers: {'Content-Type': 'application/json'},
338
+ body: JSON.stringify({name: 'John', email: 'john@example.com'})
339
+ }).then(r => r.json())
340
+ "
341
+
342
+ # Check network connectivity
343
+ chrome-cdp-cli eval "navigator.onLine"
344
+ ```
179
345
 
180
- # Get HTML content
181
- chrome-cdp-cli get_html --output page.html
346
+ #### Browser Information
347
+ ```bash
348
+ # Get user agent
349
+ chrome-cdp-cli eval "navigator.userAgent"
350
+
351
+ # Get viewport size
352
+ chrome-cdp-cli eval "{width: window.innerWidth, height: window.innerHeight}"
353
+
354
+ # Get screen resolution
355
+ chrome-cdp-cli eval "{width: screen.width, height: screen.height}"
356
+
357
+ # Get browser language
358
+ chrome-cdp-cli eval "navigator.language"
359
+
360
+ # Get cookies
361
+ chrome-cdp-cli eval "document.cookie"
362
+ ```
363
+
364
+ ### ⏳ Not Yet Implemented
365
+
366
+ These features require dedicated handlers and are not yet available:
367
+
368
+ - Native page management commands (new_page, close_page, list_pages, select_page)
369
+ - Native element interaction commands (click, hover, fill, drag)
370
+ - Native performance profiling commands
371
+ - Native device emulation commands
372
+ - Advanced output formatting options
373
+
374
+ ## The Power of Eval
375
+
376
+ The `eval` command is the most powerful feature of this CLI tool. It allows you to execute any JavaScript code in the browser context, making it possible to achieve almost any browser automation task. Here are some advanced examples:
377
+
378
+ ### Advanced Automation Examples
379
+
380
+ ```bash
381
+ # Wait for element to appear
382
+ chrome-cdp-cli eval "
383
+ new Promise(resolve => {
384
+ const check = () => {
385
+ const element = document.querySelector('#dynamic-content');
386
+ if (element) resolve(element.textContent);
387
+ else setTimeout(check, 100);
388
+ };
389
+ check();
390
+ })
391
+ "
392
+
393
+ # Scroll to element
394
+ chrome-cdp-cli eval "
395
+ document.querySelector('#target').scrollIntoView({behavior: 'smooth'});
396
+ "
397
+
398
+ # Take element screenshot (get element bounds for screenshot)
399
+ chrome-cdp-cli eval "
400
+ const element = document.querySelector('#target');
401
+ const rect = element.getBoundingClientRect();
402
+ ({x: rect.x, y: rect.y, width: rect.width, height: rect.height})
403
+ "
404
+
405
+ # Simulate complex user interactions
406
+ chrome-cdp-cli eval "
407
+ const element = document.querySelector('#button');
408
+ element.dispatchEvent(new MouseEvent('mousedown'));
409
+ setTimeout(() => element.dispatchEvent(new MouseEvent('mouseup')), 100);
410
+ "
411
+
412
+ # Extract structured data
413
+ chrome-cdp-cli eval "
414
+ Array.from(document.querySelectorAll('.product')).map(product => ({
415
+ name: product.querySelector('.name').textContent,
416
+ price: product.querySelector('.price').textContent,
417
+ image: product.querySelector('img').src
418
+ }))
419
+ "
420
+
421
+ # Monitor page changes
422
+ chrome-cdp-cli eval "
423
+ new Promise(resolve => {
424
+ const observer = new MutationObserver(mutations => {
425
+ resolve(mutations.length + ' changes detected');
426
+ observer.disconnect();
427
+ });
428
+ observer.observe(document.body, {childList: true, subtree: true});
429
+ setTimeout(() => {
430
+ observer.disconnect();
431
+ resolve('No changes in 5 seconds');
432
+ }, 5000);
433
+ })
434
+ "
182
435
  ```
183
436
 
437
+ ## Current Limitations & Roadmap
438
+
439
+ ### Current Limitations
440
+
441
+ - **No native page management**: Creating, closing, and switching between tabs requires manual implementation
442
+ - **No native element interaction**: Clicking, hovering, and form filling must be done via eval
443
+ - **No performance profiling**: Advanced performance analysis requires manual JavaScript
444
+ - **No device emulation**: Mobile/tablet simulation not yet implemented
445
+ - **Basic output formatting**: Advanced JSON/text formatting options not available
446
+
447
+ ### Upcoming Features
448
+
449
+ 1. **Native Page Management Commands**
450
+ - `new_page`, `close_page`, `list_pages`, `select_page`
451
+ - Direct CDP Target domain integration
452
+
453
+ 2. **Native Element Interaction**
454
+ - `click`, `hover`, `fill`, `drag` commands
455
+ - CSS selector-based element targeting
456
+
457
+ 3. **Performance Analysis**
458
+ - `performance_start_trace`, `performance_stop_trace`
459
+ - Built-in performance metrics and analysis
460
+
461
+ 4. **Device Emulation**
462
+ - `emulate` command for device simulation
463
+ - Network condition simulation
464
+
465
+ 5. **Advanced Output Formatting**
466
+ - Enhanced JSON/text formatting
467
+ - Quiet and verbose modes
468
+ - Custom output templates
469
+
470
+ ### Why Use Eval Workarounds?
471
+
472
+ The eval approach offers several advantages:
473
+
474
+ - **Immediate availability**: No waiting for feature implementation
475
+ - **Maximum flexibility**: Any JavaScript operation is possible
476
+ - **Learning opportunity**: Better understanding of browser APIs
477
+ - **Custom solutions**: Tailor automation to specific needs
478
+ - **Future-proof**: Works with any web technology
479
+
184
480
  ## Configuration
185
481
 
186
482
  ### Configuration File
@@ -17,6 +17,8 @@ class CLIApplication {
17
17
  this.cli.registerHandler(new handlers_1.EvaluateScriptHandler());
18
18
  this.cli.registerHandler(new handlers_1.TakeScreenshotHandler());
19
19
  this.cli.registerHandler(new handlers_1.TakeSnapshotHandler());
20
+ this.cli.registerHandler(new handlers_1.GetConsoleMessageHandler());
21
+ this.cli.registerHandler(new handlers_1.ListConsoleMessagesHandler());
20
22
  }
21
23
  async run(argv) {
22
24
  try {
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GetConsoleMessageHandler = void 0;
4
+ const ConsoleMonitor_1 = require("../monitors/ConsoleMonitor");
5
+ class GetConsoleMessageHandler {
6
+ constructor() {
7
+ this.name = 'get_console_message';
8
+ this.consoleMonitor = null;
9
+ }
10
+ async execute(client, args) {
11
+ try {
12
+ const params = args;
13
+ if (!this.consoleMonitor) {
14
+ this.consoleMonitor = new ConsoleMonitor_1.ConsoleMonitor(client);
15
+ }
16
+ if (params.startMonitoring || !this.consoleMonitor.isActive()) {
17
+ await this.consoleMonitor.startMonitoring();
18
+ }
19
+ const filter = {};
20
+ if (params.type) {
21
+ filter.types = [params.type];
22
+ }
23
+ if (params.textPattern) {
24
+ filter.textPattern = params.textPattern;
25
+ }
26
+ const latestMessage = this.consoleMonitor.getLatestMessage(filter);
27
+ if (!latestMessage) {
28
+ return {
29
+ success: true,
30
+ data: null
31
+ };
32
+ }
33
+ return {
34
+ success: true,
35
+ data: {
36
+ type: latestMessage.type,
37
+ text: latestMessage.text,
38
+ args: latestMessage.args,
39
+ timestamp: latestMessage.timestamp,
40
+ stackTrace: latestMessage.stackTrace
41
+ }
42
+ };
43
+ }
44
+ catch (error) {
45
+ return {
46
+ success: false,
47
+ error: error instanceof Error ? error.message : 'Unknown error occurred'
48
+ };
49
+ }
50
+ }
51
+ validateArgs(args) {
52
+ if (!args || typeof args !== 'object') {
53
+ return true;
54
+ }
55
+ const params = args;
56
+ if (params.type !== undefined) {
57
+ if (typeof params.type !== 'string') {
58
+ return false;
59
+ }
60
+ const validTypes = ['log', 'info', 'warn', 'error', 'debug'];
61
+ if (!validTypes.includes(params.type)) {
62
+ return false;
63
+ }
64
+ }
65
+ if (params.textPattern !== undefined && typeof params.textPattern !== 'string') {
66
+ return false;
67
+ }
68
+ if (params.startMonitoring !== undefined && typeof params.startMonitoring !== 'boolean') {
69
+ return false;
70
+ }
71
+ return true;
72
+ }
73
+ getHelp() {
74
+ return `get_console_message - Get the latest console message
75
+
76
+ Usage:
77
+ get_console_message [options]
78
+
79
+ Options:
80
+ --type <type> Filter by message type (log, info, warn, error, debug)
81
+ --textPattern <pattern> Filter by text pattern (regex)
82
+ --startMonitoring Start monitoring if not already active
83
+
84
+ Examples:
85
+ get_console_message
86
+ get_console_message --type error
87
+ get_console_message --textPattern "API"
88
+ get_console_message --startMonitoring`;
89
+ }
90
+ }
91
+ exports.GetConsoleMessageHandler = GetConsoleMessageHandler;
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GetNetworkRequestHandler = void 0;
4
+ const NetworkMonitor_1 = require("../monitors/NetworkMonitor");
5
+ class GetNetworkRequestHandler {
6
+ constructor() {
7
+ this.name = 'get_network_request';
8
+ this.networkMonitor = null;
9
+ }
10
+ async execute(client, args) {
11
+ try {
12
+ const params = args;
13
+ if (!this.networkMonitor) {
14
+ this.networkMonitor = new NetworkMonitor_1.NetworkMonitor(client);
15
+ }
16
+ if (!this.networkMonitor.isActive()) {
17
+ await this.networkMonitor.startMonitoring();
18
+ }
19
+ const filter = params.filter ? {
20
+ methods: params.filter.methods,
21
+ urlPattern: params.filter.urlPattern,
22
+ statusCodes: params.filter.statusCodes,
23
+ } : undefined;
24
+ const latestRequest = this.networkMonitor.getLatestRequest(filter);
25
+ if (!latestRequest) {
26
+ return {
27
+ success: true,
28
+ data: null
29
+ };
30
+ }
31
+ return {
32
+ success: true,
33
+ data: latestRequest
34
+ };
35
+ }
36
+ catch (error) {
37
+ return {
38
+ success: false,
39
+ error: error instanceof Error ? error.message : 'Unknown error occurred'
40
+ };
41
+ }
42
+ }
43
+ getNetworkMonitor() {
44
+ return this.networkMonitor;
45
+ }
46
+ setNetworkMonitor(monitor) {
47
+ this.networkMonitor = monitor;
48
+ }
49
+ }
50
+ exports.GetNetworkRequestHandler = GetNetworkRequestHandler;
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ListConsoleMessagesHandler = void 0;
4
+ const ConsoleMonitor_1 = require("../monitors/ConsoleMonitor");
5
+ class ListConsoleMessagesHandler {
6
+ constructor() {
7
+ this.name = 'list_console_messages';
8
+ this.consoleMonitor = null;
9
+ }
10
+ async execute(client, args) {
11
+ try {
12
+ const params = args;
13
+ if (!this.consoleMonitor) {
14
+ this.consoleMonitor = new ConsoleMonitor_1.ConsoleMonitor(client);
15
+ }
16
+ if (params.startMonitoring || !this.consoleMonitor.isActive()) {
17
+ await this.consoleMonitor.startMonitoring();
18
+ }
19
+ const filter = {};
20
+ if (params.types && params.types.length > 0) {
21
+ filter.types = params.types;
22
+ }
23
+ if (params.textPattern) {
24
+ filter.textPattern = params.textPattern;
25
+ }
26
+ if (params.maxMessages && params.maxMessages > 0) {
27
+ filter.maxMessages = params.maxMessages;
28
+ }
29
+ if (params.startTime) {
30
+ filter.startTime = params.startTime;
31
+ }
32
+ if (params.endTime) {
33
+ filter.endTime = params.endTime;
34
+ }
35
+ const messages = this.consoleMonitor.getMessages(filter);
36
+ return {
37
+ success: true,
38
+ data: {
39
+ messages: messages.map(msg => ({
40
+ type: msg.type,
41
+ text: msg.text,
42
+ args: msg.args,
43
+ timestamp: msg.timestamp,
44
+ stackTrace: msg.stackTrace
45
+ })),
46
+ totalCount: messages.length,
47
+ isMonitoring: this.consoleMonitor.isActive()
48
+ }
49
+ };
50
+ }
51
+ catch (error) {
52
+ return {
53
+ success: false,
54
+ error: error instanceof Error ? error.message : 'Unknown error occurred'
55
+ };
56
+ }
57
+ }
58
+ validateArgs(args) {
59
+ if (!args || typeof args !== 'object') {
60
+ return true;
61
+ }
62
+ const params = args;
63
+ if (params.types !== undefined) {
64
+ if (!Array.isArray(params.types)) {
65
+ return false;
66
+ }
67
+ const validTypes = ['log', 'info', 'warn', 'error', 'debug'];
68
+ for (const type of params.types) {
69
+ if (typeof type !== 'string' || !validTypes.includes(type)) {
70
+ return false;
71
+ }
72
+ }
73
+ }
74
+ if (params.textPattern !== undefined && typeof params.textPattern !== 'string') {
75
+ return false;
76
+ }
77
+ if (params.maxMessages !== undefined) {
78
+ if (typeof params.maxMessages !== 'number' || params.maxMessages <= 0) {
79
+ return false;
80
+ }
81
+ }
82
+ if (params.startTime !== undefined && typeof params.startTime !== 'number') {
83
+ return false;
84
+ }
85
+ if (params.endTime !== undefined && typeof params.endTime !== 'number') {
86
+ return false;
87
+ }
88
+ if (params.startMonitoring !== undefined && typeof params.startMonitoring !== 'boolean') {
89
+ return false;
90
+ }
91
+ return true;
92
+ }
93
+ getHelp() {
94
+ return `list_console_messages - List all captured console messages
95
+
96
+ Usage:
97
+ list_console_messages [options]
98
+
99
+ Options:
100
+ --types <types> Filter by message types (comma-separated: log,info,warn,error,debug)
101
+ --textPattern <pattern> Filter by text pattern (regex)
102
+ --maxMessages <count> Maximum number of messages to return
103
+ --startTime <timestamp> Filter messages after this timestamp
104
+ --endTime <timestamp> Filter messages before this timestamp
105
+ --startMonitoring Start monitoring if not already active
106
+
107
+ Examples:
108
+ list_console_messages
109
+ list_console_messages --types error,warn
110
+ list_console_messages --textPattern "API" --maxMessages 10
111
+ list_console_messages --startTime 1640995200000`;
112
+ }
113
+ }
114
+ exports.ListConsoleMessagesHandler = ListConsoleMessagesHandler;
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ListNetworkRequestsHandler = void 0;
4
+ const NetworkMonitor_1 = require("../monitors/NetworkMonitor");
5
+ class ListNetworkRequestsHandler {
6
+ constructor() {
7
+ this.name = 'list_network_requests';
8
+ this.networkMonitor = null;
9
+ }
10
+ async execute(client, args) {
11
+ try {
12
+ const params = args;
13
+ if (!this.networkMonitor) {
14
+ this.networkMonitor = new NetworkMonitor_1.NetworkMonitor(client);
15
+ }
16
+ if (!this.networkMonitor.isActive()) {
17
+ await this.networkMonitor.startMonitoring();
18
+ }
19
+ const filter = params.filter ? {
20
+ methods: params.filter.methods,
21
+ urlPattern: params.filter.urlPattern,
22
+ statusCodes: params.filter.statusCodes,
23
+ maxRequests: params.filter.maxRequests,
24
+ startTime: params.filter.startTime,
25
+ endTime: params.filter.endTime,
26
+ } : undefined;
27
+ const requests = this.networkMonitor.getRequests(filter);
28
+ return {
29
+ success: true,
30
+ data: {
31
+ requests,
32
+ count: requests.length,
33
+ isMonitoring: this.networkMonitor.isActive()
34
+ }
35
+ };
36
+ }
37
+ catch (error) {
38
+ return {
39
+ success: false,
40
+ error: error instanceof Error ? error.message : 'Unknown error occurred'
41
+ };
42
+ }
43
+ }
44
+ getNetworkMonitor() {
45
+ return this.networkMonitor;
46
+ }
47
+ setNetworkMonitor(monitor) {
48
+ this.networkMonitor = monitor;
49
+ }
50
+ }
51
+ exports.ListNetworkRequestsHandler = ListNetworkRequestsHandler;
@@ -17,3 +17,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./EvaluateScriptHandler"), exports);
18
18
  __exportStar(require("./TakeScreenshotHandler"), exports);
19
19
  __exportStar(require("./TakeSnapshotHandler"), exports);
20
+ __exportStar(require("./GetConsoleMessageHandler"), exports);
21
+ __exportStar(require("./ListConsoleMessagesHandler"), exports);
22
+ __exportStar(require("./GetNetworkRequestHandler"), exports);
23
+ __exportStar(require("./ListNetworkRequestsHandler"), exports);
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ConsoleMonitor = void 0;
4
+ class ConsoleMonitor {
5
+ constructor(client) {
6
+ this.messages = [];
7
+ this.isMonitoring = false;
8
+ this.messageHandler = null;
9
+ this.client = client;
10
+ }
11
+ async startMonitoring() {
12
+ if (this.isMonitoring) {
13
+ return;
14
+ }
15
+ await this.client.send('Runtime.enable');
16
+ this.messageHandler = (params) => {
17
+ this.handleConsoleMessage(params);
18
+ };
19
+ this.client.on('Runtime.consoleAPICalled', this.messageHandler);
20
+ this.isMonitoring = true;
21
+ }
22
+ async stopMonitoring() {
23
+ if (!this.isMonitoring || !this.messageHandler) {
24
+ return;
25
+ }
26
+ this.client.off('Runtime.consoleAPICalled', this.messageHandler);
27
+ this.messageHandler = null;
28
+ this.isMonitoring = false;
29
+ }
30
+ getMessages(filter) {
31
+ let filteredMessages = [...this.messages];
32
+ if (filter) {
33
+ if (filter.types && filter.types.length > 0) {
34
+ filteredMessages = filteredMessages.filter(msg => filter.types.includes(msg.type));
35
+ }
36
+ if (filter.textPattern) {
37
+ const pattern = new RegExp(filter.textPattern, 'i');
38
+ filteredMessages = filteredMessages.filter(msg => pattern.test(msg.text));
39
+ }
40
+ if (filter.startTime) {
41
+ filteredMessages = filteredMessages.filter(msg => msg.timestamp >= filter.startTime);
42
+ }
43
+ if (filter.endTime) {
44
+ filteredMessages = filteredMessages.filter(msg => msg.timestamp <= filter.endTime);
45
+ }
46
+ if (filter.maxMessages && filter.maxMessages > 0) {
47
+ filteredMessages = filteredMessages.slice(-filter.maxMessages);
48
+ }
49
+ }
50
+ return filteredMessages;
51
+ }
52
+ getLatestMessage(filter) {
53
+ const messages = this.getMessages(filter);
54
+ return messages.length > 0 ? messages[messages.length - 1] : null;
55
+ }
56
+ clearMessages() {
57
+ this.messages = [];
58
+ }
59
+ getMessageCount(filter) {
60
+ return this.getMessages(filter).length;
61
+ }
62
+ isActive() {
63
+ return this.isMonitoring;
64
+ }
65
+ handleConsoleMessage(params) {
66
+ try {
67
+ const consoleParams = params;
68
+ const message = {
69
+ type: this.mapConsoleType(consoleParams.type),
70
+ text: this.formatConsoleArgs(consoleParams.args),
71
+ args: consoleParams.args.map(arg => arg.value || arg.description || ''),
72
+ timestamp: consoleParams.timestamp,
73
+ stackTrace: consoleParams.stackTrace ?
74
+ this.convertStackTrace(consoleParams.stackTrace.callFrames) : undefined
75
+ };
76
+ this.messages.push(message);
77
+ if (this.messages.length > 1000) {
78
+ this.messages = this.messages.slice(-1000);
79
+ }
80
+ }
81
+ catch (error) {
82
+ console.error('Error handling console message:', error);
83
+ }
84
+ }
85
+ mapConsoleType(cdpType) {
86
+ switch (cdpType) {
87
+ case 'log':
88
+ return 'log';
89
+ case 'info':
90
+ return 'info';
91
+ case 'warning':
92
+ return 'warn';
93
+ case 'error':
94
+ return 'error';
95
+ case 'debug':
96
+ return 'debug';
97
+ default:
98
+ return 'log';
99
+ }
100
+ }
101
+ formatConsoleArgs(args) {
102
+ return args.map(arg => {
103
+ if (arg.value !== undefined) {
104
+ if (typeof arg.value === 'string') {
105
+ return arg.value;
106
+ }
107
+ return JSON.stringify(arg.value);
108
+ }
109
+ return arg.description || '';
110
+ }).join(' ');
111
+ }
112
+ convertStackTrace(callFrames) {
113
+ return callFrames.map(frame => ({
114
+ functionName: frame.functionName || '<anonymous>',
115
+ url: frame.url,
116
+ lineNumber: frame.lineNumber,
117
+ columnNumber: frame.columnNumber
118
+ }));
119
+ }
120
+ }
121
+ exports.ConsoleMonitor = ConsoleMonitor;
@@ -0,0 +1,162 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NetworkMonitor = void 0;
4
+ class NetworkMonitor {
5
+ constructor(client) {
6
+ this.requests = new Map();
7
+ this.completedRequests = [];
8
+ this.isMonitoring = false;
9
+ this.requestWillBeSentHandler = null;
10
+ this.responseReceivedHandler = null;
11
+ this.loadingFinishedHandler = null;
12
+ this.loadingFailedHandler = null;
13
+ this.client = client;
14
+ }
15
+ async startMonitoring() {
16
+ if (this.isMonitoring) {
17
+ return;
18
+ }
19
+ await this.client.send('Network.enable');
20
+ this.requestWillBeSentHandler = (params) => {
21
+ this.handleRequestWillBeSent(params);
22
+ };
23
+ this.responseReceivedHandler = (params) => {
24
+ this.handleResponseReceived(params);
25
+ };
26
+ this.loadingFinishedHandler = (params) => {
27
+ this.handleLoadingFinished(params);
28
+ };
29
+ this.loadingFailedHandler = (params) => {
30
+ this.handleLoadingFailed(params);
31
+ };
32
+ this.client.on('Network.requestWillBeSent', this.requestWillBeSentHandler);
33
+ this.client.on('Network.responseReceived', this.responseReceivedHandler);
34
+ this.client.on('Network.loadingFinished', this.loadingFinishedHandler);
35
+ this.client.on('Network.loadingFailed', this.loadingFailedHandler);
36
+ this.isMonitoring = true;
37
+ }
38
+ async stopMonitoring() {
39
+ if (!this.isMonitoring) {
40
+ return;
41
+ }
42
+ if (this.requestWillBeSentHandler) {
43
+ this.client.off('Network.requestWillBeSent', this.requestWillBeSentHandler);
44
+ this.requestWillBeSentHandler = null;
45
+ }
46
+ if (this.responseReceivedHandler) {
47
+ this.client.off('Network.responseReceived', this.responseReceivedHandler);
48
+ this.responseReceivedHandler = null;
49
+ }
50
+ if (this.loadingFinishedHandler) {
51
+ this.client.off('Network.loadingFinished', this.loadingFinishedHandler);
52
+ this.loadingFinishedHandler = null;
53
+ }
54
+ if (this.loadingFailedHandler) {
55
+ this.client.off('Network.loadingFailed', this.loadingFailedHandler);
56
+ this.loadingFailedHandler = null;
57
+ }
58
+ this.isMonitoring = false;
59
+ }
60
+ getRequests(filter) {
61
+ let filteredRequests = [...this.completedRequests];
62
+ if (filter) {
63
+ if (filter.methods && filter.methods.length > 0) {
64
+ filteredRequests = filteredRequests.filter(req => filter.methods.includes(req.method.toUpperCase()));
65
+ }
66
+ if (filter.urlPattern) {
67
+ const pattern = new RegExp(filter.urlPattern, 'i');
68
+ filteredRequests = filteredRequests.filter(req => pattern.test(req.url));
69
+ }
70
+ if (filter.statusCodes && filter.statusCodes.length > 0) {
71
+ filteredRequests = filteredRequests.filter(req => req.status && filter.statusCodes.includes(req.status));
72
+ }
73
+ if (filter.startTime) {
74
+ filteredRequests = filteredRequests.filter(req => req.timestamp >= filter.startTime);
75
+ }
76
+ if (filter.endTime) {
77
+ filteredRequests = filteredRequests.filter(req => req.timestamp <= filter.endTime);
78
+ }
79
+ if (filter.maxRequests && filter.maxRequests > 0) {
80
+ filteredRequests = filteredRequests.slice(-filter.maxRequests);
81
+ }
82
+ }
83
+ return filteredRequests;
84
+ }
85
+ getLatestRequest(filter) {
86
+ const requests = this.getRequests(filter);
87
+ return requests.length > 0 ? requests[requests.length - 1] : null;
88
+ }
89
+ clearRequests() {
90
+ this.requests.clear();
91
+ this.completedRequests = [];
92
+ }
93
+ getRequestCount(filter) {
94
+ return this.getRequests(filter).length;
95
+ }
96
+ isActive() {
97
+ return this.isMonitoring;
98
+ }
99
+ handleRequestWillBeSent(params) {
100
+ try {
101
+ const requestParams = params;
102
+ const networkRequest = {
103
+ requestId: requestParams.requestId,
104
+ url: requestParams.request.url,
105
+ method: requestParams.request.method,
106
+ headers: requestParams.request.headers,
107
+ timestamp: requestParams.wallTime * 1000,
108
+ };
109
+ this.requests.set(requestParams.requestId, networkRequest);
110
+ }
111
+ catch (error) {
112
+ console.error('Error handling requestWillBeSent:', error);
113
+ }
114
+ }
115
+ handleResponseReceived(params) {
116
+ try {
117
+ const responseParams = params;
118
+ const request = this.requests.get(responseParams.requestId);
119
+ if (request) {
120
+ request.status = responseParams.response.status;
121
+ request.responseHeaders = responseParams.response.headers;
122
+ }
123
+ }
124
+ catch (error) {
125
+ console.error('Error handling responseReceived:', error);
126
+ }
127
+ }
128
+ handleLoadingFinished(params) {
129
+ try {
130
+ const loadingParams = params;
131
+ const request = this.requests.get(loadingParams.requestId);
132
+ if (request) {
133
+ this.completedRequests.push(request);
134
+ this.requests.delete(loadingParams.requestId);
135
+ if (this.completedRequests.length > 1000) {
136
+ this.completedRequests = this.completedRequests.slice(-1000);
137
+ }
138
+ }
139
+ }
140
+ catch (error) {
141
+ console.error('Error handling loadingFinished:', error);
142
+ }
143
+ }
144
+ handleLoadingFailed(params) {
145
+ try {
146
+ const failedParams = params;
147
+ const request = this.requests.get(failedParams.requestId);
148
+ if (request) {
149
+ request.status = 0;
150
+ this.completedRequests.push(request);
151
+ this.requests.delete(failedParams.requestId);
152
+ if (this.completedRequests.length > 1000) {
153
+ this.completedRequests = this.completedRequests.slice(-1000);
154
+ }
155
+ }
156
+ }
157
+ catch (error) {
158
+ console.error('Error handling loadingFailed:', error);
159
+ }
160
+ }
161
+ }
162
+ exports.NetworkMonitor = NetworkMonitor;
@@ -0,0 +1,18 @@
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./ConsoleMonitor"), exports);
18
+ __exportStar(require("./NetworkMonitor"), exports);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "chrome-cdp-cli",
3
- "version": "1.0.1",
4
- "description": "Command-line tool for controlling Chrome browser instances via the Chrome DevTools Protocol",
3
+ "version": "1.1.0",
4
+ "description": "Command-line tool for controlling Chrome browser instances via the Chrome DevTools Protocol. Core features: JavaScript execution, screenshot capture, DOM snapshots, console/network monitoring. Additional features available via eval workarounds.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "bin": {
@@ -51,12 +51,12 @@
51
51
  "license": "MIT",
52
52
  "repository": {
53
53
  "type": "git",
54
- "url": "https://github.com/nickxiao42/chrome-devtools-cli.git"
54
+ "url": "git+https://github.com/nicoster/chrome-devtools-cli.git"
55
55
  },
56
56
  "bugs": {
57
- "url": "https://github.com/nickxiao42/chrome-devtools-cli/issues"
57
+ "url": "https://github.com/nicoster/chrome-devtools-cli/issues"
58
58
  },
59
- "homepage": "https://github.com/nickxiao42/chrome-devtools-cli#readme",
59
+ "homepage": "https://github.com/nicoster/chrome-devtools-cli#readme",
60
60
  "dependencies": {
61
61
  "commander": "^11.1.0",
62
62
  "node-fetch": "^2.7.0",