UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

93 lines • 4.11 kB
import { ContentType, DirListing, FileStat, FileType } from "../../types.js"; import { enoent, enotdir } from "../../errors/fs.js"; import { WandbAPIError } from "./errors.js"; import { LEAVES, parts, runVars, safeName } from "./pathing.js"; import { RUN_EXISTS } from "./queries.js"; import { rstripSlash } from "../../utils/slash.js"; export function row(name, directory, size = null, content = null) { if (directory) return new FileStat({ name, type: FileType.DIRECTORY, size: 0 }); return new FileStat({ name, type: FileType.FILE, size, content }); } function leaf(name) { return row(name, false, null, name.endsWith('.json') ? ContentType.JSON : ContentType.TEXT); } export function fileTree(files) { const directories = new Map([['', new Map()]]); for (const file of files) { const segments = file.name.split('/'); if (!segments.every(safeName)) throw new WandbAPIError('W&B unsafe run file name'); let parent = ''; for (const [depth, child] of segments.entries()) { const node = row(child, depth < segments.length - 1, file.sizeBytes, ContentType.BINARY); const children = directories.get(parent) ?? new Map(); const previous = children.get(child); if (previous && previous.type !== node.type) throw new WandbAPIError('W&B file and directory name collision'); children.set(child, node); directories.set(parent, children); parent = parent ? parent + '/' + child : child; } } return new Map([...directories].map(([parent, children]) => [parent, [...children.values()]])); } /** * A run's `files/` directory, or one below it, from the one call that lists * every file of the run: each directory below the listed one is proven by the * same call, so it rides along as a seed. Mirrors `_run_files` in * `mirage/core/wandb/readdir.py`. */ function runFiles(path, prefix, files) { if (prefix && files.some((f) => f.name === prefix)) throw enotdir(path); const tree = fileTree(files); const entries = tree.get(prefix); if (!entries) throw enoent(path); const stem = prefix ? prefix + '/' : ''; const seeds = new Map(); for (const [directory, rows] of tree) { if (directory.startsWith(stem) && directory !== prefix) { seeds.set(directory.slice(stem.length), rows); } } return new DirListing({ entries, seeds }); } /** A directory's rows. Mirrors `readdir` in `mirage/core/wandb/readdir.py`. */ export async function readdir(accessor, pathSpec, index, limit) { const path = pathSpec.pattern ? pathSpec.dir : pathSpec; const ps = parts(accessor, path); if (ps.length === 0) return [...new Set(accessor.config.entities)].map((e) => row(e, true)); let nodes; if (ps.length === 1) nodes = await accessor.client.projects(ps[0] ?? '', limit); else if (ps.length === 2) nodes = await accessor.client.runs(ps[0] ?? '', ps[1] ?? '', limit); else if (ps.length === 3) { // A run the stored listing of its project names is not asked about again. const key = rstripSlash(path.virtual); const listed = await index?.listDir(key.slice(0, key.lastIndexOf('/')) || '/', key); if (listed?.entries?.includes(key) !== true) await accessor.client.run(runVars(ps), RUN_EXISTS); return [...LEAVES.map(leaf), row('files', true)]; } else if (ps[3] === 'files') { const files = await accessor.client.files(runVars(ps)); return runFiles(path, ps.slice(4).join('/'), files); } else if (LEAVES.includes(ps[3] ?? '')) throw enotdir(path); else throw enoent(path); const rows = nodes.map((node) => { if (!safeName(node.name)) throw new WandbAPIError('W&B unsafe object name'); return row(node.name, true); }); if (limit !== undefined && rows.length >= limit) return new DirListing({ entries: rows, partial: true }); return rows; } //# sourceMappingURL=readdir.js.map