berget 2.2.7 → 2.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/publish.yml +6 -6
- package/.github/workflows/test.yml +1 -1
- package/.prettierrc +5 -3
- package/dist/index.js +24 -25
- package/dist/package.json +5 -3
- package/dist/src/agents/app.js +8 -8
- package/dist/src/agents/backend.js +3 -3
- package/dist/src/agents/devops.js +8 -8
- package/dist/src/agents/frontend.js +3 -3
- package/dist/src/agents/fullstack.js +3 -3
- package/dist/src/agents/index.js +18 -18
- package/dist/src/agents/quality.js +8 -8
- package/dist/src/agents/security.js +8 -8
- package/dist/src/client.js +115 -127
- package/dist/src/commands/api-keys.js +195 -202
- package/dist/src/commands/auth.js +16 -25
- package/dist/src/commands/autocomplete.js +8 -8
- package/dist/src/commands/billing.js +10 -19
- package/dist/src/commands/chat.js +139 -170
- package/dist/src/commands/clusters.js +21 -30
- package/dist/src/commands/code/__tests__/auth-sync.test.js +189 -186
- package/dist/src/commands/code/__tests__/fake-api-key-service.js +3 -13
- package/dist/src/commands/code/__tests__/fake-auth-service.js +21 -29
- package/dist/src/commands/code/__tests__/fake-command-runner.js +22 -33
- package/dist/src/commands/code/__tests__/fake-file-store.js +19 -41
- package/dist/src/commands/code/__tests__/fake-prompter.js +81 -97
- package/dist/src/commands/code/__tests__/setup-flow.test.js +295 -295
- package/dist/src/commands/code/adapters/clack-prompter.js +15 -32
- package/dist/src/commands/code/adapters/fs-file-store.js +25 -44
- package/dist/src/commands/code/adapters/spawn-command-runner.js +27 -41
- package/dist/src/commands/code/auth-sync.js +215 -228
- package/dist/src/commands/code/errors.js +15 -12
- package/dist/src/commands/code/setup.js +390 -425
- package/dist/src/commands/code.js +279 -294
- package/dist/src/commands/index.js +5 -5
- package/dist/src/commands/models.js +16 -25
- package/dist/src/commands/users.js +9 -18
- package/dist/src/constants/command-structure.js +138 -138
- package/dist/src/services/api-key-service.js +132 -152
- package/dist/src/services/auth-service.js +81 -95
- package/dist/src/services/browser-auth.js +121 -131
- package/dist/src/services/chat-service.js +369 -386
- package/dist/src/services/cluster-service.js +47 -62
- package/dist/src/services/collaborator-service.js +9 -21
- package/dist/src/services/flux-service.js +13 -25
- package/dist/src/services/helm-service.js +9 -21
- package/dist/src/services/kubectl-service.js +15 -29
- package/dist/src/utils/config-checker.js +7 -7
- package/dist/src/utils/config-loader.js +109 -109
- package/dist/src/utils/default-api-key.js +129 -139
- package/dist/src/utils/env-manager.js +55 -66
- package/dist/src/utils/error-handler.js +62 -62
- package/dist/src/utils/logger.js +74 -67
- package/dist/src/utils/markdown-renderer.js +28 -28
- package/dist/src/utils/opencode-validator.js +67 -69
- package/dist/src/utils/token-manager.js +67 -65
- package/dist/tests/commands/chat.test.js +30 -39
- package/dist/tests/commands/code.test.js +186 -195
- package/dist/tests/utils/config-loader.test.js +107 -107
- package/dist/tests/utils/env-manager.test.js +81 -90
- package/dist/tests/utils/opencode-validator.test.js +42 -41
- package/dist/vitest.config.js +1 -1
- package/eslint.config.mjs +50 -30
- package/index.ts +30 -31
- package/package.json +5 -3
- package/src/agents/app.ts +9 -9
- package/src/agents/backend.ts +4 -4
- package/src/agents/devops.ts +9 -9
- package/src/agents/frontend.ts +4 -4
- package/src/agents/fullstack.ts +4 -4
- package/src/agents/index.ts +27 -25
- package/src/agents/quality.ts +9 -9
- package/src/agents/security.ts +9 -9
- package/src/agents/types.ts +10 -10
- package/src/client.ts +85 -77
- package/src/commands/api-keys.ts +190 -185
- package/src/commands/auth.ts +15 -14
- package/src/commands/autocomplete.ts +10 -10
- package/src/commands/billing.ts +13 -12
- package/src/commands/chat.ts +145 -142
- package/src/commands/clusters.ts +20 -19
- package/src/commands/code/__tests__/auth-sync.test.ts +176 -175
- package/src/commands/code/__tests__/fake-api-key-service.ts +2 -2
- package/src/commands/code/__tests__/fake-auth-service.ts +18 -18
- package/src/commands/code/__tests__/fake-command-runner.ts +28 -22
- package/src/commands/code/__tests__/fake-file-store.ts +15 -15
- package/src/commands/code/__tests__/fake-prompter.ts +86 -85
- package/src/commands/code/__tests__/setup-flow.test.ts +253 -251
- package/src/commands/code/adapters/clack-prompter.ts +32 -30
- package/src/commands/code/adapters/fs-file-store.ts +18 -17
- package/src/commands/code/adapters/spawn-command-runner.ts +20 -15
- package/src/commands/code/auth-sync.ts +210 -210
- package/src/commands/code/errors.ts +11 -11
- package/src/commands/code/ports/auth-services.ts +7 -7
- package/src/commands/code/ports/command-runner.ts +2 -2
- package/src/commands/code/ports/file-store.ts +3 -3
- package/src/commands/code/ports/prompter.ts +13 -13
- package/src/commands/code/setup.ts +408 -406
- package/src/commands/code.ts +288 -287
- package/src/commands/index.ts +11 -10
- package/src/commands/models.ts +19 -18
- package/src/commands/users.ts +11 -10
- package/src/constants/command-structure.ts +159 -159
- package/src/services/api-key-service.ts +85 -85
- package/src/services/auth-service.ts +55 -54
- package/src/services/browser-auth.ts +62 -62
- package/src/services/chat-service.ts +169 -170
- package/src/services/cluster-service.ts +28 -28
- package/src/services/collaborator-service.ts +6 -6
- package/src/services/flux-service.ts +17 -17
- package/src/services/helm-service.ts +11 -11
- package/src/services/kubectl-service.ts +12 -12
- package/src/types/api.d.ts +1933 -1933
- package/src/types/json.d.ts +1 -1
- package/src/utils/config-checker.ts +6 -6
- package/src/utils/config-loader.ts +130 -129
- package/src/utils/default-api-key.ts +81 -80
- package/src/utils/env-manager.ts +37 -37
- package/src/utils/error-handler.ts +64 -64
- package/src/utils/logger.ts +72 -66
- package/src/utils/markdown-renderer.ts +28 -28
- package/src/utils/opencode-validator.ts +72 -71
- package/src/utils/token-manager.ts +69 -68
- package/tests/commands/chat.test.ts +32 -31
- package/tests/commands/code.test.ts +182 -181
- package/tests/utils/config-loader.test.ts +111 -110
- package/tests/utils/env-manager.test.ts +83 -79
- package/tests/utils/opencode-validator.test.ts +43 -42
- package/tsconfig.json +2 -1
- package/vitest.config.ts +2 -2
package/src/commands/chat.ts
CHANGED
|
@@ -1,48 +1,32 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import readline from
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
import { ApiKeyService } from
|
|
7
|
-
import { AuthService } from
|
|
8
|
-
import {
|
|
9
|
-
import { DefaultApiKeyManager } from
|
|
10
|
-
import {
|
|
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(
|
|
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(
|
|
38
|
-
.argument(
|
|
39
|
-
.option(
|
|
40
|
-
|
|
41
|
-
.option(
|
|
42
|
-
.option(
|
|
43
|
-
.option(
|
|
44
|
-
.option(
|
|
45
|
-
.option(
|
|
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
|
|
56
|
-
if (
|
|
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 =
|
|
42
|
+
apiKey = environmentApiKey;
|
|
59
43
|
|
|
60
44
|
// Debug the API key (first few characters only)
|
|
61
|
-
if (process.argv.includes(
|
|
45
|
+
if (process.argv.includes('--debug')) {
|
|
62
46
|
console.log(
|
|
63
|
-
chalk.yellow(
|
|
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(
|
|
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(
|
|
105
|
-
console.log(chalk.yellow(
|
|
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(
|
|
96
|
+
chalk.yellow('2. Set a default API key with: berget api-keys set-default <id>'),
|
|
111
97
|
);
|
|
112
|
-
console.log(chalk.yellow(
|
|
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(
|
|
118
|
-
console.log(chalk.yellow(
|
|
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 (
|
|
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(
|
|
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(
|
|
160
|
-
errorMessage.includes(
|
|
161
|
-
errorMessage.includes(
|
|
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(
|
|
151
|
+
chalk.yellow('Authentication required. Please run `berget auth login` first.'),
|
|
166
152
|
);
|
|
167
153
|
} else {
|
|
168
|
-
console.error(chalk.red(
|
|
154
|
+
console.error(chalk.red('Error fetching API key:'));
|
|
169
155
|
console.error(error);
|
|
170
156
|
}
|
|
171
|
-
console.log(chalk.yellow(
|
|
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(
|
|
181
|
-
console.log(chalk.yellow(
|
|
182
|
-
console.log(chalk.yellow(
|
|
183
|
-
console.log(chalk.yellow(
|
|
184
|
-
console.log(chalk.yellow(
|
|
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(
|
|
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(
|
|
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 ===
|
|
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(
|
|
262
|
+
console.error(chalk.red('\nStreaming error:'), streamError);
|
|
277
263
|
|
|
278
264
|
// Fallback to non-streaming if streaming fails
|
|
279
|
-
console.log(chalk.yellow(
|
|
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(
|
|
309
|
-
console.error(chalk.red(
|
|
310
|
-
throw new Error(
|
|
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(
|
|
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(
|
|
330
|
+
rl.question(chalk.green('You: '), async (input) => {
|
|
345
331
|
// Check if user wants to exit
|
|
346
|
-
if (input.toLowerCase() ===
|
|
347
|
-
console.log(chalk.cyan(
|
|
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(
|
|
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 ===
|
|
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(
|
|
390
|
+
console.error(chalk.red('\nStreaming error:'), streamError);
|
|
405
391
|
|
|
406
392
|
// Fallback to non-streaming if streaming fails
|
|
407
|
-
console.log(chalk.yellow(
|
|
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(
|
|
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(
|
|
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(
|
|
452
|
-
console.error(chalk.red(
|
|
453
|
-
throw new Error(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
500
|
-
.option(
|
|
501
|
-
.option(
|
|
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 (
|
|
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(
|
|
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(
|
|
536
|
+
console.error(chalk.red('Error fetching API key:'));
|
|
551
537
|
console.error(error);
|
|
552
|
-
console.log(chalk.yellow(
|
|
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(
|
|
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(
|
|
566
|
-
console.log(chalk.dim(
|
|
567
|
-
console.log(chalk.dim(
|
|
568
|
-
console.log(chalk.dim(
|
|
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(
|
|
576
|
-
if (model.capabilities.function_calling) capabilities.push(
|
|
577
|
-
if (model.capabilities.json_mode) capabilities.push(
|
|
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(
|
|
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
|
+
}
|