openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
164 lines (163 loc) • 8.73 kB
JavaScript
import { i as executeSqliteQuerySync, o as getNodeSqliteKysely } from "./kysely-sync-DUWzQvCU.mjs";
import { i as deferSqliteWorkerCommitReceipt, s as requestSqliteWorkerOperationAdmission } from "./sqlite-worker-operation-admission-C5lBzzTk.mjs";
import { l as runOpenClawStateWriteTransaction } from "./openclaw-state-db-DJZ7EKoI.mjs";
import { i as matchesWorkerPlacementTarget, r as isFailedWorkerPlacementEnvironmentGone } from "./placement-target-BZL1jHxv.mjs";
import { c as normalizeIdentity, o as nextGeneration, u as normalizeWorkerPlacementExecutionMode } from "./placement-record-BZ5ExJ2S.mjs";
import { a as query, f as updateTransition, i as getRequired, n as find, o as readWorkerPlacementsForReconcileInDatabase, s as readWorkerPlacementsInDatabase, t as ensureLocal } from "./placement-row-codec-Cpn-JKmv.mjs";
import { o as hasWorkerWorkspacePendingResult } from "./placement-workspace-result-DabzjaLS.mjs";
import { i as readWorkerPlacementMovesReadOnly, n as readWorkerSessionPlacementProjectionInDatabase, r as createPlacementMoveOps } from "./placement-read-projection-GVzVGrUt.mjs";
import { r as assertSessionWorkspaceUnreserved } from "./placement-workspace-reservation.kernel-BwT507ly.mjs";
import { n as consumePreparedEnvironment } from "./prepared-environment-store-DDlKn4GK.mjs";
import { r as findWorkerEnvironment } from "./store-row-codec-l38X6-mv.mjs";
import { t as retireWorkerSessionPlacement } from "./placement-retirement-Dk5eSCzb.mjs";
//#region src/gateway/worker-environments/placement-lifecycle.worker.ts
function operation(type, execute, deferredAdmission = false) {
return (input, { write }) => {
return write(({ db, path }) => {
const admit = (facts) => requestSqliteWorkerOperationAdmission({
stage: "transaction",
facts
});
if (!deferredAdmission) admit();
const receipt = execute({
path,
instanceId: "",
now: () => input.nowMs ?? Date.now(),
read: () => db,
write: (mutate) => mutate(db)
}, input, admit);
requestSqliteWorkerOperationAdmission({
stage: "commit",
facts: receipt
});
deferSqliteWorkerCommitReceipt(db, receipt);
return receipt;
}, { operationLabel: type });
};
}
const placementReadOperations = {
"workerPlacements.read": (input, { open }) => readWorkerPlacementsInDatabase(open().db, input.sessionIds),
"workerPlacements.readWithMove": (input, { open }) => {
const { projection } = readWorkerSessionPlacementProjectionInDatabase(open().db, [input.sessionId], []);
return {
placement: projection.placements.get(input.sessionId),
move: projection.moves.get(input.sessionId)
};
},
"workerPlacements.readMove": (input, { open }) => readWorkerPlacementMovesReadOnly(open().db, [input.sessionId]).get(input.sessionId),
"workerPlacements.readReconcile": (input, { open }) => readWorkerPlacementsForReconcileInDatabase(open().db, input.sessionKey)
};
const placementLifecycleOperations = {
"workerPlacements.beginMove": operation("workerPlacements.beginMove", (runtime, input, admit) => ({
sessionId: input.sessionId,
...createPlacementMoveOps(runtime).beginPlacementMove(input, (placement, joined) => admit({
sessionId: input.sessionId,
placement,
joined
}))
}), true),
"workerPlacements.moveError": operation("workerPlacements.moveError", (runtime, input) => ({
sessionId: input.sessionId,
changed: createPlacementMoveOps(runtime).recordPlacementMoveError(input)
})),
"workerPlacements.cancelMove": operation("workerPlacements.cancelMove", (runtime, input) => ({
sessionId: input.sessionId,
changed: createPlacementMoveOps(runtime).cancelPlacementMove(input)
})),
"workerPlacements.completeMoveSource": operation("workerPlacements.completeMoveSource", (runtime, input) => ({
sessionId: input.sessionId,
...createPlacementMoveOps(runtime).completePlacementMoveSourceToLocal(input)
})),
"workerPlacements.completeAbandonedMoveSource": operation("workerPlacements.completeAbandonedMoveSource", (runtime, input) => ({
sessionId: input.sessionId,
...createPlacementMoveOps(runtime).completeAbandonedPlacementMoveSourceToLocal(input)
})),
"workerPlacements.completeMove": operation("workerPlacements.completeMove", (runtime, input) => ({
sessionId: input.sessionId,
placement: createPlacementMoveOps(runtime).completePlacementMoveToWorker(input)
})),
"workerPlacements.retire": operation("workerPlacements.retire", (runtime, input) => {
const db = runtime.read();
const retired = retireWorkerSessionPlacement(db, input, { onlyIfCurrent: true });
if (!retired && find(db, input.sessionId)) throw new Error(`Worker session placement ${input.sessionId} changed before retirement`);
return retired ? {
sessionId: input.sessionId,
retired: input.expectedState
} : {
sessionId: input.sessionId,
changed: false
};
}),
"workerPlacements.bindPrepared": operation("workerPlacements.bindPrepared", (runtime, input) => {
const nowMs = runtime.now();
const consumed = consumePreparedEnvironment(runtime.read(), input, nowMs);
if (!consumed) return { sessionId: input.sessionId };
return {
sessionId: input.sessionId,
placement: updateTransition(runtime.read(), consumed.placement, "provisioning", { environmentId: input.environmentId }, nowMs),
environment: {
environmentId: input.environmentId,
patch: consumed.environmentPatch
}
};
})
};
//#endregion
//#region src/gateway/worker-environments/placement-dispatch-store.worker.ts
function startWorkerPlacementDispatchInWorker(input, database) {
const identity = normalizeIdentity(input.placement);
const executionMode = normalizeWorkerPlacementExecutionMode(input.placement.executionMode);
return runOpenClawStateWriteTransaction(({ db }) => {
requestSqliteWorkerOperationAdmission({
stage: "transaction",
facts: void 0
});
const current = ensureLocal(db, identity, input.nowMs);
assertSessionWorkspaceUnreserved(db, identity.sessionId);
if (current.state !== "local" && current.state !== "reclaimed" && current.state !== "failed") throw new Error(`Cannot dispatch session ${identity.sessionId} from placement ${current.state}`);
const expected = input.placement.expectedPlacement;
if (expected) {
if (!matchesWorkerPlacementTarget(current, expected) || current.executionMode !== executionMode || current.state !== "reclaimed" && current.state !== "failed" || current.turnClaim) throw new Error(`Worker placement ${identity.sessionId} changed before redispatch`);
const journal = executeSqliteQuerySync(db, getNodeSqliteKysely(db).selectFrom("worker_workspace_reconciliations").select("session_id").where("session_id", "=", identity.sessionId)).rows[0];
if (hasWorkerWorkspacePendingResult(db, identity.sessionId) || journal || readWorkerPlacementMovesReadOnly(db, [identity.sessionId]).has(identity.sessionId)) throw new Error(`Worker placement ${identity.sessionId} still has pending workspace recovery`);
if (current.state === "failed") {
const environment = current.environmentId ? findWorkerEnvironment(db, current.environmentId) : void 0;
if (current.activeOwnerEpoch === null || !environment || !isFailedWorkerPlacementEnvironmentGone({
environmentService: { get: () => environment },
placement: current
})) throw new Error(`Failed worker placement ${identity.sessionId} still requires recovery`);
}
}
if (executeSqliteQuerySync(db, query(db).updateTable("worker_session_placements").set({
state: "requested",
execution_mode: executionMode,
environment_id: null,
transition_generation: nextGeneration(current.generation),
active_owner_epoch: null,
workspace_base_manifest_ref: null,
remote_workspace_dir: null,
worker_bundle_hash: null,
last_transcript_ack_cursor: null,
last_live_event_ack_cursor: null,
recovery_error: null,
terminal_reason: null,
terminal_at_ms: null,
updated_at_ms: input.nowMs,
state_changed_at_ms: input.nowMs
}).where("session_id", "=", current.sessionId).where("state", "=", current.state).where("transition_generation", "=", current.generation)).numAffectedRows !== 1n) throw new Error(`Session ${identity.sessionId} placement changed during dispatch barrier`);
const updated = getRequired(db, identity.sessionId);
deferSqliteWorkerCommitReceipt(db, updated);
requestSqliteWorkerOperationAdmission({
stage: "commit",
facts: updated
});
return updated;
}, { database }, { operationLabel: "workerPlacements.startDispatch" });
}
const workerPlacementOperations = {
...placementLifecycleOperations,
...placementReadOperations,
"workerPlacements.startDispatch": (input, { open }) => startWorkerPlacementDispatchInWorker(input, open())
};
//#endregion
export { workerPlacementOperations };