diff --git a/examples/blackbox_recipes/claude_code/claude_code_runner.py b/examples/blackbox_recipes/claude_code/claude_code_runner.py index 4d7e614c..2058e324 100644 --- a/examples/blackbox_recipes/claude_code/claude_code_runner.py +++ b/examples/blackbox_recipes/claude_code/claude_code_runner.py @@ -45,7 +45,10 @@ def __init__(self, sandbox): async def communicate(self, input: str, timeout=600, check="ignore", error_msg="Command failed") -> str: result = await self._sandbox.run(input, timeout=int(timeout)) if check == "raise" and result.exit_code != 0: - raise RuntimeError(f"{error_msg}: {result.stdout[:200]}") + raise RuntimeError( + f"{error_msg} (exit_code={result.exit_code}): " + f"stdout={result.stdout[:200]} stderr={result.stderr[:200]}" + ) return result.stdout async def write_file(self, path: str | Path, content: str) -> None: diff --git a/examples/blackbox_recipes/sandbox_client.py b/examples/blackbox_recipes/sandbox_client.py index 7fa05f2a..10602a5a 100644 --- a/examples/blackbox_recipes/sandbox_client.py +++ b/examples/blackbox_recipes/sandbox_client.py @@ -213,21 +213,23 @@ async def create( raise RuntimeError(f"Failed to create sandbox after {max_retries} retries") from last_error - async def run(self, cmd: str, *, timeout: int = 600) -> CommandResult: - """Execute *cmd* inside the sandbox via ``sandbox.commands.run``.""" + async def run( + self, cmd: str, *, timeout: int = 600, + cwd: str | None = None, envs: dict[str, str] | None = None + ) -> CommandResult: + """Run *cmd* in a one-shot shell; stderr is merged into stdout.""" + shell = None try: - result = await asyncio.to_thread( - self._sandbox.commands.run, - cmd, - timeout=timeout, - ) - return CommandResult( - stdout=getattr(result, "stdout", ""), - stderr=getattr(result, "stderr", ""), - exit_code=getattr(result, "exit_code", -1), - ) + shell = await self._sandbox.shells.create(cwd=cwd, envs=envs) + return await shell.run(cmd, timeout=timeout) except Exception as e: return CommandResult(stdout="", stderr=str(e), exit_code=-1) + finally: + if shell is not None: + try: + await shell.kill() + except Exception: + pass async def cleanup(self) -> None: """Kill the sandbox if still running."""