Executor
The Executor API lets you run shell commands on the device without opening a visual terminal session. It supports one-off commands, long-running processes with real-time streaming, stdin writes, and background execution via a foreground service.
WARNING
Prefer visible terminals for transparency. Avoid hiding work in the background and do not start long‑running processes without good reason. For interactive or long‑lived tasks, use a terminal session instead.
Access
The global Executor is an Executor instance (clobbered to window.Executor by the terminal plugin). It has a built-in BackgroundExecutor instance for background-mode processes.
const Executor = globalThis.Executor; // Executor instance
const background = Executor.BackgroundExecutor; // BackgroundExecutor instanceBoth instances share the same methods.
NOTE
Which executor should I use?
- Use the background executor (
Executor.BackgroundExecutor) for short-running commands. It starts processes directly with no foreground service and no notification, so it is lighter, but Android can kill the process once the app leaves the foreground. Use it for quick, self-contained commands that finish in seconds. - Use the foreground executor (
Executor) for long-running commands. Its processes run under a foreground service with a persistent notification, which keeps them alive while the app is in the background. This is the default mode;moveToForeground()/moveToBackground()switch it at runtime.
One-off execution
execute(command, alpine?)
- Purpose: Runs a single shell command and waits for it to finish. Output is returned after the process exits (no live streaming of output).
- Parameters:
command(string): The command to run.alpine(boolean, optional): Run inside the Alpine sandbox whentrue; run in the Android environment whenfalse.
- Returns:
Promise<string>that resolves with stdout on success, or rejects with an error/stderr on failure.
// Outputting hello on stdout
Executor.execute('echo hello')
.then(console.log)
.catch(console.error);or with async/await:
const output = await Executor.execute('echo hello');
console.log(output);WARNING
Do not run things like an infinite loop or a shell because execute() waits for the process to exit and a shell never exits on its own, avoid running those commands with this function.
Long-running processes
start(command, onData, alpine?)
- Starts a shell process and enables real-time streaming of
stdout,stderr, andexit. - Parameters:
command(string): The command to run (e.g."sh","ls -al").onData(function):(type, data) => void.typeis"stdout","stderr", or"exit"(the process exit code);datais the output line or exit code.alpine(boolean, optional): Run inside the Alpine sandbox whentrue.
- Returns:
Promise<string>resolving to a unique process UUID used bywrite(),stop(), andisRunning().
const uuid = await Executor.start("sh", (type, data) => {
console.log(`[${type}] ${data}`);
});
Executor.write(uuid, "echo Hello World\r");
Executor.stop(uuid);write(uuid, input)
Sends input to a running process's stdin.
- Returns:
Promise<string>.
await Executor.write(uuid, "ls /sdcard\r");stop(uuid)
Terminates a running process.
- Returns:
Promise<string>.
isRunning(uuid)
Checks whether a process is still running.
- Returns:
Promise<boolean>.
if (await Executor.isRunning(uuid)) {
await Executor.stop(uuid);
}spawnStream(cmd, callback, onError?)
Spawns a process and exposes it as a raw WebSocket stream. Once the process is ready the callback is invoked with the connected WebSocket; use ws.send() to write to stdin and ws.onmessage to read stdout.
- Parameters:
cmd(string[]): Command and arguments (e.g.["sh", "-c", "echo hi"]).callback(function):(ws) => void.onError(function, optional): error handler.
Managing processes
listProcesses()
Lists the processes currently managed by this Executor.
- Returns:
Promise<Array<{ id, command, alpine, startedAt, background }>>.backgroundistruefor aBackgroundExecutor.
listAllProcesses()
Lists all running OS processes under the app's user id.
- Returns:
Promise<Array<{ pid, ppid, name, command, state, memory, isSelf }>>.
killProcess(pid)
Forcefully kills a process by its native PID.
- Returns:
Promise<string>.
Service control
moveToForeground() / moveToBackground()
Moves the Executor service between foreground (shows the notification) and background.
- Returns:
Promise<string>.
stopService()
Stops the Executor service completely. This does not guarantee that all running processes are killed - the service just stops being active. The processes will keep running until stopped.
- Returns:
Promise<string>.
Advanced
loadLibrary(path)
Loads a native library from the given path.
- Returns:
Promise<string>.
await Executor.loadLibrary('/path/to/library.so');WARNING
loadLibrary() has been deprecated and is no longer supported on newer Acode versions.
setProotDebug(enabled)
Toggles proot debug output (used for the Alpine sandbox).
- Returns:
Promise<string>.
Related APIs
- Visual terminal sessions: Terminal
