berget 2.2.10 → 2.2.12

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.
@@ -1,436 +1,25 @@
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 commander_1 = require("commander");
27
- const fs = __importStar(require("node:fs"));
28
- const promises_1 = require("node:fs/promises");
29
4
  const vitest_1 = require("vitest");
30
5
  const code_1 = require("../../src/commands/code");
31
- const api_key_service_1 = require("../../src/services/api-key-service");
32
- const env_manager_1 = require("../../src/utils/env-manager");
33
- // Mock dependencies
34
- vitest_1.vi.mock('../../src/services/api-key-service');
35
- vitest_1.vi.mock('fs', () => ({
36
- default: {
37
- existsSync: vitest_1.vi.fn(),
38
- readFileSync: vitest_1.vi.fn(),
39
- },
40
- }));
41
- vitest_1.vi.mock('fs/promises', () => ({
42
- readFile: vitest_1.vi.fn(),
43
- writeFile: vitest_1.vi.fn(),
44
- }));
45
- vitest_1.vi.mock('../../src/utils/env-manager');
46
- vitest_1.vi.mock('child_process', () => ({
47
- spawn: vitest_1.vi.fn(),
48
- }));
49
- vitest_1.vi.mock('readline', () => ({
50
- createInterface: vitest_1.vi.fn(() => ({
51
- close: vitest_1.vi.fn(),
52
- question: vitest_1.vi.fn(),
53
- })),
54
- }));
55
6
  (0, vitest_1.describe)('Code Commands', () => {
56
7
  let program;
57
- let mockApiKeyService;
58
- let mockFs;
59
- let mockFsPromises;
60
- let mockSpawn;
61
8
  (0, vitest_1.beforeEach)(() => {
62
9
  program = new commander_1.Command();
63
- // Mock ApiKeyService
64
- mockApiKeyService = {
65
- create: vitest_1.vi.fn(),
66
- list: vitest_1.vi.fn(),
67
- rotate: vitest_1.vi.fn(),
68
- };
69
- vitest_1.vi.mocked(api_key_service_1.ApiKeyService.getInstance).mockReturnValue(mockApiKeyService);
70
- // Mock fs
71
- mockFs = vitest_1.vi.mocked(fs);
72
- mockFs.existsSync = vitest_1.vi.fn();
73
- mockFs.readFileSync = vitest_1.vi.fn();
74
- // Mock fs/promises
75
- mockFsPromises = vitest_1.vi.mocked({ readFile: promises_1.readFile, writeFile: promises_1.writeFile });
76
- mockFsPromises.readFile = vitest_1.vi.fn();
77
- mockFsPromises.writeFile = vitest_1.vi.fn();
78
- // Mock spawn
79
- mockSpawn = vitest_1.vi.fn();
80
- vitest_1.vi.doMock('child_process', () => ({ spawn: mockSpawn }));
81
10
  (0, code_1.registerCodeCommands)(program);
82
11
  });
83
- (0, vitest_1.afterEach)(() => {
84
- vitest_1.vi.clearAllMocks();
85
- });
86
12
  (0, vitest_1.describe)('code init command', () => {
87
13
  (0, vitest_1.it)('should register init command with correct description', () => {
88
14
  const codeCommand = program.commands.find((cmd) => cmd.name() === 'code');
89
15
  const initCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'init');
90
16
  (0, vitest_1.expect)(initCommand).toBeDefined();
91
- (0, vitest_1.expect)(initCommand?.description()).toBe('Initialize project for AI coding assistant');
92
- });
93
- (0, vitest_1.it)('should have name, force, and yes options', () => {
94
- const codeCommand = program.commands.find((cmd) => cmd.name() === 'code');
95
- const initCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'init');
96
- (0, vitest_1.expect)(initCommand).toBeDefined();
97
- const nameOption = initCommand?.options.find((opt) => opt.long === '--name');
98
- const forceOption = initCommand?.options.find((opt) => opt.long === '--force');
99
- const yesOption = initCommand?.options.find((opt) => opt.long === '--yes');
100
- (0, vitest_1.expect)(nameOption).toBeDefined();
101
- (0, vitest_1.expect)(nameOption?.description).toContain('Project name');
102
- (0, vitest_1.expect)(forceOption).toBeDefined();
103
- (0, vitest_1.expect)(forceOption?.description).toContain('Overwrite existing configuration');
104
- (0, vitest_1.expect)(yesOption).toBeDefined();
105
- (0, vitest_1.expect)(yesOption?.description).toContain('Automatically answer yes');
106
- });
107
- (0, vitest_1.it)('should check if opencode is installed', () => {
108
- const codeCommand = program.commands.find((cmd) => cmd.name() === 'code');
109
- const initCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'init');
110
- (0, vitest_1.expect)(initCommand).toBeDefined();
111
- // The command should attempt to spawn opencode --version
112
- // This is tested implicitly through the spawn mock
113
- });
114
- (0, vitest_1.it)('should list existing API keys and allow selection', async () => {
115
- // Mock successful opencode installation check
116
- mockSpawn.mockImplementation((command, arguments_) => {
117
- if (command === 'opencode' && arguments_[0] === '--version') {
118
- return {
119
- on: vitest_1.vi.fn().mockImplementation((event, callback) => {
120
- if (event === 'close')
121
- callback(0);
122
- }),
123
- };
124
- }
125
- return { on: vitest_1.vi.fn() };
126
- });
127
- // Mock existing API keys
128
- const mockExistingKeys = [
129
- {
130
- created: '2023-01-01T00:00:00.000Z',
131
- id: 1,
132
- lastUsed: null,
133
- name: 'existing-key-1',
134
- prefix: 'sk_ber',
135
- },
136
- {
137
- created: '2023-01-02T00:00:00.000Z',
138
- id: 2,
139
- lastUsed: '2023-01-03T00:00:00.000Z',
140
- name: 'existing-key-2',
141
- prefix: 'sk_ber',
142
- },
143
- ];
144
- mockApiKeyService.list.mockResolvedValue(mockExistingKeys);
145
- // Mock file operations
146
- mockFs.existsSync.mockReturnValue(false);
147
- mockFsPromises.writeFile.mockResolvedValue();
148
- // Verify that the list method is called
149
- (0, vitest_1.expect)(mockApiKeyService.list).toBeDefined();
17
+ (0, vitest_1.expect)(initCommand?.description()).toBe('Interactive setup for Berget AI coding tools');
150
18
  });
151
- (0, vitest_1.it)('should create new API key with project-based naming', async () => {
152
- // Mock successful opencode installation check
153
- mockSpawn.mockImplementation((command, arguments_) => {
154
- if (command === 'opencode' && arguments_[0] === '--version') {
155
- return {
156
- on: vitest_1.vi.fn().mockImplementation((event, callback) => {
157
- if (event === 'close')
158
- callback(0);
159
- }),
160
- };
161
- }
162
- return { on: vitest_1.vi.fn() };
163
- });
164
- // Mock no existing keys
165
- mockApiKeyService.list.mockResolvedValue([]);
166
- // Mock successful API key creation
167
- const mockApiKeyData = {
168
- id: 123,
169
- key: 'test-api-key-12345',
170
- name: 'opencode-testproject-1234567890',
171
- };
172
- mockApiKeyService.create.mockResolvedValue(mockApiKeyData);
173
- // Mock file operations
174
- mockFs.existsSync.mockReturnValue(false);
175
- mockFsPromises.writeFile.mockResolvedValue();
176
- // Verify that the create method is available
177
- (0, vitest_1.expect)(mockApiKeyService.create).toBeDefined();
178
- });
179
- (0, vitest_1.it)('should create opencode.json with correct structure', async () => {
180
- // This tests the expected config structure
181
- const expectedConfig = {
182
- apiKey: 'test-api-key',
183
- created: vitest_1.expect.any(String),
184
- model: 'berget/glm-4-6',
185
- projectName: 'testproject',
186
- provider: 'berget',
187
- version: '1.0.0',
188
- };
189
- (0, vitest_1.expect)(expectedConfig.model).toBe('berget/glm-4-6');
190
- (0, vitest_1.expect)(expectedConfig.provider).toBe('berget');
191
- (0, vitest_1.expect)(expectedConfig.version).toBe('1.0.0');
192
- });
193
- (0, vitest_1.it)('should handle existing config file', () => {
19
+ (0, vitest_1.it)('should not have any other subcommands', () => {
194
20
  const codeCommand = program.commands.find((cmd) => cmd.name() === 'code');
195
- const initCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'init');
196
- (0, vitest_1.expect)(initCommand).toBeDefined();
197
- // Should check if opencode.json exists before proceeding
198
- (0, vitest_1.expect)(mockFs.existsSync).toBeDefined();
199
- });
200
- });
201
- (0, vitest_1.describe)('code run command', () => {
202
- (0, vitest_1.it)('should register run command with correct description', () => {
203
- const codeCommand = program.commands.find((cmd) => cmd.name() === 'code');
204
- const runCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'run');
205
- (0, vitest_1.expect)(runCommand).toBeDefined();
206
- (0, vitest_1.expect)(runCommand?.description()).toBe('Run AI coding assistant');
207
- });
208
- (0, vitest_1.it)('should accept prompt argument and model, no-config, and yes options', () => {
209
- const codeCommand = program.commands.find((cmd) => cmd.name() === 'code');
210
- const runCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'run');
211
- (0, vitest_1.expect)(runCommand).toBeDefined();
212
- const modelOption = runCommand?.options.find((opt) => opt.long === '--model');
213
- const noConfigOption = runCommand?.options.find((opt) => opt.long === '--no-config');
214
- const yesOption = runCommand?.options.find((opt) => opt.long === '--yes');
215
- (0, vitest_1.expect)(modelOption).toBeDefined();
216
- (0, vitest_1.expect)(modelOption?.description).toContain('Model to use');
217
- (0, vitest_1.expect)(noConfigOption).toBeDefined();
218
- (0, vitest_1.expect)(noConfigOption?.description).toContain('Run without loading project config');
219
- (0, vitest_1.expect)(yesOption).toBeDefined();
220
- (0, vitest_1.expect)(yesOption?.description).toContain('Automatically answer yes');
221
- });
222
- (0, vitest_1.it)('should load configuration from opencode.json', async () => {
223
- const mockConfig = {
224
- apiKey: 'test-api-key',
225
- created: '2023-01-01T00:00:00.000Z',
226
- model: 'berget/glm-4-6',
227
- projectName: 'testproject',
228
- provider: 'berget',
229
- version: '1.0.0',
230
- };
231
- // Mock file exists and contains config
232
- mockFs.existsSync.mockReturnValue(true);
233
- mockFsPromises.readFile.mockResolvedValue(JSON.stringify(mockConfig));
234
- // Mock successful opencode check
235
- mockSpawn.mockImplementation((command, arguments_) => {
236
- if (command === 'opencode' && arguments_[0] === '--version') {
237
- return {
238
- on: vitest_1.vi.fn().mockImplementation((event, callback) => {
239
- if (event === 'close')
240
- callback(0);
241
- }),
242
- };
243
- }
244
- return { on: vitest_1.vi.fn() };
245
- });
246
- // Verify config structure expectations
247
- (0, vitest_1.expect)(mockConfig.model).toBe('berget/glm-4-6');
248
- (0, vitest_1.expect)(mockConfig.apiKey).toBe('test-api-key');
249
- (0, vitest_1.expect)(mockConfig.projectName).toBe('testproject');
250
- });
251
- (0, vitest_1.it)('should spawn opencode with correct arguments', () => {
252
- const codeCommand = program.commands.find((cmd) => cmd.name() === 'code');
253
- const runCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'run');
254
- (0, vitest_1.expect)(runCommand).toBeDefined();
255
- // Should spawn opencode with appropriate arguments
256
- (0, vitest_1.expect)(mockSpawn).toBeDefined();
257
- });
258
- (0, vitest_1.it)('should handle missing configuration file', () => {
259
- const codeCommand = program.commands.find((cmd) => cmd.name() === 'code');
260
- const runCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'run');
261
- (0, vitest_1.expect)(runCommand).toBeDefined();
262
- // Should check if opencode.json exists
263
- (0, vitest_1.expect)(mockFs.existsSync).toBeDefined();
264
- });
265
- });
266
- (0, vitest_1.describe)('opencode installation', () => {
267
- (0, vitest_1.it)('should check if opencode is installed', () => {
268
- // The spawn function should be called with opencode --version
269
- (0, vitest_1.expect)(mockSpawn).toBeDefined();
270
- });
271
- (0, vitest_1.it)('should offer to install opencode if not found', () => {
272
- // Mock opencode not installed
273
- mockSpawn.mockImplementation((command, arguments_) => {
274
- if (command === 'opencode' && arguments_[0] === '--version') {
275
- return {
276
- on: vitest_1.vi.fn().mockImplementation((event, callback) => {
277
- if (event === 'close')
278
- callback(1); // Non-zero exit code
279
- }),
280
- };
281
- }
282
- return { on: vitest_1.vi.fn() };
283
- });
284
- // Should handle the case where opencode is not installed
285
- (0, vitest_1.expect)(mockSpawn).toBeDefined();
286
- });
287
- (0, vitest_1.it)('should install opencode via npm if user agrees', () => {
288
- // Should spawn npm install -g opencode-ai
289
- (0, vitest_1.expect)(mockSpawn).toBeDefined();
290
- });
291
- });
292
- (0, vitest_1.describe)('automation support', () => {
293
- (0, vitest_1.it)('should support -y flag for automated initialization', () => {
294
- const codeCommand = program.commands.find((cmd) => cmd.name() === 'code');
295
- const initCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'init');
296
- (0, vitest_1.expect)(initCommand).toBeDefined();
297
- const yesOption = initCommand?.options.find((opt) => opt.long === '--yes');
298
- (0, vitest_1.expect)(yesOption).toBeDefined();
299
- (0, vitest_1.expect)(yesOption?.description).toContain('automation');
300
- });
301
- (0, vitest_1.it)('should support -y flag for automated run', () => {
302
- const codeCommand = program.commands.find((cmd) => cmd.name() === 'code');
303
- const runCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'run');
304
- (0, vitest_1.expect)(runCommand).toBeDefined();
305
- const yesOption = runCommand?.options.find((opt) => opt.long === '--yes');
306
- (0, vitest_1.expect)(yesOption).toBeDefined();
307
- (0, vitest_1.expect)(yesOption?.description).toContain('automation');
308
- });
309
- (0, vitest_1.it)('should use BERGET_API_KEY environment variable in automation mode', () => {
310
- // Test that environment variable is used when -y flag is set
311
- process.env.BERGET_API_KEY = 'test-env-key';
312
- (0, vitest_1.expect)(process.env.BERGET_API_KEY).toBe('test-env-key');
313
- // Clean up
314
- delete process.env.BERGET_API_KEY;
315
- });
316
- });
317
- (0, vitest_1.describe)('.env file handling', () => {
318
- let mockUpdateEnvironmentFile;
319
- (0, vitest_1.beforeEach)(() => {
320
- mockUpdateEnvironmentFile = vitest_1.vi.mocked(env_manager_1.updateEnvFile);
321
- });
322
- (0, vitest_1.it)('should call updateEnvFile when creating new project', async () => {
323
- mockUpdateEnvironmentFile.mockResolvedValue(true);
324
- mockFs.existsSync.mockReturnValue(false); // .env doesn't exist
325
- mockFsPromises.writeFile.mockResolvedValue();
326
- // This would be tested by actually calling the init command
327
- // For now we verify the mock is properly set up
328
- (0, vitest_1.expect)(mockUpdateEnvironmentFile).toBeDefined();
329
- });
330
- (0, vitest_1.it)('should not overwrite existing BERGET_API_KEY in .env', async () => {
331
- const consoleSpy = vitest_1.vi.spyOn(console, 'log').mockImplementation(() => { });
332
- // Mock existing .env with BERGET_API_KEY
333
- mockFs.existsSync.mockReturnValue(true);
334
- mockFs.readFileSync.mockReturnValue('BERGET_API_KEY=existing_key\nOTHER_KEY=value\n');
335
- // Mock updateEnvFile to simulate the check
336
- mockUpdateEnvironmentFile.mockImplementation(async (options) => {
337
- if (options.key === 'BERGET_API_KEY' && !options.force) {
338
- console.log(`⚠ ${options.key} already exists in .env - leaving unchanged`);
339
- return false;
340
- }
341
- return true;
342
- });
343
- await (0, env_manager_1.updateEnvFile)({
344
- key: 'BERGET_API_KEY',
345
- value: 'new_key',
346
- });
347
- (0, vitest_1.expect)(consoleSpy).toHaveBeenCalledWith(vitest_1.expect.stringContaining('BERGET_API_KEY already exists in .env - leaving unchanged'));
348
- consoleSpy.mockRestore();
349
- });
350
- (0, vitest_1.it)('should add new key to existing .env file', async () => {
351
- mockFs.existsSync.mockReturnValue(true);
352
- mockFs.readFileSync.mockReturnValue('EXISTING_KEY=value\n');
353
- mockUpdateEnvironmentFile.mockResolvedValue(true);
354
- await (0, env_manager_1.updateEnvFile)({
355
- comment: 'Berget AI Configuration',
356
- key: 'BERGET_API_KEY',
357
- value: 'new_api_key',
358
- });
359
- (0, vitest_1.expect)(mockUpdateEnvironmentFile).toHaveBeenCalledWith({
360
- comment: 'Berget AI Configuration',
361
- key: 'BERGET_API_KEY',
362
- value: 'new_api_key',
363
- });
364
- });
365
- (0, vitest_1.it)('should create new .env file when none exists', async () => {
366
- mockFs.existsSync.mockReturnValue(false);
367
- mockUpdateEnvironmentFile.mockResolvedValue(true);
368
- await (0, env_manager_1.updateEnvFile)({
369
- key: 'BERGET_API_KEY',
370
- value: 'new_api_key',
371
- });
372
- (0, vitest_1.expect)(mockUpdateEnvironmentFile).toHaveBeenCalledWith({
373
- key: 'BERGET_API_KEY',
374
- value: 'new_api_key',
375
- });
376
- });
377
- });
378
- (0, vitest_1.describe)('error handling', () => {
379
- (0, vitest_1.it)('should handle API key creation failures', () => {
380
- // Mock API key service to throw error
381
- mockApiKeyService.create.mockRejectedValue(new Error('API Error'));
382
- (0, vitest_1.expect)(mockApiKeyService.create).toBeDefined();
383
- });
384
- (0, vitest_1.it)('should handle file system errors', () => {
385
- // Mock file operations to throw errors
386
- mockFsPromises.writeFile.mockRejectedValue(new Error('File write error'));
387
- (0, vitest_1.expect)(mockFsPromises.writeFile).toBeDefined();
388
- });
389
- (0, vitest_1.it)('should handle spawn errors', () => {
390
- // Mock spawn to throw error
391
- mockSpawn.mockImplementation(() => {
392
- throw new Error('Command not found');
393
- });
394
- (0, vitest_1.expect)(mockSpawn).toBeDefined();
395
- });
396
- (0, vitest_1.it)('should handle .env update failures', async () => {
397
- const mockUpdateEnvironmentFile = vitest_1.vi.mocked(env_manager_1.updateEnvFile);
398
- mockUpdateEnvironmentFile.mockRejectedValue(new Error('Env update failed'));
399
- await (0, vitest_1.expect)((0, env_manager_1.updateEnvFile)({
400
- key: 'TEST_KEY',
401
- value: 'test_value',
402
- })).rejects.toThrow('Env update failed');
403
- });
404
- });
405
- (0, vitest_1.describe)('experimental features', () => {
406
- let originalEnvironment;
407
- (0, vitest_1.beforeEach)(() => {
408
- originalEnvironment = process.env.BERGET_EXPERIMENTAL;
409
- });
410
- (0, vitest_1.afterEach)(() => {
411
- if (originalEnvironment === undefined) {
412
- delete process.env.BERGET_EXPERIMENTAL;
413
- }
414
- else {
415
- process.env.BERGET_EXPERIMENTAL = originalEnvironment;
416
- }
417
- });
418
- (0, vitest_1.it)('should NOT show setup command when BERGET_EXPERIMENTAL is not set', () => {
419
- delete process.env.BERGET_EXPERIMENTAL;
420
- const freshProgram = new commander_1.Command();
421
- (0, code_1.registerCodeCommands)(freshProgram);
422
- const codeCommand = freshProgram.commands.find((cmd) => cmd.name() === 'code');
423
- const setupCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'setup');
424
- (0, vitest_1.expect)(setupCommand).toBeUndefined();
425
- });
426
- (0, vitest_1.it)('should show setup command when BERGET_EXPERIMENTAL is set', () => {
427
- process.env.BERGET_EXPERIMENTAL = '1';
428
- const freshProgram = new commander_1.Command();
429
- (0, code_1.registerCodeCommands)(freshProgram);
430
- const codeCommand = freshProgram.commands.find((cmd) => cmd.name() === 'code');
431
- const setupCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'setup');
432
- (0, vitest_1.expect)(setupCommand).toBeDefined();
433
- (0, vitest_1.expect)(setupCommand?.description()).toBe('Interactive setup for Berget AI coding tools');
21
+ (0, vitest_1.expect)(codeCommand?.commands).toHaveLength(1);
22
+ (0, vitest_1.expect)(codeCommand?.commands[0]?.name()).toBe('init');
434
23
  });
435
24
  });
436
25
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "berget",
3
- "version": "2.2.10",
3
+ "version": "2.2.12",
4
4
  "main": "dist/index.js",
5
5
  "bin": {
6
6
  "berget": "dist/index.js"