UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

98 lines (97 loc) 3.45 kB
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 };