polyv-live-cli
Version:
CLI tool for managing PolyV live streaming services.
544 lines • 19.7 kB
JavaScript
"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