blax
Version:
Blax - HMS-Powered Multi-Agent Platform with Government Agency Analysis, Deep Research, and Enterprise-Ready Deployment. No local LLM keys required.
214 lines • 7.28 kB
JavaScript
import WebSocket from 'ws';
import { EventEmitter } from 'events';
import { eventBus } from '../../core/eventBus.js';
import { blaxConfig } from '../../config/blax.js';
export class HubAdapter extends EventEmitter {
ws;
config;
connected = false;
reconnectAttempts = 0;
maxReconnectAttempts = 5;
heartbeatTimer;
constructor(config) {
super();
this.config = {
url: blaxConfig.api.hubUrl,
reconnect: true,
heartbeatInterval: 30000,
...config
};
}
async connect() {
if (this.connected) {
return;
}
return new Promise((resolve, reject) => {
try {
const headers = {};
if (this.config.auth) {
if (this.config.auth.type === 'jwt') {
headers['Authorization'] = `Bearer ${this.config.auth.token}`;
}
else if (this.config.auth.type === 'apikey') {
headers['X-API-Key'] = this.config.auth.token;
}
}
this.ws = new WebSocket(this.config.url, { headers });
this.ws.on('open', () => {
this.connected = true;
this.reconnectAttempts = 0;
this.startHeartbeat();
this.emit('connected');
eventBus.publish({
type: 'hub.connected',
timestamp: Date.now(),
source: 'tlnt',
data: { url: this.config.url }
});
resolve();
});
this.ws.on('message', (data) => {
try {
const event = JSON.parse(data.toString());
this.handleHubEvent(event);
}
catch (error) {
console.error('Failed to parse hub message:', error);
}
});
this.ws.on('close', (code, reason) => {
this.connected = false;
this.stopHeartbeat();
this.emit('disconnected', { code, reason: reason.toString() });
eventBus.publish({
type: 'hub.disconnected',
timestamp: Date.now(),
source: 'tlnt',
data: { code, reason: reason.toString() }
});
if (this.config.reconnect && this.reconnectAttempts < this.maxReconnectAttempts) {
this.scheduleReconnect();
}
});
this.ws.on('error', (error) => {
this.emit('error', error);
if (!this.connected) {
reject(error);
}
});
setTimeout(() => {
if (!this.connected) {
reject(new Error('Connection timeout'));
}
}, 10000);
}
catch (error) {
reject(error);
}
});
}
disconnect() {
if (this.ws) {
this.ws.close();
this.ws = undefined;
}
this.connected = false;
this.stopHeartbeat();
}
async listAgents() {
return this.sendRequest('agents.list', {});
}
async listDeals() {
return this.sendRequest('deals.list', {});
}
async createDeal(deal) {
return this.sendRequest('deals.create', deal);
}
async submitAction(agentId, action, data) {
return this.sendRequest('agents.action', {
agentId,
action,
data
});
}
async getAgentLogs(agentId, options = {}) {
return this.sendRequest('agents.logs', {
agentId,
...options
});
}
async sendRequest(method, params) {
if (!this.connected || !this.ws) {
throw new Error('Not connected to hub');
}
return new Promise((resolve, reject) => {
const requestId = this.generateRequestId();
const message = {
id: requestId,
method,
params
};
const timeout = setTimeout(() => {
reject(new Error('Request timeout'));
}, 30000);
const handleResponse = (event) => {
try {
const response = JSON.parse(event.data?.toString() || event.toString());
if (response.id === requestId) {
clearTimeout(timeout);
this.ws?.removeEventListener('message', handleResponse);
if (response.error) {
reject(new Error(response.error.message || 'Hub request failed'));
}
else {
resolve(response.result);
}
}
}
catch (error) {
// Ignore parse errors for non-response messages
}
};
this.ws.addEventListener('message', handleResponse);
this.ws.send(JSON.stringify(message));
});
}
handleHubEvent(event) {
this.emit('hubEvent', event);
eventBus.publish(event);
switch (event.type) {
case 'agent.started':
case 'agent.stopped':
case 'agent.error':
this.emit('agentEvent', event);
break;
case 'deal.created':
case 'deal.updated':
case 'deal.completed':
this.emit('dealEvent', event);
break;
case 'metric.recorded':
this.emit('metricEvent', event);
break;
}
}
startHeartbeat() {
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer);
}
this.heartbeatTimer = setInterval(() => {
if (this.connected && this.ws) {
this.ws.ping();
}
}, this.config.heartbeatInterval);
}
stopHeartbeat() {
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer);
this.heartbeatTimer = undefined;
}
}
scheduleReconnect() {
this.reconnectAttempts++;
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
setTimeout(() => {
this.connect().catch(() => {
// Reconnection failed, will try again if attempts remain
});
}, delay);
}
generateRequestId() {
return `req_${Date.now()}_${Math.random().toString(36).substring(2)}`;
}
isConnected() {
return this.connected;
}
getConnectionStatus() {
return {
connected: this.connected,
url: this.config.url,
reconnectAttempts: this.reconnectAttempts
};
}
}
//# sourceMappingURL=hubAdapter.js.map