openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
152 lines (151 loc) • 6.06 kB
JavaScript
import { t as createDeferredCore } from "./deferred-D0La5CRk.mjs";
import { t as ensureSqliteLibrarySelected } from "./bun-sqlite-library-CmVami_Y.mjs";
import { r as getChildLogger } from "./logger-DQckz-uQ.mjs";
import { s as throwSqliteLifecycleErrors } from "./sqlite-lifecycle-errors-DuOygjcn.mjs";
import { n as createSqliteSnapshotStagingDirectory } from "./sqlite-snapshot-staging-BlSkZs0_.mjs";
import { c as removeTempDirectoryAsync, l as retainSnapshotTempDirectory, t as SqliteSnapshotCleanupError, u as retainSnapshotWork } from "./sqlite-readonly-location-cleanup-EEgKcRbD.mjs";
import { o as getOpenClawDatabaseMaintenanceScope } from "./openclaw-state-db-async-lifecycle-8N3pIeYH.mjs";
import { s as resolveOpenClawStateSqlitePath } from "./openclaw-state-db.paths-BvBEgHyD.mjs";
import { _ as registerOpenClawStateDatabaseAsyncResource, n as captureOpenClawStateDatabaseReadAdmission } from "./openclaw-state-db-cache-BEArcjbO.mjs";
import { r as isArtifactPreservingStateRead } from "./openclaw-state-db-readonly-BLZyK3So.mjs";
import { t as resolveRuntimeProcessEntrypointUrl } from "./runtime-process-url-DpjnU3O9.mjs";
import { t as WorkerTaskPool } from "./worker-task-pool-Dx19K932.mjs";
import { t as restoreCronLoadError } from "./load-error-CCqCdGGN.mjs";
import { t as cronStoreKey } from "./key-BBZ40bDq.mjs";
import fs from "node:fs";
//#region src/cron/store/read-only.ts
function emptyLoadedCronStore() {
return {
store: {
version: 1,
jobs: []
},
configJobs: [],
configJobIndexes: [],
configJobRuntimeEntries: [],
invalidConfigRows: []
};
}
/** Loads cron jobs from existing SQLite state without creating or migrating it. */
async function loadCronJobsStoreWithConfigJobsReadOnly(storePath, env = process.env) {
return (await readCronState({
storeKey: cronStoreKey(storePath),
env
})).loaded ?? emptyLoadedCronStore();
}
/** Doctor inventory includes raw definitions in every persisted partition. */
async function inspectCronJobsReadOnly(env) {
return (await readCronState({ env })).inventory ?? [];
}
async function readCronState({ storeKey, env, history }) {
const statePath = resolveOpenClawStateSqlitePath(env);
if (!fs.existsSync(statePath)) return { ok: true };
const preserveArtifacts = storeKey === void 0 || isArtifactPreservingStateRead();
const admission = captureOpenClawStateDatabaseReadAdmission(statePath);
const identity = admission.identity;
const canonicalPath = identity.canonicalPath;
const maintenance = getOpenClawDatabaseMaintenanceScope();
ensureSqliteLibrarySelected();
const environment = { ...process.env };
const environmentBytes = Object.entries(environment).reduce((bytes, [key, value]) => bytes + Buffer.byteLength(key) + Buffer.byteLength(value ?? ""), 0);
const pool = new WorkerTaskPool({
workerUrl: resolveRuntimeProcessEntrypointUrl("cronReadOnly"),
workerOptions: { env: environment },
maxWorkers: 1,
sharedCompute: true
});
const controller = new AbortController();
const producerSettled = createDeferredCore();
let stagingRoot;
let releaseSnapshot;
let workerStopped = false;
let readSucceeded = false;
let cleaned = false;
let cleanupPending;
const cleanup = () => {
if (cleaned) return Promise.resolve();
return cleanupPending ??= (async () => {
if (!workerStopped) {
await pool.close();
workerStopped = true;
}
await producerSettled.promise;
releaseSnapshot?.();
releaseSnapshot = void 0;
if (stagingRoot) {
let cause;
if (!await removeTempDirectoryAsync(stagingRoot, (error) => cause = error)) {
const error = new SqliteSnapshotCleanupError("Cron read-only state snapshot cleanup failed.", { cause });
if (!readSucceeded) throw error;
try {
getChildLogger({ subsystem: "cron" }).warn({
path: stagingRoot,
error: String(cause)
}, "Cron read-only snapshot cleanup deferred; the read completed successfully.");
} catch {}
}
stagingRoot = void 0;
}
cleaned = true;
unregister();
})().finally(() => {
cleanupPending = void 0;
});
};
const resource = { async close(target) {
if (!target || target.key === identity?.key || target.canonicalPath === canonicalPath) {
controller.abort(/* @__PURE__ */ new Error("Cron read-only load closed"));
await cleanup();
}
} };
const unregister = registerOpenClawStateDatabaseAsyncResource(resource);
const run = async () => {
let loaded = { ok: true };
const failures = [];
try {
maintenance?.own(resource, "shared-resources", () => resource.close());
if (preserveArtifacts) {
stagingRoot = await createSqliteSnapshotStagingDirectory(void 0, false, controller.signal, true);
releaseSnapshot = retainSnapshotTempDirectory(stagingRoot);
}
const location = statePath;
controller.signal.throwIfAborted();
admission.assertCurrent();
const result = await pool.run({
location,
storeKey,
history,
stagingRoot
}, {
signal: controller.signal,
inputBytes: Buffer.byteLength(location) + Buffer.byteLength(storeKey ?? "") + Buffer.byteLength(history?.jobId ?? "") + Buffer.byteLength(stagingRoot ?? "") + environmentBytes
});
if (!result.ok) throw restoreCronLoadError(result.error);
loaded = result;
readSucceeded = true;
} catch (error) {
failures.push(error);
} finally {
producerSettled.resolve();
try {
await cleanup();
} catch (error) {
failures.push(error);
}
}
throwSqliteLifecycleErrors(failures, "Cron read-only state inspection and cleanup failed.");
controller.signal.throwIfAborted();
admission.assertCurrent();
return loaded;
};
return await retainSnapshotWork(run(), () => controller.abort(/* @__PURE__ */ new Error("Cron read-only load closed")));
}
async function readCronRunRecords(storeKey, jobId) {
return (await readCronState({
storeKey: cronStoreKey(storeKey),
env: process.env,
history: { jobId }
})).history ?? [];
}
//#endregion
export { loadCronJobsStoreWithConfigJobsReadOnly as n, readCronRunRecords as r, inspectCronJobsReadOnly as t };