@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
302 lines • 11.9 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 { humanScaled, humanSize } from "../../../../commands/builtin/utils/formatting.js";
import { dispatchStat, nearestAncestor } from "../../../../commands/builtin/utils/paths.js";
import { enoent, enotdir, isDotWalkError, isMissingPath, walkRefusal, } from "../../../../errors/fs.js";
import { fsErrorLine } from "../../../../errors/render.js";
import { encodeText } from "../../../../shell/bytes.js";
import { CapacityState, PathSpec } from "../../../../types.js";
import { pathVisible } from "../../../../utils/hidden.js";
import { rstripSlash } from "../../../../utils/slash.js";
import { compareCodePoints } from "../../../../utils/sort.js";
import { scopePath } from "../scope.js";
import { fail, ok, result, splitValueFlags } from "../shared.js";
import { BLOCK_SUFFIX, SI_UNITS } from "./constants.js";
// Parse a -B/--block-size argument into [bytes, header-label]; a plain byte
// count or a 1024-based suffix (K/M/G/T), labelled after the raw argument.
function parseBlock(text) {
const t = text.trim();
if (t.length === 0)
return null;
const suffix = t.slice(-1).toUpperCase();
let value;
if (suffix in BLOCK_SUFFIX) {
const head = t.slice(0, -1) || '1';
if (!/^\d+$/.test(head))
return null;
value = parseInt(head, 10) * (BLOCK_SUFFIX[suffix] ?? 1);
}
else if (/^\d+$/.test(t)) {
value = parseInt(t, 10);
}
else {
return null;
}
// GNU rejects a zero (or non-positive) block size rather than scaling.
if (value <= 0)
return null;
return [value, t];
}
// The last size-format flag (-h/-H/-k/-B) in the leading option run. GNU df
// lets a later size flag override the earlier ones (`df -h -B1M` prints a
// block header, `df -B1M -h` prints `Size`), so the display format is
// whichever appears last. Returns the flag letter, or null when none appear.
function lastFormat(args) {
let last = null;
let i = 0;
while (i < args.length) {
const arg = args[i];
if (arg === undefined)
break;
const s = scopePath(arg);
if (s === '--' || !(s.length >= 2 && s.startsWith('-') && s[1] !== '-'))
break;
const body = s.slice(1);
for (let j = 0; j < body.length; j++) {
const c = body[j];
if (c === 'h' || c === 'H' || c === 'k' || c === 'B')
last = c;
if (c === 'B') {
if (body.slice(j + 1).length === 0)
i += 1;
break;
}
}
i += 1;
}
return last;
}
// What stat-ing one FILE operand answers, null when it is there. GNU df
// stats each FILE to find its filesystem and names the one it cannot reach
// with the errno it got, so a plain file in the chain is ENOTDIR, told apart
// from an absent name by walking the chain on a miss, since a store answers
// both with ENOENT. Mirrors Python's _operand_error.
async function operandError(dispatch, spec) {
const stat = dispatchStat(dispatch);
try {
await stat(spec);
}
catch (err) {
const code = err.code;
if (code === 'ENOTDIR')
return err;
if (!isMissingPath(err))
throw err;
if (isDotWalkError(err))
return err;
const [, parentIsDir] = await nearestAncestor(stat, spec);
return parentIsDir ? err : enotdir(spec);
}
return null;
}
// Human-readable size in powers of 1000 (df -H). Same rounding as -h: GNU df
// rounds the two alike, and only the base differs.
function humanSi(n) {
return humanScaled(n, 1000, SI_UNITS);
}
// Bytes as a count of `block`-byte units, rounded up like GNU df.
function scale(nbytes, block) {
return String(Math.ceil(nbytes / block));
}
// GNU df use-percent: ceil(used / (used + avail) * 100), or `-` when the
// denominator is zero.
function usePct(used, avail) {
const denom = used + avail;
if (denom <= 0)
return '-';
return `${String(Math.ceil((used * 100) / denom))}%`;
}
// The three numeric cells (block or inode) for one mount, or three `-` when
// capacity is not a known quota (never a fabricated 0).
function numCells(cap, human, si, block, inodes) {
const quota = cap.state === CapacityState.QUOTA;
if (inodes) {
if (quota && cap.inodes != null) {
return [String(cap.inodes), String(cap.inodesUsed ?? 0), String(cap.inodesFree ?? 0)];
}
return ['-', '-', '-'];
}
if (quota && cap.total != null) {
const used = cap.used ?? 0;
const avail = cap.available ?? 0;
if (human) {
const fmt = si ? humanSi : humanSize;
return [fmt(cap.total), fmt(used), fmt(avail)];
}
return [scale(cap.total, block), scale(used, block), scale(avail, block)];
}
return ['-', '-', '-'];
}
// The Use%/IUse% cell for one mount, or `-` outside a known quota.
function pctCell(cap, inodes) {
if (cap.state !== CapacityState.QUOTA)
return '-';
if (inodes) {
if (cap.inodes == null)
return '-';
return usePct(cap.inodesUsed ?? 0, cap.inodesFree ?? 0);
}
if (cap.total == null)
return '-';
return usePct(cap.used ?? 0, cap.available ?? 0);
}
// Resolve df operands to the mounts to report, deduped and ordered. No
// operand (or the workspace root `/`) reports every mount the session's
// profile leaves visible; a path operand reports the mount containing it, and
// one inside a hidden mount is absent. GNU df maps each FILE to its
// filesystem; one it cannot reach is reported in its own words and the rest
// still print, exit 1. Mirrors Python's _target_mounts.
async function targetMounts(registry, dispatch, session, operands) {
const ordered = [...registry.visibleMounts()]
.filter((m) => pathVisible(session.visibility, m.prefix.replace(/\/+$/, '') || '/'))
.sort((a, b) => compareCodePoints(a.prefix, b.prefix));
if (operands.length === 0)
return [ordered, []];
const seen = new Set();
const out = [];
const errors = [];
for (const op of operands) {
const spec = PathSpec.fromStrPath(op, undefined, session.cwd);
if (spec.walkError !== null) {
// The empty name reads as the working directory in `virtual`, which
// may well be a mount root, and a link loop reaches no filesystem
// at all.
errors.push(fsErrorLine('df', spec, walkRefusal(spec)));
continue;
}
const virtual = spec.virtual;
if (virtual === '' || virtual === '/') {
for (const m of ordered) {
if (!seen.has(m.prefix)) {
seen.add(m.prefix);
out.push(m);
}
}
continue;
}
const mount = registry.tryMountFor(virtual);
if (mount === null || !ordered.includes(mount)) {
errors.push(fsErrorLine('df', spec, enoent(spec)));
continue;
}
// The mount root is the filesystem itself (always present); a deeper
// path must be reachable before its mount is accepted.
const root = rstripSlash(mount.prefix) || '/';
if (rstripSlash(virtual) !== root || spec.dotted !== null) {
const failure = await operandError(dispatch, spec);
if (failure !== null) {
errors.push(fsErrorLine('df', spec, failure));
continue;
}
}
if (!seen.has(mount.prefix)) {
seen.add(mount.prefix);
out.push(mount);
}
}
return [out, errors];
}
// GNU df column layout: Filesystem left-justified (min width 14), Type (when
// present) left, numeric columns right-justified, Mounted on left with no
// trailing pad, single-space separators.
function renderTable(header, rows, showType) {
const ncols = header.length;
const left = new Set([0, ncols - 1]);
if (showType)
left.add(1);
const widths = header.map((h, c) => Math.max(h.length, ...rows.map((r) => (r[c] ?? '').length), 0));
widths[0] = Math.max(widths[0] ?? 0, 14);
const lines = [];
for (const cells of [header, ...rows]) {
const parts = [];
for (let c = 0; c < ncols; c++) {
const cell = cells[c] ?? '';
const w = widths[c] ?? 0;
if (c === ncols - 1)
parts.push(cell);
else if (left.has(c))
parts.push(cell.padEnd(w));
else
parts.push(cell.padStart(w));
}
lines.push(parts.join(' '));
}
return lines.join('\n') + '\n';
}
// df [OPTION]... [FILE]...: report per-mount capacity. A mount reports real
// numbers only when its backend can; every other backend shows `-` rather
// than a fabricated total.
export async function handleDf(registry, session, dispatch, args) {
const { flags, values, operands, bad } = splitValueFlags(args, 'hHkiaTP', 'B');
if (bad !== null)
return fail('df', `df: invalid option -- '${bad}'\n`, 2);
const posix = flags.has('P');
const bArg = values.get('B');
let bParsed = null;
if (bArg !== undefined) {
bParsed = parseBlock(bArg);
if (bParsed === null)
return fail('df', `df: invalid -B argument '${bArg}'\n`, 1);
}
// GNU resolves the mutually overriding size flags last-wins, so -h/-H
// (human) or -k/-B (block) is chosen by whichever appears last.
const lf = lastFormat(args);
const si = lf === 'H';
const human = lf === 'h' || lf === 'H';
let block = 1024;
let blockLabel = posix ? '1024-blocks' : '1K-blocks';
if (lf === 'B' && bParsed !== null) {
block = bParsed[0];
blockLabel = `${bParsed[1]}-blocks`;
}
const inodes = flags.has('i');
const showType = flags.has('T');
const [mounts, errors] = await targetMounts(registry, dispatch, session, operands);
let numHeaders;
let pctHeader;
if (inodes) {
numHeaders = ['Inodes', 'IUsed', 'IFree'];
pctHeader = 'IUse%';
}
else if (human) {
numHeaders = ['Size', 'Used', 'Avail'];
pctHeader = 'Use%';
}
else {
numHeaders = [blockLabel, 'Used', 'Available'];
pctHeader = posix ? 'Capacity' : 'Use%';
}
const header = ['Filesystem'];
if (showType)
header.push('Type');
header.push(...numHeaders, pctHeader, 'Mounted on');
const data = [];
for (const mount of mounts) {
const cap = await mount.use(() => mount.vfs.capacity());
const cells = [mount.vfs.name];
if (showType)
cells.push(mount.vfs.name);
for (const item of numCells(cap, human, si, block, inodes))
cells.push(item);
cells.push(pctCell(cap, inodes));
cells.push(rstripSlash(mount.prefix) || '/');
data.push(cells);
}
const table = data.length > 0 ? encodeText(renderTable(header, data, showType)) : null;
if (errors.length > 0) {
return result('df', { out: table, exitCode: 1, stderr: errors.join('') });
}
return ok('df', table);
}
//# sourceMappingURL=df.js.map