SandboxProcessManager
Added in: @mastra/core@1.7.0
Abstract base class for managing background processes in sandboxes. Provides methods to spawn processes, list them, get handles by PID, and kill them.
LocalSandbox, E2BSandbox, and DaytonaSandbox all include built-in process managers. You don't need to instantiate this class directly unless you're building a custom sandbox provider.
Usage exampleDirect link to Usage example
Access the process manager through the sandbox's processes property:
import { LocalSandbox } from '@mastra/core/workspace'
const sandbox = new LocalSandbox({ workingDirectory: './workspace' })
await sandbox.start()
// Spawn a background process
const handle = await sandbox.processes.spawn('node server.js', {
env: { PORT: '3000' },
onStdout: data => console.log(data),
})
// List all tracked processes
const procs = await sandbox.processes.list()
// Get a handle by PID
const proc = await sandbox.processes.get(handle.pid)
// Kill a process
await sandbox.processes.kill(handle.pid)
MethodsDirect link to Methods
spawn(command, options?)Direct link to spawncommand-options
Spawn a background process. Returns a ProcessHandle immediately without waiting for the process to finish.
const handle = await sandbox.processes.spawn('npm run dev', {
cwd: '/app',
env: { NODE_ENV: 'development' },
onStdout: data => console.log(data),
})
Parameters:
command:
options?:
timeout?:
env?:
cwd?:
onStdout?:
onStderr?:
abortSignal?:
Returns: Promise<ProcessHandle>
list()Direct link to list
List all tracked processes. Returns info about each process including PID, running state, and exit code.
const procs = await sandbox.processes.list()
for (const proc of procs) {
console.log(proc.pid, proc.running, proc.exitCode)
}
Returns: Promise<ProcessInfo[]>
get(pid)Direct link to getpid
Get a handle to a process by PID. Returns undefined if the process is not found or has already been dismissed.
const handle = await sandbox.processes.get(1234)
if (handle) {
console.log(handle.stdout)
await handle.kill()
}
Returns: Promise<ProcessHandle | undefined>
kill(pid)Direct link to killpid
Kill a process by PID. Waits for the process to terminate before returning. Returns true if the process was killed, false if it was not found.
const killed = await sandbox.processes.kill(handle.pid)
Returns: Promise<boolean>
ProcessInfoDirect link to ProcessInfo
Information about a tracked process, returned by list().
pid:
command?:
running:
exitCode?:
ProcessHandleDirect link to ProcessHandle
Handle to a spawned background process. Provides methods to read output, send stdin, wait for completion, and kill the process.
You don't create ProcessHandle instances directly — they're returned by spawn() and get().
Usage exampleDirect link to Usage example
const handle = await sandbox.processes.spawn('npm run dev', {
onStdout: data => console.log(data),
})
// Read accumulated output
console.log(handle.pid)
console.log(handle.stdout)
console.log(handle.stderr)
console.log(handle.exitCode) // undefined while running
// Wait for completion
const result = await handle.wait()
// Send stdin
await handle.sendStdin('input data\n')
// Kill the process
await handle.kill()
PropertiesDirect link to Properties
pid:
stdout:
stderr:
exitCode:
command:
reader:
writer:
MethodsDirect link to Methods
wait(options?)Direct link to waitoptions
Wait for the process to exit and return the result. Optionally pass onStdout/onStderr callbacks to stream output while waiting. Callbacks are automatically removed when wait() resolves.
// Simple wait
const result = await handle.wait()
console.log(result.success, result.exitCode, result.stdout)
// Wait with streaming
const result = await handle.wait({
onStdout: data => process.stdout.write(data),
onStderr: data => process.stderr.write(data),
})
Parameters:
options?:
onStdout?:
onStderr?:
Returns: Promise<CommandResult>
The CommandResult object contains:
success:
exitCode:
stdout:
stderr:
executionTimeMs:
timedOut?:
killed?:
kill()Direct link to kill
Kill the process. Returns true if the process was killed, false if it had already exited.
const killed = await handle.kill()
Returns: Promise<boolean>
sendStdin(data)Direct link to sendstdindata
Send data to the process's stdin. Throws if the process has already exited or stdin is not available.
await handle.sendStdin('console.log("hello")\n')
Returns: Promise<void>
Stream interopDirect link to Stream interop
ProcessHandle exposes reader and writer properties for integration with Node.js stream-based protocols like LSP or JSON-RPC:
import {
createMessageConnection,
StreamMessageReader,
StreamMessageWriter,
} from 'vscode-jsonrpc/node'
const handle = await sandbox.processes.spawn('typescript-language-server --stdio')
const connection = createMessageConnection(
new StreamMessageReader(handle.reader),
new StreamMessageWriter(handle.writer),
)
connection.listen()
Building a custom process managerDirect link to Building a custom process manager
To build a process manager for a custom sandbox provider, extend SandboxProcessManager and implement spawn() and list(). The base class automatically wraps your methods with ensureRunning() so the sandbox starts before any process operation.
import { SandboxProcessManager, ProcessHandle } from '@mastra/core/workspace'
import type { ProcessInfo, SpawnProcessOptions } from '@mastra/core/workspace'
class MyProcessManager extends SandboxProcessManager<MySandbox> {
async spawn(command: string, options: SpawnProcessOptions = {}): Promise<ProcessHandle> {
// Your spawn implementation
const handle = new MyProcessHandle(/* ... */)
this._tracked.set(handle.pid, handle)
return handle
}
async list(): Promise<ProcessInfo[]> {
return Array.from(this._tracked.values()).map(handle => ({
pid: handle.pid,
running: handle.exitCode === undefined,
exitCode: handle.exitCode,
}))
}
}
Pass the process manager to your sandbox via the processes option in MastraSandbox:
class MySandbox extends MastraSandbox {
constructor() {
super({
name: 'MySandbox',
processes: new MyProcessManager(),
})
}
}
When a process manager is provided, MastraSandbox automatically creates a default executeCommand implementation that uses spawn() + wait(), so you don't need to implement both.