berget 2.0.6 → 2.1.1
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 +163 -2
- package/dist/package.json +1 -1
- package/dist/src/commands/chat.js +1 -2
- package/dist/src/commands/code.js +152 -87
- package/dist/src/services/auth-service.js +1 -2
- package/dist/src/services/chat-service.js +77 -5
- package/dist/src/utils/config-loader.js +83 -27
- package/dist/tests/commands/chat.test.js +2 -2
- package/dist/tests/commands/code.test.js +4 -4
- package/dist/tests/utils/config-loader.test.js +248 -163
- package/opencode.json +31 -71
- package/package.json +1 -1
- package/src/commands/chat.ts +68 -61
- package/src/commands/code.ts +304 -213
- package/src/services/auth-service.ts +1 -4
- package/src/services/chat-service.ts +86 -5
- package/src/utils/config-loader.ts +113 -38
- package/tests/commands/chat.test.ts +2 -2
- package/tests/commands/code.test.ts +4 -4
- package/tests/utils/config-loader.test.ts +320 -0
- package/blog-post.md +0 -176
|
@@ -1,182 +1,267 @@
|
|
|
1
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
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
3
|
const vitest_1 = require("vitest");
|
|
27
4
|
const config_loader_1 = require("../../src/utils/config-loader");
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
};
|
|
5
|
+
// Mock fs module
|
|
6
|
+
const mockFs = vitest_1.vi.hoisted(() => ({
|
|
7
|
+
existsSync: vitest_1.vi.fn(),
|
|
8
|
+
readFileSync: vitest_1.vi.fn(),
|
|
9
|
+
writeFileSync: vitest_1.vi.fn(),
|
|
10
|
+
mkdirSync: vitest_1.vi.fn(),
|
|
11
|
+
}));
|
|
12
|
+
vitest_1.vi.mock('fs', () => mockFs);
|
|
13
|
+
(0, vitest_1.describe)('ConfigLoader', () => {
|
|
14
|
+
const testConfigPath = '/tmp/test-opencode.json';
|
|
15
|
+
let configLoader;
|
|
48
16
|
(0, vitest_1.beforeEach)(() => {
|
|
49
|
-
//
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
config_loader_1.ConfigLoader.instance = null;
|
|
17
|
+
// Reset mocks and clear singleton
|
|
18
|
+
vitest_1.vi.clearAllMocks();
|
|
19
|
+
config_loader_1.ConfigLoader.clearInstance();
|
|
20
|
+
// Create new instance for each test using getInstance
|
|
21
|
+
configLoader = config_loader_1.ConfigLoader.getInstance(testConfigPath);
|
|
55
22
|
});
|
|
56
23
|
(0, vitest_1.afterEach)(() => {
|
|
57
|
-
|
|
58
|
-
|
|
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');
|
|
24
|
+
vitest_1.vi.clearAllMocks();
|
|
25
|
+
config_loader_1.ConfigLoader.clearInstance();
|
|
83
26
|
});
|
|
84
|
-
(0, vitest_1.
|
|
85
|
-
(0,
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
27
|
+
(0, vitest_1.describe)('when config file does not exist', () => {
|
|
28
|
+
(0, vitest_1.beforeEach)(() => {
|
|
29
|
+
mockFs.existsSync.mockReturnValue(false);
|
|
30
|
+
});
|
|
31
|
+
(0, vitest_1.describe)('getModelConfig', () => {
|
|
32
|
+
(0, vitest_1.it)('should return default values when config file does not exist', () => {
|
|
33
|
+
const modelConfig = configLoader.getModelConfig();
|
|
34
|
+
(0, vitest_1.expect)(modelConfig).toEqual({
|
|
35
|
+
primary: 'berget/glm-4.7',
|
|
36
|
+
small: 'berget/gpt-oss'
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
(0, vitest_1.it)('should return default values when using convenience function', () => {
|
|
40
|
+
const modelConfig = (0, config_loader_1.getModelConfig)(testConfigPath);
|
|
41
|
+
(0, vitest_1.expect)(modelConfig).toEqual({
|
|
42
|
+
primary: 'berget/glm-4.7',
|
|
43
|
+
small: 'berget/gpt-oss'
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
(0, vitest_1.describe)('getProviderModels', () => {
|
|
48
|
+
(0, vitest_1.it)('should return default provider models when config file does not exist', () => {
|
|
49
|
+
const models = configLoader.getProviderModels();
|
|
50
|
+
(0, vitest_1.expect)(models).toEqual({
|
|
51
|
+
'glm-4.7': {
|
|
52
|
+
name: 'GLM-4.7',
|
|
53
|
+
limit: { output: 4000, context: 90000 }
|
|
54
|
+
},
|
|
55
|
+
'gpt-oss': {
|
|
56
|
+
name: 'GPT-OSS',
|
|
57
|
+
limit: { output: 4000, context: 128000 },
|
|
58
|
+
modalities: {
|
|
59
|
+
input: ['text', 'image'],
|
|
60
|
+
output: ['text']
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
'llama-8b': {
|
|
64
|
+
name: 'llama-3.1-8b',
|
|
65
|
+
limit: { output: 4000, context: 128000 }
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
(0, vitest_1.it)('should return default provider models when using convenience function', () => {
|
|
70
|
+
const models = (0, config_loader_1.getProviderModels)(testConfigPath);
|
|
71
|
+
(0, vitest_1.expect)(models).toEqual({
|
|
72
|
+
'glm-4.7': {
|
|
73
|
+
name: 'GLM-4.7',
|
|
74
|
+
limit: { output: 4000, context: 90000 }
|
|
75
|
+
},
|
|
76
|
+
'gpt-oss': {
|
|
77
|
+
name: 'GPT-OSS',
|
|
78
|
+
limit: { output: 4000, context: 128000 },
|
|
79
|
+
modalities: {
|
|
80
|
+
input: ['text', 'image'],
|
|
81
|
+
output: ['text']
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
'llama-8b': {
|
|
85
|
+
name: 'llama-3.1-8b',
|
|
86
|
+
limit: { output: 4000, context: 128000 }
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
(0, vitest_1.describe)('getAllAgentConfigs', () => {
|
|
92
|
+
(0, vitest_1.it)('should return empty object when config file does not exist', () => {
|
|
93
|
+
const agents = configLoader.getAllAgentConfigs();
|
|
94
|
+
(0, vitest_1.expect)(agents).toEqual({});
|
|
95
|
+
});
|
|
96
|
+
(0, vitest_1.it)('should return empty object when using convenience function', () => {
|
|
97
|
+
const agents = (0, config_loader_1.getAllAgentConfigs)(testConfigPath);
|
|
98
|
+
(0, vitest_1.expect)(agents).toEqual({});
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
(0, vitest_1.describe)('getAgentConfig', () => {
|
|
102
|
+
(0, vitest_1.it)('should return null when config file does not exist', () => {
|
|
103
|
+
const agent = configLoader.getAgentConfig('fullstack');
|
|
104
|
+
(0, vitest_1.expect)(agent).toBeNull();
|
|
105
|
+
});
|
|
106
|
+
});
|
|
144
107
|
});
|
|
145
|
-
(0, vitest_1.
|
|
146
|
-
const
|
|
108
|
+
(0, vitest_1.describe)('when config file exists', () => {
|
|
109
|
+
const mockConfig = {
|
|
110
|
+
model: 'custom-model',
|
|
111
|
+
small_model: 'custom-small-model',
|
|
112
|
+
agent: {
|
|
113
|
+
fullstack: {
|
|
114
|
+
model: 'custom-agent-model',
|
|
115
|
+
temperature: 0.5,
|
|
116
|
+
mode: 'primary',
|
|
117
|
+
permission: {
|
|
118
|
+
edit: 'allow',
|
|
119
|
+
bash: 'allow',
|
|
120
|
+
webfetch: 'allow'
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
command: {
|
|
125
|
+
test: {
|
|
126
|
+
description: 'Test command'
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
watcher: {
|
|
130
|
+
ignore: ['custom-ignore']
|
|
131
|
+
},
|
|
132
|
+
provider: {
|
|
147
133
|
berget: {
|
|
148
134
|
models: {
|
|
149
135
|
'custom-model': {
|
|
150
136
|
name: 'Custom Model',
|
|
151
|
-
limit: { output:
|
|
137
|
+
limit: { output: 8000, context: 160000 }
|
|
152
138
|
}
|
|
153
139
|
}
|
|
154
140
|
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
(0, vitest_1.
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
(0, vitest_1.beforeEach)(() => {
|
|
144
|
+
mockFs.existsSync.mockReturnValue(true);
|
|
145
|
+
mockFs.readFileSync.mockReturnValue(JSON.stringify(mockConfig));
|
|
146
|
+
});
|
|
147
|
+
(0, vitest_1.describe)('getModelConfig', () => {
|
|
148
|
+
(0, vitest_1.it)('should return values from config file', () => {
|
|
149
|
+
const modelConfig = configLoader.getModelConfig();
|
|
150
|
+
(0, vitest_1.expect)(modelConfig).toEqual({
|
|
151
|
+
primary: 'custom-model',
|
|
152
|
+
small: 'custom-small-model'
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
(0, vitest_1.describe)('getProviderModels', () => {
|
|
157
|
+
(0, vitest_1.it)('should return models from config file', () => {
|
|
158
|
+
const models = configLoader.getProviderModels();
|
|
159
|
+
(0, vitest_1.expect)(models).toEqual({
|
|
160
|
+
'custom-model': {
|
|
161
|
+
name: 'Custom Model',
|
|
162
|
+
limit: { output: 8000, context: 160000 }
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
(0, vitest_1.describe)('getAllAgentConfigs', () => {
|
|
168
|
+
(0, vitest_1.it)('should return agents from config file', () => {
|
|
169
|
+
const agents = configLoader.getAllAgentConfigs();
|
|
170
|
+
(0, vitest_1.expect)(agents).toEqual(mockConfig.agent);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
(0, vitest_1.describe)('getAgentConfig', () => {
|
|
174
|
+
(0, vitest_1.it)('should return specific agent from config file', () => {
|
|
175
|
+
const agent = configLoader.getAgentConfig('fullstack');
|
|
176
|
+
(0, vitest_1.expect)(agent).toEqual(mockConfig.agent.fullstack);
|
|
177
|
+
});
|
|
178
|
+
(0, vitest_1.it)('should return null for non-existent agent', () => {
|
|
179
|
+
const agent = configLoader.getAgentConfig('nonexistent');
|
|
180
|
+
(0, vitest_1.expect)(agent).toBeNull();
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
(0, vitest_1.describe)('when config file is invalid JSON', () => {
|
|
185
|
+
(0, vitest_1.beforeEach)(() => {
|
|
186
|
+
mockFs.existsSync.mockReturnValue(true);
|
|
187
|
+
mockFs.readFileSync.mockReturnValue('invalid json {');
|
|
188
|
+
});
|
|
189
|
+
(0, vitest_1.it)('should fall back to defaults for getModelConfig', () => {
|
|
190
|
+
const modelConfig = configLoader.getModelConfig();
|
|
191
|
+
(0, vitest_1.expect)(modelConfig).toEqual({
|
|
192
|
+
primary: 'berget/glm-4.7',
|
|
193
|
+
small: 'berget/gpt-oss'
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
(0, vitest_1.it)('should fall back to defaults for getProviderModels', () => {
|
|
197
|
+
const models = configLoader.getProviderModels();
|
|
198
|
+
(0, vitest_1.expect)(models).toEqual({
|
|
199
|
+
'glm-4.7': {
|
|
200
|
+
name: 'GLM-4.7',
|
|
201
|
+
limit: { output: 4000, context: 90000 }
|
|
202
|
+
},
|
|
203
|
+
'gpt-oss': {
|
|
204
|
+
name: 'GPT-OSS',
|
|
205
|
+
limit: { output: 4000, context: 128000 },
|
|
206
|
+
modalities: {
|
|
207
|
+
input: ['text', 'image'],
|
|
208
|
+
output: ['text']
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
'llama-8b': {
|
|
212
|
+
name: 'llama-3.1-8b',
|
|
213
|
+
limit: { output: 4000, context: 128000 }
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
(0, vitest_1.it)('should fall back to defaults for getAllAgentConfigs', () => {
|
|
218
|
+
const agents = configLoader.getAllAgentConfigs();
|
|
219
|
+
(0, vitest_1.expect)(agents).toEqual({});
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
(0, vitest_1.describe)('singleton pattern', () => {
|
|
223
|
+
(0, vitest_1.it)('should return the same instance for same path', () => {
|
|
224
|
+
config_loader_1.ConfigLoader.clearInstance();
|
|
225
|
+
const loader1 = config_loader_1.ConfigLoader.getInstance(testConfigPath);
|
|
226
|
+
const loader2 = config_loader_1.ConfigLoader.getInstance(testConfigPath);
|
|
227
|
+
(0, vitest_1.expect)(loader1).toBe(loader2);
|
|
228
|
+
});
|
|
229
|
+
(0, vitest_1.it)('should return the same instance even for different paths (true singleton)', () => {
|
|
230
|
+
config_loader_1.ConfigLoader.clearInstance();
|
|
231
|
+
const loader1 = config_loader_1.ConfigLoader.getInstance('/path1/config.json');
|
|
232
|
+
const loader2 = config_loader_1.ConfigLoader.getInstance('/path2/config.json');
|
|
233
|
+
// ConfigLoader is a true singleton - it returns the same instance regardless of path
|
|
234
|
+
(0, vitest_1.expect)(loader1).toBe(loader2);
|
|
235
|
+
});
|
|
168
236
|
});
|
|
169
|
-
(0, vitest_1.
|
|
170
|
-
(0,
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
237
|
+
(0, vitest_1.describe)('init scenario regression tests', () => {
|
|
238
|
+
(0, vitest_1.it)('should handle missing config file during init scenario', () => {
|
|
239
|
+
// This test specifically verifies the fix for the init issue
|
|
240
|
+
mockFs.existsSync.mockReturnValue(false);
|
|
241
|
+
// All these methods should work without throwing errors
|
|
242
|
+
(0, vitest_1.expect)(() => configLoader.getModelConfig()).not.toThrow();
|
|
243
|
+
(0, vitest_1.expect)(() => configLoader.getProviderModels()).not.toThrow();
|
|
244
|
+
(0, vitest_1.expect)(() => configLoader.getAllAgentConfigs()).not.toThrow();
|
|
245
|
+
(0, vitest_1.expect)(() => configLoader.getAgentConfig('fullstack')).not.toThrow();
|
|
246
|
+
// And return sensible defaults
|
|
247
|
+
(0, vitest_1.expect)(configLoader.getModelConfig()).toEqual({
|
|
248
|
+
primary: 'berget/glm-4.7',
|
|
249
|
+
small: 'berget/gpt-oss'
|
|
250
|
+
});
|
|
251
|
+
(0, vitest_1.expect)(configLoader.getAllAgentConfigs()).toEqual({});
|
|
252
|
+
(0, vitest_1.expect)(configLoader.getAgentConfig('fullstack')).toBeNull();
|
|
253
|
+
});
|
|
254
|
+
(0, vitest_1.it)('should work with convenience functions during init scenario', () => {
|
|
255
|
+
// This test verifies that convenience functions also work during init
|
|
256
|
+
mockFs.existsSync.mockReturnValue(false);
|
|
257
|
+
(0, vitest_1.expect)(() => (0, config_loader_1.getModelConfig)(testConfigPath)).not.toThrow();
|
|
258
|
+
(0, vitest_1.expect)(() => (0, config_loader_1.getProviderModels)(testConfigPath)).not.toThrow();
|
|
259
|
+
(0, vitest_1.expect)(() => (0, config_loader_1.getAllAgentConfigs)(testConfigPath)).not.toThrow();
|
|
260
|
+
(0, vitest_1.expect)((0, config_loader_1.getModelConfig)(testConfigPath)).toEqual({
|
|
261
|
+
primary: 'berget/glm-4.7',
|
|
262
|
+
small: 'berget/gpt-oss'
|
|
263
|
+
});
|
|
264
|
+
(0, vitest_1.expect)((0, config_loader_1.getAllAgentConfigs)(testConfigPath)).toEqual({});
|
|
265
|
+
});
|
|
181
266
|
});
|
|
182
267
|
});
|