@a8techads/cli 0.2.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.
Files changed (2) hide show
  1. package/dist/a8techads.js +4491 -0
  2. package/package.json +26 -0
@@ -0,0 +1,4491 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from "node:module";
3
+ var __create = Object.create;
4
+ var __getProtoOf = Object.getPrototypeOf;
5
+ var __defProp = Object.defineProperty;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ function __accessProp(key) {
9
+ return this[key];
10
+ }
11
+ var __toESMCache_node;
12
+ var __toESMCache_esm;
13
+ var __toESM = (mod, isNodeMode, target) => {
14
+ var canCache = mod != null && typeof mod === "object";
15
+ if (canCache) {
16
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
17
+ var cached = cache.get(mod);
18
+ if (cached)
19
+ return cached;
20
+ }
21
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
22
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
23
+ for (let key of __getOwnPropNames(mod))
24
+ if (!__hasOwnProp.call(to, key))
25
+ __defProp(to, key, {
26
+ get: __accessProp.bind(mod, key),
27
+ enumerable: true
28
+ });
29
+ if (canCache)
30
+ cache.set(mod, to);
31
+ return to;
32
+ };
33
+ var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
34
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
35
+
36
+ // node_modules/commander/lib/error.js
37
+ var require_error = __commonJS((exports) => {
38
+ class CommanderError extends Error {
39
+ constructor(exitCode, code, message) {
40
+ super(message);
41
+ Error.captureStackTrace(this, this.constructor);
42
+ this.name = this.constructor.name;
43
+ this.code = code;
44
+ this.exitCode = exitCode;
45
+ this.nestedError = undefined;
46
+ }
47
+ }
48
+
49
+ class InvalidArgumentError extends CommanderError {
50
+ constructor(message) {
51
+ super(1, "commander.invalidArgument", message);
52
+ Error.captureStackTrace(this, this.constructor);
53
+ this.name = this.constructor.name;
54
+ }
55
+ }
56
+ exports.CommanderError = CommanderError;
57
+ exports.InvalidArgumentError = InvalidArgumentError;
58
+ });
59
+
60
+ // node_modules/commander/lib/argument.js
61
+ var require_argument = __commonJS((exports) => {
62
+ var { InvalidArgumentError } = require_error();
63
+
64
+ class Argument {
65
+ constructor(name, description) {
66
+ this.description = description || "";
67
+ this.variadic = false;
68
+ this.parseArg = undefined;
69
+ this.defaultValue = undefined;
70
+ this.defaultValueDescription = undefined;
71
+ this.argChoices = undefined;
72
+ switch (name[0]) {
73
+ case "<":
74
+ this.required = true;
75
+ this._name = name.slice(1, -1);
76
+ break;
77
+ case "[":
78
+ this.required = false;
79
+ this._name = name.slice(1, -1);
80
+ break;
81
+ default:
82
+ this.required = true;
83
+ this._name = name;
84
+ break;
85
+ }
86
+ if (this._name.length > 3 && this._name.slice(-3) === "...") {
87
+ this.variadic = true;
88
+ this._name = this._name.slice(0, -3);
89
+ }
90
+ }
91
+ name() {
92
+ return this._name;
93
+ }
94
+ _concatValue(value, previous) {
95
+ if (previous === this.defaultValue || !Array.isArray(previous)) {
96
+ return [value];
97
+ }
98
+ return previous.concat(value);
99
+ }
100
+ default(value, description) {
101
+ this.defaultValue = value;
102
+ this.defaultValueDescription = description;
103
+ return this;
104
+ }
105
+ argParser(fn) {
106
+ this.parseArg = fn;
107
+ return this;
108
+ }
109
+ choices(values) {
110
+ this.argChoices = values.slice();
111
+ this.parseArg = (arg, previous) => {
112
+ if (!this.argChoices.includes(arg)) {
113
+ throw new InvalidArgumentError(`Allowed choices are ${this.argChoices.join(", ")}.`);
114
+ }
115
+ if (this.variadic) {
116
+ return this._concatValue(arg, previous);
117
+ }
118
+ return arg;
119
+ };
120
+ return this;
121
+ }
122
+ argRequired() {
123
+ this.required = true;
124
+ return this;
125
+ }
126
+ argOptional() {
127
+ this.required = false;
128
+ return this;
129
+ }
130
+ }
131
+ function humanReadableArgName(arg) {
132
+ const nameOutput = arg.name() + (arg.variadic === true ? "..." : "");
133
+ return arg.required ? "<" + nameOutput + ">" : "[" + nameOutput + "]";
134
+ }
135
+ exports.Argument = Argument;
136
+ exports.humanReadableArgName = humanReadableArgName;
137
+ });
138
+
139
+ // node_modules/commander/lib/help.js
140
+ var require_help = __commonJS((exports) => {
141
+ var { humanReadableArgName } = require_argument();
142
+
143
+ class Help {
144
+ constructor() {
145
+ this.helpWidth = undefined;
146
+ this.minWidthToWrap = 40;
147
+ this.sortSubcommands = false;
148
+ this.sortOptions = false;
149
+ this.showGlobalOptions = false;
150
+ }
151
+ prepareContext(contextOptions) {
152
+ this.helpWidth = this.helpWidth ?? contextOptions.helpWidth ?? 80;
153
+ }
154
+ visibleCommands(cmd) {
155
+ const visibleCommands = cmd.commands.filter((cmd2) => !cmd2._hidden);
156
+ const helpCommand = cmd._getHelpCommand();
157
+ if (helpCommand && !helpCommand._hidden) {
158
+ visibleCommands.push(helpCommand);
159
+ }
160
+ if (this.sortSubcommands) {
161
+ visibleCommands.sort((a, b) => {
162
+ return a.name().localeCompare(b.name());
163
+ });
164
+ }
165
+ return visibleCommands;
166
+ }
167
+ compareOptions(a, b) {
168
+ const getSortKey = (option) => {
169
+ return option.short ? option.short.replace(/^-/, "") : option.long.replace(/^--/, "");
170
+ };
171
+ return getSortKey(a).localeCompare(getSortKey(b));
172
+ }
173
+ visibleOptions(cmd) {
174
+ const visibleOptions = cmd.options.filter((option) => !option.hidden);
175
+ const helpOption = cmd._getHelpOption();
176
+ if (helpOption && !helpOption.hidden) {
177
+ const removeShort = helpOption.short && cmd._findOption(helpOption.short);
178
+ const removeLong = helpOption.long && cmd._findOption(helpOption.long);
179
+ if (!removeShort && !removeLong) {
180
+ visibleOptions.push(helpOption);
181
+ } else if (helpOption.long && !removeLong) {
182
+ visibleOptions.push(cmd.createOption(helpOption.long, helpOption.description));
183
+ } else if (helpOption.short && !removeShort) {
184
+ visibleOptions.push(cmd.createOption(helpOption.short, helpOption.description));
185
+ }
186
+ }
187
+ if (this.sortOptions) {
188
+ visibleOptions.sort(this.compareOptions);
189
+ }
190
+ return visibleOptions;
191
+ }
192
+ visibleGlobalOptions(cmd) {
193
+ if (!this.showGlobalOptions)
194
+ return [];
195
+ const globalOptions = [];
196
+ for (let ancestorCmd = cmd.parent;ancestorCmd; ancestorCmd = ancestorCmd.parent) {
197
+ const visibleOptions = ancestorCmd.options.filter((option) => !option.hidden);
198
+ globalOptions.push(...visibleOptions);
199
+ }
200
+ if (this.sortOptions) {
201
+ globalOptions.sort(this.compareOptions);
202
+ }
203
+ return globalOptions;
204
+ }
205
+ visibleArguments(cmd) {
206
+ if (cmd._argsDescription) {
207
+ cmd.registeredArguments.forEach((argument) => {
208
+ argument.description = argument.description || cmd._argsDescription[argument.name()] || "";
209
+ });
210
+ }
211
+ if (cmd.registeredArguments.find((argument) => argument.description)) {
212
+ return cmd.registeredArguments;
213
+ }
214
+ return [];
215
+ }
216
+ subcommandTerm(cmd) {
217
+ const args = cmd.registeredArguments.map((arg) => humanReadableArgName(arg)).join(" ");
218
+ return cmd._name + (cmd._aliases[0] ? "|" + cmd._aliases[0] : "") + (cmd.options.length ? " [options]" : "") + (args ? " " + args : "");
219
+ }
220
+ optionTerm(option) {
221
+ return option.flags;
222
+ }
223
+ argumentTerm(argument) {
224
+ return argument.name();
225
+ }
226
+ longestSubcommandTermLength(cmd, helper) {
227
+ return helper.visibleCommands(cmd).reduce((max, command) => {
228
+ return Math.max(max, this.displayWidth(helper.styleSubcommandTerm(helper.subcommandTerm(command))));
229
+ }, 0);
230
+ }
231
+ longestOptionTermLength(cmd, helper) {
232
+ return helper.visibleOptions(cmd).reduce((max, option) => {
233
+ return Math.max(max, this.displayWidth(helper.styleOptionTerm(helper.optionTerm(option))));
234
+ }, 0);
235
+ }
236
+ longestGlobalOptionTermLength(cmd, helper) {
237
+ return helper.visibleGlobalOptions(cmd).reduce((max, option) => {
238
+ return Math.max(max, this.displayWidth(helper.styleOptionTerm(helper.optionTerm(option))));
239
+ }, 0);
240
+ }
241
+ longestArgumentTermLength(cmd, helper) {
242
+ return helper.visibleArguments(cmd).reduce((max, argument) => {
243
+ return Math.max(max, this.displayWidth(helper.styleArgumentTerm(helper.argumentTerm(argument))));
244
+ }, 0);
245
+ }
246
+ commandUsage(cmd) {
247
+ let cmdName = cmd._name;
248
+ if (cmd._aliases[0]) {
249
+ cmdName = cmdName + "|" + cmd._aliases[0];
250
+ }
251
+ let ancestorCmdNames = "";
252
+ for (let ancestorCmd = cmd.parent;ancestorCmd; ancestorCmd = ancestorCmd.parent) {
253
+ ancestorCmdNames = ancestorCmd.name() + " " + ancestorCmdNames;
254
+ }
255
+ return ancestorCmdNames + cmdName + " " + cmd.usage();
256
+ }
257
+ commandDescription(cmd) {
258
+ return cmd.description();
259
+ }
260
+ subcommandDescription(cmd) {
261
+ return cmd.summary() || cmd.description();
262
+ }
263
+ optionDescription(option) {
264
+ const extraInfo = [];
265
+ if (option.argChoices) {
266
+ extraInfo.push(`choices: ${option.argChoices.map((choice) => JSON.stringify(choice)).join(", ")}`);
267
+ }
268
+ if (option.defaultValue !== undefined) {
269
+ const showDefault = option.required || option.optional || option.isBoolean() && typeof option.defaultValue === "boolean";
270
+ if (showDefault) {
271
+ extraInfo.push(`default: ${option.defaultValueDescription || JSON.stringify(option.defaultValue)}`);
272
+ }
273
+ }
274
+ if (option.presetArg !== undefined && option.optional) {
275
+ extraInfo.push(`preset: ${JSON.stringify(option.presetArg)}`);
276
+ }
277
+ if (option.envVar !== undefined) {
278
+ extraInfo.push(`env: ${option.envVar}`);
279
+ }
280
+ if (extraInfo.length > 0) {
281
+ return `${option.description} (${extraInfo.join(", ")})`;
282
+ }
283
+ return option.description;
284
+ }
285
+ argumentDescription(argument) {
286
+ const extraInfo = [];
287
+ if (argument.argChoices) {
288
+ extraInfo.push(`choices: ${argument.argChoices.map((choice) => JSON.stringify(choice)).join(", ")}`);
289
+ }
290
+ if (argument.defaultValue !== undefined) {
291
+ extraInfo.push(`default: ${argument.defaultValueDescription || JSON.stringify(argument.defaultValue)}`);
292
+ }
293
+ if (extraInfo.length > 0) {
294
+ const extraDescription = `(${extraInfo.join(", ")})`;
295
+ if (argument.description) {
296
+ return `${argument.description} ${extraDescription}`;
297
+ }
298
+ return extraDescription;
299
+ }
300
+ return argument.description;
301
+ }
302
+ formatHelp(cmd, helper) {
303
+ const termWidth = helper.padWidth(cmd, helper);
304
+ const helpWidth = helper.helpWidth ?? 80;
305
+ function callFormatItem(term, description) {
306
+ return helper.formatItem(term, termWidth, description, helper);
307
+ }
308
+ let output = [
309
+ `${helper.styleTitle("Usage:")} ${helper.styleUsage(helper.commandUsage(cmd))}`,
310
+ ""
311
+ ];
312
+ const commandDescription = helper.commandDescription(cmd);
313
+ if (commandDescription.length > 0) {
314
+ output = output.concat([
315
+ helper.boxWrap(helper.styleCommandDescription(commandDescription), helpWidth),
316
+ ""
317
+ ]);
318
+ }
319
+ const argumentList = helper.visibleArguments(cmd).map((argument) => {
320
+ return callFormatItem(helper.styleArgumentTerm(helper.argumentTerm(argument)), helper.styleArgumentDescription(helper.argumentDescription(argument)));
321
+ });
322
+ if (argumentList.length > 0) {
323
+ output = output.concat([
324
+ helper.styleTitle("Arguments:"),
325
+ ...argumentList,
326
+ ""
327
+ ]);
328
+ }
329
+ const optionList = helper.visibleOptions(cmd).map((option) => {
330
+ return callFormatItem(helper.styleOptionTerm(helper.optionTerm(option)), helper.styleOptionDescription(helper.optionDescription(option)));
331
+ });
332
+ if (optionList.length > 0) {
333
+ output = output.concat([
334
+ helper.styleTitle("Options:"),
335
+ ...optionList,
336
+ ""
337
+ ]);
338
+ }
339
+ if (helper.showGlobalOptions) {
340
+ const globalOptionList = helper.visibleGlobalOptions(cmd).map((option) => {
341
+ return callFormatItem(helper.styleOptionTerm(helper.optionTerm(option)), helper.styleOptionDescription(helper.optionDescription(option)));
342
+ });
343
+ if (globalOptionList.length > 0) {
344
+ output = output.concat([
345
+ helper.styleTitle("Global Options:"),
346
+ ...globalOptionList,
347
+ ""
348
+ ]);
349
+ }
350
+ }
351
+ const commandList = helper.visibleCommands(cmd).map((cmd2) => {
352
+ return callFormatItem(helper.styleSubcommandTerm(helper.subcommandTerm(cmd2)), helper.styleSubcommandDescription(helper.subcommandDescription(cmd2)));
353
+ });
354
+ if (commandList.length > 0) {
355
+ output = output.concat([
356
+ helper.styleTitle("Commands:"),
357
+ ...commandList,
358
+ ""
359
+ ]);
360
+ }
361
+ return output.join(`
362
+ `);
363
+ }
364
+ displayWidth(str) {
365
+ return stripColor(str).length;
366
+ }
367
+ styleTitle(str) {
368
+ return str;
369
+ }
370
+ styleUsage(str) {
371
+ return str.split(" ").map((word) => {
372
+ if (word === "[options]")
373
+ return this.styleOptionText(word);
374
+ if (word === "[command]")
375
+ return this.styleSubcommandText(word);
376
+ if (word[0] === "[" || word[0] === "<")
377
+ return this.styleArgumentText(word);
378
+ return this.styleCommandText(word);
379
+ }).join(" ");
380
+ }
381
+ styleCommandDescription(str) {
382
+ return this.styleDescriptionText(str);
383
+ }
384
+ styleOptionDescription(str) {
385
+ return this.styleDescriptionText(str);
386
+ }
387
+ styleSubcommandDescription(str) {
388
+ return this.styleDescriptionText(str);
389
+ }
390
+ styleArgumentDescription(str) {
391
+ return this.styleDescriptionText(str);
392
+ }
393
+ styleDescriptionText(str) {
394
+ return str;
395
+ }
396
+ styleOptionTerm(str) {
397
+ return this.styleOptionText(str);
398
+ }
399
+ styleSubcommandTerm(str) {
400
+ return str.split(" ").map((word) => {
401
+ if (word === "[options]")
402
+ return this.styleOptionText(word);
403
+ if (word[0] === "[" || word[0] === "<")
404
+ return this.styleArgumentText(word);
405
+ return this.styleSubcommandText(word);
406
+ }).join(" ");
407
+ }
408
+ styleArgumentTerm(str) {
409
+ return this.styleArgumentText(str);
410
+ }
411
+ styleOptionText(str) {
412
+ return str;
413
+ }
414
+ styleArgumentText(str) {
415
+ return str;
416
+ }
417
+ styleSubcommandText(str) {
418
+ return str;
419
+ }
420
+ styleCommandText(str) {
421
+ return str;
422
+ }
423
+ padWidth(cmd, helper) {
424
+ return Math.max(helper.longestOptionTermLength(cmd, helper), helper.longestGlobalOptionTermLength(cmd, helper), helper.longestSubcommandTermLength(cmd, helper), helper.longestArgumentTermLength(cmd, helper));
425
+ }
426
+ preformatted(str) {
427
+ return /\n[^\S\r\n]/.test(str);
428
+ }
429
+ formatItem(term, termWidth, description, helper) {
430
+ const itemIndent = 2;
431
+ const itemIndentStr = " ".repeat(itemIndent);
432
+ if (!description)
433
+ return itemIndentStr + term;
434
+ const paddedTerm = term.padEnd(termWidth + term.length - helper.displayWidth(term));
435
+ const spacerWidth = 2;
436
+ const helpWidth = this.helpWidth ?? 80;
437
+ const remainingWidth = helpWidth - termWidth - spacerWidth - itemIndent;
438
+ let formattedDescription;
439
+ if (remainingWidth < this.minWidthToWrap || helper.preformatted(description)) {
440
+ formattedDescription = description;
441
+ } else {
442
+ const wrappedDescription = helper.boxWrap(description, remainingWidth);
443
+ formattedDescription = wrappedDescription.replace(/\n/g, `
444
+ ` + " ".repeat(termWidth + spacerWidth));
445
+ }
446
+ return itemIndentStr + paddedTerm + " ".repeat(spacerWidth) + formattedDescription.replace(/\n/g, `
447
+ ${itemIndentStr}`);
448
+ }
449
+ boxWrap(str, width) {
450
+ if (width < this.minWidthToWrap)
451
+ return str;
452
+ const rawLines = str.split(/\r\n|\n/);
453
+ const chunkPattern = /[\s]*[^\s]+/g;
454
+ const wrappedLines = [];
455
+ rawLines.forEach((line) => {
456
+ const chunks = line.match(chunkPattern);
457
+ if (chunks === null) {
458
+ wrappedLines.push("");
459
+ return;
460
+ }
461
+ let sumChunks = [chunks.shift()];
462
+ let sumWidth = this.displayWidth(sumChunks[0]);
463
+ chunks.forEach((chunk) => {
464
+ const visibleWidth = this.displayWidth(chunk);
465
+ if (sumWidth + visibleWidth <= width) {
466
+ sumChunks.push(chunk);
467
+ sumWidth += visibleWidth;
468
+ return;
469
+ }
470
+ wrappedLines.push(sumChunks.join(""));
471
+ const nextChunk = chunk.trimStart();
472
+ sumChunks = [nextChunk];
473
+ sumWidth = this.displayWidth(nextChunk);
474
+ });
475
+ wrappedLines.push(sumChunks.join(""));
476
+ });
477
+ return wrappedLines.join(`
478
+ `);
479
+ }
480
+ }
481
+ function stripColor(str) {
482
+ const sgrPattern = /\x1b\[\d*(;\d*)*m/g;
483
+ return str.replace(sgrPattern, "");
484
+ }
485
+ exports.Help = Help;
486
+ exports.stripColor = stripColor;
487
+ });
488
+
489
+ // node_modules/commander/lib/option.js
490
+ var require_option = __commonJS((exports) => {
491
+ var { InvalidArgumentError } = require_error();
492
+
493
+ class Option {
494
+ constructor(flags, description) {
495
+ this.flags = flags;
496
+ this.description = description || "";
497
+ this.required = flags.includes("<");
498
+ this.optional = flags.includes("[");
499
+ this.variadic = /\w\.\.\.[>\]]$/.test(flags);
500
+ this.mandatory = false;
501
+ const optionFlags = splitOptionFlags(flags);
502
+ this.short = optionFlags.shortFlag;
503
+ this.long = optionFlags.longFlag;
504
+ this.negate = false;
505
+ if (this.long) {
506
+ this.negate = this.long.startsWith("--no-");
507
+ }
508
+ this.defaultValue = undefined;
509
+ this.defaultValueDescription = undefined;
510
+ this.presetArg = undefined;
511
+ this.envVar = undefined;
512
+ this.parseArg = undefined;
513
+ this.hidden = false;
514
+ this.argChoices = undefined;
515
+ this.conflictsWith = [];
516
+ this.implied = undefined;
517
+ }
518
+ default(value, description) {
519
+ this.defaultValue = value;
520
+ this.defaultValueDescription = description;
521
+ return this;
522
+ }
523
+ preset(arg) {
524
+ this.presetArg = arg;
525
+ return this;
526
+ }
527
+ conflicts(names) {
528
+ this.conflictsWith = this.conflictsWith.concat(names);
529
+ return this;
530
+ }
531
+ implies(impliedOptionValues) {
532
+ let newImplied = impliedOptionValues;
533
+ if (typeof impliedOptionValues === "string") {
534
+ newImplied = { [impliedOptionValues]: true };
535
+ }
536
+ this.implied = Object.assign(this.implied || {}, newImplied);
537
+ return this;
538
+ }
539
+ env(name) {
540
+ this.envVar = name;
541
+ return this;
542
+ }
543
+ argParser(fn) {
544
+ this.parseArg = fn;
545
+ return this;
546
+ }
547
+ makeOptionMandatory(mandatory = true) {
548
+ this.mandatory = !!mandatory;
549
+ return this;
550
+ }
551
+ hideHelp(hide = true) {
552
+ this.hidden = !!hide;
553
+ return this;
554
+ }
555
+ _concatValue(value, previous) {
556
+ if (previous === this.defaultValue || !Array.isArray(previous)) {
557
+ return [value];
558
+ }
559
+ return previous.concat(value);
560
+ }
561
+ choices(values) {
562
+ this.argChoices = values.slice();
563
+ this.parseArg = (arg, previous) => {
564
+ if (!this.argChoices.includes(arg)) {
565
+ throw new InvalidArgumentError(`Allowed choices are ${this.argChoices.join(", ")}.`);
566
+ }
567
+ if (this.variadic) {
568
+ return this._concatValue(arg, previous);
569
+ }
570
+ return arg;
571
+ };
572
+ return this;
573
+ }
574
+ name() {
575
+ if (this.long) {
576
+ return this.long.replace(/^--/, "");
577
+ }
578
+ return this.short.replace(/^-/, "");
579
+ }
580
+ attributeName() {
581
+ if (this.negate) {
582
+ return camelcase(this.name().replace(/^no-/, ""));
583
+ }
584
+ return camelcase(this.name());
585
+ }
586
+ is(arg) {
587
+ return this.short === arg || this.long === arg;
588
+ }
589
+ isBoolean() {
590
+ return !this.required && !this.optional && !this.negate;
591
+ }
592
+ }
593
+
594
+ class DualOptions {
595
+ constructor(options) {
596
+ this.positiveOptions = new Map;
597
+ this.negativeOptions = new Map;
598
+ this.dualOptions = new Set;
599
+ options.forEach((option) => {
600
+ if (option.negate) {
601
+ this.negativeOptions.set(option.attributeName(), option);
602
+ } else {
603
+ this.positiveOptions.set(option.attributeName(), option);
604
+ }
605
+ });
606
+ this.negativeOptions.forEach((value, key) => {
607
+ if (this.positiveOptions.has(key)) {
608
+ this.dualOptions.add(key);
609
+ }
610
+ });
611
+ }
612
+ valueFromOption(value, option) {
613
+ const optionKey = option.attributeName();
614
+ if (!this.dualOptions.has(optionKey))
615
+ return true;
616
+ const preset = this.negativeOptions.get(optionKey).presetArg;
617
+ const negativeValue = preset !== undefined ? preset : false;
618
+ return option.negate === (negativeValue === value);
619
+ }
620
+ }
621
+ function camelcase(str) {
622
+ return str.split("-").reduce((str2, word) => {
623
+ return str2 + word[0].toUpperCase() + word.slice(1);
624
+ });
625
+ }
626
+ function splitOptionFlags(flags) {
627
+ let shortFlag;
628
+ let longFlag;
629
+ const shortFlagExp = /^-[^-]$/;
630
+ const longFlagExp = /^--[^-]/;
631
+ const flagParts = flags.split(/[ |,]+/).concat("guard");
632
+ if (shortFlagExp.test(flagParts[0]))
633
+ shortFlag = flagParts.shift();
634
+ if (longFlagExp.test(flagParts[0]))
635
+ longFlag = flagParts.shift();
636
+ if (!shortFlag && shortFlagExp.test(flagParts[0]))
637
+ shortFlag = flagParts.shift();
638
+ if (!shortFlag && longFlagExp.test(flagParts[0])) {
639
+ shortFlag = longFlag;
640
+ longFlag = flagParts.shift();
641
+ }
642
+ if (flagParts[0].startsWith("-")) {
643
+ const unsupportedFlag = flagParts[0];
644
+ const baseError = `option creation failed due to '${unsupportedFlag}' in option flags '${flags}'`;
645
+ if (/^-[^-][^-]/.test(unsupportedFlag))
646
+ throw new Error(`${baseError}
647
+ - a short flag is a single dash and a single character
648
+ - either use a single dash and a single character (for a short flag)
649
+ - or use a double dash for a long option (and can have two, like '--ws, --workspace')`);
650
+ if (shortFlagExp.test(unsupportedFlag))
651
+ throw new Error(`${baseError}
652
+ - too many short flags`);
653
+ if (longFlagExp.test(unsupportedFlag))
654
+ throw new Error(`${baseError}
655
+ - too many long flags`);
656
+ throw new Error(`${baseError}
657
+ - unrecognised flag format`);
658
+ }
659
+ if (shortFlag === undefined && longFlag === undefined)
660
+ throw new Error(`option creation failed due to no flags found in '${flags}'.`);
661
+ return { shortFlag, longFlag };
662
+ }
663
+ exports.Option = Option;
664
+ exports.DualOptions = DualOptions;
665
+ });
666
+
667
+ // node_modules/commander/lib/suggestSimilar.js
668
+ var require_suggestSimilar = __commonJS((exports) => {
669
+ var maxDistance = 3;
670
+ function editDistance(a, b) {
671
+ if (Math.abs(a.length - b.length) > maxDistance)
672
+ return Math.max(a.length, b.length);
673
+ const d = [];
674
+ for (let i = 0;i <= a.length; i++) {
675
+ d[i] = [i];
676
+ }
677
+ for (let j = 0;j <= b.length; j++) {
678
+ d[0][j] = j;
679
+ }
680
+ for (let j = 1;j <= b.length; j++) {
681
+ for (let i = 1;i <= a.length; i++) {
682
+ let cost = 1;
683
+ if (a[i - 1] === b[j - 1]) {
684
+ cost = 0;
685
+ } else {
686
+ cost = 1;
687
+ }
688
+ d[i][j] = Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost);
689
+ if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
690
+ d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + 1);
691
+ }
692
+ }
693
+ }
694
+ return d[a.length][b.length];
695
+ }
696
+ function suggestSimilar(word, candidates) {
697
+ if (!candidates || candidates.length === 0)
698
+ return "";
699
+ candidates = Array.from(new Set(candidates));
700
+ const searchingOptions = word.startsWith("--");
701
+ if (searchingOptions) {
702
+ word = word.slice(2);
703
+ candidates = candidates.map((candidate) => candidate.slice(2));
704
+ }
705
+ let similar = [];
706
+ let bestDistance = maxDistance;
707
+ const minSimilarity = 0.4;
708
+ candidates.forEach((candidate) => {
709
+ if (candidate.length <= 1)
710
+ return;
711
+ const distance = editDistance(word, candidate);
712
+ const length = Math.max(word.length, candidate.length);
713
+ const similarity = (length - distance) / length;
714
+ if (similarity > minSimilarity) {
715
+ if (distance < bestDistance) {
716
+ bestDistance = distance;
717
+ similar = [candidate];
718
+ } else if (distance === bestDistance) {
719
+ similar.push(candidate);
720
+ }
721
+ }
722
+ });
723
+ similar.sort((a, b) => a.localeCompare(b));
724
+ if (searchingOptions) {
725
+ similar = similar.map((candidate) => `--${candidate}`);
726
+ }
727
+ if (similar.length > 1) {
728
+ return `
729
+ (Did you mean one of ${similar.join(", ")}?)`;
730
+ }
731
+ if (similar.length === 1) {
732
+ return `
733
+ (Did you mean ${similar[0]}?)`;
734
+ }
735
+ return "";
736
+ }
737
+ exports.suggestSimilar = suggestSimilar;
738
+ });
739
+
740
+ // node_modules/commander/lib/command.js
741
+ var require_command = __commonJS((exports) => {
742
+ var EventEmitter = __require("node:events").EventEmitter;
743
+ var childProcess = __require("node:child_process");
744
+ var path = __require("node:path");
745
+ var fs = __require("node:fs");
746
+ var process2 = __require("node:process");
747
+ var { Argument, humanReadableArgName } = require_argument();
748
+ var { CommanderError } = require_error();
749
+ var { Help, stripColor } = require_help();
750
+ var { Option, DualOptions } = require_option();
751
+ var { suggestSimilar } = require_suggestSimilar();
752
+
753
+ class Command extends EventEmitter {
754
+ constructor(name) {
755
+ super();
756
+ this.commands = [];
757
+ this.options = [];
758
+ this.parent = null;
759
+ this._allowUnknownOption = false;
760
+ this._allowExcessArguments = false;
761
+ this.registeredArguments = [];
762
+ this._args = this.registeredArguments;
763
+ this.args = [];
764
+ this.rawArgs = [];
765
+ this.processedArgs = [];
766
+ this._scriptPath = null;
767
+ this._name = name || "";
768
+ this._optionValues = {};
769
+ this._optionValueSources = {};
770
+ this._storeOptionsAsProperties = false;
771
+ this._actionHandler = null;
772
+ this._executableHandler = false;
773
+ this._executableFile = null;
774
+ this._executableDir = null;
775
+ this._defaultCommandName = null;
776
+ this._exitCallback = null;
777
+ this._aliases = [];
778
+ this._combineFlagAndOptionalValue = true;
779
+ this._description = "";
780
+ this._summary = "";
781
+ this._argsDescription = undefined;
782
+ this._enablePositionalOptions = false;
783
+ this._passThroughOptions = false;
784
+ this._lifeCycleHooks = {};
785
+ this._showHelpAfterError = false;
786
+ this._showSuggestionAfterError = true;
787
+ this._savedState = null;
788
+ this._outputConfiguration = {
789
+ writeOut: (str) => process2.stdout.write(str),
790
+ writeErr: (str) => process2.stderr.write(str),
791
+ outputError: (str, write) => write(str),
792
+ getOutHelpWidth: () => process2.stdout.isTTY ? process2.stdout.columns : undefined,
793
+ getErrHelpWidth: () => process2.stderr.isTTY ? process2.stderr.columns : undefined,
794
+ getOutHasColors: () => useColor() ?? (process2.stdout.isTTY && process2.stdout.hasColors?.()),
795
+ getErrHasColors: () => useColor() ?? (process2.stderr.isTTY && process2.stderr.hasColors?.()),
796
+ stripColor: (str) => stripColor(str)
797
+ };
798
+ this._hidden = false;
799
+ this._helpOption = undefined;
800
+ this._addImplicitHelpCommand = undefined;
801
+ this._helpCommand = undefined;
802
+ this._helpConfiguration = {};
803
+ }
804
+ copyInheritedSettings(sourceCommand) {
805
+ this._outputConfiguration = sourceCommand._outputConfiguration;
806
+ this._helpOption = sourceCommand._helpOption;
807
+ this._helpCommand = sourceCommand._helpCommand;
808
+ this._helpConfiguration = sourceCommand._helpConfiguration;
809
+ this._exitCallback = sourceCommand._exitCallback;
810
+ this._storeOptionsAsProperties = sourceCommand._storeOptionsAsProperties;
811
+ this._combineFlagAndOptionalValue = sourceCommand._combineFlagAndOptionalValue;
812
+ this._allowExcessArguments = sourceCommand._allowExcessArguments;
813
+ this._enablePositionalOptions = sourceCommand._enablePositionalOptions;
814
+ this._showHelpAfterError = sourceCommand._showHelpAfterError;
815
+ this._showSuggestionAfterError = sourceCommand._showSuggestionAfterError;
816
+ return this;
817
+ }
818
+ _getCommandAndAncestors() {
819
+ const result = [];
820
+ for (let command = this;command; command = command.parent) {
821
+ result.push(command);
822
+ }
823
+ return result;
824
+ }
825
+ command(nameAndArgs, actionOptsOrExecDesc, execOpts) {
826
+ let desc = actionOptsOrExecDesc;
827
+ let opts = execOpts;
828
+ if (typeof desc === "object" && desc !== null) {
829
+ opts = desc;
830
+ desc = null;
831
+ }
832
+ opts = opts || {};
833
+ const [, name, args] = nameAndArgs.match(/([^ ]+) *(.*)/);
834
+ const cmd = this.createCommand(name);
835
+ if (desc) {
836
+ cmd.description(desc);
837
+ cmd._executableHandler = true;
838
+ }
839
+ if (opts.isDefault)
840
+ this._defaultCommandName = cmd._name;
841
+ cmd._hidden = !!(opts.noHelp || opts.hidden);
842
+ cmd._executableFile = opts.executableFile || null;
843
+ if (args)
844
+ cmd.arguments(args);
845
+ this._registerCommand(cmd);
846
+ cmd.parent = this;
847
+ cmd.copyInheritedSettings(this);
848
+ if (desc)
849
+ return this;
850
+ return cmd;
851
+ }
852
+ createCommand(name) {
853
+ return new Command(name);
854
+ }
855
+ createHelp() {
856
+ return Object.assign(new Help, this.configureHelp());
857
+ }
858
+ configureHelp(configuration) {
859
+ if (configuration === undefined)
860
+ return this._helpConfiguration;
861
+ this._helpConfiguration = configuration;
862
+ return this;
863
+ }
864
+ configureOutput(configuration) {
865
+ if (configuration === undefined)
866
+ return this._outputConfiguration;
867
+ Object.assign(this._outputConfiguration, configuration);
868
+ return this;
869
+ }
870
+ showHelpAfterError(displayHelp = true) {
871
+ if (typeof displayHelp !== "string")
872
+ displayHelp = !!displayHelp;
873
+ this._showHelpAfterError = displayHelp;
874
+ return this;
875
+ }
876
+ showSuggestionAfterError(displaySuggestion = true) {
877
+ this._showSuggestionAfterError = !!displaySuggestion;
878
+ return this;
879
+ }
880
+ addCommand(cmd, opts) {
881
+ if (!cmd._name) {
882
+ throw new Error(`Command passed to .addCommand() must have a name
883
+ - specify the name in Command constructor or using .name()`);
884
+ }
885
+ opts = opts || {};
886
+ if (opts.isDefault)
887
+ this._defaultCommandName = cmd._name;
888
+ if (opts.noHelp || opts.hidden)
889
+ cmd._hidden = true;
890
+ this._registerCommand(cmd);
891
+ cmd.parent = this;
892
+ cmd._checkForBrokenPassThrough();
893
+ return this;
894
+ }
895
+ createArgument(name, description) {
896
+ return new Argument(name, description);
897
+ }
898
+ argument(name, description, fn, defaultValue) {
899
+ const argument = this.createArgument(name, description);
900
+ if (typeof fn === "function") {
901
+ argument.default(defaultValue).argParser(fn);
902
+ } else {
903
+ argument.default(fn);
904
+ }
905
+ this.addArgument(argument);
906
+ return this;
907
+ }
908
+ arguments(names) {
909
+ names.trim().split(/ +/).forEach((detail) => {
910
+ this.argument(detail);
911
+ });
912
+ return this;
913
+ }
914
+ addArgument(argument) {
915
+ const previousArgument = this.registeredArguments.slice(-1)[0];
916
+ if (previousArgument && previousArgument.variadic) {
917
+ throw new Error(`only the last argument can be variadic '${previousArgument.name()}'`);
918
+ }
919
+ if (argument.required && argument.defaultValue !== undefined && argument.parseArg === undefined) {
920
+ throw new Error(`a default value for a required argument is never used: '${argument.name()}'`);
921
+ }
922
+ this.registeredArguments.push(argument);
923
+ return this;
924
+ }
925
+ helpCommand(enableOrNameAndArgs, description) {
926
+ if (typeof enableOrNameAndArgs === "boolean") {
927
+ this._addImplicitHelpCommand = enableOrNameAndArgs;
928
+ return this;
929
+ }
930
+ enableOrNameAndArgs = enableOrNameAndArgs ?? "help [command]";
931
+ const [, helpName, helpArgs] = enableOrNameAndArgs.match(/([^ ]+) *(.*)/);
932
+ const helpDescription = description ?? "display help for command";
933
+ const helpCommand = this.createCommand(helpName);
934
+ helpCommand.helpOption(false);
935
+ if (helpArgs)
936
+ helpCommand.arguments(helpArgs);
937
+ if (helpDescription)
938
+ helpCommand.description(helpDescription);
939
+ this._addImplicitHelpCommand = true;
940
+ this._helpCommand = helpCommand;
941
+ return this;
942
+ }
943
+ addHelpCommand(helpCommand, deprecatedDescription) {
944
+ if (typeof helpCommand !== "object") {
945
+ this.helpCommand(helpCommand, deprecatedDescription);
946
+ return this;
947
+ }
948
+ this._addImplicitHelpCommand = true;
949
+ this._helpCommand = helpCommand;
950
+ return this;
951
+ }
952
+ _getHelpCommand() {
953
+ const hasImplicitHelpCommand = this._addImplicitHelpCommand ?? (this.commands.length && !this._actionHandler && !this._findCommand("help"));
954
+ if (hasImplicitHelpCommand) {
955
+ if (this._helpCommand === undefined) {
956
+ this.helpCommand(undefined, undefined);
957
+ }
958
+ return this._helpCommand;
959
+ }
960
+ return null;
961
+ }
962
+ hook(event, listener) {
963
+ const allowedValues = ["preSubcommand", "preAction", "postAction"];
964
+ if (!allowedValues.includes(event)) {
965
+ throw new Error(`Unexpected value for event passed to hook : '${event}'.
966
+ Expecting one of '${allowedValues.join("', '")}'`);
967
+ }
968
+ if (this._lifeCycleHooks[event]) {
969
+ this._lifeCycleHooks[event].push(listener);
970
+ } else {
971
+ this._lifeCycleHooks[event] = [listener];
972
+ }
973
+ return this;
974
+ }
975
+ exitOverride(fn) {
976
+ if (fn) {
977
+ this._exitCallback = fn;
978
+ } else {
979
+ this._exitCallback = (err) => {
980
+ if (err.code !== "commander.executeSubCommandAsync") {
981
+ throw err;
982
+ } else {}
983
+ };
984
+ }
985
+ return this;
986
+ }
987
+ _exit(exitCode, code, message) {
988
+ if (this._exitCallback) {
989
+ this._exitCallback(new CommanderError(exitCode, code, message));
990
+ }
991
+ process2.exit(exitCode);
992
+ }
993
+ action(fn) {
994
+ const listener = (args) => {
995
+ const expectedArgsCount = this.registeredArguments.length;
996
+ const actionArgs = args.slice(0, expectedArgsCount);
997
+ if (this._storeOptionsAsProperties) {
998
+ actionArgs[expectedArgsCount] = this;
999
+ } else {
1000
+ actionArgs[expectedArgsCount] = this.opts();
1001
+ }
1002
+ actionArgs.push(this);
1003
+ return fn.apply(this, actionArgs);
1004
+ };
1005
+ this._actionHandler = listener;
1006
+ return this;
1007
+ }
1008
+ createOption(flags, description) {
1009
+ return new Option(flags, description);
1010
+ }
1011
+ _callParseArg(target, value, previous, invalidArgumentMessage) {
1012
+ try {
1013
+ return target.parseArg(value, previous);
1014
+ } catch (err) {
1015
+ if (err.code === "commander.invalidArgument") {
1016
+ const message = `${invalidArgumentMessage} ${err.message}`;
1017
+ this.error(message, { exitCode: err.exitCode, code: err.code });
1018
+ }
1019
+ throw err;
1020
+ }
1021
+ }
1022
+ _registerOption(option) {
1023
+ const matchingOption = option.short && this._findOption(option.short) || option.long && this._findOption(option.long);
1024
+ if (matchingOption) {
1025
+ const matchingFlag = option.long && this._findOption(option.long) ? option.long : option.short;
1026
+ throw new Error(`Cannot add option '${option.flags}'${this._name && ` to command '${this._name}'`} due to conflicting flag '${matchingFlag}'
1027
+ - already used by option '${matchingOption.flags}'`);
1028
+ }
1029
+ this.options.push(option);
1030
+ }
1031
+ _registerCommand(command) {
1032
+ const knownBy = (cmd) => {
1033
+ return [cmd.name()].concat(cmd.aliases());
1034
+ };
1035
+ const alreadyUsed = knownBy(command).find((name) => this._findCommand(name));
1036
+ if (alreadyUsed) {
1037
+ const existingCmd = knownBy(this._findCommand(alreadyUsed)).join("|");
1038
+ const newCmd = knownBy(command).join("|");
1039
+ throw new Error(`cannot add command '${newCmd}' as already have command '${existingCmd}'`);
1040
+ }
1041
+ this.commands.push(command);
1042
+ }
1043
+ addOption(option) {
1044
+ this._registerOption(option);
1045
+ const oname = option.name();
1046
+ const name = option.attributeName();
1047
+ if (option.negate) {
1048
+ const positiveLongFlag = option.long.replace(/^--no-/, "--");
1049
+ if (!this._findOption(positiveLongFlag)) {
1050
+ this.setOptionValueWithSource(name, option.defaultValue === undefined ? true : option.defaultValue, "default");
1051
+ }
1052
+ } else if (option.defaultValue !== undefined) {
1053
+ this.setOptionValueWithSource(name, option.defaultValue, "default");
1054
+ }
1055
+ const handleOptionValue = (val, invalidValueMessage, valueSource) => {
1056
+ if (val == null && option.presetArg !== undefined) {
1057
+ val = option.presetArg;
1058
+ }
1059
+ const oldValue = this.getOptionValue(name);
1060
+ if (val !== null && option.parseArg) {
1061
+ val = this._callParseArg(option, val, oldValue, invalidValueMessage);
1062
+ } else if (val !== null && option.variadic) {
1063
+ val = option._concatValue(val, oldValue);
1064
+ }
1065
+ if (val == null) {
1066
+ if (option.negate) {
1067
+ val = false;
1068
+ } else if (option.isBoolean() || option.optional) {
1069
+ val = true;
1070
+ } else {
1071
+ val = "";
1072
+ }
1073
+ }
1074
+ this.setOptionValueWithSource(name, val, valueSource);
1075
+ };
1076
+ this.on("option:" + oname, (val) => {
1077
+ const invalidValueMessage = `error: option '${option.flags}' argument '${val}' is invalid.`;
1078
+ handleOptionValue(val, invalidValueMessage, "cli");
1079
+ });
1080
+ if (option.envVar) {
1081
+ this.on("optionEnv:" + oname, (val) => {
1082
+ const invalidValueMessage = `error: option '${option.flags}' value '${val}' from env '${option.envVar}' is invalid.`;
1083
+ handleOptionValue(val, invalidValueMessage, "env");
1084
+ });
1085
+ }
1086
+ return this;
1087
+ }
1088
+ _optionEx(config, flags, description, fn, defaultValue) {
1089
+ if (typeof flags === "object" && flags instanceof Option) {
1090
+ throw new Error("To add an Option object use addOption() instead of option() or requiredOption()");
1091
+ }
1092
+ const option = this.createOption(flags, description);
1093
+ option.makeOptionMandatory(!!config.mandatory);
1094
+ if (typeof fn === "function") {
1095
+ option.default(defaultValue).argParser(fn);
1096
+ } else if (fn instanceof RegExp) {
1097
+ const regex = fn;
1098
+ fn = (val, def) => {
1099
+ const m = regex.exec(val);
1100
+ return m ? m[0] : def;
1101
+ };
1102
+ option.default(defaultValue).argParser(fn);
1103
+ } else {
1104
+ option.default(fn);
1105
+ }
1106
+ return this.addOption(option);
1107
+ }
1108
+ option(flags, description, parseArg, defaultValue) {
1109
+ return this._optionEx({}, flags, description, parseArg, defaultValue);
1110
+ }
1111
+ requiredOption(flags, description, parseArg, defaultValue) {
1112
+ return this._optionEx({ mandatory: true }, flags, description, parseArg, defaultValue);
1113
+ }
1114
+ combineFlagAndOptionalValue(combine = true) {
1115
+ this._combineFlagAndOptionalValue = !!combine;
1116
+ return this;
1117
+ }
1118
+ allowUnknownOption(allowUnknown = true) {
1119
+ this._allowUnknownOption = !!allowUnknown;
1120
+ return this;
1121
+ }
1122
+ allowExcessArguments(allowExcess = true) {
1123
+ this._allowExcessArguments = !!allowExcess;
1124
+ return this;
1125
+ }
1126
+ enablePositionalOptions(positional = true) {
1127
+ this._enablePositionalOptions = !!positional;
1128
+ return this;
1129
+ }
1130
+ passThroughOptions(passThrough = true) {
1131
+ this._passThroughOptions = !!passThrough;
1132
+ this._checkForBrokenPassThrough();
1133
+ return this;
1134
+ }
1135
+ _checkForBrokenPassThrough() {
1136
+ if (this.parent && this._passThroughOptions && !this.parent._enablePositionalOptions) {
1137
+ throw new Error(`passThroughOptions cannot be used for '${this._name}' without turning on enablePositionalOptions for parent command(s)`);
1138
+ }
1139
+ }
1140
+ storeOptionsAsProperties(storeAsProperties = true) {
1141
+ if (this.options.length) {
1142
+ throw new Error("call .storeOptionsAsProperties() before adding options");
1143
+ }
1144
+ if (Object.keys(this._optionValues).length) {
1145
+ throw new Error("call .storeOptionsAsProperties() before setting option values");
1146
+ }
1147
+ this._storeOptionsAsProperties = !!storeAsProperties;
1148
+ return this;
1149
+ }
1150
+ getOptionValue(key) {
1151
+ if (this._storeOptionsAsProperties) {
1152
+ return this[key];
1153
+ }
1154
+ return this._optionValues[key];
1155
+ }
1156
+ setOptionValue(key, value) {
1157
+ return this.setOptionValueWithSource(key, value, undefined);
1158
+ }
1159
+ setOptionValueWithSource(key, value, source) {
1160
+ if (this._storeOptionsAsProperties) {
1161
+ this[key] = value;
1162
+ } else {
1163
+ this._optionValues[key] = value;
1164
+ }
1165
+ this._optionValueSources[key] = source;
1166
+ return this;
1167
+ }
1168
+ getOptionValueSource(key) {
1169
+ return this._optionValueSources[key];
1170
+ }
1171
+ getOptionValueSourceWithGlobals(key) {
1172
+ let source;
1173
+ this._getCommandAndAncestors().forEach((cmd) => {
1174
+ if (cmd.getOptionValueSource(key) !== undefined) {
1175
+ source = cmd.getOptionValueSource(key);
1176
+ }
1177
+ });
1178
+ return source;
1179
+ }
1180
+ _prepareUserArgs(argv, parseOptions) {
1181
+ if (argv !== undefined && !Array.isArray(argv)) {
1182
+ throw new Error("first parameter to parse must be array or undefined");
1183
+ }
1184
+ parseOptions = parseOptions || {};
1185
+ if (argv === undefined && parseOptions.from === undefined) {
1186
+ if (process2.versions?.electron) {
1187
+ parseOptions.from = "electron";
1188
+ }
1189
+ const execArgv = process2.execArgv ?? [];
1190
+ if (execArgv.includes("-e") || execArgv.includes("--eval") || execArgv.includes("-p") || execArgv.includes("--print")) {
1191
+ parseOptions.from = "eval";
1192
+ }
1193
+ }
1194
+ if (argv === undefined) {
1195
+ argv = process2.argv;
1196
+ }
1197
+ this.rawArgs = argv.slice();
1198
+ let userArgs;
1199
+ switch (parseOptions.from) {
1200
+ case undefined:
1201
+ case "node":
1202
+ this._scriptPath = argv[1];
1203
+ userArgs = argv.slice(2);
1204
+ break;
1205
+ case "electron":
1206
+ if (process2.defaultApp) {
1207
+ this._scriptPath = argv[1];
1208
+ userArgs = argv.slice(2);
1209
+ } else {
1210
+ userArgs = argv.slice(1);
1211
+ }
1212
+ break;
1213
+ case "user":
1214
+ userArgs = argv.slice(0);
1215
+ break;
1216
+ case "eval":
1217
+ userArgs = argv.slice(1);
1218
+ break;
1219
+ default:
1220
+ throw new Error(`unexpected parse option { from: '${parseOptions.from}' }`);
1221
+ }
1222
+ if (!this._name && this._scriptPath)
1223
+ this.nameFromFilename(this._scriptPath);
1224
+ this._name = this._name || "program";
1225
+ return userArgs;
1226
+ }
1227
+ parse(argv, parseOptions) {
1228
+ this._prepareForParse();
1229
+ const userArgs = this._prepareUserArgs(argv, parseOptions);
1230
+ this._parseCommand([], userArgs);
1231
+ return this;
1232
+ }
1233
+ async parseAsync(argv, parseOptions) {
1234
+ this._prepareForParse();
1235
+ const userArgs = this._prepareUserArgs(argv, parseOptions);
1236
+ await this._parseCommand([], userArgs);
1237
+ return this;
1238
+ }
1239
+ _prepareForParse() {
1240
+ if (this._savedState === null) {
1241
+ this.saveStateBeforeParse();
1242
+ } else {
1243
+ this.restoreStateBeforeParse();
1244
+ }
1245
+ }
1246
+ saveStateBeforeParse() {
1247
+ this._savedState = {
1248
+ _name: this._name,
1249
+ _optionValues: { ...this._optionValues },
1250
+ _optionValueSources: { ...this._optionValueSources }
1251
+ };
1252
+ }
1253
+ restoreStateBeforeParse() {
1254
+ if (this._storeOptionsAsProperties)
1255
+ throw new Error(`Can not call parse again when storeOptionsAsProperties is true.
1256
+ - either make a new Command for each call to parse, or stop storing options as properties`);
1257
+ this._name = this._savedState._name;
1258
+ this._scriptPath = null;
1259
+ this.rawArgs = [];
1260
+ this._optionValues = { ...this._savedState._optionValues };
1261
+ this._optionValueSources = { ...this._savedState._optionValueSources };
1262
+ this.args = [];
1263
+ this.processedArgs = [];
1264
+ }
1265
+ _checkForMissingExecutable(executableFile, executableDir, subcommandName) {
1266
+ if (fs.existsSync(executableFile))
1267
+ return;
1268
+ const executableDirMessage = executableDir ? `searched for local subcommand relative to directory '${executableDir}'` : "no directory for search for local subcommand, use .executableDir() to supply a custom directory";
1269
+ const executableMissing = `'${executableFile}' does not exist
1270
+ - if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
1271
+ - if the default executable name is not suitable, use the executableFile option to supply a custom name or path
1272
+ - ${executableDirMessage}`;
1273
+ throw new Error(executableMissing);
1274
+ }
1275
+ _executeSubCommand(subcommand, args) {
1276
+ args = args.slice();
1277
+ let launchWithNode = false;
1278
+ const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
1279
+ function findFile(baseDir, baseName) {
1280
+ const localBin = path.resolve(baseDir, baseName);
1281
+ if (fs.existsSync(localBin))
1282
+ return localBin;
1283
+ if (sourceExt.includes(path.extname(baseName)))
1284
+ return;
1285
+ const foundExt = sourceExt.find((ext) => fs.existsSync(`${localBin}${ext}`));
1286
+ if (foundExt)
1287
+ return `${localBin}${foundExt}`;
1288
+ return;
1289
+ }
1290
+ this._checkForMissingMandatoryOptions();
1291
+ this._checkForConflictingOptions();
1292
+ let executableFile = subcommand._executableFile || `${this._name}-${subcommand._name}`;
1293
+ let executableDir = this._executableDir || "";
1294
+ if (this._scriptPath) {
1295
+ let resolvedScriptPath;
1296
+ try {
1297
+ resolvedScriptPath = fs.realpathSync(this._scriptPath);
1298
+ } catch {
1299
+ resolvedScriptPath = this._scriptPath;
1300
+ }
1301
+ executableDir = path.resolve(path.dirname(resolvedScriptPath), executableDir);
1302
+ }
1303
+ if (executableDir) {
1304
+ let localFile = findFile(executableDir, executableFile);
1305
+ if (!localFile && !subcommand._executableFile && this._scriptPath) {
1306
+ const legacyName = path.basename(this._scriptPath, path.extname(this._scriptPath));
1307
+ if (legacyName !== this._name) {
1308
+ localFile = findFile(executableDir, `${legacyName}-${subcommand._name}`);
1309
+ }
1310
+ }
1311
+ executableFile = localFile || executableFile;
1312
+ }
1313
+ launchWithNode = sourceExt.includes(path.extname(executableFile));
1314
+ let proc;
1315
+ if (process2.platform !== "win32") {
1316
+ if (launchWithNode) {
1317
+ args.unshift(executableFile);
1318
+ args = incrementNodeInspectorPort(process2.execArgv).concat(args);
1319
+ proc = childProcess.spawn(process2.argv[0], args, { stdio: "inherit" });
1320
+ } else {
1321
+ proc = childProcess.spawn(executableFile, args, { stdio: "inherit" });
1322
+ }
1323
+ } else {
1324
+ this._checkForMissingExecutable(executableFile, executableDir, subcommand._name);
1325
+ args.unshift(executableFile);
1326
+ args = incrementNodeInspectorPort(process2.execArgv).concat(args);
1327
+ proc = childProcess.spawn(process2.execPath, args, { stdio: "inherit" });
1328
+ }
1329
+ if (!proc.killed) {
1330
+ const signals = ["SIGUSR1", "SIGUSR2", "SIGTERM", "SIGINT", "SIGHUP"];
1331
+ signals.forEach((signal) => {
1332
+ process2.on(signal, () => {
1333
+ if (proc.killed === false && proc.exitCode === null) {
1334
+ proc.kill(signal);
1335
+ }
1336
+ });
1337
+ });
1338
+ }
1339
+ const exitCallback = this._exitCallback;
1340
+ proc.on("close", (code) => {
1341
+ code = code ?? 1;
1342
+ if (!exitCallback) {
1343
+ process2.exit(code);
1344
+ } else {
1345
+ exitCallback(new CommanderError(code, "commander.executeSubCommandAsync", "(close)"));
1346
+ }
1347
+ });
1348
+ proc.on("error", (err) => {
1349
+ if (err.code === "ENOENT") {
1350
+ this._checkForMissingExecutable(executableFile, executableDir, subcommand._name);
1351
+ } else if (err.code === "EACCES") {
1352
+ throw new Error(`'${executableFile}' not executable`);
1353
+ }
1354
+ if (!exitCallback) {
1355
+ process2.exit(1);
1356
+ } else {
1357
+ const wrappedError = new CommanderError(1, "commander.executeSubCommandAsync", "(error)");
1358
+ wrappedError.nestedError = err;
1359
+ exitCallback(wrappedError);
1360
+ }
1361
+ });
1362
+ this.runningCommand = proc;
1363
+ }
1364
+ _dispatchSubcommand(commandName, operands, unknown) {
1365
+ const subCommand = this._findCommand(commandName);
1366
+ if (!subCommand)
1367
+ this.help({ error: true });
1368
+ subCommand._prepareForParse();
1369
+ let promiseChain;
1370
+ promiseChain = this._chainOrCallSubCommandHook(promiseChain, subCommand, "preSubcommand");
1371
+ promiseChain = this._chainOrCall(promiseChain, () => {
1372
+ if (subCommand._executableHandler) {
1373
+ this._executeSubCommand(subCommand, operands.concat(unknown));
1374
+ } else {
1375
+ return subCommand._parseCommand(operands, unknown);
1376
+ }
1377
+ });
1378
+ return promiseChain;
1379
+ }
1380
+ _dispatchHelpCommand(subcommandName) {
1381
+ if (!subcommandName) {
1382
+ this.help();
1383
+ }
1384
+ const subCommand = this._findCommand(subcommandName);
1385
+ if (subCommand && !subCommand._executableHandler) {
1386
+ subCommand.help();
1387
+ }
1388
+ return this._dispatchSubcommand(subcommandName, [], [this._getHelpOption()?.long ?? this._getHelpOption()?.short ?? "--help"]);
1389
+ }
1390
+ _checkNumberOfArguments() {
1391
+ this.registeredArguments.forEach((arg, i) => {
1392
+ if (arg.required && this.args[i] == null) {
1393
+ this.missingArgument(arg.name());
1394
+ }
1395
+ });
1396
+ if (this.registeredArguments.length > 0 && this.registeredArguments[this.registeredArguments.length - 1].variadic) {
1397
+ return;
1398
+ }
1399
+ if (this.args.length > this.registeredArguments.length) {
1400
+ this._excessArguments(this.args);
1401
+ }
1402
+ }
1403
+ _processArguments() {
1404
+ const myParseArg = (argument, value, previous) => {
1405
+ let parsedValue = value;
1406
+ if (value !== null && argument.parseArg) {
1407
+ const invalidValueMessage = `error: command-argument value '${value}' is invalid for argument '${argument.name()}'.`;
1408
+ parsedValue = this._callParseArg(argument, value, previous, invalidValueMessage);
1409
+ }
1410
+ return parsedValue;
1411
+ };
1412
+ this._checkNumberOfArguments();
1413
+ const processedArgs = [];
1414
+ this.registeredArguments.forEach((declaredArg, index) => {
1415
+ let value = declaredArg.defaultValue;
1416
+ if (declaredArg.variadic) {
1417
+ if (index < this.args.length) {
1418
+ value = this.args.slice(index);
1419
+ if (declaredArg.parseArg) {
1420
+ value = value.reduce((processed, v) => {
1421
+ return myParseArg(declaredArg, v, processed);
1422
+ }, declaredArg.defaultValue);
1423
+ }
1424
+ } else if (value === undefined) {
1425
+ value = [];
1426
+ }
1427
+ } else if (index < this.args.length) {
1428
+ value = this.args[index];
1429
+ if (declaredArg.parseArg) {
1430
+ value = myParseArg(declaredArg, value, declaredArg.defaultValue);
1431
+ }
1432
+ }
1433
+ processedArgs[index] = value;
1434
+ });
1435
+ this.processedArgs = processedArgs;
1436
+ }
1437
+ _chainOrCall(promise, fn) {
1438
+ if (promise && promise.then && typeof promise.then === "function") {
1439
+ return promise.then(() => fn());
1440
+ }
1441
+ return fn();
1442
+ }
1443
+ _chainOrCallHooks(promise, event) {
1444
+ let result = promise;
1445
+ const hooks = [];
1446
+ this._getCommandAndAncestors().reverse().filter((cmd) => cmd._lifeCycleHooks[event] !== undefined).forEach((hookedCommand) => {
1447
+ hookedCommand._lifeCycleHooks[event].forEach((callback) => {
1448
+ hooks.push({ hookedCommand, callback });
1449
+ });
1450
+ });
1451
+ if (event === "postAction") {
1452
+ hooks.reverse();
1453
+ }
1454
+ hooks.forEach((hookDetail) => {
1455
+ result = this._chainOrCall(result, () => {
1456
+ return hookDetail.callback(hookDetail.hookedCommand, this);
1457
+ });
1458
+ });
1459
+ return result;
1460
+ }
1461
+ _chainOrCallSubCommandHook(promise, subCommand, event) {
1462
+ let result = promise;
1463
+ if (this._lifeCycleHooks[event] !== undefined) {
1464
+ this._lifeCycleHooks[event].forEach((hook) => {
1465
+ result = this._chainOrCall(result, () => {
1466
+ return hook(this, subCommand);
1467
+ });
1468
+ });
1469
+ }
1470
+ return result;
1471
+ }
1472
+ _parseCommand(operands, unknown) {
1473
+ const parsed = this.parseOptions(unknown);
1474
+ this._parseOptionsEnv();
1475
+ this._parseOptionsImplied();
1476
+ operands = operands.concat(parsed.operands);
1477
+ unknown = parsed.unknown;
1478
+ this.args = operands.concat(unknown);
1479
+ if (operands && this._findCommand(operands[0])) {
1480
+ return this._dispatchSubcommand(operands[0], operands.slice(1), unknown);
1481
+ }
1482
+ if (this._getHelpCommand() && operands[0] === this._getHelpCommand().name()) {
1483
+ return this._dispatchHelpCommand(operands[1]);
1484
+ }
1485
+ if (this._defaultCommandName) {
1486
+ this._outputHelpIfRequested(unknown);
1487
+ return this._dispatchSubcommand(this._defaultCommandName, operands, unknown);
1488
+ }
1489
+ if (this.commands.length && this.args.length === 0 && !this._actionHandler && !this._defaultCommandName) {
1490
+ this.help({ error: true });
1491
+ }
1492
+ this._outputHelpIfRequested(parsed.unknown);
1493
+ this._checkForMissingMandatoryOptions();
1494
+ this._checkForConflictingOptions();
1495
+ const checkForUnknownOptions = () => {
1496
+ if (parsed.unknown.length > 0) {
1497
+ this.unknownOption(parsed.unknown[0]);
1498
+ }
1499
+ };
1500
+ const commandEvent = `command:${this.name()}`;
1501
+ if (this._actionHandler) {
1502
+ checkForUnknownOptions();
1503
+ this._processArguments();
1504
+ let promiseChain;
1505
+ promiseChain = this._chainOrCallHooks(promiseChain, "preAction");
1506
+ promiseChain = this._chainOrCall(promiseChain, () => this._actionHandler(this.processedArgs));
1507
+ if (this.parent) {
1508
+ promiseChain = this._chainOrCall(promiseChain, () => {
1509
+ this.parent.emit(commandEvent, operands, unknown);
1510
+ });
1511
+ }
1512
+ promiseChain = this._chainOrCallHooks(promiseChain, "postAction");
1513
+ return promiseChain;
1514
+ }
1515
+ if (this.parent && this.parent.listenerCount(commandEvent)) {
1516
+ checkForUnknownOptions();
1517
+ this._processArguments();
1518
+ this.parent.emit(commandEvent, operands, unknown);
1519
+ } else if (operands.length) {
1520
+ if (this._findCommand("*")) {
1521
+ return this._dispatchSubcommand("*", operands, unknown);
1522
+ }
1523
+ if (this.listenerCount("command:*")) {
1524
+ this.emit("command:*", operands, unknown);
1525
+ } else if (this.commands.length) {
1526
+ this.unknownCommand();
1527
+ } else {
1528
+ checkForUnknownOptions();
1529
+ this._processArguments();
1530
+ }
1531
+ } else if (this.commands.length) {
1532
+ checkForUnknownOptions();
1533
+ this.help({ error: true });
1534
+ } else {
1535
+ checkForUnknownOptions();
1536
+ this._processArguments();
1537
+ }
1538
+ }
1539
+ _findCommand(name) {
1540
+ if (!name)
1541
+ return;
1542
+ return this.commands.find((cmd) => cmd._name === name || cmd._aliases.includes(name));
1543
+ }
1544
+ _findOption(arg) {
1545
+ return this.options.find((option) => option.is(arg));
1546
+ }
1547
+ _checkForMissingMandatoryOptions() {
1548
+ this._getCommandAndAncestors().forEach((cmd) => {
1549
+ cmd.options.forEach((anOption) => {
1550
+ if (anOption.mandatory && cmd.getOptionValue(anOption.attributeName()) === undefined) {
1551
+ cmd.missingMandatoryOptionValue(anOption);
1552
+ }
1553
+ });
1554
+ });
1555
+ }
1556
+ _checkForConflictingLocalOptions() {
1557
+ const definedNonDefaultOptions = this.options.filter((option) => {
1558
+ const optionKey = option.attributeName();
1559
+ if (this.getOptionValue(optionKey) === undefined) {
1560
+ return false;
1561
+ }
1562
+ return this.getOptionValueSource(optionKey) !== "default";
1563
+ });
1564
+ const optionsWithConflicting = definedNonDefaultOptions.filter((option) => option.conflictsWith.length > 0);
1565
+ optionsWithConflicting.forEach((option) => {
1566
+ const conflictingAndDefined = definedNonDefaultOptions.find((defined) => option.conflictsWith.includes(defined.attributeName()));
1567
+ if (conflictingAndDefined) {
1568
+ this._conflictingOption(option, conflictingAndDefined);
1569
+ }
1570
+ });
1571
+ }
1572
+ _checkForConflictingOptions() {
1573
+ this._getCommandAndAncestors().forEach((cmd) => {
1574
+ cmd._checkForConflictingLocalOptions();
1575
+ });
1576
+ }
1577
+ parseOptions(argv) {
1578
+ const operands = [];
1579
+ const unknown = [];
1580
+ let dest = operands;
1581
+ const args = argv.slice();
1582
+ function maybeOption(arg) {
1583
+ return arg.length > 1 && arg[0] === "-";
1584
+ }
1585
+ let activeVariadicOption = null;
1586
+ while (args.length) {
1587
+ const arg = args.shift();
1588
+ if (arg === "--") {
1589
+ if (dest === unknown)
1590
+ dest.push(arg);
1591
+ dest.push(...args);
1592
+ break;
1593
+ }
1594
+ if (activeVariadicOption && !maybeOption(arg)) {
1595
+ this.emit(`option:${activeVariadicOption.name()}`, arg);
1596
+ continue;
1597
+ }
1598
+ activeVariadicOption = null;
1599
+ if (maybeOption(arg)) {
1600
+ const option = this._findOption(arg);
1601
+ if (option) {
1602
+ if (option.required) {
1603
+ const value = args.shift();
1604
+ if (value === undefined)
1605
+ this.optionMissingArgument(option);
1606
+ this.emit(`option:${option.name()}`, value);
1607
+ } else if (option.optional) {
1608
+ let value = null;
1609
+ if (args.length > 0 && !maybeOption(args[0])) {
1610
+ value = args.shift();
1611
+ }
1612
+ this.emit(`option:${option.name()}`, value);
1613
+ } else {
1614
+ this.emit(`option:${option.name()}`);
1615
+ }
1616
+ activeVariadicOption = option.variadic ? option : null;
1617
+ continue;
1618
+ }
1619
+ }
1620
+ if (arg.length > 2 && arg[0] === "-" && arg[1] !== "-") {
1621
+ const option = this._findOption(`-${arg[1]}`);
1622
+ if (option) {
1623
+ if (option.required || option.optional && this._combineFlagAndOptionalValue) {
1624
+ this.emit(`option:${option.name()}`, arg.slice(2));
1625
+ } else {
1626
+ this.emit(`option:${option.name()}`);
1627
+ args.unshift(`-${arg.slice(2)}`);
1628
+ }
1629
+ continue;
1630
+ }
1631
+ }
1632
+ if (/^--[^=]+=/.test(arg)) {
1633
+ const index = arg.indexOf("=");
1634
+ const option = this._findOption(arg.slice(0, index));
1635
+ if (option && (option.required || option.optional)) {
1636
+ this.emit(`option:${option.name()}`, arg.slice(index + 1));
1637
+ continue;
1638
+ }
1639
+ }
1640
+ if (maybeOption(arg)) {
1641
+ dest = unknown;
1642
+ }
1643
+ if ((this._enablePositionalOptions || this._passThroughOptions) && operands.length === 0 && unknown.length === 0) {
1644
+ if (this._findCommand(arg)) {
1645
+ operands.push(arg);
1646
+ if (args.length > 0)
1647
+ unknown.push(...args);
1648
+ break;
1649
+ } else if (this._getHelpCommand() && arg === this._getHelpCommand().name()) {
1650
+ operands.push(arg);
1651
+ if (args.length > 0)
1652
+ operands.push(...args);
1653
+ break;
1654
+ } else if (this._defaultCommandName) {
1655
+ unknown.push(arg);
1656
+ if (args.length > 0)
1657
+ unknown.push(...args);
1658
+ break;
1659
+ }
1660
+ }
1661
+ if (this._passThroughOptions) {
1662
+ dest.push(arg);
1663
+ if (args.length > 0)
1664
+ dest.push(...args);
1665
+ break;
1666
+ }
1667
+ dest.push(arg);
1668
+ }
1669
+ return { operands, unknown };
1670
+ }
1671
+ opts() {
1672
+ if (this._storeOptionsAsProperties) {
1673
+ const result = {};
1674
+ const len = this.options.length;
1675
+ for (let i = 0;i < len; i++) {
1676
+ const key = this.options[i].attributeName();
1677
+ result[key] = key === this._versionOptionName ? this._version : this[key];
1678
+ }
1679
+ return result;
1680
+ }
1681
+ return this._optionValues;
1682
+ }
1683
+ optsWithGlobals() {
1684
+ return this._getCommandAndAncestors().reduce((combinedOptions, cmd) => Object.assign(combinedOptions, cmd.opts()), {});
1685
+ }
1686
+ error(message, errorOptions) {
1687
+ this._outputConfiguration.outputError(`${message}
1688
+ `, this._outputConfiguration.writeErr);
1689
+ if (typeof this._showHelpAfterError === "string") {
1690
+ this._outputConfiguration.writeErr(`${this._showHelpAfterError}
1691
+ `);
1692
+ } else if (this._showHelpAfterError) {
1693
+ this._outputConfiguration.writeErr(`
1694
+ `);
1695
+ this.outputHelp({ error: true });
1696
+ }
1697
+ const config = errorOptions || {};
1698
+ const exitCode = config.exitCode || 1;
1699
+ const code = config.code || "commander.error";
1700
+ this._exit(exitCode, code, message);
1701
+ }
1702
+ _parseOptionsEnv() {
1703
+ this.options.forEach((option) => {
1704
+ if (option.envVar && option.envVar in process2.env) {
1705
+ const optionKey = option.attributeName();
1706
+ if (this.getOptionValue(optionKey) === undefined || ["default", "config", "env"].includes(this.getOptionValueSource(optionKey))) {
1707
+ if (option.required || option.optional) {
1708
+ this.emit(`optionEnv:${option.name()}`, process2.env[option.envVar]);
1709
+ } else {
1710
+ this.emit(`optionEnv:${option.name()}`);
1711
+ }
1712
+ }
1713
+ }
1714
+ });
1715
+ }
1716
+ _parseOptionsImplied() {
1717
+ const dualHelper = new DualOptions(this.options);
1718
+ const hasCustomOptionValue = (optionKey) => {
1719
+ return this.getOptionValue(optionKey) !== undefined && !["default", "implied"].includes(this.getOptionValueSource(optionKey));
1720
+ };
1721
+ this.options.filter((option) => option.implied !== undefined && hasCustomOptionValue(option.attributeName()) && dualHelper.valueFromOption(this.getOptionValue(option.attributeName()), option)).forEach((option) => {
1722
+ Object.keys(option.implied).filter((impliedKey) => !hasCustomOptionValue(impliedKey)).forEach((impliedKey) => {
1723
+ this.setOptionValueWithSource(impliedKey, option.implied[impliedKey], "implied");
1724
+ });
1725
+ });
1726
+ }
1727
+ missingArgument(name) {
1728
+ const message = `error: missing required argument '${name}'`;
1729
+ this.error(message, { code: "commander.missingArgument" });
1730
+ }
1731
+ optionMissingArgument(option) {
1732
+ const message = `error: option '${option.flags}' argument missing`;
1733
+ this.error(message, { code: "commander.optionMissingArgument" });
1734
+ }
1735
+ missingMandatoryOptionValue(option) {
1736
+ const message = `error: required option '${option.flags}' not specified`;
1737
+ this.error(message, { code: "commander.missingMandatoryOptionValue" });
1738
+ }
1739
+ _conflictingOption(option, conflictingOption) {
1740
+ const findBestOptionFromValue = (option2) => {
1741
+ const optionKey = option2.attributeName();
1742
+ const optionValue = this.getOptionValue(optionKey);
1743
+ const negativeOption = this.options.find((target) => target.negate && optionKey === target.attributeName());
1744
+ const positiveOption = this.options.find((target) => !target.negate && optionKey === target.attributeName());
1745
+ if (negativeOption && (negativeOption.presetArg === undefined && optionValue === false || negativeOption.presetArg !== undefined && optionValue === negativeOption.presetArg)) {
1746
+ return negativeOption;
1747
+ }
1748
+ return positiveOption || option2;
1749
+ };
1750
+ const getErrorMessage = (option2) => {
1751
+ const bestOption = findBestOptionFromValue(option2);
1752
+ const optionKey = bestOption.attributeName();
1753
+ const source = this.getOptionValueSource(optionKey);
1754
+ if (source === "env") {
1755
+ return `environment variable '${bestOption.envVar}'`;
1756
+ }
1757
+ return `option '${bestOption.flags}'`;
1758
+ };
1759
+ const message = `error: ${getErrorMessage(option)} cannot be used with ${getErrorMessage(conflictingOption)}`;
1760
+ this.error(message, { code: "commander.conflictingOption" });
1761
+ }
1762
+ unknownOption(flag) {
1763
+ if (this._allowUnknownOption)
1764
+ return;
1765
+ let suggestion = "";
1766
+ if (flag.startsWith("--") && this._showSuggestionAfterError) {
1767
+ let candidateFlags = [];
1768
+ let command = this;
1769
+ do {
1770
+ const moreFlags = command.createHelp().visibleOptions(command).filter((option) => option.long).map((option) => option.long);
1771
+ candidateFlags = candidateFlags.concat(moreFlags);
1772
+ command = command.parent;
1773
+ } while (command && !command._enablePositionalOptions);
1774
+ suggestion = suggestSimilar(flag, candidateFlags);
1775
+ }
1776
+ const message = `error: unknown option '${flag}'${suggestion}`;
1777
+ this.error(message, { code: "commander.unknownOption" });
1778
+ }
1779
+ _excessArguments(receivedArgs) {
1780
+ if (this._allowExcessArguments)
1781
+ return;
1782
+ const expected = this.registeredArguments.length;
1783
+ const s = expected === 1 ? "" : "s";
1784
+ const forSubcommand = this.parent ? ` for '${this.name()}'` : "";
1785
+ const message = `error: too many arguments${forSubcommand}. Expected ${expected} argument${s} but got ${receivedArgs.length}.`;
1786
+ this.error(message, { code: "commander.excessArguments" });
1787
+ }
1788
+ unknownCommand() {
1789
+ const unknownName = this.args[0];
1790
+ let suggestion = "";
1791
+ if (this._showSuggestionAfterError) {
1792
+ const candidateNames = [];
1793
+ this.createHelp().visibleCommands(this).forEach((command) => {
1794
+ candidateNames.push(command.name());
1795
+ if (command.alias())
1796
+ candidateNames.push(command.alias());
1797
+ });
1798
+ suggestion = suggestSimilar(unknownName, candidateNames);
1799
+ }
1800
+ const message = `error: unknown command '${unknownName}'${suggestion}`;
1801
+ this.error(message, { code: "commander.unknownCommand" });
1802
+ }
1803
+ version(str, flags, description) {
1804
+ if (str === undefined)
1805
+ return this._version;
1806
+ this._version = str;
1807
+ flags = flags || "-V, --version";
1808
+ description = description || "output the version number";
1809
+ const versionOption = this.createOption(flags, description);
1810
+ this._versionOptionName = versionOption.attributeName();
1811
+ this._registerOption(versionOption);
1812
+ this.on("option:" + versionOption.name(), () => {
1813
+ this._outputConfiguration.writeOut(`${str}
1814
+ `);
1815
+ this._exit(0, "commander.version", str);
1816
+ });
1817
+ return this;
1818
+ }
1819
+ description(str, argsDescription) {
1820
+ if (str === undefined && argsDescription === undefined)
1821
+ return this._description;
1822
+ this._description = str;
1823
+ if (argsDescription) {
1824
+ this._argsDescription = argsDescription;
1825
+ }
1826
+ return this;
1827
+ }
1828
+ summary(str) {
1829
+ if (str === undefined)
1830
+ return this._summary;
1831
+ this._summary = str;
1832
+ return this;
1833
+ }
1834
+ alias(alias) {
1835
+ if (alias === undefined)
1836
+ return this._aliases[0];
1837
+ let command = this;
1838
+ if (this.commands.length !== 0 && this.commands[this.commands.length - 1]._executableHandler) {
1839
+ command = this.commands[this.commands.length - 1];
1840
+ }
1841
+ if (alias === command._name)
1842
+ throw new Error("Command alias can't be the same as its name");
1843
+ const matchingCommand = this.parent?._findCommand(alias);
1844
+ if (matchingCommand) {
1845
+ const existingCmd = [matchingCommand.name()].concat(matchingCommand.aliases()).join("|");
1846
+ throw new Error(`cannot add alias '${alias}' to command '${this.name()}' as already have command '${existingCmd}'`);
1847
+ }
1848
+ command._aliases.push(alias);
1849
+ return this;
1850
+ }
1851
+ aliases(aliases) {
1852
+ if (aliases === undefined)
1853
+ return this._aliases;
1854
+ aliases.forEach((alias) => this.alias(alias));
1855
+ return this;
1856
+ }
1857
+ usage(str) {
1858
+ if (str === undefined) {
1859
+ if (this._usage)
1860
+ return this._usage;
1861
+ const args = this.registeredArguments.map((arg) => {
1862
+ return humanReadableArgName(arg);
1863
+ });
1864
+ return [].concat(this.options.length || this._helpOption !== null ? "[options]" : [], this.commands.length ? "[command]" : [], this.registeredArguments.length ? args : []).join(" ");
1865
+ }
1866
+ this._usage = str;
1867
+ return this;
1868
+ }
1869
+ name(str) {
1870
+ if (str === undefined)
1871
+ return this._name;
1872
+ this._name = str;
1873
+ return this;
1874
+ }
1875
+ nameFromFilename(filename) {
1876
+ this._name = path.basename(filename, path.extname(filename));
1877
+ return this;
1878
+ }
1879
+ executableDir(path2) {
1880
+ if (path2 === undefined)
1881
+ return this._executableDir;
1882
+ this._executableDir = path2;
1883
+ return this;
1884
+ }
1885
+ helpInformation(contextOptions) {
1886
+ const helper = this.createHelp();
1887
+ const context = this._getOutputContext(contextOptions);
1888
+ helper.prepareContext({
1889
+ error: context.error,
1890
+ helpWidth: context.helpWidth,
1891
+ outputHasColors: context.hasColors
1892
+ });
1893
+ const text = helper.formatHelp(this, helper);
1894
+ if (context.hasColors)
1895
+ return text;
1896
+ return this._outputConfiguration.stripColor(text);
1897
+ }
1898
+ _getOutputContext(contextOptions) {
1899
+ contextOptions = contextOptions || {};
1900
+ const error = !!contextOptions.error;
1901
+ let baseWrite;
1902
+ let hasColors;
1903
+ let helpWidth;
1904
+ if (error) {
1905
+ baseWrite = (str) => this._outputConfiguration.writeErr(str);
1906
+ hasColors = this._outputConfiguration.getErrHasColors();
1907
+ helpWidth = this._outputConfiguration.getErrHelpWidth();
1908
+ } else {
1909
+ baseWrite = (str) => this._outputConfiguration.writeOut(str);
1910
+ hasColors = this._outputConfiguration.getOutHasColors();
1911
+ helpWidth = this._outputConfiguration.getOutHelpWidth();
1912
+ }
1913
+ const write = (str) => {
1914
+ if (!hasColors)
1915
+ str = this._outputConfiguration.stripColor(str);
1916
+ return baseWrite(str);
1917
+ };
1918
+ return { error, write, hasColors, helpWidth };
1919
+ }
1920
+ outputHelp(contextOptions) {
1921
+ let deprecatedCallback;
1922
+ if (typeof contextOptions === "function") {
1923
+ deprecatedCallback = contextOptions;
1924
+ contextOptions = undefined;
1925
+ }
1926
+ const outputContext = this._getOutputContext(contextOptions);
1927
+ const eventContext = {
1928
+ error: outputContext.error,
1929
+ write: outputContext.write,
1930
+ command: this
1931
+ };
1932
+ this._getCommandAndAncestors().reverse().forEach((command) => command.emit("beforeAllHelp", eventContext));
1933
+ this.emit("beforeHelp", eventContext);
1934
+ let helpInformation = this.helpInformation({ error: outputContext.error });
1935
+ if (deprecatedCallback) {
1936
+ helpInformation = deprecatedCallback(helpInformation);
1937
+ if (typeof helpInformation !== "string" && !Buffer.isBuffer(helpInformation)) {
1938
+ throw new Error("outputHelp callback must return a string or a Buffer");
1939
+ }
1940
+ }
1941
+ outputContext.write(helpInformation);
1942
+ if (this._getHelpOption()?.long) {
1943
+ this.emit(this._getHelpOption().long);
1944
+ }
1945
+ this.emit("afterHelp", eventContext);
1946
+ this._getCommandAndAncestors().forEach((command) => command.emit("afterAllHelp", eventContext));
1947
+ }
1948
+ helpOption(flags, description) {
1949
+ if (typeof flags === "boolean") {
1950
+ if (flags) {
1951
+ this._helpOption = this._helpOption ?? undefined;
1952
+ } else {
1953
+ this._helpOption = null;
1954
+ }
1955
+ return this;
1956
+ }
1957
+ flags = flags ?? "-h, --help";
1958
+ description = description ?? "display help for command";
1959
+ this._helpOption = this.createOption(flags, description);
1960
+ return this;
1961
+ }
1962
+ _getHelpOption() {
1963
+ if (this._helpOption === undefined) {
1964
+ this.helpOption(undefined, undefined);
1965
+ }
1966
+ return this._helpOption;
1967
+ }
1968
+ addHelpOption(option) {
1969
+ this._helpOption = option;
1970
+ return this;
1971
+ }
1972
+ help(contextOptions) {
1973
+ this.outputHelp(contextOptions);
1974
+ let exitCode = Number(process2.exitCode ?? 0);
1975
+ if (exitCode === 0 && contextOptions && typeof contextOptions !== "function" && contextOptions.error) {
1976
+ exitCode = 1;
1977
+ }
1978
+ this._exit(exitCode, "commander.help", "(outputHelp)");
1979
+ }
1980
+ addHelpText(position, text) {
1981
+ const allowedValues = ["beforeAll", "before", "after", "afterAll"];
1982
+ if (!allowedValues.includes(position)) {
1983
+ throw new Error(`Unexpected value for position to addHelpText.
1984
+ Expecting one of '${allowedValues.join("', '")}'`);
1985
+ }
1986
+ const helpEvent = `${position}Help`;
1987
+ this.on(helpEvent, (context) => {
1988
+ let helpStr;
1989
+ if (typeof text === "function") {
1990
+ helpStr = text({ error: context.error, command: context.command });
1991
+ } else {
1992
+ helpStr = text;
1993
+ }
1994
+ if (helpStr) {
1995
+ context.write(`${helpStr}
1996
+ `);
1997
+ }
1998
+ });
1999
+ return this;
2000
+ }
2001
+ _outputHelpIfRequested(args) {
2002
+ const helpOption = this._getHelpOption();
2003
+ const helpRequested = helpOption && args.find((arg) => helpOption.is(arg));
2004
+ if (helpRequested) {
2005
+ this.outputHelp();
2006
+ this._exit(0, "commander.helpDisplayed", "(outputHelp)");
2007
+ }
2008
+ }
2009
+ }
2010
+ function incrementNodeInspectorPort(args) {
2011
+ return args.map((arg) => {
2012
+ if (!arg.startsWith("--inspect")) {
2013
+ return arg;
2014
+ }
2015
+ let debugOption;
2016
+ let debugHost = "127.0.0.1";
2017
+ let debugPort = "9229";
2018
+ let match;
2019
+ if ((match = arg.match(/^(--inspect(-brk)?)$/)) !== null) {
2020
+ debugOption = match[1];
2021
+ } else if ((match = arg.match(/^(--inspect(-brk|-port)?)=([^:]+)$/)) !== null) {
2022
+ debugOption = match[1];
2023
+ if (/^\d+$/.test(match[3])) {
2024
+ debugPort = match[3];
2025
+ } else {
2026
+ debugHost = match[3];
2027
+ }
2028
+ } else if ((match = arg.match(/^(--inspect(-brk|-port)?)=([^:]+):(\d+)$/)) !== null) {
2029
+ debugOption = match[1];
2030
+ debugHost = match[3];
2031
+ debugPort = match[4];
2032
+ }
2033
+ if (debugOption && debugPort !== "0") {
2034
+ return `${debugOption}=${debugHost}:${parseInt(debugPort) + 1}`;
2035
+ }
2036
+ return arg;
2037
+ });
2038
+ }
2039
+ function useColor() {
2040
+ if (process2.env.NO_COLOR || process2.env.FORCE_COLOR === "0" || process2.env.FORCE_COLOR === "false")
2041
+ return false;
2042
+ if (process2.env.FORCE_COLOR || process2.env.CLICOLOR_FORCE !== undefined)
2043
+ return true;
2044
+ return;
2045
+ }
2046
+ exports.Command = Command;
2047
+ exports.useColor = useColor;
2048
+ });
2049
+
2050
+ // node_modules/commander/index.js
2051
+ var require_commander = __commonJS((exports) => {
2052
+ var { Argument } = require_argument();
2053
+ var { Command } = require_command();
2054
+ var { CommanderError, InvalidArgumentError } = require_error();
2055
+ var { Help } = require_help();
2056
+ var { Option } = require_option();
2057
+ exports.program = new Command;
2058
+ exports.createCommand = (name) => new Command(name);
2059
+ exports.createOption = (flags, description) => new Option(flags, description);
2060
+ exports.createArgument = (name, description) => new Argument(name, description);
2061
+ exports.Command = Command;
2062
+ exports.Option = Option;
2063
+ exports.Argument = Argument;
2064
+ exports.Help = Help;
2065
+ exports.CommanderError = CommanderError;
2066
+ exports.InvalidArgumentError = InvalidArgumentError;
2067
+ exports.InvalidOptionArgumentError = InvalidArgumentError;
2068
+ });
2069
+
2070
+ // node_modules/commander/esm.mjs
2071
+ var import__ = __toESM(require_commander(), 1);
2072
+ var {
2073
+ program,
2074
+ createCommand,
2075
+ createArgument,
2076
+ createOption,
2077
+ CommanderError,
2078
+ InvalidArgumentError,
2079
+ InvalidOptionArgumentError,
2080
+ Command,
2081
+ Argument,
2082
+ Option,
2083
+ Help
2084
+ } = import__.default;
2085
+
2086
+ // src/auth/login.ts
2087
+ import { createServer } from "http";
2088
+ import { exec } from "child_process";
2089
+
2090
+ // node_modules/oauth4webapi/build/index.js
2091
+ var USER_AGENT;
2092
+ if (typeof navigator === "undefined" || !navigator.userAgent?.startsWith?.("Mozilla/5.0 ")) {
2093
+ const NAME = "oauth4webapi";
2094
+ const VERSION = "v3.8.5";
2095
+ USER_AGENT = `${NAME}/${VERSION}`;
2096
+ }
2097
+ var ERR_INVALID_ARG_VALUE = "ERR_INVALID_ARG_VALUE";
2098
+ var ERR_INVALID_ARG_TYPE = "ERR_INVALID_ARG_TYPE";
2099
+ function CodedTypeError(message, code, cause) {
2100
+ const err = new TypeError(message, { cause });
2101
+ Object.assign(err, { code });
2102
+ return err;
2103
+ }
2104
+ var allowInsecureRequests = Symbol();
2105
+ var clockSkew = Symbol();
2106
+ var clockTolerance = Symbol();
2107
+ var customFetch = Symbol();
2108
+ var modifyAssertion = Symbol();
2109
+ var jweDecrypt = Symbol();
2110
+ var jwksCache = Symbol();
2111
+ var encoder = new TextEncoder;
2112
+ var decoder = new TextDecoder;
2113
+ function buf(input) {
2114
+ if (typeof input === "string") {
2115
+ return encoder.encode(input);
2116
+ }
2117
+ return decoder.decode(input);
2118
+ }
2119
+ var encodeBase64Url;
2120
+ if (Uint8Array.prototype.toBase64) {
2121
+ encodeBase64Url = (input) => {
2122
+ if (input instanceof ArrayBuffer) {
2123
+ input = new Uint8Array(input);
2124
+ }
2125
+ return input.toBase64({ alphabet: "base64url", omitPadding: true });
2126
+ };
2127
+ } else {
2128
+ const CHUNK_SIZE = 32768;
2129
+ encodeBase64Url = (input) => {
2130
+ if (input instanceof ArrayBuffer) {
2131
+ input = new Uint8Array(input);
2132
+ }
2133
+ const arr = [];
2134
+ for (let i = 0;i < input.byteLength; i += CHUNK_SIZE) {
2135
+ arr.push(String.fromCharCode.apply(null, input.subarray(i, i + CHUNK_SIZE)));
2136
+ }
2137
+ return btoa(arr.join("")).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
2138
+ };
2139
+ }
2140
+ var decodeBase64Url;
2141
+ if (Uint8Array.fromBase64) {
2142
+ decodeBase64Url = (input) => {
2143
+ try {
2144
+ return Uint8Array.fromBase64(input, { alphabet: "base64url" });
2145
+ } catch (cause) {
2146
+ throw CodedTypeError("The input to be decoded is not correctly encoded.", ERR_INVALID_ARG_VALUE, cause);
2147
+ }
2148
+ };
2149
+ } else {
2150
+ decodeBase64Url = (input) => {
2151
+ try {
2152
+ const binary = atob(input.replace(/-/g, "+").replace(/_/g, "/").replace(/\s/g, ""));
2153
+ const bytes = new Uint8Array(binary.length);
2154
+ for (let i = 0;i < binary.length; i++) {
2155
+ bytes[i] = binary.charCodeAt(i);
2156
+ }
2157
+ return bytes;
2158
+ } catch (cause) {
2159
+ throw CodedTypeError("The input to be decoded is not correctly encoded.", ERR_INVALID_ARG_VALUE, cause);
2160
+ }
2161
+ };
2162
+ }
2163
+ function b64u(input) {
2164
+ if (typeof input === "string") {
2165
+ return decodeBase64Url(input);
2166
+ }
2167
+ return encodeBase64Url(input);
2168
+ }
2169
+
2170
+ class UnsupportedOperationError extends Error {
2171
+ code;
2172
+ constructor(message, options) {
2173
+ super(message, options);
2174
+ this.name = this.constructor.name;
2175
+ this.code = UNSUPPORTED_OPERATION;
2176
+ Error.captureStackTrace?.(this, this.constructor);
2177
+ }
2178
+ }
2179
+
2180
+ class OperationProcessingError extends Error {
2181
+ code;
2182
+ constructor(message, options) {
2183
+ super(message, options);
2184
+ this.name = this.constructor.name;
2185
+ if (options?.code) {
2186
+ this.code = options?.code;
2187
+ }
2188
+ Error.captureStackTrace?.(this, this.constructor);
2189
+ }
2190
+ }
2191
+ function OPE(message, code, cause) {
2192
+ return new OperationProcessingError(message, { code, cause });
2193
+ }
2194
+ async function calculateJwkThumbprint(jwk) {
2195
+ let components;
2196
+ switch (jwk.kty) {
2197
+ case "EC":
2198
+ components = {
2199
+ crv: jwk.crv,
2200
+ kty: jwk.kty,
2201
+ x: jwk.x,
2202
+ y: jwk.y
2203
+ };
2204
+ break;
2205
+ case "OKP":
2206
+ components = {
2207
+ crv: jwk.crv,
2208
+ kty: jwk.kty,
2209
+ x: jwk.x
2210
+ };
2211
+ break;
2212
+ case "AKP":
2213
+ components = {
2214
+ alg: jwk.alg,
2215
+ kty: jwk.kty,
2216
+ pub: jwk.pub
2217
+ };
2218
+ break;
2219
+ case "RSA":
2220
+ components = {
2221
+ e: jwk.e,
2222
+ kty: jwk.kty,
2223
+ n: jwk.n
2224
+ };
2225
+ break;
2226
+ default:
2227
+ throw new UnsupportedOperationError("unsupported JWK key type", { cause: jwk });
2228
+ }
2229
+ return b64u(await crypto.subtle.digest("SHA-256", buf(JSON.stringify(components))));
2230
+ }
2231
+ function assertCryptoKey(key, it) {
2232
+ if (!(key instanceof CryptoKey)) {
2233
+ throw CodedTypeError(`${it} must be a CryptoKey`, ERR_INVALID_ARG_TYPE);
2234
+ }
2235
+ }
2236
+ function assertPrivateKey(key, it) {
2237
+ assertCryptoKey(key, it);
2238
+ if (key.type !== "private") {
2239
+ throw CodedTypeError(`${it} must be a private CryptoKey`, ERR_INVALID_ARG_VALUE);
2240
+ }
2241
+ }
2242
+ function assertPublicKey(key, it) {
2243
+ assertCryptoKey(key, it);
2244
+ if (key.type !== "public") {
2245
+ throw CodedTypeError(`${it} must be a public CryptoKey`, ERR_INVALID_ARG_VALUE);
2246
+ }
2247
+ }
2248
+ function assertString(input, it, code, cause) {
2249
+ try {
2250
+ if (typeof input !== "string") {
2251
+ throw CodedTypeError(`${it} must be a string`, ERR_INVALID_ARG_TYPE, cause);
2252
+ }
2253
+ if (input.length === 0) {
2254
+ throw CodedTypeError(`${it} must not be empty`, ERR_INVALID_ARG_VALUE, cause);
2255
+ }
2256
+ } catch (err) {
2257
+ if (code) {
2258
+ throw OPE(err.message, code, cause);
2259
+ }
2260
+ throw err;
2261
+ }
2262
+ }
2263
+ function randomBytes() {
2264
+ return b64u(crypto.getRandomValues(new Uint8Array(32)));
2265
+ }
2266
+ function generateRandomCodeVerifier() {
2267
+ return randomBytes();
2268
+ }
2269
+ function generateRandomState() {
2270
+ return randomBytes();
2271
+ }
2272
+ async function calculatePKCECodeChallenge(codeVerifier) {
2273
+ assertString(codeVerifier, "codeVerifier");
2274
+ return b64u(await crypto.subtle.digest("SHA-256", buf(codeVerifier)));
2275
+ }
2276
+ function psAlg(key) {
2277
+ switch (key.algorithm.hash.name) {
2278
+ case "SHA-256":
2279
+ return "PS256";
2280
+ case "SHA-384":
2281
+ return "PS384";
2282
+ case "SHA-512":
2283
+ return "PS512";
2284
+ default:
2285
+ throw new UnsupportedOperationError("unsupported RsaHashedKeyAlgorithm hash name", {
2286
+ cause: key
2287
+ });
2288
+ }
2289
+ }
2290
+ function rsAlg(key) {
2291
+ switch (key.algorithm.hash.name) {
2292
+ case "SHA-256":
2293
+ return "RS256";
2294
+ case "SHA-384":
2295
+ return "RS384";
2296
+ case "SHA-512":
2297
+ return "RS512";
2298
+ default:
2299
+ throw new UnsupportedOperationError("unsupported RsaHashedKeyAlgorithm hash name", {
2300
+ cause: key
2301
+ });
2302
+ }
2303
+ }
2304
+ function esAlg(key) {
2305
+ switch (key.algorithm.namedCurve) {
2306
+ case "P-256":
2307
+ return "ES256";
2308
+ case "P-384":
2309
+ return "ES384";
2310
+ case "P-521":
2311
+ return "ES512";
2312
+ default:
2313
+ throw new UnsupportedOperationError("unsupported EcKeyAlgorithm namedCurve", { cause: key });
2314
+ }
2315
+ }
2316
+ function keyToJws(key) {
2317
+ switch (key.algorithm.name) {
2318
+ case "RSA-PSS":
2319
+ return psAlg(key);
2320
+ case "RSASSA-PKCS1-v1_5":
2321
+ return rsAlg(key);
2322
+ case "ECDSA":
2323
+ return esAlg(key);
2324
+ case "Ed25519":
2325
+ case "ML-DSA-44":
2326
+ case "ML-DSA-65":
2327
+ case "ML-DSA-87":
2328
+ return key.algorithm.name;
2329
+ case "EdDSA":
2330
+ return "Ed25519";
2331
+ default:
2332
+ throw new UnsupportedOperationError("unsupported CryptoKey algorithm name", { cause: key });
2333
+ }
2334
+ }
2335
+ function getClockSkew(client) {
2336
+ const skew = client?.[clockSkew];
2337
+ return typeof skew === "number" && Number.isFinite(skew) ? skew : 0;
2338
+ }
2339
+ function epochTime() {
2340
+ return Math.floor(Date.now() / 1000);
2341
+ }
2342
+ async function signJwt(header, payload, key) {
2343
+ if (!key.usages.includes("sign")) {
2344
+ throw CodedTypeError('CryptoKey instances used for signing assertions must include "sign" in their "usages"', ERR_INVALID_ARG_VALUE);
2345
+ }
2346
+ const input = `${b64u(buf(JSON.stringify(header)))}.${b64u(buf(JSON.stringify(payload)))}`;
2347
+ const signature = b64u(await crypto.subtle.sign(keyToSubtle(key), key, buf(input)));
2348
+ return `${input}.${signature}`;
2349
+ }
2350
+ var jwkCache;
2351
+ async function getSetPublicJwkCache(key, alg) {
2352
+ const { kty, e, n, x, y, crv, pub } = await crypto.subtle.exportKey("jwk", key);
2353
+ const jwk = { kty, e, n, x, y, crv, pub };
2354
+ if (kty === "AKP")
2355
+ jwk.alg = alg;
2356
+ jwkCache.set(key, jwk);
2357
+ return jwk;
2358
+ }
2359
+ async function publicJwk(key, alg) {
2360
+ jwkCache ||= new WeakMap;
2361
+ return jwkCache.get(key) || getSetPublicJwkCache(key, alg);
2362
+ }
2363
+ var URLParse = URL.parse ? (url, base) => URL.parse(url, base) : (url, base) => {
2364
+ try {
2365
+ return new URL(url, base);
2366
+ } catch {
2367
+ return null;
2368
+ }
2369
+ };
2370
+ class DPoPHandler {
2371
+ #header;
2372
+ #privateKey;
2373
+ #publicKey;
2374
+ #clockSkew;
2375
+ #modifyAssertion;
2376
+ #map;
2377
+ #jkt;
2378
+ constructor(client, keyPair, options) {
2379
+ assertPrivateKey(keyPair?.privateKey, '"DPoP.privateKey"');
2380
+ assertPublicKey(keyPair?.publicKey, '"DPoP.publicKey"');
2381
+ if (!keyPair.publicKey.extractable) {
2382
+ throw CodedTypeError('"DPoP.publicKey.extractable" must be true', ERR_INVALID_ARG_VALUE);
2383
+ }
2384
+ this.#modifyAssertion = options?.[modifyAssertion];
2385
+ this.#clockSkew = getClockSkew(client);
2386
+ this.#privateKey = keyPair.privateKey;
2387
+ this.#publicKey = keyPair.publicKey;
2388
+ branded.add(this);
2389
+ }
2390
+ #get(key) {
2391
+ this.#map ||= new Map;
2392
+ let item = this.#map.get(key);
2393
+ if (item) {
2394
+ this.#map.delete(key);
2395
+ this.#map.set(key, item);
2396
+ }
2397
+ return item;
2398
+ }
2399
+ #set(key, val) {
2400
+ this.#map ||= new Map;
2401
+ this.#map.delete(key);
2402
+ if (this.#map.size === 100) {
2403
+ this.#map.delete(this.#map.keys().next().value);
2404
+ }
2405
+ this.#map.set(key, val);
2406
+ }
2407
+ async calculateThumbprint() {
2408
+ if (!this.#jkt) {
2409
+ const jwk = await crypto.subtle.exportKey("jwk", this.#publicKey);
2410
+ this.#jkt ||= await calculateJwkThumbprint(jwk);
2411
+ }
2412
+ return this.#jkt;
2413
+ }
2414
+ async addProof(url, headers, htm, accessToken) {
2415
+ const alg = keyToJws(this.#privateKey);
2416
+ this.#header ||= {
2417
+ alg,
2418
+ typ: "dpop+jwt",
2419
+ jwk: await publicJwk(this.#publicKey, alg)
2420
+ };
2421
+ const nonce = this.#get(url.origin);
2422
+ const now = epochTime() + this.#clockSkew;
2423
+ const payload = {
2424
+ iat: now,
2425
+ jti: randomBytes(),
2426
+ htm,
2427
+ nonce,
2428
+ htu: `${url.origin}${url.pathname}`,
2429
+ ath: accessToken ? b64u(await crypto.subtle.digest("SHA-256", buf(accessToken))) : undefined
2430
+ };
2431
+ this.#modifyAssertion?.(this.#header, payload);
2432
+ headers.set("dpop", await signJwt(this.#header, payload, this.#privateKey));
2433
+ }
2434
+ cacheNonce(response, url) {
2435
+ try {
2436
+ const nonce = response.headers.get("dpop-nonce");
2437
+ if (nonce) {
2438
+ this.#set(url.origin, nonce);
2439
+ }
2440
+ } catch {}
2441
+ }
2442
+ }
2443
+ var tokenMatch = "[a-zA-Z0-9!#$%&\\'\\*\\+\\-\\.\\^_`\\|~]+";
2444
+ var token68Match = "[a-zA-Z0-9\\-\\._\\~\\+\\/]+={0,2}";
2445
+ var quotedMatch = '"((?:[^"\\\\]|\\\\[\\s\\S])*)"';
2446
+ var quotedParamMatcher = "(" + tokenMatch + ")\\s*=\\s*" + quotedMatch;
2447
+ var paramMatcher = "(" + tokenMatch + ")\\s*=\\s*(" + tokenMatch + ")";
2448
+ var schemeRE = new RegExp("^[,\\s]*(" + tokenMatch + ")");
2449
+ var quotedParamRE = new RegExp("^[,\\s]*" + quotedParamMatcher + "[,\\s]*(.*)");
2450
+ var unquotedParamRE = new RegExp("^[,\\s]*" + paramMatcher + "[,\\s]*(.*)");
2451
+ var token68ParamRE = new RegExp("^(" + token68Match + ")(?:$|[,\\s])(.*)");
2452
+ var skipSubjectCheck = Symbol();
2453
+ var idTokenClaims = new WeakMap;
2454
+ var jwtRefs = new WeakMap;
2455
+ var branded = new WeakSet;
2456
+ var nopkce = Symbol();
2457
+ var expectNoNonce = Symbol();
2458
+ var skipAuthTimeCheck = Symbol();
2459
+ var UNSUPPORTED_OPERATION = "OAUTH_UNSUPPORTED_OPERATION";
2460
+ function checkRsaKeyAlgorithm(key) {
2461
+ const { algorithm } = key;
2462
+ if (typeof algorithm.modulusLength !== "number" || algorithm.modulusLength < 2048) {
2463
+ throw new UnsupportedOperationError(`unsupported ${algorithm.name} modulusLength`, {
2464
+ cause: key
2465
+ });
2466
+ }
2467
+ }
2468
+ function ecdsaHashName(key) {
2469
+ const { algorithm } = key;
2470
+ switch (algorithm.namedCurve) {
2471
+ case "P-256":
2472
+ return "SHA-256";
2473
+ case "P-384":
2474
+ return "SHA-384";
2475
+ case "P-521":
2476
+ return "SHA-512";
2477
+ default:
2478
+ throw new UnsupportedOperationError("unsupported ECDSA namedCurve", { cause: key });
2479
+ }
2480
+ }
2481
+ function keyToSubtle(key) {
2482
+ switch (key.algorithm.name) {
2483
+ case "ECDSA":
2484
+ return {
2485
+ name: key.algorithm.name,
2486
+ hash: ecdsaHashName(key)
2487
+ };
2488
+ case "RSA-PSS": {
2489
+ checkRsaKeyAlgorithm(key);
2490
+ switch (key.algorithm.hash.name) {
2491
+ case "SHA-256":
2492
+ case "SHA-384":
2493
+ case "SHA-512":
2494
+ return {
2495
+ name: key.algorithm.name,
2496
+ saltLength: parseInt(key.algorithm.hash.name.slice(-3), 10) >> 3
2497
+ };
2498
+ default:
2499
+ throw new UnsupportedOperationError("unsupported RSA-PSS hash name", { cause: key });
2500
+ }
2501
+ }
2502
+ case "RSASSA-PKCS1-v1_5":
2503
+ checkRsaKeyAlgorithm(key);
2504
+ return key.algorithm.name;
2505
+ case "ML-DSA-44":
2506
+ case "ML-DSA-65":
2507
+ case "ML-DSA-87":
2508
+ case "Ed25519":
2509
+ return key.algorithm.name;
2510
+ }
2511
+ throw new UnsupportedOperationError("unsupported CryptoKey algorithm name", { cause: key });
2512
+ }
2513
+ var skipStateCheck = Symbol();
2514
+ var expectNoState = Symbol();
2515
+ var _nodiscoverycheck = Symbol();
2516
+ var _expectedIssuer = Symbol();
2517
+
2518
+ // src/store/credentials.ts
2519
+ import { join } from "path";
2520
+ import { homedir } from "os";
2521
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, chmodSync } from "fs";
2522
+ var CONFIG_DIR = join(homedir(), ".alpineads");
2523
+ var CREDENTIALS_PATH = join(CONFIG_DIR, "credentials.json");
2524
+ function ensureDir() {
2525
+ if (!existsSync(CONFIG_DIR)) {
2526
+ mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 });
2527
+ }
2528
+ }
2529
+ function loadCredentials() {
2530
+ if (!existsSync(CREDENTIALS_PATH)) {
2531
+ return { version: 1, current_profile: "default", profiles: {} };
2532
+ }
2533
+ const data = readFileSync(CREDENTIALS_PATH, "utf-8");
2534
+ return JSON.parse(data);
2535
+ }
2536
+ function saveCredentials(creds) {
2537
+ ensureDir();
2538
+ writeFileSync(CREDENTIALS_PATH, JSON.stringify(creds, null, 2), {
2539
+ mode: 384
2540
+ });
2541
+ chmodSync(CREDENTIALS_PATH, 384);
2542
+ }
2543
+ function getCurrentProfile(creds) {
2544
+ return creds.profiles[creds.current_profile] ?? null;
2545
+ }
2546
+ function getProfile(creds, name) {
2547
+ return creds.profiles[name] ?? null;
2548
+ }
2549
+
2550
+ // src/store/context.ts
2551
+ import { join as join2 } from "path";
2552
+ import { homedir as homedir2 } from "os";
2553
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, chmodSync as chmodSync2 } from "fs";
2554
+ var CONFIG_DIR2 = join2(homedir2(), ".alpineads");
2555
+ var CONTEXT_PATH = join2(CONFIG_DIR2, "context.json");
2556
+ function ensureDir2() {
2557
+ if (!existsSync2(CONFIG_DIR2)) {
2558
+ mkdirSync2(CONFIG_DIR2, { recursive: true, mode: 448 });
2559
+ }
2560
+ }
2561
+ function loadContext() {
2562
+ if (!existsSync2(CONTEXT_PATH)) {
2563
+ return { version: 1, current_profile: "default", profiles: {} };
2564
+ }
2565
+ const data = readFileSync2(CONTEXT_PATH, "utf-8");
2566
+ return JSON.parse(data);
2567
+ }
2568
+ function saveContext(ctx) {
2569
+ ensureDir2();
2570
+ writeFileSync2(CONTEXT_PATH, JSON.stringify(ctx, null, 2), { mode: 384 });
2571
+ chmodSync2(CONTEXT_PATH, 384);
2572
+ }
2573
+ function getCurrentContext(ctx) {
2574
+ return ctx.profiles[ctx.current_profile] ?? null;
2575
+ }
2576
+ function setCurrentContext(ctx, update) {
2577
+ const current = ctx.profiles[ctx.current_profile] ?? {
2578
+ tenant_id: null,
2579
+ app: "dsp",
2580
+ current_capability: null,
2581
+ impersonation: null
2582
+ };
2583
+ ctx.profiles[ctx.current_profile] = { ...current, ...update };
2584
+ return ctx;
2585
+ }
2586
+ function appToCapability(app) {
2587
+ switch (app) {
2588
+ case "dsp":
2589
+ return "ADVERTISER";
2590
+ case "ssp":
2591
+ return "PUBLISHER";
2592
+ case "console":
2593
+ return null;
2594
+ default:
2595
+ return null;
2596
+ }
2597
+ }
2598
+
2599
+ // src/utils/jwt.ts
2600
+ function decodeJwt(token) {
2601
+ try {
2602
+ const parts = token.split(".");
2603
+ if (parts.length !== 3)
2604
+ return null;
2605
+ const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf-8"));
2606
+ return payload;
2607
+ } catch {
2608
+ return null;
2609
+ }
2610
+ }
2611
+ function getAuthClaims(claims) {
2612
+ return claims["https://alpineads.com/auth"] ?? claims.ext?.["https://alpineads.com/auth"] ?? null;
2613
+ }
2614
+ function isTokenExpired(claims) {
2615
+ return Date.now() / 1000 > claims.exp - 30;
2616
+ }
2617
+
2618
+ // src/auth/login.ts
2619
+ var LOOPBACK_PORT = 18920;
2620
+ var REDIRECT_URI = `http://127.0.0.1:${LOOPBACK_PORT}/callback`;
2621
+ var CLIENT_ID = "alpineads-cli";
2622
+ var SCOPES = "openid offline_access";
2623
+ var DEFAULT_API_URL = "https://api.a8.tech";
2624
+ var DEFAULT_AUTH_URL = "https://auth.a8.tech";
2625
+ async function login(opts = {}) {
2626
+ const profileName = opts.profile ?? "default";
2627
+ const apiUrl = opts.apiUrl ?? DEFAULT_API_URL;
2628
+ const authUrl = opts.authUrl ?? DEFAULT_AUTH_URL;
2629
+ const tokenEndpoint = `${authUrl}/.ory/hydra/oauth2/token`;
2630
+ const authorizationEndpoint = `${authUrl}/.ory/hydra/oauth2/auth`;
2631
+ const codeVerifier = generateRandomCodeVerifier();
2632
+ const codeChallenge = await calculatePKCECodeChallenge(codeVerifier);
2633
+ const state = generateRandomState();
2634
+ const authorizeUrl = new URL(authorizationEndpoint);
2635
+ authorizeUrl.searchParams.set("response_type", "code");
2636
+ authorizeUrl.searchParams.set("client_id", CLIENT_ID);
2637
+ authorizeUrl.searchParams.set("redirect_uri", REDIRECT_URI);
2638
+ authorizeUrl.searchParams.set("scope", SCOPES);
2639
+ authorizeUrl.searchParams.set("state", state);
2640
+ authorizeUrl.searchParams.set("code_challenge", codeChallenge);
2641
+ authorizeUrl.searchParams.set("code_challenge_method", "S256");
2642
+ authorizeUrl.searchParams.set("audience", apiUrl);
2643
+ if (opts.loginHint) {
2644
+ authorizeUrl.searchParams.set("login_hint", opts.loginHint);
2645
+ }
2646
+ if (opts.tenantId) {
2647
+ authorizeUrl.searchParams.set("tenant_id", opts.tenantId);
2648
+ }
2649
+ const code = await waitForAuthorizationCode(authorizeUrl.toString(), state);
2650
+ console.log("Exchanging authorization code for tokens...");
2651
+ const tokenResponse = await fetch(tokenEndpoint, {
2652
+ method: "POST",
2653
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
2654
+ body: new URLSearchParams({
2655
+ grant_type: "authorization_code",
2656
+ code,
2657
+ redirect_uri: REDIRECT_URI,
2658
+ client_id: CLIENT_ID,
2659
+ code_verifier: codeVerifier
2660
+ })
2661
+ });
2662
+ if (!tokenResponse.ok) {
2663
+ const text = await tokenResponse.text();
2664
+ throw new Error(`Token exchange failed (${tokenResponse.status}): ${text}`);
2665
+ }
2666
+ const tokens = await tokenResponse.json();
2667
+ const claims = decodeJwt(tokens.access_token);
2668
+ const authClaims = claims ? getAuthClaims(claims) : null;
2669
+ const tenants = await fetchTenants(apiUrl, tokens.access_token);
2670
+ const expiresAt = new Date(Date.now() + tokens.expires_in * 1000).toISOString();
2671
+ const profileCreds = {
2672
+ email: authClaims?.email ?? "unknown",
2673
+ user_id: authClaims?.user_id ?? claims?.sub ?? "unknown",
2674
+ access_token: tokens.access_token,
2675
+ refresh_token: tokens.refresh_token ?? "",
2676
+ expires_at: expiresAt,
2677
+ token_endpoint: tokenEndpoint,
2678
+ api_url: apiUrl,
2679
+ client_id: CLIENT_ID,
2680
+ tenants
2681
+ };
2682
+ const creds = loadCredentials();
2683
+ creds.current_profile = profileName;
2684
+ creds.profiles[profileName] = profileCreds;
2685
+ saveCredentials(creds);
2686
+ const tenantId = authClaims?.tenant_id ?? tenants[0]?.tenant_id ?? null;
2687
+ const tenant = tenants.find((t) => t.tenant_id === tenantId);
2688
+ const app = deriveApp(tenant);
2689
+ const capability = appToCapability(app);
2690
+ const profileCtx = {
2691
+ tenant_id: tenantId,
2692
+ app,
2693
+ current_capability: capability,
2694
+ impersonation: null
2695
+ };
2696
+ const ctx = loadContext();
2697
+ ctx.current_profile = profileName;
2698
+ ctx.profiles[profileName] = profileCtx;
2699
+ saveContext(ctx);
2700
+ console.log(`
2701
+ Logged in as ${profileCreds.email}`);
2702
+ console.log(`Profile: ${profileName}`);
2703
+ if (tenant) {
2704
+ console.log(`Tenant: ${tenant.tenant_name} (${tenant.tenant_id})`);
2705
+ }
2706
+ console.log(`App: ${app}`);
2707
+ if (capability) {
2708
+ console.log(`Capability: ${capability}`);
2709
+ }
2710
+ }
2711
+ function deriveApp(tenant) {
2712
+ if (!tenant)
2713
+ return "dsp";
2714
+ if (tenant.is_platform)
2715
+ return "console";
2716
+ if (tenant.capabilities.includes("ADVERTISER"))
2717
+ return "dsp";
2718
+ if (tenant.capabilities.includes("PUBLISHER"))
2719
+ return "ssp";
2720
+ return "dsp";
2721
+ }
2722
+ async function fetchTenants(apiUrl, accessToken) {
2723
+ for (const prefix of ["/api/v1/dsp", "/api/v1/ssp", "/api/v1/console"]) {
2724
+ try {
2725
+ const response = await fetch(`${apiUrl}${prefix}/me`, {
2726
+ headers: { Authorization: `Bearer ${accessToken}` }
2727
+ });
2728
+ if (response.ok) {
2729
+ const data = await response.json();
2730
+ if (data.data?.tenants)
2731
+ return data.data.tenants;
2732
+ if (data.data?.tenant_id) {
2733
+ return [
2734
+ {
2735
+ tenant_id: data.data.tenant_id,
2736
+ tenant_name: data.data.tenant_name ?? "Unknown",
2737
+ capabilities: data.data.capabilities ?? [],
2738
+ is_platform: data.data.is_platform ?? false,
2739
+ role: data.data.role ?? "unknown"
2740
+ }
2741
+ ];
2742
+ }
2743
+ }
2744
+ } catch {}
2745
+ }
2746
+ const claims = decodeJwt(accessToken);
2747
+ const authClaims = claims ? getAuthClaims(claims) : null;
2748
+ if (authClaims?.tenant_id) {
2749
+ return [
2750
+ {
2751
+ tenant_id: authClaims.tenant_id,
2752
+ tenant_name: authClaims.tenant_name ?? "Unknown",
2753
+ capabilities: authClaims.tenant_capabilities ?? [],
2754
+ is_platform: false,
2755
+ role: authClaims.role ?? "unknown"
2756
+ }
2757
+ ];
2758
+ }
2759
+ return [];
2760
+ }
2761
+ function waitForAuthorizationCode(authorizeUrl, expectedState) {
2762
+ return new Promise((resolve, reject) => {
2763
+ const timeout = setTimeout(() => {
2764
+ server?.close();
2765
+ reject(new Error("Login timed out after 120 seconds"));
2766
+ }, 120000);
2767
+ const server = createServer((req, res) => {
2768
+ const url = new URL(req.url ?? "/", `http://127.0.0.1:${LOOPBACK_PORT}`);
2769
+ if (url.pathname !== "/callback") {
2770
+ res.writeHead(404);
2771
+ res.end("Not found");
2772
+ return;
2773
+ }
2774
+ const code = url.searchParams.get("code");
2775
+ const state = url.searchParams.get("state");
2776
+ const error = url.searchParams.get("error");
2777
+ if (error) {
2778
+ const desc = url.searchParams.get("error_description") ?? error;
2779
+ res.writeHead(200, { "Content-Type": "text/html" });
2780
+ res.end(htmlPage("Login Failed", desc));
2781
+ clearTimeout(timeout);
2782
+ server.close();
2783
+ reject(new Error(`OAuth error: ${desc}`));
2784
+ return;
2785
+ }
2786
+ if (!code || state !== expectedState) {
2787
+ res.writeHead(400, { "Content-Type": "text/html" });
2788
+ res.end(htmlPage("Invalid Callback", "State mismatch or missing code."));
2789
+ clearTimeout(timeout);
2790
+ server.close();
2791
+ reject(new Error("Invalid OAuth callback: state mismatch or missing code"));
2792
+ return;
2793
+ }
2794
+ res.writeHead(200, { "Content-Type": "text/html" });
2795
+ res.end(htmlPage("Login Successful", "You can close this window and return to the terminal."));
2796
+ clearTimeout(timeout);
2797
+ server.close();
2798
+ resolve(code);
2799
+ });
2800
+ server.on("error", (err) => {
2801
+ clearTimeout(timeout);
2802
+ if (err.code === "EADDRINUSE") {
2803
+ reject(new Error(`Error: Port ${LOOPBACK_PORT} is in use. Close any other 'a8techads auth login' process and retry.`));
2804
+ } else {
2805
+ reject(err);
2806
+ }
2807
+ });
2808
+ server.listen(LOOPBACK_PORT, "127.0.0.1", () => {
2809
+ console.log(`
2810
+ Opening browser for authentication...`);
2811
+ console.log(`If the browser does not open, visit this URL manually:
2812
+ `);
2813
+ console.log(` ${authorizeUrl}
2814
+ `);
2815
+ const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
2816
+ exec(`${cmd} "${authorizeUrl}"`, () => {});
2817
+ });
2818
+ });
2819
+ }
2820
+ function htmlPage(title, message) {
2821
+ return `<!DOCTYPE html><html><body style="font-family:system-ui;text-align:center;padding:40px"><h1>${title}</h1><p>${message}</p></body></html>`;
2822
+ }
2823
+
2824
+ // src/auth/client-credentials.ts
2825
+ var DEFAULT_API_URL2 = "https://api.a8.tech";
2826
+ var DEFAULT_AUTH_URL2 = "https://auth.a8.tech";
2827
+ async function loginClientCredentials(opts) {
2828
+ const apiUrl = opts.apiUrl ?? DEFAULT_API_URL2;
2829
+ const authUrl = opts.authUrl ?? DEFAULT_AUTH_URL2;
2830
+ const tokenEndpoint = `${authUrl}/.ory/hydra/oauth2/token`;
2831
+ console.log("Authenticating with client credentials...");
2832
+ const resp = await fetch(tokenEndpoint, {
2833
+ method: "POST",
2834
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
2835
+ body: new URLSearchParams({
2836
+ grant_type: "client_credentials",
2837
+ client_id: opts.clientId,
2838
+ client_secret: opts.clientSecret,
2839
+ scope: "openid"
2840
+ })
2841
+ });
2842
+ if (!resp.ok) {
2843
+ const text = await resp.text();
2844
+ throw new Error(`Client credentials auth failed (${resp.status}): ${text}`);
2845
+ }
2846
+ const tokens = await resp.json();
2847
+ const claims = decodeJwt(tokens.access_token);
2848
+ const authClaims = claims ? getAuthClaims(claims) : null;
2849
+ const profileName = `client:${opts.clientId}`;
2850
+ const tenants = await fetchTenantsForClient(apiUrl, tokens.access_token);
2851
+ const expiresAt = new Date(Date.now() + tokens.expires_in * 1000).toISOString();
2852
+ const profileCreds = {
2853
+ email: `client:${opts.clientId}`,
2854
+ user_id: authClaims?.user_id ?? claims?.sub ?? opts.clientId,
2855
+ access_token: tokens.access_token,
2856
+ refresh_token: "",
2857
+ expires_at: expiresAt,
2858
+ token_endpoint: tokenEndpoint,
2859
+ api_url: apiUrl,
2860
+ client_id: opts.clientId,
2861
+ tenants
2862
+ };
2863
+ const creds = loadCredentials();
2864
+ creds.current_profile = profileName;
2865
+ creds.profiles[profileName] = profileCreds;
2866
+ saveCredentials(creds);
2867
+ const tenantId = authClaims?.tenant_id ?? tenants[0]?.tenant_id ?? null;
2868
+ const tenant = tenants.find((t) => t.tenant_id === tenantId);
2869
+ const app = deriveApp2(tenant);
2870
+ const capability = appToCapability(app);
2871
+ const profileCtx = {
2872
+ tenant_id: tenantId,
2873
+ app,
2874
+ current_capability: capability,
2875
+ impersonation: null
2876
+ };
2877
+ const ctx = loadContext();
2878
+ ctx.current_profile = profileName;
2879
+ ctx.profiles[profileName] = profileCtx;
2880
+ saveContext(ctx);
2881
+ console.log(`
2882
+ Authenticated as ${profileName}`);
2883
+ console.log(`Profile: ${profileName}`);
2884
+ if (tenant) {
2885
+ console.log(`Tenant: ${tenant.tenant_name} (${tenant.tenant_id})`);
2886
+ }
2887
+ console.log(`App: ${app}`);
2888
+ if (capability) {
2889
+ console.log(`Capability: ${capability}`);
2890
+ }
2891
+ console.log(`Token expires: ${expiresAt}`);
2892
+ console.log(`
2893
+ Note: Client credentials tokens cannot be refreshed. Re-authenticate on expiry.`);
2894
+ }
2895
+ function deriveApp2(tenant) {
2896
+ if (!tenant)
2897
+ return "dsp";
2898
+ if (tenant.is_platform)
2899
+ return "console";
2900
+ if (tenant.capabilities.includes("ADVERTISER"))
2901
+ return "dsp";
2902
+ if (tenant.capabilities.includes("PUBLISHER"))
2903
+ return "ssp";
2904
+ return "dsp";
2905
+ }
2906
+ async function fetchTenantsForClient(apiUrl, accessToken) {
2907
+ for (const prefix of ["/api/v1/dsp", "/api/v1/ssp", "/api/v1/console"]) {
2908
+ try {
2909
+ const response = await fetch(`${apiUrl}${prefix}/me`, {
2910
+ headers: { Authorization: `Bearer ${accessToken}` }
2911
+ });
2912
+ if (response.ok) {
2913
+ const data = await response.json();
2914
+ if (data.data?.tenants)
2915
+ return data.data.tenants;
2916
+ if (data.data?.tenant_id) {
2917
+ return [{
2918
+ tenant_id: data.data.tenant_id,
2919
+ tenant_name: data.data.tenant_name ?? "Unknown",
2920
+ capabilities: data.data.capabilities ?? [],
2921
+ is_platform: data.data.is_platform ?? false,
2922
+ role: data.data.role ?? "unknown"
2923
+ }];
2924
+ }
2925
+ }
2926
+ } catch {}
2927
+ }
2928
+ const claims = decodeJwt(accessToken);
2929
+ const authClaims = claims ? getAuthClaims(claims) : null;
2930
+ if (authClaims?.tenant_id) {
2931
+ return [{
2932
+ tenant_id: authClaims.tenant_id,
2933
+ tenant_name: authClaims.tenant_name ?? "Unknown",
2934
+ capabilities: authClaims.tenant_capabilities ?? [],
2935
+ is_platform: false,
2936
+ role: authClaims.role ?? "unknown"
2937
+ }];
2938
+ }
2939
+ return [];
2940
+ }
2941
+
2942
+ // src/auth/logout.ts
2943
+ function logout(profileName) {
2944
+ const creds = loadCredentials();
2945
+ const name = profileName ?? creds.current_profile;
2946
+ if (!creds.profiles[name]) {
2947
+ console.log(`Profile "${name}" not found.`);
2948
+ return;
2949
+ }
2950
+ delete creds.profiles[name];
2951
+ saveCredentials(creds);
2952
+ const ctx = loadContext();
2953
+ delete ctx.profiles[name];
2954
+ saveContext(ctx);
2955
+ console.log(`Logged out from profile "${name}".`);
2956
+ }
2957
+
2958
+ // src/auth/refresh.ts
2959
+ async function refreshTokenIfNeeded(profileName) {
2960
+ const creds = loadCredentials();
2961
+ const profile = profileName ? getProfile(creds, profileName) : getCurrentProfile(creds);
2962
+ if (!profile)
2963
+ return false;
2964
+ const claims = decodeJwt(profile.access_token);
2965
+ if (!claims || !isTokenExpired(claims))
2966
+ return false;
2967
+ if (!profile.refresh_token) {
2968
+ throw new Error('Token expired and no refresh token available. Run "a8techads auth login" to re-authenticate.');
2969
+ }
2970
+ const response = await fetch(profile.token_endpoint, {
2971
+ method: "POST",
2972
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
2973
+ body: new URLSearchParams({
2974
+ grant_type: "refresh_token",
2975
+ refresh_token: profile.refresh_token,
2976
+ client_id: profile.client_id
2977
+ })
2978
+ });
2979
+ if (!response.ok) {
2980
+ const text = await response.text();
2981
+ throw new Error(`Token refresh failed (${response.status}): ${text}
2982
+ Run "a8techads auth login" to re-authenticate.`);
2983
+ }
2984
+ const tokens = await response.json();
2985
+ const expiresAt = new Date(Date.now() + tokens.expires_in * 1000).toISOString();
2986
+ profile.access_token = tokens.access_token;
2987
+ if (tokens.refresh_token) {
2988
+ profile.refresh_token = tokens.refresh_token;
2989
+ }
2990
+ profile.expires_at = expiresAt;
2991
+ saveCredentials(creds);
2992
+ return true;
2993
+ }
2994
+
2995
+ // src/auth/token.ts
2996
+ async function outputToken(profileName) {
2997
+ const creds = loadCredentials();
2998
+ const name = profileName ?? creds.current_profile;
2999
+ await refreshTokenIfNeeded(name);
3000
+ const freshCreds = loadCredentials();
3001
+ const profile = getProfile(freshCreds, name);
3002
+ if (!profile) {
3003
+ console.error(`No credentials for profile "${name}". Run "a8techads auth login --profile ${name}" first.`);
3004
+ process.exit(1);
3005
+ }
3006
+ process.stdout.write(profile.access_token);
3007
+ }
3008
+
3009
+ // src/auth/status.ts
3010
+ function showAuthStatus(profileNameOverride) {
3011
+ const creds = loadCredentials();
3012
+ const ctx = loadContext();
3013
+ const profileName = profileNameOverride ?? creds.current_profile;
3014
+ const profile = creds.profiles[profileName] ?? null;
3015
+ const profileCtx = ctx.profiles[profileName] ?? null;
3016
+ console.log(`Profile: ${profileName}`);
3017
+ if (!profile) {
3018
+ console.log("Auth mode: none");
3019
+ console.log('Token: not authenticated (run "a8techads auth login")');
3020
+ return;
3021
+ }
3022
+ const isClientCreds = profileName.startsWith("client:");
3023
+ console.log(`Auth mode: ${isClientCreds ? "client_credentials" : "browser"}`);
3024
+ if (isClientCreds) {
3025
+ console.log(`Client ID: ${profile.client_id}`);
3026
+ } else {
3027
+ console.log(`User: ${profile.email}`);
3028
+ }
3029
+ const claims = decodeJwt(profile.access_token);
3030
+ if (claims) {
3031
+ const expired = isTokenExpired(claims);
3032
+ if (expired) {
3033
+ if (isClientCreds) {
3034
+ console.log("Token: expired (re-authenticate with client credentials)");
3035
+ } else if (profile.refresh_token) {
3036
+ console.log("Token: expired (will auto-refresh on next use)");
3037
+ } else {
3038
+ console.log('Token: expired (run "a8techads auth login")');
3039
+ }
3040
+ } else {
3041
+ const remainingMs = (claims.exp - 30) * 1000 - Date.now();
3042
+ const remainingMin = Math.floor(remainingMs / 60000);
3043
+ const remainingHrs = Math.floor(remainingMin / 60);
3044
+ const mins = remainingMin % 60;
3045
+ const timeStr = remainingHrs > 0 ? `${remainingHrs}h ${mins}m` : `${mins}m`;
3046
+ console.log(`Token: valid (expires in ${timeStr})`);
3047
+ }
3048
+ const authClaims = getAuthClaims(claims);
3049
+ if (authClaims) {
3050
+ const tenant = profile.tenants.find((t) => t.tenant_id === authClaims.tenant_id);
3051
+ const tenantDisplay = tenant ? `${tenant.tenant_name} (${authClaims.tenant_id})` : authClaims.tenant_id ?? "unknown";
3052
+ console.log(`Tenant: ${tenantDisplay}`);
3053
+ const cap = profileCtx?.current_capability ?? authClaims.current_capability;
3054
+ console.log(`Capability: ${cap ?? "none"}`);
3055
+ if (authClaims.role) {
3056
+ console.log(`Role: ${authClaims.role}`);
3057
+ }
3058
+ if (authClaims.tenant_capabilities && authClaims.tenant_capabilities.length > 1) {
3059
+ console.log(`Capabilities: ${authClaims.tenant_capabilities.join(", ")} (dual-cap)`);
3060
+ }
3061
+ }
3062
+ } else {
3063
+ console.log("Token: invalid (cannot decode)");
3064
+ }
3065
+ console.log(`API URL: ${profile.api_url}`);
3066
+ if (!isClientCreds && !profile.refresh_token) {
3067
+ console.log(`
3068
+ Warning: No refresh token. Token cannot be auto-refreshed.`);
3069
+ }
3070
+ }
3071
+
3072
+ // src/commands/auth.ts
3073
+ function createAuthCommand() {
3074
+ const auth = new Command("auth").description("Authentication management — browser OAuth and client credentials").addHelpText("after", `
3075
+ Examples:
3076
+ $ a8techads auth login # Browser OAuth + PKCE
3077
+ $ a8techads auth login --client-id X --client-secret Y # Client credentials
3078
+ $ a8techads auth status # Show current auth state
3079
+ $ a8techads auth token # Print access token for scripting
3080
+ $ a8techads auth logout # Clear stored tokens`);
3081
+ auth.command("login").description(`Authenticate via browser OAuth or client credentials.
3082
+
3083
+ Browser mode (default): Opens browser for OAuth 2.1 Authorization Code + PKCE.
3084
+ Client credentials mode: Non-interactive auth using --client-id and --client-secret.
3085
+
3086
+ Requires: network access to auth server.`).option("-p, --profile <name>", "Profile name (browser mode only)", "default").option("--api-url <url>", "API base URL (default: https://api.a8.tech)").option("--auth-url <url>", "Auth server URL (default: https://auth.a8.tech)").option("--client-id <id>", "OAuth client ID (enables client_credentials flow)").option("--client-secret <secret>", "OAuth client secret (requires --client-id)").addHelpText("after", `
3087
+ Examples:
3088
+ $ a8techads auth login # Interactive browser login
3089
+ $ a8techads auth login -p staging --api-url https://api.staging.a8.tech
3090
+ $ a8techads auth login --client-id svc-001 --client-secret s3cret
3091
+
3092
+ Note: Client credentials tokens cannot be refreshed. The CLI will prompt
3093
+ for re-authentication when the token expires.`).action(async (opts) => {
3094
+ try {
3095
+ if (opts.clientId && opts.clientSecret) {
3096
+ await loginClientCredentials({
3097
+ clientId: opts.clientId,
3098
+ clientSecret: opts.clientSecret,
3099
+ apiUrl: opts.apiUrl,
3100
+ authUrl: opts.authUrl
3101
+ });
3102
+ } else if (opts.clientId || opts.clientSecret) {
3103
+ console.error("Error: Both --client-id and --client-secret are required for client credentials flow.");
3104
+ console.error('Run "a8techads auth login --help" for usage.');
3105
+ process.exit(1);
3106
+ } else {
3107
+ await login({
3108
+ profile: opts.profile,
3109
+ apiUrl: opts.apiUrl,
3110
+ authUrl: opts.authUrl
3111
+ });
3112
+ }
3113
+ } catch (err) {
3114
+ console.error(`Login failed: ${err.message}`);
3115
+ console.error("If this persists, check your network connection and auth server URL.");
3116
+ process.exit(1);
3117
+ }
3118
+ });
3119
+ auth.command("logout").description(`Clear stored tokens for a profile.
3120
+
3121
+ Requires: an existing profile.`).option("-p, --profile <name>", "Profile name (default: current profile)").addHelpText("after", `
3122
+ Examples:
3123
+ $ a8techads auth logout # Logout current profile
3124
+ $ a8techads auth logout -p staging # Logout specific profile`).action((opts) => {
3125
+ logout(opts.profile);
3126
+ });
3127
+ auth.command("token").description(`Output current access token to stdout.
3128
+
3129
+ Auto-refreshes browser tokens if expired. Client credentials tokens
3130
+ cannot be refreshed — re-authenticate if expired.
3131
+
3132
+ Requires: an active profile with valid credentials.`).option("-p, --profile <name>", "Profile name (default: current profile)").addHelpText("after", `
3133
+ Examples:
3134
+ $ a8techads auth token # Print token for current profile
3135
+ $ a8techads auth token -p staging # Print token for specific profile
3136
+ $ curl -H "Authorization: Bearer $(a8techads auth token)" https://api.a8.tech/api/v1/dsp/me`).action(async (opts) => {
3137
+ try {
3138
+ await outputToken(opts.profile);
3139
+ } catch (err) {
3140
+ console.error(err.message);
3141
+ console.error('Run "a8techads auth login" to authenticate.');
3142
+ process.exit(1);
3143
+ }
3144
+ });
3145
+ auth.command("status").description(`Show current authentication state including profile, token validity,
3146
+ tenant, capability, and role.
3147
+
3148
+ Requires: none (shows "not authenticated" if no profile).`).option("-p, --profile <name>", "Profile name (default: current profile)").addHelpText("after", `
3149
+ Examples:
3150
+ $ a8techads auth status # Status of current profile
3151
+ $ a8techads auth status -p client:svc # Status of specific client profile`).action((opts) => {
3152
+ showAuthStatus(opts.profile);
3153
+ });
3154
+ return auth;
3155
+ }
3156
+
3157
+ // src/commands/profile.ts
3158
+ function createProfileCommand() {
3159
+ const profile = new Command("profile").description("Profile management");
3160
+ profile.command("list").description("List all profiles").action(() => {
3161
+ const creds = loadCredentials();
3162
+ const profiles = Object.keys(creds.profiles);
3163
+ if (profiles.length === 0) {
3164
+ console.log('No profiles. Run "a8techads auth login" to create one.');
3165
+ return;
3166
+ }
3167
+ console.log(`Profiles:
3168
+ `);
3169
+ for (const name of profiles) {
3170
+ const p = creds.profiles[name];
3171
+ const marker = name === creds.current_profile ? " (active)" : "";
3172
+ console.log(` ${name}${marker}`);
3173
+ console.log(` Email: ${p.email}`);
3174
+ console.log(` API: ${p.api_url}`);
3175
+ if (p.tenants.length > 0) {
3176
+ console.log(` Tenants: ${p.tenants.map((t) => `${t.tenant_name} (${t.tenant_id})`).join(", ")}`);
3177
+ }
3178
+ console.log();
3179
+ }
3180
+ });
3181
+ profile.command("use").description("Switch active profile").argument("<name>", "Profile name to switch to").action((name) => {
3182
+ const creds = loadCredentials();
3183
+ if (!creds.profiles[name]) {
3184
+ console.error(`Profile "${name}" not found. Run "a8techads profile list" to see available profiles.`);
3185
+ process.exit(1);
3186
+ }
3187
+ creds.current_profile = name;
3188
+ saveCredentials(creds);
3189
+ const ctx = loadContext();
3190
+ ctx.current_profile = name;
3191
+ saveContext(ctx);
3192
+ const p = creds.profiles[name];
3193
+ console.log(`Switched to profile "${name}" (${p.email})`);
3194
+ });
3195
+ return profile;
3196
+ }
3197
+
3198
+ // src/commands/context.ts
3199
+ function createContextCommand() {
3200
+ const context = new Command("context").description("Workspace context — tenant, capability, and app selection").addHelpText("after", `
3201
+ Examples:
3202
+ $ a8techads context show # Show current context
3203
+ $ a8techads context set-tenant <tenant-id> # Switch tenant
3204
+ $ a8techads context set-capability ADVERTISER # Switch capability
3205
+ $ a8techads context dsp # Shorthand for ADVERTISER
3206
+ $ a8techads context ssp # Shorthand for PUBLISHER
3207
+
3208
+ Requires: an active authenticated profile (run "a8techads auth login" first).`);
3209
+ context.command("show").description(`Show current workspace context including profile, tenant, app,
3210
+ and capability. Warns if token tenant mismatches context tenant.`).addHelpText("after", `
3211
+ Examples:
3212
+ $ a8techads context show`).action(() => {
3213
+ const creds = loadCredentials();
3214
+ const ctx = loadContext();
3215
+ const profileName = creds.current_profile;
3216
+ const profile = getCurrentProfile(creds);
3217
+ const profileCtx = getCurrentContext(ctx);
3218
+ if (!profile) {
3219
+ console.log('No active profile. Run "a8techads auth login" to authenticate.');
3220
+ return;
3221
+ }
3222
+ const claims = decodeJwt(profile.access_token);
3223
+ const authClaims = claims ? getAuthClaims(claims) : null;
3224
+ const tokenTenantId = authClaims?.tenant_id ?? null;
3225
+ const ctxTenantId = profileCtx?.tenant_id ?? null;
3226
+ const ctxTenant = profile.tenants.find((t) => t.tenant_id === ctxTenantId);
3227
+ const tokenTenant = profile.tenants.find((t) => t.tenant_id === tokenTenantId);
3228
+ const match = ctxTenantId === tokenTenantId;
3229
+ console.log(`Profile: ${profileName}`);
3230
+ console.log(`Email: ${profile.email}`);
3231
+ console.log(`Tenant: ${ctxTenant ? `${ctxTenant.tenant_name} (${ctxTenantId})` : ctxTenantId ?? "none"}`);
3232
+ console.log(`Token tenant: ${tokenTenant ? `${tokenTenant.tenant_name} (${tokenTenantId})` : tokenTenantId ?? "none"}`);
3233
+ if (match) {
3234
+ console.log(" (match)");
3235
+ } else {
3236
+ console.log(` MISMATCH — run 'a8techads context set-tenant ${ctxTenantId}' to re-authenticate`);
3237
+ }
3238
+ console.log(`App: ${profileCtx?.app ?? "none"}`);
3239
+ console.log(`Capability: ${profileCtx?.current_capability ?? "null (platform)"}`);
3240
+ });
3241
+ context.command("set-tenant").description(`Switch to a different tenant. Triggers re-authentication if the
3242
+ tenant differs from the current token tenant.
3243
+
3244
+ Requires: authenticated profile with access to the target tenant.`).argument("<tenant-id>", "Tenant ID to switch to").addHelpText("after", `
3245
+ Examples:
3246
+ $ a8techads context set-tenant 550e8400-e29b-41d4-a716-446655440000`).action(async (tenantId) => {
3247
+ const creds = loadCredentials();
3248
+ const profile = getCurrentProfile(creds);
3249
+ if (!profile) {
3250
+ console.error('No active profile. Run "a8techads auth login" first.');
3251
+ process.exit(1);
3252
+ }
3253
+ const tenant = profile.tenants.find((t) => t.tenant_id === tenantId);
3254
+ if (!tenant) {
3255
+ console.error(`Tenant ${tenantId} not found in current profile.`);
3256
+ console.error('Run "a8techads auth login" to refresh tenant list.');
3257
+ console.error(`
3258
+ Available tenants:`);
3259
+ for (const t of profile.tenants) {
3260
+ console.error(` ${t.tenant_name} (${t.tenant_id})`);
3261
+ }
3262
+ process.exit(1);
3263
+ }
3264
+ const claims = decodeJwt(profile.access_token);
3265
+ const authClaims = claims ? getAuthClaims(claims) : null;
3266
+ const tokenTenantId = authClaims?.tenant_id ?? null;
3267
+ if (tenantId === tokenTenantId) {
3268
+ const ctx = loadContext();
3269
+ const app = deriveAppFromTenant(tenant);
3270
+ const capability = appToCapability(app);
3271
+ setCurrentContext(ctx, {
3272
+ tenant_id: tenantId,
3273
+ app,
3274
+ current_capability: capability
3275
+ });
3276
+ saveContext(ctx);
3277
+ console.log("Tenant already active.");
3278
+ console.log(`Tenant: ${tenant.tenant_name} (${tenantId})`);
3279
+ console.log(`App: ${app}`);
3280
+ if (capability)
3281
+ console.log(`Capability: ${capability}`);
3282
+ return;
3283
+ }
3284
+ console.log("Switching tenant requires re-authentication...");
3285
+ try {
3286
+ await login({
3287
+ profile: creds.current_profile,
3288
+ apiUrl: profile.api_url,
3289
+ authUrl: deriveAuthUrl(profile.token_endpoint),
3290
+ loginHint: profile.email,
3291
+ tenantId
3292
+ });
3293
+ console.log(`Switched to tenant: ${tenant.tenant_name}`);
3294
+ } catch (err) {
3295
+ console.error(`Re-authentication failed: ${err.message}`);
3296
+ console.error("Check your network connection and try again.");
3297
+ process.exit(1);
3298
+ }
3299
+ });
3300
+ context.command("set-app").description(`Set active app view (auto-derives capability).
3301
+
3302
+ Requires: authenticated profile.`).argument("<app>", "App to switch to: dsp, ssp, or console").addHelpText("after", `
3303
+ Examples:
3304
+ $ a8techads context set-app dsp # ADVERTISER capability
3305
+ $ a8techads context set-app ssp # PUBLISHER capability
3306
+ $ a8techads context set-app console # Platform (no capability)`).action((app) => {
3307
+ if (!["dsp", "ssp", "console"].includes(app)) {
3308
+ console.error("Invalid app. Must be one of: dsp, ssp, console");
3309
+ console.error('Run "a8techads context set-app --help" for usage.');
3310
+ process.exit(1);
3311
+ }
3312
+ const creds = loadCredentials();
3313
+ const profile = getCurrentProfile(creds);
3314
+ const ctx = loadContext();
3315
+ const profileCtx = getCurrentContext(ctx);
3316
+ const capability = appToCapability(app);
3317
+ if (profile && profileCtx?.tenant_id) {
3318
+ const tenant = profile.tenants.find((t) => t.tenant_id === profileCtx.tenant_id);
3319
+ if (app === "console" && tenant && !tenant.is_platform) {
3320
+ console.warn("Warning: Current tenant is not a platform tenant. Console routes may be inaccessible.");
3321
+ }
3322
+ if (app === "dsp" && tenant && !tenant.capabilities.includes("ADVERTISER")) {
3323
+ console.warn("Warning: Tenant does not have ADVERTISER capability. DSP routes may return 403.");
3324
+ }
3325
+ if (app === "ssp" && tenant && !tenant.capabilities.includes("PUBLISHER")) {
3326
+ console.warn("Warning: Tenant does not have PUBLISHER capability. SSP routes may return 403.");
3327
+ }
3328
+ }
3329
+ setCurrentContext(ctx, {
3330
+ app,
3331
+ current_capability: capability
3332
+ });
3333
+ saveContext(ctx);
3334
+ console.log(`App: ${app}`);
3335
+ console.log(`Capability: ${capability ?? "null (platform)"}`);
3336
+ });
3337
+ context.command("set-capability").description(`Override effective capability directly.
3338
+
3339
+ Sent as X-Effective-Capability header on API requests.
3340
+
3341
+ Requires: tenant must have the requested capability.`).argument("<capability>", "Capability: ADVERTISER or PUBLISHER").addHelpText("after", `
3342
+ Examples:
3343
+ $ a8techads context set-capability ADVERTISER
3344
+ $ a8techads context set-capability PUBLISHER
3345
+
3346
+ Shortcuts:
3347
+ $ a8techads context dsp # Same as set-capability ADVERTISER
3348
+ $ a8techads context ssp # Same as set-capability PUBLISHER`).action((cap) => {
3349
+ const normalized = cap.toUpperCase();
3350
+ if (!["ADVERTISER", "PUBLISHER"].includes(normalized)) {
3351
+ console.error("Invalid capability. Must be ADVERTISER or PUBLISHER.");
3352
+ console.error('Run "a8techads context set-capability --help" for usage.');
3353
+ process.exit(1);
3354
+ }
3355
+ const ctx = loadContext();
3356
+ setCurrentContext(ctx, { current_capability: normalized });
3357
+ saveContext(ctx);
3358
+ console.log(`Capability: ${normalized}`);
3359
+ });
3360
+ context.command("dsp").description("Switch to ADVERTISER capability (shorthand for set-capability ADVERTISER)").action(() => {
3361
+ const ctx = loadContext();
3362
+ setCurrentContext(ctx, { current_capability: "ADVERTISER", app: "dsp" });
3363
+ saveContext(ctx);
3364
+ console.log("Capability: ADVERTISER");
3365
+ console.log("App: dsp");
3366
+ });
3367
+ context.command("ssp").description("Switch to PUBLISHER capability (shorthand for set-capability PUBLISHER)").action(() => {
3368
+ const ctx = loadContext();
3369
+ setCurrentContext(ctx, { current_capability: "PUBLISHER", app: "ssp" });
3370
+ saveContext(ctx);
3371
+ console.log("Capability: PUBLISHER");
3372
+ console.log("App: ssp");
3373
+ });
3374
+ return context;
3375
+ }
3376
+ function deriveAppFromTenant(tenant) {
3377
+ if (tenant.is_platform)
3378
+ return "console";
3379
+ if (tenant.capabilities.includes("ADVERTISER"))
3380
+ return "dsp";
3381
+ if (tenant.capabilities.includes("PUBLISHER"))
3382
+ return "ssp";
3383
+ return "dsp";
3384
+ }
3385
+ function deriveAuthUrl(tokenEndpoint) {
3386
+ const url = new URL(tokenEndpoint);
3387
+ return url.origin;
3388
+ }
3389
+
3390
+ // src/utils/http.ts
3391
+ async function apiRequest(opts) {
3392
+ await refreshTokenIfNeeded();
3393
+ const creds = loadCredentials();
3394
+ const profile = getCurrentProfile(creds);
3395
+ if (!profile) {
3396
+ throw new Error('No active profile. Run "a8techads auth login" to authenticate.');
3397
+ }
3398
+ const ctx = loadContext();
3399
+ const context = getCurrentContext(ctx);
3400
+ validateTenantMatch(profile.access_token, context?.tenant_id ?? null);
3401
+ const headers = {
3402
+ Authorization: `Bearer ${profile.access_token}`,
3403
+ "Content-Type": "application/json",
3404
+ ...opts.headers
3405
+ };
3406
+ if (context?.current_capability) {
3407
+ headers["X-Effective-Capability"] = context.current_capability;
3408
+ }
3409
+ const url = `${profile.api_url}${opts.path}`;
3410
+ return fetch(url, {
3411
+ method: opts.method ?? "GET",
3412
+ headers,
3413
+ body: opts.body ? JSON.stringify(opts.body) : undefined
3414
+ });
3415
+ }
3416
+ function validateTenantMatch(accessToken, contextTenantId) {
3417
+ const claims = decodeJwt(accessToken);
3418
+ if (!claims)
3419
+ return;
3420
+ const authClaims = getAuthClaims(claims);
3421
+ const tokenTenantId = authClaims?.tenant_id ?? null;
3422
+ if (!contextTenantId || !tokenTenantId)
3423
+ return;
3424
+ if (contextTenantId !== tokenTenantId) {
3425
+ throw new Error(`Token tenant mismatch. Context tenant: ${contextTenantId}, token tenant: ${tokenTenantId}.
3426
+ ` + `Run 'a8techads context set-tenant ${contextTenantId}' to re-authenticate.`);
3427
+ }
3428
+ }
3429
+
3430
+ // src/utils/api-prefix.ts
3431
+ function getApiPrefix() {
3432
+ const ctx = loadContext();
3433
+ const context = getCurrentContext(ctx);
3434
+ const app = context?.app ?? "dsp";
3435
+ switch (app) {
3436
+ case "dsp":
3437
+ return "/api/v1/dsp";
3438
+ case "ssp":
3439
+ return "/api/v1/ssp";
3440
+ case "console":
3441
+ return "/api/v1/console";
3442
+ default:
3443
+ return "/api/v1/dsp";
3444
+ }
3445
+ }
3446
+ function dspPrefix() {
3447
+ return "/api/v1/dsp";
3448
+ }
3449
+ function sspPrefix() {
3450
+ return "/api/v1/ssp";
3451
+ }
3452
+
3453
+ // src/utils/output.ts
3454
+ function printData(data, columns, format = "table") {
3455
+ if (format === "json") {
3456
+ console.log(JSON.stringify(data, null, 2));
3457
+ return;
3458
+ }
3459
+ if (format === "csv") {
3460
+ console.log(columns.map((c) => c.header).join(","));
3461
+ for (const row of data) {
3462
+ console.log(columns.map((c) => {
3463
+ const val = formatValue(row, c);
3464
+ return val.includes(",") ? `"${val}"` : val;
3465
+ }).join(","));
3466
+ }
3467
+ return;
3468
+ }
3469
+ if (data.length === 0) {
3470
+ console.log("No results.");
3471
+ return;
3472
+ }
3473
+ const widths = columns.map((c) => {
3474
+ const headerLen = c.header.length;
3475
+ const maxDataLen = data.reduce((max, row) => {
3476
+ const val = formatValue(row, c);
3477
+ return Math.max(max, val.length);
3478
+ }, 0);
3479
+ return c.width ?? Math.min(Math.max(headerLen, maxDataLen), 40);
3480
+ });
3481
+ const header = columns.map((c, i) => c.header.padEnd(widths[i])).join(" ");
3482
+ console.log(header);
3483
+ console.log(columns.map((_, i) => "-".repeat(widths[i])).join(" "));
3484
+ for (const row of data) {
3485
+ const line = columns.map((c, i) => {
3486
+ const val = formatValue(row, c);
3487
+ return val.length > widths[i] ? val.slice(0, widths[i] - 1) + "…" : val.padEnd(widths[i]);
3488
+ }).join(" ");
3489
+ console.log(line);
3490
+ }
3491
+ }
3492
+ function printDetail(data, format = "table") {
3493
+ if (format === "json") {
3494
+ console.log(JSON.stringify(data, null, 2));
3495
+ return;
3496
+ }
3497
+ const maxKeyLen = Math.max(...Object.keys(data).map((k) => k.length));
3498
+ for (const [key, value] of Object.entries(data)) {
3499
+ const displayValue = value === null || value === undefined ? "-" : typeof value === "object" ? JSON.stringify(value) : String(value);
3500
+ console.log(`${key.padEnd(maxKeyLen)} ${displayValue}`);
3501
+ }
3502
+ }
3503
+ function formatValue(row, col) {
3504
+ const val = row[col.key];
3505
+ if (col.format)
3506
+ return col.format(val);
3507
+ if (val === null || val === undefined)
3508
+ return "-";
3509
+ if (typeof val === "number")
3510
+ return val.toLocaleString();
3511
+ return String(val);
3512
+ }
3513
+ function addFormatOption(cmd) {
3514
+ return cmd.option("-f, --format <format>", "Output format: table, json, csv (default: table)", "table");
3515
+ }
3516
+
3517
+ // src/commands/campaigns.ts
3518
+ var COLUMNS = [
3519
+ { key: "id", header: "ID", width: 36 },
3520
+ { key: "name", header: "NAME", width: 30 },
3521
+ { key: "status", header: "STATUS", width: 12 },
3522
+ { key: "budget", header: "BUDGET", width: 10, format: (v) => v != null ? `$${Number(v).toFixed(2)}` : "-" },
3523
+ { key: "spent", header: "SPENT", width: 10, format: (v) => v != null ? `$${Number(v).toFixed(2)}` : "-" }
3524
+ ];
3525
+ function createCampaignsCommand() {
3526
+ const cmd = new Command("campaigns").description(`Campaign management (DSP)
3527
+
3528
+ Requires: ADVERTISER capability.`).addHelpText("after", `
3529
+ Examples:
3530
+ $ a8techads campaigns list
3531
+ $ a8techads campaigns get <id>
3532
+ $ a8techads campaigns create --name "Summer Sale" --budget 500
3533
+ $ a8techads campaigns pause <id>`);
3534
+ addFormatOption(cmd.command("list").description("List campaigns with optional filters.").option("--status <status>", "Filter by status (draft, active, paused, etc.)").option("--limit <n>", "Max results", "20")).action(async (opts) => {
3535
+ const params = new URLSearchParams;
3536
+ if (opts.status)
3537
+ params.set("status", opts.status);
3538
+ params.set("limit", opts.limit);
3539
+ const resp = await apiRequest({ path: `${dspPrefix()}/campaigns?${params}` });
3540
+ const json = await resp.json();
3541
+ if (!resp.ok) {
3542
+ console.error(`Error: ${json.error ?? resp.statusText}`);
3543
+ process.exit(1);
3544
+ }
3545
+ const rows = (json.data ?? json).map((c) => ({
3546
+ id: c.id,
3547
+ name: c.name,
3548
+ status: c.status,
3549
+ budget: c.budget ?? c.dailyBudget,
3550
+ spent: c.stats?.spend ?? c.spent
3551
+ }));
3552
+ printData(rows, COLUMNS, opts.format);
3553
+ });
3554
+ addFormatOption(cmd.command("get").description("Get campaign details by ID.").argument("<id>", "Campaign ID")).action(async (id, opts) => {
3555
+ const resp = await apiRequest({ path: `${dspPrefix()}/campaigns/${id}` });
3556
+ const json = await resp.json();
3557
+ if (!resp.ok) {
3558
+ console.error(`Error: ${json.error ?? resp.statusText}`);
3559
+ process.exit(1);
3560
+ }
3561
+ printDetail(json.data ?? json, opts.format);
3562
+ });
3563
+ addFormatOption(cmd.command("stats").description("Get campaign performance statistics.").argument("<id>", "Campaign ID").option("--from <date>", "Start date (YYYY-MM-DD)").option("--to <date>", "End date (YYYY-MM-DD)")).action(async (id, opts) => {
3564
+ const params = new URLSearchParams;
3565
+ if (opts.from)
3566
+ params.set("from", opts.from);
3567
+ if (opts.to)
3568
+ params.set("to", opts.to);
3569
+ const resp = await apiRequest({ path: `${dspPrefix()}/campaigns/${id}/stats?${params}` });
3570
+ const json = await resp.json();
3571
+ if (!resp.ok) {
3572
+ console.error(`Error: ${json.error ?? resp.statusText}`);
3573
+ process.exit(1);
3574
+ }
3575
+ printDetail(json.data ?? json, opts.format);
3576
+ });
3577
+ cmd.command("create").description(`Create a new campaign.
3578
+
3579
+ Requires: ADVERTISER capability, advertiser_admin or advertiser_member role.`).option("--name <name>", "Campaign name (required)").option("--budget <amount>", "Daily budget in dollars").option("--from-json <file>", "Create from JSON file").addHelpText("after", `
3580
+ Examples:
3581
+ $ a8techads campaigns create --name "Summer Sale" --budget 500
3582
+ $ a8techads campaigns create --from-json campaign.json`).action(async (opts) => {
3583
+ let body;
3584
+ if (opts.fromJson) {
3585
+ const { readFileSync: readFileSync3 } = await import("fs");
3586
+ body = JSON.parse(readFileSync3(opts.fromJson, "utf-8"));
3587
+ } else {
3588
+ if (!opts.name) {
3589
+ console.error('Error: --name is required. Run "a8techads campaigns create --help".');
3590
+ process.exit(1);
3591
+ }
3592
+ body = { name: opts.name };
3593
+ if (opts.budget)
3594
+ body.dailyBudget = Number(opts.budget);
3595
+ }
3596
+ const resp = await apiRequest({ method: "POST", path: `${dspPrefix()}/campaigns`, body });
3597
+ const json = await resp.json();
3598
+ if (!resp.ok) {
3599
+ console.error(`Error: ${json.error ?? json.errors ?? resp.statusText}`);
3600
+ process.exit(1);
3601
+ }
3602
+ console.log(`Campaign created: ${json.data?.id ?? json.id}`);
3603
+ });
3604
+ cmd.command("update").description("Update an existing campaign.").argument("<id>", "Campaign ID").option("--name <name>", "New name").option("--budget <amount>", "New daily budget").option("--from-json <file>", "Update from JSON file").action(async (id, opts) => {
3605
+ let body;
3606
+ if (opts.fromJson) {
3607
+ const { readFileSync: readFileSync3 } = await import("fs");
3608
+ body = JSON.parse(readFileSync3(opts.fromJson, "utf-8"));
3609
+ } else {
3610
+ body = {};
3611
+ if (opts.name)
3612
+ body.name = opts.name;
3613
+ if (opts.budget)
3614
+ body.dailyBudget = Number(opts.budget);
3615
+ }
3616
+ const resp = await apiRequest({ method: "PATCH", path: `${dspPrefix()}/campaigns/${id}`, body });
3617
+ if (!resp.ok) {
3618
+ const j = await resp.json();
3619
+ console.error(`Error: ${j.error ?? resp.statusText}`);
3620
+ process.exit(1);
3621
+ }
3622
+ console.log(`Campaign ${id} updated.`);
3623
+ });
3624
+ cmd.command("delete").description("Delete a campaign.").argument("<id>", "Campaign ID").option("--yes", "Skip confirmation").action(async (id, opts) => {
3625
+ if (!opts.yes) {
3626
+ console.error("Add --yes to confirm deletion.");
3627
+ process.exit(1);
3628
+ }
3629
+ const resp = await apiRequest({ method: "DELETE", path: `${dspPrefix()}/campaigns/${id}` });
3630
+ if (!resp.ok && resp.status !== 204) {
3631
+ console.error(`Error: ${resp.statusText}`);
3632
+ process.exit(1);
3633
+ }
3634
+ console.log(`Campaign ${id} deleted.`);
3635
+ });
3636
+ for (const action of ["pause", "resume", "submit", "withdraw"]) {
3637
+ const endpoint = action === "submit" ? `submit` : action === "withdraw" ? `withdraw` : `status`;
3638
+ const method = action === "pause" || action === "resume" ? "PATCH" : "POST";
3639
+ cmd.command(action).description(`${action.charAt(0).toUpperCase() + action.slice(1)} a campaign.`).argument("<id>", "Campaign ID").action(async (id) => {
3640
+ const path = action === "pause" || action === "resume" ? `${dspPrefix()}/campaigns/${id}/status` : `${dspPrefix()}/campaigns/${id}/${action}`;
3641
+ const body = action === "pause" ? { status: "paused" } : action === "resume" ? { status: "active" } : undefined;
3642
+ const resp = await apiRequest({ method, path, body });
3643
+ if (!resp.ok) {
3644
+ const j = await resp.json();
3645
+ console.error(`Error: ${j.error ?? resp.statusText}`);
3646
+ process.exit(1);
3647
+ }
3648
+ console.log(`Campaign ${id} ${action}d.`);
3649
+ });
3650
+ }
3651
+ cmd.command("duplicate").description("Duplicate a campaign.").argument("<id>", "Campaign ID").action(async (id) => {
3652
+ const resp = await apiRequest({ method: "POST", path: `${dspPrefix()}/campaigns/${id}/duplicate` });
3653
+ const json = await resp.json();
3654
+ if (!resp.ok) {
3655
+ console.error(`Error: ${json.error ?? resp.statusText}`);
3656
+ process.exit(1);
3657
+ }
3658
+ console.log(`Campaign duplicated. New ID: ${json.data?.id ?? json.id}`);
3659
+ });
3660
+ return cmd;
3661
+ }
3662
+
3663
+ // src/commands/variations.ts
3664
+ var COLUMNS2 = [
3665
+ { key: "id", header: "ID", width: 36 },
3666
+ { key: "name", header: "NAME", width: 25 },
3667
+ { key: "type", header: "TYPE", width: 10 },
3668
+ { key: "status", header: "STATUS", width: 10 },
3669
+ { key: "campaignId", header: "CAMPAIGN", width: 36 }
3670
+ ];
3671
+ function createVariationsCommand() {
3672
+ const cmd = new Command("variations").description(`Ad variation management (DSP)
3673
+
3674
+ Requires: ADVERTISER capability.`).addHelpText("after", `
3675
+ Examples:
3676
+ $ a8techads variations list --campaign <campaign-id>
3677
+ $ a8techads variations create --campaign <id> --name "Banner v1" --from-json variation.json`);
3678
+ addFormatOption(cmd.command("list").description("List ad variations.").option("--campaign <id>", "Filter by campaign ID").option("--limit <n>", "Max results", "50")).action(async (opts) => {
3679
+ const params = new URLSearchParams({ limit: opts.limit });
3680
+ if (opts.campaign)
3681
+ params.set("campaign_id", opts.campaign);
3682
+ const resp = await apiRequest({ path: `${dspPrefix()}/variations?${params}` });
3683
+ const json = await resp.json();
3684
+ if (!resp.ok) {
3685
+ console.error(`Error: ${json.error ?? resp.statusText}`);
3686
+ process.exit(1);
3687
+ }
3688
+ const rows = (json.data ?? json).map((v) => ({
3689
+ id: v.id,
3690
+ name: v.name,
3691
+ type: v.type,
3692
+ status: v.status,
3693
+ campaignId: v.campaignId ?? v.campaign_id
3694
+ }));
3695
+ printData(rows, COLUMNS2, opts.format);
3696
+ });
3697
+ addFormatOption(cmd.command("get").description("Get variation details.").argument("<id>", "Variation ID")).action(async (id, opts) => {
3698
+ const resp = await apiRequest({ path: `${dspPrefix()}/variations/${id}` });
3699
+ const json = await resp.json();
3700
+ if (!resp.ok) {
3701
+ console.error(`Error: ${json.error ?? resp.statusText}`);
3702
+ process.exit(1);
3703
+ }
3704
+ printDetail(json.data ?? json, opts.format);
3705
+ });
3706
+ cmd.command("create").description("Create a new ad variation.").option("--campaign <id>", "Campaign ID (required)").option("--name <name>", "Variation name (required)").option("--from-json <file>", "Create from JSON file").action(async (opts) => {
3707
+ let body;
3708
+ if (opts.fromJson) {
3709
+ const { readFileSync: readFileSync3 } = await import("fs");
3710
+ body = JSON.parse(readFileSync3(opts.fromJson, "utf-8"));
3711
+ } else {
3712
+ if (!opts.campaign || !opts.name) {
3713
+ console.error("Error: --campaign and --name are required.");
3714
+ process.exit(1);
3715
+ }
3716
+ body = { campaignId: opts.campaign, name: opts.name };
3717
+ }
3718
+ const resp = await apiRequest({ method: "POST", path: `${dspPrefix()}/variations`, body });
3719
+ const json = await resp.json();
3720
+ if (!resp.ok) {
3721
+ console.error(`Error: ${json.error ?? resp.statusText}`);
3722
+ process.exit(1);
3723
+ }
3724
+ console.log(`Variation created: ${json.data?.id ?? json.id}`);
3725
+ });
3726
+ cmd.command("update").description("Update a variation.").argument("<id>", "Variation ID").option("--name <name>", "New name").option("--from-json <file>", "Update from JSON file").action(async (id, opts) => {
3727
+ let body;
3728
+ if (opts.fromJson) {
3729
+ const { readFileSync: readFileSync3 } = await import("fs");
3730
+ body = JSON.parse(readFileSync3(opts.fromJson, "utf-8"));
3731
+ } else {
3732
+ body = {};
3733
+ if (opts.name)
3734
+ body.name = opts.name;
3735
+ }
3736
+ const resp = await apiRequest({ method: "PATCH", path: `${dspPrefix()}/variations/${id}`, body });
3737
+ if (!resp.ok) {
3738
+ const j = await resp.json();
3739
+ console.error(`Error: ${j.error ?? resp.statusText}`);
3740
+ process.exit(1);
3741
+ }
3742
+ console.log(`Variation ${id} updated.`);
3743
+ });
3744
+ cmd.command("delete").description("Delete a variation.").argument("<id>", "Variation ID").option("--yes", "Skip confirmation").action(async (id, opts) => {
3745
+ if (!opts.yes) {
3746
+ console.error("Add --yes to confirm deletion.");
3747
+ process.exit(1);
3748
+ }
3749
+ const resp = await apiRequest({ method: "DELETE", path: `${dspPrefix()}/variations/${id}` });
3750
+ if (!resp.ok && resp.status !== 204) {
3751
+ console.error(`Error: ${resp.statusText}`);
3752
+ process.exit(1);
3753
+ }
3754
+ console.log(`Variation ${id} deleted.`);
3755
+ });
3756
+ return cmd;
3757
+ }
3758
+
3759
+ // src/commands/sites.ts
3760
+ var COLUMNS3 = [
3761
+ { key: "id", header: "ID", width: 36 },
3762
+ { key: "name", header: "NAME", width: 25 },
3763
+ { key: "domain", header: "DOMAIN", width: 25 },
3764
+ { key: "status", header: "STATUS", width: 12 },
3765
+ { key: "zoneCount", header: "ZONES", width: 6 }
3766
+ ];
3767
+ function createSitesCommand() {
3768
+ const cmd = new Command("sites").description(`Site management (SSP)
3769
+
3770
+ Requires: PUBLISHER capability.`).addHelpText("after", `
3771
+ Examples:
3772
+ $ a8techads sites list
3773
+ $ a8techads sites get <id>
3774
+ $ a8techads sites create --name "My Blog" --domain blog.example.com`);
3775
+ addFormatOption(cmd.command("list").description("List publisher sites.").option("--status <status>", "Filter by status").option("--limit <n>", "Max results", "20")).action(async (opts) => {
3776
+ const params = new URLSearchParams;
3777
+ if (opts.status)
3778
+ params.set("status", opts.status);
3779
+ params.set("limit", opts.limit);
3780
+ const resp = await apiRequest({ path: `${sspPrefix()}/sites?${params}` });
3781
+ const json = await resp.json();
3782
+ if (!resp.ok) {
3783
+ console.error(`Error: ${json.error ?? resp.statusText}`);
3784
+ process.exit(1);
3785
+ }
3786
+ const rows = (json.data ?? json).map((s) => ({
3787
+ id: s.id,
3788
+ name: s.name,
3789
+ domain: s.domain,
3790
+ status: s.status,
3791
+ zoneCount: s.zoneCount ?? s.zone_count ?? "-"
3792
+ }));
3793
+ printData(rows, COLUMNS3, opts.format);
3794
+ });
3795
+ addFormatOption(cmd.command("get").description("Get site details by ID.").argument("<id>", "Site ID")).action(async (id, opts) => {
3796
+ const resp = await apiRequest({ path: `${sspPrefix()}/sites/${id}` });
3797
+ const json = await resp.json();
3798
+ if (!resp.ok) {
3799
+ console.error(`Error: ${json.error ?? resp.statusText}`);
3800
+ process.exit(1);
3801
+ }
3802
+ printDetail(json.data ?? json, opts.format);
3803
+ });
3804
+ cmd.command("create").description("Create a new site.").option("--name <name>", "Site name (required)").option("--domain <domain>", "Site domain (required)").option("--type <type>", "Site type: WEB, APP, CTV", "WEB").option("--from-json <file>", "Create from JSON file").action(async (opts) => {
3805
+ let body;
3806
+ if (opts.fromJson) {
3807
+ const { readFileSync: readFileSync3 } = await import("fs");
3808
+ body = JSON.parse(readFileSync3(opts.fromJson, "utf-8"));
3809
+ } else {
3810
+ if (!opts.name || !opts.domain) {
3811
+ console.error("Error: --name and --domain are required.");
3812
+ process.exit(1);
3813
+ }
3814
+ body = { name: opts.name, domain: opts.domain, type: opts.type };
3815
+ }
3816
+ const resp = await apiRequest({ method: "POST", path: `${sspPrefix()}/sites`, body });
3817
+ const json = await resp.json();
3818
+ if (!resp.ok) {
3819
+ console.error(`Error: ${json.error ?? resp.statusText}`);
3820
+ process.exit(1);
3821
+ }
3822
+ console.log(`Site created: ${json.data?.id ?? json.id}`);
3823
+ });
3824
+ cmd.command("update").description("Update a site.").argument("<id>", "Site ID").option("--name <name>", "New name").option("--from-json <file>", "Update from JSON file").action(async (id, opts) => {
3825
+ let body;
3826
+ if (opts.fromJson) {
3827
+ const { readFileSync: readFileSync3 } = await import("fs");
3828
+ body = JSON.parse(readFileSync3(opts.fromJson, "utf-8"));
3829
+ } else {
3830
+ body = {};
3831
+ if (opts.name)
3832
+ body.name = opts.name;
3833
+ }
3834
+ const resp = await apiRequest({ method: "PATCH", path: `${sspPrefix()}/sites/${id}`, body });
3835
+ if (!resp.ok) {
3836
+ const j = await resp.json();
3837
+ console.error(`Error: ${j.error ?? resp.statusText}`);
3838
+ process.exit(1);
3839
+ }
3840
+ console.log(`Site ${id} updated.`);
3841
+ });
3842
+ cmd.command("delete").description("Delete a site.").argument("<id>", "Site ID").option("--yes", "Skip confirmation").action(async (id, opts) => {
3843
+ if (!opts.yes) {
3844
+ console.error("Add --yes to confirm deletion.");
3845
+ process.exit(1);
3846
+ }
3847
+ const resp = await apiRequest({ method: "DELETE", path: `${sspPrefix()}/sites/${id}` });
3848
+ if (!resp.ok && resp.status !== 204) {
3849
+ console.error(`Error: ${resp.statusText}`);
3850
+ process.exit(1);
3851
+ }
3852
+ console.log(`Site ${id} deleted.`);
3853
+ });
3854
+ for (const action of ["submit", "pause", "resume", "archive"]) {
3855
+ cmd.command(action).description(`${action.charAt(0).toUpperCase() + action.slice(1)} a site.`).argument("<id>", "Site ID").action(async (id) => {
3856
+ const method = action === "submit" ? "POST" : "PATCH";
3857
+ const resp = await apiRequest({ method, path: `${sspPrefix()}/sites/${id}/${action}` });
3858
+ if (!resp.ok) {
3859
+ const j = await resp.json();
3860
+ console.error(`Error: ${j.error ?? resp.statusText}`);
3861
+ process.exit(1);
3862
+ }
3863
+ console.log(`Site ${id} ${action}d.`);
3864
+ });
3865
+ }
3866
+ cmd.command("verify").description("Verify site ownership via DNS, meta tag, or ads.txt.").argument("<id>", "Site ID").option("--method <method>", "Verification method: dns, meta, ads_txt", "dns").action(async (id, opts) => {
3867
+ const resp = await apiRequest({ method: "POST", path: `${sspPrefix()}/sites/${id}/verify`, body: { method: opts.method } });
3868
+ const json = await resp.json();
3869
+ if (!resp.ok) {
3870
+ console.error(`Error: ${json.error ?? resp.statusText}`);
3871
+ process.exit(1);
3872
+ }
3873
+ console.log(`Verification result: ${json.data?.status ?? json.status ?? JSON.stringify(json)}`);
3874
+ });
3875
+ return cmd;
3876
+ }
3877
+
3878
+ // src/commands/zones.ts
3879
+ var COLUMNS4 = [
3880
+ { key: "id", header: "ID", width: 36 },
3881
+ { key: "name", header: "NAME", width: 25 },
3882
+ { key: "format", header: "FORMAT", width: 18 },
3883
+ { key: "status", header: "STATUS", width: 10 }
3884
+ ];
3885
+ function createZonesCommand() {
3886
+ const cmd = new Command("zones").description(`Ad zone management (SSP)
3887
+
3888
+ Requires: PUBLISHER capability.`).addHelpText("after", `
3889
+ Examples:
3890
+ $ a8techads zones list --site <site-id>
3891
+ $ a8techads zones get <id>
3892
+ $ a8techads zones create --site <site-id> --name "Header Banner" --format banner_300x250
3893
+ $ a8techads zones tag <id>`);
3894
+ addFormatOption(cmd.command("list").description("List ad zones.").option("--site <id>", "Filter by site ID").option("--limit <n>", "Max results", "50")).action(async (opts) => {
3895
+ const params = new URLSearchParams;
3896
+ if (opts.site)
3897
+ params.set("site_id", opts.site);
3898
+ params.set("limit", opts.limit);
3899
+ const resp = await apiRequest({ path: `${sspPrefix()}/zones?${params}` });
3900
+ const json = await resp.json();
3901
+ if (!resp.ok) {
3902
+ console.error(`Error: ${json.error ?? resp.statusText}`);
3903
+ process.exit(1);
3904
+ }
3905
+ const rows = (json.data ?? json).map((z) => ({
3906
+ id: z.id,
3907
+ name: z.name,
3908
+ format: z.adFormat ?? z.ad_format ?? "-",
3909
+ status: z.status
3910
+ }));
3911
+ printData(rows, COLUMNS4, opts.format);
3912
+ });
3913
+ addFormatOption(cmd.command("get").description("Get zone details by ID.").argument("<id>", "Zone ID")).action(async (id, opts) => {
3914
+ const resp = await apiRequest({ path: `${sspPrefix()}/zones/${id}` });
3915
+ const json = await resp.json();
3916
+ if (!resp.ok) {
3917
+ console.error(`Error: ${json.error ?? resp.statusText}`);
3918
+ process.exit(1);
3919
+ }
3920
+ printDetail(json.data ?? json, opts.format);
3921
+ });
3922
+ cmd.command("create").description("Create a new ad zone.").option("--site <id>", "Site ID (required)").option("--name <name>", "Zone name (required)").option("--format <format>", "Ad format (e.g., banner_300x250)").option("--from-json <file>", "Create from JSON file").action(async (opts) => {
3923
+ let body;
3924
+ if (opts.fromJson) {
3925
+ const { readFileSync: readFileSync3 } = await import("fs");
3926
+ body = JSON.parse(readFileSync3(opts.fromJson, "utf-8"));
3927
+ } else {
3928
+ if (!opts.site || !opts.name) {
3929
+ console.error("Error: --site and --name are required.");
3930
+ process.exit(1);
3931
+ }
3932
+ body = { siteId: opts.site, name: opts.name };
3933
+ if (opts.format)
3934
+ body.adFormat = opts.format;
3935
+ }
3936
+ const resp = await apiRequest({ method: "POST", path: `${sspPrefix()}/zones`, body });
3937
+ const json = await resp.json();
3938
+ if (!resp.ok) {
3939
+ console.error(`Error: ${json.error ?? resp.statusText}`);
3940
+ process.exit(1);
3941
+ }
3942
+ console.log(`Zone created: ${json.data?.id ?? json.id}`);
3943
+ });
3944
+ cmd.command("update").description("Update a zone.").argument("<id>", "Zone ID").option("--name <name>", "New name").option("--from-json <file>", "Update from JSON file").action(async (id, opts) => {
3945
+ let body;
3946
+ if (opts.fromJson) {
3947
+ const { readFileSync: readFileSync3 } = await import("fs");
3948
+ body = JSON.parse(readFileSync3(opts.fromJson, "utf-8"));
3949
+ } else {
3950
+ body = {};
3951
+ if (opts.name)
3952
+ body.name = opts.name;
3953
+ }
3954
+ const resp = await apiRequest({ method: "PATCH", path: `${sspPrefix()}/zones/${id}`, body });
3955
+ if (!resp.ok) {
3956
+ const j = await resp.json();
3957
+ console.error(`Error: ${j.error ?? resp.statusText}`);
3958
+ process.exit(1);
3959
+ }
3960
+ console.log(`Zone ${id} updated.`);
3961
+ });
3962
+ cmd.command("delete").description("Delete a zone.").argument("<id>", "Zone ID").option("--yes", "Skip confirmation").action(async (id, opts) => {
3963
+ if (!opts.yes) {
3964
+ console.error("Add --yes to confirm deletion.");
3965
+ process.exit(1);
3966
+ }
3967
+ const resp = await apiRequest({ method: "DELETE", path: `${sspPrefix()}/zones/${id}` });
3968
+ if (!resp.ok && resp.status !== 204) {
3969
+ console.error(`Error: ${resp.statusText}`);
3970
+ process.exit(1);
3971
+ }
3972
+ console.log(`Zone ${id} deleted.`);
3973
+ });
3974
+ cmd.command("tag").description("Get the ad tag code for a zone (JS or VAST).").argument("<id>", "Zone ID").action(async (id) => {
3975
+ const resp = await apiRequest({ path: `${sspPrefix()}/zones/${id}/tag` });
3976
+ const json = await resp.json();
3977
+ if (!resp.ok) {
3978
+ console.error(`Error: ${json.error ?? resp.statusText}`);
3979
+ process.exit(1);
3980
+ }
3981
+ console.log(json.data?.code ?? json.code ?? JSON.stringify(json, null, 2));
3982
+ });
3983
+ return cmd;
3984
+ }
3985
+
3986
+ // src/commands/reports.ts
3987
+ function createReportsCommand() {
3988
+ const cmd = new Command("reports").description(`Analytics and reporting
3989
+
3990
+ Routes based on current capability (DSP or SSP).`).addHelpText("after", `
3991
+ Examples:
3992
+ $ a8techads reports query --metrics spend,impressions --dimensions date
3993
+ $ a8techads reports list
3994
+ $ a8techads reports templates`);
3995
+ addFormatOption(cmd.command("query").description(`Execute an ad-hoc analytics query.
3996
+
3997
+ Requires: authenticated profile with DSP or SSP capability.`).option("--metrics <list>", "Comma-separated metrics (e.g., spend,impressions,clicks,ctr)", "impressions,clicks,spend").option("--dimensions <list>", "Comma-separated dimensions (e.g., date,campaign,geo)", "date").option("--from <date>", "Start date (YYYY-MM-DD)").option("--to <date>", "End date (YYYY-MM-DD)").option("--limit <n>", "Max rows", "100").addHelpText("after", `
3998
+ Examples:
3999
+ $ a8techads reports query --metrics spend,impressions --dimensions date --from 2026-03-01 --to 2026-03-20
4000
+ $ a8techads reports query --metrics revenue --dimensions publisher --format json
4001
+ $ a8techads reports query --dimensions geo --limit 10`)).action(async (opts) => {
4002
+ const body = {
4003
+ metrics: opts.metrics.split(","),
4004
+ dimensions: opts.dimensions.split(","),
4005
+ limit: Number(opts.limit)
4006
+ };
4007
+ if (opts.from)
4008
+ body.dateRange = { ...body.dateRange ?? {}, start: opts.from };
4009
+ if (opts.to)
4010
+ body.dateRange = { ...body.dateRange ?? {}, end: opts.to };
4011
+ const resp = await apiRequest({ method: "POST", path: `${getApiPrefix()}/reports/query`, body });
4012
+ const json = await resp.json();
4013
+ if (!resp.ok) {
4014
+ console.error(`Error: ${json.error ?? resp.statusText}`);
4015
+ process.exit(1);
4016
+ }
4017
+ const rows = json.data?.rows ?? json.rows ?? [];
4018
+ if (rows.length === 0) {
4019
+ console.log("No results.");
4020
+ return;
4021
+ }
4022
+ if (opts.format === "json") {
4023
+ console.log(JSON.stringify(json.data ?? json, null, 2));
4024
+ return;
4025
+ }
4026
+ const keys = Object.keys(rows[0]);
4027
+ const columns = keys.map((k) => ({ key: k, header: k.toUpperCase(), width: Math.max(k.length, 12) }));
4028
+ printData(rows, columns, opts.format);
4029
+ const summary = json.data?.summary ?? json.summary;
4030
+ if (summary && opts.format === "table") {
4031
+ console.log(`
4032
+ Summary:`);
4033
+ for (const [k, v] of Object.entries(summary)) {
4034
+ console.log(` ${k}: ${v}`);
4035
+ }
4036
+ }
4037
+ });
4038
+ addFormatOption(cmd.command("list").description("List saved reports.")).action(async (opts) => {
4039
+ const resp = await apiRequest({ path: `${getApiPrefix()}/reports/saved` });
4040
+ const json = await resp.json();
4041
+ if (!resp.ok) {
4042
+ console.error(`Error: ${json.error ?? resp.statusText}`);
4043
+ process.exit(1);
4044
+ }
4045
+ const rows = (json.data ?? json).map((r) => ({ id: r.id, name: r.name, createdAt: r.createdAt ?? r.created_at }));
4046
+ printData(rows, [
4047
+ { key: "id", header: "ID", width: 36 },
4048
+ { key: "name", header: "NAME", width: 30 },
4049
+ { key: "createdAt", header: "CREATED", width: 20 }
4050
+ ], opts.format);
4051
+ });
4052
+ addFormatOption(cmd.command("get").description("Get a saved report by ID.").argument("<id>", "Report ID")).action(async (id, opts) => {
4053
+ const resp = await apiRequest({ path: `${getApiPrefix()}/reports/${id}` });
4054
+ const json = await resp.json();
4055
+ if (!resp.ok) {
4056
+ console.error(`Error: ${json.error ?? resp.statusText}`);
4057
+ process.exit(1);
4058
+ }
4059
+ printDetail(json.data ?? json, opts.format);
4060
+ });
4061
+ addFormatOption(cmd.command("templates").description("List available report templates.")).action(async (opts) => {
4062
+ const resp = await apiRequest({ path: `${getApiPrefix()}/reports/templates` });
4063
+ const json = await resp.json();
4064
+ if (!resp.ok) {
4065
+ console.error(`Error: ${json.error ?? resp.statusText}`);
4066
+ process.exit(1);
4067
+ }
4068
+ const rows = (json.data ?? json).map((t) => ({ id: t.id, name: t.name, description: t.description }));
4069
+ printData(rows, [
4070
+ { key: "id", header: "ID", width: 20 },
4071
+ { key: "name", header: "NAME", width: 25 },
4072
+ { key: "description", header: "DESCRIPTION", width: 40 }
4073
+ ], opts.format);
4074
+ });
4075
+ cmd.command("create").description("Create a saved report.").option("--name <name>", "Report name (required)").option("--from-json <file>", "Create from JSON file").action(async (opts) => {
4076
+ let body;
4077
+ if (opts.fromJson) {
4078
+ const { readFileSync: readFileSync3 } = await import("fs");
4079
+ body = JSON.parse(readFileSync3(opts.fromJson, "utf-8"));
4080
+ } else {
4081
+ if (!opts.name) {
4082
+ console.error("Error: --name is required.");
4083
+ process.exit(1);
4084
+ }
4085
+ body = { name: opts.name };
4086
+ }
4087
+ const resp = await apiRequest({ method: "POST", path: `${getApiPrefix()}/reports`, body });
4088
+ const json = await resp.json();
4089
+ if (!resp.ok) {
4090
+ console.error(`Error: ${json.error ?? resp.statusText}`);
4091
+ process.exit(1);
4092
+ }
4093
+ console.log(`Report created: ${json.data?.id ?? json.id}`);
4094
+ });
4095
+ cmd.command("delete").description("Delete a saved report.").argument("<id>", "Report ID").option("--yes", "Skip confirmation").action(async (id, opts) => {
4096
+ if (!opts.yes) {
4097
+ console.error("Add --yes to confirm deletion.");
4098
+ process.exit(1);
4099
+ }
4100
+ const resp = await apiRequest({ method: "DELETE", path: `${getApiPrefix()}/reports/${id}` });
4101
+ if (!resp.ok && resp.status !== 204) {
4102
+ console.error(`Error: ${resp.statusText}`);
4103
+ process.exit(1);
4104
+ }
4105
+ console.log(`Report ${id} deleted.`);
4106
+ });
4107
+ return cmd;
4108
+ }
4109
+
4110
+ // src/commands/billing.ts
4111
+ function createBillingCommand() {
4112
+ const cmd = new Command("billing").description(`Billing and payments (DSP)
4113
+
4114
+ Requires: ADVERTISER capability.`).addHelpText("after", `
4115
+ Examples:
4116
+ $ a8techads billing balance
4117
+ $ a8techads billing transactions --limit 10
4118
+ $ a8techads billing deposit --amount 100
4119
+ $ a8techads billing settings`);
4120
+ addFormatOption(cmd.command("balance").description("Show current account balance.")).action(async (opts) => {
4121
+ const resp = await apiRequest({ path: `${dspPrefix()}/payments/balance` });
4122
+ const json = await resp.json();
4123
+ if (!resp.ok) {
4124
+ console.error(`Error: ${json.error ?? resp.statusText}`);
4125
+ process.exit(1);
4126
+ }
4127
+ printDetail(json.data ?? json, opts.format);
4128
+ });
4129
+ addFormatOption(cmd.command("transactions").description("List transaction history.").option("--limit <n>", "Max results", "20")).action(async (opts) => {
4130
+ const params = new URLSearchParams({ limit: opts.limit });
4131
+ const resp = await apiRequest({ path: `${dspPrefix()}/payments/transactions?${params}` });
4132
+ const json = await resp.json();
4133
+ if (!resp.ok) {
4134
+ console.error(`Error: ${json.error ?? resp.statusText}`);
4135
+ process.exit(1);
4136
+ }
4137
+ const columns = [
4138
+ { key: "id", header: "ID", width: 36 },
4139
+ { key: "type", header: "TYPE", width: 12 },
4140
+ { key: "amount", header: "AMOUNT", width: 12, format: (v) => v != null ? `$${Number(v).toFixed(2)}` : "-" },
4141
+ { key: "createdAt", header: "DATE", width: 20 }
4142
+ ];
4143
+ const rows = (json.data ?? json).map((t) => ({
4144
+ id: t.id,
4145
+ type: t.type ?? t.transactionType,
4146
+ amount: t.amount,
4147
+ createdAt: t.createdAt ?? t.created_at
4148
+ }));
4149
+ printData(rows, columns, opts.format);
4150
+ });
4151
+ addFormatOption(cmd.command("settings").description("Show payment/billing settings.")).action(async (opts) => {
4152
+ const resp = await apiRequest({ path: `${dspPrefix()}/payments/settings` });
4153
+ const json = await resp.json();
4154
+ if (!resp.ok) {
4155
+ console.error(`Error: ${json.error ?? resp.statusText}`);
4156
+ process.exit(1);
4157
+ }
4158
+ printDetail(json.data ?? json, opts.format);
4159
+ });
4160
+ cmd.command("deposit").description("Initiate a deposit ($10-$100,000).").option("--amount <dollars>", "Deposit amount in dollars (required)").action(async (opts) => {
4161
+ if (!opts.amount) {
4162
+ console.error("Error: --amount is required.");
4163
+ process.exit(1);
4164
+ }
4165
+ const amount = Number(opts.amount);
4166
+ if (isNaN(amount) || amount < 10 || amount > 1e5) {
4167
+ console.error("Error: Amount must be between $10 and $100,000.");
4168
+ process.exit(1);
4169
+ }
4170
+ const resp = await apiRequest({ method: "POST", path: `${dspPrefix()}/payments/deposit`, body: { amount } });
4171
+ const json = await resp.json();
4172
+ if (!resp.ok) {
4173
+ console.error(`Error: ${json.error ?? resp.statusText}`);
4174
+ process.exit(1);
4175
+ }
4176
+ console.log(`Deposit of $${amount.toFixed(2)} initiated.`);
4177
+ });
4178
+ cmd.command("settings-update").description("Update billing settings.").option("--auto-recharge <bool>", "Enable/disable auto-recharge (true/false)").option("--from-json <file>", "Update from JSON file").action(async (opts) => {
4179
+ let body;
4180
+ if (opts.fromJson) {
4181
+ const { readFileSync: readFileSync3 } = await import("fs");
4182
+ body = JSON.parse(readFileSync3(opts.fromJson, "utf-8"));
4183
+ } else {
4184
+ body = {};
4185
+ if (opts.autoRecharge !== undefined)
4186
+ body.autoRecharge = opts.autoRecharge === "true";
4187
+ }
4188
+ const resp = await apiRequest({ method: "PUT", path: `${dspPrefix()}/payments/settings`, body });
4189
+ if (!resp.ok) {
4190
+ const j = await resp.json();
4191
+ console.error(`Error: ${j.error ?? resp.statusText}`);
4192
+ process.exit(1);
4193
+ }
4194
+ console.log("Billing settings updated.");
4195
+ });
4196
+ return cmd;
4197
+ }
4198
+
4199
+ // src/commands/users.ts
4200
+ function usersPrefix() {
4201
+ const ctx = loadContext();
4202
+ const context = getCurrentContext(ctx);
4203
+ const app = context?.app ?? "dsp";
4204
+ if (app === "console") {
4205
+ console.error("Error: Users commands are not available in Console mode.");
4206
+ console.error('Use "a8techads admin tenants members" for console user management.');
4207
+ console.error('Switch to DSP or SSP: "a8techads context dsp" or "a8techads context ssp"');
4208
+ process.exit(1);
4209
+ }
4210
+ return app === "ssp" ? "/api/v1/ssp" : "/api/v1/dsp";
4211
+ }
4212
+ var COLUMNS5 = [
4213
+ { key: "id", header: "ID", width: 36 },
4214
+ { key: "email", header: "EMAIL", width: 30 },
4215
+ { key: "name", header: "NAME", width: 20 },
4216
+ { key: "role", header: "ROLE", width: 20 },
4217
+ { key: "status", header: "STATUS", width: 10 }
4218
+ ];
4219
+ function createUsersCommand() {
4220
+ const cmd = new Command("users").description(`Team member management (DSP / SSP only)
4221
+
4222
+ Requires: ADVERTISER or PUBLISHER capability.
4223
+ Not available in Console mode — use "a8techads admin tenants members" instead.`).addHelpText("after", `
4224
+ Examples:
4225
+ $ a8techads users list
4226
+ $ a8techads users get <id>
4227
+ $ a8techads users invite --email user@example.com --role advertiser_admin`);
4228
+ addFormatOption(cmd.command("list").description("List team members.").option("--limit <n>", "Max results", "50")).action(async (opts) => {
4229
+ const params = new URLSearchParams({ limit: opts.limit });
4230
+ const resp = await apiRequest({ path: `${usersPrefix()}/users?${params}` });
4231
+ const json = await resp.json();
4232
+ if (!resp.ok) {
4233
+ console.error(`Error: ${json.error ?? resp.statusText}`);
4234
+ process.exit(1);
4235
+ }
4236
+ const rows = (json.data ?? json).map((u) => ({
4237
+ id: u.id ?? u.userId ?? u.user_id,
4238
+ email: u.email,
4239
+ name: u.name,
4240
+ role: u.role,
4241
+ status: u.status
4242
+ }));
4243
+ printData(rows, COLUMNS5, opts.format);
4244
+ });
4245
+ addFormatOption(cmd.command("get").description("Get team member details.").argument("<id>", "User ID")).action(async (id, opts) => {
4246
+ const resp = await apiRequest({ path: `${usersPrefix()}/users/${id}` });
4247
+ const json = await resp.json();
4248
+ if (!resp.ok) {
4249
+ console.error(`Error: ${json.error ?? resp.statusText}`);
4250
+ process.exit(1);
4251
+ }
4252
+ printDetail(json.data ?? json, opts.format);
4253
+ });
4254
+ cmd.command("invite").description(`Invite a new team member.
4255
+
4256
+ For single-capability tenants, use --role.
4257
+ For dual-capability tenants, use --capability-roles to assign roles
4258
+ for each capability independently.
4259
+
4260
+ Requires: admin role.`).option("--email <email>", "Email address (required)").option("--role <role>", "Single role (e.g., advertiser_admin, publisher_member)").option("--capability-roles <json>", `Per-capability roles as JSON (e.g., '{"ADVERTISER":"advertiser_admin","PUBLISHER":"publisher_member"}')`).addHelpText("after", `
4261
+ Examples:
4262
+ # Single-capability invite
4263
+ $ a8techads users invite --email user@example.com --role advertiser_admin
4264
+
4265
+ # Dual-capability invite (assign roles for both capabilities)
4266
+ $ a8techads users invite --email user@example.com \\
4267
+ --capability-roles '{"ADVERTISER":"advertiser_admin","PUBLISHER":"publisher_member"}'`).action(async (opts) => {
4268
+ if (!opts.email) {
4269
+ console.error("Error: --email is required.");
4270
+ process.exit(1);
4271
+ }
4272
+ if (!opts.role && !opts.capabilityRoles) {
4273
+ console.error("Error: Either --role or --capability-roles is required.");
4274
+ console.error('Run "a8techads users invite --help" for usage.');
4275
+ process.exit(1);
4276
+ }
4277
+ const body = { email: opts.email };
4278
+ if (opts.capabilityRoles) {
4279
+ try {
4280
+ body.capabilityRoles = JSON.parse(opts.capabilityRoles);
4281
+ } catch {
4282
+ console.error("Error: --capability-roles must be valid JSON.");
4283
+ console.error(`Example: '{"ADVERTISER":"advertiser_admin","PUBLISHER":"publisher_member"}'`);
4284
+ process.exit(1);
4285
+ }
4286
+ } else {
4287
+ body.role = opts.role;
4288
+ }
4289
+ const resp = await apiRequest({ method: "POST", path: `${usersPrefix()}/users`, body });
4290
+ const json = await resp.json();
4291
+ if (!resp.ok) {
4292
+ console.error(`Error: ${json.error ?? resp.statusText}`);
4293
+ process.exit(1);
4294
+ }
4295
+ console.log(`Invitation sent to ${opts.email}.`);
4296
+ });
4297
+ cmd.command("update").description(`Update a team member role.
4298
+
4299
+ Note: Per-capability role updates (capabilityRoles) are supported on
4300
+ invite only. Update currently accepts a single role.
4301
+
4302
+ Requires: admin role.`).argument("<id>", "User ID").option("--role <role>", "New role (required)").addHelpText("after", `
4303
+ Examples:
4304
+ $ a8techads users update <id> --role advertiser_admin
4305
+ $ a8techads users update <id> --role publisher_member`).action(async (id, opts) => {
4306
+ if (!opts.role) {
4307
+ console.error("Error: --role is required.");
4308
+ console.error('Run "a8techads users update --help" for usage.');
4309
+ process.exit(1);
4310
+ }
4311
+ const resp = await apiRequest({ method: "PATCH", path: `${usersPrefix()}/users/${id}`, body: { role: opts.role } });
4312
+ if (!resp.ok) {
4313
+ const j = await resp.json();
4314
+ console.error(`Error: ${j.error ?? resp.statusText}`);
4315
+ process.exit(1);
4316
+ }
4317
+ console.log(`User ${id} role updated to ${opts.role}.`);
4318
+ });
4319
+ return cmd;
4320
+ }
4321
+
4322
+ // src/commands/settings.ts
4323
+ function settingsPrefix() {
4324
+ const ctx = loadContext();
4325
+ const context = getCurrentContext(ctx);
4326
+ const app = context?.app ?? "dsp";
4327
+ if (app === "console") {
4328
+ console.error("Error: Settings commands are not available in Console mode.");
4329
+ console.error('Switch to DSP or SSP context: "a8techads context dsp" or "a8techads context ssp"');
4330
+ process.exit(1);
4331
+ }
4332
+ return app === "ssp" ? "/api/v1/ssp" : "/api/v1/dsp";
4333
+ }
4334
+ function createSettingsCommand() {
4335
+ const cmd = new Command("settings").description(`Tenant settings (DSP / SSP only)
4336
+
4337
+ Requires: ADVERTISER or PUBLISHER capability.
4338
+ Not available in Console mode.`).addHelpText("after", `
4339
+ Examples:
4340
+ $ a8techads settings show
4341
+ $ a8techads settings update --from-json settings.json
4342
+ $ a8techads settings profile
4343
+ $ a8techads settings profile-update --company-name "New Name"`);
4344
+ addFormatOption(cmd.command("show").description("Show current tenant settings.")).action(async (opts) => {
4345
+ const resp = await apiRequest({ path: `${settingsPrefix()}/settings` });
4346
+ const json = await resp.json();
4347
+ if (!resp.ok) {
4348
+ console.error(`Error: ${json.error ?? resp.statusText}`);
4349
+ process.exit(1);
4350
+ }
4351
+ printDetail(json.data ?? json, opts.format);
4352
+ });
4353
+ addFormatOption(cmd.command("profile").description("Show tenant profile (contact/business info).")).action(async (opts) => {
4354
+ const resp = await apiRequest({ path: `${settingsPrefix()}/settings/profile` });
4355
+ const json = await resp.json();
4356
+ if (!resp.ok) {
4357
+ console.error(`Error: ${json.error ?? resp.statusText}`);
4358
+ process.exit(1);
4359
+ }
4360
+ printDetail(json.data ?? json, opts.format);
4361
+ });
4362
+ cmd.command("update").description(`Update tenant settings.
4363
+
4364
+ Requires: admin role.`).option("--from-json <file>", "Update from JSON file").action(async (opts) => {
4365
+ if (!opts.fromJson) {
4366
+ console.error("Error: --from-json is required for settings update.");
4367
+ process.exit(1);
4368
+ }
4369
+ const { readFileSync: readFileSync3 } = await import("fs");
4370
+ const body = JSON.parse(readFileSync3(opts.fromJson, "utf-8"));
4371
+ const resp = await apiRequest({ method: "PATCH", path: `${settingsPrefix()}/settings`, body });
4372
+ if (!resp.ok) {
4373
+ const j = await resp.json();
4374
+ console.error(`Error: ${j.error ?? resp.statusText}`);
4375
+ process.exit(1);
4376
+ }
4377
+ console.log("Settings updated.");
4378
+ });
4379
+ cmd.command("profile-update").description("Update tenant profile.").option("--company-name <name>", "Company name").option("--from-json <file>", "Update from JSON file").action(async (opts) => {
4380
+ let body;
4381
+ if (opts.fromJson) {
4382
+ const { readFileSync: readFileSync3 } = await import("fs");
4383
+ body = JSON.parse(readFileSync3(opts.fromJson, "utf-8"));
4384
+ } else {
4385
+ body = {};
4386
+ if (opts.companyName)
4387
+ body.companyName = opts.companyName;
4388
+ }
4389
+ const resp = await apiRequest({ method: "PATCH", path: `${settingsPrefix()}/settings/profile`, body });
4390
+ if (!resp.ok) {
4391
+ const j = await resp.json();
4392
+ console.error(`Error: ${j.error ?? resp.statusText}`);
4393
+ process.exit(1);
4394
+ }
4395
+ console.log("Profile updated.");
4396
+ });
4397
+ return cmd;
4398
+ }
4399
+
4400
+ // src/commands/invoices.ts
4401
+ function createInvoicesCommand() {
4402
+ const cmd = new Command("invoices").description(`Invoice management (DSP)
4403
+
4404
+ Requires: ADVERTISER capability.`).addHelpText("after", `
4405
+ Examples:
4406
+ $ a8techads invoices list
4407
+ $ a8techads invoices get <id>
4408
+ $ a8techads invoices spending`);
4409
+ addFormatOption(cmd.command("list").description("List invoices.")).action(async (opts) => {
4410
+ const resp = await apiRequest({ path: `${dspPrefix()}/invoices` });
4411
+ const json = await resp.json();
4412
+ if (!resp.ok) {
4413
+ console.error(`Error: ${json.error ?? resp.statusText}`);
4414
+ process.exit(1);
4415
+ }
4416
+ const columns = [
4417
+ { key: "id", header: "ID", width: 36 },
4418
+ { key: "period", header: "PERIOD", width: 20 },
4419
+ { key: "amount", header: "AMOUNT", width: 12, format: (v) => v != null ? `$${Number(v).toFixed(2)}` : "-" },
4420
+ { key: "status", header: "STATUS", width: 12 }
4421
+ ];
4422
+ const rows = (json.data ?? json).map((i) => ({
4423
+ id: i.id,
4424
+ period: i.period ?? i.billingPeriod,
4425
+ amount: i.amount ?? i.totalAmount,
4426
+ status: i.status
4427
+ }));
4428
+ printData(rows, columns, opts.format);
4429
+ });
4430
+ addFormatOption(cmd.command("get").description("Get invoice details.").argument("<id>", "Invoice ID")).action(async (id, opts) => {
4431
+ const resp = await apiRequest({ path: `${dspPrefix()}/invoices/${id}` });
4432
+ const json = await resp.json();
4433
+ if (!resp.ok) {
4434
+ console.error(`Error: ${json.error ?? resp.statusText}`);
4435
+ process.exit(1);
4436
+ }
4437
+ printDetail(json.data ?? json, opts.format);
4438
+ });
4439
+ addFormatOption(cmd.command("spending").description("Show current period spending summary.")).action(async (opts) => {
4440
+ const resp = await apiRequest({ path: `${dspPrefix()}/invoices/spending` });
4441
+ const json = await resp.json();
4442
+ if (!resp.ok) {
4443
+ console.error(`Error: ${json.error ?? resp.statusText}`);
4444
+ process.exit(1);
4445
+ }
4446
+ printDetail(json.data ?? json, opts.format);
4447
+ });
4448
+ return cmd;
4449
+ }
4450
+
4451
+ // src/index.ts
4452
+ function createProgram() {
4453
+ const program2 = new Command().name("a8techads").description("A8TechAds CLI — programmatic ad platform management").version("0.2.0").addHelpText("after", `
4454
+ Command Groups:
4455
+ auth Authentication (login, logout, token, status)
4456
+ profile Multi-profile management
4457
+ context Workspace context (tenant, capability, app)
4458
+ campaigns Campaign management (DSP)
4459
+ variations Ad variation management (DSP)
4460
+ sites Site management (SSP)
4461
+ zones Ad zone management (SSP)
4462
+ reports Analytics and reporting
4463
+ billing Billing and payments (DSP)
4464
+ invoices Invoice management (DSP)
4465
+ users Team member management
4466
+ settings Tenant settings
4467
+
4468
+ Getting Started:
4469
+ $ a8techads auth login # Authenticate
4470
+ $ a8techads context show # Check current context
4471
+ $ a8techads campaigns list # List campaigns (DSP)
4472
+ $ a8techads sites list # List sites (SSP)
4473
+ $ a8techads reports query --metrics spend # Run analytics query`);
4474
+ program2.addCommand(createAuthCommand());
4475
+ program2.addCommand(createProfileCommand());
4476
+ program2.addCommand(createContextCommand());
4477
+ program2.addCommand(createCampaignsCommand());
4478
+ program2.addCommand(createVariationsCommand());
4479
+ program2.addCommand(createSitesCommand());
4480
+ program2.addCommand(createZonesCommand());
4481
+ program2.addCommand(createReportsCommand());
4482
+ program2.addCommand(createBillingCommand());
4483
+ program2.addCommand(createUsersCommand());
4484
+ program2.addCommand(createSettingsCommand());
4485
+ program2.addCommand(createInvoicesCommand());
4486
+ return program2;
4487
+ }
4488
+
4489
+ // bin/alpineads.ts
4490
+ var program2 = createProgram();
4491
+ program2.parse();