A PTY (pseudo-terminal) is an interactive process inside a VM. Use it for
shells, REPLs, editors, package managers, and commands that need terminal
semantics or need to run longer than vm.exec()’s five-minute limit.
PTY sessions live in the guest and outlive the WebSocket connection that opened them. Detaching leaves the process running so you can attach again.
Open A Session
const session = await vm.pty.open({
cols: 120,
rows: 30,
onData: (bytes) => process.stdout.write(Buffer.from(bytes)),
onExit: (code) => console.log("program exited with", code),
onError: (error) => console.error(error),
});
session.write("echo hello\n");
console.log(session.sessionId);
Omit exec for a login shell, or start a specific interactive command:
const python = await vm.linuxUser("developer").pty.open({
exec: "python3",
onData: (bytes) => process.stdout.write(Buffer.from(bytes)),
});
cols and rows default to 80 by 24. In Node, the SDK uses its bundled ws
dependency so it can authenticate the WebSocket handshake.
Send Input, Resize, And Signal
session.write("ls -la\n");
session.write(new Uint8Array([0x03])); // Ctrl-C
session.resize({ cols: 200, rows: 60 });
session.signal("sigint");
session.signal("sigkill");
Signals use the lowercase values sigint and sigkill.
Detach And Reattach
Save the session ID before detaching. detach() closes only the current
WebSocket; it does not terminate the guest process.
const sessionId = session.sessionId;
session.detach();
const attached = await vm.linuxUser("developer").pty.attach({
session: sessionId,
onData: (bytes) => process.stdout.write(Buffer.from(bytes)),
onExit: (code) => console.log("exited", code),
});
session takes the numeric ID, or the slug the session was opened with —
name a session at open() and you can reattach to it from anywhere without
storing the ID.
The current SDK does not auto-reconnect. If a connection drops, call
attach() with the saved session again to create a new local handle.
List And Close
const developer = vm.linuxUser("developer");
const { sessions } = await developer.pty.list();
for (const session of sessions) {
console.log(session.sessionId, session.state, session.linuxUser, session.cols, session.rows);
}
await developer.pty.close(sessionId);
close() kills and removes the guest session. detach() leaves it running.
Lifecycle Behavior
- A client disconnect does not end the guest session.
- Pausing freezes the entire VM, including its PTY processes. Start the VM before attaching again.
- Stopping or deleting the VM terminates its sessions.
- Client input — keystrokes, resizes, signals — counts as VM activity: it resets an idle timeout, and typing into a paused VM’s session wakes the VM. Output alone does not, so a VM that only prints can still idle out.
Identity-scoped callers use vm.linuxUser("name") when opening, attaching, listing,
or closing sessions. The SDK sends that scope as linuxUser in the API query.
The Linux user must be included in that identity’s VM permission grant, and a
grant carrying allowedLinuxUsers refuses a request that names no user.
The unscoped vm.pty surface runs as the VM’s default user: the account holding
uid 1000, or root in an image that has no such account. vm.linuxUser("root")
opens a root session on any image.