UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

106 lines (105 loc) • 5.56 kB
import { r as resolveRealpathOrAbsolute } from "./boundary-path-CAv_plX7.mjs"; import { n as ok, t as err } from "./result-BQGgYouL.mjs"; import { i as isMigrationArchiveArtifactName } from "./artifacts-BvSTVPj-.mjs"; import { o as resolveSessionArtifactDirectory } from "./paths-dpReqUI8.mjs"; import { t as runTasksWithConcurrency } from "./run-with-concurrency-Dtu208ef.mjs"; import { r as listDurableSqliteTargetPathsForSessionStorePath } from "./session-sqlite-target-Cnk9g9d0.mjs"; import fs from "node:fs"; import path from "node:path"; //#region src/config/sessions/disk-budget-files.ts const SESSIONS_DIR_STAT_CONCURRENCY = 8; async function removeFileIfExists(filePath) { const stat = await fs.promises.stat(filePath).catch(() => null); if (!stat?.isFile()) return err("not-removed"); return fs.promises.rm(filePath).then(() => ok(stat.size), () => err("not-removed")); } async function removeFileForBudget(params) { const resolvedPath = path.resolve(params.filePath); const canonicalPath = params.canonicalPath ?? resolveRealpathOrAbsolute(resolvedPath); if (params.dryRun) { if (params.simulatedRemovedPaths.has(canonicalPath)) return err("not-removed"); const size = params.fileSizesByPath.get(canonicalPath); if (size === void 0) return err("not-removed"); params.simulatedRemovedPaths.add(canonicalPath); params.onRemovedPath?.(canonicalPath); return ok(size); } const removal = await removeFileIfExists(resolvedPath); if (removal.ok) params.onRemovedPath?.(canonicalPath); return removal; } async function readSessionFileStat(filePath) { const stat = await fs.promises.stat(filePath).catch(() => null); return stat?.isFile() ? { path: filePath, canonicalPath: resolveRealpathOrAbsolute(filePath), name: path.basename(filePath), size: stat.size, mtimeMs: stat.mtimeMs } : null; } async function readSessionsDirFiles(sessionsDir) { const tasks = (await fs.promises.readdir(sessionsDir, { withFileTypes: true }).catch(() => [])).filter((dirent) => dirent.isFile() && !isMigrationArchiveArtifactName(dirent.name)).map((dirent) => () => readSessionFileStat(path.join(sessionsDir, dirent.name))); const { results } = await runTasksWithConcurrency({ tasks, limit: SESSIONS_DIR_STAT_CONCURRENCY }); return results.filter((file) => Boolean(file)); } async function readSqliteDatabaseFiles(databasePaths) { const files = []; for (const databasePath of databasePaths) for (const filePath of [databasePath, `${databasePath}-wal`]) { const file = await readSessionFileStat(filePath); if (file) files.push(file); } return files; } /** Measures current physical session artifacts plus the agent SQLite main file and WAL. */ async function readSessionPhysicalDiskUsage(storePath) { const sessionsDir = resolveSessionArtifactDirectory(storePath); const sessionsDirFiles = await readSessionsDirFiles(sessionsDir); const coldArchiveFiles = await readSessionsDirFiles(path.join(sessionsDir, "cold")); const promptBlobFiles = await readSessionPromptBlobFiles(sessionsDir); const databasePaths = listDurableSqliteTargetPathsForSessionStorePath(storePath); const databaseFiles = await readSqliteDatabaseFiles(databasePaths); const databaseSharedMemoryPaths = new Set(databasePaths.map((databasePath) => resolveRealpathOrAbsolute(`${databasePath}-shm`))); const databaseMainPaths = new Set(databaseFiles.filter((file) => !file.path.endsWith("-wal")).map((file) => file.canonicalPath)); const databaseWalPaths = new Set(databaseFiles.filter((file) => file.path.endsWith("-wal")).map((file) => file.canonicalPath)); const uniqueFiles = /* @__PURE__ */ new Map(); for (const file of [ ...sessionsDirFiles, ...coldArchiveFiles, ...promptBlobFiles, ...databaseFiles ]) if (!databaseSharedMemoryPaths.has(file.canonicalPath)) uniqueFiles.set(file.canonicalPath, file); const databaseMainBytes = [...databaseMainPaths].reduce((sum, databasePath) => sum + (uniqueFiles.get(databasePath)?.size ?? 0), 0); const databaseWalBytes = [...databaseWalPaths].reduce((sum, databasePath) => sum + (uniqueFiles.get(databasePath)?.size ?? 0), 0); const totalBytes = [...uniqueFiles.values()].reduce((sum, file) => sum + file.size, 0); return { databaseMainBytes, databaseWalBytes, sessionFilesBytes: totalBytes - databaseMainBytes - databaseWalBytes, totalBytes }; } async function readSessionPromptBlobFiles(sessionsDir) { const root = path.join(sessionsDir, "skills-prompts", "sha256"); const prefixEntries = await fs.promises.readdir(root, { withFileTypes: true }).catch(() => []); const files = []; for (const prefixEntry of prefixEntries) { if (!prefixEntry.isDirectory() || !/^[a-f0-9]{2}$/u.test(prefixEntry.name)) continue; const prefixDir = path.join(root, prefixEntry.name); const blobEntries = await fs.promises.readdir(prefixDir, { withFileTypes: true }).catch(() => []); for (const blobEntry of blobEntries) { if (!blobEntry.isFile() || !/^[a-f0-9]{64}\.txt$/u.test(blobEntry.name) && !isSessionPromptBlobTempArtifactName(blobEntry.name)) continue; const file = await readSessionFileStat(path.join(prefixDir, blobEntry.name)); if (file) files.push(file); } } return files; } function isSessionPromptBlobTempArtifactName(name) { return /^[a-f0-9]{64}\.txt\.(?:\d+\.)?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.tmp$/u.test(name); } //#endregion export { removeFileForBudget as a, readSessionsDirFiles as i, readSessionPhysicalDiskUsage as n, removeFileIfExists as o, readSessionPromptBlobFiles as r, isSessionPromptBlobTempArtifactName as t };