Files
openclaw/src/infra/pairing-files.ts
2026-04-01 18:02:31 +09:00

68 lines
2.0 KiB
TypeScript

import path from "node:path";
import { resolveStateDir } from "../config/paths.js";
export { createAsyncLock, readJsonFile, writeJsonAtomic } from "./json-files.js";
export function resolvePairingPaths(baseDir: string | undefined, subdir: string) {
const root = baseDir ?? resolveStateDir();
const dir = path.join(root, subdir);
return {
dir,
pendingPath: path.join(dir, "pending.json"),
pairedPath: path.join(dir, "paired.json"),
};
}
export function pruneExpiredPending<T extends { ts: number }>(
pendingById: Record<string, T>,
nowMs: number,
ttlMs: number,
) {
for (const [id, req] of Object.entries(pendingById)) {
if (nowMs - req.ts > ttlMs) {
delete pendingById[id];
}
}
}
export type PendingPairingRequestResult<TPending> = {
status: "pending";
request: TPending;
created: boolean;
};
export async function reconcilePendingPairingRequests<
TPending extends { requestId: string },
TIncoming,
>(params: {
pendingById: Record<string, TPending>;
existing: readonly TPending[];
incoming: TIncoming;
canRefreshSingle: (existing: TPending, incoming: TIncoming) => boolean;
refreshSingle: (existing: TPending, incoming: TIncoming) => TPending;
buildReplacement: (params: { existing: readonly TPending[]; incoming: TIncoming }) => TPending;
persist: () => Promise<void>;
}): Promise<PendingPairingRequestResult<TPending>> {
if (
params.existing.length === 1 &&
params.canRefreshSingle(params.existing[0], params.incoming)
) {
const refreshed = params.refreshSingle(params.existing[0], params.incoming);
params.pendingById[refreshed.requestId] = refreshed;
await params.persist();
return { status: "pending", request: refreshed, created: false };
}
for (const existing of params.existing) {
delete params.pendingById[existing.requestId];
}
const request = params.buildReplacement({
existing: params.existing,
incoming: params.incoming,
});
params.pendingById[request.requestId] = request;
await params.persist();
return { status: "pending", request, created: true };
}