deepline 0.1.238 → 0.1.240
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/apps/play-runner-workers/src/entry.ts +3 -0
- package/dist/bundling-sources/sdk/src/client.ts +72 -8
- package/dist/bundling-sources/sdk/src/http.ts +1 -0
- package/dist/bundling-sources/sdk/src/index.ts +4 -0
- package/dist/bundling-sources/sdk/src/release.ts +2 -2
- package/dist/bundling-sources/sdk/src/types.ts +63 -2
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +43 -16
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +13 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-latency-trace.ts +3 -18
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +14 -0
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +12 -3
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +47 -6
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-session-execution.ts +38 -8
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +46 -5
- package/dist/bundling-sources/shared_libs/play-runtime/secret-redaction.ts +5 -1
- package/dist/cli/index.js +376 -49
- package/dist/cli/index.mjs +376 -49
- package/dist/index.d.mts +107 -6
- package/dist/index.d.ts +107 -6
- package/dist/index.js +55 -11
- package/dist/index.mjs +55 -11
- package/package.json +1 -1
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { DaytonaSandbox } from './daytona-lifecycle';
|
|
2
2
|
|
|
3
3
|
export type DetachedDaytonaStartupDiagnostic = {
|
|
4
|
+
readyMarkerObserved: boolean;
|
|
5
|
+
startAcknowledgementAttempted: boolean;
|
|
4
6
|
commandFound: boolean;
|
|
5
7
|
exitCode: number | null;
|
|
6
8
|
sessionFound: boolean;
|
|
@@ -51,6 +53,8 @@ export async function inspectDetachedDaytonaStartup(input: {
|
|
|
51
53
|
}): Promise<DetachedDaytonaStartupDiagnostic> {
|
|
52
54
|
const errors: string[] = [];
|
|
53
55
|
const diagnostic: DetachedDaytonaStartupDiagnostic = {
|
|
56
|
+
readyMarkerObserved: false,
|
|
57
|
+
startAcknowledgementAttempted: false,
|
|
54
58
|
commandFound: false,
|
|
55
59
|
exitCode: null,
|
|
56
60
|
sessionFound: false,
|
|
@@ -112,11 +116,17 @@ export type DetachedDaytonaCommandStart = {
|
|
|
112
116
|
cmdId: string;
|
|
113
117
|
};
|
|
114
118
|
|
|
115
|
-
|
|
119
|
+
// The marker now proves artifact materialization plus an accepted gateway
|
|
120
|
+
// heartbeat. Keep the handoff bounded, while allowing a cold Fly gateway and
|
|
121
|
+
// one timed-out heartbeat request to recover before the worker rejects it.
|
|
122
|
+
export const DAYTONA_RUNNER_READY_TIMEOUT_MS = 60_000;
|
|
116
123
|
const DAYTONA_RUNNER_READY_POLL_MS = 250;
|
|
117
124
|
|
|
118
125
|
export class DaytonaRunnerInitializationError extends Error {
|
|
119
|
-
constructor(
|
|
126
|
+
constructor(
|
|
127
|
+
message: string,
|
|
128
|
+
readonly startupDiagnostic: DetachedDaytonaStartupDiagnostic,
|
|
129
|
+
) {
|
|
120
130
|
super(message);
|
|
121
131
|
this.name = 'DaytonaRunnerInitializationError';
|
|
122
132
|
}
|
|
@@ -150,15 +160,17 @@ export async function startDetachedDaytonaCommand(input: {
|
|
|
150
160
|
|
|
151
161
|
/**
|
|
152
162
|
* A detached command id only proves that Daytona accepted the request. Before
|
|
153
|
-
* the worker parks, require a marker written
|
|
154
|
-
*
|
|
155
|
-
*
|
|
163
|
+
* the worker parks, require a marker written only after the initialized runner
|
|
164
|
+
* materializes its artifact and the receipt gateway accepts its first
|
|
165
|
+
* heartbeat. This closes the unobservable handoff where a sandbox or command
|
|
166
|
+
* disappears after executeSessionCommand but before durable runner liveness.
|
|
156
167
|
*/
|
|
157
168
|
export async function confirmDetachedDaytonaRunnerReady(input: {
|
|
158
169
|
sandbox: DaytonaSandbox;
|
|
159
170
|
sessionId: string;
|
|
160
171
|
cmdId: string;
|
|
161
172
|
readyPath: string;
|
|
173
|
+
startAckPath: string;
|
|
162
174
|
exitCodePath: string;
|
|
163
175
|
timeoutMs?: number;
|
|
164
176
|
pollMs?: number;
|
|
@@ -176,6 +188,8 @@ export async function confirmDetachedDaytonaRunnerReady(input: {
|
|
|
176
188
|
const pollMs = Math.max(1, input.pollMs ?? DAYTONA_RUNNER_READY_POLL_MS);
|
|
177
189
|
const deadline = now() + timeoutMs;
|
|
178
190
|
let markerDiagnosis = 'ready_marker_unavailable';
|
|
191
|
+
let readyMarkerObserved = false;
|
|
192
|
+
let startAcknowledgementAttempted = false;
|
|
179
193
|
|
|
180
194
|
while (now() < deadline) {
|
|
181
195
|
try {
|
|
@@ -183,8 +197,21 @@ export async function confirmDetachedDaytonaRunnerReady(input: {
|
|
|
183
197
|
const parsed = JSON.parse(marker.toString('utf-8')) as {
|
|
184
198
|
ready?: unknown;
|
|
185
199
|
};
|
|
186
|
-
if (parsed.ready === true)
|
|
187
|
-
|
|
200
|
+
if (parsed.ready === true) {
|
|
201
|
+
readyMarkerObserved = true;
|
|
202
|
+
startAcknowledgementAttempted = true;
|
|
203
|
+
try {
|
|
204
|
+
await input.sandbox.fs.uploadFile(
|
|
205
|
+
Buffer.from('{"start":true}', 'utf-8'),
|
|
206
|
+
input.startAckPath,
|
|
207
|
+
);
|
|
208
|
+
return;
|
|
209
|
+
} catch {
|
|
210
|
+
markerDiagnosis = 'start_ack_unavailable';
|
|
211
|
+
}
|
|
212
|
+
} else {
|
|
213
|
+
markerDiagnosis = 'ready_marker_invalid';
|
|
214
|
+
}
|
|
188
215
|
} catch {
|
|
189
216
|
markerDiagnosis = 'ready_marker_unavailable';
|
|
190
217
|
}
|
|
@@ -197,9 +224,12 @@ export async function confirmDetachedDaytonaRunnerReady(input: {
|
|
|
197
224
|
cmdId: input.cmdId,
|
|
198
225
|
exitCodePath: input.exitCodePath,
|
|
199
226
|
});
|
|
227
|
+
diagnostic.readyMarkerObserved = readyMarkerObserved;
|
|
228
|
+
diagnostic.startAcknowledgementAttempted = startAcknowledgementAttempted;
|
|
200
229
|
const oomIndicated =
|
|
201
230
|
diagnostic.exitCode === 137 || diagnostic.exitCodeFile === 137;
|
|
202
231
|
throw new DaytonaRunnerInitializationError(
|
|
203
|
-
`RUNTIME_SANDBOX_START_FAILED: Daytona accepted detached command ${input.cmdId} in sandbox ${input.sandbox.id}, but the play runner did not
|
|
232
|
+
`RUNTIME_SANDBOX_START_FAILED: Daytona accepted detached command ${input.cmdId} in sandbox ${input.sandbox.id}, but the play runner did not materialize and establish receipt-gateway liveness within ${timeoutMs}ms. Marker diagnosis: ${markerDiagnosis}. Daytona diagnosis: ${JSON.stringify(diagnostic)}. OOM is ${oomIndicated ? 'indicated by exit code 137' : 'not confirmed by the available Daytona evidence'}. The worker will not park this runner; the failed runner was stopped and was not retried in place.`,
|
|
233
|
+
diagnostic,
|
|
204
234
|
);
|
|
205
235
|
}
|
|
@@ -474,6 +474,27 @@ function isRetryableDaytonaInfrastructureFailure(error: unknown): boolean {
|
|
|
474
474
|
return DAYTONA_INFRASTRUCTURE_RETRY_PATTERN.test(message);
|
|
475
475
|
}
|
|
476
476
|
|
|
477
|
+
function isRetryableUnstartedDaytonaRunner(
|
|
478
|
+
error: unknown,
|
|
479
|
+
): error is DaytonaRunnerInitializationError {
|
|
480
|
+
if (!(error instanceof DaytonaRunnerInitializationError)) return false;
|
|
481
|
+
const diagnostic = error.startupDiagnostic;
|
|
482
|
+
// The ready fence runs before any customer play code. If Daytona accepted
|
|
483
|
+
// the command but the worker never observed its ready marker, the runner
|
|
484
|
+
// cannot pass the reverse start-ack fence and customer code cannot begin.
|
|
485
|
+
// Terminate that sandbox and retry once on a fresh one even when its Node
|
|
486
|
+
// process is still alive. Do not retry after a start acknowledgement was
|
|
487
|
+
// attempted (its write outcome may be ambiguous), or after a runner exit.
|
|
488
|
+
return (
|
|
489
|
+
diagnostic.commandFound &&
|
|
490
|
+
diagnostic.sessionFound &&
|
|
491
|
+
diagnostic.exitCode === null &&
|
|
492
|
+
diagnostic.exitCodeFile === null &&
|
|
493
|
+
!diagnostic.readyMarkerObserved &&
|
|
494
|
+
!diagnostic.startAcknowledgementAttempted
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
|
|
477
498
|
function prepareDaytonaExecution(
|
|
478
499
|
input: PlayRunnerPrepareInput,
|
|
479
500
|
callbacks: Parameters<PlayRunnerBackend['execute']>[1],
|
|
@@ -785,11 +806,11 @@ export const daytonaPlayRunnerBackend: PlayRunnerBackend = {
|
|
|
785
806
|
});
|
|
786
807
|
// Push execution (B2-final, park-and-wake): start the runner command
|
|
787
808
|
// DETACHED via a Daytona session and return a `detached_runner`
|
|
788
|
-
// suspension after the runner writes its one-time ready marker.
|
|
789
|
-
// marker
|
|
790
|
-
//
|
|
791
|
-
// the worker parks (releasing its
|
|
792
|
-
//
|
|
809
|
+
// suspension only after the runner writes its one-time ready marker.
|
|
810
|
+
// The marker proves artifact materialization and an accepted gateway
|
|
811
|
+
// heartbeat: a Daytona cmdId alone does not prove durable liveness.
|
|
812
|
+
// After that bounded startup check, the worker parks (releasing its
|
|
813
|
+
// claim and slot) and wakes on terminal push or the ceiling timeout.
|
|
793
814
|
const sessionId = `deepline-play-${randomUUID()}`;
|
|
794
815
|
let start: Awaited<ReturnType<typeof startDetachedDaytonaCommand>>;
|
|
795
816
|
try {
|
|
@@ -819,6 +840,7 @@ export const daytonaPlayRunnerBackend: PlayRunnerBackend = {
|
|
|
819
840
|
sessionId: start.sessionId,
|
|
820
841
|
cmdId: start.cmdId,
|
|
821
842
|
readyPath: stagedPayload.readyPath,
|
|
843
|
+
startAckPath: stagedPayload.startAckPath,
|
|
822
844
|
exitCodePath: stagedPayload.exitCodePath,
|
|
823
845
|
}),
|
|
824
846
|
cancellationPromise,
|
|
@@ -868,6 +890,25 @@ export const daytonaPlayRunnerBackend: PlayRunnerBackend = {
|
|
|
868
890
|
onCancel();
|
|
869
891
|
throw new Error(DAYTONA_CANCELLED_ERROR);
|
|
870
892
|
}
|
|
893
|
+
if (
|
|
894
|
+
executionAttempt < DAYTONA_RUNTIME_INITIALIZATION_MAX_ATTEMPTS &&
|
|
895
|
+
isRetryableUnstartedDaytonaRunner(error)
|
|
896
|
+
) {
|
|
897
|
+
emitDaytonaStage(callbacks, config.context, 'execute:retry', {
|
|
898
|
+
sandboxId: sandboxForAttempt?.id ?? null,
|
|
899
|
+
attempt: executionAttempt + 1,
|
|
900
|
+
reason: 'runner_startup_not_live',
|
|
901
|
+
error: error.message,
|
|
902
|
+
elapsedMs: Date.now() - startedAt,
|
|
903
|
+
});
|
|
904
|
+
if (sandboxForAttempt) {
|
|
905
|
+
activeAcquiredResource!.billingEndedAt = Date.now();
|
|
906
|
+
await reportRetiringRuntimeResource(activeAcquiredResource!);
|
|
907
|
+
sandboxCleanup.stashActiveSandboxForRetry();
|
|
908
|
+
}
|
|
909
|
+
acquiredSandboxPromise = sandboxLifecycle.createFreshSandbox();
|
|
910
|
+
continue;
|
|
911
|
+
}
|
|
871
912
|
if (
|
|
872
913
|
executionAttempt < DAYTONA_INFRASTRUCTURE_MAX_ATTEMPTS &&
|
|
873
914
|
isRetryableDaytonaInfrastructureFailure(error)
|
|
@@ -2,7 +2,11 @@ export const SECRET_REDACTION_PLACEHOLDER = '[REDACTED_SECRET]';
|
|
|
2
2
|
|
|
3
3
|
const BEARER_TOKEN_RE = /\bBearer\s+[A-Za-z0-9._~+/=-]{12,}\b/g;
|
|
4
4
|
const BASIC_CREDENTIAL_RE = /\bBasic\s+[A-Za-z0-9+/]{4,}={0,2}\b/g;
|
|
5
|
-
|
|
5
|
+
// Bound each segment so V8 cannot recursively backtrack across a multi-megabyte
|
|
6
|
+
// opaque receipt value that contains no dots. Real executor/provider JWT
|
|
7
|
+
// segments are far smaller; 16 KiB per segment still leaves ample headroom.
|
|
8
|
+
const JWT_RE =
|
|
9
|
+
/\b[A-Za-z0-9_-]{8,16384}\.[A-Za-z0-9_-]{1,16384}\.[A-Za-z0-9_-]{8,16384}\b/g;
|
|
6
10
|
const PRIVATE_KEY_RE =
|
|
7
11
|
/-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z0-9 ]*PRIVATE KEY-----/g;
|
|
8
12
|
const SENSITIVE_FIELD_NAME =
|