@sema-agent/core
Version:
Stateless, task-oriented AI agent core
107 lines (106 loc) • 5.88 kB
JavaScript
import { existsSync, renameSync } from "node:fs";
import { join } from "node:path";
import { writerOf } from "../../core/permission-rule-store.js";
import { createPermissionRuleStoreProvider } from "../../core/permission-rule-provider.js";
import { syncPermissionRules } from "../../core/permission-rule-sync.js";
import { thrownText } from "../../core/thrown-value.js";
import { FileStoreLockError } from "./fs-atomic.js";
import { FileDurableRulePartitionProvider, LOCAL_OWNER_FILE, principalRuleFileName, readRuleAdoptionFile, writeRuleAdoptionFile, } from "./permission-rule-store.js";
export async function adoptFilePermissionRuleStore(opts) {
if (typeof opts.toPrincipal !== "string" || opts.toPrincipal === "") {
throw new Error("adoptFilePermissionRuleStore requires the adopting principal — adoption is the act of giving this bucket an identity");
}
if (opts.from.kind === "principal" && opts.from.principal === opts.toPrincipal) {
throw new Error(`the bucket already belongs to "${opts.toPrincipal}" — there is nothing to adopt`);
}
const sourceFile = join(opts.dir, opts.from.kind === "local-owner" ? LOCAL_OWNER_FILE : principalRuleFileName(opts.from.principal));
const targetFile = join(opts.dir, principalRuleFileName(opts.toPrincipal));
const existing = readRuleAdoptionFile(opts.dir);
if (existing !== undefined) {
if ("adopted" in existing) {
if (existing.adopted.toPrincipal === opts.toPrincipal && sameOwner(existing.adopted.from, opts.from))
return { status: "adopted" };
throw new Error(`this directory already carries a completed adoption (${ownerText(existing.adopted.from)} → "${existing.adopted.toPrincipal}") — a second adoption of the same directory is not a shape this helper supports`);
}
if (existing.marker.toPrincipal !== opts.toPrincipal || !sameOwner(existing.marker.from, opts.from)) {
throw new Error(`an adoption toward "${existing.marker.toPrincipal}" is already in flight in this directory — refusing to start a different one (resume the original, or repair the marker by hand)`);
}
}
else if (existsSync(targetFile)) {
throw new Error(`the target bucket for "${opts.toPrincipal}" already exists — merging two existing buckets goes through an explicit sync join, not a rename`);
}
const provider = new FileDurableRulePartitionProvider(opts.dir, opts.onError);
try {
let lockProbe;
try {
lockProbe = writerOf(provider.forPrincipal(opts.toPrincipal));
}
catch (err) {
if (!(err instanceof FileStoreLockError))
throw err;
throw new FileStoreLockError(err.code, `the permission-rule writer lock ${err.lockPath} refuses the adoption (${err.code}${err.ownerPid !== undefined ? `, held by pid ${err.ownerPid}` : ""}) — stop the live writer, then run the adoption again`, err.lockPath, err.ownerPid);
}
if (lockProbe === undefined)
throw new Error("the target store refused a write face");
let phase = existing !== undefined && "marker" in existing ? existing.marker.phase : 2;
if (existing === undefined) {
writeRuleAdoptionFile(opts.dir, marker(opts.from, opts.toPrincipal, 2));
phase = 2;
}
if (phase < 3) {
const sourcePresent = existsSync(sourceFile);
const targetPresent = existsSync(targetFile);
if (sourcePresent && targetPresent) {
throw new Error(`both the source and the target bucket exist — merging two existing buckets goes through an explicit sync join, not a rename; the adoption marker is left in place for inspection`);
}
if (sourcePresent)
renameSync(sourceFile, targetFile);
writeRuleAdoptionFile(opts.dir, marker(opts.from, opts.toPrincipal, 3));
phase = 3;
}
if (phase < 4) {
writeRuleAdoptionFile(opts.dir, marker(opts.from, opts.toPrincipal, 4));
phase = 4;
}
let syncResult;
if (phase < 5) {
try {
syncResult = await syncPermissionRules({
provider: createPermissionRuleStoreProvider({ durable: provider }),
principal: opts.toPrincipal,
transport: opts.transport,
...(opts.now !== undefined ? { now: opts.now } : {}),
});
}
catch (err) {
return { status: "stalled", phase: 4, error: `the first sync round failed: ${thrownText(err)}` };
}
if (!syncResult.ok) {
return {
status: "stalled",
phase: 4,
error: `the first sync round did not land clean (${(syncResult.warnings ?? []).join("; ") || `${syncResult.dropped.length} drop(s)`}) — resume when the endpoint is healthy`,
};
}
writeRuleAdoptionFile(opts.dir, marker(opts.from, opts.toPrincipal, 5));
phase = 5;
}
writeRuleAdoptionFile(opts.dir, {
schemaVersion: 1,
adopted: { from: opts.from, toPrincipal: opts.toPrincipal, atMs: (opts.now ?? Date.now)() },
});
return { status: "adopted", ...(syncResult !== undefined ? { syncResult } : {}) };
}
finally {
provider.dispose();
}
}
function marker(from, toPrincipal, phase) {
return { schemaVersion: 1, marker: { from, toPrincipal, phase } };
}
function sameOwner(a, b) {
return a.kind === "principal" ? b.kind === "principal" && a.principal === b.principal : b.kind === "local-owner";
}
function ownerText(o) {
return o.kind === "principal" ? `principal "${o.principal}"` : "the local owner";
}