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
package/src/types/json.d.ts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import * as fs from
|
|
2
|
-
import * as path from
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Check for .bergetconfig file and handle cluster switching
|
|
6
6
|
*/
|
|
7
7
|
export function checkBergetConfig(): void {
|
|
8
|
-
const configPath = path.join(process.cwd(),
|
|
8
|
+
const configPath = path.join(process.cwd(), '.bergetconfig');
|
|
9
9
|
if (fs.existsSync(configPath)) {
|
|
10
10
|
try {
|
|
11
|
-
const config = fs.readFileSync(configPath,
|
|
11
|
+
const config = fs.readFileSync(configPath, 'utf8');
|
|
12
12
|
const match = config.match(/cluster:\s*(.+)/);
|
|
13
13
|
if (match && match[1]) {
|
|
14
14
|
const clusterName = match[1].trim();
|
|
15
15
|
console.log(`🔄 Berget: Switched to cluster "${clusterName}"`);
|
|
16
|
-
console.log(
|
|
17
|
-
console.log(
|
|
16
|
+
console.log('✓ kubectl config updated');
|
|
17
|
+
console.log('');
|
|
18
18
|
}
|
|
19
19
|
} catch {
|
|
20
20
|
// Silently ignore errors reading config
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import * as fs from
|
|
2
|
-
import * as path from
|
|
3
|
-
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
|
|
4
|
+
import { logger } from './logger';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Centralized agent configuration loader
|
|
@@ -8,18 +9,18 @@ import { logger } from "./logger";
|
|
|
8
9
|
*/
|
|
9
10
|
|
|
10
11
|
export interface AgentConfig {
|
|
12
|
+
description?: string;
|
|
13
|
+
mode: 'primary' | 'subagent';
|
|
11
14
|
model: string;
|
|
12
|
-
|
|
13
|
-
top_p: number;
|
|
14
|
-
mode: "primary" | "subagent";
|
|
15
|
+
note?: string;
|
|
15
16
|
permission: {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
webfetch:
|
|
17
|
+
bash: 'allow' | 'deny';
|
|
18
|
+
edit: 'allow' | 'deny';
|
|
19
|
+
webfetch: 'allow' | 'deny';
|
|
19
20
|
};
|
|
20
|
-
description?: string;
|
|
21
21
|
prompt?: string;
|
|
22
|
-
|
|
22
|
+
temperature: number;
|
|
23
|
+
top_p: number;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
export interface ModelConfig {
|
|
@@ -27,47 +28,40 @@ export interface ModelConfig {
|
|
|
27
28
|
small: string;
|
|
28
29
|
}
|
|
29
30
|
|
|
31
|
+
export interface OpenCodeConfig {
|
|
32
|
+
$schema?: string;
|
|
33
|
+
agent?: Record<string, AgentConfig>;
|
|
34
|
+
autoupdate?: boolean;
|
|
35
|
+
command?: Record<string, any>;
|
|
36
|
+
model?: string;
|
|
37
|
+
provider?: Record<string, any>;
|
|
38
|
+
share?: string;
|
|
39
|
+
small_model?: string;
|
|
40
|
+
theme?: string;
|
|
41
|
+
username?: string;
|
|
42
|
+
watcher?: Record<string, any>;
|
|
43
|
+
}
|
|
44
|
+
|
|
30
45
|
export interface ProviderModelConfig {
|
|
31
|
-
name: string;
|
|
32
46
|
limit: {
|
|
33
|
-
output: number;
|
|
34
47
|
context: number;
|
|
48
|
+
output: number;
|
|
35
49
|
};
|
|
36
50
|
modalities?: {
|
|
37
51
|
input: string[];
|
|
38
52
|
output: string[];
|
|
39
53
|
};
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
export interface OpenCodeConfig {
|
|
43
|
-
$schema?: string;
|
|
44
|
-
username?: string;
|
|
45
|
-
theme?: string;
|
|
46
|
-
share?: string;
|
|
47
|
-
autoupdate?: boolean;
|
|
48
|
-
model?: string;
|
|
49
|
-
small_model?: string;
|
|
50
|
-
agent?: Record<string, AgentConfig>;
|
|
51
|
-
command?: Record<string, any>;
|
|
52
|
-
watcher?: Record<string, any>;
|
|
53
|
-
provider?: Record<string, any>;
|
|
54
|
+
name: string;
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
export class ConfigLoader {
|
|
57
58
|
private static instance: ConfigLoader;
|
|
58
|
-
private config:
|
|
59
|
+
private config: null | OpenCodeConfig = null;
|
|
59
60
|
private configPath: string;
|
|
60
61
|
|
|
61
62
|
private constructor(configPath?: string) {
|
|
62
63
|
// Default to opencode.json in current working directory
|
|
63
|
-
this.configPath = configPath || path.join(process.cwd(),
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
public static getInstance(configPath?: string): ConfigLoader {
|
|
67
|
-
if (!ConfigLoader.instance) {
|
|
68
|
-
ConfigLoader.instance = new ConfigLoader(configPath);
|
|
69
|
-
}
|
|
70
|
-
return ConfigLoader.instance;
|
|
64
|
+
this.configPath = configPath || path.join(process.cwd(), 'opencode.json');
|
|
71
65
|
}
|
|
72
66
|
|
|
73
67
|
/**
|
|
@@ -77,30 +71,11 @@ export class ConfigLoader {
|
|
|
77
71
|
ConfigLoader.instance = null as any;
|
|
78
72
|
}
|
|
79
73
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
public loadConfig(): OpenCodeConfig {
|
|
84
|
-
if (this.config) {
|
|
85
|
-
return this.config;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
try {
|
|
89
|
-
if (!fs.existsSync(this.configPath)) {
|
|
90
|
-
throw new Error(`Configuration file not found: ${this.configPath}`);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const configContent = fs.readFileSync(this.configPath, "utf8");
|
|
94
|
-
this.config = JSON.parse(configContent) as OpenCodeConfig;
|
|
95
|
-
|
|
96
|
-
logger.debug(`Loaded configuration from ${this.configPath}`);
|
|
97
|
-
return this.config;
|
|
98
|
-
} catch (error) {
|
|
99
|
-
logger.error(`Failed to load configuration from ${this.configPath}:`, error);
|
|
100
|
-
throw new Error(
|
|
101
|
-
`Failed to load configuration: ${error instanceof Error ? error.message : String(error)}`
|
|
102
|
-
);
|
|
74
|
+
public static getInstance(configPath?: string): ConfigLoader {
|
|
75
|
+
if (!ConfigLoader.instance) {
|
|
76
|
+
ConfigLoader.instance = new ConfigLoader(configPath);
|
|
103
77
|
}
|
|
78
|
+
return ConfigLoader.instance;
|
|
104
79
|
}
|
|
105
80
|
|
|
106
81
|
/**
|
|
@@ -116,6 +91,13 @@ export class ConfigLoader {
|
|
|
116
91
|
}
|
|
117
92
|
}
|
|
118
93
|
|
|
94
|
+
/**
|
|
95
|
+
* Get list of all available agent names
|
|
96
|
+
*/
|
|
97
|
+
public getAgentNames(): string[] {
|
|
98
|
+
return Object.keys(this.getAllAgentConfigs());
|
|
99
|
+
}
|
|
100
|
+
|
|
119
101
|
/**
|
|
120
102
|
* Get all agent configurations
|
|
121
103
|
*/
|
|
@@ -129,6 +111,26 @@ export class ConfigLoader {
|
|
|
129
111
|
}
|
|
130
112
|
}
|
|
131
113
|
|
|
114
|
+
/**
|
|
115
|
+
* Get command configurations
|
|
116
|
+
*/
|
|
117
|
+
public getCommandConfigs(): Record<string, any> {
|
|
118
|
+
try {
|
|
119
|
+
const config = this.loadConfig();
|
|
120
|
+
return config.command || {};
|
|
121
|
+
} catch {
|
|
122
|
+
// Config file doesn't exist, return empty object
|
|
123
|
+
return {};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Get the current configuration path
|
|
129
|
+
*/
|
|
130
|
+
public getConfigPath(): string {
|
|
131
|
+
return this.configPath;
|
|
132
|
+
}
|
|
133
|
+
|
|
132
134
|
/**
|
|
133
135
|
* Get model configuration
|
|
134
136
|
*/
|
|
@@ -137,19 +139,40 @@ export class ConfigLoader {
|
|
|
137
139
|
const config = this.loadConfig();
|
|
138
140
|
|
|
139
141
|
// Extract from config or fall back to defaults
|
|
140
|
-
const primary = config.model ||
|
|
141
|
-
const small = config.small_model ||
|
|
142
|
+
const primary = config.model || 'berget/glm-4.7';
|
|
143
|
+
const small = config.small_model || 'berget/gpt-oss';
|
|
142
144
|
|
|
143
145
|
return { primary, small };
|
|
144
146
|
} catch {
|
|
145
147
|
// Fallback to defaults when no config exists (init scenario)
|
|
146
148
|
return {
|
|
147
|
-
primary:
|
|
148
|
-
small:
|
|
149
|
+
primary: 'berget/glm-4.7',
|
|
150
|
+
small: 'berget/gpt-oss',
|
|
149
151
|
};
|
|
150
152
|
}
|
|
151
153
|
}
|
|
152
154
|
|
|
155
|
+
/**
|
|
156
|
+
* Get list of primary agents (mode: 'primary')
|
|
157
|
+
*/
|
|
158
|
+
public getPrimaryAgentNames(): string[] {
|
|
159
|
+
const agents = this.getAllAgentConfigs();
|
|
160
|
+
return Object.keys(agents).filter((name) => agents[name].mode === 'primary');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Get provider configuration
|
|
165
|
+
*/
|
|
166
|
+
public getProviderConfig(): Record<string, any> {
|
|
167
|
+
try {
|
|
168
|
+
const config = this.loadConfig();
|
|
169
|
+
return config.provider || {};
|
|
170
|
+
} catch {
|
|
171
|
+
// Config file doesn't exist, return empty object
|
|
172
|
+
return {};
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
153
176
|
/**
|
|
154
177
|
* Get provider model configuration
|
|
155
178
|
*/
|
|
@@ -167,36 +190,31 @@ export class ConfigLoader {
|
|
|
167
190
|
|
|
168
191
|
// Fallback to defaults
|
|
169
192
|
return {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
193
|
+
'glm-4.7': {
|
|
194
|
+
limit: { context: 90_000, output: 4000 },
|
|
195
|
+
name: 'GLM-4.7',
|
|
173
196
|
},
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
limit: { output: 4000, context: 128000 },
|
|
197
|
+
'gpt-oss': {
|
|
198
|
+
limit: { context: 128_000, output: 4000 },
|
|
177
199
|
modalities: {
|
|
178
|
-
input: [
|
|
179
|
-
output: [
|
|
200
|
+
input: ['text', 'image'],
|
|
201
|
+
output: ['text'],
|
|
180
202
|
},
|
|
203
|
+
name: 'GPT-OSS',
|
|
181
204
|
},
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
205
|
+
'llama-8b': {
|
|
206
|
+
limit: { context: 128_000, output: 4000 },
|
|
207
|
+
name: 'llama-3.1-8b',
|
|
185
208
|
},
|
|
186
209
|
};
|
|
187
210
|
}
|
|
188
211
|
|
|
189
212
|
/**
|
|
190
|
-
* Get
|
|
213
|
+
* Get list of subagents (mode: 'subagent')
|
|
191
214
|
*/
|
|
192
|
-
public
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
return config.command || {};
|
|
196
|
-
} catch {
|
|
197
|
-
// Config file doesn't exist, return empty object
|
|
198
|
-
return {};
|
|
199
|
-
}
|
|
215
|
+
public getSubagentNames(): string[] {
|
|
216
|
+
const agents = this.getAllAgentConfigs();
|
|
217
|
+
return Object.keys(agents).filter((name) => agents[name].mode === 'subagent');
|
|
200
218
|
}
|
|
201
219
|
|
|
202
220
|
/**
|
|
@@ -207,25 +225,12 @@ export class ConfigLoader {
|
|
|
207
225
|
const config = this.loadConfig();
|
|
208
226
|
return (
|
|
209
227
|
config.watcher || {
|
|
210
|
-
ignore: [
|
|
228
|
+
ignore: ['node_modules', 'dist', '.git', 'coverage'],
|
|
211
229
|
}
|
|
212
230
|
);
|
|
213
231
|
} catch {
|
|
214
232
|
// Config file doesn't exist, return default watcher config
|
|
215
|
-
return { ignore: [
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Get provider configuration
|
|
221
|
-
*/
|
|
222
|
-
public getProviderConfig(): Record<string, any> {
|
|
223
|
-
try {
|
|
224
|
-
const config = this.loadConfig();
|
|
225
|
-
return config.provider || {};
|
|
226
|
-
} catch {
|
|
227
|
-
// Config file doesn't exist, return empty object
|
|
228
|
-
return {};
|
|
233
|
+
return { ignore: ['node_modules', 'dist', '.git', 'coverage'] };
|
|
229
234
|
}
|
|
230
235
|
}
|
|
231
236
|
|
|
@@ -237,26 +242,29 @@ export class ConfigLoader {
|
|
|
237
242
|
}
|
|
238
243
|
|
|
239
244
|
/**
|
|
240
|
-
*
|
|
245
|
+
* Load configuration from opencode.json
|
|
241
246
|
*/
|
|
242
|
-
public
|
|
243
|
-
|
|
244
|
-
|
|
247
|
+
public loadConfig(): OpenCodeConfig {
|
|
248
|
+
if (this.config) {
|
|
249
|
+
return this.config;
|
|
250
|
+
}
|
|
245
251
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
const agents = this.getAllAgentConfigs();
|
|
251
|
-
return Object.keys(agents).filter(name => agents[name].mode === "primary");
|
|
252
|
-
}
|
|
252
|
+
try {
|
|
253
|
+
if (!fs.existsSync(this.configPath)) {
|
|
254
|
+
throw new Error(`Configuration file not found: ${this.configPath}`);
|
|
255
|
+
}
|
|
253
256
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
257
|
+
const configContent = fs.readFileSync(this.configPath, 'utf8');
|
|
258
|
+
this.config = JSON.parse(configContent) as OpenCodeConfig;
|
|
259
|
+
|
|
260
|
+
logger.debug(`Loaded configuration from ${this.configPath}`);
|
|
261
|
+
return this.config;
|
|
262
|
+
} catch (error) {
|
|
263
|
+
logger.error(`Failed to load configuration from ${this.configPath}:`, error);
|
|
264
|
+
throw new Error(
|
|
265
|
+
`Failed to load configuration: ${error instanceof Error ? error.message : String(error)}`,
|
|
266
|
+
);
|
|
267
|
+
}
|
|
260
268
|
}
|
|
261
269
|
|
|
262
270
|
/**
|
|
@@ -274,20 +282,6 @@ export class ConfigLoader {
|
|
|
274
282
|
this.configPath = configPath;
|
|
275
283
|
this.config = null; // Force reload
|
|
276
284
|
}
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* Get the current configuration path
|
|
280
|
-
*/
|
|
281
|
-
public getConfigPath(): string {
|
|
282
|
-
return this.configPath;
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* Convenience function to get the config loader instance
|
|
288
|
-
*/
|
|
289
|
-
export function getConfigLoader(configPath?: string): ConfigLoader {
|
|
290
|
-
return ConfigLoader.getInstance(configPath);
|
|
291
285
|
}
|
|
292
286
|
|
|
293
287
|
/**
|
|
@@ -304,6 +298,13 @@ export function getAllAgentConfigs(configPath?: string): Record<string, AgentCon
|
|
|
304
298
|
return getConfigLoader(configPath).getAllAgentConfigs();
|
|
305
299
|
}
|
|
306
300
|
|
|
301
|
+
/**
|
|
302
|
+
* Convenience function to get the config loader instance
|
|
303
|
+
*/
|
|
304
|
+
export function getConfigLoader(configPath?: string): ConfigLoader {
|
|
305
|
+
return ConfigLoader.getInstance(configPath);
|
|
306
|
+
}
|
|
307
|
+
|
|
307
308
|
/**
|
|
308
309
|
* Convenience function to get model configuration
|
|
309
310
|
*/
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import * as fs from
|
|
2
|
-
import * as
|
|
3
|
-
import * as
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
import {
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as os from 'node:os';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import readline from 'node:readline';
|
|
5
|
+
|
|
6
|
+
import { ApiKeyService } from '../services/api-key-service';
|
|
7
|
+
import { logger } from './logger';
|
|
7
8
|
|
|
8
9
|
interface DefaultApiKeyData {
|
|
9
10
|
id: string;
|
|
11
|
+
key: string;
|
|
10
12
|
name: string;
|
|
11
13
|
prefix: string;
|
|
12
|
-
key: string;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
/**
|
|
@@ -22,11 +23,11 @@ export class DefaultApiKeyManager {
|
|
|
22
23
|
|
|
23
24
|
private constructor() {
|
|
24
25
|
// Set up config file path in user's home directory
|
|
25
|
-
const bergetDir = path.join(os.homedir(),
|
|
26
|
+
const bergetDir = path.join(os.homedir(), '.berget');
|
|
26
27
|
if (!fs.existsSync(bergetDir)) {
|
|
27
28
|
fs.mkdirSync(bergetDir, { recursive: true });
|
|
28
29
|
}
|
|
29
|
-
this.configFilePath = path.join(bergetDir,
|
|
30
|
+
this.configFilePath = path.join(bergetDir, 'default-api-key.json');
|
|
30
31
|
this.loadConfig();
|
|
31
32
|
}
|
|
32
33
|
|
|
@@ -38,52 +39,17 @@ export class DefaultApiKeyManager {
|
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
/**
|
|
41
|
-
*
|
|
42
|
-
*/
|
|
43
|
-
private loadConfig(): void {
|
|
44
|
-
try {
|
|
45
|
-
if (fs.existsSync(this.configFilePath)) {
|
|
46
|
-
const data = fs.readFileSync(this.configFilePath, "utf8");
|
|
47
|
-
this.defaultApiKey = JSON.parse(data);
|
|
48
|
-
}
|
|
49
|
-
} catch {
|
|
50
|
-
logger.debug("Failed to load default API key configuration");
|
|
51
|
-
this.defaultApiKey = null;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Save default API key to file
|
|
57
|
-
*/
|
|
58
|
-
private saveConfig(): void {
|
|
59
|
-
try {
|
|
60
|
-
if (this.defaultApiKey) {
|
|
61
|
-
fs.writeFileSync(this.configFilePath, JSON.stringify(this.defaultApiKey, null, 2));
|
|
62
|
-
// Set file permissions to be readable only by the owner
|
|
63
|
-
fs.chmodSync(this.configFilePath, 0o600);
|
|
64
|
-
} else {
|
|
65
|
-
// If default API key is null, remove the file
|
|
66
|
-
if (fs.existsSync(this.configFilePath)) {
|
|
67
|
-
fs.unlinkSync(this.configFilePath);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
} catch {
|
|
71
|
-
logger.debug("Failed to save default API key configuration");
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Set the default API key
|
|
42
|
+
* Clear the default API key
|
|
77
43
|
*/
|
|
78
|
-
public
|
|
79
|
-
this.defaultApiKey =
|
|
44
|
+
public clearDefaultApiKey(): void {
|
|
45
|
+
this.defaultApiKey = null;
|
|
80
46
|
this.saveConfig();
|
|
81
47
|
}
|
|
82
48
|
|
|
83
49
|
/**
|
|
84
50
|
* Get the default API key string
|
|
85
51
|
*/
|
|
86
|
-
public getDefaultApiKey():
|
|
52
|
+
public getDefaultApiKey(): null | string {
|
|
87
53
|
return this.defaultApiKey?.key || null;
|
|
88
54
|
}
|
|
89
55
|
|
|
@@ -94,43 +60,35 @@ export class DefaultApiKeyManager {
|
|
|
94
60
|
return this.defaultApiKey;
|
|
95
61
|
}
|
|
96
62
|
|
|
97
|
-
/**
|
|
98
|
-
* Clear the default API key
|
|
99
|
-
*/
|
|
100
|
-
public clearDefaultApiKey(): void {
|
|
101
|
-
this.defaultApiKey = null;
|
|
102
|
-
this.saveConfig();
|
|
103
|
-
}
|
|
104
|
-
|
|
105
63
|
/**
|
|
106
64
|
* Prompts the user to select a default API key if none is set
|
|
107
65
|
* @returns The selected API key or null if none was selected
|
|
108
66
|
*/
|
|
109
|
-
public async promptForDefaultApiKey(): Promise<
|
|
67
|
+
public async promptForDefaultApiKey(): Promise<null | string> {
|
|
110
68
|
try {
|
|
111
|
-
logger.debug(
|
|
69
|
+
logger.debug('promptForDefaultApiKey called');
|
|
112
70
|
|
|
113
71
|
// If we already have a default API key, return it
|
|
114
72
|
if (this.defaultApiKey) {
|
|
115
|
-
logger.debug(
|
|
73
|
+
logger.debug('Using existing default API key');
|
|
116
74
|
return this.defaultApiKey.key;
|
|
117
75
|
}
|
|
118
76
|
|
|
119
|
-
logger.debug(
|
|
77
|
+
logger.debug('No default API key found, getting ApiKeyService');
|
|
120
78
|
|
|
121
79
|
const apiKeyService = ApiKeyService.getInstance();
|
|
122
80
|
|
|
123
81
|
// Get all API keys
|
|
124
82
|
let apiKeys;
|
|
125
83
|
try {
|
|
126
|
-
logger.debug(
|
|
84
|
+
logger.debug('Calling apiKeyService.list()');
|
|
127
85
|
|
|
128
86
|
apiKeys = await apiKeyService.list();
|
|
129
87
|
|
|
130
88
|
logger.debug(`Got ${apiKeys ? apiKeys.length : 0} API keys`);
|
|
131
89
|
|
|
132
90
|
if (!apiKeys || apiKeys.length === 0) {
|
|
133
|
-
logger.warn(
|
|
91
|
+
logger.warn('No API keys found. Create one with:');
|
|
134
92
|
logger.info(' berget api-keys create --name "My Key"');
|
|
135
93
|
return null;
|
|
136
94
|
}
|
|
@@ -138,14 +96,14 @@ export class DefaultApiKeyManager {
|
|
|
138
96
|
// Check if this is an authentication error
|
|
139
97
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
140
98
|
const isAuthError =
|
|
141
|
-
errorMessage.includes(
|
|
142
|
-
errorMessage.includes(
|
|
143
|
-
errorMessage.includes(
|
|
99
|
+
errorMessage.includes('Unauthorized') ||
|
|
100
|
+
errorMessage.includes('Authentication failed') ||
|
|
101
|
+
errorMessage.includes('AUTH_FAILED');
|
|
144
102
|
|
|
145
103
|
if (isAuthError) {
|
|
146
|
-
logger.warn(
|
|
104
|
+
logger.warn('Authentication required. Please run `berget auth login` first.');
|
|
147
105
|
} else {
|
|
148
|
-
logger.error(
|
|
106
|
+
logger.error('Error fetching API keys:');
|
|
149
107
|
if (error instanceof Error) {
|
|
150
108
|
logger.error(error.message);
|
|
151
109
|
logger.debug(`API key list error: ${error.message}`);
|
|
@@ -155,12 +113,12 @@ export class DefaultApiKeyManager {
|
|
|
155
113
|
return null;
|
|
156
114
|
}
|
|
157
115
|
|
|
158
|
-
logger.info(
|
|
116
|
+
logger.info('Select an API key to use as default:');
|
|
159
117
|
|
|
160
118
|
// Display available API keys
|
|
161
|
-
apiKeys.
|
|
119
|
+
for (const [index, key] of apiKeys.entries()) {
|
|
162
120
|
logger.log(` ${index + 1}. ${key.name} (${key.prefix}...)`);
|
|
163
|
-
}
|
|
121
|
+
}
|
|
164
122
|
|
|
165
123
|
// Create readline interface for user input
|
|
166
124
|
const rl = readline.createInterface({
|
|
@@ -169,20 +127,20 @@ export class DefaultApiKeyManager {
|
|
|
169
127
|
});
|
|
170
128
|
|
|
171
129
|
// Prompt for selection
|
|
172
|
-
const selection = await new Promise<number>(resolve => {
|
|
173
|
-
rl.question(
|
|
130
|
+
const selection = await new Promise<number>((resolve) => {
|
|
131
|
+
rl.question('Enter number (or press Enter to cancel): ', (answer) => {
|
|
174
132
|
rl.close();
|
|
175
|
-
const
|
|
176
|
-
if (isNaN(
|
|
133
|
+
const number_ = Number.parseInt(answer.trim(), 10);
|
|
134
|
+
if (isNaN(number_) || number_ < 1 || number_ > apiKeys.length) {
|
|
177
135
|
resolve(-1); // Invalid selection
|
|
178
136
|
} else {
|
|
179
|
-
resolve(
|
|
137
|
+
resolve(number_ - 1); // Convert to zero-based index
|
|
180
138
|
}
|
|
181
139
|
});
|
|
182
140
|
});
|
|
183
141
|
|
|
184
142
|
if (selection === -1) {
|
|
185
|
-
logger.warn(
|
|
143
|
+
logger.warn('No API key selected');
|
|
186
144
|
return null;
|
|
187
145
|
}
|
|
188
146
|
|
|
@@ -190,23 +148,66 @@ export class DefaultApiKeyManager {
|
|
|
190
148
|
|
|
191
149
|
// Create a new API key with the selected name
|
|
192
150
|
const newKey = await apiKeyService.create({
|
|
151
|
+
description: 'Created automatically by the Berget CLI for default use',
|
|
193
152
|
name: `CLI Default (copy of ${selectedKey.name})`,
|
|
194
|
-
description: "Created automatically by the Berget CLI for default use",
|
|
195
153
|
});
|
|
196
154
|
|
|
197
155
|
// Save the new key as default
|
|
198
156
|
this.setDefaultApiKey(
|
|
199
157
|
newKey.id.toString(),
|
|
200
158
|
newKey.name,
|
|
201
|
-
newKey.key.
|
|
202
|
-
newKey.key
|
|
159
|
+
newKey.key.slice(0, 8), // Use first 8 chars as prefix
|
|
160
|
+
newKey.key,
|
|
203
161
|
);
|
|
204
162
|
|
|
205
163
|
logger.success(`✓ Default API key set to: ${newKey.name}`);
|
|
206
164
|
return newKey.key;
|
|
207
165
|
} catch (error) {
|
|
208
|
-
logger.error(
|
|
166
|
+
logger.error('Failed to set default API key:', error);
|
|
209
167
|
return null;
|
|
210
168
|
}
|
|
211
169
|
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Set the default API key
|
|
173
|
+
*/
|
|
174
|
+
public setDefaultApiKey(id: string, name: string, prefix: string, key: string): void {
|
|
175
|
+
this.defaultApiKey = { id, key, name, prefix };
|
|
176
|
+
this.saveConfig();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Load default API key from file
|
|
181
|
+
*/
|
|
182
|
+
private loadConfig(): void {
|
|
183
|
+
try {
|
|
184
|
+
if (fs.existsSync(this.configFilePath)) {
|
|
185
|
+
const data = fs.readFileSync(this.configFilePath, 'utf8');
|
|
186
|
+
this.defaultApiKey = JSON.parse(data);
|
|
187
|
+
}
|
|
188
|
+
} catch {
|
|
189
|
+
logger.debug('Failed to load default API key configuration');
|
|
190
|
+
this.defaultApiKey = null;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Save default API key to file
|
|
196
|
+
*/
|
|
197
|
+
private saveConfig(): void {
|
|
198
|
+
try {
|
|
199
|
+
if (this.defaultApiKey) {
|
|
200
|
+
fs.writeFileSync(this.configFilePath, JSON.stringify(this.defaultApiKey, null, 2));
|
|
201
|
+
// Set file permissions to be readable only by the owner
|
|
202
|
+
fs.chmodSync(this.configFilePath, 0o600);
|
|
203
|
+
} else {
|
|
204
|
+
// If default API key is null, remove the file
|
|
205
|
+
if (fs.existsSync(this.configFilePath)) {
|
|
206
|
+
fs.unlinkSync(this.configFilePath);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
} catch {
|
|
210
|
+
logger.debug('Failed to save default API key configuration');
|
|
211
|
+
}
|
|
212
|
+
}
|
|
212
213
|
}
|