berget 1.4.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/.env.example +5 -0
  2. package/AGENTS.md +184 -0
  3. package/TODO.md +2 -0
  4. package/blog-post.md +176 -0
  5. package/dist/index.js +11 -8
  6. package/dist/package.json +7 -2
  7. package/dist/src/commands/api-keys.js +4 -2
  8. package/dist/src/commands/chat.js +21 -11
  9. package/dist/src/commands/code.js +1424 -0
  10. package/dist/src/commands/index.js +2 -0
  11. package/dist/src/constants/command-structure.js +12 -0
  12. package/dist/src/schemas/opencode-schema.json +1121 -0
  13. package/dist/src/services/cluster-service.js +1 -1
  14. package/dist/src/utils/default-api-key.js +2 -2
  15. package/dist/src/utils/env-manager.js +86 -0
  16. package/dist/src/utils/error-handler.js +10 -3
  17. package/dist/src/utils/markdown-renderer.js +4 -4
  18. package/dist/src/utils/opencode-validator.js +122 -0
  19. package/dist/src/utils/token-manager.js +2 -2
  20. package/dist/tests/commands/chat.test.js +20 -18
  21. package/dist/tests/commands/code.test.js +414 -0
  22. package/dist/tests/utils/env-manager.test.js +148 -0
  23. package/dist/tests/utils/opencode-validator.test.js +103 -0
  24. package/index.ts +67 -32
  25. package/opencode.json +182 -0
  26. package/package.json +7 -2
  27. package/src/client.ts +20 -20
  28. package/src/commands/api-keys.ts +93 -60
  29. package/src/commands/auth.ts +4 -2
  30. package/src/commands/billing.ts +6 -3
  31. package/src/commands/chat.ts +149 -107
  32. package/src/commands/clusters.ts +2 -2
  33. package/src/commands/code.ts +1696 -0
  34. package/src/commands/index.ts +2 -0
  35. package/src/commands/models.ts +3 -3
  36. package/src/commands/users.ts +2 -2
  37. package/src/constants/command-structure.ts +112 -58
  38. package/src/schemas/opencode-schema.json +991 -0
  39. package/src/services/api-key-service.ts +1 -1
  40. package/src/services/auth-service.ts +27 -25
  41. package/src/services/chat-service.ts +26 -23
  42. package/src/services/cluster-service.ts +5 -5
  43. package/src/services/collaborator-service.ts +3 -3
  44. package/src/services/flux-service.ts +2 -2
  45. package/src/services/helm-service.ts +2 -2
  46. package/src/services/kubectl-service.ts +3 -6
  47. package/src/types/api.d.ts +1032 -1010
  48. package/src/types/json.d.ts +3 -3
  49. package/src/utils/default-api-key.ts +54 -42
  50. package/src/utils/env-manager.ts +98 -0
  51. package/src/utils/error-handler.ts +24 -15
  52. package/src/utils/logger.ts +12 -12
  53. package/src/utils/markdown-renderer.ts +18 -18
  54. package/src/utils/opencode-validator.ts +134 -0
  55. package/src/utils/token-manager.ts +35 -23
  56. package/tests/commands/chat.test.ts +43 -31
  57. package/tests/commands/code.test.ts +505 -0
  58. package/tests/utils/env-manager.test.ts +199 -0
  59. package/tests/utils/opencode-validator.test.ts +118 -0
  60. package/tsconfig.json +8 -8
  61. package/-27b-it +0 -0
  62. package/examples/README.md +0 -95
  63. package/examples/ai-review.sh +0 -30
  64. package/examples/install-global-security-hook.sh +0 -170
  65. package/examples/security-check.sh +0 -102
  66. package/examples/smart-commit.sh +0 -26
@@ -23,8 +23,8 @@ export function registerApiKeyCommands(program: Command): void {
23
23
  if (keys.length === 0) {
24
24
  console.log(
25
25
  chalk.yellow(
26
- 'No API keys found. Create one with `berget api-key create --name <name>`'
27
- )
26
+ 'No API keys found. Create one with `berget api-key create --name <name>`',
27
+ ),
28
28
  )
29
29
  return
30
30
  }
@@ -39,21 +39,24 @@ export function registerApiKeyCommands(program: Command): void {
39
39
  chalk.dim('PREFIX'.padEnd(12)) +
40
40
  chalk.dim('STATUS'.padEnd(12)) +
41
41
  chalk.dim('CREATED'.padEnd(12)) +
42
- chalk.dim('LAST USED')
42
+ chalk.dim('LAST USED'),
43
43
  )
44
44
 
45
45
  console.log(chalk.dim('─'.repeat(85)))
46
46
 
47
47
  keys.forEach((key: ApiKey) => {
48
- const lastUsed = key.lastUsed ? key.lastUsed.substring(0, 10) : 'Never'
48
+ const lastUsed = key.lastUsed
49
+ ? key.lastUsed.substring(0, 10)
50
+ : 'Never'
49
51
  const status = key.active
50
52
  ? chalk.green('● Active')
51
53
  : chalk.red('● Inactive')
52
54
 
53
55
  // Format the prefix to ensure it's not too long
54
- const prefixStr = key.prefix.length > 12
55
- ? key.prefix.substring(0, 12) + '...'
56
- : key.prefix
56
+ const prefixStr =
57
+ key.prefix.length > 12
58
+ ? key.prefix.substring(0, 12) + '...'
59
+ : key.prefix
57
60
 
58
61
  console.log(
59
62
  String(key.id).padEnd(10) +
@@ -61,21 +64,21 @@ export function registerApiKeyCommands(program: Command): void {
61
64
  prefixStr.padEnd(15) +
62
65
  status.padEnd(12) +
63
66
  key.created.substring(0, 10).padEnd(12) +
64
- lastUsed
67
+ lastUsed,
65
68
  )
66
69
  })
67
70
 
68
71
  console.log('')
69
72
  console.log(
70
73
  chalk.dim(
71
- 'Use `berget api-key create --name <name>` to create a new API key'
72
- )
74
+ 'Use `berget api-key create --name <name>` to create a new API key',
75
+ ),
73
76
  )
74
77
  console.log(
75
- chalk.dim('Use `berget api-key delete <id>` to delete an API key')
78
+ chalk.dim('Use `berget api-key delete <id>` to delete an API key'),
76
79
  )
77
80
  console.log(
78
- chalk.dim('Use `berget api-key rotate <id>` to rotate an API key')
81
+ chalk.dim('Use `berget api-key rotate <id>` to rotate an API key'),
79
82
  )
80
83
  } catch (error) {
81
84
  handleError('Failed to list API keys', error)
@@ -93,7 +96,7 @@ export function registerApiKeyCommands(program: Command): void {
93
96
  console.error(chalk.red('Error: --name is required'))
94
97
  console.log('')
95
98
  console.log(
96
- 'Usage: berget api-key create --name <name> [--description <description>]'
99
+ 'Usage: berget api-key create --name <name> [--description <description>]',
97
100
  )
98
101
  return
99
102
  }
@@ -118,23 +121,25 @@ export function registerApiKeyCommands(program: Command): void {
118
121
  }
119
122
  console.log(
120
123
  `${chalk.dim('Created:')} ${new Date(
121
- result.created
122
- ).toLocaleString()}`
124
+ result.created,
125
+ ).toLocaleString()}`,
123
126
  )
124
127
  console.log('')
125
128
  console.log(chalk.bold('API key:'))
126
129
  console.log(chalk.cyan(result.key))
127
130
  console.log('')
128
131
  console.log(
129
- chalk.yellow('⚠️ IMPORTANT: Save this API key in a secure location.')
132
+ chalk.yellow(
133
+ '⚠️ IMPORTANT: Save this API key in a secure location.',
134
+ ),
130
135
  )
131
136
  console.log(chalk.yellow(' It will not be displayed again.'))
132
137
 
133
138
  console.log('')
134
139
  console.log(
135
140
  chalk.dim(
136
- 'Use this key in your applications to authenticate with the Berget API.'
137
- )
141
+ 'Use this key in your applications to authenticate with the Berget API.',
142
+ ),
138
143
  )
139
144
  } catch (error) {
140
145
  handleError('Failed to create API key', error)
@@ -156,11 +161,13 @@ export function registerApiKeyCommands(program: Command): void {
156
161
  console.log('')
157
162
  console.log(
158
163
  chalk.dim(
159
- 'Applications using this key will no longer be able to authenticate.'
160
- )
164
+ 'Applications using this key will no longer be able to authenticate.',
165
+ ),
161
166
  )
162
167
  console.log(
163
- chalk.dim('Use `berget api-key list` to see your remaining API keys.')
168
+ chalk.dim(
169
+ 'Use `berget api-key list` to see your remaining API keys.',
170
+ ),
164
171
  )
165
172
  } catch (error) {
166
173
  handleError('Failed to delete API key', error)
@@ -170,14 +177,14 @@ export function registerApiKeyCommands(program: Command): void {
170
177
  apiKey
171
178
  .command(ApiKeyService.COMMANDS.ROTATE)
172
179
  .description(
173
- 'Rotate an API key (creates a new one and invalidates the old one)'
180
+ 'Rotate an API key (creates a new one and invalidates the old one)',
174
181
  )
175
182
  .argument('<id>', 'ID of the API key to rotate')
176
183
  .action(async (id) => {
177
184
  try {
178
185
  console.log(chalk.blue(`Rotating API key ${id}...`))
179
186
  console.log(
180
- chalk.dim('This will invalidate the old key and generate a new one.')
187
+ chalk.dim('This will invalidate the old key and generate a new one.'),
181
188
  )
182
189
 
183
190
  const apiKeyService = ApiKeyService.getInstance()
@@ -195,8 +202,8 @@ export function registerApiKeyCommands(program: Command): void {
195
202
  }
196
203
  console.log(
197
204
  `${chalk.dim('Created:')} ${new Date(
198
- result.created
199
- ).toLocaleString()}`
205
+ result.created,
206
+ ).toLocaleString()}`,
200
207
  )
201
208
  console.log('')
202
209
  console.log(chalk.bold('New API key:'))
@@ -204,15 +211,17 @@ export function registerApiKeyCommands(program: Command): void {
204
211
  console.log('')
205
212
  console.log(
206
213
  chalk.yellow(
207
- '⚠️ IMPORTANT: Update your applications with this new API key.'
208
- )
214
+ '⚠️ IMPORTANT: Update your applications with this new API key.',
215
+ ),
209
216
  )
210
217
  console.log(
211
218
  chalk.yellow(
212
- ' The old key has been invalidated and will no longer work.'
213
- )
219
+ ' The old key has been invalidated and will no longer work.',
220
+ ),
221
+ )
222
+ console.log(
223
+ chalk.yellow(' This new key will not be displayed again.'),
214
224
  )
215
- console.log(chalk.yellow(' This new key will not be displayed again.'))
216
225
  } catch (error) {
217
226
  handleError('Failed to rotate API key', error)
218
227
  }
@@ -226,27 +235,29 @@ export function registerApiKeyCommands(program: Command): void {
226
235
  .option('--end <date>', 'End date (YYYY-MM-DD)')
227
236
  .action(async (id, options) => {
228
237
  try {
229
- console.log(chalk.blue(`Fetching usage statistics for API key ${id}...`))
238
+ console.log(
239
+ chalk.blue(`Fetching usage statistics for API key ${id}...`),
240
+ )
230
241
 
231
242
  const apiKeyService = ApiKeyService.getInstance()
232
243
  const usage = await apiKeyService.describe(id)
233
244
 
234
245
  console.log('')
235
246
  console.log(
236
- chalk.bold(`Usage statistics for API key: ${usage.name} (${id})`)
247
+ chalk.bold(`Usage statistics for API key: ${usage.name} (${id})`),
237
248
  )
238
249
  console.log('')
239
250
 
240
251
  // Period information
241
252
  console.log(
242
- chalk.dim(`Period: ${usage.period.start} to ${usage.period.end}`)
253
+ chalk.dim(`Period: ${usage.period.start} to ${usage.period.end}`),
243
254
  )
244
255
  console.log('')
245
256
 
246
257
  // Request statistics
247
258
  console.log(chalk.bold('Request statistics:'))
248
259
  console.log(
249
- `Total requests: ${chalk.cyan(usage.requests.total.toLocaleString())}`
260
+ `Total requests: ${chalk.cyan(usage.requests.total.toLocaleString())}`,
250
261
  )
251
262
 
252
263
  // Daily breakdown if available
@@ -256,9 +267,11 @@ export function registerApiKeyCommands(program: Command): void {
256
267
  console.log(chalk.dim('─'.repeat(30)))
257
268
  console.log(chalk.dim('DATE'.padEnd(12) + 'REQUESTS'))
258
269
 
259
- usage.requests.daily.forEach((day: { date: string; count: number }) => {
260
- console.log(`${day.date.padEnd(12)}${day.count.toLocaleString()}`)
261
- })
270
+ usage.requests.daily.forEach(
271
+ (day: { date: string; count: number }) => {
272
+ console.log(`${day.date.padEnd(12)}${day.count.toLocaleString()}`)
273
+ },
274
+ )
262
275
  }
263
276
 
264
277
  // Model usage if available
@@ -271,7 +284,7 @@ export function registerApiKeyCommands(program: Command): void {
271
284
  chalk.dim('REQUESTS'.padEnd(10)) +
272
285
  chalk.dim('INPUT'.padEnd(12)) +
273
286
  chalk.dim('OUTPUT'.padEnd(12)) +
274
- chalk.dim('TOTAL TOKENS')
287
+ chalk.dim('TOTAL TOKENS'),
275
288
  )
276
289
 
277
290
  usage.models.forEach(
@@ -289,23 +302,23 @@ export function registerApiKeyCommands(program: Command): void {
289
302
  model.requests.toString().padEnd(10) +
290
303
  model.tokens.input.toLocaleString().padEnd(12) +
291
304
  model.tokens.output.toLocaleString().padEnd(12) +
292
- model.tokens.total.toLocaleString()
305
+ model.tokens.total.toLocaleString(),
293
306
  )
294
- }
307
+ },
295
308
  )
296
309
  }
297
310
 
298
311
  console.log('')
299
312
  console.log(
300
313
  chalk.dim(
301
- 'Use these statistics to understand your API usage and optimize your costs.'
302
- )
314
+ 'Use these statistics to understand your API usage and optimize your costs.',
315
+ ),
303
316
  )
304
317
  } catch (error) {
305
318
  handleError('Failed to get API key usage', error)
306
319
  }
307
320
  })
308
-
321
+
309
322
  apiKey
310
323
  .command(ApiKeyService.COMMANDS.SET_DEFAULT)
311
324
  .description('Set an API key as the default for chat commands')
@@ -314,35 +327,47 @@ export function registerApiKeyCommands(program: Command): void {
314
327
  try {
315
328
  const apiKeyService = ApiKeyService.getInstance()
316
329
  const keys = await apiKeyService.list()
317
- const selectedKey = keys.find(key => key.id.toString() === id)
318
-
330
+ const selectedKey = keys.find((key) => key.id.toString() === id)
331
+
319
332
  if (!selectedKey) {
320
333
  console.error(chalk.red(`Error: API key with ID ${id} not found`))
321
334
  return
322
335
  }
323
-
336
+
324
337
  // Save the default API key
325
338
  const defaultApiKeyManager = DefaultApiKeyManager.getInstance()
326
-
339
+
327
340
  // We need to rotate the key to get the actual key value
328
341
  const rotatedKey = await apiKeyService.rotate(id)
329
-
342
+
330
343
  defaultApiKeyManager.setDefaultApiKey(
331
- id,
332
- selectedKey.name,
344
+ id,
345
+ selectedKey.name,
333
346
  selectedKey.prefix,
334
- rotatedKey.key
347
+ rotatedKey.key,
348
+ )
349
+
350
+ console.log(
351
+ chalk.green(
352
+ `✓ API key "${selectedKey.name}" set as default for chat commands`,
353
+ ),
335
354
  )
336
-
337
- console.log(chalk.green(`✓ API key "${selectedKey.name}" set as default for chat commands`))
338
355
  console.log('')
339
- console.log(chalk.dim('This API key will be used by default when running chat commands'))
340
- console.log(chalk.dim('You can override it with --api-key or --api-key-id options'))
356
+ console.log(
357
+ chalk.dim(
358
+ 'This API key will be used by default when running chat commands',
359
+ ),
360
+ )
361
+ console.log(
362
+ chalk.dim(
363
+ 'You can override it with --api-key or --api-key-id options',
364
+ ),
365
+ )
341
366
  } catch (error) {
342
367
  handleError('Failed to set default API key', error)
343
368
  }
344
369
  })
345
-
370
+
346
371
  apiKey
347
372
  .command(ApiKeyService.COMMANDS.GET_DEFAULT)
348
373
  .description('Show the current default API key')
@@ -350,7 +375,7 @@ export function registerApiKeyCommands(program: Command): void {
350
375
  try {
351
376
  const defaultApiKeyManager = DefaultApiKeyManager.getInstance()
352
377
  const defaultApiKeyData = defaultApiKeyManager.getDefaultApiKeyData()
353
-
378
+
354
379
  if (!defaultApiKeyData) {
355
380
  console.log(chalk.yellow('No default API key set'))
356
381
  console.log('')
@@ -358,15 +383,23 @@ export function registerApiKeyCommands(program: Command): void {
358
383
  console.log(chalk.cyan(' berget api-keys set-default <id>'))
359
384
  return
360
385
  }
361
-
386
+
362
387
  console.log(chalk.bold('Default API key:'))
363
388
  console.log('')
364
389
  console.log(`${chalk.dim('ID:')} ${defaultApiKeyData.id}`)
365
390
  console.log(`${chalk.dim('Name:')} ${defaultApiKeyData.name}`)
366
391
  console.log(`${chalk.dim('Prefix:')} ${defaultApiKeyData.prefix}`)
367
392
  console.log('')
368
- console.log(chalk.dim('This API key will be used by default when running chat commands'))
369
- console.log(chalk.dim('You can override it with --api-key or --api-key-id options'))
393
+ console.log(
394
+ chalk.dim(
395
+ 'This API key will be used by default when running chat commands',
396
+ ),
397
+ )
398
+ console.log(
399
+ chalk.dim(
400
+ 'You can override it with --api-key or --api-key-id options',
401
+ ),
402
+ )
370
403
  } catch (error) {
371
404
  handleError('Failed to get default API key', error)
372
405
  }
@@ -38,7 +38,7 @@ export function registerAuthCommands(program: Command): void {
38
38
 
39
39
  if (profile) {
40
40
  console.log(
41
- chalk.bold(`Logged in as: ${profile.name || profile.login}`)
41
+ chalk.bold(`Logged in as: ${profile.name || profile.login}`),
42
42
  )
43
43
  console.log(`Email: ${chalk.cyan(profile.email || 'Not available')}`)
44
44
  console.log(`Role: ${chalk.cyan(profile.role || 'Not available')}`)
@@ -48,7 +48,9 @@ export function registerAuthCommands(program: Command): void {
48
48
  }
49
49
  } else {
50
50
  console.log(
51
- chalk.yellow('You are not logged in. Use `berget login` to log in.')
51
+ chalk.yellow(
52
+ 'You are not logged in. Use `berget login` to log in.',
53
+ ),
52
54
  )
53
55
  }
54
56
  } catch (error) {
@@ -21,9 +21,12 @@ export function registerBillingCommands(program: Command): void {
21
21
  let response
22
22
 
23
23
  if (options.model) {
24
- const { data, error } = await client.GET('/v1/usage/tokens/{modelId}', {
25
- params: { path: { modelId: options.model } },
26
- })
24
+ const { data, error } = await client.GET(
25
+ '/v1/usage/tokens/{modelId}',
26
+ {
27
+ params: { path: { modelId: options.model } },
28
+ },
29
+ )
27
30
  if (error) throw new Error(JSON.stringify(error))
28
31
  response = data
29
32
  } else {