UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

432 lines (431 loc) 20.7 kB
import "./src-vebZIeLe.js"; import { t as stableStringify } from "./stable-stringify-C8X7niaI.js"; import { a as getNodeSqliteKysely, r as executeSqliteQuerySync } from "./kysely-sync-COmh4HWh.js"; import { D as tableHasColumn } from "./openclaw-state-db-cache-C7ljO0xP.js"; import { _t as coerceRequiredSqliteNumber, i as openOpenClawStateDatabase, s as runOpenClawStateWriteTransaction } from "./openclaw-state-db-BRTnL-D8.js"; import { c as upgradeClawInstallSchema, l as digestClawAgentConfig, n as deleteCachedClawInstallSchemaVersion, o as CLAW_INSTALL_RECORD_SCHEMA_VERSION, s as parseClawInstallRecordSchemaVersion, t as cacheClawInstallSchemaVersion } from "./provenance-runtime-read-DgsfNx12.js"; //#region src/claws/package-extension-provenance.ts const CLAW_PACKAGE_REF_SCHEMA_VERSION = "openclaw.clawPackageRef.v1"; function toPackageRefExtensionSqlParams(extension) { return { extension_id: extension?.id ?? null, extension_format: extension?.format ?? null, extension_detected_format: extension?.detectedFormat ?? null, extension_mapped_json: extension ? JSON.stringify(extension.mapped) : null, extension_unavailable_json: extension ? JSON.stringify(extension.unavailable) : null, extension_adapter_identity: extension?.adapterIdentity ?? null }; } function parsePackageRefExtension(row) { const values = [ row.extension_id, row.extension_format, row.extension_detected_format, row.extension_mapped_json, row.extension_unavailable_json, row.extension_adapter_identity ]; if (values.every((value) => value === null)) return; if (values.some((value) => value === null)) throw new Error(`Claw package reference ${row.package_kind}:${row.package_ref} has incomplete extension provenance.`); const formats = /* @__PURE__ */ new Set([ "openclaw", "claude", "codex", "cursor" ]); if (!formats.has(row.extension_format) || !formats.has(row.extension_detected_format)) throw new Error(`Claw package reference ${row.package_kind}:${row.package_ref} has unsupported extension format provenance.`); const mapped = JSON.parse(row.extension_mapped_json); const unavailable = JSON.parse(row.extension_unavailable_json); if (!Array.isArray(mapped) || !mapped.every((value) => typeof value === "string") || !Array.isArray(unavailable) || !unavailable.every((value) => typeof value === "string")) throw new Error(`Claw package reference ${row.package_kind}:${row.package_ref} has invalid extension inventory provenance.`); return { id: row.extension_id, format: row.extension_format, detectedFormat: row.extension_detected_format, mapped, unavailable, adapterIdentity: row.extension_adapter_identity }; } function rowToPackageRef(row) { const extension = parsePackageRefExtension(row); return { schemaVersion: CLAW_PACKAGE_REF_SCHEMA_VERSION, agentId: row.agent_id, clawName: row.claw_name, kind: row.package_kind, source: row.package_source, ref: row.package_ref, version: row.package_version, integrity: row.package_integrity, status: row.package_status, relationship: row.relationship, origin: row.origin, independentOwner: coerceRequiredSqliteNumber(row.independent_owner) === 1, ...extension ? { extension } : {}, installedAtMs: coerceRequiredSqliteNumber(row.installed_at_ms), updatedAtMs: coerceRequiredSqliteNumber(row.updated_at_ms) }; } //#endregion //#region src/claws/provenance-bootstrap.ts function selectClawBootstrapProvenanceColumns(db) { return `${tableHasColumn(db, "claw_installs", "bootstrap_source_path") ? "bootstrap_source_path" : "NULL AS bootstrap_source_path"}, ${tableHasColumn(db, "claw_installs", "bootstrap_content_digest") ? "bootstrap_content_digest" : "NULL AS bootstrap_content_digest"}`; } function clawBootstrapProvenanceFromRow(row) { return row.bootstrap_source_path && row.bootstrap_content_digest ? { bootstrap: { sourcePath: row.bootstrap_source_path, contentDigest: row.bootstrap_content_digest } } : {}; } //#endregion //#region src/claws/provenance-legacy-columns.ts function canSelect(db, table, projection) { try { db.prepare(`SELECT ${projection} FROM ${table} LIMIT 0`); return true; } catch { return false; } } /** * Read-only opens never run the additive column migration, so a same-version * database written before a column existed must still answer planning reads. * Absent columns project as SQL NULL, which the row parsers already treat as * "no recorded provenance". */ function legacySafeColumnProjection(db, table, columns) { const full = columns.join(", "); if (canSelect(db, table, full)) return full; return columns.map((column) => canSelect(db, table, column) ? column : `NULL AS ${column}`).join(", "); } //#endregion //#region src/claws/provenance.ts function rowToRecord(row) { return { schemaVersion: parseClawInstallRecordSchemaVersion(row.schema_version), claw: { kind: row.source_kind, name: row.claw_name, version: row.claw_version, packageRoot: row.package_root, manifestPath: row.manifest_path, integrityKind: row.integrity_kind, integrity: row.integrity, byteLength: coerceRequiredSqliteNumber(row.source_byte_length) }, manifestSchemaVersion: coerceRequiredSqliteNumber(row.manifest_schema_version), planIntegrity: row.plan_integrity, agentId: row.agent_id, workspace: row.workspace, agentConfigDigest: row.agent_config_digest, agentOwnedPaths: JSON.parse(row.agent_owned_paths_json), ...clawBootstrapProvenanceFromRow(row), status: row.status, addedAtMs: coerceRequiredSqliteNumber(row.added_at_ms), updatedAtMs: coerceRequiredSqliteNumber(row.updated_at_ms) }; } function agentOwnedPaths(plan) { return plan.actions.filter((action) => action.kind === "agent").map((action) => action.target); } function bootstrapProvenance(plan) { const action = plan.actions.find((candidate) => candidate.kind === "bootstrap"); const sourcePath = action?.details?.sourcePath; return action && typeof sourcePath === "string" && action.digest ? { sourcePath, contentDigest: action.digest } : void 0; } function clawInstallRecordMatchesPlan(record, plan) { const bootstrap = bootstrapProvenance(plan); return record.claw.kind === plan.claw.kind && record.claw.name === plan.claw.name && record.claw.version === plan.claw.version && record.claw.packageRoot === plan.claw.packageRoot && record.claw.manifestPath === plan.claw.manifestPath && record.claw.integrityKind === plan.claw.integrityKind && record.claw.integrity === plan.claw.integrity && record.claw.byteLength === plan.claw.byteLength && record.manifestSchemaVersion === plan.manifestSchemaVersion && record.planIntegrity === plan.planIntegrity && record.workspace === plan.agent.workspace && record.agentConfigDigest === digestClawAgentConfig(plan.agent.config) && stableStringify(record.agentOwnedPaths) === stableStringify(agentOwnedPaths(plan)) && record.bootstrap?.sourcePath === bootstrap?.sourcePath && record.bootstrap?.contentDigest === bootstrap?.contentDigest; } function selectClawInstallRow(db, agentId) { const bootstrapColumns = selectClawBootstrapProvenanceColumns(db); return db.prepare(`SELECT agent_id, schema_version, source_kind, claw_name, claw_version, package_root, manifest_path, integrity_kind, integrity, source_byte_length, manifest_schema_version, plan_integrity, workspace, agent_config_digest, agent_owned_paths_json, ${bootstrapColumns}, status, added_at_ms, updated_at_ms FROM claw_installs WHERE agent_id = ?`).get(agentId); } function readClawInstallRecordFromDatabase(db, agentId) { const row = selectClawInstallRow(db, agentId); return row ? rowToRecord(row) : void 0; } function readClawInstallRecord(agentId, options = {}) { const row = selectClawInstallRow(openOpenClawStateDatabase(options).db, agentId); return row ? rowToRecord(row) : void 0; } function persistClawInstallRecord(plan, options = {}) { const nowMs = options.nowMs ?? Date.now(); const status = options.status ?? "complete"; const agentConfigDigest = digestClawAgentConfig(plan.agent.config); const ownedPaths = agentOwnedPaths(plan); const bootstrap = bootstrapProvenance(plan); const persistedRecord = runOpenClawStateWriteTransaction(({ db }) => { const existing = selectClawInstallRow(db, plan.agent.finalId); if (existing) { const record = rowToRecord(existing); const expectedPlan = options.expectedExistingPlan ?? plan; if (existing.status !== "complete" && clawInstallRecordMatchesPlan(record, expectedPlan)) { if (record.schemaVersion !== "openclaw.clawInstallRecord.v2") { if (options.deferLegacyPlanUpgrade) return record; return upgradeClawInstallSchema(db, plan.agent.finalId, record, options.expectedExistingRecord, { planIntegrity: plan.planIntegrity, agentConfigDigest }); } return record; } throw new Error(`Claw install record for agent ${JSON.stringify(plan.agent.finalId)} already exists.`); } executeSqliteQuerySync(db, getNodeSqliteKysely(db).insertInto("claw_installs").values({ agent_id: plan.agent.finalId, schema_version: CLAW_INSTALL_RECORD_SCHEMA_VERSION, source_kind: plan.claw.kind, claw_name: plan.claw.name, claw_version: plan.claw.version, package_root: plan.claw.packageRoot, manifest_path: plan.claw.manifestPath, integrity_kind: plan.claw.integrityKind, integrity: plan.claw.integrity, source_byte_length: plan.claw.byteLength, manifest_schema_version: plan.manifestSchemaVersion, plan_integrity: plan.planIntegrity, workspace: plan.agent.workspace, agent_config_digest: agentConfigDigest, agent_owned_paths_json: JSON.stringify(ownedPaths), bootstrap_source_path: bootstrap?.sourcePath ?? null, bootstrap_content_digest: bootstrap?.contentDigest ?? null, status, added_at_ms: nowMs, updated_at_ms: nowMs })); return { schemaVersion: CLAW_INSTALL_RECORD_SCHEMA_VERSION, claw: plan.claw, manifestSchemaVersion: plan.manifestSchemaVersion, planIntegrity: plan.planIntegrity, agentId: plan.agent.finalId, workspace: plan.agent.workspace, agentConfigDigest, agentOwnedPaths: ownedPaths, ...bootstrap ? { bootstrap } : {}, status, addedAtMs: nowMs, updatedAtMs: nowMs }; }, options); cacheClawInstallSchemaVersion(plan.agent.finalId, persistedRecord.schemaVersion, persistedRecord.agentConfigDigest, options); return persistedRecord; } function updateClawInstallRecordStatus(agentId, status, options = {}) { runOpenClawStateWriteTransaction(({ db }) => { const expectedStatuses = options.expectedStatuses ?? []; let query = getNodeSqliteKysely(db).updateTable("claw_installs").set({ status, updated_at_ms: options.nowMs ?? Date.now() }).where("agent_id", "=", agentId); if (expectedStatuses.length > 0) query = query.where("status", "in", expectedStatuses); if (executeSqliteQuerySync(db, query).numAffectedRows !== 1n) throw new Error(`Claw install record for agent ${JSON.stringify(agentId)} did not match the expected phase.`); }, options); } function deleteClawInstallRecord(agentId, options = {}) { runOpenClawStateWriteTransaction(({ db }) => { const expectedStatuses = options.expectedStatuses ?? []; let query = getNodeSqliteKysely(db).deleteFrom("claw_installs").where("agent_id", "=", agentId); if (expectedStatuses.length > 0) query = query.where("status", "in", expectedStatuses); if (executeSqliteQuerySync(db, query).numAffectedRows !== 1n) throw new Error(`Claw install record for agent ${JSON.stringify(agentId)} did not match the expected phase.`); }, options); deleteCachedClawInstallSchemaVersion(agentId, options); } function readClawInstallRecords(options = {}) { const database = openOpenClawStateDatabase(options); const bootstrapColumns = selectClawBootstrapProvenanceColumns(database.db); return database.db.prepare(`SELECT schema_version, source_kind, claw_name, claw_version, package_root, manifest_path, integrity_kind, integrity, source_byte_length, manifest_schema_version, plan_integrity, agent_id, workspace, agent_config_digest, agent_owned_paths_json, ${bootstrapColumns}, status, added_at_ms, updated_at_ms FROM claw_installs ORDER BY agent_id`).all().map(rowToRecord); } function updateClawInstallRecord(plan, options = {}) { const current = readClawInstallRecord(plan.agent.finalId, options); if (!current) throw new Error(`No Claw install record exists for agent ${JSON.stringify(plan.agent.finalId)}.`); const updatedAtMs = options.nowMs ?? Date.now(); const status = options.status ?? "complete"; const agentConfigDigest = digestClawAgentConfig(plan.agent.config); const ownedAgentPaths = plan.actions.filter((action) => action.kind === "agent").map((action) => action.target); const bootstrap = bootstrapProvenance(plan) ?? current.bootstrap; runOpenClawStateWriteTransaction(({ db }) => { if (executeSqliteQuerySync(db, getNodeSqliteKysely(db).updateTable("claw_installs").set({ schema_version: "openclaw.clawInstallRecord.v2", source_kind: plan.claw.kind, claw_name: plan.claw.name, claw_version: plan.claw.version, package_root: plan.claw.packageRoot, manifest_path: plan.claw.manifestPath, integrity_kind: plan.claw.integrityKind, integrity: plan.claw.integrity, source_byte_length: plan.claw.byteLength, manifest_schema_version: plan.manifestSchemaVersion, plan_integrity: plan.planIntegrity, workspace: plan.agent.workspace, agent_config_digest: agentConfigDigest, agent_owned_paths_json: JSON.stringify(ownedAgentPaths), bootstrap_source_path: bootstrap?.sourcePath ?? null, bootstrap_content_digest: bootstrap?.contentDigest ?? null, status, updated_at_ms: updatedAtMs }).where("agent_id", "=", plan.agent.finalId).where("claw_version", "=", options.expectedClaw?.version ?? current.claw.version).where("integrity", "=", options.expectedClaw?.integrity ?? current.claw.integrity)).numAffectedRows !== 1n) throw new Error(`Claw install record changed for agent ${JSON.stringify(plan.agent.finalId)}.`); }, options); const record = { schemaVersion: CLAW_INSTALL_RECORD_SCHEMA_VERSION, claw: plan.claw, manifestSchemaVersion: plan.manifestSchemaVersion, planIntegrity: plan.planIntegrity, agentId: plan.agent.finalId, workspace: plan.agent.workspace, agentConfigDigest, agentOwnedPaths: ownedAgentPaths, ...bootstrap ? { bootstrap } : {}, status, addedAtMs: current.addedAtMs, updatedAtMs }; cacheClawInstallSchemaVersion(plan.agent.finalId, record.schemaVersion, record.agentConfigDigest, options); return record; } function persistClawPackageRef(plan, pkg, options = {}) { const nowMs = options.nowMs ?? Date.now(); let record = { schemaVersion: CLAW_PACKAGE_REF_SCHEMA_VERSION, agentId: plan.agent.finalId, clawName: plan.claw.name, kind: pkg.kind, source: pkg.source, ref: pkg.ref, version: pkg.version, integrity: pkg.integrity, status: options.status ?? "complete", relationship: options.relationship ?? (pkg.kind === "skill" ? "managed" : "referenced"), origin: options.origin ?? "claw-introduced", independentOwner: options.independentOwner ?? false, ...pkg.extension ? { extension: pkg.extension } : {}, installedAtMs: nowMs, updatedAtMs: nowMs }; runOpenClawStateWriteTransaction(({ db }) => { const existing = db.prepare(`SELECT schema_version, agent_id, claw_name, package_kind, package_source, package_ref, package_version, package_integrity, package_status, relationship, origin, independent_owner, extension_id, extension_format, extension_detected_format, extension_mapped_json, extension_unavailable_json, extension_adapter_identity, installed_at_ms, updated_at_ms FROM claw_package_refs WHERE agent_id = @agent_id AND package_kind = @package_kind AND package_source = @package_source AND package_ref = @package_ref AND package_version = @package_version`).get({ agent_id: record.agentId, package_kind: record.kind, package_source: record.source, package_ref: record.ref, package_version: record.version }); if (existing) { const previous = rowToPackageRef(existing); if (previous.integrity !== record.integrity) throw new Error(`Claw package reference ${record.kind}:${record.ref}@${record.version} changed integrity from ${previous.integrity} to ${record.integrity}.`); record = { ...record, relationship: previous.relationship, origin: previous.origin === "claw-introduced" ? "claw-introduced" : record.origin, independentOwner: previous.independentOwner || record.independentOwner, installedAtMs: previous.installedAtMs }; executeSqliteQuerySync(db, getNodeSqliteKysely(db).updateTable("claw_package_refs").set({ schema_version: record.schemaVersion, claw_name: record.clawName, package_status: record.status, relationship: record.relationship, origin: record.origin, independent_owner: record.independentOwner ? 1 : 0, ...toPackageRefExtensionSqlParams(record.extension), updated_at_ms: record.updatedAtMs }).where("agent_id", "=", record.agentId).where("package_kind", "=", record.kind).where("package_source", "=", record.source).where("package_ref", "=", record.ref).where("package_version", "=", record.version).where("package_integrity", "=", record.integrity)); return; } executeSqliteQuerySync(db, getNodeSqliteKysely(db).insertInto("claw_package_refs").values({ agent_id: record.agentId, package_kind: record.kind, package_source: record.source, package_ref: record.ref, package_version: record.version, package_integrity: record.integrity, schema_version: record.schemaVersion, claw_name: record.clawName, package_status: record.status, relationship: record.relationship, origin: record.origin, independent_owner: record.independentOwner ? 1 : 0, ...toPackageRefExtensionSqlParams(record.extension), installed_at_ms: record.installedAtMs, updated_at_ms: record.updatedAtMs })); }, options); return record; } function updateClawPackageRefStatus(ref, status, options = {}) { const nowMs = options.nowMs ?? Date.now(); runOpenClawStateWriteTransaction(({ db }) => { executeSqliteQuerySync(db, getNodeSqliteKysely(db).updateTable("claw_package_refs").set({ package_status: status, updated_at_ms: nowMs }).where("agent_id", "=", ref.agentId).where("package_kind", "=", ref.kind).where("package_source", "=", ref.source).where("package_ref", "=", ref.ref).where("package_version", "=", ref.version).where("package_integrity", "=", ref.integrity)); }, options); return { ...ref, status, updatedAtMs: nowMs }; } function readClawPackageRefs(options = {}) { const database = openOpenClawStateDatabase(options); if (options.readOnly && !database.db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'claw_package_refs'").get()) return []; const conditions = []; const params = {}; for (const [column, value] of [ ["agent_id", options.agentId], ["package_kind", options.kind], ["package_source", options.source], ["package_ref", options.ref], ["package_version", options.version], ["package_integrity", options.integrity], ["package_status", options.status] ]) if (value !== void 0) { conditions.push(`${column} = @${column}`); params[column] = value; } const where = conditions.length > 0 ? ` WHERE ${conditions.join(" AND ")}` : ""; const extensionColumns = legacySafeColumnProjection(database.db, "claw_package_refs", [ "extension_id", "extension_format", "extension_detected_format", "extension_mapped_json", "extension_unavailable_json", "extension_adapter_identity" ]); return database.db.prepare(`SELECT schema_version, agent_id, claw_name, package_kind, package_source, package_ref, package_version, package_integrity, package_status, relationship, origin, independent_owner, ${extensionColumns}, installed_at_ms, updated_at_ms FROM claw_package_refs${where} ORDER BY agent_id, package_kind, package_ref`).all(params).map(rowToPackageRef); } //#endregion export { readClawInstallRecord as a, readClawPackageRefs as c, updateClawPackageRefStatus as d, CLAW_PACKAGE_REF_SCHEMA_VERSION as f, persistClawPackageRef as i, updateClawInstallRecord as l, deleteClawInstallRecord as n, readClawInstallRecordFromDatabase as o, toPackageRefExtensionSqlParams as p, persistClawInstallRecord as r, readClawInstallRecords as s, clawInstallRecordMatchesPlan as t, updateClawInstallRecordStatus as u };