UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

182 lines • 7.25 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 { isMissingPath } from "../errors/fs.js"; import { materialize } from "../io/types.js"; import { PathSpec } from "../types.js"; import { mountKey, mountPrefixOf } from "../utils/key_prefix.js"; export const STDIN_OPERAND = '-'; /** * Drop a script's first line the way CPython's `-x` does. * * The first line is emptied rather than removed, because CPython keeps * the line numbering of the whole file: a raise on physical line 2 * still reports line 2 under `-x` (pinned on 3.12.11). A file with no * newline at all is one line, so `-x` leaves nothing. * * @param code the script's text as read. */ export function skipFirstLine(code) { const at = code.indexOf('\n'); return at === -1 ? '' : code.slice(at); } /** * The script an interpreter's operands name, null when the program comes * from `-c`, `-m` or stdin. The exec check and the runtime both ask it. * * @param paths the path operands. * @param texts the text operands. * @param flags the interpreter switches. * @param cwd the working directory a text operand resolves against. */ export function scriptOperand(paths, texts, flags, cwd) { if (typeof flags.m === 'string' || typeof flags.c === 'string') return null; const [first] = paths; if (first !== undefined) return first; const [word] = texts; if (word !== undefined && word !== STDIN_OPERAND) { const base = cwd === null ? '/' : typeof cwd === 'string' ? cwd : cwd.virtual; return PathSpec.fromStrPath(word, undefined, base === '' ? '/' : base); } return null; } /** * An interpreter that ran nothing: its message and exit 1. * * @param message what it says on stderr, newline included. */ function failed(message) { return { stdout: new Uint8Array(), stderr: new TextEncoder().encode(message), exitCode: 1 }; } /** * Resolve what a language runtime runs from its operands, as a real * interpreter does. * * `-c` wins, else `-m` names a module, else the first operand is the * script (read through `ctx.vfs`), else piped stdin is the program. A * script CLI names itself `ctx.spec.name`. * * @param paths the path operands. * @param texts the text operands. * @param ctx the run's context; `flags` holds the switches (`c`, `m`, * `x`, `scriptCli`). * @param label the command name the messages use. * @param rules what this interpreter calls itself in the entry points * that carry no file name. * @param moduleCode the program a `-m` runs, given the module and the * label; null when this runtime cannot run a module. * @param runtimeName the runtime's name, for the `-m` refusal. * @returns the program, or why nothing ran. */ export async function resolveProgram(paths, texts, ctx, label, rules, moduleCode, runtimeName) { const decoder = new TextDecoder('utf-8', { fatal: false }); const flags = ctx.flags; const payload = flags.c; const module = flags.m; let code = typeof payload === 'string' ? payload : null; let script = null; let argv0 = rules.payload; let args; if (typeof module === 'string') { // runpy's alter_sys overwrites argv[0] with the module's own file, // which no caller can know here; the module name stands in for the // runtimes that never reach runpy. code = moduleCode(module, label); if (code === null) { // Exit 1, CPython's code for a `-m` that could not run, but not its // "No module named" wording: nothing was searched for, so naming the // runtime is the honest report. return failed(`${label}: -m is not supported by the '${runtimeName}' runtime\n`); } args = [...paths.map((p) => p.virtual), ...texts]; argv0 = module; } else if (code !== null) { args = [...paths.map((p) => p.virtual), ...texts]; } else if (paths[0] !== undefined) { script = paths[0]; args = [...paths.slice(1).map((p) => p.virtual), ...texts]; argv0 = rules.namesFile === true ? paths[0].rawPath : undefined; } else if (texts[0] === STDIN_OPERAND) { // The explicit stdin spelling. It is an operand, not a flag, so it // ends option parsing like any other, and the words after it are the // program's argv exactly as a script's would be. args = texts.slice(1); argv0 = rules.stdinOperand; } else if (texts[0] !== undefined) { script = scriptOperand(paths, texts, flags, ctx.cwd); args = texts.slice(1); // As typed, which is what CPython puts in argv[0]; the resolved // spelling is script's job. argv0 = rules.namesFile === true ? texts[0] : undefined; } else { args = []; argv0 = rules.bareStdin; } if (code === null && script !== null) { if (ctx.vfs === undefined) return failed(`${label}: no dispatch available to read script\n`); // Read by the key its mount gives it, which the dispatcher reads by. const keyed = new PathSpec({ virtual: script.virtual, directory: script.directory, pattern: script.pattern, resolved: script.resolved, vfsPath: mountKey(script.virtual, mountPrefixOf(script.virtual, script.vfsPath)), }); let data; try { ; [data] = await ctx.vfs.dispatch('read', keyed); } catch (err) { if (!isMissingPath(err)) throw err; return failed(`${label}: ${script.virtual}: No such file\n`); } const bytes = data instanceof Uint8Array ? data : data === null || data === undefined ? new Uint8Array() : await materialize(data); code = decoder.decode(bytes); if (flags.x === true) code = skipFirstLine(code); } let stdin = ctx.stdin; if (code === null) { const source = stdin === null ? null : await materialize(stdin); if (source === null || source.length === 0) return failed(`${label}: no input\n`); code = decoder.decode(source); stdin = null; } const scriptCli = flags.scriptCli === true; if (scriptCli && ctx.spec !== undefined) argv0 = ctx.spec.name; return { code, args, ...(argv0 !== undefined ? { prog: argv0 } : {}), stdin, ...(script !== null ? { scriptPath: script } : {}), scriptCli, }; } //# sourceMappingURL=source.js.map