@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
178 lines • 8.4 kB
JavaScript
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
import { DISPATCH_BUILDERS } from "../../commands/builtin/generic/crossmount/constants.js";
import { runDu } from "../../commands/builtin/generic/crossmount/du.js";
import { runFind } from "../../commands/builtin/generic/crossmount/find.js";
import { handleCrossMount } from "../../commands/builtin/generic/crossmount/route.js";
import { runDispatch } from "../../commands/builtin/generic_bind/dispatch.js";
import { UsageError } from "../../commands/errors.js";
import { CommandTimeoutError } from "../../errors/types.js";
import { readFailExitCode } from "../../commands/spec/usage.js";
import { IOResult } from "../../io/types.js";
import { encodeText } from "../../shell/bytes.js";
import { compareCodePoints } from "../../utils/sort.js";
import { stripSlash } from "../../utils/slash.js";
import { formatFsError } from "../../errors/render.js";
import { MountCommandUnsupported } from "../mount/registry.js";
import { ExecutionNode } from "../types.js";
import { readerOf } from "../files.js";
import { lineFacts } from "../lookup/index.js";
const TRAVERSAL_CMDS = new Set(['find', 'du']);
// The walks the fan-out composes over nested mounts: find and du from each
// mount's rows, ls and the searches from each mount's own part. tree walks
// them through the dispatcher's listings.
const COMPOSED = new Set([...TRAVERSAL_CMDS, 'ls', 'grep', 'rg']);
/**
* One mount's part of a find or du, on the command that mount serves. A mount
* that registers no find or du of its own still answers its part through the
* generic walk over the dispatcher, since both read only the metadata every
* mount serves.
*/
function ownPart(runSingle, registry, dispatch, cwd, vfs, follow, find) {
return async (cmdName, paths, texts, bag, opts) => {
try {
await registry.resolveMount(cmdName, paths, cwd);
}
catch (err) {
if (!(err instanceof MountCommandUnsupported))
throw err;
const builder = DISPATCH_BUILDERS.get(cmdName);
if (builder === undefined)
throw new Error(`No dispatch builder for ${cmdName}`);
return runDispatch(builder, paths, texts, bag, dispatch, cwd, vfs, opts?.stdin ?? null, opts?.signal, [], follow, registry.diff, registry.ids, opts?.find ?? find);
}
return runSingle(cmdName, paths, texts, bag, opts ?? {});
};
}
// Whether a walk over these operands fans out over the mounts nested in
// them: `walks` is read off the line's words, so the facts the admission
// gate kept for the line answer.
export function shouldFanOut(cmdName, paths, words, cwd, registry) {
// Use the raw mount table: hidden descendants still shadow backend keys.
// A refused operand names nothing; every valid operand may own a subtree.
if (!paths.some((p) => p.walkError === null && registry.descendantMounts(p.virtual).length > 0))
return false;
return COMPOSED.has(cmdName) && lineFacts(cmdName, words, cwd).walks;
}
/**
* Compose a traversal over the mounts inside its operands. Each mount's own
* command answers for its part: find and du from every mount's structured
* rows and measurements, a search from its owned scopes. No output is
* inspected to recover paths or repair depth and totals.
*/
export async function fanOutTraversal(cmdName, paths, texts, flagKwargs, registry, primaryMount, cwd, cmdStr, stdin, vfs, sessionView, signal, dispatch, native,
// The line's operand resolution, which a walk through the dispatcher's
// globs take.
follow,
// find's expression, parsed once for the line.
find) {
signal?.throwIfAborted();
if (dispatch === undefined || native === undefined)
throw new Error('traversal requires dispatcher and native execution');
let stdout;
let io;
const part = ownPart(native, registry, dispatch, cwd, vfs, follow, find);
try {
if (cmdName === 'find') {
;
[stdout, io] = await runFind(paths, texts, flagKwargs, find, dispatch, part, cwd, vfs, stdin, signal, follow);
}
else if (cmdName === 'du') {
;
[stdout, io] = await runDu(paths, texts, flagKwargs, dispatch, part, cwd, vfs, stdin, signal, true, follow);
}
else {
;
[stdout, io] = await handleCrossMount(cmdName, [...paths], [...texts], flagKwargs, dispatch, native, stdin, undefined, vfs, sessionView, cwd, [], null, undefined, follow, registry.diff, registry.ids, signal);
}
}
catch (err) {
if (err instanceof CommandTimeoutError || (err instanceof Error && err.name === 'AbortError'))
throw err;
stdout = null;
if (err instanceof UsageError) {
io = new IOResult({
exitCode: err.exitCode,
stderr: encodeText(`${err.message}\n`),
});
}
else {
// A backend failure anywhere in the walk (a 5xx from a nested mount)
// is this command's result, in its voice, as the single-mount entry point
// reports it; the rest of the line still runs.
io = new IOResult({
exitCode: readFailExitCode(cmdName, err),
stderr: formatFsError(cmdName, err, paths),
});
}
}
// Only the mounts the walk can reach bound its output: a hidden one never
// contributes a row, so its stricter limit must not apply.
const prefixes = new Set([primaryMount.prefix]);
const reader = vfs === undefined ? null : readerOf(vfs);
for (const path of paths) {
if (path.walkError !== null)
continue;
for (const mount of registry.descendantMounts(path.virtual))
if (reader === null || reader.visible('/' + stripSlash(mount.prefix)))
prefixes.add(mount.prefix);
}
io.producer = {
command: cmdName,
prefixes: [...prefixes].sort(compareCodePoints),
declared: null,
};
return [
stdout,
io,
new ExecutionNode({
command: cmdStr,
exitCode: io.exitCode,
stderr: await io.materializeStderr(),
}),
];
}
export function runWithFanout(runSingle, registry, cwd, vfs, sessionView, signal, dispatch,
// The line's operand resolution, which a walk through the dispatcher's
// globs take.
follow,
// find's expression, parsed once for the line.
find,
// The line's words, name first, whose facts say whether it walks.
words = []) {
return async (cmdName, paths, texts, flagKwargs, opts) => {
const stdin = opts?.stdin ?? null;
if (!shouldFanOut(cmdName, paths, words, cwd, registry)) {
const run = TRAVERSAL_CMDS.has(cmdName) && dispatch !== undefined
? ownPart(runSingle, registry, dispatch, cwd, vfs, follow, find)
: runSingle;
return run(cmdName, paths, texts, flagKwargs, opts ?? {});
}
let mount = null;
try {
mount = await registry.resolveMount(cmdName, paths, cwd);
}
catch (err) {
// The single-mount runner owns the wording for a command this mount
// does not serve, so let it report rather than re-throwing.
if (!(err instanceof MountCommandUnsupported))
throw err;
}
if (mount === null)
return runSingle(cmdName, paths, texts, flagKwargs, opts ?? {});
const [stdout, io] = await fanOutTraversal(cmdName, paths, texts, flagKwargs, registry, mount, cwd, cmdName, stdin, vfs, sessionView, signal, dispatch, runSingle, follow, opts?.find ?? find);
return [stdout, io];
};
}
//# sourceMappingURL=fanout.js.map