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.
Files changed (130) hide show
  1. package/.github/workflows/publish.yml +6 -6
  2. package/.github/workflows/test.yml +1 -1
  3. package/.prettierrc +5 -3
  4. package/dist/index.js +24 -25
  5. package/dist/package.json +7 -3
  6. package/dist/src/agents/app.js +8 -8
  7. package/dist/src/agents/backend.js +3 -3
  8. package/dist/src/agents/devops.js +8 -8
  9. package/dist/src/agents/frontend.js +3 -3
  10. package/dist/src/agents/fullstack.js +3 -3
  11. package/dist/src/agents/index.js +18 -18
  12. package/dist/src/agents/quality.js +8 -8
  13. package/dist/src/agents/security.js +8 -8
  14. package/dist/src/client.js +115 -127
  15. package/dist/src/commands/api-keys.js +181 -202
  16. package/dist/src/commands/auth.js +16 -25
  17. package/dist/src/commands/autocomplete.js +8 -8
  18. package/dist/src/commands/billing.js +10 -19
  19. package/dist/src/commands/chat.js +139 -170
  20. package/dist/src/commands/clusters.js +21 -30
  21. package/dist/src/commands/code/__tests__/auth-sync.test.js +189 -186
  22. package/dist/src/commands/code/__tests__/fake-api-key-service.js +3 -13
  23. package/dist/src/commands/code/__tests__/fake-auth-service.js +21 -29
  24. package/dist/src/commands/code/__tests__/fake-command-runner.js +22 -33
  25. package/dist/src/commands/code/__tests__/fake-file-store.js +19 -41
  26. package/dist/src/commands/code/__tests__/fake-prompter.js +81 -97
  27. package/dist/src/commands/code/__tests__/setup-flow.test.js +295 -295
  28. package/dist/src/commands/code/adapters/clack-prompter.js +15 -32
  29. package/dist/src/commands/code/adapters/fs-file-store.js +25 -44
  30. package/dist/src/commands/code/adapters/spawn-command-runner.js +27 -41
  31. package/dist/src/commands/code/auth-sync.js +215 -228
  32. package/dist/src/commands/code/errors.js +15 -12
  33. package/dist/src/commands/code/setup.js +390 -425
  34. package/dist/src/commands/code.js +279 -294
  35. package/dist/src/commands/index.js +5 -5
  36. package/dist/src/commands/models.js +16 -25
  37. package/dist/src/commands/users.js +9 -18
  38. package/dist/src/constants/command-structure.js +138 -138
  39. package/dist/src/services/api-key-service.js +132 -152
  40. package/dist/src/services/auth-service.js +81 -95
  41. package/dist/src/services/browser-auth.js +121 -131
  42. package/dist/src/services/chat-service.js +369 -386
  43. package/dist/src/services/cluster-service.js +47 -62
  44. package/dist/src/services/collaborator-service.js +9 -21
  45. package/dist/src/services/flux-service.js +13 -25
  46. package/dist/src/services/helm-service.js +9 -21
  47. package/dist/src/services/kubectl-service.js +15 -29
  48. package/dist/src/utils/config-checker.js +8 -8
  49. package/dist/src/utils/config-loader.js +109 -109
  50. package/dist/src/utils/default-api-key.js +129 -139
  51. package/dist/src/utils/env-manager.js +55 -66
  52. package/dist/src/utils/error-handler.js +62 -62
  53. package/dist/src/utils/logger.js +74 -67
  54. package/dist/src/utils/markdown-renderer.js +28 -28
  55. package/dist/src/utils/opencode-validator.js +67 -69
  56. package/dist/src/utils/token-manager.js +67 -65
  57. package/dist/tests/commands/chat.test.js +30 -39
  58. package/dist/tests/commands/code.test.js +186 -195
  59. package/dist/tests/utils/config-loader.test.js +107 -107
  60. package/dist/tests/utils/env-manager.test.js +81 -90
  61. package/dist/tests/utils/opencode-validator.test.js +42 -41
  62. package/dist/vitest.config.js +1 -1
  63. package/eslint.config.mjs +65 -30
  64. package/index.ts +30 -31
  65. package/package.json +7 -3
  66. package/src/agents/app.ts +9 -9
  67. package/src/agents/backend.ts +4 -4
  68. package/src/agents/devops.ts +9 -9
  69. package/src/agents/frontend.ts +4 -4
  70. package/src/agents/fullstack.ts +4 -4
  71. package/src/agents/index.ts +27 -25
  72. package/src/agents/quality.ts +9 -9
  73. package/src/agents/security.ts +9 -9
  74. package/src/agents/types.ts +10 -10
  75. package/src/client.ts +85 -77
  76. package/src/commands/api-keys.ts +180 -185
  77. package/src/commands/auth.ts +15 -14
  78. package/src/commands/autocomplete.ts +10 -10
  79. package/src/commands/billing.ts +13 -12
  80. package/src/commands/chat.ts +145 -142
  81. package/src/commands/clusters.ts +20 -19
  82. package/src/commands/code/__tests__/auth-sync.test.ts +176 -175
  83. package/src/commands/code/__tests__/fake-api-key-service.ts +2 -2
  84. package/src/commands/code/__tests__/fake-auth-service.ts +18 -18
  85. package/src/commands/code/__tests__/fake-command-runner.ts +28 -22
  86. package/src/commands/code/__tests__/fake-file-store.ts +15 -15
  87. package/src/commands/code/__tests__/fake-prompter.ts +86 -85
  88. package/src/commands/code/__tests__/setup-flow.test.ts +253 -251
  89. package/src/commands/code/adapters/clack-prompter.ts +32 -30
  90. package/src/commands/code/adapters/fs-file-store.ts +18 -17
  91. package/src/commands/code/adapters/spawn-command-runner.ts +20 -15
  92. package/src/commands/code/auth-sync.ts +210 -210
  93. package/src/commands/code/errors.ts +11 -11
  94. package/src/commands/code/ports/auth-services.ts +7 -7
  95. package/src/commands/code/ports/command-runner.ts +2 -2
  96. package/src/commands/code/ports/file-store.ts +3 -3
  97. package/src/commands/code/ports/prompter.ts +13 -13
  98. package/src/commands/code/setup.ts +408 -406
  99. package/src/commands/code.ts +288 -287
  100. package/src/commands/index.ts +11 -10
  101. package/src/commands/models.ts +19 -18
  102. package/src/commands/users.ts +11 -10
  103. package/src/constants/command-structure.ts +159 -159
  104. package/src/services/api-key-service.ts +85 -85
  105. package/src/services/auth-service.ts +55 -54
  106. package/src/services/browser-auth.ts +62 -62
  107. package/src/services/chat-service.ts +170 -171
  108. package/src/services/cluster-service.ts +28 -28
  109. package/src/services/collaborator-service.ts +6 -6
  110. package/src/services/flux-service.ts +17 -17
  111. package/src/services/helm-service.ts +11 -11
  112. package/src/services/kubectl-service.ts +12 -12
  113. package/src/types/api.d.ts +1933 -1933
  114. package/src/types/json.d.ts +1 -1
  115. package/src/utils/config-checker.ts +7 -7
  116. package/src/utils/config-loader.ts +130 -129
  117. package/src/utils/default-api-key.ts +81 -80
  118. package/src/utils/env-manager.ts +37 -37
  119. package/src/utils/error-handler.ts +64 -64
  120. package/src/utils/logger.ts +72 -66
  121. package/src/utils/markdown-renderer.ts +28 -28
  122. package/src/utils/opencode-validator.ts +72 -71
  123. package/src/utils/token-manager.ts +69 -68
  124. package/tests/commands/chat.test.ts +32 -31
  125. package/tests/commands/code.test.ts +182 -181
  126. package/tests/utils/config-loader.test.ts +111 -110
  127. package/tests/utils/env-manager.test.ts +83 -79
  128. package/tests/utils/opencode-validator.test.ts +43 -42
  129. package/tsconfig.json +2 -1
  130. package/vitest.config.ts +2 -2
@@ -1,29 +1,29 @@
1
- import { createAuthenticatedClient } from "../client";
2
- import { handleError } from "../utils/error-handler";
3
- import { COMMAND_GROUPS, SUBCOMMANDS } from "../constants/command-structure";
1
+ import { createAuthenticatedClient } from '../client';
2
+ import { COMMAND_GROUPS, SUBCOMMANDS } from '../constants/command-structure';
3
+ import { handleError } from '../utils/error-handler';
4
4
 
5
5
  export interface ApiKey {
6
+ active: boolean;
7
+ created: string;
8
+ description: null | string;
6
9
  id: number;
10
+ lastUsed: null | string;
11
+ modified: string;
7
12
  name: string;
8
- description: string | null;
9
- created: string;
10
- lastUsed: string | null;
11
13
  prefix: string;
12
- active: boolean;
13
- modified: string;
14
14
  }
15
15
 
16
- export interface CreateApiKeyOptions {
16
+ export interface ApiKeyResponse {
17
+ created: string;
18
+ description: null | string;
19
+ id: number;
20
+ key: string;
17
21
  name: string;
18
- description?: string;
19
22
  }
20
23
 
21
- export interface ApiKeyResponse {
22
- id: number;
24
+ export interface CreateApiKeyOptions {
25
+ description?: string;
23
26
  name: string;
24
- description: string | null;
25
- key: string;
26
- created: string;
27
27
  }
28
28
 
29
29
  /**
@@ -31,15 +31,15 @@ export interface ApiKeyResponse {
31
31
  * Command group: api-keys
32
32
  */
33
33
  export class ApiKeyService {
34
- private static instance: ApiKeyService;
35
- private client = createAuthenticatedClient();
36
-
37
34
  // Command group name for this service
38
35
  public static readonly COMMAND_GROUP = COMMAND_GROUPS.API_KEYS;
39
-
40
36
  // Subcommands for this service
41
37
  public static readonly COMMANDS = SUBCOMMANDS.API_KEYS;
42
38
 
39
+ private static instance: ApiKeyService;
40
+
41
+ private client = createAuthenticatedClient();
42
+
43
43
  private constructor() {}
44
44
 
45
45
  public static getInstance(): ApiKeyService {
@@ -49,21 +49,6 @@ export class ApiKeyService {
49
49
  return ApiKeyService.instance;
50
50
  }
51
51
 
52
- /**
53
- * List all API keys
54
- * Command: berget api-keys list
55
- */
56
- public async list(): Promise<ApiKey[]> {
57
- try {
58
- const { data, error } = await this.client.GET("/v1/api-keys");
59
- if (error) throw error;
60
- return data || [];
61
- } catch (error) {
62
- handleError("Failed to list API keys", error);
63
- throw error;
64
- }
65
- }
66
-
67
52
  /**
68
53
  * Create a new API key
69
54
  * Command: berget api-keys create
@@ -72,18 +57,18 @@ export class ApiKeyService {
72
57
  try {
73
58
  // Validate input before sending request
74
59
  if (!options.name || options.name.trim().length === 0) {
75
- throw new Error("API key name is required and cannot be empty");
60
+ throw new Error('API key name is required and cannot be empty');
76
61
  }
77
62
 
78
63
  if (options.name.length > 100) {
79
- throw new Error("API key name must be 100 characters or less");
64
+ throw new Error('API key name must be 100 characters or less');
80
65
  }
81
66
 
82
67
  if (options.description && options.description.length > 500) {
83
- throw new Error("API key description must be 500 characters or less");
68
+ throw new Error('API key description must be 500 characters or less');
84
69
  }
85
70
 
86
- const { data, error } = await this.client.POST("/v1/api-keys", {
71
+ const { data, error } = await this.client.POST('/v1/api-keys', {
87
72
  body: options,
88
73
  });
89
74
 
@@ -91,46 +76,46 @@ export class ApiKeyService {
91
76
  // Enhanced error handling with specific troubleshooting
92
77
 
93
78
  // Handle specific error cases
94
- if (typeof error === "object" && error !== null) {
95
- const errorObj = error as any;
96
-
97
- if (errorObj.error?.code === "API_KEY_CREATION_FAILED") {
98
- let detailedMessage = "Failed to create API key. This could be due to:\n";
99
- detailedMessage += "• Account limits or quota restrictions\n";
100
- detailedMessage += "• Insufficient permissions for API key creation\n";
101
- detailedMessage += "• Temporary server issues\n";
102
- detailedMessage += "• Billing or subscription issues\n\n";
103
- detailedMessage += "Troubleshooting steps:\n";
104
- detailedMessage += "1. Check if you have reached your API key limit\n";
105
- detailedMessage += "2. Verify your account has API key creation permissions\n";
106
- detailedMessage += "3. Check your billing status and subscription\n";
107
- detailedMessage += "4. Try again in a few minutes if this is a temporary issue\n";
108
- detailedMessage += "5. Contact support if the problem persists";
79
+ if (typeof error === 'object' && error !== null) {
80
+ const errorObject = error as any;
81
+
82
+ if (errorObject.error?.code === 'API_KEY_CREATION_FAILED') {
83
+ let detailedMessage = 'Failed to create API key. This could be due to:\n';
84
+ detailedMessage += '• Account limits or quota restrictions\n';
85
+ detailedMessage += '• Insufficient permissions for API key creation\n';
86
+ detailedMessage += '• Temporary server issues\n';
87
+ detailedMessage += '• Billing or subscription issues\n\n';
88
+ detailedMessage += 'Troubleshooting steps:\n';
89
+ detailedMessage += '1. Check if you have reached your API key limit\n';
90
+ detailedMessage += '2. Verify your account has API key creation permissions\n';
91
+ detailedMessage += '3. Check your billing status and subscription\n';
92
+ detailedMessage += '4. Try again in a few minutes if this is a temporary issue\n';
93
+ detailedMessage += '5. Contact support if the problem persists';
109
94
 
110
95
  throw new Error(detailedMessage);
111
96
  }
112
97
 
113
- if (errorObj.error?.code === "USER_NOT_FOUND") {
98
+ if (errorObject.error?.code === 'USER_NOT_FOUND') {
114
99
  throw new Error(
115
- "Your account is still being set up. Please wait a moment and try again.\n\nIf this issue persists, please contact support at support@berget.ai"
100
+ 'Your account is still being set up. Please wait a moment and try again.\n\nIf this issue persists, please contact support at support@berget.ai',
116
101
  );
117
102
  }
118
103
 
119
- if (errorObj.error?.code === "QUOTA_EXCEEDED") {
104
+ if (errorObject.error?.code === 'QUOTA_EXCEEDED') {
120
105
  throw new Error(
121
- "You have reached your API key limit. Please delete existing keys or contact support to increase your quota."
106
+ 'You have reached your API key limit. Please delete existing keys or contact support to increase your quota.',
122
107
  );
123
108
  }
124
109
 
125
- if (errorObj.error?.code === "INSUFFICIENT_PERMISSIONS") {
110
+ if (errorObject.error?.code === 'INSUFFICIENT_PERMISSIONS') {
126
111
  throw new Error(
127
- "Your account does not have permission to create API keys. Please contact your administrator."
112
+ 'Your account does not have permission to create API keys. Please contact your administrator.',
128
113
  );
129
114
  }
130
115
 
131
- if (errorObj.error?.code === "BILLING_REQUIRED") {
116
+ if (errorObject.error?.code === 'BILLING_REQUIRED') {
132
117
  throw new Error(
133
- "A valid billing method is required to create API keys. Please add a payment method."
118
+ 'A valid billing method is required to create API keys. Please add a payment method.',
134
119
  );
135
120
  }
136
121
  }
@@ -139,30 +124,30 @@ export class ApiKeyService {
139
124
  }
140
125
 
141
126
  if (!data) {
142
- throw new Error("No data received from server");
127
+ throw new Error('No data received from server');
143
128
  }
144
129
 
145
130
  return data;
146
131
  } catch (error) {
147
- console.error("Failed to create API key:", error);
132
+ console.error('Failed to create API key:', error);
148
133
 
149
134
  // Add additional context for common issues
150
135
  if (error instanceof Error) {
151
- if (error.message.includes("ECONNREFUSED")) {
152
- throw new Error("Cannot connect to Berget API. Please check your internet connection.");
136
+ if (error.message.includes('ECONNREFUSED')) {
137
+ throw new Error('Cannot connect to Berget API. Please check your internet connection.');
153
138
  }
154
139
 
155
- if (error.message.includes("ENOTFOUND")) {
156
- throw new Error("Cannot resolve Berget API hostname. Please check your DNS settings.");
140
+ if (error.message.includes('ENOTFOUND')) {
141
+ throw new Error('Cannot resolve Berget API hostname. Please check your DNS settings.');
157
142
  }
158
143
 
159
- if (error.message.includes("401") || error.message.includes("Unauthorized")) {
160
- throw new Error("Authentication failed. Please run `berget auth login` to log in again.");
144
+ if (error.message.includes('401') || error.message.includes('Unauthorized')) {
145
+ throw new Error('Authentication failed. Please run `berget auth login` to log in again.');
161
146
  }
162
147
 
163
- if (error.message.includes("403")) {
148
+ if (error.message.includes('403')) {
164
149
  throw new Error(
165
- "Access forbidden. Your account may not have permission to create API keys."
150
+ 'Access forbidden. Your account may not have permission to create API keys.',
166
151
  );
167
152
  }
168
153
  }
@@ -177,47 +162,62 @@ export class ApiKeyService {
177
162
  */
178
163
  public async delete(id: string): Promise<boolean> {
179
164
  try {
180
- const { error } = await this.client.DELETE("/v1/api-keys/{id}", {
165
+ const { error } = await this.client.DELETE('/v1/api-keys/{id}', {
181
166
  params: { path: { id } },
182
167
  });
183
168
  if (error) throw new Error(JSON.stringify(error));
184
169
  return true;
185
170
  } catch (error) {
186
- console.error("Failed to delete API key:", error);
171
+ console.error('Failed to delete API key:', error);
187
172
  throw error;
188
173
  }
189
174
  }
190
175
 
191
176
  /**
192
- * Rotate an API key
193
- * Command: berget api-keys rotate
177
+ * Get usage statistics for an API key
178
+ * Command: berget api-keys describe
194
179
  */
195
- public async rotate(id: string): Promise<ApiKeyResponse> {
180
+ public async describe(id: string): Promise<any> {
196
181
  try {
197
- const { data, error } = await this.client.PUT("/v1/api-keys/{id}/rotate", {
182
+ const { data, error } = await this.client.GET('/v1/api-keys/{id}/usage', {
198
183
  params: { path: { id } },
199
184
  });
200
185
  if (error) throw new Error(JSON.stringify(error));
201
- return data!;
186
+ return data;
202
187
  } catch (error) {
203
- console.error("Failed to rotate API key:", error);
188
+ console.error('Failed to get API key usage:', error);
204
189
  throw error;
205
190
  }
206
191
  }
207
192
 
208
193
  /**
209
- * Get usage statistics for an API key
210
- * Command: berget api-keys describe
194
+ * List all API keys
195
+ * Command: berget api-keys list
211
196
  */
212
- public async describe(id: string): Promise<any> {
197
+ public async list(): Promise<ApiKey[]> {
213
198
  try {
214
- const { data, error } = await this.client.GET("/v1/api-keys/{id}/usage", {
199
+ const { data, error } = await this.client.GET('/v1/api-keys');
200
+ if (error) throw error;
201
+ return data || [];
202
+ } catch (error) {
203
+ handleError('Failed to list API keys', error);
204
+ throw error;
205
+ }
206
+ }
207
+
208
+ /**
209
+ * Rotate an API key
210
+ * Command: berget api-keys rotate
211
+ */
212
+ public async rotate(id: string): Promise<ApiKeyResponse> {
213
+ try {
214
+ const { data, error } = await this.client.PUT('/v1/api-keys/{id}/rotate', {
215
215
  params: { path: { id } },
216
216
  });
217
217
  if (error) throw new Error(JSON.stringify(error));
218
- return data;
218
+ return data!;
219
219
  } catch (error) {
220
- console.error("Failed to get API key usage:", error);
220
+ console.error('Failed to rotate API key:', error);
221
221
  throw error;
222
222
  }
223
223
  }
@@ -1,41 +1,32 @@
1
- import { createAuthenticatedClient, saveAuthToken, clearAuthToken } from "../client";
2
- import chalk from "chalk";
3
- import { handleError } from "../utils/error-handler";
4
- import { COMMAND_GROUPS, SUBCOMMANDS } from "../constants/command-structure";
5
- import { BrowserAuth } from "./browser-auth";
1
+ import chalk from 'chalk';
2
+
3
+ import { clearAuthToken, createAuthenticatedClient, saveAuthToken } from '../client';
4
+ import { COMMAND_GROUPS, SUBCOMMANDS } from '../constants/command-structure';
5
+ import { handleError } from '../utils/error-handler';
6
+ import { BrowserAuth } from './browser-auth';
6
7
 
7
8
  // Keycloak configuration based on environment
8
- const isStageMode = process.argv.includes("--stage");
9
- const isLocalMode = process.argv.includes("--local");
9
+ const isStageMode = process.argv.includes('--stage');
10
+ const isLocalMode = process.argv.includes('--local');
10
11
  const KEYCLOAK_URL =
11
- isStageMode || isLocalMode ? "https://keycloak.stage.berget.ai" : "https://keycloak.berget.ai";
12
- const KEYCLOAK_REALM = "berget";
13
- const KEYCLOAK_CLIENT_ID = "berget-code";
12
+ isStageMode || isLocalMode ? 'https://keycloak.stage.berget.ai' : 'https://keycloak.berget.ai';
13
+ const KEYCLOAK_REALM = 'berget';
14
+ const KEYCLOAK_CLIENT_ID = 'berget-code';
14
15
  const CALLBACK_PORT = 8787;
15
16
 
16
- function makeBrowserAuth(debug?: boolean): BrowserAuth {
17
- return new BrowserAuth({
18
- keycloakUrl: KEYCLOAK_URL,
19
- realm: KEYCLOAK_REALM,
20
- clientId: KEYCLOAK_CLIENT_ID,
21
- callbackPort: CALLBACK_PORT,
22
- debug,
23
- });
24
- }
25
-
26
17
  /**
27
18
  * Service for authentication operations
28
19
  * Command group: auth
29
20
  */
30
21
  export class AuthService {
31
- private static instance: AuthService;
32
-
33
22
  // Command group name for this service
34
23
  public static readonly COMMAND_GROUP = COMMAND_GROUPS.AUTH;
35
24
 
36
25
  // Subcommands for this service
37
26
  public static readonly COMMANDS = SUBCOMMANDS.AUTH;
38
27
 
28
+ private static instance: AuthService;
29
+
39
30
  private constructor() {}
40
31
 
41
32
  public static getInstance(): AuthService {
@@ -45,20 +36,6 @@ export class AuthService {
45
36
  return AuthService.instance;
46
37
  }
47
38
 
48
- public async whoami(): Promise<any> {
49
- try {
50
- // Create fresh client to ensure we have the latest token
51
- const client = createAuthenticatedClient();
52
- const { data: profile, error } = await client.GET("/v1/users/me");
53
- if (error) {
54
- return null;
55
- }
56
- return profile;
57
- } catch {
58
- return null;
59
- }
60
- }
61
-
62
39
  /**
63
40
  * Browser-based PKCE login for interactive CLI use.
64
41
  * Prints status to stdout/stderr. Use loginInteractive() when you need
@@ -68,20 +45,20 @@ export class AuthService {
68
45
  try {
69
46
  clearAuthToken();
70
47
 
71
- console.log(chalk.blue("Initiating login process..."));
48
+ console.log(chalk.blue('Initiating login process...'));
72
49
 
73
- const auth = makeBrowserAuth(process.argv.includes("--debug"));
50
+ const auth = makeBrowserAuth(process.argv.includes('--debug'));
74
51
  const result = await auth.start();
75
52
 
76
53
  if (!result.success) {
77
- console.log(chalk.red(`\nAuthentication failed: ${result.error || "Unknown error"}`));
54
+ console.log(chalk.red(`\nAuthentication failed: ${result.error || 'Unknown error'}`));
78
55
  return false;
79
56
  }
80
57
 
81
58
  saveAuthToken(result.accessToken!, result.refreshToken!, result.expiresIn!);
82
59
 
83
- if (process.argv.includes("--debug")) {
84
- console.log(chalk.yellow("DEBUG: Token data received:"));
60
+ if (process.argv.includes('--debug')) {
61
+ console.log(chalk.yellow('DEBUG: Token data received:'));
85
62
  console.log(
86
63
  chalk.yellow(
87
64
  JSON.stringify(
@@ -89,13 +66,13 @@ export class AuthService {
89
66
  expires_in: result.expiresIn,
90
67
  },
91
68
  null,
92
- 2
93
- )
94
- )
69
+ 2,
70
+ ),
71
+ ),
95
72
  );
96
73
  }
97
74
 
98
- console.log(chalk.green("\n✓ Successfully logged in to Berget"));
75
+ console.log(chalk.green('\n✓ Successfully logged in to Berget'));
99
76
 
100
77
  try {
101
78
  const profile = await this.whoami();
@@ -106,13 +83,13 @@ export class AuthService {
106
83
  // Ignore errors fetching profile
107
84
  }
108
85
 
109
- console.log(chalk.cyan("\nNext steps:"));
110
- console.log(chalk.cyan(" • Create an API key: berget api-keys create"));
111
- console.log(chalk.cyan(" • Setup OpenCode: berget code init"));
86
+ console.log(chalk.cyan('\nNext steps:'));
87
+ console.log(chalk.cyan(' • Create an API key: berget api-keys create'));
88
+ console.log(chalk.cyan(' • Setup OpenCode: berget code init'));
112
89
 
113
90
  return true;
114
91
  } catch (error) {
115
- handleError("Login failed", error);
92
+ handleError('Login failed', error);
116
93
  return false;
117
94
  }
118
95
  }
@@ -123,16 +100,16 @@ export class AuthService {
123
100
  * their own UI (e.g. via clack/prompts).
124
101
  */
125
102
  public async loginInteractive(): Promise<{
126
- success: boolean;
127
103
  accessToken?: string;
128
- refreshToken?: string;
129
- expiresIn?: number;
130
104
  error?: string;
105
+ expiresIn?: number;
106
+ refreshToken?: string;
107
+ success: boolean;
131
108
  }> {
132
109
  try {
133
110
  clearAuthToken();
134
111
 
135
- const auth = makeBrowserAuth(process.argv.includes("--debug"));
112
+ const auth = makeBrowserAuth(process.argv.includes('--debug'));
136
113
  const result = await auth.start();
137
114
 
138
115
  if (result.success) {
@@ -142,9 +119,33 @@ export class AuthService {
142
119
  return result;
143
120
  } catch (error) {
144
121
  return {
145
- success: false,
146
122
  error: error instanceof Error ? error.message : String(error),
123
+ success: false,
147
124
  };
148
125
  }
149
126
  }
127
+
128
+ public async whoami(): Promise<any> {
129
+ try {
130
+ // Create fresh client to ensure we have the latest token
131
+ const client = createAuthenticatedClient();
132
+ const { data: profile, error } = await client.GET('/v1/users/me');
133
+ if (error) {
134
+ return null;
135
+ }
136
+ return profile;
137
+ } catch {
138
+ return null;
139
+ }
140
+ }
141
+ }
142
+
143
+ function makeBrowserAuth(debug?: boolean): BrowserAuth {
144
+ return new BrowserAuth({
145
+ callbackPort: CALLBACK_PORT,
146
+ clientId: KEYCLOAK_CLIENT_ID,
147
+ debug,
148
+ keycloakUrl: KEYCLOAK_URL,
149
+ realm: KEYCLOAK_REALM,
150
+ });
150
151
  }