# sandboxes
Universal library for AI code execution sandboxes.
[](https://www.python.org/downloads/)
[](https://opensource.org/licenses/MIT)
## Overview
`sandboxes` provides a unified interface for sandboxed code execution across multiple providers:
- **Current providers**: E2B, Modal, Daytona
- **Experimental**: Cloudflare (requires self-hosted Worker deployment)
Write your code once and switch between providers with a single line change, or let the library automatically select a provider.
Includes a Python API plus full-featured CLI for use from any runtime.
## Installation
Add to your project:
```bash
uv add cased-sandboxes
```
or install with your preferred Python package manager and use the CLI
for any language, e.g.,:
```bash
uv pip install cased-sandboxes
```
## Quick Start
### One-line Execution + Auto-select Provider
```python
import asyncio
from sandboxes import run
async def main():
# Creates a temporary sandbox, runs the command, then destroys the sandbox
result = await run("echo 'Hello from sandbox!'")
print(result.stdout)
# Behind the scenes, run() does this:
# 1. Auto-detects available providers (e.g., E2B, Modal, Daytona)
# 2. Creates a new sandbox with the first available provider
# 3. Executes your command in that isolated environment
# 4. Returns the result
# 5. Automatically destroys the sandbox
asyncio.run(main())
```
### Multiple Commands
```python
import asyncio
from sandboxes import run_many
async def main():
# Execute multiple commands in one sandbox
results = await run_many([
"pip install requests",
"python -c 'import requests; print(requests.__version__)'"
])
for result in results:
print(result.stdout)
asyncio.run(main())
```
### Persistent Sandbox Sessions
```python
import asyncio
from sandboxes import Sandbox
async def main():
async with Sandbox.create() as sandbox:
# Install dependencies
await sandbox.execute("pip install numpy pandas")
# Run your code
result = await sandbox.execute("python analyze.py")
print(result.stdout)
await sandbox.upload("data.csv", "/tmp/data.csv")
await sandbox.download("/tmp/results.csv", "results.csv")
# Automatically cleaned up on exit
asyncio.run(main())
```
### Smart Sandbox Reuse
Use `get_or_create` with labels (which can include pre-set unique ids) to re-use particular sandboxes. Useful for agent sessions over time.
```python
import asyncio
from sandboxes import Sandbox
async def main():
# First call creates a new sandbox
sandbox1 = await Sandbox.get_or_create(
labels={"project": "ml-training", "gpu": "true"}
)
# Later calls reuse the same sandbox
sandbox2 = await Sandbox.get_or_create(
labels={"project": "ml-training", "gpu": "true"}
)
assert sandbox1.id == sandbox2.id # Same sandbox
asyncio.run(main())
```
### Provider Selection with Automatic Failover
```python
import asyncio
from sandboxes import Sandbox, run
async def main():
# Control where your code runs
sandbox = await Sandbox.create(
provider="e2b", # Try E2B first
fallback=["modal", "cloudflare", "daytona"], # Automatic failover
)
# The library automatically tries the next provider if one fails
print(f"Using: {sandbox._provider_name}")
# Or specify directly with run()
result = await run("bash my-script.sh", provider="modal")
asyncio.run(main())
```
### Custom Images and Templates
```python
import asyncio
from sandboxes import Sandbox, SandboxConfig
from sandboxes.providers import ModalProvider, E2BProvider, DaytonaProvider
async def main():
# High-level API - works with any provider
sandbox = await Sandbox.create(image="python:3.12-slim")
# Or with specific providers
daytona_provider = DaytonaProvider()
config = SandboxConfig(image="daytonaio/ai-test:0.2.3")
sandbox = await daytona_provider.create_sandbox(config)
asyncio.run(main())
# Via CLI
# sandboxes run "python --version" --image python:3.12-slim
```
## API Reference
### Core Classes
- **`Sandbox`**: High-level interface with automatic provider management
- **`SandboxConfig`**: Configuration for sandbox creation (labels, timeout, image)
- **`ExecutionResult`**: Standardized result object (stdout, stderr, exit_code)
- **`Manager`**: Multi-provider orchestration with failover
- **`SandboxProvider`**: Abstract base class for provider implementations
### Key Methods
```python
# High-level functions
await run(command: str, provider: str = None) -> ExecutionResult
await run_many(commands: list[str], provider: str = None) -> list[ExecutionResult]
# Sandbox methods
await Sandbox.create(provider=None, fallback=None, labels=None, image=None) -> Sandbox
await Sandbox.get_or_create(labels: dict) -> Sandbox
await Sandbox.find(labels: dict) -> Sandbox | None
await sandbox.execute(command: str) -> ExecutionResult
await sandbox.execute_many(commands: list[str]) -> list[ExecutionResult]
await sandbox.stream(command: str) -> AsyncIterator[str]
await sandbox.upload(local_path: str, remote_path: str)
await sandbox.download(remote_path: str, local_path: str)
await sandbox.destroy()
```
## Command Line Interface
`sandboxes` includes a powerful CLI for running code in **any language** from your terminal. Execute TypeScript, Go, Rust, Python, or any other language in isolated sandboxes.
You can call the CLI from any language, or write a wrapper for it.
### Quick Start
```bash
# Run TypeScript from a file
sandboxes run --file script.ts
# Run Go code from stdin
cat main.go | sandboxes run --lang go
# Direct command execution
sandboxes run "python3 -c 'print(sum(range(100)))'"
# Run with specific provider
sandboxes run "python3 --version" --provider e2b
# List all sandboxes
sandboxes list
```
### Commands
#### `run` - Execute Code
```bash
# 1. From file (auto-detects language)
sandboxes run --file script.py
sandboxes run --file main.go
# 2. From stdin/pipe
cat script.py | sandboxes run --lang python
echo 'console.log("Hello!")' | sandboxes run --lang node
# 3. Direct command
sandboxes run "python3 -c 'print(42)'"
```
**Options:**
```bash
# Specify provider
sandboxes run --file app.py -p e2b
# Environment variables
sandboxes run --file script.py -e API_KEY=secret -e DEBUG=1
# Labels for reuse
sandboxes run --file app.py -l project=myapp --reuse
# Keep sandbox (don't auto-destroy)
sandboxes run --file script.py --keep
# Timeout
sandboxes run --file script.sh -t 600
```
Supported languages with auto-detect: `python`, `node/javascript`, `typescript`, `go`, `rust`, `bash/sh`
#### `list` - List Sandboxes
View all active sandboxes:
```bash
# List all sandboxes
sandboxes list
# Filter by provider
sandboxes list -p e2b
# Filter by labels
sandboxes list -l env=prod
# JSON output
sandboxes list --json
```
#### `exec` - Execute in Existing Sandbox
```bash
sandboxes exec sb-abc123 "ls -la" -p modal
sandboxes exec sb-abc123 "python script.py" -p e2b -e DEBUG=1
```
#### `destroy` - Remove Sandbox
```bash
sandboxes destroy sb-abc123 -p e2b
```
#### `providers` - Check Providers
```bash
sandboxes providers
```
#### `test` - Test Provider Connectivity
```bash
sandboxes test # Test all
sandboxes test -p e2b # Test specific
```
### CLI Examples
#### Development Workflow
```bash
# Create development sandbox
sandboxes run "git clone https://github.com/user/repo.git /app" \
-l project=myapp \
-l env=dev \
--keep
# List to get sandbox ID
sandboxes list -l project=myapp
# Run commands in the sandbox
sandboxes exec sb-abc123 "cd /app && npm install" -p e2b
sandboxes exec sb-abc123 "cd /app && npm test" -p e2b
# Cleanup when done
sandboxes destroy sb-abc123 -p e2b
```
#### Multi-Language Code Testing
```bash
# TypeScript
echo 'const x: number = 42; console.log(x)' > test.ts
sandboxes run --file test.ts
# Go with automatic dependency installation
sandboxes run --file main.go --deps
# Go from stdin
cat main.go | sandboxes run --lang go
# Python from remote URL
curl -s https://example.com/script.py | sandboxes run --lang python
```
**Auto-Dependency Installation (`golang` only for now):** Use `--deps` to automatically install dependencies from `go.mod` (located in the same directory as your code file). The CLI will upload `go.mod` and `go.sum` (if present) and run `go mod download` before executing your code.
## Provider Configuration
You'll need API keys from one of the supported providers.
### Automatic Configuration
The library automatically detects available providers from environment variables:
```bash
# Set any of these environment variables:
export E2B_API_KEY="..."
export MODAL_TOKEN_ID="..." # Or use `modal token set`
export DAYTONA_API_KEY="..."
export CLOUDFLARE_SANDBOX_BASE_URL="https://your-worker.workers.dev"
export CLOUDFLARE_API_TOKEN="..."
```
Then just use:
```python
import asyncio
from sandboxes import Sandbox
async def main():
sandbox = await Sandbox.create() # Auto-selects first available provider
asyncio.run(main())
```
#### How Auto-Detection Works
When you call `Sandbox.create()` or `run()`, the library checks for providers in this priority order:
1. **Daytona** - Looks for `DAYTONA_API_KEY`
2. **E2B** - Looks for `E2B_API_KEY`
3. **Modal** - Looks for `~/.modal.toml` or `MODAL_TOKEN_ID`
4. **Cloudflare** *(experimental)* - Looks for `CLOUDFLARE_SANDBOX_BASE_URL` + `CLOUDFLARE_API_TOKEN`
**The first provider with valid credentials becomes the default.** Cloudflare requires deploying your own Worker.
#### Customizing the Default Provider
You can override the auto-detected default:
```python
from sandboxes import Sandbox
# Option 1: Set default provider explicitly
Sandbox.configure(default_provider="modal")
# Option 2: Specify provider per call
sandbox = await Sandbox.create(provider="e2b")
# Option 3: Use fallback chain
sandbox = await Sandbox.create(
provider="daytona",
fallback=["e2b", "modal"]
)
# Check which providers are available
Sandbox._ensure_manager()
print(f"Available: {list(Sandbox._manager.providers.keys())}")
print(f"Default: {Sandbox._manager.default_provider}")
```
### Manual Provider Configuration
For more control, you can configure providers manually:
```python
from sandboxes import Sandbox
# Configure providers programmatically
Sandbox.configure(
e2b_api_key="your-key",
cloudflare_config={
"base_url": "https://your-worker.workers.dev",
"api_token": "your-token",
},
default_provider="e2b"
)
```
### Direct Provider Usage (Low-Level API)
For advanced use cases, you can work with providers directly:
```python
from sandboxes.providers import (
E2BProvider,
ModalProvider,
DaytonaProvider,
CloudflareProvider,
)
# E2B - Uses E2B_API_KEY env var
provider = E2BProvider()
# Modal - Uses ~/.modal.toml for auth
provider = ModalProvider()
# Daytona - Uses DAYTONA_API_KEY env var
provider = DaytonaProvider()
# Cloudflare - Requires base_url and token
provider = CloudflareProvider(
base_url="https://your-worker.workers.dev",
api_token="your-token",
)
```
Each provider requires appropriate authentication:
- **E2B**: Set `E2B_API_KEY` environment variable
- **Modal**: Run `modal token set` to configure
- **Daytona**: Set `DAYTONA_API_KEY` environment variable
- **Cloudflare** *(experimental)*: Deploy the [Cloudflare sandbox Worker](https://github.com/cloudflare/sandbox-sdk) and set `CLOUDFLARE_SANDBOX_BASE_URL`, `CLOUDFLARE_API_TOKEN`, and (optionally) `CLOUDFLARE_ACCOUNT_ID`
> **Cloudflare setup tips (experimental)**
>
> ⚠️ **Note**: Cloudflare support is experimental and requires self-hosting a Worker.
>
> 1. Clone the Cloudflare `sandbox-sdk` repository and deploy the `examples/basic` Worker with `wrangler`.
> 2. Provision a Workers Paid plan and enable Containers + Docker Hub registry for your account.
> 3. Define a secret (e.g. `SANDBOX_API_TOKEN`) in Wrangler and reuse the same value for `CLOUDFLARE_API_TOKEN` locally.
> 4. Set `CLOUDFLARE_SANDBOX_BASE_URL` to the Worker URL (e.g. `https://cf-sandbox.your-subdomain.workers.dev`).
## Advanced Usage
### Multi-Provider Orchestration
```python
import asyncio
from sandboxes import Manager, SandboxConfig
from sandboxes.providers import E2BProvider, ModalProvider, DaytonaProvider, CloudflareProvider
async def main():
# Initialize manager and register providers
manager = Manager(default_provider="e2b")
manager.register_provider("e2b", E2BProvider, {})
manager.register_provider("modal", ModalProvider, {})
manager.register_provider("daytona", DaytonaProvider, {})
manager.register_provider(
"cloudflare",
CloudflareProvider,
{"base_url": "https://your-worker.workers.dev", "api_token": "..."}
)
# Manager handles failover automatically
sandbox = await manager.create_sandbox(
SandboxConfig(labels={"task": "test"}),
fallback_providers=["modal", "daytona"] # Try these if primary fails
)
asyncio.run(main())
```
### Sandbox Reuse (Provider-Level)
For advanced control, work directly with providers instead of the high-level `Sandbox` API:
```python
import asyncio
from sandboxes import SandboxConfig
from sandboxes.providers import E2BProvider
async def main():
provider = E2BProvider()
# Sandboxes can be reused based on labels
config = SandboxConfig(
labels={"project": "ml-training", "gpu": "true"}
)
# This will find existing sandbox or create new one
sandbox = await provider.get_or_create_sandbox(config)
# Later in another process...
# This will find the same sandbox
sandbox = await provider.find_sandbox({"project": "ml-training"})
asyncio.run(main())
```
### Streaming Execution
```python
import asyncio
from sandboxes.providers import E2BProvider
async def main():
provider = E2BProvider()
sandbox = await provider.create_sandbox()
# Stream output as it's generated
async for chunk in provider.stream_execution(
sandbox.id,
"for i in range(10): print(i); time.sleep(1)"
):
print(chunk, end="")
asyncio.run(main())
```
### Connection Pooling
```python
import asyncio
from sandboxes import SandboxConfig
from sandboxes.pool import ConnectionPool
from sandboxes.providers import E2BProvider
async def main():
# Create a connection pool for better performance
pool = ConnectionPool(
provider=E2BProvider(),
max_connections=10,
max_idle_time=300,
ttl=3600
)
# Get or create connection
conn = await pool.get_or_create(
SandboxConfig(labels={"pool": "ml"})
)
# Return to pool when done
await pool.release(conn)
asyncio.run(main())
```
## Architecture
### Core Components
- **`Sandbox`**: High-level interface with automatic provider management
- **`SandboxProvider`**: Abstract base class for all providers
- **`SandboxConfig`**: Configuration for sandbox creation
- **`ExecutionResult`**: Standardized execution results
- **`Manager`**: Multi-provider orchestration
- **`ConnectionPool`**: Connection pooling with TTL
- **`RetryPolicy`**: Configurable retry logic
- **`CircuitBreaker`**: Fault tolerance
## Environment Variables
```bash
# E2B
export E2B_API_KEY="e2b_..."
# Daytona
export DAYTONA_API_KEY="dtn_..."
# Modal (or use modal token set)
export MODAL_TOKEN_ID="..."
export MODAL_TOKEN_SECRET="..."
# Cloudflare
export CLOUDFLARE_SANDBOX_BASE_URL="https://your-worker.workers.dev"
export CLOUDFLARE_API_TOKEN="..."
export CLOUDFLARE_ACCOUNT_ID="..." # Optional
```
## Multi-Language Support
While `sandboxes` is a Python library, it can execute code in **any language** available in the sandbox environment. The sandboxes run standard Linux containers, so you can execute TypeScript, Go, Rust, Java, or any other language.
### Running TypeScript
```python
import asyncio
from sandboxes import Sandbox
async def run_typescript():
"""Execute TypeScript code in a sandbox."""
async with Sandbox.create() as sandbox:
# TypeScript code
ts_code = '''
const greeting: string = "Hello from TypeScript!";
const numbers: number[] = [1, 2, 3, 4, 5];
const sum: number = numbers.reduce((a, b) => a + b, 0);
console.log(greeting);
console.log(`Sum of numbers: ${sum}`);
console.log(`Type system ensures safety at compile time`);
'''
# Run with ts-node (npx auto-installs)
result = await sandbox.execute(
f"echo '{ts_code}' > /tmp/app.ts && npx -y ts-node /tmp/app.ts"
)
print(result.stdout)
# Output:
# Hello from TypeScript!
# Sum of numbers: 15
# Type system ensures safety at compile time
asyncio.run(run_typescript())
```
### Running Go
```python
import asyncio
from sandboxes import Sandbox
async def run_go():
"""Execute Go code in a sandbox."""
async with Sandbox.create() as sandbox:
# Go code
go_code = '''package main
import (
"fmt"
"math"
)
func main() {
fmt.Println("Hello from Go!")
// Calculate fibonacci
n := 10
fmt.Printf("Fibonacci(%d) = %d\\n", n, fibonacci(n))
// Demonstrate type safety
radius := 5.0
area := math.Pi * radius * radius
fmt.Printf("Circle area (r=%.1f): %.2f\\n", radius, area)
}
func fibonacci(n int) int {
if n <= 1 {
return n
}
return fibonacci(n-1) + fibonacci(n-2)
}
'''
# Save and run Go code
result = await sandbox.execute(f'''
cat > /tmp/main.go << 'EOF'
{go_code}
EOF
go run /tmp/main.go
''')
print(result.stdout)
# Output:
# Hello from Go!
# Fibonacci(10) = 55
# Circle area (r=5.0): 78.54
asyncio.run(run_go())
```
## Common Use Cases
### AI Agent Code Execution
```python
import asyncio
from sandboxes import Sandbox
async def execute_agent_code(code: str, language: str = "python"):
"""Safely execute AI-generated code."""
async with Sandbox.create() as sandbox:
# Install any required packages first
if "import" in code:
# Extract and install imports (simplified)
await sandbox.execute("pip install requests numpy")
# Execute the code
result = await sandbox.execute(f"{language} -c '{code}'")
if result.exit_code != 0:
return f"Error: {result.stderr}"
return result.stdout
# Example usage
asyncio.run(execute_agent_code("print('Hello!')", "python"))
```
### Data Processing Pipeline
```python
import asyncio
from sandboxes import Sandbox
async def process_dataset(dataset_url: str):
"""Process data in isolated environment."""
async with Sandbox.create(labels={"task": "data-pipeline"}) as sandbox:
# Setup environment
await sandbox.execute_many([
"pip install pandas numpy scikit-learn",
f"wget {dataset_url} -O data.csv"
])
# Upload processing script
await sandbox.upload("process.py", "/tmp/process.py")
# Run processing with streaming output
async for output in sandbox.stream("python /tmp/process.py"):
print(output, end="")
# Download results
await sandbox.download("/tmp/results.csv", "results.csv")
# Example usage
asyncio.run(process_dataset("https://example.com/data.csv"))
```
### Code Testing and Validation
```python
import asyncio
from sandboxes import Sandbox
async def test_solution(code: str, test_cases: list):
"""Test code against multiple test cases."""
results = []
async with Sandbox.create() as sandbox:
# Save the code
await sandbox.upload("solution.py", "/tmp/solution.py")
# Run each test case
for i, test in enumerate(test_cases):
result = await sandbox.execute(
f"python /tmp/solution.py < {test['input']}"
)
results.append({
"test": i + 1,
"passed": result.stdout.strip() == test['expected'],
"output": result.stdout.strip()
})
return results
# Example usage
asyncio.run(test_solution("print(sum(map(int, input().split())))", [
{"input": "1 2 3", "expected": "6"}
]))
```
## Troubleshooting
### No Providers Available
```python
# If you see: "No provider specified and no default provider set"
# Solution 1: Set environment variables
export E2B_API_KEY="your-key"
# Solution 2: Configure manually
from sandboxes import Sandbox
Sandbox.configure(e2b_api_key="your-key")
# Solution 3: Use low-level API
from sandboxes.providers import E2BProvider
provider = E2BProvider(api_key="your-key")
```
### Provider Failures
```python
import asyncio
from sandboxes import Sandbox
from sandboxes.exceptions import ProviderError
async def main():
# Enable automatic failover
sandbox = await Sandbox.create(
provider="e2b",
fallback=["modal", "cloudflare", "daytona"]
)
# Or handle errors manually
try:
sandbox = await Sandbox.create(provider="e2b")
except ProviderError:
sandbox = await Sandbox.create(provider="modal")
asyncio.run(main())
```
### Debugging
```python
import asyncio
import logging
from sandboxes import Sandbox
async def main():
# Enable debug logging
logging.basicConfig(level=logging.DEBUG)
# Check provider health
Sandbox._ensure_manager()
for name, provider in Sandbox._manager.providers.items():
health = await provider.health_check()
print(f"{name}: {'✅' if health else '❌'}")
asyncio.run(main())
```
## Security Disclosure
We take security seriously. If you discover a security vulnerability in this library or any of its dependencies, please report it responsibly.
**Responsible Disclosure:**
- Email security reports to: **ted@cased.com**
- Include a detailed description of the vulnerability
- Provide steps to reproduce if possible
- Allow reasonable time for a fix before public disclosure
We will acknowledge your report within 48 hours and work with you to address the issue.
## License
MIT License - see [LICENSE](LICENSE) file for details.
## Acknowledgments
Built by [Cased](https://cased.com)
Special thanks to the teams at E2B, Modal, Daytona, and Cloudflare for their excellent sandbox platforms.
Raw data
{
"_id": null,
"home_page": null,
"name": "cased-sandboxes",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.11",
"maintainer_email": null,
"keywords": "ai, cloudflare, code-execution, daytona, e2b, modal, sandbox, vercel",
"author": null,
"author_email": "Sandboxes Contributors <ted@cased.com>",
"download_url": "https://files.pythonhosted.org/packages/50/2d/ab875465c75d8abe0949216c2fcd2d3736cff3eb4c219b25ff480a4434cb/cased_sandboxes-0.2.3.tar.gz",
"platform": null,
"description": "# sandboxes\n\nUniversal library for AI code execution sandboxes.\n\n[](https://www.python.org/downloads/)\n[](https://opensource.org/licenses/MIT)\n\n## Overview\n\n`sandboxes` provides a unified interface for sandboxed code execution across multiple providers:\n\n- **Current providers**: E2B, Modal, Daytona\n- **Experimental**: Cloudflare (requires self-hosted Worker deployment)\n\nWrite your code once and switch between providers with a single line change, or let the library automatically select a provider.\nIncludes a Python API plus full-featured CLI for use from any runtime.\n\n## Installation\n\nAdd to your project:\n\n```bash\nuv add cased-sandboxes\n```\n\nor install with your preferred Python package manager and use the CLI\nfor any language, e.g.,:\n\n```bash\nuv pip install cased-sandboxes\n```\n\n## Quick Start\n\n### One-line Execution + Auto-select Provider\n\n```python\nimport asyncio\nfrom sandboxes import run\n\nasync def main():\n # Creates a temporary sandbox, runs the command, then destroys the sandbox\n result = await run(\"echo 'Hello from sandbox!'\")\n print(result.stdout)\n\n # Behind the scenes, run() does this:\n # 1. Auto-detects available providers (e.g., E2B, Modal, Daytona)\n # 2. Creates a new sandbox with the first available provider\n # 3. Executes your command in that isolated environment\n # 4. Returns the result\n # 5. Automatically destroys the sandbox\n\nasyncio.run(main())\n```\n\n### Multiple Commands\n\n```python\nimport asyncio\nfrom sandboxes import run_many\n\nasync def main():\n # Execute multiple commands in one sandbox\n results = await run_many([\n \"pip install requests\",\n \"python -c 'import requests; print(requests.__version__)'\"\n ])\n for result in results:\n print(result.stdout)\n\nasyncio.run(main())\n```\n\n### Persistent Sandbox Sessions\n\n```python\nimport asyncio\nfrom sandboxes import Sandbox\n\nasync def main():\n async with Sandbox.create() as sandbox:\n # Install dependencies\n await sandbox.execute(\"pip install numpy pandas\")\n\n # Run your code\n result = await sandbox.execute(\"python analyze.py\")\n print(result.stdout)\n\n await sandbox.upload(\"data.csv\", \"/tmp/data.csv\")\n await sandbox.download(\"/tmp/results.csv\", \"results.csv\")\n # Automatically cleaned up on exit\n\nasyncio.run(main())\n```\n\n### Smart Sandbox Reuse\n\nUse `get_or_create` with labels (which can include pre-set unique ids) to re-use particular sandboxes. Useful for agent sessions over time.\n\n```python\nimport asyncio\nfrom sandboxes import Sandbox\n\nasync def main():\n # First call creates a new sandbox\n sandbox1 = await Sandbox.get_or_create(\n labels={\"project\": \"ml-training\", \"gpu\": \"true\"}\n )\n\n # Later calls reuse the same sandbox\n sandbox2 = await Sandbox.get_or_create(\n labels={\"project\": \"ml-training\", \"gpu\": \"true\"}\n )\n\n assert sandbox1.id == sandbox2.id # Same sandbox\n\nasyncio.run(main())\n```\n\n### Provider Selection with Automatic Failover\n\n```python\nimport asyncio\nfrom sandboxes import Sandbox, run\n\nasync def main():\n # Control where your code runs\n sandbox = await Sandbox.create(\n provider=\"e2b\", # Try E2B first\n fallback=[\"modal\", \"cloudflare\", \"daytona\"], # Automatic failover\n )\n\n # The library automatically tries the next provider if one fails\n print(f\"Using: {sandbox._provider_name}\")\n\n # Or specify directly with run()\n result = await run(\"bash my-script.sh\", provider=\"modal\")\n\nasyncio.run(main())\n```\n\n### Custom Images and Templates\n\n```python\nimport asyncio\nfrom sandboxes import Sandbox, SandboxConfig\nfrom sandboxes.providers import ModalProvider, E2BProvider, DaytonaProvider\n\nasync def main():\n # High-level API - works with any provider\n sandbox = await Sandbox.create(image=\"python:3.12-slim\")\n\n # Or with specific providers\n daytona_provider = DaytonaProvider()\n config = SandboxConfig(image=\"daytonaio/ai-test:0.2.3\")\n sandbox = await daytona_provider.create_sandbox(config)\n\nasyncio.run(main())\n\n# Via CLI\n# sandboxes run \"python --version\" --image python:3.12-slim\n```\n\n## API Reference\n\n### Core Classes\n\n- **`Sandbox`**: High-level interface with automatic provider management\n- **`SandboxConfig`**: Configuration for sandbox creation (labels, timeout, image)\n- **`ExecutionResult`**: Standardized result object (stdout, stderr, exit_code)\n- **`Manager`**: Multi-provider orchestration with failover\n- **`SandboxProvider`**: Abstract base class for provider implementations\n\n### Key Methods\n\n```python\n# High-level functions\nawait run(command: str, provider: str = None) -> ExecutionResult\nawait run_many(commands: list[str], provider: str = None) -> list[ExecutionResult]\n\n# Sandbox methods\nawait Sandbox.create(provider=None, fallback=None, labels=None, image=None) -> Sandbox\nawait Sandbox.get_or_create(labels: dict) -> Sandbox\nawait Sandbox.find(labels: dict) -> Sandbox | None\nawait sandbox.execute(command: str) -> ExecutionResult\nawait sandbox.execute_many(commands: list[str]) -> list[ExecutionResult]\nawait sandbox.stream(command: str) -> AsyncIterator[str]\nawait sandbox.upload(local_path: str, remote_path: str)\nawait sandbox.download(remote_path: str, local_path: str)\nawait sandbox.destroy()\n```\n\n## Command Line Interface\n\n`sandboxes` includes a powerful CLI for running code in **any language** from your terminal. Execute TypeScript, Go, Rust, Python, or any other language in isolated sandboxes.\nYou can call the CLI from any language, or write a wrapper for it.\n\n### Quick Start\n\n```bash\n# Run TypeScript from a file\nsandboxes run --file script.ts\n\n# Run Go code from stdin\ncat main.go | sandboxes run --lang go\n\n# Direct command execution\nsandboxes run \"python3 -c 'print(sum(range(100)))'\"\n\n# Run with specific provider\nsandboxes run \"python3 --version\" --provider e2b\n\n# List all sandboxes\nsandboxes list\n```\n\n### Commands\n\n#### `run` - Execute Code\n\n```bash\n# 1. From file (auto-detects language)\nsandboxes run --file script.py\nsandboxes run --file main.go\n\n# 2. From stdin/pipe\ncat script.py | sandboxes run --lang python\necho 'console.log(\"Hello!\")' | sandboxes run --lang node\n\n# 3. Direct command\nsandboxes run \"python3 -c 'print(42)'\"\n```\n\n**Options:**\n\n```bash\n# Specify provider\nsandboxes run --file app.py -p e2b\n\n# Environment variables\nsandboxes run --file script.py -e API_KEY=secret -e DEBUG=1\n\n# Labels for reuse\nsandboxes run --file app.py -l project=myapp --reuse\n\n# Keep sandbox (don't auto-destroy)\nsandboxes run --file script.py --keep\n\n# Timeout\nsandboxes run --file script.sh -t 600\n```\n\nSupported languages with auto-detect: `python`, `node/javascript`, `typescript`, `go`, `rust`, `bash/sh`\n\n#### `list` - List Sandboxes\n\nView all active sandboxes:\n\n```bash\n# List all sandboxes\nsandboxes list\n\n# Filter by provider\nsandboxes list -p e2b\n\n# Filter by labels\nsandboxes list -l env=prod\n\n# JSON output\nsandboxes list --json\n```\n\n#### `exec` - Execute in Existing Sandbox\n\n```bash\nsandboxes exec sb-abc123 \"ls -la\" -p modal\nsandboxes exec sb-abc123 \"python script.py\" -p e2b -e DEBUG=1\n```\n\n#### `destroy` - Remove Sandbox\n\n```bash\nsandboxes destroy sb-abc123 -p e2b\n```\n\n#### `providers` - Check Providers\n\n```bash\nsandboxes providers\n```\n\n#### `test` - Test Provider Connectivity\n\n```bash\nsandboxes test # Test all\nsandboxes test -p e2b # Test specific\n```\n\n### CLI Examples\n\n#### Development Workflow\n\n```bash\n# Create development sandbox\nsandboxes run \"git clone https://github.com/user/repo.git /app\" \\\n -l project=myapp \\\n -l env=dev \\\n --keep\n\n# List to get sandbox ID\nsandboxes list -l project=myapp\n\n# Run commands in the sandbox\nsandboxes exec sb-abc123 \"cd /app && npm install\" -p e2b\nsandboxes exec sb-abc123 \"cd /app && npm test\" -p e2b\n\n# Cleanup when done\nsandboxes destroy sb-abc123 -p e2b\n```\n\n#### Multi-Language Code Testing\n\n```bash\n# TypeScript\necho 'const x: number = 42; console.log(x)' > test.ts\nsandboxes run --file test.ts\n\n# Go with automatic dependency installation\nsandboxes run --file main.go --deps\n\n# Go from stdin\ncat main.go | sandboxes run --lang go\n\n# Python from remote URL\ncurl -s https://example.com/script.py | sandboxes run --lang python\n```\n\n**Auto-Dependency Installation (`golang` only for now):** Use `--deps` to automatically install dependencies from `go.mod` (located in the same directory as your code file). The CLI will upload `go.mod` and `go.sum` (if present) and run `go mod download` before executing your code.\n\n\n## Provider Configuration\n\nYou'll need API keys from one of the supported providers.\n\n### Automatic Configuration\n\nThe library automatically detects available providers from environment variables:\n\n```bash\n# Set any of these environment variables:\nexport E2B_API_KEY=\"...\"\nexport MODAL_TOKEN_ID=\"...\" # Or use `modal token set`\nexport DAYTONA_API_KEY=\"...\"\nexport CLOUDFLARE_SANDBOX_BASE_URL=\"https://your-worker.workers.dev\"\nexport CLOUDFLARE_API_TOKEN=\"...\"\n```\n\nThen just use:\n```python\nimport asyncio\nfrom sandboxes import Sandbox\n\nasync def main():\n sandbox = await Sandbox.create() # Auto-selects first available provider\n\nasyncio.run(main())\n```\n\n#### How Auto-Detection Works\n\nWhen you call `Sandbox.create()` or `run()`, the library checks for providers in this priority order:\n\n1. **Daytona** - Looks for `DAYTONA_API_KEY`\n2. **E2B** - Looks for `E2B_API_KEY`\n3. **Modal** - Looks for `~/.modal.toml` or `MODAL_TOKEN_ID`\n4. **Cloudflare** *(experimental)* - Looks for `CLOUDFLARE_SANDBOX_BASE_URL` + `CLOUDFLARE_API_TOKEN`\n\n**The first provider with valid credentials becomes the default.** Cloudflare requires deploying your own Worker.\n\n#### Customizing the Default Provider\n\nYou can override the auto-detected default:\n\n```python\nfrom sandboxes import Sandbox\n\n# Option 1: Set default provider explicitly\nSandbox.configure(default_provider=\"modal\")\n\n# Option 2: Specify provider per call\nsandbox = await Sandbox.create(provider=\"e2b\")\n\n# Option 3: Use fallback chain\nsandbox = await Sandbox.create(\n provider=\"daytona\",\n fallback=[\"e2b\", \"modal\"]\n)\n\n# Check which providers are available\nSandbox._ensure_manager()\nprint(f\"Available: {list(Sandbox._manager.providers.keys())}\")\nprint(f\"Default: {Sandbox._manager.default_provider}\")\n```\n\n### Manual Provider Configuration\n\nFor more control, you can configure providers manually:\n\n```python\nfrom sandboxes import Sandbox\n\n# Configure providers programmatically\nSandbox.configure(\n e2b_api_key=\"your-key\",\n cloudflare_config={\n \"base_url\": \"https://your-worker.workers.dev\",\n \"api_token\": \"your-token\",\n },\n default_provider=\"e2b\"\n)\n```\n\n### Direct Provider Usage (Low-Level API)\n\nFor advanced use cases, you can work with providers directly:\n\n```python\nfrom sandboxes.providers import (\n E2BProvider,\n ModalProvider,\n DaytonaProvider,\n CloudflareProvider,\n)\n\n# E2B - Uses E2B_API_KEY env var\nprovider = E2BProvider()\n\n# Modal - Uses ~/.modal.toml for auth\nprovider = ModalProvider()\n\n# Daytona - Uses DAYTONA_API_KEY env var\nprovider = DaytonaProvider()\n\n# Cloudflare - Requires base_url and token\nprovider = CloudflareProvider(\n base_url=\"https://your-worker.workers.dev\",\n api_token=\"your-token\",\n)\n```\n\nEach provider requires appropriate authentication:\n- **E2B**: Set `E2B_API_KEY` environment variable\n- **Modal**: Run `modal token set` to configure\n- **Daytona**: Set `DAYTONA_API_KEY` environment variable\n- **Cloudflare** *(experimental)*: Deploy the [Cloudflare sandbox Worker](https://github.com/cloudflare/sandbox-sdk) and set `CLOUDFLARE_SANDBOX_BASE_URL`, `CLOUDFLARE_API_TOKEN`, and (optionally) `CLOUDFLARE_ACCOUNT_ID`\n\n> **Cloudflare setup tips (experimental)**\n>\n> \u26a0\ufe0f **Note**: Cloudflare support is experimental and requires self-hosting a Worker.\n>\n> 1. Clone the Cloudflare `sandbox-sdk` repository and deploy the `examples/basic` Worker with `wrangler`.\n> 2. Provision a Workers Paid plan and enable Containers + Docker Hub registry for your account.\n> 3. Define a secret (e.g. `SANDBOX_API_TOKEN`) in Wrangler and reuse the same value for `CLOUDFLARE_API_TOKEN` locally.\n> 4. Set `CLOUDFLARE_SANDBOX_BASE_URL` to the Worker URL (e.g. `https://cf-sandbox.your-subdomain.workers.dev`).\n\n## Advanced Usage\n\n### Multi-Provider Orchestration\n\n```python\nimport asyncio\nfrom sandboxes import Manager, SandboxConfig\nfrom sandboxes.providers import E2BProvider, ModalProvider, DaytonaProvider, CloudflareProvider\n\nasync def main():\n # Initialize manager and register providers\n manager = Manager(default_provider=\"e2b\")\n\n manager.register_provider(\"e2b\", E2BProvider, {})\n manager.register_provider(\"modal\", ModalProvider, {})\n manager.register_provider(\"daytona\", DaytonaProvider, {})\n manager.register_provider(\n \"cloudflare\",\n CloudflareProvider,\n {\"base_url\": \"https://your-worker.workers.dev\", \"api_token\": \"...\"}\n )\n\n # Manager handles failover automatically\n sandbox = await manager.create_sandbox(\n SandboxConfig(labels={\"task\": \"test\"}),\n fallback_providers=[\"modal\", \"daytona\"] # Try these if primary fails\n )\n\nasyncio.run(main())\n```\n\n### Sandbox Reuse (Provider-Level)\n\nFor advanced control, work directly with providers instead of the high-level `Sandbox` API:\n\n```python\nimport asyncio\nfrom sandboxes import SandboxConfig\nfrom sandboxes.providers import E2BProvider\n\nasync def main():\n provider = E2BProvider()\n\n # Sandboxes can be reused based on labels\n config = SandboxConfig(\n labels={\"project\": \"ml-training\", \"gpu\": \"true\"}\n )\n\n # This will find existing sandbox or create new one\n sandbox = await provider.get_or_create_sandbox(config)\n\n # Later in another process...\n # This will find the same sandbox\n sandbox = await provider.find_sandbox({\"project\": \"ml-training\"})\n\nasyncio.run(main())\n```\n\n### Streaming Execution\n\n```python\nimport asyncio\nfrom sandboxes.providers import E2BProvider\n\nasync def main():\n provider = E2BProvider()\n sandbox = await provider.create_sandbox()\n\n # Stream output as it's generated\n async for chunk in provider.stream_execution(\n sandbox.id,\n \"for i in range(10): print(i); time.sleep(1)\"\n ):\n print(chunk, end=\"\")\n\nasyncio.run(main())\n```\n\n### Connection Pooling\n\n```python\nimport asyncio\nfrom sandboxes import SandboxConfig\nfrom sandboxes.pool import ConnectionPool\nfrom sandboxes.providers import E2BProvider\n\nasync def main():\n # Create a connection pool for better performance\n pool = ConnectionPool(\n provider=E2BProvider(),\n max_connections=10,\n max_idle_time=300,\n ttl=3600\n )\n\n # Get or create connection\n conn = await pool.get_or_create(\n SandboxConfig(labels={\"pool\": \"ml\"})\n )\n\n # Return to pool when done\n await pool.release(conn)\n\nasyncio.run(main())\n```\n\n## Architecture\n\n### Core Components\n\n- **`Sandbox`**: High-level interface with automatic provider management\n- **`SandboxProvider`**: Abstract base class for all providers\n- **`SandboxConfig`**: Configuration for sandbox creation\n- **`ExecutionResult`**: Standardized execution results\n- **`Manager`**: Multi-provider orchestration\n- **`ConnectionPool`**: Connection pooling with TTL\n- **`RetryPolicy`**: Configurable retry logic\n- **`CircuitBreaker`**: Fault tolerance\n\n\n## Environment Variables\n\n```bash\n# E2B\nexport E2B_API_KEY=\"e2b_...\"\n\n# Daytona\nexport DAYTONA_API_KEY=\"dtn_...\"\n\n# Modal (or use modal token set)\nexport MODAL_TOKEN_ID=\"...\"\nexport MODAL_TOKEN_SECRET=\"...\"\n\n# Cloudflare\nexport CLOUDFLARE_SANDBOX_BASE_URL=\"https://your-worker.workers.dev\"\nexport CLOUDFLARE_API_TOKEN=\"...\"\nexport CLOUDFLARE_ACCOUNT_ID=\"...\" # Optional\n```\n\n## Multi-Language Support\n\nWhile `sandboxes` is a Python library, it can execute code in **any language** available in the sandbox environment. The sandboxes run standard Linux containers, so you can execute TypeScript, Go, Rust, Java, or any other language.\n\n### Running TypeScript\n\n```python\nimport asyncio\nfrom sandboxes import Sandbox\n\nasync def run_typescript():\n \"\"\"Execute TypeScript code in a sandbox.\"\"\"\n async with Sandbox.create() as sandbox:\n # TypeScript code\n ts_code = '''\nconst greeting: string = \"Hello from TypeScript!\";\nconst numbers: number[] = [1, 2, 3, 4, 5];\nconst sum: number = numbers.reduce((a, b) => a + b, 0);\n\nconsole.log(greeting);\nconsole.log(`Sum of numbers: ${sum}`);\nconsole.log(`Type system ensures safety at compile time`);\n'''\n\n # Run with ts-node (npx auto-installs)\n result = await sandbox.execute(\n f\"echo '{ts_code}' > /tmp/app.ts && npx -y ts-node /tmp/app.ts\"\n )\n\n print(result.stdout)\n # Output:\n # Hello from TypeScript!\n # Sum of numbers: 15\n # Type system ensures safety at compile time\n\nasyncio.run(run_typescript())\n```\n\n### Running Go\n\n```python\nimport asyncio\nfrom sandboxes import Sandbox\n\nasync def run_go():\n \"\"\"Execute Go code in a sandbox.\"\"\"\n async with Sandbox.create() as sandbox:\n # Go code\n go_code = '''package main\n\nimport (\n \"fmt\"\n \"math\"\n)\n\nfunc main() {\n fmt.Println(\"Hello from Go!\")\n\n // Calculate fibonacci\n n := 10\n fmt.Printf(\"Fibonacci(%d) = %d\\\\n\", n, fibonacci(n))\n\n // Demonstrate type safety\n radius := 5.0\n area := math.Pi * radius * radius\n fmt.Printf(\"Circle area (r=%.1f): %.2f\\\\n\", radius, area)\n}\n\nfunc fibonacci(n int) int {\n if n <= 1 {\n return n\n }\n return fibonacci(n-1) + fibonacci(n-2)\n}\n'''\n\n # Save and run Go code\n result = await sandbox.execute(f'''\ncat > /tmp/main.go << 'EOF'\n{go_code}\nEOF\ngo run /tmp/main.go\n''')\n\n print(result.stdout)\n # Output:\n # Hello from Go!\n # Fibonacci(10) = 55\n # Circle area (r=5.0): 78.54\n\nasyncio.run(run_go())\n```\n\n## Common Use Cases\n\n### AI Agent Code Execution\n\n```python\nimport asyncio\nfrom sandboxes import Sandbox\n\nasync def execute_agent_code(code: str, language: str = \"python\"):\n \"\"\"Safely execute AI-generated code.\"\"\"\n async with Sandbox.create() as sandbox:\n # Install any required packages first\n if \"import\" in code:\n # Extract and install imports (simplified)\n await sandbox.execute(\"pip install requests numpy\")\n\n # Execute the code\n result = await sandbox.execute(f\"{language} -c '{code}'\")\n\n if result.exit_code != 0:\n return f\"Error: {result.stderr}\"\n return result.stdout\n\n# Example usage\nasyncio.run(execute_agent_code(\"print('Hello!')\", \"python\"))\n```\n\n### Data Processing Pipeline\n\n```python\nimport asyncio\nfrom sandboxes import Sandbox\n\nasync def process_dataset(dataset_url: str):\n \"\"\"Process data in isolated environment.\"\"\"\n async with Sandbox.create(labels={\"task\": \"data-pipeline\"}) as sandbox:\n # Setup environment\n await sandbox.execute_many([\n \"pip install pandas numpy scikit-learn\",\n f\"wget {dataset_url} -O data.csv\"\n ])\n\n # Upload processing script\n await sandbox.upload(\"process.py\", \"/tmp/process.py\")\n\n # Run processing with streaming output\n async for output in sandbox.stream(\"python /tmp/process.py\"):\n print(output, end=\"\")\n\n # Download results\n await sandbox.download(\"/tmp/results.csv\", \"results.csv\")\n\n# Example usage\nasyncio.run(process_dataset(\"https://example.com/data.csv\"))\n```\n\n### Code Testing and Validation\n\n```python\nimport asyncio\nfrom sandboxes import Sandbox\n\nasync def test_solution(code: str, test_cases: list):\n \"\"\"Test code against multiple test cases.\"\"\"\n results = []\n\n async with Sandbox.create() as sandbox:\n # Save the code\n await sandbox.upload(\"solution.py\", \"/tmp/solution.py\")\n\n # Run each test case\n for i, test in enumerate(test_cases):\n result = await sandbox.execute(\n f\"python /tmp/solution.py < {test['input']}\"\n )\n results.append({\n \"test\": i + 1,\n \"passed\": result.stdout.strip() == test['expected'],\n \"output\": result.stdout.strip()\n })\n\n return results\n\n# Example usage\nasyncio.run(test_solution(\"print(sum(map(int, input().split())))\", [\n {\"input\": \"1 2 3\", \"expected\": \"6\"}\n]))\n```\n\n## Troubleshooting\n\n### No Providers Available\n\n```python\n# If you see: \"No provider specified and no default provider set\"\n\n# Solution 1: Set environment variables\nexport E2B_API_KEY=\"your-key\"\n\n# Solution 2: Configure manually\nfrom sandboxes import Sandbox\nSandbox.configure(e2b_api_key=\"your-key\")\n\n# Solution 3: Use low-level API\nfrom sandboxes.providers import E2BProvider\nprovider = E2BProvider(api_key=\"your-key\")\n```\n\n### Provider Failures\n\n```python\nimport asyncio\nfrom sandboxes import Sandbox\nfrom sandboxes.exceptions import ProviderError\n\nasync def main():\n # Enable automatic failover\n sandbox = await Sandbox.create(\n provider=\"e2b\",\n fallback=[\"modal\", \"cloudflare\", \"daytona\"]\n )\n\n # Or handle errors manually\n try:\n sandbox = await Sandbox.create(provider=\"e2b\")\n except ProviderError:\n sandbox = await Sandbox.create(provider=\"modal\")\n\nasyncio.run(main())\n```\n\n### Debugging\n\n```python\nimport asyncio\nimport logging\nfrom sandboxes import Sandbox\n\nasync def main():\n # Enable debug logging\n logging.basicConfig(level=logging.DEBUG)\n\n # Check provider health\n Sandbox._ensure_manager()\n for name, provider in Sandbox._manager.providers.items():\n health = await provider.health_check()\n print(f\"{name}: {'\u2705' if health else '\u274c'}\")\n\nasyncio.run(main())\n```\n\n## Security Disclosure\n\nWe take security seriously. If you discover a security vulnerability in this library or any of its dependencies, please report it responsibly.\n\n**Responsible Disclosure:**\n- Email security reports to: **ted@cased.com**\n- Include a detailed description of the vulnerability\n- Provide steps to reproduce if possible\n- Allow reasonable time for a fix before public disclosure\n\nWe will acknowledge your report within 48 hours and work with you to address the issue.\n\n## License\n\nMIT License - see [LICENSE](LICENSE) file for details.\n## Acknowledgments\n\nBuilt by [Cased](https://cased.com)\n\nSpecial thanks to the teams at E2B, Modal, Daytona, and Cloudflare for their excellent sandbox platforms.\n",
"bugtrack_url": null,
"license": null,
"summary": "Universal library for AI code execution sandboxes",
"version": "0.2.3",
"project_urls": null,
"split_keywords": [
"ai",
" cloudflare",
" code-execution",
" daytona",
" e2b",
" modal",
" sandbox",
" vercel"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "3122ba3baa4c21862fa6eb3e24ae249fdfd8f1b4a20590d251c50d0148420d9d",
"md5": "43f575c52c80518810cc3cc7783c0b73",
"sha256": "56ee5dc7b0a9d52f8dcb2f683dc3ee4332b7c64eb30e9d11c3f171e61bc352a7"
},
"downloads": -1,
"filename": "cased_sandboxes-0.2.3-py3-none-any.whl",
"has_sig": false,
"md5_digest": "43f575c52c80518810cc3cc7783c0b73",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.11",
"size": 48935,
"upload_time": "2025-10-07T11:03:18",
"upload_time_iso_8601": "2025-10-07T11:03:18.888449Z",
"url": "https://files.pythonhosted.org/packages/31/22/ba3baa4c21862fa6eb3e24ae249fdfd8f1b4a20590d251c50d0148420d9d/cased_sandboxes-0.2.3-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "502dab875465c75d8abe0949216c2fcd2d3736cff3eb4c219b25ff480a4434cb",
"md5": "fbe21048a0a91605a5d2b923e1f67287",
"sha256": "1362c6d5b37ca4078d4def0f5e501b400f804d17c29f65b9ddab150b53fb2414"
},
"downloads": -1,
"filename": "cased_sandboxes-0.2.3.tar.gz",
"has_sig": false,
"md5_digest": "fbe21048a0a91605a5d2b923e1f67287",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.11",
"size": 41473,
"upload_time": "2025-10-07T11:03:19",
"upload_time_iso_8601": "2025-10-07T11:03:19.899281Z",
"url": "https://files.pythonhosted.org/packages/50/2d/ab875465c75d8abe0949216c2fcd2d3736cff3eb4c219b25ff480a4434cb/cased_sandboxes-0.2.3.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-10-07 11:03:19",
"github": false,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"lcname": "cased-sandboxes"
}