berget 2.2.7 → 2.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/.github/workflows/publish.yml +6 -6
  2. package/.github/workflows/test.yml +1 -1
  3. package/.prettierrc +5 -3
  4. package/dist/index.js +24 -25
  5. package/dist/package.json +5 -3
  6. package/dist/src/agents/app.js +8 -8
  7. package/dist/src/agents/backend.js +3 -3
  8. package/dist/src/agents/devops.js +8 -8
  9. package/dist/src/agents/frontend.js +3 -3
  10. package/dist/src/agents/fullstack.js +3 -3
  11. package/dist/src/agents/index.js +18 -18
  12. package/dist/src/agents/quality.js +8 -8
  13. package/dist/src/agents/security.js +8 -8
  14. package/dist/src/client.js +115 -127
  15. package/dist/src/commands/api-keys.js +195 -202
  16. package/dist/src/commands/auth.js +16 -25
  17. package/dist/src/commands/autocomplete.js +8 -8
  18. package/dist/src/commands/billing.js +10 -19
  19. package/dist/src/commands/chat.js +139 -170
  20. package/dist/src/commands/clusters.js +21 -30
  21. package/dist/src/commands/code/__tests__/auth-sync.test.js +189 -186
  22. package/dist/src/commands/code/__tests__/fake-api-key-service.js +3 -13
  23. package/dist/src/commands/code/__tests__/fake-auth-service.js +21 -29
  24. package/dist/src/commands/code/__tests__/fake-command-runner.js +22 -33
  25. package/dist/src/commands/code/__tests__/fake-file-store.js +19 -41
  26. package/dist/src/commands/code/__tests__/fake-prompter.js +81 -97
  27. package/dist/src/commands/code/__tests__/setup-flow.test.js +295 -295
  28. package/dist/src/commands/code/adapters/clack-prompter.js +15 -32
  29. package/dist/src/commands/code/adapters/fs-file-store.js +25 -44
  30. package/dist/src/commands/code/adapters/spawn-command-runner.js +27 -41
  31. package/dist/src/commands/code/auth-sync.js +215 -228
  32. package/dist/src/commands/code/errors.js +15 -12
  33. package/dist/src/commands/code/setup.js +390 -425
  34. package/dist/src/commands/code.js +279 -294
  35. package/dist/src/commands/index.js +5 -5
  36. package/dist/src/commands/models.js +16 -25
  37. package/dist/src/commands/users.js +9 -18
  38. package/dist/src/constants/command-structure.js +138 -138
  39. package/dist/src/services/api-key-service.js +132 -152
  40. package/dist/src/services/auth-service.js +81 -95
  41. package/dist/src/services/browser-auth.js +121 -131
  42. package/dist/src/services/chat-service.js +369 -386
  43. package/dist/src/services/cluster-service.js +47 -62
  44. package/dist/src/services/collaborator-service.js +9 -21
  45. package/dist/src/services/flux-service.js +13 -25
  46. package/dist/src/services/helm-service.js +9 -21
  47. package/dist/src/services/kubectl-service.js +15 -29
  48. package/dist/src/utils/config-checker.js +7 -7
  49. package/dist/src/utils/config-loader.js +109 -109
  50. package/dist/src/utils/default-api-key.js +129 -139
  51. package/dist/src/utils/env-manager.js +55 -66
  52. package/dist/src/utils/error-handler.js +62 -62
  53. package/dist/src/utils/logger.js +74 -67
  54. package/dist/src/utils/markdown-renderer.js +28 -28
  55. package/dist/src/utils/opencode-validator.js +67 -69
  56. package/dist/src/utils/token-manager.js +67 -65
  57. package/dist/tests/commands/chat.test.js +30 -39
  58. package/dist/tests/commands/code.test.js +186 -195
  59. package/dist/tests/utils/config-loader.test.js +107 -107
  60. package/dist/tests/utils/env-manager.test.js +81 -90
  61. package/dist/tests/utils/opencode-validator.test.js +42 -41
  62. package/dist/vitest.config.js +1 -1
  63. package/eslint.config.mjs +50 -30
  64. package/index.ts +30 -31
  65. package/package.json +5 -3
  66. package/src/agents/app.ts +9 -9
  67. package/src/agents/backend.ts +4 -4
  68. package/src/agents/devops.ts +9 -9
  69. package/src/agents/frontend.ts +4 -4
  70. package/src/agents/fullstack.ts +4 -4
  71. package/src/agents/index.ts +27 -25
  72. package/src/agents/quality.ts +9 -9
  73. package/src/agents/security.ts +9 -9
  74. package/src/agents/types.ts +10 -10
  75. package/src/client.ts +85 -77
  76. package/src/commands/api-keys.ts +190 -185
  77. package/src/commands/auth.ts +15 -14
  78. package/src/commands/autocomplete.ts +10 -10
  79. package/src/commands/billing.ts +13 -12
  80. package/src/commands/chat.ts +145 -142
  81. package/src/commands/clusters.ts +20 -19
  82. package/src/commands/code/__tests__/auth-sync.test.ts +176 -175
  83. package/src/commands/code/__tests__/fake-api-key-service.ts +2 -2
  84. package/src/commands/code/__tests__/fake-auth-service.ts +18 -18
  85. package/src/commands/code/__tests__/fake-command-runner.ts +28 -22
  86. package/src/commands/code/__tests__/fake-file-store.ts +15 -15
  87. package/src/commands/code/__tests__/fake-prompter.ts +86 -85
  88. package/src/commands/code/__tests__/setup-flow.test.ts +253 -251
  89. package/src/commands/code/adapters/clack-prompter.ts +32 -30
  90. package/src/commands/code/adapters/fs-file-store.ts +18 -17
  91. package/src/commands/code/adapters/spawn-command-runner.ts +20 -15
  92. package/src/commands/code/auth-sync.ts +210 -210
  93. package/src/commands/code/errors.ts +11 -11
  94. package/src/commands/code/ports/auth-services.ts +7 -7
  95. package/src/commands/code/ports/command-runner.ts +2 -2
  96. package/src/commands/code/ports/file-store.ts +3 -3
  97. package/src/commands/code/ports/prompter.ts +13 -13
  98. package/src/commands/code/setup.ts +408 -406
  99. package/src/commands/code.ts +288 -287
  100. package/src/commands/index.ts +11 -10
  101. package/src/commands/models.ts +19 -18
  102. package/src/commands/users.ts +11 -10
  103. package/src/constants/command-structure.ts +159 -159
  104. package/src/services/api-key-service.ts +85 -85
  105. package/src/services/auth-service.ts +55 -54
  106. package/src/services/browser-auth.ts +62 -62
  107. package/src/services/chat-service.ts +169 -170
  108. package/src/services/cluster-service.ts +28 -28
  109. package/src/services/collaborator-service.ts +6 -6
  110. package/src/services/flux-service.ts +17 -17
  111. package/src/services/helm-service.ts +11 -11
  112. package/src/services/kubectl-service.ts +12 -12
  113. package/src/types/api.d.ts +1933 -1933
  114. package/src/types/json.d.ts +1 -1
  115. package/src/utils/config-checker.ts +6 -6
  116. package/src/utils/config-loader.ts +130 -129
  117. package/src/utils/default-api-key.ts +81 -80
  118. package/src/utils/env-manager.ts +37 -37
  119. package/src/utils/error-handler.ts +64 -64
  120. package/src/utils/logger.ts +72 -66
  121. package/src/utils/markdown-renderer.ts +28 -28
  122. package/src/utils/opencode-validator.ts +72 -71
  123. package/src/utils/token-manager.ts +69 -68
  124. package/tests/commands/chat.test.ts +32 -31
  125. package/tests/commands/code.test.ts +182 -181
  126. package/tests/utils/config-loader.test.ts +111 -110
  127. package/tests/utils/env-manager.test.ts +83 -79
  128. package/tests/utils/opencode-validator.test.ts +43 -42
  129. package/tsconfig.json +2 -1
  130. package/vitest.config.ts +2 -2
@@ -1,47 +1,19 @@
1
- import { Command } from "commander";
2
- import chalk from "chalk";
3
- import { ApiKeyService, ApiKey } from "../services/api-key-service";
4
- import { handleError } from "../utils/error-handler";
5
- import { DefaultApiKeyManager } from "../utils/default-api-key";
1
+ import chalk from 'chalk';
2
+ import { Command } from 'commander';
6
3
 
7
- // Helper functions for better date formatting
8
- function formatDate(dateString: string): string {
9
- const date = new Date(dateString);
10
- const now = new Date();
11
- const diffTime = Math.abs(now.getTime() - date.getTime());
12
- const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
13
-
14
- if (diffDays === 0) return chalk.green("Today");
15
- if (diffDays === 1) return chalk.yellow("Yesterday");
16
- if (diffDays < 7) return chalk.yellow(`${diffDays} days ago`);
17
- if (diffDays < 30) return chalk.blue(`${Math.floor(diffDays / 7)} weeks ago`);
18
- if (diffDays < 365) return chalk.magenta(`${Math.floor(diffDays / 30)} months ago`);
19
- return chalk.gray(`${Math.floor(diffDays / 365)} years ago`);
20
- }
21
-
22
- function formatLastUsed(dateString: string): string {
23
- const date = new Date(dateString);
24
- const now = new Date();
25
- const diffTime = Math.abs(now.getTime() - date.getTime());
26
- const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
27
-
28
- if (diffDays === 0) return chalk.green("Today");
29
- if (diffDays === 1) return chalk.yellow("Yesterday");
30
- if (diffDays < 7) return chalk.yellow(`${diffDays} days ago`);
31
- if (diffDays < 30) return chalk.blue(`${Math.floor(diffDays / 7)} weeks ago`);
32
- if (diffDays < 365) return chalk.magenta(`${Math.floor(diffDays / 30)} months ago`);
33
- return chalk.gray(`${Math.floor(diffDays / 365)} years ago`);
34
- }
4
+ import { ApiKey, ApiKeyService } from '../services/api-key-service';
5
+ import { DefaultApiKeyManager } from '../utils/default-api-key';
6
+ import { handleError } from '../utils/error-handler';
35
7
 
36
8
  /**
37
9
  * Register API key commands
38
10
  */
39
11
  export function registerApiKeyCommands(program: Command): void {
40
- const apiKey = program.command(ApiKeyService.COMMAND_GROUP).description("Manage API keys");
12
+ const apiKey = program.command(ApiKeyService.COMMAND_GROUP).description('Manage API keys');
41
13
 
42
14
  apiKey
43
15
  .command(ApiKeyService.COMMANDS.LIST)
44
- .description("List all API keys")
16
+ .description('List all API keys')
45
17
  .action(async () => {
46
18
  try {
47
19
  const apiKeyService = ApiKeyService.getInstance();
@@ -49,13 +21,15 @@ export function registerApiKeyCommands(program: Command): void {
49
21
 
50
22
  if (keys.length === 0) {
51
23
  console.log(
52
- chalk.yellow("No API keys found. Create one with `berget api-key create --name <name>`")
24
+ chalk.yellow(
25
+ 'No API keys found. Create one with `berget api-key create --name <name>`',
26
+ ),
53
27
  );
54
28
  return;
55
29
  }
56
30
 
57
- console.log(chalk.bold("🔑 Your API keys:"));
58
- console.log("");
31
+ console.log(chalk.bold('🔑 Your API keys:'));
32
+ console.log('');
59
33
 
60
34
  // Create a more readable table with better spacing and colors
61
35
  const idWidth = 10;
@@ -66,116 +40,118 @@ export function registerApiKeyCommands(program: Command): void {
66
40
  const usedWidth = 15;
67
41
 
68
42
  console.log(
69
- chalk.dim("ID".padEnd(idWidth)) +
70
- chalk.dim("NAME".padEnd(nameWidth)) +
71
- chalk.dim("PREFIX".padEnd(prefixWidth)) +
72
- chalk.dim("STATUS".padEnd(statusWidth)) +
73
- chalk.dim("CREATED".padEnd(createdWidth)) +
74
- chalk.dim("LAST USED")
43
+ chalk.dim('ID'.padEnd(idWidth)) +
44
+ chalk.dim('NAME'.padEnd(nameWidth)) +
45
+ chalk.dim('PREFIX'.padEnd(prefixWidth)) +
46
+ chalk.dim('STATUS'.padEnd(statusWidth)) +
47
+ chalk.dim('CREATED'.padEnd(createdWidth)) +
48
+ chalk.dim('LAST USED'),
75
49
  );
76
50
 
77
51
  console.log(
78
52
  chalk.dim(
79
- "".repeat(
80
- idWidth + nameWidth + prefixWidth + statusWidth + createdWidth + usedWidth + 5
81
- )
82
- )
53
+ ''.repeat(
54
+ idWidth + nameWidth + prefixWidth + statusWidth + createdWidth + usedWidth + 5,
55
+ ),
56
+ ),
83
57
  );
84
58
 
85
59
  keys.forEach((key: ApiKey) => {
86
- const lastUsed = key.lastUsed ? formatLastUsed(key.lastUsed) : chalk.yellow("Never used");
87
- const status = key.active ? chalk.green("● Active") : chalk.red("● Inactive");
60
+ const lastUsed = key.lastUsed ? formatLastUsed(key.lastUsed) : chalk.yellow('Never used');
61
+ const status = key.active ? chalk.green('● Active') : chalk.red('● Inactive');
88
62
 
89
63
  // Show only first 8 characters of ID for easier reading
90
- const shortId = chalk.cyan(String(key.id).substring(0, 8));
64
+ const shortId = chalk.cyan(String(key.id).slice(0, 8));
91
65
 
92
66
  // Format the prefix with better truncation
93
- const prefixStr =
67
+ const prefixString =
94
68
  key.prefix.length > prefixWidth
95
- ? key.prefix.substring(0, prefixWidth - 3) + "..."
69
+ ? key.prefix.slice(0, Math.max(0, prefixWidth - 3)) + '...'
96
70
  : chalk.gray(key.prefix);
97
71
 
98
72
  // Truncate name if too long
99
- const nameStr =
100
- key.name.length > nameWidth ? key.name.substring(0, nameWidth - 3) + "..." : key.name;
73
+ const nameString =
74
+ key.name.length > nameWidth
75
+ ? key.name.slice(0, Math.max(0, nameWidth - 3)) + '...'
76
+ : key.name;
101
77
 
102
78
  // Format created date
103
79
  const createdDate = formatDate(key.created);
104
80
 
105
81
  console.log(
106
82
  shortId.padEnd(idWidth) +
107
- nameStr.padEnd(nameWidth) +
108
- prefixStr.padEnd(prefixWidth) +
83
+ nameString.padEnd(nameWidth) +
84
+ prefixString.padEnd(prefixWidth) +
109
85
  status.padEnd(statusWidth) +
110
86
  createdDate.padEnd(createdWidth) +
111
- lastUsed
87
+ lastUsed,
112
88
  );
113
89
  });
114
90
 
115
- console.log("");
116
- console.log(chalk.dim("Use `berget api-key create --name <name>` to create a new API key"));
117
- console.log(chalk.dim("Use `berget api-key delete <id>` to delete an API key"));
118
- console.log(chalk.dim("Use `berget api-key rotate <id>` to rotate an API key"));
91
+ console.log('');
92
+ console.log(chalk.dim('Use `berget api-key create --name <name>` to create a new API key'));
93
+ console.log(chalk.dim('Use `berget api-key delete <id>` to delete an API key'));
94
+ console.log(chalk.dim('Use `berget api-key rotate <id>` to rotate an API key'));
119
95
  } catch (error) {
120
- handleError("Failed to list API keys", error);
96
+ handleError('Failed to list API keys', error);
121
97
  }
122
98
  });
123
99
 
124
100
  apiKey
125
101
  .command(ApiKeyService.COMMANDS.CREATE)
126
- .description("Create a new API key")
127
- .option("--name <name>", "Name of the API key")
128
- .option("--description <description>", "Description of the API key")
102
+ .description('Create a new API key')
103
+ .option('--name <name>', 'Name of the API key')
104
+ .option('--description <description>', 'Description of the API key')
129
105
 
130
- .action(async options => {
106
+ .action(async (options) => {
131
107
  try {
132
108
  if (!options.name) {
133
- console.error(chalk.red("Error: --name is required"));
134
- console.log("");
135
- console.log("Usage: berget api-key create --name <name> [--description <description>]");
109
+ console.error(chalk.red('Error: --name is required'));
110
+ console.log('');
111
+ console.log('Usage: berget api-key create --name <name> [--description <description>]');
136
112
  return;
137
113
  }
138
114
 
139
- console.log(chalk.blue("Creating API key..."));
115
+ console.log(chalk.blue('Creating API key...'));
140
116
 
141
117
  const apiKeyService = ApiKeyService.getInstance();
142
118
  const result = await apiKeyService.create({
143
- name: options.name,
144
119
  description: options.description,
120
+ name: options.name,
145
121
  });
146
122
 
147
- console.log("");
148
- console.log(chalk.green("✓ API key created"));
149
- console.log("");
150
- console.log(chalk.bold("API key details:"));
151
- console.log("");
152
- console.log(`${chalk.dim("ID:")} ${result.id}`);
153
- console.log(`${chalk.dim("Name:")} ${result.name}`);
123
+ console.log('');
124
+ console.log(chalk.green('✓ API key created'));
125
+ console.log('');
126
+ console.log(chalk.bold('API key details:'));
127
+ console.log('');
128
+ console.log(`${chalk.dim('ID:')} ${result.id}`);
129
+ console.log(`${chalk.dim('Name:')} ${result.name}`);
154
130
  if (result.description) {
155
- console.log(`${chalk.dim("Description:")} ${result.description}`);
131
+ console.log(`${chalk.dim('Description:')} ${result.description}`);
156
132
  }
157
- console.log(`${chalk.dim("Created:")} ${new Date(result.created).toLocaleString()}`);
158
- console.log("");
159
- console.log(chalk.bold("API key:"));
133
+ console.log(`${chalk.dim('Created:')} ${new Date(result.created).toLocaleString()}`);
134
+ console.log('');
135
+ console.log(chalk.bold('API key:'));
160
136
  console.log(chalk.cyan(result.key));
161
- console.log("");
162
- console.log(chalk.yellow("⚠️ IMPORTANT: Save this API key in a secure location."));
163
- console.log(chalk.yellow(" It will not be displayed again."));
137
+ console.log('');
138
+ console.log(chalk.yellow('⚠️ IMPORTANT: Save this API key in a secure location.'));
139
+ console.log(chalk.yellow(' It will not be displayed again.'));
164
140
 
165
- console.log("");
141
+ console.log('');
166
142
  console.log(
167
- chalk.dim("Use this key in your applications to authenticate with the Berget API.")
143
+ chalk.dim('Use this key in your applications to authenticate with the Berget API.'),
168
144
  );
169
145
  } catch (error) {
170
- handleError("Failed to create API key", error);
146
+ handleError('Failed to create API key', error);
171
147
  }
172
148
  });
173
149
 
174
150
  apiKey
175
151
  .command(ApiKeyService.COMMANDS.DELETE)
176
- .description("Delete an API key")
177
- .argument("<identifier>", "ID (first 8 chars), full ID, or name of the API key to delete")
178
- .action(async identifier => {
152
+ .description('Delete an API key')
153
+ .argument('<identifier>', 'ID (first 8 chars), full ID, or name of the API key to delete')
154
+ .action(async (identifier) => {
179
155
  try {
180
156
  const apiKeyService = ApiKeyService.getInstance();
181
157
 
@@ -190,110 +166,110 @@ export function registerApiKeyCommands(program: Command): void {
190
166
 
191
167
  // Check for exact matches first (full ID or exact name)
192
168
  let exactMatches = keys.filter(
193
- key => String(key.id) === identifier || key.name === identifier
169
+ (key) => String(key.id) === identifier || key.name === identifier,
194
170
  );
195
171
 
196
172
  // If no exact matches, check for short ID matches
197
173
  if (exactMatches.length === 0) {
198
- exactMatches = keys.filter(key => String(key.id).substring(0, 8) === identifier);
174
+ exactMatches = keys.filter((key) => String(key.id).slice(0, 8) === identifier);
199
175
  }
200
176
 
201
177
  // If still no matches, check for partial name matches
202
178
  if (exactMatches.length === 0) {
203
- exactMatches = keys.filter(key =>
204
- key.name.toLowerCase().includes(identifier.toLowerCase())
179
+ exactMatches = keys.filter((key) =>
180
+ key.name.toLowerCase().includes(identifier.toLowerCase()),
205
181
  );
206
182
  }
207
183
 
208
184
  // Handle multiple matches
209
185
  if (exactMatches.length > 1) {
210
186
  console.error(chalk.red(`Error: Multiple API keys found matching "${identifier}"`));
211
- console.log("");
212
- console.log("Please be more specific. Matching keys:");
213
- exactMatches.forEach(key => {
214
- const shortId = String(key.id).substring(0, 8);
187
+ console.log('');
188
+ console.log('Please be more specific. Matching keys:');
189
+ for (const key of exactMatches) {
190
+ const shortId = String(key.id).slice(0, 8);
215
191
  console.log(` ${shortId.padEnd(8)} ${key.name}`);
216
- });
217
- console.log("");
218
- console.log("Use the first 8 characters of the ID to specify which key to delete.");
192
+ }
193
+ console.log('');
194
+ console.log('Use the first 8 characters of the ID to specify which key to delete.');
219
195
  return;
220
196
  }
221
197
 
222
198
  // Handle no matches
223
199
  if (exactMatches.length === 0) {
224
200
  console.error(chalk.red(`Error: No API key found matching "${identifier}"`));
225
- console.log("");
226
- console.log("Available API keys:");
227
- keys.forEach(key => {
228
- const shortId = String(key.id).substring(0, 8);
201
+ console.log('');
202
+ console.log('Available API keys:');
203
+ for (const key of keys) {
204
+ const shortId = String(key.id).slice(0, 8);
229
205
  console.log(` ${shortId.padEnd(8)} ${key.name}`);
230
- });
231
- console.log("");
232
- console.log("Use the first 8 characters of the ID, full ID, or name to delete.");
206
+ }
207
+ console.log('');
208
+ console.log('Use the first 8 characters of the ID, full ID, or name to delete.');
233
209
  return;
234
210
  }
235
211
 
236
212
  const matchingKey = exactMatches[0];
237
213
 
238
214
  const keyId = String(matchingKey.id);
239
- const shortId = keyId.substring(0, 8);
215
+ const shortId = keyId.slice(0, 8);
240
216
 
241
217
  console.log(chalk.blue(`Deleting API key ${shortId} (${matchingKey.name})...`));
242
218
 
243
219
  await apiKeyService.delete(keyId);
244
220
 
245
221
  console.log(chalk.green(`✓ API key ${shortId} (${matchingKey.name}) has been deleted`));
246
- console.log("");
222
+ console.log('');
247
223
  console.log(
248
- chalk.dim("Applications using this key will no longer be able to authenticate.")
224
+ chalk.dim('Applications using this key will no longer be able to authenticate.'),
249
225
  );
250
- console.log(chalk.dim("Use `berget api-key list` to see your remaining API keys."));
226
+ console.log(chalk.dim('Use `berget api-key list` to see your remaining API keys.'));
251
227
  } catch (error) {
252
- handleError("Failed to delete API key", error);
228
+ handleError('Failed to delete API key', error);
253
229
  }
254
230
  });
255
231
 
256
232
  apiKey
257
233
  .command(ApiKeyService.COMMANDS.ROTATE)
258
- .description("Rotate an API key (creates a new one and invalidates the old one)")
259
- .argument("<id>", "ID of the API key to rotate")
260
- .action(async id => {
234
+ .description('Rotate an API key (creates a new one and invalidates the old one)')
235
+ .argument('<id>', 'ID of the API key to rotate')
236
+ .action(async (id) => {
261
237
  try {
262
238
  console.log(chalk.blue(`Rotating API key ${id}...`));
263
- console.log(chalk.dim("This will invalidate the old key and generate a new one."));
239
+ console.log(chalk.dim('This will invalidate the old key and generate a new one.'));
264
240
 
265
241
  const apiKeyService = ApiKeyService.getInstance();
266
242
  const result = await apiKeyService.rotate(id);
267
243
 
268
- console.log("");
269
- console.log(chalk.green("✓ API key rotated"));
270
- console.log("");
271
- console.log(chalk.bold("New API key details:"));
272
- console.log("");
273
- console.log(`${chalk.dim("ID:")} ${result.id}`);
274
- console.log(`${chalk.dim("Name:")} ${result.name}`);
244
+ console.log('');
245
+ console.log(chalk.green('✓ API key rotated'));
246
+ console.log('');
247
+ console.log(chalk.bold('New API key details:'));
248
+ console.log('');
249
+ console.log(`${chalk.dim('ID:')} ${result.id}`);
250
+ console.log(`${chalk.dim('Name:')} ${result.name}`);
275
251
  if (result.description) {
276
- console.log(`${chalk.dim("Description:")} ${result.description}`);
252
+ console.log(`${chalk.dim('Description:')} ${result.description}`);
277
253
  }
278
- console.log(`${chalk.dim("Created:")} ${new Date(result.created).toLocaleString()}`);
279
- console.log("");
280
- console.log(chalk.bold("New API key:"));
254
+ console.log(`${chalk.dim('Created:')} ${new Date(result.created).toLocaleString()}`);
255
+ console.log('');
256
+ console.log(chalk.bold('New API key:'));
281
257
  console.log(chalk.cyan(result.key));
282
- console.log("");
283
- console.log(chalk.yellow("⚠️ IMPORTANT: Update your applications with this new API key."));
284
- console.log(chalk.yellow(" The old key has been invalidated and will no longer work."));
285
- console.log(chalk.yellow(" This new key will not be displayed again."));
258
+ console.log('');
259
+ console.log(chalk.yellow('⚠️ IMPORTANT: Update your applications with this new API key.'));
260
+ console.log(chalk.yellow(' The old key has been invalidated and will no longer work.'));
261
+ console.log(chalk.yellow(' This new key will not be displayed again.'));
286
262
  } catch (error) {
287
- handleError("Failed to rotate API key", error);
263
+ handleError('Failed to rotate API key', error);
288
264
  }
289
265
  });
290
266
 
291
267
  apiKey
292
268
  .command(ApiKeyService.COMMANDS.DESCRIBE)
293
- .description("Show usage statistics for an API key")
294
- .argument("<id>", "ID of the API key")
295
- .option("--start <date>", "Start date (YYYY-MM-DD)")
296
- .option("--end <date>", "End date (YYYY-MM-DD)")
269
+ .description('Show usage statistics for an API key')
270
+ .argument('<id>', 'ID of the API key')
271
+ .option('--start <date>', 'Start date (YYYY-MM-DD)')
272
+ .option('--end <date>', 'End date (YYYY-MM-DD)')
297
273
  .action(async (id, _options) => {
298
274
  try {
299
275
  console.log(chalk.blue(`Fetching usage statistics for API key ${id}...`));
@@ -301,41 +277,41 @@ export function registerApiKeyCommands(program: Command): void {
301
277
  const apiKeyService = ApiKeyService.getInstance();
302
278
  const usage = await apiKeyService.describe(id);
303
279
 
304
- console.log("");
280
+ console.log('');
305
281
  console.log(chalk.bold(`Usage statistics for API key: ${usage.name} (${id})`));
306
- console.log("");
282
+ console.log('');
307
283
 
308
284
  // Period information
309
285
  console.log(chalk.dim(`Period: ${usage.period.start} to ${usage.period.end}`));
310
- console.log("");
286
+ console.log('');
311
287
 
312
288
  // Request statistics
313
- console.log(chalk.bold("Request statistics:"));
289
+ console.log(chalk.bold('Request statistics:'));
314
290
  console.log(`Total requests: ${chalk.cyan(usage.requests.total.toLocaleString())}`);
315
291
 
316
292
  // Daily breakdown if available
317
293
  if (usage.requests.daily && usage.requests.daily.length > 0) {
318
- console.log("");
319
- console.log(chalk.bold("Daily breakdown:"));
320
- console.log(chalk.dim("".repeat(30)));
321
- console.log(chalk.dim("DATE".padEnd(12) + "REQUESTS"));
294
+ console.log('');
295
+ console.log(chalk.bold('Daily breakdown:'));
296
+ console.log(chalk.dim(''.repeat(30)));
297
+ console.log(chalk.dim('DATE'.padEnd(12) + 'REQUESTS'));
322
298
 
323
- usage.requests.daily.forEach((day: { date: string; count: number }) => {
299
+ usage.requests.daily.forEach((day: { count: number; date: string }) => {
324
300
  console.log(`${day.date.padEnd(12)}${day.count.toLocaleString()}`);
325
301
  });
326
302
  }
327
303
 
328
304
  // Model usage if available
329
305
  if (usage.models && usage.models.length > 0) {
330
- console.log("");
331
- console.log(chalk.bold("Model usage:"));
332
- console.log(chalk.dim("".repeat(70)));
306
+ console.log('');
307
+ console.log(chalk.bold('Model usage:'));
308
+ console.log(chalk.dim(''.repeat(70)));
333
309
  console.log(
334
- chalk.dim("MODEL".padEnd(20)) +
335
- chalk.dim("REQUESTS".padEnd(10)) +
336
- chalk.dim("INPUT".padEnd(12)) +
337
- chalk.dim("OUTPUT".padEnd(12)) +
338
- chalk.dim("TOTAL TOKENS")
310
+ chalk.dim('MODEL'.padEnd(20)) +
311
+ chalk.dim('REQUESTS'.padEnd(10)) +
312
+ chalk.dim('INPUT'.padEnd(12)) +
313
+ chalk.dim('OUTPUT'.padEnd(12)) +
314
+ chalk.dim('TOTAL TOKENS'),
339
315
  );
340
316
 
341
317
  usage.models.forEach(
@@ -353,30 +329,30 @@ export function registerApiKeyCommands(program: Command): void {
353
329
  model.requests.toString().padEnd(10) +
354
330
  model.tokens.input.toLocaleString().padEnd(12) +
355
331
  model.tokens.output.toLocaleString().padEnd(12) +
356
- model.tokens.total.toLocaleString()
332
+ model.tokens.total.toLocaleString(),
357
333
  );
358
- }
334
+ },
359
335
  );
360
336
  }
361
337
 
362
- console.log("");
338
+ console.log('');
363
339
  console.log(
364
- chalk.dim("Use these statistics to understand your API usage and optimize your costs.")
340
+ chalk.dim('Use these statistics to understand your API usage and optimize your costs.'),
365
341
  );
366
342
  } catch (error) {
367
- handleError("Failed to get API key usage", error);
343
+ handleError('Failed to get API key usage', error);
368
344
  }
369
345
  });
370
346
 
371
347
  apiKey
372
348
  .command(ApiKeyService.COMMANDS.SET_DEFAULT)
373
- .description("Set an API key as the default for chat commands")
374
- .argument("<id>", "ID of the API key to set as default")
375
- .action(async id => {
349
+ .description('Set an API key as the default for chat commands')
350
+ .argument('<id>', 'ID of the API key to set as default')
351
+ .action(async (id) => {
376
352
  try {
377
353
  const apiKeyService = ApiKeyService.getInstance();
378
354
  const keys = await apiKeyService.list();
379
- const selectedKey = keys.find(key => key.id.toString() === id);
355
+ const selectedKey = keys.find((key) => key.id.toString() === id);
380
356
 
381
357
  if (!selectedKey) {
382
358
  console.error(chalk.red(`Error: API key with ID ${id} not found`));
@@ -393,46 +369,75 @@ export function registerApiKeyCommands(program: Command): void {
393
369
  id,
394
370
  selectedKey.name,
395
371
  selectedKey.prefix,
396
- rotatedKey.key
372
+ rotatedKey.key,
397
373
  );
398
374
 
399
375
  console.log(
400
- chalk.green(`✓ API key "${selectedKey.name}" set as default for chat commands`)
376
+ chalk.green(`✓ API key "${selectedKey.name}" set as default for chat commands`),
401
377
  );
402
- console.log("");
403
- console.log(chalk.dim("This API key will be used by default when running chat commands"));
404
- console.log(chalk.dim("You can override it with --api-key or --api-key-id options"));
378
+ console.log('');
379
+ console.log(chalk.dim('This API key will be used by default when running chat commands'));
380
+ console.log(chalk.dim('You can override it with --api-key or --api-key-id options'));
405
381
  } catch (error) {
406
- handleError("Failed to set default API key", error);
382
+ handleError('Failed to set default API key', error);
407
383
  }
408
384
  });
409
385
 
410
386
  apiKey
411
387
  .command(ApiKeyService.COMMANDS.GET_DEFAULT)
412
- .description("Show the current default API key")
388
+ .description('Show the current default API key')
413
389
  .action(() => {
414
390
  try {
415
391
  const defaultApiKeyManager = DefaultApiKeyManager.getInstance();
416
392
  const defaultApiKeyData = defaultApiKeyManager.getDefaultApiKeyData();
417
393
 
418
394
  if (!defaultApiKeyData) {
419
- console.log(chalk.yellow("No default API key set"));
420
- console.log("");
421
- console.log("To set a default API key, run:");
422
- console.log(chalk.cyan(" berget api-keys set-default <id>"));
395
+ console.log(chalk.yellow('No default API key set'));
396
+ console.log('');
397
+ console.log('To set a default API key, run:');
398
+ console.log(chalk.cyan(' berget api-keys set-default <id>'));
423
399
  return;
424
400
  }
425
401
 
426
- console.log(chalk.bold("Default API key:"));
427
- console.log("");
428
- console.log(`${chalk.dim("ID:")} ${defaultApiKeyData.id}`);
429
- console.log(`${chalk.dim("Name:")} ${defaultApiKeyData.name}`);
430
- console.log(`${chalk.dim("Prefix:")} ${defaultApiKeyData.prefix}`);
431
- console.log("");
432
- console.log(chalk.dim("This API key will be used by default when running chat commands"));
433
- console.log(chalk.dim("You can override it with --api-key or --api-key-id options"));
402
+ console.log(chalk.bold('Default API key:'));
403
+ console.log('');
404
+ console.log(`${chalk.dim('ID:')} ${defaultApiKeyData.id}`);
405
+ console.log(`${chalk.dim('Name:')} ${defaultApiKeyData.name}`);
406
+ console.log(`${chalk.dim('Prefix:')} ${defaultApiKeyData.prefix}`);
407
+ console.log('');
408
+ console.log(chalk.dim('This API key will be used by default when running chat commands'));
409
+ console.log(chalk.dim('You can override it with --api-key or --api-key-id options'));
434
410
  } catch (error) {
435
- handleError("Failed to get default API key", error);
411
+ handleError('Failed to get default API key', error);
436
412
  }
437
413
  });
438
414
  }
415
+
416
+ // Helper functions for better date formatting
417
+ function formatDate(dateString: string): string {
418
+ const date = new Date(dateString);
419
+ const now = new Date();
420
+ const diffTime = Math.abs(now.getTime() - date.getTime());
421
+ const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
422
+
423
+ if (diffDays === 0) return chalk.green('Today');
424
+ if (diffDays === 1) return chalk.yellow('Yesterday');
425
+ if (diffDays < 7) return chalk.yellow(`${diffDays} days ago`);
426
+ if (diffDays < 30) return chalk.blue(`${Math.floor(diffDays / 7)} weeks ago`);
427
+ if (diffDays < 365) return chalk.magenta(`${Math.floor(diffDays / 30)} months ago`);
428
+ return chalk.gray(`${Math.floor(diffDays / 365)} years ago`);
429
+ }
430
+
431
+ function formatLastUsed(dateString: string): string {
432
+ const date = new Date(dateString);
433
+ const now = new Date();
434
+ const diffTime = Math.abs(now.getTime() - date.getTime());
435
+ const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
436
+
437
+ if (diffDays === 0) return chalk.green('Today');
438
+ if (diffDays === 1) return chalk.yellow('Yesterday');
439
+ if (diffDays < 7) return chalk.yellow(`${diffDays} days ago`);
440
+ if (diffDays < 30) return chalk.blue(`${Math.floor(diffDays / 7)} weeks ago`);
441
+ if (diffDays < 365) return chalk.magenta(`${Math.floor(diffDays / 30)} months ago`);
442
+ return chalk.gray(`${Math.floor(diffDays / 365)} years ago`);
443
+ }