People who want the OpenCode coding agent…
在 GitHub 查看 ↗opencode
Purpose
People who want the OpenCode coding agent (sst/opencode, npm package opencode-ai) inside an Agent Sandbox. The sandbox installs the pinned CLI and exposes it as named commands. A caller sends one headless prompt, or starts OpenCode's own HTTP server and opens that port with sb.network.get_url.
Capabilities
After the sandbox is READY the image has opencode 1.19.0 on PATH, git, and ripgrep. The platform process is agent-sandbox-server on port 9000.
The primary API is opencode_run: one opencode run invocation, JSON events, then a single JSON result. Parameters cover the prompt, provider/model, agent name, session continue (--continue or --session), and the working directory.
The secondary API is opencode serve on port 4096. It is not started by start_cmd. opencode_serve_start launches it, opencode_serve_status reports it, and opencode_serve_stop stops it. Clients that speak the OpenCode HTTP API use sb.network.get_url(4096). GET /global/health on that port returns {"healthy": true, "version": "1.19.0"}.
opencode_version and opencode_setup_check do not call a model. opencode remains as a compatibility alias of opencode_run and returns the raw JSON-event stdout.
The interactive TUI is not started. Express has no separate TTY for the platform process, and a named command is one request and one JSON body.
Exposed API
opencode_run(prompt, model="", agent="", session="", continue_session=false, cwd="") -> {text, stdout, session_id, model, agent, cwd}
Upstream argv, as a list, with stdin /dev/null:
opencode run --pure --format json --auto --model <model> --dir <cwd> [--agent <agent>] [--session <id>] [--continue] <prompt>
<model> is the model argument, else OPENCODE_MODEL, else dashscope/qwen3.8-flash. <cwd> defaults to /home/user/app when that directory exists. --auto approves permissions the agent has not explicitly denied. --pure skips external plugins. A non-zero exit raises RuntimeError. The child OPENCODE_CONFIG points at the image opencode.json, or at a temp copy when the requested DashScope model id is not already listed.
opencode(prompt, model="", agent="", session="", continue_session=false, cwd="") -> stdout string
Same argv as opencode_run. Kept so callers of the previous opencode command still receive the CLI stdout. New callers should use opencode_run.
opencode_version() -> "1.19.0"
Argv: opencode --version.
opencode_setup_check() -> "ok"
Argv: opencode --version, then a read of opencode.json. Checks the pinned version and that provider dashscope uses @ai-sdk/openai-compatible with {env:ABX_LLM_BASE_URL} and {env:ABX_LLM_API_KEY}. No network call.
opencode_serve_start(port=4096, hostname="0.0.0.0") -> {running, port, hostname, pid, healthy}
Argv: opencode serve --pure --hostname <hostname> --port <port>. Waits until GET /global/health on 127.0.0.1 reports healthy: true. The bind address is 0.0.0.0 so the sandbox port proxy can reach it. A second start on the same port returns the current status.
opencode_serve_status() -> {running, port, hostname, pid, healthy}
No upstream process is spawned. Reads the pid file and probes /global/health.
opencode_serve_stop() -> "stopped"
Sends SIGTERM to the serve process group. Idempotent.
How to build
Base image fc-e2b-registry.<region>.cr.aliyuncs.com/runtime/base:<tag> (the platform image already has Python 3.13 and Node.js 20). npm view opencode-ai version on 2026-10-15 still resolved latest to 1.19.0; the Dockerfile installs opencode-ai@1.19.0 and runs opencode --version.
agent-sandbox-server comes from a wheel injected by abx template deploy when one is in the build context. Otherwise the Dockerfile installs python-sdk main 61c43e0be65a1f57aa09186d578e7b399e48429b (the commit on main that includes PR #21) with:
pip install --no-cache-dir "agent-sandbox-server @ https://github.com/Alibaba-Agent-Sandbox/python-sdk/archive/61c43e0be65a1f57aa09186d578e7b399e48429b.tar.gz#subdirectory=server-sdk"
The PyPI pin agent-sandbox-server==0.3.0 is recorded in the Dockerfile comment and is not what the image runs. That release predates the Express server fixes.
start_cmd: agent-sandbox-server serve --port 9000 --commands /home/user/app/commands.py
ready_cmd: agent-sandbox-server healthcheck --port 9000
No CMD or ENTRYPOINT. Runtime user user. Work directory /home/user/app. The context file opencode.json is copied there.
Provider config (V1 schema, verified with opencode-ai@1.19.0 against an OpenAI-compatible DashScope URL):
{
"$schema": "https://opencode.ai/config.json",
"model": "dashscope/qwen3.8-flash",
"provider": {
"dashscope": {
"npm": "@ai-sdk/openai-compatible",
"name": "DashScope",
"options": {
"baseURL": "{env:ABX_LLM_BASE_URL}",
"apiKey": "{env:ABX_LLM_API_KEY}"
},
"models": {
"qwen3.8-flash": {"name": "Qwen3.8 Flash"},
"qwen3.8-max": {"name": "Qwen3.8 Max"}
}
}
}
}
{env:NAME} is the substitution OpenCode documents for 1.x config. A local opencode run --format json --model dashscope/qwen3.8-flash against ABX_LLM_BASE_URL returned a text event containing ABX_E2E_OK.
Express feasibility
Feasible: yes
Limiting factor: none
Express (non-root user, 2 vCPU, no KVM, no Docker, no extra capabilities, about 10G rootfs, egress allowed) can run this CLI. The binary is a standalone ELF from the npm postinstall; it does not need Docker or KVM. Egress is required for a real model call and for npm install at image build. Memory is 2048 MB, which covers the CLI plus one headless run. The serve process is an extra Node-free native binary on port 4096 and fits the same limit.
Risks
ABX_LLM_API_KEY, OPENCODE_API_KEY, ABX_SERVER_TOKEN, and OPENCODE_SERVER_PASSWORD are declared empty and injected at create. The image and opencode.json store {env:...} placeholders, not values. Exception text strips values of environment variables whose names contain KEY, SECRET, TOKEN, or PASSWORD.
opencode_run passes --auto, so tool permissions that the agent has not explicitly denied are approved. That is required for a non-interactive run and is the dangerous flag upstream documents.
opencode serve logs a warning when OPENCODE_SERVER_PASSWORD is unset. The sandbox gateway still fronts the port. Set the password when the OpenCode HTTP API should require basic auth (OPENCODE_SERVER_USERNAME defaults to opencode in the CLI).
The CLI is pinned and OPENCODE_DISABLE_AUTOUPDATE=true, so a running sandbox does not download a newer binary. Model ids in opencode.json are the ones this endpoint answered with (qwen3.8-flash, qwen3.8-max). Another endpoint needs model set to an id that endpoint serves. OpenCode Zen (opencode/big-pickle, OPENCODE_API_KEY) is not the default; pass model to use it.
The template does not start the TUI, does not run opencode upgrade, and does not write credentials into the image.
Test plan
L0: python3 scripts/run_local_e2e.py. e2e_test.py checks GET /health, the command list (including parameter names on opencode_run), and HTTP 400 for POST /commands/opencode_run without prompt. When opencode is on PATH, it also expects opencode_version 1.19.0, opencode_setup_check ok, and a serve start on 127.0.0.1 with /global/health. E2E_RUN_LLM=0 skips the model call. CI does not install the CLI; those cases SKIP and the process still exits 0.
L1: docker build with the Alibaba base image. The log shows opencode-ai@1.19.0 and opencode --version printing 1.19.0.
L2: python3 scripts/express_e2e.py opencode on alicloud-express. Smoke is opencode_version expecting 1.19.0 and opencode_setup_check expecting ok, through abx run and Sandbox.run. No model key.
L3: create one sandbox with ABX_LLM_API_KEY and ABX_LLM_BASE_URL, call opencode_run with a prompt that asks for ABX_E2E_OK, assert the marker in the result, then delete the sandbox. The key in this environment is the OpenAI-compatible DashScope endpoint already used for the local probe. An empty key is a failure when E2E_REQUIRE_LLM=1.
L4: not planned. No other provider is in this run.