Reaperxxxx commited on
Commit
357a130
Β·
verified Β·
1 Parent(s): 2a89c1d

Update server.js

Browse files
Files changed (1) hide show
  1. server.js +56 -7
server.js CHANGED
@@ -71,7 +71,9 @@ const RECOMMENDED_CMDS = [
71
  ];
72
  const BLOCKED_CMDS = ["sudo","su","reboot","shutdown","mkfs","fdisk"];
73
  function isSafeCmd(cmd) {
 
74
  const name = cmd.trim().split(/\s+/)[0];
 
75
  return !BLOCKED_CMDS.includes(name);
76
  }
77
 
@@ -162,8 +164,13 @@ const DESTRUCTIVE_PATTERNS = [
162
  function filterCommands(commands, alreadyRun = []) {
163
  const safe = [];
164
  const skipped = [];
165
- const runSet = new Set(alreadyRun.map(c => c.trim()));
166
- for (const cmd of commands) {
 
 
 
 
 
167
  const trimmed = cmd.trim();
168
  if (!trimmed) continue;
169
  if (runSet.has(trimmed)) { skipped.push({ cmd: trimmed, reason: "duplicate" }); continue; }
@@ -235,7 +242,31 @@ function parseJSON(raw) {
235
  return null;
236
  }
237
 
238
- // ── EXTRACT COMMANDS FROM TEXT ─────────────────────────────────────────────
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
239
  // When AI returns markdown/prose instead of JSON, pull shell commands from it
240
  function extractCommandsFromText(text) {
241
  const cmds = [];
@@ -629,8 +660,13 @@ async function runAgentLoop(opts) {
629
  // Deduplicates against already-run commands, structures output, guards retries
630
  const cmdRetryCount = {}; // tracks per-cmd-pattern retry attempts
631
  async function runBatch(commands) {
 
 
 
 
 
632
  const alreadyRunCmds = allCommandResults.map(r => r.cmd);
633
- const { safe, skipped } = filterCommands(commands, alreadyRunCmds);
634
 
635
  // Notify about skipped commands so AI sees them in history
636
  for (const s of skipped) {
@@ -809,10 +845,16 @@ Respond with ONLY raw JSON:
809
  "commands": ["cp ${filePath} ${filePath}.bak", "..."],
810
  "reasoning": "what sections you need to read and why",
811
  "done": false
812
- }`;
 
 
 
 
 
 
813
 
814
  const planRaw = await callAI(planPrompt, "", modelKey, transcript);
815
- let plan = parseJSON(planRaw);
816
 
817
  if (!plan || (!plan.commands && !plan.read_file)) {
818
  const extracted = extractCommandsFromText(planRaw || "");
@@ -892,10 +934,17 @@ B) Full file rewrite (for major changes like library swaps):
892
  "reasoning": "why a full rewrite is needed"
893
  }
894
 
 
 
 
 
 
 
 
895
  CRITICAL: done:true only when sed -n CONFIRMS the change is physically in the file, OR full_rewrite:true is set.`;
896
 
897
  const contRaw = await callAI(continuePrompt, "", modelKey, transcript);
898
- let cont = parseJSON(contRaw);
899
 
900
  if (!cont) {
901
  const extracted = extractCommandsFromText(contRaw || "");
 
71
  ];
72
  const BLOCKED_CMDS = ["sudo","su","reboot","shutdown","mkfs","fdisk"];
73
  function isSafeCmd(cmd) {
74
+ if (cmd == null || typeof cmd !== "string") return false;
75
  const name = cmd.trim().split(/\s+/)[0];
76
+ if (!name) return false;
77
  return !BLOCKED_CMDS.includes(name);
78
  }
79
 
 
164
  function filterCommands(commands, alreadyRun = []) {
165
  const safe = [];
166
  const skipped = [];
167
+ const runSet = new Set(alreadyRun.map(c => (typeof c === "string" ? c.trim() : "")));
168
+ // Normalise input: flatten, unwrap {cmd} objects, drop anything not a string
169
+ const flat = (Array.isArray(commands) ? commands : [])
170
+ .flat(3)
171
+ .map(c => (c != null && typeof c === "object" && typeof c.cmd === "string") ? c.cmd : c)
172
+ .filter(c => c != null && typeof c === "string");
173
+ for (const cmd of flat) {
174
  const trimmed = cmd.trim();
175
  if (!trimmed) continue;
176
  if (runSet.has(trimmed)) { skipped.push({ cmd: trimmed, reason: "duplicate" }); continue; }
 
242
  return null;
243
  }
244
 
245
+ // ── SANITIZE AI RESPONSE ───────────────────────────────────────────────────
246
+ // Ensures "commands" is always a flat array of non-empty strings.
247
+ // The AI occasionally returns nulls, objects, nested arrays, or step-label strings.
248
+ function sanitizeAIResponse(parsed) {
249
+ if (!parsed) return parsed;
250
+ if (parsed.commands !== undefined) {
251
+ const raw = Array.isArray(parsed.commands) ? parsed.commands : [];
252
+ parsed.commands = raw
253
+ .flat(5)
254
+ .map(c => {
255
+ if (c == null) return null;
256
+ if (typeof c === "string") return c.trim() || null;
257
+ // Unwrap {cmd: "..."} or {command: "..."} objects
258
+ if (typeof c === "object") {
259
+ const v = c.cmd || c.command || c.shell || c.run || c.exec;
260
+ return typeof v === "string" ? v.trim() || null : null;
261
+ }
262
+ return null;
263
+ })
264
+ .filter(c => c != null && c.length > 0 && isSafeCmd(c));
265
+ }
266
+ return parsed;
267
+ }
268
+
269
+
270
  // When AI returns markdown/prose instead of JSON, pull shell commands from it
271
  function extractCommandsFromText(text) {
272
  const cmds = [];
 
660
  // Deduplicates against already-run commands, structures output, guards retries
661
  const cmdRetryCount = {}; // tracks per-cmd-pattern retry attempts
662
  async function runBatch(commands) {
663
+ // Normalise before anything else β€” AI sometimes returns nulls, objects, nested arrays
664
+ const normalised = (Array.isArray(commands) ? commands : [])
665
+ .flat(3)
666
+ .map(c => (c != null && typeof c === "object" && typeof c.cmd === "string") ? c.cmd : c)
667
+ .filter(c => c != null && typeof c === "string" && c.trim().length > 0);
668
  const alreadyRunCmds = allCommandResults.map(r => r.cmd);
669
+ const { safe, skipped } = filterCommands(normalised, alreadyRunCmds);
670
 
671
  // Notify about skipped commands so AI sees them in history
672
  for (const s of skipped) {
 
845
  "commands": ["cp ${filePath} ${filePath}.bak", "..."],
846
  "reasoning": "what sections you need to read and why",
847
  "done": false
848
+ }
849
+
850
+ COMMANDS FORMAT RULES β€” VIOLATIONS WILL CRASH THE SYSTEM:
851
+ - "commands" MUST be a flat array of plain strings only
852
+ - NEVER put null, objects, or nested arrays inside "commands"
853
+ - NEVER omit "commands" β€” use [] if no commands needed this round
854
+ - Every entry must be a complete, executable bash command string`;
855
 
856
  const planRaw = await callAI(planPrompt, "", modelKey, transcript);
857
+ let plan = sanitizeAIResponse(parseJSON(planRaw));
858
 
859
  if (!plan || (!plan.commands && !plan.read_file)) {
860
  const extracted = extractCommandsFromText(planRaw || "");
 
934
  "reasoning": "why a full rewrite is needed"
935
  }
936
 
937
+ COMMANDS FORMAT RULES β€” VIOLATIONS WILL CRASH THE SYSTEM:
938
+ - "commands" MUST be a flat array of plain strings. Example: ["sed -i '7s/get/head/' bot.js"]
939
+ - NEVER put objects, null, arrays-within-arrays, or non-strings in "commands"
940
+ - NEVER omit "commands" β€” use [] if you have no commands to run
941
+ - EVERY command must be a complete shell command runnable in bash
942
+ - NEVER include comments, explanations, or step labels inside the commands array
943
+
944
  CRITICAL: done:true only when sed -n CONFIRMS the change is physically in the file, OR full_rewrite:true is set.`;
945
 
946
  const contRaw = await callAI(continuePrompt, "", modelKey, transcript);
947
+ let cont = sanitizeAIResponse(parseJSON(contRaw));
948
 
949
  if (!cont) {
950
  const extracted = extractCommandsFromText(contRaw || "");