npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@tty-pt/axil-tty

v1.3.2

Published

AXIL PTY multiplexer module

Readme

axil-tty

C99 BSD-2-Clause PTY terminals

WebSocket-to-PTY bridge module for axil — browser terminal sessions over a login shell.

A dynamic module for axil that adds:

  • WebSocket-to-PTY bridging (browser terminal sessions)
  • Telnet negotiation (NAWS window resize, ECHO, SGA)
  • Browser terminal JS/CSS assets (axil-tty.js, axil-tty.css, demo.js)
  • Login-shell sh handler — spawns a shell over a WebSocket

Contents

Features

  • WebSocket↔PTY bridging — browser terminal sessions wired to a PTY on the server.
  • Telnet negotiation — NAWS window resize, ECHO, and SGA for a proper terminal experience. WILL ECHO is negotiated once per session, and every PTY keeps the line discipline's own ECHO, so the client stays a pipe: the driver echoes each keystroke as it is typed, owns line editing and ^? erase, and turns the guest's \n into the \r\n a terminal needs.
  • Login-shell handler — the first NAWS on a connection spawns a login shell over the WebSocket, via axil_tty_shell/axil_tty_exec. An sh command is also registered, but see the caveat below: over a WebSocket axil never runs cmd_parse, so it is only reachable from a host module's own command pipeline (axil-nd), not by a client typing sh.
  • Browser assets — axil-tty.js, axil-tty.css, and demo.js served from htdocs/ (installed under share/axil/htdocs).

Install

Prebuilt packages are distributed from tty.pt for Linux (APT / Alpine / Arch / Fedora-RHEL), macOS (Homebrew), Windows (winget / MSYS2), and OpenBSD. Follow the [installation instructions] (https://github.com/tty-pt/ci/blob/main/docs/install.md) and use libaxil-tty as the package name. The module installs as libaxil-tty.so in the axil module directory, and the NPM terminal widget is published as @tty-pt/libaxil-tty.

Build from source

The module builds with a plain make (the shared [mk include.mk] (https://github.com/tty-pt/mk) is expected as a sibling directory):

make                  # builds lib/libaxil-tty.so (and the lib/axil-tty.so link)
make test             # run the in-tree test suite (./test.sh)
sudo make install     # module + htdocs → $(PREFIX), default /usr/local

Dependencies: libaxil, libcorm, libxylem packages (from the tty.pt repo) provide the headers and libraries.

Quick start

Start axil with the module loaded:

axil -d -A -p 8080 -m axil-tty
# run from the repo's lib/ so `-m axil-tty` resolves, or pass the installed
# module path, e.g. -m axil-tty once installed, or -m /path/to/lib/axil-tty

If the libaxil-tty package is installed, -m axil-tty resolves from anywhere (the module lives in the axil module directory); the working-directory hint above only matters when running from a source checkout.

Command-line options (inherited from axil)

| Option | Description | |--------|-------------| | -m PATH | Load module from PATH (colon-separated list) | | -A | Auto-authenticate all connections | | -p PORT | HTTP/WS listen port | | -C PATH | Change directory before starting | | -d | Don't detach (run in foreground) |

Open http://localhost:8080/tty for a terminal connected to a login shell. The module serves its page and WebSocket upgrade on /tty; the server root is not claimed by this module.

Browser terminal

Install the NPM package:

npm install @tty-pt/libaxil-tty

JavaScript/TypeScript API:

import { create } from "@tty-pt/libaxil-tty";

// Create terminal instance
const term = create(document.getElementById("terminal"), {
  proto: "ws",        // or "wss" for secure
  port: 4201,
  // "/tty" is the default, matching the route the module serves
  url: `ws://${location.hostname}:${location.port}/tty`,
  // Every key is optional: `sub` is merged over the defaults, so you only
  // override what you need and onOpen/onClose/onMessage/cols/rows keep their
  // default implementations. The returned instance is a copy — mutating it
  // does not write back into this object.
  sub: {
    onOpen: (term, ws) => {
      console.log("Connected to server");
    },
    onClose: () => {
      console.log("Disconnected, reconnecting...");
    },
    onMessage: (ev, arr) => {
      // Return true to continue default processing
      return true;
    },
    cols: 80,
    rows: 25,
  },
  debug: false,
});

See types/axil-tty.d.ts for full TypeScript definitions.

C API

#include <ttypt/axil-tty.h>

// Spawn a login shell on a WebSocket connection
call_axil_tty_shell(fd);

// Spawn a specific command
char *argv[] = { "/bin/bash", NULL };
call_axil_tty_exec(fd, argv);

// Is this connection ours? True only for a client on GET:/tty.
call_axil_tty_owns(fd);

Two modules on one process

axil dispatches on_axil_tick to every loaded module, and axil never routes WebSocket frames through on_axil_parse — frames reach a module only after it calls axil_fd_watch() and pulls them with axil_ws_read(). So if another module (axil-nd) is loaded alongside this one, both ticks would drain the same socket and split its frame stream.

axil_tty_owns(fd) is the arbitration point: axil-tty sets it for connections on its own GET:/tty route, and a host module should skip any fd for which it returns true. axil-nd does exactly this, and forwards the frames it reads to axil_tty_input() so this module's PTY bridge and NAWS handling still apply.

Note that the gate on on_axil_connect is defensive rather than load-bearing: loaded as a DT_NEEDED dependency of another module, axil dispatches on_axil_connect to the primary module only (verified with an instrumented build).

WebSocket upgrade

axil does not auto-upgrade WebSocket requests. Modules must opt in explicitly. axil-tty registers a GET:/tty handler that detects the upgrade and calls axil_ws_upgrade(fd):

static int
handle_tty(socket_t fd, char *body)
{
    char key[ENV_VALUE_LEN] = {0};
    if (axil_env_get(fd, key, "HTTP_SEC_WEBSOCKET_KEY") == 0) {
        axil_ws_upgrade(fd);  /* performs handshake, calls axil_connect() */
        return 0;
    }
    serve_htdocs(fd, "index.html");
    return 0;
}

After axil_ws_upgrade succeeds, axil_connect() fires — axil-tty's on_axil_connect handler sets up the PTY and Telnet negotiation.

Testing

./test.sh             # or: make test

The suite boots axil with the module on a random port and verifies the WebSocket handshake (computed Sec-WebSocket-Accept), the telnet IAC negotiation (DO NAWS, WILL ECHO, WONT SGA), that a partial line comes back echoed before Enter — the line discipline echoing each keystroke as it arrives, which is the whole feature — that PTY output (echo AXIL_TEST) streams back over the socket CRLF-terminated, and that the browser assets are served.

make builds the C module only; the bundle the browser loads is bun run build. Neither installs — sudo make install is what puts the module and htdocs/ under $(PREFIX), and axil -m axil-tty loads that copy, so a rebuilt bundle is not what you are testing until it is installed. For a loop that needs no sudo:

bun run build && AXIL_HTDOCS=./htdocs axil -d -A -p 8080 -m ./lib/axil-tty

Documentation

License

BSD 2-Clause License. Copyright (c) 2026, tty-pt. See LICENSE.