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