Skip to main content
Glama
tunnels.ts3.05 kB
import fs from "fs"; import os from "os"; import { log, debugLog, tunnelStoragePath } from "./logger.js"; import { TunnelInfo } from "./types.js"; import { closeSpecificTunnel } from "./processes.js"; // Store active tunnels to manage them later export const activeTunnels = new Map<string, TunnelInfo>(); /** * Function to save tunnels to a file */ export const saveTunnels = (): void => { try { // Convert Map to a serializable object const tunnelsData = Array.from(activeTunnels.entries()).map( ([name, info]) => { // Create a serializable version of the tunnel info return { name, url: info.url, publicUrl: info.publicUrl, created: info.created.toISOString(), pid: info.pid, hostId: os.hostname(), // Add hostname to identify the host }; }, ); fs.writeFileSync(tunnelStoragePath, JSON.stringify(tunnelsData, null, 2)); log(`Saved ${tunnelsData.length} tunnel(s) to ${tunnelStoragePath}`); debugLog(`Saved tunnels to storage file`); } catch (error) { const err = error as Error; log(`Error saving tunnels to file: ${err.message}`); debugLog(`Error saving tunnels: ${err.message}`); } }; /** * Function to load tunnels from file */ export const loadTunnels = (): void => { try { if (!fs.existsSync(tunnelStoragePath)) { log(`No tunnel storage file found at ${tunnelStoragePath}`); return; } const data = fs.readFileSync(tunnelStoragePath, "utf8"); const tunnelsData = JSON.parse(data); // Process each tunnel tunnelsData.forEach((tunnelData: any) => { // Skip if tunnel is already loaded if (activeTunnels.has(tunnelData.name)) return; // Check if this is a remote tunnel or a local one const isRemoteTunnel = tunnelData.hostId !== os.hostname(); // Create a tunnel object with appropriate properties based on type activeTunnels.set(tunnelData.name, { url: tunnelData.url, publicUrl: tunnelData.publicUrl, created: new Date(tunnelData.created), pid: tunnelData.pid, hostId: tunnelData.hostId, isRemote: isRemoteTunnel, tunnel: { // Remote tunnels cannot be closed from this instance // Local tunnels can be closed via processes close: async () => { if (isRemoteTunnel) { log( `Cannot close remote tunnel "${tunnelData.name}" from this host`, ); return false; } else { log(`Closing local tunnel "${tunnelData.name}"`); return closeSpecificTunnel(tunnelData.url, tunnelData.pid); } }, }, }); }); log(`Loaded ${tunnelsData.length} tunnel(s) from storage file`); debugLog(`Loaded tunnels from storage file`); } catch (error) { const err = error as Error; log(`Error loading tunnels from file: ${err.message}`); debugLog(`Error loading tunnels: ${err.message}`); } };

Implementation Reference

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/minte-app/untun-mcp'

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