fix(e2b): harden remote process lifecycle

This commit is contained in:
Tianyi Cui
2026-07-29 05:31:22 +08:00
parent 8877f5d582
commit fd78d9bc58
18 changed files with 586 additions and 153 deletions
+63 -5
View File
@@ -3,6 +3,64 @@
import { Buffer } from 'node:buffer'
import type { SubprocessOutputRead, SubprocessOutputReader } from '@deepseek-ai/dsh-subprocess'
const BASE64_TEXT = /^[A-Za-z0-9+/]+={0,2}$/u
/** Reserved non-base64 frame proving that one remote encoder reached clean EOF. */
export const E2B_OUTPUT_COMPLETE_FRAME = '!dsh-e2b-output-complete!'
/** Incrementally decode newline-delimited base64 frames emitted by one remote encoder. */
export class E2BBase64Decoder {
private pending = ''
private complete = false
/**
* Decode every complete newline-delimited frame in one arbitrarily split SDK callback.
* @param text - ASCII base64 frames from E2B's decoded callback.
* @returns the complete raw bytes made available by this callback.
*/
push(text: string): Buffer {
if (text.length === 0) return Buffer.alloc(0)
this.pending += text
const decoded: Buffer[] = []
for (;;) {
const boundary = this.pending.indexOf('\n')
if (boundary < 0) break
const frame = this.pending.slice(0, boundary)
this.pending = this.pending.slice(boundary + 1)
if (frame === E2B_OUTPUT_COMPLETE_FRAME) {
if (this.complete) throw new Error('subprocess-e2b: duplicate output transport completion')
this.complete = true
continue
}
if (this.complete) throw new Error('subprocess-e2b: output transport continued after completion')
if (!BASE64_TEXT.test(frame)) {
throw new Error('subprocess-e2b: invalid base64 output transport')
}
const bytes = Buffer.from(frame, 'base64')
if (bytes.toString('base64') !== frame) {
throw new Error('subprocess-e2b: invalid base64 output transport')
}
decoded.push(bytes)
}
return Buffer.concat(decoded)
}
/**
* Validate clean encoder completion, or discard an interrupted trailing frame after requested termination.
* @param requireComplete - Whether natural completion requires the reserved EOF frame.
*/
finish(requireComplete = true): void {
if (!requireComplete) {
this.pending = ''
return
}
if (this.pending.length > 0) {
throw new Error('subprocess-e2b: truncated base64 output transport')
}
if (!this.complete) throw new Error('subprocess-e2b: incomplete output transport')
}
}
/** Offset reader used for one collect-mode E2B stream. */
export class E2BOutputReader implements SubprocessOutputReader {
private chunks: Buffer[] = []
@@ -27,12 +85,12 @@ export class E2BOutputReader implements SubprocessOutputReader {
}
/**
* Append one decoded SDK output event.
* @param text - Event text delivered by E2B.
* Append one byte-faithful decoded transport event.
* @param bytes - Raw command bytes recovered from the ASCII SDK transport.
*/
push(text: string): void {
if (text.length === 0) return
const chunk = Buffer.from(text)
push(bytes: Uint8Array): void {
if (bytes.length === 0) return
const chunk = Buffer.from(bytes)
this.totalBytes += chunk.length
this.chunks.push(chunk)
this.retainedBytes += chunk.length