This commit is contained in:
Raven Scott 2024-12-10 04:53:24 -05:00
parent ddb88268da
commit d46ffd5086

View File

@ -14,7 +14,7 @@ const groqClient = new Groq({
apiKey: GROQ_API_KEY, apiKey: GROQ_API_KEY,
}); });
// A small helper for nice verbose logging: // Logging helpers
function logHeader(message) { function logHeader(message) {
console.log('\n' + '═'.repeat(80)); console.log('\n' + '═'.repeat(80));
console.log('═ ' + message); console.log('═ ' + message);
@ -53,7 +53,7 @@ function indentMultiline(text) {
return text.split('\n').map(line => ' ' + line).join('\n'); return text.split('\n').map(line => ' ' + line).join('\n');
} }
// Helper to execute a command in the container: // Execute a command in the container
async function execCommandInContainer(cmd, pwd = '/home') { async function execCommandInContainer(cmd, pwd = '/home') {
const response = await unirest const response = await unirest
.post(`${DISCORD_LINUX_API_URL}/exec`) .post(`${DISCORD_LINUX_API_URL}/exec`)
@ -67,22 +67,22 @@ async function execCommandInContainer(cmd, pwd = '/home') {
return response.body; return response.body;
} }
// This function queries the AI for instructions to achieve a goal. // Ask AI for instructions
async function askAIForInstructions(context, goal) { async function askAIForInstructions(context, goal) {
const systemPrompt = `You are a world-class Linux system administration assistant, given the ability to access and run commands on a remote Debian/Ubuntu-based Linux container. Your mission is to help achieve the following goal: ${goal}. const systemPrompt = `You are a highly skilled Linux system administration assistant with direct command-line access to a Debian/Ubuntu-based Linux container.
Rules: Your mission is to achieve the following goal: "${goal}"
1. Return only shell commands needed, line-by-line, no explanation.
2. If previous attempts failed, refine your approach and fix the issues based on the provided errors and output.
3. If you need to run multiple commands, separate them by new lines.
4. Consider common steps: updating package lists, installing packages, verifying installation.
5. The container might be minimal, so consider installing or fixing repositories if needed.
6. Always ensure commands are non-interactive.
7. Do not use markdown formatting at all ever.
8. All commands are non-interactive
9. If installing packages, always use -y to allow for non-interactive commands
`;
const userPrompt = `CONTEXT:\n${context}\n\nPlease provide the exact shell commands to achieve the goal above.`; 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 = { const params = {
messages: [ messages: [
@ -97,19 +97,10 @@ Rules:
return aiResponse; return aiResponse;
} }
// This function is used for chatting with the AI like a chatbot. It can answer questions, // Chat with the AI in interactive mode
// give advice, or provide commands about the container or any tasks you want to do next.
async function chatWithAI(context, userMessage) { async function chatWithAI(context, userMessage) {
const systemPrompt = `You are a helpful Linux system administration assistant with the ability to access and run commands on a remote Debian/Ubuntu-based Linux container. const systemPrompt = `You are a helpful Linux system administration assistant with direct command-line access to a Debian/Ubuntu-based Linux container.
You can help answer questions about the container state, suggest commands, or assist with Linux-related tasks. You can answer questions, suggest commands, or help with Linux tasks related to the current context. Stay on topic.`;
Rules:
1. You can provide explanations, instructions, and command suggestions.
2. If giving commands, list them clearly line-by-line.
3. For explanations or answers, you can use normal text.
4. Do not use markdown formatting.
5. Keep answers concise and clear.
6. The user may then choose to run commands you suggest.
`;
const userPrompt = `Context:\n${context}\n\nUser says: ${userMessage}`; const userPrompt = `Context:\n${context}\n\nUser says: ${userMessage}`;
@ -131,16 +122,16 @@ function parseCommandsFromAIResponse(aiResponse) {
return lines; return lines;
} }
// This function attempts to automatically achieve a given goal using the AI instructions up to MAX_ITERATIONS // Automate the given goal using up to MAX_ITERATIONS
async function automateGoal(context, goal) { async function automateGoal(context, goal) {
context += `\n\n[NEW AUTOMATION ATTEMPT FOR GOAL: "${goal}"]\n`;
logHeader(`ATTEMPTING TO AUTOMATE GOAL: ${goal}`); logHeader(`ATTEMPTING TO AUTOMATE GOAL: ${goal}`);
let iteration = 0; let iteration = 0;
let success = false; let success = false;
while (iteration < MAX_ITERATIONS && !success) { while (iteration < MAX_ITERATIONS && !success) {
iteration++; iteration++;
logHeader(`ITERATION ${iteration} OF ${MAX_ITERATIONS}`); logHeader(`ITERATION ${iteration} OF ${MAX_ITERATIONS}`);
logSubHeader('Asking AI for instructions'); logSubHeader('Asking AI for instructions');
const instructions = await askAIForInstructions(context, goal); const instructions = await askAIForInstructions(context, goal);
console.log("AI PROVIDED COMMANDS:\n" + indentMultiline(instructions)); console.log("AI PROVIDED COMMANDS:\n" + indentMultiline(instructions));
@ -158,7 +149,6 @@ async function automateGoal(context, goal) {
attemptLog += `\n> ${cmd}\nstdout:\n${stdout}\nstderr:\n${stderr}\n`; attemptLog += `\n> ${cmd}\nstdout:\n${stdout}\nstderr:\n${stderr}\n`;
// If we find a non-empty stderr, we consider it a failure signal
if (stderr && stderr.trim().length > 0) { if (stderr && stderr.trim().length > 0) {
logInfo(`Command failed with error. Will request refined instructions next iteration.`); logInfo(`Command failed with error. Will request refined instructions next iteration.`);
allCommandsSucceeded = false; allCommandsSucceeded = false;
@ -170,11 +160,9 @@ async function automateGoal(context, goal) {
context += `\n\n${attemptLog}`; context += `\n\n${attemptLog}`;
// If no commands failed, assume success for now.
if (allCommandsSucceeded) { if (allCommandsSucceeded) {
logInfo("All commands executed successfully.");
success = true; success = true;
} else {
logInfo("At least one command failed. The AI will refine approach in next iteration.");
} }
} }
@ -188,8 +176,7 @@ async function automateGoal(context, goal) {
return {context, success}; return {context, success};
} }
// After achieving or attempting the goal, we start an interactive chat loop. // Start interactive chat loop
// Added the ability to run "automate \"Your new goal\"" from the chat mode.
async function startChatLoop(context) { async function startChatLoop(context) {
const rl = readline.createInterface({ const rl = readline.createInterface({
input: process.stdin, input: process.stdin,
@ -197,10 +184,11 @@ async function startChatLoop(context) {
}); });
logHeader("Entering Interactive Chat Mode"); logHeader("Entering Interactive Chat Mode");
console.log("You can now ask the AI about the container or request additional tasks."); console.log("You can ask the AI about the container or request tasks.");
console.log("Type 'exit' to quit."); console.log("Type 'exit' to quit.");
console.log("If the AI suggests commands, you can run them by typing 'run <line_number>'."); console.log("If the AI suggests commands, run them with 'run <line_number>'.");
console.log("To re-enter automated mode with a new goal, type: automate \"Your new goal\"\n"); console.log('To automate a new goal, type: automate "Your new goal"');
console.log();
let lastAIResponse = ""; let lastAIResponse = "";
@ -211,7 +199,7 @@ async function startChatLoop(context) {
return; return;
} }
// If the user wants to run a command from the last AI response: // Run a command from AI suggestion:
if (input.startsWith('run ')) { if (input.startsWith('run ')) {
const lineNum = parseInt(input.replace('run ', '').trim(), 10); const lineNum = parseInt(input.replace('run ', '').trim(), 10);
const commands = parseCommandsFromAIResponse(lastAIResponse); const commands = parseCommandsFromAIResponse(lastAIResponse);
@ -229,15 +217,13 @@ async function startChatLoop(context) {
return promptUser(); return promptUser();
} }
// If the user wants to automate a new goal: // Automate a new goal:
if (input.trim().toLowerCase().startsWith('automate ')) { if (input.trim().toLowerCase().startsWith('automate ')) {
const goalMatch = input.match(/^automate\s+["'](.+)["']$/i); const goalMatch = input.match(/^automate\s+["'](.+)["']$/i);
if (goalMatch && goalMatch[1]) { if (goalMatch && goalMatch[1]) {
const newGoal = goalMatch[1]; const newGoal = goalMatch[1];
// Run automated attempts on the new goal
const result = await automateGoal(context, newGoal); const result = await automateGoal(context, newGoal);
context = result.context; context = result.context;
// After finishing automated attempts, return to chat mode
return promptUser(); return promptUser();
} else { } else {
console.log("To automate a new goal, use: automate \"Your new goal\""); console.log("To automate a new goal, use: automate \"Your new goal\"");
@ -245,7 +231,7 @@ async function startChatLoop(context) {
} }
} }
// Otherwise, treat input as a question to the AI // Otherwise, treat as a normal chat message
lastAIResponse = await chatWithAI(context, input); lastAIResponse = await chatWithAI(context, input);
console.log("AI:", lastAIResponse); console.log("AI:", lastAIResponse);
promptUser(); promptUser();
@ -256,7 +242,6 @@ async function startChatLoop(context) {
} }
async function main() { async function main() {
// Retrieve the initial goal from command-line arguments
const args = process.argv.slice(2); const args = process.argv.slice(2);
const initialGoal = args.join(' ').trim(); const initialGoal = args.join(' ').trim();
@ -265,15 +250,12 @@ async function main() {
process.exit(1); process.exit(1);
} }
let context = "Initial attempt. No commands have been run yet.\n" + let context = "Initial attempt. We have a Debian/Ubuntu container.\n";
"We are working with a Debian/Ubuntu container.\n" + context += "Initial Goal: " + initialGoal;
"Goal: " + initialGoal;
// Attempt to achieve the initial goal:
const result = await automateGoal(context, initialGoal); const result = await automateGoal(context, initialGoal);
context = result.context; context = result.context;
// After finishing attempts, start chat mode:
await startChatLoop(context); await startChatLoop(context);
} }