UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

136 lines • 5.88 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 { IOResult } from "../../../../io/types.js"; import { encodeText } from "../../../../shell/bytes.js"; import { program } from "../../../lookup/lookup.js"; import { ExecutionNode } from "../../../types.js"; import { lastOf, scanOptions } from "../getopt.js"; import { builtinUsage, result } from "../shared.js"; import { describe, locations, programFile } from "./classify.js"; import { TYPE_OPTIONS, WHICH_OPTIONS, WHICH_USAGE } from "./constants.js"; import { NameKind } from "./types.js"; /** * Run the `type` builtin (`type [-afptP] name [name ...]`). * * Resolution matches `command -V`, but the exit rule is `type`'s: 0 only * when every name resolves. `-t` prints the classification word; `-p` * prints the file of a name that resolves to one (none for a builtin, * which still resolves) and `-P` searches PATH for one even past a * builtin, a miss there being a miss; the three are one group, the last * winning. `-a` prints one line per layer holding the name (a builtin * that is also a program ends with its file's line), `-f` ignores the * function table, and a missing name warns on stderr unless a word-only * mode (`-t`/`-p`/`-P`) is active. Pinned against bash 5.2 on * debian:stable-slim. */ export function handleType(args, session, registry, parser) { const scan = scanOptions(args, TYPE_OPTIONS); if (scan.bad !== null) { return result('type', { exitCode: 2, stderr: `bash: type: ${scan.bad}: invalid option\n${builtinUsage('type')}`, }); } const mode = lastOf(scan.letters, 'tpP'); const allMode = scan.letters.includes('a'); const hidden = scan.letters.includes('f') ? NameKind.FUNCTION : null; const outLines = []; const errLines = []; let allFound = true; for (const name of scan.operands) { if (mode === 'P') { if (program(name, session, registry) === null) allFound = false; else outLines.push(`${programFile(name)}\n`); continue; } const kinds = locations(name, session, registry, allMode, hidden); if (kinds.length === 0) { allFound = false; if (mode === null) errLines.push(`bash: type: ${name}: not found\n`); continue; } if (mode === 't') for (const item of kinds.map((kind) => `${kind}\n`)) outLines.push(item); else if (mode === 'p') { for (const kind of kinds) if (kind === NameKind.FILE) outLines.push(`${programFile(name)}\n`); } else outLines.push(...kinds.map((kind) => `${describe(name, kind, session, parser)}\n`)); } const out = outLines.length > 0 ? encodeText(outLines.join('')) : null; const err = encodeText(errLines.join('')); const code = scan.operands.length === 0 || allFound ? 0 : 1; return [ out, new IOResult({ exitCode: code, stderr: err }), new ExecutionNode({ command: 'type', exitCode: code, stderr: err }), ]; } /** * Run the `which` builtin (`which [-as] name [name ...]`). * * Pinned against debianutils `which` (debian:stable-slim): it prints the * file PATH finds for each name, which is the program's under `/usr/bin` * (the one PATH directory), a miss prints nothing at all, the exit status * is 0 only when every name resolves (1 with no operands), and `-s` * reports through the status alone. A builtin with no program (`cd`), a * function, an alias and a reserved word are no file, so each is a miss; * `-a` has one directory to search and so one line per name. `$PATH` * itself is not read: mirage runs a program by its name whatever PATH * holds, so `which` answers as dispatch does. The refusal for an unknown * option is bash's shape, not the C tool's `Illegal option`, because this * is a builtin. */ export function handleWhich(args, session, registry) { const scan = scanOptions(args, WHICH_OPTIONS); if (scan.bad !== null) { return result('which', { exitCode: 2, stderr: `which: ${scan.bad}: invalid option\n${WHICH_USAGE}`, }); } const silent = scan.letters.includes('s'); const outLines = []; let allFound = true; for (const name of scan.operands) { if (program(name, session, registry) === null) { allFound = false; continue; } if (!silent) outLines.push(`${programFile(name)}\n`); } const out = outLines.length > 0 ? encodeText(outLines.join('')) : null; const code = scan.operands.length > 0 && allFound ? 0 : 1; return [ out, new IOResult({ exitCode: code }), new ExecutionNode({ command: 'which', exitCode: code }), ]; } /** The `type` arm. */ export function typeBuiltin(call) { return Promise.resolve(handleType([...call.argv.args], call.context.session, call.registry, call.parser)); } /** The `which` arm. */ export function whichBuiltin(call) { return Promise.resolve(handleWhich([...call.argv.args], call.context.session, call.registry)); } //# sourceMappingURL=lookup.js.map