Skip to content

Instantly share code, notes, and snippets.

@Aaronontheweb
Created June 9, 2026 13:35
Show Gist options
  • Select an option

  • Save Aaronontheweb/276765cea4d1f538e24ec79149372ae1 to your computer and use it in GitHub Desktop.

Select an option

Save Aaronontheweb/276765cea4d1f538e24ec79149372ae1 to your computer and use it in GitHub Desktop.
Programmatic OpenCode control via HTTP API
name opencode-driver
version 1.0.0
description Programmatic OpenCode control via HTTP server API for planning, implementation, and code generation tasks.

opencode-driver

A NetClaw skill for programmatic OpenCode control via its HTTP server API. Drives OpenCode's session/message model for planning, implementation, and code generation tasks.

Prerequisites

  • OpenCode must be installed (opencode on PATH)
  • OpenCode server must be running (opencode serve)

Environment

Variable Purpose
OPENCODE_API_URL Override default server URL (default: http://localhost:4096)
OPENCODE_SERVER_PASSWORD Basic auth password if required

Quick Reference

Core Workflow

  1. check_availability → verify OpenCode is installed, server is running, and providers are connected
  2. create_session → start a new session with title
  3. send_message → single-shot (blocking) or send_message_async → fire-and-forget
  4. get_session_status → check for stuck/retry loops
  5. abort_session → kill a wedged session before starting fresh

Model Selection

Models are objects with providerID and modelID. Configure your preferred providers in your OpenCode config:

Provider Model When to Use
openai gpt-4o Default for all coding tasks. Fast, cheap, great at real-time codegen.
openai o4-mini (extended thinking) Super-complicated tasks. Use when Spark stalls or you need deep reasoning/architecture. Slow but thorough.
ollama qwen2.5-coder:32b or llama3.1:8b Local fallback when cloud providers are down or for quick internal checks.
local-llm qwen2.5:32b-instruct Local fallback via your dedicated GPU.

Commands

check_availability

Run this first. Verifies OpenCode is installed, server is reachable, and providers are connected.

# Step 1: Verify binary exists
which opencode
opencode --version

# Step 2: Verify server is responding
GET {OPENCODE_API_URL}/global/health

# Step 3: Verify providers are connected
GET {OPENCODE_API_URL}/provider

Returns success if all three pass. If the server isn't running but the binary is, the skill should start it:

opencode serve --port 4096 --hostname 127.0.0.1 &

create_session

Start a new session. Always use this for automation — never reuse old sessions.

POST {OPENCODE_API_URL}/session
Body: { "title": "task-description-here" }

Returns session object with id and slug.

send_message

Send a prompt to a session and wait for the full response.

POST {OPENCODE_API_URL}/session/{SESSION_ID}/message
Body: {
  "model": { "providerID": "openai", "modelID": "gpt-5.3-codex-spark" },
  "parts": [{ "type": "text", "text": "Your prompt here" }],
  "thinking": { "type": "enabled", "budget_tokens": 8192 }
}

Critical: Wrap in timeout. If response doesn't return within 60s, abort the session and try again. See "Safety" section.

The thinking field enables OpenAI's effort/extended thinking for Codex. budget_tokens controls how much the model can think before responding.

send_message_async

Fire-and-forget prompt. Returns 204 No Content immediately.

POST {OPENCODE_API_URL}/session/{SESSION_ID}/prompt_async
Body: (same as send_message above)

Check progress via get_session_status or poll GET {OPENCODE_API_URL}/session/status.

get_session_status

Check if a session is ready, busy, or stuck in retry.

GET {OPENCODE_API_URL}/session/status

Returns:

{
  "ses_abc123": { "type": "ready" },
  "ses_def456": { "type": "busy" },
  "ses_ghi789": { "type": "retry", "attempt": 42, "next": 1234567890 }
}

If type is "retry", the session is stuck. Do NOT send messages to it. Delete or kill it.

abort_session

Interrupt a running session (abort in-progress generation).

POST {OPENCODE_API_URL}/session/{SESSION_ID}/abort

Returns true if aborted successfully.

delete_session

Permanently remove a session and all its data.

DELETE {OPENCODE_API_URL}/session/{SESSION_ID}

list_sessions

List all sessions.

GET {OPENCODE_API_URL}/session

list_models

Show available models from configured providers.

GET {OPENCODE_API_URL}/provider

Safety Rules

These are non-negotiable for reliable external control:

  1. NEVER reuse sessions for automation. Create a new session per task. Delete it when done.

  2. Always check session status before sending. If type is "retry", the session is permanently stuck until the process is killed. Abort it immediately.

  3. Always abort before sending to a session that may have prior activity. Call abort_session first if there's any chance the session has a hanging request.

  4. Timeout on all message calls. If a message call doesn't return within 60 seconds, abort that session. OpenCode's retry loops will never recover on their own.

  5. Kill stuck processes. If the server itself shows multiple sessions in retry, kill the opencode process: kill -9 <PID>. Restart the server afterwards.

  6. No silent failures. If the server returns an error (connection refused, auth failure, model not found), report it and stop — don't retry indefinitely.

Use Cases

1. Single-Shot Planning (Spark)

check_availability
create_session(title="architecture-plan")
send_message(model="openai/gpt-5.3-codex-spark", text="Design a clean architecture for X...")
→ Receive plan
delete_session()

2. Super-Complex Architecture (GPT-5.5)

# Use GPT-5.5 when Spark is too shallow or the problem is highly complex
check_availability
create_session(title="deep-architecture-plan")
send_message(
  model="openai/gpt-5.5",
  text="Design a high-performance distributed system for X with these constraints: [...]"
)
→ Receive deep analysis
delete_session()

3. Multi-Step Implementation

# Step 1: Plan
create_session(title="impl-plan")
send_message(model="openai/gpt-5.3-codex-spark", text="Plan steps for X...")
→ Get plan steps

# Step 2: Implement (new session, passes plan as context)
create_session(title="impl-phase1")
send_message(
  model="openai/gpt-5.3-codex-spark",
  text="Implement: [paste plan steps]"
)
→ Receive implementation

4. Continuation / Follow-Up

# Continue existing session with additional context
create_session(title="followup")
send_message(
  parent_id="ses_previous_plan",  # Optional: reference prior session
  text="Based on the previous plan, now implement step 3..."
)

5. Async Batch Processing

create_session(title="batch-1")
send_message_async(model="openai/gpt-5.3-codex-spark", text="Task A")

create_session(title="batch-2")
send_message_async(model="openai/gpt-5.3-codex-spark", text="Task B")

# Poll until complete
while status["ses_batch_1"].type == "busy" or status["ses_batch_2"].type == "busy":
    sleep(2)
    status = get_session_status()

# Retrieve results
messages = GET /session/{session_id}/message

Error Patterns

opencode: command not found

OpenCode isn't installed. Install via: npm install -g @opencode-ai/opencode or brew install opencode or curl -fsSL https://opencode.ai/install.sh | bash.

500 / Connection Refused

OpenCode server isn't running. Start it: opencode serve --port 4096 --hostname 127.0.0.1 &

Cannot connect to API

The LLM provider is unreachable (OpenAI, Ollama, etc.). Verify the provider is configured and accessible. This is NOT a retry situation — fix the provider and abort the session.

Missing key / Expected object | null

Bad model spec. Models must be {"providerID": "...", "modelID": "..."}, not a string.

401 / auth required

Server requires basic auth. Set OPENCODE_SERVER_PASSWORD env var or pass -u opencode -p <password> to curl.

Session in retry state

The session has a stuck request. Delete it: DELETE /session/{id} and start fresh. If the process count grows, kill opencode and restart.

Configuration

Your OpenCode config lives at ~/.config/opencode/opencode.json. The server inherits this config.

Default model (used when not specified in message body):

{
  "model": "openai/gpt-5.3-codex-spark"
}

You can override per-request by passing the model field in the message body.

Notes

  • OpenCode sessions maintain full conversation history (message chain)
  • The thinking/budget_tokens fields are OpenAI-specific for Codex extended thinking. Use budget_tokens: 8192 for heavy reasoning tasks on GPT-5.5.
  • For local models (Ollama, petabridge-big-gpu), omit thinking — those providers don't support it yet
  • Each message call is a full LLM round-trip (not streaming)
  • SSE events at GET {OPENCODE_API_URL}/event provide real-time output if you want streaming
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment