Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ PLAN.md
# tracked code and must not be caught by this rule.
/artifacts/

# Generated bundle for the worker backend's ShellWorker. Built
# by packages/computer/src/backends/worker/script/build-bundle.mjs
# on prepare / pretest / pretypecheck.
packages/computer/src/backends/worker/generated-bundle.ts
# Generated shell-module groups for the worker backend's
# ShellWorker (core plus one file per optional feature). Built by
# packages/computer/src/backends/worker/script/build-bundle.mjs on
# prepare / pretest / pretypecheck.
packages/computer/src/backends/worker/generated/

# SEA binary destinations populated at publish time from
# artifacts/computerd/ via the build-bin step. The @cloudflare/computer
Expand Down
10 changes: 9 additions & 1 deletion docs/12_worker_backend.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,15 @@ network-bound `git` subcommands do. See
`@cloudflare/computer/backends/worker` as `SHELL_MODULES`
(a record of module name → source covering the entry plus
every code-split chunk); the backend hands the whole record
to the Loader callback itself.
to the Loader callback itself. `SHELL_MODULES` is assembled
from per-feature groups — a core group plus one per optional
command (`curl`, `html-to-markdown`, `python`, `sqlite`,
`js-exec`), each published on its own
`@cloudflare/computer/shell/<feature>` subpath. Aliasing a
subpath to `@cloudflare/computer/empty` in `wrangler.jsonc`
drops that feature's chunks — and its heavy dependency (undici
for `curl`, domino for `html-to-markdown`) — from the uploaded
Worker.

The DO's backend wiring fits in three lines:

Expand Down
31 changes: 31 additions & 0 deletions examples/worker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,37 @@ curl -X POST http://127.0.0.1:8787/c/demo/exec \
-d '{"command":"cat hello.txt && wc -l hello.txt","encoding":"utf8"}'
```

## Trimming the shell bundle

`SHELL_MODULES` carries every just-bash command, and a handful of
optional ones dominate the upload: `curl` drags in undici
(~620 KB) and `html-to-markdown` drags in domino (~555 KB), with
`python`, `sqlite`, and `js-exec` behind them. Each optional
command's chunks live behind their own
`@cloudflare/computer/shell/<feature>` subpath, so a Worker that
doesn't need one can drop it at build time with a wrangler
`alias`:

```jsonc
"alias": {
"@cloudflare/computer/shell/curl": "@cloudflare/computer/empty",
"@cloudflare/computer/shell/html-to-markdown": "@cloudflare/computer/empty"
}
```

Aliasing a feature to `@cloudflare/computer/empty` swaps its
module group for an empty table, so its chunks — the heavy
dependency included — never enter the uploaded Worker. The
available features are `curl`, `html-to-markdown`, `python`,
`sqlite`, and `js-exec`. The command still parses; invoking one
after its chunk is gone fails at runtime with a "module not
found". Leaving the alias out ships the whole shell.

This example drops `curl` and `html-to-markdown`, which takes the
Worker upload from ~5,956 KiB to ~4,774 KiB raw (~1,344 KiB to
~1,071 KiB gzip). Remove the `alias` entries in
[`wrangler.jsonc`](wrangler.jsonc) to ship them.

## Layout

```
Expand Down
16 changes: 16 additions & 0 deletions examples/worker/wrangler.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,22 @@
"compatibility_date": "2026-05-26",
"compatibility_flags": ["nodejs_compat", "experimental"],

// Drop optional just-bash commands this example doesn't use.
// @cloudflare/computer bundles each optional command's chunks
// (curl's undici, html-to-markdown's domino, python, sqlite,
// js-exec) behind its own @cloudflare/computer/shell/<feature>
// subpath. Aliasing a feature to @cloudflare/computer/empty
// swaps it for an empty module table, so its chunks — including
// the heavy dependency — never enter the uploaded Worker.
// Dropping curl and html-to-markdown alone removes ~1.2 MB raw
// (~620 KB undici + ~555 KB domino). The commands still parse;
// invoking one fails at runtime once its chunk is gone. Remove
// an entry to ship that feature.
"alias": {
"@cloudflare/computer/shell/curl": "@cloudflare/computer/empty",
"@cloudflare/computer/shell/html-to-markdown": "@cloudflare/computer/empty"
},

// Worker Loader binding. The DO calls env.LOADER.get(id, ...)
// to mint a Dynamic Worker that the WorkerBackend then
// dispatches shell.exec into.
Expand Down
23 changes: 23 additions & 0 deletions packages/computer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,29 @@ export class ContainerExample extends withWorkspace(
) {}
```

The worker backend bundles every just-bash command into
`SHELL_MODULES`, and a few optional ones dominate the upload —
`curl` carries undici (~620 KB) and `html-to-markdown` carries
domino (~555 KB). Each optional command's chunks sit behind their
own `@cloudflare/computer/shell/<feature>` subpath, so a Worker
that doesn't need one drops it at build time by aliasing the
subpath to `@cloudflare/computer/empty` in `wrangler.jsonc`:

```jsonc
"alias": {
"@cloudflare/computer/shell/curl": "@cloudflare/computer/empty",
"@cloudflare/computer/shell/html-to-markdown": "@cloudflare/computer/empty"
}
```

That swaps the feature's module group for an empty table, so its
chunks — the heavy dependency included — never reach the uploaded
Worker; dropping both above removes ~1.2 MB raw. The features are
`curl`, `html-to-markdown`, `python`, `sqlite`, and `js-exec`.
The command still parses, but invoking one after its chunk is
gone fails at runtime with a "module not found". Leaving the
alias out ships the whole shell. See `examples/worker/`.

Filesystem only — no backend, no shell:

```ts
Expand Down
28 changes: 28 additions & 0 deletions packages/computer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,34 @@
"types": "./dist/backends/worker/index.d.ts",
"import": "./dist/backends/worker/index.js"
},
"./shell/core": {
"types": "./dist/backends/worker/shell/core.d.ts",
"default": "./dist/backends/worker/shell/core.js"
},
"./shell/curl": {
"types": "./dist/backends/worker/shell/curl.d.ts",
"default": "./dist/backends/worker/shell/curl.js"
},
"./shell/html-to-markdown": {
"types": "./dist/backends/worker/shell/html-to-markdown.d.ts",
"default": "./dist/backends/worker/shell/html-to-markdown.js"
},
"./shell/python": {
"types": "./dist/backends/worker/shell/python.d.ts",
"default": "./dist/backends/worker/shell/python.js"
},
"./shell/sqlite": {
"types": "./dist/backends/worker/shell/sqlite.d.ts",
"default": "./dist/backends/worker/shell/sqlite.js"
},
"./shell/js-exec": {
"types": "./dist/backends/worker/shell/js-exec.d.ts",
"default": "./dist/backends/worker/shell/js-exec.js"
},
"./empty": {
"types": "./dist/backends/worker/empty.d.ts",
"default": "./dist/backends/worker/empty.js"
},
"./observe/cloudflare": {
"types": "./dist/observe/cloudflare.d.ts",
"import": "./dist/observe/cloudflare.js"
Expand Down
19 changes: 19 additions & 0 deletions packages/computer/rolldown.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,18 @@ export default defineConfig({
"tools/index": "src/tools/index.ts",
"backends/container/index": "src/backends/container/index.ts",
"backends/worker/index": "src/backends/worker/index.ts",
// The shell-module groups build-bundle.mjs emits, plus the
// empty stub. Each is its own entry so it lands at the dist
// path the ./shell/* and ./empty package exports point at;
// shell-modules.ts imports them by subpath (kept external
// below) so a consumer can alias any of them out.
"backends/worker/shell/core": "src/backends/worker/generated/core.ts",
"backends/worker/shell/curl": "src/backends/worker/generated/curl.ts",
"backends/worker/shell/html-to-markdown": "src/backends/worker/generated/html-to-markdown.ts",
"backends/worker/shell/python": "src/backends/worker/generated/python.ts",
"backends/worker/shell/sqlite": "src/backends/worker/generated/sqlite.ts",
"backends/worker/shell/js-exec": "src/backends/worker/generated/js-exec.ts",
"backends/worker/empty": "src/backends/worker/empty.ts",
"observe/cloudflare": "src/observe/cloudflare.ts",
},
external: [
Expand All @@ -41,6 +53,13 @@ export default defineConfig({
"isomorphic-git",
/^isomorphic-git\//,
"just-bash",
// shell-modules.ts imports the generated groups by their
// published subpath so a consumer's wrangler `alias` can swap
// any of them for @cloudflare/computer/empty. Keep the
// specifiers intact in the emitted bundle rather than inlining
// the group here; each group is built as its own entry above.
/^@cloudflare\/computer\/shell\//,
"@cloudflare/computer/empty",
"node:crypto",
"node:events",
],
Expand Down
6 changes: 6 additions & 0 deletions packages/computer/src/backends/worker/empty.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Empty shell-module group. Alias a
// @cloudflare/computer/shell/<feature> subpath to this module
// (published as @cloudflare/computer/empty) in wrangler.jsonc to
// drop that feature's chunks from the uploaded Worker.

export default Object.freeze({}) as Readonly<Record<string, { js: string }>>;
58 changes: 0 additions & 58 deletions packages/computer/src/backends/worker/generated-bundle.test.ts

This file was deleted.

2 changes: 1 addition & 1 deletion packages/computer/src/backends/worker/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export { type WorkspaceFs, WorkspaceFsAdapter } from "./adapter.js";
export { type ArtifactsCommandHost, defineArtifactsCommand } from "./artifacts-command.js";
export { type AssetsCommandHost, defineAssetsCommand } from "./assets-command.js";
export { type ExecInput, ShellWorker, type ShellWorkerOptions } from "./entrypoint.js";
export { SHELL_MODULES } from "./generated-bundle.js";
export { defineGitCommand, type GitCommandHost } from "./git-command.js";
export { SHELL_RUNTIME_MODULES } from "./runtime-modules.js";
export { SHELL_MODULES } from "./shell-modules.js";
export { WorkerBackend, type WorkerBackendOptions, type WorkerShellFetcher } from "./worker.js";
Loading
Loading