Skip to content

xiongxz/opencode-cubesandbox-web

Repository files navigation

opencode CubeSandbox Web

CubeSandbox-ready opencode Web template generated from agent-deploy-kit and then customized for a basic coding-agent demo.

It runs three surfaces in one sandbox:

  • 49983: CubeSandbox envd control plane and readiness probe.
  • 49999: small adapter API for /v1/init, health, and a simple /v1/agent/run demo endpoint.
  • 4096: native opencode Web UI and native opencode HTTP API.

See HTTP_API_USAGE.md for the sandbox HTTP API calling guide and protocol examples.

What It Does

The adapter starts opencode web --hostname 0.0.0.0 --port 4096 inside /workspace/repo. If that directory is empty, it seeds a tiny Python project with app.py and test_app.py so the first demo can ask opencode to modify code.

The adapter keeps ADK's CubeSandbox contract and observability layout. Calls to POST /v1/agent/run create or reuse an opencode session, send a text prompt to the native POST /session/{id}/message API, and return the opencode response plus the session_id.

Build

docker buildx build --platform linux/amd64 \
  -f Dockerfile.cubesandbox \
  -t <registry>/opencode-cubesandbox-web:latest \
  --push .

For reproducible builds, pin OPENCODE_VERSION:

docker buildx build --platform linux/amd64 \
  --build-arg OPENCODE_VERSION=<version> \
  -f Dockerfile.cubesandbox \
  -t <registry>/opencode-cubesandbox-web:<version> \
  --push .

For release builds that need stronger reproducibility, also pin base image digests:

docker buildx build --platform linux/amd64 \
  --build-arg OPENCODE_VERSION=1.14.48 \
  --build-arg CUBESANDBOX_BASE='ghcr.io/tencentcloud/cubesandbox-base:2026.16@sha256:4e6ef7cdcfe9d5d9221f9206a3cedb669eea25c2d35709af8fd199147ad6ceb9' \
  --build-arg NODE_IMAGE='node:22-bookworm@sha256:62e4daa6819762bbd3072af77cc282ab72c631c4aed30dd7980192babaf385b3' \
  -f Dockerfile.cubesandbox \
  -t <registry>/opencode-cubesandbox-web:1.14.48 \
  --push .

Create CubeSandbox Template

cubemastercli tpl create-from-image \
  --image <registry>/opencode-cubesandbox-web:latest \
  --writable-layer-size 2G \
  --expose-port 49983 \
  --expose-port 49999 \
  --expose-port 4096 \
  --probe 49983 \
  --probe-path /health

agent.build.yaml contains the same port contract for platforms that consume it directly.

Runtime Init

Use the adapter API after sandbox boot to inject model credentials and provider settings. Do not bake shared API keys into the image.

curl -X POST http://<sandbox-host>:49999/v1/init \
  -H 'Content-Type: application/json' \
  -d '{
    "config": {
      "llm_base_url": "https://api.openai.com/v1",
      "llm_api_key": "sk-...",
      "llm_model": "gpt-5.2-codex",
      "opencode_provider_id": "openai",
      "opencode_agent": "build",
      "opencode_workdir": "/workspace/repo"
    }
  }'

Common aliases also work: OPENAI_API_KEY, OPENAI_BASE_URL, OPENAI_MODEL, OPENCODE_MODEL, and OPENCODE_WORKSPACE.

If OPENCODE_SERVER_PASSWORD is set before boot, native opencode Web/API uses Basic auth. Supplying opencode_server_password in /v1/init stores it for adapter-to-opencode calls and restarts opencode, but it is best treated as a boot-time setting.

Basic Coding Demo

curl -X POST http://<sandbox-host>:49999/v1/agent/run \
  -H 'Content-Type: application/json' \
  -d '{
    "query": "Modify app.py to add a subtract(a, b) function, add tests, and run pytest.",
    "input": {
      "title": "add subtract demo",
      "agent": "build"
    }
  }'

The response includes:

  • structured_output.session_id: reuse this for follow-up turns.
  • structured_output.message: native opencode message payload.
  • final_text: best-effort extraction of text from opencode's response.

Follow-up turn:

curl -X POST http://<sandbox-host>:49999/v1/agent/run \
  -H 'Content-Type: application/json' \
  -d '{
    "session_id": "ses_...",
    "query": "Now update README.md to document the new subtract function."
  }'

Local Run Without CubeSandbox

Install Python dependencies and the opencode CLI, then point runtime state at a writable local directory:

UV_CACHE_DIR="$PWD/.local/uv-cache" uv sync
npm install --prefix "$PWD/.local/npm" opencode-ai

PATH="$PWD/.local/npm/node_modules/.bin:$PATH" \
OPENCODE_WORKDIR="$PWD/.local/repo" \
OPENCODE_RUNTIME_ROOT="$PWD/.local/runtime" \
AGENT_OBSERVABILITY_DIR="$PWD/.local/agent/logs" \
OPENCODE_HOST=127.0.0.1 \
OPENCODE_PORT=55112 \
./.venv/bin/uvicorn deploy_adapter.cubesandbox.server:app --host 127.0.0.1 --port 55111

Then check:

curl http://127.0.0.1:55111/healthz
curl http://127.0.0.1:55112/global/health

Use POST /v1/init to inject llm_api_key, llm_base_url, and llm_model before running a real coding task.

Native opencode Web

Open the sandbox-exposed 4096 port in a browser when CubeSandbox host-based routing is available. In a typical CubeSandbox public-port layout this is something like:

https://4096-<sandbox_id>.cube.app

When only CubeSandbox path proxy URLs are available, prefer the adapter proxy on port 49999:

http://<cube-api-host>/api/v1/sandboxes/<sandbox_id>/proxy/49999/web/

The adapter proxy rewrites opencode Web's root-relative static assets and runtime API calls so the UI can run under the CubeSandbox path prefix.

Native opencode OpenAPI schema is available from the same port at:

https://4096-<sandbox_id>.cube.app/doc

Useful native APIs:

  • GET /global/health
  • GET /event
  • GET /config/providers
  • PUT /auth/{providerID}
  • POST /session
  • POST /session/{sessionID}/message
  • POST /session/{sessionID}/prompt_async
  • GET /session/{sessionID}/message

Local Smoke Test

docker buildx build --platform linux/amd64 --load \
  -f Dockerfile.cubesandbox \
  -t opencode-cubesandbox-web:local .

docker run --rm \
  -p 49983:49983 \
  -p 49999:49999 \
  -p 4096:4096 \
  opencode-cubesandbox-web:local

Then check:

curl -i http://127.0.0.1:49983/health
curl http://127.0.0.1:49999/healthz
curl http://127.0.0.1:4096/global/health

Path Proxy Regression Test

The opencode Web UI is normally served from /, but CubeSandbox path proxy exposes it under:

/api/v1/sandboxes/<sandbox_id>/proxy/49999/web/

Before changing the proxy rewrite code, run the local regression checks:

./.venv/bin/python -m unittest tests.test_path_proxy_rewrite -v
./.venv/bin/python tools/mock_path_proxy_smoke.py

The mock smoke test covers HTML entry assets, dynamic /assets/*.js chunks, CSS url(/assets/...), SSE, and prompt_async under a CubeSandbox-style path prefix.

Files

  • Dockerfile.cubesandbox: CubeSandbox image with Node 22, opencode, Python adapter, and envd.
  • agent.build.yaml: build/runtime contract.
  • deploy_adapter/cubesandbox/server.py: adapter HTTP API.
  • deploy_adapter/cubesandbox/opencode_service.py: opencode process and native API bridge.
  • deploy_adapter/cubesandbox/runtime_config.py: /v1/init config normalization.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages