UNPKG

navflow-browser-server

Version:

Standalone Playwright browser server for NavFlow - enables browser automation with API key authentication, workspace device management, session sync, LLM discovery tools, and requires Node.js v22+

271 lines 9.51 kB
"use strict"; /** * Tunnel Client - Connects to proxy server and handles HTTP request forwarding */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const ws_1 = __importDefault(require("ws")); const http_1 = __importDefault(require("http")); const events_1 = require("events"); class TunnelClient extends events_1.EventEmitter { constructor(localServerPort = 3002, proxyServerUrl = 'ws://localhost:8080/tunnel') { super(); this.ws = null; this.connected = false; this.tunnelInfo = null; this.pendingRequests = new Map(); this.reconnectAttempts = 0; this.maxReconnectAttempts = 5; this.reconnectDelay = 5000; // 5 seconds this.localServerPort = localServerPort; this.proxyServerUrl = proxyServerUrl; } /** * Connect to the proxy server */ async connect() { return new Promise((resolve, reject) => { console.log('[TunnelClient] Connecting to proxy server:', this.proxyServerUrl); this.ws = new ws_1.default(this.proxyServerUrl); this.ws.on('open', () => { console.log('[TunnelClient] Connected to proxy server'); this.connected = true; this.reconnectAttempts = 0; resolve(); }); this.ws.on('message', (data) => { try { const message = JSON.parse(data.toString()); this.handleMessage(message); } catch (error) { console.error('[TunnelClient] Error parsing message:', error); } }); this.ws.on('close', () => { console.log('[TunnelClient] Disconnected from proxy server'); this.connected = false; this.tunnelInfo = null; this.attemptReconnect(); }); this.ws.on('error', (error) => { console.error('[TunnelClient] WebSocket error:', error); if (!this.connected) { reject(error); } }); // Connection timeout setTimeout(() => { if (!this.connected) { reject(new Error('Connection timeout')); } }, 10000); }); } /** * Handle incoming messages from proxy server */ handleMessage(message) { switch (message.type) { case 'tunnel_registered': this.tunnelInfo = { tunnelId: message.tunnelId, password: message.password }; this.displayTunnelInfo(); break; case 'http_request': this.handleHttpRequest(message); break; case 'ping': // Respond to ping if (this.ws) { this.ws.send(JSON.stringify({ type: 'pong', timestamp: new Date().toISOString() })); } break; case 'pong': // Pong received break; case 'webrtc_answer': case 'webrtc_ice_candidate': // Forward WebRTC signaling to ScreenShareService console.log(`[TunnelClient] Received WebRTC message: ${message.type}`); this.emit('webrtc_message', message); break; case 'start_screen_share': // Handle screen share start request console.log(`[TunnelClient] Received screen share start request for session: ${message.sessionId}`); this.emit('start_screen_share', message); break; default: console.log('[TunnelClient] Unknown message type:', message.type); } } /** * Handle HTTP request from proxy server */ async handleHttpRequest(message) { const { id, method, url, headers, body } = message; try { // Make request to local server const response = await this.makeLocalRequest(method, url, headers, body); // Send response back to proxy if (this.ws) { this.ws.send(JSON.stringify({ id: id, statusCode: response.statusCode, headers: response.headers, body: response.body })); } } catch (error) { console.error('[TunnelClient] Error handling HTTP request:', error); // Send error response if (this.ws) { this.ws.send(JSON.stringify({ id: id, error: error.message, statusCode: 500 })); } } } /** * Make HTTP request to local server */ async makeLocalRequest(method, url, headers, body) { return new Promise((resolve, reject) => { const options = { hostname: 'localhost', port: this.localServerPort, path: url, method: method, headers: headers || {} }; const req = http_1.default.request(options, (res) => { let responseBody = ''; res.on('data', (chunk) => { responseBody += chunk; }); res.on('end', () => { // Try to parse JSON response let parsedBody = responseBody; try { if (responseBody && res.headers['content-type']?.includes('application/json')) { parsedBody = JSON.parse(responseBody); } } catch (e) { // Keep as string if not JSON } resolve({ statusCode: res.statusCode, headers: res.headers, body: parsedBody }); }); }); req.on('error', (error) => { reject(error); }); // Send request body if present if (body) { if (typeof body === 'object') { req.write(JSON.stringify(body)); } else { req.write(body); } } req.end(); }); } /** * Display tunnel information in console */ displayTunnelInfo() { if (!this.tunnelInfo) return; console.log(''); console.log('🌐 ===== TUNNEL CONNECTION ESTABLISHED ====='); console.log(''); console.log(`📍 Local Server: http://localhost:${this.localServerPort}`); console.log(`🆔 Tunnel ID: ${this.tunnelInfo.tunnelId}`); console.log(`🔑 Password: ${this.tunnelInfo.password}`); console.log(''); console.log('🔧 Frontend Configuration:'); console.log(` Tunnel ID: ${this.tunnelInfo.tunnelId}`); console.log(` Password: ${this.tunnelInfo.password}`); console.log(''); console.log('💡 Configure these credentials in your frontend settings'); console.log('==============================================='); console.log(''); } /** * Attempt to reconnect to proxy server */ attemptReconnect() { if (this.reconnectAttempts >= this.maxReconnectAttempts) { console.error('[TunnelClient] Max reconnection attempts reached'); return; } this.reconnectAttempts++; console.log(`[TunnelClient] Attempting to reconnect (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`); setTimeout(() => { this.connect().catch((error) => { console.error('[TunnelClient] Reconnection failed:', error); }); }, this.reconnectDelay); } /** * Send ping to proxy server */ ping() { if (this.ws && this.connected) { this.ws.send(JSON.stringify({ type: 'ping', timestamp: new Date().toISOString() })); } } /** * Get current tunnel information */ getTunnelInfo() { return this.tunnelInfo; } /** * Check if connected to proxy */ isConnected() { return this.connected && this.ws !== null && this.ws.readyState === ws_1.default.OPEN; } /** * Send WebRTC signaling message through tunnel */ sendWebRTCMessage(message) { if (this.ws && this.connected) { this.ws.send(JSON.stringify(message)); } } /** * Get WebSocket instance for direct access */ getWebSocket() { return this.ws; } /** * Disconnect from proxy server */ disconnect() { if (this.ws) { console.log('[TunnelClient] Disconnecting from proxy server'); this.ws.close(); this.ws = null; } this.connected = false; this.tunnelInfo = null; } } exports.default = TunnelClient; //# sourceMappingURL=tunnelClient.js.map