@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
106 lines • 5.04 kB
TypeScript
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