11 KiB
@hyperdx/cli Development Guide
What is @hyperdx/cli?
A terminal CLI for searching, tailing, and inspecting logs and traces from HyperDX. It provides both an interactive TUI (built with Ink — React for terminals) and a non-interactive streaming mode for piping.
The CLI connects to the HyperDX API server and queries ClickHouse directly
through the API's /clickhouse-proxy endpoint, using the same query generation
logic (@hyperdx/common-utils) as the web frontend.
CLI Commands
hdx tui -s <url> # Interactive TUI (main command)
hdx stream -s <url> --source "Logs" # Non-interactive streaming to stdout
hdx sources -s <url> # List available sources
hdx auth login -s <url> # Sign in (interactive or -e/-p flags)
hdx auth status # Show auth status (reads saved session)
hdx auth logout # Clear saved session
The -s, --server <url> flag is required for commands that talk to the API. If
omitted, the CLI falls back to the server URL saved in the session file from a
previous hdx auth login.
Architecture
src/
├── cli.tsx # Entry point — Commander CLI with commands:
│ # tui, stream, sources, auth (login/logout/status)
│ # Also contains the standalone LoginPrompt component
├── App.tsx # App shell — state machine:
│ # loading → login → pick-source → EventViewer
├── api/
│ ├── client.ts # ApiClient (REST + session cookies)
│ │ # ProxyClickhouseClient (routes through /clickhouse-proxy)
│ └── eventQuery.ts # Query builders:
│ # buildEventSearchQuery (table view, uses renderChartConfig)
│ # buildTraceSpansSql (waterfall trace spans)
│ # buildTraceLogsSql (waterfall correlated logs)
│ # buildFullRowSql (SELECT * for row detail)
├── components/
│ ├── AlertsPage.tsx # Alerts overview page — list + detail with recent history (Shift+A)
│ ├── EventViewer.tsx # Main TUI view — table, search, detail panel with tabs
│ ├── TraceWaterfall.tsx # Trace waterfall chart with j/k navigation + event details
│ ├── RowOverview.tsx # Structured overview (top-level attrs, event attrs, resource attrs)
│ ├── ColumnValues.tsx # Shared key-value renderer (used by Column Values tab + Event Details)
│ ├── LoginForm.tsx # Email/password login form (used inside TUI App)
│ └── SourcePicker.tsx # Arrow-key source selector
└── utils/
├── config.ts # Session persistence (~/.config/hyperdx/cli/session.json)
├── editor.ts # $EDITOR integration for time range and select clause editing
└── silenceLogs.ts # Suppresses console.debug/warn/error from common-utils
Key Components
EventViewer (components/EventViewer.tsx)
The main TUI component (~1100 lines). Handles:
- Table view: Dynamic columns derived from query results, percentage-based
widths,
overflowX="hidden"for truncation - Search: Lucene query via
/key, submits on Enter - Follow mode: Slides time range forward every 2s, pauses when detail panel is open, restores on close
- Detail panel: Three tabs (Overview / Column Values / Trace), cycled via
Tab key. Detail search via
/filters content within the active tab. - Select editor:
skey opens$EDITORwith the current SELECT clause. Custom selects are stored per source ID. - State management: ~20
useStatehooks. Key states includeevents,expandedRow,detailTab,isFollowing,customSelectMap,traceSelectedIndex.
TraceWaterfall (components/TraceWaterfall.tsx)
Port of the web frontend's DBTraceWaterfallChart. Key details:
- Tree building: Single-pass DAG builder over time-sorted rows. Direct port
of the web frontend's logic — do NOT modify without checking
DBTraceWaterfallChartfirst. - Correlated logs: Fetches log events via
buildTraceLogsSqland merges them into the span tree (logs attach as children of the span with matching SpanId, usingSpanId-logsuffix to avoid key collisions). - j/k navigation:
selectedIndex+onSelectedIndexChangeprops controlled by EventViewer.effectiveIndexfalls back tohighlightHintwhen no j/k navigation has occurred. - Event Details:
SELECT *fetch for the selected span/log, rendered via the sharedColumnValuescomponent. Uses stable scalar deps (selectedNodeSpanId,selectedNodeTimestamp,selectedNodeKind) to avoid infinite re-fetch loops. - Duration formatting: Dynamic units —
1.2s,3.5ms,45.2μs,123ns. - Highlight:
inverseon label and duration text for the selected row. Bar color unchanged.
RowOverview (components/RowOverview.tsx)
Port of the web frontend's DBRowOverviewPanel. Three sections:
- Top Level Attributes: Standard OTel fields (TraceId, SpanId, SpanName, ServiceName, Duration, StatusCode, etc.)
- Span/Log Attributes: Flattened from
source.eventAttributesExpression, shown with key count header - Resource Attributes: Flattened from
source.resourceAttributesExpression, rendered as chips withbackgroundColor="#3a3a3a"and cyan key / white value
ColumnValues (components/ColumnValues.tsx)
Shared component for rendering key-value pairs from a row data object. Used by:
- Column Values tab in the detail panel
- Event Details section in the Trace tab's waterfall
Supports searchQuery filtering and wrapLines toggle.
Web Frontend Alignment
This package mirrors several web frontend components. Always check the corresponding web component before making changes to ensure behavior stays consistent:
| CLI Component | Web Component | Notes |
|---|---|---|
TraceWaterfall |
DBTraceWaterfallChart |
Tree builder is a direct port |
RowOverview |
DBRowOverviewPanel |
Same sections and field list |
| Trace tab logic | DBTracePanel |
Source resolution (trace/log) |
| Detail panel | DBRowSidePanel |
Tab structure, highlight hint |
| Event query | DBTraceWaterfallChart |
getConfig() → buildTrace*Sql |
Key expression mappings from the web frontend's getConfig():
Timestamp→displayedTimestampValueExpression(NOTtimestampValueExpression)Duration→durationExpression(raw, not seconds like web frontend)Body→bodyExpression(logs) orspanNameExpression(traces)SpanId→spanIdExpressionParentSpanId→parentSpanIdExpression(traces only)
Keybindings (TUI mode)
| Key | Action |
|---|---|
j / ↓ |
Move selection down |
k / ↑ |
Move selection up |
l / Enter |
Expand row detail |
h / Esc |
Close detail / blur search |
G |
Jump to newest |
g |
Jump to oldest |
/ |
Search (global in table, filter in detail) |
Tab |
Cycle sources/searches or detail tabs |
Shift+Tab |
Cycle backwards |
s |
Edit SELECT clause in $EDITOR |
t |
Edit time range in $EDITOR |
f |
Toggle follow mode (live tail) |
w |
Toggle line wrap |
A (Shift+A) |
Open alerts page |
? |
Toggle help screen |
q |
Quit |
In the Trace tab, j/k navigate spans/logs in the waterfall instead of
the main table.
Development
# Run in dev mode (tsx, no compile step)
cd packages/cli
yarn dev tui -s http://localhost:8000
# Type check
npx tsc --noEmit
# Bundle with tsup
yarn build
# Compile standalone binary (current platform)
yarn compile
# Cross-compile
yarn compile:macos # macOS ARM64
yarn compile:macos-x64 # macOS x64
yarn compile:linux # Linux x64
Key Patterns
Session Management
Session is stored at ~/.config/hyperdx/cli/session.json with mode 0o600.
Contains apiUrl and cookies[]. The ApiClient constructor loads the saved
session and checks if the stored apiUrl matches the requested one.
ClickHouse Proxy Client
ProxyClickhouseClient extends BaseClickhouseClient from common-utils. It:
- Routes queries through
/clickhouse-proxy(setspathname) - Injects session cookies for auth
- Passes
x-hyperdx-connection-idheader - Disables basic auth (
set_basic_auth_header: false) - Forces
content-type: text/plainto prevent Express body parser issues
Source Expressions
Sources have many expression fields that map to ClickHouse column names. Key ones used in the CLI:
timestampValueExpression— Primary timestamp (oftenTimestampTime, DateTime)displayedTimestampValueExpression— High-precision timestamp (oftenTimestamp, DateTime64 with nanoseconds). Use this for waterfall queries.traceIdExpression,spanIdExpression,parentSpanIdExpressionbodyExpression,spanNameExpression,serviceNameExpressiondurationExpression+durationPrecision(3=ms, 6=μs, 9=ns)eventAttributesExpression,resourceAttributesExpressionlogSourceId,traceSourceId— Correlated source IDs
useInput Handler Ordering
The useInput callback in EventViewer has a specific priority order. Do not
reorder these checks:
?toggles help (except when search focused)- Any key closes help when showing
focusDetailSearch— consumes all keys except Esc/EnterfocusSearch— consumes all keys except Tab/Esc- Trace tab j/k — when detail panel open and Trace tab active
- General j/k, G/g, Enter/Esc, Tab, etc.
- Single-key shortcuts:
w,f,/,s,t,q
Dynamic Table Columns
When customSelect is set (via s key), columns are derived from the query
result keys. Otherwise, hardcoded percentage-based columns are used per source
kind. The getDynamicColumns function distributes 60% evenly among non-last
columns, with the last column getting the remainder.
Follow Mode
- Enabled by default on startup
- Slides
timeRangeforward every 2s, triggering a replace fetch - Paused when detail panel opens (
wasFollowingRefsaves previous state) - Restored when detail panel closes
Custom Select Per Source
customSelectMap: Record<string, string> stores custom SELECT overrides keyed
by source.id. Each source remembers its own custom select independently.