{"id":47418246,"url":"https://github.com/vercel-labs/just-bash","last_synced_at":"2026-04-04T15:00:35.607Z","repository":{"id":331152766,"uuid":"1121859126","full_name":"vercel-labs/just-bash","owner":"vercel-labs","description":"Bash for Agents","archived":false,"fork":false,"pushed_at":"2026-03-27T20:47:42.000Z","size":23273,"stargazers_count":2311,"open_issues_count":40,"forks_count":139,"subscribers_count":8,"default_branch":"main","last_synced_at":"2026-03-28T02:44:54.262Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://justbash.dev/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vercel-labs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":"THREAT_MODEL.md","audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2025-12-23T17:10:51.000Z","updated_at":"2026-03-28T02:03:34.000Z","dependencies_parsed_at":"2026-01-04T06:04:34.099Z","dependency_job_id":null,"html_url":"https://github.com/vercel-labs/just-bash","commit_stats":null,"previous_names":["vercel-labs/just-bash"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/vercel-labs/just-bash","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vercel-labs%2Fjust-bash","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vercel-labs%2Fjust-bash/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vercel-labs%2Fjust-bash/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vercel-labs%2Fjust-bash/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vercel-labs","download_url":"https://codeload.github.com/vercel-labs/just-bash/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vercel-labs%2Fjust-bash/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31403952,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T10:20:44.708Z","status":"ssl_error","status_checked_at":"2026-04-04T10:20:06.846Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-03-21T10:00:30.804Z","updated_at":"2026-04-04T15:00:35.601Z","avatar_url":"https://github.com/vercel-labs.png","language":"TypeScript","funding_links":[],"categories":["TypeScript","Shell \u0026 Bash","Official"],"sub_categories":[],"readme":"# just-bash\n\nA virtual bash environment with an in-memory filesystem, written in TypeScript and designed for AI agents.\n\nBroad support for standard unix commands and bash syntax with optional curl, Python, JS/TS, and sqlite support.\n\n**Note**: This is beta software. Use at your own risk and please provide feedback. See [security model](#security-model).\n\n## Quick Start\n\n```bash\nnpm install just-bash\n```\n\n```typescript\nimport { Bash } from \"just-bash\";\n\nconst bash = new Bash();\nawait bash.exec('echo \"Hello\" \u003e greeting.txt');\nconst result = await bash.exec(\"cat greeting.txt\");\nconsole.log(result.stdout); // \"Hello\\n\"\nconsole.log(result.exitCode); // 0\n```\n\nEach `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.\n\n## Custom Commands\n\nExtend just-bash with your own TypeScript commands using `defineCommand`:\n\n```typescript\nimport { Bash, defineCommand } from \"just-bash\";\n\nconst hello = defineCommand(\"hello\", async (args, ctx) =\u003e {\n  const name = args[0] || \"world\";\n  return { stdout: `Hello, ${name}!\\n`, stderr: \"\", exitCode: 0 };\n});\n\nconst upper = defineCommand(\"upper\", async (args, ctx) =\u003e {\n  return { stdout: ctx.stdin.toUpperCase(), stderr: \"\", exitCode: 0 };\n});\n\nconst bash = new Bash({ customCommands: [hello, upper] });\n\nawait bash.exec(\"hello Alice\"); // \"Hello, Alice!\\n\"\nawait bash.exec(\"echo 'test' | upper\"); // \"TEST\\n\"\n```\n\nCustom commands receive a `CommandContext` with `fs`, `cwd`, `env`, `stdin`, and `exec` (for subcommands), and work with pipes, redirections, and all shell features.\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003ch2\u003eSupported Commands\u003c/h2\u003e\u003c/summary\u003e\n\n### File Operations\n\n`cat`, `cp`, `file`, `ln`, `ls`, `mkdir`, `mv`, `readlink`, `rm`, `rmdir`, `split`, `stat`, `touch`, `tree`\n\n### Text Processing\n\n`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`\n\n### Data Processing\n\n`jq` (JSON), `sqlite3` (SQLite), `xan` (CSV), `yq` (YAML/XML/TOML/CSV)\n\n### Optional Runtimes\n\n`js-exec` (JavaScript/TypeScript via QuickJS; requires `javascript: true`), `python3`/`python` (Python via CPython; requires `python: true`)\n\n### Compression \u0026 Archives\n\n`gzip` (+ `gunzip`, `zcat`), `tar`\n\n### Navigation \u0026 Environment\n\n`basename`, `cd`, `dirname`, `du`, `echo`, `env`, `export`, `find`, `hostname`, `printenv`, `pwd`, `tee`\n\n### Shell Utilities\n\n`alias`, `bash`, `chmod`, `clear`, `date`, `expr`, `false`, `help`, `history`, `seq`, `sh`, `sleep`, `time`, `timeout`, `true`, `unalias`, `which`, `whoami`\n\n### Network\n\n`curl`, `html-to-markdown` (require [network configuration](#network-access))\n\nAll commands support `--help` for usage information.\n\n### Shell Features\n\n- **Pipes**: `cmd1 | cmd2`\n- **Redirections**: `\u003e`, `\u003e\u003e`, `2\u003e`, `2\u003e\u00261`, `\u003c`\n- **Command chaining**: `\u0026\u0026`, `||`, `;`\n- **Variables**: `$VAR`, `${VAR}`, `${VAR:-default}`\n- **Positional parameters**: `$1`, `$2`, `$@`, `$#`\n- **Glob patterns**: `*`, `?`, `[...]`\n- **If statements**: `if COND; then CMD; elif COND; then CMD; else CMD; fi`\n- **Functions**: `function name { ... }` or `name() { ... }`\n- **Local variables**: `local VAR=value`\n- **Loops**: `for`, `while`, `until`\n- **Symbolic links**: `ln -s target link`\n- **Hard links**: `ln target link`\n\n\u003c/details\u003e\n\n## Configuration\n\n```typescript\nconst env = new Bash({\n  files: { \"/data/file.txt\": \"content\" }, // Initial files\n  env: { MY_VAR: \"value\" }, // Initial environment\n  cwd: \"/app\", // Starting directory (default: /home/user)\n  executionLimits: { maxCallDepth: 50 }, // See \"Execution Protection\"\n  python: true, // Enable python3/python commands\n  javascript: true, // Enable js-exec command\n  // Or with bootstrap: javascript: { bootstrap: \"globalThis.X = 1;\" }\n});\n\n// Per-exec overrides\nawait env.exec(\"echo $TEMP\", { env: { TEMP: \"value\" }, cwd: \"/tmp\" });\n\n// Pass stdin to the script\nawait env.exec(\"cat\", { stdin: \"hello from stdin\\n\" });\n\n// Start with a clean environment\nawait env.exec(\"env\", { replaceEnv: true, env: { ONLY: \"this\" } });\n\n// Pass arguments without shell escaping (like spawnSync)\nawait env.exec(\"grep\", { args: [\"-r\", \"TODO\", \"src/\"] });\n\n// Cancel long-running scripts\nconst controller = new AbortController();\nsetTimeout(() =\u003e controller.abort(), 5000);\nawait env.exec(\"while true; do sleep 1; done\", { signal: controller.signal });\n\n// Preserve leading whitespace (e.g., for heredocs)\nawait env.exec(\"cat \u003c\u003cEOF\\n  indented\\nEOF\", { rawScript: true });\n```\n\n`exec()` options:\n\n| Option | Type | Description |\n|---|---|---|\n| `env` | `Record\u003cstring, string\u003e` | Environment variables for this execution only |\n| `cwd` | `string` | Working directory for this execution only |\n| `stdin` | `string` | Standard input passed to the script |\n| `args` | `string[]` | Additional argv passed directly to the first command (bypasses shell parsing; does not change `$1`, `$2`, ...) |\n| `replaceEnv` | `boolean` | Start with empty env instead of merging (default: `false`) |\n| `signal` | `AbortSignal` | Cooperative cancellation; stops at next statement boundary |\n| `rawScript` | `boolean` | Skip leading-whitespace normalization (default: `false`) |\n\n## Filesystem Options\n\nFour filesystem implementations:\n\n**InMemoryFs** (default) - Pure in-memory filesystem, no disk access:\n\n```typescript\nimport { Bash } from \"just-bash\";\n\nconst env = new Bash({\n  files: {\n    \"/data/config.json\": '{\"key\": \"value\"}',\n    // Lazy: called on first read, cached. Never called if written before read.\n    \"/data/large.csv\": () =\u003e \"col1,col2\\na,b\\n\",\n    \"/data/remote.txt\": async () =\u003e (await fetch(\"https://example.com\")).text(),\n  },\n});\n```\n\n**OverlayFs** - Copy-on-write over a real directory. Reads come from disk, writes stay in memory:\n\n```typescript\nimport { Bash } from \"just-bash\";\nimport { OverlayFs } from \"just-bash/fs/overlay-fs\";\n\nconst overlay = new OverlayFs({ root: \"/path/to/project\" });\nconst env = new Bash({ fs: overlay, cwd: overlay.getMountPoint() });\n\nawait env.exec(\"cat package.json\"); // reads from disk\nawait env.exec('echo \"modified\" \u003e package.json'); // stays in memory\n```\n\n**ReadWriteFs** - Direct read-write access to a real directory. Use this if you want the agent to be able to write to your disk:\n\n```typescript\nimport { Bash } from \"just-bash\";\nimport { ReadWriteFs } from \"just-bash/fs/read-write-fs\";\n\nconst rwfs = new ReadWriteFs({ root: \"/path/to/sandbox\" });\nconst env = new Bash({ fs: rwfs });\n\nawait env.exec('echo \"hello\" \u003e file.txt'); // writes to real filesystem\n```\n\nKeep `ReadWriteFs` pointed at a workspace directory, not at the installed `just-bash` package or any other trusted runtime code. Guest-writable roots should stay separate from trusted code.\n\n**MountableFs** - Mount multiple filesystems at different paths. Combines read-only and read-write filesystems into a unified namespace:\n\n```typescript\nimport { Bash, MountableFs, InMemoryFs } from \"just-bash\";\nimport { OverlayFs } from \"just-bash/fs/overlay-fs\";\nimport { ReadWriteFs } from \"just-bash/fs/read-write-fs\";\n\nconst fs = new MountableFs({ base: new InMemoryFs() });\n\n// Mount read-only knowledge base\nfs.mount(\"/mnt/knowledge\", new OverlayFs({ root: \"/path/to/knowledge\", readOnly: true }));\n\n// Mount read-write workspace\nfs.mount(\"/home/agent\", new ReadWriteFs({ root: \"/path/to/workspace\" }));\n\nconst bash = new Bash({ fs, cwd: \"/home/agent\" });\n\nawait bash.exec(\"ls /mnt/knowledge\"); // reads from knowledge base\nawait bash.exec(\"cp /mnt/knowledge/doc.txt ./\"); // cross-mount copy\nawait bash.exec('echo \"notes\" \u003e notes.txt'); // writes to workspace\n```\n\nYou can also configure mounts in the constructor:\n\n```typescript\nimport { MountableFs, InMemoryFs } from \"just-bash\";\nimport { OverlayFs } from \"just-bash/fs/overlay-fs\";\nimport { ReadWriteFs } from \"just-bash/fs/read-write-fs\";\n\nconst fs = new MountableFs({\n  base: new InMemoryFs(),\n  mounts: [\n    { mountPoint: \"/data\", filesystem: new OverlayFs({ root: \"/shared/data\" }) },\n    { mountPoint: \"/workspace\", filesystem: new ReadWriteFs({ root: \"/tmp/work\" }) },\n  ],\n});\n```\n\n## Optional Capabilities\n\n### Network Access\n\nNetwork access is disabled by default. Enable it with the `network` option:\n\n```typescript\n// Allow specific URLs with GET/HEAD only (safest)\nconst env = new Bash({\n  network: {\n    allowedUrlPrefixes: [\n      \"https://api.github.com/repos/myorg/\",\n      \"https://api.example.com\",\n    ],\n  },\n});\n\n// Allow specific URLs with additional methods\nconst env = new Bash({\n  network: {\n    allowedUrlPrefixes: [\"https://api.example.com\"],\n    allowedMethods: [\"GET\", \"HEAD\", \"POST\"], // Default: [\"GET\", \"HEAD\"]\n  },\n});\n\n// Inject credentials via header transforms (secrets never enter the sandbox)\nconst env = new Bash({\n  network: {\n    allowedUrlPrefixes: [\n      \"https://public-api.com\", // plain string — no transforms\n      {\n        url: \"https://ai-gateway.vercel.sh\",\n        transform: [{ headers: { Authorization: \"Bearer secret\" } }],\n      },\n    ],\n  },\n});\n\n// Allow all URLs and methods (use with caution)\nconst env = new Bash({\n  network: { dangerouslyAllowFullInternetAccess: true },\n});\n```\n\n**Note:** The `curl` command only exists when network is configured. Without network configuration, `curl` returns \"command not found\".\n\n#### Allow-List Security\n\nThe allow-list enforces:\n\n- **Origin matching**: URLs must match the exact origin (scheme + host + port)\n- **Path prefix**: Only paths starting with the specified prefix are allowed\n- **HTTP method restrictions**: Only GET and HEAD by default (configure `allowedMethods` for more)\n- **Redirect protection**: Redirects to non-allowed URLs are blocked\n- **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\n\n#### Using curl\n\n```bash\n# Fetch and process data\ncurl -s https://api.example.com/data | grep pattern\n\n# Download and convert HTML to Markdown\ncurl -s https://example.com | html-to-markdown\n\n# POST JSON data\ncurl -X POST -H \"Content-Type: application/json\" \\\n  -d '{\"key\":\"value\"}' https://api.example.com/endpoint\n```\n\n### JavaScript Support\n\nJavaScript and TypeScript execution via QuickJS is opt-in due to additional security surface. Enable with `javascript: true`:\n\n```typescript\nconst env = new Bash({\n  javascript: true,\n});\n\n// Execute JavaScript code\nawait env.exec('js-exec -c \"console.log(1 + 2)\"');\n\n// Run script files (.js, .mjs, .ts, .mts)\nawait env.exec('js-exec script.js');\n\n// ES module mode with imports\nawait env.exec('js-exec -m -c \"import fs from \\'fs\\'; console.log(fs.readFileSync(\\'/data/file.txt\\', \\'utf8\\'))\"');\n```\n\n#### Bootstrap Code\n\nRun setup code before every `js-exec` invocation with the `bootstrap` option:\n\n```typescript\nconst env = new Bash({\n  javascript: {\n    bootstrap: `\n      globalThis.API_BASE = \"https://api.example.com\";\n      globalThis.formatDate = (d) =\u003e new Date(d).toISOString();\n    `,\n  },\n});\n\nawait env.exec('js-exec -c \"console.log(API_BASE)\"');\n// Output: https://api.example.com\n```\n\n#### Node.js Compatibility\n\n`js-exec` supports `require()` and `import` with these Node.js modules:\n\n- **fs**: `readFileSync`, `writeFileSync`, `readdirSync`, `statSync`, `existsSync`, `mkdirSync`, `rmSync`, `fs.promises.*`\n- **path**: `join`, `resolve`, `dirname`, `basename`, `extname`, `relative`, `normalize`\n- **child_process**: `execSync`, `spawnSync`\n- **process**: `argv`, `cwd()`, `exit()`, `env`, `platform`, `version`\n- **Other modules**: `os`, `url`, `assert`, `util`, `events`, `buffer`, `stream`, `string_decoder`, `querystring`\n- **Globals**: `console`, `fetch`, `Buffer`, `URL`, `URLSearchParams`\n\n`fs.readFileSync()` returns a `Buffer` by default (matching Node.js). Pass an encoding like `'utf8'` to get a string.\n\n**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).\n\n### Python Support\n\nPython (CPython compiled to WASM) is opt-in due to additional security surface. Enable with `python: true`:\n\n```typescript\nconst env = new Bash({\n  python: true,\n});\n\n// Execute Python code\nawait env.exec('python3 -c \"print(1 + 2)\"');\n\n// Run Python scripts\nawait env.exec('python3 script.py');\n```\n\n**Note:** The `python3` and `python` commands only exist when `python: true` is configured. Python is not available in browser environments.\n\n### SQLite Support\n\n`sqlite3` uses sql.js (SQLite compiled to WASM), sandboxed from the real filesystem:\n\n```typescript\nconst env = new Bash();\n\n// Query in-memory database\nawait env.exec('sqlite3 :memory: \"SELECT 1 + 1\"');\n\n// Query file-based database\nawait env.exec('sqlite3 data.db \"SELECT * FROM users\"');\n```\n\n**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.\n\n## AST Transform Plugins\n\nParse 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.\n\n```typescript\nimport { Bash, BashTransformPipeline, TeePlugin, CommandCollectorPlugin } from \"just-bash\";\n\n// Standalone pipeline — output can be run by any shell\nconst pipeline = new BashTransformPipeline()\n  .use(new TeePlugin({ outputDir: \"/tmp/logs\" }))\n  .use(new CommandCollectorPlugin());\nconst result = pipeline.transform(\"echo hello | grep hello\");\nresult.script;             // transformed bash string\nresult.metadata.commands;  // [\"echo\", \"grep\", \"tee\"]\n\n// Integrated API — exec() auto-applies transforms and returns metadata\nconst bash = new Bash();\nbash.registerTransformPlugin(new CommandCollectorPlugin());\nconst execResult = await bash.exec(\"echo hello | grep hello\");\nexecResult.metadata?.commands; // [\"echo\", \"grep\"]\n```\n\nSee [src/transform/README.md](src/transform/README.md) for the full API, built-in plugins, and how to write custom plugins.\n\n## Integrations\n\n### AI SDK Tool\n\n[`bash-tool`](https://github.com/vercel-labs/bash-tool) wraps just-bash as an [AI SDK](https://ai-sdk.dev/) tool:\n\n```bash\nnpm install bash-tool\n```\n\n```typescript\nimport { createBashTool } from \"bash-tool\";\nimport { generateText } from \"ai\";\n\nconst bashTool = createBashTool({\n  files: { \"/data/users.json\": '[{\"name\": \"Alice\"}, {\"name\": \"Bob\"}]' },\n});\n\nconst result = await generateText({\n  model: \"anthropic/claude-sonnet-4\",\n  tools: { bash: bashTool },\n  prompt: \"Count the users in /data/users.json\",\n});\n```\n\nSee [bash-tool](https://github.com/vercel-labs/bash-tool) for more.\n\n### Vercel Sandbox Compatible API\n\n`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.\n\n```typescript\nimport { Sandbox } from \"just-bash\";\n\n// Create a sandbox instance\nconst sandbox = await Sandbox.create({ cwd: \"/app\" });\n\n// Write files to the virtual filesystem\nawait sandbox.writeFiles({\n  \"/app/script.sh\": 'echo \"Hello World\"',\n  \"/app/data.json\": '{\"key\": \"value\"}',\n});\n\n// Run commands and get results\nconst cmd = await sandbox.runCommand(\"bash /app/script.sh\");\nconst output = await cmd.stdout(); // \"Hello World\\n\"\nconst exitCode = (await cmd.wait()).exitCode; // 0\n\n// Read files back\nconst content = await sandbox.readFile(\"/app/data.json\");\n\n// Create directories\nawait sandbox.mkDir(\"/app/logs\", { recursive: true });\n\n// Clean up (no-op for Bash, but API-compatible)\nawait sandbox.stop();\n```\n\n## CLI\n\n### CLI Binary\n\nInstall globally (`npm install -g just-bash`) for a sandboxed CLI:\n\n```bash\n# Execute inline script\njust-bash -c 'ls -la \u0026\u0026 cat package.json | head -5'\n\n# Execute with specific project root\njust-bash -c 'grep -r \"TODO\" src/' --root /path/to/project\n\n# Pipe script from stdin\necho 'find . -name \"*.ts\" | wc -l' | just-bash\n\n# Execute a script file\njust-bash ./scripts/deploy.sh\n\n# Get JSON output for programmatic use\njust-bash -c 'echo hello' --json\n# Output: {\"stdout\":\"hello\\n\",\"stderr\":\"\",\"exitCode\":0}\n```\n\nThe CLI uses OverlayFS — reads come from the real filesystem, but all writes stay in memory and are discarded after execution.\n\n**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.\n\nOptions:\n\n- `-c \u003cscript\u003e` - Execute script from argument\n- `--root \u003cpath\u003e` - Root directory (default: current directory)\n- `--cwd \u003cpath\u003e` - Working directory in sandbox\n- `-e, --errexit` - Exit on first error\n- `--json` - Output as JSON\n\n### Interactive Shell\n\n```bash\npnpm shell\n```\n\nThe interactive shell has full internet access by default. Disable with `--no-network`:\n\n```bash\npnpm shell --no-network\n```\n\n## Execution Protection\n\nBash protects against infinite loops and deep recursion with configurable limits:\n\n```typescript\nconst env = new Bash({\n  executionLimits: {\n    maxCallDepth: 100, // Max function recursion depth\n    maxCommandCount: 10000, // Max total commands executed\n    maxLoopIterations: 10000, // Max iterations per loop\n    maxAwkIterations: 10000, // Max iterations in awk programs\n    maxSedIterations: 10000, // Max iterations in sed scripts\n  },\n});\n```\n\nAll limits have defaults. Error messages tell you which limit was hit. Increase as needed for your workload.\n\n## Security Model\n\n- The shell only has access to the provided filesystem.\n- 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.\n- There is no network access by default. When enabled, requests are checked against URL prefix allow-lists and HTTP-method allow-lists.\n- Python and JavaScript execution are off by default as they represent additional security surface.\n- Execution is protected against infinite loops and deep recursion with configurable limits.\n- Use [Vercel Sandbox](https://vercel.com/docs/vercel-sandbox) if you need a full VM with arbitrary binary execution.\n\n## Browser Support\n\nThe 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).\n\n## Default Layout\n\nWhen created without options, Bash provides a Unix-like directory structure:\n\n- `/home/user` - Default working directory (and `$HOME`)\n- `/bin` - Contains stubs for all built-in commands\n- `/usr/bin` - Additional binary directory\n- `/tmp` - Temporary files directory\n\nCommands can be invoked by path (e.g., `/bin/ls`) or by name.\n\n## AI Agent Instructions\n\nFor AI agents, [`bash-tool`](https://github.com/vercel-labs/bash-tool) provides additional guidance in its `AGENTS.md`:\n\n```bash\ncat node_modules/bash-tool/dist/AGENTS.md\n```\n\n## License\n\nApache-2.0\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvercel-labs%2Fjust-bash","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvercel-labs%2Fjust-bash","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvercel-labs%2Fjust-bash/lists"}