UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

69 lines • 3.23 kB
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. ========= // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ========= Copyright 2026 @ Strukto.AI All Rights Reserved. ========= import { FileType } from "../../types.js"; import { rstripSlash } from "../../utils/slash.js"; import { download, upload, createDirectory, listDirectoryContents } from "./client.js"; import { backendPath } from "./path.js"; import { read } from "./read.js"; import { stat } from "./stat.js"; import { write } from "./write.js"; import { eisdir } from "../../errors/fs.js"; /** * Copy a directory tree file by file, filling `copied` with each source * entry copied, by backend path, as its listing showed it, for a move to * delete only an entry still as it was. Mirrors Python's `copy_tree`. */ export async function copyTree(accessor, remoteSrc, remoteDst, copied = null) { await createDirectory(accessor, remoteDst); for (const entry of await listDirectoryContents(accessor, remoteSrc)) { const name = rstripSlash(entry.path).split('/').pop() ?? ''; const childDst = `${rstripSlash(remoteDst)}/${name}`; if (entry.isDirectory) { await copyTree(accessor, entry.path, childDst, copied); } else { await upload(accessor, childDst, await download(accessor, entry.path), true); } copied?.set(entry.path, entry); } } export async function copy(accessor, src, dst, recursive = false) { const srcStat = await stat(accessor, src); // Same-path guard runs after stat (and the non-recursive directory check) // so a missing source or `cp` of a directory still raises. const samePath = backendPath(accessor.config, src) === backendPath(accessor.config, dst); if (srcStat.type === FileType.DIRECTORY) { if (!recursive) throw eisdir(src.virtual); if (samePath) return; const remoteSrc = backendPath(accessor.config, src); const remoteDst = backendPath(accessor.config, dst); if (remoteDst.startsWith(remoteSrc + '/')) { // Copying a directory into its own subtree creates the destination // inside the source, so the walk would descend into the fresh copy // forever. Refuse before any create_directory/upload. throw new Error(`cannot copy a directory, '${src.virtual}', into itself, '${dst.virtual}'`); } await copyTree(accessor, remoteSrc, remoteDst); return; } if (samePath) { // Copying a file onto itself would re-upload it; skip. return; } const data = await read(accessor, src); await write(accessor, dst, data); } //# sourceMappingURL=copy.js.map