使用 Worker Loader 动态 Worker 作为工作区 Shell:computer 项目 worker-shell 后端实践指南

发布时间:2026/9/16 14:56:25

使用 Worker Loader 动态 Worker 作为工作区 Shell:computer 项目 worker-shell 后端实践指南 使用 Worker Loader 动态 Worker 作为工作区 Shellcomputer 项目 worker-shell 后端实践指南【免费下载链接】computerGive your agent a computer 项目地址: https://gitcode.com/GitHub_Trending/computer1/computer本文以cloudflare/computer开源仓库中的examples/worker-shell示例为主线讲解如何在 Cloudflare Worker Durable Object 之上通过worker_loaders绑定把一个**动态 WorkerDynamic Worker**当作 Workspace 的 Shell 来执行命令无需容器、无需 Dockerfile用 pre-bundled 的 just-bash 解释器即可获得cat、grep、awk、sed、jq等文本工具能力。读完本文你将掌握WorkerShellBackend的配置方式、Worker Loader 回调与WorkspaceServiceProxy回环的底层原理、命令特性组的按需打包机制以及完整可复现的本地运行与 HTTP 冒烟测试流程。概览一个没有容器的 Shellexamples/worker-shell是一个预览性质PREVIEW ONLY的示例API 尚不稳定设计仍在演进。它的定位是一个 Cloudflare Worker Durable Object 运行一个 Workspace而该 Workspace 的 Shell 是通过env.LOADER加载的动态 Worker。这个动态 Worker 内运行的 shell 是 just-bash 保持一致因此同样的curl配方可以直接复用只是底层不再依赖容器。与 container 后端相比worker-shell 的差异核心在于没有 Dockerfile、没有容器运行时examples/worker-shell/目录下不存在 Dockerfile动态 Worker 的生命周期完全交给 Worker Loader 管理README 原话 The DO is a thin host. Theres no Dockerfile; the Dynamic Worker lifecycle is the loaders problem.Shell 以预构建模块字符串pre-built module string的形式随包分发动态 Worker 的源码来自cloudflare/computer/backends/worker-shell而不是项目中的源码文件文件系统仍然落在宿主 DO 的 SQLite 上每次 exec 都会通过回环 RPC 回到宿主 DO存储句柄始终有效一个 Workspace 对应一个 DO 是天然边界。架构与请求链路README 给出了完整的架构示意client ─► Worker /c/name/{file,exec} │ (DO RPC calls) ▼ DO (ContainerExample) ──► Workspace ──► WorkerShellBackend │ │ env.LOADER.get(...) ▼ Dynamic Worker (ShellWorker) │ │ env.HOST.get(id) │ .getWorkspace() ▼ back to ContainerExample DO完整调用链可以拆解为五步对应 README 的 Architecture 小节DO 构造后端ContainerExampleDO 从cloudflare/computer/backends/worker-shell构造WorkerShellBackend传入 Loader 绑定、一个指向自身的{binding, id}引用以及ctx。后端内部完成剩余工作构建 Loader 回调包含代码分割的 shell 模块 seek-bzip 桩、通过ctx.exports.WorkspaceServiceProxy(...)铸造一个WorkspaceServiceProxy回环、调用env.LOADER.get(...).getEntrypoint(ShellWorker)并把得到的 Fetcher 转成ShellRPC。Loader 回调接线回环Loader 回调把WorkspaceServiceProxy回环接入动态 Worker 的env.HOST。这个代理是一个小型WorkerEntrypoint其getWorkspace()方法在宿主侧做 Durable Object 命名空间查找并返回WorkspaceStub。为什么要用代理因为一个原始的DurableObjectNamespace无法通过结构化克隆structured clone进入 Loader 的 env而代理产生的绑定形态的 Fetcher 可以。这一点在 entrypoint.ts 的ShellWorkerEnv注释中有明确说明。ShellWorker执行命令ShellWorker随cloudflare/computer/backends/worker-shell分发位于动态 Worker 内部。每次exec(input)都会调用env.HOST.getWorkspace()围绕WorkspaceFsAdapter包装的 stub 的.fs构建一个全新的Bash实例运行命令并在运行落定后释放dispose该 stub。文件系统 RPC 回宿主Bash内部产生的文件系统操作通过宿主 DO 自己的 RPC 面往返因此存储句柄始终有效一个 DO 对应一个 Workspace 成为天然边界。同步短路BackendHandle.sync为none。由于只有唯一的权威存储DO 的 SQLitepush 和 pull 直接短路运行时结果的pushed/pulled计数恒为零。在 worker-shell.ts 中可以看到第 1 步与第 5 步的落地WorkerShellBackend.connect()返回{ rpc, sync: none, close }其中sync显式声明为none且noopSync()中的push、fetchChanges、readEntry、hasObjects、watermarks等同步方法在被调用时会直接抛错must not be called从机制上杜绝了误用。配置解读wrangler.jsonc 与 Worker 入口Worker DO Loader 的声明式配置examples/worker-shell/wrangler.jsonc 是示例的核心配置四个关键块缺一不可{ $schema: node_modules/wrangler/config-schema.json, name: computer-worker-shell-example, main: src/index.ts, compatibility_date: 2026-05-26, compatibility_flags: [nodejs_compat, experimental], worker_loaders: [ { binding: LOADER } ], durable_objects: { bindings: [ { name: ContainerExample, class_name: ContainerExample } ] }, r2_buckets: [ { binding: Bucket, bucket_name: computer-worker-shell-hello } ], migrations: [ { tag: v1, new_sqlite_classes: [ContainerExample] } ] }worker_loaders是本示例的灵魂DO 通过env.LOADER.get(id, ...)铸造动态 Worker随后WorkerShellBackend将shell.exec分派进去durable_objects注册了与 container 示例同名的 DO 类ContainerExample注释明确说明 Mirrors examples/container beat for beat — same DO class name, same routes, same R2 mount. The only difference is the shellr2_buckets定义了挂在/workspace/r2的只读 Bucket详见下文 R2 挂载小节migrations用new_sqlite_classes声明 DO 使用 SQLite 存储compatibility_flags需要nodejs_compatjust-bash 依赖的 Node 兼容层与experimental。宿主 DO 的构造代码examples/worker-shell/src/index.ts 中ContainerExample通过withWorkspacemixin 拥有 Workspace并在 options 回调里装配后端与挂载export class ContainerExample extends withWorkspace(class extends DurableObjectEnv {}, (self) { const { ctx, env } self as unknown as { ctx: DurableObjectState; env: Env }; return { storage: ctx.storage as unknown as DurableObjectStorageLike, backends: [ new WorkerShellBackend({ loader: env.LOADER, workspace: { binding: ContainerExample, id: ctx.id.toString() }, ctx, commands: [curl, jq], }), ], mounts: { /workspace/r2: R2Bucket(env.Bucket), }, }; }) {}注意WorkerShellBackend的三个必填参数正是架构中描述的三要素loaderLoader 绑定、workspace指向自身 DO 的{binding, id}、ctx。在 worker-shell.ts 的构造函数里可以看到校验逻辑source未提供时三者缺一不可否则抛出 WorkerShellBackend requiressourceor all ofloader,workspace, andctx.。WorkerShellBackendOptions还支持以下可选参数源码级说明参数作用默认值id后端在 Workspace 中注册的选择器当同一 Workspace 承载多个同类型后端如不同 Loader 或不同 shell 配置时需覆盖worker-shellcompatibilityDate动态 Worker 的兼容性日期computer 包发布时的兼容日期源码中DEFAULT_COMPAT_DATE 2026-06-17compatibilityFlags追加到默认[nodejs_compat]之上的额外兼容性标志[nodejs_compat]commands核心之外要包含的命令特性组见下文[]egress出口网络策略{ mode: none }HTTP 文件与执行接口示例暴露了三组路由URL 与磁盘路径一一对应。核心语义继承自 README 的 HTTP surface 小节PUT /c/name/file/workspace/path raw body → writeFile at /workspace/path GET /c/name/file/workspace/path octet-stream of /workspace/path (any path outside /workspace returns 400) POST /c/name/exec { command | argv, cwd?, encoding? } cwd defaults to /workspace → JSON { exitCode, stdout, stderr }路径语义PUT /c/name/file/workspace/hello.txt写入/workspace/hello.txtGET /c/name/file/workspace/r2/x读取/workspace/r2/x—— URL 与磁盘路径总是严格匹配。任何落在/workspace之外的 URL 返回 400。这一约束在源码的resolveMountPath()中实现先校验候选路径必须等于/workspace或以/workspace/开头再拒绝任何包含..的路径src/index.ts。exec 语义命令默认在cwd /workspace下执行。command或argv二选一提供argv形式会先经shellQuote()做 shell 安全转义再拼接非[A-Za-z0-9_\-:,./%]的字符用单引号包裹。请求体支持encoding: utf8。返回值是{ exitCode, stdout, stderr }的 JSON。错误统一走errorJSON返回{ error, code }其中ENOENT映射为 404其余为 500。能力边界命令运行在 just-bash 解释器内——提供cat、grep、awk、sed、jq、sort等宽泛的文本类工具但不是完整的 Linux 用户态。此外动态 Worker 的globalOutbound: null因此 shell 自身无法访问公网详见 egress 讨论。命令特性组按需打包的 shell 能力这是 worker-shell 示例最具工程特色的一环shell 以「特性组feature groups」形式随包分发按需引入。README 与源码一致地阐述了这套机制cloudflare/computer/backends/worker-shell提供常开核心组always-on core外加每个命令一个可选组路径形如cloudflare/computer/shell/featureWorkerShellBackend将核心组与你传入commands选项的组合并并把合并结果展开进 Loader 回调的 modules 表一个从未被 import 的组在你的模块图中不可达打包器会直接丢弃它——选择命令就是加一行 import剔除命令就是删掉那行 import核心入口模块在冷启动时解析每个被选中的组的 chunk 保持冷状态直到脚本真正触及它。示例选择了curl和sqliteREADME 中的示例代码注示例源码 src/index.ts 实际引入的是curl与jqREADME 中的完整配置片段如下import curlModules from cloudflare/computer/shell/curl; import sqliteModules from cloudflare/computer/shell/sqlite; new WorkerShellBackend({ loader: env.LOADER, workspace: { binding: ContainerExample, id: ctx.id.toString() }, ctx, commands: [curlModules, sqliteModules], });源码 shell-modules.ts 揭示了合并机制的底层实现SHELL_CORE_MODULES是核心组的冻结快照assembleShellModules(groups)以核心组为底逐个Object.assign合并传入组键冲突时后组胜出但构建时保证组间不相交最后整体冻结。文件头注释说明这些模块由build-bundle.mjs用 esbuild 的splitting: true生成每次构建把产物分割为核心组 每个可选命令一组各自发布到cloudflare/computer/shell/feature子路径。可选组目前包括README 明示html-to-markdown、python、js-exec、yq、file、xan、jq以及curl、sqlite。注意python与js-exec依赖node:worker_threads无法在 workerd 中运行entrypoint.ts 中shellOptions注释有说明。ShellWorker 执行生命周期源码级动态 Worker 内部的核心类是 entrypoint.ts 导出的ShellWorker继承WorkerEntrypoint。每次exec的生命周期如下生成 id 与超时控制id缺省时用crypto.randomUUID()timeoutMs存在时设置定时器超时即controller.abort()并标记timedOut。同一 id 若已存在则抛EEXEC_BUSY获取工作区 stubawait this.env.HOST.getWorkspace()——注意每次 exec 独立获取并发 exec 各拿各的 stub没有共享实例状态可竞争装配自定义命令defineGitCommand(ws)、defineAssetsCommand(ws)、defineArtifactsCommand(...)再叠加子类的extraCommands(ws)钩子构建 Bash 实例new Bash({ fs: new WorkspaceFsAdapter(ws.fs), cwd, fetch, customCommands, defenseInDepth: { enabled: false }, executionLimits: { maxOutputSize: 1024 * 1024 } })。其中defenseInDepth必须关闭just-bash 的进程内防御盒通过node:module的registerHooks注册 ESM loader 钩子而 workerd 暴露该方法但抛 not implemented且 shell 本身已运行在隔离的动态 Worker 中真正的边界已经存在防御盒反而冗余executionLimits.maxOutputSize限制单次输出上限为 1 MiBfetch默认是defaultSecureFetch——对隔离区全局fetch的薄封装因此curl默认启用传入null可彻底移除curl传入自定义SecureFetch可做白名单或凭据注入异常与超时归并捕获的异常被折算为stderr文本退出码按timedOut ? 124 : aborted ? 130 : 1赋值124 对应超时、130 对应 SIGINT 语义事件帧输出把stdout/stderr/exit三个事件序列化为NDJSON每行一个 JSON 对象字节流返回。宿主侧 worker-shell.ts 的decodeFramedEvents负责按行解析帧校验字段id、递增seq、name为stdout|stderr|exit、值类型再把 utf8 字符串重新编码为Uint8Array以符合ExecEvent的线上形状非法帧抛EPROTOCOL释放资源finally中清理定时器、从执行表删除 id并ws[Symbol.dispose]?.()释放工作区 stub。getExec在当前实现中故意缺席ShellWorker.getExec直接抛ENOENTno such exec因为每次 exec 都是独立作用域本次隔离区看到的 id 无法被后续请求触达killExec则通过执行表找到对应AbortController并 abort在语句边界协作式停止 just-bash。WorkspaceFsAdapterjust-bash 与 Workspace 之间的桥adapter.ts 的WorkspaceFsAdapter把 just-bash 的IFileSystem接口适配到 Workspace 文件系统 stub。要点直接转发readFile、readdir、mkdir、rm、chmod、symlink、readlink、stat、lstat等一一映射合成实现appendFile读旧内容 拼接 写回、cp递归复制目录需recursive、mvcopy delete注释明确说明该方式非原子与 just-bash 其他适配器一致POSIX 兼容桩/dev、/dev/null为虚拟路径stat/readdir/readFile/writeFile对其有特殊处理使依赖/dev/null的脚本可用已知缺口fail loudly / documented no-oplink硬链接抛ENOSYS——存储不支持依赖硬链接的脚本会明确失败utimes是文档化的空操作——存储没有 atime 列无状态设计适配器不持有状态单实例可在多次 exec 间安全复用路径探针优化exists在宿主侧解析预期未命中而非把 ENOENT 抛过 Workers RPC避免 just-bash 的 PATH 探测触发 workerd 报告未捕获异常这正是下文「已知限制」中 PATH-walk 诊断的根源与缓解。R2 挂载与种子数据与 container 示例完全一致/workspace/r2通过R2Bucket(env.Bucket)挂载为只读 Bucket写操作会以EROFS拒绝。首次使用需播种一次数据package.json 中的脚本npm run seed:r2:local --workspace example/computer-worker-shell # 或部署后 npm run seed:r2 --workspace example/computer-worker-shell这两个脚本分别执行wrangler r2 object put computer-worker-shell-hello/hello.txt --file ./seed/data/hello.txt --local与--remote将 seed/data/hello.txt内容为hello world上传到computer-worker-shell-helloBucket。种子完成后GET /c/demo/file/workspace/r2/hello.txt即可读到hello world。本地运行与冒烟测试无需 Docker、无需额外构建步骤——shell 以预打包特性组的形式存在于cloudflare/computer/backends/worker-shell中这是 worker-shell 后端相对容器后端最大的落地优势。启动本地开发npm run dev --workspace example/computer-worker-shell冒烟测试与 container 示例共用同一套curl配方README 原文curl http://127.0.0.1:8787/ echo hello | curl -X PUT --data-binary - \ http://127.0.0.1:8787/c/demo/file/workspace/hello.txt curl http://127.0.0.1:8787/c/demo/file/workspace/hello.txt curl -X POST http://127.0.0.1:8787/c/demo/exec \ -H content-type: application/json \ -d {command:cat hello.txt wc -l hello.txt,encoding:utf8}依次验证根路由返回路由说明、PUT 写文件204、GET 读回文件内容、POST exec 执行cat hello.txt wc -l hello.txt返回 JSON 结果exitCode/stdout/stderr。部署命令同样简洁npm run deploy --workspace example/computer-worker-shell即wrangler deploy。package.json中还有typechecktsc --noEmit用于类型校验。目录布局examples/worker-shell/极其精简README 的 Layout 小节examples/worker-shell/ wrangler.jsonc Worker DO worker_loaders binding src/index.ts Worker handler DO (ContainerExample)就这两个文件外加seed/data/hello.txtR2 种子数据与工程配置。动态 Worker 的源码不在这里——它以预构建模块字符串的形式从cloudflare/computer/backends/worker-shell分发含SHELL_CORE_MODULES、SHELL_RUNTIME_MODULES与各特性组。SHELL_RUNTIME_MODULES是 just-bash 静态原生导入在 workerd 下加载所需的模块 shim由后端在构造 Loader 回调时自动展开进modules表见 index.ts 的导出清单。测试侧可参考 worker-shell.test.ts它用SQLiteTestStorage 一个 fake fetcher模拟动态 Worker 产出的 NDJSON 字节帧验证后端能正确返回sync: none的BackendHandle并把帧流解码回ReadableStreamExecEvent——这正是「后端不做的事」的边界声明不把 WorkspaceFilesystemStub 作为 exec 参数传递因为 I/O 上下文必须保持在 DO 的请求里。已知限制README 明确列出的三条限制均在源码中有对应印证Exec 是 run-and-collect 模式handler 会await handle.result()并一次性返回 JSONjust-bash 本身不流式输出 chunk每次运行最多产生一个 stdout 事件和一个 stderr 事件entrypoint.ts 中事件数组的组装逻辑可见getExec重挂载被有意剔除每次 exec 作用域独立一个信封中观察到的 id 无法被后续请求触达getExec恒抛ENOENTwrangler dev下的 PATH-walk 诊断噪音just-bash 会对每个命令名探测每个$PATH目录每次未命中都会在开发日志中打印Uncaught WorkspaceFsError: no such path: ...——尽管 just-bash 已在本地捕获该 rejection。纯属表面噪音exec 仍返回正确结果。总结worker-shell 示例展示了cloudflare/computer的一种「轻 Shell」形态宿主 DO 拥有权威的 SQLite 存储与 WorkspaceShell 则委托给 Worker Loader 铸造的动态 Worker 内的 just-bash 解释器文件系统通过WorkspaceServiceProxy回环回到宿主 DO 的请求上下文命令能力通过「核心组 按需特性组」实现零冗余打包。理解这条链路就同时掌握了 Cloudflare Worker Loader 动态 Worker、WorkspaceServiceProxy绑定回环、以及 just-bash 在 workerd 下的运行约束——这些知识可以平移复用到任何需要「无容器 shell」的 Cloudflare Workers 场景。【免费下载链接】computerGive your agent a computer 项目地址: https://gitcode.com/GitHub_Trending/computer1/computer创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/9/16 14:56:25

QT1011硬件状态机与R7KA8D2KFLCAC复位实操指南

1. 这不是“万能遥控器”,而是工业级人机交互模块的现场复位实操指南你手边如果真有一块标着QT1011和R7KA8D2KFLCAC的黑色小板子,它大概率不是什么消费级智能配件,而是一套嵌入在工业控制柜、医疗设备外壳内侧、或是楼宇自控终端背后的本地操…

2026/9/16 14:56:25

STM32嵌入式开发:VS Code替代Keil的底层原理与实战配置

1. 为什么STM32开发者正在集体“逃离”Keil,转向VS Code?我第一次在客户现场看到工程师用VS Code调试STM32F407时,他正把一个UART中断服务函数拖进Git Diff面板,旁边贴着一张手写的寄存器映射草稿纸。那一刻我就意识到&#xff1a…

2026/9/16 14:56:24

Proteus仿真STM32 ADC精度问题与软件映射解决方案

简介:本资源是一套基于Proteus与Keil MDK联合仿真的STM32F103R6数字电压表完整工程,面向嵌入式初学者及课程设计实践者,解决两路模拟电压采集、AD转换与数码管动态显示的核心教学难点。压缩包共599个文件,涵盖337个C源码&#xff…

2026/9/16 15:41:44

Django实战:构建多平台电商数据爬虫与清洗入库系统

简介:一份聚焦电商数据采集的Python爬虫分析系统源码,面向高校学生、课程设计与毕业设计人群,适合用来学习爬虫开发与Django项目搭建。系统实现了对京东、淘宝、苏宁、亚马逊中国四个主流电商平台的商品信息抓取,字段涵盖商品名称…

2026/9/16 15:41:44

HTTP与HTTPS详解:请求头、状态码与抓包排障实战

先别急着复制代码,也别急着看框架源码,很多后端新人甚至干了两三年的开发,遇到接口报错还是只会看“500 - Internal Server Error”这七个单词,然后一脸茫然。真正的问题往往藏在状态码、响应头甚至一次重定向的细节里。这篇东西我…

2026/9/16 15:41:44

2026数据智能体选型决策地图:四类厂商本质差异与落地标尺

1. 这不是又一份“厂商对比表”,而是一张数据智能体落地的决策地图2026年,数据智能体(Data Agent)已不再是PPT里的概念名词,它正批量嵌入企业BI看板、供应链预警系统、客户成功工单流、甚至财务月结流程中。我去年帮三…

2026/9/16 15:41:44

微信小程序社区养老系统为何首选SSM架构

简介:本资源是一套完整的社区养老服务微信小程序毕业设计/课程设计项目源码,面向Java初学者与Web开发学习者,聚焦SSM框架实战与小程序前后端协同开发场景。项目以解决社区养老信息互通、服务预约与邻里互助等现实需求为目标,涵盖信…

2026/9/16 15:41:44

M3U8与HLS视频流深入解析:从切片、AES加密到多码流自适应

1. M3U8索引文件到底在管什么——从一次黑屏排查说起前段时间接了个视频站点的改造需求,客户反馈说网页里嵌的视频总是播着播着就黑屏,尤其有些用户网络一波动,整个页面直接卡死。我第一反应是文件太大、浏览器撑不住,去服务器上一…

2026/9/16 12:52:37

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/16 0:04:09

PHP源码部署实战:从环境配置到运行情侣游戏全攻略

简介:这是一套面向情侣互动场景的PHP完整源码,集成情侣飞行棋、真心话大冒险、情趣骰子等玩法,并内置完整分销制度,可自定义多种返佣比例,源码完全开源无加密,支持微信无感自动授权登录与第三方授权&#x…

2026/9/15 14:22:53

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/15 21:31:11

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/15 11:42:23

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码