feat(wrap): add OpenCode support with headroom wrap opencode#1173
Open
praffulomg wants to merge 1 commit into
Open
feat(wrap): add OpenCode support with headroom wrap opencode#1173praffulomg wants to merge 1 commit into
praffulomg wants to merge 1 commit into
Conversation
OpenCode (anomalyco/opencode) is an OpenAI/Anthropic-compatible client that does not honor OPENAI_BASE_URL/ANTHROPIC_BASE_URL env vars. The new wrap target injects an inline OPENCODE_CONFIG_CONTENT config pointing both the openai and anthropic providers baseURL at the local Headroom proxy. Both baseURLs are /v1-based so @ai-sdk/openai resolves to /v1/chat/completions and @ai-sdk/anthropic (which appends only /messages) resolves to /v1/messages -- the proxy actual routes. Per-project savings are attributed via the X-Headroom-Project header; autoupdate is disabled to pin OpenCode under the wrapper.
Additive only: new headroom/providers/opencode/ package + @wrap.command("opencode") mirroring the aider/vibe pattern. No proxy/registry/auth-mode changes (OpenCode is BYOK = PAYG, already mapped). Existing wraps unaffected.
Contributor
PR governanceThis PR follows the template and is marked ready for human review. |
21 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Adds
headroom wrap opencode, extending Headroom's existing wrap family (claude, codex, copilot, aider, cursor, ...) to the OpenCode CLI so its LLM traffic is routed through Headroom's local compression proxy.OpenCode does not honor the generic
OPENAI_BASE_URL/ANTHROPIC_BASE_URLenvironment variables (it only uses env vars for API keys and${VAR}substitution). Endpoint redirection must therefore be injected via OpenCode's own config mechanism. The wrapper sets theOPENCODE_CONFIG_CONTENTenv var to an inline JSON config that points both theopenaiandanthropicproviders'baseURLat the local proxy.Both baseURLs are
/v1-based (http://127.0.0.1:{port}/v1). This is deliberate and load-bearing:@ai-sdk/openaiappends/chat/completionsand@ai-sdk/anthropicappends only/messagesto the configured base, so a/v1base resolves to the proxy's existing/v1/chat/completionsand/v1/messagesroutes. A base without/v1would silently fall through the proxy's verbatim catch-all and forward uncompressed with no error.Type of Change
Changes Made
headroom/providers/opencode/__init__.py— re-exportsbuild_launch_envandproxy_base_url(mirrorsproviders/codex).runtime.py—proxy_base_url(port) -> http://127.0.0.1:{port}/v1;build_launch_env(port, environ, project)emitsOPENCODE_CONFIG_CONTENTJSON with both providers'baseURLset to the/v1proxy base, an optionalX-Headroom-Projectheader (viasanitize_project_name) for per-project savings attribution, andautoupdate: falseto pin OpenCode under the wrapper.headroom/cli/wrap.py— added the_build_opencode_launch_envimport and a new@wrap.command("opencode")(mirrors the additive Pattern A launch flow: resolve binary viashutil.which("opencode")→ build env →_launch_tool(..., tool_label="OPENCODE", agent_type="opencode")); updated thewrapgroup docstring; removed an obsolete note stating the command did not exist.headroom/telemetry/context.py— added"opencode"to_KNOWN_WRAP_AGENTS(telemetry stack label only).README.md— added an OpenCode row to the agent compatibility matrix and the wrap synopsis.CHANGELOG.md— added an Unreleased > Features entry.tests/test_provider_opencode.py— new unit tests (5 assertions).Additive only. No changes to the proxy, provider registry, or auth-mode classification — existing wraps are unaffected.
Testing
ruff check)mypyon the new package)Test Output
Real Behavior Proof
uv run --no-sync ruff check headroom/cli/wrap.py headroom/telemetry/context.py headroom/providers/opencode/ tests/test_provider_opencode.py;uv run --no-sync mypy headroom/providers/opencode/;uv run --no-sync pytest tests/test_provider_opencode.py -q;uv run --no-sync python -c "from headroom.providers.opencode import build_launch_env, proxy_base_url; print(build_launch_env(8787, {}, 'Demo Proj'))"; and Click CliRunner invocations ofwrap --helpandwrap opencode --help.headroom wrap --helplistsopencodewith claude/codex/aider/copilot/cursor still intact;opencodesubcommand --help exits 0; ruff "All checks passed!"; mypy "Success: no issues found in 2 source files"; pytest 5 passed (new) + 231 passed regression (0 failed/skipped).opencodebinary hitting a real upstream API (binary not available in sandbox); mypy was run only on the new headroom/providers/opencode package, not the full headroom tree.Review Readiness
Checklist
/v1rationale is documented inruntime.py)Additional Notes
/v1suffix on both provider baseURLs is intentional and the most important correctness detail —tests/test_provider_opencode.pyexplicitly asserts both end in/v1to guard against a silent regression (a wrong path falls through the proxy catch-all uncompressed with no 404 in logs).X-Headroom-Projectheader (OpenCode supports custom per-provider headers via config), consistent with the proxy's existing header-basedclassify_project.headroom install opencode, no Docker/scripts changes, and no auth-mode/UA classification (OpenCode is BYOK → PAYG, which is the correct aggressive-compression mode;classify_clientalready maps theopencode/user agent). These are noted as possible follow-ups.mypywas run against the newheadroom/providers/opencode/package (clean), not the entireheadroomtree.