1import * as AWS from "alchemy/AWS";
2import * as Alchemy from "alchemy";
3import * as Cloudflare from "alchemy/Cloudflare";
4import * as Effect from "effect/Effect";
5import * as Layer from "effect/Layer";
6import ShellImageLive from "./src/shell-image.ts";
7import ShellWorker from "./src/worker.ts";
8
9/**
10 * Browser terminal → Cloudflare Worker → Durable Object → AWS Lambda MicroVM.
11 *
12 * A hand-rolled terminal SPA (served inline by the Worker) opens a WebSocket to
13 * a per-session Durable Object. The Worker provisions the session's MicroVM
14 * (assume-role control plane) and hands its endpoint + auth token to the DO,
15 * which POSTs each typed command to the VM's streaming `/exec` route and
16 * forwards stdout/stderr back to the browser as it is produced.
17 *
18 * Runs end-to-end under `alchemy dev`: the Worker + DO in local workerd, the
19 * MicroVM on the Floci emulator, connected by the cross-cloud endpoint + CA
20 * wiring. A live `alchemy deploy` needs the AWS Lambda MicroVM preview
21 * entitlement on the account.
22 */
23export default Alchemy.Stack(
24 "CloudflareMicrovmShell",
25 {
26 providers: Layer.mergeAll(AWS.providers(), Cloudflare.providers()),
27 state: Alchemy.localState(),
28 },
29 Effect.gen(function* () {
30 const worker = yield* ShellWorker;
31 return { url: worker.url.as<string>() };
32 }).pipe(Effect.provide(ShellImageLive)),
33);