@agenzo/admin-cli 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1862 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ notify,
4
+ render,
5
+ resolveFormat
6
+ } from "./chunk-UIGWXIDT.js";
7
+ import {
8
+ Formatter,
9
+ createSpinner
10
+ } from "./chunk-AZALPHQR.js";
11
+
12
+ // src/index.ts
13
+ import { Command } from "commander";
14
+
15
+ // src/utils/errors.ts
16
+ var CliError = class extends Error {
17
+ };
18
+ var ApiBusinessError = class extends CliError {
19
+ constructor(errorCode, errorMessage, statusCode) {
20
+ super(`[${errorCode}] ${errorMessage}`);
21
+ this.errorCode = errorCode;
22
+ this.errorMessage = errorMessage;
23
+ this.statusCode = statusCode;
24
+ }
25
+ errorCode;
26
+ errorMessage;
27
+ statusCode;
28
+ code = "API_BUSINESS_ERROR";
29
+ };
30
+ var NetworkError = class extends CliError {
31
+ constructor(url, timeout, cause) {
32
+ const detail = cause?.message;
33
+ let msg;
34
+ if (timeout) {
35
+ msg = `Request timed out (${timeout}ms): ${url}`;
36
+ } else if (detail) {
37
+ msg = `Connection failed: ${url} (${detail})`;
38
+ } else {
39
+ msg = `Connection failed: ${url}`;
40
+ }
41
+ super(msg);
42
+ this.url = url;
43
+ this.timeout = timeout;
44
+ this.cause = cause;
45
+ }
46
+ url;
47
+ timeout;
48
+ cause;
49
+ code = "NETWORK_ERROR";
50
+ };
51
+ var AuthError = class extends CliError {
52
+ constructor(message, suggestion) {
53
+ super(message);
54
+ this.suggestion = suggestion;
55
+ }
56
+ suggestion;
57
+ code = "AUTH_ERROR";
58
+ };
59
+ var ConfigError = class extends CliError {
60
+ constructor(message, filePath) {
61
+ super(message);
62
+ this.filePath = filePath;
63
+ }
64
+ filePath;
65
+ code = "CONFIG_ERROR";
66
+ };
67
+ var ValidationError = class extends CliError {
68
+ code = "VALIDATION_ERROR";
69
+ constructor(message) {
70
+ super(message);
71
+ }
72
+ };
73
+ var IdempotencyKeyRequiredError = class extends CliError {
74
+ code = "IDEMPOTENCY_KEY_REQUIRED_ERROR";
75
+ constructor(commandPath) {
76
+ super(
77
+ `\`${commandPath}\` requires --idempotency-key <key>. Supply a unique key so the write can be safely retried.`
78
+ );
79
+ }
80
+ };
81
+ var UpgradeRequiredError = class extends CliError {
82
+ constructor(currentVersion, minVersion, upgradeCommand) {
83
+ super(
84
+ `agenzo-admin-cli ${currentVersion} is out of date \u2014 the server requires ${minVersion} or newer. To upgrade, run: ${upgradeCommand}`
85
+ );
86
+ this.currentVersion = currentVersion;
87
+ this.minVersion = minVersion;
88
+ this.upgradeCommand = upgradeCommand;
89
+ }
90
+ currentVersion;
91
+ minVersion;
92
+ upgradeCommand;
93
+ code = "UPGRADE_REQUIRED";
94
+ };
95
+ var UserCancelError = class extends CliError {
96
+ code = "USER_CANCEL_ERROR";
97
+ constructor(message = "Operation cancelled by user") {
98
+ super(message);
99
+ this.name = "UserCancelError";
100
+ }
101
+ };
102
+ function authErrorCode(error) {
103
+ const haystack = `${error.message} ${error.suggestion}`.toLowerCase();
104
+ if (haystack.includes("timed out") || haystack.includes("timeout")) {
105
+ return "AUTH_TIMEOUT";
106
+ }
107
+ if (haystack.includes("expired") || haystack.includes("session") || haystack.includes("refresh")) {
108
+ return "AUTH_SESSION_EXPIRED";
109
+ }
110
+ return "AUTH_NOT_SIGNED_IN";
111
+ }
112
+ function errorCodeFor(error) {
113
+ if (error instanceof UpgradeRequiredError) {
114
+ return "UPGRADE_REQUIRED";
115
+ }
116
+ if (error instanceof UserCancelError) {
117
+ return "USER_CANCELLED";
118
+ }
119
+ if (error instanceof AuthError) {
120
+ return authErrorCode(error);
121
+ }
122
+ if (error instanceof ValidationError) {
123
+ return "PARAM_INVALID";
124
+ }
125
+ if (error instanceof IdempotencyKeyRequiredError) {
126
+ return "PARAM_IDEMPOTENCY_KEY_REQUIRED";
127
+ }
128
+ if (error instanceof ConfigError) {
129
+ return "INTERNAL_ERROR";
130
+ }
131
+ if (error instanceof NetworkError) {
132
+ return "UPSTREAM_UNAVAILABLE";
133
+ }
134
+ if (error instanceof ApiBusinessError) {
135
+ const status = error.statusCode;
136
+ if (status === 401) {
137
+ return "AUTH_FAILED";
138
+ }
139
+ if (status === 403) {
140
+ return "KEY_SCOPE_DENIED";
141
+ }
142
+ if (status === 404) {
143
+ return "ORG_NOT_FOUND";
144
+ }
145
+ if (status === 409) {
146
+ return "ORG_CONFLICT";
147
+ }
148
+ if (status === 429) {
149
+ return "RATE_LIMITED";
150
+ }
151
+ if (status >= 500) {
152
+ return "UPSTREAM_UNAVAILABLE";
153
+ }
154
+ return "PARAM_INVALID";
155
+ }
156
+ return "INTERNAL_ERROR";
157
+ }
158
+ function toErrorEnvelope(error) {
159
+ const code = errorCodeFor(error);
160
+ let message;
161
+ if (error instanceof Error && error.message) {
162
+ message = error.message;
163
+ } else if (typeof error === "string" && error.length > 0) {
164
+ message = error;
165
+ } else {
166
+ message = "Unexpected error";
167
+ }
168
+ const http = error instanceof ApiBusinessError ? error.statusCode : void 0;
169
+ if (http === void 0) {
170
+ return { error: { code, message } };
171
+ }
172
+ return { error: { code, message, http } };
173
+ }
174
+
175
+ // src/utils/version.ts
176
+ import { readFileSync } from "fs";
177
+ import { dirname, join } from "path";
178
+ import { fileURLToPath } from "url";
179
+ var UPGRADE_COMMAND = "npm install -g agenzo-admin-cli@latest";
180
+ var FALLBACK_VERSION = "0.1.1";
181
+ var cachedVersion = null;
182
+ function getCurrentVersion() {
183
+ if (cachedVersion !== null) return cachedVersion;
184
+ const here = dirname(fileURLToPath(import.meta.url));
185
+ const candidates = [
186
+ join(here, "..", "package.json"),
187
+ // dist/ → project root
188
+ join(here, "..", "..", "package.json")
189
+ // src/utils/ → project root
190
+ ];
191
+ for (const p of candidates) {
192
+ try {
193
+ const pkg = JSON.parse(readFileSync(p, "utf-8"));
194
+ if (typeof pkg.version === "string") {
195
+ cachedVersion = pkg.version;
196
+ return cachedVersion;
197
+ }
198
+ } catch {
199
+ }
200
+ }
201
+ cachedVersion = FALLBACK_VERSION;
202
+ console.warn(
203
+ `[agenzo-admin-cli] package.json not found; using fallback version ${FALLBACK_VERSION}`
204
+ );
205
+ return cachedVersion;
206
+ }
207
+ function compareVersions(a, b) {
208
+ const parse = (v) => {
209
+ const clean = v.trim().replace(/^v/, "").split(/[-+]/)[0];
210
+ const parts = clean.split(".");
211
+ return [
212
+ parseInt(parts[0] ?? "0", 10) || 0,
213
+ parseInt(parts[1] ?? "0", 10) || 0,
214
+ parseInt(parts[2] ?? "0", 10) || 0
215
+ ];
216
+ };
217
+ const [a1, a2, a3] = parse(a);
218
+ const [b1, b2, b3] = parse(b);
219
+ if (a1 !== b1) return a1 - b1;
220
+ if (a2 !== b2) return a2 - b2;
221
+ return a3 - b3;
222
+ }
223
+ function isBelow(current, minimum) {
224
+ return compareVersions(current, minimum) < 0;
225
+ }
226
+
227
+ // src/api/client.ts
228
+ var ApiClient = class {
229
+ baseUrl;
230
+ timeout;
231
+ constructor(config) {
232
+ this.baseUrl = config.baseUrl.replace(/\/+$/, "");
233
+ this.timeout = config.timeout ?? 6e4;
234
+ }
235
+ buildHeaders(auth) {
236
+ const headers = {
237
+ "User-Agent": `agenzo-admin-cli/${getCurrentVersion()}`
238
+ };
239
+ if (auth.type === "bearer") {
240
+ headers["Authorization"] = `Bearer ${auth.token}`;
241
+ } else if (auth.type === "api-key") {
242
+ headers["X-Api-Key"] = auth.key;
243
+ }
244
+ return headers;
245
+ }
246
+ async get(path, auth, params) {
247
+ let url = `${this.baseUrl}${path}`;
248
+ if (params && Object.keys(params).length > 0) {
249
+ const searchParams = new URLSearchParams(params);
250
+ url += `?${searchParams.toString()}`;
251
+ }
252
+ return this.request(url, {
253
+ method: "GET",
254
+ headers: this.buildHeaders(auth)
255
+ });
256
+ }
257
+ async post(path, auth, body, extraHeaders) {
258
+ const url = `${this.baseUrl}${path}`;
259
+ const headers = this.buildHeaders(auth);
260
+ headers["Content-Type"] = "application/json";
261
+ if (extraHeaders) {
262
+ Object.assign(headers, extraHeaders);
263
+ }
264
+ return this.request(url, {
265
+ method: "POST",
266
+ headers,
267
+ body: body ? JSON.stringify(body) : void 0
268
+ });
269
+ }
270
+ async request(url, init) {
271
+ const controller = new AbortController();
272
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
273
+ try {
274
+ const response = await fetch(url, {
275
+ ...init,
276
+ signal: controller.signal
277
+ });
278
+ this.enforceMinVersion(response.headers.get("x-cli-min-version"));
279
+ const contentType = response.headers.get("content-type") ?? "";
280
+ let responseBody;
281
+ if (contentType.includes("application/json")) {
282
+ responseBody = await response.json();
283
+ } else {
284
+ const text = await response.text();
285
+ if (!response.ok) {
286
+ const statusMsg = this.friendlyStatusMessage(response.status);
287
+ return {
288
+ success: false,
289
+ errorCode: 0,
290
+ errorMessage: statusMsg,
291
+ statusCode: response.status
292
+ };
293
+ }
294
+ try {
295
+ responseBody = JSON.parse(text);
296
+ } catch {
297
+ return {
298
+ success: false,
299
+ errorCode: 0,
300
+ errorMessage: `Unexpected response from server (${response.status})`,
301
+ statusCode: response.status
302
+ };
303
+ }
304
+ }
305
+ const code = responseBody.code;
306
+ const message = responseBody.message;
307
+ const data = responseBody.data;
308
+ if (response.ok && (!code || code === "0000")) {
309
+ const payload = data ?? responseBody;
310
+ return { success: true, data: payload, message };
311
+ }
312
+ const errorCode = code ? parseInt(code, 10) : 0;
313
+ let errorMsg = message ?? response.statusText;
314
+ if (!errorMsg || errorMsg === response.statusText) {
315
+ const detail = responseBody.detail;
316
+ if (Array.isArray(detail)) {
317
+ errorMsg = detail.map((d) => {
318
+ const loc = d.loc?.slice(1).join(".") ?? "";
319
+ return loc ? `${loc}: ${d.msg}` : String(d.msg);
320
+ }).join("; ");
321
+ } else if (typeof detail === "string") {
322
+ errorMsg = detail;
323
+ }
324
+ }
325
+ return {
326
+ success: false,
327
+ errorCode: isNaN(errorCode) ? 0 : errorCode,
328
+ errorMessage: errorMsg,
329
+ statusCode: response.status
330
+ };
331
+ } catch (error) {
332
+ if (error instanceof UpgradeRequiredError) {
333
+ throw error;
334
+ }
335
+ if (error instanceof DOMException && error.name === "AbortError") {
336
+ throw new NetworkError(url, this.timeout);
337
+ }
338
+ throw new NetworkError(url, void 0, error instanceof Error ? error : void 0);
339
+ } finally {
340
+ clearTimeout(timeoutId);
341
+ }
342
+ }
343
+ /**
344
+ * Validate that the running CLI meets the server-advertised minimum.
345
+ * Missing or empty header → skip (backward compat with servers that don't
346
+ * advertise yet, and clients hitting legacy proxies).
347
+ */
348
+ enforceMinVersion(headerValue) {
349
+ const minVersion = headerValue?.trim();
350
+ if (!minVersion) return;
351
+ const current = getCurrentVersion();
352
+ if (isBelow(current, minVersion)) {
353
+ throw new UpgradeRequiredError(current, minVersion, UPGRADE_COMMAND);
354
+ }
355
+ }
356
+ friendlyStatusMessage(status) {
357
+ const messages = {
358
+ 400: "Invalid request. Please check your input.",
359
+ 401: "Authentication failed. Please check your API key or login again.",
360
+ 403: "Access denied. You do not have permission for this operation.",
361
+ 404: "Resource not found. Please check the ID.",
362
+ 409: "Conflict. This resource may already exist.",
363
+ 422: "Invalid input. Please check the request parameters.",
364
+ 429: "Too many requests. Please wait and try again.",
365
+ 500: "Something went wrong on the server. Please try again later.",
366
+ 502: "Service is temporarily unavailable. Please try again in a moment.",
367
+ 503: "Service is temporarily unavailable. Please try again in a moment.",
368
+ 504: "The request took too long. Please try again."
369
+ };
370
+ return messages[status] ?? `Something went wrong (${status}). Please try again later.`;
371
+ }
372
+ };
373
+
374
+ // src/config/config-manager.ts
375
+ import { readFile, writeFile, mkdir } from "fs/promises";
376
+ import { join as join2 } from "path";
377
+ import { homedir } from "os";
378
+ var DEFAULT_CONFIG = {
379
+ active_org: null,
380
+ active_developer_id: null,
381
+ api_host: "https://agent.everonet.com",
382
+ api_path: "/api/v3/agent-pay"
383
+ };
384
+ var ConfigManager = class {
385
+ basePath;
386
+ configPath;
387
+ constructor(basePath) {
388
+ this.basePath = basePath ?? join2(homedir(), ".agenzo-admin-cli");
389
+ this.configPath = join2(this.basePath, "config.json");
390
+ }
391
+ async ensureDirectories() {
392
+ await mkdir(this.basePath, { recursive: true });
393
+ await mkdir(join2(this.basePath, "credentials"), { recursive: true });
394
+ }
395
+ async load() {
396
+ try {
397
+ const content = await readFile(this.configPath, "utf-8");
398
+ try {
399
+ const raw = JSON.parse(content);
400
+ if (raw.api_base_url && !raw.api_host) {
401
+ const url = String(raw.api_base_url);
402
+ const pathIndex = url.indexOf("/api/");
403
+ raw.api_host = pathIndex > 0 ? url.slice(0, pathIndex) : url;
404
+ raw.api_path = pathIndex > 0 ? url.slice(pathIndex) : DEFAULT_CONFIG.api_path;
405
+ delete raw.api_base_url;
406
+ }
407
+ return {
408
+ active_org: raw.active_org ?? null,
409
+ active_developer_id: raw.active_developer_id ?? null,
410
+ api_host: raw.api_host ?? DEFAULT_CONFIG.api_host,
411
+ api_path: raw.api_path ?? DEFAULT_CONFIG.api_path
412
+ };
413
+ } catch {
414
+ throw new ConfigError(
415
+ `Invalid config file: ${this.configPath}`,
416
+ this.configPath
417
+ );
418
+ }
419
+ } catch (error) {
420
+ if (error instanceof ConfigError) throw error;
421
+ if (error.code === "ENOENT") {
422
+ return { ...DEFAULT_CONFIG };
423
+ }
424
+ throw error;
425
+ }
426
+ }
427
+ async save(config) {
428
+ await this.ensureDirectories();
429
+ await writeFile(this.configPath, JSON.stringify(config, null, 2), "utf-8");
430
+ }
431
+ async getActiveOrg() {
432
+ const config = await this.load();
433
+ return config.active_org;
434
+ }
435
+ async setActiveOrg(orgId) {
436
+ const config = await this.load();
437
+ config.active_org = orgId;
438
+ await this.save(config);
439
+ }
440
+ async getApiBaseUrl() {
441
+ const config = await this.load();
442
+ const host = config.api_host.replace(/\/+$/, "");
443
+ const path = config.api_path.startsWith("/") ? config.api_path : `/${config.api_path}`;
444
+ return `${host}${path}`;
445
+ }
446
+ async setApiHost(host) {
447
+ const config = await this.load();
448
+ config.api_host = host;
449
+ await this.save(config);
450
+ }
451
+ async getApiHost() {
452
+ const config = await this.load();
453
+ return config.api_host;
454
+ }
455
+ };
456
+
457
+ // src/config/credential-store.ts
458
+ import { readFile as readFile2, writeFile as writeFile2, readdir, unlink, access, mkdir as mkdir2 } from "fs/promises";
459
+ import { join as join3 } from "path";
460
+ import { homedir as homedir2 } from "os";
461
+ var CredentialStore = class {
462
+ basePath;
463
+ constructor(basePath) {
464
+ this.basePath = basePath ?? join3(homedir2(), ".agenzo-admin-cli", "credentials");
465
+ }
466
+ filePath(orgId) {
467
+ return join3(this.basePath, `${orgId}.json`);
468
+ }
469
+ async ensureDir() {
470
+ await mkdir2(this.basePath, { recursive: true });
471
+ }
472
+ async get(orgId) {
473
+ try {
474
+ const content = await readFile2(this.filePath(orgId), "utf-8");
475
+ return JSON.parse(content);
476
+ } catch (error) {
477
+ if (error.code === "ENOENT") {
478
+ return null;
479
+ }
480
+ throw error;
481
+ }
482
+ }
483
+ async save(credential) {
484
+ await this.ensureDir();
485
+ await writeFile2(
486
+ this.filePath(credential.org_id),
487
+ JSON.stringify(credential, null, 2),
488
+ "utf-8"
489
+ );
490
+ }
491
+ async delete(orgId) {
492
+ try {
493
+ await unlink(this.filePath(orgId));
494
+ } catch (error) {
495
+ if (error.code === "ENOENT") {
496
+ return;
497
+ }
498
+ throw error;
499
+ }
500
+ }
501
+ async listAll() {
502
+ try {
503
+ const files = await readdir(this.basePath);
504
+ const jsonFiles = files.filter((f) => f.endsWith(".json"));
505
+ const credentials = [];
506
+ for (const file of jsonFiles) {
507
+ try {
508
+ const content = await readFile2(join3(this.basePath, file), "utf-8");
509
+ credentials.push(JSON.parse(content));
510
+ } catch {
511
+ }
512
+ }
513
+ return credentials;
514
+ } catch (error) {
515
+ if (error.code === "ENOENT") {
516
+ return [];
517
+ }
518
+ throw error;
519
+ }
520
+ }
521
+ async exists(orgId) {
522
+ try {
523
+ await access(this.filePath(orgId));
524
+ return true;
525
+ } catch {
526
+ return false;
527
+ }
528
+ }
529
+ };
530
+
531
+ // src/config/key-store.ts
532
+ import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
533
+ import { dirname as dirname2, join as join4 } from "path";
534
+ import { homedir as homedir3 } from "os";
535
+ var KeyStore = class {
536
+ filePath;
537
+ constructor(filePath) {
538
+ this.filePath = filePath ?? join4(homedir3(), ".agenzo-admin-cli", "keys.json");
539
+ }
540
+ async loadData() {
541
+ try {
542
+ const content = await readFile3(this.filePath, "utf-8");
543
+ return JSON.parse(content);
544
+ } catch (error) {
545
+ if (error.code === "ENOENT") {
546
+ return {};
547
+ }
548
+ throw error;
549
+ }
550
+ }
551
+ async saveData(data) {
552
+ await mkdir3(dirname2(this.filePath), { recursive: true });
553
+ await writeFile3(this.filePath, JSON.stringify(data, null, 2), "utf-8");
554
+ }
555
+ async add(orgId, key) {
556
+ const data = await this.loadData();
557
+ if (!data[orgId]) {
558
+ data[orgId] = [];
559
+ }
560
+ data[orgId].push(key);
561
+ await this.saveData(data);
562
+ }
563
+ async update(orgId, keyId, newKeyValue) {
564
+ const data = await this.loadData();
565
+ const keys = data[orgId];
566
+ if (!keys) return;
567
+ const key = keys.find((k) => k.key_id === keyId);
568
+ if (key) {
569
+ key.key_value = newKeyValue;
570
+ await this.saveData(data);
571
+ }
572
+ }
573
+ async list(orgId) {
574
+ const data = await this.loadData();
575
+ return data[orgId] ?? [];
576
+ }
577
+ async get(orgId, keyId) {
578
+ const data = await this.loadData();
579
+ const keys = data[orgId];
580
+ if (!keys) return null;
581
+ return keys.find((k) => k.key_id === keyId) ?? null;
582
+ }
583
+ };
584
+
585
+ // src/utils/prompt-engine.ts
586
+ import { input, password, select } from "@inquirer/prompts";
587
+ var PromptEngine = class _PromptEngine {
588
+ /** Return flagValue directly if provided, otherwise prompt interactively */
589
+ static async resolveInput(flagValue, config) {
590
+ if (flagValue !== void 0) {
591
+ return flagValue;
592
+ }
593
+ if (config.type === "password") {
594
+ return password({ message: config.message, mask: "*" });
595
+ }
596
+ if (config.type === "select" && config.choices) {
597
+ return select({
598
+ message: config.message,
599
+ choices: config.choices
600
+ });
601
+ }
602
+ return input({
603
+ message: config.message,
604
+ validate: config.validate
605
+ });
606
+ }
607
+ /** Always collect CVV interactively with masked display */
608
+ static async collectCvv() {
609
+ return password({
610
+ message: "CVV:",
611
+ mask: "*"
612
+ });
613
+ }
614
+ /** Collect payment method params based on type */
615
+ static async collectPaymentMethodParams(type, flags) {
616
+ const params = { type };
617
+ const email = await _PromptEngine.resolveInput(flags.cardEmail ?? flags.email, {
618
+ message: "Email (for 3DS verification):"
619
+ });
620
+ params.email = email;
621
+ if (type === "card") {
622
+ params.card_number = await _PromptEngine.resolveInput(flags.cardNumber, {
623
+ message: "Card number:"
624
+ });
625
+ params.expiry_date = await _PromptEngine.resolveInput(flags.expiry, {
626
+ message: "Expiry (MMYY):"
627
+ });
628
+ if (flags.cvv) {
629
+ params.cvv = flags.cvv;
630
+ } else {
631
+ params.cvv = await _PromptEngine.collectCvv();
632
+ }
633
+ }
634
+ return params;
635
+ }
636
+ };
637
+
638
+ // src/auth/auth-service.ts
639
+ var POLL_INTERVAL_MS = 3e3;
640
+ var POLL_TIMEOUT_MS = 10 * 60 * 1e3;
641
+ var TOKEN_REFRESH_THRESHOLD_S = 300;
642
+ var AuthService = class {
643
+ constructor(apiClient, credentialStore, configManager) {
644
+ this.apiClient = apiClient;
645
+ this.credentialStore = credentialStore;
646
+ this.configManager = configManager;
647
+ }
648
+ apiClient;
649
+ credentialStore;
650
+ configManager;
651
+ async login(email, options = {}) {
652
+ const { Formatter: Formatter2 } = await import("./formatter-3JVOJXN6.js");
653
+ let isNewRegistration = false;
654
+ const noAuth = { type: "none" };
655
+ const idempotencyHeaders = options.idempotencyKey ? { "Idempotency-Key": options.idempotencyKey } : void 0;
656
+ const loginResult = await this.apiClient.post(
657
+ "/auth/login",
658
+ noAuth,
659
+ { email },
660
+ idempotencyHeaders
661
+ );
662
+ let magicLinkToken;
663
+ if (!loginResult.success && loginResult.errorCode === 1007) {
664
+ isNewRegistration = true;
665
+ const orgName = await PromptEngine.resolveInput(void 0, {
666
+ message: "Organization name:"
667
+ });
668
+ const registerBody = {
669
+ email,
670
+ organization_name: orgName
671
+ };
672
+ let registerResult = await this.apiClient.post(
673
+ "/auth/register",
674
+ noAuth,
675
+ registerBody,
676
+ idempotencyHeaders
677
+ );
678
+ if (!registerResult.success && registerResult.errorCode === 1103) {
679
+ const invitationCode = await PromptEngine.resolveInput(void 0, {
680
+ message: "Invitation code:"
681
+ });
682
+ registerBody.invitation_code = invitationCode;
683
+ registerResult = await this.apiClient.post(
684
+ "/auth/register",
685
+ noAuth,
686
+ registerBody,
687
+ idempotencyHeaders
688
+ );
689
+ }
690
+ if (!registerResult.success) {
691
+ throw new AuthError(
692
+ `Registration failed: ${registerResult.errorMessage}`,
693
+ "Please check your input and try again"
694
+ );
695
+ }
696
+ magicLinkToken = registerResult.data.magic_link_token;
697
+ } else if (!loginResult.success) {
698
+ throw new AuthError(
699
+ `Login failed: ${loginResult.errorMessage}`,
700
+ "Please check your email and try again"
701
+ );
702
+ } else {
703
+ magicLinkToken = loginResult.data.magic_link_token;
704
+ }
705
+ if (!options.quiet) {
706
+ console.error(Formatter2.status("success", "Magic link sent. Please check your inbox."));
707
+ }
708
+ const credential = await this.pollMagicLinkStatus(
709
+ magicLinkToken,
710
+ email,
711
+ options.quiet
712
+ );
713
+ await this.credentialStore.save(credential);
714
+ await this.configManager.setActiveOrg(credential.org_id);
715
+ return { credential, isNewRegistration };
716
+ }
717
+ async pollMagicLinkStatus(magicLinkToken, email, quiet = false) {
718
+ const startTime = Date.now();
719
+ const noAuth = { type: "none" };
720
+ const spinner = quiet ? null : createSpinner("Waiting for email verification");
721
+ while (Date.now() - startTime < POLL_TIMEOUT_MS) {
722
+ const result = await this.apiClient.get(
723
+ "/auth/magic-links/status",
724
+ noAuth,
725
+ { token: magicLinkToken }
726
+ );
727
+ if (!result.success) {
728
+ spinner?.stop();
729
+ if (result.errorCode === 1101) {
730
+ throw new AuthError("Magic link expired", "Please run agenzo-admin-cli auth login again");
731
+ }
732
+ throw new AuthError(
733
+ `Polling failed: [${result.errorCode}] ${result.errorMessage}`,
734
+ "Please run agenzo-admin-cli auth login again"
735
+ );
736
+ }
737
+ const data = result.data;
738
+ if (data.status === "CONSUMED") {
739
+ spinner?.stop();
740
+ const raw = data;
741
+ const org = raw.organization;
742
+ const orgId = String(raw.organization_id ?? org?.id ?? "");
743
+ const orgName = String(org?.name ?? "");
744
+ let accessExpiresAt;
745
+ const rawExpires = raw.expires_at ?? data.access_token_expires_at;
746
+ if (typeof rawExpires === "string") {
747
+ accessExpiresAt = Math.floor(new Date(rawExpires).getTime() / 1e3);
748
+ } else {
749
+ accessExpiresAt = rawExpires ?? 0;
750
+ }
751
+ const refreshExpiresAt = data.refresh_token_expires_at ?? accessExpiresAt + 30 * 24 * 60 * 60;
752
+ return {
753
+ org_id: orgId,
754
+ org_name: orgName,
755
+ email,
756
+ access_token: data.access_token,
757
+ refresh_token: data.refresh_token,
758
+ access_token_expires_at: accessExpiresAt,
759
+ refresh_token_expires_at: refreshExpiresAt,
760
+ api_host: await this.configManager.getApiHost()
761
+ };
762
+ }
763
+ if (data.status === "EXPIRED") {
764
+ spinner?.stop();
765
+ throw new AuthError("Magic link expired", "Please run agenzo-admin-cli auth login again");
766
+ }
767
+ await this.sleep(POLL_INTERVAL_MS);
768
+ }
769
+ spinner?.stop();
770
+ throw new AuthError("Login timed out (10 minutes)", "Please run agenzo-admin-cli auth login again");
771
+ }
772
+ async logout() {
773
+ const orgId = await this.configManager.getActiveOrg();
774
+ if (!orgId) {
775
+ throw new AuthError("Not signed in", "Please run agenzo-admin-cli auth login first");
776
+ }
777
+ const credential = await this.credentialStore.get(orgId);
778
+ if (credential) {
779
+ try {
780
+ await this.apiClient.post(
781
+ "/auth/logout",
782
+ { type: "bearer", token: credential.access_token }
783
+ );
784
+ } catch {
785
+ }
786
+ }
787
+ await this.credentialStore.delete(orgId);
788
+ }
789
+ async getValidAccessToken() {
790
+ const orgId = await this.configManager.getActiveOrg();
791
+ if (!orgId) {
792
+ throw new AuthError("Not signed in", "Please run agenzo-admin-cli auth login first");
793
+ }
794
+ const credential = await this.credentialStore.get(orgId);
795
+ if (!credential) {
796
+ throw new AuthError("Not signed in", "Please run agenzo-admin-cli auth login first");
797
+ }
798
+ const now = Math.floor(Date.now() / 1e3);
799
+ if (credential.access_token_expires_at - now < TOKEN_REFRESH_THRESHOLD_S) {
800
+ try {
801
+ await this.refreshToken(orgId);
802
+ const refreshed = await this.credentialStore.get(orgId);
803
+ return refreshed.access_token;
804
+ } catch {
805
+ return this.autoReLogin(credential);
806
+ }
807
+ }
808
+ return credential.access_token;
809
+ }
810
+ /**
811
+ * Automatically re-login using the stored email.
812
+ * Sends a magic link and polls until verified, then updates the
813
+ * credential without changing the active org.
814
+ */
815
+ async autoReLogin(credential) {
816
+ const { Formatter: Formatter2 } = await import("./formatter-3JVOJXN6.js");
817
+ const { resolveFormat: resolveFormat2 } = await import("./output-VMJKMMKS.js");
818
+ const quiet = resolveFormat2(void 0) === "json";
819
+ if (!quiet) {
820
+ console.error(Formatter2.status("info", "Session expired, re-authenticating"));
821
+ console.error(Formatter2.status("loading", "Sending magic link"));
822
+ }
823
+ const noAuth = { type: "none" };
824
+ const loginResult = await this.apiClient.post(
825
+ "/auth/login",
826
+ noAuth,
827
+ { email: credential.email }
828
+ );
829
+ if (!loginResult.success) {
830
+ throw new AuthError(
831
+ `Auto re-login failed: ${loginResult.errorMessage}`,
832
+ "Please run agenzo-admin-cli auth login manually"
833
+ );
834
+ }
835
+ const newCredential = await this.pollMagicLinkStatus(
836
+ loginResult.data.magic_link_token,
837
+ credential.email,
838
+ quiet
839
+ );
840
+ const activeOrg = await this.configManager.getActiveOrg();
841
+ if (activeOrg && newCredential.org_id !== activeOrg) {
842
+ await this.credentialStore.save(newCredential);
843
+ await this.configManager.setActiveOrg(newCredential.org_id);
844
+ if (!quiet) {
845
+ console.error(Formatter2.status("warning", `You were signed into a different organization: ${newCredential.org_name}. Your active organization has been updated.`));
846
+ }
847
+ throw new AuthError(
848
+ "Please run your command again.",
849
+ "The active organization was switched during re-authentication."
850
+ );
851
+ }
852
+ await this.credentialStore.save(newCredential);
853
+ if (!quiet) {
854
+ console.error(Formatter2.status("success", "Re-authenticated successfully"));
855
+ }
856
+ return newCredential.access_token;
857
+ }
858
+ async refreshToken(orgId) {
859
+ const credential = await this.credentialStore.get(orgId);
860
+ if (!credential) {
861
+ throw new AuthError("Not signed in", "Please run agenzo-admin-cli auth login first");
862
+ }
863
+ const result = await this.apiClient.post(
864
+ "/auth/refresh",
865
+ { type: "bearer", token: credential.access_token },
866
+ { refresh_token: credential.refresh_token }
867
+ );
868
+ if (!result.success) {
869
+ if (result.errorCode === 1002) {
870
+ throw new AuthError("Session expired", "Please run agenzo-admin-cli auth login again");
871
+ }
872
+ throw new AuthError(
873
+ `Token refresh failed: [${result.errorCode}] ${result.errorMessage}`,
874
+ "Please run agenzo-admin-cli auth login again"
875
+ );
876
+ }
877
+ credential.access_token = result.data.access_token;
878
+ credential.refresh_token = result.data.refresh_token;
879
+ if (result.data.access_token_expires_at) {
880
+ credential.access_token_expires_at = result.data.access_token_expires_at;
881
+ } else if (result.data.expires_at) {
882
+ credential.access_token_expires_at = Math.floor(new Date(result.data.expires_at).getTime() / 1e3);
883
+ }
884
+ await this.credentialStore.save(credential);
885
+ }
886
+ /**
887
+ * Execute an authenticated API call with automatic token recovery.
888
+ * If the call returns 1002 (token invalid), attempts refresh → re-login → retry once.
889
+ */
890
+ async executeWithAuth(apiFn) {
891
+ const token = await this.getValidAccessToken();
892
+ const result = await apiFn(token);
893
+ if (!result.success && result.errorCode === 1002) {
894
+ const freshToken = await this.recoverToken();
895
+ return apiFn(freshToken);
896
+ }
897
+ return result;
898
+ }
899
+ /**
900
+ * Attempt token refresh; if that fails, fall back to auto re-login.
901
+ */
902
+ async recoverToken() {
903
+ const orgId = await this.configManager.getActiveOrg();
904
+ if (!orgId) {
905
+ throw new AuthError("Not signed in", "Please run agenzo-admin-cli auth login first");
906
+ }
907
+ const credential = await this.credentialStore.get(orgId);
908
+ if (!credential) {
909
+ throw new AuthError("Not signed in", "Please run agenzo-admin-cli auth login first");
910
+ }
911
+ try {
912
+ await this.refreshToken(orgId);
913
+ const refreshed = await this.credentialStore.get(orgId);
914
+ return refreshed.access_token;
915
+ } catch {
916
+ return this.autoReLogin(credential);
917
+ }
918
+ }
919
+ sleep(ms) {
920
+ return new Promise((resolve) => setTimeout(resolve, ms));
921
+ }
922
+ };
923
+
924
+ // src/utils/exit.ts
925
+ function isUserCancel(error) {
926
+ if (typeof error !== "object" || error === null) {
927
+ return false;
928
+ }
929
+ const code = error.code;
930
+ const name = error.name;
931
+ return code === "USER_CANCEL_ERROR" || name === "UserCancelError";
932
+ }
933
+ function exitCodeFor(error) {
934
+ if (error instanceof UpgradeRequiredError) {
935
+ return 2;
936
+ }
937
+ if (error instanceof AuthError) {
938
+ return 3;
939
+ }
940
+ if (isUserCancel(error)) {
941
+ return 5;
942
+ }
943
+ if (error instanceof NetworkError) {
944
+ return 4;
945
+ }
946
+ if (error instanceof ValidationError) {
947
+ return 1;
948
+ }
949
+ if (error instanceof IdempotencyKeyRequiredError) {
950
+ return 1;
951
+ }
952
+ if (error instanceof ConfigError) {
953
+ return 1;
954
+ }
955
+ if (error instanceof ApiBusinessError) {
956
+ const status = error.statusCode;
957
+ if (status === 401 || status === 403) {
958
+ return 3;
959
+ }
960
+ if (status >= 500) {
961
+ return 4;
962
+ }
963
+ return 1;
964
+ }
965
+ return 1;
966
+ }
967
+
968
+ // src/auth/login.ts
969
+ function registerLoginCommand(program, deps) {
970
+ program.command("login").description("Sign in to Agent Payment API").option("--email <email>", "Email address").option(
971
+ "--idempotency-key <key>",
972
+ "Forwarded verbatim as the Idempotency-Key header on login/registration"
973
+ ).action(async (options, command) => {
974
+ const format = resolveFormat(command.optsWithGlobals().format);
975
+ const email = await PromptEngine.resolveInput(options.email, {
976
+ message: "Email:"
977
+ });
978
+ let idempotencyKey = options.idempotencyKey;
979
+ if (!idempotencyKey) {
980
+ if (command.optsWithGlobals().yes) {
981
+ throw new IdempotencyKeyRequiredError("auth login");
982
+ }
983
+ idempotencyKey = await PromptEngine.resolveInput(void 0, {
984
+ message: "Idempotency key (unique per write, for safe retry):",
985
+ validate: (v) => v.trim().length > 0 || "Idempotency key is required"
986
+ });
987
+ }
988
+ const result = await deps.authService.login(email, {
989
+ idempotencyKey,
990
+ quiet: format === "json"
991
+ });
992
+ const signedInMessage = result.isNewRegistration ? "Registered and signed in" : "Signed in successfully";
993
+ notify(format, "success", signedInMessage);
994
+ const data = {
995
+ org_id: result.credential.org_id,
996
+ org_name: result.credential.org_name,
997
+ email: result.credential.email,
998
+ is_new_registration: result.isNewRegistration
999
+ };
1000
+ const commandResult = {
1001
+ data,
1002
+ text: () => Formatter.keyValue([
1003
+ ["Org ID", data.org_id],
1004
+ ["Org Name", data.org_name],
1005
+ ["Email", data.email]
1006
+ ]),
1007
+ note: signedInMessage
1008
+ };
1009
+ render(commandResult, { format });
1010
+ });
1011
+ }
1012
+
1013
+ // src/auth/logout.ts
1014
+ function registerLogoutCommand(program, deps) {
1015
+ program.command("logout").description("Sign out of current organization").action(async (_options, command) => {
1016
+ const format = resolveFormat(command.optsWithGlobals().format);
1017
+ await deps.authService.logout();
1018
+ notify(format, "success", "Signed out");
1019
+ const data = { signed_out: true };
1020
+ const commandResult = {
1021
+ data,
1022
+ text: () => Formatter.status("success", "Signed out"),
1023
+ note: "Signed out"
1024
+ };
1025
+ render(commandResult, { format });
1026
+ });
1027
+ }
1028
+
1029
+ // src/config/set.ts
1030
+ var DEFAULT_HOST = "https://agent.everonet.com";
1031
+ async function applyHost(deps, host, verb, format) {
1032
+ await deps.configManager.setApiHost(host);
1033
+ const credentials = await deps.credentialStore.listAll();
1034
+ const match = credentials.find((c) => c.api_host === host);
1035
+ let activeOrg;
1036
+ if (match) {
1037
+ await deps.configManager.setActiveOrg(match.org_id);
1038
+ activeOrg = match.org_id;
1039
+ } else {
1040
+ const config = await deps.configManager.load();
1041
+ config.active_org = null;
1042
+ await deps.configManager.save(config);
1043
+ activeOrg = null;
1044
+ }
1045
+ notify(format, "success", `API host ${verb}: ${host}`);
1046
+ notify(
1047
+ format,
1048
+ "info",
1049
+ match ? `Switched to organization: ${match.org_name} (${match.org_id})` : "No organization found for this host. Please run login."
1050
+ );
1051
+ return {
1052
+ data: { api_host: host, active_org: activeOrg },
1053
+ // stdout payload projection (table mode). The ✓/ℹ status lines are emitted
1054
+ // by `notify` above (stderr) — do NOT repeat them here, or table mode would
1055
+ // print each line twice (once on stderr via notify, once on stdout via render).
1056
+ text: () => Formatter.keyValue([
1057
+ ["API Host", host],
1058
+ ["Active Org", activeOrg ?? "(none)"]
1059
+ ])
1060
+ };
1061
+ }
1062
+ function registerConfigCommand(program, deps) {
1063
+ const configCmd = program.command("config").description("Manage CLI configuration");
1064
+ configCmd.command("set-host <host>").description("Set API host (e.g. https://agent.everonet.com)").action(async (host, _options, command) => {
1065
+ const format = resolveFormat(command.optsWithGlobals().format);
1066
+ const result = await applyHost(deps, host, "set to", format);
1067
+ render(result, { format });
1068
+ });
1069
+ configCmd.command("show").description("Show current configuration").action(async (_options, command) => {
1070
+ const format = resolveFormat(command.optsWithGlobals().format);
1071
+ const config = await deps.configManager.load();
1072
+ const data = {
1073
+ api_host: config.api_host,
1074
+ api_path: config.api_path,
1075
+ active_org: config.active_org
1076
+ };
1077
+ const commandResult = {
1078
+ data,
1079
+ text: () => Formatter.keyValue([
1080
+ ["API Host", data.api_host],
1081
+ ["API Path", data.api_path],
1082
+ ["Active Org", data.active_org ?? "(none)"]
1083
+ ])
1084
+ };
1085
+ render(commandResult, { format });
1086
+ });
1087
+ configCmd.command("reset-host").description("Reset API host to default (https://agent.everonet.com)").action(async (_options, command) => {
1088
+ const format = resolveFormat(command.optsWithGlobals().format);
1089
+ const result = await applyHost(deps, DEFAULT_HOST, "reset to", format);
1090
+ render(result, { format });
1091
+ });
1092
+ }
1093
+
1094
+ // src/orgs/get.ts
1095
+ function registerMeCommand(parent, deps) {
1096
+ parent.command("get").description("View current organization").action(async (_options, command) => {
1097
+ const format = resolveFormat(command.optsWithGlobals().format);
1098
+ const result = await deps.authService.executeWithAuth(
1099
+ (token) => deps.apiClient.get(
1100
+ "/organizations/me",
1101
+ { type: "bearer", token }
1102
+ )
1103
+ );
1104
+ if (!result.success) {
1105
+ throw new ApiBusinessError(result.errorCode, result.errorMessage, result.statusCode);
1106
+ }
1107
+ const org = result.data;
1108
+ const commandResult = {
1109
+ data: org,
1110
+ text: () => Formatter.keyValue([
1111
+ ["Org ID", org.id],
1112
+ ["Name", org.name],
1113
+ ["Email", org.email],
1114
+ ["Status", org.status],
1115
+ ["Created", Formatter.formatTime(org.created_at)],
1116
+ ["Updated", Formatter.formatTime(org.updated_at)]
1117
+ ])
1118
+ };
1119
+ render(commandResult, { format });
1120
+ });
1121
+ }
1122
+
1123
+ // src/orgs/update.ts
1124
+ function registerUpdateCommand(parent, deps) {
1125
+ parent.command("update").description("Update current organization").option("--name <name>", "New organization name").option("--email <email>", "New email").option("--idempotency-key <key>", "Idempotency key forwarded as the Idempotency-Key header").action(async (options, command) => {
1126
+ const format = resolveFormat(command.optsWithGlobals().format);
1127
+ const body = {};
1128
+ if (options.name) body.name = options.name;
1129
+ if (options.email) body.email = options.email;
1130
+ let idempotencyKey = options.idempotencyKey;
1131
+ if (!idempotencyKey) {
1132
+ if (command.optsWithGlobals().yes) {
1133
+ throw new IdempotencyKeyRequiredError("orgs update");
1134
+ }
1135
+ idempotencyKey = await PromptEngine.resolveInput(void 0, {
1136
+ message: "Idempotency key (unique per write, for safe retry):",
1137
+ validate: (v) => v.trim().length > 0 || "Idempotency key is required"
1138
+ });
1139
+ }
1140
+ const extraHeaders = {
1141
+ "Idempotency-Key": String(idempotencyKey)
1142
+ };
1143
+ const result = await deps.authService.executeWithAuth(
1144
+ (token) => deps.apiClient.post(
1145
+ "/organizations/me/update",
1146
+ { type: "bearer", token },
1147
+ body,
1148
+ extraHeaders
1149
+ )
1150
+ );
1151
+ if (!result.success) {
1152
+ throw new ApiBusinessError(result.errorCode, result.errorMessage, result.statusCode);
1153
+ }
1154
+ const data = result.data;
1155
+ if (data.magic_link_token) {
1156
+ notify(
1157
+ format,
1158
+ "info",
1159
+ "Verification email sent to the new address. The email changes once verified."
1160
+ );
1161
+ const verifyResult = {
1162
+ data: {
1163
+ magic_link_token: data.magic_link_token,
1164
+ expires_at: data.expires_at
1165
+ },
1166
+ text: () => Formatter.keyValue([
1167
+ ["Status", "PENDING_EMAIL_VERIFICATION"],
1168
+ ["Magic Link Token", data.magic_link_token ?? ""],
1169
+ ["Expires At", Formatter.formatTime(data.expires_at)]
1170
+ ])
1171
+ };
1172
+ render(verifyResult, { format });
1173
+ return;
1174
+ }
1175
+ const org = data;
1176
+ const activeOrg = await deps.configManager.getActiveOrg();
1177
+ if (activeOrg) {
1178
+ const cred = await deps.credentialStore.get(activeOrg);
1179
+ if (cred && options.name) {
1180
+ cred.org_name = org.name;
1181
+ await deps.credentialStore.save(cred);
1182
+ }
1183
+ }
1184
+ notify(format, "success", "Organization updated");
1185
+ const commandResult = {
1186
+ data: org,
1187
+ text: () => Formatter.keyValue([
1188
+ ["Org ID", org.id],
1189
+ ["Name", org.name],
1190
+ ["Email", org.email],
1191
+ ["Status", org.status]
1192
+ ])
1193
+ };
1194
+ render(commandResult, { format });
1195
+ });
1196
+ }
1197
+
1198
+ // src/orgs/list.ts
1199
+ function registerListCommand(parent, deps) {
1200
+ parent.command("list").description("List all signed-in organizations").action(async (_options, command) => {
1201
+ const format = resolveFormat(command.optsWithGlobals().format);
1202
+ const credentials = await deps.credentialStore.listAll();
1203
+ const activeOrg = await deps.configManager.getActiveOrg();
1204
+ const currentHost = await deps.configManager.getApiHost();
1205
+ const data = credentials.filter((cred) => cred.api_host === currentHost).map((cred) => ({
1206
+ org_id: cred.org_id,
1207
+ org_name: cred.org_name,
1208
+ email: cred.email,
1209
+ active: cred.org_id === activeOrg
1210
+ }));
1211
+ const commandResult = {
1212
+ data,
1213
+ text: () => {
1214
+ if (data.length === 0) {
1215
+ return Formatter.status("info", "No signed-in organizations");
1216
+ }
1217
+ const headers = ["", "Org ID", "Org Name", "Email"];
1218
+ const rows = data.map((item) => [
1219
+ item.active ? "*" : "",
1220
+ item.org_id,
1221
+ item.org_name,
1222
+ item.email
1223
+ ]);
1224
+ return Formatter.table(headers, rows);
1225
+ }
1226
+ };
1227
+ render(commandResult, { format });
1228
+ });
1229
+ }
1230
+
1231
+ // src/orgs/switch.ts
1232
+ function registerSwitchCommand(parent, deps) {
1233
+ parent.command("switch <org_id>").description("Switch active organization").action(async (orgId, _options, command) => {
1234
+ const format = resolveFormat(command.optsWithGlobals().format);
1235
+ const credential = await deps.credentialStore.get(orgId);
1236
+ if (!credential) {
1237
+ throw new AuthError(
1238
+ `Organization ${orgId} not signed in locally`,
1239
+ "Please run agenzo-admin-cli auth login to sign in to this organization"
1240
+ );
1241
+ }
1242
+ const currentHost = await deps.configManager.getApiHost();
1243
+ if (credential.api_host && credential.api_host !== currentHost) {
1244
+ throw new ValidationError(
1245
+ `Organization ${orgId} belongs to a different environment (${credential.api_host})`
1246
+ );
1247
+ }
1248
+ await deps.configManager.setActiveOrg(orgId);
1249
+ notify(format, "success", `Switched to organization ${orgId}`);
1250
+ const commandResult = {
1251
+ data: { active_org: orgId },
1252
+ text: () => Formatter.status("success", `Switched to organization ${orgId}`)
1253
+ };
1254
+ render(commandResult, { format });
1255
+ });
1256
+ }
1257
+
1258
+ // src/developers/billing-mode.ts
1259
+ var VALID_BILLING_MODES = ["pay_per_call", "monthly_settlement"];
1260
+ var DEFAULT_BILLING_MODE = "pay_per_call";
1261
+ function isBillingMode(value) {
1262
+ return VALID_BILLING_MODES.includes(value);
1263
+ }
1264
+ function resolveBillingMode(flag) {
1265
+ if (flag === void 0) {
1266
+ return DEFAULT_BILLING_MODE;
1267
+ }
1268
+ const normalized = flag.trim().toLowerCase();
1269
+ if (!isBillingMode(normalized)) {
1270
+ throw new ValidationError(
1271
+ `Invalid --billing-mode: ${flag}. Allowed: ${VALID_BILLING_MODES.join(", ")}.`
1272
+ );
1273
+ }
1274
+ return normalized;
1275
+ }
1276
+
1277
+ // src/developers/create.ts
1278
+ function registerCreateCommand(parent, deps) {
1279
+ parent.command("create").description("Create a developer").option("--developer-name <name>", "Developer name").option("--developer-email <email>", "Developer email").option(
1280
+ "--billing-mode <mode>",
1281
+ "Billing mode: pay_per_call | monthly_settlement (default: pay_per_call)"
1282
+ ).option("--idempotency-key <key>", "Idempotency key forwarded as the Idempotency-Key header").action(async (options, command) => {
1283
+ const format = resolveFormat(command.optsWithGlobals().format);
1284
+ const name = await PromptEngine.resolveInput(options.developerName, {
1285
+ message: "Developer name:"
1286
+ });
1287
+ const email = await PromptEngine.resolveInput(options.developerEmail, {
1288
+ message: "Developer email:"
1289
+ });
1290
+ const billingMode = resolveBillingMode(options.billingMode);
1291
+ let idempotencyKey = options.idempotencyKey;
1292
+ if (!idempotencyKey) {
1293
+ if (command.optsWithGlobals().yes) {
1294
+ throw new IdempotencyKeyRequiredError("developers create");
1295
+ }
1296
+ idempotencyKey = await PromptEngine.resolveInput(void 0, {
1297
+ message: "Idempotency key (unique per write, for safe retry):",
1298
+ validate: (v) => v.trim().length > 0 || "Idempotency key is required"
1299
+ });
1300
+ }
1301
+ const extraHeaders = {
1302
+ "Idempotency-Key": String(idempotencyKey)
1303
+ };
1304
+ const result = await deps.authService.executeWithAuth(
1305
+ (token) => deps.apiClient.post(
1306
+ "/developers/create",
1307
+ { type: "bearer", token },
1308
+ { name, email, billing_mode: billingMode },
1309
+ extraHeaders
1310
+ )
1311
+ );
1312
+ if (!result.success) {
1313
+ throw new ApiBusinessError(result.errorCode, result.errorMessage, result.statusCode);
1314
+ }
1315
+ const dev = result.data;
1316
+ const idDisplay = String(
1317
+ dev.id ?? dev.developer_id ?? "-"
1318
+ );
1319
+ const commandResult = {
1320
+ data: dev,
1321
+ note: "Developer created",
1322
+ text: () => Formatter.keyValue([
1323
+ ["ID", idDisplay],
1324
+ ["Org ID", String(dev.organization_id ?? "-")],
1325
+ ["Name", String(dev.name ?? "-")],
1326
+ ["Email", String(dev.email ?? "-")],
1327
+ ["Status", String(dev.status ?? "-")],
1328
+ ["Billing Mode", String(dev.billing_mode ?? "-")]
1329
+ ])
1330
+ };
1331
+ if (commandResult.note) {
1332
+ notify(format, "success", commandResult.note);
1333
+ }
1334
+ render(commandResult, { format });
1335
+ });
1336
+ }
1337
+
1338
+ // src/developers/list.ts
1339
+ function registerListCommand2(parent, deps) {
1340
+ parent.command("list").description("List all developers").action(async (_options, command) => {
1341
+ const format = resolveFormat(command.optsWithGlobals().format);
1342
+ const result = await deps.authService.executeWithAuth(
1343
+ (token) => deps.apiClient.get(
1344
+ "/developers",
1345
+ { type: "bearer", token }
1346
+ )
1347
+ );
1348
+ if (!result.success) {
1349
+ throw new ApiBusinessError(result.errorCode, result.errorMessage, result.statusCode);
1350
+ }
1351
+ const developers = result.data;
1352
+ const commandResult = {
1353
+ data: developers,
1354
+ text: () => {
1355
+ if (developers.length === 0) {
1356
+ return Formatter.status("info", "No developers found");
1357
+ }
1358
+ const headers = ["ID", "Name", "Email", "Status"];
1359
+ const rows = developers.map((d) => [d.id, d.name, d.email, d.status]);
1360
+ return Formatter.table(headers, rows);
1361
+ }
1362
+ };
1363
+ render(commandResult, { format });
1364
+ });
1365
+ }
1366
+
1367
+ // src/developers/get.ts
1368
+ function registerGetCommand(parent, deps) {
1369
+ parent.command("get <developer_id>").description("View developer details").action(async (developerId, _options, command) => {
1370
+ const format = resolveFormat(command.optsWithGlobals().format);
1371
+ const result = await deps.authService.executeWithAuth(
1372
+ (token) => deps.apiClient.get(
1373
+ `/developers/${developerId}`,
1374
+ { type: "bearer", token }
1375
+ )
1376
+ );
1377
+ if (!result.success) {
1378
+ throw new ApiBusinessError(result.errorCode, result.errorMessage, result.statusCode);
1379
+ }
1380
+ const dev = result.data;
1381
+ const commandResult = {
1382
+ data: dev,
1383
+ text: () => Formatter.keyValue([
1384
+ ["ID", dev.id],
1385
+ ["Name", dev.name],
1386
+ ["Email", dev.email],
1387
+ ["Status", dev.status],
1388
+ ["Billing Mode", dev.billing_mode ?? "-"],
1389
+ ["Created", Formatter.formatTime(dev.created_at)],
1390
+ ["Updated", Formatter.formatTime(dev.updated_at)]
1391
+ ])
1392
+ };
1393
+ render(commandResult, { format });
1394
+ });
1395
+ }
1396
+
1397
+ // src/developers/update.ts
1398
+ function registerUpdateCommand2(parent, deps) {
1399
+ parent.command("update <developer_id>").description("Update developer info").option("--name <name>", "New name").option("--email <email>", "New email").option("--idempotency-key <key>", "Idempotency key forwarded as the Idempotency-Key header").action(async (developerId, options, command) => {
1400
+ const format = resolveFormat(command.optsWithGlobals().format);
1401
+ const body = {};
1402
+ if (options.name) body.name = options.name;
1403
+ if (options.email) body.email = options.email;
1404
+ let idempotencyKey = options.idempotencyKey;
1405
+ if (!idempotencyKey) {
1406
+ if (command.optsWithGlobals().yes) {
1407
+ throw new IdempotencyKeyRequiredError("developers update");
1408
+ }
1409
+ idempotencyKey = await PromptEngine.resolveInput(void 0, {
1410
+ message: "Idempotency key (unique per write, for safe retry):",
1411
+ validate: (v) => v.trim().length > 0 || "Idempotency key is required"
1412
+ });
1413
+ }
1414
+ const extraHeaders = {
1415
+ "Idempotency-Key": String(idempotencyKey)
1416
+ };
1417
+ const result = await deps.authService.executeWithAuth(
1418
+ (token) => deps.apiClient.post(
1419
+ `/developers/${developerId}/update`,
1420
+ { type: "bearer", token },
1421
+ body,
1422
+ extraHeaders
1423
+ )
1424
+ );
1425
+ if (!result.success) {
1426
+ throw new ApiBusinessError(result.errorCode, result.errorMessage, result.statusCode);
1427
+ }
1428
+ const dev = result.data;
1429
+ const commandResult = {
1430
+ data: dev,
1431
+ note: "Developer updated",
1432
+ text: () => Formatter.keyValue([
1433
+ ["ID", dev.id],
1434
+ ["Name", dev.name],
1435
+ ["Email", dev.email],
1436
+ ["Status", dev.status]
1437
+ ])
1438
+ };
1439
+ if (commandResult.note) {
1440
+ notify(format, "success", commandResult.note);
1441
+ }
1442
+ render(commandResult, { format });
1443
+ });
1444
+ }
1445
+
1446
+ // src/keys/scope.ts
1447
+ import { checkbox } from "@inquirer/prompts";
1448
+ var VALID_SCOPES = ["token", "merchant", "payment"];
1449
+ var DEFAULT_SCOPES = ["token", "merchant", "payment"];
1450
+ function isScope(value) {
1451
+ return VALID_SCOPES.includes(value);
1452
+ }
1453
+ function parseScopeFlag(raw) {
1454
+ const parts = raw.split(",").map((s) => s.trim().toLowerCase()).filter((s) => s.length > 0);
1455
+ if (parts.length === 0) {
1456
+ throw new ValidationError(
1457
+ `Invalid --scope: empty. Expected a comma-separated subset of ${VALID_SCOPES.join(", ")}.`
1458
+ );
1459
+ }
1460
+ const invalid = parts.filter((p) => !isScope(p));
1461
+ if (invalid.length > 0) {
1462
+ throw new ValidationError(
1463
+ `Invalid --scope value(s): ${invalid.join(", ")}. Allowed: ${VALID_SCOPES.join(", ")}.`
1464
+ );
1465
+ }
1466
+ return DEFAULT_SCOPES.filter((s) => parts.includes(s));
1467
+ }
1468
+ async function resolveScopes(flag, nonInteractive) {
1469
+ if (flag !== void 0) {
1470
+ return parseScopeFlag(flag);
1471
+ }
1472
+ if (nonInteractive) {
1473
+ return DEFAULT_SCOPES;
1474
+ }
1475
+ const selected = await checkbox({
1476
+ message: "Select scopes (which CLIs this key may call):",
1477
+ choices: DEFAULT_SCOPES.map((s) => ({ name: s, value: s, checked: true }))
1478
+ });
1479
+ return selected.length > 0 ? selected : DEFAULT_SCOPES;
1480
+ }
1481
+
1482
+ // src/keys/create.ts
1483
+ function registerCreateCommand2(parent, deps) {
1484
+ const cmd = parent.command("create").description("Create an API Key").option("--developer-id <developer_id>", "Developer ID (e.g. dev_01KPX...)").option("--key-name <key_name>", "Key name (e.g. Production Key)").option(
1485
+ "--scope <scope>",
1486
+ "Comma-separated CLI scopes: token,merchant,payment (default: all three)"
1487
+ ).option(
1488
+ "--idempotency-key <key>",
1489
+ "Idempotency key forwarded verbatim as the Idempotency-Key header"
1490
+ );
1491
+ cmd.action(async () => {
1492
+ const opts = cmd.optsWithGlobals();
1493
+ const format = resolveFormat(opts.format);
1494
+ const developerId = await PromptEngine.resolveInput(opts.developerId, {
1495
+ message: "Developer ID (e.g. dev_01KPX...):"
1496
+ });
1497
+ const name = await PromptEngine.resolveInput(opts.keyName, {
1498
+ message: "Key name (e.g. Production Key):"
1499
+ });
1500
+ const scope = await resolveScopes(
1501
+ opts.scope,
1502
+ Boolean(opts.yes)
1503
+ );
1504
+ let idempotencyKey = opts.idempotencyKey;
1505
+ if (!idempotencyKey) {
1506
+ if (opts.yes) {
1507
+ throw new IdempotencyKeyRequiredError("keys create");
1508
+ }
1509
+ idempotencyKey = await PromptEngine.resolveInput(void 0, {
1510
+ message: "Idempotency key (unique per write, for safe retry):",
1511
+ validate: (v) => v.trim().length > 0 || "Idempotency key is required"
1512
+ });
1513
+ }
1514
+ const extraHeaders = {
1515
+ "Idempotency-Key": idempotencyKey
1516
+ };
1517
+ const result = await deps.authService.executeWithAuth(
1518
+ (token) => deps.apiClient.post(
1519
+ "/keys/create",
1520
+ { type: "bearer", token },
1521
+ { developer_id: developerId, name, scope },
1522
+ extraHeaders
1523
+ )
1524
+ );
1525
+ if (!result.success) {
1526
+ throw new ApiBusinessError(result.errorCode, result.errorMessage, result.statusCode);
1527
+ }
1528
+ const key = result.data;
1529
+ if (!key.scope || key.scope.length === 0) {
1530
+ key.scope = scope;
1531
+ }
1532
+ const orgId = await deps.configManager.getActiveOrg();
1533
+ if (orgId && key.api_key) {
1534
+ await deps.keyStore.add(orgId, {
1535
+ key_id: key.id,
1536
+ developer_id: key.developer_id,
1537
+ name: key.name,
1538
+ key_value: key.api_key,
1539
+ created_at: key.created_at
1540
+ });
1541
+ }
1542
+ notify(format, "success", "API Key created");
1543
+ if (format === "table") {
1544
+ console.error(Formatter.status("warning", `API Key: ${key.api_key}`));
1545
+ console.error(
1546
+ Formatter.status("warning", "Save it now \u2014 this key is shown only once")
1547
+ );
1548
+ }
1549
+ const cmdResult = {
1550
+ data: key,
1551
+ text: () => Formatter.keyValue([
1552
+ ["Name", key.name],
1553
+ ["Scope", (key.scope ?? []).join(", ")],
1554
+ ["Status", key.status]
1555
+ ])
1556
+ };
1557
+ render(cmdResult, { format });
1558
+ });
1559
+ }
1560
+
1561
+ // src/keys/list.ts
1562
+ function toMetadata(key) {
1563
+ const { api_key: _api_key, ...metadata } = key;
1564
+ return metadata;
1565
+ }
1566
+ function registerListCommand3(parent, deps) {
1567
+ const cmd = parent.command("list").description("List API Keys").option("--developer-id <developer_id>", "Developer ID (e.g. dev_01KPX...)");
1568
+ cmd.action(async () => {
1569
+ const opts = cmd.optsWithGlobals();
1570
+ const format = resolveFormat(opts.format);
1571
+ const developerId = await PromptEngine.resolveInput(opts.developerId, {
1572
+ message: "Developer ID (e.g. dev_01KPX...):"
1573
+ });
1574
+ const result = await deps.authService.executeWithAuth(
1575
+ (token) => deps.apiClient.get(
1576
+ "/keys",
1577
+ { type: "bearer", token },
1578
+ { developer_id: developerId }
1579
+ )
1580
+ );
1581
+ if (!result.success) {
1582
+ throw new ApiBusinessError(result.errorCode, result.errorMessage, result.statusCode);
1583
+ }
1584
+ const keys = result.data.map(toMetadata);
1585
+ const cmdResult = {
1586
+ data: keys,
1587
+ text: () => {
1588
+ if (keys.length === 0) {
1589
+ return Formatter.status("info", "No API Keys found");
1590
+ }
1591
+ const headers = ["ID", "Developer", "Name", "Scope", "Status", "Last Used"];
1592
+ const rows = keys.map((k) => [
1593
+ k.id,
1594
+ k.developer_id,
1595
+ k.name,
1596
+ (k.scope ?? []).join(","),
1597
+ k.status,
1598
+ k.last_used_at ? Formatter.formatTime(k.last_used_at) : "Never"
1599
+ ]);
1600
+ return Formatter.table(headers, rows);
1601
+ }
1602
+ };
1603
+ render(cmdResult, { format });
1604
+ });
1605
+ }
1606
+
1607
+ // src/keys/get.ts
1608
+ function toMetadata2(key) {
1609
+ const { api_key: _api_key, ...metadata } = key;
1610
+ return metadata;
1611
+ }
1612
+ function registerGetCommand2(parent, deps) {
1613
+ const cmd = parent.command("get <key_id>").description("View API Key details");
1614
+ cmd.action(async (keyId) => {
1615
+ const opts = cmd.optsWithGlobals();
1616
+ const format = resolveFormat(opts.format);
1617
+ const result = await deps.authService.executeWithAuth(
1618
+ (token) => deps.apiClient.get(
1619
+ `/keys/${keyId}`,
1620
+ { type: "bearer", token }
1621
+ )
1622
+ );
1623
+ if (!result.success) {
1624
+ throw new ApiBusinessError(result.errorCode, result.errorMessage, result.statusCode);
1625
+ }
1626
+ const k = toMetadata2(result.data);
1627
+ const cmdResult = {
1628
+ data: k,
1629
+ text: () => Formatter.keyValue([
1630
+ ["Key ID", k.id],
1631
+ ["Developer ID", k.developer_id],
1632
+ ["Name", k.name],
1633
+ ["Scope", (k.scope ?? []).join(", ")],
1634
+ ["Status", k.status],
1635
+ ["Last Used", k.last_used_at ? Formatter.formatTime(k.last_used_at) : "Never"],
1636
+ ["Created", Formatter.formatTime(k.created_at)]
1637
+ ])
1638
+ };
1639
+ render(cmdResult, { format });
1640
+ });
1641
+ }
1642
+
1643
+ // src/keys/rotate.ts
1644
+ function registerRotateCommand(parent, deps) {
1645
+ const cmd = parent.command("rotate <key_id>").description("Rotate API Key").option(
1646
+ "--idempotency-key <key>",
1647
+ "Idempotency key forwarded verbatim as the Idempotency-Key header"
1648
+ );
1649
+ cmd.action(async (keyId) => {
1650
+ const opts = cmd.optsWithGlobals();
1651
+ const format = resolveFormat(opts.format);
1652
+ let idempotencyKey = opts.idempotencyKey;
1653
+ if (!idempotencyKey) {
1654
+ if (opts.yes) {
1655
+ throw new IdempotencyKeyRequiredError("keys rotate");
1656
+ }
1657
+ idempotencyKey = await PromptEngine.resolveInput(void 0, {
1658
+ message: "Idempotency key (unique per write, for safe retry):",
1659
+ validate: (v) => v.trim().length > 0 || "Idempotency key is required"
1660
+ });
1661
+ }
1662
+ const extraHeaders = {
1663
+ "Idempotency-Key": idempotencyKey
1664
+ };
1665
+ const result = await deps.authService.executeWithAuth(
1666
+ (token) => deps.apiClient.post(
1667
+ `/keys/${keyId}/rotate`,
1668
+ { type: "bearer", token },
1669
+ void 0,
1670
+ extraHeaders
1671
+ )
1672
+ );
1673
+ if (!result.success) {
1674
+ throw new ApiBusinessError(result.errorCode, result.errorMessage, result.statusCode);
1675
+ }
1676
+ const key = result.data;
1677
+ const orgId = await deps.configManager.getActiveOrg();
1678
+ if (orgId && key.api_key) {
1679
+ await deps.keyStore.update(orgId, keyId, key.api_key);
1680
+ }
1681
+ notify(format, "success", "API Key rotated");
1682
+ if (format === "table") {
1683
+ console.error(Formatter.status("warning", `New API Key: ${key.api_key}`));
1684
+ console.error(
1685
+ Formatter.status("warning", "Save it now \u2014 this key is shown only once")
1686
+ );
1687
+ }
1688
+ const cmdResult = {
1689
+ data: key,
1690
+ text: () => Formatter.keyValue([
1691
+ ["Key ID", key.id],
1692
+ ["Name", key.name],
1693
+ ["Scope", (key.scope ?? []).join(", ")],
1694
+ ["Status", key.status]
1695
+ ])
1696
+ };
1697
+ render(cmdResult, { format });
1698
+ });
1699
+ }
1700
+
1701
+ // src/keys/disable.ts
1702
+ function registerDisableCommand(parent, deps) {
1703
+ const cmd = parent.command("disable <key_id>").description("Disable API Key").option(
1704
+ "--idempotency-key <key>",
1705
+ "Idempotency key forwarded verbatim as the Idempotency-Key header"
1706
+ );
1707
+ cmd.action(async (keyId) => {
1708
+ const opts = cmd.optsWithGlobals();
1709
+ const format = resolveFormat(opts.format);
1710
+ let idempotencyKey = opts.idempotencyKey;
1711
+ if (!idempotencyKey) {
1712
+ if (opts.yes) {
1713
+ throw new IdempotencyKeyRequiredError("keys disable");
1714
+ }
1715
+ idempotencyKey = await PromptEngine.resolveInput(void 0, {
1716
+ message: "Idempotency key (unique per write, for safe retry):",
1717
+ validate: (v) => v.trim().length > 0 || "Idempotency key is required"
1718
+ });
1719
+ }
1720
+ const extraHeaders = {
1721
+ "Idempotency-Key": idempotencyKey
1722
+ };
1723
+ const result = await deps.authService.executeWithAuth(
1724
+ (token) => deps.apiClient.post(
1725
+ `/keys/${keyId}/disable`,
1726
+ { type: "bearer", token },
1727
+ void 0,
1728
+ extraHeaders
1729
+ )
1730
+ );
1731
+ if (!result.success) {
1732
+ throw new ApiBusinessError(result.errorCode, result.errorMessage, result.statusCode);
1733
+ }
1734
+ const disableResult = result.data;
1735
+ notify(format, "success", `API Key ${keyId} disabled`);
1736
+ const cmdResult = {
1737
+ data: disableResult,
1738
+ text: () => Formatter.keyValue([
1739
+ ["Status", disableResult.status]
1740
+ ])
1741
+ };
1742
+ render(cmdResult, { format });
1743
+ });
1744
+ }
1745
+
1746
+ // src/accounts/get.ts
1747
+ function registerGetCommand3(parent, deps) {
1748
+ parent.command("get").description("Query a developer's settlement account").option("--developer-id <id>", "Developer ID to query").action(async (options, command) => {
1749
+ const format = resolveFormat(command.optsWithGlobals().format);
1750
+ const developerId = await PromptEngine.resolveInput(options.developerId, {
1751
+ message: "Developer ID (e.g. dev_01HZ...):"
1752
+ });
1753
+ const result = await deps.authService.executeWithAuth(
1754
+ (token) => deps.apiClient.get(
1755
+ "/accounts",
1756
+ { type: "bearer", token },
1757
+ { developer_id: developerId }
1758
+ )
1759
+ );
1760
+ if (!result.success) {
1761
+ throw new ApiBusinessError(result.errorCode, result.errorMessage, result.statusCode);
1762
+ }
1763
+ const account = result.data;
1764
+ if (!account) {
1765
+ notify(
1766
+ format,
1767
+ "info",
1768
+ "No settlement account found for this developer. Complete offline contract signing first."
1769
+ );
1770
+ render({ data: null, text: () => "" }, { format });
1771
+ return;
1772
+ }
1773
+ const commandResult = {
1774
+ data: account,
1775
+ text: () => Formatter.keyValue([
1776
+ ["Account ID", account.id],
1777
+ ["Developer ID", account.developer_id],
1778
+ ["Balance", String(account.balance)],
1779
+ ["Currency", account.currency],
1780
+ ["Status", account.status],
1781
+ ["Created", Formatter.formatTime(account.created_at)],
1782
+ ["Updated", Formatter.formatTime(account.updated_at)]
1783
+ ])
1784
+ };
1785
+ render(commandResult, { format });
1786
+ });
1787
+ }
1788
+
1789
+ // src/index.ts
1790
+ var programRef;
1791
+ async function main() {
1792
+ const configManager = new ConfigManager();
1793
+ await configManager.ensureDirectories();
1794
+ const credentialStore = new CredentialStore();
1795
+ const keyStore = new KeyStore();
1796
+ const apiBaseUrl = await configManager.getApiBaseUrl();
1797
+ const apiClient = new ApiClient({ baseUrl: apiBaseUrl });
1798
+ const authService = new AuthService(apiClient, credentialStore, configManager);
1799
+ const controlPlaneDeps = { apiClient, authService, credentialStore, configManager };
1800
+ const keysDeps = { apiClient, authService, keyStore, configManager };
1801
+ const orgsDeps = { credentialStore, configManager };
1802
+ const program = new Command();
1803
+ programRef = program;
1804
+ program.name("agenzo-admin-cli").version(getCurrentVersion()).description(
1805
+ "Agenzo control plane: login, organizations, developers, API keys, settlement accounts, and config"
1806
+ ).option("--verbose", "Show verbose logs").option("--yes", "Skip confirmation prompts (for automation/AI Agents)").option(
1807
+ "--format <format>",
1808
+ "Output format: json | table (default: table; or set AGENZO_FORMAT)"
1809
+ );
1810
+ program.hook("preAction", (thisCommand) => {
1811
+ const flag = thisCommand.opts().format;
1812
+ process.env.AGENZO_FORMAT = resolveFormat(flag);
1813
+ });
1814
+ const authCmd = program.command("auth").description("Authentication");
1815
+ registerLoginCommand(authCmd, { authService });
1816
+ registerLogoutCommand(authCmd, { authService });
1817
+ registerConfigCommand(program, { configManager, credentialStore });
1818
+ const orgsCmd = program.command("orgs").description("Organization management");
1819
+ registerMeCommand(orgsCmd, controlPlaneDeps);
1820
+ registerUpdateCommand(orgsCmd, controlPlaneDeps);
1821
+ registerListCommand(orgsCmd, orgsDeps);
1822
+ registerSwitchCommand(orgsCmd, orgsDeps);
1823
+ const devsCmd = program.command("developers").description("Developer management");
1824
+ registerCreateCommand(devsCmd, controlPlaneDeps);
1825
+ registerListCommand2(devsCmd, controlPlaneDeps);
1826
+ registerGetCommand(devsCmd, controlPlaneDeps);
1827
+ registerUpdateCommand2(devsCmd, controlPlaneDeps);
1828
+ const keysCmd = program.command("keys").description("API Key management");
1829
+ registerCreateCommand2(keysCmd, keysDeps);
1830
+ registerListCommand3(keysCmd, controlPlaneDeps);
1831
+ registerGetCommand2(keysCmd, controlPlaneDeps);
1832
+ registerRotateCommand(keysCmd, keysDeps);
1833
+ registerDisableCommand(keysCmd, controlPlaneDeps);
1834
+ const accountsCmd = program.command("accounts").description("Settlement account management");
1835
+ registerGetCommand3(accountsCmd, controlPlaneDeps);
1836
+ await program.parseAsync(process.argv);
1837
+ }
1838
+ function resolveActiveFormat() {
1839
+ const flag = programRef?.opts().format;
1840
+ return resolveFormat(flag);
1841
+ }
1842
+ function reportError(error) {
1843
+ const envelope = toErrorEnvelope(error);
1844
+ const format = resolveActiveFormat();
1845
+ if (format === "json") {
1846
+ console.error(JSON.stringify(envelope));
1847
+ } else {
1848
+ console.error(Formatter.status("error", envelope.error.message));
1849
+ if (error instanceof AuthError) {
1850
+ console.error(Formatter.status("info", error.suggestion));
1851
+ }
1852
+ if (!(error instanceof CliError) && process.argv.includes("--verbose")) {
1853
+ console.error(error);
1854
+ }
1855
+ }
1856
+ process.exit(exitCodeFor(error));
1857
+ }
1858
+ process.on("SIGINT", () => {
1859
+ reportError(new UserCancelError());
1860
+ });
1861
+ main().catch(reportError);
1862
+ //# sourceMappingURL=index.js.map