codecrypto-cli 1.0.21 → 1.0.22

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.
@@ -1,6 +1,7 @@
1
1
  import { Command } from 'commander';
2
2
  import chalk from 'chalk';
3
3
  import ora from 'ora';
4
+ import inquirer from 'inquirer';
4
5
  import { execSync, spawn } from 'child_process';
5
6
  import * as fs from 'fs';
6
7
  import * as path from 'path';
@@ -59,8 +60,8 @@ export const deployMcpCommand = new Command('deploy-mcp')
59
60
  projectName = path.basename(resolvedProjectPath).toLowerCase().replace(/[^a-z0-9._-]/g, '-');
60
61
  }
61
62
 
62
- // Obtener el prefijo de imagen desde token.json (email sin @dominio)
63
- const dockerHubUsername = getDockerHubUsernameFromToken();
63
+ // Obtener el prefijo de imagen desde Docker config o solicitar por teclado
64
+ const dockerHubUsername = await getDockerHubUsernameFromToken();
64
65
  const imageBase = `${dockerHubUsername}/${projectName}`;
65
66
  const imageName = `${imageBase}:${version}`;
66
67
  const imageLatest = `${imageBase}:latest`;
@@ -564,49 +565,182 @@ echo "✅ Deployment preparation completed inside container"
564
565
  }
565
566
  });
566
567
 
567
- // Función para obtener el username de Docker Hub desde token.json
568
- function getDockerHubUsernameFromToken(): string {
569
- const tokenFilePath = path.join(os.homedir(), '.codecrypto', 'token.json');
568
+ // Función para obtener el username de Docker Hub desde Docker config o solicitar por teclado
569
+ async function getDockerHubUsernameFromToken(): Promise<string> {
570
+ // Obtener el username desde Docker config (requerido para poder hacer push)
571
+ const dockerConfigPath = path.join(os.homedir(), '.docker', 'config.json');
570
572
 
571
- if (!fs.existsSync(tokenFilePath)) {
572
- throw new Error(`Token file not found at ${tokenFilePath}. Please run 'codecrypto auth' first.`);
573
+ if (!fs.existsSync(dockerConfigPath)) {
574
+ throw new Error(
575
+ 'Docker authentication not found. Please login to Docker first.\n' +
576
+ ' Run: docker login\n' +
577
+ ' This is required to push images to Docker Hub.'
578
+ );
573
579
  }
574
580
 
575
- let tokenData: any;
576
581
  try {
577
- tokenData = JSON.parse(fs.readFileSync(tokenFilePath, 'utf-8'));
582
+ const dockerConfig = JSON.parse(fs.readFileSync(dockerConfigPath, 'utf-8'));
583
+
584
+ // Verificar si hay credsStore (las credenciales están en el keychain del sistema)
585
+ const hasCredsStore = dockerConfig.credsStore && dockerConfig.credsStore !== '';
586
+
587
+ // Buscar username en Docker Hub auth
588
+ let dockerHubKeys: string[] = [];
589
+ if (dockerConfig.auths && Object.keys(dockerConfig.auths).length > 0) {
590
+ dockerHubKeys = Object.keys(dockerConfig.auths).filter(reg =>
591
+ reg.includes('docker.io') ||
592
+ reg.includes('hub.docker.com') ||
593
+ reg === 'https://index.docker.io/v1/'
594
+ );
595
+
596
+ // Si hay credsStore, los auths pueden estar vacíos pero las credenciales están en el keychain
597
+ if (hasCredsStore && dockerHubKeys.length > 0) {
598
+ // Intentar obtener username desde docker info
599
+ try {
600
+ const dockerInfo = execSync('docker info', { encoding: 'utf-8', stdio: 'pipe', timeout: 5000 });
601
+ const usernameMatch = dockerInfo.match(/Username:\s*(.+)/i);
602
+ if (usernameMatch && usernameMatch[1]) {
603
+ let username = usernameMatch[1].trim();
604
+
605
+ // Si el username es un email, extraer solo la parte antes del @
606
+ if (username.includes('@')) {
607
+ username = username.split('@')[0];
608
+ }
609
+
610
+ // Sanitizar el username para Docker Hub
611
+ username = username.toLowerCase()
612
+ .replace(/[^a-z0-9._-]/g, '-')
613
+ .replace(/^-+|-+$/g, '')
614
+ .replace(/-+/g, '-')
615
+ .replace(/\.+/g, '.')
616
+ .substring(0, 30);
617
+
618
+ if (username) {
619
+ return username;
620
+ }
621
+ }
622
+ } catch (infoError) {
623
+ // Si docker info falla, continuar con otros métodos
624
+ }
625
+ }
626
+
627
+ // Si no hay credsStore, buscar username directamente en auths
628
+ if (!hasCredsStore) {
629
+ for (const key of dockerHubKeys) {
630
+ const authData = dockerConfig.auths[key];
631
+ if (authData && authData.username) {
632
+ let username = authData.username;
633
+
634
+ // Si el username es un email, extraer solo la parte antes del @
635
+ if (username.includes('@')) {
636
+ username = username.split('@')[0];
637
+ }
638
+
639
+ // Sanitizar el username para Docker Hub
640
+ username = username.toLowerCase()
641
+ .replace(/[^a-z0-9._-]/g, '-')
642
+ .replace(/^-+|-+$/g, '')
643
+ .replace(/-+/g, '-')
644
+ .replace(/\.+/g, '.')
645
+ .substring(0, 30);
646
+
647
+ if (username) {
648
+ return username;
649
+ }
650
+ }
651
+ }
652
+ }
653
+ }
654
+
655
+ // Si hay credsStore pero no encontramos username, intentar con docker info
656
+ if (hasCredsStore) {
657
+ try {
658
+ const dockerInfo = execSync('docker info', { encoding: 'utf-8', stdio: 'pipe', timeout: 5000 });
659
+ const usernameMatch = dockerInfo.match(/Username:\s*(.+)/i);
660
+ if (usernameMatch && usernameMatch[1]) {
661
+ let username = usernameMatch[1].trim();
662
+
663
+ // Si el username es un email, extraer solo la parte antes del @
664
+ if (username.includes('@')) {
665
+ username = username.split('@')[0];
666
+ }
667
+
668
+ // Sanitizar el username para Docker Hub
669
+ username = username.toLowerCase()
670
+ .replace(/[^a-z0-9._-]/g, '-')
671
+ .replace(/^-+|-+$/g, '')
672
+ .replace(/-+/g, '-')
673
+ .replace(/\.+/g, '.')
674
+ .substring(0, 30);
675
+
676
+ if (username) {
677
+ return username;
678
+ }
679
+ }
680
+ } catch (infoError) {
681
+ // Continuar con el error
682
+ }
683
+ }
684
+
685
+ // Si llegamos aquí, no encontramos el username - solicitar por teclado
686
+ console.log(chalk.yellow('\n⚠️ Could not automatically detect Docker Hub username.'));
687
+ console.log(chalk.gray(' Please enter your Docker Hub username manually.\n'));
688
+
689
+ const answer = await inquirer.prompt([
690
+ {
691
+ type: 'input',
692
+ name: 'username',
693
+ message: 'Docker Hub username:',
694
+ validate: (input: string) => {
695
+ if (!input || input.trim().length === 0) {
696
+ return 'Username is required';
697
+ }
698
+ // Validar formato básico de username Docker Hub
699
+ const sanitized = input.toLowerCase().replace(/[^a-z0-9._-]/g, '');
700
+ if (sanitized !== input.toLowerCase()) {
701
+ return 'Username can only contain lowercase letters, numbers, dots, hyphens, and underscores';
702
+ }
703
+ if (input.length > 30) {
704
+ return 'Username must be 30 characters or less';
705
+ }
706
+ return true;
707
+ },
708
+ filter: (input: string) => {
709
+ // Si el input es un email, extraer solo la parte antes del @
710
+ if (input.includes('@')) {
711
+ return input.split('@')[0].trim();
712
+ }
713
+ return input.trim();
714
+ }
715
+ }
716
+ ]);
717
+
718
+ let username = answer.username.toLowerCase();
719
+
720
+ // Sanitizar el username para Docker Hub
721
+ username = username
722
+ .replace(/[^a-z0-9._-]/g, '-')
723
+ .replace(/^-+|-+$/g, '')
724
+ .replace(/-+/g, '-')
725
+ .replace(/\.+/g, '.')
726
+ .substring(0, 30);
727
+
728
+ if (!username) {
729
+ throw new Error('Invalid username provided');
730
+ }
731
+
732
+ return username;
578
733
  } catch (error: any) {
579
- throw new Error(`Failed to read token file: ${error.message}`);
580
- }
581
-
582
- // Obtener el email del token
583
- const email = tokenData.email;
584
- if (!email) {
585
- throw new Error('Email not found in token.json. Please run "codecrypto auth --force" to refresh your token.');
586
- }
587
-
588
- // Extraer el username del email (parte antes del @) y sanitizarlo para Docker Hub
589
- const emailParts = email.split('@');
590
- if (emailParts.length < 2) {
591
- throw new Error(`Invalid email format in token.json: ${email}`);
592
- }
593
-
594
- let username = emailParts[0].toLowerCase();
595
-
596
- // Sanitizar el username para Docker Hub
597
- // Docker Hub permite: letras minúsculas, números, guiones, guiones bajos
598
- username = username
599
- .replace(/[^a-z0-9._-]/g, '-') // Reemplazar caracteres inválidos con guiones
600
- .replace(/^-+|-+$/g, '') // Eliminar guiones al inicio y final
601
- .replace(/-+/g, '-') // Reemplazar múltiples guiones con uno solo
602
- .replace(/\.+/g, '.') // Reemplazar múltiples puntos con uno solo
603
- .substring(0, 30); // Limitar longitud (Docker Hub tiene límites)
604
-
605
- if (!username) {
606
- throw new Error(`Could not extract valid username from email: ${email}`);
734
+ // Si el error ya tiene mensaje, relanzarlo
735
+ if (error.message && (error.message.includes('Docker') || error.message.includes('username'))) {
736
+ throw error;
737
+ }
738
+ // Si hay error leyendo Docker config, lanzar error
739
+ throw new Error(
740
+ `Failed to read Docker config: ${error.message || String(error)}\n` +
741
+ ' Please ensure you are logged in to Docker: docker login'
742
+ );
607
743
  }
608
-
609
- return username;
610
744
  }
611
745
 
612
746
  // Función para leer certificados Docker desde token.json
@@ -1,6 +1,7 @@
1
1
  import { Command } from 'commander';
2
2
  import chalk from 'chalk';
3
3
  import ora from 'ora';
4
+ import inquirer from 'inquirer';
4
5
  import { execSync, spawn } from 'child_process';
5
6
  import * as fs from 'fs';
6
7
  import * as path from 'path';
@@ -134,8 +135,8 @@ export const deployCommand = new Command('deploy')
134
135
  projectName = path.basename(resolvedProjectPath).toLowerCase().replace(/[^a-z0-9._-]/g, '-');
135
136
  }
136
137
 
137
- // Obtener el prefijo de imagen desde token.json (email sin @dominio)
138
- const dockerHubUsername = getDockerHubUsernameFromToken();
138
+ // Obtener el prefijo de imagen desde Docker config o solicitar por teclado
139
+ const dockerHubUsername = await getDockerHubUsernameFromToken();
139
140
  const imageBase = `${dockerHubUsername}/${projectName}`;
140
141
  const imageName = `${imageBase}:${version}`;
141
142
  const imageLatest = `${imageBase}:latest`;
@@ -702,49 +703,182 @@ CMD ${JSON.stringify(startCmd)}
702
703
  }
703
704
  });
704
705
 
705
- // Función para obtener el username de Docker Hub desde token.json
706
- function getDockerHubUsernameFromToken(): string {
707
- const tokenFilePath = path.join(os.homedir(), '.codecrypto', 'token.json');
706
+ // Función para obtener el username de Docker Hub desde Docker config o solicitar por teclado
707
+ async function getDockerHubUsernameFromToken(): Promise<string> {
708
+ // Obtener el username desde Docker config (requerido para poder hacer push)
709
+ const dockerConfigPath = path.join(os.homedir(), '.docker', 'config.json');
708
710
 
709
- if (!fs.existsSync(tokenFilePath)) {
710
- throw new Error(`Token file not found at ${tokenFilePath}. Please run 'codecrypto auth' first.`);
711
+ if (!fs.existsSync(dockerConfigPath)) {
712
+ throw new Error(
713
+ 'Docker authentication not found. Please login to Docker first.\n' +
714
+ ' Run: docker login\n' +
715
+ ' This is required to push images to Docker Hub.'
716
+ );
711
717
  }
712
718
 
713
- let tokenData: any;
714
719
  try {
715
- tokenData = JSON.parse(fs.readFileSync(tokenFilePath, 'utf-8'));
720
+ const dockerConfig = JSON.parse(fs.readFileSync(dockerConfigPath, 'utf-8'));
721
+
722
+ // Verificar si hay credsStore (las credenciales están en el keychain del sistema)
723
+ const hasCredsStore = dockerConfig.credsStore && dockerConfig.credsStore !== '';
724
+
725
+ // Buscar username en Docker Hub auth
726
+ let dockerHubKeys: string[] = [];
727
+ if (dockerConfig.auths && Object.keys(dockerConfig.auths).length > 0) {
728
+ dockerHubKeys = Object.keys(dockerConfig.auths).filter(reg =>
729
+ reg.includes('docker.io') ||
730
+ reg.includes('hub.docker.com') ||
731
+ reg === 'https://index.docker.io/v1/'
732
+ );
733
+
734
+ // Si hay credsStore, los auths pueden estar vacíos pero las credenciales están en el keychain
735
+ if (hasCredsStore && dockerHubKeys.length > 0) {
736
+ // Intentar obtener username desde docker info
737
+ try {
738
+ const dockerInfo = execSync('docker info', { encoding: 'utf-8', stdio: 'pipe', timeout: 5000 });
739
+ const usernameMatch = dockerInfo.match(/Username:\s*(.+)/i);
740
+ if (usernameMatch && usernameMatch[1]) {
741
+ let username = usernameMatch[1].trim();
742
+
743
+ // Si el username es un email, extraer solo la parte antes del @
744
+ if (username.includes('@')) {
745
+ username = username.split('@')[0];
746
+ }
747
+
748
+ // Sanitizar el username para Docker Hub
749
+ username = username.toLowerCase()
750
+ .replace(/[^a-z0-9._-]/g, '-')
751
+ .replace(/^-+|-+$/g, '')
752
+ .replace(/-+/g, '-')
753
+ .replace(/\.+/g, '.')
754
+ .substring(0, 30);
755
+
756
+ if (username) {
757
+ return username;
758
+ }
759
+ }
760
+ } catch (infoError) {
761
+ // Si docker info falla, continuar con otros métodos
762
+ }
763
+ }
764
+
765
+ // Si no hay credsStore, buscar username directamente en auths
766
+ if (!hasCredsStore) {
767
+ for (const key of dockerHubKeys) {
768
+ const authData = dockerConfig.auths[key];
769
+ if (authData && authData.username) {
770
+ let username = authData.username;
771
+
772
+ // Si el username es un email, extraer solo la parte antes del @
773
+ if (username.includes('@')) {
774
+ username = username.split('@')[0];
775
+ }
776
+
777
+ // Sanitizar el username para Docker Hub
778
+ username = username.toLowerCase()
779
+ .replace(/[^a-z0-9._-]/g, '-')
780
+ .replace(/^-+|-+$/g, '')
781
+ .replace(/-+/g, '-')
782
+ .replace(/\.+/g, '.')
783
+ .substring(0, 30);
784
+
785
+ if (username) {
786
+ return username;
787
+ }
788
+ }
789
+ }
790
+ }
791
+ }
792
+
793
+ // Si hay credsStore pero no encontramos username, intentar con docker info
794
+ if (hasCredsStore) {
795
+ try {
796
+ const dockerInfo = execSync('docker info', { encoding: 'utf-8', stdio: 'pipe', timeout: 5000 });
797
+ const usernameMatch = dockerInfo.match(/Username:\s*(.+)/i);
798
+ if (usernameMatch && usernameMatch[1]) {
799
+ let username = usernameMatch[1].trim();
800
+
801
+ // Si el username es un email, extraer solo la parte antes del @
802
+ if (username.includes('@')) {
803
+ username = username.split('@')[0];
804
+ }
805
+
806
+ // Sanitizar el username para Docker Hub
807
+ username = username.toLowerCase()
808
+ .replace(/[^a-z0-9._-]/g, '-')
809
+ .replace(/^-+|-+$/g, '')
810
+ .replace(/-+/g, '-')
811
+ .replace(/\.+/g, '.')
812
+ .substring(0, 30);
813
+
814
+ if (username) {
815
+ return username;
816
+ }
817
+ }
818
+ } catch (infoError) {
819
+ // Continuar con el error
820
+ }
821
+ }
822
+
823
+ // Si llegamos aquí, no encontramos el username - solicitar por teclado
824
+ console.log(chalk.yellow('\n⚠️ Could not automatically detect Docker Hub username.'));
825
+ console.log(chalk.gray(' Please enter your Docker Hub username manually.\n'));
826
+
827
+ const answer = await inquirer.prompt([
828
+ {
829
+ type: 'input',
830
+ name: 'username',
831
+ message: 'Docker Hub username:',
832
+ validate: (input: string) => {
833
+ if (!input || input.trim().length === 0) {
834
+ return 'Username is required';
835
+ }
836
+ // Validar formato básico de username Docker Hub
837
+ const sanitized = input.toLowerCase().replace(/[^a-z0-9._-]/g, '');
838
+ if (sanitized !== input.toLowerCase()) {
839
+ return 'Username can only contain lowercase letters, numbers, dots, hyphens, and underscores';
840
+ }
841
+ if (input.length > 30) {
842
+ return 'Username must be 30 characters or less';
843
+ }
844
+ return true;
845
+ },
846
+ filter: (input: string) => {
847
+ // Si el input es un email, extraer solo la parte antes del @
848
+ if (input.includes('@')) {
849
+ return input.split('@')[0].trim();
850
+ }
851
+ return input.trim();
852
+ }
853
+ }
854
+ ]);
855
+
856
+ let username = answer.username.toLowerCase();
857
+
858
+ // Sanitizar el username para Docker Hub
859
+ username = username
860
+ .replace(/[^a-z0-9._-]/g, '-')
861
+ .replace(/^-+|-+$/g, '')
862
+ .replace(/-+/g, '-')
863
+ .replace(/\.+/g, '.')
864
+ .substring(0, 30);
865
+
866
+ if (!username) {
867
+ throw new Error('Invalid username provided');
868
+ }
869
+
870
+ return username;
716
871
  } catch (error: any) {
717
- throw new Error(`Failed to read token file: ${error.message}`);
718
- }
719
-
720
- // Obtener el email del token
721
- const email = tokenData.email;
722
- if (!email) {
723
- throw new Error('Email not found in token.json. Please run "codecrypto auth --force" to refresh your token.');
724
- }
725
-
726
- // Extraer el username del email (parte antes del @) y sanitizarlo para Docker Hub
727
- const emailParts = email.split('@');
728
- if (emailParts.length < 2) {
729
- throw new Error(`Invalid email format in token.json: ${email}`);
730
- }
731
-
732
- let username = emailParts[0].toLowerCase();
733
-
734
- // Sanitizar el username para Docker Hub
735
- // Docker Hub permite: letras minúsculas, números, guiones, guiones bajos
736
- username = username
737
- .replace(/[^a-z0-9._-]/g, '-') // Reemplazar caracteres inválidos con guiones
738
- .replace(/^-+|-+$/g, '') // Eliminar guiones al inicio y final
739
- .replace(/-+/g, '-') // Reemplazar múltiples guiones con uno solo
740
- .replace(/\.+/g, '.') // Reemplazar múltiples puntos con uno solo
741
- .substring(0, 30); // Limitar longitud (Docker Hub tiene límites)
742
-
743
- if (!username) {
744
- throw new Error(`Could not extract valid username from email: ${email}`);
872
+ // Si el error ya tiene mensaje, relanzarlo
873
+ if (error.message && (error.message.includes('Docker') || error.message.includes('username'))) {
874
+ throw error;
875
+ }
876
+ // Si hay error leyendo Docker config, lanzar error
877
+ throw new Error(
878
+ `Failed to read Docker config: ${error.message || String(error)}\n` +
879
+ ' Please ensure you are logged in to Docker: docker login'
880
+ );
745
881
  }
746
-
747
- return username;
748
882
  }
749
883
 
750
884
  // Función para leer certificados Docker desde token.json