feat(sandbox): pin claude and codex in the bundle so boxes update on wake

Merged
SA
1mo ago
+144-8

Why

Claude Code and Codex were npm install -g'd into the image. A box is a fork of a golden built from that image, so it keeps the version it was created with forever. Codex's own updater fails on a box too, because the npm prefix is owned by root. That's why Kunal is stuck on an old Codex.

What

Both CLIs move into the bundle, the part a box already updates on every wake:

  • bundle/assets.json: two new rows.
    • claude is the native linux-x64 binary from the stable channel (2.1.280).
    • codex is Codex's standalone linux package from its GitHub release (0.159.2).
    • Each row's sha matches the checksum the vendor publishes.
  • bundle/steps/install-agent-clis.sh:
    • Links claude into /usr/local/bin.
    • Unpacks Codex into /opt/codex/<sha>/ and links bin/codex. Codex finds its bundled rg, bwrap and code-mode host through the codex-package.json next to the binary.
    • Removes the old npm installs from existing boxes.
  • src/image.ts: removes the npm line. A fresh image still has both CLIs, because the image build runs every step.
  • bun run assets claude|codex [version]: produces the rows. With no version it takes stable (Claude) or latest (Codex).

To update from now on: bun run assets codex, then bun run build --publish locally to upload the asset, then merge. Every box installs the new version on its next boot. A box that is already running keeps the old version until it stops.

Both assets and the bundle are already on cdn.superset.sh, so the boot-twice CI job can fetch them.

Verification

  • Ran the step in a node:24-bookworm-slim container set up like an existing box, with 2.1.257 / 0.152.0 installed through npm:
    • It moved the box to 2.1.280 / 0.159.2 and removed the npm copies.
    • A second run changed nothing.
  • codex doctor as a non-root user:
    • Reports install method other, with the bundled rg found.
    • Update action is "manual", so Codex does not offer a self-update that would fail.
  • bun run lint, typecheck and bun test in packages/sandbox all pass.
  • Still owed: waking a real box after merge and checking claude --version, codex --version and boot.log.

Summary by cubic

Claude and Codex were pinned to the version a box was forked with, since both were npm install -g'd at image build and that layer never changes on a live box. Codex's own updater also failed because the npm prefix is root-owned. Both CLIs now move into the bundle, which every box updates on wake.

  • Pins claude (2.1.280 native linux-x64 binary) and codex (0.159.2 standalone package) as bundle assets with vendor-published checksums.
  • Adds an install-agent-clis bundle step that links claude and unpacks Codex into /opt/codex/<sha>/, cleaning up the old npm installs on existing boxes.
  • Removes the npm install line from the image build; fresh images still get both CLIs because the bundle step runs on boot.
  • Adds bun run assets claude|codex [version] to produce the asset rows; a version bump is one assets.json row plus a local bun run build --publish to upload the asset.
  • Any running box keeps its old version until it stops; the new version is installed on its next boot.

Summary by CodeRabbit

  • New Features
    • Sandbox environments now include Claude Code 2.1.280 and OpenAI Codex 0.159.2, alongside the existing Superset CLI.
    • Claude Code and Codex are available as command-line tools at the versions pinned to the sandbox bundle. Both are checked after installation to confirm they’re available.