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