12 KiB
description, kind
| description | kind |
|---|---|
| 共享远程沙箱内的 shell 命令与终端:agent 可以在那里运行什么、输出如何处理,以及可以期待什么——面向 E2B 家族的部署方与维护者。 | package-reference |
@deepseek-ai/dsh-subprocess-e2b
English | 中文
概述
dsh-subprocess-e2b 让 agent(智能体)的 shell 命令与终端在远程沙箱内运行:agent 可以执行 Bash、打开交互式终端并读取其输出,体验与本地执行完全一致,而宿主机器上什么都不会运行。现有的命令、终端与语言服务器功能无需任何改动即可继续工作——不需要 E2B 专用工具。密钥与宿主环境变量绝不会泄漏进沙箱:只有 agent 显式请求的环境条目才会传入。请与 dsh-e2b、dsh-fs-e2b 一起使用,让命令、终端与文件共享同一个远程世界。主要代价是远程延迟——每条命令都要经过一段短暂异步初始化,而不是立即启动。
目录
使用本包
当 agent 的 shell 命令与终端应在远程沙箱内而非你的机器上运行时,使用本包。它是 E2B 家族的命令半边:命令、终端与文件共享同一个远程世界。
何时选择
当组合已经使用 E2B 沙箱且希望命令与终端在其中运行时,选择本包。宿主执行请选择本地子进程包。需要立即获得进程 ID 的工具——例如 ACP(Agent Client Protocol)子进程后端——无法使用本包。
配置
唯一设置是包检查运行中命令状态的频率;默认值适合大多数部署,调大它可以减少远程请求,代价是退出检测略慢。
| 字段 | 默认值 | 含义 |
|---|---|---|
pollMs |
20 |
包检查运行中命令状态的频率(毫秒) |
生成的配置目录是每个受支持字段及其 JSDoc 的穷尽式真源。
运行命令
agent 可以在沙箱中按指定的工作目录与环境运行命令,选择输出的交付方式(实时流式、在大小上限内捕获,或路由到应用自身的输出),并在命令卡住时停止它——停止会先礼貌地请求命令退出,短暂宽限期后再强制终止,因此卡住的命令不会残留。非常大的输出可以保存到沙箱中的文件里,供 agent 稍后读取。命令的退出码会正常报告;如果命令运行期间沙箱消失,该命令会被视为已结束而不是报错。
使用终端
agent 可以在沙箱中打开交互式终端、发送输入、读取输出,并向其中运行的程序发送信号——提示符、交互式工具与全屏程序的行为与本地完全一致。scrollback 与就绪检测等终端功能由终端工具提供,无需改动即可工作。
保持环境干净
命令在干净、沙箱原生的环境中运行:宿主变量与形似凭据的值不会被隐式传入,只有 agent 显式请求的条目才会被设置。这使密钥不会进入沙箱。
如果沙箱消失
沙箱是短暂的:如果命令或终端运行期间沙箱被删除——无论是到期、关闭还是被别处移除——受影响的命令会被视为干净地结束。请不要依赖能在沙箱中存续的工作。
默认沙箱镜像自带命令工作所需的运行时与工具:node、bash、setsid、ps、awk、tr、env、base64、chmod、tee、head、rm、kill、id 与 getent。
理解实现
实现细节——点击展开
本节解释提供方背后的设计决策,并指出实现它们的代码位置;可观察行为已在使用本包中完整说明。
设计理念
- 延后的远程身份。 同步 seam 从不阻塞在网络请求上:句柄异步发布真实进程组 ID,包装层的私有文件是 pid、退出码与 spill 有效性的权威来源。
- 单一终止阶梯。 终止、回滚与资源释放共享同一条进程组信号路径——先
SIGTERM,再SIGKILL加 SDK kill 回退——并把已证明的完全停稳视为最终状态。 - 环境必须显式。 宿主内容与形似凭据的内容都不会隐式进入沙箱;每个环境值都会被清理,每个
spec.env条目都是显式选择。
源码地图
| 文件 | 职责 |
|---|---|
src/index.ts |
插件入口:E2BSubprocessRuntime、Config、spawn 与 spawnTerminal、资源释放 |
src/process.ts |
E2BSubprocessHandle:远程包装层、发布、终止、输出投影 |
src/terminal.ts |
E2BTerminalHandle:PTY 分配、会话拆除 |
src/environment.ts |
远程环境探测、清理、序列化 |
src/output.ts |
base64 解码器与有界输出读取器 |
src/remote.ts |
共享控制 shell 辅助:选项构造、轮询 tick、进程组信号 |
src/invariant.ts |
不变式伴生插件(无运行时不变式;存活远程句柄是私有的拆除所有权) |
远程包装层
引导脚本会从沙箱 PATH 解析自身所需的工具,拒绝任何缺失或不可执行的路径,通过 env -i 与 setsid --wait 执行 exec,把进程组 ID 与退出码发布到 ctx.e2b.runtimeRoot/processes 下的私有文件,并把 stdout 与 stderr 重定向到带保留完成帧的 base64 编码器;tee 与 head -c 约束可选 spill 文件的大小。
进程身份与发布
同步 seam 会立即返回句柄,同时命令异步启动;pid 在包装层发布进程组 ID 且适配器验证通过之前保持 -1,stdin 与常规观察都等待该发布。启动信号会在分配前中止环境与私有状态准备;分配开始后,取消会等待可清理的临时 SDK 句柄。
环境边界
一次受信任的控制 shell 探测会从 passwd 条目解析沙箱用户的登录主目录,以 base64 ASCII 传输沙箱环境,再进行一次严格 UTF-8 解码;随后包装层移除环境中的 DSH_* 与形似凭据的名称(*KEY*、*SECRET*、*TOKEN*),并把每个有效的 spec.env 条目恢复为调用方显式选择。空名称、= 与违反 NUL 分帧规则的条目会在启动前被拒绝;在用户 profile 脚本运行前,此后的命令与 PTY 登录 shell 会获得位于根目录下、全新随机生成的 HOME,并为每个被清理的环境变量名设置空值覆盖。私有环境文件在使用后会被删除。
输出处理
远程包装层先把原始字节分流到可选的有界 spill 文件,再把每个实时分片编码为换行分隔的 base64 ASCII 帧;宿主会跨任意 SDK 回调边界增量恢复字节。pipe 模式把字节写入宿主 Node 流,inherit 模式写入 harness 进程流,collect 模式保留有界的宿主尾部并支持偏移读取。对于 collect 或 inherit 输出,超过 graceMs 后适配器会断开未完成的 SDK 流并扣留其不完整的 spill;原始 pipe 自然完成时则会等待无损传输并保留背压。批量与流式 stdin 都使用 SDK 句柄。
终止阶梯
终止与回滚共享同一条容错信号路径(signalRemoteGroups),在宽限期满时从 SIGTERM 升级到 SIGKILL,以 SDK kill 作为回退,并在报告成功前用有界进程表探测证明完全停稳;仅含僵尸进程的进程组视为空,SandboxNotFoundError 视为完全停稳。
进一步探索
当包级约定不够用时阅读以下页面。它们从家族组合逐步进入子进程 seam 表面,以及渲染它的消费方。
- E2B 提供方家族地图——沙箱所有者与三包组合。
- 子进程子系统——子进程 seam 约定与生成的 Cordis 表面。
- 子进程 seam 包——本提供方实现的抽象约定。
- Bash 执行器——向模型渲染所启动命令的消费方。
- PTY 终端后端——渲染终端会话的消费方。
- 生成配置目录——每个受支持配置字段及其源声明。
模型体验
通过消费方 seam 间接影响模型,例如 bash 执行器家族;它们渲染远程输出、退出事实、后台增量与 spill 路径。
KV Cache 影响
不会直接失效:请求前缀变更由消费方 seam 负责;本后端的传输永远不会进入请求。
已知限制与延期工作
这些限制说明本提供方何时不合适,或何时需要特别的运维注意。它们是当前包约束,不是任务积压。
- SDK 仍会在宿主内存中保留完整命令输出:即使本适配器公开的是有界原始字节尾部,E2B
CommandHandle.stdout与.stderr仍会累积 base64 传输内容,因此无法达到子进程 seam 通常提供的宿主内存边界,而且传输保留量大于源数据流。 - 不支持需要同步 PID 的消费方:远程启动期间
pid保持-1;包括 ACP 子进程后端在内,要求立即获得正 PID 的消费方无法原样使用本提供方。 - 私有状态随沙箱生命周期存在:进程目录与有效的 spill 文件会留在
.dsh-e2b下,直到所有者删除沙箱;本 POC 不提供沙箱内清理。 - 控制状态与沙箱用户同 UID:E2B 以同一默认用户运行每条命令,因此
0700/0600权限无法把.dsh-e2b控制文件与并发运行的沙箱进程隔离开;真正的隔离需要 E2B 提供按命令用户或带外控制通道。 - 数值进程身份没有复用围栏:E2B 公开基于数值 PID/PGID 的输入、信号发送与清理操作,却没有与身份原子绑定的替代方案;在 E2B 新增身份原语,或实际故障证明需要更窄的协议之前,替代方案继续延后。
- 初始环境探测会继承沙箱默认值:E2B 会把命令覆盖与默认环境条目合并,因此探测无法在枚举未知且形似凭据的名称之前将它们置空;因此,该 POC 不支持把 secret 放入沙箱默认环境变量。
- E2B 不公开信号事实:适配器请求的
SIGTERM或SIGKILL只有在包装层发布的直接退出码没有胜出时才报告为信号;其他未请求的 SDK 退出始终保留为退出码,包括等于128 + signal的值。 - 无法精确检查终端 stdin 等待状态:E2B 会公开前台进程组,但不提供证明其正在等待 fd 0 所需的 syscall 证据,因此通用 PTY 后端会回退到受控提示符标记与有界静默机制。
- 依赖 Linux 工具与 E2B 传输语义:没有 Windows、逃逸会话恢复或网络分区的保真层。
开发备注
维护者的工作上下文——点击展开
本开发备注是维护者的工作上下文:开放问题与尚未决定的探索方向。它明确不具权威性——已交付的行为、限制与既定理由以上文和包代码为准。
开放:数值进程身份
E2B 公开基于数值 PID/PGID 的输入、信号发送与清理操作,却没有与身份原子绑定的替代方案。适配器会尽量减少宿主往返,并在 E2B 新增身份原语或实际故障证明需要更窄的协议之前,继续延后替代方案(TODO(e2b-pgid-identity))。
开放:替换环境与状态观察
由于 E2B 会合并命令覆盖,初始环境探测会继承沙箱默认值;又因为 E2B 无法独立于后代持有的输出观察直接命令的退出,collect/inherit 命令状态需要控制面轮询。两者都只能靠 E2B 的新原语来弥合(TODO(e2b-replace-environment)、TODO(e2b-status-watch))。