export type PlaySandboxRuntimeLimits = { timeoutSeconds: number; memoryGiB: number; cpu: number; diskGiB: number; }; export type PlaySandboxSize = 'standard'; export const PLAY_SANDBOX_SIZE_LIMITS = { standard: { memoryGiB: 1, cpu: 1, diskGiB: 3, }, } as const satisfies Record< PlaySandboxSize, Omit >; export const STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS: PlaySandboxRuntimeLimits = { timeoutSeconds: 30 * 60, ...PLAY_SANDBOX_SIZE_LIMITS.standard, }; // Product-wide ceilings. Organisation entitlements may lower these, but a // customer-authored Play can never ask Daytona for an unbounded resource. export const MAX_PLAY_SANDBOX_RUNTIME_LIMITS: PlaySandboxRuntimeLimits = { timeoutSeconds: 4 * 60 * 60, memoryGiB: STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS.memoryGiB, cpu: STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS.cpu, diskGiB: STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS.diskGiB, }; export type PlaySandboxRuntimeDeclaration = { timeout?: string; size?: PlaySandboxSize; }; function parseTimeout(value: string): number | null { const match = /^(\d+)\s*([mh])$/i.exec(value.trim()); if (!match) return null; const amount = Number(match[1]); if (!Number.isSafeInteger(amount) || amount <= 0) return null; return amount * (match[2].toLowerCase() === 'h' ? 3600 : 60); } export function validatePlaySandboxRuntimeLimits( value: PlaySandboxRuntimeLimits, ): PlaySandboxRuntimeLimits { const base = STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS; for (const key of ['memoryGiB', 'cpu', 'diskGiB'] as const) { if (value[key] !== base[key]) { throw new Error( `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).`, ); } } if ( !Number.isSafeInteger(value.timeoutSeconds) || value.timeoutSeconds <= 0 ) { throw new Error( 'Invalid runtime sandbox timeout. Use timeout like "90m" or "2h".', ); } if (value.timeoutSeconds > MAX_PLAY_SANDBOX_RUNTIME_LIMITS.timeoutSeconds) { throw new Error( `Requested runtime timeoutSeconds=${value.timeoutSeconds} exceeds this organisation's maximum ${MAX_PLAY_SANDBOX_RUNTIME_LIMITS.timeoutSeconds}.`, ); } return { ...value }; } export function resolvePlaySandboxRuntimeLimits( declaration: PlaySandboxRuntimeDeclaration | null | undefined, ): PlaySandboxRuntimeLimits { const base = STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS; if (!declaration) return { ...base }; const unsupportedProperties = Object.keys(declaration).filter( (key) => key !== 'timeout' && key !== 'size', ); if (unsupportedProperties.length > 0) { throw new Error( `Unsupported runtime sandbox option${unsupportedProperties.length === 1 ? '' : 's'}: ${unsupportedProperties.join(', ')}. ` + 'Use runtime.size with a Deepline prebuilt sandbox. Supported sizes: "standard".', ); } if (declaration.size !== undefined && declaration.size !== 'standard') { throw new Error( `Unsupported runtime sandbox size "${String(declaration.size)}". Supported sizes: "standard".`, ); } const timeoutSeconds = declaration.timeout ? parseTimeout(declaration.timeout) : base.timeoutSeconds; if (timeoutSeconds === null) { throw new Error( 'Invalid runtime sandbox timeout. Use timeout like "90m" or "2h".', ); } const size = declaration.size ?? 'standard'; return validatePlaySandboxRuntimeLimits({ timeoutSeconds, ...PLAY_SANDBOX_SIZE_LIMITS[size], }); }