Local Environment Setup
The local adapter enables command execution on your local machine with native shell integration, runtime optimization, and comprehensive process management.
Installationβ
The local adapter is included in the core package:
npm install @xec-sh/core
# or
yarn add @xec-sh/core
Basic Configurationβ
Default Setupβ
The local adapter works out of the box with zero configuration:
import { $ } from '@xec-sh/core';
// Executes using your system's default shell
const result = await $`echo "Hello, World!"`;
console.log(result.stdout); // "Hello, World!"
Custom Configurationβ
Configure the local adapter for specific requirements:
import { createExecutionEngine } from '@xec-sh/core';
const engine = createExecutionEngine({
adapters: {
local: {
// Prefer Bun runtime if available
preferBun: true,
// Force specific implementation
forceImplementation: 'node', // or 'bun'
// Set user/group IDs (Unix only)
uid: 1000,
gid: 1000,
// Custom kill signal
killSignal: 'SIGTERM',
// Default timeout
defaultTimeout: 30000,
// Output encoding
encoding: 'utf8',
// Maximum buffer size (10MB)
maxBuffer: 10 * 1024 * 1024
}
}
});
const result = await engine.execute({ adapter: 'local' }, 'ls -la');
Shell Configurationβ
Shell Detectionβ
The local adapter automatically detects your system shell:
// Auto-detection based on platform
const result = await $`echo $0`;
// Output: /bin/bash (Linux/Mac) or cmd.exe (Windows)
Explicit Shell Selectionβ
Specify which shell to use:
// Use specific shell binary
await $.with({ shell: '/bin/bash' })`echo $BASH_VERSION`;
await $.with({ shell: '/bin/zsh' })`echo $ZSH_VERSION`;
await $.with({ shell: '/usr/bin/fish' })`echo $FISH_VERSION`;
// Use boolean for system default
await $.with({ shell: true })`echo "Using system shell"`;
// Disable shell (direct execution)
await $.with({ shell: false })`/usr/bin/node --version`;
Shell Optionsβ
Configure shell-specific behavior:
// Bash with specific options
await $.with({
shell: '/bin/bash',
env: {
BASH_ENV: '~/.bashrc',
SHELLOPTS: 'errexit:nounset'
}
})`source ~/.bash_profile && run-command`;
// PowerShell on Windows
await $.with({
shell: 'powershell.exe'
})`Get-Process | Where-Object {$_.CPU -gt 100}`;
// Command prompt on Windows
await $.with({
shell: 'cmd.exe'
})`dir /b *.txt`;
Runtime Selectionβ
Automatic Runtime Detectionβ
The adapter automatically detects and uses the best available runtime:
import { RuntimeDetector } from '@xec-sh/core';
if (RuntimeDetector.isBun()) {
console.log('Running with Bun - optimized performance');
} else {
console.log('Running with Node.js');
}
// Execution automatically uses detected runtime
await $`node --version || bun --version`;
Bun Optimizationβ
When Bun is available, the adapter uses optimized APIs:
// Configure to prefer Bun
const engine = createExecutionEngine({
adapters: {
local: {
preferBun: true // Use Bun.spawn when available
}
}
});
// Force Bun implementation (fails if Bun not available)
const bunEngine = createExecutionEngine({
adapters: {
local: {
forceImplementation: 'bun'
}
}
});
Process Managementβ
Working Directoryβ
Set the working directory for command execution:
// Relative path (from current working directory)
await $.with({ cwd: './src' })`ls -la`;
// Absolute path
await $.with({ cwd: '/tmp' })`pwd`; // Output: /tmp
// Chain directory changes
const projectDir = '/home/user/project';
await $.with({ cwd: projectDir })`npm install`;
await $.with({ cwd: `${projectDir}/src` })`npm test`;
Environment Variablesβ
Manage environment variables for processes:
// Add specific variables
await $.with({
env: {
NODE_ENV: 'production',
DEBUG: 'app:*'
}
})`node app.js`;
// Merge with existing environment
await $.with({
env: {
...process.env,
CUSTOM_VAR: 'value'
}
})`./script.sh`;
// Clear environment (Unix only)
await $.with({
env: {}
})`env`; // Shows empty environment
Process Signalsβ
Handle process signals and termination:
// Custom timeout with specific signal
const proc = $.with({
timeout: 5000,
killSignal: 'SIGKILL'
})`sleep 10`;
try {
await proc;
} catch (error) {
console.log('Process killed after timeout');
}
// Manual signal handling
const longRunning = $`tail -f /var/log/system.log`;
// Kill after 10 seconds
setTimeout(() => longRunning.kill('SIGTERM'), 10000);
// Handle graceful shutdown
process.on('SIGINT', async () => {
longRunning.kill();
await longRunning.catch(() => {}); // Ignore kill error
process.exit(0);
});
Input/Output Streamsβ
Standard Inputβ
Provide input to processes:
// String input
await $.with({ stdin: 'Hello, World!' })`cat`;
// Buffer input
const buffer = Buffer.from('Binary data', 'utf8');
await $.with({ stdin: buffer })`wc -c`;
// Stream input
import { createReadStream } from 'fs';
const stream = createReadStream('input.txt');
await $.with({ stdin: stream })`grep "pattern"`;
// Pipe from another command
const output = await $`echo "test"`;
await $.with({ stdin: output.stdout })`tr '[:lower:]' '[:upper:]'`;
Output Handlingβ
Control how output is captured:
// Default - capture stdout and stderr
const result = await $`ls -la`;
console.log(result.stdout);
console.log(result.stderr);
// Inherit parent's stdio
await $.with({
stdout: 'inherit',
stderr: 'inherit'
})`npm install`; // Output goes directly to console
// Ignore output
await $.with({
stdout: 'ignore',
stderr: 'ignore'
})`silent-command`;
// Pipe to custom streams
import { createWriteStream } from 'fs';
const logFile = createWriteStream('output.log');
await $.with({
stdout: logFile,
stderr: logFile
})`verbose-command`;
Error Handlingβ
Exit Code Handlingβ
Configure how non-zero exit codes are handled:
// Default - throw on non-zero exit
try {
await $`exit 1`;
} catch (error) {
console.log('Exit code:', error.exitCode); // 1
console.log('Command:', error.command); // "exit 1"
}
// Don't throw on non-zero exit
const result = await $.with({ throwOnNonZeroExit: false })`exit 42`;
console.log(result.exitCode); // 42
console.log(result.failed); // true
// Check specific exit codes
const { exitCode } = await $.with({ throwOnNonZeroExit: false })`grep "pattern" file.txt`;
if (exitCode === 0) {
console.log('Pattern found');
} else if (exitCode === 1) {
console.log('Pattern not found');
} else {
console.log('Error occurred');
}
Timeout Handlingβ
Set execution timeouts:
// Timeout with error
try {
await $.with({ timeout: 1000 })`sleep 5`;
} catch (error) {
if (error.name === 'TimeoutError') {
console.log('Command timed out after', error.timeout, 'ms');
}
}
// Timeout with custom signal
await $.with({
timeout: 5000,
killSignal: 'SIGKILL' // Force kill on timeout
})`potentially-hanging-command`;
// No timeout (default)
await $.with({ timeout: 0 })`long-running-process`;
Error Contextβ
Access detailed error information:
try {
await $.with({ cwd: '/nonexistent' })`ls`;
} catch (error) {
console.log('Error code:', error.code); // 'ENOENT'
console.log('Exit code:', error.exitCode); // null (spawn failed)
console.log('Signal:', error.signal); // null
console.log('Command:', error.command); // 'ls'
console.log('Working dir:', error.cwd); // '/nonexistent'
console.log('Stack trace:', error.stack);
}
Performance Optimizationβ
Buffer Managementβ
Control memory usage for large outputs:
// Increase buffer for large outputs
const result = await $.with({
maxBuffer: 100 * 1024 * 1024 // 100MB
})`cat large-file.txt`;
// Stream processing for unlimited output
const proc = $`find / -type f`;
proc.stdout.pipe(process.stdout);
await proc;
Parallel Executionβ
Run multiple commands efficiently:
// Parallel execution
const [result1, result2, result3] = await Promise.all([
$`command1`,
$`command2`,
$`command3`
]);
// Sequential with shared state
const tempDir = await $`mktemp -d`;
await $.with({ cwd: tempDir.stdout.trim() })`touch file1.txt`;
await $.with({ cwd: tempDir.stdout.trim() })`touch file2.txt`;
Process Reuseβ
Optimize repeated executions:
// Create reusable configuration
const nodeExec = (script) => $.with({
shell: false,
timeout: 10000
})`node -e ${script}`;
// Reuse configuration
await nodeExec('console.log("Test 1")');
await nodeExec('console.log("Test 2")');
await nodeExec('console.log("Test 3")');
Platform-Specific Featuresβ
Unix/Linux/macOSβ
Unix-specific features and configurations:
// Set user and group IDs
await $.with({
uid: 1000,
gid: 1000
})`whoami`;
// Use shell features
await $.with({ shell: '/bin/bash' })`
set -euo pipefail
source ~/.bashrc
alias ll='ls -la'
ll /tmp
`;
// Signal handling
const proc = $`sleep 100`;
proc.kill('SIGUSR1'); // Send custom signal
Windowsβ
Windows-specific features and configurations:
// PowerShell execution
await $.with({ shell: 'powershell.exe' })`
Get-Service |
Where-Object {$_.Status -eq "Running"} |
Select-Object -First 10
`;
// Command prompt
await $.with({ shell: 'cmd.exe' })`
echo off
set MY_VAR=value
echo %MY_VAR%
`;
// Windows-specific paths
await $.with({
cwd: 'C:\\Program Files'
})`dir /b`;
Security Considerationsβ
Command Injection Preventionβ
Safely handle user input:
// DON'T do this - vulnerable to injection
const userInput = '; rm -rf /';
// await $`echo ${userInput}`; // DANGEROUS!
// DO this - use arguments array
await $.with({ shell: false })`echo ${userInput}`; // Safe - treated as single argument
// Or escape arguments
import { escapeArg } from '@xec-sh/core';
await $`echo ${escapeArg(userInput)}`; // Safe - properly escaped
Privilege Managementβ
Control process privileges:
// Drop privileges (Unix only)
await $.with({
uid: 65534, // nobody user
gid: 65534 // nogroup
})`whoami`; // Output: nobody
// Avoid running as root when possible
if (process.getuid() === 0) {
console.warn('Running as root - consider using lower privileges');
}
Resource Limitsβ
Prevent resource exhaustion:
// Limit execution time
await $.with({ timeout: 5000 })`potentially-infinite-loop`;
// Limit output buffer
await $.with({ maxBuffer: 1024 * 1024 })`cat /dev/random`;
// Limit concurrent processes
const semaphore = new Semaphore(5); // Max 5 concurrent
await Promise.all(
commands.map(cmd =>
semaphore.acquire().then(() => $`${cmd}`)
)
);
Troubleshootingβ
Common Issuesβ
Command Not Foundβ
// Problem: Command not in PATH
try {
await $`custom-command`;
} catch (error) {
if (error.code === 'ENOENT') {
// Solution 1: Use full path
await $`/usr/local/bin/custom-command`;
// Solution 2: Update PATH
await $.with({
env: {
PATH: `${process.env.PATH}:/usr/local/bin`
}
})`custom-command`;
}
}
Working Directory Issuesβ
// Problem: Directory doesn't exist
try {
await $.with({ cwd: '/nonexistent' })`ls`;
} catch (error) {
// Solution: Create directory first
await $`mkdir -p /nonexistent`;
await $.with({ cwd: '/nonexistent' })`ls`;
}
Permission Deniedβ
// Problem: Insufficient permissions
try {
await $`cat /etc/shadow`;
} catch (error) {
if (error.stderr.includes('Permission denied')) {
// Solution: Use appropriate privileges
console.log('Requires elevated privileges');
}
}
Next Stepsβ
- Shell Configuration - Advanced shell setup and customization
- Debugging Techniques - Debug local command execution
- SSH Environment - Execute commands on remote servers
- Performance Guide - Optimize execution performance