@demicodes/shell 0.7.2 → 0.9.0

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.
package/dist/index.d.mts CHANGED
@@ -91,7 +91,21 @@ interface BashEnvironmentOptions {
91
91
  shellIdFactory?: () => string;
92
92
  commandIdFactory?: () => string;
93
93
  initialEnv?: Record<string, string>;
94
+ /** Cap on a command's text stdout. Guards against log floods. */
94
95
  maxOutputBytes?: number;
96
+ /**
97
+ * Cap on a final stdout stream that is raw bytes rather than text.
98
+ *
99
+ * Separate from `maxOutputBytes` because the two guard different risks. Text
100
+ * costs context roughly in proportion to its bytes, so a tight cap is what
101
+ * stops a stray `cat` of a log file from flooding a window. Raw bytes are
102
+ * carried to be looked at — a picture, a clip — and there a megabyte buys far
103
+ * more than a megabyte of text does, so the same number would forbid the
104
+ * ordinary case. This layer deliberately stops at that distinction: which
105
+ * modality the bytes are, and what a given model should be shown, is decided
106
+ * above, where models are known.
107
+ */
108
+ maxBinaryBytes?: number;
95
109
  }
96
110
  interface ShellExecInput {
97
111
  script: string;
@@ -182,10 +196,12 @@ interface CommandMetadataRecord {
182
196
  /** Final stdout stream that is not valid UTF-8: raw bytes for the boundary above. */
183
197
  interface BinaryStdout {
184
198
  data: Uint8Array;
185
- /** True when the stream exceeded the exec's output limit; data is capped. */
199
+ /** True when the stream exceeded the applicable cap; data is capped. */
186
200
  truncated: boolean;
187
201
  /** Total byte count of the un-capped stream. */
188
202
  totalBytes: number;
203
+ /** The byte ceiling that applied, so the boundary above can name it. */
204
+ limitBytes: number;
189
205
  }
190
206
  type ShellCommandStatus = {
191
207
  status: 'exited';
@@ -229,6 +245,7 @@ declare class BashEnvironment {
229
245
  private readonly commandIdFactory;
230
246
  private readonly initialEnv;
231
247
  private readonly defaultOutputLimitBytes;
248
+ private readonly defaultBinaryLimitBytes;
232
249
  private readonly shells;
233
250
  private readonly defaultShellByAgentSessionId;
234
251
  private readonly commandsById;
package/dist/index.mjs CHANGED
@@ -784,6 +784,12 @@ function decodeForkStdin(stdin) {
784
784
  //#region src/environment.ts
785
785
  const DEFAULT_TIMEOUT_MS = 1e4;
786
786
  const DEFAULT_OUTPUT_LIMIT_BYTES = 1024 * 1024;
787
+ /**
788
+ * Ceiling for a raw-byte final stream. Sized so an ordinary viewing-grade clip
789
+ * survives the shell and reaches the layer that decides what to do with it,
790
+ * while still bounding a runaway producer.
791
+ */
792
+ const DEFAULT_BINARY_LIMIT_BYTES = 16 * 1024 * 1024;
787
793
  /** Upper bound for a single exec observation window (also the command-bridge wait ceiling). */
788
794
  const MAX_TIMEOUT_MS = 6e5;
789
795
  var BashEnvironment = class {
@@ -793,6 +799,7 @@ var BashEnvironment = class {
793
799
  commandIdFactory;
794
800
  initialEnv;
795
801
  defaultOutputLimitBytes;
802
+ defaultBinaryLimitBytes;
796
803
  shells = /* @__PURE__ */ new Map();
797
804
  defaultShellByAgentSessionId = /* @__PURE__ */ new Map();
798
805
  commandsById = /* @__PURE__ */ new Map();
@@ -805,6 +812,7 @@ var BashEnvironment = class {
805
812
  this.commandIdFactory = options.commandIdFactory ?? (() => globalThis.crypto.randomUUID());
806
813
  this.initialEnv = options.initialEnv ?? {};
807
814
  this.defaultOutputLimitBytes = options.maxOutputBytes ?? DEFAULT_OUTPUT_LIMIT_BYTES;
815
+ this.defaultBinaryLimitBytes = options.maxBinaryBytes ?? DEFAULT_BINARY_LIMIT_BYTES;
808
816
  }
809
817
  getShell(shellId) {
810
818
  return this.shells.get(shellId) ?? null;
@@ -1426,14 +1434,15 @@ var BashEnvironment = class {
1426
1434
  const strict = decodeUtf8Strict(bytes);
1427
1435
  if (strict !== null) stdoutText = strict;
1428
1436
  else {
1429
- const cap = record.outputLimitBytes;
1437
+ const cap = this.defaultBinaryLimitBytes;
1430
1438
  const truncated = bytes.length > cap;
1431
1439
  binary = {
1432
1440
  data: truncated ? bytes.slice(0, cap) : bytes,
1433
1441
  truncated,
1434
- totalBytes: bytes.length
1442
+ totalBytes: bytes.length,
1443
+ limitBytes: cap
1435
1444
  };
1436
- stdoutText = `<binary stdout: ${bytes.length} bytes${truncated ? `, exceeds the ${cap}-byte output limit` : ""}; raw bytes at /@/commands/${record.id}/stdout.bin>\n`;
1445
+ stdoutText = `<binary stdout: ${bytes.length} bytes${truncated ? `, exceeds the ${cap}-byte binary limit` : ""}; raw bytes at /@/commands/${record.id}/stdout.bin>\n`;
1437
1446
  }
1438
1447
  }
1439
1448
  const stderrText = foreground ? resultOrError.stderr : decodeBytesToUtf8(unsafeBytesFromLatin1(resultOrError.stderr));
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@demicodes/shell",
3
3
  "description": "Sandboxable bash engine and Host contract for Demi.",
4
- "version": "0.7.2",
4
+ "version": "0.9.0",
5
5
  "private": false,
6
6
  "type": "module",
7
7
  "exports": {
@@ -19,8 +19,8 @@
19
19
  }
20
20
  },
21
21
  "dependencies": {
22
- "@demicodes/utils": "^0.7.2",
23
22
  "@demicodes/just-bash": "^3.1.0-demi.2",
23
+ "@demicodes/utils": "^0.9.0",
24
24
  "zod": "^4.0.0"
25
25
  },
26
26
  "license": "Apache-2.0",