UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

106 lines • 4.77 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 MIRAGE_RS_WASM from "../generated/mirage_rs.js"; import { decodeBase64 } from "../utils/base64.js"; import { StreamRefusal } from "./errors.js"; const encoder = new TextEncoder(); const decoder = new TextDecoder(); /** * The mirage-rs NFSv3 server, which does no I/O of its own: the host feeds * it each connection's bytes, runs the file operations `requests` names, * and hands each result back through `complete`. It runs on a wasm * instance of its own, so a long-lived server never holds the one streams * share. Mirrors Python's NFSEngine. */ export class NFSEngine { exports; handle; constructor(exports, handle) { this.exports = exports; this.handle = handle; } /** * Open a server: `exportPath` is the path a client mounts, `generation` is * stamped into every file handle (one from another generation is stale), * and `port` is the port the host listens on. */ static async open(exportPath, generation, port) { const module = await WebAssembly.compile(decodeBase64(MIRAGE_RS_WASM)); const exports = new WebAssembly.Instance(module, {}).exports; const config = encoder.encode(JSON.stringify({ export: exportPath, generation, port })); const ptr = exports.mirage_rs_args(config.length); new Uint8Array(exports.memory.buffer, ptr, config.length).set(config); const handle = exports.mirage_rs_nfs_open(config.length); if (handle === 0) { const ptr = exports.mirage_rs_error(0); const message = decoder.decode(new Uint8Array(exports.memory.buffer, ptr, exports.mirage_rs_error_len(0))); throw new StreamRefusal(message, new Uint8Array()); } return new NFSEngine(exports, handle); } /** Take what connection `conn` sent; returns the replies it answers at once. */ feed(conn, data) { this.put([data]); if (this.exports.mirage_rs_nfs_feed(this.handle, conn, data.length) !== 0) throw this.refusal(); return this.output(); } /** The file operations calls wait on, each with its `id`. */ requests() { this.exports.mirage_rs_nfs_requests(this.handle); return JSON.parse(decoder.decode(this.output())); } /** * Hand back one request's result: `{error: 'ENOENT'}`, or what it asked for * (`attrs`, `entries`, `target`), and the bytes a read returned. Returns the * call's connection (null once it has closed) and its reply once it has one. */ complete(id, reply, data = new Uint8Array()) { const json = encoder.encode(JSON.stringify(reply)); this.put([json, data]); const conn = this.exports.mirage_rs_nfs_complete(this.handle, id, json.length, data.length); if (conn === -2) throw this.refusal(); return [conn === -1 ? null : conn, this.output()]; } /** Forget connection `conn` and the calls it has out. */ close(conn) { this.exports.mirage_rs_nfs_disconnect(this.handle, conn); } /** Free the server. */ dispose() { this.exports.mirage_rs_nfs_close(this.handle); } put(parts) { const len = parts.reduce((sum, part) => sum + part.length, 0); const ptr = this.exports.mirage_rs_nfs_buffer(this.handle, len); const view = new Uint8Array(this.exports.memory.buffer, ptr, len); let at = 0; for (const part of parts) { view.set(part, at); at += part.length; } } output() { const ptr = this.exports.mirage_rs_nfs_output(this.handle); const len = this.exports.mirage_rs_nfs_output_len(this.handle); return new Uint8Array(this.exports.memory.buffer, ptr, len).slice(); } refusal() { const ptr = this.exports.mirage_rs_nfs_error(this.handle); const len = this.exports.mirage_rs_nfs_error_len(this.handle); return new StreamRefusal(decoder.decode(new Uint8Array(this.exports.memory.buffer, ptr, len)), new Uint8Array()); } } //# sourceMappingURL=nfs.js.map