@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
143 lines • 5.42 kB
JavaScript
// ========= 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