deepline 0.1.200 → 0.1.202
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 +49 -13
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/map-chunk-plan.ts +25 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-dispatch.ts +139 -124
- package/dist/bundling-sources/sdk/src/release.ts +2 -2
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +17 -2
- package/dist/bundling-sources/shared_libs/play-runtime/bounded-dispatch.ts +52 -0
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +152 -23
- package/dist/bundling-sources/shared_libs/play-runtime/coordinator-headers.ts +10 -0
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +11 -0
- package/dist/bundling-sources/shared_libs/play-runtime/execution-capabilities.ts +121 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/adaptive-admission.ts +6 -1
- package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-budget-state-backend.ts +22 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts +86 -19
- package/dist/bundling-sources/shared_libs/play-runtime/governor/block-reserving-budget-state-backend.ts +141 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/budget-state-backend.ts +43 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/governor.ts +129 -41
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +40 -2
- package/dist/bundling-sources/shared_libs/play-runtime/receipt-completion-sink.ts +92 -33
- package/dist/bundling-sources/shared_libs/play-runtime/resource-governor.ts +13 -5
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +43 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +122 -120
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +10 -8
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +140 -32
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +327 -31
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-scheduler-topology.ts +26 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +21 -0
- package/dist/bundling-sources/shared_libs/plays/dataset.ts +32 -10
- package/dist/cli/index.js +198 -76
- package/dist/cli/index.mjs +204 -82
- package/dist/index.js +3 -2
- package/dist/index.mjs +3 -2
- package/package.json +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/adaptive-tool-dispatcher.ts +0 -355
|
@@ -60,16 +60,18 @@ function nodeMaterializePayloadCommand(input: {
|
|
|
60
60
|
function remoteRuntimeContextForDaytona(
|
|
61
61
|
context: DaytonaExecutionContext,
|
|
62
62
|
): DaytonaRemoteRuntimeContext {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
};
|
|
63
|
+
const gatewayBaseUrl = context.receiptGatewayBaseUrl?.trim();
|
|
64
|
+
if (!gatewayBaseUrl) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
'Daytona execution requires DEEPLINE_RUNTIME_RECEIPT_GATEWAY_URL. Refusing to put app credentials or database sessions in the customer-code sandbox.',
|
|
67
|
+
);
|
|
69
68
|
}
|
|
70
69
|
return {
|
|
71
70
|
...context,
|
|
72
|
-
|
|
71
|
+
baseUrl: gatewayBaseUrl,
|
|
72
|
+
receiptGatewayBaseUrl: gatewayBaseUrl,
|
|
73
|
+
vercelProtectionBypassToken: null,
|
|
74
|
+
dbSessionStrategy: 'gateway_only',
|
|
73
75
|
preloadedDbSessions: undefined,
|
|
74
76
|
};
|
|
75
77
|
}
|
|
@@ -215,7 +217,7 @@ export async function stageDaytonaRunnerPayload(input: {
|
|
|
215
217
|
configPath,
|
|
216
218
|
artifactCodePath,
|
|
217
219
|
})} && DEEPLINE_PLAY_RUNNER_PROGRESS_EVENT_PATH=${shellQuote(progressEventPath)} DEEPLINE_PLAY_RUNNER_STDOUT_EVENT_MODE=compact node ${shellQuote(runnerPath)} ${shellQuote(configPath)}`;
|
|
218
|
-
const command = `rm -f ${shellQuote(outputPath)} ${shellQuote(exitCodePath)} ${shellQuote(progressEventPath)}; ( ${runnerCommand} ) > ${shellQuote(outputPath)} 2>&1; code=$?; printf '%s' "$code" > ${shellQuote(exitCodePath)}; printf 'deepline runner output captured: %s\\n' ${shellQuote(outputPath)}; exit "$code"`;
|
|
220
|
+
const command = `rm -f ${shellQuote(outputPath)} ${shellQuote(exitCodePath)} ${shellQuote(progressEventPath)} ${shellQuote(`${progressEventPath}.*`)}; ( ${runnerCommand} ) > ${shellQuote(outputPath)} 2>&1; code=$?; printf '%s' "$code" > ${shellQuote(exitCodePath)}; printf 'deepline runner output captured: %s\\n' ${shellQuote(outputPath)}; exit "$code"`;
|
|
219
221
|
|
|
220
222
|
return {
|
|
221
223
|
workDir: input.workDir,
|
|
@@ -46,6 +46,71 @@ const DAYTONA_RUNTIME_DB_CONNECT_MAX_ATTEMPTS = 2;
|
|
|
46
46
|
const DAYTONA_INFRASTRUCTURE_MAX_ATTEMPTS = 2;
|
|
47
47
|
const DAYTONA_UPLOAD_MAX_ATTEMPTS = 2;
|
|
48
48
|
const DAYTONA_UPLOAD_ATTEMPT_DEADLINE_MS = 90_000;
|
|
49
|
+
/**
|
|
50
|
+
* Client-side wall-clock grace for `sandbox.process.executeCommand`. The
|
|
51
|
+
* `timeout` argument we pass is a SERVER-side directive; the Daytona SDK's
|
|
52
|
+
* underlying HTTP call has no client deadline, so a wedged socket (observed:
|
|
53
|
+
* `AggregateError [ETIMEDOUT]`) leaves the await hanging forever and holds the
|
|
54
|
+
* worker slot. We race the call against this deadline = server timeout + grace,
|
|
55
|
+
* so a genuine server-side runtime-limit hit still returns first, but a wedged
|
|
56
|
+
* client transport fails the attempt loudly and lets the engine retry ladder
|
|
57
|
+
* take over instead of livelocking the slot.
|
|
58
|
+
*/
|
|
59
|
+
const DAYTONA_EXECUTE_CLIENT_GRACE_MS = 60_000;
|
|
60
|
+
const DAYTONA_EXECUTE_CLIENT_DEADLINE_MS =
|
|
61
|
+
DAYTONA_EXECUTE_TIMEOUT_SECONDS * 1_000 + DAYTONA_EXECUTE_CLIENT_GRACE_MS;
|
|
62
|
+
/**
|
|
63
|
+
* Bound for individual sandbox/runtime API calls that have no natural timeout
|
|
64
|
+
* (e.g. recording the acquired runtime resource). A wedged one of these must
|
|
65
|
+
* fail the attempt, not hold the slot.
|
|
66
|
+
*/
|
|
67
|
+
const DAYTONA_API_CALL_DEADLINE_MS = 60_000;
|
|
68
|
+
|
|
69
|
+
export class DaytonaOperationDeadlineError extends Error {
|
|
70
|
+
readonly operation: string;
|
|
71
|
+
readonly deadlineMs: number;
|
|
72
|
+
|
|
73
|
+
constructor(input: { operation: string; deadlineMs: number }) {
|
|
74
|
+
super(
|
|
75
|
+
`Daytona operation "${input.operation}" exceeded client deadline of ${input.deadlineMs}ms and was abandoned to free the worker slot.`,
|
|
76
|
+
);
|
|
77
|
+
this.name = 'DaytonaOperationDeadlineError';
|
|
78
|
+
this.operation = input.operation;
|
|
79
|
+
this.deadlineMs = input.deadlineMs;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Race `promise` against a wall-clock deadline. On breach, reject loudly with a
|
|
85
|
+
* `DaytonaOperationDeadlineError` (the caller's retry/failure classification
|
|
86
|
+
* decides what happens next). The timer is always cleared so a fast-resolving
|
|
87
|
+
* promise never leaves a dangling handle.
|
|
88
|
+
*/
|
|
89
|
+
export async function withDaytonaOperationDeadline<T>(input: {
|
|
90
|
+
operation: string;
|
|
91
|
+
deadlineMs: number;
|
|
92
|
+
promise: Promise<T>;
|
|
93
|
+
}): Promise<T> {
|
|
94
|
+
let deadlineTimer: ReturnType<typeof setTimeout> | null = null;
|
|
95
|
+
const deadlinePromise = new Promise<never>((_resolve, reject) => {
|
|
96
|
+
deadlineTimer = setTimeout(() => {
|
|
97
|
+
reject(
|
|
98
|
+
new DaytonaOperationDeadlineError({
|
|
99
|
+
operation: input.operation,
|
|
100
|
+
deadlineMs: input.deadlineMs,
|
|
101
|
+
}),
|
|
102
|
+
);
|
|
103
|
+
}, input.deadlineMs);
|
|
104
|
+
deadlineTimer.unref?.();
|
|
105
|
+
});
|
|
106
|
+
try {
|
|
107
|
+
return await Promise.race([input.promise, deadlinePromise]);
|
|
108
|
+
} finally {
|
|
109
|
+
if (deadlineTimer) {
|
|
110
|
+
clearTimeout(deadlineTimer);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
49
114
|
const RUNTIME_POSTGRES_CONNECT_RETRY_PATTERN =
|
|
50
115
|
/\bRuntime Postgres\b.*\b(connection timed out|connect timeout|ETIMEDOUT|ECONNRESET|ECONNREFUSED|Connection terminated|Connection ended unexpectedly)\b/i;
|
|
51
116
|
const DAYTONA_INFRASTRUCTURE_RETRY_PATTERN =
|
|
@@ -223,10 +288,11 @@ function createDaytonaProgressSidecarPoller(input: {
|
|
|
223
288
|
context: DaytonaExecutionContext;
|
|
224
289
|
startedAt: number;
|
|
225
290
|
processedEventKeys: Set<string>;
|
|
226
|
-
}): { start(): void; stop(): Promise<void> } {
|
|
291
|
+
}): { start(): void; stop(flush?: boolean): Promise<void> } {
|
|
227
292
|
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
228
293
|
let stopped = false;
|
|
229
294
|
let inFlight: Promise<void> = Promise.resolve();
|
|
295
|
+
let segment = 0;
|
|
230
296
|
let offsetBytes = 0;
|
|
231
297
|
let pendingLine = '';
|
|
232
298
|
let warned = false;
|
|
@@ -259,20 +325,39 @@ function createDaytonaProgressSidecarPoller(input: {
|
|
|
259
325
|
|
|
260
326
|
const poll = async (flushPartial = false): Promise<void> => {
|
|
261
327
|
try {
|
|
262
|
-
const
|
|
328
|
+
const manifest = await input.sandbox.fs.downloadFile(
|
|
263
329
|
input.progressEventPath,
|
|
264
330
|
5,
|
|
265
331
|
);
|
|
266
|
-
const
|
|
267
|
-
|
|
332
|
+
const parsedLatestSegment = Number.parseInt(
|
|
333
|
+
Buffer.isBuffer(manifest)
|
|
334
|
+
? manifest.toString('utf-8').trim()
|
|
335
|
+
: String(manifest).trim(),
|
|
336
|
+
10,
|
|
337
|
+
);
|
|
338
|
+
const latestSegment = Number.isFinite(parsedLatestSegment)
|
|
339
|
+
? Math.max(segment, parsedLatestSegment)
|
|
340
|
+
: segment;
|
|
341
|
+
while (segment <= latestSegment) {
|
|
342
|
+
const segmentPath = `${input.progressEventPath}.${String(
|
|
343
|
+
segment,
|
|
344
|
+
).padStart(6, '0')}`;
|
|
345
|
+
const file = await input.sandbox.fs.downloadFile(segmentPath, 5);
|
|
346
|
+
const buffer = Buffer.isBuffer(file) ? file : Buffer.from(file);
|
|
347
|
+
if (buffer.length < offsetBytes) {
|
|
348
|
+
offsetBytes = 0;
|
|
349
|
+
pendingLine = '';
|
|
350
|
+
}
|
|
351
|
+
if (buffer.length > offsetBytes) {
|
|
352
|
+
const chunk = buffer.subarray(offsetBytes).toString('utf-8');
|
|
353
|
+
offsetBytes = buffer.length;
|
|
354
|
+
processText(chunk, flushPartial && segment === latestSegment);
|
|
355
|
+
}
|
|
356
|
+
if (segment >= latestSegment) break;
|
|
357
|
+
segment += 1;
|
|
268
358
|
offsetBytes = 0;
|
|
269
|
-
pendingLine = '';
|
|
270
359
|
}
|
|
271
|
-
if (
|
|
272
|
-
const chunk = buffer.subarray(offsetBytes).toString('utf-8');
|
|
273
|
-
offsetBytes = buffer.length;
|
|
274
|
-
processText(chunk, flushPartial);
|
|
275
|
-
} else if (flushPartial && pendingLine.trim()) {
|
|
360
|
+
if (flushPartial && pendingLine.trim()) {
|
|
276
361
|
processText('\n', true);
|
|
277
362
|
}
|
|
278
363
|
} catch (error) {
|
|
@@ -304,14 +389,14 @@ function createDaytonaProgressSidecarPoller(input: {
|
|
|
304
389
|
start() {
|
|
305
390
|
inFlight = inFlight.then(() => poll()).finally(schedule);
|
|
306
391
|
},
|
|
307
|
-
async stop() {
|
|
392
|
+
async stop(flush = true) {
|
|
308
393
|
stopped = true;
|
|
309
394
|
if (timer) {
|
|
310
395
|
clearTimeout(timer);
|
|
311
396
|
timer = null;
|
|
312
397
|
}
|
|
313
398
|
await inFlight.catch(() => {});
|
|
314
|
-
await poll(true);
|
|
399
|
+
if (flush) await poll(true);
|
|
315
400
|
},
|
|
316
401
|
};
|
|
317
402
|
}
|
|
@@ -422,6 +507,12 @@ function isRetryableDaytonaInfrastructureFailure(error: unknown): boolean {
|
|
|
422
507
|
if (error instanceof DaytonaCommandTransportError) {
|
|
423
508
|
return false;
|
|
424
509
|
}
|
|
510
|
+
// A client-side deadline breach is a wedged transport (hung socket / SDK call
|
|
511
|
+
// with no client timeout). Retry once on a fresh sandbox before spending an
|
|
512
|
+
// engine attempt, same as the other transport-level infrastructure failures.
|
|
513
|
+
if (error instanceof DaytonaOperationDeadlineError) {
|
|
514
|
+
return true;
|
|
515
|
+
}
|
|
425
516
|
const message = error instanceof Error ? error.message : String(error);
|
|
426
517
|
if (!message || /cancelled|aborted/i.test(message)) {
|
|
427
518
|
return false;
|
|
@@ -566,15 +657,24 @@ export const daytonaPlayRunnerBackend: PlayRunnerBackend = {
|
|
|
566
657
|
acquiredSandbox.billingStartedAt - startedAt;
|
|
567
658
|
const sandbox = acquiredSandbox.sandbox;
|
|
568
659
|
sandboxForAttempt = sandbox;
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
660
|
+
const resourceAcquiredNotice = callbacks?.onRuntimeResourceAcquired?.(
|
|
661
|
+
{
|
|
662
|
+
kind: 'daytona_sandbox',
|
|
663
|
+
sandboxId: sandbox.id,
|
|
664
|
+
billingStartedAt: acquiredSandbox.billingStartedAt,
|
|
665
|
+
cpu: typeof sandbox.cpu === 'number' ? sandbox.cpu : null,
|
|
666
|
+
memoryGiB:
|
|
667
|
+
typeof sandbox.memory === 'number' ? sandbox.memory : null,
|
|
668
|
+
diskGiB: typeof sandbox.disk === 'number' ? sandbox.disk : null,
|
|
669
|
+
},
|
|
670
|
+
);
|
|
671
|
+
if (resourceAcquiredNotice) {
|
|
672
|
+
await withDaytonaOperationDeadline({
|
|
673
|
+
operation: 'onRuntimeResourceAcquired',
|
|
674
|
+
deadlineMs: DAYTONA_API_CALL_DEADLINE_MS,
|
|
675
|
+
promise: resourceAcquiredNotice,
|
|
676
|
+
});
|
|
677
|
+
}
|
|
578
678
|
throwIfCancellationRequested();
|
|
579
679
|
|
|
580
680
|
const { workdir: workDir } = loadDaytonaRunnerPathsConfig();
|
|
@@ -676,16 +776,19 @@ export const daytonaPlayRunnerBackend: PlayRunnerBackend = {
|
|
|
676
776
|
try {
|
|
677
777
|
progressSidecar.start();
|
|
678
778
|
execution = await Promise.race([
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
779
|
+
withDaytonaOperationDeadline({
|
|
780
|
+
operation: 'sandbox.process.executeCommand',
|
|
781
|
+
deadlineMs: DAYTONA_EXECUTE_CLIENT_DEADLINE_MS,
|
|
782
|
+
promise: sandbox.process.executeCommand(
|
|
783
|
+
stagedPayload.command,
|
|
784
|
+
stagedPayload.workDir,
|
|
785
|
+
undefined,
|
|
786
|
+
DAYTONA_EXECUTE_TIMEOUT_SECONDS,
|
|
787
|
+
),
|
|
788
|
+
}),
|
|
685
789
|
cancellationPromise,
|
|
686
790
|
]);
|
|
687
791
|
} catch (error) {
|
|
688
|
-
await progressSidecar.stop();
|
|
689
792
|
if (
|
|
690
793
|
cancellationRequested ||
|
|
691
794
|
callbacks?.cancellationSignal?.aborted
|
|
@@ -729,7 +832,10 @@ export const daytonaPlayRunnerBackend: PlayRunnerBackend = {
|
|
|
729
832
|
});
|
|
730
833
|
execution = recovered.execution;
|
|
731
834
|
} finally {
|
|
732
|
-
await progressSidecar.stop(
|
|
835
|
+
await progressSidecar.stop(
|
|
836
|
+
!cancellationRequested &&
|
|
837
|
+
callbacks?.cancellationSignal?.aborted !== true,
|
|
838
|
+
);
|
|
733
839
|
}
|
|
734
840
|
const runnerOutput = await resolveDaytonaCommandOutput({
|
|
735
841
|
sandbox,
|
|
@@ -818,9 +924,11 @@ export const daytonaPlayRunnerBackend: PlayRunnerBackend = {
|
|
|
818
924
|
error:
|
|
819
925
|
typeof execution.exitCode === 'number' &&
|
|
820
926
|
execution.exitCode !== 0
|
|
821
|
-
? `Daytona play runner exited with code ${execution.exitCode}
|
|
822
|
-
|
|
823
|
-
|
|
927
|
+
? `Daytona play runner exited with code ${execution.exitCode}${
|
|
928
|
+
execution.exitCode === 137
|
|
929
|
+
? ' (sandbox OOM or forced SIGKILL)'
|
|
930
|
+
: ''
|
|
931
|
+
}.${outputTail ? ` Output tail: ${outputTail}` : ''}`
|
|
824
932
|
: 'Daytona play runner produced no result.',
|
|
825
933
|
runtimeTiming,
|
|
826
934
|
}),
|