@deinossrl/dgp-agent 1.4.59 → 1.5.0

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 (3) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/index.mjs +163 -5
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog - DGP Agent
2
2
 
3
+ ## [1.5.0] - 2026-01-12
4
+
5
+ ### Added
6
+ - **Auto-creación de schemas en restore**: Detecta automáticamente qué schemas necesita el backup y los crea antes de restaurar
7
+ - Agregado `psql.exe` a los binarios instalados (pg_dump, pg_restore, psql)
8
+ - Detección robusta de schemas con 3 métodos:
9
+ 1. `pg_restore --list` con múltiples patrones de búsqueda
10
+ 2. Análisis directo del contenido del backup si --list falla
11
+ 3. Fallback a parsing manual del archivo
12
+ - Manejo inteligente de errores al crear schemas (ya existe, sin permisos, etc.)
13
+
14
+ ### Changed
15
+ - Logs más detallados durante detección y creación de schemas
16
+ - Mejor manejo de errores críticos vs warnings
17
+
18
+ ### Fixed
19
+ - Restauraciones personalizadas ahora funcionan correctamente sin necesidad de crear schemas manualmente
20
+ - Resuelve el problema de "schema does not exist" en backups selectivos
21
+
3
22
  ## [1.4.59] - 2026-01-12
4
23
 
5
24
  ### Added
package/index.mjs CHANGED
@@ -1532,16 +1532,21 @@ async function findOrInstallPgDump() {
1532
1532
  // Copiar pg_dump.exe, pg_restore.exe y TODAS las DLLs al BIN_DIR raíz
1533
1533
  const binSrcDir = join(BIN_DIR, 'pgsql', 'bin');
1534
1534
 
1535
- // Copiar pg_dump.exe
1535
+ // Copiar pg_dump.exe, pg_restore.exe y psql.exe
1536
1536
  execSync(`copy "${extractedPgDump}" "${localPgDump}"`, { shell: 'cmd.exe' });
1537
1537
 
1538
- // Copiar pg_restore.exe
1539
1538
  const extractedPgRestore = join(BIN_DIR, 'pgsql', 'bin', 'pg_restore.exe');
1540
1539
  const localPgRestore = join(BIN_DIR, 'pg_restore.exe');
1541
1540
  if (existsSync(extractedPgRestore)) {
1542
1541
  execSync(`copy "${extractedPgRestore}" "${localPgRestore}"`, { shell: 'cmd.exe' });
1543
1542
  }
1544
1543
 
1544
+ const extractedPsql = join(BIN_DIR, 'pgsql', 'bin', 'psql.exe');
1545
+ const localPsql = join(BIN_DIR, 'psql.exe');
1546
+ if (existsSync(extractedPsql)) {
1547
+ execSync(`copy "${extractedPsql}" "${localPsql}"`, { shell: 'cmd.exe' });
1548
+ }
1549
+
1545
1550
  // Copiar TODAS las DLLs (no solo algunas específicas)
1546
1551
  execSync(`copy "${binSrcDir}\\*.dll" "${BIN_DIR}\\"`, { shell: 'cmd.exe' });
1547
1552
 
@@ -1582,16 +1587,21 @@ async function findOrInstallPgDump() {
1582
1587
  if (existsSync(extractedPgDump)) {
1583
1588
  const binSrcDir = join(BIN_DIR, 'pgsql', 'bin');
1584
1589
 
1585
- // Copiar pg_dump.exe
1590
+ // Copiar pg_dump.exe, pg_restore.exe y psql.exe
1586
1591
  execSync(`copy "${extractedPgDump}" "${localPgDump}"`, { shell: 'cmd.exe' });
1587
1592
 
1588
- // Copiar pg_restore.exe
1589
1593
  const extractedPgRestore = join(BIN_DIR, 'pgsql', 'bin', 'pg_restore.exe');
1590
1594
  const localPgRestore = join(BIN_DIR, 'pg_restore.exe');
1591
1595
  if (existsSync(extractedPgRestore)) {
1592
1596
  execSync(`copy "${extractedPgRestore}" "${localPgRestore}"`, { shell: 'cmd.exe' });
1593
1597
  }
1594
1598
 
1599
+ const extractedPsql = join(BIN_DIR, 'pgsql', 'bin', 'psql.exe');
1600
+ const localPsql = join(BIN_DIR, 'psql.exe');
1601
+ if (existsSync(extractedPsql)) {
1602
+ execSync(`copy "${extractedPsql}" "${localPsql}"`, { shell: 'cmd.exe' });
1603
+ }
1604
+
1595
1605
  // Copiar TODAS las DLLs
1596
1606
  execSync(`copy "${binSrcDir}\\*.dll" "${BIN_DIR}\\"`, { shell: 'cmd.exe' });
1597
1607
 
@@ -1958,7 +1968,155 @@ async function executePgRestore(command) {
1958
1968
  .update({ status: 'running', started_at: new Date().toISOString() })
1959
1969
  .eq('id', restore_id);
1960
1970
 
1961
- // Construir argumentos de pg_restore
1971
+ // PASO 1: Detectar y crear schemas necesarios antes de restaurar
1972
+ logInfo('Analizando backup para detectar schemas...');
1973
+ await addCommandLog('info', 'Detectando schemas del backup...');
1974
+
1975
+ const schemas = new Set();
1976
+
1977
+ try {
1978
+ // Usar pg_restore --list para ver el contenido del backup
1979
+ const listResult = spawnSync(pgRestorePath, ['--list', localFilePath], {
1980
+ env: process.env,
1981
+ encoding: 'utf-8',
1982
+ stdio: 'pipe',
1983
+ });
1984
+
1985
+ if (listResult.status === 0 && listResult.stdout) {
1986
+ const listOutput = listResult.stdout;
1987
+
1988
+ // Método 1: Buscar líneas con "SCHEMA - nombre"
1989
+ const schemaLines = listOutput.match(/^\s*\d+;\s*\d+\s+\d+\s+SCHEMA\s+-\s+(\w+)/gm) || [];
1990
+ schemaLines.forEach(line => {
1991
+ const match = line.match(/SCHEMA\s+-\s+(\w+)/);
1992
+ if (match && match[1] && match[1] !== 'public' && match[1] !== '-') {
1993
+ schemas.add(match[1]);
1994
+ }
1995
+ });
1996
+
1997
+ // Método 2: Buscar referencias a schemas en tablas (ej: "TABLE - crm.quotes")
1998
+ const tableLines = listOutput.match(/TABLE\s+-\s+(\w+)\./g) || [];
1999
+ tableLines.forEach(line => {
2000
+ const match = line.match(/TABLE\s+-\s+(\w+)\./);
2001
+ if (match && match[1] && match[1] !== 'public') {
2002
+ schemas.add(match[1]);
2003
+ }
2004
+ });
2005
+
2006
+ // Método 3: Buscar "SCHEMA COMMENT" o "ACL - SCHEMA"
2007
+ const schemaRefs = listOutput.match(/(?:SCHEMA COMMENT|ACL - SCHEMA)\s+-\s+(\w+)/g) || [];
2008
+ schemaRefs.forEach(line => {
2009
+ const match = line.match(/(?:SCHEMA COMMENT|ACL - SCHEMA)\s+-\s+(\w+)/);
2010
+ if (match && match[1] && match[1] !== 'public') {
2011
+ schemas.add(match[1]);
2012
+ }
2013
+ });
2014
+
2015
+ logInfo(`pg_restore --list ejecutado correctamente`);
2016
+ } else {
2017
+ logInfo(`pg_restore --list no devolvió output, buscando en contenido del backup...`);
2018
+ }
2019
+
2020
+ // Si no encontramos schemas con --list, parsear el archivo directamente
2021
+ if (schemas.size === 0) {
2022
+ try {
2023
+ const backupContent = readFileSync(localFilePath, 'utf-8').substring(0, 500000); // Primeros 500KB
2024
+
2025
+ // Buscar patrones "schema.table" o "CREATE TABLE schema."
2026
+ const schemaPatterns = [
2027
+ /CREATE TABLE\s+(\w+)\./gi,
2028
+ /INSERT INTO\s+(\w+)\./gi,
2029
+ /ALTER TABLE\s+(\w+)\./gi,
2030
+ /DROP.*ON\s+(\w+)\./gi,
2031
+ ];
2032
+
2033
+ schemaPatterns.forEach(pattern => {
2034
+ const matches = backupContent.matchAll(pattern);
2035
+ for (const match of matches) {
2036
+ if (match[1] && match[1] !== 'public' && match[1] !== 'pg_catalog') {
2037
+ schemas.add(match[1]);
2038
+ }
2039
+ }
2040
+ });
2041
+
2042
+ logInfo('Schemas detectados por análisis directo del backup');
2043
+ } catch (parseError) {
2044
+ logInfo(`No se pudo parsear directamente: ${parseError.message}`);
2045
+ }
2046
+ }
2047
+
2048
+ } catch (listError) {
2049
+ logInfo(`Error en detección de schemas: ${listError.message}`);
2050
+ }
2051
+
2052
+ // Crear los schemas detectados
2053
+ if (schemas.size > 0) {
2054
+ logInfo(`Schemas detectados: ${Array.from(schemas).join(', ')}`);
2055
+ await addCommandLog('info', `Creando schemas: ${Array.from(schemas).join(', ')}`);
2056
+
2057
+ const psqlPath = pgRestorePath.replace('pg_restore', 'psql');
2058
+ const psqlExe = psqlPath.replace('.exe', '') + '.exe';
2059
+
2060
+ // Verificar que psql existe
2061
+ if (!existsSync(psqlExe)) {
2062
+ logInfo('psql no disponible, schemas deben existir previamente');
2063
+ } else {
2064
+ // Crear cada schema
2065
+ for (const schemaName of schemas) {
2066
+ try {
2067
+ const createSchemaSQL = `CREATE SCHEMA IF NOT EXISTS "${schemaName}";`;
2068
+ const psqlArgs = [];
2069
+
2070
+ // Conexión (misma que pg_restore)
2071
+ if (target_environment.connection_type === 'supabase') {
2072
+ const projectRef = target_environment.supabase_project_ref;
2073
+ psqlArgs.push(`--host=db.${projectRef}.supabase.co`);
2074
+ psqlArgs.push(`--port=5432`);
2075
+ psqlArgs.push(`--username=postgres`);
2076
+ psqlArgs.push(`--dbname=postgres`);
2077
+ } else if (target_environment.connection_type === 'postgresql') {
2078
+ psqlArgs.push(`--host=${target_environment.db_host}`);
2079
+ psqlArgs.push(`--port=${target_environment.db_port || 5432}`);
2080
+ psqlArgs.push(`--username=${target_environment.db_user}`);
2081
+ psqlArgs.push(`--dbname=${target_environment.db_name}`);
2082
+ }
2083
+
2084
+ psqlArgs.push('--command', createSchemaSQL);
2085
+ psqlArgs.push('--no-psqlrc'); // No cargar archivos de configuración
2086
+ psqlArgs.push('--quiet'); // Silencioso
2087
+
2088
+ logInfo(`Creando schema: ${schemaName}`);
2089
+
2090
+ const createResult = spawnSync(psqlExe, psqlArgs, {
2091
+ env: process.env,
2092
+ encoding: 'utf-8',
2093
+ stdio: 'pipe',
2094
+ });
2095
+
2096
+ if (createResult.status === 0) {
2097
+ logSuccess(`✓ Schema "${schemaName}" creado`);
2098
+ } else {
2099
+ const stderr = createResult.stderr || '';
2100
+ if (stderr.includes('already exists')) {
2101
+ logInfo(`✓ Schema "${schemaName}" ya existe`);
2102
+ } else if (stderr.includes('permission denied')) {
2103
+ logError(`✗ Sin permisos para crear schema "${schemaName}"`);
2104
+ throw new Error(`No hay permisos para crear el schema "${schemaName}"`);
2105
+ } else {
2106
+ logInfo(`⚠ Schema "${schemaName}": ${stderr.substring(0, 200)}`);
2107
+ }
2108
+ }
2109
+ } catch (schemaError) {
2110
+ logError(`Error creando schema "${schemaName}": ${schemaError.message}`);
2111
+ throw schemaError;
2112
+ }
2113
+ }
2114
+ }
2115
+ } else {
2116
+ logInfo('No se detectaron schemas personalizados (solo public)');
2117
+ }
2118
+
2119
+ // PASO 2: Construir argumentos de pg_restore
1962
2120
  const pgRestoreArgs = [];
1963
2121
 
1964
2122
  // Conexión
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deinossrl/dgp-agent",
3
- "version": "1.4.59",
3
+ "version": "1.5.0",
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": {