docs / client

Client

Framework-agnostic TypeScript WebSocket client with auto-reconnect and session persistence. Use this in any JavaScript environment — no React required.

client/src/client.ts

Install

terminal
npm install @walkie-talkie/client

Quick Start

connect.ts
import { WalkieTalkieClient } from '@walkie-talkie/client';

const client = new WalkieTalkieClient();

// Listen for state changes
client.onStateChange((state) => {
  console.log('Connection:', state);
  // 'connecting' | 'authenticating' | 'connected' | 'reconnecting' | ...
});

// Listen for messages
client.onMessage((msg) => {
  if (msg.type === 'terminal:output') {
    process.stdout.write(msg.data);
  }
  if (msg.type === 'terminal:created') {
    console.log('Terminal ready:', msg.terminal.id);
  }
});

// Connect with a token
client.connect('http://localhost:3456', 'abcd-ef01-2345-6789');

Connection States

type ConnectionState =
  | 'disconnected'   // Not connected
  | 'connecting'     // WebSocket opening
  | 'authenticating' // WS open, waiting for auth response
  | 'connected'      // Authenticated and ready
  | 'reconnecting'   // Auto-reconnecting after disconnect
  | 'error';         // Auth failed or fatal error

State transitions are emitted via onStateChange(). The client auto-reconnects on unexpected disconnects using exponential backoff (1s to 30s max).

API

WalkieTalkieClient

MethodDescription
connect(serverUrl, token)Connect and authenticate with a one-time token
resumeSession(serverUrl, sessionId)Reconnect using an existing session ID
disconnect()Close connection (no auto-reconnect)
send(msg)Send a ClientMessage
onMessage(handler)Subscribe to server messages. Returns unsubscribe function.
onStateChange(handler)Subscribe to connection state changes. Returns unsubscribe function.
getSessionId()Current session ID or null
getServerUrl()Current server URL
getState()Current ConnectionState
isResumingtrue during session resume (vs. fresh connect)

Sending Messages

Use send() with typed protocol messages:

usage.ts
// Create a terminal
client.send({
  type: 'terminal:create',
  cols: 120,
  rows: 40,
});

// Send input (e.g. a command)
client.send({
  type: 'terminal:input',
  terminalId: 'uuid-of-terminal',
  data: 'ls -la\n',
});

// Resize
client.send({
  type: 'terminal:resize',
  terminalId: 'uuid-of-terminal',
  cols: 160,
  rows: 50,
});

// Kill a terminal
client.send({
  type: 'terminal:kill',
  terminalId: 'uuid-of-terminal',
});

// List all terminals in the session
client.send({ type: 'terminal:list' });

Session Resume

After a successful auth, the server returns a sessionId. Save it and use it to reconnect later — no new token needed:

resume.ts
// First connection — save the session
client.onStateChange((state) => {
  if (state === 'connected') {
    const sessionId = client.getSessionId();
    localStorage.setItem('wt-session', sessionId);
    localStorage.setItem('wt-server', client.getServerUrl());
  }
});

// Later — resume
const sessionId = localStorage.getItem('wt-session');
const serverUrl = localStorage.getItem('wt-server');
if (sessionId && serverUrl) {
  client.resumeSession(serverUrl, sessionId);
}

On resume, the server replays the terminal list and scrollback buffer, so your terminals appear exactly where you left off.

Auto-Reconnect

If the WebSocket disconnects unexpectedly (network drop, server restart), the client automatically reconnects using exponential backoff:

Call disconnect() to stop reconnection attempts.

Storage Utilities

client/src/storage.ts

The client package also exports localStorage helpers for managing saved connections:

storage.ts
import {
  getSavedConnections,
  saveConnection,
  removeConnection,
  clearConnections,
  loadState,
  saveState,
} from '@walkie-talkie/client';

// Get recent connections (max 10)
const connections = getSavedConnections();
// → [{ serverUrl, sessionId, connectedAt }]

// Save a connection
saveConnection({
  serverUrl: 'http://localhost:3456',
  sessionId: 'uuid-here',
  connectedAt: Date.now(),
});

// Generic state persistence
saveState('my-key', { zoom: 1.5 });
const state = loadState('my-key', { zoom: 1 });