@@ -177,11 +177,23 @@ func (c *AgentHarnessOpenShellClient) ExecSandboxID(ctx context.Context, sandbox
177177 return name , nil
178178}
179179
180+ type ExecSandboxResult struct {
181+ ExitCode int32
182+ Stdout string
183+ Stderr string
184+ }
185+
180186// ExecSandbox runs a command inside the sandbox via OpenShell ExecSandbox streaming RPC.
181187func (c * AgentHarnessOpenShellClient ) ExecSandbox (ctx context.Context , sandboxID string , command []string , stdin []byte , env map [string ]string , timeoutSec uint32 ) (int32 , string , error ) {
188+ res , err := c .ExecSandboxOutput (ctx , sandboxID , command , stdin , env , timeoutSec )
189+ return res .ExitCode , res .Stderr , err
190+ }
191+
192+ // ExecSandboxOutput runs a command inside the sandbox and captures stdout, stderr, and the exit code.
193+ func (c * AgentHarnessOpenShellClient ) ExecSandboxOutput (ctx context.Context , sandboxID string , command []string , stdin []byte , env map [string ]string , timeoutSec uint32 ) (ExecSandboxResult , error ) {
182194 osCli := c .openShell ()
183195 if osCli == nil {
184- return - 1 , "" , fmt .Errorf ("openshell client is nil" )
196+ return ExecSandboxResult { ExitCode : - 1 } , fmt .Errorf ("openshell client is nil" )
185197 }
186198 req := & openshellv1.ExecSandboxRequest {
187199 SandboxId : sandboxID ,
@@ -194,8 +206,9 @@ func (c *AgentHarnessOpenShellClient) ExecSandbox(ctx context.Context, sandboxID
194206 }
195207 stream , err := osCli .ExecSandbox (ctx , req )
196208 if err != nil {
197- return - 1 , "" , err
209+ return ExecSandboxResult { ExitCode : - 1 } , err
198210 }
211+ var stdout strings.Builder
199212 var stderr strings.Builder
200213 var exitCode int32 = - 1
201214 for {
@@ -204,10 +217,13 @@ func (c *AgentHarnessOpenShellClient) ExecSandbox(ctx context.Context, sandboxID
204217 if errors .Is (err , io .EOF ) {
205218 break
206219 }
207- return exitCode , stderr .String (), err
220+ return ExecSandboxResult { ExitCode : exitCode , Stdout : stdout . String (), Stderr : stderr .String ()} , err
208221 }
209222 switch p := ev .GetPayload ().(type ) {
210223 case * openshellv1.ExecSandboxEvent_Stdout :
224+ if p .Stdout != nil {
225+ stdout .Write (p .Stdout .GetData ())
226+ }
211227 case * openshellv1.ExecSandboxEvent_Stderr :
212228 if p .Stderr != nil {
213229 stderr .Write (p .Stderr .GetData ())
@@ -219,9 +235,9 @@ func (c *AgentHarnessOpenShellClient) ExecSandbox(ctx context.Context, sandboxID
219235 }
220236 }
221237 if exitCode == - 1 {
222- return exitCode , stderr .String (), fmt .Errorf ("ExecSandbox finished without exit status" )
238+ return ExecSandboxResult { ExitCode : exitCode , Stdout : stdout . String (), Stderr : stderr .String ()} , fmt .Errorf ("ExecSandbox finished without exit status" )
223239 }
224- return exitCode , stderr .String (), nil
240+ return ExecSandboxResult { ExitCode : exitCode , Stdout : stdout . String (), Stderr : stderr .String ()} , nil
225241}
226242
227243// ErrEmptyResponse is returned when OpenShell returns success with an empty Sandbox payload.
0 commit comments