node-os-utils
Version:
Advanced cross-platform operating system monitoring utilities with TypeScript support
604 lines • 22.5 kB
JavaScript
"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