codecrypto-cli 1.0.17 → 1.0.18
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.
- package/dist/commands/auth.js +1 -1
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/deploy-mcp.d.ts +3 -0
- package/dist/commands/deploy-mcp.d.ts.map +1 -0
- package/dist/commands/deploy-mcp.js +858 -0
- package/dist/commands/deploy-mcp.js.map +1 -0
- package/dist/commands/deploy.js +3 -1
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +53 -116
- package/dist/commands/doctor.js.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/auth.ts +1 -1
- package/src/commands/deploy-mcp.ts +882 -0
- package/src/commands/deploy.ts +3 -1
- package/src/commands/doctor.ts +64 -122
- package/src/index.ts +2 -0
package/src/commands/deploy.ts
CHANGED
|
@@ -17,7 +17,7 @@ export const deployCommand = new Command('deploy')
|
|
|
17
17
|
.option('--service-name <name>', 'Service name for remote deployment')
|
|
18
18
|
.option('--env-file <path>', 'Path to .env file with environment variables')
|
|
19
19
|
.option('--port <port>', 'Application port', '3000')
|
|
20
|
-
.option('--domain-base <domain>', 'Domain base for deployment', '
|
|
20
|
+
.option('--domain-base <domain>', 'Domain base for deployment', 'codecrypto.academy')
|
|
21
21
|
.option('--image-version <version>', 'Image version/tag to deploy (overrides package.json version)')
|
|
22
22
|
.option('--skip-git-check', 'Skip Git repository status check', false)
|
|
23
23
|
.action(async (projectPath: string, destFolder: string, options) => {
|
|
@@ -958,6 +958,7 @@ async function deployToRemoteServer(config: {
|
|
|
958
958
|
'-l', `traefik.http.routers.${serviceName}-router.entrypoints=websecure`,
|
|
959
959
|
'-l', `traefik.http.routers.${serviceName}-router.tls=true`,
|
|
960
960
|
'-l', `traefik.http.services.${serviceName}-service.loadbalancer.server.port=${config.port}`,
|
|
961
|
+
'-l', `traefik.http.services.${serviceName}-service.loadbalancer.passHostHeader=true`,
|
|
961
962
|
config.imageName
|
|
962
963
|
];
|
|
963
964
|
|
|
@@ -976,6 +977,7 @@ async function deployToRemoteServer(config: {
|
|
|
976
977
|
-l traefik.http.routers.${serviceName}-router.entrypoints=websecure \\
|
|
977
978
|
-l traefik.http.routers.${serviceName}-router.tls=true \\
|
|
978
979
|
-l traefik.http.services.${serviceName}-service.loadbalancer.server.port=${config.port} \\
|
|
980
|
+
-l traefik.http.services.${serviceName}-service.loadbalancer.passHostHeader=true \\
|
|
979
981
|
${config.imageName}`;
|
|
980
982
|
|
|
981
983
|
console.log(chalk.gray('\n🚀 Deployment command (executing on remote Docker):'));
|
package/src/commands/doctor.ts
CHANGED
|
@@ -155,7 +155,9 @@ export const doctorCommand = new Command('doctor')
|
|
|
155
155
|
const tokenData = JSON.parse(fs.readFileSync(tokenFilePath, 'utf-8'));
|
|
156
156
|
if (tokenData.token && tokenData.email) {
|
|
157
157
|
// Validar el token con el servidor
|
|
158
|
-
|
|
158
|
+
// Siempre usar la URL correcta, ignorando serverUrl guardado si es diferente
|
|
159
|
+
const defaultServerUrl = 'https://proyectos.proyectos.codecrypto.academy';
|
|
160
|
+
const serverUrl = process.env.CODECRYPTO_API_URL || process.env.API_URL || defaultServerUrl;
|
|
159
161
|
const token = String(tokenData.token).trim();
|
|
160
162
|
|
|
161
163
|
try {
|
|
@@ -184,18 +186,25 @@ export const doctorCommand = new Command('doctor')
|
|
|
184
186
|
} else {
|
|
185
187
|
const errorText = await response.text();
|
|
186
188
|
let errorMessage = 'Token validation failed';
|
|
189
|
+
let errorCode = '';
|
|
187
190
|
try {
|
|
188
191
|
const errorJson = JSON.parse(errorText);
|
|
189
192
|
errorMessage = errorJson.error || errorJson.message || errorMessage;
|
|
193
|
+
errorCode = errorJson.code || errorJson.errorCode || '';
|
|
190
194
|
} catch {
|
|
191
195
|
errorMessage = errorText || errorMessage;
|
|
192
196
|
}
|
|
193
197
|
|
|
198
|
+
// Construir mensaje con código de error si existe
|
|
199
|
+
const fullErrorMessage = errorCode
|
|
200
|
+
? `${errorMessage}\n\n${errorCode}`
|
|
201
|
+
: errorMessage;
|
|
202
|
+
|
|
194
203
|
tokenCheck.fail(`Token validation failed: ${errorMessage}`);
|
|
195
204
|
checks.push({
|
|
196
205
|
name: 'CodeCrypto Token',
|
|
197
206
|
status: 'error',
|
|
198
|
-
message: `Token validation failed: ${
|
|
207
|
+
message: `Token validation failed: ${fullErrorMessage}`,
|
|
199
208
|
details: 'Run "codecrypto auth --force" to refresh your token'
|
|
200
209
|
});
|
|
201
210
|
}
|
|
@@ -255,10 +264,10 @@ export const doctorCommand = new Command('doctor')
|
|
|
255
264
|
});
|
|
256
265
|
}
|
|
257
266
|
|
|
258
|
-
// 8. Check server access (proyectos.codecrypto.academy)
|
|
259
|
-
const serverCheck = ora('Checking access to proyectos.codecrypto.academy...').start();
|
|
267
|
+
// 8. Check server access (proyectos.proyectos.codecrypto.academy)
|
|
268
|
+
const serverCheck = ora('Checking access to proyectos.proyectos.codecrypto.academy...').start();
|
|
260
269
|
try {
|
|
261
|
-
const serverUrl = 'https://proyectos.codecrypto.academy';
|
|
270
|
+
const serverUrl = 'https://proyectos.proyectos.codecrypto.academy';
|
|
262
271
|
const controller = new AbortController();
|
|
263
272
|
const timeoutId = setTimeout(() => controller.abort(), 10000); // 10 seconds timeout
|
|
264
273
|
|
|
@@ -296,7 +305,7 @@ export const doctorCommand = new Command('doctor')
|
|
|
296
305
|
name: 'Server Access',
|
|
297
306
|
status: 'error',
|
|
298
307
|
message: 'Server connection timeout',
|
|
299
|
-
details: `Cannot reach https://proyectos.codecrypto.academy. Check your internet connection.`
|
|
308
|
+
details: `Cannot reach https://proyectos.proyectos.codecrypto.academy. Check your internet connection.`
|
|
300
309
|
});
|
|
301
310
|
} else if (errorMsg.includes('ECONNREFUSED') || errorMsg.includes('ENOTFOUND') || errorMsg.includes('getaddrinfo')) {
|
|
302
311
|
serverCheck.fail('Cannot resolve server address');
|
|
@@ -304,7 +313,7 @@ export const doctorCommand = new Command('doctor')
|
|
|
304
313
|
name: 'Server Access',
|
|
305
314
|
status: 'error',
|
|
306
315
|
message: 'Cannot resolve server address',
|
|
307
|
-
details: `Cannot reach https://proyectos.codecrypto.academy. Check DNS resolution and network connectivity.`
|
|
316
|
+
details: `Cannot reach https://proyectos.proyectos.codecrypto.academy. Check DNS resolution and network connectivity.`
|
|
308
317
|
});
|
|
309
318
|
} else {
|
|
310
319
|
serverCheck.fail('Cannot access server');
|
|
@@ -312,7 +321,7 @@ export const doctorCommand = new Command('doctor')
|
|
|
312
321
|
name: 'Server Access',
|
|
313
322
|
status: 'error',
|
|
314
323
|
message: 'Cannot access server',
|
|
315
|
-
details: `Error: ${errorMsg}. Check if https://proyectos.codecrypto.academy is accessible.`
|
|
324
|
+
details: `Error: ${errorMsg}. Check if https://proyectos.proyectos.codecrypto.academy is accessible.`
|
|
316
325
|
});
|
|
317
326
|
}
|
|
318
327
|
}
|
|
@@ -448,37 +457,17 @@ export const doctorCommand = new Command('doctor')
|
|
|
448
457
|
});
|
|
449
458
|
}
|
|
450
459
|
|
|
451
|
-
// 13. Check
|
|
452
|
-
const
|
|
460
|
+
// 13. Check Besu1 network access (main blockchain)
|
|
461
|
+
const besu1Check = ora('Checking Besu1 network (https://besu1.proyectos.codecrypto.academy, chain-id: 81234)...').start();
|
|
453
462
|
try {
|
|
454
|
-
const
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
name: 'anvil',
|
|
458
|
-
status: 'success',
|
|
459
|
-
message: `anvil installed: ${anvilVersion}`
|
|
460
|
-
});
|
|
461
|
-
} catch (error) {
|
|
462
|
-
anvilCheck.fail('anvil not found');
|
|
463
|
-
checks.push({
|
|
464
|
-
name: 'anvil',
|
|
465
|
-
status: 'error',
|
|
466
|
-
message: 'anvil is not installed or not in PATH',
|
|
467
|
-
details: 'anvil comes with Foundry. Run "foundryup" to update.'
|
|
468
|
-
});
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
// 14. Check if Anvil is running on correct port and chain-id
|
|
472
|
-
const anvilRunningCheck = ora('Checking if Anvil is running (port 8545, chain-id 31337)...').start();
|
|
473
|
-
try {
|
|
474
|
-
// Try to get chain id from the RPC
|
|
475
|
-
const rpcUrl = 'http://localhost:8545';
|
|
463
|
+
const rpcUrl = 'https://besu1.proyectos.codecrypto.academy';
|
|
464
|
+
|
|
465
|
+
// Verificar chain-id
|
|
476
466
|
const chainIdResponse = execSync(
|
|
477
467
|
`cast chain-id --rpc-url ${rpcUrl}`,
|
|
478
|
-
{ encoding: 'utf-8', stdio: 'pipe', timeout:
|
|
468
|
+
{ encoding: 'utf-8', stdio: 'pipe', timeout: 10000 }
|
|
479
469
|
).trim();
|
|
480
470
|
|
|
481
|
-
// cast chain-id returns decimal number, but handle hex if needed
|
|
482
471
|
let chainId: number;
|
|
483
472
|
if (chainIdResponse.startsWith('0x') || chainIdResponse.startsWith('0X')) {
|
|
484
473
|
chainId = parseInt(chainIdResponse, 16);
|
|
@@ -486,98 +475,36 @@ export const doctorCommand = new Command('doctor')
|
|
|
486
475
|
chainId = parseInt(chainIdResponse, 10);
|
|
487
476
|
}
|
|
488
477
|
|
|
489
|
-
|
|
490
|
-
|
|
478
|
+
// Verificar que el chain-id es correcto
|
|
479
|
+
if (chainId !== 81234) {
|
|
480
|
+
besu1Check.fail(`Besu1 network chain-id mismatch: ${chainId} (expected 81234)`);
|
|
491
481
|
checks.push({
|
|
492
|
-
name: '
|
|
493
|
-
status: 'success',
|
|
494
|
-
message: `Anvil running on port 8545 with chain-id ${chainId}`
|
|
495
|
-
});
|
|
496
|
-
} else {
|
|
497
|
-
anvilRunningCheck.fail(`Anvil running but wrong chain-id: ${chainId} (expected 31337)`);
|
|
498
|
-
checks.push({
|
|
499
|
-
name: 'Anvil Running',
|
|
500
|
-
status: 'error',
|
|
501
|
-
message: `Anvil running but wrong chain-id: ${chainId}`,
|
|
502
|
-
details: 'Start Anvil with: anvil --chain-id 31337 --port 8545'
|
|
503
|
-
});
|
|
504
|
-
}
|
|
505
|
-
} catch (error: any) {
|
|
506
|
-
const errorMsg = error.message || String(error);
|
|
507
|
-
if (errorMsg.includes('ECONNREFUSED') || errorMsg.includes('Connection refused')) {
|
|
508
|
-
anvilRunningCheck.fail('Anvil is not running');
|
|
509
|
-
checks.push({
|
|
510
|
-
name: 'Anvil Running',
|
|
511
|
-
status: 'error',
|
|
512
|
-
message: 'Anvil is not running on port 8545',
|
|
513
|
-
details: 'Start Anvil with: anvil --chain-id 31337 --port 8545'
|
|
514
|
-
});
|
|
515
|
-
} else if (errorMsg.includes('timeout') || errorMsg.includes('ETIMEDOUT')) {
|
|
516
|
-
anvilRunningCheck.fail('Anvil connection timeout');
|
|
517
|
-
checks.push({
|
|
518
|
-
name: 'Anvil Running',
|
|
482
|
+
name: 'Besu1 Network',
|
|
519
483
|
status: 'error',
|
|
520
|
-
message:
|
|
521
|
-
details: 'Check if
|
|
484
|
+
message: `Besu1 network chain-id mismatch: ${chainId} (expected 81234)`,
|
|
485
|
+
details: 'Check if https://besu1.proyectos.codecrypto.academy is the correct network'
|
|
522
486
|
});
|
|
523
487
|
} else {
|
|
524
|
-
|
|
488
|
+
// Verificar que puede obtener el block number (confirma que la blockchain está operativa)
|
|
489
|
+
const blockNumberResponse = execSync(
|
|
490
|
+
`cast block-number --rpc-url ${rpcUrl}`,
|
|
491
|
+
{ encoding: 'utf-8', stdio: 'pipe', timeout: 10000 }
|
|
492
|
+
).trim();
|
|
493
|
+
|
|
494
|
+
let blockNumber: number;
|
|
495
|
+
if (blockNumberResponse.startsWith('0x') || blockNumberResponse.startsWith('0X')) {
|
|
496
|
+
blockNumber = parseInt(blockNumberResponse, 16);
|
|
497
|
+
} else {
|
|
498
|
+
blockNumber = parseInt(blockNumberResponse, 10);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
besu1Check.succeed(`Besu1 network accessible (chain-id: ${chainId}, block: ${blockNumber})`);
|
|
525
502
|
checks.push({
|
|
526
|
-
name: '
|
|
527
|
-
status: '
|
|
528
|
-
message:
|
|
529
|
-
details: `Error: ${errorMsg}. Start Anvil with: anvil --chain-id 31337 --port 8545`
|
|
503
|
+
name: 'Besu1 Network',
|
|
504
|
+
status: 'success',
|
|
505
|
+
message: `Besu1 network accessible: ${rpcUrl} (chain-id: ${chainId}, block: ${blockNumber})`
|
|
530
506
|
});
|
|
531
507
|
}
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
// 15. Check Anvil account 0
|
|
535
|
-
const anvilAccountCheck = ora('Checking Anvil account 0...').start();
|
|
536
|
-
try {
|
|
537
|
-
const rpcUrl = 'http://localhost:8545';
|
|
538
|
-
const balance = execSync(
|
|
539
|
-
`cast balance 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 --rpc-url ${rpcUrl}`,
|
|
540
|
-
{ encoding: 'utf-8', stdio: 'pipe', timeout: 5000 }
|
|
541
|
-
).trim();
|
|
542
|
-
|
|
543
|
-
anvilAccountCheck.succeed(`Account 0 balance: ${balance} wei`);
|
|
544
|
-
checks.push({
|
|
545
|
-
name: 'Anvil Account 0',
|
|
546
|
-
status: 'success',
|
|
547
|
-
message: `Account 0 available with balance: ${balance} wei`
|
|
548
|
-
});
|
|
549
|
-
} catch (error: any) {
|
|
550
|
-
anvilAccountCheck.fail('Cannot access Anvil account 0');
|
|
551
|
-
checks.push({
|
|
552
|
-
name: 'Anvil Account 0',
|
|
553
|
-
status: 'error',
|
|
554
|
-
message: 'Cannot access Anvil account 0',
|
|
555
|
-
details: 'Make sure Anvil is running and account 0 is unlocked'
|
|
556
|
-
});
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
// 16. Check Besu1 network access
|
|
560
|
-
const besu1Check = ora('Checking Besu1 network (https://besu1.proyectos.codecrypto.academy)...').start();
|
|
561
|
-
try {
|
|
562
|
-
const rpcUrl = 'https://besu1.proyectos.codecrypto.academy';
|
|
563
|
-
const chainIdResponse = execSync(
|
|
564
|
-
`cast chain-id --rpc-url ${rpcUrl}`,
|
|
565
|
-
{ encoding: 'utf-8', stdio: 'pipe', timeout: 10000 }
|
|
566
|
-
).trim();
|
|
567
|
-
|
|
568
|
-
let chainId: number;
|
|
569
|
-
if (chainIdResponse.startsWith('0x') || chainIdResponse.startsWith('0X')) {
|
|
570
|
-
chainId = parseInt(chainIdResponse, 16);
|
|
571
|
-
} else {
|
|
572
|
-
chainId = parseInt(chainIdResponse, 10);
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
besu1Check.succeed(`Besu1 network accessible (chain-id: ${chainId})`);
|
|
576
|
-
checks.push({
|
|
577
|
-
name: 'Besu1 Network',
|
|
578
|
-
status: 'success',
|
|
579
|
-
message: `Besu1 network accessible: ${rpcUrl} (chain-id: ${chainId})`
|
|
580
|
-
});
|
|
581
508
|
} catch (error: any) {
|
|
582
509
|
const errorMsg = error.message || String(error);
|
|
583
510
|
if (errorMsg.includes('ECONNREFUSED') || errorMsg.includes('Connection refused')) {
|
|
@@ -607,10 +534,12 @@ export const doctorCommand = new Command('doctor')
|
|
|
607
534
|
}
|
|
608
535
|
}
|
|
609
536
|
|
|
610
|
-
//
|
|
537
|
+
// 14. Check Besu2 network access
|
|
611
538
|
const besu2Check = ora('Checking Besu2 network (https://besu2.proyectos.codecrypto.academy)...').start();
|
|
612
539
|
try {
|
|
613
540
|
const rpcUrl = 'https://besu2.proyectos.codecrypto.academy';
|
|
541
|
+
|
|
542
|
+
// Verificar chain-id
|
|
614
543
|
const chainIdResponse = execSync(
|
|
615
544
|
`cast chain-id --rpc-url ${rpcUrl}`,
|
|
616
545
|
{ encoding: 'utf-8', stdio: 'pipe', timeout: 10000 }
|
|
@@ -623,11 +552,24 @@ export const doctorCommand = new Command('doctor')
|
|
|
623
552
|
chainId = parseInt(chainIdResponse, 10);
|
|
624
553
|
}
|
|
625
554
|
|
|
626
|
-
|
|
555
|
+
// Verificar que puede obtener el block number (confirma que la blockchain está operativa)
|
|
556
|
+
const blockNumberResponse = execSync(
|
|
557
|
+
`cast block-number --rpc-url ${rpcUrl}`,
|
|
558
|
+
{ encoding: 'utf-8', stdio: 'pipe', timeout: 10000 }
|
|
559
|
+
).trim();
|
|
560
|
+
|
|
561
|
+
let blockNumber: number;
|
|
562
|
+
if (blockNumberResponse.startsWith('0x') || blockNumberResponse.startsWith('0X')) {
|
|
563
|
+
blockNumber = parseInt(blockNumberResponse, 16);
|
|
564
|
+
} else {
|
|
565
|
+
blockNumber = parseInt(blockNumberResponse, 10);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
besu2Check.succeed(`Besu2 network accessible (chain-id: ${chainId}, block: ${blockNumber})`);
|
|
627
569
|
checks.push({
|
|
628
570
|
name: 'Besu2 Network',
|
|
629
571
|
status: 'success',
|
|
630
|
-
message: `Besu2 network accessible: ${rpcUrl} (chain-id: ${chainId})`
|
|
572
|
+
message: `Besu2 network accessible: ${rpcUrl} (chain-id: ${chainId}, block: ${blockNumber})`
|
|
631
573
|
});
|
|
632
574
|
} catch (error: any) {
|
|
633
575
|
const errorMsg = error.message || String(error);
|
package/src/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ import * as fs from 'fs';
|
|
|
5
5
|
import * as path from 'path';
|
|
6
6
|
import * as os from 'os';
|
|
7
7
|
import { deployCommand } from './commands/deploy';
|
|
8
|
+
import { deployMcpCommand } from './commands/deploy-mcp';
|
|
8
9
|
import { deployScCommand } from './commands/deploy-sc';
|
|
9
10
|
import { authCommand } from './commands/auth';
|
|
10
11
|
import { doctorCommand } from './commands/doctor';
|
|
@@ -53,6 +54,7 @@ program
|
|
|
53
54
|
|
|
54
55
|
// Registrar comandos
|
|
55
56
|
program.addCommand(deployCommand);
|
|
57
|
+
program.addCommand(deployMcpCommand);
|
|
56
58
|
program.addCommand(deployScCommand);
|
|
57
59
|
program.addCommand(authCommand);
|
|
58
60
|
program.addCommand(doctorCommand);
|