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.
- package/README.md +92 -0
- package/dist/index.js +7 -471
- package/dist/src/client.js +193 -102
- package/dist/src/commands/api-keys.js +271 -0
- package/dist/src/commands/auth.js +65 -0
- package/dist/src/commands/autocomplete.js +24 -0
- package/dist/src/commands/billing.js +53 -0
- package/dist/src/commands/chat.js +276 -0
- package/dist/src/commands/clusters.js +69 -0
- package/dist/src/commands/index.js +25 -0
- package/dist/src/commands/models.js +69 -0
- package/dist/src/commands/users.js +43 -0
- package/dist/src/constants/command-structure.js +14 -0
- package/dist/src/services/auth-service.js +49 -47
- package/dist/src/services/chat-service.js +177 -0
- package/dist/src/utils/config-checker.js +50 -0
- package/dist/src/utils/default-api-key.js +111 -0
- package/dist/src/utils/token-manager.js +165 -0
- package/index.ts +5 -566
- package/package.json +6 -1
- package/src/client.ts +262 -80
- package/src/commands/api-keys.ts +364 -0
- package/src/commands/auth.ts +58 -0
- package/src/commands/autocomplete.ts +19 -0
- package/src/commands/billing.ts +41 -0
- package/src/commands/chat.ts +345 -0
- package/src/commands/clusters.ts +65 -0
- package/src/commands/index.ts +23 -0
- package/src/commands/models.ts +63 -0
- package/src/commands/users.ts +37 -0
- package/src/constants/command-structure.ts +16 -0
- package/src/services/auth-service.ts +90 -50
- package/src/services/chat-service.ts +177 -0
- package/src/types/api.d.ts +58 -192
- package/src/utils/config-checker.ts +23 -0
- package/src/utils/default-api-key.ts +94 -0
- package/src/utils/token-manager.ts +150 -0
|
@@ -0,0 +1,345 @@
|
|
|
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 } 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: berget-70b-instruct)')
|
|
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
|
+
// If no API key or API key ID provided, check for default API key
|
|
57
|
+
if (!apiKey && !apiKeyId) {
|
|
58
|
+
const defaultApiKeyManager = DefaultApiKeyManager.getInstance()
|
|
59
|
+
const defaultApiKey = defaultApiKeyManager.getDefaultApiKey()
|
|
60
|
+
|
|
61
|
+
if (defaultApiKey) {
|
|
62
|
+
apiKeyId = defaultApiKey.id
|
|
63
|
+
console.log(
|
|
64
|
+
chalk.dim(`Using default API key: ${defaultApiKey.name}`)
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// If no direct API key, try to get one from API key ID
|
|
70
|
+
if (!apiKey && apiKeyId) {
|
|
71
|
+
try {
|
|
72
|
+
const apiKeyService = ApiKeyService.getInstance()
|
|
73
|
+
const keys = await apiKeyService.list()
|
|
74
|
+
const selectedKey = keys.find(
|
|
75
|
+
(key) => key.id.toString() === options.apiKeyId
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
if (!selectedKey) {
|
|
79
|
+
console.log(
|
|
80
|
+
chalk.yellow(
|
|
81
|
+
`API key with ID ${options.apiKeyId} not found. Using default authentication.`
|
|
82
|
+
)
|
|
83
|
+
)
|
|
84
|
+
} else {
|
|
85
|
+
console.log(chalk.dim(`Using API key: ${selectedKey.name}`))
|
|
86
|
+
|
|
87
|
+
// We need to rotate the key to get the actual key value
|
|
88
|
+
if (
|
|
89
|
+
await confirm(
|
|
90
|
+
chalk.yellow(
|
|
91
|
+
`To use API key "${selectedKey.name}", it needs to be rotated. This will invalidate the current key. Continue? (y/n)`
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
) {
|
|
95
|
+
const rotatedKey = await apiKeyService.rotate(options.apiKeyId)
|
|
96
|
+
apiKey = rotatedKey.key
|
|
97
|
+
console.log(
|
|
98
|
+
chalk.green(
|
|
99
|
+
`API key "${selectedKey.name}" rotated successfully.`
|
|
100
|
+
)
|
|
101
|
+
)
|
|
102
|
+
} else {
|
|
103
|
+
console.log(
|
|
104
|
+
chalk.yellow('Using default authentication instead.')
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
} catch (error) {
|
|
109
|
+
console.error(chalk.red('Error fetching API key:'))
|
|
110
|
+
console.error(error)
|
|
111
|
+
console.log(chalk.yellow('Using default authentication instead.'))
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Verify we have authentication before starting chat
|
|
116
|
+
if (!apiKey) {
|
|
117
|
+
try {
|
|
118
|
+
AuthService.getInstance()
|
|
119
|
+
} catch (error) {
|
|
120
|
+
console.log(chalk.red('Error: Authentication required for chat'))
|
|
121
|
+
console.log(chalk.yellow('Please either:'))
|
|
122
|
+
console.log(chalk.yellow('1. Log in with `berget auth login`'))
|
|
123
|
+
console.log(chalk.yellow('2. Provide an API key with `--api-key`'))
|
|
124
|
+
console.log(
|
|
125
|
+
chalk.yellow('3. Provide an API key ID with `--api-key-id`')
|
|
126
|
+
)
|
|
127
|
+
console.log(
|
|
128
|
+
chalk.yellow(
|
|
129
|
+
'4. Set a default API key with `berget api-keys set-default <id>`'
|
|
130
|
+
)
|
|
131
|
+
)
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Set up readline interface for user input
|
|
137
|
+
const rl = readline.createInterface({
|
|
138
|
+
input: process.stdin,
|
|
139
|
+
output: process.stdout,
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
// Prepare messages array
|
|
143
|
+
const messages: ChatMessage[] = []
|
|
144
|
+
|
|
145
|
+
// Add system message if provided
|
|
146
|
+
if (options.system) {
|
|
147
|
+
messages.push({
|
|
148
|
+
role: 'system',
|
|
149
|
+
content: options.system,
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
console.log(chalk.cyan('Chat with Berget AI (type "exit" to quit)'))
|
|
154
|
+
console.log(chalk.cyan('----------------------------------------'))
|
|
155
|
+
|
|
156
|
+
// Start the conversation loop
|
|
157
|
+
const askQuestion = () => {
|
|
158
|
+
rl.question(chalk.green('You: '), async (input) => {
|
|
159
|
+
// Check if user wants to exit
|
|
160
|
+
if (input.toLowerCase() === 'exit') {
|
|
161
|
+
console.log(chalk.cyan('Goodbye!'))
|
|
162
|
+
rl.close()
|
|
163
|
+
return
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Add user message
|
|
167
|
+
messages.push({
|
|
168
|
+
role: 'user',
|
|
169
|
+
content: input,
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
// Call the API
|
|
174
|
+
const response = await chatService.createCompletion({
|
|
175
|
+
model: options.args?.[0] || 'berget-70b-instruct',
|
|
176
|
+
messages: messages,
|
|
177
|
+
temperature:
|
|
178
|
+
options.temperature !== undefined ? options.temperature : 0.7,
|
|
179
|
+
max_tokens: options.maxTokens || 4096,
|
|
180
|
+
apiKey: apiKey,
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
// Debug output
|
|
184
|
+
if (program.opts().debug) {
|
|
185
|
+
console.log(chalk.yellow('DEBUG: Full response:'))
|
|
186
|
+
console.log(chalk.yellow(JSON.stringify(response, null, 2)))
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Check if response has the expected structure
|
|
190
|
+
if (
|
|
191
|
+
!response ||
|
|
192
|
+
!response.choices ||
|
|
193
|
+
!response.choices[0] ||
|
|
194
|
+
!response.choices[0].message
|
|
195
|
+
) {
|
|
196
|
+
console.error(
|
|
197
|
+
chalk.red('Error: Unexpected response format from API')
|
|
198
|
+
)
|
|
199
|
+
console.error(
|
|
200
|
+
chalk.red('Response:', JSON.stringify(response, null, 2))
|
|
201
|
+
)
|
|
202
|
+
throw new Error('Unexpected response format from API')
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Get assistant's response
|
|
206
|
+
const assistantMessage = response.choices[0].message.content
|
|
207
|
+
|
|
208
|
+
// Add to messages array
|
|
209
|
+
messages.push({
|
|
210
|
+
role: 'assistant',
|
|
211
|
+
content: assistantMessage,
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
// Display the response
|
|
215
|
+
console.log(chalk.blue('Assistant: ') + assistantMessage)
|
|
216
|
+
console.log() // Empty line for better readability
|
|
217
|
+
|
|
218
|
+
// Continue the conversation
|
|
219
|
+
askQuestion()
|
|
220
|
+
} catch (error) {
|
|
221
|
+
console.error(chalk.red('Error: Failed to get response'))
|
|
222
|
+
if (error instanceof Error) {
|
|
223
|
+
console.error(chalk.red(error.message))
|
|
224
|
+
}
|
|
225
|
+
// Continue despite error
|
|
226
|
+
askQuestion()
|
|
227
|
+
}
|
|
228
|
+
})
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Start the conversation
|
|
232
|
+
askQuestion()
|
|
233
|
+
} catch (error) {
|
|
234
|
+
handleError('Failed to create chat completion', error)
|
|
235
|
+
}
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
chat
|
|
239
|
+
.command(SUBCOMMANDS.CHAT.LIST)
|
|
240
|
+
.description('List available chat models')
|
|
241
|
+
.option('-k, --api-key <key>', 'API key to use for this request')
|
|
242
|
+
.option(
|
|
243
|
+
'--api-key-id <id>',
|
|
244
|
+
'ID of the API key to use from your saved keys'
|
|
245
|
+
)
|
|
246
|
+
.action(async (options) => {
|
|
247
|
+
try {
|
|
248
|
+
// If API key ID is provided, fetch the actual key
|
|
249
|
+
let apiKey = options.apiKey
|
|
250
|
+
let apiKeyId = options.apiKeyId
|
|
251
|
+
|
|
252
|
+
// If no API key or API key ID provided, check for default API key
|
|
253
|
+
if (!apiKey && !apiKeyId) {
|
|
254
|
+
const defaultApiKeyManager = DefaultApiKeyManager.getInstance()
|
|
255
|
+
const defaultApiKey = defaultApiKeyManager.getDefaultApiKey()
|
|
256
|
+
|
|
257
|
+
if (defaultApiKey) {
|
|
258
|
+
apiKeyId = defaultApiKey.id
|
|
259
|
+
console.log(
|
|
260
|
+
chalk.dim(`Using default API key: ${defaultApiKey.name}`)
|
|
261
|
+
)
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (apiKeyId && !apiKey) {
|
|
266
|
+
try {
|
|
267
|
+
const apiKeyService = ApiKeyService.getInstance()
|
|
268
|
+
const keys = await apiKeyService.list()
|
|
269
|
+
const selectedKey = keys.find(
|
|
270
|
+
(key) => key.id.toString() === options.apiKeyId
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
if (!selectedKey) {
|
|
274
|
+
console.log(
|
|
275
|
+
chalk.yellow(
|
|
276
|
+
`API key with ID ${options.apiKeyId} not found. Using default authentication.`
|
|
277
|
+
)
|
|
278
|
+
)
|
|
279
|
+
} else {
|
|
280
|
+
console.log(chalk.dim(`Using API key: ${selectedKey.name}`))
|
|
281
|
+
|
|
282
|
+
// We need to rotate the key to get the actual key value
|
|
283
|
+
if (
|
|
284
|
+
await confirm(
|
|
285
|
+
chalk.yellow(
|
|
286
|
+
`To use API key "${selectedKey.name}", it needs to be rotated. This will invalidate the current key. Continue? (y/n)`
|
|
287
|
+
)
|
|
288
|
+
)
|
|
289
|
+
) {
|
|
290
|
+
const rotatedKey = await apiKeyService.rotate(options.apiKeyId)
|
|
291
|
+
apiKey = rotatedKey.key
|
|
292
|
+
console.log(
|
|
293
|
+
chalk.green(
|
|
294
|
+
`API key "${selectedKey.name}" rotated successfully.`
|
|
295
|
+
)
|
|
296
|
+
)
|
|
297
|
+
} else {
|
|
298
|
+
console.log(
|
|
299
|
+
chalk.yellow('Using default authentication instead.')
|
|
300
|
+
)
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
} catch (error) {
|
|
304
|
+
console.error(chalk.red('Error fetching API key:'))
|
|
305
|
+
console.error(error)
|
|
306
|
+
console.log(chalk.yellow('Using default authentication instead.'))
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const chatService = ChatService.getInstance()
|
|
311
|
+
const models = await chatService.listModels(apiKey)
|
|
312
|
+
|
|
313
|
+
// Debug output
|
|
314
|
+
if (program.opts().debug) {
|
|
315
|
+
console.log(chalk.yellow('DEBUG: Models response:'))
|
|
316
|
+
console.log(chalk.yellow(JSON.stringify(models, null, 2)))
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
console.log(chalk.bold('Available Chat Models:'))
|
|
320
|
+
console.log(chalk.dim('─'.repeat(70)))
|
|
321
|
+
console.log(
|
|
322
|
+
chalk.dim('MODEL ID'.padEnd(30)) +
|
|
323
|
+
chalk.dim('OWNER'.padEnd(25)) +
|
|
324
|
+
chalk.dim('CAPABILITIES')
|
|
325
|
+
)
|
|
326
|
+
console.log(chalk.dim('─'.repeat(70)))
|
|
327
|
+
|
|
328
|
+
models.data.forEach((model: any) => {
|
|
329
|
+
const capabilities = []
|
|
330
|
+
if (model.capabilities.vision) capabilities.push('vision')
|
|
331
|
+
if (model.capabilities.function_calling)
|
|
332
|
+
capabilities.push('function_calling')
|
|
333
|
+
if (model.capabilities.json_mode) capabilities.push('json_mode')
|
|
334
|
+
|
|
335
|
+
console.log(
|
|
336
|
+
model.id.padEnd(30) +
|
|
337
|
+
model.owned_by.padEnd(25) +
|
|
338
|
+
capabilities.join(', ')
|
|
339
|
+
)
|
|
340
|
+
})
|
|
341
|
+
} catch (error) {
|
|
342
|
+
handleError('Failed to list chat models', error)
|
|
343
|
+
}
|
|
344
|
+
})
|
|
345
|
+
}
|
|
@@ -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
|
}
|