@deinossrl/dgp-agent 1.4.39 → 1.4.40

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 (2) hide show
  1. package/index.mjs +64 -9
  2. package/package.json +1 -1
package/index.mjs CHANGED
@@ -352,7 +352,7 @@ function getPlinkPath() {
352
352
  }
353
353
 
354
354
  // Versión del agente
355
- const AGENT_VERSION = '1.4.39';
355
+ const AGENT_VERSION = '1.4.40';
356
356
  let AGENT_MODE = 'smart'; // Siempre inteligente
357
357
 
358
358
  // Configuración (prioridad: env vars > archivo config > platform config > defaults)
@@ -887,6 +887,61 @@ function shell(command, options = {}) {
887
887
  // Alias para compatibilidad
888
888
  const shellSync = shell;
889
889
 
890
+ /**
891
+ * Verifica si un error es recuperable (timeout, conexión, etc)
892
+ */
893
+ function isRecoverableError(error) {
894
+ const msg = error.message.toLowerCase();
895
+ return (
896
+ msg.includes('timeout') ||
897
+ msg.includes('timed out') ||
898
+ msg.includes('connection refused') ||
899
+ msg.includes('network') ||
900
+ msg.includes('temporarily unavailable') ||
901
+ (error.code === 255 && msg.includes('ssh'))
902
+ );
903
+ }
904
+
905
+ /**
906
+ * Ejecuta un comando shell de forma asíncrona con reintentos automáticos
907
+ */
908
+ async function shellAsyncWithRetry(command, options = {}, maxRetries = 3, baseDelay = 2000) {
909
+ let lastError;
910
+
911
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
912
+ try {
913
+ logInfo(`[Attempt ${attempt}/${maxRetries}] Executing: ${command.substring(0, 80)}...`);
914
+ const result = await shellAsync(command, options);
915
+ if (attempt > 1) {
916
+ logSuccess(`✓ Command succeeded after ${attempt} attempts`);
917
+ }
918
+ return result;
919
+ } catch (error) {
920
+ lastError = error;
921
+
922
+ // Si no es recuperable, fallar inmediatamente
923
+ if (!isRecoverableError(error)) {
924
+ throw error;
925
+ }
926
+
927
+ // Si es el último intento, fallar
928
+ if (attempt === maxRetries) {
929
+ logError(`✗ Command failed after ${maxRetries} attempts`);
930
+ throw error;
931
+ }
932
+
933
+ // Calcular delay con backoff exponencial
934
+ const delay = baseDelay * Math.pow(2, attempt - 1);
935
+ log(`⚠️ Recoverable error (${error.message}), retrying in ${delay}ms...`, 'yellow');
936
+
937
+ // Esperar antes de reintentar
938
+ await new Promise(resolve => setTimeout(resolve, delay));
939
+ }
940
+ }
941
+
942
+ throw lastError;
943
+ }
944
+
890
945
  /**
891
946
  * Ejecuta un comando shell de forma asíncrona con output en tiempo real
892
947
  */
@@ -1225,10 +1280,10 @@ async function executeDeploy(command) {
1225
1280
  ? '/var/www/tenminuteia-prod/'
1226
1281
  : '/var/www/tenminuteia-staging/');
1227
1282
 
1228
- // Construir base del comando SSH
1283
+ // Construir base del comando SSH con timeout largo para conexiones lentas
1229
1284
  const sshBase = sshKeyPath
1230
- ? `ssh -i ${sshKeyPath} -o StrictHostKeyChecking=no ${ssh_user}@${server_host}`
1231
- : `ssh ${ssh_user}@${server_host}`;
1285
+ ? `ssh -i ${sshKeyPath} -o StrictHostKeyChecking=no -o ConnectTimeout=60 -o ServerAliveInterval=10 ${ssh_user}@${server_host}`
1286
+ : `ssh -o ConnectTimeout=60 -o ServerAliveInterval=10 ${ssh_user}@${server_host}`;
1232
1287
 
1233
1288
  // Step 1: Git clone/pull on server
1234
1289
  currentStep = 'git_setup_server';
@@ -1240,7 +1295,7 @@ async function executeDeploy(command) {
1240
1295
  try {
1241
1296
  logInfo(`Attempting to update existing repository...`);
1242
1297
  const gitPullCmd = `${sshBase} "cd ${projectFolder} && git fetch origin && git checkout ${branch} && git pull origin ${branch}"`;
1243
- await shellAsync(gitPullCmd);
1298
+ await shellAsyncWithRetry(gitPullCmd, {}, 5, 3000); // 5 reintentos, 3s base delay
1244
1299
  logSuccess(`Code updated on server`);
1245
1300
  } catch (pullError) {
1246
1301
  // Si falla el pull, probablemente el repo no existe
@@ -1260,7 +1315,7 @@ async function executeDeploy(command) {
1260
1315
  // Crear directorio padre y clonar
1261
1316
  const parentDir = projectFolder.substring(0, projectFolder.lastIndexOf('/'));
1262
1317
  const cloneCmd = `${sshBase} "mkdir -p ${parentDir} && git clone ${repoUrl} ${projectFolder} && cd ${projectFolder} && git checkout ${branch}"`;
1263
- await shellAsync(cloneCmd);
1318
+ await shellAsyncWithRetry(cloneCmd, {}, 5, 3000); // 5 reintentos, 3s base delay
1264
1319
 
1265
1320
  logSuccess(`Repository cloned successfully`);
1266
1321
  }
@@ -1273,7 +1328,7 @@ async function executeDeploy(command) {
1273
1328
  steps.push({ step: currentStep, status: 'running' });
1274
1329
 
1275
1330
  const npmInstallCmd = `${sshBase} "cd ${projectFolder} && npm ci"`;
1276
- await shellAsync(npmInstallCmd);
1331
+ await shellAsyncWithRetry(npmInstallCmd, {}, 3, 2000); // 3 reintentos, 2s base delay
1277
1332
 
1278
1333
  steps[steps.length - 1].status = 'success';
1279
1334
  logSuccess(`Dependencies installed on server`);
@@ -1284,7 +1339,7 @@ async function executeDeploy(command) {
1284
1339
  steps.push({ step: currentStep, status: 'running' });
1285
1340
 
1286
1341
  const npmBuildCmd = `${sshBase} "cd ${projectFolder} && npm run build"`;
1287
- await shellAsync(npmBuildCmd);
1342
+ await shellAsyncWithRetry(npmBuildCmd, {}, 3, 2000); // 3 reintentos, 2s base delay
1288
1343
 
1289
1344
  steps[steps.length - 1].status = 'success';
1290
1345
  logSuccess(`Build completed on server`);
@@ -1295,7 +1350,7 @@ async function executeDeploy(command) {
1295
1350
  steps.push({ step: currentStep, status: 'running' });
1296
1351
 
1297
1352
  const reloadNginxCmd = `${sshBase} "sudo nginx -t && sudo systemctl reload nginx"`;
1298
- await shellAsync(reloadNginxCmd);
1353
+ await shellAsyncWithRetry(reloadNginxCmd, {}, 3, 2000); // 3 reintentos, 2s base delay
1299
1354
 
1300
1355
  steps[steps.length - 1].status = 'success';
1301
1356
  logSuccess(`Nginx reloaded`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deinossrl/dgp-agent",
3
- "version": "1.4.39",
3
+ "version": "1.4.40",
4
4
  "description": "Agente local para Despliegue-GPT - Reporta el estado del repositorio Git a la plataforma TenMinute IA",
5
5
  "main": "index.mjs",
6
6
  "bin": {