@deinossrl/dgp-agent 1.4.40 → 1.4.42
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/CHANGELOG.md +33 -0
- package/index.mjs +263 -5
- package/package.json +4 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Changelog - DGP Agent
|
|
2
|
+
|
|
3
|
+
## [1.4.42] - 2026-01-12
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- El agente ahora muestra su versión al iniciar
|
|
7
|
+
- Versión leída automáticamente desde package.json (no hardcoded)
|
|
8
|
+
- Versión visible en comando `dgp-agent status`
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- Mejorado display de información de versión en startup y status
|
|
12
|
+
|
|
13
|
+
## [1.4.41] - 2026-01-12
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
- Soporte completo para comandos `pg_dump` desde la plataforma web
|
|
17
|
+
- Integración con Supabase Storage para subir backups automáticamente
|
|
18
|
+
- Nueva dependencia: `@supabase/supabase-js` v2.39.0
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- Corregido schema de comandos: ahora busca en `dgp.agent_commands` correctamente
|
|
22
|
+
- Agregados headers `Accept-Profile` y `Content-Profile` para acceso al schema dgp
|
|
23
|
+
- URL de comandos actualizada de `dgp.agent_commands` a `agent_commands` con headers de schema
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
- Mejorado manejo de comandos con soporte para múltiples schemas en Supabase
|
|
27
|
+
|
|
28
|
+
## [1.4.40] - 2026-01-10
|
|
29
|
+
|
|
30
|
+
### Previous Release
|
|
31
|
+
- Funcionalidad base de reporte de estado Git
|
|
32
|
+
- Comandos deploy, git_commit_push, test_connection
|
|
33
|
+
- Integración con IA (Claude) para tareas automatizadas
|
package/index.mjs
CHANGED
|
@@ -25,9 +25,17 @@
|
|
|
25
25
|
|
|
26
26
|
import { execSync, spawn, spawnSync } from 'child_process';
|
|
27
27
|
import { hostname, homedir } from 'os';
|
|
28
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync, chmodSync, createWriteStream } from 'fs';
|
|
28
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, chmodSync, createWriteStream, statSync, unlinkSync } from 'fs';
|
|
29
29
|
import { join, dirname } from 'path';
|
|
30
|
+
import { fileURLToPath } from 'url';
|
|
30
31
|
import https from 'https';
|
|
32
|
+
import { createClient } from '@supabase/supabase-js';
|
|
33
|
+
|
|
34
|
+
// Get version from package.json
|
|
35
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
36
|
+
const __dirname = dirname(__filename);
|
|
37
|
+
const packageJson = JSON.parse(readFileSync(join(__dirname, 'package.json'), 'utf-8'));
|
|
38
|
+
const AGENT_VERSION = packageJson.version;
|
|
31
39
|
|
|
32
40
|
// ============================================
|
|
33
41
|
// CONFIG FILE MANAGEMENT
|
|
@@ -351,14 +359,12 @@ function getPlinkPath() {
|
|
|
351
359
|
}
|
|
352
360
|
}
|
|
353
361
|
|
|
354
|
-
// Versión del agente
|
|
355
|
-
const AGENT_VERSION = '1.4.40';
|
|
356
362
|
let AGENT_MODE = 'smart'; // Siempre inteligente
|
|
357
363
|
|
|
358
364
|
// Configuración (prioridad: env vars > archivo config > platform config > defaults)
|
|
359
365
|
const CONFIG = {
|
|
360
366
|
apiUrl: process.env.DGP_API_URL || fileConfig.apiUrl || 'https://asivayhbrqennwiwttds.supabase.co/functions/v1/dgp-agent-status',
|
|
361
|
-
commandsUrl: process.env.DGP_COMMANDS_URL || fileConfig.commandsUrl || 'https://asivayhbrqennwiwttds.supabase.co/rest/v1/
|
|
367
|
+
commandsUrl: process.env.DGP_COMMANDS_URL || fileConfig.commandsUrl || 'https://asivayhbrqennwiwttds.supabase.co/rest/v1/agent_commands',
|
|
362
368
|
supabaseKey: process.env.DGP_SUPABASE_KEY || fileConfig.supabaseKey || 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImFzaXZheWhicnFlbm53aXd0dGRzIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjczMDAwOTcsImV4cCI6MjA4Mjg3NjA5N30.s3a7dR-dPkEXI7B2lUTUXU69923hhuX6meheNeo5EKA',
|
|
363
369
|
interval: parseInt(process.env.DGP_INTERVAL || fileConfig.interval || '30', 10),
|
|
364
370
|
commandPollInterval: parseInt(process.env.DGP_COMMAND_POLL_INTERVAL || fileConfig.commandPollInterval || '10', 10),
|
|
@@ -783,6 +789,8 @@ async function runAIMode() {
|
|
|
783
789
|
headers: {
|
|
784
790
|
'apikey': CONFIG.supabaseKey,
|
|
785
791
|
'Authorization': `Bearer ${CONFIG.supabaseKey}`,
|
|
792
|
+
'Accept-Profile': 'dgp',
|
|
793
|
+
'Content-Profile': 'dgp',
|
|
786
794
|
},
|
|
787
795
|
});
|
|
788
796
|
|
|
@@ -1119,6 +1127,8 @@ async function getPendingCommands() {
|
|
|
1119
1127
|
headers: {
|
|
1120
1128
|
'apikey': CONFIG.supabaseKey,
|
|
1121
1129
|
'Authorization': `Bearer ${CONFIG.supabaseKey}`,
|
|
1130
|
+
'Accept-Profile': 'dgp',
|
|
1131
|
+
'Content-Profile': 'dgp',
|
|
1122
1132
|
},
|
|
1123
1133
|
});
|
|
1124
1134
|
|
|
@@ -1170,6 +1180,8 @@ async function updateCommandLogs(commandId, logs) {
|
|
|
1170
1180
|
'Authorization': `Bearer ${CONFIG.supabaseKey}`,
|
|
1171
1181
|
'Content-Type': 'application/json',
|
|
1172
1182
|
'Prefer': 'return=minimal',
|
|
1183
|
+
'Accept-Profile': 'dgp',
|
|
1184
|
+
'Content-Profile': 'dgp',
|
|
1173
1185
|
},
|
|
1174
1186
|
body: JSON.stringify({
|
|
1175
1187
|
result: { logs: logs },
|
|
@@ -1409,6 +1421,246 @@ async function executeDeploy(command) {
|
|
|
1409
1421
|
}
|
|
1410
1422
|
}
|
|
1411
1423
|
|
|
1424
|
+
/**
|
|
1425
|
+
* Ejecuta pg_dump para crear backup de PostgreSQL
|
|
1426
|
+
*/
|
|
1427
|
+
async function executePgDump(command) {
|
|
1428
|
+
const { id, params } = command;
|
|
1429
|
+
const backupId = params.backup_id;
|
|
1430
|
+
const environmentId = params.environment_id;
|
|
1431
|
+
|
|
1432
|
+
logCommand(`=== Ejecutando pg_dump (Backup) ===`);
|
|
1433
|
+
logInfo(`Backup ID: ${backupId}`);
|
|
1434
|
+
logInfo(`Environment ID: ${environmentId}`);
|
|
1435
|
+
|
|
1436
|
+
// Iniciar tracking de logs
|
|
1437
|
+
currentCommandId = id;
|
|
1438
|
+
commandLogs = [];
|
|
1439
|
+
|
|
1440
|
+
// Crear directorio para backups si no existe
|
|
1441
|
+
const backupsDir = join(CONFIG_DIR, 'backups');
|
|
1442
|
+
if (!existsSync(backupsDir)) {
|
|
1443
|
+
mkdirSync(backupsDir, { recursive: true });
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
let localFilePath = null;
|
|
1447
|
+
let storagePath = null;
|
|
1448
|
+
|
|
1449
|
+
try {
|
|
1450
|
+
await updateCommandStatus(id, 'running', {});
|
|
1451
|
+
await addCommandLog('info', 'Iniciando backup de base de datos...');
|
|
1452
|
+
|
|
1453
|
+
// Determinar extensión según formato
|
|
1454
|
+
const format = params.format || 'custom';
|
|
1455
|
+
const extension = format === 'custom' ? 'dump' : format === 'tar' ? 'tar' : 'sql';
|
|
1456
|
+
|
|
1457
|
+
// Generar nombre de archivo local
|
|
1458
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5);
|
|
1459
|
+
const fileName = `backup_${environmentId}_${timestamp}.${extension}`;
|
|
1460
|
+
localFilePath = join(backupsDir, fileName);
|
|
1461
|
+
|
|
1462
|
+
// Construir comando pg_dump
|
|
1463
|
+
let pgDumpCmd = 'pg_dump';
|
|
1464
|
+
const pgDumpArgs = [];
|
|
1465
|
+
|
|
1466
|
+
// Configurar conexión según tipo
|
|
1467
|
+
if (params.connection_type === 'postgresql') {
|
|
1468
|
+
// PostgreSQL genérico
|
|
1469
|
+
pgDumpArgs.push(`--host=${params.host}`);
|
|
1470
|
+
pgDumpArgs.push(`--port=${params.port || 5432}`);
|
|
1471
|
+
pgDumpArgs.push(`--username=${params.username}`);
|
|
1472
|
+
pgDumpArgs.push(`--dbname=${params.database}`);
|
|
1473
|
+
|
|
1474
|
+
// Si hay password, configurar PGPASSWORD
|
|
1475
|
+
if (params.password_encrypted) {
|
|
1476
|
+
// TODO: Desencriptar password si está encriptado
|
|
1477
|
+
process.env.PGPASSWORD = params.password_encrypted;
|
|
1478
|
+
}
|
|
1479
|
+
} else {
|
|
1480
|
+
// Supabase
|
|
1481
|
+
const projectRef = params.project_ref;
|
|
1482
|
+
pgDumpArgs.push(`--host=db.${projectRef}.supabase.co`);
|
|
1483
|
+
pgDumpArgs.push(`--port=5432`);
|
|
1484
|
+
pgDumpArgs.push(`--username=postgres`);
|
|
1485
|
+
pgDumpArgs.push(`--dbname=postgres`);
|
|
1486
|
+
|
|
1487
|
+
// Para Supabase, necesitamos el service_role_key como password
|
|
1488
|
+
if (params.service_role_key) {
|
|
1489
|
+
process.env.PGPASSWORD = params.service_role_key;
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
// Opciones de formato
|
|
1494
|
+
if (format === 'custom') {
|
|
1495
|
+
pgDumpArgs.push('--format=custom');
|
|
1496
|
+
} else if (format === 'tar') {
|
|
1497
|
+
pgDumpArgs.push('--format=tar');
|
|
1498
|
+
} else {
|
|
1499
|
+
pgDumpArgs.push('--format=plain');
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
// Opciones de contenido
|
|
1503
|
+
if (params.backup_type === 'schema') {
|
|
1504
|
+
pgDumpArgs.push('--schema-only');
|
|
1505
|
+
} else if (params.backup_type === 'data') {
|
|
1506
|
+
pgDumpArgs.push('--data-only');
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
// Incluir opciones adicionales
|
|
1510
|
+
if (params.include_privileges) {
|
|
1511
|
+
pgDumpArgs.push('--no-privileges');
|
|
1512
|
+
}
|
|
1513
|
+
if (params.include_owner) {
|
|
1514
|
+
pgDumpArgs.push('--no-owner');
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
// Schemas específicos
|
|
1518
|
+
if (params.schemas && params.schemas.length > 0) {
|
|
1519
|
+
params.schemas.forEach(schema => {
|
|
1520
|
+
pgDumpArgs.push(`--schema=${schema}`);
|
|
1521
|
+
});
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
// Tablas específicas
|
|
1525
|
+
if (params.tables && params.tables.length > 0) {
|
|
1526
|
+
params.tables.forEach(table => {
|
|
1527
|
+
pgDumpArgs.push(`--table=${table}`);
|
|
1528
|
+
});
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
// Archivo de salida
|
|
1532
|
+
pgDumpArgs.push(`--file=${localFilePath}`);
|
|
1533
|
+
|
|
1534
|
+
// Ejecutar pg_dump
|
|
1535
|
+
const fullCommand = `${pgDumpCmd} ${pgDumpArgs.join(' ')}`;
|
|
1536
|
+
logCommand(`Ejecutando: pg_dump [conexión oculta]`);
|
|
1537
|
+
await addCommandLog('command', 'Ejecutando pg_dump...');
|
|
1538
|
+
|
|
1539
|
+
await shellAsync(fullCommand);
|
|
1540
|
+
|
|
1541
|
+
// Verificar que el archivo se creó
|
|
1542
|
+
if (!existsSync(localFilePath)) {
|
|
1543
|
+
throw new Error('El archivo de backup no se generó correctamente');
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
const fileSize = statSync(localFilePath).size;
|
|
1547
|
+
logSuccess(`Backup creado localmente (${(fileSize / 1024 / 1024).toFixed(2)} MB)`);
|
|
1548
|
+
await addCommandLog('success', `Backup creado: ${(fileSize / 1024 / 1024).toFixed(2)} MB`);
|
|
1549
|
+
|
|
1550
|
+
// Subir a Supabase Storage
|
|
1551
|
+
logInfo('Subiendo backup a Supabase Storage...');
|
|
1552
|
+
await addCommandLog('info', 'Subiendo a Storage...');
|
|
1553
|
+
|
|
1554
|
+
const supabase = createClient(
|
|
1555
|
+
CONFIG.apiUrl.replace('/functions/v1/dgp-agent-status', ''),
|
|
1556
|
+
CONFIG.supabaseKey
|
|
1557
|
+
);
|
|
1558
|
+
|
|
1559
|
+
// Usar storage_path de los params (ya definido en la DB)
|
|
1560
|
+
storagePath = params.storage_path;
|
|
1561
|
+
if (!storagePath) {
|
|
1562
|
+
throw new Error('storage_path no definido en los parámetros');
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
// Leer archivo y subirlo
|
|
1566
|
+
const fileBuffer = readFileSync(localFilePath);
|
|
1567
|
+
const { data: uploadData, error: uploadError } = await supabase.storage
|
|
1568
|
+
.from('database-backups')
|
|
1569
|
+
.upload(storagePath, fileBuffer, {
|
|
1570
|
+
contentType: format === 'sql' ? 'application/sql' : 'application/octet-stream',
|
|
1571
|
+
upsert: false
|
|
1572
|
+
});
|
|
1573
|
+
|
|
1574
|
+
if (uploadError) {
|
|
1575
|
+
throw new Error(`Error subiendo a Storage: ${uploadError.message}`);
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
logSuccess(`Archivo subido a Storage: ${storagePath}`);
|
|
1579
|
+
await addCommandLog('success', `Subido a: ${storagePath}`);
|
|
1580
|
+
|
|
1581
|
+
// Actualizar registro en db_backups
|
|
1582
|
+
logInfo('Actualizando registro de backup...');
|
|
1583
|
+
const { error: updateError } = await supabase
|
|
1584
|
+
.schema('dgp')
|
|
1585
|
+
.from('db_backups')
|
|
1586
|
+
.update({
|
|
1587
|
+
status: 'completed',
|
|
1588
|
+
storage_path: storagePath,
|
|
1589
|
+
size_bytes: fileSize,
|
|
1590
|
+
completed_at: new Date().toISOString()
|
|
1591
|
+
})
|
|
1592
|
+
.eq('id', backupId);
|
|
1593
|
+
|
|
1594
|
+
if (updateError) {
|
|
1595
|
+
logError(`Error actualizando registro: ${updateError.message}`);
|
|
1596
|
+
await addCommandLog('error', `Error actualizando DB: ${updateError.message}`);
|
|
1597
|
+
} else {
|
|
1598
|
+
logSuccess('Registro de backup actualizado');
|
|
1599
|
+
await addCommandLog('success', 'Backup registrado en DB');
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
// Limpiar archivo local (opcional)
|
|
1603
|
+
try {
|
|
1604
|
+
unlinkSync(localFilePath);
|
|
1605
|
+
logInfo('Archivo local eliminado');
|
|
1606
|
+
} catch (e) {
|
|
1607
|
+
// Ignorar errores al eliminar
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
// Marcar comando como completado
|
|
1611
|
+
await updateCommandStatus(id, 'completed', {
|
|
1612
|
+
backup_id: backupId,
|
|
1613
|
+
storage_path: storagePath,
|
|
1614
|
+
size_bytes: fileSize,
|
|
1615
|
+
local_path: localFilePath
|
|
1616
|
+
});
|
|
1617
|
+
|
|
1618
|
+
console.log('');
|
|
1619
|
+
logSuccess(`=== Backup completado exitosamente ===`);
|
|
1620
|
+
console.log('');
|
|
1621
|
+
|
|
1622
|
+
return { success: true };
|
|
1623
|
+
|
|
1624
|
+
} catch (error) {
|
|
1625
|
+
logError(`Backup failed: ${error.message}`);
|
|
1626
|
+
await addCommandLog('error', error.message);
|
|
1627
|
+
|
|
1628
|
+
// Limpiar archivo local si existe
|
|
1629
|
+
if (localFilePath && existsSync(localFilePath)) {
|
|
1630
|
+
try {
|
|
1631
|
+
unlinkSync(localFilePath);
|
|
1632
|
+
} catch (e) {
|
|
1633
|
+
// Ignorar
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
// Actualizar estado del backup en DB
|
|
1638
|
+
try {
|
|
1639
|
+
const supabase = createClient(
|
|
1640
|
+
CONFIG.apiUrl.replace('/functions/v1/dgp-agent-status', ''),
|
|
1641
|
+
CONFIG.supabaseKey
|
|
1642
|
+
);
|
|
1643
|
+
|
|
1644
|
+
await supabase
|
|
1645
|
+
.schema('dgp')
|
|
1646
|
+
.from('db_backups')
|
|
1647
|
+
.update({
|
|
1648
|
+
status: 'failed',
|
|
1649
|
+
error_message: error.message
|
|
1650
|
+
})
|
|
1651
|
+
.eq('id', backupId);
|
|
1652
|
+
} catch (e) {
|
|
1653
|
+
// Ignorar errores al actualizar
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
await updateCommandStatus(id, 'failed', {}, error.message);
|
|
1657
|
+
return { success: false, error: error.message };
|
|
1658
|
+
} finally {
|
|
1659
|
+
// Limpiar variables de entorno
|
|
1660
|
+
delete process.env.PGPASSWORD;
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1412
1664
|
/**
|
|
1413
1665
|
* Ejecuta un comando recibido
|
|
1414
1666
|
* @param {Object} command - El comando a ejecutar
|
|
@@ -1432,6 +1684,9 @@ async function executeCommand(command, useAI = false) {
|
|
|
1432
1684
|
case 'test_connection':
|
|
1433
1685
|
return await executeTestConnection(command);
|
|
1434
1686
|
|
|
1687
|
+
case 'pg_dump':
|
|
1688
|
+
return await executePgDump(command);
|
|
1689
|
+
|
|
1435
1690
|
case 'rollback':
|
|
1436
1691
|
logError('Rollback not implemented yet');
|
|
1437
1692
|
await updateCommandStatus(command.id, 'failed', {}, 'Rollback not implemented');
|
|
@@ -1874,7 +2129,7 @@ async function executeGitCommitPush(command, useAI = false) {
|
|
|
1874
2129
|
function printStatus(status) {
|
|
1875
2130
|
console.log('');
|
|
1876
2131
|
console.log(`${colors.blue}═══════════════════════════════════════════════════════${colors.reset}`);
|
|
1877
|
-
console.log(`${colors.blue} DGP Agent Status${colors.reset}`);
|
|
2132
|
+
console.log(`${colors.blue} DGP Agent Status v${AGENT_VERSION}${colors.reset}`);
|
|
1878
2133
|
console.log(`${colors.blue}═══════════════════════════════════════════════════════${colors.reset}`);
|
|
1879
2134
|
console.log(` Machine: ${colors.yellow}${CONFIG.machineId}${colors.reset}`);
|
|
1880
2135
|
console.log(` Branch: ${colors.green}${status.branch}${colors.reset}`);
|
|
@@ -1927,6 +2182,7 @@ async function runAgent() {
|
|
|
1927
2182
|
}
|
|
1928
2183
|
}
|
|
1929
2184
|
|
|
2185
|
+
logInfo(`DGP Agent v${AGENT_VERSION}`);
|
|
1930
2186
|
logInfo(`Machine ID: ${CONFIG.machineId}`);
|
|
1931
2187
|
logInfo(`IA: ${CONFIG.anthropicApiKey ? '✓ Habilitada' : '✗ No configurada'}`);
|
|
1932
2188
|
logInfo(`Reporta cada ${CONFIG.interval}s | Escucha cada ${CONFIG.commandPollInterval}s`);
|
|
@@ -1962,6 +2218,8 @@ async function runAgent() {
|
|
|
1962
2218
|
headers: {
|
|
1963
2219
|
'apikey': CONFIG.supabaseKey,
|
|
1964
2220
|
'Authorization': `Bearer ${CONFIG.supabaseKey}`,
|
|
2221
|
+
'Accept-Profile': 'dgp',
|
|
2222
|
+
'Content-Profile': 'dgp',
|
|
1965
2223
|
},
|
|
1966
2224
|
});
|
|
1967
2225
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deinossrl/dgp-agent",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.42",
|
|
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": {
|
|
@@ -29,5 +29,8 @@
|
|
|
29
29
|
"homepage": "https://github.com/DEINOS-SRL/tenminuteia#readme",
|
|
30
30
|
"engines": {
|
|
31
31
|
"node": ">=18.0.0"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@supabase/supabase-js": "^2.39.0"
|
|
32
35
|
}
|
|
33
36
|
}
|