diff --git a/apps/docs/content/docs/en/workflows/blocks/pi.mdx b/apps/docs/content/docs/en/workflows/blocks/pi.mdx index dbe3a59c40a..b502d34e459 100644 --- a/apps/docs/content/docs/en/workflows/blocks/pi.mdx +++ b/apps/docs/content/docs/en/workflows/blocks/pi.mdx @@ -211,7 +211,7 @@ The one case neither layer can rescue is a *first* prompt that already exceeds t ### Create PR [#setup-cloud-pr] -Create PR runs in a sandbox image with the Pi CLI and git baked in. +Create PR runs in a sandbox image with the Pi CLI, Git, Node.js, and Bun baked in. Repository dependencies are not preinstalled; Pi can run `bun install` when a repository needs them. 1. **Enable sandbox execution.** On self-hosted Sim, set `E2B_ENABLED=true`, `E2B_API_KEY`, `E2B_PI_TEMPLATE_ID` (the Pi template id), and `NEXT_PUBLIC_E2B_ENABLED=true` (this reveals Create PR, Update PR, and Review Code in the UI). Build the template with `bun run apps/sim/scripts/build-pi-e2b-template.ts`. These modes stay hidden until `NEXT_PUBLIC_E2B_ENABLED` is set. diff --git a/apps/sim/scripts/build-pi-daytona-snapshot.ts b/apps/sim/scripts/build-pi-daytona-snapshot.ts index 99cce3bf645..e746ab92ac7 100644 --- a/apps/sim/scripts/build-pi-daytona-snapshot.ts +++ b/apps/sim/scripts/build-pi-daytona-snapshot.ts @@ -28,9 +28,10 @@ import { Daytona, Image } from '@daytona/sdk' import { getErrorMessage } from '@sim/utils/errors' import { PI_APT, + PI_BUN_VERSION_ASSERT, + PI_GLOBAL_NPM_PACKAGES, PI_NODE_MAJOR, PI_NODE_VERSION_ASSERT, - PI_NPM, PI_SANDBOX_CPU_COUNT, PI_SANDBOX_MEMORY_GB, } from '@/scripts/pi-sandbox-packages' @@ -63,7 +64,8 @@ export const piImage = Image.base(BASE_IMAGE).runCommands( // ships an older Node fails here rather than at the first agent run. `apt-get update && curl -fsSL https://deb.nodesource.com/setup_${PI_NODE_MAJOR}.x | bash - && ${APT_PREFIX} nodejs && rm -rf /var/lib/apt/lists/* && ${PI_NODE_VERSION_ASSERT}`, `apt-get update && ${APT_PREFIX} ${PI_APT.join(' ')} && rm -rf /var/lib/apt/lists/*`, - `npm install -g ${PI_NPM.join(' ')}`, + `npm install -g ${PI_GLOBAL_NPM_PACKAGES.join(' ')}`, + PI_BUN_VERSION_ASSERT, // The clone target. E2B's base ships a world-writable /code; Pi writes to // /workspace (cloud-review-tools.ts:14), so create it explicitly. 'mkdir -p /workspace' diff --git a/apps/sim/scripts/build-pi-e2b-template.ts b/apps/sim/scripts/build-pi-e2b-template.ts index 53283566ba6..9ab2fa2e52b 100644 --- a/apps/sim/scripts/build-pi-e2b-template.ts +++ b/apps/sim/scripts/build-pi-e2b-template.ts @@ -19,9 +19,10 @@ import { defaultBuildLogger, Template, waitForTimeout } from '@e2b/code-interpreter' import { PI_APT, + PI_BUN_VERSION_ASSERT, + PI_GLOBAL_NPM_PACKAGES, PI_NODE_MAJOR, PI_NODE_VERSION_ASSERT, - PI_NPM, PI_SANDBOX_CPU_COUNT, PI_SANDBOX_MEMORY_MB, } from '@/scripts/pi-sandbox-packages' @@ -38,7 +39,8 @@ const piTemplate = Template() .fromTemplate('code-interpreter-v1') .runCmd(INSTALL_NODE_COMMAND, { user: 'root' }) .aptInstall([...PI_APT]) - .npmInstall([...PI_NPM], { g: true }) + .npmInstall([...PI_GLOBAL_NPM_PACKAGES], { g: true }) + .runCmd(PI_BUN_VERSION_ASSERT, { user: 'root' }) .setStartCmd(START_COMMAND, waitForTimeout(1_000)) async function main() { diff --git a/apps/sim/scripts/pi-sandbox-packages.ts b/apps/sim/scripts/pi-sandbox-packages.ts index 90f3567219b..59d7c29632d 100644 --- a/apps/sim/scripts/pi-sandbox-packages.ts +++ b/apps/sim/scripts/pi-sandbox-packages.ts @@ -13,8 +13,12 @@ * owns the Pi block. */ -/** Exact first-party Pi versions mirrored from bun.lock — image builds run npm independently. */ -export const PI_NPM = [ +/** Bun version mirrored from the root packageManager field. */ +export const PI_BUN_VERSION = '1.3.13' + +/** Exact global package versions mirrored from package.json and bun.lock. */ +export const PI_GLOBAL_NPM_PACKAGES = [ + `bun@${PI_BUN_VERSION}`, '@earendil-works/pi-coding-agent@0.80.10', '@earendil-works/pi-agent-core@0.80.10', '@earendil-works/pi-ai@0.80.10', @@ -51,6 +55,9 @@ export const PI_NODE_MAJOR = 22 export const PI_NODE_VERSION_ASSERT = 'node -e "const [major, minor] = process.versions.node.split(\'.\').map(Number); if (major < 22 || (major === 22 && minor < 19)) process.exit(1)"' +/** Fails the build loudly if the sandbox does not contain the repository's Bun version. */ +export const PI_BUN_VERSION_ASSERT = `test "$(bun --version)" = "${PI_BUN_VERSION}"` + /** * The review tools run `python3 /workspace/sim-review-tools.py` * (`cloud-review-tools.ts:15`). E2B's `code-interpreter-v1` base ships Python, so