deepline 0.1.313 → 0.1.315
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 +136 -38
- package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts +11 -11
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +20 -93
- 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.315',
|
|
161
161
|
contracts: {
|
|
162
162
|
api: {
|
|
163
163
|
name: 'sdk-http-api',
|
|
@@ -1291,6 +1291,12 @@ 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;
|
|
1294
1300
|
/**
|
|
1295
1301
|
* Direct durable boundaries (including ctx.fetch) arrive independently from
|
|
1296
1302
|
* concurrent map rows. Hold one microtask's worth so they can use the bulk
|
|
@@ -2094,17 +2100,19 @@ export class PlayContextImpl {
|
|
|
2094
2100
|
try {
|
|
2095
2101
|
const receipts = await this.dispatchChunkedRuntimeReceiptRequest(
|
|
2096
2102
|
requests,
|
|
2097
|
-
(chunk) =>
|
|
2098
|
-
|
|
2103
|
+
(chunk) => {
|
|
2104
|
+
const leaseId = `receipt-lease:${crypto.randomUUID()}`;
|
|
2105
|
+
return claimReceipts({
|
|
2099
2106
|
keys: chunk.map((request) => request.key),
|
|
2100
|
-
leaseIds: chunk.map(() =>
|
|
2107
|
+
leaseIds: chunk.map(() => leaseId),
|
|
2101
2108
|
runId: this.currentReceiptOwnerRunId,
|
|
2102
2109
|
runAttempt: this.currentRunAttempt,
|
|
2103
2110
|
leaseAware: true,
|
|
2104
2111
|
...(first.reclaimRunning ? { reclaimRunning: true } : {}),
|
|
2105
2112
|
...(first.forceRefresh ? { forceRefresh: true } : {}),
|
|
2106
2113
|
...(first.forceFailedRefresh ? { forceFailedRefresh: true } : {}),
|
|
2107
|
-
})
|
|
2114
|
+
});
|
|
2115
|
+
},
|
|
2108
2116
|
);
|
|
2109
2117
|
for (let index = 0; index < requests.length; index += 1) {
|
|
2110
2118
|
const request = requests[index]!;
|
|
@@ -2203,59 +2211,148 @@ export class PlayContextImpl {
|
|
|
2203
2211
|
private async assertRuntimeToolReceiptOwnership(
|
|
2204
2212
|
requests: ToolCallRequest[],
|
|
2205
2213
|
): Promise<void> {
|
|
2206
|
-
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) => {
|
|
2207
2233
|
const receiptKey = request.receiptKey?.trim() || null;
|
|
2208
2234
|
if (!receiptKey) return [];
|
|
2209
2235
|
const leaseId = request.receiptLeaseId?.trim() || null;
|
|
2210
2236
|
if (!leaseId) return [];
|
|
2211
2237
|
return [{ receiptKey, leaseId }];
|
|
2212
2238
|
});
|
|
2213
|
-
|
|
2239
|
+
}
|
|
2214
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
|
+
}
|
|
2215
2249
|
const byLeaseId = new Map<string, string[]>();
|
|
2216
2250
|
for (const target of targets) {
|
|
2217
2251
|
const keys = byLeaseId.get(target.leaseId) ?? [];
|
|
2218
2252
|
keys.push(target.receiptKey);
|
|
2219
2253
|
byLeaseId.set(target.leaseId, keys);
|
|
2220
2254
|
}
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
const receipts = await this.#options.heartbeatRuntimeStepReceipts({
|
|
2255
|
+
await Promise.all(
|
|
2256
|
+
[...byLeaseId].map(async ([leaseId, keys]) => {
|
|
2257
|
+
const receipts = await this.#options.heartbeatRuntimeStepReceipts!({
|
|
2225
2258
|
runId: this.currentReceiptOwnerRunId,
|
|
2226
2259
|
runAttempt: this.currentRunAttempt,
|
|
2227
2260
|
leaseId,
|
|
2228
2261
|
keys,
|
|
2229
2262
|
});
|
|
2230
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);
|
|
2231
2315
|
}
|
|
2232
2316
|
return;
|
|
2233
2317
|
}
|
|
2234
2318
|
|
|
2235
2319
|
if (
|
|
2236
|
-
|
|
2237
|
-
|
|
2320
|
+
this.#options.getRuntimeStepReceipt ||
|
|
2321
|
+
this.#options.getRuntimeStepReceipts
|
|
2238
2322
|
) {
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
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);
|
|
2257
2346
|
}
|
|
2347
|
+
return;
|
|
2258
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);
|
|
2259
2356
|
}
|
|
2260
2357
|
|
|
2261
2358
|
private assertRuntimeToolReceiptHeartbeatResult(
|
|
@@ -2458,22 +2555,23 @@ export class PlayContextImpl {
|
|
|
2458
2555
|
if (uniqueKeys.length === 0) return new Map();
|
|
2459
2556
|
const claimReceipts = this.#options.claimRuntimeStepReceipts;
|
|
2460
2557
|
const receipts = claimReceipts
|
|
2461
|
-
? await this.dispatchChunkedRuntimeReceiptRequest(uniqueKeys, (chunk) =>
|
|
2462
|
-
|
|
2558
|
+
? await this.dispatchChunkedRuntimeReceiptRequest(uniqueKeys, (chunk) => {
|
|
2559
|
+
const leaseId = `receipt-lease:${crypto.randomUUID()}`;
|
|
2560
|
+
return claimReceipts({
|
|
2463
2561
|
keys: chunk,
|
|
2464
2562
|
// A transport retry can replay this mutation after the store
|
|
2465
2563
|
// committed but before the response body was consumed. Stable
|
|
2466
2564
|
// caller-owned tokens distinguish that replay from a concurrent
|
|
2467
2565
|
// claimant without weakening the provider-call execution fence.
|
|
2468
|
-
leaseIds: chunk.map(() =>
|
|
2566
|
+
leaseIds: chunk.map(() => leaseId),
|
|
2469
2567
|
runId: this.currentReceiptOwnerRunId,
|
|
2470
2568
|
runAttempt: this.currentRunAttempt,
|
|
2471
2569
|
leaseAware: true,
|
|
2472
2570
|
...(reclaimRunning ? { reclaimRunning: true } : {}),
|
|
2473
2571
|
...(forceRefresh ? { forceRefresh: true } : {}),
|
|
2474
2572
|
...(forceFailedRefresh ? { forceFailedRefresh: true } : {}),
|
|
2475
|
-
})
|
|
2476
|
-
)
|
|
2573
|
+
});
|
|
2574
|
+
})
|
|
2477
2575
|
: await Promise.all(
|
|
2478
2576
|
uniqueKeys.map((key) =>
|
|
2479
2577
|
this.claimRuntimeStepReceipt(
|
|
@@ -8286,7 +8384,7 @@ export class PlayContextImpl {
|
|
|
8286
8384
|
executionAuthScopeDigest: owner.executionAuthScopeDigest,
|
|
8287
8385
|
receiptLeaseExpiresAt: owner.receiptLeaseExpiresAt,
|
|
8288
8386
|
heartbeatReceipt: () =>
|
|
8289
|
-
this.
|
|
8387
|
+
this.renewRuntimeToolReceiptOwnership([owner]),
|
|
8290
8388
|
providerIdempotencyKey:
|
|
8291
8389
|
this.providerIdempotencyKeyForToolCall({
|
|
8292
8390
|
cacheKey: owner.cacheKey,
|
|
@@ -8563,7 +8661,7 @@ export class PlayContextImpl {
|
|
|
8563
8661
|
batch.memberRequests,
|
|
8564
8662
|
),
|
|
8565
8663
|
heartbeatReceipt: () =>
|
|
8566
|
-
this.
|
|
8664
|
+
this.renewRuntimeToolReceiptOwnership(
|
|
8567
8665
|
batch.memberRequests,
|
|
8568
8666
|
),
|
|
8569
8667
|
},
|
|
@@ -8726,7 +8824,7 @@ export class PlayContextImpl {
|
|
|
8726
8824
|
}),
|
|
8727
8825
|
receiptLeaseExpiresAt: request.receiptLeaseExpiresAt,
|
|
8728
8826
|
heartbeatReceipt: () =>
|
|
8729
|
-
this.
|
|
8827
|
+
this.renewRuntimeToolReceiptOwnership([request]),
|
|
8730
8828
|
}
|
|
8731
8829
|
: {}),
|
|
8732
8830
|
timeoutMs: resolveRuntimeTimeoutMsForClaimedOwners([
|
package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts
CHANGED
|
@@ -814,36 +814,36 @@ export class AppRuntimeRateStateBackend implements RateStateBackend {
|
|
|
814
814
|
}
|
|
815
815
|
|
|
816
816
|
/**
|
|
817
|
-
* Multiplier applied to
|
|
818
|
-
* `adaptiveMaxRps` ceiling.
|
|
819
|
-
*
|
|
817
|
+
* Multiplier applied to an adaptive pacing rule's `requestsPerWindow` to derive
|
|
818
|
+
* its `adaptiveMaxRps` ceiling. The configured rate is a safe seed, while 429
|
|
819
|
+
* feedback drives AIMD toward the provider's actual limit.
|
|
820
820
|
*/
|
|
821
|
-
const
|
|
821
|
+
const ADAPTIVE_MAX_RPS_MULTIPLIER = 25;
|
|
822
822
|
|
|
823
823
|
/**
|
|
824
824
|
* A rule id shaped `default:<toolId>` is the undeclared-tool default-pacing rule
|
|
825
|
-
* minted by `defaultPacingForTool` in governor.ts.
|
|
826
|
-
* base rate; declared providers pin at their contractual rate.
|
|
825
|
+
* minted by `defaultPacingForTool` in governor.ts.
|
|
827
826
|
*/
|
|
828
827
|
function isDefaultPacingRuleId(ruleId: string): boolean {
|
|
829
828
|
return ruleId.startsWith('default:');
|
|
830
829
|
}
|
|
831
830
|
|
|
832
831
|
/**
|
|
833
|
-
* Preserve explicit advisory ceilings
|
|
834
|
-
*
|
|
835
|
-
*
|
|
832
|
+
* Preserve explicit advisory ceilings. Otherwise stamp the base * 25 ceiling
|
|
833
|
+
* onto legacy default-pacing rules and declared per-second rules. This lets a
|
|
834
|
+
* declared 10 QPS provider discover a higher entitlement, while longer-window
|
|
835
|
+
* declared budgets remain fixed because multiplying RPM into RPS is unsafe.
|
|
836
836
|
*/
|
|
837
837
|
function withAdaptiveMaxRps(rules: readonly PacingRule[]): PacingRule[] {
|
|
838
838
|
return rules.map((rule) =>
|
|
839
839
|
rule.adaptiveMaxRps != null
|
|
840
840
|
? rule
|
|
841
|
-
: isDefaultPacingRuleId(rule.ruleId)
|
|
841
|
+
: isDefaultPacingRuleId(rule.ruleId) || rule.windowMs === 1_000
|
|
842
842
|
? ({
|
|
843
843
|
...rule,
|
|
844
844
|
adaptiveMaxRps:
|
|
845
845
|
rule.requestsPerWindow *
|
|
846
|
-
|
|
846
|
+
ADAPTIVE_MAX_RPS_MULTIPLIER,
|
|
847
847
|
} as PacingRule)
|
|
848
848
|
: rule,
|
|
849
849
|
);
|
package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts
CHANGED
|
@@ -1,33 +1,29 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Image,
|
|
3
|
-
type CreateSandboxFromImageParams,
|
|
4
|
-
type Daytona,
|
|
5
|
-
} from '@daytonaio/sdk';
|
|
6
|
-
import { DAYTONA_DEFAULT_WORKDIR } from '@shared_libs/play-runtime/daytona-runtime-config';
|
|
1
|
+
import { type Daytona } from '@daytonaio/sdk';
|
|
7
2
|
import type {
|
|
8
3
|
PlayRunnerExecutionConfig,
|
|
9
4
|
PlayRunnerResult,
|
|
10
5
|
} from '@shared_libs/play-runtime/protocol';
|
|
11
6
|
import { isIsolatedRuntimeSchedulerSchema } from '@shared_libs/play-runtime/runtime-scheduler-topology';
|
|
12
7
|
import { PLAY_RUNNER_TIMEOUT_SECONDS } from '@shared_libs/play-runtime/runtime-constants';
|
|
13
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS,
|
|
10
|
+
validatePlaySandboxRuntimeLimits,
|
|
11
|
+
} from '@shared_libs/play-runtime/sandbox-runtime-limits';
|
|
14
12
|
|
|
15
13
|
const DAYTONA_CREATE_TIMEOUT_SECONDS = 10;
|
|
16
|
-
const DAYTONA_CUSTOM_RESOURCE_CREATE_TIMEOUT_SECONDS = 120;
|
|
17
14
|
const DAYTONA_CREATE_RETRY_DELAYS_MS = [0, 500, 1_500] as const;
|
|
18
15
|
// Explicit runner deadline + scheduler GC own the normal lifecycle. Daytona's
|
|
19
16
|
// inactivity stop is a wider crash backstop measured from sandbox creation, so
|
|
20
17
|
// setup time cannot consume the terminal-flush grace.
|
|
21
18
|
const DAYTONA_AUTO_ARCHIVE_INTERVAL_MINUTES = 15;
|
|
22
19
|
const DAYTONA_SANDBOX_LABEL_SOURCE = 'deepline-play-runner';
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
export const
|
|
29
|
-
|
|
30
|
-
export const DAYTONA_SANDBOX_DISK_GIB = 3;
|
|
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;
|
|
31
27
|
export const DAYTONA_SANDBOX_GPU = 0;
|
|
32
28
|
const DAYTONA_NETWORK_ALLOW_LIST_ENV = 'DEEPLINE_DAYTONA_NETWORK_ALLOW_LIST';
|
|
33
29
|
|
|
@@ -182,15 +178,10 @@ async function createOneShotDaytonaSandbox(input: {
|
|
|
182
178
|
orgId: string;
|
|
183
179
|
context: DaytonaExecutionContext;
|
|
184
180
|
}): Promise<DaytonaSandbox> {
|
|
185
|
-
const limits =
|
|
186
|
-
input.context.sandboxRuntimeLimits
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
memory: `${input.context.sandboxRuntimeLimits.memoryGiB}GiB`,
|
|
190
|
-
cpu: input.context.sandboxRuntimeLimits.cpu,
|
|
191
|
-
disk: `${input.context.sandboxRuntimeLimits.diskGiB}GiB`,
|
|
192
|
-
}
|
|
193
|
-
: null,
|
|
181
|
+
const limits = validatePlaySandboxRuntimeLimits(
|
|
182
|
+
input.context.sandboxRuntimeLimits ?? {
|
|
183
|
+
...STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS,
|
|
184
|
+
},
|
|
194
185
|
);
|
|
195
186
|
const orgId = normalizeLabelValue(input.orgId);
|
|
196
187
|
const workflowId = normalizeLabelValue(input.context.workflowId);
|
|
@@ -213,10 +204,6 @@ async function createOneShotDaytonaSandbox(input: {
|
|
|
213
204
|
runtimeSchedulerSchema: input.context.runtimeSchedulerSchema ?? null,
|
|
214
205
|
});
|
|
215
206
|
|
|
216
|
-
const hasCustomResources =
|
|
217
|
-
limits.cpu !== DAYTONA_SANDBOX_CPU ||
|
|
218
|
-
limits.memoryGiB !== DAYTONA_SANDBOX_MEMORY_GIB ||
|
|
219
|
-
limits.diskGiB !== DAYTONA_SANDBOX_DISK_GIB;
|
|
220
207
|
const commonParams = {
|
|
221
208
|
labels,
|
|
222
209
|
ephemeral: true,
|
|
@@ -232,25 +219,6 @@ async function createOneShotDaytonaSandbox(input: {
|
|
|
232
219
|
// contradictory flag.
|
|
233
220
|
...(networkAllowList ? { networkAllowList } : {}),
|
|
234
221
|
};
|
|
235
|
-
if (hasCustomResources) {
|
|
236
|
-
// Daytona fixes resources into snapshots and rejects a resources field on
|
|
237
|
-
// snapshot/default-image creates. Its supported custom-resource path
|
|
238
|
-
// builds from an OCI image and applies CPU, memory, and disk at creation.
|
|
239
|
-
const createParams: CreateSandboxFromImageParams = {
|
|
240
|
-
...commonParams,
|
|
241
|
-
image: DAYTONA_CUSTOM_RESOURCE_IMAGE,
|
|
242
|
-
resources: {
|
|
243
|
-
cpu: limits.cpu,
|
|
244
|
-
memory: limits.memoryGiB,
|
|
245
|
-
disk: limits.diskGiB,
|
|
246
|
-
},
|
|
247
|
-
};
|
|
248
|
-
return input.daytona.create(createParams, {
|
|
249
|
-
timeout: DAYTONA_CUSTOM_RESOURCE_CREATE_TIMEOUT_SECONDS,
|
|
250
|
-
});
|
|
251
|
-
}
|
|
252
|
-
// Keep the fast default snapshot for standard resources, including plays
|
|
253
|
-
// that override timeout only.
|
|
254
222
|
return input.daytona.create(commonParams, {
|
|
255
223
|
timeout: DAYTONA_CREATE_TIMEOUT_SECONDS,
|
|
256
224
|
});
|
|
@@ -316,15 +284,10 @@ async function acquireOneShotDaytonaSandbox(input: {
|
|
|
316
284
|
emitStage: DaytonaStageEmitter;
|
|
317
285
|
startedAt: number;
|
|
318
286
|
}): Promise<AcquiredDaytonaSandbox> {
|
|
319
|
-
const limits =
|
|
320
|
-
input.context.sandboxRuntimeLimits
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
memory: `${input.context.sandboxRuntimeLimits.memoryGiB}GiB`,
|
|
324
|
-
cpu: input.context.sandboxRuntimeLimits.cpu,
|
|
325
|
-
disk: `${input.context.sandboxRuntimeLimits.diskGiB}GiB`,
|
|
326
|
-
}
|
|
327
|
-
: null,
|
|
287
|
+
const limits = validatePlaySandboxRuntimeLimits(
|
|
288
|
+
input.context.sandboxRuntimeLimits ?? {
|
|
289
|
+
...STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS,
|
|
290
|
+
},
|
|
328
291
|
);
|
|
329
292
|
input.emitStage('create:start');
|
|
330
293
|
const result = await createRetriedOneShotDaytonaSandbox(input);
|
|
@@ -334,42 +297,6 @@ async function acquireOneShotDaytonaSandbox(input: {
|
|
|
334
297
|
diskGiB: result.sandbox.disk,
|
|
335
298
|
gpu: result.sandbox.gpu ?? 0,
|
|
336
299
|
};
|
|
337
|
-
// Compatibility fallback for Daytona targets that ignore create-time
|
|
338
|
-
// resources but still support resize. The hosted target should normally
|
|
339
|
-
// match immediately; either path is verified before code or billing starts.
|
|
340
|
-
if (
|
|
341
|
-
typeof result.sandbox.resize === 'function' &&
|
|
342
|
-
(granted.cpu !== limits.cpu ||
|
|
343
|
-
granted.memoryGiB !== limits.memoryGiB ||
|
|
344
|
-
granted.diskGiB !== limits.diskGiB)
|
|
345
|
-
) {
|
|
346
|
-
try {
|
|
347
|
-
if (granted.diskGiB !== limits.diskGiB) {
|
|
348
|
-
await result.sandbox.stop(60);
|
|
349
|
-
await result.sandbox.resize({
|
|
350
|
-
cpu: limits.cpu,
|
|
351
|
-
memory: limits.memoryGiB,
|
|
352
|
-
disk: limits.diskGiB,
|
|
353
|
-
});
|
|
354
|
-
await result.sandbox.start(60);
|
|
355
|
-
} else {
|
|
356
|
-
await result.sandbox.resize({
|
|
357
|
-
cpu: limits.cpu,
|
|
358
|
-
memory: limits.memoryGiB,
|
|
359
|
-
disk: limits.diskGiB,
|
|
360
|
-
});
|
|
361
|
-
}
|
|
362
|
-
} catch (error) {
|
|
363
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
364
|
-
return await rejectAcquiredSandbox(
|
|
365
|
-
result.sandbox,
|
|
366
|
-
`Daytona sandbox resize failed before execution: ${message}.`,
|
|
367
|
-
);
|
|
368
|
-
}
|
|
369
|
-
granted.cpu = result.sandbox.cpu;
|
|
370
|
-
granted.memoryGiB = result.sandbox.memory;
|
|
371
|
-
granted.diskGiB = result.sandbox.disk;
|
|
372
|
-
}
|
|
373
300
|
if (
|
|
374
301
|
granted.cpu !== limits.cpu ||
|
|
375
302
|
granted.memoryGiB !== limits.memoryGiB ||
|