HotPlex TypeScript Client
May 22, 2026 ยท View on GitHub
TypeScript/Node.js client SDK for HotPlex Gateway
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
wsandeventemitter3) - ๐งช 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
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
url | string | โ | - | Gateway WebSocket URL (e.g., ws://localhost:8888) |
workerType | WorkerType | โ | - | Worker type (CLAUDE_CODE, OPENCODE_SERVER, etc.) |
authToken | string | โ | - | API key for deferred browser auth |
sessionId | string | โ | auto | Resume existing session |
reconnect | boolean | โ | true | Enable auto-reconnection |
reconnectMaxAttempts | number | โ | 5 | Max reconnection attempts |
timeout | number | โ | 30000 | Connection timeout (ms) |
metadata | Record<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
| Event | Data Type | Description |
|---|---|---|
message.start | MessageStartData | Emitted when a new message stream starts |
message.delta | MessageDeltaData | Streaming content chunks (most common) |
message.end | MessageEndData | Emitted when message stream ends |
Lifecycle Events
| Event | Data Type | Description |
|---|---|---|
state | StateData | Session state changed (running, idle, etc.) |
done | DoneData | Task completed with success status and stats |
error | ErrorData | Protocol-level error occurred |
Connection Events
| Event | Data Type | Description |
|---|---|---|
connected | InitAckData | WebSocket connected and session initialized |
disconnected | string | WebSocket disconnected with reason |
reconnecting | number | Attempting 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(), andsendInputAsync(). These are usually terminal or require immediate action. errorevents: 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.endhandler - 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 APIenvelope.ts: AEP message codectypes.ts: TypeScript definitionsconstants.ts: Protocol constants
Comparison with Python Client
| Feature | TypeScript | Python |
|---|---|---|
| Async Model | async/await | async/await |
| Event System | EventEmitter3 | Decorator callbacks |
| Type System | interface + generic | dataclass + TypeVar |
| Reconnect | Auto (exponential backoff) | Manual |
| Testing | Vitest | pytest |
| Package Size | ~50KB | ~30KB |
Examples
See examples/ directory:
quickstart.ts: Minimal examplecomplete.ts: Full-featured demo
Related
- 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