@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.
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +8 -0
- package/dist/app.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +42 -10
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/binaryGenerators.d.ts +23 -0
- package/dist/infrastructure/binaryGenerators.d.ts.map +1 -0
- package/dist/infrastructure/binaryGenerators.js +55 -0
- package/dist/infrastructure/binaryGenerators.js.map +1 -0
- package/dist/infrastructure/container-assembly.d.ts.map +1 -1
- package/dist/infrastructure/container-assembly.js +8 -2
- package/dist/infrastructure/container-assembly.js.map +1 -1
- package/dist/infrastructure/container-dispatchers.d.ts.map +1 -1
- package/dist/infrastructure/container-dispatchers.js +5 -3
- package/dist/infrastructure/container-dispatchers.js.map +1 -1
- package/dist/infrastructure/container-executor-deps.d.ts.map +1 -1
- package/dist/infrastructure/container-executor-deps.js +13 -2
- package/dist/infrastructure/container-executor-deps.js.map +1 -1
- package/dist/infrastructure/container-registries.d.ts +1 -1
- package/dist/infrastructure/container-registries.d.ts.map +1 -1
- package/dist/infrastructure/container-registries.js +14 -0
- package/dist/infrastructure/container-registries.js.map +1 -1
- package/dist/infrastructure/container.d.ts +1 -24
- package/dist/infrastructure/container.d.ts.map +1 -1
- package/dist/infrastructure/container.js +1 -50
- package/dist/infrastructure/container.js.map +1 -1
- package/dist/infrastructure/containers/CloudflareContainerTransport.d.ts +22 -5
- package/dist/infrastructure/containers/CloudflareContainerTransport.d.ts.map +1 -1
- package/dist/infrastructure/containers/CloudflareContainerTransport.js +53 -25
- package/dist/infrastructure/containers/CloudflareContainerTransport.js.map +1 -1
- package/dist/infrastructure/containers/ContainerInstanceRegistry.d.ts +26 -9
- package/dist/infrastructure/containers/ContainerInstanceRegistry.d.ts.map +1 -1
- package/dist/infrastructure/containers/ContainerInstanceRegistry.js +37 -13
- package/dist/infrastructure/containers/ContainerInstanceRegistry.js.map +1 -1
- package/dist/infrastructure/containers/UiTesterContainer.d.ts +4 -0
- package/dist/infrastructure/containers/UiTesterContainer.d.ts.map +1 -0
- package/dist/infrastructure/containers/UiTesterContainer.js +18 -0
- package/dist/infrastructure/containers/UiTesterContainer.js.map +1 -0
- package/dist/infrastructure/containers/deployJobDeps.d.ts +20 -0
- package/dist/infrastructure/containers/deployJobDeps.d.ts.map +1 -0
- package/dist/infrastructure/containers/deployJobDeps.js +38 -0
- package/dist/infrastructure/containers/deployJobDeps.js.map +1 -0
- package/dist/infrastructure/containers/runContainerNamespace.d.ts +38 -0
- package/dist/infrastructure/containers/runContainerNamespace.d.ts.map +1 -0
- package/dist/infrastructure/containers/runContainerNamespace.js +37 -0
- package/dist/infrastructure/containers/runContainerNamespace.js.map +1 -0
- package/dist/infrastructure/dispatchTokenMint.d.ts +17 -0
- package/dist/infrastructure/dispatchTokenMint.d.ts.map +1 -0
- package/dist/infrastructure/dispatchTokenMint.js +23 -0
- package/dist/infrastructure/dispatchTokenMint.js.map +1 -0
- package/dist/infrastructure/env.d.ts +13 -0
- package/dist/infrastructure/env.d.ts.map +1 -1
- package/dist/infrastructure/env.js.map +1 -1
- package/dist/infrastructure/repositories/D1LiveContainerRepository.d.ts +3 -3
- package/dist/infrastructure/repositories/D1LiveContainerRepository.d.ts.map +1 -1
- package/dist/infrastructure/repositories/D1LiveContainerRepository.js +12 -7
- package/dist/infrastructure/repositories/D1LiveContainerRepository.js.map +1 -1
- package/migrations/0094_live_container_image.sql +11 -0
- 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
|
-
|
|
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
|
-
//
|
|
109
|
-
//
|
|
110
|
-
//
|
|
111
|
-
//
|
|
112
|
-
|
|
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.
|
|
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
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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
|
|
140
|
-
// dispatches to the same instance; the harness keys the job by `ref.jobId` (in the
|
|
141
|
-
//
|
|
142
|
-
|
|
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
|
|
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.
|
|
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.
|
|
272
|
+
await this.registry.release(containerKeyForRef(ref), ref.image);
|
|
245
273
|
return;
|
|
246
274
|
}
|
|
247
|
-
const stub = this.
|
|
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.
|
|
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":"AAAA,OAAO,EACL,iBAAiB,EACjB,wBAAwB,EACxB,sBAAsB,EACtB,oBAAoB,EACpB,qBAAqB,
|
|
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 {
|
|
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
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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,
|
|
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
|
-
|
|
11
|
+
resolveNamespace;
|
|
12
12
|
store;
|
|
13
13
|
clock;
|
|
14
|
-
constructor(
|
|
15
|
-
|
|
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(
|
|
26
|
+
async register(record) {
|
|
25
27
|
try {
|
|
26
|
-
await this.store.add({
|
|
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
|
-
|
|
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;
|
|
1
|
+
{"version":3,"file":"ContainerInstanceRegistry.js","sourceRoot":"","sources":["../../../src/infrastructure/containers/ContainerInstanceRegistry.ts"],"names":[],"mappings":"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"}
|
|
@@ -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"}
|