@cat-factory/worker 0.192.0 → 0.194.1

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 (61) hide show
  1. package/dist/app.d.ts.map +1 -1
  2. package/dist/app.js +8 -0
  3. package/dist/app.js.map +1 -1
  4. package/dist/index.d.ts +2 -0
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +42 -10
  7. package/dist/index.js.map +1 -1
  8. package/dist/infrastructure/binaryGenerators.d.ts +23 -0
  9. package/dist/infrastructure/binaryGenerators.d.ts.map +1 -0
  10. package/dist/infrastructure/binaryGenerators.js +55 -0
  11. package/dist/infrastructure/binaryGenerators.js.map +1 -0
  12. package/dist/infrastructure/container-assembly.d.ts.map +1 -1
  13. package/dist/infrastructure/container-assembly.js +8 -2
  14. package/dist/infrastructure/container-assembly.js.map +1 -1
  15. package/dist/infrastructure/container-dispatchers.d.ts.map +1 -1
  16. package/dist/infrastructure/container-dispatchers.js +5 -3
  17. package/dist/infrastructure/container-dispatchers.js.map +1 -1
  18. package/dist/infrastructure/container-executor-deps.d.ts.map +1 -1
  19. package/dist/infrastructure/container-executor-deps.js +13 -2
  20. package/dist/infrastructure/container-executor-deps.js.map +1 -1
  21. package/dist/infrastructure/container-registries.d.ts +1 -1
  22. package/dist/infrastructure/container-registries.d.ts.map +1 -1
  23. package/dist/infrastructure/container-registries.js +14 -0
  24. package/dist/infrastructure/container-registries.js.map +1 -1
  25. package/dist/infrastructure/container.d.ts +1 -24
  26. package/dist/infrastructure/container.d.ts.map +1 -1
  27. package/dist/infrastructure/container.js +1 -50
  28. package/dist/infrastructure/container.js.map +1 -1
  29. package/dist/infrastructure/containers/CloudflareContainerTransport.d.ts +22 -5
  30. package/dist/infrastructure/containers/CloudflareContainerTransport.d.ts.map +1 -1
  31. package/dist/infrastructure/containers/CloudflareContainerTransport.js +53 -25
  32. package/dist/infrastructure/containers/CloudflareContainerTransport.js.map +1 -1
  33. package/dist/infrastructure/containers/ContainerInstanceRegistry.d.ts +26 -9
  34. package/dist/infrastructure/containers/ContainerInstanceRegistry.d.ts.map +1 -1
  35. package/dist/infrastructure/containers/ContainerInstanceRegistry.js +37 -13
  36. package/dist/infrastructure/containers/ContainerInstanceRegistry.js.map +1 -1
  37. package/dist/infrastructure/containers/UiTesterContainer.d.ts +4 -0
  38. package/dist/infrastructure/containers/UiTesterContainer.d.ts.map +1 -0
  39. package/dist/infrastructure/containers/UiTesterContainer.js +18 -0
  40. package/dist/infrastructure/containers/UiTesterContainer.js.map +1 -0
  41. package/dist/infrastructure/containers/deployJobDeps.d.ts +20 -0
  42. package/dist/infrastructure/containers/deployJobDeps.d.ts.map +1 -0
  43. package/dist/infrastructure/containers/deployJobDeps.js +38 -0
  44. package/dist/infrastructure/containers/deployJobDeps.js.map +1 -0
  45. package/dist/infrastructure/containers/runContainerNamespace.d.ts +38 -0
  46. package/dist/infrastructure/containers/runContainerNamespace.d.ts.map +1 -0
  47. package/dist/infrastructure/containers/runContainerNamespace.js +37 -0
  48. package/dist/infrastructure/containers/runContainerNamespace.js.map +1 -0
  49. package/dist/infrastructure/dispatchTokenMint.d.ts +17 -0
  50. package/dist/infrastructure/dispatchTokenMint.d.ts.map +1 -0
  51. package/dist/infrastructure/dispatchTokenMint.js +23 -0
  52. package/dist/infrastructure/dispatchTokenMint.js.map +1 -0
  53. package/dist/infrastructure/env.d.ts +13 -0
  54. package/dist/infrastructure/env.d.ts.map +1 -1
  55. package/dist/infrastructure/env.js.map +1 -1
  56. package/dist/infrastructure/repositories/D1LiveContainerRepository.d.ts +3 -3
  57. package/dist/infrastructure/repositories/D1LiveContainerRepository.d.ts.map +1 -1
  58. package/dist/infrastructure/repositories/D1LiveContainerRepository.js +12 -7
  59. package/dist/infrastructure/repositories/D1LiveContainerRepository.js.map +1 -1
  60. package/migrations/0094_live_container_image.sql +11 -0
  61. package/package.json +19 -18
@@ -1,4 +1,4 @@
1
- import { composePostMortem, CONTAINER_EVICTION_ERROR, HARNESS_SHUTDOWN_ERROR, harnessDispatchError, readRunnerDispatchAck, } from '@cat-factory/kernel';
1
+ import { composePostMortem, CONTAINER_EVICTION_ERROR, HARNESS_SHUTDOWN_ERROR, harnessDispatchError, readRunnerDispatchAck, containerKeyForRef, } from '@cat-factory/kernel';
2
2
  import { describeContainerExit, isRolloutSignal, } from './stopCause';
3
3
  // The human-readable message for a failed poll the transport maps to a container eviction. The
4
4
  // eviction verdict the engine acts on rides the STRUCTURED `RunnerJobView.evicted` field
@@ -98,19 +98,29 @@ const STOP_TIMEOUT_MS = 30_000;
98
98
  // (matches the harness server + the local Docker transport). Sent on every harness
99
99
  // call so a container that requires the secret accepts the Worker's dispatch/poll.
100
100
  const HARNESS_SECRET_HEADER = 'x-harness-secret';
101
+ /**
102
+ * The resolver a transport DEDICATED to one container class is built with: the deploy adapter's,
103
+ * and every test that drives the transport over a single fake namespace. It ignores the variant
104
+ * because such a transport is chosen by its caller, not by the job.
105
+ *
106
+ * It lives beside the transport rather than beside the resolver TYPE because constructing one is
107
+ * its only purpose.
108
+ */
109
+ export function fixedContainerNamespace(namespace) {
110
+ return () => namespace;
111
+ }
101
112
  export class CloudflareContainerTransport {
102
- namespace;
113
+ resolveNamespace;
103
114
  registry;
104
115
  sharedSecret;
105
116
  /** Backend id recorded in run diagnostics (per-run Cloudflare Container). */
106
117
  backend = 'cloudflare-container';
107
118
  constructor(
108
- // Either per-run container class: `ExecutionContainer` (the agent harness, bound as
109
- // `EXEC_CONTAINER`) or `DeployContainer` (the deploy harness, bound as `DEPLOY_CONTAINER`).
110
- // Both expose the same `/jobs` HTTP contract on 8080 plus `recentStopObservation`/`shutdown`,
111
- // so this transport drives either unchanged — a deploy-dedicated instance simply gets the
112
- // deploy namespace.
113
- namespace,
119
+ // Which per-run container class serves a given image variant. Every class (executor, UI
120
+ // tester, deploy harness) exposes the same `/jobs` HTTP contract on 8080 plus
121
+ // `recentStopObservation`/`shutdown`, so this transport drives any of them unchanged: a
122
+ // deploy-dedicated instance is simply built over a resolver pinned to that one namespace.
123
+ resolveNamespace,
114
124
  /** Live-container inventory + reaper kill path; absent in tests (reaping off). */
115
125
  registry,
116
126
  /**
@@ -121,7 +131,7 @@ export class CloudflareContainerTransport {
121
131
  * the local transport's behaviour.
122
132
  */
123
133
  sharedSecret) {
124
- this.namespace = namespace;
134
+ this.resolveNamespace = resolveNamespace;
125
135
  this.registry = registry;
126
136
  this.sharedSecret = sharedSecret;
127
137
  }
@@ -129,17 +139,36 @@ export class CloudflareContainerTransport {
129
139
  secretHeader() {
130
140
  return this.sharedSecret ? { [HARNESS_SECRET_HEADER]: this.sharedSecret } : {};
131
141
  }
132
- // NB: the `RunnerDispatchOptions` (provisioning hints) the port allows are
133
- // intentionally ignored here. A Cloudflare Container's instance type is fixed per
134
- // container class by the wrangler `[[containers]] instance_type` — there is no
135
- // per-DO/per-request sizing API — so a resolved instance-type id is meaningless on
136
- // this backend. Per-service sizing applies only to the backends that can honour it
137
- // (the self-hosted pool and the local Docker transport).
142
+ /**
143
+ * The Durable Object id addressing this job's container: the class that serves the ref's image
144
+ * variant, named by `containerKeyForRef`.
145
+ *
146
+ * Both halves of that come off the REF, which is why the variant rides there rather than only
147
+ * on the dispatch options: `poll`, `release` and `stopJob` get no options, and a run whose
148
+ * `tester-ui` step sits in its own container would otherwise be polled at the container its
149
+ * coder step is running in, reading as an evicted job while the browser one worked on.
150
+ */
151
+ stubFor(ref) {
152
+ const namespace = this.resolveNamespace(ref.image ?? 'default');
153
+ const id = namespace.idFromName(containerKeyForRef(ref));
154
+ return { id, stub: namespace.get(id) };
155
+ }
156
+ // NB: the SIZING hints in `RunnerDispatchOptions` are intentionally ignored here. A
157
+ // Cloudflare Container's instance type is fixed per container class by the wrangler
158
+ // `[[containers]] instance_type`, with no per-DO/per-request sizing API, so a resolved
159
+ // instance-type id is meaningless on this backend. Per-service sizing applies only to the
160
+ // backends that can honour it (the self-hosted pool and the local Docker transport).
161
+ //
162
+ // The `image` hint is NOT in that group: it selects the container class, which this backend
163
+ // does honour. It is read off the REF rather than the options, so `poll`/`release`/`stopJob`
164
+ // (which get no options) address the same container the dispatch started.
138
165
  async dispatch(ref, spec, kind = 'agent') {
139
- // The container is per-RUN (one Durable Object per run id), so every step of a run
140
- // dispatches to the same instance; the harness keys the job by `ref.jobId` (in the
141
- // spec body), unique per step, so siblings never collide in its registries.
142
- const stub = this.namespace.get(this.namespace.idFromName(ref.runId));
166
+ // The container is per-RUN AND per image variant, so every step of a run on the same image
167
+ // dispatches to the same instance; the harness keys the job by `ref.jobId` (in the spec
168
+ // body), unique per step, so siblings never collide in its registries. A step declaring a
169
+ // different image gets its own container, which is why the inventory key below is the
170
+ // variant-qualified one rather than the bare run id.
171
+ const { stub } = this.stubFor(ref);
143
172
  // One harness endpoint for every kind: POST /jobs with the kind in the body. The
144
173
  // harness reads `kind` to pick the validator + registry; the rest is the job spec.
145
174
  const res = await stub.fetch('http://container/jobs', {
@@ -162,7 +191,7 @@ export class CloudflareContainerTransport {
162
191
  // the reaper can find it if its run record ever diverges from reality. Idempotent
163
192
  // across a run's steps — the store preserves the earliest startedAt for a key.
164
193
  // Best-effort — the registry swallows store errors.
165
- await this.registry?.register(ref.runId, kind);
194
+ await this.registry?.register({ containerKey: containerKeyForRef(ref), kind, image: ref.image });
166
195
  // The harness's capability handshake rides the acceptance body. Read AFTER the registry
167
196
  // write so a malformed/absent body can never cost the reaper its record of a live container.
168
197
  return readRunnerDispatchAck(await safeJson(res));
@@ -191,8 +220,7 @@ export class CloudflareContainerTransport {
191
220
  // the closest thing to a stable "container id" to surface in the run's details (a
192
221
  // Cloudflare Container has no public URL). Derived here so the executor can show WHICH
193
222
  // container the run is on. Cheap (no extra round-trip): `idFromName` is local.
194
- const doId = this.namespace.idFromName(ref.runId);
195
- const stub = this.namespace.get(doId);
223
+ const { id: doId, stub } = this.stubFor(ref);
196
224
  let res;
197
225
  try {
198
226
  res = await stub.fetch(`http://container/jobs/${encodeURIComponent(ref.jobId)}`, {
@@ -241,10 +269,10 @@ export class CloudflareContainerTransport {
241
269
  // single step within it, so the whole run's container goes regardless).
242
270
  if (this.registry) {
243
271
  // The registry owns the single kill path (shutdown + inventory removal).
244
- await this.registry.release(ref.runId);
272
+ await this.registry.release(containerKeyForRef(ref), ref.image);
245
273
  return;
246
274
  }
247
- const stub = this.namespace.get(this.namespace.idFromName(ref.runId));
275
+ const { stub } = this.stubFor(ref);
248
276
  await stub.shutdown();
249
277
  }
250
278
  /**
@@ -259,7 +287,7 @@ export class CloudflareContainerTransport {
259
287
  * telling them to go and look.
260
288
  */
261
289
  async stopJob(ref) {
262
- const stub = this.namespace.get(this.namespace.idFromName(ref.runId));
290
+ const { stub } = this.stubFor(ref);
263
291
  try {
264
292
  const res = await stub.fetch(`http://container/jobs/${encodeURIComponent(ref.jobId)}`, {
265
293
  method: 'DELETE',
@@ -1 +1 @@
1
- {"version":3,"file":"CloudflareContainerTransport.js","sourceRoot":"","sources":["../../../src/infrastructure/containers/CloudflareContainerTransport.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"CloudflareContainerTransport.js","sourceRoot":"","sources":["../../../src/infrastructure/containers/CloudflareContainerTransport.ts"],"names":[],"mappings":"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"}
@@ -1,14 +1,21 @@
1
- import type { Clock } from '@cat-factory/kernel';
2
- import type { DurableObjectNamespace } from '@cloudflare/workers-types';
3
- import type { ExecutionContainer } from './ExecutionContainer';
1
+ import type { Clock, RunnerImageVariant } from '@cat-factory/kernel';
2
+ import type { ResolveRunContainerNamespace } from './runContainerNamespace';
4
3
  /** One live per-run container, as the registry records it. */
5
4
  export interface LiveContainerRecord {
6
- /** The idFromName() argument: the execution/bootstrap job id (also the run id). */
5
+ /** The idFromName() argument (kernel's `containerKeyForRef`): the run id, variant-qualified. */
7
6
  containerKey: string;
8
7
  /** The dispatch kind ('run' | 'blueprint' | 'bootstrap'); diagnostic only. */
9
8
  kind: string;
10
9
  /** Owning workspace, when known (the transport seam carries only the job id). */
11
10
  workspaceId?: string;
11
+ /**
12
+ * The executor image variant, which is WHICH CONTAINER CLASS holds this instance. Not
13
+ * diagnostic: the reaper kills through a Durable Object namespace, and the key alone cannot
14
+ * say which one, so a leaked UI-tester container would be looked up in the executor namespace,
15
+ * where `idFromName` happily returns a stub for a container that never existed and the kill
16
+ * reads as a success. Absent ⇒ the default executor class.
17
+ */
18
+ image?: RunnerImageVariant;
12
19
  /** Epoch ms of the FIRST dispatch = the container's true age. */
13
20
  startedAt: number;
14
21
  }
@@ -30,16 +37,24 @@ export interface LiveContainerStore {
30
37
  * reaper alike, so the two can never diverge.
31
38
  */
32
39
  export declare class ContainerInstanceRegistry {
33
- private readonly namespace;
40
+ /** Resolves the container class an image variant lives in, shared with the transport. */
41
+ private readonly resolveNamespace;
34
42
  private readonly store;
35
43
  private readonly clock;
36
- constructor(namespace: DurableObjectNamespace<ExecutionContainer>, store: LiveContainerStore, clock: Clock);
44
+ constructor(
45
+ /** Resolves the container class an image variant lives in, shared with the transport. */
46
+ resolveNamespace: ResolveRunContainerNamespace, store: LiveContainerStore, clock: Clock);
37
47
  /**
38
48
  * Record a freshly-dispatched container in the inventory. Best-effort: a write
39
49
  * failure must never break the dispatch it is bookkeeping for, and the earliest
40
50
  * `startedAt` is preserved across replayed dispatches (so age stays truthful).
41
51
  */
42
- register(containerKey: string, kind: string, workspaceId?: string): Promise<void>;
52
+ register(record: {
53
+ containerKey: string;
54
+ kind: string;
55
+ workspaceId?: string;
56
+ image?: RunnerImageVariant;
57
+ }): Promise<void>;
43
58
  /**
44
59
  * Reclaim a container now and drop its inventory row — the single kill path.
45
60
  * Idempotent: `ExecutionContainer.shutdown` swallows "already gone", so this is a
@@ -47,16 +62,18 @@ export declare class ContainerInstanceRegistry {
47
62
  * SIGKILL resolves, so a (rare) transport-level failure leaves the row for the
48
63
  * reaper to retry rather than silently dropping a still-live container.
49
64
  */
50
- release(containerKey: string): Promise<void>;
65
+ release(containerKey: string, image?: RunnerImageVariant): Promise<void>;
51
66
  /**
52
67
  * The load-bearing backstop: kill every container whose first dispatch is older
53
68
  * than `epochMs` (its legitimate maximum lifetime has elapsed). With normal runs
54
69
  * self-reclaiming on their terminal path, a reaped container is a genuine LEAK, so
55
70
  * each kill is logged loudly. One wedged container never aborts the sweep — each
56
- * release is isolated. Returns how many were actually reaped.
71
+ * release is isolated. Returns how many were actually reaped, and how many the sweep
72
+ * could not even address (see below).
57
73
  */
58
74
  reapStaleBefore(epochMs: number): Promise<{
59
75
  reaped: number;
76
+ unreachable: number;
60
77
  }>;
61
78
  }
62
79
  //# sourceMappingURL=ContainerInstanceRegistry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ContainerInstanceRegistry.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/containers/ContainerInstanceRegistry.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAChD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAA;AACvE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAW9D,8DAA8D;AAC9D,MAAM,WAAW,mBAAmB;IAClC,mFAAmF;IACnF,YAAY,EAAE,MAAM,CAAA;IACpB,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAA;IACZ,iFAAiF;IACjF,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,GAAG,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/C,MAAM,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3C,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAAA;CACnE;AAED;;;;;;GAMG;AACH,qBAAa,yBAAyB;IAElC,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,KAAK;IAHxB,YACmB,SAAS,EAAE,sBAAsB,CAAC,kBAAkB,CAAC,EACrD,KAAK,EAAE,kBAAkB,EACzB,KAAK,EAAE,KAAK,EAC3B;IAEJ;;;;OAIG;IACG,QAAQ,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAUtF;IAED;;;;;;OAMG;IACG,OAAO,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGjD;IAED;;;;;;OAMG;IACG,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAsBlE;CACF"}
1
+ {"version":3,"file":"ContainerInstanceRegistry.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/containers/ContainerInstanceRegistry.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACpE,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,yBAAyB,CAAA;AAW3E,8DAA8D;AAC9D,MAAM,WAAW,mBAAmB;IAClC,gGAAgG;IAChG,YAAY,EAAE,MAAM,CAAA;IACpB,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAA;IACZ,iFAAiF;IACjF,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,kBAAkB,CAAA;IAC1B,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,GAAG,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/C,MAAM,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3C,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAAA;CACnE;AAED;;;;;;GAMG;AACH,qBAAa,yBAAyB;IAElC,yFAAyF;IACzF,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,KAAK;IAJxB;IACE,yFAAyF;IACxE,gBAAgB,EAAE,4BAA4B,EAC9C,KAAK,EAAE,kBAAkB,EACzB,KAAK,EAAE,KAAK,EAC3B;IAEJ;;;;OAIG;IACG,QAAQ,CAAC,MAAM,EAAE;QACrB,YAAY,EAAE,MAAM,CAAA;QACpB,IAAI,EAAE,MAAM,CAAA;QACZ,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,KAAK,CAAC,EAAE,kBAAkB,CAAA;KAC3B,GAAG,OAAO,CAAC,IAAI,CAAC,CAWhB;IAED;;;;;;OAMG;IACG,OAAO,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAI7E;IAED;;;;;;;OAOG;IACG,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CA4CvF;CACF"}
@@ -1,4 +1,4 @@
1
- import { getErrorMessage } from '@cat-factory/kernel';
1
+ import { getErrorMessage, getErrorReason, RUNNER_IMAGE_UNWIRED_REASON } from '@cat-factory/kernel';
2
2
  import { logger } from '../observability/logger';
3
3
  /**
4
4
  * Owns the per-run container namespace + the live-container inventory. It is the
@@ -8,11 +8,13 @@ import { logger } from '../observability/logger';
8
8
  * reaper alike, so the two can never diverge.
9
9
  */
10
10
  export class ContainerInstanceRegistry {
11
- namespace;
11
+ resolveNamespace;
12
12
  store;
13
13
  clock;
14
- constructor(namespace, store, clock) {
15
- this.namespace = namespace;
14
+ constructor(
15
+ /** Resolves the container class an image variant lives in, shared with the transport. */
16
+ resolveNamespace, store, clock) {
17
+ this.resolveNamespace = resolveNamespace;
16
18
  this.store = store;
17
19
  this.clock = clock;
18
20
  }
@@ -21,14 +23,15 @@ export class ContainerInstanceRegistry {
21
23
  * failure must never break the dispatch it is bookkeeping for, and the earliest
22
24
  * `startedAt` is preserved across replayed dispatches (so age stays truthful).
23
25
  */
24
- async register(containerKey, kind, workspaceId) {
26
+ async register(record) {
25
27
  try {
26
- await this.store.add({ containerKey, kind, workspaceId, startedAt: this.clock.now() });
28
+ await this.store.add({ ...record, startedAt: this.clock.now() });
27
29
  }
28
30
  catch (error) {
29
31
  logger.warn('container-registry: failed to record live container (continuing)', {
30
- containerKey,
31
- kind,
32
+ containerKey: record.containerKey,
33
+ kind: record.kind,
34
+ image: record.image,
32
35
  err: getErrorMessage(error),
33
36
  });
34
37
  }
@@ -40,8 +43,9 @@ export class ContainerInstanceRegistry {
40
43
  * SIGKILL resolves, so a (rare) transport-level failure leaves the row for the
41
44
  * reaper to retry rather than silently dropping a still-live container.
42
45
  */
43
- async release(containerKey) {
44
- await this.namespace.get(this.namespace.idFromName(containerKey)).shutdown();
46
+ async release(containerKey, image) {
47
+ const namespace = this.resolveNamespace(image ?? 'default');
48
+ await namespace.get(namespace.idFromName(containerKey)).shutdown();
45
49
  await this.store.remove(containerKey);
46
50
  }
47
51
  /**
@@ -49,23 +53,43 @@ export class ContainerInstanceRegistry {
49
53
  * than `epochMs` (its legitimate maximum lifetime has elapsed). With normal runs
50
54
  * self-reclaiming on their terminal path, a reaped container is a genuine LEAK, so
51
55
  * each kill is logged loudly. One wedged container never aborts the sweep — each
52
- * release is isolated. Returns how many were actually reaped.
56
+ * release is isolated. Returns how many were actually reaped, and how many the sweep
57
+ * could not even address (see below).
53
58
  */
54
59
  async reapStaleBefore(epochMs) {
55
60
  const stale = await this.store.listStartedBefore(epochMs);
56
61
  let reaped = 0;
62
+ let unreachable = 0;
57
63
  for (const record of stale) {
58
64
  logger.warn('container-reaper: killing leaked container past its max lifetime', {
59
65
  containerKey: record.containerKey,
60
66
  kind: record.kind,
67
+ image: record.image,
61
68
  workspaceId: record.workspaceId,
62
69
  ageMs: this.clock.now() - record.startedAt,
63
70
  });
64
71
  try {
65
- await this.release(record.containerKey);
72
+ await this.release(record.containerKey, record.image);
66
73
  reaped++;
67
74
  }
68
75
  catch (error) {
76
+ if (getErrorReason(error) === RUNNER_IMAGE_UNWIRED_REASON) {
77
+ // The class this row's variant lives in is no longer BOUND (the binding was removed, or
78
+ // the Worker rolled back past it), so there is no namespace to get a stub from and no
79
+ // retry can produce one. Left in place the row re-throws on every pass for ever, which
80
+ // is a permanent error rate that names nothing new and still never kills the container.
81
+ // So it is dropped, and the fact the reaper cannot act on it is stated ONCE, loudly,
82
+ // with the binding to restore.
83
+ unreachable++;
84
+ logger.error('container-reaper: cannot address a leaked container, its class is not bound', {
85
+ containerKey: record.containerKey,
86
+ image: record.image,
87
+ workspaceId: record.workspaceId,
88
+ err: getErrorMessage(error),
89
+ });
90
+ await this.store.remove(record.containerKey);
91
+ continue;
92
+ }
69
93
  // Leave the row in place so the next pass retries this one.
70
94
  logger.error('container-reaper: failed to kill leaked container (will retry next pass)', {
71
95
  containerKey: record.containerKey,
@@ -73,7 +97,7 @@ export class ContainerInstanceRegistry {
73
97
  });
74
98
  }
75
99
  }
76
- return { reaped };
100
+ return { reaped, unreachable };
77
101
  }
78
102
  }
79
103
  //# sourceMappingURL=ContainerInstanceRegistry.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ContainerInstanceRegistry.js","sourceRoot":"","sources":["../../../src/infrastructure/containers/ContainerInstanceRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAIrD,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAA;AAiChD;;;;;;GAMG;AACH,MAAM,OAAO,yBAAyB;IAEjB,SAAS;IACT,KAAK;IACL,KAAK;IAHxB,YACmB,SAAqD,EACrD,KAAyB,EACzB,KAAY;yBAFZ,SAAS;qBACT,KAAK;qBACL,KAAK;IACrB,CAAC;IAEJ;;;;OAIG;IACH,KAAK,CAAC,QAAQ,CAAC,YAAoB,EAAE,IAAY,EAAE,WAAoB;QACrE,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;QACxF,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,IAAI,CAAC,kEAAkE,EAAE;gBAC9E,YAAY;gBACZ,IAAI;gBACJ,GAAG,EAAE,eAAe,CAAC,KAAK,CAAC;aAC5B,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,OAAO,CAAC,YAAoB;QAChC,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAA;QAC5E,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,CAAA;IACvC,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,eAAe,CAAC,OAAe;QACnC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAA;QACzD,IAAI,MAAM,GAAG,CAAC,CAAA;QACd,KAAK,MAAM,MAAM,IAAI,KAAK,EAAE,CAAC;YAC3B,MAAM,CAAC,IAAI,CAAC,kEAAkE,EAAE;gBAC9E,YAAY,EAAE,MAAM,CAAC,YAAY;gBACjC,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,WAAW,EAAE,MAAM,CAAC,WAAW;gBAC/B,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,SAAS;aAC3C,CAAC,CAAA;YACF,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAA;gBACvC,MAAM,EAAE,CAAA;YACV,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,4DAA4D;gBAC5D,MAAM,CAAC,KAAK,CAAC,0EAA0E,EAAE;oBACvF,YAAY,EAAE,MAAM,CAAC,YAAY;oBACjC,GAAG,EAAE,eAAe,CAAC,KAAK,CAAC;iBAC5B,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,CAAA;IACnB,CAAC;CACF"}
1
+ {"version":3,"file":"ContainerInstanceRegistry.js","sourceRoot":"","sources":["../../../src/infrastructure/containers/ContainerInstanceRegistry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { RunContainer } from './RunContainer';
2
+ export declare class UiTesterContainer extends RunContainer {
3
+ }
4
+ //# sourceMappingURL=UiTesterContainer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UiTesterContainer.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/containers/UiTesterContainer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAgB7C,qBAAa,iBAAkB,SAAQ,YAAY;CAAG"}
@@ -0,0 +1,18 @@
1
+ import { RunContainer } from './RunContainer';
2
+ // One UI-TESTER container per run, mirroring {@link ExecutionContainer} but pulling the heavier
3
+ // UI image (`Dockerfile.ui`: the executor harness plus Playwright + Chromium, pnpm/yarn, the
4
+ // `serve` static server and a headless JRE + WireMock) instead of the plain executor image. A
5
+ // Cloudflare Container's image is pinned per container CLASS, so the `image: 'ui'` dispatch
6
+ // variant needs its own class; this one is bound as `UI_CONTAINER`, so the browser and the JRE
7
+ // never bloat the cold start of every coder/merger/ci-fixer run.
8
+ //
9
+ // It is a SECOND container for the same run rather than a replacement: a run mixes ordinary
10
+ // agent steps with a `tester-ui` step, and a per-run container cannot change image mid-run. The
11
+ // two are addressed by `containerKeyForRef`, which qualifies the run id with the variant.
12
+ //
13
+ // The UI image serves the SAME `POST /jobs` + `GET /jobs/{id}` contract on 8080 (it layers onto
14
+ // the same harness), so the generic `CloudflareContainerTransport` drives it unchanged, and its
15
+ // lifecycle behaviour is {@link RunContainer}'s, shared byte-for-byte with the agent one.
16
+ export class UiTesterContainer extends RunContainer {
17
+ }
18
+ //# sourceMappingURL=UiTesterContainer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UiTesterContainer.js","sourceRoot":"","sources":["../../../src/infrastructure/containers/UiTesterContainer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAE7C,gGAAgG;AAChG,6FAA6F;AAC7F,8FAA8F;AAC9F,4FAA4F;AAC5F,+FAA+F;AAC/F,iEAAiE;AACjE,EAAE;AACF,4FAA4F;AAC5F,gGAAgG;AAChG,0FAA0F;AAC1F,EAAE;AACF,gGAAgG;AAChG,gGAAgG;AAChG,0FAA0F;AAC1F,MAAM,OAAO,iBAAkB,SAAQ,YAAY;CAAG"}
@@ -0,0 +1,20 @@
1
+ import type { Clock } from '@cat-factory/kernel';
2
+ import type { CoreDependencies } from '@cat-factory/orchestration';
3
+ import type { D1Database } from '@cloudflare/workers-types';
4
+ import type { AppConfig } from '../config';
5
+ import type { Env } from '../env';
6
+ /**
7
+ * Wire the async, container-backed Kubernetes deploy lifecycle (slice 9's
8
+ * `EnvironmentProvisioningService` seams) onto the Worker facade: a `deployJobClient` that
9
+ * dispatches/polls/releases a `deploy`-kind job on the per-run `DeployContainer` (the separate
10
+ * deploy-harness image — real `kubectl`/`kustomize`/`helm`), plus `resolveDeployCloneTarget` to
11
+ * hand the container concrete manifests-repo clone coords + a short-lived install token.
12
+ *
13
+ * Gated on the environments module, the `DEPLOY_CONTAINER` binding AND the GitHub App
14
+ * (the clone-target seam needs to mint install tokens + resolve a block's repo). Absent any
15
+ * prerequisite ⇒ `{}` — a render-needing config then fails loudly (the synchronous raw-manifest
16
+ * REST path is unaffected), exactly the unwired behaviour slice 9 shipped. Mirrors Node's pool
17
+ * deploy wiring; the deploy container is the Worker's analogue of Node's self-hosted pool.
18
+ */
19
+ export declare function selectDeployDeps(env: Env, config: AppConfig, db: D1Database, clock: Clock): Partial<CoreDependencies>;
20
+ //# sourceMappingURL=deployJobDeps.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deployJobDeps.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/containers/deployJobDeps.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAChD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAA;AAElE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AAC3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAC1C,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAA;AAQjC;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,SAAS,EACjB,EAAE,EAAE,UAAU,EACd,KAAK,EAAE,KAAK,GACX,OAAO,CAAC,gBAAgB,CAAC,CA4B3B"}
@@ -0,0 +1,38 @@
1
+ import { makeResolveDeployCloneTarget, RunnerJobClient } from '@cat-factory/server';
2
+ import { buildAppRegistry, buildResolveRepoTarget } from '../container-vcs-identity';
3
+ import { workerDispatchTokenMint } from '../dispatchTokenMint';
4
+ import { CloudflareContainerTransport, fixedContainerNamespace, } from './CloudflareContainerTransport';
5
+ /**
6
+ * Wire the async, container-backed Kubernetes deploy lifecycle (slice 9's
7
+ * `EnvironmentProvisioningService` seams) onto the Worker facade: a `deployJobClient` that
8
+ * dispatches/polls/releases a `deploy`-kind job on the per-run `DeployContainer` (the separate
9
+ * deploy-harness image — real `kubectl`/`kustomize`/`helm`), plus `resolveDeployCloneTarget` to
10
+ * hand the container concrete manifests-repo clone coords + a short-lived install token.
11
+ *
12
+ * Gated on the environments module, the `DEPLOY_CONTAINER` binding AND the GitHub App
13
+ * (the clone-target seam needs to mint install tokens + resolve a block's repo). Absent any
14
+ * prerequisite ⇒ `{}` — a render-needing config then fails loudly (the synchronous raw-manifest
15
+ * REST path is unaffected), exactly the unwired behaviour slice 9 shipped. Mirrors Node's pool
16
+ * deploy wiring; the deploy container is the Worker's analogue of Node's self-hosted pool.
17
+ */
18
+ export function selectDeployDeps(env, config, db, clock) {
19
+ if (!config.environments.encryptionKey ||
20
+ !env.DEPLOY_CONTAINER ||
21
+ !config.github.enabled ||
22
+ !env.GITHUB_APP_PRIVATE_KEY) {
23
+ return {};
24
+ }
25
+ // A deploy-DEDICATED transport: the deploy job's `ref.runId` addresses a `DeployContainer`
26
+ // instance in its own DO namespace (no collision with the agent `EXEC_CONTAINER`), and the
27
+ // harness keys the job by `ref.jobId`. No instance registry is wired (the `sleepAfter` idle
28
+ // timer + explicit `release` reclaim it), so cross-namespace reaping stays the exec
29
+ // container's concern. The client is deploy-only, so `poll`/`release` need no per-ref routing.
30
+ const deployTransport = new CloudflareContainerTransport(fixedContainerNamespace(env.DEPLOY_CONTAINER), undefined, env.HARNESS_SHARED_SECRET?.trim() || undefined);
31
+ const registry = buildAppRegistry(env, config, db, clock);
32
+ return {
33
+ deployJobClient: new RunnerJobClient(async () => deployTransport),
34
+ // Narrowed to the repo being rendered, like every other container dispatch.
35
+ resolveDeployCloneTarget: makeResolveDeployCloneTarget(buildResolveRepoTarget(db), workerDispatchTokenMint(registry)),
36
+ };
37
+ }
38
+ //# sourceMappingURL=deployJobDeps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deployJobDeps.js","sourceRoot":"","sources":["../../../src/infrastructure/containers/deployJobDeps.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,4BAA4B,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAInF,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAA;AACpF,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAA;AAC9D,OAAO,EACL,4BAA4B,EAC5B,uBAAuB,GACxB,MAAM,gCAAgC,CAAA;AAEvC;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,gBAAgB,CAC9B,GAAQ,EACR,MAAiB,EACjB,EAAc,EACd,KAAY;IAEZ,IACE,CAAC,MAAM,CAAC,YAAY,CAAC,aAAa;QAClC,CAAC,GAAG,CAAC,gBAAgB;QACrB,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO;QACtB,CAAC,GAAG,CAAC,sBAAsB,EAC3B,CAAC;QACD,OAAO,EAAE,CAAA;IACX,CAAC;IACD,2FAA2F;IAC3F,2FAA2F;IAC3F,4FAA4F;IAC5F,oFAAoF;IACpF,+FAA+F;IAC/F,MAAM,eAAe,GAAG,IAAI,4BAA4B,CACtD,uBAAuB,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAC7C,SAAS,EACT,GAAG,CAAC,qBAAqB,EAAE,IAAI,EAAE,IAAI,SAAS,CAC/C,CAAA;IACD,MAAM,QAAQ,GAAG,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,CAAC,CAAA;IACzD,OAAO;QACL,eAAe,EAAE,IAAI,eAAe,CAAC,KAAK,IAAI,EAAE,CAAC,eAAe,CAAC;QACjE,4EAA4E;QAC5E,wBAAwB,EAAE,4BAA4B,CACpD,sBAAsB,CAAC,EAAE,CAAC,EAC1B,uBAAuB,CAAC,QAAQ,CAAC,CAClC;KACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,38 @@
1
+ import type { RunnerImageVariant } from '@cat-factory/kernel';
2
+ import type { DurableObjectNamespace } from '@cloudflare/workers-types';
3
+ import type { DeployContainer } from './DeployContainer';
4
+ import type { ExecutionContainer } from './ExecutionContainer';
5
+ import type { UiTesterContainer } from './UiTesterContainer';
6
+ /**
7
+ * Any per-run container class. The three exist only because a Cloudflare Container's image is
8
+ * pinned per container CLASS by the wrangler `[[containers]]` block; each serves the same
9
+ * `/jobs` HTTP contract and the same `RunContainer` lifecycle, so a namespace of any of them
10
+ * drives the same transport.
11
+ */
12
+ export type RunContainerNamespace = DurableObjectNamespace<ExecutionContainer> | DurableObjectNamespace<DeployContainer> | DurableObjectNamespace<UiTesterContainer>;
13
+ /**
14
+ * Picks the container class that serves an image variant. A transport built for ONE class (the
15
+ * deploy path) ignores the variant; the agent path routes `ui` to its own class.
16
+ *
17
+ * It THROWS for a variant this deployment has not wired, and that refusal is the point. The
18
+ * alternative, falling back to the default class, is what this seam exists to stop: a
19
+ * browser-driven tester silently lands on an image with no browser, pays for a checkout, an
20
+ * install and the model's first turns, then reports an `abort` a reader cannot tell apart from
21
+ * an app that would not boot. Refusing costs one dispatch and names the binding to add.
22
+ */
23
+ export type ResolveRunContainerNamespace = (variant: RunnerImageVariant) => RunContainerNamespace;
24
+ /**
25
+ * The agent path's resolver: the plain executor class for everything, the UI class for the `ui`
26
+ * variant (today only `tester-ui` declares it).
27
+ *
28
+ * `deploy` never reaches here. The deploy adapter builds its own dedicated transport over
29
+ * `DEPLOY_CONTAINER`, so a `deploy` variant arriving on the AGENT path is a wiring mistake in a
30
+ * kind's registration rather than a missing binding. It is refused too, and separately, because
31
+ * the two need different fixes: one is an operator adding a binding, the other a developer
32
+ * correcting a registry entry.
33
+ */
34
+ export declare function agentContainerNamespace(bindings: {
35
+ exec: DurableObjectNamespace<ExecutionContainer>;
36
+ ui?: DurableObjectNamespace<UiTesterContainer>;
37
+ }): ResolveRunContainerNamespace;
38
+ //# sourceMappingURL=runContainerNamespace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runContainerNamespace.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/containers/runContainerNamespace.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAC7D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAA;AACvE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACxD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAC9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAE5D;;;;;GAKG;AACH,MAAM,MAAM,qBAAqB,GAC7B,sBAAsB,CAAC,kBAAkB,CAAC,GAC1C,sBAAsB,CAAC,eAAe,CAAC,GACvC,sBAAsB,CAAC,iBAAiB,CAAC,CAAA;AAE7C;;;;;;;;;GASG;AACH,MAAM,MAAM,4BAA4B,GAAG,CAAC,OAAO,EAAE,kBAAkB,KAAK,qBAAqB,CAAA;AAEjG;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE;IAChD,IAAI,EAAE,sBAAsB,CAAC,kBAAkB,CAAC,CAAA;IAChD,EAAE,CAAC,EAAE,sBAAsB,CAAC,iBAAiB,CAAC,CAAA;CAC/C,GAAG,4BAA4B,CA8B/B"}
@@ -0,0 +1,37 @@
1
+ import { RUNNER_IMAGE_UNWIRED_REASON, UnavailableError, unservableImageVariant, } from '@cat-factory/kernel';
2
+ /**
3
+ * The agent path's resolver: the plain executor class for everything, the UI class for the `ui`
4
+ * variant (today only `tester-ui` declares it).
5
+ *
6
+ * `deploy` never reaches here. The deploy adapter builds its own dedicated transport over
7
+ * `DEPLOY_CONTAINER`, so a `deploy` variant arriving on the AGENT path is a wiring mistake in a
8
+ * kind's registration rather than a missing binding. It is refused too, and separately, because
9
+ * the two need different fixes: one is an operator adding a binding, the other a developer
10
+ * correcting a registry entry.
11
+ */
12
+ export function agentContainerNamespace(bindings) {
13
+ return (variant) => {
14
+ if (variant === 'ui') {
15
+ if (bindings.ui)
16
+ return bindings.ui;
17
+ throw new UnavailableError('This step runs on the UI-tester executor image (Playwright + a browser), but this ' +
18
+ 'deployment binds no UI-tester container class, so there is nothing to dispatch it to. ' +
19
+ 'Add a `[[containers]]` class named UiTesterContainer pinned to the published ' +
20
+ 'cat-factory-executor-ui image plus its UI_CONTAINER durable-object binding, then ' +
21
+ 'redeploy. Until then, drop the `tester-ui` step from the pipeline: the ' +
22
+ 'visual-confirmation gate still runs on screenshots a person uploads.', RUNNER_IMAGE_UNWIRED_REASON, { image: variant, binding: 'UI_CONTAINER' });
23
+ }
24
+ if (variant === 'deploy') {
25
+ throw new UnavailableError('An agent step declared the `deploy` executor image, which the agent runner path does ' +
26
+ 'not serve: deploy jobs run through the environment-provisioning adapter and its own ' +
27
+ 'DEPLOY_CONTAINER transport. Correct the agent kind’s registration.', RUNNER_IMAGE_UNWIRED_REASON, { image: variant, binding: 'DEPLOY_CONTAINER' });
28
+ }
29
+ if (variant === 'default')
30
+ return bindings.exec;
31
+ // Exhaustive, so a variant added to the union fails the BUILD here rather than routing
32
+ // silently to the executor class — the same fall-through the local transport's `imageFor`
33
+ // had, and the one this whole seam exists to refuse.
34
+ return unservableImageVariant(variant);
35
+ };
36
+ }
37
+ //# sourceMappingURL=runContainerNamespace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runContainerNamespace.js","sourceRoot":"","sources":["../../../src/infrastructure/containers/runContainerNamespace.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,2BAA2B,EAC3B,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,qBAAqB,CAAA;AA8B5B;;;;;;;;;GASG;AACH,MAAM,UAAU,uBAAuB,CAAC,QAGvC;IACC,OAAO,CAAC,OAAO,EAAE,EAAE;QACjB,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,IAAI,QAAQ,CAAC,EAAE;gBAAE,OAAO,QAAQ,CAAC,EAAE,CAAA;YACnC,MAAM,IAAI,gBAAgB,CACxB,oFAAoF;gBAClF,wFAAwF;gBACxF,+EAA+E;gBAC/E,mFAAmF;gBACnF,yEAAyE;gBACzE,sEAAsE,EACxE,2BAA2B,EAC3B,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,CAC5C,CAAA;QACH,CAAC;QACD,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;YACzB,MAAM,IAAI,gBAAgB,CACxB,uFAAuF;gBACrF,sFAAsF;gBACtF,oEAAoE,EACtE,2BAA2B,EAC3B,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,kBAAkB,EAAE,CAChD,CAAA;QACH,CAAC;QACD,IAAI,OAAO,KAAK,SAAS;YAAE,OAAO,QAAQ,CAAC,IAAI,CAAA;QAC/C,uFAAuF;QACvF,0FAA0F;QAC1F,qDAAqD;QACrD,OAAO,sBAAsB,CAAC,OAAO,CAAC,CAAA;IACxC,CAAC,CAAA;AACH,CAAC"}
@@ -0,0 +1,17 @@
1
+ import { type GitHubAppRegistry, type MintInstallationToken } from '@cat-factory/server';
2
+ /**
3
+ * The clone/push credential for a Worker container dispatch: the App registry's mint, narrowed by
4
+ * the shared builder to the repos that dispatch resolved. Every Worker site that hands a container
5
+ * a GitHub token goes through this, so none of them can quietly fall back to an installation-wide
6
+ * one. No `resolveRunInitiatorToken`: the step executor composes its own mint with that chain (see
7
+ * `container-executor-deps.ts`), while bootstrap, repair and the deploy clone have no initiator to
8
+ * act as, and saying so once here beats three call sites each omitting it by accident.
9
+ *
10
+ * It sits in a LEAF module rather than in the composition root because three of its callers are
11
+ * modules the root itself pulls in: importing it back out of `container.ts` made each of them
12
+ * depend on the whole root, and for `containers/deployJobDeps.ts` (which the root re-exported) it
13
+ * closed a genuine cycle that only stayed harmless while every reference sat inside a function
14
+ * body.
15
+ */
16
+ export declare function workerDispatchTokenMint(registry: GitHubAppRegistry): MintInstallationToken;
17
+ //# sourceMappingURL=dispatchTokenMint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatchTokenMint.d.ts","sourceRoot":"","sources":["../../src/infrastructure/dispatchTokenMint.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,iBAAiB,EAEtB,KAAK,qBAAqB,EAE3B,MAAM,qBAAqB,CAAA;AAE5B;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,qBAAqB,CAM1F"}