@lunora/container 1.0.0-alpha.6 → 1.0.0-alpha.61

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.
package/dist/index.d.mts CHANGED
@@ -1,17 +1,100 @@
1
- import { C as ContainerConfig, a as ContainerDefinition, b as ContainerImageSource, N as NormalizedContainerImage } from "./packem_shared/types.d-BlNwNY44.mjs";
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.mjs";
3
- /** Options for explicitly starting an instance (mirrors `@cloudflare/containers`). */
1
+ import { D as DurableObjectJurisdiction, a as ContainerConfig, C as ContainerDefinition, b as ContainerImageSource, N as NormalizedContainerImage } from "./packem_shared/jurisdiction.d-DPvNG2h5.mjs";
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/jurisdiction.d-DPvNG2h5.mjs";
3
+ /**
4
+ * Per-call options for `ContainerHandle.exec`.
5
+ */
6
+ interface ContainerExecOptions {
7
+ /** Arguments passed to `command`, unshelled — the container must not concatenate them into a shell string. */
8
+ args?: ReadonlyArray<string>;
9
+ /** Working directory for the command, relative to the container's own root. */
10
+ cwd?: string;
11
+ /** Extra environment for this command only, merged over the container's env. */
12
+ env?: Readonly<Record<string, string>>;
13
+ /**
14
+ * Cap on the response body, in bytes. The whole `{code,stdout,stderr}`
15
+ * document has to be held in memory to be parsed, and a build box or job
16
+ * runner routinely writes tens of megabytes — a single unbounded `exec`
17
+ * that returns more than the isolate's memory limit kills the isolate and
18
+ * every other in-flight request sharing it, not just this call. So a
19
+ * runner that overruns the cap fails the call loudly rather than taking
20
+ * the shard down with it; raise this (or have the runner cap its own
21
+ * output) when a command legitimately produces more.
22
+ *
23
+ * Default {@link DEFAULT_EXEC_MAX_OUTPUT_BYTES} (1MB).
24
+ */
25
+ maxOutputBytes?: number;
26
+ /**
27
+ * Abort the call. Composed with {@link ContainerExecOptions.timeoutMs} when
28
+ * both are given — whichever fires first wins.
29
+ */
30
+ signal?: AbortSignal;
31
+ /**
32
+ * Give up after this many ms. Covers the whole call — the request *and*
33
+ * reading the response body, since `fetch` resolves on headers and a
34
+ * runner that answers `200` and then stalls mid-body would otherwise sit
35
+ * past the deadline. Sent to the container as well, so a well-behaved
36
+ * runner can kill the process rather than leak it when the caller walks
37
+ * away.
38
+ */
39
+ timeoutMs?: number;
40
+ }
41
+ /**
42
+ * The outcome of a `ContainerHandle.exec` call.
43
+ *
44
+ * A non-zero `code` is **not** an error: a command that ran and failed is a
45
+ * result, and the caller decides what to do with it. Only a failure to *run*
46
+ * the command — transport, a non-2xx from the runner, an unparseable body —
47
+ * throws.
48
+ */
49
+ interface ContainerExecResult {
50
+ /** Process exit code. Non-zero means the command ran and failed. */
51
+ code: number;
52
+ /** Everything the command wrote to stderr. */
53
+ stderr: string;
54
+ /** Everything the command wrote to stdout. */
55
+ stdout: string;
56
+ }
57
+ /**
58
+ * The route `ContainerHandle.exec` POSTs to. Namespaced under
59
+ * `/__lunora/` so it cannot collide with an application route the container
60
+ * already serves — the previous ad-hoc convention was a bare `/exec`, which an
61
+ * app could plausibly own for its own purposes.
62
+ *
63
+ * Exported because it is a *contract*, not an implementation detail: a
64
+ * container image has to serve exactly this route, and `@lunora/agent`'s
65
+ * human-in-the-loop gate has to recognise it. Both had it hard-coded as a
66
+ * second literal, which drifts silently — and on the gate's side, drift
67
+ * un-gates model-chosen command execution.
68
+ */
69
+ declare const CONTAINER_EXEC_PATH = "/__lunora/exec";
70
+ /**
71
+ * Options for explicitly starting an instance (mirrors `@cloudflare/containers`).
72
+ */
4
73
  interface ContainerStartOptions {
5
74
  /** Override outbound internet access for this start. */
6
75
  enableInternet?: boolean;
7
76
  /** Override the container entrypoint. */
8
77
  entrypoint?: string[];
9
- /** Per-instance environment, merged over the definition's `env`/secrets. */
78
+ /**
79
+ * Per-instance environment. REPLACES the definition's `env`, `secrets` and
80
+ * `secretsStore` (the Secrets Store is not read), so pass every variable the
81
+ * container needs. It is persisted for the instance: every later start —
82
+ * including the implicit restart a `fetch`/`exec` triggers after the
83
+ * container slept, crashed or hit `hardTimeout` — uses it, until
84
+ * `destroy()`. The override cannot change while the container is running
85
+ * or still starting: a start whose `envVars` differ is rejected with
86
+ * `CONFLICT` in either case, so `stop()` it (and let any start in flight
87
+ * finish) first. So
88
+ * `start({ envVars: {} })` is a sandbox without the declared credentials,
89
+ * for as long as the instance exists.
90
+ */
10
91
  envVars?: Record<string, string>;
11
92
  /** Metadata labels attached for metrics/observability. */
12
93
  labels?: Record<string, string>;
13
94
  }
14
- /** A container instance's runtime state, as returned by `getState()`. Structural — the platform adds fields over time. */
95
+ /**
96
+ * A container instance's runtime state, as returned by `getState()`. Structural — the platform adds fields over time.
97
+ */
15
98
  interface ContainerInstanceState {
16
99
  [key: string]: unknown;
17
100
  /** Process exit code, present once the instance has `stopped_with_code`. */
@@ -28,6 +111,8 @@ interface ContainerStubLike {
28
111
  destroy?: () => Promise<void>;
29
112
  fetch: (input: Request) => Promise<Response>;
30
113
  getState?: () => Promise<ContainerInstanceState>;
114
+ /** The container DO's exec entry (`LunoraContainer.lunoraExec`), the only way a request reaches `/__lunora/exec`. */
115
+ lunoraExec?: (request: Request) => Promise<Response>;
31
116
  removeAllowedHost?: (hostname: string) => Promise<void>;
32
117
  removeDeniedHost?: (hostname: string) => Promise<void>;
33
118
  renewActivityTimeout?: () => Promise<void>;
@@ -37,68 +122,82 @@ interface ContainerStubLike {
37
122
  stop?: (signal?: number | string) => Promise<void>;
38
123
  }
39
124
  /**
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. */
125
+ * What the client needs from a Durable Object namespace binding.
126
+ */
46
127
  interface ContainerNamespaceLike {
47
128
  get: (id: unknown) => ContainerStubLike;
48
129
  idFromName: (name: string) => unknown;
49
130
  /**
50
- * Derive a jurisdiction-restricted subnamespace. Optional because older
51
- * workers-types releases (and test doubles) may not expose it.
52
- */
131
+ * Derive a jurisdiction-restricted subnamespace. Optional because older
132
+ * workers-types releases (and test doubles) may not expose it.
133
+ */
53
134
  jurisdiction?: (jurisdiction: DurableObjectJurisdiction) => ContainerNamespaceLike;
54
135
  }
55
- /** A handle on one container instance (one Durable Object). */
136
+ /**
137
+ * A handle on one container instance (one Durable Object).
138
+ */
56
139
  interface ContainerHandle {
57
140
  /**
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
- */
141
+ * Run a command inside the container and return its exit code and output.
142
+ *
143
+ * A container is an HTTP server, so there is no platform-level exec to call:
144
+ * the command is POSTed to `/__lunora/exec` and the container app
145
+ * serves that route. What this method adds over hand-rolling that fetch is
146
+ * the **contract** — a pinned path, a typed request and response, and the
147
+ * distinction between "the command failed" (a `code`) and "the command could
148
+ * not be run" (a throw). Before it existed every caller invented its own
149
+ * `/exec` convention and read the raw body back as if it were output, which
150
+ * silently turned a runner's 500 into a successful-looking result.
151
+ *
152
+ * The container side must accept `{ args, command, cwd, env, timeoutMs }`
153
+ * and answer `{ code, stdout, stderr }` as JSON.
154
+ */
155
+ exec: (command: string, options?: ContainerExecOptions) => Promise<ContainerExecResult>;
156
+ /**
157
+ * Send an HTTP (or WebSocket-upgrade) request to the container. A path
158
+ * string (`"/transcode"`) is resolved against a synthetic origin; a full
159
+ * `Request`/URL passes through unchanged.
160
+ */
62
161
  fetch: (input: Request | string, init?: RequestInit) => Promise<Response>;
63
162
  /**
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
- */
163
+ * Return a handle that routes every request to `targetPort` on the
164
+ * container instead of the definition's `defaultPort` — for multi-port
165
+ * containers (declare the ports in `requiredPorts`). Sets the
166
+ * `cf-container-target-port` header the way `@cloudflare/containers`'
167
+ * `switchPort` does, so it composes with `.get()`, `.any()`, and `.pool()`:
168
+ * `ctx.containers.app.get("u1").port(9090).fetch("/admin")`.
169
+ */
71
170
  port: (targetPort: number) => ContainerHandle;
72
171
  }
73
172
  /**
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
- */
173
+ * A handle on a *named* instance (from `.get(name)`) — `fetch` plus explicit
174
+ * lifecycle control. The per-entity pattern (a sandbox per user, a room per
175
+ * game, a job runner per id) often needs to tear down or inspect the instance
176
+ * rather than wait for `sleepAfter`, so these wrap the container DO's
177
+ * `start`/`stop`/`destroy`/`getState`.
178
+ */
80
179
  interface ContainerInstanceHandle extends ContainerHandle {
81
180
  /** Stop and discard the instance (its ephemeral disk is lost). */
82
181
  destroy: () => Promise<void>;
83
182
  /**
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
- */
183
+ * Adjust this instance's egress allow/deny lists at runtime — the dynamic
184
+ * counterpart to the static `allowedHosts`/`deniedHosts` config. Useful for
185
+ * per-tenant egress policy. Requires the worker to export `ContainerProxy`
186
+ * (codegen re-exports it from the generated container file whenever any
187
+ * container is defined, so the runtime controls always work).
188
+ */
90
189
  egress: ContainerEgressControls;
91
190
  /** Read the instance's current runtime state. */
92
191
  getState: () => Promise<ContainerInstanceState>;
93
192
  /**
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
- */
193
+ * Reset the instance's `sleepAfter` idle timer. The platform renews it on
194
+ * each proxied request, and because `@lunora/container` proxies WebSocket
195
+ * frames through the Durable Object, message traffic on an open socket
196
+ * renews it too (the WebSocket-keepalive gap of cloudflare/containers#147 is
197
+ * closed in the bundled base). This manual control is the escape hatch for
198
+ * keeping a container awake during activity that is neither an HTTP request
199
+ * nor a WS message — e.g. a long out-of-band job running inside it.
200
+ */
102
201
  renewActivityTimeout: () => Promise<void>;
103
202
  /** Explicitly start the instance, optionally with per-instance env/entrypoint. */
104
203
  start: (options?: ContainerStartOptions) => Promise<void>;
@@ -106,11 +205,11 @@ interface ContainerInstanceHandle extends ContainerHandle {
106
205
  stop: (signal?: number | string) => Promise<void>;
107
206
  }
108
207
  /**
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
- */
208
+ * Runtime egress-firewall controls for a named instance (`handle.egress.*`).
209
+ * Each maps to the corresponding `@cloudflare/containers` `Container` RPC, so
210
+ * an app can tighten or relax a single instance's allowed/denied hosts after
211
+ * start without redeploying.
212
+ */
114
213
  interface ContainerEgressControls {
115
214
  /** Add one hostname (or glob) to the allow-list. */
116
215
  allow: (hostname: string) => Promise<void>;
@@ -125,86 +224,96 @@ interface ContainerEgressControls {
125
224
  /** Replace the entire deny-list. */
126
225
  setDenied: (hosts: ReadonlyArray<string>) => Promise<void>;
127
226
  }
128
- /** The per-definition accessor exposed as `ctx.containers.&lt;exportName>`. */
227
+ /**
228
+ * The per-definition accessor exposed as `ctx.containers.<exportName>`.
229
+ */
129
230
  interface ContainerAccessor {
130
231
  /**
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
- */
232
+ * A random instance from a fixed pool of `count` (defaults to the
233
+ * definition's `maxInstances`, else 3 — mirroring `getRandom` from
234
+ * `@cloudflare/containers`). For stateless, interchangeable workloads.
235
+ *
236
+ * Like `.get()`, a path/URL-string fetch transparently retries the
237
+ * cold-start "instance is provisioning" transients (cloudflare/containers#45,
238
+ * #139); pass {@link InstanceRetryOptions} to tune or disable it.
239
+ */
139
240
  any: (count?: number, options?: InstanceRetryOptions) => ContainerHandle;
140
241
  /**
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
- */
242
+ * The instance for `name` — one container per entity (user, room, job…),
243
+ * with lifecycle control.
244
+ *
245
+ * A path/URL-string fetch transparently retries the platform's cold-start
246
+ * transients — "there is no Container instance available" / "container is
247
+ * not listening" while an instance is still provisioning
248
+ * (cloudflare/containers#45, #139) — on the *same* instance with backoff,
249
+ * since the request never reached the app. Pass {@link InstanceRetryOptions}
250
+ * to tune attempts/backoff or disable it (`{ attempts: 1 }`). A pre-built
251
+ * `Request` (possibly a one-shot stream body) is sent once, never retried.
252
+ */
152
253
  get: (name: string, options?: InstanceRetryOptions) => ContainerInstanceHandle;
153
254
  /**
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
- */
255
+ * A resilient handle over the pool: each `fetch` picks a random instance and,
256
+ * on a thrown error or a retryable response (5xx by default), retries on a
257
+ * freshly-picked instance with exponential backoff. Until Cloudflare ships
258
+ * native autoscaling + health-aware routing this is the recommended way to
259
+ * call a stateless container pool — it rides over a single cold/unhealthy
260
+ * instance instead of failing the whole request.
261
+ *
262
+ * Because a retry re-issues the request, pass a **replayable** body — a path
263
+ * string plus an `init.body` string/`ArrayBuffer` (re-created each attempt).
264
+ * A pre-built `Request` carrying a stream body can only be sent once, so it
265
+ * is not retry-safe here; use `.get()`/`.any()` for those.
266
+ */
166
267
  pool: (options?: PoolOptions) => ContainerHandle;
167
268
  }
168
- /** Tuning for a pooled, retrying container handle. See {@link ContainerAccessor.pool}. */
269
+ /**
270
+ * Tuning for a pooled, retrying container handle. See {@link ContainerAccessor.pool}.
271
+ */
169
272
  interface PoolOptions {
170
273
  /** Total attempts before giving up (each on a freshly-picked instance). Default 3. */
171
274
  attempts?: number;
172
275
  /** Base backoff in ms between attempts; doubles each retry (0 disables the wait). Default 100. */
173
276
  backoffMs?: number;
174
277
  /**
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
- */
278
+ * Upper bound on a single backoff sleep, in ms. The doubling delay is clamped
279
+ * to this ceiling so a large `attempts` count can't produce an unboundedly
280
+ * long wait. Default {@link DEFAULT_MAX_BACKOFF_MS} (30s).
281
+ */
179
282
  maxBackoffMs?: number;
180
283
  /**
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
- */
284
+ * Whether a *returned* response should be retried on another instance.
285
+ * Defaults to retrying any `5xx`. A thrown error (network/start failure) is
286
+ * always retried regardless of this predicate.
287
+ *
288
+ * Applies to `fetch` only. `exec` retries on the cold-start transients
289
+ * alone, because its caller never chose the request and a command that
290
+ * already ran must not be re-run just because the runner failed afterwards.
291
+ */
185
292
  retryOn?: (response: Response) => boolean;
186
293
  /** Pool size to spread picks across. Defaults to the definition's `maxInstances`, else 3. */
187
294
  size?: number;
188
295
  }
189
296
  /**
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
- */
297
+ * Tuning for the cold-start retry on a `.get()`/`.any()` handle. The retry fires
298
+ * only on the platform's provisioning transients (no-instance / not-listening /
299
+ * rate-limited — see {@link isColdStartTransient}), which is why it's safe by
300
+ * default: those responses mean the request never reached the container.
301
+ */
195
302
  interface InstanceRetryOptions {
196
303
  /**
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
- */
304
+ * Total attempts on a cold-start transient before the last outcome is
305
+ * surfaced as-is. `1` disables the retry. Default
306
+ * {@link DEFAULT_COLD_START_ATTEMPTS}.
307
+ */
201
308
  attempts?: number;
202
309
  /** Base backoff in ms between attempts; doubles each retry (0 disables the wait). Default {@link DEFAULT_COLD_START_BACKOFF_MS}. */
203
310
  backoffMs?: number;
204
311
  /** Upper bound on a single backoff sleep, in ms. Default {@link DEFAULT_MAX_BACKOFF_MS} (30s). */
205
312
  maxBackoffMs?: number;
206
313
  }
207
- /** Wiring info for one definition, emitted by codegen into the generated DO. */
314
+ /**
315
+ * Wiring info for one definition, emitted by codegen into the generated DO.
316
+ */
208
317
  interface ContainerBindingSpec {
209
318
  /** Durable Object binding name, e.g. `CONTAINER_TRANSCODER`. */
210
319
  binding: string;
@@ -214,70 +323,219 @@ interface ContainerBindingSpec {
214
323
  maxInstances?: number;
215
324
  }
216
325
  /**
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. */
326
+ * Build the `ctx.containers` record from the Worker `env`. Called by the
327
+ * generated ShardDO with the specs codegen derived from `lunora/containers.ts`.
328
+ * A missing binding doesn't throw here — only when the handle is actually used —
329
+ * so one unprovisioned container never breaks unrelated functions.
330
+ *
331
+ * The last two arguments carry the dispatch's telemetry verdict onto every
332
+ * outbound container `fetch`, both read off the inbound request by the DO:
333
+ *
334
+ * - `traceparent` — the W3C trace context, so the container's own spans stitch
335
+ * under the Worker's trace rather than starting a disconnected one.
336
+ * - `sampleErrors` — the tail-bias toggle, so the container exports what THIS
337
+ * dispatch decided instead of falling back to its own environment and agreeing
338
+ * with the other tiers only by coincidence. `undefined` (an alarm, a
339
+ * subscription re-run, a non-Lunora caller propagated no verdict) omits the
340
+ * header and leaves the container on its own configuration.
341
+ */
342
+ declare const createContainerContext: (env: Record<string, unknown>, specs: ReadonlyArray<ContainerBindingSpec>, jurisdiction?: DurableObjectJurisdiction, traceparent?: string, sampleErrors?: boolean) => Record<string, ContainerAccessor>;
343
+ /**
344
+ * A test handler: receives the request plus the targeted instance name.
345
+ */
225
346
  type ContainerTestHandler = (request: Request, instance: {
226
347
  name: string;
227
348
  }) => Promise<Response> | Response;
228
349
  /**
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
- */
350
+ * Docker-free test double for `ctx.containers`: each export name maps to a
351
+ * fetch handler that plays the container. Mirrors the real shape exactly, so
352
+ * action handlers under test can't tell the difference.
353
+ *
354
+ * ```ts
355
+ * const containers = createContainerTestContext({
356
+ * transcoder: (request) => new Response("ok"),
357
+ * });
358
+ * ```
359
+ */
239
360
  declare const createContainerTestContext: (handlers: Record<string, ContainerTestHandler>) => Record<string, ContainerAccessor>;
240
361
  /**
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
- */
362
+ * Normalize a `ContainerImageSource` into the shape wrangler wants: a
363
+ * Dockerfile path + build context for local builds, or a fully-qualified
364
+ * reference for pre-built images.
365
+ *
366
+ * A local-path string whose basename starts with `Dockerfile` (so
367
+ * `Dockerfile.dev` also counts) is used as-is with its directory as the build
368
+ * context; any other path is treated as the build-context directory and the
369
+ * Dockerfile is expected at `<dir>/Dockerfile`.
370
+ */
250
371
  declare const normalizeContainerImage: (image: ContainerImageSource) => NormalizedContainerImage;
251
372
  /**
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
- */
373
+ * The generated Container DO class name for a `lunora/containers.ts` export:
374
+ * `transcoder` → `TranscoderContainer`. wrangler's `containers[].class_name`
375
+ * and the Durable Object binding's `class_name` both reference it, so codegen
376
+ * and the config layer MUST derive it identically — always via this helper.
377
+ */
257
378
  declare const containerClassName: (exportName: string) => string;
258
379
  /**
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
- */
380
+ * The Durable Object binding name for a container export: `transcoder` →
381
+ * `CONTAINER_TRANSCODER`, `imageResizer` → `CONTAINER_IMAGE_RESIZER`. The
382
+ * `CONTAINER_` prefix namespaces these away from `SHARD`/`SESSION`/`SCHEDULER`
383
+ * so a container export can never collide with the built-in bindings.
384
+ */
264
385
  declare const containerBindingName: (exportName: string) => string;
265
386
  /**
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
- */
387
+ * The local image tag a Railpack `{ build }` container is built and pushed
388
+ * under: `transcoder` → `lunora-transcoder:build`. The config reconciler writes
389
+ * it as the wrangler `containers[].image`, and `lunora deploy` builds that tag
390
+ * with Railpack and `wrangler containers push`es it before deploying — so all
391
+ * three derive the tag from this one helper and can never disagree.
392
+ */
272
393
  declare const containerBuildTag: (exportName: string) => string;
394
+ /**
395
+ * `defineContainer` is part of the experimental `@lunora/container` API and may change without a major version bump.
396
+ */
273
397
  declare const defineContainer: (config: ContainerConfig) => ContainerDefinition;
274
- /** True when a value is a `defineContainer` result (the runtime brand check). */
398
+ /**
399
+ * True when a value is a `defineContainer` result (the runtime brand check).
400
+ */
275
401
  declare const isContainerDefinition: (value: unknown) => value is ContainerDefinition;
276
402
  /**
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
- */
403
+ * The container's full environment at instance start: the static `env` block
404
+ * plus every declared secret resolved from the Worker `env`. A declared secret
405
+ * missing from the Worker env fails fast — starting the container without a
406
+ * credential it was promised yields far worse errors downstream.
407
+ */
282
408
  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 };
409
+ export { CONTAINER_EXEC_PATH,
410
+ /**
411
+ * `@lunora/container` — Cloudflare Containers for Lunora.
412
+ *
413
+ * This root export is Node-safe (no Cloudflare runtime imports): the
414
+ * `defineContainer` authoring surface, the naming/normalization helpers
415
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
416
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
417
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
418
+ * behind the `@lunora/container/do` subpath.
419
+ */
420
+ type ContainerAccessor,
421
+ /**
422
+ * `@lunora/container` — Cloudflare Containers for Lunora.
423
+ *
424
+ * This root export is Node-safe (no Cloudflare runtime imports): the
425
+ * `defineContainer` authoring surface, the naming/normalization helpers
426
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
427
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
428
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
429
+ * behind the `@lunora/container/do` subpath.
430
+ */
431
+ type ContainerBindingSpec, type ContainerConfig, type ContainerDefinition,
432
+ /**
433
+ * `@lunora/container` — Cloudflare Containers for Lunora.
434
+ *
435
+ * This root export is Node-safe (no Cloudflare runtime imports): the
436
+ * `defineContainer` authoring surface, the naming/normalization helpers
437
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
438
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
439
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
440
+ * behind the `@lunora/container/do` subpath.
441
+ */
442
+ type ContainerEgressControls, type ContainerExecOptions, type ContainerExecResult,
443
+ /**
444
+ * `@lunora/container` — Cloudflare Containers for Lunora.
445
+ *
446
+ * This root export is Node-safe (no Cloudflare runtime imports): the
447
+ * `defineContainer` authoring surface, the naming/normalization helpers
448
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
449
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
450
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
451
+ * behind the `@lunora/container/do` subpath.
452
+ */
453
+ type ContainerHandle, type ContainerImageSource,
454
+ /**
455
+ * `@lunora/container` — Cloudflare Containers for Lunora.
456
+ *
457
+ * This root export is Node-safe (no Cloudflare runtime imports): the
458
+ * `defineContainer` authoring surface, the naming/normalization helpers
459
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
460
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
461
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
462
+ * behind the `@lunora/container/do` subpath.
463
+ */
464
+ type ContainerInstanceHandle,
465
+ /**
466
+ * `@lunora/container` — Cloudflare Containers for Lunora.
467
+ *
468
+ * This root export is Node-safe (no Cloudflare runtime imports): the
469
+ * `defineContainer` authoring surface, the naming/normalization helpers
470
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
471
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
472
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
473
+ * behind the `@lunora/container/do` subpath.
474
+ */
475
+ type ContainerInstanceState,
476
+ /**
477
+ * `@lunora/container` — Cloudflare Containers for Lunora.
478
+ *
479
+ * This root export is Node-safe (no Cloudflare runtime imports): the
480
+ * `defineContainer` authoring surface, the naming/normalization helpers
481
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
482
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
483
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
484
+ * behind the `@lunora/container/do` subpath.
485
+ */
486
+ type ContainerNamespaceLike,
487
+ /**
488
+ * `@lunora/container` — Cloudflare Containers for Lunora.
489
+ *
490
+ * This root export is Node-safe (no Cloudflare runtime imports): the
491
+ * `defineContainer` authoring surface, the naming/normalization helpers
492
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
493
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
494
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
495
+ * behind the `@lunora/container/do` subpath.
496
+ */
497
+ type ContainerStartOptions,
498
+ /**
499
+ * `@lunora/container` — Cloudflare Containers for Lunora.
500
+ *
501
+ * This root export is Node-safe (no Cloudflare runtime imports): the
502
+ * `defineContainer` authoring surface, the naming/normalization helpers
503
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
504
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
505
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
506
+ * behind the `@lunora/container/do` subpath.
507
+ */
508
+ type ContainerTestHandler,
509
+ /**
510
+ * `@lunora/container` — Cloudflare Containers for Lunora.
511
+ *
512
+ * This root export is Node-safe (no Cloudflare runtime imports): the
513
+ * `defineContainer` authoring surface, the naming/normalization helpers
514
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
515
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
516
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
517
+ * behind the `@lunora/container/do` subpath.
518
+ */
519
+ type DurableObjectJurisdiction,
520
+ /**
521
+ * `@lunora/container` — Cloudflare Containers for Lunora.
522
+ *
523
+ * This root export is Node-safe (no Cloudflare runtime imports): the
524
+ * `defineContainer` authoring surface, the naming/normalization helpers
525
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
526
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
527
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
528
+ * behind the `@lunora/container/do` subpath.
529
+ */
530
+ type InstanceRetryOptions, type NormalizedContainerImage,
531
+ /**
532
+ * `@lunora/container` — Cloudflare Containers for Lunora.
533
+ *
534
+ * This root export is Node-safe (no Cloudflare runtime imports): the
535
+ * `defineContainer` authoring surface, the naming/normalization helpers
536
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
537
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
538
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
539
+ * behind the `@lunora/container/do` subpath.
540
+ */
541
+ type PoolOptions, containerBindingName, containerBuildTag, containerClassName, createContainerContext, createContainerTestContext, defineContainer, isContainerDefinition, normalizeContainerImage, resolveContainerEnvVariables as resolveContainerEnvVars };