berget 2.0.2 → 2.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,217 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.getProviderModels = exports.getModelConfig = exports.getAllAgentConfigs = exports.getAgentConfig = exports.getConfigLoader = exports.ConfigLoader = void 0;
27
+ const fs = __importStar(require("fs"));
28
+ const path = __importStar(require("path"));
29
+ const logger_1 = require("./logger");
30
+ class ConfigLoader {
31
+ constructor(configPath) {
32
+ this.config = null;
33
+ // Default to opencode.json in current working directory
34
+ this.configPath = configPath || path.join(process.cwd(), 'opencode.json');
35
+ }
36
+ static getInstance(configPath) {
37
+ if (!ConfigLoader.instance) {
38
+ ConfigLoader.instance = new ConfigLoader(configPath);
39
+ }
40
+ return ConfigLoader.instance;
41
+ }
42
+ /**
43
+ * Load configuration from opencode.json
44
+ */
45
+ loadConfig() {
46
+ if (this.config) {
47
+ return this.config;
48
+ }
49
+ try {
50
+ if (!fs.existsSync(this.configPath)) {
51
+ throw new Error(`Configuration file not found: ${this.configPath}`);
52
+ }
53
+ const configContent = fs.readFileSync(this.configPath, 'utf8');
54
+ this.config = JSON.parse(configContent);
55
+ logger_1.logger.debug(`Loaded configuration from ${this.configPath}`);
56
+ return this.config;
57
+ }
58
+ catch (error) {
59
+ logger_1.logger.error(`Failed to load configuration from ${this.configPath}:`, error);
60
+ throw new Error(`Failed to load configuration: ${error instanceof Error ? error.message : String(error)}`);
61
+ }
62
+ }
63
+ /**
64
+ * Get agent configuration by name
65
+ */
66
+ getAgentConfig(agentName) {
67
+ var _a;
68
+ const config = this.loadConfig();
69
+ return ((_a = config.agent) === null || _a === void 0 ? void 0 : _a[agentName]) || null;
70
+ }
71
+ /**
72
+ * Get all agent configurations
73
+ */
74
+ getAllAgentConfigs() {
75
+ const config = this.loadConfig();
76
+ return config.agent || {};
77
+ }
78
+ /**
79
+ * Get model configuration
80
+ */
81
+ getModelConfig() {
82
+ const config = this.loadConfig();
83
+ // Extract from config or fall back to defaults
84
+ const primary = config.model || 'berget/deepseek-r1';
85
+ const small = config.small_model || 'berget/gpt-oss';
86
+ return { primary, small };
87
+ }
88
+ /**
89
+ * Get provider model configuration
90
+ */
91
+ getProviderModels() {
92
+ var _a, _b;
93
+ const config = this.loadConfig();
94
+ // Extract from provider configuration
95
+ if ((_b = (_a = config.provider) === null || _a === void 0 ? void 0 : _a.berget) === null || _b === void 0 ? void 0 : _b.models) {
96
+ return config.provider.berget.models;
97
+ }
98
+ // Fallback to defaults
99
+ return {
100
+ 'deepseek-r1': {
101
+ name: 'GLM-4.6',
102
+ limit: { output: 4000, context: 90000 }
103
+ },
104
+ 'gpt-oss': {
105
+ name: 'GPT-OSS',
106
+ limit: { output: 4000, context: 128000 }
107
+ },
108
+ 'llama-8b': {
109
+ name: 'llama-3.1-8b',
110
+ limit: { output: 4000, context: 128000 }
111
+ }
112
+ };
113
+ }
114
+ /**
115
+ * Get command configurations
116
+ */
117
+ getCommandConfigs() {
118
+ const config = this.loadConfig();
119
+ return config.command || {};
120
+ }
121
+ /**
122
+ * Get watcher configuration
123
+ */
124
+ getWatcherConfig() {
125
+ const config = this.loadConfig();
126
+ return config.watcher || { ignore: ['node_modules', 'dist', '.git', 'coverage'] };
127
+ }
128
+ /**
129
+ * Get provider configuration
130
+ */
131
+ getProviderConfig() {
132
+ const config = this.loadConfig();
133
+ return config.provider || {};
134
+ }
135
+ /**
136
+ * Check if an agent exists
137
+ */
138
+ hasAgent(agentName) {
139
+ return agentName in this.getAllAgentConfigs();
140
+ }
141
+ /**
142
+ * Get list of all available agent names
143
+ */
144
+ getAgentNames() {
145
+ return Object.keys(this.getAllAgentConfigs());
146
+ }
147
+ /**
148
+ * Get list of primary agents (mode: 'primary')
149
+ */
150
+ getPrimaryAgentNames() {
151
+ const agents = this.getAllAgentConfigs();
152
+ return Object.keys(agents).filter(name => agents[name].mode === 'primary');
153
+ }
154
+ /**
155
+ * Get list of subagents (mode: 'subagent')
156
+ */
157
+ getSubagentNames() {
158
+ const agents = this.getAllAgentConfigs();
159
+ return Object.keys(agents).filter(name => agents[name].mode === 'subagent');
160
+ }
161
+ /**
162
+ * Reload configuration from file
163
+ */
164
+ reloadConfig() {
165
+ this.config = null;
166
+ return this.loadConfig();
167
+ }
168
+ /**
169
+ * Set custom configuration path (for testing or different environments)
170
+ */
171
+ setConfigPath(configPath) {
172
+ this.configPath = configPath;
173
+ this.config = null; // Force reload
174
+ }
175
+ /**
176
+ * Get the current configuration path
177
+ */
178
+ getConfigPath() {
179
+ return this.configPath;
180
+ }
181
+ }
182
+ exports.ConfigLoader = ConfigLoader;
183
+ /**
184
+ * Convenience function to get the config loader instance
185
+ */
186
+ function getConfigLoader(configPath) {
187
+ return ConfigLoader.getInstance(configPath);
188
+ }
189
+ exports.getConfigLoader = getConfigLoader;
190
+ /**
191
+ * Convenience function to get agent configuration
192
+ */
193
+ function getAgentConfig(agentName, configPath) {
194
+ return getConfigLoader(configPath).getAgentConfig(agentName);
195
+ }
196
+ exports.getAgentConfig = getAgentConfig;
197
+ /**
198
+ * Convenience function to get all agent configurations
199
+ */
200
+ function getAllAgentConfigs(configPath) {
201
+ return getConfigLoader(configPath).getAllAgentConfigs();
202
+ }
203
+ exports.getAllAgentConfigs = getAllAgentConfigs;
204
+ /**
205
+ * Convenience function to get model configuration
206
+ */
207
+ function getModelConfig(configPath) {
208
+ return getConfigLoader(configPath).getModelConfig();
209
+ }
210
+ exports.getModelConfig = getModelConfig;
211
+ /**
212
+ * Convenience function to get provider models
213
+ */
214
+ function getProviderModels(configPath) {
215
+ return getConfigLoader(configPath).getProviderModels();
216
+ }
217
+ exports.getProviderModels = getProviderModels;
@@ -0,0 +1,182 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ const vitest_1 = require("vitest");
27
+ const config_loader_1 = require("../../src/utils/config-loader");
28
+ const path = __importStar(require("path"));
29
+ const fs_1 = require("fs");
30
+ (0, vitest_1.describe)('ConfigLoader Singleton Pattern', () => {
31
+ const testConfigPath = path.join(process.cwd(), 'test-config.json');
32
+ const testConfigPath2 = path.join(process.cwd(), 'test-config2.json');
33
+ const testConfig = {
34
+ $schema: 'https://opencode.ai/config.json',
35
+ username: 'test-user',
36
+ model: 'test-model',
37
+ agent: {
38
+ test: {
39
+ model: 'test-model',
40
+ temperature: 0.5,
41
+ mode: 'primary',
42
+ permission: { edit: 'allow', bash: 'allow', webfetch: 'allow' },
43
+ description: 'Test agent',
44
+ prompt: 'Test prompt'
45
+ }
46
+ }
47
+ };
48
+ (0, vitest_1.beforeEach)(() => {
49
+ // Clean up any existing test files
50
+ if ((0, fs_1.existsSync)(testConfigPath))
51
+ (0, fs_1.unlinkSync)(testConfigPath);
52
+ if ((0, fs_1.existsSync)(testConfigPath2))
53
+ (0, fs_1.unlinkSync)(testConfigPath2);
54
+ config_loader_1.ConfigLoader.instance = null;
55
+ });
56
+ (0, vitest_1.afterEach)(() => {
57
+ // Clean up test files
58
+ if ((0, fs_1.existsSync)(testConfigPath))
59
+ (0, fs_1.unlinkSync)(testConfigPath);
60
+ if ((0, fs_1.existsSync)(testConfigPath2))
61
+ (0, fs_1.unlinkSync)(testConfigPath2);
62
+ config_loader_1.ConfigLoader.instance = null;
63
+ });
64
+ (0, vitest_1.it)('should create singleton instance on first call', () => {
65
+ const instance1 = config_loader_1.ConfigLoader.getInstance(testConfigPath);
66
+ const instance2 = config_loader_1.ConfigLoader.getInstance();
67
+ (0, vitest_1.expect)(instance1).toBe(instance2);
68
+ (0, vitest_1.expect)(instance1.getConfigPath()).toBe(testConfigPath);
69
+ });
70
+ (0, vitest_1.it)('should ignore configPath parameter after first instantiation', () => {
71
+ const instance1 = config_loader_1.ConfigLoader.getInstance(testConfigPath);
72
+ const instance2 = config_loader_1.ConfigLoader.getInstance(testConfigPath2); // Should be ignored
73
+ (0, vitest_1.expect)(instance1).toBe(instance2);
74
+ (0, vitest_1.expect)(instance1.getConfigPath()).toBe(testConfigPath); // Still uses first path
75
+ });
76
+ (0, vitest_1.it)('should load and cache configuration', () => {
77
+ (0, fs_1.writeFileSync)(testConfigPath, JSON.stringify(testConfig));
78
+ const loader = config_loader_1.ConfigLoader.getInstance(testConfigPath);
79
+ const config1 = loader.loadConfig();
80
+ const config2 = loader.loadConfig();
81
+ (0, vitest_1.expect)(config1).toBe(config2); // Same object reference (cached)
82
+ (0, vitest_1.expect)(config1.username).toBe('test-user');
83
+ });
84
+ (0, vitest_1.it)('should reload configuration when reloadConfig is called', () => {
85
+ (0, fs_1.writeFileSync)(testConfigPath, JSON.stringify(testConfig));
86
+ const loader = config_loader_1.ConfigLoader.getInstance(testConfigPath);
87
+ const config1 = loader.loadConfig();
88
+ // Modify file
89
+ const modifiedConfig = Object.assign(Object.assign({}, testConfig), { username: 'modified-user' });
90
+ (0, fs_1.writeFileSync)(testConfigPath, JSON.stringify(modifiedConfig));
91
+ const config2 = loader.loadConfig(); // Should still be cached
92
+ (0, vitest_1.expect)(config2).toBe(config1);
93
+ (0, vitest_1.expect)(config2.username).toBe('test-user');
94
+ const config3 = loader.reloadConfig(); // Should reload
95
+ (0, vitest_1.expect)(config3).not.toBe(config1);
96
+ (0, vitest_1.expect)(config3.username).toBe('modified-user');
97
+ });
98
+ (0, vitest_1.it)('should change config path and clear cache with setConfigPath', () => {
99
+ (0, fs_1.writeFileSync)(testConfigPath, JSON.stringify(testConfig));
100
+ (0, fs_1.writeFileSync)(testConfigPath2, JSON.stringify(Object.assign(Object.assign({}, testConfig), { username: 'user2' })));
101
+ const loader = config_loader_1.ConfigLoader.getInstance(testConfigPath);
102
+ const config1 = loader.loadConfig();
103
+ (0, vitest_1.expect)(config1.username).toBe('test-user');
104
+ loader.setConfigPath(testConfigPath2);
105
+ const config2 = loader.loadConfig();
106
+ (0, vitest_1.expect)(config2.username).toBe('user2');
107
+ (0, vitest_1.expect)(loader.getConfigPath()).toBe(testConfigPath2);
108
+ });
109
+ (0, vitest_1.it)('should throw error when config file does not exist', () => {
110
+ const loader = config_loader_1.ConfigLoader.getInstance('/nonexistent/config.json');
111
+ (0, vitest_1.expect)(() => loader.loadConfig()).toThrow('Configuration file not found');
112
+ });
113
+ (0, vitest_1.it)('should throw error when config file has invalid JSON', () => {
114
+ (0, fs_1.writeFileSync)(testConfigPath, 'invalid json content');
115
+ const loader = config_loader_1.ConfigLoader.getInstance(testConfigPath);
116
+ (0, vitest_1.expect)(() => loader.loadConfig()).toThrow('Invalid JSON in configuration file');
117
+ });
118
+ (0, vitest_1.it)('should provide fallback model configuration with warning', () => {
119
+ const minimalConfig = { username: 'test' }; // No model settings
120
+ (0, fs_1.writeFileSync)(testConfigPath, JSON.stringify(minimalConfig));
121
+ const loader = config_loader_1.ConfigLoader.getInstance(testConfigPath);
122
+ const modelConfig = loader.getModelConfig();
123
+ (0, vitest_1.expect)(modelConfig.primary).toBe('berget/deepseek-r1');
124
+ (0, vitest_1.expect)(modelConfig.small).toBe('berget/gpt-oss');
125
+ });
126
+ (0, vitest_1.it)('should provide fallback provider models with warning', () => {
127
+ const minimalConfig = { username: 'test' }; // No provider settings
128
+ (0, fs_1.writeFileSync)(testConfigPath, JSON.stringify(minimalConfig));
129
+ const loader = config_loader_1.ConfigLoader.getInstance(testConfigPath);
130
+ const providerModels = loader.getProviderModels();
131
+ (0, vitest_1.expect)(providerModels['deepseek-r1']).toBeDefined();
132
+ (0, vitest_1.expect)(providerModels['gpt-oss']).toBeDefined();
133
+ (0, vitest_1.expect)(providerModels['llama-8b']).toBeDefined();
134
+ });
135
+ (0, vitest_1.it)('should provide fallback watcher configuration with warning', () => {
136
+ const minimalConfig = { username: 'test' }; // No watcher settings
137
+ (0, fs_1.writeFileSync)(testConfigPath, JSON.stringify(minimalConfig));
138
+ const loader = config_loader_1.ConfigLoader.getInstance(testConfigPath);
139
+ const watcherConfig = loader.getWatcherConfig();
140
+ (0, vitest_1.expect)(watcherConfig.ignore).toContain('node_modules');
141
+ (0, vitest_1.expect)(watcherConfig.ignore).toContain('dist');
142
+ (0, vitest_1.expect)(watcherConfig.ignore).toContain('.git');
143
+ (0, vitest_1.expect)(watcherConfig.ignore).toContain('coverage');
144
+ });
145
+ (0, vitest_1.it)('should use actual configuration when provided', () => {
146
+ const fullConfig = Object.assign(Object.assign({}, testConfig), { model: 'custom-primary-model', small_model: 'custom-small-model', provider: {
147
+ berget: {
148
+ models: {
149
+ 'custom-model': {
150
+ name: 'Custom Model',
151
+ limit: { output: 2000, context: 4000 }
152
+ }
153
+ }
154
+ }
155
+ }, watcher: {
156
+ ignore: ['custom-ignore']
157
+ } });
158
+ (0, fs_1.writeFileSync)(testConfigPath, JSON.stringify(fullConfig));
159
+ const loader = config_loader_1.ConfigLoader.getInstance(testConfigPath);
160
+ const modelConfig = loader.getModelConfig();
161
+ (0, vitest_1.expect)(modelConfig.primary).toBe('custom-primary-model');
162
+ (0, vitest_1.expect)(modelConfig.small).toBe('custom-small-model');
163
+ const providerModels = loader.getProviderModels();
164
+ (0, vitest_1.expect)(providerModels['custom-model']).toBeDefined();
165
+ (0, vitest_1.expect)(providerModels['custom-model'].name).toBe('Custom Model');
166
+ const watcherConfig = loader.getWatcherConfig();
167
+ (0, vitest_1.expect)(watcherConfig.ignore).toEqual(['custom-ignore']);
168
+ });
169
+ (0, vitest_1.it)('should maintain consistent getter method behavior', () => {
170
+ (0, fs_1.writeFileSync)(testConfigPath, JSON.stringify(testConfig));
171
+ const loader = config_loader_1.ConfigLoader.getInstance(testConfigPath);
172
+ // All getter methods should use the same cached config
173
+ const agentConfig = loader.getAgentConfig('test');
174
+ const allAgents = loader.getAllAgentConfigs();
175
+ const commandConfigs = loader.getCommandConfigs();
176
+ const providerConfig = loader.getProviderConfig();
177
+ (0, vitest_1.expect)(agentConfig).toBeDefined();
178
+ (0, vitest_1.expect)(allAgents.test).toBeDefined();
179
+ (0, vitest_1.expect)(commandConfigs).toEqual({});
180
+ (0, vitest_1.expect)(providerConfig).toEqual({});
181
+ });
182
+ });
package/index.ts CHANGED
@@ -5,6 +5,8 @@ import { registerCommands } from './src/commands'
5
5
  import { checkBergetConfig } from './src/utils/config-checker'
6
6
  import chalk from 'chalk'
7
7
  import { version } from './package.json'
8
+ process.env.DOTENV_CONFIG_OVERRIDE = 'true'
9
+ import 'dotenv/config'
8
10
 
9
11
  // Set version and description
10
12
  program
@@ -18,7 +20,7 @@ program
18
20
  \\____/ \\___|_| \\__, |\\___|\\_\\_ \\_| |_/\\___/
19
21
  __/ |
20
22
  |___/ AI on European terms
21
- Version: ${version}`,
23
+ Version: ${version}`
22
24
  )
23
25
  .version(version, '-v, --version')
24
26
  .option('--local', 'Use local API endpoint (hidden)', false)
@@ -35,34 +37,32 @@ if (process.argv.length <= 2) {
35
37
  console.log(chalk.blue('\nWelcome to the Berget CLI!'))
36
38
  console.log(chalk.blue('Common commands:'))
37
39
  console.log(
38
- chalk.blue(` ${chalk.bold('berget auth login')} - Log in to Berget`),
40
+ chalk.blue(` ${chalk.bold('berget auth login')} - Log in to Berget`)
39
41
  )
40
42
  console.log(
41
43
  chalk.blue(
42
- ` ${chalk.bold('berget models list')} - List available AI models`,
43
- ),
44
+ ` ${chalk.bold('berget models list')} - List available AI models`
45
+ )
44
46
  )
45
47
  console.log(
46
48
  chalk.blue(
47
- ` ${chalk.bold('berget chat run')} - Start a chat session`,
48
- ),
49
+ ` ${chalk.bold('berget chat run')} - Start a chat session`
50
+ )
49
51
  )
50
52
  console.log(
51
53
  chalk.blue(
52
54
  ` ${chalk.bold(
53
- 'berget code init',
54
- )} - Initialize AI coding assistant`,
55
- ),
55
+ 'berget code init'
56
+ )} - Initialize AI coding assistant`
57
+ )
56
58
  )
57
59
  console.log(
58
- chalk.blue(
59
- ` ${chalk.bold('berget api-keys list')} - List your API keys`,
60
- ),
60
+ chalk.blue(` ${chalk.bold('berget api-keys list')} - List your API keys`)
61
61
  )
62
62
  console.log(
63
63
  chalk.blue(
64
- `\nRun ${chalk.bold('berget --help')} for a complete list of commands.`,
65
- ),
64
+ `\nRun ${chalk.bold('berget --help')} for a complete list of commands.`
65
+ )
66
66
  )
67
67
  }
68
68
 
@@ -89,15 +89,15 @@ program.on('command:*', (operands) => {
89
89
  console.log(
90
90
  chalk.yellow(
91
91
  `Did you mean? ${chalk.bold(
92
- `berget ${commonMistakes[unknownCommand]}`,
93
- )}`,
94
- ),
92
+ `berget ${commonMistakes[unknownCommand]}`
93
+ )}`
94
+ )
95
95
  )
96
96
  } else {
97
97
  // Try to find similar commands
98
98
  const availableCommands = program.commands.map((cmd) => cmd.name())
99
99
  const similarCommands = availableCommands.filter(
100
- (cmd) => cmd.includes(unknownCommand) || unknownCommand.includes(cmd),
100
+ (cmd) => cmd.includes(unknownCommand) || unknownCommand.includes(cmd)
101
101
  )
102
102
 
103
103
  if (similarCommands.length > 0) {
@@ -108,7 +108,7 @@ program.on('command:*', (operands) => {
108
108
  }
109
109
 
110
110
  console.log(
111
- chalk.blue('\nRun `berget --help` for a list of available commands.'),
111
+ chalk.blue('\nRun `berget --help` for a list of available commands.')
112
112
  )
113
113
  }
114
114
 
package/opencode.json CHANGED
@@ -58,7 +58,7 @@
58
58
  "webfetch": "allow"
59
59
  },
60
60
  "description": "Declarative GitOps infra with FluxCD, Kustomize, Helm, operators.",
61
- "prompt": "You are Berget Code DevOps agent. Voice: Scandinavian calm—precise, concise, confident. Start simple: k8s/{deployment,service,ingress}. Add FluxCD sync to repo and image automation. Use Kustomize bases/overlays (staging, production). Add dependencies via Helm from upstream sources; prefer native operators when available (CloudNativePG, cert-manager, external-dns). SemVer with -rc tags keeps CI environments current. Observability with Prometheus/Grafana. No manual kubectl in production—Git is the source of truth."
61
+ "prompt": "You are Berget Code DevOps agent. Voice: Scandinavian calm—precise, concise, confident. Start simple: k8s/{deployment,service,ingress}. Add FluxCD sync to repo and image automation. Use Kustomize bases/overlays (staging, production). Add dependencies via Helm from upstream sources; prefer native operators when available (CloudNativePG, cert-manager, external-dns). SemVer with -rc tags keeps CI environments current. Observability with Prometheus/Grafana. No manual kubectl in production—Git is the source of truth.\n\nHelm Values Configuration Process:\n1. Documentation First Approach: Always fetch official documentation from Artifact Hub/GitHub for the specific chart version before writing values. Search Artifact Hub for exact chart version documentation, check the chart's GitHub repository for official docs and examples, verify the exact version being used in the deployment.\n2. Validation Requirements: Check for available validation schemas before committing YAML files. Use Helm's built-in validation tools (helm lint, helm template). Validate against JSON schema if available for the chart. Ensure YAML syntax correctness with linters.\n3. Standard Workflow: Identify chart name and exact version. Fetch official documentation from Artifact Hub/GitHub. Check for available schemas and validation tools. Write values according to official documentation. Validate against schema (if available). Test with helm template or helm lint. Commit validated YAML files.\n4. Quality Assurance: Never commit unvalidated Helm values. Use helm dependency update when adding new charts. Test rendering with helm template --dry-run before deployment. Document any custom values with comments referencing official docs."
62
62
  },
63
63
  "app": {
64
64
  "model": "berget/deepseek-r1",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "berget",
3
- "version": "2.0.2",
3
+ "version": "2.0.4",
4
4
  "main": "dist/index.js",
5
5
  "bin": {
6
6
  "berget": "dist/index.js"
@@ -34,7 +34,7 @@ export function registerApiKeyCommands(program: Command): void {
34
34
 
35
35
  // Create a table-like format with headers
36
36
  console.log(
37
- chalk.dim('ID'.padEnd(10)) +
37
+ chalk.dim('ID (8)'.padEnd(8)) +
38
38
  chalk.dim('NAME'.padEnd(25)) +
39
39
  chalk.dim('PREFIX'.padEnd(12)) +
40
40
  chalk.dim('STATUS'.padEnd(12)) +
@@ -52,6 +52,9 @@ export function registerApiKeyCommands(program: Command): void {
52
52
  ? chalk.green('● Active')
53
53
  : chalk.red('● Inactive')
54
54
 
55
+ // Show only first 8 characters of ID for easier reading
56
+ const shortId = String(key.id).substring(0, 8)
57
+
55
58
  // Format the prefix to ensure it's not too long
56
59
  const prefixStr =
57
60
  key.prefix.length > 12
@@ -59,7 +62,7 @@ export function registerApiKeyCommands(program: Command): void {
59
62
  : key.prefix
60
63
 
61
64
  console.log(
62
- String(key.id).padEnd(10) +
65
+ shortId.padEnd(8) +
63
66
  key.name.padEnd(25) +
64
67
  prefixStr.padEnd(15) +
65
68
  status.padEnd(12) +
@@ -90,6 +93,7 @@ export function registerApiKeyCommands(program: Command): void {
90
93
  .description('Create a new API key')
91
94
  .option('--name <name>', 'Name of the API key')
92
95
  .option('--description <description>', 'Description of the API key')
96
+
93
97
  .action(async (options) => {
94
98
  try {
95
99
  if (!options.name) {
@@ -149,15 +153,96 @@ export function registerApiKeyCommands(program: Command): void {
149
153
  apiKey
150
154
  .command(ApiKeyService.COMMANDS.DELETE)
151
155
  .description('Delete an API key')
152
- .argument('<id>', 'ID of the API key to delete')
153
- .action(async (id) => {
156
+ .argument(
157
+ '<identifier>',
158
+ 'ID (first 8 chars), full ID, or name of the API key to delete',
159
+ )
160
+ .action(async (identifier) => {
154
161
  try {
155
- console.log(chalk.blue(`Deleting API key ${id}...`))
156
-
157
162
  const apiKeyService = ApiKeyService.getInstance()
158
- await apiKeyService.delete(id)
159
163
 
160
- console.log(chalk.green(`✓ API key ${id} has been deleted`))
164
+ // First, get all API keys to find the matching one
165
+ const keys = await apiKeyService.list()
166
+
167
+ // Try to find the key by:
168
+ // 1. Full ID match
169
+ // 2. Short ID (first 8 chars) match
170
+ // 3. Exact name match
171
+ // 4. Partial name match
172
+
173
+ // Check for exact matches first (full ID or exact name)
174
+ let exactMatches = keys.filter(
175
+ (key) => String(key.id) === identifier || key.name === identifier,
176
+ )
177
+
178
+ // If no exact matches, check for short ID matches
179
+ if (exactMatches.length === 0) {
180
+ exactMatches = keys.filter(
181
+ (key) => String(key.id).substring(0, 8) === identifier,
182
+ )
183
+ }
184
+
185
+ // If still no matches, check for partial name matches
186
+ if (exactMatches.length === 0) {
187
+ exactMatches = keys.filter((key) =>
188
+ key.name.toLowerCase().includes(identifier.toLowerCase()),
189
+ )
190
+ }
191
+
192
+ // Handle multiple matches
193
+ if (exactMatches.length > 1) {
194
+ console.error(
195
+ chalk.red(
196
+ `Error: Multiple API keys found matching "${identifier}"`,
197
+ ),
198
+ )
199
+ console.log('')
200
+ console.log('Please be more specific. Matching keys:')
201
+ exactMatches.forEach((key) => {
202
+ const shortId = String(key.id).substring(0, 8)
203
+ console.log(` ${shortId.padEnd(8)} ${key.name}`)
204
+ })
205
+ console.log('')
206
+ console.log(
207
+ 'Use the first 8 characters of the ID to specify which key to delete.',
208
+ )
209
+ return
210
+ }
211
+
212
+ // Handle no matches
213
+ if (exactMatches.length === 0) {
214
+ console.error(
215
+ chalk.red(`Error: No API key found matching "${identifier}"`),
216
+ )
217
+ console.log('')
218
+ console.log('Available API keys:')
219
+ keys.forEach((key) => {
220
+ const shortId = String(key.id).substring(0, 8)
221
+ console.log(` ${shortId.padEnd(8)} ${key.name}`)
222
+ })
223
+ console.log('')
224
+ console.log(
225
+ 'Use the first 8 characters of the ID, full ID, or name to delete.',
226
+ )
227
+ return
228
+ }
229
+
230
+ const matchingKey = exactMatches[0]
231
+
232
+ const keyId = String(matchingKey.id)
233
+ const shortId = keyId.substring(0, 8)
234
+
235
+ console.log(
236
+ chalk.blue(`Deleting API key ${shortId} (${matchingKey.name})...`),
237
+ )
238
+
239
+ await apiKeyService.delete(keyId)
240
+
241
+ console.log(
242
+ chalk.green(
243
+ `✓ API key ${shortId} (${matchingKey.name}) has been deleted`,
244
+ ),
245
+ )
161
246
  console.log('')
162
247
  console.log(
163
248
  chalk.dim(