@deinossrl/dgp-agent 1.2.6 β 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.
- package/index.mjs +310 -18
- 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.
|
|
526
|
+
agent_version: '1.3.0',
|
|
240
527
|
status,
|
|
241
528
|
};
|
|
242
529
|
|
|
@@ -287,32 +574,29 @@ async function getPendingCommands() {
|
|
|
287
574
|
}
|
|
288
575
|
|
|
289
576
|
/**
|
|
290
|
-
* Actualiza el estado de un comando
|
|
577
|
+
* Actualiza el estado de un comando via Edge Function
|
|
291
578
|
*/
|
|
292
579
|
async function updateCommandStatus(commandId, status, result = {}, errorMessage = null) {
|
|
293
|
-
const
|
|
294
|
-
status,
|
|
295
|
-
result,
|
|
296
|
-
error_message: errorMessage,
|
|
297
|
-
...(status === 'running' ? { picked_up_at: new Date().toISOString() } : {}),
|
|
298
|
-
...(status === 'success' || status === 'failed' ? { completed_at: new Date().toISOString() } : {}),
|
|
299
|
-
};
|
|
300
|
-
|
|
301
|
-
const url = `${CONFIG.commandsUrl}?id=eq.${commandId}`;
|
|
580
|
+
const url = 'https://asivayhbrqennwiwttds.supabase.co/functions/v1/dgp-agent-command-update';
|
|
302
581
|
|
|
303
582
|
const response = await fetch(url, {
|
|
304
|
-
method: '
|
|
583
|
+
method: 'POST',
|
|
305
584
|
headers: {
|
|
306
585
|
'apikey': CONFIG.supabaseKey,
|
|
307
586
|
'Authorization': `Bearer ${CONFIG.supabaseKey}`,
|
|
308
587
|
'Content-Type': 'application/json',
|
|
309
|
-
'Prefer': 'return=minimal',
|
|
310
588
|
},
|
|
311
|
-
body: JSON.stringify(
|
|
589
|
+
body: JSON.stringify({
|
|
590
|
+
commandId,
|
|
591
|
+
status,
|
|
592
|
+
result,
|
|
593
|
+
error_message: errorMessage,
|
|
594
|
+
}),
|
|
312
595
|
});
|
|
313
596
|
|
|
314
597
|
if (!response.ok) {
|
|
315
|
-
|
|
598
|
+
const text = await response.text();
|
|
599
|
+
throw new Error(`Failed to update command: HTTP ${response.status} - ${text}`);
|
|
316
600
|
}
|
|
317
601
|
}
|
|
318
602
|
|
|
@@ -538,7 +822,7 @@ async function runAgent(deployMode = false) {
|
|
|
538
822
|
console.log('');
|
|
539
823
|
console.log(`${colors.green}βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ${colors.reset}`);
|
|
540
824
|
console.log(`${colors.green}β DGP Agent - Despliegue-GPT Local Agent β${colors.reset}`);
|
|
541
|
-
console.log(`${colors.green}β @deinossrl/dgp-agent v1.
|
|
825
|
+
console.log(`${colors.green}β @deinossrl/dgp-agent v1.3.0 β${colors.reset}`);
|
|
542
826
|
console.log(`${colors.green}βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ${colors.reset}`);
|
|
543
827
|
console.log('');
|
|
544
828
|
|
|
@@ -654,7 +938,7 @@ async function showStatus() {
|
|
|
654
938
|
function showHelp() {
|
|
655
939
|
console.log(`
|
|
656
940
|
${colors.bold}${colors.cyan}DGP Agent - Despliegue-GPT Local Agent${colors.reset}
|
|
657
|
-
${colors.gray}@deinossrl/dgp-agent v1.
|
|
941
|
+
${colors.gray}@deinossrl/dgp-agent v1.3.0${colors.reset}
|
|
658
942
|
|
|
659
943
|
${colors.bold}DESCRIPCIΓN${colors.reset}
|
|
660
944
|
Agente local que reporta el estado de tu repositorio Git
|
|
@@ -668,6 +952,8 @@ ${colors.bold}USO${colors.reset}
|
|
|
668
952
|
${colors.cyan}dgp-agent${colors.reset} Inicia el agente (solo reporta estado)
|
|
669
953
|
${colors.cyan}dgp-agent deploy${colors.reset} Modo deploy (reporta + escucha comandos)
|
|
670
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
|
|
671
957
|
${colors.cyan}dgp-agent help${colors.reset} Muestra esta ayuda
|
|
672
958
|
|
|
673
959
|
${colors.bold}MODOS DE OPERACIΓN${colors.reset}
|
|
@@ -702,6 +988,8 @@ ${colors.bold}REQUISITOS PARA DEPLOY${colors.reset}
|
|
|
702
988
|
- Permisos sudo para reload nginx (vΓa sudoers sin password)
|
|
703
989
|
|
|
704
990
|
${colors.bold}CHANGELOG${colors.reset}
|
|
991
|
+
${colors.cyan}v1.3.0${colors.reset} - AI Mode: ejecuta tareas con lenguaje natural
|
|
992
|
+
${colors.cyan}v1.2.7${colors.reset} - Fix: update via Edge Function (401 fix)
|
|
705
993
|
${colors.cyan}v1.2.6${colors.reset} - Comando git_commit_push desde la UI
|
|
706
994
|
${colors.cyan}v1.2.5${colors.reset} - SincronizaciΓ³n de versiones y changelog
|
|
707
995
|
${colors.cyan}v1.2.4${colors.reset} - Mejoras en ejecuciΓ³n async de comandos shell
|
|
@@ -737,7 +1025,11 @@ switch (command) {
|
|
|
737
1025
|
case 'version':
|
|
738
1026
|
case '-v':
|
|
739
1027
|
case '--version':
|
|
740
|
-
console.log('@deinossrl/dgp-agent v1.
|
|
1028
|
+
console.log('@deinossrl/dgp-agent v1.3.0');
|
|
1029
|
+
break;
|
|
1030
|
+
case 'ai':
|
|
1031
|
+
case '--ai':
|
|
1032
|
+
runAIMode();
|
|
741
1033
|
break;
|
|
742
1034
|
default:
|
|
743
1035
|
runAgent(false); // Status-only mode
|