UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

86 lines • 3.4 kB
import { detectFileType } from "../../commands/builtin/file_sniff.js"; import { FileStat, FileType } from "../../types.js"; import { MIME_FOR_EXTENSION, MIME_FOR_FILE_TYPE, MODEL_IMAGE_MIMES, READ_FILE_MIME, TEXT_FILE_EXTENSIONS, } from "./read_file/constants.js"; function extOf(path) { const dot = path.lastIndexOf('.'); const slash = path.lastIndexOf('/'); if (dot < 0 || dot < slash) return ''; return path.slice(dot + 1).toLowerCase(); } function filenameOf(path) { const slash = path.lastIndexOf('/'); const filename = slash < 0 ? path : path.slice(slash + 1); return filename === '' ? 'file' : filename; } function mimeForExtension(path) { const ext = extOf(path); return (MIME_FOR_EXTENSION[ext] ?? (TEXT_FILE_EXTENSIONS.has(ext) ? READ_FILE_MIME.PLAIN_TEXT : undefined)); } function mimeForDetectedType(type) { return MIME_FOR_FILE_TYPE[type] ?? READ_FILE_MIME.BINARY; } function mimeFor(path, bytes, stat) { const extensionMime = mimeForExtension(path); if (extensionMime !== undefined) return extensionMime; if (extOf(path) !== '') return READ_FILE_MIME.BINARY; return mimeForDetectedType(detectFileType(bytes, stat)); } /** * A file a model takes as media, from the bytes already read: an image * the models read, or a PDF. Its extension decides; a name with none is * sniffed from its bytes alone, so no stat is needed beyond the read. * Undefined for anything else, which the text read answers. */ export function mediaOf(path, data) { const ext = extOf(path); const mimeType = ext === '' ? mimeForDetectedType(detectFileType(data, new FileStat({ name: filenameOf(path), type: FileType.FILE }))) : MIME_FOR_EXTENSION[ext]; if (mimeType === undefined) return undefined; const base = { path, mimeType, bytes: data.byteLength }; if (MODEL_IMAGE_MIMES.has(mimeType)) return { ...base, kind: 'image', data }; if (mimeType === READ_FILE_MIME.PDF) { return { ...base, kind: 'file', data, filename: filenameOf(path) }; } return undefined; } function isTextMime(mimeType) { return (mimeType.startsWith('text/') || mimeType === READ_FILE_MIME.JSON || mimeType === READ_FILE_MIME.SVG); } export async function readWorkspaceFile(vfs, path, reader) { const stat = await vfs.stat(path); if (stat.type === FileType.DIRECTORY) { throw new Error(`Cannot read directory as a file: ${path}`); } const data = reader === undefined ? await vfs.read(path, { raw: true }) : await reader(path); const mimeType = mimeFor(path, data, stat); const base = { path, mimeType, bytes: data.byteLength }; if (isTextMime(mimeType)) { return { ...base, kind: 'text', content: new TextDecoder('utf-8', { fatal: false }).decode(data), }; } if (MODEL_IMAGE_MIMES.has(mimeType)) { return { ...base, kind: 'image', data }; } if (mimeType === READ_FILE_MIME.PDF) { return { ...base, kind: 'file', data, filename: filenameOf(path) }; } return { ...base, kind: 'binary', note: `Binary file ${path} (${mimeType}, ${String(data.byteLength)} bytes). ` + 'Use the shell tool with commands such as head, file, wc and od to inspect it.', }; } //# sourceMappingURL=read_file.js.map