UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

121 lines (120 loc) • 5.27 kB
import { o as resolveUserPath } from "./home-dir-Dc6nN6HB.mjs"; import "./utils-CitNzLyF.mjs"; import { t as hasHttpUrlPrefix } from "./url-protocol-De2aQ55s.mjs"; import { n as normalizeMediaReferenceSource, r as parseInboundMediaUri, t as MediaReferenceError } from "./inbound-media-uri-Bc4RWtcM.mjs"; import { d as resolveMediaBufferPath, s as getMediaDir } from "./store-7jnF8fA5.mjs"; import path from "node:path"; import fs from "node:fs/promises"; import { safeFileURLToPath } from "@openclaw/fs-safe/advanced"; //#region src/media/media-reference.ts /** Classifies media reference schemes before local resolution or sandbox rewriting. */ function classifyMediaReferenceSource(source, options) { const allowDataUrl = options?.allowDataUrl ?? true; const looksLikeWindowsDrivePath = /^[a-zA-Z]:[\\/]/.test(source); const hasScheme = /^[a-z][a-z0-9+.-]*:/i.test(source); const isFileUrl = /^file:/i.test(source); const isHttpUrl = hasHttpUrlPrefix(source); const isDataUrl = /^data:/i.test(source); const isMediaStoreUrl = /^media:\/\//i.test(source); return { hasScheme, hasUnsupportedScheme: hasScheme && !looksLikeWindowsDrivePath && !isFileUrl && !isHttpUrl && !isMediaStoreUrl && !(allowDataUrl && isDataUrl), isDataUrl, isFileUrl, isHttpUrl, isMediaStoreUrl, looksLikeWindowsDrivePath }; } function maybeLocalPathFromSource(source) { if (/^file:/i.test(source)) try { return safeFileURLToPath(source); } catch { return null; } if (source.startsWith("~")) return resolveUserPath(source); if (path.isAbsolute(source)) return source; return null; } function relativePathEscapesBase(relativePath) { return relativePath === ".." || relativePath.startsWith("../") || relativePath.startsWith("..\\") || path.isAbsolute(relativePath); } async function resolvePathForContainment(candidate) { try { return await fs.realpath(candidate); } catch { return path.resolve(candidate); } } /** Converts a managed inbound path to a URI without exposing paths outside its store. */ function buildInboundMediaUriFromPath(source) { const localPath = maybeLocalPathFromSource(source.trim()); if (!localPath) return; const inboundDir = path.resolve(getMediaDir(), "inbound"); const relativePath = path.relative(inboundDir, path.resolve(localPath)); if (!relativePath || relativePathEscapesBase(relativePath) || relativePath.includes(path.sep) || relativePath.includes("\\")) return; try { return parseInboundMediaUri(`media://inbound/${relativePath}`)?.normalizedSource; } catch { return; } } /** Rewrites inbound media-store URIs to sandbox-relative paths for staged agent inputs. */ function resolveMediaReferenceSandboxPath(source, inboundDir = "media/inbound") { const normalizedSource = normalizeMediaReferenceSource(source); const uri = parseInboundMediaUri(normalizedSource); if (!uri) return { resolved: normalizedSource }; return { resolved: path.posix.join(inboundDir.replace(/\\/g, "/"), uri.id), rewrittenFrom: uri.normalizedSource }; } /** Resolves inbound media:// URIs or first-level inbound file paths to concrete store files. */ async function resolveInboundMediaReference(source) { const normalizedSource = normalizeMediaReferenceSource(source); if (!normalizedSource) return null; const uri = parseInboundMediaUri(normalizedSource); if (uri) return { ...uri, physicalPath: await resolveInboundMediaPath(uri.id, uri.normalizedSource), sourceType: "uri" }; const localPath = maybeLocalPathFromSource(normalizedSource); if (!localPath) return null; const rawInboundDir = path.resolve(getMediaDir(), "inbound"); const rawResolvedPath = path.resolve(localPath); const rawRel = path.relative(rawInboundDir, rawResolvedPath); const rel = rawRel && !relativePathEscapesBase(rawRel) ? rawRel : path.relative(await resolvePathForContainment(rawInboundDir), await resolvePathForContainment(localPath)); if (!rel || relativePathEscapesBase(rel) || rel.includes(path.sep)) return null; return { id: rel, normalizedSource, physicalPath: await resolveInboundMediaPath(rel, normalizedSource), sourceType: "path" }; } /** Resolves a media reference while preserving whether it belongs to the inbound store. */ async function resolveMediaReferenceLocalPathInfo(source) { const normalizedSource = normalizeMediaReferenceSource(source); const inboundReference = await resolveInboundMediaReference(normalizedSource); return inboundReference ? { kind: "inbound", path: inboundReference.physicalPath } : { kind: "local", path: normalizedSource }; } /** Converts inbound media references for callers that need a direct local file path. */ async function resolveMediaReferenceLocalPath(source) { return (await resolveMediaReferenceLocalPathInfo(source)).path; } async function resolveInboundMediaPath(id, source) { try { return await resolveMediaBufferPath(id, "inbound"); } catch (err) { throw new MediaReferenceError("invalid-path", err instanceof Error ? err.message : `Invalid media reference: ${source}`, { cause: err }); } } //#endregion export { resolveMediaReferenceLocalPathInfo as a, resolveMediaReferenceLocalPath as i, classifyMediaReferenceSource as n, resolveMediaReferenceSandboxPath as o, resolveInboundMediaReference as r, buildInboundMediaUriFromPath as t };