@@ -501,6 +501,7 @@ func shellStopBackground(arguments map[string]interface{}) (*protocol.CallToolRe
501501func shellBuildCommand (ctx context.Context , command string ) (* exec.Cmd , error ) {
502502 cmd := exec .CommandContext (ctx , shellPlatformShell (), shellPlatformShellArg (), command )
503503 cmd .Env = shellCommandEnv (false )
504+ shellSetProcessGroup (cmd )
504505 return cmd , nil
505506}
506507
@@ -511,28 +512,42 @@ func shellBuildPtyCommand(ctx context.Context, command string) (gopty.Pty, *gopt
511512 }
512513 cmd := ptmx .CommandContext (ctx , shellPlatformShell (), shellPlatformShellArg (), command )
513514 cmd .Env = shellCommandEnv (true )
515+ shellSetProcessGroupPty (cmd )
514516 return ptmx , cmd , nil
515517}
516518
517519func shellRunForeground (ctx context.Context , command string , workdir string , usePTY bool ) (string , error ) {
518- cmd , err := shellBuildCommand (ctx , command )
519- if err != nil {
520- return fmt .Sprintf ("Error: %s" , err .Error ()), err
520+ if ! usePTY {
521+ return shellRunForegroundCmd (ctx , command , workdir )
521522 }
523+ return shellRunForegroundPTY (ctx , command , workdir )
524+ }
522525
523- if ! usePTY {
524- if workdir != "" {
525- cmd .Dir = workdir
526- }
526+ func shellRunForegroundCmd (ctx context.Context , command string , workdir string ) (string , error ) {
527+ cmd := exec .Command (shellPlatformShell (), shellPlatformShellArg (), command )
528+ cmd .Env = shellCommandEnv (false )
529+ shellSetProcessGroup (cmd )
530+ if workdir != "" {
531+ cmd .Dir = workdir
532+ }
533+
534+ var stdout , stderr bytes.Buffer
535+ cmd .Stdout = & stdout
536+ cmd .Stderr = & stderr
527537
528- var stdout , stderr bytes. Buffer
529- cmd . Stdout = & stdout
530- cmd . Stderr = & stderr
538+ if startErr := cmd . Start (); startErr != nil {
539+ return fmt . Sprintf ( "Error: %s" , startErr . Error ()), startErr
540+ }
531541
532- runErr := cmd .Run ()
542+ waitDone := make (chan error , 1 )
543+ go func () {
544+ waitDone <- cmd .Wait ()
545+ }()
546+
547+ select {
548+ case runErr := <- waitDone :
533549 stdoutStr := stdout .String ()
534550 stderrStr := stderr .String ()
535-
536551 if runErr != nil {
537552 var parts []string
538553 parts = append (parts , fmt .Sprintf ("Error: %s" , runErr .Error ()))
@@ -544,7 +559,6 @@ func shellRunForeground(ctx context.Context, command string, workdir string, use
544559 }
545560 return strings .Join (parts , "\n " ), runErr
546561 }
547-
548562 result := stdoutStr
549563 if stderrStr != "" {
550564 if result != "" {
@@ -557,8 +571,15 @@ func shellRunForeground(ctx context.Context, command string, workdir string, use
557571 result = "(no output)"
558572 }
559573 return result , nil
574+
575+ case <- ctx .Done ():
576+ shellKillProcessGroup (cmd .Process )
577+ <- waitDone
578+ return "" , ctx .Err ()
560579 }
580+ }
561581
582+ func shellRunForegroundPTY (ctx context.Context , command string , workdir string ) (string , error ) {
562583 ptmx , ptyCmd , err := shellBuildPtyCommand (ctx , command )
563584 if err != nil {
564585 return fmt .Sprintf ("Error: %s" , err .Error ()), err
@@ -586,25 +607,34 @@ func shellRunForeground(ctx context.Context, command string, workdir string, use
586607 copyDone <- nil
587608 }()
588609
589- runErr := ptyCmd .Wait ()
590- _ = ptmx .Close ()
591- copyErr := <- copyDone
610+ waitDone := make (chan error , 1 )
611+ go func () {
612+ waitDone <- ptyCmd .Wait ()
613+ }()
592614
593- if ctx .Err () == context .DeadlineExceeded {
594- return "" , ctx .Err ()
595- }
596- if copyErr != nil && runErr == nil {
597- runErr = copyErr
598- }
615+ select {
616+ case runErr := <- waitDone :
617+ _ = ptmx .Close ()
618+ copyErr := <- copyDone
619+ if copyErr != nil && runErr == nil {
620+ runErr = copyErr
621+ }
622+ text := output .String ()
623+ if text == "" {
624+ text = "(no output)"
625+ }
626+ if runErr != nil {
627+ return fmt .Sprintf ("Error: %s\n Output:\n %s" , runErr .Error (), text ), runErr
628+ }
629+ return text , nil
599630
600- text := output . String ()
601- if text == "" {
602- text = "(no output)"
603- }
604- if runErr != nil {
605- return fmt . Sprintf ( "Error: %s \n Output: \n %s " , runErr . Error (), text ), runErr
631+ case <- ctx . Done ():
632+ shellKillProcessGroup ( ptyCmd . Process )
633+ _ = ptmx . Close ()
634+ <- waitDone
635+ <- copyDone
636+ return " " , ctx . Err ()
606637 }
607- return text , nil
608638}
609639
610640func shellStringArg (arguments map [string ]interface {}, key string , defaultValue string ) string {
@@ -971,13 +1001,13 @@ func (s *shellSession) stop() {
9711001 return
9721002 }
9731003 if err := process .Signal (os .Interrupt ); err != nil {
974- _ = process . Kill ( )
1004+ shellKillProcessGroup ( process )
9751005 return
9761006 }
9771007 select {
9781008 case <- done :
9791009 case <- time .After (2 * time .Second ):
980- _ = process . Kill ( )
1010+ shellKillProcessGroup ( process )
9811011 }
9821012}
9831013
0 commit comments