berget 2.0.3 → 2.0.5
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/AGENTS.md +29 -0
- package/dist/index.js +2 -0
- package/dist/package.json +1 -1
- package/dist/src/commands/api-keys.js +119 -24
- package/dist/src/commands/chat.js +6 -6
- package/dist/src/commands/code.js +103 -57
- package/dist/src/constants/command-structure.js +2 -0
- package/dist/src/services/api-key-service.js +64 -1
- package/dist/src/utils/config-loader.js +217 -0
- package/dist/src/utils/error-handler.js +98 -22
- package/dist/tests/utils/config-loader.test.js +182 -0
- package/index.ts +19 -19
- package/opencode.json +1 -1
- package/package.json +1 -1
- package/src/commands/api-keys.ts +156 -28
- package/src/commands/chat.ts +6 -6
- package/src/commands/code.ts +119 -58
- package/src/constants/command-structure.ts +2 -0
- package/src/services/api-key-service.ts +100 -2
- package/src/utils/config-loader.ts +261 -0
- package/src/utils/error-handler.ts +120 -23
|
@@ -50,17 +50,80 @@ class ApiKeyService {
|
|
|
50
50
|
* Command: berget api-keys create
|
|
51
51
|
*/
|
|
52
52
|
create(options) {
|
|
53
|
+
var _a, _b, _c, _d;
|
|
53
54
|
return __awaiter(this, void 0, void 0, function* () {
|
|
54
55
|
try {
|
|
56
|
+
// Validate input before sending request
|
|
57
|
+
if (!options.name || options.name.trim().length === 0) {
|
|
58
|
+
throw new Error('API key name is required and cannot be empty');
|
|
59
|
+
}
|
|
60
|
+
if (options.name.length > 100) {
|
|
61
|
+
throw new Error('API key name must be 100 characters or less');
|
|
62
|
+
}
|
|
63
|
+
if (options.description && options.description.length > 500) {
|
|
64
|
+
throw new Error('API key description must be 500 characters or less');
|
|
65
|
+
}
|
|
55
66
|
const { data, error } = yield this.client.POST('/v1/api-keys', {
|
|
56
67
|
body: options,
|
|
57
68
|
});
|
|
58
|
-
if (error)
|
|
69
|
+
if (error) {
|
|
70
|
+
// Enhanced error handling with specific troubleshooting
|
|
71
|
+
// Handle specific error cases
|
|
72
|
+
if (typeof error === 'object' && error !== null) {
|
|
73
|
+
const errorObj = error;
|
|
74
|
+
if (((_a = errorObj.error) === null || _a === void 0 ? void 0 : _a.code) === 'API_KEY_CREATION_FAILED') {
|
|
75
|
+
let detailedMessage = 'Failed to create API key. This could be due to:\n';
|
|
76
|
+
detailedMessage += '• Account limits or quota restrictions\n';
|
|
77
|
+
detailedMessage +=
|
|
78
|
+
'• Insufficient permissions for API key creation\n';
|
|
79
|
+
detailedMessage += '• Temporary server issues\n';
|
|
80
|
+
detailedMessage += '• Billing or subscription issues\n\n';
|
|
81
|
+
detailedMessage += 'Troubleshooting steps:\n';
|
|
82
|
+
detailedMessage +=
|
|
83
|
+
'1. Check if you have reached your API key limit\n';
|
|
84
|
+
detailedMessage +=
|
|
85
|
+
'2. Verify your account has API key creation permissions\n';
|
|
86
|
+
detailedMessage += '3. Check your billing status and subscription\n';
|
|
87
|
+
detailedMessage +=
|
|
88
|
+
'4. Try again in a few minutes if this is a temporary issue\n';
|
|
89
|
+
detailedMessage += '5. Contact support if the problem persists';
|
|
90
|
+
throw new Error(detailedMessage);
|
|
91
|
+
}
|
|
92
|
+
if (((_b = errorObj.error) === null || _b === void 0 ? void 0 : _b.code) === 'QUOTA_EXCEEDED') {
|
|
93
|
+
throw new Error('You have reached your API key limit. Please delete existing keys or contact support to increase your quota.');
|
|
94
|
+
}
|
|
95
|
+
if (((_c = errorObj.error) === null || _c === void 0 ? void 0 : _c.code) === 'INSUFFICIENT_PERMISSIONS') {
|
|
96
|
+
throw new Error('Your account does not have permission to create API keys. Please contact your administrator.');
|
|
97
|
+
}
|
|
98
|
+
if (((_d = errorObj.error) === null || _d === void 0 ? void 0 : _d.code) === 'BILLING_REQUIRED') {
|
|
99
|
+
throw new Error('A valid billing method is required to create API keys. Please add a payment method.');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
59
102
|
throw new Error(JSON.stringify(error));
|
|
103
|
+
}
|
|
104
|
+
if (!data) {
|
|
105
|
+
throw new Error('No data received from server');
|
|
106
|
+
}
|
|
60
107
|
return data;
|
|
61
108
|
}
|
|
62
109
|
catch (error) {
|
|
63
110
|
console.error('Failed to create API key:', error);
|
|
111
|
+
// Add additional context for common issues
|
|
112
|
+
if (error instanceof Error) {
|
|
113
|
+
if (error.message.includes('ECONNREFUSED')) {
|
|
114
|
+
throw new Error('Cannot connect to Berget API. Please check your internet connection.');
|
|
115
|
+
}
|
|
116
|
+
if (error.message.includes('ENOTFOUND')) {
|
|
117
|
+
throw new Error('Cannot resolve Berget API hostname. Please check your DNS settings.');
|
|
118
|
+
}
|
|
119
|
+
if (error.message.includes('401') ||
|
|
120
|
+
error.message.includes('Unauthorized')) {
|
|
121
|
+
throw new Error('Authentication failed. Please run `berget auth login` to log in again.');
|
|
122
|
+
}
|
|
123
|
+
if (error.message.includes('403')) {
|
|
124
|
+
throw new Error('Access forbidden. Your account may not have permission to create API keys.');
|
|
125
|
+
}
|
|
126
|
+
}
|
|
64
127
|
throw error;
|
|
65
128
|
}
|
|
66
129
|
});
|
|
@@ -0,0 +1,217 @@
|
|
|
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.getProviderModels = exports.getModelConfig = exports.getAllAgentConfigs = exports.getAgentConfig = exports.getConfigLoader = exports.ConfigLoader = void 0;
|
|
27
|
+
const fs = __importStar(require("fs"));
|
|
28
|
+
const path = __importStar(require("path"));
|
|
29
|
+
const logger_1 = require("./logger");
|
|
30
|
+
class ConfigLoader {
|
|
31
|
+
constructor(configPath) {
|
|
32
|
+
this.config = null;
|
|
33
|
+
// Default to opencode.json in current working directory
|
|
34
|
+
this.configPath = configPath || path.join(process.cwd(), 'opencode.json');
|
|
35
|
+
}
|
|
36
|
+
static getInstance(configPath) {
|
|
37
|
+
if (!ConfigLoader.instance) {
|
|
38
|
+
ConfigLoader.instance = new ConfigLoader(configPath);
|
|
39
|
+
}
|
|
40
|
+
return ConfigLoader.instance;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Load configuration from opencode.json
|
|
44
|
+
*/
|
|
45
|
+
loadConfig() {
|
|
46
|
+
if (this.config) {
|
|
47
|
+
return this.config;
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
if (!fs.existsSync(this.configPath)) {
|
|
51
|
+
throw new Error(`Configuration file not found: ${this.configPath}`);
|
|
52
|
+
}
|
|
53
|
+
const configContent = fs.readFileSync(this.configPath, 'utf8');
|
|
54
|
+
this.config = JSON.parse(configContent);
|
|
55
|
+
logger_1.logger.debug(`Loaded configuration from ${this.configPath}`);
|
|
56
|
+
return this.config;
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
logger_1.logger.error(`Failed to load configuration from ${this.configPath}:`, error);
|
|
60
|
+
throw new Error(`Failed to load configuration: ${error instanceof Error ? error.message : String(error)}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Get agent configuration by name
|
|
65
|
+
*/
|
|
66
|
+
getAgentConfig(agentName) {
|
|
67
|
+
var _a;
|
|
68
|
+
const config = this.loadConfig();
|
|
69
|
+
return ((_a = config.agent) === null || _a === void 0 ? void 0 : _a[agentName]) || null;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Get all agent configurations
|
|
73
|
+
*/
|
|
74
|
+
getAllAgentConfigs() {
|
|
75
|
+
const config = this.loadConfig();
|
|
76
|
+
return config.agent || {};
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Get model configuration
|
|
80
|
+
*/
|
|
81
|
+
getModelConfig() {
|
|
82
|
+
const config = this.loadConfig();
|
|
83
|
+
// Extract from config or fall back to defaults
|
|
84
|
+
const primary = config.model || 'berget/deepseek-r1';
|
|
85
|
+
const small = config.small_model || 'berget/gpt-oss';
|
|
86
|
+
return { primary, small };
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Get provider model configuration
|
|
90
|
+
*/
|
|
91
|
+
getProviderModels() {
|
|
92
|
+
var _a, _b;
|
|
93
|
+
const config = this.loadConfig();
|
|
94
|
+
// Extract from provider configuration
|
|
95
|
+
if ((_b = (_a = config.provider) === null || _a === void 0 ? void 0 : _a.berget) === null || _b === void 0 ? void 0 : _b.models) {
|
|
96
|
+
return config.provider.berget.models;
|
|
97
|
+
}
|
|
98
|
+
// Fallback to defaults
|
|
99
|
+
return {
|
|
100
|
+
'deepseek-r1': {
|
|
101
|
+
name: 'GLM-4.6',
|
|
102
|
+
limit: { output: 4000, context: 90000 }
|
|
103
|
+
},
|
|
104
|
+
'gpt-oss': {
|
|
105
|
+
name: 'GPT-OSS',
|
|
106
|
+
limit: { output: 4000, context: 128000 }
|
|
107
|
+
},
|
|
108
|
+
'llama-8b': {
|
|
109
|
+
name: 'llama-3.1-8b',
|
|
110
|
+
limit: { output: 4000, context: 128000 }
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Get command configurations
|
|
116
|
+
*/
|
|
117
|
+
getCommandConfigs() {
|
|
118
|
+
const config = this.loadConfig();
|
|
119
|
+
return config.command || {};
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Get watcher configuration
|
|
123
|
+
*/
|
|
124
|
+
getWatcherConfig() {
|
|
125
|
+
const config = this.loadConfig();
|
|
126
|
+
return config.watcher || { ignore: ['node_modules', 'dist', '.git', 'coverage'] };
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Get provider configuration
|
|
130
|
+
*/
|
|
131
|
+
getProviderConfig() {
|
|
132
|
+
const config = this.loadConfig();
|
|
133
|
+
return config.provider || {};
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Check if an agent exists
|
|
137
|
+
*/
|
|
138
|
+
hasAgent(agentName) {
|
|
139
|
+
return agentName in this.getAllAgentConfigs();
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Get list of all available agent names
|
|
143
|
+
*/
|
|
144
|
+
getAgentNames() {
|
|
145
|
+
return Object.keys(this.getAllAgentConfigs());
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Get list of primary agents (mode: 'primary')
|
|
149
|
+
*/
|
|
150
|
+
getPrimaryAgentNames() {
|
|
151
|
+
const agents = this.getAllAgentConfigs();
|
|
152
|
+
return Object.keys(agents).filter(name => agents[name].mode === 'primary');
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Get list of subagents (mode: 'subagent')
|
|
156
|
+
*/
|
|
157
|
+
getSubagentNames() {
|
|
158
|
+
const agents = this.getAllAgentConfigs();
|
|
159
|
+
return Object.keys(agents).filter(name => agents[name].mode === 'subagent');
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Reload configuration from file
|
|
163
|
+
*/
|
|
164
|
+
reloadConfig() {
|
|
165
|
+
this.config = null;
|
|
166
|
+
return this.loadConfig();
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Set custom configuration path (for testing or different environments)
|
|
170
|
+
*/
|
|
171
|
+
setConfigPath(configPath) {
|
|
172
|
+
this.configPath = configPath;
|
|
173
|
+
this.config = null; // Force reload
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Get the current configuration path
|
|
177
|
+
*/
|
|
178
|
+
getConfigPath() {
|
|
179
|
+
return this.configPath;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
exports.ConfigLoader = ConfigLoader;
|
|
183
|
+
/**
|
|
184
|
+
* Convenience function to get the config loader instance
|
|
185
|
+
*/
|
|
186
|
+
function getConfigLoader(configPath) {
|
|
187
|
+
return ConfigLoader.getInstance(configPath);
|
|
188
|
+
}
|
|
189
|
+
exports.getConfigLoader = getConfigLoader;
|
|
190
|
+
/**
|
|
191
|
+
* Convenience function to get agent configuration
|
|
192
|
+
*/
|
|
193
|
+
function getAgentConfig(agentName, configPath) {
|
|
194
|
+
return getConfigLoader(configPath).getAgentConfig(agentName);
|
|
195
|
+
}
|
|
196
|
+
exports.getAgentConfig = getAgentConfig;
|
|
197
|
+
/**
|
|
198
|
+
* Convenience function to get all agent configurations
|
|
199
|
+
*/
|
|
200
|
+
function getAllAgentConfigs(configPath) {
|
|
201
|
+
return getConfigLoader(configPath).getAllAgentConfigs();
|
|
202
|
+
}
|
|
203
|
+
exports.getAllAgentConfigs = getAllAgentConfigs;
|
|
204
|
+
/**
|
|
205
|
+
* Convenience function to get model configuration
|
|
206
|
+
*/
|
|
207
|
+
function getModelConfig(configPath) {
|
|
208
|
+
return getConfigLoader(configPath).getModelConfig();
|
|
209
|
+
}
|
|
210
|
+
exports.getModelConfig = getModelConfig;
|
|
211
|
+
/**
|
|
212
|
+
* Convenience function to get provider models
|
|
213
|
+
*/
|
|
214
|
+
function getProviderModels(configPath) {
|
|
215
|
+
return getConfigLoader(configPath).getProviderModels();
|
|
216
|
+
}
|
|
217
|
+
exports.getProviderModels = getProviderModels;
|
|
@@ -9,44 +9,120 @@ const chalk_1 = __importDefault(require("chalk"));
|
|
|
9
9
|
* Formats and prints error messages in a consistent way
|
|
10
10
|
*/
|
|
11
11
|
function handleError(message, error) {
|
|
12
|
-
console.error(chalk_1.default.red(
|
|
12
|
+
console.error(chalk_1.default.red(`❌ Error: ${message}`));
|
|
13
|
+
let errorDetails = '';
|
|
14
|
+
let errorCode = '';
|
|
15
|
+
let errorType = '';
|
|
13
16
|
// If the error is a string (like JSON.stringify(error))
|
|
14
17
|
if (typeof error === 'string') {
|
|
15
18
|
try {
|
|
16
19
|
// Try to parse it as JSON
|
|
17
20
|
const parsedError = JSON.parse(error);
|
|
18
21
|
if (parsedError.error) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
22
|
+
errorDetails = parsedError.error.message || parsedError.error;
|
|
23
|
+
errorCode = parsedError.error.code || parsedError.code;
|
|
24
|
+
errorType = parsedError.error.type || '';
|
|
23
25
|
}
|
|
24
26
|
else {
|
|
25
|
-
|
|
27
|
+
errorDetails = error;
|
|
26
28
|
}
|
|
27
29
|
}
|
|
28
30
|
catch (_a) {
|
|
29
31
|
// If it's not valid JSON, just print the string
|
|
30
|
-
|
|
32
|
+
errorDetails = error;
|
|
31
33
|
}
|
|
32
34
|
}
|
|
33
35
|
else if (error && error.message) {
|
|
34
36
|
// If it's an Error object
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if ((typeof error === 'string' &&
|
|
39
|
-
(error.includes('Unauthorized') ||
|
|
40
|
-
error.includes('Authentication failed'))) ||
|
|
41
|
-
(error &&
|
|
42
|
-
error.message &&
|
|
43
|
-
(error.message.includes('Unauthorized') ||
|
|
44
|
-
error.message.includes('Authentication failed'))) ||
|
|
45
|
-
(error &&
|
|
46
|
-
error.code &&
|
|
47
|
-
(error.code === 401 || error.code === 'AUTH_FAILED'))) {
|
|
48
|
-
console.error(chalk_1.default.yellow('\nYou need to be logged in to use this command.'));
|
|
49
|
-
console.error(chalk_1.default.yellow('Run `berget auth login` to authenticate.'));
|
|
37
|
+
errorDetails = error.message;
|
|
38
|
+
errorCode = error.code;
|
|
39
|
+
errorType = error.type;
|
|
50
40
|
}
|
|
41
|
+
// Print error details
|
|
42
|
+
if (errorDetails) {
|
|
43
|
+
console.error(chalk_1.default.dim(`📝 Details: ${errorDetails}`));
|
|
44
|
+
}
|
|
45
|
+
if (errorCode) {
|
|
46
|
+
console.error(chalk_1.default.dim(`🔢 Code: ${errorCode}`));
|
|
47
|
+
}
|
|
48
|
+
// Provide helpful troubleshooting based on error type
|
|
49
|
+
provideTroubleshootingTips(errorType, errorCode, errorDetails);
|
|
51
50
|
}
|
|
52
51
|
exports.handleError = handleError;
|
|
52
|
+
/**
|
|
53
|
+
* Provides helpful troubleshooting tips based on error type
|
|
54
|
+
*/
|
|
55
|
+
function provideTroubleshootingTips(errorType, errorCode, errorDetails) {
|
|
56
|
+
console.error(chalk_1.default.blue('\n💡 Troubleshooting tips:'));
|
|
57
|
+
// Authentication errors
|
|
58
|
+
if (errorType === 'authentication_error' ||
|
|
59
|
+
errorCode === 'AUTH_FAILED' ||
|
|
60
|
+
(errorDetails === null || errorDetails === void 0 ? void 0 : errorDetails.includes('Unauthorized')) ||
|
|
61
|
+
(errorDetails === null || errorDetails === void 0 ? void 0 : errorDetails.includes('Authentication failed'))) {
|
|
62
|
+
console.error(chalk_1.default.yellow(' 🔐 Authentication issue detected:'));
|
|
63
|
+
console.error(chalk_1.default.white(' • Run `berget auth login` to log in'));
|
|
64
|
+
console.error(chalk_1.default.white(' • Check if your session has expired'));
|
|
65
|
+
console.error(chalk_1.default.white(' • Verify you have the correct permissions'));
|
|
66
|
+
}
|
|
67
|
+
// Network/connection errors
|
|
68
|
+
if ((errorDetails === null || errorDetails === void 0 ? void 0 : errorDetails.includes('fetch failed')) ||
|
|
69
|
+
(errorDetails === null || errorDetails === void 0 ? void 0 : errorDetails.includes('ECONNREFUSED')) ||
|
|
70
|
+
(errorDetails === null || errorDetails === void 0 ? void 0 : errorDetails.includes('ENOTFOUND')) ||
|
|
71
|
+
(errorDetails === null || errorDetails === void 0 ? void 0 : errorDetails.includes('network'))) {
|
|
72
|
+
console.error(chalk_1.default.yellow(' 🌐 Network issue detected:'));
|
|
73
|
+
console.error(chalk_1.default.white(' • Check your internet connection'));
|
|
74
|
+
console.error(chalk_1.default.white(' • Verify you can reach api.berget.ai'));
|
|
75
|
+
console.error(chalk_1.default.white(' • Try again in a few minutes'));
|
|
76
|
+
console.error(chalk_1.default.white(' • Check if any firewall is blocking the request'));
|
|
77
|
+
}
|
|
78
|
+
// API key errors
|
|
79
|
+
if ((errorCode === null || errorCode === void 0 ? void 0 : errorCode.includes('API_KEY')) ||
|
|
80
|
+
(errorDetails === null || errorDetails === void 0 ? void 0 : errorDetails.includes('API key')) ||
|
|
81
|
+
errorType === 'invalid_request_error') {
|
|
82
|
+
console.error(chalk_1.default.yellow(' 🔑 API key issue detected:'));
|
|
83
|
+
console.error(chalk_1.default.white(' • Run `berget api-keys list` to check your keys'));
|
|
84
|
+
console.error(chalk_1.default.white(' • Create a new key with `berget api-keys create --name "My Key"`'));
|
|
85
|
+
console.error(chalk_1.default.white(' • Set a default key with `berget api-keys set-default <id>`'));
|
|
86
|
+
console.error(chalk_1.default.white(' • Check if your API key has expired'));
|
|
87
|
+
}
|
|
88
|
+
// Rate limiting
|
|
89
|
+
if (errorCode === 'RATE_LIMIT_EXCEEDED' ||
|
|
90
|
+
(errorDetails === null || errorDetails === void 0 ? void 0 : errorDetails.includes('rate limit')) ||
|
|
91
|
+
(errorDetails === null || errorDetails === void 0 ? void 0 : errorDetails.includes('too many requests'))) {
|
|
92
|
+
console.error(chalk_1.default.yellow(' ⏱️ Rate limit exceeded:'));
|
|
93
|
+
console.error(chalk_1.default.white(' • Wait a few minutes before trying again'));
|
|
94
|
+
console.error(chalk_1.default.white(' • Consider upgrading your plan for higher limits'));
|
|
95
|
+
console.error(chalk_1.default.white(' • Use `berget billing get-usage` to check your usage'));
|
|
96
|
+
}
|
|
97
|
+
// Server errors
|
|
98
|
+
if ((errorCode === null || errorCode === void 0 ? void 0 : errorCode.includes('SERVER_ERROR')) ||
|
|
99
|
+
errorType === 'server_error' ||
|
|
100
|
+
(errorCode && parseInt(errorCode) >= 500)) {
|
|
101
|
+
console.error(chalk_1.default.yellow(' 🖥️ Server issue detected:'));
|
|
102
|
+
console.error(chalk_1.default.white(' • This is a temporary problem on our end'));
|
|
103
|
+
console.error(chalk_1.default.white(' • Try again in a few minutes'));
|
|
104
|
+
console.error(chalk_1.default.white(' • Check status.berget.ai for service status'));
|
|
105
|
+
console.error(chalk_1.default.white(' • Contact support if the problem persists'));
|
|
106
|
+
}
|
|
107
|
+
// Cluster errors
|
|
108
|
+
if ((errorCode === null || errorCode === void 0 ? void 0 : errorCode.includes('CLUSTERS')) ||
|
|
109
|
+
(errorDetails === null || errorDetails === void 0 ? void 0 : errorDetails.includes('cluster'))) {
|
|
110
|
+
console.error(chalk_1.default.yellow(' 🏗️ Cluster issue detected:'));
|
|
111
|
+
console.error(chalk_1.default.white(' • Clusters may be temporarily unavailable'));
|
|
112
|
+
console.error(chalk_1.default.white(' • Try again later or contact support'));
|
|
113
|
+
console.error(chalk_1.default.white(' • Check your cluster permissions'));
|
|
114
|
+
}
|
|
115
|
+
// Generic fallback
|
|
116
|
+
if (!(errorType === null || errorType === void 0 ? void 0 : errorType.includes('authentication')) &&
|
|
117
|
+
!(errorDetails === null || errorDetails === void 0 ? void 0 : errorDetails.includes('fetch failed')) &&
|
|
118
|
+
!(errorCode === null || errorCode === void 0 ? void 0 : errorCode.includes('API_KEY')) &&
|
|
119
|
+
!(errorCode === null || errorCode === void 0 ? void 0 : errorCode.includes('RATE_LIMIT')) &&
|
|
120
|
+
!(errorCode === null || errorCode === void 0 ? void 0 : errorCode.includes('SERVER_ERROR')) &&
|
|
121
|
+
!(errorCode === null || errorCode === void 0 ? void 0 : errorCode.includes('CLUSTERS'))) {
|
|
122
|
+
console.error(chalk_1.default.yellow(' ❓ General issue:'));
|
|
123
|
+
console.error(chalk_1.default.white(' • Try running the command with --debug for more info'));
|
|
124
|
+
console.error(chalk_1.default.white(' • Check your configuration with `berget auth whoami`'));
|
|
125
|
+
console.error(chalk_1.default.white(' • Contact support if the problem persists'));
|
|
126
|
+
}
|
|
127
|
+
console.error(chalk_1.default.dim('\nNeed more help? Visit https://docs.berget.ai or contact support@berget.ai'));
|
|
128
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
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
|
+
const vitest_1 = require("vitest");
|
|
27
|
+
const config_loader_1 = require("../../src/utils/config-loader");
|
|
28
|
+
const path = __importStar(require("path"));
|
|
29
|
+
const fs_1 = require("fs");
|
|
30
|
+
(0, vitest_1.describe)('ConfigLoader Singleton Pattern', () => {
|
|
31
|
+
const testConfigPath = path.join(process.cwd(), 'test-config.json');
|
|
32
|
+
const testConfigPath2 = path.join(process.cwd(), 'test-config2.json');
|
|
33
|
+
const testConfig = {
|
|
34
|
+
$schema: 'https://opencode.ai/config.json',
|
|
35
|
+
username: 'test-user',
|
|
36
|
+
model: 'test-model',
|
|
37
|
+
agent: {
|
|
38
|
+
test: {
|
|
39
|
+
model: 'test-model',
|
|
40
|
+
temperature: 0.5,
|
|
41
|
+
mode: 'primary',
|
|
42
|
+
permission: { edit: 'allow', bash: 'allow', webfetch: 'allow' },
|
|
43
|
+
description: 'Test agent',
|
|
44
|
+
prompt: 'Test prompt'
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
(0, vitest_1.beforeEach)(() => {
|
|
49
|
+
// Clean up any existing test files
|
|
50
|
+
if ((0, fs_1.existsSync)(testConfigPath))
|
|
51
|
+
(0, fs_1.unlinkSync)(testConfigPath);
|
|
52
|
+
if ((0, fs_1.existsSync)(testConfigPath2))
|
|
53
|
+
(0, fs_1.unlinkSync)(testConfigPath2);
|
|
54
|
+
config_loader_1.ConfigLoader.instance = null;
|
|
55
|
+
});
|
|
56
|
+
(0, vitest_1.afterEach)(() => {
|
|
57
|
+
// Clean up test files
|
|
58
|
+
if ((0, fs_1.existsSync)(testConfigPath))
|
|
59
|
+
(0, fs_1.unlinkSync)(testConfigPath);
|
|
60
|
+
if ((0, fs_1.existsSync)(testConfigPath2))
|
|
61
|
+
(0, fs_1.unlinkSync)(testConfigPath2);
|
|
62
|
+
config_loader_1.ConfigLoader.instance = null;
|
|
63
|
+
});
|
|
64
|
+
(0, vitest_1.it)('should create singleton instance on first call', () => {
|
|
65
|
+
const instance1 = config_loader_1.ConfigLoader.getInstance(testConfigPath);
|
|
66
|
+
const instance2 = config_loader_1.ConfigLoader.getInstance();
|
|
67
|
+
(0, vitest_1.expect)(instance1).toBe(instance2);
|
|
68
|
+
(0, vitest_1.expect)(instance1.getConfigPath()).toBe(testConfigPath);
|
|
69
|
+
});
|
|
70
|
+
(0, vitest_1.it)('should ignore configPath parameter after first instantiation', () => {
|
|
71
|
+
const instance1 = config_loader_1.ConfigLoader.getInstance(testConfigPath);
|
|
72
|
+
const instance2 = config_loader_1.ConfigLoader.getInstance(testConfigPath2); // Should be ignored
|
|
73
|
+
(0, vitest_1.expect)(instance1).toBe(instance2);
|
|
74
|
+
(0, vitest_1.expect)(instance1.getConfigPath()).toBe(testConfigPath); // Still uses first path
|
|
75
|
+
});
|
|
76
|
+
(0, vitest_1.it)('should load and cache configuration', () => {
|
|
77
|
+
(0, fs_1.writeFileSync)(testConfigPath, JSON.stringify(testConfig));
|
|
78
|
+
const loader = config_loader_1.ConfigLoader.getInstance(testConfigPath);
|
|
79
|
+
const config1 = loader.loadConfig();
|
|
80
|
+
const config2 = loader.loadConfig();
|
|
81
|
+
(0, vitest_1.expect)(config1).toBe(config2); // Same object reference (cached)
|
|
82
|
+
(0, vitest_1.expect)(config1.username).toBe('test-user');
|
|
83
|
+
});
|
|
84
|
+
(0, vitest_1.it)('should reload configuration when reloadConfig is called', () => {
|
|
85
|
+
(0, fs_1.writeFileSync)(testConfigPath, JSON.stringify(testConfig));
|
|
86
|
+
const loader = config_loader_1.ConfigLoader.getInstance(testConfigPath);
|
|
87
|
+
const config1 = loader.loadConfig();
|
|
88
|
+
// Modify file
|
|
89
|
+
const modifiedConfig = Object.assign(Object.assign({}, testConfig), { username: 'modified-user' });
|
|
90
|
+
(0, fs_1.writeFileSync)(testConfigPath, JSON.stringify(modifiedConfig));
|
|
91
|
+
const config2 = loader.loadConfig(); // Should still be cached
|
|
92
|
+
(0, vitest_1.expect)(config2).toBe(config1);
|
|
93
|
+
(0, vitest_1.expect)(config2.username).toBe('test-user');
|
|
94
|
+
const config3 = loader.reloadConfig(); // Should reload
|
|
95
|
+
(0, vitest_1.expect)(config3).not.toBe(config1);
|
|
96
|
+
(0, vitest_1.expect)(config3.username).toBe('modified-user');
|
|
97
|
+
});
|
|
98
|
+
(0, vitest_1.it)('should change config path and clear cache with setConfigPath', () => {
|
|
99
|
+
(0, fs_1.writeFileSync)(testConfigPath, JSON.stringify(testConfig));
|
|
100
|
+
(0, fs_1.writeFileSync)(testConfigPath2, JSON.stringify(Object.assign(Object.assign({}, testConfig), { username: 'user2' })));
|
|
101
|
+
const loader = config_loader_1.ConfigLoader.getInstance(testConfigPath);
|
|
102
|
+
const config1 = loader.loadConfig();
|
|
103
|
+
(0, vitest_1.expect)(config1.username).toBe('test-user');
|
|
104
|
+
loader.setConfigPath(testConfigPath2);
|
|
105
|
+
const config2 = loader.loadConfig();
|
|
106
|
+
(0, vitest_1.expect)(config2.username).toBe('user2');
|
|
107
|
+
(0, vitest_1.expect)(loader.getConfigPath()).toBe(testConfigPath2);
|
|
108
|
+
});
|
|
109
|
+
(0, vitest_1.it)('should throw error when config file does not exist', () => {
|
|
110
|
+
const loader = config_loader_1.ConfigLoader.getInstance('/nonexistent/config.json');
|
|
111
|
+
(0, vitest_1.expect)(() => loader.loadConfig()).toThrow('Configuration file not found');
|
|
112
|
+
});
|
|
113
|
+
(0, vitest_1.it)('should throw error when config file has invalid JSON', () => {
|
|
114
|
+
(0, fs_1.writeFileSync)(testConfigPath, 'invalid json content');
|
|
115
|
+
const loader = config_loader_1.ConfigLoader.getInstance(testConfigPath);
|
|
116
|
+
(0, vitest_1.expect)(() => loader.loadConfig()).toThrow('Invalid JSON in configuration file');
|
|
117
|
+
});
|
|
118
|
+
(0, vitest_1.it)('should provide fallback model configuration with warning', () => {
|
|
119
|
+
const minimalConfig = { username: 'test' }; // No model settings
|
|
120
|
+
(0, fs_1.writeFileSync)(testConfigPath, JSON.stringify(minimalConfig));
|
|
121
|
+
const loader = config_loader_1.ConfigLoader.getInstance(testConfigPath);
|
|
122
|
+
const modelConfig = loader.getModelConfig();
|
|
123
|
+
(0, vitest_1.expect)(modelConfig.primary).toBe('berget/deepseek-r1');
|
|
124
|
+
(0, vitest_1.expect)(modelConfig.small).toBe('berget/gpt-oss');
|
|
125
|
+
});
|
|
126
|
+
(0, vitest_1.it)('should provide fallback provider models with warning', () => {
|
|
127
|
+
const minimalConfig = { username: 'test' }; // No provider settings
|
|
128
|
+
(0, fs_1.writeFileSync)(testConfigPath, JSON.stringify(minimalConfig));
|
|
129
|
+
const loader = config_loader_1.ConfigLoader.getInstance(testConfigPath);
|
|
130
|
+
const providerModels = loader.getProviderModels();
|
|
131
|
+
(0, vitest_1.expect)(providerModels['deepseek-r1']).toBeDefined();
|
|
132
|
+
(0, vitest_1.expect)(providerModels['gpt-oss']).toBeDefined();
|
|
133
|
+
(0, vitest_1.expect)(providerModels['llama-8b']).toBeDefined();
|
|
134
|
+
});
|
|
135
|
+
(0, vitest_1.it)('should provide fallback watcher configuration with warning', () => {
|
|
136
|
+
const minimalConfig = { username: 'test' }; // No watcher settings
|
|
137
|
+
(0, fs_1.writeFileSync)(testConfigPath, JSON.stringify(minimalConfig));
|
|
138
|
+
const loader = config_loader_1.ConfigLoader.getInstance(testConfigPath);
|
|
139
|
+
const watcherConfig = loader.getWatcherConfig();
|
|
140
|
+
(0, vitest_1.expect)(watcherConfig.ignore).toContain('node_modules');
|
|
141
|
+
(0, vitest_1.expect)(watcherConfig.ignore).toContain('dist');
|
|
142
|
+
(0, vitest_1.expect)(watcherConfig.ignore).toContain('.git');
|
|
143
|
+
(0, vitest_1.expect)(watcherConfig.ignore).toContain('coverage');
|
|
144
|
+
});
|
|
145
|
+
(0, vitest_1.it)('should use actual configuration when provided', () => {
|
|
146
|
+
const fullConfig = Object.assign(Object.assign({}, testConfig), { model: 'custom-primary-model', small_model: 'custom-small-model', provider: {
|
|
147
|
+
berget: {
|
|
148
|
+
models: {
|
|
149
|
+
'custom-model': {
|
|
150
|
+
name: 'Custom Model',
|
|
151
|
+
limit: { output: 2000, context: 4000 }
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}, watcher: {
|
|
156
|
+
ignore: ['custom-ignore']
|
|
157
|
+
} });
|
|
158
|
+
(0, fs_1.writeFileSync)(testConfigPath, JSON.stringify(fullConfig));
|
|
159
|
+
const loader = config_loader_1.ConfigLoader.getInstance(testConfigPath);
|
|
160
|
+
const modelConfig = loader.getModelConfig();
|
|
161
|
+
(0, vitest_1.expect)(modelConfig.primary).toBe('custom-primary-model');
|
|
162
|
+
(0, vitest_1.expect)(modelConfig.small).toBe('custom-small-model');
|
|
163
|
+
const providerModels = loader.getProviderModels();
|
|
164
|
+
(0, vitest_1.expect)(providerModels['custom-model']).toBeDefined();
|
|
165
|
+
(0, vitest_1.expect)(providerModels['custom-model'].name).toBe('Custom Model');
|
|
166
|
+
const watcherConfig = loader.getWatcherConfig();
|
|
167
|
+
(0, vitest_1.expect)(watcherConfig.ignore).toEqual(['custom-ignore']);
|
|
168
|
+
});
|
|
169
|
+
(0, vitest_1.it)('should maintain consistent getter method behavior', () => {
|
|
170
|
+
(0, fs_1.writeFileSync)(testConfigPath, JSON.stringify(testConfig));
|
|
171
|
+
const loader = config_loader_1.ConfigLoader.getInstance(testConfigPath);
|
|
172
|
+
// All getter methods should use the same cached config
|
|
173
|
+
const agentConfig = loader.getAgentConfig('test');
|
|
174
|
+
const allAgents = loader.getAllAgentConfigs();
|
|
175
|
+
const commandConfigs = loader.getCommandConfigs();
|
|
176
|
+
const providerConfig = loader.getProviderConfig();
|
|
177
|
+
(0, vitest_1.expect)(agentConfig).toBeDefined();
|
|
178
|
+
(0, vitest_1.expect)(allAgents.test).toBeDefined();
|
|
179
|
+
(0, vitest_1.expect)(commandConfigs).toEqual({});
|
|
180
|
+
(0, vitest_1.expect)(providerConfig).toEqual({});
|
|
181
|
+
});
|
|
182
|
+
});
|