UNPKG

polyv-live-cli

Version:

CLI tool for managing PolyV live streaming services.

544 lines 19.7 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; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.SystemResourceService = exports.NetworkUtils = void 0; const os = __importStar(require("os")); const fs = __importStar(require("fs")); const events_1 = require("events"); class NetworkUtils { static formatBandwidth(bytesPerSecond) { const units = ['bps', 'Kbps', 'Mbps', 'Gbps', 'Tbps']; let value = bytesPerSecond * 8; let unitIndex = 0; while (value >= 1000 && unitIndex < units.length - 1) { value /= 1000; unitIndex++; } const unit = units[unitIndex] || 'bps'; const formatted = `${value.toFixed(2)} ${unit}`; return { value, unit, formatted }; } static formatBytes(bytes) { const units = ['B', 'KB', 'MB', 'GB', 'TB']; let value = bytes; let unitIndex = 0; while (value >= 1024 && unitIndex < units.length - 1) { value /= 1024; unitIndex++; } const unit = units[unitIndex] || 'B'; const formatted = `${value.toFixed(2)} ${unit}`; return { value, unit, formatted }; } static calculateUtilization(currentRate, maxRate) { if (maxRate === 0) return 0; return Math.min(100, (currentRate / maxRate) * 100); } static getConnectionState(interfaces) { const activeInterfaces = interfaces.filter(iface => iface.isUp && !iface.name.startsWith('lo')); if (activeInterfaces.length === 0) { return 'disconnected'; } const hasErrors = activeInterfaces.some(iface => iface.errors > 0 || iface.dropped > 0); return hasErrors ? 'limited' : 'connected'; } } exports.NetworkUtils = NetworkUtils; class SystemResourceService extends events_1.EventEmitter { constructor() { super(); this.previousCpuUsage = []; this.previousProcessUsage = process.cpuUsage(); this.history = []; this.historyMaxSize = 1000; this.isWindows = os.platform() === 'win32'; this.isLinux = os.platform() === 'linux'; this.isMacOS = os.platform() === 'darwin'; this.previousNetworkStats = new Map(); this.networkRates = new Map(); this.processHistory = []; this.processHistoryMaxSize = 100; this.processStartTime = Date.now(); this.peakMemoryUsage = 0; this.cpuUsageReadings = []; this.initializeBaseline(); } initializeBaseline() { this.previousCpuUsage = os.cpus(); this.previousProcessUsage = process.cpuUsage(); } async getSystemResources() { try { const [cpu, memory, network, processInfo, disk] = await Promise.all([ this.getCpuUsage(), this.getMemoryUsage(), this.getNetworkUsage(), this.getProcessUsage(), this.getDiskUsage(), ]); const resources = { cpu, memory, network, process: processInfo, ...(disk && { disk }), }; this.addToHistory(resources); this.emit('resourceUpdate', resources); return resources; } catch (error) { const errorInstance = error instanceof Error ? error : new Error(String(error)); this.emit('error', errorInstance); throw errorInstance; } } async getCpuUsage() { const cpus = os.cpus(); const currentCpuUsage = cpus; let totalIdle = 0; let totalTick = 0; if (this.previousCpuUsage.length === cpus.length) { for (let i = 0; i < cpus.length; i++) { const cpu = cpus[i]; const prevCpu = this.previousCpuUsage[i]; if (cpu && prevCpu) { const idle = cpu.times.idle - prevCpu.times.idle; const total = Object.values(cpu.times).reduce((a, b) => a + b, 0) - Object.values(prevCpu.times).reduce((a, b) => a + b, 0); totalIdle += idle; totalTick += total; } } } this.previousCpuUsage = currentCpuUsage; const usage = totalTick > 0 ? Math.round((1 - totalIdle / totalTick) * 100) : 0; return { usage: Math.max(0, Math.min(100, usage)), cores: cpus.length, model: cpus[0]?.model || 'Unknown', speed: cpus[0]?.speed || 0, ...(this.isWindows ? {} : { loadAverage: os.loadavg() }), }; } async getMemoryUsage() { const totalMem = os.totalmem(); const freeMem = os.freemem(); const usedMem = totalMem - freeMem; const usage = Math.round((usedMem / totalMem) * 100); return { total: totalMem, used: usedMem, free: freeMem, usage: usedMem, available: freeMem, percentage: usage, }; } async getNetworkUsage() { const interfaces = os.networkInterfaces(); const networkInterfaces = []; let totalBytesIn = 0; let totalBytesOut = 0; let totalRateIn = 0; let totalRateOut = 0; let totalErrors = 0; const now = Date.now(); try { for (const [name, addresses] of Object.entries(interfaces)) { if (!addresses || addresses.length === 0) continue; const activeAddress = addresses.find(addr => !addr.internal && addr.family === 'IPv4'); if (!activeAddress) continue; const networkStats = await this.getNetworkStats(name); if (networkStats) { const previousStats = this.previousNetworkStats.get(name); let rateIn = 0; let rateOut = 0; if (previousStats) { const timeDiff = (now - previousStats.timestamp) / 1000; if (timeDiff > 0) { rateIn = Math.max(0, (networkStats.bytesIn - previousStats.bytesIn) / timeDiff); rateOut = Math.max(0, (networkStats.bytesOut - previousStats.bytesOut) / timeDiff); } } this.previousNetworkStats.set(name, { bytesIn: networkStats.bytesIn, bytesOut: networkStats.bytesOut, timestamp: now, }); this.networkRates.set(name, { rateIn, rateOut }); networkStats.rateIn = rateIn; networkStats.rateOut = rateOut; networkInterfaces.push(networkStats); totalBytesIn += networkStats.bytesIn; totalBytesOut += networkStats.bytesOut; totalRateIn += rateIn; totalRateOut += rateOut; totalErrors += networkStats.errors; } } } catch (error) { } return { interfaces: networkInterfaces, totalBytesIn, totalBytesOut, timestamp: now, connections: await this.getNetworkConnections(), totalRateIn, totalRateOut, activeInterfaces: networkInterfaces.filter(iface => iface.isUp).length, errorRate: totalErrors, connectionState: NetworkUtils.getConnectionState(networkInterfaces), }; } async getNetworkStats(interfaceName) { try { if (this.isLinux) { return await this.getLinuxNetworkStats(interfaceName); } else if (this.isMacOS) { return await this.getMacNetworkStats(interfaceName); } else if (this.isWindows) { return await this.getWindowsNetworkStats(interfaceName); } } catch (error) { return null; } return null; } getNetworkRates() { return new Map(this.networkRates); } getFormattedNetworkStats() { const history = this.getHistory(60000); if (history.length === 0) { return { totalBandwidth: '0 bps', totalBytes: '0 B', connectionState: 'unknown', }; } const latest = history[history.length - 1]; if (!latest) { return { totalBandwidth: '0 bps', totalBytes: '0 B', connectionState: 'unknown', }; } const totalRate = latest.network.bytesIn + latest.network.bytesOut; const totalBytes = latest.network.bytesIn + latest.network.bytesOut; return { totalBandwidth: NetworkUtils.formatBandwidth(totalRate).formatted, totalBytes: NetworkUtils.formatBytes(totalBytes).formatted, connectionState: 'connected', }; } async getLinuxNetworkStats(interfaceName) { try { const statsPath = `/proc/net/dev`; if (!fs.existsSync(statsPath)) return null; const data = fs.readFileSync(statsPath, 'utf8'); const lines = data.split('\n'); for (const line of lines) { if (line.includes(interfaceName)) { const parts = line.trim().split(/\s+/); if (parts.length >= 17) { return { name: interfaceName, isUp: true, bytesIn: parseInt(parts[1] || '0') || 0, bytesOut: parseInt(parts[9] || '0') || 0, packetsIn: parseInt(parts[2] || '0') || 0, packetsOut: parseInt(parts[10] || '0') || 0, errors: (parseInt(parts[3] || '0') || 0) + (parseInt(parts[11] || '0') || 0), dropped: (parseInt(parts[4] || '0') || 0) + (parseInt(parts[12] || '0') || 0), }; } } } } catch (error) { } return null; } async getMacNetworkStats(interfaceName) { try { return { name: interfaceName, isUp: true, bytesIn: 0, bytesOut: 0, packetsIn: 0, packetsOut: 0, errors: 0, dropped: 0, }; } catch (error) { return null; } } async getWindowsNetworkStats(interfaceName) { try { return { name: interfaceName, isUp: true, bytesIn: 0, bytesOut: 0, packetsIn: 0, packetsOut: 0, errors: 0, dropped: 0, }; } catch (error) { return null; } } async getNetworkConnections() { try { if (this.isLinux) { const tcpPath = '/proc/net/tcp'; if (fs.existsSync(tcpPath)) { const data = fs.readFileSync(tcpPath, 'utf8'); return data.split('\n').length - 2; } } return 0; } catch (error) { return 0; } } async getProcessUsage() { const currentUsage = process.cpuUsage(this.previousProcessUsage); const memoryUsage = process.memoryUsage(); const cpuUsage = (currentUsage.user + currentUsage.system) / 1000000; this.previousProcessUsage = process.cpuUsage(); this.peakMemoryUsage = Math.max(this.peakMemoryUsage, memoryUsage.heapUsed); this.cpuUsageReadings.push(cpuUsage); if (this.cpuUsageReadings.length > 50) { this.cpuUsageReadings.shift(); } const avgCpuUsage = this.cpuUsageReadings.reduce((sum, usage) => sum + usage, 0) / this.cpuUsageReadings.length; this.processHistory.push({ timestamp: Date.now(), cpu: cpuUsage, memory: memoryUsage.heapUsed, heapUsed: memoryUsage.heapUsed, }); if (this.processHistory.length > this.processHistoryMaxSize) { this.processHistory.shift(); } const status = this.determineProcessStatus(cpuUsage, memoryUsage); const performanceRating = this.calculatePerformanceRating(cpuUsage, memoryUsage.heapUsed); const fileDescriptors = await this.getFileDescriptorCount(); const result = { pid: process.pid, cpuUsage: Math.round(cpuUsage * 100) / 100, memoryUsage: memoryUsage.heapUsed, uptime: process.uptime(), status, heapUsed: memoryUsage.heapUsed, heapTotal: memoryUsage.heapTotal, external: memoryUsage.external, arrayBuffers: memoryUsage.arrayBuffers, rss: memoryUsage.rss, avgCpuUsage: Math.round(avgCpuUsage * 100) / 100, peakMemoryUsage: this.peakMemoryUsage, performanceRating, }; if (fileDescriptors !== undefined) { result.fileDescriptors = fileDescriptors; } const threadCount = await this.getThreadCount(); if (threadCount !== undefined) { result.threadCount = threadCount; } return result; } async getDiskUsage() { try { if (this.isLinux || this.isMacOS) { fs.statSync('/'); return { used: 0, total: 0, available: 0, percentage: 0, }; } } catch (error) { } return undefined; } determineProcessStatus(cpuUsage, memoryUsage) { if (cpuUsage < 0.01) { return 'idle'; } else if (cpuUsage > 0.5) { return 'running'; } else if (memoryUsage.heapUsed > memoryUsage.heapTotal * 0.9) { return 'blocked'; } else { return 'running'; } } calculatePerformanceRating(cpuUsage, memoryUsage) { const memoryMB = memoryUsage / (1024 * 1024); if (cpuUsage < 0.1 && memoryMB < 50) { return 'excellent'; } else if (cpuUsage < 0.3 && memoryMB < 100) { return 'good'; } else if (cpuUsage < 0.5 && memoryMB < 200) { return 'fair'; } else { return 'poor'; } } async getFileDescriptorCount() { try { if (this.isLinux) { const fdDir = `/proc/${process.pid}/fd`; if (fs.existsSync(fdDir)) { const files = fs.readdirSync(fdDir); return files.length; } } return undefined; } catch (error) { return undefined; } } async getThreadCount() { try { if (this.isLinux) { const statusPath = `/proc/${process.pid}/status`; if (fs.existsSync(statusPath)) { const statusData = fs.readFileSync(statusPath, 'utf8'); const threadsLine = statusData.split('\n').find(line => line.startsWith('Threads:')); if (threadsLine) { const threads = parseInt(threadsLine.split('\t')[1] || '0'); return threads; } } } return 1; } catch (error) { return undefined; } } getProcessHistory() { return [...this.processHistory]; } getProcessPerformanceSummary() { const runtime = Date.now() - this.processStartTime; const avgCpuUsage = this.cpuUsageReadings.reduce((sum, usage) => sum + usage, 0) / this.cpuUsageReadings.length; const currentMemoryUsage = process.memoryUsage().heapUsed; return { runtime, avgCpuUsage: Math.round(avgCpuUsage * 100) / 100, peakMemoryUsage: this.peakMemoryUsage, currentMemoryUsage, performanceRating: this.calculatePerformanceRating(avgCpuUsage, currentMemoryUsage), }; } addToHistory(resources) { const historyEntry = { timestamp: Date.now(), cpu: resources.cpu.usage, memory: resources.memory.percentage, network: { bytesIn: resources.network.totalBytesIn, bytesOut: resources.network.totalBytesOut, }, process: { cpu: resources.process.cpuUsage, memory: resources.process.memoryUsage, }, }; this.history.push(historyEntry); if (this.history.length > this.historyMaxSize) { this.history.shift(); } } getHistory(timeRange) { if (!timeRange) { return [...this.history]; } const cutoff = Date.now() - timeRange; return this.history.filter(entry => entry.timestamp >= cutoff); } clearHistory() { this.history = []; } getSystemInfo() { return { platform: os.platform(), architecture: os.arch(), hostname: os.hostname(), uptime: os.uptime(), nodeVersion: process.version, }; } isResourceStressed(resources) { const cpuStressed = resources.cpu.usage > 80; const memoryStressed = resources.memory.percentage > 85; return { cpu: cpuStressed, memory: memoryStressed, overall: cpuStressed || memoryStressed, }; } } exports.SystemResourceService = SystemResourceService; //# sourceMappingURL=system-resource.service.js.map