@cat-factory/worker 0.175.0 → 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/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/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/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/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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"execution.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/config/execution.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"execution.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/config/execution.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,eAAe,EAErB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAA;AAEjC,YAAY,EAAE,eAAe,EAAE,CAAA;AAO/B,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,GAAG,GAAG,eAAe,CAuC7D"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DEFAULT_ADVANCE_TIMEOUT, DEFAULT_CI_POLL_INTERVAL, DEFAULT_DECISION_TIMEOUT, DEFAULT_JOB_POLL_INTERVAL, resolveDurationEnv, } from '@cat-factory/server';
|
|
1
2
|
function intEnv(value, fallback) {
|
|
2
3
|
const n = value ? Number(value) : NaN;
|
|
3
4
|
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
@@ -7,12 +8,20 @@ export function loadExecutionConfig(env) {
|
|
|
7
8
|
// NOT a hard deadline: a parked run waits for a human indefinitely and is never failed
|
|
8
9
|
// for waiting. This is just the chunk length for each `waitForEvent` wait — on expiry the
|
|
9
10
|
// driver re-loops (re-checking storage, then re-arming) rather than killing the run.
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
//
|
|
12
|
+
// Every duration below is resolved through the SHARED parser, which is what keeps this
|
|
13
|
+
// facade and Node reading one value the same way: the string stored here is canonical, so
|
|
14
|
+
// what reaches `step.sleep` / `step.do` is always a form Workflows admits, and a value
|
|
15
|
+
// neither runtime can honour falls back to the same default on both with one warning.
|
|
16
|
+
decisionTimeout: resolveDurationEnv('DECISION_TIMEOUT', env.DECISION_TIMEOUT, DEFAULT_DECISION_TIMEOUT).canonical,
|
|
17
|
+
jobPollInterval: resolveDurationEnv('JOB_POLL_INTERVAL', env.JOB_POLL_INTERVAL, DEFAULT_JOB_POLL_INTERVAL).canonical,
|
|
12
18
|
jobMaxPolls: intEnv(env.JOB_MAX_POLLS, 280),
|
|
13
19
|
jobPollFailureTolerance: intEnv(env.JOB_POLL_FAILURE_TOLERANCE, 6),
|
|
14
|
-
ciPollInterval: env.CI_POLL_INTERVAL
|
|
20
|
+
ciPollInterval: resolveDurationEnv('CI_POLL_INTERVAL', env.CI_POLL_INTERVAL, DEFAULT_CI_POLL_INTERVAL).canonical,
|
|
15
21
|
ciMaxPolls: intEnv(env.CI_MAX_POLLS, 120),
|
|
22
|
+
// The per-durable-step ceiling the Workflows driver wraps each advance and status read in.
|
|
23
|
+
// Node races the same value in `driveExecution`, which is why it is one knob.
|
|
24
|
+
advanceTimeout: resolveDurationEnv('ADVANCE_TIMEOUT', env.ADVANCE_TIMEOUT, DEFAULT_ADVANCE_TIMEOUT).canonical,
|
|
16
25
|
// Hard floor of 75 min: a misconfigured low value must never reap live work
|
|
17
26
|
// (the longest legitimate container lifetime is ≈70 min of driver polling).
|
|
18
27
|
containerMaxAgeMs: Math.max(75, intEnv(env.CONTAINER_MAX_AGE_MINUTES, 90)) * 60_000,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"execution.js","sourceRoot":"","sources":["../../../src/infrastructure/config/execution.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"execution.js","sourceRoot":"","sources":["../../../src/infrastructure/config/execution.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,wBAAwB,EACxB,wBAAwB,EACxB,yBAAyB,EAEzB,kBAAkB,GACnB,MAAM,qBAAqB,CAAA;AAK5B,SAAS,MAAM,CAAC,KAAyB,EAAE,QAAgB;IACzD,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAA;IACrC,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAA;AACnD,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,GAAQ;IAC1C,OAAO;QACL,uFAAuF;QACvF,0FAA0F;QAC1F,qFAAqF;QACrF,EAAE;QACF,uFAAuF;QACvF,0FAA0F;QAC1F,uFAAuF;QACvF,sFAAsF;QACtF,eAAe,EAAE,kBAAkB,CACjC,kBAAkB,EAClB,GAAG,CAAC,gBAAgB,EACpB,wBAAwB,CACzB,CAAC,SAAS;QACX,eAAe,EAAE,kBAAkB,CACjC,mBAAmB,EACnB,GAAG,CAAC,iBAAiB,EACrB,yBAAyB,CAC1B,CAAC,SAAS;QACX,WAAW,EAAE,MAAM,CAAC,GAAG,CAAC,aAAa,EAAE,GAAG,CAAC;QAC3C,uBAAuB,EAAE,MAAM,CAAC,GAAG,CAAC,0BAA0B,EAAE,CAAC,CAAC;QAClE,cAAc,EAAE,kBAAkB,CAChC,kBAAkB,EAClB,GAAG,CAAC,gBAAgB,EACpB,wBAAwB,CACzB,CAAC,SAAS;QACX,UAAU,EAAE,MAAM,CAAC,GAAG,CAAC,YAAY,EAAE,GAAG,CAAC;QACzC,2FAA2F;QAC3F,8EAA8E;QAC9E,cAAc,EAAE,kBAAkB,CAChC,iBAAiB,EACjB,GAAG,CAAC,eAAe,EACnB,uBAAuB,CACxB,CAAC,SAAS;QACX,4EAA4E;QAC5E,4EAA4E;QAC5E,iBAAiB,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,yBAAyB,EAAE,EAAE,CAAC,CAAC,GAAG,MAAM;KACpF,CAAA;AACH,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type RunnerDispatchAck, type RunnerDispatchKind, type RunnerJobRef, type RunnerJobStopOutcome, type RunnerJobView, type RunnerTransport } from '@cat-factory/kernel';
|
|
2
2
|
import type { DurableObjectNamespace } from '@cloudflare/workers-types';
|
|
3
3
|
import type { DeployContainer } from './DeployContainer';
|
|
4
|
-
import {
|
|
4
|
+
import type { ExecutionContainer } from './ExecutionContainer';
|
|
5
5
|
import type { ContainerInstanceRegistry } from './ContainerInstanceRegistry';
|
|
6
6
|
export declare class CloudflareContainerTransport implements RunnerTransport {
|
|
7
7
|
private readonly namespace;
|
|
@@ -31,6 +31,20 @@ export declare class CloudflareContainerTransport implements RunnerTransport {
|
|
|
31
31
|
/** Header bag for a harness call: the shared secret when configured, else empty. */
|
|
32
32
|
private secretHeader;
|
|
33
33
|
dispatch(ref: RunnerJobRef, spec: Record<string, unknown>, kind?: RunnerDispatchKind): Promise<RunnerDispatchAck | undefined>;
|
|
34
|
+
/**
|
|
35
|
+
* The failed view for a container that has gone away, spending whatever the container recorded
|
|
36
|
+
* about its own reclaim.
|
|
37
|
+
*
|
|
38
|
+
* BOTH eviction paths funnel through here, including the one that already knows the cause from
|
|
39
|
+
* the thrown rollout signal, because the claim is not only how the cause is read: it is how a
|
|
40
|
+
* record is marked spent. A path that reported an eviction without claiming would leave the
|
|
41
|
+
* record behind to excuse a second one.
|
|
42
|
+
*
|
|
43
|
+
* Claiming is best-effort. An unreachable DO answers nothing, which reads as the crash it may
|
|
44
|
+
* well have been, and a container mid-rollout drain routinely is unreachable — so the cause the
|
|
45
|
+
* transport OBSERVED itself wins over a read that could not happen.
|
|
46
|
+
*/
|
|
47
|
+
private evictionOf;
|
|
34
48
|
poll(ref: RunnerJobRef): Promise<RunnerJobView>;
|
|
35
49
|
/**
|
|
36
50
|
* Reclaim the per-run container now (SIGKILL via the DO's `shutdown` RPC) instead
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CloudflareContainerTransport.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/containers/CloudflareContainerTransport.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,YAAY,EACjB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,eAAe,EACrB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAA;AACvE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACxD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"CloudflareContainerTransport.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/containers/CloudflareContainerTransport.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,YAAY,EACjB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,eAAe,EACrB,MAAM,qBAAqB,CAAA;AAC5B,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;AAE9D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAA;AAqE5E,qBAAa,4BAA6B,YAAW,eAAe;IAUhE,OAAO,CAAC,QAAQ,CAAC,SAAS;IAG1B,kFAAkF;IAClF,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;IAC1B;;;;;;OAMG;IACH,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC;IArBhC,6EAA6E;IAC7E,QAAQ,CAAC,OAAO,0BAAyB;IAEzC,YAMmB,SAAS,EACtB,sBAAsB,CAAC,kBAAkB,CAAC,GAC1C,sBAAsB,CAAC,eAAe,CAAC;IAC3C,kFAAkF;IACjE,QAAQ,CAAC,EAAE,yBAAyB,YAAA;IACrD;;;;;;OAMG;IACc,YAAY,CAAC,EAAE,MAAM,YAAA,EACpC;IAEJ,oFAAoF;IACpF,OAAO,CAAC,YAAY;IAUd,QAAQ,CACZ,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,IAAI,GAAE,kBAA4B,GACjC,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CA+BxC;IAED;;;;;;;;;;;;OAYG;YACW,UAAU;IASlB,IAAI,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC,CAyCpD;IAED;;;;;OAKG;IACG,OAAO,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAU9C;IAED;;;;;;;;;;OAUG;IACG,OAAO,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAqB9D;CACF"}
|
|
@@ -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"}
|