Skip to content

Commit 78555a5

Browse files
committed
docs: align the docs with the unified execution backend
Update the prose and interface listings for the execution changes: the shell wire field is source rather than command and carries an optional structured input; the exit event carries an optional structured result; the workspace no longer takes a waitUntil hook and a JavaScript run stays alive while its event stream is consumed; and the host command driver is the CommandExecutor, with encoding and result accumulation living in the runtime. Touches the capnweb wire contract, the runtime lifecycle and worker backend notes, the isolate JavaScript guide, the runtime migration table, and the computer and example READMEs.
1 parent 2749897 commit 78555a5

8 files changed

Lines changed: 39 additions & 29 deletions

File tree

docs/08_capnweb_interface.md

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -162,15 +162,19 @@ See SUMMARY §0 'Investigation notes' (commits dc692c0, c95c74d,
162162

163163
```ts
164164
interface ShellRPC {
165-
// Spawn a command. Returns a handle whose `events` stream
166-
// yields stdout / stderr / exit frames. The stream is the
167-
// single source of truth — there is no buffered-return
168-
// variant. The handle's id can be passed to getExec to
169-
// reattach after a reconnect.
165+
// Spawn an execution. `source` is a shell command line for a
166+
// command backend, or module source for a callable backend.
167+
// Returns a handle whose `events` stream yields stdout / stderr /
168+
// exit frames. The stream is the single source of truth — there
169+
// is no buffered-return variant. The handle's id can be passed to
170+
// getExec to reattach after a reconnect. `input` carries a
171+
// structured value for a callable backend; command backends
172+
// ignore it.
170173
exec(input: {
171-
command: string;
172-
cwd?: string;
173-
id?: string;
174+
source: string;
175+
cwd?: string;
176+
id?: string;
177+
input?: unknown;
174178
}): Promise<{ id: string; events: ReadableStream<ExecEvent> }>;
175179

176180
// Reattach to an in-flight or recently-completed exec by id.
@@ -195,9 +199,15 @@ interface ShellRPC {
195199
type ExecEvent =
196200
| { id: string; seq: number; name: "stdout"; value: Uint8Array }
197201
| { id: string; seq: number; name: "stderr"; value: Uint8Array }
198-
| { id: string; seq: number; name: "exit"; value: number };
202+
| { id: string; seq: number; name: "exit"; value: number; result?: unknown };
199203
```
200204

205+
The `exit` frame carries the process exit code and, for a callable
206+
backend that ran to a zero exit, the structured return value on
207+
`result`. The value and the exit code settle together, so a single
208+
terminal frame carries both rather than splitting them across two
209+
frames. Command backends never set `result`.
210+
201211
All payloads on the wire are binary. The host-side `Workspace.runtime`
202212
converts to `string` when the caller passes `encoding: "utf8"`. Every
203213
event carries a monotonic `seq` (per exec id) so callers can resume

docs/11_lifecycle.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ Two things have to change for capnweb + hibernation to work:
340340
is dropped by the idempotent apply path. No attachment write is
341341
required.
342342
- **Exec streams: store `{ [id]: seq }` per in-flight exec.**
343-
The `WorkspaceShell` driver inside the DO is the only place
343+
The `CommandExecutor` driver inside the DO is the only place
344344
that knows where the consumer got to in the event stream.
345345
Every time it surfaces an event to the caller (or on some
346346
reasonable debounce) it has to update the attachment so the

docs/12_worker_backend.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,9 @@ the same.
138138
`BackendHandle.sync` is `"none"`. With a single authoritative
139139
store there's nothing to ship or fetch; `Workspace.push` and
140140
`Workspace.pull` short-circuit on the bit and the reconcile pass
141-
on connect is skipped. The shell exec bracket still calls them so
142-
the surface stays uniform — every `ExecResult.pushed`, `pulled`,
143-
and `skipped` is empty.
141+
on connect is skipped. The exec sync bracket still calls them so
142+
the surface stays uniform — the pushed, pulled, and skipped counts
143+
on the runtime result are empty.
144144

145145
## Event stream framing
146146

@@ -152,9 +152,9 @@ shape across the isolate hop.
152152

153153
The backend decodes the frames into structured `ExecEvent` values
154154
and re-encodes string payloads (`stdout` / `stderr`) into
155-
`Uint8Array` so the existing `WorkspaceShell` utf8 decoder
156-
transforms in `packages/computer/src/shell.ts` see the shape
157-
they already handle.
155+
`Uint8Array` so the runtime's utf8 decoder transforms, which
156+
accumulate the result from raw events, see the shape they already
157+
handle.
158158

159159
## Fetcher factory escape hatch
160160

docs/17_isolate_javascript.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import { WorkerJavaScriptBackend } from "@cloudflare/computer/backends/worker-ja
88

99
const workspace = new Workspace({
1010
storage: ctx.storage,
11-
waitUntil: ctx.waitUntil.bind(ctx),
1211
backends: [
1312
new WorkerJavaScriptBackend({
1413
loader: env.LOADER,
@@ -52,7 +51,7 @@ const result = await handle.result();
5251

5352
The source is a real ES module. Static imports, literal dynamic imports, and top-level await are supported. If the module default-exports a function, Workspace invokes it with `options.input`. Otherwise module evaluation completes with a `null` structured result.
5453

55-
`waitUntil` is required for this backend. `runtime.exec()` returns before the Dynamic Worker finishes, so the host must attach completion to the Durable Object event lifetime. Construction fails when a module backend connects without this hook.
54+
`runtime.exec()` returns before the Dynamic Worker finishes: the run keeps advancing while its event stream is consumed and the host call into the Dynamic Worker stays in flight. That pending work keeps the Durable Object resident on its own. A run whose handle is returned but never read can be evicted once the object goes idle; drain the event stream (or `result()`) to keep the run alive, and schedule an alarm through `ctx.storage.setAlarm()` for work that must survive eviction.
5655

5756
## Durable relative imports
5857

docs/18_runtime_migration.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ This change is a breaking preview-API migration. Public execution now uses one r
1212
| `workspace.shell.dispose(id, options)` | `workspace.runtime.disposeExec(id, options)` |
1313
| `workspace.code` / script execution | `workspace.runtime.exec(source, { backend: "worker-javascript", input })` |
1414

15-
`WorkspaceShell` still exists internally to implement command backends. It is not a public Workspace property.
15+
`CommandExecutor` exists internally to implement command backends. It is not a public Workspace property.
1616

1717
## Default backend IDs
1818

examples/worker-javascript/README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,10 @@ client ─► Worker /c/<name>/{file,exec}
5050
store (the DO's SQLite); push and pull short-circuit.
5151
`pushed` / `pulled` are always zero.
5252

53-
The backend requires `waitUntil`, so the DO passes
54-
`ctx.waitUntil.bind(ctx)` into the Workspace options. The DO is a
55-
thin host; the Dynamic Worker lifecycle is the loader's problem.
53+
The DO is a thin host; the Dynamic Worker lifecycle is the loader's
54+
problem. A run keeps advancing while its event stream is consumed, so
55+
the request that drains the handle holds the object resident until the
56+
run finishes.
5657

5758
## Paths
5859

examples/worker-shell/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ client ─► Worker /c/<name>/{file,exec}
5757
and one workspace per DO is the natural boundary.
5858
5. `BackendHandle.sync` is `"none"`. There's a single
5959
authoritative store (the DO's SQLite); push and pull
60-
short-circuit. `ExecResult.pushed` / `pulled` are always
61-
zero.
60+
short-circuit. The runtime result's `pushed` / `pulled`
61+
counts are always zero.
6262

6363
The DO is a thin host. There's no Dockerfile; the Dynamic Worker
6464
lifecycle is the loader's problem.

packages/computer/README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@ Three backends ship today on tree-shakeable subpaths:
3434
`node:fs/promises`, and trusted `ws:git` / `ws:artifacts` modules. See
3535
[`docs/17_isolate_javascript.md`](../../docs/17_isolate_javascript.md).
3636

37-
The worker-JavaScript backend runs after `runtime.exec()` returns. Pass
38-
`waitUntil: ctx.waitUntil.bind(ctx)` to `Workspace` so completion remains
39-
attached to the Durable Object event. The backend refuses to connect without
40-
this lifecycle hook. It admits one execution at a time by default and bounds
41-
completed execution retention by time and count.
37+
The worker-JavaScript backend runs after `runtime.exec()` returns. The run
38+
keeps advancing while its event stream is consumed, which holds the Durable
39+
Object resident; a handle that is never read can be evicted once the object
40+
goes idle. It admits one execution at a time by default and bounds completed
41+
execution retention by time and count.
4242

4343
A backend can declare `sync: "none"` on the handle it returns to
4444
opt out of the push/pull bracket entirely — the worker backend

0 commit comments

Comments
 (0)