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
@@ -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 http = __importStar(require("http"));
37
- const crypto = __importStar(require("crypto"));
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
- return __awaiter(this, void 0, void 0, function* () {
44
- const { keycloakUrl, realm, clientId, callbackPort, debug } = this.options;
45
- // Generate PKCE code verifier and challenge
46
- const codeVerifier = this.generateCodeVerifier();
47
- const codeChallenge = this.generateCodeChallenge(codeVerifier);
48
- const state = crypto.randomBytes(16).toString("hex");
49
- const redirectUri = `http://localhost:${callbackPort}/callback`;
50
- // Build authorization URL
51
- const authUrl = new URL(`${keycloakUrl}/realms/${realm}/protocol/openid-connect/auth`);
52
- authUrl.searchParams.set("client_id", clientId);
53
- authUrl.searchParams.set("response_type", "code");
54
- authUrl.searchParams.set("redirect_uri", redirectUri);
55
- authUrl.searchParams.set("scope", "openid email profile");
56
- authUrl.searchParams.set("state", state);
57
- authUrl.searchParams.set("code_challenge", codeChallenge);
58
- authUrl.searchParams.set("code_challenge_method", "S256");
59
- // Create a promise that resolves when we receive the callback
60
- const authResult = yield new Promise(resolve => {
61
- let resolved = false;
62
- const sockets = new Set();
63
- const safeResolve = (result) => {
64
- if (resolved)
65
- return;
66
- resolved = true;
67
- clearTimeout(timeoutHandle);
68
- server.close();
69
- // Force-close all active sockets so the server stops immediately
70
- for (const socket of sockets) {
71
- socket.destroy();
72
- }
73
- sockets.clear();
74
- resolve(result);
75
- };
76
- const server = http.createServer((req, res) => {
77
- const requestUrl = new URL(req.url || "", `http://localhost:${callbackPort}`);
78
- if (requestUrl.pathname === "/callback") {
79
- const receivedState = requestUrl.searchParams.get("state") || "";
80
- const code = requestUrl.searchParams.get("code") || "";
81
- const error = requestUrl.searchParams.get("error") || "";
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
- // Set Connection: close so the browser doesn't keep the socket alive
138
- // after we respond, and force-end the connection
139
- if (error) {
140
- res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", Connection: "close" });
141
- res.end(errorPage("Authentication Failed", requestUrl.searchParams.get("error_description") || error));
142
- safeResolve({ success: false, error });
143
- return;
144
- }
145
- if (receivedState !== state) {
146
- res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", Connection: "close" });
147
- res.end(errorPage("Authentication Failed", "Invalid state parameter. Please try again."));
148
- safeResolve({ success: false, error: "Invalid state parameter" });
149
- return;
150
- }
151
- res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", Connection: "close" });
152
- res.end(`
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
- safeResolve({ success: true, code });
207
- }
208
- });
209
- // Track sockets so we can destroy them on shutdown
210
- server.on("connection", (socket) => {
211
- sockets.add(socket);
212
- socket.on("close", () => sockets.delete(socket));
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
- if (!authResult.success || !authResult.code) {
235
- return {
236
- success: false,
237
- error: authResult.error || "Unknown error",
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
- if (!tokenResponse.ok) {
256
- const errorText = yield tokenResponse.text();
257
- return {
258
- success: false,
259
- error: `Failed to exchange code for tokens: ${errorText}`,
260
- };
261
- }
262
- const tokenData = (yield tokenResponse.json());
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
- success: true,
265
- accessToken: tokenData.access_token,
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
- generateCodeVerifier() {
272
- return crypto.randomBytes(32).toString("base64url");
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("sha256").update(verifier).digest("base64url");
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;