@lunora/container 1.0.0-alpha.7 → 1.0.0-alpha.71

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +24 -5
  2. package/dist/bridge.d.mts +43 -33
  3. package/dist/bridge.d.ts +43 -33
  4. package/dist/bridge.mjs +1 -79
  5. package/dist/do/index.d.mts +353 -91
  6. package/dist/do/index.d.ts +353 -91
  7. package/dist/do/index.mjs +1 -328
  8. package/dist/index.d.mts +584 -153
  9. package/dist/index.d.ts +584 -153
  10. package/dist/index.mjs +1 -2
  11. package/dist/otel.d.mts +217 -0
  12. package/dist/otel.d.ts +217 -0
  13. package/dist/otel.mjs +1 -0
  14. package/dist/packem_shared/CONTAINER_EXEC_PATH-7Hcd6-zs.mjs +1 -0
  15. package/dist/packem_shared/CUSTOM_INSTANCE_MIN_MEMORY_MIB_PER_VCPU-D8Hy9Rxv.mjs +1 -0
  16. package/dist/packem_shared/ContainerProxy-CfXjS6ZI.mjs +27 -0
  17. package/dist/packem_shared/DURABLE_OBJECT_POLICY_FORBIDDEN_KEYS-gYi8RBUO.mjs +1 -0
  18. package/dist/packem_shared/LunoraSandboxContainer-DPnhJUu1.mjs +1 -0
  19. package/dist/packem_shared/abort-deadline-jwbW9Ala.mjs +1 -0
  20. package/dist/packem_shared/createContainerContext-p6QxWjsm.mjs +1 -0
  21. package/dist/packem_shared/exec-BYLW70sR.mjs +1 -0
  22. package/dist/packem_shared/jurisdiction-DtE9s70w.mjs +1 -0
  23. package/dist/packem_shared/jurisdiction.d-BwpauCoI.d.mts +471 -0
  24. package/dist/packem_shared/jurisdiction.d-BwpauCoI.d.ts +471 -0
  25. package/dist/packem_shared/sampling-D21rpu_s.mjs +1 -0
  26. package/dist/packem_shared/sandbox-types.d-DXlHnsEg.d.mts +110 -0
  27. package/dist/packem_shared/sandbox-types.d-DXlHnsEg.d.ts +110 -0
  28. package/dist/sandbox/index.d.mts +66 -0
  29. package/dist/sandbox/index.d.ts +66 -0
  30. package/dist/sandbox/index.mjs +1 -0
  31. package/package.json +11 -2
  32. package/dist/packem_shared/ContainerProxy-DWqUX_re.mjs +0 -1474
  33. package/dist/packem_shared/containerBindingName-D5KTrblH.mjs +0 -218
  34. package/dist/packem_shared/createContainerContext-Cibbh7AE.mjs +0 -228
  35. package/dist/packem_shared/types.d-BlNwNY44.d.mts +0 -250
  36. package/dist/packem_shared/types.d-BlNwNY44.d.ts +0 -250
package/dist/index.d.ts CHANGED
@@ -1,17 +1,131 @@
1
- import { C as ContainerConfig, a as ContainerDefinition, b as ContainerImageSource, N as NormalizedContainerImage } from "./packem_shared/types.d-BlNwNY44.js";
2
- export type { B as BuildImageSource, c as ContainerInstanceType, d as ContainerReadinessCheck, e as ContainerRollout, f as CustomContainerInstanceType, g as NamedContainerInstanceType, R as RegistryImageSource } from "./packem_shared/types.d-BlNwNY44.js";
3
- /** Options for explicitly starting an instance (mirrors `@cloudflare/containers`). */
1
+ import { a as ContainerSpawnOptions, b as ContainerSnapshot, c as ContainerProcess, d as ContainerRuntimeInstanceType, e as ContainerSpawnRequest, S as SpawnResult, D as DurableObjectJurisdiction, f as DefaultScheduledContainerConfig, g as DurableObjectScheduledContainerConfig, C as ContainerDefinition, h as ContainerImageSource, N as NormalizedContainerImage } from "./packem_shared/jurisdiction.d-BwpauCoI.js";
2
+ export type { B as BuildImageSource, i as ContainerBackupStorage, j as ContainerConfig, k as ContainerConfigBase, l as ContainerInstanceType, m as ContainerNamedImageSource, n as ContainerReadinessCheck, o as ContainerRollout, p as CustomContainerInstanceType, q as NamedContainerInstanceType, R as RegistryImageSource } from "./packem_shared/jurisdiction.d-BwpauCoI.js";
3
+ import { b as ContainerBackupOptions, C as ContainerFileOptions, c as ContainerMountRequest, a as ContainerFileContent, d as ContainerSandboxControls } from "./packem_shared/sandbox-types.d-DXlHnsEg.js";
4
+ export type { e as ContainerFiles, f as ContainerMountCredentials } from "./packem_shared/sandbox-types.d-DXlHnsEg.js";
5
+ import { DirectoryBackupRecord, S3MountInspection, SandboxDirectoryEntry, SandboxFileStat } from '@cloudflare/sandbox';
6
+ export type { DirectoryBackupRecord, S3MountInspection, SandboxDirectoryEntry, SandboxFileStat } from '@cloudflare/sandbox';
7
+ /**
8
+ * Per-call options for `ContainerHandle.exec`.
9
+ */
10
+ interface ContainerExecOptions {
11
+ /** Arguments passed to `command`, unshelled — the container must not concatenate them into a shell string. */
12
+ args?: ReadonlyArray<string>;
13
+ /** Working directory for the command, relative to the container's own root. */
14
+ cwd?: string;
15
+ /** Extra environment for this command only, merged over the container's env. */
16
+ env?: Readonly<Record<string, string>>;
17
+ /**
18
+ * Cap on the response body, in bytes. The whole `{code,stdout,stderr}`
19
+ * document has to be held in memory to be parsed, and a build box or job
20
+ * runner routinely writes tens of megabytes — a single unbounded `exec`
21
+ * that returns more than the isolate's memory limit kills the isolate and
22
+ * every other in-flight request sharing it, not just this call. So a
23
+ * runner that overruns the cap fails the call loudly rather than taking
24
+ * the shard down with it; raise this (or have the runner cap its own
25
+ * output) when a command legitimately produces more.
26
+ *
27
+ * Default {@link DEFAULT_EXEC_MAX_OUTPUT_BYTES} (1MB).
28
+ */
29
+ maxOutputBytes?: number;
30
+ /**
31
+ * Abort the call. Composed with {@link ContainerExecOptions.timeoutMs} when
32
+ * both are given — whichever fires first wins.
33
+ */
34
+ signal?: AbortSignal;
35
+ /**
36
+ * Give up after this many ms. Covers the whole call — the request *and*
37
+ * reading the response body, since `fetch` resolves on headers and a
38
+ * runner that answers `200` and then stalls mid-body would otherwise sit
39
+ * past the deadline. Sent to the container as well, so a well-behaved
40
+ * runner can kill the process rather than leak it when the caller walks
41
+ * away.
42
+ */
43
+ timeoutMs?: number;
44
+ }
45
+ /**
46
+ * The outcome of a `ContainerHandle.exec` call.
47
+ *
48
+ * A non-zero `code` is **not** an error: a command that ran and failed is a
49
+ * result, and the caller decides what to do with it. Only a failure to *run*
50
+ * the command — transport, a non-2xx from the runner, an unparseable body —
51
+ * throws.
52
+ */
53
+ interface ContainerExecResult {
54
+ /** Process exit code. Non-zero means the command ran and failed. */
55
+ code: number;
56
+ /** Everything the command wrote to stderr. */
57
+ stderr: string;
58
+ /** Everything the command wrote to stdout. */
59
+ stdout: string;
60
+ }
61
+ /**
62
+ * The route `ContainerHandle.exec` POSTs to. Namespaced under
63
+ * `/__lunora/` so it cannot collide with an application route the container
64
+ * already serves — the previous ad-hoc convention was a bare `/exec`, which an
65
+ * app could plausibly own for its own purposes.
66
+ *
67
+ * Exported because it is a *contract*, not an implementation detail: a
68
+ * container image has to serve exactly this route, and `@lunora/agent`'s
69
+ * human-in-the-loop gate has to recognise it. Both had it hard-coded as a
70
+ * second literal, which drifts silently — and on the gate's side, drift
71
+ * un-gates model-chosen command execution.
72
+ */
73
+ declare const CONTAINER_EXEC_PATH = "/__lunora/exec";
74
+ /** Options for `handle.terminal()`. */
75
+ interface ContainerTerminalOptions extends Omit<ContainerSpawnOptions, "pty" | "signal" | "stdin"> {
76
+ /** Initial size. Defaults to the request's `?cols=&rows=` search params, else `80×24`. */
77
+ cols?: number;
78
+ /** The program to run. Defaults to `"sh"`. */
79
+ command?: string;
80
+ /** Initial size. Defaults to the request's `?cols=&rows=` search params, else `80×24`. */
81
+ rows?: number;
82
+ }
83
+ /**
84
+ * Options for explicitly starting an instance (mirrors `@cloudflare/containers`).
85
+ */
4
86
  interface ContainerStartOptions {
5
87
  /** Override outbound internet access for this start. */
6
88
  enableInternet?: boolean;
7
89
  /** Override the container entrypoint. */
8
90
  entrypoint?: string[];
9
- /** Per-instance environment, merged over the definition's `env`/secrets. */
91
+ /**
92
+ * Per-instance environment. REPLACES the definition's `env`, `secrets` and
93
+ * `secretsStore` (the Secrets Store is not read), so pass every variable the
94
+ * container needs. It is persisted for the instance: every later start —
95
+ * including the implicit restart a `fetch`/`exec` triggers after the
96
+ * container slept, crashed or hit `hardTimeout` — uses it, until
97
+ * `destroy()`. The override cannot change while the container is running
98
+ * or still starting: a start whose `envVars` differ is rejected with
99
+ * `CONFLICT` in either case, so `stop()` it (and let any start in flight
100
+ * finish) first. So
101
+ * `start({ envVars: {} })` is a sandbox without the declared credentials,
102
+ * for as long as the instance exists.
103
+ */
10
104
  envVars?: Record<string, string>;
105
+ /**
106
+ * The image to start (`schedulingPolicy: "durable_object"` only): a key of
107
+ * the definition's `images`, or a Cloudflare-managed `"cloudflare/…"` image.
108
+ * Persisted like `envVars`, so the implicit restart after a sleep boots the
109
+ * same image. Mutually exclusive with `snapshot`.
110
+ */
111
+ image?: string;
112
+ /**
113
+ * The instance size (`schedulingPolicy: "durable_object"` only). Persisted
114
+ * with `image`; defaults to the definition's `instanceType`, else `"lite"`.
115
+ */
116
+ instanceType?: ContainerRuntimeInstanceType;
11
117
  /** Metadata labels attached for metrics/observability. */
12
118
  labels?: Record<string, string>;
119
+ /**
120
+ * Restore a filesystem saved by `snapshot()` instead of starting from an
121
+ * image (`schedulingPolicy: "durable_object"` only). Applies to this start
122
+ * only — a later restart after a sleep boots the image again.
123
+ */
124
+ snapshot?: ContainerSnapshot;
13
125
  }
14
- /** A container instance's runtime state, as returned by `getState()`. Structural — the platform adds fields over time. */
126
+ /**
127
+ * A container instance's runtime state, as returned by `getState()`. Structural — the platform adds fields over time.
128
+ */
15
129
  interface ContainerInstanceState {
16
130
  [key: string]: unknown;
17
131
  /** Process exit code, present once the instance has `stopped_with_code`. */
@@ -28,6 +142,34 @@ interface ContainerStubLike {
28
142
  destroy?: () => Promise<void>;
29
143
  fetch: (input: Request) => Promise<Response>;
30
144
  getState?: () => Promise<ContainerInstanceState>;
145
+ lunoraBackup?: (directory: string, options?: ContainerBackupOptions) => Promise<DirectoryBackupRecord>;
146
+ lunoraDeleteBackup?: (backup: DirectoryBackupRecord) => Promise<void>;
147
+ /** The container DO's exec entry (`LunoraContainer.lunoraExec`), the only way a request reaches `/__lunora/exec`. */
148
+ lunoraExec?: (request: Request) => Promise<Response>;
149
+ lunoraInspectMount?: (path: string) => Promise<S3MountInspection>;
150
+ lunoraMkdir?: (path: string, options?: ContainerFileOptions & {
151
+ recursive?: boolean;
152
+ }) => Promise<void>;
153
+ lunoraMount?: (request: ContainerMountRequest) => Promise<void>;
154
+ lunoraReadDirectory?: (path: string, options?: ContainerFileOptions) => Promise<SandboxDirectoryEntry[]>;
155
+ lunoraReadFile?: (path: string, options?: ContainerFileOptions) => Promise<Response>;
156
+ lunoraRemove?: (path: string, options?: ContainerFileOptions & {
157
+ force?: boolean;
158
+ recursive?: boolean;
159
+ }) => Promise<void>;
160
+ lunoraRename?: (source: string, destination: string, options?: ContainerFileOptions) => Promise<void>;
161
+ lunoraRestore?: (backup: DirectoryBackupRecord, options?: {
162
+ directory?: string;
163
+ }) => Promise<void>;
164
+ /** The container DO's snapshot entry (`LunoraContainer.lunoraSnapshot`). */
165
+ lunoraSnapshot?: (options?: {
166
+ name?: string;
167
+ }) => Promise<ContainerSnapshot>;
168
+ /** The container DO's streaming-exec entry (`LunoraContainer.lunoraSpawn`). */
169
+ lunoraSpawn?: (request: ContainerSpawnRequest) => Promise<SpawnResult>;
170
+ lunoraStat?: (path: string, options?: ContainerFileOptions) => Promise<SandboxFileStat>;
171
+ lunoraUnmount?: (path: string) => Promise<void>;
172
+ lunoraWriteFile?: (path: string, content: ContainerFileContent, options?: ContainerFileOptions) => Promise<void>;
31
173
  removeAllowedHost?: (hostname: string) => Promise<void>;
32
174
  removeDeniedHost?: (hostname: string) => Promise<void>;
33
175
  renewActivityTimeout?: () => Promise<void>;
@@ -37,80 +179,126 @@ interface ContainerStubLike {
37
179
  stop?: (signal?: number | string) => Promise<void>;
38
180
  }
39
181
  /**
40
- * Cloudflare Durable Object data-residency jurisdiction. Widening union —
41
- * Cloudflare adds values over time.
42
- * @see https://developers.cloudflare.com/durable-objects/reference/data-location/
43
- */
44
- type DurableObjectJurisdiction = "eu" | "fedramp" | "us";
45
- /** What the client needs from a Durable Object namespace binding. */
182
+ * What the client needs from a Durable Object namespace binding.
183
+ */
46
184
  interface ContainerNamespaceLike {
47
185
  get: (id: unknown) => ContainerStubLike;
48
186
  idFromName: (name: string) => unknown;
49
187
  /**
50
- * Derive a jurisdiction-restricted subnamespace. Optional because older
51
- * workers-types releases (and test doubles) may not expose it.
52
- */
188
+ * Derive a jurisdiction-restricted subnamespace. Optional because older
189
+ * workers-types releases (and test doubles) may not expose it.
190
+ */
53
191
  jurisdiction?: (jurisdiction: DurableObjectJurisdiction) => ContainerNamespaceLike;
54
192
  }
55
- /** A handle on one container instance (one Durable Object). */
193
+ /**
194
+ * A handle on one container instance (one Durable Object).
195
+ */
56
196
  interface ContainerHandle {
57
197
  /**
58
- * Send an HTTP (or WebSocket-upgrade) request to the container. A path
59
- * string (`"/transcode"`) is resolved against a synthetic origin; a full
60
- * `Request`/URL passes through unchanged.
61
- */
198
+ * Run a command inside the container and return its exit code and output.
199
+ *
200
+ * A container is an HTTP server, so there is no platform-level exec to call:
201
+ * the command is POSTed to `/__lunora/exec` and the container app
202
+ * serves that route. What this method adds over hand-rolling that fetch is
203
+ * the **contract** — a pinned path, a typed request and response, and the
204
+ * distinction between "the command failed" (a `code`) and "the command could
205
+ * not be run" (a throw). Before it existed every caller invented its own
206
+ * `/exec` convention and read the raw body back as if it were output, which
207
+ * silently turned a runner's 500 into a successful-looking result.
208
+ *
209
+ * The container side must accept `{ args, command, cwd, env, timeoutMs }`
210
+ * and answer `{ code, stdout, stderr }` as JSON.
211
+ */
212
+ exec: (command: string, options?: ContainerExecOptions) => Promise<ContainerExecResult>;
213
+ /**
214
+ * Send an HTTP (or WebSocket-upgrade) request to the container. A path
215
+ * string (`"/transcode"`) is resolved against a synthetic origin; a full
216
+ * `Request`/URL passes through unchanged.
217
+ */
62
218
  fetch: (input: Request | string, init?: RequestInit) => Promise<Response>;
63
219
  /**
64
- * Return a handle that routes every request to `targetPort` on the
65
- * container instead of the definition's `defaultPort` — for multi-port
66
- * containers (declare the ports in `requiredPorts`). Sets the
67
- * `cf-container-target-port` header the way `@cloudflare/containers`'
68
- * `switchPort` does, so it composes with `.get()`, `.any()`, and `.pool()`:
69
- * `ctx.containers.app.get("u1").port(9090).fetch("/admin")`.
70
- */
220
+ * Return a handle that routes every request to `targetPort` on the
221
+ * container instead of the definition's `defaultPort` — for multi-port
222
+ * containers (declare the ports in `requiredPorts`). Sets the
223
+ * `cf-container-target-port` header the way `@cloudflare/containers`'
224
+ * `switchPort` does, so it composes with `.get()`, `.any()`, and `.pool()`:
225
+ * `ctx.containers.app.get("u1").port(9090).fetch("/admin")`.
226
+ */
71
227
  port: (targetPort: number) => ContainerHandle;
72
228
  }
73
229
  /**
74
- * A handle on a *named* instance (from `.get(name)`) — `fetch` plus explicit
75
- * lifecycle control. The per-entity pattern (a sandbox per user, a room per
76
- * game, a job runner per id) often needs to tear down or inspect the instance
77
- * rather than wait for `sleepAfter`, so these wrap the container DO's
78
- * `start`/`stop`/`destroy`/`getState`.
79
- */
230
+ * A handle on a *named* instance (from `.get(name)`) — `fetch` plus explicit
231
+ * lifecycle control. The per-entity pattern (a sandbox per user, a room per
232
+ * game, a job runner per id) often needs to tear down or inspect the instance
233
+ * rather than wait for `sleepAfter`, so these wrap the container DO's
234
+ * `start`/`stop`/`destroy`/`getState`.
235
+ */
80
236
  interface ContainerInstanceHandle extends ContainerHandle {
81
237
  /** Stop and discard the instance (its ephemeral disk is lost). */
82
238
  destroy: () => Promise<void>;
83
239
  /**
84
- * Adjust this instance's egress allow/deny lists at runtime — the dynamic
85
- * counterpart to the static `allowedHosts`/`deniedHosts` config. Useful for
86
- * per-tenant egress policy. Requires the worker to export `ContainerProxy`
87
- * (codegen re-exports it from the generated container file whenever any
88
- * container is defined, so the runtime controls always work).
89
- */
240
+ * Adjust this instance's egress allow/deny lists at runtime — the dynamic
241
+ * counterpart to the static `allowedHosts`/`deniedHosts` config. Useful for
242
+ * per-tenant egress policy. Requires the worker to export `ContainerProxy`
243
+ * (codegen re-exports it from the generated container file whenever any
244
+ * container is defined, so the runtime controls always work).
245
+ */
90
246
  egress: ContainerEgressControls;
91
247
  /** Read the instance's current runtime state. */
92
248
  getState: () => Promise<ContainerInstanceState>;
93
249
  /**
94
- * Reset the instance's `sleepAfter` idle timer. The platform renews it on
95
- * each proxied request, and because `@lunora/container` proxies WebSocket
96
- * frames through the Durable Object, message traffic on an open socket
97
- * renews it too (the WebSocket-keepalive gap of cloudflare/containers#147 is
98
- * closed in the bundled base). This manual control is the escape hatch for
99
- * keeping a container awake during activity that is neither an HTTP request
100
- * nor a WS message — e.g. a long out-of-band job running inside it.
101
- */
250
+ * Reset the instance's `sleepAfter` idle timer. The platform renews it on
251
+ * each proxied request, and because `@lunora/container` proxies WebSocket
252
+ * frames through the Durable Object, message traffic on an open socket
253
+ * renews it too (the WebSocket-keepalive gap of cloudflare/containers#147 is
254
+ * closed in the bundled base). This manual control is the escape hatch for
255
+ * keeping a container awake during activity that is neither an HTTP request
256
+ * nor a WS message — e.g. a long out-of-band job running inside it.
257
+ */
102
258
  renewActivityTimeout: () => Promise<void>;
259
+ /**
260
+ * Save the running container's filesystem and return a handle to restore it
261
+ * with `start({ snapshot })` — here or in another instance of the same
262
+ * container (`schedulingPolicy: "durable_object"` only; Cloudflare beta).
263
+ * Memory and processes are not captured.
264
+ */
265
+ snapshot: (options?: {
266
+ name?: string;
267
+ }) => Promise<ContainerSnapshot>;
268
+ /**
269
+ * Start a process and stream it, rather than buffer it the way `exec`
270
+ * does: its stdout/stderr arrive as streams, `stdin` can be piped, and the
271
+ * process can be killed or (under `pty`) resized while it runs. Needs the
272
+ * runtime's native exec; the container is started first when it is not
273
+ * running, and kept awake until the process exits.
274
+ */
275
+ spawn: (command: string, options?: ContainerSpawnOptions) => Promise<ContainerProcess>;
103
276
  /** Explicitly start the instance, optionally with per-instance env/entrypoint. */
104
277
  start: (options?: ContainerStartOptions) => Promise<void>;
105
278
  /** Stop the instance (optionally with a signal); it can start again on the next request. */
106
279
  stop: (signal?: number | string) => Promise<void>;
280
+ /**
281
+ * Answer a WebSocket upgrade `request` with a browser terminal: a shell
282
+ * (default `sh`) on a PTY, bridged to the socket. Binary frames are
283
+ * keystrokes, a text frame `{"cols":n,"rows":n}` resizes, and the socket
284
+ * closes when the shell exits. Authenticate the request first — whoever
285
+ * holds the socket has a shell in the container. Call it from a Worker
286
+ * route (see {@link getContainer}) and return the response as-is.
287
+ */
288
+ terminal: (request: Request, options?: ContainerTerminalOptions) => Promise<Response>;
107
289
  }
108
290
  /**
109
- * Runtime egress-firewall controls for a named instance (`handle.egress.*`).
110
- * Each maps to the corresponding `@cloudflare/containers` `Container` RPC, so
111
- * an app can tighten or relax a single instance's allowed/denied hosts after
112
- * start without redeploying.
113
- */
291
+ * A named instance of a `defineContainer({ sandbox: true })` container: the
292
+ * {@link ContainerInstanceHandle} plus the Sandbox SDK helpers — structured
293
+ * file operations, directory backups to R2 and S3-compatible bucket mounts.
294
+ */
295
+ interface SandboxContainerInstanceHandle extends ContainerInstanceHandle, ContainerSandboxControls {}
296
+ /**
297
+ * Runtime egress-firewall controls for a named instance (`handle.egress.*`).
298
+ * Each maps to the corresponding `@cloudflare/containers` `Container` RPC, so
299
+ * an app can tighten or relax a single instance's allowed/denied hosts after
300
+ * start without redeploying.
301
+ */
114
302
  interface ContainerEgressControls {
115
303
  /** Add one hostname (or glob) to the allow-list. */
116
304
  allow: (hostname: string) => Promise<void>;
@@ -125,86 +313,100 @@ interface ContainerEgressControls {
125
313
  /** Replace the entire deny-list. */
126
314
  setDenied: (hosts: ReadonlyArray<string>) => Promise<void>;
127
315
  }
128
- /** The per-definition accessor exposed as `ctx.containers.&lt;exportName>`. */
316
+ /**
317
+ * The per-definition accessor exposed as `ctx.containers.<exportName>`.
318
+ */
129
319
  interface ContainerAccessor {
130
320
  /**
131
- * A random instance from a fixed pool of `count` (defaults to the
132
- * definition's `maxInstances`, else 3 — mirroring `getRandom` from
133
- * `@cloudflare/containers`). For stateless, interchangeable workloads.
134
- *
135
- * Like `.get()`, a path/URL-string fetch transparently retries the
136
- * cold-start "instance is provisioning" transients (cloudflare/containers#45,
137
- * #139); pass {@link InstanceRetryOptions} to tune or disable it.
138
- */
321
+ * A random instance from a fixed pool of `count` (defaults to the
322
+ * definition's `maxInstances`, else 3 — mirroring `getRandom` from
323
+ * `@cloudflare/containers`). For stateless, interchangeable workloads.
324
+ *
325
+ * Like `.get()`, a path/URL-string fetch transparently retries the
326
+ * cold-start "instance is provisioning" transients (cloudflare/containers#45,
327
+ * #139); pass {@link InstanceRetryOptions} to tune or disable it.
328
+ */
139
329
  any: (count?: number, options?: InstanceRetryOptions) => ContainerHandle;
140
330
  /**
141
- * The instance for `name` — one container per entity (user, room, job…),
142
- * with lifecycle control.
143
- *
144
- * A path/URL-string fetch transparently retries the platform's cold-start
145
- * transients — "there is no Container instance available" / "container is
146
- * not listening" while an instance is still provisioning
147
- * (cloudflare/containers#45, #139) — on the *same* instance with backoff,
148
- * since the request never reached the app. Pass {@link InstanceRetryOptions}
149
- * to tune attempts/backoff or disable it (`{ attempts: 1 }`). A pre-built
150
- * `Request` (possibly a one-shot stream body) is sent once, never retried.
151
- */
331
+ * The instance for `name` — one container per entity (user, room, job…),
332
+ * with lifecycle control.
333
+ *
334
+ * A path/URL-string fetch transparently retries the platform's cold-start
335
+ * transients — "there is no Container instance available" / "container is
336
+ * not listening" while an instance is still provisioning
337
+ * (cloudflare/containers#45, #139) — on the *same* instance with backoff,
338
+ * since the request never reached the app. Pass {@link InstanceRetryOptions}
339
+ * to tune attempts/backoff or disable it (`{ attempts: 1 }`). A pre-built
340
+ * `Request` (possibly a one-shot stream body) is sent once, never retried.
341
+ */
152
342
  get: (name: string, options?: InstanceRetryOptions) => ContainerInstanceHandle;
153
343
  /**
154
- * A resilient handle over the pool: each `fetch` picks a random instance and,
155
- * on a thrown error or a retryable response (5xx by default), retries on a
156
- * freshly-picked instance with exponential backoff. Until Cloudflare ships
157
- * native autoscaling + health-aware routing this is the recommended way to
158
- * call a stateless container pool — it rides over a single cold/unhealthy
159
- * instance instead of failing the whole request.
160
- *
161
- * Because a retry re-issues the request, pass a **replayable** body — a path
162
- * string plus an `init.body` string/`ArrayBuffer` (re-created each attempt).
163
- * A pre-built `Request` carrying a stream body can only be sent once, so it
164
- * is not retry-safe here; use `.get()`/`.any()` for those.
165
- */
344
+ * A resilient handle over the pool: each `fetch` picks a random instance and,
345
+ * on a thrown error or a retryable response (5xx by default), retries on a
346
+ * freshly-picked instance with exponential backoff. Until Cloudflare ships
347
+ * native autoscaling + health-aware routing this is the recommended way to
348
+ * call a stateless container pool — it rides over a single cold/unhealthy
349
+ * instance instead of failing the whole request.
350
+ *
351
+ * Because a retry re-issues the request, pass a **replayable** body — a path
352
+ * string plus an `init.body` string/`ArrayBuffer` (re-created each attempt).
353
+ * A pre-built `Request` carrying a stream body can only be sent once, so it
354
+ * is not retry-safe here; use `.get()`/`.any()` for those.
355
+ */
166
356
  pool: (options?: PoolOptions) => ContainerHandle;
167
357
  }
168
- /** Tuning for a pooled, retrying container handle. See {@link ContainerAccessor.pool}. */
358
+ /** `ctx.containers.<name>` for a `defineContainer({ sandbox: true })` container: `.get()` adds the sandbox helpers. */
359
+ interface SandboxContainerAccessor extends ContainerAccessor {
360
+ get: (name: string, options?: InstanceRetryOptions) => SandboxContainerInstanceHandle;
361
+ }
362
+ /**
363
+ * Tuning for a pooled, retrying container handle. See {@link ContainerAccessor.pool}.
364
+ */
169
365
  interface PoolOptions {
170
366
  /** Total attempts before giving up (each on a freshly-picked instance). Default 3. */
171
367
  attempts?: number;
172
368
  /** Base backoff in ms between attempts; doubles each retry (0 disables the wait). Default 100. */
173
369
  backoffMs?: number;
174
370
  /**
175
- * Upper bound on a single backoff sleep, in ms. The doubling delay is clamped
176
- * to this ceiling so a large `attempts` count can't produce an unboundedly
177
- * long wait. Default {@link DEFAULT_MAX_BACKOFF_MS} (30s).
178
- */
371
+ * Upper bound on a single backoff sleep, in ms. The doubling delay is clamped
372
+ * to this ceiling so a large `attempts` count can't produce an unboundedly
373
+ * long wait. Default {@link DEFAULT_MAX_BACKOFF_MS} (30s).
374
+ */
179
375
  maxBackoffMs?: number;
180
376
  /**
181
- * Whether a *returned* response should be retried on another instance.
182
- * Defaults to retrying any `5xx`. A thrown error (network/start failure) is
183
- * always retried regardless of this predicate.
184
- */
377
+ * Whether a *returned* response should be retried on another instance.
378
+ * Defaults to retrying any `5xx`. A thrown error (network/start failure) is
379
+ * always retried regardless of this predicate.
380
+ *
381
+ * Applies to `fetch` only. `exec` retries on the cold-start transients
382
+ * alone, because its caller never chose the request and a command that
383
+ * already ran must not be re-run just because the runner failed afterwards.
384
+ */
185
385
  retryOn?: (response: Response) => boolean;
186
386
  /** Pool size to spread picks across. Defaults to the definition's `maxInstances`, else 3. */
187
387
  size?: number;
188
388
  }
189
389
  /**
190
- * Tuning for the cold-start retry on a `.get()`/`.any()` handle. The retry fires
191
- * only on the platform's provisioning transients (no-instance / not-listening /
192
- * rate-limited — see {@link isColdStartTransient}), which is why it's safe by
193
- * default: those responses mean the request never reached the container.
194
- */
390
+ * Tuning for the cold-start retry on a `.get()`/`.any()` handle. The retry fires
391
+ * only on the platform's provisioning transients (no-instance / not-listening /
392
+ * rate-limited — see {@link isColdStartTransient}), which is why it's safe by
393
+ * default: those responses mean the request never reached the container.
394
+ */
195
395
  interface InstanceRetryOptions {
196
396
  /**
197
- * Total attempts on a cold-start transient before the last outcome is
198
- * surfaced as-is. `1` disables the retry. Default
199
- * {@link DEFAULT_COLD_START_ATTEMPTS}.
200
- */
397
+ * Total attempts on a cold-start transient before the last outcome is
398
+ * surfaced as-is. `1` disables the retry. Default
399
+ * {@link DEFAULT_COLD_START_ATTEMPTS}.
400
+ */
201
401
  attempts?: number;
202
402
  /** Base backoff in ms between attempts; doubles each retry (0 disables the wait). Default {@link DEFAULT_COLD_START_BACKOFF_MS}. */
203
403
  backoffMs?: number;
204
404
  /** Upper bound on a single backoff sleep, in ms. Default {@link DEFAULT_MAX_BACKOFF_MS} (30s). */
205
405
  maxBackoffMs?: number;
206
406
  }
207
- /** Wiring info for one definition, emitted by codegen into the generated DO. */
407
+ /**
408
+ * Wiring info for one definition, emitted by codegen into the generated DO.
409
+ */
208
410
  interface ContainerBindingSpec {
209
411
  /** Durable Object binding name, e.g. `CONTAINER_TRANSCODER`. */
210
412
  binding: string;
@@ -214,70 +416,299 @@ interface ContainerBindingSpec {
214
416
  maxInstances?: number;
215
417
  }
216
418
  /**
217
- * Build the `ctx.containers` record from the Worker `env`. Called by the
218
- * generated ShardDO with the specs codegen derived from
219
- * `lunora/containers.ts`. A missing binding doesn't throw here — only when the
220
- * handle is actually used — so one unprovisioned container never breaks
221
- * unrelated functions.
222
- */
223
- declare const createContainerContext: (env: Record<string, unknown>, specs: ReadonlyArray<ContainerBindingSpec>, jurisdiction?: DurableObjectJurisdiction) => Record<string, ContainerAccessor>;
224
- /** A test handler: receives the request plus the targeted instance name. */
419
+ * Build the `ctx.containers` record from the Worker `env`. Called by the
420
+ * generated ShardDO with the specs codegen derived from `lunora/containers.ts`.
421
+ * A missing binding doesn't throw here — only when the handle is actually used —
422
+ * so one unprovisioned container never breaks unrelated functions.
423
+ *
424
+ * The last two arguments carry the dispatch's telemetry verdict onto every
425
+ * outbound container `fetch`, both read off the inbound request by the DO:
426
+ *
427
+ * - `traceparent` — the W3C trace context, so the container's own spans stitch
428
+ * under the Worker's trace rather than starting a disconnected one.
429
+ * - `sampleErrors` — the tail-bias toggle, so the container exports what THIS
430
+ * dispatch decided instead of falling back to its own environment and agreeing
431
+ * with the other tiers only by coincidence. `undefined` (an alarm, a
432
+ * subscription re-run, a non-Lunora caller propagated no verdict) omits the
433
+ * header and leaves the container on its own configuration.
434
+ */
435
+ declare const createContainerContext: (env: Record<string, unknown>, specs: ReadonlyArray<ContainerBindingSpec>, jurisdiction?: DurableObjectJurisdiction, traceparent?: string, sampleErrors?: boolean) => Record<string, ContainerAccessor>;
436
+ /**
437
+ * A named container instance straight off the Worker `env`, for code that has
438
+ * no `ctx.containers` — typically an `httpRouter` route that opens a
439
+ * {@link ContainerInstanceHandle.terminal} or proxies a preview port, both of
440
+ * which must hand a WebSocket upgrade response back to the browser. The
441
+ * binding is derived from `exportName` exactly as codegen derives it. Pass
442
+ * {@link SandboxContainerInstanceHandle} as the type argument for a
443
+ * `sandbox: true` container.
444
+ *
445
+ * ```ts
446
+ * app.get("/terminal", async (c) => getContainer(c.env, "box", userId).terminal(c.req.raw));
447
+ * ```
448
+ */
449
+ declare const getContainer: <Handle extends ContainerInstanceHandle = ContainerInstanceHandle>(env: Record<string, unknown>, exportName: string, name: string, options?: InstanceRetryOptions & {
450
+ jurisdiction?: DurableObjectJurisdiction;
451
+ }) => Handle;
452
+ /**
453
+ * A test handler: receives the request plus the targeted instance name.
454
+ */
225
455
  type ContainerTestHandler = (request: Request, instance: {
226
456
  name: string;
227
457
  }) => Promise<Response> | Response;
228
458
  /**
229
- * Docker-free test double for `ctx.containers`: each export name maps to a
230
- * fetch handler that plays the container. Mirrors the real shape exactly, so
231
- * action handlers under test can't tell the difference.
232
- *
233
- * ```ts
234
- * const containers = createContainerTestContext({
235
- * transcoder: (request) => new Response("ok"),
236
- * });
237
- * ```
238
- */
459
+ * Docker-free test double for `ctx.containers`: each export name maps to a
460
+ * fetch handler that plays the container. Mirrors the real shape exactly, so
461
+ * action handlers under test can't tell the difference.
462
+ *
463
+ * ```ts
464
+ * const containers = createContainerTestContext({
465
+ * transcoder: (request) => new Response("ok"),
466
+ * });
467
+ * ```
468
+ */
239
469
  declare const createContainerTestContext: (handlers: Record<string, ContainerTestHandler>) => Record<string, ContainerAccessor>;
470
+ /** Whether an image name is a Cloudflare-managed image (`cloudflare/debian-trixie`), which needs no `images` entry. */
471
+ declare const isManagedImage: (name: string) => boolean;
472
+ /**
473
+ * The default-policy keys the `durable_object` policy refuses — wrangler rejects
474
+ * `max_instances` under it, and a rollout has nothing to roll. One table, read by
475
+ * `defineContainer` and by codegen's static discovery.
476
+ */
477
+ declare const DURABLE_OBJECT_POLICY_FORBIDDEN_KEYS: readonly ["maxInstances", "rollout"];
240
478
  /**
241
- * Normalize a `ContainerImageSource` into the shape wrangler wants: a
242
- * Dockerfile path + build context for local builds, or a fully-qualified
243
- * reference for pre-built images.
244
- *
245
- * A local-path string whose basename starts with `Dockerfile` (so
246
- * `Dockerfile.dev` also counts) is used as-is with its directory as the build
247
- * context; any other path is treated as the build-context directory and the
248
- * Dockerfile is expected at `&lt;dir>/Dockerfile`.
249
- */
479
+ * Normalize a `ContainerImageSource` into the shape wrangler wants: a
480
+ * Dockerfile path + build context for local builds, or a fully-qualified
481
+ * reference for pre-built images.
482
+ *
483
+ * A local-path string whose basename starts with `Dockerfile` (so
484
+ * `Dockerfile.dev` also counts) is used as-is with its directory as the build
485
+ * context; any other path is treated as the build-context directory and the
486
+ * Dockerfile is expected at `<dir>/Dockerfile`.
487
+ */
250
488
  declare const normalizeContainerImage: (image: ContainerImageSource) => NormalizedContainerImage;
251
489
  /**
252
- * The generated Container DO class name for a `lunora/containers.ts` export:
253
- * `transcoder` → `TranscoderContainer`. wrangler's `containers[].class_name`
254
- * and the Durable Object binding's `class_name` both reference it, so codegen
255
- * and the config layer MUST derive it identically — always via this helper.
256
- */
490
+ * The generated Container DO class name for a `lunora/containers.ts` export:
491
+ * `transcoder` → `TranscoderContainer`. wrangler's `containers[].class_name`
492
+ * and the Durable Object binding's `class_name` both reference it, so codegen
493
+ * and the config layer MUST derive it identically — always via this helper.
494
+ */
257
495
  declare const containerClassName: (exportName: string) => string;
258
496
  /**
259
- * The Durable Object binding name for a container export: `transcoder` →
260
- * `CONTAINER_TRANSCODER`, `imageResizer` → `CONTAINER_IMAGE_RESIZER`. The
261
- * `CONTAINER_` prefix namespaces these away from `SHARD`/`SESSION`/`SCHEDULER`
262
- * so a container export can never collide with the built-in bindings.
263
- */
497
+ * The Durable Object binding name for a container export: `transcoder` →
498
+ * `CONTAINER_TRANSCODER`, `imageResizer` → `CONTAINER_IMAGE_RESIZER`. The
499
+ * `CONTAINER_` prefix namespaces these away from `SHARD`/`SESSION`/`SCHEDULER`
500
+ * so a container export can never collide with the built-in bindings.
501
+ */
264
502
  declare const containerBindingName: (exportName: string) => string;
265
503
  /**
266
- * The local image tag a Railpack `{ build }` container is built and pushed
267
- * under: `transcoder` → `lunora-transcoder:build`. The config reconciler writes
268
- * it as the wrangler `containers[].image`, and `lunora deploy` builds that tag
269
- * with Railpack and `wrangler containers push`es it before deploying — so all
270
- * three derive the tag from this one helper and can never disagree.
271
- */
504
+ * The local image tag a Railpack `{ build }` container is built and pushed
505
+ * under: `transcoder` → `lunora-transcoder:build`. The config reconciler writes
506
+ * it as the wrangler `containers[].image`, and `lunora deploy` builds that tag
507
+ * with Railpack and `wrangler containers push`es it before deploying — so all
508
+ * three derive the tag from this one helper and can never disagree.
509
+ */
272
510
  declare const containerBuildTag: (exportName: string) => string;
273
- declare const defineContainer: (config: ContainerConfig) => ContainerDefinition;
274
- /** True when a value is a `defineContainer` result (the runtime brand check). */
511
+ /** Whether a registry reference is digest-pinned in the Cloudflare registry — the only kind a named image accepts. */
512
+ declare const isCloudflareRegistryDigest: (reference: unknown) => boolean;
513
+ /**
514
+ * `defineContainer` is part of the experimental `@lunora/container` API and may change without a major version bump.
515
+ */
516
+ declare const defineContainer: {
517
+ (config: DefaultScheduledContainerConfig): DefaultScheduledContainerConfig & {
518
+ readonly isLunoraContainer: true;
519
+ };
520
+ (config: DurableObjectScheduledContainerConfig): DurableObjectScheduledContainerConfig & {
521
+ readonly isLunoraContainer: true;
522
+ };
523
+ };
524
+ /**
525
+ * True when a value is a `defineContainer` result (the runtime brand check).
526
+ */
275
527
  declare const isContainerDefinition: (value: unknown) => value is ContainerDefinition;
276
528
  /**
277
- * The container's full environment at instance start: the static `env` block
278
- * plus every declared secret resolved from the Worker `env`. A declared secret
279
- * missing from the Worker env fails fast — starting the container without a
280
- * credential it was promised yields far worse errors downstream.
281
- */
529
+ * The container's full environment at instance start: the static `env` block
530
+ * plus every declared secret resolved from the Worker `env`. A declared secret
531
+ * missing from the Worker env fails fast — starting the container without a
532
+ * credential it was promised yields far worse errors downstream.
533
+ */
282
534
  declare const resolveContainerEnvVariables: (definition: ContainerDefinition, workerEnv: Record<string, unknown>, exportName?: string) => Record<string, string>;
283
- export { type ContainerAccessor, type ContainerBindingSpec, type ContainerConfig, type ContainerDefinition, type ContainerEgressControls, type ContainerHandle, type ContainerImageSource, type ContainerInstanceHandle, type ContainerInstanceState, type ContainerNamespaceLike, type ContainerStartOptions, type ContainerTestHandler, type DurableObjectJurisdiction, type InstanceRetryOptions, type NormalizedContainerImage, type PoolOptions, containerBindingName, containerBuildTag, containerClassName, createContainerContext, createContainerTestContext, defineContainer, isContainerDefinition, normalizeContainerImage, resolveContainerEnvVariables as resolveContainerEnvVars };
535
+ /**
536
+ * Cloudflare's documented bounds for a custom container instance type
537
+ * (https://developers.cloudflare.com/containers/platform-details/limits/):
538
+ * 1–4 vCPU, up to 12 GiB memory, up to 20 GB disk at any memory size, and at
539
+ * least 3 GiB memory per vCPU. Below 1 vCPU, Cloudflare points at the named
540
+ * `lite` / `basic` instance types instead.
541
+ *
542
+ * The one copy of these numbers. `defineContainer` checks a `durable_object`
543
+ * container's runtime size against it, and `@lunora/config`'s wrangler validator
544
+ * checks `containers[].instance_type` against it (config already depends on this
545
+ * package, so the import runs one way). Pure data, Node-safe.
546
+ */
547
+ /** Inclusive bounds for one dimension of a custom instance type. */
548
+ interface InstanceDimensionLimits {
549
+ readonly max: number;
550
+ readonly min: number;
551
+ }
552
+ /** Per-dimension bounds, keyed by the `CustomContainerInstanceType` field names. */
553
+ declare const CUSTOM_INSTANCE_TYPE_LIMITS: Readonly<{
554
+ diskMb: InstanceDimensionLimits;
555
+ memoryMib: InstanceDimensionLimits;
556
+ vcpu: InstanceDimensionLimits;
557
+ }>;
558
+ /** The memory floor per vCPU: 3 GiB. */
559
+ declare const CUSTOM_INSTANCE_MIN_MEMORY_MIB_PER_VCPU = 3072;
560
+ export { CONTAINER_EXEC_PATH, CUSTOM_INSTANCE_MIN_MEMORY_MIB_PER_VCPU, CUSTOM_INSTANCE_TYPE_LIMITS,
561
+ /**
562
+ * `@lunora/container` — Cloudflare Containers for Lunora.
563
+ *
564
+ * This root export is Node-safe (no Cloudflare runtime imports): the
565
+ * `defineContainer` authoring surface, the naming/normalization helpers
566
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
567
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
568
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
569
+ * behind the `@lunora/container/do` subpath.
570
+ */
571
+ type ContainerAccessor, type ContainerBackupOptions,
572
+ /**
573
+ * `@lunora/container` — Cloudflare Containers for Lunora.
574
+ *
575
+ * This root export is Node-safe (no Cloudflare runtime imports): the
576
+ * `defineContainer` authoring surface, the naming/normalization helpers
577
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
578
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
579
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
580
+ * behind the `@lunora/container/do` subpath.
581
+ */
582
+ type ContainerBindingSpec, type ContainerDefinition,
583
+ /**
584
+ * `@lunora/container` — Cloudflare Containers for Lunora.
585
+ *
586
+ * This root export is Node-safe (no Cloudflare runtime imports): the
587
+ * `defineContainer` authoring surface, the naming/normalization helpers
588
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
589
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
590
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
591
+ * behind the `@lunora/container/do` subpath.
592
+ */
593
+ type ContainerEgressControls, type ContainerExecOptions, type ContainerExecResult, type ContainerFileContent, type ContainerFileOptions,
594
+ /**
595
+ * `@lunora/container` — Cloudflare Containers for Lunora.
596
+ *
597
+ * This root export is Node-safe (no Cloudflare runtime imports): the
598
+ * `defineContainer` authoring surface, the naming/normalization helpers
599
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
600
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
601
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
602
+ * behind the `@lunora/container/do` subpath.
603
+ */
604
+ type ContainerHandle, type ContainerImageSource,
605
+ /**
606
+ * `@lunora/container` — Cloudflare Containers for Lunora.
607
+ *
608
+ * This root export is Node-safe (no Cloudflare runtime imports): the
609
+ * `defineContainer` authoring surface, the naming/normalization helpers
610
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
611
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
612
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
613
+ * behind the `@lunora/container/do` subpath.
614
+ */
615
+ type ContainerInstanceHandle,
616
+ /**
617
+ * `@lunora/container` — Cloudflare Containers for Lunora.
618
+ *
619
+ * This root export is Node-safe (no Cloudflare runtime imports): the
620
+ * `defineContainer` authoring surface, the naming/normalization helpers
621
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
622
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
623
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
624
+ * behind the `@lunora/container/do` subpath.
625
+ */
626
+ type ContainerInstanceState, type ContainerMountRequest,
627
+ /**
628
+ * `@lunora/container` — Cloudflare Containers for Lunora.
629
+ *
630
+ * This root export is Node-safe (no Cloudflare runtime imports): the
631
+ * `defineContainer` authoring surface, the naming/normalization helpers
632
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
633
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
634
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
635
+ * behind the `@lunora/container/do` subpath.
636
+ */
637
+ type ContainerNamespaceLike, type ContainerProcess, type ContainerRuntimeInstanceType, type ContainerSandboxControls, type ContainerSnapshot, type ContainerSpawnOptions,
638
+ /**
639
+ * `@lunora/container` — Cloudflare Containers for Lunora.
640
+ *
641
+ * This root export is Node-safe (no Cloudflare runtime imports): the
642
+ * `defineContainer` authoring surface, the naming/normalization helpers
643
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
644
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
645
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
646
+ * behind the `@lunora/container/do` subpath.
647
+ */
648
+ type ContainerStartOptions, type ContainerTerminalOptions,
649
+ /**
650
+ * `@lunora/container` — Cloudflare Containers for Lunora.
651
+ *
652
+ * This root export is Node-safe (no Cloudflare runtime imports): the
653
+ * `defineContainer` authoring surface, the naming/normalization helpers
654
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
655
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
656
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
657
+ * behind the `@lunora/container/do` subpath.
658
+ */
659
+ type ContainerTestHandler, DURABLE_OBJECT_POLICY_FORBIDDEN_KEYS, type DefaultScheduledContainerConfig,
660
+ /**
661
+ * `@lunora/container` — Cloudflare Containers for Lunora.
662
+ *
663
+ * This root export is Node-safe (no Cloudflare runtime imports): the
664
+ * `defineContainer` authoring surface, the naming/normalization helpers
665
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
666
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
667
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
668
+ * behind the `@lunora/container/do` subpath.
669
+ */
670
+ type DurableObjectJurisdiction, type DurableObjectScheduledContainerConfig, type InstanceDimensionLimits,
671
+ /**
672
+ * `@lunora/container` — Cloudflare Containers for Lunora.
673
+ *
674
+ * This root export is Node-safe (no Cloudflare runtime imports): the
675
+ * `defineContainer` authoring surface, the naming/normalization helpers
676
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
677
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
678
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
679
+ * behind the `@lunora/container/do` subpath.
680
+ */
681
+ type InstanceRetryOptions, type NormalizedContainerImage,
682
+ /**
683
+ * `@lunora/container` — Cloudflare Containers for Lunora.
684
+ *
685
+ * This root export is Node-safe (no Cloudflare runtime imports): the
686
+ * `defineContainer` authoring surface, the naming/normalization helpers
687
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
688
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
689
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
690
+ * behind the `@lunora/container/do` subpath.
691
+ */
692
+ type PoolOptions,
693
+ /**
694
+ * `@lunora/container` — Cloudflare Containers for Lunora.
695
+ *
696
+ * This root export is Node-safe (no Cloudflare runtime imports): the
697
+ * `defineContainer` authoring surface, the naming/normalization helpers
698
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
699
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
700
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
701
+ * behind the `@lunora/container/do` subpath.
702
+ */
703
+ type SandboxContainerAccessor,
704
+ /**
705
+ * `@lunora/container` — Cloudflare Containers for Lunora.
706
+ *
707
+ * This root export is Node-safe (no Cloudflare runtime imports): the
708
+ * `defineContainer` authoring surface, the naming/normalization helpers
709
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
710
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
711
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
712
+ * behind the `@lunora/container/do` subpath.
713
+ */
714
+ type SandboxContainerInstanceHandle, containerBindingName, containerBuildTag, containerClassName, createContainerContext, createContainerTestContext, defineContainer, getContainer, isCloudflareRegistryDigest, isContainerDefinition, isManagedImage, normalizeContainerImage, resolveContainerEnvVariables as resolveContainerEnvVars };