@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getrouter/getrouter-cli",
3
- "version": "0.1.13",
3
+ "version": "0.1.14",
4
4
  "type": "module",
5
5
  "description": "CLI for getrouter.dev",
6
6
  "bin": {
package/src/cli.ts CHANGED
@@ -2,12 +2,14 @@ import { Command } from "commander";
2
2
  import { version } from "../package.json";
3
3
  import { registerCommands } from "./cmd";
4
4
 
5
- export const createProgram = () => {
5
+ export function createProgram(): Command {
6
6
  const program = new Command();
7
7
  program
8
8
  .name("getrouter")
9
9
  .description("CLI for getrouter.dev")
10
10
  .version(version);
11
+
11
12
  registerCommands(program);
13
+
12
14
  return program;
13
- };
15
+ }
package/src/cmd/auth.ts CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  } from "../core/auth/device";
10
10
  import { writeAuth } from "../core/config";
11
11
 
12
- export const registerAuthCommands = (program: Command) => {
12
+ export function registerAuthCommands(program: Command): void {
13
13
  program
14
14
  .command("login")
15
15
  .description("Login with device flow")
@@ -41,4 +41,4 @@ export const registerAuthCommands = (program: Command) => {
41
41
  clearAuth();
42
42
  console.log("Cleared local auth data.");
43
43
  });
44
- };
44
+ }
package/src/cmd/claude.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import type { Command } from "commander";
2
2
  import { buildAnthropicEnv, registerEnvCommand } from "./env";
3
3
 
4
- export const registerClaudeCommand = (program: Command) => {
4
+ export function registerClaudeCommand(program: Command): void {
5
5
  registerEnvCommand(program, {
6
6
  name: "claude",
7
7
  description: "Configure Claude environment",
8
8
  vars: buildAnthropicEnv,
9
9
  });
10
- };
10
+ }
package/src/cmd/codex.ts CHANGED
@@ -107,7 +107,7 @@ type CodexCommandOptions = {
107
107
  model?: string;
108
108
  };
109
109
 
110
- export const registerCodexCommand = (program: Command) => {
110
+ export function registerCodexCommand(program: Command): void {
111
111
  const codex = program.command("codex").description("Configure Codex");
112
112
 
113
113
  codex
@@ -168,40 +168,43 @@ export const registerCodexCommand = (program: Command) => {
168
168
  updatedAt: now,
169
169
  };
170
170
  backup.updatedAt = now;
171
+
171
172
  backup.config ??= {};
172
173
  backup.config.previous ??= {};
174
+ const previousConfig = backup.config.previous;
173
175
  if (
174
- backup.config.previous.model === undefined &&
176
+ previousConfig.model === undefined &&
175
177
  existingRoot.model &&
176
178
  existingRoot.model !== installedRoot.model
177
179
  ) {
178
- backup.config.previous.model = existingRoot.model;
180
+ previousConfig.model = existingRoot.model;
179
181
  }
180
182
  if (
181
- backup.config.previous.reasoning === undefined &&
183
+ previousConfig.reasoning === undefined &&
182
184
  existingRoot.reasoning &&
183
185
  existingRoot.reasoning !== installedRoot.reasoning
184
186
  ) {
185
- backup.config.previous.reasoning = existingRoot.reasoning;
187
+ previousConfig.reasoning = existingRoot.reasoning;
186
188
  }
187
189
  if (
188
- backup.config.previous.provider === undefined &&
190
+ previousConfig.provider === undefined &&
189
191
  existingRoot.provider &&
190
192
  existingRoot.provider !== installedRoot.provider
191
193
  ) {
192
- backup.config.previous.provider = existingRoot.provider;
194
+ previousConfig.provider = existingRoot.provider;
193
195
  }
194
196
  backup.config.installed = installedRoot;
195
197
 
196
198
  backup.auth ??= {};
199
+ const authBackup = backup.auth;
197
200
  if (
198
- backup.auth.previousOpenaiKey === undefined &&
201
+ authBackup.previousOpenaiKey === undefined &&
199
202
  existingOpenaiKey &&
200
203
  existingOpenaiKey !== apiKey
201
204
  ) {
202
- backup.auth.previousOpenaiKey = existingOpenaiKey;
205
+ authBackup.previousOpenaiKey = existingOpenaiKey;
203
206
  }
204
- backup.auth.installedOpenaiKey = apiKey;
207
+ authBackup.installedOpenaiKey = apiKey;
205
208
  writeCodexBackup(backup);
206
209
 
207
210
  const mergedConfig = mergeCodexToml(existingConfig, {
@@ -248,11 +251,16 @@ export const registerCodexCommand = (program: Command) => {
248
251
  const authContent = authExists
249
252
  ? fs.readFileSync(authPath, "utf8").trim()
250
253
  : "";
251
- const authData = authExists
252
- ? authContent
253
- ? (JSON.parse(authContent) as Record<string, unknown>)
254
- : {}
255
- : null;
254
+
255
+ let authData: Record<string, unknown> | null = null;
256
+ if (authExists) {
257
+ if (authContent) {
258
+ authData = JSON.parse(authContent) as Record<string, unknown>;
259
+ } else {
260
+ authData = {};
261
+ }
262
+ }
263
+
256
264
  const authResult = authData
257
265
  ? removeAuthJson(authData, {
258
266
  installed: installedOpenaiKey,
@@ -283,4 +291,4 @@ export const registerCodexCommand = (program: Command) => {
283
291
  fs.unlinkSync(backupPath);
284
292
  }
285
293
  });
286
- };
294
+ }
package/src/cmd/env.ts CHANGED
@@ -32,10 +32,10 @@ type EnvCommandConfig = {
32
32
  vars: (apiKey: string) => EnvVars;
33
33
  };
34
34
 
35
- export const registerEnvCommand = (
35
+ export function registerEnvCommand(
36
36
  program: Command,
37
37
  config: EnvCommandConfig,
38
- ) => {
38
+ ): void {
39
39
  program
40
40
  .command(config.name)
41
41
  .description(config.description)
@@ -90,14 +90,18 @@ export const registerEnvCommand = (
90
90
  console.log(sourceLine);
91
91
  }
92
92
  });
93
- };
93
+ }
94
94
 
95
- export const buildOpenAIEnv = (apiKey: string): EnvVars => ({
96
- openaiBaseUrl: CODEX_BASE_URL,
97
- openaiApiKey: apiKey,
98
- });
95
+ export function buildOpenAIEnv(apiKey: string): EnvVars {
96
+ return {
97
+ openaiBaseUrl: CODEX_BASE_URL,
98
+ openaiApiKey: apiKey,
99
+ };
100
+ }
99
101
 
100
- export const buildAnthropicEnv = (apiKey: string): EnvVars => ({
101
- anthropicBaseUrl: CLAUDE_BASE_URL,
102
- anthropicApiKey: apiKey,
103
- });
102
+ export function buildAnthropicEnv(apiKey: string): EnvVars {
103
+ return {
104
+ anthropicBaseUrl: CLAUDE_BASE_URL,
105
+ anthropicApiKey: apiKey,
106
+ };
107
+ }
package/src/cmd/index.ts CHANGED
@@ -7,7 +7,7 @@ import { registerModelsCommands } from "./models";
7
7
  import { registerStatusCommand } from "./status";
8
8
  import { registerUsagesCommand } from "./usages";
9
9
 
10
- export const registerCommands = (program: Command) => {
10
+ export function registerCommands(program: Command): void {
11
11
  registerAuthCommands(program);
12
12
  registerCodexCommand(program);
13
13
  registerClaudeCommand(program);
@@ -15,4 +15,4 @@ export const registerCommands = (program: Command) => {
15
15
  registerModelsCommands(program);
16
16
  registerStatusCommand(program);
17
17
  registerUsagesCommand(program);
18
- };
18
+ }
package/src/cmd/keys.ts CHANGED
@@ -66,18 +66,41 @@ const requireInteractiveForSelection = () =>
66
66
  const requireInteractiveForAction = (action: string) =>
67
67
  requireInteractive(`Interactive mode required for keys ${action}.`);
68
68
 
69
+ type PromptKeyResult =
70
+ | { cancelled: true }
71
+ | { cancelled: false; name: string | undefined; enabled: boolean };
72
+
73
+ const promptKeyDetails = async (
74
+ initialName: string | undefined,
75
+ initialEnabled: boolean,
76
+ ): Promise<PromptKeyResult> => {
77
+ const nameResult = await promptKeyName(initialName);
78
+ if (nameResult.cancelled) return { cancelled: true };
79
+
80
+ const enabledResult = await promptKeyEnabled(initialEnabled);
81
+ if (enabledResult.cancelled) return { cancelled: true };
82
+
83
+ return {
84
+ cancelled: false,
85
+ name: nameResult.name,
86
+ enabled: enabledResult.enabled,
87
+ };
88
+ };
89
+
69
90
  const updateConsumer = async (
70
91
  consumerService: Pick<ConsumerService, "UpdateConsumer">,
71
92
  consumer: routercommonv1_Consumer,
72
93
  name: string | undefined,
73
94
  enabled: boolean | undefined,
74
95
  ) => {
75
- const updateMask = [
76
- name !== undefined && name !== consumer.name ? "name" : null,
77
- enabled !== undefined && enabled !== consumer.enabled ? "enabled" : null,
78
- ]
79
- .filter(Boolean)
80
- .join(",");
96
+ const updateMaskParts: string[] = [];
97
+ if (name !== undefined && name !== consumer.name) {
98
+ updateMaskParts.push("name");
99
+ }
100
+ if (enabled !== undefined && enabled !== consumer.enabled) {
101
+ updateMaskParts.push("enabled");
102
+ }
103
+ const updateMask = updateMaskParts.join(",");
81
104
  if (!updateMask) {
82
105
  return consumer;
83
106
  }
@@ -108,42 +131,32 @@ const listConsumers = async (
108
131
  outputConsumers(sorted, showApiKey);
109
132
  };
110
133
 
111
- const resolveConsumerForUpdate = async (
112
- consumerService: Pick<ConsumerService, "GetConsumer" | "ListConsumers">,
113
- id?: string,
114
- ) => {
115
- if (id) {
116
- return consumerService.GetConsumer({ id });
117
- }
118
- requireInteractiveForSelection();
119
- return await selectConsumerList(consumerService, "Select key to update");
120
- };
121
-
122
- const resolveConsumerForDelete = async (
134
+ const resolveConsumer = async (
123
135
  consumerService: Pick<ConsumerService, "GetConsumer" | "ListConsumers">,
136
+ message: string,
124
137
  id?: string,
125
138
  ) => {
126
139
  if (id) {
127
140
  return consumerService.GetConsumer({ id });
128
141
  }
129
142
  requireInteractiveForSelection();
130
- return await selectConsumerList(consumerService, "Select key to delete");
143
+ return await selectConsumerList(consumerService, message);
131
144
  };
132
145
 
133
146
  const createConsumer = async (
134
147
  consumerService: Pick<ConsumerService, "CreateConsumer" | "UpdateConsumer">,
135
148
  ) => {
136
149
  requireInteractiveForAction("create");
137
- const nameResult = await promptKeyName();
138
- if (nameResult.cancelled) return;
139
- const enabledResult = await promptKeyEnabled(true);
140
- if (enabledResult.cancelled) return;
150
+
151
+ const details = await promptKeyDetails(undefined, true);
152
+ if (details.cancelled) return;
153
+
141
154
  let consumer = await consumerService.CreateConsumer({});
142
155
  consumer = await updateConsumer(
143
156
  consumerService,
144
157
  consumer,
145
- nameResult.name,
146
- enabledResult.enabled,
158
+ details.name,
159
+ details.enabled,
147
160
  );
148
161
  outputConsumerTable(consumer, true);
149
162
  console.log("Please store this API key securely.");
@@ -157,17 +170,24 @@ const updateConsumerById = async (
157
170
  id?: string,
158
171
  ) => {
159
172
  requireInteractiveForAction("update");
160
- const selected = await resolveConsumerForUpdate(consumerService, id);
173
+ const selected = await resolveConsumer(
174
+ consumerService,
175
+ "Select key to update",
176
+ id,
177
+ );
161
178
  if (!selected?.id) return;
162
- const nameResult = await promptKeyName(selected.name);
163
- if (nameResult.cancelled) return;
164
- const enabledResult = await promptKeyEnabled(selected.enabled ?? true);
165
- if (enabledResult.cancelled) return;
179
+
180
+ const details = await promptKeyDetails(
181
+ selected.name,
182
+ selected.enabled ?? true,
183
+ );
184
+ if (details.cancelled) return;
185
+
166
186
  const consumer = await updateConsumer(
167
187
  consumerService,
168
188
  selected,
169
- nameResult.name,
170
- enabledResult.enabled,
189
+ details.name,
190
+ details.enabled,
171
191
  );
172
192
  outputConsumerTable(consumer, false);
173
193
  };
@@ -180,7 +200,11 @@ const deleteConsumerById = async (
180
200
  id?: string,
181
201
  ) => {
182
202
  requireInteractiveForAction("delete");
183
- const selected = await resolveConsumerForDelete(consumerService, id);
203
+ const selected = await resolveConsumer(
204
+ consumerService,
205
+ "Select key to delete",
206
+ id,
207
+ );
184
208
  if (!selected?.id) return;
185
209
  const confirmed = await confirmDelete(selected);
186
210
  if (!confirmed) return;
@@ -188,7 +212,7 @@ const deleteConsumerById = async (
188
212
  outputConsumerTable(selected, false);
189
213
  };
190
214
 
191
- export const registerKeysCommands = (program: Command) => {
215
+ export function registerKeysCommands(program: Command): void {
192
216
  const keys = program.command("keys").description("Manage API keys");
193
217
  keys.option("--show", "Show full API keys");
194
218
  keys.allowExcessArguments(false);
@@ -233,4 +257,4 @@ export const registerKeysCommands = (program: Command) => {
233
257
  const { consumerService } = createApiClients({});
234
258
  await deleteConsumerById(consumerService, id);
235
259
  });
236
- };
260
+ }
package/src/cmd/models.ts CHANGED
@@ -5,20 +5,22 @@ import type { routercommonv1_Model } from "../generated/router/dashboard/v1";
5
5
 
6
6
  const modelHeaders = ["ID", "NAME", "AUTHOR", "ENABLED", "UPDATED_AT"];
7
7
 
8
- const modelRow = (model: routercommonv1_Model) => [
9
- String(model.id ?? ""),
10
- String(model.name ?? ""),
11
- String(model.author ?? ""),
12
- String(model.enabled ?? ""),
13
- String(model.updatedAt ?? ""),
14
- ];
15
-
16
- const outputModels = (models: routercommonv1_Model[]) => {
8
+ function formatModelRow(model: routercommonv1_Model): string[] {
9
+ return [
10
+ String(model.id ?? ""),
11
+ String(model.name ?? ""),
12
+ String(model.author ?? ""),
13
+ String(model.enabled ?? ""),
14
+ String(model.updatedAt ?? ""),
15
+ ];
16
+ }
17
+
18
+ function outputModels(models: routercommonv1_Model[]): void {
17
19
  console.log("🧠 Models");
18
- console.log(renderTable(modelHeaders, models.map(modelRow)));
19
- };
20
+ console.log(renderTable(modelHeaders, models.map(formatModelRow)));
21
+ }
20
22
 
21
- const listModels = async () => {
23
+ async function listModels(): Promise<void> {
22
24
  const { modelService } = createApiClients({});
23
25
  const res = await modelService.ListModels({
24
26
  pageSize: undefined,
@@ -26,24 +28,20 @@ const listModels = async () => {
26
28
  filter: undefined,
27
29
  });
28
30
  const models = res?.models ?? [];
31
+
29
32
  if (models.length === 0) {
30
33
  console.log("😕 No models found");
31
34
  return;
32
35
  }
33
- outputModels(models);
34
- };
35
-
36
- export const registerModelsCommands = (program: Command) => {
37
- const models = program.command("models").description("List models");
38
36
 
39
- models.action(async () => {
40
- await listModels();
41
- });
37
+ outputModels(models);
38
+ }
42
39
 
43
- models
44
- .command("list")
40
+ export function registerModelsCommands(program: Command): void {
41
+ const models = program
42
+ .command("models")
45
43
  .description("List models")
46
- .action(async () => {
47
- await listModels();
48
- });
49
- };
44
+ .action(listModels);
45
+
46
+ models.command("list").description("List models").action(listModels);
47
+ }
package/src/cmd/status.ts CHANGED
@@ -4,103 +4,154 @@ import { getAuthStatus } from "../core/auth";
4
4
 
5
5
  const LABEL_WIDTH = 10;
6
6
 
7
- const formatLine = (label: string, value: string | number | undefined) => {
8
- if (value == null || value === "") return null;
9
- return ` ${label.padEnd(LABEL_WIDTH, " ")}: ${value}`;
7
+ type LineValue = string | number | undefined;
8
+
9
+ type SectionLine = readonly [label: string, value: LineValue];
10
+ type SectionItem = SectionLine | false | null | undefined;
11
+
12
+ type AuthStatus = "logged_in" | "logged_out";
13
+
14
+ type Subscription = {
15
+ plan?: {
16
+ name?: string;
17
+ requestPerMinute?: number;
18
+ tokenPerMinute?: string | number;
19
+ };
20
+ status?: string;
21
+ startAt?: string;
22
+ endAt?: string;
10
23
  };
11
24
 
12
- const formatAuthStatus = (status: "logged_in" | "logged_out") =>
13
- status === "logged_in" ? "✅ Logged in" : "❌ Logged out";
25
+ function line(label: string, value: LineValue): SectionLine {
26
+ return [label, value];
27
+ }
28
+
29
+ function formatLine(label: string, value: LineValue): string | null {
30
+ if (value == null || value === "") {
31
+ return null;
32
+ }
33
+
34
+ return ` ${label.padEnd(LABEL_WIDTH, " ")}: ${value}`;
35
+ }
36
+
37
+ function renderSection(title: string, items: SectionItem[]): string {
38
+ const lines: string[] = [];
39
+
40
+ for (const item of items) {
41
+ if (!item) {
42
+ continue;
43
+ }
44
+
45
+ const rendered = formatLine(item[0], item[1]);
46
+ if (rendered) {
47
+ lines.push(rendered);
48
+ }
49
+ }
50
+
51
+ return [title, ...lines].join("\n");
52
+ }
53
+
54
+ function formatAuthStatus(status: AuthStatus): string {
55
+ if (status === "logged_in") {
56
+ return "✅ Logged in";
57
+ }
58
+
59
+ return "❌ Logged out";
60
+ }
61
+
62
+ function formatToken(token?: string): string | undefined {
63
+ if (!token) {
64
+ return undefined;
65
+ }
14
66
 
15
- const formatToken = (token?: string) => {
16
- if (!token) return undefined;
17
67
  const trimmed = token.trim();
18
- if (trimmed.length <= 12) return trimmed;
68
+ if (trimmed.length <= 12) {
69
+ return trimmed;
70
+ }
71
+
19
72
  return `${trimmed.slice(0, 4)}...${trimmed.slice(-4)}`;
20
- };
73
+ }
74
+
75
+ function formatWindow(startAt?: string, endAt?: string): string | undefined {
76
+ if (!startAt && !endAt) {
77
+ return undefined;
78
+ }
79
+
80
+ if (startAt && endAt) {
81
+ return `${startAt} → ${endAt}`;
82
+ }
83
+
84
+ if (startAt) {
85
+ return `${startAt} →`;
86
+ }
21
87
 
22
- const formatWindow = (startAt?: string, endAt?: string) => {
23
- if (!startAt && !endAt) return undefined;
24
- if (startAt && endAt) return `${startAt} → ${endAt}`;
25
- if (startAt) return `${startAt} →`;
26
88
  return `→ ${endAt}`;
27
- };
89
+ }
28
90
 
29
- const formatLimits = (
91
+ function formatLimits(
30
92
  requestPerMinute?: number,
31
93
  tokenPerMinute?: string | number,
32
- ) => {
94
+ ): string | undefined {
33
95
  const parts: string[] = [];
96
+
34
97
  if (typeof requestPerMinute === "number") {
35
98
  parts.push(`${requestPerMinute} req/min`);
36
99
  }
100
+
37
101
  if (tokenPerMinute) {
38
102
  parts.push(`${tokenPerMinute} tok/min`);
39
103
  }
40
- if (parts.length === 0) return undefined;
104
+
105
+ if (parts.length === 0) {
106
+ return undefined;
107
+ }
108
+
41
109
  return parts.join(" · ");
42
- };
110
+ }
43
111
 
44
- const renderAuthSection = () => {
112
+ function renderAuthSection(): string {
45
113
  const status = getAuthStatus();
46
- const lines = [
47
- formatLine("Status", formatAuthStatus(status.status)),
48
- status.status === "logged_in"
49
- ? formatLine("Expires", status.expiresAt)
50
- : null,
51
- status.status === "logged_in"
52
- ? formatLine("TokenType", status.tokenType)
53
- : null,
54
- status.status === "logged_in"
55
- ? formatLine("Access", formatToken(status.accessToken))
56
- : null,
57
- status.status === "logged_in"
58
- ? formatLine("Refresh", formatToken(status.refreshToken))
59
- : null,
60
- ].filter(Boolean) as string[];
61
- return ["🔐 Auth", ...lines].join("\n");
62
- };
114
+ const isLoggedIn = status.status === "logged_in";
115
+
116
+ return renderSection("🔐 Auth", [
117
+ line("Status", formatAuthStatus(status.status)),
118
+ isLoggedIn && line("Expires", status.expiresAt),
119
+ isLoggedIn && line("TokenType", status.tokenType),
120
+ isLoggedIn && line("Access", formatToken(status.accessToken)),
121
+ isLoggedIn && line("Refresh", formatToken(status.refreshToken)),
122
+ ]);
123
+ }
63
124
 
64
- const renderSubscriptionSection = (
65
- subscription: {
66
- plan?: {
67
- name?: string;
68
- requestPerMinute?: number;
69
- tokenPerMinute?: string | number;
70
- };
71
- status?: string;
72
- startAt?: string;
73
- endAt?: string;
74
- } | null,
75
- ) => {
125
+ function renderSubscriptionSection(subscription: Subscription | null): string {
76
126
  if (!subscription) {
77
- return ["📦 Subscription", formatLine("Status", "No active subscription")]
78
- .filter(Boolean)
79
- .join("\n");
127
+ return renderSection("📦 Subscription", [
128
+ line("Status", "No active subscription"),
129
+ ]);
80
130
  }
131
+
81
132
  const limits = formatLimits(
82
133
  subscription.plan?.requestPerMinute,
83
134
  subscription.plan?.tokenPerMinute,
84
135
  );
85
- const windowLabel = formatWindow(subscription.startAt, subscription.endAt);
86
- const lines = [
87
- formatLine("Plan", subscription.plan?.name),
88
- formatLine("Status", subscription.status),
89
- formatLine("Window", windowLabel),
90
- formatLine("Limits", limits),
91
- ].filter(Boolean) as string[];
92
- return ["📦 Subscription", ...lines].join("\n");
93
- };
94
136
 
95
- export const registerStatusCommand = (program: Command) => {
137
+ return renderSection("📦 Subscription", [
138
+ line("Plan", subscription.plan?.name),
139
+ line("Status", subscription.status),
140
+ line("Window", formatWindow(subscription.startAt, subscription.endAt)),
141
+ line("Limits", limits),
142
+ ]);
143
+ }
144
+
145
+ export function registerStatusCommand(program: Command): void {
96
146
  program
97
147
  .command("status")
98
148
  .description("Show login and subscription status")
99
149
  .action(async () => {
100
150
  const { subscriptionService } = createApiClients({});
101
151
  const subscription = await subscriptionService.CurrentSubscription({});
152
+
102
153
  console.log(renderAuthSection());
103
154
  console.log("");
104
155
  console.log(renderSubscriptionSection(subscription));
105
156
  });
106
- };
157
+ }
package/src/cmd/usages.ts CHANGED
@@ -8,7 +8,7 @@ type UsageListResponse = {
8
8
  nextPageToken?: string;
9
9
  };
10
10
 
11
- const collectUsages = async () => {
11
+ async function collectUsages(): Promise<ReturnType<typeof aggregateUsages>> {
12
12
  const { usageService } = createApiClients({});
13
13
  const res = (await usageService.ListUsage({
14
14
  pageSize: 7,
@@ -16,9 +16,9 @@ const collectUsages = async () => {
16
16
  })) as UsageListResponse;
17
17
  const usages = res?.usages ?? [];
18
18
  return aggregateUsages(usages, 7);
19
- };
19
+ }
20
20
 
21
- export const registerUsagesCommand = (program: Command) => {
21
+ export function registerUsagesCommand(program: Command): void {
22
22
  program
23
23
  .command("usages")
24
24
  .description("Show recent usage")
@@ -26,4 +26,4 @@ export const registerUsagesCommand = (program: Command) => {
26
26
  const aggregated = await collectUsages();
27
27
  console.log(renderUsageChart(aggregated));
28
28
  });
29
- };
29
+ }