UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

143 lines • 5.42 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 { specOf } from "../../spec/builtins.js"; import { FlagView } from "../../spec/flag_view.js"; import { READ_FAILURES } from "../../../errors/constants.js"; import { fsStrerror, isFsError } from "../../../errors/fs.js"; import { AsyncLineIterator } from "../../../io/async_line_iterator.js"; import { IOResult, materialize } from "../../../io/types.js"; import { ByteSpool } from "../../../io/spool.js"; import { StreamRefusal } from "../../../rs/errors.js"; import { closeStream, mergeOutput, openStream } from "../../../rs/stream.js"; import { concat } from "../../../utils/bytes.js"; import { byteView, encodeText, fromByteView } from "../../../shell/bytes.js"; import { LineFacts } from "../../../types.js"; import { neverOpened, stdinStream } from "../utils/stream.js"; const NEWLINE = new Uint8Array([0x0a]); /** * Convert each operand in order through mirage-rs's iconv, as glibc 2.41 * converts. An input that cannot be opened is reported and skipped; one that * opens and then refuses the read, a directory, ends the run. Mirrors * Python's `iconv_generic`. */ export async function iconvGeneric(paths, ctx, stream, write) { const fl = new FlagView(ctx.flags, specOf('iconv')); if (fl.asBool('list')) { const list = await openStream('iconv', { list: true }); try { return [list.finish(), new IOResult()]; } finally { list.close(); } } let engine; try { engine = await openStream('iconv', { from: byteView(fl.asStr('from_code', '')), to: byteView(fl.asStr('to_code', '')), omit: fl.asBool('c'), inputs: Math.max(paths.length, 1), }); } catch (err) { if (!(err instanceof StreamRefusal)) throw err; return [ null, new IOResult({ exitCode: 1, stderr: concat([fromByteView(err.message), NEWLINE]) }), ]; } const converter = engine; const read = stdinStream(stream, ctx.stdin); const operands = paths.length > 0 ? paths.map((path) => ({ path, reader: new AsyncLineIterator(read(path)) })) : [{ path: null, reader: new AsyncLineIterator(ctx.stdin ?? new Uint8Array()) }]; const io = new IOResult(); const report = async (line) => { if (line.byteLength === 0) return; io.exitCode = 1; io.stderr = concat([await materialize(io.stderr), line]); }; const readInput = async (index) => { await report(converter.takeStderr()); const operand = operands[index]; if (operand === undefined) return null; const { path, reader } = operand; try { return await reader.readChunk(); } catch (err) { if (!isFsError(err)) throw err; const code = err.code; const strerror = String(fsStrerror(err)); let line; if (path === null || (code !== undefined && READ_FAILURES.has(code)) || !neverOpened(reader, path, err)) { line = `error while reading the input: ${strerror}`; converter.cancel(); } else { line = `cannot open input file \`${path.rawPath}': ${strerror}`; } await report(encodeText(`iconv: ${line}\n`)); return null; } }; async function* output() { try { yield* mergeOutput(converter, readInput, ctx.signal); } catch (err) { if (!(err instanceof StreamRefusal)) throw err; await report(concat([fromByteView(err.message), NEWLINE])); if (err.output.byteLength > 0) yield err.output; } finally { await report(converter.takeStderr()); if (converter.status() !== 0) io.exitCode = 1; for (const { reader } of operands) await reader.discard(); await closeStream(converter); } } const converted = output(); const outputPath = fl.asPath('output'); if (outputPath !== undefined) { const spool = new ByteSpool(); try { await spool.fill(converted); await write(outputPath, spool.stream()); } finally { await spool.close(); } return [null, io]; } return [converted, io]; } /** * An iconv line writes only the file `-o` names. Mirrors Python's `facts`. */ export function facts(fl, _operands) { return new LineFacts({ writes: fl.raw('output') !== undefined }); } //# sourceMappingURL=iconv.js.map