fix: Use timestamp-based file extraction instead of /output directory

Replace the hardcoded /output directory approach with timestamp-based
file extraction (matching ClaudeCodeBlock's pattern). This removes the
need to create a special directory and captures any files created or
modified during execution in /home/user.

Also fixes a potential UnboundLocalError in sandbox_files.py where
data_uri could be referenced in the except block before assignment,
and moves base64/mimetypes to top-level imports.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Nicholas Tindle
2026-02-11 15:46:57 -06:00
parent a2856c1863
commit 1026f437a9
3 changed files with 21 additions and 21 deletions

View File

@@ -92,8 +92,8 @@ class CodeExecutionResult(MainCodeExecutionResult):
class BaseE2BExecutorMixin:
"""Shared implementation methods for E2B executor blocks."""
# Default output directory for file extraction
OUTPUT_DIR = "/output"
# Default working directory in E2B sandboxes
WORKING_DIR = "/home/user"
async def execute_code(
self,
@@ -115,8 +115,8 @@ class BaseE2BExecutorMixin:
3. Connect to existing sandbox and execute (ExecuteCodeStepBlock)
Args:
extract_files: If True and execution_context provided, extract files from
/output directory and store to workspace.
extract_files: If True and execution_context provided, extract files
created/modified during execution and store to workspace.
""" # noqa
sandbox = None
files: list[SandboxFileOutput] = []
@@ -131,13 +131,16 @@ class BaseE2BExecutorMixin:
sandbox = await AsyncSandbox.create(
api_key=api_key, template=template_id, timeout=timeout
)
# Create /output directory for file extraction
if extract_files:
await sandbox.commands.run(f"mkdir -p {self.OUTPUT_DIR}")
if setup_commands:
for cmd in setup_commands:
await sandbox.commands.run(cmd)
# Capture timestamp before execution to scope file extraction
start_timestamp = None
if extract_files:
ts_result = await sandbox.commands.run("date -u +%Y-%m-%dT%H:%M:%S")
start_timestamp = ts_result.stdout.strip() if ts_result.stdout else None
# Execute the code
execution = await sandbox.run_code(
code,
@@ -153,13 +156,13 @@ class BaseE2BExecutorMixin:
stdout_logs = "".join(execution.logs.stdout)
stderr_logs = "".join(execution.logs.stderr)
# Extract files from /output if requested
# Extract files created/modified during this execution
if extract_files and execution_context:
files = await extract_and_store_sandbox_files(
sandbox=sandbox,
working_directory=self.OUTPUT_DIR,
working_directory=self.WORKING_DIR,
execution_context=execution_context,
since_timestamp=None, # Get all files in /output
since_timestamp=start_timestamp,
text_only=False, # Include binary files too
)
@@ -277,7 +280,7 @@ class ExecuteCodeBlock(Block, BaseE2BExecutorMixin):
stderr_logs: str = SchemaField(description="Standard error logs from execution")
files: list[SandboxFileOutput] = SchemaField(
description=(
"Files written to /output directory during execution. "
"Files created or modified during execution. "
"Each file has path, name, content, and workspace_ref (if stored)."
),
)

View File

@@ -5,7 +5,9 @@ This module provides common file extraction and workspace storage functionality
for blocks that run code in E2B sandboxes (Claude Code, Code Executor, etc.).
"""
import base64
import logging
import mimetypes
import shlex
from dataclasses import dataclass
from typing import TYPE_CHECKING
@@ -217,18 +219,13 @@ async def store_sandbox_files(
else:
content_str = f"[Binary file: {len(file.content)} bytes]"
# Build data URI (needed for storage and as binary fallback)
mime_type = mimetypes.guess_type(file.name)[0] or "application/octet-stream"
data_uri = f"data:{mime_type};base64,{base64.b64encode(file.content).decode()}"
# Try to store in workspace
workspace_ref: str | None = None
try:
# Convert bytes to data URI for store_media_file
import base64
import mimetypes
mime_type = mimetypes.guess_type(file.name)[0] or "application/octet-stream"
data_uri = (
f"data:{mime_type};base64,{base64.b64encode(file.content).decode()}"
)
result = await store_media_file(
file=MediaFileType(data_uri),
execution_context=execution_context,

View File

@@ -215,7 +215,7 @@ The sandbox includes pip and npm pre-installed. Set timeout to limit execution t
| response | Text output (if any) of the main execution result | str |
| stdout_logs | Standard output logs from execution | str |
| stderr_logs | Standard error logs from execution | str |
| files | Files written to /output directory during execution. Each file has path, name, content, and workspace_ref (if stored). | List[SandboxFileOutput] |
| files | Files created or modified during execution. Each file has path, name, content, and workspace_ref (if stored). | List[SandboxFileOutput] |
### Possible use case
<!-- MANUAL: use_case -->