UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

101 lines • 3.77 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 {} from "../../io/types.js"; import { RAMIndexCacheStore } from "../../cache/index/ram.js"; import { RAMAccessor } from "../../accessor/ram.js"; import { FileStat, FileType } from "../../types.js"; import { RAMVFS } from "../ram/ram.js"; import { activePipe } from "../../core/dev/device.js"; import { keyStat } from "../../core/dev/stat.js"; import { childKeys } from "../../core/ram/readdir.js"; import { gnuBasename, norm } from "../../utils/path.js"; import { DevStore } from "./store.js"; /** * The index every DevVFS runs under: it never keeps an entry, * because a path-only index would publish one session's descriptors to * another. Mirrors the `NULL_INDEX` Python's registry uses for DevVFS. */ export class DevIndex extends RAMIndexCacheStore { seed() { return undefined; } put() { return Promise.resolve(); } setDir() { return Promise.resolve([]); } refillDir() { return Promise.resolve([]); } setPartialDir() { return Promise.resolve(); } addEntry() { return Promise.resolve(); } } export class DevVFS extends RAMVFS { devices; store; accessor; constructor(devices = new DevStore()) { super(); this.devices = devices; this.store = devices; this.accessor = new RAMAccessor(this.store); } forSession(owner) { return new DevVFS(this.devices.forSession(owner)); } allocateInput(pipe) { return this.store.files.allocateInput(pipe); } releaseInput(path, allocation) { const files = this.store.files; if (files.releaseInput(path, allocation)) { this.store.modified.delete(path.slice(4)); this.store.attrs.delete(path.slice(4)); } } /** * The RAM listing, each child as `stat` answers it, so a device or a live * descriptor lists as one. Mirrors Python's `readdir`. */ async readdir(path) { return (await childKeys(this.accessor, path)).map((key) => keyStat(this.accessor, key) ?? new FileStat({ name: gnuBasename(key), type: FileType.DIRECTORY })); } /** Null: a walk of /dev is one readdir. Mirrors Python's `read_tree`. */ readTree(_path) { return Promise.resolve(null); } /** * A whole read of a pipe answers its stream. The stream is finite: a * command that consumes a whole input reads the refusing read, and only * the two bounded streaming commands opt into the endless source * (`commands/builtin/dev`). */ read(path, handed, offset = 0, size = null, signal) { // Not async, so a pipe is bound as the read opens and a descriptor reads // on after the pipe's path is released. if (offset === 0 && size === null) { const pipe = activePipe(this.accessor, norm(path.mountPath)); if (pipe !== null) return pipe.stream(path, signal); } return super.read(path, handed, offset, size, signal); } } //# sourceMappingURL=dev.js.map