@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
118 lines • 5.82 kB
TypeScript
import type { Files } from '../../workspace/files.ts';
import type { SessionState } from '../../workspace/session/session.ts';
import { type WorkspaceBinding } from '../binding.ts';
import type { MountType, PipeWriter, RunResult, SandboxPipe } from '../types.ts';
/** A served prefix's SFTP server, as `session` (node's PipeServer). */
export interface PipeServer {
/** Answer the client's requests on its pipe until it closes it. */
serve(reader: ReadableStream<Uint8Array>, writer: PipeWriter): Promise<void>;
/** Land every write the client has been told is done. */
sync(): Promise<void>;
}
/**
* Where node puts the SFTP server a served prefix's pipe runs: as
* `session`, each request run by `run`. Core has none of its own:
* MountCore and ssh2 are node's.
*/
export declare function registerPipeServer(make: (files: Files, session: SessionState, run: (op: () => Promise<void>) => Promise<void>) => PipeServer): void;
/**
* Each mountpoint a `/proc/mounts` lists, with its type. The kernel
* escapes a space in a path as `\040`; that is undone. A point mounted
* twice keeps the later (top) entry's type.
*/
export declare function mountTable(text: string): Map<string, string>;
/**
* The prefixes a sandbox mounts: each one under no other. A prefix under
* another is served through it. The root and the workspace's devices are
* left out: a sandbox's `/` and `/dev` are its own, and nothing can be
* mounted over them.
*/
export declare function topPrefixes(prefixes: Iterable<string>): string[];
/**
* The workspace prefixes one sandbox sees, by its `mountType`.
*
* `ssh`: every prefix the session sees is served from the host over SFTP,
* and the sandbox mounts each with `sshfs -o passive` on a pipe the
* provider holds open, so every file op in the sandbox is an op of the
* session, held to its hides, modes and policies. `fuse`: a mirage in the
* sandbox serves its prefixes itself and the host serves none. `both`: a
* prefix the sandbox already mounts is its own, and the host serves the
* rest. A prefix the sandbox serves itself is out of the session's rules'
* reach, so a line is refused while a hide, rule or mode cap covers
* anything under one, hidden mounts included.
*
* Each line first brings the mounts in step with the workspace: a mount
* added or shown since is served, one hidden is dropped, and a server
* that ended is started again. While a rule reaches under a prefix, the
* sandbox's own mounts are read again for each line, so one it mounted
* since is refused too. A server runs outside the line that started it,
* so it answers as the session alone and never with a grant that line was
* given. What the host serves shows one session's view, and a program a line starts may outlive it, so under `ssh` and
* `both` the sandbox belongs to the first session it serves or runs a line
* for, until it is closed: a line of another session is refused rather
* than shown, or showing, the first one's view, and so is a session made
* again under the same id, which a program the first one started could
* otherwise read through. Under `fuse` the host serves nothing, and any
* session may run.
*/
export declare class SandboxMounts {
private readonly runtime;
private readonly mountType;
private readonly run;
private readonly openPipe;
private session;
private readonly lines;
private readonly served;
private seen;
private lock;
/**
* @param runtime the runtime's name, for messages.
* @param mountType ssh, fuse or both.
* @param run runs one setup command in the sandbox.
* @param openPipe starts a command in the sandbox with its stdin and
* stdout held open.
*/
constructor(runtime: string, mountType: MountType, run: (line: string, signal?: AbortSignal) => Promise<RunResult>, openPipe: (command: string) => Promise<SandboxPipe>);
get fileEffectsDispatched(): boolean;
/**
* Bring the mounts in step, then refuse a line that may not run: one the
* session's restrictions reach past the dispatcher is refused as a policy
* refuses it (126), one the sandbox cannot serve because another session
* holds it fails (1). Null when the line may run.
*/
admit(binding: WorkspaceBinding | null, signal?: AbortSignal): Promise<RunResult | null>;
/** Unmount every served prefix and end its SFTP server. */
close(): Promise<void>;
/**
* Land every write a served prefix's server has acknowledged.
*
* sshfs answers close(2) once the file's writes are acknowledged and
* sends the SFTP close that lands them afterwards, so a line can end
* before they land. Its programs have closed their files by then, so
* this lands every one, outside the line as that close would, and
* `fileEffectsDispatched` holds. Each landing keeps the line's write
* admission alone: a capture waiting for the line lets it through rather
* than waiting on it. Then the line lends its admission to no more
* requests, and waits out the ones it lent it to, so a capture never
* reads past one still running.
*/
sync(): Promise<void>;
/**
* Run one served request with the write admission of the latest line
* running in the sandbox, from its `admit` to its `sync`, and none
* between lines. A program's file op then counts as its line's, so a
* capture waiting for that line lets it land rather than holding it while
* the line waits on it.
*/
private lend;
private unserved;
private locked;
private reconcile;
private serve;
/** Remove the helper's setup log; keeping it only costs a file. */
private dropLog;
private table;
private drop;
private unmount;
}
//# sourceMappingURL=mounts.d.ts.map