UNPKG

node-os-utils

Version:

Advanced cross-platform operating system monitoring utilities with TypeScript support

604 lines 22.5 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.WindowsAdapter = void 0; const os_1 = __importDefault(require("os")); const fs_1 = require("fs"); const platform_adapter_1 = require("../core/platform-adapter"); const base_monitor_1 = require("../core/base-monitor"); const command_executor_1 = require("../utils/command-executor"); const errors_1 = require("../types/errors"); /** * Windows 平台适配器 * * 通过 PowerShell / WMI / Node.js 原生 API 获取系统信息 */ class WindowsAdapter extends platform_adapter_1.BasePlatformAdapter { constructor() { super('win32'); this.executor = new command_executor_1.CommandExecutor('win32'); } /** * 执行系统命令 */ async executeCommand(command, options) { return this.executor.execute(command, options); } /** * 读取文件内容 */ async readFile(path) { try { return await fs_1.promises.readFile(path, 'utf8'); } catch (error) { if (error?.code === 'ENOENT') { throw new errors_1.MonitorError(`File not found: ${path}`, errors_1.ErrorCode.FILE_NOT_FOUND, this.platformName, { path }); } if (error?.code === 'EACCES') { throw new errors_1.MonitorError(`Permission denied: ${path}`, errors_1.ErrorCode.PERMISSION_DENIED, this.platformName, { path }); } throw new errors_1.MonitorError(`Failed to read file: ${path}`, errors_1.ErrorCode.COMMAND_FAILED, this.platformName, { path, error: error?.message }); } } /** * 检查文件是否存在 */ async fileExists(path) { try { await fs_1.promises.access(path); return true; } catch { return false; } } /** * 组合 Node.js 与 WMI 数据,构建 CPU 基本信息 */ async getCPUInfo() { const cpus = os_1.default.cpus(); if (!cpus || cpus.length === 0) { throw this.createCommandError('getCPUInfo', 'Unable to read CPU information'); } const logicalCores = cpus.length; let physicalCores = Math.max(1, Math.floor(logicalCores / 2)); let manufacturer = cpus[0].model.includes('Intel') ? 'Intel' : cpus[0].model.includes('AMD') ? 'AMD' : 'Unknown'; let model = cpus[0].model; let maxFrequency = cpus[0].speed; try { const wmi = await this.executePowerShell('Get-CimInstance Win32_Processor | Select-Object Name,Manufacturer,NumberOfCores,NumberOfLogicalProcessors,MaxClockSpeed | ConvertTo-Json'); const info = this.ensureArray(wmi)[0]; if (info) { physicalCores = this.safeParseInt(info.NumberOfCores, physicalCores); manufacturer = info.Manufacturer || manufacturer; model = info.Name || model; maxFrequency = this.safeParseInt(info.MaxClockSpeed, maxFrequency); } } catch { // PowerShell/WMI 不可用(如 Deno 兼容层),降级到纯 os 模块数据 base_monitor_1.BaseMonitor.warnDegradation('cpu.command_failed', 'Windows PowerShell/WMI unavailable, falling back to os.cpus() data'); } return { model, manufacturer, architecture: os_1.default.arch(), cores: physicalCores || logicalCores, threads: logicalCores, baseFrequency: cpus[0].speed, maxFrequency, cache: {}, features: [] }; } /** * 获取 CPU 使用率,使用采样计算 */ async getCPUUsage() { return this.sampleCpuUsage(); } /** 获取 CPU 温度 */ async getCPUTemperature() { throw this.createUnsupportedError('cpu.temperature'); } /** * 获取内存信息,补充物理/虚拟内存详情 */ async getMemoryInfo() { const total = os_1.default.totalmem(); const free = os_1.default.freemem(); const used = Math.max(0, total - free); const memoryInfo = { total, available: free, free, used, cached: 0, buffers: 0, pressure: { level: 'normal', score: total > 0 ? (used / total) * 100 : 0 } }; try { const osInfo = await this.executePowerShell('Get-CimInstance Win32_OperatingSystem | Select-Object TotalVisibleMemorySize,FreePhysicalMemory,TotalVirtualMemorySize,FreeVirtualMemory | ConvertTo-Json'); const details = this.ensureArray(osInfo)[0]; if (details) { const totalKb = this.safeParseNumber(details.TotalVisibleMemorySize) * 1024; const freeKb = this.safeParseNumber(details.FreePhysicalMemory) * 1024; const totalVirtualKb = this.safeParseNumber(details.TotalVirtualMemorySize) * 1024; const freeVirtualKb = this.safeParseNumber(details.FreeVirtualMemory) * 1024; if (totalKb > 0) { memoryInfo.total = totalKb; } if (freeKb >= 0) { memoryInfo.free = freeKb; memoryInfo.available = freeKb; memoryInfo.used = Math.max(0, memoryInfo.total - memoryInfo.available); } memoryInfo.swap = { total: totalVirtualKb, free: freeVirtualKb, used: Math.max(0, totalVirtualKb - freeVirtualKb) }; } } catch { // PowerShell/WMI 不可用(如 Deno 兼容层),降级到纯 os 模块数据 base_monitor_1.BaseMonitor.warnDegradation('memory.command_failed', 'Windows PowerShell/WMI unavailable, falling back to os.totalmem()/os.freemem() data'); } return memoryInfo; } /** 获取内存使用情况 */ async getMemoryUsage() { const info = await this.getMemoryInfo(); const usage = info.total > 0 ? (info.used / info.total) * 100 : 0; return { ...info, usagePercentage: usage }; } /** 获取磁盘基本信息 */ async getDiskInfo() { const drives = await this.getFileSystemDrives(); return drives.map(drive => { const total = drive.used + drive.free; return { device: drive.root || `${drive.name}:`, name: drive.name, mountPoint: drive.root || `${drive.name}:`, filesystem: drive.filesystem || 'NTFS', total, used: drive.used, available: drive.free, usagePercentage: total > 0 ? (drive.used / total) * 100 : 0 }; }); } /** 获取磁盘 I/O 信息 */ async getDiskIO() { throw this.createUnsupportedError('disk.io'); } /** 获取磁盘使用情况 */ async getDiskUsage() { const info = await this.getDiskInfo(); return info; } /** 获取磁盘统计 */ async getDiskStats() { throw this.createUnsupportedError('disk.stats'); } /** 获取挂载点信息 */ async getMounts() { const drives = await this.getDiskInfo(); return drives.map(drive => ({ device: drive.device, mountPoint: drive.mountPoint, filesystem: drive.filesystem, options: 'rw', dump: 0, pass: 0 })); } /** 获取文件系统信息 */ async getFileSystems() { const drives = await this.getDiskInfo(); return drives.map(drive => ({ name: drive.device, type: drive.filesystem, mountPoint: drive.mountPoint, options: 'rw' })); } /** 获取网络接口 */ async getNetworkInterfaces() { return os_1.default.networkInterfaces(); } /** 获取网络统计 */ async getNetworkStats() { try { const stats = await this.executePowerShell('Get-NetAdapterStatistics | Select-Object Name,ReceivedBytes,SentBytes,ReceivedUnicastPackets,SentUnicastPackets,InboundErrors,OutboundErrors | ConvertTo-Json'); return this.ensureArray(stats).map((item) => ({ interface: item.Name, rxBytes: item.ReceivedBytes, txBytes: item.SentBytes, rxPackets: item.ReceivedUnicastPackets, txPackets: item.SentUnicastPackets, rxErrors: item.InboundErrors || 0, txErrors: item.OutboundErrors || 0 })); } catch (error) { if (error instanceof errors_1.MonitorError) { throw error; } throw new errors_1.MonitorError(`getNetworkStats failed: ${error?.message || String(error)}`, errors_1.ErrorCode.COMMAND_FAILED, 'win32', { originalError: error }); } } /** 获取网络连接 */ async getNetworkConnections() { throw this.createUnsupportedError('network.connections'); } /** 获取默认网关 */ async getDefaultGateway() { try { const result = await this.executePowerShell('Get-CimInstance Win32_NetworkAdapterConfiguration | Where-Object { $_.IPEnabled -eq $true -and $_.DefaultIPGateway } | Select-Object -First 1 DefaultIPGateway,Description | ConvertTo-Json'); const gateway = this.ensureArray(result)[0]; if (!gateway) { return null; } const value = Array.isArray(gateway.DefaultIPGateway) ? gateway.DefaultIPGateway[0] : gateway.DefaultIPGateway; return { gateway: value, interface: gateway.Description }; } catch { return null; } } /** 获取进程列表 */ async getProcessList() { try { const processes = await this.executePowerShell('Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId,Name,CommandLine,CreationDate,Priority,ThreadCount,WorkingSetSize | ConvertTo-Json', { maxBuffer: 1024 * 1024 * 10 }); return this.ensureArray(processes).map((proc) => this.normalizeProcess(proc)); } catch (error) { throw this.createUnsupportedError('process.list'); } } /** 获取进程信息 */ async getProcessInfo(pid) { try { const processes = await this.executePowerShell(`Get-CimInstance Win32_Process -Filter "ProcessId = ${pid}" | Select-Object ProcessId,ParentProcessId,Name,CommandLine,CreationDate,Priority,ThreadCount,WorkingSetSize | ConvertTo-Json`); const process = this.ensureArray(processes)[0]; if (!process) { return null; } return this.normalizeProcess(process); } catch (error) { throw this.createUnsupportedError('process.info'); } } /** 获取进程列表(旧接口兼容) */ async getProcesses() { return this.getProcessList(); } /** 杀死进程 */ async killProcess(pid, signal) { const command = signal === 'SIGKILL' ? `taskkill /PID ${pid} /F` : `taskkill /PID ${pid}`; try { await this.executeCommand(command, { timeout: 5000 }); return true; } catch { return false; } } /** 获取进程打开文件 */ async getProcessOpenFiles() { throw this.createUnsupportedError('process.openFiles'); } /** 获取进程环境变量 */ async getProcessEnvironment() { throw this.createUnsupportedError('process.environment'); } /** 获取系统信息 */ async getSystemInfo() { const uptimeSeconds = os_1.default.uptime(); const uptimeMs = uptimeSeconds * 1000; const bootTime = Date.now() - uptimeMs; const load = os_1.default.loadavg(); let systemDetails = {}; try { const info = await this.executePowerShell('Get-CimInstance Win32_OperatingSystem | Select-Object Caption,Version,BuildNumber,LastBootUpTime,NumberOfProcesses | ConvertTo-Json'); systemDetails = this.ensureArray(info)[0] || {}; } catch { // 忽略 } return { hostname: os_1.default.hostname(), platform: 'win32', distro: systemDetails.Caption || 'Windows', release: systemDetails.Version ? `${systemDetails.Version}${systemDetails.BuildNumber ? ' (Build ' + systemDetails.BuildNumber + ')' : ''}` : os_1.default.release(), kernel: typeof os_1.default.version === 'function' ? os_1.default.version() : os_1.default.release(), arch: os_1.default.arch(), uptime: uptimeMs, uptimeSeconds, bootTime, loadAverage: { load1: load[0], load5: load[1], load15: load[2] }, processCount: this.safeParseInt(systemDetails.NumberOfProcesses, 0), userCount: undefined, timezone: Intl.DateTimeFormat().resolvedOptions().timeZone }; } /** 获取系统负载 */ async getSystemLoad() { const load = os_1.default.loadavg(); return { load1: load[0], load5: load[1], load15: load[2] }; } /** 获取系统运行时间 */ async getSystemUptime() { const uptimeSeconds = os_1.default.uptime(); return { uptimeSeconds, uptime: uptimeSeconds * 1000, bootTime: Date.now() - uptimeSeconds * 1000 }; } /** 获取系统用户 */ async getSystemUsers() { throw this.createUnsupportedError('system.users'); } /** 获取系统服务 */ async getSystemServices() { try { const services = await this.executePowerShell('Get-Service | Select-Object Name,Status,StartType,DisplayName | ConvertTo-Json'); return this.ensureArray(services).map((service) => ({ name: service.Name, status: service.Status, enabled: service.StartType !== 'Disabled', description: service.DisplayName })); } catch { throw this.createUnsupportedError('system.services'); } } /** 初始化支持的功能 */ initializeSupportedFeatures() { return { cpu: { info: true, usage: true, temperature: false, frequency: true, cache: false, perCore: true, cores: true }, memory: { info: true, usage: true, swap: false, pressure: false, detailed: true, virtual: true }, disk: { info: true, io: false, health: false, smart: false, filesystem: true, usage: true, stats: false, mounts: true, filesystems: true }, network: { interfaces: true, stats: true, connections: false, bandwidth: false, gateway: true }, process: { list: true, details: true, tree: false, monitor: false, info: true, kill: true, openFiles: false, environment: false }, system: { info: true, load: true, uptime: true, users: false, services: true } }; } /** * 采样 CPU 使用率 * * 通过两次读取 Node.js os.cpus() 的累积时间片来计算 delta,从而得到整体与单核的使用率 */ async sampleCpuUsage(interval = 200) { const first = os_1.default.cpus(); await this.delay(interval); const second = os_1.default.cpus(); let totalUser = 0; let totalSystem = 0; let totalIdle = 0; let totalTotal = 0; const perCore = []; for (let i = 0; i < Math.min(first.length, second.length); i++) { const a = first[i].times; const b = second[i].times; const user = Math.max(0, b.user - a.user); const system = Math.max(0, b.sys - a.sys); const idle = Math.max(0, b.idle - a.idle); const nice = Math.max(0, b.nice - a.nice); const irq = Math.max(0, b.irq - a.irq); const total = user + system + idle + nice + irq; if (total === 0) { perCore.push(0); continue; } totalUser += user; totalSystem += system; totalIdle += idle; totalTotal += total; perCore.push(((total - idle) / total) * 100); } const overall = totalTotal > 0 ? ((totalTotal - totalIdle) / totalTotal) * 100 : 0; const userPct = totalTotal > 0 ? (totalUser / totalTotal) * 100 : 0; const systemPct = totalTotal > 0 ? (totalSystem / totalTotal) * 100 : 0; const idlePct = totalTotal > 0 ? (totalIdle / totalTotal) * 100 : 100; return { overall, user: userPct, system: systemPct, idle: idlePct, cores: perCore }; } /** * 执行 PowerShell 命令并转换 JSON * * @param command PowerShell 脚本片段 * @param options 执行选项,例如超时与缓冲区 */ async executePowerShell(command, options = {}) { const fullCommand = this.executor.buildCommand('powershell', ['-NoProfile', '-Command', command]); const result = await this.executeCommand(fullCommand, options); if (result.exitCode !== 0) { throw this.createCommandError(fullCommand, { exitCode: result.exitCode, stderr: result.stderr }); } const cleaned = this.sanitizeJson(result.stdout); if (!cleaned) { return []; } return this.safeParseJSON(cleaned, []); } /** * 将 WMI 返回的进程信息转换为统一结构 */ normalizeProcess(raw) { const totalMemory = os_1.default.totalmem(); const workingSet = this.safeParseNumber(raw.WorkingSetSize); const startTime = this.parseWmiDate(raw.CreationDate); return { pid: this.safeParseInt(raw.ProcessId), ppid: this.safeParseInt(raw.ParentProcessId), name: raw.Name || 'unknown', command: raw.CommandLine || raw.Name || '', state: 'running', cpuUsage: 0, memoryUsage: workingSet, memoryPercentage: totalMemory > 0 ? (workingSet / totalMemory) * 100 : 0, startTime, priority: raw.Priority, threads: this.safeParseInt(raw.ThreadCount) }; } /** * 将 WMI/CIM 返回的日期字符串解析为 UTC 时间戳 */ parseWmiDate(value) { if (!value) { return Date.now(); } const match = value.match(/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/); if (!match) { return Date.now(); } const [, year, month, day, hour, minute, second] = match; return Date.UTC(Number(year), Number(month) - 1, Number(day), Number(hour), Number(minute), Number(second)); } /** * 获取文件系统驱动器 * * 优先使用 PowerShell PSDrive,失败时回退到 WMI */ async getFileSystemDrives() { try { const result = await this.executePowerShell('Get-PSDrive -PSProvider FileSystem | Select-Object Name,Root,Used,Free | ConvertTo-Json'); return this.ensureArray(result).map((drive) => ({ name: drive.Name, root: drive.Root, used: this.safeParseNumber(drive.Used), free: this.safeParseNumber(drive.Free), filesystem: 'NTFS' })); } catch { // 回退到 wmic try { const wmi = await this.executePowerShell('Get-CimInstance Win32_LogicalDisk | Select-Object DeviceID,FileSystem,Size,FreeSpace | ConvertTo-Json'); return this.ensureArray(wmi).map((disk) => { const size = this.safeParseNumber(disk.Size); const free = Math.max(0, this.safeParseNumber(disk.FreeSpace)); const total = size > 0 ? size : free; const used = size > 0 ? Math.max(0, size - free) : Math.max(0, total - free); return { name: disk.DeviceID?.replace(':', ''), root: disk.DeviceID, used, free, filesystem: disk.FileSystem }; }); } catch (error) { throw this.createCommandError('getFileSystemDrives', error); } } } /** * 将对象包装为数组,防止 PowerShell 只返回单项 */ ensureArray(data) { if (data === null || data === undefined) { return []; } return Array.isArray(data) ? data : [data]; } /** * 清洗 JSON 输出,去除 BOM 并裁剪空白 */ sanitizeJson(output) { return output .replace(/^\uFEFF/, '') .trim(); } /** * Promise 形式的延时工具 */ async delay(ms) { await new Promise(resolve => setTimeout(resolve, ms)); } } exports.WindowsAdapter = WindowsAdapter; //# sourceMappingURL=windows-adapter.js.map