UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

186 lines (185 loc) • 8.3 kB
import { i as getNodeSqliteKysely, n as executeSqliteQuerySync, r as executeSqliteQueryTakeFirstSync } from "./kysely-sync-BMSZn9yz.mjs"; import { i as requestSqliteWorkerOperationAdmission, r as deferSqliteWorkerCommitReceipt } from "./sqlite-worker-operation-admission-DqFz0WIG.mjs"; import { l as runOpenClawStateWriteTransaction } from "./openclaw-state-db-VZFEEleX.mjs"; //#region src/gateway/managed-image-record-store.kernel.ts const MANAGED_IMAGE_RECORD_COLUMNS = [ "attachment_id", "session_key", "agent_id", "message_id", "created_at", "updated_at", "retention_class", "alt", "original_media_root", "original_media_id", "original_media_subdir", "original_content_type", "original_width", "original_height", "original_size_bytes", "original_filename", "cleanup_pending" ]; function managedImageRecordToRow(record) { return { attachment_id: record.attachmentId, session_key: record.sessionKey, agent_id: record.agentId ?? null, message_id: record.messageId, created_at: record.createdAt, updated_at: record.updatedAt ?? null, retention_class: record.retentionClass ?? null, alt: record.alt, original_media_root: record.original.mediaRoot, original_media_id: record.original.mediaId, original_media_subdir: record.original.mediaSubdir, original_content_type: record.original.contentType, original_width: record.original.width, original_height: record.original.height, original_size_bytes: record.original.sizeBytes, original_filename: record.original.filename, record_json: JSON.stringify(record) }; } function managedImageRecordFromRow(row) { return { attachmentId: row.attachment_id, sessionKey: row.session_key, ...row.agent_id ? { agentId: row.agent_id } : {}, messageId: row.message_id, createdAt: row.created_at, ...row.updated_at ? { updatedAt: row.updated_at } : {}, ...row.retention_class === "history" || row.retention_class === "transient" ? { retentionClass: row.retention_class } : {}, alt: row.alt, original: { mediaRoot: row.original_media_root, mediaId: row.original_media_id, mediaSubdir: row.original_media_subdir, contentType: row.original_content_type, width: row.original_width, height: row.original_height, sizeBytes: row.original_size_bytes, filename: row.original_filename } }; } function managedImageRecordsEqual(left, right) { return JSON.stringify(left) === JSON.stringify(right); } function readManagedImageRecordInDatabase(db, attachmentId) { const row = executeSqliteQueryTakeFirstSync(db, getNodeSqliteKysely(db).selectFrom("managed_outgoing_image_records").select(MANAGED_IMAGE_RECORD_COLUMNS).where("attachment_id", "=", attachmentId).where("cleanup_pending", "=", 0)); return row ? managedImageRecordFromRow(row) : null; } function listManagedImageRecordEntriesInDatabase(db, sessionKey) { let query = getNodeSqliteKysely(db).selectFrom("managed_outgoing_image_records").select(MANAGED_IMAGE_RECORD_COLUMNS); if (sessionKey) query = query.where("session_key", "=", sessionKey); return executeSqliteQuerySync(db, query.orderBy("created_at", "desc").orderBy("attachment_id", "asc")).rows.map((row) => ({ record: managedImageRecordFromRow(row), cleanupPending: row.cleanup_pending === 1 })); } function listManagedImageOriginalMediaIdsInDatabase(db) { return executeSqliteQuerySync(db, getNodeSqliteKysely(db).selectFrom("managed_outgoing_image_records").select([ "original_media_id", "original_width", "original_height", "original_size_bytes", "cleanup_pending" ]).orderBy("created_at", "desc").orderBy("attachment_id", "asc")).rows.map((row) => row.original_media_id); } function insertManagedImageRecordInDatabase(db, record) { executeSqliteQuerySync(db, getNodeSqliteKysely(db).insertInto("managed_outgoing_image_records").values(managedImageRecordToRow(record))); return true; } /** Promote a transient record atomically so concurrent message commits cannot lose state. */ function attachManagedImageRecordInDatabase(db, params) { const stateDb = getNodeSqliteKysely(db); const row = executeSqliteQueryTakeFirstSync(db, stateDb.selectFrom("managed_outgoing_image_records").select(MANAGED_IMAGE_RECORD_COLUMNS).where("attachment_id", "=", params.attachmentId).where("session_key", "=", params.sessionKey)); if (!row) return false; if (row.cleanup_pending === 1) return false; const current = managedImageRecordFromRow(row); if (current.messageId === params.messageId && current.retentionClass === "history") return true; const nextRow = managedImageRecordToRow({ ...current, messageId: params.messageId, retentionClass: "history", updatedAt: params.updatedAt }); executeSqliteQuerySync(db, stateDb.updateTable("managed_outgoing_image_records").set({ message_id: nextRow.message_id, retention_class: nextRow.retention_class, updated_at: nextRow.updated_at, record_json: nextRow.record_json }).where("attachment_id", "=", params.attachmentId)); return true; } /** Claim only the exact row cleanup planned against; concurrent updates win. */ function claimManagedImageRecordCleanupInDatabase(db, planned) { const stateDb = getNodeSqliteKysely(db); const row = executeSqliteQueryTakeFirstSync(db, stateDb.selectFrom("managed_outgoing_image_records").select(MANAGED_IMAGE_RECORD_COLUMNS).where("attachment_id", "=", planned.attachmentId)); if (!row || row.cleanup_pending === 1 || !managedImageRecordsEqual(managedImageRecordFromRow(row), planned)) return false; executeSqliteQuerySync(db, stateDb.updateTable("managed_outgoing_image_records").set({ cleanup_pending: 1 }).where("attachment_id", "=", planned.attachmentId)); return true; } /** Delete a durably claimed row only after its attachment file is gone. */ function deleteClaimedManagedImageRecordInDatabase(db, planned) { const stateDb = getNodeSqliteKysely(db); const row = executeSqliteQueryTakeFirstSync(db, stateDb.selectFrom("managed_outgoing_image_records").select(MANAGED_IMAGE_RECORD_COLUMNS).where("attachment_id", "=", planned.attachmentId)); if (!row || row.cleanup_pending !== 1 || !managedImageRecordsEqual(managedImageRecordFromRow(row), planned)) return false; executeSqliteQuerySync(db, stateDb.deleteFrom("managed_outgoing_image_records").where("attachment_id", "=", planned.attachmentId)); return true; } function executeManagedImageRecordMutation(command, database) { return runOpenClawStateWriteTransaction(({ db }) => { requestSqliteWorkerOperationAdmission({ stage: "transaction", facts: void 0 }); let result; switch (command.type) { case "managedImages.insert": result = insertManagedImageRecordInDatabase(db, command.input); break; case "managedImages.attach": result = attachManagedImageRecordInDatabase(db, command.input); break; case "managedImages.claimCleanup": result = claimManagedImageRecordCleanupInDatabase(db, command.input); break; case "managedImages.deleteClaimed": result = deleteClaimedManagedImageRecordInDatabase(db, command.input); } const receipt = { type: command.type, result }; requestSqliteWorkerOperationAdmission({ stage: "commit", facts: receipt }); deferSqliteWorkerCommitReceipt(db, receipt); return result; }, { database }, { operationLabel: command.type }); } function isManagedImageRecordCommand(command) { switch (command.type) { case "managedImages.insert": case "managedImages.attach": case "managedImages.claimCleanup": case "managedImages.deleteClaimed": case "managedImages.read": case "managedImages.entries": case "managedImages.originalMediaIds": return true; default: return false; } } function executeManagedImageRecordCommand(command, database) { switch (command.type) { case "managedImages.read": return readManagedImageRecordInDatabase(database.db, command.input.attachmentId); case "managedImages.entries": return listManagedImageRecordEntriesInDatabase(database.db, command.input.sessionKey); case "managedImages.originalMediaIds": return listManagedImageOriginalMediaIdsInDatabase(database.db); default: return executeManagedImageRecordMutation(command, database); } } //#endregion export { managedImageRecordsEqual as a, managedImageRecordToRow as i, isManagedImageRecordCommand as n, managedImageRecordFromRow as r, executeManagedImageRecordCommand as t };