@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
123 lines • 4.49 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 { IOResult } from "../io/types.js";
import { isBuiltinGrammar, registeredSpec } from "./spec/builtins.js";
import { compileSpec } from "./spec/compile.js";
import { OWN_OPTION_LOOP } from "./spec/constants.js";
import { helpPage, versionLine } from "./spec/standard.js";
/**
* One command as a mount registers it: the grammar (with `--help` and
* `--version` added), the backend it belongs to, the file extension it
* handles (null for every file), the handler, how to merge a run split
* across mounts, whether it changes files, and its output limit.
*/
export class Command {
name;
spec;
vfs;
filetype;
fn;
aggregate;
write;
limit;
constructor(init) {
this.name = init.name;
this.spec = init.spec;
this.vfs = init.vfs;
this.filetype = init.filetype ?? null;
this.fn = init.fn;
this.aggregate = init.aggregate ?? null;
this.write = init.write ?? false;
this.limit = init.limit ?? null;
Object.freeze(this);
}
}
/** A fixed list of commands, looked up by name and file extension. */
export class CommandCatalog extends Array {
#byKey;
constructor(commands) {
super(...commands);
const byKey = new Map();
for (const command of commands) {
byKey.set(CommandCatalog.key(command.name, command.filetype), command);
}
this.#byKey = byKey;
Object.freeze(this);
}
get size() {
return this.length;
}
toArray() {
return this;
}
get(name, filetype = null) {
return this.#byKey.get(CommandCatalog.key(name, filetype)) ?? null;
}
require(name, filetype = null) {
const found = this.get(name, filetype);
if (found === null) {
throw new Error(`command '${name}' with filetype ${String(filetype)} is not registered`);
}
return found;
}
static key(name, filetype) {
return `${name}\0${filetype ?? ''}`;
}
static get [Symbol.species]() {
return Array;
}
}
const ENC = new TextEncoder();
/**
* Add `--help` and `--version` to a command, as GNU tools have.
*
* Either one prints to stdout and exits 0 without running the handler. A
* command that declares its own `--version`, or a program that runs its own
* option loop (OWN_OPTION_LOOP), answers that option itself.
*/
function answerStandardOptions(name, spec, fn) {
const ownVersion = compileSpec(spec).options.some((o) => o.names.includes('--version'));
const ownHelp = isBuiltinGrammar(name, spec) && OWN_OPTION_LOOP.has(name);
const helpText = helpPage(name, spec);
const versionText = versionLine(name);
const wrapped = async (accessor, paths, texts, ctx) => {
if (!ownHelp && ctx.flags.help === true)
return [ENC.encode(helpText), new IOResult()];
if (!ownVersion && ctx.flags.version === true) {
return [ENC.encode(versionText), new IOResult()];
}
return fn(accessor, paths, texts, ctx);
};
return { spec: registeredSpec(name, spec), fn: wrapped };
}
/**
* Register a handler as a command of one or more VFSes: one
* `Command` per VFS, with the handler wrapped to answer `--help`
* and `--version`.
*/
export function command(options) {
const vfsNames = Array.isArray(options.vfs) ? options.vfs : [options.vfs];
const { spec, fn } = answerStandardOptions(options.name, options.spec, options.fn);
return vfsNames.map((vfs) => new Command({
name: options.name,
spec,
vfs,
filetype: options.filetype ?? null,
fn,
aggregate: options.aggregate ?? null,
write: options.write ?? false,
limit: options.limit ?? null,
}));
}
//# sourceMappingURL=config.js.map