UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

86 lines • 3.53 kB
// ========= 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 { materialize } from "../../../../io/types.js"; import { eacces } from "../../../../errors/fs.js"; import { UsageError } from "../../../errors.js"; import { FlagView } from "../../../spec/flag_view.js"; const DEC = new TextDecoder(); /** Refuse an operand on a verb that takes none. */ export function noOperands(texts) { if (texts.length > 0) throw new UsageError(`unrecognized arguments: ${texts.join(' ')}`); } /** The one operand a verb requires, named as its usage line names it. */ export function oneOperand(texts, name) { const first = texts[0]; if (first === undefined) throw new UsageError(`the following arguments are required: ${name}`); noOperands(texts.slice(1)); return first; } /** The operand a verb may take once, null when the line has none. */ export function optionalOperand(texts) { noOperands(texts.slice(1)); return texts[0] ?? null; } /** * The base a line addresses, refused (EACCES) when `baseIds` excludes it. * Refused here, before any request, so a base the install was not given is * never reached: the scope the mount enforces on reads. The executor * prefixes the refusal with the words the line was typed under, `<head> base * get: <base-id>: Permission denied`, exit 1. */ export function scopedBase(config, baseId) { const wanted = config.baseIds; if (wanted !== undefined && !wanted.includes(baseId)) { throw eacces(baseId, `${baseId}: Permission denied`); } return baseId; } /** * The schema table a line names, by id first and then by name. Airtable * takes either spelling in a path, and an id can never be another table's * name, so the id match wins. */ export function findTable(tables, ref) { return tables.find((t) => t.id === ref) ?? tables.find((t) => t.name === ref); } /** A flag's value decoded as a JSON object. */ export function jsonObject(flag, text) { let value; try { value = JSON.parse(text); } catch { throw new UsageError(`${flag} must be valid JSON`); } if (typeof value !== 'object' || value === null || Array.isArray(value)) { throw new UsageError(`${flag} must be a JSON object`); } return value; } /** Piped input as text, a leading byte order mark dropped. */ export async function stdinText(stdin) { return DEC.decode(await materialize(stdin)); } /** * Run one verb on the install's accessor, which outlives the line: the * workspace closes it with the install. A verb never names itself: the CLI * may be installed under any head word, so a refusal is left to the executor, * which prefixes it with the words the line was typed under. */ export function run(verb) { return (accessor, _paths, texts, ctx) => verb(accessor, texts, ctx, new FlagView(ctx.flags, ctx.spec)); } //# sourceMappingURL=util.js.map