codecrypto-cli 1.0.20 → 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,7 +60,9 @@ export const deployMcpCommand = new Command('deploy-mcp')
59
60
  projectName = path.basename(resolvedProjectPath).toLowerCase().replace(/[^a-z0-9._-]/g, '-');
60
61
  }
61
62
 
62
- const imageBase = `jviejo/${projectName}`;
63
+ // Obtener el prefijo de imagen desde Docker config o solicitar por teclado
64
+ const dockerHubUsername = await getDockerHubUsernameFromToken();
65
+ const imageBase = `${dockerHubUsername}/${projectName}`;
63
66
  const imageName = `${imageBase}:${version}`;
64
67
  const imageLatest = `${imageBase}:latest`;
65
68
 
@@ -562,6 +565,184 @@ echo "✅ Deployment preparation completed inside container"
562
565
  }
563
566
  });
564
567
 
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');
572
+
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
+ );
579
+ }
580
+
581
+ try {
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;
733
+ } catch (error: any) {
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
+ );
743
+ }
744
+ }
745
+
565
746
  // Función para leer certificados Docker desde token.json
566
747
  function loadDockerCertificatesFromToken(): { caPem: string; certPem: string; keyPem: string; certPath: string } {
567
748
  const tokenFilePath = path.join(os.homedir(), '.codecrypto', '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,7 +135,9 @@ export const deployCommand = new Command('deploy')
134
135
  projectName = path.basename(resolvedProjectPath).toLowerCase().replace(/[^a-z0-9._-]/g, '-');
135
136
  }
136
137
 
137
- const imageBase = `jviejo/${projectName}`;
138
+ // Obtener el prefijo de imagen desde Docker config o solicitar por teclado
139
+ const dockerHubUsername = await getDockerHubUsernameFromToken();
140
+ const imageBase = `${dockerHubUsername}/${projectName}`;
138
141
  const imageName = `${imageBase}:${version}`;
139
142
  const imageLatest = `${imageBase}:latest`;
140
143
 
@@ -700,6 +703,184 @@ CMD ${JSON.stringify(startCmd)}
700
703
  }
701
704
  });
702
705
 
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');
710
+
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
+ );
717
+ }
718
+
719
+ try {
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;
871
+ } catch (error: any) {
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
+ );
881
+ }
882
+ }
883
+
703
884
  // Función para leer certificados Docker desde token.json
704
885
  function loadDockerCertificatesFromToken(): { caPem: string; certPem: string; keyPem: string; certPath: string } {
705
886
  const tokenFilePath = path.join(os.homedir(), '.codecrypto', 'token.json');
@@ -127,23 +127,210 @@ export const doctorCommand = new Command('doctor')
127
127
  });
128
128
  }
129
129
 
130
- // 6. Check Docker Hub authentication (optional)
131
- const dockerAuthCheck = ora('Checking Docker Hub authentication...').start();
130
+ // 6. Check Docker authentication
131
+ const dockerAuthCheck = ora('Checking Docker authentication...').start();
132
132
  try {
133
- execSync('docker pull hello-world:latest', { encoding: 'utf-8', stdio: 'pipe' });
134
- dockerAuthCheck.succeed('Docker Hub authentication OK');
135
- checks.push({
136
- name: 'Docker Hub Auth',
137
- status: 'success',
138
- message: 'Docker Hub authentication verified'
139
- });
140
- } catch (error) {
141
- dockerAuthCheck.warn('Docker Hub authentication may be required');
133
+ // Obtener email del token.json para comparar
134
+ const tokenFilePath = path.join(os.homedir(), '.codecrypto', 'token.json');
135
+ let tokenEmail: string | null = null;
136
+ if (fs.existsSync(tokenFilePath)) {
137
+ try {
138
+ const tokenData = JSON.parse(fs.readFileSync(tokenFilePath, 'utf-8'));
139
+ tokenEmail = tokenData.email || null;
140
+ } catch {
141
+ // Ignorar errores al leer token
142
+ }
143
+ }
144
+
145
+ // Verificar si hay credenciales guardadas en Docker config
146
+ const dockerConfigPath = path.join(os.homedir(), '.docker', 'config.json');
147
+ let dockerUsername: string | null = null;
148
+ let dockerEmail: string | null = null;
149
+ let hasDockerAuth = false;
150
+ let dockerAuthInfo = '';
151
+
152
+ if (fs.existsSync(dockerConfigPath)) {
153
+ try {
154
+ const dockerConfig = JSON.parse(fs.readFileSync(dockerConfigPath, 'utf-8'));
155
+
156
+ // Verificar si hay auths configurados
157
+ if (dockerConfig.auths && Object.keys(dockerConfig.auths).length > 0) {
158
+ hasDockerAuth = true;
159
+ const registries = Object.keys(dockerConfig.auths);
160
+ dockerAuthInfo = `Authenticated to: ${registries.join(', ')}`;
161
+
162
+ // Intentar obtener el username de Docker Hub
163
+ const dockerHubKey = registries.find(reg =>
164
+ reg.includes('docker.io') ||
165
+ reg.includes('hub.docker.com') ||
166
+ reg === 'https://index.docker.io/v1/'
167
+ );
168
+
169
+ if (dockerHubKey && dockerConfig.auths[dockerHubKey]) {
170
+ // El username puede estar en auths[registry].auth (base64) o directamente
171
+ const authData = dockerConfig.auths[dockerHubKey];
172
+ if (authData && authData.username) {
173
+ dockerUsername = authData.username;
174
+ // Si el username parece un email, usarlo como email
175
+ if (dockerUsername && dockerUsername.includes('@')) {
176
+ dockerEmail = dockerUsername;
177
+ }
178
+ }
179
+ }
180
+
181
+ // Intentar obtener username de docker info como fallback
182
+ if (!dockerUsername) {
183
+ try {
184
+ const dockerInfo = execSync('docker info', { encoding: 'utf-8', stdio: 'pipe', timeout: 5000 });
185
+ const usernameMatch = dockerInfo.match(/Username:\s*(.+)/i);
186
+ if (usernameMatch) {
187
+ dockerUsername = usernameMatch[1].trim();
188
+ if (dockerUsername.includes('@')) {
189
+ dockerEmail = dockerUsername;
190
+ }
191
+ }
192
+ } catch {
193
+ // Ignorar errores
194
+ }
195
+ }
196
+
197
+ // Verificar específicamente Docker Hub
198
+ const hasDockerHub = registries.some(reg =>
199
+ reg.includes('docker.io') ||
200
+ reg.includes('hub.docker.com') ||
201
+ reg === 'https://index.docker.io/v1/'
202
+ );
203
+
204
+ // Construir mensaje con información de email/usuario
205
+ let authMessage = `Docker authentication verified: ${dockerAuthInfo}`;
206
+ if (dockerUsername || dockerEmail) {
207
+ const dockerIdentity = dockerEmail || dockerUsername;
208
+ authMessage += ` (User: ${dockerIdentity})`;
209
+
210
+ // Comparar con token.json
211
+ if (tokenEmail && dockerEmail) {
212
+ if (dockerEmail.toLowerCase() === tokenEmail.toLowerCase()) {
213
+ authMessage += ` ✓ Matches token.json email`;
214
+ } else {
215
+ authMessage += ` ⚠ Different from token.json email (${tokenEmail})`;
216
+ }
217
+ } else if (tokenEmail && dockerUsername && !dockerEmail) {
218
+ // Comparar username con email del token (parte antes del @)
219
+ const tokenUsername = tokenEmail.split('@')[0].toLowerCase();
220
+ if (dockerUsername.toLowerCase() === tokenUsername) {
221
+ authMessage += ` ✓ Username matches token.json email`;
222
+ } else {
223
+ authMessage += ` ⚠ Username differs from token.json email (${tokenEmail})`;
224
+ }
225
+ }
226
+ }
227
+
228
+ if (hasDockerHub) {
229
+ dockerAuthCheck.succeed(`Docker authentication OK (Docker Hub configured)`);
230
+ checks.push({
231
+ name: 'Docker Auth',
232
+ status: 'success',
233
+ message: authMessage
234
+ });
235
+ } else {
236
+ dockerAuthCheck.succeed(`Docker authentication OK (${registries.length} registry/registries configured)`);
237
+ checks.push({
238
+ name: 'Docker Auth',
239
+ status: 'success',
240
+ message: authMessage
241
+ });
242
+ }
243
+ } else {
244
+ // No hay auths configurados, intentar verificar con pull
245
+ try {
246
+ execSync('docker pull hello-world:latest', { encoding: 'utf-8', stdio: 'pipe', timeout: 10000 });
247
+ dockerAuthCheck.succeed('Docker authentication OK (public access)');
248
+ let message = 'Docker can access public images (authentication may not be required)';
249
+ if (tokenEmail) {
250
+ message += ` (Token email: ${tokenEmail})`;
251
+ }
252
+ checks.push({
253
+ name: 'Docker Auth',
254
+ status: 'success',
255
+ message: message
256
+ });
257
+ } catch (pullError) {
258
+ dockerAuthCheck.warn('Docker authentication not configured');
259
+ let details = 'Run "docker login" if you need to push images to Docker Hub or private registries';
260
+ if (tokenEmail) {
261
+ details += ` (Token email: ${tokenEmail})`;
262
+ }
263
+ checks.push({
264
+ name: 'Docker Auth',
265
+ status: 'warning',
266
+ message: 'Docker authentication not configured',
267
+ details: details
268
+ });
269
+ }
270
+ }
271
+ } catch (configError) {
272
+ // Error leyendo config, intentar verificar con pull
273
+ try {
274
+ execSync('docker pull hello-world:latest', { encoding: 'utf-8', stdio: 'pipe', timeout: 10000 });
275
+ dockerAuthCheck.succeed('Docker authentication OK');
276
+ let message = 'Docker can access public images';
277
+ if (tokenEmail) {
278
+ message += ` (Token email: ${tokenEmail})`;
279
+ }
280
+ checks.push({
281
+ name: 'Docker Auth',
282
+ status: 'success',
283
+ message: message
284
+ });
285
+ } catch (pullError) {
286
+ dockerAuthCheck.warn('Docker authentication may be required');
287
+ let details = 'Run "docker login" if you need to push images';
288
+ if (tokenEmail) {
289
+ details += ` (Token email: ${tokenEmail})`;
290
+ }
291
+ checks.push({
292
+ name: 'Docker Auth',
293
+ status: 'warning',
294
+ message: 'Docker authentication not verified',
295
+ details: details
296
+ });
297
+ }
298
+ }
299
+ } else {
300
+ // No existe config.json, intentar verificar con pull
301
+ try {
302
+ execSync('docker pull hello-world:latest', { encoding: 'utf-8', stdio: 'pipe', timeout: 10000 });
303
+ dockerAuthCheck.succeed('Docker authentication OK (public access)');
304
+ let message = 'Docker can access public images (authentication may not be required)';
305
+ if (tokenEmail) {
306
+ message += ` (Token email: ${tokenEmail})`;
307
+ }
308
+ checks.push({
309
+ name: 'Docker Auth',
310
+ status: 'success',
311
+ message: message
312
+ });
313
+ } catch (error) {
314
+ dockerAuthCheck.warn('Docker authentication not configured');
315
+ let details = 'Run "docker login" if you need to push images to Docker Hub or private registries';
316
+ if (tokenEmail) {
317
+ details += ` (Token email: ${tokenEmail})`;
318
+ }
319
+ checks.push({
320
+ name: 'Docker Auth',
321
+ status: 'warning',
322
+ message: 'Docker authentication not configured',
323
+ details: details
324
+ });
325
+ }
326
+ }
327
+ } catch (error: any) {
328
+ dockerAuthCheck.fail('Docker authentication check failed');
142
329
  checks.push({
143
- name: 'Docker Hub Auth',
144
- status: 'warning',
145
- message: 'Docker Hub authentication not verified',
146
- details: 'Run "docker login" if you need to push images'
330
+ name: 'Docker Auth',
331
+ status: 'error',
332
+ message: 'Docker authentication check failed',
333
+ details: `Error: ${error.message || String(error)}`
147
334
  });
148
335
  }
149
336
 
@@ -499,11 +686,11 @@ export const doctorCommand = new Command('doctor')
499
686
  }
500
687
 
501
688
  besu1Check.succeed(`Besu1 network accessible (chain-id: ${chainId}, block: ${blockNumber})`);
502
- checks.push({
503
- name: 'Besu1 Network',
504
- status: 'success',
689
+ checks.push({
690
+ name: 'Besu1 Network',
691
+ status: 'success',
505
692
  message: `Besu1 network accessible: ${rpcUrl} (chain-id: ${chainId}, block: ${blockNumber})`
506
- });
693
+ });
507
694
  }
508
695
  } catch (error: any) {
509
696
  const errorMsg = error.message || String(error);