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.
@@ -6,23 +6,87 @@ class ArgumentParser {
6
6
  constructor() {
7
7
  this.commands = new Map();
8
8
  this.aliases = new Map();
9
+ this.globalOptionDefs = [
10
+ {
11
+ name: "host",
12
+ short: "h",
13
+ type: "string",
14
+ description: "Chrome host address",
15
+ default: "localhost",
16
+ },
17
+ {
18
+ name: "port",
19
+ short: "p",
20
+ type: "number",
21
+ description: "DevTools port",
22
+ default: 9222,
23
+ },
24
+ {
25
+ name: "format",
26
+ short: "f",
27
+ type: "string",
28
+ description: "Output format",
29
+ choices: ["json", "text"],
30
+ default: "text",
31
+ },
32
+ {
33
+ name: "verbose",
34
+ short: "v",
35
+ type: "boolean",
36
+ description: "Enable verbose logging",
37
+ default: false,
38
+ },
39
+ {
40
+ name: "quiet",
41
+ short: "q",
42
+ type: "boolean",
43
+ description: "Enable quiet mode",
44
+ default: false,
45
+ },
46
+ {
47
+ name: "timeout",
48
+ short: "t",
49
+ type: "number",
50
+ description: "Command timeout in milliseconds",
51
+ default: 30000,
52
+ },
53
+ {
54
+ name: "debug",
55
+ short: "d",
56
+ type: "boolean",
57
+ description: "Enable debug logging",
58
+ default: false,
59
+ },
60
+ {
61
+ name: "config",
62
+ short: "c",
63
+ type: "string",
64
+ description: "Configuration file path",
65
+ },
66
+ {
67
+ name: "target-index",
68
+ short: "i",
69
+ type: "number",
70
+ description: "Target page index (1-based, excludes DevTools windows)",
71
+ },
72
+ ];
9
73
  this.helpSystem = new HelpSystem_1.HelpSystem(undefined, this);
10
74
  }
11
75
  parseArguments(argv) {
12
76
  try {
13
77
  if (!Array.isArray(argv)) {
14
- return this.createParseResult('help', {}, [], true);
78
+ return this.createParseResult("help", {}, [], true);
15
79
  }
16
80
  const sliceStart = Math.max(0, Math.min(2, argv.length));
17
81
  const args = argv.slice(sliceStart);
18
82
  if (args.length === 0) {
19
- return this.createParseResult('help', {}, [], true);
83
+ return this.createParseResult("help", {}, [], true);
20
84
  }
21
- if (args.includes('--help')) {
22
- return this.createParseResult('help', {}, [], true);
85
+ if (args.includes("--help")) {
86
+ return this.createParseResult("help", {}, [], true);
23
87
  }
24
- if (args.includes('--version') || args.includes('-V')) {
25
- return this.createParseResult('version', {}, [], true);
88
+ if (args.includes("--version") || args.includes("-V")) {
89
+ return this.createParseResult("version", {}, [], true);
26
90
  }
27
91
  let globalOptions;
28
92
  let commandName;
@@ -34,41 +98,49 @@ class ArgumentParser {
34
98
  commandArgs = parseResult.commandArgs;
35
99
  }
36
100
  catch (parseError) {
37
- if (args.some(arg => arg === '--help' || arg === 'help')) {
38
- return this.createParseResult('help', {}, [], true);
101
+ if (args.some((arg) => arg === "--help" || arg === "help")) {
102
+ return this.createParseResult("help", {}, [], true);
39
103
  }
40
104
  throw parseError;
41
105
  }
42
106
  const resolvedCommand = this.resolveCommandName(commandName);
43
- if (resolvedCommand === 'help') {
107
+ if (resolvedCommand === "help") {
44
108
  return this.createParseResult(resolvedCommand, globalOptions, [], true, commandArgs);
45
109
  }
46
110
  const commandDef = this.commands.get(resolvedCommand);
47
111
  if (!commandDef) {
48
- return this.createParseResult(resolvedCommand, globalOptions, [`Unknown command: ${commandName}. Use 'help' to see available commands.`], false);
112
+ return this.createParseResult(resolvedCommand, globalOptions, [
113
+ `Unknown command: ${commandName}. Use 'help' to see available commands.`,
114
+ ], false);
49
115
  }
50
116
  const parseResult = this.parseCommandArguments(commandDef, commandArgs);
51
- const allOptions = { ...globalOptions, ...parseResult.options };
117
+ const allOptions = {
118
+ ...globalOptions,
119
+ ...parseResult.globalOptions,
120
+ ...parseResult.options,
121
+ };
52
122
  return this.createParseResult(resolvedCommand, allOptions, parseResult.errors, parseResult.errors.length === 0, parseResult.arguments);
53
123
  }
54
124
  catch (error) {
55
125
  const errorMessage = error instanceof Error ? error.message : String(error);
56
126
  if (process.env.DEBUG) {
57
- console.error('Parse error details:', error);
127
+ console.error("Parse error details:", error);
58
128
  if (error instanceof Error && error.stack) {
59
- console.error('Stack trace:', error.stack);
129
+ console.error("Stack trace:", error.stack);
60
130
  }
61
131
  }
62
- if (errorMessage.includes('Invalid count value')) {
63
- return this.createParseResult('help', {}, [`Parse error: Invalid argument format. Please check your command syntax.`], false);
132
+ if (errorMessage.includes("Invalid count value")) {
133
+ return this.createParseResult("help", {}, [
134
+ `Parse error: Invalid argument format. Please check your command syntax.`,
135
+ ], false);
64
136
  }
65
- return this.createParseResult('help', {}, [`Parse error: ${errorMessage}`], false);
137
+ return this.createParseResult("help", {}, [`Parse error: ${errorMessage}`], false);
66
138
  }
67
139
  }
68
140
  registerCommand(command) {
69
141
  const validation = this.validateCommandDefinition(command);
70
142
  if (!validation.valid) {
71
- throw new Error(`Invalid command definition: ${validation.errors.join(', ')}`);
143
+ throw new Error(`Invalid command definition: ${validation.errors.join(", ")}`);
72
144
  }
73
145
  this.commands.set(command.name, command);
74
146
  for (const alias of command.aliases) {
@@ -80,7 +152,7 @@ class ArgumentParser {
80
152
  }
81
153
  generateHelp(command) {
82
154
  if (command) {
83
- if (command.startsWith('topic ')) {
155
+ if (command.startsWith("topic ")) {
84
156
  const topicName = command.substring(6);
85
157
  return this.helpSystem.generateTopicHelp(topicName);
86
158
  }
@@ -94,7 +166,7 @@ class ArgumentParser {
94
166
  return {
95
167
  valid: false,
96
168
  errors: [`Unknown command: ${command}`],
97
- warnings: []
169
+ warnings: [],
98
170
  };
99
171
  }
100
172
  const errors = [];
@@ -104,23 +176,27 @@ class ArgumentParser {
104
176
  errors.push(`Required option --${option.name} is missing`);
105
177
  }
106
178
  }
107
- const requiredArgs = commandDef.arguments.filter(arg => arg.required);
179
+ const requiredArgs = commandDef.arguments.filter((arg) => arg.required);
108
180
  if (args.arguments.length < requiredArgs.length) {
109
181
  const providedCount = Math.max(0, Math.min(args.arguments.length, requiredArgs.length));
110
182
  const missingCount = Math.max(0, requiredArgs.length - providedCount);
111
183
  if (missingCount > 0) {
112
184
  const startIndex = Math.max(0, providedCount);
113
185
  const endIndex = Math.min(requiredArgs.length, startIndex + missingCount);
114
- if (startIndex < endIndex && startIndex >= 0 && endIndex <= requiredArgs.length) {
115
- const missingArgs = requiredArgs.slice(startIndex, endIndex).map(arg => arg.name);
186
+ if (startIndex < endIndex &&
187
+ startIndex >= 0 &&
188
+ endIndex <= requiredArgs.length) {
189
+ const missingArgs = requiredArgs
190
+ .slice(startIndex, endIndex)
191
+ .map((arg) => arg.name);
116
192
  if (missingArgs.length > 0) {
117
- errors.push(`Missing required arguments: ${missingArgs.join(', ')}`);
193
+ errors.push(`Missing required arguments: ${missingArgs.join(", ")}`);
118
194
  }
119
195
  }
120
196
  }
121
197
  }
122
198
  for (const [optionName, value] of Object.entries(args.options)) {
123
- const optionDef = commandDef.options.find(opt => opt.name === optionName);
199
+ const optionDef = commandDef.options.find((opt) => opt.name === optionName);
124
200
  if (optionDef) {
125
201
  const optionValidation = this.validateOptionValue(optionDef, value);
126
202
  if (!optionValidation.valid) {
@@ -142,30 +218,21 @@ class ArgumentParser {
142
218
  return {
143
219
  valid: errors.length === 0,
144
220
  errors,
145
- warnings
221
+ warnings,
146
222
  };
147
223
  }
148
224
  parseGlobalAndCommand(args) {
149
- if (args.includes('--help')) {
150
- return { globalOptions: {}, commandName: 'help', commandArgs: [] };
225
+ if (args.includes("--help")) {
226
+ return { globalOptions: {}, commandName: "help", commandArgs: [] };
151
227
  }
152
228
  const globalOptions = {};
153
- let commandName = 'help';
229
+ let commandName = "help";
154
230
  let commandArgs = [];
155
231
  let i = 0;
156
- const globalOptionDefs = [
157
- { name: 'host', short: 'h', type: 'string', description: 'Chrome host address', default: 'localhost' },
158
- { name: 'port', short: 'p', type: 'number', description: 'DevTools port', default: 9222 },
159
- { name: 'format', short: 'f', type: 'string', description: 'Output format', choices: ['json', 'text'], default: 'text' },
160
- { name: 'verbose', short: 'v', type: 'boolean', description: 'Enable verbose logging', default: false },
161
- { name: 'quiet', short: 'q', type: 'boolean', description: 'Enable quiet mode', default: false },
162
- { name: 'timeout', short: 't', type: 'number', description: 'Command timeout in milliseconds', default: 30000 },
163
- { name: 'debug', short: 'd', type: 'boolean', description: 'Enable debug logging', default: false },
164
- { name: 'config', short: 'c', type: 'string', description: 'Configuration file path' }
165
- ];
232
+ const globalOptionDefs = this.globalOptionDefs;
166
233
  while (i < args.length) {
167
234
  const arg = args[i];
168
- if (arg.startsWith('--')) {
235
+ if (arg.startsWith("--")) {
169
236
  const { option, value, consumed } = this.parseLongOption(arg, args, i, globalOptionDefs);
170
237
  if (option) {
171
238
  globalOptions[option.name] = value;
@@ -184,7 +251,7 @@ class ArgumentParser {
184
251
  break;
185
252
  }
186
253
  }
187
- else if (arg.startsWith('-') && arg.length > 1) {
254
+ else if (arg.startsWith("-") && arg.length > 1) {
188
255
  const { option, value, consumed } = this.parseShortOption(arg, args, i, globalOptionDefs);
189
256
  if (option) {
190
257
  globalOptions[option.name] = value;
@@ -221,25 +288,25 @@ class ArgumentParser {
221
288
  let optionName = arg.substring(2);
222
289
  let value;
223
290
  let consumed = 1;
224
- const equalIndex = optionName.indexOf('=');
291
+ const equalIndex = optionName.indexOf("=");
225
292
  if (equalIndex !== -1) {
226
293
  value = optionName.substring(equalIndex + 1);
227
294
  optionName = optionName.substring(0, equalIndex);
228
295
  }
229
296
  let isNegated = false;
230
- if (optionName.startsWith('no-')) {
297
+ if (optionName.startsWith("no-")) {
231
298
  isNegated = true;
232
299
  optionName = optionName.substring(3);
233
300
  }
234
- const optionDef = optionDefs.find(opt => opt.name === optionName);
301
+ const optionDef = optionDefs.find((opt) => opt.name === optionName);
235
302
  if (!optionDef) {
236
303
  return { option: null, value: null, consumed: 0 };
237
304
  }
238
- if (optionDef.type === 'boolean') {
305
+ if (optionDef.type === "boolean") {
239
306
  value = !isNegated;
240
307
  }
241
308
  else if (value === undefined) {
242
- if (index + 1 < args.length && !args[index + 1].startsWith('-')) {
309
+ if (index + 1 < args.length && !args[index + 1].startsWith("-")) {
243
310
  value = args[index + 1];
244
311
  consumed = 2;
245
312
  }
@@ -255,17 +322,17 @@ class ArgumentParser {
255
322
  if (shortName.length > 1) {
256
323
  return { option: null, value: null, consumed: 0 };
257
324
  }
258
- const optionDef = optionDefs.find(opt => opt.short === shortName);
325
+ const optionDef = optionDefs.find((opt) => opt.short === shortName);
259
326
  if (!optionDef) {
260
327
  return { option: null, value: null, consumed: 0 };
261
328
  }
262
329
  let value;
263
330
  let consumed = 1;
264
- if (optionDef.type === 'boolean') {
331
+ if (optionDef.type === "boolean") {
265
332
  value = true;
266
333
  }
267
334
  else {
268
- if (index + 1 < args.length && !args[index + 1].startsWith('-')) {
335
+ if (index + 1 < args.length && !args[index + 1].startsWith("-")) {
269
336
  value = args[index + 1];
270
337
  consumed = 2;
271
338
  }
@@ -286,49 +353,54 @@ class ArgumentParser {
286
353
  }
287
354
  parseCommandArguments(commandDef, args) {
288
355
  const options = {};
356
+ const lateGlobalOptions = {};
289
357
  const arguments_ = [];
290
358
  const errors = [];
291
359
  let i = 0;
292
360
  while (i < args.length) {
293
361
  const arg = args[i];
294
- if (arg.startsWith('--')) {
362
+ if (arg.startsWith("--")) {
295
363
  try {
296
364
  const { option, value, consumed } = this.parseLongOption(arg, args, i, commandDef.options);
297
365
  if (option) {
298
366
  options[option.name] = value;
299
- if (consumed > 0) {
300
- i += consumed;
367
+ i += consumed > 0 ? consumed : 1;
368
+ }
369
+ else {
370
+ const { option: gOpt, value: gVal, consumed: gConsumed, } = this.parseLongOption(arg, args, i, this.globalOptionDefs);
371
+ if (gOpt) {
372
+ lateGlobalOptions[gOpt.name] = gVal;
373
+ i += gConsumed > 0 ? gConsumed : 1;
301
374
  }
302
375
  else {
376
+ errors.push(`Unknown option: ${arg}`);
303
377
  i++;
304
378
  }
305
379
  }
306
- else {
307
- errors.push(`Unknown option: ${arg}`);
308
- i++;
309
- }
310
380
  }
311
381
  catch (error) {
312
382
  errors.push(error instanceof Error ? error.message : String(error));
313
383
  i++;
314
384
  }
315
385
  }
316
- else if (arg.startsWith('-') && arg.length > 1) {
386
+ else if (arg.startsWith("-") && arg.length > 1) {
317
387
  try {
318
388
  const { option, value, consumed } = this.parseShortOption(arg, args, i, commandDef.options);
319
389
  if (option) {
320
390
  options[option.name] = value;
321
- if (consumed > 0) {
322
- i += consumed;
391
+ i += consumed > 0 ? consumed : 1;
392
+ }
393
+ else {
394
+ const { option: gOpt, value: gVal, consumed: gConsumed, } = this.parseShortOption(arg, args, i, this.globalOptionDefs);
395
+ if (gOpt) {
396
+ lateGlobalOptions[gOpt.name] = gVal;
397
+ i += gConsumed > 0 ? gConsumed : 1;
323
398
  }
324
399
  else {
400
+ errors.push(`Unknown option: ${arg}`);
325
401
  i++;
326
402
  }
327
403
  }
328
- else {
329
- errors.push(`Unknown option: ${arg}`);
330
- i++;
331
- }
332
404
  }
333
405
  catch (error) {
334
406
  errors.push(error instanceof Error ? error.message : String(error));
@@ -340,7 +412,12 @@ class ArgumentParser {
340
412
  i++;
341
413
  }
342
414
  }
343
- return { options, arguments: arguments_, errors };
415
+ return {
416
+ options,
417
+ globalOptions: lateGlobalOptions,
418
+ arguments: arguments_,
419
+ errors,
420
+ };
344
421
  }
345
422
  convertOptionValue(optionDef, value) {
346
423
  if (value === undefined || value === null) {
@@ -348,32 +425,36 @@ class ArgumentParser {
348
425
  }
349
426
  const stringValue = String(value);
350
427
  switch (optionDef.type) {
351
- case 'number': {
428
+ case "number": {
352
429
  const numValue = Number(stringValue);
353
430
  if (isNaN(numValue)) {
354
431
  throw new Error(`Option --${optionDef.name} must be a number, got: ${stringValue}`);
355
432
  }
356
433
  return numValue;
357
434
  }
358
- case 'boolean': {
359
- if (typeof value === 'boolean') {
435
+ case "boolean": {
436
+ if (typeof value === "boolean") {
360
437
  return value;
361
438
  }
362
439
  const lowerValue = stringValue.toLowerCase();
363
- if (lowerValue === 'true' || lowerValue === '1' || lowerValue === 'yes') {
440
+ if (lowerValue === "true" ||
441
+ lowerValue === "1" ||
442
+ lowerValue === "yes") {
364
443
  return true;
365
444
  }
366
- if (lowerValue === 'false' || lowerValue === '0' || lowerValue === 'no') {
445
+ if (lowerValue === "false" ||
446
+ lowerValue === "0" ||
447
+ lowerValue === "no") {
367
448
  return false;
368
449
  }
369
450
  throw new Error(`Option --${optionDef.name} must be a boolean, got: ${stringValue}`);
370
451
  }
371
- case 'array':
452
+ case "array":
372
453
  if (Array.isArray(value)) {
373
454
  return value;
374
455
  }
375
- return stringValue.split(',').map(s => s.trim());
376
- case 'string':
456
+ return stringValue.split(",").map((s) => s.trim());
457
+ case "string":
377
458
  default:
378
459
  return stringValue;
379
460
  }
@@ -383,7 +464,7 @@ class ArgumentParser {
383
464
  const warnings = [];
384
465
  if (optionDef.choices && optionDef.choices.length > 0) {
385
466
  if (!optionDef.choices.includes(String(value))) {
386
- errors.push(`Option --${optionDef.name} must be one of: ${optionDef.choices.join(', ')}`);
467
+ errors.push(`Option --${optionDef.name} must be one of: ${optionDef.choices.join(", ")}`);
387
468
  }
388
469
  }
389
470
  if (optionDef.validator) {
@@ -396,24 +477,24 @@ class ArgumentParser {
396
477
  return {
397
478
  valid: errors.length === 0,
398
479
  errors,
399
- warnings
480
+ warnings,
400
481
  };
401
482
  }
402
483
  validateArgumentValue(argDef, value) {
403
484
  const errors = [];
404
485
  const warnings = [];
405
486
  switch (argDef.type) {
406
- case 'number':
487
+ case "number":
407
488
  if (isNaN(Number(value))) {
408
489
  errors.push(`Argument ${argDef.name} must be a number, got: ${value}`);
409
490
  }
410
491
  break;
411
- case 'file':
412
- if (typeof value !== 'string' || value.trim().length === 0) {
492
+ case "file":
493
+ if (typeof value !== "string" || value.trim().length === 0) {
413
494
  errors.push(`Argument ${argDef.name} must be a valid file path`);
414
495
  }
415
496
  break;
416
- case 'url':
497
+ case "url":
417
498
  try {
418
499
  new URL(String(value));
419
500
  }
@@ -432,7 +513,7 @@ class ArgumentParser {
432
513
  return {
433
514
  valid: errors.length === 0,
434
515
  errors,
435
- warnings
516
+ warnings,
436
517
  };
437
518
  }
438
519
  resolveCommandName(commandName) {
@@ -440,41 +521,41 @@ class ArgumentParser {
440
521
  }
441
522
  validateCommandDefinition(command) {
442
523
  const errors = [];
443
- if (!command.name || typeof command.name !== 'string') {
444
- errors.push('Command name is required and must be a string');
524
+ if (!command.name || typeof command.name !== "string") {
525
+ errors.push("Command name is required and must be a string");
445
526
  }
446
- if (!command.description || typeof command.description !== 'string') {
447
- errors.push('Command description is required and must be a string');
527
+ if (!command.description || typeof command.description !== "string") {
528
+ errors.push("Command description is required and must be a string");
448
529
  }
449
530
  if (!Array.isArray(command.aliases)) {
450
- errors.push('Command aliases must be an array');
531
+ errors.push("Command aliases must be an array");
451
532
  }
452
533
  if (!Array.isArray(command.options)) {
453
- errors.push('Command options must be an array');
534
+ errors.push("Command options must be an array");
454
535
  }
455
536
  if (!Array.isArray(command.arguments)) {
456
- errors.push('Command arguments must be an array');
537
+ errors.push("Command arguments must be an array");
457
538
  }
458
539
  for (const option of command.options) {
459
- if (!option.name || typeof option.name !== 'string') {
540
+ if (!option.name || typeof option.name !== "string") {
460
541
  errors.push(`Option name is required and must be a string`);
461
542
  }
462
- if (!['string', 'number', 'boolean', 'array'].includes(option.type)) {
543
+ if (!["string", "number", "boolean", "array"].includes(option.type)) {
463
544
  errors.push(`Option ${option.name} has invalid type: ${option.type}`);
464
545
  }
465
546
  }
466
547
  for (const arg of command.arguments) {
467
- if (!arg.name || typeof arg.name !== 'string') {
548
+ if (!arg.name || typeof arg.name !== "string") {
468
549
  errors.push(`Argument name is required and must be a string`);
469
550
  }
470
- if (!['string', 'number', 'file', 'url'].includes(arg.type)) {
551
+ if (!["string", "number", "file", "url"].includes(arg.type)) {
471
552
  errors.push(`Argument ${arg.name} has invalid type: ${arg.type}`);
472
553
  }
473
554
  }
474
555
  return {
475
556
  valid: errors.length === 0,
476
557
  errors,
477
- warnings: []
558
+ warnings: [],
478
559
  };
479
560
  }
480
561
  createParseResult(command, options, errors, success, arguments_ = []) {
@@ -484,7 +565,7 @@ class ArgumentParser {
484
565
  options,
485
566
  arguments: arguments_,
486
567
  errors,
487
- warnings: []
568
+ warnings: [],
488
569
  };
489
570
  }
490
571
  getCommands() {