UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

96 lines • 3.92 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 { classify } from "../../../errors/index.js"; import { isAborting } from "../../../errors/fs.js"; import { POSIX } from "../../../errors/posix.js"; export class MontyUnavailableError extends Error { constructor(message, options) { super(message, options); this.name = 'MontyUnavailableError'; } } // The builtin a guest `except`s for a condition, where CPython raises a // subclass for its errno; every other condition is a plain OSError. const EXCEPTIONS = { ENOENT: 'FileNotFoundError', ENOTDIR: 'NotADirectoryError', EISDIR: 'IsADirectoryError', EEXIST: 'FileExistsError', EACCES: 'PermissionError', EPERM: 'PermissionError', }; /** * The guest-python rendering for a condition: CPython on Linux, whose * number and phrase are the shared POSIX table's (that table is Linux's * numbering already). */ export function cpythonError(condition) { return { exception: EXCEPTIONS[condition] ?? 'OSError', ...POSIX[condition] }; } /** The traceback monty renders for one of its own errors. */ export function displayError(err) { const e = err; if (typeof e.display === 'function') return e.display('traceback'); return e instanceof Error ? e.message : String(err); } /** * Build the guest-side exception for one condition, in CPython's * message shape. * * Args: * code: the condition, e.g. ENOENT. * path: the path the operation names. * target: rename's destination, when there is one. */ export function guestError(code, path, target) { const row = cpythonError(code); const where = target === undefined ? `'${path}'` : `'${path}' -> '${target}'`; const guest = Object.assign(new Error(`[Errno ${String(row.errno)}] ${row.phrase}: ${where}`), { guestCondition: code, }); guest.name = row.exception; return guest; } /** * Re-throw a mount failure under its python exception name: the monty * engine raises `err.name` as the matching guest exception type * (PYTHON_EXC_NAMES), so agent code can `except FileNotFoundError` * exactly as it does on the python host. Every named condition * converts (a non-empty rmdir is an OSError with errno 39, not a raw * JS error), and a failure the vocabulary does not name is EIO, as a * kernel reports a device that failed (the engine knows only builtin * types, so a backend's own error would reach the guest as * RuntimeError). A stopped run's refusal is no failed device: it leaves * as itself, so a callback making call after call (a scan) ends with * its run. * * Args: * err: whatever the mount op rejected with. * path: the path the operation names. * target: a rename's destination, which CPython prints after the * source. */ export function asGuestError(err, path, target) { // A guest exception this adapter already built (a refusal before any // mount op) is CPython's shape already, and keeps its own condition. A // backend error that merely shares a CPython name is classified like // any other. if (err.guestCondition !== undefined) return err; if (isAborting(err)) return err; return guestError(classify(err) ?? 'EIO', path, target); } //# sourceMappingURL=errors.js.map