UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

130 lines • 5.23 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 { openStreamSync } from "../rs/stream.js"; import { decodeText, encodeText } from "../shell/bytes.js"; // '#' starts a comment and '~' starts a home-directory expansion, but // only as the first character of a word, so GNU quotes them there alone. export const LEADING_SPECIAL = new Set('#~'); // What rules out the "name" form for a name holding a single quote. // Narrower than the trigger set: a space and a ':' are harmless inside // double quotes, while the conditional characters #, ~, { and } lose their // position rule and always rule it out. export const DQ_BLOCKERS = new Set('!"#$&()*;<=>?[\\^`{|}~'); export const NAMED_ESCAPES = new Map([ ['\x07', 'a'], ['\b', 'b'], ['\t', 't'], ['\n', 'n'], ['\v', 'v'], ['\f', 'f'], ['\r', 'r'], ]); // The commands whose operand mirage reports shell-quoted, each one probed // against its own GNU original on debian:stable-slim. GNU picks the policy // per diagnostic, not per command family: cat/wc/cut and most of the read // family quote only when the name needs it (the shell-escape quoting style), // while head/tail/tac/fmt/split/csplit/truncate/strings quote always and // word the line differently ("cannot open X for reading"). Those say it in // GNU's words (FAILURE_WORDING in errors.ts). For the rest // mirage renders one line shape, so it renders one policy too: quote when // the name needs it, which is the same answer for every name that carries // a metacharacter and differs only for the plain ones GNU's always-quoting // half would dress up. // // Absent on purpose, in two groups. GNU prints the operand bare for grep, // sed, cmp, diff, rev (util-linux), md5 (BSD) and zcat (gzip). And the // tools that are nobody's coreutils — awk, column, file, iconv, jq, look, // xxd — keep their own original's diagnostic, which is not this one. export const SHELL_QUOTED_COMMANDS = new Set([ 'b2sum', 'base64', 'cat', 'comm', 'csplit', 'cut', 'df', 'expand', 'fmt', 'fold', 'join', 'md5sum', 'nl', 'od', 'paste', 'readlink', 'realpath', 'sha1sum', 'sha224sum', 'sha256sum', 'sha384sum', 'sha512sum', 'shuf', 'sort', 'split', 'strings', 'tee', 'tsort', 'unexpand', 'uniq', 'wc', ]); function quote(name, style) { const stream = openStreamSync('quote', { style }); try { stream.feed(encodeText(name)); return decodeText(stream.finish()); } finally { stream.close(); } } // Whether a command reports its path operands shell-quoted. export function quotesOperands(cmdName) { return SHELL_QUOTED_COMMANDS.has(cmdName); } /** GNU ls -b, using shellQuoteAlways's documented UTF-8 locale policy. */ export function escapeName(name) { return quote(name, 'escape'); } // Wrap a name so a shell would read it back as itself: GNU's // shell-escape-always quoting style. The "name" form when the name holds a // single quote and nothing else that would need escaping there, otherwise // the 'name' form, where an embedded quote becomes '\'' and a run of // control characters becomes one $'...' group. Deliberate divergence: a // non-ASCII character is ordinary and stays as itself, which is GNU under // a UTF-8 locale; the C locale would render every byte of it in octal, // and mirage has no locale to switch on. export function shellQuoteAlways(name) { return quote(name, 'shell_always'); } // Wrap a name only when a shell would read it back as something else: // GNU's shell-escape quoting style, byte-identical with the python // shell_quote. `nope.txt` stays bare and `*.txt` becomes `'*.txt'`, which // is what makes a name carrying a metacharacter readable in a diagnostic // without dressing up every ordinary one. export function shellQuote(name) { return quote(name, 'shell'); } // Wrap text so bash reads it back as exactly one word: the quoting bash // itself writes, always the 'text' form, with an embedded quote spelled // '\'' and every other character, newlines included, left as itself. This // is not shellQuoteAlways, which is GNU's diagnostic rendering and may // answer with the "text" form or a $'...' group; bash uses this one // wherever it builds a command line out of data (`alias` printing a // value, `mapfile -C` handing a record to a callback), so text that is // shell syntax stays a single argument. export function singleQuote(text) { return `'${text.replaceAll("'", "'\\''")}'`; } //# sourceMappingURL=quote.js.map