-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathagent-chat.mjs
264 lines (218 loc) · 8.5 KB
/
agent-chat.mjs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
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);
});