deepline 0.3.128 → 0.3.129
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/bundling-sources/sdk/src/client.ts +34 -9
- package/dist/bundling-sources/sdk/src/http.ts +22 -0
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-data-plane/r2.ts +22 -0
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +265 -2
- package/dist/bundling-sources/shared_libs/play-runtime/bounded-batch-function.ts +682 -0
- package/dist/bundling-sources/shared_libs/play-runtime/child-execution-strategy.ts +21 -5
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +5390 -1732
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +107 -1
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/commands/roll-execution-relay.ts +7 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-machines.ts +229 -7
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-registry.ts +25 -4
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-gateway-capacity.ts +71 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-gateway-drain.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-topology.ts +4 -1
- package/dist/bundling-sources/shared_libs/play-runtime/durable-receipt-execution.ts +694 -28
- package/dist/bundling-sources/shared_libs/play-runtime/egress-bandwidth-probe.ts +167 -0
- package/dist/bundling-sources/shared_libs/play-runtime/execute-server-timing.ts +103 -0
- package/dist/bundling-sources/shared_libs/play-runtime/execution-gateway-receipt-output-reference.ts +34 -0
- package/dist/bundling-sources/shared_libs/play-runtime/execution-gateway-response-size.ts +27 -0
- package/dist/bundling-sources/shared_libs/play-runtime/gateway-postgres-admission.ts +66 -1
- package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts +425 -71
- package/dist/bundling-sources/shared_libs/play-runtime/governor/governor.ts +210 -32
- package/dist/bundling-sources/shared_libs/play-runtime/governor/in-memory-rate-state-backend.ts +27 -13
- package/dist/bundling-sources/shared_libs/play-runtime/governor/policy.ts +19 -6
- package/dist/bundling-sources/shared_libs/play-runtime/governor/rate-state-backend.ts +61 -2
- package/dist/bundling-sources/shared_libs/play-runtime/integration-operation-reader-fence/contracts.ts +33 -1
- package/dist/bundling-sources/shared_libs/play-runtime/integration-operation-reader-fence/job.ts +42 -1
- package/dist/bundling-sources/shared_libs/play-runtime/launch-source-policy.ts +11 -11
- package/dist/bundling-sources/shared_libs/play-runtime/ledger-safe-payload.ts +49 -16
- package/dist/bundling-sources/shared_libs/play-runtime/map-memory-limits.ts +1653 -98
- package/dist/bundling-sources/shared_libs/play-runtime/memory-ledger.ts +807 -0
- package/dist/bundling-sources/shared_libs/play-runtime/memory-pressure-signal.ts +138 -0
- package/dist/bundling-sources/shared_libs/play-runtime/output-size-limits.ts +85 -3
- package/dist/bundling-sources/shared_libs/play-runtime/persistence-latch.ts +60 -2
- package/dist/bundling-sources/shared_libs/play-runtime/play-run-recovery-policy.ts +46 -1
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/contracts.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/job.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/ports.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/postgres-adapter.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runtime-semantics.ts +70 -27
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +56 -0
- package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/adaptive-provider-capacity-controller.ts +192 -0
- package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/epoch-aimd-rate-controller.ts +194 -0
- package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/fixture-capacity-simulator.ts +220 -0
- package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/gradient2-concurrency-controller.ts +214 -0
- package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/provider-capacity-policy.ts +43 -0
- package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/provider-concurrency-policy.ts +30 -0
- package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/provider-rate-policy.ts +95 -0
- package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/provider-throttle-classifier.ts +40 -0
- package/dist/bundling-sources/shared_libs/play-runtime/resource-governor.ts +1535 -11
- package/dist/bundling-sources/shared_libs/play-runtime/run-failure.ts +49 -2
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +6 -1
- package/dist/bundling-sources/shared_libs/play-runtime/run-log-buffer.ts +55 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/egress-probe.ts +221 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +339 -29
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-deadline.ts +28 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-progress-push.ts +192 -165
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +6 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +90 -5
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-runtime-watchdog.ts +281 -6
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +27 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/local-process.ts +19 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/modal.ts +108 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/runtime-sandbox-reconciliation.ts +40 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/types.ts +11 -5
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-actions.ts +30 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api-compression.ts +276 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +723 -115
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-capacity-policy.ts +98 -2
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-contract.ts +3 -2
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-environment.ts +41 -4
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-flow.ts +448 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-map-row-feed.ts +215 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-operation-contract.ts +11 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-receipt-store-adapter.ts +161 -14
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-receipt-value.ts +184 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-receipt-writer.ts +607 -121
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-sandbox-placement-policy.ts +4 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-sheet-row-writer.ts +21 -0
- package/dist/bundling-sources/shared_libs/play-runtime/sandbox-runs/contract.ts +15 -2
- package/dist/bundling-sources/shared_libs/play-runtime/sandbox-runs/runner-backend-adapter.ts +71 -14
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-play-deploy-task.ts +2 -2
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +288 -119
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-shared.ts +8 -6
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-worker-required-env.ts +28 -3
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/canonical-integration-operation-store.ts +179 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/gateway-heavy-request-admission.ts +80 -2
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/gateway-progress-registry.ts +31 -2
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/integration-operation-store-contract.ts +104 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/integration-operation-value-reference.ts +47 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/integration-operation-value-retirement.ts +193 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/integration-operation-value-storage.ts +420 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-pool.ts +41 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-progress.ts +134 -3
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-rate-state.ts +1657 -95
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +885 -73
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/receipt-completion-coalescer.ts +452 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/receipt-gateway.ts +601 -42
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-terminal-transition.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-receipt-value-storage.ts +371 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/worker-resource-metrics.ts +86 -7
- package/dist/bundling-sources/shared_libs/play-runtime/secret-redaction.ts +153 -22
- package/dist/bundling-sources/shared_libs/play-runtime/tool-call/index.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-call/receipt-cohort.ts +188 -38
- package/dist/bundling-sources/shared_libs/play-runtime/tool-call/runtime-step-receipts-adapter.ts +28 -13
- package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +44 -1
- package/dist/bundling-sources/shared_libs/play-runtime/tool-result.ts +56 -4
- package/dist/bundling-sources/shared_libs/play-runtime/work-receipts.ts +2 -0
- package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +16 -10
- package/dist/bundling-sources/shared_libs/plays/dataset.ts +4 -1
- package/dist/cli/index.js +250 -38
- package/dist/cli/index.mjs +250 -38
- package/dist/{compiler-manifest-DfSnbCTW.d.mts → compiler-manifest-BIqRyj5m.d.mts} +1 -1
- package/dist/{compiler-manifest-DfSnbCTW.d.ts → compiler-manifest-BIqRyj5m.d.ts} +1 -1
- package/dist/index.d.mts +4 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +231 -35
- package/dist/index.mjs +231 -35
- package/dist/install-integrity.json +31 -2
- package/dist/plays/bundle-play-file.d.mts +2 -2
- package/dist/plays/bundle-play-file.d.ts +2 -2
- package/dist/plays/bundle-play-file.mjs +65 -1
- package/dist/release.d.mts +1 -1
- package/dist/release.d.ts +1 -1
- package/dist/release.js +1 -1
- package/dist/release.mjs +1 -1
- package/package.json +1 -1
|
@@ -1,10 +1,132 @@
|
|
|
1
|
+
import { STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS } from '../../../plays/sandbox-runtime-limits';
|
|
2
|
+
import { RUNTIME_SANDBOX_MEMORY_ABORT_CODE } from '../../run-failure';
|
|
3
|
+
import {
|
|
4
|
+
RUNTIME_MEMORY_LEDGER_PATH_ENV,
|
|
5
|
+
RUNTIME_MEMORY_PRESSURE_PATH_ENV,
|
|
6
|
+
} from '../../memory-pressure-signal';
|
|
7
|
+
|
|
1
8
|
/** Exit code reserved for the external runner watchdog's runtime deadline. */
|
|
2
9
|
export const DAYTONA_RUNTIME_WATCHDOG_EXIT_CODE = 124;
|
|
3
10
|
|
|
11
|
+
/** The cgroup memory envelope one standard sandbox is given, in MiB. */
|
|
12
|
+
export const STANDARD_PLAY_SANDBOX_MEMORY_MIB =
|
|
13
|
+
STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS.memoryGiB * 1024;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Share of the sandbox cgroup that V8 may use for its old generation.
|
|
17
|
+
*
|
|
18
|
+
* This used to be 0.75, which was not an envelope at all: 768 MiB of old space
|
|
19
|
+
* plus the default ~192 MiB young generation let V8 alone reach 960 MiB inside
|
|
20
|
+
* a 1024 MiB cgroup before a single native buffer, code page, or in-flight
|
|
21
|
+
* transport body was counted. The cgroup kills on RSS, and RSS runs 1.3-1.5x
|
|
22
|
+
* the V8 live bytes, so V8 never had to collect before the kernel killed the
|
|
23
|
+
* process. Run 34064408068 died exactly that way at residentPeak ~1 GiB.
|
|
24
|
+
*
|
|
25
|
+
* At 0.5 the V8 heap ceiling sits at 512 MiB of the 1024 MiB cgroup, so V8
|
|
26
|
+
* collects hard while there is still cgroup headroom for everything V8 does
|
|
27
|
+
* not own.
|
|
28
|
+
*/
|
|
29
|
+
export const STANDARD_PLAY_RUNNER_OLD_SPACE_SHARE = 0.5;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* `--max-old-space-size` for the customer runner. This is the single V8
|
|
33
|
+
* envelope for both the Daytona and local-process substrates; map admission
|
|
34
|
+
* samples this actual V8 ceiling.
|
|
35
|
+
*/
|
|
36
|
+
export const STANDARD_PLAY_RUNNER_MAX_OLD_SPACE_SIZE_MIB = Math.floor(
|
|
37
|
+
STANDARD_PLAY_SANDBOX_MEMORY_MIB * STANDARD_PLAY_RUNNER_OLD_SPACE_SHARE,
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* `--max-semi-space-size` for the customer runner. V8 reserves three
|
|
42
|
+
* semi-spaces, so 16 MiB adds ~48 MiB to `heap_size_limit` where the 64-bit
|
|
43
|
+
* default (64 MiB) adds ~192 MiB. Capping the young generation is what makes
|
|
44
|
+
* the flag list an envelope rather than an old-space number with an
|
|
45
|
+
* unaccounted nursery bolted on top.
|
|
46
|
+
*/
|
|
47
|
+
export const STANDARD_PLAY_RUNNER_MAX_SEMI_SPACE_SIZE_MIB = 16;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Approximately what `v8.getHeapStatistics().heap_size_limit` reports under
|
|
51
|
+
* the flags below: 512 + 3 x 16 = 560 MiB of the 1024 MiB sandbox.
|
|
52
|
+
*/
|
|
53
|
+
export const STANDARD_PLAY_RUNNER_HEAP_SIZE_LIMIT_MIB =
|
|
54
|
+
STANDARD_PLAY_RUNNER_MAX_OLD_SPACE_SIZE_MIB +
|
|
55
|
+
3 * STANDARD_PLAY_RUNNER_MAX_SEMI_SPACE_SIZE_MIB;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The one Node exec argv every customer-runner spawn site uses. `--expose-gc`
|
|
59
|
+
* is not a debug flag here: the runner calls `globalThis.gc()` when this
|
|
60
|
+
* watchdog signals memory pressure, which is the only way a hold can return
|
|
61
|
+
* bytes to the cgroup before the abort threshold.
|
|
62
|
+
*/
|
|
63
|
+
export const PLAY_RUNNER_NODE_EXEC_ARGV: readonly string[] = [
|
|
64
|
+
`--max-old-space-size=${STANDARD_PLAY_RUNNER_MAX_OLD_SPACE_SIZE_MIB}`,
|
|
65
|
+
`--max-semi-space-size=${STANDARD_PLAY_RUNNER_MAX_SEMI_SPACE_SIZE_MIB}`,
|
|
66
|
+
'--expose-gc',
|
|
67
|
+
// The sandbox reports the host's 48-64 cores while its cgroup quota is one
|
|
68
|
+
// core, so V8 would spawn parallel and concurrent GC helpers that only
|
|
69
|
+
// contend with the main thread for that core (51-run Daytona CPU
|
|
70
|
+
// experiment, 2026-09-07: gzip on the threadpool accounted for every GC
|
|
71
|
+
// event and every event-loop stall). One core gets one GC thread.
|
|
72
|
+
'--single-threaded-gc',
|
|
73
|
+
];
|
|
74
|
+
|
|
4
75
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
76
|
+
* libuv threadpool size for the customer runner. The default is 4 and it is
|
|
77
|
+
* sized for a machine, not a one-core cgroup: zlib and fs work on four threads
|
|
78
|
+
* plus the main thread thrash on one core. Two keeps gzip and file I/O off the
|
|
79
|
+
* main thread without oversubscribing it.
|
|
80
|
+
*/
|
|
81
|
+
export const PLAY_RUNNER_UV_THREADPOOL_SIZE = 2;
|
|
82
|
+
|
|
83
|
+
/** How often the watchdog parent samples the cgroup's memory usage. */
|
|
84
|
+
export const RUNNER_MEMORY_WATCH_INTERVAL_MS = 250;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Fraction of the cgroup limit at which the watchdog signals the runner
|
|
88
|
+
* (SIGUSR2) to pause new admission, release caches, and collect.
|
|
89
|
+
*/
|
|
90
|
+
export const RUNNER_MEMORY_PRESSURE_RATIO = 0.8;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Fraction of the cgroup limit at which the watchdog terminates the runner
|
|
94
|
+
* itself. A controlled abort carrying the runtime's byte ledger is a failure
|
|
95
|
+
* the run can report; the kernel's OOM kill is not.
|
|
96
|
+
*/
|
|
97
|
+
export const RUNNER_MEMORY_ABORT_RATIO = 0.92;
|
|
98
|
+
|
|
99
|
+
/** Minimum spacing between pressure signals while usage stays above the ratio. */
|
|
100
|
+
export const RUNNER_MEMORY_PRESSURE_SIGNAL_INTERVAL_MS = 2_000;
|
|
101
|
+
|
|
102
|
+
/** Grace between SIGTERM and SIGKILL when the watchdog stops the runner. */
|
|
103
|
+
export const RUNNER_TERMINATION_KILL_GRACE_MS = 1_000;
|
|
104
|
+
|
|
105
|
+
/** Default cgroup mount point. Overridable by argv so tests can inject a root. */
|
|
106
|
+
export const RUNNER_MEMORY_CGROUP_ROOT = '/sys/fs/cgroup';
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* cgroup v1 reports "no limit" as a near-2^63 sentinel. Anything at or above
|
|
110
|
+
* this is not a real sandbox envelope, so the watch treats it as absent.
|
|
111
|
+
*/
|
|
112
|
+
export const RUNNER_MEMORY_CGROUP_UNLIMITED_BYTES = 2 ** 50;
|
|
113
|
+
|
|
114
|
+
/** `reason` written into the termination diagnostic on a memory abort. */
|
|
115
|
+
export const RUNTIME_SANDBOX_MEMORY_ABORT_REASON =
|
|
116
|
+
RUNTIME_SANDBOX_MEMORY_ABORT_CODE;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Build the tiny parent process that owns the hard runtime deadline and the
|
|
120
|
+
* sandbox memory envelope outside the customer-code Node event loop. A
|
|
121
|
+
* synchronous infinite loop can block the runner's own timer, but it cannot
|
|
122
|
+
* block this separate process; nor can a runner whose heap is thrashing run
|
|
123
|
+
* its own memory watch reliably.
|
|
124
|
+
*
|
|
125
|
+
* argv (after `node -e <source>`):
|
|
126
|
+
* 1 runnerPath, 2 configPath, 3 runtimeStartedPath, 4 runtimeCompletedPath,
|
|
127
|
+
* 5 limitMs, 6 startupLimitMs, 7 terminalGraceMs, 8 runtimeLimitMarkerPath,
|
|
128
|
+
* 9 terminationDiagnosticPath, 10 memoryLedgerPath, 11 memoryPressurePath,
|
|
129
|
+
* 12 cgroupRoot.
|
|
8
130
|
*/
|
|
9
131
|
export function buildDaytonaRuntimeWatchdogSource(): string {
|
|
10
132
|
return `
|
|
@@ -19,18 +141,30 @@ const startupLimitMs = Math.max(1, Number.parseInt(process.argv[6] || '', 10) ||
|
|
|
19
141
|
const terminalGraceMs = Math.max(1, Number.parseInt(process.argv[7] || '', 10) || 1);
|
|
20
142
|
const runtimeLimitMarkerPath = process.argv[8] || '';
|
|
21
143
|
const terminationDiagnosticPath = process.argv[9] || '';
|
|
144
|
+
const memoryLedgerPath = process.argv[10] || '';
|
|
145
|
+
const memoryPressurePath = process.argv[11] || '';
|
|
146
|
+
const cgroupRoot = process.argv[12] || ${JSON.stringify(RUNNER_MEMORY_CGROUP_ROOT)};
|
|
22
147
|
const runtimeLimitSeconds = Math.max(1, Math.ceil(limitMs / 1_000));
|
|
23
148
|
const child = spawn(
|
|
24
149
|
process.execPath,
|
|
25
|
-
['
|
|
150
|
+
[${PLAY_RUNNER_NODE_EXEC_ARGV.map((flag) => JSON.stringify(flag)).join(', ')}, runnerPath, configPath],
|
|
26
151
|
{
|
|
27
|
-
env:
|
|
152
|
+
env: {
|
|
153
|
+
...process.env,
|
|
154
|
+
...(memoryLedgerPath
|
|
155
|
+
? { ${JSON.stringify(RUNTIME_MEMORY_LEDGER_PATH_ENV)}: memoryLedgerPath }
|
|
156
|
+
: {}),
|
|
157
|
+
...(memoryPressurePath
|
|
158
|
+
? { ${JSON.stringify(RUNTIME_MEMORY_PRESSURE_PATH_ENV)}: memoryPressurePath }
|
|
159
|
+
: {}),
|
|
160
|
+
},
|
|
28
161
|
stdio: 'inherit',
|
|
29
162
|
},
|
|
30
163
|
);
|
|
31
164
|
let timedOut = false;
|
|
32
165
|
let startupTimedOut = false;
|
|
33
166
|
let terminalFlushTimedOut = false;
|
|
167
|
+
let memoryAborted = false;
|
|
34
168
|
let settled = false;
|
|
35
169
|
let forceKillTimer = null;
|
|
36
170
|
let runtimeTimer = null;
|
|
@@ -56,7 +190,7 @@ function writeTerminationDiagnostic(reason, details = {}) {
|
|
|
56
190
|
function stopChild(reason, details) {
|
|
57
191
|
writeTerminationDiagnostic(reason, details);
|
|
58
192
|
child.kill('SIGTERM');
|
|
59
|
-
forceKillTimer = setTimeout(() => child.kill('SIGKILL'),
|
|
193
|
+
forceKillTimer = setTimeout(() => child.kill('SIGKILL'), ${RUNNER_TERMINATION_KILL_GRACE_MS});
|
|
60
194
|
}
|
|
61
195
|
|
|
62
196
|
function markRuntimeLimit() {
|
|
@@ -66,6 +200,144 @@ function markRuntimeLimit() {
|
|
|
66
200
|
} catch {}
|
|
67
201
|
}
|
|
68
202
|
|
|
203
|
+
// --- sandbox memory envelope ------------------------------------------------
|
|
204
|
+
// Reads the cgroup the sandbox actually lives in, from OUTSIDE the customer
|
|
205
|
+
// event loop. cgroup v2 first, then v1; where neither exists (macOS
|
|
206
|
+
// local-process, for instance) the watch reports itself off once and stays off.
|
|
207
|
+
let memorySource = null;
|
|
208
|
+
let memoryWatchDisabledReported = false;
|
|
209
|
+
let lastMemoryPressureSignalAt = 0;
|
|
210
|
+
|
|
211
|
+
function readMemoryNumberFile(path) {
|
|
212
|
+
try {
|
|
213
|
+
const raw = fs.readFileSync(path, 'utf8').trim();
|
|
214
|
+
if (!raw) return null;
|
|
215
|
+
if (raw === 'max') return Number.POSITIVE_INFINITY;
|
|
216
|
+
const value = Number.parseInt(raw, 10);
|
|
217
|
+
return Number.isFinite(value) ? value : null;
|
|
218
|
+
} catch {}
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function memorySourceCandidates() {
|
|
223
|
+
if (memorySource) return [memorySource];
|
|
224
|
+
return [
|
|
225
|
+
{
|
|
226
|
+
kind: 'cgroup_v2',
|
|
227
|
+
usagePath: cgroupRoot + '/memory.current',
|
|
228
|
+
limitPath: cgroupRoot + '/memory.max',
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
kind: 'cgroup_v1',
|
|
232
|
+
usagePath: cgroupRoot + '/memory.usage_in_bytes',
|
|
233
|
+
limitPath: cgroupRoot + '/memory.limit_in_bytes',
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
kind: 'cgroup_v1_nested',
|
|
237
|
+
usagePath: cgroupRoot + '/memory/memory.usage_in_bytes',
|
|
238
|
+
limitPath: cgroupRoot + '/memory/memory.limit_in_bytes',
|
|
239
|
+
},
|
|
240
|
+
];
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function readCgroupMemory() {
|
|
244
|
+
const candidates = memorySourceCandidates();
|
|
245
|
+
for (let index = 0; index < candidates.length; index += 1) {
|
|
246
|
+
const candidate = candidates[index];
|
|
247
|
+
const usageBytes = readMemoryNumberFile(candidate.usagePath);
|
|
248
|
+
const limitBytes = readMemoryNumberFile(candidate.limitPath);
|
|
249
|
+
if (usageBytes === null || limitBytes === null) continue;
|
|
250
|
+
if (!(limitBytes > 0)) continue;
|
|
251
|
+
if (limitBytes >= ${RUNNER_MEMORY_CGROUP_UNLIMITED_BYTES}) continue;
|
|
252
|
+
memorySource = candidate;
|
|
253
|
+
return {
|
|
254
|
+
source: candidate.kind,
|
|
255
|
+
usageBytes: usageBytes,
|
|
256
|
+
limitBytes: limitBytes,
|
|
257
|
+
ratio: usageBytes / limitBytes,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function reportMemoryWatchDisabled() {
|
|
264
|
+
if (memoryWatchDisabledReported) return;
|
|
265
|
+
memoryWatchDisabledReported = true;
|
|
266
|
+
// stdout, not stderr: an absent cgroup is an expected substrate fact on a
|
|
267
|
+
// host without memory accounting, and stderr is reserved for terminations.
|
|
268
|
+
process.stdout.write(
|
|
269
|
+
'[memory] sandbox memory watch disabled: no cgroup memory accounting under ' +
|
|
270
|
+
cgroupRoot +
|
|
271
|
+
'\\n',
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function readMemoryLedger() {
|
|
276
|
+
if (!memoryLedgerPath) return null;
|
|
277
|
+
try {
|
|
278
|
+
const raw = fs.readFileSync(memoryLedgerPath, 'utf8');
|
|
279
|
+
if (!raw.trim()) return null;
|
|
280
|
+
return JSON.parse(raw);
|
|
281
|
+
} catch {}
|
|
282
|
+
return null;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function publishMemoryPressure(sample) {
|
|
286
|
+
if (!memoryPressurePath) return;
|
|
287
|
+
try {
|
|
288
|
+
fs.writeFileSync(
|
|
289
|
+
memoryPressurePath,
|
|
290
|
+
JSON.stringify({
|
|
291
|
+
schemaVersion: 1,
|
|
292
|
+
source: sample.source,
|
|
293
|
+
usageBytes: sample.usageBytes,
|
|
294
|
+
limitBytes: sample.limitBytes,
|
|
295
|
+
ratio: sample.ratio,
|
|
296
|
+
at: Date.now(),
|
|
297
|
+
}),
|
|
298
|
+
);
|
|
299
|
+
} catch {}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const memoryWatch = setInterval(() => {
|
|
303
|
+
if (settled || memoryAborted) return;
|
|
304
|
+
const sample = readCgroupMemory();
|
|
305
|
+
if (!sample) {
|
|
306
|
+
reportMemoryWatchDisabled();
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
if (sample.ratio >= ${RUNNER_MEMORY_ABORT_RATIO}) {
|
|
310
|
+
memoryAborted = true;
|
|
311
|
+
clearInterval(memoryWatch);
|
|
312
|
+
process.stderr.write(
|
|
313
|
+
'${RUNTIME_SANDBOX_MEMORY_ABORT_REASON}: sandbox memory usage ' +
|
|
314
|
+
sample.usageBytes +
|
|
315
|
+
' of ' +
|
|
316
|
+
sample.limitBytes +
|
|
317
|
+
' bytes reached the abort threshold; the runner was stopped before the kernel could kill it.\\n',
|
|
318
|
+
);
|
|
319
|
+
stopChild('${RUNTIME_SANDBOX_MEMORY_ABORT_REASON}', {
|
|
320
|
+
memorySource: sample.source,
|
|
321
|
+
memoryUsageBytes: sample.usageBytes,
|
|
322
|
+
memoryLimitBytes: sample.limitBytes,
|
|
323
|
+
memoryUsageRatio: Math.round(sample.ratio * 10_000) / 10_000,
|
|
324
|
+
memoryLedger: readMemoryLedger(),
|
|
325
|
+
});
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
if (sample.ratio >= ${RUNNER_MEMORY_PRESSURE_RATIO}) {
|
|
329
|
+
const now = Date.now();
|
|
330
|
+
if (now - lastMemoryPressureSignalAt < ${RUNNER_MEMORY_PRESSURE_SIGNAL_INTERVAL_MS}) {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
lastMemoryPressureSignalAt = now;
|
|
334
|
+
publishMemoryPressure(sample);
|
|
335
|
+
try {
|
|
336
|
+
child.kill('SIGUSR2');
|
|
337
|
+
} catch {}
|
|
338
|
+
}
|
|
339
|
+
}, ${RUNNER_MEMORY_WATCH_INTERVAL_MS});
|
|
340
|
+
|
|
69
341
|
function armRuntimeDeadline() {
|
|
70
342
|
if (settled || runtimeTimer) return;
|
|
71
343
|
clearTimeout(startupTimer);
|
|
@@ -124,6 +396,7 @@ function finish(code) {
|
|
|
124
396
|
if (settled) return;
|
|
125
397
|
settled = true;
|
|
126
398
|
clearInterval(runtimeBoundaryPoll);
|
|
399
|
+
clearInterval(memoryWatch);
|
|
127
400
|
clearTimeout(startupTimer);
|
|
128
401
|
if (runtimeTimer) clearTimeout(runtimeTimer);
|
|
129
402
|
if (terminalGraceTimer) clearTimeout(terminalGraceTimer);
|
|
@@ -134,6 +407,8 @@ function finish(code) {
|
|
|
134
407
|
? 1
|
|
135
408
|
: terminalFlushTimedOut
|
|
136
409
|
? 1
|
|
410
|
+
: memoryAborted
|
|
411
|
+
? 1
|
|
137
412
|
: typeof code === 'number'
|
|
138
413
|
? code
|
|
139
414
|
: 1;
|
|
@@ -511,6 +511,8 @@ export async function inspectDetachedDaytonaRunner(input: {
|
|
|
511
511
|
cmdId: string;
|
|
512
512
|
outputPath: string;
|
|
513
513
|
exitCodePath: string;
|
|
514
|
+
/** Refuse to read a sandbox outside the launch-pinned Daytona organization. */
|
|
515
|
+
expectedOrganizationId?: string | null;
|
|
514
516
|
}): Promise<{
|
|
515
517
|
exitCode: number | null;
|
|
516
518
|
result: PlayRunnerResult | null;
|
|
@@ -521,7 +523,8 @@ export async function inspectDetachedDaytonaRunner(input: {
|
|
|
521
523
|
| 'result_missing'
|
|
522
524
|
| 'command_inspection_failed'
|
|
523
525
|
| 'sandbox_missing'
|
|
524
|
-
| 'sandbox_lookup_failed'
|
|
526
|
+
| 'sandbox_lookup_failed'
|
|
527
|
+
| 'wrong_routing_domain';
|
|
525
528
|
detail: string | null;
|
|
526
529
|
};
|
|
527
530
|
}> {
|
|
@@ -529,6 +532,21 @@ export async function inspectDetachedDaytonaRunner(input: {
|
|
|
529
532
|
const { clientOptions } = loadDaytonaRequiredConfig();
|
|
530
533
|
const daytona = daytonaSdkClientFactory.createFull(clientOptions);
|
|
531
534
|
const sandbox = (await daytona.get(input.sandboxId)) as DaytonaSandbox;
|
|
535
|
+
const expectedOrganizationId = input.expectedOrganizationId?.trim() || null;
|
|
536
|
+
const observedOrganizationId = sandbox.organizationId?.trim() || null;
|
|
537
|
+
if (
|
|
538
|
+
expectedOrganizationId &&
|
|
539
|
+
observedOrganizationId !== expectedOrganizationId
|
|
540
|
+
) {
|
|
541
|
+
return {
|
|
542
|
+
exitCode: null,
|
|
543
|
+
result: null,
|
|
544
|
+
diagnosis: {
|
|
545
|
+
stage: 'wrong_routing_domain',
|
|
546
|
+
detail: 'Sandbox belongs to a different Daytona organization.',
|
|
547
|
+
},
|
|
548
|
+
};
|
|
549
|
+
}
|
|
532
550
|
let exitCode: number | null = null;
|
|
533
551
|
let commandFound = false;
|
|
534
552
|
let commandInspectionError: string | null = null;
|
|
@@ -911,6 +929,7 @@ export function retryableRuntimeInitializationFailureReason(
|
|
|
911
929
|
| 'runtime_receipt_gateway_transport'
|
|
912
930
|
| 'daytona_public_status'
|
|
913
931
|
| 'daytona_runner_startup_transport'
|
|
932
|
+
| 'daytona_infrastructure'
|
|
914
933
|
| null {
|
|
915
934
|
if (result.status !== 'failed') return null;
|
|
916
935
|
if (RUNTIME_POSTGRES_CONNECT_RETRY_PATTERN.test(result.error)) {
|
|
@@ -941,6 +960,13 @@ export function retryableRuntimeInitializationFailureReason(
|
|
|
941
960
|
) {
|
|
942
961
|
return 'runtime_api_transport';
|
|
943
962
|
}
|
|
963
|
+
if (
|
|
964
|
+
rowsProcessed === 0 &&
|
|
965
|
+
result.steps.length === 0 &&
|
|
966
|
+
DAYTONA_INFRASTRUCTURE_RETRY_PATTERN.test(result.error)
|
|
967
|
+
) {
|
|
968
|
+
return 'daytona_infrastructure';
|
|
969
|
+
}
|
|
944
970
|
return null;
|
|
945
971
|
}
|
|
946
972
|
|
package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/local-process.ts
CHANGED
|
@@ -441,6 +441,22 @@ export const localProcessPlayRunnerBackend: PlayRunnerBackend = {
|
|
|
441
441
|
workspaceRoot,
|
|
442
442
|
'runtime-limit.marker',
|
|
443
443
|
);
|
|
444
|
+
const terminationDiagnosticPath = join(
|
|
445
|
+
workspaceRoot,
|
|
446
|
+
'runtime-termination.json',
|
|
447
|
+
);
|
|
448
|
+
// The local substrate shares the sandbox memory envelope: same
|
|
449
|
+
// watchdog, same flags, same signal contract. On a host without
|
|
450
|
+
// cgroup memory accounting the watch reports itself off once and
|
|
451
|
+
// the runtime deadline behaviour is unchanged.
|
|
452
|
+
const memoryLedgerPath = join(
|
|
453
|
+
workspaceRoot,
|
|
454
|
+
'memory-ledger.json',
|
|
455
|
+
);
|
|
456
|
+
const memoryPressurePath = join(
|
|
457
|
+
workspaceRoot,
|
|
458
|
+
'memory-pressure.json',
|
|
459
|
+
);
|
|
444
460
|
const result = await runCommand(
|
|
445
461
|
'node',
|
|
446
462
|
[
|
|
@@ -454,6 +470,9 @@ export const localProcessPlayRunnerBackend: PlayRunnerBackend = {
|
|
|
454
470
|
String(PLAY_RUNNER_STARTUP_GRACE_SECONDS * 1_000),
|
|
455
471
|
String(PLAY_RUNNER_TERMINAL_GRACE_SECONDS * 1_000),
|
|
456
472
|
runtimeLimitMarkerPath,
|
|
473
|
+
terminationDiagnosticPath,
|
|
474
|
+
memoryLedgerPath,
|
|
475
|
+
memoryPressurePath,
|
|
457
476
|
],
|
|
458
477
|
{
|
|
459
478
|
cwd: workspaceRoot,
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
SandboxRunnerPreCodeDeferralError,
|
|
7
7
|
} from '../types';
|
|
8
8
|
import { buildPlayRunnerBundle } from '../bundle';
|
|
9
|
+
import { findPlayRunnerResult, parsePlayRunnerEvents } from '../runner-events';
|
|
9
10
|
import type {
|
|
10
11
|
PlayRunnerExecutionConfig,
|
|
11
12
|
PlayRunnerResult,
|
|
@@ -39,6 +40,7 @@ const MODAL_SANDBOX_CREATE_TIMEOUT_MS =
|
|
|
39
40
|
RUNTIME_RELIABILITY_POLICY.sandbox.modalSandboxCreateTimeoutMs;
|
|
40
41
|
const MODAL_PAYLOAD_UPLOAD_TIMEOUT_MS = 90_000;
|
|
41
42
|
const MODAL_TERMINATION_TIMEOUT_MS = 10_000;
|
|
43
|
+
const MODAL_DETACHED_INSPECTION_TIMEOUT_MS = 10_000;
|
|
42
44
|
const MODAL_CAPACITY_RELEASE_RETRY_DELAYS_MS = [0, 100, 500, 2_000] as const;
|
|
43
45
|
const MODAL_LATE_CREATE_PERSIST_RETRY_DELAYS_MS = [0, 100, 500, 2_000] as const;
|
|
44
46
|
type ModalSandbox = Awaited<ReturnType<ModalClient['sandboxes']['create']>>;
|
|
@@ -820,6 +822,112 @@ export async function findModalSandboxIdsByTags(input: {
|
|
|
820
822
|
return [...new Set(sandboxIds)];
|
|
821
823
|
}
|
|
822
824
|
|
|
825
|
+
/**
|
|
826
|
+
* Best-effort terminal recovery for an already-detached Modal runner. This
|
|
827
|
+
* reads only the payload wrapper's captured files; it never executes Play
|
|
828
|
+
* code or launches a replacement sandbox.
|
|
829
|
+
*/
|
|
830
|
+
export async function inspectDetachedModalRunner(input: {
|
|
831
|
+
sandboxId: string;
|
|
832
|
+
outputPath: string;
|
|
833
|
+
exitCodePath: string;
|
|
834
|
+
expectedAppId?: string | null;
|
|
835
|
+
}): Promise<{
|
|
836
|
+
exitCode: number | null;
|
|
837
|
+
result: PlayRunnerResult | null;
|
|
838
|
+
diagnosis: { stage: string; detail: string | null };
|
|
839
|
+
}> {
|
|
840
|
+
const expectedAppId = input.expectedAppId?.trim() || null;
|
|
841
|
+
if (!expectedAppId) {
|
|
842
|
+
return {
|
|
843
|
+
exitCode: null,
|
|
844
|
+
result: null,
|
|
845
|
+
diagnosis: {
|
|
846
|
+
stage: 'missing_routing_domain',
|
|
847
|
+
detail: 'Modal sandbox inspection requires the launch-pinned app.',
|
|
848
|
+
},
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
853
|
+
try {
|
|
854
|
+
return await Promise.race([
|
|
855
|
+
(async () => {
|
|
856
|
+
const { client } = await loadModalClientConfig();
|
|
857
|
+
for await (const sandbox of client.sandboxes.list({
|
|
858
|
+
appId: expectedAppId,
|
|
859
|
+
})) {
|
|
860
|
+
if (sandbox.sandboxId !== input.sandboxId) continue;
|
|
861
|
+
const [output, exitCode] = await Promise.allSettled([
|
|
862
|
+
sandbox.filesystem.readText(input.outputPath),
|
|
863
|
+
sandbox.filesystem.readText(input.exitCodePath),
|
|
864
|
+
]);
|
|
865
|
+
if (output.status !== 'fulfilled') {
|
|
866
|
+
return {
|
|
867
|
+
exitCode: null,
|
|
868
|
+
result: null,
|
|
869
|
+
diagnosis: {
|
|
870
|
+
stage: 'result_missing',
|
|
871
|
+
detail: 'Captured runner output is unavailable.',
|
|
872
|
+
},
|
|
873
|
+
};
|
|
874
|
+
}
|
|
875
|
+
const parsedExitCode =
|
|
876
|
+
exitCode.status === 'fulfilled' &&
|
|
877
|
+
/^-?\d+$/u.test(exitCode.value.trim())
|
|
878
|
+
? Number(exitCode.value.trim())
|
|
879
|
+
: null;
|
|
880
|
+
const result =
|
|
881
|
+
findPlayRunnerResult(parsePlayRunnerEvents(output.value)) ?? null;
|
|
882
|
+
return {
|
|
883
|
+
exitCode:
|
|
884
|
+
parsedExitCode !== null && Number.isSafeInteger(parsedExitCode)
|
|
885
|
+
? parsedExitCode
|
|
886
|
+
: null,
|
|
887
|
+
result,
|
|
888
|
+
diagnosis: result
|
|
889
|
+
? { stage: 'salvaged', detail: null }
|
|
890
|
+
: { stage: 'result_missing', detail: null },
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
return {
|
|
894
|
+
exitCode: null,
|
|
895
|
+
result: null,
|
|
896
|
+
diagnosis: {
|
|
897
|
+
stage: 'sandbox_missing',
|
|
898
|
+
detail: 'Sandbox was not found in the launch-pinned Modal app.',
|
|
899
|
+
},
|
|
900
|
+
};
|
|
901
|
+
})(),
|
|
902
|
+
new Promise<never>((_resolve, reject) => {
|
|
903
|
+
timer = setTimeout(() => {
|
|
904
|
+
reject(
|
|
905
|
+
new Error(
|
|
906
|
+
`Modal detached-runner inspection exceeded ${MODAL_DETACHED_INSPECTION_TIMEOUT_MS}ms.`,
|
|
907
|
+
),
|
|
908
|
+
);
|
|
909
|
+
}, MODAL_DETACHED_INSPECTION_TIMEOUT_MS);
|
|
910
|
+
}),
|
|
911
|
+
]);
|
|
912
|
+
} catch (error) {
|
|
913
|
+
console.warn('[play-runner.modal.detached_inspect_failed]', {
|
|
914
|
+
sandboxId: input.sandboxId,
|
|
915
|
+
expectedAppId,
|
|
916
|
+
error: error instanceof Error ? error.message : String(error),
|
|
917
|
+
});
|
|
918
|
+
return {
|
|
919
|
+
exitCode: null,
|
|
920
|
+
result: null,
|
|
921
|
+
diagnosis: {
|
|
922
|
+
stage: 'sandbox_lookup_failed',
|
|
923
|
+
detail: 'Modal sandbox inspection could not complete.',
|
|
924
|
+
},
|
|
925
|
+
};
|
|
926
|
+
} finally {
|
|
927
|
+
if (timer) clearTimeout(timer);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
|
|
823
931
|
export async function readDetachedModalRuntimeCompletion(input: {
|
|
824
932
|
sandboxId: string;
|
|
825
933
|
runtimeCompletedPath: string;
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
deleteDaytonaSandboxByIdWithOutcome,
|
|
3
|
+
inspectDetachedDaytonaRunner,
|
|
3
4
|
readDetachedDaytonaRuntimeCompletion,
|
|
4
5
|
} from './backends/daytona';
|
|
5
6
|
import {
|
|
6
7
|
deleteModalSandboxById,
|
|
8
|
+
inspectDetachedModalRunner,
|
|
7
9
|
readDetachedModalRuntimeCompletion,
|
|
8
10
|
} from './backends/modal';
|
|
9
11
|
import {
|
|
@@ -12,6 +14,7 @@ import {
|
|
|
12
14
|
type RuntimeSandboxRef,
|
|
13
15
|
} from '../runtime-sandbox-contract';
|
|
14
16
|
import type { DetachedRunnerSuspension } from '../suspension';
|
|
17
|
+
import type { PlayRunnerResult } from '../protocol';
|
|
15
18
|
|
|
16
19
|
export type {
|
|
17
20
|
RuntimeSandboxProviderId,
|
|
@@ -21,6 +24,15 @@ export type {
|
|
|
21
24
|
export type RuntimeSandboxReconciliationAdapter = {
|
|
22
25
|
provider: RuntimeSandboxProviderId;
|
|
23
26
|
delete(ref: RuntimeSandboxRef): Promise<RuntimeSandboxDeleteOutcome>;
|
|
27
|
+
/**
|
|
28
|
+
* Read an already-captured detached-runner terminal without executing Play
|
|
29
|
+
* code. The scheduler owns whether this is safe to commit; adapters only
|
|
30
|
+
* implement their provider's file read.
|
|
31
|
+
*/
|
|
32
|
+
inspectDetachedRunner(
|
|
33
|
+
ref: RuntimeSandboxRef,
|
|
34
|
+
suspension: DetachedRunnerSuspension,
|
|
35
|
+
): Promise<RuntimeSandboxDetachedRunnerInspection | null>;
|
|
24
36
|
readWorkCompletedAt(
|
|
25
37
|
ref: RuntimeSandboxRef,
|
|
26
38
|
path: string,
|
|
@@ -29,12 +41,22 @@ export type RuntimeSandboxReconciliationAdapter = {
|
|
|
29
41
|
|
|
30
42
|
export type RuntimeSandboxReconciliation = {
|
|
31
43
|
delete(ref: RuntimeSandboxRef): Promise<RuntimeSandboxDeleteOutcome>;
|
|
44
|
+
inspectDetachedRunner(
|
|
45
|
+
ref: RuntimeSandboxRef,
|
|
46
|
+
suspension: DetachedRunnerSuspension,
|
|
47
|
+
): Promise<RuntimeSandboxDetachedRunnerInspection | null>;
|
|
32
48
|
readWorkCompletedAt(
|
|
33
49
|
ref: RuntimeSandboxRef,
|
|
34
50
|
path: string,
|
|
35
51
|
): Promise<number | null>;
|
|
36
52
|
};
|
|
37
53
|
|
|
54
|
+
export type RuntimeSandboxDetachedRunnerInspection = {
|
|
55
|
+
exitCode: number | null;
|
|
56
|
+
result: PlayRunnerResult | null;
|
|
57
|
+
diagnosis: { stage: string; detail: string | null };
|
|
58
|
+
};
|
|
59
|
+
|
|
38
60
|
export type RuntimeSandboxDeleteOutcome =
|
|
39
61
|
| {
|
|
40
62
|
kind: 'deleted' | 'already_absent';
|
|
@@ -94,6 +116,8 @@ export function createRuntimeSandboxReconciliation(input: {
|
|
|
94
116
|
};
|
|
95
117
|
return {
|
|
96
118
|
delete: (ref) => resolve(ref).delete(ref),
|
|
119
|
+
inspectDetachedRunner: (ref, suspension) =>
|
|
120
|
+
resolve(ref).inspectDetachedRunner(ref, suspension),
|
|
97
121
|
readWorkCompletedAt: (ref, path) =>
|
|
98
122
|
resolve(ref).readWorkCompletedAt(ref, path),
|
|
99
123
|
};
|
|
@@ -181,6 +205,15 @@ export const defaultRuntimeSandboxReconciliation =
|
|
|
181
205
|
routingDomain: outcome.organizationId,
|
|
182
206
|
};
|
|
183
207
|
},
|
|
208
|
+
inspectDetachedRunner: (ref, suspension) =>
|
|
209
|
+
inspectDetachedDaytonaRunner({
|
|
210
|
+
sandboxId: ref.resourceId,
|
|
211
|
+
sessionId: suspension.sessionId,
|
|
212
|
+
cmdId: suspension.cmdId,
|
|
213
|
+
outputPath: suspension.outputPath,
|
|
214
|
+
exitCodePath: suspension.exitCodePath,
|
|
215
|
+
expectedOrganizationId: ref.routingDomain,
|
|
216
|
+
}),
|
|
184
217
|
readWorkCompletedAt: (ref, path) =>
|
|
185
218
|
readDetachedDaytonaRuntimeCompletion({
|
|
186
219
|
sandboxId: ref.resourceId,
|
|
@@ -200,6 +233,13 @@ export const defaultRuntimeSandboxReconciliation =
|
|
|
200
233
|
routingDomain: outcome.appId,
|
|
201
234
|
};
|
|
202
235
|
},
|
|
236
|
+
inspectDetachedRunner: (ref, suspension) =>
|
|
237
|
+
inspectDetachedModalRunner({
|
|
238
|
+
sandboxId: ref.resourceId,
|
|
239
|
+
outputPath: suspension.outputPath,
|
|
240
|
+
exitCodePath: suspension.exitCodePath,
|
|
241
|
+
expectedAppId: ref.routingDomain,
|
|
242
|
+
}),
|
|
203
243
|
readWorkCompletedAt: (ref, path) =>
|
|
204
244
|
readDetachedModalRuntimeCompletion({
|
|
205
245
|
sandboxId: ref.resourceId,
|