berget 2.0.6 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,320 @@
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
+
9
+ // Mock fs module
10
+ const mockFs = vi.hoisted(() => ({
11
+ existsSync: vi.fn(),
12
+ readFileSync: vi.fn(),
13
+ writeFileSync: vi.fn(),
14
+ mkdirSync: vi.fn(),
15
+ }))
16
+
17
+ vi.mock('fs', () => mockFs)
18
+
19
+ describe('ConfigLoader', () => {
20
+ const testConfigPath = '/tmp/test-opencode.json'
21
+ let configLoader: ConfigLoader
22
+
23
+ beforeEach(() => {
24
+ // Reset mocks and clear singleton
25
+ vi.clearAllMocks()
26
+ ConfigLoader.clearInstance()
27
+
28
+ // Create new instance for each test using getInstance
29
+ configLoader = ConfigLoader.getInstance(testConfigPath)
30
+ })
31
+
32
+ afterEach(() => {
33
+ vi.clearAllMocks()
34
+ ConfigLoader.clearInstance()
35
+ })
36
+
37
+ describe('when config file does not exist', () => {
38
+ beforeEach(() => {
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()
45
+
46
+ expect(modelConfig).toEqual({
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
+ expect(modelConfig).toEqual({
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
+ expect(models).toEqual({
67
+ 'glm-4.7': {
68
+ name: 'GLM-4.7',
69
+ limit: { output: 4000, context: 90000 }
70
+ },
71
+ 'gpt-oss': {
72
+ name: 'GPT-OSS',
73
+ limit: { output: 4000, context: 128000 },
74
+ modalities: {
75
+ input: ['text', 'image'],
76
+ output: ['text']
77
+ }
78
+ },
79
+ 'llama-8b': {
80
+ name: 'llama-3.1-8b',
81
+ limit: { output: 4000, context: 128000 }
82
+ }
83
+ })
84
+ })
85
+
86
+ it('should return default provider models when using convenience function', () => {
87
+ const models = getProviderModels(testConfigPath)
88
+
89
+ expect(models).toEqual({
90
+ 'glm-4.7': {
91
+ name: 'GLM-4.7',
92
+ limit: { output: 4000, context: 90000 }
93
+ },
94
+ 'gpt-oss': {
95
+ name: 'GPT-OSS',
96
+ limit: { output: 4000, context: 128000 },
97
+ modalities: {
98
+ input: ['text', 'image'],
99
+ output: ['text']
100
+ }
101
+ },
102
+ 'llama-8b': {
103
+ name: 'llama-3.1-8b',
104
+ limit: { output: 4000, context: 128000 }
105
+ }
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
+ const mockConfig = {
135
+ model: 'custom-model',
136
+ small_model: 'custom-small-model',
137
+ agent: {
138
+ fullstack: {
139
+ model: 'custom-agent-model',
140
+ temperature: 0.5,
141
+ mode: 'primary' as const,
142
+ permission: {
143
+ edit: 'allow' as const,
144
+ bash: 'allow' as const,
145
+ webfetch: 'allow' as const
146
+ }
147
+ }
148
+ },
149
+ command: {
150
+ test: {
151
+ description: 'Test command'
152
+ }
153
+ },
154
+ watcher: {
155
+ ignore: ['custom-ignore']
156
+ },
157
+ provider: {
158
+ berget: {
159
+ models: {
160
+ 'custom-model': {
161
+ name: 'Custom Model',
162
+ limit: { output: 8000, context: 160000 }
163
+ }
164
+ }
165
+ }
166
+ }
167
+ }
168
+
169
+ beforeEach(() => {
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
+ expect(modelConfig).toEqual({
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
+ expect(models).toEqual({
190
+ 'custom-model': {
191
+ name: 'Custom Model',
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 }
243
+ },
244
+ 'gpt-oss': {
245
+ name: 'GPT-OSS',
246
+ limit: { output: 4000, context: 128000 },
247
+ modalities: {
248
+ input: ['text', 'image'],
249
+ output: ['text']
250
+ }
251
+ },
252
+ 'llama-8b': {
253
+ name: 'llama-3.1-8b',
254
+ limit: { output: 4000, context: 128000 }
255
+ }
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
+ // ConfigLoader is a true singleton - it returns the same instance regardless of path
281
+ expect(loader1).toBe(loader2)
282
+ })
283
+ })
284
+
285
+ describe('init scenario regression tests', () => {
286
+ it('should handle missing config file during init scenario', () => {
287
+ // This test specifically verifies the fix for the init issue
288
+ mockFs.existsSync.mockReturnValue(false)
289
+
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('fullstack')).not.toThrow()
295
+
296
+ // And return sensible defaults
297
+ expect(configLoader.getModelConfig()).toEqual({
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
+ // 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
+
313
+ expect(getModelConfig(testConfigPath)).toEqual({
314
+ primary: 'berget/glm-4.7',
315
+ small: 'berget/gpt-oss'
316
+ })
317
+ expect(getAllAgentConfigs(testConfigPath)).toEqual({})
318
+ })
319
+ })
320
+ })
package/blog-post.md DELETED
@@ -1,176 +0,0 @@
1
- # OpenCode + Berget AI: Den ultimata AI-kodassistensen för svenska företag
2
-
3
- I en värld där AI-drivna kodassistenter blir standard, står svenska företag inför ett kritiskt val: hur man balanserar produktivitetsvinster med data-suveränitet och efterlevnad. Med integrationen mellan OpenCode och Berget AI får du nu det bästa av två världar – en kraftfull, open source AI-kodassistent med svensk datainfrastruktur.
4
-
5
- ## Vad är OpenCode?
6
-
7
- OpenCode är en open source AI-kodassistent byggd för terminalen. Till skillnad från många kommersiella alternativ ger OpenCode dig full kontroll över din kod och dina data. Med över 26 000 GitHub-stjärnor och 200 000+ aktiva utvecklare varje månad har det blivit ett av de mest populära verktygen för AI-assisterad programmering.
8
-
9
- **Nödvändiga funktioner i OpenCode:**
10
-
11
- - 🎯 **Native TUI** – Responsivt terminalgränssnitt som fungerar i din befintliga workflow
12
- - 🔄 **Multi-session** – Köra flera agenter parallellt på samma projekt
13
- - 🔗 **Share links** – Dela sessioner för kodgranskning och felsökning
14
- - 🤖 **Any model** – Stöd för 75+ LLM-providers via Models.dev
15
- - 🛠️ **LSP enabled** – Automatisk laddning av rätt Language Servers för LLM:en
16
-
17
- ## Berget AI: Svensk datainfrastruktur
18
-
19
- Berget AI är en svensk AI-infrastrukturleverantör som säkerställer att din data aldrig lämnar Sverige. Detta är särskilt viktigt för företag som hanterar känslig information, personuppgifter eller skyddad kod.
20
-
21
- **Fördelar med Berget AI:**
22
-
23
- - 🇸🇪 **Data-suveränitet** – All data bearbetas inom Sverige
24
- - ⚖️ **GDPR-kompatibilitet** – Full efterlevnad med EU:s dataskyddslagar
25
- - 🔒 **Säkerhet** – Industriell säkerhet med kryptering och isolering
26
- - 🏛️ **Reglering** – Enklare efterlevnad av svenska och EU-regleringar
27
- - 🚀 **Prestanda** – Låg latens med svensk infrastruktur
28
-
29
- ## Varför kombinationen är oslagbar
30
-
31
- ### 1. Data som aldrig lämnar Sverige
32
-
33
- När du använder OpenCode med Berget AI förblir all din kod – inklusive API-nycklar, konfigurationer och känslig affärslogik – inom Sveriges gränser. Detta eliminerar risken för dataexponering som kan uppstå med internationella molntjänster.
34
-
35
- ```bash
36
- # Initiera ditt projekt med AI-assistent
37
- berget code init
38
-
39
- # Din kod och data förblir alltid i Sverige ✅
40
- ```
41
-
42
- ### 2. Förenklad reglering och efterlevnad
43
-
44
- För svenska företag, särskilt inom finans, vård och offentlig sektor, är efterlevnad avgörande. Med Berget AI får du:
45
-
46
- - **GDPR-säker hantering** av personuppgifter i koden
47
- - **Klassificerad information** hanteras säkert
48
- - **Revisionsbara spår** av all AI-interaktion
49
- - **Lokal lagring** av konfigurationer och API-nycklar
50
-
51
- ### 3. Säker hantering av känslig kod
52
-
53
- Många företag arbetar med kod som innehåller:
54
-
55
- - API-nycklar och hemligheter
56
- - Affärskritisk algoritmlogik
57
- - Känslig kunddata
58
- - Proprietära systemintegrationer
59
-
60
- Med OpenCode + Berget AI kan du använda AI-assistens för:
61
-
62
- ```bash
63
- # Refaktorera känslig kod utan att lämna Sverige
64
- berget code run "Refaktorera denna autentiseringsmodul"
65
-
66
- # Få hjälp med API-integrationer säkert
67
- berget code run "Optimera denna databasanslutning"
68
- ```
69
-
70
- ### 4. Projektspecifika API-nycklar
71
-
72
- Varje projekt får sin egen unika API-nyckel som skapas automatiskt:
73
-
74
- ```json
75
- {
76
- "model": "berget/deepseek-r1",
77
- "apiKey": "projekt-specifik-nyckel",
78
- "projectName": "mitt-svenska-projekt",
79
- "provider": "berget",
80
- "created": "2025-01-01T00:00:00.000Z",
81
- "version": "1.0.0"
82
- }
83
- ```
84
-
85
- Detta ger:
86
-
87
- - 🔐 **Isolering** mellan projekt
88
- - 📊 **Spårbarhet** av användning
89
- - 🔄 **Enkel rotation** av nycklar
90
- - 🎯 **Granulär kontroll** av åtkomst
91
-
92
- ## Kom igång på 2 minuter
93
-
94
- ### Steg 1: Installera OpenCode
95
-
96
- ```bash
97
- curl -fsSL https://opencode.ai/install | bash
98
- ```
99
-
100
- ### Steg 2: Initiera ditt projekt
101
-
102
- ```bash
103
- cd ditt-projekt
104
- berget code init
105
- ```
106
-
107
- ### Steg 3: Starta AI-assistenen
108
-
109
- ```bash
110
- berget code run
111
- ```
112
-
113
- Det var allt! Du har nu en fullfjädrad AI-kodassistent med svensk datainfrastruktur.
114
-
115
- ## Användningsfall för svenska företag
116
-
117
- ### Finans & Försäkring
118
-
119
- - Refaktorera transaktionslogik med full säkerhet
120
- - Hjälp med compliance-kod (KYC, AML)
121
- - Optimera riskberäkningsalgoritmer
122
-
123
- ### Vård & Hälsa
124
-
125
- - Utveckla patientdata-system med GDPR-säkerhet
126
- - Hjälp med medicinsk kod och integrationer
127
- - Säker hantering av journalsystem
128
-
129
- ### Offentlig Sektor
130
-
131
- - Utveckla e-tjänster med svensk data
132
- - Hjälp med myndighetsintegrationer
133
- - Säker hantering av medborgardata
134
-
135
- ### Industri & Tillverkning
136
-
137
- - Optimera PLC-kod och styrsystem
138
- - Hjälp med IoT-integrationer
139
- - Säker utveckling av produktionssystem
140
-
141
- ## Teknisk integration
142
-
143
- OpenCode + Berget AI använder den kraftfulla **GLM-4.6** modellen (för närvarande deepseek-r1) som är optimerad för kodning:
144
-
145
- ```bash
146
- # Se vilka modeller som finns tillgängliga
147
- berget models list
148
-
149
- # Använd specifik modell
150
- berget code run --model berget/glm-4-6 "Hjälp mig optimera denna funktion"
151
- ```
152
-
153
- ## Framtiden för svensk AI-utveckling
154
-
155
- Kombinationen av OpenCode och Berget AI representerar nästa generations AI-utveckling för svenska företag:
156
-
157
- 1. **Produktivitet** – AI-assisterad kodning utan kompromisser
158
- 2. **Säkerhet** – Full kontroll över din data och kod
159
- 3. **Efterlevnad** – Inbyggt stöd för svenska och EU-regleringar
160
- 4. **Flexibilitet** – Open source med frihet att välja modeller och verktyg
161
- 5. **Skalbarhet** – Från små projekt till enterprise-nivå
162
-
163
- ## Sammanfattning
164
-
165
- OpenCode + Berget AI är inte bara ett verktyg – det är en strategisk lösning för svenska företag som vill leda inom AI-driven utveckling utan att kompromissa med säkerhet och efterlevnad.
166
-
167
- Med svensk datainfrastruktur, projektspecifika API-nycklar och en open source AI-assistent får du det bästa av två världar: global AI-kompetens med lokal dataskydd.
168
-
169
- **Redo att transformera din kodning med svensk AI?**
170
-
171
- ```bash
172
- # Börja idag
173
- berget code init
174
- ```
175
-
176
- _Din kod, din data, ditt land – nu med AI-superkrafter._