@cat-factory/worker 0.175.0 → 0.177.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.
Files changed (43) hide show
  1. package/dist/infrastructure/config/execution.d.ts +1 -1
  2. package/dist/infrastructure/config/execution.d.ts.map +1 -1
  3. package/dist/infrastructure/config/execution.js +12 -3
  4. package/dist/infrastructure/config/execution.js.map +1 -1
  5. package/dist/infrastructure/containers/CloudflareContainerTransport.d.ts +15 -1
  6. package/dist/infrastructure/containers/CloudflareContainerTransport.d.ts.map +1 -1
  7. package/dist/infrastructure/containers/CloudflareContainerTransport.js +73 -17
  8. package/dist/infrastructure/containers/CloudflareContainerTransport.js.map +1 -1
  9. package/dist/infrastructure/containers/DeployContainer.d.ts +2 -20
  10. package/dist/infrastructure/containers/DeployContainer.d.ts.map +1 -1
  11. package/dist/infrastructure/containers/DeployContainer.js +7 -56
  12. package/dist/infrastructure/containers/DeployContainer.js.map +1 -1
  13. package/dist/infrastructure/containers/ExecutionContainer.d.ts +2 -51
  14. package/dist/infrastructure/containers/ExecutionContainer.d.ts.map +1 -1
  15. package/dist/infrastructure/containers/ExecutionContainer.js +9 -93
  16. package/dist/infrastructure/containers/ExecutionContainer.js.map +1 -1
  17. package/dist/infrastructure/containers/RunContainer.d.ts +150 -0
  18. package/dist/infrastructure/containers/RunContainer.d.ts.map +1 -0
  19. package/dist/infrastructure/containers/RunContainer.js +229 -0
  20. package/dist/infrastructure/containers/RunContainer.js.map +1 -0
  21. package/dist/infrastructure/containers/stopCause.d.ts +240 -0
  22. package/dist/infrastructure/containers/stopCause.d.ts.map +1 -0
  23. package/dist/infrastructure/containers/stopCause.js +313 -0
  24. package/dist/infrastructure/containers/stopCause.js.map +1 -0
  25. package/dist/infrastructure/env.d.ts +10 -0
  26. package/dist/infrastructure/env.d.ts.map +1 -1
  27. package/dist/infrastructure/env.js.map +1 -1
  28. package/dist/infrastructure/repositories/D1BinaryArtifactMetadataStore.d.ts +4 -1
  29. package/dist/infrastructure/repositories/D1BinaryArtifactMetadataStore.d.ts.map +1 -1
  30. package/dist/infrastructure/repositories/D1BinaryArtifactMetadataStore.js +68 -5
  31. package/dist/infrastructure/repositories/D1BinaryArtifactMetadataStore.js.map +1 -1
  32. package/dist/infrastructure/repositories/D1DocumentRepository.d.ts.map +1 -1
  33. package/dist/infrastructure/repositories/D1DocumentRepository.js +5 -3
  34. package/dist/infrastructure/repositories/D1DocumentRepository.js.map +1 -1
  35. package/dist/infrastructure/repositories/chunk.d.ts +10 -2
  36. package/dist/infrastructure/repositories/chunk.d.ts.map +1 -1
  37. package/dist/infrastructure/repositories/chunk.js +13 -4
  38. package/dist/infrastructure/repositories/chunk.js.map +1 -1
  39. package/dist/infrastructure/workflows/ExecutionWorkflow.d.ts.map +1 -1
  40. package/dist/infrastructure/workflows/ExecutionWorkflow.js +17 -8
  41. package/dist/infrastructure/workflows/ExecutionWorkflow.js.map +1 -1
  42. package/migrations/0087_document_renders.sql +28 -0
  43. package/package.json +18 -18
@@ -1,4 +1,4 @@
1
- import type { ExecutionConfig } from '@cat-factory/server';
1
+ import { type ExecutionConfig } from '@cat-factory/server';
2
2
  import type { Env } from '../env';
3
3
  export type { ExecutionConfig };
4
4
  export declare function loadExecutionConfig(env: Env): ExecutionConfig;
@@ -1 +1 @@
1
- {"version":3,"file":"execution.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/config/execution.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAC1D,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAA;AAEjC,YAAY,EAAE,eAAe,EAAE,CAAA;AAO/B,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,GAAG,GAAG,eAAe,CAe7D"}
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
- decisionTimeout: env.DECISION_TIMEOUT?.trim() || '24 hours',
11
- jobPollInterval: env.JOB_POLL_INTERVAL?.trim() || '15 seconds',
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?.trim() || '30 seconds',
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":"AAKA,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,EAAE,GAAG,CAAC,gBAAgB,EAAE,IAAI,EAAE,IAAI,UAAU;QAC3D,eAAe,EAAE,GAAG,CAAC,iBAAiB,EAAE,IAAI,EAAE,IAAI,YAAY;QAC9D,WAAW,EAAE,MAAM,CAAC,GAAG,CAAC,aAAa,EAAE,GAAG,CAAC;QAC3C,uBAAuB,EAAE,MAAM,CAAC,GAAG,CAAC,0BAA0B,EAAE,CAAC,CAAC;QAClE,cAAc,EAAE,GAAG,CAAC,gBAAgB,EAAE,IAAI,EAAE,IAAI,YAAY;QAC5D,UAAU,EAAE,MAAM,CAAC,GAAG,CAAC,YAAY,EAAE,GAAG,CAAC;QACzC,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
+ {"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 { type ExecutionContainer } from './ExecutionContainer';
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,KAAK,kBAAkB,EAAmB,MAAM,sBAAsB,CAAA;AAC/E,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAA;AA2C5E,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;IAEK,IAAI,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC,CA6CpD;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
+ {"version":3,"file":"CloudflareContainerTransport.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/containers/CloudflareContainerTransport.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,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;AAO9D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAA;AAqF5E,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;IAWlB,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,53 @@
1
- import { CONTAINER_EVICTION_ERROR, harnessDispatchError, readRunnerDispatchAck, } from '@cat-factory/kernel';
2
- import { isRolloutSignal } from './ExecutionContainer';
1
+ import { composePostMortem, CONTAINER_EVICTION_ERROR, harnessDispatchError, readRunnerDispatchAck, } from '@cat-factory/kernel';
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
5
- // (`crash` / `transient`) minted alongside it the "(container evicted or crashed)" /
6
- // "(transient infrastructure eviction)" wording is now DESCRIPTIVE context only, no longer
7
- // regex-classified by any consumer (error-message coverage I5). The Cloudflare-specific
8
- // "rollout ⇒ transient" mapping lives here, in the facade; the engine stays runtime-neutral.
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 stop.
28
+ *
29
+ * The two halves of that observation answer different questions and are read independently. The
30
+ * `cause` decides the VERDICT: no observed cause ⇒ the container is simply gone with nothing to
31
+ * explain it, which is a `crash` (an OOM, a genuine fault) and recovers on the small budget. The
32
+ * `exit` state decides the DETAIL, and is attached whether or not a cause was recognised: on a
33
+ * runtime that cannot hand a log tail back to the Worker it is the only post-mortem there is,
34
+ * and a crash is exactly the case with no cause to name (finding D1).
35
+ *
36
+ * Independent does not mean unaware of each other. The cause is handed to the detail's wording
37
+ * so the two cannot contradict: the same reclaim that mints the `transient` verdict here is
38
+ * performed with a SIGKILL, and an exit code read as if nothing else explained it would tell the
39
+ * operator "out-of-memory kill" directly underneath "idle container reclaimed between polls".
40
+ */
41
+ function evictionView(observed) {
42
+ const detail = composePostMortem([describeContainerExit(observed.exit, observed.cause)]);
43
+ const { cause } = observed;
44
+ return {
45
+ state: 'failed',
46
+ error: cause ? TRANSIENT_EVICTION_ERROR[cause] : EVICTION_ERROR,
47
+ evicted: cause ? 'transient' : 'crash',
48
+ ...(detail ? { detail } : {}),
49
+ };
50
+ }
11
51
  // The default runner transport: a per-RUN Cloudflare Container. One Durable Object
12
52
  // instance per run id (`ref.runId`) hosts that run's whole sequence of step jobs; the
13
53
  // base Container.fetch proxies to the Pi harness inside it, which keys each job by the
@@ -46,7 +86,7 @@ export class CloudflareContainerTransport {
46
86
  constructor(
47
87
  // Either per-run container class: `ExecutionContainer` (the agent harness, bound as
48
88
  // `EXEC_CONTAINER`) or `DeployContainer` (the deploy harness, bound as `DEPLOY_CONTAINER`).
49
- // Both expose the same `/jobs` HTTP contract on 8080 plus `recentlyRolledOut`/`shutdown`,
89
+ // Both expose the same `/jobs` HTTP contract on 8080 plus `recentStopObservation`/`shutdown`,
50
90
  // so this transport drives either unchanged — a deploy-dedicated instance simply gets the
51
91
  // deploy namespace.
52
92
  namespace,
@@ -106,6 +146,25 @@ export class CloudflareContainerTransport {
106
146
  // write so a malformed/absent body can never cost the reaper its record of a live container.
107
147
  return readRunnerDispatchAck(await safeJson(res));
108
148
  }
149
+ /**
150
+ * The failed view for a container that has gone away, spending whatever the container recorded
151
+ * about its own reclaim.
152
+ *
153
+ * BOTH eviction paths funnel through here, including the one that already knows the cause from
154
+ * the thrown rollout signal, because the claim is not only how the cause is read: it is how a
155
+ * record is marked spent. A path that reported an eviction without claiming would leave the
156
+ * record behind to excuse a second one.
157
+ *
158
+ * Claiming is best-effort. An unreachable DO answers nothing, which reads as the crash it may
159
+ * well have been, and a container mid-rollout drain routinely is unreachable — so the cause the
160
+ * transport OBSERVED itself wins over a read that could not happen.
161
+ */
162
+ async evictionOf(stub, ref, observed) {
163
+ const claimed = await stub.recentStopObservation(ref.jobId).catch(() => ({}));
164
+ // The transport's own observation wins for the CAUSE (see above); the exit state can only
165
+ // ever come from the container, so it rides through whichever way the cause was decided.
166
+ return evictionView({ ...claimed, ...(observed ? { cause: observed } : {}) });
167
+ }
109
168
  async poll(ref) {
110
169
  // The per-run container is the Durable Object addressed by the run id; its DO id is
111
170
  // the closest thing to a stable "container id" to surface in the run's details (a
@@ -126,9 +185,8 @@ export class CloudflareContainerTransport {
126
185
  // "new version rollout" signal (exit 143) rather than returning a 404. Report it
127
186
  // as a transient rollout eviction so the engine recovers it on the larger
128
187
  // rollout budget instead of failing the run.
129
- if (isRolloutSignal(err)) {
130
- return { state: 'failed', error: ROLLOUT_EVICTION_ERROR, evicted: 'transient' };
131
- }
188
+ if (isRolloutSignal(err))
189
+ return this.evictionOf(stub, ref, 'rollout');
132
190
  throw err;
133
191
  }
134
192
  if (res.status === 404) {
@@ -136,13 +194,11 @@ export class CloudflareContainerTransport {
136
194
  // stops (the run-sweeper may then re-drive it from durable state). The eviction
137
195
  // verdict rides the STRUCTURED `evicted` field the caller reads directly (the
138
196
  // "(container evicted or crashed)" string is descriptive context only — no consumer
139
- // regex-matches it any more, see I5). Ask the DO whether it was just drained by a
140
- // new-version rollout (a deploy) if so, tag it so the engine treats it as
141
- // transient infra churn rather than a crash/OOM.
142
- const rolledOut = await stub.recentlyRolledOut().catch(() => false);
143
- return rolledOut
144
- ? { state: 'failed', error: ROLLOUT_EVICTION_ERROR, evicted: 'transient' }
145
- : { state: 'failed', error: EVICTION_ERROR, evicted: 'crash' };
197
+ // regex-matches it any more, see I5). Ask the DO what it observed about its own reclaim
198
+ // (a new-version rollout, or its idle window elapsing between polls). Either is infra
199
+ // churn the engine should ride out on the larger transient budget rather than spend the
200
+ // crash budget on.
201
+ return this.evictionOf(stub, ref);
146
202
  }
147
203
  if (!res.ok) {
148
204
  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":"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"}
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 { Container } from '@cloudflare/containers';
2
- import type { StopParams } from '@cloudflare/containers';
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,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAA;AAqBjC,qBAAa,eAAgB,SAAQ,SAAS,CAAC,GAAG,CAAC;IAExC,WAAW,SAAO;IAGlB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAElC;IAIG,UAAU,SAAQ;IAE3B;;;;;OAKG;IACY,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAKvD;IAED,4FAA4F;IACnF,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAIxC;IAED,wFAAwF;IAClF,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC,CAG1C;IAED,sFAAsF;IAChF,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAM9B;CACF"}
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 { Container } from '@cloudflare/containers';
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. A Cloudflare Container's image is pinned per container class
8
- // by the wrangler `[[containers]]` block, so the `image: 'deploy'` dispatch variant needs its own
9
- // class this one bound as `DEPLOY_CONTAINER`. The transport routes a deploy job here while
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). As
14
- // with the executor container, no long-lived secrets are configured: the per-job apiserver +
15
- // git tokens arrive in the `/jobs` request body and the optional inbound-auth shared secret is
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,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAGlD,8FAA8F;AAC9F,yDAAyD;AACzD,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,6BAA6B,GAC9B,MAAM,sBAAsB,CAAA;AAE7B,+FAA+F;AAC/F,gGAAgG;AAChG,gGAAgG;AAChG,kGAAkG;AAClG,+FAA+F;AAC/F,8FAA8F;AAC9F,EAAE;AACF,8FAA8F;AAC9F,+FAA+F;AAC/F,6FAA6F;AAC7F,+FAA+F;AAC/F,gCAAgC;AAChC,MAAM,OAAO,eAAgB,SAAQ,SAAc;IACjD,kFAAkF;IACzE,WAAW,GAAG,IAAI,CAAA;IAC3B,0FAA0F;IAC1F,kFAAkF;IACzE,OAAO,GAA2B,IAAI,CAAC,GAAG,CAAC,qBAAqB;QACvE,CAAC,CAAC,EAAE,qBAAqB,EAAE,IAAI,CAAC,GAAG,CAAC,qBAAqB,EAAE;QAC3D,CAAC,CAAC,EAAE,CAAA;IACN,sFAAsF;IACtF,2FAA2F;IAC3F,yCAAyC;IAChC,UAAU,GAAG,KAAK,CAAA;IAE3B;;;;;OAKG;IACM,KAAK,CAAC,OAAO,CAAC,KAAc;QACnC,IAAI,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;QAC3D,CAAC;QACD,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;IAC7B,CAAC;IAED,4FAA4F;IACnF,MAAM,CAAC,MAAkB;QAChC,IAAI,MAAM,CAAC,MAAM,KAAK,gBAAgB,IAAI,MAAM,CAAC,QAAQ,KAAK,GAAG,EAAE,CAAC;YAClE,KAAK,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;QAC1D,CAAC;IACH,CAAC;IAED,wFAAwF;IACxF,KAAK,CAAC,iBAAiB;QACrB,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAS,iBAAiB,CAAC,CAAA;QAChE,OAAO,OAAO,EAAE,KAAK,QAAQ,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,6BAA6B,CAAA;IACnF,CAAC;IAED,sFAAsF;IACtF,KAAK,CAAC,QAAQ;QACZ,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,OAAO,EAAE,CAAA;QACtB,CAAC;QAAC,MAAM,CAAC;YACP,mDAAmD;QACrD,CAAC;IACH,CAAC;CACF"}
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 { Container } from '@cloudflare/containers';
2
- import type { StopParams } from '@cloudflare/containers';
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,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAA;AAEjC,uFAAuF;AACvF,eAAO,MAAM,iBAAiB,gBAAgB,CAAA;AAC9C,0EAA0E;AAC1E,eAAO,MAAM,6BAA6B,SAAU,CAAA;AAEpD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAIvD;AAaD,qBAAa,kBAAmB,SAAQ,SAAS,CAAC,GAAG,CAAC;IAE3C,WAAW,SAAO;IAIlB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAElC;IAMG,UAAU,SAAQ;IAE3B;;;;;;;;OAQG;IACY,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAMvD;IAED;;;;OAIG;IACM,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAIxC;IAED;;;;;;OAMG;IACG,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC,CAG1C;IAED;;;;;;OAMG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAM9B;CACF"}
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 { Container } from '@cloudflare/containers';
2
- /** DO-storage key recording when a new-version rollout last drained this container. */
3
- export const ROLLED_OUT_AT_KEY = 'rolledOutAt';
4
- /** How long after a rollout a 404 poll is still attributed to it (ms). */
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
- // No long-lived secrets are configured here: the image carries none, and the per-job
24
- // GitHub token + LLM session token are passed in the `/run` request body at dispatch
25
- // time, never via image build args or class-level env vars. The one exception is the
26
- // optional `HARNESS_SHARED_SECRET` below an inbound-auth shared secret (not a tenant
27
- // credential), passed so the harness can require it on the Worker → harness calls.
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,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAIlD,uFAAuF;AACvF,MAAM,CAAC,MAAM,iBAAiB,GAAG,aAAa,CAAA;AAC9C,0EAA0E;AAC1E,MAAM,CAAC,MAAM,6BAA6B,GAAG,OAAO,CAAA;AAEpD;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,MAAM,OAAO,GACX,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IAC5F,OAAO,8DAA8D,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;AACrF,CAAC;AAED,gFAAgF;AAChF,kFAAkF;AAClF,4EAA4E;AAC5E,8EAA8E;AAC9E,gFAAgF;AAChF,EAAE;AACF,qFAAqF;AACrF,qFAAqF;AACrF,qFAAqF;AACrF,uFAAuF;AACvF,mFAAmF;AACnF,MAAM,OAAO,kBAAmB,SAAQ,SAAc;IACpD,oEAAoE;IAC3D,WAAW,GAAG,IAAI,CAAA;IAC3B,oFAAoF;IACpF,oFAAoF;IACpF,gFAAgF;IACvE,OAAO,GAA2B,IAAI,CAAC,GAAG,CAAC,qBAAqB;QACvE,CAAC,CAAC,EAAE,qBAAqB,EAAE,IAAI,CAAC,GAAG,CAAC,qBAAqB,EAAE;QAC3D,CAAC,CAAC,EAAE,CAAA;IACN,gFAAgF;IAChF,0EAA0E;IAC1E,8EAA8E;IAC9E,8EAA8E;IAC9E,oCAAoC;IAC3B,UAAU,GAAG,KAAK,CAAA;IAE3B;;;;;;;;OAQG;IACM,KAAK,CAAC,OAAO,CAAC,KAAc;QACnC,IAAI,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;QAC3D,CAAC;QACD,uEAAuE;QACvE,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;IAC7B,CAAC;IAED;;;;OAIG;IACM,MAAM,CAAC,MAAkB;QAChC,IAAI,MAAM,CAAC,MAAM,KAAK,gBAAgB,IAAI,MAAM,CAAC,QAAQ,KAAK,GAAG,EAAE,CAAC;YAClE,KAAK,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;QAC1D,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,iBAAiB;QACrB,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAS,iBAAiB,CAAC,CAAA;QAChE,OAAO,OAAO,EAAE,KAAK,QAAQ,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,6BAA6B,CAAA;IACnF,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,QAAQ;QACZ,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,OAAO,EAAE,CAAA;QACtB,CAAC;QAAC,MAAM,CAAC;YACP,mDAAmD;QACrD,CAAC;IACH,CAAC;CACF"}
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"}