HotPlex TypeScript Client

May 22, 2026 ยท View on GitHub

TypeScript/Node.js client SDK for HotPlex Gateway

npm version License


Features

  • ๐Ÿš€ Full AEP v1 Support - Complete implementation of Agent Exchange Protocol
  • ๐Ÿ”„ Auto-Reconnection - Exponential backoff with configurable retry limits
  • ๐Ÿ“ก Event-Driven API - Clean EventEmitter-based event handling
  • ๐ŸŽฏ Type-Safe - Full TypeScript type definitions
  • ๐Ÿ”ง Zero Dependencies - Minimal deps (only ws and eventemitter3)
  • ๐Ÿงช Well-Tested - Comprehensive unit and integration tests

Installation

npm

npm install @hotplex/client

yarn

yarn add @hotplex/client

From Source

git clone https://github.com/hrygo/hotplex.git
cd hotplex/examples/typescript-client
npm install
npm run build

Quick Start

Minimal Example

import { HotPlexClient, WorkerType } from "@hotplex/client";

const client = new HotPlexClient({
  url: "ws://localhost:8888",
  workerType: WorkerType.CLAUDE_CODE,
  authToken: process.env.HOTPLEX_API_KEY,
});

// Handle streaming output
client.on("message_delta", (data) => {
  process.stdout.write(data.content);
});

// Handle completion
client.on("done", (data) => {
  console.log(`\nโœ… Done! Success: ${data.success}`);
  client.close();
});

// Connect and send
(async () => {
  try {
    await client.connect();
    await client.sendInput("Write a hello world in TypeScript");
  } catch (err) {
    console.error("Error:", err);
    process.exit(1);
  }
})();

Run Example

# Terminal 1: Start gateway
./hotplex

# Terminal 2: Run example
cd examples/typescript-client
npm install
export HOTPLEX_API_KEY="your-api-key"
npx tsx examples/quickstart.ts

API Reference

Constructor

new HotPlexClient(config: ClientConfig)

ClientConfig

OptionTypeRequiredDefaultDescription
urlstringโœ…-Gateway WebSocket URL (e.g., ws://localhost:8888)
workerTypeWorkerTypeโœ…-Worker type (CLAUDE_CODE, OPENCODE_SERVER, etc.)
authTokenstringโŒ-API key for deferred browser auth
sessionIdstringโŒautoResume existing session
reconnectbooleanโŒtrueEnable auto-reconnection
reconnectMaxAttemptsnumberโŒ5Max reconnection attempts
timeoutnumberโŒ30000Connection timeout (ms)
metadataRecord<string, any>โŒ{}Session metadata

Methods

connect()

Establishes WebSocket connection and initializes session.

await client.connect(): Promise<InitAckData>

Returns: InitAckData

{
  sessionId: string;
  status: "ok";
}

sendInputAsync()

Send user input and wait for the task to complete (or fail).

await client.sendInputAsync(content: string, metadata?: Record<string, any>): Promise<void>

Example:

try {
  await client.sendInputAsync("Write a hello world in Go");
  console.log("Task finished successfully");
} catch (err) {
  if (err instanceof TimeoutError) {
    console.error("Task timed out");
  } else {
    console.error("Task failed:", err.message);
  }
}

sendToolResult()

Send tool execution result.

await client.sendToolResult(id: string, output: unknown, error?: string): Promise<void>

Example:

await client.sendToolResult("call_123", JSON.stringify({ files: ["main.go"] }));

sendPermissionResponse()

Send permission approval/denial.

await client.sendPermissionResponse(permissionId: string, allowed: boolean, reason?: string): Promise<void>

Example:

await client.sendPermissionResponse("perm_456", true, "User approved");

close()

Close connection and cleanup resources.

client.close();

Events

All events use EventEmitter3.

Message Events

EventData TypeDescription
message.startMessageStartDataEmitted when a new message stream starts
message.deltaMessageDeltaDataStreaming content chunks (most common)
message.endMessageEndDataEmitted when message stream ends

Lifecycle Events

EventData TypeDescription
stateStateDataSession state changed (running, idle, etc.)
doneDoneDataTask completed with success status and stats
errorErrorDataProtocol-level error occurred

Connection Events

EventData TypeDescription
connectedInitAckDataWebSocket connected and session initialized
disconnectedstringWebSocket disconnected with reason
reconnectingnumberAttempting to reconnect (current attempt)

Advanced Usage

Session Resumption

Resume an existing session within its retention period.

const client = new HotPlexClient({
  url: "ws://localhost:8888",
  workerType: WorkerType.ClaudeCode,
});

// Connect to existing session
await client.resume("sess_existing_uuid");

Streaming Message Collection

Collect streaming deltas into a full message:

let fullMessage = "";

client.on("message.delta", (data) => {
  fullMessage += data.content;
});

client.on("message.end", () => {
  console.log("Full Message:", fullMessage);
});

Tool Implementation

Handle tool calls from the worker:

client.on("tool_call", async (data) => {
  console.log(`Tool call: ${data.name}`);
  const result = await myToolRunner(data.name, data.input);
  
  await client.sendToolResult(data.id, result);
});

Error Handling

Custom Error Classes

The client provides several error classes for different failure modes:

import {
  HotPlexError,
  ConnectionError,
  SessionError,
  TimeoutError,
  ProtocolError,
} from "@hotplex/client";

try {
  await client.connect();
} catch (err) {
  if (err instanceof ConnectionError) {
    console.error("Network issue:", err.message);
  } else if (err instanceof SessionError) {
    console.error("Gateway rejected session:", err.code);
  }
}

Error Events vs Exceptions

  • Exceptions: Thrown by connect(), resume(), and sendInputAsync(). These are usually terminal or require immediate action.
  • error events: Emitted for asynchronous protocol errors that don't necessarily break the connection.
client.on("error", (data) => {
  console.error(`Protocol error [${data.code}]: ${data.message}`);
});

Testing

Run Tests

npm test                 # Unit tests
npm run test:coverage    # Coverage report
npm run test:integration # Integration tests (requires gateway)

Test Utilities

import { createTestClient, waitForEvent } from "@hotplex/client/testing";

describe("MyClient", () => {
  it("should handle messages", async () => {
    const client = createTestClient();

    await client.connect();
    await client.sendInput("test");

    const done = await waitForEvent(client, "done", 5000);
    expect(done.success).toBe(true);
  });
});

Performance

Memory Management

The client automatically manages memory:

  • Clears message buffers after message.end
  • Limits pending message queue (configurable)
  • Cleans up event listeners on close

Backpressure

When the server is overloaded, it may drop message_delta events. The client:

  • Continues processing (no exceptions)
  • Can detect gaps in message.end handler
  • Should implement retry logic if needed

Connection Pooling

For multiple sessions, create separate client instances:

const clients = await Promise.all([
  new HotPlexClient(config).connect(),
  new HotPlexClient(config).connect(),
  new HotPlexClient(config).connect(),
]);

// Use clients in parallel
await Promise.all(
  clients.map(c => c.sendInput("Task..."))
);

Troubleshooting

Connection Refused

Error: Connection refused ws://localhost:8888

Solution: Check if gateway is running:

curl http://localhost:9999/admin/health

No Events Received

Symptoms: Connected but no message_delta events

Debug:

client.on("state", (data) => {
  console.log("State:", data.state);
});

client.on("error", (data) => {
  console.error("Error:", data);
});

Authentication Failed

Error [UNAUTHORIZED]: Invalid API key

Solution: Verify authToken matches your gateway API key:

const client = new HotPlexClient({
  authToken: process.env.HOTPLEX_API_KEY, // Ensure this is set
});

TypeScript Errors

Property 'sendInput' does not exist on type 'HotPlexClient'

Solution: Ensure correct import:

import { HotPlexClient } from "@hotplex/client";
// not
// import { Client } from "@hotplex/client";

Development

Build

npm run build        # Compile TypeScript
npm run build:watch  # Watch mode

Lint

npm run lint         # Check issues
npm run lint:fix     # Auto-fix

Generate Docs

npm run docs         # Generate API docs

Architecture

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚         HotPlexClient                   โ”‚
โ”‚  - Event registration (on/off/emit)     โ”‚
โ”‚  - Message builders (sendInput, etc)    โ”‚
โ”‚  - State management                     โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚         Transport (WebSocket)           โ”‚
โ”‚  - Connection lifecycle                 โ”‚
โ”‚  - Auto-reconnect with backoff          โ”‚
โ”‚  - Message queue                        โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚         Protocol (AEP v1)               โ”‚
โ”‚  - NDJSON codec                         โ”‚
โ”‚  - Envelope builder                     โ”‚
โ”‚  - Event type definitions               โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Source Files:

  • client.ts: High-level client API
  • envelope.ts: AEP message codec
  • types.ts: TypeScript definitions
  • constants.ts: Protocol constants

Comparison with Python Client

FeatureTypeScriptPython
Async Modelasync/awaitasync/await
Event SystemEventEmitter3Decorator callbacks
Type Systeminterface + genericdataclass + TypeVar
ReconnectAuto (exponential backoff)Manual
TestingVitestpytest
Package Size~50KB~30KB

Examples

See examples/ directory:


  • Protocol Spec: docs/architecture/AEP-v1-Protocol.md
  • Python Client: examples/python-client/
  • Go Client: client/
  • Java Client: examples/java-client/

License

Apache-2.0


Support