A small TypeScript demo for running a real shell through node-pty. It includes both a local terminal bridge and a browser-based terminal UI powered by xterm.js over WebSockets.
- Spawns the default system shell with
node-pty - Runs directly in the current terminal with raw input forwarding
- Serves an xterm.js browser terminal at
http://127.0.0.1:3000 - Handles terminal resize messages from the browser client
- Supports optional token protection for the browser terminal WebSocket
- Requires a token when binding to a non-loopback host
pnpm installStart a shell inside the current terminal:
pnpm devBy default, the app uses your platform shell:
- Windows:
pwsh.exe - macOS:
$SHELLorzsh - Linux:
$SHELLorbash
You can also pass a shell and arguments:
pnpm dev /bin/bash --loginStart the HTTP and WebSocket server:
pnpm dev:webOpen:
http://127.0.0.1:3000
The page in public/index.html connects to the /terminal WebSocket endpoint and streams input/output to a node-pty shell.
The server loads environment variables from .env automatically. Use .env.example as a starting point:
HOST=127.0.0.1
PORT=3000
# TERMINAL_TOKEN=change-meHOST defaults to 127.0.0.1 and PORT defaults to 3000.
TERMINAL_TOKEN is optional for local loopback use. If set, open the browser with the same token:
TERMINAL_TOKEN=change-me
pnpm dev:webhttp://127.0.0.1:3000/?token=change-me
If you bind HOST to a non-loopback address such as 0.0.0.0, TERMINAL_TOKEN is required because the server exposes a real PTY shell.
pnpm dev: run the local PTY terminalpnpm dev:web: run the browser terminal serverpnpm build: compile TypeScript todist/pnpm prod: run the compiled local PTY terminalpnpm prod:web: run the compiled browser terminal server
pnpm buildRun the compiled apps:
pnpm prod
pnpm prod:webIf you see this error on macOS when opening the browser terminal:
Error: posix_spawnp failed.
The node-pty macOS spawn-helper binary may not have executable permission after installation with pnpm.
Check the permission:
ls -l node_modules/.pnpm/node-pty@*/node_modules/node-pty/prebuilds/darwin-*/spawn-helperIf the output shows permissions like this:
-rw-r--r--
fix it with:
chmod +x node_modules/.pnpm/node-pty@*/node_modules/node-pty/prebuilds/darwin-*/spawn-helperThen start the browser terminal again:
pnpm dev:websrc/index.ts: local terminal entry pointsrc/server.ts: HTTP and WebSocket server for the browser terminalsrc/terminal.ts: sharednode-ptyshell helperspublic/index.html: xterm.js browser terminal UI.env.example: server configuration exampledist/: compiled JavaScript output
This project exposes an interactive shell. Keep the server bound to 127.0.0.1 for local development unless you understand the risk. If you bind to a network interface, set TERMINAL_TOKEN and avoid exposing the server to untrusted networks.