
从 bash 到结构化沙箱Cloudflare Agents 的 cloudflare/shell 演进全解析【免费下载链接】agentsBuild and deploy AI Agents on Cloudflare项目地址: https://gitcode.com/GitHub_Trending/agents1/agentscloudflare/shell是 Cloudflare Agents 项目一个用于在 Cloudflare Workers 上构建与部署 AI Agent 的框架中的核心实验性包它把传统解析 shell 语法的执行方式替换为在隔离 Worker 中运行 JavaScript、并通过类型化state对象操作文件系统的沙箱模型。本文以packages/shell/CHANGELOG.md为骨架按版本时间线0.1.0 → 0.4.3逐项拆解每一次 API 演进背后的动机、语义变更与底层实现并结合packages/shell/src下的源码与packages/shell/README.md中的实战示例帮助读者理解这一文件系统运行时的设计取舍掌握在 codemode 沙箱中组合state.*与git.*工具的方法。一、定位与设计理念为什么取代 bash 解释器根据packages/shell/README.md的官方定义cloudflare/shell是 Sandboxed JavaScript execution and filesystem runtime for Cloudflare Workers agents。它不是bash 解释器不解析 shell 语法、不暴露管道、不模拟 POSIX shell 行为而是执行 JavaScript。其设计目标来自 README Design goals 一节包括结构化状态操作而非 shell 语法解析粗粒度的宿主端操作如glob()、diff()以减少频繁的 RPC 通信同时兼容临时性内存状态InMemoryFs与持久化Workspace安全执行默认提供隔离级别的超时与出站网络阻断。包的核心组成README What it is包括运行时无关的StateBackend接口、FileSystem接口及其两种实现InMemoryFs临时、WorkspaceFileSystem持久、把任意FileSystem包装成StateBackend的FileSystemStateBackend、SQLite 可选 R2 的持久文件存储Workspace、面向 codemode 的stateTools()/gitTools()ToolProvider以及一套带类型声明的state标准库。包与cloudflare/codemode的分工是codemode 负责执行编排工具调用的沙箱 JavaScriptshell 负责提供文件系统后端与 ToolProvider。二、0.1.0初始架构——Workspace、InMemoryFs 与 stateTools0.1.0PR #1122是包的首次发布一次性引入了完整的能力矩阵Workspace持久化 SQLite R2 存储、InMemoryFs、统一的FileSystem接口、FileSystemStateBackend以及用于把state.*组装进 codemode 沙箱执行的stateTools(workspace)/stateToolsFromBackend(backend)。2.1 Workspace 的存储模型从 packages/shell/src/filesystem.ts 的实现看Workspace把虚拟文件系统建模为一张 SQLite 表主键path辅以parent_path、name、typefile|directory|symlink、mime_type、size、storage_backendinline|r2、r2_key、content_encoding、content、target以及created_at/modified_at时间戳并在parent_path上建索引见 filesystem.ts#L332-L350。大文件落盘的阈值常量DEFAULT_INLINE_THRESHOLD为1_500_000 字节约 1.5 MB见 filesystem.ts#L184。当写入内容size threshold且配置了r2桶时文件内容被写入 R2SQLite 行只保留元数据与r2_key反之则以内联inline方式把内容文本为utf8字节数据为base64直接存入 SQLite写入分支见 filesystem.ts#L756-L848。未配置 R2 而写入大文件时会发出console.warn提示内联存储可能触及 SQLite 行大小限制。2.2 FileSystem 接口与 InMemoryFsFileSystem是运行时无关的抽象接口两种实现分别是临时性的InMemoryFs0.1.1中被重写为基于树而非扁平 map 的存储和把Workspace适配成FileSystem的WorkspaceFileSystem。WorkspaceFileSystem处理两类关键差异Workspace.readFile/readFileBytes对缺失文件返回null而FileSystem要求抛出带code: ENOENT的错误Workspace.stat/lstat返回FileStat而FileSystem.stat/lstat返回FsStat { type, size, mtime, mode? }见 packages/shell/src/workspace.ts#L26-L161。createWorkspaceStateBackend(workspace)则是复用WorkspaceFileSystem将其包装进FileSystemStateBackend的工厂见 workspace.ts#L172-L176。2.3 stateTools向沙箱暴露 state.*stateTools(workspace)返回一个名为state的ToolProvider把StateBackend的全部方法映射为沙箱内可调用的工具实现见 packages/shell/src/workers.ts#L39-L87并随包附带一套 TypeScript 类型声明STATE_TYPES让 LLM 拿到准确的函数签名。StateBackend接口本身非常庞大包含读写文件、JSON 辅助、搜索替换、目录查询、归档压缩、结构化编辑规划等 46 个方法完整接口见 packages/shell/src/backend.ts#L275-L368方法名列表见 backend.ts#L370-L417。一个最小的内存后端用法README 示例import { createMemoryStateBackend } from cloudflare/shell; import { stateToolsFromBackend } from cloudflare/shell/workers; import { DynamicWorkerExecutor, resolveProvider } from cloudflare/codemode; const backend createMemoryStateBackend({ files: { /src/app.ts: export const answer foo;\n } }); const executor new DynamicWorkerExecutor({ loader: env.LOADER }); const result await executor.execute( async () { const text await state.readFile(/src/app.ts); await state.writeFile(/src/app.ts, text.replace(foo, bar)); return await state.readFile(/src/app.ts); }, [resolveProvider(stateToolsFromBackend(backend))] );三、0.2.0SqlBackend 接口——从 Agents 专用走向通用0.2.0PR #1174是一次关键的抽象重构把原先基于 tagged-template 的 SQL 宿主接口替换为普通SqlBackend接口Workspace现在通过单个 options 对象接受SqlStorage、D1Database或任意自定义的{ query, run }后端。从源码看SqlBackend只要求两个方法见 filesystem.ts#L37-L43export interface SqlBackend { queryT Recordstring, SqlParam( sql: string, ...params: SqlParam[] ): T[] | PromiseT[]; run(sql: string, ...params: SqlParam[]): void | Promisevoid; }其中SqlParam string | number | boolean | null。SqlSource类型在运行时被自动识别拥有databaseSize属性的判定为SqlStorageDO 内建 SQLite拥有prepare/batch的判定为D1Database其余视为裸SqlBackend判定与适配见 filesystem.ts#L46-L92。这使Workspace可以脱离 Agents 框架运行在任意 Durable Object 或 D1 数据库之上// Durable Object任意带 SQLite 存储的 DO const workspace new Workspace({ sql: ctx.storage.sql }); // D1 const workspace new Workspace({ sql: env.MY_DB }); // Agent带 R2 与懒加载 name用于可观测性 class MyAgent extends AgentEnv { workspace new Workspace({ sql: this.ctx.storage.sql, r2: this.env.WORKSPACE_FILES, name: () this.name }); }WorkspaceOptions的完整字段见 filesystem.ts#L96-L116字段类型默认值说明sqlSqlSource必填SqlStorage、D1Database或自定义SqlBackendnamespacestringdefault隔离本 workspace 表的名字空间须以字母开头且只含字母数字下划线r2R2Bucket无大文件存储的 R2 桶可选r2Prefixstring取nameR2 对象键前缀inlineThresholdnumber1_500_000文件溢出到 R2 的字节阈值onChange(event) void无文件/目录变更回调事件含type: create|update|delete、path、entryTypenamestring \| (() string \| undefined)无用作默认 R2 前缀与可观测性事件名函数形式支持在 DO 中this.name尚未赋值时懒求值四、0.3.x 系列git 集成、健壮性与并发安全4.1 0.3.0isomorphic-git 集成0.3.0PR #1136新增cloudflare/shell/git导出提供纯 JS、由 Workspace 文件系统支撑的 git 操作createGit(filesystem)用于直接调用gitTools(workspace)则是面向 codemode 沙箱的ToolProvider并自动注入鉴权 token。可用的 git 命令有init、clone、status、add、rm、commit、log、branch、checkout、fetch、pull、push、diff、remoteREADME 中列出ToolProvider 中命令枚举见 packages/shell/src/git/provider.ts#L65-L80。Agent 内组合使用README 示例import { Agent } from agents; import { Workspace, WorkspaceFileSystem } from cloudflare/shell; import { createGit } from cloudflare/shell/git; class MyAgent extends AgentEnv { workspace new Workspace({ sql: this.ctx.storage.sql, name: () this.name }); async run() { const git createGit(new WorkspaceFileSystem(this.workspace)); await git.clone({ url: https://github.com/org/repo, depth: 1 }); await this.workspace.writeFile(/README.md, # Updated); await git.add({ filepath: . }); await git.commit({ message: update readme, author: { name: Agent, email: agentexample.com } }); await git.push({ token: this.env.GITHUB_TOKEN }); } }4.2 0.3.2修复 git.clone 的 ENOENT0.3.2PR #1249修复了不带depth的git.clone()失败于ENOENT: .git/shallow的问题。根因是 git 文件系统适配器的unlink没有携带.code错误码导致 isomorphic-git 无法把缺失文件当作可忽略场景处理修复方式是让unlink包装带.code的错误使 isomorphic-git 能优雅处理不存在的文件。4.3 0.3.3Workspace 幂等构造0.3.3PR #1333改变了Workspace的构造语义同一{sql, namespace}上的重复构造默认幂等——只要影响持久存储位置的选项r2、r2Prefix、inlineThreshold一致就允许并存。此前任何二次构造都会抛出Workspace namespace ns is already registered on this agent这会卡死两类合理场景Vite HMR 对仍存活的ctx.storage.sql重新求值 DO 模块以及接受sql并临时构造短生命周期Workspace的辅助函数。源码中通过workspaceRegistryWeakMap键为SqlSource记录每个{sql, namespace}的存储配置若二次构造传入不同的r2/r2Prefix/inlineThreshold构造函数会抛出指明冲突字段与双方取值的错误——因为存储选项分歧会让大文件被路由到不同 R2 key 或按不同尺寸分类导致通过一个实例读不到另一个实例写入的数据见 filesystem.ts#L203-L288。onChange被有意排除在一致性检查之外每个实例只为自己的写入调用自己的监听器这是既有的按实例语义。4.4 0.3.4WorkspaceFsLike——跨 DO 代理的钥匙0.3.4PR #1384引入WorkspaceFsLikeWorkspaceFileSystem构造函数与createWorkspaceStateBackend参数不再要求具体Workspace只要求 16 个文件系统方法的Pick集合见 filesystem.ts#L162-L180。这是一次非破坏性变更——Workspace本身天然满足WorkspaceFsLike既有调用点无需修改。它的实际价值在于开发者可以把真实Workspace包在自己的代理层后面最常见的是跨 Durable Object 代理通过 RPC 把每个调用转发给父 Agent 的 workspace再把它作为 codemodestate.*沙箱 API 的存储后端传给createWorkspaceStateBackend。仓库中 examples/assistant 的SharedWorkspace提供了这一端到端模式。4.5 0.3.5 / 0.3.6 / 0.3.8依赖演进与隐藏 Basic 认证0.3.5isomorphic-git从^1.37.5升到^1.37.6运行时cloudflare/vitest-pool-workers升到^0.15.1仅测试不影响发布产物包自身无 API 与运行时行为变化。0.3.6PR #1431为 shell 的 git 工具 Provider 增加隐藏的默认 Basic auth 凭据。0.3.8常规依赖更新。Basic 认证的使用方式README 示例——适用于需要 Basic auth 的 Git 服务器import { stateTools } from cloudflare/shell/workers; import { gitTools } from cloudflare/shell/git; const providers [ resolveProvider(stateTools(this.workspace)), resolveProvider( gitTools(this.workspace, { auth: { username: git, password: this.env.GIT_PASSWORD } }) ) ];鉴权注入逻辑在 packages/shell/src/git/provider.ts#L83-L142clone/fetch/pull/push四个命令会被自动注入默认凭据AUTH_COMMANDS集合如果同时配置了auth与token优先使用auth若沙箱调用本身显式传了token/username/password则直接调用级凭据覆盖所有默认值。默认凭据对 LLM 全程不可见。4.6 0.3.7二进制值跨沙箱边界保真0.3.7PR #1521修复了二进制值在 codemode 工具调用间丢失的问题Uint8Array参数与结果现在能完整穿过沙箱边界——这使 codemode 中的state.writeFileBytes()可以接收字节数组readFileBytes()的结果也保持为Uint8Array。4.7 0.3.9并发写下的父目录幂等创建0.3.9PR #1613解决了一个并发竞态两个写入同时把文件创建到同一个缺失目录时此前会暴露 SQLite 主键约束错误。修复后ensureParentDir在创建隐式父目录时使用INSERT OR IGNORE ... RETURNING path若插入成功则发出一次目录 create 事件若插入被忽略说明并发方已创建则回读该行确认类型是目录从而幂等创建、且只发出一个目录创建事件实现见 filesystem.ts#L1470-L1532。五、0.4.x 系列StateConnector 与 D1 范围扫描优化5.1 0.4.0StateConnector——codemode durable 运行时的正式连接器0.4.0PR #1656是 0.4 系列最重要的一次演进新增StateConnector把state.*文件系统 API 正式变成 codemode 的 connectorstateConnector(ctx, backend) // 或 new StateConnector(ctx, backend)它会暴露StateBackend的每个方法readFile、writeFile、editFile、ls、find、grep、readJson、mergeJson等作为 codemode durable 运行时的 connector 工具。两个值得注意的语义源码注释与实现见 packages/shell/src/connector.ts统一对象参数沙箱内模型以state.readFile({ path })、state.writeFile({ path, content })形式调用——每个方法只接受单个对象参数与位置参数风格的ToolProvider表面刻意不同connector 通过STATE_METHODS元数据把对象参数映射回后端的 positional 参数callStateMethod。读操作标记replay: reexecute纯读取的结果文件内容、目录列表、搜索命中永不进入 durable 重放日志恢复执行时会重新读取文件系统只有写操作被记录并重放。这避免了把大文件内容重复写入日志是 durable 执行下节省存储与日志体积的关键设计。getTypeScriptTypes()返回STATE_TYPES作为权威签名。同时旧的createStateToolProvider/stateTools路径保持不变并且现在同样接受对象风格参数——stateTools的实现通过isObjectArgsCall检测单个纯对象、键是方法参数名子集的调用形态自动路由到对象→positional 映射其余形态按原有 positional 调用处理保证旧沙箱代码兼容见 packages/shell/src/workers.ts#L17-L66。state.*的类型声明与系统提示词也同步更新为对象参数约定。配合createCodemodeRuntime的使用方式connector.ts 内嵌文档import { stateConnector, createWorkspaceStateBackend } from cloudflare/shell; const runtime createCodemodeRuntime({ ctx: this.ctx, executor, connectors: [ stateConnector(this.ctx, createWorkspaceStateBackend(this.workspace)) ] });5.2 0.4.2用主键范围扫描取代 LIKE 匹配0.4.2PR #1716是一次针对 D1 的 SQL 优化在Workspace.rm({ recursive: true })与glob预过滤中用主键范围扫描取代LIKE模式匹配。D1 此前会拒绝复杂的LIKE ? ESCAPE ?查询并抛出D1_ERROR: LIKE or GLOB pattern too complex: SQLITE_ERROR范围谓词path {dir}/ AND path {dir}0绕开了该限制直接扫描path索引且无需对路径名中的%/_做转义。实现细节非常精巧glob先通过getGlobPrefix提取无通配符的前缀——若前缀以/结尾目录前缀使用path ? AND path ?的[prefix, prefix去掉尾部斜杠0]半开区间因为0是/的下一个字符该区间恰好覆盖目录下所有子路径若模式不含通配符则退化为path ?精确匹配再由正则做最终过滤见 filesystem.ts#L1070-L1105。递归删除deleteDescendants采用完全相同的范围扫描策略批量收集 R2 键、批量删除 R2 对象并执行DELETE ... WHERE path ? AND path ?见 filesystem.ts#L1534-L1566。5.3 0.4.1 / 0.4.3文档随包发布与依赖下限0.4.1PR #1772把每个包的文档含 Workspace 指南docs/index.md包含进其发布的 npm 包中——package.json的files字段确实列出了dist、docs、README.md见 packages/shell/package.json#L55-L59。0.4.3PR #1977要求cloudflare/codemode0.5.0 或更新版本——这也与package.json中cloudflare/codemode: 0.5.0的依赖声明一致见 package.json#L13-L16当前包版本为0.4.3。六、实战在 codemode 沙箱中组合 state.* 与 git.*把stateTools与gitTools一起作为 Provider 传入可以让沙箱内的 LLM 同时拥有文件系统操作与 git 操作能力且默认凭据自动注入、对模型不可见import { Agent } from agents; import { Workspace } from cloudflare/shell; import { stateTools } from cloudflare/shell/workers; import { gitTools } from cloudflare/shell/git; import { DynamicWorkerExecutor, resolveProvider } from cloudflare/codemode; class MyAgent extends AgentEnv { workspace new Workspace({ sql: this.ctx.storage.sql, r2: this.env.MY_BUCKET, name: () this.name }); async run(code: string) { const executor new DynamicWorkerExecutor({ loader: this.env.LOADER }); return executor.execute(code, [ resolveProvider(stateTools(this.workspace)), resolveProvider(gitTools(this.workspace, { token: this.env.GITHUB_TOKEN })) ]); } }沙箱内典型的克隆 → 修改 → 提交 → 推送工作流await git.clone({ url: https://github.com/org/repo, depth: 1 }); const plan await state.planEdits([ { kind: replace, path: /repo/src/app.ts, search: foo, replacement: bar }, { kind: writeJson, path: /repo/config.json, value: { enabled: true } } ]); await state.applyEditPlan(plan); await git.add({ filepath: . }); await git.commit({ message: fix: update app, author: { name: Agent, email: agentexample.com } }); await git.push();七、state对象 API 总览与 shell 命令对照沙箱内的state对象按功能分组README 完整清单原始文件系统readFile、writeFile、appendFile、readFileBytes、writeFileBytes、mkdir、rm、cp、mv、symlink、readlink、realpath、readdir、glob、stat、lstat、exists、diff、diffContentJSON 辅助readJson、writeJson、queryJson、updateJson搜索与替换searchText、searchFiles、replaceInFile、replaceInFiles支持dryRun、rollbackOnError文件系统查询find、walkTree、summarizeTree归档与压缩createArchive、listArchive、extractArchive、compressFile、decompressFile、hashFile、detectFile结构化编辑规划planEdits、applyEditPlan、applyEdits。README 还给出了 shell 命令到state的粗略翻译表方便迁移心智模型cat→readFilegrep→searchText/searchFilessed→replaceInFile/replaceInFilesjq→readJson/queryJson/updateJsontar→createArchive/listArchive/extractArchivegzip→compressFile/decompressFilesha256sum/file→hashFile/detectFilegit→git.*。批量写入默认在任一失败时整体回滚设置rollbackOnError: false可允许部分进度replaceInFiles与applyEdits的失败还会抛出StateBatchOperationError携带operation、rolledBack、rollbackError字段见 packages/shell/src/backend.ts#L252-L269。八、演进总结与阅读路线从版本时间线可以清晰地看到cloudflare/shell的演进脉络版本主题关键结论0.1.0初始发布以 Workspace / InMemoryFs / FileSystemStateBackend / stateTools 奠定结构化 JS 沙箱架构0.1.1InMemoryFs 重写树存储替代扁平 map0.2.0SqlBackend从 tagged-template 到{query, run}接口支持任意 DO / D10.3.0git 集成cloudflare/shell/gitisomorphic-git 自动注入鉴权0.3.2–0.3.9健壮性ENOENT 修复、幂等构造、WorkspaceFsLike、Basic auth、二进制保真、并发父目录0.4.0StateConnectorstate.*成为 codemode durable connector对象参数约定 replay: reexecute0.4.2D1 优化主键范围扫描取代 LIKE规避LIKE or GLOB pattern too complex0.4.3依赖下限要求cloudflare/codemode 0.5.0如需深入源码推荐的阅读顺序是packages/shell/src/backend.tsStateBackend 契约→ packages/shell/src/filesystem.tsWorkspace 存储引擎→ packages/shell/src/workspace.tsFileSystem 适配→ packages/shell/src/workers.ts 与 packages/shell/src/connector.ts工具暴露与 durable 集成→ packages/shell/src/git/provider.tsgit 工具与鉴权注入。包内测试覆盖了 workspace、git、内存后端、状态连接器与逐出等场景packages/shell/src/tests是理解各版本行为变更的最佳佐证。需要注意该包在 README 中明确标注为ExperimentalAPI 表面仍在收敛预期会有破坏性变更。【免费下载链接】agentsBuild and deploy AI Agents on Cloudflare项目地址: https://gitcode.com/GitHub_Trending/agents1/agents创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考