|
|
@@ -443,6 +443,31 @@ export const ShellTool = Tool.define(
|
|
|
let expired = false
|
|
|
let aborted = false
|
|
|
|
|
|
+ const closeSink = Effect.fnUntraced(function* () {
|
|
|
+ const stream = sink
|
|
|
+ if (!stream) return
|
|
|
+ sink = undefined
|
|
|
+ if (stream.destroyed || stream.closed) return
|
|
|
+ yield* Effect.promise(
|
|
|
+ () =>
|
|
|
+ new Promise<void>((resolve) => {
|
|
|
+ let settled = false
|
|
|
+ const done = () => {
|
|
|
+ if (settled) return
|
|
|
+ settled = true
|
|
|
+ stream.off("close", done)
|
|
|
+ stream.off("error", done)
|
|
|
+ stream.off("finish", done)
|
|
|
+ resolve()
|
|
|
+ }
|
|
|
+ stream.once("close", done)
|
|
|
+ stream.once("error", done)
|
|
|
+ stream.once("finish", done)
|
|
|
+ stream.end(done)
|
|
|
+ }),
|
|
|
+ ).pipe(Effect.catch(() => Effect.void))
|
|
|
+ })
|
|
|
+
|
|
|
yield* ctx.metadata({
|
|
|
metadata: {
|
|
|
output: "",
|
|
|
@@ -452,6 +477,7 @@ export const ShellTool = Tool.define(
|
|
|
|
|
|
const code: number | null = yield* Effect.scoped(
|
|
|
Effect.gen(function* () {
|
|
|
+ yield* Effect.addFinalizer(closeSink)
|
|
|
const handle = yield* spawner.spawn(cmd(input.shell, input.command, input.cwd, input.env))
|
|
|
|
|
|
yield* Effect.forkScoped(
|
|
|
@@ -555,17 +581,6 @@ export const ShellTool = Tool.define(
|
|
|
if (meta.length > 0) {
|
|
|
output += "\n\n<shell_metadata>\n" + meta.join("\n") + "\n</shell_metadata>"
|
|
|
}
|
|
|
- if (sink) {
|
|
|
- const stream = sink
|
|
|
- yield* Effect.promise(
|
|
|
- () =>
|
|
|
- new Promise<void>((resolve) => {
|
|
|
- stream.end(() => resolve())
|
|
|
- stream.on("error", () => resolve())
|
|
|
- }),
|
|
|
- )
|
|
|
- }
|
|
|
-
|
|
|
return {
|
|
|
title: input.description,
|
|
|
metadata: {
|