mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-19 21:21:10 +00:00
83 lines
2.5 KiB
TypeScript
83 lines
2.5 KiB
TypeScript
import {
|
|
readConfigFileSnapshotForWrite,
|
|
resolveConfigSnapshotHash,
|
|
writeConfigFile,
|
|
type ConfigWriteOptions,
|
|
} from "./io.js";
|
|
import type { ConfigFileSnapshot, OpenClawConfig } from "./types.js";
|
|
|
|
export type ConfigMutationBase = "runtime" | "source";
|
|
|
|
export class ConfigMutationConflictError extends Error {
|
|
readonly currentHash: string | null;
|
|
|
|
constructor(message: string, params: { currentHash: string | null }) {
|
|
super(message);
|
|
this.name = "ConfigMutationConflictError";
|
|
this.currentHash = params.currentHash;
|
|
}
|
|
}
|
|
|
|
export type ConfigReplaceResult = {
|
|
path: string;
|
|
previousHash: string | null;
|
|
snapshot: ConfigFileSnapshot;
|
|
nextConfig: OpenClawConfig;
|
|
};
|
|
|
|
function assertBaseHashMatches(snapshot: ConfigFileSnapshot, expectedHash?: string): string | null {
|
|
const currentHash = resolveConfigSnapshotHash(snapshot) ?? null;
|
|
if (expectedHash !== undefined && expectedHash !== currentHash) {
|
|
throw new ConfigMutationConflictError("config changed since last load", {
|
|
currentHash,
|
|
});
|
|
}
|
|
return currentHash;
|
|
}
|
|
|
|
export async function replaceConfigFile(params: {
|
|
nextConfig: OpenClawConfig;
|
|
baseHash?: string;
|
|
writeOptions?: ConfigWriteOptions;
|
|
}): Promise<ConfigReplaceResult> {
|
|
const { snapshot, writeOptions } = await readConfigFileSnapshotForWrite();
|
|
const previousHash = assertBaseHashMatches(snapshot, params.baseHash);
|
|
await writeConfigFile(params.nextConfig, {
|
|
...writeOptions,
|
|
...params.writeOptions,
|
|
});
|
|
return {
|
|
path: snapshot.path,
|
|
previousHash,
|
|
snapshot,
|
|
nextConfig: params.nextConfig,
|
|
};
|
|
}
|
|
|
|
export async function mutateConfigFile<T = void>(params: {
|
|
base?: ConfigMutationBase;
|
|
baseHash?: string;
|
|
writeOptions?: ConfigWriteOptions;
|
|
mutate: (
|
|
draft: OpenClawConfig,
|
|
context: { snapshot: ConfigFileSnapshot; previousHash: string | null },
|
|
) => Promise<T | void> | T | void;
|
|
}): Promise<ConfigReplaceResult & { result: T | undefined }> {
|
|
const { snapshot, writeOptions } = await readConfigFileSnapshotForWrite();
|
|
const previousHash = assertBaseHashMatches(snapshot, params.baseHash);
|
|
const baseConfig = params.base === "runtime" ? snapshot.runtimeConfig : snapshot.sourceConfig;
|
|
const draft = structuredClone(baseConfig) as OpenClawConfig;
|
|
const result = (await params.mutate(draft, { snapshot, previousHash })) as T | undefined;
|
|
await writeConfigFile(draft, {
|
|
...writeOptions,
|
|
...params.writeOptions,
|
|
});
|
|
return {
|
|
path: snapshot.path,
|
|
previousHash,
|
|
snapshot,
|
|
nextConfig: draft,
|
|
result,
|
|
};
|
|
}
|