@@ -28,7 +28,9 @@ import { ReadTool } from "../tool/read"
2828import { FileTime } from "../file/time"
2929import { Flag } from "../flag/flag"
3030import { ulid } from "ulid"
31- import { spawn } from "child_process"
31+ import { ChildProcess , ChildProcessSpawner } from "effect/unstable/process"
32+ import * as CrossSpawnSpawner from "@/effect/cross-spawn-spawner"
33+ import * as Stream from "effect/Stream"
3234import { Command } from "../command"
3335import { pathToFileURL , fileURLToPath } from "url"
3436import { ConfigMarkdown } from "../config/markdown"
@@ -96,6 +98,7 @@ export namespace SessionPrompt {
9698 const filetime = yield * FileTime . Service
9799 const registry = yield * ToolRegistry . Service
98100 const truncate = yield * Truncate . Service
101+ const spawner = yield * ChildProcessSpawner . ChildProcessSpawner
99102 const scope = yield * Scope . Scope
100103
101104 const state = yield * InstanceState . make (
@@ -809,30 +812,34 @@ NOTE: At any point in time through this workflow you should feel free to ask the
809812 fish : { args : [ "-c" , input . command ] } ,
810813 zsh : {
811814 args : [
812- "-c" ,
813815 "-l" ,
816+ "-c" ,
814817 `
818+ __oc_cwd=$PWD
815819 [[ -f ~/.zshenv ]] && source ~/.zshenv >/dev/null 2>&1 || true
816820 [[ -f "\${ZDOTDIR:-$HOME}/.zshrc" ]] && source "\${ZDOTDIR:-$HOME}/.zshrc" >/dev/null 2>&1 || true
821+ cd "$__oc_cwd"
817822 eval ${ JSON . stringify ( input . command ) }
818823 ` ,
819824 ] ,
820825 } ,
821826 bash : {
822827 args : [
823- "-c" ,
824828 "-l" ,
829+ "-c" ,
825830 `
831+ __oc_cwd=$PWD
826832 shopt -s expand_aliases
827833 [[ -f ~/.bashrc ]] && source ~/.bashrc >/dev/null 2>&1 || true
834+ cd "$__oc_cwd"
828835 eval ${ JSON . stringify ( input . command ) }
829836 ` ,
830837 ] ,
831838 } ,
832839 cmd : { args : [ "/c" , input . command ] } ,
833840 powershell : { args : [ "-NoProfile" , "-Command" , input . command ] } ,
834841 pwsh : { args : [ "-NoProfile" , "-Command" , input . command ] } ,
835- "" : { args : [ "-c" , ` ${ input . command } ` ] } ,
842+ "" : { args : [ "-c" , input . command ] } ,
836843 }
837844
838845 const args = ( invocations [ shellName ] ?? invocations [ "" ] ) . args
@@ -842,51 +849,20 @@ NOTE: At any point in time through this workflow you should feel free to ask the
842849 { cwd, sessionID : input . sessionID , callID : part . callID } ,
843850 { env : { } } ,
844851 )
845- const proc = yield * Effect . sync ( ( ) =>
846- spawn ( sh , args , {
847- cwd,
848- detached : process . platform !== "win32" ,
849- windowsHide : process . platform === "win32" ,
850- stdio : [ "ignore" , "pipe" , "pipe" ] ,
851- env : {
852- ...process . env ,
853- ...shellEnv . env ,
854- TERM : "dumb" ,
855- } ,
856- } ) ,
857- )
858852
859- let output = ""
860- const write = ( ) => {
861- if ( part . state . status !== "running" ) return
862- part . state . metadata = { output, description : "" }
863- void Effect . runFork ( sessions . updatePart ( part ) )
864- }
865-
866- proc . stdout ?. on ( "data" , ( chunk ) => {
867- output += chunk . toString ( )
868- write ( )
869- } )
870- proc . stderr ?. on ( "data" , ( chunk ) => {
871- output += chunk . toString ( )
872- write ( )
853+ const cmd = ChildProcess . make ( sh , args , {
854+ cwd,
855+ extendEnv : true ,
856+ env : { ...shellEnv . env , TERM : "dumb" } ,
857+ stdin : "ignore" ,
858+ forceKillAfter : "3 seconds" ,
873859 } )
874860
861+ let output = ""
875862 let aborted = false
876- let exited = false
877- let finished = false
878- const kill = Effect . promise ( ( ) => Shell . killTree ( proc , { exited : ( ) => exited } ) )
879-
880- const abortHandler = ( ) => {
881- if ( aborted ) return
882- aborted = true
883- void Effect . runFork ( kill )
884- }
885863
886864 const finish = Effect . uninterruptible (
887865 Effect . gen ( function * ( ) {
888- if ( finished ) return
889- finished = true
890866 if ( aborted ) {
891867 output += "\n\n" + [ "<metadata>" , "User aborted the command" , "</metadata>" ] . join ( "\n" )
892868 }
@@ -908,20 +884,26 @@ NOTE: At any point in time through this workflow you should feel free to ask the
908884 } ) ,
909885 )
910886
911- const exit = yield * Effect . promise ( ( ) => {
912- signal . addEventListener ( "abort" , abortHandler , { once : true } )
913- if ( signal . aborted ) abortHandler ( )
914- return new Promise < void > ( ( resolve ) => {
915- const close = ( ) => {
916- exited = true
917- proc . off ( "close" , close )
918- resolve ( )
919- }
920- proc . once ( "close" , close )
921- } )
887+ const exit = yield * Effect . gen ( function * ( ) {
888+ const handle = yield * spawner . spawn ( cmd )
889+ yield * Stream . runForEach ( Stream . decodeText ( handle . all ) , ( chunk ) =>
890+ Effect . sync ( ( ) => {
891+ output += chunk
892+ if ( part . state . status === "running" ) {
893+ part . state . metadata = { output, description : "" }
894+ void Effect . runFork ( sessions . updatePart ( part ) )
895+ }
896+ } ) ,
897+ )
898+ yield * handle . exitCode
922899 } ) . pipe (
923- Effect . onInterrupt ( ( ) => Effect . sync ( abortHandler ) ) ,
924- Effect . ensuring ( Effect . sync ( ( ) => signal . removeEventListener ( "abort" , abortHandler ) ) ) ,
900+ Effect . scoped ,
901+ Effect . onInterrupt ( ( ) =>
902+ Effect . sync ( ( ) => {
903+ aborted = true
904+ } ) ,
905+ ) ,
906+ Effect . orDie ,
925907 Effect . ensuring ( finish ) ,
926908 Effect . exit ,
927909 )
@@ -1735,6 +1717,7 @@ NOTE: At any point in time through this workflow you should feel free to ask the
17351717 Layer . provide ( Session . defaultLayer ) ,
17361718 Layer . provide ( Agent . defaultLayer ) ,
17371719 Layer . provide ( Bus . layer ) ,
1720+ Layer . provide ( CrossSpawnSpawner . defaultLayer ) ,
17381721 ) ,
17391722 ) ,
17401723 )
0 commit comments