@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
94 lines • 4.3 kB
TypeScript
import type { Accessor } from '../../accessor/base.ts';
import type { CLI } from '../../commands/cli/types.ts';
import { type Clock } from '../../utils/clock.ts';
import type { CLIInstall } from './types.ts';
/**
* Installed CLIs, keyed by head word.
*
* Fully separate from the mount registry: a CLI exists because it was
* installed (YAML `clis:` section or registerCli), never because
* storage was mounted. Install is fail-loud: a bad name, a colliding
* name, or a config the spec's configModel rejects throws at install
* time, so a workspace that loads has only valid entries.
*
* The lifecycle is host-side only, and must stay that way: install and
* uninstall are called by the program embedding mirage, never by a line
* the agent types, so an agent cannot take away the tools it was given.
* Do not add an `install`/`uninstall` shell builtin. What an agent can
* do is shadow a head word with a shell function, which is bash's own
* rule, reversible with `unset -f`, bypassable with `command <name>`,
* and visible through `type -a`. Pinning a head word against that
* belongs in the policy layer's `preCommand`, since it is a
* per-deployment call rather than a property of the registry.
*/
export declare class CLIRegistry {
private readonly peers;
private readonly clock;
private readonly installs;
private readonly retiring;
private shutdown;
private clocksBound;
/**
* @param peers the mounts' accessors, which an install may share an account with.
* @param clock the workspace's clock, which each install's accessor reads
* time and waits on.
*/
constructor(peers?: () => readonly Accessor[], clock?: Clock);
/**
* Refuse a head word no CLI may be installed under: not a single word,
* or one that collides with a shell builtin, a keyword or a general
* command. Mirrors Python's `CLIRegistry.check_name`.
*/
checkName(name: string): void;
/**
* Install a CLI under a head word. The name must be a single word and
* must not collide with another installed CLI, a shell builtin, or a
* general command (a runtime capture of the same name is fine: the
* policy steers per line).
*/
install(name: string, spec: CLI, config?: Record<string, unknown> | null): CLIInstall;
private validateConfig;
/**
* Hand every install's accessor the workspace clock, once the workspace is
* built; an install made later takes it at once. Each install holds on its
* own, so removing one leaves another that shares its client holding.
* Mirrors Python's `CLIRegistry.bind_clocks`.
*
* @throws Error when another workspace holds a client on another clock;
* nothing stays bound.
*/
bindClocks(): void;
/**
* Let go of the workspace clock on every install's accessor, as the
* workspace closes or fails to come to be. Mirrors Python's
* `CLIRegistry.release_clocks`.
*/
releaseClocks(): void;
/**
* Remove an installed CLI now and close its accessor once the lines running
* on it and their output streams are done; its head word stops resolving
* (127) at once.
*/
uninstall(name: string): Promise<void>;
/**
* Close every install's accessor, including the ones still being removed,
* as the workspace closes; one failed close does not skip the rest.
*
* The workspace aborts its lines before this runs, so a leaf still on an
* install is unwinding (or ignoring its abort): it gets `ABORT_JOIN_MS` to
* settle before its accessor goes, rather than meeting a closed client
* mid-request, and no longer, so one that never settles cannot hold the
* workspace open. Each close is tracked, as a removal is, so a second close
* waits for the clients the first is still closing.
*/
close(): Promise<void>;
/** Keep `closing` in `retiring` until it settles, so `close` waits for it. */
private track;
/** Look up an installation by head word. */
get(name: string): CLIInstall | null;
/** Snapshot of the installed CLIs keyed by head word. */
items(): Map<string, CLIInstall>;
/** The installed head words. */
names(): ReadonlySet<string>;
}
//# sourceMappingURL=registry.d.ts.map