@phnx-labs/term-cli
v0.1.0
Published
Standalone PTY engine for coding agents — real pseudoterminals over a unix-socket sidecar
Readme
term
Standalone PTY server and client. Real pseudoterminals over a unix-socket
sidecar — drive REPLs, TUIs, and anything else that needs a real terminal,
from a script or from an agent. Extracted from agents-cli's agents pty
(PHNX-4091); the CLI is term, published as @phnx-labs/term-cli.
Install
npm i -g @phnx-labs/term-cliRequires Node >= 22.12.0 — dist/term.cjs require()s the ESM-only
commander and chalk packages, which needs Node's stable
require(esm) support (unflagged from 22.12.0 / 20.19.0).
First run
$ SID=$(term start --json | node -e "process.stdin.on('data',d=>process.stdout.write(JSON.parse(d).id))")
$ term exec "$SID" "echo hello-from-term" --json
{"ok":true,"submitted":true}
$ term screen "$SID"
hello-from-term
user@host:~$
$ term list --json
[{"id":"5f452f16","pid":4124410,"shell":"bash","cwd":"/home/user", ...}]
$ term stop "$SID"start returns a session ID. exec sends a command and returns immediately
(non-blocking) — poll screen to see clean rendered text (no ANSI codes) or
read for the raw PTY stream. write sends literal keystrokes, with
\n/\t/\e/\xHH escape support by default (--raw to send bytes
verbatim, e.g. real control characters already in the string). stop ends
the session. Every command takes --json for machine-readable output; run
term --help or term <command> --help for the full flag reference and more
examples.
The server/sidecar model
term is a thin CLI in front of a background server: the first command
auto-starts it, and every later command in this session — from any process —
talks to the same server over a unix socket. You do not need to manage it
directly (term server start|stop|status if you want to).
$TERM_HOME(default~/.term) holds the socket, the server's pid file, and its log — override it to isolate sessions (tests do this with a temp dir per run).$TERM_BINoverrides whichtermbinary a consumer's client library shells out to (used by tests, and by anything pinning a specific install).- The socket directory is created
0700and the socket itself0600— only the owning user can read or write it. term server stopkills the server and reaps every session's PTY child with it; nothing is left running in the background.
Remote use
term works the same way over SSH — nothing about it is local-only:
ssh myhost term start --json
ssh myhost term exec "$SID" "some-command"This is how agents-cli drives an interactive device-code login flow on a
remote fleet device: it SSHes in and drives term exactly as it would
locally.
Relationship to agents-cli
agents-cli used to own this PTY engine directly as agents pty. As of
PHNX-4091, that command is gone: agents-cli's OAuth device-code driver
(agents fleet login, agents auth mint) now spawns the standalone term
binary from PATH (or $TERM_BIN) instead. Anything that used to shell out to
agents pty should install @phnx-labs/term-cli and use term — the verbs,
flags, and JSON shapes are unchanged.
Platform support
Ships as a single obfuscated, minified JavaScript bundle that runs under real
Node — not a compiled native binary. The underlying PTY binding
(@homebridge/node-pty-prebuilt-multiarch) and the headless terminal emulator
used to render screen (@xterm/headless) are ordinary npm runtime
dependencies; their own prebuilt native-addon resolution runs at npm
install time, per platform, same as any other npm package that ships a
native addon. Tested on Linux and macOS; Windows follows the same code path
(node-pty supports it) but is not part of this project's CI matrix yet.
This is a deliberate deviation from a bun build --compile --bytecode
native-binary build: on Linux/arm64, node-pty's native addon loads fine
under Bun's own runtime, but pty.spawn()'s forkpty()'d child exits
immediately (exitCode 0, signal SIGHUP) before any data flows — the addon
loads without erroring but does not actually function under Bun. Real Node
runs it correctly. So the sidecar server, and the CLI itself, always run
under Node.
Troubleshooting
Cannot find module '.../pty.node'(or a startup message pointing at@homebridge/node-pty-prebuilt-multiarch) — the native addon's prebuilt binary is missing for your platform/Node ABI. Runnpm rebuild @homebridge/node-pty-prebuilt-multiarch, or reinstall on a supported Node LTS. See docs/command-reference.md for the actionable message the server prints in this case.ERR_REQUIRE_ESM— your Node is older than 22.12.0; see Install.term server failed to start within 5 seconds— check~/.term/logs.jsonl(or$TERM_HOME/logs.jsonl) for the server's own startup error, printed in the same message.- Session says
exited— the shell or command inside it exited;term screen <id>still shows the last rendered output,term stop <id>cleans it up.
License
FSL-1.1-Apache-2.0. See LICENSE.
