discord-linux-groq-agent/agent-chat.mjs
Raven Scott d46ffd5086 fix
2024-12-10 04:53:24 -05:00

265 lines
8.5 KiB
JavaScript

import 'dotenv/config';
import Groq from 'groq-sdk';
import unirest from 'unirest';
import readline from 'readline';
const DISCORD_LINUX_API_URL = 'https://api.ssh.surf';
const DISCORD_LINUX_API_KEY = process.env['DISCORD_LINUX_API_KEY'];
const GROQ_API_KEY = process.env['GROQ_API_KEY'];
const MAX_ITERATIONS = 5;
// Initialize the Groq client
const groqClient = new Groq({
apiKey: GROQ_API_KEY,
});
// Logging helpers
function logHeader(message) {
console.log('\n' + '═'.repeat(80));
console.log('═ ' + message);
console.log('═'.repeat(80) + '\n');
}
function logSubHeader(message) {
console.log('\n' + '-'.repeat(60));
console.log('> ' + message);
console.log('-'.repeat(60) + '\n');
}
function logInfo(message) {
console.log(`INFO: ${message}`);
}
function logCommandStart(cmd) {
console.log(`\n[EXECUTING COMMAND]\n$ ${cmd}\n`);
}
function logCommandResult(stdout, stderr) {
if (stdout && stdout.trim().length > 0) {
console.log("[STDOUT]:\n" + indentMultiline(stdout));
} else {
console.log("[STDOUT]: (empty)\n");
}
if (stderr && stderr.trim().length > 0) {
console.log("[STDERR]:\n" + indentMultiline(stderr));
} else {
console.log("[STDERR]: (empty)\n");
}
}
function indentMultiline(text) {
return text.split('\n').map(line => ' ' + line).join('\n');
}
// Execute a command in the container
async function execCommandInContainer(cmd, pwd = '/home') {
const response = await unirest
.post(`${DISCORD_LINUX_API_URL}/exec`)
.headers({
'Accept': 'application/json',
'Content-Type': 'application/json',
'x-ssh-auth': DISCORD_LINUX_API_KEY
})
.send({ cmd, pwd });
return response.body;
}
// Ask AI for instructions
async function askAIForInstructions(context, goal) {
const systemPrompt = `You are a highly skilled Linux system administration assistant with direct command-line access to a Debian/Ubuntu-based Linux container.
Your mission is to achieve the following goal: "${goal}"
Follow these rules:
1. Return only shell commands needed to achieve this exact goal, line-by-line, no explanations.
2. The commands must be directly related to accomplishing the goal. Do not run unrelated commands.
3. If previous attempts failed, adjust the commands based on the context and errors.
4. Consider common steps if needed (e.g., update packages before installing).
5. Always ensure non-interactive operation (use -y for apt, etc.).
6. No markdown formatting. Just commands, one per line. No extra text.
7. Only include necessary commands. Avoid irrelevant repository additions or unrelated installations.
8. Do not forget the exact goal. All commands must focus on achieving the requested goal.`;
const userPrompt = `CONTEXT:\n${context}\n\nPlease provide the exact shell commands to achieve the goal: "${goal}"`;
const params = {
messages: [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: userPrompt }
],
model: 'llama3-8b-8192',
};
const chatCompletion = await groqClient.chat.completions.create(params);
const aiResponse = chatCompletion.choices[0].message.content.trim();
return aiResponse;
}
// Chat with the AI in interactive mode
async function chatWithAI(context, userMessage) {
const systemPrompt = `You are a helpful Linux system administration assistant with direct command-line access to a Debian/Ubuntu-based Linux container.
You can answer questions, suggest commands, or help with Linux tasks related to the current context. Stay on topic.`;
const userPrompt = `Context:\n${context}\n\nUser says: ${userMessage}`;
const params = {
messages: [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: userPrompt }
],
model: 'llama3-8b-8192',
};
const chatCompletion = await groqClient.chat.completions.create(params);
const aiResponse = chatCompletion.choices[0].message.content.trim();
return aiResponse;
}
function parseCommandsFromAIResponse(aiResponse) {
const lines = aiResponse.split('\n').map(l => l.trim()).filter(l => l.length > 0);
return lines;
}
// Automate the given goal using up to MAX_ITERATIONS
async function automateGoal(context, goal) {
context += `\n\n[NEW AUTOMATION ATTEMPT FOR GOAL: "${goal}"]\n`;
logHeader(`ATTEMPTING TO AUTOMATE GOAL: ${goal}`);
let iteration = 0;
let success = false;
while (iteration < MAX_ITERATIONS && !success) {
iteration++;
logHeader(`ITERATION ${iteration} OF ${MAX_ITERATIONS}`);
logSubHeader('Asking AI for instructions');
const instructions = await askAIForInstructions(context, goal);
console.log("AI PROVIDED COMMANDS:\n" + indentMultiline(instructions));
const commands = parseCommandsFromAIResponse(instructions);
let allCommandsSucceeded = true;
let attemptLog = `Attempt #${iteration}:\nAI instructions:\n${instructions}\n\nCommand results:\n`;
for (const cmd of commands) {
logCommandStart(cmd);
const result = await execCommandInContainer(cmd);
const stdout = result.stdout || '';
const stderr = result.stderr || '';
logCommandResult(stdout, stderr);
attemptLog += `\n> ${cmd}\nstdout:\n${stdout}\nstderr:\n${stderr}\n`;
if (stderr && stderr.trim().length > 0) {
logInfo(`Command failed with error. Will request refined instructions next iteration.`);
allCommandsSucceeded = false;
break;
} else {
logInfo(`Command executed successfully.`);
}
}
context += `\n\n${attemptLog}`;
// If no commands failed, assume success for now.
if (allCommandsSucceeded) {
success = true;
}
}
if (success) {
logHeader("SUCCESS! The goal appears to have been achieved.");
} else {
logHeader("FAILURE TO ACHIEVE GOAL WITHIN MAX ITERATIONS");
logInfo("Below is the final accumulated context/logs:\n" + context);
}
return {context, success};
}
// Start interactive chat loop
async function startChatLoop(context) {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
logHeader("Entering Interactive Chat Mode");
console.log("You can ask the AI about the container or request tasks.");
console.log("Type 'exit' to quit.");
console.log("If the AI suggests commands, run them with 'run <line_number>'.");
console.log('To automate a new goal, type: automate "Your new goal"');
console.log();
let lastAIResponse = "";
async function promptUser() {
rl.question("> ", async (input) => {
if (input.trim().toLowerCase() === 'exit') {
rl.close();
return;
}
// Run a command from AI suggestion:
if (input.startsWith('run ')) {
const lineNum = parseInt(input.replace('run ', '').trim(), 10);
const commands = parseCommandsFromAIResponse(lastAIResponse);
if (!isNaN(lineNum) && lineNum > 0 && lineNum <= commands.length) {
const cmd = commands[lineNum - 1];
logInfo(`Running command from AI suggestion: ${cmd}`);
const result = await execCommandInContainer(cmd);
const stdout = result.stdout || '';
const stderr = result.stderr || '';
logCommandResult(stdout, stderr);
context += `\nUser ran command: ${cmd}\nstdout:\n${stdout}\nstderr:\n${stderr}`;
} else {
console.log("Invalid line number for running command.");
}
return promptUser();
}
// Automate a new goal:
if (input.trim().toLowerCase().startsWith('automate ')) {
const goalMatch = input.match(/^automate\s+["'](.+)["']$/i);
if (goalMatch && goalMatch[1]) {
const newGoal = goalMatch[1];
const result = await automateGoal(context, newGoal);
context = result.context;
return promptUser();
} else {
console.log("To automate a new goal, use: automate \"Your new goal\"");
return promptUser();
}
}
// Otherwise, treat as a normal chat message
lastAIResponse = await chatWithAI(context, input);
console.log("AI:", lastAIResponse);
promptUser();
});
}
promptUser();
}
async function main() {
const args = process.argv.slice(2);
const initialGoal = args.join(' ').trim();
if (!initialGoal) {
console.error("Usage: node script.js \"Your goal here\"");
process.exit(1);
}
let context = "Initial attempt. We have a Debian/Ubuntu container.\n";
context += "Initial Goal: " + initialGoal;
const result = await automateGoal(context, initialGoal);
context = result.context;
await startChatLoop(context);
}
main().catch(err => {
console.error("An error occurred:", err);
});