cloudflare-sandbox
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCloudflare Sandboxes SDK
Cloudflare Sandboxes SDK
Status: Production Ready (Open Beta)
Last Updated: 2025-10-29
Dependencies: , (recommended for understanding)
Latest Versions: , Docker image:
cloudflare-worker-basecloudflare-durable-objects@cloudflare/sandbox@0.4.12cloudflare/sandbox:0.4.12状态:生产就绪(公开测试版)
最后更新:2025-10-29
依赖项:、(建议了解)
最新版本:,Docker镜像:
cloudflare-worker-basecloudflare-durable-objects@cloudflare/sandbox@0.4.12cloudflare/sandbox:0.4.12Quick Start (15 Minutes)
快速开始(15分钟)
1. Install SDK and Setup Wrangler
1. 安装SDK并配置Wrangler
bash
npm install @cloudflare/sandbox@latestwrangler.jsonc:
jsonc
{
"name": "my-sandbox-worker",
"main": "src/index.ts",
"compatibility_flags": ["nodejs_compat"],
"containers": [{
"class_name": "Sandbox",
"image": "cloudflare/sandbox:0.4.12",
"instance_type": "lite"
}],
"durable_objects": {
"bindings": [{
"class_name": "Sandbox",
"name": "Sandbox"
}]
},
"migrations": [{
"tag": "v1",
"new_sqlite_classes": ["Sandbox"]
}]
}Why this matters:
- enables Node.js APIs required by SDK
nodejs_compat - defines the Ubuntu container image
containers - binding enables persistent routing
durable_objects - registers the Sandbox class
migrations
bash
npm install @cloudflare/sandbox@latestwrangler.jsonc:
jsonc
{
"name": "my-sandbox-worker",
"main": "src/index.ts",
"compatibility_flags": ["nodejs_compat"],
"containers": [{
"class_name": "Sandbox",
"image": "cloudflare/sandbox:0.4.12",
"instance_type": "lite"
}],
"durable_objects": {
"bindings": [{
"class_name": "Sandbox",
"name": "Sandbox"
}]
},
"migrations": [{
"tag": "v1",
"new_sqlite_classes": ["Sandbox"]
}]
}配置说明:
- 启用SDK所需的Node.js API
nodejs_compat - 定义Ubuntu容器镜像
containers - 绑定实现持久化路由
durable_objects - 注册Sandbox类
migrations
2. Create Your First Sandbox Worker
2. 创建第一个Sandbox Worker
typescript
import { getSandbox, type Sandbox } from '@cloudflare/sandbox';
export { Sandbox } from '@cloudflare/sandbox';
type Env = {
Sandbox: DurableObjectNamespace<Sandbox>;
};
export default {
async fetch(request: Request, env: Env): Promise<Response> {
// Get sandbox instance (creates if doesn't exist)
const sandbox = getSandbox(env.Sandbox, 'my-first-sandbox');
// Execute Python code
const result = await sandbox.exec('python3 -c "print(2 + 2)"');
return Response.json({
output: result.stdout,
success: result.success,
exitCode: result.exitCode
});
}
};CRITICAL:
- MUST export from
{ Sandbox }in your Worker@cloudflare/sandbox - Sandbox ID determines routing (same ID = same container)
- First request creates container (~2-3 min cold start)
- Subsequent requests are fast (<1s)
typescript
import { getSandbox, type Sandbox } from '@cloudflare/sandbox';
export { Sandbox } from '@cloudflare/sandbox';
type Env = {
Sandbox: DurableObjectNamespace<Sandbox>;
};
export default {
async fetch(request: Request, env: Env): Promise<Response> {
// 获取沙箱实例(不存在则创建)
const sandbox = getSandbox(env.Sandbox, 'my-first-sandbox');
// 执行Python代码
const result = await sandbox.exec('python3 -c "print(2 + 2)"');
return Response.json({
output: result.stdout,
success: result.success,
exitCode: result.exitCode
});
}
};关键注意事项:
- 必须在Worker中导出 来自
{ Sandbox }@cloudflare/sandbox - Sandbox ID决定路由(同一ID对应同一容器)
- 首次请求会创建容器(冷启动约2-3分钟)
- 后续请求响应快速(<1秒)
3. Deploy and Test
3. 部署并测试
bash
npm run deploy
curl https://your-worker.workers.devExpected output:
json
{
"output": "4\n",
"success": true,
"exitCode": 0
}bash
npm run deploy
curl https://your-worker.workers.dev预期输出:
json
{
"output": "4\n",
"success": true,
"exitCode": 0
}Architecture (Understanding the 3-Layer Model)
架构(理解三层模型)
How Sandboxes Work
Sandboxes工作原理
┌─────────────────────────────────────────┐
│ Your Worker (Layer 1) │
│ - Handles HTTP requests │
│ - Calls getSandbox() │
│ - Uses sandbox.exec(), writeFile(), etc│
└──────────────┬──────────────────────────┘
│ RPC via Durable Object
┌──────────────▼──────────────────────────┐
│ Durable Object (Layer 2) │
│ - Routes by sandbox ID │
│ - Maintains persistent identity │
│ - Geographic stickiness │
└──────────────┬──────────────────────────┘
│ Container API
┌──────────────▼──────────────────────────┐
│ Ubuntu Container (Layer 3) │
│ - Full Linux environment │
│ - Python 3.11, Node 20, Git, etc. │
│ - Filesystem: /workspace, /tmp, /home │
│ - Process isolation (VM-based) │
└─────────────────────────────────────────┘Key Insight: Workers handle API logic (fast), Durable Objects route requests (persistent identity), Containers execute code (full capabilities).
┌─────────────────────────────────────────┐
│ 你的Worker(第一层) │
│ - 处理HTTP请求 │
│ - 调用getSandbox() │
│ - 使用sandbox.exec()、writeFile()等方法│
└──────────────┬──────────────────────────┘
│ 通过Durable Object进行RPC调用
┌──────────────▼──────────────────────────┐
│ Durable Object(第二层) │
│ - 按sandbox ID路由请求 │
│ - 维护持久化标识 │
│ - 地域粘性 │
└──────────────┬──────────────────────────┘
│ 容器API
┌──────────────▼──────────────────────────┐
│ Ubuntu容器(第三层) │
│ - 完整Linux环境 │
│ - 预装Python 3.11、Node 20、Git等工具 │
│ - 文件系统:/workspace、/tmp、/home │
│ - 基于VM的进程隔离 │
└─────────────────────────────────────────┘核心要点:Worker处理API逻辑(快速),Durable Object负责请求路由(持久化标识),容器执行代码(完整能力)。
Critical Container Lifecycle (Most Important Section!)
关键容器生命周期(最重要的章节!)
Container States
容器状态
┌─────────┐ First request ┌────────┐ ~10 min idle ┌──────┐
│ Not │ ───────────────>│ Active │ ─────────────> │ Idle │
│ Created │ │ │ │ │
└─────────┘ └───┬────┘ └──┬───┘
│ ^ │
│ │ New request │
│ └──────────────────────┘
│ │
▼ ▼
Files persist ALL FILES DELETED
Processes run ALL PROCESSES KILLED
State maintained ALL STATE RESET┌─────────┐ 首次请求 ┌────────┐ 闲置约10分钟 ┌──────┐
│ 未创建 │ ────────────>│ 活跃中 │ ───────────> │ 闲置 │
│ │ │ │ │ │
└─────────┘ └───┬────┘ └──┬───┘
│ ^ │
│ │ 新请求 │
│ └──────────────────────┘
│ │
▼ ▼
文件保持存在 所有文件被删除
进程持续运行 所有进程被终止
状态被维护 所有状态重置The #1 Gotcha: Ephemeral by Default
头号陷阱:默认临时特性
While Container is Active (~10 min after last request):
- ✅ Files in ,
/workspace,/tmppersist/home - ✅ Background processes keep running
- ✅ Shell environment variables remain
- ✅ Session working directories preserved
When Container Goes Idle (after inactivity):
- ❌ ALL files deleted (entire filesystem reset)
- ❌ ALL processes terminated
- ❌ ALL shell state lost
- ⚠️ Next request creates fresh container from scratch
This is NOT like a traditional server. Sandboxes are ephemeral by design.
容器活跃时(最后一次请求后约10分钟内):
- ✅ 、
/workspace、/tmp中的文件会保留/home - ✅ 后台进程持续运行
- ✅ Shell环境变量保持不变
- ✅ 会话工作目录被保留
容器闲置后(无活动后):
- ❌ 所有文件被删除(整个文件系统重置)
- ❌ 所有进程被终止
- ❌ 所有Shell状态丢失
- ⚠️ 下一次请求会从头创建全新容器
这和传统服务器不同。Sandboxes设计为临时特性。
Handling Persistence
处理持久化
For Important Data: Use external storage
typescript
// Save to R2 before container goes idle
await sandbox.writeFile('/workspace/data.txt', content);
const fileData = await sandbox.readFile('/workspace/data.txt');
await env.R2.put('backup/data.txt', fileData);
// Restore on next request
const restored = await env.R2.get('backup/data.txt');
if (restored) {
await sandbox.writeFile('/workspace/data.txt', await restored.text());
}For Build Artifacts: Accept ephemerality or use caching
typescript
// Check if setup needed (handles cold starts)
const exists = await sandbox.readdir('/workspace/project').catch(() => null);
if (!exists) {
await sandbox.gitCheckout(repoUrl, '/workspace/project');
await sandbox.exec('npm install', { cwd: '/workspace/project' });
}
// Now safe to run build
await sandbox.exec('npm run build', { cwd: '/workspace/project' });重要数据:使用外部存储
typescript
// 在容器闲置前保存到R2
await sandbox.writeFile('/workspace/data.txt', content);
const fileData = await sandbox.readFile('/workspace/data.txt');
await env.R2.put('backup/data.txt', fileData);
// 下次请求时恢复
const restored = await env.R2.get('backup/data.txt');
if (restored) {
await sandbox.writeFile('/workspace/data.txt', await restored.text());
}构建产物:接受临时特性或使用缓存
typescript
// 检查是否需要初始化(处理冷启动)
const exists = await sandbox.readdir('/workspace/project').catch(() => null);
if (!exists) {
await sandbox.gitCheckout(repoUrl, '/workspace/project');
await sandbox.exec('npm install', { cwd: '/workspace/project' });
}
// 现在可以安全执行构建
await sandbox.exec('npm run build', { cwd: '/workspace/project' });Session Management (Game-Changer for Chat Agents)
会话管理(对话式Agent的关键特性)
What Are Sessions?
什么是会话?
Sessions are bash shell contexts within one sandbox. Think terminal tabs.
Key Properties:
- Each session has separate working directory
- Sessions share same filesystem
- Working directory persists across commands in same session
- Perfect for multi-step workflows
会话是单个沙箱内的bash Shell上下文,可以理解为终端标签页。
核心特性:
- 每个会话有独立的工作目录
- 会话共享同一文件系统
- 同一会话内的命令会保留工作目录
- 完美适配多步骤工作流
Pattern: Chat-Based Coding Agent
模式:基于对话的编码Agent
typescript
type ConversationState = {
sandboxId: string;
sessionId: string;
};
// First message: Create sandbox and session
const sandboxId = `user-${userId}`;
const sandbox = getSandbox(env.Sandbox, sandboxId);
const sessionId = await sandbox.createSession();
// Store in conversation state (database, KV, etc.)
await env.KV.put(`conversation:${conversationId}`, JSON.stringify({
sandboxId,
sessionId
}));
// Later messages: Reuse same session
const state = await env.KV.get(`conversation:${conversationId}`);
const { sandboxId, sessionId } = JSON.parse(state);
const sandbox = getSandbox(env.Sandbox, sandboxId);
// Commands run in same context
await sandbox.exec('cd /workspace/project', { session: sessionId });
await sandbox.exec('ls -la', { session: sessionId }); // Still in /workspace/project
await sandbox.exec('git status', { session: sessionId }); // Still in /workspace/projecttypescript
type ConversationState = {
sandboxId: string;
sessionId: string;
};
// 第一条消息:创建沙箱和会话
const sandboxId = `user-${userId}`;
const sandbox = getSandbox(env.Sandbox, sandboxId);
const sessionId = await sandbox.createSession();
// 存储对话状态(数据库、KV等)
await env.KV.put(`conversation:${conversationId}`, JSON.stringify({
sandboxId,
sessionId
}));
// 后续消息:复用同一会话
const state = await env.KV.get(`conversation:${conversationId}`);
const { sandboxId, sessionId } = JSON.parse(state);
const sandbox = getSandbox(env.Sandbox, sandboxId);
// 命令在同一上下文中执行
await sandbox.exec('cd /workspace/project', { session: sessionId });
await sandbox.exec('ls -la', { session: sessionId }); // 仍处于/workspace/project目录
await sandbox.exec('git status', { session: sessionId }); // 仍处于/workspace/project目录Without Sessions (Common Mistake)
错误用法(常见误区)
typescript
// ❌ WRONG: Each command runs in separate session
await sandbox.exec('cd /workspace/project');
await sandbox.exec('ls'); // NOT in /workspace/project (different session)typescript
// ❌ 错误:每个命令在独立会话中运行
await sandbox.exec('cd /workspace/project');
await sandbox.exec('ls'); // 不在/workspace/project目录(不同会话)Pattern: Parallel Execution
模式:并行执行
typescript
const session1 = await sandbox.createSession();
const session2 = await sandbox.createSession();
// Run different tasks simultaneously
await Promise.all([
sandbox.exec('python train_model.py', { session: session1 }),
sandbox.exec('node generate_reports.js', { session: session2 })
]);typescript
const session1 = await sandbox.createSession();
const session2 = await sandbox.createSession();
// 同时运行不同任务
await Promise.all([
sandbox.exec('python train_model.py', { session: session1 }),
sandbox.exec('node generate_reports.js', { session: session2 })
]);Sandbox Naming Strategies
Sandbox命名策略
Per-User Sandboxes (Persistent Workspace)
按用户划分(持久化工作区)
typescript
const sandbox = getSandbox(env.Sandbox, `user-${userId}`);Pros: User's work persists while actively using (10 min idle time)
Cons: Geographic lock-in (first request determines location)
Use Cases: Interactive notebooks, IDEs, persistent workspaces
typescript
const sandbox = getSandbox(env.Sandbox, `user-${userId}`);优点:用户工作内容在活跃期间保留(闲置10分钟内)
缺点:地域锁定(首次请求决定位置)
适用场景:交互式笔记本、IDE、持久化工作区
Per-Session Sandboxes (Fresh Each Time)
按会话划分(每次全新环境)
typescript
const sandboxId = `session-${Date.now()}-${crypto.randomUUID()}`;
const sandbox = getSandbox(env.Sandbox, sandboxId);
// Always destroy after use
await sandbox.destroy();Pros: Clean environment, no state pollution
Cons: No persistence between requests
Use Cases: One-shot code execution, CI/CD, testing
typescript
const sandboxId = `session-${Date.now()}-${crypto.randomUUID()}`;
const sandbox = getSandbox(env.Sandbox, sandboxId);
// 使用后务必销毁
await sandbox.destroy();优点:环境干净,无状态污染
缺点:请求间无持久化
适用场景:一次性代码执行、CI/CD、测试
Per-Task Sandboxes (Idempotent & Traceable)
按任务划分(幂等且可追溯)
typescript
const sandbox = getSandbox(env.Sandbox, `build-${repoName}-${commitSha}`);Pros: Reproducible, debuggable, cacheable
Cons: Need explicit cleanup strategy
Use Cases: Build systems, data pipelines, automated workflows
typescript
const sandbox = getSandbox(env.Sandbox, `build-${repoName}-${commitSha}`);优点:可重现、可调试、可缓存
缺点:需要显式清理策略
适用场景:构建系统、数据管道、自动化工作流
Core API Reference
核心API参考
Getting a Sandbox
获取沙箱
typescript
import { getSandbox } from '@cloudflare/sandbox';
const sandbox = getSandbox(env.Sandbox, 'unique-sandbox-id');
// Creates new sandbox if doesn't exist, or gets existing onetypescript
import { getSandbox } from '@cloudflare/sandbox';
const sandbox = getSandbox(env.Sandbox, 'unique-sandbox-id');
// 不存在则创建新沙箱,否则获取已有沙箱Executing Commands
执行命令
typescript
// Basic execution
const result = await sandbox.exec('python3 script.py');
console.log(result.stdout); // Standard output
console.log(result.stderr); // Standard error
console.log(result.exitCode); // Exit code (0 = success)
console.log(result.success); // Boolean (exitCode === 0)
// With options
const result = await sandbox.exec('npm install', {
cwd: '/workspace/project', // Working directory
timeout: 120000, // Timeout in ms (default: 30s)
session: sessionId, // Session ID
env: { NODE_ENV: 'production' } // Environment variables
});
// Always check exit codes!
if (!result.success) {
throw new Error(`Command failed: ${result.stderr}`);
}typescript
// 基础执行
const result = await sandbox.exec('python3 script.py');
console.log(result.stdout); // 标准输出
console.log(result.stderr); // 标准错误
console.log(result.exitCode); // 退出码(0=成功)
console.log(result.success); // 布尔值(exitCode === 0)
// 带选项的执行
const result = await sandbox.exec('npm install', {
cwd: '/workspace/project', // 工作目录
timeout: 120000, // 超时时间(毫秒,默认30秒)
session: sessionId, // 会话ID
env: { NODE_ENV: 'production' } // 环境变量
});
// 务必检查退出码!
if (!result.success) {
throw new Error(`命令执行失败: ${result.stderr}`);
}File Operations
文件操作
typescript
// Write file
await sandbox.writeFile('/workspace/data.txt', 'content');
// Read file
const content = await sandbox.readFile('/workspace/data.txt');
// Create directory
await sandbox.mkdir('/workspace/project', { recursive: true });
// List directory
const files = await sandbox.readdir('/workspace');
console.log(files); // ['data.txt', 'project']
// Delete file/directory
await sandbox.rm('/workspace/data.txt');
await sandbox.rm('/workspace/project', { recursive: true });typescript
// 写入文件
await sandbox.writeFile('/workspace/data.txt', 'content');
// 读取文件
const content = await sandbox.readFile('/workspace/data.txt');
// 创建目录
await sandbox.mkdir('/workspace/project', { recursive: true });
// 列出目录内容
const files = await sandbox.readdir('/workspace');
console.log(files); // ['data.txt', 'project']
// 删除文件/目录
await sandbox.rm('/workspace/data.txt');
await sandbox.rm('/workspace/project', { recursive: true });Git Operations
Git操作
typescript
// Clone repository (optimized, faster than exec('git clone'))
await sandbox.gitCheckout(
'https://github.com/user/repo',
'/workspace/repo'
);
// Now use standard git commands
await sandbox.exec('git status', { cwd: '/workspace/repo' });
await sandbox.exec('git diff', { cwd: '/workspace/repo' });typescript
// 克隆仓库(优化过,比exec('git clone')更快)
await sandbox.gitCheckout(
'https://github.com/user/repo',
'/workspace/repo'
);
// 之后使用标准Git命令
await sandbox.exec('git status', { cwd: '/workspace/repo' });
await sandbox.exec('git diff', { cwd: '/workspace/repo' });Code Interpreter (Jupyter-like)
代码解释器(类Jupyter)
typescript
// Create Python context
const ctx = await sandbox.createCodeContext({ language: 'python' });
// Execute code - last expression auto-returned
const result = await sandbox.runCode(`
import pandas as pd
df = pd.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6]})
df['a'].sum() # This value is automatically returned
`, { context: ctx });
console.log(result.results[0].text); // "6"
console.log(result.logs); // Output from print() statements
console.log(result.error); // Any errorstypescript
// 创建Python上下文
const ctx = await sandbox.createCodeContext({ language: 'python' });
// 执行代码 - 最后一个表达式自动返回
const result = await sandbox.runCode(`
import pandas as pd
df = pd.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6]})
df['a'].sum() // 该值会自动返回
`, { context: ctx });
console.log(result.results[0].text); // "6"
console.log(result.logs); // print()语句的输出
console.log(result.error); // 任何错误信息Background Processes
后台进程
typescript
// Start long-running process
const proc = await sandbox.spawn('python server.py');
console.log(proc.pid);
// Check if still running
const running = await sandbox.isProcessRunning(proc.pid);
// Kill process
await sandbox.killProcess(proc.pid);typescript
// 启动长期运行的进程
const proc = await sandbox.spawn('python server.py');
console.log(proc.pid);
// 检查进程是否仍在运行
const running = await sandbox.isProcessRunning(proc.pid);
// 终止进程
await sandbox.killProcess(proc.pid);Cleanup
清理
typescript
// Destroy sandbox permanently
await sandbox.destroy();
// All files deleted, container removed, cannot be recoveredtypescript
// 永久销毁沙箱
await sandbox.destroy();
// 所有文件被删除,容器被移除,无法恢复Critical Rules
关键规则
Always Do
务必遵守
✅ Check exit codes -
✅ Use sessions for multi-step workflows - Preserve working directory
✅ Handle cold starts - Check if files exist before assuming they're there
✅ Set timeouts - Prevent hanging on long operations
✅ Destroy ephemeral sandboxes - Cleanup temp/session-based sandboxes
✅ Use external storage for persistence - R2/KV/D1 for important data
✅ Validate user input - Sanitize before exec() to prevent command injection
✅ Export Sandbox class -
if (!result.success) { handle error }export { Sandbox } from '@cloudflare/sandbox'✅ 检查退出码 -
✅ 多步骤工作流使用会话 - 保留工作目录
✅ 处理冷启动 - 执行命令前检查文件是否存在
✅ 设置超时 - 防止长时间操作挂起
✅ 销毁临时沙箱 - 清理临时/会话型沙箱
✅ 重要数据使用外部存储 - R2/KV/D1存储重要数据
✅ 验证用户输入 - 执行exec()前进行 sanitize 防止命令注入
✅ 导出Sandbox类 -
if (!result.success) { 处理错误 }export { Sandbox } from '@cloudflare/sandbox'Never Do
严禁操作
❌ Assume files persist after idle - Container resets after ~10 min
❌ Ignore exit codes - Always check or
❌ Chain commands without sessions - then won't work
❌ Execute unsanitized user input - Use code interpreter or validate thoroughly
❌ Forget nodejs_compat flag - Required in wrangler.jsonc
❌ Skip migrations - Durable Objects need migration entries
❌ Use .workers.dev for preview URLs - Need custom domain
❌ Create unlimited sandboxes - Destroy ephemeral ones to avoid leaks
result.successresult.exitCodecd /dirls❌ 假设文件在闲置后仍保留 - 容器约10分钟后重置
❌ 忽略退出码 - 务必检查或
❌ 无会话时链式执行命令 - 后执行不会生效
❌ 执行未 sanitize 的用户输入 - 使用代码解释器或彻底验证
❌ 忘记nodejs_compat标志 - wrangler.jsonc中必须添加
❌ 跳过迁移配置 - Durable Objects需要迁移条目
❌ 使用.workers.dev作为预览URL - 需要自定义域名
❌ 创建无限沙箱 - 销毁临时沙箱避免资源泄漏
result.successresult.exitCodecd /dirlsKnown Issues Prevention
已知问题预防
This skill prevents 10 documented issues:
本技能可预防10个已记录的问题:
Issue #1: Missing nodejs_compat Flag
问题1:缺少nodejs_compat标志
Error: or
Source: https://developers.cloudflare.com/sandbox/get-started/
Why It Happens: SDK requires Node.js APIs not available in standard Workers
Prevention: Add to wrangler.jsonc
ReferenceError: fetch is not definedBuffer is not defined"compatibility_flags": ["nodejs_compat"]错误: 或
来源:https://developers.cloudflare.com/sandbox/get-started/
原因:SDK需要标准Worker中未提供的Node.js API
解决:在wrangler.jsonc中添加
ReferenceError: fetch is not definedBuffer is not defined"compatibility_flags": ["nodejs_compat"]Issue #2: Missing Migrations
问题2:缺少迁移配置
Error:
Source: https://developers.cloudflare.com/durable-objects/
Why It Happens: Durable Objects must be registered via migrations
Prevention: Include migrations array in wrangler.jsonc
Error: Class 'Sandbox' not found错误:
来源:https://developers.cloudflare.com/durable-objects/
原因:Durable Objects必须通过迁移注册
解决:在wrangler.jsonc中包含migrations数组
Error: Class 'Sandbox' not foundIssue #3: Assuming File Persistence
问题3:假设文件永久保留
Error: Files disappear after inactivity
Source: https://developers.cloudflare.com/sandbox/concepts/sandboxes/
Why It Happens: Containers go idle after ~10 min, all state reset
Prevention: Use external storage (R2/KV) or check existence on each request
错误:闲置后文件消失
来源:https://developers.cloudflare.com/sandbox/concepts/sandboxes/
原因:容器闲置约10分钟后会重置所有状态
解决:使用R2/KV进行持久化,或每次请求前检查并重建环境
Issue #4: Session Directory Confusion
问题4:会话目录混乱
Error: Commands execute in wrong directory
Source: https://developers.cloudflare.com/sandbox/concepts/sessions/
Why It Happens: Each exec() uses new session unless explicitly specified
Prevention: Create session with , pass to all related commands
createSession()错误:命令在错误目录执行
来源:https://developers.cloudflare.com/sandbox/concepts/sessions/
原因:除非显式指定,否则每个exec()使用新会话
解决:使用创建会话,所有相关命令传入该会话ID
createSession()Issue #5: Ignoring Exit Codes
问题5:忽略退出码
Error: Assuming command succeeded when it failed
Source: Shell best practices
Why It Happens: Not checking or
Prevention: Always check:
result.successresult.exitCodeif (!result.success) throw new Error(result.stderr)错误:假设命令执行成功但实际失败
来源:Shell最佳实践
原因:未检查或
解决:务必检查:
result.successresult.exitCodeif (!result.success) throw new Error(result.stderr)Issue #6: Not Handling Cold Starts
问题6:未处理冷启动
Error: Commands fail because dependencies aren't installed
Source: https://developers.cloudflare.com/sandbox/concepts/sandboxes/
Why It Happens: Container resets after idle period
Prevention: Check if setup needed before running commands
错误:因依赖未安装导致命令执行失败
来源:https://developers.cloudflare.com/sandbox/concepts/sandboxes/
原因:容器闲置后会重置
解决:执行命令前检查是否需要初始化
Issue #7: Docker Not Running (Local Dev)
问题7:本地开发时Docker未运行
Error: during local development
Source: https://developers.cloudflare.com/sandbox/get-started/
Why It Happens: Local dev requires Docker daemon
Prevention: Ensure Docker Desktop is running before
Failed to build containernpm run dev错误:本地开发时
来源:https://developers.cloudflare.com/sandbox/get-started/
原因:本地开发需要Docker守护进程
解决:前确保Docker Desktop已运行
Failed to build containernpm run devIssue #8: Version Mismatch (Package vs Docker Image)
问题8:版本不匹配(包与Docker镜像)
Error: API methods not available or behaving unexpectedly
Source: GitHub issues
Why It Happens: npm package version doesn't match Docker image version
Prevention: Keep package and image in sync
@cloudflare/sandboxcloudflare/sandbox错误:API方法不可用或行为异常
来源:GitHub issues
原因:npm包版本与Docker镜像版本不一致
解决:保持包和镜像版本同步
@cloudflare/sandboxcloudflare/sandboxIssue #9: Not Cleaning Up Ephemeral Sandboxes
问题9:未清理临时沙箱
Error: Resource exhaustion, unexpected costs
Source: Resource management best practices
Why It Happens: Creating sandboxes without destroying them
Prevention: in finally block for temp sandboxes
await sandbox.destroy()错误:资源耗尽、意外成本
来源:资源管理最佳实践
原因:创建沙箱后未销毁
解决:临时沙箱在finally块中执行
await sandbox.destroy()Issue #10: Command Injection Vulnerability
问题10:命令注入漏洞
Error: Security breach from unsanitized user input
Source: Security best practices
Why It Happens: Passing user input directly to
Prevention: Use code interpreter API or validate/sanitize input thoroughly
exec()错误:未 sanitize 的用户输入导致安全漏洞
来源:安全最佳实践
原因:直接将用户输入传入
解决:使用代码解释器API或彻底验证/ sanitize 输入
exec()Configuration Files Reference
配置文件参考
wrangler.jsonc (Full Example)
wrangler.jsonc(完整示例)
jsonc
{
"name": "my-sandbox-app",
"main": "src/index.ts",
"compatibility_date": "2025-10-29",
"compatibility_flags": ["nodejs_compat"],
"containers": [{
"class_name": "Sandbox",
"image": "cloudflare/sandbox:0.4.12",
"instance_type": "lite"
}],
"durable_objects": {
"bindings": [{
"class_name": "Sandbox",
"name": "Sandbox"
}]
},
"migrations": [{
"tag": "v1",
"new_sqlite_classes": ["Sandbox"]
}],
"env": {
"ANTHROPIC_API_KEY": {
"description": "Optional: For AI features"
}
},
"observability": {
"enabled": true
}
}Why these settings:
- : Required for SDK to work
nodejs_compat - : Specific version ensures consistency
containers.image - : Smallest instance, upgrade to "large" for more resources
instance_type: "lite" - : Registers Sandbox Durable Object class
migrations - : Enable logging for debugging
observability
jsonc
{
"name": "my-sandbox-app",
"main": "src/index.ts",
"compatibility_date": "2025-10-29",
"compatibility_flags": ["nodejs_compat"],
"containers": [{
"class_name": "Sandbox",
"image": "cloudflare/sandbox:0.4.12",
"instance_type": "lite"
}],
"durable_objects": {
"bindings": [{
"class_name": "Sandbox",
"name": "Sandbox"
}]
},
"migrations": [{
"tag": "v1",
"new_sqlite_classes": ["Sandbox"]
}],
"env": {
"ANTHROPIC_API_KEY": {
"description": "可选:用于AI功能"
}
},
"observability": {
"enabled": true
}
}配置说明:
- :SDK运行必需
nodejs_compat - :指定版本确保一致性
containers.image - :最小实例规格,资源需求高时升级为"large"
instance_type: "lite" - :注册Sandbox Durable Object类
migrations - :启用日志用于调试
observability
Dockerfile (Optional - For Local Dev)
Dockerfile(可选 - 本地开发)
Only needed if extending base image:
dockerfile
FROM cloudflare/sandbox:0.4.12仅在扩展基础镜像时需要:
dockerfile
FROM cloudflare/sandbox:0.4.12Install additional tools
安装额外工具
RUN apt-get update && apt-get install -y
ffmpeg
imagemagick
&& rm -rf /var/lib/apt/lists/*
ffmpeg
imagemagick
&& rm -rf /var/lib/apt/lists/*
RUN apt-get update && apt-get install -y
ffmpeg
imagemagick
&& rm -rf /var/lib/apt/lists/*
ffmpeg
imagemagick
&& rm -rf /var/lib/apt/lists/*
Expose port for preview URLs (local dev only)
暴露端口用于预览URL(仅本地开发)
EXPOSE 8080
---EXPOSE 8080
---Common Patterns
常见模式
Pattern 1: One-Shot Code Execution
模式1:一次性代码执行
typescript
export default {
async fetch(request: Request, env: Env) {
const { code, language } = await request.json();
// Create ephemeral sandbox
const sandboxId = `exec-${Date.now()}-${crypto.randomUUID()}`;
const sandbox = getSandbox(env.Sandbox, sandboxId);
try {
// Create code context
const ctx = await sandbox.createCodeContext({ language });
// Execute code safely
const result = await sandbox.runCode(code, {
context: ctx,
timeout: 10000
});
return Response.json({
result: result.results?.[0]?.text,
logs: result.logs,
error: result.error
});
} finally {
// Always cleanup
await sandbox.destroy();
}
}
};When to use: API endpoints for code execution, code playgrounds, learning platforms
typescript
export default {
async fetch(request: Request, env: Env) {
const { code, language } = await request.json();
// 创建临时沙箱
const sandboxId = `exec-${Date.now()}-${crypto.randomUUID()}`;
const sandbox = getSandbox(env.Sandbox, sandboxId);
try {
// 创建代码上下文
const ctx = await sandbox.createCodeContext({ language });
// 安全执行代码
const result = await sandbox.runCode(code, {
context: ctx,
timeout: 10000
});
return Response.json({
result: result.results?.[0]?.text,
logs: result.logs,
error: result.error
});
} finally {
// 务必清理
await sandbox.destroy();
}
}
};适用场景:代码执行API端点、代码 playground、学习平台
Pattern 2: Persistent User Workspace
模式2:持久化用户工作区
typescript
export default {
async fetch(request: Request, env: Env) {
const userId = request.headers.get('X-User-ID');
const { command, sessionId: existingSession } = await request.json();
// User-specific sandbox (persists while active)
const sandbox = getSandbox(env.Sandbox, `user-${userId}`);
// Get or create session
let sessionId = existingSession;
if (!sessionId) {
sessionId = await sandbox.createSession();
}
// Execute command in persistent context
const result = await sandbox.exec(command, {
session: sessionId,
timeout: 30000
});
return Response.json({
sessionId, // Return for next request
output: result.stdout,
error: result.stderr,
success: result.success
});
}
};When to use: Interactive coding environments, notebooks, IDEs, development workspaces
typescript
export default {
async fetch(request: Request, env: Env) {
const userId = request.headers.get('X-User-ID');
const { command, sessionId: existingSession } = await request.json();
// 用户专属沙箱(活跃期间持久化)
const sandbox = getSandbox(env.Sandbox, `user-${userId}`);
// 获取或创建会话
let sessionId = existingSession;
if (!sessionId) {
sessionId = await sandbox.createSession();
}
// 在持久化上下文中执行命令
const result = await sandbox.exec(command, {
session: sessionId,
timeout: 30000
});
return Response.json({
sessionId, // 返回给下一次请求
output: result.stdout,
error: result.stderr,
success: result.success
});
}
};适用场景:交互式编码环境、笔记本、IDE、开发工作区
Pattern 3: CI/CD Build Pipeline
模式3:CI/CD构建流水线
typescript
async function runBuild(repoUrl: string, commit: string, env: Env) {
const sandboxId = `build-${repoUrl.split('/').pop()}-${commit}`;
const sandbox = getSandbox(env.Sandbox, sandboxId);
try {
// Clone repository
await sandbox.gitCheckout(repoUrl, '/workspace/repo');
// Checkout specific commit
await sandbox.exec(`git checkout ${commit}`, {
cwd: '/workspace/repo'
});
// Install dependencies
const install = await sandbox.exec('npm install', {
cwd: '/workspace/repo',
timeout: 180000 // 3 minutes
});
if (!install.success) {
throw new Error(`Install failed: ${install.stderr}`);
}
// Run build
const build = await sandbox.exec('npm run build', {
cwd: '/workspace/repo',
timeout: 300000 // 5 minutes
});
if (!build.success) {
throw new Error(`Build failed: ${build.stderr}`);
}
// Save artifacts to R2
const dist = await sandbox.exec('tar -czf dist.tar.gz dist', {
cwd: '/workspace/repo'
});
const artifact = await sandbox.readFile('/workspace/repo/dist.tar.gz');
await env.R2.put(`builds/${commit}.tar.gz`, artifact);
return { success: true, artifactKey: `builds/${commit}.tar.gz` };
} finally {
// Optional: Keep sandbox for debugging or destroy
// await sandbox.destroy();
}
}When to use: Build systems, testing pipelines, deployment automation
typescript
async function runBuild(repoUrl: string, commit: string, env: Env) {
const sandboxId = `build-${repoUrl.split('/').pop()}-${commit}`;
const sandbox = getSandbox(env.Sandbox, sandboxId);
try {
// 克隆仓库
await sandbox.gitCheckout(repoUrl, '/workspace/repo');
// 切换到指定提交
await sandbox.exec(`git checkout ${commit}`, {
cwd: '/workspace/repo'
});
// 安装依赖
const install = await sandbox.exec('npm install', {
cwd: '/workspace/repo',
timeout: 180000 // 3分钟
});
if (!install.success) {
throw new Error(`安装失败: ${install.stderr}`);
}
// 执行构建
const build = await sandbox.exec('npm run build', {
cwd: '/workspace/repo',
timeout: 300000 // 5分钟
});
if (!build.success) {
throw new Error(`构建失败: ${build.stderr}`);
}
// 将构建产物保存到R2
const dist = await sandbox.exec('tar -czf dist.tar.gz dist', {
cwd: '/workspace/repo'
});
const artifact = await sandbox.readFile('/workspace/repo/dist.tar.gz');
await env.R2.put(`builds/${commit}.tar.gz`, artifact);
return { success: true, artifactKey: `builds/${commit}.tar.gz` };
} finally {
// 可选:保留沙箱用于调试或销毁
// await sandbox.destroy();
}
}适用场景:构建系统、测试流水线、部署自动化
Pattern 4: AI Agent with Claude Code
模式4:集成Claude Code的AI Agent
typescript
async function runClaudeCodeOnRepo(
repoUrl: string,
task: string,
env: Env
): Promise<{ diff: string; logs: string }> {
const sandboxId = `claude-${Date.now()}`;
const sandbox = getSandbox(env.Sandbox, sandboxId);
try {
// Clone repository
await sandbox.gitCheckout(repoUrl, '/workspace/repo');
// Run Claude Code CLI with secure environment injection
// ⚠️ SECURITY: Use env option instead of shell export to prevent key exposure in logs
const result = await sandbox.exec(
`claude -p "${task}" --permission-mode acceptEdits`,
{
cwd: '/workspace/repo',
timeout: 300000, // 5 minutes
env: {
ANTHROPIC_API_KEY: env.ANTHROPIC_API_KEY
}
}
);
// Get diff of changes
const diff = await sandbox.exec('git diff', {
cwd: '/workspace/repo'
});
return {
diff: diff.stdout,
logs: result.stdout
};
} finally {
await sandbox.destroy();
}
}When to use: Automated code refactoring, code generation, AI-powered development
typescript
async function runClaudeCodeOnRepo(
repoUrl: string,
task: string,
env: Env
): Promise<{ diff: string; logs: string }> {
const sandboxId = `claude-${Date.now()}`;
const sandbox = getSandbox(env.Sandbox, sandboxId);
try {
// 克隆仓库
await sandbox.gitCheckout(repoUrl, '/workspace/repo');
// 运行Claude Code CLI并安全注入环境变量
// ⚠️ 安全提示:使用env选项而非Shell export,避免密钥在日志中暴露
const result = await sandbox.exec(
`claude -p "${task}" --permission-mode acceptEdits`,
{
cwd: '/workspace/repo',
timeout: 300000, // 5分钟
env: {
ANTHROPIC_API_KEY: env.ANTHROPIC_API_KEY
}
}
);
// 获取变更差异
const diff = await sandbox.exec('git diff', {
cwd: '/workspace/repo'
});
return {
diff: diff.stdout,
logs: result.stdout
};
} finally {
await sandbox.destroy();
}
}适用场景:自动化代码重构、代码生成、AI驱动开发
Using Bundled Resources
使用内置资源
Scripts (scripts/)
脚本(scripts/)
- - Interactive wrangler.jsonc configuration
setup-sandbox-binding.sh - - Validation script to test sandbox setup
test-sandbox.ts
Example Usage:
bash
undefined- - 交互式wrangler.jsonc配置脚本
setup-sandbox-binding.sh - - 验证沙箱配置的测试脚本
test-sandbox.ts
使用示例:
bash
undefinedSetup wrangler config
配置wrangler
./scripts/setup-sandbox-binding.sh
./scripts/setup-sandbox-binding.sh
Test sandbox
测试沙箱
npx tsx scripts/test-sandbox.ts
undefinednpx tsx scripts/test-sandbox.ts
undefinedReferences (references/)
参考文档(references/)
- - Deep dive on container lifecycle and persistence
references/persistence-guide.md - - Advanced session patterns and best practices
references/session-management.md - - Complete list of errors with solutions
references/common-errors.md - - Choosing sandbox IDs for different use cases
references/naming-strategies.md
When Claude should load these:
- Load when debugging state issues or cold starts
persistence-guide.md - Load when building multi-step workflows or chat agents
session-management.md - Load when encountering specific errors
common-errors.md - Load when designing sandbox architecture
naming-strategies.md
- - 容器生命周期与持久化深度指南
references/persistence-guide.md - - 高级会话模式与最佳实践
references/session-management.md - - 完整错误列表与解决方案
references/common-errors.md - - 不同场景下的沙箱ID选择策略
references/naming-strategies.md
Claude应加载这些文档的场景:
- 调试状态问题或冷启动时加载
persistence-guide.md - 构建多步骤工作流或对话Agent时加载
session-management.md - 遇到特定错误时加载
common-errors.md - 设计沙箱架构时加载
naming-strategies.md
Advanced Topics
高级主题
Geographic Distribution
地域分布
First request to a sandbox ID determines its geographic location (via Durable Objects).
For Global Apps:
typescript
// Option 1: Multiple sandboxes per user (better latency)
const region = request.cf?.colo || 'default';
const sandbox = getSandbox(env.Sandbox, `user-${userId}-${region}`);
// Option 2: Single sandbox (simpler, higher latency for distant users)
const sandbox = getSandbox(env.Sandbox, `user-${userId}`);首次请求沙箱ID时会确定其地理位置(通过Durable Objects)。
全球应用方案:
typescript
// 方案1:每个用户多个沙箱(更低延迟)
const region = request.cf?.colo || 'default';
const sandbox = getSandbox(env.Sandbox, `user-${userId}-${region}`);
// 方案2:单个沙箱(更简单,远程用户延迟更高)
const sandbox = getSandbox(env.Sandbox, `user-${userId}`);Error Handling Strategy
错误处理策略
typescript
async function safeSandboxExec(
sandbox: Sandbox,
cmd: string,
options?: any
) {
try {
const result = await sandbox.exec(cmd, {
...options,
timeout: options?.timeout || 30000
});
if (!result.success) {
console.error(`Command failed: ${cmd}`, {
exitCode: result.exitCode,
stderr: result.stderr
});
return {
success: false,
error: result.stderr,
exitCode: result.exitCode
};
}
return {
success: true,
output: result.stdout,
exitCode: 0
};
} catch (error) {
console.error(`Sandbox error:`, error);
return {
success: false,
error: error.message,
exitCode: -1
};
}
}typescript
async function safeSandboxExec(
sandbox: Sandbox,
cmd: string,
options?: any
) {
try {
const result = await sandbox.exec(cmd, {
...options,
timeout: options?.timeout || 30000
});
if (!result.success) {
console.error(`命令执行失败: ${cmd}`, {
exitCode: result.exitCode,
stderr: result.stderr
});
return {
success: false,
error: result.stderr,
exitCode: result.exitCode
};
}
return {
success: true,
output: result.stdout,
exitCode: 0
};
} catch (error) {
console.error(`沙箱错误:`, error);
return {
success: false,
error: error.message,
exitCode: -1
};
}
}Security Hardening
安全加固
typescript
// Input validation
function sanitizeCommand(input: string): string {
const dangerous = ['rm -rf', '$(', '`', '&&', '||', ';', '|'];
for (const pattern of dangerous) {
if (input.includes(pattern)) {
throw new Error(`Dangerous pattern detected: ${pattern}`);
}
}
return input;
}
// Use code interpreter instead of direct exec for untrusted code
async function executeUntrustedCode(code: string, sandbox: Sandbox) {
const ctx = await sandbox.createCodeContext({ language: 'python' });
return await sandbox.runCode(code, { context: ctx, timeout: 10000 });
}typescript
// 输入验证
function sanitizeCommand(input: string): string {
const dangerous = ['rm -rf', '$(', '`', '&&', '||', ';', '|'];
for (const pattern of dangerous) {
if (input.includes(pattern)) {
throw new Error(`检测到危险模式: ${pattern}`);
}
}
return input;
}
// 不可信代码使用代码解释器而非直接exec
async function executeUntrustedCode(code: string, sandbox: Sandbox) {
const ctx = await sandbox.createCodeContext({ language: 'python' });
return await sandbox.runCode(code, { context: ctx, timeout: 10000 });
}Dependencies
依赖项
Required:
- - Sandbox SDK
@cloudflare/sandbox@0.4.12 - - Deployment CLI
wrangler@latest - Docker Desktop - Local development only
Optional:
- - For AI features
@anthropic-ai/sdk - - TypeScript types
@cloudflare/workers-types
必需:
- - Sandbox SDK
@cloudflare/sandbox@0.4.12 - - 部署CLI
wrangler@latest - Docker Desktop - 仅本地开发需要
可选:
- - 用于AI功能
@anthropic-ai/sdk - - TypeScript类型定义
@cloudflare/workers-types
Official Documentation
官方文档
- Cloudflare Sandboxes: https://developers.cloudflare.com/sandbox/
- Architecture Guide: https://developers.cloudflare.com/sandbox/concepts/architecture/
- API Reference: https://developers.cloudflare.com/sandbox/api-reference/
- Durable Objects: https://developers.cloudflare.com/durable-objects/
- GitHub SDK: https://github.com/cloudflare/sandbox-sdk
- Cloudflare Sandboxes: https://developers.cloudflare.com/sandbox/
- 架构指南: https://developers.cloudflare.com/sandbox/concepts/architecture/
- API参考: https://developers.cloudflare.com/sandbox/api-reference/
- Durable Objects: https://developers.cloudflare.com/durable-objects/
- GitHub SDK: https://github.com/cloudflare/sandbox-sdk
Package Versions (Verified 2025-10-29)
包版本(2025-10-29验证)
json
{
"dependencies": {
"@cloudflare/sandbox": "^0.4.12"
},
"devDependencies": {
"wrangler": "^3.80.0",
"@cloudflare/workers-types": "^4.20241106.0"
}
}Docker Image:
cloudflare/sandbox:0.4.12json
{
"dependencies": {
"@cloudflare/sandbox": "^0.4.12"
},
"devDependencies": {
"wrangler": "^3.80.0",
"@cloudflare/workers-types": "^4.20241106.0"
}
}Docker镜像:
cloudflare/sandbox:0.4.12Production Example
生产示例
This skill is based on official Cloudflare tutorials:
- Claude Code Integration: https://developers.cloudflare.com/sandbox/tutorials/claude-code/
- AI Code Executor: https://developers.cloudflare.com/sandbox/tutorials/ai-code-executor/
- Build Time: ~15 min (setup) + ~5 min (first deploy)
- Errors: 0 (all 10 known issues prevented)
- Validation: ✅ Tested with Python, Node.js, Git, background processes, sessions
本技能基于官方Cloudflare教程:
- Claude Code集成: https://developers.cloudflare.com/sandbox/tutorials/claude-code/
- AI代码执行器: https://developers.cloudflare.com/sandbox/tutorials/ai-code-executor/
- 构建时间: ~15分钟(配置)+ ~5分钟(首次部署)
- 错误: 0(10个已知问题全部预防)
- 验证: ✅ 已测试Python、Node.js、Git、后台进程、会话
Troubleshooting
故障排除
Problem: "Class 'Sandbox' not found"
问题:"Class 'Sandbox' not found"
Solution: Add migrations to wrangler.jsonc and ensure in Worker
export { Sandbox }解决: 在wrangler.jsonc中添加迁移配置,并确保Worker中
export { Sandbox }Problem: Files disappear after some time
问题:文件一段时间后消失
Solution: Container goes idle after ~10 min. Use R2/KV for persistence or rebuild environment
解决: 容器闲置约10分钟后会重置。使用R2/KV进行持久化,或重建环境
Problem: Commands execute in wrong directory
问题:命令在错误目录执行
Solution: Create session with , pass sessionId to all related commands
createSession()解决: 使用创建会话,所有相关命令传入sessionId
createSession()Problem: Docker error during local dev
问题:本地开发时Docker错误
Solution: Ensure Docker Desktop is running before
npm run dev解决: 前确保Docker Desktop已运行
npm run devProblem: "fetch is not defined"
问题:"fetch is not defined"
Solution: Add to wrangler.jsonc
"compatibility_flags": ["nodejs_compat"]解决: 在wrangler.jsonc中添加
"compatibility_flags": ["nodejs_compat"]Complete Setup Checklist
完整配置检查清单
-
npm install @cloudflare/sandbox@latest - Add to compatibility_flags
nodejs_compat - Add containers configuration with correct image version
- Add Durable Objects binding
- Add migrations for Sandbox class
- Export Sandbox class in Worker:
export { Sandbox } from '@cloudflare/sandbox' - Docker Desktop running (local dev only)
- Test with simple exec command
- Verify exit codes are being checked
- Implement cleanup for ephemeral sandboxes
- Test cold start behavior
Questions? Issues?
- Check for specific error solutions
references/common-errors.md - Verify all steps in wrangler.jsonc configuration
- Check official docs: https://developers.cloudflare.com/sandbox/
- Ensure Docker is running for local development
- Confirm package version matches Docker image version
-
npm install @cloudflare/sandbox@latest - 在compatibility_flags中添加
nodejs_compat - 添加容器配置并指定正确镜像版本
- 添加Durable Objects绑定
- 为Sandbox类添加迁移配置
- 在Worker中导出Sandbox类:
export { Sandbox } from '@cloudflare/sandbox' - Docker Desktop已运行(仅本地开发)
- 使用简单exec命令测试
- 验证退出码检查逻辑
- 实现临时沙箱的清理逻辑
- 测试冷启动行为
有疑问?遇到问题?
- 查看获取特定错误解决方案
references/common-errors.md - 验证wrangler.jsonc中的所有配置步骤
- 查看官方文档: https://developers.cloudflare.com/sandbox/
- 本地开发确保Docker已运行
- 确认包版本与Docker镜像版本一致