by lysonober · v0.0.3
Create and manage E2B sandboxes for Dify workflows, with Claude Code and Next.js tooling.
This community listing does not yet include every recommended support, privacy, pricing, and permission disclosure. Review the available package permissions before installing.
Available inside your emploidai workspace after installation.
Available inside your emploidai workspace after installation.
Author: lysonober
Version: 0.0.3
Type: Tool
Better E2B Sandbox creates and manages E2B cloud sandboxes inside Dify workflows. It supports Claude Code integration, Next.js project scaffolding (npm and Bun), stateful interactive shell sessions, common package installation, file operations, template building, and full sandbox lifecycle management with pause, resume, and kill capabilities.
When you create a new workflow in Dify, the Web App and Backend Service API access points are enabled by default. This means anyone with the URL can trigger your workflow, which in turn can create sandboxes, run commands, read files, and consume your E2B and Anthropic API credits.
Before publishing or sharing your workflow, you must:
Without these precautions, a leaked workflow URL allows anyone to create sandboxes under your E2B account, run arbitrary code via Claude Code, and access any credentials (GitHub tokens, Vercel tokens, Anthropic keys) that you configured in the plugin. E2B bills per second for running sandboxes, so unauthorized usage can result in unexpected charges.
This is not a limitation of this plugin. It is a standard Dify workflow security consideration that applies to any plugin that manages external resources.
The single most confusing concept in E2B is the sandbox timeout. If you do not understand this, you will waste money or lose work. Here is how it works.
When you create a sandbox, you set a timeout. For example, Create Claude Code Sandbox with timeout_preset=1800 gives you a 30-minute sandbox. This means the sandbox will automatically shut down 30 minutes after creation, regardless of what is running inside it.
Here is the critical part: every time you call Sandbox.connect(), the timeout resets. Every tool in this plugin that talks to an existing sandbox (Send Sandbox Input, Install Packages, Read Sandbox File, Write Sandbox File, Start Shell Session, Pause Sandbox, and so on) internally calls Sandbox.connect(). That connect call resets the sandbox timeout to whatever value the tool passes.
You create a Claude Code sandbox with sandbox_timeout_seconds=86400 (24 hours). The sandbox starts with a 24-hour clock. You store the sandbox_id. Five minutes later, you call Send Sandbox Input with the default timeout_preset=1800 (30 minutes). At that moment, the 24-hour clock is thrown away and replaced with a 30-minute clock. The sandbox will now die 30 minutes after that Send Sandbox Input call, not 24 hours after creation.
What you should do: If you need a long-lived sandbox, make sure every subsequent tool call also uses a long timeout. If you created a 24-hour sandbox, set timeout_preset=86400 (or use sandbox_timeout_seconds=86400) on every Send Sandbox Input call too. Or better yet, use Pause Sandbox between interactions, which preserves state indefinitely (up to 30 days) without consuming compute.
You have a sandbox running a background task. The user has not sent a message in 25 minutes, and the 30-minute timeout is about to expire. You call Send Sandbox Input with action=extend_lifecycle_1h. This action does not run any command. It just connects with timeout=3600 (1 hour), which resets the sandbox clock to 1 hour from now. The background task keeps running.
You want Claude Code to monitor an API endpoint every 5 minutes for the next 6 hours. You call Setup Sandbox Heartbeat with task_description="Check https://api.example.com/health every 5 minutes and log the response to /tmp/health.log", suggested_interval="5 minutes", estimated_duration="6 hours", and sandbox_timeout_seconds=21600 (6 hours). The tool sends a specialized prompt to Claude Code that tells it to create a background script with nohup while true; do curl ...; sleep 300; done &. Claude Code creates the script and starts it. The sandbox stays alive for 6 hours because of the timeout you set.
But what if your E2B plan only allows 1-hour sandboxes? Then you need a different approach: set the sandbox timeout to 1 hour, and have your Dify workflow call Send Sandbox Input with action=extend_lifecycle_1h every 50 minutes. Each call resets the clock. The background task survives because the sandbox never expires.
This plugin provides 18 tools. Here is what each one does, when to use it, and how its parameters work.
The most basic creation tool. It starts a new sandbox from a template alias (or the default base template if you leave template_alias empty).
Parameters that matter:
template_alias: The template to use. Leave empty for a bare Linux sandbox. Use claude-code if you want Claude Code pre-installed (but Create Claude Code Sandbox is better for that). Use any custom alias you built with Build Template.envs_json: Extra environment variables. Format: DEBUG=true;NODE_ENV=development or {"DEBUG":"true"}.metadata_json: Labels for filtering in List Sandboxes. Format: userId=user_123;app=dify.ports: Comma-separated port numbers. The tool generates external URLs like https://3000-SANDBOX_ID.e2b.app for each port.env_injection_scope: sandbox_global (default) writes env vars permanently into the sandbox. command_only means env vars are only available when explicitly passed with each command.github_token_override, it overrides both the envs_json GITHUB_TOKEN and the provider credential. Same for Vercel parameters.Story: Creating a sandbox to analyze a private repository
You want Claude Code to review code in a private GitHub repo. You call Create Sandbox with template_alias=claude-code, github_token_override=ghp_xxxxx, github_username_override=lysonober, ports=3000. The sandbox starts with GITHUB_TOKEN and GITHUB_USER injected. You then call Send Sandbox Input with action=shell_command, input_text="git clone https://$GITHUB_TOKEN@github.com/lysonober/my-private-repo.git". The clone succeeds because the token is in the environment.
Creates a sandbox specifically optimized for Claude Code. This is the tool you should use when your primary goal is to run Claude Code prompts.
What it does that Create Sandbox does not:
claude-code template alias, which has Claude Code CLI pre-installed.claude update automatically to ensure the CLI is up to date. The update tries three commands in order: yes | claude update, claude update, npm install -g @anthropic-ai/claude-code@latest. The first one that succeeds wins..claude/settings.local.json with your chosen permissions preset.Permissions presets:
allow_all (default): Bypasses all permission prompts. Best for non-interactive sandbox environments where Claude Code cannot ask the user for confirmation. This is the recommended default for Dify workflows.dev_standard: Allows common development operations (read, edit, write, git status, npm, pip, pytest) but asks before git push, WebFetch. Denies reading .env files and using curl/wget.ci_build_test: Non-interactive mode for CI pipelines. Auto-denies anything not explicitly allowed.review_readonly: Read-only access. Cannot edit, write, or run commands.plan_mode: Planning only. No tool execution at all.Story: Setting up a Claude Code sandbox with project context
You are building a Dify bot for a law firm. Each lawyer has domain expertise that should persist across conversations. You call Create Claude Code Sandbox with:
permissions_preset=allow_alluser_memory_content="I am a corporate lawyer specializing in M&A contracts. Flag indemnification clauses exceeding $10M. Highlight non-compete terms over 2 years."project_memory_content="Tech stack: Next.js, PostgreSQL, Prisma. Follow ESLint rules. Use TypeScript strict mode."sandbox_timeout_seconds=3600The sandbox starts with Claude Code configured to bypass all permission prompts, the lawyer's expertise written to ~/.claude/CLAUDE.md, and project conventions written to CLAUDE.md in the working directory. Every Claude Code prompt in this sandbox will see both memory files.
Creates a sandbox with a fresh Next.js project scaffolded using the latest create-next-app. This tool does not rely on any pre-built Next.js template. Instead, it always starts from the base template and runs the scaffolding commands at creation time, guaranteeing you get the latest Next.js and shadcn versions.
What happens step by step:
npx create-next-app@latest . --ts --tailwind --eslint --import-alias "@/*" --use-npm --app --src-dir in /home/user/nextjs-app.npx shadcn@latest init -d (non-interactive shadcn initialization).shadcn_add_all=true, runs yes | npx shadcn@latest add --all (installs all shadcn UI components).npm run dev -- --hostname 0.0.0.0 --port 3000.Important: This tool requires allow_internet_access=true (it is the default). Without internet, npx cannot download packages.
Story: Creating a Next.js sandbox and then adding Claude Code
You want users to be able to create a Next.js project and then use Claude Code to modify it. Your Dify workflow does:
shadcn_add_all=true, vercel_token_override=xxx, vercel_org_id_override=team_xxx. Store the sandbox_id.preset_command=claude_code_latest and the sandbox_id from step 1. Now the sandbox has both Next.js and Claude Code.action=claude_code, input_text="Add a dark mode toggle to the navbar using shadcn components", resume_session=false (fresh session since Claude Code was just installed).The user sees the dev server URL from step 1, and after step 3, the Next.js project has a dark mode toggle.
Same as Create Next.js Sandbox, but uses Bun instead of npm. The tool first installs Bun (curl -fsSL https://bun.sh/install | bash), then uses bunx create-next-app@latest and bun run dev. The port readiness timeout is 90 seconds (longer than npm because Bun installation adds time). Dev server log goes to /tmp/nextjs-bun-dev.log.
The primary interaction tool. Once you have a sandbox_id, this is how you talk to it.
Actions:
claude_code (default): Sends input_text as a prompt to Claude Code CLI. Requires ANTHROPIC_API_KEY (from provider credentials or anthropic_api_key_override). If resume_session=true (default), uses claude --continue -p to resume the most recent conversation. If false, starts fresh with claude -p.shell_command: Runs input_text as a shell command. Each call is a fresh shell, so cd and export do not persist across calls. Use Start Shell Session if you need stateful sessions.heartbeat: Runs echo heartbeat. Use this to keep the sandbox alive without doing anything meaningful.extend_lifecycle_1h: Does not run any command. Just connects with a 1-hour timeout to reset the sandbox clock.What happens when Claude Code CLI is missing:
If you select action=claude_code on a sandbox that does not have Claude Code installed (for example, a sandbox created with Create Sandbox or Create Next.js Sandbox), the tool detects "command not found" in the error output and returns a clear message telling you to run Install Packages with the Claude Code Latest preset first. It does not crash.
What happens when Claude Code CLI is outdated: If Claude Code CLI refuses to run because it needs an update (error message contains "needs an update" and "claude update"), the tool automatically runs the update sequence (same as Create Claude Code Sandbox) and retries the original command. This only triggers when the CLI explicitly refuses, not on every call.
Story: Multi-turn conversation with Claude Code
Turn 1: User says "Create a Python web scraper for Hacker News". Your workflow calls Create Claude Code Sandbox, stores sandbox_id, then calls Send Sandbox Input with action=claude_code, input_text="Create a Python web scraper that fetches the top 10 stories from Hacker News and saves them to stories.json", resume_session=false. Claude Code creates the scraper. Workflow returns the output and pauses the sandbox.
Turn 2: User says "Add error handling and retry logic". Your workflow calls Send Sandbox Input with the same sandbox_id, action=claude_code, input_text="Add error handling with exponential backoff retry logic to the scraper", resume_session=true. Claude Code remembers the scraper it created and modifies it. Because resume_session is true, it has full context from turn 1.
Turn 3: User says "Run it and show me the output". Your workflow calls Send Sandbox Input with action=shell_command, input_text="cd /home/user && python scraper.py && cat stories.json". The shell command runs the scraper and returns the JSON output.
Installs tools and packages in an existing sandbox. This is the general-purpose "add stuff to a sandbox" tool.
How to use:
preset_command dropdown. Pick "Claude Code Latest", "OpenClaw", or "Remotion".additional_preset_commands with comma delimiter: claude_code_latest,openclaw.custom_commands with your chosen delimiter.Preset commands and what they actually run:
claude_code_latest: npm install -g @anthropic-ai/claude-code@latestopenclaw: curl -fsSL https://openclaw.ai/install.sh | bashremotion: npx create-video@latestPresets execute first, then custom commands, in order. Each command runs sequentially as the user "user" (not root).
Story: Installing multiple tools at once
You created a base sandbox and want to set up a full development environment. You call Install Packages with:
preset_command=claude_code_latestcustom_commands="npm install -g pnpm\npip install requests beautifulsoup4\napt-get update && apt-get install -y jq"custom_delimiter=newlineThis runs four commands in order: (1) npm install Claude Code, (2) npm install pnpm, (3) pip install requests and beautifulsoup4, (4) apt-get install jq. The output tells you which succeeded and which failed.
These three tools work together to provide stateful interactive shell sessions. They solve the problem that sbx.commands.run() (used by Send Sandbox Input with action=shell_command) starts a fresh shell every time, so cd, exported variables, and shell history do not persist.
Start Shell Session:
session_pid (integer) that you use for all subsequent interactions.cwd parameter sets the initial working directory.Send Shell Session Input:
input_text to the PTY and reads back output.read_timeout_seconds (default 2) controls how long to wait for output. For fast commands like ls, 2 seconds is fine. For slow commands like npm install, increase it.max_output_chars (default 4000) caps the output length to avoid flooding.Kill Shell Session:
Story: Using a stateful shell to navigate and build a project
Step 1: Start Shell Session with sandbox_id=xxx, cwd=/home/user. Returns session_pid=42.
Step 2: Send Shell Session Input with session_pid=42, input_text="ls". Output shows the directory listing.
Step 3: Send Shell Session Input with session_pid=42, input_text="cd my-project". Output shows the prompt changed to my-project directory.
Step 4: Send Shell Session Input with session_pid=42, input_text="npm install", read_timeout_seconds=30. The shell remembers you are in my-project because the PTY session is stateful.
Step 5: Send Shell Session Input with session_pid=42, input_text="npm run build", read_timeout_seconds=60.
Step 6: Kill Shell Session with session_pid=42.
Notice how steps 3 through 5 all happen in the same shell. If you had used Send Sandbox Input with action=shell_command, each call would start in /home/user and you would have to prepend cd my-project && to every command.
A specialized Claude Code tool for creating periodic background tasks. It prepends a detailed system prompt to your task description that teaches Claude Code how to write a background loop script.
When to use: When you want something to happen repeatedly inside the sandbox over a long period. Examples: health checks, data polling, log rotation, periodic API calls.
Parameters:
task_description (required): What should happen. Be specific.suggested_interval: How often. Examples: "5 minutes", "30 seconds", "1 hour".estimated_duration: How long. Examples: "6 hours", "1 day", "until stopped".Story: Monitoring a deployment
You just deployed a web app from a sandbox. You want to monitor it for the next 2 hours. You call Setup Sandbox Heartbeat with:
task_description="Check https://my-app.vercel.app every 2 minutes. If the HTTP status is not 200, write an alert to /tmp/alerts.log with the timestamp and status code."suggested_interval="2 minutes"estimated_duration="2 hours"sandbox_timeout_seconds=7200Claude Code creates a script like:
#!/bin/bash
while true; do
STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://my-app.vercel.app)
if [ "$STATUS" != "200" ]; then
echo "$(date): ALERT status=$STATUS" >> /tmp/alerts.log
fi
sleep 120
done
It runs it with nohup ./monitor.sh & and reports the PID. Two hours later, the sandbox expires. If you need to check the alerts before that, call Read Sandbox File with file_path=/tmp/alerts.log.
A security tool that blocks or redacts known secret values from output text before it reaches end users.
How it works:
additional_secrets parameter.block mode (default): If any secret is found, replaces the entire output with a warning message.redact mode: Replaces each matched secret with [REDACTED] and returns the modified text.Story: Preventing API key leakage in a chatbot
Your Dify workflow runs Claude Code and returns the output to users. Claude Code might accidentally print environment variables (for example, if the user asks "show me all environment variables" and Claude runs env). Without Guard Output, the ANTHROPIC_API_KEY would be sent to the user.
Your workflow: Send Sandbox Input (claude_code) -> Guard Output (text = output from previous step, mode = redact) -> Reply node. If Claude Code's output contains the API key, it gets replaced with [REDACTED] before reaching the user.
Reads a file or lists a directory from the sandbox filesystem.
For files: Returns the file content directly.
For directories: Lists all entries (files, directories, symlinks) in a tree format, then reads file contents in parallel using up to 50 concurrent threads. You can control how many files to read with max_files_content (0 = tree only, -1 = read all, positive number = limit). Hidden files (starting with .) are excluded by default unless include_hidden=true.
Story: Reviewing what Claude Code created
After running Claude Code to create a project, you call Read Sandbox File with file_path=/home/user/my-project. The tool returns:
/home/user/my-project/
├── src/
├── package.json
├── tsconfig.json
└── README.md
FILE CONTENTS
--- /home/user/my-project/package.json ---
{ "name": "my-project", ... }
--- /home/user/my-project/tsconfig.json ---
{ "compilerOptions": { ... } }
--- /home/user/my-project/README.md ---
# My Project
...
To read the src directory, make another call with file_path=/home/user/my-project/src.
Writes content to a file in the sandbox. Parent directories are created automatically.
Story: Injecting a configuration file before running Claude Code
You want Claude Code to work with a specific .env file. You call Write Sandbox File with file_path=/home/user/my-project/.env, content="DATABASE_URL=postgres://...\nAPI_KEY=test123". Then you call Send Sandbox Input with action=claude_code, input_text="Read the .env file and create a database connection module".
Freezes a sandbox in place. The sandbox stops consuming compute resources, but its entire state (filesystem, memory, running processes) is preserved for up to 30 days.
Technical detail: Pausing takes approximately 4 seconds per 1 GiB of RAM.
When to use: Between user interactions in a multi-turn conversation. Pause after each response, resume (by calling any tool with the sandbox_id) when the user sends the next message.
Story: Cost-efficient multi-turn conversation
Your chatbot creates a sandbox for each user conversation. A user sends a message at 10:00 AM, you create a sandbox and run Claude Code. The response takes 30 seconds. Without pausing, the sandbox runs (and costs money) until the 30-minute timeout. With pausing, you pause immediately after getting the response. The sandbox costs 30 seconds of compute instead of 30 minutes. When the user replies at 10:15 AM, any tool call with that sandbox_id automatically resumes it.
Lists all sandboxes (running and paused) in your E2B account with optional filtering.
Filters:
state_filter: running, paused, or all (default).sandbox_ids: Comma-separated IDs to look up specific sandboxes.metadata_user_id, metadata_app, metadata_env, metadata_custom (key=value pairs).Story: Cleaning up forgotten sandboxes
You notice your E2B bill is higher than expected. You call List Sandboxes with state_filter=running and see 15 running sandboxes. Most of them have metadata_app=dify and metadata_user_id values. You identify 10 sandboxes from yesterday's testing that were never killed. You call Kill Sandbox for each one.
Permanently destroys a sandbox. All files, Claude Code conversation history, and running processes are gone. There is no undo.
When to use: When a conversation ends and you are certain you will not need the sandbox again.
Creates a reusable E2B template with pre-installed dependencies and repositories. The build runs on E2B's infrastructure, not on your Dify server.
Base strategies:
e2b_base_image (default): Start from E2B's base Linux image.template_alias: Extend an existing template (yours or a public one).docker_image: Start from any Docker image.What you can pre-install:
apt_packages: System packages (comma-separated). Example: jq,curl,git.pip_packages: Python packages (comma-separated). Example: requests,beautifulsoup4,pandas.npm_packages: Node packages (comma-separated). Example: typescript,@anthropic-ai/claude-code.git_repositories: Git URLs to clone (comma-separated). Cloned to /home/user/REPO_NAME.custom_dockerfile_commands: Advanced shell commands.Story: Building a template for a specific project
Your team works on a Python ML project. Every sandbox needs numpy, pandas, scikit-learn, and the project repo. You call Build Template with:
template_name=ml-project-basepip_packages=numpy,pandas,scikit-learn,matplotlibgit_repositories=https://github.com/your-org/ml-project.gitcpu_count=4memory_mb=4096The build takes a few minutes. After that, every Create Sandbox with template_alias=ml-project-base starts with everything pre-installed. No waiting for pip install or git clone.
Lists all templates in your E2B account with their aliases, build status, CPU, memory, and visibility settings.
All creation tools support environment variable injection with a clear three-layer priority system:
github_token_override, vercel_token_override, etc.For example, if your provider credential has VERCEL_TOKEN=aaa, your envs_json has VERCEL_TOKEN=bbb, and your tool parameter has vercel_token_override=ccc, the sandbox gets VERCEL_TOKEN=ccc.
The env_injection_scope parameter controls when variables are injected:
sandbox_global (default): Variables are set at sandbox creation time and persist for the entire sandbox lifetime.command_only: Variables are only available when explicitly passed with each command (as a shell prefix).Vercel variables deserve special mention: when you set Vercel Team ID (via vercel_org_id_override or provider credential), the plugin injects both VERCEL_ORG_ID and VERCEL_TEAM_ID with the same value, because different Vercel tools expect different variable names.
Create Claude Code Sandbox writes .claude/settings.local.json inside the sandbox. This file controls what Claude Code is allowed to do.
| Preset | defaultMode | Best For |
|---|---|---|
| allow_all (default) | bypassPermissions | Non-interactive sandboxes, Dify workflows |
| dev_standard | acceptEdits | Day-to-day development with safety rails |
| dev_webfetch_allowlist | acceptEdits | Development needing external docs/packages |
| ci_build_test | dontAsk | CI pipelines, automated testing |
| review_readonly | dontAsk | Code review, no modifications |
| plan_mode | plan | Planning only, no execution |
You can append custom rules via permissions_allow, permissions_ask, and permissions_deny parameters. Format: semicolon-separated rules like Bash(npm run build);Read(./docs/**) or JSON array like ["Bash(npm run build)","Read(./docs/**)"].
Mistake: Using shell_command action for multi-step operations
Each action=shell_command call starts a fresh shell. If you call input_text="cd /home/user/project" followed by input_text="npm install", the second call runs in /home/user (the default), not in /home/user/project.
Fix: Either combine commands with && (e.g., input_text="cd /home/user/project && npm install"), or use Start Shell Session for a stateful session.
Mistake: Forgetting that connect resets the timeout
As explained above, every tool call resets the sandbox timeout. If you create a sandbox with a 2-hour timeout but all your Send Sandbox Input calls use the default 30-minute timeout, the sandbox will die 30 minutes after the last interaction, not 2 hours after creation.
Fix: Set consistent timeouts across all tool calls, or use Pause Sandbox between interactions.
Mistake: Using claude_code action on a non-Claude-Code sandbox
If you create a sandbox with Create Sandbox or Create Next.js Sandbox, Claude Code CLI is not installed. Calling Send Sandbox Input with action=claude_code will fail.
Fix: Either use Create Claude Code Sandbox, or call Install Packages with preset_command=claude_code_latest before using the claude_code action.
Mistake: Not using Guard Output before replying to users
Claude Code might accidentally output API keys or tokens. If your workflow sends Claude Code output directly to a Reply node, those secrets reach the end user.
Fix: Always place Guard Output between Send Sandbox Input and the Reply node.
User Message -> Create Claude Code Sandbox -> Send Sandbox Input (claude_code) -> Guard Output -> Reply -> Kill Sandbox
First turn: User Message -> Create Claude Code Sandbox -> Store sandbox_id -> Send Sandbox Input (claude_code) -> Guard Output -> Reply -> Pause Sandbox
Subsequent turns: User Message -> Send Sandbox Input (claude_code, same sandbox_id, resume_session=true) -> Guard Output -> Reply -> Pause Sandbox
Final turn (detected by Dify logic): ... -> Reply -> Kill Sandbox
Create Next.js Sandbox -> Store sandbox_id -> Install Packages (claude_code_latest) -> Send Sandbox Input (claude_code, "modify the project...") -> Guard Output -> Reply
One-time setup: Build Template (with git repo, packages) -> Store template alias
Every conversation: Create Sandbox (template_alias=your-template) -> Send Sandbox Input -> ...
E2B bills per second for running sandboxes. Paused sandboxes preserve state without running compute costs.
Practical guidance: Use Pause Sandbox between interactions to stop paying for idle compute. A 30-minute running sandbox with 2 vCPU and 512 MiB RAM costs about $0.05 for the compute plus $0.004 for memory. Pausing reduces that to near zero between interactions.
Email: lysonober@gmail.com