standalone-toolbar-service
Version:
独立的Stagewise工具栏服务 - 支持SRPC通信和WebSocket广播,可与MCP反馈收集器集成
143 lines (142 loc) • 5.41 kB
JavaScript
import { WebSocketServer } from 'ws';
export class SRPCWebSocketBridge {
constructor(server) {
this.connections = new Map();
this.methods = {};
this.wss = new WebSocketServer({ server });
this.wss.on('connection', (ws) => {
const connectionId = `conn_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
console.log(`[SRPC] WebSocket client connected: ${connectionId}`);
this.connections.set(connectionId, ws);
this.setupWebSocketHandlers(ws, connectionId);
ws.on('close', () => {
console.log(`[SRPC] WebSocket client disconnected: ${connectionId}`);
this.connections.delete(connectionId);
});
});
}
register(methodHandlers) {
Object.entries(methodHandlers).forEach(([methodName, handler]) => {
this.methods[methodName] = { handler };
console.log(`[SRPC] Registered RPC method: ${methodName}`);
});
}
setupWebSocketHandlers(ws, connectionId) {
ws.on('message', (data) => {
try {
const message = JSON.parse(data.toString());
console.log(`[SRPC] Received message from ${connectionId}:`, message);
this.handleMessage(message, ws, connectionId);
}
catch (error) {
console.error(`[SRPC] Error handling WebSocket message from ${connectionId}:`, error);
}
});
ws.on('error', (error) => {
console.error(`[SRPC] WebSocket error for ${connectionId}:`, error);
});
}
handleMessage(message, ws, connectionId) {
const { messageType } = message;
switch (messageType) {
case 'request':
this.handleRequest(message, ws, connectionId);
break;
case 'response':
console.log(`[SRPC] Received response from ${connectionId}:`, message);
break;
case 'update':
console.log(`[SRPC] Received update from ${connectionId}:`, message);
break;
case 'error':
console.error(`[SRPC] Received error from ${connectionId}:`, message);
break;
default:
console.warn(`[SRPC] Unknown message type from ${connectionId}: ${messageType}`);
}
}
async handleRequest(message, ws, connectionId) {
const { id, method, payload } = message;
if (!method) {
this.sendError(id, 'Method name is required', ws, connectionId);
return;
}
const methodDef = this.methods[method];
if (!methodDef) {
this.sendError(id, `Method not found: ${method}`, ws, connectionId);
return;
}
try {
console.log(`[SRPC] Calling method: ${method} with payload from ${connectionId}:`, payload);
const sendUpdate = (update) => {
this.sendUpdate(id, method, update, ws, connectionId);
};
const result = await methodDef.handler(payload, sendUpdate);
this.sendResponse(id, method, result, ws, connectionId);
}
catch (error) {
this.sendError(id, error instanceof Error ? error.message : String(error), ws, connectionId);
}
}
sendResponse(id, method, payload, ws, connectionId) {
if (!ws || ws.readyState !== ws.OPEN) {
throw new Error(`WebSocket connection ${connectionId} is not open`);
}
const responseMessage = {
id,
messageType: 'response',
method,
payload,
};
console.log(`[SRPC] Sending response to ${connectionId}:`, responseMessage);
ws.send(JSON.stringify(responseMessage));
}
sendUpdate(id, method, payload, ws, connectionId) {
if (!ws || ws.readyState !== ws.OPEN) {
throw new Error(`WebSocket connection ${connectionId} is not open`);
}
const updateMessage = {
id,
messageType: 'update',
method,
payload,
};
console.log(`[SRPC] Sending update to ${connectionId}:`, updateMessage);
ws.send(JSON.stringify(updateMessage));
}
sendError(id, errorMessage, ws, connectionId) {
if (!ws || ws.readyState !== ws.OPEN) {
throw new Error(`WebSocket connection ${connectionId} is not open`);
}
const errorResponse = {
id,
messageType: 'error',
error: {
message: errorMessage,
},
};
console.log(`[SRPC] Sending error to ${connectionId}:`, errorResponse);
ws.send(JSON.stringify(errorResponse));
}
isConnected() {
return this.connections.size > 0;
}
getConnectionCount() {
return this.connections.size;
}
getRegisteredMethods() {
return Object.keys(this.methods);
}
close() {
console.log('[SRPC] Closing WebSocket server');
this.wss.close();
this.connections.forEach((ws, connectionId) => {
console.log(`[SRPC] Closing connection: ${connectionId}`);
ws.close();
});
this.connections.clear();
}
}
export function createSRPCBridge(server) {
return new SRPCWebSocketBridge(server);
}