berget 2.2.5 → 2.2.7

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 (148) hide show
  1. package/.github/workflows/publish.yml +8 -8
  2. package/.github/workflows/test.yml +12 -6
  3. package/.husky/pre-commit +1 -0
  4. package/.prettierignore +15 -0
  5. package/.prettierrc +5 -3
  6. package/CONTRIBUTING.md +38 -0
  7. package/README.md +2 -148
  8. package/dist/index.js +21 -21
  9. package/dist/package.json +30 -2
  10. package/dist/src/agents/app.js +28 -0
  11. package/dist/src/agents/backend.js +25 -0
  12. package/dist/src/agents/devops.js +34 -0
  13. package/dist/src/agents/frontend.js +25 -0
  14. package/dist/src/agents/fullstack.js +25 -0
  15. package/dist/src/agents/index.js +61 -0
  16. package/dist/src/agents/quality.js +70 -0
  17. package/dist/src/agents/security.js +26 -0
  18. package/dist/src/agents/types.js +2 -0
  19. package/dist/src/client.js +54 -62
  20. package/dist/src/commands/api-keys.js +132 -140
  21. package/dist/src/commands/auth.js +9 -9
  22. package/dist/src/commands/autocomplete.js +9 -9
  23. package/dist/src/commands/billing.js +7 -9
  24. package/dist/src/commands/chat.js +90 -92
  25. package/dist/src/commands/clusters.js +12 -12
  26. package/dist/src/commands/code/__tests__/auth-sync.test.js +348 -0
  27. package/dist/src/commands/code/__tests__/fake-api-key-service.js +23 -0
  28. package/dist/src/commands/code/__tests__/fake-auth-service.js +55 -0
  29. package/dist/src/commands/code/__tests__/fake-command-runner.js +50 -0
  30. package/dist/src/commands/code/__tests__/fake-file-store.js +55 -0
  31. package/dist/src/commands/code/__tests__/fake-prompter.js +133 -0
  32. package/dist/src/commands/code/__tests__/setup-flow.test.js +505 -0
  33. package/dist/src/commands/code/adapters/clack-prompter.js +81 -0
  34. package/dist/src/commands/code/adapters/fs-file-store.js +80 -0
  35. package/dist/src/commands/code/adapters/spawn-command-runner.js +53 -0
  36. package/dist/src/commands/code/auth-sync.js +283 -0
  37. package/dist/src/commands/code/errors.js +27 -0
  38. package/dist/src/commands/code/ports/auth-services.js +2 -0
  39. package/dist/src/commands/code/ports/command-runner.js +2 -0
  40. package/dist/src/commands/code/ports/file-store.js +2 -0
  41. package/dist/src/commands/code/ports/prompter.js +2 -0
  42. package/dist/src/commands/code/setup.js +533 -0
  43. package/dist/src/commands/code.js +223 -779
  44. package/dist/src/commands/models.js +13 -15
  45. package/dist/src/commands/users.js +6 -8
  46. package/dist/src/constants/command-structure.js +116 -114
  47. package/dist/src/services/api-key-service.js +43 -48
  48. package/dist/src/services/auth-service.js +60 -299
  49. package/dist/src/services/browser-auth.js +278 -0
  50. package/dist/src/services/chat-service.js +78 -91
  51. package/dist/src/services/cluster-service.js +6 -6
  52. package/dist/src/services/collaborator-service.js +5 -8
  53. package/dist/src/services/flux-service.js +5 -8
  54. package/dist/src/services/helm-service.js +5 -8
  55. package/dist/src/services/kubectl-service.js +7 -10
  56. package/dist/src/utils/config-checker.js +5 -5
  57. package/dist/src/utils/config-loader.js +25 -25
  58. package/dist/src/utils/default-api-key.js +23 -23
  59. package/dist/src/utils/env-manager.js +7 -7
  60. package/dist/src/utils/error-handler.js +60 -61
  61. package/dist/src/utils/logger.js +7 -7
  62. package/dist/src/utils/markdown-renderer.js +2 -2
  63. package/dist/src/utils/opencode-validator.js +17 -20
  64. package/dist/src/utils/token-manager.js +38 -11
  65. package/dist/tests/commands/chat.test.js +24 -24
  66. package/dist/tests/commands/code.test.js +169 -138
  67. package/dist/tests/utils/config-loader.test.js +114 -114
  68. package/dist/tests/utils/env-manager.test.js +57 -57
  69. package/dist/tests/utils/opencode-validator.test.js +44 -43
  70. package/dist/vitest.config.js +1 -1
  71. package/eslint.config.mjs +47 -0
  72. package/index.ts +42 -48
  73. package/package.json +30 -2
  74. package/src/agents/app.ts +27 -0
  75. package/src/agents/backend.ts +24 -0
  76. package/src/agents/devops.ts +33 -0
  77. package/src/agents/frontend.ts +24 -0
  78. package/src/agents/fullstack.ts +24 -0
  79. package/src/agents/index.ts +71 -0
  80. package/src/agents/quality.ts +69 -0
  81. package/src/agents/security.ts +26 -0
  82. package/src/agents/types.ts +17 -0
  83. package/src/client.ts +125 -167
  84. package/src/commands/api-keys.ts +261 -358
  85. package/src/commands/auth.ts +24 -30
  86. package/src/commands/autocomplete.ts +12 -12
  87. package/src/commands/billing.ts +22 -27
  88. package/src/commands/chat.ts +230 -323
  89. package/src/commands/clusters.ts +33 -33
  90. package/src/commands/code/__tests__/auth-sync.test.ts +481 -0
  91. package/src/commands/code/__tests__/fake-api-key-service.ts +13 -0
  92. package/src/commands/code/__tests__/fake-auth-service.ts +50 -0
  93. package/src/commands/code/__tests__/fake-command-runner.ts +44 -0
  94. package/src/commands/code/__tests__/fake-file-store.ts +44 -0
  95. package/src/commands/code/__tests__/fake-prompter.ts +121 -0
  96. package/src/commands/code/__tests__/setup-flow.test.ts +628 -0
  97. package/src/commands/code/adapters/clack-prompter.ts +55 -0
  98. package/src/commands/code/adapters/fs-file-store.ts +37 -0
  99. package/src/commands/code/adapters/spawn-command-runner.ts +40 -0
  100. package/src/commands/code/auth-sync.ts +329 -0
  101. package/src/commands/code/errors.ts +23 -0
  102. package/src/commands/code/ports/auth-services.ts +14 -0
  103. package/src/commands/code/ports/command-runner.ts +10 -0
  104. package/src/commands/code/ports/file-store.ts +7 -0
  105. package/src/commands/code/ports/prompter.ts +29 -0
  106. package/src/commands/code/setup.ts +630 -0
  107. package/src/commands/code.ts +335 -1074
  108. package/src/commands/index.ts +19 -19
  109. package/src/commands/models.ts +32 -37
  110. package/src/commands/users.ts +15 -22
  111. package/src/constants/command-structure.ts +120 -140
  112. package/src/services/api-key-service.ts +96 -113
  113. package/src/services/auth-service.ts +92 -339
  114. package/src/services/browser-auth.ts +296 -0
  115. package/src/services/chat-service.ts +246 -279
  116. package/src/services/cluster-service.ts +29 -32
  117. package/src/services/collaborator-service.ts +13 -18
  118. package/src/services/flux-service.ts +16 -18
  119. package/src/services/helm-service.ts +16 -18
  120. package/src/services/kubectl-service.ts +12 -14
  121. package/src/types/api.d.ts +924 -926
  122. package/src/types/json.d.ts +3 -3
  123. package/src/utils/config-checker.ts +10 -10
  124. package/src/utils/config-loader.ts +110 -127
  125. package/src/utils/default-api-key.ts +81 -93
  126. package/src/utils/env-manager.ts +36 -40
  127. package/src/utils/error-handler.ts +83 -78
  128. package/src/utils/logger.ts +41 -41
  129. package/src/utils/markdown-renderer.ts +11 -11
  130. package/src/utils/opencode-validator.ts +51 -56
  131. package/src/utils/token-manager.ts +84 -64
  132. package/templates/agents/app.md +23 -0
  133. package/templates/agents/backend.md +23 -0
  134. package/templates/agents/devops.md +30 -0
  135. package/templates/agents/frontend.md +25 -0
  136. package/templates/agents/fullstack.md +23 -0
  137. package/templates/agents/quality.md +69 -0
  138. package/templates/agents/security.md +21 -0
  139. package/tests/commands/chat.test.ts +60 -70
  140. package/tests/commands/code.test.ts +346 -345
  141. package/tests/utils/config-loader.test.ts +260 -260
  142. package/tests/utils/env-manager.test.ts +127 -134
  143. package/tests/utils/opencode-validator.test.ts +65 -69
  144. package/tsconfig.json +2 -2
  145. package/vitest.config.ts +3 -3
  146. package/AGENTS.md +0 -374
  147. package/TODO.md +0 -19
  148. package/opencode.json +0 -146
@@ -1,17 +1,13 @@
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 {
6
- ChatService,
7
- ChatMessage,
8
- ChatCompletionOptions,
9
- } from '../services/chat-service'
10
- import { ApiKeyService } from '../services/api-key-service'
11
- import { AuthService } from '../services/auth-service'
12
- import { handleError } from '../utils/error-handler'
13
- import { DefaultApiKeyManager } from '../utils/default-api-key'
14
- import { renderMarkdown, containsMarkdown } from '../utils/markdown-renderer'
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
+ import { renderMarkdown, containsMarkdown } from "../utils/markdown-renderer";
15
11
 
16
12
  /**
17
13
  * Helper function to get user confirmation
@@ -20,139 +16,107 @@ async function confirm(question: string): Promise<boolean> {
20
16
  const rl = readline.createInterface({
21
17
  input: process.stdin,
22
18
  output: process.stdout,
23
- })
24
-
25
- return new Promise<boolean>((resolve) => {
26
- rl.question(question, (answer) => {
27
- rl.close()
28
- resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes')
29
- })
30
- })
19
+ });
20
+
21
+ return new Promise<boolean>(resolve => {
22
+ rl.question(question, answer => {
23
+ rl.close();
24
+ resolve(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
25
+ });
26
+ });
31
27
  }
32
28
 
33
29
  /**
34
30
  * Register chat commands
35
31
  */
36
32
  export function registerChatCommands(program: Command): void {
37
- const chat = program
38
- .command(COMMAND_GROUPS.CHAT)
39
- .description('Interact with AI chat models')
33
+ const chat = program.command(COMMAND_GROUPS.CHAT).description("Interact with AI chat models");
40
34
 
41
35
  chat
42
36
  .command(SUBCOMMANDS.CHAT.RUN)
43
- .description('Run a chat session with a specified model')
44
- .argument('[message]', 'Message to send directly (skips interactive mode)')
45
- .option('-m, --model <model>', 'Model to use (default: glm-4.7)')
46
-
47
- .option('-t, --temperature <temp>', 'Temperature (0-1)', parseFloat)
48
- .option('--max-tokens <tokens>', 'Maximum tokens to generate', parseInt)
49
- .option('-k, --api-key <key>', 'API key to use for this chat session')
50
- .option(
51
- '--api-key-id <id>',
52
- 'ID of the API key to use from your saved keys'
53
- )
54
- .option(
55
- '--no-stream',
56
- 'Disable streaming (streaming is enabled by default)'
57
- )
37
+ .description("Run a chat session with a specified model")
38
+ .argument("[message]", "Message to send directly (skips interactive mode)")
39
+ .option("-m, --model <model>", "Model to use (default: glm-4.7)")
40
+
41
+ .option("-t, --temperature <temp>", "Temperature (0-1)", parseFloat)
42
+ .option("--max-tokens <tokens>", "Maximum tokens to generate", parseInt)
43
+ .option("-k, --api-key <key>", "API key to use for this chat session")
44
+ .option("--api-key-id <id>", "ID of the API key to use from your saved keys")
45
+ .option("--no-stream", "Disable streaming (streaming is enabled by default)")
58
46
  .action(async (message, options) => {
59
47
  try {
60
- const chatService = ChatService.getInstance()
48
+ const chatService = ChatService.getInstance();
61
49
 
62
50
  // Check if we have an API key or need to get one
63
- let apiKey = options.apiKey
64
- let apiKeyId = options.apiKeyId
51
+ let apiKey = options.apiKey;
52
+ let apiKeyId = options.apiKeyId;
65
53
 
66
54
  // Check for environment variable first
67
- const envApiKey = process.env.BERGET_API_KEY
55
+ const envApiKey = process.env.BERGET_API_KEY;
68
56
  if (envApiKey) {
69
- console.log(
70
- chalk.dim(`Using API key from BERGET_API_KEY environment variable`)
71
- )
72
- apiKey = envApiKey
57
+ console.log(chalk.dim(`Using API key from BERGET_API_KEY environment variable`));
58
+ apiKey = envApiKey;
73
59
 
74
60
  // Debug the API key (first few characters only)
75
- if (process.argv.includes('--debug')) {
61
+ if (process.argv.includes("--debug")) {
76
62
  console.log(
77
- chalk.yellow(
78
- `DEBUG: API key from env starts with: ${envApiKey.substring(
79
- 0,
80
- 4
81
- )}...`
82
- )
83
- )
63
+ chalk.yellow(`DEBUG: API key from env starts with: ${envApiKey.substring(0, 4)}...`)
64
+ );
84
65
  }
85
66
  }
86
67
  // If API key is already provided via command line, use it
87
68
  else if (options.apiKey) {
88
- console.log(chalk.dim(`Using API key from command line argument`))
89
- apiKey = options.apiKey
69
+ console.log(chalk.dim(`Using API key from command line argument`));
70
+ apiKey = options.apiKey;
90
71
  }
91
72
  // If no API key or API key ID provided and no env var, check for default API key
92
73
  else if (!apiKey && !apiKeyId) {
93
74
  try {
94
- const defaultApiKeyManager = DefaultApiKeyManager.getInstance()
95
- const defaultApiKeyData =
96
- defaultApiKeyManager.getDefaultApiKeyData()
75
+ const defaultApiKeyManager = DefaultApiKeyManager.getInstance();
76
+ const defaultApiKeyData = defaultApiKeyManager.getDefaultApiKeyData();
97
77
 
98
78
  if (defaultApiKeyData) {
99
- apiKeyId = defaultApiKeyData.id
100
- apiKey = defaultApiKeyData.key
79
+ apiKeyId = defaultApiKeyData.id;
80
+ apiKey = defaultApiKeyData.key;
101
81
 
102
82
  if (apiKey) {
103
- console.log(
104
- chalk.dim(`Using default API key: ${defaultApiKeyData.name}`)
105
- )
83
+ console.log(chalk.dim(`Using default API key: ${defaultApiKeyData.name}`));
106
84
  } else {
107
85
  console.log(
108
86
  chalk.yellow(
109
87
  `Default API key "${defaultApiKeyData.name}" exists but the key value is missing.`
110
88
  )
111
- )
89
+ );
112
90
  console.log(
113
91
  chalk.yellow(
114
92
  `Try rotating the key with: berget api-keys rotate ${defaultApiKeyData.id}`
115
93
  )
116
- )
94
+ );
117
95
  }
118
96
  } else {
119
97
  // No default API key, prompt the user to create one
120
- console.log(chalk.yellow('No default API key set.'))
98
+ console.log(chalk.yellow("No default API key set."));
121
99
 
122
100
  // Try to prompt for a default API key
123
- apiKey = await defaultApiKeyManager.promptForDefaultApiKey()
101
+ apiKey = await defaultApiKeyManager.promptForDefaultApiKey();
124
102
 
125
103
  if (!apiKey) {
104
+ console.log(chalk.red("Error: An API key is required to use the chat command."));
105
+ console.log(chalk.yellow("You can:"));
126
106
  console.log(
127
- chalk.red(
128
- 'Error: An API key is required to use the chat command.'
129
- )
130
- )
131
- console.log(chalk.yellow('You can:'))
132
- console.log(
133
- chalk.yellow(
134
- '1. Create an API key with: berget api-keys create --name "My Key"'
135
- )
136
- )
107
+ chalk.yellow('1. Create an API key with: berget api-keys create --name "My Key"')
108
+ );
137
109
  console.log(
138
- chalk.yellow(
139
- '2. Set a default API key with: berget api-keys set-default <id>'
140
- )
141
- )
142
- console.log(
143
- chalk.yellow(
144
- '3. Provide an API key with the --api-key option'
145
- )
146
- )
147
- return
110
+ chalk.yellow("2. Set a default API key with: berget api-keys set-default <id>")
111
+ );
112
+ console.log(chalk.yellow("3. Provide an API key with the --api-key option"));
113
+ return;
148
114
  }
149
115
  }
150
116
  } catch (error) {
151
- if (process.argv.includes('--debug')) {
152
- console.log(
153
- chalk.yellow('DEBUG: Error checking default API key:')
154
- )
155
- console.log(chalk.yellow(String(error)))
117
+ if (process.argv.includes("--debug")) {
118
+ console.log(chalk.yellow("DEBUG: Error checking default API key:"));
119
+ console.log(chalk.yellow(String(error)));
156
120
  }
157
121
  }
158
122
  }
@@ -160,20 +124,18 @@ export function registerChatCommands(program: Command): void {
160
124
  // If no direct API key, try to get one from API key ID
161
125
  if (!apiKey && apiKeyId) {
162
126
  try {
163
- const apiKeyService = ApiKeyService.getInstance()
164
- const keys = await apiKeyService.list()
165
- const selectedKey = keys.find(
166
- (key) => key.id.toString() === options.apiKeyId
167
- )
127
+ const apiKeyService = ApiKeyService.getInstance();
128
+ const keys = await apiKeyService.list();
129
+ const selectedKey = keys.find(key => key.id.toString() === options.apiKeyId);
168
130
 
169
131
  if (!selectedKey) {
170
132
  console.log(
171
133
  chalk.yellow(
172
134
  `API key with ID ${options.apiKeyId} not found. Using default authentication.`
173
135
  )
174
- )
136
+ );
175
137
  } else {
176
- console.log(chalk.dim(`Using API key: ${selectedKey.name}`))
138
+ console.log(chalk.dim(`Using API key: ${selectedKey.name}`));
177
139
 
178
140
  // We need to rotate the key to get the actual key value
179
141
  if (
@@ -183,121 +145,107 @@ export function registerChatCommands(program: Command): void {
183
145
  )
184
146
  )
185
147
  ) {
186
- const rotatedKey = await apiKeyService.rotate(options.apiKeyId)
187
- apiKey = rotatedKey.key
188
- console.log(
189
- chalk.green(
190
- `API key "${selectedKey.name}" rotated successfully.`
191
- )
192
- )
148
+ const rotatedKey = await apiKeyService.rotate(options.apiKeyId);
149
+ apiKey = rotatedKey.key;
150
+ console.log(chalk.green(`API key "${selectedKey.name}" rotated successfully.`));
193
151
  } else {
194
- console.log(
195
- chalk.yellow('Using default authentication instead.')
196
- )
152
+ console.log(chalk.yellow("Using default authentication instead."));
197
153
  }
198
154
  }
199
155
  } catch (error) {
200
156
  // Check if this is an authentication error
201
- const errorMessage =
202
- error instanceof Error ? error.message : String(error)
157
+ const errorMessage = error instanceof Error ? error.message : String(error);
203
158
  const isAuthError =
204
- errorMessage.includes('Unauthorized') ||
205
- errorMessage.includes('Authentication failed') ||
206
- errorMessage.includes('AUTH_FAILED')
159
+ errorMessage.includes("Unauthorized") ||
160
+ errorMessage.includes("Authentication failed") ||
161
+ errorMessage.includes("AUTH_FAILED");
207
162
 
208
163
  if (isAuthError) {
209
164
  console.log(
210
- chalk.yellow(
211
- 'Authentication required. Please run `berget auth login` first.'
212
- )
213
- )
165
+ chalk.yellow("Authentication required. Please run `berget auth login` first.")
166
+ );
214
167
  } else {
215
- console.error(chalk.red('Error fetching API key:'))
216
- console.error(error)
168
+ console.error(chalk.red("Error fetching API key:"));
169
+ console.error(error);
217
170
  }
218
- console.log(chalk.yellow('Using default authentication instead.'))
171
+ console.log(chalk.yellow("Using default authentication instead."));
219
172
  }
220
173
  }
221
174
 
222
175
  // Verify we have authentication before starting chat
223
176
  if (!apiKey) {
224
177
  try {
225
- AuthService.getInstance()
226
- } catch (error) {
227
- console.log(chalk.red('Error: Authentication required for chat'))
228
- console.log(chalk.yellow('Please either:'))
229
- console.log(chalk.yellow('1. Log in with `berget auth login`'))
230
- console.log(chalk.yellow('2. Provide an API key with `--api-key`'))
178
+ AuthService.getInstance();
179
+ } catch {
180
+ console.log(chalk.red("Error: Authentication required for chat"));
181
+ console.log(chalk.yellow("Please either:"));
182
+ console.log(chalk.yellow("1. Log in with `berget auth login`"));
183
+ console.log(chalk.yellow("2. Provide an API key with `--api-key`"));
184
+ console.log(chalk.yellow("3. Provide an API key ID with `--api-key-id`"));
231
185
  console.log(
232
- chalk.yellow('3. Provide an API key ID with `--api-key-id`')
233
- )
234
- console.log(
235
- chalk.yellow(
236
- '4. Set a default API key with `berget api-keys set-default <id>`'
237
- )
238
- )
239
- return
186
+ chalk.yellow("4. Set a default API key with `berget api-keys set-default <id>`")
187
+ );
188
+ return;
240
189
  }
241
190
  }
242
191
 
243
192
  // Prepare messages array
244
- const messages: ChatMessage[] = []
193
+ const messages: ChatMessage[] = [];
245
194
 
246
195
  // Add system message if provided
247
196
  if (options.system) {
248
197
  messages.push({
249
- role: 'system',
198
+ role: "system",
250
199
  content: options.system,
251
- })
200
+ });
252
201
  }
253
202
 
254
203
  // Check if input is being piped in
255
- let inputMessage = message
256
- let stdinContent = ''
204
+ let inputMessage = message;
205
+ let stdinContent = "";
257
206
 
258
207
  if (!process.stdin.isTTY) {
259
208
  // Read from stdin (piped input)
260
- const chunks = []
209
+ const chunks = [];
261
210
  for await (const chunk of process.stdin) {
262
- chunks.push(chunk)
211
+ chunks.push(chunk);
263
212
  }
264
- stdinContent = Buffer.concat(chunks).toString('utf8').trim()
213
+ stdinContent = Buffer.concat(chunks).toString("utf8").trim();
265
214
  }
266
215
 
267
216
  // Combine stdin content with message if both exist
268
217
  if (stdinContent && message) {
269
- inputMessage = `${stdinContent}\n\n${message}`
218
+ inputMessage = `${stdinContent}\n\n${message}`;
270
219
  } else if (stdinContent && !message) {
271
- inputMessage = stdinContent
220
+ inputMessage = stdinContent;
272
221
  }
273
222
 
274
223
  // If a message is provided (either as argument, from stdin, or both), send it directly and exit
275
224
  if (inputMessage) {
276
225
  // Add user message
277
226
  messages.push({
278
- role: 'user',
227
+ role: "user",
279
228
  content: inputMessage,
280
- })
229
+ });
281
230
 
282
231
  try {
283
232
  // Call the API
284
233
  const completionOptions: ChatCompletionOptions = {
285
- model: options.model || 'openai/gpt-oss',
234
+ model: options.model || "openai/gpt-oss",
286
235
  messages: messages,
287
- temperature:
288
- options.temperature !== undefined ? options.temperature : 0.7,
236
+ temperature: options.temperature !== undefined ? options.temperature : 0.7,
289
237
  max_tokens: options.maxTokens || 4096,
290
238
  stream: options.stream !== false,
291
- }
239
+ };
292
240
 
293
241
  // Only add apiKey if it actually exists
294
242
  if (apiKey) {
295
- completionOptions.apiKey = apiKey
243
+ completionOptions.apiKey = apiKey;
296
244
  }
297
245
 
298
246
  // Add streaming support (now default)
299
247
  if (completionOptions.stream) {
300
- let assistantResponse = ''
248
+ let assistantResponse = "";
301
249
 
302
250
  // Stream the response in real-time
303
251
  completionOptions.onChunk = (chunk: any) => {
@@ -307,36 +255,32 @@ export function registerChatCommands(program: Command): void {
307
255
  chunk.choices[0].delta &&
308
256
  chunk.choices[0].delta.content
309
257
  ) {
310
- const content = chunk.choices[0].delta.content
258
+ const content = chunk.choices[0].delta.content;
311
259
  try {
312
- process.stdout.write(content)
260
+ process.stdout.write(content);
313
261
  } catch (error: any) {
314
262
  // Handle EPIPE errors gracefully (when pipe is closed)
315
- if (error.code === 'EPIPE') {
263
+ if (error.code === "EPIPE") {
316
264
  // Stop streaming if the pipe is closed
317
- return
265
+ return;
318
266
  }
319
- throw error
267
+ throw error;
320
268
  }
321
- assistantResponse += content
269
+ assistantResponse += content;
322
270
  }
323
- }
271
+ };
324
272
 
325
273
  try {
326
- await chatService.createCompletion(completionOptions)
274
+ await chatService.createCompletion(completionOptions);
327
275
  } catch (streamError) {
328
- console.error(chalk.red('\nStreaming error:'), streamError)
276
+ console.error(chalk.red("\nStreaming error:"), streamError);
329
277
 
330
278
  // Fallback to non-streaming if streaming fails
331
- console.log(
332
- chalk.yellow('Falling back to non-streaming mode...')
333
- )
334
- completionOptions.stream = false
335
- delete completionOptions.onChunk
279
+ console.log(chalk.yellow("Falling back to non-streaming mode..."));
280
+ completionOptions.stream = false;
281
+ delete completionOptions.onChunk;
336
282
 
337
- const response = await chatService.createCompletion(
338
- completionOptions
339
- )
283
+ const response = await chatService.createCompletion(completionOptions);
340
284
 
341
285
  if (
342
286
  response &&
@@ -344,17 +288,15 @@ export function registerChatCommands(program: Command): void {
344
288
  response.choices[0] &&
345
289
  response.choices[0].message
346
290
  ) {
347
- assistantResponse = response.choices[0].message.content
348
- console.log(assistantResponse)
291
+ assistantResponse = response.choices[0].message.content;
292
+ console.log(assistantResponse);
349
293
  }
350
294
  }
351
- console.log() // Add newline at the end
352
- return
295
+ console.log(); // Add newline at the end
296
+ return;
353
297
  }
354
298
 
355
- const response = await chatService.createCompletion(
356
- completionOptions
357
- )
299
+ const response = await chatService.createCompletion(completionOptions);
358
300
 
359
301
  // Check if response has the expected structure
360
302
  if (
@@ -363,32 +305,28 @@ export function registerChatCommands(program: Command): void {
363
305
  !response.choices[0] ||
364
306
  !response.choices[0].message
365
307
  ) {
366
- console.error(
367
- chalk.red('Error: Unexpected response format from API')
368
- )
369
- console.error(
370
- chalk.red('Response:', JSON.stringify(response, null, 2))
371
- )
372
- throw new Error('Unexpected response format from API')
308
+ console.error(chalk.red("Error: Unexpected response format from API"));
309
+ console.error(chalk.red("Response:", JSON.stringify(response, null, 2)));
310
+ throw new Error("Unexpected response format from API");
373
311
  }
374
312
 
375
313
  // Get assistant's response
376
- const assistantMessage = response.choices[0].message.content
314
+ const assistantMessage = response.choices[0].message.content;
377
315
 
378
316
  // Display the response
379
317
  if (containsMarkdown(assistantMessage)) {
380
- console.log(renderMarkdown(assistantMessage))
318
+ console.log(renderMarkdown(assistantMessage));
381
319
  } else {
382
- console.log(assistantMessage)
320
+ console.log(assistantMessage);
383
321
  }
384
322
 
385
- return
323
+ return;
386
324
  } catch (error) {
387
- console.error(chalk.red('Error: Failed to get response'))
325
+ console.error(chalk.red("Error: Failed to get response"));
388
326
  if (error instanceof Error) {
389
- console.error(chalk.red(error.message))
327
+ console.error(chalk.red(error.message));
390
328
  }
391
- process.exit(1)
329
+ process.exit(1);
392
330
  }
393
331
  }
394
332
 
@@ -396,47 +334,46 @@ export function registerChatCommands(program: Command): void {
396
334
  const rl = readline.createInterface({
397
335
  input: process.stdin,
398
336
  output: process.stdout,
399
- })
337
+ });
400
338
 
401
- console.log(chalk.cyan('Chat with Berget AI (type "exit" to quit)'))
402
- console.log(chalk.cyan('----------------------------------------'))
339
+ console.log(chalk.cyan('Chat with Berget AI (type "exit" to quit)'));
340
+ console.log(chalk.cyan("----------------------------------------"));
403
341
 
404
342
  // Start the conversation loop
405
343
  const askQuestion = () => {
406
- rl.question(chalk.green('You: '), async (input) => {
344
+ rl.question(chalk.green("You: "), async input => {
407
345
  // Check if user wants to exit
408
- if (input.toLowerCase() === 'exit') {
409
- console.log(chalk.cyan('Goodbye!'))
410
- rl.close()
411
- return
346
+ if (input.toLowerCase() === "exit") {
347
+ console.log(chalk.cyan("Goodbye!"));
348
+ rl.close();
349
+ return;
412
350
  }
413
351
 
414
352
  // Add user message
415
353
  messages.push({
416
- role: 'user',
354
+ role: "user",
417
355
  content: input,
418
- })
356
+ });
419
357
 
420
358
  try {
421
359
  // Call the API
422
360
  const completionOptions: ChatCompletionOptions = {
423
- model: options.model || 'openai/gpt-oss',
361
+ model: options.model || "openai/gpt-oss",
424
362
  messages: messages,
425
- temperature:
426
- options.temperature !== undefined ? options.temperature : 0.7,
363
+ temperature: options.temperature !== undefined ? options.temperature : 0.7,
427
364
  max_tokens: options.maxTokens || 4096,
428
365
  stream: options.stream !== false,
429
- }
366
+ };
430
367
 
431
368
  // Only add apiKey if it actually exists
432
369
  if (apiKey) {
433
- completionOptions.apiKey = apiKey
370
+ completionOptions.apiKey = apiKey;
434
371
  }
435
372
 
436
373
  // Add streaming support (now default)
437
374
  if (completionOptions.stream) {
438
- let assistantResponse = ''
439
- console.log(chalk.blue('Assistant: '))
375
+ let assistantResponse = "";
376
+ console.log(chalk.blue("Assistant: "));
440
377
 
441
378
  // Stream the response in real-time
442
379
  completionOptions.onChunk = (chunk: any) => {
@@ -446,36 +383,32 @@ export function registerChatCommands(program: Command): void {
446
383
  chunk.choices[0].delta &&
447
384
  chunk.choices[0].delta.content
448
385
  ) {
449
- const content = chunk.choices[0].delta.content
386
+ const content = chunk.choices[0].delta.content;
450
387
  try {
451
- process.stdout.write(content)
388
+ process.stdout.write(content);
452
389
  } catch (error: any) {
453
390
  // Handle EPIPE errors gracefully (when pipe is closed)
454
- if (error.code === 'EPIPE') {
391
+ if (error.code === "EPIPE") {
455
392
  // Stop streaming if the pipe is closed
456
- return
393
+ return;
457
394
  }
458
- throw error
395
+ throw error;
459
396
  }
460
- assistantResponse += content
397
+ assistantResponse += content;
461
398
  }
462
- }
399
+ };
463
400
 
464
401
  try {
465
- await chatService.createCompletion(completionOptions)
402
+ await chatService.createCompletion(completionOptions);
466
403
  } catch (streamError) {
467
- console.error(chalk.red('\nStreaming error:'), streamError)
404
+ console.error(chalk.red("\nStreaming error:"), streamError);
468
405
 
469
406
  // Fallback to non-streaming if streaming fails
470
- console.log(
471
- chalk.yellow('Falling back to non-streaming mode...')
472
- )
473
- completionOptions.stream = false
474
- delete completionOptions.onChunk
407
+ console.log(chalk.yellow("Falling back to non-streaming mode..."));
408
+ completionOptions.stream = false;
409
+ delete completionOptions.onChunk;
475
410
 
476
- const response = await chatService.createCompletion(
477
- completionOptions
478
- )
411
+ const response = await chatService.createCompletion(completionOptions);
479
412
 
480
413
  if (
481
414
  response &&
@@ -483,31 +416,29 @@ export function registerChatCommands(program: Command): void {
483
416
  response.choices[0] &&
484
417
  response.choices[0].message
485
418
  ) {
486
- assistantResponse = response.choices[0].message.content
487
- console.log(assistantResponse)
419
+ assistantResponse = response.choices[0].message.content;
420
+ console.log(assistantResponse);
488
421
  }
489
422
  }
490
- console.log('\n')
423
+ console.log("\n");
491
424
 
492
425
  // Add assistant response to messages
493
426
  messages.push({
494
- role: 'assistant',
427
+ role: "assistant",
495
428
  content: assistantResponse,
496
- })
429
+ });
497
430
 
498
431
  // Continue the conversation
499
- askQuestion()
500
- return
432
+ askQuestion();
433
+ return;
501
434
  }
502
435
 
503
- const response = await chatService.createCompletion(
504
- completionOptions
505
- )
436
+ const response = await chatService.createCompletion(completionOptions);
506
437
 
507
438
  // Debug output
508
439
  if (program.opts().debug) {
509
- console.log(chalk.yellow('DEBUG: Full response:'))
510
- console.log(chalk.yellow(JSON.stringify(response, null, 2)))
440
+ console.log(chalk.yellow("DEBUG: Full response:"));
441
+ console.log(chalk.yellow(JSON.stringify(response, null, 2)));
511
442
  }
512
443
 
513
444
  // Check if response has the expected structure
@@ -517,99 +448,88 @@ export function registerChatCommands(program: Command): void {
517
448
  !response.choices[0] ||
518
449
  !response.choices[0].message
519
450
  ) {
520
- console.error(
521
- chalk.red('Error: Unexpected response format from API')
522
- )
523
- console.error(
524
- chalk.red('Response:', JSON.stringify(response, null, 2))
525
- )
526
- throw new Error('Unexpected response format from API')
451
+ console.error(chalk.red("Error: Unexpected response format from API"));
452
+ console.error(chalk.red("Response:", JSON.stringify(response, null, 2)));
453
+ throw new Error("Unexpected response format from API");
527
454
  }
528
455
 
529
456
  // Get assistant's response
530
- const assistantMessage = response.choices[0].message.content
457
+ const assistantMessage = response.choices[0].message.content;
531
458
 
532
459
  // Add to messages array
533
460
  messages.push({
534
- role: 'assistant',
461
+ role: "assistant",
535
462
  content: assistantMessage,
536
- })
463
+ });
537
464
 
538
465
  // Display the response
539
- console.log(chalk.blue('Assistant: '))
466
+ console.log(chalk.blue("Assistant: "));
540
467
 
541
468
  // Check if the response contains markdown and render it if it does
542
469
  if (containsMarkdown(assistantMessage)) {
543
- console.log(renderMarkdown(assistantMessage))
470
+ console.log(renderMarkdown(assistantMessage));
544
471
  } else {
545
- console.log(assistantMessage)
472
+ console.log(assistantMessage);
546
473
  }
547
474
 
548
- console.log() // Empty line for better readability
475
+ console.log(); // Empty line for better readability
549
476
 
550
477
  // Continue the conversation
551
- askQuestion()
478
+ askQuestion();
552
479
  } catch (error) {
553
- console.error(chalk.red('Error: Failed to get response'))
480
+ console.error(chalk.red("Error: Failed to get response"));
554
481
  if (error instanceof Error) {
555
- console.error(chalk.red(error.message))
482
+ console.error(chalk.red(error.message));
556
483
  }
557
484
  // Continue despite error
558
- askQuestion()
485
+ askQuestion();
559
486
  }
560
- })
561
- }
487
+ });
488
+ };
562
489
 
563
490
  // Start the conversation
564
- askQuestion()
491
+ askQuestion();
565
492
  } catch (error) {
566
- handleError('Failed to create chat completion', error)
493
+ handleError("Failed to create chat completion", error);
567
494
  }
568
- })
495
+ });
569
496
 
570
497
  chat
571
498
  .command(SUBCOMMANDS.CHAT.LIST)
572
- .description('List available chat models')
573
- .option('-k, --api-key <key>', 'API key to use for this request')
574
- .option(
575
- '--api-key-id <id>',
576
- 'ID of the API key to use from your saved keys'
577
- )
578
- .action(async (options) => {
499
+ .description("List available chat models")
500
+ .option("-k, --api-key <key>", "API key to use for this request")
501
+ .option("--api-key-id <id>", "ID of the API key to use from your saved keys")
502
+ .action(async options => {
579
503
  try {
580
504
  // If API key ID is provided, fetch the actual key
581
- let apiKey = options.apiKey
582
- let apiKeyId = options.apiKeyId
505
+ let apiKey = options.apiKey;
506
+ let apiKeyId = options.apiKeyId;
583
507
 
584
508
  // If no API key or API key ID provided, check for default API key
585
509
  if (!apiKey && !apiKeyId) {
586
- const defaultApiKeyManager = DefaultApiKeyManager.getInstance()
587
- const defaultApiKeyData = defaultApiKeyManager.getDefaultApiKeyData()
510
+ const defaultApiKeyManager = DefaultApiKeyManager.getInstance();
511
+ const defaultApiKeyData = defaultApiKeyManager.getDefaultApiKeyData();
588
512
 
589
513
  if (defaultApiKeyData) {
590
- apiKeyId = defaultApiKeyData.id
591
- console.log(
592
- chalk.dim(`Using default API key: ${defaultApiKeyData.name}`)
593
- )
514
+ apiKeyId = defaultApiKeyData.id;
515
+ console.log(chalk.dim(`Using default API key: ${defaultApiKeyData.name}`));
594
516
  }
595
517
  }
596
518
 
597
519
  if (apiKeyId && !apiKey) {
598
520
  try {
599
- const apiKeyService = ApiKeyService.getInstance()
600
- const keys = await apiKeyService.list()
601
- const selectedKey = keys.find(
602
- (key) => key.id.toString() === options.apiKeyId
603
- )
521
+ const apiKeyService = ApiKeyService.getInstance();
522
+ const keys = await apiKeyService.list();
523
+ const selectedKey = keys.find(key => key.id.toString() === options.apiKeyId);
604
524
 
605
525
  if (!selectedKey) {
606
526
  console.log(
607
527
  chalk.yellow(
608
528
  `API key with ID ${options.apiKeyId} not found. Using default authentication.`
609
529
  )
610
- )
530
+ );
611
531
  } else {
612
- console.log(chalk.dim(`Using API key: ${selectedKey.name}`))
532
+ console.log(chalk.dim(`Using API key: ${selectedKey.name}`));
613
533
 
614
534
  // We need to rotate the key to get the actual key value
615
535
  if (
@@ -619,63 +539,50 @@ export function registerChatCommands(program: Command): void {
619
539
  )
620
540
  )
621
541
  ) {
622
- const rotatedKey = await apiKeyService.rotate(options.apiKeyId)
623
- apiKey = rotatedKey.key
624
- console.log(
625
- chalk.green(
626
- `API key "${selectedKey.name}" rotated successfully.`
627
- )
628
- )
542
+ const rotatedKey = await apiKeyService.rotate(options.apiKeyId);
543
+ apiKey = rotatedKey.key;
544
+ console.log(chalk.green(`API key "${selectedKey.name}" rotated successfully.`));
629
545
  } else {
630
- console.log(
631
- chalk.yellow('Using default authentication instead.')
632
- )
546
+ console.log(chalk.yellow("Using default authentication instead."));
633
547
  }
634
548
  }
635
549
  } catch (error) {
636
- console.error(chalk.red('Error fetching API key:'))
637
- console.error(error)
638
- console.log(chalk.yellow('Using default authentication instead.'))
550
+ console.error(chalk.red("Error fetching API key:"));
551
+ console.error(error);
552
+ console.log(chalk.yellow("Using default authentication instead."));
639
553
  }
640
554
  }
641
555
 
642
- const chatService = ChatService.getInstance()
643
- const models = await chatService.listModels(apiKey)
556
+ const chatService = ChatService.getInstance();
557
+ const models = await chatService.listModels(apiKey);
644
558
 
645
559
  // Debug output
646
560
  if (program.opts().debug) {
647
- console.log(chalk.yellow('DEBUG: Models response:'))
648
- console.log(chalk.yellow(JSON.stringify(models, null, 2)))
561
+ console.log(chalk.yellow("DEBUG: Models response:"));
562
+ console.log(chalk.yellow(JSON.stringify(models, null, 2)));
649
563
  }
650
564
 
651
- console.log(chalk.bold('Available Chat Models:'))
652
- console.log(chalk.dim(''.repeat(70)))
653
- console.log(
654
- chalk.dim('MODEL ID'.padEnd(40)) + chalk.dim('CAPABILITIES')
655
- )
656
- console.log(chalk.dim('─'.repeat(70)))
565
+ console.log(chalk.bold("Available Chat Models:"));
566
+ console.log(chalk.dim("".repeat(70)));
567
+ console.log(chalk.dim("MODEL ID".padEnd(40)) + chalk.dim("CAPABILITIES"));
568
+ console.log(chalk.dim("─".repeat(70)));
657
569
 
658
570
  // Filter to only show active models
659
- const activeModels = models.data.filter(
660
- (model: any) => model.active === true
661
- )
571
+ const activeModels = models.data.filter((model: any) => model.active === true);
662
572
 
663
573
  activeModels.forEach((model: any) => {
664
- const capabilities = []
665
- if (model.capabilities.vision) capabilities.push('vision')
666
- if (model.capabilities.function_calling)
667
- capabilities.push('function_calling')
668
- if (model.capabilities.json_mode) capabilities.push('json_mode')
574
+ const capabilities = [];
575
+ if (model.capabilities.vision) capabilities.push("vision");
576
+ if (model.capabilities.function_calling) capabilities.push("function_calling");
577
+ if (model.capabilities.json_mode) capabilities.push("json_mode");
669
578
 
670
579
  // Format model ID in Huggingface compatible format (owner/model)
671
- const modelId = `${model.owned_by.toLowerCase()}/${model.id}`.padEnd(
672
- 40
673
- )
580
+ const modelId = `${model.owned_by.toLowerCase()}/${model.id}`.padEnd(40);
674
581
 
675
- console.log(modelId + capabilities.join(', '))
676
- })
582
+ console.log(modelId + capabilities.join(", "));
583
+ });
677
584
  } catch (error) {
678
- handleError('Failed to list chat models', error)
585
+ handleError("Failed to list chat models", error);
679
586
  }
680
- })
587
+ });
681
588
  }