11import { spawn } from 'child_process' ;
22
3+ import { v4 as uuidv4 } from 'uuid' ;
34import { z } from 'zod' ;
45import { zodToJsonSchema } from 'zod-to-json-schema' ;
56
@@ -102,13 +103,13 @@ export const shellStartTool: Tool<Parameters, ReturnType> = {
102103
103104 return new Promise ( ( resolve ) => {
104105 try {
105- // Register this shell process with the shell tracker and get the shellId
106- const shellId = shellTracker . registerShell ( command ) ;
106+ // Generate a unique ID for this process
107+ const shellId = uuidv4 ( ) ;
107108
108- let hasResolved = false ;
109+ // Register this shell process with the shell tracker
110+ shellTracker . registerShell ( command ) ;
109111
110- // Flag to track if we're in forced async mode (timeout=0)
111- const forceAsyncMode = timeout === 0 ;
112+ let hasResolved = false ;
112113
113114 // Determine if we need to use a special approach for stdin content
114115 const isWindows =
@@ -118,8 +119,8 @@ export const shellStartTool: Tool<Parameters, ReturnType> = {
118119 if ( stdinContent && stdinContent . length > 0 ) {
119120 // Replace literal \\n with actual newlines and \\t with actual tabs
120121 stdinContent = stdinContent
121- . replace ( / \\ \\ n / g, '\ \n' )
122- . replace ( / \\ \\ t / g, '\ \t' ) ;
122+ . replace ( / \\ n / g, '\n' )
123+ . replace ( / \\ t / g, '\t' ) ;
123124
124125 if ( isWindows ) {
125126 // Windows approach using PowerShell
@@ -222,41 +223,26 @@ export const shellStartTool: Tool<Parameters, ReturnType> = {
222223 signaled : signal !== null ,
223224 } ) ;
224225
225- // If we're in forced async mode (timeout=0), always return async results
226- if ( forceAsyncMode ) {
227- if ( ! hasResolved ) {
228- hasResolved = true ;
229- resolve ( {
230- mode : 'async' ,
231- shellId,
232- stdout : processState . stdout . join ( '' ) . trim ( ) ,
233- stderr : processState . stderr . join ( '' ) . trim ( ) ,
234- ...( code !== 0 && {
235- error : `Process exited with code ${ code } ${ signal ? ` and signal ${ signal } ` : '' } ` ,
236- } ) ,
237- } ) ;
238- }
239- } else {
240- // Normal behavior - return sync results if the process completes quickly
241- if ( ! hasResolved ) {
242- hasResolved = true ;
243- // If we haven't resolved yet, this happened within the timeout
244- // so return sync results
245- resolve ( {
246- mode : 'sync' ,
247- stdout : processState . stdout . join ( '' ) . trim ( ) ,
248- stderr : processState . stderr . join ( '' ) . trim ( ) ,
249- exitCode : code ?? 1 ,
250- ...( code !== 0 && {
251- error : `Process exited with code ${ code } ${ signal ? ` and signal ${ signal } ` : '' } ` ,
252- } ) ,
253- } ) ;
254- }
226+ // For test environment with timeout=0, we should still return sync results
227+ // when the process completes quickly
228+ if ( ! hasResolved ) {
229+ hasResolved = true ;
230+ // If we haven't resolved yet, this happened within the timeout
231+ // so return sync results
232+ resolve ( {
233+ mode : 'sync' ,
234+ stdout : processState . stdout . join ( '' ) . trim ( ) ,
235+ stderr : processState . stderr . join ( '' ) . trim ( ) ,
236+ exitCode : code ?? 1 ,
237+ ...( code !== 0 && {
238+ error : `Process exited with code ${ code } ${ signal ? ` and signal ${ signal } ` : '' } ` ,
239+ } ) ,
240+ } ) ;
255241 }
256242 } ) ;
257243
258244 // For test environment, when timeout is explicitly set to 0, we want to force async mode
259- if ( forceAsyncMode ) {
245+ if ( timeout === 0 ) {
260246 // Force async mode immediately
261247 hasResolved = true ;
262248 resolve ( {
@@ -303,21 +289,17 @@ export const shellStartTool: Tool<Parameters, ReturnType> = {
303289 } ,
304290 { logger } ,
305291 ) => {
306- logger . log ( `Command: ${ command } ` ) ;
307- logger . log ( `Description: ${ description } ` ) ;
308- if ( timeout !== DEFAULT_TIMEOUT ) {
309- logger . log ( `Timeout: ${ timeout } ms` ) ;
310- }
311- if ( showStdIn ) {
312- logger . log ( `Show stdin: ${ showStdIn } ` ) ;
313- }
314- if ( showStdout ) {
315- logger . log ( `Show stdout: ${ showStdout } ` ) ;
316- }
317- if ( stdinContent ) {
318- logger . log (
319- `With stdin content: ${ stdinContent . slice ( 0 , 50 ) } ${ stdinContent . length > 50 ? '...' : '' } ` ,
320- ) ;
292+ logger . log (
293+ `Running "${ command } ", ${ description } (timeout: ${ timeout } ms, showStdIn: ${ showStdIn } , showStdout: ${ showStdout } ${ stdinContent ? ', with stdin content' : '' } )` ,
294+ ) ;
295+ } ,
296+ logReturns : ( output , { logger } ) => {
297+ if ( output . mode === 'async' ) {
298+ logger . log ( `Process started with instance ID: ${ output . shellId } ` ) ;
299+ } else {
300+ if ( output . exitCode !== 0 ) {
301+ logger . error ( `Process quit with exit code: ${ output . exitCode } ` ) ;
302+ }
321303 }
322304 } ,
323305} ;
0 commit comments