@deinossrl/dgp-agent 1.2.7 → 1.4.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.
- package/index.mjs +508 -13
- package/package.json +1 -1
package/index.mjs
CHANGED
|
@@ -24,17 +24,89 @@
|
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
import { execSync, spawn } from 'child_process';
|
|
27
|
-
import { hostname } from 'os';
|
|
27
|
+
import { hostname, homedir } from 'os';
|
|
28
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
29
|
+
import { join, dirname } from 'path';
|
|
28
30
|
|
|
29
|
-
//
|
|
31
|
+
// ============================================
|
|
32
|
+
// CONFIG FILE MANAGEMENT
|
|
33
|
+
// ============================================
|
|
34
|
+
|
|
35
|
+
const CONFIG_DIR = join(homedir(), '.dgp-agent');
|
|
36
|
+
const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Lee la configuración del archivo
|
|
40
|
+
*/
|
|
41
|
+
function loadConfigFile() {
|
|
42
|
+
try {
|
|
43
|
+
if (existsSync(CONFIG_FILE)) {
|
|
44
|
+
const content = readFileSync(CONFIG_FILE, 'utf-8');
|
|
45
|
+
return JSON.parse(content);
|
|
46
|
+
}
|
|
47
|
+
} catch (e) {
|
|
48
|
+
// Ignore errors, return empty config
|
|
49
|
+
}
|
|
50
|
+
return {};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Guarda la configuración en el archivo
|
|
55
|
+
*/
|
|
56
|
+
function saveConfigFile(config) {
|
|
57
|
+
try {
|
|
58
|
+
if (!existsSync(CONFIG_DIR)) {
|
|
59
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
60
|
+
}
|
|
61
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), 'utf-8');
|
|
62
|
+
return true;
|
|
63
|
+
} catch (e) {
|
|
64
|
+
console.error(`Error saving config: ${e.message}`);
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Obtiene un valor de la configuración
|
|
71
|
+
*/
|
|
72
|
+
function getConfigValue(key) {
|
|
73
|
+
const config = loadConfigFile();
|
|
74
|
+
return config[key];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Establece un valor en la configuración
|
|
79
|
+
*/
|
|
80
|
+
function setConfigValue(key, value) {
|
|
81
|
+
const config = loadConfigFile();
|
|
82
|
+
config[key] = value;
|
|
83
|
+
return saveConfigFile(config);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Elimina un valor de la configuración
|
|
88
|
+
*/
|
|
89
|
+
function deleteConfigValue(key) {
|
|
90
|
+
const config = loadConfigFile();
|
|
91
|
+
delete config[key];
|
|
92
|
+
return saveConfigFile(config);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Cargar config del archivo
|
|
96
|
+
const fileConfig = loadConfigFile();
|
|
97
|
+
|
|
98
|
+
// Configuración (prioridad: env vars > archivo config > defaults)
|
|
30
99
|
const CONFIG = {
|
|
31
|
-
apiUrl: process.env.DGP_API_URL || 'https://asivayhbrqennwiwttds.supabase.co/functions/v1/dgp-agent-status',
|
|
32
|
-
commandsUrl: process.env.DGP_COMMANDS_URL || 'https://asivayhbrqennwiwttds.supabase.co/rest/v1/dgp_agent_commands',
|
|
33
|
-
supabaseKey: process.env.DGP_SUPABASE_KEY || 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImFzaXZheWhicnFlbm53aXd0dGRzIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjczMDAwOTcsImV4cCI6MjA4Mjg3NjA5N30.s3a7dR-dPkEXI7B2lUTUXU69923hhuX6meheNeo5EKA',
|
|
34
|
-
interval: parseInt(process.env.DGP_INTERVAL || '30', 10),
|
|
35
|
-
commandPollInterval: parseInt(process.env.DGP_COMMAND_POLL_INTERVAL || '10', 10),
|
|
36
|
-
machineId: process.env.DGP_MACHINE_ID || `${hostname()}-${process.env.USERNAME || process.env.USER || 'dev'}`,
|
|
37
|
-
authToken: process.env.DGP_AUTH_TOKEN || null,
|
|
100
|
+
apiUrl: process.env.DGP_API_URL || fileConfig.apiUrl || 'https://asivayhbrqennwiwttds.supabase.co/functions/v1/dgp-agent-status',
|
|
101
|
+
commandsUrl: process.env.DGP_COMMANDS_URL || fileConfig.commandsUrl || 'https://asivayhbrqennwiwttds.supabase.co/rest/v1/dgp_agent_commands',
|
|
102
|
+
supabaseKey: process.env.DGP_SUPABASE_KEY || fileConfig.supabaseKey || 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImFzaXZheWhicnFlbm53aXd0dGRzIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjczMDAwOTcsImV4cCI6MjA4Mjg3NjA5N30.s3a7dR-dPkEXI7B2lUTUXU69923hhuX6meheNeo5EKA',
|
|
103
|
+
interval: parseInt(process.env.DGP_INTERVAL || fileConfig.interval || '30', 10),
|
|
104
|
+
commandPollInterval: parseInt(process.env.DGP_COMMAND_POLL_INTERVAL || fileConfig.commandPollInterval || '10', 10),
|
|
105
|
+
machineId: process.env.DGP_MACHINE_ID || fileConfig.machineId || `${hostname()}-${process.env.USERNAME || process.env.USER || 'dev'}`,
|
|
106
|
+
authToken: process.env.DGP_AUTH_TOKEN || fileConfig.authToken || null,
|
|
107
|
+
// Claude AI config
|
|
108
|
+
anthropicApiKey: process.env.ANTHROPIC_API_KEY || fileConfig.anthropicApiKey || null,
|
|
109
|
+
aiModel: process.env.DGP_AI_MODEL || fileConfig.aiModel || 'claude-sonnet-4-20250514',
|
|
38
110
|
};
|
|
39
111
|
|
|
40
112
|
// Colores para la consola
|
|
@@ -71,6 +143,290 @@ function logCommand(message) {
|
|
|
71
143
|
log(message, 'magenta');
|
|
72
144
|
}
|
|
73
145
|
|
|
146
|
+
function logAI(message) {
|
|
147
|
+
log(`🤖 ${message}`, 'cyan');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// ============================================
|
|
151
|
+
// CLAUDE AI INTEGRATION
|
|
152
|
+
// ============================================
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Llama a la API de Claude
|
|
156
|
+
*/
|
|
157
|
+
async function callClaude(prompt, systemPrompt = null) {
|
|
158
|
+
if (!CONFIG.anthropicApiKey) {
|
|
159
|
+
throw new Error('ANTHROPIC_API_KEY not configured. Set it as environment variable.');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const messages = [{ role: 'user', content: prompt }];
|
|
163
|
+
|
|
164
|
+
const body = {
|
|
165
|
+
model: CONFIG.aiModel,
|
|
166
|
+
max_tokens: 4096,
|
|
167
|
+
messages,
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
if (systemPrompt) {
|
|
171
|
+
body.system = systemPrompt;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const response = await fetch('https://api.anthropic.com/v1/messages', {
|
|
175
|
+
method: 'POST',
|
|
176
|
+
headers: {
|
|
177
|
+
'Content-Type': 'application/json',
|
|
178
|
+
'x-api-key': CONFIG.anthropicApiKey,
|
|
179
|
+
'anthropic-version': '2023-06-01',
|
|
180
|
+
},
|
|
181
|
+
body: JSON.stringify(body),
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
if (!response.ok) {
|
|
185
|
+
const text = await response.text();
|
|
186
|
+
throw new Error(`Claude API error: ${response.status} - ${text}`);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const data = await response.json();
|
|
190
|
+
return data.content[0].text;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Analiza el repositorio y genera contexto para IA
|
|
195
|
+
*/
|
|
196
|
+
function getRepoContext() {
|
|
197
|
+
const context = {
|
|
198
|
+
cwd: process.cwd(),
|
|
199
|
+
machineId: CONFIG.machineId,
|
|
200
|
+
git: {},
|
|
201
|
+
project: {},
|
|
202
|
+
files: [],
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
// Info de git
|
|
206
|
+
try {
|
|
207
|
+
context.git.branch = shellSync('git branch --show-current').trim();
|
|
208
|
+
context.git.remoteUrl = shellSync('git config --get remote.origin.url').trim();
|
|
209
|
+
context.git.lastCommit = shellSync('git log -1 --oneline').trim();
|
|
210
|
+
context.git.status = shellSync('git status --short').trim();
|
|
211
|
+
} catch (e) {
|
|
212
|
+
context.git.error = e.message;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Detectar tipo de proyecto
|
|
216
|
+
try {
|
|
217
|
+
const files = shellSync('ls -la').split('\n').slice(1);
|
|
218
|
+
context.files = files.map(f => f.split(/\s+/).pop()).filter(Boolean);
|
|
219
|
+
|
|
220
|
+
if (context.files.includes('package.json')) {
|
|
221
|
+
context.project.type = 'node';
|
|
222
|
+
try {
|
|
223
|
+
const pkg = JSON.parse(shellSync('cat package.json'));
|
|
224
|
+
context.project.name = pkg.name;
|
|
225
|
+
context.project.scripts = Object.keys(pkg.scripts || {});
|
|
226
|
+
context.project.dependencies = Object.keys(pkg.dependencies || {});
|
|
227
|
+
context.project.devDependencies = Object.keys(pkg.devDependencies || {});
|
|
228
|
+
} catch (e) {}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (context.files.includes('requirements.txt') || context.files.includes('setup.py')) {
|
|
232
|
+
context.project.type = 'python';
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (context.files.includes('Cargo.toml')) {
|
|
236
|
+
context.project.type = 'rust';
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (context.files.includes('go.mod')) {
|
|
240
|
+
context.project.type = 'go';
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Detectar frameworks
|
|
244
|
+
if (context.project.dependencies) {
|
|
245
|
+
if (context.project.dependencies.includes('react')) context.project.framework = 'react';
|
|
246
|
+
if (context.project.dependencies.includes('vue')) context.project.framework = 'vue';
|
|
247
|
+
if (context.project.dependencies.includes('next')) context.project.framework = 'nextjs';
|
|
248
|
+
if (context.project.dependencies.includes('vite')) context.project.bundler = 'vite';
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Detectar CI/CD
|
|
252
|
+
if (context.files.includes('.github')) {
|
|
253
|
+
context.project.ci = 'github-actions';
|
|
254
|
+
}
|
|
255
|
+
if (context.files.includes('.gitlab-ci.yml')) {
|
|
256
|
+
context.project.ci = 'gitlab-ci';
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
} catch (e) {
|
|
260
|
+
context.filesError = e.message;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return context;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Ejecuta una tarea usando IA
|
|
268
|
+
*/
|
|
269
|
+
async function executeAITask(task) {
|
|
270
|
+
logAI(`Analizando tarea: "${task}"`);
|
|
271
|
+
|
|
272
|
+
const context = getRepoContext();
|
|
273
|
+
|
|
274
|
+
const systemPrompt = `Eres un agente DevOps inteligente que ejecuta tareas en repositorios.
|
|
275
|
+
Tu trabajo es analizar la tarea solicitada y devolver los comandos shell que se deben ejecutar.
|
|
276
|
+
|
|
277
|
+
IMPORTANTE:
|
|
278
|
+
- Solo devuelve comandos que sean seguros de ejecutar
|
|
279
|
+
- No hagas cambios destructivos sin confirmación
|
|
280
|
+
- Usa el contexto del proyecto para elegir los comandos correctos
|
|
281
|
+
- Responde SOLO con JSON válido, sin explicaciones adicionales
|
|
282
|
+
|
|
283
|
+
Formato de respuesta:
|
|
284
|
+
{
|
|
285
|
+
"analysis": "breve explicación de lo que vas a hacer",
|
|
286
|
+
"commands": ["comando1", "comando2"],
|
|
287
|
+
"requires_confirmation": true/false,
|
|
288
|
+
"warning": "mensaje de advertencia si aplica"
|
|
289
|
+
}`;
|
|
290
|
+
|
|
291
|
+
const prompt = `Contexto del repositorio:
|
|
292
|
+
${JSON.stringify(context, null, 2)}
|
|
293
|
+
|
|
294
|
+
Tarea a ejecutar: ${task}
|
|
295
|
+
|
|
296
|
+
Devuelve los comandos necesarios en formato JSON.`;
|
|
297
|
+
|
|
298
|
+
try {
|
|
299
|
+
const response = await callClaude(prompt, systemPrompt);
|
|
300
|
+
|
|
301
|
+
// Parsear respuesta JSON
|
|
302
|
+
let result;
|
|
303
|
+
try {
|
|
304
|
+
// Extraer JSON de la respuesta (puede venir con markdown)
|
|
305
|
+
const jsonMatch = response.match(/\{[\s\S]*\}/);
|
|
306
|
+
if (jsonMatch) {
|
|
307
|
+
result = JSON.parse(jsonMatch[0]);
|
|
308
|
+
} else {
|
|
309
|
+
throw new Error('No JSON found in response');
|
|
310
|
+
}
|
|
311
|
+
} catch (e) {
|
|
312
|
+
logError(`Failed to parse AI response: ${e.message}`);
|
|
313
|
+
console.log('Raw response:', response);
|
|
314
|
+
return { success: false, error: 'Invalid AI response' };
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
logAI(`Análisis: ${result.analysis}`);
|
|
318
|
+
|
|
319
|
+
if (result.warning) {
|
|
320
|
+
log(`⚠️ ${result.warning}`, 'yellow');
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (result.requires_confirmation) {
|
|
324
|
+
log('Esta operación requiere confirmación. Usa --force para ejecutar sin confirmar.', 'yellow');
|
|
325
|
+
console.log('\nComandos a ejecutar:');
|
|
326
|
+
result.commands.forEach((cmd, i) => console.log(` ${i + 1}. ${cmd}`));
|
|
327
|
+
return { success: true, requiresConfirmation: true, commands: result.commands };
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Ejecutar comandos
|
|
331
|
+
logAI('Ejecutando comandos...');
|
|
332
|
+
const results = [];
|
|
333
|
+
|
|
334
|
+
for (const cmd of result.commands) {
|
|
335
|
+
logCommand(`$ ${cmd}`);
|
|
336
|
+
try {
|
|
337
|
+
const output = await shellAsync(cmd);
|
|
338
|
+
logSuccess('✓ Completado');
|
|
339
|
+
if (output.trim()) {
|
|
340
|
+
console.log(colors.gray + output + colors.reset);
|
|
341
|
+
}
|
|
342
|
+
results.push({ cmd, success: true, output });
|
|
343
|
+
} catch (e) {
|
|
344
|
+
logError(`✗ Falló: ${e.message}`);
|
|
345
|
+
results.push({ cmd, success: false, error: e.message });
|
|
346
|
+
break; // Detener en el primer error
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return { success: true, results };
|
|
351
|
+
|
|
352
|
+
} catch (e) {
|
|
353
|
+
logError(`AI task failed: ${e.message}`);
|
|
354
|
+
return { success: false, error: e.message };
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Modo interactivo con IA
|
|
360
|
+
*/
|
|
361
|
+
async function runAIMode() {
|
|
362
|
+
console.log('');
|
|
363
|
+
console.log(`${colors.cyan}╔═══════════════════════════════════════════════════════╗${colors.reset}`);
|
|
364
|
+
console.log(`${colors.cyan}║ DGP Agent - AI Mode 🤖 ║${colors.reset}`);
|
|
365
|
+
console.log(`${colors.cyan}║ Powered by Claude ║${colors.reset}`);
|
|
366
|
+
console.log(`${colors.cyan}╚═══════════════════════════════════════════════════════╝${colors.reset}`);
|
|
367
|
+
console.log('');
|
|
368
|
+
|
|
369
|
+
if (!CONFIG.anthropicApiKey) {
|
|
370
|
+
logError('ANTHROPIC_API_KEY not set.');
|
|
371
|
+
log('Set your API key: export ANTHROPIC_API_KEY=sk-ant-...', 'yellow');
|
|
372
|
+
process.exit(1);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Analizar repo
|
|
376
|
+
logAI('Analizando repositorio...');
|
|
377
|
+
const context = getRepoContext();
|
|
378
|
+
|
|
379
|
+
console.log('');
|
|
380
|
+
console.log(`${colors.bold}Proyecto detectado:${colors.reset}`);
|
|
381
|
+
console.log(` Tipo: ${context.project.type || 'desconocido'}`);
|
|
382
|
+
console.log(` Framework: ${context.project.framework || 'ninguno'}`);
|
|
383
|
+
console.log(` Bundler: ${context.project.bundler || 'ninguno'}`);
|
|
384
|
+
console.log(` CI/CD: ${context.project.ci || 'no detectado'}`);
|
|
385
|
+
console.log(` Branch: ${context.git.branch}`);
|
|
386
|
+
console.log(` Scripts: ${context.project.scripts?.join(', ') || 'ninguno'}`);
|
|
387
|
+
console.log('');
|
|
388
|
+
|
|
389
|
+
// Obtener tarea de los argumentos
|
|
390
|
+
const taskArgs = process.argv.slice(3).join(' ');
|
|
391
|
+
|
|
392
|
+
if (taskArgs) {
|
|
393
|
+
// Ejecutar tarea pasada como argumento
|
|
394
|
+
await executeAITask(taskArgs);
|
|
395
|
+
} else {
|
|
396
|
+
// Modo interactivo
|
|
397
|
+
const readline = await import('readline');
|
|
398
|
+
const rl = readline.createInterface({
|
|
399
|
+
input: process.stdin,
|
|
400
|
+
output: process.stdout,
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
const askQuestion = () => {
|
|
404
|
+
rl.question(`${colors.cyan}🤖 ¿Qué querés hacer? ${colors.reset}`, async (answer) => {
|
|
405
|
+
if (answer.toLowerCase() === 'exit' || answer.toLowerCase() === 'salir') {
|
|
406
|
+
console.log('¡Hasta luego!');
|
|
407
|
+
rl.close();
|
|
408
|
+
process.exit(0);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (answer.trim()) {
|
|
412
|
+
await executeAITask(answer);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
console.log('');
|
|
416
|
+
askQuestion();
|
|
417
|
+
});
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
log('Escribí "exit" o "salir" para terminar', 'gray');
|
|
421
|
+
console.log('');
|
|
422
|
+
askQuestion();
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// ============================================
|
|
427
|
+
// GIT FUNCTIONS
|
|
428
|
+
// ============================================
|
|
429
|
+
|
|
74
430
|
/**
|
|
75
431
|
* Ejecuta un comando git y retorna el resultado
|
|
76
432
|
*/
|
|
@@ -236,7 +592,7 @@ async function reportStatus(status) {
|
|
|
236
592
|
const payload = {
|
|
237
593
|
machine_id: CONFIG.machineId,
|
|
238
594
|
timestamp: new Date().toISOString(),
|
|
239
|
-
agent_version: '1.
|
|
595
|
+
agent_version: '1.4.0',
|
|
240
596
|
status,
|
|
241
597
|
};
|
|
242
598
|
|
|
@@ -535,7 +891,7 @@ async function runAgent(deployMode = false) {
|
|
|
535
891
|
console.log('');
|
|
536
892
|
console.log(`${colors.green}╔═══════════════════════════════════════════════════════╗${colors.reset}`);
|
|
537
893
|
console.log(`${colors.green}║ DGP Agent - Despliegue-GPT Local Agent ║${colors.reset}`);
|
|
538
|
-
console.log(`${colors.green}║ @deinossrl/dgp-agent v1.
|
|
894
|
+
console.log(`${colors.green}║ @deinossrl/dgp-agent v1.4.0 ║${colors.reset}`);
|
|
539
895
|
console.log(`${colors.green}╚═══════════════════════════════════════════════════════╝${colors.reset}`);
|
|
540
896
|
console.log('');
|
|
541
897
|
|
|
@@ -651,7 +1007,7 @@ async function showStatus() {
|
|
|
651
1007
|
function showHelp() {
|
|
652
1008
|
console.log(`
|
|
653
1009
|
${colors.bold}${colors.cyan}DGP Agent - Despliegue-GPT Local Agent${colors.reset}
|
|
654
|
-
${colors.gray}@deinossrl/dgp-agent v1.
|
|
1010
|
+
${colors.gray}@deinossrl/dgp-agent v1.4.0${colors.reset}
|
|
655
1011
|
|
|
656
1012
|
${colors.bold}DESCRIPCIÓN${colors.reset}
|
|
657
1013
|
Agente local que reporta el estado de tu repositorio Git
|
|
@@ -665,6 +1021,9 @@ ${colors.bold}USO${colors.reset}
|
|
|
665
1021
|
${colors.cyan}dgp-agent${colors.reset} Inicia el agente (solo reporta estado)
|
|
666
1022
|
${colors.cyan}dgp-agent deploy${colors.reset} Modo deploy (reporta + escucha comandos)
|
|
667
1023
|
${colors.cyan}dgp-agent status${colors.reset} Muestra el estado actual una vez
|
|
1024
|
+
${colors.cyan}dgp-agent ai${colors.reset} Modo IA interactivo (requiere API key)
|
|
1025
|
+
${colors.cyan}dgp-agent ai "tarea"${colors.reset} Ejecuta una tarea con IA
|
|
1026
|
+
${colors.cyan}dgp-agent config${colors.reset} Gestiona la configuración local
|
|
668
1027
|
${colors.cyan}dgp-agent help${colors.reset} Muestra esta ayuda
|
|
669
1028
|
|
|
670
1029
|
${colors.bold}MODOS DE OPERACIÓN${colors.reset}
|
|
@@ -699,6 +1058,8 @@ ${colors.bold}REQUISITOS PARA DEPLOY${colors.reset}
|
|
|
699
1058
|
- Permisos sudo para reload nginx (vía sudoers sin password)
|
|
700
1059
|
|
|
701
1060
|
${colors.bold}CHANGELOG${colors.reset}
|
|
1061
|
+
${colors.cyan}v1.4.0${colors.reset} - Config persistente: dgp-agent config set/get/list
|
|
1062
|
+
${colors.cyan}v1.3.0${colors.reset} - AI Mode: ejecuta tareas con lenguaje natural
|
|
702
1063
|
${colors.cyan}v1.2.7${colors.reset} - Fix: update via Edge Function (401 fix)
|
|
703
1064
|
${colors.cyan}v1.2.6${colors.reset} - Comando git_commit_push desde la UI
|
|
704
1065
|
${colors.cyan}v1.2.5${colors.reset} - Sincronización de versiones y changelog
|
|
@@ -712,6 +1073,133 @@ ${colors.bold}MÁS INFO${colors.reset}
|
|
|
712
1073
|
`);
|
|
713
1074
|
}
|
|
714
1075
|
|
|
1076
|
+
/**
|
|
1077
|
+
* Maneja el comando config
|
|
1078
|
+
*/
|
|
1079
|
+
function handleConfigCommand(args) {
|
|
1080
|
+
const subcommand = args[0];
|
|
1081
|
+
const key = args[1];
|
|
1082
|
+
const value = args.slice(2).join(' ');
|
|
1083
|
+
|
|
1084
|
+
// Mapeo de claves amigables a claves internas
|
|
1085
|
+
const keyMap = {
|
|
1086
|
+
'anthropic-api-key': 'anthropicApiKey',
|
|
1087
|
+
'api-key': 'anthropicApiKey',
|
|
1088
|
+
'ai-model': 'aiModel',
|
|
1089
|
+
'model': 'aiModel',
|
|
1090
|
+
'auth-token': 'authToken',
|
|
1091
|
+
'token': 'authToken',
|
|
1092
|
+
'interval': 'interval',
|
|
1093
|
+
'machine-id': 'machineId',
|
|
1094
|
+
};
|
|
1095
|
+
|
|
1096
|
+
const resolveKey = (k) => keyMap[k] || k;
|
|
1097
|
+
|
|
1098
|
+
switch (subcommand) {
|
|
1099
|
+
case 'set':
|
|
1100
|
+
if (!key || !value) {
|
|
1101
|
+
console.log(`${colors.red}Error: dgp-agent config set <key> <value>${colors.reset}`);
|
|
1102
|
+
console.log(`\nClaves disponibles:`);
|
|
1103
|
+
Object.keys(keyMap).forEach(k => console.log(` ${colors.cyan}${k}${colors.reset}`));
|
|
1104
|
+
process.exit(1);
|
|
1105
|
+
}
|
|
1106
|
+
const setKey = resolveKey(key);
|
|
1107
|
+
if (setConfigValue(setKey, value)) {
|
|
1108
|
+
logSuccess(`Config saved: ${key} = ${value.substring(0, 20)}${value.length > 20 ? '...' : ''}`);
|
|
1109
|
+
console.log(`${colors.gray}Archivo: ${CONFIG_FILE}${colors.reset}`);
|
|
1110
|
+
}
|
|
1111
|
+
break;
|
|
1112
|
+
|
|
1113
|
+
case 'get':
|
|
1114
|
+
if (!key) {
|
|
1115
|
+
console.log(`${colors.red}Error: dgp-agent config get <key>${colors.reset}`);
|
|
1116
|
+
process.exit(1);
|
|
1117
|
+
}
|
|
1118
|
+
const getKey = resolveKey(key);
|
|
1119
|
+
const val = getConfigValue(getKey);
|
|
1120
|
+
if (val) {
|
|
1121
|
+
console.log(val);
|
|
1122
|
+
} else {
|
|
1123
|
+
console.log(`${colors.gray}(not set)${colors.reset}`);
|
|
1124
|
+
}
|
|
1125
|
+
break;
|
|
1126
|
+
|
|
1127
|
+
case 'list':
|
|
1128
|
+
case 'ls':
|
|
1129
|
+
const config = loadConfigFile();
|
|
1130
|
+
console.log(`\n${colors.bold}Configuración guardada:${colors.reset}`);
|
|
1131
|
+
console.log(`${colors.gray}Archivo: ${CONFIG_FILE}${colors.reset}\n`);
|
|
1132
|
+
|
|
1133
|
+
if (Object.keys(config).length === 0) {
|
|
1134
|
+
console.log(`${colors.gray} (vacío)${colors.reset}`);
|
|
1135
|
+
} else {
|
|
1136
|
+
Object.entries(config).forEach(([k, v]) => {
|
|
1137
|
+
// Ocultar parcialmente las API keys
|
|
1138
|
+
let displayValue = v;
|
|
1139
|
+
if (k.toLowerCase().includes('key') || k.toLowerCase().includes('token')) {
|
|
1140
|
+
displayValue = typeof v === 'string' && v.length > 10
|
|
1141
|
+
? v.substring(0, 10) + '...' + v.substring(v.length - 4)
|
|
1142
|
+
: '***';
|
|
1143
|
+
}
|
|
1144
|
+
console.log(` ${colors.cyan}${k}${colors.reset}: ${displayValue}`);
|
|
1145
|
+
});
|
|
1146
|
+
}
|
|
1147
|
+
console.log('');
|
|
1148
|
+
break;
|
|
1149
|
+
|
|
1150
|
+
case 'delete':
|
|
1151
|
+
case 'rm':
|
|
1152
|
+
case 'remove':
|
|
1153
|
+
if (!key) {
|
|
1154
|
+
console.log(`${colors.red}Error: dgp-agent config delete <key>${colors.reset}`);
|
|
1155
|
+
process.exit(1);
|
|
1156
|
+
}
|
|
1157
|
+
const delKey = resolveKey(key);
|
|
1158
|
+
if (deleteConfigValue(delKey)) {
|
|
1159
|
+
logSuccess(`Config deleted: ${key}`);
|
|
1160
|
+
}
|
|
1161
|
+
break;
|
|
1162
|
+
|
|
1163
|
+
case 'path':
|
|
1164
|
+
console.log(CONFIG_FILE);
|
|
1165
|
+
break;
|
|
1166
|
+
|
|
1167
|
+
default:
|
|
1168
|
+
console.log(`
|
|
1169
|
+
${colors.bold}${colors.cyan}dgp-agent config${colors.reset} - Gestión de configuración
|
|
1170
|
+
|
|
1171
|
+
${colors.bold}USO${colors.reset}
|
|
1172
|
+
${colors.cyan}dgp-agent config set <key> <value>${colors.reset} Guardar un valor
|
|
1173
|
+
${colors.cyan}dgp-agent config get <key>${colors.reset} Obtener un valor
|
|
1174
|
+
${colors.cyan}dgp-agent config list${colors.reset} Listar toda la config
|
|
1175
|
+
${colors.cyan}dgp-agent config delete <key>${colors.reset} Eliminar un valor
|
|
1176
|
+
${colors.cyan}dgp-agent config path${colors.reset} Mostrar ruta del archivo
|
|
1177
|
+
|
|
1178
|
+
${colors.bold}CLAVES DISPONIBLES${colors.reset}
|
|
1179
|
+
${colors.yellow}anthropic-api-key${colors.reset} Tu API key de Anthropic (Claude)
|
|
1180
|
+
${colors.yellow}ai-model${colors.reset} Modelo de IA (default: claude-sonnet-4-20250514)
|
|
1181
|
+
${colors.yellow}auth-token${colors.reset} Token JWT para autenticación
|
|
1182
|
+
${colors.yellow}interval${colors.reset} Intervalo de reporte en segundos
|
|
1183
|
+
${colors.yellow}machine-id${colors.reset} ID personalizado de la máquina
|
|
1184
|
+
|
|
1185
|
+
${colors.bold}EJEMPLOS${colors.reset}
|
|
1186
|
+
${colors.gray}# Configurar API key de Claude${colors.reset}
|
|
1187
|
+
dgp-agent config set anthropic-api-key sk-ant-api03-...
|
|
1188
|
+
|
|
1189
|
+
${colors.gray}# Ver configuración actual${colors.reset}
|
|
1190
|
+
dgp-agent config list
|
|
1191
|
+
|
|
1192
|
+
${colors.gray}# Obtener un valor específico${colors.reset}
|
|
1193
|
+
dgp-agent config get ai-model
|
|
1194
|
+
|
|
1195
|
+
${colors.bold}NOTAS${colors.reset}
|
|
1196
|
+
- La config se guarda en: ${colors.gray}${CONFIG_FILE}${colors.reset}
|
|
1197
|
+
- Las variables de entorno tienen prioridad sobre el archivo
|
|
1198
|
+
- Las API keys se ocultan parcialmente al listar
|
|
1199
|
+
`);
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
|
|
715
1203
|
// CLI
|
|
716
1204
|
const args = process.argv.slice(2);
|
|
717
1205
|
const command = args[0];
|
|
@@ -735,7 +1223,14 @@ switch (command) {
|
|
|
735
1223
|
case 'version':
|
|
736
1224
|
case '-v':
|
|
737
1225
|
case '--version':
|
|
738
|
-
console.log('@deinossrl/dgp-agent v1.
|
|
1226
|
+
console.log('@deinossrl/dgp-agent v1.4.0');
|
|
1227
|
+
break;
|
|
1228
|
+
case 'ai':
|
|
1229
|
+
case '--ai':
|
|
1230
|
+
runAIMode();
|
|
1231
|
+
break;
|
|
1232
|
+
case 'config':
|
|
1233
|
+
handleConfigCommand(args.slice(1));
|
|
739
1234
|
break;
|
|
740
1235
|
default:
|
|
741
1236
|
runAgent(false); // Status-only mode
|