deniz.in

Markets

Weather

Loading weather

· via dev.to (home feed)

OpenCode-Wrap exposes OpenCode's free model behind an OpenAI-compatible local API

A developer has published OpenCode-Wrap, a dependency-free Node server that translates OpenAI's chat-completions API onto OpenCode's free-tier model, removing per-token costs for small automation jobs.

OpenCode-Wrap exposes OpenCode's free model behind an OpenAI-compatible local API

What was built

A developer has released OpenCode-Wrap, an open-source Node server that puts an OpenAI-compatible chat-completions API in front of OpenCode's server mode. According to the author's post on dev.to, the motivation was a string of tiny jobs — renaming sessions, summarising compaction output, throwaway glue scripts — where paying per token felt wasteful, while existing free tiers all demanded signups, keys and dashboards.

OpenCode had meanwhile made a free model available, muse-spark-1.3-contributor-free, but the dev.to post notes it was only reachable through the OpenCode CLI, with no HTTP endpoint that other tools could call. OpenCode-Wrap fills that gap: a shim that speaks OpenAI's /v1/chat/completions protocol on one side and opencode serve's session-based model on the other.

A deliberately thin architecture

The pipeline is short: a caller hits OpenCode-Wrap on port 8000, the wrapper translates the request and forwards it to opencode serve on port 4100, which talks to the free model. The whole thing is a single Node process with zero npm dependencies, starts with npx opencode-wrap, and reuses the credentials the local opencode CLI already holds, so there is no separate API key to manage or config to write. If no opencode serve instance is reachable, the wrapper launches its own.

Where the actual work lives

The dev.to post is candid that protocol translation is the hard part, because OpenAI's request shape and opencode's session-and-event model differ substantially.

Sessions are handled per request. The wrapper keeps no state: every incoming call creates a fresh opencode session, replays the full message history into it, and deletes the session afterwards. The author concedes this looks wasteful but argues it makes the server trivially crash-safe, with no session state to corrupt.

Streaming is passed through live. When a request sets stream to true, backend message.part.delta events are forwarded as OpenAI-style SSE chunks the moment they arrive rather than buffered. If the event bus is unreachable, the wrapper falls back to buffered replay. A subtler edge case: once streaming headers have been sent, the HTTP status can no longer change, so a backend that dies mid-stream has its failure delivered inside the stream as an error chunk.

Tool calling is the least transparent area. Because opencode serve offers no passthrough for custom tools, the wrapper describes caller-supplied tools in the prompt and parses fenced tool calls out of the model's response, returning them as proper OpenAI tool_calls. Opencode-native tools such as bash, read and edit simply execute server-side. One documented quirk: with tool_choice set to auto, the model occasionally answers from general knowledge instead of invoking a tool, so callers should force required or name a specific tool.

Errors are normalised to OpenAI conventions. The free tier reportedly throws transient 500s, so the wrapper retries three times with backoff on fresh sessions and surfaces persistent failures as 429 or 502, letting callers apply familiar retry and fallback logic. Model IDs are trimmed and aliased, obvious typos fail fast with a 400 and a suggestion, and empty, content-less responses are retried rather than served.

How it slots into a wider stack

The author also maintains Lynkr, a gateway for spreading subscriptions across multiple providers. Two integration patterns are described: pointing Lynkr at the local endpoint so the free model becomes just another provider in the routing tier, or bypassing the gateway entirely by registering the wrapper in opencode.c as the small_model, sending session titles and compaction summaries to the free backend while the primary model stays on paid routing.

Guardrails, and what the author would change

Because it fronts a shared free tier, the wrapper enforces fair-use behaviour: personal and light use only, automatic backoff with a 429 response when the backend signals slowdown, request bodies capped at 8MB, and localhost-only exposure since it reuses CLI credentials rather than issuing its own secrets.

The acknowledged weak spot is the per-request session replay. A session pool with incremental history would cut latency for chatty workloads, but the author decided that the added statefulness would make debugging harder and that simplicity was worth more for personal-scale use.

Why it matters

The OpenAI chat-completions API has become the default dialect of the LLM tooling ecosystem, and this project shows how much leverage a thin translation shim can buy: wrap a non-conforming backend in that protocol and every compatible script, agent framework and editor gains a new backend overnight. It also demonstrates a sensible cost pattern — routing high-volume, low-stakes background work such as titling and summarisation to a free local backend while reserving paid models for substantive tasks. The compromises along the way, from prompt-injected tool calling to hard fair-use limits and localhost-only credentials, are a useful map of where such shims stop being invisible.

  • #opencode
  • #llm
  • #developer-tools
  • #open-source
  • #node-js

Related posts