chrome-cdp-cli 2.1.3 → 2.1.5
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
|
@@ -81,6 +81,9 @@ cdp log -f
|
|
|
81
81
|
# Follow network requests in real time
|
|
82
82
|
cdp network -f
|
|
83
83
|
|
|
84
|
+
# List available Chrome pages
|
|
85
|
+
cdp list
|
|
86
|
+
|
|
84
87
|
# Element interactions
|
|
85
88
|
cdp click "#submit-button"
|
|
86
89
|
cdp fill "#email" "user@example.com"
|
|
@@ -126,6 +129,7 @@ Use arrow keys to navigate, Enter to select, `q`/ESC/Ctrl+C to cancel (exits cle
|
|
|
126
129
|
To skip the prompt, pass `-i` before or after the command:
|
|
127
130
|
|
|
128
131
|
```bash
|
|
132
|
+
cdp list
|
|
129
133
|
cdp -i 1 eval "document.title"
|
|
130
134
|
cdp eval "document.title" -i 1 # also works
|
|
131
135
|
```
|
|
@@ -84,6 +84,12 @@ class CLIApplication {
|
|
|
84
84
|
this.configureDebugMode(command.config.debug);
|
|
85
85
|
this.logger.debug("CLIApplication.run called with argv:", argv);
|
|
86
86
|
this.logger.debug("Parsed command:", command);
|
|
87
|
+
if (command.name === "list") {
|
|
88
|
+
const result = await this.executeListCommand(command);
|
|
89
|
+
this.outputResult(result, command);
|
|
90
|
+
return (result.exitCode ||
|
|
91
|
+
(result.success ? CommandRouter_1.ExitCode.SUCCESS : CommandRouter_1.ExitCode.GENERAL_ERROR));
|
|
92
|
+
}
|
|
87
93
|
if (this.needsConnection(command.name)) {
|
|
88
94
|
this.logger.debug("Command needs connection, ensuring connection...");
|
|
89
95
|
try {
|
|
@@ -131,11 +137,65 @@ class CLIApplication {
|
|
|
131
137
|
"help",
|
|
132
138
|
"connect",
|
|
133
139
|
"disconnect",
|
|
140
|
+
"list",
|
|
134
141
|
"install_cursor_command",
|
|
135
142
|
"install_claude_skill",
|
|
136
143
|
];
|
|
137
144
|
return !noConnectionCommands.includes(commandName);
|
|
138
145
|
}
|
|
146
|
+
async executeListCommand(command) {
|
|
147
|
+
try {
|
|
148
|
+
const targets = await this.connectionManager.discoverTargets(command.config.host, command.config.port);
|
|
149
|
+
const selectableTargets = targets
|
|
150
|
+
.filter((target) => target.type === "page")
|
|
151
|
+
.filter((target) => !this.isDevToolsWindow(target));
|
|
152
|
+
if (command.config.outputFormat === "json") {
|
|
153
|
+
return {
|
|
154
|
+
success: true,
|
|
155
|
+
data: {
|
|
156
|
+
count: selectableTargets.length,
|
|
157
|
+
targets: selectableTargets.map((target, index) => ({
|
|
158
|
+
index: index + 1,
|
|
159
|
+
id: target.id,
|
|
160
|
+
title: target.title || "(Untitled)",
|
|
161
|
+
url: target.url,
|
|
162
|
+
type: target.type,
|
|
163
|
+
})),
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
if (selectableTargets.length === 0) {
|
|
168
|
+
return {
|
|
169
|
+
success: true,
|
|
170
|
+
data: `No selectable Chrome pages found at ${command.config.host}:${command.config.port}.\n` +
|
|
171
|
+
"Open a regular tab in Chrome and try again.",
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
const lines = [
|
|
175
|
+
`Found ${selectableTargets.length} selectable Chrome page(s):`,
|
|
176
|
+
"",
|
|
177
|
+
...selectableTargets.flatMap((target, index) => [
|
|
178
|
+
`[${index + 1}] ${target.title || "(Untitled)"}`,
|
|
179
|
+
` ${target.url}`,
|
|
180
|
+
]),
|
|
181
|
+
"",
|
|
182
|
+
"Use `cdp -i <number> <command>` to target one of these pages.",
|
|
183
|
+
];
|
|
184
|
+
return {
|
|
185
|
+
success: true,
|
|
186
|
+
data: lines.join("\n"),
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
return {
|
|
191
|
+
success: false,
|
|
192
|
+
error: error instanceof Error
|
|
193
|
+
? error.message
|
|
194
|
+
: String(error),
|
|
195
|
+
exitCode: CommandRouter_1.ExitCode.CONNECTION_ERROR,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
}
|
|
139
199
|
isDevToolsWindow(target) {
|
|
140
200
|
const url = target.url.toLowerCase();
|
|
141
201
|
const title = target.title.toLowerCase();
|
|
@@ -55,6 +55,28 @@ class CommandSchemaRegistry {
|
|
|
55
55
|
options: [],
|
|
56
56
|
arguments: [],
|
|
57
57
|
});
|
|
58
|
+
this.registerCommand({
|
|
59
|
+
name: "list",
|
|
60
|
+
aliases: ["ls"],
|
|
61
|
+
description: "List available Chrome pages you can target",
|
|
62
|
+
usage: "cdp [global-options] list",
|
|
63
|
+
examples: [
|
|
64
|
+
{
|
|
65
|
+
command: "cdp list",
|
|
66
|
+
description: "List selectable Chrome pages",
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
command: "cdp --format json list",
|
|
70
|
+
description: "List selectable Chrome pages as JSON",
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
command: "cdp --host localhost --port 9223 list",
|
|
74
|
+
description: "List pages from a specific Chrome instance",
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
options: [],
|
|
78
|
+
arguments: [],
|
|
79
|
+
});
|
|
58
80
|
this.registerCommand({
|
|
59
81
|
name: "eval",
|
|
60
82
|
aliases: ["js", "execute"],
|
|
@@ -568,7 +590,7 @@ class CommandSchemaRegistry {
|
|
|
568
590
|
name: "log",
|
|
569
591
|
aliases: ["console"],
|
|
570
592
|
description: "Follow console messages in real-time",
|
|
571
|
-
usage: "cdp log [options]",
|
|
593
|
+
usage: "cdp log [PATTERN] [options]",
|
|
572
594
|
examples: [
|
|
573
595
|
{ command: "cdp log", description: "Follow all console messages" },
|
|
574
596
|
{
|
|
@@ -576,8 +598,20 @@ class CommandSchemaRegistry {
|
|
|
576
598
|
description: "Follow only errors and warnings",
|
|
577
599
|
},
|
|
578
600
|
{
|
|
579
|
-
command:
|
|
580
|
-
description: "Follow messages matching /
|
|
601
|
+
command: "cdp log '\\[AI'",
|
|
602
|
+
description: "Follow messages matching regex /\\[AI/i",
|
|
603
|
+
},
|
|
604
|
+
{
|
|
605
|
+
command: "cdp log -e error -e warn",
|
|
606
|
+
description: "Follow messages matching 'error' OR 'warn'",
|
|
607
|
+
},
|
|
608
|
+
{
|
|
609
|
+
command: "cdp log -F '[AI'",
|
|
610
|
+
description: "Follow messages containing literal string '[AI'",
|
|
611
|
+
},
|
|
612
|
+
{
|
|
613
|
+
command: "cdp log -v 'debug'",
|
|
614
|
+
description: "Follow messages NOT matching 'debug'",
|
|
581
615
|
},
|
|
582
616
|
{
|
|
583
617
|
command: "cdp log --format json",
|
|
@@ -592,9 +626,28 @@ class CommandSchemaRegistry {
|
|
|
592
626
|
type: "string",
|
|
593
627
|
},
|
|
594
628
|
{
|
|
595
|
-
name: "
|
|
596
|
-
|
|
629
|
+
name: "expression",
|
|
630
|
+
short: "e",
|
|
631
|
+
description: "Pattern to match (regex by default). May be specified multiple times (OR logic).",
|
|
597
632
|
type: "string",
|
|
633
|
+
multiple: true,
|
|
634
|
+
},
|
|
635
|
+
{
|
|
636
|
+
name: "fixed-strings",
|
|
637
|
+
short: "F",
|
|
638
|
+
description: "Treat pattern(s) as literal strings, not regexps",
|
|
639
|
+
type: "boolean",
|
|
640
|
+
},
|
|
641
|
+
{
|
|
642
|
+
name: "invert-match",
|
|
643
|
+
short: "v",
|
|
644
|
+
description: "Output messages that do NOT match the pattern",
|
|
645
|
+
type: "boolean",
|
|
646
|
+
},
|
|
647
|
+
{
|
|
648
|
+
name: "case-sensitive",
|
|
649
|
+
description: "Match is case-sensitive (default: case-insensitive)",
|
|
650
|
+
type: "boolean",
|
|
598
651
|
},
|
|
599
652
|
{
|
|
600
653
|
name: "follow",
|
|
@@ -609,7 +662,14 @@ class CommandSchemaRegistry {
|
|
|
609
662
|
choices: ["text", "json", "pretty"],
|
|
610
663
|
},
|
|
611
664
|
],
|
|
612
|
-
arguments: [
|
|
665
|
+
arguments: [
|
|
666
|
+
{
|
|
667
|
+
name: "pattern",
|
|
668
|
+
description: "Pattern to match (regex by default, case-insensitive)",
|
|
669
|
+
type: "string",
|
|
670
|
+
required: false,
|
|
671
|
+
},
|
|
672
|
+
],
|
|
613
673
|
});
|
|
614
674
|
this.registerCommand({
|
|
615
675
|
name: "network",
|
package/dist/cli/HelpSystem.js
CHANGED
|
@@ -163,7 +163,7 @@ class HelpSystem {
|
|
|
163
163
|
orderedCategories.push([cat, categorizedCommands.get(cat)]);
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
|
-
for (const [cat, cmds] of categorizedCommands) {
|
|
166
|
+
for (const [cat, cmds] of Array.from(categorizedCommands.entries())) {
|
|
167
167
|
if (!categoryOrder.includes(cat)) {
|
|
168
168
|
orderedCategories.push([cat, cmds]);
|
|
169
169
|
}
|
|
@@ -310,11 +310,11 @@ class HelpSystem {
|
|
|
310
310
|
}
|
|
311
311
|
}
|
|
312
312
|
}
|
|
313
|
-
return
|
|
313
|
+
return Array.from(new Set(similar)).slice(0, 5);
|
|
314
314
|
}
|
|
315
315
|
findContextualSuggestions(error, commandName) {
|
|
316
316
|
const suggestions = [];
|
|
317
|
-
for (const [errorPattern, helpers] of this.contextualHelpers) {
|
|
317
|
+
for (const [errorPattern, helpers] of Array.from(this.contextualHelpers.entries())) {
|
|
318
318
|
if (error.toLowerCase().includes(errorPattern.toLowerCase())) {
|
|
319
319
|
suggestions.push(...helpers);
|
|
320
320
|
}
|
|
@@ -351,7 +351,7 @@ class HelpSystem {
|
|
|
351
351
|
else if (command.name.includes("install")) {
|
|
352
352
|
category = "Installation & Setup";
|
|
353
353
|
}
|
|
354
|
-
else if (["help", "version"].includes(command.name)) {
|
|
354
|
+
else if (["help", "version", "list"].includes(command.name)) {
|
|
355
355
|
category = "Help & Information";
|
|
356
356
|
}
|
|
357
357
|
if (!categories.has(category)) {
|
|
@@ -359,7 +359,7 @@ class HelpSystem {
|
|
|
359
359
|
}
|
|
360
360
|
categories.get(category).push(command);
|
|
361
361
|
}
|
|
362
|
-
for (const [, categoryCommands] of categories) {
|
|
362
|
+
for (const [, categoryCommands] of Array.from(categories.entries())) {
|
|
363
363
|
categoryCommands.sort((a, b) => a.name.localeCompare(b.name));
|
|
364
364
|
}
|
|
365
365
|
return categories;
|
package/dist/client/CDPClient.js
CHANGED
|
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.CDPClient = void 0;
|
|
7
|
-
const
|
|
7
|
+
const WebSocket = require("ws");
|
|
8
8
|
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
9
9
|
class CDPClient {
|
|
10
10
|
constructor() {
|
|
@@ -26,7 +26,7 @@ class CDPClient {
|
|
|
26
26
|
targetId = pageTarget.id;
|
|
27
27
|
}
|
|
28
28
|
const wsUrl = await this.getWebSocketUrl(host, port, targetId);
|
|
29
|
-
this.ws = new
|
|
29
|
+
this.ws = new WebSocket(wsUrl);
|
|
30
30
|
return new Promise((resolve, reject) => {
|
|
31
31
|
if (!this.ws) {
|
|
32
32
|
reject(new Error('WebSocket not initialized'));
|
|
@@ -20,21 +20,44 @@ class ListConsoleMessagesHandler {
|
|
|
20
20
|
};
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
|
+
buildMatcher(params) {
|
|
24
|
+
const rawExpressions = params.expression
|
|
25
|
+
? Array.isArray(params.expression)
|
|
26
|
+
? params.expression
|
|
27
|
+
: [params.expression]
|
|
28
|
+
: [];
|
|
29
|
+
const patterns = [
|
|
30
|
+
...(params.pattern ? [params.pattern] : []),
|
|
31
|
+
...rawExpressions,
|
|
32
|
+
];
|
|
33
|
+
if (patterns.length === 0)
|
|
34
|
+
return () => true;
|
|
35
|
+
const fixedStrings = params.fixedStrings ?? params["fixed-strings"] ?? false;
|
|
36
|
+
const caseSensitive = params.caseSensitive ?? params["case-sensitive"] ?? false;
|
|
37
|
+
const invertMatch = params.invertMatch ?? params["invert-match"] ?? false;
|
|
38
|
+
const flags = caseSensitive ? "" : "i";
|
|
39
|
+
const tests = patterns.map((p) => {
|
|
40
|
+
if (fixedStrings) {
|
|
41
|
+
return caseSensitive
|
|
42
|
+
? (text) => text.includes(p)
|
|
43
|
+
: (text) => text.toLowerCase().includes(p.toLowerCase());
|
|
44
|
+
}
|
|
45
|
+
const re = new RegExp(p, flags);
|
|
46
|
+
return (text) => re.test(text);
|
|
47
|
+
});
|
|
48
|
+
const anyMatch = (text) => tests.some((t) => t(text));
|
|
49
|
+
return invertMatch ? (text) => !anyMatch(text) : anyMatch;
|
|
50
|
+
}
|
|
23
51
|
async executeFollowMode(client, params) {
|
|
24
52
|
if (!this.consoleMonitor) {
|
|
25
53
|
this.consoleMonitor = new ConsoleMonitor_1.ConsoleMonitor(client);
|
|
26
54
|
}
|
|
27
55
|
await this.consoleMonitor.startMonitoring();
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
filter.types = params.types;
|
|
31
|
-
}
|
|
32
|
-
if (params.textPattern) {
|
|
33
|
-
filter.textPattern = params.textPattern;
|
|
34
|
-
}
|
|
56
|
+
const typeFilter = params.types && params.types.length > 0 ? params.types : null;
|
|
57
|
+
const matchText = this.buildMatcher(params);
|
|
35
58
|
const outputFormat = params.format || "text";
|
|
36
59
|
const messageCallback = (message) => {
|
|
37
|
-
if (!this.shouldOutputMessage(message,
|
|
60
|
+
if (!this.shouldOutputMessage(message, typeFilter, matchText)) {
|
|
38
61
|
return;
|
|
39
62
|
}
|
|
40
63
|
this.outputMessage(message, outputFormat);
|
|
@@ -69,19 +92,11 @@ class ListConsoleMessagesHandler {
|
|
|
69
92
|
isLongRunning: true,
|
|
70
93
|
};
|
|
71
94
|
}
|
|
72
|
-
shouldOutputMessage(message,
|
|
73
|
-
if (
|
|
74
|
-
|
|
75
|
-
return false;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
if (filter.textPattern) {
|
|
79
|
-
const pattern = new RegExp(filter.textPattern, "i");
|
|
80
|
-
if (!pattern.test(message.text)) {
|
|
81
|
-
return false;
|
|
82
|
-
}
|
|
95
|
+
shouldOutputMessage(message, typeFilter, matchText) {
|
|
96
|
+
if (typeFilter && !typeFilter.includes(message.type)) {
|
|
97
|
+
return false;
|
|
83
98
|
}
|
|
84
|
-
return
|
|
99
|
+
return matchText(message.text);
|
|
85
100
|
}
|
|
86
101
|
outputMessage(message, format) {
|
|
87
102
|
switch (format) {
|
|
@@ -142,10 +157,16 @@ class ListConsoleMessagesHandler {
|
|
|
142
157
|
return false;
|
|
143
158
|
}
|
|
144
159
|
}
|
|
145
|
-
if (params.
|
|
146
|
-
typeof params.textPattern !== "string") {
|
|
160
|
+
if (params.pattern !== undefined && typeof params.pattern !== "string") {
|
|
147
161
|
return false;
|
|
148
162
|
}
|
|
163
|
+
if (params.expression !== undefined) {
|
|
164
|
+
const exprs = Array.isArray(params.expression)
|
|
165
|
+
? params.expression
|
|
166
|
+
: [params.expression];
|
|
167
|
+
if (!exprs.every((e) => typeof e === "string"))
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
149
170
|
if (params.format !== undefined) {
|
|
150
171
|
const validFormats = ["text", "json", "pretty"];
|
|
151
172
|
if (typeof params.format !== "string" ||
|
|
@@ -159,21 +180,30 @@ class ListConsoleMessagesHandler {
|
|
|
159
180
|
return `log - Follow console messages in real-time
|
|
160
181
|
|
|
161
182
|
Usage:
|
|
162
|
-
cdp log [options]
|
|
183
|
+
cdp log [PATTERN] [options]
|
|
184
|
+
|
|
185
|
+
Arguments:
|
|
186
|
+
PATTERN Pattern to match (regex, case-insensitive by default)
|
|
163
187
|
|
|
164
188
|
Options:
|
|
165
|
-
--
|
|
166
|
-
--
|
|
167
|
-
--
|
|
189
|
+
-e, --expression <pat> Pattern to match; may be used multiple times (OR logic)
|
|
190
|
+
-F, --fixed-strings Treat pattern(s) as literal strings, not regexps
|
|
191
|
+
-v, --invert-match Output messages that do NOT match the pattern
|
|
192
|
+
--case-sensitive Case-sensitive matching (default: case-insensitive)
|
|
193
|
+
--types <types> Filter by message types (log,info,warn,error,debug)
|
|
194
|
+
--format <format> Output format: text, json, or pretty (default: text)
|
|
168
195
|
-f, --follow Alias flag (follow mode is always active)
|
|
169
196
|
|
|
170
197
|
Examples:
|
|
171
|
-
cdp log
|
|
172
|
-
cdp log
|
|
173
|
-
cdp log
|
|
174
|
-
cdp log
|
|
175
|
-
cdp log
|
|
176
|
-
cdp log
|
|
198
|
+
cdp log # Follow all console messages
|
|
199
|
+
cdp log '\\[AI' # Messages matching regex /\\[AI/i
|
|
200
|
+
cdp log -e error -e warn # Messages containing 'error' OR 'warn'
|
|
201
|
+
cdp log -F '[AI' # Messages containing literal '[AI'
|
|
202
|
+
cdp log -v debug # Messages NOT containing 'debug'
|
|
203
|
+
cdp log '404' --case-sensitive # Case-sensitive match
|
|
204
|
+
cdp log --types error,warn # Filter by type
|
|
205
|
+
cdp log --format json # Output as JSON (one object per line)
|
|
206
|
+
cdp log --format pretty # Colorized output
|
|
177
207
|
|
|
178
208
|
Note:
|
|
179
209
|
This command runs continuously and streams console messages in real-time.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chrome-cdp-cli",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.5",
|
|
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",
|