aios-core 4.2.0 → 4.2.2

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.
@@ -7,8 +7,8 @@
7
7
  # - SHA256 hashes for change detection
8
8
  # - File types for categorization
9
9
  #
10
- version: 4.2.0
11
- generated_at: "2026-02-16T00:03:48.806Z"
10
+ version: 4.2.2
11
+ generated_at: "2026-02-16T00:23:14.678Z"
12
12
  generator: scripts/generate-install-manifest.js
13
13
  file_count: 992
14
14
  files:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aios-core",
3
- "version": "4.2.0",
3
+ "version": "4.2.2",
4
4
  "description": "Synkra AIOS: AI-Orchestrated System for Full Stack Development - Core Framework",
5
5
  "bin": {
6
6
  "aios": "bin/aios.js",
@@ -215,11 +215,11 @@ async function stepLicenseGate(options = {}) {
215
215
  message: colors.primary('How would you like to activate Pro?'),
216
216
  choices: [
217
217
  {
218
- name: 'Login with email and password (Recommended)',
218
+ name: 'Login or create account (Recommended)',
219
219
  value: 'email',
220
220
  },
221
221
  {
222
- name: 'Enter license key',
222
+ name: 'Enter license key (legacy)',
223
223
  value: 'key',
224
224
  },
225
225
  ],
@@ -306,6 +306,88 @@ async function stepLicenseGateWithEmail() {
306
306
  return authenticateWithEmail(email.trim(), password);
307
307
  }
308
308
 
309
+ /**
310
+ * Prompt user to create a new account interactively.
311
+ *
312
+ * Asks for confirmation, then password with confirmation.
313
+ * Calls signup API and logs in to get session token.
314
+ *
315
+ * @param {object} client - LicenseApiClient instance
316
+ * @param {string} email - User email
317
+ * @returns {Promise<Object>} Result with { success, sessionToken } or { success: false, error }
318
+ */
319
+ async function promptCreateAccount(client, email) {
320
+ const inquirer = require('inquirer');
321
+
322
+ console.log('');
323
+ showInfo(`No account found for ${email}.`);
324
+
325
+ const { wantCreate } = await inquirer.prompt([
326
+ {
327
+ type: 'confirm',
328
+ name: 'wantCreate',
329
+ message: colors.primary('Would you like to create an account?'),
330
+ default: true,
331
+ },
332
+ ]);
333
+
334
+ if (!wantCreate) {
335
+ return { success: false, error: 'Account creation cancelled.' };
336
+ }
337
+
338
+ // Ask for password with confirmation
339
+ const { newPassword } = await inquirer.prompt([
340
+ {
341
+ type: 'password',
342
+ name: 'newPassword',
343
+ message: colors.primary('Choose a password:'),
344
+ mask: '*',
345
+ validate: (input) => {
346
+ if (!input || input.length < MIN_PASSWORD_LENGTH) {
347
+ return `Password must be at least ${MIN_PASSWORD_LENGTH} characters`;
348
+ }
349
+ return true;
350
+ },
351
+ },
352
+ ]);
353
+
354
+ const { confirmPassword } = await inquirer.prompt([
355
+ {
356
+ type: 'password',
357
+ name: 'confirmPassword',
358
+ message: colors.primary('Confirm password:'),
359
+ mask: '*',
360
+ validate: (input) => {
361
+ if (input !== newPassword) {
362
+ return 'Passwords do not match';
363
+ }
364
+ return true;
365
+ },
366
+ },
367
+ ]);
368
+
369
+ // Create account
370
+ const spinner = createSpinner('Creating account...');
371
+ spinner.start();
372
+
373
+ try {
374
+ await client.signup(email, confirmPassword);
375
+ spinner.succeed('Account created! Verification email sent.');
376
+
377
+ // Login to get session token
378
+ const loginResult = await client.login(email, confirmPassword);
379
+ return { success: true, sessionToken: loginResult.sessionToken };
380
+ } catch (signupError) {
381
+ if (signupError.code === 'EMAIL_ALREADY_REGISTERED') {
382
+ spinner.fail('An account already exists with this email but the password is incorrect.');
383
+ showInfo('Forgot your password? Visit https://pro.synkra.ai/reset-password or contact support@synkra.ai');
384
+ return { success: false, error: signupError.message };
385
+ }
386
+ spinner.fail(`Account creation failed: ${signupError.message}`);
387
+ return { success: false, error: signupError.message };
388
+ }
389
+ }
390
+
309
391
  /**
310
392
  * Authenticate with email and password.
311
393
  *
@@ -339,48 +421,122 @@ async function authenticateWithEmail(email, password) {
339
421
  };
340
422
  }
341
423
 
342
- // Try login first
343
- const spinner = createSpinner('Authenticating...');
344
- spinner.start();
345
-
346
424
  let sessionToken;
347
425
  let emailVerified;
426
+ let currentPassword = password;
348
427
 
349
- try {
350
- const loginResult = await client.login(email, password);
351
- sessionToken = loginResult.sessionToken;
352
- emailVerified = loginResult.emailVerified;
353
- spinner.succeed('Authenticated successfully.');
354
- } catch (loginError) {
355
- // If invalid credentials, try signup for new users
356
- if (loginError.code === 'INVALID_CREDENTIALS') {
357
- spinner.info('No account found. Creating a new account...');
428
+ // Login with retry loop (max 3 attempts for wrong password)
429
+ for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
430
+ const spinner = createSpinner('Authenticating...');
431
+ spinner.start();
358
432
 
359
- try {
360
- await client.signup(email, password);
361
- showSuccess('Account created. Verification email sent!');
362
- emailVerified = false;
363
-
364
- // Login after signup to get session token
365
- const loginAfterSignup = await client.login(email, password);
366
- sessionToken = loginAfterSignup.sessionToken;
367
- } catch (signupError) {
368
- if (signupError.code === 'EMAIL_ALREADY_REGISTERED') {
369
- showError('An account exists with this email but the password is incorrect.');
370
- showInfo('Forgot your password? Visit https://pro.synkra.ai/reset-password or contact support@synkra.ai');
371
- return { success: false, error: signupError.message };
433
+ try {
434
+ const loginResult = await client.login(email, currentPassword);
435
+ sessionToken = loginResult.sessionToken;
436
+ emailVerified = loginResult.emailVerified;
437
+ spinner.succeed('Authenticated successfully.');
438
+ break; // Success, exit retry loop
439
+ } catch (loginError) {
440
+ if (loginError.code === 'INVALID_CREDENTIALS') {
441
+ spinner.stop();
442
+
443
+ // Try to determine if account exists by attempting signup
444
+ // If signup fails with EMAIL_ALREADY_REGISTERED, account exists (wrong password)
445
+ // If signup succeeds or would succeed, account doesn't exist (new user)
446
+ if (isCIEnvironment()) {
447
+ // CI mode: try auto-signup
448
+ try {
449
+ await client.signup(email, currentPassword);
450
+ showSuccess('Account created. Verification email sent!');
451
+ emailVerified = false;
452
+ const loginAfterSignup = await client.login(email, currentPassword);
453
+ sessionToken = loginAfterSignup.sessionToken;
454
+ break;
455
+ } catch (signupError) {
456
+ if (signupError.code === 'EMAIL_ALREADY_REGISTERED') {
457
+ showError('Account exists but the password is incorrect.');
458
+ showInfo('Forgot your password? Visit https://pro.synkra.ai/reset-password or contact support@synkra.ai');
459
+ return { success: false, error: signupError.message };
460
+ }
461
+ return { success: false, error: signupError.message };
462
+ }
372
463
  }
373
- return { success: false, error: signupError.message };
464
+
465
+ // Interactive mode: check if account exists
466
+ const accountCheckSpinner = createSpinner('Checking account...');
467
+ accountCheckSpinner.start();
468
+
469
+ let accountExists = false;
470
+ try {
471
+ // Try a lightweight signup to probe. If EMAIL_ALREADY_REGISTERED, account exists.
472
+ await client.signup(email, currentPassword);
473
+ // Signup succeeded — account was just created
474
+ accountCheckSpinner.succeed('Account created! Verification email sent.');
475
+ const loginAfterSignup = await client.login(email, currentPassword);
476
+ sessionToken = loginAfterSignup.sessionToken;
477
+ emailVerified = false;
478
+ break;
479
+ } catch (probeError) {
480
+ if (probeError.code === 'EMAIL_ALREADY_REGISTERED') {
481
+ accountExists = true;
482
+ accountCheckSpinner.stop();
483
+ } else {
484
+ accountCheckSpinner.stop();
485
+ // Unknown error during probe — offer create account flow
486
+ const signupResult = await promptCreateAccount(client, email);
487
+ if (!signupResult.success) {
488
+ return signupResult;
489
+ }
490
+ sessionToken = signupResult.sessionToken;
491
+ emailVerified = false;
492
+ break;
493
+ }
494
+ }
495
+
496
+ if (accountExists) {
497
+ // Account exists but password is wrong — retry
498
+ const remaining = MAX_RETRIES - attempt;
499
+ if (remaining > 0) {
500
+ showError(`Incorrect password. ${remaining} attempt${remaining > 1 ? 's' : ''} remaining.`);
501
+ showInfo('Forgot your password? Visit https://pro.synkra.ai/reset-password');
502
+
503
+ const inquirer = require('inquirer');
504
+ const { retryPassword } = await inquirer.prompt([
505
+ {
506
+ type: 'password',
507
+ name: 'retryPassword',
508
+ message: colors.primary('Password:'),
509
+ mask: '*',
510
+ validate: (input) => {
511
+ if (!input || input.length < MIN_PASSWORD_LENGTH) {
512
+ return `Password must be at least ${MIN_PASSWORD_LENGTH} characters`;
513
+ }
514
+ return true;
515
+ },
516
+ },
517
+ ]);
518
+ currentPassword = retryPassword;
519
+ continue; // Retry login
520
+ } else {
521
+ showError('Maximum login attempts reached.');
522
+ showInfo('Forgot your password? Visit https://pro.synkra.ai/reset-password or contact support@synkra.ai');
523
+ return { success: false, error: 'Maximum login attempts reached.' };
524
+ }
525
+ }
526
+ } else if (loginError.code === 'AUTH_RATE_LIMITED') {
527
+ spinner.fail(loginError.message);
528
+ return { success: false, error: loginError.message };
529
+ } else {
530
+ spinner.fail(`Authentication failed: ${loginError.message}`);
531
+ return { success: false, error: loginError.message };
374
532
  }
375
- } else if (loginError.code === 'AUTH_RATE_LIMITED') {
376
- spinner.fail(loginError.message);
377
- return { success: false, error: loginError.message };
378
- } else {
379
- spinner.fail(`Authentication failed: ${loginError.message}`);
380
- return { success: false, error: loginError.message };
381
533
  }
382
534
  }
383
535
 
536
+ if (!sessionToken) {
537
+ return { success: false, error: 'Authentication failed after all attempts.' };
538
+ }
539
+
384
540
  // Wait for email verification if needed
385
541
  if (!emailVerified) {
386
542
  const verifyResult = await waitForEmailVerification(client, sessionToken);
@@ -918,6 +1074,7 @@ module.exports = {
918
1074
  stepLicenseGateWithKey,
919
1075
  stepLicenseGateWithKeyInteractive,
920
1076
  stepLicenseGateWithEmail,
1077
+ promptCreateAccount,
921
1078
  loadLicenseApi,
922
1079
  loadFeatureGate,
923
1080
  loadProScaffolder,