A comprehensive shell command execution plugin for ElizaOS with PTY support, background execution, session management, and security restrictions.
Key Features:
- π PTY Support - Run interactive terminal applications with full pseudo-terminal support
- β±οΈ Background Execution - Long-running commands automatically background with session management
- π Session Management - Track, poll, and interact with running processes
- π Security First - Directory restrictions, forbidden commands, and timeout protection
- π Command History - Per-conversation command tracking
Available in three languages:
- π¦ TypeScript - Primary implementation for Node.js (full feature support)
- π Python - Native Python implementation
- π¦ Rust - High-performance Rust implementation
Just want your agent to execute commands? Here's the fastest path:
-
Install the plugin:
cd your-eliza-project bun add @elizaos/plugin-shell -
Create/update your
.env:SHELL_ALLOWED_DIRECTORY=/path/to/safe/directory
-
Add to your character:
const character = { // ... other config plugins: ["@elizaos/plugin-shell"], };
-
Run:
bun start
SHELL_ALLOWED_DIRECTORY - choose wisely!
- β Cross-platform support: Works on Linux, macOS, and Windows
- β Directory restriction: Commands are restricted to a specified directory for safety
- β Command filtering: Configurable list of forbidden commands
- β Timeout protection: Automatic termination of long-running commands
- β Command history: Tracks command execution history per conversation
- β File operation tracking: Monitors file creation, modification, and deletion
- β Shell context provider: Provides command history and working directory to agent context
- β Output capture: Returns both stdout and stderr from executed commands
- β Safety first: Disabled by default, requires explicit enabling
- β Multi-language: Available in TypeScript, Python, and Rust
- β
PTY Support: Run interactive terminal applications (vim, htop, etc.) with
@lydell/node-pty - β Background Execution: Commands automatically background after configurable yield window
- β Session Management: Track running/finished sessions, poll output, send keys
- β Process Control: List, poll, log, write, send-keys, submit, paste, kill, clear, remove
- β Output Truncation: Configurable max output with intelligent truncation
- β Platform-specific Shell: Auto-detects shell (bash, sh, PowerShell on Windows)
plugin-shell/
βββ typescript/ # TypeScript implementation
β βββ actions/ # EXECUTE_COMMAND, CLEAR_SHELL_HISTORY
β βββ providers/ # SHELL_HISTORY provider
β βββ services/ # ShellService
β βββ utils/ # Path validation, security checks
β βββ types/ # Type definitions
β βββ __tests__/ # Unit tests
βββ python/ # Python implementation
β βββ elizaos_plugin_shell/
β β βββ service.py # ShellService
β β βββ path_utils.py
β β βββ types.py
β βββ tests/ # Python tests
βββ rust/ # Rust implementation
β βββ src/
β β βββ lib.rs
β β βββ service.rs # ShellService
β β βββ path_utils.rs
β β βββ types.rs
β βββ tests/ # Integration tests
βββ package.json # NPM package config
# Using bun (recommended)
bun add @elizaos/plugin-shell
# Using npm
npm install @elizaos/plugin-shell
# Using pnpm
pnpm add @elizaos/plugin-shellpip install elizaos-plugin-shellAdd to your Cargo.toml:
[dependencies]
elizaos-plugin-shell = "1.2.0"Set the following environment variables:
# Set the allowed directory (commands can only run here)
SHELL_ALLOWED_DIRECTORY=/home/user/safe-workspace
# OPTIONAL: Set custom timeout in milliseconds (default: 30000)
SHELL_TIMEOUT=60000
# OPTIONAL: Add additional forbidden commands (comma-separated)
SHELL_FORBIDDEN_COMMANDS=rm,mv,cp,chmod,chown,shutdown,reboot
# OPTIONAL: Maximum output characters to capture (default: 200000)
SHELL_MAX_OUTPUT_CHARS=200000
# OPTIONAL: Milliseconds before backgrounding (default: 10000)
SHELL_BACKGROUND_MS=10000
# OPTIONAL: Allow background execution (default: true)
SHELL_ALLOW_BACKGROUND=true
# OPTIONAL: Session record TTL in milliseconds (default: 1800000 = 30min)
SHELL_JOB_TTL_MS=1800000import { shellPlugin, ShellService } from "@elizaos/plugin-shell";
// Use as a plugin
const character = {
plugins: [shellPlugin],
};
// Or use the service directly
const service = new ShellService(runtime);
const result = await service.executeCommand("ls -la", "conversation-123");
// Advanced: Use exec with PTY and background support
const execResult = await service.exec("npm install", {
pty: true, // Run with pseudo-terminal
background: false, // Or yieldMs: 5000 to auto-background after 5s
timeout: 300, // 5 minute timeout
workdir: "/project",
});
if (execResult.status === "running") {
console.log(`Background session: ${execResult.sessionId}`);
// Poll for updates
const pollResult = await service.processAction({
action: "poll",
sessionId: execResult.sessionId,
});
console.log(pollResult.message);
}
// Get the service from an Eliza agent runtime
const shellService = runtime.getService<ShellService>("shell");from elizaos_plugin_shell import ShellService, ShellConfig
config = ShellConfig.from_env()
service = ShellService(config)
result = await service.execute_command("ls -la", "conversation-123")
if result.success:
print(f"Output: {result.stdout}")use elizaos_plugin_shell::{ShellConfig, ShellService};
let config = ShellConfig::from_env()?;
let mut service = ShellService::new(config);
let result = service.execute_command("ls -la", Some("conversation-123")).await?;
if result.success {
println!("Output: {}", result.stdout);
}Executes ANY shell command within the allowed directory, including file operations.
Examples:
run ls -la- List files with detailsexecute npm test- Run testscreate a file called hello.txt- Creates a new filecheck git status- Show git repository status
Manage running and finished shell sessions. Supports the following operations:
| Action | Description |
|---|---|
list |
List all running and finished sessions |
poll |
Get new output from a running session |
log |
Get session output with offset/limit |
write |
Write data to session stdin |
send-keys |
Send terminal key sequences (arrows, ctrl, etc) |
submit |
Send carriage return (Enter) |
paste |
Paste text with bracketed paste mode |
kill |
Kill a running session |
clear |
Clear a finished session record |
remove |
Kill (if running) and remove session |
Examples:
list all running processescheck session calm-harborkill the process swift-reefsend enter to session brisk-cove
Clears the command history for the current conversation.
Examples:
clear my shell historyreset the terminal history
The plugin includes a SHELL_HISTORY provider that makes the following information available to the agent:
- Recent Commands: Last 10 executed commands with their outputs
- Current Working Directory: The current directory within the allowed path
- Allowed Directory: The configured safe directory boundary
- File Operations: Recent file creation, modification, and deletion operations
All commands execute within SHELL_ALLOWED_DIRECTORY:
- Attempts to navigate outside are blocked
- Absolute paths outside the boundary are rejected
cd ..stops at the allowed directory root
By default, these potentially dangerous commands are blocked:
- Destructive:
rm -rf /,rmdir - Permission changes:
chmod 777,chown,chgrp - System operations:
shutdown,reboot,halt,poweroff - Process control:
kill -9,killall,pkill - User management:
sudo rm -rf,su,passwd,useradd,userdel - Disk operations:
format,fdisk,mkfs,dd if=/dev/zero,shred
- No Shell Expansion: Commands execute without dangerous shell interpretation
- Timeout Protection: Commands auto-terminate after timeout
- Command History: All executed commands are logged for audit
- Path Traversal Protection: Blocks
../and similar patterns
cd typescript
bun run build.ts # Build
npx vitest # Run testscd python
pip install -e ".[dev]" # Install with dev dependencies
pytest # Run tests
mypy elizaos_plugin_shell # Type checkcd rust
cargo build --release # Build
cargo test # Run tests# From plugin root
bun run build # Build TypeScript
bun run build:python # Build Python
bun run build:rust # Build Rust
bun run test # Test TypeScript
bun run test:python # Test Python
bun run test:rust # Test Rust| Field | Type | Description |
|---|---|---|
success |
boolean | Whether the command executed successfully |
stdout |
string | Standard output from the command |
stderr |
string | Standard error output |
exitCode |
number | null | Exit code of the command |
error |
string | undefined | Error message if command failed |
executedIn |
string | Directory where command was executed |
| Field | Type | Description |
|---|---|---|
type |
FileOperationType | Type of operation (create, write, read, delete, mkdir, move, copy) |
target |
string | Target file/directory path |
secondaryTarget |
string | undefined | Secondary target for move/copy |
| Field | Type | Default | Description |
|---|---|---|---|
enabled |
boolean | false | Whether shell is enabled |
allowedDirectory |
string | cwd | Directory to restrict commands to |
timeout |
number | 30000 | Timeout in milliseconds |
forbiddenCommands |
string[] | [...] | List of forbidden commands |
maxOutputChars |
number | 200000 | Max output characters to capture |
pendingMaxOutputChars |
number | 200000 | Max pending output per stream |
defaultBackgroundMs |
number | 10000 | Default background yield window |
allowBackground |
boolean | true | Allow background execution |
| Field | Type | Description |
|---|---|---|
id |
string | Unique session identifier (slug) |
command |
string | The executed command |
pid |
number | undefined | Process ID |
startedAt |
number | Start timestamp |
cwd |
string | undefined | Working directory |
aggregated |
string | Accumulated output |
tail |
string | Last 2000 chars of output |
exited |
boolean | Whether process has exited |
exitCode |
number | null | Exit code (if exited) |
exitSignal |
string | number | null | Exit signal (if killed) |
truncated |
boolean | Whether output was truncated |
backgrounded |
boolean | Whether running in background |
type ExecResult =
| { status: "running"; sessionId: string; pid?: number; startedAt: number; cwd?: string; tail?: string }
| { status: "completed" | "failed"; exitCode: number | null; durationMs: number; aggregated: string; cwd?: string; timedOut?: boolean; reason?: string }| Field | Type | Description |
|---|---|---|
workdir |
string | Working directory |
env |
Record<string, string> | Additional environment variables |
yieldMs |
number | Yield to background after this time |
background |
boolean | Run immediately in background |
timeout |
number | Timeout in seconds |
pty |
boolean | Use pseudo-terminal |
conversationId |
string | Conversation ID for history tracking |
scopeKey |
string | Scope key for session isolation |
sessionKey |
string | Session key for notifications |
notifyOnExit |
boolean | Notify on background exit |
onUpdate |
(session) => void | Callback for output updates |
Contributions are welcome! Please ensure:
- All three language implementations stay in feature parity
- Tests pass for all languages
- Follow the code style of each language
- Update documentation as needed
MIT - See LICENSE for details.