@poe-platform/safe-bash 0.1.802 → 0.1.803

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 (150) hide show
  1. package/README.md +4 -2
  2. package/dist/image-ast/index.browser.js +13 -4
  3. package/dist/image-ast/index.js +13 -4
  4. package/dist/safe-bash/chunks/{chunk-KDNSPLII.js → chunk-2LSAMQER.js} +1 -0
  5. package/dist/safe-bash/chunks/{chunk-NZQXYWA3.js → chunk-5OHX7C2V.js} +0 -1
  6. package/dist/safe-bash/chunks/{chunk-SHEMU2Z7.js → chunk-B7PIL77G.js} +82 -63
  7. package/dist/safe-bash/chunks/{chunk-WDLGKIHD.js → chunk-EEEWOYEG.js} +4 -4
  8. package/dist/safe-bash/chunks/{chunk-GETUOD5D.js → chunk-GDYNCWRV.js} +1 -1
  9. package/dist/safe-bash/chunks/{chunk-RSTHLAF3.js → chunk-JMMEIDPF.js} +1 -1
  10. package/dist/safe-bash/chunks/{chunk-44ALJ4QG.js → chunk-PNBBUWSM.js} +71 -55
  11. package/dist/safe-bash/chunks/{chunk-NQCM3TYR.js → chunk-RYFQ3LZG.js} +73 -44
  12. package/dist/safe-bash/chunks/chunk-VBTDHOWC.js +55 -0
  13. package/dist/safe-bash/command-chunks/{chunk-ORYGZYKW.js → chunk-4FEVBOL5.js} +1 -1
  14. package/dist/safe-bash/command-chunks/{chunk-VXSLJH7G.js → chunk-6JSIFWYT.js} +26 -6
  15. package/dist/safe-bash/command-chunks/chunk-A7MU7762.js +200 -0
  16. package/dist/safe-bash/command-chunks/{chunk-KJIMDLBB.js → chunk-BXCXRYVU.js} +1 -1
  17. package/dist/safe-bash/command-chunks/{chunk-F4TEEZTL.js → chunk-CQHP3I66.js} +1 -1
  18. package/dist/safe-bash/command-chunks/{chunk-PEF7K3UQ.js → chunk-EARHPPPJ.js} +1 -1
  19. package/dist/safe-bash/command-chunks/{chunk-QKFSRZGQ.js → chunk-GFC2NTDG.js} +2 -2
  20. package/dist/safe-bash/command-chunks/chunk-GIRNFXP3.js +466 -0
  21. package/dist/safe-bash/command-chunks/{chunk-FPVUD6TH.js → chunk-HDCNKOIX.js} +9 -7
  22. package/dist/safe-bash/command-chunks/{chunk-MQFIPHIQ.js → chunk-ILX2NOLF.js} +18 -15
  23. package/dist/safe-bash/command-chunks/{chunk-5E4WAO4O.js → chunk-JD5QNRNO.js} +3 -3
  24. package/dist/safe-bash/command-chunks/{chunk-I237SNW5.js → chunk-LFJJQQND.js} +1 -1
  25. package/dist/safe-bash/command-chunks/{chunk-W5DNF4JH.js → chunk-LOICUCHG.js} +1 -1
  26. package/dist/safe-bash/command-chunks/{chunk-7U7EXOXX.js → chunk-NASVPT6F.js} +2 -1
  27. package/dist/safe-bash/command-chunks/{chunk-OQKC5ZU2.js → chunk-NKIELG7Y.js} +22 -1
  28. package/dist/safe-bash/command-chunks/{chunk-WPYIOKE3.js → chunk-PICVQ6B5.js} +2 -2
  29. package/dist/safe-bash/command-chunks/{chunk-YOFC45RK.js → chunk-QVOS4YXL.js} +1 -1
  30. package/dist/safe-bash/command-chunks/{chunk-VGG2DL5Q.js → chunk-TRR4MYWF.js} +4 -0
  31. package/dist/safe-bash/command-chunks/{chunk-2JRBIDAD.js → chunk-V4K2JDYU.js} +2 -2
  32. package/dist/safe-bash/command-chunks/{chunk-6GWJUQAV.js → chunk-WWIBP2ET.js} +38 -111
  33. package/dist/safe-bash/command-chunks/chunk-ZDN7MJE6.js +168 -0
  34. package/dist/safe-bash/commands/bytes/compression/index.js +54 -51
  35. package/dist/safe-bash/commands/chmod/index.browser.js +14 -0
  36. package/dist/safe-bash/commands/chmod/index.d.ts +2 -0
  37. package/dist/safe-bash/commands/chmod/index.js +2 -0
  38. package/dist/safe-bash/commands/metadata/chmod.d.ts +1 -7
  39. package/dist/safe-bash/commands/metadata/chmod.js +1 -204
  40. package/dist/safe-bash/commands/metadata/index.browser.js +1 -5
  41. package/dist/safe-bash/commands/metadata/internal.d.ts +1 -30
  42. package/dist/safe-bash/commands/metadata/internal.js +1 -76
  43. package/dist/safe-bash/commands/metadata/mktemp.d.ts +1 -3
  44. package/dist/safe-bash/commands/metadata/mktemp.js +1 -202
  45. package/dist/safe-bash/commands/metadata/stat.d.ts +1 -7
  46. package/dist/safe-bash/commands/metadata/stat.js +1 -530
  47. package/dist/safe-bash/commands/mktemp/index.browser.js +13 -0
  48. package/dist/safe-bash/commands/mktemp/index.d.ts +2 -0
  49. package/dist/safe-bash/commands/mktemp/index.js +2 -0
  50. package/dist/safe-bash/commands/node/index.browser.js +2 -2
  51. package/dist/safe-bash/commands/portable-random.d.ts +1 -1
  52. package/dist/safe-bash/commands/portable-random.js +1 -12
  53. package/dist/safe-bash/commands/python/index.browser.js +3 -3
  54. package/dist/safe-bash/commands/stat/index.browser.js +13 -0
  55. package/dist/safe-bash/commands/stat/index.d.ts +2 -0
  56. package/dist/safe-bash/commands/stat/index.js +2 -0
  57. package/dist/safe-bash/commands/text.js +46 -10
  58. package/dist/safe-bash/full-core.browser.js +86 -86
  59. package/dist/safe-bash/jobs.browser.js +2 -2
  60. package/dist/safe-bash/opt-in/commands/yq/native-work.js +1 -0
  61. package/dist/safe-bash/optional-host.browser.js +10 -10
  62. package/dist/safe-bash/plugins/index.browser.js +13 -13
  63. package/dist/safe-bash/registry-entry.browser.js +16 -16
  64. package/dist/safe-bash/search.browser.js +14 -14
  65. package/dist/safe-bash/shell/runtime.d.ts +3 -0
  66. package/dist/safe-bash/shell/runtime.js +20 -3
  67. package/dist/safe-bash/shell/types.d.ts +2 -0
  68. package/dist/safe-bash/shell/worker-limits.js +1 -0
  69. package/dist/safe-bash/shell-entry.browser.js +12 -12
  70. package/dist/safe-bash/worker/host.js +1 -1
  71. package/dist/safe-bash-command-bc/index.js +12 -5
  72. package/dist/safe-bash-command-bzip2/index.js +4 -3
  73. package/dist/safe-bash-command-chmod/LICENSE +21 -0
  74. package/dist/safe-bash-command-chmod/command.d.ts +7 -0
  75. package/dist/safe-bash-command-chmod/command.js +11 -0
  76. package/dist/safe-bash-command-chmod/index.d.ts +6 -0
  77. package/dist/safe-bash-command-chmod/index.js +27 -0
  78. package/dist/safe-bash-command-exiftool/index.js +5 -5
  79. package/dist/safe-bash-command-find/find.js +1 -1
  80. package/dist/safe-bash-command-find/index.js +1 -1
  81. package/dist/safe-bash-command-fmt/index.js +9 -2
  82. package/dist/safe-bash-command-gzip/index.js +45 -42
  83. package/dist/safe-bash-command-imagemagick/index.js +3 -1
  84. package/dist/safe-bash-command-jq/index.js +1 -1
  85. package/dist/safe-bash-command-jq/jq.js +1 -1
  86. package/dist/safe-bash-command-less/index.js +12 -3
  87. package/dist/safe-bash-command-mktemp/LICENSE +21 -0
  88. package/dist/safe-bash-command-mktemp/command.d.ts +3 -0
  89. package/dist/safe-bash-command-mktemp/command.js +9 -0
  90. package/dist/safe-bash-command-mktemp/index.d.ts +6 -0
  91. package/dist/safe-bash-command-mktemp/index.js +25 -0
  92. package/dist/safe-bash-command-od/index.js +6 -2
  93. package/dist/safe-bash-command-op/index.js +25 -6
  94. package/dist/safe-bash-command-rg/index.js +1 -1
  95. package/dist/safe-bash-command-rg/rg-command.js +1 -1
  96. package/dist/safe-bash-command-sed/index.js +1 -1
  97. package/dist/safe-bash-command-sed/sed.js +1 -1
  98. package/dist/safe-bash-command-sips/index.js +13 -5
  99. package/dist/safe-bash-command-sponge/index.js +10 -3
  100. package/dist/safe-bash-command-stat/LICENSE +21 -0
  101. package/dist/safe-bash-command-stat/command.d.ts +7 -0
  102. package/dist/safe-bash-command-stat/command.js +9 -0
  103. package/dist/safe-bash-command-stat/index.d.ts +6 -0
  104. package/dist/safe-bash-command-stat/index.js +25 -0
  105. package/dist/safe-bash-command-unrtf/index.js +11 -1
  106. package/dist/safe-bash-command-xxd/index.js +6 -2
  107. package/dist/safe-bash-command-xz/command.d.ts +1 -1
  108. package/dist/safe-bash-command-xz/index.js +3 -2
  109. package/dist/safe-bash-command-yq/index.js +1 -0
  110. package/dist/safe-bash-command-yq/native-work.js +1 -0
  111. package/dist/safe-bash-command-yq/query.js +3 -0
  112. package/dist/safe-bash-compression-engine/bounded-codec.js +3 -3
  113. package/dist/safe-bash-compression-engine/codec-loader.js +2 -2
  114. package/dist/safe-bash-compression-engine/files.js +6 -6
  115. package/dist/safe-bash-compression-engine/index.js +5 -5
  116. package/dist/safe-bash-compression-engine/operand.js +6 -6
  117. package/dist/safe-bash-compression-engine/options.js +1 -1
  118. package/dist/safe-bash-compression-engine/stream.js +5 -5
  119. package/dist/safe-bash-compression-engine/zstd-decode.js +4 -4
  120. package/dist/safe-bash-contracts/escaping.js +2 -2
  121. package/dist/safe-bash-contracts/filesystem-output.js +2 -2
  122. package/dist/safe-bash-contracts/index.js +2 -2
  123. package/dist/safe-bash-contracts/io.d.ts +2 -0
  124. package/dist/safe-bash-contracts/io.js +1 -1
  125. package/dist/safe-bash-contracts/output.js +2 -2
  126. package/dist/safe-bash-io-engine/commands/search/requirements.js +1 -1
  127. package/dist/safe-bash-io-engine/commands/text-programs/shared.js +1 -1
  128. package/dist/safe-bash-mcp/index.browser.js +11 -1
  129. package/dist/safe-bash-mcp/index.js +35 -16
  130. package/dist/safe-bash-metadata-engine/LICENSE +21 -0
  131. package/dist/safe-bash-metadata-engine/index.d.ts +30 -0
  132. package/dist/safe-bash-metadata-engine/index.js +82 -0
  133. package/dist/safe-bash-metadata-engine/random.d.ts +1 -0
  134. package/dist/safe-bash-metadata-engine/random.js +21 -0
  135. package/dist/safe-bash-regex-engine/index.js +1 -1
  136. package/dist/safe-bash-regex-engine/text/regex.js +1 -1
  137. package/dist/safe-bash-search-engine/ergonomic-regex.js +2 -2
  138. package/dist/safe-bash-search-engine/glob.js +2 -2
  139. package/dist/safe-bash-search-engine/grep.js +2 -2
  140. package/dist/safe-bash-search-engine/matcher.js +2 -2
  141. package/dist/safe-bash-search-engine/options.d.ts +2 -0
  142. package/dist/safe-bash-search-engine/options.js +1 -1
  143. package/dist/safe-bash-search-engine/output.js +1 -1
  144. package/dist/safe-bash-search-engine/shared.js +2 -2
  145. package/dist/safe-bash-search-engine/walk.js +3 -3
  146. package/dist/safe-playwright-cloudflare/browser-snapshot-json.js +2 -2
  147. package/dist/safe-playwright-cloudflare/browser-snapshot-references.js +1 -1
  148. package/package.json +31 -5
  149. package/dist/safe-bash/chunks/chunk-VSAJ4OTM.js +0 -962
  150. package/dist/safe-bash/chunks/{chunk-BJA5XNP6.js → chunk-A7XEHALN.js} +3 -3
@@ -163,6 +163,7 @@ export const defaultLimits = {
163
163
  maxPathnameComponents: Infinity,
164
164
  maxRedirects: Infinity,
165
165
  maxPipelineStages: Infinity,
166
+ maxPipelineBytes: Infinity,
166
167
  maxLoopIterations: Infinity,
167
168
  maxSubstitutionDepth: Infinity,
168
169
  maxFunctionDepth: Infinity,
@@ -453,6 +454,7 @@ function tickSharedWallClockBudgets() {
453
454
  }
454
455
  }
455
456
  export class Budget {
457
+ pipelineBytes = 0;
456
458
  get fileSystemOperations() {
457
459
  return this._fileSystemOperations;
458
460
  }
@@ -494,7 +496,7 @@ export class Budget {
494
496
  this.maxSourceBytesSmi = limits.maxSourceBytes | 0;
495
497
  if (limits.maxPipelineStages < 0x3fffffff)
496
498
  this.maxPipelineStagesSmi = limits.maxPipelineStages | 0;
497
- if (limits.pipeHighWaterMark < 65536)
499
+ if (limits.pipeHighWaterMark < 65536 || limits.maxPipelineBytes !== Infinity)
498
500
  this.canSyncPurePipe = false;
499
501
  this.signal = signal ? combineManagedSignals(signal, this.controller.signal) : this.controller.signal;
500
502
  if (signal)
@@ -643,6 +645,16 @@ export class Budget {
643
645
  set fsOperations(value) {
644
646
  this._fileSystemOperations = value;
645
647
  }
648
+ pipelineInput = (bytes) => {
649
+ if (this.limits.maxPipelineBytes === Infinity)
650
+ return;
651
+ this.assertPipelineInput(bytes);
652
+ this.pipelineBytes += bytes;
653
+ };
654
+ assertPipelineInput(bytes) {
655
+ if (bytes > this.limits.maxPipelineBytes - this.pipelineBytes)
656
+ this.fail("maxPipelineBytes");
657
+ }
646
658
  reservePipelineStages(count) {
647
659
  this.signal.throwIfAborted();
648
660
  if (count > this.limits.maxPipelineStages - this._pipelineStages)
@@ -1493,6 +1505,7 @@ class BudgetedPipeStageSink {
1493
1505
  return Boolean(w.open && pipe && !pipe.failed && !pipe.signal?.aborted && pipe.readerReferences && (!pipe.writes || pipe.writes.size === 0) && (pipe.availableBytes ?? 0) < (pipe.highWaterMark ?? 0));
1494
1506
  }
1495
1507
  writeSync(chunk) {
1508
+ this.budget.assertPipelineInput(chunk.byteLength);
1496
1509
  this.budget.bytes += chunk.byteLength;
1497
1510
  this.writable.write(chunk);
1498
1511
  if (chunk.byteLength && this.written)
@@ -8264,7 +8277,7 @@ export class Runtime {
8264
8277
  const n = pipeline.commands.length;
8265
8278
  this.budget.enterPipelineStages(n);
8266
8279
  this.budget.commands += n;
8267
- const pipeOptions = { highWaterMark: this.budget.limits.pipeHighWaterMark, signal: this.signal };
8280
+ const pipeOptions = { highWaterMark: this.budget.limits.pipeHighWaterMark, signal: this.signal, admitWrite: this.budget.pipelineInput };
8268
8281
  const pipes = new Array(n - 1);
8269
8282
  for (let i = 0; i < n - 1; i++) {
8270
8283
  pipes[i] = createBytePipe(pipeOptions);
@@ -18326,7 +18339,7 @@ export class Runtime {
18326
18339
  }
18327
18340
  for (let index = 1; index < pipeline.commands.length; index++)
18328
18341
  pipes.push(createBytePipe({
18329
- highWaterMark: this.budget.limits.pipeHighWaterMark, signal: this.signal,
18342
+ highWaterMark: this.budget.limits.pipeHighWaterMark, signal: this.signal, admitWrite: this.budget.pipelineInput,
18330
18343
  }));
18331
18344
  for (let index = 0; index < pipeline.commands.length; index++) {
18332
18345
  controllers.push(createManagedControlController());
@@ -35476,6 +35489,10 @@ export class Runtime {
35476
35489
  return true;
35477
35490
  }
35478
35491
  tryFastPureSubstitution(part, state, rawState, io) {
35492
+ if (!cCollation(rawState.variables.LC_ALL || rawState.variables.LC_COLLATE || rawState.variables.LANG || "C"))
35493
+ return undefined;
35494
+ if (this.budget.limits.maxPipelineBytes !== Infinity)
35495
+ return undefined;
35479
35496
  // Synchronous evaluators do not expose cumulative source-read accounting.
35480
35497
  // Use the command context whenever the caller has set an input ceiling.
35481
35498
  if (this.budget.limits.maxInputBytes !== Infinity)
@@ -55,6 +55,8 @@ export interface ShellLimits {
55
55
  * Zero permits only redirect-free commands. Not a global byte or filesystem-call budget. */
56
56
  readonly maxRedirects?: number;
57
57
  readonly maxPipelineStages?: number;
58
+ /** Cumulative bytes written between pipeline stages per invocation, including nested pipelines. */
59
+ readonly maxPipelineBytes?: number;
58
60
  readonly maxLoopIterations?: number;
59
61
  readonly maxSubstitutionDepth?: number;
60
62
  /** Maximum simultaneously active shell functions; zero rejects function calls. Unlimited when omitted. */
@@ -45,6 +45,7 @@ export const cloudflareWorkerLimits = Object.freeze({
45
45
  maxPathnameComponents: 64,
46
46
  maxRedirects: 64,
47
47
  maxPipelineStages: 64,
48
+ maxPipelineBytes: 4 * 1024 * 1024,
48
49
  maxLoopIterations: 1_000,
49
50
  maxSubstitutionDepth: 16,
50
51
  maxFunctionDepth: 16,
@@ -1,28 +1,32 @@
1
1
  import {
2
2
  cloudflareWorkerLimits
3
- } from "./chunks/chunk-KDNSPLII.js";
3
+ } from "./chunks/chunk-2LSAMQER.js";
4
4
  import {
5
5
  Shell,
6
6
  ShellLimitError,
7
7
  ShellSyntaxError,
8
8
  parseShell
9
- } from "./chunks/chunk-SHEMU2Z7.js";
9
+ } from "./chunks/chunk-B7PIL77G.js";
10
+ import "./chunks/chunk-A7XEHALN.js";
10
11
  import "./chunks/chunk-TFIBK2GI.js";
11
12
  import "./chunks/chunk-3NDIFSNM.js";
12
- import "./chunks/chunk-44ALJ4QG.js";
13
+ import "./chunks/chunk-6O77NL2S.js";
14
+ import "./chunks/chunk-PNBBUWSM.js";
13
15
  import "./chunks/chunk-MHMMRCYC.js";
16
+ import "./chunks/chunk-OV6IHKO6.js";
17
+ import "./chunks/chunk-CHYGPFVM.js";
14
18
  import "./chunks/chunk-AUSNDQTJ.js";
15
19
  import "./chunks/chunk-CTVWDACF.js";
16
20
  import "./chunks/chunk-3R3AANOR.js";
17
- import "./chunks/chunk-OV6IHKO6.js";
18
21
  import "./chunks/chunk-XVOSKJE2.js";
19
22
  import "./chunks/chunk-C5OWC2TL.js";
20
23
  import "./chunks/chunk-PH63PLPZ.js";
21
- import "./chunks/chunk-CHYGPFVM.js";
22
- import "./chunks/chunk-6O77NL2S.js";
23
- import "./chunks/chunk-BJA5XNP6.js";
24
+ import "./chunks/chunk-D3ANYGMC.js";
24
25
  import "./chunks/chunk-J42RIHHJ.js";
25
26
  import "./chunks/chunk-I5HZ4FGE.js";
27
+ import "./chunks/chunk-VTH3MLDU.js";
28
+ import "./chunks/chunk-5OHX7C2V.js";
29
+ import "./chunks/chunk-74GHTNJA.js";
26
30
  import {
27
31
  CommandRegistry,
28
32
  commandRuntimeIdentity,
@@ -33,11 +37,7 @@ import "./chunks/chunk-55YJASTN.js";
33
37
  import "./chunks/chunk-4BSJLKNM.js";
34
38
  import "./chunks/chunk-DQP3VHSB.js";
35
39
  import "./chunks/chunk-GEG3BWGE.js";
36
- import "./chunks/chunk-VSAJ4OTM.js";
37
- import "./chunks/chunk-D3ANYGMC.js";
38
- import "./chunks/chunk-VTH3MLDU.js";
39
- import "./chunks/chunk-NZQXYWA3.js";
40
- import "./chunks/chunk-74GHTNJA.js";
40
+ import "./chunks/chunk-VBTDHOWC.js";
41
41
  import "./chunks/chunk-QGUV42T5.js";
42
42
  import "./chunks/chunk-ZVD5IS4V.js";
43
43
  import "./chunks/chunk-E7OYNDGL.js";
@@ -14,7 +14,7 @@ export function createWorkerKillAfterPolicy(registry, configuration, requiresMod
14
14
  // These counters/leases belong to the interpreter, not to a separate Shell.
15
15
  // Until a shared ledger is available, refuse rather than reset their quota.
16
16
  const limits = runtimeContext?.budget.limits ?? configuration.limits;
17
- const shared = ["maxCommands", "maxLoopIterations", "maxSourceBytes", "maxParseUnits", "maxExpansionBytes", "maxExpansionFields", "maxPipelineStages", "maxSubstitutionDepth", "maxFunctionDepth", "maxCpuMs"];
17
+ const shared = ["maxCommands", "maxLoopIterations", "maxSourceBytes", "maxParseUnits", "maxExpansionBytes", "maxExpansionFields", "maxPipelineStages", "maxPipelineBytes", "maxSubstitutionDepth", "maxFunctionDepth", "maxCpuMs"];
18
18
  if (shared.some(key => limits?.[key] !== undefined && limits[key] !== Infinity)) {
19
19
  await writeBytes(context.stderr, new TextEncoder().encode("timeout: worker escalation cannot preserve finite shared interpreter quotas\n"), context.signal);
20
20
  return { exitCode: 125 };
@@ -35,11 +35,12 @@ var UsageError = class extends Error {
35
35
  super(message);
36
36
  }
37
37
  };
38
- async function collectSourceBytes(source, signal, maxBytes) {
38
+ async function collectSourceBytes(source, signal, maxBytes, admit) {
39
39
  const chunks = [];
40
40
  let total = 0;
41
41
  for await (const chunk of readBytes(source, signal)) {
42
42
  total += chunk.byteLength;
43
+ admit(chunk.byteLength);
43
44
  if (total > maxBytes) throw new Error(`input exceeds maximum size (${maxBytes} bytes)`);
44
45
  chunks.push(chunk);
45
46
  }
@@ -673,6 +674,11 @@ function createBcCommand(options = {}) {
673
674
  description: "Arbitrary-precision calculator language",
674
675
  runtimeIdentity: commandRuntimeIdentity,
675
676
  async execute(context) {
677
+ let inputBytes = 0;
678
+ const admit = (bytes) => {
679
+ context.signal.throwIfAborted();
680
+ context.inputBudget?.check(inputBytes += bytes);
681
+ };
676
682
  context.signal.throwIfAborted();
677
683
  try {
678
684
  let mathlib = false;
@@ -721,14 +727,15 @@ function createBcCommand(options = {}) {
721
727
  }
722
728
  const sources = [...expressions];
723
729
  if (files.length === 0 && expressions.length === 0) {
724
- sources.push(decoder.decode(await collectSourceBytes(context.stdin, context.signal, maxInputBytes)));
730
+ sources.push(decoder.decode(await collectSourceBytes(context.stdin, context.signal, maxInputBytes, admit)));
725
731
  } else if (files.length > 0) {
726
732
  for (const file of files) {
727
733
  if (file === "-") {
728
- sources.push(decoder.decode(await collectSourceBytes(context.stdin, context.signal, maxInputBytes)));
734
+ sources.push(decoder.decode(await collectSourceBytes(context.stdin, context.signal, maxInputBytes, admit)));
729
735
  } else {
730
- const raw = await context.fs.readFile(pathPosix.resolve(context.cwd, file));
736
+ const raw = await context.fs.readFile(pathPosix.resolve(context.cwd, file), { signal: context.signal });
731
737
  context.signal.throwIfAborted();
738
+ admit(raw.byteLength);
732
739
  if (raw.byteLength > maxInputBytes) throw new Error(`bc program exceeds maximum input size (${maxInputBytes} bytes)`);
733
740
  sources.push(decoder.decode(raw));
734
741
  }
@@ -1064,7 +1071,7 @@ function createBcCommand(options = {}) {
1064
1071
  return { exitCode: 0 };
1065
1072
  } catch (err) {
1066
1073
  context.signal.throwIfAborted();
1067
- if (err instanceof Error && err.name === "AbortError") throw err;
1074
+ if (err instanceof Error && (err.name === "AbortError" || err.name === "BudgetExceededError")) throw err;
1068
1075
  const msg = err instanceof Error ? err.message : String(err);
1069
1076
  const code = err instanceof UsageError ? 2 : 1;
1070
1077
  await writeText(context.stderr, `bc: ${msg}
@@ -6,7 +6,8 @@ import {
6
6
  init_buffer();
7
7
  import { PublicDiagnostic, UsageError } from "../safe-bash-contracts/diagnostics.js";
8
8
  import {
9
- commandRuntimeIdentity
9
+ commandRuntimeIdentity,
10
+ withInputByteBudget
10
11
  } from "../safe-bash-contracts/index.js";
11
12
  import { codeOf, diagnostic, output } from "../safe-bash-compression-engine/internal.js";
12
13
  import { runOperand } from "../safe-bash-compression-engine/operand.js";
@@ -35,7 +36,7 @@ function createNamedBzip2Command(name, options = {}) {
35
36
  name,
36
37
  description: `${name} block-sorting file compressor`,
37
38
  runtimeIdentity: commandRuntimeIdentity,
38
- async execute(context) {
39
+ execute: withInputByteBudget(async (context) => {
39
40
  context.signal.throwIfAborted();
40
41
  try {
41
42
  const parsed = parseOptions(name, context.args);
@@ -105,7 +106,7 @@ No FILE or FILE '-' uses stdin; file output uses private VFS staging.
105
106
  await diagnostic(context, error);
106
107
  return { exitCode: error instanceof UsageError ? 2 : 1 };
107
108
  }
108
- }
109
+ })
109
110
  };
110
111
  }
111
112
  function createBzip2Command(options = {}) {
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Poe Platform
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,7 @@
1
+ export { modeChange } from "../safe-bash-io-engine/commands/mode-change.js";
2
+ import { type MetadataCommandsOptions } from "../safe-bash-metadata-engine/index.js";
3
+ export declare function createChmodCommand(configuration?: MetadataCommandsOptions): import("../safe-bash-contracts/index.js").CommandDefinition;
4
+ export declare function evalSyncChmod(opArgs: readonly string[], umask: number, chmodNodeSync?: (filePath: string, change: (stat: {
5
+ type: "file" | "directory" | "symlink";
6
+ mode: number;
7
+ }) => number) => boolean): string | undefined;
@@ -0,0 +1,11 @@
1
+ import {
2
+ createChmodCommand,
3
+ evalSyncChmod,
4
+ modeChange
5
+ } from "../safe-bash/command-chunks/chunk-A7MU7762.js";
6
+ import "../safe-bash/command-chunks/chunk-HX3SHTCE.js";
7
+ export {
8
+ createChmodCommand,
9
+ evalSyncChmod,
10
+ modeChange
11
+ };
@@ -0,0 +1,6 @@
1
+ import type { VirtualShellPlugin } from "../safe-bash-contracts/index.js";
2
+ import type { MetadataCommandsOptions } from "../safe-bash-metadata-engine/index.js";
3
+ export * from "./command.js";
4
+ export type { MetadataCommandsOptions as ChmodCommandsOptions, MetadataLimits as ChmodLimits } from "../safe-bash-metadata-engine/index.js";
5
+ export declare function createChmodCommands(options?: MetadataCommandsOptions): import("../safe-bash-contracts/index.js").CommandDefinition[];
6
+ export declare function chmodCommands(options?: MetadataCommandsOptions): VirtualShellPlugin;
@@ -0,0 +1,27 @@
1
+ import {
2
+ createChmodCommand,
3
+ evalSyncChmod,
4
+ modeChange
5
+ } from "../safe-bash/command-chunks/chunk-A7MU7762.js";
6
+ import {
7
+ init_buffer
8
+ } from "../safe-bash/command-chunks/chunk-HX3SHTCE.js";
9
+
10
+ // packages/safe-bash-command-chmod/src/index.ts
11
+ init_buffer();
12
+ function createChmodCommands(options = {}) {
13
+ return [createChmodCommand(options)];
14
+ }
15
+ function chmodCommands(options = {}) {
16
+ const commands = createChmodCommands(options);
17
+ return { name: "chmod-commands", setup(host) {
18
+ for (const command of commands) host.commands.register(command, { replace: options.replace ?? false });
19
+ } };
20
+ }
21
+ export {
22
+ chmodCommands,
23
+ createChmodCommand,
24
+ createChmodCommands,
25
+ evalSyncChmod,
26
+ modeChange
27
+ };
@@ -10,7 +10,7 @@ init_buffer();
10
10
  import { yieldTurn } from "../safe-bash-contracts/yield.js";
11
11
  import { commandRuntimeIdentity, getCommandArguments } from "../safe-bash-contracts/command.js";
12
12
  import { FsError as FsError2 } from "../safe-bash-contracts/errors.js";
13
- import { readBytes as readBytes2, writeBytes } from "../safe-bash-contracts/io.js";
13
+ import { readBytes as readBytes2, writeBytes, withInputByteBudget } from "../safe-bash-contracts/io.js";
14
14
  import { createOutputOperation } from "../safe-bash-contracts/output.js";
15
15
  import { shellValueByteLength } from "../safe-bash-contracts/value.js";
16
16
 
@@ -1779,7 +1779,7 @@ function createExiftoolCommand(options = {}) {
1779
1779
  name: "exiftool",
1780
1780
  runtimeIdentity: commandRuntimeIdentity,
1781
1781
  description: "Inspect and edit admitted PNG, JPEG and PDF metadata through virtual files",
1782
- async execute(context) {
1782
+ execute: withInputByteBudget(async (context) => {
1783
1783
  const publication = new Publication(context, limits.maxStagingAttempts);
1784
1784
  let stdout, stderr;
1785
1785
  let failed = false, failure;
@@ -1824,7 +1824,7 @@ function createExiftoolCommand(options = {}) {
1824
1824
  invocation = parseArguments(await expandArgfiles(context.args, context, publication, resources), resources.limits);
1825
1825
  } catch (error) {
1826
1826
  context.signal.throwIfAborted();
1827
- if (error instanceof ResourceLimitError) throw error;
1827
+ if (error instanceof ResourceLimitError || error instanceof Error && error.name === "BudgetExceededError" || error instanceof FsError2 && error.code === "EFBIG") throw error;
1828
1828
  await output("Error: " + (error instanceof Error ? error.message : String(error)) + "\n", true);
1829
1829
  return { exitCode: 1 };
1830
1830
  }
@@ -1948,7 +1948,7 @@ function createExiftoolCommand(options = {}) {
1948
1948
  tags = jpeg ? inspectJpeg(bytes, resources).tags : png ? inspectPng(bytes, resources).tags : isPdf ? inspectPdf(bytes, resources).tags : [];
1949
1949
  } catch (error) {
1950
1950
  context.signal.throwIfAborted();
1951
- if (error instanceof ResourceLimitError) throw error;
1951
+ if (error instanceof ResourceLimitError || error instanceof Error && error.name === "BudgetExceededError" || error instanceof FsError2 && error.code === "EFBIG") throw error;
1952
1952
  errors++;
1953
1953
  const message = error instanceof FsError2 && error.code === "ENOENT" ? "File not found" : error instanceof Error ? error.message : String(error);
1954
1954
  await output("Error: " + message + " - " + file + "\n", true);
@@ -2053,7 +2053,7 @@ function createExiftoolCommand(options = {}) {
2053
2053
  } finally {
2054
2054
  await cleanup();
2055
2055
  }
2056
- }
2056
+ })
2057
2057
  });
2058
2058
  }
2059
2059
  var exiftoolCommand = createExiftoolCommand();
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createFindDefinitions,
3
3
  evalSyncFind
4
- } from "../safe-bash/command-chunks/chunk-QKFSRZGQ.js";
4
+ } from "../safe-bash/command-chunks/chunk-GFC2NTDG.js";
5
5
  import "../safe-bash/command-chunks/chunk-5ZMZ573Q.js";
6
6
  import "../safe-bash/command-chunks/chunk-HX3SHTCE.js";
7
7
  export {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createFindDefinitions,
3
3
  evalSyncFind
4
- } from "../safe-bash/command-chunks/chunk-QKFSRZGQ.js";
4
+ } from "../safe-bash/command-chunks/chunk-GFC2NTDG.js";
5
5
  import "../safe-bash/command-chunks/chunk-5ZMZ573Q.js";
6
6
  import {
7
7
  init_buffer
@@ -2217,12 +2217,14 @@ function pathOf(context, path) {
2217
2217
  return isAbsolutePath(path) ? path : `${context.cwd.endsWith("/") ? context.cwd.slice(0, -1) : context.cwd}/${path}`;
2218
2218
  }
2219
2219
  var InputBudget = class {
2220
- constructor(maximum, sourceBufferBytes) {
2220
+ constructor(maximum, sourceBufferBytes, inputBudget) {
2221
2221
  this.maximum = maximum;
2222
2222
  this.sourceBufferBytes = sourceBufferBytes;
2223
+ this.inputBudget = inputBudget;
2223
2224
  }
2224
2225
  maximum;
2225
2226
  sourceBufferBytes;
2227
+ inputBudget;
2226
2228
  used = 0;
2227
2229
  get maxReadBytes() {
2228
2230
  return Math.max(0, Math.min(this.sourceBufferBytes, this.maximum - this.used));
@@ -2234,6 +2236,7 @@ var InputBudget = class {
2234
2236
  if (size > this.maximum - this.used) throw new FsError("EFBIG", { message: "byte command input limit exceeded" });
2235
2237
  if (size > this.maxReadBytes) throw new FmtError("LIMIT", "source chunk retention limit exceeded");
2236
2238
  this.used += size;
2239
+ this.inputBudget?.check(this.used);
2237
2240
  if (!size) yield byteSpan(chunk, 0, 0);
2238
2241
  for (let offset = 0; offset < size; offset += 4096) yield byteSpan(chunk, offset, Math.min(4096, size - offset));
2239
2242
  }
@@ -2390,7 +2393,7 @@ async function fmt(context, configuration = {}) {
2390
2393
  const engineBytes = 10120 + settings.prefix.length;
2391
2394
  const batchBytes = limits.retainedBytes >= Math.max(32768, engineBytes + 16384 + 4096) ? 16384 : 0;
2392
2395
  const engineLimit = limits.retainedBytes - batchBytes;
2393
- const budget = new InputBudget(limits.inputBytes, engineLimit - engineBytes);
2396
+ const budget = new InputBudget(limits.inputBytes, engineLimit - engineBytes, context.inputBudget);
2394
2397
  let work = 0, outputBytes = 0;
2395
2398
  stdout = createOutputOperation({ signal: local.signal }, context.stdout);
2396
2399
  const outputContext = { ...context, stdout: stdout.output };
@@ -2406,6 +2409,7 @@ async function fmt(context, configuration = {}) {
2406
2409
  try {
2407
2410
  await current.open(name, nameBytes);
2408
2411
  } catch (error) {
2412
+ if (error instanceof Error && (error.name === "BudgetExceededError" || error.name === "AbortError")) throw error;
2409
2413
  local.signal.throwIfAborted();
2410
2414
  await diagnostic(context, fileError(error, name, nameBytes, true, context));
2411
2415
  await current.close();
@@ -2439,6 +2443,7 @@ async function fmt(context, configuration = {}) {
2439
2443
  bytes2 = await current.next();
2440
2444
  if (bytes2?.length) received = true;
2441
2445
  } catch (error) {
2446
+ if (error instanceof Error && (error.name === "BudgetExceededError" || error.name === "AbortError")) throw error;
2442
2447
  local.signal.throwIfAborted();
2443
2448
  if (isFsError(error, "EFBIG")) throw new FmtError("LIMIT", "byte command input limit exceeded");
2444
2449
  readFailed = true;
@@ -2467,6 +2472,7 @@ async function fmt(context, configuration = {}) {
2467
2472
  }
2468
2473
  if (outBatchUsed) await flushOutBatch();
2469
2474
  } catch (error) {
2475
+ if (error instanceof Error && (error.name === "BudgetExceededError" || error.name === "AbortError")) throw error;
2470
2476
  local.signal.throwIfAborted();
2471
2477
  try {
2472
2478
  await flushOutBatch();
@@ -2496,6 +2502,7 @@ async function fmt(context, configuration = {}) {
2496
2502
  } catch (error) {
2497
2503
  failed = true;
2498
2504
  context.signal.throwIfAborted();
2505
+ if (error instanceof Error && (error.name === "BudgetExceededError" || error.name === "AbortError")) throw error;
2499
2506
  if (error instanceof FmtError && error.usage) await writeBytes(context.stderr, Uint8Array.from(`fmt: ${error.message}
2500
2507
  Try 'fmt --help' for more information.
2501
2508
  `, (character) => character.charCodeAt(0)), context.signal);
@@ -16,10 +16,11 @@ function createGzipCommands(config = {}) {
16
16
  if (maxDecodedBytes !== void 0 && maxDecodedBytes !== Infinity && (!Number.isSafeInteger(maxDecodedBytes) || maxDecodedBytes < 0)) {
17
17
  throw new RangeError("maxDecodedBytes must be a nonnegative safe integer or Infinity");
18
18
  }
19
- const commands = profiles.filter((profile) => profile.format === "gzip").flatMap((profile) => profile.names.map((name) => define(name, withInputByteBudget(async (context) => {
20
- const options = parseOptions(name, context.args);
21
- if (options.help) {
22
- await output(context, `Usage: ${name} [OPTION]... [FILE]...
19
+ const commands = profiles.filter((profile) => profile.format === "gzip").flatMap((profile) => profile.names.map((name) => {
20
+ const command = define(name, async (context) => {
21
+ const options = parseOptions(name, context.args);
22
+ if (options.help) {
23
+ await output(context, `Usage: ${name} [OPTION]... [FILE]...
23
24
  -c, --stdout, --to-stdout
24
25
  -d, --decompress, --uncompress
25
26
  -k, --keep
@@ -29,48 +30,50 @@ ${options.format === "zstd" ? "-1..-9, --best\nHigher levels and --fast[=NUM] ar
29
30
  `}-h, --help
30
31
  No FILE or FILE '-' uses stdin; file output uses private VFS staging.
31
32
  `);
32
- return { exitCode: 0 };
33
- }
34
- let plans;
35
- let planningFailed = false;
36
- try {
37
- if (options.operands.length > 1) {
38
- plans = [];
39
- for (const name2 of options.operands) {
40
- try {
41
- plans.push(...await planOperands(context, { ...options, operands: [name2] }));
42
- } catch (error) {
43
- context.signal.throwIfAborted();
44
- if (codeOf(error) === "EROFS") throw error;
45
- planningFailed = true;
46
- if (options.quiet < 2) await diagnostic(context, error);
47
- }
48
- }
49
- if (plans.length > 1) await verifyOperandDestinations(context, plans);
50
- } else plans = await planOperands(context, options);
51
- } catch (error) {
52
- context.signal.throwIfAborted();
53
- if (options.quiet < 2) throw error;
54
- return { exitCode: 1 };
55
- }
56
- const decodedBudget = new DecodedBudget(maxDecodedBytes);
57
- let exitCode = planningFailed ? 1 : 0;
58
- for (const plan of plans) {
33
+ return { exitCode: 0 };
34
+ }
35
+ let plans;
36
+ let planningFailed = false;
59
37
  try {
60
- const warned = await runOperand(context, plan, options, decodedBudget);
61
- if (warned) {
62
- if (!options.quiet) await diagnostic(context, new PublicDiagnostic(`${plan.source}: decompression OK, trailing garbage ignored`));
63
- if (exitCode === 0) exitCode = 2;
64
- }
38
+ if (options.operands.length > 1) {
39
+ plans = [];
40
+ for (const name2 of options.operands) {
41
+ try {
42
+ plans.push(...await planOperands(context, { ...options, operands: [name2] }));
43
+ } catch (error) {
44
+ context.signal.throwIfAborted();
45
+ if (codeOf(error) === "EROFS") throw error;
46
+ planningFailed = true;
47
+ if (options.quiet < 2) await diagnostic(context, error);
48
+ }
49
+ }
50
+ if (plans.length > 1) await verifyOperandDestinations(context, plans);
51
+ } else plans = await planOperands(context, options);
65
52
  } catch (error) {
66
53
  context.signal.throwIfAborted();
67
- if (options.quiet < 2) await diagnostic(context, error);
68
- exitCode = 1;
69
- if (decodedBudget.exceeded) break;
54
+ if (options.quiet < 2) throw error;
55
+ return { exitCode: 1 };
70
56
  }
71
- }
72
- return { exitCode };
73
- }))));
57
+ const decodedBudget = new DecodedBudget(maxDecodedBytes);
58
+ let exitCode = planningFailed ? 1 : 0;
59
+ for (const plan of plans) {
60
+ try {
61
+ const warned = await runOperand(context, plan, options, decodedBudget);
62
+ if (warned) {
63
+ if (!options.quiet) await diagnostic(context, new PublicDiagnostic(`${plan.source}: decompression OK, trailing garbage ignored`));
64
+ if (exitCode === 0) exitCode = 2;
65
+ }
66
+ } catch (error) {
67
+ context.signal.throwIfAborted();
68
+ if (options.quiet < 2) await diagnostic(context, error);
69
+ exitCode = 1;
70
+ if (decodedBudget.exceeded) break;
71
+ }
72
+ }
73
+ return { exitCode };
74
+ });
75
+ return { ...command, execute: withInputByteBudget(async (context) => command.execute(context)) };
76
+ }));
74
77
  if (maxDecodedBytes === void 0 || maxDecodedBytes === Infinity) {
75
78
  for (const c of commands) builtInDirectContextExecutors.add(c.execute);
76
79
  }
@@ -35,6 +35,7 @@ import {
35
35
 
36
36
  // packages/safe-bash-command-imagemagick/src/index.ts
37
37
  init_buffer();
38
+ import { FsError } from "../safe-bash-contracts/errors.js";
38
39
  import { resolvePath } from "../safe-bash-contracts/path.js";
39
40
  import { yieldTurn, drainCooperativeSteps as drainSteps } from "../safe-bash-contracts/yield.js";
40
41
  import { InputByteBudget } from "../safe-bash-contracts/io.js";
@@ -5089,7 +5090,8 @@ async function executeVfsMagickTool(context, runner, maxInputBytes) {
5089
5090
  bytes = await context.fs.readFile(resolvePath(context.cwd, candidate), {
5090
5091
  signal: invocation.signal
5091
5092
  });
5092
- } catch {
5093
+ } catch (error) {
5094
+ if (!(error instanceof FsError) || !["ENOENT", "ENOTDIR", "EISDIR", "EACCES", "EPERM"].includes(error.code)) throw error;
5093
5095
  continue;
5094
5096
  }
5095
5097
  chargeInput(bytes.byteLength);
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createJqCommand
3
- } from "../safe-bash/command-chunks/chunk-MQFIPHIQ.js";
3
+ } from "../safe-bash/command-chunks/chunk-ILX2NOLF.js";
4
4
  import {
5
5
  init_buffer
6
6
  } from "../safe-bash/command-chunks/chunk-HX3SHTCE.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createJqCommand,
3
3
  executeJq
4
- } from "../safe-bash/command-chunks/chunk-MQFIPHIQ.js";
4
+ } from "../safe-bash/command-chunks/chunk-ILX2NOLF.js";
5
5
  import "../safe-bash/command-chunks/chunk-HX3SHTCE.js";
6
6
  export {
7
7
  createJqCommand,