erosolar-cli 1.6.3 → 1.6.5

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 (72) hide show
  1. package/dist/core/resultVerification.d.ts +138 -0
  2. package/dist/core/resultVerification.d.ts.map +1 -0
  3. package/dist/core/resultVerification.js +465 -0
  4. package/dist/core/resultVerification.js.map +1 -0
  5. package/dist/plugins/providers/anthropic/index.d.ts +9 -0
  6. package/dist/plugins/providers/anthropic/index.d.ts.map +1 -1
  7. package/dist/plugins/providers/anthropic/index.js +13 -1
  8. package/dist/plugins/providers/anthropic/index.js.map +1 -1
  9. package/dist/plugins/providers/deepseek/index.d.ts +9 -0
  10. package/dist/plugins/providers/deepseek/index.d.ts.map +1 -1
  11. package/dist/plugins/providers/deepseek/index.js +28 -1
  12. package/dist/plugins/providers/deepseek/index.js.map +1 -1
  13. package/dist/plugins/providers/google/index.d.ts +9 -0
  14. package/dist/plugins/providers/google/index.d.ts.map +1 -1
  15. package/dist/plugins/providers/google/index.js +13 -1
  16. package/dist/plugins/providers/google/index.js.map +1 -1
  17. package/dist/plugins/providers/ollama/index.d.ts +5 -1
  18. package/dist/plugins/providers/ollama/index.d.ts.map +1 -1
  19. package/dist/plugins/providers/ollama/index.js +11 -2
  20. package/dist/plugins/providers/ollama/index.js.map +1 -1
  21. package/dist/plugins/providers/openai/index.d.ts +9 -0
  22. package/dist/plugins/providers/openai/index.d.ts.map +1 -1
  23. package/dist/plugins/providers/openai/index.js +13 -1
  24. package/dist/plugins/providers/openai/index.js.map +1 -1
  25. package/dist/plugins/providers/xai/index.d.ts +9 -0
  26. package/dist/plugins/providers/xai/index.d.ts.map +1 -1
  27. package/dist/plugins/providers/xai/index.js +15 -1
  28. package/dist/plugins/providers/xai/index.js.map +1 -1
  29. package/dist/providers/anthropicProvider.d.ts +7 -0
  30. package/dist/providers/anthropicProvider.d.ts.map +1 -1
  31. package/dist/providers/anthropicProvider.js +87 -9
  32. package/dist/providers/anthropicProvider.js.map +1 -1
  33. package/dist/providers/googleProvider.d.ts +17 -0
  34. package/dist/providers/googleProvider.d.ts.map +1 -1
  35. package/dist/providers/googleProvider.js +113 -32
  36. package/dist/providers/googleProvider.js.map +1 -1
  37. package/dist/providers/openaiChatCompletionsProvider.d.ts +26 -0
  38. package/dist/providers/openaiChatCompletionsProvider.d.ts.map +1 -1
  39. package/dist/providers/openaiChatCompletionsProvider.js +162 -27
  40. package/dist/providers/openaiChatCompletionsProvider.js.map +1 -1
  41. package/dist/providers/openaiResponsesProvider.d.ts +17 -0
  42. package/dist/providers/openaiResponsesProvider.d.ts.map +1 -1
  43. package/dist/providers/openaiResponsesProvider.js +109 -25
  44. package/dist/providers/openaiResponsesProvider.js.map +1 -1
  45. package/dist/providers/resilientProvider.d.ts.map +1 -1
  46. package/dist/providers/resilientProvider.js +47 -1
  47. package/dist/providers/resilientProvider.js.map +1 -1
  48. package/dist/shell/interactiveShell.d.ts +6 -0
  49. package/dist/shell/interactiveShell.d.ts.map +1 -1
  50. package/dist/shell/interactiveShell.js +48 -0
  51. package/dist/shell/interactiveShell.js.map +1 -1
  52. package/dist/shell/taskCompletionDetector.d.ts +4 -0
  53. package/dist/shell/taskCompletionDetector.d.ts.map +1 -1
  54. package/dist/shell/taskCompletionDetector.js +27 -1
  55. package/dist/shell/taskCompletionDetector.js.map +1 -1
  56. package/dist/tools/bashTools.d.ts.map +1 -1
  57. package/dist/tools/bashTools.js +26 -8
  58. package/dist/tools/bashTools.js.map +1 -1
  59. package/dist/tools/browserAutomationTools.d.ts.map +1 -1
  60. package/dist/tools/browserAutomationTools.js +9 -1
  61. package/dist/tools/browserAutomationTools.js.map +1 -1
  62. package/dist/tools/cloudTools.d.ts.map +1 -1
  63. package/dist/tools/cloudTools.js +436 -35
  64. package/dist/tools/cloudTools.js.map +1 -1
  65. package/dist/tools/codeGenerationTools.d.ts.map +1 -1
  66. package/dist/tools/codeGenerationTools.js +77 -7
  67. package/dist/tools/codeGenerationTools.js.map +1 -1
  68. package/dist/ui/persistentPrompt.d.ts +10 -0
  69. package/dist/ui/persistentPrompt.d.ts.map +1 -1
  70. package/dist/ui/persistentPrompt.js +82 -18
  71. package/dist/ui/persistentPrompt.js.map +1 -1
  72. package/package.json +1 -1
@@ -23,6 +23,7 @@
23
23
  import { spawn } from 'child_process';
24
24
  import * as fs from 'fs';
25
25
  import * as path from 'path';
26
+ import { verifiedSuccess, verifiedFailure, unverifiedResult, requiresUserAction, partialSuccess, analyzeOutput, OutputPatterns, verifyUrlAccessible, } from '../core/resultVerification.js';
26
27
  const CLOUD_PROVIDERS = {
27
28
  firebase: {
28
29
  id: 'firebase',
@@ -471,16 +472,44 @@ Supports:
471
472
  const target = args['target'] || 'all';
472
473
  const project = args['project'];
473
474
  const dryRun = args['dry_run'] === true;
475
+ const startTime = Date.now();
476
+ const checks = [];
474
477
  // First check status
475
478
  const status = await getFullCLIStatus('firebase', workingDir);
479
+ checks.push({
480
+ check: 'Firebase CLI installed',
481
+ passed: status.installed,
482
+ details: status.installed ? `v${status.version}` : 'Not found',
483
+ });
476
484
  if (!status.installed) {
477
- return `Firebase CLI not installed.\n\n🔧 Auto-fix: Run \`npm install -g firebase-tools\`\n\nOr use cloud_fix tool with provider="firebase"`;
485
+ return requiresUserAction('Firebase CLI not installed', 'The Firebase CLI (firebase-tools) must be installed before deployment.', [
486
+ 'Run: npm install -g firebase-tools',
487
+ 'Or use cloud_fix tool with provider="firebase"',
488
+ 'Documentation: https://firebase.google.com/docs/cli',
489
+ ], Date.now() - startTime);
478
490
  }
491
+ checks.push({
492
+ check: 'Firebase authenticated',
493
+ passed: status.authenticated,
494
+ details: status.authenticated ? 'Credentials valid' : 'Not logged in',
495
+ });
479
496
  if (!status.authenticated) {
480
- return `Firebase not authenticated.\n\n🔧 Fix options:\n1. Interactive: \`firebase login\`\n2. CI/Headless: \`firebase login:ci\` and set FIREBASE_TOKEN\n3. Service account: Set GOOGLE_APPLICATION_CREDENTIALS`;
497
+ return requiresUserAction('Firebase authentication required', 'You must authenticate with Firebase before deploying.\nThe CLI is not currently logged in.', [
498
+ 'Interactive login: firebase login',
499
+ 'CI/Headless: firebase login:ci and set FIREBASE_TOKEN',
500
+ 'Service account: Set GOOGLE_APPLICATION_CREDENTIALS environment variable',
501
+ ], Date.now() - startTime);
481
502
  }
503
+ checks.push({
504
+ check: 'firebase.json exists',
505
+ passed: status.configExists,
506
+ details: status.configExists ? 'Found' : 'Not found',
507
+ });
482
508
  if (!status.configExists) {
483
- return `No firebase.json found in ${workingDir}.\n\n🔧 Initialize with: \`firebase init\``;
509
+ return requiresUserAction('Firebase configuration missing', `No firebase.json found in ${workingDir}.\nThis file is required to deploy to Firebase.`, [
510
+ 'Initialize Firebase: firebase init',
511
+ 'Select the services you want to deploy (Hosting, Functions, etc.)',
512
+ ], Date.now() - startTime);
484
513
  }
485
514
  // Build deploy command
486
515
  let cmd = 'firebase deploy';
@@ -495,25 +524,76 @@ Supports:
495
524
  }
496
525
  cmd += ' 2>&1';
497
526
  const result = await runCommand(`cd "${workingDir}" && ${cmd}`, 300000); // 5 min timeout
527
+ const durationMs = Date.now() - startTime;
528
+ const output = result.stdout + result.stderr;
529
+ // Analyze output with Firebase-specific patterns
530
+ const analysis = analyzeOutput(output, OutputPatterns.firebase, result.exitCode);
531
+ checks.push({
532
+ check: 'Deployment command',
533
+ passed: result.exitCode === 0,
534
+ details: `Exit code: ${result.exitCode}`,
535
+ });
536
+ // Check for hosting URL in output (indicates actual successful deployment)
537
+ const hostingUrlMatch = output.match(/Hosting URL:\s*(https?:\/\/[^\s]+)/i);
538
+ const functionUrlMatch = output.match(/Function URL[^:]*:\s*(https?:\/\/[^\s]+)/i);
539
+ if (hostingUrlMatch && hostingUrlMatch[1]) {
540
+ // Verify the deployed URL is actually accessible
541
+ const urlCheck = await verifyUrlAccessible(hostingUrlMatch[1], 200, 15000);
542
+ checks.push(urlCheck);
543
+ if (urlCheck.passed && result.exitCode === 0) {
544
+ return verifiedSuccess('Firebase deployment completed and verified', `Target: ${target}\n${project ? `Project: ${project}\n` : ''}${dryRun ? '(Dry run)\n' : ''}\nHosting URL: ${hostingUrlMatch[1]}\n\nDeployment Output:\n${output}`, checks, durationMs);
545
+ }
546
+ else if (result.exitCode === 0) {
547
+ // Deployment said success but URL not accessible
548
+ return partialSuccess('Firebase deployment completed but URL verification failed', `The deployment command succeeded but the hosting URL is not yet accessible.\nThis may be due to propagation delay.\n\nHosting URL: ${hostingUrlMatch[1]}\n\nDeployment Output:\n${output}`, checks, ['Wait 1-2 minutes for deployment to propagate', `Then verify: ${hostingUrlMatch[1]}`], durationMs);
549
+ }
550
+ }
551
+ if (functionUrlMatch && result.exitCode === 0) {
552
+ checks.push({
553
+ check: 'Function deployment',
554
+ passed: true,
555
+ details: functionUrlMatch[1],
556
+ });
557
+ return verifiedSuccess('Firebase Functions deployment completed', `Target: ${target}\n${project ? `Project: ${project}\n` : ''}\nFunction URL: ${functionUrlMatch[1]}\n\nDeployment Output:\n${output}`, checks, durationMs);
558
+ }
559
+ // Exit code 0 but no URL found - might be config-only deployment
560
+ if (result.exitCode === 0 && analysis.isSuccess) {
561
+ checks.push({
562
+ check: 'Output analysis',
563
+ passed: true,
564
+ details: 'Success pattern detected',
565
+ });
566
+ return verifiedSuccess('Firebase deployment completed', `Target: ${target}\n${project ? `Project: ${project}\n` : ''}${dryRun ? '(Dry run)\n' : ''}\nDeployment Output:\n${output}`, checks, durationMs);
567
+ }
568
+ // Exit code 0 but output suggests problems
569
+ if (result.exitCode === 0 && analysis.isFailure) {
570
+ const firebaseProvider = CLOUD_PROVIDERS['firebase'];
571
+ const errorSuggestions = firebaseProvider
572
+ ? analyzeDeploymentError(output, firebaseProvider).split('\n').slice(1).map(s => s.replace(/^[•]\s*/, ''))
573
+ : [];
574
+ return verifiedFailure('Firebase deployment command succeeded but output indicates errors', `The command exited with code 0 but the output contains error indicators.\n\nOutput:\n${output}`, errorSuggestions, checks, durationMs);
575
+ }
576
+ // Exit code 0 but can't verify
498
577
  if (result.exitCode === 0) {
499
- return `✅ Firebase deployment successful!\n\n${result.stdout}`;
578
+ return unverifiedResult('Firebase deployment may have completed', `The command exited with code 0 but we cannot verify the deployment succeeded.\nNo hosting URL or success patterns found in output.\n\nOutput:\n${output}`, ['Manually check Firebase Console: https://console.firebase.google.com', 'Verify your deployment target is configured in firebase.json'], durationMs);
500
579
  }
501
- // Analyze error and provide fixes
502
- const output = result.stdout + result.stderr;
503
- let fix = '';
504
- if (output.includes('authentication') || output.includes('login')) {
505
- fix = '\n\n🔧 Fix: Run `firebase login --reauth`';
580
+ // Deployment failed - analyze the error
581
+ const firebaseProviderForError = CLOUD_PROVIDERS['firebase'];
582
+ const errorAnalysis = analyzeDeploymentError(output, firebaseProviderForError);
583
+ const suggestedFixes = [];
584
+ if (output.includes('authentication') || output.includes('login') || output.includes('not logged in')) {
585
+ suggestedFixes.push('Run: firebase login --reauth');
506
586
  }
507
- else if (output.includes('permission') || output.includes('denied')) {
508
- fix = '\n\n🔧 Fix: Check Firebase project permissions at https://console.firebase.google.com';
587
+ if (output.includes('permission') || output.includes('denied') || output.includes('PERMISSION_DENIED')) {
588
+ suggestedFixes.push('Check Firebase project permissions at https://console.firebase.google.com');
509
589
  }
510
- else if (output.includes('quota') || output.includes('limit')) {
511
- fix = '\n\n🔧 Fix: Quota exceeded. Check billing at https://console.firebase.google.com/billing';
590
+ if (output.includes('quota') || output.includes('limit')) {
591
+ suggestedFixes.push('Check billing/quota at https://console.firebase.google.com/billing');
512
592
  }
513
- else if (output.includes('build') || output.includes('compile')) {
514
- fix = '\n\n🔧 Fix: Build errors detected. Run your build command first (e.g., npm run build)';
593
+ if (output.includes('build') || output.includes('compile')) {
594
+ suggestedFixes.push('Fix build errors first: npm run build');
515
595
  }
516
- return `❌ Firebase deployment failed\n\n${output}${fix}`;
596
+ return verifiedFailure(`Firebase deployment failed with exit code ${result.exitCode}`, `Target: ${target}\n${project ? `Project: ${project}\n` : ''}\n${errorAnalysis}\n\nFull Output:\n${output}`, suggestedFixes.length > 0 ? suggestedFixes : ['Review the error output above', 'Check Firebase documentation for the specific error'], checks, durationMs);
517
597
  },
518
598
  },
519
599
  {
@@ -554,13 +634,34 @@ Automatically handles authentication and common issues.`,
554
634
  const action = args['action'];
555
635
  const region = args['region'];
556
636
  const extraArgs = args['args'];
637
+ const startTime = Date.now();
638
+ const checks = [];
557
639
  // Check status
558
640
  const status = await getFullCLIStatus('aliyun', workingDir);
641
+ checks.push({
642
+ check: 'Aliyun CLI installed',
643
+ passed: status.installed,
644
+ details: status.installed ? `v${status.version}` : 'Not found',
645
+ });
559
646
  if (!status.installed) {
560
- return `Aliyun CLI not installed.\n\n🔧 Install:\n- macOS: brew install aliyun-cli\n- Linux: curl -O https://aliyuncli.alicdn.com/aliyun-cli-linux-latest-amd64.tgz\n\nDocs: https://www.alibabacloud.com/help/doc-detail/139508.htm`;
647
+ return requiresUserAction('Aliyun CLI not installed', 'The Aliyun CLI must be installed before running commands.', [
648
+ 'macOS: brew install aliyun-cli',
649
+ 'Linux: curl -O https://aliyuncli.alicdn.com/aliyun-cli-linux-latest-amd64.tgz',
650
+ 'Docs: https://www.alibabacloud.com/help/doc-detail/139508.htm',
651
+ ], Date.now() - startTime);
561
652
  }
653
+ checks.push({
654
+ check: 'Aliyun authenticated',
655
+ passed: status.authenticated,
656
+ details: status.authenticated ? 'Credentials valid' : 'Not configured',
657
+ });
562
658
  if (!status.authenticated) {
563
- return `Aliyun not configured.\n\n🔧 Fix:\n1. Run: aliyun configure\n2. Enter your AccessKey ID and Secret\n3. Get keys at: https://ram.console.aliyun.com/manage/ak\n\nOr set environment variables:\n- ALIBABA_CLOUD_ACCESS_KEY_ID\n- ALIBABA_CLOUD_ACCESS_KEY_SECRET`;
659
+ return requiresUserAction('Aliyun not configured', 'You must configure Aliyun credentials before running commands.', [
660
+ 'Run: aliyun configure',
661
+ 'Enter your AccessKey ID and Secret',
662
+ 'Get keys at: https://ram.console.aliyun.com/manage/ak',
663
+ 'Or set: ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET',
664
+ ], Date.now() - startTime);
564
665
  }
565
666
  // Build command
566
667
  let cmd = `aliyun ${service} ${action}`;
@@ -572,19 +673,47 @@ Automatically handles authentication and common issues.`,
572
673
  }
573
674
  cmd += ' 2>&1';
574
675
  const result = await runCommand(cmd, 120000);
575
- if (result.exitCode === 0) {
576
- return `✅ Aliyun ${service} ${action} successful!\n\n${result.stdout}`;
676
+ const durationMs = Date.now() - startTime;
677
+ const output = result.stdout + result.stderr;
678
+ checks.push({
679
+ check: 'Command execution',
680
+ passed: result.exitCode === 0,
681
+ details: `Exit code: ${result.exitCode}`,
682
+ });
683
+ // Analyze output for success/failure patterns
684
+ const analysis = analyzeOutput(output, OutputPatterns.command, result.exitCode);
685
+ if (result.exitCode === 0 && analysis.confidence !== 'low') {
686
+ // Try to parse JSON response for additional verification
687
+ try {
688
+ const jsonResponse = JSON.parse(output.trim());
689
+ if (jsonResponse.RequestId) {
690
+ checks.push({
691
+ check: 'API response received',
692
+ passed: true,
693
+ details: `RequestId: ${jsonResponse.RequestId}`,
694
+ });
695
+ }
696
+ }
697
+ catch {
698
+ // Not JSON, but command succeeded
699
+ }
700
+ return verifiedSuccess(`Aliyun ${service} ${action} completed`, `Service: ${service}\nAction: ${action}${region ? `\nRegion: ${region}` : ''}\n\nOutput:\n${output}`, checks, durationMs);
701
+ }
702
+ if (result.exitCode === 0 && analysis.confidence === 'low') {
703
+ return unverifiedResult(`Aliyun ${service} ${action} may have completed`, `The command exited with code 0 but we cannot confirm success.\n\nOutput:\n${output}`, ['Manually verify the operation in Aliyun Console'], durationMs);
577
704
  }
578
705
  // Error analysis
579
- const output = result.stdout + result.stderr;
580
- let fix = '';
706
+ const suggestedFixes = [];
581
707
  if (output.includes('InvalidAccessKeyId') || output.includes('SignatureDoesNotMatch')) {
582
- fix = '\n\n🔧 Fix: Invalid credentials. Run `aliyun configure` with correct AccessKey';
708
+ suggestedFixes.push('Invalid credentials. Run `aliyun configure` with correct AccessKey');
583
709
  }
584
- else if (output.includes('Forbidden')) {
585
- fix = '\n\n🔧 Fix: Permission denied. Check RAM policies at https://ram.console.aliyun.com';
710
+ if (output.includes('Forbidden')) {
711
+ suggestedFixes.push('Permission denied. Check RAM policies at https://ram.console.aliyun.com');
586
712
  }
587
- return `❌ Aliyun command failed\n\n${output}${fix}`;
713
+ if (output.includes('InvalidRegionId')) {
714
+ suggestedFixes.push('Invalid region. Check available regions with: aliyun ecs DescribeRegions');
715
+ }
716
+ return verifiedFailure(`Aliyun ${service} ${action} failed with exit code ${result.exitCode}`, `Service: ${service}\nAction: ${action}${region ? `\nRegion: ${region}` : ''}\n\nOutput:\n${output}`, suggestedFixes.length > 0 ? suggestedFixes : ['Review the error message above'], checks, durationMs);
588
717
  },
589
718
  },
590
719
  {
@@ -624,37 +753,76 @@ Supports deployment to:
624
753
  const providerId = args['provider']?.toLowerCase();
625
754
  const command = args['command'] || 'deploy';
626
755
  const autoFix = args['auto_fix'] !== false;
756
+ const startTime = Date.now();
757
+ const checks = [];
627
758
  const provider = CLOUD_PROVIDERS[providerId];
628
759
  if (!provider) {
629
- return `Unknown provider: ${providerId}\n\nAvailable: ${Object.keys(CLOUD_PROVIDERS).join(', ')}`;
760
+ return verifiedFailure(`Unknown cloud provider: ${providerId}`, `The provider "${providerId}" is not recognized.`, [`Available providers: ${Object.keys(CLOUD_PROVIDERS).join(', ')}`], [], Date.now() - startTime);
630
761
  }
631
762
  // Check and potentially fix status
632
763
  let status = await getFullCLIStatus(providerId, workingDir);
764
+ checks.push({
765
+ check: `${provider.name} CLI installed`,
766
+ passed: status.installed,
767
+ details: status.installed ? `v${status.version}` : 'Not found',
768
+ });
633
769
  if (!status.installed && autoFix) {
634
770
  const fixes = await autoFixIssues(providerId, workingDir);
635
771
  // Re-check after fix attempt
636
772
  status = await getFullCLIStatus(providerId, workingDir);
637
773
  if (!status.installed) {
638
- return `${provider.name} CLI could not be installed.\n\n${fixes.join('\n')}`;
774
+ return requiresUserAction(`${provider.name} CLI could not be auto-installed`, `Automatic installation failed. Manual installation required.\n\nAuto-fix attempts:\n${fixes.join('\n')}`, [
775
+ `Install manually: ${provider.installCommand}`,
776
+ `Documentation: ${provider.installUrl}`,
777
+ ], Date.now() - startTime);
639
778
  }
640
779
  }
641
780
  if (!status.installed) {
642
- return `${provider.name} CLI not installed.\n\nInstall: ${provider.installCommand}\nDocs: ${provider.installUrl}`;
781
+ return requiresUserAction(`${provider.name} CLI not installed`, `The ${provider.name} CLI must be installed before deployment.`, [
782
+ `Install: ${provider.installCommand}`,
783
+ `Docs: ${provider.installUrl}`,
784
+ ], Date.now() - startTime);
643
785
  }
786
+ checks.push({
787
+ check: `${provider.name} authenticated`,
788
+ passed: status.authenticated,
789
+ details: status.authenticated ? 'Credentials valid' : 'Not logged in',
790
+ });
644
791
  if (!status.authenticated) {
645
- return `${provider.name} not authenticated.\n\nFix: ${provider.loginCommand}\n\nFor CI, set: ${provider.envVars?.join(' or ') || 'appropriate credentials'}`;
792
+ return requiresUserAction(`${provider.name} authentication required`, `You must authenticate with ${provider.name} before deploying.`, [
793
+ `Login: ${provider.loginCommand}`,
794
+ `For CI, set: ${provider.envVars?.join(' or ') || 'appropriate credentials'}`,
795
+ ], Date.now() - startTime);
646
796
  }
647
797
  // Execute deployment
648
798
  const cmd = `${provider.cliCommand} ${command} 2>&1`;
649
799
  const result = await runCommand(`cd "${workingDir}" && ${cmd}`, 300000);
800
+ const durationMs = Date.now() - startTime;
801
+ const output = result.stdout + result.stderr;
802
+ checks.push({
803
+ check: 'Deployment command',
804
+ passed: result.exitCode === 0,
805
+ details: `Exit code: ${result.exitCode}`,
806
+ });
807
+ // Analyze output
808
+ const analysis = analyzeOutput(output, OutputPatterns.command, result.exitCode);
809
+ if (result.exitCode === 0 && analysis.isSuccess) {
810
+ return verifiedSuccess(`${provider.name} deployment completed`, `Command: ${provider.cliCommand} ${command}\n\nOutput:\n${output}`, checks, durationMs);
811
+ }
812
+ if (result.exitCode === 0 && analysis.isFailure) {
813
+ return verifiedFailure(`${provider.name} deployment command succeeded but output indicates errors`, `The command exited with code 0 but the output contains error indicators.\n\nOutput:\n${output}`, [], checks, durationMs);
814
+ }
650
815
  if (result.exitCode === 0) {
651
- return `✅ ${provider.name} deployment successful!\n\n${result.stdout}`;
816
+ // Can't determine success - mark as unverified
817
+ return unverifiedResult(`${provider.name} deployment may have completed`, `The command exited with code 0 but we cannot confirm success.\n\nOutput:\n${output}`, [`Manually verify deployment at ${provider.name} console`], durationMs);
652
818
  }
653
- // Provide error context and fixes
654
- const output = result.stdout + result.stderr;
655
- // AI-driven error analysis
819
+ // Deployment failed
656
820
  const errorAnalysis = analyzeDeploymentError(output, provider);
657
- return `❌ ${provider.name} deployment failed\n\n${output}\n\n${errorAnalysis}`;
821
+ const suggestedActions = errorAnalysis
822
+ .split('\n')
823
+ .filter(line => line.includes('•') || line.includes('Fix:'))
824
+ .map(line => line.replace(/^[•\s]+/, '').replace(/^\s*Fix:\s*/i, ''));
825
+ return verifiedFailure(`${provider.name} deployment failed with exit code ${result.exitCode}`, `Command: ${provider.cliCommand} ${command}\n\n${errorAnalysis}\n\nFull Output:\n${output}`, suggestedActions.length > 0 ? suggestedActions : ['Review the error output', 'Check provider documentation'], checks, durationMs);
658
826
  },
659
827
  },
660
828
  {
@@ -700,6 +868,239 @@ Auto-detects project type (React, Vue, Angular, Next.js, etc.) and configures ap
700
868
  return `📝 ${CLOUD_PROVIDERS[providerId]?.name || providerId} Configuration\n\nProject type: ${detectedType}\n\n${config}`;
701
869
  },
702
870
  },
871
+ {
872
+ name: 'cloud_login',
873
+ description: `Interactive login for cloud providers.
874
+
875
+ Supports browser-based OAuth login for:
876
+ - Firebase (firebase login)
877
+ - Google Cloud (gcloud auth login)
878
+ - AWS (aws configure)
879
+ - Azure (az login)
880
+ - Vercel (vercel login)
881
+ - Netlify (netlify login)
882
+ - Cloudflare (wrangler login)
883
+ - Aliyun (aliyun configure)
884
+ - Fly.io (flyctl auth login)
885
+ - Railway (railway login)
886
+ - Supabase (supabase login)
887
+
888
+ Options:
889
+ - reauth: Force re-authentication even if already logged in
890
+ - no_localhost: Use device code flow (for remote/headless machines with browser access elsewhere)
891
+ - ci_mode: Get instructions for CI/CD environment setup
892
+
893
+ Use this when cloud_status shows authentication issues.`,
894
+ parameters: {
895
+ type: 'object',
896
+ properties: {
897
+ provider: {
898
+ type: 'string',
899
+ description: 'Cloud provider to login: firebase, gcloud, aws, azure, vercel, netlify, cloudflare, aliyun, fly, railway, supabase',
900
+ },
901
+ reauth: {
902
+ type: 'boolean',
903
+ description: 'Force re-authentication (default: false)',
904
+ },
905
+ no_localhost: {
906
+ type: 'boolean',
907
+ description: 'Use device code flow instead of localhost redirect (default: false)',
908
+ },
909
+ ci_mode: {
910
+ type: 'boolean',
911
+ description: 'Show CI/CD setup instructions instead of interactive login (default: false)',
912
+ },
913
+ },
914
+ required: ['provider'],
915
+ },
916
+ handler: async (args) => {
917
+ const providerId = args['provider']?.toLowerCase();
918
+ const reauth = args['reauth'] === true;
919
+ const noLocalhost = args['no_localhost'] === true;
920
+ const ciMode = args['ci_mode'] === true;
921
+ const provider = CLOUD_PROVIDERS[providerId];
922
+ if (!provider) {
923
+ return `Unknown provider: ${providerId}\n\nAvailable: ${Object.keys(CLOUD_PROVIDERS).join(', ')}`;
924
+ }
925
+ // Check if CLI is installed
926
+ const installCheck = await checkCLIInstalled(provider);
927
+ if (!installCheck.installed) {
928
+ return `${provider.name} CLI not installed.\n\nInstall with: ${provider.installCommand}\nDocs: ${provider.installUrl}`;
929
+ }
930
+ // Build login command based on provider and options
931
+ const loginCommands = {
932
+ firebase: {
933
+ standard: 'firebase login',
934
+ reauth: 'firebase login --reauth',
935
+ no_localhost: 'firebase login --no-localhost',
936
+ ci: 'firebase login:ci',
937
+ },
938
+ gcloud: {
939
+ standard: 'gcloud auth login',
940
+ reauth: 'gcloud auth login --force',
941
+ no_localhost: 'gcloud auth login --no-browser',
942
+ ci: 'gcloud auth activate-service-account --key-file=',
943
+ },
944
+ aws: {
945
+ standard: 'aws configure',
946
+ reauth: 'aws configure',
947
+ no_localhost: 'aws configure',
948
+ ci: 'aws configure set',
949
+ },
950
+ azure: {
951
+ standard: 'az login',
952
+ reauth: 'az login',
953
+ no_localhost: 'az login --use-device-code',
954
+ ci: 'az login --service-principal',
955
+ },
956
+ vercel: {
957
+ standard: 'vercel login',
958
+ reauth: 'vercel login',
959
+ no_localhost: 'vercel login',
960
+ ci: 'vercel login --token',
961
+ },
962
+ netlify: {
963
+ standard: 'netlify login',
964
+ reauth: 'netlify login',
965
+ no_localhost: 'netlify login',
966
+ ci: 'netlify login --new',
967
+ },
968
+ cloudflare: {
969
+ standard: 'wrangler login',
970
+ reauth: 'wrangler login',
971
+ no_localhost: 'wrangler login',
972
+ ci: 'wrangler login',
973
+ },
974
+ aliyun: {
975
+ standard: 'aliyun configure',
976
+ reauth: 'aliyun configure',
977
+ no_localhost: 'aliyun configure',
978
+ ci: 'aliyun configure set',
979
+ },
980
+ fly: {
981
+ standard: 'flyctl auth login',
982
+ reauth: 'flyctl auth login',
983
+ no_localhost: 'flyctl auth login',
984
+ ci: 'flyctl auth token',
985
+ },
986
+ railway: {
987
+ standard: 'railway login',
988
+ reauth: 'railway login',
989
+ no_localhost: 'railway login',
990
+ ci: 'railway login --browserless',
991
+ },
992
+ supabase: {
993
+ standard: 'supabase login',
994
+ reauth: 'supabase login',
995
+ no_localhost: 'supabase login',
996
+ ci: 'supabase login',
997
+ },
998
+ };
999
+ const providerCommands = loginCommands[providerId] || {
1000
+ standard: provider.loginCommand,
1001
+ reauth: provider.loginCommand,
1002
+ no_localhost: provider.loginCommand,
1003
+ ci: provider.loginCommand,
1004
+ };
1005
+ // Select the appropriate command
1006
+ if (ciMode) {
1007
+ const cmd = providerCommands['ci'] || provider.loginCommand;
1008
+ return `CI/Headless Authentication for ${provider.name}
1009
+
1010
+ For CI/CD environments, you need to set environment variables instead of interactive login.
1011
+
1012
+ Command (for generating token): ${cmd}
1013
+
1014
+ Environment variables to set:
1015
+ ${provider.envVars?.map(v => ` - ${v}`).join('\n') || ' (none defined)'}
1016
+
1017
+ For Firebase specifically:
1018
+ 1. Run locally: firebase login:ci
1019
+ 2. Copy the token
1020
+ 3. Set FIREBASE_TOKEN in your CI environment
1021
+
1022
+ For service accounts (Firebase/GCP):
1023
+ 1. Create a service account in Google Cloud Console
1024
+ 2. Download the JSON key file
1025
+ 3. Set GOOGLE_APPLICATION_CREDENTIALS=/path/to/key.json`;
1026
+ }
1027
+ let cmd;
1028
+ if (reauth) {
1029
+ cmd = providerCommands['reauth'] || provider.loginCommand;
1030
+ }
1031
+ else if (noLocalhost) {
1032
+ cmd = providerCommands['no_localhost'] || provider.loginCommand;
1033
+ }
1034
+ else {
1035
+ cmd = providerCommands['standard'] || provider.loginCommand;
1036
+ }
1037
+ // Use 'script' command to provide pseudo-TTY for the login command
1038
+ // This enables browser-based OAuth even in non-interactive shells
1039
+ const platform = process.platform;
1040
+ let wrappedCmd;
1041
+ if (platform === 'darwin') {
1042
+ // macOS: script -q /dev/null <command>
1043
+ wrappedCmd = `script -q /dev/null ${cmd}`;
1044
+ }
1045
+ else if (platform === 'linux') {
1046
+ // Linux: script -q -c "<command>" /dev/null
1047
+ wrappedCmd = `script -q -c "${cmd}" /dev/null`;
1048
+ }
1049
+ else {
1050
+ // Windows or other: try direct execution
1051
+ wrappedCmd = cmd;
1052
+ }
1053
+ try {
1054
+ const loginStartTime = Date.now();
1055
+ // Run with longer timeout for OAuth flow (user needs to complete in browser)
1056
+ const result = await runCommand(wrappedCmd, 180000);
1057
+ const loginDurationMs = Date.now() - loginStartTime;
1058
+ const loginChecks = [];
1059
+ loginChecks.push({
1060
+ check: 'Login command executed',
1061
+ passed: result.exitCode === 0,
1062
+ details: `Exit code: ${result.exitCode}`,
1063
+ });
1064
+ // CRITICAL: Don't trust string matching for "success" - actually verify authentication
1065
+ // The word "success" can appear in error messages like "Failed to verify success"
1066
+ // Always verify by actually checking authentication state
1067
+ const authCheck = await checkAuthentication(provider);
1068
+ loginChecks.push({
1069
+ check: 'Authentication verification',
1070
+ passed: authCheck.authenticated,
1071
+ details: authCheck.authenticated ? 'Credentials valid' : (authCheck.error || 'Not authenticated'),
1072
+ });
1073
+ if (authCheck.authenticated) {
1074
+ return verifiedSuccess(`${provider.name} login completed and verified`, `Provider: ${provider.name}\nCommand: ${cmd}\n\n${authCheck.details || 'Authentication verified.'}`, loginChecks, loginDurationMs);
1075
+ }
1076
+ else if (result.exitCode === 0) {
1077
+ // Command succeeded but auth verification failed
1078
+ // This can happen with race conditions or when auth takes time to propagate
1079
+ return unverifiedResult(`${provider.name} login command completed but verification failed`, `The login command exited successfully but we could not verify authentication.\n\nCommand output:\n${result.stdout}\n\nThis may be a temporary issue or the authentication may not have propagated yet.`, [
1080
+ `Wait a moment and try: ${provider.checkAuthCommand}`,
1081
+ `Re-run login: ${cmd}`,
1082
+ `Check credentials manually`,
1083
+ ], loginDurationMs);
1084
+ }
1085
+ else {
1086
+ // Command failed
1087
+ const combined = result.stdout + result.stderr;
1088
+ return verifiedFailure(`${provider.name} login failed`, `Exit code: ${result.exitCode}\n\nOutput:\n${combined || '(no output)'}`, [
1089
+ `Try again: ${cmd}`,
1090
+ `For browser issues: ${providerCommands['no_localhost'] || cmd}`,
1091
+ `Check network connection`,
1092
+ `Documentation: ${provider.installUrl}`,
1093
+ ], loginChecks, loginDurationMs);
1094
+ }
1095
+ }
1096
+ catch (error) {
1097
+ return verifiedFailure(`Error during ${provider.name} login`, error instanceof Error ? error.message : String(error), [
1098
+ `Run manually: ${cmd}`,
1099
+ `For headless environments, set: ${provider.envVars?.join(', ') || 'appropriate credentials'}`,
1100
+ ]);
1101
+ }
1102
+ },
1103
+ },
703
1104
  ];
704
1105
  }
705
1106
  // Helper functions