berget 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +47 -1
- package/dist/package.json +35 -0
- package/dist/src/client.js +24 -7
- package/dist/src/commands/api-keys.js +13 -7
- package/dist/src/commands/chat.js +84 -18
- package/dist/src/services/api-key-service.js +6 -16
- package/dist/src/services/chat-service.js +159 -36
- package/dist/src/utils/default-api-key.js +131 -5
- package/dist/src/utils/error-handler.js +4 -4
- package/index.ts +52 -1
- package/package.json +3 -2
- package/src/client.ts +23 -6
- package/src/commands/api-keys.ts +17 -7
- package/src/commands/chat.ts +122 -22
- package/src/services/api-key-service.ts +12 -20
- package/src/services/chat-service.ts +158 -40
- package/src/types/api.d.ts +203 -9
- package/src/types/json.d.ts +4 -0
- package/src/utils/default-api-key.ts +141 -6
- package/src/utils/error-handler.ts +4 -4
- package/tsconfig.json +1 -1
|
@@ -22,6 +22,15 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
22
22
|
__setModuleDefault(result, mod);
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
+
});
|
|
33
|
+
};
|
|
25
34
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
35
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
36
|
};
|
|
@@ -31,6 +40,8 @@ const fs = __importStar(require("fs"));
|
|
|
31
40
|
const path = __importStar(require("path"));
|
|
32
41
|
const os = __importStar(require("os"));
|
|
33
42
|
const chalk_1 = __importDefault(require("chalk"));
|
|
43
|
+
const api_key_service_1 = require("../services/api-key-service");
|
|
44
|
+
const readline_1 = __importDefault(require("readline"));
|
|
34
45
|
/**
|
|
35
46
|
* Manages the default API key for chat commands
|
|
36
47
|
*/
|
|
@@ -62,7 +73,9 @@ class DefaultApiKeyManager {
|
|
|
62
73
|
}
|
|
63
74
|
}
|
|
64
75
|
catch (error) {
|
|
65
|
-
|
|
76
|
+
if (process.argv.includes('--debug')) {
|
|
77
|
+
console.error(chalk_1.default.dim('Failed to load default API key configuration'));
|
|
78
|
+
}
|
|
66
79
|
this.defaultApiKey = null;
|
|
67
80
|
}
|
|
68
81
|
}
|
|
@@ -84,20 +97,29 @@ class DefaultApiKeyManager {
|
|
|
84
97
|
}
|
|
85
98
|
}
|
|
86
99
|
catch (error) {
|
|
87
|
-
|
|
100
|
+
if (process.argv.includes('--debug')) {
|
|
101
|
+
console.error(chalk_1.default.dim('Failed to save default API key configuration'));
|
|
102
|
+
}
|
|
88
103
|
}
|
|
89
104
|
}
|
|
90
105
|
/**
|
|
91
106
|
* Set the default API key
|
|
92
107
|
*/
|
|
93
|
-
setDefaultApiKey(id, name, prefix) {
|
|
94
|
-
this.defaultApiKey = { id, name, prefix };
|
|
108
|
+
setDefaultApiKey(id, name, prefix, key) {
|
|
109
|
+
this.defaultApiKey = { id, name, prefix, key };
|
|
95
110
|
this.saveConfig();
|
|
96
111
|
}
|
|
97
112
|
/**
|
|
98
|
-
* Get the default API key
|
|
113
|
+
* Get the default API key string
|
|
99
114
|
*/
|
|
100
115
|
getDefaultApiKey() {
|
|
116
|
+
var _a;
|
|
117
|
+
return ((_a = this.defaultApiKey) === null || _a === void 0 ? void 0 : _a.key) || null;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Get the default API key data object
|
|
121
|
+
*/
|
|
122
|
+
getDefaultApiKeyData() {
|
|
101
123
|
return this.defaultApiKey;
|
|
102
124
|
}
|
|
103
125
|
/**
|
|
@@ -107,5 +129,109 @@ class DefaultApiKeyManager {
|
|
|
107
129
|
this.defaultApiKey = null;
|
|
108
130
|
this.saveConfig();
|
|
109
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* Prompts the user to select a default API key if none is set
|
|
134
|
+
* @returns The selected API key or null if none was selected
|
|
135
|
+
*/
|
|
136
|
+
promptForDefaultApiKey() {
|
|
137
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
138
|
+
try {
|
|
139
|
+
const isDebug = process.argv.includes('--debug');
|
|
140
|
+
if (isDebug) {
|
|
141
|
+
console.log(chalk_1.default.yellow('DEBUG: promptForDefaultApiKey called'));
|
|
142
|
+
}
|
|
143
|
+
// If we already have a default API key, return it
|
|
144
|
+
if (this.defaultApiKey) {
|
|
145
|
+
if (isDebug) {
|
|
146
|
+
console.log(chalk_1.default.yellow('DEBUG: Using existing default API key'));
|
|
147
|
+
}
|
|
148
|
+
return this.defaultApiKey.key;
|
|
149
|
+
}
|
|
150
|
+
if (isDebug) {
|
|
151
|
+
console.log(chalk_1.default.yellow('DEBUG: No default API key found, getting ApiKeyService'));
|
|
152
|
+
}
|
|
153
|
+
const apiKeyService = api_key_service_1.ApiKeyService.getInstance();
|
|
154
|
+
// Get all API keys
|
|
155
|
+
let apiKeys;
|
|
156
|
+
try {
|
|
157
|
+
if (isDebug) {
|
|
158
|
+
console.log(chalk_1.default.yellow('DEBUG: Calling apiKeyService.list()'));
|
|
159
|
+
}
|
|
160
|
+
apiKeys = yield apiKeyService.list();
|
|
161
|
+
if (isDebug) {
|
|
162
|
+
console.log(chalk_1.default.yellow(`DEBUG: Got ${apiKeys ? apiKeys.length : 0} API keys`));
|
|
163
|
+
}
|
|
164
|
+
if (!apiKeys || apiKeys.length === 0) {
|
|
165
|
+
console.log(chalk_1.default.yellow('No API keys found. Create one with:'));
|
|
166
|
+
console.log(chalk_1.default.blue(' berget api-keys create --name "My Key"'));
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
// Check if this is an authentication error
|
|
172
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
173
|
+
const isAuthError = errorMessage.includes('Unauthorized') ||
|
|
174
|
+
errorMessage.includes('Authentication failed') ||
|
|
175
|
+
errorMessage.includes('AUTH_FAILED');
|
|
176
|
+
if (isAuthError) {
|
|
177
|
+
console.log(chalk_1.default.yellow('Authentication required. Please run `berget auth login` first.'));
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
console.log(chalk_1.default.red('Error fetching API keys:'));
|
|
181
|
+
if (error instanceof Error) {
|
|
182
|
+
console.log(chalk_1.default.red(error.message));
|
|
183
|
+
if (isDebug) {
|
|
184
|
+
console.log(chalk_1.default.yellow(`DEBUG: API key list error: ${error.message}`));
|
|
185
|
+
console.log(chalk_1.default.yellow(`DEBUG: Stack: ${error.stack}`));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
console.log(chalk_1.default.blue('Select an API key to use as default:'));
|
|
192
|
+
// Display available API keys
|
|
193
|
+
apiKeys.forEach((key, index) => {
|
|
194
|
+
console.log(` ${index + 1}. ${key.name} (${key.prefix}...)`);
|
|
195
|
+
});
|
|
196
|
+
// Create readline interface for user input
|
|
197
|
+
const rl = readline_1.default.createInterface({
|
|
198
|
+
input: process.stdin,
|
|
199
|
+
output: process.stdout
|
|
200
|
+
});
|
|
201
|
+
// Prompt for selection
|
|
202
|
+
const selection = yield new Promise((resolve) => {
|
|
203
|
+
rl.question('Enter number (or press Enter to cancel): ', (answer) => {
|
|
204
|
+
rl.close();
|
|
205
|
+
const num = parseInt(answer.trim(), 10);
|
|
206
|
+
if (isNaN(num) || num < 1 || num > apiKeys.length) {
|
|
207
|
+
resolve(-1); // Invalid selection
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
resolve(num - 1); // Convert to zero-based index
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
if (selection === -1) {
|
|
215
|
+
console.log(chalk_1.default.yellow('No API key selected'));
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
const selectedKey = apiKeys[selection];
|
|
219
|
+
// Create a new API key with the selected name
|
|
220
|
+
const newKey = yield apiKeyService.create({
|
|
221
|
+
name: `CLI Default (copy of ${selectedKey.name})`,
|
|
222
|
+
description: 'Created automatically by the Berget CLI for default use'
|
|
223
|
+
});
|
|
224
|
+
// Save the new key as default
|
|
225
|
+
this.setDefaultApiKey(newKey.id.toString(), newKey.name, newKey.key.substring(0, 8), // Use first 8 chars as prefix
|
|
226
|
+
newKey.key);
|
|
227
|
+
console.log(chalk_1.default.green(`✓ Default API key set to: ${newKey.name}`));
|
|
228
|
+
return newKey.key;
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
console.error(chalk_1.default.red('Failed to set default API key:'), error);
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
}
|
|
110
236
|
}
|
|
111
237
|
exports.DefaultApiKeyManager = DefaultApiKeyManager;
|
|
@@ -35,11 +35,11 @@ function handleError(message, error) {
|
|
|
35
35
|
console.error(chalk_1.default.dim(`Details: ${error.message}`));
|
|
36
36
|
}
|
|
37
37
|
// Check for authentication errors
|
|
38
|
-
if ((typeof error === 'string' && error.includes('Unauthorized')) ||
|
|
39
|
-
(error && error.message && error.message.includes('Unauthorized')) ||
|
|
40
|
-
(error && error.code && error.code === 401)) {
|
|
38
|
+
if ((typeof error === 'string' && (error.includes('Unauthorized') || error.includes('Authentication failed'))) ||
|
|
39
|
+
(error && error.message && (error.message.includes('Unauthorized') || error.message.includes('Authentication failed'))) ||
|
|
40
|
+
(error && error.code && (error.code === 401 || error.code === 'AUTH_FAILED'))) {
|
|
41
41
|
console.error(chalk_1.default.yellow('\nYou need to be logged in to use this command.'));
|
|
42
|
-
console.error(chalk_1.default.yellow('Run `berget login` to authenticate.'));
|
|
42
|
+
console.error(chalk_1.default.yellow('Run `berget auth login` to authenticate.'));
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
exports.handleError = handleError;
|
package/index.ts
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
import { program } from 'commander'
|
|
4
4
|
import { registerCommands } from './src/commands'
|
|
5
5
|
import { checkBergetConfig } from './src/utils/config-checker'
|
|
6
|
+
import chalk from 'chalk'
|
|
7
|
+
import { version } from './package.json'
|
|
6
8
|
|
|
7
9
|
// Set version and description
|
|
8
10
|
program
|
|
@@ -17,7 +19,7 @@ program
|
|
|
17
19
|
__/ |
|
|
18
20
|
|___/ AI on European terms`
|
|
19
21
|
)
|
|
20
|
-
.version(
|
|
22
|
+
.version(version, '-v, --version')
|
|
21
23
|
.option('--local', 'Use local API endpoint (hidden)', false)
|
|
22
24
|
.option('--debug', 'Enable debug output', false)
|
|
23
25
|
|
|
@@ -27,6 +29,55 @@ registerCommands(program)
|
|
|
27
29
|
// Check for .bergetconfig if not running a command
|
|
28
30
|
if (process.argv.length <= 2) {
|
|
29
31
|
checkBergetConfig()
|
|
32
|
+
|
|
33
|
+
// Show helpful welcome message
|
|
34
|
+
console.log(chalk.blue('\nWelcome to the Berget CLI!'));
|
|
35
|
+
console.log(chalk.blue('Common commands:'));
|
|
36
|
+
console.log(chalk.blue(` ${chalk.bold('berget auth login')} - Log in to Berget`));
|
|
37
|
+
console.log(chalk.blue(` ${chalk.bold('berget models list')} - List available AI models`));
|
|
38
|
+
console.log(chalk.blue(` ${chalk.bold('berget chat run')} - Start a chat session`));
|
|
39
|
+
console.log(chalk.blue(` ${chalk.bold('berget api-keys list')} - List your API keys`));
|
|
40
|
+
console.log(chalk.blue(`\nRun ${chalk.bold('berget --help')} for a complete list of commands.`));
|
|
30
41
|
}
|
|
31
42
|
|
|
43
|
+
// Add helpful suggestions for common command mistakes
|
|
44
|
+
const commonMistakes: Record<string, string> = {
|
|
45
|
+
'login': 'auth login',
|
|
46
|
+
'logout': 'auth logout',
|
|
47
|
+
'whoami': 'auth whoami',
|
|
48
|
+
'list-models': 'models list',
|
|
49
|
+
'list-keys': 'api-keys list',
|
|
50
|
+
'create-key': 'api-keys create',
|
|
51
|
+
'list-clusters': 'clusters list',
|
|
52
|
+
'usage': 'billing usage'
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Add error handler for unknown commands
|
|
56
|
+
program.on('command:*', (operands) => {
|
|
57
|
+
const unknownCommand = operands[0] as string;
|
|
58
|
+
console.error(chalk.red(`Error: unknown command '${unknownCommand}'`));
|
|
59
|
+
|
|
60
|
+
// Check if this is a known mistake and suggest the correct command
|
|
61
|
+
if (unknownCommand in commonMistakes) {
|
|
62
|
+
console.log(chalk.yellow(`Did you mean? ${chalk.bold(`berget ${commonMistakes[unknownCommand]}`)}`));
|
|
63
|
+
} else {
|
|
64
|
+
// Try to find similar commands
|
|
65
|
+
const availableCommands = program.commands.map(cmd => cmd.name());
|
|
66
|
+
const similarCommands = availableCommands.filter(cmd =>
|
|
67
|
+
cmd.includes(unknownCommand) || unknownCommand.includes(cmd)
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
if (similarCommands.length > 0) {
|
|
71
|
+
console.log(chalk.yellow('Similar commands:'));
|
|
72
|
+
similarCommands.forEach(cmd => {
|
|
73
|
+
console.log(chalk.yellow(` ${chalk.bold(`berget ${cmd}`)}`));
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
console.log(chalk.blue('\nRun `berget --help` for a list of available commands.'));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
process.exit(1);
|
|
81
|
+
});
|
|
82
|
+
|
|
32
83
|
program.parse(process.argv)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "berget",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"bin": {
|
|
6
6
|
"berget": "dist/index.js"
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"fs-extra": "^11.3.0",
|
|
30
30
|
"open": "^9.1.0",
|
|
31
31
|
"openapi-fetch": "^0.9.1",
|
|
32
|
-
"openapi-typescript": "^6.7.4"
|
|
32
|
+
"openapi-typescript": "^6.7.4",
|
|
33
|
+
"readline": "^1.3.0"
|
|
33
34
|
}
|
|
34
35
|
}
|
package/src/client.ts
CHANGED
|
@@ -126,21 +126,38 @@ export const createAuthenticatedClient = () => {
|
|
|
126
126
|
// If we get an auth error, try to refresh the token and retry
|
|
127
127
|
if (result.error) {
|
|
128
128
|
// Detect various forms of authentication errors
|
|
129
|
-
|
|
129
|
+
let isAuthError = false;
|
|
130
|
+
|
|
131
|
+
try {
|
|
130
132
|
// Standard 401 Unauthorized
|
|
131
|
-
(typeof result.error === 'object' && result.error.status === 401)
|
|
133
|
+
if (typeof result.error === 'object' && result.error.status === 401) {
|
|
134
|
+
isAuthError = true;
|
|
135
|
+
}
|
|
132
136
|
// OAuth specific errors
|
|
133
|
-
(result.error.error &&
|
|
137
|
+
else if (result.error.error &&
|
|
134
138
|
(result.error.error.code === 'invalid_token' ||
|
|
135
139
|
result.error.error.code === 'token_expired' ||
|
|
136
140
|
result.error.error.message === 'Invalid API key' ||
|
|
137
141
|
result.error.error.message?.toLowerCase().includes('token') ||
|
|
138
|
-
result.error.error.message?.toLowerCase().includes('unauthorized')))
|
|
142
|
+
result.error.error.message?.toLowerCase().includes('unauthorized'))) {
|
|
143
|
+
isAuthError = true;
|
|
144
|
+
}
|
|
139
145
|
// Message-based detection as fallback
|
|
140
|
-
(typeof result.error === 'string' &&
|
|
146
|
+
else if (typeof result.error === 'string' &&
|
|
141
147
|
(result.error.toLowerCase().includes('unauthorized') ||
|
|
142
148
|
result.error.toLowerCase().includes('token') ||
|
|
143
|
-
result.error.toLowerCase().includes('auth')))
|
|
149
|
+
result.error.toLowerCase().includes('auth'))) {
|
|
150
|
+
isAuthError = true;
|
|
151
|
+
}
|
|
152
|
+
} catch (parseError) {
|
|
153
|
+
// If we can't parse the error structure, do a simple string check
|
|
154
|
+
const errorStr = String(result.error);
|
|
155
|
+
if (errorStr.toLowerCase().includes('unauthorized') ||
|
|
156
|
+
errorStr.toLowerCase().includes('token') ||
|
|
157
|
+
errorStr.toLowerCase().includes('auth')) {
|
|
158
|
+
isAuthError = true;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
144
161
|
|
|
145
162
|
if (isAuthError && tokenManager.getRefreshToken()) {
|
|
146
163
|
if (process.argv.includes('--debug')) {
|
package/src/commands/api-keys.ts
CHANGED
|
@@ -50,10 +50,15 @@ export function registerApiKeyCommands(program: Command): void {
|
|
|
50
50
|
? chalk.green('● Active')
|
|
51
51
|
: chalk.red('● Inactive')
|
|
52
52
|
|
|
53
|
+
// Format the prefix to ensure it's not too long
|
|
54
|
+
const prefixStr = key.prefix.length > 12
|
|
55
|
+
? key.prefix.substring(0, 12) + '...'
|
|
56
|
+
: key.prefix
|
|
57
|
+
|
|
53
58
|
console.log(
|
|
54
59
|
String(key.id).padEnd(10) +
|
|
55
60
|
key.name.padEnd(25) +
|
|
56
|
-
|
|
61
|
+
prefixStr.padEnd(15) +
|
|
57
62
|
status.padEnd(12) +
|
|
58
63
|
key.created.substring(0, 10).padEnd(12) +
|
|
59
64
|
lastUsed
|
|
@@ -318,10 +323,15 @@ export function registerApiKeyCommands(program: Command): void {
|
|
|
318
323
|
|
|
319
324
|
// Save the default API key
|
|
320
325
|
const defaultApiKeyManager = DefaultApiKeyManager.getInstance()
|
|
326
|
+
|
|
327
|
+
// We need to rotate the key to get the actual key value
|
|
328
|
+
const rotatedKey = await apiKeyService.rotate(id)
|
|
329
|
+
|
|
321
330
|
defaultApiKeyManager.setDefaultApiKey(
|
|
322
331
|
id,
|
|
323
332
|
selectedKey.name,
|
|
324
|
-
selectedKey.prefix
|
|
333
|
+
selectedKey.prefix,
|
|
334
|
+
rotatedKey.key
|
|
325
335
|
)
|
|
326
336
|
|
|
327
337
|
console.log(chalk.green(`✓ API key "${selectedKey.name}" set as default for chat commands`))
|
|
@@ -339,9 +349,9 @@ export function registerApiKeyCommands(program: Command): void {
|
|
|
339
349
|
.action(() => {
|
|
340
350
|
try {
|
|
341
351
|
const defaultApiKeyManager = DefaultApiKeyManager.getInstance()
|
|
342
|
-
const
|
|
352
|
+
const defaultApiKeyData = defaultApiKeyManager.getDefaultApiKeyData()
|
|
343
353
|
|
|
344
|
-
if (!
|
|
354
|
+
if (!defaultApiKeyData) {
|
|
345
355
|
console.log(chalk.yellow('No default API key set'))
|
|
346
356
|
console.log('')
|
|
347
357
|
console.log('To set a default API key, run:')
|
|
@@ -351,9 +361,9 @@ export function registerApiKeyCommands(program: Command): void {
|
|
|
351
361
|
|
|
352
362
|
console.log(chalk.bold('Default API key:'))
|
|
353
363
|
console.log('')
|
|
354
|
-
console.log(`${chalk.dim('ID:')} ${
|
|
355
|
-
console.log(`${chalk.dim('Name:')} ${
|
|
356
|
-
console.log(`${chalk.dim('Prefix:')} ${
|
|
364
|
+
console.log(`${chalk.dim('ID:')} ${defaultApiKeyData.id}`)
|
|
365
|
+
console.log(`${chalk.dim('Name:')} ${defaultApiKeyData.name}`)
|
|
366
|
+
console.log(`${chalk.dim('Prefix:')} ${defaultApiKeyData.prefix}`)
|
|
357
367
|
console.log('')
|
|
358
368
|
console.log(chalk.dim('This API key will be used by default when running chat commands'))
|
|
359
369
|
console.log(chalk.dim('You can override it with --api-key or --api-key-id options'))
|
package/src/commands/chat.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { Command } from 'commander'
|
|
|
2
2
|
import chalk from 'chalk'
|
|
3
3
|
import readline from 'readline'
|
|
4
4
|
import { COMMAND_GROUPS, SUBCOMMANDS } from '../constants/command-structure'
|
|
5
|
-
import { ChatService, ChatMessage } from '../services/chat-service'
|
|
5
|
+
import { ChatService, ChatMessage, ChatCompletionOptions } from '../services/chat-service'
|
|
6
6
|
import { ApiKeyService } from '../services/api-key-service'
|
|
7
7
|
import { AuthService } from '../services/auth-service'
|
|
8
8
|
import { handleError } from '../utils/error-handler'
|
|
@@ -36,7 +36,7 @@ export function registerChatCommands(program: Command): void {
|
|
|
36
36
|
chat
|
|
37
37
|
.command(SUBCOMMANDS.CHAT.RUN)
|
|
38
38
|
.description('Run a chat session with a specified model')
|
|
39
|
-
.argument('[model]', 'Model to use (default:
|
|
39
|
+
.argument('[model]', 'Model to use (default: google/gemma-3-27b-it)')
|
|
40
40
|
.option('-s, --system <message>', 'System message')
|
|
41
41
|
.option('-t, --temperature <temp>', 'Temperature (0-1)', parseFloat)
|
|
42
42
|
.option('-m, --max-tokens <tokens>', 'Maximum tokens to generate', parseInt)
|
|
@@ -52,17 +52,76 @@ export function registerChatCommands(program: Command): void {
|
|
|
52
52
|
// Check if we have an API key or need to get one
|
|
53
53
|
let apiKey = options.apiKey
|
|
54
54
|
let apiKeyId = options.apiKeyId
|
|
55
|
+
|
|
56
|
+
// Check for environment variable first
|
|
57
|
+
const envApiKey = process.env.BERGET_API_KEY;
|
|
58
|
+
if (envApiKey) {
|
|
59
|
+
console.log(
|
|
60
|
+
chalk.dim(`Using API key from BERGET_API_KEY environment variable`)
|
|
61
|
+
)
|
|
62
|
+
apiKey = envApiKey;
|
|
63
|
+
}
|
|
64
|
+
// If no API key or API key ID provided and no env var, check for default API key
|
|
65
|
+
else if (!apiKey && !apiKeyId) {
|
|
66
|
+
try {
|
|
67
|
+
const defaultApiKeyManager = DefaultApiKeyManager.getInstance()
|
|
68
|
+
const defaultApiKeyData = defaultApiKeyManager.getDefaultApiKeyData()
|
|
69
|
+
|
|
70
|
+
if (defaultApiKeyData) {
|
|
71
|
+
apiKeyId = defaultApiKeyData.id
|
|
72
|
+
apiKey = defaultApiKeyData.key
|
|
73
|
+
|
|
74
|
+
if (apiKey) {
|
|
75
|
+
console.log(
|
|
76
|
+
chalk.dim(`Using default API key: ${defaultApiKeyData.name}`)
|
|
77
|
+
)
|
|
78
|
+
} else {
|
|
79
|
+
console.log(
|
|
80
|
+
chalk.yellow(`Default API key "${defaultApiKeyData.name}" exists but the key value is missing.`)
|
|
81
|
+
)
|
|
82
|
+
console.log(
|
|
83
|
+
chalk.yellow(`Try rotating the key with: berget api-keys rotate ${defaultApiKeyData.id}`)
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
// No default API key, prompt the user to create one
|
|
88
|
+
console.log(chalk.yellow('No default API key set.'))
|
|
55
89
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const defaultApiKeyManager = DefaultApiKeyManager.getInstance()
|
|
59
|
-
const defaultApiKey = defaultApiKeyManager.getDefaultApiKey()
|
|
90
|
+
// Try to prompt for a default API key
|
|
91
|
+
apiKey = await defaultApiKeyManager.promptForDefaultApiKey()
|
|
60
92
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
93
|
+
if (!apiKey) {
|
|
94
|
+
console.log(
|
|
95
|
+
chalk.red(
|
|
96
|
+
'Error: An API key is required to use the chat command.'
|
|
97
|
+
)
|
|
98
|
+
)
|
|
99
|
+
console.log(chalk.yellow('You can:'))
|
|
100
|
+
console.log(
|
|
101
|
+
chalk.yellow(
|
|
102
|
+
'1. Create an API key with: berget api-keys create --name "My Key"'
|
|
103
|
+
)
|
|
104
|
+
)
|
|
105
|
+
console.log(
|
|
106
|
+
chalk.yellow(
|
|
107
|
+
'2. Set a default API key with: berget api-keys set-default <id>'
|
|
108
|
+
)
|
|
109
|
+
)
|
|
110
|
+
console.log(
|
|
111
|
+
chalk.yellow(
|
|
112
|
+
'3. Provide an API key with the --api-key option'
|
|
113
|
+
)
|
|
114
|
+
)
|
|
115
|
+
return
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
} catch (error) {
|
|
119
|
+
if (process.argv.includes('--debug')) {
|
|
120
|
+
console.log(
|
|
121
|
+
chalk.yellow('DEBUG: Error checking default API key:')
|
|
122
|
+
)
|
|
123
|
+
console.log(chalk.yellow(String(error)))
|
|
124
|
+
}
|
|
66
125
|
}
|
|
67
126
|
}
|
|
68
127
|
|
|
@@ -106,9 +165,19 @@ export function registerChatCommands(program: Command): void {
|
|
|
106
165
|
}
|
|
107
166
|
}
|
|
108
167
|
} catch (error) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
168
|
+
// Check if this is an authentication error
|
|
169
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
170
|
+
const isAuthError = errorMessage.includes('Unauthorized') ||
|
|
171
|
+
errorMessage.includes('Authentication failed') ||
|
|
172
|
+
errorMessage.includes('AUTH_FAILED');
|
|
173
|
+
|
|
174
|
+
if (isAuthError) {
|
|
175
|
+
console.log(chalk.yellow('Authentication required. Please run `berget auth login` first.'));
|
|
176
|
+
} else {
|
|
177
|
+
console.error(chalk.red('Error fetching API key:'));
|
|
178
|
+
console.error(error);
|
|
179
|
+
}
|
|
180
|
+
console.log(chalk.yellow('Using default authentication instead.'));
|
|
112
181
|
}
|
|
113
182
|
}
|
|
114
183
|
|
|
@@ -171,14 +240,45 @@ export function registerChatCommands(program: Command): void {
|
|
|
171
240
|
|
|
172
241
|
try {
|
|
173
242
|
// Call the API
|
|
174
|
-
|
|
175
|
-
|
|
243
|
+
console.log(chalk.yellow('DEBUG: Preparing completion options'))
|
|
244
|
+
|
|
245
|
+
const completionOptions: ChatCompletionOptions = {
|
|
246
|
+
model: options.args?.[0] || 'google/gemma-3-27b-it',
|
|
176
247
|
messages: messages,
|
|
177
248
|
temperature:
|
|
178
249
|
options.temperature !== undefined ? options.temperature : 0.7,
|
|
179
250
|
max_tokens: options.maxTokens || 4096,
|
|
180
|
-
|
|
181
|
-
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Only add apiKey if it actually exists
|
|
254
|
+
if (apiKey) {
|
|
255
|
+
completionOptions.apiKey = apiKey
|
|
256
|
+
console.log(chalk.yellow('DEBUG: Using API key from command options or default'))
|
|
257
|
+
} else {
|
|
258
|
+
console.log(chalk.yellow('DEBUG: No API key available in chat command'))
|
|
259
|
+
// If we got this far with defaultApiKeyData but no apiKey, there's a problem
|
|
260
|
+
const defaultApiKeyManager = DefaultApiKeyManager.getInstance();
|
|
261
|
+
if (defaultApiKeyManager.getDefaultApiKeyData()) {
|
|
262
|
+
console.log(chalk.yellow('DEBUG: Default API key data exists but key is missing'))
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Debug output
|
|
267
|
+
console.log(chalk.yellow('DEBUG: Completion options:'))
|
|
268
|
+
console.log(chalk.yellow(JSON.stringify({
|
|
269
|
+
...completionOptions,
|
|
270
|
+
apiKey: completionOptions.apiKey ? '***' : undefined,
|
|
271
|
+
messages: completionOptions.messages.map((m: any) => ({
|
|
272
|
+
role: m.role,
|
|
273
|
+
content: m.content.length > 50 ? m.content.substring(0, 50) + '...' : m.content
|
|
274
|
+
}))
|
|
275
|
+
}, null, 2)))
|
|
276
|
+
|
|
277
|
+
console.log(chalk.yellow('DEBUG: Calling chatService.createCompletion'))
|
|
278
|
+
|
|
279
|
+
const response = await chatService.createCompletion(
|
|
280
|
+
completionOptions
|
|
281
|
+
)
|
|
182
282
|
|
|
183
283
|
// Debug output
|
|
184
284
|
if (program.opts().debug) {
|
|
@@ -252,12 +352,12 @@ export function registerChatCommands(program: Command): void {
|
|
|
252
352
|
// If no API key or API key ID provided, check for default API key
|
|
253
353
|
if (!apiKey && !apiKeyId) {
|
|
254
354
|
const defaultApiKeyManager = DefaultApiKeyManager.getInstance()
|
|
255
|
-
const
|
|
355
|
+
const defaultApiKeyData = defaultApiKeyManager.getDefaultApiKeyData()
|
|
256
356
|
|
|
257
|
-
if (
|
|
258
|
-
apiKeyId =
|
|
357
|
+
if (defaultApiKeyData) {
|
|
358
|
+
apiKeyId = defaultApiKeyData.id
|
|
259
359
|
console.log(
|
|
260
|
-
chalk.dim(`Using default API key: ${
|
|
360
|
+
chalk.dim(`Using default API key: ${defaultApiKeyData.name}`)
|
|
261
361
|
)
|
|
262
362
|
}
|
|
263
363
|
}
|
|
@@ -33,10 +33,10 @@ export interface ApiKeyResponse {
|
|
|
33
33
|
export class ApiKeyService {
|
|
34
34
|
private static instance: ApiKeyService
|
|
35
35
|
private client = createAuthenticatedClient()
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
// Command group name for this service
|
|
38
38
|
public static readonly COMMAND_GROUP = COMMAND_GROUPS.API_KEYS
|
|
39
|
-
|
|
39
|
+
|
|
40
40
|
// Subcommands for this service
|
|
41
41
|
public static readonly COMMANDS = SUBCOMMANDS.API_KEYS
|
|
42
42
|
|
|
@@ -56,18 +56,7 @@ export class ApiKeyService {
|
|
|
56
56
|
public async list(): Promise<ApiKey[]> {
|
|
57
57
|
try {
|
|
58
58
|
const { data, error } = await this.client.GET('/v1/api-keys')
|
|
59
|
-
if (error)
|
|
60
|
-
// Check if this is an authentication error
|
|
61
|
-
const errorObj = typeof error === 'string' ? JSON.parse(error) : error;
|
|
62
|
-
if (errorObj.status === 401) {
|
|
63
|
-
throw new Error(JSON.stringify({
|
|
64
|
-
error: "Authentication failed. Your session may have expired.",
|
|
65
|
-
code: "AUTH_FAILED",
|
|
66
|
-
details: "Please run 'berget login' to authenticate again."
|
|
67
|
-
}))
|
|
68
|
-
}
|
|
69
|
-
throw new Error(JSON.stringify(error))
|
|
70
|
-
}
|
|
59
|
+
if (error) throw error
|
|
71
60
|
return data || []
|
|
72
61
|
} catch (error) {
|
|
73
62
|
handleError('Failed to list API keys', error)
|
|
@@ -82,7 +71,7 @@ export class ApiKeyService {
|
|
|
82
71
|
public async create(options: CreateApiKeyOptions): Promise<ApiKeyResponse> {
|
|
83
72
|
try {
|
|
84
73
|
const { data, error } = await this.client.POST('/v1/api-keys', {
|
|
85
|
-
body: options
|
|
74
|
+
body: options,
|
|
86
75
|
})
|
|
87
76
|
if (error) throw new Error(JSON.stringify(error))
|
|
88
77
|
return data!
|
|
@@ -99,7 +88,7 @@ export class ApiKeyService {
|
|
|
99
88
|
public async delete(id: string): Promise<boolean> {
|
|
100
89
|
try {
|
|
101
90
|
const { error } = await this.client.DELETE('/v1/api-keys/{id}', {
|
|
102
|
-
params: { path: { id } }
|
|
91
|
+
params: { path: { id } },
|
|
103
92
|
})
|
|
104
93
|
if (error) throw new Error(JSON.stringify(error))
|
|
105
94
|
return true
|
|
@@ -115,9 +104,12 @@ export class ApiKeyService {
|
|
|
115
104
|
*/
|
|
116
105
|
public async rotate(id: string): Promise<ApiKeyResponse> {
|
|
117
106
|
try {
|
|
118
|
-
const { data, error } = await this.client.PUT(
|
|
119
|
-
|
|
120
|
-
|
|
107
|
+
const { data, error } = await this.client.PUT(
|
|
108
|
+
'/v1/api-keys/{id}/rotate',
|
|
109
|
+
{
|
|
110
|
+
params: { path: { id } },
|
|
111
|
+
}
|
|
112
|
+
)
|
|
121
113
|
if (error) throw new Error(JSON.stringify(error))
|
|
122
114
|
return data!
|
|
123
115
|
} catch (error) {
|
|
@@ -133,7 +125,7 @@ export class ApiKeyService {
|
|
|
133
125
|
public async describe(id: string): Promise<any> {
|
|
134
126
|
try {
|
|
135
127
|
const { data, error } = await this.client.GET('/v1/api-keys/{id}/usage', {
|
|
136
|
-
params: { path: { id } }
|
|
128
|
+
params: { path: { id } },
|
|
137
129
|
})
|
|
138
130
|
if (error) throw new Error(JSON.stringify(error))
|
|
139
131
|
return data
|