From 90482142d28163d97cf41ef0a832b9ba14a05ff7 Mon Sep 17 00:00:00 2001 From: Raven Scott Date: Tue, 10 Dec 2024 04:10:46 -0500 Subject: [PATCH] first commit --- .gitignore | 3 + README.md | 204 +++++++++++++++++++++++++++++++++++++++++++++++++++ agent.mjs | 165 +++++++++++++++++++++++++++++++++++++++++ default.env | 2 + package.json | 7 ++ 5 files changed, 381 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 agent.mjs create mode 100644 default.env create mode 100644 package.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..941d536 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.env +node_modules +package-lock.json \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..1310eed --- /dev/null +++ b/README.md @@ -0,0 +1,204 @@ +# Remote Container Manager + +## Table of Contents + +- [Overview](#overview) +- [Features](#features) +- [Prerequisites](#prerequisites) +- [Installation](#installation) +- [Configuration](#configuration) +- [Usage](#usage) +- [Logging](#logging) +- [Troubleshooting](#troubleshooting) +- [Contributing](#contributing) +- [License](#license) + +## Overview + +**Remote Container Manager** is a Node.js script designed to automate the management of remote Debian/Ubuntu-based Linux containers. Leveraging AI-driven command generation and robust API integrations, this tool simplifies tasks such as package installation and service management by executing precise shell commands on remote containers. + +## Features + +- **AI-Powered Command Generation:** Utilizes AI to generate shell commands based on your specified goals and context. +- **Verbose Logging:** Detailed and formatted logs make it easy to track the script's actions and outputs. +- **API Integration:** Connects seamlessly with Groq and Discord Linux APIs to execute commands remotely. +- **Iterative Execution:** Attempts to achieve the desired state through multiple iterations, refining commands based on previous outcomes. +- **Environment Configuration:** Easily manage API keys and other settings via environment variables. + +## Prerequisites + +Before you begin, ensure you have met the following requirements: + +- **Node.js:** Version 14.0 or higher. [Download Node.js](https://nodejs.org/) +- **Git:** For cloning the repository. [Download Git](https://git-scm.com/) +- **API Access:** + - **Groq API Key** + - **Discord Linux API Key** + +## Installation + +1. **Clone the Repository** + + ```bash + git clone https://github.com/yourusername/remote-container-manager.git + cd remote-container-manager + ``` + +2. **Install Dependencies** + + Ensure you have [Node.js](https://nodejs.org/) installed. Then, install the required packages: + + ```bash + npm install + ``` + +## Configuration + +1. **Create Environment Variables** + + The script relies on environment variables for API keys and configurations. An example file named `default.env` is provided. Follow these steps to set up your environment variables: + + - **Copy the Example `.env` File** + + ```bash + cp default.env .env + ``` + + - **Edit the `.env` File** + + Open the `.env` file in your preferred text editor and replace the placeholder values with your actual API keys. + + ```env + # .env + + DISCORD_LINUX_API_URL=https://api.ssh.surf + DISCORD_LINUX_API_KEY=your_discord_linux_api_key + GROQ_API_KEY=your_groq_api_key + ``` + + - **Environment Variables Explained** + + | Variable | Description | + | ---------------------- | -------------------------------------------------- | + | `DISCORD_LINUX_API_URL`| The base URL for the Discord Linux API. | + | `DISCORD_LINUX_API_KEY`| Your API key for authenticating with Discord Linux. | + | `GROQ_API_KEY` | Your Groq API key for AI-driven command generation.| + +## Usage + +To run the script, use the following command: + +```bash +node index.js +``` + +**Example Goal:** + +```javascript +const goal = 'install apache2 (apache2) on the container and run it with service'; +``` + +**Script Flow:** + +1. **Initialization:** Sets up the Groq client and logs the starting process. +2. **AI Instruction Request:** Sends the current context and goal to the AI to receive shell commands. +3. **Command Execution:** Executes each command on the remote container, logging the output. +4. **Iteration:** Repeats the process up to a maximum number of iterations (`MAX_ITERATIONS`) until the goal is achieved or attempts are exhausted. + +## Logging + +The script features comprehensive and formatted logging to ensure transparency and ease of monitoring. + +### Log Components + +- **Headers:** Clearly marked sections indicating the start of processes and iterations. +- **Subheaders:** Indicate specific actions like requesting AI instructions. +- **Info Messages:** Provide status updates on command execution success or failure. +- **Command Execution Logs:** Detailed output of each executed command, including `stdout` and `stderr`. + +**Example Log Output:** + +``` +════════════════════════════════════════════════════════════════════════════════════════ +═ STARTING PROCESS TO ACHIEVE GOAL: install apache2 (apache2) on the container and run it with service +════════════════════════════════════════════════════════════════════════════════════════ + +════════════════════════════════════════════════════════════════════════════════════════ +═ ITERATION 1 OF 5 +════════════════════════════════════════════════════════════════════════════════════════ + +------------------------------------------------------------ +> Asking AI for instructions +------------------------------------------------------------ + +AI PROVIDED COMMANDS: +sudo apt-get update +sudo apt-get install -y apache2 +sudo service apache2 start + +[EXECUTING COMMAND] +$ sudo apt-get update + +[STDOUT]: + Get:1 http://archive.ubuntu.com/ubuntu focal InRelease [265 kB] + ... + +[STDERR]: (empty) + +INFO: Command executed successfully. + +... + +SUCCESS! The goal appears to have been achieved. +``` + +## Troubleshooting + +If you encounter issues while running the script, consider the following steps: + +1. **Check Environment Variables:** + - Ensure all required environment variables are set correctly in the `.env` file. + - Verify that API keys are valid and have the necessary permissions. + +2. **API Connectivity:** + - Confirm that the `DISCORD_LINUX_API_URL` is correct and reachable. + - Check your internet connection and any firewall settings that might block API requests. + +3. **Dependencies:** + - Make sure all dependencies are installed by running `npm install`. + - Verify that you're using a compatible Node.js version (14.0 or higher). + +4. **Script Errors:** + - Review the console logs for specific error messages. + - Ensure that the remote container is accessible and configured correctly. + +5. **AI Command Accuracy:** + - If the AI provides incorrect or ineffective commands, consider refining the `systemPrompt` or providing more context. + +## Contributing + +Contributions are welcome! Follow these steps to contribute: + +1. **Fork the Repository** + +2. **Create a Feature Branch** + + ```bash + git checkout -b feature/YourFeature + ``` + +3. **Commit Your Changes** + + ```bash + git commit -m "Add your feature" + ``` + +4. **Push to the Branch** + + ```bash + git push origin feature/YourFeature + ``` + +5. **Open a Pull Request** + +Please ensure your contributions adhere to the project's coding standards and include appropriate documentation. diff --git a/agent.mjs b/agent.mjs new file mode 100644 index 0000000..567dd32 --- /dev/null +++ b/agent.mjs @@ -0,0 +1,165 @@ +import 'dotenv/config'; +import Groq from 'groq-sdk'; +import unirest from 'unirest'; + +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, +}); + +// A small helper for nice verbose logging: +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'); +} + +// Helper to 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; +} + +// This function queries the AI for instructions. It returns a series of commands to try. +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}. +Rules: +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.`; + + 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; +} + +async function main() { + const goal = 'install apache2 (apache2) on the container and run it with service'; + let context = "Initial attempt. No commands have been run yet.\n" + + "We are working with a Debian/Ubuntu container.\n" + + "Goal: " + goal; + + logHeader(`STARTING PROCESS TO ACHIEVE 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 detected in STDERR. Will request refined instructions next iteration.`); + allCommandsSucceeded = false; + break; + } else { + logInfo(`Command executed successfully.`); + } + } + + context += `\n\n${attemptLog}`; + + if (allCommandsSucceeded) { + logInfo("All commands executed successfully."); + success = true; + } else { + logInfo("At least one command failed. The AI will refine approach in next iteration."); + } + } + + 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); + } +} + +main().catch(err => { + console.error("An error occurred:", err); +}); diff --git a/default.env b/default.env new file mode 100644 index 0000000..206ac47 --- /dev/null +++ b/default.env @@ -0,0 +1,2 @@ +DISCORD_LINUX_API_KEY= +GROQ_API_KEY= diff --git a/package.json b/package.json new file mode 100644 index 0000000..35aa778 --- /dev/null +++ b/package.json @@ -0,0 +1,7 @@ +{ + "dependencies": { + "dotenv": "^16.4.7", + "groq-sdk": "^0.9.0", + "unirest": "^0.6.0" + } +}