import { db } from '../db/index.js'; import { recordProgress } from './progress.js'; /** Record receipts in the report transaction, independently of the execution worker. */ export function recordAgentReceipt(operationId: string, state: string, at: string): void { // Host operations also serve installations that have no provisioning schema yet. if (!db().prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='native_provisioning'").get()) return; const job = db().prepare(`SELECT d.id, d.phase_key, COALESCE(n.operation_id,m.remove_operation_id) AS operation_id FROM native_provisioning n JOIN server_deployments d ON d.id=n.deployment_id LEFT JOIN ec_host_migrations m ON m.deployment_id=n.deployment_id WHERE (n.operation_id=? OR n.stage_operation_id=? OR m.remove_operation_id=?) AND d.server_deleted_at IS NULL AND d.state IN ('queued','waiting','running','awaiting-health','interrupted')`).get(operationId, operationId, operationId) as { id: string; phase_key: string | null; operation_id: string | null } | undefined; if (!job) return; const staging = job.operation_id !== operationId; const message = `${staging ? 'File staging' : 'Host agent command'} ${state} (${operationId})`; if (!staging && job.phase_key === 'container-action') { db().prepare('UPDATE server_deployments SET stage=? WHERE id=?').run(`Host agent command ${state}`, job.id); } recordProgress(job.id, { blocker: null, event: { kind: 'agent', message, details: { operationId, state, receivedAt: at } }, ...(['accepted', 'running', 'completed'].includes(state) ? { checkpoint: { key: `${staging ? 'staging' : 'agent'}-${state}`, label: message, at }, ...(!staging ? { command: { state: 'accepted' as const, message: 'Host agent acknowledged the command.', at } } : {}) } : {}) }); }