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/client.ts
CHANGED
|
@@ -1,67 +1,68 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
import
|
|
7
|
-
import { TokenManager } from './utils/token-manager'
|
|
8
|
-
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import createClient from 'openapi-fetch';
|
|
3
|
+
|
|
4
|
+
import type { paths } from './types/api';
|
|
5
|
+
|
|
6
|
+
import { logger } from './utils/logger';
|
|
7
|
+
import { TokenManager } from './utils/token-manager';
|
|
8
|
+
|
|
9
|
+
type ApiMethod = (...args: unknown[]) => Promise<any>;
|
|
9
10
|
|
|
10
11
|
// API Base URL
|
|
11
12
|
// Use --local flag to test against local API
|
|
12
13
|
// Use --stage flag to test against stage API
|
|
13
|
-
const isLocalMode = process.argv.includes('--local')
|
|
14
|
-
const isStageMode = process.argv.includes('--stage')
|
|
14
|
+
const isLocalMode = process.argv.includes('--local');
|
|
15
|
+
const isStageMode = process.argv.includes('--stage');
|
|
15
16
|
|
|
16
17
|
export const API_BASE_URL =
|
|
17
18
|
process.env.BERGET_API_URL ||
|
|
18
|
-
(isLocalMode
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
(isLocalMode
|
|
20
|
+
? 'http://localhost:3000'
|
|
21
|
+
: isStageMode
|
|
22
|
+
? 'https://api.stage.berget.ai'
|
|
23
|
+
: 'https://api.berget.ai'); // production default
|
|
21
24
|
|
|
22
25
|
if (isLocalMode && !process.env.BERGET_API_URL) {
|
|
23
|
-
logger.debug('Using local API endpoint: http://localhost:3000')
|
|
26
|
+
logger.debug('Using local API endpoint: http://localhost:3000');
|
|
24
27
|
} else if (isStageMode && !process.env.BERGET_API_URL) {
|
|
25
|
-
logger.debug('Using stage API endpoint: https://api.stage.berget.ai')
|
|
28
|
+
logger.debug('Using stage API endpoint: https://api.stage.berget.ai');
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
// Create a typed client for the Berget API
|
|
29
32
|
export const apiClient = createClient<paths>({
|
|
30
33
|
baseUrl: API_BASE_URL,
|
|
31
34
|
headers: {
|
|
32
|
-
'Content-Type': 'application/json',
|
|
33
35
|
Accept: 'application/json',
|
|
36
|
+
'Content-Type': 'application/json',
|
|
34
37
|
},
|
|
35
|
-
})
|
|
38
|
+
});
|
|
36
39
|
|
|
37
40
|
// Authentication functions
|
|
38
|
-
export const getAuthToken = ():
|
|
39
|
-
const tokenManager = TokenManager.getInstance()
|
|
40
|
-
return tokenManager.getAccessToken()
|
|
41
|
-
}
|
|
41
|
+
export const getAuthToken = (): null | string => {
|
|
42
|
+
const tokenManager = TokenManager.getInstance();
|
|
43
|
+
return tokenManager.getAccessToken();
|
|
44
|
+
};
|
|
42
45
|
|
|
43
46
|
export const saveAuthToken = (
|
|
44
47
|
accessToken: string,
|
|
45
48
|
refreshToken: string,
|
|
46
49
|
expiresIn: number = 3600,
|
|
47
50
|
): void => {
|
|
48
|
-
const tokenManager = TokenManager.getInstance()
|
|
49
|
-
tokenManager.setTokens(accessToken, refreshToken, expiresIn)
|
|
50
|
-
}
|
|
51
|
+
const tokenManager = TokenManager.getInstance();
|
|
52
|
+
tokenManager.setTokens(accessToken, refreshToken, expiresIn);
|
|
53
|
+
};
|
|
51
54
|
|
|
52
55
|
export const clearAuthToken = (): void => {
|
|
53
|
-
const tokenManager = TokenManager.getInstance()
|
|
54
|
-
tokenManager.clearTokens()
|
|
55
|
-
}
|
|
56
|
+
const tokenManager = TokenManager.getInstance();
|
|
57
|
+
tokenManager.clearTokens();
|
|
58
|
+
};
|
|
56
59
|
|
|
57
60
|
// Create an authenticated client with refresh token support
|
|
58
61
|
export const createAuthenticatedClient = () => {
|
|
59
|
-
const tokenManager = TokenManager.getInstance()
|
|
62
|
+
const tokenManager = TokenManager.getInstance();
|
|
60
63
|
|
|
61
64
|
if (!tokenManager.getAccessToken()) {
|
|
62
|
-
logger.debug(
|
|
63
|
-
'No authentication token found. Please run `berget auth login` first.',
|
|
64
|
-
)
|
|
65
|
+
logger.debug('No authentication token found. Please run `berget auth login` first.');
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
// Create the base client
|
|
@@ -69,77 +70,65 @@ export const createAuthenticatedClient = () => {
|
|
|
69
70
|
baseUrl: API_BASE_URL,
|
|
70
71
|
headers: tokenManager.getAccessToken()
|
|
71
72
|
? {
|
|
73
|
+
Accept: 'application/json',
|
|
72
74
|
Authorization: `Bearer ${tokenManager.getAccessToken()}`,
|
|
73
75
|
'Content-Type': 'application/json',
|
|
74
|
-
Accept: 'application/json',
|
|
75
76
|
}
|
|
76
77
|
: {
|
|
77
|
-
'Content-Type': 'application/json',
|
|
78
78
|
Accept: 'application/json',
|
|
79
|
+
'Content-Type': 'application/json',
|
|
79
80
|
},
|
|
80
|
-
})
|
|
81
|
+
});
|
|
81
82
|
|
|
82
83
|
// Wrap the client to handle token refresh
|
|
83
84
|
return new Proxy(client, {
|
|
84
|
-
get(target,
|
|
85
|
+
get(target, property: string | symbol) {
|
|
85
86
|
// For HTTP methods (GET, POST, etc.), add token refresh logic
|
|
86
87
|
if (
|
|
87
|
-
typeof target[
|
|
88
|
-
['
|
|
88
|
+
typeof target[property as keyof typeof target] === 'function' &&
|
|
89
|
+
['DELETE', 'GET', 'PATCH', 'POST', 'PUT'].includes(String(property))
|
|
89
90
|
) {
|
|
90
|
-
return async (...
|
|
91
|
+
return async (...arguments_: any[]) => {
|
|
91
92
|
// Check if token is expired before making the request
|
|
92
93
|
if (tokenManager.isTokenExpired() && tokenManager.getRefreshToken()) {
|
|
93
|
-
await refreshAccessToken(tokenManager)
|
|
94
|
+
await refreshAccessToken(tokenManager);
|
|
94
95
|
}
|
|
95
96
|
|
|
96
97
|
// Update the Authorization header with the current token
|
|
97
|
-
if (
|
|
98
|
-
!
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
if (!args[1]) args[1] = {}
|
|
102
|
-
if (!args[1].headers) args[1].headers = {}
|
|
103
|
-
args[1].headers.Authorization = `Bearer ${tokenManager.getAccessToken()}`
|
|
98
|
+
if (!arguments_[1]?.headers?.Authorization && tokenManager.getAccessToken()) {
|
|
99
|
+
if (!arguments_[1]) arguments_[1] = {};
|
|
100
|
+
if (!arguments_[1].headers) arguments_[1].headers = {};
|
|
101
|
+
arguments_[1].headers.Authorization = `Bearer ${tokenManager.getAccessToken()}`;
|
|
104
102
|
}
|
|
105
103
|
|
|
106
104
|
// Make the original request
|
|
107
|
-
let result
|
|
105
|
+
let result;
|
|
108
106
|
try {
|
|
109
|
-
result = await (target[
|
|
110
|
-
...args,
|
|
111
|
-
)
|
|
107
|
+
result = await (target[property as keyof typeof target] as ApiMethod)(...arguments_);
|
|
112
108
|
} catch (requestError) {
|
|
113
109
|
logger.debug(
|
|
114
110
|
`Request error: ${
|
|
115
|
-
requestError instanceof Error
|
|
116
|
-
? requestError.message
|
|
117
|
-
: String(requestError)
|
|
111
|
+
requestError instanceof Error ? requestError.message : String(requestError)
|
|
118
112
|
}`,
|
|
119
|
-
)
|
|
113
|
+
);
|
|
120
114
|
return {
|
|
121
115
|
error: {
|
|
122
116
|
message: `Request failed: ${
|
|
123
|
-
requestError instanceof Error
|
|
124
|
-
? requestError.message
|
|
125
|
-
: String(requestError)
|
|
117
|
+
requestError instanceof Error ? requestError.message : String(requestError)
|
|
126
118
|
}`,
|
|
127
119
|
},
|
|
128
|
-
}
|
|
120
|
+
};
|
|
129
121
|
}
|
|
130
122
|
|
|
131
123
|
// If we get an auth error, try to refresh the token and retry
|
|
132
124
|
if (result.error) {
|
|
133
125
|
// Detect various forms of authentication errors
|
|
134
|
-
let isAuthError = false
|
|
126
|
+
let isAuthError = false;
|
|
135
127
|
|
|
136
128
|
try {
|
|
137
129
|
// Standard 401 Unauthorized
|
|
138
|
-
if (
|
|
139
|
-
|
|
140
|
-
result.error.status === 401
|
|
141
|
-
) {
|
|
142
|
-
isAuthError = true
|
|
130
|
+
if (typeof result.error === 'object' && result.error.status === 401) {
|
|
131
|
+
isAuthError = true;
|
|
143
132
|
}
|
|
144
133
|
// OAuth specific errors
|
|
145
134
|
else if (
|
|
@@ -148,11 +137,9 @@ export const createAuthenticatedClient = () => {
|
|
|
148
137
|
result.error.error.code === 'token_expired' ||
|
|
149
138
|
result.error.error.message === 'Invalid API key' ||
|
|
150
139
|
result.error.error.message?.toLowerCase().includes('token') ||
|
|
151
|
-
result.error.error.message
|
|
152
|
-
?.toLowerCase()
|
|
153
|
-
.includes('unauthorized'))
|
|
140
|
+
result.error.error.message?.toLowerCase().includes('unauthorized'))
|
|
154
141
|
) {
|
|
155
|
-
isAuthError = true
|
|
142
|
+
isAuthError = true;
|
|
156
143
|
}
|
|
157
144
|
// Message-based detection as fallback
|
|
158
145
|
else if (
|
|
@@ -161,169 +148,148 @@ export const createAuthenticatedClient = () => {
|
|
|
161
148
|
result.error.toLowerCase().includes('token') ||
|
|
162
149
|
result.error.toLowerCase().includes('auth'))
|
|
163
150
|
) {
|
|
164
|
-
isAuthError = true
|
|
151
|
+
isAuthError = true;
|
|
165
152
|
}
|
|
166
|
-
} catch
|
|
153
|
+
} catch {
|
|
167
154
|
// If we can't parse the error structure, do a simple string check
|
|
168
|
-
const
|
|
155
|
+
const errorString = String(result.error);
|
|
169
156
|
if (
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
157
|
+
errorString.toLowerCase().includes('unauthorized') ||
|
|
158
|
+
errorString.toLowerCase().includes('token') ||
|
|
159
|
+
errorString.toLowerCase().includes('auth')
|
|
173
160
|
) {
|
|
174
|
-
isAuthError = true
|
|
161
|
+
isAuthError = true;
|
|
175
162
|
}
|
|
176
163
|
}
|
|
177
164
|
|
|
178
165
|
if (isAuthError && tokenManager.getRefreshToken()) {
|
|
179
|
-
logger.debug('Auth error detected, attempting token refresh')
|
|
180
|
-
logger.debug(
|
|
181
|
-
`Error details: ${JSON.stringify(result.error, null, 2)}`,
|
|
182
|
-
)
|
|
166
|
+
logger.debug('Auth error detected, attempting token refresh');
|
|
167
|
+
logger.debug(`Error details: ${JSON.stringify(result.error, null, 2)}`);
|
|
183
168
|
|
|
184
|
-
const refreshed = await refreshAccessToken(tokenManager)
|
|
169
|
+
const refreshed = await refreshAccessToken(tokenManager);
|
|
185
170
|
if (refreshed) {
|
|
186
|
-
logger.debug('Token refreshed successfully, retrying request')
|
|
171
|
+
logger.debug('Token refreshed successfully, retrying request');
|
|
187
172
|
|
|
188
173
|
// Update the Authorization header with the new token
|
|
189
|
-
if (!
|
|
190
|
-
if (!
|
|
191
|
-
|
|
174
|
+
if (!arguments_[1]) arguments_[1] = {};
|
|
175
|
+
if (!arguments_[1].headers) arguments_[1].headers = {};
|
|
176
|
+
arguments_[1].headers.Authorization = `Bearer ${tokenManager.getAccessToken()}`;
|
|
192
177
|
|
|
193
178
|
// Retry the request
|
|
194
|
-
return await (target[
|
|
195
|
-
...args,
|
|
196
|
-
)
|
|
179
|
+
return await (target[property as keyof typeof target] as ApiMethod)(...arguments_);
|
|
197
180
|
} else {
|
|
198
|
-
logger.debug('Token refresh failed')
|
|
181
|
+
logger.debug('Token refresh failed');
|
|
199
182
|
|
|
200
183
|
// Add a more helpful error message for users
|
|
201
184
|
if (typeof result.error === 'object') {
|
|
202
185
|
result.error.userMessage =
|
|
203
|
-
'Your session has expired. Please run `berget auth login` to log in again.'
|
|
186
|
+
'Your session has expired. Please run `berget auth login` to log in again.';
|
|
204
187
|
}
|
|
205
188
|
}
|
|
206
189
|
}
|
|
207
190
|
}
|
|
208
191
|
|
|
209
|
-
return result
|
|
210
|
-
}
|
|
192
|
+
return result;
|
|
193
|
+
};
|
|
211
194
|
}
|
|
212
195
|
|
|
213
196
|
// For other properties, just return the original
|
|
214
|
-
return target[
|
|
197
|
+
return target[property as keyof typeof target];
|
|
215
198
|
},
|
|
216
|
-
})
|
|
217
|
-
}
|
|
199
|
+
});
|
|
200
|
+
};
|
|
218
201
|
|
|
219
202
|
// Keycloak configuration for token refresh (must match auth-service.ts)
|
|
220
|
-
const KEYCLOAK_URL =
|
|
221
|
-
? 'https://keycloak.stage.berget.ai'
|
|
222
|
-
|
|
223
|
-
const
|
|
224
|
-
const KEYCLOAK_CLIENT_ID = 'berget-code'
|
|
203
|
+
const KEYCLOAK_URL =
|
|
204
|
+
isStageMode || isLocalMode ? 'https://keycloak.stage.berget.ai' : 'https://keycloak.berget.ai';
|
|
205
|
+
const KEYCLOAK_REALM = 'berget';
|
|
206
|
+
const KEYCLOAK_CLIENT_ID = 'berget-code';
|
|
225
207
|
|
|
226
208
|
// Helper function to refresh the access token
|
|
227
|
-
async function refreshAccessToken(
|
|
228
|
-
tokenManager: TokenManager,
|
|
229
|
-
): Promise<boolean> {
|
|
209
|
+
async function refreshAccessToken(tokenManager: TokenManager): Promise<boolean> {
|
|
230
210
|
try {
|
|
231
|
-
const refreshToken = tokenManager.getRefreshToken()
|
|
232
|
-
if (!refreshToken) return false
|
|
211
|
+
const refreshToken = tokenManager.getRefreshToken();
|
|
212
|
+
if (!refreshToken) return false;
|
|
233
213
|
|
|
234
|
-
logger.debug('Attempting to refresh access token')
|
|
214
|
+
logger.debug('Attempting to refresh access token');
|
|
235
215
|
|
|
236
216
|
// Refresh directly against Keycloak (berget-code is a public PKCE client)
|
|
237
217
|
try {
|
|
238
218
|
const response = await fetch(
|
|
239
219
|
`${KEYCLOAK_URL}/realms/${KEYCLOAK_REALM}/protocol/openid-connect/token`,
|
|
240
220
|
{
|
|
241
|
-
method: 'POST',
|
|
242
|
-
headers: {
|
|
243
|
-
'Content-Type': 'application/x-www-form-urlencoded',
|
|
244
|
-
},
|
|
245
221
|
body: new URLSearchParams({
|
|
246
|
-
grant_type: 'refresh_token',
|
|
247
222
|
client_id: KEYCLOAK_CLIENT_ID,
|
|
223
|
+
grant_type: 'refresh_token',
|
|
248
224
|
refresh_token: refreshToken,
|
|
249
225
|
}),
|
|
226
|
+
headers: {
|
|
227
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
228
|
+
},
|
|
229
|
+
method: 'POST',
|
|
250
230
|
},
|
|
251
|
-
)
|
|
231
|
+
);
|
|
252
232
|
|
|
253
233
|
// Handle HTTP errors
|
|
254
234
|
if (!response.ok) {
|
|
255
|
-
logger.debug(
|
|
256
|
-
`Token refresh error: HTTP ${response.status} ${response.statusText}`,
|
|
257
|
-
)
|
|
235
|
+
logger.debug(`Token refresh error: HTTP ${response.status} ${response.statusText}`);
|
|
258
236
|
|
|
259
237
|
// Check if the refresh token itself is expired or invalid
|
|
260
238
|
if (response.status === 401 || response.status === 403) {
|
|
261
239
|
console.warn(
|
|
262
|
-
chalk.yellow(
|
|
263
|
-
|
|
264
|
-
),
|
|
265
|
-
)
|
|
240
|
+
chalk.yellow('Your refresh token has expired. Please run `berget auth login` again.'),
|
|
241
|
+
);
|
|
266
242
|
// Clear tokens if unauthorized - they're invalid
|
|
267
|
-
tokenManager.clearTokens()
|
|
243
|
+
tokenManager.clearTokens();
|
|
268
244
|
} else {
|
|
269
245
|
console.warn(
|
|
270
|
-
chalk.yellow(
|
|
271
|
-
|
|
272
|
-
),
|
|
273
|
-
)
|
|
246
|
+
chalk.yellow(`Failed to refresh token: ${response.status} ${response.statusText}`),
|
|
247
|
+
);
|
|
274
248
|
}
|
|
275
|
-
return false
|
|
249
|
+
return false;
|
|
276
250
|
}
|
|
277
251
|
|
|
278
252
|
// Parse the response
|
|
279
|
-
const contentType = response.headers.get('content-type')
|
|
253
|
+
const contentType = response.headers.get('content-type');
|
|
280
254
|
if (!contentType || !contentType.includes('application/json')) {
|
|
281
|
-
console.warn(
|
|
282
|
-
|
|
283
|
-
)
|
|
284
|
-
return false
|
|
255
|
+
console.warn(chalk.yellow(`Unexpected content type in response: ${contentType}`));
|
|
256
|
+
return false;
|
|
285
257
|
}
|
|
286
258
|
|
|
287
|
-
const data = await response.json()
|
|
259
|
+
const data = (await response.json()) as {
|
|
260
|
+
expires_in?: number;
|
|
261
|
+
refresh_token?: string;
|
|
262
|
+
token: string;
|
|
263
|
+
};
|
|
288
264
|
|
|
289
265
|
// Validate the response data
|
|
290
266
|
if (!data || !data.token) {
|
|
291
|
-
console.warn(
|
|
292
|
-
|
|
293
|
-
'Invalid token response. Please run `berget auth login` again.',
|
|
294
|
-
),
|
|
295
|
-
)
|
|
296
|
-
return false
|
|
267
|
+
console.warn(chalk.yellow('Invalid token response. Please run `berget auth login` again.'));
|
|
268
|
+
return false;
|
|
297
269
|
}
|
|
298
270
|
|
|
299
|
-
logger.debug('Token refreshed successfully')
|
|
271
|
+
logger.debug('Token refreshed successfully');
|
|
300
272
|
|
|
301
273
|
// Update the token
|
|
302
|
-
tokenManager.updateAccessToken(data.token, data.expires_in || 3600)
|
|
274
|
+
tokenManager.updateAccessToken(data.token, data.expires_in || 3600);
|
|
303
275
|
|
|
304
276
|
// If a new refresh token was provided, update that too
|
|
305
277
|
if (data.refresh_token) {
|
|
306
|
-
tokenManager.setTokens(
|
|
307
|
-
|
|
308
|
-
data.refresh_token,
|
|
309
|
-
data.expires_in || 3600,
|
|
310
|
-
)
|
|
311
|
-
logger.debug('Refresh token also updated')
|
|
278
|
+
tokenManager.setTokens(data.token, data.refresh_token, data.expires_in || 3600);
|
|
279
|
+
logger.debug('Refresh token also updated');
|
|
312
280
|
}
|
|
313
281
|
} catch (fetchError) {
|
|
314
282
|
console.warn(
|
|
315
283
|
chalk.yellow(
|
|
316
284
|
`Failed to refresh token: ${
|
|
317
|
-
fetchError instanceof Error
|
|
318
|
-
? fetchError.message
|
|
319
|
-
: String(fetchError)
|
|
285
|
+
fetchError instanceof Error ? fetchError.message : String(fetchError)
|
|
320
286
|
}`,
|
|
321
287
|
),
|
|
322
|
-
)
|
|
323
|
-
return false
|
|
288
|
+
);
|
|
289
|
+
return false;
|
|
324
290
|
}
|
|
325
291
|
|
|
326
|
-
return true
|
|
292
|
+
return true;
|
|
327
293
|
} catch (error) {
|
|
328
294
|
console.warn(
|
|
329
295
|
chalk.yellow(
|
|
@@ -331,7 +297,7 @@ async function refreshAccessToken(
|
|
|
331
297
|
error instanceof Error ? error.message : String(error)
|
|
332
298
|
}`,
|
|
333
299
|
),
|
|
334
|
-
)
|
|
335
|
-
return false
|
|
300
|
+
);
|
|
301
|
+
return false;
|
|
336
302
|
}
|
|
337
303
|
}
|