/** * Управляет кешированием файлов (внутренних, внешних, промптов). * Сохраняет файлы в указанной папке с UUID в имени. */ import { TFile, normalizePath } from 'obsidian'; import LLMAgentPlugin from 'main'; import { v4 as uuidv4 } from 'uuid'; // Нужно установить: npm install uuid @types/uuid import { getMimeType } from 'src/utils/fileUtils'; export class CacheManager { private plugin: LLMAgentPlugin; constructor(plugin: LLMAgentPlugin) { this.plugin = plugin; } /** * Возвращает абсолютный путь к папке кеша */ public getCacheFolderPath(): string { const cacheFolder = this.plugin.settings.refCacheFolder; // Если путь относительный, делаем его относительно vault if (!this.isAbsolutePath(cacheFolder)) { return this.plugin.app.vault.adapter.getBasePath() + '/' + cacheFolder; } return cacheFolder; } /** * Проверяет, является ли путь абсолютным */ private isAbsolutePath(path: string): boolean { // Windows: C:\, D:\, и т.д. if (/^[a-zA-Z]:\\/.test(path)) return true; // Unix: / if (path.startsWith('/')) return true; return false; } /** * Создает папку кеша, если она не существует */ private async ensureCacheFolderExists(): Promise { const cachePath = this.getCacheFolderPath(); if (!window.require) return; // Web версия const fs = window.require('fs').promises; try { await fs.access(cachePath); } catch { await fs.mkdir(cachePath, { recursive: true }); } } /** * Кеширует файл из vault */ async cacheVaultFile(file: TFile): Promise<{ uuid: string; cachedPath: string }> { await this.ensureCacheFolderExists(); const uuid = uuidv4(); const extension = file.extension; const cachedFileName = `${file.basename}-${uuid}.${extension}`; const content = await this.plugin.app.vault.readBinary(file); const cacheFolderPath = this.getCacheFolderPath(); const cachedFilePath = `${cacheFolderPath}/${cachedFileName}`; if (!window.require) { throw new Error('Кеширование доступно только в desktop версии'); } const fs = window.require('fs').promises; await fs.writeFile(cachedFilePath, Buffer.from(content)); return { uuid: uuid, cachedPath: cachedFileName // Относительный путь }; } /** * Кеширует внешний файл */ async cacheExternalFile(absolutePath: string): Promise<{ uuid: string; cachedPath: string; name: string; mimeType: string }> { await this.ensureCacheFolderExists(); if (!window.require) { throw new Error('Кеширование доступно только в desktop версии'); } const fs = window.require('fs').promises; const path = window.require('path'); const content = await fs.readFile(absolutePath); const fileName = path.basename(absolutePath); const extension = path.extname(fileName).substring(1); const baseName = path.basename(fileName, path.extname(fileName)); const uuid = uuidv4(); const cachedFileName = `${baseName}-${uuid}.${extension}`; const cacheFolderPath = this.getCacheFolderPath(); const cachedFilePath = `${cacheFolderPath}/${cachedFileName}`; await fs.writeFile(cachedFilePath, content); return { uuid: uuid, cachedPath: cachedFileName, name: fileName, mimeType: getMimeType(fileName) }; } /** * Восстанавливает кеш из исходного файла */ async restoreCache(sourcePath: string, type: 'file' | 'prompt' | 'external', uuid: string): Promise { if (type === 'external') { // Для внешних файлов sourcePath - абсолютный const result = await this.cacheExternalFile(sourcePath); return result.cachedPath; } else if (type === 'file') { // Для внутренних файлов sourcePath - относительный const file = this.plugin.app.vault.getAbstractFileByPath(sourcePath); if (file instanceof TFile) { const result = await this.cacheVaultFile(file); return result.cachedPath; } } else if (type === 'prompt') { // Для промптов const promptsFolder = this.plugin.settings.promptsFolder; const fullPath = this.isAbsolutePath(sourcePath) ? sourcePath : `${promptsFolder}/${sourcePath}`; const file = this.plugin.app.vault.getAbstractFileByPath(fullPath); if (file instanceof TFile) { const result = await this.cacheVaultFile(file); return result.cachedPath; } } throw new Error(`Не удалось восстановить кеш для ${sourcePath}`); } /** * Читает содержимое из кеша */ async readCachedFile(cachedPath: string): Promise { const cacheFolderPath = this.getCacheFolderPath(); const fullPath = `${cacheFolderPath}/${cachedPath}`; if (!window.require) { throw new Error('Чтение кеша доступно только в desktop версии'); } const fs = window.require('fs').promises; const buffer = await fs.readFile(fullPath); return buffer.buffer; } /** * Проверяет существование кешированного файла */ async cachedFileExists(cachedPath: string): Promise { const cacheFolderPath = this.getCacheFolderPath(); const fullPath = `${cacheFolderPath}/${cachedPath}`; if (!window.require) return false; const fs = window.require('fs').promises; try { await fs.access(fullPath); return true; } catch { return false; } } }