Telnet for Node.js in TypeScript. Wraps a raw socket into a TelnetStream that handles option negotiation, IAC escaping, terminal type, window size, environment variables, binary mode, and compression so you don't have to.
npm install @svene/tellimport { Server } from "@svene/tell";
const server = Server((connection) => {
const [width, height] = connection.windowSize ?? [0, 0];
console.log("terminal:", connection.term ?? "unknown");
console.log("size:", `${width}x${height}`);
connection.write("hello from tell\r\n> ");
connection.on("data", (chunk: Buffer) => {
const input = chunk.toString("utf8").trim();
connection.write(`you said: ${input}\r\n> `);
});
connection.on("interrupt", () => {
connection.end("bye\r\n");
});
});
server.listen(1337);Pass any object matching this shape to Server(handler, { logger }). If you don't, nothing gets logged.
interface Logger {
debug(msg: string): void;
info(msg: string): void;
warn(msg: string): void;
error(msg: string): void;
child(scope: string): Logger;
}pino, winston, consola - wrap them to match the interface and you're done. See examples/arpanet/logger.ts for a pino adapter.
packages/tell/ - the library (zero runtime deps)
examples/arpanet/ - multi-user time-sharing system (pino, figlet, cli-table3, picocolors)
examples/mikrotik-demo/ - MikroTik RouterOS simulator (pino, cli-table3, picocolors)
npm run build # build all workspaces
npm test # run library tests
npm run arpanet # build & run the time-sharing demo
npm run mikrotik # build & run the RouterOS simulatorStart the arpanet time-sharing system:
npm run arpanetThen connect from another terminal:
telnet localhost 2323Login with admin/admin or guest/guest. You get a shell with who, finger, write, wall, ps, df, and others.
login: admin
password:
_ _
| |_____ _____| |__ _ __ ___
| / _ \ V / -_) / _` / _/ -_)
|_\___/\_/\___|_\__,_\__\___|
Thu May 29 2026
type 'help' for available commands
admin@lovelace$ who
admin XTERM-GHOSTTY 91x45 3s ago unknown
admin@lovelace$ hostname
lovelace
admin@lovelace$ logout
goodbye.
Start the MikroTik RouterOS simulator:
npm run mikrotikThis runs an automated sequence (login, /system resource print, /interface print, identity change) then drops you into a live RouterOS-style CLI.
MikroTik 7.15
Login: admin
Password:
MMM MMM KKK TTTTTTTTTTT KKK
MMMM MMMM KKK TTTTTTTTTTT KKK
MMM MMMM MMM III KKK KKK RRRRRR OOOOOO TTT III KKK KKK
MMM MM MMM III KKKKK RRR RR OOO OOO TTT III KKKKK
MMM MMM III KKK KKK RRRRRR OOO OOO TTT III KKK KKK
MMM MMM III KKK KKK RRR RR OOOOOO TTT III KKK KKK
MikroTik RouterOS 7.15 (c) 1999-2024 http://www.mikrotik.com/
[admin@MikroTik] > /system resource print
uptime: 1h51m23s
version: 7.15 (stable)
free-memory: 18337.4MiB
total-memory: 31970.2MiB
cpu: AMD Ryzen 7 5700X 8-Core Processor
cpu-count: 16
[admin@MikroTik] > /ip address print
Flags: X - disabled, D - dynamic
# ADDRESS NETWORK INTERFACE
0 D 127.0.0.1/8 127.0.0.0 lo
1 D 192.168.1.47/24 192.168.1.0 wlp9s0
[admin@MikroTik] > /system identity set name=GatewayRouter
[admin@GatewayRouter] >
Try /ping 8.8.8.8 or /export.
Server(listener, options?)- creates anet.Server, negotiates telnet options, passes a readyTelnetStreamto the listenerTelnetStream- emitsdata,end,close,error,resize,environment,interrupt,suspend,will,wont,do,dont,negotiatedCommands,Options- protocol byte constantsnoopLogger- silent logger (the default)
Early. Not battle-tested, only used in some of my projects. Use at your own risk in anything that matters.