berget 1.1.0 → 1.3.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/-27b-it +0 -0
- package/dist/index.js +47 -1
- package/dist/package.json +35 -0
- package/dist/src/client.js +55 -48
- package/dist/src/commands/api-keys.js +13 -7
- package/dist/src/commands/chat.js +100 -18
- package/dist/src/services/api-key-service.js +6 -16
- package/dist/src/services/chat-service.js +284 -56
- package/dist/src/utils/default-api-key.js +115 -6
- package/dist/src/utils/error-handler.js +4 -4
- package/dist/src/utils/logger.js +160 -0
- package/dist/src/utils/token-manager.js +6 -9
- package/index.ts +52 -1
- package/package.json +3 -2
- package/src/client.ts +83 -81
- package/src/commands/api-keys.ts +17 -7
- package/src/commands/chat.ts +142 -22
- package/src/services/api-key-service.ts +12 -20
- package/src/services/chat-service.ts +407 -87
- package/src/types/api.d.ts +203 -9
- package/src/types/json.d.ts +4 -0
- package/src/utils/default-api-key.ts +124 -6
- package/src/utils/error-handler.ts +4 -4
- package/src/utils/logger.ts +159 -0
- package/src/utils/token-manager.ts +6 -5
- package/tsconfig.json +1 -1
package/-27b-it
ADDED
|
File without changes
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
3
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
7
|
const commander_1 = require("commander");
|
|
5
8
|
const commands_1 = require("./src/commands");
|
|
6
9
|
const config_checker_1 = require("./src/utils/config-checker");
|
|
10
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
11
|
+
const package_json_1 = require("./package.json");
|
|
7
12
|
// Set version and description
|
|
8
13
|
commander_1.program
|
|
9
14
|
.name('berget')
|
|
@@ -15,7 +20,7 @@ commander_1.program
|
|
|
15
20
|
\\____/ \\___|_| \\__, |\\___|\\_\\_ \\_| |_/\\___/
|
|
16
21
|
__/ |
|
|
17
22
|
|___/ AI on European terms`)
|
|
18
|
-
.version(
|
|
23
|
+
.version(package_json_1.version, '-v, --version')
|
|
19
24
|
.option('--local', 'Use local API endpoint (hidden)', false)
|
|
20
25
|
.option('--debug', 'Enable debug output', false);
|
|
21
26
|
// Register all commands
|
|
@@ -23,5 +28,46 @@ commander_1.program
|
|
|
23
28
|
// Check for .bergetconfig if not running a command
|
|
24
29
|
if (process.argv.length <= 2) {
|
|
25
30
|
(0, config_checker_1.checkBergetConfig)();
|
|
31
|
+
// Show helpful welcome message
|
|
32
|
+
console.log(chalk_1.default.blue('\nWelcome to the Berget CLI!'));
|
|
33
|
+
console.log(chalk_1.default.blue('Common commands:'));
|
|
34
|
+
console.log(chalk_1.default.blue(` ${chalk_1.default.bold('berget auth login')} - Log in to Berget`));
|
|
35
|
+
console.log(chalk_1.default.blue(` ${chalk_1.default.bold('berget models list')} - List available AI models`));
|
|
36
|
+
console.log(chalk_1.default.blue(` ${chalk_1.default.bold('berget chat run')} - Start a chat session`));
|
|
37
|
+
console.log(chalk_1.default.blue(` ${chalk_1.default.bold('berget api-keys list')} - List your API keys`));
|
|
38
|
+
console.log(chalk_1.default.blue(`\nRun ${chalk_1.default.bold('berget --help')} for a complete list of commands.`));
|
|
26
39
|
}
|
|
40
|
+
// Add helpful suggestions for common command mistakes
|
|
41
|
+
const commonMistakes = {
|
|
42
|
+
'login': 'auth login',
|
|
43
|
+
'logout': 'auth logout',
|
|
44
|
+
'whoami': 'auth whoami',
|
|
45
|
+
'list-models': 'models list',
|
|
46
|
+
'list-keys': 'api-keys list',
|
|
47
|
+
'create-key': 'api-keys create',
|
|
48
|
+
'list-clusters': 'clusters list',
|
|
49
|
+
'usage': 'billing usage'
|
|
50
|
+
};
|
|
51
|
+
// Add error handler for unknown commands
|
|
52
|
+
commander_1.program.on('command:*', (operands) => {
|
|
53
|
+
const unknownCommand = operands[0];
|
|
54
|
+
console.error(chalk_1.default.red(`Error: unknown command '${unknownCommand}'`));
|
|
55
|
+
// Check if this is a known mistake and suggest the correct command
|
|
56
|
+
if (unknownCommand in commonMistakes) {
|
|
57
|
+
console.log(chalk_1.default.yellow(`Did you mean? ${chalk_1.default.bold(`berget ${commonMistakes[unknownCommand]}`)}`));
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
// Try to find similar commands
|
|
61
|
+
const availableCommands = commander_1.program.commands.map(cmd => cmd.name());
|
|
62
|
+
const similarCommands = availableCommands.filter(cmd => cmd.includes(unknownCommand) || unknownCommand.includes(cmd));
|
|
63
|
+
if (similarCommands.length > 0) {
|
|
64
|
+
console.log(chalk_1.default.yellow('Similar commands:'));
|
|
65
|
+
similarCommands.forEach(cmd => {
|
|
66
|
+
console.log(chalk_1.default.yellow(` ${chalk_1.default.bold(`berget ${cmd}`)}`));
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
console.log(chalk_1.default.blue('\nRun `berget --help` for a list of available commands.'));
|
|
70
|
+
}
|
|
71
|
+
process.exit(1);
|
|
72
|
+
});
|
|
27
73
|
commander_1.program.parse(process.argv);
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "berget",
|
|
3
|
+
"version": "1.3.0",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"bin": {
|
|
6
|
+
"berget": "dist/index.js"
|
|
7
|
+
},
|
|
8
|
+
"private": false,
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "node --import tsx ./index.ts --local",
|
|
11
|
+
"login": "node --import tsx ./index.ts --local auth login",
|
|
12
|
+
"logout": "node --import tsx ./index.ts --local auth logout",
|
|
13
|
+
"whoami": "node --import tsx ./index.ts --local auth whoami",
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"prepublishOnly": "npm run build",
|
|
16
|
+
"generate-types": "openapi-typescript https://api.berget.ai/openapi.json -o src/types/api.d.ts"
|
|
17
|
+
},
|
|
18
|
+
"author": "Berget AI AB",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"description": "This is a cli command for interacting with the AI infrastructure provider Berget",
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@types/node": "^20.11.20",
|
|
23
|
+
"tsx": "^4.19.3",
|
|
24
|
+
"typescript": "^5.3.3"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"chalk": "^4.1.2",
|
|
28
|
+
"commander": "^12.0.0",
|
|
29
|
+
"fs-extra": "^11.3.0",
|
|
30
|
+
"open": "^9.1.0",
|
|
31
|
+
"openapi-fetch": "^0.9.1",
|
|
32
|
+
"openapi-typescript": "^6.7.4",
|
|
33
|
+
"readline": "^1.3.0"
|
|
34
|
+
}
|
|
35
|
+
}
|
package/dist/src/client.js
CHANGED
|
@@ -12,21 +12,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
12
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.createAuthenticatedClient = exports.clearAuthToken = exports.saveAuthToken = exports.getAuthToken = exports.apiClient = void 0;
|
|
15
|
+
exports.createAuthenticatedClient = exports.clearAuthToken = exports.saveAuthToken = exports.getAuthToken = exports.apiClient = exports.API_BASE_URL = void 0;
|
|
16
16
|
const openapi_fetch_1 = __importDefault(require("openapi-fetch"));
|
|
17
17
|
const chalk_1 = __importDefault(require("chalk"));
|
|
18
18
|
const token_manager_1 = require("./utils/token-manager");
|
|
19
|
+
const logger_1 = require("./utils/logger");
|
|
19
20
|
// API Base URL
|
|
20
21
|
// Use --local flag to test against local API
|
|
21
22
|
const isLocalMode = process.argv.includes('--local');
|
|
22
|
-
|
|
23
|
+
exports.API_BASE_URL = process.env.BERGET_API_URL ||
|
|
23
24
|
(isLocalMode ? 'http://localhost:3000' : 'https://api.berget.ai');
|
|
24
|
-
if (isLocalMode && !process.env.BERGET_API_URL
|
|
25
|
-
|
|
25
|
+
if (isLocalMode && !process.env.BERGET_API_URL) {
|
|
26
|
+
logger_1.logger.debug('Using local API endpoint: http://localhost:3000');
|
|
26
27
|
}
|
|
27
28
|
// Create a typed client for the Berget API
|
|
28
29
|
exports.apiClient = (0, openapi_fetch_1.default)({
|
|
29
|
-
baseUrl: API_BASE_URL,
|
|
30
|
+
baseUrl: exports.API_BASE_URL,
|
|
30
31
|
headers: {
|
|
31
32
|
'Content-Type': 'application/json',
|
|
32
33
|
Accept: 'application/json',
|
|
@@ -51,12 +52,12 @@ exports.clearAuthToken = clearAuthToken;
|
|
|
51
52
|
// Create an authenticated client with refresh token support
|
|
52
53
|
const createAuthenticatedClient = () => {
|
|
53
54
|
const tokenManager = token_manager_1.TokenManager.getInstance();
|
|
54
|
-
if (!tokenManager.getAccessToken()
|
|
55
|
-
|
|
55
|
+
if (!tokenManager.getAccessToken()) {
|
|
56
|
+
logger_1.logger.debug('No authentication token found. Please run `berget auth login` first.');
|
|
56
57
|
}
|
|
57
58
|
// Create the base client
|
|
58
59
|
const client = (0, openapi_fetch_1.default)({
|
|
59
|
-
baseUrl: API_BASE_URL,
|
|
60
|
+
baseUrl: exports.API_BASE_URL,
|
|
60
61
|
headers: tokenManager.getAccessToken()
|
|
61
62
|
? {
|
|
62
63
|
Authorization: `Bearer ${tokenManager.getAccessToken()}`,
|
|
@@ -75,13 +76,14 @@ const createAuthenticatedClient = () => {
|
|
|
75
76
|
if (typeof target[prop] === 'function' &&
|
|
76
77
|
['GET', 'POST', 'PUT', 'DELETE', 'PATCH'].includes(String(prop))) {
|
|
77
78
|
return (...args) => __awaiter(this, void 0, void 0, function* () {
|
|
78
|
-
var _a, _b;
|
|
79
|
+
var _a, _b, _c, _d;
|
|
79
80
|
// Check if token is expired before making the request
|
|
80
81
|
if (tokenManager.isTokenExpired() && tokenManager.getRefreshToken()) {
|
|
81
82
|
yield refreshAccessToken(tokenManager);
|
|
82
83
|
}
|
|
83
84
|
// Update the Authorization header with the current token
|
|
84
|
-
if (
|
|
85
|
+
if (!((_b = (_a = args[1]) === null || _a === void 0 ? void 0 : _a.headers) === null || _b === void 0 ? void 0 : _b.Authorization) &&
|
|
86
|
+
tokenManager.getAccessToken()) {
|
|
85
87
|
if (!args[1])
|
|
86
88
|
args[1] = {};
|
|
87
89
|
if (!args[1].headers)
|
|
@@ -94,11 +96,9 @@ const createAuthenticatedClient = () => {
|
|
|
94
96
|
result = yield target[prop](...args);
|
|
95
97
|
}
|
|
96
98
|
catch (requestError) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
: String(requestError)}`));
|
|
101
|
-
}
|
|
99
|
+
logger_1.logger.debug(`Request error: ${requestError instanceof Error
|
|
100
|
+
? requestError.message
|
|
101
|
+
: String(requestError)}`);
|
|
102
102
|
return {
|
|
103
103
|
error: {
|
|
104
104
|
message: `Request failed: ${requestError instanceof Error
|
|
@@ -110,31 +110,45 @@ const createAuthenticatedClient = () => {
|
|
|
110
110
|
// If we get an auth error, try to refresh the token and retry
|
|
111
111
|
if (result.error) {
|
|
112
112
|
// Detect various forms of authentication errors
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
113
|
+
let isAuthError = false;
|
|
114
|
+
try {
|
|
115
|
+
// Standard 401 Unauthorized
|
|
116
|
+
if (typeof result.error === 'object' &&
|
|
117
|
+
result.error.status === 401) {
|
|
118
|
+
isAuthError = true;
|
|
119
|
+
}
|
|
116
120
|
// OAuth specific errors
|
|
117
|
-
(result.error.error &&
|
|
121
|
+
else if (result.error.error &&
|
|
118
122
|
(result.error.error.code === 'invalid_token' ||
|
|
119
123
|
result.error.error.code === 'token_expired' ||
|
|
120
124
|
result.error.error.message === 'Invalid API key' ||
|
|
121
|
-
((
|
|
122
|
-
((
|
|
125
|
+
((_c = result.error.error.message) === null || _c === void 0 ? void 0 : _c.toLowerCase().includes('token')) ||
|
|
126
|
+
((_d = result.error.error.message) === null || _d === void 0 ? void 0 : _d.toLowerCase().includes('unauthorized')))) {
|
|
127
|
+
isAuthError = true;
|
|
128
|
+
}
|
|
123
129
|
// Message-based detection as fallback
|
|
124
|
-
(typeof result.error === 'string' &&
|
|
130
|
+
else if (typeof result.error === 'string' &&
|
|
125
131
|
(result.error.toLowerCase().includes('unauthorized') ||
|
|
126
132
|
result.error.toLowerCase().includes('token') ||
|
|
127
|
-
result.error.toLowerCase().includes('auth')))
|
|
128
|
-
|
|
129
|
-
if (process.argv.includes('--debug')) {
|
|
130
|
-
console.log(chalk_1.default.yellow('DEBUG: Auth error detected, attempting token refresh'));
|
|
131
|
-
console.log(chalk_1.default.yellow(`DEBUG: Error details: ${JSON.stringify(result.error, null, 2)}`));
|
|
133
|
+
result.error.toLowerCase().includes('auth'))) {
|
|
134
|
+
isAuthError = true;
|
|
132
135
|
}
|
|
136
|
+
}
|
|
137
|
+
catch (parseError) {
|
|
138
|
+
// If we can't parse the error structure, do a simple string check
|
|
139
|
+
const errorStr = String(result.error);
|
|
140
|
+
if (errorStr.toLowerCase().includes('unauthorized') ||
|
|
141
|
+
errorStr.toLowerCase().includes('token') ||
|
|
142
|
+
errorStr.toLowerCase().includes('auth')) {
|
|
143
|
+
isAuthError = true;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (isAuthError && tokenManager.getRefreshToken()) {
|
|
147
|
+
logger_1.logger.debug('Auth error detected, attempting token refresh');
|
|
148
|
+
logger_1.logger.debug(`Error details: ${JSON.stringify(result.error, null, 2)}`);
|
|
133
149
|
const refreshed = yield refreshAccessToken(tokenManager);
|
|
134
150
|
if (refreshed) {
|
|
135
|
-
|
|
136
|
-
console.log(chalk_1.default.green('DEBUG: Token refreshed successfully, retrying request'));
|
|
137
|
-
}
|
|
151
|
+
logger_1.logger.debug('Token refreshed successfully, retrying request');
|
|
138
152
|
// Update the Authorization header with the new token
|
|
139
153
|
if (!args[1])
|
|
140
154
|
args[1] = {};
|
|
@@ -145,12 +159,11 @@ const createAuthenticatedClient = () => {
|
|
|
145
159
|
return yield target[prop](...args);
|
|
146
160
|
}
|
|
147
161
|
else {
|
|
148
|
-
|
|
149
|
-
console.log(chalk_1.default.red('DEBUG: Token refresh failed'));
|
|
150
|
-
}
|
|
162
|
+
logger_1.logger.debug('Token refresh failed');
|
|
151
163
|
// Add a more helpful error message for users
|
|
152
164
|
if (typeof result.error === 'object') {
|
|
153
|
-
result.error.userMessage =
|
|
165
|
+
result.error.userMessage =
|
|
166
|
+
'Your session has expired. Please run `berget auth login` to log in again.';
|
|
154
167
|
}
|
|
155
168
|
}
|
|
156
169
|
}
|
|
@@ -171,12 +184,10 @@ function refreshAccessToken(tokenManager) {
|
|
|
171
184
|
const refreshToken = tokenManager.getRefreshToken();
|
|
172
185
|
if (!refreshToken)
|
|
173
186
|
return false;
|
|
174
|
-
|
|
175
|
-
console.log(chalk_1.default.yellow('DEBUG: Attempting to refresh access token'));
|
|
176
|
-
}
|
|
187
|
+
logger_1.logger.debug('Attempting to refresh access token');
|
|
177
188
|
// Use fetch directly since this endpoint might not be in the OpenAPI spec
|
|
178
189
|
try {
|
|
179
|
-
const response = yield fetch(`${API_BASE_URL}/v1/auth/refresh`, {
|
|
190
|
+
const response = yield fetch(`${exports.API_BASE_URL}/v1/auth/refresh`, {
|
|
180
191
|
method: 'POST',
|
|
181
192
|
headers: {
|
|
182
193
|
'Content-Type': 'application/json',
|
|
@@ -186,9 +197,7 @@ function refreshAccessToken(tokenManager) {
|
|
|
186
197
|
});
|
|
187
198
|
// Handle HTTP errors
|
|
188
199
|
if (!response.ok) {
|
|
189
|
-
|
|
190
|
-
console.log(chalk_1.default.yellow(`DEBUG: Token refresh error: HTTP ${response.status} ${response.statusText}`));
|
|
191
|
-
}
|
|
200
|
+
logger_1.logger.debug(`Token refresh error: HTTP ${response.status} ${response.statusText}`);
|
|
192
201
|
// Check if the refresh token itself is expired or invalid
|
|
193
202
|
if (response.status === 401 || response.status === 403) {
|
|
194
203
|
console.warn(chalk_1.default.yellow('Your refresh token has expired. Please run `berget auth login` again.'));
|
|
@@ -212,21 +221,19 @@ function refreshAccessToken(tokenManager) {
|
|
|
212
221
|
console.warn(chalk_1.default.yellow('Invalid token response. Please run `berget auth login` again.'));
|
|
213
222
|
return false;
|
|
214
223
|
}
|
|
215
|
-
|
|
216
|
-
console.log(chalk_1.default.green('DEBUG: Token refreshed successfully'));
|
|
217
|
-
}
|
|
224
|
+
logger_1.logger.debug('Token refreshed successfully');
|
|
218
225
|
// Update the token
|
|
219
226
|
tokenManager.updateAccessToken(data.token, data.expires_in || 3600);
|
|
220
227
|
// If a new refresh token was provided, update that too
|
|
221
228
|
if (data.refresh_token) {
|
|
222
229
|
tokenManager.setTokens(data.token, data.refresh_token, data.expires_in || 3600);
|
|
223
|
-
|
|
224
|
-
console.log(chalk_1.default.green('DEBUG: Refresh token also updated'));
|
|
225
|
-
}
|
|
230
|
+
logger_1.logger.debug('Refresh token also updated');
|
|
226
231
|
}
|
|
227
232
|
}
|
|
228
233
|
catch (fetchError) {
|
|
229
|
-
console.warn(chalk_1.default.yellow(`Failed to refresh token: ${fetchError instanceof Error
|
|
234
|
+
console.warn(chalk_1.default.yellow(`Failed to refresh token: ${fetchError instanceof Error
|
|
235
|
+
? fetchError.message
|
|
236
|
+
: String(fetchError)}`));
|
|
230
237
|
return false;
|
|
231
238
|
}
|
|
232
239
|
return true;
|
|
@@ -50,9 +50,13 @@ function registerApiKeyCommands(program) {
|
|
|
50
50
|
const status = key.active
|
|
51
51
|
? chalk_1.default.green('● Active')
|
|
52
52
|
: chalk_1.default.red('● Inactive');
|
|
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;
|
|
53
57
|
console.log(String(key.id).padEnd(10) +
|
|
54
58
|
key.name.padEnd(25) +
|
|
55
|
-
|
|
59
|
+
prefixStr.padEnd(15) +
|
|
56
60
|
status.padEnd(12) +
|
|
57
61
|
key.created.substring(0, 10).padEnd(12) +
|
|
58
62
|
lastUsed);
|
|
@@ -230,7 +234,9 @@ function registerApiKeyCommands(program) {
|
|
|
230
234
|
}
|
|
231
235
|
// Save the default API key
|
|
232
236
|
const defaultApiKeyManager = default_api_key_1.DefaultApiKeyManager.getInstance();
|
|
233
|
-
|
|
237
|
+
// We need to rotate the key to get the actual key value
|
|
238
|
+
const rotatedKey = yield apiKeyService.rotate(id);
|
|
239
|
+
defaultApiKeyManager.setDefaultApiKey(id, selectedKey.name, selectedKey.prefix, rotatedKey.key);
|
|
234
240
|
console.log(chalk_1.default.green(`✓ API key "${selectedKey.name}" set as default for chat commands`));
|
|
235
241
|
console.log('');
|
|
236
242
|
console.log(chalk_1.default.dim('This API key will be used by default when running chat commands'));
|
|
@@ -246,8 +252,8 @@ function registerApiKeyCommands(program) {
|
|
|
246
252
|
.action(() => {
|
|
247
253
|
try {
|
|
248
254
|
const defaultApiKeyManager = default_api_key_1.DefaultApiKeyManager.getInstance();
|
|
249
|
-
const
|
|
250
|
-
if (!
|
|
255
|
+
const defaultApiKeyData = defaultApiKeyManager.getDefaultApiKeyData();
|
|
256
|
+
if (!defaultApiKeyData) {
|
|
251
257
|
console.log(chalk_1.default.yellow('No default API key set'));
|
|
252
258
|
console.log('');
|
|
253
259
|
console.log('To set a default API key, run:');
|
|
@@ -256,9 +262,9 @@ function registerApiKeyCommands(program) {
|
|
|
256
262
|
}
|
|
257
263
|
console.log(chalk_1.default.bold('Default API key:'));
|
|
258
264
|
console.log('');
|
|
259
|
-
console.log(`${chalk_1.default.dim('ID:')} ${
|
|
260
|
-
console.log(`${chalk_1.default.dim('Name:')} ${
|
|
261
|
-
console.log(`${chalk_1.default.dim('Prefix:')} ${
|
|
265
|
+
console.log(`${chalk_1.default.dim('ID:')} ${defaultApiKeyData.id}`);
|
|
266
|
+
console.log(`${chalk_1.default.dim('Name:')} ${defaultApiKeyData.name}`);
|
|
267
|
+
console.log(`${chalk_1.default.dim('Prefix:')} ${defaultApiKeyData.prefix}`);
|
|
262
268
|
console.log('');
|
|
263
269
|
console.log(chalk_1.default.dim('This API key will be used by default when running chat commands'));
|
|
264
270
|
console.log(chalk_1.default.dim('You can override it with --api-key or --api-key-id options'));
|
|
@@ -48,25 +48,70 @@ function registerChatCommands(program) {
|
|
|
48
48
|
chat
|
|
49
49
|
.command(command_structure_1.SUBCOMMANDS.CHAT.RUN)
|
|
50
50
|
.description('Run a chat session with a specified model')
|
|
51
|
-
.argument('[model]', 'Model to use (default:
|
|
51
|
+
.argument('[model]', 'Model to use (default: google/gemma-3-27b-it)')
|
|
52
52
|
.option('-s, --system <message>', 'System message')
|
|
53
53
|
.option('-t, --temperature <temp>', 'Temperature (0-1)', parseFloat)
|
|
54
54
|
.option('-m, --max-tokens <tokens>', 'Maximum tokens to generate', parseInt)
|
|
55
55
|
.option('-k, --api-key <key>', 'API key to use for this chat session')
|
|
56
56
|
.option('--api-key-id <id>', 'ID of the API key to use from your saved keys')
|
|
57
|
+
.option('--stream', 'Stream the response')
|
|
57
58
|
.action((options) => __awaiter(this, void 0, void 0, function* () {
|
|
58
59
|
try {
|
|
59
60
|
const chatService = chat_service_1.ChatService.getInstance();
|
|
60
61
|
// Check if we have an API key or need to get one
|
|
61
62
|
let apiKey = options.apiKey;
|
|
62
63
|
let apiKeyId = options.apiKeyId;
|
|
63
|
-
//
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
64
|
+
// Check for environment variable first
|
|
65
|
+
const envApiKey = process.env.BERGET_API_KEY;
|
|
66
|
+
if (envApiKey) {
|
|
67
|
+
console.log(chalk_1.default.dim(`Using API key from BERGET_API_KEY environment variable`));
|
|
68
|
+
apiKey = envApiKey;
|
|
69
|
+
// Debug the API key (first few characters only)
|
|
70
|
+
if (process.argv.includes('--debug')) {
|
|
71
|
+
console.log(chalk_1.default.yellow(`DEBUG: API key from env starts with: ${envApiKey.substring(0, 4)}...`));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// If API key is already provided via command line, use it
|
|
75
|
+
else if (options.apiKey) {
|
|
76
|
+
console.log(chalk_1.default.dim(`Using API key from command line argument`));
|
|
77
|
+
apiKey = options.apiKey;
|
|
78
|
+
}
|
|
79
|
+
// If no API key or API key ID provided and no env var, check for default API key
|
|
80
|
+
else if (!apiKey && !apiKeyId) {
|
|
81
|
+
try {
|
|
82
|
+
const defaultApiKeyManager = default_api_key_1.DefaultApiKeyManager.getInstance();
|
|
83
|
+
const defaultApiKeyData = defaultApiKeyManager.getDefaultApiKeyData();
|
|
84
|
+
if (defaultApiKeyData) {
|
|
85
|
+
apiKeyId = defaultApiKeyData.id;
|
|
86
|
+
apiKey = defaultApiKeyData.key;
|
|
87
|
+
if (apiKey) {
|
|
88
|
+
console.log(chalk_1.default.dim(`Using default API key: ${defaultApiKeyData.name}`));
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
console.log(chalk_1.default.yellow(`Default API key "${defaultApiKeyData.name}" exists but the key value is missing.`));
|
|
92
|
+
console.log(chalk_1.default.yellow(`Try rotating the key with: berget api-keys rotate ${defaultApiKeyData.id}`));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
// No default API key, prompt the user to create one
|
|
97
|
+
console.log(chalk_1.default.yellow('No default API key set.'));
|
|
98
|
+
// Try to prompt for a default API key
|
|
99
|
+
apiKey = yield defaultApiKeyManager.promptForDefaultApiKey();
|
|
100
|
+
if (!apiKey) {
|
|
101
|
+
console.log(chalk_1.default.red('Error: An API key is required to use the chat command.'));
|
|
102
|
+
console.log(chalk_1.default.yellow('You can:'));
|
|
103
|
+
console.log(chalk_1.default.yellow('1. Create an API key with: berget api-keys create --name "My Key"'));
|
|
104
|
+
console.log(chalk_1.default.yellow('2. Set a default API key with: berget api-keys set-default <id>'));
|
|
105
|
+
console.log(chalk_1.default.yellow('3. Provide an API key with the --api-key option'));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
if (process.argv.includes('--debug')) {
|
|
112
|
+
console.log(chalk_1.default.yellow('DEBUG: Error checking default API key:'));
|
|
113
|
+
console.log(chalk_1.default.yellow(String(error)));
|
|
114
|
+
}
|
|
70
115
|
}
|
|
71
116
|
}
|
|
72
117
|
// If no direct API key, try to get one from API key ID
|
|
@@ -92,8 +137,18 @@ function registerChatCommands(program) {
|
|
|
92
137
|
}
|
|
93
138
|
}
|
|
94
139
|
catch (error) {
|
|
95
|
-
|
|
96
|
-
|
|
140
|
+
// Check if this is an authentication error
|
|
141
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
142
|
+
const isAuthError = errorMessage.includes('Unauthorized') ||
|
|
143
|
+
errorMessage.includes('Authentication failed') ||
|
|
144
|
+
errorMessage.includes('AUTH_FAILED');
|
|
145
|
+
if (isAuthError) {
|
|
146
|
+
console.log(chalk_1.default.yellow('Authentication required. Please run `berget auth login` first.'));
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
console.error(chalk_1.default.red('Error fetching API key:'));
|
|
150
|
+
console.error(error);
|
|
151
|
+
}
|
|
97
152
|
console.log(chalk_1.default.yellow('Using default authentication instead.'));
|
|
98
153
|
}
|
|
99
154
|
}
|
|
@@ -145,13 +200,40 @@ function registerChatCommands(program) {
|
|
|
145
200
|
});
|
|
146
201
|
try {
|
|
147
202
|
// Call the API
|
|
148
|
-
const
|
|
149
|
-
model: ((_a = options.args) === null || _a === void 0 ? void 0 : _a[0]) || '
|
|
203
|
+
const completionOptions = {
|
|
204
|
+
model: ((_a = options.args) === null || _a === void 0 ? void 0 : _a[0]) || 'google/gemma-3-27b-it',
|
|
150
205
|
messages: messages,
|
|
151
206
|
temperature: options.temperature !== undefined ? options.temperature : 0.7,
|
|
152
207
|
max_tokens: options.maxTokens || 4096,
|
|
153
|
-
|
|
154
|
-
}
|
|
208
|
+
stream: options.stream || false
|
|
209
|
+
};
|
|
210
|
+
// Only add apiKey if it actually exists
|
|
211
|
+
if (apiKey) {
|
|
212
|
+
completionOptions.apiKey = apiKey;
|
|
213
|
+
}
|
|
214
|
+
// Add streaming support
|
|
215
|
+
if (options.stream) {
|
|
216
|
+
let assistantResponse = '';
|
|
217
|
+
process.stdout.write(chalk_1.default.blue('Assistant: '));
|
|
218
|
+
completionOptions.onChunk = (chunk) => {
|
|
219
|
+
if (chunk.choices && chunk.choices[0] && chunk.choices[0].delta && chunk.choices[0].delta.content) {
|
|
220
|
+
const content = chunk.choices[0].delta.content;
|
|
221
|
+
process.stdout.write(content);
|
|
222
|
+
assistantResponse += content;
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
yield chatService.createCompletion(completionOptions);
|
|
226
|
+
console.log('\n');
|
|
227
|
+
// Add assistant response to messages
|
|
228
|
+
messages.push({
|
|
229
|
+
role: 'assistant',
|
|
230
|
+
content: assistantResponse
|
|
231
|
+
});
|
|
232
|
+
// Continue the conversation
|
|
233
|
+
askQuestion();
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
const response = yield chatService.createCompletion(completionOptions);
|
|
155
237
|
// Debug output
|
|
156
238
|
if (program.opts().debug) {
|
|
157
239
|
console.log(chalk_1.default.yellow('DEBUG: Full response:'));
|
|
@@ -209,10 +291,10 @@ function registerChatCommands(program) {
|
|
|
209
291
|
// If no API key or API key ID provided, check for default API key
|
|
210
292
|
if (!apiKey && !apiKeyId) {
|
|
211
293
|
const defaultApiKeyManager = default_api_key_1.DefaultApiKeyManager.getInstance();
|
|
212
|
-
const
|
|
213
|
-
if (
|
|
214
|
-
apiKeyId =
|
|
215
|
-
console.log(chalk_1.default.dim(`Using default API key: ${
|
|
294
|
+
const defaultApiKeyData = defaultApiKeyManager.getDefaultApiKeyData();
|
|
295
|
+
if (defaultApiKeyData) {
|
|
296
|
+
apiKeyId = defaultApiKeyData.id;
|
|
297
|
+
console.log(chalk_1.default.dim(`Using default API key: ${defaultApiKeyData.name}`));
|
|
216
298
|
}
|
|
217
299
|
}
|
|
218
300
|
if (apiKeyId && !apiKey) {
|
|
@@ -35,18 +35,8 @@ class ApiKeyService {
|
|
|
35
35
|
return __awaiter(this, void 0, void 0, function* () {
|
|
36
36
|
try {
|
|
37
37
|
const { data, error } = yield this.client.GET('/v1/api-keys');
|
|
38
|
-
if (error)
|
|
39
|
-
|
|
40
|
-
const errorObj = typeof error === 'string' ? JSON.parse(error) : error;
|
|
41
|
-
if (errorObj.status === 401) {
|
|
42
|
-
throw new Error(JSON.stringify({
|
|
43
|
-
error: "Authentication failed. Your session may have expired.",
|
|
44
|
-
code: "AUTH_FAILED",
|
|
45
|
-
details: "Please run 'berget login' to authenticate again."
|
|
46
|
-
}));
|
|
47
|
-
}
|
|
48
|
-
throw new Error(JSON.stringify(error));
|
|
49
|
-
}
|
|
38
|
+
if (error)
|
|
39
|
+
throw error;
|
|
50
40
|
return data || [];
|
|
51
41
|
}
|
|
52
42
|
catch (error) {
|
|
@@ -63,7 +53,7 @@ class ApiKeyService {
|
|
|
63
53
|
return __awaiter(this, void 0, void 0, function* () {
|
|
64
54
|
try {
|
|
65
55
|
const { data, error } = yield this.client.POST('/v1/api-keys', {
|
|
66
|
-
body: options
|
|
56
|
+
body: options,
|
|
67
57
|
});
|
|
68
58
|
if (error)
|
|
69
59
|
throw new Error(JSON.stringify(error));
|
|
@@ -83,7 +73,7 @@ class ApiKeyService {
|
|
|
83
73
|
return __awaiter(this, void 0, void 0, function* () {
|
|
84
74
|
try {
|
|
85
75
|
const { error } = yield this.client.DELETE('/v1/api-keys/{id}', {
|
|
86
|
-
params: { path: { id } }
|
|
76
|
+
params: { path: { id } },
|
|
87
77
|
});
|
|
88
78
|
if (error)
|
|
89
79
|
throw new Error(JSON.stringify(error));
|
|
@@ -103,7 +93,7 @@ class ApiKeyService {
|
|
|
103
93
|
return __awaiter(this, void 0, void 0, function* () {
|
|
104
94
|
try {
|
|
105
95
|
const { data, error } = yield this.client.PUT('/v1/api-keys/{id}/rotate', {
|
|
106
|
-
params: { path: { id } }
|
|
96
|
+
params: { path: { id } },
|
|
107
97
|
});
|
|
108
98
|
if (error)
|
|
109
99
|
throw new Error(JSON.stringify(error));
|
|
@@ -123,7 +113,7 @@ class ApiKeyService {
|
|
|
123
113
|
return __awaiter(this, void 0, void 0, function* () {
|
|
124
114
|
try {
|
|
125
115
|
const { data, error } = yield this.client.GET('/v1/api-keys/{id}/usage', {
|
|
126
|
-
params: { path: { id } }
|
|
116
|
+
params: { path: { id } },
|
|
127
117
|
});
|
|
128
118
|
if (error)
|
|
129
119
|
throw new Error(JSON.stringify(error));
|