Repository navigation
feat(setup): bundle the OpenCode CLI as a managed runtime binary - #1002
Merged
Merged
Conversation
Adds the fourth managed runtime artifact alongside tmux, mitmproxy, and cloudflared: the upstream anomalyco/opencode release zip, hash-pinned per arch (archive sha256 + bytes + extracted binary sha256) in third_party/opencode/manifest.json. Binaries never enter git; the manifest plus fetch-opencode.mjs is the source of truth and version bumps stay one-file PRs. Darwin x86_64 pins the -baseline asset so one binary covers every Intel Mac with no AVX2 assumption. fetch verifies the archive hash, byte size, AND the extracted binary's hash before committing it to the cache, then smokes the version natively; verify re-checks hashes for every arch plus a no-Homebrew otool gate natively (upstream links only system dylibs). runtime:prepare:mac and the runtime:verify aggregate now include opencode, and copy-packaged-resources stages it under out/main/runtime/opencode/ for the existing asarUnpack glob. Refs #994
OpenCode panes on a fresh install need the CLI on a PATH the app never sees — Finder-launched processes inherit no shell rc, so ~/.opencode/bin is invisible exactly where it matters (#994; adjacent fallout in #993). The binary now ships (previous commit), so this wires the resolution: runtimeTools grows an opencode resolver in the proven tmux shape (find + chmod + real-exec probe, --version); toolchain layers it in front of cached/PATH lookups at the getToolPath choke point so all four transcriptEngine call sites and every version probe get bundled precedence without per-site edits. A valid manual override still beats the bundle — the user's explicit word beats every probe, the same policy the setup gate applies — and the override lives outside cachedPaths so PATH revalidation can never clobber the shipped binary. The gate's opencode row now clears as found with source 'bundled' instead of nagging for a PATH binary. cliUpdate's type note records that the bundled version moves with app updates, so it stays out of the npm auto-update union. Fixes #994
The #994 manifest ships 1.18.31 while the accepted record still said 1.18.30, and the file's own rule is that acceptance moves only after a compatibility review. The review ran 2026-09-19: deterministic suites green, hash-verified official asset, production panes on 1.18.31 since Sep 14. The live suite could not discriminate the two versions (it fails identically on both, including the recorded 1.18.30 — #1009), so the acceptance note records that basis honestly instead of implying a passing live check. npm latest is 1.18.31, which also clears the upstream-watch drift. Refs #994, Refs #1009
Owner
Author
Detailed review (pre-merge)Verdict: approve. Architecture, security, and conventions all check out:
One finding, resolved in-branch: the pin (1.18.31) disagreed with
Merging once CI on 80ceedd is green. |
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.
Problem
A fresh install of Agent Code cannot use OpenCode panes unless the
opencodeCLI is on PATH — but the binary typically lives at~/.opencode/bin, added by a shell rc a Finder-launched app never sources (adjacent fallout observed in #993). The bundling pipeline already ships tmux/mitmproxy/cloudflared; OpenCode is the fourth tool and the first provider CLI.Implemented behavior
third_party/opencodeholds the manifest pinning upstreamanomalyco/opencodev1.18.31 — darwin-arm64 (opencode-darwin-arm64.zip) and darwin-x86_64 (opencode-darwin-x64-baseline.zip, so one artifact covers every Intel Mac with no AVX2 assumption). Each entry pins archive sha256 + bytes + extracted binary sha256. No binary ever enters git; version bumps stay one-file manifest PRs.fetch-opencode.mjsdownloads, double-hash-verifies (archive, then extracted binary), extracts via/usr/bin/unzip, and smokes--versionnatively;verify-opencode.mjs --strictre-checks hashes for every arch plus a nativeotool -Lno-Homebrew gate (upstream links only system dylibs). Wired intoruntime:prepare:macand theruntime:verifyaggregate; staged bycopy-packaged-resources.mjsunder the existingasarUnpackglob.runtimeTools.tsgains anopencoderesolver (tmux shape: find + chmod + real-exec probe).toolchain.tslayers it in front of cached/PATH lookups at thegetToolPathchoke point — everytranscriptEnginecall site and version probe gets bundled precedence with zero per-site edits. A valid manual override still beats the bundle; a dead one falls through. The override lives outsidecachedPathsso PATH revalidation can never clobber the shipped binary, and dev builds without a staged cache keep today's behavior.found: true,source: 'bundled', no install prompt when the artifact ships (mitmdump precedent: bundled > PATH).cliUpdate.tsrecords why.Design decisions and tradeoffs
opencode-ai: per-asset checksum pinning, no postinstall indirection, identical lifecycle to the other bundled tools.Issues
Fixes #994
Plan:
docs/superpowers/plans/2026-09-18-opencode-bundled-runtime.md(first commit on the branch)Tests and verification
toolchain.bundledOverride.test.ts— bundled beats cached PATH path; valid manual override wins (and skips the bundled resolve entirely); dead override falls back to bundled; null resolver keeps today's behavior.npm run runtime:fetch:opencode -- --all+verify-opencode.mjs --strict— green against the real 46+48 MB assets (native arm64: hash +--version+ linkage; x86_64: cross-arch hash).npm run typecheck— clean. Setup + providerSwitch suites — 78/78.npm run build:app— exit 0; stagedout/main/runtime/opencode/{manifest.json, darwin-arm64/opencode, darwin-x86_64/opencode}; staged arm64 binary sha25616c960ba…matches the manifest pin (the asarUnpack swap is the shared, tmux-proven path).git statusclean of binaries — cache/ ignored, manifest is the source of truth.Known limitations / risks
getPlatformKeyreturns null off-darwin, PATH fallback applies).