|
@@ -50,6 +50,7 @@ import { fn } from "@/util/fn"
|
|
|
import { SessionProcessor } from "./processor"
|
|
import { SessionProcessor } from "./processor"
|
|
|
import { TaskTool } from "@/tool/task"
|
|
import { TaskTool } from "@/tool/task"
|
|
|
import { SessionStatus } from "./status"
|
|
import { SessionStatus } from "./status"
|
|
|
|
|
+import { Shell } from "@/shell/shell"
|
|
|
|
|
|
|
|
// @ts-ignore
|
|
// @ts-ignore
|
|
|
globalThis.AI_SDK_LOG_WARNINGS = false
|
|
globalThis.AI_SDK_LOG_WARNINGS = false
|
|
@@ -1172,6 +1173,12 @@ export namespace SessionPrompt {
|
|
|
})
|
|
})
|
|
|
export type ShellInput = z.infer<typeof ShellInput>
|
|
export type ShellInput = z.infer<typeof ShellInput>
|
|
|
export async function shell(input: ShellInput) {
|
|
export async function shell(input: ShellInput) {
|
|
|
|
|
+ const abort = start(input.sessionID)
|
|
|
|
|
+ if (!abort) {
|
|
|
|
|
+ throw new Session.BusyError(input.sessionID)
|
|
|
|
|
+ }
|
|
|
|
|
+ using _ = defer(() => cancel(input.sessionID))
|
|
|
|
|
+
|
|
|
const session = await Session.get(input.sessionID)
|
|
const session = await Session.get(input.sessionID)
|
|
|
if (session.revert) {
|
|
if (session.revert) {
|
|
|
SessionRevert.cleanup(session)
|
|
SessionRevert.cleanup(session)
|
|
@@ -1244,8 +1251,10 @@ export namespace SessionPrompt {
|
|
|
},
|
|
},
|
|
|
}
|
|
}
|
|
|
await Session.updatePart(part)
|
|
await Session.updatePart(part)
|
|
|
- const shell = process.env["SHELL"] ?? (process.platform === "win32" ? process.env["COMSPEC"] || "cmd.exe" : "bash")
|
|
|
|
|
- const shellName = path.basename(shell).toLowerCase()
|
|
|
|
|
|
|
+ const shell = Shell.preferred()
|
|
|
|
|
+ const shellName = (
|
|
|
|
|
+ process.platform === "win32" ? path.win32.basename(shell, ".exe") : path.basename(shell)
|
|
|
|
|
+ ).toLowerCase()
|
|
|
|
|
|
|
|
const invocations: Record<string, { args: string[] }> = {
|
|
const invocations: Record<string, { args: string[] }> = {
|
|
|
nu: {
|
|
nu: {
|
|
@@ -1275,12 +1284,15 @@ export namespace SessionPrompt {
|
|
|
`,
|
|
`,
|
|
|
],
|
|
],
|
|
|
},
|
|
},
|
|
|
- // Windows cmd.exe
|
|
|
|
|
- "cmd.exe": {
|
|
|
|
|
|
|
+ // Windows cmd
|
|
|
|
|
+ cmd: {
|
|
|
args: ["/c", input.command],
|
|
args: ["/c", input.command],
|
|
|
},
|
|
},
|
|
|
// Windows PowerShell
|
|
// Windows PowerShell
|
|
|
- "powershell.exe": {
|
|
|
|
|
|
|
+ powershell: {
|
|
|
|
|
+ args: ["-NoProfile", "-Command", input.command],
|
|
|
|
|
+ },
|
|
|
|
|
+ pwsh: {
|
|
|
args: ["-NoProfile", "-Command", input.command],
|
|
args: ["-NoProfile", "-Command", input.command],
|
|
|
},
|
|
},
|
|
|
// Fallback: any shell that doesn't match those above
|
|
// Fallback: any shell that doesn't match those above
|
|
@@ -1327,11 +1339,34 @@ export namespace SessionPrompt {
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
|
|
+ let aborted = false
|
|
|
|
|
+ let exited = false
|
|
|
|
|
+
|
|
|
|
|
+ const kill = () => Shell.killTree(proc, { exited: () => exited })
|
|
|
|
|
+
|
|
|
|
|
+ if (abort.aborted) {
|
|
|
|
|
+ aborted = true
|
|
|
|
|
+ await kill()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const abortHandler = () => {
|
|
|
|
|
+ aborted = true
|
|
|
|
|
+ void kill()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ abort.addEventListener("abort", abortHandler, { once: true })
|
|
|
|
|
+
|
|
|
await new Promise<void>((resolve) => {
|
|
await new Promise<void>((resolve) => {
|
|
|
proc.on("close", () => {
|
|
proc.on("close", () => {
|
|
|
|
|
+ exited = true
|
|
|
|
|
+ abort.removeEventListener("abort", abortHandler)
|
|
|
resolve()
|
|
resolve()
|
|
|
})
|
|
})
|
|
|
})
|
|
})
|
|
|
|
|
+
|
|
|
|
|
+ if (aborted) {
|
|
|
|
|
+ output += "\n\n" + ["<metadata>", "User aborted the command", "</metadata>"].join("\n")
|
|
|
|
|
+ }
|
|
|
msg.time.completed = Date.now()
|
|
msg.time.completed = Date.now()
|
|
|
await Session.updateMessage(msg)
|
|
await Session.updateMessage(msg)
|
|
|
if (part.state.status === "running") {
|
|
if (part.state.status === "running") {
|