UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

205 lines (204 loc) 10.4 kB
import { o as normalizeLowercaseStringOrEmpty } from "./string-coerce-CIXf7egm.js"; import { o as resolveRequiredHomeDir, t as expandHomePrefix } from "./home-dir-BPhrG-aM.js"; import { o as safeRealpathSync } from "./boundary-path-DMNeww4q.js"; import { n as normalizeAgentId } from "./agent-id-CeT3w4ap.js"; import { w as resolveStateDir } from "./paths-D2sRr1a_.js"; import "./session-key-BnWWjqNc.js"; import { r as isCompactionCheckpointTranscriptFileName } from "./artifacts-DMDO9PHy.js"; import fs from "node:fs"; import path from "node:path"; import os from "node:os"; //#region src/config/sessions/paths.ts function resolveAgentSessionsDir(agentId, env = process.env, homedir = () => resolveRequiredHomeDir(env, os.homedir)) { if (!agentId?.trim()) throw new Error("Session storage path requires an explicit agent id."); const root = resolveStateDir(env, homedir); const id = normalizeAgentId(agentId); return path.join(root, "agents", id, "sessions"); } function resolveSessionTranscriptsDirForAgent(agentId, env = process.env, homedir = () => resolveRequiredHomeDir(env, os.homedir)) { return resolveAgentSessionsDir(agentId, env, homedir); } function resolveDefaultSessionStorePath(agentId) { return path.join(resolveAgentSessionsDir(agentId), "sessions.json"); } const MULTI_STORE_PATH_SENTINEL = "(multiple)"; const SQLITE_TRANSCRIPT_TARGET_PREFIX = "sqlite:"; function resolveSessionFilePathOptions(params) { const agentId = params.agentId?.trim(); const storePath = params.storePath?.trim(); if (storePath && storePath !== MULTI_STORE_PATH_SENTINEL) { const sessionsDir = path.dirname(path.resolve(storePath)); return agentId ? { sessionsDir, agentId } : { sessionsDir }; } if (agentId) return { agentId }; } const SAFE_SESSION_ID_RE = /^[\p{L}\p{N}][\p{L}\p{N}\p{M}._-]{0,127}$/u; function validateSessionId(sessionId) { const trimmed = sessionId.trim(); if (trimmed !== trimmed.normalize("NFC") || !SAFE_SESSION_ID_RE.test(trimmed) || Buffer.byteLength(`${trimmed}.jsonl`, "utf8") > 255 || isCompactionCheckpointTranscriptFileName(`${trimmed}.jsonl`)) throw new Error(`Invalid session ID: ${sessionId}`); return trimmed; } function resolveSessionsDir(opts) { const sessionsDir = opts?.sessionsDir?.trim(); if (sessionsDir) return path.resolve(sessionsDir); if (!opts?.agentId?.trim()) throw new Error("Session storage path requires an explicit agent id."); return resolveAgentSessionsDir(opts.agentId); } function resolvePathFromAgentSessionsDir(agentSessionsDir, candidateAbsPath) { const agentBase = safeRealpathSync(path.resolve(agentSessionsDir)) ?? path.resolve(agentSessionsDir); const realCandidate = safeRealpathSync(candidateAbsPath) ?? candidateAbsPath; const relative = path.relative(agentBase, realCandidate); if (!relative || relative.startsWith("..") || path.isAbsolute(relative)) return resolveRerootedSessionPath(agentBase, candidateAbsPath); return path.resolve(agentBase, relative); } function resolveRerootedSessionPath(agentSessionsDir, candidateAbsPath) { const parsed = resolveAgentSessionsPathParts(candidateAbsPath); if (!parsed) return; const relativeSegments = parsed.parts.slice(parsed.sessionsIndex + 1); if (relativeSegments.length === 0) return; const rerooted = path.resolve(agentSessionsDir, ...relativeSegments); const contained = path.relative(agentSessionsDir, rerooted); if (!contained || contained.startsWith("..") || path.isAbsolute(contained)) return; return fs.existsSync(rerooted) ? rerooted : void 0; } function resolveSiblingAgentSessionsDir(baseSessionsDir, agentId) { const resolvedBase = path.resolve(baseSessionsDir); if (path.basename(resolvedBase) !== "sessions") return; const baseAgentDir = path.dirname(resolvedBase); const baseAgentsDir = path.dirname(baseAgentDir); if (path.basename(baseAgentsDir) !== "agents") return; const rootDir = path.dirname(baseAgentsDir); return path.join(rootDir, "agents", normalizeAgentId(agentId), "sessions"); } function resolveAgentSessionsPathParts(candidateAbsPath) { const parts = path.normalize(path.resolve(candidateAbsPath)).split(path.sep).filter(Boolean); const sessionsIndex = parts.lastIndexOf("sessions"); if (sessionsIndex < 2 || parts[sessionsIndex - 2] !== "agents") return null; return { parts, sessionsIndex }; } function extractAgentIdFromAbsoluteSessionPath(candidateAbsPath) { const parsed = resolveAgentSessionsPathParts(candidateAbsPath); if (!parsed) return; const { parts, sessionsIndex } = parsed; return parts[sessionsIndex - 1] || void 0; } function resolveStructuralSessionFallbackPath(candidateAbsPath, expectedAgentId) { const parsed = resolveAgentSessionsPathParts(candidateAbsPath); if (!parsed) return; const { parts, sessionsIndex } = parsed; const agentIdPart = parts[sessionsIndex - 1]; if (!agentIdPart) return; const normalizedAgentId = normalizeAgentId(agentIdPart); if (normalizedAgentId !== normalizeLowercaseStringOrEmpty(agentIdPart)) return; if (normalizedAgentId !== normalizeAgentId(expectedAgentId)) return; const relativeSegments = parts.slice(sessionsIndex + 1); if (relativeSegments.length !== 1) return; const fileName = relativeSegments[0]; if (!fileName || fileName === "." || fileName === "..") return; return path.normalize(path.resolve(candidateAbsPath)); } function resolvePathWithinSessionsDir(sessionsDir, candidate, opts) { const trimmed = candidate.trim(); if (!trimmed) throw new Error("Session file path must not be empty"); const resolvedBase = path.resolve(sessionsDir); const realBase = safeRealpathSync(resolvedBase) ?? resolvedBase; const realTrimmed = path.isAbsolute(trimmed) ? safeRealpathSync(trimmed) ?? trimmed : trimmed; const normalized = path.isAbsolute(realTrimmed) ? path.relative(realBase, realTrimmed) : realTrimmed; if (normalized.startsWith("..") && path.isAbsolute(realTrimmed)) { const tryAgentFallback = (agentId) => { const normalizedAgentId = normalizeAgentId(agentId); const siblingSessionsDir = resolveSiblingAgentSessionsDir(realBase, normalizedAgentId); if (siblingSessionsDir) { const siblingResolved = resolvePathFromAgentSessionsDir(siblingSessionsDir, realTrimmed); if (siblingResolved) return siblingResolved; } return resolvePathFromAgentSessionsDir(resolveAgentSessionsDir(normalizedAgentId), realTrimmed); }; const explicitAgentId = opts?.agentId?.trim(); if (explicitAgentId) { const resolvedFromAgent = tryAgentFallback(explicitAgentId); if (resolvedFromAgent) return resolvedFromAgent; } const extractedAgentId = extractAgentIdFromAbsoluteSessionPath(realTrimmed); if (extractedAgentId) { const resolvedFromPath = tryAgentFallback(extractedAgentId); if (resolvedFromPath) return resolvedFromPath; const structuralFallback = resolveStructuralSessionFallbackPath(realTrimmed, extractedAgentId); if (structuralFallback) return structuralFallback; } } if (!normalized || normalized.startsWith("..") || path.isAbsolute(normalized)) throw new Error("Session file path must be within sessions directory"); return path.resolve(realBase, normalized); } function resolveSessionTranscriptPathInDir(sessionId, sessionsDir, topicId) { const safeSessionId = validateSessionId(sessionId); const safeTopicId = typeof topicId === "string" ? encodeURIComponent(topicId) : typeof topicId === "number" ? String(topicId) : void 0; const fileName = safeTopicId !== void 0 ? `${safeSessionId}-topic-${safeTopicId}.jsonl` : `${safeSessionId}.jsonl`; if (Buffer.byteLength(fileName, "utf8") > 255) throw new Error(`Invalid session transcript filename: ${fileName}`); return resolvePathWithinSessionsDir(sessionsDir, fileName); } function resolveSessionTranscriptPath(sessionId, agentId, topicId) { return resolveSessionTranscriptPathInDir(sessionId, resolveAgentSessionsDir(agentId), topicId); } function resolveSessionFilePathCore(sessionId, entry, opts) { const sessionsDir = resolveSessionsDir(opts); const candidate = entry && "sessionFile" in entry && typeof entry.sessionFile === "string" ? entry.sessionFile.trim() : void 0; if (candidate) { if (candidate.startsWith(SQLITE_TRANSCRIPT_TARGET_PREFIX)) return candidate; try { return resolvePathWithinSessionsDir(sessionsDir, candidate, { agentId: opts?.agentId }); } catch {} } return resolveSessionTranscriptPathInDir(sessionId, sessionsDir); } var SessionStoreAgentIdRequiredError = class extends Error { constructor() { super("Session store path requires an explicit agent id."); this.name = "SessionStoreAgentIdRequiredError"; } }; /** Resolves fixed literal paths without an owner; derived or templated paths require agentId. */ function resolveSessionStorePathCore(store, opts) { const env = opts?.env ?? process.env; const homedir = () => resolveRequiredHomeDir(env, os.homedir); if (!store) { if (!opts?.agentId?.trim()) throw new SessionStoreAgentIdRequiredError(); const agentId = normalizeAgentId(opts.agentId); return path.join(resolveAgentSessionsDir(agentId, env, homedir), "sessions.json"); } if (store.includes("{agentId}")) { if (!opts?.agentId?.trim()) throw new SessionStoreAgentIdRequiredError(); const agentId = normalizeAgentId(opts.agentId); const expanded = store.replaceAll("{agentId}", agentId); if (expanded.startsWith("~")) return path.resolve(expandHomePrefix(expanded, { home: resolveRequiredHomeDir(env, homedir), env, homedir })); return path.resolve(expanded); } if (store.startsWith("~")) return path.resolve(expandHomePrefix(store, { home: resolveRequiredHomeDir(env, homedir), env, homedir })); return path.resolve(store); } function resolveAgentsDirFromSessionStorePath(storePath) { const candidateAbsPath = path.resolve(storePath); if (path.basename(candidateAbsPath) !== "sessions.json") return; const sessionsDir = path.dirname(candidateAbsPath); if (path.basename(sessionsDir) !== "sessions") return; const agentDir = path.dirname(sessionsDir); const agentsDir = path.dirname(agentDir); if (path.basename(agentsDir) !== "agents") return; return agentsDir; } //#endregion export { resolveSessionFilePathOptions as a, resolveSessionTranscriptPathInDir as c, resolveSessionFilePathCore as i, resolveSessionTranscriptsDirForAgent as l, resolveAgentsDirFromSessionStorePath as n, resolveSessionStorePathCore as o, resolveDefaultSessionStorePath as r, resolveSessionTranscriptPath as s, SessionStoreAgentIdRequiredError as t, validateSessionId as u };