@getrouter/getrouter-cli 0.1.13 โ†’ 0.1.14

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/bin.mjs CHANGED
@@ -8,7 +8,7 @@ import { randomInt } from "node:crypto";
8
8
  import prompts from "prompts";
9
9
 
10
10
  //#region package.json
11
- var version = "0.1.12";
11
+ var version = "0.1.13";
12
12
 
13
13
  //#endregion
14
14
  //#region src/generated/router/dashboard/v1/index.ts
@@ -204,15 +204,15 @@ function createAuthServiceClient(handler) {
204
204
 
205
205
  //#endregion
206
206
  //#region src/core/config/fs.ts
207
- const getCorruptBackupPath = (filePath) => {
207
+ function getCorruptBackupPath(filePath) {
208
208
  const dir = path.dirname(filePath);
209
209
  const ext = path.extname(filePath);
210
210
  const base = path.basename(filePath, ext);
211
211
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
212
212
  const rand = Math.random().toString(16).slice(2, 8);
213
213
  return path.join(dir, `${base}.corrupt-${stamp}-${rand}${ext}`);
214
- };
215
- const readJsonFile = (filePath) => {
214
+ }
215
+ function readJsonFile(filePath) {
216
216
  if (!fs.existsSync(filePath)) return null;
217
217
  let raw;
218
218
  try {
@@ -233,62 +233,101 @@ const readJsonFile = (filePath) => {
233
233
  }
234
234
  return null;
235
235
  }
236
- };
237
- const writeJsonFile = (filePath, value) => {
236
+ }
237
+ function writeJsonFile(filePath, value) {
238
238
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
239
239
  fs.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf8");
240
- };
240
+ }
241
241
 
242
242
  //#endregion
243
243
  //#region src/core/config/paths.ts
244
- const resolveConfigDir = () => process.env.GETROUTER_CONFIG_DIR || path.join(os.homedir(), ".getrouter");
245
- const getConfigPath = () => path.join(resolveConfigDir(), "config.json");
246
- const getAuthPath = () => path.join(resolveConfigDir(), "auth.json");
244
+ function resolveConfigDir() {
245
+ return process.env.GETROUTER_CONFIG_DIR || path.join(os.homedir(), ".getrouter");
246
+ }
247
+ function getConfigPath() {
248
+ return path.join(resolveConfigDir(), "config.json");
249
+ }
250
+ function getAuthPath() {
251
+ return path.join(resolveConfigDir(), "auth.json");
252
+ }
247
253
 
248
254
  //#endregion
249
255
  //#region src/core/config/types.ts
250
- const defaultConfig = () => ({
251
- apiBase: "https://getrouter.dev",
252
- json: false
253
- });
254
- const defaultAuthState = () => ({
255
- accessToken: "",
256
- refreshToken: "",
257
- expiresAt: "",
258
- tokenType: "Bearer"
259
- });
256
+ function defaultConfig() {
257
+ return {
258
+ apiBase: "https://getrouter.dev",
259
+ json: false
260
+ };
261
+ }
262
+ function defaultAuthState() {
263
+ return {
264
+ accessToken: "",
265
+ refreshToken: "",
266
+ expiresAt: "",
267
+ tokenType: "Bearer"
268
+ };
269
+ }
260
270
 
261
271
  //#endregion
262
272
  //#region src/core/config/index.ts
263
- const readConfig = () => ({
264
- ...defaultConfig(),
265
- ...readJsonFile(getConfigPath()) ?? {}
266
- });
267
- const readAuth = () => ({
268
- ...defaultAuthState(),
269
- ...readJsonFile(getAuthPath()) ?? {}
270
- });
271
- const writeAuth = (auth) => {
273
+ function readConfig() {
274
+ return {
275
+ ...defaultConfig(),
276
+ ...readJsonFile(getConfigPath()) ?? {}
277
+ };
278
+ }
279
+ function readAuth() {
280
+ return {
281
+ ...defaultAuthState(),
282
+ ...readJsonFile(getAuthPath()) ?? {}
283
+ };
284
+ }
285
+ function writeAuth(auth) {
272
286
  const authPath = getAuthPath();
273
287
  writeJsonFile(authPath, auth);
274
288
  if (process.platform !== "win32") fs.chmodSync(authPath, 384);
275
- };
289
+ }
276
290
 
277
291
  //#endregion
278
292
  //#region src/core/http/url.ts
279
- const getApiBase = () => {
293
+ function getApiBase() {
280
294
  return (readConfig().apiBase || "").replace(/\/+$/, "");
281
- };
282
- const buildApiUrl = (path$1) => {
295
+ }
296
+ function buildApiUrl(path$1) {
283
297
  const base = getApiBase();
284
- const normalized = path$1.replace(/^\/+/, "");
285
- return base ? `${base}/${normalized}` : `/${normalized}`;
286
- };
298
+ const normalizedPath = path$1.replace(/^\/+/, "");
299
+ if (base) return `${base}/${normalizedPath}`;
300
+ return `/${normalizedPath}`;
301
+ }
302
+
303
+ //#endregion
304
+ //#region src/core/auth/index.ts
305
+ function isTokenExpired(expiresAt, bufferMs = 0) {
306
+ if (!expiresAt) return true;
307
+ const timestampMs = Date.parse(expiresAt);
308
+ if (Number.isNaN(timestampMs)) return true;
309
+ return timestampMs <= Date.now() + bufferMs;
310
+ }
311
+ function getAuthStatus() {
312
+ const auth = readAuth();
313
+ if (!Boolean(auth.accessToken && auth.refreshToken) || isTokenExpired(auth.expiresAt)) return { status: "logged_out" };
314
+ return {
315
+ status: "logged_in",
316
+ expiresAt: auth.expiresAt,
317
+ accessToken: auth.accessToken,
318
+ refreshToken: auth.refreshToken,
319
+ tokenType: auth.tokenType
320
+ };
321
+ }
322
+ function clearAuth() {
323
+ writeAuth(defaultAuthState());
324
+ }
287
325
 
288
326
  //#endregion
289
327
  //#region src/core/auth/refresh.ts
290
328
  const EXPIRY_BUFFER_MS = 60 * 1e3;
291
- const refreshAccessToken = async ({ fetchImpl }) => {
329
+ async function refreshAccessToken(options) {
330
+ const { fetchImpl } = options;
292
331
  const auth = readAuth();
293
332
  if (!auth.refreshToken) return null;
294
333
  const res = await (fetchImpl ?? fetch)(buildApiUrl("v1/dashboard/auth/token"), {
@@ -305,32 +344,44 @@ const refreshAccessToken = async ({ fetchImpl }) => {
305
344
  tokenType: "Bearer"
306
345
  });
307
346
  return token;
308
- };
347
+ }
309
348
 
310
349
  //#endregion
311
350
  //#region src/core/http/errors.ts
312
- const createApiError = (payload, fallbackMessage, status) => {
351
+ function createApiError(payload, fallbackMessage, status) {
313
352
  const payloadObject = payload && typeof payload === "object" ? payload : void 0;
314
- const message = payloadObject && typeof payloadObject.message === "string" ? payloadObject.message : fallbackMessage;
353
+ const message = typeof payloadObject?.message === "string" ? payloadObject.message : fallbackMessage;
315
354
  const err = new Error(message);
316
- if (payloadObject && typeof payloadObject.code === "string") err.code = payloadObject.code;
317
- if (payloadObject && payloadObject.details != null) err.details = payloadObject.details;
355
+ const code = payloadObject?.code;
356
+ if (typeof code === "string") err.code = code;
357
+ const details = payloadObject?.details;
358
+ if (details != null) err.details = details;
318
359
  if (typeof status === "number") err.status = status;
319
360
  return err;
320
- };
361
+ }
321
362
 
322
363
  //#endregion
323
364
  //#region src/core/http/retry.ts
324
- const defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
325
- const isRetryableError = (error) => {
365
+ function defaultSleep(ms) {
366
+ return new Promise((resolve) => setTimeout(resolve, ms));
367
+ }
368
+ function isServerError(status) {
369
+ return status >= 500 || status === 408 || status === 429;
370
+ }
371
+ function getErrorStatus(error) {
372
+ if (typeof error !== "object" || error === null) return;
373
+ if (!("status" in error)) return;
374
+ const status = error.status;
375
+ if (typeof status !== "number") return;
376
+ return status;
377
+ }
378
+ function isRetryableError(error, _attempt) {
326
379
  if (error instanceof TypeError) return true;
327
- if (typeof error === "object" && error !== null && "status" in error && typeof error.status === "number") {
328
- const status = error.status;
329
- return status >= 500 || status === 408 || status === 429;
330
- }
331
- return false;
332
- };
333
- const withRetry = async (fn, options = {}) => {
380
+ const status = getErrorStatus(error);
381
+ if (status === void 0) return false;
382
+ return isServerError(status);
383
+ }
384
+ async function withRetry(fn, options = {}) {
334
385
  const { maxRetries = 3, initialDelayMs = 1e3, maxDelayMs = 1e4, shouldRetry = isRetryableError, onRetry, sleep = defaultSleep } = options;
335
386
  let lastError;
336
387
  let delay = initialDelayMs;
@@ -344,40 +395,38 @@ const withRetry = async (fn, options = {}) => {
344
395
  delay = Math.min(delay * 2, maxDelayMs);
345
396
  }
346
397
  throw lastError;
347
- };
348
- const isServerError = (status) => status >= 500 || status === 408 || status === 429;
398
+ }
349
399
 
350
400
  //#endregion
351
401
  //#region src/core/http/request.ts
352
- const getAuthCookieName = () => process.env.GETROUTER_AUTH_COOKIE || process.env.KRATOS_AUTH_COOKIE || "access_token";
353
- const buildHeaders = (accessToken) => {
402
+ function getAuthCookieName() {
403
+ const routerCookieName = process.env.GETROUTER_AUTH_COOKIE;
404
+ if (routerCookieName) return routerCookieName;
405
+ const kratosCookieName = process.env.KRATOS_AUTH_COOKIE;
406
+ if (kratosCookieName) return kratosCookieName;
407
+ return "access_token";
408
+ }
409
+ function buildHeaders(accessToken) {
354
410
  const headers = { "Content-Type": "application/json" };
355
411
  if (accessToken) {
356
412
  headers.Authorization = `Bearer ${accessToken}`;
357
413
  headers.Cookie = `${getAuthCookieName()}=${accessToken}`;
358
414
  }
359
415
  return headers;
360
- };
361
- const doFetch = async (url, method, headers, body, fetchImpl) => {
416
+ }
417
+ async function doFetch(url, method, headers, body, fetchImpl) {
362
418
  return (fetchImpl ?? fetch)(url, {
363
419
  method,
364
420
  headers,
365
421
  body: body == null ? void 0 : JSON.stringify(body)
366
422
  });
367
- };
368
- const shouldRetryResponse = (error) => {
369
- if (typeof error === "object" && error !== null && "status" in error && typeof error.status === "number") return isServerError(error.status);
370
- return error instanceof TypeError;
371
- };
372
- const requestJson = async ({ path: path$1, method, body, fetchImpl, maxRetries = 3, includeAuth = true, _retrySleep }) => {
423
+ }
424
+ async function requestJson({ path: path$1, method, body, fetchImpl, maxRetries = 3, includeAuth = true, _retrySleep }) {
373
425
  return withRetry(async () => {
374
- const auth = includeAuth ? readAuth() : {
375
- accessToken: void 0,
376
- refreshToken: void 0
377
- };
378
426
  const url = buildApiUrl(path$1);
379
- let res = await doFetch(url, method, includeAuth ? buildHeaders(auth.accessToken) : buildHeaders(), body, fetchImpl);
380
- if (includeAuth && res.status === 401 && auth.refreshToken) {
427
+ const auth = includeAuth ? readAuth() : void 0;
428
+ let res = await doFetch(url, method, buildHeaders(auth?.accessToken), body, fetchImpl);
429
+ if (includeAuth && res.status === 401 && auth?.refreshToken) {
381
430
  const refreshed = await refreshAccessToken({ fetchImpl });
382
431
  if (refreshed?.accessToken) res = await doFetch(url, method, buildHeaders(refreshed.accessToken), body, fetchImpl);
383
432
  }
@@ -385,21 +434,21 @@ const requestJson = async ({ path: path$1, method, body, fetchImpl, maxRetries =
385
434
  return await res.json();
386
435
  }, {
387
436
  maxRetries,
388
- shouldRetry: shouldRetryResponse,
437
+ shouldRetry: isRetryableError,
389
438
  sleep: _retrySleep
390
439
  });
391
- };
440
+ }
392
441
 
393
442
  //#endregion
394
443
  //#region src/core/api/client.ts
395
- const createApiClients = ({ fetchImpl, clients, includeAuth = true }) => {
396
- const factories = clients ?? {
397
- createConsumerServiceClient,
398
- createAuthServiceClient,
399
- createSubscriptionServiceClient,
400
- createUsageServiceClient,
401
- createModelServiceClient
402
- };
444
+ const defaultFactories = {
445
+ createConsumerServiceClient,
446
+ createAuthServiceClient,
447
+ createSubscriptionServiceClient,
448
+ createUsageServiceClient,
449
+ createModelServiceClient
450
+ };
451
+ function createApiClients({ fetchImpl, clients: factories = defaultFactories, includeAuth = true } = {}) {
403
452
  const handler = async ({ path: path$1, method, body }) => {
404
453
  return requestJson({
405
454
  path: path$1,
@@ -416,30 +465,7 @@ const createApiClients = ({ fetchImpl, clients, includeAuth = true }) => {
416
465
  subscriptionService: factories.createSubscriptionServiceClient(handler),
417
466
  usageService: factories.createUsageServiceClient(handler)
418
467
  };
419
- };
420
-
421
- //#endregion
422
- //#region src/core/auth/index.ts
423
- const isExpired = (expiresAt) => {
424
- if (!expiresAt) return true;
425
- const t = Date.parse(expiresAt);
426
- if (Number.isNaN(t)) return true;
427
- return t <= Date.now();
428
- };
429
- const getAuthStatus = () => {
430
- const auth = readAuth();
431
- if (!Boolean(auth.accessToken && auth.refreshToken) || isExpired(auth.expiresAt)) return { status: "logged_out" };
432
- return {
433
- status: "logged_in",
434
- expiresAt: auth.expiresAt,
435
- accessToken: auth.accessToken,
436
- refreshToken: auth.refreshToken,
437
- tokenType: auth.tokenType
438
- };
439
- };
440
- const clearAuth = () => {
441
- writeAuth(defaultAuthState());
442
- };
468
+ }
443
469
 
444
470
  //#endregion
445
471
  //#region src/core/auth/device.ts
@@ -450,6 +476,11 @@ const generateAuthCode = () => {
450
476
  return out;
451
477
  };
452
478
  const buildLoginUrl = (authCode) => `https://getrouter.dev/auth/${authCode}`;
479
+ const getErrorCode = (err) => {
480
+ if (typeof err !== "object" || err === null) return void 0;
481
+ if (!("code" in err)) return void 0;
482
+ return err.code;
483
+ };
453
484
  const spawnBrowser = (command, args) => {
454
485
  try {
455
486
  const child = spawn(command, args, {
@@ -457,8 +488,10 @@ const spawnBrowser = (command, args) => {
457
488
  detached: true
458
489
  });
459
490
  child.on("error", (err) => {
460
- const code = typeof err === "object" && err !== null && "code" in err ? err.code : void 0;
461
- const reason = code === "ENOENT" ? ` (${command} not found)` : code ? ` (${code})` : "";
491
+ const code = getErrorCode(err);
492
+ let reason = "";
493
+ if (code === "ENOENT") reason = ` (${command} not found)`;
494
+ else if (typeof code === "string") reason = ` (${code})`;
462
495
  console.log(`โš ๏ธ Unable to open browser${reason}. Please open the URL manually.`);
463
496
  });
464
497
  child.unref();
@@ -468,31 +501,42 @@ const spawnBrowser = (command, args) => {
468
501
  };
469
502
  const openLoginUrl = async (url) => {
470
503
  try {
471
- if (process.platform === "darwin") {
472
- spawnBrowser("open", [url]);
473
- return;
474
- }
475
- if (process.platform === "win32") {
476
- spawnBrowser("cmd", [
477
- "/c",
478
- "start",
479
- "",
480
- url
481
- ]);
482
- return;
483
- }
484
- spawnBrowser("xdg-open", [url]);
504
+ const entry = {
505
+ darwin: {
506
+ command: "open",
507
+ args: [url]
508
+ },
509
+ win32: {
510
+ command: "cmd",
511
+ args: [
512
+ "/c",
513
+ "start",
514
+ "",
515
+ url
516
+ ]
517
+ }
518
+ }[process.platform] ?? {
519
+ command: "xdg-open",
520
+ args: [url]
521
+ };
522
+ spawnBrowser(entry.command, entry.args);
485
523
  } catch {}
486
524
  };
487
525
  const pollAuthorize = async ({ authorize, code, timeoutMs = 300 * 1e3, initialDelayMs = 1e3, maxDelayMs = 1e4, sleep = (ms) => new Promise((r) => setTimeout(r, ms)), now = () => Date.now(), onRetry }) => {
488
526
  const start = now();
489
527
  let delay = initialDelayMs;
490
528
  let attempt = 0;
529
+ const getErrorStatus$1 = (err) => {
530
+ if (typeof err !== "object" || err === null) return void 0;
531
+ if (!("status" in err)) return void 0;
532
+ const status = err.status;
533
+ return typeof status === "number" ? status : void 0;
534
+ };
491
535
  while (true) {
492
536
  try {
493
537
  return await authorize({ code });
494
538
  } catch (err) {
495
- const status = typeof err === "object" && err !== null && "status" in err ? err.status : void 0;
539
+ const status = getErrorStatus$1(err);
496
540
  if (status === 404) {} else if (status === 400) throw new Error("Auth code already used. Please log in again.");
497
541
  else if (status === 403) throw new Error("Auth code expired. Please log in again.");
498
542
  else throw err;
@@ -507,7 +551,7 @@ const pollAuthorize = async ({ authorize, code, timeoutMs = 300 * 1e3, initialDe
507
551
 
508
552
  //#endregion
509
553
  //#region src/cmd/auth.ts
510
- const registerAuthCommands = (program) => {
554
+ function registerAuthCommands(program) {
511
555
  program.command("login").description("Login with device flow").action(async () => {
512
556
  const { authService } = createApiClients({ includeAuth: false });
513
557
  const authCode = generateAuthCode();
@@ -532,7 +576,7 @@ const registerAuthCommands = (program) => {
532
576
  clearAuth();
533
577
  console.log("Cleared local auth data.");
534
578
  });
535
- };
579
+ }
536
580
 
537
581
  //#endregion
538
582
  //#region src/core/api/pagination.ts
@@ -544,7 +588,7 @@ const registerAuthCommands = (program) => {
544
588
  * @param getNextToken - Function that extracts the next page token from the response
545
589
  * @returns Array of all items across all pages
546
590
  */
547
- const fetchAllPages = async (fetchPage, getItems, getNextToken) => {
591
+ async function fetchAllPages(fetchPage, getItems, getNextToken) {
548
592
  const allItems = [];
549
593
  let pageToken;
550
594
  do {
@@ -554,7 +598,7 @@ const fetchAllPages = async (fetchPage, getItems, getNextToken) => {
554
598
  pageToken = getNextToken(response);
555
599
  } while (pageToken);
556
600
  return allItems;
557
- };
601
+ }
558
602
 
559
603
  //#endregion
560
604
  //#region src/core/interactive/fuzzy.ts
@@ -657,21 +701,28 @@ const promptKeyEnabled = async (initial) => {
657
701
  enabled: typeof response.enabled === "boolean" ? response.enabled : initial
658
702
  };
659
703
  };
660
- const selectConsumer = async (consumerService) => {
661
- const consumers = await fetchAllPages((pageToken) => consumerService.ListConsumers({
662
- pageSize: void 0,
663
- pageToken
664
- }), (res) => res?.consumers ?? [], (res) => res?.nextPageToken || void 0);
704
+ const fetchConsumers = async (consumerService) => fetchAllPages((pageToken) => consumerService.ListConsumers({
705
+ pageSize: void 0,
706
+ pageToken
707
+ }), (res) => res?.consumers ?? [], (res) => res?.nextPageToken || void 0);
708
+ const ensureConsumers = (consumers) => {
665
709
  if (consumers.length === 0) {
666
710
  console.log("No available API keys. Create one at https://getrouter.dev/dashboard/keys");
667
711
  return null;
668
712
  }
669
713
  const sorted = sortConsumersByUpdatedAtDesc(consumers);
670
- const nameCounts = buildNameCounts(sorted);
714
+ return {
715
+ sorted,
716
+ nameCounts: buildNameCounts(sorted)
717
+ };
718
+ };
719
+ const selectConsumer = async (consumerService) => {
720
+ const prepared = ensureConsumers(await fetchConsumers(consumerService));
721
+ if (!prepared) return null;
671
722
  return await fuzzySelect({
672
723
  message: "๐Ÿ”Ž Search keys",
673
- choices: sorted.map((consumer) => ({
674
- title: formatChoice(consumer, nameCounts),
724
+ choices: prepared.sorted.map((consumer) => ({
725
+ title: formatChoice(consumer, prepared.nameCounts),
675
726
  value: consumer,
676
727
  keywords: [
677
728
  normalizeName(consumer),
@@ -682,22 +733,14 @@ const selectConsumer = async (consumerService) => {
682
733
  }) ?? null;
683
734
  };
684
735
  const selectConsumerList = async (consumerService, message) => {
685
- const consumers = await fetchAllPages((pageToken) => consumerService.ListConsumers({
686
- pageSize: void 0,
687
- pageToken
688
- }), (res) => res?.consumers ?? [], (res) => res?.nextPageToken || void 0);
689
- if (consumers.length === 0) {
690
- console.log("No available API keys. Create one at https://getrouter.dev/dashboard/keys");
691
- return null;
692
- }
693
- const sorted = sortConsumersByUpdatedAtDesc(consumers);
694
- const nameCounts = buildNameCounts(sorted);
736
+ const prepared = ensureConsumers(await fetchConsumers(consumerService));
737
+ if (!prepared) return null;
695
738
  const response = await prompts({
696
739
  type: "select",
697
740
  name: "value",
698
741
  message,
699
- choices: sorted.map((consumer) => ({
700
- title: formatChoice(consumer, nameCounts),
742
+ choices: prepared.sorted.map((consumer) => ({
743
+ title: formatChoice(consumer, prepared.nameCounts),
701
744
  value: consumer
702
745
  }))
703
746
  });
@@ -716,24 +759,30 @@ const confirmDelete = async (consumer) => {
716
759
 
717
760
  //#endregion
718
761
  //#region src/core/setup/env.ts
719
- const quoteEnvValue = (shell, value) => {
762
+ function quoteEnvValue(shell, value) {
720
763
  if (shell === "ps1") return `'${value.replaceAll("'", "''")}'`;
721
764
  return `'${value.replaceAll("'", "'\\''")}'`;
722
- };
723
- const renderLine = (shell, key, value) => {
765
+ }
766
+ function renderLine(shell, key, value) {
724
767
  if (shell === "ps1") return `$env:${key}=${quoteEnvValue(shell, value)}`;
725
768
  return `export ${key}=${quoteEnvValue(shell, value)}`;
726
- };
727
- const renderEnv = (shell, vars) => {
769
+ }
770
+ function renderEnv(shell, vars) {
771
+ const entries = [
772
+ ["openaiBaseUrl", "OPENAI_BASE_URL"],
773
+ ["openaiApiKey", "OPENAI_API_KEY"],
774
+ ["anthropicBaseUrl", "ANTHROPIC_BASE_URL"],
775
+ ["anthropicApiKey", "ANTHROPIC_API_KEY"]
776
+ ];
728
777
  const lines = [];
729
- if (vars.openaiBaseUrl) lines.push(renderLine(shell, "OPENAI_BASE_URL", vars.openaiBaseUrl));
730
- if (vars.openaiApiKey) lines.push(renderLine(shell, "OPENAI_API_KEY", vars.openaiApiKey));
731
- if (vars.anthropicBaseUrl) lines.push(renderLine(shell, "ANTHROPIC_BASE_URL", vars.anthropicBaseUrl));
732
- if (vars.anthropicApiKey) lines.push(renderLine(shell, "ANTHROPIC_API_KEY", vars.anthropicApiKey));
778
+ for (const [varKey, envKey] of entries) {
779
+ const value = vars[varKey];
780
+ if (value) lines.push(renderLine(shell, envKey, value));
781
+ }
733
782
  lines.push("");
734
783
  return lines.join("\n");
735
- };
736
- const renderHook = (shell) => {
784
+ }
785
+ function renderHook(shell) {
737
786
  if (shell === "pwsh") return [
738
787
  "function getrouter {",
739
788
  " $cmd = Get-Command getrouter -CommandType Application,ExternalScript -ErrorAction SilentlyContinue | Select-Object -First 1",
@@ -799,19 +848,21 @@ const renderHook = (shell) => {
799
848
  "}",
800
849
  ""
801
850
  ].join("\n");
802
- };
803
- const getEnvFilePath = (shell, configDir) => path.join(configDir, shell === "ps1" ? "env.ps1" : "env.sh");
804
- const getHookFilePath = (shell, configDir) => {
851
+ }
852
+ function getEnvFilePath(shell, configDir) {
853
+ return path.join(configDir, shell === "ps1" ? "env.ps1" : "env.sh");
854
+ }
855
+ function getHookFilePath(shell, configDir) {
805
856
  if (shell === "pwsh") return path.join(configDir, "hook.ps1");
806
857
  if (shell === "fish") return path.join(configDir, "hook.fish");
807
858
  return path.join(configDir, "hook.sh");
808
- };
809
- const writeEnvFile = (filePath, content) => {
859
+ }
860
+ function writeEnvFile(filePath, content) {
810
861
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
811
862
  fs.writeFileSync(filePath, content, "utf8");
812
863
  if (process.platform !== "win32") fs.chmodSync(filePath, 384);
813
- };
814
- const resolveShellRcPath = (shell, homeDir) => {
864
+ }
865
+ function resolveShellRcPath(shell, homeDir) {
815
866
  if (shell === "zsh") return path.join(homeDir, ".zshrc");
816
867
  if (shell === "bash") return path.join(homeDir, ".bashrc");
817
868
  if (shell === "fish") return path.join(homeDir, ".config/fish/config.fish");
@@ -820,9 +871,11 @@ const resolveShellRcPath = (shell, homeDir) => {
820
871
  return path.join(homeDir, ".config/powershell/Microsoft.PowerShell_profile.ps1");
821
872
  }
822
873
  return null;
823
- };
824
- const resolveEnvShell = (shell) => shell === "pwsh" ? "ps1" : "sh";
825
- const detectShell = () => {
874
+ }
875
+ function resolveEnvShell(shell) {
876
+ return shell === "pwsh" ? "ps1" : "sh";
877
+ }
878
+ function detectShell() {
826
879
  const shellPath = process.env.SHELL;
827
880
  if (shellPath) {
828
881
  const name = shellPath.split("/").pop()?.toLowerCase();
@@ -830,39 +883,41 @@ const detectShell = () => {
830
883
  }
831
884
  if (process.platform === "win32") return "pwsh";
832
885
  return "bash";
833
- };
834
- const applyEnvVars = (vars) => {
886
+ }
887
+ function applyEnvVars(vars) {
835
888
  if (vars.openaiBaseUrl) process.env.OPENAI_BASE_URL = vars.openaiBaseUrl;
836
889
  if (vars.openaiApiKey) process.env.OPENAI_API_KEY = vars.openaiApiKey;
837
890
  if (vars.anthropicBaseUrl) process.env.ANTHROPIC_BASE_URL = vars.anthropicBaseUrl;
838
891
  if (vars.anthropicApiKey) process.env.ANTHROPIC_API_KEY = vars.anthropicApiKey;
839
- };
840
- const formatSourceLine = (shell, envPath) => shell === "ps1" ? `. ${envPath}` : `source ${envPath}`;
841
- const trySourceEnv = (shell, envShell, envPath) => {
892
+ }
893
+ function formatSourceLine(shell, envPath) {
894
+ return shell === "ps1" ? `. ${envPath}` : `source ${envPath}`;
895
+ }
896
+ function trySourceEnv(shell, envShell, envPath) {
842
897
  try {
843
898
  if (envShell === "ps1") {
844
899
  execSync(`pwsh -NoProfile -Command ". '${envPath}'"`, { stdio: "ignore" });
845
900
  return;
846
901
  }
847
- execSync(`${shell} -c "${shell === "fish" ? "source" : "source"} '${envPath}'"`, { stdio: "ignore" });
902
+ execSync(`${shell} -c "source '${envPath}'"`, { stdio: "ignore" });
848
903
  } catch {}
849
- };
850
- const appendRcIfMissing = (rcPath, line) => {
904
+ }
905
+ function appendRcIfMissing(rcPath, line$1) {
851
906
  let content = "";
852
907
  if (fs.existsSync(rcPath)) {
853
908
  content = fs.readFileSync(rcPath, "utf8");
854
- if (content.includes(line)) return false;
909
+ if (content.includes(line$1)) return false;
855
910
  }
856
911
  const prefix = content && !content.endsWith("\n") ? "\n" : "";
857
912
  fs.mkdirSync(path.dirname(rcPath), { recursive: true });
858
- fs.writeFileSync(rcPath, `${content}${prefix}${line}\n`, "utf8");
913
+ fs.writeFileSync(rcPath, `${content}${prefix}${line$1}\n`, "utf8");
859
914
  return true;
860
- };
915
+ }
861
916
 
862
917
  //#endregion
863
918
  //#region src/cmd/env.ts
864
919
  const CLAUDE_BASE_URL = "https://api.getrouter.dev/claude";
865
- const registerEnvCommand = (program, config) => {
920
+ function registerEnvCommand(program, config) {
866
921
  program.command(config.name).description(config.description).option("--install", "Install into shell rc").action(async (options) => {
867
922
  if (!process.stdin.isTTY) throw new Error("Interactive mode required for key selection.");
868
923
  const shell = detectShell();
@@ -901,44 +956,46 @@ const registerEnvCommand = (program, config) => {
901
956
  console.log(sourceLine);
902
957
  }
903
958
  });
904
- };
905
- const buildAnthropicEnv = (apiKey) => ({
906
- anthropicBaseUrl: CLAUDE_BASE_URL,
907
- anthropicApiKey: apiKey
908
- });
959
+ }
960
+ function buildAnthropicEnv(apiKey) {
961
+ return {
962
+ anthropicBaseUrl: CLAUDE_BASE_URL,
963
+ anthropicApiKey: apiKey
964
+ };
965
+ }
909
966
 
910
967
  //#endregion
911
968
  //#region src/cmd/claude.ts
912
- const registerClaudeCommand = (program) => {
969
+ function registerClaudeCommand(program) {
913
970
  registerEnvCommand(program, {
914
971
  name: "claude",
915
972
  description: "Configure Claude environment",
916
973
  vars: buildAnthropicEnv
917
974
  });
918
- };
975
+ }
919
976
 
920
977
  //#endregion
921
978
  //#region src/core/api/providerModels.ts
922
- const asTrimmedString = (value) => {
979
+ function asTrimmedString(value) {
923
980
  if (typeof value !== "string") return null;
924
981
  const trimmed = value.trim();
925
982
  return trimmed.length > 0 ? trimmed : null;
926
- };
927
- const buildProviderModelsPath = (tag) => {
983
+ }
984
+ function buildProviderModelsPath(tag) {
928
985
  const query = new URLSearchParams();
929
986
  if (tag) query.set("tag", tag);
930
987
  const qs = query.toString();
931
988
  return `v1/dashboard/providers/models${qs ? `?${qs}` : ""}`;
932
- };
933
- const listProviderModels = async ({ tag, fetchImpl }) => {
989
+ }
990
+ async function listProviderModels(options) {
934
991
  const raw = (await requestJson({
935
- path: buildProviderModelsPath(tag),
992
+ path: buildProviderModelsPath(options.tag),
936
993
  method: "GET",
937
- fetchImpl,
994
+ fetchImpl: options.fetchImpl,
938
995
  maxRetries: 0
939
996
  }))?.models;
940
997
  return (Array.isArray(raw) ? raw : []).map(asTrimmedString).filter(Boolean);
941
- };
998
+ }
942
999
 
943
1000
  //#endregion
944
1001
  //#region src/core/interactive/codex.ts
@@ -1038,20 +1095,68 @@ const PROVIDER_KEYS = [
1038
1095
  "wire_api",
1039
1096
  "requires_openai_auth"
1040
1097
  ];
1041
- const rootValues = (input) => ({
1042
- model: `"${input.model}"`,
1043
- model_reasoning_effort: `"${input.reasoning}"`,
1044
- model_provider: `"${CODEX_PROVIDER}"`
1045
- });
1046
- const providerValues = () => ({
1047
- name: `"${CODEX_PROVIDER}"`,
1048
- base_url: `"${CODEX_BASE_URL}"`,
1049
- wire_api: `"responses"`,
1050
- requires_openai_auth: "true"
1051
- });
1052
- const matchHeader = (line) => line.match(/^\s*\[([^\]]+)\]\s*$/);
1053
- const matchKey = (line) => line.match(/^\s*([A-Za-z0-9_.-]+)\s*=/);
1054
- const parseTomlRhsValue = (rhs) => {
1098
+ function splitLines(content) {
1099
+ if (content.length === 0) return [];
1100
+ return content.split(/\r?\n/);
1101
+ }
1102
+ function isLegacyTomlRootMarker(key) {
1103
+ return LEGACY_TOML_ROOT_MARKERS.includes(key);
1104
+ }
1105
+ function rootValues(input) {
1106
+ return {
1107
+ model: `"${input.model}"`,
1108
+ model_reasoning_effort: `"${input.reasoning}"`,
1109
+ model_provider: `"${CODEX_PROVIDER}"`
1110
+ };
1111
+ }
1112
+ function providerValues() {
1113
+ return {
1114
+ name: `"${CODEX_PROVIDER}"`,
1115
+ base_url: `"${CODEX_BASE_URL}"`,
1116
+ wire_api: `"responses"`,
1117
+ requires_openai_auth: "true"
1118
+ };
1119
+ }
1120
+ const HEADER_RE = /^\s*\[([^\]]+)\]\s*$/;
1121
+ const KEY_RE = /^\s*([A-Za-z0-9_.-]+)\s*=/;
1122
+ function matchHeader(line$1) {
1123
+ return line$1.match(HEADER_RE);
1124
+ }
1125
+ function matchKey(line$1) {
1126
+ return line$1.match(KEY_RE);
1127
+ }
1128
+ function readKeyFromLine(line$1) {
1129
+ return matchKey(line$1)?.[1];
1130
+ }
1131
+ function readStringValue(data, key) {
1132
+ const value = data[key];
1133
+ return typeof value === "string" ? value : void 0;
1134
+ }
1135
+ function findSectionEnd(lines, startIndex) {
1136
+ for (let i = startIndex; i < lines.length; i += 1) {
1137
+ const line$1 = lines[i];
1138
+ if (line$1 !== void 0 && matchHeader(line$1)) return i;
1139
+ }
1140
+ return lines.length;
1141
+ }
1142
+ function upsertKeyLines(lines, startIndex, endIndex, keys, valueMap) {
1143
+ const found = /* @__PURE__ */ new Set();
1144
+ for (let i = startIndex; i < endIndex; i += 1) {
1145
+ const line$1 = lines[i];
1146
+ if (line$1 === void 0) continue;
1147
+ const keyMatch = matchKey(line$1);
1148
+ if (!keyMatch) continue;
1149
+ const key = keyMatch[1];
1150
+ if (!keys.includes(key)) continue;
1151
+ lines[i] = `${key} = ${valueMap[key]}`;
1152
+ found.add(key);
1153
+ }
1154
+ return found;
1155
+ }
1156
+ function missingKeyLines(keys, found, valueMap) {
1157
+ return keys.filter((key) => !found.has(key)).map((key) => `${key} = ${valueMap[key]}`);
1158
+ }
1159
+ function parseTomlRhsValue(rhs) {
1055
1160
  const trimmed = rhs.trim();
1056
1161
  if (!trimmed) return "";
1057
1162
  const first = trimmed[0];
@@ -1061,150 +1166,119 @@ const parseTomlRhsValue = (rhs) => {
1061
1166
  }
1062
1167
  const hashIndex = trimmed.indexOf("#");
1063
1168
  return (hashIndex === -1 ? trimmed : trimmed.slice(0, hashIndex)).trim();
1064
- };
1065
- const readRootValue = (lines, key) => {
1066
- for (const line of lines) {
1067
- if (matchHeader(line)) break;
1068
- if (matchKey(line)?.[1] === key) {
1069
- const parts = line.split("=");
1070
- parts.shift();
1071
- return parseTomlRhsValue(parts.join("="));
1072
- }
1169
+ }
1170
+ function readRootValue(lines, key) {
1171
+ for (const line$1 of lines) {
1172
+ if (matchHeader(line$1)) break;
1173
+ if (readKeyFromLine(line$1) === key) return parseTomlRhsValue(line$1.slice(line$1.indexOf("=") + 1));
1073
1174
  }
1074
- };
1075
- const readCodexTomlRootValues = (content) => {
1076
- const lines = content.length ? content.split(/\r?\n/) : [];
1175
+ }
1176
+ function readCodexTomlRootValues(content) {
1177
+ const lines = splitLines(content);
1077
1178
  return {
1078
1179
  model: readRootValue(lines, "model"),
1079
1180
  reasoning: readRootValue(lines, "model_reasoning_effort"),
1080
1181
  provider: readRootValue(lines, "model_provider")
1081
1182
  };
1082
- };
1083
- const normalizeTomlString = (value) => {
1183
+ }
1184
+ function normalizeTomlString(value) {
1084
1185
  if (!value) return "";
1085
1186
  const trimmed = value.trim();
1086
1187
  if (trimmed.startsWith("\"") && trimmed.endsWith("\"") || trimmed.startsWith("'") && trimmed.endsWith("'")) return trimmed.slice(1, -1).trim().toLowerCase();
1087
1188
  return trimmed.replace(/['"]/g, "").trim().toLowerCase();
1088
- };
1089
- const stripLegacyRootMarkers = (lines) => {
1189
+ }
1190
+ function stripLegacyRootMarkers(lines) {
1090
1191
  const updated = [];
1091
1192
  let inRoot = true;
1092
- for (const line of lines) {
1093
- if (matchHeader(line)) inRoot = false;
1193
+ for (const line$1 of lines) {
1194
+ if (matchHeader(line$1)) inRoot = false;
1094
1195
  if (inRoot) {
1095
- const key = matchKey(line)?.[1];
1096
- if (key && LEGACY_TOML_ROOT_MARKERS.includes(key)) continue;
1196
+ const key = readKeyFromLine(line$1);
1197
+ if (key !== void 0 && isLegacyTomlRootMarker(key)) continue;
1097
1198
  }
1098
- updated.push(line);
1199
+ updated.push(line$1);
1099
1200
  }
1100
1201
  return updated;
1101
- };
1102
- const mergeCodexToml = (content, input) => {
1103
- const updated = [...stripLegacyRootMarkers(content.length ? content.split(/\r?\n/) : [])];
1202
+ }
1203
+ function mergeCodexToml(content, input) {
1204
+ const updated = stripLegacyRootMarkers(splitLines(content));
1104
1205
  const rootValueMap = rootValues(input);
1105
1206
  const providerValueMap = providerValues();
1106
- let currentSection = null;
1107
- let firstHeaderIndex = null;
1108
- const rootFound = /* @__PURE__ */ new Set();
1109
- for (let i = 0; i < updated.length; i += 1) {
1110
- const headerMatch = matchHeader(updated[i] ?? "");
1111
- if (headerMatch) {
1112
- currentSection = headerMatch[1]?.trim() ?? null;
1113
- if (firstHeaderIndex === null) firstHeaderIndex = i;
1114
- continue;
1115
- }
1116
- if (currentSection !== null) continue;
1117
- const keyMatch = matchKey(updated[i] ?? "");
1118
- if (!keyMatch) continue;
1119
- const key = keyMatch[1];
1120
- if (ROOT_KEYS.includes(key)) {
1121
- updated[i] = `${key} = ${rootValueMap[key]}`;
1122
- rootFound.add(key);
1123
- }
1124
- }
1125
- const insertIndex = firstHeaderIndex ?? updated.length;
1126
- const missingRoot = ROOT_KEYS.filter((key) => !rootFound.has(key)).map((key) => `${key} = ${rootValueMap[key]}`);
1207
+ const firstHeaderIndex = updated.findIndex((line$1) => matchHeader(line$1) !== null);
1208
+ const rootEnd = firstHeaderIndex === -1 ? updated.length : firstHeaderIndex;
1209
+ const missingRoot = missingKeyLines(ROOT_KEYS, upsertKeyLines(updated, 0, rootEnd, ROOT_KEYS, rootValueMap), rootValueMap);
1127
1210
  if (missingRoot.length > 0) {
1211
+ const insertIndex = rootEnd;
1128
1212
  const needsBlank = insertIndex < updated.length && updated[insertIndex]?.trim() !== "";
1129
1213
  updated.splice(insertIndex, 0, ...missingRoot, ...needsBlank ? [""] : []);
1130
1214
  }
1131
1215
  const providerHeader = `[${PROVIDER_SECTION}]`;
1132
- const providerHeaderIndex = updated.findIndex((line) => line.trim() === providerHeader);
1216
+ const providerHeaderIndex = updated.findIndex((line$1) => line$1.trim() === providerHeader);
1133
1217
  if (providerHeaderIndex === -1) {
1134
1218
  if (updated.length > 0 && updated[updated.length - 1]?.trim() !== "") updated.push("");
1135
- updated.push(providerHeader);
1136
- for (const key of PROVIDER_KEYS) updated.push(`${key} = ${providerValueMap[key]}`);
1219
+ updated.push(providerHeader, ...PROVIDER_KEYS.map((key) => `${key} = ${providerValueMap[key]}`));
1137
1220
  return updated.join("\n");
1138
1221
  }
1139
- let providerEnd = updated.length;
1140
- for (let i = providerHeaderIndex + 1; i < updated.length; i += 1) if (matchHeader(updated[i] ?? "")) {
1141
- providerEnd = i;
1142
- break;
1143
- }
1144
- const providerFound = /* @__PURE__ */ new Set();
1145
- for (let i = providerHeaderIndex + 1; i < providerEnd; i += 1) {
1146
- const keyMatch = matchKey(updated[i] ?? "");
1147
- if (!keyMatch) continue;
1148
- const key = keyMatch[1];
1149
- if (PROVIDER_KEYS.includes(key)) {
1150
- updated[i] = `${key} = ${providerValueMap[key]}`;
1151
- providerFound.add(key);
1152
- }
1153
- }
1154
- const missingProvider = PROVIDER_KEYS.filter((key) => !providerFound.has(key)).map((key) => `${key} = ${providerValueMap[key]}`);
1222
+ const providerStart = providerHeaderIndex + 1;
1223
+ const providerEnd = findSectionEnd(updated, providerStart);
1224
+ const missingProvider = missingKeyLines(PROVIDER_KEYS, upsertKeyLines(updated, providerStart, providerEnd, PROVIDER_KEYS, providerValueMap), providerValueMap);
1155
1225
  if (missingProvider.length > 0) updated.splice(providerEnd, 0, ...missingProvider);
1156
1226
  return updated.join("\n");
1157
- };
1158
- const mergeAuthJson = (data, apiKey) => {
1227
+ }
1228
+ function mergeAuthJson(data, apiKey) {
1159
1229
  const next = { ...data };
1160
1230
  for (const key of LEGACY_AUTH_MARKERS) if (key in next) delete next[key];
1161
1231
  next.OPENAI_API_KEY = apiKey;
1162
1232
  return next;
1163
- };
1164
- const stripGetrouterProviderSection = (lines) => {
1233
+ }
1234
+ function stripGetrouterProviderSection(lines) {
1165
1235
  const updated = [];
1166
1236
  let skipSection = false;
1167
- for (const line of lines) {
1168
- const headerMatch = matchHeader(line);
1237
+ for (const line$1 of lines) {
1238
+ const headerMatch = matchHeader(line$1);
1169
1239
  if (headerMatch) {
1170
- if ((headerMatch[1]?.trim() ?? "") === PROVIDER_SECTION) {
1240
+ if (headerMatch[1]?.trim() === PROVIDER_SECTION) {
1171
1241
  skipSection = true;
1172
1242
  continue;
1173
1243
  }
1174
1244
  skipSection = false;
1175
1245
  }
1176
1246
  if (skipSection) continue;
1177
- updated.push(line);
1247
+ updated.push(line$1);
1178
1248
  }
1179
1249
  return updated;
1180
- };
1181
- const stripLegacyMarkersFromRoot = (rootLines) => rootLines.filter((line) => {
1182
- const key = matchKey(line)?.[1];
1183
- return !(key && LEGACY_TOML_ROOT_MARKERS.includes(key));
1184
- });
1185
- const setOrDeleteRootKey = (rootLines, key, value) => {
1186
- const idx = rootLines.findIndex((line) => matchKey(line)?.[1] === key);
1250
+ }
1251
+ function stripLegacyMarkersFromRoot(rootLines) {
1252
+ return rootLines.filter((line$1) => {
1253
+ const key = readKeyFromLine(line$1);
1254
+ return !(key !== void 0 && isLegacyTomlRootMarker(key));
1255
+ });
1256
+ }
1257
+ function setOrDeleteRootKey(rootLines, key, value) {
1258
+ const idx = rootLines.findIndex((line$1) => readKeyFromLine(line$1) === key);
1187
1259
  if (value === void 0) {
1188
1260
  if (idx !== -1) rootLines.splice(idx, 1);
1189
1261
  return;
1190
1262
  }
1191
1263
  if (idx !== -1) rootLines[idx] = `${key} = ${value}`;
1192
1264
  else rootLines.push(`${key} = ${value}`);
1193
- };
1194
- const deleteRootKey = (rootLines, key) => {
1265
+ }
1266
+ function deleteRootKey(rootLines, key) {
1195
1267
  setOrDeleteRootKey(rootLines, key, void 0);
1196
- };
1197
- const hasLegacyRootMarkers = (lines) => lines.some((line) => {
1198
- const key = matchKey(line)?.[1];
1199
- return !!(key && LEGACY_TOML_ROOT_MARKERS.includes(key));
1200
- });
1201
- const removeCodexConfig = (content, options) => {
1268
+ }
1269
+ function hasLegacyRootMarkers(lines) {
1270
+ return lines.some((line$1) => {
1271
+ const key = readKeyFromLine(line$1);
1272
+ return key !== void 0 && isLegacyTomlRootMarker(key);
1273
+ });
1274
+ }
1275
+ function removeCodexConfig(content, options) {
1202
1276
  const { restoreRoot, allowRootRemoval = true } = options ?? {};
1203
- const lines = content.length ? content.split(/\r?\n/) : [];
1277
+ const lines = splitLines(content);
1204
1278
  const providerIsGetrouter = normalizeTomlString(readRootValue(lines, "model_provider")) === CODEX_PROVIDER;
1205
1279
  const canRemoveRoot = allowRootRemoval || hasLegacyRootMarkers(lines);
1206
1280
  const stripped = stripGetrouterProviderSection(lines);
1207
- const firstHeaderIndex = stripped.findIndex((line) => matchHeader(line));
1281
+ const firstHeaderIndex = stripped.findIndex((line$1) => matchHeader(line$1));
1208
1282
  const rootEnd = firstHeaderIndex === -1 ? stripped.length : firstHeaderIndex;
1209
1283
  const rootLines = stripLegacyMarkersFromRoot(stripped.slice(0, rootEnd));
1210
1284
  const restLines = stripped.slice(rootEnd);
@@ -1225,21 +1299,21 @@ const removeCodexConfig = (content, options) => {
1225
1299
  content: nextContent,
1226
1300
  changed: nextContent !== content
1227
1301
  };
1228
- };
1229
- const removeAuthJson = (data, options) => {
1302
+ }
1303
+ function removeAuthJson(data, options) {
1230
1304
  const { installed, restore } = options ?? {};
1231
1305
  const next = { ...data };
1232
1306
  let changed = false;
1233
- const legacyInstalled = typeof next._getrouter_codex_installed_openai_api_key === "string" ? next._getrouter_codex_installed_openai_api_key : void 0;
1234
- const legacyRestore = typeof next._getrouter_codex_backup_openai_api_key === "string" ? next._getrouter_codex_backup_openai_api_key : void 0;
1307
+ const legacyInstalled = readStringValue(next, "_getrouter_codex_installed_openai_api_key");
1308
+ const legacyRestore = readStringValue(next, "_getrouter_codex_backup_openai_api_key");
1235
1309
  const effectiveInstalled = installed ?? legacyInstalled;
1236
1310
  const effectiveRestore = restore ?? legacyRestore;
1237
1311
  for (const key of LEGACY_AUTH_MARKERS) if (key in next) {
1238
1312
  delete next[key];
1239
1313
  changed = true;
1240
1314
  }
1241
- const current = typeof next.OPENAI_API_KEY === "string" ? next.OPENAI_API_KEY : void 0;
1242
- const restoreValue = typeof effectiveRestore === "string" && effectiveRestore.trim().length > 0 ? effectiveRestore : void 0;
1315
+ const current = readStringValue(next, "OPENAI_API_KEY");
1316
+ const restoreValue = effectiveRestore?.trim() ? effectiveRestore : void 0;
1243
1317
  if (effectiveInstalled && current && current === effectiveInstalled) {
1244
1318
  if (restoreValue) next.OPENAI_API_KEY = restoreValue;
1245
1319
  else delete next.OPENAI_API_KEY;
@@ -1253,7 +1327,7 @@ const removeAuthJson = (data, options) => {
1253
1327
  data: next,
1254
1328
  changed
1255
1329
  };
1256
- };
1330
+ }
1257
1331
 
1258
1332
  //#endregion
1259
1333
  //#region src/cmd/codex.ts
@@ -1300,7 +1374,7 @@ const promptReasoning = async (model) => await fuzzySelect({
1300
1374
  choices: REASONING_FUZZY_CHOICES
1301
1375
  });
1302
1376
  const formatReasoningLabel = (id) => REASONING_CHOICES.find((choice) => choice.id === id)?.label ?? id;
1303
- const registerCodexCommand = (program) => {
1377
+ function registerCodexCommand(program) {
1304
1378
  const codex = program.command("codex").description("Configure Codex");
1305
1379
  codex.option("-m, --model <model>", "Set codex model (skips model selection)").action(async (options) => {
1306
1380
  requireInteractive$1();
@@ -1340,13 +1414,15 @@ const registerCodexCommand = (program) => {
1340
1414
  backup.updatedAt = now;
1341
1415
  backup.config ??= {};
1342
1416
  backup.config.previous ??= {};
1343
- if (backup.config.previous.model === void 0 && existingRoot.model && existingRoot.model !== installedRoot.model) backup.config.previous.model = existingRoot.model;
1344
- if (backup.config.previous.reasoning === void 0 && existingRoot.reasoning && existingRoot.reasoning !== installedRoot.reasoning) backup.config.previous.reasoning = existingRoot.reasoning;
1345
- if (backup.config.previous.provider === void 0 && existingRoot.provider && existingRoot.provider !== installedRoot.provider) backup.config.previous.provider = existingRoot.provider;
1417
+ const previousConfig = backup.config.previous;
1418
+ if (previousConfig.model === void 0 && existingRoot.model && existingRoot.model !== installedRoot.model) previousConfig.model = existingRoot.model;
1419
+ if (previousConfig.reasoning === void 0 && existingRoot.reasoning && existingRoot.reasoning !== installedRoot.reasoning) previousConfig.reasoning = existingRoot.reasoning;
1420
+ if (previousConfig.provider === void 0 && existingRoot.provider && existingRoot.provider !== installedRoot.provider) previousConfig.provider = existingRoot.provider;
1346
1421
  backup.config.installed = installedRoot;
1347
1422
  backup.auth ??= {};
1348
- if (backup.auth.previousOpenaiKey === void 0 && existingOpenaiKey && existingOpenaiKey !== apiKey) backup.auth.previousOpenaiKey = existingOpenaiKey;
1349
- backup.auth.installedOpenaiKey = apiKey;
1423
+ const authBackup = backup.auth;
1424
+ if (authBackup.previousOpenaiKey === void 0 && existingOpenaiKey && existingOpenaiKey !== apiKey) authBackup.previousOpenaiKey = existingOpenaiKey;
1425
+ authBackup.installedOpenaiKey = apiKey;
1350
1426
  writeCodexBackup(backup);
1351
1427
  const mergedConfig = mergeCodexToml(existingConfig, {
1352
1428
  model,
@@ -1376,7 +1452,9 @@ const registerCodexCommand = (program) => {
1376
1452
  allowRootRemoval: hasBackup
1377
1453
  }) : null;
1378
1454
  const authContent = authExists ? fs.readFileSync(authPath, "utf8").trim() : "";
1379
- const authData = authExists ? authContent ? JSON.parse(authContent) : {} : null;
1455
+ let authData = null;
1456
+ if (authExists) if (authContent) authData = JSON.parse(authContent);
1457
+ else authData = {};
1380
1458
  const authResult = authData ? removeAuthJson(authData, {
1381
1459
  installed: installedOpenaiKey,
1382
1460
  restore: restoreOpenaiKey
@@ -1394,7 +1472,7 @@ const registerCodexCommand = (program) => {
1394
1472
  const backupPath = resolveCodexBackupPath();
1395
1473
  if (fs.existsSync(backupPath)) fs.unlinkSync(backupPath);
1396
1474
  });
1397
- };
1475
+ }
1398
1476
 
1399
1477
  //#endregion
1400
1478
  //#region src/core/config/redact.ts
@@ -1403,28 +1481,28 @@ const SECRET_KEYS = new Set([
1403
1481
  "refreshToken",
1404
1482
  "apiKey"
1405
1483
  ]);
1406
- const mask = (value) => {
1484
+ function mask(value) {
1407
1485
  if (!value) return "";
1408
1486
  if (value.length <= 8) return "****";
1409
1487
  return `${value.slice(0, 4)}...${value.slice(-4)}`;
1410
- };
1411
- const redactSecrets = (obj) => {
1488
+ }
1489
+ function redactSecrets(obj) {
1412
1490
  const out = { ...obj };
1413
1491
  for (const key of Object.keys(out)) {
1414
1492
  const value = out[key];
1415
1493
  if (SECRET_KEYS.has(key) && typeof value === "string") out[key] = mask(value);
1416
1494
  }
1417
1495
  return out;
1418
- };
1496
+ }
1419
1497
 
1420
1498
  //#endregion
1421
1499
  //#region src/core/output/table.ts
1422
- const truncate = (value, max) => {
1500
+ function truncate(value, max) {
1423
1501
  if (value.length <= max) return value;
1424
1502
  if (max <= 3) return value.slice(0, max);
1425
1503
  return `${value.slice(0, max - 3)}...`;
1426
- };
1427
- const renderTable = (headers, rows, options = {}) => {
1504
+ }
1505
+ function renderTable(headers, rows, options = {}) {
1428
1506
  const maxColWidth = options.maxColWidth ?? 32;
1429
1507
  const normalized = rows.map((row) => row.map((cell) => cell && cell.length > 0 ? cell : "-"));
1430
1508
  const widths = headers.map((header, index) => {
@@ -1436,7 +1514,7 @@ const renderTable = (headers, rows, options = {}) => {
1436
1514
  return truncate(cell ?? "-", widths[index]).padEnd(widths[index], " ");
1437
1515
  }).join(" ");
1438
1516
  return `${renderRow(headers)}\n${normalized.map((row) => renderRow(row)).join("\n")}`;
1439
- };
1517
+ }
1440
1518
 
1441
1519
  //#endregion
1442
1520
  //#region src/cmd/keys.ts
@@ -1469,8 +1547,22 @@ const requireInteractive = (message) => {
1469
1547
  };
1470
1548
  const requireInteractiveForSelection = () => requireInteractive("Interactive mode required when key id is omitted.");
1471
1549
  const requireInteractiveForAction = (action) => requireInteractive(`Interactive mode required for keys ${action}.`);
1550
+ const promptKeyDetails = async (initialName, initialEnabled) => {
1551
+ const nameResult = await promptKeyName(initialName);
1552
+ if (nameResult.cancelled) return { cancelled: true };
1553
+ const enabledResult = await promptKeyEnabled(initialEnabled);
1554
+ if (enabledResult.cancelled) return { cancelled: true };
1555
+ return {
1556
+ cancelled: false,
1557
+ name: nameResult.name,
1558
+ enabled: enabledResult.enabled
1559
+ };
1560
+ };
1472
1561
  const updateConsumer = async (consumerService, consumer, name, enabled) => {
1473
- const updateMask = [name !== void 0 && name !== consumer.name ? "name" : null, enabled !== void 0 && enabled !== consumer.enabled ? "enabled" : null].filter(Boolean).join(",");
1562
+ const updateMaskParts = [];
1563
+ if (name !== void 0 && name !== consumer.name) updateMaskParts.push("name");
1564
+ if (enabled !== void 0 && enabled !== consumer.enabled) updateMaskParts.push("enabled");
1565
+ const updateMask = updateMaskParts.join(",");
1474
1566
  if (!updateMask) return consumer;
1475
1567
  return consumerService.UpdateConsumer({
1476
1568
  consumer: {
@@ -1487,46 +1579,37 @@ const listConsumers = async (consumerService, showApiKey) => {
1487
1579
  pageToken
1488
1580
  }), (res) => res?.consumers ?? [], (res) => res?.nextPageToken || void 0)), showApiKey);
1489
1581
  };
1490
- const resolveConsumerForUpdate = async (consumerService, id) => {
1582
+ const resolveConsumer = async (consumerService, message, id) => {
1491
1583
  if (id) return consumerService.GetConsumer({ id });
1492
1584
  requireInteractiveForSelection();
1493
- return await selectConsumerList(consumerService, "Select key to update");
1494
- };
1495
- const resolveConsumerForDelete = async (consumerService, id) => {
1496
- if (id) return consumerService.GetConsumer({ id });
1497
- requireInteractiveForSelection();
1498
- return await selectConsumerList(consumerService, "Select key to delete");
1585
+ return await selectConsumerList(consumerService, message);
1499
1586
  };
1500
1587
  const createConsumer = async (consumerService) => {
1501
1588
  requireInteractiveForAction("create");
1502
- const nameResult = await promptKeyName();
1503
- if (nameResult.cancelled) return;
1504
- const enabledResult = await promptKeyEnabled(true);
1505
- if (enabledResult.cancelled) return;
1589
+ const details = await promptKeyDetails(void 0, true);
1590
+ if (details.cancelled) return;
1506
1591
  let consumer = await consumerService.CreateConsumer({});
1507
- consumer = await updateConsumer(consumerService, consumer, nameResult.name, enabledResult.enabled);
1592
+ consumer = await updateConsumer(consumerService, consumer, details.name, details.enabled);
1508
1593
  outputConsumerTable(consumer, true);
1509
1594
  console.log("Please store this API key securely.");
1510
1595
  };
1511
1596
  const updateConsumerById = async (consumerService, id) => {
1512
1597
  requireInteractiveForAction("update");
1513
- const selected = await resolveConsumerForUpdate(consumerService, id);
1598
+ const selected = await resolveConsumer(consumerService, "Select key to update", id);
1514
1599
  if (!selected?.id) return;
1515
- const nameResult = await promptKeyName(selected.name);
1516
- if (nameResult.cancelled) return;
1517
- const enabledResult = await promptKeyEnabled(selected.enabled ?? true);
1518
- if (enabledResult.cancelled) return;
1519
- outputConsumerTable(await updateConsumer(consumerService, selected, nameResult.name, enabledResult.enabled), false);
1600
+ const details = await promptKeyDetails(selected.name, selected.enabled ?? true);
1601
+ if (details.cancelled) return;
1602
+ outputConsumerTable(await updateConsumer(consumerService, selected, details.name, details.enabled), false);
1520
1603
  };
1521
1604
  const deleteConsumerById = async (consumerService, id) => {
1522
1605
  requireInteractiveForAction("delete");
1523
- const selected = await resolveConsumerForDelete(consumerService, id);
1606
+ const selected = await resolveConsumer(consumerService, "Select key to delete", id);
1524
1607
  if (!selected?.id) return;
1525
1608
  if (!await confirmDelete(selected)) return;
1526
1609
  await consumerService.DeleteConsumer({ id: selected.id });
1527
1610
  outputConsumerTable(selected, false);
1528
1611
  };
1529
- const registerKeysCommands = (program) => {
1612
+ function registerKeysCommands(program) {
1530
1613
  const keys = program.command("keys").description("Manage API keys");
1531
1614
  keys.option("--show", "Show full API keys");
1532
1615
  keys.allowExcessArguments(false);
@@ -1551,7 +1634,7 @@ const registerKeysCommands = (program) => {
1551
1634
  const { consumerService } = createApiClients({});
1552
1635
  await deleteConsumerById(consumerService, id);
1553
1636
  });
1554
- };
1637
+ }
1555
1638
 
1556
1639
  //#endregion
1557
1640
  //#region src/cmd/models.ts
@@ -1562,18 +1645,20 @@ const modelHeaders = [
1562
1645
  "ENABLED",
1563
1646
  "UPDATED_AT"
1564
1647
  ];
1565
- const modelRow = (model) => [
1566
- String(model.id ?? ""),
1567
- String(model.name ?? ""),
1568
- String(model.author ?? ""),
1569
- String(model.enabled ?? ""),
1570
- String(model.updatedAt ?? "")
1571
- ];
1572
- const outputModels = (models) => {
1648
+ function formatModelRow(model) {
1649
+ return [
1650
+ String(model.id ?? ""),
1651
+ String(model.name ?? ""),
1652
+ String(model.author ?? ""),
1653
+ String(model.enabled ?? ""),
1654
+ String(model.updatedAt ?? "")
1655
+ ];
1656
+ }
1657
+ function outputModels(models) {
1573
1658
  console.log("๐Ÿง  Models");
1574
- console.log(renderTable(modelHeaders, models.map(modelRow)));
1575
- };
1576
- const listModels = async () => {
1659
+ console.log(renderTable(modelHeaders, models.map(formatModelRow)));
1660
+ }
1661
+ async function listModels() {
1577
1662
  const { modelService } = createApiClients({});
1578
1663
  const models = (await modelService.ListModels({
1579
1664
  pageSize: void 0,
@@ -1585,66 +1670,75 @@ const listModels = async () => {
1585
1670
  return;
1586
1671
  }
1587
1672
  outputModels(models);
1588
- };
1589
- const registerModelsCommands = (program) => {
1590
- const models = program.command("models").description("List models");
1591
- models.action(async () => {
1592
- await listModels();
1593
- });
1594
- models.command("list").description("List models").action(async () => {
1595
- await listModels();
1596
- });
1597
- };
1673
+ }
1674
+ function registerModelsCommands(program) {
1675
+ program.command("models").description("List models").action(listModels).command("list").description("List models").action(listModels);
1676
+ }
1598
1677
 
1599
1678
  //#endregion
1600
1679
  //#region src/cmd/status.ts
1601
1680
  const LABEL_WIDTH = 10;
1602
- const formatLine = (label, value) => {
1681
+ function line(label, value) {
1682
+ return [label, value];
1683
+ }
1684
+ function formatLine(label, value) {
1603
1685
  if (value == null || value === "") return null;
1604
1686
  return ` ${label.padEnd(LABEL_WIDTH, " ")}: ${value}`;
1605
- };
1606
- const formatAuthStatus = (status) => status === "logged_in" ? "โœ… Logged in" : "โŒ Logged out";
1607
- const formatToken = (token) => {
1608
- if (!token) return void 0;
1687
+ }
1688
+ function renderSection(title, items) {
1689
+ const lines = [];
1690
+ for (const item of items) {
1691
+ if (!item) continue;
1692
+ const rendered = formatLine(item[0], item[1]);
1693
+ if (rendered) lines.push(rendered);
1694
+ }
1695
+ return [title, ...lines].join("\n");
1696
+ }
1697
+ function formatAuthStatus(status) {
1698
+ if (status === "logged_in") return "โœ… Logged in";
1699
+ return "โŒ Logged out";
1700
+ }
1701
+ function formatToken(token) {
1702
+ if (!token) return;
1609
1703
  const trimmed = token.trim();
1610
1704
  if (trimmed.length <= 12) return trimmed;
1611
1705
  return `${trimmed.slice(0, 4)}...${trimmed.slice(-4)}`;
1612
- };
1613
- const formatWindow = (startAt, endAt) => {
1614
- if (!startAt && !endAt) return void 0;
1706
+ }
1707
+ function formatWindow(startAt, endAt) {
1708
+ if (!startAt && !endAt) return;
1615
1709
  if (startAt && endAt) return `${startAt} โ†’ ${endAt}`;
1616
1710
  if (startAt) return `${startAt} โ†’`;
1617
1711
  return `โ†’ ${endAt}`;
1618
- };
1619
- const formatLimits = (requestPerMinute, tokenPerMinute) => {
1712
+ }
1713
+ function formatLimits(requestPerMinute, tokenPerMinute) {
1620
1714
  const parts = [];
1621
1715
  if (typeof requestPerMinute === "number") parts.push(`${requestPerMinute} req/min`);
1622
1716
  if (tokenPerMinute) parts.push(`${tokenPerMinute} tok/min`);
1623
- if (parts.length === 0) return void 0;
1717
+ if (parts.length === 0) return;
1624
1718
  return parts.join(" ยท ");
1625
- };
1626
- const renderAuthSection = () => {
1719
+ }
1720
+ function renderAuthSection() {
1627
1721
  const status = getAuthStatus();
1628
- return ["๐Ÿ” Auth", ...[
1629
- formatLine("Status", formatAuthStatus(status.status)),
1630
- status.status === "logged_in" ? formatLine("Expires", status.expiresAt) : null,
1631
- status.status === "logged_in" ? formatLine("TokenType", status.tokenType) : null,
1632
- status.status === "logged_in" ? formatLine("Access", formatToken(status.accessToken)) : null,
1633
- status.status === "logged_in" ? formatLine("Refresh", formatToken(status.refreshToken)) : null
1634
- ].filter(Boolean)].join("\n");
1635
- };
1636
- const renderSubscriptionSection = (subscription) => {
1637
- if (!subscription) return ["๐Ÿ“ฆ Subscription", formatLine("Status", "No active subscription")].filter(Boolean).join("\n");
1722
+ const isLoggedIn = status.status === "logged_in";
1723
+ return renderSection("๐Ÿ” Auth", [
1724
+ line("Status", formatAuthStatus(status.status)),
1725
+ isLoggedIn && line("Expires", status.expiresAt),
1726
+ isLoggedIn && line("TokenType", status.tokenType),
1727
+ isLoggedIn && line("Access", formatToken(status.accessToken)),
1728
+ isLoggedIn && line("Refresh", formatToken(status.refreshToken))
1729
+ ]);
1730
+ }
1731
+ function renderSubscriptionSection(subscription) {
1732
+ if (!subscription) return renderSection("๐Ÿ“ฆ Subscription", [line("Status", "No active subscription")]);
1638
1733
  const limits = formatLimits(subscription.plan?.requestPerMinute, subscription.plan?.tokenPerMinute);
1639
- const windowLabel = formatWindow(subscription.startAt, subscription.endAt);
1640
- return ["๐Ÿ“ฆ Subscription", ...[
1641
- formatLine("Plan", subscription.plan?.name),
1642
- formatLine("Status", subscription.status),
1643
- formatLine("Window", windowLabel),
1644
- formatLine("Limits", limits)
1645
- ].filter(Boolean)].join("\n");
1646
- };
1647
- const registerStatusCommand = (program) => {
1734
+ return renderSection("๐Ÿ“ฆ Subscription", [
1735
+ line("Plan", subscription.plan?.name),
1736
+ line("Status", subscription.status),
1737
+ line("Window", formatWindow(subscription.startAt, subscription.endAt)),
1738
+ line("Limits", limits)
1739
+ ]);
1740
+ }
1741
+ function registerStatusCommand(program) {
1648
1742
  program.command("status").description("Show login and subscription status").action(async () => {
1649
1743
  const { subscriptionService } = createApiClients({});
1650
1744
  const subscription = await subscriptionService.CurrentSubscription({});
@@ -1652,62 +1746,49 @@ const registerStatusCommand = (program) => {
1652
1746
  console.log("");
1653
1747
  console.log(renderSubscriptionSection(subscription));
1654
1748
  });
1655
- };
1749
+ }
1656
1750
 
1657
1751
  //#endregion
1658
1752
  //#region src/core/output/usages.ts
1659
1753
  const TOTAL_BLOCK = "โ–ˆ";
1660
1754
  const DEFAULT_WIDTH = 24;
1661
- const formatTokens = (value) => {
1755
+ function formatTokens(value) {
1662
1756
  const abs = Math.abs(value);
1663
1757
  if (abs < 1e3) return Math.round(value).toString();
1664
- for (const unit of [
1665
- {
1666
- threshold: 1e9,
1667
- suffix: "B"
1668
- },
1669
- {
1670
- threshold: 1e6,
1671
- suffix: "M"
1672
- },
1673
- {
1674
- threshold: 1e3,
1675
- suffix: "K"
1676
- }
1677
- ]) if (abs >= unit.threshold) {
1678
- const scaled = value / unit.threshold;
1679
- const decimals = Math.abs(scaled) < 10 ? 1 : 0;
1680
- let output = scaled.toFixed(decimals);
1681
- if (output.endsWith(".0")) output = output.slice(0, -2);
1682
- return `${output}${unit.suffix}`;
1758
+ let threshold = 1e3;
1759
+ let suffix = "K";
1760
+ if (abs >= 1e9) {
1761
+ threshold = 1e9;
1762
+ suffix = "B";
1763
+ } else if (abs >= 1e6) {
1764
+ threshold = 1e6;
1765
+ suffix = "M";
1683
1766
  }
1684
- return Math.round(value).toString();
1685
- };
1686
- const renderUsageChart = (rows, width = DEFAULT_WIDTH) => {
1767
+ const scaled = value / threshold;
1768
+ const decimals = Math.abs(scaled) < 10 ? 1 : 0;
1769
+ return `${scaled.toFixed(decimals).replace(/\.0$/, "")}${suffix}`;
1770
+ }
1771
+ function renderUsageChart(rows, width = DEFAULT_WIDTH) {
1687
1772
  const header = "๐Ÿ“Š Usage (last 7 days) ยท Tokens";
1688
1773
  if (rows.length === 0) return `${header}\n\nNo usage data available.`;
1689
- const normalized = rows.map((row) => {
1690
- const total = Number(row.totalTokens);
1691
- const safeTotal = Number.isFinite(total) ? total : 0;
1774
+ const data = rows.map((row) => {
1775
+ const rawTotal = Number(row.totalTokens);
1692
1776
  return {
1693
1777
  day: row.day,
1694
- total: safeTotal
1778
+ total: Number.isFinite(rawTotal) ? rawTotal : 0
1695
1779
  };
1696
1780
  });
1697
- const totals = normalized.map((row) => row.total);
1698
- const maxTotal = Math.max(0, ...totals);
1781
+ const maxTotal = Math.max(0, ...data.map((d) => d.total));
1699
1782
  return [
1700
1783
  header,
1701
1784
  "",
1702
- ...normalized.map((row) => {
1703
- if (maxTotal === 0 || row.total === 0) return `${row.day} ${"".padEnd(width, " ")} 0`;
1704
- const scaled = Math.max(1, Math.round(row.total / maxTotal * width));
1705
- const bar = TOTAL_BLOCK.repeat(scaled);
1706
- const totalLabel = formatTokens(row.total);
1707
- return `${row.day} ${bar.padEnd(width, " ")} ${totalLabel}`;
1785
+ ...data.map(({ day, total }) => {
1786
+ if (maxTotal === 0 || total === 0) return `${day} ${" ".repeat(width)} 0`;
1787
+ const scaled = Math.max(1, Math.round(total / maxTotal * width));
1788
+ return `${day} ${TOTAL_BLOCK.repeat(scaled).padEnd(width, " ")} ${formatTokens(total)}`;
1708
1789
  })
1709
1790
  ].join("\n");
1710
- };
1791
+ }
1711
1792
 
1712
1793
  //#endregion
1713
1794
  //#region src/core/usages/aggregate.ts
@@ -1722,7 +1803,7 @@ const toNumber = (value) => {
1722
1803
  }
1723
1804
  return 0;
1724
1805
  };
1725
- const aggregateUsages = (usages, maxDays = 7) => {
1806
+ function aggregateUsages(usages, maxDays = 7) {
1726
1807
  const totals = /* @__PURE__ */ new Map();
1727
1808
  for (const usage of usages) {
1728
1809
  if (!usage.createdAt) continue;
@@ -1747,27 +1828,27 @@ const aggregateUsages = (usages, maxDays = 7) => {
1747
1828
  totals.set(day, current);
1748
1829
  }
1749
1830
  return Array.from(totals.values()).sort((a, b) => b.day.localeCompare(a.day)).slice(0, maxDays);
1750
- };
1831
+ }
1751
1832
 
1752
1833
  //#endregion
1753
1834
  //#region src/cmd/usages.ts
1754
- const collectUsages = async () => {
1835
+ async function collectUsages() {
1755
1836
  const { usageService } = createApiClients({});
1756
1837
  return aggregateUsages((await usageService.ListUsage({
1757
1838
  pageSize: 7,
1758
1839
  pageToken: void 0
1759
1840
  }))?.usages ?? [], 7);
1760
- };
1761
- const registerUsagesCommand = (program) => {
1841
+ }
1842
+ function registerUsagesCommand(program) {
1762
1843
  program.command("usages").description("Show recent usage").action(async () => {
1763
1844
  const aggregated = await collectUsages();
1764
1845
  console.log(renderUsageChart(aggregated));
1765
1846
  });
1766
- };
1847
+ }
1767
1848
 
1768
1849
  //#endregion
1769
1850
  //#region src/cmd/index.ts
1770
- const registerCommands = (program) => {
1851
+ function registerCommands(program) {
1771
1852
  registerAuthCommands(program);
1772
1853
  registerCodexCommand(program);
1773
1854
  registerClaudeCommand(program);
@@ -1775,16 +1856,16 @@ const registerCommands = (program) => {
1775
1856
  registerModelsCommands(program);
1776
1857
  registerStatusCommand(program);
1777
1858
  registerUsagesCommand(program);
1778
- };
1859
+ }
1779
1860
 
1780
1861
  //#endregion
1781
1862
  //#region src/cli.ts
1782
- const createProgram = () => {
1863
+ function createProgram() {
1783
1864
  const program = new Command();
1784
1865
  program.name("getrouter").description("CLI for getrouter.dev").version(version);
1785
1866
  registerCommands(program);
1786
1867
  return program;
1787
- };
1868
+ }
1788
1869
 
1789
1870
  //#endregion
1790
1871
  //#region src/bin.ts