berget 1.3.1 → 2.0.0
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/.env.example +5 -0
- package/.github/workflows/publish.yml +56 -0
- package/.github/workflows/test.yml +38 -0
- package/AGENTS.md +184 -0
- package/README.md +177 -38
- package/TODO.md +2 -0
- package/blog-post.md +176 -0
- package/dist/index.js +11 -8
- package/dist/package.json +14 -3
- package/dist/src/commands/api-keys.js +4 -2
- package/dist/src/commands/chat.js +182 -23
- package/dist/src/commands/code.js +1424 -0
- package/dist/src/commands/index.js +2 -0
- package/dist/src/constants/command-structure.js +12 -0
- package/dist/src/schemas/opencode-schema.json +1121 -0
- package/dist/src/services/chat-service.js +10 -10
- package/dist/src/services/cluster-service.js +1 -1
- package/dist/src/utils/default-api-key.js +2 -2
- package/dist/src/utils/env-manager.js +86 -0
- package/dist/src/utils/error-handler.js +10 -3
- package/dist/src/utils/markdown-renderer.js +4 -4
- package/dist/src/utils/opencode-validator.js +122 -0
- package/dist/src/utils/token-manager.js +2 -2
- package/dist/tests/commands/chat.test.js +109 -0
- package/dist/tests/commands/code.test.js +414 -0
- package/dist/tests/utils/env-manager.test.js +148 -0
- package/dist/tests/utils/opencode-validator.test.js +103 -0
- package/dist/vitest.config.js +9 -0
- package/index.ts +67 -32
- package/opencode.json +182 -0
- package/package.json +14 -3
- package/src/client.ts +20 -20
- package/src/commands/api-keys.ts +93 -60
- package/src/commands/auth.ts +4 -2
- package/src/commands/billing.ts +6 -3
- package/src/commands/chat.ts +291 -97
- package/src/commands/clusters.ts +2 -2
- package/src/commands/code.ts +1696 -0
- package/src/commands/index.ts +2 -0
- package/src/commands/models.ts +3 -3
- package/src/commands/users.ts +2 -2
- package/src/constants/command-structure.ts +112 -58
- package/src/schemas/opencode-schema.json +991 -0
- package/src/services/api-key-service.ts +1 -1
- package/src/services/auth-service.ts +27 -25
- package/src/services/chat-service.ts +37 -44
- package/src/services/cluster-service.ts +5 -5
- package/src/services/collaborator-service.ts +3 -3
- package/src/services/flux-service.ts +2 -2
- package/src/services/helm-service.ts +2 -2
- package/src/services/kubectl-service.ts +3 -6
- package/src/types/api.d.ts +1032 -1010
- package/src/types/json.d.ts +3 -3
- package/src/utils/default-api-key.ts +54 -42
- package/src/utils/env-manager.ts +98 -0
- package/src/utils/error-handler.ts +24 -15
- package/src/utils/logger.ts +12 -12
- package/src/utils/markdown-renderer.ts +18 -18
- package/src/utils/opencode-validator.ts +134 -0
- package/src/utils/token-manager.ts +35 -23
- package/tests/commands/chat.test.ts +129 -0
- package/tests/commands/code.test.ts +505 -0
- package/tests/utils/env-manager.test.ts +199 -0
- package/tests/utils/opencode-validator.test.ts +118 -0
- package/tsconfig.json +8 -8
- package/vitest.config.ts +8 -0
- package/-27b-it +0 -0
package/src/commands/api-keys.ts
CHANGED
|
@@ -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
|
|
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 =
|
|
55
|
-
|
|
56
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
260
|
-
|
|
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(
|
|
340
|
-
|
|
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(
|
|
369
|
-
|
|
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
|
}
|
package/src/commands/auth.ts
CHANGED
|
@@ -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(
|
|
51
|
+
chalk.yellow(
|
|
52
|
+
'You are not logged in. Use `berget login` to log in.',
|
|
53
|
+
),
|
|
52
54
|
)
|
|
53
55
|
}
|
|
54
56
|
} catch (error) {
|
package/src/commands/billing.ts
CHANGED
|
@@ -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(
|
|
25
|
-
|
|
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 {
|