mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-11 22:30:45 +00:00
* refactor: extract filesystem safety primitives * refactor: use fs-safe for file access helpers * refactor: reuse fs-safe for media reads * refactor: use fs-safe for image reads * refactor: reuse fs-safe in qqbot media opener * refactor: reuse fs-safe for local media checks * refactor: consume cleaner fs-safe api * refactor: align fs-safe json option names * fix: preserve fs-safe migration contracts * refactor: use fs-safe primitive subpaths * refactor: use grouped fs-safe subpaths * refactor: align fs-safe api usage * refactor: adapt private state store api * chore: refresh proof gate * refactor: follow fs-safe json api split * refactor: follow reduced fs-safe surface * build: default fs-safe python helper off * fix: preserve fs-safe plugin sdk aliases * refactor: consolidate fs-safe usage * refactor: unify fs-safe store usage * refactor: trim fs-safe temp workspace usage * refactor: hide low-level fs-safe primitives * build: use published fs-safe package * fix: preserve outbound recovery durability after rebase * chore: refresh pr checks
107 lines
3.2 KiB
TypeScript
107 lines
3.2 KiB
TypeScript
import fs from "node:fs/promises";
|
|
import path from "node:path";
|
|
import { pathExists } from "openclaw/plugin-sdk/security-runtime";
|
|
import { compileMemoryWikiVault } from "./compile.js";
|
|
import type { ResolvedMemoryWikiConfig } from "./config.js";
|
|
import { appendMemoryWikiLog } from "./log.js";
|
|
import { renderMarkdownFence, renderWikiMarkdown, slugifyWikiSegment } from "./markdown.js";
|
|
import { initializeMemoryWikiVault } from "./vault.js";
|
|
|
|
type IngestMemoryWikiSourceResult = {
|
|
sourcePath: string;
|
|
pageId: string;
|
|
pagePath: string;
|
|
title: string;
|
|
bytes: number;
|
|
created: boolean;
|
|
indexUpdatedFiles: string[];
|
|
};
|
|
|
|
function resolveSourceTitle(sourcePath: string, explicitTitle?: string): string {
|
|
if (explicitTitle?.trim()) {
|
|
return explicitTitle.trim();
|
|
}
|
|
return path.basename(sourcePath, path.extname(sourcePath)).replace(/[-_]+/g, " ").trim();
|
|
}
|
|
|
|
function assertUtf8Text(buffer: Buffer, sourcePath: string): string {
|
|
const preview = buffer.subarray(0, Math.min(buffer.length, 4096));
|
|
if (preview.includes(0)) {
|
|
throw new Error(`Cannot ingest binary file as markdown source: ${sourcePath}`);
|
|
}
|
|
return buffer.toString("utf8");
|
|
}
|
|
|
|
export async function ingestMemoryWikiSource(params: {
|
|
config: ResolvedMemoryWikiConfig;
|
|
inputPath: string;
|
|
title?: string;
|
|
nowMs?: number;
|
|
}): Promise<IngestMemoryWikiSourceResult> {
|
|
await initializeMemoryWikiVault(params.config, { nowMs: params.nowMs });
|
|
const sourcePath = path.resolve(params.inputPath);
|
|
const buffer = await fs.readFile(sourcePath);
|
|
const content = assertUtf8Text(buffer, sourcePath);
|
|
const title = resolveSourceTitle(sourcePath, params.title);
|
|
const slug = slugifyWikiSegment(title);
|
|
const pageId = `source.${slug}`;
|
|
const pageRelativePath = path.join("sources", `${slug}.md`);
|
|
const pagePath = path.join(params.config.vault.path, pageRelativePath);
|
|
const created = !(await pathExists(pagePath));
|
|
const timestamp = new Date(params.nowMs ?? Date.now()).toISOString();
|
|
|
|
const markdown = renderWikiMarkdown({
|
|
frontmatter: {
|
|
pageType: "source",
|
|
id: pageId,
|
|
title,
|
|
sourceType: "local-file",
|
|
sourcePath,
|
|
ingestedAt: timestamp,
|
|
updatedAt: timestamp,
|
|
status: "active",
|
|
},
|
|
body: [
|
|
`# ${title}`,
|
|
"",
|
|
"## Source",
|
|
`- Type: \`local-file\``,
|
|
`- Path: \`${sourcePath}\``,
|
|
`- Bytes: ${buffer.byteLength}`,
|
|
`- Updated: ${timestamp}`,
|
|
"",
|
|
"## Content",
|
|
renderMarkdownFence(content, "text"),
|
|
"",
|
|
"## Notes",
|
|
"<!-- openclaw:human:start -->",
|
|
"<!-- openclaw:human:end -->",
|
|
"",
|
|
].join("\n"),
|
|
});
|
|
|
|
await fs.writeFile(pagePath, markdown, "utf8");
|
|
await appendMemoryWikiLog(params.config.vault.path, {
|
|
type: "ingest",
|
|
timestamp,
|
|
details: {
|
|
inputPath: sourcePath,
|
|
pageId,
|
|
pagePath: pageRelativePath.split(path.sep).join("/"),
|
|
bytes: buffer.byteLength,
|
|
created,
|
|
},
|
|
});
|
|
const compile = await compileMemoryWikiVault(params.config);
|
|
|
|
return {
|
|
sourcePath,
|
|
pageId,
|
|
pagePath: pageRelativePath.split(path.sep).join("/"),
|
|
title,
|
|
bytes: buffer.byteLength,
|
|
created,
|
|
indexUpdatedFiles: compile.updatedFiles,
|
|
};
|
|
}
|