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
@@ -33,10 +33,10 @@ export interface ApiKeyResponse {
33
33
  export class ApiKeyService {
34
34
  private static instance: ApiKeyService
35
35
  private client = createAuthenticatedClient()
36
-
36
+
37
37
  // Command group name for this service
38
38
  public static readonly COMMAND_GROUP = COMMAND_GROUPS.API_KEYS
39
-
39
+
40
40
  // Subcommands for this service
41
41
  public static readonly COMMANDS = SUBCOMMANDS.API_KEYS
42
42
 
@@ -56,18 +56,7 @@ export class ApiKeyService {
56
56
  public async list(): Promise<ApiKey[]> {
57
57
  try {
58
58
  const { data, error } = await this.client.GET('/v1/api-keys')
59
- if (error) {
60
- // Check if this is an authentication error
61
- const errorObj = typeof error === 'string' ? JSON.parse(error) : error;
62
- if (errorObj.status === 401) {
63
- throw new Error(JSON.stringify({
64
- error: "Authentication failed. Your session may have expired.",
65
- code: "AUTH_FAILED",
66
- details: "Please run 'berget login' to authenticate again."
67
- }))
68
- }
69
- throw new Error(JSON.stringify(error))
70
- }
59
+ if (error) throw error
71
60
  return data || []
72
61
  } catch (error) {
73
62
  handleError('Failed to list API keys', error)
@@ -82,7 +71,7 @@ export class ApiKeyService {
82
71
  public async create(options: CreateApiKeyOptions): Promise<ApiKeyResponse> {
83
72
  try {
84
73
  const { data, error } = await this.client.POST('/v1/api-keys', {
85
- body: options
74
+ body: options,
86
75
  })
87
76
  if (error) throw new Error(JSON.stringify(error))
88
77
  return data!
@@ -99,7 +88,7 @@ export class ApiKeyService {
99
88
  public async delete(id: string): Promise<boolean> {
100
89
  try {
101
90
  const { error } = await this.client.DELETE('/v1/api-keys/{id}', {
102
- params: { path: { id } }
91
+ params: { path: { id } },
103
92
  })
104
93
  if (error) throw new Error(JSON.stringify(error))
105
94
  return true
@@ -115,9 +104,12 @@ export class ApiKeyService {
115
104
  */
116
105
  public async rotate(id: string): Promise<ApiKeyResponse> {
117
106
  try {
118
- const { data, error } = await this.client.PUT('/v1/api-keys/{id}/rotate', {
119
- params: { path: { id } }
120
- })
107
+ const { data, error } = await this.client.PUT(
108
+ '/v1/api-keys/{id}/rotate',
109
+ {
110
+ params: { path: { id } },
111
+ }
112
+ )
121
113
  if (error) throw new Error(JSON.stringify(error))
122
114
  return data!
123
115
  } catch (error) {
@@ -133,7 +125,7 @@ export class ApiKeyService {
133
125
  public async describe(id: string): Promise<any> {
134
126
  try {
135
127
  const { data, error } = await this.client.GET('/v1/api-keys/{id}/usage', {
136
- params: { path: { id } }
128
+ params: { path: { id } },
137
129
  })
138
130
  if (error) throw new Error(JSON.stringify(error))
139
131
  return data
@@ -16,10 +16,10 @@ import { COMMAND_GROUPS, SUBCOMMANDS } from '../constants/command-structure'
16
16
  export class AuthService {
17
17
  private static instance: AuthService
18
18
  private client = createAuthenticatedClient()
19
-
19
+
20
20
  // Command group name for this service
21
21
  public static readonly COMMAND_GROUP = COMMAND_GROUPS.AUTH
22
-
22
+
23
23
  // Subcommands for this service
24
24
  public static readonly COMMANDS = SUBCOMMANDS.AUTH
25
25
 
@@ -32,6 +32,21 @@ export class AuthService {
32
32
  return AuthService.instance
33
33
  }
34
34
 
35
+ public async whoami(): Promise<any> {
36
+ try {
37
+ const { data: profile, error } = await this.client.GET('/v1/users/me')
38
+ if (error) {
39
+ throw new Error(
40
+ error ? JSON.stringify(error) : 'Failed to get user profile'
41
+ )
42
+ }
43
+ return profile
44
+ } catch (error) {
45
+ handleError('Failed to get user profile', error)
46
+ return null
47
+ }
48
+ }
49
+
35
50
  public async login(): Promise<boolean> {
36
51
  try {
37
52
  // Clear any existing token to ensure a fresh login
@@ -53,27 +68,40 @@ export class AuthService {
53
68
  )
54
69
  }
55
70
 
71
+ // Type assertion for deviceData
72
+ const typedDeviceData = deviceData as {
73
+ verification_url?: string
74
+ user_code?: string
75
+ device_code?: string
76
+ expires_in?: number
77
+ interval?: number
78
+ }
79
+
56
80
  // Display information to user
57
81
  console.log(chalk.cyan('\nTo complete login:'))
58
82
  console.log(
59
83
  chalk.cyan(
60
84
  `1. Open this URL: ${chalk.bold(
61
- deviceData.verification_url || 'https://auth.berget.ai/device'
85
+ typedDeviceData.verification_url ||
86
+ 'https://keycloak.berget.ai/device'
62
87
  )}`
63
88
  )
64
89
  )
65
- console.log(
66
- chalk.cyan(
67
- `2. Enter this code: ${chalk.bold(deviceData.user_code || '')}\n`
90
+ if (!typedDeviceData.verification_url)
91
+ console.log(
92
+ chalk.cyan(
93
+ `2. Enter this code: ${chalk.bold(
94
+ typedDeviceData.user_code || ''
95
+ )}\n`
96
+ )
68
97
  )
69
- )
70
98
 
71
99
  // Try to open browser automatically
72
100
  try {
73
- if (deviceData.verification_url) {
101
+ if (typedDeviceData.verification_url) {
74
102
  // Use dynamic import for the 'open' package
75
- const open = await import('open').then(m => m.default);
76
- await open(deviceData.verification_url);
103
+ const open = await import('open').then((m) => m.default)
104
+ await open(typedDeviceData.verification_url)
77
105
  console.log(
78
106
  chalk.dim(
79
107
  "Browser opened automatically. If it didn't open, please use the URL above."
@@ -93,9 +121,11 @@ export class AuthService {
93
121
  // Step 2: Poll for completion
94
122
  const startTime = Date.now()
95
123
  const expiresIn =
96
- deviceData.expires_in !== undefined ? deviceData.expires_in : 900
124
+ typedDeviceData.expires_in !== undefined
125
+ ? typedDeviceData.expires_in
126
+ : 900
97
127
  const expiresAt = startTime + expiresIn * 1000
98
- let pollInterval = (deviceData.interval || 5) * 1000
128
+ let pollInterval = (typedDeviceData.interval || 5) * 1000
99
129
 
100
130
  const spinner = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
101
131
  let spinnerIdx = 0
@@ -111,7 +141,7 @@ export class AuthService {
111
141
  spinnerIdx = (spinnerIdx + 1) % spinner.length
112
142
 
113
143
  // Check if authentication is complete
114
- const deviceCode = deviceData.device_code || ''
144
+ const deviceCode = typedDeviceData.device_code || ''
115
145
  const { data: tokenData, error: tokenError } = await apiClient.POST(
116
146
  '/v1/auth/device/token',
117
147
  {
@@ -170,21 +200,56 @@ export class AuthService {
170
200
  }
171
201
  continue
172
202
  }
173
- } else if (tokenData && tokenData.token) {
174
- // Success!
175
- saveAuthToken(tokenData.token)
176
-
177
- process.stdout.write('\r' + ' '.repeat(50) + '\r') // Clear the spinner line
178
- console.log(chalk.green('✓ Successfully logged in to Berget'))
203
+ } else if (tokenData) {
204
+ // Type assertion for tokenData
205
+ const typedTokenData = tokenData as {
206
+ token?: string
207
+ refresh_token?: string
208
+ expires_in?: number
209
+ refresh_expires_in?: number
210
+ user?: {
211
+ id?: string
212
+ email?: string
213
+ name?: string
214
+ }
215
+ }
179
216
 
180
- if (tokenData.user) {
181
- const user = tokenData.user as any
182
- console.log(
183
- chalk.green(`Logged in as ${user.name || user.email || 'User'}`)
217
+ if (typedTokenData.token) {
218
+ // Success!
219
+ saveAuthToken(
220
+ typedTokenData.token,
221
+ typedTokenData.refresh_token || '',
222
+ typedTokenData.expires_in || 3600
184
223
  )
185
- }
186
224
 
187
- return true
225
+ if (process.argv.includes('--debug')) {
226
+ console.log(chalk.yellow('DEBUG: Token data received:'))
227
+ console.log(
228
+ chalk.yellow(
229
+ JSON.stringify(
230
+ {
231
+ expires_in: typedTokenData.expires_in,
232
+ refresh_expires_in: typedTokenData.refresh_expires_in,
233
+ },
234
+ null,
235
+ 2
236
+ )
237
+ )
238
+ )
239
+ }
240
+
241
+ process.stdout.write('\r' + ' '.repeat(50) + '\r') // Clear the spinner line
242
+ console.log(chalk.green('✓ Successfully logged in to Berget'))
243
+
244
+ if (typedTokenData.user) {
245
+ const user = typedTokenData.user
246
+ console.log(
247
+ chalk.green(`Logged in as ${user.name || user.email || 'User'}`)
248
+ )
249
+ }
250
+
251
+ return true
252
+ }
188
253
  }
189
254
  }
190
255
 
@@ -195,29 +260,4 @@ export class AuthService {
195
260
  return false
196
261
  }
197
262
  }
198
-
199
- public async isAuthenticated(): Promise<boolean> {
200
- try {
201
- // Call an API endpoint that requires authentication
202
- const { data, error } = await this.client.GET('/v1/users/me')
203
- return !!data && !error
204
- } catch {
205
- return false
206
- }
207
- }
208
-
209
- /**
210
- * Get current user profile
211
- * Command: berget auth whoami
212
- */
213
- public async whoami() {
214
- try {
215
- const { data, error } = await this.client.GET('/v1/users/me')
216
- if (error) throw new Error(JSON.stringify(error))
217
- return data
218
- } catch (error) {
219
- handleError('Failed to get user profile', error)
220
- throw error
221
- }
222
- }
223
263
  }
@@ -0,0 +1,295 @@
1
+ import { createAuthenticatedClient } from '../client'
2
+ import { COMMAND_GROUPS, SUBCOMMANDS } from '../constants/command-structure'
3
+ import chalk from 'chalk'
4
+
5
+ export interface ChatMessage {
6
+ role: 'system' | 'user' | 'assistant'
7
+ content: string
8
+ }
9
+
10
+ export interface ChatCompletionOptions {
11
+ model: string
12
+ messages: ChatMessage[]
13
+ temperature?: number
14
+ max_tokens?: number
15
+ stream?: boolean
16
+ top_p?: number
17
+ apiKey?: string
18
+ }
19
+
20
+ /**
21
+ * Service for interacting with the chat API
22
+ * Command group: chat
23
+ */
24
+ export class ChatService {
25
+ private static instance: ChatService
26
+ private client = createAuthenticatedClient()
27
+
28
+ // Command group name for this service
29
+ public static readonly COMMAND_GROUP = 'chat'
30
+
31
+ // Subcommands for this service
32
+ public static readonly COMMANDS = {
33
+ RUN: 'run',
34
+ LIST: 'list'
35
+ }
36
+
37
+ private constructor() {}
38
+
39
+ public static getInstance(): ChatService {
40
+ if (!ChatService.instance) {
41
+ ChatService.instance = new ChatService()
42
+ }
43
+ return ChatService.instance
44
+ }
45
+
46
+ /**
47
+ * Create a chat completion
48
+ * Command: berget chat completion
49
+ */
50
+ public async createCompletion(options: ChatCompletionOptions): Promise<any> {
51
+ try {
52
+ console.log(chalk.yellow('DEBUG: Starting createCompletion method'))
53
+
54
+ // Check if options is defined
55
+ if (!options) {
56
+ console.log(chalk.red('ERROR: options is undefined'))
57
+ throw new Error('Chat completion options are undefined')
58
+ }
59
+
60
+ // Log the raw options object
61
+ console.log(chalk.yellow('DEBUG: Raw options:'), typeof options, options ? 'defined' : 'undefined')
62
+
63
+ const headers: Record<string, string> = {}
64
+
65
+ // Check if debug is enabled
66
+ const isDebug = process.argv.includes('--debug')
67
+
68
+ if (isDebug) {
69
+ console.log(chalk.yellow('DEBUG: Starting createCompletion with options:'))
70
+ try {
71
+ console.log(chalk.yellow(JSON.stringify({
72
+ ...options,
73
+ apiKey: options.apiKey ? '***' : undefined,
74
+ messages: options.messages ? `${options.messages.length} messages` : undefined
75
+ }, null, 2)))
76
+ } catch (error) {
77
+ console.log(chalk.red('ERROR: Failed to stringify options:'), error)
78
+ }
79
+ }
80
+
81
+ // Create a copy of options to avoid modifying the original
82
+ const optionsCopy = { ...options }
83
+
84
+ if (isDebug) {
85
+ console.log(chalk.yellow('DEBUG: Checking for API key'))
86
+ console.log(chalk.yellow(`DEBUG: optionsCopy.apiKey exists: ${!!optionsCopy.apiKey}`))
87
+ }
88
+
89
+ // Check for environment variables first - prioritize this over everything else
90
+ const envApiKey = process.env.BERGET_API_KEY;
91
+ if (envApiKey) {
92
+ if (isDebug) {
93
+ console.log(chalk.yellow('DEBUG: Using API key from BERGET_API_KEY environment variable'));
94
+ }
95
+ optionsCopy.apiKey = envApiKey;
96
+ }
97
+ // Only try to get the default API key if no API key is provided and no env var is set
98
+ else if (!optionsCopy.apiKey) {
99
+ if (isDebug) {
100
+ console.log(chalk.yellow('DEBUG: No API key provided, trying to get default'))
101
+ }
102
+
103
+ try {
104
+ // Import the DefaultApiKeyManager directly
105
+ if (isDebug) {
106
+ console.log(chalk.yellow('DEBUG: Importing DefaultApiKeyManager'))
107
+ }
108
+
109
+ const DefaultApiKeyManager = (await import('../utils/default-api-key')).DefaultApiKeyManager;
110
+ const defaultApiKeyManager = DefaultApiKeyManager.getInstance();
111
+
112
+ if (isDebug) {
113
+ console.log(chalk.yellow('DEBUG: Got DefaultApiKeyManager instance'))
114
+ }
115
+
116
+ // Try to get the default API key
117
+ if (isDebug) {
118
+ console.log(chalk.yellow('DEBUG: Calling promptForDefaultApiKey'))
119
+ }
120
+
121
+ const defaultApiKeyData = defaultApiKeyManager.getDefaultApiKeyData();
122
+ const apiKey = defaultApiKeyData?.key || await defaultApiKeyManager.promptForDefaultApiKey();
123
+
124
+ if (isDebug) {
125
+ console.log(chalk.yellow(`DEBUG: Default API key data exists: ${!!defaultApiKeyData}`))
126
+ console.log(chalk.yellow(`DEBUG: promptForDefaultApiKey returned: ${apiKey ? 'a key' : 'null'}`))
127
+ }
128
+
129
+ if (apiKey) {
130
+ if (isDebug) {
131
+ console.log(chalk.yellow('DEBUG: Using API key from default API key manager'));
132
+ }
133
+ optionsCopy.apiKey = apiKey;
134
+ } else {
135
+ console.log(chalk.yellow('No API key available. You need to either:'));
136
+ console.log(chalk.yellow('1. Create an API key with: berget api-keys create --name "My Key"'));
137
+ console.log(chalk.yellow('2. Set a default API key with: berget api-keys set-default <id>'));
138
+ console.log(chalk.yellow('3. Provide an API key with the --api-key option'));
139
+ console.log(chalk.yellow('4. Set the BERGET_API_KEY environment variable'));
140
+ console.log(chalk.yellow('\nExample:'));
141
+ console.log(chalk.yellow(' export BERGET_API_KEY=your_api_key_here'));
142
+ console.log(chalk.yellow(' # or for a single command:'));
143
+ console.log(chalk.yellow(' BERGET_API_KEY=your_api_key_here berget chat run google/gemma-3-27b-it'));
144
+ throw new Error('No API key provided and no default API key set');
145
+ }
146
+
147
+ // Set the API key in the options
148
+ if (isDebug) {
149
+ console.log(chalk.yellow('DEBUG: Setting API key in options'))
150
+ }
151
+
152
+ // Only set the API key if it's not null
153
+ if (apiKey) {
154
+ optionsCopy.apiKey = apiKey;
155
+ }
156
+ } catch (error) {
157
+ console.log(chalk.red('Error getting API key:'))
158
+ if (error instanceof Error) {
159
+ console.log(chalk.red(error.message))
160
+ }
161
+ console.log(chalk.yellow('Please create an API key with: berget api-keys create --name "My Key"'))
162
+ throw new Error('Failed to get API key')
163
+ }
164
+ }
165
+
166
+ if (isDebug) {
167
+ console.log(chalk.yellow('DEBUG: Chat completion options:'))
168
+ console.log(chalk.yellow(JSON.stringify({
169
+ ...optionsCopy,
170
+ apiKey: optionsCopy.apiKey ? '***' : undefined // Hide the actual API key in debug output
171
+ }, null, 2)))
172
+ }
173
+
174
+ // If an API key is provided, use it for this request
175
+ if (optionsCopy.apiKey) {
176
+ headers['Authorization'] = `Bearer ${optionsCopy.apiKey}`
177
+ // Remove apiKey from options before sending to API
178
+ const { apiKey, ...requestOptions } = optionsCopy
179
+
180
+ if (isDebug) {
181
+ console.log(chalk.yellow('DEBUG: Using provided API key'))
182
+ console.log(chalk.yellow('DEBUG: Request options:'))
183
+ console.log(chalk.yellow(JSON.stringify(requestOptions, null, 2)))
184
+ }
185
+
186
+ try {
187
+ const response = await this.client.POST('/v1/chat/completions', {
188
+ body: requestOptions,
189
+ headers
190
+ })
191
+
192
+ // Check if response has an error property
193
+ const responseAny = response as any;
194
+ if (responseAny && responseAny.error)
195
+ throw new Error(JSON.stringify(responseAny.error))
196
+
197
+ if (isDebug) {
198
+ console.log(chalk.yellow('DEBUG: API response:'))
199
+ console.log(chalk.yellow(JSON.stringify(response, null, 2)))
200
+
201
+ // Output the complete response data for debugging
202
+ console.log(chalk.yellow('DEBUG: Complete response data:'))
203
+ console.log(chalk.yellow(JSON.stringify(response.data, null, 2)))
204
+ }
205
+
206
+ return response.data
207
+ } catch (requestError) {
208
+ if (process.argv.includes('--debug')) {
209
+ console.log(chalk.red(`DEBUG: Request error: ${requestError instanceof Error ? requestError.message : String(requestError)}`))
210
+ }
211
+ throw requestError
212
+ }
213
+ } else {
214
+ // We've exhausted all options for getting an API key
215
+ console.log(chalk.yellow('No API key available. You need to either:'));
216
+ console.log(chalk.yellow('1. Create an API key with: berget api-keys create --name "My Key"'));
217
+ console.log(chalk.yellow('2. Set a default API key with: berget api-keys set-default <id>'));
218
+ console.log(chalk.yellow('3. Provide an API key with the --api-key option'));
219
+ console.log(chalk.yellow('4. Set the BERGET_API_KEY environment variable'));
220
+ console.log(chalk.yellow('\nExample:'));
221
+ console.log(chalk.yellow(' export BERGET_API_KEY=your_api_key_here'));
222
+ console.log(chalk.yellow(' # or for a single command:'));
223
+ console.log(chalk.yellow(' BERGET_API_KEY=your_api_key_here berget chat run google/gemma-3-27b-it'));
224
+ throw new Error('No API key available. Please provide an API key or set a default API key.');
225
+ }
226
+ } catch (error) {
227
+ // Improved error handling
228
+ let errorMessage = 'Failed to create chat completion';
229
+
230
+ if (error instanceof Error) {
231
+ try {
232
+ // Try to parse the error message as JSON
233
+ const parsedError = JSON.parse(error.message);
234
+ if (parsedError.error && parsedError.error.message) {
235
+ errorMessage = `Chat error: ${parsedError.error.message}`;
236
+ }
237
+ } catch (e) {
238
+ // If parsing fails, use the original error message
239
+ errorMessage = `Chat error: ${error.message}`;
240
+ }
241
+ }
242
+
243
+ console.error(chalk.red(errorMessage));
244
+ throw new Error(errorMessage);
245
+ }
246
+ }
247
+
248
+ /**
249
+ * List available models
250
+ * Command: berget chat list
251
+ */
252
+ public async listModels(apiKey?: string): Promise<any> {
253
+ try {
254
+ // Check for environment variable first, then fallback to provided API key
255
+ const envApiKey = process.env.BERGET_API_KEY;
256
+ const effectiveApiKey = envApiKey || apiKey;
257
+
258
+ if (effectiveApiKey) {
259
+ const headers = {
260
+ 'Authorization': `Bearer ${effectiveApiKey}`
261
+ }
262
+
263
+ const { data, error } = await this.client.GET('/v1/models', { headers })
264
+ if (error) throw new Error(JSON.stringify(error))
265
+ return data
266
+ } else {
267
+ const { data, error } = await this.client.GET('/v1/models')
268
+ if (error) throw new Error(JSON.stringify(error))
269
+ return data
270
+ }
271
+ } catch (error) {
272
+ // Improved error handling
273
+ let errorMessage = 'Failed to list models';
274
+
275
+ if (error instanceof Error) {
276
+ try {
277
+ // Try to parse the error message as JSON
278
+ const parsedError = JSON.parse(error.message);
279
+ if (parsedError.error) {
280
+ errorMessage = `Models error: ${typeof parsedError.error === 'string' ?
281
+ parsedError.error :
282
+ (parsedError.error.message || JSON.stringify(parsedError.error))}`;
283
+ }
284
+ } catch (e) {
285
+ // If parsing fails, use the original error message
286
+ errorMessage = `Models error: ${error.message}`;
287
+ }
288
+ }
289
+
290
+ console.error(chalk.red(errorMessage));
291
+ throw new Error(errorMessage);
292
+ }
293
+ }
294
+
295
+ }