import { EmptyResultSchema } from '@modelcontextprotocol/sdk/types.js';
import { HeartbeatMonitor } from '../../mcp-server/transports/heartbeat.js';
import { startHttpTransport } from '../../mcp-server/transports/http/httpServer.js';
import { startStdioTransport, stopStdioTransport, } from '../../mcp-server/transports/stdio/stdioTransport.js';
import { requestContextService } from '../../utils/internal/requestContext.js';
export class TransportManager {
    config;
    logger;
    createMcpServer;
    taskManager;
    manifest;
    serverInstance = null;
    shutdown = null;
    heartbeat = null;
    constructor(config, logger, createMcpServer, taskManager, manifest) {
        this.config = config;
        this.logger = logger;
        this.createMcpServer = createMcpServer;
        this.taskManager = taskManager;
        this.manifest = manifest;
    }
    async start() {
        const context = requestContextService.createRequestContext({
            operation: 'TransportManager.start',
            transport: this.config.mcpTransportType,
        });
        this.logger.info(`Starting transport: ${this.config.mcpTransportType}`, context);
        if (this.config.mcpTransportType === 'http') {
            // HTTP: pass factory so each request gets a fresh McpServer+transport pair
            // (SDK 1.26.0 security fix — GHSA-345p-7cg4-v4c7)
            const handle = await startHttpTransport(this.createMcpServer, context, this.manifest);
            this.serverInstance = handle.server;
            this.shutdown = (ctx) => handle.stop(ctx);
        }
        else if (this.config.mcpTransportType === 'stdio') {
            // Stdio: single client, single connection — one server instance is correct
            const mcpServer = await this.createMcpServer();
            this.serverInstance = await startStdioTransport(mcpServer, context);
            // Start heartbeat for stdio transport — periodically pings the client
            // to detect dead connections (orphaned child processes, crashed hosts).
            if (this.config.mcpHeartbeatIntervalMs > 0) {
                const timeoutMs = Math.min(this.config.mcpHeartbeatIntervalMs, 10_000);
                this.heartbeat = new HeartbeatMonitor({
                    intervalMs: this.config.mcpHeartbeatIntervalMs,
                    missThreshold: this.config.mcpHeartbeatMissThreshold,
                    sendPing: () => mcpServer.server.request({ method: 'ping' }, EmptyResultSchema, {
                        timeout: timeoutMs,
                    }),
                    onDead: () => void this.stop('heartbeat_timeout'),
                    transport: 'stdio',
                }, context);
                this.heartbeat.start();
            }
            this.shutdown = async (ctx) => {
                this.heartbeat?.stop();
                this.heartbeat = null;
                await stopStdioTransport(mcpServer, ctx);
            };
        }
        else {
            const transportType = String(this.config.mcpTransportType);
            const error = new Error(`Unsupported transport type: ${transportType}`);
            this.logger.crit(error.message, context);
            throw error;
        }
    }
    async stop(signal) {
        const context = requestContextService.createRequestContext({
            operation: 'TransportManager.stop',
            signal,
        });
        if (!this.shutdown) {
            this.logger.warning('Stop called but no active server instance found.', context);
            return;
        }
        await this.shutdown(context);
        // Clean up task manager timers to allow clean process exit
        this.taskManager.cleanup();
        this.serverInstance = null;
        this.shutdown = null;
    }
    getServer() {
        return this.serverInstance;
    }
}
//# sourceMappingURL=manager.js.map