UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

106 lines • 5.04 kB
import type { RuntimeFiles } from '../../files.ts'; import type { MontyFsBits } from './loader.ts'; import { type Clock } from '../../../utils/clock.ts'; /** * Monty's OS callbacks: every path a guest names is the workspace's. * * This is monty's tier of the interception taxonomy: the engine calls * one host callback per operation and takes back a value (or a promise * of one) or NOT_HANDLED, which the sandbox raises as the call's * default refusal. Every path goes to the file adapter, and nothing is * kept aside: structure is open (a listing, whether a name is a * directory or a link) and content goes only through the runtime's * view (`RuntimeFiles.serves`: the announced mounts and what a link * reaches), so a guest lists what a shell lists and reads and writes * nothing the view withholds. The python twin answers the same way. * Declining is reserved for an operation these callbacks do not implement. * * Monty hands the callbacks whole-file calls: an open, then reads of the * whole file and appends of each new write. So an open applies its * mode's effect on the mount (`RuntimeFiles.open`, closed again at once) * and nothing else, and each write after it ships only its own bytes. * * Args: * bits: the loaded engine's callback pieces (NOT_HANDLED sentinel * and the MontyFileHandle an `open` answer must be). * env: the run's environment, readable both ways python's monty * spells it (`os.getenv` and `os.environ`). * files: the execution's file adapter, or null outside a workspace, * where every path is out of view. */ export declare class MontyFs { private readonly bits; private readonly notHandled; private readonly fileHandle; private readonly env; private readonly files; /** * The workspace's system clock the run starts on, null for the host's: * the engine calls back from native code, which the line's binding does * not reach. */ readonly clock: Clock | null; constructor(bits: MontyFsBits, env: Record<string, string>, files: RuntimeFiles | null); private wall; readonly handle: (name: string, args: unknown[], kwargs?: Record<string, unknown>) => unknown; /** Outside a workspace there is no filesystem: probes answer false, the rest refuse. */ private unbound; private op; /** * The subtree under `path` in one call, for walk, scandir and glob. * * One listing per directory, each entry kinded by the stat that * listing already made, so a walk costs what `find` costs instead of * the three probes per entry monty's default asks. The glob's pattern * is only a hint, since monty filters the tree itself. A directory * below `path` that will not list reads as empty, as it does on * monty's own mounts; a tree past SCAN_MAX_ENTRIES raises * RuntimeError, because a walk would swallow an OSError. * * Args: * path: the directory to scan, or a file to describe. * kwargs: `max_depth` (the deepest level to return, none for the * whole tree) and `follow_symlinks` (descend into linked * directories). * files: the execution's file adapter. */ private scan; /** * Whether a listed entry is a directory and whether a regular file. * The listing's own stat answers for a path in the view, except for a * link, whose row is the link's and not what it reaches, and a row the * listing could not stat. Those, and every path outside the view, ask * the view as `Path.is_dir` would, so a file the view withholds (the * history beside the mounts) is not offered as one. A stat that fails * leaves the entry neither, CPython's rule inside `os.walk`. */ private kind; /** * Whether the name plane holds a symlink at `path`, asked through * readlink. A refusal the backend did not mean as "no link here" * comes out as itself (NOT_A_LINK), which is what CPython's own * `Path.is_symlink` does. */ private isLink; private open; /** Replace a file; the return is python's: characters for text, bytes for bytes. */ private write; /** * Send only the appended bytes; monty hands an append nothing else. * Re-sending everything written so far turns a write loop quadratic, * so a mount with its own append op carries just these bytes, and the * adapter falls back to a whole-file write only for the mount without * one. The return is python's: characters for text, bytes for bytes. */ private append; /** * Create a directory, keeping pathlib's flags: `parents` rides through * to the backend op, which takes it; `exist_ok` is answered here, * since the op has no such argument and backends differ on whether * creating an existing directory raises at all. `exist_ok` forgives * an existing directory only — a file at the target still raises, * pathlib's own rule. */ private mkdir; } //# sourceMappingURL=fs.d.ts.map