Support multi-instance Gateway: use gatewayWsUrl from request

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
ValueOn AG 2026-02-13 23:00:33 +01:00
parent 859ffc11f2
commit ab970292d9
4 changed files with 15 additions and 8 deletions

View file

@ -133,7 +133,9 @@ export class BotOrchestrator {
* Connect to the Gateway WebSocket for this session.
*/
private async _connectToGateway(): Promise<void> {
const wsUrl = `${this._options.gatewayWsUrl}/${this._options.instanceId}/bot/ws/${this._sessionId}`;
// gatewayWsUrl is the full WebSocket URL provided by the Gateway
// It already includes instanceId and sessionId
const wsUrl = this._options.gatewayWsUrl;
this._logger.info(`Connecting to Gateway: ${wsUrl}`);
return new Promise((resolve, reject) => {

View file

@ -19,8 +19,8 @@ async function main(): Promise<void> {
// Start HTTP server
httpServer = new HttpServer({
onJoinRequest: async (sessionId, meetingUrl, botName, instanceId) => {
await sessionManager.createSession(sessionId, meetingUrl, botName, instanceId);
onJoinRequest: async (sessionId, meetingUrl, botName, instanceId, gatewayWsUrl) => {
await sessionManager.createSession(sessionId, meetingUrl, botName, instanceId, gatewayWsUrl);
},
onLeaveRequest: async (sessionId) => {
await sessionManager.endSession(sessionId);

View file

@ -4,7 +4,7 @@ import { logger } from '../utils/logger';
import { config } from '../config';
export interface HttpServerCallbacks {
onJoinRequest: (sessionId: string, meetingUrl: string, botName?: string, instanceId?: string) => Promise<void>;
onJoinRequest: (sessionId: string, meetingUrl: string, botName?: string, instanceId?: string, gatewayWsUrl?: string) => Promise<void>;
onLeaveRequest: (sessionId: string) => Promise<void>;
onStatusRequest: (sessionId: string) => { state: string; error?: string } | null;
}
@ -77,14 +77,14 @@ export class HttpServer {
// Deploy a new bot
this._app.post('/api/bot', async (req: Request, res: Response) => {
try {
const { sessionId, meetingUrl, botName, instanceId } = req.body;
const { sessionId, meetingUrl, botName, instanceId, gatewayWsUrl } = req.body;
if (!sessionId || !meetingUrl) {
res.status(400).json({ error: 'Missing required fields: sessionId, meetingUrl' });
return;
}
await this._callbacks.onJoinRequest(sessionId, meetingUrl, botName, instanceId);
await this._callbacks.onJoinRequest(sessionId, meetingUrl, botName, instanceId, gatewayWsUrl);
res.json({
success: true,

View file

@ -29,12 +29,14 @@ export class SessionManager {
* @param meetingUrl - Teams meeting URL
* @param botName - Display name for the bot
* @param instanceId - Feature instance ID (for Gateway routing)
* @param gatewayWsUrl - Full WebSocket URL to connect back to Gateway (supports multi-instance)
*/
async createSession(
sessionId: string,
meetingUrl: string,
botName?: string,
instanceId?: string
instanceId?: string,
gatewayWsUrl?: string
): Promise<void> {
if (this._sessions.has(sessionId)) {
logger.warn(`Session ${sessionId} already exists`);
@ -42,6 +44,7 @@ export class SessionManager {
}
logger.info(`Creating session ${sessionId} for meeting: ${meetingUrl}`);
logger.info(`Gateway WebSocket URL: ${gatewayWsUrl || 'not provided, using config'}`);
const callbacks: OrchestratorCallbacks = {
onStateChange: (state, message) => {
@ -56,8 +59,10 @@ export class SessionManager {
};
// Options for Gateway connection
// Use the gatewayWsUrl from the request if provided (supports multi-instance gateways)
// Otherwise fall back to config (for local development)
const options: OrchestratorOptions = {
gatewayWsUrl: config.gatewayWsUrl,
gatewayWsUrl: gatewayWsUrl || config.gatewayWsUrl,
instanceId: instanceId || 'default',
};