fix(e2b): preserve signals after output drain
This commit is contained in:
@@ -512,7 +512,9 @@ export class E2BSubprocessHandle implements SubprocessHandle {
|
|||||||
this.stdoutReader?.invalidateSpill()
|
this.stdoutReader?.invalidateSpill()
|
||||||
this.stderrReader?.invalidateSpill()
|
this.stderrReader?.invalidateSpill()
|
||||||
await handle.disconnect()
|
await handle.disconnect()
|
||||||
return { exitCode, signal: null }
|
return this.terminationSignal === null
|
||||||
|
? { exitCode, signal: null }
|
||||||
|
: { exitCode: null, signal: this.terminationSignal }
|
||||||
}
|
}
|
||||||
const completed = await Promise.race([settlement, waitTick().then(() => undefined)])
|
const completed = await Promise.race([settlement, waitTick().then(() => undefined)])
|
||||||
if (completed !== undefined) return this.commandOutcome(completed)
|
if (completed !== undefined) return this.commandOutcome(completed)
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ class FakeSandbox {
|
|||||||
readonly signalErrors: unknown[] = []
|
readonly signalErrors: unknown[] = []
|
||||||
trapsTerm = false
|
trapsTerm = false
|
||||||
delaysKill = false
|
delaysKill = false
|
||||||
|
delaysKillCompletion = false
|
||||||
sdkKillStops = true
|
sdkKillStops = true
|
||||||
alive = true
|
alive = true
|
||||||
ambient = 'PATH=/ambient/bin\0KEEP=safe\0NPM_TOKEN=secret\0DSH_STALE=old\0BROKEN\0=bad\0'
|
ambient = 'PATH=/ambient/bin\0KEEP=safe\0NPM_TOKEN=secret\0DSH_STALE=old\0BROKEN\0=bad\0'
|
||||||
@@ -247,7 +248,7 @@ class FakeSandbox {
|
|||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
if (!this.delaysKill) this.alive = false
|
if (!this.delaysKill) this.alive = false
|
||||||
this.handle.fail(137)
|
if (!this.delaysKillCompletion) this.handle.fail(137)
|
||||||
return { exitCode: 0, stdout: '', stderr: '' }
|
return { exitCode: 0, stdout: '', stderr: '' }
|
||||||
}
|
}
|
||||||
if ((options as StartOptions | undefined)?.background === true) {
|
if ((options as StartOptions | undefined)?.background === true) {
|
||||||
@@ -473,6 +474,25 @@ describe('E2BSubprocessHandle', () => {
|
|||||||
expect(fake.handle.disconnects).toBe(0)
|
expect(fake.handle.disconnects).toBe(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('preserves a requested signal when output draining expires', async () => {
|
||||||
|
const fake = new FakeSandbox()
|
||||||
|
fake.trapsTerm = true
|
||||||
|
fake.delaysKill = true
|
||||||
|
fake.delaysKillCompletion = true
|
||||||
|
fake.sdkKillStops = false
|
||||||
|
const handle = new E2BSubprocessHandle(runtime(fake), spec({ graceMs: 5 }), '/runtime/drain-signal')
|
||||||
|
await flush()
|
||||||
|
|
||||||
|
handle.terminate()
|
||||||
|
await vi.waitFor(() => { expect(fake.commandsSeen).toContain('kill -KILL -- -4242') })
|
||||||
|
fake.exitStatus = '143\n'
|
||||||
|
|
||||||
|
await expect(handle.done).resolves.toEqual({ exitCode: null, signal: 'SIGKILL' })
|
||||||
|
expect(fake.handle.disconnects).toBe(1)
|
||||||
|
fake.alive = false
|
||||||
|
await expect(handle.waitForExit()).resolves.toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
it('rejects an invalid direct-command exit status', async () => {
|
it('rejects an invalid direct-command exit status', async () => {
|
||||||
const fake = new FakeSandbox()
|
const fake = new FakeSandbox()
|
||||||
const handle = new E2BSubprocessHandle(runtime(fake), spec(), '/runtime/invalid-status')
|
const handle = new E2BSubprocessHandle(runtime(fake), spec(), '/runtime/invalid-status')
|
||||||
|
|||||||
Reference in New Issue
Block a user