berget 2.2.6 → 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 +2 -2
- package/.github/workflows/test.yml +10 -4
- package/.husky/pre-commit +1 -0
- package/.prettierignore +15 -0
- package/.prettierrc +7 -3
- package/CONTRIBUTING.md +38 -0
- package/README.md +2 -148
- package/dist/index.js +10 -11
- package/dist/package.json +30 -2
- package/dist/src/agents/app.js +28 -0
- package/dist/src/agents/backend.js +25 -0
- package/dist/src/agents/devops.js +34 -0
- package/dist/src/agents/frontend.js +25 -0
- package/dist/src/agents/fullstack.js +25 -0
- package/dist/src/agents/index.js +61 -0
- package/dist/src/agents/quality.js +70 -0
- package/dist/src/agents/security.js +26 -0
- package/dist/src/agents/types.js +2 -0
- package/dist/src/client.js +97 -117
- package/dist/src/commands/api-keys.js +75 -90
- package/dist/src/commands/auth.js +7 -16
- package/dist/src/commands/autocomplete.js +1 -1
- package/dist/src/commands/billing.js +6 -17
- package/dist/src/commands/chat.js +68 -101
- package/dist/src/commands/clusters.js +9 -18
- package/dist/src/commands/code/__tests__/auth-sync.test.js +351 -0
- package/dist/src/commands/code/__tests__/fake-api-key-service.js +13 -0
- package/dist/src/commands/code/__tests__/fake-auth-service.js +47 -0
- package/dist/src/commands/code/__tests__/fake-command-runner.js +21 -34
- package/dist/src/commands/code/__tests__/fake-file-store.js +20 -33
- package/dist/src/commands/code/__tests__/fake-prompter.js +83 -57
- package/dist/src/commands/code/__tests__/setup-flow.test.js +359 -92
- package/dist/src/commands/code/adapters/clack-prompter.js +15 -22
- package/dist/src/commands/code/adapters/fs-file-store.js +26 -40
- package/dist/src/commands/code/adapters/spawn-command-runner.js +27 -37
- package/dist/src/commands/code/auth-sync.js +270 -0
- package/dist/src/commands/code/errors.js +12 -9
- package/dist/src/commands/code/ports/auth-services.js +2 -0
- package/dist/src/commands/code/setup.js +387 -281
- package/dist/src/commands/code.js +205 -332
- package/dist/src/commands/index.js +5 -5
- package/dist/src/commands/models.js +6 -17
- package/dist/src/commands/users.js +5 -16
- package/dist/src/constants/command-structure.js +104 -104
- package/dist/src/services/api-key-service.js +132 -157
- package/dist/src/services/auth-service.js +89 -342
- package/dist/src/services/browser-auth.js +268 -0
- package/dist/src/services/chat-service.js +371 -401
- package/dist/src/services/cluster-service.js +47 -62
- package/dist/src/services/collaborator-service.js +10 -25
- package/dist/src/services/flux-service.js +14 -29
- package/dist/src/services/helm-service.js +10 -25
- package/dist/src/services/kubectl-service.js +16 -33
- package/dist/src/utils/config-checker.js +3 -3
- package/dist/src/utils/config-loader.js +95 -95
- package/dist/src/utils/default-api-key.js +124 -134
- package/dist/src/utils/env-manager.js +55 -66
- package/dist/src/utils/error-handler.js +20 -21
- package/dist/src/utils/logger.js +72 -65
- package/dist/src/utils/markdown-renderer.js +27 -27
- package/dist/src/utils/opencode-validator.js +63 -68
- package/dist/src/utils/token-manager.js +74 -45
- package/dist/tests/commands/chat.test.js +16 -25
- package/dist/tests/commands/code.test.js +95 -104
- package/dist/tests/utils/config-loader.test.js +48 -48
- package/dist/tests/utils/env-manager.test.js +43 -52
- package/dist/tests/utils/opencode-validator.test.js +22 -21
- package/dist/vitest.config.js +1 -1
- package/eslint.config.mjs +67 -0
- package/index.ts +35 -42
- package/package.json +30 -2
- package/src/agents/app.ts +27 -0
- package/src/agents/backend.ts +24 -0
- package/src/agents/devops.ts +33 -0
- package/src/agents/frontend.ts +24 -0
- package/src/agents/fullstack.ts +24 -0
- package/src/agents/index.ts +73 -0
- package/src/agents/quality.ts +69 -0
- package/src/agents/security.ts +26 -0
- package/src/agents/types.ts +17 -0
- package/src/client.ts +118 -152
- package/src/commands/api-keys.ts +241 -333
- package/src/commands/auth.ts +22 -27
- package/src/commands/autocomplete.ts +9 -9
- package/src/commands/billing.ts +20 -24
- package/src/commands/chat.ts +248 -338
- package/src/commands/clusters.ts +27 -26
- package/src/commands/code/__tests__/auth-sync.test.ts +482 -0
- package/src/commands/code/__tests__/fake-api-key-service.ts +13 -0
- package/src/commands/code/__tests__/fake-auth-service.ts +50 -0
- package/src/commands/code/__tests__/fake-command-runner.ts +45 -42
- package/src/commands/code/__tests__/fake-file-store.ts +32 -23
- package/src/commands/code/__tests__/fake-prompter.ts +116 -77
- package/src/commands/code/__tests__/setup-flow.test.ts +624 -268
- package/src/commands/code/adapters/clack-prompter.ts +53 -39
- package/src/commands/code/adapters/fs-file-store.ts +32 -27
- package/src/commands/code/adapters/spawn-command-runner.ts +38 -29
- package/src/commands/code/auth-sync.ts +329 -0
- package/src/commands/code/errors.ts +18 -18
- package/src/commands/code/ports/auth-services.ts +14 -0
- package/src/commands/code/ports/command-runner.ts +8 -4
- package/src/commands/code/ports/file-store.ts +5 -4
- package/src/commands/code/ports/prompter.ts +24 -18
- package/src/commands/code/setup.ts +570 -340
- package/src/commands/code.ts +338 -539
- package/src/commands/index.ts +20 -19
- package/src/commands/models.ts +28 -32
- package/src/commands/users.ts +15 -21
- package/src/constants/command-structure.ts +134 -157
- package/src/services/api-key-service.ts +105 -122
- package/src/services/auth-service.ts +99 -345
- package/src/services/browser-auth.ts +296 -0
- package/src/services/chat-service.ts +265 -299
- package/src/services/cluster-service.ts +42 -45
- package/src/services/collaborator-service.ts +14 -19
- package/src/services/flux-service.ts +23 -25
- package/src/services/helm-service.ts +19 -21
- package/src/services/kubectl-service.ts +17 -19
- package/src/types/api.d.ts +1905 -1907
- package/src/types/json.d.ts +2 -2
- package/src/utils/config-checker.ts +10 -10
- package/src/utils/config-loader.ts +162 -178
- package/src/utils/default-api-key.ts +114 -125
- package/src/utils/env-manager.ts +53 -57
- package/src/utils/error-handler.ts +61 -56
- package/src/utils/logger.ts +79 -73
- package/src/utils/markdown-renderer.ts +31 -31
- package/src/utils/opencode-validator.ts +85 -89
- package/src/utils/token-manager.ts +108 -87
- package/templates/agents/app.md +1 -0
- package/templates/agents/backend.md +1 -0
- package/templates/agents/devops.md +2 -0
- package/templates/agents/frontend.md +1 -0
- package/templates/agents/fullstack.md +1 -0
- package/templates/agents/quality.md +45 -40
- package/templates/agents/security.md +1 -0
- package/tests/commands/chat.test.ts +53 -62
- package/tests/commands/code.test.ts +265 -310
- package/tests/utils/config-loader.test.ts +189 -188
- package/tests/utils/env-manager.test.ts +110 -113
- package/tests/utils/opencode-validator.test.ts +52 -56
- package/tsconfig.json +4 -3
- package/vitest.config.ts +3 -3
- package/AGENTS.md +0 -374
- package/TODO.md +0 -19
package/src/commands/api-keys.ts
CHANGED
|
@@ -1,73 +1,43 @@
|
|
|
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
|
|
41
|
-
.command(ApiKeyService.COMMAND_GROUP)
|
|
42
|
-
.description('Manage API keys')
|
|
12
|
+
const apiKey = program.command(ApiKeyService.COMMAND_GROUP).description('Manage API keys');
|
|
43
13
|
|
|
44
14
|
apiKey
|
|
45
15
|
.command(ApiKeyService.COMMANDS.LIST)
|
|
46
16
|
.description('List all API keys')
|
|
47
17
|
.action(async () => {
|
|
48
18
|
try {
|
|
49
|
-
const apiKeyService = ApiKeyService.getInstance()
|
|
50
|
-
const keys = await apiKeyService.list()
|
|
19
|
+
const apiKeyService = ApiKeyService.getInstance();
|
|
20
|
+
const keys = await apiKeyService.list();
|
|
51
21
|
|
|
52
22
|
if (keys.length === 0) {
|
|
53
23
|
console.log(
|
|
54
24
|
chalk.yellow(
|
|
55
25
|
'No API keys found. Create one with `berget api-key create --name <name>`',
|
|
56
26
|
),
|
|
57
|
-
)
|
|
58
|
-
return
|
|
27
|
+
);
|
|
28
|
+
return;
|
|
59
29
|
}
|
|
60
30
|
|
|
61
|
-
console.log(chalk.bold('🔑 Your API keys:'))
|
|
62
|
-
console.log('')
|
|
31
|
+
console.log(chalk.bold('🔑 Your API keys:'));
|
|
32
|
+
console.log('');
|
|
63
33
|
|
|
64
34
|
// Create a more readable table with better spacing and colors
|
|
65
|
-
const idWidth = 10
|
|
66
|
-
const nameWidth = 30
|
|
67
|
-
const prefixWidth = 20
|
|
68
|
-
const statusWidth = 12
|
|
69
|
-
const createdWidth = 12
|
|
70
|
-
const usedWidth = 15
|
|
35
|
+
const idWidth = 10;
|
|
36
|
+
const nameWidth = 30;
|
|
37
|
+
const prefixWidth = 20;
|
|
38
|
+
const statusWidth = 12;
|
|
39
|
+
const createdWidth = 12;
|
|
40
|
+
const usedWidth = 15;
|
|
71
41
|
|
|
72
42
|
console.log(
|
|
73
43
|
chalk.dim('ID'.padEnd(idWidth)) +
|
|
@@ -75,61 +45,57 @@ export function registerApiKeyCommands(program: Command): void {
|
|
|
75
45
|
chalk.dim('PREFIX'.padEnd(prefixWidth)) +
|
|
76
46
|
chalk.dim('STATUS'.padEnd(statusWidth)) +
|
|
77
47
|
chalk.dim('CREATED'.padEnd(createdWidth)) +
|
|
78
|
-
chalk.dim('LAST USED')
|
|
79
|
-
)
|
|
48
|
+
chalk.dim('LAST USED'),
|
|
49
|
+
);
|
|
80
50
|
|
|
81
|
-
console.log(
|
|
51
|
+
console.log(
|
|
52
|
+
chalk.dim(
|
|
53
|
+
'─'.repeat(
|
|
54
|
+
idWidth + nameWidth + prefixWidth + statusWidth + createdWidth + usedWidth + 5,
|
|
55
|
+
),
|
|
56
|
+
),
|
|
57
|
+
);
|
|
82
58
|
|
|
83
59
|
keys.forEach((key: ApiKey) => {
|
|
84
|
-
const lastUsed = key.lastUsed
|
|
85
|
-
|
|
86
|
-
: chalk.yellow('Never used')
|
|
87
|
-
const status = key.active
|
|
88
|
-
? chalk.green('● Active')
|
|
89
|
-
: chalk.red('● Inactive')
|
|
60
|
+
const lastUsed = key.lastUsed ? formatLastUsed(key.lastUsed) : chalk.yellow('Never used');
|
|
61
|
+
const status = key.active ? chalk.green('● Active') : chalk.red('● Inactive');
|
|
90
62
|
|
|
91
63
|
// Show only first 8 characters of ID for easier reading
|
|
92
|
-
const shortId = chalk.cyan(String(key.id).
|
|
64
|
+
const shortId = chalk.cyan(String(key.id).slice(0, 8));
|
|
93
65
|
|
|
94
66
|
// Format the prefix with better truncation
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
67
|
+
const prefixString =
|
|
68
|
+
key.prefix.length > prefixWidth
|
|
69
|
+
? key.prefix.slice(0, Math.max(0, prefixWidth - 3)) + '...'
|
|
70
|
+
: chalk.gray(key.prefix);
|
|
98
71
|
|
|
99
72
|
// Truncate name if too long
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
73
|
+
const nameString =
|
|
74
|
+
key.name.length > nameWidth
|
|
75
|
+
? key.name.slice(0, Math.max(0, nameWidth - 3)) + '...'
|
|
76
|
+
: key.name;
|
|
103
77
|
|
|
104
78
|
// Format created date
|
|
105
|
-
const createdDate = formatDate(key.created)
|
|
79
|
+
const createdDate = formatDate(key.created);
|
|
106
80
|
|
|
107
81
|
console.log(
|
|
108
82
|
shortId.padEnd(idWidth) +
|
|
109
|
-
|
|
110
|
-
|
|
83
|
+
nameString.padEnd(nameWidth) +
|
|
84
|
+
prefixString.padEnd(prefixWidth) +
|
|
111
85
|
status.padEnd(statusWidth) +
|
|
112
86
|
createdDate.padEnd(createdWidth) +
|
|
113
|
-
lastUsed
|
|
114
|
-
)
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
console.log('')
|
|
118
|
-
console.log(
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
),
|
|
122
|
-
)
|
|
123
|
-
console.log(
|
|
124
|
-
chalk.dim('Use `berget api-key delete <id>` to delete an API key'),
|
|
125
|
-
)
|
|
126
|
-
console.log(
|
|
127
|
-
chalk.dim('Use `berget api-key rotate <id>` to rotate an API key'),
|
|
128
|
-
)
|
|
87
|
+
lastUsed,
|
|
88
|
+
);
|
|
89
|
+
});
|
|
90
|
+
|
|
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'));
|
|
129
95
|
} catch (error) {
|
|
130
|
-
handleError('Failed to list API keys', error)
|
|
96
|
+
handleError('Failed to list API keys', error);
|
|
131
97
|
}
|
|
132
|
-
})
|
|
98
|
+
});
|
|
133
99
|
|
|
134
100
|
apiKey
|
|
135
101
|
.command(ApiKeyService.COMMANDS.CREATE)
|
|
@@ -140,72 +106,57 @@ export function registerApiKeyCommands(program: Command): void {
|
|
|
140
106
|
.action(async (options) => {
|
|
141
107
|
try {
|
|
142
108
|
if (!options.name) {
|
|
143
|
-
console.error(chalk.red('Error: --name is required'))
|
|
144
|
-
console.log('')
|
|
145
|
-
console.log(
|
|
146
|
-
|
|
147
|
-
)
|
|
148
|
-
return
|
|
109
|
+
console.error(chalk.red('Error: --name is required'));
|
|
110
|
+
console.log('');
|
|
111
|
+
console.log('Usage: berget api-key create --name <name> [--description <description>]');
|
|
112
|
+
return;
|
|
149
113
|
}
|
|
150
114
|
|
|
151
|
-
console.log(chalk.blue('Creating API key...'))
|
|
115
|
+
console.log(chalk.blue('Creating API key...'));
|
|
152
116
|
|
|
153
|
-
const apiKeyService = ApiKeyService.getInstance()
|
|
117
|
+
const apiKeyService = ApiKeyService.getInstance();
|
|
154
118
|
const result = await apiKeyService.create({
|
|
155
|
-
name: options.name,
|
|
156
119
|
description: options.description,
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
console.log(
|
|
161
|
-
console.log('')
|
|
162
|
-
console.log(
|
|
163
|
-
console.log('')
|
|
164
|
-
console.log(
|
|
165
|
-
console.log(`${chalk.dim('
|
|
120
|
+
name: options.name,
|
|
121
|
+
});
|
|
122
|
+
|
|
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}`);
|
|
166
130
|
if (result.description) {
|
|
167
|
-
console.log(`${chalk.dim('Description:')} ${result.description}`)
|
|
131
|
+
console.log(`${chalk.dim('Description:')} ${result.description}`);
|
|
168
132
|
}
|
|
133
|
+
console.log(`${chalk.dim('Created:')} ${new Date(result.created).toLocaleString()}`);
|
|
134
|
+
console.log('');
|
|
135
|
+
console.log(chalk.bold('API key:'));
|
|
136
|
+
console.log(chalk.cyan(result.key));
|
|
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.'));
|
|
140
|
+
|
|
141
|
+
console.log('');
|
|
169
142
|
console.log(
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
).toLocaleString()}`,
|
|
173
|
-
)
|
|
174
|
-
console.log('')
|
|
175
|
-
console.log(chalk.bold('API key:'))
|
|
176
|
-
console.log(chalk.cyan(result.key))
|
|
177
|
-
console.log('')
|
|
178
|
-
console.log(
|
|
179
|
-
chalk.yellow(
|
|
180
|
-
'⚠️ IMPORTANT: Save this API key in a secure location.',
|
|
181
|
-
),
|
|
182
|
-
)
|
|
183
|
-
console.log(chalk.yellow(' It will not be displayed again.'))
|
|
184
|
-
|
|
185
|
-
console.log('')
|
|
186
|
-
console.log(
|
|
187
|
-
chalk.dim(
|
|
188
|
-
'Use this key in your applications to authenticate with the Berget API.',
|
|
189
|
-
),
|
|
190
|
-
)
|
|
143
|
+
chalk.dim('Use this key in your applications to authenticate with the Berget API.'),
|
|
144
|
+
);
|
|
191
145
|
} catch (error) {
|
|
192
|
-
handleError('Failed to create API key', error)
|
|
146
|
+
handleError('Failed to create API key', error);
|
|
193
147
|
}
|
|
194
|
-
})
|
|
148
|
+
});
|
|
195
149
|
|
|
196
150
|
apiKey
|
|
197
151
|
.command(ApiKeyService.COMMANDS.DELETE)
|
|
198
152
|
.description('Delete an API key')
|
|
199
|
-
.argument(
|
|
200
|
-
'<identifier>',
|
|
201
|
-
'ID (first 8 chars), full ID, or name of the API key to delete',
|
|
202
|
-
)
|
|
153
|
+
.argument('<identifier>', 'ID (first 8 chars), full ID, or name of the API key to delete')
|
|
203
154
|
.action(async (identifier) => {
|
|
204
155
|
try {
|
|
205
|
-
const apiKeyService = ApiKeyService.getInstance()
|
|
156
|
+
const apiKeyService = ApiKeyService.getInstance();
|
|
206
157
|
|
|
207
158
|
// First, get all API keys to find the matching one
|
|
208
|
-
const keys = await apiKeyService.list()
|
|
159
|
+
const keys = await apiKeyService.list();
|
|
209
160
|
|
|
210
161
|
// Try to find the key by:
|
|
211
162
|
// 1. Full ID match
|
|
@@ -216,144 +167,102 @@ export function registerApiKeyCommands(program: Command): void {
|
|
|
216
167
|
// Check for exact matches first (full ID or exact name)
|
|
217
168
|
let exactMatches = keys.filter(
|
|
218
169
|
(key) => String(key.id) === identifier || key.name === identifier,
|
|
219
|
-
)
|
|
170
|
+
);
|
|
220
171
|
|
|
221
172
|
// If no exact matches, check for short ID matches
|
|
222
173
|
if (exactMatches.length === 0) {
|
|
223
|
-
exactMatches = keys.filter(
|
|
224
|
-
(key) => String(key.id).substring(0, 8) === identifier,
|
|
225
|
-
)
|
|
174
|
+
exactMatches = keys.filter((key) => String(key.id).slice(0, 8) === identifier);
|
|
226
175
|
}
|
|
227
176
|
|
|
228
177
|
// If still no matches, check for partial name matches
|
|
229
178
|
if (exactMatches.length === 0) {
|
|
230
179
|
exactMatches = keys.filter((key) =>
|
|
231
180
|
key.name.toLowerCase().includes(identifier.toLowerCase()),
|
|
232
|
-
)
|
|
181
|
+
);
|
|
233
182
|
}
|
|
234
183
|
|
|
235
184
|
// Handle multiple matches
|
|
236
185
|
if (exactMatches.length > 1) {
|
|
237
|
-
console.error(
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
})
|
|
248
|
-
console.log('')
|
|
249
|
-
console.log(
|
|
250
|
-
'Use the first 8 characters of the ID to specify which key to delete.',
|
|
251
|
-
)
|
|
252
|
-
return
|
|
186
|
+
console.error(chalk.red(`Error: Multiple API keys found matching "${identifier}"`));
|
|
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);
|
|
191
|
+
console.log(` ${shortId.padEnd(8)} ${key.name}`);
|
|
192
|
+
}
|
|
193
|
+
console.log('');
|
|
194
|
+
console.log('Use the first 8 characters of the ID to specify which key to delete.');
|
|
195
|
+
return;
|
|
253
196
|
}
|
|
254
197
|
|
|
255
198
|
// Handle no matches
|
|
256
199
|
if (exactMatches.length === 0) {
|
|
257
|
-
console.error(
|
|
258
|
-
|
|
259
|
-
)
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
console.log(
|
|
268
|
-
'Use the first 8 characters of the ID, full ID, or name to delete.',
|
|
269
|
-
)
|
|
270
|
-
return
|
|
200
|
+
console.error(chalk.red(`Error: No API key found matching "${identifier}"`));
|
|
201
|
+
console.log('');
|
|
202
|
+
console.log('Available API keys:');
|
|
203
|
+
for (const key of keys) {
|
|
204
|
+
const shortId = String(key.id).slice(0, 8);
|
|
205
|
+
console.log(` ${shortId.padEnd(8)} ${key.name}`);
|
|
206
|
+
}
|
|
207
|
+
console.log('');
|
|
208
|
+
console.log('Use the first 8 characters of the ID, full ID, or name to delete.');
|
|
209
|
+
return;
|
|
271
210
|
}
|
|
272
211
|
|
|
273
|
-
const matchingKey = exactMatches[0]
|
|
212
|
+
const matchingKey = exactMatches[0];
|
|
274
213
|
|
|
275
|
-
const keyId = String(matchingKey.id)
|
|
276
|
-
const shortId = keyId.
|
|
214
|
+
const keyId = String(matchingKey.id);
|
|
215
|
+
const shortId = keyId.slice(0, 8);
|
|
277
216
|
|
|
278
|
-
console.log(
|
|
279
|
-
chalk.blue(`Deleting API key ${shortId} (${matchingKey.name})...`),
|
|
280
|
-
)
|
|
217
|
+
console.log(chalk.blue(`Deleting API key ${shortId} (${matchingKey.name})...`));
|
|
281
218
|
|
|
282
|
-
await apiKeyService.delete(keyId)
|
|
219
|
+
await apiKeyService.delete(keyId);
|
|
283
220
|
|
|
221
|
+
console.log(chalk.green(`✓ API key ${shortId} (${matchingKey.name}) has been deleted`));
|
|
222
|
+
console.log('');
|
|
284
223
|
console.log(
|
|
285
|
-
chalk.
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
)
|
|
289
|
-
console.log('')
|
|
290
|
-
console.log(
|
|
291
|
-
chalk.dim(
|
|
292
|
-
'Applications using this key will no longer be able to authenticate.',
|
|
293
|
-
),
|
|
294
|
-
)
|
|
295
|
-
console.log(
|
|
296
|
-
chalk.dim(
|
|
297
|
-
'Use `berget api-key list` to see your remaining API keys.',
|
|
298
|
-
),
|
|
299
|
-
)
|
|
224
|
+
chalk.dim('Applications using this key will no longer be able to authenticate.'),
|
|
225
|
+
);
|
|
226
|
+
console.log(chalk.dim('Use `berget api-key list` to see your remaining API keys.'));
|
|
300
227
|
} catch (error) {
|
|
301
|
-
handleError('Failed to delete API key', error)
|
|
228
|
+
handleError('Failed to delete API key', error);
|
|
302
229
|
}
|
|
303
|
-
})
|
|
230
|
+
});
|
|
304
231
|
|
|
305
232
|
apiKey
|
|
306
233
|
.command(ApiKeyService.COMMANDS.ROTATE)
|
|
307
|
-
.description(
|
|
308
|
-
'Rotate an API key (creates a new one and invalidates the old one)',
|
|
309
|
-
)
|
|
234
|
+
.description('Rotate an API key (creates a new one and invalidates the old one)')
|
|
310
235
|
.argument('<id>', 'ID of the API key to rotate')
|
|
311
236
|
.action(async (id) => {
|
|
312
237
|
try {
|
|
313
|
-
console.log(chalk.blue(`Rotating API key ${id}...`))
|
|
314
|
-
console.log(
|
|
315
|
-
|
|
316
|
-
)
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
console.log('')
|
|
322
|
-
console.log(chalk.
|
|
323
|
-
console.log('')
|
|
324
|
-
console.log(chalk.
|
|
325
|
-
console.log('')
|
|
326
|
-
console.log(`${chalk.dim('ID:')} ${result.id}`)
|
|
327
|
-
console.log(`${chalk.dim('Name:')} ${result.name}`)
|
|
238
|
+
console.log(chalk.blue(`Rotating API key ${id}...`));
|
|
239
|
+
console.log(chalk.dim('This will invalidate the old key and generate a new one.'));
|
|
240
|
+
|
|
241
|
+
const apiKeyService = ApiKeyService.getInstance();
|
|
242
|
+
const result = await apiKeyService.rotate(id);
|
|
243
|
+
|
|
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}`);
|
|
328
251
|
if (result.description) {
|
|
329
|
-
console.log(`${chalk.dim('Description:')} ${result.description}`)
|
|
252
|
+
console.log(`${chalk.dim('Description:')} ${result.description}`);
|
|
330
253
|
}
|
|
331
|
-
console.log(
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
)
|
|
336
|
-
console.log('')
|
|
337
|
-
console.log(chalk.
|
|
338
|
-
console.log(chalk.
|
|
339
|
-
console.log('')
|
|
340
|
-
console.log(
|
|
341
|
-
chalk.yellow(
|
|
342
|
-
'⚠️ IMPORTANT: Update your applications with this new API key.',
|
|
343
|
-
),
|
|
344
|
-
)
|
|
345
|
-
console.log(
|
|
346
|
-
chalk.yellow(
|
|
347
|
-
' The old key has been invalidated and will no longer work.',
|
|
348
|
-
),
|
|
349
|
-
)
|
|
350
|
-
console.log(
|
|
351
|
-
chalk.yellow(' This new key will not be displayed again.'),
|
|
352
|
-
)
|
|
254
|
+
console.log(`${chalk.dim('Created:')} ${new Date(result.created).toLocaleString()}`);
|
|
255
|
+
console.log('');
|
|
256
|
+
console.log(chalk.bold('New API key:'));
|
|
257
|
+
console.log(chalk.cyan(result.key));
|
|
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.'));
|
|
353
262
|
} catch (error) {
|
|
354
|
-
handleError('Failed to rotate API key', error)
|
|
263
|
+
handleError('Failed to rotate API key', error);
|
|
355
264
|
}
|
|
356
|
-
})
|
|
265
|
+
});
|
|
357
266
|
|
|
358
267
|
apiKey
|
|
359
268
|
.command(ApiKeyService.COMMANDS.DESCRIBE)
|
|
@@ -361,69 +270,59 @@ export function registerApiKeyCommands(program: Command): void {
|
|
|
361
270
|
.argument('<id>', 'ID of the API key')
|
|
362
271
|
.option('--start <date>', 'Start date (YYYY-MM-DD)')
|
|
363
272
|
.option('--end <date>', 'End date (YYYY-MM-DD)')
|
|
364
|
-
.action(async (id,
|
|
273
|
+
.action(async (id, _options) => {
|
|
365
274
|
try {
|
|
366
|
-
console.log(
|
|
367
|
-
chalk.blue(`Fetching usage statistics for API key ${id}...`),
|
|
368
|
-
)
|
|
275
|
+
console.log(chalk.blue(`Fetching usage statistics for API key ${id}...`));
|
|
369
276
|
|
|
370
|
-
const apiKeyService = ApiKeyService.getInstance()
|
|
371
|
-
const usage = await apiKeyService.describe(id)
|
|
277
|
+
const apiKeyService = ApiKeyService.getInstance();
|
|
278
|
+
const usage = await apiKeyService.describe(id);
|
|
372
279
|
|
|
373
|
-
console.log('')
|
|
374
|
-
console.log(
|
|
375
|
-
|
|
376
|
-
)
|
|
377
|
-
console.log('')
|
|
280
|
+
console.log('');
|
|
281
|
+
console.log(chalk.bold(`Usage statistics for API key: ${usage.name} (${id})`));
|
|
282
|
+
console.log('');
|
|
378
283
|
|
|
379
284
|
// Period information
|
|
380
|
-
console.log(
|
|
381
|
-
|
|
382
|
-
)
|
|
383
|
-
console.log('')
|
|
285
|
+
console.log(chalk.dim(`Period: ${usage.period.start} to ${usage.period.end}`));
|
|
286
|
+
console.log('');
|
|
384
287
|
|
|
385
288
|
// Request statistics
|
|
386
|
-
console.log(chalk.bold('Request statistics:'))
|
|
387
|
-
console.log(
|
|
388
|
-
`Total requests: ${chalk.cyan(usage.requests.total.toLocaleString())}`,
|
|
389
|
-
)
|
|
289
|
+
console.log(chalk.bold('Request statistics:'));
|
|
290
|
+
console.log(`Total requests: ${chalk.cyan(usage.requests.total.toLocaleString())}`);
|
|
390
291
|
|
|
391
292
|
// Daily breakdown if available
|
|
392
293
|
if (usage.requests.daily && usage.requests.daily.length > 0) {
|
|
393
|
-
console.log('')
|
|
394
|
-
console.log(chalk.bold('Daily breakdown:'))
|
|
395
|
-
console.log(chalk.dim('─'.repeat(30)))
|
|
396
|
-
console.log(chalk.dim('DATE'.padEnd(12) + 'REQUESTS'))
|
|
397
|
-
|
|
398
|
-
usage.requests.daily.forEach(
|
|
399
|
-
(day
|
|
400
|
-
|
|
401
|
-
},
|
|
402
|
-
)
|
|
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'));
|
|
298
|
+
|
|
299
|
+
usage.requests.daily.forEach((day: { count: number; date: string }) => {
|
|
300
|
+
console.log(`${day.date.padEnd(12)}${day.count.toLocaleString()}`);
|
|
301
|
+
});
|
|
403
302
|
}
|
|
404
303
|
|
|
405
304
|
// Model usage if available
|
|
406
305
|
if (usage.models && usage.models.length > 0) {
|
|
407
|
-
console.log('')
|
|
408
|
-
console.log(chalk.bold('Model usage:'))
|
|
409
|
-
console.log(chalk.dim('─'.repeat(70)))
|
|
306
|
+
console.log('');
|
|
307
|
+
console.log(chalk.bold('Model usage:'));
|
|
308
|
+
console.log(chalk.dim('─'.repeat(70)));
|
|
410
309
|
console.log(
|
|
411
310
|
chalk.dim('MODEL'.padEnd(20)) +
|
|
412
311
|
chalk.dim('REQUESTS'.padEnd(10)) +
|
|
413
312
|
chalk.dim('INPUT'.padEnd(12)) +
|
|
414
313
|
chalk.dim('OUTPUT'.padEnd(12)) +
|
|
415
314
|
chalk.dim('TOTAL TOKENS'),
|
|
416
|
-
)
|
|
315
|
+
);
|
|
417
316
|
|
|
418
317
|
usage.models.forEach(
|
|
419
318
|
(model: {
|
|
420
|
-
name: string
|
|
421
|
-
requests: number
|
|
319
|
+
name: string;
|
|
320
|
+
requests: number;
|
|
422
321
|
tokens: {
|
|
423
|
-
input: number
|
|
424
|
-
output: number
|
|
425
|
-
total: number
|
|
426
|
-
}
|
|
322
|
+
input: number;
|
|
323
|
+
output: number;
|
|
324
|
+
total: number;
|
|
325
|
+
};
|
|
427
326
|
}) => {
|
|
428
327
|
console.log(
|
|
429
328
|
model.name.padEnd(20) +
|
|
@@ -431,21 +330,19 @@ export function registerApiKeyCommands(program: Command): void {
|
|
|
431
330
|
model.tokens.input.toLocaleString().padEnd(12) +
|
|
432
331
|
model.tokens.output.toLocaleString().padEnd(12) +
|
|
433
332
|
model.tokens.total.toLocaleString(),
|
|
434
|
-
)
|
|
333
|
+
);
|
|
435
334
|
},
|
|
436
|
-
)
|
|
335
|
+
);
|
|
437
336
|
}
|
|
438
337
|
|
|
439
|
-
console.log('')
|
|
338
|
+
console.log('');
|
|
440
339
|
console.log(
|
|
441
|
-
chalk.dim(
|
|
442
|
-
|
|
443
|
-
),
|
|
444
|
-
)
|
|
340
|
+
chalk.dim('Use these statistics to understand your API usage and optimize your costs.'),
|
|
341
|
+
);
|
|
445
342
|
} catch (error) {
|
|
446
|
-
handleError('Failed to get API key usage', error)
|
|
343
|
+
handleError('Failed to get API key usage', error);
|
|
447
344
|
}
|
|
448
|
-
})
|
|
345
|
+
});
|
|
449
346
|
|
|
450
347
|
apiKey
|
|
451
348
|
.command(ApiKeyService.COMMANDS.SET_DEFAULT)
|
|
@@ -453,83 +350,94 @@ export function registerApiKeyCommands(program: Command): void {
|
|
|
453
350
|
.argument('<id>', 'ID of the API key to set as default')
|
|
454
351
|
.action(async (id) => {
|
|
455
352
|
try {
|
|
456
|
-
const apiKeyService = ApiKeyService.getInstance()
|
|
457
|
-
const keys = await apiKeyService.list()
|
|
458
|
-
const selectedKey = keys.find((key) => key.id.toString() === id)
|
|
353
|
+
const apiKeyService = ApiKeyService.getInstance();
|
|
354
|
+
const keys = await apiKeyService.list();
|
|
355
|
+
const selectedKey = keys.find((key) => key.id.toString() === id);
|
|
459
356
|
|
|
460
357
|
if (!selectedKey) {
|
|
461
|
-
console.error(chalk.red(`Error: API key with ID ${id} not found`))
|
|
462
|
-
return
|
|
358
|
+
console.error(chalk.red(`Error: API key with ID ${id} not found`));
|
|
359
|
+
return;
|
|
463
360
|
}
|
|
464
361
|
|
|
465
362
|
// Save the default API key
|
|
466
|
-
const defaultApiKeyManager = DefaultApiKeyManager.getInstance()
|
|
363
|
+
const defaultApiKeyManager = DefaultApiKeyManager.getInstance();
|
|
467
364
|
|
|
468
365
|
// We need to rotate the key to get the actual key value
|
|
469
|
-
const rotatedKey = await apiKeyService.rotate(id)
|
|
366
|
+
const rotatedKey = await apiKeyService.rotate(id);
|
|
470
367
|
|
|
471
368
|
defaultApiKeyManager.setDefaultApiKey(
|
|
472
369
|
id,
|
|
473
370
|
selectedKey.name,
|
|
474
371
|
selectedKey.prefix,
|
|
475
372
|
rotatedKey.key,
|
|
476
|
-
)
|
|
373
|
+
);
|
|
477
374
|
|
|
478
375
|
console.log(
|
|
479
|
-
chalk.green(
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
)
|
|
483
|
-
console.log('')
|
|
484
|
-
console.log(
|
|
485
|
-
chalk.dim(
|
|
486
|
-
'This API key will be used by default when running chat commands',
|
|
487
|
-
),
|
|
488
|
-
)
|
|
489
|
-
console.log(
|
|
490
|
-
chalk.dim(
|
|
491
|
-
'You can override it with --api-key or --api-key-id options',
|
|
492
|
-
),
|
|
493
|
-
)
|
|
376
|
+
chalk.green(`✓ API key "${selectedKey.name}" set as default for chat commands`),
|
|
377
|
+
);
|
|
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'));
|
|
494
381
|
} catch (error) {
|
|
495
|
-
handleError('Failed to set default API key', error)
|
|
382
|
+
handleError('Failed to set default API key', error);
|
|
496
383
|
}
|
|
497
|
-
})
|
|
384
|
+
});
|
|
498
385
|
|
|
499
386
|
apiKey
|
|
500
387
|
.command(ApiKeyService.COMMANDS.GET_DEFAULT)
|
|
501
388
|
.description('Show the current default API key')
|
|
502
389
|
.action(() => {
|
|
503
390
|
try {
|
|
504
|
-
const defaultApiKeyManager = DefaultApiKeyManager.getInstance()
|
|
505
|
-
const defaultApiKeyData = defaultApiKeyManager.getDefaultApiKeyData()
|
|
391
|
+
const defaultApiKeyManager = DefaultApiKeyManager.getInstance();
|
|
392
|
+
const defaultApiKeyData = defaultApiKeyManager.getDefaultApiKeyData();
|
|
506
393
|
|
|
507
394
|
if (!defaultApiKeyData) {
|
|
508
|
-
console.log(chalk.yellow('No default API key set'))
|
|
509
|
-
console.log('')
|
|
510
|
-
console.log('To set a default API key, run:')
|
|
511
|
-
console.log(chalk.cyan(' berget api-keys set-default <id>'))
|
|
512
|
-
return
|
|
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>'));
|
|
399
|
+
return;
|
|
513
400
|
}
|
|
514
401
|
|
|
515
|
-
console.log(chalk.bold('Default API key:'))
|
|
516
|
-
console.log('')
|
|
517
|
-
console.log(`${chalk.dim('ID:')} ${defaultApiKeyData.id}`)
|
|
518
|
-
console.log(`${chalk.dim('Name:')} ${defaultApiKeyData.name}`)
|
|
519
|
-
console.log(`${chalk.dim('Prefix:')} ${defaultApiKeyData.prefix}`)
|
|
520
|
-
console.log('')
|
|
521
|
-
console.log(
|
|
522
|
-
|
|
523
|
-
'This API key will be used by default when running chat commands',
|
|
524
|
-
),
|
|
525
|
-
)
|
|
526
|
-
console.log(
|
|
527
|
-
chalk.dim(
|
|
528
|
-
'You can override it with --api-key or --api-key-id options',
|
|
529
|
-
),
|
|
530
|
-
)
|
|
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'));
|
|
531
410
|
} catch (error) {
|
|
532
|
-
handleError('Failed to get default API key', error)
|
|
411
|
+
handleError('Failed to get default API key', error);
|
|
533
412
|
}
|
|
534
|
-
})
|
|
413
|
+
});
|
|
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
|
+
const date = new Date(dateString);
|
|
433
|
+
const now = new Date();
|
|
434
|
+
const diffTime = Math.abs(now.getTime() - date.getTime());
|
|
435
|
+
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
|
436
|
+
|
|
437
|
+
if (diffDays === 0) return chalk.green('Today');
|
|
438
|
+
if (diffDays === 1) return chalk.yellow('Yesterday');
|
|
439
|
+
if (diffDays < 7) return chalk.yellow(`${diffDays} days ago`);
|
|
440
|
+
if (diffDays < 30) return chalk.blue(`${Math.floor(diffDays / 7)} weeks ago`);
|
|
441
|
+
if (diffDays < 365) return chalk.magenta(`${Math.floor(diffDays / 30)} months ago`);
|
|
442
|
+
return chalk.gray(`${Math.floor(diffDays / 365)} years ago`);
|
|
535
443
|
}
|