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.
- package/dist/cli/ArgumentParser.js +172 -91
- package/dist/cli/CLIApplication.js +181 -57
- package/dist/cli/CommandRouter.js +98 -74
- package/dist/cli/CommandSchemaRegistry.js +506 -398
- package/dist/cli/EnhancedCLIInterface.js +2 -1
- package/dist/cli/HelpSystem.js +286 -256
- package/dist/handlers/ClickHandler.js +91 -27
- package/dist/handlers/InstallClaudeSkillHandler.js +220 -220
- package/dist/handlers/InstallCursorCommandHandler.js +60 -60
- package/dist/handlers/ListConsoleMessagesHandler.js +126 -178
- package/dist/handlers/ListNetworkRequestsHandler.js +128 -108
- package/dist/handlers/RestartProxyHandler.js +4 -4
- package/dist/handlers/TakeScreenshotHandler.js +70 -59
- package/dist/handlers/TakeSnapshotHandler.js +223 -165
- package/dist/handlers/index.js +0 -1
- package/dist/monitors/ConsoleMonitor.js +29 -0
- package/dist/monitors/NetworkMonitor.js +43 -19
- package/dist/proxy/server/CDPProxyServer.js +5 -1
- package/dist/proxy/server/CommandExecutionService.js +1 -1
- package/dist/proxy/server/ProxyAPIServer.js +11 -6
- package/package.json +3 -2
|
@@ -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 ===
|
|
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 ===
|
|
56
|
+
if (parseError.message === "VERSION_COMMAND_EXECUTED") {
|
|
60
57
|
return CommandRouter_1.ExitCode.SUCCESS;
|
|
61
58
|
}
|
|
62
|
-
if (parseError.message ===
|
|
59
|
+
if (parseError.message === "HELP_COMMAND_EXECUTED") {
|
|
63
60
|
return CommandRouter_1.ExitCode.SUCCESS;
|
|
64
61
|
}
|
|
65
62
|
}
|
|
@@ -85,49 +82,148 @@ class CLIApplication {
|
|
|
85
82
|
this.logger.setLevel(2);
|
|
86
83
|
}
|
|
87
84
|
this.configureDebugMode(command.config.debug);
|
|
88
|
-
this.logger.debug(
|
|
89
|
-
this.logger.debug(
|
|
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(
|
|
97
|
-
|
|
88
|
+
this.logger.debug("Command needs connection, ensuring connection...");
|
|
89
|
+
try {
|
|
90
|
+
await this.ensureConnection(command);
|
|
91
|
+
}
|
|
92
|
+
catch (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;
|
|
105
|
+
}
|
|
98
106
|
}
|
|
99
|
-
this.logger.debug(
|
|
107
|
+
this.logger.debug("Executing command via CLI interface...");
|
|
100
108
|
const result = await this.cli.execute(command);
|
|
101
|
-
this.logger.debug(
|
|
109
|
+
this.logger.debug("Command execution result:", result);
|
|
102
110
|
this.outputResult(result, command);
|
|
103
|
-
return result.exitCode ||
|
|
111
|
+
return (result.exitCode ||
|
|
112
|
+
(result.success ? CommandRouter_1.ExitCode.SUCCESS : CommandRouter_1.ExitCode.GENERAL_ERROR));
|
|
104
113
|
}
|
|
105
114
|
catch (error) {
|
|
106
|
-
this.logger.debug(
|
|
115
|
+
this.logger.debug("Error in CLIApplication.run:", error);
|
|
107
116
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
108
|
-
console.error(this.formatError(errorMessage, {
|
|
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
|
+
}));
|
|
109
126
|
return CommandRouter_1.ExitCode.GENERAL_ERROR;
|
|
110
127
|
}
|
|
111
128
|
}
|
|
112
|
-
async ensureProxyReady() {
|
|
113
|
-
try {
|
|
114
|
-
const isReady = await this.proxyManager.ensureProxyReady();
|
|
115
|
-
if (!isReady) {
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
catch (error) {
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
129
|
needsConnection(commandName) {
|
|
122
130
|
const noConnectionCommands = [
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
131
|
+
"help",
|
|
132
|
+
"connect",
|
|
133
|
+
"disconnect",
|
|
134
|
+
"install_cursor_command",
|
|
135
|
+
"install_claude_skill",
|
|
128
136
|
];
|
|
129
137
|
return !noConnectionCommands.includes(commandName);
|
|
130
138
|
}
|
|
139
|
+
isDevToolsWindow(target) {
|
|
140
|
+
const url = target.url.toLowerCase();
|
|
141
|
+
const title = target.title.toLowerCase();
|
|
142
|
+
return (url.startsWith("chrome-devtools://") ||
|
|
143
|
+
url.startsWith("devtools://") ||
|
|
144
|
+
title.includes("devtools") ||
|
|
145
|
+
title.includes("chrome devtools"));
|
|
146
|
+
}
|
|
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);
|
|
225
|
+
});
|
|
226
|
+
}
|
|
131
227
|
async ensureConnection(command) {
|
|
132
228
|
if (this.client) {
|
|
133
229
|
return;
|
|
@@ -136,26 +232,55 @@ class CLIApplication {
|
|
|
136
232
|
const targets = await this.connectionManager.discoverTargets(command.config.host, command.config.port);
|
|
137
233
|
if (targets.length === 0) {
|
|
138
234
|
throw new Error(`No Chrome targets found at ${command.config.host}:${command.config.port}. ` +
|
|
139
|
-
|
|
235
|
+
"Make sure Chrome is running with --remote-debugging-port=9222");
|
|
236
|
+
}
|
|
237
|
+
const pageTargets = targets.filter((target) => target.type === "page");
|
|
238
|
+
const nonDevToolsTargets = pageTargets.filter((target) => !this.isDevToolsWindow(target));
|
|
239
|
+
if (nonDevToolsTargets.length === 0) {
|
|
240
|
+
throw new Error("No page targets available (excluding DevTools windows). Open a tab in Chrome.");
|
|
241
|
+
}
|
|
242
|
+
let selectedTarget;
|
|
243
|
+
if (command.config.targetIndex !== undefined) {
|
|
244
|
+
const index = command.config.targetIndex - 1;
|
|
245
|
+
if (index < 0 || index >= 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}.`);
|
|
252
|
+
}
|
|
253
|
+
selectedTarget = nonDevToolsTargets[index];
|
|
254
|
+
}
|
|
255
|
+
else if (nonDevToolsTargets.length === 1) {
|
|
256
|
+
selectedTarget = nonDevToolsTargets[0];
|
|
140
257
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
throw new Error('No page targets available. Open a tab in Chrome.');
|
|
258
|
+
else {
|
|
259
|
+
selectedTarget = await this.selectTargetInteractive(nonDevToolsTargets);
|
|
144
260
|
}
|
|
145
|
-
this.client = await this.connectionManager.connectToTarget(
|
|
261
|
+
this.client = (await this.connectionManager.connectToTarget(selectedTarget));
|
|
146
262
|
this.cli.setClient(this.client);
|
|
147
263
|
if (command.config.verbose) {
|
|
148
|
-
this.logger.info(`Connected to Chrome target: ${
|
|
264
|
+
this.logger.info(`Connected to Chrome target: ${selectedTarget.title} (${selectedTarget.url})`);
|
|
149
265
|
}
|
|
150
266
|
}
|
|
151
267
|
catch (error) {
|
|
152
|
-
|
|
268
|
+
if (error instanceof Error) {
|
|
269
|
+
throw error;
|
|
270
|
+
}
|
|
271
|
+
throw new Error(String(error));
|
|
153
272
|
}
|
|
154
273
|
}
|
|
155
274
|
outputResult(result, command) {
|
|
275
|
+
if (result._rawOutput === true) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
156
278
|
if (command.config.quiet && result.success) {
|
|
157
279
|
return;
|
|
158
280
|
}
|
|
281
|
+
if (result.isLongRunning === true) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
159
284
|
const output = this.cli.formatOutput(result, command.config.outputFormat);
|
|
160
285
|
if (result.success) {
|
|
161
286
|
console.log(output);
|
|
@@ -169,45 +294,50 @@ class CLIApplication {
|
|
|
169
294
|
const args = argv.slice(2);
|
|
170
295
|
for (let i = 0; i < args.length; i++) {
|
|
171
296
|
const arg = args[i];
|
|
172
|
-
if (arg ===
|
|
297
|
+
if (arg === "--config" || arg === "-c") {
|
|
173
298
|
if (i + 1 < args.length) {
|
|
174
299
|
options.configFile = args[i + 1];
|
|
175
300
|
}
|
|
176
301
|
}
|
|
177
|
-
else if (arg ===
|
|
302
|
+
else if (arg === "--profile") {
|
|
178
303
|
if (i + 1 < args.length) {
|
|
179
304
|
options.profile = args[i + 1];
|
|
180
305
|
}
|
|
181
306
|
}
|
|
182
|
-
else if (arg ===
|
|
307
|
+
else if (arg === "--host" || arg === "-h") {
|
|
183
308
|
if (i + 1 < args.length) {
|
|
184
309
|
options.host = args[i + 1];
|
|
185
310
|
}
|
|
186
311
|
}
|
|
187
|
-
else if (arg ===
|
|
312
|
+
else if (arg === "--port" || arg === "-p") {
|
|
188
313
|
if (i + 1 < args.length) {
|
|
189
314
|
options.port = parseInt(args[i + 1], 10);
|
|
190
315
|
}
|
|
191
316
|
}
|
|
192
|
-
else if (arg ===
|
|
317
|
+
else if (arg === "--timeout" || arg === "-t") {
|
|
193
318
|
if (i + 1 < args.length) {
|
|
194
319
|
options.timeout = parseInt(args[i + 1], 10);
|
|
195
320
|
}
|
|
196
321
|
}
|
|
197
|
-
else if (arg ===
|
|
322
|
+
else if (arg === "--format" || arg === "-f") {
|
|
198
323
|
if (i + 1 < args.length) {
|
|
199
324
|
options.outputFormat = args[i + 1];
|
|
200
325
|
}
|
|
201
326
|
}
|
|
202
|
-
else if (arg ===
|
|
327
|
+
else if (arg === "--verbose" || arg === "-v") {
|
|
203
328
|
options.verbose = true;
|
|
204
329
|
}
|
|
205
|
-
else if (arg ===
|
|
330
|
+
else if (arg === "--quiet" || arg === "-q") {
|
|
206
331
|
options.quiet = true;
|
|
207
332
|
}
|
|
208
|
-
else if (arg ===
|
|
333
|
+
else if (arg === "--debug" || arg === "-d") {
|
|
209
334
|
options.debug = true;
|
|
210
335
|
}
|
|
336
|
+
else if (arg === "--target-index") {
|
|
337
|
+
if (i + 1 < args.length) {
|
|
338
|
+
options.targetIndex = parseInt(args[i + 1], 10);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
211
341
|
}
|
|
212
342
|
return options;
|
|
213
343
|
}
|
|
@@ -220,15 +350,9 @@ class CLIApplication {
|
|
|
220
350
|
await this.client.disconnect();
|
|
221
351
|
}
|
|
222
352
|
catch (error) {
|
|
223
|
-
this.logger.error(
|
|
353
|
+
this.logger.error("Error during shutdown:", error);
|
|
224
354
|
}
|
|
225
355
|
}
|
|
226
|
-
try {
|
|
227
|
-
await this.proxyManager.shutdown();
|
|
228
|
-
}
|
|
229
|
-
catch (error) {
|
|
230
|
-
this.logger.error('Error shutting down proxy manager:', error);
|
|
231
|
-
}
|
|
232
356
|
}
|
|
233
357
|
getCLI() {
|
|
234
358
|
return this.cli;
|