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

autotel-terminal

v17.0.8

Published

Terminal dashboard for autotel traces using react-ink

Readme

autotel-terminal

Terminal trace viewer for autotel : Ink (React-for-CLI) powered dashboard for live trace inspection during development. Zero setup, trace-first, autotel-only.

npm version License: MIT

Overview

autotel-terminal is a terminal-native trace viewer for autotel, built with Ink (React for CLIs). It runs in the same Node.js process as your app, wiring directly to the Autotel tracer provider. It streams OpenTelemetry spans from your app and shows them as traces: recent traces list, span tree per trace, search, and a simple waterfall. Built for local development: no browser, no extra backends.

If you need a standalone, multi-signal TUI that receives OTLP/Zipkin/Prometheus/Datadog traffic over the network (traces, metrics, logs), see tools like otel-tui. autotel-terminal stays Autotel-native and process-local: you call renderTerminal() from your Node app and get an Ink dashboard for that process.

Features

  • Trace-first UI : Recent traces list; open a trace to see its span tree (parent/child)
  • Real-time streaming : Spans appear as they complete; optional pause/resume
  • Search : Filter by span name (/); combine with error-only filter (e)
  • Span details : Key attributes first (e.g. http.route, db.operation); full list + waterfall for selected trace
  • Relative time & errors : "2s ago" labels; error badge and new-error indicator
  • Help overlay : ? shows all shortcuts
  • Simple setup : Add StreamingSpanProcessor to autotel and call renderTerminal()

Installation

npm install autotel-terminal autotel
# or
pnpm add autotel-terminal autotel
# or
yarn add autotel-terminal autotel

Standalone CLI (second terminal)

You can run the dashboard as a standalone OTLP receiver that accepts traces, logs, and metrics:

npx autotel-terminal

Then run your app in another terminal with OTLP JSON pointed at the dashboard:

OTEL_EXPORTER_OTLP_PROTOCOL=http/json \
OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4319 \
node app.js

Port 4319 is used by default to avoid clashing with the standard OTLP port (4318).

CLI Options

-p, --port <port>    Port to listen on (default: 4319, env: AUTOTEL_TERMINAL_PORT)
-H, --host <host>    Host to bind to (default: 127.0.0.1, env: AUTOTEL_TERMINAL_HOST)
-t, --title <title>  Dashboard title (env: AUTOTEL_TERMINAL_TITLE)
-h, --help           Show help message
-v, --version        Show version number

OTLP Endpoints

| Endpoint | Signal | Description | |----------|--------|-------------| | POST /v1/traces | Traces | Receives OTLP JSON spans, streamed into the TUI | | POST /v1/logs | Logs | Receives OTLP JSON logs, shown in the Logs view (l) | | POST /v1/metrics | Metrics | Accepts OTLP JSON metrics (acknowledged and counted) | | GET /healthz | — | Health check |

Quick Start

Recommended Usage

Create a StreamingSpanProcessor and pass it to init(), then use renderTerminal() with the stream:

import { init, trace } from 'autotel';
import {
  renderTerminal,
  StreamingSpanProcessor,
  createTerminalSpanStream,
} from 'autotel-terminal';

// Create streaming processor for the terminal dashboard
const streamingProcessor = new StreamingSpanProcessor(null);

// Initialize autotel with the streaming processor
init({
  service: 'my-app',
  endpoint: 'http://localhost:4318',
  spanProcessors: [streamingProcessor],
});

// Create the stream and launch the dashboard
const terminalStream = createTerminalSpanStream(streamingProcessor);
renderTerminal({ title: 'My App Traces' }, terminalStream);

// Your traced code will now appear in the dashboard
const myFunction = trace((ctx) => async () => {
  ctx.setAttribute('example', 'value');
  // ... your code
});

await myFunction();

With Backend Export

To stream to the terminal AND export to a backend (e.g., Jaeger, OTLP collector):

import { init } from 'autotel';
import {
  StreamingSpanProcessor,
  createTerminalSpanStream,
  renderTerminal,
} from 'autotel-terminal';
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';

// Create exporter and base processor for your backend
const exporter = new OTLPTraceExporter({ url: 'http://localhost:4318/v1/traces' });
const batchProcessor = new BatchSpanProcessor(exporter);

// Create streaming processor that wraps your batch processor
// Spans are forwarded to both the dashboard AND the backend
const streamingProcessor = new StreamingSpanProcessor(batchProcessor);

// Initialize autotel
init({
  service: 'my-app',
  spanProcessors: [streamingProcessor],
});

// Create stream and launch dashboard
const stream = createTerminalSpanStream(streamingProcessor);
renderTerminal({ title: 'My App Traces' }, stream);

With Custom Options

renderTerminal(
  {
    title: 'My App Traces',
    showStats: true,
    maxSpans: 200,
    colors: true, // Auto-detected if TTY
  },
  stream,
);

Dashboard Controls

Once the dashboard is running, use these keyboard controls:

  • ↑/↓ - Navigate traces, spans, or logs
  • Enter - Open selected trace (trace view) to see span tree
  • Esc - Back to trace list or exit search
  • t - Toggle trace view / span list
  • l - Toggle logs view (when a log stream is hooked up)
  • v - Toggle service summary view
  • E - Toggle errors view
  • S - Filter spans to selected service (from service.name)
  • R - Filter spans to selected route (from http.route)
  • H - Cycle status filter (all → 2xx → 4xx → 5xx)
  • x - Clear filters
  • J - Export selected trace as JSON (printed to stdout)
  • / - Search by span name or log message (type to filter)
  • p - Pause/resume live updates
  • r - Record snapshot (clears buffers and records until a limit, then pauses)
  • e - Toggle error-only filter
  • c - Clear all spans
  • ? - Show help overlay
  • Ctrl+C - Exit dashboard

Dashboard Features

Trace View (default)

The left panel shows recent traces (grouped by trace ID):

  • Root span name, duration, trace ID (short), relative time ("2s ago")
  • Error badge when any span in the trace failed
  • Enter to open a trace and see its span tree (ASCII parent-child: ├──, └──)
  • Esc to go back to the trace list

Span List (toggle with t)

Flat list of recent spans with:

  • Span name (truncated), duration (color-coded: green < 500ms, yellow > 500ms, red = error)
  • Relative time
  • Selection indicator (cyan )

Search

Press / to filter by span name. Type to narrow; Esc to clear.

Span Details

The right panel shows detailed information for the selected span:

  • Name, status, duration (with "Nx avg" when slower than average for that span name)
  • Trace ID, Span ID, Parent Span ID
  • Span kind (INTERNAL, SERVER, CLIENT, etc.)
  • Key attributes (http.route, db.operation, code.function, etc.) first
  • Remaining attributes
  • Waterfall for the selected trace (when a trace is open): horizontal bars by duration

Statistics Bar

When enabled, shows:

  • Total spans
  • Error count
  • Average duration
  • P95 latency

API Reference

renderTerminal(options?, stream?)

Render the terminal dashboard.

Parameters:

  • options - Optional dashboard configuration
  • stream - The terminal span stream (from createTerminalSpanStream)

Options:

interface TerminalOptions {
  /** Dashboard title (default: 'Autotel Trace Inspector') */
  title?: string;

  /** Show statistics bar (default: true) */
  showStats?: boolean;

  /** Maximum number of spans to display (default: 100) */
  maxSpans?: number;

  /** Enable colors (default: true if TTY) */
  colors?: boolean;
}

Example:

renderTerminal(
  {
    title: 'API Server Traces',
    showStats: true,
    maxSpans: 200,
  },
  stream,
);

StreamingSpanProcessor

A span processor that emits completed spans to subscribers. Can wrap another processor or work standalone.

import { StreamingSpanProcessor } from 'autotel-terminal';
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';

// Standalone (no forwarding)
const streamingProcessor = new StreamingSpanProcessor(null);

// Or wrap an existing processor (spans forwarded to both)
const batchProcessor = new BatchSpanProcessor(exporter);
const streamingProcessor = new StreamingSpanProcessor(batchProcessor);

// Subscribe to spans directly (alternative to using createTerminalSpanStream)
const unsubscribe = streamingProcessor.subscribe((span) => {
  console.log('Span ended:', span.name);
});

// Later, unsubscribe
unsubscribe();

Constructor:

new StreamingSpanProcessor(wrappedProcessor?: SpanProcessor | null)
  • wrappedProcessor - Optional processor to wrap. If provided, spans are forwarded to it. If null, spans are only emitted to subscribers.

Methods:

  • subscribe(callback: (span: ReadableSpan) => void): () => void - Subscribe to span end events. Returns unsubscribe function.
  • forceFlush(): Promise<void> - Flush wrapped processor (if any).
  • shutdown(): Promise<void> - Shutdown processor and clear subscribers.

createTerminalSpanStream(processor)

Create a terminal-compatible stream from a StreamingSpanProcessor.

import { createTerminalSpanStream, StreamingSpanProcessor } from 'autotel-terminal';

const processor = new StreamingSpanProcessor(null);
const stream = createTerminalSpanStream(processor);

// Subscribe to span events
stream.onSpanEnd((event) => {
  console.log('Span:', event.name, event.durationMs + 'ms');
});

Returns: TerminalSpanStream with onSpanEnd(callback) method.

Event Format:

interface TerminalSpanEvent {
  name: string;
  spanId: string;
  traceId: string;
  parentSpanId?: string;
  startTime: number;
  endTime: number;
  durationMs: number;
  status: 'OK' | 'ERROR' | 'UNSET';
  kind?: string;
  attributes?: Record<string, unknown>;
}

Use Cases

Development Debugging

Perfect for debugging during development - see traces in real-time without leaving your terminal:

import { init } from 'autotel';
import {
  StreamingSpanProcessor,
  createTerminalSpanStream,
  renderTerminal,
} from 'autotel-terminal';

const streamingProcessor = new StreamingSpanProcessor(null);

init({
  service: 'dev-server',
  spanProcessors: [streamingProcessor],
});

const stream = createTerminalSpanStream(streamingProcessor);
renderTerminal({ title: 'Dev Server Traces' }, stream);

Testing

Use the streaming processor to assert on spans in tests:

import { StreamingSpanProcessor, createTerminalSpanStream } from 'autotel-terminal';
import { init } from 'autotel';

const processor = new StreamingSpanProcessor(null);
const stream = createTerminalSpanStream(processor);

init({
  service: 'test',
  spanProcessors: [processor],
});

// Collect spans
const spans: TerminalSpanEvent[] = [];
stream.onSpanEnd((span) => spans.push(span));

// Run your code
await myFunction();

// Assert on spans
expect(spans).toHaveLength(1);
expect(spans[0].name).toBe('myFunction');

Custom Dashboards

Build your own dashboard using the streaming processor:

import { StreamingSpanProcessor, createTerminalSpanStream } from 'autotel-terminal';

const processor = new StreamingSpanProcessor(null);
const stream = createTerminalSpanStream(processor);

stream.onSpanEnd((event) => {
  // Send to your custom dashboard
  myCustomDashboard.addSpan(event);
});

Integration with autotel

autotel-terminal is built for autotel. It auto-wires to the global tracer provider and expects StreamingSpanProcessor in your spanProcessors array:

import { init } from 'autotel';
import {
  StreamingSpanProcessor,
  createTerminalSpanStream,
  renderTerminal,
} from 'autotel-terminal';

const streamingProcessor = new StreamingSpanProcessor(null);

init({
  service: 'my-app',
  endpoint: 'http://localhost:4318',
  spanProcessors: [streamingProcessor],
});

const stream = createTerminalSpanStream(streamingProcessor);
renderTerminal({}, stream);

Wiring logs into the dashboard

The terminal dashboard can show logs in the Logs view (l) if you emit TerminalLogEvents into the global log stream.

In your app (or adapter), call getTerminalLogStream().emit(...) and include traceId / spanId when possible for correlation:

import { getTerminalLogStream } from 'autotel-terminal';

const logStream = getTerminalLogStream();

logStream.emit({
  time: Date.now(),
  level: 'info',
  message: 'request completed',
  traceId: '...', // optional but recommended
  spanId: '...',  // optional but recommended
  attributes: {
    'service.name': 'my-service',
    'http.route': '/users/:id',
    'http.status_code': 200,
  },
});

This is intentionally Autotel-native: you can wire this from request logger/canonical log lines, producing one coherent log snapshot per request.

Requirements

  • Node.js 22+
  • Terminal with TTY support (for colors and interactivity)
  • autotel package (peer dependency)

Bundle Size

  • Core dashboard: ~25KB minified
  • With dependencies (ink, react): ~150KB total
  • Development tool - not intended for production bundles

Limitations

  • Development only : Not designed for production use
  • TTY required : Colors and interactivity require a terminal
  • Memory : Keeps spans in memory (limited by maxSpans option)
  • Single instance : Only one dashboard can run at a time

Manual verification

When testing the dashboard (e.g. with example-terminal or your app), you can verify:

  • Trace list : Trigger some traced work; recent traces appear with root span name, duration, relative time
  • Trace tree : Select a trace and press Enter; span tree shows with indented children
  • Search : Press /, type a span name; list filters; Esc clears
  • Errors : Press e for error-only filter; traces with failed spans show an error badge
  • Help : Press ? to see all shortcuts
  • Waterfall : With a trace open, the details panel shows a simple duration waterfall
  • Key attributes : In span details, http.route, db.operation, code.function, etc. appear first

Examples

See the autotel examples directory for complete working examples:

  • apps/example-terminal - Terminal dashboard with live trace viewing

License

MIT © Jag Reehal

Links