@lanonasis/cli 3.1.13 → 3.3.15

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.
@@ -36,6 +36,340 @@ async function handleAuthDelay(config) {
36
36
  console.log();
37
37
  }
38
38
  }
39
+ // Enhanced authentication failure handler
40
+ async function handleAuthenticationFailure(error, config, authMethod = 'jwt') {
41
+ // Increment failure count
42
+ await config.incrementFailureCount();
43
+ const failureCount = config.getFailureCount();
44
+ // Determine error type and provide specific guidance
45
+ const errorType = categorizeAuthError(error);
46
+ console.log();
47
+ console.log(chalk.red('✖ Authentication failed'));
48
+ switch (errorType) {
49
+ case 'invalid_credentials':
50
+ console.log(chalk.red('Invalid credentials provided'));
51
+ if (authMethod === 'vendor_key') {
52
+ console.log(chalk.gray('• Check your vendor key format: pk_xxx.sk_xxx'));
53
+ console.log(chalk.gray('• Verify the key is active in your account dashboard'));
54
+ console.log(chalk.gray('• Ensure you copied the complete key including both parts'));
55
+ }
56
+ else {
57
+ console.log(chalk.gray('• Double-check your email and password'));
58
+ console.log(chalk.gray('• Passwords are case-sensitive'));
59
+ console.log(chalk.gray('• Consider resetting your password if needed'));
60
+ }
61
+ break;
62
+ case 'network_error':
63
+ console.log(chalk.red('Network connection failed'));
64
+ console.log(chalk.gray('• Check your internet connection'));
65
+ console.log(chalk.gray('• Verify you can access https://api.lanonasis.com'));
66
+ console.log(chalk.gray('• Try again in a few moments'));
67
+ if (failureCount >= 2) {
68
+ console.log(chalk.gray('• Consider using a different network if issues persist'));
69
+ }
70
+ break;
71
+ case 'server_error':
72
+ console.log(chalk.red('Server temporarily unavailable'));
73
+ console.log(chalk.gray('• The authentication service may be experiencing issues'));
74
+ console.log(chalk.gray('• Please try again in a few minutes'));
75
+ console.log(chalk.gray('• Check https://status.lanonasis.com for service status'));
76
+ break;
77
+ case 'rate_limited':
78
+ console.log(chalk.red('Too many authentication attempts'));
79
+ console.log(chalk.gray('• Please wait before trying again'));
80
+ console.log(chalk.gray('• Rate limiting helps protect your account'));
81
+ console.log(chalk.gray('• Consider using a vendor key for automated access'));
82
+ break;
83
+ case 'expired_token':
84
+ console.log(chalk.red('Authentication token has expired'));
85
+ console.log(chalk.gray('• Please log in again to refresh your session'));
86
+ console.log(chalk.gray('• Consider using a vendor key for longer-term access'));
87
+ await config.clearInvalidCredentials();
88
+ break;
89
+ default:
90
+ console.log(chalk.red(`Unexpected error: ${error.message || 'Unknown error'}`));
91
+ console.log(chalk.gray('• Please try again'));
92
+ console.log(chalk.gray('• If the problem persists, contact support'));
93
+ }
94
+ // Progressive guidance for repeated failures
95
+ if (failureCount >= 3) {
96
+ console.log();
97
+ console.log(chalk.yellow('💡 Multiple failures detected. Recovery options:'));
98
+ if (authMethod === 'vendor_key') {
99
+ console.log(chalk.cyan('• Generate a new vendor key from your dashboard'));
100
+ console.log(chalk.cyan('• Try: lanonasis auth logout && lanonasis auth login'));
101
+ console.log(chalk.cyan('• Switch to browser login: lanonasis auth login --use-web-auth'));
102
+ }
103
+ else {
104
+ console.log(chalk.cyan('• Reset your password if you\'re unsure'));
105
+ console.log(chalk.cyan('• Try vendor key authentication instead'));
106
+ console.log(chalk.cyan('• Clear stored config: lanonasis auth logout'));
107
+ }
108
+ if (failureCount >= 5) {
109
+ console.log(chalk.yellow('• Consider contacting support if issues persist'));
110
+ console.log(chalk.gray('• Include error details and your email address'));
111
+ }
112
+ }
113
+ }
114
+ // Categorize authentication errors for specific handling
115
+ function categorizeAuthError(error) {
116
+ if (!error)
117
+ return 'unknown';
118
+ // Check HTTP status codes
119
+ if (error.response?.status) {
120
+ const status = error.response.status;
121
+ switch (status) {
122
+ case 401:
123
+ // Check if it's specifically an expired token
124
+ if (error.response.data?.error?.includes('expired') || error.response.data?.message?.includes('expired')) {
125
+ return 'expired_token';
126
+ }
127
+ return 'invalid_credentials';
128
+ case 403:
129
+ return 'invalid_credentials';
130
+ case 429:
131
+ return 'rate_limited';
132
+ case 500:
133
+ case 502:
134
+ case 503:
135
+ case 504:
136
+ return 'server_error';
137
+ }
138
+ }
139
+ // Check error codes for network issues
140
+ if (error.code) {
141
+ switch (error.code) {
142
+ case 'ECONNREFUSED':
143
+ case 'ENOTFOUND':
144
+ case 'ECONNRESET':
145
+ case 'ETIMEDOUT':
146
+ case 'ENETUNREACH':
147
+ return 'network_error';
148
+ }
149
+ }
150
+ // Check error messages
151
+ const message = error.message?.toLowerCase() || '';
152
+ if (message.includes('network') || message.includes('connection') || message.includes('timeout')) {
153
+ return 'network_error';
154
+ }
155
+ if (message.includes('invalid') || message.includes('unauthorized') || message.includes('forbidden')) {
156
+ return 'invalid_credentials';
157
+ }
158
+ if (message.includes('expired')) {
159
+ return 'expired_token';
160
+ }
161
+ if (message.includes('rate limit') || message.includes('too many')) {
162
+ return 'rate_limited';
163
+ }
164
+ return 'unknown';
165
+ }
166
+ export async function diagnoseCommand() {
167
+ const config = new CLIConfig();
168
+ await config.init();
169
+ console.log(chalk.blue.bold('🔍 Authentication Diagnostic'));
170
+ console.log(colors.info('━'.repeat(50)));
171
+ console.log();
172
+ const diagnostics = {
173
+ configExists: false,
174
+ hasCredentials: false,
175
+ credentialType: 'none',
176
+ credentialsValid: false,
177
+ tokenExpired: false,
178
+ authFailures: 0,
179
+ lastFailure: null,
180
+ endpointsReachable: false,
181
+ serviceDiscovery: false,
182
+ deviceId: null
183
+ };
184
+ // Step 1: Check if config exists
185
+ console.log(chalk.cyan('1. Configuration File'));
186
+ try {
187
+ const configExists = await config.exists();
188
+ diagnostics.configExists = configExists;
189
+ if (configExists) {
190
+ console.log(chalk.green(' ✓ Config file exists at'), config.getConfigPath());
191
+ }
192
+ else {
193
+ console.log(chalk.red(' ✖ Config file not found at'), config.getConfigPath());
194
+ console.log(chalk.gray(' → Run: lanonasis auth login'));
195
+ }
196
+ }
197
+ catch (error) {
198
+ console.log(chalk.red(' ✖ Error checking config:'), error instanceof Error ? error.message : 'Unknown error');
199
+ }
200
+ // Step 2: Check stored credentials
201
+ console.log(chalk.cyan('\n2. Stored Credentials'));
202
+ const token = config.getToken();
203
+ const vendorKey = config.getVendorKey();
204
+ const authMethod = config.get('authMethod');
205
+ if (vendorKey) {
206
+ diagnostics.hasCredentials = true;
207
+ diagnostics.credentialType = 'vendor_key';
208
+ console.log(chalk.green(' ✓ Vendor key found'));
209
+ // Validate vendor key format
210
+ const formatValidation = config.validateVendorKeyFormat(vendorKey);
211
+ if (formatValidation === true) {
212
+ console.log(chalk.green(' ✓ Vendor key format is valid'));
213
+ }
214
+ else {
215
+ console.log(chalk.red(' ✖ Vendor key format is invalid:'));
216
+ console.log(chalk.gray(` ${formatValidation}`));
217
+ }
218
+ }
219
+ else if (token) {
220
+ diagnostics.hasCredentials = true;
221
+ diagnostics.credentialType = authMethod === 'oauth' ? 'oauth' : 'jwt';
222
+ console.log(chalk.green(` ✓ ${diagnostics.credentialType.toUpperCase()} token found`));
223
+ // Check token expiry
224
+ try {
225
+ const isAuth = await config.isAuthenticated();
226
+ if (!isAuth) {
227
+ diagnostics.tokenExpired = true;
228
+ console.log(chalk.red(' ✖ Token is expired'));
229
+ }
230
+ else {
231
+ console.log(chalk.green(' ✓ Token is not expired'));
232
+ }
233
+ }
234
+ catch (error) {
235
+ console.log(chalk.yellow(' ⚠ Could not validate token expiry'));
236
+ }
237
+ }
238
+ else {
239
+ console.log(chalk.red(' ✖ No credentials found'));
240
+ console.log(chalk.gray(' → Run: lanonasis auth login'));
241
+ }
242
+ // Step 3: Check authentication failures
243
+ console.log(chalk.cyan('\n3. Authentication History'));
244
+ diagnostics.authFailures = config.getFailureCount();
245
+ diagnostics.lastFailure = config.getLastAuthFailure() ?? null;
246
+ if (diagnostics.authFailures === 0) {
247
+ console.log(chalk.green(' ✓ No recent authentication failures'));
248
+ }
249
+ else {
250
+ console.log(chalk.yellow(` ⚠ ${diagnostics.authFailures} recent authentication failures`));
251
+ if (diagnostics.lastFailure) {
252
+ const lastFailureDate = new Date(diagnostics.lastFailure);
253
+ console.log(chalk.gray(` Last failure: ${lastFailureDate.toLocaleString()}`));
254
+ }
255
+ if (config.shouldDelayAuth()) {
256
+ const delayMs = config.getAuthDelayMs();
257
+ console.log(chalk.yellow(` ⚠ Authentication delay active: ${Math.round(delayMs / 1000)}s`));
258
+ }
259
+ }
260
+ // Step 4: Test credential validation against server
261
+ console.log(chalk.cyan('\n4. Server Validation'));
262
+ if (diagnostics.hasCredentials) {
263
+ const spinner = ora('Testing credentials against server...').start();
264
+ try {
265
+ const isValid = await config.validateStoredCredentials();
266
+ diagnostics.credentialsValid = isValid;
267
+ if (isValid) {
268
+ spinner.succeed('Credentials are valid');
269
+ console.log(chalk.green(' ✓ Server authentication successful'));
270
+ }
271
+ else {
272
+ spinner.fail('Credentials are invalid');
273
+ console.log(chalk.red(' ✖ Server rejected credentials'));
274
+ console.log(chalk.gray(' → Try: lanonasis auth login'));
275
+ }
276
+ }
277
+ catch (error) {
278
+ spinner.fail('Server validation failed');
279
+ console.log(chalk.red(' ✖ Could not validate with server:'));
280
+ console.log(chalk.gray(` ${error instanceof Error ? error.message : 'Unknown error'}`));
281
+ }
282
+ }
283
+ else {
284
+ console.log(chalk.gray(' - Skipped (no credentials to validate)'));
285
+ }
286
+ // Step 5: Test endpoint connectivity
287
+ console.log(chalk.cyan('\n5. Endpoint Connectivity'));
288
+ const spinner2 = ora('Testing authentication endpoints...').start();
289
+ try {
290
+ await config.discoverServices();
291
+ diagnostics.serviceDiscovery = true;
292
+ const services = config.get('discoveredServices');
293
+ if (services) {
294
+ spinner2.succeed('Authentication endpoints reachable');
295
+ console.log(chalk.green(' ✓ Service discovery successful'));
296
+ console.log(chalk.gray(` Auth endpoint: ${services.auth_base}`));
297
+ diagnostics.endpointsReachable = true;
298
+ }
299
+ else {
300
+ spinner2.warn('Using fallback endpoints');
301
+ console.log(chalk.yellow(' ⚠ Service discovery failed, using fallbacks'));
302
+ diagnostics.endpointsReachable = true; // Fallbacks still work
303
+ }
304
+ }
305
+ catch (error) {
306
+ spinner2.fail('Endpoint connectivity failed');
307
+ console.log(chalk.red(' ✖ Cannot reach authentication endpoints'));
308
+ console.log(chalk.gray(` ${error instanceof Error ? error.message : 'Unknown error'}`));
309
+ console.log(chalk.gray(' → Check internet connection'));
310
+ }
311
+ // Step 6: Device identification
312
+ console.log(chalk.cyan('\n6. Device Information'));
313
+ try {
314
+ const deviceId = await config.getDeviceId();
315
+ diagnostics.deviceId = deviceId;
316
+ console.log(chalk.green(' ✓ Device ID:'), chalk.gray(deviceId));
317
+ }
318
+ catch (error) {
319
+ console.log(chalk.yellow(' ⚠ Could not get device ID'));
320
+ }
321
+ // Summary and recommendations
322
+ console.log(chalk.blue.bold('\n📋 Diagnostic Summary'));
323
+ console.log(colors.info('━'.repeat(50)));
324
+ const issues = [];
325
+ const recommendations = [];
326
+ if (!diagnostics.configExists) {
327
+ issues.push('No configuration file found');
328
+ recommendations.push('Run: lanonasis auth login');
329
+ }
330
+ if (!diagnostics.hasCredentials) {
331
+ issues.push('No authentication credentials stored');
332
+ recommendations.push('Run: lanonasis auth login --vendor-key pk_xxx.sk_xxx');
333
+ }
334
+ if (diagnostics.hasCredentials && !diagnostics.credentialsValid) {
335
+ issues.push('Stored credentials are invalid');
336
+ recommendations.push('Run: lanonasis auth logout && lanonasis auth login');
337
+ }
338
+ if (diagnostics.tokenExpired) {
339
+ issues.push('Authentication token has expired');
340
+ recommendations.push('Run: lanonasis auth login');
341
+ }
342
+ if (diagnostics.authFailures >= 3) {
343
+ issues.push(`Multiple authentication failures (${diagnostics.authFailures})`);
344
+ recommendations.push('Wait for delay period, then try: lanonasis auth login');
345
+ }
346
+ if (!diagnostics.endpointsReachable) {
347
+ issues.push('Cannot reach authentication endpoints');
348
+ recommendations.push('Check internet connection and firewall settings');
349
+ }
350
+ if (issues.length === 0) {
351
+ console.log(chalk.green('✅ All authentication checks passed!'));
352
+ console.log(chalk.cyan(' Your authentication is working correctly.'));
353
+ }
354
+ else {
355
+ console.log(chalk.red(`❌ Found ${issues.length} issue(s):`));
356
+ issues.forEach(issue => {
357
+ console.log(chalk.red(` • ${issue}`));
358
+ });
359
+ console.log(chalk.yellow('\n💡 Recommended actions:'));
360
+ recommendations.forEach(rec => {
361
+ console.log(chalk.cyan(` • ${rec}`));
362
+ });
363
+ }
364
+ // Additional troubleshooting info
365
+ if (diagnostics.authFailures > 0 || !diagnostics.credentialsValid) {
366
+ console.log(chalk.gray('\n🔧 Additional troubleshooting:'));
367
+ console.log(chalk.gray(' • Verify your vendor key format: pk_xxx.sk_xxx'));
368
+ console.log(chalk.gray(' • Check if your key is active in the dashboard'));
369
+ console.log(chalk.gray(' • Try browser authentication: lanonasis auth login --use-web-auth'));
370
+ console.log(chalk.gray(' • Contact support if issues persist'));
371
+ }
372
+ }
39
373
  export async function loginCommand(options) {
40
374
  const config = new CLIConfig();
41
375
  await config.init();
@@ -109,19 +443,8 @@ async function handleVendorKeyAuth(vendorKey, config) {
109
443
  }
110
444
  catch (error) {
111
445
  spinner.fail('Vendor key validation failed');
112
- // Increment failure count for failed authentication
113
- await config.incrementFailureCount();
114
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
115
- console.error(chalk.red('✖ Invalid vendor key:'), errorMessage);
116
- // Provide guidance for repeated failures
117
- const failureCount = config.getFailureCount();
118
- if (failureCount >= 3) {
119
- console.log();
120
- console.log(chalk.yellow('💡 Troubleshooting tips:'));
121
- console.log(chalk.gray('• Verify your vendor key format: pk_xxx.sk_xxx'));
122
- console.log(chalk.gray('• Check if your key is active in your account dashboard'));
123
- console.log(chalk.gray('• Try: lanonasis auth logout && lanonasis auth login'));
124
- }
446
+ // Use enhanced error handling
447
+ await handleAuthenticationFailure(error, config, 'vendor_key');
125
448
  process.exit(1);
126
449
  }
127
450
  }
@@ -130,6 +453,13 @@ async function handleVendorKeyFlow(config) {
130
453
  console.log(chalk.yellow('🔑 Vendor Key Authentication'));
131
454
  console.log(chalk.gray('Vendor keys provide secure API access with format: pk_xxx.sk_xxx'));
132
455
  console.log();
456
+ // Enhanced guidance for obtaining vendor keys
457
+ console.log(chalk.cyan('📋 How to get your vendor key:'));
458
+ console.log(chalk.gray('1. Visit your Lanonasis dashboard at https://app.lanonasis.com'));
459
+ console.log(chalk.gray('2. Navigate to Settings → API Keys'));
460
+ console.log(chalk.gray('3. Click "Generate New Key" and copy the full key'));
461
+ console.log(chalk.gray('4. The key format should be: pk_[letters/numbers].sk_[letters/numbers]'));
462
+ console.log();
133
463
  const { vendorKey } = await inquirer.prompt([
134
464
  {
135
465
  type: 'password',
@@ -137,32 +467,73 @@ async function handleVendorKeyFlow(config) {
137
467
  message: 'Enter your vendor key (pk_xxx.sk_xxx):',
138
468
  mask: '*',
139
469
  validate: (input) => {
140
- if (!input)
141
- return 'Vendor key is required';
142
- if (!input.match(/^pk_[a-zA-Z0-9]+\.sk_[a-zA-Z0-9]+$/)) {
143
- return 'Invalid format. Expected: pk_xxx.sk_xxx';
144
- }
145
- return true;
470
+ return validateVendorKeyFormat(input);
146
471
  }
147
472
  }
148
473
  ]);
149
474
  await handleVendorKeyAuth(vendorKey, config);
150
475
  }
476
+ // Enhanced vendor key format validation with detailed error messages
477
+ function validateVendorKeyFormat(input) {
478
+ if (!input || input.trim().length === 0) {
479
+ return 'Vendor key is required';
480
+ }
481
+ const trimmed = input.trim();
482
+ // Check basic format
483
+ if (!trimmed.includes('.')) {
484
+ return 'Invalid format: Vendor key must contain a dot (.) separator\nExpected format: pk_xxx.sk_xxx';
485
+ }
486
+ const parts = trimmed.split('.');
487
+ if (parts.length !== 2) {
488
+ return 'Invalid format: Vendor key must have exactly two parts separated by a dot\nExpected format: pk_xxx.sk_xxx';
489
+ }
490
+ const [publicPart, secretPart] = parts;
491
+ // Validate public key part
492
+ if (!publicPart.startsWith('pk_')) {
493
+ return 'Invalid format: First part must start with "pk_"\nExpected format: pk_xxx.sk_xxx';
494
+ }
495
+ if (publicPart.length < 4) {
496
+ return 'Invalid format: Public key part is too short\nExpected format: pk_xxx.sk_xxx (where xxx is alphanumeric)';
497
+ }
498
+ const publicKeyContent = publicPart.substring(3); // Remove 'pk_'
499
+ if (!/^[a-zA-Z0-9]+$/.test(publicKeyContent)) {
500
+ return 'Invalid format: Public key part contains invalid characters\nOnly letters and numbers are allowed after "pk_"';
501
+ }
502
+ // Validate secret key part
503
+ if (!secretPart.startsWith('sk_')) {
504
+ return 'Invalid format: Second part must start with "sk_"\nExpected format: pk_xxx.sk_xxx';
505
+ }
506
+ if (secretPart.length < 4) {
507
+ return 'Invalid format: Secret key part is too short\nExpected format: pk_xxx.sk_xxx (where xxx is alphanumeric)';
508
+ }
509
+ const secretKeyContent = secretPart.substring(3); // Remove 'sk_'
510
+ if (!/^[a-zA-Z0-9]+$/.test(secretKeyContent)) {
511
+ return 'Invalid format: Secret key part contains invalid characters\nOnly letters and numbers are allowed after "sk_"';
512
+ }
513
+ // Check minimum length requirements
514
+ if (publicKeyContent.length < 8) {
515
+ return 'Invalid format: Public key part is too short (minimum 8 characters after "pk_")';
516
+ }
517
+ if (secretKeyContent.length < 16) {
518
+ return 'Invalid format: Secret key part is too short (minimum 16 characters after "sk_")';
519
+ }
520
+ return true;
521
+ }
151
522
  async function handleOAuthFlow(config) {
152
523
  console.log();
153
524
  console.log(chalk.yellow('🌐 Browser-Based Authentication'));
154
525
  console.log(chalk.gray('This will open your browser for secure authentication'));
155
526
  console.log();
156
- const { proceed } = await inquirer.prompt([
527
+ const { openBrowser } = await inquirer.prompt([
157
528
  {
158
529
  type: 'confirm',
159
- name: 'proceed',
530
+ name: 'openBrowser',
160
531
  message: 'Open browser for authentication?',
161
532
  default: true
162
533
  }
163
534
  ]);
164
- if (!proceed) {
165
- console.log(chalk.yellow('Authentication cancelled'));
535
+ if (!openBrowser) {
536
+ console.log(chalk.yellow('⚠️ Authentication cancelled'));
166
537
  return;
167
538
  }
168
539
  // Use the browser-based CLI login endpoint from MCP service
@@ -182,10 +553,30 @@ async function handleOAuthFlow(config) {
182
553
  type: 'input',
183
554
  name: 'token',
184
555
  message: 'Paste the authentication token from browser:',
185
- validate: (input) => {
556
+ validate: async (input) => {
186
557
  if (!input || input.trim().length === 0) {
187
558
  return 'Token is required';
188
559
  }
560
+ const trimmed = input.trim();
561
+ // Reject if user pasted a URL instead of token
562
+ if (trimmed.startsWith('http://') || trimmed.startsWith('https://')) {
563
+ return 'Please paste the TOKEN from the page, not the URL';
564
+ }
565
+ // Check token format - should start with 'cli_' or be a JWT
566
+ if (!trimmed.startsWith('cli_') && !trimmed.match(/^[\w-]+\.[\w-]+\.[\w-]+$/)) {
567
+ return 'Invalid token format. Expected format: cli_xxx or JWT token';
568
+ }
569
+ // Verify token with server
570
+ try {
571
+ const response = await apiClient.post('/auth/verify', { token: trimmed });
572
+ if (!response.valid) {
573
+ return 'Token verification failed. Please try again.';
574
+ }
575
+ }
576
+ catch (error) {
577
+ const errorMessage = error instanceof Error ? error.message : 'Server verification failed';
578
+ return `Token verification error: ${errorMessage}`;
579
+ }
189
580
  return true;
190
581
  }
191
582
  }
@@ -251,22 +642,12 @@ async function handleCredentialsFlow(options, config) {
251
642
  }
252
643
  catch (error) {
253
644
  spinner.fail('Login failed');
254
- // Increment failure count for failed authentication
255
- await config.incrementFailureCount();
256
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
645
+ // Use enhanced error handling
646
+ await handleAuthenticationFailure(error, config, 'jwt');
647
+ // For 401 errors, offer registration option
257
648
  const errorResponse = error && typeof error === 'object' && 'response' in error ? error.response : null;
258
649
  if (errorResponse && typeof errorResponse === 'object' && 'status' in errorResponse && errorResponse.status === 401) {
259
- console.error(chalk.red('✖ Invalid email or password'));
260
- // Provide guidance for repeated failures
261
- const failureCount = config.getFailureCount();
262
- if (failureCount >= 3) {
263
- console.log();
264
- console.log(chalk.yellow('💡 Multiple login failures detected. Consider:'));
265
- console.log(chalk.gray('• Double-check your email and password'));
266
- console.log(chalk.gray('• Reset your password if needed'));
267
- console.log(chalk.gray('• Try using a vendor key instead: lanonasis auth login --vendor-key'));
268
- }
269
- // Ask if they want to register
650
+ console.log();
270
651
  const answer = await inquirer.prompt([
271
652
  {
272
653
  type: 'confirm',
@@ -277,18 +658,7 @@ async function handleCredentialsFlow(options, config) {
277
658
  ]);
278
659
  if (answer.register) {
279
660
  await registerFlow(email);
280
- }
281
- }
282
- else {
283
- console.error(chalk.red('✖ Login failed:'), errorMessage);
284
- // Provide guidance for repeated failures
285
- const failureCount = config.getFailureCount();
286
- if (failureCount >= 3) {
287
- console.log();
288
- console.log(chalk.yellow('💡 Connection issues detected. Try:'));
289
- console.log(chalk.gray('• Check your internet connection'));
290
- console.log(chalk.gray('• Verify the service is available'));
291
- console.log(chalk.gray('• Try again later'));
661
+ return; // Don't exit if registration succeeds
292
662
  }
293
663
  }
294
664
  process.exit(1);