学习笔记 · Obsidian

Smith API:Sandbox 命令流、文件检索与隧道传输

LangChainLangSmith

Sandbox 数据面提供同步命令、SSE 可恢复命令流、WebSocket 交互、文件上传下载、服务器端 glob/grep 和 TCP tunnel。它们都能触达运行时内部数据,应使用 sandboxes:exec 等最小权限、隔离的 sandbox 身份和严格输入边界,不能因为运行环境“是临时的”就省略安全控制。

同步命令执行

POST /api/v2/sandboxes/{sandbox_id}/execute 接收 command、cwd、env、shell、timeout_seconds,返回 stdout、stderr 与 exit_code。页面描述说 command 可为 shell 字符串或 argv 数组,但当前 OpenAPI schema 将它定义为 array<string>。这是文档文字与机器契约的差异:生成客户端应以 argv 数组为稳定基线;只有在目标环境实测并固定兼容性后才发送 string。

同步接口适合输出有明确上限的短命令。HTTP 200 只表示执行请求成功,业务成功仍要检查 exit_code;stderr 非空也不一定表示失败。timeout、进程退出和网络断开是三类状态,调用方要分别记录,避免把超时解释为命令必定没有产生副作用。

shell 模式会引入 shell expansion 与注入面。来自用户或模型的参数优先作为 argv 元素传递,不拼接成一条命令;cwd 限制在工作目录;env 采用 allowlist,禁止继承控制面 secret。标准输出也可能包含 secret 或个人数据,进入 trace 前应截断与脱敏。

可恢复 SSE 命令流

POST .../execute/stream/start 要求 v2 runtime,响应是 text/event-stream。stdout/stderr payload 以 base64 传输,调用方需要按 event type 分流并在 decode 后按字节处理,不能假设每个 chunk 都是完整 UTF-8 行。

请求字段:

  • command、cwd、env、shell;
  • command_id:幂等键;已知 ID 会 attach 到正在运行的 command,而不是再启动一份;
  • stdin:整段 base64 一次写入,然后 stdin 关闭并让进程看到 EOF,不支持持续 stdin streaming;
  • timeout_seconds=0 表示无总超时;
  • idle_timeout_seconds=0 使用默认值,-1 永不因空闲终止;
  • ttl_seconds=0 使用默认值,-1 永久保留 command 状态。

调用方应始终生成并持久化一个业务请求到 command_id 的映射。SSE 连接断开时,用同一 command_id/offset 恢复;换新 ID 会重复执行可能有副作用的命令。-1 会产生无界运行或状态保留风险,只应对受控运维任务开放,并有平台外 watchdog。

当 sandbox 输出 buffer 需要确认时,start/resume 流以 ack_required 结束并给出 stdout/stderr offsets。POST .../stream/resume 的 offsets 同时表示:

  1. 从该字节位置继续发送;
  2. 确认此前字节已被调用方持久处理;
  3. 允许 sandbox 释放对应 buffer 并唤醒因 buffer 满而暂停的命令。

offset=0 会从头 replay,适合连接在任何输出前断开。消费方必须按 (command_id, stream, byte_offset) 去重,先持久化输出再推进 ack;否则 crash 后可能丢日志。Resume 只 attach,不会把 sandbox 已丢失的 command 重跑,找不到返回 404;状态冲突可返回 409。

WebSocket 执行与 TCP Tunnel

GET .../execute/ws 以 101 升级到 WebSocket,适合需要双向实时交互的命令。OpenAPI 页面只承诺升级,没有形式化 frame schema;客户端必须固定与目标运行时版本匹配的协议实现,设置最大 frame/消息、心跳、总时限和关闭码处理。

GET .../tunnel 同样用 WebSocket 建立到 sandbox TCP port 的隧道,目标端口放在 X-Sandbox-Port header。它是认证隧道,不是把端口公开到互联网。要限制可访问 port、连接时长、并发与字节数;禁止借 tunnel 扫描内部控制面、metadata service 或挂载网络。

长任务选择:

需求接口特征
短命令、有限输出sync execute一次 JSON 响应,无恢复协议
长命令、单次 stdin、可恢复输出SSE start/resumecommand_id 幂等,offset ack/backpressure
持续双向交互execute WebSocket协议需客户端与 runtime 配套
任意内部 TCP 协议tunnel WebSocket端口在 header,需更严格网络策略

文件上传与下载

Upload 使用 multipart/form-data,文件字段名为 file,目标 path 是必填 query 参数;返回实际 path 与写入字节数。Download 把文件 path 放在 query 并直接返回文件内容,不是 JSON。

这些页面没有给出最大文件、覆盖/原子替换、symlink 或 path traversal 的完整保证。客户端侧仍要:

  • 规范化 path 并限制在工作根目录,拒绝 ..、NUL 与意外绝对路径;
  • 对上传设置本地大小、类型与压缩包展开限制;
  • 大文件用流式 I/O、校验长度/哈希,不将内容整体放入 trace;
  • 如业务要求原子替换,先上传临时文件并在 sandbox 内做受控 rename;
  • 下载响应按二进制处理,不根据文件名猜测安全 MIME 并在浏览器内联执行。

网络超时后,upload 是否已完整落盘未知,应查询文件/hash 后再决定重传。Download、glob、grep 是读取,可有界重试;upload 与 command 可能有副作用,必须先核对状态或使用业务幂等设计。

Glob 与 Grep

POST .../glob 在给定 path 下按 pattern 查找,支持 **,结果按 path 字典序返回。每项包含 path、是否目录、size 与 modified_at;达到 limit 时 truncated=true,没有 cursor,因此完整扫描需要缩小 root/pattern 分片,而不是反复发送同一请求。

POST .../grep 搜索的是 literal text,不是正则表达式;可用 glob 限定文件,返回 path、line、text,并同样用 truncated 指示截断。不要把用户输入当 regex 转义后再推断服务端行为。对于二进制、大文件、生成目录和 secret 目录,先用 glob 限定集合,避免无界扫描和敏感内容进入结果。

执行可靠性与审计

command accepted
  → process spawned
  → output streamed / buffered
  → exit event observed
  → output persisted
  → business result committed

只有最后一步完成才算业务成功。断线可能发生在任意阶段;同步执行没有 command_id,因此副作用命令应在 command 自身实现幂等键。SSE 模式把 command_id 与 offset checkpoint 一起持久化。

审计记录 sandbox ID、command_id、调用者、经过规范化的可执行程序名、cwd、开始/结束时间、exit/timeout/close 状态和输出字节数。不要记录 raw stdin、env secrets、service tokens 或未脱敏 stdout/stderr。400/403/404 通常是请求、权限或资源状态问题;500 只能对明确幂等读取/attach 做退避重试,不能据此自动重跑未知状态的命令。