berget 1.0.0 → 1.2.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 (44) hide show
  1. package/README.md +92 -0
  2. package/dist/index.js +49 -467
  3. package/dist/package.json +35 -0
  4. package/dist/src/client.js +210 -102
  5. package/dist/src/commands/api-keys.js +277 -0
  6. package/dist/src/commands/auth.js +65 -0
  7. package/dist/src/commands/autocomplete.js +24 -0
  8. package/dist/src/commands/billing.js +53 -0
  9. package/dist/src/commands/chat.js +342 -0
  10. package/dist/src/commands/clusters.js +69 -0
  11. package/dist/src/commands/index.js +25 -0
  12. package/dist/src/commands/models.js +69 -0
  13. package/dist/src/commands/users.js +43 -0
  14. package/dist/src/constants/command-structure.js +14 -0
  15. package/dist/src/services/api-key-service.js +6 -16
  16. package/dist/src/services/auth-service.js +49 -47
  17. package/dist/src/services/chat-service.js +300 -0
  18. package/dist/src/utils/config-checker.js +50 -0
  19. package/dist/src/utils/default-api-key.js +237 -0
  20. package/dist/src/utils/error-handler.js +4 -4
  21. package/dist/src/utils/token-manager.js +165 -0
  22. package/index.ts +56 -566
  23. package/package.json +8 -2
  24. package/src/client.ts +279 -80
  25. package/src/commands/api-keys.ts +374 -0
  26. package/src/commands/auth.ts +58 -0
  27. package/src/commands/autocomplete.ts +19 -0
  28. package/src/commands/billing.ts +41 -0
  29. package/src/commands/chat.ts +445 -0
  30. package/src/commands/clusters.ts +65 -0
  31. package/src/commands/index.ts +23 -0
  32. package/src/commands/models.ts +63 -0
  33. package/src/commands/users.ts +37 -0
  34. package/src/constants/command-structure.ts +16 -0
  35. package/src/services/api-key-service.ts +12 -20
  36. package/src/services/auth-service.ts +90 -50
  37. package/src/services/chat-service.ts +295 -0
  38. package/src/types/api.d.ts +238 -178
  39. package/src/types/json.d.ts +4 -0
  40. package/src/utils/config-checker.ts +23 -0
  41. package/src/utils/default-api-key.ts +229 -0
  42. package/src/utils/error-handler.ts +4 -4
  43. package/src/utils/token-manager.ts +150 -0
  44. package/tsconfig.json +1 -1
@@ -0,0 +1,445 @@
1
+ import { Command } from 'commander'
2
+ import chalk from 'chalk'
3
+ import readline from 'readline'
4
+ import { COMMAND_GROUPS, SUBCOMMANDS } from '../constants/command-structure'
5
+ import { ChatService, ChatMessage, ChatCompletionOptions } from '../services/chat-service'
6
+ import { ApiKeyService } from '../services/api-key-service'
7
+ import { AuthService } from '../services/auth-service'
8
+ import { handleError } from '../utils/error-handler'
9
+ import { DefaultApiKeyManager } from '../utils/default-api-key'
10
+
11
+ /**
12
+ * Helper function to get user confirmation
13
+ */
14
+ async function confirm(question: string): Promise<boolean> {
15
+ const rl = readline.createInterface({
16
+ input: process.stdin,
17
+ output: process.stdout,
18
+ })
19
+
20
+ return new Promise<boolean>((resolve) => {
21
+ rl.question(question, (answer) => {
22
+ rl.close()
23
+ resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes')
24
+ })
25
+ })
26
+ }
27
+
28
+ /**
29
+ * Register chat commands
30
+ */
31
+ export function registerChatCommands(program: Command): void {
32
+ const chat = program
33
+ .command(COMMAND_GROUPS.CHAT)
34
+ .description('Interact with AI chat models')
35
+
36
+ chat
37
+ .command(SUBCOMMANDS.CHAT.RUN)
38
+ .description('Run a chat session with a specified model')
39
+ .argument('[model]', 'Model to use (default: google/gemma-3-27b-it)')
40
+ .option('-s, --system <message>', 'System message')
41
+ .option('-t, --temperature <temp>', 'Temperature (0-1)', parseFloat)
42
+ .option('-m, --max-tokens <tokens>', 'Maximum tokens to generate', parseInt)
43
+ .option('-k, --api-key <key>', 'API key to use for this chat session')
44
+ .option(
45
+ '--api-key-id <id>',
46
+ 'ID of the API key to use from your saved keys'
47
+ )
48
+ .action(async (options) => {
49
+ try {
50
+ const chatService = ChatService.getInstance()
51
+
52
+ // Check if we have an API key or need to get one
53
+ let apiKey = options.apiKey
54
+ let apiKeyId = options.apiKeyId
55
+
56
+ // Check for environment variable first
57
+ const envApiKey = process.env.BERGET_API_KEY;
58
+ if (envApiKey) {
59
+ console.log(
60
+ chalk.dim(`Using API key from BERGET_API_KEY environment variable`)
61
+ )
62
+ apiKey = envApiKey;
63
+ }
64
+ // If no API key or API key ID provided and no env var, check for default API key
65
+ else if (!apiKey && !apiKeyId) {
66
+ try {
67
+ const defaultApiKeyManager = DefaultApiKeyManager.getInstance()
68
+ const defaultApiKeyData = defaultApiKeyManager.getDefaultApiKeyData()
69
+
70
+ if (defaultApiKeyData) {
71
+ apiKeyId = defaultApiKeyData.id
72
+ apiKey = defaultApiKeyData.key
73
+
74
+ if (apiKey) {
75
+ console.log(
76
+ chalk.dim(`Using default API key: ${defaultApiKeyData.name}`)
77
+ )
78
+ } else {
79
+ console.log(
80
+ chalk.yellow(`Default API key "${defaultApiKeyData.name}" exists but the key value is missing.`)
81
+ )
82
+ console.log(
83
+ chalk.yellow(`Try rotating the key with: berget api-keys rotate ${defaultApiKeyData.id}`)
84
+ )
85
+ }
86
+ } else {
87
+ // No default API key, prompt the user to create one
88
+ console.log(chalk.yellow('No default API key set.'))
89
+
90
+ // Try to prompt for a default API key
91
+ apiKey = await defaultApiKeyManager.promptForDefaultApiKey()
92
+
93
+ if (!apiKey) {
94
+ console.log(
95
+ chalk.red(
96
+ 'Error: An API key is required to use the chat command.'
97
+ )
98
+ )
99
+ console.log(chalk.yellow('You can:'))
100
+ console.log(
101
+ chalk.yellow(
102
+ '1. Create an API key with: berget api-keys create --name "My Key"'
103
+ )
104
+ )
105
+ console.log(
106
+ chalk.yellow(
107
+ '2. Set a default API key with: berget api-keys set-default <id>'
108
+ )
109
+ )
110
+ console.log(
111
+ chalk.yellow(
112
+ '3. Provide an API key with the --api-key option'
113
+ )
114
+ )
115
+ return
116
+ }
117
+ }
118
+ } catch (error) {
119
+ if (process.argv.includes('--debug')) {
120
+ console.log(
121
+ chalk.yellow('DEBUG: Error checking default API key:')
122
+ )
123
+ console.log(chalk.yellow(String(error)))
124
+ }
125
+ }
126
+ }
127
+
128
+ // If no direct API key, try to get one from API key ID
129
+ if (!apiKey && apiKeyId) {
130
+ try {
131
+ const apiKeyService = ApiKeyService.getInstance()
132
+ const keys = await apiKeyService.list()
133
+ const selectedKey = keys.find(
134
+ (key) => key.id.toString() === options.apiKeyId
135
+ )
136
+
137
+ if (!selectedKey) {
138
+ console.log(
139
+ chalk.yellow(
140
+ `API key with ID ${options.apiKeyId} not found. Using default authentication.`
141
+ )
142
+ )
143
+ } else {
144
+ console.log(chalk.dim(`Using API key: ${selectedKey.name}`))
145
+
146
+ // We need to rotate the key to get the actual key value
147
+ if (
148
+ await confirm(
149
+ chalk.yellow(
150
+ `To use API key "${selectedKey.name}", it needs to be rotated. This will invalidate the current key. Continue? (y/n)`
151
+ )
152
+ )
153
+ ) {
154
+ const rotatedKey = await apiKeyService.rotate(options.apiKeyId)
155
+ apiKey = rotatedKey.key
156
+ console.log(
157
+ chalk.green(
158
+ `API key "${selectedKey.name}" rotated successfully.`
159
+ )
160
+ )
161
+ } else {
162
+ console.log(
163
+ chalk.yellow('Using default authentication instead.')
164
+ )
165
+ }
166
+ }
167
+ } catch (error) {
168
+ // Check if this is an authentication error
169
+ const errorMessage = error instanceof Error ? error.message : String(error);
170
+ const isAuthError = errorMessage.includes('Unauthorized') ||
171
+ errorMessage.includes('Authentication failed') ||
172
+ errorMessage.includes('AUTH_FAILED');
173
+
174
+ if (isAuthError) {
175
+ console.log(chalk.yellow('Authentication required. Please run `berget auth login` first.'));
176
+ } else {
177
+ console.error(chalk.red('Error fetching API key:'));
178
+ console.error(error);
179
+ }
180
+ console.log(chalk.yellow('Using default authentication instead.'));
181
+ }
182
+ }
183
+
184
+ // Verify we have authentication before starting chat
185
+ if (!apiKey) {
186
+ try {
187
+ AuthService.getInstance()
188
+ } catch (error) {
189
+ console.log(chalk.red('Error: Authentication required for chat'))
190
+ console.log(chalk.yellow('Please either:'))
191
+ console.log(chalk.yellow('1. Log in with `berget auth login`'))
192
+ console.log(chalk.yellow('2. Provide an API key with `--api-key`'))
193
+ console.log(
194
+ chalk.yellow('3. Provide an API key ID with `--api-key-id`')
195
+ )
196
+ console.log(
197
+ chalk.yellow(
198
+ '4. Set a default API key with `berget api-keys set-default <id>`'
199
+ )
200
+ )
201
+ return
202
+ }
203
+ }
204
+
205
+ // Set up readline interface for user input
206
+ const rl = readline.createInterface({
207
+ input: process.stdin,
208
+ output: process.stdout,
209
+ })
210
+
211
+ // Prepare messages array
212
+ const messages: ChatMessage[] = []
213
+
214
+ // Add system message if provided
215
+ if (options.system) {
216
+ messages.push({
217
+ role: 'system',
218
+ content: options.system,
219
+ })
220
+ }
221
+
222
+ console.log(chalk.cyan('Chat with Berget AI (type "exit" to quit)'))
223
+ console.log(chalk.cyan('----------------------------------------'))
224
+
225
+ // Start the conversation loop
226
+ const askQuestion = () => {
227
+ rl.question(chalk.green('You: '), async (input) => {
228
+ // Check if user wants to exit
229
+ if (input.toLowerCase() === 'exit') {
230
+ console.log(chalk.cyan('Goodbye!'))
231
+ rl.close()
232
+ return
233
+ }
234
+
235
+ // Add user message
236
+ messages.push({
237
+ role: 'user',
238
+ content: input,
239
+ })
240
+
241
+ try {
242
+ // Call the API
243
+ console.log(chalk.yellow('DEBUG: Preparing completion options'))
244
+
245
+ const completionOptions: ChatCompletionOptions = {
246
+ model: options.args?.[0] || 'google/gemma-3-27b-it',
247
+ messages: messages,
248
+ temperature:
249
+ options.temperature !== undefined ? options.temperature : 0.7,
250
+ max_tokens: options.maxTokens || 4096,
251
+ }
252
+
253
+ // Only add apiKey if it actually exists
254
+ if (apiKey) {
255
+ completionOptions.apiKey = apiKey
256
+ console.log(chalk.yellow('DEBUG: Using API key from command options or default'))
257
+ } else {
258
+ console.log(chalk.yellow('DEBUG: No API key available in chat command'))
259
+ // If we got this far with defaultApiKeyData but no apiKey, there's a problem
260
+ const defaultApiKeyManager = DefaultApiKeyManager.getInstance();
261
+ if (defaultApiKeyManager.getDefaultApiKeyData()) {
262
+ console.log(chalk.yellow('DEBUG: Default API key data exists but key is missing'))
263
+ }
264
+ }
265
+
266
+ // Debug output
267
+ console.log(chalk.yellow('DEBUG: Completion options:'))
268
+ console.log(chalk.yellow(JSON.stringify({
269
+ ...completionOptions,
270
+ apiKey: completionOptions.apiKey ? '***' : undefined,
271
+ messages: completionOptions.messages.map((m: any) => ({
272
+ role: m.role,
273
+ content: m.content.length > 50 ? m.content.substring(0, 50) + '...' : m.content
274
+ }))
275
+ }, null, 2)))
276
+
277
+ console.log(chalk.yellow('DEBUG: Calling chatService.createCompletion'))
278
+
279
+ const response = await chatService.createCompletion(
280
+ completionOptions
281
+ )
282
+
283
+ // Debug output
284
+ if (program.opts().debug) {
285
+ console.log(chalk.yellow('DEBUG: Full response:'))
286
+ console.log(chalk.yellow(JSON.stringify(response, null, 2)))
287
+ }
288
+
289
+ // Check if response has the expected structure
290
+ if (
291
+ !response ||
292
+ !response.choices ||
293
+ !response.choices[0] ||
294
+ !response.choices[0].message
295
+ ) {
296
+ console.error(
297
+ chalk.red('Error: Unexpected response format from API')
298
+ )
299
+ console.error(
300
+ chalk.red('Response:', JSON.stringify(response, null, 2))
301
+ )
302
+ throw new Error('Unexpected response format from API')
303
+ }
304
+
305
+ // Get assistant's response
306
+ const assistantMessage = response.choices[0].message.content
307
+
308
+ // Add to messages array
309
+ messages.push({
310
+ role: 'assistant',
311
+ content: assistantMessage,
312
+ })
313
+
314
+ // Display the response
315
+ console.log(chalk.blue('Assistant: ') + assistantMessage)
316
+ console.log() // Empty line for better readability
317
+
318
+ // Continue the conversation
319
+ askQuestion()
320
+ } catch (error) {
321
+ console.error(chalk.red('Error: Failed to get response'))
322
+ if (error instanceof Error) {
323
+ console.error(chalk.red(error.message))
324
+ }
325
+ // Continue despite error
326
+ askQuestion()
327
+ }
328
+ })
329
+ }
330
+
331
+ // Start the conversation
332
+ askQuestion()
333
+ } catch (error) {
334
+ handleError('Failed to create chat completion', error)
335
+ }
336
+ })
337
+
338
+ chat
339
+ .command(SUBCOMMANDS.CHAT.LIST)
340
+ .description('List available chat models')
341
+ .option('-k, --api-key <key>', 'API key to use for this request')
342
+ .option(
343
+ '--api-key-id <id>',
344
+ 'ID of the API key to use from your saved keys'
345
+ )
346
+ .action(async (options) => {
347
+ try {
348
+ // If API key ID is provided, fetch the actual key
349
+ let apiKey = options.apiKey
350
+ let apiKeyId = options.apiKeyId
351
+
352
+ // If no API key or API key ID provided, check for default API key
353
+ if (!apiKey && !apiKeyId) {
354
+ const defaultApiKeyManager = DefaultApiKeyManager.getInstance()
355
+ const defaultApiKeyData = defaultApiKeyManager.getDefaultApiKeyData()
356
+
357
+ if (defaultApiKeyData) {
358
+ apiKeyId = defaultApiKeyData.id
359
+ console.log(
360
+ chalk.dim(`Using default API key: ${defaultApiKeyData.name}`)
361
+ )
362
+ }
363
+ }
364
+
365
+ if (apiKeyId && !apiKey) {
366
+ try {
367
+ const apiKeyService = ApiKeyService.getInstance()
368
+ const keys = await apiKeyService.list()
369
+ const selectedKey = keys.find(
370
+ (key) => key.id.toString() === options.apiKeyId
371
+ )
372
+
373
+ if (!selectedKey) {
374
+ console.log(
375
+ chalk.yellow(
376
+ `API key with ID ${options.apiKeyId} not found. Using default authentication.`
377
+ )
378
+ )
379
+ } else {
380
+ console.log(chalk.dim(`Using API key: ${selectedKey.name}`))
381
+
382
+ // We need to rotate the key to get the actual key value
383
+ if (
384
+ await confirm(
385
+ chalk.yellow(
386
+ `To use API key "${selectedKey.name}", it needs to be rotated. This will invalidate the current key. Continue? (y/n)`
387
+ )
388
+ )
389
+ ) {
390
+ const rotatedKey = await apiKeyService.rotate(options.apiKeyId)
391
+ apiKey = rotatedKey.key
392
+ console.log(
393
+ chalk.green(
394
+ `API key "${selectedKey.name}" rotated successfully.`
395
+ )
396
+ )
397
+ } else {
398
+ console.log(
399
+ chalk.yellow('Using default authentication instead.')
400
+ )
401
+ }
402
+ }
403
+ } catch (error) {
404
+ console.error(chalk.red('Error fetching API key:'))
405
+ console.error(error)
406
+ console.log(chalk.yellow('Using default authentication instead.'))
407
+ }
408
+ }
409
+
410
+ const chatService = ChatService.getInstance()
411
+ const models = await chatService.listModels(apiKey)
412
+
413
+ // Debug output
414
+ if (program.opts().debug) {
415
+ console.log(chalk.yellow('DEBUG: Models response:'))
416
+ console.log(chalk.yellow(JSON.stringify(models, null, 2)))
417
+ }
418
+
419
+ console.log(chalk.bold('Available Chat Models:'))
420
+ console.log(chalk.dim('─'.repeat(70)))
421
+ console.log(
422
+ chalk.dim('MODEL ID'.padEnd(30)) +
423
+ chalk.dim('OWNER'.padEnd(25)) +
424
+ chalk.dim('CAPABILITIES')
425
+ )
426
+ console.log(chalk.dim('─'.repeat(70)))
427
+
428
+ models.data.forEach((model: any) => {
429
+ const capabilities = []
430
+ if (model.capabilities.vision) capabilities.push('vision')
431
+ if (model.capabilities.function_calling)
432
+ capabilities.push('function_calling')
433
+ if (model.capabilities.json_mode) capabilities.push('json_mode')
434
+
435
+ console.log(
436
+ model.id.padEnd(30) +
437
+ model.owned_by.padEnd(25) +
438
+ capabilities.join(', ')
439
+ )
440
+ })
441
+ } catch (error) {
442
+ handleError('Failed to list chat models', error)
443
+ }
444
+ })
445
+ }
@@ -0,0 +1,65 @@
1
+ import { Command } from 'commander'
2
+ import { ClusterService, Cluster } from '../services/cluster-service'
3
+ import { handleError } from '../utils/error-handler'
4
+
5
+ /**
6
+ * Register cluster commands
7
+ */
8
+ export function registerClusterCommands(program: Command): void {
9
+ const cluster = program
10
+ .command(ClusterService.COMMAND_GROUP)
11
+ .description('Manage Berget clusters')
12
+
13
+ cluster
14
+ .command(ClusterService.COMMANDS.LIST)
15
+ .description('List all Berget clusters')
16
+ .action(async () => {
17
+ try {
18
+ const clusterService = ClusterService.getInstance()
19
+ const clusters = await clusterService.list()
20
+
21
+ console.log('NAME STATUS NODES CREATED')
22
+ clusters.forEach((cluster: Cluster) => {
23
+ console.log(
24
+ `${cluster.name.padEnd(22)} ${cluster.status.padEnd(9)} ${String(
25
+ cluster.nodes
26
+ ).padEnd(8)} ${cluster.created}`
27
+ )
28
+ })
29
+ } catch (error) {
30
+ handleError('Failed to list clusters', error)
31
+ }
32
+ })
33
+
34
+ cluster
35
+ .command(ClusterService.COMMANDS.GET_USAGE)
36
+ .description('Get usage metrics for a specific cluster')
37
+ .argument('<clusterId>', 'Cluster ID')
38
+ .action(async (clusterId) => {
39
+ try {
40
+ const clusterService = ClusterService.getInstance()
41
+ const usage = await clusterService.getUsage(clusterId)
42
+
43
+ console.log('Cluster Usage:')
44
+ console.log(JSON.stringify(usage, null, 2))
45
+ } catch (error) {
46
+ handleError('Failed to get cluster usage', error)
47
+ }
48
+ })
49
+
50
+ cluster
51
+ .command(ClusterService.COMMANDS.DESCRIBE)
52
+ .description('Get detailed information about a cluster')
53
+ .argument('<clusterId>', 'Cluster ID')
54
+ .action(async (clusterId) => {
55
+ try {
56
+ const clusterService = ClusterService.getInstance()
57
+ const clusterInfo = await clusterService.describe(clusterId)
58
+
59
+ console.log('Cluster Details:')
60
+ console.log(JSON.stringify(clusterInfo, null, 2))
61
+ } catch (error) {
62
+ handleError('Failed to describe cluster', error)
63
+ }
64
+ })
65
+ }
@@ -0,0 +1,23 @@
1
+ import { Command } from 'commander'
2
+ import { registerAuthCommands } from './auth'
3
+ import { registerApiKeyCommands } from './api-keys'
4
+ import { registerClusterCommands } from './clusters'
5
+ import { registerBillingCommands } from './billing'
6
+ import { registerModelCommands } from './models'
7
+ import { registerUserCommands } from './users'
8
+ import { registerChatCommands } from './chat'
9
+ import { registerAutocompleteCommands } from './autocomplete'
10
+
11
+ /**
12
+ * Register all command groups with the program
13
+ */
14
+ export function registerCommands(program: Command): void {
15
+ registerAuthCommands(program)
16
+ registerApiKeyCommands(program)
17
+ registerClusterCommands(program)
18
+ registerBillingCommands(program)
19
+ registerModelCommands(program)
20
+ registerUserCommands(program)
21
+ registerChatCommands(program)
22
+ registerAutocompleteCommands(program)
23
+ }
@@ -0,0 +1,63 @@
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 models commands
8
+ */
9
+ export function registerModelCommands(program: Command): void {
10
+ const models = program
11
+ .command(COMMAND_GROUPS.MODELS)
12
+ .description('Manage AI models')
13
+
14
+ models
15
+ .command(SUBCOMMANDS.MODELS.LIST)
16
+ .description('List available AI models')
17
+ .option('--id <modelId>', 'Get details for a specific model')
18
+ .action(async (options) => {
19
+ try {
20
+ const client = createAuthenticatedClient()
21
+ let response
22
+
23
+ if (options.id) {
24
+ const { data, error } = await client.GET('/v1/models/{modelId}', {
25
+ params: { path: { modelId: options.id } },
26
+ })
27
+ if (error) throw new Error(JSON.stringify(error))
28
+ response = data
29
+
30
+ console.log('Model Details:')
31
+ console.log(JSON.stringify(response, null, 2))
32
+ } else {
33
+ const { data, error } = await client.GET('/v1/models')
34
+ if (error) throw new Error(JSON.stringify(error))
35
+ response = data
36
+
37
+ console.log('Available Models:')
38
+ console.log(
39
+ 'ID OWNED BY CAPABILITIES'
40
+ )
41
+ // Ensure response has the expected structure
42
+ const modelData = response as { data?: any[] };
43
+ if (modelData.data) {
44
+ modelData.data.forEach((model: any) => {
45
+ const capabilities = []
46
+ if (model.capabilities.vision) capabilities.push('vision')
47
+ if (model.capabilities.function_calling)
48
+ capabilities.push('function_calling')
49
+ if (model.capabilities.json_mode) capabilities.push('json_mode')
50
+
51
+ console.log(
52
+ `${model.id.padEnd(24)} ${model.owned_by.padEnd(
53
+ 25
54
+ )} ${capabilities.join(', ')}`
55
+ )
56
+ })
57
+ }
58
+ }
59
+ } catch (error) {
60
+ handleError('Failed to get models', error)
61
+ }
62
+ })
63
+ }
@@ -0,0 +1,37 @@
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
+ import chalk from 'chalk'
6
+
7
+ /**
8
+ * Register user commands
9
+ */
10
+ export function registerUserCommands(program: Command): void {
11
+ const users = program
12
+ .command(COMMAND_GROUPS.USERS)
13
+ .description('Manage users')
14
+
15
+ users
16
+ .command(SUBCOMMANDS.USERS.LIST)
17
+ .description('List team members')
18
+ .action(async () => {
19
+ try {
20
+ const client = createAuthenticatedClient()
21
+ const { data, error } = await client.GET('/v1/users')
22
+ if (error) throw new Error(JSON.stringify(error))
23
+
24
+ console.log('Team Members:')
25
+ console.log(
26
+ 'NAME EMAIL ROLE'
27
+ )
28
+ data.forEach((user: any) => {
29
+ console.log(
30
+ `${user.name.padEnd(24)} ${user.email.padEnd(30)} ${user.role}`
31
+ )
32
+ })
33
+ } catch (error) {
34
+ handleError('Failed to list team members', error)
35
+ }
36
+ })
37
+ }
@@ -15,6 +15,7 @@ export const COMMAND_GROUPS = {
15
15
  FLUX: 'flux',
16
16
  USERS: 'users',
17
17
  BILLING: 'billing',
18
+ CHAT: 'chat',
18
19
  }
19
20
 
20
21
  // Subcommands for each group
@@ -26,6 +27,12 @@ export const SUBCOMMANDS = {
26
27
  WHOAMI: 'whoami',
27
28
  },
28
29
 
30
+ // Chat commands
31
+ CHAT: {
32
+ RUN: 'run',
33
+ LIST: 'list',
34
+ },
35
+
29
36
  // API Keys commands
30
37
  API_KEYS: {
31
38
  LIST: 'list',
@@ -33,6 +40,8 @@ export const SUBCOMMANDS = {
33
40
  DELETE: 'delete',
34
41
  ROTATE: 'rotate',
35
42
  DESCRIBE: 'describe',
43
+ SET_DEFAULT: 'set-default',
44
+ GET_DEFAULT: 'get-default',
36
45
  },
37
46
 
38
47
  // Clusters commands
@@ -112,6 +121,8 @@ export const COMMAND_DESCRIPTIONS = {
112
121
  [`${COMMAND_GROUPS.API_KEYS} ${SUBCOMMANDS.API_KEYS.DELETE}`]: 'Delete an API key',
113
122
  [`${COMMAND_GROUPS.API_KEYS} ${SUBCOMMANDS.API_KEYS.ROTATE}`]: 'Rotate an API key',
114
123
  [`${COMMAND_GROUPS.API_KEYS} ${SUBCOMMANDS.API_KEYS.DESCRIBE}`]: 'Get usage statistics for an API key',
124
+ [`${COMMAND_GROUPS.API_KEYS} ${SUBCOMMANDS.API_KEYS.SET_DEFAULT}`]: 'Set an API key as the default for chat commands',
125
+ [`${COMMAND_GROUPS.API_KEYS} ${SUBCOMMANDS.API_KEYS.GET_DEFAULT}`]: 'Show the current default API key',
115
126
 
116
127
  // Clusters group
117
128
  [COMMAND_GROUPS.CLUSTERS]: 'Manage Kubernetes clusters',
@@ -165,4 +176,9 @@ export const COMMAND_DESCRIPTIONS = {
165
176
  [`${COMMAND_GROUPS.BILLING} ${SUBCOMMANDS.BILLING.ADD_PAYMENT_METHOD}`]: 'Add a new payment method',
166
177
  [`${COMMAND_GROUPS.BILLING} ${SUBCOMMANDS.BILLING.REMOVE_PAYMENT_METHOD}`]: 'Remove a payment method',
167
178
  [`${COMMAND_GROUPS.BILLING} ${SUBCOMMANDS.BILLING.UPDATE_SUBSCRIPTION}`]: 'Update subscription plan',
179
+
180
+ // Chat group
181
+ [COMMAND_GROUPS.CHAT]: 'Interact with AI chat models',
182
+ [`${COMMAND_GROUPS.CHAT} ${SUBCOMMANDS.CHAT.RUN}`]: 'Run a chat session with a specified model',
183
+ [`${COMMAND_GROUPS.CHAT} ${SUBCOMMANDS.CHAT.LIST}`]: 'List available chat models',
168
184
  }