Skip to main content
Glama
cloudflare-manager.ts3.71 kB
import { spawn, ChildProcess } from 'child_process'; import { ConfigLoader } from '../config/config-loader.js'; export class CloudflareTunnelManager { private tunnelProcess: ChildProcess | null = null; private tunnelUrl: string | null = null; private config = ConfigLoader.getInstance(); async startTunnel(): Promise<string> { const cloudflareConfig = this.config.getConfig().cloudflare; if (!cloudflareConfig.enabled) { throw new Error('Cloudflare tunnel is not enabled in configuration'); } // Allow Cloudflare tunnel in production for debugging // In a real production deployment, use a named tunnel instead return new Promise((resolve, reject) => { const port = this.config.getConfig().server.port; const command = cloudflareConfig.tunnelCommand.replace('8787', port.toString()); console.log(`[TUNNEL] Starting Cloudflare tunnel: ${command}`); // Parse command into command and args const [cmd, ...args] = command.split(' '); this.tunnelProcess = spawn(cmd, args, { stdio: ['ignore', 'pipe', 'pipe'] }); let urlDetected = false; // Listen to stdout for the tunnel URL this.tunnelProcess.stdout?.on('data', (data) => { const output = data.toString(); if (this.config.isDebugMode()) { console.log(`[TUNNEL:STDOUT] ${output}`); } // Look for the tunnel URL pattern const urlMatch = output.match(/https:\/\/[a-z-]+\.trycloudflare\.com/); if (urlMatch && !urlDetected) { urlDetected = true; this.tunnelUrl = urlMatch[0]; console.log(`[TUNNEL] Detected tunnel URL: ${this.tunnelUrl}`); // Update configuration with the new URL this.config.updateTunnelUrl(this.tunnelUrl!); resolve(this.tunnelUrl!); } }); // Listen to stderr for errors and additional info this.tunnelProcess.stderr?.on('data', (data) => { const output = data.toString(); if (this.config.isDebugMode()) { console.log(`[TUNNEL:STDERR] ${output}`); } // Also check stderr for URL (cloudflared sometimes outputs there) if (!urlDetected) { const urlMatch = output.match(/https:\/\/[a-z-]+\.trycloudflare\.com/); if (urlMatch) { urlDetected = true; this.tunnelUrl = urlMatch[0]; console.log(`[TUNNEL] Detected tunnel URL: ${this.tunnelUrl}`); // Update configuration with the new URL this.config.updateTunnelUrl(this.tunnelUrl!); resolve(this.tunnelUrl!); } } }); this.tunnelProcess.on('error', (error) => { console.error(`[TUNNEL] Failed to start tunnel: ${error.message}`); reject(error); }); this.tunnelProcess.on('exit', (code) => { console.log(`[TUNNEL] Process exited with code ${code}`); this.tunnelProcess = null; this.tunnelUrl = null; }); // Timeout if URL not detected within 30 seconds setTimeout(() => { if (!urlDetected) { reject(new Error('Timeout: Could not detect tunnel URL within 30 seconds')); this.stopTunnel(); } }, 30000); }); } stopTunnel(): void { if (this.tunnelProcess) { console.log('[TUNNEL] Stopping Cloudflare tunnel...'); this.tunnelProcess.kill(); this.tunnelProcess = null; this.tunnelUrl = null; } } getTunnelUrl(): string | null { return this.tunnelUrl; } isRunning(): boolean { return this.tunnelProcess !== null; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/daviddraiumbrella/invoice-monitoring'

If you have feedback or need assistance with the MCP directory API, please join our Discord server