@cat-factory/worker 0.174.1 → 0.176.0
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/infrastructure/config/execution.d.ts +1 -1
- package/dist/infrastructure/config/execution.d.ts.map +1 -1
- package/dist/infrastructure/config/execution.js +12 -3
- package/dist/infrastructure/config/execution.js.map +1 -1
- package/dist/infrastructure/config/gitlab.d.ts +10 -5
- package/dist/infrastructure/config/gitlab.d.ts.map +1 -1
- package/dist/infrastructure/config/gitlab.js +10 -7
- package/dist/infrastructure/config/gitlab.js.map +1 -1
- package/dist/infrastructure/container-assembly.d.ts.map +1 -1
- package/dist/infrastructure/container-assembly.js +5 -1
- package/dist/infrastructure/container-assembly.js.map +1 -1
- package/dist/infrastructure/container-documents-deps.d.ts.map +1 -1
- package/dist/infrastructure/container-documents-deps.js +9 -6
- package/dist/infrastructure/container-documents-deps.js.map +1 -1
- package/dist/infrastructure/container-tracker-deps.d.ts.map +1 -1
- package/dist/infrastructure/container-tracker-deps.js +7 -7
- package/dist/infrastructure/container-tracker-deps.js.map +1 -1
- package/dist/infrastructure/container.js +3 -3
- package/dist/infrastructure/container.js.map +1 -1
- package/dist/infrastructure/containers/CloudflareContainerTransport.d.ts +15 -1
- package/dist/infrastructure/containers/CloudflareContainerTransport.d.ts.map +1 -1
- package/dist/infrastructure/containers/CloudflareContainerTransport.js +55 -16
- package/dist/infrastructure/containers/CloudflareContainerTransport.js.map +1 -1
- package/dist/infrastructure/containers/DeployContainer.d.ts +2 -20
- package/dist/infrastructure/containers/DeployContainer.d.ts.map +1 -1
- package/dist/infrastructure/containers/DeployContainer.js +7 -56
- package/dist/infrastructure/containers/DeployContainer.js.map +1 -1
- package/dist/infrastructure/containers/ExecutionContainer.d.ts +2 -51
- package/dist/infrastructure/containers/ExecutionContainer.d.ts.map +1 -1
- package/dist/infrastructure/containers/ExecutionContainer.js +9 -93
- package/dist/infrastructure/containers/ExecutionContainer.js.map +1 -1
- package/dist/infrastructure/containers/RunContainer.d.ts +98 -0
- package/dist/infrastructure/containers/RunContainer.d.ts.map +1 -0
- package/dist/infrastructure/containers/RunContainer.js +164 -0
- package/dist/infrastructure/containers/RunContainer.js.map +1 -0
- package/dist/infrastructure/containers/stopCause.d.ts +105 -0
- package/dist/infrastructure/containers/stopCause.d.ts.map +1 -0
- package/dist/infrastructure/containers/stopCause.js +108 -0
- package/dist/infrastructure/containers/stopCause.js.map +1 -0
- package/dist/infrastructure/env.d.ts +10 -0
- package/dist/infrastructure/env.d.ts.map +1 -1
- package/dist/infrastructure/env.js.map +1 -1
- package/dist/infrastructure/github-deps.js +1 -1
- package/dist/infrastructure/github-deps.js.map +1 -1
- package/dist/infrastructure/repositories/D1BinaryArtifactMetadataStore.d.ts +3 -1
- package/dist/infrastructure/repositories/D1BinaryArtifactMetadataStore.d.ts.map +1 -1
- package/dist/infrastructure/repositories/D1BinaryArtifactMetadataStore.js +38 -5
- package/dist/infrastructure/repositories/D1BinaryArtifactMetadataStore.js.map +1 -1
- package/dist/infrastructure/repositories/D1DocumentConnectionRepository.d.ts +11 -14
- package/dist/infrastructure/repositories/D1DocumentConnectionRepository.d.ts.map +1 -1
- package/dist/infrastructure/repositories/D1DocumentConnectionRepository.js +11 -38
- package/dist/infrastructure/repositories/D1DocumentConnectionRepository.js.map +1 -1
- package/dist/infrastructure/repositories/D1DocumentRepository.d.ts.map +1 -1
- package/dist/infrastructure/repositories/D1DocumentRepository.js +5 -3
- package/dist/infrastructure/repositories/D1DocumentRepository.js.map +1 -1
- package/dist/infrastructure/repositories/D1TaskConnectionRepository.d.ts +8 -14
- package/dist/infrastructure/repositories/D1TaskConnectionRepository.d.ts.map +1 -1
- package/dist/infrastructure/repositories/D1TaskConnectionRepository.js +8 -38
- package/dist/infrastructure/repositories/D1TaskConnectionRepository.js.map +1 -1
- package/dist/infrastructure/tasks-deps.d.ts.map +1 -1
- package/dist/infrastructure/tasks-deps.js +10 -7
- package/dist/infrastructure/tasks-deps.js.map +1 -1
- package/dist/infrastructure/vcsConnect.d.ts.map +1 -1
- package/dist/infrastructure/vcsConnect.js +5 -2
- package/dist/infrastructure/vcsConnect.js.map +1 -1
- package/dist/infrastructure/workflows/ExecutionWorkflow.d.ts.map +1 -1
- package/dist/infrastructure/workflows/ExecutionWorkflow.js +17 -8
- package/dist/infrastructure/workflows/ExecutionWorkflow.js.map +1 -1
- package/migrations/0087_document_renders.sql +28 -0
- package/package.json +18 -18
|
@@ -1,13 +1,38 @@
|
|
|
1
1
|
import { CONTAINER_EVICTION_ERROR, harnessDispatchError, readRunnerDispatchAck, } from '@cat-factory/kernel';
|
|
2
|
-
import { isRolloutSignal } from './
|
|
2
|
+
import { 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
|
|
5
|
-
// (`crash` / `transient`) minted alongside it
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
5
|
+
// (`crash` / `transient`) minted alongside it. The "(container evicted or crashed)" wording and
|
|
6
|
+
// the parenthetical each cause adds are DESCRIPTIVE context only, no longer regex-classified by
|
|
7
|
+
// any consumer (error-message coverage I5). Which Cloudflare events count as transient churn is
|
|
8
|
+
// decided HERE, in the facade; the engine stays runtime-neutral.
|
|
9
9
|
const EVICTION_ERROR = CONTAINER_EVICTION_ERROR;
|
|
10
10
|
const ROLLOUT_EVICTION_ERROR = `${EVICTION_ERROR} (transient infrastructure eviction)`;
|
|
11
|
+
// An idle reclaim is churn too, but a DIFFERENT operator's problem from a release draining a
|
|
12
|
+
// container: it says the driver stopped polling for longer than the container's idle window,
|
|
13
|
+
// so the remedy is the poll scheduling, not the deploy. Both recover on the transient budget;
|
|
14
|
+
// only the wording tells them apart, and a shared one would hide a recurring hiccup behind
|
|
15
|
+
// "there must have been a deploy" (stuck-run audit F12).
|
|
16
|
+
const IDLE_EVICTION_ERROR = `${EVICTION_ERROR} (idle container reclaimed between polls)`;
|
|
17
|
+
/**
|
|
18
|
+
* Every cause a container can OBSERVE about its own reclaim is infrastructure churn, so the
|
|
19
|
+
* table maps each to its wording and the verdict is `transient` for all of them. Exhaustive by
|
|
20
|
+
* type, so a new cause cannot ship without deciding what the operator is told about it.
|
|
21
|
+
*/
|
|
22
|
+
const TRANSIENT_EVICTION_ERROR = {
|
|
23
|
+
rollout: ROLLOUT_EVICTION_ERROR,
|
|
24
|
+
idle: IDLE_EVICTION_ERROR,
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* The failed view for a 404 poll, given what the container itself observed about its reclaim.
|
|
28
|
+
* No observed cause ⇒ the container is simply gone with nothing to explain it, which is a
|
|
29
|
+
* `crash` (an OOM, a genuine fault) and recovers on the small budget.
|
|
30
|
+
*/
|
|
31
|
+
function evictionView(cause) {
|
|
32
|
+
if (!cause)
|
|
33
|
+
return { state: 'failed', error: EVICTION_ERROR, evicted: 'crash' };
|
|
34
|
+
return { state: 'failed', error: TRANSIENT_EVICTION_ERROR[cause], evicted: 'transient' };
|
|
35
|
+
}
|
|
11
36
|
// The default runner transport: a per-RUN Cloudflare Container. One Durable Object
|
|
12
37
|
// instance per run id (`ref.runId`) hosts that run's whole sequence of step jobs; the
|
|
13
38
|
// base Container.fetch proxies to the Pi harness inside it, which keys each job by the
|
|
@@ -46,7 +71,7 @@ export class CloudflareContainerTransport {
|
|
|
46
71
|
constructor(
|
|
47
72
|
// Either per-run container class: `ExecutionContainer` (the agent harness, bound as
|
|
48
73
|
// `EXEC_CONTAINER`) or `DeployContainer` (the deploy harness, bound as `DEPLOY_CONTAINER`).
|
|
49
|
-
// Both expose the same `/jobs` HTTP contract on 8080 plus `
|
|
74
|
+
// Both expose the same `/jobs` HTTP contract on 8080 plus `recentEvictionCause`/`shutdown`,
|
|
50
75
|
// so this transport drives either unchanged — a deploy-dedicated instance simply gets the
|
|
51
76
|
// deploy namespace.
|
|
52
77
|
namespace,
|
|
@@ -106,6 +131,23 @@ export class CloudflareContainerTransport {
|
|
|
106
131
|
// write so a malformed/absent body can never cost the reaper its record of a live container.
|
|
107
132
|
return readRunnerDispatchAck(await safeJson(res));
|
|
108
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* The failed view for a container that has gone away, spending whatever the container recorded
|
|
136
|
+
* about its own reclaim.
|
|
137
|
+
*
|
|
138
|
+
* BOTH eviction paths funnel through here, including the one that already knows the cause from
|
|
139
|
+
* the thrown rollout signal, because the claim is not only how the cause is read: it is how a
|
|
140
|
+
* record is marked spent. A path that reported an eviction without claiming would leave the
|
|
141
|
+
* record behind to excuse a second one.
|
|
142
|
+
*
|
|
143
|
+
* Claiming is best-effort. An unreachable DO answers nothing, which reads as the crash it may
|
|
144
|
+
* well have been, and a container mid-rollout drain routinely is unreachable — so the cause the
|
|
145
|
+
* transport OBSERVED itself wins over a read that could not happen.
|
|
146
|
+
*/
|
|
147
|
+
async evictionOf(stub, ref, observed) {
|
|
148
|
+
const claimed = await stub.recentEvictionCause(ref.jobId).catch(() => undefined);
|
|
149
|
+
return evictionView(observed ?? claimed);
|
|
150
|
+
}
|
|
109
151
|
async poll(ref) {
|
|
110
152
|
// The per-run container is the Durable Object addressed by the run id; its DO id is
|
|
111
153
|
// the closest thing to a stable "container id" to surface in the run's details (a
|
|
@@ -126,9 +168,8 @@ export class CloudflareContainerTransport {
|
|
|
126
168
|
// "new version rollout" signal (exit 143) rather than returning a 404. Report it
|
|
127
169
|
// as a transient rollout eviction so the engine recovers it on the larger
|
|
128
170
|
// rollout budget instead of failing the run.
|
|
129
|
-
if (isRolloutSignal(err))
|
|
130
|
-
return
|
|
131
|
-
}
|
|
171
|
+
if (isRolloutSignal(err))
|
|
172
|
+
return this.evictionOf(stub, ref, 'rollout');
|
|
132
173
|
throw err;
|
|
133
174
|
}
|
|
134
175
|
if (res.status === 404) {
|
|
@@ -136,13 +177,11 @@ export class CloudflareContainerTransport {
|
|
|
136
177
|
// stops (the run-sweeper may then re-drive it from durable state). The eviction
|
|
137
178
|
// verdict rides the STRUCTURED `evicted` field the caller reads directly (the
|
|
138
179
|
// "(container evicted or crashed)" string is descriptive context only — no consumer
|
|
139
|
-
// regex-matches it any more, see I5). Ask the DO
|
|
140
|
-
// new-version rollout
|
|
141
|
-
// transient
|
|
142
|
-
|
|
143
|
-
return
|
|
144
|
-
? { state: 'failed', error: ROLLOUT_EVICTION_ERROR, evicted: 'transient' }
|
|
145
|
-
: { state: 'failed', error: EVICTION_ERROR, evicted: 'crash' };
|
|
180
|
+
// regex-matches it any more, see I5). Ask the DO what it observed about its own reclaim
|
|
181
|
+
// (a new-version rollout, or its idle window elapsing between polls). Either is infra
|
|
182
|
+
// churn the engine should ride out on the larger transient budget rather than spend the
|
|
183
|
+
// crash budget on.
|
|
184
|
+
return this.evictionOf(stub, ref);
|
|
146
185
|
}
|
|
147
186
|
if (!res.ok) {
|
|
148
187
|
throw new Error(`Container job poll failed (HTTP ${res.status}): ${await safeText(res)}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CloudflareContainerTransport.js","sourceRoot":"","sources":["../../../src/infrastructure/containers/CloudflareContainerTransport.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,qBAAqB,GAOtB,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"CloudflareContainerTransport.js","sourceRoot":"","sources":["../../../src/infrastructure/containers/CloudflareContainerTransport.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,22 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import type { Env } from '../env';
|
|
4
|
-
export declare class DeployContainer extends Container<Env> {
|
|
5
|
-
defaultPort: number;
|
|
6
|
-
envVars: Record<string, string>;
|
|
7
|
-
sleepAfter: string;
|
|
8
|
-
/**
|
|
9
|
-
* Record that THIS run's deploy container was drained by a new-version rollout (exit 143)
|
|
10
|
-
* rather than crashing, so the transport's next 404 poll classifies it as a transient
|
|
11
|
-
* rollout eviction (recovered on the larger budget) instead of a crash. Mirrors
|
|
12
|
-
* {@link ExecutionContainer.onError}.
|
|
13
|
-
*/
|
|
14
|
-
onError(error: unknown): Promise<unknown>;
|
|
15
|
-
/** Belt-and-braces rollout capture via `onStop` (see {@link ExecutionContainer.onStop}). */
|
|
16
|
-
onStop(params: StopParams): void;
|
|
17
|
-
/** Whether this run's deploy container was rolled out within the attribution window. */
|
|
18
|
-
recentlyRolledOut(): Promise<boolean>;
|
|
19
|
-
/** Reclaim this container now (SIGKILL via the base class) rather than idling out. */
|
|
20
|
-
shutdown(): Promise<void>;
|
|
1
|
+
import { RunContainer } from './RunContainer';
|
|
2
|
+
export declare class DeployContainer extends RunContainer {
|
|
21
3
|
}
|
|
22
4
|
//# sourceMappingURL=DeployContainer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeployContainer.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/containers/DeployContainer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"DeployContainer.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/containers/DeployContainer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAW7C,qBAAa,eAAgB,SAAQ,YAAY;CAAG"}
|
|
@@ -1,62 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
// Share the rollout-attribution machinery with ExecutionContainer so the storage key + window
|
|
3
|
-
// can't drift between the two per-run container classes.
|
|
4
|
-
import { isRolloutSignal, ROLLED_OUT_AT_KEY, ROLLOUT_ATTRIBUTION_WINDOW_MS, } from './ExecutionContainer';
|
|
1
|
+
import { RunContainer } from './RunContainer';
|
|
5
2
|
// One DEPLOY container per run (addressed by the run id), mirroring {@link ExecutionContainer}
|
|
6
3
|
// but pulling the SEPARATE deploy-harness image (slim base + real `kubectl`/`kustomize`/`helm`)
|
|
7
|
-
// instead of the executor-harness
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
// agent jobs stay on `EXEC_CONTAINER`, so the k8s CLIs never bloat an agent run's cold-start.
|
|
4
|
+
// instead of the executor-harness: a Cloudflare Container's image is pinned per container
|
|
5
|
+
// class, so the `image: 'deploy'` dispatch variant needs its own class. This one is bound as
|
|
6
|
+
// `DEPLOY_CONTAINER`, so the k8s CLIs never bloat an agent run's cold start.
|
|
11
7
|
//
|
|
12
8
|
// The deploy harness serves the SAME `POST /jobs` + `GET /jobs/{id}` contract on 8080, so the
|
|
13
|
-
// generic `CloudflareContainerTransport` drives it unchanged (it just gets this namespace)
|
|
14
|
-
//
|
|
15
|
-
|
|
16
|
-
// the only class-level env var.
|
|
17
|
-
export class DeployContainer extends Container {
|
|
18
|
-
// The deploy-harness HTTP server port (matches its Dockerfile EXPOSE/ENTRYPOINT).
|
|
19
|
-
defaultPort = 8080;
|
|
20
|
-
// Hand the inbound-auth shared secret to the harness when configured (it then rejects any
|
|
21
|
-
// /jobs call without the matching `x-harness-secret` header the transport sends).
|
|
22
|
-
envVars = this.env.HARNESS_SHARED_SECRET
|
|
23
|
-
? { HARNESS_SHARED_SECRET: this.env.HARNESS_SHARED_SECRET }
|
|
24
|
-
: {};
|
|
25
|
-
// A deploy is dispatched then polled every ~15s while it renders + applies + waits on
|
|
26
|
-
// rollout, so the instance stays warm for the job's duration; the idle window only elapses
|
|
27
|
-
// once polling stops (the job finished).
|
|
28
|
-
sleepAfter = '10m';
|
|
29
|
-
/**
|
|
30
|
-
* Record that THIS run's deploy container was drained by a new-version rollout (exit 143)
|
|
31
|
-
* rather than crashing, so the transport's next 404 poll classifies it as a transient
|
|
32
|
-
* rollout eviction (recovered on the larger budget) instead of a crash. Mirrors
|
|
33
|
-
* {@link ExecutionContainer.onError}.
|
|
34
|
-
*/
|
|
35
|
-
async onError(error) {
|
|
36
|
-
if (isRolloutSignal(error)) {
|
|
37
|
-
await this.ctx.storage.put(ROLLED_OUT_AT_KEY, Date.now());
|
|
38
|
-
}
|
|
39
|
-
return super.onError(error);
|
|
40
|
-
}
|
|
41
|
-
/** Belt-and-braces rollout capture via `onStop` (see {@link ExecutionContainer.onStop}). */
|
|
42
|
-
onStop(params) {
|
|
43
|
-
if (params.reason === 'runtime_signal' && params.exitCode === 143) {
|
|
44
|
-
void this.ctx.storage.put(ROLLED_OUT_AT_KEY, Date.now());
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
/** Whether this run's deploy container was rolled out within the attribution window. */
|
|
48
|
-
async recentlyRolledOut() {
|
|
49
|
-
const at = await this.ctx.storage.get(ROLLED_OUT_AT_KEY);
|
|
50
|
-
return typeof at === 'number' && Date.now() - at <= ROLLOUT_ATTRIBUTION_WINDOW_MS;
|
|
51
|
-
}
|
|
52
|
-
/** Reclaim this container now (SIGKILL via the base class) rather than idling out. */
|
|
53
|
-
async shutdown() {
|
|
54
|
-
try {
|
|
55
|
-
await this.destroy();
|
|
56
|
-
}
|
|
57
|
-
catch {
|
|
58
|
-
// Already gone / not running — nothing to reclaim.
|
|
59
|
-
}
|
|
60
|
-
}
|
|
9
|
+
// generic `CloudflareContainerTransport` drives it unchanged (it just gets this namespace), and
|
|
10
|
+
// its lifecycle behaviour is {@link RunContainer}'s, shared byte-for-byte with the agent one.
|
|
11
|
+
export class DeployContainer extends RunContainer {
|
|
61
12
|
}
|
|
62
13
|
//# sourceMappingURL=DeployContainer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeployContainer.js","sourceRoot":"","sources":["../../../src/infrastructure/containers/DeployContainer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"DeployContainer.js","sourceRoot":"","sources":["../../../src/infrastructure/containers/DeployContainer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAE7C,+FAA+F;AAC/F,gGAAgG;AAChG,0FAA0F;AAC1F,6FAA6F;AAC7F,6EAA6E;AAC7E,EAAE;AACF,8FAA8F;AAC9F,gGAAgG;AAChG,8FAA8F;AAC9F,MAAM,OAAO,eAAgB,SAAQ,YAAY;CAAG"}
|
|
@@ -1,53 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import type { Env } from '../env';
|
|
4
|
-
/** DO-storage key recording when a new-version rollout last drained this container. */
|
|
5
|
-
export declare const ROLLED_OUT_AT_KEY = "rolledOutAt";
|
|
6
|
-
/** How long after a rollout a 404 poll is still attributed to it (ms). */
|
|
7
|
-
export declare const ROLLOUT_ATTRIBUTION_WINDOW_MS = 120000;
|
|
8
|
-
/**
|
|
9
|
-
* Whether a container error/stop is the runtime's *new-version rollout* signal — a
|
|
10
|
-
* deploy draining the old container (exit 143) — rather than a crash/OOM. The
|
|
11
|
-
* @cloudflare/containers base class surfaces this through `onError` with the wording
|
|
12
|
-
* "...new version rollout: 143" (which its own exit-code parser, keyed on the plain
|
|
13
|
-
* "runtime signalled the container to exit:" form, does not recognise).
|
|
14
|
-
*/
|
|
15
|
-
export declare function isRolloutSignal(error: unknown): boolean;
|
|
16
|
-
export declare class ExecutionContainer extends Container<Env> {
|
|
17
|
-
defaultPort: number;
|
|
18
|
-
envVars: Record<string, string>;
|
|
19
|
-
sleepAfter: string;
|
|
20
|
-
/**
|
|
21
|
-
* Record that THIS run's container was drained by a new-version rollout (a deploy,
|
|
22
|
-
* exit 143) rather than crashing. The transport's next job poll — which 404s once
|
|
23
|
-
* the container restarts empty — reads this via {@link recentlyRolledOut} to
|
|
24
|
-
* classify the eviction as a transient rollout, so the engine recovers it on the
|
|
25
|
-
* larger rollout budget instead of failing the run as a crash. Persisted to DO
|
|
26
|
-
* storage (not in-memory) so it survives a DO isolate reset that a combined
|
|
27
|
-
* worker+container deploy would cause.
|
|
28
|
-
*/
|
|
29
|
-
onError(error: unknown): Promise<unknown>;
|
|
30
|
-
/**
|
|
31
|
-
* Belt-and-braces: depending on the runtime version, a rollout drain can surface
|
|
32
|
-
* as a `runtime_signal` stop with SIGTERM (143) through `onStop` instead of (or as
|
|
33
|
-
* well as) `onError`. Record it the same way.
|
|
34
|
-
*/
|
|
35
|
-
onStop(params: StopParams): void;
|
|
36
|
-
/**
|
|
37
|
-
* Whether this run's container was drained by a new-version rollout within the
|
|
38
|
-
* last {@link ROLLOUT_ATTRIBUTION_WINDOW_MS}. Called over RPC by the transport
|
|
39
|
-
* after a job poll 404s, to tell a transient rollout eviction apart from a
|
|
40
|
-
* crash/OOM. Time-bounded so a stale flag from an earlier rollout in this run
|
|
41
|
-
* can't misclassify a later genuine crash.
|
|
42
|
-
*/
|
|
43
|
-
recentlyRolledOut(): Promise<boolean>;
|
|
44
|
-
/**
|
|
45
|
-
* Reclaim this container now (SIGKILL via the base class), rather than waiting
|
|
46
|
-
* for the `sleepAfter` idle timer. Called over RPC when a run faults so a leaked
|
|
47
|
-
* instance isn't billed while idle. Best-effort and idempotent: destroying an
|
|
48
|
-
* already-stopped container is a no-op, and we swallow any error so the caller's
|
|
49
|
-
* failure handling is never derailed by cleanup.
|
|
50
|
-
*/
|
|
51
|
-
shutdown(): Promise<void>;
|
|
1
|
+
import { RunContainer } from './RunContainer';
|
|
2
|
+
export declare class ExecutionContainer extends RunContainer {
|
|
52
3
|
}
|
|
53
4
|
//# sourceMappingURL=ExecutionContainer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExecutionContainer.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/containers/ExecutionContainer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"ExecutionContainer.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/containers/ExecutionContainer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAU7C,qBAAa,kBAAmB,SAAQ,YAAY;CAAG"}
|
|
@@ -1,96 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export const ROLLOUT_ATTRIBUTION_WINDOW_MS = 120_000;
|
|
6
|
-
/**
|
|
7
|
-
* Whether a container error/stop is the runtime's *new-version rollout* signal — a
|
|
8
|
-
* deploy draining the old container (exit 143) — rather than a crash/OOM. The
|
|
9
|
-
* @cloudflare/containers base class surfaces this through `onError` with the wording
|
|
10
|
-
* "...new version rollout: 143" (which its own exit-code parser, keyed on the plain
|
|
11
|
-
* "runtime signalled the container to exit:" form, does not recognise).
|
|
12
|
-
*/
|
|
13
|
-
export function isRolloutSignal(error) {
|
|
14
|
-
const message = error instanceof Error ? error.message : typeof error === 'string' ? error : String(error);
|
|
15
|
-
return /new version rollout|runtime signalled the container to exit/i.test(message);
|
|
16
|
-
}
|
|
17
|
-
// One container instance per run: Cloudflare Containers map a Durable Object id
|
|
18
|
-
// to a dedicated container, so addressing `env.EXEC_CONTAINER.get(<executionId>)`
|
|
19
|
-
// gives each execution its own ephemeral sandbox. The container runs the Pi
|
|
20
|
-
// coding-agent harness (see @cat-factory/executor-harness) listening on 8080;
|
|
21
|
-
// the base `Container.fetch` proxies inbound requests there once it has booted.
|
|
1
|
+
import { RunContainer } from './RunContainer';
|
|
2
|
+
// One container instance per run: Cloudflare Containers map a Durable Object id to a dedicated
|
|
3
|
+
// container, so addressing `env.EXEC_CONTAINER.get(<executionId>)` gives each execution its own
|
|
4
|
+
// ephemeral sandbox running the Pi coding-agent harness (see @cat-factory/executor-harness).
|
|
22
5
|
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
export class ExecutionContainer extends Container {
|
|
29
|
-
// The harness HTTP server port (matches the Dockerfile ENTRYPOINT).
|
|
30
|
-
defaultPort = 8080;
|
|
31
|
-
// When configured, hand the inbound-auth shared secret to the harness so it rejects
|
|
32
|
-
// any /jobs call that doesn't present the matching `x-harness-secret` header (which
|
|
33
|
-
// the transport sends). Omitted when unset, leaving the harness open as before.
|
|
34
|
-
envVars = this.env.HARNESS_SHARED_SECRET
|
|
35
|
-
? { HARNESS_SHARED_SECRET: this.env.HARNESS_SHARED_SECRET }
|
|
36
|
-
: {};
|
|
37
|
-
// A run is dispatched, then polled every ~15s while its background job runs, so
|
|
38
|
-
// the instance stays warm for the job's duration without holding a single
|
|
39
|
-
// request open. This idle window only elapses once polling stops (the job has
|
|
40
|
-
// finished); the headroom tolerates a transient gap between polls without the
|
|
41
|
-
// instance being reclaimed mid-job.
|
|
42
|
-
sleepAfter = '10m';
|
|
43
|
-
/**
|
|
44
|
-
* Record that THIS run's container was drained by a new-version rollout (a deploy,
|
|
45
|
-
* exit 143) rather than crashing. The transport's next job poll — which 404s once
|
|
46
|
-
* the container restarts empty — reads this via {@link recentlyRolledOut} to
|
|
47
|
-
* classify the eviction as a transient rollout, so the engine recovers it on the
|
|
48
|
-
* larger rollout budget instead of failing the run as a crash. Persisted to DO
|
|
49
|
-
* storage (not in-memory) so it survives a DO isolate reset that a combined
|
|
50
|
-
* worker+container deploy would cause.
|
|
51
|
-
*/
|
|
52
|
-
async onError(error) {
|
|
53
|
-
if (isRolloutSignal(error)) {
|
|
54
|
-
await this.ctx.storage.put(ROLLED_OUT_AT_KEY, Date.now());
|
|
55
|
-
}
|
|
56
|
-
// Preserve the base behaviour (log + rethrow) so nothing else changes.
|
|
57
|
-
return super.onError(error);
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Belt-and-braces: depending on the runtime version, a rollout drain can surface
|
|
61
|
-
* as a `runtime_signal` stop with SIGTERM (143) through `onStop` instead of (or as
|
|
62
|
-
* well as) `onError`. Record it the same way.
|
|
63
|
-
*/
|
|
64
|
-
onStop(params) {
|
|
65
|
-
if (params.reason === 'runtime_signal' && params.exitCode === 143) {
|
|
66
|
-
void this.ctx.storage.put(ROLLED_OUT_AT_KEY, Date.now());
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Whether this run's container was drained by a new-version rollout within the
|
|
71
|
-
* last {@link ROLLOUT_ATTRIBUTION_WINDOW_MS}. Called over RPC by the transport
|
|
72
|
-
* after a job poll 404s, to tell a transient rollout eviction apart from a
|
|
73
|
-
* crash/OOM. Time-bounded so a stale flag from an earlier rollout in this run
|
|
74
|
-
* can't misclassify a later genuine crash.
|
|
75
|
-
*/
|
|
76
|
-
async recentlyRolledOut() {
|
|
77
|
-
const at = await this.ctx.storage.get(ROLLED_OUT_AT_KEY);
|
|
78
|
-
return typeof at === 'number' && Date.now() - at <= ROLLOUT_ATTRIBUTION_WINDOW_MS;
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Reclaim this container now (SIGKILL via the base class), rather than waiting
|
|
82
|
-
* for the `sleepAfter` idle timer. Called over RPC when a run faults so a leaked
|
|
83
|
-
* instance isn't billed while idle. Best-effort and idempotent: destroying an
|
|
84
|
-
* already-stopped container is a no-op, and we swallow any error so the caller's
|
|
85
|
-
* failure handling is never derailed by cleanup.
|
|
86
|
-
*/
|
|
87
|
-
async shutdown() {
|
|
88
|
-
try {
|
|
89
|
-
await this.destroy();
|
|
90
|
-
}
|
|
91
|
-
catch {
|
|
92
|
-
// Already gone / not running — nothing to reclaim.
|
|
93
|
-
}
|
|
94
|
-
}
|
|
6
|
+
// Everything this class does (the port, the inbound-auth secret, the idle window, the
|
|
7
|
+
// reclaim-cause bookkeeping, `shutdown`) is {@link RunContainer}'s. The class exists
|
|
8
|
+
// because a Cloudflare Container's image is pinned per container CLASS by the wrangler
|
|
9
|
+
// `[[containers]]` block: this one is bound as `EXEC_CONTAINER` and carries the executor image.
|
|
10
|
+
export class ExecutionContainer extends RunContainer {
|
|
95
11
|
}
|
|
96
12
|
//# sourceMappingURL=ExecutionContainer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExecutionContainer.js","sourceRoot":"","sources":["../../../src/infrastructure/containers/ExecutionContainer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"ExecutionContainer.js","sourceRoot":"","sources":["../../../src/infrastructure/containers/ExecutionContainer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAE7C,+FAA+F;AAC/F,gGAAgG;AAChG,6FAA6F;AAC7F,EAAE;AACF,sFAAsF;AACtF,qFAAqF;AACrF,uFAAuF;AACvF,gGAAgG;AAChG,MAAM,OAAO,kBAAmB,SAAQ,YAAY;CAAG"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { Container } from '@cloudflare/containers';
|
|
2
|
+
import type { StopParams } from '@cloudflare/containers';
|
|
3
|
+
import type { Env } from '../env';
|
|
4
|
+
import { type ContainerStopCause } from './stopCause';
|
|
5
|
+
/**
|
|
6
|
+
* The behaviour every per-run Cloudflare Container shares: one Durable Object instance per run
|
|
7
|
+
* id hosts that run's sequence of jobs, the harness listens on 8080, and the base
|
|
8
|
+
* `Container.fetch` proxies inbound requests there once it has booted.
|
|
9
|
+
*
|
|
10
|
+
* The two concrete classes ({@link import('./ExecutionContainer').ExecutionContainer} and
|
|
11
|
+
* {@link import('./DeployContainer').DeployContainer}) exist only because a Cloudflare
|
|
12
|
+
* Container's IMAGE is pinned per container class by the wrangler `[[containers]]` block. They
|
|
13
|
+
* are two bindings onto one behaviour, which is why that behaviour lives here rather than being
|
|
14
|
+
* kept in step by hand across both.
|
|
15
|
+
*
|
|
16
|
+
* No long-lived secrets are configured: the image carries none, and every per-job credential
|
|
17
|
+
* (the VCS token, the LLM session token, an apiserver token) arrives in the `/jobs` request
|
|
18
|
+
* body at dispatch time. The one exception is `HARNESS_SHARED_SECRET` below, an inbound-auth
|
|
19
|
+
* shared secret rather than a tenant credential.
|
|
20
|
+
*/
|
|
21
|
+
export declare abstract class RunContainer extends Container<Env> {
|
|
22
|
+
/** The harness HTTP server port (matches each image's Dockerfile ENTRYPOINT/EXPOSE). */
|
|
23
|
+
defaultPort: number;
|
|
24
|
+
envVars: Record<string, string>;
|
|
25
|
+
sleepAfter: string;
|
|
26
|
+
/**
|
|
27
|
+
* Record that THIS run's container was drained by a new-version rollout (a deploy, exit 143)
|
|
28
|
+
* rather than crashing. The transport's next job poll (which 404s once the container restarts
|
|
29
|
+
* empty) reads this back through {@link recentEvictionCause}, so the engine
|
|
30
|
+
* recovers it on the larger transient budget instead of failing the run as a crash.
|
|
31
|
+
* Persisted to DO storage (not in-memory) so it survives the isolate reset a combined
|
|
32
|
+
* worker+container deploy causes.
|
|
33
|
+
*/
|
|
34
|
+
onError(error: unknown): Promise<unknown>;
|
|
35
|
+
/**
|
|
36
|
+
* Belt-and-braces: depending on the runtime version, a rollout drain can surface as a
|
|
37
|
+
* `runtime_signal` stop with SIGTERM (143) through `onStop` instead of (or as well as)
|
|
38
|
+
* `onError`. Record it the same way.
|
|
39
|
+
*/
|
|
40
|
+
onStop(params: StopParams): void;
|
|
41
|
+
/**
|
|
42
|
+
* The idle window elapsed. Record it as the reclaim cause it is, then reclaim as the base
|
|
43
|
+
* class would.
|
|
44
|
+
*
|
|
45
|
+
* This container is kept warm ONLY by the driver's job polls, so an idle expiry with a job
|
|
46
|
+
* still outstanding means the backend stopped polling for longer than `sleepAfter`: a
|
|
47
|
+
* poll-scheduling hiccup, not the workload dying. Unrecorded, the resulting 404 poll is
|
|
48
|
+
* indistinguishable from an OOM: it spends the single crash-eviction budget, and a second
|
|
49
|
+
* hiccup in the same step then fails a healthy run (stuck-run audit F12).
|
|
50
|
+
*
|
|
51
|
+
* This hook cannot tell that case from the ROUTINE one (the run parked on a human decision and
|
|
52
|
+
* nothing is running), because only the harness knows whether a job is still live and asking
|
|
53
|
+
* it would itself be activity. So the marker is minted for both, and what keeps that honest is
|
|
54
|
+
* the boundary in {@link fetch}: the routine marker is dropped by the next dispatch, so it
|
|
55
|
+
* cannot still be lying around to excuse the following step's crash.
|
|
56
|
+
*
|
|
57
|
+
* Nothing is recorded when the container is already gone: the base class stops nothing in
|
|
58
|
+
* that case, so a marker would be attributing a reclaim that never happened.
|
|
59
|
+
*/
|
|
60
|
+
onActivityExpired(): Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* Proxy an inbound harness call, and take a JOB ACCEPTANCE as the end of whatever this
|
|
63
|
+
* container previously observed about itself.
|
|
64
|
+
*
|
|
65
|
+
* A stop cause explains the death of a container that was serving the jobs outstanding when it
|
|
66
|
+
* was recorded. `POST /jobs` answering 2xx says a new job starts here, so nothing recorded
|
|
67
|
+
* before it can account for that job's death. Without the boundary the routine case poisons
|
|
68
|
+
* the rare one: a run parks on a human decision, its container idles out with nothing running,
|
|
69
|
+
* and the `idle` marker that leaves behind is still inside its (deliberately wide) window to
|
|
70
|
+
* excuse a genuine OOM in the NEXT step as transient churn.
|
|
71
|
+
*
|
|
72
|
+
* It has to be the acceptance rather than the container starting: a 404 poll BOOTS the
|
|
73
|
+
* container on its way to discovering the job is gone, so clearing on start would drop the
|
|
74
|
+
* record moments before the read that exists to consume it.
|
|
75
|
+
*/
|
|
76
|
+
fetch(request: Request): Promise<Response>;
|
|
77
|
+
/**
|
|
78
|
+
* Why this run's container went away, for the transport to read over RPC after `jobId`'s poll
|
|
79
|
+
* 404s: the one thing that tells a reclaim apart from a crash. `undefined` means no reclaim
|
|
80
|
+
* this container observed explains that 404, so the caller reports a crash.
|
|
81
|
+
*
|
|
82
|
+
* Claimed by the polling job rather than deleted, so a retried durable poll step re-reads the
|
|
83
|
+
* same answer while a different job still finds it spent. See {@link takeStopCause}.
|
|
84
|
+
*/
|
|
85
|
+
recentEvictionCause(jobId: string): Promise<ContainerStopCause | undefined>;
|
|
86
|
+
/**
|
|
87
|
+
* Reclaim this container now (SIGKILL via the base class), rather than waiting for the
|
|
88
|
+
* `sleepAfter` idle timer. Called over RPC when a run settles or faults, so a leaked instance
|
|
89
|
+
* isn't billed while idle. Best-effort and idempotent: destroying an already-stopped
|
|
90
|
+
* container is a no-op, and we swallow any error so the caller's failure handling is never
|
|
91
|
+
* derailed by cleanup.
|
|
92
|
+
*/
|
|
93
|
+
shutdown(): Promise<void>;
|
|
94
|
+
private record;
|
|
95
|
+
/** The DO storage, narrowed to what the stop-cause bookkeeping uses. */
|
|
96
|
+
private get storage();
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=RunContainer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RunContainer.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/containers/RunContainer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAExD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAA;AAEjC,OAAO,EAEL,KAAK,kBAAkB,EAKxB,MAAM,aAAa,CAAA;AAEpB;;;;;;;;;;;;;;;GAeG;AACH,8BAAsB,YAAa,SAAQ,SAAS,CAAC,GAAG,CAAC;IACvD,wFAAwF;IAC/E,WAAW,SAAO;IAKlB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAElC;IAMG,UAAU,SAAQ;IAE3B;;;;;;;OAOG;IACY,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAIvD;IAED;;;;OAIG;IACM,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAIxC;IAED;;;;;;;;;;;;;;;;;;OAkBG;IACY,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAGhD;IAED;;;;;;;;;;;;;;OAcG;IACY,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAWxD;IAED;;;;;;;OAOG;IACG,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CAEhF;IAED;;;;;;OAMG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAO9B;IAED,OAAO,CAAC,MAAM;IAId,wEAAwE;IACxE,OAAO,KAAK,OAAO,GAElB;CACF"}
|