import { resolveDaytonaSandboxComputeItem, resolveModalSandboxComputeItem, type ComputeBillingItem, } from './worker-api-types'; function finiteNonNegative(value: unknown): number | null { return typeof value === 'number' && Number.isFinite(value) ? Math.max(0, value) : null; } /** Project every provider-owned sandbox into a distinct internal compute item. */ export function resolveRuntimeSandboxComputeItems(input: { resources: readonly unknown[]; endedAt: number; }): ComputeBillingItem[] { const daytonaResources: unknown[] = []; const modalResources = new Map< string, { startedAt: number; endedAt: number | null; physicalCpuCores: number | null; memoryGiB: number | null; maxBillingDurationSeconds: number | null; } >(); for (const resource of input.resources) { if (!resource || typeof resource !== 'object' || Array.isArray(resource)) { continue; } const value = resource as Record; if (value.kind === 'daytona_sandbox') { daytonaResources.push(resource); continue; } if (value.kind !== 'modal_sandbox') continue; const sandboxId = typeof value.sandboxId === 'string' ? value.sandboxId.trim() : ''; const startedAt = finiteNonNegative(value.billingStartedAt); if (!sandboxId || startedAt === null) continue; const endedAt = finiteNonNegative(value.billingEndedAt); const existing = modalResources.get(sandboxId); modalResources.set(sandboxId, { startedAt: Math.min(existing?.startedAt ?? startedAt, startedAt), endedAt: endedAt === null ? (existing?.endedAt ?? null) : Math.max(existing?.endedAt ?? endedAt, endedAt), physicalCpuCores: finiteNonNegative(value.cpu) ?? existing?.physicalCpuCores ?? null, memoryGiB: finiteNonNegative(value.memoryGiB) ?? existing?.memoryGiB ?? null, maxBillingDurationSeconds: finiteNonNegative(value.maxBillingDurationSeconds) ?? existing?.maxBillingDurationSeconds ?? null, }); } const items = resolveDaytonaRuntimeComputeItems({ resources: daytonaResources, endedAt: input.endedAt, }); for (const [sandboxId, resource] of modalResources) { const observedEndedAt = resource.endedAt ?? input.endedAt; const endedAt = resource.maxBillingDurationSeconds === null ? observedEndedAt : Math.min( observedEndedAt, resource.startedAt + resource.maxBillingDurationSeconds * 1_000, ); items.push( resolveModalSandboxComputeItem({ itemId: `modal:${sandboxId}`, wallTimeSeconds: Math.max(0, endedAt - resource.startedAt) / 1_000, physicalCpuCores: resource.physicalCpuCores ?? 0.5, memoryGiB: resource.memoryGiB ?? 1, sandboxId, startedAt: resource.startedAt, endedAt, }), ); } return items; } /** * A provider create may finish after the original run has terminalized. Those * resources cannot be appended to the run's already-finalized billing * session, so the scheduler settles each through a stable adjustment session. */ export function resolveLateRuntimeSandboxComputeItems(input: { resources: readonly unknown[]; endedAt: number; }): ComputeBillingItem[] { return resolveRuntimeSandboxComputeItems({ resources: input.resources.filter( (resource) => Boolean(resource) && typeof resource === 'object' && !Array.isArray(resource) && (resource as Record).lateAcquired === true, ), endedAt: input.endedAt, }); } /** Project executor resource facts into one idempotent item per real sandbox. */ export function resolveDaytonaRuntimeComputeItems(input: { resources: readonly unknown[]; endedAt: number; }): ComputeBillingItem[] { const resourcesBySandboxId = new Map< string, { startedAt: number; endedAt: number | null; cpu: number | null; memoryGiB: number | null; diskGiB: number | null; maxBillingDurationSeconds: number | null; } >(); const items: ComputeBillingItem[] = []; for (const resource of input.resources) { if (!resource || typeof resource !== 'object' || Array.isArray(resource)) { continue; } const value = resource as Record; const sandboxId = typeof value.sandboxId === 'string' ? value.sandboxId.trim() : ''; if (value.kind !== 'daytona_sandbox' || !sandboxId) { continue; } const billingStartedAt = finiteNonNegative(value.billingStartedAt); if (billingStartedAt === null) continue; const billingEndedAt = finiteNonNegative(value.billingEndedAt); const maxBillingDurationSeconds = finiteNonNegative( value.maxBillingDurationSeconds, ); const existing = resourcesBySandboxId.get(sandboxId); resourcesBySandboxId.set(sandboxId, { startedAt: Math.min( existing?.startedAt ?? billingStartedAt, billingStartedAt, ), endedAt: billingEndedAt === null ? (existing?.endedAt ?? null) : Math.max(existing?.endedAt ?? billingEndedAt, billingEndedAt), cpu: finiteNonNegative(value.cpu) ?? existing?.cpu ?? null, memoryGiB: finiteNonNegative(value.memoryGiB) ?? existing?.memoryGiB ?? null, diskGiB: finiteNonNegative(value.diskGiB) ?? existing?.diskGiB ?? null, maxBillingDurationSeconds: maxBillingDurationSeconds ?? existing?.maxBillingDurationSeconds ?? null, }); } for (const [sandboxId, resource] of resourcesBySandboxId) { const observedEndedAt = resource.endedAt ?? input.endedAt; const endedAt = resource.maxBillingDurationSeconds === null ? observedEndedAt : Math.min( observedEndedAt, resource.startedAt + resource.maxBillingDurationSeconds * 1_000, ); items.push( resolveDaytonaSandboxComputeItem({ itemId: `daytona:${sandboxId}`, source: 'daytona', wallTimeSeconds: Math.max(0, endedAt - resource.startedAt) / 1_000, cpu: resource.cpu ?? 1, memoryGiB: resource.memoryGiB ?? 1, diskGiB: resource.diskGiB ?? 0, sandboxId, startedAt: resource.startedAt, endedAt, }), ); } return items; }