berget 1.0.0 → 1.1.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.
Files changed (37) hide show
  1. package/README.md +92 -0
  2. package/dist/index.js +7 -471
  3. package/dist/src/client.js +193 -102
  4. package/dist/src/commands/api-keys.js +271 -0
  5. package/dist/src/commands/auth.js +65 -0
  6. package/dist/src/commands/autocomplete.js +24 -0
  7. package/dist/src/commands/billing.js +53 -0
  8. package/dist/src/commands/chat.js +276 -0
  9. package/dist/src/commands/clusters.js +69 -0
  10. package/dist/src/commands/index.js +25 -0
  11. package/dist/src/commands/models.js +69 -0
  12. package/dist/src/commands/users.js +43 -0
  13. package/dist/src/constants/command-structure.js +14 -0
  14. package/dist/src/services/auth-service.js +49 -47
  15. package/dist/src/services/chat-service.js +177 -0
  16. package/dist/src/utils/config-checker.js +50 -0
  17. package/dist/src/utils/default-api-key.js +111 -0
  18. package/dist/src/utils/token-manager.js +165 -0
  19. package/index.ts +5 -566
  20. package/package.json +6 -1
  21. package/src/client.ts +262 -80
  22. package/src/commands/api-keys.ts +364 -0
  23. package/src/commands/auth.ts +58 -0
  24. package/src/commands/autocomplete.ts +19 -0
  25. package/src/commands/billing.ts +41 -0
  26. package/src/commands/chat.ts +345 -0
  27. package/src/commands/clusters.ts +65 -0
  28. package/src/commands/index.ts +23 -0
  29. package/src/commands/models.ts +63 -0
  30. package/src/commands/users.ts +37 -0
  31. package/src/constants/command-structure.ts +16 -0
  32. package/src/services/auth-service.ts +90 -50
  33. package/src/services/chat-service.ts +177 -0
  34. package/src/types/api.d.ts +58 -192
  35. package/src/utils/config-checker.ts +23 -0
  36. package/src/utils/default-api-key.ts +94 -0
  37. package/src/utils/token-manager.ts +150 -0
@@ -0,0 +1,364 @@
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'
6
+
7
+ /**
8
+ * Register API key commands
9
+ */
10
+ export function registerApiKeyCommands(program: Command): void {
11
+ const apiKey = program
12
+ .command(ApiKeyService.COMMAND_GROUP)
13
+ .description('Manage API keys')
14
+
15
+ apiKey
16
+ .command(ApiKeyService.COMMANDS.LIST)
17
+ .description('List all API keys')
18
+ .action(async () => {
19
+ try {
20
+ const apiKeyService = ApiKeyService.getInstance()
21
+ const keys = await apiKeyService.list()
22
+
23
+ if (keys.length === 0) {
24
+ console.log(
25
+ chalk.yellow(
26
+ 'No API keys found. Create one with `berget api-key create --name <name>`'
27
+ )
28
+ )
29
+ return
30
+ }
31
+
32
+ console.log(chalk.bold('Your API keys:'))
33
+ console.log('')
34
+
35
+ // Create a table-like format with headers
36
+ console.log(
37
+ chalk.dim('ID'.padEnd(10)) +
38
+ chalk.dim('NAME'.padEnd(25)) +
39
+ chalk.dim('PREFIX'.padEnd(12)) +
40
+ chalk.dim('STATUS'.padEnd(12)) +
41
+ chalk.dim('CREATED'.padEnd(12)) +
42
+ chalk.dim('LAST USED')
43
+ )
44
+
45
+ console.log(chalk.dim('─'.repeat(85)))
46
+
47
+ keys.forEach((key: ApiKey) => {
48
+ const lastUsed = key.lastUsed ? key.lastUsed.substring(0, 10) : 'Never'
49
+ const status = key.active
50
+ ? chalk.green('● Active')
51
+ : chalk.red('● Inactive')
52
+
53
+ console.log(
54
+ String(key.id).padEnd(10) +
55
+ key.name.padEnd(25) +
56
+ key.prefix.padEnd(12) +
57
+ status.padEnd(12) +
58
+ key.created.substring(0, 10).padEnd(12) +
59
+ lastUsed
60
+ )
61
+ })
62
+
63
+ console.log('')
64
+ console.log(
65
+ chalk.dim(
66
+ 'Use `berget api-key create --name <name>` to create a new API key'
67
+ )
68
+ )
69
+ console.log(
70
+ chalk.dim('Use `berget api-key delete <id>` to delete an API key')
71
+ )
72
+ console.log(
73
+ chalk.dim('Use `berget api-key rotate <id>` to rotate an API key')
74
+ )
75
+ } catch (error) {
76
+ handleError('Failed to list API keys', error)
77
+ }
78
+ })
79
+
80
+ apiKey
81
+ .command(ApiKeyService.COMMANDS.CREATE)
82
+ .description('Create a new API key')
83
+ .option('--name <name>', 'Name of the API key')
84
+ .option('--description <description>', 'Description of the API key')
85
+ .action(async (options) => {
86
+ try {
87
+ if (!options.name) {
88
+ console.error(chalk.red('Error: --name is required'))
89
+ console.log('')
90
+ console.log(
91
+ 'Usage: berget api-key create --name <name> [--description <description>]'
92
+ )
93
+ return
94
+ }
95
+
96
+ console.log(chalk.blue('Creating API key...'))
97
+
98
+ const apiKeyService = ApiKeyService.getInstance()
99
+ const result = await apiKeyService.create({
100
+ name: options.name,
101
+ description: options.description,
102
+ })
103
+
104
+ console.log('')
105
+ console.log(chalk.green('✓ API key created'))
106
+ console.log('')
107
+ console.log(chalk.bold('API key details:'))
108
+ console.log('')
109
+ console.log(`${chalk.dim('ID:')} ${result.id}`)
110
+ console.log(`${chalk.dim('Name:')} ${result.name}`)
111
+ if (result.description) {
112
+ console.log(`${chalk.dim('Description:')} ${result.description}`)
113
+ }
114
+ console.log(
115
+ `${chalk.dim('Created:')} ${new Date(
116
+ result.created
117
+ ).toLocaleString()}`
118
+ )
119
+ console.log('')
120
+ console.log(chalk.bold('API key:'))
121
+ console.log(chalk.cyan(result.key))
122
+ console.log('')
123
+ console.log(
124
+ chalk.yellow('⚠️ IMPORTANT: Save this API key in a secure location.')
125
+ )
126
+ console.log(chalk.yellow(' It will not be displayed again.'))
127
+
128
+ console.log('')
129
+ console.log(
130
+ chalk.dim(
131
+ 'Use this key in your applications to authenticate with the Berget API.'
132
+ )
133
+ )
134
+ } catch (error) {
135
+ handleError('Failed to create API key', error)
136
+ }
137
+ })
138
+
139
+ apiKey
140
+ .command(ApiKeyService.COMMANDS.DELETE)
141
+ .description('Delete an API key')
142
+ .argument('<id>', 'ID of the API key to delete')
143
+ .action(async (id) => {
144
+ try {
145
+ console.log(chalk.blue(`Deleting API key ${id}...`))
146
+
147
+ const apiKeyService = ApiKeyService.getInstance()
148
+ await apiKeyService.delete(id)
149
+
150
+ console.log(chalk.green(`✓ API key ${id} has been deleted`))
151
+ console.log('')
152
+ console.log(
153
+ chalk.dim(
154
+ 'Applications using this key will no longer be able to authenticate.'
155
+ )
156
+ )
157
+ console.log(
158
+ chalk.dim('Use `berget api-key list` to see your remaining API keys.')
159
+ )
160
+ } catch (error) {
161
+ handleError('Failed to delete API key', error)
162
+ }
163
+ })
164
+
165
+ apiKey
166
+ .command(ApiKeyService.COMMANDS.ROTATE)
167
+ .description(
168
+ 'Rotate an API key (creates a new one and invalidates the old one)'
169
+ )
170
+ .argument('<id>', 'ID of the API key to rotate')
171
+ .action(async (id) => {
172
+ try {
173
+ console.log(chalk.blue(`Rotating API key ${id}...`))
174
+ console.log(
175
+ chalk.dim('This will invalidate the old key and generate a new one.')
176
+ )
177
+
178
+ const apiKeyService = ApiKeyService.getInstance()
179
+ const result = await apiKeyService.rotate(id)
180
+
181
+ console.log('')
182
+ console.log(chalk.green('✓ API key rotated'))
183
+ console.log('')
184
+ console.log(chalk.bold('New API key details:'))
185
+ console.log('')
186
+ console.log(`${chalk.dim('ID:')} ${result.id}`)
187
+ console.log(`${chalk.dim('Name:')} ${result.name}`)
188
+ if (result.description) {
189
+ console.log(`${chalk.dim('Description:')} ${result.description}`)
190
+ }
191
+ console.log(
192
+ `${chalk.dim('Created:')} ${new Date(
193
+ result.created
194
+ ).toLocaleString()}`
195
+ )
196
+ console.log('')
197
+ console.log(chalk.bold('New API key:'))
198
+ console.log(chalk.cyan(result.key))
199
+ console.log('')
200
+ console.log(
201
+ chalk.yellow(
202
+ '⚠️ IMPORTANT: Update your applications with this new API key.'
203
+ )
204
+ )
205
+ console.log(
206
+ chalk.yellow(
207
+ ' The old key has been invalidated and will no longer work.'
208
+ )
209
+ )
210
+ console.log(chalk.yellow(' This new key will not be displayed again.'))
211
+ } catch (error) {
212
+ handleError('Failed to rotate API key', error)
213
+ }
214
+ })
215
+
216
+ apiKey
217
+ .command(ApiKeyService.COMMANDS.DESCRIBE)
218
+ .description('Show usage statistics for an API key')
219
+ .argument('<id>', 'ID of the API key')
220
+ .option('--start <date>', 'Start date (YYYY-MM-DD)')
221
+ .option('--end <date>', 'End date (YYYY-MM-DD)')
222
+ .action(async (id, options) => {
223
+ try {
224
+ console.log(chalk.blue(`Fetching usage statistics for API key ${id}...`))
225
+
226
+ const apiKeyService = ApiKeyService.getInstance()
227
+ const usage = await apiKeyService.describe(id)
228
+
229
+ console.log('')
230
+ console.log(
231
+ chalk.bold(`Usage statistics for API key: ${usage.name} (${id})`)
232
+ )
233
+ console.log('')
234
+
235
+ // Period information
236
+ console.log(
237
+ chalk.dim(`Period: ${usage.period.start} to ${usage.period.end}`)
238
+ )
239
+ console.log('')
240
+
241
+ // Request statistics
242
+ console.log(chalk.bold('Request statistics:'))
243
+ console.log(
244
+ `Total requests: ${chalk.cyan(usage.requests.total.toLocaleString())}`
245
+ )
246
+
247
+ // Daily breakdown if available
248
+ if (usage.requests.daily && usage.requests.daily.length > 0) {
249
+ console.log('')
250
+ console.log(chalk.bold('Daily breakdown:'))
251
+ console.log(chalk.dim('─'.repeat(30)))
252
+ console.log(chalk.dim('DATE'.padEnd(12) + 'REQUESTS'))
253
+
254
+ usage.requests.daily.forEach((day: { date: string; count: number }) => {
255
+ console.log(`${day.date.padEnd(12)}${day.count.toLocaleString()}`)
256
+ })
257
+ }
258
+
259
+ // Model usage if available
260
+ if (usage.models && usage.models.length > 0) {
261
+ console.log('')
262
+ console.log(chalk.bold('Model usage:'))
263
+ console.log(chalk.dim('─'.repeat(70)))
264
+ console.log(
265
+ chalk.dim('MODEL'.padEnd(20)) +
266
+ chalk.dim('REQUESTS'.padEnd(10)) +
267
+ chalk.dim('INPUT'.padEnd(12)) +
268
+ chalk.dim('OUTPUT'.padEnd(12)) +
269
+ chalk.dim('TOTAL TOKENS')
270
+ )
271
+
272
+ usage.models.forEach(
273
+ (model: {
274
+ name: string
275
+ requests: number
276
+ tokens: {
277
+ input: number
278
+ output: number
279
+ total: number
280
+ }
281
+ }) => {
282
+ console.log(
283
+ model.name.padEnd(20) +
284
+ model.requests.toString().padEnd(10) +
285
+ model.tokens.input.toLocaleString().padEnd(12) +
286
+ model.tokens.output.toLocaleString().padEnd(12) +
287
+ model.tokens.total.toLocaleString()
288
+ )
289
+ }
290
+ )
291
+ }
292
+
293
+ console.log('')
294
+ console.log(
295
+ chalk.dim(
296
+ 'Use these statistics to understand your API usage and optimize your costs.'
297
+ )
298
+ )
299
+ } catch (error) {
300
+ handleError('Failed to get API key usage', error)
301
+ }
302
+ })
303
+
304
+ apiKey
305
+ .command(ApiKeyService.COMMANDS.SET_DEFAULT)
306
+ .description('Set an API key as the default for chat commands')
307
+ .argument('<id>', 'ID of the API key to set as default')
308
+ .action(async (id) => {
309
+ try {
310
+ const apiKeyService = ApiKeyService.getInstance()
311
+ const keys = await apiKeyService.list()
312
+ const selectedKey = keys.find(key => key.id.toString() === id)
313
+
314
+ if (!selectedKey) {
315
+ console.error(chalk.red(`Error: API key with ID ${id} not found`))
316
+ return
317
+ }
318
+
319
+ // Save the default API key
320
+ const defaultApiKeyManager = DefaultApiKeyManager.getInstance()
321
+ defaultApiKeyManager.setDefaultApiKey(
322
+ id,
323
+ selectedKey.name,
324
+ selectedKey.prefix
325
+ )
326
+
327
+ console.log(chalk.green(`✓ API key "${selectedKey.name}" set as default for chat commands`))
328
+ console.log('')
329
+ console.log(chalk.dim('This API key will be used by default when running chat commands'))
330
+ console.log(chalk.dim('You can override it with --api-key or --api-key-id options'))
331
+ } catch (error) {
332
+ handleError('Failed to set default API key', error)
333
+ }
334
+ })
335
+
336
+ apiKey
337
+ .command(ApiKeyService.COMMANDS.GET_DEFAULT)
338
+ .description('Show the current default API key')
339
+ .action(() => {
340
+ try {
341
+ const defaultApiKeyManager = DefaultApiKeyManager.getInstance()
342
+ const defaultApiKey = defaultApiKeyManager.getDefaultApiKey()
343
+
344
+ if (!defaultApiKey) {
345
+ console.log(chalk.yellow('No default API key set'))
346
+ console.log('')
347
+ console.log('To set a default API key, run:')
348
+ console.log(chalk.cyan(' berget api-keys set-default <id>'))
349
+ return
350
+ }
351
+
352
+ console.log(chalk.bold('Default API key:'))
353
+ console.log('')
354
+ console.log(`${chalk.dim('ID:')} ${defaultApiKey.id}`)
355
+ console.log(`${chalk.dim('Name:')} ${defaultApiKey.name}`)
356
+ console.log(`${chalk.dim('Prefix:')} ${defaultApiKey.prefix}`)
357
+ console.log('')
358
+ console.log(chalk.dim('This API key will be used by default when running chat commands'))
359
+ console.log(chalk.dim('You can override it with --api-key or --api-key-id options'))
360
+ } catch (error) {
361
+ handleError('Failed to get default API key', error)
362
+ }
363
+ })
364
+ }
@@ -0,0 +1,58 @@
1
+ import { Command } from 'commander'
2
+ import chalk from 'chalk'
3
+ import { AuthService } from '../services/auth-service'
4
+ import { clearAuthToken } from '../client'
5
+ import { handleError } from '../utils/error-handler'
6
+
7
+ /**
8
+ * Register authentication commands
9
+ */
10
+ export function registerAuthCommands(program: Command): void {
11
+ const auth = program
12
+ .command(AuthService.COMMAND_GROUP)
13
+ .description('Manage authentication and authorization')
14
+
15
+ auth
16
+ .command(AuthService.COMMANDS.LOGIN)
17
+ .description('Log in to Berget')
18
+ .action(async () => {
19
+ const authService = AuthService.getInstance()
20
+ await authService.login()
21
+ })
22
+
23
+ auth
24
+ .command(AuthService.COMMANDS.LOGOUT)
25
+ .description('Log out from Berget')
26
+ .action(() => {
27
+ clearAuthToken()
28
+ console.log(chalk.green('You have been logged out from Berget'))
29
+ })
30
+
31
+ auth
32
+ .command(AuthService.COMMANDS.WHOAMI)
33
+ .description('Show information about the logged in user')
34
+ .action(async () => {
35
+ try {
36
+ const authService = AuthService.getInstance()
37
+ const profile = await authService.whoami()
38
+
39
+ if (profile) {
40
+ console.log(
41
+ chalk.bold(`Logged in as: ${profile.name || profile.login}`)
42
+ )
43
+ console.log(`Email: ${chalk.cyan(profile.email || 'Not available')}`)
44
+ console.log(`Role: ${chalk.cyan(profile.role || 'Not available')}`)
45
+
46
+ if (profile.company) {
47
+ console.log(`Company: ${chalk.cyan(profile.company.name)}`)
48
+ }
49
+ } else {
50
+ console.log(
51
+ chalk.yellow('You are not logged in. Use `berget login` to log in.')
52
+ )
53
+ }
54
+ } catch (error) {
55
+ handleError('You are not logged in or an error occurred', error)
56
+ }
57
+ })
58
+ }
@@ -0,0 +1,19 @@
1
+ import { Command } from 'commander'
2
+ import chalk from 'chalk'
3
+
4
+ /**
5
+ * Register autocomplete commands
6
+ */
7
+ export function registerAutocompleteCommands(program: Command): void {
8
+ const autocomplete = program
9
+ .command('autocomplete')
10
+ .command('install')
11
+ .description('Install shell autocompletion')
12
+ .action(() => {
13
+ console.log(chalk.green('✓ Berget autocomplete installed in your shell'))
14
+ console.log(chalk.green('✓ Shell completion for kubectl also installed'))
15
+ console.log('')
16
+ console.log('Restart your shell or run:')
17
+ console.log(' source ~/.bashrc')
18
+ })
19
+ }
@@ -0,0 +1,41 @@
1
+ import { Command } from 'commander'
2
+ import { COMMAND_GROUPS, SUBCOMMANDS } from '../constants/command-structure'
3
+ import { createAuthenticatedClient } from '../client'
4
+ import { handleError } from '../utils/error-handler'
5
+
6
+ /**
7
+ * Register billing commands
8
+ */
9
+ export function registerBillingCommands(program: Command): void {
10
+ const billing = program
11
+ .command(COMMAND_GROUPS.BILLING)
12
+ .description('Manage billing and usage')
13
+
14
+ billing
15
+ .command(SUBCOMMANDS.BILLING.GET_USAGE)
16
+ .description('Get token usage statistics')
17
+ .option('--model <modelId>', 'Get usage for a specific model')
18
+ .action(async (options) => {
19
+ try {
20
+ const client = createAuthenticatedClient()
21
+ let response
22
+
23
+ if (options.model) {
24
+ const { data, error } = await client.GET('/v1/usage/tokens/{modelId}', {
25
+ params: { path: { modelId: options.model } },
26
+ })
27
+ if (error) throw new Error(JSON.stringify(error))
28
+ response = data
29
+ } else {
30
+ const { data, error } = await client.GET('/v1/usage/tokens')
31
+ if (error) throw new Error(JSON.stringify(error))
32
+ response = data
33
+ }
34
+
35
+ console.log('Token Usage:')
36
+ console.log(JSON.stringify(response, null, 2))
37
+ } catch (error) {
38
+ handleError('Failed to get token usage', error)
39
+ }
40
+ })
41
+ }