berget 2.2.7 → 2.2.9

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