@coder/ai-sdk-sandbox (英语)
从 Vercel AI SDK v7 运行 CLI 编码代理 调用代理 真实的 编码器 工作空间而不是麻风云沙盒.
- 执行
HarnessV1SandboxProvider合同来源@ai-sdk/harness数字 : 传为正sandbox改为HarnessAgent完全一样@ai-sdk/sandbox-vercel。 。 。 . - 克劳德代码被验证为端到端. Codex 预计将通过同一程序工作 桥架机制,但尚未在此还原中验证.
状态 : 实验 跟踪稳定的AI SDK v7 套接字(参见 页:1
@ai-sdk/harness对应区域package.json) (中文(简体) ).
安装
pnpm add @coder/ai-sdk-sandbox @ai-sdk/harness @ai-sdk/harness-claude-code @ai-sdk/provider-utils
选择一个主机传输 :
| 运输 | 东道国要求 |
|---|---|
CoderCliTransport (默认) | coder 国 际,认证 coder login,以及 OpenSSH 客户端(ssh在路径 |
CoderNativeTransport | 部署 URL 和符号 。 没有 coder 或者说 ssh 二进制. 见 本地交通 |
快开始
将一个已存在的、正在运行的工作空间包入其中并运行 Claude 代码 :
import { HarnessAgent } from "@ai-sdk/harness/agent";
import { createClaudeCode } from "@ai-sdk/harness-claude-code";
import { createCoderWorkspace } from "@coder/ai-sdk-sandbox";
const agent = new HarnessAgent({
harness: createClaudeCode({ thinking: { type: "adaptive" } }),
sandbox: createCoderWorkspace({ workspace: "my-dev-workspace" }),
instructions: "You are a careful coding assistant.",
});
const session = await agent.createSession();
try {
const result = await agent.generate({
session,
prompt: "Create a short TODO.md in the repo root.",
});
console.log(result.text);
} finally {
await session.destroy();
}
可运行版本 : examples/claude-code.ts。 。 。 .
本地交通
使用 CoderNativeTransport 当主机不应该依赖于代码 CLI 或
打开SSH :
import { CoderNativeTransport, createCoderWorkspace } from "@coder/ai-sdk-sandbox";
const transport = new CoderNativeTransport({
url: process.env.CODER_URL!,
token: process.env.CODER_SESSION_TOKEN!,
});
const sandbox = createCoderWorkspace({
workspace: "my-dev-workspace",
transport,
});
// On application shutdown, close cached relay WebSockets:
await transport.close();
- 构造器掉回
CODER_URL财务报告和审定财务报表CODER_SESSION_TOKEN这样new CoderNativeTransport()两者都准备好了就够了. - 符号只发送到
Coder-Session-Token头曰. 没错 从未复制到工作区 .
根据需要创建工作空间
添加一个 create 块从模板创建工作空间(带有参数)
和(或)预设),并当会话结束时将其拆除:
const agent = new HarnessAgent({
harness: createClaudeCode({ thinking: { type: "adaptive" } }),
sandbox: createCoderWorkspace({
create: {
template: "docker", // required: the template to create from
preset: "Large", // optional: a template version preset
parameters: { cpus: 8, region: "us-west-2" },
useParameterDefaults: true, // accept template defaults for the rest
stopAfter: "8h", // auto-stop TTL
},
}),
});
默认这是 新鲜的每会数字 :
- 工作空间名称来源于绳子
sessionId(例如:.agent-1a2b3c4d5e6f因此,每个会话都有自己的工作空间. session.destroy()删除它.resumeSession重划同名并 复为后相.- 吊带在工作空间代理完成后运行
运行其启动脚本( e)
lifecycle_state: ready) (中文(简体) ). 一个成功的 构建 实值 单靠自己是不够的.
改为: 获取或创建一个命名的工作空间组合 workspace 与 create如果
它的存在,提供者附着于它(而永远不会删除它). 如果没有的话,
供养者创造它(默认为拥有).
createCoderWorkspace({
workspace: "my-agent-ws",
create: { template: "docker", ifExists: "attach" }, // 'attach' (default) | 'error'
});
参数对预设 :
- 预设参数值优先于重叠值
parameters同名条目(Coder的行为). 通过预设设置给定值 或者说parameters而不是两个. - 每个未设置的非偶数参数必须通过
parameters, (中文(简体) ),parameterFile,或一个preset,除非useParameterDefaults接受其定义 模板默认. - 标记所需的参数没有可用的默认值,必须始终是 提供; 否则创建失败, 因为它不能提示 非交互性.
- 如果你设置一个
preset,提供方对 模板的预设,但无法快速使用可用名称。 设定validate: false跳过.
创建设置
createCoderWorkspace({
create: {
template: "docker", // required
templateVersion: undefined, // default: the template's active version
preset: undefined, // 'none' forces no preset
parameters: {}, // { name: value }; numbers/bools stringified
parameterFile: undefined, // path to a YAML rich-parameter file
useParameterDefaults: false, // accept template defaults where unset
ephemeralParameters: {}, // one-time build parameters
stopAfter: undefined, // e.g. '8h' (auto-stop TTL)
automaticUpdates: undefined, // 'always' | 'never'
org: undefined, // --org, for ambiguous template names
owner: undefined, // owner for a derived name (owner/name)
ifExists: "attach", // 'attach' | 'error'
namePrefix: "agent", // prefix for the derived per-session name
validate: true, // preflight-check the preset name
},
readyTimeoutMs: 300_000, // wait budget for the agent to become ready
});
提供无会话的工作空间
ensureCoderWorkspace(settings) 提供工作空间 其他工具 改为
约束。 它运行同样的获得或创造 ~ 如果开始 -停止 ~等待 -准备
管道作为创建模式,而不创建带状沙盒会话.
| 设置 | 页:1 |
|---|---|
workspace | 要求明确 [owner/]workspace 名称( 没有) sessionId 以获得一个来源. |
create | 可选;形状与以上相同(namePrefix编号 :owner 页:1 没有它,工作空间必须存在. |
readyTimeoutMs, (中文(简体) ), transport, (中文(简体) ), abortSignal | 可选的. |
停止的工作空间总是开始。 调用返回一个
EnsuredCoderWorkspace数字 :
- 工作空间的最后(准备)状态快照;
created:这个电话是否创造了它;id:工作空间UUID,当运输报告一个。 其他编码器 包绑在这个手柄上.
@coder/ai-sdk-agent 这是故意的 没有 a 此软件包的依赖性.
二者由平淡的串接来作曲:
import { ensureCoderWorkspace } from "@coder/ai-sdk-sandbox";
import { CoderAgent } from "@coder/ai-sdk-agent";
const ws = await ensureCoderWorkspace({
workspace: "agent-ws",
create: { template: "docker" },
});
const agent = new CoderAgent({
baseUrl: process.env.CODER_URL!,
token: process.env.CODER_SESSION_TOKEN!,
organizationId: "<org-uuid>",
workspaceId: ws.id!, // binds the chat's workspace-scoped tools
});
非核的主张是安全的 coder 发射的 CLIs id 内
coder list -o json旧的CLI省略它,所以小心
(单位:千美元)if (ws.id === undefined) throw …)当无法将CLI版本接上.
终端用户界面
用于交互终端聊天而不是一发 generate() 电话, 包接
代理人与 @ai-sdk/tui数字 :
pnpm add @ai-sdk/tui
TUI 驱动一个无会话的代理, 因此为 TUI 注入会话 寿命 :
import { HarnessAgent, type HarnessAgentSession } from "@ai-sdk/harness/agent";
import { createClaudeCode } from "@ai-sdk/harness-claude-code";
import { runAgentTUI, type AgentTUIAgent } from "@ai-sdk/tui";
import { createCoderWorkspace } from "@coder/ai-sdk-sandbox";
const agent = new HarnessAgent({
harness: createClaudeCode({ thinking: { type: "adaptive" } }),
sandbox: createCoderWorkspace({ workspace: "my-dev-ws" }),
// or, to create a fresh workspace per session from a template:
// sandbox: createCoderWorkspace({ create: { template: 'claude-code-test' } }),
});
const toTUIAgent = (agent: HarnessAgent, session: HarnessAgentSession): AgentTUIAgent => ({
version: "agent-v1",
id: agent.id,
tools: agent.tools,
generate: (request) => agent.generate({ ...request, session }),
stream: (request) => agent.stream({ ...request, session }),
});
const session = await agent.createSession();
try {
await runAgentTUI({ title: "Claude Code @ Coder", agent: toTUIAgent(agent, session) });
} finally {
await session.destroy();
}
可运行版本 : CODER_WORKSPACE=my-dev-ws npx tsx examples/claude-code-tui.ts
(单位:千美元)来源; 用 Esc 或 Ctrl+C 退出.
工作空间要求
桥通向工作空间内部,因此工作空间图像需要:
| 所需经费 | 为什么 |
|---|---|
| 节点.js 22 登录 shell 路径 | 桥是 node bridge.mjs,本地交通的继电器是 node -e和被钉住 @anthropic-ai/claude-code 桥梁依赖需要节点22+. |
小时 在登录 shell 路径(例如)上. npm install -g pnpm(中文(简体) ) | 适配器的靴子 pnpm install --frozen-lockfile 安装桥的依赖. |
| 出入境 npm 登记 | 在第一次使用时,"靴子"下载了桥的依赖性,包括克洛德代码CLI的~250MB平台二进制(被作为npm包发去). 全部跳过注册的预扣:见 模板编写。 。 。 . |
| 出入境 型号 API | api.anthropic.com 克劳德代码, api.openai.com 用于 Codex. |
这个 模型 API 密钥 (单位:千美元)ANTHROPIC_API_KEY 编号 : OPENAI_API_KEY(中文(简体) ) | 可用桥:通过适配器配置 auth 选项或把它放入工作空间环境. |
bash 财务报告和审定财务报表 base64 | 远程命令运行通过 bash -lc;文件 I/O是基础64-编码. |
stty | 本地运输的接力靴子. |
bash, (中文(简体) ), base64,以及 stty 是任何 Linux dev 图像上的标准.
模板作者:零安装会话
将适配器的靴子绑入工作空间映像, 从而启动会话
零运行时间安装空闲。 这个
模板编写指南 覆盖多克文件,
main.tf大小 核查 经验证的一对生活在
examples/template/。 。 。 .
设置
至少其中一个 workspace 或者说 create 需要时间. 您可以同时设置
根据需要创建工作空间。 。 。 .
import {
createCoderWorkspace,
CoderCliTransport,
CoderNativeTransport,
} from "@coder/ai-sdk-sandbox";
createCoderWorkspace({
// One of these is required (TypeScript enforces it):
workspace: "my-ws", // fixed name, or (sessionId) => `agent-${sessionId}`
create: undefined, // create from a template; see "Creating workspaces"
readyTimeoutMs: 300_000, // wait budget for the agent to become ready
ports: [4000], // exposed ports; ports[0] is the bridge port
defaultWorkingDirectory: "/home/coder", // default: resolved from $HOME, else /home/coder
ownsLifecycle: false, // see "Lifecycle modes" below
ensureStarted: false, // run `coder start` before attaching
// Transport. Defaults to an ambient-login CoderCliTransport. Construct one to
// configure it, or supply a non-CLI/test transport:
transport: new CoderCliTransport({
// coderBinary: 'coder', sshBinary: 'ssh',
// url: process.env.CODER_URL, token: process.env.CODER_SESSION_TOKEN,
// env: {}, loginShell: true, waitMode: 'no',
}),
// Or connect directly to Coderd with no host CLI/OpenSSH dependency:
// transport: new CoderNativeTransport({
// url: process.env.CODER_URL,
// token: process.env.CODER_SESSION_TOKEN,
// }),
});
生命周期模式
| 模式 | 选中的 | 行为 |
|---|---|---|
| 环绕已有的工作空间 | 默认无 create (单位:千美元)ownsLifecycle: false(中文(简体) ) | stop() 财务报告和审定财务报表 destroy() 只释放主机侧资源(port-forwards);工作空间持续运行. 自然适合长生不老的工作空间. |
| 拥有工作空间 | ownsLifecycle: true | stop() 运行 coder stop· ; destroy() 运行 coder delete。 。 。 . |
| 创建模式 | create 设定 | ownsLifecycle 默认为 true: 提供者创建的工作空间被删除 destroy(),以及 onFirstCreate 运行作为它的靴子钩. |
创建模式安全规则 :
- 只有提供者的工作空间 附 录 改为(明确名称, 原为 永远 删除; 只有它实际创建的 .
- 每会场的衍生名称总是被当作拥有.
- 设定
ownsLifecycle: false用于“创建缺失但永远不删除”.
港口
- 适配器将其桥从
createClaudeCode({ port })或者,默认情况下,sandbox.ports[0].通过ports(默认)[4000]) (中文(简体) ). getPortUrl询问本地 TCP 前置的已配置的运输工具 返回回转ws://URL (中文(简体) ). CLI 运输使用 OpenSSH-L; 数据 (原始内容存档于2018-10-21) (英语). Circultive transport multers TCP sover its Coderd WebSocket.- 前面是回转的平话,所以
https编号 :wss请求下决心 他们的http编号 :ws等同回转.
如何运作
对于桥后适配器(Claude Code, Codex), a HarnessAgent 不会跑
直接是CLI探员 它安装了小节点"桥"程序 内部
沙盒{\fn黑体\fs22\bord1\shad0\3ahbe\4ah00\fscx67\fscy66\2chffffff\3ch808080}产下它 {\fn黑体\fs22\bord1\shad0\3ahbe\4ah00\fscx67\fscy66\2chffffff\3ch808080}和它交谈 认证的 WebSocket编辑
bridge 运行供应商 SDK 在工作空间和流事件返回主机.
这个提供商将合同映射到编码器原始器上:
| 合同 | CLI 运输 | 本地交通 |
|---|---|---|
run 编号 : spawn | 打开SSH 结束 coder ssh --stdio | 通过 Coderd 代理 PTY WebSocket 的版本进程中继 |
readFile 编号 : writeFile 编号 : read*编号 :write* | 基数64 超过 SSH | 本地进程中继器的基数64 |
getPortUrl({ port, protocol }) | 打开SSH -L | 中继器上的多轴 TCP 通道 |
createSession 编号 : resumeSession 编号 : id | CLI 工作空间查询 | 代码 v2 REST API |
stop 编号 : destroy | coder stop 编号 : coder delete 当生命周期拥有 | 编码工作空间建设过渡 |
桥接 WebSocket 不需要通配符访问 URL: 主机运行
HarnessAgent 已经是一个代码客户端.
为什么 OpenSSH 代替 coder ssh <ws> -- cmd
coder ssh 为命令分配一个 PTY。 PTY将新行重写为 CRLF,
将 stdout 和 stderr 合并到一个流中, 不可靠地传播退出
代码:所有致命 用于程序 和桥的分解.
coder ssh自己的帮助推荐 coder config-ssh "对于需要用户
sSH的全部功能". 该提供者负责方案等同工作:
在 a 上运行真实的 OpenSSH coder ssh --stdio 代理指挥. 这样就干净了,
分离流并正确退出代码(对照活的工作空间验证).
本土接力如何保持字节清洁
Coderd的浏览器-地平线端点是一个PTY,它本身就合并了 stdout/stderr 且没有进程退出码通道. 当地交通 它只是作为载体:
- 它会拖住一个没有依赖性的小节点中继器.
- 它将 PTY 切换为生/无 Echo 模式 .
- 它以基数64字节有效载荷交换了新线限定帧.
中继发射命令有单独的管并打开 TCP 套接字
getPortUrl它不约束工作空间端口,也不坚持证书/文件.
每个选定的工作空间代理器都有一个中继器; transport.close() 泪下吧
下来.
为什么打开SSH -L 改为 coder port-forward
网络安全带打开 getPortUrl(...) 是关键的道路.
桥发出一个 未启动 bridge-hello wS后立即设置框架
升级。 在测试中,一个新的创造 coder port-forward 隧道没有
可靠地将第一个服务器启动的框架交付给第一个WS客户端,
而SSH本地转发则如此.
此路径被校验端到端对应真实的工作空间: 两者都是合成的
WebSocket 往返旅行( R)scripts/verify-real.ts)和完整的克洛德代码转弯
使用工具(scripts/e2e-claude.ts) (中文(简体) ).
限制和注释
setNetworkPolicy未执行(接受)。 侵略受你的支配 编码器模板/部署,不是这个提供者.- 文件读取了整个文件的缓冲(二进制内容移动为 Base64). 罚款 拖曳大小的文件; 不用于流出非常大的文件 .
CoderNativeTransport目前的目标 POSIX 工作空间bash, (中文(简体) ),stty, (中文(简体) ),base64还有Node.js. 它的默认中继可执行文件是node; 覆盖relayNodeCommand当节点生活在固定的非标准路径.- 必须有多个代理的工作空间
workspace.agent; 数据 本地交通拒绝猜测. @ai-sdk/sandbox-just-bash无法曝光端口并被拒绝 桥后接应器. 这个提供者的存在恰恰是为了提供该端口.- 要运行 Claude Code / Codex,工作空间图像必须满足 工作空间要求。 。 。 .
发展
设置和重新铺设的大门在 捐款额。 。 。 .
软件包特定命令,从 packages/sandbox数字 :
# Unit + local integration tests against fake `coder`/`ssh` executables (no deployment):
pnpm test
# End-to-end against a real workspace (needs the coder CLI + a running workspace):
pnpm verify:real my-ws
# The same contract through Coderd directly. The CLI only mints a token for
# this shell; CoderNativeTransport never invokes it:
CODER_URL=https://coder.example.com \
CODER_SESSION_TOKEN="$(coder tokens create --name ai-sdk-sandbox)" \
pnpm verify:native my-ws
# Create mode: creates a throwaway workspace from a template, waits for
# readiness, runs a command in it, and deletes it:
pnpm verify:create docker
当地融合测试涵盖的内容
他们进行真正的运输(辩论楼, Stdin, Base64 文件)
环行行道、溪流、港口前行生命周期和
创建/状态/预设 JSON 路径) coder编号 :ssh 可执行文件
本地运行命令. scripts/verify-real.ts 运行相同的表面,加一
真实的WebSocket -over -SSH 往返,相对实际的工作空间.
许可证
阿帕奇 - 2.0