265 lines
8.5 KiB
JavaScript
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);
|
|
});
|