@graphteon/juricode
Version:
We are forging the future with lines of digital steel
147 lines • 5.5 kB
JavaScript
import { io } from 'socket.io-client';
import OpenHands from './open-hands';
import { getConfig } from '../utils/config';
export class WSClient {
constructor(baseUrl) {
this.baseUrl = baseUrl;
this.socket = null;
this.messageHandlers = [];
this.errorHandlers = [];
this.stateHandlers = [];
this.connectHandlers = [];
this.lastEventId = -1;
const config = getConfig();
this.baseUrl = baseUrl || config.websocketUrl || config.baseUrl;
}
async connect(conversationId) {
if (this.socket) {
this.socket.close();
}
this.lastEventId = -1;
try {
const conversation = await OpenHands.getConversation(conversationId);
if (conversation) {
OpenHands.setCurrentConversation(conversation);
}
}
catch (error) {
console.warn('Could not set current conversation:', error);
}
const headers = OpenHands.getConversationHeaders();
const authHeaders = {};
if (headers.get('X-Session-API-Key')) {
authHeaders['X-Session-API-Key'] = headers.get('X-Session-API-Key');
}
const socketUrl = (this.baseUrl || 'http://localhost:13000').replace(/\/api$/, '');
this.socket = io(socketUrl, {
transports: ['websocket', 'polling'],
query: {
latest_event_id: this.lastEventId,
conversation_id: conversationId
},
extraHeaders: authHeaders,
timeout: 10000,
forceNew: true
});
this.socket.on('connect', () => {
console.log(`✅ WebSocket connected to ${socketUrl}`);
this.connectHandlers.forEach(handler => handler());
});
this.socket.on('oh_event', (event) => {
const eventId = parseInt(event.id);
if (!isNaN(eventId)) {
this.lastEventId = eventId;
}
if (event.type === 'message' ||
(event.action === 'message' && (event.source === 'user' || event.source === 'agent'))) {
const formattedEvent = {
...event,
type: 'message',
message: event.message || event.args?.content || ''
};
this.messageHandlers.forEach(handler => handler(formattedEvent));
}
else if (event.type === 'status' && event.status === 'error') {
const error = new Error(event.message || 'Unknown error');
this.errorHandlers.forEach(handler => handler(error));
}
else if (event.observation === 'agent_state_changed') {
const state = event.extras?.agent_state;
if (state) {
console.log(`🔄 Agent state changed: ${state}`);
this.stateHandlers.forEach(handler => handler(state));
}
}
else if (event.source === 'agent') {
let message = '';
if (event.args?.thought) {
message = event.args.thought;
}
else if (event.action === 'run' && event.args?.command) {
message = `Running command: ${event.args.command}`;
}
else if (event.observation === 'run' && event.content) {
message = event.message;
if (event.content && event.content.trim()) {
message += '\n\n```\n' + event.content + '\n```';
}
}
else if (event.message || event.content || event.args?.content) {
message = event.message || event.content || event.args?.content || '';
}
if (message) {
const formattedEvent = {
...event,
type: 'message',
message
};
this.messageHandlers.forEach(handler => handler(formattedEvent));
}
}
});
this.socket.on('connect_error', (error) => {
console.log(`❌ WebSocket connection error: ${error.message}`);
console.log(` Attempted URL: ${socketUrl}`);
this.errorHandlers.forEach(handler => handler(error));
});
this.socket.on('disconnect', () => {
setTimeout(() => {
if (this.socket) {
this.socket.connect();
}
}, 1000);
});
}
onConnect(handler) {
this.connectHandlers.push(handler);
}
onMessage(handler) {
this.messageHandlers.push(handler);
}
onError(handler) {
this.errorHandlers.push(handler);
}
onStateChange(handler) {
this.stateHandlers.push(handler);
}
send(message) {
if (!this.socket) {
throw new Error('WebSocket is not connected');
}
this.socket.emit('oh_action', {
action: 'message',
args: {
content: message,
image_urls: [],
timestamp: new Date().toISOString()
}
});
}
disconnect() {
if (this.socket) {
this.socket.disconnect();
this.socket = null;
}
}
}
//# sourceMappingURL=ws-client.js.map