openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
163 lines (162 loc) • 7.14 kB
JavaScript
import { c as resolveUserPath } from "./home-dir-BPhrG-aM.js";
import "./utils-P__uGsPB.js";
import { t as hasHttpUrlPrefix } from "./url-protocol-OU3K-ySz.js";
import { d as resolveMediaBufferPath, s as getMediaDir } from "./store-Ph57PZ9P.js";
import { a as safeFileURLToPath } from "./local-file-access-B1HKoQAc.js";
import path from "node:path";
import fs from "node:fs/promises";
//#region src/media/media-reference.ts
/** Error raised when a media reference cannot be mapped to an allowed local media file. */
var MediaReferenceError = class extends Error {
constructor(code, message, options) {
super(message, options);
this.code = code;
this.name = "MediaReferenceError";
}
};
/** Strips legacy MEDIA: prefixes while preserving canonical media:// references. */
function normalizeMediaReferenceSource(source) {
const trimmed = source.trim();
if (/^media:\/\//i.test(trimmed)) return trimmed;
return trimmed.replace(/^\s*MEDIA\s*:\s*/i, "").trim();
}
/** 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);
}
}
/** Parses canonical inbound media-store URIs and rejects nested or cross-bucket references. */
function parseInboundMediaUri(source) {
const normalizedSource = normalizeMediaReferenceSource(source);
if (!/^media:\/\//i.test(normalizedSource)) return null;
let parsed;
try {
parsed = new URL(normalizedSource);
} catch (err) {
throw new MediaReferenceError("invalid-path", `Invalid media URI: ${normalizedSource}`, { cause: err });
}
if (parsed.hostname !== "inbound") throw new MediaReferenceError("path-not-allowed", `Unsupported media URI location: ${parsed.hostname || "(missing)"}`);
if (parsed.username || parsed.password || parsed.search || parsed.hash) throw new MediaReferenceError("invalid-path", `Invalid media URI: ${normalizedSource}`);
let id;
try {
id = decodeURIComponent(parsed.pathname.replace(/^\/+/, ""));
} catch (err) {
throw new MediaReferenceError("invalid-path", `Invalid media URI: ${normalizedSource}`, { cause: err });
}
if (!id || id === "." || id === ".." || id.includes("/") || id.includes("\\") || id.includes("\0")) throw new MediaReferenceError("invalid-path", `Invalid media URI: ${normalizedSource}`);
return {
id,
normalizedSource
};
}
/** 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 { parseInboundMediaUri as a, resolveMediaReferenceLocalPathInfo as c, normalizeMediaReferenceSource as i, resolveMediaReferenceSandboxPath as l, buildInboundMediaUriFromPath as n, resolveInboundMediaReference as o, classifyMediaReferenceSource as r, resolveMediaReferenceLocalPath as s, MediaReferenceError as t };