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