Skip to content

desktop: stale cli/<version> binaries are never pruned, disk grows ~175 MB per update #53617

Description

@nedu-m

Summary

OpenCode Desktop stages a full copy of the CLI binary into cli/<version>/ on every CLI version change, but the existing stage-cleanup code never runs in packaged builds because it is gated behind a development-only flag. Each build is roughly 175 MB, so disk usage grows without bound (1.4 GB after 10 days and 8 releases in my case).

Environment

  • opencode version: 2.0.24 (desktop, latest channel)
  • OS: macOS 27.0.1 (Darwin 27.0.0, arm64)
  • Terminal: N/A (Desktop app)
  • Shell: /bin/zsh
  • Install/channel: Desktop app, channel latest (app-update.yml, provider generic)
  • Active plugins: none configured (MCP servers only)

Reproduction

  1. Install OpenCode Desktop and keep it updated across several releases (I accumulated 8 CLI builds between 2026-09-27 and 2026-10-06).
  2. Inspect ~/Library/Application Support/ai.opencode.desktop/cli/ (macOS; %APPDATA%/ai.opencode.desktop/cli on Windows, ~/.config/... on Linux).
  3. Observe one directory per CLI version, each ~171 to 180 MB, none removed.

Expected Behavior

Stale CLI builds should be pruned on startup, keeping disk use bounded to a few hundred MB instead of growing with every release.

Actual Behavior

Every version is retained forever.

180M  2.0.6   2026-09-27
171M  2.0.18  2026-09-27
171M  2.0.19  2026-09-29
172M  2.0.20  2026-09-30
172M  2.0.21  2026-10-01
172M  2.0.22  2026-10-02
171M  2.0.23  2026-10-05
176M  2.0.24  2026-10-06   <- only this one is in use
1.4G total

Additional Context

A cleanup function already exists. cleanStages in packages/desktop/src/main/service/desktop-cli.ts removes every sibling directory under the staged CLI root while keeping the current one. It is correct and the shipped 2.0.24 bundle contains it.

The problem is the only call site, in packages/desktop/src/main/service/background-service.ts:

const isolated = !app.isPackaged && process.env.OPENCODE_DESKTOP_ISOLATED_SERVER === "1"
...
if (mode === "initial" && isolated && cli.binary) yield* cleanStages(cli.binary).pipe(Effect.orDie)

isolated is !app.isPackaged && ..., so it is false in every packaged build and cleanStages never runs for released apps.

Staging, by contrast, does run in production. resolveBundledCli in the same file calls installCli(bundled, version) whenever app.isPackaged || isolated, which is always true for a released app.

So the net behavior is: production stages a new build on every version change and never prunes.

Suggested fix: invoke cleanStages under the same condition that triggers staging, app.isPackaged || isolated, so packaged builds prune on startup. This is a one line change in background-service.ts and would also self-heal existing installs on the next launch.

Related but distinct: #24255 (prunes Bun's install cache at ~/.bun/install/cache/, a different path), #29037 (cache management UI, closed as not planned), #37495 (SQLite WAL growth).

Workaround for affected users: delete all but the newest directory under cli/.

Activity

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions