@@ -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 cache = yield * InstanceState . make (
@@ -808,23 +811,35 @@ NOTE: At any point in time through this workflow you should feel free to ask the
808811 nu : { args : [ "-c" , input . command ] } ,
809812 fish : { args : [ "-c" , input . command ] } ,
810813 zsh : {
811- args : [ "-l" , "-c" , input . command ] ,
814+ args : [
815+ "-l" ,
816+ "-c" ,
817+ `
818+ __oc_cwd=$PWD
819+ [[ -f ~/.zshenv ]] && source ~/.zshenv >/dev/null 2>&1 || true
820+ [[ -f "\${ZDOTDIR:-$HOME}/.zshrc" ]] && source "\${ZDOTDIR:-$HOME}/.zshrc" >/dev/null 2>&1 || true
821+ cd "$__oc_cwd"
822+ eval ${ JSON . stringify ( input . command ) }
823+ ` ,
824+ ] ,
812825 } ,
813826 bash : {
814827 args : [
815828 "-l" ,
816829 "-c" ,
817830 `
831+ __oc_cwd=$PWD
818832 shopt -s expand_aliases
819833 [[ -f ~/.bashrc ]] && source ~/.bashrc >/dev/null 2>&1 || true
834+ cd "$__oc_cwd"
820835 eval ${ JSON . stringify ( input . command ) }
821836 ` ,
822837 ] ,
823838 } ,
824839 cmd : { args : [ "/c" , input . command ] } ,
825840 powershell : { args : [ "-NoProfile" , "-Command" , input . command ] } ,
826841 pwsh : { args : [ "-NoProfile" , "-Command" , input . command ] } ,
827- "" : { args : [ "-c" , ` ${ input . command } ` ] } ,
842+ "" : { args : [ "-c" , input . command ] } ,
828843 }
829844
830845 const args = ( invocations [ shellName ] ?? invocations [ "" ] ) . args
@@ -834,51 +849,20 @@ NOTE: At any point in time through this workflow you should feel free to ask the
834849 { cwd, sessionID : input . sessionID , callID : part . callID } ,
835850 { env : { } } ,
836851 )
837- const proc = yield * Effect . sync ( ( ) =>
838- spawn ( sh , args , {
839- cwd,
840- detached : process . platform !== "win32" ,
841- windowsHide : process . platform === "win32" ,
842- stdio : [ "ignore" , "pipe" , "pipe" ] ,
843- env : {
844- ...process . env ,
845- ...shellEnv . env ,
846- TERM : "dumb" ,
847- } ,
848- } ) ,
849- )
850-
851- let output = ""
852- const write = ( ) => {
853- if ( part . state . status !== "running" ) return
854- part . state . metadata = { output, description : "" }
855- void Effect . runFork ( sessions . updatePart ( part ) )
856- }
857852
858- proc . stdout ?. on ( "data" , ( chunk ) => {
859- output += chunk . toString ( )
860- write ( )
861- } )
862- proc . stderr ?. on ( "data" , ( chunk ) => {
863- output += chunk . toString ( )
864- 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" ,
865859 } )
866860
861+ let output = ""
867862 let aborted = false
868- let exited = false
869- let finished = false
870- const kill = Effect . promise ( ( ) => Shell . killTree ( proc , { exited : ( ) => exited } ) )
871-
872- const abortHandler = ( ) => {
873- if ( aborted ) return
874- aborted = true
875- void Effect . runFork ( kill )
876- }
877863
878864 const finish = Effect . uninterruptible (
879865 Effect . gen ( function * ( ) {
880- if ( finished ) return
881- finished = true
882866 if ( aborted ) {
883867 output += "\n\n" + [ "<metadata>" , "User aborted the command" , "</metadata>" ] . join ( "\n" )
884868 }
@@ -900,20 +884,26 @@ NOTE: At any point in time through this workflow you should feel free to ask the
900884 } ) ,
901885 )
902886
903- const exit = yield * Effect . promise ( ( ) => {
904- signal . addEventListener ( "abort" , abortHandler , { once : true } )
905- if ( signal . aborted ) abortHandler ( )
906- return new Promise < void > ( ( resolve ) => {
907- const close = ( ) => {
908- exited = true
909- proc . off ( "close" , close )
910- resolve ( )
911- }
912- proc . once ( "close" , close )
913- } )
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
914899 } ) . pipe (
915- Effect . onInterrupt ( ( ) => Effect . sync ( abortHandler ) ) ,
916- 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 ,
917907 Effect . ensuring ( finish ) ,
918908 Effect . exit ,
919909 )
@@ -1727,6 +1717,7 @@ NOTE: At any point in time through this workflow you should feel free to ask the
17271717 Layer . provide ( Session . defaultLayer ) ,
17281718 Layer . provide ( Agent . defaultLayer ) ,
17291719 Layer . provide ( Bus . layer ) ,
1720+ Layer . provide ( CrossSpawnSpawner . defaultLayer ) ,
17301721 ) ,
17311722 ) ,
17321723 )
0 commit comments