openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
182 lines (181 loc) • 7.75 kB
JavaScript
import { a as getNodeSqliteKysely, i as executeSqliteQueryTakeFirstSync, r as executeSqliteQuerySync } from "./kysely-sync-COmh4HWh.js";
import { s as resolveOpenClawStateSqlitePath } from "./openclaw-state-db-schema-version-c1ZL6JGz.js";
import { _t as coerceRequiredSqliteNumber, i as openOpenClawStateDatabase, s as runOpenClawStateWriteTransaction } from "./openclaw-state-db-BRTnL-D8.js";
import { g as parseOfficialExternalPluginCatalogTimestamp, l as isOfficialExternalPluginCatalogSequence, t as HostedCatalogSignedFeedMonotonicityError } from "./official-external-plugin-catalog-Dzu7dwBN.js";
import { existsSync } from "node:fs";
import { createHash } from "node:crypto";
//#region src/plugins/official-external-plugin-catalog-snapshot-store.ts
/** Persists hosted official external plugin catalog snapshots in OpenClaw state. */
function resolveStoreEnv(options) {
if (!options.stateDir) return options.env;
return {
...options.env ?? process.env,
OPENCLAW_STATE_DIR: options.stateDir
};
}
function resolveStateDatabaseOptions(options) {
const env = resolveStoreEnv(options);
return {
...env ? { env } : {},
...options.stateDatabasePath ? { path: options.stateDatabasePath } : {}
};
}
function resolveStateDatabasePath(options) {
if (options.stateDatabasePath) return options.stateDatabasePath;
return resolveOpenClawStateSqlitePath(resolveStoreEnv(options) ?? process.env);
}
function rowToTrustState(row) {
if (row.trust_mode !== "signed" || !row.trust_key_id || row.trust_signature_count === null || row.trust_threshold === null || !row.trust_verified_at) return;
return {
mode: "signed",
signedBy: row.trust_key_id,
signatureCount: coerceRequiredSqliteNumber(row.trust_signature_count),
threshold: coerceRequiredSqliteNumber(row.trust_threshold),
verifiedAt: row.trust_verified_at
};
}
function decodeBase64Payload(payload) {
const normalized = payload.replace(/-/g, "+").replace(/_/g, "/");
return Buffer.from(normalized, "base64").toString("utf8");
}
function readMonotonicStateFromBody(body) {
try {
const document = JSON.parse(body);
const payload = typeof document.payload === "string" ? decodeBase64Payload(document.payload) : body;
const feed = typeof document.payload === "string" ? JSON.parse(payload) : document;
if (!isOfficialExternalPluginCatalogSequence(feed.sequence)) return;
if (typeof feed.generatedAt !== "string" || parseOfficialExternalPluginCatalogTimestamp(feed.generatedAt) === void 0) return {
sequence: feed.sequence,
payloadSha256: createHash("sha256").update(payload).digest("hex")
};
return {
sequence: feed.sequence,
generatedAt: feed.generatedAt,
payloadSha256: createHash("sha256").update(payload).digest("hex")
};
} catch {
return;
}
}
function isMonotonicRollback(params) {
if (params.candidate.sequence < params.current.sequence) return true;
if (params.candidate.sequence > params.current.sequence) return false;
if (params.candidate.generatedAt === void 0 || params.current.generatedAt === void 0) return false;
return Date.parse(params.candidate.generatedAt) < Date.parse(params.current.generatedAt);
}
function assertSignedSnapshotWriteIsMonotonic(params) {
if (params.candidate?.mode !== "signed-feed" || params.current?.trust_mode !== "signed") return;
const current = readMonotonicStateFromBody(params.current.body);
if (!current) return;
if (isMonotonicRollback({
candidate: params.candidate,
current
})) throw new HostedCatalogSignedFeedMonotonicityError("hosted catalog signed feed sequence is older than current snapshot");
if (params.candidate.sequence !== current.sequence || current.generatedAt === void 0) return;
const candidate = readMonotonicStateFromBody(params.candidateBody);
if (candidate?.sequence === params.candidate.sequence && candidate.payloadSha256 !== current.payloadSha256) throw new HostedCatalogSignedFeedMonotonicityError("hosted catalog signed feed payload changed without a sequence increment");
}
function rowToSnapshot(row) {
if (!row) return null;
const metadata = {
url: row.feed_url,
status: coerceRequiredSqliteNumber(row.status),
checksum: row.checksum,
...row.etag ? { etag: row.etag } : {},
...row.last_modified ? { lastModified: row.last_modified } : {}
};
const trust = rowToTrustState(row);
const storedMonotonic = trust ? readMonotonicStateFromBody(row.body) : void 0;
const monotonic = storedMonotonic ? {
mode: "signed-feed",
sequence: storedMonotonic.sequence,
...storedMonotonic.generatedAt ? { generatedAt: storedMonotonic.generatedAt } : {}
} : void 0;
return {
body: row.body,
metadata,
savedAt: row.saved_at,
...trust ? { trust } : {},
...monotonic ? { monotonic } : {}
};
}
/** Creates a snapshot store backed by the shared `state/openclaw.sqlite` database. */
function createSqliteHostedOfficialExternalPluginCatalogSnapshotStore(options = {}) {
return {
async read(url) {
const pathname = resolveStateDatabasePath(options);
if (!existsSync(pathname)) return null;
const database = openOpenClawStateDatabase(resolveStateDatabaseOptions(options));
const stateDb = getNodeSqliteKysely(database.db);
return rowToSnapshot(executeSqliteQueryTakeFirstSync(database.db, stateDb.selectFrom("official_external_plugin_catalog_snapshots").select([
"feed_url",
"body",
"status",
"etag",
"last_modified",
"checksum",
"saved_at",
"trust_mode",
"trust_key_id",
"trust_signature_count",
"trust_threshold",
"trust_verified_at"
]).where("feed_url", "=", url)));
},
async write(snapshot) {
const now = Date.now();
runOpenClawStateWriteTransaction((database) => {
const stateDb = getNodeSqliteKysely(database.db);
const current = executeSqliteQueryTakeFirstSync(database.db, stateDb.selectFrom("official_external_plugin_catalog_snapshots").select([
"feed_url",
"body",
"status",
"etag",
"last_modified",
"checksum",
"saved_at",
"trust_mode",
"trust_key_id",
"trust_signature_count",
"trust_threshold",
"trust_verified_at"
]).where("feed_url", "=", snapshot.metadata.url));
assertSignedSnapshotWriteIsMonotonic({
candidate: snapshot.monotonic,
candidateBody: snapshot.body,
current
});
executeSqliteQuerySync(database.db, stateDb.insertInto("official_external_plugin_catalog_snapshots").values({
feed_url: snapshot.metadata.url,
body: snapshot.body,
status: snapshot.metadata.status,
etag: snapshot.metadata.etag ?? null,
last_modified: snapshot.metadata.lastModified ?? null,
checksum: snapshot.metadata.checksum,
saved_at: snapshot.savedAt,
updated_at_ms: now,
trust_mode: snapshot.trust?.mode ?? null,
trust_key_id: snapshot.trust?.signedBy ?? null,
trust_signature_count: snapshot.trust?.signatureCount ?? null,
trust_threshold: snapshot.trust?.threshold ?? null,
trust_verified_at: snapshot.trust?.verifiedAt ?? null
}).onConflict((conflict) => conflict.column("feed_url").doUpdateSet({
body: snapshot.body,
status: snapshot.metadata.status,
etag: snapshot.metadata.etag ?? null,
last_modified: snapshot.metadata.lastModified ?? null,
checksum: snapshot.metadata.checksum,
saved_at: snapshot.savedAt,
updated_at_ms: now,
trust_mode: snapshot.trust?.mode ?? null,
trust_key_id: snapshot.trust?.signedBy ?? null,
trust_signature_count: snapshot.trust?.signatureCount ?? null,
trust_threshold: snapshot.trust?.threshold ?? null,
trust_verified_at: snapshot.trust?.verifiedAt ?? null
})));
}, resolveStateDatabaseOptions(options));
}
};
}
//#endregion
export { createSqliteHostedOfficialExternalPluginCatalogSnapshotStore };