UNPKG

vibetunnel

Version:

Terminal sharing server with web interface - supports macOS, Linux, and headless environments

551 lines 80.6 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.StreamWatcher = void 0; const chalk_1 = __importDefault(require("chalk")); const fs = __importStar(require("fs")); const logger_js_1 = require("../utils/logger.js"); const pruning_detector_js_1 = require("../utils/pruning-detector.js"); const git_watcher_js_1 = require("./git-watcher.js"); const logger = (0, logger_js_1.createLogger)('stream-watcher'); // Constants const HEADER_READ_BUFFER_SIZE = 4096; // Type guard functions function isOutputEvent(event) { return (Array.isArray(event) && event.length === 3 && event[1] === 'o' && typeof event[0] === 'number'); } function isResizeEvent(event) { return (Array.isArray(event) && event.length === 3 && event[1] === 'r' && typeof event[0] === 'number'); } function isExitEvent(event) { return Array.isArray(event) && event[0] === 'exit'; } class StreamWatcher { constructor(sessionManager) { this.activeWatchers = new Map(); this.sessionManager = sessionManager; // Clean up notification listeners on exit process.on('beforeExit', () => { this.cleanup(); }); logger.debug('stream watcher initialized'); } /** * Process a clear sequence event and update tracking variables */ processClearSequence(event, eventIndex, fileOffset, currentResize, eventLine) { const prunePoint = (0, pruning_detector_js_1.findLastPrunePoint)(event[2]); if (!prunePoint) return null; // Calculate precise offset using shared utility const lastClearOffset = (0, pruning_detector_js_1.calculatePruningPositionInFile)(fileOffset, eventLine, prunePoint.position); // Use shared logging function (0, pruning_detector_js_1.logPruningDetection)(prunePoint.sequence, lastClearOffset, '(retroactive scan)'); logger.debug(`found at event index ${eventIndex}, ` + `current resize: ${currentResize ? currentResize[2] : 'none'}`); return { lastClearIndex: eventIndex, lastClearOffset, lastResizeBeforeClear: currentResize, }; } /** * Parse a line of asciinema data and return the parsed event */ parseAsciinemaLine(line) { if (!line.trim()) return null; try { const parsed = JSON.parse(line); // Check if it's a header if (parsed.version && parsed.width && parsed.height) { return parsed; } // Check if it's an event if (Array.isArray(parsed)) { if (parsed[0] === 'exit') { return parsed; } else if (parsed.length >= 3 && typeof parsed[0] === 'number') { return parsed; } } return null; } catch (e) { logger.debug(`skipping invalid JSON line: ${e}`); return null; } } /** * Send an event to the client with proper formatting */ sendEventToClient(client, event, makeInstant = false) { try { let dataToSend = event; // For existing content, set timestamp to 0 if (makeInstant && Array.isArray(event) && event.length >= 3 && typeof event[0] === 'number') { dataToSend = [0, event[1], event[2]]; } client.response.write(`data: ${JSON.stringify(dataToSend)}\n\n`); // Handle exit events if (Array.isArray(event) && isExitEvent(event)) { logger.log(chalk_1.default.yellow(`session ${client.response.locals?.sessionId || 'unknown'} already ended, closing stream`)); client.response.end(); } } catch (error) { logger.debug(`client write failed (likely disconnected): ${error instanceof Error ? error.message : String(error)}`); } } /** * Add a client to watch a stream file */ addClient(sessionId, streamPath, response) { logger.debug(`adding client to session ${sessionId}`); const startTime = Date.now() / 1000; const client = { response, startTime }; let watcherInfo = this.activeWatchers.get(sessionId); if (!watcherInfo) { // Create new watcher for this session logger.log(chalk_1.default.green(`creating new stream watcher for session ${sessionId}`)); watcherInfo = { clients: new Set(), lastOffset: 0, lastSize: 0, lastMtime: 0, lineBuffer: '', }; this.activeWatchers.set(sessionId, watcherInfo); // Send existing content first this.sendExistingContent(sessionId, streamPath, client); // Get current file size and stats if (fs.existsSync(streamPath)) { const stats = fs.statSync(streamPath); watcherInfo.lastOffset = stats.size; watcherInfo.lastSize = stats.size; watcherInfo.lastMtime = stats.mtimeMs; logger.debug(`initial file size: ${stats.size} bytes`); } else { logger.debug(`stream file does not exist yet: ${streamPath}`); } // Start watching for new content this.startWatching(sessionId, streamPath, watcherInfo); // Start git watching if this is a git repository this.startGitWatching(sessionId, response); } else { // Send existing content to new client this.sendExistingContent(sessionId, streamPath, client); // Add this client to git watcher git_watcher_js_1.gitWatcher.addClient(sessionId, response); } // Add client to set watcherInfo.clients.add(client); logger.log(chalk_1.default.blue(`client connected to stream ${sessionId} (${watcherInfo.clients.size} total)`)); } /** * Remove a client */ removeClient(sessionId, response) { const watcherInfo = this.activeWatchers.get(sessionId); if (!watcherInfo) { logger.debug(`no watcher found for session ${sessionId}`); return; } // Find and remove client let clientToRemove; for (const client of watcherInfo.clients) { if (client.response === response) { clientToRemove = client; break; } } if (clientToRemove) { watcherInfo.clients.delete(clientToRemove); logger.log(chalk_1.default.yellow(`client disconnected from stream ${sessionId} (${watcherInfo.clients.size} remaining)`)); // Remove client from git watcher git_watcher_js_1.gitWatcher.removeClient(sessionId, response); // If no more clients, stop watching if (watcherInfo.clients.size === 0) { logger.log(chalk_1.default.yellow(`stopping watcher for session ${sessionId} (no clients)`)); if (watcherInfo.watcher) { watcherInfo.watcher.close(); } this.activeWatchers.delete(sessionId); // Stop git watching when no clients remain git_watcher_js_1.gitWatcher.stopWatching(sessionId); } } } /** * Send existing content to a client */ sendExistingContent(sessionId, streamPath, client) { try { // Load existing session info or use defaults, but don't save incomplete session data const sessionInfo = this.sessionManager.loadSessionInfo(sessionId); // Validate offset to ensure we don't read beyond file size let startOffset = sessionInfo?.lastClearOffset ?? 0; if (fs.existsSync(streamPath)) { const stats = fs.statSync(streamPath); startOffset = Math.min(startOffset, stats.size); } // Read header line separately (first line of file) // We need to track byte position separately from string length due to UTF-8 encoding let header = null; let fd = null; try { fd = fs.openSync(streamPath, 'r'); const buf = Buffer.alloc(HEADER_READ_BUFFER_SIZE); let data = ''; // Important: Use filePosition (bytes) not data.length (characters) for fs.readSync // UTF-8 strings have character count != byte count for multi-byte characters let filePosition = 0; // Track actual byte position in file let bytesRead = fs.readSync(fd, buf, 0, buf.length, filePosition); while (!data.includes('\n') && bytesRead > 0) { data += buf.toString('utf8', 0, bytesRead); // Increment by actual bytes read, not string characters // This ensures correct file positioning for subsequent reads filePosition += bytesRead; if (!data.includes('\n')) { // Use filePosition (byte offset) not data.length (character count) bytesRead = fs.readSync(fd, buf, 0, buf.length, filePosition); } } const idx = data.indexOf('\n'); if (idx !== -1) { header = JSON.parse(data.slice(0, idx)); } } catch (e) { logger.debug(`failed to read asciinema header for session ${sessionId}: ${e}`); } finally { // Ensure file descriptor is always closed to prevent leaks // This executes even if an exception occurs during read operations if (fd !== null) { try { fs.closeSync(fd); } catch (closeError) { logger.debug(`failed to close file descriptor: ${closeError}`); } } } // Analyze the stream starting from stored offset to find the most recent clear sequence // This allows us to prune old terminal content and only send what's currently visible const analysisStream = fs.createReadStream(streamPath, { encoding: 'utf8', start: startOffset, }); let lineBuffer = ''; const events = []; let lastClearIndex = -1; let lastResizeBeforeClear = null; let currentResize = null; // Track byte offset in the file for accurate position tracking // This is crucial for UTF-8 encoded files where character count != byte count let fileOffset = startOffset; let lastClearOffset = startOffset; analysisStream.on('data', (chunk) => { lineBuffer += chunk.toString(); let index = lineBuffer.indexOf('\n'); while (index !== -1) { const line = lineBuffer.slice(0, index); lineBuffer = lineBuffer.slice(index + 1); // Calculate byte length of the line plus newline character // Buffer.byteLength correctly handles multi-byte UTF-8 characters fileOffset += Buffer.byteLength(line, 'utf8') + 1; if (line.trim()) { try { const parsed = JSON.parse(line); if (parsed.version && parsed.width && parsed.height) { header = parsed; } else if (Array.isArray(parsed)) { // Check if it's an exit event first if (parsed[0] === 'exit') { events.push(parsed); } else if (parsed.length >= 3 && typeof parsed[0] === 'number') { const event = parsed; // Track resize events if (isResizeEvent(event)) { currentResize = event; } // Check for clear sequence in output events if (isOutputEvent(event) && (0, pruning_detector_js_1.containsPruningSequence)(event[2])) { const clearResult = this.processClearSequence(event, events.length, fileOffset, currentResize, line); if (clearResult) { lastClearIndex = clearResult.lastClearIndex; lastClearOffset = clearResult.lastClearOffset; lastResizeBeforeClear = clearResult.lastResizeBeforeClear; } } events.push(event); } } } catch (e) { logger.debug(`skipping invalid JSON line during analysis: ${e}`); } } index = lineBuffer.indexOf('\n'); } }); analysisStream.on('end', () => { // Process any remaining line in analysis if (lineBuffer.trim()) { try { const parsed = JSON.parse(lineBuffer); fileOffset += Buffer.byteLength(lineBuffer, 'utf8'); if (Array.isArray(parsed)) { if (parsed[0] === 'exit') { events.push(parsed); } else if (parsed.length >= 3 && typeof parsed[0] === 'number') { const event = parsed; if (isResizeEvent(event)) { currentResize = event; } if (isOutputEvent(event) && (0, pruning_detector_js_1.containsPruningSequence)(event[2])) { const clearResult = this.processClearSequence(event, events.length, fileOffset, currentResize, lineBuffer); if (clearResult) { lastClearIndex = clearResult.lastClearIndex; lastClearOffset = clearResult.lastClearOffset; lastResizeBeforeClear = clearResult.lastResizeBeforeClear; } } events.push(event); } } } catch (e) { logger.debug(`skipping invalid JSON in line buffer during analysis: ${e}`); } } // Now replay the stream with pruning let startIndex = 0; if (lastClearIndex >= 0) { // Start from after the last clear startIndex = lastClearIndex + 1; logger.log(chalk_1.default.green(`pruning stream: skipping ${lastClearIndex + 1} events before last clear at offset ${lastClearOffset}`)); // Persist new clear offset to session only if session already exists if (sessionInfo) { sessionInfo.lastClearOffset = lastClearOffset; this.sessionManager.saveSessionInfo(sessionId, sessionInfo); } } // Send header first - update dimensions if we have a resize if (header) { const headerToSend = { ...header }; if (lastClearIndex >= 0 && lastResizeBeforeClear) { // Update header with last known dimensions before clear const dimensions = lastResizeBeforeClear[2].split('x'); headerToSend.width = Number.parseInt(dimensions[0], 10); headerToSend.height = Number.parseInt(dimensions[1], 10); } client.response.write(`data: ${JSON.stringify(headerToSend)}\n\n`); } // Send remaining events let exitEventFound = false; for (let i = startIndex; i < events.length; i++) { const event = events[i]; if (isExitEvent(event)) { exitEventFound = true; client.response.write(`data: ${JSON.stringify(event)}\n\n`); } else if (isOutputEvent(event) || isResizeEvent(event)) { // Set timestamp to 0 for existing content const instantEvent = [0, event[1], event[2]]; client.response.write(`data: ${JSON.stringify(instantEvent)}\n\n`); } } // If exit event found, close connection if (exitEventFound) { logger.log(chalk_1.default.yellow(`session ${client.response.locals?.sessionId || 'unknown'} already ended, closing stream`)); client.response.end(); } }); analysisStream.on('error', (error) => { logger.error('failed to analyze stream for pruning:', error); // If stream fails, client will simply not receive existing content // This is extremely rare and would indicate a serious filesystem issue }); } catch (error) { logger.error('failed to create read stream:', error); } } /** * Start watching a file for changes */ startWatching(sessionId, streamPath, watcherInfo) { logger.log(chalk_1.default.green(`started watching stream file for session ${sessionId}`)); // Use standard fs.watch with stat checking watcherInfo.watcher = fs.watch(streamPath, { persistent: true }, (eventType) => { if (eventType === 'change') { try { // Check if file actually changed by comparing stats const stats = fs.statSync(streamPath); // Only process if size increased (append-only file) if (stats.size > watcherInfo.lastSize || stats.mtimeMs > watcherInfo.lastMtime) { const sizeDiff = stats.size - watcherInfo.lastSize; if (sizeDiff > 0) { logger.debug(`file grew by ${sizeDiff} bytes`); } watcherInfo.lastSize = stats.size; watcherInfo.lastMtime = stats.mtimeMs; // Read only new data if (stats.size > watcherInfo.lastOffset) { const fd = fs.openSync(streamPath, 'r'); const buffer = Buffer.alloc(stats.size - watcherInfo.lastOffset); fs.readSync(fd, buffer, 0, buffer.length, watcherInfo.lastOffset); fs.closeSync(fd); // Update offset watcherInfo.lastOffset = stats.size; // Process new data const newData = buffer.toString('utf8'); watcherInfo.lineBuffer += newData; // Process complete lines const lines = watcherInfo.lineBuffer.split('\n'); watcherInfo.lineBuffer = lines.pop() || ''; for (const line of lines) { if (line.trim()) { this.broadcastLine(sessionId, line, watcherInfo); } } } } } catch (error) { logger.error('failed to read file changes:', error); } } }); watcherInfo.watcher.on('error', (error) => { logger.error(`file watcher error for session ${sessionId}:`, error); }); } /** * Broadcast a line to all clients */ broadcastLine(sessionId, line, watcherInfo) { const parsed = this.parseAsciinemaLine(line); if (!parsed) { // Handle non-JSON as raw output logger.debug(`broadcasting raw output line: ${line.substring(0, 50)}...`); const currentTime = Date.now() / 1000; for (const client of watcherInfo.clients) { const castEvent = [currentTime - client.startTime, 'o', line]; this.sendEventToClient(client, castEvent); } return; } // Skip duplicate headers if (!Array.isArray(parsed)) { return; } // Handle exit events if (isExitEvent(parsed)) { logger.log(chalk_1.default.yellow(`session ${sessionId} ended with exit code ${parsed[1]}`)); for (const client of watcherInfo.clients) { this.sendEventToClient(client, parsed); } return; } // Log resize broadcasts at debug level only if (isResizeEvent(parsed)) { logger.debug(`Broadcasting resize ${parsed[2]} to ${watcherInfo.clients.size} clients`); } // Calculate relative timestamp for each client const currentTime = Date.now() / 1000; for (const client of watcherInfo.clients) { const relativeEvent = [currentTime - client.startTime, parsed[1], parsed[2]]; try { client.response.write(`data: ${JSON.stringify(relativeEvent)}\n\n`); if (client.response.flush) client.response.flush(); } catch (error) { logger.debug(`client write failed (likely disconnected): ${error instanceof Error ? error.message : String(error)}`); } } } /** * Start git watching for a session if it's in a git repository */ async startGitWatching(sessionId, response) { try { const sessionInfo = this.sessionManager.loadSessionInfo(sessionId); if (sessionInfo?.gitRepoPath && sessionInfo.workingDir) { logger.debug(`Starting git watcher for session ${sessionId} at ${sessionInfo.gitRepoPath}`); await git_watcher_js_1.gitWatcher.startWatching(sessionId, sessionInfo.workingDir, sessionInfo.gitRepoPath); git_watcher_js_1.gitWatcher.addClient(sessionId, response); } } catch (error) { logger.error(`Failed to start git watching for session ${sessionId}:`, error); } } /** * Clean up all watchers and listeners */ cleanup() { const watcherCount = this.activeWatchers.size; if (watcherCount > 0) { logger.log(chalk_1.default.yellow(`cleaning up ${watcherCount} active watchers`)); for (const [sessionId, watcherInfo] of this.activeWatchers) { if (watcherInfo.watcher) { watcherInfo.watcher.close(); } logger.debug(`closed watcher for session ${sessionId}`); } this.activeWatchers.clear(); } // Clean up git watchers git_watcher_js_1.gitWatcher.cleanup(); } } exports.StreamWatcher = StreamWatcher; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RyZWFtLXdhdGNoZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvc2VydmVyL3NlcnZpY2VzL3N0cmVhbS13YXRjaGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLGtEQUEwQjtBQUUxQix1Q0FBeUI7QUFHekIsa0RBQWtEO0FBQ2xELHNFQUtzQztBQUN0QyxxREFBOEM7QUFFOUMsTUFBTSxNQUFNLEdBQUcsSUFBQSx3QkFBWSxFQUFDLGdCQUFnQixDQUFDLENBQUM7QUFFOUMsWUFBWTtBQUNaLE1BQU0sdUJBQXVCLEdBQUcsSUFBSSxDQUFDO0FBa0JyQyx1QkFBdUI7QUFDdkIsU0FBUyxhQUFhLENBQUMsS0FBcUI7SUFDMUMsT0FBTyxDQUNMLEtBQUssQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLElBQUksS0FBSyxDQUFDLE1BQU0sS0FBSyxDQUFDLElBQUksS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLEdBQUcsSUFBSSxPQUFPLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxRQUFRLENBQy9GLENBQUM7QUFDSixDQUFDO0FBRUQsU0FBUyxhQUFhLENBQUMsS0FBcUI7SUFDMUMsT0FBTyxDQUNMLEtBQUssQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLElBQUksS0FBSyxDQUFDLE1BQU0sS0FBSyxDQUFDLElBQUksS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLEdBQUcsSUFBSSxPQUFPLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxRQUFRLENBQy9GLENBQUM7QUFDSixDQUFDO0FBRUQsU0FBUyxXQUFXLENBQUMsS0FBcUI7SUFDeEMsT0FBTyxLQUFLLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxJQUFJLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxNQUFNLENBQUM7QUFDckQsQ0FBQztBQVdELE1BQWEsYUFBYTtJQUl4QixZQUFZLGNBQThC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O09BRUc7SUFDSyxPQUFPO1FBQ2IsTUFBTSxZQUFZLEdBQUcsSUFBSSxDQUFDLGNBQWMsQ0FBQyxJQUFJLENBQUM7UUFDOUMsSUFBSSxZQUFZLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDckIsTUFBTSxDQUFDLEdBQUcsQ0FBQyxlQUFLLENBQUMsTUFBTSxDQUFDLGVBQWUsWUFBWSxrQkFBa0IsQ0FBQyxDQUFDLENBQUM7WUFDeEUsS0FBSyxNQUFNLENBQUMsU0FBUyxFQUFFLFdBQVcsQ0FBQyxJQUFJLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQztnQkFDM0QsSUFBSSxXQUFXLENBQUMsT0FBTyxFQUFFLENBQUM7b0JBQ3hCLFdBQVcsQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLENBQUM7Z0JBQzlCLENBQUM7Z0JBQ0QsTUFBTSxDQUFDLEtBQUssQ0FBQyw4QkFBOEIsU0FBUyxFQUFFLENBQUMsQ0FBQztZQUMxRCxDQUFDO1lBQ0QsSUFBSSxDQUFDLGNBQWMsQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUM5QixDQUFDO1FBQ0Qsd0JBQXdCO1FBQ3hCLDJCQUFVLENBQUMsT0FBTyxFQUFFLENBQUM7SUFDdkIsQ0FBQztDQUNGO0FBdmxCRCxzQ0F1bEJDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGNoYWxrIGZyb20gJ2NoYWxrJztcbmltcG9ydCB0eXBlIHsgUmVzcG9uc2UgfSBmcm9tICdleHByZXNzJztcbmltcG9ydCAqIGFzIGZzIGZyb20gJ2ZzJztcbmltcG9ydCB0eXBlIHsgU2Vzc2lvbk1hbmFnZXIgfSBmcm9tICcuLi9wdHkvc2Vzc2lvbi1tYW5hZ2VyLmpzJztcbmltcG9ydCB0eXBlIHsgQXNjaWluZW1hSGVhZGVyIH0gZnJvbSAnLi4vcHR5L3R5cGVzLmpzJztcbmltcG9ydCB7IGNyZWF0ZUxvZ2dlciB9IGZyb20gJy4uL3V0aWxzL2xvZ2dlci5qcyc7XG5pbXBvcnQge1xuICBjYWxjdWxhdGVQcnVuaW5nUG9zaXRpb25JbkZpbGUsXG4gIGNvbnRhaW5zUHJ1bmluZ1NlcXVlbmNlLFxuICBmaW5kTGFzdFBydW5lUG9pbnQsXG4gIGxvZ1BydW5pbmdEZXRlY3Rpb24sXG59I