chrome-cdp-cli 2.0.5 → 2.1.1

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,31 +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
- { name: 'target-index', type: 'number', description: 'Target page index (1-based, excludes DevTools windows)' }
166
- ];
232
+ const globalOptionDefs = this.globalOptionDefs;
167
233
  while (i < args.length) {
168
234
  const arg = args[i];
169
- if (arg.startsWith('--')) {
235
+ if (arg.startsWith("--")) {
170
236
  const { option, value, consumed } = this.parseLongOption(arg, args, i, globalOptionDefs);
171
237
  if (option) {
172
238
  globalOptions[option.name] = value;
@@ -185,7 +251,7 @@ class ArgumentParser {
185
251
  break;
186
252
  }
187
253
  }
188
- else if (arg.startsWith('-') && arg.length > 1) {
254
+ else if (arg.startsWith("-") && arg.length > 1) {
189
255
  const { option, value, consumed } = this.parseShortOption(arg, args, i, globalOptionDefs);
190
256
  if (option) {
191
257
  globalOptions[option.name] = value;
@@ -222,25 +288,25 @@ class ArgumentParser {
222
288
  let optionName = arg.substring(2);
223
289
  let value;
224
290
  let consumed = 1;
225
- const equalIndex = optionName.indexOf('=');
291
+ const equalIndex = optionName.indexOf("=");
226
292
  if (equalIndex !== -1) {
227
293
  value = optionName.substring(equalIndex + 1);
228
294
  optionName = optionName.substring(0, equalIndex);
229
295
  }
230
296
  let isNegated = false;
231
- if (optionName.startsWith('no-')) {
297
+ if (optionName.startsWith("no-")) {
232
298
  isNegated = true;
233
299
  optionName = optionName.substring(3);
234
300
  }
235
- const optionDef = optionDefs.find(opt => opt.name === optionName);
301
+ const optionDef = optionDefs.find((opt) => opt.name === optionName);
236
302
  if (!optionDef) {
237
303
  return { option: null, value: null, consumed: 0 };
238
304
  }
239
- if (optionDef.type === 'boolean') {
305
+ if (optionDef.type === "boolean") {
240
306
  value = !isNegated;
241
307
  }
242
308
  else if (value === undefined) {
243
- if (index + 1 < args.length && !args[index + 1].startsWith('-')) {
309
+ if (index + 1 < args.length && !args[index + 1].startsWith("-")) {
244
310
  value = args[index + 1];
245
311
  consumed = 2;
246
312
  }
@@ -256,17 +322,17 @@ class ArgumentParser {
256
322
  if (shortName.length > 1) {
257
323
  return { option: null, value: null, consumed: 0 };
258
324
  }
259
- const optionDef = optionDefs.find(opt => opt.short === shortName);
325
+ const optionDef = optionDefs.find((opt) => opt.short === shortName);
260
326
  if (!optionDef) {
261
327
  return { option: null, value: null, consumed: 0 };
262
328
  }
263
329
  let value;
264
330
  let consumed = 1;
265
- if (optionDef.type === 'boolean') {
331
+ if (optionDef.type === "boolean") {
266
332
  value = true;
267
333
  }
268
334
  else {
269
- if (index + 1 < args.length && !args[index + 1].startsWith('-')) {
335
+ if (index + 1 < args.length && !args[index + 1].startsWith("-")) {
270
336
  value = args[index + 1];
271
337
  consumed = 2;
272
338
  }
@@ -287,49 +353,54 @@ class ArgumentParser {
287
353
  }
288
354
  parseCommandArguments(commandDef, args) {
289
355
  const options = {};
356
+ const lateGlobalOptions = {};
290
357
  const arguments_ = [];
291
358
  const errors = [];
292
359
  let i = 0;
293
360
  while (i < args.length) {
294
361
  const arg = args[i];
295
- if (arg.startsWith('--')) {
362
+ if (arg.startsWith("--")) {
296
363
  try {
297
364
  const { option, value, consumed } = this.parseLongOption(arg, args, i, commandDef.options);
298
365
  if (option) {
299
366
  options[option.name] = value;
300
- if (consumed > 0) {
301
- 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;
302
374
  }
303
375
  else {
376
+ errors.push(`Unknown option: ${arg}`);
304
377
  i++;
305
378
  }
306
379
  }
307
- else {
308
- errors.push(`Unknown option: ${arg}`);
309
- i++;
310
- }
311
380
  }
312
381
  catch (error) {
313
382
  errors.push(error instanceof Error ? error.message : String(error));
314
383
  i++;
315
384
  }
316
385
  }
317
- else if (arg.startsWith('-') && arg.length > 1) {
386
+ else if (arg.startsWith("-") && arg.length > 1) {
318
387
  try {
319
388
  const { option, value, consumed } = this.parseShortOption(arg, args, i, commandDef.options);
320
389
  if (option) {
321
390
  options[option.name] = value;
322
- if (consumed > 0) {
323
- 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;
324
398
  }
325
399
  else {
400
+ errors.push(`Unknown option: ${arg}`);
326
401
  i++;
327
402
  }
328
403
  }
329
- else {
330
- errors.push(`Unknown option: ${arg}`);
331
- i++;
332
- }
333
404
  }
334
405
  catch (error) {
335
406
  errors.push(error instanceof Error ? error.message : String(error));
@@ -341,7 +412,12 @@ class ArgumentParser {
341
412
  i++;
342
413
  }
343
414
  }
344
- return { options, arguments: arguments_, errors };
415
+ return {
416
+ options,
417
+ globalOptions: lateGlobalOptions,
418
+ arguments: arguments_,
419
+ errors,
420
+ };
345
421
  }
346
422
  convertOptionValue(optionDef, value) {
347
423
  if (value === undefined || value === null) {
@@ -349,32 +425,36 @@ class ArgumentParser {
349
425
  }
350
426
  const stringValue = String(value);
351
427
  switch (optionDef.type) {
352
- case 'number': {
428
+ case "number": {
353
429
  const numValue = Number(stringValue);
354
430
  if (isNaN(numValue)) {
355
431
  throw new Error(`Option --${optionDef.name} must be a number, got: ${stringValue}`);
356
432
  }
357
433
  return numValue;
358
434
  }
359
- case 'boolean': {
360
- if (typeof value === 'boolean') {
435
+ case "boolean": {
436
+ if (typeof value === "boolean") {
361
437
  return value;
362
438
  }
363
439
  const lowerValue = stringValue.toLowerCase();
364
- if (lowerValue === 'true' || lowerValue === '1' || lowerValue === 'yes') {
440
+ if (lowerValue === "true" ||
441
+ lowerValue === "1" ||
442
+ lowerValue === "yes") {
365
443
  return true;
366
444
  }
367
- if (lowerValue === 'false' || lowerValue === '0' || lowerValue === 'no') {
445
+ if (lowerValue === "false" ||
446
+ lowerValue === "0" ||
447
+ lowerValue === "no") {
368
448
  return false;
369
449
  }
370
450
  throw new Error(`Option --${optionDef.name} must be a boolean, got: ${stringValue}`);
371
451
  }
372
- case 'array':
452
+ case "array":
373
453
  if (Array.isArray(value)) {
374
454
  return value;
375
455
  }
376
- return stringValue.split(',').map(s => s.trim());
377
- case 'string':
456
+ return stringValue.split(",").map((s) => s.trim());
457
+ case "string":
378
458
  default:
379
459
  return stringValue;
380
460
  }
@@ -384,7 +464,7 @@ class ArgumentParser {
384
464
  const warnings = [];
385
465
  if (optionDef.choices && optionDef.choices.length > 0) {
386
466
  if (!optionDef.choices.includes(String(value))) {
387
- 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(", ")}`);
388
468
  }
389
469
  }
390
470
  if (optionDef.validator) {
@@ -397,24 +477,24 @@ class ArgumentParser {
397
477
  return {
398
478
  valid: errors.length === 0,
399
479
  errors,
400
- warnings
480
+ warnings,
401
481
  };
402
482
  }
403
483
  validateArgumentValue(argDef, value) {
404
484
  const errors = [];
405
485
  const warnings = [];
406
486
  switch (argDef.type) {
407
- case 'number':
487
+ case "number":
408
488
  if (isNaN(Number(value))) {
409
489
  errors.push(`Argument ${argDef.name} must be a number, got: ${value}`);
410
490
  }
411
491
  break;
412
- case 'file':
413
- if (typeof value !== 'string' || value.trim().length === 0) {
492
+ case "file":
493
+ if (typeof value !== "string" || value.trim().length === 0) {
414
494
  errors.push(`Argument ${argDef.name} must be a valid file path`);
415
495
  }
416
496
  break;
417
- case 'url':
497
+ case "url":
418
498
  try {
419
499
  new URL(String(value));
420
500
  }
@@ -433,7 +513,7 @@ class ArgumentParser {
433
513
  return {
434
514
  valid: errors.length === 0,
435
515
  errors,
436
- warnings
516
+ warnings,
437
517
  };
438
518
  }
439
519
  resolveCommandName(commandName) {
@@ -441,41 +521,41 @@ class ArgumentParser {
441
521
  }
442
522
  validateCommandDefinition(command) {
443
523
  const errors = [];
444
- if (!command.name || typeof command.name !== 'string') {
445
- 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");
446
526
  }
447
- if (!command.description || typeof command.description !== 'string') {
448
- 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");
449
529
  }
450
530
  if (!Array.isArray(command.aliases)) {
451
- errors.push('Command aliases must be an array');
531
+ errors.push("Command aliases must be an array");
452
532
  }
453
533
  if (!Array.isArray(command.options)) {
454
- errors.push('Command options must be an array');
534
+ errors.push("Command options must be an array");
455
535
  }
456
536
  if (!Array.isArray(command.arguments)) {
457
- errors.push('Command arguments must be an array');
537
+ errors.push("Command arguments must be an array");
458
538
  }
459
539
  for (const option of command.options) {
460
- if (!option.name || typeof option.name !== 'string') {
540
+ if (!option.name || typeof option.name !== "string") {
461
541
  errors.push(`Option name is required and must be a string`);
462
542
  }
463
- if (!['string', 'number', 'boolean', 'array'].includes(option.type)) {
543
+ if (!["string", "number", "boolean", "array"].includes(option.type)) {
464
544
  errors.push(`Option ${option.name} has invalid type: ${option.type}`);
465
545
  }
466
546
  }
467
547
  for (const arg of command.arguments) {
468
- if (!arg.name || typeof arg.name !== 'string') {
548
+ if (!arg.name || typeof arg.name !== "string") {
469
549
  errors.push(`Argument name is required and must be a string`);
470
550
  }
471
- if (!['string', 'number', 'file', 'url'].includes(arg.type)) {
551
+ if (!["string", "number", "file", "url"].includes(arg.type)) {
472
552
  errors.push(`Argument ${arg.name} has invalid type: ${arg.type}`);
473
553
  }
474
554
  }
475
555
  return {
476
556
  valid: errors.length === 0,
477
557
  errors,
478
- warnings: []
558
+ warnings: [],
479
559
  };
480
560
  }
481
561
  createParseResult(command, options, errors, success, arguments_ = []) {
@@ -485,7 +565,7 @@ class ArgumentParser {
485
565
  options,
486
566
  arguments: arguments_,
487
567
  errors,
488
- warnings: []
568
+ warnings: [],
489
569
  };
490
570
  }
491
571
  getCommands() {