berget 2.2.7 → 2.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +7 -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 +181 -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 +8 -8
- 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 +65 -30
- package/index.ts +30 -31
- package/package.json +7 -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 +180 -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 +170 -171
- 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 +7 -7
- 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/commands/api-keys.ts
CHANGED
|
@@ -1,47 +1,19 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import { ApiKeyService, ApiKey } from "../services/api-key-service";
|
|
4
|
-
import { handleError } from "../utils/error-handler";
|
|
5
|
-
import { DefaultApiKeyManager } from "../utils/default-api-key";
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { Command } from 'commander';
|
|
6
3
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const now = new Date();
|
|
11
|
-
const diffTime = Math.abs(now.getTime() - date.getTime());
|
|
12
|
-
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
|
13
|
-
|
|
14
|
-
if (diffDays === 0) return chalk.green("Today");
|
|
15
|
-
if (diffDays === 1) return chalk.yellow("Yesterday");
|
|
16
|
-
if (diffDays < 7) return chalk.yellow(`${diffDays} days ago`);
|
|
17
|
-
if (diffDays < 30) return chalk.blue(`${Math.floor(diffDays / 7)} weeks ago`);
|
|
18
|
-
if (diffDays < 365) return chalk.magenta(`${Math.floor(diffDays / 30)} months ago`);
|
|
19
|
-
return chalk.gray(`${Math.floor(diffDays / 365)} years ago`);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function formatLastUsed(dateString: string): string {
|
|
23
|
-
const date = new Date(dateString);
|
|
24
|
-
const now = new Date();
|
|
25
|
-
const diffTime = Math.abs(now.getTime() - date.getTime());
|
|
26
|
-
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
|
27
|
-
|
|
28
|
-
if (diffDays === 0) return chalk.green("Today");
|
|
29
|
-
if (diffDays === 1) return chalk.yellow("Yesterday");
|
|
30
|
-
if (diffDays < 7) return chalk.yellow(`${diffDays} days ago`);
|
|
31
|
-
if (diffDays < 30) return chalk.blue(`${Math.floor(diffDays / 7)} weeks ago`);
|
|
32
|
-
if (diffDays < 365) return chalk.magenta(`${Math.floor(diffDays / 30)} months ago`);
|
|
33
|
-
return chalk.gray(`${Math.floor(diffDays / 365)} years ago`);
|
|
34
|
-
}
|
|
4
|
+
import { ApiKey, ApiKeyService } from '../services/api-key-service';
|
|
5
|
+
import { DefaultApiKeyManager } from '../utils/default-api-key';
|
|
6
|
+
import { handleError } from '../utils/error-handler';
|
|
35
7
|
|
|
36
8
|
/**
|
|
37
9
|
* Register API key commands
|
|
38
10
|
*/
|
|
39
11
|
export function registerApiKeyCommands(program: Command): void {
|
|
40
|
-
const apiKey = program.command(ApiKeyService.COMMAND_GROUP).description(
|
|
12
|
+
const apiKey = program.command(ApiKeyService.COMMAND_GROUP).description('Manage API keys');
|
|
41
13
|
|
|
42
14
|
apiKey
|
|
43
15
|
.command(ApiKeyService.COMMANDS.LIST)
|
|
44
|
-
.description(
|
|
16
|
+
.description('List all API keys')
|
|
45
17
|
.action(async () => {
|
|
46
18
|
try {
|
|
47
19
|
const apiKeyService = ApiKeyService.getInstance();
|
|
@@ -49,13 +21,15 @@ export function registerApiKeyCommands(program: Command): void {
|
|
|
49
21
|
|
|
50
22
|
if (keys.length === 0) {
|
|
51
23
|
console.log(
|
|
52
|
-
chalk.yellow(
|
|
24
|
+
chalk.yellow(
|
|
25
|
+
'No API keys found. Create one with `berget api-key create --name <name>`',
|
|
26
|
+
),
|
|
53
27
|
);
|
|
54
28
|
return;
|
|
55
29
|
}
|
|
56
30
|
|
|
57
|
-
console.log(chalk.bold(
|
|
58
|
-
console.log(
|
|
31
|
+
console.log(chalk.bold('🔑 Your API keys:'));
|
|
32
|
+
console.log('');
|
|
59
33
|
|
|
60
34
|
// Create a more readable table with better spacing and colors
|
|
61
35
|
const idWidth = 10;
|
|
@@ -66,116 +40,118 @@ export function registerApiKeyCommands(program: Command): void {
|
|
|
66
40
|
const usedWidth = 15;
|
|
67
41
|
|
|
68
42
|
console.log(
|
|
69
|
-
chalk.dim(
|
|
70
|
-
chalk.dim(
|
|
71
|
-
chalk.dim(
|
|
72
|
-
chalk.dim(
|
|
73
|
-
chalk.dim(
|
|
74
|
-
chalk.dim(
|
|
43
|
+
chalk.dim('ID'.padEnd(idWidth)) +
|
|
44
|
+
chalk.dim('NAME'.padEnd(nameWidth)) +
|
|
45
|
+
chalk.dim('PREFIX'.padEnd(prefixWidth)) +
|
|
46
|
+
chalk.dim('STATUS'.padEnd(statusWidth)) +
|
|
47
|
+
chalk.dim('CREATED'.padEnd(createdWidth)) +
|
|
48
|
+
chalk.dim('LAST USED'),
|
|
75
49
|
);
|
|
76
50
|
|
|
77
51
|
console.log(
|
|
78
52
|
chalk.dim(
|
|
79
|
-
|
|
80
|
-
idWidth + nameWidth + prefixWidth + statusWidth + createdWidth + usedWidth + 5
|
|
81
|
-
)
|
|
82
|
-
)
|
|
53
|
+
'─'.repeat(
|
|
54
|
+
idWidth + nameWidth + prefixWidth + statusWidth + createdWidth + usedWidth + 5,
|
|
55
|
+
),
|
|
56
|
+
),
|
|
83
57
|
);
|
|
84
58
|
|
|
85
59
|
keys.forEach((key: ApiKey) => {
|
|
86
|
-
const lastUsed = key.lastUsed ? formatLastUsed(key.lastUsed) : chalk.yellow(
|
|
87
|
-
const status = key.active ? chalk.green(
|
|
60
|
+
const lastUsed = key.lastUsed ? formatLastUsed(key.lastUsed) : chalk.yellow('Never used');
|
|
61
|
+
const status = key.active ? chalk.green('● Active') : chalk.red('● Inactive');
|
|
88
62
|
|
|
89
63
|
// Show only first 8 characters of ID for easier reading
|
|
90
|
-
const shortId = chalk.cyan(String(key.id).
|
|
64
|
+
const shortId = chalk.cyan(String(key.id).slice(0, 8));
|
|
91
65
|
|
|
92
66
|
// Format the prefix with better truncation
|
|
93
|
-
const
|
|
67
|
+
const prefixString =
|
|
94
68
|
key.prefix.length > prefixWidth
|
|
95
|
-
? key.prefix.
|
|
69
|
+
? key.prefix.slice(0, Math.max(0, prefixWidth - 3)) + '...'
|
|
96
70
|
: chalk.gray(key.prefix);
|
|
97
71
|
|
|
98
72
|
// Truncate name if too long
|
|
99
|
-
const
|
|
100
|
-
key.name.length > nameWidth
|
|
73
|
+
const nameString =
|
|
74
|
+
key.name.length > nameWidth
|
|
75
|
+
? key.name.slice(0, Math.max(0, nameWidth - 3)) + '...'
|
|
76
|
+
: key.name;
|
|
101
77
|
|
|
102
78
|
// Format created date
|
|
103
79
|
const createdDate = formatDate(key.created);
|
|
104
80
|
|
|
105
81
|
console.log(
|
|
106
82
|
shortId.padEnd(idWidth) +
|
|
107
|
-
|
|
108
|
-
|
|
83
|
+
nameString.padEnd(nameWidth) +
|
|
84
|
+
prefixString.padEnd(prefixWidth) +
|
|
109
85
|
status.padEnd(statusWidth) +
|
|
110
86
|
createdDate.padEnd(createdWidth) +
|
|
111
|
-
lastUsed
|
|
87
|
+
lastUsed,
|
|
112
88
|
);
|
|
113
89
|
});
|
|
114
90
|
|
|
115
|
-
console.log(
|
|
116
|
-
console.log(chalk.dim(
|
|
117
|
-
console.log(chalk.dim(
|
|
118
|
-
console.log(chalk.dim(
|
|
91
|
+
console.log('');
|
|
92
|
+
console.log(chalk.dim('Use `berget api-key create --name <name>` to create a new API key'));
|
|
93
|
+
console.log(chalk.dim('Use `berget api-key delete <id>` to delete an API key'));
|
|
94
|
+
console.log(chalk.dim('Use `berget api-key rotate <id>` to rotate an API key'));
|
|
119
95
|
} catch (error) {
|
|
120
|
-
handleError(
|
|
96
|
+
handleError('Failed to list API keys', error);
|
|
121
97
|
}
|
|
122
98
|
});
|
|
123
99
|
|
|
124
100
|
apiKey
|
|
125
101
|
.command(ApiKeyService.COMMANDS.CREATE)
|
|
126
|
-
.description(
|
|
127
|
-
.option(
|
|
128
|
-
.option(
|
|
102
|
+
.description('Create a new API key')
|
|
103
|
+
.option('--name <name>', 'Name of the API key')
|
|
104
|
+
.option('--description <description>', 'Description of the API key')
|
|
129
105
|
|
|
130
|
-
.action(async options => {
|
|
106
|
+
.action(async (options) => {
|
|
131
107
|
try {
|
|
132
108
|
if (!options.name) {
|
|
133
|
-
console.error(chalk.red(
|
|
134
|
-
console.log(
|
|
135
|
-
console.log(
|
|
109
|
+
console.error(chalk.red('Error: --name is required'));
|
|
110
|
+
console.log('');
|
|
111
|
+
console.log('Usage: berget api-key create --name <name> [--description <description>]');
|
|
136
112
|
return;
|
|
137
113
|
}
|
|
138
114
|
|
|
139
|
-
console.log(chalk.blue(
|
|
115
|
+
console.log(chalk.blue('Creating API key...'));
|
|
140
116
|
|
|
141
117
|
const apiKeyService = ApiKeyService.getInstance();
|
|
142
118
|
const result = await apiKeyService.create({
|
|
143
|
-
name: options.name,
|
|
144
119
|
description: options.description,
|
|
120
|
+
name: options.name,
|
|
145
121
|
});
|
|
146
122
|
|
|
147
|
-
console.log(
|
|
148
|
-
console.log(chalk.green(
|
|
149
|
-
console.log(
|
|
150
|
-
console.log(chalk.bold(
|
|
151
|
-
console.log(
|
|
152
|
-
console.log(`${chalk.dim(
|
|
153
|
-
console.log(`${chalk.dim(
|
|
123
|
+
console.log('');
|
|
124
|
+
console.log(chalk.green('✓ API key created'));
|
|
125
|
+
console.log('');
|
|
126
|
+
console.log(chalk.bold('API key details:'));
|
|
127
|
+
console.log('');
|
|
128
|
+
console.log(`${chalk.dim('ID:')} ${result.id}`);
|
|
129
|
+
console.log(`${chalk.dim('Name:')} ${result.name}`);
|
|
154
130
|
if (result.description) {
|
|
155
|
-
console.log(`${chalk.dim(
|
|
131
|
+
console.log(`${chalk.dim('Description:')} ${result.description}`);
|
|
156
132
|
}
|
|
157
|
-
console.log(`${chalk.dim(
|
|
158
|
-
console.log(
|
|
159
|
-
console.log(chalk.bold(
|
|
133
|
+
console.log(`${chalk.dim('Created:')} ${new Date(result.created).toLocaleString()}`);
|
|
134
|
+
console.log('');
|
|
135
|
+
console.log(chalk.bold('API key:'));
|
|
160
136
|
console.log(chalk.cyan(result.key));
|
|
161
|
-
console.log(
|
|
162
|
-
console.log(chalk.yellow(
|
|
163
|
-
console.log(chalk.yellow(
|
|
137
|
+
console.log('');
|
|
138
|
+
console.log(chalk.yellow('⚠️ IMPORTANT: Save this API key in a secure location.'));
|
|
139
|
+
console.log(chalk.yellow(' It will not be displayed again.'));
|
|
164
140
|
|
|
165
|
-
console.log(
|
|
141
|
+
console.log('');
|
|
166
142
|
console.log(
|
|
167
|
-
chalk.dim(
|
|
143
|
+
chalk.dim('Use this key in your applications to authenticate with the Berget API.'),
|
|
168
144
|
);
|
|
169
145
|
} catch (error) {
|
|
170
|
-
handleError(
|
|
146
|
+
handleError('Failed to create API key', error);
|
|
171
147
|
}
|
|
172
148
|
});
|
|
173
149
|
|
|
174
150
|
apiKey
|
|
175
151
|
.command(ApiKeyService.COMMANDS.DELETE)
|
|
176
|
-
.description(
|
|
177
|
-
.argument(
|
|
178
|
-
.action(async identifier => {
|
|
152
|
+
.description('Delete an API key')
|
|
153
|
+
.argument('<identifier>', 'ID (first 8 chars), full ID, or name of the API key to delete')
|
|
154
|
+
.action(async (identifier) => {
|
|
179
155
|
try {
|
|
180
156
|
const apiKeyService = ApiKeyService.getInstance();
|
|
181
157
|
|
|
@@ -190,110 +166,110 @@ export function registerApiKeyCommands(program: Command): void {
|
|
|
190
166
|
|
|
191
167
|
// Check for exact matches first (full ID or exact name)
|
|
192
168
|
let exactMatches = keys.filter(
|
|
193
|
-
key => String(key.id) === identifier || key.name === identifier
|
|
169
|
+
(key) => String(key.id) === identifier || key.name === identifier,
|
|
194
170
|
);
|
|
195
171
|
|
|
196
172
|
// If no exact matches, check for short ID matches
|
|
197
173
|
if (exactMatches.length === 0) {
|
|
198
|
-
exactMatches = keys.filter(key => String(key.id).
|
|
174
|
+
exactMatches = keys.filter((key) => String(key.id).slice(0, 8) === identifier);
|
|
199
175
|
}
|
|
200
176
|
|
|
201
177
|
// If still no matches, check for partial name matches
|
|
202
178
|
if (exactMatches.length === 0) {
|
|
203
|
-
exactMatches = keys.filter(key =>
|
|
204
|
-
key.name.toLowerCase().includes(identifier.toLowerCase())
|
|
179
|
+
exactMatches = keys.filter((key) =>
|
|
180
|
+
key.name.toLowerCase().includes(identifier.toLowerCase()),
|
|
205
181
|
);
|
|
206
182
|
}
|
|
207
183
|
|
|
208
184
|
// Handle multiple matches
|
|
209
185
|
if (exactMatches.length > 1) {
|
|
210
186
|
console.error(chalk.red(`Error: Multiple API keys found matching "${identifier}"`));
|
|
211
|
-
console.log(
|
|
212
|
-
console.log(
|
|
213
|
-
|
|
214
|
-
const shortId = String(key.id).
|
|
187
|
+
console.log('');
|
|
188
|
+
console.log('Please be more specific. Matching keys:');
|
|
189
|
+
for (const key of exactMatches) {
|
|
190
|
+
const shortId = String(key.id).slice(0, 8);
|
|
215
191
|
console.log(` ${shortId.padEnd(8)} ${key.name}`);
|
|
216
|
-
}
|
|
217
|
-
console.log(
|
|
218
|
-
console.log(
|
|
192
|
+
}
|
|
193
|
+
console.log('');
|
|
194
|
+
console.log('Use the first 8 characters of the ID to specify which key to delete.');
|
|
219
195
|
return;
|
|
220
196
|
}
|
|
221
197
|
|
|
222
198
|
// Handle no matches
|
|
223
199
|
if (exactMatches.length === 0) {
|
|
224
200
|
console.error(chalk.red(`Error: No API key found matching "${identifier}"`));
|
|
225
|
-
console.log(
|
|
226
|
-
console.log(
|
|
227
|
-
|
|
228
|
-
const shortId = String(key.id).
|
|
201
|
+
console.log('');
|
|
202
|
+
console.log('Available API keys:');
|
|
203
|
+
for (const key of keys) {
|
|
204
|
+
const shortId = String(key.id).slice(0, 8);
|
|
229
205
|
console.log(` ${shortId.padEnd(8)} ${key.name}`);
|
|
230
|
-
}
|
|
231
|
-
console.log(
|
|
232
|
-
console.log(
|
|
206
|
+
}
|
|
207
|
+
console.log('');
|
|
208
|
+
console.log('Use the first 8 characters of the ID, full ID, or name to delete.');
|
|
233
209
|
return;
|
|
234
210
|
}
|
|
235
211
|
|
|
236
212
|
const matchingKey = exactMatches[0];
|
|
237
213
|
|
|
238
214
|
const keyId = String(matchingKey.id);
|
|
239
|
-
const shortId = keyId.
|
|
215
|
+
const shortId = keyId.slice(0, 8);
|
|
240
216
|
|
|
241
217
|
console.log(chalk.blue(`Deleting API key ${shortId} (${matchingKey.name})...`));
|
|
242
218
|
|
|
243
219
|
await apiKeyService.delete(keyId);
|
|
244
220
|
|
|
245
221
|
console.log(chalk.green(`✓ API key ${shortId} (${matchingKey.name}) has been deleted`));
|
|
246
|
-
console.log(
|
|
222
|
+
console.log('');
|
|
247
223
|
console.log(
|
|
248
|
-
chalk.dim(
|
|
224
|
+
chalk.dim('Applications using this key will no longer be able to authenticate.'),
|
|
249
225
|
);
|
|
250
|
-
console.log(chalk.dim(
|
|
226
|
+
console.log(chalk.dim('Use `berget api-key list` to see your remaining API keys.'));
|
|
251
227
|
} catch (error) {
|
|
252
|
-
handleError(
|
|
228
|
+
handleError('Failed to delete API key', error);
|
|
253
229
|
}
|
|
254
230
|
});
|
|
255
231
|
|
|
256
232
|
apiKey
|
|
257
233
|
.command(ApiKeyService.COMMANDS.ROTATE)
|
|
258
|
-
.description(
|
|
259
|
-
.argument(
|
|
260
|
-
.action(async id => {
|
|
234
|
+
.description('Rotate an API key (creates a new one and invalidates the old one)')
|
|
235
|
+
.argument('<id>', 'ID of the API key to rotate')
|
|
236
|
+
.action(async (id) => {
|
|
261
237
|
try {
|
|
262
238
|
console.log(chalk.blue(`Rotating API key ${id}...`));
|
|
263
|
-
console.log(chalk.dim(
|
|
239
|
+
console.log(chalk.dim('This will invalidate the old key and generate a new one.'));
|
|
264
240
|
|
|
265
241
|
const apiKeyService = ApiKeyService.getInstance();
|
|
266
242
|
const result = await apiKeyService.rotate(id);
|
|
267
243
|
|
|
268
|
-
console.log(
|
|
269
|
-
console.log(chalk.green(
|
|
270
|
-
console.log(
|
|
271
|
-
console.log(chalk.bold(
|
|
272
|
-
console.log(
|
|
273
|
-
console.log(`${chalk.dim(
|
|
274
|
-
console.log(`${chalk.dim(
|
|
244
|
+
console.log('');
|
|
245
|
+
console.log(chalk.green('✓ API key rotated'));
|
|
246
|
+
console.log('');
|
|
247
|
+
console.log(chalk.bold('New API key details:'));
|
|
248
|
+
console.log('');
|
|
249
|
+
console.log(`${chalk.dim('ID:')} ${result.id}`);
|
|
250
|
+
console.log(`${chalk.dim('Name:')} ${result.name}`);
|
|
275
251
|
if (result.description) {
|
|
276
|
-
console.log(`${chalk.dim(
|
|
252
|
+
console.log(`${chalk.dim('Description:')} ${result.description}`);
|
|
277
253
|
}
|
|
278
|
-
console.log(`${chalk.dim(
|
|
279
|
-
console.log(
|
|
280
|
-
console.log(chalk.bold(
|
|
254
|
+
console.log(`${chalk.dim('Created:')} ${new Date(result.created).toLocaleString()}`);
|
|
255
|
+
console.log('');
|
|
256
|
+
console.log(chalk.bold('New API key:'));
|
|
281
257
|
console.log(chalk.cyan(result.key));
|
|
282
|
-
console.log(
|
|
283
|
-
console.log(chalk.yellow(
|
|
284
|
-
console.log(chalk.yellow(
|
|
285
|
-
console.log(chalk.yellow(
|
|
258
|
+
console.log('');
|
|
259
|
+
console.log(chalk.yellow('⚠️ IMPORTANT: Update your applications with this new API key.'));
|
|
260
|
+
console.log(chalk.yellow(' The old key has been invalidated and will no longer work.'));
|
|
261
|
+
console.log(chalk.yellow(' This new key will not be displayed again.'));
|
|
286
262
|
} catch (error) {
|
|
287
|
-
handleError(
|
|
263
|
+
handleError('Failed to rotate API key', error);
|
|
288
264
|
}
|
|
289
265
|
});
|
|
290
266
|
|
|
291
267
|
apiKey
|
|
292
268
|
.command(ApiKeyService.COMMANDS.DESCRIBE)
|
|
293
|
-
.description(
|
|
294
|
-
.argument(
|
|
295
|
-
.option(
|
|
296
|
-
.option(
|
|
269
|
+
.description('Show usage statistics for an API key')
|
|
270
|
+
.argument('<id>', 'ID of the API key')
|
|
271
|
+
.option('--start <date>', 'Start date (YYYY-MM-DD)')
|
|
272
|
+
.option('--end <date>', 'End date (YYYY-MM-DD)')
|
|
297
273
|
.action(async (id, _options) => {
|
|
298
274
|
try {
|
|
299
275
|
console.log(chalk.blue(`Fetching usage statistics for API key ${id}...`));
|
|
@@ -301,41 +277,41 @@ export function registerApiKeyCommands(program: Command): void {
|
|
|
301
277
|
const apiKeyService = ApiKeyService.getInstance();
|
|
302
278
|
const usage = await apiKeyService.describe(id);
|
|
303
279
|
|
|
304
|
-
console.log(
|
|
280
|
+
console.log('');
|
|
305
281
|
console.log(chalk.bold(`Usage statistics for API key: ${usage.name} (${id})`));
|
|
306
|
-
console.log(
|
|
282
|
+
console.log('');
|
|
307
283
|
|
|
308
284
|
// Period information
|
|
309
285
|
console.log(chalk.dim(`Period: ${usage.period.start} to ${usage.period.end}`));
|
|
310
|
-
console.log(
|
|
286
|
+
console.log('');
|
|
311
287
|
|
|
312
288
|
// Request statistics
|
|
313
|
-
console.log(chalk.bold(
|
|
289
|
+
console.log(chalk.bold('Request statistics:'));
|
|
314
290
|
console.log(`Total requests: ${chalk.cyan(usage.requests.total.toLocaleString())}`);
|
|
315
291
|
|
|
316
292
|
// Daily breakdown if available
|
|
317
293
|
if (usage.requests.daily && usage.requests.daily.length > 0) {
|
|
318
|
-
console.log(
|
|
319
|
-
console.log(chalk.bold(
|
|
320
|
-
console.log(chalk.dim(
|
|
321
|
-
console.log(chalk.dim(
|
|
294
|
+
console.log('');
|
|
295
|
+
console.log(chalk.bold('Daily breakdown:'));
|
|
296
|
+
console.log(chalk.dim('─'.repeat(30)));
|
|
297
|
+
console.log(chalk.dim('DATE'.padEnd(12) + 'REQUESTS'));
|
|
322
298
|
|
|
323
|
-
usage.requests.daily.forEach((day: {
|
|
299
|
+
usage.requests.daily.forEach((day: { count: number; date: string }) => {
|
|
324
300
|
console.log(`${day.date.padEnd(12)}${day.count.toLocaleString()}`);
|
|
325
301
|
});
|
|
326
302
|
}
|
|
327
303
|
|
|
328
304
|
// Model usage if available
|
|
329
305
|
if (usage.models && usage.models.length > 0) {
|
|
330
|
-
console.log(
|
|
331
|
-
console.log(chalk.bold(
|
|
332
|
-
console.log(chalk.dim(
|
|
306
|
+
console.log('');
|
|
307
|
+
console.log(chalk.bold('Model usage:'));
|
|
308
|
+
console.log(chalk.dim('─'.repeat(70)));
|
|
333
309
|
console.log(
|
|
334
|
-
chalk.dim(
|
|
335
|
-
chalk.dim(
|
|
336
|
-
chalk.dim(
|
|
337
|
-
chalk.dim(
|
|
338
|
-
chalk.dim(
|
|
310
|
+
chalk.dim('MODEL'.padEnd(20)) +
|
|
311
|
+
chalk.dim('REQUESTS'.padEnd(10)) +
|
|
312
|
+
chalk.dim('INPUT'.padEnd(12)) +
|
|
313
|
+
chalk.dim('OUTPUT'.padEnd(12)) +
|
|
314
|
+
chalk.dim('TOTAL TOKENS'),
|
|
339
315
|
);
|
|
340
316
|
|
|
341
317
|
usage.models.forEach(
|
|
@@ -353,30 +329,30 @@ export function registerApiKeyCommands(program: Command): void {
|
|
|
353
329
|
model.requests.toString().padEnd(10) +
|
|
354
330
|
model.tokens.input.toLocaleString().padEnd(12) +
|
|
355
331
|
model.tokens.output.toLocaleString().padEnd(12) +
|
|
356
|
-
model.tokens.total.toLocaleString()
|
|
332
|
+
model.tokens.total.toLocaleString(),
|
|
357
333
|
);
|
|
358
|
-
}
|
|
334
|
+
},
|
|
359
335
|
);
|
|
360
336
|
}
|
|
361
337
|
|
|
362
|
-
console.log(
|
|
338
|
+
console.log('');
|
|
363
339
|
console.log(
|
|
364
|
-
chalk.dim(
|
|
340
|
+
chalk.dim('Use these statistics to understand your API usage and optimize your costs.'),
|
|
365
341
|
);
|
|
366
342
|
} catch (error) {
|
|
367
|
-
handleError(
|
|
343
|
+
handleError('Failed to get API key usage', error);
|
|
368
344
|
}
|
|
369
345
|
});
|
|
370
346
|
|
|
371
347
|
apiKey
|
|
372
348
|
.command(ApiKeyService.COMMANDS.SET_DEFAULT)
|
|
373
|
-
.description(
|
|
374
|
-
.argument(
|
|
375
|
-
.action(async id => {
|
|
349
|
+
.description('Set an API key as the default for chat commands')
|
|
350
|
+
.argument('<id>', 'ID of the API key to set as default')
|
|
351
|
+
.action(async (id) => {
|
|
376
352
|
try {
|
|
377
353
|
const apiKeyService = ApiKeyService.getInstance();
|
|
378
354
|
const keys = await apiKeyService.list();
|
|
379
|
-
const selectedKey = keys.find(key => key.id.toString() === id);
|
|
355
|
+
const selectedKey = keys.find((key) => key.id.toString() === id);
|
|
380
356
|
|
|
381
357
|
if (!selectedKey) {
|
|
382
358
|
console.error(chalk.red(`Error: API key with ID ${id} not found`));
|
|
@@ -393,46 +369,65 @@ export function registerApiKeyCommands(program: Command): void {
|
|
|
393
369
|
id,
|
|
394
370
|
selectedKey.name,
|
|
395
371
|
selectedKey.prefix,
|
|
396
|
-
rotatedKey.key
|
|
372
|
+
rotatedKey.key,
|
|
397
373
|
);
|
|
398
374
|
|
|
399
375
|
console.log(
|
|
400
|
-
chalk.green(`✓ API key "${selectedKey.name}" set as default for chat commands`)
|
|
376
|
+
chalk.green(`✓ API key "${selectedKey.name}" set as default for chat commands`),
|
|
401
377
|
);
|
|
402
|
-
console.log(
|
|
403
|
-
console.log(chalk.dim(
|
|
404
|
-
console.log(chalk.dim(
|
|
378
|
+
console.log('');
|
|
379
|
+
console.log(chalk.dim('This API key will be used by default when running chat commands'));
|
|
380
|
+
console.log(chalk.dim('You can override it with --api-key or --api-key-id options'));
|
|
405
381
|
} catch (error) {
|
|
406
|
-
handleError(
|
|
382
|
+
handleError('Failed to set default API key', error);
|
|
407
383
|
}
|
|
408
384
|
});
|
|
409
385
|
|
|
410
386
|
apiKey
|
|
411
387
|
.command(ApiKeyService.COMMANDS.GET_DEFAULT)
|
|
412
|
-
.description(
|
|
388
|
+
.description('Show the current default API key')
|
|
413
389
|
.action(() => {
|
|
414
390
|
try {
|
|
415
391
|
const defaultApiKeyManager = DefaultApiKeyManager.getInstance();
|
|
416
392
|
const defaultApiKeyData = defaultApiKeyManager.getDefaultApiKeyData();
|
|
417
393
|
|
|
418
394
|
if (!defaultApiKeyData) {
|
|
419
|
-
console.log(chalk.yellow(
|
|
420
|
-
console.log(
|
|
421
|
-
console.log(
|
|
422
|
-
console.log(chalk.cyan(
|
|
395
|
+
console.log(chalk.yellow('No default API key set'));
|
|
396
|
+
console.log('');
|
|
397
|
+
console.log('To set a default API key, run:');
|
|
398
|
+
console.log(chalk.cyan(' berget api-keys set-default <id>'));
|
|
423
399
|
return;
|
|
424
400
|
}
|
|
425
401
|
|
|
426
|
-
console.log(chalk.bold(
|
|
427
|
-
console.log(
|
|
428
|
-
console.log(`${chalk.dim(
|
|
429
|
-
console.log(`${chalk.dim(
|
|
430
|
-
console.log(`${chalk.dim(
|
|
431
|
-
console.log(
|
|
432
|
-
console.log(chalk.dim(
|
|
433
|
-
console.log(chalk.dim(
|
|
402
|
+
console.log(chalk.bold('Default API key:'));
|
|
403
|
+
console.log('');
|
|
404
|
+
console.log(`${chalk.dim('ID:')} ${defaultApiKeyData.id}`);
|
|
405
|
+
console.log(`${chalk.dim('Name:')} ${defaultApiKeyData.name}`);
|
|
406
|
+
console.log(`${chalk.dim('Prefix:')} ${defaultApiKeyData.prefix}`);
|
|
407
|
+
console.log('');
|
|
408
|
+
console.log(chalk.dim('This API key will be used by default when running chat commands'));
|
|
409
|
+
console.log(chalk.dim('You can override it with --api-key or --api-key-id options'));
|
|
434
410
|
} catch (error) {
|
|
435
|
-
handleError(
|
|
411
|
+
handleError('Failed to get default API key', error);
|
|
436
412
|
}
|
|
437
413
|
});
|
|
438
414
|
}
|
|
415
|
+
|
|
416
|
+
// Helper functions for better date formatting
|
|
417
|
+
function formatDate(dateString: string): string {
|
|
418
|
+
const date = new Date(dateString);
|
|
419
|
+
const now = new Date();
|
|
420
|
+
const diffTime = Math.abs(now.getTime() - date.getTime());
|
|
421
|
+
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
|
422
|
+
|
|
423
|
+
if (diffDays === 0) return chalk.green('Today');
|
|
424
|
+
if (diffDays === 1) return chalk.yellow('Yesterday');
|
|
425
|
+
if (diffDays < 7) return chalk.yellow(`${diffDays} days ago`);
|
|
426
|
+
if (diffDays < 30) return chalk.blue(`${Math.floor(diffDays / 7)} weeks ago`);
|
|
427
|
+
if (diffDays < 365) return chalk.magenta(`${Math.floor(diffDays / 30)} months ago`);
|
|
428
|
+
return chalk.gray(`${Math.floor(diffDays / 365)} years ago`);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function formatLastUsed(dateString: string): string {
|
|
432
|
+
return formatDate(dateString);
|
|
433
|
+
}
|