openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
158 lines (157 loc) • 11.8 kB
JavaScript
import { c as isRecord } from "./record-coerce-TIsquoVR.mjs";
import { a as getNodeSqliteKysely, i as executeSqliteQueryTakeFirstSync, r as executeSqliteQuerySync } from "./kysely-sync-K7ftY-g4.mjs";
import { n as find } from "./placement-row-codec-DzKFQEtn.mjs";
import { p as parseWorkerEnvironmentState } from "./store-validation-BhdOaJpU.mjs";
import { t as publishWorkerEnvironmentNativeMutation } from "./store-native-publication-BFCYOamu.mjs";
import { isDeepStrictEqual } from "node:util";
import { Buffer } from "node:buffer";
//#region src/gateway/worker-environments/prepared-environment-store.ts
const query = (db) => getNodeSqliteKysely(db);
function readWorkerEnvironmentPreparation(row) {
const { preparation_key: key, preparation_purpose: storedPurpose, preparation_demand_at_ms: demandAtMs, preparation_expires_at_ms: expiresAtMs, preparation_consumed_at_ms: consumedAtMs } = row;
if (key === null && storedPurpose === null && demandAtMs === null && expiresAtMs === null && consumedAtMs === null) return null;
if (storedPurpose !== null && storedPurpose !== "reserve" && storedPurpose !== "build" || typeof key !== "string" || !/^[a-f0-9]{64}$/u.test(key) || demandAtMs === null || !Number.isSafeInteger(demandAtMs) || demandAtMs < 0 || expiresAtMs === null || !Number.isSafeInteger(expiresAtMs) || expiresAtMs <= demandAtMs || consumedAtMs !== null && (!Number.isSafeInteger(consumedAtMs) || consumedAtMs < demandAtMs || consumedAtMs >= expiresAtMs)) throw new Error("Worker environment preparation metadata is invalid");
return {
purpose: storedPurpose ?? "reserve",
key,
demandAtMs,
expiresAtMs,
consumedAtMs
};
}
function workerEnvironmentPreparationColumns(preparation) {
const columns = {
preparation_key: preparation?.key ?? null,
preparation_purpose: preparation?.purpose ?? null,
preparation_demand_at_ms: preparation?.demandAtMs ?? null,
preparation_expires_at_ms: preparation?.expiresAtMs ?? null,
preparation_consumed_at_ms: null
};
readWorkerEnvironmentPreparation(columns);
return columns;
}
function hasPlacementReference(db, environmentId) {
return Boolean(executeSqliteQueryTakeFirstSync(db, query(db).selectFrom("worker_session_placements").select("session_id").where("environment_id", "=", environmentId).limit(1)));
}
function snapshotProjectKey(snapshot) {
if (!isRecord(snapshot) || !isRecord(snapshot.project) || typeof snapshot.project.key !== "string" || !/^[a-f0-9]{64}$/u.test(snapshot.project.key)) throw new Error("Prepared worker has an invalid project identity");
return snapshot.project.key;
}
function selectPreparedEnvironmentReservations(records) {
return records.filter((record) => record.preparation !== null && !["failed", "destroyed"].includes(record.state) && (record.preparation.consumedAtMs === null || record.destroyRequestedAtMs !== null || record.state === "orphaned")).map((record) => ({
environmentId: record.environmentId,
profileId: record.profileId,
leaseId: record.leaseId,
cleanupOrder: record.destroyRequestedAtMs !== null || record.state === "orphaned" ? 0 : 1,
state: record.state,
purpose: record.preparation.purpose,
projectKey: snapshotProjectKey(record.profileSnapshot),
createdAtMs: record.createdAtMs
})).toSorted((a, b) => a.cleanupOrder - b.cleanupOrder || a.createdAtMs - b.createdAtMs || Buffer.compare(Buffer.from(a.environmentId), Buffer.from(b.environmentId)));
}
function readPreparedReservations(db) {
return selectPreparedEnvironmentReservations(executeSqliteQuerySync(db, query(db).selectFrom("worker_environments").selectAll().where("preparation_key", "is not", null)).rows.map((row) => ({
environmentId: row.environment_id,
profileId: row.profile_id,
profileSnapshot: JSON.parse(row.profile_snapshot_json),
leaseId: row.lease_id,
state: parseWorkerEnvironmentState(row.state),
preparation: readWorkerEnvironmentPreparation(row),
destroyRequestedAtMs: row.destroy_requested_at_ms,
createdAtMs: row.created_at_ms
})));
}
function preparedCapacityFromReservations(reserved, input) {
return Math.max(0, Math.min(input.maxTotal - reserved.length, input.target - reserved.filter((row) => row.profileId === input.profileId && row.projectKey === input.projectKey).length));
}
function isPreparedReservationWithinCapacity(reservations, input) {
const owned = reservations.find((row) => row.environmentId === input.environmentId);
if (!owned) return false;
const reserved = owned.leaseId ? reservations.filter((row) => row.cleanupOrder === 1) : reservations;
const index = reserved.findIndex((row) => row.environmentId === input.environmentId);
if (index < 0 || index >= input.maxTotal) return false;
if (owned.purpose === "build" && owned.state !== "ready") return true;
return reserved.slice(0, index + 1).filter((row) => row.profileId === owned.profileId && row.projectKey === owned.projectKey).length <= input.target;
}
function createPreparedEnvironmentStoreOps(options) {
const { db } = options;
return {
ensurePreparedIntent(input) {
if (!Number.isSafeInteger(input.target) || input.target < 0 || !Number.isSafeInteger(input.maxTotal) || input.maxTotal < 0) throw new Error("Prepared worker capacity must be a non-negative safe integer");
workerEnvironmentPreparationColumns(input.intent.preparation);
const existing = options.get(db, input.intent.environmentId);
if (existing) {
if (existing.providerId !== input.intent.providerId || existing.profileId !== input.intent.profileId || existing.provisionOperationId !== input.intent.provisionOperationId || !isDeepStrictEqual(existing.profileSnapshot, input.intent.profileSnapshot) || existing.preparation?.key !== input.intent.preparation.key || existing.preparation.purpose !== input.intent.preparation.purpose || existing.preparation.demandAtMs !== input.intent.preparation.demandAtMs || existing.preparation.expiresAtMs !== input.intent.preparation.expiresAtMs) throw new Error("Prepared environment intent identity changed");
return existing;
}
const nowMs = options.now();
const build = input.intent.preparation.purpose === "build";
if (build && input.maxTotal === 0) return;
if (build) {
const reusable = executeSqliteQueryTakeFirstSync(db, query(db).selectFrom("worker_environments").select("environment_id").where("profile_id", "=", input.intent.profileId).where("provider_id", "=", input.intent.providerId).where("preparation_key", "=", input.intent.preparation.key).where("preparation_consumed_at_ms", "is", null).where("destroy_requested_at_ms", "is", null).where("state", "not in", [
"failed",
"destroyed",
"orphaned"
]).where("preparation_expires_at_ms", ">", nowMs).orderBy("created_at_ms").orderBy("environment_id").limit(1));
if (reusable) {
executeSqliteQuerySync(db, query(db).updateTable("worker_environments").set({
preparation_purpose: "build",
updated_at_ms: nowMs
}).where("environment_id", "=", reusable.environment_id).where("state", "!=", "ready").where((eb) => eb.or([eb("preparation_purpose", "is", null), eb("preparation_purpose", "=", "reserve")])));
return options.get(db, reusable.environment_id);
}
}
if (!build && input.target === 0 || input.maxTotal === 0 || input.intent.preparation.demandAtMs > nowMs || input.intent.preparation.expiresAtMs <= nowMs) return;
if (build ? readPreparedReservations(db).length >= input.maxTotal : preparedCapacityFromReservations(readPreparedReservations(db), {
...input,
profileId: input.intent.profileId
}) === 0) return;
if (snapshotProjectKey(input.intent.profileSnapshot) !== input.projectKey) throw new Error("Prepared worker capacity does not match the admitted project");
return options.createIntent(db, input.intent);
},
requestPreparedDestroy(input) {
const current = options.get(db, input.environmentId);
const preparation = current?.preparation;
const nowMs = options.now();
if (!current || !preparation || preparation.key !== input.preparationKey || current.ownerEpoch !== input.ownerEpoch || preparation.consumedAtMs !== null || current.attachedSessionIds.length !== 0 || current.state === "failed" || current.state === "destroyed" || input.reason === "expired" && preparation.expiresAtMs > nowMs || hasPlacementReference(db, current.environmentId)) return;
if (current.destroyRequestedAtMs !== null) return current;
executeSqliteQuerySync(db, query(db).updateTable("worker_environments").set({
destroy_requested_at_ms: nowMs,
teardown_terminal_state: "destroyed",
updated_at_ms: nowMs
}).where("environment_id", "=", current.environmentId));
return options.get(db, current.environmentId);
}
};
}
/** Placement and consumption commit together; dropping a placement never recreates a spare. */
function consumePreparedEnvironment(db, input, nowMs) {
input.assertCurrent();
const placement = find(db, input.sessionId);
if (!placement || placement.state !== "requested" || placement.generation !== input.expectedGeneration || placement.agentId !== input.agentId || placement.sessionKey !== input.sessionKey || placement.executionMode !== input.executionMode || placement.turnClaim !== null) return;
const environment = executeSqliteQueryTakeFirstSync(db, query(db).selectFrom("worker_environments").selectAll().where("environment_id", "=", input.environmentId));
if (!environment) return;
const preparation = readWorkerEnvironmentPreparation(environment);
const profile = JSON.parse(environment.profile_snapshot_json);
if (!preparation || preparation.key !== input.preparationKey || preparation.consumedAtMs !== null || preparation.demandAtMs > nowMs || preparation.expiresAtMs <= nowMs || !isRecord(profile) || profile.executionMode !== input.executionMode || environment.state !== "ready" || environment.owner_epoch !== input.ownerEpoch || environment.provider_id !== input.providerId || environment.profile_id !== input.profileId || environment.node_device_id !== input.nodeDeviceId || environment.lease_id !== input.leaseId || environment.shared_host !== 0 || environment.bootstrap_bundle_hash !== input.bundleHash || environment.destroy_requested_at_ms !== null || environment.attached_session_ids_json !== "[]" || hasPlacementReference(db, input.environmentId)) return;
input.assertCurrent();
executeSqliteQuerySync(db, query(db).updateTable("worker_environments").set({
preparation_consumed_at_ms: nowMs,
updated_at_ms: nowMs
}).where("environment_id", "=", input.environmentId));
publishWorkerEnvironmentNativeMutation(db, input.environmentId, {
preparation: {
...preparation,
consumedAtMs: nowMs
},
updatedAtMs: nowMs
});
return placement;
}
function assertPreparedEnvironmentAttachment(db, environment, sessionId, binding) {
if (!environment.preparation) return;
const placement = find(db, sessionId);
if (!binding || binding.sessionId !== sessionId || environment.profileSnapshot.executionMode !== binding.executionMode || environment.state !== "ready" || environment.preparation.key !== binding.preparationKey || environment.preparation.consumedAtMs === null || !placement || placement.state !== "syncing" || placement.generation !== binding.generation || placement.sessionKey !== binding.sessionKey || placement.agentId !== binding.agentId || placement.executionMode !== binding.executionMode || placement.environmentId !== environment.environmentId || placement.turnClaim !== null) throw new Error("Prepared worker attachment lost its exact placement reservation");
}
//#endregion
export { preparedCapacityFromReservations as a, workerEnvironmentPreparationColumns as c, isPreparedReservationWithinCapacity as i, consumePreparedEnvironment as n, readWorkerEnvironmentPreparation as o, createPreparedEnvironmentStoreOps as r, selectPreparedEnvironmentReservations as s, assertPreparedEnvironmentAttachment as t };