deepline 0.3.247 → 0.3.248
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 +177 -13
- package/dist/bundling-sources/sdk/src/index.ts +8 -0
- package/dist/bundling-sources/sdk/src/plays/input-example.ts +40 -0
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/sdk/src/runs/overview.ts +457 -0
- package/dist/bundling-sources/sdk/src/runs/receipts.ts +23 -0
- package/dist/bundling-sources/sdk/src/tool-calls.ts +21 -1
- package/dist/bundling-sources/sdk/src/types.ts +16 -1
- package/dist/bundling-sources/shared_libs/observability/scheduled-jobs.json +19 -1
- package/dist/bundling-sources/shared_libs/play-data-plane/column-names.ts +17 -3
- package/dist/bundling-sources/shared_libs/play-data-plane/sheet-contract.ts +40 -8
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +26 -1
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +142 -14
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +2 -1
- package/dist/bundling-sources/shared_libs/play-runtime/daytona-runtime-config.ts +6 -3
- package/dist/bundling-sources/shared_libs/play-runtime/daytona-sandbox-janitor.ts +26 -21
- package/dist/bundling-sources/shared_libs/play-runtime/daytona-sandbox-reaper.ts +23 -21
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-machines.ts +17 -0
- package/dist/bundling-sources/shared_libs/play-runtime/log-context.ts +105 -0
- package/dist/bundling-sources/shared_libs/play-runtime/output-size-limits.ts +8 -1
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +9 -0
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger-event-transport.ts +40 -4
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +50 -5
- package/dist/bundling-sources/shared_libs/play-runtime/run-lifecycle-policy.ts +3 -1
- package/dist/bundling-sources/shared_libs/play-runtime/run-log-buffer.ts +28 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +14 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-terminal-push.ts +56 -16
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-control-transport.ts +184 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-launch-transport.ts +327 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +87 -30
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-session-execution.ts +24 -13
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-start-admission.ts +23 -6
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +76 -33
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +90 -35
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-pool.ts +16 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-progress.ts +62 -21
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +368 -66
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/receipt-gateway.ts +22 -4
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-outbox-recovery.ts +105 -121
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-event-search.ts +1347 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-queue-health.ts +16 -19
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-receipt-value-storage.ts +14 -6
- package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +16 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-http-errors.ts +40 -0
- package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +5 -2
- package/dist/bundling-sources/shared_libs/plays/dataset-summary.ts +11 -7
- package/dist/bundling-sources/shared_libs/plays/static-pipeline-storage-codec.ts +5 -2
- package/dist/bundling-sources/shared_libs/plays/static-pipeline.ts +11 -2
- package/dist/cli/index.js +3504 -2368
- package/dist/cli/index.mjs +3188 -2052
- package/dist/{compiler-manifest-n8ZtPv2h.d.mts → compiler-manifest-DavmLa8y.d.mts} +1 -0
- package/dist/{compiler-manifest-n8ZtPv2h.d.ts → compiler-manifest-DavmLa8y.d.ts} +1 -0
- package/dist/index.d.mts +97 -5
- package/dist/index.d.ts +97 -5
- package/dist/index.js +135 -15
- package/dist/index.mjs +135 -15
- package/dist/install-integrity.json +9 -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 +1 -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,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createDaytonaLaunchClient } from './daytona-launch-transport';
|
|
2
|
+
import type { DaytonaClientOptions } from '../../daytona-runtime-config';
|
|
3
|
+
import {
|
|
4
|
+
createDaytonaControlClient,
|
|
5
|
+
DAYTONA_CONTROL_TIMEOUT_MS,
|
|
6
|
+
DaytonaControlTimeoutError,
|
|
7
|
+
} from './daytona-control-transport';
|
|
2
8
|
import { randomUUID } from 'node:crypto';
|
|
3
9
|
import type {
|
|
4
10
|
PlayRunnerBackend,
|
|
@@ -51,8 +57,6 @@ import { RUNTIME_RELIABILITY_POLICY } from '../../runtime-reliability-policy';
|
|
|
51
57
|
import { daytonaSandboxStartAdmission } from './daytona-start-admission';
|
|
52
58
|
import { RUNNER_MEMORY_CGROUP_ROOT } from './daytona-runtime-watchdog';
|
|
53
59
|
|
|
54
|
-
const DAYTONA_COMMAND_RECOVERY_TIMEOUT_MS =
|
|
55
|
-
RUNTIME_RELIABILITY_POLICY.sandbox.daytonaCommandRecoveryTimeoutMs;
|
|
56
60
|
const DAYTONA_COMMAND_RECOVERY_POLL_MS =
|
|
57
61
|
RUNTIME_RELIABILITY_POLICY.sandbox.daytonaCommandRecoveryPollMs;
|
|
58
62
|
const DAYTONA_INFRASTRUCTURE_MAX_ATTEMPTS = 2;
|
|
@@ -275,6 +279,7 @@ type DaytonaPreparedExecution = PlayRunnerPreparedExecution & {
|
|
|
275
279
|
kind: 'daytona';
|
|
276
280
|
bundlePromise: Promise<string>;
|
|
277
281
|
sandboxLifecycle: OneShotDaytonaSandboxLifecycle;
|
|
282
|
+
daytona: DaytonaClient;
|
|
278
283
|
};
|
|
279
284
|
|
|
280
285
|
class DaytonaUploadDeadlineBreachError extends Error {
|
|
@@ -295,13 +300,11 @@ class DaytonaUploadDeadlineBreachError extends Error {
|
|
|
295
300
|
}
|
|
296
301
|
|
|
297
302
|
export const daytonaSdkClientFactory = {
|
|
298
|
-
create(input:
|
|
299
|
-
return
|
|
300
|
-
},
|
|
301
|
-
/** Full client (get/delete by id) for best-effort GC of orphaned sandboxes. */
|
|
302
|
-
createFull(input: ConstructorParameters<typeof Daytona>[0]): Daytona {
|
|
303
|
-
return new Daytona(input);
|
|
303
|
+
create(input: DaytonaClientOptions, signal?: AbortSignal): DaytonaClient {
|
|
304
|
+
return createDaytonaLaunchClient(input, signal);
|
|
304
305
|
},
|
|
306
|
+
/** Request-scoped native deadlines for detached recovery and cleanup. */
|
|
307
|
+
createControl: createDaytonaControlClient,
|
|
305
308
|
};
|
|
306
309
|
|
|
307
310
|
function stringProperty(value: unknown, key: string): string | null {
|
|
@@ -667,11 +670,11 @@ export async function readDetachedDaytonaCrashDiagnostic(input: {
|
|
|
667
670
|
resourceSnapshot: null,
|
|
668
671
|
collectionFailure,
|
|
669
672
|
});
|
|
673
|
+
let control: ReturnType<typeof createDaytonaControlClient> | undefined;
|
|
670
674
|
try {
|
|
671
675
|
const { clientOptions } = loadDaytonaRequiredConfig();
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
.get(input.sandboxId)) as DaytonaSandbox;
|
|
676
|
+
control = daytonaSdkClientFactory.createControl(clientOptions);
|
|
677
|
+
const sandbox = (await control.get(input.sandboxId)) as DaytonaSandbox;
|
|
675
678
|
const expectedOrganizationId = input.expectedOrganizationId?.trim() || null;
|
|
676
679
|
const observedOrganizationId = sandbox.organizationId?.trim() || null;
|
|
677
680
|
if (
|
|
@@ -781,6 +784,8 @@ export async function readDetachedDaytonaCrashDiagnostic(input: {
|
|
|
781
784
|
};
|
|
782
785
|
} catch (error) {
|
|
783
786
|
return unavailable(describeDaytonaLookupFailure(error));
|
|
787
|
+
} finally {
|
|
788
|
+
control?.dispose();
|
|
784
789
|
}
|
|
785
790
|
}
|
|
786
791
|
|
|
@@ -829,10 +834,11 @@ export async function inspectDetachedDaytonaRunner(input: {
|
|
|
829
834
|
detail: string | null;
|
|
830
835
|
};
|
|
831
836
|
}> {
|
|
837
|
+
let control: ReturnType<typeof createDaytonaControlClient> | undefined;
|
|
832
838
|
try {
|
|
833
839
|
const { clientOptions } = loadDaytonaRequiredConfig();
|
|
834
|
-
|
|
835
|
-
const sandbox = (await
|
|
840
|
+
control = daytonaSdkClientFactory.createControl(clientOptions);
|
|
841
|
+
const sandbox = (await control.get(input.sandboxId)) as DaytonaSandbox;
|
|
836
842
|
const expectedOrganizationId = input.expectedOrganizationId?.trim() || null;
|
|
837
843
|
const observedOrganizationId = sandbox.organizationId?.trim() || null;
|
|
838
844
|
if (
|
|
@@ -868,11 +874,15 @@ export async function inspectDetachedDaytonaRunner(input: {
|
|
|
868
874
|
error: error instanceof Error ? error.message : String(error),
|
|
869
875
|
});
|
|
870
876
|
}
|
|
877
|
+
// The full captured stdout file can scale with customer output. Metadata
|
|
878
|
+
// time must not consume its existing independent file-recovery budget.
|
|
879
|
+
const recoveryFiles = control.legacyOutputRecovery(sandbox);
|
|
880
|
+
control.dispose();
|
|
871
881
|
const recovered = await recoverDaytonaCommandExecution({
|
|
872
|
-
sandbox,
|
|
882
|
+
sandbox: recoveryFiles,
|
|
873
883
|
outputPath: input.outputPath,
|
|
874
884
|
exitCodePath: input.exitCodePath,
|
|
875
|
-
|
|
885
|
+
deadlineAtMs: Date.now() + 10_000,
|
|
876
886
|
});
|
|
877
887
|
if (!recovered.execution) {
|
|
878
888
|
return {
|
|
@@ -916,6 +926,8 @@ export async function inspectDetachedDaytonaRunner(input: {
|
|
|
916
926
|
detail: failure.detail,
|
|
917
927
|
},
|
|
918
928
|
};
|
|
929
|
+
} finally {
|
|
930
|
+
control?.dispose();
|
|
919
931
|
}
|
|
920
932
|
}
|
|
921
933
|
|
|
@@ -925,11 +937,11 @@ export async function readDetachedDaytonaRuntimeCompletion(input: {
|
|
|
925
937
|
runtimeCompletedPath: string;
|
|
926
938
|
expectedOrganizationId?: string | null;
|
|
927
939
|
}): Promise<number | null> {
|
|
940
|
+
let control: ReturnType<typeof createDaytonaControlClient> | undefined;
|
|
928
941
|
try {
|
|
929
942
|
const { clientOptions } = loadDaytonaRequiredConfig();
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
.get(input.sandboxId)) as DaytonaSandbox;
|
|
943
|
+
control = daytonaSdkClientFactory.createControl(clientOptions);
|
|
944
|
+
const sandbox = (await control.get(input.sandboxId)) as DaytonaSandbox;
|
|
933
945
|
const expectedOrganizationId = input.expectedOrganizationId?.trim() || null;
|
|
934
946
|
const observedOrganizationId = sandbox.organizationId?.trim() || null;
|
|
935
947
|
if (
|
|
@@ -965,6 +977,8 @@ export async function readDetachedDaytonaRuntimeCompletion(input: {
|
|
|
965
977
|
},
|
|
966
978
|
);
|
|
967
979
|
return null;
|
|
980
|
+
} finally {
|
|
981
|
+
control?.dispose();
|
|
968
982
|
}
|
|
969
983
|
}
|
|
970
984
|
|
|
@@ -996,10 +1010,14 @@ export async function deleteDaytonaSandboxByIdWithOutcome(input: {
|
|
|
996
1010
|
detail: 'Sandbox ID is required.',
|
|
997
1011
|
};
|
|
998
1012
|
}
|
|
1013
|
+
let control: ReturnType<typeof createDaytonaControlClient> | undefined;
|
|
999
1014
|
try {
|
|
1000
1015
|
const { clientOptions } = loadDaytonaRequiredConfig();
|
|
1001
|
-
|
|
1002
|
-
|
|
1016
|
+
control = daytonaSdkClientFactory.createControl(
|
|
1017
|
+
clientOptions,
|
|
1018
|
+
(input.timeoutSeconds ?? 30) * 1_000,
|
|
1019
|
+
);
|
|
1020
|
+
const sandbox = await control.get(sandboxId);
|
|
1003
1021
|
const observedOrganizationId = sandbox.organizationId?.trim() || null;
|
|
1004
1022
|
if (
|
|
1005
1023
|
expectedOrganizationId &&
|
|
@@ -1012,7 +1030,7 @@ export async function deleteDaytonaSandboxByIdWithOutcome(input: {
|
|
|
1012
1030
|
detail: 'Sandbox belongs to a different Daytona organization.',
|
|
1013
1031
|
};
|
|
1014
1032
|
}
|
|
1015
|
-
await
|
|
1033
|
+
await control.delete(sandbox);
|
|
1016
1034
|
return {
|
|
1017
1035
|
kind: 'deleted',
|
|
1018
1036
|
organizationId: observedOrganizationId,
|
|
@@ -1077,6 +1095,8 @@ export async function deleteDaytonaSandboxByIdWithOutcome(input: {
|
|
|
1077
1095
|
(failure.httpStatus ? `http_${failure.httpStatus}` : 'delete_failed'),
|
|
1078
1096
|
detail: failure.detail,
|
|
1079
1097
|
};
|
|
1098
|
+
} finally {
|
|
1099
|
+
control?.dispose();
|
|
1080
1100
|
}
|
|
1081
1101
|
}
|
|
1082
1102
|
|
|
@@ -1140,7 +1160,7 @@ function emitDaytonaStage(
|
|
|
1140
1160
|
}
|
|
1141
1161
|
|
|
1142
1162
|
async function downloadDaytonaTextFile(
|
|
1143
|
-
sandbox: DaytonaSandbox,
|
|
1163
|
+
sandbox: Pick<DaytonaSandbox, 'fs'>,
|
|
1144
1164
|
remotePath: string,
|
|
1145
1165
|
): Promise<string> {
|
|
1146
1166
|
const file = await sandbox.fs.downloadFile(remotePath, 15);
|
|
@@ -1148,13 +1168,12 @@ async function downloadDaytonaTextFile(
|
|
|
1148
1168
|
}
|
|
1149
1169
|
|
|
1150
1170
|
async function recoverDaytonaCommandExecution(input: {
|
|
1151
|
-
sandbox: DaytonaSandbox
|
|
1171
|
+
sandbox: Pick<DaytonaSandbox, 'fs'>;
|
|
1152
1172
|
outputPath: string;
|
|
1153
1173
|
exitCodePath: string;
|
|
1154
|
-
|
|
1174
|
+
deadlineAtMs: number;
|
|
1155
1175
|
}): Promise<{ execution: DaytonaExecution | null; lastError: unknown }> {
|
|
1156
|
-
const deadline =
|
|
1157
|
-
Date.now() + (input.timeoutMs ?? DAYTONA_COMMAND_RECOVERY_TIMEOUT_MS);
|
|
1176
|
+
const deadline = input.deadlineAtMs;
|
|
1158
1177
|
let lastError: unknown = null;
|
|
1159
1178
|
|
|
1160
1179
|
while (Date.now() < deadline) {
|
|
@@ -1177,7 +1196,9 @@ async function recoverDaytonaCommandExecution(input: {
|
|
|
1177
1196
|
};
|
|
1178
1197
|
} catch (error) {
|
|
1179
1198
|
lastError = error;
|
|
1180
|
-
|
|
1199
|
+
const remainingMs = deadline - Date.now();
|
|
1200
|
+
if (remainingMs <= 0) break;
|
|
1201
|
+
await sleep(Math.min(DAYTONA_COMMAND_RECOVERY_POLL_MS, remainingMs));
|
|
1181
1202
|
}
|
|
1182
1203
|
}
|
|
1183
1204
|
|
|
@@ -1273,6 +1294,7 @@ export function retryableRuntimeInitializationFailureReason(
|
|
|
1273
1294
|
|
|
1274
1295
|
function isRetryableDaytonaInfrastructureFailure(error: unknown): boolean {
|
|
1275
1296
|
if (error instanceof DaytonaRunnerInitializationError) return false;
|
|
1297
|
+
if (error instanceof DaytonaControlTimeoutError) return true;
|
|
1276
1298
|
const message = error instanceof Error ? error.message : String(error);
|
|
1277
1299
|
if (!message || /cancelled|aborted/i.test(message)) {
|
|
1278
1300
|
return false;
|
|
@@ -1304,7 +1326,10 @@ function prepareDaytonaExecution(
|
|
|
1304
1326
|
callbacks: Parameters<PlayRunnerBackend['execute']>[1],
|
|
1305
1327
|
): DaytonaPreparedExecution {
|
|
1306
1328
|
const { clientOptions: daytonaOptions } = loadDaytonaRequiredConfig();
|
|
1307
|
-
const daytona = daytonaSdkClientFactory.create(
|
|
1329
|
+
const daytona = daytonaSdkClientFactory.create(
|
|
1330
|
+
daytonaOptions,
|
|
1331
|
+
callbacks?.cancellationSignal,
|
|
1332
|
+
);
|
|
1308
1333
|
const bundlePromise = buildPlayRunnerBundle();
|
|
1309
1334
|
const sandboxLifecycle = createOneShotDaytonaSandboxLifecycle({
|
|
1310
1335
|
daytona,
|
|
@@ -1318,9 +1343,11 @@ function prepareDaytonaExecution(
|
|
|
1318
1343
|
: undefined,
|
|
1319
1344
|
releaseSandboxCapacity: callbacks?.releaseSandboxCapacity,
|
|
1320
1345
|
startAdmission: daytonaSandboxStartAdmission,
|
|
1346
|
+
cancellationSignal: callbacks?.cancellationSignal,
|
|
1321
1347
|
});
|
|
1322
1348
|
return {
|
|
1323
1349
|
kind: 'daytona',
|
|
1350
|
+
daytona,
|
|
1324
1351
|
bundlePromise,
|
|
1325
1352
|
sandboxLifecycle,
|
|
1326
1353
|
dispose: sandboxLifecycle.dispose,
|
|
@@ -1566,6 +1593,17 @@ export const daytonaPlayRunnerBackend: PlayRunnerBackend = {
|
|
|
1566
1593
|
sandbox,
|
|
1567
1594
|
sessionId,
|
|
1568
1595
|
readiness: readRunnerReadiness,
|
|
1596
|
+
runControl: async (operation) => {
|
|
1597
|
+
const scope = preparedExecution.daytona.scope(
|
|
1598
|
+
sandbox,
|
|
1599
|
+
DAYTONA_CONTROL_TIMEOUT_MS,
|
|
1600
|
+
);
|
|
1601
|
+
try {
|
|
1602
|
+
return await operation(scope.sandbox);
|
|
1603
|
+
} finally {
|
|
1604
|
+
scope.dispose();
|
|
1605
|
+
}
|
|
1606
|
+
},
|
|
1569
1607
|
cancellation: cancellationPromise,
|
|
1570
1608
|
onCommandAccepted: async () => {
|
|
1571
1609
|
await callbacks?.onRunnerCommandAccepted?.({
|
|
@@ -1610,10 +1648,14 @@ export const daytonaPlayRunnerBackend: PlayRunnerBackend = {
|
|
|
1610
1648
|
let stagedPayload: Awaited<
|
|
1611
1649
|
ReturnType<typeof stageDaytonaRunnerPayload>
|
|
1612
1650
|
>;
|
|
1651
|
+
const uploadScope = preparedExecution.daytona.scope(
|
|
1652
|
+
sandbox,
|
|
1653
|
+
DAYTONA_UPLOAD_ATTEMPT_DEADLINE_MS,
|
|
1654
|
+
);
|
|
1613
1655
|
try {
|
|
1614
1656
|
stagedPayload = await Promise.race([
|
|
1615
1657
|
stageDaytonaRunnerPayload({
|
|
1616
|
-
sandbox,
|
|
1658
|
+
sandbox: uploadScope.sandbox,
|
|
1617
1659
|
bundlePromise,
|
|
1618
1660
|
config,
|
|
1619
1661
|
workDir,
|
|
@@ -1639,7 +1681,7 @@ export const daytonaPlayRunnerBackend: PlayRunnerBackend = {
|
|
|
1639
1681
|
if (executionAttempt < DAYTONA_UPLOAD_MAX_ATTEMPTS) {
|
|
1640
1682
|
acquiredSandbox.billingEndedAt = Date.now();
|
|
1641
1683
|
await reportRetiringRuntimeResource(acquiredSandbox);
|
|
1642
|
-
sandboxCleanup.stashActiveSandboxForRetry();
|
|
1684
|
+
await sandboxCleanup.stashActiveSandboxForRetry();
|
|
1643
1685
|
emitDaytonaStage(callbacks, config.context, 'recreate:start', {
|
|
1644
1686
|
previousSandboxId: sandbox.id,
|
|
1645
1687
|
attempt: executionAttempt + 1,
|
|
@@ -1652,6 +1694,7 @@ export const daytonaPlayRunnerBackend: PlayRunnerBackend = {
|
|
|
1652
1694
|
}
|
|
1653
1695
|
throw error;
|
|
1654
1696
|
} finally {
|
|
1697
|
+
uploadScope.dispose();
|
|
1655
1698
|
if (uploadDeadlineTimer) {
|
|
1656
1699
|
clearTimeout(uploadDeadlineTimer);
|
|
1657
1700
|
}
|
|
@@ -1768,7 +1811,7 @@ export const daytonaPlayRunnerBackend: PlayRunnerBackend = {
|
|
|
1768
1811
|
if (sandboxForAttempt) {
|
|
1769
1812
|
activeAcquiredResource!.billingEndedAt = Date.now();
|
|
1770
1813
|
await reportRetiringRuntimeResource(activeAcquiredResource!);
|
|
1771
|
-
sandboxCleanup.stashActiveSandboxForRetry();
|
|
1814
|
+
await sandboxCleanup.stashActiveSandboxForRetry();
|
|
1772
1815
|
}
|
|
1773
1816
|
acquiredSandboxPromise = sandboxLifecycle.createFreshSandbox();
|
|
1774
1817
|
continue;
|
|
@@ -1796,7 +1839,7 @@ export const daytonaPlayRunnerBackend: PlayRunnerBackend = {
|
|
|
1796
1839
|
const sandbox = sandboxCleanup.currentSandbox();
|
|
1797
1840
|
if (sandbox) {
|
|
1798
1841
|
try {
|
|
1799
|
-
await
|
|
1842
|
+
await preparedExecution.daytona.delete(sandbox, 30);
|
|
1800
1843
|
console.info(
|
|
1801
1844
|
'[play-runner.daytona.resource_report_failure_cleanup_done]',
|
|
1802
1845
|
{ sandboxId: sandbox.id },
|
package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts
CHANGED
|
@@ -2885,21 +2885,33 @@ async function executeAssignedAbsurdAttempt(input: {
|
|
|
2885
2885
|
),
|
|
2886
2886
|
),
|
|
2887
2887
|
});
|
|
2888
|
+
const finalizeSuspensionProgress = () =>
|
|
2889
|
+
runWithAbsurdExecutionLeaseHeartbeat({
|
|
2890
|
+
ctx,
|
|
2891
|
+
runId,
|
|
2892
|
+
operation: () =>
|
|
2893
|
+
input.progressReporter.finalize(input.runnerResult, {
|
|
2894
|
+
status: 'running',
|
|
2895
|
+
required: suspension.kind !== 'detached_runner',
|
|
2896
|
+
replayAllTerminalLogs: input.fromDetachedRunner,
|
|
2897
|
+
terminalLogProducerAttempt: input.terminalLogProducerAttempt,
|
|
2898
|
+
}),
|
|
2899
|
+
});
|
|
2900
|
+
// A real suspension closes an invocation's counter segment before its
|
|
2901
|
+
// wait or handoff. Detached terminal recovery can replay the retained
|
|
2902
|
+
// result and its idempotent final segment after a crash. A detached_runner
|
|
2903
|
+
// placeholder still owns a live runner; preserve its park-first recovery.
|
|
2904
|
+
if (suspension.kind !== 'detached_runner') {
|
|
2905
|
+
await finalizeSuspensionProgress();
|
|
2906
|
+
}
|
|
2888
2907
|
const outcome = await createPlayRunAttemptSuspensionJob({
|
|
2889
2908
|
store: attemptStore,
|
|
2890
2909
|
engine,
|
|
2891
2910
|
lifecycle: {
|
|
2892
2911
|
waiting: async (lifecycle) => {
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
operation: () =>
|
|
2897
|
-
input.progressReporter.finalize(input.runnerResult, {
|
|
2898
|
-
status: 'running',
|
|
2899
|
-
replayAllTerminalLogs: input.fromDetachedRunner,
|
|
2900
|
-
terminalLogProducerAttempt: input.terminalLogProducerAttempt,
|
|
2901
|
-
}),
|
|
2902
|
-
});
|
|
2912
|
+
if (suspension.kind === 'detached_runner') {
|
|
2913
|
+
await finalizeSuspensionProgress();
|
|
2914
|
+
}
|
|
2903
2915
|
await projectLifecycleOutbox({
|
|
2904
2916
|
ctx,
|
|
2905
2917
|
deps,
|
|
@@ -6996,30 +7008,27 @@ async function reconcileReservedDaytonaCancellationOnce(input: {
|
|
|
6996
7008
|
),
|
|
6997
7009
|
);
|
|
6998
7010
|
if (!operation) return;
|
|
6999
|
-
const [{
|
|
7000
|
-
|
|
7001
|
-
|
|
7002
|
-
|
|
7003
|
-
|
|
7011
|
+
const [{ createDaytonaControlClient }, { loadDaytonaRequiredConfig }] =
|
|
7012
|
+
await Promise.all([
|
|
7013
|
+
import('../runner-backends/backends/daytona-control-transport'),
|
|
7014
|
+
import('../daytona-runtime-config'),
|
|
7015
|
+
]);
|
|
7016
|
+
const control = createDaytonaControlClient(
|
|
7004
7017
|
loadDaytonaRequiredConfig({ environment: input.environment }).clientOptions,
|
|
7018
|
+
ABSURD_SANDBOX_GC_TIMEOUT_MS,
|
|
7005
7019
|
);
|
|
7006
7020
|
const sandboxName = `dl-${operation.providerOperationToken}`;
|
|
7007
|
-
let sandbox: Awaited<ReturnType<typeof
|
|
7021
|
+
let sandbox: Awaited<ReturnType<typeof control.get>>;
|
|
7008
7022
|
try {
|
|
7009
|
-
sandbox = await
|
|
7010
|
-
deleteRuntimeSandbox: (name) => daytona.get(name),
|
|
7011
|
-
sandboxId: sandboxName,
|
|
7012
|
-
timeoutMs: ABSURD_SANDBOX_GC_TIMEOUT_MS,
|
|
7013
|
-
});
|
|
7023
|
+
sandbox = await control.get(sandboxName);
|
|
7014
7024
|
} catch (error) {
|
|
7015
7025
|
const message = error instanceof Error ? error.message : String(error);
|
|
7016
7026
|
if (!/\b(?:sandbox )?not found\b|\b404\b/i.test(message)) throw error;
|
|
7017
7027
|
if (
|
|
7018
7028
|
Date.now() - operation.createdAtMs <
|
|
7019
7029
|
RUNTIME_RELIABILITY_POLICY.recovery.horizonMs
|
|
7020
|
-
)
|
|
7030
|
+
)
|
|
7021
7031
|
return;
|
|
7022
|
-
}
|
|
7023
7032
|
await input.runWithClient((client) =>
|
|
7024
7033
|
abandonRuntimeSandboxOperationReservation(
|
|
7025
7034
|
client,
|
|
@@ -7028,6 +7037,8 @@ async function reconcileReservedDaytonaCancellationOnce(input: {
|
|
|
7028
7037
|
),
|
|
7029
7038
|
);
|
|
7030
7039
|
return;
|
|
7040
|
+
} finally {
|
|
7041
|
+
control.dispose();
|
|
7031
7042
|
}
|
|
7032
7043
|
if (sandbox.labels?.operationKey !== operation.providerOperationToken) {
|
|
7033
7044
|
throw new Error(
|
|
@@ -7422,6 +7433,49 @@ export type RuntimeWorkerHost = {
|
|
|
7422
7433
|
close(): Promise<void>;
|
|
7423
7434
|
};
|
|
7424
7435
|
|
|
7436
|
+
/** Own shutdown ordering across the primary queue, listener and maintenance.
|
|
7437
|
+
* Stop accepting claims synchronously; accepted work and every controller
|
|
7438
|
+
* must settle before shutdown succeeds or reports its collected failures. */
|
|
7439
|
+
export function createAbsurdRuntimeWorkerHostLifecycle(input: {
|
|
7440
|
+
worker: RuntimeWorkerHost;
|
|
7441
|
+
closeWake: () => void;
|
|
7442
|
+
stopControllers: () => void;
|
|
7443
|
+
controllers: readonly (Promise<unknown> | null)[];
|
|
7444
|
+
}): RuntimeWorkerHost {
|
|
7445
|
+
let closing: Promise<void> | null = null;
|
|
7446
|
+
return {
|
|
7447
|
+
close: () => {
|
|
7448
|
+
if (closing) return closing;
|
|
7449
|
+
const start = (operation: () => unknown): Promise<unknown> => {
|
|
7450
|
+
try {
|
|
7451
|
+
return Promise.resolve(operation());
|
|
7452
|
+
} catch (error) {
|
|
7453
|
+
return Promise.reject(error);
|
|
7454
|
+
}
|
|
7455
|
+
};
|
|
7456
|
+
// Invoke all stop operations now, before awaiting any controller. Even
|
|
7457
|
+
// a synchronous listener failure must not leave the queue claiming.
|
|
7458
|
+
const stops = [
|
|
7459
|
+
start(input.stopControllers),
|
|
7460
|
+
start(() => input.worker.close()),
|
|
7461
|
+
start(input.closeWake),
|
|
7462
|
+
];
|
|
7463
|
+
closing = Promise.allSettled([...stops, ...input.controllers]).then(
|
|
7464
|
+
(results) => {
|
|
7465
|
+
const errors = results.flatMap((result) =>
|
|
7466
|
+
result.status === 'rejected' ? [result.reason] : [],
|
|
7467
|
+
);
|
|
7468
|
+
if (errors.length === 1) throw errors[0];
|
|
7469
|
+
if (errors.length > 1) {
|
|
7470
|
+
throw new AggregateError(errors, 'Absurd worker shutdown failed.');
|
|
7471
|
+
}
|
|
7472
|
+
},
|
|
7473
|
+
);
|
|
7474
|
+
return closing;
|
|
7475
|
+
},
|
|
7476
|
+
};
|
|
7477
|
+
}
|
|
7478
|
+
|
|
7425
7479
|
function resolveAbsurdDrainExit(explicit?: boolean | null): boolean {
|
|
7426
7480
|
if (explicit != null) return explicit;
|
|
7427
7481
|
return process.env[ABSURD_DRAIN_EXIT_ENV]?.trim() === '1';
|
|
@@ -7777,23 +7831,24 @@ export async function startRuntimeWorkerHost(
|
|
|
7777
7831
|
workerName: workerBase,
|
|
7778
7832
|
});
|
|
7779
7833
|
|
|
7780
|
-
return {
|
|
7781
|
-
|
|
7782
|
-
|
|
7783
|
-
|
|
7784
|
-
|
|
7785
|
-
|
|
7834
|
+
return createAbsurdRuntimeWorkerHostLifecycle({
|
|
7835
|
+
worker,
|
|
7836
|
+
// Release LISTEN before joining accepted work, so a replacement can elect
|
|
7837
|
+
// its listener without exhausting primary scheduler sessions.
|
|
7838
|
+
closeWake: () => onWake?.close(),
|
|
7839
|
+
stopControllers: () => {
|
|
7786
7840
|
drainStopped = true;
|
|
7787
7841
|
billingStopped = true;
|
|
7788
7842
|
cleanupStopped = true;
|
|
7789
7843
|
adoptionStopped = true;
|
|
7790
|
-
await drainWatch;
|
|
7791
|
-
await adoptionSupervisor;
|
|
7792
|
-
await billingController;
|
|
7793
|
-
await cleanupController;
|
|
7794
|
-
await worker.close();
|
|
7795
7844
|
},
|
|
7796
|
-
|
|
7845
|
+
controllers: [
|
|
7846
|
+
drainWatch,
|
|
7847
|
+
adoptionSupervisor,
|
|
7848
|
+
billingController,
|
|
7849
|
+
cleanupController,
|
|
7850
|
+
],
|
|
7851
|
+
});
|
|
7797
7852
|
}
|
|
7798
7853
|
|
|
7799
7854
|
// Re-export for the entrypoint script's telemetry/observability wiring.
|
|
@@ -69,6 +69,22 @@ export function normalizePostgresConnectionStringForPg(url: string): string {
|
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
/** PlanetScale's connection login includes a branch suffix; SQL sees the role before it. */
|
|
73
|
+
export function planetScalePooledSqlRole(connectionString: string): string {
|
|
74
|
+
const url = new URL(connectionString);
|
|
75
|
+
const username = decodeURIComponent(url.username);
|
|
76
|
+
const match = /^([A-Za-z_][A-Za-z0-9_]*)\.[A-Za-z0-9_]+$/.exec(username);
|
|
77
|
+
if (
|
|
78
|
+
!['postgres:', 'postgresql:'].includes(url.protocol) ||
|
|
79
|
+
!url.hostname.endsWith('.psdb.cloud') ||
|
|
80
|
+
url.port !== '6432' ||
|
|
81
|
+
!match
|
|
82
|
+
) {
|
|
83
|
+
throw new Error('Expected a pooled PlanetScale Postgres role URL.');
|
|
84
|
+
}
|
|
85
|
+
return match[1];
|
|
86
|
+
}
|
|
87
|
+
|
|
72
88
|
/**
|
|
73
89
|
* Canonical env var for the workflow runtime control-plane Postgres
|
|
74
90
|
* (scheduler queue, leases, receipts, outbox). Hard-cutover runtime wiring
|