deepline 0.1.312 → 0.1.314
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/play.ts +5 -9
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +224 -34
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +38 -81
- package/dist/bundling-sources/shared_libs/play-runtime/sandbox-runtime-limits.ts +68 -61
- package/dist/bundling-sources/shared_libs/plays/bundling/index.ts +46 -18
- package/dist/cli/index.js +58 -44
- package/dist/cli/index.mjs +58 -44
- package/dist/index.d.mts +5 -9
- package/dist/index.d.ts +5 -9
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/plays/bundle-play-file.d.mts +2 -3
- package/dist/plays/bundle-play-file.d.ts +2 -3
- package/dist/plays/bundle-play-file.mjs +37 -16
- package/package.json +1 -1
|
@@ -198,16 +198,12 @@ export type PlayBindings = {
|
|
|
198
198
|
/** Stop the run before a billed action would push total run credits above this cap. */
|
|
199
199
|
maxCreditsPerRun?: number;
|
|
200
200
|
};
|
|
201
|
-
/** Requested sandbox
|
|
201
|
+
/** Requested prebuilt sandbox and runtime deadline. */
|
|
202
202
|
runtime?: {
|
|
203
203
|
/** Duration such as `"90m"` or `"2h"`. */
|
|
204
204
|
timeout?: string;
|
|
205
|
-
/**
|
|
206
|
-
|
|
207
|
-
/** Whole vCPU count. */
|
|
208
|
-
cpu?: number;
|
|
209
|
-
/** Disk such as `"10GiB"`. */
|
|
210
|
-
disk?: string;
|
|
205
|
+
/** Deepline-managed prebuilt sandbox size. */
|
|
206
|
+
size?: 'standard';
|
|
211
207
|
};
|
|
212
208
|
/** Webhook trigger with optional HMAC signature verification. */
|
|
213
209
|
webhook?: {
|
|
@@ -1204,7 +1200,7 @@ export type DefinePlayConfig<TInput, TOutput extends PlayReturnObject> = {
|
|
|
1204
1200
|
bindings?: PlayBindings;
|
|
1205
1201
|
/** Billing options. */
|
|
1206
1202
|
billing?: PlayBindings['billing'];
|
|
1207
|
-
/** Requested sandbox
|
|
1203
|
+
/** Requested prebuilt sandbox and runtime deadline. */
|
|
1208
1204
|
runtime?: PlayBindings['runtime'];
|
|
1209
1205
|
/** Runtime compatibility override. Omit for the current typed contract. */
|
|
1210
1206
|
compatibility?: PlayBindings['compatibility'];
|
|
@@ -1367,7 +1363,7 @@ export type DefinedPlay<TInput, TOutput extends PlayReturnObject> = ((
|
|
|
1367
1363
|
DeeplineNamedPlay<TInput, TOutput> & {
|
|
1368
1364
|
/** Optional trigger bindings (cron, webhook). */
|
|
1369
1365
|
readonly bindings?: PlayBindings;
|
|
1370
|
-
/** Requested sandbox
|
|
1366
|
+
/** Requested prebuilt sandbox and runtime deadline. */
|
|
1371
1367
|
readonly runtime?: PlayBindings['runtime'];
|
|
1372
1368
|
/** Runtime compatibility explicitly selected by the author. */
|
|
1373
1369
|
readonly compatibility?: PlayBindings['compatibility'];
|
|
@@ -157,7 +157,7 @@ export const SDK_RELEASE = {
|
|
|
157
157
|
// 0.1.253 makes play-page browser opening opt-in and retires --no-open.
|
|
158
158
|
// 0.1.254 removes the internal operations tree from the published SDK CLI.
|
|
159
159
|
// Operators use the checkout-local deepline-admin binary instead.
|
|
160
|
-
version: '0.1.
|
|
160
|
+
version: '0.1.314',
|
|
161
161
|
contracts: {
|
|
162
162
|
api: {
|
|
163
163
|
name: 'sdk-http-api',
|
|
@@ -1291,6 +1291,31 @@ function createPacingResolver(
|
|
|
1291
1291
|
export class PlayContextImpl {
|
|
1292
1292
|
private rowStates = new Map<number, RowState>();
|
|
1293
1293
|
private toolCallQueue: ToolCallRequest[] = [];
|
|
1294
|
+
private pendingRuntimeToolOwnershipAssertions: Array<{
|
|
1295
|
+
targets: Array<{ receiptKey: string; leaseId: string }>;
|
|
1296
|
+
resolve: () => void;
|
|
1297
|
+
reject: (error: unknown) => void;
|
|
1298
|
+
}> = [];
|
|
1299
|
+
private runtimeToolOwnershipAssertionFlushScheduled = false;
|
|
1300
|
+
/**
|
|
1301
|
+
* Direct durable boundaries (including ctx.fetch) arrive independently from
|
|
1302
|
+
* concurrent map rows. Hold one microtask's worth so they can use the bulk
|
|
1303
|
+
* receipt gateway instead of serial remote claim requests.
|
|
1304
|
+
*/
|
|
1305
|
+
private pendingRuntimeReceiptClaims = new Map<
|
|
1306
|
+
string,
|
|
1307
|
+
{
|
|
1308
|
+
scheduled: boolean;
|
|
1309
|
+
requests: Array<{
|
|
1310
|
+
key: string;
|
|
1311
|
+
reclaimRunning: boolean;
|
|
1312
|
+
forceRefresh: boolean;
|
|
1313
|
+
forceFailedRefresh: boolean;
|
|
1314
|
+
resolve: (receipt: RuntimeStepReceipt | null) => void;
|
|
1315
|
+
reject: (error: unknown) => void;
|
|
1316
|
+
}>;
|
|
1317
|
+
}
|
|
1318
|
+
>();
|
|
1294
1319
|
/**
|
|
1295
1320
|
* Fixed, non-resetting coalescing deadlines for ready scheduling lanes. A
|
|
1296
1321
|
* deadline starts when the first request enters an empty batch bucket. New
|
|
@@ -2002,6 +2027,14 @@ export class PlayContextImpl {
|
|
|
2002
2027
|
forceRefresh = false,
|
|
2003
2028
|
forceFailedRefresh = false,
|
|
2004
2029
|
): Promise<RuntimeStepReceipt | null> {
|
|
2030
|
+
if (this.#options.claimRuntimeStepReceipts) {
|
|
2031
|
+
return await this.enqueueRuntimeStepReceiptClaim({
|
|
2032
|
+
key,
|
|
2033
|
+
reclaimRunning,
|
|
2034
|
+
forceRefresh,
|
|
2035
|
+
forceFailedRefresh,
|
|
2036
|
+
});
|
|
2037
|
+
}
|
|
2005
2038
|
if (!this.#options.claimRuntimeStepReceipt) {
|
|
2006
2039
|
return null;
|
|
2007
2040
|
}
|
|
@@ -2025,6 +2058,73 @@ export class PlayContextImpl {
|
|
|
2025
2058
|
};
|
|
2026
2059
|
}
|
|
2027
2060
|
|
|
2061
|
+
private async enqueueRuntimeStepReceiptClaim(input: {
|
|
2062
|
+
key: string;
|
|
2063
|
+
reclaimRunning: boolean;
|
|
2064
|
+
forceRefresh: boolean;
|
|
2065
|
+
forceFailedRefresh: boolean;
|
|
2066
|
+
}): Promise<RuntimeStepReceipt | null> {
|
|
2067
|
+
const batchKey = [
|
|
2068
|
+
input.reclaimRunning ? 'reclaim' : 'ordinary',
|
|
2069
|
+
input.forceRefresh ? 'force' : 'cached',
|
|
2070
|
+
input.forceFailedRefresh ? 'failed-force' : 'failed-cached',
|
|
2071
|
+
].join(':');
|
|
2072
|
+
let batch = this.pendingRuntimeReceiptClaims.get(batchKey);
|
|
2073
|
+
if (!batch) {
|
|
2074
|
+
batch = { scheduled: false, requests: [] };
|
|
2075
|
+
this.pendingRuntimeReceiptClaims.set(batchKey, batch);
|
|
2076
|
+
}
|
|
2077
|
+
return await new Promise<RuntimeStepReceipt | null>((resolve, reject) => {
|
|
2078
|
+
batch!.requests.push({ ...input, resolve, reject });
|
|
2079
|
+
if (batch!.scheduled) return;
|
|
2080
|
+
batch!.scheduled = true;
|
|
2081
|
+
queueMicrotask(
|
|
2082
|
+
() => void this.flushRuntimeStepReceiptClaimBatch(batchKey),
|
|
2083
|
+
);
|
|
2084
|
+
});
|
|
2085
|
+
}
|
|
2086
|
+
|
|
2087
|
+
private async flushRuntimeStepReceiptClaimBatch(
|
|
2088
|
+
batchKey: string,
|
|
2089
|
+
): Promise<void> {
|
|
2090
|
+
const batch = this.pendingRuntimeReceiptClaims.get(batchKey);
|
|
2091
|
+
if (!batch) return;
|
|
2092
|
+
this.pendingRuntimeReceiptClaims.delete(batchKey);
|
|
2093
|
+
const requests = batch.requests;
|
|
2094
|
+
const first = requests[0];
|
|
2095
|
+
const claimReceipts = this.#options.claimRuntimeStepReceipts;
|
|
2096
|
+
if (!first || !claimReceipts) {
|
|
2097
|
+
for (const request of requests) request.resolve(null);
|
|
2098
|
+
return;
|
|
2099
|
+
}
|
|
2100
|
+
try {
|
|
2101
|
+
const receipts = await this.dispatchChunkedRuntimeReceiptRequest(
|
|
2102
|
+
requests,
|
|
2103
|
+
(chunk) => {
|
|
2104
|
+
const leaseId = `receipt-lease:${crypto.randomUUID()}`;
|
|
2105
|
+
return claimReceipts({
|
|
2106
|
+
keys: chunk.map((request) => request.key),
|
|
2107
|
+
leaseIds: chunk.map(() => leaseId),
|
|
2108
|
+
runId: this.currentReceiptOwnerRunId,
|
|
2109
|
+
runAttempt: this.currentRunAttempt,
|
|
2110
|
+
leaseAware: true,
|
|
2111
|
+
...(first.reclaimRunning ? { reclaimRunning: true } : {}),
|
|
2112
|
+
...(first.forceRefresh ? { forceRefresh: true } : {}),
|
|
2113
|
+
...(first.forceFailedRefresh ? { forceFailedRefresh: true } : {}),
|
|
2114
|
+
});
|
|
2115
|
+
},
|
|
2116
|
+
);
|
|
2117
|
+
for (let index = 0; index < requests.length; index += 1) {
|
|
2118
|
+
const request = requests[index]!;
|
|
2119
|
+
request.resolve(
|
|
2120
|
+
this.normalizeRuntimeStepReceipt(request.key, receipts[index]),
|
|
2121
|
+
);
|
|
2122
|
+
}
|
|
2123
|
+
} catch (error) {
|
|
2124
|
+
for (const request of requests) request.reject(error);
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
|
|
2028
2128
|
private async completeRuntimeStepReceipt(
|
|
2029
2129
|
key: string,
|
|
2030
2130
|
_runId: string,
|
|
@@ -2111,59 +2211,148 @@ export class PlayContextImpl {
|
|
|
2111
2211
|
private async assertRuntimeToolReceiptOwnership(
|
|
2112
2212
|
requests: ToolCallRequest[],
|
|
2113
2213
|
): Promise<void> {
|
|
2114
|
-
const targets =
|
|
2214
|
+
const targets = this.runtimeToolReceiptOwnershipTargets(requests);
|
|
2215
|
+
if (targets.length === 0) return;
|
|
2216
|
+
|
|
2217
|
+
return await new Promise<void>((resolve, reject) => {
|
|
2218
|
+
this.pendingRuntimeToolOwnershipAssertions.push({
|
|
2219
|
+
targets,
|
|
2220
|
+
resolve,
|
|
2221
|
+
reject,
|
|
2222
|
+
});
|
|
2223
|
+
if (this.runtimeToolOwnershipAssertionFlushScheduled) return;
|
|
2224
|
+
this.runtimeToolOwnershipAssertionFlushScheduled = true;
|
|
2225
|
+
queueMicrotask(() => void this.flushRuntimeToolOwnershipAssertionBatch());
|
|
2226
|
+
});
|
|
2227
|
+
}
|
|
2228
|
+
|
|
2229
|
+
private runtimeToolReceiptOwnershipTargets(
|
|
2230
|
+
requests: ToolCallRequest[],
|
|
2231
|
+
): Array<{ receiptKey: string; leaseId: string }> {
|
|
2232
|
+
return requests.flatMap((request) => {
|
|
2115
2233
|
const receiptKey = request.receiptKey?.trim() || null;
|
|
2116
2234
|
if (!receiptKey) return [];
|
|
2117
2235
|
const leaseId = request.receiptLeaseId?.trim() || null;
|
|
2118
2236
|
if (!leaseId) return [];
|
|
2119
2237
|
return [{ receiptKey, leaseId }];
|
|
2120
2238
|
});
|
|
2121
|
-
|
|
2239
|
+
}
|
|
2122
2240
|
|
|
2241
|
+
private async renewRuntimeToolReceiptOwnership(
|
|
2242
|
+
requests: ToolCallRequest[],
|
|
2243
|
+
): Promise<void> {
|
|
2244
|
+
const targets = this.runtimeToolReceiptOwnershipTargets(requests);
|
|
2245
|
+
if (targets.length === 0) return;
|
|
2246
|
+
if (!this.#options.heartbeatRuntimeStepReceipts) {
|
|
2247
|
+
return await this.assertRuntimeToolReceiptOwnership(requests);
|
|
2248
|
+
}
|
|
2123
2249
|
const byLeaseId = new Map<string, string[]>();
|
|
2124
2250
|
for (const target of targets) {
|
|
2125
2251
|
const keys = byLeaseId.get(target.leaseId) ?? [];
|
|
2126
2252
|
keys.push(target.receiptKey);
|
|
2127
2253
|
byLeaseId.set(target.leaseId, keys);
|
|
2128
2254
|
}
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
const receipts = await this.#options.heartbeatRuntimeStepReceipts({
|
|
2255
|
+
await Promise.all(
|
|
2256
|
+
[...byLeaseId].map(async ([leaseId, keys]) => {
|
|
2257
|
+
const receipts = await this.#options.heartbeatRuntimeStepReceipts!({
|
|
2133
2258
|
runId: this.currentReceiptOwnerRunId,
|
|
2134
2259
|
runAttempt: this.currentRunAttempt,
|
|
2135
2260
|
leaseId,
|
|
2136
2261
|
keys,
|
|
2137
2262
|
});
|
|
2138
2263
|
this.assertRuntimeToolReceiptHeartbeatResult(keys, leaseId, receipts);
|
|
2264
|
+
}),
|
|
2265
|
+
);
|
|
2266
|
+
}
|
|
2267
|
+
|
|
2268
|
+
private async flushRuntimeToolOwnershipAssertionBatch(): Promise<void> {
|
|
2269
|
+
this.runtimeToolOwnershipAssertionFlushScheduled = false;
|
|
2270
|
+
const assertions = this.pendingRuntimeToolOwnershipAssertions.splice(0);
|
|
2271
|
+
if (assertions.length === 0) return;
|
|
2272
|
+
const targets = assertions.flatMap((assertion) => assertion.targets);
|
|
2273
|
+
|
|
2274
|
+
if (this.#options.heartbeatRuntimeStepReceipts) {
|
|
2275
|
+
try {
|
|
2276
|
+
const byLeaseId = new Map<string, string[]>();
|
|
2277
|
+
for (const target of targets) {
|
|
2278
|
+
const keys = byLeaseId.get(target.leaseId) ?? [];
|
|
2279
|
+
keys.push(target.receiptKey);
|
|
2280
|
+
byLeaseId.set(target.leaseId, keys);
|
|
2281
|
+
}
|
|
2282
|
+
const renewed = new Map<string, RuntimeStepReceipt | null>();
|
|
2283
|
+
await Promise.all(
|
|
2284
|
+
[...byLeaseId].map(async ([leaseId, keys]) => {
|
|
2285
|
+
const receipts = await this.#options.heartbeatRuntimeStepReceipts!({
|
|
2286
|
+
runId: this.currentReceiptOwnerRunId,
|
|
2287
|
+
runAttempt: this.currentRunAttempt,
|
|
2288
|
+
leaseId,
|
|
2289
|
+
keys,
|
|
2290
|
+
});
|
|
2291
|
+
for (let index = 0; index < keys.length; index += 1) {
|
|
2292
|
+
renewed.set(keys[index]!, receipts[index] ?? null);
|
|
2293
|
+
}
|
|
2294
|
+
}),
|
|
2295
|
+
);
|
|
2296
|
+
for (const assertion of assertions) {
|
|
2297
|
+
const lost = assertion.targets.find((target) => {
|
|
2298
|
+
const receipt = renewed.get(target.receiptKey);
|
|
2299
|
+
return !this.runtimeToolReceiptStillOwned(receipt, target.leaseId);
|
|
2300
|
+
});
|
|
2301
|
+
if (lost) {
|
|
2302
|
+
assertion.reject(
|
|
2303
|
+
new RuntimeReceiptLeaseLostError({
|
|
2304
|
+
receiptKey: lost.receiptKey,
|
|
2305
|
+
runId: this.currentReceiptOwnerRunId,
|
|
2306
|
+
leaseId: lost.leaseId,
|
|
2307
|
+
}),
|
|
2308
|
+
);
|
|
2309
|
+
} else {
|
|
2310
|
+
assertion.resolve();
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2313
|
+
} catch (error) {
|
|
2314
|
+
for (const assertion of assertions) assertion.reject(error);
|
|
2139
2315
|
}
|
|
2140
2316
|
return;
|
|
2141
2317
|
}
|
|
2142
2318
|
|
|
2143
2319
|
if (
|
|
2144
|
-
|
|
2145
|
-
|
|
2320
|
+
this.#options.getRuntimeStepReceipt ||
|
|
2321
|
+
this.#options.getRuntimeStepReceipts
|
|
2146
2322
|
) {
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2323
|
+
try {
|
|
2324
|
+
const latest = await this.getRuntimeStepReceipts(
|
|
2325
|
+
targets.map((target) => target.receiptKey),
|
|
2326
|
+
);
|
|
2327
|
+
for (const assertion of assertions) {
|
|
2328
|
+
const lost = assertion.targets.find((target) => {
|
|
2329
|
+
const receipt = latest.get(target.receiptKey);
|
|
2330
|
+
return !this.runtimeToolReceiptStillOwned(receipt, target.leaseId);
|
|
2331
|
+
});
|
|
2332
|
+
if (lost) {
|
|
2333
|
+
assertion.reject(
|
|
2334
|
+
new RuntimeReceiptLeaseLostError({
|
|
2335
|
+
receiptKey: lost.receiptKey,
|
|
2336
|
+
runId: this.currentReceiptOwnerRunId,
|
|
2337
|
+
leaseId: lost.leaseId,
|
|
2338
|
+
}),
|
|
2339
|
+
);
|
|
2340
|
+
} else {
|
|
2341
|
+
assertion.resolve();
|
|
2342
|
+
}
|
|
2343
|
+
}
|
|
2344
|
+
} catch (error) {
|
|
2345
|
+
for (const assertion of assertions) assertion.reject(error);
|
|
2165
2346
|
}
|
|
2347
|
+
return;
|
|
2166
2348
|
}
|
|
2349
|
+
|
|
2350
|
+
const error = new RuntimeReceiptLeaseLostError({
|
|
2351
|
+
receiptKey: targets[0]?.receiptKey ?? 'unknown',
|
|
2352
|
+
runId: this.currentReceiptOwnerRunId,
|
|
2353
|
+
leaseId: targets[0]?.leaseId ?? 'unknown',
|
|
2354
|
+
});
|
|
2355
|
+
for (const assertion of assertions) assertion.reject(error);
|
|
2167
2356
|
}
|
|
2168
2357
|
|
|
2169
2358
|
private assertRuntimeToolReceiptHeartbeatResult(
|
|
@@ -2366,22 +2555,23 @@ export class PlayContextImpl {
|
|
|
2366
2555
|
if (uniqueKeys.length === 0) return new Map();
|
|
2367
2556
|
const claimReceipts = this.#options.claimRuntimeStepReceipts;
|
|
2368
2557
|
const receipts = claimReceipts
|
|
2369
|
-
? await this.dispatchChunkedRuntimeReceiptRequest(uniqueKeys, (chunk) =>
|
|
2370
|
-
|
|
2558
|
+
? await this.dispatchChunkedRuntimeReceiptRequest(uniqueKeys, (chunk) => {
|
|
2559
|
+
const leaseId = `receipt-lease:${crypto.randomUUID()}`;
|
|
2560
|
+
return claimReceipts({
|
|
2371
2561
|
keys: chunk,
|
|
2372
2562
|
// A transport retry can replay this mutation after the store
|
|
2373
2563
|
// committed but before the response body was consumed. Stable
|
|
2374
2564
|
// caller-owned tokens distinguish that replay from a concurrent
|
|
2375
2565
|
// claimant without weakening the provider-call execution fence.
|
|
2376
|
-
leaseIds: chunk.map(() =>
|
|
2566
|
+
leaseIds: chunk.map(() => leaseId),
|
|
2377
2567
|
runId: this.currentReceiptOwnerRunId,
|
|
2378
2568
|
runAttempt: this.currentRunAttempt,
|
|
2379
2569
|
leaseAware: true,
|
|
2380
2570
|
...(reclaimRunning ? { reclaimRunning: true } : {}),
|
|
2381
2571
|
...(forceRefresh ? { forceRefresh: true } : {}),
|
|
2382
2572
|
...(forceFailedRefresh ? { forceFailedRefresh: true } : {}),
|
|
2383
|
-
})
|
|
2384
|
-
)
|
|
2573
|
+
});
|
|
2574
|
+
})
|
|
2385
2575
|
: await Promise.all(
|
|
2386
2576
|
uniqueKeys.map((key) =>
|
|
2387
2577
|
this.claimRuntimeStepReceipt(
|
|
@@ -8194,7 +8384,7 @@ export class PlayContextImpl {
|
|
|
8194
8384
|
executionAuthScopeDigest: owner.executionAuthScopeDigest,
|
|
8195
8385
|
receiptLeaseExpiresAt: owner.receiptLeaseExpiresAt,
|
|
8196
8386
|
heartbeatReceipt: () =>
|
|
8197
|
-
this.
|
|
8387
|
+
this.renewRuntimeToolReceiptOwnership([owner]),
|
|
8198
8388
|
providerIdempotencyKey:
|
|
8199
8389
|
this.providerIdempotencyKeyForToolCall({
|
|
8200
8390
|
cacheKey: owner.cacheKey,
|
|
@@ -8471,7 +8661,7 @@ export class PlayContextImpl {
|
|
|
8471
8661
|
batch.memberRequests,
|
|
8472
8662
|
),
|
|
8473
8663
|
heartbeatReceipt: () =>
|
|
8474
|
-
this.
|
|
8664
|
+
this.renewRuntimeToolReceiptOwnership(
|
|
8475
8665
|
batch.memberRequests,
|
|
8476
8666
|
),
|
|
8477
8667
|
},
|
|
@@ -8634,7 +8824,7 @@ export class PlayContextImpl {
|
|
|
8634
8824
|
}),
|
|
8635
8825
|
receiptLeaseExpiresAt: request.receiptLeaseExpiresAt,
|
|
8636
8826
|
heartbeatReceipt: () =>
|
|
8637
|
-
this.
|
|
8827
|
+
this.renewRuntimeToolReceiptOwnership([request]),
|
|
8638
8828
|
}
|
|
8639
8829
|
: {}),
|
|
8640
8830
|
timeoutMs: resolveRuntimeTimeoutMsForClaimedOwners([
|
package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Daytona } from '@daytonaio/sdk';
|
|
2
2
|
import type {
|
|
3
3
|
PlayRunnerExecutionConfig,
|
|
4
4
|
PlayRunnerResult,
|
|
5
5
|
} from '@shared_libs/play-runtime/protocol';
|
|
6
6
|
import { isIsolatedRuntimeSchedulerSchema } from '@shared_libs/play-runtime/runtime-scheduler-topology';
|
|
7
7
|
import { PLAY_RUNNER_TIMEOUT_SECONDS } from '@shared_libs/play-runtime/runtime-constants';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS,
|
|
10
|
+
validatePlaySandboxRuntimeLimits,
|
|
11
|
+
} from '@shared_libs/play-runtime/sandbox-runtime-limits';
|
|
9
12
|
|
|
10
13
|
const DAYTONA_CREATE_TIMEOUT_SECONDS = 10;
|
|
11
14
|
const DAYTONA_CREATE_RETRY_DELAYS_MS = [0, 500, 1_500] as const;
|
|
@@ -14,11 +17,13 @@ const DAYTONA_CREATE_RETRY_DELAYS_MS = [0, 500, 1_500] as const;
|
|
|
14
17
|
// setup time cannot consume the terminal-flush grace.
|
|
15
18
|
const DAYTONA_AUTO_ARCHIVE_INTERVAL_MINUTES = 15;
|
|
16
19
|
const DAYTONA_SANDBOX_LABEL_SOURCE = 'deepline-play-runner';
|
|
17
|
-
// Daytona's default
|
|
18
|
-
// during acquisition, before customer code
|
|
19
|
-
export const DAYTONA_SANDBOX_CPU =
|
|
20
|
-
export const DAYTONA_SANDBOX_MEMORY_GIB =
|
|
21
|
-
|
|
20
|
+
// Daytona's Deepline-managed default snapshot is the only admitted prebuilt.
|
|
21
|
+
// Resources are verified during acquisition, before customer code or billing.
|
|
22
|
+
export const DAYTONA_SANDBOX_CPU = STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS.cpu;
|
|
23
|
+
export const DAYTONA_SANDBOX_MEMORY_GIB =
|
|
24
|
+
STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS.memoryGiB;
|
|
25
|
+
export const DAYTONA_SANDBOX_DISK_GIB =
|
|
26
|
+
STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS.diskGiB;
|
|
22
27
|
export const DAYTONA_SANDBOX_GPU = 0;
|
|
23
28
|
const DAYTONA_NETWORK_ALLOW_LIST_ENV = 'DEEPLINE_DAYTONA_NETWORK_ALLOW_LIST';
|
|
24
29
|
|
|
@@ -173,15 +178,10 @@ async function createOneShotDaytonaSandbox(input: {
|
|
|
173
178
|
orgId: string;
|
|
174
179
|
context: DaytonaExecutionContext;
|
|
175
180
|
}): Promise<DaytonaSandbox> {
|
|
176
|
-
const limits =
|
|
177
|
-
input.context.sandboxRuntimeLimits
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
memory: `${input.context.sandboxRuntimeLimits.memoryGiB}GiB`,
|
|
181
|
-
cpu: input.context.sandboxRuntimeLimits.cpu,
|
|
182
|
-
disk: `${input.context.sandboxRuntimeLimits.diskGiB}GiB`,
|
|
183
|
-
}
|
|
184
|
-
: null,
|
|
181
|
+
const limits = validatePlaySandboxRuntimeLimits(
|
|
182
|
+
input.context.sandboxRuntimeLimits ?? {
|
|
183
|
+
...STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS,
|
|
184
|
+
},
|
|
185
185
|
);
|
|
186
186
|
const orgId = normalizeLabelValue(input.orgId);
|
|
187
187
|
const workflowId = normalizeLabelValue(input.context.workflowId);
|
|
@@ -204,26 +204,24 @@ async function createOneShotDaytonaSandbox(input: {
|
|
|
204
204
|
runtimeSchedulerSchema: input.context.runtimeSchedulerSchema ?? null,
|
|
205
205
|
});
|
|
206
206
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
{ timeout: DAYTONA_CREATE_TIMEOUT_SECONDS },
|
|
226
|
-
);
|
|
207
|
+
const commonParams = {
|
|
208
|
+
labels,
|
|
209
|
+
ephemeral: true,
|
|
210
|
+
autoStopInterval: Math.ceil(
|
|
211
|
+
(limits.timeoutSeconds + PLAY_RUNNER_TIMEOUT_SECONDS - 30 * 60) / 60,
|
|
212
|
+
),
|
|
213
|
+
autoArchiveInterval: DAYTONA_AUTO_ARCHIVE_INTERVAL_MINUTES,
|
|
214
|
+
// A non-empty `networkAllowList` IS the "block all egress except these
|
|
215
|
+
// CIDRs" control; Daytona rejects create when `networkBlockAll: true` is
|
|
216
|
+
// combined with a non-empty allow-list ("networkBlockAll: true cannot be
|
|
217
|
+
// combined with a non-empty networkAllowList or domainAllowList"). Pass
|
|
218
|
+
// the allow-list alone so the egress restriction holds without the
|
|
219
|
+
// contradictory flag.
|
|
220
|
+
...(networkAllowList ? { networkAllowList } : {}),
|
|
221
|
+
};
|
|
222
|
+
return input.daytona.create(commonParams, {
|
|
223
|
+
timeout: DAYTONA_CREATE_TIMEOUT_SECONDS,
|
|
224
|
+
});
|
|
227
225
|
}
|
|
228
226
|
|
|
229
227
|
async function createRetriedOneShotDaytonaSandbox(input: {
|
|
@@ -286,15 +284,10 @@ async function acquireOneShotDaytonaSandbox(input: {
|
|
|
286
284
|
emitStage: DaytonaStageEmitter;
|
|
287
285
|
startedAt: number;
|
|
288
286
|
}): Promise<AcquiredDaytonaSandbox> {
|
|
289
|
-
const limits =
|
|
290
|
-
input.context.sandboxRuntimeLimits
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
memory: `${input.context.sandboxRuntimeLimits.memoryGiB}GiB`,
|
|
294
|
-
cpu: input.context.sandboxRuntimeLimits.cpu,
|
|
295
|
-
disk: `${input.context.sandboxRuntimeLimits.diskGiB}GiB`,
|
|
296
|
-
}
|
|
297
|
-
: null,
|
|
287
|
+
const limits = validatePlaySandboxRuntimeLimits(
|
|
288
|
+
input.context.sandboxRuntimeLimits ?? {
|
|
289
|
+
...STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS,
|
|
290
|
+
},
|
|
298
291
|
);
|
|
299
292
|
input.emitStage('create:start');
|
|
300
293
|
const result = await createRetriedOneShotDaytonaSandbox(input);
|
|
@@ -304,42 +297,6 @@ async function acquireOneShotDaytonaSandbox(input: {
|
|
|
304
297
|
diskGiB: result.sandbox.disk,
|
|
305
298
|
gpu: result.sandbox.gpu ?? 0,
|
|
306
299
|
};
|
|
307
|
-
// Daytona's SDK only accepts resources when a custom image is supplied.
|
|
308
|
-
// We deliberately retain the provider's fast default image, then resize
|
|
309
|
-
// before any customer code or billing window begins.
|
|
310
|
-
if (
|
|
311
|
-
typeof result.sandbox.resize === 'function' &&
|
|
312
|
-
(granted.cpu !== limits.cpu ||
|
|
313
|
-
granted.memoryGiB !== limits.memoryGiB ||
|
|
314
|
-
granted.diskGiB !== limits.diskGiB)
|
|
315
|
-
) {
|
|
316
|
-
try {
|
|
317
|
-
if (granted.diskGiB !== limits.diskGiB) {
|
|
318
|
-
await result.sandbox.stop(60);
|
|
319
|
-
await result.sandbox.resize({
|
|
320
|
-
cpu: limits.cpu,
|
|
321
|
-
memory: limits.memoryGiB,
|
|
322
|
-
disk: limits.diskGiB,
|
|
323
|
-
});
|
|
324
|
-
await result.sandbox.start(60);
|
|
325
|
-
} else {
|
|
326
|
-
await result.sandbox.resize({
|
|
327
|
-
cpu: limits.cpu,
|
|
328
|
-
memory: limits.memoryGiB,
|
|
329
|
-
disk: limits.diskGiB,
|
|
330
|
-
});
|
|
331
|
-
}
|
|
332
|
-
} catch (error) {
|
|
333
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
334
|
-
return await rejectAcquiredSandbox(
|
|
335
|
-
result.sandbox,
|
|
336
|
-
`Daytona sandbox resize failed before execution: ${message}.`,
|
|
337
|
-
);
|
|
338
|
-
}
|
|
339
|
-
granted.cpu = result.sandbox.cpu;
|
|
340
|
-
granted.memoryGiB = result.sandbox.memory;
|
|
341
|
-
granted.diskGiB = result.sandbox.disk;
|
|
342
|
-
}
|
|
343
300
|
if (
|
|
344
301
|
granted.cpu !== limits.cpu ||
|
|
345
302
|
granted.memoryGiB !== limits.memoryGiB ||
|