@deinossrl/dgp-agent 1.2.7 β†’ 1.3.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 +298 -4
  2. package/package.json +1 -1
package/index.mjs CHANGED
@@ -35,6 +35,9 @@ const CONFIG = {
35
35
  commandPollInterval: parseInt(process.env.DGP_COMMAND_POLL_INTERVAL || '10', 10),
36
36
  machineId: process.env.DGP_MACHINE_ID || `${hostname()}-${process.env.USERNAME || process.env.USER || 'dev'}`,
37
37
  authToken: process.env.DGP_AUTH_TOKEN || null,
38
+ // Claude AI config
39
+ anthropicApiKey: process.env.ANTHROPIC_API_KEY || null,
40
+ aiModel: process.env.DGP_AI_MODEL || 'claude-sonnet-4-20250514',
38
41
  };
39
42
 
40
43
  // Colores para la consola
@@ -71,6 +74,290 @@ function logCommand(message) {
71
74
  log(message, 'magenta');
72
75
  }
73
76
 
77
+ function logAI(message) {
78
+ log(`πŸ€– ${message}`, 'cyan');
79
+ }
80
+
81
+ // ============================================
82
+ // CLAUDE AI INTEGRATION
83
+ // ============================================
84
+
85
+ /**
86
+ * Llama a la API de Claude
87
+ */
88
+ async function callClaude(prompt, systemPrompt = null) {
89
+ if (!CONFIG.anthropicApiKey) {
90
+ throw new Error('ANTHROPIC_API_KEY not configured. Set it as environment variable.');
91
+ }
92
+
93
+ const messages = [{ role: 'user', content: prompt }];
94
+
95
+ const body = {
96
+ model: CONFIG.aiModel,
97
+ max_tokens: 4096,
98
+ messages,
99
+ };
100
+
101
+ if (systemPrompt) {
102
+ body.system = systemPrompt;
103
+ }
104
+
105
+ const response = await fetch('https://api.anthropic.com/v1/messages', {
106
+ method: 'POST',
107
+ headers: {
108
+ 'Content-Type': 'application/json',
109
+ 'x-api-key': CONFIG.anthropicApiKey,
110
+ 'anthropic-version': '2023-06-01',
111
+ },
112
+ body: JSON.stringify(body),
113
+ });
114
+
115
+ if (!response.ok) {
116
+ const text = await response.text();
117
+ throw new Error(`Claude API error: ${response.status} - ${text}`);
118
+ }
119
+
120
+ const data = await response.json();
121
+ return data.content[0].text;
122
+ }
123
+
124
+ /**
125
+ * Analiza el repositorio y genera contexto para IA
126
+ */
127
+ function getRepoContext() {
128
+ const context = {
129
+ cwd: process.cwd(),
130
+ machineId: CONFIG.machineId,
131
+ git: {},
132
+ project: {},
133
+ files: [],
134
+ };
135
+
136
+ // Info de git
137
+ try {
138
+ context.git.branch = shellSync('git branch --show-current').trim();
139
+ context.git.remoteUrl = shellSync('git config --get remote.origin.url').trim();
140
+ context.git.lastCommit = shellSync('git log -1 --oneline').trim();
141
+ context.git.status = shellSync('git status --short').trim();
142
+ } catch (e) {
143
+ context.git.error = e.message;
144
+ }
145
+
146
+ // Detectar tipo de proyecto
147
+ try {
148
+ const files = shellSync('ls -la').split('\n').slice(1);
149
+ context.files = files.map(f => f.split(/\s+/).pop()).filter(Boolean);
150
+
151
+ if (context.files.includes('package.json')) {
152
+ context.project.type = 'node';
153
+ try {
154
+ const pkg = JSON.parse(shellSync('cat package.json'));
155
+ context.project.name = pkg.name;
156
+ context.project.scripts = Object.keys(pkg.scripts || {});
157
+ context.project.dependencies = Object.keys(pkg.dependencies || {});
158
+ context.project.devDependencies = Object.keys(pkg.devDependencies || {});
159
+ } catch (e) {}
160
+ }
161
+
162
+ if (context.files.includes('requirements.txt') || context.files.includes('setup.py')) {
163
+ context.project.type = 'python';
164
+ }
165
+
166
+ if (context.files.includes('Cargo.toml')) {
167
+ context.project.type = 'rust';
168
+ }
169
+
170
+ if (context.files.includes('go.mod')) {
171
+ context.project.type = 'go';
172
+ }
173
+
174
+ // Detectar frameworks
175
+ if (context.project.dependencies) {
176
+ if (context.project.dependencies.includes('react')) context.project.framework = 'react';
177
+ if (context.project.dependencies.includes('vue')) context.project.framework = 'vue';
178
+ if (context.project.dependencies.includes('next')) context.project.framework = 'nextjs';
179
+ if (context.project.dependencies.includes('vite')) context.project.bundler = 'vite';
180
+ }
181
+
182
+ // Detectar CI/CD
183
+ if (context.files.includes('.github')) {
184
+ context.project.ci = 'github-actions';
185
+ }
186
+ if (context.files.includes('.gitlab-ci.yml')) {
187
+ context.project.ci = 'gitlab-ci';
188
+ }
189
+
190
+ } catch (e) {
191
+ context.filesError = e.message;
192
+ }
193
+
194
+ return context;
195
+ }
196
+
197
+ /**
198
+ * Ejecuta una tarea usando IA
199
+ */
200
+ async function executeAITask(task) {
201
+ logAI(`Analizando tarea: "${task}"`);
202
+
203
+ const context = getRepoContext();
204
+
205
+ const systemPrompt = `Eres un agente DevOps inteligente que ejecuta tareas en repositorios.
206
+ Tu trabajo es analizar la tarea solicitada y devolver los comandos shell que se deben ejecutar.
207
+
208
+ IMPORTANTE:
209
+ - Solo devuelve comandos que sean seguros de ejecutar
210
+ - No hagas cambios destructivos sin confirmaciΓ³n
211
+ - Usa el contexto del proyecto para elegir los comandos correctos
212
+ - Responde SOLO con JSON vΓ‘lido, sin explicaciones adicionales
213
+
214
+ Formato de respuesta:
215
+ {
216
+ "analysis": "breve explicaciΓ³n de lo que vas a hacer",
217
+ "commands": ["comando1", "comando2"],
218
+ "requires_confirmation": true/false,
219
+ "warning": "mensaje de advertencia si aplica"
220
+ }`;
221
+
222
+ const prompt = `Contexto del repositorio:
223
+ ${JSON.stringify(context, null, 2)}
224
+
225
+ Tarea a ejecutar: ${task}
226
+
227
+ Devuelve los comandos necesarios en formato JSON.`;
228
+
229
+ try {
230
+ const response = await callClaude(prompt, systemPrompt);
231
+
232
+ // Parsear respuesta JSON
233
+ let result;
234
+ try {
235
+ // Extraer JSON de la respuesta (puede venir con markdown)
236
+ const jsonMatch = response.match(/\{[\s\S]*\}/);
237
+ if (jsonMatch) {
238
+ result = JSON.parse(jsonMatch[0]);
239
+ } else {
240
+ throw new Error('No JSON found in response');
241
+ }
242
+ } catch (e) {
243
+ logError(`Failed to parse AI response: ${e.message}`);
244
+ console.log('Raw response:', response);
245
+ return { success: false, error: 'Invalid AI response' };
246
+ }
247
+
248
+ logAI(`AnΓ‘lisis: ${result.analysis}`);
249
+
250
+ if (result.warning) {
251
+ log(`⚠️ ${result.warning}`, 'yellow');
252
+ }
253
+
254
+ if (result.requires_confirmation) {
255
+ log('Esta operaciΓ³n requiere confirmaciΓ³n. Usa --force para ejecutar sin confirmar.', 'yellow');
256
+ console.log('\nComandos a ejecutar:');
257
+ result.commands.forEach((cmd, i) => console.log(` ${i + 1}. ${cmd}`));
258
+ return { success: true, requiresConfirmation: true, commands: result.commands };
259
+ }
260
+
261
+ // Ejecutar comandos
262
+ logAI('Ejecutando comandos...');
263
+ const results = [];
264
+
265
+ for (const cmd of result.commands) {
266
+ logCommand(`$ ${cmd}`);
267
+ try {
268
+ const output = await shellAsync(cmd);
269
+ logSuccess('βœ“ Completado');
270
+ if (output.trim()) {
271
+ console.log(colors.gray + output + colors.reset);
272
+ }
273
+ results.push({ cmd, success: true, output });
274
+ } catch (e) {
275
+ logError(`βœ— FallΓ³: ${e.message}`);
276
+ results.push({ cmd, success: false, error: e.message });
277
+ break; // Detener en el primer error
278
+ }
279
+ }
280
+
281
+ return { success: true, results };
282
+
283
+ } catch (e) {
284
+ logError(`AI task failed: ${e.message}`);
285
+ return { success: false, error: e.message };
286
+ }
287
+ }
288
+
289
+ /**
290
+ * Modo interactivo con IA
291
+ */
292
+ async function runAIMode() {
293
+ console.log('');
294
+ console.log(`${colors.cyan}╔═══════════════════════════════════════════════════════╗${colors.reset}`);
295
+ console.log(`${colors.cyan}β•‘ DGP Agent - AI Mode πŸ€– β•‘${colors.reset}`);
296
+ console.log(`${colors.cyan}β•‘ Powered by Claude β•‘${colors.reset}`);
297
+ console.log(`${colors.cyan}β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•${colors.reset}`);
298
+ console.log('');
299
+
300
+ if (!CONFIG.anthropicApiKey) {
301
+ logError('ANTHROPIC_API_KEY not set.');
302
+ log('Set your API key: export ANTHROPIC_API_KEY=sk-ant-...', 'yellow');
303
+ process.exit(1);
304
+ }
305
+
306
+ // Analizar repo
307
+ logAI('Analizando repositorio...');
308
+ const context = getRepoContext();
309
+
310
+ console.log('');
311
+ console.log(`${colors.bold}Proyecto detectado:${colors.reset}`);
312
+ console.log(` Tipo: ${context.project.type || 'desconocido'}`);
313
+ console.log(` Framework: ${context.project.framework || 'ninguno'}`);
314
+ console.log(` Bundler: ${context.project.bundler || 'ninguno'}`);
315
+ console.log(` CI/CD: ${context.project.ci || 'no detectado'}`);
316
+ console.log(` Branch: ${context.git.branch}`);
317
+ console.log(` Scripts: ${context.project.scripts?.join(', ') || 'ninguno'}`);
318
+ console.log('');
319
+
320
+ // Obtener tarea de los argumentos
321
+ const taskArgs = process.argv.slice(3).join(' ');
322
+
323
+ if (taskArgs) {
324
+ // Ejecutar tarea pasada como argumento
325
+ await executeAITask(taskArgs);
326
+ } else {
327
+ // Modo interactivo
328
+ const readline = await import('readline');
329
+ const rl = readline.createInterface({
330
+ input: process.stdin,
331
+ output: process.stdout,
332
+ });
333
+
334
+ const askQuestion = () => {
335
+ rl.question(`${colors.cyan}πŸ€– ΒΏQuΓ© querΓ©s hacer? ${colors.reset}`, async (answer) => {
336
+ if (answer.toLowerCase() === 'exit' || answer.toLowerCase() === 'salir') {
337
+ console.log('Β‘Hasta luego!');
338
+ rl.close();
339
+ process.exit(0);
340
+ }
341
+
342
+ if (answer.trim()) {
343
+ await executeAITask(answer);
344
+ }
345
+
346
+ console.log('');
347
+ askQuestion();
348
+ });
349
+ };
350
+
351
+ log('EscribΓ­ "exit" o "salir" para terminar', 'gray');
352
+ console.log('');
353
+ askQuestion();
354
+ }
355
+ }
356
+
357
+ // ============================================
358
+ // GIT FUNCTIONS
359
+ // ============================================
360
+
74
361
  /**
75
362
  * Ejecuta un comando git y retorna el resultado
76
363
  */
@@ -236,7 +523,7 @@ async function reportStatus(status) {
236
523
  const payload = {
237
524
  machine_id: CONFIG.machineId,
238
525
  timestamp: new Date().toISOString(),
239
- agent_version: '1.2.5',
526
+ agent_version: '1.3.0',
240
527
  status,
241
528
  };
242
529
 
@@ -535,7 +822,7 @@ async function runAgent(deployMode = false) {
535
822
  console.log('');
536
823
  console.log(`${colors.green}╔═══════════════════════════════════════════════════════╗${colors.reset}`);
537
824
  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}`);
825
+ console.log(`${colors.green}β•‘ @deinossrl/dgp-agent v1.3.0 β•‘${colors.reset}`);
539
826
  console.log(`${colors.green}β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•${colors.reset}`);
540
827
  console.log('');
541
828
 
@@ -651,7 +938,7 @@ async function showStatus() {
651
938
  function showHelp() {
652
939
  console.log(`
653
940
  ${colors.bold}${colors.cyan}DGP Agent - Despliegue-GPT Local Agent${colors.reset}
654
- ${colors.gray}@deinossrl/dgp-agent v1.2.7${colors.reset}
941
+ ${colors.gray}@deinossrl/dgp-agent v1.3.0${colors.reset}
655
942
 
656
943
  ${colors.bold}DESCRIPCIΓ“N${colors.reset}
657
944
  Agente local que reporta el estado de tu repositorio Git
@@ -665,6 +952,8 @@ ${colors.bold}USO${colors.reset}
665
952
  ${colors.cyan}dgp-agent${colors.reset} Inicia el agente (solo reporta estado)
666
953
  ${colors.cyan}dgp-agent deploy${colors.reset} Modo deploy (reporta + escucha comandos)
667
954
  ${colors.cyan}dgp-agent status${colors.reset} Muestra el estado actual una vez
955
+ ${colors.cyan}dgp-agent ai${colors.reset} Modo IA interactivo (requiere ANTHROPIC_API_KEY)
956
+ ${colors.cyan}dgp-agent ai "tarea"${colors.reset} Ejecuta una tarea con IA
668
957
  ${colors.cyan}dgp-agent help${colors.reset} Muestra esta ayuda
669
958
 
670
959
  ${colors.bold}MODOS DE OPERACIΓ“N${colors.reset}
@@ -699,6 +988,7 @@ ${colors.bold}REQUISITOS PARA DEPLOY${colors.reset}
699
988
  - Permisos sudo para reload nginx (vΓ­a sudoers sin password)
700
989
 
701
990
  ${colors.bold}CHANGELOG${colors.reset}
991
+ ${colors.cyan}v1.3.0${colors.reset} - AI Mode: ejecuta tareas con lenguaje natural
702
992
  ${colors.cyan}v1.2.7${colors.reset} - Fix: update via Edge Function (401 fix)
703
993
  ${colors.cyan}v1.2.6${colors.reset} - Comando git_commit_push desde la UI
704
994
  ${colors.cyan}v1.2.5${colors.reset} - SincronizaciΓ³n de versiones y changelog
@@ -735,7 +1025,11 @@ switch (command) {
735
1025
  case 'version':
736
1026
  case '-v':
737
1027
  case '--version':
738
- console.log('@deinossrl/dgp-agent v1.2.7');
1028
+ console.log('@deinossrl/dgp-agent v1.3.0');
1029
+ break;
1030
+ case 'ai':
1031
+ case '--ai':
1032
+ runAIMode();
739
1033
  break;
740
1034
  default:
741
1035
  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.3.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": {