@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.
Files changed (2) hide show
  1. package/index.mjs +508 -13
  2. 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
- // Configuración
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.2.5',
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.2.7 ║${colors.reset}`);
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.2.7${colors.reset}
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.2.7');
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deinossrl/dgp-agent",
3
- "version": "1.2.7",
3
+ "version": "1.4.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": {