deepline 0.1.313 → 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.
@@ -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 envelope; the server enforces the final allowed limits. */
201
+ /** Requested prebuilt sandbox and runtime deadline. */
202
202
  runtime?: {
203
203
  /** Duration such as `"90m"` or `"2h"`. */
204
204
  timeout?: string;
205
- /** Memory such as `"4GiB"`. */
206
- memory?: string;
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 envelope; the server enforces the final allowed limits. */
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 envelope. */
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.313',
160
+ version: '0.1.314',
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
- claimReceipts({
2103
+ (chunk) => {
2104
+ const leaseId = `receipt-lease:${crypto.randomUUID()}`;
2105
+ return claimReceipts({
2099
2106
  keys: chunk.map((request) => request.key),
2100
- leaseIds: chunk.map(() => `receipt-lease:${crypto.randomUUID()}`),
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 = requests.flatMap((request) => {
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
- if (targets.length === 0) return;
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
- if (this.#options.heartbeatRuntimeStepReceipts) {
2223
- for (const [leaseId, keys] of byLeaseId) {
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
- !this.#options.getRuntimeStepReceipt &&
2237
- !this.#options.getRuntimeStepReceipts
2320
+ this.#options.getRuntimeStepReceipt ||
2321
+ this.#options.getRuntimeStepReceipts
2238
2322
  ) {
2239
- throw new RuntimeReceiptLeaseLostError({
2240
- receiptKey: targets[0]?.receiptKey ?? 'unknown',
2241
- runId: this.currentReceiptOwnerRunId,
2242
- leaseId: targets[0]?.leaseId ?? 'unknown',
2243
- });
2244
- }
2245
-
2246
- const latest = await this.getRuntimeStepReceipts(
2247
- targets.map((target) => target.receiptKey),
2248
- );
2249
- for (const target of targets) {
2250
- const receipt = latest.get(target.receiptKey);
2251
- if (!this.runtimeToolReceiptStillOwned(receipt, target.leaseId)) {
2252
- throw new RuntimeReceiptLeaseLostError({
2253
- receiptKey: target.receiptKey,
2254
- runId: this.currentReceiptOwnerRunId,
2255
- leaseId: target.leaseId,
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
- claimReceipts({
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(() => `receipt-lease:${crypto.randomUUID()}`),
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.assertRuntimeToolReceiptOwnership([owner]),
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.assertRuntimeToolReceiptOwnership(
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.assertRuntimeToolReceiptOwnership([request]),
8827
+ this.renewRuntimeToolReceiptOwnership([request]),
8730
8828
  }
8731
8829
  : {}),
8732
8830
  timeoutMs: resolveRuntimeTimeoutMsForClaimedOwners([
@@ -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 { resolvePlaySandboxRuntimeLimits } from '@shared_libs/play-runtime/sandbox-runtime-limits';
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
- const DAYTONA_CUSTOM_RESOURCE_IMAGE = Image.base(
24
- 'node:20-bookworm-slim',
25
- ).workdir(DAYTONA_DEFAULT_WORKDIR);
26
- // Daytona's default image is the fast path. Resources are pinned at creation
27
- // and verified during acquisition, before customer code or billing starts.
28
- export const DAYTONA_SANDBOX_CPU = 1;
29
- export const DAYTONA_SANDBOX_MEMORY_GIB = 1;
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 = resolvePlaySandboxRuntimeLimits(
186
- input.context.sandboxRuntimeLimits
187
- ? {
188
- timeout: `${input.context.sandboxRuntimeLimits.timeoutSeconds / 60}m`,
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 = resolvePlaySandboxRuntimeLimits(
320
- input.context.sandboxRuntimeLimits
321
- ? {
322
- timeout: `${input.context.sandboxRuntimeLimits.timeoutSeconds / 60}m`,
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 ||
@@ -5,36 +5,38 @@ export type PlaySandboxRuntimeLimits = {
5
5
  diskGiB: number;
6
6
  };
7
7
 
8
+ export type PlaySandboxSize = 'standard';
9
+
10
+ export const PLAY_SANDBOX_SIZE_LIMITS = {
11
+ standard: {
12
+ memoryGiB: 1,
13
+ cpu: 1,
14
+ diskGiB: 3,
15
+ },
16
+ } as const satisfies Record<
17
+ PlaySandboxSize,
18
+ Omit<PlaySandboxRuntimeLimits, 'timeoutSeconds'>
19
+ >;
20
+
8
21
  export const STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS: PlaySandboxRuntimeLimits = {
9
22
  timeoutSeconds: 30 * 60,
10
- memoryGiB: 1,
11
- cpu: 1,
12
- diskGiB: 3,
23
+ ...PLAY_SANDBOX_SIZE_LIMITS.standard,
13
24
  };
14
25
 
15
26
  // Product-wide ceilings. Organisation entitlements may lower these, but a
16
27
  // customer-authored Play can never ask Daytona for an unbounded resource.
17
28
  export const MAX_PLAY_SANDBOX_RUNTIME_LIMITS: PlaySandboxRuntimeLimits = {
18
29
  timeoutSeconds: 4 * 60 * 60,
19
- memoryGiB: 16,
20
- cpu: 4,
21
- diskGiB: 50,
30
+ memoryGiB: STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS.memoryGiB,
31
+ cpu: STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS.cpu,
32
+ diskGiB: STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS.diskGiB,
22
33
  };
23
34
 
24
35
  export type PlaySandboxRuntimeDeclaration = {
25
36
  timeout?: string;
26
- memory?: string;
27
- cpu?: number;
28
- disk?: string;
37
+ size?: PlaySandboxSize;
29
38
  };
30
39
 
31
- function parsePositiveInteger(value: string, unit: string): number | null {
32
- const match = new RegExp(`^(\\d+)\\s*${unit}$`, 'i').exec(value.trim());
33
- if (!match) return null;
34
- const parsed = Number(match[1]);
35
- return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : null;
36
- }
37
-
38
40
  function parseTimeout(value: string): number | null {
39
41
  const match = /^(\d+)\s*([mh])$/i.exec(value.trim());
40
42
  if (!match) return null;
@@ -43,60 +45,65 @@ function parseTimeout(value: string): number | null {
43
45
  return amount * (match[2].toLowerCase() === 'h' ? 3600 : 60);
44
46
  }
45
47
 
46
- export function resolvePlaySandboxRuntimeLimits(
47
- declaration: PlaySandboxRuntimeDeclaration | null | undefined,
48
+ export function validatePlaySandboxRuntimeLimits(
49
+ value: PlaySandboxRuntimeLimits,
48
50
  ): PlaySandboxRuntimeLimits {
49
51
  const base = STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS;
50
- if (!declaration) return { ...base };
51
- const timeoutSeconds = declaration.timeout
52
- ? parseTimeout(declaration.timeout)
53
- : base.timeoutSeconds;
54
- const memoryGiB = declaration.memory
55
- ? parsePositiveInteger(declaration.memory, 'GiB')
56
- : base.memoryGiB;
57
- const diskGiB = declaration.disk
58
- ? parsePositiveInteger(declaration.disk, 'GiB')
59
- : base.diskGiB;
60
- const cpu = declaration.cpu ?? base.cpu;
61
- if (
62
- timeoutSeconds === null ||
63
- memoryGiB === null ||
64
- diskGiB === null ||
65
- !Number.isSafeInteger(cpu) ||
66
- cpu <= 0
67
- ) {
68
- throw new Error(
69
- 'Invalid runtime sandbox declaration. Use timeout like "90m" or "2h", memory/disk like "4GiB", and a positive integer cpu.',
70
- );
71
- }
72
- const resolved = { timeoutSeconds, memoryGiB, cpu, diskGiB };
73
52
  for (const key of ['memoryGiB', 'cpu', 'diskGiB'] as const) {
74
- if (resolved[key] < base[key]) {
53
+ if (value[key] !== base[key]) {
75
54
  throw new Error(
76
- `Requested runtime ${key}=${resolved[key]} is below the supported minimum ${base[key]}.`,
55
+ `Unsupported runtime sandbox resources: ${key}=${value[key]}. ` +
56
+ `Use runtime.size with a Deepline prebuilt sandbox. Supported sizes: "standard" ` +
57
+ `(${base.cpu} CPU, ${base.memoryGiB}GiB memory, ${base.diskGiB}GiB disk).`,
77
58
  );
78
59
  }
79
60
  }
80
- for (const key of Object.keys(
81
- resolved,
82
- ) as (keyof PlaySandboxRuntimeLimits)[]) {
83
- if (resolved[key] > MAX_PLAY_SANDBOX_RUNTIME_LIMITS[key]) {
84
- throw new Error(
85
- `Requested runtime ${key}=${resolved[key]} exceeds this organisation's maximum ${MAX_PLAY_SANDBOX_RUNTIME_LIMITS[key]}.`,
86
- );
87
- }
61
+ if (
62
+ !Number.isSafeInteger(value.timeoutSeconds) ||
63
+ value.timeoutSeconds <= 0
64
+ ) {
65
+ throw new Error(
66
+ 'Invalid runtime sandbox timeout. Use timeout like "90m" or "2h".',
67
+ );
68
+ }
69
+ if (value.timeoutSeconds > MAX_PLAY_SANDBOX_RUNTIME_LIMITS.timeoutSeconds) {
70
+ throw new Error(
71
+ `Requested runtime timeoutSeconds=${value.timeoutSeconds} exceeds this organisation's maximum ${MAX_PLAY_SANDBOX_RUNTIME_LIMITS.timeoutSeconds}.`,
72
+ );
88
73
  }
89
- return resolved;
74
+ return { ...value };
90
75
  }
91
76
 
92
- export function hasNonStandardPlaySandboxRuntimeLimits(
93
- value: PlaySandboxRuntimeLimits,
94
- ): boolean {
95
- return Object.keys(value).some(
96
- (key) =>
97
- value[key as keyof PlaySandboxRuntimeLimits] !==
98
- STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS[
99
- key as keyof PlaySandboxRuntimeLimits
100
- ],
77
+ export function resolvePlaySandboxRuntimeLimits(
78
+ declaration: PlaySandboxRuntimeDeclaration | null | undefined,
79
+ ): PlaySandboxRuntimeLimits {
80
+ const base = STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS;
81
+ if (!declaration) return { ...base };
82
+ const unsupportedProperties = Object.keys(declaration).filter(
83
+ (key) => key !== 'timeout' && key !== 'size',
101
84
  );
85
+ if (unsupportedProperties.length > 0) {
86
+ throw new Error(
87
+ `Unsupported runtime sandbox option${unsupportedProperties.length === 1 ? '' : 's'}: ${unsupportedProperties.join(', ')}. ` +
88
+ 'Use runtime.size with a Deepline prebuilt sandbox. Supported sizes: "standard".',
89
+ );
90
+ }
91
+ if (declaration.size !== undefined && declaration.size !== 'standard') {
92
+ throw new Error(
93
+ `Unsupported runtime sandbox size "${String(declaration.size)}". Supported sizes: "standard".`,
94
+ );
95
+ }
96
+ const timeoutSeconds = declaration.timeout
97
+ ? parseTimeout(declaration.timeout)
98
+ : base.timeoutSeconds;
99
+ if (timeoutSeconds === null) {
100
+ throw new Error(
101
+ 'Invalid runtime sandbox timeout. Use timeout like "90m" or "2h".',
102
+ );
103
+ }
104
+ const size = declaration.size ?? 'standard';
105
+ return validatePlaySandboxRuntimeLimits({
106
+ timeoutSeconds,
107
+ ...PLAY_SANDBOX_SIZE_LIMITS[size],
108
+ });
102
109
  }
@@ -887,6 +887,35 @@ function resolveStaticProperty(
887
887
  return resolution;
888
888
  }
889
889
 
890
+ function staticPropertyNames(
891
+ node: AstNode | null | undefined,
892
+ context: PlayMetadataExtractionContext,
893
+ ancestors = new Set<AstNode>(),
894
+ ): Set<string> | null {
895
+ if (!node) return new Set();
896
+ const object = objectExpressionFromNode(node, context);
897
+ if (!object || ancestors.has(object)) return null;
898
+ const nextAncestors = new Set(ancestors).add(object);
899
+ const names = new Set<string>();
900
+ for (const property of astArray(object.properties)) {
901
+ if (property.type === 'SpreadElement') {
902
+ const spreadNames = staticPropertyNames(
903
+ isAstNode(property.argument) ? property.argument : null,
904
+ context,
905
+ nextAncestors,
906
+ );
907
+ if (!spreadNames) return null;
908
+ for (const name of spreadNames) names.add(name);
909
+ continue;
910
+ }
911
+ if (property.type !== 'Property') return null;
912
+ const name = propertyNameFromKey(property);
913
+ if (name === null) return null;
914
+ names.add(name);
915
+ }
916
+ return names;
917
+ }
918
+
890
919
  function staticNumberFromExpression(
891
920
  node: AstNode | null | undefined,
892
921
  context: PlayMetadataExtractionContext,
@@ -957,30 +986,30 @@ function sandboxRuntimeDeclarationFromOptions(
957
986
  directRuntime.kind === 'found' ? directRuntime : bindingRuntime;
958
987
  if (runtime.kind !== 'found') return null;
959
988
 
989
+ const propertyNames = staticPropertyNames(runtime.value, context);
990
+ const unsupportedProperties = propertyNames
991
+ ? [...propertyNames].filter(
992
+ (propertyName) => propertyName !== 'timeout' && propertyName !== 'size',
993
+ )
994
+ : [];
995
+ if (unsupportedProperties.length > 0) {
996
+ throw new Error(
997
+ `Unsupported runtime sandbox option${unsupportedProperties.length === 1 ? '' : 's'} "${unsupportedProperties.join('", "')}". ` +
998
+ 'Use runtime.size with a Deepline prebuilt sandbox. Supported sizes: "standard".',
999
+ );
1000
+ }
960
1001
  const timeout = resolveStaticProperty(runtime.value, 'timeout', context);
961
- const memory = resolveStaticProperty(runtime.value, 'memory', context);
962
- const cpu = resolveStaticProperty(runtime.value, 'cpu', context);
963
- const disk = resolveStaticProperty(runtime.value, 'disk', context);
1002
+ const size = resolveStaticProperty(runtime.value, 'size', context);
964
1003
  const declaration: PlaySandboxRuntimeDeclaration = {};
965
1004
  if (timeout.kind === 'found') {
966
1005
  const value = staticStringFromExpression(timeout.value, context);
967
1006
  if (value === null) return null;
968
1007
  declaration.timeout = value;
969
1008
  }
970
- if (memory.kind === 'found') {
971
- const value = staticStringFromExpression(memory.value, context);
972
- if (value === null) return null;
973
- declaration.memory = value;
974
- }
975
- if (cpu.kind === 'found') {
976
- const value = staticNumberFromExpression(cpu.value, context);
977
- if (value === null) return null;
978
- declaration.cpu = value;
979
- }
980
- if (disk.kind === 'found') {
981
- const value = staticStringFromExpression(disk.value, context);
1009
+ if (size.kind === 'found') {
1010
+ const value = staticStringFromExpression(size.value, context);
982
1011
  if (value === null) return null;
983
- declaration.disk = value;
1012
+ declaration.size = value as PlaySandboxRuntimeDeclaration['size'];
984
1013
  }
985
1014
  return declaration;
986
1015
  }
@@ -1596,8 +1625,7 @@ async function analyzeSourceGraph(
1596
1625
  }
1597
1626
  const playName = metadata?.name ?? null;
1598
1627
  const playDescription = metadata?.description ?? null;
1599
- const sandboxRuntimeDeclaration =
1600
- metadata?.sandboxRuntimeDeclaration ?? null;
1628
+ const sandboxRuntimeDeclaration = metadata?.sandboxRuntimeDeclaration ?? null;
1601
1629
 
1602
1630
  return {
1603
1631
  sourceCode,
package/dist/cli/index.js CHANGED
@@ -1037,7 +1037,7 @@ var SDK_RELEASE = {
1037
1037
  // 0.1.253 makes play-page browser opening opt-in and retires --no-open.
1038
1038
  // 0.1.254 removes the internal operations tree from the published SDK CLI.
1039
1039
  // Operators use the checkout-local deepline-admin binary instead.
1040
- version: "0.1.313",
1040
+ version: "0.1.314",
1041
1041
  contracts: {
1042
1042
  api: {
1043
1043
  name: "sdk-http-api",
@@ -12143,24 +12143,23 @@ function resolveEnabledExecutionProfile(override) {
12143
12143
  }
12144
12144
 
12145
12145
  // ../shared_libs/play-runtime/sandbox-runtime-limits.ts
12146
+ var PLAY_SANDBOX_SIZE_LIMITS = {
12147
+ standard: {
12148
+ memoryGiB: 1,
12149
+ cpu: 1,
12150
+ diskGiB: 3
12151
+ }
12152
+ };
12146
12153
  var STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS = {
12147
12154
  timeoutSeconds: 30 * 60,
12148
- memoryGiB: 1,
12149
- cpu: 1,
12150
- diskGiB: 3
12155
+ ...PLAY_SANDBOX_SIZE_LIMITS.standard
12151
12156
  };
12152
12157
  var MAX_PLAY_SANDBOX_RUNTIME_LIMITS = {
12153
12158
  timeoutSeconds: 4 * 60 * 60,
12154
- memoryGiB: 16,
12155
- cpu: 4,
12156
- diskGiB: 50
12159
+ memoryGiB: STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS.memoryGiB,
12160
+ cpu: STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS.cpu,
12161
+ diskGiB: STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS.diskGiB
12157
12162
  };
12158
- function parsePositiveInteger3(value, unit) {
12159
- const match = new RegExp(`^(\\d+)\\s*${unit}$`, "i").exec(value.trim());
12160
- if (!match) return null;
12161
- const parsed = Number(match[1]);
12162
- return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : null;
12163
- }
12164
12163
  function parseTimeout(value) {
12165
12164
  const match = /^(\d+)\s*([mh])$/i.exec(value.trim());
12166
12165
  if (!match) return null;
@@ -12168,41 +12167,54 @@ function parseTimeout(value) {
12168
12167
  if (!Number.isSafeInteger(amount) || amount <= 0) return null;
12169
12168
  return amount * (match[2].toLowerCase() === "h" ? 3600 : 60);
12170
12169
  }
12171
- function resolvePlaySandboxRuntimeLimits(declaration) {
12170
+ function validatePlaySandboxRuntimeLimits(value) {
12172
12171
  const base = STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS;
12173
- if (!declaration) return { ...base };
12174
- const timeoutSeconds = declaration.timeout ? parseTimeout(declaration.timeout) : base.timeoutSeconds;
12175
- const memoryGiB = declaration.memory ? parsePositiveInteger3(declaration.memory, "GiB") : base.memoryGiB;
12176
- const diskGiB = declaration.disk ? parsePositiveInteger3(declaration.disk, "GiB") : base.diskGiB;
12177
- const cpu = declaration.cpu ?? base.cpu;
12178
- if (timeoutSeconds === null || memoryGiB === null || diskGiB === null || !Number.isSafeInteger(cpu) || cpu <= 0) {
12179
- throw new Error(
12180
- 'Invalid runtime sandbox declaration. Use timeout like "90m" or "2h", memory/disk like "4GiB", and a positive integer cpu.'
12181
- );
12182
- }
12183
- const resolved = { timeoutSeconds, memoryGiB, cpu, diskGiB };
12184
12172
  for (const key of ["memoryGiB", "cpu", "diskGiB"]) {
12185
- if (resolved[key] < base[key]) {
12173
+ if (value[key] !== base[key]) {
12186
12174
  throw new Error(
12187
- `Requested runtime ${key}=${resolved[key]} is below the supported minimum ${base[key]}.`
12175
+ `Unsupported runtime sandbox resources: ${key}=${value[key]}. Use runtime.size with a Deepline prebuilt sandbox. Supported sizes: "standard" (${base.cpu} CPU, ${base.memoryGiB}GiB memory, ${base.diskGiB}GiB disk).`
12188
12176
  );
12189
12177
  }
12190
12178
  }
12191
- for (const key of Object.keys(
12192
- resolved
12193
- )) {
12194
- if (resolved[key] > MAX_PLAY_SANDBOX_RUNTIME_LIMITS[key]) {
12195
- throw new Error(
12196
- `Requested runtime ${key}=${resolved[key]} exceeds this organisation's maximum ${MAX_PLAY_SANDBOX_RUNTIME_LIMITS[key]}.`
12197
- );
12198
- }
12179
+ if (!Number.isSafeInteger(value.timeoutSeconds) || value.timeoutSeconds <= 0) {
12180
+ throw new Error(
12181
+ 'Invalid runtime sandbox timeout. Use timeout like "90m" or "2h".'
12182
+ );
12199
12183
  }
12200
- return resolved;
12184
+ if (value.timeoutSeconds > MAX_PLAY_SANDBOX_RUNTIME_LIMITS.timeoutSeconds) {
12185
+ throw new Error(
12186
+ `Requested runtime timeoutSeconds=${value.timeoutSeconds} exceeds this organisation's maximum ${MAX_PLAY_SANDBOX_RUNTIME_LIMITS.timeoutSeconds}.`
12187
+ );
12188
+ }
12189
+ return { ...value };
12201
12190
  }
12202
- function hasNonStandardPlaySandboxRuntimeLimits(value) {
12203
- return Object.keys(value).some(
12204
- (key) => value[key] !== STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS[key]
12191
+ function resolvePlaySandboxRuntimeLimits(declaration) {
12192
+ const base = STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS;
12193
+ if (!declaration) return { ...base };
12194
+ const unsupportedProperties = Object.keys(declaration).filter(
12195
+ (key) => key !== "timeout" && key !== "size"
12205
12196
  );
12197
+ if (unsupportedProperties.length > 0) {
12198
+ throw new Error(
12199
+ `Unsupported runtime sandbox option${unsupportedProperties.length === 1 ? "" : "s"}: ${unsupportedProperties.join(", ")}. Use runtime.size with a Deepline prebuilt sandbox. Supported sizes: "standard".`
12200
+ );
12201
+ }
12202
+ if (declaration.size !== void 0 && declaration.size !== "standard") {
12203
+ throw new Error(
12204
+ `Unsupported runtime sandbox size "${String(declaration.size)}". Supported sizes: "standard".`
12205
+ );
12206
+ }
12207
+ const timeoutSeconds = declaration.timeout ? parseTimeout(declaration.timeout) : base.timeoutSeconds;
12208
+ if (timeoutSeconds === null) {
12209
+ throw new Error(
12210
+ 'Invalid runtime sandbox timeout. Use timeout like "90m" or "2h".'
12211
+ );
12212
+ }
12213
+ const size = declaration.size ?? "standard";
12214
+ return validatePlaySandboxRuntimeLimits({
12215
+ timeoutSeconds,
12216
+ ...PLAY_SANDBOX_SIZE_LIMITS[size]
12217
+ });
12206
12218
  }
12207
12219
 
12208
12220
  // ../shared_libs/play-runtime/worker-api-types.ts
@@ -12265,9 +12277,11 @@ function estimateDaytonaMaximumComputeCredits(limits) {
12265
12277
  );
12266
12278
  }
12267
12279
  function formatNonStandardSandboxRuntimeWarning(limits) {
12268
- if (!hasNonStandardPlaySandboxRuntimeLimits(limits)) return null;
12280
+ if (limits.timeoutSeconds <= STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS.timeoutSeconds) {
12281
+ return null;
12282
+ }
12269
12283
  const maximumComputeCredits = estimateDaytonaMaximumComputeCredits(limits);
12270
- return `This Play requests a non-standard sandbox (${limits.timeoutSeconds}s, ${limits.memoryGiB}GiB memory, ${limits.cpu} CPU, ${limits.diskGiB}GiB disk). Maximum compute estimate: ${maximumComputeCredits.toFixed(2)} Deepline credits. It may queue longer.`;
12284
+ return `This Play requests the standard sandbox for up to ${limits.timeoutSeconds}s (${limits.memoryGiB}GiB memory, ${limits.cpu} CPU, ${limits.diskGiB}GiB disk). Maximum compute estimate: ${maximumComputeCredits.toFixed(2)} Deepline credits. Longer runtimes are risky.`;
12271
12285
  }
12272
12286
 
12273
12287
  // ../shared_libs/play-runtime/internal-step-ids.ts
@@ -12829,7 +12843,7 @@ function looksLikeFilePath(target) {
12829
12843
  }
12830
12844
  return target.includes("\\") || /\.(ts|js|mjs|play\.ts)$/.test(target);
12831
12845
  }
12832
- function parsePositiveInteger4(value, flagName) {
12846
+ function parsePositiveInteger3(value, flagName) {
12833
12847
  const parsed = Number.parseInt(value, 10);
12834
12848
  if (!Number.isFinite(parsed) || parsed <= 0) {
12835
12849
  throw new Error(`${flagName} must be a positive integer.`);
@@ -16349,7 +16363,7 @@ function parsePlayRunOptions(args) {
16349
16363
  );
16350
16364
  }
16351
16365
  if ((arg === "--tail-timeout-ms" || arg === "--timeout-ms") && args[index + 1]) {
16352
- waitTimeoutMs = parsePositiveInteger4(args[++index], arg);
16366
+ waitTimeoutMs = parsePositiveInteger3(args[++index], arg);
16353
16367
  continue;
16354
16368
  }
16355
16369
  if (PLAY_RUN_RESERVED_BOOLEAN_FLAGS.has(arg)) {
@@ -17510,7 +17524,7 @@ async function handleRunLogs(args) {
17510
17524
  for (let index = 0; index < args.length; index += 1) {
17511
17525
  const arg = args[index];
17512
17526
  if (arg === "--limit" && args[index + 1]) {
17513
- limit = parsePositiveInteger4(args[++index], "--limit");
17527
+ limit = parsePositiveInteger3(args[++index], "--limit");
17514
17528
  continue;
17515
17529
  }
17516
17530
  if (arg === "--out" && args[index + 1]) {
@@ -1022,7 +1022,7 @@ var SDK_RELEASE = {
1022
1022
  // 0.1.253 makes play-page browser opening opt-in and retires --no-open.
1023
1023
  // 0.1.254 removes the internal operations tree from the published SDK CLI.
1024
1024
  // Operators use the checkout-local deepline-admin binary instead.
1025
- version: "0.1.313",
1025
+ version: "0.1.314",
1026
1026
  contracts: {
1027
1027
  api: {
1028
1028
  name: "sdk-http-api",
@@ -12172,24 +12172,23 @@ function resolveEnabledExecutionProfile(override) {
12172
12172
  }
12173
12173
 
12174
12174
  // ../shared_libs/play-runtime/sandbox-runtime-limits.ts
12175
+ var PLAY_SANDBOX_SIZE_LIMITS = {
12176
+ standard: {
12177
+ memoryGiB: 1,
12178
+ cpu: 1,
12179
+ diskGiB: 3
12180
+ }
12181
+ };
12175
12182
  var STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS = {
12176
12183
  timeoutSeconds: 30 * 60,
12177
- memoryGiB: 1,
12178
- cpu: 1,
12179
- diskGiB: 3
12184
+ ...PLAY_SANDBOX_SIZE_LIMITS.standard
12180
12185
  };
12181
12186
  var MAX_PLAY_SANDBOX_RUNTIME_LIMITS = {
12182
12187
  timeoutSeconds: 4 * 60 * 60,
12183
- memoryGiB: 16,
12184
- cpu: 4,
12185
- diskGiB: 50
12188
+ memoryGiB: STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS.memoryGiB,
12189
+ cpu: STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS.cpu,
12190
+ diskGiB: STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS.diskGiB
12186
12191
  };
12187
- function parsePositiveInteger3(value, unit) {
12188
- const match = new RegExp(`^(\\d+)\\s*${unit}$`, "i").exec(value.trim());
12189
- if (!match) return null;
12190
- const parsed = Number(match[1]);
12191
- return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : null;
12192
- }
12193
12192
  function parseTimeout(value) {
12194
12193
  const match = /^(\d+)\s*([mh])$/i.exec(value.trim());
12195
12194
  if (!match) return null;
@@ -12197,41 +12196,54 @@ function parseTimeout(value) {
12197
12196
  if (!Number.isSafeInteger(amount) || amount <= 0) return null;
12198
12197
  return amount * (match[2].toLowerCase() === "h" ? 3600 : 60);
12199
12198
  }
12200
- function resolvePlaySandboxRuntimeLimits(declaration) {
12199
+ function validatePlaySandboxRuntimeLimits(value) {
12201
12200
  const base = STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS;
12202
- if (!declaration) return { ...base };
12203
- const timeoutSeconds = declaration.timeout ? parseTimeout(declaration.timeout) : base.timeoutSeconds;
12204
- const memoryGiB = declaration.memory ? parsePositiveInteger3(declaration.memory, "GiB") : base.memoryGiB;
12205
- const diskGiB = declaration.disk ? parsePositiveInteger3(declaration.disk, "GiB") : base.diskGiB;
12206
- const cpu = declaration.cpu ?? base.cpu;
12207
- if (timeoutSeconds === null || memoryGiB === null || diskGiB === null || !Number.isSafeInteger(cpu) || cpu <= 0) {
12208
- throw new Error(
12209
- 'Invalid runtime sandbox declaration. Use timeout like "90m" or "2h", memory/disk like "4GiB", and a positive integer cpu.'
12210
- );
12211
- }
12212
- const resolved = { timeoutSeconds, memoryGiB, cpu, diskGiB };
12213
12201
  for (const key of ["memoryGiB", "cpu", "diskGiB"]) {
12214
- if (resolved[key] < base[key]) {
12202
+ if (value[key] !== base[key]) {
12215
12203
  throw new Error(
12216
- `Requested runtime ${key}=${resolved[key]} is below the supported minimum ${base[key]}.`
12204
+ `Unsupported runtime sandbox resources: ${key}=${value[key]}. Use runtime.size with a Deepline prebuilt sandbox. Supported sizes: "standard" (${base.cpu} CPU, ${base.memoryGiB}GiB memory, ${base.diskGiB}GiB disk).`
12217
12205
  );
12218
12206
  }
12219
12207
  }
12220
- for (const key of Object.keys(
12221
- resolved
12222
- )) {
12223
- if (resolved[key] > MAX_PLAY_SANDBOX_RUNTIME_LIMITS[key]) {
12224
- throw new Error(
12225
- `Requested runtime ${key}=${resolved[key]} exceeds this organisation's maximum ${MAX_PLAY_SANDBOX_RUNTIME_LIMITS[key]}.`
12226
- );
12227
- }
12208
+ if (!Number.isSafeInteger(value.timeoutSeconds) || value.timeoutSeconds <= 0) {
12209
+ throw new Error(
12210
+ 'Invalid runtime sandbox timeout. Use timeout like "90m" or "2h".'
12211
+ );
12228
12212
  }
12229
- return resolved;
12213
+ if (value.timeoutSeconds > MAX_PLAY_SANDBOX_RUNTIME_LIMITS.timeoutSeconds) {
12214
+ throw new Error(
12215
+ `Requested runtime timeoutSeconds=${value.timeoutSeconds} exceeds this organisation's maximum ${MAX_PLAY_SANDBOX_RUNTIME_LIMITS.timeoutSeconds}.`
12216
+ );
12217
+ }
12218
+ return { ...value };
12230
12219
  }
12231
- function hasNonStandardPlaySandboxRuntimeLimits(value) {
12232
- return Object.keys(value).some(
12233
- (key) => value[key] !== STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS[key]
12220
+ function resolvePlaySandboxRuntimeLimits(declaration) {
12221
+ const base = STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS;
12222
+ if (!declaration) return { ...base };
12223
+ const unsupportedProperties = Object.keys(declaration).filter(
12224
+ (key) => key !== "timeout" && key !== "size"
12234
12225
  );
12226
+ if (unsupportedProperties.length > 0) {
12227
+ throw new Error(
12228
+ `Unsupported runtime sandbox option${unsupportedProperties.length === 1 ? "" : "s"}: ${unsupportedProperties.join(", ")}. Use runtime.size with a Deepline prebuilt sandbox. Supported sizes: "standard".`
12229
+ );
12230
+ }
12231
+ if (declaration.size !== void 0 && declaration.size !== "standard") {
12232
+ throw new Error(
12233
+ `Unsupported runtime sandbox size "${String(declaration.size)}". Supported sizes: "standard".`
12234
+ );
12235
+ }
12236
+ const timeoutSeconds = declaration.timeout ? parseTimeout(declaration.timeout) : base.timeoutSeconds;
12237
+ if (timeoutSeconds === null) {
12238
+ throw new Error(
12239
+ 'Invalid runtime sandbox timeout. Use timeout like "90m" or "2h".'
12240
+ );
12241
+ }
12242
+ const size = declaration.size ?? "standard";
12243
+ return validatePlaySandboxRuntimeLimits({
12244
+ timeoutSeconds,
12245
+ ...PLAY_SANDBOX_SIZE_LIMITS[size]
12246
+ });
12235
12247
  }
12236
12248
 
12237
12249
  // ../shared_libs/play-runtime/worker-api-types.ts
@@ -12294,9 +12306,11 @@ function estimateDaytonaMaximumComputeCredits(limits) {
12294
12306
  );
12295
12307
  }
12296
12308
  function formatNonStandardSandboxRuntimeWarning(limits) {
12297
- if (!hasNonStandardPlaySandboxRuntimeLimits(limits)) return null;
12309
+ if (limits.timeoutSeconds <= STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS.timeoutSeconds) {
12310
+ return null;
12311
+ }
12298
12312
  const maximumComputeCredits = estimateDaytonaMaximumComputeCredits(limits);
12299
- return `This Play requests a non-standard sandbox (${limits.timeoutSeconds}s, ${limits.memoryGiB}GiB memory, ${limits.cpu} CPU, ${limits.diskGiB}GiB disk). Maximum compute estimate: ${maximumComputeCredits.toFixed(2)} Deepline credits. It may queue longer.`;
12313
+ return `This Play requests the standard sandbox for up to ${limits.timeoutSeconds}s (${limits.memoryGiB}GiB memory, ${limits.cpu} CPU, ${limits.diskGiB}GiB disk). Maximum compute estimate: ${maximumComputeCredits.toFixed(2)} Deepline credits. Longer runtimes are risky.`;
12300
12314
  }
12301
12315
 
12302
12316
  // ../shared_libs/play-runtime/internal-step-ids.ts
@@ -12858,7 +12872,7 @@ function looksLikeFilePath(target) {
12858
12872
  }
12859
12873
  return target.includes("\\") || /\.(ts|js|mjs|play\.ts)$/.test(target);
12860
12874
  }
12861
- function parsePositiveInteger4(value, flagName) {
12875
+ function parsePositiveInteger3(value, flagName) {
12862
12876
  const parsed = Number.parseInt(value, 10);
12863
12877
  if (!Number.isFinite(parsed) || parsed <= 0) {
12864
12878
  throw new Error(`${flagName} must be a positive integer.`);
@@ -16378,7 +16392,7 @@ function parsePlayRunOptions(args) {
16378
16392
  );
16379
16393
  }
16380
16394
  if ((arg === "--tail-timeout-ms" || arg === "--timeout-ms") && args[index + 1]) {
16381
- waitTimeoutMs = parsePositiveInteger4(args[++index], arg);
16395
+ waitTimeoutMs = parsePositiveInteger3(args[++index], arg);
16382
16396
  continue;
16383
16397
  }
16384
16398
  if (PLAY_RUN_RESERVED_BOOLEAN_FLAGS.has(arg)) {
@@ -17539,7 +17553,7 @@ async function handleRunLogs(args) {
17539
17553
  for (let index = 0; index < args.length; index += 1) {
17540
17554
  const arg = args[index];
17541
17555
  if (arg === "--limit" && args[index + 1]) {
17542
- limit = parsePositiveInteger4(args[++index], "--limit");
17556
+ limit = parsePositiveInteger3(args[++index], "--limit");
17543
17557
  continue;
17544
17558
  }
17545
17559
  if (arg === "--out" && args[index + 1]) {
package/dist/index.d.mts CHANGED
@@ -3844,16 +3844,12 @@ type PlayBindings = {
3844
3844
  /** Stop the run before a billed action would push total run credits above this cap. */
3845
3845
  maxCreditsPerRun?: number;
3846
3846
  };
3847
- /** Requested sandbox envelope; the server enforces the final allowed limits. */
3847
+ /** Requested prebuilt sandbox and runtime deadline. */
3848
3848
  runtime?: {
3849
3849
  /** Duration such as `"90m"` or `"2h"`. */
3850
3850
  timeout?: string;
3851
- /** Memory such as `"4GiB"`. */
3852
- memory?: string;
3853
- /** Whole vCPU count. */
3854
- cpu?: number;
3855
- /** Disk such as `"10GiB"`. */
3856
- disk?: string;
3851
+ /** Deepline-managed prebuilt sandbox size. */
3852
+ size?: 'standard';
3857
3853
  };
3858
3854
  /** Webhook trigger with optional HMAC signature verification. */
3859
3855
  webhook?: {
@@ -4669,7 +4665,7 @@ type DefinePlayConfig<TInput, TOutput extends PlayReturnObject> = {
4669
4665
  bindings?: PlayBindings;
4670
4666
  /** Billing options. */
4671
4667
  billing?: PlayBindings['billing'];
4672
- /** Requested sandbox envelope; the server enforces the final allowed limits. */
4668
+ /** Requested prebuilt sandbox and runtime deadline. */
4673
4669
  runtime?: PlayBindings['runtime'];
4674
4670
  /** Runtime compatibility override. Omit for the current typed contract. */
4675
4671
  compatibility?: PlayBindings['compatibility'];
@@ -4713,7 +4709,7 @@ declare function runIf<Row, Value>(predicate: (row: Row, index: number) => boole
4713
4709
  type DefinedPlay<TInput, TOutput extends PlayReturnObject> = ((ctx: DeeplinePlayRuntimeContext, input: TInput) => Promise<TOutput>) & DeeplineNamedPlay<TInput, TOutput> & {
4714
4710
  /** Optional trigger bindings (cron, webhook). */
4715
4711
  readonly bindings?: PlayBindings;
4716
- /** Requested sandbox envelope. */
4712
+ /** Requested prebuilt sandbox and runtime deadline. */
4717
4713
  readonly runtime?: PlayBindings['runtime'];
4718
4714
  /** Runtime compatibility explicitly selected by the author. */
4719
4715
  readonly compatibility?: PlayBindings['compatibility'];
package/dist/index.d.ts CHANGED
@@ -3844,16 +3844,12 @@ type PlayBindings = {
3844
3844
  /** Stop the run before a billed action would push total run credits above this cap. */
3845
3845
  maxCreditsPerRun?: number;
3846
3846
  };
3847
- /** Requested sandbox envelope; the server enforces the final allowed limits. */
3847
+ /** Requested prebuilt sandbox and runtime deadline. */
3848
3848
  runtime?: {
3849
3849
  /** Duration such as `"90m"` or `"2h"`. */
3850
3850
  timeout?: string;
3851
- /** Memory such as `"4GiB"`. */
3852
- memory?: string;
3853
- /** Whole vCPU count. */
3854
- cpu?: number;
3855
- /** Disk such as `"10GiB"`. */
3856
- disk?: string;
3851
+ /** Deepline-managed prebuilt sandbox size. */
3852
+ size?: 'standard';
3857
3853
  };
3858
3854
  /** Webhook trigger with optional HMAC signature verification. */
3859
3855
  webhook?: {
@@ -4669,7 +4665,7 @@ type DefinePlayConfig<TInput, TOutput extends PlayReturnObject> = {
4669
4665
  bindings?: PlayBindings;
4670
4666
  /** Billing options. */
4671
4667
  billing?: PlayBindings['billing'];
4672
- /** Requested sandbox envelope; the server enforces the final allowed limits. */
4668
+ /** Requested prebuilt sandbox and runtime deadline. */
4673
4669
  runtime?: PlayBindings['runtime'];
4674
4670
  /** Runtime compatibility override. Omit for the current typed contract. */
4675
4671
  compatibility?: PlayBindings['compatibility'];
@@ -4713,7 +4709,7 @@ declare function runIf<Row, Value>(predicate: (row: Row, index: number) => boole
4713
4709
  type DefinedPlay<TInput, TOutput extends PlayReturnObject> = ((ctx: DeeplinePlayRuntimeContext, input: TInput) => Promise<TOutput>) & DeeplineNamedPlay<TInput, TOutput> & {
4714
4710
  /** Optional trigger bindings (cron, webhook). */
4715
4711
  readonly bindings?: PlayBindings;
4716
- /** Requested sandbox envelope. */
4712
+ /** Requested prebuilt sandbox and runtime deadline. */
4717
4713
  readonly runtime?: PlayBindings['runtime'];
4718
4714
  /** Runtime compatibility explicitly selected by the author. */
4719
4715
  readonly compatibility?: PlayBindings['compatibility'];
package/dist/index.js CHANGED
@@ -760,7 +760,7 @@ var SDK_RELEASE = {
760
760
  // 0.1.253 makes play-page browser opening opt-in and retires --no-open.
761
761
  // 0.1.254 removes the internal operations tree from the published SDK CLI.
762
762
  // Operators use the checkout-local deepline-admin binary instead.
763
- version: "0.1.313",
763
+ version: "0.1.314",
764
764
  contracts: {
765
765
  api: {
766
766
  name: "sdk-http-api",
package/dist/index.mjs CHANGED
@@ -686,7 +686,7 @@ var SDK_RELEASE = {
686
686
  // 0.1.253 makes play-page browser opening opt-in and retires --no-open.
687
687
  // 0.1.254 removes the internal operations tree from the published SDK CLI.
688
688
  // Operators use the checkout-local deepline-admin binary instead.
689
- version: "0.1.313",
689
+ version: "0.1.314",
690
690
  contracts: {
691
691
  api: {
692
692
  name: "sdk-http-api",
@@ -40,11 +40,10 @@ type PlayBundleArtifact = {
40
40
  cacheHit: boolean;
41
41
  };
42
42
 
43
+ type PlaySandboxSize = 'standard';
43
44
  type PlaySandboxRuntimeDeclaration = {
44
45
  timeout?: string;
45
- memory?: string;
46
- cpu?: number;
47
- disk?: string;
46
+ size?: PlaySandboxSize;
48
47
  };
49
48
 
50
49
  type ImportedPlayDependency = {
@@ -40,11 +40,10 @@ type PlayBundleArtifact = {
40
40
  cacheHit: boolean;
41
41
  };
42
42
 
43
+ type PlaySandboxSize = 'standard';
43
44
  type PlaySandboxRuntimeDeclaration = {
44
45
  timeout?: string;
45
- memory?: string;
46
- cpu?: number;
47
- disk?: string;
46
+ size?: PlaySandboxSize;
48
47
  };
49
48
 
50
49
  type ImportedPlayDependency = {
@@ -556,6 +556,30 @@ function resolveStaticProperty(node, propertyName, context, ancestors = /* @__PU
556
556
  }
557
557
  return resolution;
558
558
  }
559
+ function staticPropertyNames(node, context, ancestors = /* @__PURE__ */ new Set()) {
560
+ if (!node) return /* @__PURE__ */ new Set();
561
+ const object = objectExpressionFromNode(node, context);
562
+ if (!object || ancestors.has(object)) return null;
563
+ const nextAncestors = new Set(ancestors).add(object);
564
+ const names = /* @__PURE__ */ new Set();
565
+ for (const property of astArray(object.properties)) {
566
+ if (property.type === "SpreadElement") {
567
+ const spreadNames = staticPropertyNames(
568
+ isAstNode(property.argument) ? property.argument : null,
569
+ context,
570
+ nextAncestors
571
+ );
572
+ if (!spreadNames) return null;
573
+ for (const name2 of spreadNames) names.add(name2);
574
+ continue;
575
+ }
576
+ if (property.type !== "Property") return null;
577
+ const name = propertyNameFromKey(property);
578
+ if (name === null) return null;
579
+ names.add(name);
580
+ }
581
+ return names;
582
+ }
559
583
  function staticNumberFromExpression(node, context, seen = /* @__PURE__ */ new Set()) {
560
584
  const expression = unwrapStaticExpression(node);
561
585
  if (!expression) return null;
@@ -600,30 +624,27 @@ function sandboxRuntimeDeclarationFromOptions(node, context) {
600
624
  const bindingRuntime = bindings.kind === "found" ? resolveStaticProperty(bindings.value, "runtime", context) : { kind: "absent" };
601
625
  const runtime = directRuntime.kind === "found" ? directRuntime : bindingRuntime;
602
626
  if (runtime.kind !== "found") return null;
627
+ const propertyNames = staticPropertyNames(runtime.value, context);
628
+ const unsupportedProperties = propertyNames ? [...propertyNames].filter(
629
+ (propertyName) => propertyName !== "timeout" && propertyName !== "size"
630
+ ) : [];
631
+ if (unsupportedProperties.length > 0) {
632
+ throw new Error(
633
+ `Unsupported runtime sandbox option${unsupportedProperties.length === 1 ? "" : "s"} "${unsupportedProperties.join('", "')}". Use runtime.size with a Deepline prebuilt sandbox. Supported sizes: "standard".`
634
+ );
635
+ }
603
636
  const timeout = resolveStaticProperty(runtime.value, "timeout", context);
604
- const memory = resolveStaticProperty(runtime.value, "memory", context);
605
- const cpu = resolveStaticProperty(runtime.value, "cpu", context);
606
- const disk = resolveStaticProperty(runtime.value, "disk", context);
637
+ const size = resolveStaticProperty(runtime.value, "size", context);
607
638
  const declaration = {};
608
639
  if (timeout.kind === "found") {
609
640
  const value = staticStringFromExpression(timeout.value, context);
610
641
  if (value === null) return null;
611
642
  declaration.timeout = value;
612
643
  }
613
- if (memory.kind === "found") {
614
- const value = staticStringFromExpression(memory.value, context);
615
- if (value === null) return null;
616
- declaration.memory = value;
617
- }
618
- if (cpu.kind === "found") {
619
- const value = staticNumberFromExpression(cpu.value, context);
620
- if (value === null) return null;
621
- declaration.cpu = value;
622
- }
623
- if (disk.kind === "found") {
624
- const value = staticStringFromExpression(disk.value, context);
644
+ if (size.kind === "found") {
645
+ const value = staticStringFromExpression(size.value, context);
625
646
  if (value === null) return null;
626
- declaration.disk = value;
647
+ declaration.size = value;
627
648
  }
628
649
  return declaration;
629
650
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepline",
3
- "version": "0.1.313",
3
+ "version": "0.1.314",
4
4
  "description": "Deepline SDK + CLI — B2B data enrichment powered by durable cloud execution",
5
5
  "license": "MIT",
6
6
  "repository": {