Skip to content

feat(setup): bundle the OpenCode CLI as a managed runtime binary #994

Closed

Description

@Juliusolsson05

Motivation

A fresh install of Agent Code cannot use OpenCode panes unless the user has the opencode CLI on PATH. Today the binary lives only at ~/.opencode/bin on this dev machine — and a Finder-launched app never sees that PATH (no shell rc is sourced), so OpenCode panes fail on the exact machines that need them most. Adjacent to the state-lock fallout observed in #993 (OpenCode panes erroring in the installed app).

The bundling pipeline already exists for three tools (tmux #120, mitmdump #119, cloudflared): third_party/<tool>/manifest.json is the source of truth, scripts/runtime-tools/fetch-*.mjs + verify-*.mjs reproduce the cache, runtime:prepare:mac gates release builds, and src/main/setup/runtimeTools.ts resolves bundled binaries ahead of PATH. OpenCode is the natural fourth tool.

Intended behavior

  • third_party/opencode/ pins upstream anomalyco/opencode v1.18.31 CLI release assets (MIT): opencode-darwin-arm64.zip and opencode-darwin-x64-baseline.zip, each a single opencode binary inside the archive. Binaries are NEVER committed; the manifest's per-arch archive sha256 + bytes + extracted binarySha256 are the source of truth.
  • npm run runtime:fetch:opencode downloads, hash-verifies, extracts, and caches under third_party/opencode/cache/<platform>/opencode; verify-opencode.mjs --strict re-checks content hashes, native --version smoke, and a no-Homebrew otool -L linkage gate. runtime:prepare:mac runs both.
  • copy-packaged-resources.mjs stages the tool into out/main/runtime/opencode/ (already covered by the asarUnpack glob).
  • runtimeTools.ts grows an opencode BundledToolId with the tmux-shaped resolver (single binary, find + chmod + real-exec probe).
  • The setup gate's opencode row reports source: 'bundled' and is never install-prompted when the bundled artifact is present (same precedence comment as mitmdump: bundled > PATH).
  • Spawn-path resolution: getToolPath('opencode', ...) returns the bundled binary ahead of setup-cached/PATH lookups, EXCEPT a valid manual override still wins (the user's explicit word beats every probe). This covers transcriptEngine.ts import/export/profile call sites without touching each one.
  • Update-flow awareness: opencode remains intentionally outside the CLI auto-update surface; when bundled, its version moves with app updates, not npm. The type-level note in cliUpdate.ts is updated to record that.

Acceptance criteria

  • npm run runtime:fetch:opencode -- --all + verify-opencode.mjs --strict pass against the pinned hashes (v1.18.31: arm64 archive caf7f31f…, x64-baseline archive ef4dcd35…)
  • runtime:prepare:mac includes opencode and stays green
  • Setup gate shows OpenCode as found/bundled with no install prompt when the artifact ships; PATH fallback still works when it does not
  • getToolPath('opencode') resolves the bundled path with cached/PATH as fallback; a valid manual override beats bundled
  • npm run build:app stages out/main/runtime/opencode/<platform>/opencode; packaged-app resolution lands on the asar-unpacked path
  • No binary or archive enters git (cache/ ignored)

Environment

macOS; upstream assets inspected from anomalyco/opencode release v1.18.31 (darwin CLI zips: single opencode binary, --version prints bare semver, links only system dylibs).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions