@poe-platform/safe-bash 0.1.500 → 0.1.502

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/dist/safe-bash/commands/apply-patch/apply.d.ts.map +1 -1
  2. package/dist/safe-bash/commands/apply-patch/apply.js +5 -2
  3. package/dist/safe-bash/commands/apply-patch/apply.js.map +1 -1
  4. package/dist/safe-bash/commands/archive/extract.d.ts.map +1 -1
  5. package/dist/safe-bash/commands/archive/extract.js +5 -2
  6. package/dist/safe-bash/commands/archive/extract.js.map +1 -1
  7. package/dist/safe-bash/commands/archive/internal.d.ts.map +1 -1
  8. package/dist/safe-bash/commands/archive/internal.js +6 -2
  9. package/dist/safe-bash/commands/archive/internal.js.map +1 -1
  10. package/dist/safe-bash/commands/bytes/compression/files.d.ts.map +1 -1
  11. package/dist/safe-bash/commands/bytes/compression/files.js +10 -6
  12. package/dist/safe-bash/commands/bytes/compression/files.js.map +1 -1
  13. package/dist/safe-bash/commands/diff-patch/diff.d.ts.map +1 -1
  14. package/dist/safe-bash/commands/diff-patch/diff.js +4 -3
  15. package/dist/safe-bash/commands/diff-patch/diff.js.map +1 -1
  16. package/dist/safe-bash/commands/diff-patch/shared.d.ts.map +1 -1
  17. package/dist/safe-bash/commands/diff-patch/shared.js +5 -3
  18. package/dist/safe-bash/commands/diff-patch/shared.js.map +1 -1
  19. package/dist/safe-bash/commands/du/du.d.ts.map +1 -1
  20. package/dist/safe-bash/commands/du/du.js +6 -6
  21. package/dist/safe-bash/commands/du/du.js.map +1 -1
  22. package/dist/safe-bash/commands/file/index.d.ts.map +1 -1
  23. package/dist/safe-bash/commands/file/index.js +9 -3
  24. package/dist/safe-bash/commands/file/index.js.map +1 -1
  25. package/dist/safe-bash/commands/filesystem.d.ts.map +1 -1
  26. package/dist/safe-bash/commands/filesystem.js +25 -9
  27. package/dist/safe-bash/commands/filesystem.js.map +1 -1
  28. package/dist/safe-bash/commands/find.js +1 -1
  29. package/dist/safe-bash/commands/find.js.map +1 -1
  30. package/dist/safe-bash/commands/html-to-markdown/input.d.ts.map +1 -1
  31. package/dist/safe-bash/commands/html-to-markdown/input.js +12 -18
  32. package/dist/safe-bash/commands/html-to-markdown/input.js.map +1 -1
  33. package/dist/safe-bash/commands/internal.d.ts +1 -1
  34. package/dist/safe-bash/commands/internal.d.ts.map +1 -1
  35. package/dist/safe-bash/commands/internal.js +3 -2
  36. package/dist/safe-bash/commands/internal.js.map +1 -1
  37. package/dist/safe-bash/commands/metadata/internal.d.ts +2 -2
  38. package/dist/safe-bash/commands/metadata/internal.d.ts.map +1 -1
  39. package/dist/safe-bash/commands/metadata/internal.js +1 -1
  40. package/dist/safe-bash/commands/metadata/internal.js.map +1 -1
  41. package/dist/safe-bash/commands/metadata/mktemp.d.ts.map +1 -1
  42. package/dist/safe-bash/commands/metadata/mktemp.js +5 -2
  43. package/dist/safe-bash/commands/metadata/mktemp.js.map +1 -1
  44. package/dist/safe-bash/commands/metadata/stat.js +1 -1
  45. package/dist/safe-bash/commands/metadata/stat.js.map +1 -1
  46. package/dist/safe-bash/commands/move.d.ts.map +1 -1
  47. package/dist/safe-bash/commands/move.js +3 -2
  48. package/dist/safe-bash/commands/move.js.map +1 -1
  49. package/dist/safe-bash/commands/network/body.d.ts.map +1 -1
  50. package/dist/safe-bash/commands/network/body.js +3 -1
  51. package/dist/safe-bash/commands/network/body.js.map +1 -1
  52. package/dist/safe-bash/commands/predicates.d.ts.map +1 -1
  53. package/dist/safe-bash/commands/predicates.js +8 -2
  54. package/dist/safe-bash/commands/predicates.js.map +1 -1
  55. package/dist/safe-bash/commands/safejs/index.d.ts.map +1 -1
  56. package/dist/safe-bash/commands/safejs/index.js +2 -1
  57. package/dist/safe-bash/commands/safejs/index.js.map +1 -1
  58. package/dist/safe-bash/commands/split/io.d.ts.map +1 -1
  59. package/dist/safe-bash/commands/split/io.js +2 -1
  60. package/dist/safe-bash/commands/split/io.js.map +1 -1
  61. package/dist/safe-bash/commands/split/split.d.ts.map +1 -1
  62. package/dist/safe-bash/commands/split/split.js +2 -1
  63. package/dist/safe-bash/commands/split/split.js.map +1 -1
  64. package/dist/safe-bash/commands/stream-format/shared.d.ts.map +1 -1
  65. package/dist/safe-bash/commands/stream-format/shared.js +7 -4
  66. package/dist/safe-bash/commands/stream-format/shared.js.map +1 -1
  67. package/dist/safe-bash/commands/stream-inspection/shared.d.ts.map +1 -1
  68. package/dist/safe-bash/commands/stream-inspection/shared.js +7 -4
  69. package/dist/safe-bash/commands/stream-inspection/shared.js.map +1 -1
  70. package/dist/safe-bash/commands/streams.d.ts.map +1 -1
  71. package/dist/safe-bash/commands/streams.js +4 -2
  72. package/dist/safe-bash/commands/streams.js.map +1 -1
  73. package/dist/safe-bash/commands/structured/jq.d.ts.map +1 -1
  74. package/dist/safe-bash/commands/structured/jq.js +15 -5
  75. package/dist/safe-bash/commands/structured/jq.js.map +1 -1
  76. package/dist/safe-bash/commands/table-text/internal.d.ts.map +1 -1
  77. package/dist/safe-bash/commands/table-text/internal.js +5 -4
  78. package/dist/safe-bash/commands/table-text/internal.js.map +1 -1
  79. package/dist/safe-bash/commands/text-programs/awk-runtime.d.ts.map +1 -1
  80. package/dist/safe-bash/commands/text-programs/awk-runtime.js +10 -4
  81. package/dist/safe-bash/commands/text-programs/awk-runtime.js.map +1 -1
  82. package/dist/safe-bash/commands/text.d.ts.map +1 -1
  83. package/dist/safe-bash/commands/text.js +3 -2
  84. package/dist/safe-bash/commands/text.js.map +1 -1
  85. package/dist/safe-bash/commands/time-env/date.d.ts.map +1 -1
  86. package/dist/safe-bash/commands/time-env/date.js +3 -2
  87. package/dist/safe-bash/commands/time-env/date.js.map +1 -1
  88. package/dist/safe-bash/commands/tree/tree.d.ts.map +1 -1
  89. package/dist/safe-bash/commands/tree/tree.js +2 -1
  90. package/dist/safe-bash/commands/tree/tree.js.map +1 -1
  91. package/dist/safe-bash/core.browser.js +226 -118
  92. package/dist/safe-bash/core.browser.js.map +4 -4
  93. package/dist/safe-bash/shell/conditional.d.ts.map +1 -1
  94. package/dist/safe-bash/shell/conditional.js +10 -6
  95. package/dist/safe-bash/shell/conditional.js.map +1 -1
  96. package/dist/safe-bash/shell/input.d.ts +1 -1
  97. package/dist/safe-bash/shell/input.d.ts.map +1 -1
  98. package/dist/safe-bash/shell/input.js +7 -3
  99. package/dist/safe-bash/shell/input.js.map +1 -1
  100. package/dist/safe-bash/shell/runtime.d.ts +2 -1
  101. package/dist/safe-bash/shell/runtime.d.ts.map +1 -1
  102. package/dist/safe-bash/shell/runtime.js +37 -28
  103. package/dist/safe-bash/shell/runtime.js.map +1 -1
  104. package/dist/safe-bash/shell/shell.d.ts.map +1 -1
  105. package/dist/safe-bash/shell/shell.js +3 -1
  106. package/dist/safe-bash/shell/shell.js.map +1 -1
  107. package/package.json +2 -2
@@ -4962,7 +4962,8 @@ async function* input(context2, name2 = "-") {
4962
4962
  } else {
4963
4963
  await assertInputRequirements(context2, [name2]);
4964
4964
  const path = pathOf(context2, name2);
4965
- if (context2.fs.readStream && context2.fs.capabilities.streamingRead !== false) {
4965
+ const capabilities = await context2.fs.capabilitiesFor?.(path, { signal: context2.signal }) ?? context2.fs.capabilities;
4966
+ if (context2.fs.readStream && capabilities.streamingRead !== false) {
4966
4967
  let emitted = false;
4967
4968
  let reading = true;
4968
4969
  try {
@@ -4978,7 +4979,7 @@ async function* input(context2, name2 = "-") {
4978
4979
  if (!reading || emitted || !(error instanceof FsError) || error.code !== "ENOTSUP") throw error;
4979
4980
  }
4980
4981
  }
4981
- if (context2.fs.capabilities.read === false) throw new FsError("ENOTSUP", { syscall: "readFile", path });
4982
+ if (capabilities.read === false) throw new FsError("ENOTSUP", { syscall: "readFile", path });
4982
4983
  yield* readBytes2({
4983
4984
  async *[Symbol.asyncIterator]() {
4984
4985
  const bytes3 = await context2.fs.readFile(path, { signal: context2.signal, maxBytes: bufferLimit });
@@ -5559,8 +5560,9 @@ async function moveAcrossDevices(context2, source2, target, noClobber, budget) {
5559
5560
  for (const entry of [...plan].reverse()) {
5560
5561
  context2.signal.throwIfAborted();
5561
5562
  if (entry.stat.type !== "symlink") {
5562
- if (context2.fs.capabilities.permissions === true && context2.fs.chmod) await context2.fs.chmod(entry.target, entry.stat.mode & 4095, { signal: context2.signal });
5563
- if (context2.fs.capabilities.timestamps === true && context2.fs.utimes) {
5563
+ const capabilities = await context2.fs.capabilitiesFor?.(entry.target, { signal: context2.signal }) ?? context2.fs.capabilities;
5564
+ if (capabilities.permissions === true && context2.fs.chmod) await context2.fs.chmod(entry.target, entry.stat.mode & 4095, { signal: context2.signal });
5565
+ if (capabilities.timestamps === true && context2.fs.utimes) {
5564
5566
  try {
5565
5567
  await context2.fs.utimes(entry.target, entry.stat.atimeMs, entry.stat.mtimeMs, { signal: context2.signal });
5566
5568
  } catch (error) {
@@ -5668,6 +5670,8 @@ function needCapability(context2, capability) {
5668
5670
  if (!context2.fs[capability]) throw new FsError("ENOTSUP", { syscall: capability });
5669
5671
  }
5670
5672
  async function admitEmptyDirectory(context2, path, readDirectory) {
5673
+ const stat = await maybeStat(context2, path, false);
5674
+ if (stat && stat.type !== "directory") throw new FsError("ENOTDIR", { syscall: "rmdir", path });
5671
5675
  try {
5672
5676
  await admitFilesystemModes(context2, "rmdir", ["directory"], [path]);
5673
5677
  } catch (error) {
@@ -5746,14 +5750,15 @@ async function copy(context2, source2, target, flags3, readDirectory, top = true
5746
5750
  }
5747
5751
  if (!preflight) await context2.fs.symlink(linkTarget, target, { signal: context2.signal });
5748
5752
  } else {
5749
- await admitFilesystemModes(context2, "cp", ["file", ...flags3.has("f") && targetStat ? ["replace", "exclusive"] : []], [target]);
5753
+ const replace = flags3.has("f") && targetStat !== void 0 && targetStat.type !== "character";
5754
+ await admitFilesystemModes(context2, "cp", ["file", ...replace ? ["replace", "exclusive"] : []], [target]);
5750
5755
  if (targetStat?.type === "directory") throw new FsError("EISDIR", { path: target });
5751
5756
  if (preflight) return;
5752
5757
  try {
5753
5758
  await context2.fs.copyFile(source2, target, { signal: context2.signal });
5754
5759
  } catch (error) {
5755
5760
  context2.signal.throwIfAborted();
5756
- if (!flags3.has("f") || !targetStat || codeOf(error) !== "EACCES") throw error;
5761
+ if (!replace || codeOf(error) !== "EACCES") throw error;
5757
5762
  const existing2 = await maybeStat(context2, target, false);
5758
5763
  if (existing2) {
5759
5764
  const sourceEntry = await context2.fs.lstat(source2, { signal: context2.signal });
@@ -5772,7 +5777,7 @@ async function copy(context2, source2, target, flags3, readDirectory, top = true
5772
5777
  `);
5773
5778
  }
5774
5779
  function modeText(stat) {
5775
- let text3 = stat.type === "directory" ? "d" : stat.type === "symlink" ? "l" : "-";
5780
+ let text3 = stat.type === "directory" ? "d" : stat.type === "symlink" ? "l" : stat.type === "character" ? "c" : "-";
5776
5781
  for (const shift of [6, 3, 0]) {
5777
5782
  const mode = stat.mode >> shift;
5778
5783
  text3 += (mode & 4 ? "r" : "-") + (mode & 2 ? "w" : "-") + (mode & 1 ? "x" : "-");
@@ -5790,17 +5795,23 @@ function filesystemCommands(maxDirectoryEntries) {
5790
5795
  requireOperands(parsed.operands);
5791
5796
  const mode = value(parsed, "m");
5792
5797
  if (mode !== void 0 && !/^[0-7]{1,4}$/u.test(mode)) throw new UsageError(`invalid mode '${mode}' (octal required)`);
5793
- await preflightOperands(context2, parsed.operands, (operand) => admitFilesystemModes(
5794
- context2,
5795
- "mkdir",
5796
- [parsed.flags.has("p") ? "parents" : "directory"],
5797
- [pathOf(context2, operand)]
5798
- ));
5799
- return eachOperand(context2, parsed.operands, async (operand) => {
5800
- await context2.fs.mkdir(pathOf(context2, operand), { recursive: parsed.flags.has("p"), ...mode === void 0 ? {} : { mode: parseInt(mode, 8) }, signal: context2.signal });
5801
- if (parsed.flags.has("v")) await output(context2, `mkdir: created directory '${escapeText(operand, "display")}'
5798
+ const createDirectory = async (operand, preflight) => {
5799
+ const path = pathOf(context2, operand);
5800
+ const recursive = parsed.flags.has("p");
5801
+ const stat = await maybeStat(context2, path, recursive);
5802
+ if (stat) {
5803
+ if (recursive && stat.type === "directory") return;
5804
+ throw new FsError("EEXIST", { syscall: "mkdir", path });
5805
+ }
5806
+ await admitFilesystemModes(context2, "mkdir", [recursive ? "parents" : "directory"], [path]);
5807
+ if (!preflight) {
5808
+ await context2.fs.mkdir(path, { recursive, ...mode === void 0 ? {} : { mode: parseInt(mode, 8) }, signal: context2.signal });
5809
+ if (parsed.flags.has("v")) await output(context2, `mkdir: created directory '${escapeText(operand, "display")}'
5802
5810
  `);
5803
- });
5811
+ }
5812
+ };
5813
+ await preflightOperands(context2, parsed.operands, (operand) => createDirectory(operand, true));
5814
+ return eachOperand(context2, parsed.operands, (operand) => createDirectory(operand, false));
5804
5815
  }),
5805
5816
  define("touch", async (context2) => {
5806
5817
  const parsed = options2(context2.args, "camr:", { "no-create": "c", reference: "r" });
@@ -7170,8 +7181,15 @@ function streamCommands(maxTeeTargets = 64, maxTailFollowHandles = 64) {
7170
7181
  try {
7171
7182
  for (const operand of parsed.operands) {
7172
7183
  try {
7173
- assertCommandRequirements(context2, teeRequirements, [parsed.flags.has("a") ? "append" : "overwrite"]);
7174
- targets.add(await openFileOutput(context2, pathOf(context2, operand), parsed.flags.has("a") ? "a" : "w"));
7184
+ const path = pathOf(context2, operand);
7185
+ const capabilities = await context2.fs.capabilitiesFor?.(path, { signal: context2.signal }) ?? context2.fs.capabilities;
7186
+ assertCommandRequirements(
7187
+ context2,
7188
+ teeRequirements,
7189
+ [parsed.flags.has("a") ? "append" : "overwrite"],
7190
+ context2.fs.capabilities.readOnly === true ? { ...capabilities, readOnly: true } : capabilities
7191
+ );
7192
+ targets.add(await openFileOutput(context2, path, parsed.flags.has("a") ? "a" : "w"));
7175
7193
  } catch (error) {
7176
7194
  context2.signal.throwIfAborted();
7177
7195
  await diagnostic(context2, error);
@@ -7526,9 +7544,10 @@ async function emitRecords(context2, records4, destination2) {
7526
7544
  return;
7527
7545
  }
7528
7546
  await admitTextOutput(context2, destination2);
7529
- if (context2.fs.writeStream && context2.fs.capabilities.streamingWrite !== false) await context2.fs.writeStream(pathOf(context2, destination2), records4, { signal: context2.signal });
7547
+ const capabilities = await context2.fs.capabilitiesFor?.(pathOf(context2, destination2), { signal: context2.signal }) ?? context2.fs.capabilities;
7548
+ if (context2.fs.writeStream && capabilities.streamingWrite !== false) await context2.fs.writeStream(pathOf(context2, destination2), records4, { signal: context2.signal });
7530
7549
  else {
7531
- if (context2.fs.capabilities.write === false) throw new FsError("ENOTSUP", { syscall: "writeFile", path: pathOf(context2, destination2) });
7550
+ if (capabilities.write === false) throw new FsError("ENOTSUP", { syscall: "writeFile", path: pathOf(context2, destination2) });
7532
7551
  let size = 0;
7533
7552
  const chunks = [];
7534
7553
  for await (const bytes3 of records4) {
@@ -7901,7 +7920,7 @@ function predicateCommands() {
7901
7920
  if (!args.length) return { exitCode: 1 };
7902
7921
  if (args.length === 1) return { exitCode: args[0] ? 0 : 1 };
7903
7922
  if (args.length === 2 && args[0] === "!") return { exitCode: args[1] ? 1 : 0 };
7904
- const unary2 = /* @__PURE__ */ new Set(["-n", "-z", "-e", "-a", "-f", "-d", "-L", "-h", "-s", "-r", "-w", "-x"]);
7923
+ const unary2 = /* @__PURE__ */ new Set(["-n", "-z", "-e", "-a", "-f", "-c", "-d", "-L", "-h", "-s", "-r", "-w", "-x"]);
7905
7924
  const binary3 = /* @__PURE__ */ new Set(["=", "==", "!=", "<", ">", "-eq", "-ne", "-lt", "-le", "-gt", "-ge", "-nt", "-ot", "-ef"]);
7906
7925
  let offset = 0;
7907
7926
  const number4 = (text3) => {
@@ -7936,7 +7955,9 @@ function predicateCommands() {
7936
7955
  const rightStat = await metadata(context2, right);
7937
7956
  if (operator === "-nt") return leftStat !== void 0 && (!rightStat || leftStat.mtimeMs > rightStat.mtimeMs);
7938
7957
  if (operator === "-ot") return rightStat !== void 0 && (!leftStat || leftStat.mtimeMs < rightStat.mtimeMs);
7939
- return leftStat?.ino !== void 0 && rightStat?.ino !== void 0 && leftStat.ino === rightStat.ino && leftStat.dev === rightStat.dev;
7958
+ const identity2 = compareCopyIdentity(leftStat, rightStat);
7959
+ if (identity2 !== "unknown") return identity2 === "same";
7960
+ return leftStat?.ino !== void 0 && rightStat?.ino !== void 0 && leftStat.type === rightStat.type && leftStat.ino === rightStat.ino && leftStat.dev === rightStat.dev;
7940
7961
  }
7941
7962
  const leftNumber = number4(token);
7942
7963
  const rightNumber = number4(right);
@@ -7974,6 +7995,7 @@ function predicateCommands() {
7974
7995
  const stat = await metadata(context2, operand, token === "-L" || token === "-h");
7975
7996
  if (!stat) return false;
7976
7997
  if (token === "-f") return stat.type === "file";
7998
+ if (token === "-c") return stat.type === "character";
7977
7999
  if (token === "-d") return stat.type === "directory";
7978
8000
  if (token === "-L" || token === "-h") return stat.type === "symlink";
7979
8001
  if (token === "-s") return stat.size > 0;
@@ -8924,7 +8946,7 @@ function findCommands(execute5, maxDirectoryEntries) {
8924
8946
  };
8925
8947
  }
8926
8948
  if (token === "-type") {
8927
- const types = { f: "file", d: "directory", l: "symlink" };
8949
+ const types = { f: "file", d: "directory", l: "symlink", c: "character" };
8928
8950
  if (!types[operand]) throw new UsageError(`unsupported file type '${operand}'`);
8929
8951
  return async (entry) => entry.stat.type === types[operand];
8930
8952
  }
@@ -11711,8 +11733,14 @@ var AwkRuntime = class {
11711
11733
  const useStdin = file === "-";
11712
11734
  const source2 = (async function* () {
11713
11735
  if (useStdin) yield* context2.stdin;
11714
- else if (context2.fs.readStream) yield* context2.fs.readStream(name2, { signal: context2.signal });
11715
- else yield await context2.fs.readFile(name2, { signal: context2.signal, maxBytes: budget.maxBufferBytes });
11736
+ else {
11737
+ budget.step();
11738
+ await budget.checkpoint();
11739
+ const capabilities = await context2.fs.capabilitiesFor?.(name2, { signal: context2.signal }) ?? context2.fs.capabilities;
11740
+ context2.signal.throwIfAborted();
11741
+ if (context2.fs.readStream && capabilities.streamingRead !== false) yield* context2.fs.readStream(name2, { signal: context2.signal });
11742
+ else yield await context2.fs.readFile(name2, { signal: context2.signal, maxBytes: budget.maxBufferBytes });
11743
+ }
11716
11744
  })();
11717
11745
  try {
11718
11746
  reader = new Reader(source2, budget, this.retention);
@@ -14338,10 +14366,12 @@ function argumentsFor(args, budget) {
14338
14366
  return options3;
14339
14367
  }
14340
14368
  async function readProgram2(context2, path, limits) {
14341
- const absolute = resolvePath(context2.cwd, path);
14369
+ const absolute = pathOf(context2, path);
14342
14370
  const chunks = [];
14343
14371
  let size = 0;
14344
- if (context2.fs.readStream) {
14372
+ const capabilities = context2.fs.capabilitiesFor ? await interruptible(() => context2.fs.capabilitiesFor(absolute, { signal: context2.signal }), context2.signal) : context2.fs.capabilities;
14373
+ context2.signal.throwIfAborted();
14374
+ if (context2.fs.readStream && capabilities.streamingRead !== false) {
14345
14375
  for await (const chunk of readBytes2(context2.fs.readStream(absolute, { signal: context2.signal }), context2.signal)) {
14346
14376
  size += chunk.byteLength;
14347
14377
  if (size > limits.maxSourceBytes) throw new JqLimitError("maxSourceBytes");
@@ -14368,9 +14398,12 @@ async function* inputSources(context2, options3, budget) {
14368
14398
  usedStdin = true;
14369
14399
  source2 = context2.stdin;
14370
14400
  } else {
14371
- const absolute = resolvePath(context2.cwd, file);
14401
+ const absolute = pathOf(context2, file);
14372
14402
  const remaining = budget.limits.maxInputBytes - budget.inputBytes;
14373
- if (context2.fs.readStream) source2 = context2.fs.readStream(absolute, { signal: context2.signal });
14403
+ await budget.tick();
14404
+ const capabilities = context2.fs.capabilitiesFor ? await interruptible(() => context2.fs.capabilitiesFor(absolute, { signal: context2.signal }), context2.signal) : context2.fs.capabilities;
14405
+ context2.signal.throwIfAborted();
14406
+ if (context2.fs.readStream && capabilities.streamingRead !== false) source2 = context2.fs.readStream(absolute, { signal: context2.signal });
14374
14407
  else source2 = toByteSource(await interruptible(() => context2.fs.readFile(absolute, { signal: context2.signal, maxBytes: remaining }), context2.signal));
14375
14408
  }
14376
14409
  budget.inputLocation = { name: file === "-" ? "<stdin>" : file, line: 0, complete: false };
@@ -21010,10 +21043,11 @@ async function planOperands(context2, options3) {
21010
21043
  continue;
21011
21044
  }
21012
21045
  if (!name2) throw new FsError("ENOENT", { path: name2 });
21013
- if (!context2.fs.readStream || context2.fs.capabilities.streamingRead === false) {
21046
+ const source2 = pathOf(context2, name2);
21047
+ const sourceCapabilities = await context2.fs.capabilitiesFor?.(source2, { signal: context2.signal }) ?? context2.fs.capabilities;
21048
+ if (!context2.fs.readStream || sourceCapabilities.streamingRead === false) {
21014
21049
  throw new FsError("ENOTSUP", { message: "named input requires VFS streaming reads; no readFile fallback" });
21015
21050
  }
21016
- const source2 = pathOf(context2, name2);
21017
21051
  const sourceStat = await context2.fs.lstat(source2, { signal: context2.signal });
21018
21052
  if (sourceStat.type !== "file") throw new FsError("EINVAL", { path: source2, message: "input must be a regular, non-symlink file" });
21019
21053
  const realSource = await context2.fs.realpath(source2, { signal: context2.signal });
@@ -21024,20 +21058,22 @@ async function planOperands(context2, options3) {
21024
21058
  if (context2.fs.capabilities.readOnly === true) {
21025
21059
  throw new FsError("EROFS", { syscall: context2.command, path: realSource });
21026
21060
  }
21027
- if (!context2.fs.writeStream || context2.fs.capabilities.streamingWrite === false) {
21028
- throw new FsError("ENOTSUP", { message: "file output requires VFS streaming writes (use -c for stdout)" });
21029
- }
21030
21061
  if (!options3.keep && !options3.force && (sourceStat.nlink ?? 1) > 1) {
21031
21062
  throw new FsError("EINVAL", { path: source2, message: "input has multiple links (use -k or -f)" });
21032
21063
  }
21033
21064
  const destination2 = outputPath(realSource, options3);
21065
+ const capabilities = await context2.fs.capabilitiesFor?.(destination2, { signal: context2.signal }) ?? context2.fs.capabilities;
21066
+ if (capabilities.readOnly === true) throw new FsError("EROFS", { syscall: context2.command, path: destination2 });
21067
+ if (!context2.fs.writeStream || capabilities.streamingWrite === false) {
21068
+ throw new FsError("ENOTSUP", { message: "file output requires VFS streaming writes (use -c for stdout)" });
21069
+ }
21034
21070
  const destinationStat = await existing(context2, destination2);
21035
21071
  if (destinationStat) {
21036
21072
  if (destinationStat.type !== "file" || sameIdentity(sourceStat, destinationStat) || realSource === await context2.fs.realpath(destination2, { signal: context2.signal })) {
21037
21073
  throw new FsError("EINVAL", { path: destination2, message: "destination is not a distinct regular file" });
21038
21074
  }
21039
21075
  if (!options3.force) throw new FsError("EEXIST", { path: destination2 });
21040
- if (context2.fs.capabilities.atomicRename !== true) {
21076
+ if (capabilities.atomicRename !== true) {
21041
21077
  throw new FsError("ENOTSUP", { path: destination2, message: "forced replacement requires VFS atomicRename" });
21042
21078
  }
21043
21079
  }
@@ -21370,7 +21406,8 @@ var Budget11 = class {
21370
21406
  async read(path) {
21371
21407
  this.context.signal.throwIfAborted();
21372
21408
  const remaining = this.limits.maxInputBytes - this.inputBytes;
21373
- const bytes3 = path === "-" ? await collectBytes2(this.chunks(this.context.stdin), { signal: this.context.signal, maxBytes: remaining }) : this.context.fs.readStream ? await collectBytes2(this.chunks(this.context.fs.readStream(path, { signal: this.context.signal })), { signal: this.context.signal, maxBytes: remaining }) : await host(this.context, () => this.context.fs.readFile(path, { signal: this.context.signal, maxBytes: remaining }));
21409
+ const capabilities = path === "-" ? void 0 : await host(this.context, async () => await this.context.fs.capabilitiesFor?.(path, { signal: this.context.signal }) ?? this.context.fs.capabilities);
21410
+ const bytes3 = path === "-" ? await collectBytes2(this.chunks(this.context.stdin), { signal: this.context.signal, maxBytes: remaining }) : this.context.fs.readStream && capabilities?.streamingRead !== false ? await collectBytes2(this.chunks(this.context.fs.readStream(path, { signal: this.context.signal })), { signal: this.context.signal, maxBytes: remaining }) : await host(this.context, () => this.context.fs.readFile(path, { signal: this.context.signal, maxBytes: remaining }));
21374
21411
  this.inputBytes += bytes3.byteLength;
21375
21412
  if (this.inputBytes > this.limits.maxInputBytes) throw new ToolError("input byte limit exceeded");
21376
21413
  return this.text(bytes3);
@@ -21405,7 +21442,7 @@ async function host(context2, operation2) {
21405
21442
  async function inspect(budget, path) {
21406
21443
  const context2 = budget.context;
21407
21444
  if (path.length > 4096) throw new ToolError("path length limit exceeded");
21408
- const absolute = resolvePath(context2.cwd, path);
21445
+ const absolute = pathOf(context2, path);
21409
21446
  const parts2 = absolute.split("/").filter(Boolean);
21410
21447
  if (absolute.length > 4096 || parts2.length > 256) throw new ToolError("path length/depth limit exceeded");
21411
21448
  let current = "";
@@ -21794,7 +21831,7 @@ async function run(context2, budget) {
21794
21831
  const names = /* @__PURE__ */ new Set();
21795
21832
  for (const path of [leftStat ? left : void 0, rightStat ? right : void 0]) {
21796
21833
  if (path === void 0) continue;
21797
- const entries3 = await host(context2, () => context2.fs.readdir(resolvePath(context2.cwd, path), { signal: context2.signal }));
21834
+ const entries3 = await host(context2, () => context2.fs.readdir(pathOf(context2, path), { signal: context2.signal }));
21798
21835
  for (const entry of entries3) {
21799
21836
  budget.step();
21800
21837
  if (!entry.name || entry.name === "." || entry.name === ".." || /[\/\\\0\r\n\t]/u.test(entry.name)) throw new ToolError("unsafe directory entry name");
@@ -21808,7 +21845,7 @@ async function run(context2, budget) {
21808
21845
  const read = async (path, exists) => {
21809
21846
  if (!exists) return "";
21810
21847
  if (path === "-") return stdin ??= await budget.read("-");
21811
- return budget.read(resolvePath(context2.cwd, path));
21848
+ return budget.read(pathOf(context2, path));
21812
21849
  };
21813
21850
  const oldText = await read(left, !!leftStat);
21814
21851
  const newText = await read(right, !!rightStat);
@@ -23070,7 +23107,7 @@ function metadataCommand(name2, handler) {
23070
23107
  } };
23071
23108
  }
23072
23109
  function permissionString(mode, type2) {
23073
- let text3 = type2 === "directory" ? "d" : type2 === "symlink" ? "l" : "-";
23110
+ let text3 = type2 === "directory" ? "d" : type2 === "symlink" ? "l" : type2 === "character" ? "c" : "-";
23074
23111
  for (const [read, write2, execute5, special2, lower, upper] of [
23075
23112
  [256, 128, 64, 2048, "s", "S"],
23076
23113
  [32, 16, 8, 1024, "s", "S"],
@@ -23390,7 +23427,7 @@ async function render(context2, path, name2, stat, format2, escapes, limit) {
23390
23427
  }
23391
23428
  } else if (code === "%") text3 = "%";
23392
23429
  else if (code === "A") text3 = permissionString(stat.mode, stat.type);
23393
- else if (code === "F") text3 = stat.type === "directory" ? "directory" : stat.type === "symlink" ? "symbolic link" : stat.size === 0 ? "regular empty file" : "regular file";
23430
+ else if (code === "F") text3 = stat.type === "directory" ? "directory" : stat.type === "symlink" ? "symbolic link" : stat.type === "character" ? "character special file" : stat.size === 0 ? "regular empty file" : "regular file";
23394
23431
  else if (["x", "y", "z", "w"].includes(code)) {
23395
23432
  const value2 = times[code.toUpperCase()];
23396
23433
  text3 = code === "w" && value2 === void 0 ? "-" : timestamp2(available(value2, code));
@@ -23515,7 +23552,6 @@ function createMktempCommand(configuration = {}) {
23515
23552
  try {
23516
23553
  if (!parsed.dryRun) {
23517
23554
  if (context2.fs.capabilities.readOnly) throw new FsError("EROFS", { syscall: "mktemp" });
23518
- if (context2.fs.capabilities.permissions !== true) throw new FsError("ENOTSUP", { syscall: "mktemp", message: "private temporary creation requires declared permission support" });
23519
23555
  }
23520
23556
  for (let attempt = 0; attempt < configured.limits.maxAttempts; attempt++) {
23521
23557
  await budget.step();
@@ -23534,6 +23570,9 @@ function createMktempCommand(configuration = {}) {
23534
23570
  if (codeOf(error) !== "ENOENT") throw error;
23535
23571
  }
23536
23572
  } else {
23573
+ const capabilities = await context2.fs.capabilitiesFor?.(path, { signal: context2.signal }) ?? context2.fs.capabilities;
23574
+ if (capabilities.readOnly === true) throw new FsError("EROFS", { syscall: "mktemp" });
23575
+ if (capabilities.permissions !== true) throw new FsError("ENOTSUP", { syscall: "mktemp", message: "private temporary creation requires declared permission support" });
23537
23576
  try {
23538
23577
  const mode = (parsed.directory ? 448 : 384) & ~configured.umask;
23539
23578
  if (parsed.directory) await context2.fs.mkdir(path, { mode, recursive: false, signal: context2.signal });
@@ -23714,7 +23753,9 @@ async function* bounded(source2, maximum, signal, chunkSize2) {
23714
23753
  }
23715
23754
  async function* fileSource(context2, path, limits) {
23716
23755
  context2.signal.throwIfAborted();
23717
- if (context2.fs.readStream) {
23756
+ const capabilities = await operation(context2, () => context2.fs.capabilitiesFor?.(path, { signal: context2.signal }) ?? context2.fs.capabilities);
23757
+ context2.signal.throwIfAborted();
23758
+ if (context2.fs.readStream && capabilities.streamingRead !== false) {
23718
23759
  yield* readBytes2(context2.fs.readStream(path, { signal: context2.signal, chunkSize: limits.chunkSize }), context2.signal);
23719
23760
  } else {
23720
23761
  const stat = await operation(context2, () => context2.fs.stat(path, { signal: context2.signal }));
@@ -23726,7 +23767,9 @@ async function* fileSource(context2, path, limits) {
23726
23767
  }
23727
23768
  async function publish2(context2, path, source2, mode = 384) {
23728
23769
  const options3 = { signal: context2.signal, flag: "wx", mode };
23729
- if (context2.fs.writeStream) {
23770
+ const capabilities = await operation(context2, () => context2.fs.capabilitiesFor?.(path, { signal: context2.signal }) ?? context2.fs.capabilities);
23771
+ context2.signal.throwIfAborted();
23772
+ if (context2.fs.writeStream && capabilities.streamingWrite !== false) {
23730
23773
  let finished = false;
23731
23774
  const observed = (async function* () {
23732
23775
  yield* readBytes2(source2, context2.signal);
@@ -24492,7 +24535,7 @@ async function readArchive(context2, source2, options3, budget) {
24492
24535
  archivePath = await operation(context2, () => context2.fs.realpath(vfsPath(context2.cwd, options3.archive), { signal: context2.signal }));
24493
24536
  archiveStat = await operation(context2, () => context2.fs.stat(archivePath, { signal: context2.signal }));
24494
24537
  }
24495
- if (options3.mode === "x") await checkRoot(context2, resolvePath(options3.cwd));
24538
+ if (options3.mode === "x") await checkRoot(context2, options3.cwd);
24496
24539
  try {
24497
24540
  while (true) {
24498
24541
  const block = await reader.exact(512);
@@ -24553,7 +24596,10 @@ async function readArchive(context2, source2, options3, budget) {
24553
24596
  const operand = options3.operands[index];
24554
24597
  const wanted = operand.name.replace(/^\/+/u, "").replace(/\/+$/u, "");
24555
24598
  if (name2.replace(/\/+$/u, "") === wanted || name2.startsWith(`${wanted}/`)) {
24556
- if (!selected) root = resolvePath(operand.cwd);
24599
+ if (!selected) {
24600
+ if (options3.mode === "x") await checkRoot(context2, operand.cwd);
24601
+ root = resolvePath(operand.cwd);
24602
+ }
24557
24603
  selected = true;
24558
24604
  matched.add(index);
24559
24605
  }
@@ -24864,10 +24910,11 @@ var Inputs = class {
24864
24910
  let source2;
24865
24911
  if (name2 === "-") source2 = this.context.stdin;
24866
24912
  else {
24867
- const path = resolvePath(this.context.cwd, name2);
24913
+ const path = pathOf(this.context, name2);
24868
24914
  const stat = await this.context.fs.stat(path, { signal: this.signal });
24869
24915
  if (stat.type === "directory") throw new FsError("EISDIR", { path });
24870
- if (this.context.fs.readStream) source2 = this.context.fs.readStream(path, { signal: this.signal });
24916
+ const capabilities = await this.context.fs.capabilitiesFor?.(path, { signal: this.signal }) ?? this.context.fs.capabilities;
24917
+ if (this.context.fs.readStream && capabilities.streamingRead !== false) source2 = this.context.fs.readStream(path, { signal: this.signal });
24871
24918
  else {
24872
24919
  const { context: context2, signal, budget } = this;
24873
24920
  source2 = (async function* () {
@@ -25430,11 +25477,14 @@ var Session = class {
25430
25477
  } };
25431
25478
  } else {
25432
25479
  if (!name2) throw new FsError("ENOENT", { path: name2 });
25433
- const path = resolvePath(session.context.cwd, name2);
25480
+ const path = pathOf(session.context, name2);
25434
25481
  const stat = await session.context.fs.stat(path, { signal });
25435
25482
  signal.throwIfAborted();
25436
25483
  if (stat.type === "directory") throw new FsError("EISDIR", { path });
25437
- if (session.context.fs.readStream) yield* session.context.fs.readStream(path, { signal });
25484
+ await session.step();
25485
+ const capabilities = await session.context.fs.capabilitiesFor?.(path, { signal }) ?? session.context.fs.capabilities;
25486
+ signal.throwIfAborted();
25487
+ if (session.context.fs.readStream && capabilities.streamingRead !== false) yield* session.context.fs.readStream(path, { signal });
25438
25488
  else yield await session.context.fs.readFile(path, { signal, maxBytes: Math.min(session.limits.maxChunkBytes, session.limits.maxInputBytes - session.inputBytes) });
25439
25489
  }
25440
25490
  })();
@@ -25937,11 +25987,14 @@ var Session2 = class {
25937
25987
  } };
25938
25988
  } else {
25939
25989
  if (!name2) throw new FsError("ENOENT", { path: name2 });
25940
- const path = resolvePath(session.context.cwd, name2);
25990
+ const path = pathOf(session.context, name2);
25941
25991
  const stat = await session.context.fs.stat(path, { signal });
25942
25992
  signal.throwIfAborted();
25943
25993
  if (stat.type === "directory") throw new FsError("EISDIR", { path });
25944
- if (session.context.fs.readStream) yield* session.context.fs.readStream(path, { signal });
25994
+ await session.step();
25995
+ const capabilities = await session.context.fs.capabilitiesFor?.(path, { signal }) ?? session.context.fs.capabilities;
25996
+ signal.throwIfAborted();
25997
+ if (session.context.fs.readStream && capabilities.streamingRead !== false) yield* session.context.fs.readStream(path, { signal });
25945
25998
  else yield await session.context.fs.readFile(path, { signal, maxBytes: Math.min(session.limits.maxChunkBytes, session.limits.maxInputBytes - session.inputBytes) });
25946
25999
  }
25947
26000
  })();
@@ -26710,7 +26763,8 @@ var Cursor = class {
26710
26763
  if (input3 === "-") yield* readBytes2(context2.stdin, signal);
26711
26764
  else {
26712
26765
  const path = pathOf(context2, input3);
26713
- if (context2.fs.readStream && context2.fs.capabilities.streamingRead !== false) {
26766
+ const capabilities = await interruptible2(() => Promise.resolve(context2.fs.capabilitiesFor?.(path, { signal }) ?? context2.fs.capabilities), signal);
26767
+ if (context2.fs.readStream && capabilities.streamingRead !== false) {
26714
26768
  yield* readBytes2(context2.fs.readStream(path, { signal, chunkSize: limits.maxChunkBytes }), signal);
26715
26769
  } else {
26716
26770
  const maxBytes = Math.min(limits.maxInputBytes, limits.maxBufferBytes);
@@ -26977,7 +27031,8 @@ async function run3(context2, limits) {
26977
27031
  yield chunk;
26978
27032
  }
26979
27033
  })();
26980
- if (context2.fs.writeStream && context2.fs.capabilities.streamingWrite !== false) {
27034
+ const capabilities = await interruptible2(() => Promise.resolve(context2.fs.capabilitiesFor?.(destination2.path, { signal }) ?? context2.fs.capabilities), signal);
27035
+ if (context2.fs.writeStream && capabilities.streamingWrite !== false) {
26981
27036
  await interruptible2(() => context2.fs.writeStream(destination2.path, source2, { signal, flag: destination2.flag }), signal);
26982
27037
  } else {
26983
27038
  const bytes3 = await collectBytes2(source2, { signal, maxBytes: limits.maxBufferBytes });
@@ -27706,7 +27761,7 @@ function createDateCommand(configuration) {
27706
27761
  if (parsed.reference !== void 0) {
27707
27762
  try {
27708
27763
  if (!parsed.reference) throw new FsError("ENOENT", { path: parsed.reference });
27709
- const stat = await context2.fs.stat(resolvePath(context2.cwd, parsed.reference), { signal: context2.signal });
27764
+ const stat = await context2.fs.stat(pathOf(context2, parsed.reference), { signal: context2.signal });
27710
27765
  context2.signal.throwIfAborted();
27711
27766
  instant = millisecondsInstant(stat.mtimeMs);
27712
27767
  } catch (error) {
@@ -28419,7 +28474,7 @@ var Walker2 = class {
28419
28474
  this.budget.text(operand);
28420
28475
  this.budget.entry();
28421
28476
  if (this.args.json && index) await this.write(`,${this.newline()}`);
28422
- const entry = await this.inspect(resolvePath(this.budget.context.cwd, operand), operand, operand);
28477
+ const entry = await this.inspect(pathOf(this.budget.context, operand), operand, operand);
28423
28478
  await this.visit(entry, [], "", true, 0);
28424
28479
  }
28425
28480
  if (this.args.report) {
@@ -28849,10 +28904,11 @@ async function inspect2(context2, name2, follow, describe2, budget, stdinUsed) {
28849
28904
  return result3;
28850
28905
  }
28851
28906
  if (!name2 || name2.includes("\0")) throw new FsError(name2 ? "EINVAL" : "ENOENT", { path: name2 });
28852
- const path = resolvePath(context2.cwd, name2);
28907
+ const path = pathOf(context2, name2);
28853
28908
  const fs = context2.fs;
28854
28909
  const stat = await budget.host(() => follow ? fs.stat(path, { signal: budget.signal }) : fs.lstat(path, { signal: budget.signal }));
28855
28910
  if (stat.type === "directory") return { description: "directory", mime: "inode/directory", encoding: "binary" };
28911
+ if (stat.type === "character") return { description: "character special", mime: "inode/chardevice", encoding: "binary" };
28856
28912
  if (stat.type === "symlink") {
28857
28913
  if (follow) throw new FsError("ENOTSUP", { path, message: "followed stat returned a symbolic link" });
28858
28914
  const target = fs.readlink ? await budget.host(() => fs.readlink(path, { signal: budget.signal })) : void 0;
@@ -28861,7 +28917,8 @@ async function inspect2(context2, name2, follow, describe2, budget, stdinUsed) {
28861
28917
  }
28862
28918
  if (stat.type !== "file") throw new FsError("ENOTSUP", { path, message: "unsupported filesystem entry type" });
28863
28919
  let sample;
28864
- if (fs.readStream) {
28920
+ const capabilities = fs.capabilitiesFor ? await budget.host(() => fs.capabilitiesFor(path, { signal: budget.signal })) : fs.capabilities;
28921
+ if (fs.readStream && capabilities.streamingRead !== false) {
28865
28922
  const controller = new AbortController();
28866
28923
  const signal = AbortSignal.any([budget.signal, controller.signal]);
28867
28924
  try {
@@ -30226,25 +30283,17 @@ var Inputs2 = class {
30226
30283
  if (name2 === "-") source2 = this.context.stdin;
30227
30284
  else {
30228
30285
  const context2 = this.context, budget = this.budget, path = pathOf(context2, name2);
30229
- if (context2.fs.readStream) source2 = context2.fs.readStream(path, { signal: context2.signal, chunkSize: 16384 });
30230
- else {
30231
- let consumed = false;
30232
- source2 = { [Symbol.asyncIterator]() {
30233
- return {
30234
- async next() {
30235
- if (consumed) return { done: true, value: void 0 };
30236
- consumed = true;
30237
- const value2 = await context2.fs.readFile(path, { signal: context2.signal, maxBytes: budget.limits.maxInputBytes - budget.input });
30238
- context2.signal.throwIfAborted();
30239
- return { done: false, value: value2 };
30240
- },
30241
- async return() {
30242
- consumed = true;
30243
- return { done: true, value: void 0 };
30244
- }
30245
- };
30246
- } };
30247
- }
30286
+ source2 = (async function* () {
30287
+ const capabilities = await context2.fs.capabilitiesFor?.(path, { signal: context2.signal }) ?? context2.fs.capabilities;
30288
+ context2.signal.throwIfAborted();
30289
+ if (context2.fs.readStream && capabilities.streamingRead !== false) {
30290
+ yield* readBytes2(context2.fs.readStream(path, { signal: context2.signal, chunkSize: 16384 }), context2.signal);
30291
+ } else {
30292
+ const value2 = await context2.fs.readFile(path, { signal: context2.signal, maxBytes: budget.limits.maxInputBytes - budget.input });
30293
+ context2.signal.throwIfAborted();
30294
+ yield value2;
30295
+ }
30296
+ })();
30248
30297
  }
30249
30298
  const cursor = new Cursor2(source2, this.context.signal);
30250
30299
  this.cursors.push(cursor);
@@ -31249,7 +31298,7 @@ var Walker3 = class {
31249
31298
  let stat;
31250
31299
  try {
31251
31300
  stat = await this.budget.fs(() => context2.fs.lstat(path, { signal: context2.signal }));
31252
- if (!stat || !["file", "directory", "symlink"].includes(stat.type)) throw new PublicDiagnostic("invalid entry type");
31301
+ if (!stat || !["file", "directory", "symlink", "character"].includes(stat.type)) throw new PublicDiagnostic("invalid entry type");
31253
31302
  } catch (error) {
31254
31303
  await this.failure(error, display2);
31255
31304
  return { bytes: 0, complete: false };
@@ -31281,10 +31330,9 @@ var Walker3 = class {
31281
31330
  const { context: context2 } = this.budget;
31282
31331
  this.budget.text(context2.cwd);
31283
31332
  const paths = this.args.operands.map((operand) => {
31284
- const path = resolvePath(context2.cwd, operand);
31285
- const resolved = operand.endsWith("/") && path !== "/" ? path + "/" : path;
31286
- this.budget.text(resolved);
31287
- return resolved;
31333
+ const path = pathOf(context2, operand === "" ? "." : operand);
31334
+ this.budget.text(path);
31335
+ return path;
31288
31336
  });
31289
31337
  for (let index = 0; index < paths.length; index++) {
31290
31338
  const amount = await this.walk(paths[index], this.args.operands[index], 0);
@@ -33095,7 +33143,10 @@ var Invocation2 = class {
33095
33143
  try {
33096
33144
  await this.work.fs(target, () => this.context.fs.access(target, 2, { signal: this.context.signal }));
33097
33145
  } catch (error) {
33098
- if (error instanceof FileFailure2 && (error.error.code === "ENOTSUP" || error.error.code === "EOPNOTSUPP") && this.context.fs.capabilities.permissions !== true) return;
33146
+ if (error instanceof FileFailure2 && (error.error.code === "ENOTSUP" || error.error.code === "EOPNOTSUPP")) {
33147
+ const capabilities = await this.work.fs(target, async () => await this.context.fs.capabilitiesFor?.(target, { signal: this.context.signal }) ?? this.context.fs.capabilities);
33148
+ if (capabilities.permissions !== true) return;
33149
+ }
33099
33150
  throw error;
33100
33151
  }
33101
33152
  }
@@ -33382,6 +33433,7 @@ init_platform();
33382
33433
 
33383
33434
  // packages/safe-bash/src/shell/shell.ts
33384
33435
  init_platform();
33436
+ import { createDeviceFileSystem } from "@poe-platform/safe-fs/core";
33385
33437
 
33386
33438
  // packages/safe-bash/src/shell/env-warning.ts
33387
33439
  init_platform();
@@ -33976,20 +34028,22 @@ async function unary(operator, value2, context2) {
33976
34028
  return context2.present(value2);
33977
34029
  }
33978
34030
  if (operator === "-o") return context2.option(value2);
33979
- if (!["-e", "-a", "-f", "-d", "-s", "-L", "-h", "-r", "-w", "-x"].includes(operator)) unsupported3(operator);
34031
+ if (!["-e", "-a", "-f", "-c", "-d", "-s", "-L", "-h", "-r", "-w", "-x"].includes(operator)) unsupported3(operator);
33980
34032
  if (value2 === "") return false;
33981
34033
  if (/^\/dev\/(?:fd(?:\/|$)|stdin$|stdout$|stderr$)/u.test(value2)) unsupported3("descriptor predicate");
33982
34034
  const access = ["-r", "-w", "-x"].includes(operator);
33983
- if (access && context2.fs.capabilities.permissions !== true) unsupported3("unobservable access permission");
33984
34035
  try {
33985
34036
  if (access) {
33986
- await context2.fs.access(resolvePath(context2.cwd, value2), operator === "-r" ? 4 : operator === "-w" ? 2 : 1, { signal: context2.signal });
34037
+ const capabilities = await context2.fs.capabilitiesFor?.(pathOf(context2, value2), { signal: context2.signal }) ?? context2.fs.capabilities;
34038
+ if (capabilities.permissions !== true) unsupported3("unobservable access permission");
34039
+ await context2.fs.access(pathOf(context2, value2), operator === "-r" ? 4 : operator === "-w" ? 2 : 1, { signal: context2.signal });
33987
34040
  context2.signal.throwIfAborted();
33988
34041
  return true;
33989
34042
  }
33990
- const metadata3 = await (operator === "-L" || operator === "-h" ? context2.fs.lstat(resolvePath(context2.cwd, value2), { signal: context2.signal }) : context2.fs.stat(resolvePath(context2.cwd, value2), { signal: context2.signal }));
34043
+ const metadata3 = await (operator === "-L" || operator === "-h" ? context2.fs.lstat(pathOf(context2, value2), { signal: context2.signal }) : context2.fs.stat(pathOf(context2, value2), { signal: context2.signal }));
33991
34044
  context2.signal.throwIfAborted();
33992
34045
  if (operator === "-f") return metadata3.type === "file";
34046
+ if (operator === "-c") return metadata3.type === "character";
33993
34047
  if (operator === "-d") return metadata3.type === "directory";
33994
34048
  if (operator === "-s") return metadata3.size > 0;
33995
34049
  if (operator === "-L" || operator === "-h") return metadata3.type === "symlink";
@@ -38474,19 +38528,20 @@ var CdLookup = class {
38474
38528
  const rawBytes = absolute ? targetBytes : component.startsWith("/") ? componentBytes + 1 + targetBytes : cwdBytes + 1 + (component ? componentBytes + 1 : 0) + targetBytes;
38475
38529
  if (rawBytes > 65536) throw new PublicDiagnostic("cd: path exceeds 65536 UTF-8 bytes");
38476
38530
  await this.charge(2 * rawBytes);
38477
- const raw = absolute ? target : component.startsWith("/") ? `${component}/${target}` : component ? `${cwd}/${component}/${target}` : `${cwd}/${target}`;
38531
+ const raw = absolute ? target : pathOf({ cwd }, component ? `${component}/${target}` : target);
38478
38532
  const path = resolvePath(cwd, raw);
38533
+ const operand = pathOf({ cwd }, raw);
38479
38534
  await this.scan(path);
38480
38535
  this.signal.throwIfAborted();
38481
38536
  if (++this.probes > 4097) throw new PublicDiagnostic("cd: probe limit exceeded");
38482
38537
  await this.charge(1);
38483
38538
  this.signal.throwIfAborted();
38484
- const stat = await fs.stat(path, { signal: this.signal });
38539
+ const stat = await fs.stat(operand, { signal: this.signal });
38485
38540
  this.signal.throwIfAborted();
38486
38541
  if (stat.type !== "directory") throw new FsError("ENOTDIR", { path });
38487
38542
  await this.charge(1);
38488
38543
  this.signal.throwIfAborted();
38489
- await fs.access(path, ACCESS_MODES.X_OK, { signal: this.signal });
38544
+ await fs.access(operand, ACCESS_MODES.X_OK, { signal: this.signal });
38490
38545
  this.signal.throwIfAborted();
38491
38546
  return path;
38492
38547
  };
@@ -38841,7 +38896,7 @@ var InvocationCancellationOwner = class {
38841
38896
  }
38842
38897
  };
38843
38898
  var Runtime = class _Runtime {
38844
- constructor(fs, commands, middleware, budget, signal = budget.signal, fileWrites = /* @__PURE__ */ new Map(), outputFiles = /* @__PURE__ */ new Map(), commandSignal = signal, cancellation, cancellationState, cancellationOwner, cancellationDepth, cancellationMaxDepth, outcomeFrame = void 0) {
38899
+ constructor(fs, commands, middleware, budget, signal = budget.signal, fileWrites = /* @__PURE__ */ new Map(), outputFiles = /* @__PURE__ */ new Map(), commandSignal = signal, cancellation, cancellationState, cancellationOwner, cancellationDepth, cancellationMaxDepth, outcomeFrame = void 0, inputProfile = fs) {
38845
38900
  this.fs = fs;
38846
38901
  this.commands = commands;
38847
38902
  this.middleware = middleware;
@@ -38856,6 +38911,7 @@ var Runtime = class _Runtime {
38856
38911
  this.cancellationDepth = cancellationDepth;
38857
38912
  this.cancellationMaxDepth = cancellationMaxDepth;
38858
38913
  this.outcomeFrame = outcomeFrame;
38914
+ this.inputProfile = inputProfile;
38859
38915
  this.fs = scopeFileSystem(fs, () => budget.fileSystemOperation(), signal);
38860
38916
  const checkpoint = () => budget.cpuCheckpoint();
38861
38917
  registerYieldCheckpoint(signal, checkpoint);
@@ -38875,6 +38931,7 @@ var Runtime = class _Runtime {
38875
38931
  cancellationDepth;
38876
38932
  cancellationMaxDepth;
38877
38933
  outcomeFrame;
38934
+ inputProfile;
38878
38935
  async ereDiagnostic(io, detail) {
38879
38936
  try {
38880
38937
  await this.diagnostic(io, detail);
@@ -39073,7 +39130,8 @@ var Runtime = class _Runtime {
39073
39130
  owner ?? this.cancellationOwner,
39074
39131
  prepared.owned ? childDepth : this.cancellationDepth,
39075
39132
  this.cancellationMaxDepth,
39076
- frame
39133
+ frame,
39134
+ this.inputProfile
39077
39135
  );
39078
39136
  let captured;
39079
39137
  const executeChild = () => execute5(runtime, scope);
@@ -39616,7 +39674,8 @@ var Runtime = class _Runtime {
39616
39674
  owner,
39617
39675
  childDepth,
39618
39676
  this.cancellationMaxDepth,
39619
- frame
39677
+ frame,
39678
+ this.inputProfile
39620
39679
  );
39621
39680
  const input3 = new ShellInput(incoming?.readable ?? io.stdin, this.budget, signal);
39622
39681
  const pipeOutput = outgoing && { [outputFailure]: outgoing.abort, ownedOutput: outgoing.writable.ownedOutput, write: async (chunk) => {
@@ -40115,13 +40174,13 @@ var Runtime = class _Runtime {
40115
40174
  }
40116
40175
  }
40117
40176
  } else {
40118
- const path = resolvePath(state.cwd, target);
40177
+ const path = pathOf(state, target);
40119
40178
  const options3 = { signal: this.signal };
40120
40179
  if (redirect.operator === "<") {
40121
40180
  await interruptible3(this.fs.access(path, 4, options3), this.signal);
40122
40181
  const stat = await interruptible3(this.fs.stat(path, options3), this.signal);
40123
40182
  if (stat.type === "directory" && !fileShortcut) throw new PublicDiagnostic(`${target}: Is a directory`);
40124
- const source2 = stat.type === "directory" ? toByteSource("") : await fileInput(this.fs, path, this.budget.limits.maxInputBytes, this.signal);
40183
+ const source2 = stat.type === "directory" ? toByteSource("") : await fileInput(this.fs, path, this.budget.limits.maxInputBytes, this.signal, this.inputProfile);
40125
40184
  const input3 = new ShellInput(source2, this.budget, this.signal);
40126
40185
  inputs2.add(input3);
40127
40186
  descriptors.set(redirect.descriptor, { input: input3, stdinIsDefault: false });
@@ -40129,24 +40188,25 @@ var Runtime = class _Runtime {
40129
40188
  const append = redirect.operator === ">>";
40130
40189
  const capabilities = await this.fs.capabilitiesFor?.(path, options3) ?? this.fs.capabilities;
40131
40190
  const random = capabilities.randomAccessWrite === true;
40191
+ const key = resolvePath(state.cwd, path);
40132
40192
  let file;
40133
- await this.fileOperation(path, async () => {
40134
- file = this.outputFiles.get(path) ?? { data: void 0, references: 0 };
40193
+ await this.fileOperation(key, async () => {
40194
+ file = this.outputFiles.get(key) ?? { data: void 0, references: 0 };
40135
40195
  if (!random && file.references) throw new FsError("ENOTSUP", { path, message: "Conflicting sequential output descriptors" });
40136
40196
  file.references++;
40137
- this.outputFiles.set(path, file);
40197
+ this.outputFiles.set(key, file);
40138
40198
  });
40139
40199
  let closed = false;
40140
40200
  let offset = 0;
40141
40201
  const incremental = async () => {
40142
- await this.fileOperation(path, async () => {
40202
+ await this.fileOperation(key, async () => {
40143
40203
  if (append) await this.fs.appendFile(path, new Uint8Array(), options3);
40144
40204
  else await this.fs.writeFile(path, new Uint8Array(), { ...options3, flag: "w" });
40145
40205
  if (!append) file.data = new Uint8Array();
40146
40206
  });
40147
40207
  return { write: (chunk) => {
40148
40208
  const copy2 = new Uint8Array(chunk);
40149
- return this.fileOperation(path, async () => {
40209
+ return this.fileOperation(key, async () => {
40150
40210
  if (closed) throw new Error("Output descriptor is closed");
40151
40211
  const current = file.data;
40152
40212
  let atEOF = false;
@@ -40178,7 +40238,7 @@ var Runtime = class _Runtime {
40178
40238
  if (closed) return;
40179
40239
  closed = true;
40180
40240
  resumePathCache();
40181
- if (--file.references === 0 && this.outputFiles.get(path) === file) this.outputFiles.delete(path);
40241
+ if (--file.references === 0 && this.outputFiles.get(key) === file) this.outputFiles.delete(key);
40182
40242
  };
40183
40243
  let target2;
40184
40244
  let outputScope;
@@ -40422,7 +40482,8 @@ var Runtime = class _Runtime {
40422
40482
  this.cancellationOwner,
40423
40483
  this.cancellationDepth,
40424
40484
  this.cancellationMaxDepth,
40425
- this.outcomeFrame
40485
+ this.outcomeFrame,
40486
+ this.inputProfile
40426
40487
  );
40427
40488
  try {
40428
40489
  return await runtime.dispatchScoped(name2, values, state, { ...io, [invocationScope]: scope }, assignments2, bypassFunctions);
@@ -40818,11 +40879,12 @@ ${functionDisplay(name2, state.functions.get(name2))}`;
40818
40879
  let denied;
40819
40880
  const matches3 = [];
40820
40881
  for (const target of pathTargets(name2, state.variables.PATH, this.budget.limits, this.signal, (limit) => this.budget.fail(limit))) {
40821
- const resolved = resolvePath(state.cwd, target);
40882
+ const resolved = pathOf(state, target);
40822
40883
  try {
40823
40884
  const options3 = { signal: this.signal };
40824
40885
  if (!await interruptible3(this.budget.pathLookup.isFile(this.fs, resolved, this.signal), this.signal)) continue;
40825
- if (this.fs.capabilities.permissions !== true) throw new CommandFailure2(`${target}: execution permissions are not supported by this filesystem`, 126);
40886
+ const capabilities = this.fs.capabilitiesFor ? await interruptible3(this.fs.capabilitiesFor(resolved, options3), this.signal) : this.fs.capabilities;
40887
+ if (capabilities.permissions !== true) throw new CommandFailure2(`${target}: execution permissions are not supported by this filesystem`, 126);
40826
40888
  await interruptible3(this.fs.access(resolved, ACCESS_MODES.X_OK, options3), this.signal);
40827
40889
  matches3.push(target);
40828
40890
  if (!all) return matches3;
@@ -41037,7 +41099,8 @@ ${prefix3} line ${offset + line}: \`${source2.split("\n")[line - 1] ?? ""}'
41037
41099
  this.cancellationOwner,
41038
41100
  this.cancellationDepth,
41039
41101
  this.cancellationMaxDepth,
41040
- this.outcomeFrame
41102
+ this.outcomeFrame,
41103
+ this.inputProfile
41041
41104
  );
41042
41105
  let input3;
41043
41106
  try {
@@ -41214,7 +41277,7 @@ ${prefix3} line ${offset + line}: \`${source2.split("\n")[line - 1] ?? ""}'
41214
41277
  async scriptFile(context2, state, io, target, args, direct, errexit = false, loadedSource, braceexpand = true) {
41215
41278
  if (target === "") throw new CommandFailure2(`${context2.command}: : No such file or directory`, 127);
41216
41279
  if (state.depth >= this.budget.limits.maxSubstitutionDepth) this.budget.fail("maxSubstitutionDepth");
41217
- const path = resolvePath(state.cwd, target);
41280
+ const path = pathOf(state, target);
41218
41281
  let source2;
41219
41282
  let environmentInterpreter = null;
41220
41283
  let interpreterProfile;
@@ -41224,7 +41287,10 @@ ${prefix3} line ${offset + line}: \`${source2.split("\n")[line - 1] ?? ""}'
41224
41287
  const options3 = { signal: this.signal };
41225
41288
  const stat = await interruptible3(this.fs.stat(path, options3), this.signal);
41226
41289
  if (stat.type !== "file") throw new CommandFailure2(`${target}: ${stat.type === "directory" ? "Is a directory" : "not a regular file"}`, 126);
41227
- if (direct && this.fs.capabilities.permissions !== true) throw new CommandFailure2(`${target}: execution permissions are not supported by this filesystem`, 126);
41290
+ if (direct) {
41291
+ const capabilities = this.fs.capabilitiesFor ? await interruptible3(this.fs.capabilitiesFor(path, options3), this.signal) : this.fs.capabilities;
41292
+ if (capabilities.permissions !== true) throw new CommandFailure2(`${target}: execution permissions are not supported by this filesystem`, 126);
41293
+ }
41228
41294
  await interruptible3(this.fs.access(path, ACCESS_MODES.R_OK | (direct ? ACCESS_MODES.X_OK : 0), options3), this.signal);
41229
41295
  const maxBytes = this.budget.limits.maxSourceBytes - this.budget.sourceBytes;
41230
41296
  if (stat.size > maxBytes) this.budget.fail("maxSourceBytes");
@@ -41362,7 +41428,7 @@ ${prefix3} line ${offset + line}: \`${source2.split("\n")[line - 1] ?? ""}'
41362
41428
  if (filename && !filename.includes("/") && state.variables.PATH) {
41363
41429
  let found = false;
41364
41430
  for (const candidate of pathTargets(filename, state.variables.PATH, this.budget.limits, this.signal, (limit) => this.budget.fail(limit))) {
41365
- const path2 = resolvePath(state.cwd, candidate);
41431
+ const path2 = pathOf(state, candidate);
41366
41432
  try {
41367
41433
  if (!await interruptible3(this.budget.pathLookup.isFile(this.fs, path2, this.signal), this.signal)) continue;
41368
41434
  await interruptible3(this.fs.access(path2, ACCESS_MODES.R_OK, options3), this.signal);
@@ -41377,7 +41443,7 @@ ${prefix3} line ${offset + line}: \`${source2.split("\n")[line - 1] ?? ""}'
41377
41443
  if (!found && state.profile === "sh") throw new CommandFailure2(`${context2.command}: ${filename}: file not found`, 1);
41378
41444
  }
41379
41445
  if (!filename) throw new CommandFailure2(": No such file or directory", 1);
41380
- const path = resolvePath(state.cwd, target);
41446
+ const path = pathOf(state, target);
41381
41447
  const stat = await interruptible3(this.fs.stat(path, options3), this.signal);
41382
41448
  if (stat.type === "directory") throw new CommandFailure2(`${context2.command}: ${target}: is a directory`, 1);
41383
41449
  if (stat.type !== "file") throw new CommandFailure2(`${target}: not a regular file`, 1);
@@ -43257,7 +43323,7 @@ ${prefix3} line ${offset + line}: \`${source2.split("\n")[line - 1] ?? ""}'
43257
43323
  for (const candidate of candidates) {
43258
43324
  let entries3;
43259
43325
  try {
43260
- const pending = this.fs.readdir(resolvePath(state.cwd, candidate || "."), { signal: this.signal });
43326
+ const pending = this.fs.readdir(pathOf(state, candidate || "."), { signal: this.signal });
43261
43327
  entries3 = arrayStore(state) ? await interruptible3(pending, this.signal) : await pending;
43262
43328
  } catch (error) {
43263
43329
  if (["ENOENT", "ENOTDIR", "EACCES"].includes(errorCode(error) ?? "")) continue;
@@ -43278,7 +43344,7 @@ ${prefix3} line ${offset + line}: \`${source2.split("\n")[line - 1] ?? ""}'
43278
43344
  const found = [];
43279
43345
  for (const candidate of candidates) {
43280
43346
  try {
43281
- const pending = this.fs.stat(resolvePath(state.cwd, candidate), { signal: this.signal });
43347
+ const pending = this.fs.stat(pathOf(state, candidate), { signal: this.signal });
43282
43348
  const stat = arrayStore(state) ? await interruptible3(pending, this.signal) : await pending;
43283
43349
  if (!value2.endsWith("/") || stat.type === "directory") found.push(candidate + (value2.endsWith("/") ? "/" : ""));
43284
43350
  } catch (error) {
@@ -43290,7 +43356,7 @@ ${prefix3} line ${offset + line}: \`${source2.split("\n")[line - 1] ?? ""}'
43290
43356
  };
43291
43357
 
43292
43358
  // packages/safe-bash/src/shell/input.ts
43293
- async function fileInput(fs, path, maxBytes, signal) {
43359
+ async function fileInput(fs, path, maxBytes, signal, inputProfile = fs) {
43294
43360
  signal.throwIfAborted();
43295
43361
  async function bufferedInput() {
43296
43362
  signal.throwIfAborted();
@@ -43300,7 +43366,13 @@ async function fileInput(fs, path, maxBytes, signal) {
43300
43366
  return toByteSource(bytes3);
43301
43367
  }
43302
43368
  const readStream = fs.readStream;
43303
- if (!readStream || fs.capabilities.streamingRead === false) return bufferedInput();
43369
+ const capabilities = await interruptible3(Promise.resolve(fs.capabilitiesFor?.(path, { signal }) ?? fs.capabilities), signal);
43370
+ if (!readStream || capabilities.streamingRead === false) {
43371
+ if (!inputProfile.readStream || inputProfile.capabilities.streamingRead === false) return bufferedInput();
43372
+ return { async *[Symbol.asyncIterator]() {
43373
+ yield* await bufferedInput();
43374
+ } };
43375
+ }
43304
43376
  let iterator;
43305
43377
  try {
43306
43378
  iterator = readStream.call(fs, path, { signal })[Symbol.asyncIterator]();
@@ -43894,8 +43966,9 @@ var Shell = class {
43894
43966
  profile: "bash"
43895
43967
  };
43896
43968
  const admission = Runtime.rootCancellationAdmission(budget);
43969
+ const filesystem = options3.fs ?? this.#options.fs;
43897
43970
  const runtime = new Runtime(
43898
- options3.fs ?? this.#options.fs,
43971
+ createDeviceFileSystem(filesystem),
43899
43972
  this.commands,
43900
43973
  [...this.#middleware],
43901
43974
  budget,
@@ -43907,7 +43980,9 @@ var Shell = class {
43907
43980
  cancellationState,
43908
43981
  owner,
43909
43982
  0,
43910
- admission.maxDepth
43983
+ admission.maxDepth,
43984
+ void 0,
43985
+ filesystem
43911
43986
  );
43912
43987
  exitCode = 0;
43913
43988
  while (true) {
@@ -44109,7 +44184,7 @@ init_platform();
44109
44184
  // packages/safe-js/dist/browser/safe-fs.js
44110
44185
  init_platform();
44111
44186
 
44112
- // packages/safe-js/dist/browser/chunks/chunk-I3HSJDAA.js
44187
+ // packages/safe-js/dist/browser/chunks/chunk-YJ7IIV57.js
44113
44188
  init_platform();
44114
44189
  var platform = Object.freeze({
44115
44190
  errno(_code) {
@@ -45477,6 +45552,38 @@ function stockDescriptorWrite(filesystem) {
45477
45552
  }
45478
45553
  return true;
45479
45554
  }
45555
+ var deviceCapabilities = Object.freeze({
45556
+ readOnly: false,
45557
+ read: true,
45558
+ stat: true,
45559
+ realpath: true,
45560
+ access: true,
45561
+ readdir: false,
45562
+ write: true,
45563
+ append: true,
45564
+ exclusiveCreate: true,
45565
+ streamingRead: true,
45566
+ retainedRead: true,
45567
+ streamingWrite: true,
45568
+ streamingAppend: true,
45569
+ copy: true,
45570
+ exclusiveCopy: true,
45571
+ remove: false,
45572
+ removeDirectory: false,
45573
+ recursiveRemove: false,
45574
+ rename: false,
45575
+ mkdir: false,
45576
+ recursiveMkdir: false,
45577
+ symlinks: false,
45578
+ hardlinks: false,
45579
+ readlink: false,
45580
+ permissions: false,
45581
+ timestamps: false,
45582
+ truncate: false,
45583
+ randomAccessWrite: false,
45584
+ atomicRename: false,
45585
+ descriptorWriteStream: true
45586
+ });
45480
45587
  var syntheticStat = Object.freeze({
45481
45588
  type: "directory",
45482
45589
  size: 0,
@@ -47351,7 +47458,8 @@ function createSafeJsCommands(options3 = {}, dialect = {
47351
47458
  let filename = parsed.file;
47352
47459
  if (source2 === void 0) {
47353
47460
  if (!fromStdin) filename = pathOf(context2, parsed.file);
47354
- const bytes3 = fromStdin ? context2.stdin : context2.fs.readStream && context2.fs.capabilities.streamingRead !== false ? context2.fs.readStream(filename, { signal, chunkSize: 65536 }) : toByteSource(await withSignal(signal, () => context2.fs.readFile(filename, { signal, maxBytes: limits.maxSourceBytes })));
47461
+ const capabilities = fromStdin ? void 0 : await withSignal(signal, async () => await context2.fs.capabilitiesFor?.(filename, { signal }) ?? context2.fs.capabilities);
47462
+ const bytes3 = fromStdin ? context2.stdin : context2.fs.readStream && capabilities?.streamingRead !== false ? context2.fs.readStream(filename, { signal, chunkSize: 65536 }) : toByteSource(await withSignal(signal, () => context2.fs.readFile(filename, { signal, maxBytes: limits.maxSourceBytes })));
47355
47463
  const reader = new GuestInput(bytes3, limits.maxSourceBytes, signal, fail4, "maxSourceBytes");
47356
47464
  try {
47357
47465
  source2 = await reader.readText();