codecrypto-cli 1.0.21 → 1.0.23

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`;
@@ -458,14 +459,22 @@ echo "✅ Deployment preparation completed inside container"
458
459
  }
459
460
 
460
461
  // Paso 5: Construir y hacer push
461
- const buildDockerSpinner = ora('Building Docker image for multiple platforms...').start();
462
+ // Detectar si estamos en Windows (buildx tiene problemas con ARM en Windows)
463
+ const isWindows = os.platform() === 'win32';
464
+ const platform = isWindows ? 'linux/amd64' : 'linux/amd64,linux/arm64';
465
+ const platformDisplay = isWindows ? 'linux/amd64' : 'linux/amd64, linux/arm64';
466
+ const buildSpinnerText = isWindows
467
+ ? 'Building Docker image for linux/amd64...'
468
+ : 'Building Docker image for multiple platforms...';
469
+
470
+ const buildDockerSpinner = ora(buildSpinnerText).start();
462
471
  try {
463
472
  const tags = shouldPush
464
473
  ? [`--tag ${imageName}`, `--tag ${imageLatest}`, '--push']
465
474
  : [`--tag ${imageName}`, `--tag ${imageLatest}`, '--load'];
466
475
 
467
476
  const buildCommand = `docker buildx build \
468
- --platform linux/amd64,linux/arm64 \
477
+ --platform ${platform} \
469
478
  ${tags.join(' \\\n ')} \
470
479
  --progress=plain \
471
480
  .`;
@@ -475,7 +484,10 @@ echo "✅ Deployment preparation completed inside container"
475
484
  console.log(chalk.cyan(buildCommand));
476
485
  console.log(chalk.gray('─'.repeat(60)));
477
486
  console.log(chalk.gray(`\n Working directory: ${chalk.cyan(resolvedDestFolder)}`));
478
- console.log(chalk.gray(` Platforms: ${chalk.cyan('linux/amd64, linux/arm64')}`));
487
+ console.log(chalk.gray(` Platforms: ${chalk.cyan(platformDisplay)}`));
488
+ if (isWindows) {
489
+ console.log(chalk.yellow(` Note: Building only for linux/amd64 on Windows (ARM builds are disabled)`));
490
+ }
479
491
  console.log(chalk.gray(` Push to registry: ${shouldPush ? chalk.green('Yes') : chalk.yellow('No')}\n`));
480
492
 
481
493
  execSync(buildCommand, {
@@ -564,49 +576,182 @@ echo "✅ Deployment preparation completed inside container"
564
576
  }
565
577
  });
566
578
 
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');
579
+ // Función para obtener el username de Docker Hub desde Docker config o solicitar por teclado
580
+ async function getDockerHubUsernameFromToken(): Promise<string> {
581
+ // Obtener el username desde Docker config (requerido para poder hacer push)
582
+ const dockerConfigPath = path.join(os.homedir(), '.docker', 'config.json');
570
583
 
571
- if (!fs.existsSync(tokenFilePath)) {
572
- throw new Error(`Token file not found at ${tokenFilePath}. Please run 'codecrypto auth' first.`);
584
+ if (!fs.existsSync(dockerConfigPath)) {
585
+ throw new Error(
586
+ 'Docker authentication not found. Please login to Docker first.\n' +
587
+ ' Run: docker login\n' +
588
+ ' This is required to push images to Docker Hub.'
589
+ );
573
590
  }
574
591
 
575
- let tokenData: any;
576
592
  try {
577
- tokenData = JSON.parse(fs.readFileSync(tokenFilePath, 'utf-8'));
593
+ const dockerConfig = JSON.parse(fs.readFileSync(dockerConfigPath, 'utf-8'));
594
+
595
+ // Verificar si hay credsStore (las credenciales están en el keychain del sistema)
596
+ const hasCredsStore = dockerConfig.credsStore && dockerConfig.credsStore !== '';
597
+
598
+ // Buscar username en Docker Hub auth
599
+ let dockerHubKeys: string[] = [];
600
+ if (dockerConfig.auths && Object.keys(dockerConfig.auths).length > 0) {
601
+ dockerHubKeys = Object.keys(dockerConfig.auths).filter(reg =>
602
+ reg.includes('docker.io') ||
603
+ reg.includes('hub.docker.com') ||
604
+ reg === 'https://index.docker.io/v1/'
605
+ );
606
+
607
+ // Si hay credsStore, los auths pueden estar vacíos pero las credenciales están en el keychain
608
+ if (hasCredsStore && dockerHubKeys.length > 0) {
609
+ // Intentar obtener username desde docker info
610
+ try {
611
+ const dockerInfo = execSync('docker info', { encoding: 'utf-8', stdio: 'pipe', timeout: 5000 });
612
+ const usernameMatch = dockerInfo.match(/Username:\s*(.+)/i);
613
+ if (usernameMatch && usernameMatch[1]) {
614
+ let username = usernameMatch[1].trim();
615
+
616
+ // Si el username es un email, extraer solo la parte antes del @
617
+ if (username.includes('@')) {
618
+ username = username.split('@')[0];
619
+ }
620
+
621
+ // Sanitizar el username para Docker Hub
622
+ username = username.toLowerCase()
623
+ .replace(/[^a-z0-9._-]/g, '-')
624
+ .replace(/^-+|-+$/g, '')
625
+ .replace(/-+/g, '-')
626
+ .replace(/\.+/g, '.')
627
+ .substring(0, 30);
628
+
629
+ if (username) {
630
+ return username;
631
+ }
632
+ }
633
+ } catch (infoError) {
634
+ // Si docker info falla, continuar con otros métodos
635
+ }
636
+ }
637
+
638
+ // Si no hay credsStore, buscar username directamente en auths
639
+ if (!hasCredsStore) {
640
+ for (const key of dockerHubKeys) {
641
+ const authData = dockerConfig.auths[key];
642
+ if (authData && authData.username) {
643
+ let username = authData.username;
644
+
645
+ // Si el username es un email, extraer solo la parte antes del @
646
+ if (username.includes('@')) {
647
+ username = username.split('@')[0];
648
+ }
649
+
650
+ // Sanitizar el username para Docker Hub
651
+ username = username.toLowerCase()
652
+ .replace(/[^a-z0-9._-]/g, '-')
653
+ .replace(/^-+|-+$/g, '')
654
+ .replace(/-+/g, '-')
655
+ .replace(/\.+/g, '.')
656
+ .substring(0, 30);
657
+
658
+ if (username) {
659
+ return username;
660
+ }
661
+ }
662
+ }
663
+ }
664
+ }
665
+
666
+ // Si hay credsStore pero no encontramos username, intentar con docker info
667
+ if (hasCredsStore) {
668
+ try {
669
+ const dockerInfo = execSync('docker info', { encoding: 'utf-8', stdio: 'pipe', timeout: 5000 });
670
+ const usernameMatch = dockerInfo.match(/Username:\s*(.+)/i);
671
+ if (usernameMatch && usernameMatch[1]) {
672
+ let username = usernameMatch[1].trim();
673
+
674
+ // Si el username es un email, extraer solo la parte antes del @
675
+ if (username.includes('@')) {
676
+ username = username.split('@')[0];
677
+ }
678
+
679
+ // Sanitizar el username para Docker Hub
680
+ username = username.toLowerCase()
681
+ .replace(/[^a-z0-9._-]/g, '-')
682
+ .replace(/^-+|-+$/g, '')
683
+ .replace(/-+/g, '-')
684
+ .replace(/\.+/g, '.')
685
+ .substring(0, 30);
686
+
687
+ if (username) {
688
+ return username;
689
+ }
690
+ }
691
+ } catch (infoError) {
692
+ // Continuar con el error
693
+ }
694
+ }
695
+
696
+ // Si llegamos aquí, no encontramos el username - solicitar por teclado
697
+ console.log(chalk.yellow('\n⚠️ Could not automatically detect Docker Hub username.'));
698
+ console.log(chalk.gray(' Please enter your Docker Hub username manually.\n'));
699
+
700
+ const answer = await inquirer.prompt([
701
+ {
702
+ type: 'input',
703
+ name: 'username',
704
+ message: 'Docker Hub username:',
705
+ validate: (input: string) => {
706
+ if (!input || input.trim().length === 0) {
707
+ return 'Username is required';
708
+ }
709
+ // Validar formato básico de username Docker Hub
710
+ const sanitized = input.toLowerCase().replace(/[^a-z0-9._-]/g, '');
711
+ if (sanitized !== input.toLowerCase()) {
712
+ return 'Username can only contain lowercase letters, numbers, dots, hyphens, and underscores';
713
+ }
714
+ if (input.length > 30) {
715
+ return 'Username must be 30 characters or less';
716
+ }
717
+ return true;
718
+ },
719
+ filter: (input: string) => {
720
+ // Si el input es un email, extraer solo la parte antes del @
721
+ if (input.includes('@')) {
722
+ return input.split('@')[0].trim();
723
+ }
724
+ return input.trim();
725
+ }
726
+ }
727
+ ]);
728
+
729
+ let username = answer.username.toLowerCase();
730
+
731
+ // Sanitizar el username para Docker Hub
732
+ username = username
733
+ .replace(/[^a-z0-9._-]/g, '-')
734
+ .replace(/^-+|-+$/g, '')
735
+ .replace(/-+/g, '-')
736
+ .replace(/\.+/g, '.')
737
+ .substring(0, 30);
738
+
739
+ if (!username) {
740
+ throw new Error('Invalid username provided');
741
+ }
742
+
743
+ return username;
578
744
  } 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}`);
745
+ // Si el error ya tiene mensaje, relanzarlo
746
+ if (error.message && (error.message.includes('Docker') || error.message.includes('username'))) {
747
+ throw error;
748
+ }
749
+ // Si hay error leyendo Docker config, lanzar error
750
+ throw new Error(
751
+ `Failed to read Docker config: ${error.message || String(error)}\n` +
752
+ ' Please ensure you are logged in to Docker: docker login'
753
+ );
607
754
  }
608
-
609
- return username;
610
755
  }
611
756
 
612
757
  // 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`;
@@ -615,14 +616,22 @@ CMD ${JSON.stringify(startCmd)}
615
616
  }
616
617
 
617
618
  // Paso 5: Construir y hacer push
618
- const buildDockerSpinner = ora('Building Docker image for multiple platforms...').start();
619
+ // Detectar si estamos en Windows (buildx tiene problemas con ARM en Windows)
620
+ const isWindows = os.platform() === 'win32';
621
+ const platform = isWindows ? 'linux/amd64' : 'linux/amd64,linux/arm64';
622
+ const platformDisplay = isWindows ? 'linux/amd64' : 'linux/amd64, linux/arm64';
623
+ const buildSpinnerText = isWindows
624
+ ? 'Building Docker image for linux/amd64...'
625
+ : 'Building Docker image for multiple platforms...';
626
+
627
+ const buildDockerSpinner = ora(buildSpinnerText).start();
619
628
  try {
620
629
  const tags = shouldPush
621
630
  ? [`--tag ${imageName}`, `--tag ${imageLatest}`, '--push']
622
631
  : [`--tag ${imageName}`, `--tag ${imageLatest}`, '--load'];
623
632
 
624
633
  const buildCommand = `docker buildx build \
625
- --platform linux/amd64,linux/arm64 \
634
+ --platform ${platform} \
626
635
  ${tags.join(' \\\n ')} \
627
636
  --progress=plain \
628
637
  .`;
@@ -632,7 +641,10 @@ CMD ${JSON.stringify(startCmd)}
632
641
  console.log(chalk.cyan(buildCommand));
633
642
  console.log(chalk.gray('─'.repeat(60)));
634
643
  console.log(chalk.gray(`\n Working directory: ${chalk.cyan(resolvedDestFolder)}`));
635
- console.log(chalk.gray(` Platforms: ${chalk.cyan('linux/amd64, linux/arm64')}`));
644
+ console.log(chalk.gray(` Platforms: ${chalk.cyan(platformDisplay)}`));
645
+ if (isWindows) {
646
+ console.log(chalk.yellow(` Note: Building only for linux/amd64 on Windows (ARM builds are disabled)`));
647
+ }
636
648
  console.log(chalk.gray(` Push to registry: ${shouldPush ? chalk.green('Yes') : chalk.yellow('No')}\n`));
637
649
 
638
650
  execSync(buildCommand, {
@@ -702,49 +714,182 @@ CMD ${JSON.stringify(startCmd)}
702
714
  }
703
715
  });
704
716
 
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');
717
+ // Función para obtener el username de Docker Hub desde Docker config o solicitar por teclado
718
+ async function getDockerHubUsernameFromToken(): Promise<string> {
719
+ // Obtener el username desde Docker config (requerido para poder hacer push)
720
+ const dockerConfigPath = path.join(os.homedir(), '.docker', 'config.json');
708
721
 
709
- if (!fs.existsSync(tokenFilePath)) {
710
- throw new Error(`Token file not found at ${tokenFilePath}. Please run 'codecrypto auth' first.`);
722
+ if (!fs.existsSync(dockerConfigPath)) {
723
+ throw new Error(
724
+ 'Docker authentication not found. Please login to Docker first.\n' +
725
+ ' Run: docker login\n' +
726
+ ' This is required to push images to Docker Hub.'
727
+ );
711
728
  }
712
729
 
713
- let tokenData: any;
714
730
  try {
715
- tokenData = JSON.parse(fs.readFileSync(tokenFilePath, 'utf-8'));
731
+ const dockerConfig = JSON.parse(fs.readFileSync(dockerConfigPath, 'utf-8'));
732
+
733
+ // Verificar si hay credsStore (las credenciales están en el keychain del sistema)
734
+ const hasCredsStore = dockerConfig.credsStore && dockerConfig.credsStore !== '';
735
+
736
+ // Buscar username en Docker Hub auth
737
+ let dockerHubKeys: string[] = [];
738
+ if (dockerConfig.auths && Object.keys(dockerConfig.auths).length > 0) {
739
+ dockerHubKeys = Object.keys(dockerConfig.auths).filter(reg =>
740
+ reg.includes('docker.io') ||
741
+ reg.includes('hub.docker.com') ||
742
+ reg === 'https://index.docker.io/v1/'
743
+ );
744
+
745
+ // Si hay credsStore, los auths pueden estar vacíos pero las credenciales están en el keychain
746
+ if (hasCredsStore && dockerHubKeys.length > 0) {
747
+ // Intentar obtener username desde docker info
748
+ try {
749
+ const dockerInfo = execSync('docker info', { encoding: 'utf-8', stdio: 'pipe', timeout: 5000 });
750
+ const usernameMatch = dockerInfo.match(/Username:\s*(.+)/i);
751
+ if (usernameMatch && usernameMatch[1]) {
752
+ let username = usernameMatch[1].trim();
753
+
754
+ // Si el username es un email, extraer solo la parte antes del @
755
+ if (username.includes('@')) {
756
+ username = username.split('@')[0];
757
+ }
758
+
759
+ // Sanitizar el username para Docker Hub
760
+ username = username.toLowerCase()
761
+ .replace(/[^a-z0-9._-]/g, '-')
762
+ .replace(/^-+|-+$/g, '')
763
+ .replace(/-+/g, '-')
764
+ .replace(/\.+/g, '.')
765
+ .substring(0, 30);
766
+
767
+ if (username) {
768
+ return username;
769
+ }
770
+ }
771
+ } catch (infoError) {
772
+ // Si docker info falla, continuar con otros métodos
773
+ }
774
+ }
775
+
776
+ // Si no hay credsStore, buscar username directamente en auths
777
+ if (!hasCredsStore) {
778
+ for (const key of dockerHubKeys) {
779
+ const authData = dockerConfig.auths[key];
780
+ if (authData && authData.username) {
781
+ let username = authData.username;
782
+
783
+ // Si el username es un email, extraer solo la parte antes del @
784
+ if (username.includes('@')) {
785
+ username = username.split('@')[0];
786
+ }
787
+
788
+ // Sanitizar el username para Docker Hub
789
+ username = username.toLowerCase()
790
+ .replace(/[^a-z0-9._-]/g, '-')
791
+ .replace(/^-+|-+$/g, '')
792
+ .replace(/-+/g, '-')
793
+ .replace(/\.+/g, '.')
794
+ .substring(0, 30);
795
+
796
+ if (username) {
797
+ return username;
798
+ }
799
+ }
800
+ }
801
+ }
802
+ }
803
+
804
+ // Si hay credsStore pero no encontramos username, intentar con docker info
805
+ if (hasCredsStore) {
806
+ try {
807
+ const dockerInfo = execSync('docker info', { encoding: 'utf-8', stdio: 'pipe', timeout: 5000 });
808
+ const usernameMatch = dockerInfo.match(/Username:\s*(.+)/i);
809
+ if (usernameMatch && usernameMatch[1]) {
810
+ let username = usernameMatch[1].trim();
811
+
812
+ // Si el username es un email, extraer solo la parte antes del @
813
+ if (username.includes('@')) {
814
+ username = username.split('@')[0];
815
+ }
816
+
817
+ // Sanitizar el username para Docker Hub
818
+ username = username.toLowerCase()
819
+ .replace(/[^a-z0-9._-]/g, '-')
820
+ .replace(/^-+|-+$/g, '')
821
+ .replace(/-+/g, '-')
822
+ .replace(/\.+/g, '.')
823
+ .substring(0, 30);
824
+
825
+ if (username) {
826
+ return username;
827
+ }
828
+ }
829
+ } catch (infoError) {
830
+ // Continuar con el error
831
+ }
832
+ }
833
+
834
+ // Si llegamos aquí, no encontramos el username - solicitar por teclado
835
+ console.log(chalk.yellow('\n⚠️ Could not automatically detect Docker Hub username.'));
836
+ console.log(chalk.gray(' Please enter your Docker Hub username manually.\n'));
837
+
838
+ const answer = await inquirer.prompt([
839
+ {
840
+ type: 'input',
841
+ name: 'username',
842
+ message: 'Docker Hub username:',
843
+ validate: (input: string) => {
844
+ if (!input || input.trim().length === 0) {
845
+ return 'Username is required';
846
+ }
847
+ // Validar formato básico de username Docker Hub
848
+ const sanitized = input.toLowerCase().replace(/[^a-z0-9._-]/g, '');
849
+ if (sanitized !== input.toLowerCase()) {
850
+ return 'Username can only contain lowercase letters, numbers, dots, hyphens, and underscores';
851
+ }
852
+ if (input.length > 30) {
853
+ return 'Username must be 30 characters or less';
854
+ }
855
+ return true;
856
+ },
857
+ filter: (input: string) => {
858
+ // Si el input es un email, extraer solo la parte antes del @
859
+ if (input.includes('@')) {
860
+ return input.split('@')[0].trim();
861
+ }
862
+ return input.trim();
863
+ }
864
+ }
865
+ ]);
866
+
867
+ let username = answer.username.toLowerCase();
868
+
869
+ // Sanitizar el username para Docker Hub
870
+ username = username
871
+ .replace(/[^a-z0-9._-]/g, '-')
872
+ .replace(/^-+|-+$/g, '')
873
+ .replace(/-+/g, '-')
874
+ .replace(/\.+/g, '.')
875
+ .substring(0, 30);
876
+
877
+ if (!username) {
878
+ throw new Error('Invalid username provided');
879
+ }
880
+
881
+ return username;
716
882
  } 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}`);
883
+ // Si el error ya tiene mensaje, relanzarlo
884
+ if (error.message && (error.message.includes('Docker') || error.message.includes('username'))) {
885
+ throw error;
886
+ }
887
+ // Si hay error leyendo Docker config, lanzar error
888
+ throw new Error(
889
+ `Failed to read Docker config: ${error.message || String(error)}\n` +
890
+ ' Please ensure you are logged in to Docker: docker login'
891
+ );
745
892
  }
746
-
747
- return username;
748
893
  }
749
894
 
750
895
  // Función para leer certificados Docker desde token.json