跳转到内容
本站为社区维护的非官方中文翻译,与 Cloudflare, Inc. 无关联。本站不提供账户登录,请勿在任何非官方站点输入您的 Cloudflare 账户信息。官方文档:developers.cloudflare.com

在 sandbox 中运行 OpenCode

最后更新 查看 MarkdownAgent 设置

在 Linux sandbox 中运行 OpenCode ↗,根据提示编辑 GitHub 仓库,然后将其更改作为 diff 读取。OpenCode 通过 AI Gateway 调用模型。你的 Worker 会为这些请求添加 gateway token,且 sandbox 只能访问你的 gateway 和 github.com。

前提条件

  • 来自 构建编程 Agent runner 的 sandbox-coding-agent 项目。本页会替换镜像、模型,以及 Durable Object 中与 Agent 相关的部分。
  • AI Gateway 中的 Anthropic 凭据,可以是 Unified Billing 额度,或作为 provider key 存储的 Anthropic API key。

将 Worker 切换到 OpenCode

  1. 替换 Dockerfile。只有高亮行与教程中的 Dockerfile 不同:

    Dockerfiledockerfile
    FROM node:24-trixie-slim
    
    RUN apt-get update \
        && apt-get install -y --no-install-recommends bash ca-certificates git ripgrep \
        && rm -rf /var/lib/apt/lists/*
    
    RUN npm install --global --ignore-scripts [email protected] \
        && ln -s /usr/local/lib/node_modules/opencode-linux-x64-baseline/bin/opencode /usr/local/bin/opencode
    
    COPY --from=docker.io/cloudflare/sandbox:1.0.1 /usr/local/bin/sandbox-shim /usr/local/bin/sandbox-shim
    
    WORKDIR /workspace
    CMD ["sleep", "infinity"]

    镜像会直接安装 baseline x64 OpenCode 二进制文件。baseline 二进制文件可在每台 x64 CPU 上运行。opencode-ai 软件包会为构建镜像的机器 CPU 选择二进制文件。在其他架构上构建的 amd64 镜像会在模拟器下运行,此时软件包可能选错二进制文件。

    OpenCode 使用镜像中的 rg 搜索文件。没有 ripgrep 时,它会尝试从 GitHub 下载 rg。镜像会固定 OpenCode 版本,因为 OpenCode 的模型列表和事件格式可能在版本之间变化。

  2. 在 wrangler.jsonc 中,将 MODEL 设为 OpenCode 用于 AI Gateway 的模型 ID:

    {
        "vars": {
            "AI_GATEWAY_ACCOUNT_ID": "<ACCOUNT_ID>",
            "AI_GATEWAY_ID": "default",
            "MODEL": "cloudflare-ai-gateway/anthropic/claude-sonnet-5",
        },
    }
    [vars]
    AI_GATEWAY_ACCOUNT_ID = "<ACCOUNT_ID>"
    AI_GATEWAY_ID = "default"
    MODEL = "cloudflare-ai-gateway/anthropic/claude-sonnet-5"

    OpenCode 用于 AI Gateway 的模型 ID 形式为 cloudflare-ai-gateway/<PROVIDER>/<MODEL>。OpenCode 使用固定版本内置的模型列表。要打印该列表,请使用相同的 OpenCode 版本运行 opencode models cloudflare-ai-gateway。

  3. 在 src/sandbox.ts 中,将 ResultEvent 常量替换为承载回复和错误的 OpenCode 事件 schema:

    src/sandbox.tsts
    const OpencodeEvent = z.union([
        z.object({ type: z.literal("text"), part: z.object({ text: z.string() }) }),
        z.object({
            type: z.literal("error"),
            error: z.object({
                name: z.string(),
                data: z.object({ message: z.string().optional() }).optional(),
            }),
        }),
    ]);
  4. 替换 agentCommand() 和 agentEnv() 方法:

    src/sandbox.tsts
    export class AgentSandbox extends DurableObject<Env> {
        // ...
    
        private agentCommand(prompt: string): string[] {
            return [
                "opencode",
                // Run one task without an interactive terminal
                "run",
                // Write one JSON event per line to the events file
                "--format",
                "json",
                "--auto",
                // Select the AI Gateway model from `MODEL`
                "--model",
                this.env.MODEL,
                "--",
                prompt,
            ];
        }
    
        private agentEnv(): Record<string, string> {
            return {
                CLOUDFLARE_API_TOKEN: "provided-by-worker",
                CLOUDFLARE_ACCOUNT_ID: this.env.AI_GATEWAY_ACCOUNT_ID,
                CLOUDFLARE_GATEWAY_ID: this.env.AI_GATEWAY_ID,
                OPENCODE_DISABLE_MODELS_FETCH: "1",
                OPENCODE_DISABLE_AUTOUPDATE: "1",
                OPENCODE_DISABLE_DEFAULT_PLUGINS: "1",
                OPENCODE_DISABLE_LSP_DOWNLOAD: "1",
                OPENCODE_DISABLE_SHARE: "1",
            };
        }
    }

    --auto 会批准 OpenCode 配置未拒绝的每个工具调用。没有它时,OpenCode 会在没有终端的运行中拒绝权限请求。container 会限制 OpenCode 命令可以访问的范围。

    OpenCode 内置的 cloudflare-ai-gateway provider 会从环境读取账户和 gateway ID。对于 Anthropic 模型,它会将请求发送到 gateway 的通用端点:不带后缀的 gateway 路径。runner 中的 Outbound entrypoint 允许该路径及其子路径。CLOUDFLARE_API_TOKEN 是占位符,因为没有 token 时 OpenCode 不会启动。Outbound entrypoint 会用 gateway token 替换 token header。

    OPENCODE_DISABLE_* 变量会让 OpenCode 使用二进制内置的模型列表,并关闭其更新检查、默认插件、语言服务器下载和分享功能。OpenCode 仍会在后台尝试从 npm 安装 @opencode-ai/plugin。该请求会从 Outbound entrypoint 收到 403 响应,OpenCode 会继续运行。

  5. 替换 outcome() 方法:

    src/sandbox.tsts
    export class AgentSandbox extends DurableObject<Env> {
        // ...
    
        private async outcome(exitCode: number): Promise<TaskStatus> {
            let text = "";
            let error: string | undefined;
            const events = await this.files.readFile(eventsPath);
    
            for await (const line of readLines(events.body)) {
                const parsed = OpencodeEvent.safeParse(parseJson(line));
    
                if (!parsed.success) {
                    continue;
                }
    
                if (parsed.data.type === "text") {
                    text = parsed.data.part.text;
                } else {
                    error = parsed.data.error.data?.message ?? parsed.data.error.name;
                }
            }
    
            if (exitCode === 0) {
                return { state: "succeeded", result: text };
            }
    
            if (error === undefined) {
                const stderr = (await this.readOptionalText(stderrPath)) ?? "";
                return {
                    state: "failed",
                    error: `opencode exited with ${exitCode}: ${stderr.slice(-2000)}`,
                };
            }
    
            return { state: "failed", error };
        }
    }

    出现任何模型或 session 错误时,OpenCode 都会以非零退出码退出,因此由退出码决定结果。成功的任务会返回最后一个 text 事件。失败的任务会返回最后一个 error 事件中的消息,例如 gateway 对拒绝的 token 或缺少 Unified Billing 额度返回的消息。

  6. 部署你的 Worker:

    npx wrangler deploy

    教程中的 AI_GATEWAY_TOKEN secret 会继续保留。正在运行的 container 会保留启动时使用的镜像,因此请为 OpenCode 使用新的 sandbox 名称。

运行任务

  1. 将仓库克隆到名为 opencode-1 的 sandbox:

    curl "$WORKER_URL/sandboxes/opencode-1/repository" \
        --json '{"url": "https://github.com/octocat/Hello-World"}'
  2. 启动任务:

    curl "$WORKER_URL/sandboxes/opencode-1/task" \
        --data "Add a file NOTES.md with a one-sentence summary of this repository. Do not commit."
  3. 检查任务,直到其状态为 succeeded 或 failed:

    curl "$WORKER_URL/sandboxes/opencode-1/task"

    succeeded 任务会包含 OpenCode 的最后一条文本回复。

  4. 读取更改:

    curl "$WORKER_URL/sandboxes/opencode-1/diff"

    diff 会显示新的 NOTES.md 文件。

相关资源

这篇文档对您有帮助吗?