export function sanitizeOpenClawArchiveName(value: string) { const normalized = String(value || "") .normalize("NFKC") .replace(/[\\/:*?"<>|\u0000-\u001f]/g, "-") .replace(/\s+/g, " ") .replace(/^\.+|\.+$/g, "") .trim() .slice(0, 120); return normalized || `Claw文件夹-${Date.now()}`; } function sanitizeArchivePathSegment(value: string, fallback: string) { const normalized = String(value || "") .normalize("NFKC") .replace(/[\\:*?"<>|\u0000-\u001f]/g, "-") .replace(/\s+/g, " ") .replace(/^\.+|\.+$/g, "") .trim() .slice(0, 160); return normalized || fallback; } export function sanitizeOpenClawArchiveEntryPath( relativePath: string, fallbackFileName: string, index: number ) { const fallback = sanitizeArchivePathSegment( fallbackFileName, `文件-${index + 1}` ); const segments = String(relativePath || fallback) .replace(/\\/g, "/") .split("/") .filter((segment) => segment && segment !== "." && segment !== "..") .slice(0, 30) .map((segment, segmentIndex) => sanitizeArchivePathSegment(segment, `目录-${segmentIndex + 1}`) ); return (segments.length > 0 ? segments : [fallback]).join("/"); } export function countOpenClawArchiveRootFolders(paths: string[]) { return new Set( paths .map((path) => String(path || "").replace(/\\/g, "/").split("/")[0]) .filter(Boolean) ).size; }