//// // Runs commands and such on the server. // This is the "shell" - the "terminal" is the browser UI. import type { CommandResult } from "./shared/types" import { sessionStore } from "./session" import { commandExists, loadCommandModule } from "./commands" import { ALS } from "./session" export async function runCommand(sessionId: string, taskId: string, input: string, ws?: any): Promise { const [cmd = "", ...args] = input.split(" ") return runCommandFn({ sessionId, taskId, ws }, async () => exec(cmd, args)) } export async function runCommandFn( { sessionId, taskId, ws }: { sessionId: string, taskId?: string, ws?: any }, fn: () => Promise ): Promise { try { const state = sessionStore(sessionId, taskId, ws) return processExecOutput(await ALS.run(state, async () => fn())) } catch (err) { return { status: "error", output: errorMessage(err) } } } async function exec(cmd: string, args: string[]): Promise { if (!commandExists(cmd)) return { status: "error", output: `${cmd} not found` } const module = await loadCommandModule(cmd) if (module?.game) return { status: "ok", output: { game: cmd } } if (!module || !module.default) return { status: "error", output: `${cmd} has no default export` } return await module.default(...args) } export function processExecOutput(output: string | any): CommandResult { if (typeof output === "object" && "status" in output && "output" in output) return output if (typeof output === "string") { return { status: "ok", output } } else if (typeof output === "object") { if (output.error) { return { status: "error", output: output.error } } else if (isJSX(output)) { return { status: "ok", output: { html: output.toString() } } } else if (output.html && isJSX(output.html)) { output.html = output.html.toString() return { status: "ok", output } } else { return { status: "ok", output } } } else if (output === undefined) { return { status: "ok", output: "" } } else { return { status: "ok", output: String(output) } } } function errorMessage(error: Error | any): string { if (!(error instanceof Error)) return String(error) let msg = `${error.name}: ${error.message}` if (error.stack) msg += `\n${error.stack}` return msg } function isJSX(obj: any): boolean { return typeof obj === 'object' && 'tag' in obj && 'props' in obj && 'children' in obj }