@cloudflare/computer 0.2.x 版本演进全解读:Worker 后端、Git 增强与执行可靠性改进
【免费下载链接】computerGive your agent a computer 👾项目地址: https://gitcode.com/GitHub_Trending/computer1/computer
导读
@cloudflare/computer是 Cloudflare Computer 项目的核心 npm 包,它把 Durable Object、SQLite 与多种执行后端(容器 shell、Worker shell、Worker JavaScript)组合成可供 AI Agent 使用的工作区(Workspace)。本文以 packages/computer/CHANGELOG.md 为主线,逐一拆解 0.2.0 与 0.2.1 两个版本引入的架构级变更:Worker 后端的模块化瘦身、egress 网络策略统一、文件系统工具集增强、git 模块的 revision 支持,以及执行中断后的容器重连恢复机制。阅读完成后,你将掌握这些变更背后的设计动机、对应的源码位置与可落地的配置方式。
版本总览:0.2.x 带来了什么
- 0.2.0:一次"整合 + 扩充"的小版本。将 egress 配置在三个执行后端间统一;通过 RPC 暴露有界(bounded)的工作区字节读取并返回带文件元数据的分页目录列表;
read工具支持图片与 data URI 格式;find/grep工具支持更多过滤参数并新增delete工具;通过 worker-shell 把大量命令改为按需引入(opt-in)以大幅压缩 bundle 体积。 - 0.2.1:一次聚焦可靠性的补丁版本。在 Worker JavaScript 能力(capability)超限错误中内嵌配置的字节上限;git 模块支持短 revision id;
container-shell操作在 computerd 重启后可安全重连,进程本地执行在容器替换后返回EEXEC_LOST。
下文按主题拆解,每个主题都同时给出"变更内容 + 源码/文档依据 + 实战含义"。
一、bundle 瘦身:worker-shell 命令按需引入
变更内容
0.2.0 通过 PR #41(commita753db7)"Reduced the size of bundles using worker-shell by making many commands opt-in",即把 worker-shell 中较重的命令拆成"核心组 + 可选组",只有被 import 的命令组才会进入最终 bundle。官方文档 docs/12_worker_backend.md 的 "Optional shell commands" 一节对此有完整说明。
实现机制
从源码看,这一设计建立在"以 import 而非编译期 flag 选择"的机制之上(见 packages/computer/src/backends/worker-shell/shell-modules.ts):
- 构建脚本
build-bundle.mjs以splitting: true运行 esbuild,把产物按模块切分为一个核心组(所有常驻命令 +ShellWorker入口)和每个可选命令各自的一组,并分别发布到@cloudflare/computer/shell/<feature>子路径; SHELL_CORE_MODULES是常驻的核心模块组,类型为Readonly<Record<string, { js: string }>>(模块名 → 源码字符串);assembleShellModules([...groups])把核心组与消费者传入的可选组合并,供需要自己构造 Loader 回调的外部 runtime source 使用。
由于核心模块组从不引用可选组,一个从未被 import 的组在消费者的模块图中不可达,bundler 会直接丢弃它——没有需要设置的构建标志,也没有默认开启的额外成本(这一点在 packages/computer/src/backends/worker-shell/shell-modules.ts 的注释中明确说明)。
如何按需引入命令
import curlModules from "@cloudflare/computer/shell/curl"; import htmlToMarkdownModules from "@cloudflare/computer/shell/html-to-markdown"; new WorkerShellBackend({ loader: env.LOADER, workspace: { binding: "ContainerExample", id: ctx.id.toString() }, ctx, commands: [curlModules, htmlToMarkdownModules], });完整可用的可选命令组为:curl、html-to-markdown、python、sqlite、js-exec、yq、file、xan、jq。核心组始终携带cat、ls、grep、sed、awk、sort等常驻命令。
两点值得注意的实现细节(均来自文档与源码):
curl运行在一个基于 isolate 全局fetch的SecureFetch适配器上——undici在构建期被重定向到抛错的 stub,永远不会被打包进去;- 即使引入了
curl,出站网络仍由后端的WorkspaceEgressPolicy管控,而不是由 shell 自行决定,因此"启用 curl"不等于"打开网络"。
测试侧也有对应约束:packages/computer/src/backends/worker-shell/shell-modules.test.ts 验证了assembleShellModules()与核心组等价、加入curlModules/sqliteModules后模块表被正确合并。
实战含义
对以 worker-shell 作为执行后端的 Agent 而言,bundle 体积直接决定 Worker 的冷启动与按量成本。把jq、python、sqlite这类重量级命令改成按需引入后,不用的功能在模块图中"物理消失",相比传统的 feature flag 方案更彻底。
二、egress 网络策略统一
变更内容
0.2.0 通过 PR #88(commit9ecb912)"Consolidate egress configuration across the existing backends",并在 examples/egress 提供了三后端对照示例。
egress 三模式速查
examples/egress/README.md 用EGRESS_MODE环境变量统一驱动容器 shell、Worker shell、Worker JavaScript 三个后端,模式对照如下:
EGRESS_MODE | Workspace 策略 | 行为 |
|---|---|---|
none | { mode: "none" } | 阻止出站网络访问(默认) |
all | { mode: "direct" } | 允许直接出站访问 |
custom | { mode: "http-gateway" } | 经由仅放行https://example.com的网关,其他来源返回403 |
在 Worker shell 后端中,模式直接体现为构造参数(见 docs/12_worker_backend.md 的 "Wire shape" 一节):
// 默认:阻断环境网络 new WorkerShellBackend({ loader: env.LOADER, workspace: { binding: "ContainerExample", id: ctx.id.toString() }, ctx, egress: { mode: "none" }, }); // 直接出站 new WorkerShellBackend({ /* ... */ egress: { mode: "direct" }, }); // 经由 Fetcher 网关 new WorkerShellBackend({ /* ... */ egress: { mode: "http-gateway", gateway, revision }, });网关模式与 isolate 缓存的关系
http-gateway模式需要稳定的revision:它让 Worker Loader 在网关策略未变化前复用同一个 isolate;没有revision时,每个后端生命周期都会使用全新的缓存身份。因为 egress 策略身份参与 isolate 缓存 key(缓存 id 形如workspace-shell:${workspace.id}再加上策略身份),一个策略变化绝不能复用一个网络权限更宽的 isolate。
这些模式约束的是 shell 发起的环境级fetch()与connect();宿主侧能力保持独立——例如宿主转发的 git 命令可以拥有自己的网络权限,而环境级 shell 网络被完全阻断。
运行示例验证
npm run dev --workspace @example/computer-egress -- --var EGRESS_MODE:none npm run dev --workspace @example/computer-egress -- --var EGRESS_MODE:all npm run dev --workspace @example/computer-egress -- --var EGRESS_MODE:customcurl -X POST 'http://127.0.0.1:8787/fetch?url=https%3A%2F%2Fexample.com'all模式下三个后端(container / worker-shell / worker-javascript)都返回200与text/html;none模式下各后端返回error对象;custom模式下访问 allowlist 之外(如cloudflare.com)的来源,三个后端一致返回403与text/plain。示例的目录结构(examples/egress)中Dockerfile打包 computerd、FUSE 库与 curl,wrangler.jsonc声明 Worker Loader、container、durable object 与EGRESS_MODE。
三、文件系统工具增强:有界读取、分页目录与图片支持
有界字节读取与分页目录列表
0.2.0 的 commit2bfce96通过 RPC 暴露了有界(bounded)的工作区字节读取,并让目录列表支持分页且附带文件元数据。这意味着在大文件或大目录场景下,工具层不再需要一次性把整个内容拉进 isolate 内存。
read工具支持图片与 data 格式
commit19a65bc扩展了read工具,使其支持图片与 data URI 格式。相关实现位于 packages/computer/src/tools/fs/read.ts:
- 结果类型包含
kind: "image" | "file" | "binary"等分支,图片/文件分支返回{ type: "data", data }的 data URI 形态; - 媒体类型识别由 packages/computer/src/tools/fs/media.ts 完成:既按扩展名(
.png/.jpg/.jpeg/.gif/.webp)也按**文件魔数(magic bytes)**判定,SVG 则按内容前缀识别为text类型(image/svg+xml); - 默认有"内联发送给模型的最大图片/PDF 大小"上限,空文件会返回
Cannot attach empty file错误(见 packages/computer/src/tools/fs/read.test.ts)。
测试 packages/computer/src/tools/fs/media.test.ts 覆盖了 PNG/JPEG/GIF/WEBP 的魔数识别,以及image.JPG这类大小写混合扩展名。
find/grep过滤参数与delete工具
commitf673226更新了find和grep,支持额外过滤参数,并新增了delete工具。以 packages/computer/src/tools/fs/find.ts 为例,find的输入 schema 包括:
path:绝对目录(默认/workspace);pattern:相对于path的 glob(如**/*.ts或src/?.js);limit/offset:分页参数,limit默认 200、上限 1000。
执行时后端会多取一条记录(limit + 1)来判断是否截断,并在结果中给出nextOffset供继续翻页——这是把"分页目录列表"能力落到工具层后的典型用法。
四、Worker JavaScript 后端:能力超限错误内嵌字节上限
变更内容
0.2.1 的 PR #102(commite09135b)"Embed the configured byte limit directly in Worker JavaScript capability size errors":当传给 Worker JavaScript 的能力(capability,例如注入的模块或参数)超过配置上限时,生成的错误信息会直接包含配置的字节上限,而不是含糊的"太大"。
源码依据
packages/computer/src/backends/worker-javascript/worker-javascript.test.ts 的测试用例includes the configured capability byte limit in generated errors展示了完整链路:
new WorkerJavaScriptBackend({ loader: { load }, maxCapabilityBytes: 256, }); // ... const capabilities = load.mock.calls[0]?.[0].modules["workspace-capabilities.js"]; expect(capabilities).toContain("exceeds 256 bytes");测试先以maxCapabilityBytes: 256构造后端并执行一次runtime.exec,然后断言生成的workspace-capabilities.js模块源码中包含 "exceeds 256 bytes"。也就是说,能力超限错误现在是可诊断、可量化的——错误文本直接告诉你"上限是多少",方便判断是该调大配置还是精简注入内容。
实战含义
对在runtime.exec中注入大模块或大参数的 Agent 而言,遇到超限错误时可以直接根据错误信息定位是配置问题还是输入问题,无需再翻源码确认上限值。
五、git 模块:短 revision id 支持
变更内容
0.2.1 的 PR #95(commit1e6c027)为 git 模块增加了对短 revision id的支持,即允许在 ref 位置使用完整 40 位 OID 的缩写形式(如abc1234)。
实现机制
从 packages/computer/src/git/reads.ts 看,解析层把 revision spec 分为"基础 ref + 祖先遍历步骤":
- 完整 OID 是 40 位十六进制;
isAbbreviatedOid(ref)判断ref.length < 40且全为[0-9a-f],据此识别缩写 OID; parseRevision用正则/^(.*?)((?:[\^~][0-9]*)*)$/把形如HEAD^、HEAD^2、HEAD~N、HEAD~2^2的gitrevisions(7)后缀与基础 ref 分离——~N展开为 N 个 first-parent 步骤,^N选择第 N 个父提交(1 基),后缀可左到右串联。
对应测试分散在 packages/computer/src/git/cli.test.ts:覆盖diff/log/show/reset --hard等命令对 revision 后缀的预解析(见其中pre-resolves revision suffixes in from/to refs、show pre-resolves a revision suffix to an oid、--hard resolves a revision suffix in the ref等用例)。这意味着 Agent 的 git 工具现在可以写出git show abc1234或git diff HEAD~2^2这类接近真实 git CLI 习惯的命令,由工具层在宿主侧解析成精确 OID 再执行。
六、执行可靠性:container-shell 重连与EEXEC_LOST
变更内容
0.2.1 的 PR #103(commit8afbb7c)解决了一个真实的生产痛点:
container-shell操作在computerd 重启后、当重试是安全的时候,会重新连接而不是直接失败;- 进程本地(process-local)执行在容器被替换后返回
EEXEC_LOST错误。
官方文档在 docs/05_runtime_interface.md 的 "Command synchronization" 一节对此有更详细说明。
源码依据
在 packages/computer/src/workspace.ts 的执行调度逻辑中,EEXEC_LOST被作为一等错误码处理:
- 当
intent.runtimeId !== undefined且错误码为EEXEC_LOST时,调度器清除该运行时的调度器条目,并返回{ status: "lost", backend, runtimeId, error }——即明确标记"执行丢失",而不是把它当作普通重试或失败; - 其他错误则走常规的重试路径:超过
retryMaxAttempts返回status: "exhausted",否则构造下一次尝试的 intent(attempt + 1、携带targetCursor)重新入调度队列。
EEXEC_LOST的定义在 packages/computer/src/workspace.ts 与 packages/computer/src/workspace.ts 两处(WorkspaceExecutionLostError),测试覆盖见 packages/computer/src/workspace.test.ts:既有直接rejects.toMatchObject({ code: "EEXEC_LOST" })的用例,也有"旧执行对象的kill()在容器替换后同样返回EEXEC_LOST"的用例。
设计含义
这套机制把"底层容器被替换"这一不可控事件,在 API 层转化为语义明确的EEXEC_LOST,让上层 Agent 应用能够区分"执行失败"与"执行丢失(可安全重试)",从而在 computerd 重启、容器漂移等场景下做出正确的重连与重试决策。
七、同步拉取的峰值内存优化
变更内容
0.2.0 的 PR #87(commit8758b51)优化了同步 pull 过程中的峰值内存:应用一个文件条目时,直接链接(link)发送方已暂存的 chunks,而不再把 chunks 读回来拼接成整个文件的 buffer。
为什么重要
改动前的做法是"读取所有 chunks → 拼接为整个文件 buffer → 写入",这会让 isolate 同时持有约两倍文件大小的内存(chunk 数据 + 拼接缓冲)。改动后应用路径只做 chunk 级链接。
源码侧的依据在 packages/dofs/src/sync/apply.ts:linkStagedChunksSync相关的注释明确写着 "Link a file entry to staged chunks without loading payload bytes"——声明的大小在不加载 payload 的前提下校验,chunk 哈希沿袭stageBlob的信任契约,随后调用linkStagedChunksSync完成链接。
实战含义
对工作区包含大文件(如二进制资产、大日志)的同步场景,这一改动显著降低 Durable Object 内单个 isolate 的峰值内存占用,降低 OOM 风险,也让大文件同步在内存受限的 Workers 环境里更可预期。
八、git 边界修复与 SQLite 批处理
0.2.0 的 PR #77(commit5062158)是一次"正确性 + 稳定性"补丁:
- 修复 git 的
diff/status/log各类边界情况(edge cases); - 在 Durable Object SQLite 的限制内批量处理同步哈希探测(sync hash probes),避免逐个探测触发 SQL 语句上限或性能问题;
- RPC 目标的计数改为按**身份(identity)**统计,避免同一目标被重复计数。
这三项分别对应:git 命令输出正确性、同步过程中的 SQLite 交互开销、以及运行时资源统计口径。对深度使用同步与 git 工具的 Agent 而言,它们直接影响结果的可靠性与大规模工作区下的稳定性。
总结与升级建议
回顾 0.2.x 两个版本,@cloudflare/computer的演进主线非常清晰:
| 维度 | 0.2.0 核心变更 | 0.2.1 核心变更 |
|---|---|---|
| 执行后端 | worker-shell 命令按需引入,bundle 大幅瘦身 | 能力超限错误内嵌字节上限 |
| 网络 | egress 策略三后端统一(none / direct / http-gateway) | container-shell 重连恢复 +EEXEC_LOST |
| 文件系统 | 有界读取、分页目录、图片/data 格式、delete工具 | — |
| git | diff/status/log 边界修复 | 短 revision id 支持 |
| 内存 | 同步 pull 改为 chunk 链接,峰值内存减半 | — |
对使用者而言,建议的落地顺序是:
- 若使用 worker-shell,尽快把
curl、sqlite、python等命令改为按@cloudflare/computer/shell/<feature>显式引入,观察 bundle 体积与冷启动变化; - 统一用
WorkspaceEgressPolicy的三种模式管理 Agent 网络边界,网关模式下务必配置稳定的revision以复用 isolate; - 在容器后端出现 computerd 重启或容器替换时,识别
EEXEC_LOST并走安全重试/重连路径,而不是盲目重放执行; - 升级后利用
read工具的图片与 data 格式支持,让多模态 Agent 能直接消费工作区内的图片资产。
进一步深入可阅读 docs/12_worker_backend.md(Worker 后端的完整设计与 fidelity gaps)、docs/05_runtime_interface.md(命令同步与运行时会话)、examples/egress(三后端 egress 对照示例)与 packages/computer/CHANGELOG.md 原文。
【免费下载链接】computerGive your agent a computer 👾项目地址: https://gitcode.com/GitHub_Trending/computer1/computer
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考