berget 2.2.7 → 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 +6 -6
- package/.github/workflows/test.yml +1 -1
- package/.prettierrc +5 -3
- package/dist/index.js +24 -25
- package/dist/package.json +5 -3
- package/dist/src/agents/app.js +8 -8
- package/dist/src/agents/backend.js +3 -3
- package/dist/src/agents/devops.js +8 -8
- package/dist/src/agents/frontend.js +3 -3
- package/dist/src/agents/fullstack.js +3 -3
- package/dist/src/agents/index.js +18 -18
- package/dist/src/agents/quality.js +8 -8
- package/dist/src/agents/security.js +8 -8
- package/dist/src/client.js +115 -127
- package/dist/src/commands/api-keys.js +195 -202
- package/dist/src/commands/auth.js +16 -25
- package/dist/src/commands/autocomplete.js +8 -8
- package/dist/src/commands/billing.js +10 -19
- package/dist/src/commands/chat.js +139 -170
- package/dist/src/commands/clusters.js +21 -30
- package/dist/src/commands/code/__tests__/auth-sync.test.js +189 -186
- package/dist/src/commands/code/__tests__/fake-api-key-service.js +3 -13
- package/dist/src/commands/code/__tests__/fake-auth-service.js +21 -29
- package/dist/src/commands/code/__tests__/fake-command-runner.js +22 -33
- package/dist/src/commands/code/__tests__/fake-file-store.js +19 -41
- package/dist/src/commands/code/__tests__/fake-prompter.js +81 -97
- package/dist/src/commands/code/__tests__/setup-flow.test.js +295 -295
- package/dist/src/commands/code/adapters/clack-prompter.js +15 -32
- package/dist/src/commands/code/adapters/fs-file-store.js +25 -44
- package/dist/src/commands/code/adapters/spawn-command-runner.js +27 -41
- package/dist/src/commands/code/auth-sync.js +215 -228
- package/dist/src/commands/code/errors.js +15 -12
- package/dist/src/commands/code/setup.js +390 -425
- package/dist/src/commands/code.js +279 -294
- package/dist/src/commands/index.js +5 -5
- package/dist/src/commands/models.js +16 -25
- package/dist/src/commands/users.js +9 -18
- package/dist/src/constants/command-structure.js +138 -138
- package/dist/src/services/api-key-service.js +132 -152
- package/dist/src/services/auth-service.js +81 -95
- package/dist/src/services/browser-auth.js +121 -131
- package/dist/src/services/chat-service.js +369 -386
- package/dist/src/services/cluster-service.js +47 -62
- package/dist/src/services/collaborator-service.js +9 -21
- package/dist/src/services/flux-service.js +13 -25
- package/dist/src/services/helm-service.js +9 -21
- package/dist/src/services/kubectl-service.js +15 -29
- package/dist/src/utils/config-checker.js +7 -7
- package/dist/src/utils/config-loader.js +109 -109
- package/dist/src/utils/default-api-key.js +129 -139
- package/dist/src/utils/env-manager.js +55 -66
- package/dist/src/utils/error-handler.js +62 -62
- package/dist/src/utils/logger.js +74 -67
- package/dist/src/utils/markdown-renderer.js +28 -28
- package/dist/src/utils/opencode-validator.js +67 -69
- package/dist/src/utils/token-manager.js +67 -65
- package/dist/tests/commands/chat.test.js +30 -39
- package/dist/tests/commands/code.test.js +186 -195
- package/dist/tests/utils/config-loader.test.js +107 -107
- package/dist/tests/utils/env-manager.test.js +81 -90
- package/dist/tests/utils/opencode-validator.test.js +42 -41
- package/dist/vitest.config.js +1 -1
- package/eslint.config.mjs +50 -30
- package/index.ts +30 -31
- package/package.json +5 -3
- package/src/agents/app.ts +9 -9
- package/src/agents/backend.ts +4 -4
- package/src/agents/devops.ts +9 -9
- package/src/agents/frontend.ts +4 -4
- package/src/agents/fullstack.ts +4 -4
- package/src/agents/index.ts +27 -25
- package/src/agents/quality.ts +9 -9
- package/src/agents/security.ts +9 -9
- package/src/agents/types.ts +10 -10
- package/src/client.ts +85 -77
- package/src/commands/api-keys.ts +190 -185
- package/src/commands/auth.ts +15 -14
- package/src/commands/autocomplete.ts +10 -10
- package/src/commands/billing.ts +13 -12
- package/src/commands/chat.ts +145 -142
- package/src/commands/clusters.ts +20 -19
- package/src/commands/code/__tests__/auth-sync.test.ts +176 -175
- package/src/commands/code/__tests__/fake-api-key-service.ts +2 -2
- package/src/commands/code/__tests__/fake-auth-service.ts +18 -18
- package/src/commands/code/__tests__/fake-command-runner.ts +28 -22
- package/src/commands/code/__tests__/fake-file-store.ts +15 -15
- package/src/commands/code/__tests__/fake-prompter.ts +86 -85
- package/src/commands/code/__tests__/setup-flow.test.ts +253 -251
- package/src/commands/code/adapters/clack-prompter.ts +32 -30
- package/src/commands/code/adapters/fs-file-store.ts +18 -17
- package/src/commands/code/adapters/spawn-command-runner.ts +20 -15
- package/src/commands/code/auth-sync.ts +210 -210
- package/src/commands/code/errors.ts +11 -11
- package/src/commands/code/ports/auth-services.ts +7 -7
- package/src/commands/code/ports/command-runner.ts +2 -2
- package/src/commands/code/ports/file-store.ts +3 -3
- package/src/commands/code/ports/prompter.ts +13 -13
- package/src/commands/code/setup.ts +408 -406
- package/src/commands/code.ts +288 -287
- package/src/commands/index.ts +11 -10
- package/src/commands/models.ts +19 -18
- package/src/commands/users.ts +11 -10
- package/src/constants/command-structure.ts +159 -159
- package/src/services/api-key-service.ts +85 -85
- package/src/services/auth-service.ts +55 -54
- package/src/services/browser-auth.ts +62 -62
- package/src/services/chat-service.ts +169 -170
- package/src/services/cluster-service.ts +28 -28
- package/src/services/collaborator-service.ts +6 -6
- package/src/services/flux-service.ts +17 -17
- package/src/services/helm-service.ts +11 -11
- package/src/services/kubectl-service.ts +12 -12
- package/src/types/api.d.ts +1933 -1933
- package/src/types/json.d.ts +1 -1
- package/src/utils/config-checker.ts +6 -6
- package/src/utils/config-loader.ts +130 -129
- package/src/utils/default-api-key.ts +81 -80
- package/src/utils/env-manager.ts +37 -37
- package/src/utils/error-handler.ts +64 -64
- package/src/utils/logger.ts +72 -66
- package/src/utils/markdown-renderer.ts +28 -28
- package/src/utils/opencode-validator.ts +72 -71
- package/src/utils/token-manager.ts +69 -68
- package/tests/commands/chat.test.ts +32 -31
- package/tests/commands/code.test.ts +182 -181
- package/tests/utils/config-loader.test.ts +111 -110
- package/tests/utils/env-manager.test.ts +83 -79
- package/tests/utils/opencode-validator.test.ts +43 -42
- package/tsconfig.json +2 -1
- package/vitest.config.ts +2 -2
|
@@ -22,64 +22,55 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
22
22
|
__setModuleDefault(result, mod);
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
-
});
|
|
33
|
-
};
|
|
34
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
26
|
exports.BrowserAuth = void 0;
|
|
36
|
-
const
|
|
37
|
-
const
|
|
27
|
+
const crypto = __importStar(require("node:crypto"));
|
|
28
|
+
const http = __importStar(require("node:http"));
|
|
38
29
|
class BrowserAuth {
|
|
30
|
+
options;
|
|
39
31
|
constructor(options) {
|
|
40
32
|
this.options = options;
|
|
41
33
|
}
|
|
42
|
-
start() {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const errorPage = (title, message) => `
|
|
34
|
+
async start() {
|
|
35
|
+
const { callbackPort, clientId, debug, keycloakUrl, realm } = this.options;
|
|
36
|
+
// Generate PKCE code verifier and challenge
|
|
37
|
+
const codeVerifier = this.generateCodeVerifier();
|
|
38
|
+
const codeChallenge = this.generateCodeChallenge(codeVerifier);
|
|
39
|
+
const state = crypto.randomBytes(16).toString('hex');
|
|
40
|
+
const redirectUri = `http://localhost:${callbackPort}/callback`;
|
|
41
|
+
// Build authorization URL
|
|
42
|
+
const authUrl = new URL(`${keycloakUrl}/realms/${realm}/protocol/openid-connect/auth`);
|
|
43
|
+
authUrl.searchParams.set('client_id', clientId);
|
|
44
|
+
authUrl.searchParams.set('response_type', 'code');
|
|
45
|
+
authUrl.searchParams.set('redirect_uri', redirectUri);
|
|
46
|
+
authUrl.searchParams.set('scope', 'openid email profile');
|
|
47
|
+
authUrl.searchParams.set('state', state);
|
|
48
|
+
authUrl.searchParams.set('code_challenge', codeChallenge);
|
|
49
|
+
authUrl.searchParams.set('code_challenge_method', 'S256');
|
|
50
|
+
// Create a promise that resolves when we receive the callback
|
|
51
|
+
const authResult = await new Promise((resolve) => {
|
|
52
|
+
let resolved = false;
|
|
53
|
+
const sockets = new Set();
|
|
54
|
+
const safeResolve = (result) => {
|
|
55
|
+
if (resolved)
|
|
56
|
+
return;
|
|
57
|
+
resolved = true;
|
|
58
|
+
clearTimeout(timeoutHandle);
|
|
59
|
+
server.close();
|
|
60
|
+
// Force-close all active sockets so the server stops immediately
|
|
61
|
+
for (const socket of sockets) {
|
|
62
|
+
socket.destroy();
|
|
63
|
+
}
|
|
64
|
+
sockets.clear();
|
|
65
|
+
resolve(result);
|
|
66
|
+
};
|
|
67
|
+
const server = http.createServer((request, res) => {
|
|
68
|
+
const requestUrl = new URL(request.url || '', `http://localhost:${callbackPort}`);
|
|
69
|
+
if (requestUrl.pathname === '/callback') {
|
|
70
|
+
const receivedState = requestUrl.searchParams.get('state') || '';
|
|
71
|
+
const code = requestUrl.searchParams.get('code') || '';
|
|
72
|
+
const error = requestUrl.searchParams.get('error') || '';
|
|
73
|
+
const errorPage = (title, message) => `
|
|
83
74
|
<!DOCTYPE html>
|
|
84
75
|
<html lang="en">
|
|
85
76
|
<head>
|
|
@@ -134,22 +125,22 @@ class BrowserAuth {
|
|
|
134
125
|
</body>
|
|
135
126
|
</html>
|
|
136
127
|
`;
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
128
|
+
// Set Connection: close so the browser doesn't keep the socket alive
|
|
129
|
+
// after we respond, and force-end the connection
|
|
130
|
+
if (error) {
|
|
131
|
+
res.writeHead(200, { Connection: 'close', 'Content-Type': 'text/html; charset=utf-8' });
|
|
132
|
+
res.end(errorPage('Authentication Failed', requestUrl.searchParams.get('error_description') || error));
|
|
133
|
+
safeResolve({ error, success: false });
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (receivedState !== state) {
|
|
137
|
+
res.writeHead(200, { Connection: 'close', 'Content-Type': 'text/html; charset=utf-8' });
|
|
138
|
+
res.end(errorPage('Authentication Failed', 'Invalid state parameter. Please try again.'));
|
|
139
|
+
safeResolve({ error: 'Invalid state parameter', success: false });
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
res.writeHead(200, { Connection: 'close', 'Content-Type': 'text/html; charset=utf-8' });
|
|
143
|
+
res.end(`
|
|
153
144
|
<!DOCTYPE html>
|
|
154
145
|
<html lang="en">
|
|
155
146
|
<head>
|
|
@@ -203,76 +194,75 @@ class BrowserAuth {
|
|
|
203
194
|
</body>
|
|
204
195
|
</html>
|
|
205
196
|
`);
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
});
|
|
214
|
-
server.listen(callbackPort, () => {
|
|
215
|
-
if (debug) {
|
|
216
|
-
console.log(`Callback server listening on port ${callbackPort}`);
|
|
217
|
-
}
|
|
218
|
-
});
|
|
219
|
-
// Set timeout for the server
|
|
220
|
-
const timeoutHandle = setTimeout(() => {
|
|
221
|
-
safeResolve({ success: false, error: "Authentication timed out" });
|
|
222
|
-
}, 5 * 60 * 1000); // 5 minute timeout
|
|
223
|
-
// Open browser
|
|
224
|
-
(() => __awaiter(this, void 0, void 0, function* () {
|
|
225
|
-
try {
|
|
226
|
-
const open = yield Promise.resolve().then(() => __importStar(require("open"))).then(m => m.default);
|
|
227
|
-
yield open(authUrl.toString());
|
|
228
|
-
}
|
|
229
|
-
catch (_a) {
|
|
230
|
-
// Browser failed to open - user must open URL manually
|
|
231
|
-
}
|
|
232
|
-
}))();
|
|
197
|
+
safeResolve({ code, success: true });
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
// Track sockets so we can destroy them on shutdown
|
|
201
|
+
server.on('connection', (socket) => {
|
|
202
|
+
sockets.add(socket);
|
|
203
|
+
socket.on('close', () => sockets.delete(socket));
|
|
233
204
|
});
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
// Exchange authorization code for tokens
|
|
241
|
-
const tokenUrl = `${keycloakUrl}/realms/${realm}/protocol/openid-connect/token`;
|
|
242
|
-
const tokenResponse = yield fetch(tokenUrl, {
|
|
243
|
-
method: "POST",
|
|
244
|
-
headers: {
|
|
245
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
246
|
-
},
|
|
247
|
-
body: new URLSearchParams({
|
|
248
|
-
grant_type: "authorization_code",
|
|
249
|
-
client_id: clientId,
|
|
250
|
-
code: authResult.code,
|
|
251
|
-
redirect_uri: redirectUri,
|
|
252
|
-
code_verifier: codeVerifier,
|
|
253
|
-
}).toString(),
|
|
205
|
+
server.listen(callbackPort, () => {
|
|
206
|
+
if (debug) {
|
|
207
|
+
console.log(`Callback server listening on port ${callbackPort}`);
|
|
208
|
+
}
|
|
254
209
|
});
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
210
|
+
// Set timeout for the server
|
|
211
|
+
const timeoutHandle = setTimeout(() => {
|
|
212
|
+
safeResolve({ error: 'Authentication timed out', success: false });
|
|
213
|
+
}, 5 * 60 * 1000); // 5 minute timeout
|
|
214
|
+
// Open browser
|
|
215
|
+
(async () => {
|
|
216
|
+
try {
|
|
217
|
+
const open = await Promise.resolve().then(() => __importStar(require('open'))).then((m) => m.default);
|
|
218
|
+
await open(authUrl.toString());
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
// Browser failed to open - user must open URL manually
|
|
222
|
+
}
|
|
223
|
+
})();
|
|
224
|
+
});
|
|
225
|
+
if (!authResult.success || !authResult.code) {
|
|
263
226
|
return {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
refreshToken: tokenData.refresh_token,
|
|
267
|
-
expiresIn: tokenData.expires_in,
|
|
227
|
+
error: authResult.error || 'Unknown error',
|
|
228
|
+
success: false,
|
|
268
229
|
};
|
|
230
|
+
}
|
|
231
|
+
// Exchange authorization code for tokens
|
|
232
|
+
const tokenUrl = `${keycloakUrl}/realms/${realm}/protocol/openid-connect/token`;
|
|
233
|
+
const tokenResponse = await fetch(tokenUrl, {
|
|
234
|
+
body: new URLSearchParams({
|
|
235
|
+
client_id: clientId,
|
|
236
|
+
code: authResult.code,
|
|
237
|
+
code_verifier: codeVerifier,
|
|
238
|
+
grant_type: 'authorization_code',
|
|
239
|
+
redirect_uri: redirectUri,
|
|
240
|
+
}).toString(),
|
|
241
|
+
headers: {
|
|
242
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
243
|
+
},
|
|
244
|
+
method: 'POST',
|
|
269
245
|
});
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
246
|
+
if (!tokenResponse.ok) {
|
|
247
|
+
const errorText = await tokenResponse.text();
|
|
248
|
+
return {
|
|
249
|
+
error: `Failed to exchange code for tokens: ${errorText}`,
|
|
250
|
+
success: false,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
const tokenData = (await tokenResponse.json());
|
|
254
|
+
return {
|
|
255
|
+
accessToken: tokenData.access_token,
|
|
256
|
+
expiresIn: tokenData.expires_in,
|
|
257
|
+
refreshToken: tokenData.refresh_token,
|
|
258
|
+
success: true,
|
|
259
|
+
};
|
|
273
260
|
}
|
|
274
261
|
generateCodeChallenge(verifier) {
|
|
275
|
-
return crypto.createHash(
|
|
262
|
+
return crypto.createHash('sha256').update(verifier).digest('base64url');
|
|
263
|
+
}
|
|
264
|
+
generateCodeVerifier() {
|
|
265
|
+
return crypto.randomBytes(32).toString('base64url');
|
|
276
266
|
}
|
|
277
267
|
}
|
|
278
268
|
exports.BrowserAuth = BrowserAuth;
|