just-bash 2.12.8 → 2.13.1

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 (163) hide show
  1. package/README.md +332 -260
  2. package/dist/AGENTS.md +1 -1
  3. package/dist/Bash.d.ts +17 -0
  4. package/dist/bin/chunks/{awk2-J2PNSA7C.js → awk2-VTJMI54B.js} +1 -1
  5. package/dist/bin/chunks/{chunk-O5B6WWQQ.js → chunk-A3HQTYHR.js} +1 -1
  6. package/dist/bin/chunks/chunk-AAW7UMPO.js +9 -0
  7. package/dist/bin/chunks/{chunk-2V53PP6G.js → chunk-B3RU2PUI.js} +27 -27
  8. package/dist/bin/chunks/chunk-CQG2HEAL.js +5 -0
  9. package/dist/bin/chunks/chunk-DOXYBGNA.js +12 -0
  10. package/dist/bin/chunks/chunk-FBJJY4ZV.js +14 -0
  11. package/dist/bin/{shell/chunks/chunk-YAEF6X2N.js → chunks/chunk-GR23MPTT.js} +1 -1
  12. package/dist/bin/{shell/chunks/chunk-35JD7YEM.js → chunks/chunk-HJEHIH4P.js} +13 -13
  13. package/dist/bin/chunks/{chunk-UG4GMDQL.js → chunk-LGF54XJQ.js} +1 -1
  14. package/dist/bin/chunks/chunk-LIYVQA3X.js +2 -0
  15. package/dist/bin/chunks/{chunk-2BC3N3L2.js → chunk-ORUYSLP4.js} +4 -4
  16. package/dist/bin/chunks/{chunk-REGSV3X5.js → chunk-SDLWFYVT.js} +1 -1
  17. package/dist/bin/chunks/{chunk-STOAUD75.js → chunk-THNL3XFF.js} +8 -8
  18. package/dist/bin/chunks/chunk-TKTKRRAL.js +11 -0
  19. package/dist/bin/chunks/{chunk-ZJGIBTWD.js → chunk-V7ZOPVQS.js} +1 -1
  20. package/dist/bin/chunks/{chunk-ZYQQ6B7B.js → chunk-ZO5PSLKR.js} +2 -2
  21. package/dist/bin/chunks/curl-SRTMGOVV.js +26 -0
  22. package/dist/bin/chunks/env-NTPN5QYM.js +2 -0
  23. package/dist/bin/{shell/chunks/expansion-JBCP2CHQ.js → chunks/expansion-2RO5M3QC.js} +1 -1
  24. package/dist/bin/{shell/chunks/find-MTLF23HX.js → chunks/find-GAYRV4IF.js} +1 -1
  25. package/dist/bin/{shell/chunks/flag-coverage-CFWN3JJN.js → chunks/flag-coverage-A4G6STMS.js} +1 -1
  26. package/dist/bin/chunks/{help-4H52JYYC.js → help-DVG4AAGE.js} +1 -1
  27. package/dist/bin/{shell/chunks/jq-JFXEKNLN.js → chunks/jq-V7FYGIKO.js} +1 -1
  28. package/dist/bin/chunks/js-exec-JORCTVUT.js +97 -0
  29. package/dist/bin/chunks/js-exec-worker.js +4369 -0
  30. package/dist/bin/chunks/python3-PF7AHNQM.js +12 -0
  31. package/dist/bin/chunks/rg-C6KMBFNG.js +2 -0
  32. package/dist/bin/chunks/time-DDS6JJ23.js +2 -0
  33. package/dist/bin/chunks/{timeout-JJWIFL7W.js → timeout-Z24MNWOP.js} +1 -1
  34. package/dist/bin/chunks/worker.js +88 -33
  35. package/dist/bin/chunks/{xan-M6MLWZCU.js → xan-MOZFJGMY.js} +1 -1
  36. package/dist/bin/chunks/xargs-SCYIFXOW.js +2 -0
  37. package/dist/bin/chunks/{yq-YWUQUXJJ.js → yq-JJLSDDST.js} +1 -1
  38. package/dist/bin/just-bash.js +212 -211
  39. package/dist/bin/shell/chunks/{awk2-J2PNSA7C.js → awk2-VTJMI54B.js} +1 -1
  40. package/dist/bin/shell/chunks/{chunk-O5B6WWQQ.js → chunk-A3HQTYHR.js} +1 -1
  41. package/dist/bin/shell/chunks/chunk-AAW7UMPO.js +9 -0
  42. package/dist/bin/shell/chunks/{chunk-2V53PP6G.js → chunk-B3RU2PUI.js} +27 -27
  43. package/dist/bin/shell/chunks/chunk-CQG2HEAL.js +5 -0
  44. package/dist/bin/shell/chunks/chunk-DOXYBGNA.js +12 -0
  45. package/dist/bin/shell/chunks/chunk-FBJJY4ZV.js +14 -0
  46. package/dist/bin/{chunks/chunk-YAEF6X2N.js → shell/chunks/chunk-GR23MPTT.js} +1 -1
  47. package/dist/bin/{chunks/chunk-35JD7YEM.js → shell/chunks/chunk-HJEHIH4P.js} +13 -13
  48. package/dist/bin/shell/chunks/{chunk-UG4GMDQL.js → chunk-LGF54XJQ.js} +1 -1
  49. package/dist/bin/shell/chunks/chunk-LIYVQA3X.js +2 -0
  50. package/dist/bin/shell/chunks/{chunk-2BC3N3L2.js → chunk-ORUYSLP4.js} +4 -4
  51. package/dist/bin/shell/chunks/{chunk-REGSV3X5.js → chunk-SDLWFYVT.js} +1 -1
  52. package/dist/bin/shell/chunks/{chunk-STOAUD75.js → chunk-THNL3XFF.js} +8 -8
  53. package/dist/bin/shell/chunks/chunk-TKTKRRAL.js +11 -0
  54. package/dist/bin/shell/chunks/{chunk-ZJGIBTWD.js → chunk-V7ZOPVQS.js} +1 -1
  55. package/dist/bin/shell/chunks/{chunk-ZYQQ6B7B.js → chunk-ZO5PSLKR.js} +2 -2
  56. package/dist/bin/shell/chunks/curl-SRTMGOVV.js +26 -0
  57. package/dist/bin/shell/chunks/env-NTPN5QYM.js +2 -0
  58. package/dist/bin/{chunks/expansion-JBCP2CHQ.js → shell/chunks/expansion-2RO5M3QC.js} +1 -1
  59. package/dist/bin/{chunks/find-MTLF23HX.js → shell/chunks/find-GAYRV4IF.js} +1 -1
  60. package/dist/bin/{chunks/flag-coverage-CFWN3JJN.js → shell/chunks/flag-coverage-A4G6STMS.js} +1 -1
  61. package/dist/bin/shell/chunks/{help-4H52JYYC.js → help-DVG4AAGE.js} +1 -1
  62. package/dist/bin/{chunks/jq-JFXEKNLN.js → shell/chunks/jq-V7FYGIKO.js} +1 -1
  63. package/dist/bin/shell/chunks/js-exec-B6QNYHUL.js +97 -0
  64. package/dist/bin/shell/chunks/python3-J7DP4JBQ.js +12 -0
  65. package/dist/bin/shell/chunks/rg-C6KMBFNG.js +2 -0
  66. package/dist/bin/shell/chunks/time-DDS6JJ23.js +2 -0
  67. package/dist/bin/shell/chunks/{timeout-JJWIFL7W.js → timeout-Z24MNWOP.js} +1 -1
  68. package/dist/bin/shell/chunks/{xan-M6MLWZCU.js → xan-MOZFJGMY.js} +1 -1
  69. package/dist/bin/shell/chunks/xargs-SCYIFXOW.js +2 -0
  70. package/dist/bin/shell/chunks/{yq-YWUQUXJJ.js → yq-JJLSDDST.js} +1 -1
  71. package/dist/bin/shell/shell.js +212 -212
  72. package/dist/bundle/browser.js +600 -600
  73. package/dist/bundle/chunks/{awk2-FUVZGMX2.js → awk2-POPGKRAI.js} +1 -1
  74. package/dist/bundle/chunks/{chunk-DZZS6SJP.js → chunk-7TSDKFEO.js} +1 -1
  75. package/dist/bundle/chunks/{chunk-D4QDMGEB.js → chunk-BBXLRYSX.js} +2 -2
  76. package/dist/bundle/chunks/chunk-CWQS3NFK.js +1 -0
  77. package/dist/bundle/chunks/chunk-F5CMUULS.js +13 -0
  78. package/dist/bundle/chunks/{chunk-TRD56HID.js → chunk-FEIOJCZD.js} +1 -1
  79. package/dist/bundle/chunks/chunk-H2WXJGD7.js +10 -0
  80. package/dist/bundle/chunks/{chunk-D2FZX7A2.js → chunk-LPQPILI2.js} +8 -8
  81. package/dist/bundle/chunks/{chunk-2GOYXRRP.js → chunk-MLXIYONF.js} +4 -4
  82. package/dist/bundle/chunks/chunk-NAERJDUW.js +8 -0
  83. package/dist/bundle/chunks/{chunk-OKEHYWBE.js → chunk-NYQYO467.js} +13 -13
  84. package/dist/bundle/chunks/{chunk-JKLUDNMU.js → chunk-O2DBFL6Z.js} +1 -1
  85. package/dist/bundle/chunks/{chunk-JDMQDJYE.js → chunk-OARHFVLG.js} +1 -1
  86. package/dist/bundle/chunks/{chunk-AKVMAONP.js → chunk-RUF7WQ7U.js} +1 -1
  87. package/dist/bundle/chunks/chunk-TOMNU26N.js +4 -0
  88. package/dist/bundle/chunks/{chunk-ZLJ5TCLC.js → chunk-UNYNJIFU.js} +27 -27
  89. package/dist/bundle/chunks/chunk-YTNYSM6T.js +11 -0
  90. package/dist/bundle/chunks/curl-COE4TZE6.js +25 -0
  91. package/dist/bundle/chunks/env-5EPCWSXR.js +1 -0
  92. package/dist/bundle/chunks/{expansion-XG7G47TX.js → expansion-ENLSRCXJ.js} +1 -1
  93. package/dist/bundle/chunks/{find-DOIVMX6X.js → find-TPUOAIUQ.js} +1 -1
  94. package/dist/bundle/chunks/{flag-coverage-VML3BMJT.js → flag-coverage-G2R7PMYU.js} +1 -1
  95. package/dist/bundle/chunks/{help-IA5CMGR4.js → help-VVWX7SA5.js} +1 -1
  96. package/dist/bundle/chunks/{jq-SSCW4AAA.js → jq-DIRZBOTX.js} +1 -1
  97. package/dist/bundle/chunks/js-exec-YJSMH5AG.js +96 -0
  98. package/dist/bundle/chunks/js-exec-worker.js +4369 -0
  99. package/dist/bundle/chunks/python3-6HF56IYI.js +11 -0
  100. package/dist/bundle/chunks/rg-FOQSCCX3.js +1 -0
  101. package/dist/bundle/chunks/{time-XL42Z4U5.js → time-YG5BMRIQ.js} +1 -1
  102. package/dist/bundle/chunks/{timeout-QCU4INQT.js → timeout-VRKMCG72.js} +1 -1
  103. package/dist/bundle/chunks/worker.js +88 -33
  104. package/dist/bundle/chunks/{xan-K7XYDHFV.js → xan-BXDXYEIB.js} +1 -1
  105. package/dist/bundle/chunks/xargs-I6EZUCYF.js +1 -0
  106. package/dist/bundle/chunks/{yq-WTK3HUOR.js → yq-XMVSIL6Z.js} +1 -1
  107. package/dist/bundle/index.cjs +969 -870
  108. package/dist/bundle/index.js +151 -151
  109. package/dist/commands/curl/types.d.ts +1 -1
  110. package/dist/commands/js-exec/fetch-polyfill.d.ts +6 -0
  111. package/dist/commands/js-exec/js-exec.d.ts +11 -0
  112. package/dist/commands/js-exec/module-shims.d.ts +29 -0
  113. package/dist/commands/js-exec/path-polyfill.d.ts +6 -0
  114. package/dist/commands/js-exec/worker.d.ts +30 -0
  115. package/dist/commands/python3/worker.d.ts +1 -0
  116. package/dist/commands/query-engine/safe-object.d.ts +11 -0
  117. package/dist/commands/registry.d.ts +13 -2
  118. package/dist/commands/{python3/fs-bridge-handler.d.ts → worker-bridge/bridge-handler.d.ts} +25 -8
  119. package/dist/commands/{python3 → worker-bridge}/protocol.d.ts +6 -3
  120. package/dist/commands/{python3/sync-fs-backend.d.ts → worker-bridge/sync-backend.d.ts} +25 -4
  121. package/dist/index.d.cts +4 -4
  122. package/dist/index.d.ts +4 -4
  123. package/dist/interpreter/interpreter.d.ts +3 -0
  124. package/dist/interpreter/types.d.ts +8 -0
  125. package/dist/limits.d.ts +3 -1
  126. package/dist/network/allow-list.d.ts +5 -3
  127. package/dist/network/fetch.d.ts +1 -1
  128. package/dist/network/index.d.ts +1 -1
  129. package/dist/network/types.d.ts +32 -1
  130. package/dist/types.d.ts +12 -0
  131. package/package.json +13 -12
  132. package/dist/bin/chunks/chunk-47HZU3SY.js +0 -5
  133. package/dist/bin/chunks/chunk-CZFRRDQC.js +0 -12
  134. package/dist/bin/chunks/chunk-N4EU64Y4.js +0 -9
  135. package/dist/bin/chunks/chunk-QZNF3Y3J.js +0 -2
  136. package/dist/bin/chunks/chunk-VIYJJTN2.js +0 -14
  137. package/dist/bin/chunks/curl-3GMIPMCI.js +0 -26
  138. package/dist/bin/chunks/env-HOVBNLUR.js +0 -2
  139. package/dist/bin/chunks/python3-KI2FQWSN.js +0 -17
  140. package/dist/bin/chunks/rg-34GE6REQ.js +0 -2
  141. package/dist/bin/chunks/time-GZSHCM77.js +0 -2
  142. package/dist/bin/chunks/xargs-GBL6PZ2K.js +0 -2
  143. package/dist/bin/shell/chunks/chunk-47HZU3SY.js +0 -5
  144. package/dist/bin/shell/chunks/chunk-CZFRRDQC.js +0 -12
  145. package/dist/bin/shell/chunks/chunk-N4EU64Y4.js +0 -9
  146. package/dist/bin/shell/chunks/chunk-QZNF3Y3J.js +0 -2
  147. package/dist/bin/shell/chunks/chunk-VIYJJTN2.js +0 -14
  148. package/dist/bin/shell/chunks/curl-3GMIPMCI.js +0 -26
  149. package/dist/bin/shell/chunks/env-HOVBNLUR.js +0 -2
  150. package/dist/bin/shell/chunks/python3-E5X6WBBU.js +0 -17
  151. package/dist/bin/shell/chunks/rg-34GE6REQ.js +0 -2
  152. package/dist/bin/shell/chunks/time-GZSHCM77.js +0 -2
  153. package/dist/bin/shell/chunks/xargs-GBL6PZ2K.js +0 -2
  154. package/dist/bundle/chunks/chunk-3RA5L262.js +0 -8
  155. package/dist/bundle/chunks/chunk-EX62JIX3.js +0 -13
  156. package/dist/bundle/chunks/chunk-RH6GWZAJ.js +0 -1
  157. package/dist/bundle/chunks/chunk-WECLUBEQ.js +0 -11
  158. package/dist/bundle/chunks/chunk-ZSJYNBAF.js +0 -4
  159. package/dist/bundle/chunks/curl-KM2ZAUR6.js +0 -25
  160. package/dist/bundle/chunks/env-XZY4LKEO.js +0 -1
  161. package/dist/bundle/chunks/python3-2OHR6PZU.js +0 -16
  162. package/dist/bundle/chunks/rg-RAICUFGG.js +0 -1
  163. package/dist/bundle/chunks/xargs-2BBAQDTC.js +0 -1
package/README.md CHANGED
@@ -1,96 +1,30 @@
1
1
  # just-bash
2
2
 
3
- A simulated bash environment with an in-memory virtual filesystem, written in TypeScript.
3
+ A virtual bash environment with an in-memory filesystem, written in TypeScript and designed for AI agents.
4
4
 
5
- Designed for AI agents that need a secure, sandboxed bash environment.
5
+ Broad support for standard unix commands and bash syntax with optional curl, Python, JS/TS, and sqlite support.
6
6
 
7
- Supports optional network access via `curl` with secure-by-default URL filtering.
7
+ **Note**: This is beta software. Use at your own risk and please provide feedback. See [security model](#security-model).
8
8
 
9
- **Note**: This is beta software. Use at your own risk and please provide feedback.
10
-
11
- ## Table of Contents
12
-
13
- - [Security model](#security-model)
14
- - [Installation](#installation)
15
- - [Usage](#usage)
16
- - [Basic API](#basic-api)
17
- - [Configuration](#configuration)
18
- - [Custom Commands](#custom-commands)
19
- - [Filesystem Options](#filesystem-options)
20
- - [AI SDK Tool](#ai-sdk-tool)
21
- - [Vercel Sandbox Compatible API](#vercel-sandbox-compatible-api)
22
- - [CLI Binary](#cli-binary)
23
- - [Interactive Shell](#interactive-shell)
24
- - [Supported Commands](#supported-commands)
25
- - [Shell Features](#shell-features)
26
- - [Default Layout](#default-layout)
27
- - [Network Access](#network-access)
28
- - [Execution Protection](#execution-protection)
29
- - [AST Transform Plugins](#ast-transform-plugins)
30
- - [Development](#development)
31
-
32
- ## Security model
33
-
34
- - The shell only has access to the provided file system.
35
- - Execution is protected against infinite loops or recursion. However, Bash is not fully robust against DOS from input. If you need to be robust against this, use process isolation at the OS level.
36
- - Binaries or even WASM are inherently unsupported (Use [Vercel Sandbox](https://vercel.com/docs/vercel-sandbox) or a similar product if a full VM is needed).
37
- - There is no network access by default.
38
- - Network access can be enabled, but requests are checked against URL prefix allow-lists and HTTP-method allow-lists. See [network access](#network-access) for details
39
-
40
- ## Installation
9
+ ## Quick Start
41
10
 
42
11
  ```bash
43
12
  npm install just-bash
44
13
  ```
45
14
 
46
- ## Usage
47
-
48
- ### Basic API
49
-
50
15
  ```typescript
51
16
  import { Bash } from "just-bash";
52
17
 
53
- const env = new Bash();
54
- await env.exec('echo "Hello" > greeting.txt');
55
- const result = await env.exec("cat greeting.txt");
18
+ const bash = new Bash();
19
+ await bash.exec('echo "Hello" > greeting.txt');
20
+ const result = await bash.exec("cat greeting.txt");
56
21
  console.log(result.stdout); // "Hello\n"
57
22
  console.log(result.exitCode); // 0
58
- console.log(result.env); // Final environment after execution
59
- ```
60
-
61
- Each `exec()` is isolated—env vars, functions, and cwd don't persist across calls (filesystem does).
62
-
63
- ### Configuration
64
-
65
- ```typescript
66
- const env = new Bash({
67
- files: { "/data/file.txt": "content" }, // Initial files
68
- env: { MY_VAR: "value" }, // Initial environment
69
- cwd: "/app", // Starting directory (default: /home/user)
70
- executionLimits: { maxCallDepth: 50 }, // See "Execution Protection"
71
- });
72
-
73
- // Per-exec overrides
74
- await env.exec("echo $TEMP", { env: { TEMP: "value" }, cwd: "/tmp" });
75
23
  ```
76
24
 
77
- #### Lazy Files
25
+ Each `exec()` call gets its own isolated shell state — environment variables, functions, and working directory reset between calls. The **filesystem is shared** across calls, so files written in one `exec()` are visible in the next.
78
26
 
79
- File values can be functions (sync or async). The function is called on first read and the result is cached — if the file is written to before being read, the function is never called:
80
-
81
- ```typescript
82
- const env = new Bash({
83
- files: {
84
- "/data/config.json": () => JSON.stringify({ key: "value" }),
85
- "/data/remote.txt": async () => (await fetch("https://example.com")).text(),
86
- "/data/static.txt": "always loaded",
87
- },
88
- });
89
- ```
90
-
91
- This is useful for large or expensive-to-compute content that may not be needed.
92
-
93
- ### Custom Commands
27
+ ## Custom Commands
94
28
 
95
29
  Extend just-bash with your own TypeScript commands using `defineCommand`:
96
30
 
@@ -112,17 +46,125 @@ await bash.exec("hello Alice"); // "Hello, Alice!\n"
112
46
  await bash.exec("echo 'test' | upper"); // "TEST\n"
113
47
  ```
114
48
 
115
- Custom commands receive the full `CommandContext` with access to `fs`, `cwd`, `env`, `stdin`, and `exec` for running subcommands.
49
+ Custom commands receive a `CommandContext` with `fs`, `cwd`, `env`, `stdin`, and `exec` (for subcommands), and work with pipes, redirections, and all shell features.
116
50
 
117
- ### Filesystem Options
51
+ <details>
52
+ <summary><h2>Supported Commands</h2></summary>
53
+
54
+ ### File Operations
55
+
56
+ `cat`, `cp`, `file`, `ln`, `ls`, `mkdir`, `mv`, `readlink`, `rm`, `rmdir`, `split`, `stat`, `touch`, `tree`
57
+
58
+ ### Text Processing
118
59
 
119
- Four filesystem implementations are available:
60
+ `awk`, `base64`, `column`, `comm`, `cut`, `diff`, `expand`, `fold`, `grep` (+ `egrep`, `fgrep`), `head`, `join`, `md5sum`, `nl`, `od`, `paste`, `printf`, `rev`, `rg`, `sed`, `sha1sum`, `sha256sum`, `sort`, `strings`, `tac`, `tail`, `tr`, `unexpand`, `uniq`, `wc`, `xargs`
61
+
62
+ ### Data Processing
63
+
64
+ `jq` (JSON), `sqlite3` (SQLite), `xan` (CSV), `yq` (YAML/XML/TOML/CSV)
65
+
66
+ ### Optional Runtimes
67
+
68
+ `js-exec` (JavaScript/TypeScript via QuickJS; requires `javascript: true`), `python3`/`python` (Python via CPython; requires `python: true`)
69
+
70
+ ### Compression & Archives
71
+
72
+ `gzip` (+ `gunzip`, `zcat`), `tar`
73
+
74
+ ### Navigation & Environment
75
+
76
+ `basename`, `cd`, `dirname`, `du`, `echo`, `env`, `export`, `find`, `hostname`, `printenv`, `pwd`, `tee`
77
+
78
+ ### Shell Utilities
79
+
80
+ `alias`, `bash`, `chmod`, `clear`, `date`, `expr`, `false`, `help`, `history`, `seq`, `sh`, `sleep`, `time`, `timeout`, `true`, `unalias`, `which`, `whoami`
81
+
82
+ ### Network
83
+
84
+ `curl`, `html-to-markdown` (require [network configuration](#network-access))
85
+
86
+ All commands support `--help` for usage information.
87
+
88
+ ### Shell Features
89
+
90
+ - **Pipes**: `cmd1 | cmd2`
91
+ - **Redirections**: `>`, `>>`, `2>`, `2>&1`, `<`
92
+ - **Command chaining**: `&&`, `||`, `;`
93
+ - **Variables**: `$VAR`, `${VAR}`, `${VAR:-default}`
94
+ - **Positional parameters**: `$1`, `$2`, `$@`, `$#`
95
+ - **Glob patterns**: `*`, `?`, `[...]`
96
+ - **If statements**: `if COND; then CMD; elif COND; then CMD; else CMD; fi`
97
+ - **Functions**: `function name { ... }` or `name() { ... }`
98
+ - **Local variables**: `local VAR=value`
99
+ - **Loops**: `for`, `while`, `until`
100
+ - **Symbolic links**: `ln -s target link`
101
+ - **Hard links**: `ln target link`
102
+
103
+ </details>
104
+
105
+ ## Configuration
106
+
107
+ ```typescript
108
+ const env = new Bash({
109
+ files: { "/data/file.txt": "content" }, // Initial files
110
+ env: { MY_VAR: "value" }, // Initial environment
111
+ cwd: "/app", // Starting directory (default: /home/user)
112
+ executionLimits: { maxCallDepth: 50 }, // See "Execution Protection"
113
+ python: true, // Enable python3/python commands
114
+ javascript: true, // Enable js-exec command
115
+ // Or with bootstrap: javascript: { bootstrap: "globalThis.X = 1;" }
116
+ });
117
+
118
+ // Per-exec overrides
119
+ await env.exec("echo $TEMP", { env: { TEMP: "value" }, cwd: "/tmp" });
120
+
121
+ // Pass stdin to the script
122
+ await env.exec("cat", { stdin: "hello from stdin\n" });
123
+
124
+ // Start with a clean environment
125
+ await env.exec("env", { replaceEnv: true, env: { ONLY: "this" } });
126
+
127
+ // Pass arguments without shell escaping (like spawnSync)
128
+ await env.exec("grep", { args: ["-r", "TODO", "src/"] });
129
+
130
+ // Cancel long-running scripts
131
+ const controller = new AbortController();
132
+ setTimeout(() => controller.abort(), 5000);
133
+ await env.exec("while true; do sleep 1; done", { signal: controller.signal });
134
+
135
+ // Preserve leading whitespace (e.g., for heredocs)
136
+ await env.exec("cat <<EOF\n indented\nEOF", { rawScript: true });
137
+ ```
138
+
139
+ `exec()` options:
140
+
141
+ | Option | Type | Description |
142
+ |---|---|---|
143
+ | `env` | `Record<string, string>` | Environment variables for this execution only |
144
+ | `cwd` | `string` | Working directory for this execution only |
145
+ | `stdin` | `string` | Standard input passed to the script |
146
+ | `args` | `string[]` | Additional argv passed directly to the first command (bypasses shell parsing; does not change `$1`, `$2`, ...) |
147
+ | `replaceEnv` | `boolean` | Start with empty env instead of merging (default: `false`) |
148
+ | `signal` | `AbortSignal` | Cooperative cancellation; stops at next statement boundary |
149
+ | `rawScript` | `boolean` | Skip leading-whitespace normalization (default: `false`) |
150
+
151
+ ## Filesystem Options
152
+
153
+ Four filesystem implementations:
120
154
 
121
155
  **InMemoryFs** (default) - Pure in-memory filesystem, no disk access:
122
156
 
123
157
  ```typescript
124
158
  import { Bash } from "just-bash";
125
- const env = new Bash(); // Uses InMemoryFs by default
159
+
160
+ const env = new Bash({
161
+ files: {
162
+ "/data/config.json": '{"key": "value"}',
163
+ // Lazy: called on first read, cached. Never called if written before read.
164
+ "/data/large.csv": () => "col1,col2\na,b\n",
165
+ "/data/remote.txt": async () => (await fetch("https://example.com")).text(),
166
+ },
167
+ });
126
168
  ```
127
169
 
128
170
  **OverlayFs** - Copy-on-write over a real directory. Reads come from disk, writes stay in memory:
@@ -138,7 +180,7 @@ await env.exec("cat package.json"); // reads from disk
138
180
  await env.exec('echo "modified" > package.json'); // stays in memory
139
181
  ```
140
182
 
141
- **ReadWriteFs** - Direct read-write access to a real directory. Use this if you want the agent to be agle to write to your disk:
183
+ **ReadWriteFs** - Direct read-write access to a real directory. Use this if you want the agent to be able to write to your disk:
142
184
 
143
185
  ```typescript
144
186
  import { Bash } from "just-bash";
@@ -188,9 +230,191 @@ const fs = new MountableFs({
188
230
  });
189
231
  ```
190
232
 
233
+ ## Optional Capabilities
234
+
235
+ ### Network Access
236
+
237
+ Network access is disabled by default. Enable it with the `network` option:
238
+
239
+ ```typescript
240
+ // Allow specific URLs with GET/HEAD only (safest)
241
+ const env = new Bash({
242
+ network: {
243
+ allowedUrlPrefixes: [
244
+ "https://api.github.com/repos/myorg/",
245
+ "https://api.example.com",
246
+ ],
247
+ },
248
+ });
249
+
250
+ // Allow specific URLs with additional methods
251
+ const env = new Bash({
252
+ network: {
253
+ allowedUrlPrefixes: ["https://api.example.com"],
254
+ allowedMethods: ["GET", "HEAD", "POST"], // Default: ["GET", "HEAD"]
255
+ },
256
+ });
257
+
258
+ // Inject credentials via header transforms (secrets never enter the sandbox)
259
+ const env = new Bash({
260
+ network: {
261
+ allowedUrlPrefixes: [
262
+ "https://public-api.com", // plain string — no transforms
263
+ {
264
+ url: "https://ai-gateway.vercel.sh",
265
+ transform: [{ headers: { Authorization: "Bearer secret" } }],
266
+ },
267
+ ],
268
+ },
269
+ });
270
+
271
+ // Allow all URLs and methods (use with caution)
272
+ const env = new Bash({
273
+ network: { dangerouslyAllowFullInternetAccess: true },
274
+ });
275
+ ```
276
+
277
+ **Note:** The `curl` command only exists when network is configured. Without network configuration, `curl` returns "command not found".
278
+
279
+ #### Allow-List Security
280
+
281
+ The allow-list enforces:
282
+
283
+ - **Origin matching**: URLs must match the exact origin (scheme + host + port)
284
+ - **Path prefix**: Only paths starting with the specified prefix are allowed
285
+ - **HTTP method restrictions**: Only GET and HEAD by default (configure `allowedMethods` for more)
286
+ - **Redirect protection**: Redirects to non-allowed URLs are blocked
287
+ - **Header transforms**: Firewall headers are injected at the fetch boundary and override any user-supplied headers with the same name, preventing credential substitution from inside the sandbox. Headers are re-evaluated on each redirect so credentials are never leaked to non-transform hosts
288
+
289
+ #### Using curl
290
+
291
+ ```bash
292
+ # Fetch and process data
293
+ curl -s https://api.example.com/data | grep pattern
294
+
295
+ # Download and convert HTML to Markdown
296
+ curl -s https://example.com | html-to-markdown
297
+
298
+ # POST JSON data
299
+ curl -X POST -H "Content-Type: application/json" \
300
+ -d '{"key":"value"}' https://api.example.com/endpoint
301
+ ```
302
+
303
+ ### JavaScript Support
304
+
305
+ JavaScript and TypeScript execution via QuickJS is opt-in due to additional security surface. Enable with `javascript: true`:
306
+
307
+ ```typescript
308
+ const env = new Bash({
309
+ javascript: true,
310
+ });
311
+
312
+ // Execute JavaScript code
313
+ await env.exec('js-exec -c "console.log(1 + 2)"');
314
+
315
+ // Run script files (.js, .mjs, .ts, .mts)
316
+ await env.exec('js-exec script.js');
317
+
318
+ // ES module mode with imports
319
+ await env.exec('js-exec -m -c "import fs from \'fs\'; console.log(fs.readFileSync(\'/data/file.txt\', \'utf8\'))"');
320
+ ```
321
+
322
+ #### Bootstrap Code
323
+
324
+ Run setup code before every `js-exec` invocation with the `bootstrap` option:
325
+
326
+ ```typescript
327
+ const env = new Bash({
328
+ javascript: {
329
+ bootstrap: `
330
+ globalThis.API_BASE = "https://api.example.com";
331
+ globalThis.formatDate = (d) => new Date(d).toISOString();
332
+ `,
333
+ },
334
+ });
335
+
336
+ await env.exec('js-exec -c "console.log(API_BASE)"');
337
+ // Output: https://api.example.com
338
+ ```
339
+
340
+ #### Node.js Compatibility
341
+
342
+ `js-exec` supports `require()` and `import` with these Node.js modules:
343
+
344
+ - **fs**: `readFileSync`, `writeFileSync`, `readdirSync`, `statSync`, `existsSync`, `mkdirSync`, `rmSync`, `fs.promises.*`
345
+ - **path**: `join`, `resolve`, `dirname`, `basename`, `extname`, `relative`, `normalize`
346
+ - **child_process**: `execSync`, `spawnSync`
347
+ - **process**: `argv`, `cwd()`, `exit()`, `env`, `platform`, `version`
348
+ - **Other modules**: `os`, `url`, `assert`, `util`, `events`, `buffer`, `stream`, `string_decoder`, `querystring`
349
+ - **Globals**: `console`, `fetch`, `Buffer`, `URL`, `URLSearchParams`
350
+
351
+ `fs.readFileSync()` returns a `Buffer` by default (matching Node.js). Pass an encoding like `'utf8'` to get a string.
352
+
353
+ **Note:** The `js-exec` command only exists when `javascript` is configured. It is not available in browser environments. Execution runs in a QuickJS WASM sandbox with a 64 MB memory limit and configurable timeout (default: 10s, 60s with network).
354
+
355
+ ### Python Support
356
+
357
+ Python (CPython compiled to WASM) is opt-in due to additional security surface. Enable with `python: true`:
358
+
359
+ ```typescript
360
+ const env = new Bash({
361
+ python: true,
362
+ });
363
+
364
+ // Execute Python code
365
+ await env.exec('python3 -c "print(1 + 2)"');
366
+
367
+ // Run Python scripts
368
+ await env.exec('python3 script.py');
369
+ ```
370
+
371
+ **Note:** The `python3` and `python` commands only exist when `python: true` is configured. Python is not available in browser environments.
372
+
373
+ ### SQLite Support
374
+
375
+ `sqlite3` uses sql.js (SQLite compiled to WASM), sandboxed from the real filesystem:
376
+
377
+ ```typescript
378
+ const env = new Bash();
379
+
380
+ // Query in-memory database
381
+ await env.exec('sqlite3 :memory: "SELECT 1 + 1"');
382
+
383
+ // Query file-based database
384
+ await env.exec('sqlite3 data.db "SELECT * FROM users"');
385
+ ```
386
+
387
+ **Note:** SQLite is not available in browser environments. Queries run in a worker thread with a configurable timeout (default: 5 seconds) to prevent runaway queries from blocking execution.
388
+
389
+ ## AST Transform Plugins
390
+
391
+ Parse bash scripts into an AST, transform them, and serialize back to bash. Good for instrumenting scripts (e.g., capturing per-command stdout/stderr) or extracting metadata before execution.
392
+
393
+ ```typescript
394
+ import { Bash, BashTransformPipeline, TeePlugin, CommandCollectorPlugin } from "just-bash";
395
+
396
+ // Standalone pipeline — output can be run by any shell
397
+ const pipeline = new BashTransformPipeline()
398
+ .use(new TeePlugin({ outputDir: "/tmp/logs" }))
399
+ .use(new CommandCollectorPlugin());
400
+ const result = pipeline.transform("echo hello | grep hello");
401
+ result.script; // transformed bash string
402
+ result.metadata.commands; // ["echo", "grep", "tee"]
403
+
404
+ // Integrated API — exec() auto-applies transforms and returns metadata
405
+ const bash = new Bash();
406
+ bash.registerTransformPlugin(new CommandCollectorPlugin());
407
+ const execResult = await bash.exec("echo hello | grep hello");
408
+ execResult.metadata?.commands; // ["echo", "grep"]
409
+ ```
410
+
411
+ See [src/transform/README.md](src/transform/README.md) for the full API, built-in plugins, and how to write custom plugins.
412
+
413
+ ## Integrations
414
+
191
415
  ### AI SDK Tool
192
416
 
193
- For AI agents, use [`bash-tool`](https://github.com/vercel-labs/bash-tool) which is optimized for just-bash and provides a ready-to-use [AI SDK](https://ai-sdk.dev/) tool:
417
+ [`bash-tool`](https://github.com/vercel-labs/bash-tool) wraps just-bash as an [AI SDK](https://ai-sdk.dev/) tool:
194
418
 
195
419
  ```bash
196
420
  npm install bash-tool
@@ -211,11 +435,11 @@ const result = await generateText({
211
435
  });
212
436
  ```
213
437
 
214
- See the [bash-tool documentation](https://github.com/vercel-labs/bash-tool) for more details and examples.
438
+ See [bash-tool](https://github.com/vercel-labs/bash-tool) for more.
215
439
 
216
440
  ### Vercel Sandbox Compatible API
217
441
 
218
- Bash provides a `Sandbox` class that's API-compatible with [`@vercel/sandbox`](https://vercel.com/docs/vercel-sandbox), making it easy to swap implementations. You can start with Bash and switch to a real sandbox when you need the power of a full VM (e.g. to run node, python, or custom binaries).
442
+ `Sandbox` is a drop-in replacement for [`@vercel/sandbox`](https://vercel.com/docs/vercel-sandbox) — same API, but runs entirely in-process with the virtual filesystem. Start with just-bash for development and testing, swap in a real sandbox when you need a full VM.
219
443
 
220
444
  ```typescript
221
445
  import { Sandbox } from "just-bash";
@@ -244,9 +468,11 @@ await sandbox.mkDir("/app/logs", { recursive: true });
244
468
  await sandbox.stop();
245
469
  ```
246
470
 
471
+ ## CLI
472
+
247
473
  ### CLI Binary
248
474
 
249
- After installing globally (`npm install -g just-bash`), use the `just-bash` command as a secure alternative to `bash` for AI agents:
475
+ Install globally (`npm install -g just-bash`) for a sandboxed CLI:
250
476
 
251
477
  ```bash
252
478
  # Execute inline script
@@ -266,7 +492,9 @@ just-bash -c 'echo hello' --json
266
492
  # Output: {"stdout":"hello\n","stderr":"","exitCode":0}
267
493
  ```
268
494
 
269
- The CLI uses OverlayFS - reads come from the real filesystem, but all writes stay in memory and are discarded after execution. The project root is mounted at `/home/user/project`.
495
+ The CLI uses OverlayFS reads come from the real filesystem, but all writes stay in memory and are discarded after execution.
496
+
497
+ **Important**: The project root is mounted at `/home/user/project`. Use this path (or relative paths from the default cwd) to access your files inside the sandbox.
270
498
 
271
499
  Options:
272
500
 
@@ -282,158 +510,12 @@ Options:
282
510
  pnpm shell
283
511
  ```
284
512
 
285
- The interactive shell has full internet access enabled by default, allowing you to use `curl` to fetch data from any URL. Use `--no-network` to disable this:
513
+ The interactive shell has full internet access by default. Disable with `--no-network`:
286
514
 
287
515
  ```bash
288
516
  pnpm shell --no-network
289
517
  ```
290
518
 
291
- ## Supported Commands
292
-
293
- ### File Operations
294
-
295
- `cat`, `cp`, `file`, `ln`, `ls`, `mkdir`, `mv`, `readlink`, `rm`, `rmdir`, `split`, `stat`, `touch`, `tree`
296
-
297
- ### Text Processing
298
-
299
- `awk`, `base64`, `column`, `comm`, `cut`, `diff`, `expand`, `fold`, `grep` (+ `egrep`, `fgrep`), `head`, `join`, `md5sum`, `nl`, `od`, `paste`, `printf`, `rev`, `rg`, `sed`, `sha1sum`, `sha256sum`, `sort`, `strings`, `tac`, `tail`, `tr`, `unexpand`, `uniq`, `wc`, `xargs`
300
-
301
- ### Data Processing
302
-
303
- `jq` (JSON), `python3`/`python` (Python via Pyodide; required opt-in), `sqlite3` (SQLite), `xan` (CSV), `yq` (YAML/XML/TOML/CSV)
304
-
305
- ### Compression & Archives
306
-
307
- `gzip` (+ `gunzip`, `zcat`), `tar`
308
-
309
- ### Navigation & Environment
310
-
311
- `basename`, `cd`, `dirname`, `du`, `echo`, `env`, `export`, `find`, `hostname`, `printenv`, `pwd`, `tee`
312
-
313
- ### Shell Utilities
314
-
315
- `alias`, `bash`, `chmod`, `clear`, `date`, `expr`, `false`, `help`, `history`, `seq`, `sh`, `sleep`, `time`, `timeout`, `true`, `unalias`, `which`, `whoami`
316
-
317
- ### Network Commands
318
-
319
- `curl`, `html-to-markdown`
320
-
321
- All commands support `--help` for usage information.
322
-
323
- ## Shell Features
324
-
325
- - **Pipes**: `cmd1 | cmd2`
326
- - **Redirections**: `>`, `>>`, `2>`, `2>&1`, `<`
327
- - **Command chaining**: `&&`, `||`, `;`
328
- - **Variables**: `$VAR`, `${VAR}`, `${VAR:-default}`
329
- - **Positional parameters**: `$1`, `$2`, `$@`, `$#`
330
- - **Glob patterns**: `*`, `?`, `[...]`
331
- - **If statements**: `if COND; then CMD; elif COND; then CMD; else CMD; fi`
332
- - **Functions**: `function name { ... }` or `name() { ... }`
333
- - **Local variables**: `local VAR=value`
334
- - **Loops**: `for`, `while`, `until`
335
- - **Symbolic links**: `ln -s target link`
336
- - **Hard links**: `ln target link`
337
-
338
- ## Default Layout
339
-
340
- When created without options, Bash provides a Unix-like directory structure:
341
-
342
- - `/home/user` - Default working directory (and `$HOME`)
343
- - `/bin` - Contains stubs for all built-in commands
344
- - `/usr/bin` - Additional binary directory
345
- - `/tmp` - Temporary files directory
346
-
347
- Commands can be invoked by path (e.g., `/bin/ls`) or by name.
348
-
349
- ## Network Access
350
-
351
- Network access (and the `curl` command) is disabled by default for security. To enable it, configure the `network` option:
352
-
353
- ```typescript
354
- // Allow specific URLs with GET/HEAD only (safest)
355
- const env = new Bash({
356
- network: {
357
- allowedUrlPrefixes: [
358
- "https://api.github.com/repos/myorg/",
359
- "https://api.example.com",
360
- ],
361
- },
362
- });
363
-
364
- // Allow specific URLs with additional methods
365
- const env = new Bash({
366
- network: {
367
- allowedUrlPrefixes: ["https://api.example.com"],
368
- allowedMethods: ["GET", "HEAD", "POST"], // Default: ["GET", "HEAD"]
369
- },
370
- });
371
-
372
- // Allow all URLs and methods (use with caution)
373
- const env = new Bash({
374
- network: { dangerouslyAllowFullInternetAccess: true },
375
- });
376
- ```
377
-
378
- **Note:** The `curl` command only exists when network is configured. Without network configuration, `curl` returns "command not found".
379
-
380
- ## Python Support
381
-
382
- Python support via Pyodide is opt-in due to additional security surface. Enable it explicitly, but be aware of the risk:
383
-
384
- ```typescript
385
- const env = new Bash({
386
- python: true,
387
- });
388
-
389
- // Execute Python code
390
- await env.exec('python3 -c "print(1 + 2)"');
391
-
392
- // Run Python scripts
393
- await env.exec('python3 script.py');
394
- ```
395
-
396
- **Note:** The `python3` and `python` commands only exist when `python: true` is configured. Python is not available in browser environments.
397
-
398
- ## SQLite Support
399
-
400
- The `sqlite3` command uses sql.js (WASM-based SQLite) which is fully sandboxed and cannot access the real filesystem:
401
-
402
- ```typescript
403
- const env = new Bash();
404
-
405
- // Query in-memory database
406
- await env.exec('sqlite3 :memory: "SELECT 1 + 1"');
407
-
408
- // Query file-based database
409
- await env.exec('sqlite3 data.db "SELECT * FROM users"');
410
- ```
411
-
412
- **Note:** SQLite is not available in browser environments. Queries run in a worker thread with a configurable timeout (default: 5 seconds) to prevent runaway queries from blocking execution.
413
-
414
- ### Allow-List Security
415
-
416
- The allow-list enforces:
417
-
418
- - **Origin matching**: URLs must match the exact origin (scheme + host + port)
419
- - **Path prefix**: Only paths starting with the specified prefix are allowed
420
- - **HTTP method restrictions**: Only GET and HEAD by default (configure `allowedMethods` for more)
421
- - **Redirect protection**: Redirects to non-allowed URLs are blocked
422
-
423
- ### Using curl
424
-
425
- ```bash
426
- # Fetch and process data
427
- curl -s https://api.example.com/data | grep pattern
428
-
429
- # Download and convert HTML to Markdown
430
- curl -s https://example.com | html-to-markdown
431
-
432
- # POST JSON data
433
- curl -X POST -H "Content-Type: application/json" \
434
- -d '{"key":"value"}' https://api.example.com/endpoint
435
- ```
436
-
437
519
  ## Execution Protection
438
520
 
439
521
  Bash protects against infinite loops and deep recursion with configurable limits:
@@ -450,45 +532,35 @@ const env = new Bash({
450
532
  });
451
533
  ```
452
534
 
453
- All limits have sensible defaults. Error messages include hints on which limit to increase. Feel free to increase if your scripts intentionally go beyond them.
535
+ All limits have defaults. Error messages tell you which limit was hit. Increase as needed for your workload.
454
536
 
455
- ## AST Transform Plugins
537
+ ## Security Model
456
538
 
457
- Parse bash scripts into an AST, run transform plugins, and serialize back to executable bash. Useful for instrumenting scripts (e.g., capturing per-command stdout/stderr) or analyzing them (e.g., extracting command names) before execution.
539
+ - The shell only has access to the provided filesystem.
540
+ - All execution happens without VM isolation. This does introduce additional risk. The code base was designed to be robust against prototype-pollution attacks and other break outs to the host JS engine and filesystem.
541
+ - There is no network access by default. When enabled, requests are checked against URL prefix allow-lists and HTTP-method allow-lists.
542
+ - Python and JavaScript execution are off by default as they represent additional security surface.
543
+ - Execution is protected against infinite loops and deep recursion with configurable limits.
544
+ - Use [Vercel Sandbox](https://vercel.com/docs/vercel-sandbox) if you need a full VM with arbitrary binary execution.
458
545
 
459
- ```typescript
460
- import { Bash, BashTransformPipeline, TeePlugin, CommandCollectorPlugin } from "just-bash";
546
+ ## Browser Support
461
547
 
462
- // Standalone pipeline output can be run by any shell
463
- const pipeline = new BashTransformPipeline()
464
- .use(new TeePlugin({ outputDir: "/tmp/logs" }))
465
- .use(new CommandCollectorPlugin());
466
- const result = pipeline.transform("echo hello | grep hello");
467
- result.script; // transformed bash string
468
- result.metadata.commands; // ["echo", "grep", "tee"]
548
+ The core shell (parsing, execution, filesystem, and all built-in commands) works in browser environments. The following features require Node.js and are unavailable in browsers: `python3`/`python`, `sqlite3`, `js-exec`, and `OverlayFs`/`ReadWriteFs` (which access the real filesystem).
469
549
 
470
- // Integrated API — exec() auto-applies transforms and returns metadata
471
- const bash = new Bash();
472
- bash.registerTransformPlugin(new CommandCollectorPlugin());
473
- const execResult = await bash.exec("echo hello | grep hello");
474
- execResult.metadata?.commands; // ["echo", "grep"]
475
- ```
550
+ ## Default Layout
476
551
 
477
- See [src/transform/README.md](src/transform/README.md) for the full API, built-in plugins, and how to write custom plugins.
552
+ When created without options, Bash provides a Unix-like directory structure:
478
553
 
479
- ## Development
554
+ - `/home/user` - Default working directory (and `$HOME`)
555
+ - `/bin` - Contains stubs for all built-in commands
556
+ - `/usr/bin` - Additional binary directory
557
+ - `/tmp` - Temporary files directory
480
558
 
481
- ```bash
482
- pnpm test # Run tests in watch mode
483
- pnpm test:run # Run tests once
484
- pnpm typecheck # Type check without emitting
485
- pnpm build # Build TypeScript
486
- pnpm shell # Run interactive shell
487
- ```
559
+ Commands can be invoked by path (e.g., `/bin/ls`) or by name.
488
560
 
489
561
  ## AI Agent Instructions
490
562
 
491
- For AI agents, we recommend using [`bash-tool`](https://github.com/vercel-labs/bash-tool) which is optimized for just-bash and provides additional guidance in its `AGENTS.md`:
563
+ For AI agents, [`bash-tool`](https://github.com/vercel-labs/bash-tool) provides additional guidance in its `AGENTS.md`:
492
564
 
493
565
  ```bash
494
566
  cat node_modules/bash-tool/dist/AGENTS.md