berget 1.0.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/README.md +92 -0
- package/dist/index.js +49 -467
- package/dist/package.json +35 -0
- package/dist/src/client.js +210 -102
- package/dist/src/commands/api-keys.js +277 -0
- package/dist/src/commands/auth.js +65 -0
- package/dist/src/commands/autocomplete.js +24 -0
- package/dist/src/commands/billing.js +53 -0
- package/dist/src/commands/chat.js +342 -0
- package/dist/src/commands/clusters.js +69 -0
- package/dist/src/commands/index.js +25 -0
- package/dist/src/commands/models.js +69 -0
- package/dist/src/commands/users.js +43 -0
- package/dist/src/constants/command-structure.js +14 -0
- package/dist/src/services/api-key-service.js +6 -16
- package/dist/src/services/auth-service.js +49 -47
- package/dist/src/services/chat-service.js +300 -0
- package/dist/src/utils/config-checker.js +50 -0
- package/dist/src/utils/default-api-key.js +237 -0
- package/dist/src/utils/error-handler.js +4 -4
- package/dist/src/utils/token-manager.js +165 -0
- package/index.ts +56 -566
- package/package.json +8 -2
- package/src/client.ts +279 -80
- package/src/commands/api-keys.ts +374 -0
- package/src/commands/auth.ts +58 -0
- package/src/commands/autocomplete.ts +19 -0
- package/src/commands/billing.ts +41 -0
- package/src/commands/chat.ts +445 -0
- package/src/commands/clusters.ts +65 -0
- package/src/commands/index.ts +23 -0
- package/src/commands/models.ts +63 -0
- package/src/commands/users.ts +37 -0
- package/src/constants/command-structure.ts +16 -0
- package/src/services/api-key-service.ts +12 -20
- package/src/services/auth-service.ts +90 -50
- package/src/services/chat-service.ts +295 -0
- package/src/types/api.d.ts +238 -178
- package/src/types/json.d.ts +4 -0
- package/src/utils/config-checker.ts +23 -0
- package/src/utils/default-api-key.ts +229 -0
- package/src/utils/error-handler.ts +4 -4
- package/src/utils/token-manager.ts +150 -0
- package/tsconfig.json +1 -1
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.registerUserCommands = void 0;
|
|
13
|
+
const command_structure_1 = require("../constants/command-structure");
|
|
14
|
+
const client_1 = require("../client");
|
|
15
|
+
const error_handler_1 = require("../utils/error-handler");
|
|
16
|
+
/**
|
|
17
|
+
* Register user commands
|
|
18
|
+
*/
|
|
19
|
+
function registerUserCommands(program) {
|
|
20
|
+
const users = program
|
|
21
|
+
.command(command_structure_1.COMMAND_GROUPS.USERS)
|
|
22
|
+
.description('Manage users');
|
|
23
|
+
users
|
|
24
|
+
.command(command_structure_1.SUBCOMMANDS.USERS.LIST)
|
|
25
|
+
.description('List team members')
|
|
26
|
+
.action(() => __awaiter(this, void 0, void 0, function* () {
|
|
27
|
+
try {
|
|
28
|
+
const client = (0, client_1.createAuthenticatedClient)();
|
|
29
|
+
const { data, error } = yield client.GET('/v1/users');
|
|
30
|
+
if (error)
|
|
31
|
+
throw new Error(JSON.stringify(error));
|
|
32
|
+
console.log('Team Members:');
|
|
33
|
+
console.log('NAME EMAIL ROLE');
|
|
34
|
+
data.forEach((user) => {
|
|
35
|
+
console.log(`${user.name.padEnd(24)} ${user.email.padEnd(30)} ${user.role}`);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
(0, error_handler_1.handleError)('Failed to list team members', error);
|
|
40
|
+
}
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
exports.registerUserCommands = registerUserCommands;
|
|
@@ -17,6 +17,7 @@ exports.COMMAND_GROUPS = {
|
|
|
17
17
|
FLUX: 'flux',
|
|
18
18
|
USERS: 'users',
|
|
19
19
|
BILLING: 'billing',
|
|
20
|
+
CHAT: 'chat',
|
|
20
21
|
};
|
|
21
22
|
// Subcommands for each group
|
|
22
23
|
exports.SUBCOMMANDS = {
|
|
@@ -26,6 +27,11 @@ exports.SUBCOMMANDS = {
|
|
|
26
27
|
LOGOUT: 'logout',
|
|
27
28
|
WHOAMI: 'whoami',
|
|
28
29
|
},
|
|
30
|
+
// Chat commands
|
|
31
|
+
CHAT: {
|
|
32
|
+
RUN: 'run',
|
|
33
|
+
LIST: 'list',
|
|
34
|
+
},
|
|
29
35
|
// API Keys commands
|
|
30
36
|
API_KEYS: {
|
|
31
37
|
LIST: 'list',
|
|
@@ -33,6 +39,8 @@ exports.SUBCOMMANDS = {
|
|
|
33
39
|
DELETE: 'delete',
|
|
34
40
|
ROTATE: 'rotate',
|
|
35
41
|
DESCRIBE: 'describe',
|
|
42
|
+
SET_DEFAULT: 'set-default',
|
|
43
|
+
GET_DEFAULT: 'get-default',
|
|
36
44
|
},
|
|
37
45
|
// Clusters commands
|
|
38
46
|
CLUSTERS: {
|
|
@@ -102,6 +110,8 @@ exports.COMMAND_DESCRIPTIONS = {
|
|
|
102
110
|
[`${exports.COMMAND_GROUPS.API_KEYS} ${exports.SUBCOMMANDS.API_KEYS.DELETE}`]: 'Delete an API key',
|
|
103
111
|
[`${exports.COMMAND_GROUPS.API_KEYS} ${exports.SUBCOMMANDS.API_KEYS.ROTATE}`]: 'Rotate an API key',
|
|
104
112
|
[`${exports.COMMAND_GROUPS.API_KEYS} ${exports.SUBCOMMANDS.API_KEYS.DESCRIBE}`]: 'Get usage statistics for an API key',
|
|
113
|
+
[`${exports.COMMAND_GROUPS.API_KEYS} ${exports.SUBCOMMANDS.API_KEYS.SET_DEFAULT}`]: 'Set an API key as the default for chat commands',
|
|
114
|
+
[`${exports.COMMAND_GROUPS.API_KEYS} ${exports.SUBCOMMANDS.API_KEYS.GET_DEFAULT}`]: 'Show the current default API key',
|
|
105
115
|
// Clusters group
|
|
106
116
|
[exports.COMMAND_GROUPS.CLUSTERS]: 'Manage Kubernetes clusters',
|
|
107
117
|
[`${exports.COMMAND_GROUPS.CLUSTERS} ${exports.SUBCOMMANDS.CLUSTERS.LIST}`]: 'List all clusters',
|
|
@@ -147,4 +157,8 @@ exports.COMMAND_DESCRIPTIONS = {
|
|
|
147
157
|
[`${exports.COMMAND_GROUPS.BILLING} ${exports.SUBCOMMANDS.BILLING.ADD_PAYMENT_METHOD}`]: 'Add a new payment method',
|
|
148
158
|
[`${exports.COMMAND_GROUPS.BILLING} ${exports.SUBCOMMANDS.BILLING.REMOVE_PAYMENT_METHOD}`]: 'Remove a payment method',
|
|
149
159
|
[`${exports.COMMAND_GROUPS.BILLING} ${exports.SUBCOMMANDS.BILLING.UPDATE_SUBSCRIPTION}`]: 'Update subscription plan',
|
|
160
|
+
// Chat group
|
|
161
|
+
[exports.COMMAND_GROUPS.CHAT]: 'Interact with AI chat models',
|
|
162
|
+
[`${exports.COMMAND_GROUPS.CHAT} ${exports.SUBCOMMANDS.CHAT.RUN}`]: 'Run a chat session with a specified model',
|
|
163
|
+
[`${exports.COMMAND_GROUPS.CHAT} ${exports.SUBCOMMANDS.CHAT.LIST}`]: 'List available chat models',
|
|
150
164
|
};
|
|
@@ -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));
|
|
@@ -55,6 +55,21 @@ class AuthService {
|
|
|
55
55
|
}
|
|
56
56
|
return AuthService.instance;
|
|
57
57
|
}
|
|
58
|
+
whoami() {
|
|
59
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
60
|
+
try {
|
|
61
|
+
const { data: profile, error } = yield this.client.GET('/v1/users/me');
|
|
62
|
+
if (error) {
|
|
63
|
+
throw new Error(error ? JSON.stringify(error) : 'Failed to get user profile');
|
|
64
|
+
}
|
|
65
|
+
return profile;
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
(0, error_handler_1.handleError)('Failed to get user profile', error);
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
58
73
|
login() {
|
|
59
74
|
return __awaiter(this, void 0, void 0, function* () {
|
|
60
75
|
try {
|
|
@@ -68,16 +83,20 @@ class AuthService {
|
|
|
68
83
|
? JSON.stringify(deviceError)
|
|
69
84
|
: 'Failed to get device authorization data');
|
|
70
85
|
}
|
|
86
|
+
// Type assertion for deviceData
|
|
87
|
+
const typedDeviceData = deviceData;
|
|
71
88
|
// Display information to user
|
|
72
89
|
console.log(chalk_1.default.cyan('\nTo complete login:'));
|
|
73
|
-
console.log(chalk_1.default.cyan(`1. Open this URL: ${chalk_1.default.bold(
|
|
74
|
-
|
|
90
|
+
console.log(chalk_1.default.cyan(`1. Open this URL: ${chalk_1.default.bold(typedDeviceData.verification_url ||
|
|
91
|
+
'https://keycloak.berget.ai/device')}`));
|
|
92
|
+
if (!typedDeviceData.verification_url)
|
|
93
|
+
console.log(chalk_1.default.cyan(`2. Enter this code: ${chalk_1.default.bold(typedDeviceData.user_code || '')}\n`));
|
|
75
94
|
// Try to open browser automatically
|
|
76
95
|
try {
|
|
77
|
-
if (
|
|
96
|
+
if (typedDeviceData.verification_url) {
|
|
78
97
|
// Use dynamic import for the 'open' package
|
|
79
|
-
const open = yield Promise.resolve().then(() => __importStar(require('open'))).then(m => m.default);
|
|
80
|
-
yield open(
|
|
98
|
+
const open = yield Promise.resolve().then(() => __importStar(require('open'))).then((m) => m.default);
|
|
99
|
+
yield open(typedDeviceData.verification_url);
|
|
81
100
|
console.log(chalk_1.default.dim("Browser opened automatically. If it didn't open, please use the URL above."));
|
|
82
101
|
}
|
|
83
102
|
}
|
|
@@ -87,9 +106,11 @@ class AuthService {
|
|
|
87
106
|
console.log(chalk_1.default.dim('\nWaiting for authentication to complete...'));
|
|
88
107
|
// Step 2: Poll for completion
|
|
89
108
|
const startTime = Date.now();
|
|
90
|
-
const expiresIn =
|
|
109
|
+
const expiresIn = typedDeviceData.expires_in !== undefined
|
|
110
|
+
? typedDeviceData.expires_in
|
|
111
|
+
: 900;
|
|
91
112
|
const expiresAt = startTime + expiresIn * 1000;
|
|
92
|
-
let pollInterval = (
|
|
113
|
+
let pollInterval = (typedDeviceData.interval || 5) * 1000;
|
|
93
114
|
const spinner = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
94
115
|
let spinnerIdx = 0;
|
|
95
116
|
while (Date.now() < expiresAt) {
|
|
@@ -99,7 +120,7 @@ class AuthService {
|
|
|
99
120
|
process.stdout.write(`\r${chalk_1.default.blue(spinner[spinnerIdx])} Waiting for authentication...`);
|
|
100
121
|
spinnerIdx = (spinnerIdx + 1) % spinner.length;
|
|
101
122
|
// Check if authentication is complete
|
|
102
|
-
const deviceCode =
|
|
123
|
+
const deviceCode = typedDeviceData.device_code || '';
|
|
103
124
|
const { data: tokenData, error: tokenError } = yield client_1.apiClient.POST('/v1/auth/device/token', {
|
|
104
125
|
body: {
|
|
105
126
|
device_code: deviceCode,
|
|
@@ -147,16 +168,27 @@ class AuthService {
|
|
|
147
168
|
continue;
|
|
148
169
|
}
|
|
149
170
|
}
|
|
150
|
-
else if (tokenData
|
|
151
|
-
//
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
171
|
+
else if (tokenData) {
|
|
172
|
+
// Type assertion for tokenData
|
|
173
|
+
const typedTokenData = tokenData;
|
|
174
|
+
if (typedTokenData.token) {
|
|
175
|
+
// Success!
|
|
176
|
+
(0, client_1.saveAuthToken)(typedTokenData.token, typedTokenData.refresh_token || '', typedTokenData.expires_in || 3600);
|
|
177
|
+
if (process.argv.includes('--debug')) {
|
|
178
|
+
console.log(chalk_1.default.yellow('DEBUG: Token data received:'));
|
|
179
|
+
console.log(chalk_1.default.yellow(JSON.stringify({
|
|
180
|
+
expires_in: typedTokenData.expires_in,
|
|
181
|
+
refresh_expires_in: typedTokenData.refresh_expires_in,
|
|
182
|
+
}, null, 2)));
|
|
183
|
+
}
|
|
184
|
+
process.stdout.write('\r' + ' '.repeat(50) + '\r'); // Clear the spinner line
|
|
185
|
+
console.log(chalk_1.default.green('✓ Successfully logged in to Berget'));
|
|
186
|
+
if (typedTokenData.user) {
|
|
187
|
+
const user = typedTokenData.user;
|
|
188
|
+
console.log(chalk_1.default.green(`Logged in as ${user.name || user.email || 'User'}`));
|
|
189
|
+
}
|
|
190
|
+
return true;
|
|
158
191
|
}
|
|
159
|
-
return true;
|
|
160
192
|
}
|
|
161
193
|
}
|
|
162
194
|
console.log(chalk_1.default.red('\n\nAuthentication timed out. Please try again.'));
|
|
@@ -168,36 +200,6 @@ class AuthService {
|
|
|
168
200
|
}
|
|
169
201
|
});
|
|
170
202
|
}
|
|
171
|
-
isAuthenticated() {
|
|
172
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
173
|
-
try {
|
|
174
|
-
// Call an API endpoint that requires authentication
|
|
175
|
-
const { data, error } = yield this.client.GET('/v1/users/me');
|
|
176
|
-
return !!data && !error;
|
|
177
|
-
}
|
|
178
|
-
catch (_a) {
|
|
179
|
-
return false;
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* Get current user profile
|
|
185
|
-
* Command: berget auth whoami
|
|
186
|
-
*/
|
|
187
|
-
whoami() {
|
|
188
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
189
|
-
try {
|
|
190
|
-
const { data, error } = yield this.client.GET('/v1/users/me');
|
|
191
|
-
if (error)
|
|
192
|
-
throw new Error(JSON.stringify(error));
|
|
193
|
-
return data;
|
|
194
|
-
}
|
|
195
|
-
catch (error) {
|
|
196
|
-
(0, error_handler_1.handleError)('Failed to get user profile', error);
|
|
197
|
-
throw error;
|
|
198
|
-
}
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
203
|
}
|
|
202
204
|
exports.AuthService = AuthService;
|
|
203
205
|
// Command group name for this service
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
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
|
+
};
|
|
34
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
35
|
+
var t = {};
|
|
36
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
37
|
+
t[p] = s[p];
|
|
38
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
39
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
40
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
41
|
+
t[p[i]] = s[p[i]];
|
|
42
|
+
}
|
|
43
|
+
return t;
|
|
44
|
+
};
|
|
45
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
46
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
47
|
+
};
|
|
48
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
49
|
+
exports.ChatService = void 0;
|
|
50
|
+
const client_1 = require("../client");
|
|
51
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
52
|
+
/**
|
|
53
|
+
* Service for interacting with the chat API
|
|
54
|
+
* Command group: chat
|
|
55
|
+
*/
|
|
56
|
+
class ChatService {
|
|
57
|
+
constructor() {
|
|
58
|
+
this.client = (0, client_1.createAuthenticatedClient)();
|
|
59
|
+
}
|
|
60
|
+
static getInstance() {
|
|
61
|
+
if (!ChatService.instance) {
|
|
62
|
+
ChatService.instance = new ChatService();
|
|
63
|
+
}
|
|
64
|
+
return ChatService.instance;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Create a chat completion
|
|
68
|
+
* Command: berget chat completion
|
|
69
|
+
*/
|
|
70
|
+
createCompletion(options) {
|
|
71
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
72
|
+
try {
|
|
73
|
+
console.log(chalk_1.default.yellow('DEBUG: Starting createCompletion method'));
|
|
74
|
+
// Check if options is defined
|
|
75
|
+
if (!options) {
|
|
76
|
+
console.log(chalk_1.default.red('ERROR: options is undefined'));
|
|
77
|
+
throw new Error('Chat completion options are undefined');
|
|
78
|
+
}
|
|
79
|
+
// Log the raw options object
|
|
80
|
+
console.log(chalk_1.default.yellow('DEBUG: Raw options:'), typeof options, options ? 'defined' : 'undefined');
|
|
81
|
+
const headers = {};
|
|
82
|
+
// Check if debug is enabled
|
|
83
|
+
const isDebug = process.argv.includes('--debug');
|
|
84
|
+
if (isDebug) {
|
|
85
|
+
console.log(chalk_1.default.yellow('DEBUG: Starting createCompletion with options:'));
|
|
86
|
+
try {
|
|
87
|
+
console.log(chalk_1.default.yellow(JSON.stringify(Object.assign(Object.assign({}, options), { apiKey: options.apiKey ? '***' : undefined, messages: options.messages ? `${options.messages.length} messages` : undefined }), null, 2)));
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
console.log(chalk_1.default.red('ERROR: Failed to stringify options:'), error);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Create a copy of options to avoid modifying the original
|
|
94
|
+
const optionsCopy = Object.assign({}, options);
|
|
95
|
+
if (isDebug) {
|
|
96
|
+
console.log(chalk_1.default.yellow('DEBUG: Checking for API key'));
|
|
97
|
+
console.log(chalk_1.default.yellow(`DEBUG: optionsCopy.apiKey exists: ${!!optionsCopy.apiKey}`));
|
|
98
|
+
}
|
|
99
|
+
// Check for environment variables first - prioritize this over everything else
|
|
100
|
+
const envApiKey = process.env.BERGET_API_KEY;
|
|
101
|
+
if (envApiKey) {
|
|
102
|
+
if (isDebug) {
|
|
103
|
+
console.log(chalk_1.default.yellow('DEBUG: Using API key from BERGET_API_KEY environment variable'));
|
|
104
|
+
}
|
|
105
|
+
optionsCopy.apiKey = envApiKey;
|
|
106
|
+
}
|
|
107
|
+
// Only try to get the default API key if no API key is provided and no env var is set
|
|
108
|
+
else if (!optionsCopy.apiKey) {
|
|
109
|
+
if (isDebug) {
|
|
110
|
+
console.log(chalk_1.default.yellow('DEBUG: No API key provided, trying to get default'));
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
// Import the DefaultApiKeyManager directly
|
|
114
|
+
if (isDebug) {
|
|
115
|
+
console.log(chalk_1.default.yellow('DEBUG: Importing DefaultApiKeyManager'));
|
|
116
|
+
}
|
|
117
|
+
const DefaultApiKeyManager = (yield Promise.resolve().then(() => __importStar(require('../utils/default-api-key')))).DefaultApiKeyManager;
|
|
118
|
+
const defaultApiKeyManager = DefaultApiKeyManager.getInstance();
|
|
119
|
+
if (isDebug) {
|
|
120
|
+
console.log(chalk_1.default.yellow('DEBUG: Got DefaultApiKeyManager instance'));
|
|
121
|
+
}
|
|
122
|
+
// Try to get the default API key
|
|
123
|
+
if (isDebug) {
|
|
124
|
+
console.log(chalk_1.default.yellow('DEBUG: Calling promptForDefaultApiKey'));
|
|
125
|
+
}
|
|
126
|
+
const defaultApiKeyData = defaultApiKeyManager.getDefaultApiKeyData();
|
|
127
|
+
const apiKey = (defaultApiKeyData === null || defaultApiKeyData === void 0 ? void 0 : defaultApiKeyData.key) || (yield defaultApiKeyManager.promptForDefaultApiKey());
|
|
128
|
+
if (isDebug) {
|
|
129
|
+
console.log(chalk_1.default.yellow(`DEBUG: Default API key data exists: ${!!defaultApiKeyData}`));
|
|
130
|
+
console.log(chalk_1.default.yellow(`DEBUG: promptForDefaultApiKey returned: ${apiKey ? 'a key' : 'null'}`));
|
|
131
|
+
}
|
|
132
|
+
if (apiKey) {
|
|
133
|
+
if (isDebug) {
|
|
134
|
+
console.log(chalk_1.default.yellow('DEBUG: Using API key from default API key manager'));
|
|
135
|
+
}
|
|
136
|
+
optionsCopy.apiKey = apiKey;
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
console.log(chalk_1.default.yellow('No API key available. You need to either:'));
|
|
140
|
+
console.log(chalk_1.default.yellow('1. Create an API key with: berget api-keys create --name "My Key"'));
|
|
141
|
+
console.log(chalk_1.default.yellow('2. Set a default API key with: berget api-keys set-default <id>'));
|
|
142
|
+
console.log(chalk_1.default.yellow('3. Provide an API key with the --api-key option'));
|
|
143
|
+
console.log(chalk_1.default.yellow('4. Set the BERGET_API_KEY environment variable'));
|
|
144
|
+
console.log(chalk_1.default.yellow('\nExample:'));
|
|
145
|
+
console.log(chalk_1.default.yellow(' export BERGET_API_KEY=your_api_key_here'));
|
|
146
|
+
console.log(chalk_1.default.yellow(' # or for a single command:'));
|
|
147
|
+
console.log(chalk_1.default.yellow(' BERGET_API_KEY=your_api_key_here berget chat run google/gemma-3-27b-it'));
|
|
148
|
+
throw new Error('No API key provided and no default API key set');
|
|
149
|
+
}
|
|
150
|
+
// Set the API key in the options
|
|
151
|
+
if (isDebug) {
|
|
152
|
+
console.log(chalk_1.default.yellow('DEBUG: Setting API key in options'));
|
|
153
|
+
}
|
|
154
|
+
// Only set the API key if it's not null
|
|
155
|
+
if (apiKey) {
|
|
156
|
+
optionsCopy.apiKey = apiKey;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
console.log(chalk_1.default.red('Error getting API key:'));
|
|
161
|
+
if (error instanceof Error) {
|
|
162
|
+
console.log(chalk_1.default.red(error.message));
|
|
163
|
+
}
|
|
164
|
+
console.log(chalk_1.default.yellow('Please create an API key with: berget api-keys create --name "My Key"'));
|
|
165
|
+
throw new Error('Failed to get API key');
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (isDebug) {
|
|
169
|
+
console.log(chalk_1.default.yellow('DEBUG: Chat completion options:'));
|
|
170
|
+
console.log(chalk_1.default.yellow(JSON.stringify(Object.assign(Object.assign({}, optionsCopy), { apiKey: optionsCopy.apiKey ? '***' : undefined // Hide the actual API key in debug output
|
|
171
|
+
}), null, 2)));
|
|
172
|
+
}
|
|
173
|
+
// If an API key is provided, use it for this request
|
|
174
|
+
if (optionsCopy.apiKey) {
|
|
175
|
+
headers['Authorization'] = `Bearer ${optionsCopy.apiKey}`;
|
|
176
|
+
// Remove apiKey from options before sending to API
|
|
177
|
+
const { apiKey } = optionsCopy, requestOptions = __rest(optionsCopy, ["apiKey"]);
|
|
178
|
+
if (isDebug) {
|
|
179
|
+
console.log(chalk_1.default.yellow('DEBUG: Using provided API key'));
|
|
180
|
+
console.log(chalk_1.default.yellow('DEBUG: Request options:'));
|
|
181
|
+
console.log(chalk_1.default.yellow(JSON.stringify(requestOptions, null, 2)));
|
|
182
|
+
}
|
|
183
|
+
try {
|
|
184
|
+
const response = yield this.client.POST('/v1/chat/completions', {
|
|
185
|
+
body: requestOptions,
|
|
186
|
+
headers
|
|
187
|
+
});
|
|
188
|
+
// Check if response has an error property
|
|
189
|
+
const responseAny = response;
|
|
190
|
+
if (responseAny && responseAny.error)
|
|
191
|
+
throw new Error(JSON.stringify(responseAny.error));
|
|
192
|
+
if (isDebug) {
|
|
193
|
+
console.log(chalk_1.default.yellow('DEBUG: API response:'));
|
|
194
|
+
console.log(chalk_1.default.yellow(JSON.stringify(response, null, 2)));
|
|
195
|
+
// Output the complete response data for debugging
|
|
196
|
+
console.log(chalk_1.default.yellow('DEBUG: Complete response data:'));
|
|
197
|
+
console.log(chalk_1.default.yellow(JSON.stringify(response.data, null, 2)));
|
|
198
|
+
}
|
|
199
|
+
return response.data;
|
|
200
|
+
}
|
|
201
|
+
catch (requestError) {
|
|
202
|
+
if (process.argv.includes('--debug')) {
|
|
203
|
+
console.log(chalk_1.default.red(`DEBUG: Request error: ${requestError instanceof Error ? requestError.message : String(requestError)}`));
|
|
204
|
+
}
|
|
205
|
+
throw requestError;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
// We've exhausted all options for getting an API key
|
|
210
|
+
console.log(chalk_1.default.yellow('No API key available. You need to either:'));
|
|
211
|
+
console.log(chalk_1.default.yellow('1. Create an API key with: berget api-keys create --name "My Key"'));
|
|
212
|
+
console.log(chalk_1.default.yellow('2. Set a default API key with: berget api-keys set-default <id>'));
|
|
213
|
+
console.log(chalk_1.default.yellow('3. Provide an API key with the --api-key option'));
|
|
214
|
+
console.log(chalk_1.default.yellow('4. Set the BERGET_API_KEY environment variable'));
|
|
215
|
+
console.log(chalk_1.default.yellow('\nExample:'));
|
|
216
|
+
console.log(chalk_1.default.yellow(' export BERGET_API_KEY=your_api_key_here'));
|
|
217
|
+
console.log(chalk_1.default.yellow(' # or for a single command:'));
|
|
218
|
+
console.log(chalk_1.default.yellow(' BERGET_API_KEY=your_api_key_here berget chat run google/gemma-3-27b-it'));
|
|
219
|
+
throw new Error('No API key available. Please provide an API key or set a default API key.');
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
// Improved error handling
|
|
224
|
+
let errorMessage = 'Failed to create chat completion';
|
|
225
|
+
if (error instanceof Error) {
|
|
226
|
+
try {
|
|
227
|
+
// Try to parse the error message as JSON
|
|
228
|
+
const parsedError = JSON.parse(error.message);
|
|
229
|
+
if (parsedError.error && parsedError.error.message) {
|
|
230
|
+
errorMessage = `Chat error: ${parsedError.error.message}`;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
catch (e) {
|
|
234
|
+
// If parsing fails, use the original error message
|
|
235
|
+
errorMessage = `Chat error: ${error.message}`;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
console.error(chalk_1.default.red(errorMessage));
|
|
239
|
+
throw new Error(errorMessage);
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* List available models
|
|
245
|
+
* Command: berget chat list
|
|
246
|
+
*/
|
|
247
|
+
listModels(apiKey) {
|
|
248
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
249
|
+
try {
|
|
250
|
+
// Check for environment variable first, then fallback to provided API key
|
|
251
|
+
const envApiKey = process.env.BERGET_API_KEY;
|
|
252
|
+
const effectiveApiKey = envApiKey || apiKey;
|
|
253
|
+
if (effectiveApiKey) {
|
|
254
|
+
const headers = {
|
|
255
|
+
'Authorization': `Bearer ${effectiveApiKey}`
|
|
256
|
+
};
|
|
257
|
+
const { data, error } = yield this.client.GET('/v1/models', { headers });
|
|
258
|
+
if (error)
|
|
259
|
+
throw new Error(JSON.stringify(error));
|
|
260
|
+
return data;
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
const { data, error } = yield this.client.GET('/v1/models');
|
|
264
|
+
if (error)
|
|
265
|
+
throw new Error(JSON.stringify(error));
|
|
266
|
+
return data;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
catch (error) {
|
|
270
|
+
// Improved error handling
|
|
271
|
+
let errorMessage = 'Failed to list models';
|
|
272
|
+
if (error instanceof Error) {
|
|
273
|
+
try {
|
|
274
|
+
// Try to parse the error message as JSON
|
|
275
|
+
const parsedError = JSON.parse(error.message);
|
|
276
|
+
if (parsedError.error) {
|
|
277
|
+
errorMessage = `Models error: ${typeof parsedError.error === 'string' ?
|
|
278
|
+
parsedError.error :
|
|
279
|
+
(parsedError.error.message || JSON.stringify(parsedError.error))}`;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
catch (e) {
|
|
283
|
+
// If parsing fails, use the original error message
|
|
284
|
+
errorMessage = `Models error: ${error.message}`;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
console.error(chalk_1.default.red(errorMessage));
|
|
288
|
+
throw new Error(errorMessage);
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
exports.ChatService = ChatService;
|
|
294
|
+
// Command group name for this service
|
|
295
|
+
ChatService.COMMAND_GROUP = 'chat';
|
|
296
|
+
// Subcommands for this service
|
|
297
|
+
ChatService.COMMANDS = {
|
|
298
|
+
RUN: 'run',
|
|
299
|
+
LIST: 'list'
|
|
300
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.checkBergetConfig = void 0;
|
|
27
|
+
const fs = __importStar(require("fs"));
|
|
28
|
+
const path = __importStar(require("path"));
|
|
29
|
+
/**
|
|
30
|
+
* Check for .bergetconfig file and handle cluster switching
|
|
31
|
+
*/
|
|
32
|
+
function checkBergetConfig() {
|
|
33
|
+
const configPath = path.join(process.cwd(), '.bergetconfig');
|
|
34
|
+
if (fs.existsSync(configPath)) {
|
|
35
|
+
try {
|
|
36
|
+
const config = fs.readFileSync(configPath, 'utf8');
|
|
37
|
+
const match = config.match(/cluster:\s*(.+)/);
|
|
38
|
+
if (match && match[1]) {
|
|
39
|
+
const clusterName = match[1].trim();
|
|
40
|
+
console.log(`🔄 Berget: Switched to cluster "${clusterName}"`);
|
|
41
|
+
console.log('✓ kubectl config updated');
|
|
42
|
+
console.log('');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
// Silently ignore errors reading config
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.checkBergetConfig = checkBergetConfig;
|