openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
126 lines (125 loc) • 5.45 kB
JavaScript
import { o as resolveUserPath } from "./home-dir-Dc6nN6HB.mjs";
import "./utils-DgZn2dLu.mjs";
import { t as hasHttpUrlPrefix } from "./url-protocol-OU3K-ySz.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-CP-GWHOW.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;
}
}
async function resolveInboundMediaUri(normalizedSource) {
const uri = parseInboundMediaUri(normalizedSource);
if (!uri) return null;
return {
...uri,
physicalPath: await resolveInboundMediaPath(uri.id, uri.normalizedSource),
sourceType: "uri"
};
}
/** 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 uriSource = await resolveInboundMediaUri(normalizedSource);
if (uriSource) return uriSource;
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 };