@@ -9,6 +9,37 @@ function getStdoutSize() {
99 }
1010}
1111
12+ function isRetryableError ( err : unknown ) : boolean {
13+ // Retry on SDK TimeoutError
14+ if ( err instanceof ( e2b as any ) . TimeoutError ) return true
15+
16+ // Some environments throw AbortError for aborted/timeout fetches
17+ if ( err && typeof err === 'object' && ( err as any ) . name === 'AbortError' ) return true
18+
19+ // Network/system-level transient errors commonly exposed via code property
20+ const code = ( err as any ) ?. code ?? ( err as any ) ?. cause ?. code
21+ const retryableCodes = new Set ( [
22+ 'ECONNRESET' ,
23+ 'ECONNREFUSED' ,
24+ 'ECONNABORTED' ,
25+ 'EPIPE' ,
26+ 'ETIMEDOUT' ,
27+ 'ENOTFOUND' ,
28+ 'EAI_AGAIN' ,
29+ 'EHOSTUNREACH' ,
30+ 'EADDRINUSE' ,
31+ ] )
32+ if ( typeof code === 'string' && retryableCodes . has ( code ) ) return true
33+
34+ // Undici/Fetch may surface as TypeError: fetch failed with nested cause
35+ if ( ( err as any ) instanceof TypeError ) {
36+ const msg = String ( ( err as any ) . message || '' ) . toLowerCase ( )
37+ if ( msg . includes ( 'fetch failed' ) || msg . includes ( 'network error' ) ) return true
38+ }
39+
40+ return false
41+ }
42+
1243export async function spawnConnectedTerminal ( sandbox : e2b . Sandbox ) {
1344 // Clear local terminal emulator before starting terminal
1445 // process.stdout.write('\x1b[2J\x1b[0f')
@@ -26,11 +57,25 @@ export async function spawnConnectedTerminal(sandbox: e2b.Sandbox) {
2657
2758 const inputQueue = new BatchedQueue < Buffer > ( async ( batch ) => {
2859 const combined = Buffer . concat ( batch )
29- await sandbox . pty . sendInput ( terminalSession . pid , combined )
60+
61+ const maxRetries = 3
62+ let retry = 0
63+ do {
64+ try {
65+ await sandbox . pty . sendInput ( terminalSession . pid , combined )
66+ break
67+ } catch ( err ) {
68+ if ( ! isRetryableError ( err ) ) {
69+ // Do not retry on errors that come with valid HTTP/gRPC responses
70+ throw err
71+ }
72+ retry ++
73+ }
74+ } while ( retry < maxRetries )
3075 } , FLUSH_INPUT_INTERVAL_MS )
3176
3277 const resizeListener = process . stdout . on ( 'resize' , ( ) =>
33- sandbox . pty . resize ( terminalSession . pid , getStdoutSize ( ) )
78+ sandbox . pty . resize ( terminalSession . pid , getStdoutSize ( ) ) ,
3479 )
3580 const stdinListener = process . stdin . on ( 'data' , ( data ) => {
3681 inputQueue . push ( data )
@@ -69,8 +114,9 @@ class BatchedQueue<T> {
69114
70115 constructor (
71116 private flushHandler : ( batch : T [ ] ) => Promise < void > ,
72- private flushIntervalMs : number
73- ) { }
117+ private flushIntervalMs : number ,
118+ ) {
119+ }
74120
75121 push ( item : T ) {
76122 this . queue . push ( item )
0 commit comments