SDK 兼容性矩阵
E2B × CubeSandbox
Python SDK 快速上手
已有 E2B 代码可以使用 E2B Python SDK,也可以使用 CubeSandbox 原生 Python SDK。安装并配置以下环境变量即可:
pip install "e2b==2.29.5"
pip install cubesandbox
export CUBE_API_URL="http://127.0.0.1:3000"
export CUBE_API_KEY="e2b_000000"
export CUBE_TEMPLATE_ID="tpl-xxx"
export E2B_API_URL="http://127.0.0.1:3000"
export E2B_API_KEY="e2b_000000"01 · 版本
E2B版本兼容性
从 1.0.0 到 2.62.0 的每个 E2B Python SDK 版本都跑了完整的兼容性测试套件。
整套用例全部通过。
逐项核对后判为通过——剩余失败都是这些 SDK 版本自身缺少的能力,不是平台问题。
创建、命令、文件、查询均可正常使用;这些版本自身没有暂停、网络等新参数。
该区间未通过测试,建议使用 2.6.0 及以上版本。
该区间未通过测试。仅需基础能力时,1.6.0 及以上可用。
02 · API 对比
SDK API
按公开方法判断兼容性。同名方法不代表默认值、返回类型和副作用完全一致。
沙箱生命周期
10Sandbox.create()查看示例 ↓
import os
from e2b import Sandbox
# 上下文管理器会在代码块结束时自动销毁沙箱。
with Sandbox.create(template=os.environ["CUBE_TEMPLATE_ID"], timeout=60) as sandbox:
print(sandbox.sandbox_id)Sandbox.connect()查看示例 ↓
import os
from e2b import Sandbox
sandbox = Sandbox.create(template=os.environ["CUBE_TEMPLATE_ID"], timeout=120)
try:
connected = Sandbox.connect(sandbox.sandbox_id, timeout=60)
print(connected.commands.run("echo connected").stdout)
finally:
sandbox.kill()Sandbox.list()查看示例 ↓
from e2b import Sandbox
paginator = Sandbox.list(limit=10)
while paginator.has_next:
for info in paginator.next_items():
print(info.sandbox_id, info.state)sandbox.get_info()查看示例 ↓
import os
from e2b import Sandbox
with Sandbox.create(template=os.environ["CUBE_TEMPLATE_ID"]) as sandbox:
info = sandbox.get_info()
print(info.sandbox_id, info.state, info.started_at)sandbox.is_running()查看示例 ↓
import os
from e2b import Sandbox
with Sandbox.create(template=os.environ["CUBE_TEMPLATE_ID"]) as sandbox:
print("running:", sandbox.is_running())sandbox.kill()查看示例 ↓
import os
from e2b import Sandbox
sandbox = Sandbox.create(template=os.environ["CUBE_TEMPLATE_ID"])
sandbox_id = sandbox.sandbox_id
removed = sandbox.kill()
print(sandbox_id, removed)sandbox.pause()查看示例 ↓
import os
from e2b import Sandbox
sandbox = Sandbox.create(template=os.environ["CUBE_TEMPLATE_ID"])
sandbox_id = sandbox.sandbox_id
sandbox.pause() # 保存当前状态,之后可以继续恢复运行。
resumed = Sandbox.connect(sandbox_id)
print(resumed.commands.run("echo resumed").stdout)
resumed.kill()sandbox.set_timeout()查看示例 ↓
import os
from e2b import Sandbox
with Sandbox.create(template=os.environ["CUBE_TEMPLATE_ID"], timeout=60) as sandbox:
# 把当前生命周期超时时间重新设置为 5 分钟。
sandbox.set_timeout(300)
print(sandbox.get_info().end_at)sandbox.update_network()查看示例 ↓
import os
from e2b import Sandbox
with Sandbox.create(template=os.environ["CUBE_TEMPLATE_ID"]) as sandbox:
# 此操作会整体替换策略,因此未填写的字段会被清空。
sandbox.update_network({"deny_out": ["10.0.0.0/8"]})
print("network policy updated")sandbox.get_metrics()查看示例 ↓
import os
from e2b import Sandbox
with Sandbox.create(template=os.environ["CUBE_TEMPLATE_ID"]) as sandbox:
sandbox.commands.run("python -c 'sum(range(1000000))'")
for sample in sandbox.get_metrics():
print(sample)命令、文件与 PTY
10sandbox.commands.run()查看示例 ↓
import os
from e2b import Sandbox
with Sandbox.create(template=os.environ["CUBE_TEMPLATE_ID"]) as sandbox:
result = sandbox.commands.run(
"python -c 'print(6 * 7)'",
timeout=30,
)
print(result.stdout, result.exit_code)commands.connect()查看示例 ↓
import os
from e2b import Sandbox
with Sandbox.create(template=os.environ["CUBE_TEMPLATE_ID"]) as sandbox:
process = sandbox.commands.run("sleep 2; echo done", background=True)
pid = process.pid
process.disconnect() # 断开句柄后,进程仍会继续在沙箱中运行。
result = sandbox.commands.connect(pid).wait()
print(result.stdout)commands.listkillsend_stdin查看示例 ↓
import os
from e2b import Sandbox
with Sandbox.create(template=os.environ["CUBE_TEMPLATE_ID"]) as sandbox:
process = sandbox.commands.run("cat", background=True, stdin=True)
sandbox.commands.send_stdin(process.pid, "hello\n")
print([item.pid for item in sandbox.commands.list()])
sandbox.commands.kill(process.pid)files.readwritewrite_files查看示例 ↓
import os
from e2b import Sandbox
from e2b.sandbox.filesystem.filesystem import WriteEntry
with Sandbox.create(template=os.environ["CUBE_TEMPLATE_ID"]) as sandbox:
sandbox.files.write("/tmp/one.txt", "one")
sandbox.files.write_files([
WriteEntry(path="/tmp/two.txt", data="two"),
WriteEntry(path="/tmp/three.bin", data=b"three"),
])
print(sandbox.files.read("/tmp/one.txt"))files.listexistsremoverenamemake_dir查看示例 ↓
import os
from e2b import Sandbox
with Sandbox.create(template=os.environ["CUBE_TEMPLATE_ID"]) as sandbox:
sandbox.files.make_dir("/tmp/demo")
sandbox.files.write("/tmp/demo/a.txt", "hello")
print(sandbox.files.exists("/tmp/demo/a.txt"))
print([entry.name for entry in sandbox.files.list("/tmp/demo")])
sandbox.files.rename("/tmp/demo/a.txt", "/tmp/demo/b.txt")
sandbox.files.remove("/tmp/demo")files.get_info()查看示例 ↓
import os
from e2b import Sandbox
with Sandbox.create(template=os.environ["CUBE_TEMPLATE_ID"]) as sandbox:
sandbox.files.write("/tmp/info.txt", "hello")
info = sandbox.files.get_info("/tmp/info.txt")
print(info.name, info.type, info.path)files.watch_dir()查看示例 ↓
import os
from e2b import Sandbox
with Sandbox.create(template=os.environ["CUBE_TEMPLATE_ID"]) as sandbox:
sandbox.files.make_dir("/tmp/watched")
watcher = sandbox.files.watch_dir("/tmp/watched")
try:
sandbox.files.write("/tmp/watched/new.txt", "hello")
print(watcher.get_new_events())
finally:
watcher.stop()sandbox.pty.*查看示例 ↓
import os
from e2b import PtySize, Sandbox
with Sandbox.create(template=os.environ["CUBE_TEMPLATE_ID"]) as sandbox:
terminal = sandbox.pty.create(PtySize(rows=24, cols=80))
sandbox.pty.send_stdin(terminal.pid, b"echo hello from PTY\n")
sandbox.pty.resize(terminal.pid, PtySize(rows=30, cols=100))
sandbox.pty.kill(terminal.pid)sandbox.git.*查看示例 ↓
import os
from e2b import Sandbox
with Sandbox.create(template=os.environ["CUBE_TEMPLATE_ID"]) as sandbox:
# 基础 sandbox-code 模板默认不包含 Git。
sandbox.commands.run(
"apt-get update -qq && apt-get install -y -qq git",
timeout=120,
)
sandbox.git.clone(
"https://github.com/octocat/Hello-World.git",
path="/tmp/hello-world",
depth=1,
)
print(sandbox.git.status("/tmp/hello-world"))sandbox.run_code()查看示例 ↓
import os
from e2b_code_interpreter import Sandbox
# 需要安装 e2b-code-interpreter,并与 e2b SDK 版本保持匹配。
with Sandbox.create(template=os.environ["CUBE_TEMPLATE_ID"]) as sandbox:
execution = sandbox.run_code("x = 6 * 7; x")
print(execution.results[0].text)快照、Volume 与模板
6createlistdelete_snapshot查看示例 ↓
import os
from e2b import Sandbox
with Sandbox.create(template=os.environ["CUBE_TEMPLATE_ID"]) as sandbox:
sandbox.files.write("/tmp/state.txt", "snapshot me")
snapshot = sandbox.create_snapshot(name="docs-example")
paginator = Sandbox.list_snapshots(name="docs-example")
while paginator.has_next:
print([item.snapshot_id for item in paginator.next_items()])
Sandbox.delete_snapshot(snapshot.snapshot_id)sandbox.fork()查看示例 ↓
import os
from e2b import Sandbox
with Sandbox.create(template=os.environ["CUBE_TEMPLATE_ID"]) as parent:
parent.files.write("/tmp/shared.txt", "before fork")
children = parent.fork(count=2, timeout=60)
for child in children:
if isinstance(child, Exception):
print("fork failed:", child)
continue
print(child.files.read("/tmp/shared.txt"))
child.kill()Volume.createconnectlistdestroy查看示例 ↓
from uuid import uuid4
from e2b import Volume
volume = Volume.create(f"docs-{uuid4().hex[:8]}")
connected = Volume.connect(volume.volume_id)
print(connected.volume_id)
print([(item.volume_id, item.name) for item in Volume.list()])
Volume.destroy(volume.volume_id)volume.read_filewrite_filelist查看示例 ↓
from uuid import uuid4
from e2b import Volume
volume = Volume.create(f"docs-{uuid4().hex[:8]}")
try:
volume.write_file("/hello.txt", "hello", force=True)
print(volume.read_file("/hello.txt"))
print([entry.name for entry in volume.list("/")])
finally:
Volume.destroy(volume.volume_id)Template.build() fluent API查看示例 ↓
from uuid import uuid4
from e2b import Template
template = (
Template()
.from_python_image("3.12")
.run_cmd("python --version")
)
# 构建可能需要几分钟,并会在当前项目中创建模板。
Template.build(template, f"docs-python-{uuid4().hex[:8]}")Sandbox.create(template=snapshot_id)查看示例 ↓
import os
from e2b import Sandbox
with Sandbox.create(template=os.environ["CUBE_TEMPLATE_ID"]) as source:
source.files.write("/tmp/state.txt", "portable state")
snapshot = source.create_snapshot()
try:
# E2B 的快照本身就是模板,任何节点都可以恢复。
with Sandbox.create(template=snapshot.snapshot_id) as restored:
print(restored.files.read("/tmp/state.txt"))
finally:
Sandbox.delete_snapshot(snapshot.snapshot_id)平台服务
4sandbox.get_mcp_token()查看示例 ↓
import os
from e2b import Sandbox
mcp = {
"github/modelcontextprotocol/servers": {
"run_cmd": "npx -y @modelcontextprotocol/server-everything"
}
}
with Sandbox.create(template=os.environ["CUBE_TEMPLATE_ID"], mcp=mcp) as sandbox:
print(sandbox.get_mcp_token())Sandbox.create(iam=...)查看示例 ↓
import os
from e2b import Sandbox, Secret
identity = Secret.iam_token(
audience="sts.amazonaws.com",
token_type="JWT-SVID",
)
with Sandbox.create(template=os.environ["CUBE_TEMPLATE_ID"], iam={"tokens": {"aws": identity}}) as sandbox:
print(sandbox.sandbox_id)Secret.*查看示例 ↓
from uuid import uuid4
from e2b import Secret
name = f"docs-{uuid4().hex[:8]}"
created = Secret.create(name, "initial-value")
Secret.update(name, "rotated-value")
print(Secret.get_info(name)) # 密钥值只可写入,不会由查询接口返回。
print(Secret.list(limit=10).next_items())
Secret.destroy(created.secret_id)AsyncSandboxasync resources查看示例 ↓
import os
import asyncio
from e2b import AsyncSandbox
async def main():
sandbox = await AsyncSandbox.create(template=os.environ["CUBE_TEMPLATE_ID"])
try:
result = await sandbox.commands.run("echo async")
print(result.stdout)
finally:
await sandbox.kill()
asyncio.run(main())Cube 扩展能力
5sandbox.rollback()查看示例 ↓
from cubesandbox import Sandbox
with Sandbox.create() as sandbox:
sandbox.files.write("/tmp/value.txt", "v1")
snapshot = sandbox.create_snapshot()
try:
sandbox.files.write("/tmp/value.txt", "v2")
sandbox.rollback(snapshot.snapshot_id)
print(sandbox.files.read("/tmp/value.txt"))
finally:
Sandbox.delete_snapshot(snapshot.snapshot_id)Volume.create(driver=...)VolumeMount(read_only=True)查看示例 ↓
from cubesandbox import Sandbox, Volume, VolumeMount
# CUBE_VOLUME_DRIVER 必须指向已经配置的 Cube Volume 插件。
volume = Volume.create("docs-data", driver=os.environ["CUBE_VOLUME_DRIVER"])
try:
with Sandbox.create(
volume_mounts={"/data": VolumeMount(volume, read_only=True)}
) as sandbox:
print(sandbox.files.list("/data"))
finally:
Volume.destroy(volume.volume_id)sandbox.update_network(rules=...)查看示例 ↓
from cubesandbox import Action, Match, Rule, Sandbox
rule = Rule(
name="allow-example-api",
match=Match(host="api.example.com", path="/v1/"),
action=Action(allow=True, audit="metadata"),
)
with Sandbox.create() as sandbox:
sandbox.update_network({"rules": [rule]})Sandbox.create(distribution_scope=...)查看示例 ↓
from cubesandbox import Sandbox
# 使用 CubeOps 返回的计算节点 ID 或宿主机 IP。
with Sandbox.create(
distribution_scope=[os.environ["CUBE_COMPUTE_NODE"]]
) as sandbox:
print(sandbox.sandbox_id)GET /v1/metrics/resource查看示例 ↓
import os
from urllib.request import urlopen
cubelet_url = os.environ.get("CUBELET_URL", "http://127.0.0.1:9998")
url = f'{cubelet_url.rstrip("/")}/v1/metrics/resource'
with urlopen(url, timeout=5) as response:
print(response.read().decode())03 · Cube API
REST API 兼容性
SDK 之下的协议层:逐端点对比 e2b 客户端与 CubeAPI 的路径、入参、响应与错误码。
沙箱
14POST/v2/sandboxes有差异缺 secure/iam 入参,其余一致(响应含 alias)。
GET/v2/sandboxes有差异支持 state/metadata 筛选;缺排序、开始时间和模板筛选。
GET/sandboxes/{id}一致必填字段齐全(endAt、alias、磁盘大小)。
DELETE/sandboxes/{id}有差异路由一致;404 时 e2b 返回 False、Cube 抛异常。
POST/v2/sandboxes/{id}/connect有差异缺 on_resume 参数;恢复固定回到保存时的状态。
POST/sandboxes/{id}/pause不兼容keep_memory=false 被拒绝;暂停总是保留内存。
POST/sandboxes/{id}/timeout一致完全一致;-1 表示永不超时。
POST/sandboxes/{id}/fork一致一致;一次快照派生多份,逐个返回结果。
POST/sandboxes/{id}/snapshots不兼容mode="filesystem" 被拒绝;快照总是保留内存。
PUT/sandboxes/{id}/network有差异缺 egress_proxy、https_ports 参数。
GET/sandboxes/{id}/metrics缺失无此路由;请使用 Cubelet 指标。
POST/sandboxes/{id}/resumeCube 独有Cube 独有:显式恢复。
POST/sandboxes/{id}/rollbackCube 独有Cube 独有:原地回滚。
POST/sandboxes/{id}/cloneCube 独有Cube 独有:克隆沙箱。
快照
3GET/snapshots一致支持按名称过滤,分页一致。
DELETE/templates/{id or name}一致按 ID 或名称删除快照,返回 bool;名称歧义时返回 409。
DELETE/templates/{id}一致模板删除一致。
模板
10GET/templates有差异返回扁平列表;缺 public/builds 字段。
GET/templates/aliases/{alias}不兼容缺 aliases 字段,e2b 模型解析失败。
GET/templates/{id}一致字段齐全。
POST/templates不兼容构建模型不同:e2b 收 Dockerfile DSL,Cube 收镜像引用。
GET/v2/templates缺失无 v2 模板路由(tags 相关)。
POST/templates/tags缺失无 tags 模型。
GET/templates/{id}/builds/{bid}/status有差异缺 log_entries;多 progress/message。
GET/templates/{id}/builds/{bid}/logsCube 独有Cube 独有:构建日志。
POST/templates/{id}/rebuildCube 独有Cube 独有:重新构建。
PUT/templates/{id}/aliasCube 独有Cube 独有:写入别名。
Volume
5POST/volumes一致一致(name 可省略;响应缺 domain 字段)。
GET/volumes一致一致。
GET/volumes/{id}一致一致(缺 domain 字段)。
DELETE/volumes/{id}一致一致;404 返回 False。
*/volumecontent/**缺失卷内容 API 整组缺失(7 个端点);请先挂载,再通过 sandbox.files 访问。
ENVD 数据面
1RPCprocess / filesystem / files / execute一致命令、文件、PTY、代码执行全部一致。
数据面不经 CubeAPI:CubeProxy 直接转发到沙箱内的 envd(编译自上游 e2b-dev/infra),请求体、流式分片与错误帧完全一致;固定地址流量通过 E2b-Sandbox-* 头路由。