openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
98 lines (97 loc) • 3.45 kB
JavaScript
import { r as extractErrorCode } from "./errors-BXgSefBE.js";
import { t as resolveGlobalMap } from "./global-singleton-PwlQSEal.js";
import { t as createAsyncLock } from "./async-lock-CaiUOILd.js";
import { n as replaceFileAtomic } from "./replace-file-BrS02dAb.js";
import "./error-runtime-C8vbtAJt.js";
import "./security-runtime-CQm7DD1u.js";
import "./async-lock-runtime-sEaHdn0J.js";
import { t as replaceManagedMarkdownBlock } from "./memory-host-markdown-mHNl3RAL.js";
import path from "node:path";
import fs from "node:fs/promises";
//#region extensions/memory-core/src/dreaming-dreams-file.ts
const DREAMS_FILENAMES = ["DREAMS.md", "dreams.md"];
const DEEP_START_MARKER = "<!-- openclaw:dreaming:deep:start -->";
const DEEP_END_MARKER = "<!-- openclaw:dreaming:deep:end -->";
const dreamsFileLocks = resolveGlobalMap(Symbol.for("openclaw.memoryCore.dreamingNarrative.fileLocks"));
async function resolveDreamsPath(workspaceDir) {
for (const name of DREAMS_FILENAMES) {
const target = path.join(workspaceDir, name);
try {
await fs.access(target);
return target;
} catch (err) {
if (err?.code !== "ENOENT") throw err;
}
}
return path.join(workspaceDir, DREAMS_FILENAMES[0]);
}
async function readDreamsFile(dreamsPath) {
try {
return await fs.readFile(dreamsPath, "utf-8");
} catch (err) {
if (err?.code === "ENOENT") return "";
throw err;
}
}
async function assertSafeDreamsPath(dreamsPath) {
const stat = await fs.lstat(dreamsPath).catch((err) => {
if (extractErrorCode(err) === "ENOENT") return null;
throw err;
});
if (!stat) return;
if (stat.isSymbolicLink()) throw new Error("Refusing to write symlinked DREAMS.md");
if (!stat.isFile()) throw new Error("Refusing to write non-file DREAMS.md");
}
async function writeDreamsFileAtomic(dreamsPath, content) {
await assertSafeDreamsPath(dreamsPath);
await replaceFileAtomic({
filePath: dreamsPath,
content,
mode: 384,
preserveExistingMode: true,
tempPrefix: `${path.basename(dreamsPath)}.dreams`,
throwOnCleanupError: true
});
}
async function updateDreamsFile(params) {
const dreamsPath = await resolveDreamsPath(params.workspaceDir);
await fs.mkdir(path.dirname(dreamsPath), { recursive: true });
let lockEntry = dreamsFileLocks.get(dreamsPath);
if (!lockEntry) {
lockEntry = {
withLock: createAsyncLock(),
refs: 0
};
dreamsFileLocks.set(dreamsPath, lockEntry);
}
lockEntry.refs += 1;
try {
return await lockEntry.withLock(async () => {
const existing = await readDreamsFile(dreamsPath);
const { content, result, shouldWrite = true } = await params.updater(existing, dreamsPath);
if (shouldWrite) await writeDreamsFileAtomic(dreamsPath, content.endsWith("\n") ? content : `${content}\n`);
return result;
});
} finally {
lockEntry.refs -= 1;
if (lockEntry.refs <= 0 && dreamsFileLocks.get(dreamsPath) === lockEntry) dreamsFileLocks.delete(dreamsPath);
}
}
async function updateDeepDreamsFile(params) {
const body = params.bodyLines.length > 0 ? params.bodyLines.join("\n") : "- No durable changes.";
return await updateDreamsFile({
workspaceDir: params.workspaceDir,
updater: (existing, dreamsPath) => ({
content: replaceManagedMarkdownBlock({
original: existing,
heading: "## Deep Sleep",
startMarker: DEEP_START_MARKER,
endMarker: DEEP_END_MARKER,
body
}),
result: dreamsPath
})
});
}
//#endregion
export { updateDreamsFile as n, updateDeepDreamsFile as t };