opencode
ExperimentalOpenCode coding agent. Headless opencode run and optional opencode serve. Package opencode-ai@1.19.0.
Install
abx template deploy ./opencode -y --alias opencode
abx create --template opencode
# TemplateID from `abx --json template deploy` is also acceptedOn each provider
Express publishes this directory with abx template deploy. The other providers do not declare templates.build, so they do not register this directory.
| Provider | Status | How |
|---|---|---|
| Alibaba Cloud Agent Sandbox Express Edition | Deploys on Express | Use → |
| Alibaba Cloud Agent Sandbox Container Edition | Existing template id | Use → |
| CubeSandbox | Existing template id | Use → |
| E2B | Existing template id | Use → |
| OpenSandbox | Image URI, not this directory | Use → |
About
OpenCode (sst/opencode) in an Agent Sandbox. The image installs npm package opencode-ai@1.19.0. Named commands call that CLI. The primary call is headless opencode run. opencode serve is a second HTTP server on port 4096.
What it does
agent-sandbox-server listens on port 9000 and registers commands that run opencode. opencode_run executes one non-interactive opencode run --format json and returns the assistant text, the raw JSON events, and the session id. opencode_version and opencode_setup_check check the pinned binary and opencode.json without calling a model.
opencode_serve_start starts opencode serve on port 4096 bound to 0.0.0.0. That process is OpenCode's own HTTP API (GET /global/health). Reach it with sb.network.get_url(4096). The platform start_cmd does not start it.
opencode is the previous command name. It runs the same argv as opencode_run and returns the raw stdout string.
Stack
- Base image
fc-e2b-registry.<region>.cr.aliyuncs.com/runtime/base:<tag>.abx template deployreplaces<region>withALIBABA_CLOUD_REGION_IDorcn-hangzhou, and<tag>withABX_BASE_IMAGE_TAGorv0.0.49. Python 3.13 and Node.js 20 come from that image. opencode-ai@1.19.0(npmlateston 2026-10-15). The package binary isopencode. Upstream is sst/opencode.agent-sandbox-serverfrom python-sdk commit61c43e0be65a1f57aa09186d578e7b399e48429bwhen the build context has no wheel. A wheel injected byabx template deploywins. The PyPI releaseagent-sandbox-server==0.3.0predates that commit and is not installed.- git and ripgrep.
- Work directory
/home/user/app. Runtime useruser. No imageCMDorENTRYPOINT. - Generation 2.
start_cmdisagent-sandbox-server serve --port 9000 --commands /home/user/app/commands.py.ready_cmdisagent-sandbox-server healthcheck --port 9000.
Problem solved
Gives a caller a pinned OpenCode CLI inside a sandbox, with one JSON command for a headless turn and an optional OpenCode HTTP server on a sandbox port. Provider settings point at an OpenAI-compatible endpoint. The keys this environment uses are ABX_LLM_API_KEY and ABX_LLM_BASE_URL (DashScope compatible-mode). The image does not contain those values.
Quick start (CLI)
From a checkout of this repository, with Express credentials in the environment:
abx template deploy ./opencode -y --generation 2 \
--start-cmd 'agent-sandbox-server serve --port 9000 --commands /home/user/app/commands.py' \
--ready-cmd 'agent-sandbox-server healthcheck --port 9000'
abx create --template <template-id> -y \
--env ABX_LLM_API_KEY=<key> \
--env ABX_LLM_BASE_URL=<openai-compatible-base-url>
abx run <sandbox-id> opencode_version
abx run <sandbox-id> opencode_setup_check
abx run <sandbox-id> opencode_run --json-args '{"prompt":"Reply with exactly ABX_E2E_OK","model":"dashscope/qwen3.8-flash"}'
abx run <sandbox-id> opencode_serve_start
template.yaml already stores generation, start_cmd, and ready_cmd. The flags repeat them so the command stands alone.
--json-args is required for continue_session (boolean) and port (integer). --arg values are strings.
Remove the sandbox and the template when you are done:
abx kill <sandbox-id> -y
abx template delete <template-id> -y
python3 scripts/express_e2e.py opencode deploys, runs the smoke commands (opencode_version, opencode_setup_check), writes evidence only after a pass, and deletes that sandbox and that template.
Quick start (Python SDK)
Install the client from PyPI as agent-sandbox-client (python3 -m pip install agent-sandbox-client). The import is agent_sandbox. pip install agent-sandbox is an unrelated project.
import asyncio
from agent_sandbox import Sandbox
async def main() -> None:
sb = await Sandbox.create(
template="<template-id>",
timeout=300,
envs={
"ABX_LLM_API_KEY": "<key>",
"ABX_LLM_BASE_URL": "<openai-compatible-base-url>",
},
)
try:
print(await sb.run("opencode_version"))
result = await sb.run(
"opencode_run",
prompt="Reply with exactly ABX_E2E_OK",
model="dashscope/qwen3.8-flash",
)
print(result["text"], result["session_id"])
await sb.run(
"opencode_run",
prompt="Say ABX_E2E_OK again",
session=result["session_id"],
)
await sb.run("opencode_serve_start")
print(sb.network.get_url(4096))
finally:
await sb.kill()
asyncio.run(main())
await sb.run("opencode_run", prompt="...") returns a dict with text, stdout, session_id, model, agent, and cwd. sb.commands.run is a shell command, not this named command. Sandbox.create_sync raises TypeError on python-sdk 0.3.0 (python-sdk#22); use await Sandbox.create.
opencode_run is the primary API: one request, one JSON body, the headless CLI. opencode serve is for clients that already speak OpenCode's HTTP API. Both are in the image. Only the command server on port 9000 starts with the sandbox.
Command reference
| Command | Parameters | Returns | Upstream |
|---|---|---|---|
opencode_run |
see the table below | object | opencode run --pure --format json --auto ... |
opencode |
same as opencode_run |
string (raw JSON events) | same argv as opencode_run |
opencode_version |
none | "1.19.0" |
opencode --version |
opencode_setup_check |
none | "ok" |
opencode --version plus a read of opencode.json |
opencode_serve_start |
port integer, default 4096; hostname string, default 0.0.0.0 |
object | opencode serve --pure --hostname --port |
opencode_serve_status |
none | object | localhost GET /global/health |
opencode_serve_stop |
none | "stopped" |
SIGTERM to the process this template started |
opencode_run parameters:
| Parameter | Type | Required | Default | Meaning |
|---|---|---|---|---|
prompt |
string | yes | Last argv element. Not interpolated into a shell string. | |
model |
string | no | OPENCODE_MODEL, else dashscope/qwen3.8-flash |
--model, form provider/model. |
agent |
string | no | empty (CLI primary agent, build) |
--agent when non-empty. |
session |
string | no | empty | --session to continue that session id. |
continue_session |
boolean | no | false |
--continue (last session). Pass a JSON boolean. |
cwd |
string | no | /home/user/app when that directory exists |
--dir and the child working directory. Must be an existing directory. |
opencode_run returns:
| Field | Meaning |
|---|---|
text |
Concatenated type: text parts from the JSON events. |
stdout |
Raw stdout, one JSON event per line. |
session_id |
sessionID from those events, or empty. |
model |
Selector that was passed to --model. |
agent |
Agent name that was passed, or empty. |
cwd |
Directory that was passed to --dir. |
A missing prompt is HTTP 400. A non-zero CLI exit is HTTP 500 {"error","type":"RuntimeError"}. The message does not include secret values. Timeout is 600 seconds. Stdin is /dev/null.
opencode_serve_start returns running, port, hostname, pid, and healthy. It waits until GET http://127.0.0.1:<port>/global/health returns {"healthy": true, "version": "1.19.0"}. The public URL is sb.network.get_url(port), not that localhost address. A second start on the same port returns the current status.
Configuration
| Name | Required | Default | Purpose |
|---|---|---|---|
ABX_LLM_API_KEY |
for a DashScope call | empty | {env:ABX_LLM_API_KEY} in opencode.json. Inject at create. |
ABX_LLM_BASE_URL |
for a DashScope call | empty | OpenAI-compatible base URL, including the /v1 path. {env:ABX_LLM_BASE_URL}. |
OPENCODE_MODEL |
no | dashscope/qwen3.8-flash |
Used when model is empty. |
OPENCODE_API_KEY |
no | empty | OpenCode Zen credential when model is opencode/<id>. |
OPENCODE_SERVER_PASSWORD |
no | empty | Basic auth password for opencode serve. The CLI warns when it is unset. |
ABX_SERVER_TOKEN |
no | empty | When set, port 9000 requires Authorization: Bearer. Pass token= to the SDK client. Leave unset for anonymous calls. |
OPENCODE_CONFIG |
no | /home/user/app/opencode.json |
Config file the child process reads. |
OPENCODE_DISABLE_AUTOUPDATE |
no | true in the image |
Stops the pinned CLI from upgrading itself. |
ABX_SERVER_PORT |
no | 9000 |
Port start() binds for a local python3 commands.py. |
opencode.json defines provider dashscope with npm package @ai-sdk/openai-compatible. Model ids shipped in that file are qwen3.8-flash and qwen3.8-max. Another id under dashscope/ is added to a temporary config for that one opencode_run. A model on a different provider uses the config file as written; put that provider in opencode.json or pass a model the file already allows.
Set secret values on abx create --env or Sandbox.create(envs=...). Do not write them into the image.
Resources
| Resource | Value |
|---|---|
| cpu | 2 vCPU |
| memory | 2048 MB |
| ports | 9000 (command server), 4096 (opencode serve, only after opencode_serve_start) |
| disk | platform rootfs, about 10G on Express |
Verified
No provider has a passing run recorded for this template.
Secondary development
The command bodies live in commands.py. opencode_run builds an argv list; keep prompts and model ids as list elements. Pin every new installer in the Dockerfile (npm install -g name@version or pip install name==version).
To change the default model, edit OPENCODE_MODEL in template.yaml and the model field in opencode.json, and add the id under provider.dashscope.models when the provider is dashscope. The id has to exist on the endpoint ABX_LLM_BASE_URL points at.
Copy _skeleton only when starting a different template. This directory is the OpenCode entry. Fill changes against docs/template-spec.md and docs/design/opencode.md.
Contributing
CONTRIBUTING.md and docs/template-spec.md.
Issues
| Topic | Repository |
|---|---|
| This template, the spec, the checker | awesome-templates |
Python SDK or agent-sandbox-server |
python-sdk, title prefix [templates] |
abx CLI |
agent-sandbox, title prefix [templates] |
License
Apache-2.0, the same license as this repository. See LICENSE. The OpenCode CLI is upstream software installed at build time.