const vm = require("vm"); const { spawn } = require("child_process"); const fs = require("fs"); const os = require("os"); const path = require("path"); const { prepareJavaScriptCommand, preparePythonCommand, resolveJavaScriptHandler } = require("./command-code"); function buildCommandContext({ platform, user, message, args, argsText }) { return { platform, user, message, args: args || [], argsText: argsText || "" }; } async function runAdvancedCommand({ code, language }, ctx) { if (language === "python") { return await runPythonCommand(code, ctx); } return await runJsCommand(code, ctx); } async function runJsCommand(code, ctx) { const logs = []; const safeConsole = { log: (...args) => logs.push(args.join(" ")) }; const sandbox = { ctx, console: safeConsole, module: { exports: {} }, exports: {} }; const context = vm.createContext(sandbox); const script = new vm.Script(prepareJavaScriptCommand(code), { filename: "command.js" }); script.runInContext(context, { timeout: 1000 }); const handler = resolveJavaScriptHandler(context); if (typeof handler !== "function") { throw new Error("Dynamic command did not create a runnable handler."); } const result = handler(ctx); if (result && typeof result.then === "function") { return await promiseWithTimeout(result, 1500); } return result; } async function runPythonCommand(code, ctx) { return await new Promise((resolve, reject) => { const payload = JSON.stringify(ctx); const encoded = Buffer.from(preparePythonCommand(code), "utf8").toString("base64"); const script = ` import base64, json, sys, traceback ctx = json.loads(sys.stdin.read() or "{}") code = base64.b64decode("${encoded}").decode("utf-8") globals_dict = {} try: exec(code, globals_dict) if "run" not in globals_dict: raise Exception("Dynamic command did not create a runnable handler.") result = globals_dict["run"](ctx) if result is None: sys.exit(0) if isinstance(result, (dict, list)): print(json.dumps(result)) else: print(str(result)) except Exception: traceback.print_exc() sys.exit(2) `; const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "lumi-cmd-")); const filePath = path.join(tmpDir, "runner.py"); fs.writeFileSync(filePath, script, "utf8"); const child = spawn("python", ["-u", filePath], { stdio: ["pipe", "pipe", "pipe"] }); let stdout = ""; let stderr = ""; const timeout = setTimeout(() => { child.kill("SIGKILL"); reject(new Error("Python command timed out.")); }, 2000); child.stdout.on("data", (chunk) => { stdout += chunk.toString(); }); child.stderr.on("data", (chunk) => { stderr += chunk.toString(); }); child.on("error", (error) => { clearTimeout(timeout); cleanupTemp(tmpDir); reject(error); }); child.on("close", (code) => { clearTimeout(timeout); cleanupTemp(tmpDir); if (code && code !== 0) { reject(new Error(stderr || "Python command failed.")); return; } resolve(stdout.trim()); }); child.stdin.write(payload); child.stdin.end(); }); } function cleanupTemp(dir) { try { fs.rmSync(dir, { recursive: true, force: true }); } catch { // Ignore cleanup errors. } } function normalizeCommandResult(result) { if (result === null || result === undefined) { return ""; } if (typeof result === "string") { return result; } if (typeof result === "number" || typeof result === "boolean") { return String(result); } if (typeof result === "object" && result.content) { return String(result.content); } try { return JSON.stringify(result); } catch { return String(result); } } function promiseWithTimeout(promise, timeoutMs) { return new Promise((resolve, reject) => { const timeout = setTimeout(() => { reject(new Error("Command timed out.")); }, timeoutMs); promise .then((value) => { clearTimeout(timeout); resolve(value); }) .catch((error) => { clearTimeout(timeout); reject(error); }); }); } module.exports = { buildCommandContext, runAdvancedCommand, normalizeCommandResult };