berget 2.2.7 → 2.2.9

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