import fs from "node:fs/promises"; import path from "node:path"; import type { OpenClawConfig } from "../config/config.js"; import type { MemoryPluginPublicArtifact } from "../plugins/memory-state.js"; import { resolveMemoryDreamingWorkspaces } from "./memory-core-host-status.js"; import { resolveMemoryHostEventLogPath } from "./memory-host-events.js"; export * from "./memory-core-host-runtime-core.js"; async function pathExists(filePath: string): Promise { try { await fs.access(filePath); return true; } catch { return false; } } async function listMarkdownFilesRecursive(rootDir: string): Promise { const entries = await fs.readdir(rootDir, { withFileTypes: true }).catch(() => []); const files: string[] = []; for (const entry of entries) { const fullPath = path.join(rootDir, entry.name); if (entry.isDirectory()) { files.push(...(await listMarkdownFilesRecursive(fullPath))); continue; } if (entry.isFile() && entry.name.endsWith(".md")) { files.push(fullPath); } } return files.toSorted((left, right) => left.localeCompare(right)); } export async function listMemoryWorkspacePublicArtifacts(params: { workspaceDir: string; agentIds: string[]; }): Promise { const artifacts: MemoryPluginPublicArtifact[] = []; const workspaceEntries = new Set( (await fs.readdir(params.workspaceDir, { withFileTypes: true }).catch(() => [])) .filter((entry) => entry.isFile()) .map((entry) => entry.name), ); if (workspaceEntries.has("MEMORY.md")) { const absolutePath = path.join(params.workspaceDir, "MEMORY.md"); artifacts.push({ kind: "memory-root", workspaceDir: params.workspaceDir, relativePath: "MEMORY.md", absolutePath, agentIds: [...params.agentIds], contentType: "markdown", }); } const memoryDir = path.join(params.workspaceDir, "memory"); for (const absolutePath of await listMarkdownFilesRecursive(memoryDir)) { const relativePath = path.relative(params.workspaceDir, absolutePath).replace(/\\/g, "/"); artifacts.push({ kind: relativePath.startsWith("memory/dreaming/") ? "dream-report" : "daily-note", workspaceDir: params.workspaceDir, relativePath, absolutePath, agentIds: [...params.agentIds], contentType: "markdown", }); } const eventLogPath = resolveMemoryHostEventLogPath(params.workspaceDir); if (await pathExists(eventLogPath)) { artifacts.push({ kind: "event-log", workspaceDir: params.workspaceDir, relativePath: path.relative(params.workspaceDir, eventLogPath).replace(/\\/g, "/"), absolutePath: eventLogPath, agentIds: [...params.agentIds], contentType: "json", }); } const deduped = new Map(); for (const artifact of artifacts) { deduped.set(`${artifact.workspaceDir}\0${artifact.relativePath}\0${artifact.kind}`, artifact); } return [...deduped.values()]; } export async function listMemoryHostPublicArtifacts(params: { cfg: OpenClawConfig; }): Promise { const workspaces = resolveMemoryDreamingWorkspaces(params.cfg); const artifacts: MemoryPluginPublicArtifact[] = []; for (const workspace of workspaces) { artifacts.push( ...(await listMemoryWorkspacePublicArtifacts({ workspaceDir: workspace.workspaceDir, agentIds: workspace.agentIds, })), ); } return artifacts; }