Verify the packed worker-bundler at build time - #1798
Merged
Conversation
…te copy The compiled binary cannot resolve @cloudflare/worker-bundler by name, so the build stages the package dist beside the executable and native-bindings publishes its path. The required file list was duplicated across those two sides and had drifted: the build writes dist/index.bundled.js while the runtime check only looked for dist/index.js and dist/esbuild.wasm. Make the list one shared contract, assert the staged copy after compiling each target, and report a present-but-incomplete directory on stderr instead of silently declining to publish the path.
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
executor-marketing | 871690f | Commit Preview URL Branch Preview URL |
Aug 28 2026, 03:36 AM |
Contributor
Cloudflare previewTorn down — the PR is closed. |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
executor-cloud | 871690f | Aug 28 2026, 03:36 AM |
@executor-js/cli
@executor-js/config
@executor-js/execution
@executor-js/sdk
@executor-js/codemode-core
@executor-js/runtime-quickjs
@executor-js/plugin-file-secrets
@executor-js/plugin-graphql
@executor-js/plugin-keychain
@executor-js/plugin-mcp
@executor-js/plugin-onepassword
@executor-js/plugin-openapi
executor
commit: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The compiled
executorbinary — the same binary the desktop app ships as its daemon — cannot resolve@cloudflare/worker-bundlerby name.bun build --compileputs JS in bunfs, which has nonode_modules, so a bare specifier evaluated inside the binary throwsCannot find module '@cloudflare/worker-bundler'at startup. The package is also unembeddable in principle: its payload is a 13.9 MBesbuild.wasmthat is read from disk, not imported.The delivery mechanism for that is colocation:
build.tscopies the package'sdist/next to the executable, andnative-bindings.tspublishes the absolute path asEXECUTOR_WORKER_BUNDLER_DIRfor consumers to load from. That handoff was only half-built.dist/index.bundled.js(the entry consumers load, packed so it has no bare imports of its own), while the runtime check only looked fordist/index.jsanddist/esbuild.wasm.build.tsvalidates its build inputs but made no assertion about thebin/directory it produced, so a partial staging shipped a binary that was fine on the build machine and broken on the user's.For context on scope: the last consumer of this module was removed in #1476, which deliberately kept the packing for when it returns. So this is the delivery contract being made sound before something depends on it again, not a fix for a live import.
Fix
apps/cli/src/worker-bundler-artifact.tsholds the required-file list as a single shared contract, plus a puremissingWorkerBundlerFiles(dir, exists)that takes its existence probe as a parameter. The build script and the runtime bootstrap both use it, so they cannot disagree. This also corrects the list to includedist/index.bundled.js.build.tsasserts the staged copy after compiling each target: every required file present, a size floor on the packed entry, and the\0asmmagic on the wasm so a truncated or pointer-file copy cannot pass as real. A packaging slip is now a red build instead of a broken install. This follows the shape of the existing asset assertions inapps/cloud/scripts/build.mjsandapps/host-selfhost/scripts/assert-shell-asset.mjs.native-bindings.tsno longer swallows a bad copy. A directory that is present but incomplete is reported on stderr naming the missing files and telling the user how to repair it — not caught and ignored. A directory that is absent entirely stays quiet, since that is the normal non-packaged path (dev,bun run).Testing
typecheck,lint(0 warnings, 0 errors) andformatare green.Unit —
apps/cli/src/worker-bundler-artifact.test.ts, 6 tests. These are the build-time guard the contract needs: they fail if a required file is dropped from the list, specifically covering the two historical drift cases (wasm staged without the JS entries, and the unbundledindex.jsstaged withoutindex.bundled.js).Built the real daemon binary and ran it, rather than relying on file paths:
bun ./scripts/build-sidecar.tsfromapps/desktopcompiles the CLI binary and stages it — the new post-compile assertion passes against a correct staging.bun run test:smoke(smoke-sidecar.ts) drives the compiled binary end to end: daemon ready, 1Password SDK loads, andlistPets+getPetround-trip through a live OpenAPI server via MCP to QuickJS.Degradation path proven by tampering rather than asserted: with
worker-bundler/dist/esbuild.wasmmoved aside, the daemon still reaches ready and printsexecutor: the bundled Worker toolchain at <path> is incomplete (missing dist/esbuild.wasm). Features that build Workers will be unavailable; reinstall or update executor to repair it.With the file restored, it starts clean with no such message. Before this change the same tampering produced no output at all.
Platform limitation, stated plainly: all of the above was run on macOS arm64, not Windows. Desktop e2e targets were out of scope. This is a fair proxy but not identical coverage: the compile and staging path is genuinely shared — one
Bun.build({ compile: { target } })loop, and the worker-bundler copy is unconditional for every target — so the assertion and the shared contract behave the same everywhere. What macOS cannot exercise is the Windows-specific packaging leg (NSISextraResources) and, more importantly, thatnative-bindings.tsanchors ondirname(process.execPath): if a Windows install ever launches a relocatedexecutor.exe, every colocated sibling is lost at once, and this change makes that visible rather than fixing it. A cross-build (BUN_TARGET=bun-windows-x64) compiles but cannot be run or smoke-tested from macOS.