UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

144 lines (143 loc) 5.39 kB
import { o as isRecord } from "./record-coerce-DHZ4bFlT.js"; import { i as isPathInside } from "./path-BlG8lhgR.js"; import "./path-guards-CBe_wA_B.js"; import { i as resolveSessionFilePath } from "./paths-NEwU8m3X.js"; import { a as resolveTrajectoryPointerFilePath, i as resolveTrajectoryFilePath, s as safeTrajectorySessionFileName } from "./paths-Biq9XkB5.js"; import fs from "node:fs"; import path from "node:path"; //#region src/trajectory/cleanup.ts function canonicalizePathForComparison(filePath) { const resolved = path.resolve(filePath); try { return fs.realpathSync(resolved); } catch { return resolved; } } function isPathWithinDir(parentDir, filePath) { const resolvedParent = canonicalizePathForComparison(parentDir); const resolvedFile = canonicalizePathForComparison(filePath); return resolvedFile !== resolvedParent && isPathInside(resolvedParent, resolvedFile); } function isRegularNonSymlinkFile(filePath) { try { const lst = fs.lstatSync(filePath); if (!lst.isFile() || lst.isSymbolicLink()) return false; return fs.statSync(filePath).isFile(); } catch { return false; } } function readTrajectoryPointerFile(pointerPath, sessionId) { if (!isRegularNonSymlinkFile(pointerPath)) return null; try { const parsed = JSON.parse(fs.readFileSync(pointerPath, "utf8")); if (!isRecord(parsed)) return null; if (parsed.traceSchema !== "openclaw-trajectory-pointer" || parsed.schemaVersion !== 1 || parsed.sessionId !== sessionId || typeof parsed.runtimeFile !== "string" || !parsed.runtimeFile.trim()) return null; return { runtimeFile: path.resolve(parsed.runtimeFile) }; } catch { return null; } } function readFirstNonEmptyLine(filePath) { let fd = null; try { fd = fs.openSync(filePath, "r"); const buffer = Buffer.alloc(64 * 1024); const bytesRead = fs.readSync(fd, buffer, 0, buffer.length, 0); if (bytesRead <= 0) return null; for (const line of buffer.subarray(0, bytesRead).toString("utf8").split(/\r?\n/u)) { const trimmed = line.trim(); if (trimmed) return trimmed; } return null; } catch { return null; } finally { if (fd !== null) try { fs.closeSync(fd); } catch {} } } function runtimeFileStartsWithSessionEvent(filePath, sessionId) { if (!isRegularNonSymlinkFile(filePath)) return false; const firstLine = readFirstNonEmptyLine(filePath); if (!firstLine) return false; try { const parsed = JSON.parse(firstLine); return isRecord(parsed) && parsed.traceSchema === "openclaw-trajectory" && parsed.schemaVersion === 1 && parsed.source === "runtime" && parsed.sessionId === sessionId; } catch { return false; } } async function removeRegularFile(filePath, kind) { if (!isRegularNonSymlinkFile(filePath)) return null; await fs.promises.rm(filePath, { force: true }); return { kind, path: path.resolve(filePath) }; } function resolveRemovedSessionFile(params) { try { return resolveSessionFilePath(params.sessionId, params.sessionFile ? { sessionFile: params.sessionFile } : void 0, { sessionsDir: path.dirname(params.storePath) }); } catch { return null; } } function mayRemoveRuntimeTarget(params) { const resolved = canonicalizePathForComparison(params.filePath); const withinStoreDir = isPathWithinDir(params.storeDir, resolved); if (canonicalizePathForComparison(params.defaultRuntimePath) === resolved) return !params.restrictToStoreDir || withinStoreDir; if (params.restrictToStoreDir && withinStoreDir) return true; const expectedName = `${safeTrajectorySessionFileName(params.sessionId)}.jsonl`; if (path.basename(resolved) !== expectedName) return false; return runtimeFileStartsWithSessionEvent(resolved, params.sessionId); } async function removeSessionTrajectoryArtifacts(params) { const sessionFile = resolveRemovedSessionFile(params); if (!sessionFile) return []; const storeDir = path.dirname(path.resolve(params.storePath)); const restrictToStoreDir = params.restrictToStoreDir === true; const removed = []; const pointerPath = resolveTrajectoryPointerFilePath(sessionFile); const pointer = readTrajectoryPointerFile(pointerPath, params.sessionId); const defaultRuntimePath = resolveTrajectoryFilePath({ env: {}, sessionFile, sessionId: params.sessionId }); const runtimeCandidates = new Set([defaultRuntimePath]); if (pointer?.runtimeFile) runtimeCandidates.add(pointer.runtimeFile); for (const runtimePath of runtimeCandidates) { if (!mayRemoveRuntimeTarget({ defaultRuntimePath, filePath: runtimePath, sessionId: params.sessionId, storeDir, restrictToStoreDir })) continue; const deleted = await removeRegularFile(runtimePath, "runtime"); if (deleted) removed.push(deleted); } if (!restrictToStoreDir || isPathWithinDir(storeDir, pointerPath)) { const deletedPointer = await removeRegularFile(pointerPath, "pointer"); if (deletedPointer) removed.push(deletedPointer); } return removed; } async function removeRemovedSessionTrajectoryArtifacts(params) { const removed = []; for (const [sessionId, sessionFile] of params.removedSessionFiles) { if (params.referencedSessionIds.has(sessionId)) continue; removed.push(...await removeSessionTrajectoryArtifacts({ sessionId, sessionFile, storePath: params.storePath, restrictToStoreDir: params.restrictToStoreDir })); } return removed; } //#endregion export { removeRemovedSessionTrajectoryArtifacts, removeSessionTrajectoryArtifacts };