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.
Files changed (144) hide show
  1. package/.github/workflows/publish.yml +6 -6
  2. package/.github/workflows/test.yml +11 -5
  3. package/.husky/pre-commit +1 -0
  4. package/.prettierignore +15 -0
  5. package/.prettierrc +5 -3
  6. package/CONTRIBUTING.md +38 -0
  7. package/README.md +2 -148
  8. package/dist/index.js +21 -21
  9. package/dist/package.json +28 -2
  10. package/dist/src/agents/app.js +28 -0
  11. package/dist/src/agents/backend.js +25 -0
  12. package/dist/src/agents/devops.js +34 -0
  13. package/dist/src/agents/frontend.js +25 -0
  14. package/dist/src/agents/fullstack.js +25 -0
  15. package/dist/src/agents/index.js +61 -0
  16. package/dist/src/agents/quality.js +70 -0
  17. package/dist/src/agents/security.js +26 -0
  18. package/dist/src/agents/types.js +2 -0
  19. package/dist/src/client.js +54 -62
  20. package/dist/src/commands/api-keys.js +132 -140
  21. package/dist/src/commands/auth.js +9 -9
  22. package/dist/src/commands/autocomplete.js +9 -9
  23. package/dist/src/commands/billing.js +7 -9
  24. package/dist/src/commands/chat.js +90 -92
  25. package/dist/src/commands/clusters.js +12 -12
  26. package/dist/src/commands/code/__tests__/auth-sync.test.js +348 -0
  27. package/dist/src/commands/code/__tests__/fake-api-key-service.js +23 -0
  28. package/dist/src/commands/code/__tests__/fake-auth-service.js +55 -0
  29. package/dist/src/commands/code/__tests__/fake-command-runner.js +5 -7
  30. package/dist/src/commands/code/__tests__/fake-file-store.js +9 -0
  31. package/dist/src/commands/code/__tests__/fake-prompter.js +60 -18
  32. package/dist/src/commands/code/__tests__/setup-flow.test.js +374 -107
  33. package/dist/src/commands/code/adapters/clack-prompter.js +10 -0
  34. package/dist/src/commands/code/adapters/fs-file-store.js +8 -3
  35. package/dist/src/commands/code/adapters/spawn-command-runner.js +15 -11
  36. package/dist/src/commands/code/auth-sync.js +283 -0
  37. package/dist/src/commands/code/errors.js +4 -4
  38. package/dist/src/commands/code/ports/auth-services.js +2 -0
  39. package/dist/src/commands/code/setup.js +234 -93
  40. package/dist/src/commands/code.js +139 -251
  41. package/dist/src/commands/models.js +13 -15
  42. package/dist/src/commands/users.js +6 -8
  43. package/dist/src/constants/command-structure.js +116 -116
  44. package/dist/src/services/api-key-service.js +43 -48
  45. package/dist/src/services/auth-service.js +60 -299
  46. package/dist/src/services/browser-auth.js +278 -0
  47. package/dist/src/services/chat-service.js +78 -91
  48. package/dist/src/services/cluster-service.js +6 -6
  49. package/dist/src/services/collaborator-service.js +5 -8
  50. package/dist/src/services/flux-service.js +5 -8
  51. package/dist/src/services/helm-service.js +5 -8
  52. package/dist/src/services/kubectl-service.js +7 -10
  53. package/dist/src/utils/config-checker.js +5 -5
  54. package/dist/src/utils/config-loader.js +25 -25
  55. package/dist/src/utils/default-api-key.js +23 -23
  56. package/dist/src/utils/env-manager.js +7 -7
  57. package/dist/src/utils/error-handler.js +60 -61
  58. package/dist/src/utils/logger.js +7 -7
  59. package/dist/src/utils/markdown-renderer.js +2 -2
  60. package/dist/src/utils/opencode-validator.js +17 -20
  61. package/dist/src/utils/token-manager.js +38 -11
  62. package/dist/tests/commands/chat.test.js +24 -24
  63. package/dist/tests/commands/code.test.js +147 -147
  64. package/dist/tests/utils/config-loader.test.js +114 -114
  65. package/dist/tests/utils/env-manager.test.js +57 -57
  66. package/dist/tests/utils/opencode-validator.test.js +33 -33
  67. package/dist/vitest.config.js +1 -1
  68. package/eslint.config.mjs +47 -0
  69. package/index.ts +42 -48
  70. package/package.json +28 -2
  71. package/src/agents/app.ts +27 -0
  72. package/src/agents/backend.ts +24 -0
  73. package/src/agents/devops.ts +33 -0
  74. package/src/agents/frontend.ts +24 -0
  75. package/src/agents/fullstack.ts +24 -0
  76. package/src/agents/index.ts +71 -0
  77. package/src/agents/quality.ts +69 -0
  78. package/src/agents/security.ts +26 -0
  79. package/src/agents/types.ts +17 -0
  80. package/src/client.ts +125 -167
  81. package/src/commands/api-keys.ts +261 -358
  82. package/src/commands/auth.ts +24 -30
  83. package/src/commands/autocomplete.ts +12 -12
  84. package/src/commands/billing.ts +22 -27
  85. package/src/commands/chat.ts +230 -323
  86. package/src/commands/clusters.ts +33 -33
  87. package/src/commands/code/__tests__/auth-sync.test.ts +481 -0
  88. package/src/commands/code/__tests__/fake-api-key-service.ts +13 -0
  89. package/src/commands/code/__tests__/fake-auth-service.ts +50 -0
  90. package/src/commands/code/__tests__/fake-command-runner.ts +39 -42
  91. package/src/commands/code/__tests__/fake-file-store.ts +32 -23
  92. package/src/commands/code/__tests__/fake-prompter.ts +107 -69
  93. package/src/commands/code/__tests__/setup-flow.test.ts +624 -270
  94. package/src/commands/code/adapters/clack-prompter.ts +50 -38
  95. package/src/commands/code/adapters/fs-file-store.ts +31 -27
  96. package/src/commands/code/adapters/spawn-command-runner.ts +33 -29
  97. package/src/commands/code/auth-sync.ts +329 -0
  98. package/src/commands/code/errors.ts +15 -15
  99. package/src/commands/code/ports/auth-services.ts +14 -0
  100. package/src/commands/code/ports/command-runner.ts +8 -4
  101. package/src/commands/code/ports/file-store.ts +5 -4
  102. package/src/commands/code/ports/prompter.ts +24 -18
  103. package/src/commands/code/setup.ts +545 -317
  104. package/src/commands/code.ts +271 -473
  105. package/src/commands/index.ts +19 -19
  106. package/src/commands/models.ts +32 -37
  107. package/src/commands/users.ts +15 -22
  108. package/src/constants/command-structure.ts +119 -142
  109. package/src/services/api-key-service.ts +96 -113
  110. package/src/services/auth-service.ts +92 -339
  111. package/src/services/browser-auth.ts +296 -0
  112. package/src/services/chat-service.ts +246 -279
  113. package/src/services/cluster-service.ts +29 -32
  114. package/src/services/collaborator-service.ts +13 -18
  115. package/src/services/flux-service.ts +16 -18
  116. package/src/services/helm-service.ts +16 -18
  117. package/src/services/kubectl-service.ts +12 -14
  118. package/src/types/api.d.ts +924 -926
  119. package/src/types/json.d.ts +3 -3
  120. package/src/utils/config-checker.ts +10 -10
  121. package/src/utils/config-loader.ts +110 -127
  122. package/src/utils/default-api-key.ts +81 -93
  123. package/src/utils/env-manager.ts +36 -40
  124. package/src/utils/error-handler.ts +83 -78
  125. package/src/utils/logger.ts +41 -41
  126. package/src/utils/markdown-renderer.ts +11 -11
  127. package/src/utils/opencode-validator.ts +51 -56
  128. package/src/utils/token-manager.ts +84 -64
  129. package/templates/agents/app.md +1 -0
  130. package/templates/agents/backend.md +1 -0
  131. package/templates/agents/devops.md +2 -0
  132. package/templates/agents/frontend.md +1 -0
  133. package/templates/agents/fullstack.md +1 -0
  134. package/templates/agents/quality.md +45 -40
  135. package/templates/agents/security.md +1 -0
  136. package/tests/commands/chat.test.ts +60 -70
  137. package/tests/commands/code.test.ts +330 -376
  138. package/tests/utils/config-loader.test.ts +260 -260
  139. package/tests/utils/env-manager.test.ts +127 -134
  140. package/tests/utils/opencode-validator.test.ts +58 -63
  141. package/tsconfig.json +2 -2
  142. package/vitest.config.ts +3 -3
  143. package/AGENTS.md +0 -374
  144. package/TODO.md +0 -19
@@ -1,27 +1,4 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
2
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
3
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
4
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -37,42 +14,32 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
37
14
  Object.defineProperty(exports, "__esModule", { value: true });
38
15
  exports.AuthService = void 0;
39
16
  const client_1 = require("../client");
40
- // We'll use dynamic import for 'open' to support ESM modules in CommonJS
41
17
  const chalk_1 = __importDefault(require("chalk"));
42
18
  const error_handler_1 = require("../utils/error-handler");
43
19
  const command_structure_1 = require("../constants/command-structure");
44
- const http = __importStar(require("http"));
45
- const crypto = __importStar(require("crypto"));
46
- const url = __importStar(require("url"));
20
+ const browser_auth_1 = require("./browser-auth");
47
21
  // Keycloak configuration based on environment
48
- const isStageMode = process.argv.includes('--stage');
49
- const isLocalMode = process.argv.includes('--local');
50
- const KEYCLOAK_URL = (isStageMode || isLocalMode)
51
- ? 'https://keycloak.stage.berget.ai'
52
- : 'https://keycloak.berget.ai';
53
- const KEYCLOAK_REALM = 'berget';
54
- const KEYCLOAK_CLIENT_ID = 'berget-code';
22
+ const isStageMode = process.argv.includes("--stage");
23
+ const isLocalMode = process.argv.includes("--local");
24
+ const KEYCLOAK_URL = isStageMode || isLocalMode ? "https://keycloak.stage.berget.ai" : "https://keycloak.berget.ai";
25
+ const KEYCLOAK_REALM = "berget";
26
+ const KEYCLOAK_CLIENT_ID = "berget-code";
55
27
  const CALLBACK_PORT = 8787;
56
- /**
57
- * Generate a random string for PKCE code_verifier
58
- */
59
- function generateCodeVerifier() {
60
- return crypto.randomBytes(32).toString('base64url');
61
- }
62
- /**
63
- * Generate code_challenge from code_verifier using S256 method
64
- */
65
- function generateCodeChallenge(verifier) {
66
- return crypto.createHash('sha256').update(verifier).digest('base64url');
28
+ function makeBrowserAuth(debug) {
29
+ return new browser_auth_1.BrowserAuth({
30
+ keycloakUrl: KEYCLOAK_URL,
31
+ realm: KEYCLOAK_REALM,
32
+ clientId: KEYCLOAK_CLIENT_ID,
33
+ callbackPort: CALLBACK_PORT,
34
+ debug,
35
+ });
67
36
  }
68
37
  /**
69
38
  * Service for authentication operations
70
39
  * Command group: auth
71
40
  */
72
41
  class AuthService {
73
- constructor() {
74
- this.client = (0, client_1.createAuthenticatedClient)();
75
- }
42
+ constructor() { }
76
43
  static getInstance() {
77
44
  if (!AuthService.instance) {
78
45
  AuthService.instance = new AuthService();
@@ -84,271 +51,41 @@ class AuthService {
84
51
  try {
85
52
  // Create fresh client to ensure we have the latest token
86
53
  const client = (0, client_1.createAuthenticatedClient)();
87
- const { data: profile, error } = yield client.GET('/v1/users/me');
54
+ const { data: profile, error } = yield client.GET("/v1/users/me");
88
55
  if (error) {
89
56
  return null;
90
57
  }
91
58
  return profile;
92
59
  }
93
- catch (error) {
60
+ catch (_a) {
94
61
  return null;
95
62
  }
96
63
  });
97
64
  }
65
+ /**
66
+ * Browser-based PKCE login for interactive CLI use.
67
+ * Prints status to stdout/stderr. Use loginInteractive() when you need
68
+ * a silent, UI-agnostic result (e.g. inside the setup wizard).
69
+ */
98
70
  login() {
99
71
  return __awaiter(this, void 0, void 0, function* () {
100
72
  try {
101
- // Clear any existing token to ensure a fresh login
102
73
  (0, client_1.clearAuthToken)();
103
- console.log(chalk_1.default.blue('Initiating login process...'));
104
- // Generate PKCE code verifier and challenge
105
- const codeVerifier = generateCodeVerifier();
106
- const codeChallenge = generateCodeChallenge(codeVerifier);
107
- const state = crypto.randomBytes(16).toString('hex');
108
- const redirectUri = `http://localhost:${CALLBACK_PORT}/callback`;
109
- // Build authorization URL
110
- const authUrl = new URL(`${KEYCLOAK_URL}/realms/${KEYCLOAK_REALM}/protocol/openid-connect/auth`);
111
- authUrl.searchParams.set('client_id', KEYCLOAK_CLIENT_ID);
112
- authUrl.searchParams.set('response_type', 'code');
113
- authUrl.searchParams.set('redirect_uri', redirectUri);
114
- authUrl.searchParams.set('scope', 'openid email profile');
115
- authUrl.searchParams.set('state', state);
116
- authUrl.searchParams.set('code_challenge', codeChallenge);
117
- authUrl.searchParams.set('code_challenge_method', 'S256');
118
- // Create a promise that resolves when we receive the callback
119
- const authResult = yield new Promise((resolve) => {
120
- const server = http.createServer((req, res) => __awaiter(this, void 0, void 0, function* () {
121
- const parsedUrl = url.parse(req.url || '', true);
122
- if (parsedUrl.pathname === '/callback') {
123
- const receivedState = parsedUrl.query.state;
124
- const code = parsedUrl.query.code;
125
- const error = parsedUrl.query.error;
126
- const errorPage = (title, message) => `
127
- <!DOCTYPE html>
128
- <html lang="en">
129
- <head>
130
- <meta charset="UTF-8">
131
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
132
- <title>Berget - Authentication Failed</title>
133
- <style>
134
- * { margin: 0; padding: 0; box-sizing: border-box; }
135
- body {
136
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
137
- display: flex;
138
- justify-content: center;
139
- align-items: center;
140
- min-height: 100vh;
141
- background: linear-gradient(135deg, #0f0f1a 0%, #1a1a2e 50%, #16213e 100%);
142
- color: #fff;
143
- }
144
- .container {
145
- text-align: center;
146
- padding: 3rem;
147
- max-width: 400px;
148
- }
149
- .icon {
150
- width: 80px;
151
- height: 80px;
152
- background: linear-gradient(135deg, #f87171 0%, #ef4444 100%);
153
- border-radius: 50%;
154
- display: flex;
155
- align-items: center;
156
- justify-content: center;
157
- margin: 0 auto 1.5rem;
158
- box-shadow: 0 4px 20px rgba(248, 113, 113, 0.3);
159
- }
160
- .icon svg {
161
- width: 40px;
162
- height: 40px;
163
- stroke: #fff;
164
- stroke-width: 3;
165
- }
166
- h1 {
167
- font-size: 1.5rem;
168
- font-weight: 600;
169
- margin-bottom: 0.75rem;
170
- color: #fff;
171
- }
172
- p {
173
- color: #94a3b8;
174
- font-size: 0.95rem;
175
- line-height: 1.5;
176
- }
177
- .brand {
178
- margin-top: 2rem;
179
- opacity: 0.5;
180
- font-size: 0.8rem;
181
- letter-spacing: 0.05em;
182
- }
183
- </style>
184
- </head>
185
- <body>
186
- <div class="container">
187
- <div class="icon">
188
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
189
- <line x1="18" y1="6" x2="6" y2="18"></line>
190
- <line x1="6" y1="6" x2="18" y2="18"></line>
191
- </svg>
192
- </div>
193
- <h1>${title}</h1>
194
- <p>${message}</p>
195
- <div class="brand">BERGET</div>
196
- </div>
197
- </body>
198
- </html>
199
- `;
200
- if (error) {
201
- res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
202
- res.end(errorPage('Authentication Failed', String(parsedUrl.query.error_description || error)));
203
- server.close();
204
- resolve({ success: false, error });
205
- return;
206
- }
207
- if (receivedState !== state) {
208
- res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
209
- res.end(errorPage('Authentication Failed', 'Invalid state parameter. Please try again.'));
210
- server.close();
211
- resolve({ success: false, error: 'Invalid state parameter' });
212
- return;
213
- }
214
- res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
215
- res.end(`
216
- <!DOCTYPE html>
217
- <html lang="en">
218
- <head>
219
- <meta charset="UTF-8">
220
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
221
- <title>Berget - Authentication Successful</title>
222
- <style>
223
- * { margin: 0; padding: 0; box-sizing: border-box; }
224
- body {
225
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
226
- display: flex;
227
- justify-content: center;
228
- align-items: center;
229
- min-height: 100vh;
230
- background: linear-gradient(135deg, #0f0f1a 0%, #1a1a2e 50%, #16213e 100%);
231
- color: #fff;
232
- }
233
- .container {
234
- text-align: center;
235
- padding: 3rem;
236
- max-width: 400px;
237
- }
238
- .icon {
239
- width: 80px;
240
- height: 80px;
241
- background: linear-gradient(135deg, #4ade80 0%, #22c55e 100%);
242
- border-radius: 50%;
243
- display: flex;
244
- align-items: center;
245
- justify-content: center;
246
- margin: 0 auto 1.5rem;
247
- box-shadow: 0 4px 20px rgba(74, 222, 128, 0.3);
248
- }
249
- .icon svg {
250
- width: 40px;
251
- height: 40px;
252
- stroke: #fff;
253
- stroke-width: 3;
254
- }
255
- h1 {
256
- font-size: 1.5rem;
257
- font-weight: 600;
258
- margin-bottom: 0.75rem;
259
- color: #fff;
260
- }
261
- p {
262
- color: #94a3b8;
263
- font-size: 0.95rem;
264
- line-height: 1.5;
265
- }
266
- .brand {
267
- margin-top: 2rem;
268
- opacity: 0.5;
269
- font-size: 0.8rem;
270
- letter-spacing: 0.05em;
271
- }
272
- </style>
273
- </head>
274
- <body>
275
- <div class="container">
276
- <div class="icon">
277
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
278
- <polyline points="20 6 9 17 4 12"></polyline>
279
- </svg>
280
- </div>
281
- <h1>Authentication Successful</h1>
282
- <p>You can close this window and return to your terminal.</p>
283
- <div class="brand">BERGET</div>
284
- </div>
285
- </body>
286
- </html>
287
- `);
288
- server.close();
289
- resolve({ success: true, code });
290
- }
291
- }));
292
- server.listen(CALLBACK_PORT, () => {
293
- if (process.argv.includes('--debug')) {
294
- console.log(chalk_1.default.dim(`Callback server listening on port ${CALLBACK_PORT}`));
295
- }
296
- });
297
- // Set timeout for the server
298
- setTimeout(() => {
299
- server.close();
300
- resolve({ success: false, error: 'Authentication timed out' });
301
- }, 5 * 60 * 1000) // 5 minute timeout
302
- ;
303
- (() => __awaiter(this, void 0, void 0, function* () {
304
- try {
305
- const open = yield Promise.resolve().then(() => __importStar(require('open'))).then((m) => m.default);
306
- yield open(authUrl.toString());
307
- console.log(chalk_1.default.dim('Browser opened for authentication...'));
308
- }
309
- catch (_a) {
310
- console.log(chalk_1.default.cyan('\nPlease open this URL in your browser:'));
311
- console.log(chalk_1.default.bold(authUrl.toString()));
312
- }
313
- }))();
314
- });
315
- if (!authResult.success || !authResult.code) {
316
- console.log(chalk_1.default.red(`\nAuthentication failed: ${authResult.error || 'Unknown error'}`));
317
- return false;
318
- }
319
- // Exchange authorization code for tokens
320
- console.log(chalk_1.default.dim('Exchanging authorization code for tokens...'));
321
- const tokenUrl = `${KEYCLOAK_URL}/realms/${KEYCLOAK_REALM}/protocol/openid-connect/token`;
322
- const tokenResponse = yield fetch(tokenUrl, {
323
- method: 'POST',
324
- headers: {
325
- 'Content-Type': 'application/x-www-form-urlencoded',
326
- },
327
- body: new URLSearchParams({
328
- grant_type: 'authorization_code',
329
- client_id: KEYCLOAK_CLIENT_ID,
330
- code: authResult.code,
331
- redirect_uri: redirectUri,
332
- code_verifier: codeVerifier,
333
- }).toString(),
334
- });
335
- if (!tokenResponse.ok) {
336
- const errorText = yield tokenResponse.text();
337
- console.log(chalk_1.default.red(`\nFailed to exchange code for tokens: ${errorText}`));
74
+ console.log(chalk_1.default.blue("Initiating login process..."));
75
+ const auth = makeBrowserAuth(process.argv.includes("--debug"));
76
+ const result = yield auth.start();
77
+ if (!result.success) {
78
+ console.log(chalk_1.default.red(`\nAuthentication failed: ${result.error || "Unknown error"}`));
338
79
  return false;
339
80
  }
340
- const tokenData = (yield tokenResponse.json());
341
- // Save tokens
342
- (0, client_1.saveAuthToken)(tokenData.access_token, tokenData.refresh_token, tokenData.expires_in);
343
- if (process.argv.includes('--debug')) {
344
- console.log(chalk_1.default.yellow('DEBUG: Token data received:'));
81
+ (0, client_1.saveAuthToken)(result.accessToken, result.refreshToken, result.expiresIn);
82
+ if (process.argv.includes("--debug")) {
83
+ console.log(chalk_1.default.yellow("DEBUG: Token data received:"));
345
84
  console.log(chalk_1.default.yellow(JSON.stringify({
346
- expires_in: tokenData.expires_in,
347
- refresh_expires_in: tokenData.refresh_expires_in,
85
+ expires_in: result.expiresIn,
348
86
  }, null, 2)));
349
87
  }
350
- console.log(chalk_1.default.green('\n✓ Successfully logged in to Berget'));
351
- // Try to get user info
88
+ console.log(chalk_1.default.green("\n✓ Successfully logged in to Berget"));
352
89
  try {
353
90
  const profile = yield this.whoami();
354
91
  if (profile === null || profile === void 0 ? void 0 : profile.email) {
@@ -358,17 +95,41 @@ class AuthService {
358
95
  catch (_a) {
359
96
  // Ignore errors fetching profile
360
97
  }
361
- console.log(chalk_1.default.cyan('\nNext steps:'));
362
- console.log(chalk_1.default.cyan(' • Create an API key: berget api-keys create'));
363
- console.log(chalk_1.default.cyan(' • Setup OpenCode: berget code init'));
98
+ console.log(chalk_1.default.cyan("\nNext steps:"));
99
+ console.log(chalk_1.default.cyan(" • Create an API key: berget api-keys create"));
100
+ console.log(chalk_1.default.cyan(" • Setup OpenCode: berget code init"));
364
101
  return true;
365
102
  }
366
103
  catch (error) {
367
- (0, error_handler_1.handleError)('Login failed', error);
104
+ (0, error_handler_1.handleError)("Login failed", error);
368
105
  return false;
369
106
  }
370
107
  });
371
108
  }
109
+ /**
110
+ * Browser-based PKCE login for wizard / programmatic use.
111
+ * Does NOT print to stdout — returns tokens so callers can display
112
+ * their own UI (e.g. via clack/prompts).
113
+ */
114
+ loginInteractive() {
115
+ return __awaiter(this, void 0, void 0, function* () {
116
+ try {
117
+ (0, client_1.clearAuthToken)();
118
+ const auth = makeBrowserAuth(process.argv.includes("--debug"));
119
+ const result = yield auth.start();
120
+ if (result.success) {
121
+ (0, client_1.saveAuthToken)(result.accessToken, result.refreshToken, result.expiresIn);
122
+ }
123
+ return result;
124
+ }
125
+ catch (error) {
126
+ return {
127
+ success: false,
128
+ error: error instanceof Error ? error.message : String(error),
129
+ };
130
+ }
131
+ });
132
+ }
372
133
  }
373
134
  exports.AuthService = AuthService;
374
135
  // Command group name for this service