Commit graph

2 commits

Author SHA1 Message Date
Rudimar Ronsoni
fa05ebc849
docs: clarify OpenCode integration (#1317)
## Description

Clarifies the OpenCode documentation follow-up for PR #1105 so users can
install `headroom-opencode`, configure provider routing, use the native
plugin, and copy working retrieve/compression helper examples.

## Type of Change

- [x] Documentation update
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change

## Changes Made

- Documented how `headroom wrap opencode` wires provider config, MCP
tools, and runtime environment.
- Documented the native `HeadroomPlugin` path, `HEADROOM_PROXY_URL`,
retrieve tooling, and programmatic config helpers.
- Fixed `plugins/opencode/README.md` examples so `compressWithHeadroom`
uses the exported options-object API and `headroom_retrieve` uses
`hash`.

## Testing

- [x] Type checks pass.
- [x] Unit tests pass.
- [x] Whitespace check passes.

### Test Output

```text
plugins/opencode: npm run typecheck
> tsc --noEmit

plugins/opencode: npm test
Test Files  2 passed (2)
Tests  9 passed (9)

docs: npm run types:check
✓ Types generated successfully

repo: git diff --check
(no output)
```

## Real Behavior Proof

- Environment: Local macOS worktree at
`docs/pr-1105-documentation-followup`, Node/npm project commands run
from `plugins/opencode` and `docs`.
- Exact command / steps: Updated the README snippets, ran `npm run
typecheck`, reran `npm test` with elevated permissions after the sandbox
blocked a local `127.0.0.1` listener, ran `npm run types:check` in
`docs`, and ran `git diff --check`.
- Observed result: Typecheck completed with `tsc --noEmit`; the OpenCode
package test suite reported 2 files and 9 tests passed; docs type
generation completed successfully; `git diff --check` produced no
output.
- Not tested: Browser-rendered documentation preview. `docs: npm run
build` was started locally but produced no output for roughly 90 seconds
and was stopped, so this follow-up does not claim a fresh local docs
build result.

## Review Readiness

- [x] I have performed a self-review
- [x] This PR is ready for human review

## Additional Notes

- The linked review comment asked for README examples to match
`compressWithHeadroom(messages, options)` and
`createHeadroomRetrieveTool` requiring `hash`; both snippets now match
the exported API.
2026-06-24 21:54:05 -05:00
Rudimar Ronsoni
b4571cc346
feat: headroom wrap opencode / unwrap opencode CLI (#1105)
## Summary

This PR implements transparent `headroom wrap opencode` support without
asking users to edit OpenCode provider URLs, choose an extra CLI flag,
or maintain a static provider list.

The wrapper now lives at the runtime transport boundary: OpenCode keeps
its user/provider config, while Headroom intercepts outbound provider
traffic in-process and routes it through the local Headroom proxy.

## What changed

### Transparent OpenCode wrapping

- `headroom wrap opencode` injects the `headroom-opencode` plugin
through `OPENCODE_CONFIG_CONTENT`.
- Existing OpenCode provider URLs are preserved. We do not rewrite user
config URLs to point at Headroom.
- Existing `OPENAI_BASE_URL` and `ANTHROPIC_BASE_URL` env vars are
preserved.
- Local OpenCode traffic, localhost traffic, and Headroom proxy traffic
bypass the shim to avoid loops.

### Runtime transport interception

- Added an OpenCode plugin transport shim that wraps:
  - `globalThis.fetch`
  - `http.request` / `http.get`
  - `https.request` / `https.get`
- External provider calls are routed to the local Headroom proxy.
- The original upstream origin is passed through `x-headroom-base-url`,
so the proxy can forward to the real provider without changing OpenCode
config.
- External `http2.connect` is blocked loudly instead of allowing direct
provider traffic to leak outside Headroom.

### Live provider additions

Provider coverage is no longer based on a static config scan. Because
routing happens at outbound request time, providers added mid-session
are routed through Headroom automatically as long as they use the
covered Node transport paths.

### Subagent and child-process coverage

- The parent OpenCode plugin sets a packaged Node preload shim through
`NODE_OPTIONS=--import=.../hook-shim/handler.js`.
- The transport shim patches `child_process.spawn`, `exec`, `execFile`,
and `fork` so child Node processes receive the Headroom preload even
when OpenCode passes a custom `env`.
- The child-process shim fails closed if it loads without
`HEADROOM_OPENCODE_TRANSPORT_PROXY_URL`.
- This closes the subagent leak path where a child Node process could
otherwise start without Headroom transport interception.

## Why this goes beyond PR #1089

PR #1089 improves OpenCode provider registration, but it still focuses
on provider config shape. This PR moves the enforcement boundary to
runtime transport interception.

This PR goes further because:

- No provider URL rewriting is required.
- New providers added mid-session are covered automatically.
- Subagents and child Node processes inherit the Headroom transport
shim.
- Direct external HTTP/2 paths fail loudly instead of leaking.
- The wrap remains transparent to the user's OpenCode provider config.
- The wrapper is fail-closed for unsupported child-process preload
state.

## Additional robustness fixes

While validating the change in Docker, the full Python suite exposed
unrelated Linux/container robustness issues. These are fixed in this PR
so the suite is green:

- Binary cache handling now treats cache paths under a non-writable
existing parent as unavailable, including when tests run as root in
Docker.
- `release_version.py` honors `MANUAL_VER` before git calls so direct
script execution works outside a `.git` checkout.
- Test logger isolation now resets relevant Headroom child loggers so
proxy logging setup cannot poison later `caplog` tests.
- The scanner missing-path test now uses a guaranteed missing `tmp_path`
child instead of relying on `/nonexistent/path`.

## Validation

All implementation validation was run inside Docker.

- Full Python suite from a fresh Docker copy: `6605 passed, 523
skipped`.
- Ruff on changed Python/OpenCode paths: passed.
- OpenCode plugin typecheck: passed.
- OpenCode plugin tests: `9 passed`.
- OpenCode plugin build: passed.
- Hook shim preload smoke test: passed.

## Notes

This PR intentionally does not add a CLI option. `headroom wrap
opencode` means full wrap. Either Headroom wraps OpenCode transparently,
or the path fails loudly instead of silently leaking provider traffic.

---------

Co-authored-by: Rudimar Ronsoni <6081613+rudironsoni@users.noreply.github.com>
2026-06-22 11:07:12 -05:00