From ab970292d99b53af0f043db3e9802bfdfa6bdd3f Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Fri, 13 Feb 2026 23:00:33 +0100
Subject: [PATCH] Support multi-instance Gateway: use gatewayWsUrl from request
Co-authored-by: Cursor
---
src/bot/orchestrator.ts | 4 +++-
src/index.ts | 4 ++--
src/server/httpServer.ts | 6 +++---
src/sessionManager.ts | 9 +++++++--
4 files changed, 15 insertions(+), 8 deletions(-)
diff --git a/src/bot/orchestrator.ts b/src/bot/orchestrator.ts
index ca6c5ed..1dd5c02 100644
--- a/src/bot/orchestrator.ts
+++ b/src/bot/orchestrator.ts
@@ -133,7 +133,9 @@ export class BotOrchestrator {
* Connect to the Gateway WebSocket for this session.
*/
private async _connectToGateway(): Promise {
- 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) => {
diff --git a/src/index.ts b/src/index.ts
index 3852b73..70a81d0 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -19,8 +19,8 @@ async function main(): Promise {
// 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);
diff --git a/src/server/httpServer.ts b/src/server/httpServer.ts
index 776d8ac..8152ed1 100644
--- a/src/server/httpServer.ts
+++ b/src/server/httpServer.ts
@@ -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;
+ onJoinRequest: (sessionId: string, meetingUrl: string, botName?: string, instanceId?: string, gatewayWsUrl?: string) => Promise;
onLeaveRequest: (sessionId: string) => Promise;
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,
diff --git a/src/sessionManager.ts b/src/sessionManager.ts
index 2808504..e888113 100644
--- a/src/sessionManager.ts
+++ b/src/sessionManager.ts
@@ -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 {
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',
};