deepline 0.1.306 → 0.1.308

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.
Files changed (27) hide show
  1. package/dist/bundling-sources/sdk/src/client.ts +11 -24
  2. package/dist/bundling-sources/sdk/src/errors.ts +3 -1
  3. package/dist/bundling-sources/sdk/src/http.ts +16 -24
  4. package/dist/bundling-sources/sdk/src/play.ts +26 -0
  5. package/dist/bundling-sources/sdk/src/release.ts +1 -1
  6. package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +3 -0
  7. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +66 -11
  8. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +13 -5
  9. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +12 -3
  10. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/local-process.ts +21 -9
  11. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/types.ts +2 -0
  12. package/dist/bundling-sources/shared_libs/play-runtime/runtime-constants.ts +5 -1
  13. package/dist/bundling-sources/shared_libs/play-runtime/sandbox-compute-usage.ts +17 -2
  14. package/dist/bundling-sources/shared_libs/play-runtime/sandbox-runtime-limits.ts +102 -0
  15. package/dist/bundling-sources/shared_libs/play-runtime/tool-http-errors.ts +56 -9
  16. package/dist/bundling-sources/shared_libs/plays/bundling/index.ts +56 -0
  17. package/dist/bundling-sources/shared_libs/plays/contracts.ts +6 -0
  18. package/dist/cli/index.js +197 -42
  19. package/dist/cli/index.mjs +197 -42
  20. package/dist/index.d.mts +19 -1
  21. package/dist/index.d.ts +19 -1
  22. package/dist/index.js +33 -30
  23. package/dist/index.mjs +33 -30
  24. package/dist/plays/bundle-play-file.d.mts +8 -0
  25. package/dist/plays/bundle-play-file.d.ts +8 -0
  26. package/dist/plays/bundle-play-file.mjs +42 -0
  27. package/package.json +1 -1
@@ -1,3 +1,5 @@
1
+ import { MAX_PLAY_SANDBOX_RUNTIME_LIMITS } from './sandbox-runtime-limits';
2
+
1
3
  /** Maximum active user-code runtime for a standard play, in seconds. */
2
4
  export const STANDARD_PLAY_RUNTIME_LIMIT_SECONDS = 30 * 60;
3
5
  export const STANDARD_PLAY_RUNTIME_LIMIT_LABEL = '30 minutes';
@@ -22,7 +24,9 @@ export const PLAY_RUNNER_STARTUP_GRACE_SECONDS = 4 * 60;
22
24
  export const PLAY_RUNNER_TIMEOUT_SECONDS = 40 * 60;
23
25
 
24
26
  /** TTL for workflow executor tokens, in seconds. */
25
- export const WORKFLOW_EXECUTOR_TOKEN_TTL_SECONDS = PLAY_RUNNER_TIMEOUT_SECONDS;
27
+ export const WORKFLOW_EXECUTOR_TOKEN_TTL_SECONDS =
28
+ MAX_PLAY_SANDBOX_RUNTIME_LIMITS.timeoutSeconds +
29
+ (PLAY_RUNNER_TIMEOUT_SECONDS - STANDARD_PLAY_RUNTIME_LIMIT_SECONDS);
26
30
 
27
31
  /**
28
32
  * Absurd run-claim lease window, in seconds. The scheduler expires a claim whose
@@ -22,6 +22,7 @@ export function resolveDaytonaRuntimeComputeItems(input: {
22
22
  cpu: number | null;
23
23
  memoryGiB: number | null;
24
24
  diskGiB: number | null;
25
+ maxBillingDurationSeconds: number | null;
25
26
  }
26
27
  >();
27
28
  const items: ComputeBillingItem[] = [];
@@ -38,6 +39,9 @@ export function resolveDaytonaRuntimeComputeItems(input: {
38
39
  const billingStartedAt = finiteNonNegative(value.billingStartedAt);
39
40
  if (billingStartedAt === null) continue;
40
41
  const billingEndedAt = finiteNonNegative(value.billingEndedAt);
42
+ const maxBillingDurationSeconds = finiteNonNegative(
43
+ value.maxBillingDurationSeconds,
44
+ );
41
45
  const existing = resourcesBySandboxId.get(sandboxId);
42
46
  resourcesBySandboxId.set(sandboxId, {
43
47
  startedAt: Math.min(
@@ -46,16 +50,27 @@ export function resolveDaytonaRuntimeComputeItems(input: {
46
50
  ),
47
51
  endedAt:
48
52
  billingEndedAt === null
49
- ? existing?.endedAt ?? null
53
+ ? (existing?.endedAt ?? null)
50
54
  : Math.max(existing?.endedAt ?? billingEndedAt, billingEndedAt),
51
55
  cpu: finiteNonNegative(value.cpu) ?? existing?.cpu ?? null,
52
56
  memoryGiB:
53
57
  finiteNonNegative(value.memoryGiB) ?? existing?.memoryGiB ?? null,
54
58
  diskGiB: finiteNonNegative(value.diskGiB) ?? existing?.diskGiB ?? null,
59
+ maxBillingDurationSeconds:
60
+ maxBillingDurationSeconds ??
61
+ existing?.maxBillingDurationSeconds ??
62
+ null,
55
63
  });
56
64
  }
57
65
  for (const [sandboxId, resource] of resourcesBySandboxId) {
58
- const endedAt = resource.endedAt ?? input.endedAt;
66
+ const observedEndedAt = resource.endedAt ?? input.endedAt;
67
+ const endedAt =
68
+ resource.maxBillingDurationSeconds === null
69
+ ? observedEndedAt
70
+ : Math.min(
71
+ observedEndedAt,
72
+ resource.startedAt + resource.maxBillingDurationSeconds * 1_000,
73
+ );
59
74
  items.push(
60
75
  resolveDaytonaSandboxComputeItem({
61
76
  itemId: `daytona:${sandboxId}`,
@@ -0,0 +1,102 @@
1
+ export type PlaySandboxRuntimeLimits = {
2
+ timeoutSeconds: number;
3
+ memoryGiB: number;
4
+ cpu: number;
5
+ diskGiB: number;
6
+ };
7
+
8
+ export const STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS: PlaySandboxRuntimeLimits = {
9
+ timeoutSeconds: 30 * 60,
10
+ memoryGiB: 1,
11
+ cpu: 1,
12
+ diskGiB: 3,
13
+ };
14
+
15
+ // Product-wide ceilings. Organisation entitlements may lower these, but a
16
+ // customer-authored Play can never ask Daytona for an unbounded resource.
17
+ export const MAX_PLAY_SANDBOX_RUNTIME_LIMITS: PlaySandboxRuntimeLimits = {
18
+ timeoutSeconds: 4 * 60 * 60,
19
+ memoryGiB: 16,
20
+ cpu: 4,
21
+ diskGiB: 50,
22
+ };
23
+
24
+ export type PlaySandboxRuntimeDeclaration = {
25
+ timeout?: string;
26
+ memory?: string;
27
+ cpu?: number;
28
+ disk?: string;
29
+ };
30
+
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
+ function parseTimeout(value: string): number | null {
39
+ const match = /^(\d+)\s*([mh])$/i.exec(value.trim());
40
+ if (!match) return null;
41
+ const amount = Number(match[1]);
42
+ if (!Number.isSafeInteger(amount) || amount <= 0) return null;
43
+ return amount * (match[2].toLowerCase() === 'h' ? 3600 : 60);
44
+ }
45
+
46
+ export function resolvePlaySandboxRuntimeLimits(
47
+ declaration: PlaySandboxRuntimeDeclaration | null | undefined,
48
+ ): PlaySandboxRuntimeLimits {
49
+ 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
+ for (const key of ['memoryGiB', 'cpu', 'diskGiB'] as const) {
74
+ if (resolved[key] < base[key]) {
75
+ throw new Error(
76
+ `Requested runtime ${key}=${resolved[key]} is below the supported minimum ${base[key]}.`,
77
+ );
78
+ }
79
+ }
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
+ }
88
+ }
89
+ return resolved;
90
+ }
91
+
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
+ ],
101
+ );
102
+ }
@@ -229,6 +229,29 @@ function isHardBillingFailurePayload(
229
229
  );
230
230
  }
231
231
 
232
+ /**
233
+ * A normalized provider 402 is fatal only when the integration boundary has
234
+ * declared it account-level capacity. A raw 402 never reaches this function as
235
+ * proof by itself: providers use that status inconsistently.
236
+ */
237
+ function isProviderAccountCapacityFailurePayload(
238
+ payload: Record<string, unknown> | null,
239
+ ): payload is Record<string, unknown> {
240
+ if (!payload) return false;
241
+ const code = String(payload.code ?? payload.error_code ?? '').toUpperCase();
242
+ const category = String(
243
+ payload.error_category ?? payload.errorCategory ?? '',
244
+ ).toLowerCase();
245
+ const origin = String(
246
+ payload.failure_origin ?? payload.failureOrigin ?? '',
247
+ ).toLowerCase();
248
+ return (
249
+ code === 'PROVIDER_ACCOUNT_CAPACITY' &&
250
+ category === 'provider_account' &&
251
+ (origin === 'provider' || origin === 'provider_account')
252
+ );
253
+ }
254
+
232
255
  function normalizeHardBillingPayload(
233
256
  payload: Record<string, unknown>,
234
257
  ): Record<string, unknown> {
@@ -268,11 +291,19 @@ function formatHardBillingFailureMessage(input: {
268
291
  maxAttempts: number;
269
292
  }): string {
270
293
  const code = getStringField(input.billing, 'code');
294
+ const providerCapacity = isProviderAccountCapacityFailurePayload(
295
+ input.billing,
296
+ );
271
297
  const message =
272
298
  getStringField(input.billing, 'message') ??
273
299
  getStringField(input.billing, 'error') ??
274
- 'Deepline billing cap exceeded.';
275
- return `tool ${input.toolId} ${input.status} attempt ${input.attempt}/${input.maxAttempts}: Deepline billing cap exceeded. Run halted before marking remaining rows processed. ${code ? `code=${code}. ` : ''}${message}`;
300
+ (providerCapacity
301
+ ? 'Provider account capacity blocked execution.'
302
+ : 'Deepline billing cap exceeded.');
303
+ const headline = providerCapacity
304
+ ? 'Provider account capacity blocked execution.'
305
+ : 'Deepline billing cap exceeded.';
306
+ return `tool ${input.toolId} ${input.status} attempt ${input.attempt}/${input.maxAttempts}: ${headline} Run halted before marking remaining rows processed. ${code ? `code=${code}. ` : ''}${message}`;
276
307
  }
277
308
 
278
309
  function formatInsufficientCreditsMessage(input: {
@@ -434,8 +465,13 @@ export function normalizeToolHttpErrorMessage(input: {
434
465
  ? normalizeHardBillingPayload(billing)
435
466
  : isHardBillingFailurePayload(parsed)
436
467
  ? normalizeHardBillingPayload(parsed)
437
- : null;
468
+ : isProviderAccountCapacityFailurePayload(parsed)
469
+ ? parsed
470
+ : null;
438
471
  if (hardBillingPayload) {
472
+ const providerCapacity = isProviderAccountCapacityFailurePayload(
473
+ hardBillingPayload,
474
+ );
439
475
  return createToolHttpError(
440
476
  schemaVersion,
441
477
  formatHardBillingFailureMessage({
@@ -450,8 +486,15 @@ export function normalizeToolHttpErrorMessage(input: {
450
486
  'terminal',
451
487
  {
452
488
  ...publicOptions,
453
- origin: 'deepline',
454
- category: 'billing',
489
+ origin: providerCapacity ? 'provider' : 'deepline',
490
+ // `provider_account` is the integration-boundary category. The
491
+ // portable ToolExecutionError taxonomy canonically represents it as
492
+ // provider-owned authentication; the code retains the precise
493
+ // account-capacity reason.
494
+ category: providerCapacity ? 'authentication' : 'billing',
495
+ code: providerCapacity
496
+ ? 'PROVIDER_ACCOUNT_CAPACITY'
497
+ : publicOptions.code,
455
498
  retryable: false,
456
499
  },
457
500
  );
@@ -481,15 +524,19 @@ export function isHardBillingToolHttpError(error: unknown): boolean {
481
524
  if (
482
525
  error instanceof ToolHttpError &&
483
526
  (isInsufficientCreditsBilling(error.billing) ||
484
- isHardBillingFailurePayload(error.billing))
527
+ isHardBillingFailurePayload(error.billing) ||
528
+ isProviderAccountCapacityFailurePayload(error.billing))
485
529
  ) {
486
530
  return true;
487
531
  }
488
532
  return (
489
533
  error instanceof ToolExecutionError &&
490
- error.origin === 'deepline' &&
491
- error.category === 'billing' &&
492
- error.code !== 'BILLING_UNAVAILABLE'
534
+ ((error.origin === 'deepline' &&
535
+ error.category === 'billing' &&
536
+ error.code !== 'BILLING_UNAVAILABLE') ||
537
+ (error.origin === 'provider' &&
538
+ error.category === 'authentication' &&
539
+ error.code === 'PROVIDER_ACCOUNT_CAPACITY'))
493
540
  );
494
541
  }
495
542
 
@@ -29,6 +29,7 @@ import type {
29
29
  } from '../artifact-types';
30
30
  import { buildPlayContractCompatibility } from '../contracts';
31
31
  import type { ToolExecutionErrorSchemaVersion } from '../../tool-execution-error';
32
+ import type { PlaySandboxRuntimeDeclaration } from '../../play-runtime/sandbox-runtime-limits';
32
33
  import { validatePlaySourceFilesHaveNoInlineSecrets } from '../secret-guardrails';
33
34
  import { MAX_PLAY_BUNDLE_BYTES } from './limits';
34
35
 
@@ -126,6 +127,7 @@ export type BundledPlayFileSuccess = {
126
127
  filePath: string;
127
128
  playName: string | null;
128
129
  playDescription: string | null;
130
+ sandboxRuntimeDeclaration: PlaySandboxRuntimeDeclaration | null;
129
131
  compilerManifest?: PlayCompilerManifest;
130
132
  packagedFiles: PlayLocalFileReference[];
131
133
  unresolvedFileReferences: PlayLocalFileDiscoveryError[];
@@ -155,6 +157,7 @@ type SourceGraphAnalysis = {
155
157
  importPolicy: PlayImportPolicy;
156
158
  playName: string | null;
157
159
  playDescription: string | null;
160
+ sandboxRuntimeDeclaration: PlaySandboxRuntimeDeclaration | null;
158
161
  toolErrorSchemaVersion: ToolExecutionErrorSchemaVersion | null;
159
162
  importedPlayDependencies: ImportedPlayDependency[];
160
163
  };
@@ -505,6 +508,7 @@ type PlayMetadataExtractionContext = {
505
508
  type ExtractedPlayMetadata = {
506
509
  name: string | null;
507
510
  description: string | null;
511
+ sandboxRuntimeDeclaration: PlaySandboxRuntimeDeclaration | null;
508
512
  toolErrorSchemaVersion: ToolExecutionErrorSchemaVersion | null;
509
513
  toolErrorSchemaVersionUnknown: boolean;
510
514
  };
@@ -939,6 +943,48 @@ function toolErrorSchemaVersionFromOptions(
939
943
  return null;
940
944
  }
941
945
 
946
+ function sandboxRuntimeDeclarationFromOptions(
947
+ node: AstNode | null | undefined,
948
+ context: PlayMetadataExtractionContext,
949
+ ): PlaySandboxRuntimeDeclaration | null {
950
+ const directRuntime = resolveStaticProperty(node, 'runtime', context);
951
+ const bindings = resolveStaticProperty(node, 'bindings', context);
952
+ const bindingRuntime =
953
+ bindings.kind === 'found'
954
+ ? resolveStaticProperty(bindings.value, 'runtime', context)
955
+ : ({ kind: 'absent' } satisfies StaticPropertyResolution);
956
+ const runtime =
957
+ directRuntime.kind === 'found' ? directRuntime : bindingRuntime;
958
+ if (runtime.kind !== 'found') return null;
959
+
960
+ 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);
964
+ const declaration: PlaySandboxRuntimeDeclaration = {};
965
+ if (timeout.kind === 'found') {
966
+ const value = staticStringFromExpression(timeout.value, context);
967
+ if (value === null) return null;
968
+ declaration.timeout = value;
969
+ }
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);
982
+ if (value === null) return null;
983
+ declaration.disk = value;
984
+ }
985
+ return declaration;
986
+ }
987
+
942
988
  function stringPropertyFromObjectExpression(
943
989
  node: AstNode | null | undefined,
944
990
  propertyName: string,
@@ -1031,6 +1077,10 @@ function playMetadataFromDefinePlayCall(
1031
1077
  }
1032
1078
 
1033
1079
  const options = isObjectForm ? firstArg : (args[2] ?? null);
1080
+ const sandboxRuntimeDeclaration = sandboxRuntimeDeclarationFromOptions(
1081
+ options,
1082
+ context,
1083
+ );
1034
1084
  const toolErrorSchemaVersion = toolErrorSchemaVersionFromOptions(
1035
1085
  options,
1036
1086
  context,
@@ -1048,6 +1098,7 @@ function playMetadataFromDefinePlayCall(
1048
1098
  return {
1049
1099
  name,
1050
1100
  description,
1101
+ sandboxRuntimeDeclaration,
1051
1102
  toolErrorSchemaVersion: toolErrorSchemaVersion ?? null,
1052
1103
  toolErrorSchemaVersionUnknown,
1053
1104
  };
@@ -1545,6 +1596,8 @@ async function analyzeSourceGraph(
1545
1596
  }
1546
1597
  const playName = metadata?.name ?? null;
1547
1598
  const playDescription = metadata?.description ?? null;
1599
+ const sandboxRuntimeDeclaration =
1600
+ metadata?.sandboxRuntimeDeclaration ?? null;
1548
1601
 
1549
1602
  return {
1550
1603
  sourceCode,
@@ -1564,6 +1617,7 @@ async function analyzeSourceGraph(
1564
1617
  },
1565
1618
  playName,
1566
1619
  playDescription,
1620
+ sandboxRuntimeDeclaration,
1567
1621
  toolErrorSchemaVersion: metadata?.toolErrorSchemaVersion ?? null,
1568
1622
  importedPlayDependencies: [...importedPlayDependencies.values()].sort(
1569
1623
  (left, right) => left.filePath.localeCompare(right.filePath),
@@ -1824,6 +1878,7 @@ export async function bundlePlayFile(
1824
1878
  filePath: absolutePath,
1825
1879
  playName: analysis.playName,
1826
1880
  playDescription: analysis.playDescription,
1881
+ sandboxRuntimeDeclaration: analysis.sandboxRuntimeDeclaration,
1827
1882
  packagedFiles: discoveredFiles.files,
1828
1883
  unresolvedFileReferences: discoveredFiles.unresolved,
1829
1884
  importedPlayDependencies: analysis.importedPlayDependencies,
@@ -1898,6 +1953,7 @@ export async function bundlePlayFile(
1898
1953
  filePath: absolutePath,
1899
1954
  playName: analysis.playName,
1900
1955
  playDescription: analysis.playDescription,
1956
+ sandboxRuntimeDeclaration: analysis.sandboxRuntimeDeclaration,
1901
1957
  packagedFiles: discoveredFiles.files,
1902
1958
  unresolvedFileReferences: discoveredFiles.unresolved,
1903
1959
  importedPlayDependencies: analysis.importedPlayDependencies,
@@ -111,6 +111,12 @@ export type PlayRunContractSnapshot = {
111
111
  billingLimit?: {
112
112
  maxCreditsPerRun?: number | null;
113
113
  } | null;
114
+ runtimeLimit?: {
115
+ timeoutSeconds: number;
116
+ memoryGiB: number;
117
+ cpu: number;
118
+ diskGiB: number;
119
+ } | null;
114
120
  structuredDefinition?: unknown;
115
121
  artifactMetadata?: Record<string, unknown> | null;
116
122
  codeFormat?: 'function' | 'cjs_module' | 'esm_module' | null;