openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
167 lines (166 loc) • 7.53 kB
JavaScript
import { n as normalizeAgentId } from "./agent-id-CeT3w4ap.js";
import "./session-key-BnWWjqNc.js";
import { a as getNodeSqliteKysely, r as executeSqliteQuerySync } from "./kysely-sync-COmh4HWh.js";
import { r as isPidAlive } from "./pid-alive-XuW58Ofz.js";
import { g as runOpenClawAgentWriteTransaction } from "./openclaw-agent-db-CWtDoRbC.js";
import { n as withOpenClawAgentDatabaseReadOnly } from "./openclaw-agent-db-readonly-CHjf8FxN.js";
import { t as chunkItems } from "./chunk-items-2QWieLm-.js";
import { i as isTransientSqliteError } from "./unhandled-rejections-iZOzdBz8.js";
//#region src/infra/session-cost-usage-cache.sqlite.ts
const LEGACY_CACHE_SCOPE = "session-cost-usage";
const LEGACY_CACHE_KEY = "cache";
const REFRESH_LOCK_KEY = "refresh-lock";
const RETIRED_ROLLUP_SCOPE = "session-cost-usage-rollup-v1";
const ROLLUP_SCOPE = "session-cost-usage-rollup-v2";
function readCacheDatabase(agentId, databasePath, operation) {
try {
const result = withOpenClawAgentDatabaseReadOnly(operation, {
agentId: normalizeAgentId(agentId),
...databasePath ? { path: databasePath } : {}
});
return result.found ? result.value : void 0;
} catch (error) {
if (!isTransientSqliteError(error)) throw error;
return;
}
}
function readCacheValue(agentId, scope, key, databasePath) {
return readCacheDatabase(agentId, databasePath, (database) => {
const kysely = getNodeSqliteKysely(database.db);
return executeSqliteQuerySync(database.db, kysely.selectFrom("cache_entries").select("value_json").where("scope", "=", scope).where("key", "=", key).limit(1)).rows[0]?.value_json ?? null;
}) ?? null;
}
function deleteCacheValueIfUnchanged(params) {
runOpenClawAgentWriteTransaction((database) => {
const kysely = getNodeSqliteKysely(database.db);
executeSqliteQuerySync(database.db, kysely.deleteFrom("cache_entries").where("scope", "=", params.scope).where("key", "=", params.key).where("value_json", "=", params.valueJson));
}, {
agentId: normalizeAgentId(params.agentId),
...params.databasePath ? { path: params.databasePath } : {}
}, { operationLabel: `session-cost-usage.${params.key}.delete` });
}
function readSessionCostUsageRollupRows(agentId, databasePath, filePaths) {
return readCacheDatabase(agentId, databasePath, (database) => {
const kysely = getNodeSqliteKysely(database.db);
return (filePaths ? chunkItems([...new Set(filePaths)], 500) : [void 0]).flatMap((keys) => {
const query = kysely.selectFrom("cache_entries").select([
"key",
"value_json",
"updated_at"
]).where("scope", "=", ROLLUP_SCOPE);
return executeSqliteQuerySync(database.db, keys ? query.where("key", "in", keys) : query).rows;
}).flatMap((row) => row.value_json === null ? [] : [{
key: row.key,
valueJson: row.value_json,
updatedAt: row.updated_at
}]);
}) ?? [];
}
function writeSessionCostUsageRollup(params) {
return runOpenClawAgentWriteTransaction((database) => {
const kysely = getNodeSqliteKysely(database.db);
if ((executeSqliteQuerySync(database.db, kysely.selectFrom("cache_entries").select("value_json").where("scope", "=", ROLLUP_SCOPE).where("key", "=", params.rollupId).limit(1)).rows[0]?.value_json ?? null) !== params.previousValueJson) return false;
executeSqliteQuerySync(database.db, kysely.insertInto("cache_entries").values({
scope: ROLLUP_SCOPE,
key: params.rollupId,
value_json: params.valueJson,
blob: null,
expires_at: null,
updated_at: params.updatedAt
}).onConflict((conflict) => conflict.columns(["scope", "key"]).doUpdateSet({
value_json: params.valueJson,
blob: null,
expires_at: null,
updated_at: params.updatedAt
})));
return true;
}, {
agentId: normalizeAgentId(params.agentId),
...params.databasePath ? { path: params.databasePath } : {}
}, { operationLabel: "session-cost-usage.rollup.write" });
}
function deleteSessionCostUsageRollupsExcept(params) {
const existing = params.rows.filter((row) => !params.liveKeys.has(row.key));
runOpenClawAgentWriteTransaction((database) => {
const kysely = getNodeSqliteKysely(database.db);
for (const row of existing) executeSqliteQuerySync(database.db, kysely.deleteFrom("cache_entries").where("scope", "=", ROLLUP_SCOPE).where("key", "=", row.key).where("value_json", "=", row.valueJson).where("updated_at", "=", row.updatedAt));
executeSqliteQuerySync(database.db, kysely.deleteFrom("cache_entries").where("scope", "=", LEGACY_CACHE_SCOPE).where("key", "=", LEGACY_CACHE_KEY));
executeSqliteQuerySync(database.db, kysely.deleteFrom("cache_entries").where("scope", "=", RETIRED_ROLLUP_SCOPE));
}, {
agentId: normalizeAgentId(params.agentId),
...params.databasePath ? { path: params.databasePath } : {}
}, { operationLabel: "session-cost-usage.rollup.prune" });
}
function parseRefreshLock(raw) {
if (!raw) return null;
try {
const value = JSON.parse(raw);
if (!value || typeof value.pid !== "number" || !Number.isInteger(value.pid) || value.pid <= 0 || typeof value.startedAt !== "number" || !Number.isFinite(value.startedAt) || typeof value.ownerNonce !== "string" || !value.ownerNonce) return null;
return {
pid: value.pid,
startedAt: value.startedAt,
ownerNonce: value.ownerNonce
};
} catch {
return null;
}
}
function isSessionCostUsageRefreshRunning(agentId, databasePath) {
const raw = readCacheValue(agentId, LEGACY_CACHE_SCOPE, REFRESH_LOCK_KEY, databasePath);
const lock = parseRefreshLock(raw);
if (lock && isPidAlive(lock.pid)) return true;
if (raw !== null) deleteCacheValueIfUnchanged({
agentId,
databasePath,
scope: LEGACY_CACHE_SCOPE,
key: REFRESH_LOCK_KEY,
valueJson: raw
});
return false;
}
function acquireSessionCostUsageRefreshLock(agentId, databasePath) {
const previousRaw = readCacheValue(agentId, LEGACY_CACHE_SCOPE, REFRESH_LOCK_KEY, databasePath);
const previousLock = parseRefreshLock(previousRaw);
const previousOwnerIsRunning = previousLock ? isPidAlive(previousLock.pid) : false;
const lock = {
pid: process.pid,
startedAt: Date.now(),
ownerNonce: `${process.pid}:${Date.now()}:${process.hrtime.bigint()}`
};
const lockJson = JSON.stringify(lock);
const acquired = runOpenClawAgentWriteTransaction((database) => {
const kysely = getNodeSqliteKysely(database.db);
if ((executeSqliteQuerySync(database.db, kysely.selectFrom("cache_entries").select("value_json").where("scope", "=", LEGACY_CACHE_SCOPE).where("key", "=", REFRESH_LOCK_KEY).limit(1)).rows[0]?.value_json ?? null) !== previousRaw || previousOwnerIsRunning) return false;
executeSqliteQuerySync(database.db, kysely.insertInto("cache_entries").values({
scope: LEGACY_CACHE_SCOPE,
key: REFRESH_LOCK_KEY,
value_json: lockJson,
blob: null,
expires_at: null,
updated_at: lock.startedAt
}).onConflict((conflict) => conflict.columns(["scope", "key"]).doUpdateSet({
value_json: lockJson,
blob: null,
expires_at: null,
updated_at: lock.startedAt
})));
return true;
}, {
agentId: normalizeAgentId(agentId),
...databasePath ? { path: databasePath } : {}
}, { operationLabel: "session-cost-usage.refresh-lock.acquire" });
return {
acquired,
release: () => {
if (acquired) deleteCacheValueIfUnchanged({
agentId,
databasePath,
scope: LEGACY_CACHE_SCOPE,
key: REFRESH_LOCK_KEY,
valueJson: lockJson
});
}
};
}
//#endregion
export { writeSessionCostUsageRollup as a, readSessionCostUsageRollupRows as i, deleteSessionCostUsageRollupsExcept as n, isSessionCostUsageRefreshRunning as r, acquireSessionCostUsageRefreshLock as t };