berget 2.2.6 → 2.2.8
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/.github/workflows/publish.yml +2 -2
- package/.github/workflows/test.yml +10 -4
- package/.husky/pre-commit +1 -0
- package/.prettierignore +15 -0
- package/.prettierrc +7 -3
- package/CONTRIBUTING.md +38 -0
- package/README.md +2 -148
- package/dist/index.js +10 -11
- package/dist/package.json +30 -2
- package/dist/src/agents/app.js +28 -0
- package/dist/src/agents/backend.js +25 -0
- package/dist/src/agents/devops.js +34 -0
- package/dist/src/agents/frontend.js +25 -0
- package/dist/src/agents/fullstack.js +25 -0
- package/dist/src/agents/index.js +61 -0
- package/dist/src/agents/quality.js +70 -0
- package/dist/src/agents/security.js +26 -0
- package/dist/src/agents/types.js +2 -0
- package/dist/src/client.js +97 -117
- package/dist/src/commands/api-keys.js +75 -90
- package/dist/src/commands/auth.js +7 -16
- package/dist/src/commands/autocomplete.js +1 -1
- package/dist/src/commands/billing.js +6 -17
- package/dist/src/commands/chat.js +68 -101
- package/dist/src/commands/clusters.js +9 -18
- package/dist/src/commands/code/__tests__/auth-sync.test.js +351 -0
- package/dist/src/commands/code/__tests__/fake-api-key-service.js +13 -0
- package/dist/src/commands/code/__tests__/fake-auth-service.js +47 -0
- package/dist/src/commands/code/__tests__/fake-command-runner.js +21 -34
- package/dist/src/commands/code/__tests__/fake-file-store.js +20 -33
- package/dist/src/commands/code/__tests__/fake-prompter.js +83 -57
- package/dist/src/commands/code/__tests__/setup-flow.test.js +359 -92
- package/dist/src/commands/code/adapters/clack-prompter.js +15 -22
- package/dist/src/commands/code/adapters/fs-file-store.js +26 -40
- package/dist/src/commands/code/adapters/spawn-command-runner.js +27 -37
- package/dist/src/commands/code/auth-sync.js +270 -0
- package/dist/src/commands/code/errors.js +12 -9
- package/dist/src/commands/code/ports/auth-services.js +2 -0
- package/dist/src/commands/code/setup.js +387 -281
- package/dist/src/commands/code.js +205 -332
- package/dist/src/commands/index.js +5 -5
- package/dist/src/commands/models.js +6 -17
- package/dist/src/commands/users.js +5 -16
- package/dist/src/constants/command-structure.js +104 -104
- package/dist/src/services/api-key-service.js +132 -157
- package/dist/src/services/auth-service.js +89 -342
- package/dist/src/services/browser-auth.js +268 -0
- package/dist/src/services/chat-service.js +371 -401
- package/dist/src/services/cluster-service.js +47 -62
- package/dist/src/services/collaborator-service.js +10 -25
- package/dist/src/services/flux-service.js +14 -29
- package/dist/src/services/helm-service.js +10 -25
- package/dist/src/services/kubectl-service.js +16 -33
- package/dist/src/utils/config-checker.js +3 -3
- package/dist/src/utils/config-loader.js +95 -95
- package/dist/src/utils/default-api-key.js +124 -134
- package/dist/src/utils/env-manager.js +55 -66
- package/dist/src/utils/error-handler.js +20 -21
- package/dist/src/utils/logger.js +72 -65
- package/dist/src/utils/markdown-renderer.js +27 -27
- package/dist/src/utils/opencode-validator.js +63 -68
- package/dist/src/utils/token-manager.js +74 -45
- package/dist/tests/commands/chat.test.js +16 -25
- package/dist/tests/commands/code.test.js +95 -104
- package/dist/tests/utils/config-loader.test.js +48 -48
- package/dist/tests/utils/env-manager.test.js +43 -52
- package/dist/tests/utils/opencode-validator.test.js +22 -21
- package/dist/vitest.config.js +1 -1
- package/eslint.config.mjs +67 -0
- package/index.ts +35 -42
- package/package.json +30 -2
- package/src/agents/app.ts +27 -0
- package/src/agents/backend.ts +24 -0
- package/src/agents/devops.ts +33 -0
- package/src/agents/frontend.ts +24 -0
- package/src/agents/fullstack.ts +24 -0
- package/src/agents/index.ts +73 -0
- package/src/agents/quality.ts +69 -0
- package/src/agents/security.ts +26 -0
- package/src/agents/types.ts +17 -0
- package/src/client.ts +118 -152
- package/src/commands/api-keys.ts +241 -333
- package/src/commands/auth.ts +22 -27
- package/src/commands/autocomplete.ts +9 -9
- package/src/commands/billing.ts +20 -24
- package/src/commands/chat.ts +248 -338
- package/src/commands/clusters.ts +27 -26
- package/src/commands/code/__tests__/auth-sync.test.ts +482 -0
- package/src/commands/code/__tests__/fake-api-key-service.ts +13 -0
- package/src/commands/code/__tests__/fake-auth-service.ts +50 -0
- package/src/commands/code/__tests__/fake-command-runner.ts +45 -42
- package/src/commands/code/__tests__/fake-file-store.ts +32 -23
- package/src/commands/code/__tests__/fake-prompter.ts +116 -77
- package/src/commands/code/__tests__/setup-flow.test.ts +624 -268
- package/src/commands/code/adapters/clack-prompter.ts +53 -39
- package/src/commands/code/adapters/fs-file-store.ts +32 -27
- package/src/commands/code/adapters/spawn-command-runner.ts +38 -29
- package/src/commands/code/auth-sync.ts +329 -0
- package/src/commands/code/errors.ts +18 -18
- package/src/commands/code/ports/auth-services.ts +14 -0
- package/src/commands/code/ports/command-runner.ts +8 -4
- package/src/commands/code/ports/file-store.ts +5 -4
- package/src/commands/code/ports/prompter.ts +24 -18
- package/src/commands/code/setup.ts +570 -340
- package/src/commands/code.ts +338 -539
- package/src/commands/index.ts +20 -19
- package/src/commands/models.ts +28 -32
- package/src/commands/users.ts +15 -21
- package/src/constants/command-structure.ts +134 -157
- package/src/services/api-key-service.ts +105 -122
- package/src/services/auth-service.ts +99 -345
- package/src/services/browser-auth.ts +296 -0
- package/src/services/chat-service.ts +265 -299
- package/src/services/cluster-service.ts +42 -45
- package/src/services/collaborator-service.ts +14 -19
- package/src/services/flux-service.ts +23 -25
- package/src/services/helm-service.ts +19 -21
- package/src/services/kubectl-service.ts +17 -19
- package/src/types/api.d.ts +1905 -1907
- package/src/types/json.d.ts +2 -2
- package/src/utils/config-checker.ts +10 -10
- package/src/utils/config-loader.ts +162 -178
- package/src/utils/default-api-key.ts +114 -125
- package/src/utils/env-manager.ts +53 -57
- package/src/utils/error-handler.ts +61 -56
- package/src/utils/logger.ts +79 -73
- package/src/utils/markdown-renderer.ts +31 -31
- package/src/utils/opencode-validator.ts +85 -89
- package/src/utils/token-manager.ts +108 -87
- package/templates/agents/app.md +1 -0
- package/templates/agents/backend.md +1 -0
- package/templates/agents/devops.md +2 -0
- package/templates/agents/frontend.md +1 -0
- package/templates/agents/fullstack.md +1 -0
- package/templates/agents/quality.md +45 -40
- package/templates/agents/security.md +1 -0
- package/tests/commands/chat.test.ts +53 -62
- package/tests/commands/code.test.ts +265 -310
- package/tests/utils/config-loader.test.ts +189 -188
- package/tests/utils/env-manager.test.ts +110 -113
- package/tests/utils/opencode-validator.test.ts +52 -56
- package/tsconfig.json +4 -3
- package/vitest.config.ts +3 -3
- package/AGENTS.md +0 -374
- package/TODO.md +0 -19
|
@@ -1,320 +1,321 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
ConfigLoader,
|
|
5
|
+
getAllAgentConfigs,
|
|
6
|
+
getModelConfig,
|
|
7
|
+
getProviderModels,
|
|
8
|
+
} from '../../src/utils/config-loader';
|
|
8
9
|
|
|
9
10
|
// Mock fs module
|
|
10
11
|
const mockFs = vi.hoisted(() => ({
|
|
11
12
|
existsSync: vi.fn(),
|
|
13
|
+
mkdirSync: vi.fn(),
|
|
12
14
|
readFileSync: vi.fn(),
|
|
13
15
|
writeFileSync: vi.fn(),
|
|
14
|
-
|
|
15
|
-
}))
|
|
16
|
+
}));
|
|
16
17
|
|
|
17
|
-
vi.mock('fs', () => mockFs)
|
|
18
|
+
vi.mock('fs', () => mockFs);
|
|
18
19
|
|
|
19
20
|
describe('ConfigLoader', () => {
|
|
20
|
-
const testConfigPath = '/tmp/test-opencode.json'
|
|
21
|
-
let configLoader: ConfigLoader
|
|
21
|
+
const testConfigPath = '/tmp/test-opencode.json';
|
|
22
|
+
let configLoader: ConfigLoader;
|
|
22
23
|
|
|
23
24
|
beforeEach(() => {
|
|
24
25
|
// Reset mocks and clear singleton
|
|
25
|
-
vi.clearAllMocks()
|
|
26
|
-
ConfigLoader.clearInstance()
|
|
27
|
-
|
|
26
|
+
vi.clearAllMocks();
|
|
27
|
+
ConfigLoader.clearInstance();
|
|
28
|
+
|
|
28
29
|
// Create new instance for each test using getInstance
|
|
29
|
-
configLoader = ConfigLoader.getInstance(testConfigPath)
|
|
30
|
-
})
|
|
30
|
+
configLoader = ConfigLoader.getInstance(testConfigPath);
|
|
31
|
+
});
|
|
31
32
|
|
|
32
33
|
afterEach(() => {
|
|
33
|
-
vi.clearAllMocks()
|
|
34
|
-
ConfigLoader.clearInstance()
|
|
35
|
-
})
|
|
34
|
+
vi.clearAllMocks();
|
|
35
|
+
ConfigLoader.clearInstance();
|
|
36
|
+
});
|
|
36
37
|
|
|
37
38
|
describe('when config file does not exist', () => {
|
|
38
39
|
beforeEach(() => {
|
|
39
|
-
mockFs.existsSync.mockReturnValue(false)
|
|
40
|
-
})
|
|
40
|
+
mockFs.existsSync.mockReturnValue(false);
|
|
41
|
+
});
|
|
41
42
|
|
|
42
43
|
describe('getModelConfig', () => {
|
|
43
44
|
it('should return default values when config file does not exist', () => {
|
|
44
|
-
const modelConfig = configLoader.getModelConfig()
|
|
45
|
-
|
|
45
|
+
const modelConfig = configLoader.getModelConfig();
|
|
46
|
+
|
|
46
47
|
expect(modelConfig).toEqual({
|
|
47
48
|
primary: 'berget/glm-4.7',
|
|
48
|
-
small: 'berget/gpt-oss'
|
|
49
|
-
})
|
|
50
|
-
})
|
|
49
|
+
small: 'berget/gpt-oss',
|
|
50
|
+
});
|
|
51
|
+
});
|
|
51
52
|
|
|
52
53
|
it('should return default values when using convenience function', () => {
|
|
53
|
-
const modelConfig = getModelConfig(testConfigPath)
|
|
54
|
-
|
|
54
|
+
const modelConfig = getModelConfig(testConfigPath);
|
|
55
|
+
|
|
55
56
|
expect(modelConfig).toEqual({
|
|
56
57
|
primary: 'berget/glm-4.7',
|
|
57
|
-
small: 'berget/gpt-oss'
|
|
58
|
-
})
|
|
59
|
-
})
|
|
60
|
-
})
|
|
58
|
+
small: 'berget/gpt-oss',
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
});
|
|
61
62
|
|
|
62
63
|
describe('getProviderModels', () => {
|
|
63
64
|
it('should return default provider models when config file does not exist', () => {
|
|
64
|
-
const models = configLoader.getProviderModels()
|
|
65
|
-
|
|
65
|
+
const models = configLoader.getProviderModels();
|
|
66
|
+
|
|
66
67
|
expect(models).toEqual({
|
|
67
68
|
'glm-4.7': {
|
|
69
|
+
limit: { context: 90_000, output: 4000 },
|
|
68
70
|
name: 'GLM-4.7',
|
|
69
|
-
limit: { output: 4000, context: 90000 }
|
|
70
71
|
},
|
|
71
72
|
'gpt-oss': {
|
|
72
|
-
|
|
73
|
-
limit: { output: 4000, context: 128000 },
|
|
73
|
+
limit: { context: 128_000, output: 4000 },
|
|
74
74
|
modalities: {
|
|
75
75
|
input: ['text', 'image'],
|
|
76
|
-
output: ['text']
|
|
77
|
-
}
|
|
76
|
+
output: ['text'],
|
|
77
|
+
},
|
|
78
|
+
name: 'GPT-OSS',
|
|
78
79
|
},
|
|
79
80
|
'llama-8b': {
|
|
81
|
+
limit: { context: 128_000, output: 4000 },
|
|
80
82
|
name: 'llama-3.1-8b',
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
})
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
});
|
|
85
86
|
|
|
86
87
|
it('should return default provider models when using convenience function', () => {
|
|
87
|
-
const models = getProviderModels(testConfigPath)
|
|
88
|
-
|
|
88
|
+
const models = getProviderModels(testConfigPath);
|
|
89
|
+
|
|
89
90
|
expect(models).toEqual({
|
|
90
91
|
'glm-4.7': {
|
|
92
|
+
limit: { context: 90_000, output: 4000 },
|
|
91
93
|
name: 'GLM-4.7',
|
|
92
|
-
limit: { output: 4000, context: 90000 }
|
|
93
94
|
},
|
|
94
95
|
'gpt-oss': {
|
|
95
|
-
|
|
96
|
-
limit: { output: 4000, context: 128000 },
|
|
96
|
+
limit: { context: 128_000, output: 4000 },
|
|
97
97
|
modalities: {
|
|
98
98
|
input: ['text', 'image'],
|
|
99
|
-
output: ['text']
|
|
100
|
-
}
|
|
99
|
+
output: ['text'],
|
|
100
|
+
},
|
|
101
|
+
name: 'GPT-OSS',
|
|
101
102
|
},
|
|
102
103
|
'llama-8b': {
|
|
104
|
+
limit: { context: 128_000, output: 4000 },
|
|
103
105
|
name: 'llama-3.1-8b',
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
})
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
});
|
|
109
110
|
|
|
110
111
|
describe('getAllAgentConfigs', () => {
|
|
111
112
|
it('should return empty object when config file does not exist', () => {
|
|
112
|
-
const agents = configLoader.getAllAgentConfigs()
|
|
113
|
-
|
|
114
|
-
expect(agents).toEqual({})
|
|
115
|
-
})
|
|
113
|
+
const agents = configLoader.getAllAgentConfigs();
|
|
114
|
+
|
|
115
|
+
expect(agents).toEqual({});
|
|
116
|
+
});
|
|
116
117
|
|
|
117
118
|
it('should return empty object when using convenience function', () => {
|
|
118
|
-
const agents = getAllAgentConfigs(testConfigPath)
|
|
119
|
-
|
|
120
|
-
expect(agents).toEqual({})
|
|
121
|
-
})
|
|
122
|
-
})
|
|
119
|
+
const agents = getAllAgentConfigs(testConfigPath);
|
|
120
|
+
|
|
121
|
+
expect(agents).toEqual({});
|
|
122
|
+
});
|
|
123
|
+
});
|
|
123
124
|
|
|
124
125
|
describe('getAgentConfig', () => {
|
|
125
126
|
it('should return null when config file does not exist', () => {
|
|
126
|
-
const agent = configLoader.getAgentConfig('fullstack')
|
|
127
|
-
|
|
128
|
-
expect(agent).toBeNull()
|
|
129
|
-
})
|
|
130
|
-
})
|
|
131
|
-
})
|
|
127
|
+
const agent = configLoader.getAgentConfig('fullstack');
|
|
128
|
+
|
|
129
|
+
expect(agent).toBeNull();
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
});
|
|
132
133
|
|
|
133
134
|
describe('when config file exists', () => {
|
|
134
135
|
const mockConfig = {
|
|
135
|
-
model: 'custom-model',
|
|
136
|
-
small_model: 'custom-small-model',
|
|
137
136
|
agent: {
|
|
138
137
|
fullstack: {
|
|
139
|
-
model: 'custom-agent-model',
|
|
140
|
-
temperature: 0.5,
|
|
141
138
|
mode: 'primary' as const,
|
|
139
|
+
model: 'custom-agent-model',
|
|
142
140
|
permission: {
|
|
143
|
-
edit: 'allow' as const,
|
|
144
141
|
bash: 'allow' as const,
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
142
|
+
edit: 'allow' as const,
|
|
143
|
+
webfetch: 'allow' as const,
|
|
144
|
+
},
|
|
145
|
+
temperature: 0.5,
|
|
146
|
+
},
|
|
148
147
|
},
|
|
149
148
|
command: {
|
|
150
149
|
test: {
|
|
151
|
-
description: 'Test command'
|
|
152
|
-
}
|
|
153
|
-
},
|
|
154
|
-
watcher: {
|
|
155
|
-
ignore: ['custom-ignore']
|
|
150
|
+
description: 'Test command',
|
|
151
|
+
},
|
|
156
152
|
},
|
|
153
|
+
model: 'custom-model',
|
|
157
154
|
provider: {
|
|
158
155
|
berget: {
|
|
159
156
|
models: {
|
|
160
157
|
'custom-model': {
|
|
158
|
+
limit: { context: 160_000, output: 8000 },
|
|
161
159
|
name: 'Custom Model',
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
small_model: 'custom-small-model',
|
|
165
|
+
watcher: {
|
|
166
|
+
ignore: ['custom-ignore'],
|
|
167
|
+
},
|
|
168
|
+
};
|
|
168
169
|
|
|
169
170
|
beforeEach(() => {
|
|
170
|
-
mockFs.existsSync.mockReturnValue(true)
|
|
171
|
-
mockFs.readFileSync.mockReturnValue(JSON.stringify(mockConfig))
|
|
172
|
-
})
|
|
171
|
+
mockFs.existsSync.mockReturnValue(true);
|
|
172
|
+
mockFs.readFileSync.mockReturnValue(JSON.stringify(mockConfig));
|
|
173
|
+
});
|
|
173
174
|
|
|
174
175
|
describe('getModelConfig', () => {
|
|
175
176
|
it('should return values from config file', () => {
|
|
176
|
-
const modelConfig = configLoader.getModelConfig()
|
|
177
|
-
|
|
177
|
+
const modelConfig = configLoader.getModelConfig();
|
|
178
|
+
|
|
178
179
|
expect(modelConfig).toEqual({
|
|
179
180
|
primary: 'custom-model',
|
|
180
|
-
small: 'custom-small-model'
|
|
181
|
-
})
|
|
182
|
-
})
|
|
183
|
-
})
|
|
181
|
+
small: 'custom-small-model',
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
});
|
|
184
185
|
|
|
185
186
|
describe('getProviderModels', () => {
|
|
186
187
|
it('should return models from config file', () => {
|
|
187
|
-
const models = configLoader.getProviderModels()
|
|
188
|
-
|
|
188
|
+
const models = configLoader.getProviderModels();
|
|
189
|
+
|
|
189
190
|
expect(models).toEqual({
|
|
190
191
|
'custom-model': {
|
|
192
|
+
limit: { context: 160_000, output: 8000 },
|
|
191
193
|
name: 'Custom Model',
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
})
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
});
|
|
197
198
|
|
|
198
199
|
describe('getAllAgentConfigs', () => {
|
|
199
200
|
it('should return agents from config file', () => {
|
|
200
|
-
const agents = configLoader.getAllAgentConfigs()
|
|
201
|
-
|
|
202
|
-
expect(agents).toEqual(mockConfig.agent)
|
|
203
|
-
})
|
|
204
|
-
})
|
|
201
|
+
const agents = configLoader.getAllAgentConfigs();
|
|
202
|
+
|
|
203
|
+
expect(agents).toEqual(mockConfig.agent);
|
|
204
|
+
});
|
|
205
|
+
});
|
|
205
206
|
|
|
206
207
|
describe('getAgentConfig', () => {
|
|
207
208
|
it('should return specific agent from config file', () => {
|
|
208
|
-
const agent = configLoader.getAgentConfig('fullstack')
|
|
209
|
-
|
|
210
|
-
expect(agent).toEqual(mockConfig.agent.fullstack)
|
|
211
|
-
})
|
|
209
|
+
const agent = configLoader.getAgentConfig('fullstack');
|
|
210
|
+
|
|
211
|
+
expect(agent).toEqual(mockConfig.agent.fullstack);
|
|
212
|
+
});
|
|
212
213
|
|
|
213
214
|
it('should return null for non-existent agent', () => {
|
|
214
|
-
const agent = configLoader.getAgentConfig('nonexistent')
|
|
215
|
-
|
|
216
|
-
expect(agent).toBeNull()
|
|
217
|
-
})
|
|
218
|
-
})
|
|
219
|
-
})
|
|
215
|
+
const agent = configLoader.getAgentConfig('nonexistent');
|
|
216
|
+
|
|
217
|
+
expect(agent).toBeNull();
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
});
|
|
220
221
|
|
|
221
222
|
describe('when config file is invalid JSON', () => {
|
|
222
223
|
beforeEach(() => {
|
|
223
|
-
mockFs.existsSync.mockReturnValue(true)
|
|
224
|
-
mockFs.readFileSync.mockReturnValue('invalid json {')
|
|
225
|
-
})
|
|
224
|
+
mockFs.existsSync.mockReturnValue(true);
|
|
225
|
+
mockFs.readFileSync.mockReturnValue('invalid json {');
|
|
226
|
+
});
|
|
226
227
|
|
|
227
228
|
it('should fall back to defaults for getModelConfig', () => {
|
|
228
|
-
const modelConfig = configLoader.getModelConfig()
|
|
229
|
-
|
|
230
|
-
expect(modelConfig).toEqual({
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
})
|
|
229
|
+
const modelConfig = configLoader.getModelConfig();
|
|
230
|
+
|
|
231
|
+
expect(modelConfig).toEqual({
|
|
232
|
+
primary: 'berget/glm-4.7',
|
|
233
|
+
small: 'berget/gpt-oss',
|
|
234
|
+
});
|
|
235
|
+
});
|
|
235
236
|
|
|
236
237
|
it('should fall back to defaults for getProviderModels', () => {
|
|
237
|
-
const models = configLoader.getProviderModels()
|
|
238
|
-
|
|
239
|
-
expect(models).toEqual({
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
238
|
+
const models = configLoader.getProviderModels();
|
|
239
|
+
|
|
240
|
+
expect(models).toEqual({
|
|
241
|
+
'glm-4.7': {
|
|
242
|
+
limit: { context: 90_000, output: 4000 },
|
|
243
|
+
name: 'GLM-4.7',
|
|
244
|
+
},
|
|
244
245
|
'gpt-oss': {
|
|
245
|
-
|
|
246
|
-
limit: { output: 4000, context: 128000 },
|
|
246
|
+
limit: { context: 128_000, output: 4000 },
|
|
247
247
|
modalities: {
|
|
248
248
|
input: ['text', 'image'],
|
|
249
|
-
output: ['text']
|
|
250
|
-
}
|
|
249
|
+
output: ['text'],
|
|
250
|
+
},
|
|
251
|
+
name: 'GPT-OSS',
|
|
251
252
|
},
|
|
252
253
|
'llama-8b': {
|
|
254
|
+
limit: { context: 128_000, output: 4000 },
|
|
253
255
|
name: 'llama-3.1-8b',
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
})
|
|
256
|
+
},
|
|
257
|
+
});
|
|
258
|
+
});
|
|
258
259
|
|
|
259
260
|
it('should fall back to defaults for getAllAgentConfigs', () => {
|
|
260
|
-
const agents = configLoader.getAllAgentConfigs()
|
|
261
|
-
|
|
262
|
-
expect(agents).toEqual({})
|
|
263
|
-
})
|
|
264
|
-
})
|
|
261
|
+
const agents = configLoader.getAllAgentConfigs();
|
|
262
|
+
|
|
263
|
+
expect(agents).toEqual({});
|
|
264
|
+
});
|
|
265
|
+
});
|
|
265
266
|
|
|
266
267
|
describe('singleton pattern', () => {
|
|
267
268
|
it('should return the same instance for same path', () => {
|
|
268
|
-
ConfigLoader.clearInstance()
|
|
269
|
-
const loader1 = ConfigLoader.getInstance(testConfigPath)
|
|
270
|
-
const loader2 = ConfigLoader.getInstance(testConfigPath)
|
|
271
|
-
|
|
272
|
-
expect(loader1).toBe(loader2)
|
|
273
|
-
})
|
|
269
|
+
ConfigLoader.clearInstance();
|
|
270
|
+
const loader1 = ConfigLoader.getInstance(testConfigPath);
|
|
271
|
+
const loader2 = ConfigLoader.getInstance(testConfigPath);
|
|
272
|
+
|
|
273
|
+
expect(loader1).toBe(loader2);
|
|
274
|
+
});
|
|
274
275
|
|
|
275
276
|
it('should return the same instance even for different paths (true singleton)', () => {
|
|
276
|
-
ConfigLoader.clearInstance()
|
|
277
|
-
const loader1 = ConfigLoader.getInstance('/path1/config.json')
|
|
278
|
-
const loader2 = ConfigLoader.getInstance('/path2/config.json')
|
|
279
|
-
|
|
277
|
+
ConfigLoader.clearInstance();
|
|
278
|
+
const loader1 = ConfigLoader.getInstance('/path1/config.json');
|
|
279
|
+
const loader2 = ConfigLoader.getInstance('/path2/config.json');
|
|
280
|
+
|
|
280
281
|
// ConfigLoader is a true singleton - it returns the same instance regardless of path
|
|
281
|
-
expect(loader1).toBe(loader2)
|
|
282
|
-
})
|
|
283
|
-
})
|
|
282
|
+
expect(loader1).toBe(loader2);
|
|
283
|
+
});
|
|
284
|
+
});
|
|
284
285
|
|
|
285
286
|
describe('init scenario regression tests', () => {
|
|
286
287
|
it('should handle missing config file during init scenario', () => {
|
|
287
288
|
// This test specifically verifies the fix for the init issue
|
|
288
|
-
mockFs.existsSync.mockReturnValue(false)
|
|
289
|
-
|
|
289
|
+
mockFs.existsSync.mockReturnValue(false);
|
|
290
|
+
|
|
290
291
|
// All these methods should work without throwing errors
|
|
291
|
-
expect(() => configLoader.getModelConfig()).not.toThrow()
|
|
292
|
-
expect(() => configLoader.getProviderModels()).not.toThrow()
|
|
293
|
-
expect(() => configLoader.getAllAgentConfigs()).not.toThrow()
|
|
294
|
-
expect(() => configLoader.getAgentConfig('fullstack')).not.toThrow()
|
|
295
|
-
|
|
292
|
+
expect(() => configLoader.getModelConfig()).not.toThrow();
|
|
293
|
+
expect(() => configLoader.getProviderModels()).not.toThrow();
|
|
294
|
+
expect(() => configLoader.getAllAgentConfigs()).not.toThrow();
|
|
295
|
+
expect(() => configLoader.getAgentConfig('fullstack')).not.toThrow();
|
|
296
|
+
|
|
296
297
|
// And return sensible defaults
|
|
297
298
|
expect(configLoader.getModelConfig()).toEqual({
|
|
298
299
|
primary: 'berget/glm-4.7',
|
|
299
|
-
small: 'berget/gpt-oss'
|
|
300
|
-
})
|
|
301
|
-
expect(configLoader.getAllAgentConfigs()).toEqual({})
|
|
302
|
-
expect(configLoader.getAgentConfig('fullstack')).toBeNull()
|
|
303
|
-
})
|
|
300
|
+
small: 'berget/gpt-oss',
|
|
301
|
+
});
|
|
302
|
+
expect(configLoader.getAllAgentConfigs()).toEqual({});
|
|
303
|
+
expect(configLoader.getAgentConfig('fullstack')).toBeNull();
|
|
304
|
+
});
|
|
304
305
|
|
|
305
306
|
it('should work with convenience functions during init scenario', () => {
|
|
306
307
|
// This test verifies that convenience functions also work during init
|
|
307
|
-
mockFs.existsSync.mockReturnValue(false)
|
|
308
|
-
|
|
309
|
-
expect(() => getModelConfig(testConfigPath)).not.toThrow()
|
|
310
|
-
expect(() => getProviderModels(testConfigPath)).not.toThrow()
|
|
311
|
-
expect(() => getAllAgentConfigs(testConfigPath)).not.toThrow()
|
|
312
|
-
|
|
308
|
+
mockFs.existsSync.mockReturnValue(false);
|
|
309
|
+
|
|
310
|
+
expect(() => getModelConfig(testConfigPath)).not.toThrow();
|
|
311
|
+
expect(() => getProviderModels(testConfigPath)).not.toThrow();
|
|
312
|
+
expect(() => getAllAgentConfigs(testConfigPath)).not.toThrow();
|
|
313
|
+
|
|
313
314
|
expect(getModelConfig(testConfigPath)).toEqual({
|
|
314
315
|
primary: 'berget/glm-4.7',
|
|
315
|
-
small: 'berget/gpt-oss'
|
|
316
|
-
})
|
|
317
|
-
expect(getAllAgentConfigs(testConfigPath)).toEqual({})
|
|
318
|
-
})
|
|
319
|
-
})
|
|
320
|
-
})
|
|
316
|
+
small: 'berget/gpt-oss',
|
|
317
|
+
});
|
|
318
|
+
expect(getAllAgentConfigs(testConfigPath)).toEqual({});
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
});
|