diff --git a/nose/bin/echo.ts b/nose/bin/echo.ts new file mode 100644 index 0000000..0e06a22 --- /dev/null +++ b/nose/bin/echo.ts @@ -0,0 +1,3 @@ +export default function (...args: string[]): string { + return args.join(" ") +} \ No newline at end of file diff --git a/src/server.tsx b/src/server.tsx index 0fe9753..85f611f 100644 --- a/src/server.tsx +++ b/src/server.tsx @@ -54,12 +54,12 @@ app.on("GET", ["/js/:path{.+}", "/shared/:path{.+}"], async c => { const wsConnections: any[] = [] -app.get("/ws", upgradeWebSocket((c) => { +app.get("/ws", upgradeWebSocket(async c => { return { onOpen(_e, ws) { wsConnections.push(ws) }, - onMessage(event, ws) { + async onMessage(event, ws) { let data: Message | undefined try { @@ -72,7 +72,7 @@ app.get("/ws", upgradeWebSocket((c) => { if (!data) return - const result = runCommand(data.data as string) + const result = await runCommand(data.data as string) ws.send(JSON.stringify({ id: data.id, type: "output", data: result })) }, onClose: () => console.log('Connection closed'), diff --git a/src/shell.ts b/src/shell.ts index 118e863..9fd75fa 100644 --- a/src/shell.ts +++ b/src/shell.ts @@ -2,8 +2,44 @@ // runs commands and such. import type { CommandResult } from "./shared/types" +import { join, resolve } from "path" import { NOSE_BIN } from "./config" +import { isFile } from "./utils" -export function runCommand(input: string): CommandResult { - return { status: "ok", output: "command: " + input } +export async function runCommand(input: string): Promise { + const [cmd = "", ...args] = input.split(" ") + + if (!commandExists(cmd)) { + return { status: "error", output: `${cmd} not found` } + } + + let status: "ok" | "error" = "ok" + let output = "" + + try { + [status, output] = await exec(cmd, args) + } catch (err) { + status = "error" + output = err instanceof Error ? err.message : String(err) + } + + console.log("cmd", cmd) + console.log("args", args) + + return { status, output } +} + +function commandExists(cmd: string): boolean { + return isFile(join(NOSE_BIN, cmd + ".ts")) +} + +async function exec(cmd: string, args: string[]): Promise<["ok" | "error", string]> { + const path = join(NOSE_BIN, cmd + ".ts") + const module = await import(path + "?t+" + Date.now()) + + if (!module || !module.default) { + return ["error", `${cmd} has no default export`] + } + + return ["ok", await module.default(...args)] } \ No newline at end of file