85 lines
2.4 KiB
TypeScript
85 lines
2.4 KiB
TypeScript
////
|
|
// runs commands and such.
|
|
|
|
import type { CommandResult, CommandOutput } from "./shared/types"
|
|
import type { State } from "./state"
|
|
import { join } from "path"
|
|
import { NOSE_SYS_BIN, NOSE_BIN } from "./config"
|
|
import { isFile } from "./utils"
|
|
import { ALS } from "./state"
|
|
|
|
const sessions: Map<string, State> = new Map()
|
|
|
|
export async function runCommand(session: string, id: string, input: string): Promise<CommandResult> {
|
|
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(session, id)
|
|
|
|
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 || !module.default)
|
|
return ["error", `${cmd} has no default export`]
|
|
|
|
return processExecOutput(await module.default(...args))
|
|
}
|
|
|
|
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 {
|
|
return ["ok", output]
|
|
}
|
|
} else {
|
|
return ["ok", String(output)]
|
|
}
|
|
}
|
|
|
|
function getState(session: string, id: string): State {
|
|
let state = sessions.get(session)
|
|
if (!state) {
|
|
state = { session, project: "" }
|
|
sessions.set(session, state)
|
|
}
|
|
state.id = id
|
|
return state
|
|
}
|
|
|
|
function commandPath(cmd: string): string | undefined {
|
|
const sysPath = join(NOSE_SYS_BIN, cmd + ".ts")
|
|
const usrPath = join(NOSE_BIN, cmd + ".ts")
|
|
|
|
return (isFile(sysPath) && sysPath) || (isFile(usrPath) && usrPath) || undefined
|
|
}
|
|
|
|
function commandExists(cmd: string): boolean {
|
|
return commandPath(cmd) !== undefined
|
|
}
|
|
|
|
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
|
|
} |