feat(sandbox): pin claude and codex in the bundle so boxes update on wake
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.claudeis the native linux-x64 binary from the stable channel (2.1.280).codexis 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
claudeinto/usr/local/bin. - Unpacks Codex into
/opt/codex/<sha>/and linksbin/codex. Codex finds its bundledrg,bwrapand code-mode host through thecodex-package.jsonnext to the binary. - Removes the old npm installs from existing boxes.
- Links
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-slimcontainer 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 doctoras a non-root user:- Reports install method
other, with the bundledrgfound. - Update action is "manual", so Codex does not offer a self-update that would fail.
- Reports install method
bun run lint,typecheckandbun testinpackages/sandboxall pass.- Still owed: waking a real box after merge and checking
claude --version,codex --versionandboot.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) andcodex(0.159.2 standalone package) as bundle assets with vendor-published checksums. - Adds an
install-agent-clisbundle step that linksclaudeand 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 oneassets.jsonrow plus a localbun run build --publishto 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.