//// // Runs commands and such on the server. // This is the "shell" - the "terminal" is the browser UI. import type { CommandResult, CommandOutput } from "./shared/types" import type { Session } from "./session" import { commandExists, commandPath } from "./commands" import { ALS } from "./session" const sessions: Map = new Map() export async function runCommand(sessionId: string, taskId: string, input: string, ws?: any): Promise { const [cmd = "", ...args] = input.split(" ") if (!commandExists(cmd)) return { status: "error", output: `${cmd} not found` } let status: "ok" | "error" = "ok" let output: CommandOutput = "" const state = getState(sessionId, taskId, ws) try { [status, output] = await ALS.run(state, async () => await exec(cmd, args)) } catch (err) { status = "error" output = errorMessage(err) } return { status, output } } async function exec(cmd: string, args: string[]): Promise<["ok" | "error", CommandOutput]> { const module = await import(commandPath(cmd) + "?t+" + Date.now()) if (module?.game) return ["ok", { game: cmd }] if (!module || !module.default) return ["error", `${cmd} has no default export`] return processExecOutput(await module.default(...args)) } export function processExecOutput(output: string | any): ["ok" | "error", CommandOutput] { if (typeof output === "string") { return ["ok", output] } else if (typeof output === "object") { if (output.error) { return ["error", output.error] } else if (isJSX(output)) { return ["ok", { html: output.toString() }] } else if (output.html && isJSX(output.html)) { output.html = output.html.toString() return ["ok", output] } else { return ["ok", output] } } else if (output === undefined) { return ["ok", ""] } else { return ["ok", String(output)] } } function getState(sessionId: string, taskId: string, ws?: any): Session { let state = sessions.get(sessionId) if (!state) { state = { sessionId: sessionId, project: "" } sessions.set(sessionId, state) } state.taskId = taskId if (ws) state.ws = ws return state } 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 }