UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

118 lines • 5.82 kB
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