node-os-utils 1.3.7 → 2.0.1-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1113 -237
- package/dist/.tsbuildinfo +1 -0
- package/dist/package.json +99 -0
- package/dist/src/adapters/adapter-factory.d.ts +87 -0
- package/dist/src/adapters/adapter-factory.d.ts.map +1 -0
- package/dist/src/adapters/adapter-factory.js +272 -0
- package/dist/src/adapters/adapter-factory.js.map +1 -0
- package/dist/src/adapters/linux-adapter.d.ts +199 -0
- package/dist/src/adapters/linux-adapter.d.ts.map +1 -0
- package/dist/src/adapters/linux-adapter.js +1308 -0
- package/dist/src/adapters/linux-adapter.js.map +1 -0
- package/dist/src/adapters/macos-adapter.d.ts +232 -0
- package/dist/src/adapters/macos-adapter.d.ts.map +1 -0
- package/dist/src/adapters/macos-adapter.js +1144 -0
- package/dist/src/adapters/macos-adapter.js.map +1 -0
- package/dist/src/adapters/windows-adapter.d.ts +124 -0
- package/dist/src/adapters/windows-adapter.d.ts.map +1 -0
- package/dist/src/adapters/windows-adapter.js +601 -0
- package/dist/src/adapters/windows-adapter.js.map +1 -0
- package/dist/src/core/base-monitor.d.ts +107 -0
- package/dist/src/core/base-monitor.d.ts.map +1 -0
- package/dist/src/core/base-monitor.js +312 -0
- package/dist/src/core/base-monitor.js.map +1 -0
- package/dist/src/core/cache-manager.d.ts +157 -0
- package/dist/src/core/cache-manager.d.ts.map +1 -0
- package/dist/src/core/cache-manager.js +315 -0
- package/dist/src/core/cache-manager.js.map +1 -0
- package/dist/src/core/platform-adapter.d.ts +197 -0
- package/dist/src/core/platform-adapter.d.ts.map +1 -0
- package/dist/src/core/platform-adapter.js +244 -0
- package/dist/src/core/platform-adapter.js.map +1 -0
- package/dist/src/index.d.ts +205 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +315 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/monitors/cpu-monitor.d.ts +139 -0
- package/dist/src/monitors/cpu-monitor.d.ts.map +1 -0
- package/dist/src/monitors/cpu-monitor.js +433 -0
- package/dist/src/monitors/cpu-monitor.js.map +1 -0
- package/dist/src/monitors/disk-monitor.d.ts +136 -0
- package/dist/src/monitors/disk-monitor.d.ts.map +1 -0
- package/dist/src/monitors/disk-monitor.js +465 -0
- package/dist/src/monitors/disk-monitor.js.map +1 -0
- package/dist/src/monitors/memory-monitor.d.ts +136 -0
- package/dist/src/monitors/memory-monitor.d.ts.map +1 -0
- package/dist/src/monitors/memory-monitor.js +443 -0
- package/dist/src/monitors/memory-monitor.js.map +1 -0
- package/dist/src/monitors/network-monitor.d.ts +166 -0
- package/dist/src/monitors/network-monitor.d.ts.map +1 -0
- package/dist/src/monitors/network-monitor.js +586 -0
- package/dist/src/monitors/network-monitor.js.map +1 -0
- package/dist/src/monitors/process-monitor.d.ts +142 -0
- package/dist/src/monitors/process-monitor.d.ts.map +1 -0
- package/dist/src/monitors/process-monitor.js +460 -0
- package/dist/src/monitors/process-monitor.js.map +1 -0
- package/dist/src/monitors/system-monitor.d.ts +173 -0
- package/dist/src/monitors/system-monitor.d.ts.map +1 -0
- package/dist/src/monitors/system-monitor.js +592 -0
- package/dist/src/monitors/system-monitor.js.map +1 -0
- package/dist/src/types/common.d.ts +135 -0
- package/dist/src/types/common.d.ts.map +1 -0
- package/dist/src/types/common.js +141 -0
- package/dist/src/types/common.js.map +1 -0
- package/dist/src/types/config.d.ts +288 -0
- package/dist/src/types/config.d.ts.map +1 -0
- package/dist/src/types/config.js +3 -0
- package/dist/src/types/config.js.map +1 -0
- package/dist/src/types/errors.d.ts +40 -0
- package/dist/src/types/errors.d.ts.map +1 -0
- package/dist/src/types/errors.js +67 -0
- package/dist/src/types/errors.js.map +1 -0
- package/dist/src/types/index.d.ts +16 -0
- package/dist/src/types/index.d.ts.map +1 -0
- package/dist/src/types/index.js +36 -0
- package/dist/src/types/index.js.map +1 -0
- package/dist/src/types/monitors.d.ts +623 -0
- package/dist/src/types/monitors.d.ts.map +1 -0
- package/dist/src/types/monitors.js +3 -0
- package/dist/src/types/monitors.js.map +1 -0
- package/dist/src/types/platform.d.ts +435 -0
- package/dist/src/types/platform.d.ts.map +1 -0
- package/dist/src/types/platform.js +3 -0
- package/dist/src/types/platform.js.map +1 -0
- package/dist/src/utils/command-executor.d.ts +58 -0
- package/dist/src/utils/command-executor.d.ts.map +1 -0
- package/dist/src/utils/command-executor.js +321 -0
- package/dist/src/utils/command-executor.js.map +1 -0
- package/dist/test/config/test-config.d.ts +301 -0
- package/dist/test/config/test-config.d.ts.map +1 -0
- package/dist/test/config/test-config.js +235 -0
- package/dist/test/config/test-config.js.map +1 -0
- package/dist/test/platform/linux.test.d.ts +6 -0
- package/dist/test/platform/linux.test.d.ts.map +1 -0
- package/dist/test/platform/linux.test.js +307 -0
- package/dist/test/platform/linux.test.js.map +1 -0
- package/dist/test/platform/macos.test.d.ts +6 -0
- package/dist/test/platform/macos.test.d.ts.map +1 -0
- package/dist/test/platform/macos.test.js +276 -0
- package/dist/test/platform/macos.test.js.map +1 -0
- package/dist/test/platform/windows.test.d.ts +6 -0
- package/dist/test/platform/windows.test.d.ts.map +1 -0
- package/dist/test/platform/windows.test.js +320 -0
- package/dist/test/platform/windows.test.js.map +1 -0
- package/dist/test/unit/adapters/adapter-factory.test.d.ts +2 -0
- package/dist/test/unit/adapters/adapter-factory.test.d.ts.map +1 -0
- package/dist/test/unit/adapters/adapter-factory.test.js +147 -0
- package/dist/test/unit/adapters/adapter-factory.test.js.map +1 -0
- package/dist/test/unit/adapters/linux-adapter.test.d.ts +2 -0
- package/dist/test/unit/adapters/linux-adapter.test.d.ts.map +1 -0
- package/dist/test/unit/adapters/linux-adapter.test.js +191 -0
- package/dist/test/unit/adapters/linux-adapter.test.js.map +1 -0
- package/dist/test/unit/adapters/macos-adapter.test.d.ts +2 -0
- package/dist/test/unit/adapters/macos-adapter.test.d.ts.map +1 -0
- package/dist/test/unit/adapters/macos-adapter.test.js +224 -0
- package/dist/test/unit/adapters/macos-adapter.test.js.map +1 -0
- package/dist/test/unit/adapters/windows-adapter.test.d.ts +2 -0
- package/dist/test/unit/adapters/windows-adapter.test.d.ts.map +1 -0
- package/dist/test/unit/adapters/windows-adapter.test.js +41 -0
- package/dist/test/unit/adapters/windows-adapter.test.js.map +1 -0
- package/dist/test/unit/core/base-monitor.test.d.ts +2 -0
- package/dist/test/unit/core/base-monitor.test.d.ts.map +1 -0
- package/dist/test/unit/core/base-monitor.test.js +157 -0
- package/dist/test/unit/core/base-monitor.test.js.map +1 -0
- package/dist/test/unit/core/cache-manager.test.d.ts +6 -0
- package/dist/test/unit/core/cache-manager.test.d.ts.map +1 -0
- package/dist/test/unit/core/cache-manager.test.js +127 -0
- package/dist/test/unit/core/cache-manager.test.js.map +1 -0
- package/dist/test/unit/core/platform-adapter.test.d.ts +2 -0
- package/dist/test/unit/core/platform-adapter.test.d.ts.map +1 -0
- package/dist/test/unit/core/platform-adapter.test.js +186 -0
- package/dist/test/unit/core/platform-adapter.test.js.map +1 -0
- package/dist/test/unit/index.test.d.ts +2 -0
- package/dist/test/unit/index.test.d.ts.map +1 -0
- package/dist/test/unit/index.test.js +183 -0
- package/dist/test/unit/index.test.js.map +1 -0
- package/dist/test/unit/monitors/cpu-monitor.test.d.ts +2 -0
- package/dist/test/unit/monitors/cpu-monitor.test.d.ts.map +1 -0
- package/dist/test/unit/monitors/cpu-monitor.test.js +166 -0
- package/dist/test/unit/monitors/cpu-monitor.test.js.map +1 -0
- package/dist/test/unit/monitors/disk-monitor.test.d.ts +2 -0
- package/dist/test/unit/monitors/disk-monitor.test.d.ts.map +1 -0
- package/dist/test/unit/monitors/disk-monitor.test.js +46 -0
- package/dist/test/unit/monitors/disk-monitor.test.js.map +1 -0
- package/dist/test/unit/monitors/memory-monitor.test.d.ts +2 -0
- package/dist/test/unit/monitors/memory-monitor.test.d.ts.map +1 -0
- package/dist/test/unit/monitors/memory-monitor.test.js +142 -0
- package/dist/test/unit/monitors/memory-monitor.test.js.map +1 -0
- package/dist/test/unit/monitors/network-monitor.test.d.ts +2 -0
- package/dist/test/unit/monitors/network-monitor.test.d.ts.map +1 -0
- package/dist/test/unit/monitors/network-monitor.test.js +22 -0
- package/dist/test/unit/monitors/network-monitor.test.js.map +1 -0
- package/dist/test/unit/monitors/process-monitor.test.d.ts +2 -0
- package/dist/test/unit/monitors/process-monitor.test.d.ts.map +1 -0
- package/dist/test/unit/monitors/process-monitor.test.js +165 -0
- package/dist/test/unit/monitors/process-monitor.test.js.map +1 -0
- package/dist/test/unit/monitors/system-monitor-macos.test.d.ts +2 -0
- package/dist/test/unit/monitors/system-monitor-macos.test.d.ts.map +1 -0
- package/dist/test/unit/monitors/system-monitor-macos.test.js +115 -0
- package/dist/test/unit/monitors/system-monitor-macos.test.js.map +1 -0
- package/dist/test/unit/monitors/system-monitor.test.d.ts +2 -0
- package/dist/test/unit/monitors/system-monitor.test.d.ts.map +1 -0
- package/dist/test/unit/monitors/system-monitor.test.js +70 -0
- package/dist/test/unit/monitors/system-monitor.test.js.map +1 -0
- package/dist/test/unit/types/common.test.d.ts +6 -0
- package/dist/test/unit/types/common.test.d.ts.map +1 -0
- package/dist/test/unit/types/common.test.js +139 -0
- package/dist/test/unit/types/common.test.js.map +1 -0
- package/dist/test/unit/types/config.test.d.ts +2 -0
- package/dist/test/unit/types/config.test.d.ts.map +1 -0
- package/dist/test/unit/types/config.test.js +9 -0
- package/dist/test/unit/types/config.test.js.map +1 -0
- package/dist/test/unit/types/errors.test.d.ts +6 -0
- package/dist/test/unit/types/errors.test.d.ts.map +1 -0
- package/dist/test/unit/types/errors.test.js +184 -0
- package/dist/test/unit/types/errors.test.js.map +1 -0
- package/dist/test/unit/types/index.test.d.ts +2 -0
- package/dist/test/unit/types/index.test.d.ts.map +1 -0
- package/dist/test/unit/types/index.test.js +39 -0
- package/dist/test/unit/types/index.test.js.map +1 -0
- package/dist/test/unit/types/monitors.test.d.ts +2 -0
- package/dist/test/unit/types/monitors.test.d.ts.map +1 -0
- package/dist/test/unit/types/monitors.test.js +9 -0
- package/dist/test/unit/types/monitors.test.js.map +1 -0
- package/dist/test/unit/types/platform.test.d.ts +2 -0
- package/dist/test/unit/types/platform.test.d.ts.map +1 -0
- package/dist/test/unit/types/platform.test.js +9 -0
- package/dist/test/unit/types/platform.test.js.map +1 -0
- package/dist/test/unit/utils/command-executor.test.d.ts +6 -0
- package/dist/test/unit/utils/command-executor.test.d.ts.map +1 -0
- package/dist/test/unit/utils/command-executor.test.js +153 -0
- package/dist/test/unit/utils/command-executor.test.js.map +1 -0
- package/dist/test/unit/utils/platform-specific.test.d.ts +2 -0
- package/dist/test/unit/utils/platform-specific.test.d.ts.map +1 -0
- package/dist/test/unit/utils/platform-specific.test.js +32 -0
- package/dist/test/unit/utils/platform-specific.test.js.map +1 -0
- package/dist/test/utils/platform-specific.d.ts +139 -0
- package/dist/test/utils/platform-specific.d.ts.map +1 -0
- package/dist/test/utils/platform-specific.js +306 -0
- package/dist/test/utils/platform-specific.js.map +1 -0
- package/dist/test/utils/test-base.d.ts +157 -0
- package/dist/test/utils/test-base.d.ts.map +1 -0
- package/dist/test/utils/test-base.js +316 -0
- package/dist/test/utils/test-base.js.map +1 -0
- package/package.json +78 -18
- package/.travis.yml +0 -7
- package/index.js +0 -17
- package/lib/bucket.js +0 -14
- package/lib/cpu.js +0 -94
- package/lib/drive.js +0 -113
- package/lib/exec.js +0 -28
- package/lib/mem.js +0 -176
- package/lib/netstat.js +0 -182
- package/lib/openfiles.js +0 -25
- package/lib/os.js +0 -148
- package/lib/osCmd.js +0 -20
- package/lib/proc.js +0 -38
- package/lib/users.js +0 -18
- package/test/test.js +0 -55
- package/util/co.js +0 -237
- package/util/index.js +0 -12
|
@@ -0,0 +1,1144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.MacOSAdapter = void 0;
|
|
7
|
+
const os_1 = __importDefault(require("os"));
|
|
8
|
+
const platform_adapter_1 = require("../core/platform-adapter");
|
|
9
|
+
const command_executor_1 = require("../utils/command-executor");
|
|
10
|
+
const errors_1 = require("../types/errors");
|
|
11
|
+
/**
|
|
12
|
+
* macOS 平台适配器
|
|
13
|
+
*
|
|
14
|
+
* 实现 macOS 系统的监控功能,主要通过 sysctl、vm_stat、system_profiler 等命令
|
|
15
|
+
*/
|
|
16
|
+
class MacOSAdapter extends platform_adapter_1.BasePlatformAdapter {
|
|
17
|
+
constructor() {
|
|
18
|
+
super('darwin');
|
|
19
|
+
this.executor = new command_executor_1.CommandExecutor('darwin');
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* 执行系统命令
|
|
23
|
+
*/
|
|
24
|
+
async executeCommand(command, options) {
|
|
25
|
+
return this.executor.execute(command, options);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* 读取文件内容,捕获并转换常见的权限/不存在错误
|
|
29
|
+
*/
|
|
30
|
+
async readFile(path) {
|
|
31
|
+
try {
|
|
32
|
+
const result = await this.executeCommand(`cat "${path}"`);
|
|
33
|
+
this.validateCommandResult(result, `cat ${path}`);
|
|
34
|
+
return result.stdout;
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
const err = error;
|
|
38
|
+
const errorCode = err?.code === 'EACCES'
|
|
39
|
+
? errors_1.ErrorCode.PERMISSION_DENIED
|
|
40
|
+
: err?.code === 'ENOENT'
|
|
41
|
+
? errors_1.ErrorCode.FILE_NOT_FOUND
|
|
42
|
+
: errors_1.ErrorCode.COMMAND_FAILED;
|
|
43
|
+
throw new errors_1.MonitorError(`Failed to read file: ${path}`, errorCode, this.platformName, { path, error: err?.message, code: err?.code });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* 检查文件是否存在
|
|
48
|
+
*/
|
|
49
|
+
async fileExists(path) {
|
|
50
|
+
try {
|
|
51
|
+
const result = await this.executeCommand(`test -f "${path}"`);
|
|
52
|
+
return result.exitCode === 0;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 通过 sysctl 汇总 CPU 基本信息
|
|
60
|
+
*/
|
|
61
|
+
async getCPUInfo() {
|
|
62
|
+
try {
|
|
63
|
+
const [brand, cores, threads, freq] = await Promise.allSettled([
|
|
64
|
+
this.executeCommand('sysctl -n machdep.cpu.brand_string'),
|
|
65
|
+
this.executeCommand('sysctl -n hw.physicalcpu'),
|
|
66
|
+
this.executeCommand('sysctl -n hw.logicalcpu'),
|
|
67
|
+
this.executeCommand('sysctl -n hw.cpufrequency_max')
|
|
68
|
+
]).then(results => [
|
|
69
|
+
results[0].status === 'fulfilled' ? results[0].value : null,
|
|
70
|
+
results[1].status === 'fulfilled' ? results[1].value : null,
|
|
71
|
+
results[2].status === 'fulfilled' ? results[2].value : null,
|
|
72
|
+
results[3].status === 'fulfilled' ? results[3].value : null
|
|
73
|
+
]);
|
|
74
|
+
return this.parseCPUInfo(brand?.stdout || '', cores?.stdout || '', threads?.stdout || '', freq?.stdout || '');
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
throw this.createCommandError('getCPUInfo', error);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* 调用 top/iostat 获取 CPU 使用率,失败时链式回退
|
|
82
|
+
*/
|
|
83
|
+
async getCPUUsage() {
|
|
84
|
+
try {
|
|
85
|
+
// 使用 top 命令获取 CPU 使用率
|
|
86
|
+
const result = await this.executeCommand('top -l 1 -n 0');
|
|
87
|
+
this.validateCommandResult(result, 'top command');
|
|
88
|
+
return this.parseCPUUsageFromTop(result.stdout);
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
// 回退到 iostat
|
|
92
|
+
try {
|
|
93
|
+
const result = await this.executeCommand('iostat -c 1');
|
|
94
|
+
this.validateCommandResult(result, 'iostat command');
|
|
95
|
+
return this.parseCPUUsageFromIostat(result.stdout);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
throw this.createCommandError('getCPUUsage', error);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* 使用 powermetrics 读取温度,需要 sudo 权限
|
|
104
|
+
*/
|
|
105
|
+
async getCPUTemperature() {
|
|
106
|
+
try {
|
|
107
|
+
// macOS 温度监控需要第三方工具或私有 API
|
|
108
|
+
// 尝试使用 powermetrics(需要 sudo)
|
|
109
|
+
const result = await this.executeCommand('powermetrics -n 1 -i 1000 --samplers smc');
|
|
110
|
+
return this.parseTemperatureFromPowermetrics(result.stdout);
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
// 温度监控在 macOS 上可能需要额外权限
|
|
114
|
+
throw this.createUnsupportedError('cpu.temperature');
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* 读取 vm_stat 解析内存占用
|
|
119
|
+
*/
|
|
120
|
+
async getMemoryInfo() {
|
|
121
|
+
try {
|
|
122
|
+
const [vmStat, totalMem, pressure] = await Promise.allSettled([
|
|
123
|
+
this.executeCommand('vm_stat'),
|
|
124
|
+
this.executeCommand('sysctl -n hw.memsize'),
|
|
125
|
+
this.executeCommand('memory_pressure')
|
|
126
|
+
]).then(results => [
|
|
127
|
+
results[0].status === 'fulfilled' ? results[0].value : null,
|
|
128
|
+
results[1].status === 'fulfilled' ? results[1].value : null,
|
|
129
|
+
results[2].status === 'fulfilled' ? results[2].value : null
|
|
130
|
+
]);
|
|
131
|
+
if (vmStat)
|
|
132
|
+
this.validateCommandResult(vmStat, 'vm_stat');
|
|
133
|
+
if (totalMem)
|
|
134
|
+
this.validateCommandResult(totalMem, 'sysctl hw.memsize');
|
|
135
|
+
return this.parseMemoryInfo(vmStat?.stdout || '', totalMem?.stdout || '', pressure?.stdout || '');
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
throw this.createCommandError('getMemoryInfo', error);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* 读取 vm_stat 解析内存占用
|
|
143
|
+
*/
|
|
144
|
+
async getMemoryUsage() {
|
|
145
|
+
// macOS 上内存信息和使用情况来自相同来源
|
|
146
|
+
return this.getMemoryInfo();
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* 读取 df -h 解析磁盘占用
|
|
150
|
+
*/
|
|
151
|
+
async getDiskInfo() {
|
|
152
|
+
try {
|
|
153
|
+
const result = await this.executeCommand('df -h');
|
|
154
|
+
this.validateCommandResult(result, 'df -h');
|
|
155
|
+
return this.parseDiskInfo(result.stdout);
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
throw this.createCommandError('getDiskInfo', error);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* 读取 iostat -d 解析磁盘 I/O 统计
|
|
163
|
+
*/
|
|
164
|
+
async getDiskIO() {
|
|
165
|
+
try {
|
|
166
|
+
const result = await this.executeCommand('iostat -d');
|
|
167
|
+
this.validateCommandResult(result, 'iostat -d');
|
|
168
|
+
return this.parseDiskIO(result.stdout);
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
throw this.createCommandError('getDiskIO', error);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* 读取 ifconfig 解析网络接口列表
|
|
176
|
+
*/
|
|
177
|
+
async getNetworkInterfaces() {
|
|
178
|
+
try {
|
|
179
|
+
const result = await this.executeCommand('ifconfig');
|
|
180
|
+
this.validateCommandResult(result, 'ifconfig');
|
|
181
|
+
return this.parseNetworkInterfaces(result.stdout);
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
throw this.createCommandError('getNetworkInterfaces', error);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* 读取 netstat -ib 解析网络统计信息
|
|
189
|
+
*/
|
|
190
|
+
async getNetworkStats() {
|
|
191
|
+
try {
|
|
192
|
+
const result = await this.executeCommand('netstat -ib');
|
|
193
|
+
this.validateCommandResult(result, 'netstat -ib');
|
|
194
|
+
return this.parseNetworkStats(result.stdout);
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
throw this.createCommandError('getNetworkStats', error);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* 读取 ps -axo pid=,ppid=,comm=,%cpu=,%mem=,rss=,stat=,user=,args= 解析进程列表
|
|
202
|
+
*/
|
|
203
|
+
async getProcesses() {
|
|
204
|
+
try {
|
|
205
|
+
const result = await this.executeCommand('ps -axo pid=,ppid=,comm=,%cpu=,%mem=,rss=,stat=,user=,args=');
|
|
206
|
+
this.validateCommandResult(result, 'ps command');
|
|
207
|
+
return this.parseProcessList(result.stdout);
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
throw this.createCommandError('getProcesses', error);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* 读取 ps -p pid -o pid,ppid,command,pcpu,pmem,state,user,lstart 解析特定进程信息
|
|
215
|
+
*/
|
|
216
|
+
async getProcessInfo(pid) {
|
|
217
|
+
try {
|
|
218
|
+
const [summaryResult, commandResult, startResult] = await Promise.all([
|
|
219
|
+
this.executeCommand(`ps -p ${pid} -o pid=,ppid=,rss=,pcpu=,pmem=,state=,user=`),
|
|
220
|
+
this.executeCommand(`ps -p ${pid} -o command=`),
|
|
221
|
+
this.executeCommand(`ps -p ${pid} -o lstart=`)
|
|
222
|
+
]);
|
|
223
|
+
this.validateCommandResult(summaryResult, `ps summary ${pid}`);
|
|
224
|
+
this.validateCommandResult(commandResult, `ps command ${pid}`);
|
|
225
|
+
this.validateCommandResult(startResult, `ps lstart ${pid}`);
|
|
226
|
+
const summaryLine = summaryResult.stdout.split('\n').find(line => line.trim().length > 0) || '';
|
|
227
|
+
const commandLine = commandResult.stdout.split('\n').find(line => line.trim().length > 0) || '';
|
|
228
|
+
const lstartLine = startResult.stdout.split('\n').find(line => line.trim().length > 0) || '';
|
|
229
|
+
return this.parseProcessInfo({ summary: summaryLine, command: commandLine, start: lstartLine }, pid);
|
|
230
|
+
}
|
|
231
|
+
catch (error) {
|
|
232
|
+
throw this.createCommandError('getProcessInfo', error);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* 读取 uname -a, uptime, sysctl -n vm.loadavg, sw_vers 解析系统信息
|
|
237
|
+
*/
|
|
238
|
+
async getSystemInfo() {
|
|
239
|
+
try {
|
|
240
|
+
const [uname, uptime, loadavg, osVersion] = await Promise.allSettled([
|
|
241
|
+
this.executeCommand('uname -a'),
|
|
242
|
+
this.executeCommand('uptime'),
|
|
243
|
+
this.executeCommand('sysctl -n vm.loadavg'),
|
|
244
|
+
this.executeCommand('sw_vers')
|
|
245
|
+
]).then(results => [
|
|
246
|
+
results[0].status === 'fulfilled' ? results[0].value : null,
|
|
247
|
+
results[1].status === 'fulfilled' ? results[1].value : null,
|
|
248
|
+
results[2].status === 'fulfilled' ? results[2].value : null,
|
|
249
|
+
results[3].status === 'fulfilled' ? results[3].value : null
|
|
250
|
+
]);
|
|
251
|
+
return this.parseSystemInfo(uname?.stdout || '', uptime?.stdout || '', loadavg?.stdout || '', osVersion?.stdout || null);
|
|
252
|
+
}
|
|
253
|
+
catch (error) {
|
|
254
|
+
throw this.createCommandError('getSystemInfo', error);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* 读取 sysctl -n vm.loadavg 解析系统负载
|
|
259
|
+
*/
|
|
260
|
+
async getSystemLoad() {
|
|
261
|
+
try {
|
|
262
|
+
const result = await this.executeCommand('sysctl -n vm.loadavg');
|
|
263
|
+
this.validateCommandResult(result, 'sysctl vm.loadavg');
|
|
264
|
+
return this.parseLoadAverage(result.stdout);
|
|
265
|
+
}
|
|
266
|
+
catch (error) {
|
|
267
|
+
throw this.createCommandError('getSystemLoad', error);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* 读取 sysctl -n vm.loadavg 解析系统负载
|
|
272
|
+
*/
|
|
273
|
+
initializeSupportedFeatures() {
|
|
274
|
+
return {
|
|
275
|
+
cpu: {
|
|
276
|
+
info: true,
|
|
277
|
+
usage: true,
|
|
278
|
+
temperature: false, // 需要特殊权限
|
|
279
|
+
frequency: true,
|
|
280
|
+
cache: false,
|
|
281
|
+
perCore: false,
|
|
282
|
+
cores: true
|
|
283
|
+
},
|
|
284
|
+
memory: {
|
|
285
|
+
info: true,
|
|
286
|
+
usage: true,
|
|
287
|
+
swap: true,
|
|
288
|
+
pressure: true,
|
|
289
|
+
detailed: true,
|
|
290
|
+
virtual: true
|
|
291
|
+
},
|
|
292
|
+
disk: {
|
|
293
|
+
info: true,
|
|
294
|
+
io: true,
|
|
295
|
+
health: false,
|
|
296
|
+
smart: false,
|
|
297
|
+
filesystem: true,
|
|
298
|
+
usage: true,
|
|
299
|
+
stats: true,
|
|
300
|
+
mounts: true,
|
|
301
|
+
filesystems: true
|
|
302
|
+
},
|
|
303
|
+
network: {
|
|
304
|
+
interfaces: true,
|
|
305
|
+
stats: true,
|
|
306
|
+
connections: true,
|
|
307
|
+
bandwidth: false,
|
|
308
|
+
gateway: true
|
|
309
|
+
},
|
|
310
|
+
process: {
|
|
311
|
+
list: true,
|
|
312
|
+
details: true,
|
|
313
|
+
tree: false,
|
|
314
|
+
monitor: true,
|
|
315
|
+
info: true,
|
|
316
|
+
kill: true,
|
|
317
|
+
openFiles: true,
|
|
318
|
+
environment: true
|
|
319
|
+
},
|
|
320
|
+
system: {
|
|
321
|
+
info: true,
|
|
322
|
+
load: true,
|
|
323
|
+
uptime: true,
|
|
324
|
+
users: true,
|
|
325
|
+
services: false
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
// 私有解析方法,解析命令输出为结构化数据
|
|
330
|
+
parseCPUInfo(brand, cores, threads, freq) {
|
|
331
|
+
return {
|
|
332
|
+
model: brand.trim(),
|
|
333
|
+
manufacturer: brand.includes('Intel') ? 'Intel' : brand.includes('Apple') ? 'Apple' : 'Unknown',
|
|
334
|
+
architecture: 'Unknown',
|
|
335
|
+
cores: this.safeParseInt(cores.trim()),
|
|
336
|
+
threads: this.safeParseInt(threads.trim()),
|
|
337
|
+
baseFrequency: freq ? this.safeParseInt(freq.trim()) / 1000000 : 0, // 转换为 MHz
|
|
338
|
+
maxFrequency: freq ? this.safeParseInt(freq.trim()) / 1000000 : 0,
|
|
339
|
+
cache: {},
|
|
340
|
+
features: []
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* 解析 top 命令输出为 CPU 使用率
|
|
345
|
+
*/
|
|
346
|
+
parseCPUUsageFromTop(output) {
|
|
347
|
+
const lines = output.split('\n');
|
|
348
|
+
const cpuLine = lines.find(line => line.includes('CPU usage:'));
|
|
349
|
+
if (!cpuLine) {
|
|
350
|
+
throw this.createParseError(output, 'CPU usage line not found in top output');
|
|
351
|
+
}
|
|
352
|
+
// 解析类似 "CPU usage: 10.81% user, 13.73% sys, 75.45% idle" 的行
|
|
353
|
+
const userMatch = cpuLine.match(/([\d.]+)%\s+user/);
|
|
354
|
+
const sysMatch = cpuLine.match(/([\d.]+)%\s+sys/);
|
|
355
|
+
const idleMatch = cpuLine.match(/([\d.]+)%\s+idle/);
|
|
356
|
+
const user = userMatch ? this.safeParseNumber(userMatch[1]) : 0;
|
|
357
|
+
const system = sysMatch ? this.safeParseNumber(sysMatch[1]) : 0;
|
|
358
|
+
const idle = idleMatch ? this.safeParseNumber(idleMatch[1]) : 0;
|
|
359
|
+
return {
|
|
360
|
+
overall: 100 - idle,
|
|
361
|
+
user,
|
|
362
|
+
system,
|
|
363
|
+
idle,
|
|
364
|
+
cores: []
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* 解析 iostat 命令输出为 CPU 使用率
|
|
369
|
+
*/
|
|
370
|
+
parseCPUUsageFromIostat(output) {
|
|
371
|
+
const lines = output.split('\n');
|
|
372
|
+
const dataLine = lines[lines.length - 2]; // iostat 的最后一行数据
|
|
373
|
+
if (!dataLine) {
|
|
374
|
+
throw this.createParseError(output, 'No data line found in iostat output');
|
|
375
|
+
}
|
|
376
|
+
const fields = dataLine.trim().split(/\s+/);
|
|
377
|
+
if (fields.length >= 3) {
|
|
378
|
+
const user = this.safeParseNumber(fields[0]);
|
|
379
|
+
const system = this.safeParseNumber(fields[1]);
|
|
380
|
+
const idle = this.safeParseNumber(fields[2]);
|
|
381
|
+
return {
|
|
382
|
+
overall: 100 - idle,
|
|
383
|
+
user,
|
|
384
|
+
system,
|
|
385
|
+
idle,
|
|
386
|
+
cores: []
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
throw this.createParseError(output, 'Unable to parse iostat CPU data');
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* 解析 powermetrics 命令输出为温度
|
|
393
|
+
*/
|
|
394
|
+
parseTemperatureFromPowermetrics(output) {
|
|
395
|
+
// powermetrics 输出解析(简化版)
|
|
396
|
+
const lines = output.split('\n');
|
|
397
|
+
const temperatures = [];
|
|
398
|
+
for (const line of lines) {
|
|
399
|
+
const tempMatch = line.match(/(\w+)\s+temperature:\s+([\d.]+)/);
|
|
400
|
+
if (tempMatch) {
|
|
401
|
+
temperatures.push({
|
|
402
|
+
sensor: tempMatch[1],
|
|
403
|
+
temperature: this.safeParseNumber(tempMatch[2])
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
return temperatures;
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* 解析 vm_stat 输出为内存信息
|
|
411
|
+
*/
|
|
412
|
+
parseMemoryInfo(vmStat, totalMem, pressure) {
|
|
413
|
+
const total = this.safeParseInt(totalMem.trim());
|
|
414
|
+
// 解析 vm_stat 输出
|
|
415
|
+
const vmLines = vmStat.split('\n');
|
|
416
|
+
const pageSizeLine = vmLines.find(line => /page size of\s+\d+\s+bytes/i.test(line));
|
|
417
|
+
const pageSizeMatch = pageSizeLine ? pageSizeLine.match(/page size of\s+(\d+)\s+bytes/i) : null;
|
|
418
|
+
const detectedPageSize = pageSizeMatch && pageSizeMatch[1]
|
|
419
|
+
? this.safeParseInt(pageSizeMatch[1])
|
|
420
|
+
: 0;
|
|
421
|
+
const pageSize = detectedPageSize > 0 ? detectedPageSize : 4096;
|
|
422
|
+
let free = 0, active = 0, inactive = 0, wired = 0, compressed = 0;
|
|
423
|
+
for (const line of vmLines) {
|
|
424
|
+
const match = line.match(/Pages\s+([\w\s]+):\s+(\d+)/);
|
|
425
|
+
if (match) {
|
|
426
|
+
const [, typeRaw, count] = match;
|
|
427
|
+
const type = typeRaw.trim().toLowerCase();
|
|
428
|
+
const bytes = this.safeParseInt(count) * pageSize;
|
|
429
|
+
switch (type) {
|
|
430
|
+
case 'free':
|
|
431
|
+
free = bytes;
|
|
432
|
+
break;
|
|
433
|
+
case 'active':
|
|
434
|
+
active = bytes;
|
|
435
|
+
break;
|
|
436
|
+
case 'inactive':
|
|
437
|
+
inactive = bytes;
|
|
438
|
+
break;
|
|
439
|
+
case 'wired':
|
|
440
|
+
case 'wired down':
|
|
441
|
+
wired = bytes;
|
|
442
|
+
break;
|
|
443
|
+
case 'compressed':
|
|
444
|
+
case 'occupied':
|
|
445
|
+
case 'occupied by compressor':
|
|
446
|
+
compressed = bytes;
|
|
447
|
+
break;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
const used = active + wired + compressed;
|
|
452
|
+
const available = total - used;
|
|
453
|
+
return {
|
|
454
|
+
total,
|
|
455
|
+
used,
|
|
456
|
+
free,
|
|
457
|
+
available,
|
|
458
|
+
active,
|
|
459
|
+
inactive,
|
|
460
|
+
wired,
|
|
461
|
+
compressed,
|
|
462
|
+
usagePercentage: total > 0 ? (used / total) * 100 : 0,
|
|
463
|
+
pressure: this.parseMemoryPressure(pressure)
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* 解析 memory_pressure 输出为内存压力
|
|
468
|
+
*/
|
|
469
|
+
parseMemoryPressure(pressure) {
|
|
470
|
+
if (!pressure) {
|
|
471
|
+
return { level: 'normal', score: 0 };
|
|
472
|
+
}
|
|
473
|
+
// 简化的内存压力解析
|
|
474
|
+
if (pressure.includes('critical')) {
|
|
475
|
+
return { level: 'critical', score: 90 };
|
|
476
|
+
}
|
|
477
|
+
else if (pressure.includes('warn')) {
|
|
478
|
+
return { level: 'high', score: 70 };
|
|
479
|
+
}
|
|
480
|
+
else {
|
|
481
|
+
return { level: 'normal', score: 10 };
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* 解析 df -h 输出为磁盘信息
|
|
486
|
+
*/
|
|
487
|
+
parseDiskInfo(output) {
|
|
488
|
+
const lines = output.split('\n').filter(line => line.trim());
|
|
489
|
+
if (lines.length < 2)
|
|
490
|
+
return [];
|
|
491
|
+
const disks = [];
|
|
492
|
+
for (let i = 1; i < lines.length; i++) {
|
|
493
|
+
const fields = lines[i].split(/\s+/);
|
|
494
|
+
if (fields.length >= 9) {
|
|
495
|
+
const [filesystem, size, used, available, capacity, /* iused */ , /* ifree */ , /* iusedPercent */ , mountpoint] = fields;
|
|
496
|
+
disks.push({
|
|
497
|
+
filesystem,
|
|
498
|
+
mountpoint,
|
|
499
|
+
size: this.convertDfSizeToBytes(size),
|
|
500
|
+
used: this.convertDfSizeToBytes(used),
|
|
501
|
+
available: this.convertDfSizeToBytes(available),
|
|
502
|
+
usagePercentage: this.safeParseNumber(capacity.replace('%', ''))
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
return disks;
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* 解析 iostat -d 输出为磁盘 I/O 统计
|
|
510
|
+
*/
|
|
511
|
+
parseDiskIO(output) {
|
|
512
|
+
return this.parseIostatDisks(output).map(item => ({
|
|
513
|
+
device: item.device,
|
|
514
|
+
kbPerTransfer: item.kbPerTransfer,
|
|
515
|
+
transfersPerSec: item.transfersPerSec,
|
|
516
|
+
mbPerSec: item.mbPerSec,
|
|
517
|
+
readSpeed: item.bytePerSec,
|
|
518
|
+
iops: item.transfersPerSec
|
|
519
|
+
}));
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* 解析 ifconfig 输出为网络接口列表
|
|
523
|
+
*/
|
|
524
|
+
parseNetworkInterfaces(output) {
|
|
525
|
+
const interfaces = [];
|
|
526
|
+
const blocks = output.split(/\n(?=\w)/); // 按接口分割
|
|
527
|
+
for (const block of blocks) {
|
|
528
|
+
const lines = block.split('\n');
|
|
529
|
+
const interfaceLine = lines[0];
|
|
530
|
+
if (!interfaceLine)
|
|
531
|
+
continue;
|
|
532
|
+
const nameMatch = interfaceLine.match(/^(\w+):/);
|
|
533
|
+
if (!nameMatch)
|
|
534
|
+
continue;
|
|
535
|
+
const name = nameMatch[1];
|
|
536
|
+
const addresses = [];
|
|
537
|
+
let state = 'down';
|
|
538
|
+
let mtu = 0;
|
|
539
|
+
let macAddress = '';
|
|
540
|
+
for (const line of lines) {
|
|
541
|
+
// 状态检查
|
|
542
|
+
if (line.includes('<UP,')) {
|
|
543
|
+
state = 'up';
|
|
544
|
+
}
|
|
545
|
+
// MTU 检查
|
|
546
|
+
const mtuMatch = line.match(/mtu (\d+)/);
|
|
547
|
+
if (mtuMatch) {
|
|
548
|
+
mtu = this.safeParseInt(mtuMatch[1]);
|
|
549
|
+
}
|
|
550
|
+
// MAC 地址
|
|
551
|
+
const etherMatch = line.match(/ether\s+([0-9a-f:]+)/i);
|
|
552
|
+
if (etherMatch) {
|
|
553
|
+
macAddress = etherMatch[1].toLowerCase();
|
|
554
|
+
}
|
|
555
|
+
// IPv4 地址
|
|
556
|
+
const inetMatch = line.match(/inet\s+([^\s]+)/);
|
|
557
|
+
if (inetMatch) {
|
|
558
|
+
addresses.push({
|
|
559
|
+
address: inetMatch[1],
|
|
560
|
+
family: 'IPv4'
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
// IPv6 地址
|
|
564
|
+
const inet6Match = line.match(/inet6\s+([^\s]+)/);
|
|
565
|
+
if (inet6Match) {
|
|
566
|
+
addresses.push({
|
|
567
|
+
address: inet6Match[1],
|
|
568
|
+
family: 'IPv6'
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
interfaces.push({
|
|
573
|
+
name,
|
|
574
|
+
addresses,
|
|
575
|
+
state,
|
|
576
|
+
mtu,
|
|
577
|
+
internal: name === 'lo0',
|
|
578
|
+
mac: macAddress
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
return interfaces;
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* 解析 netstat -ib/-i 输出,兼容是否包含字节列两种格式
|
|
585
|
+
*/
|
|
586
|
+
parseNetworkStats(output) {
|
|
587
|
+
const lines = output.split('\n').filter(line => line.trim());
|
|
588
|
+
if (lines.length === 0)
|
|
589
|
+
return [];
|
|
590
|
+
const header = lines[0].toLowerCase();
|
|
591
|
+
const stats = [];
|
|
592
|
+
for (let i = 1; i < lines.length; i++) {
|
|
593
|
+
const fields = lines[i].trim().split(/\s+/);
|
|
594
|
+
if (fields.length < 8)
|
|
595
|
+
continue;
|
|
596
|
+
const name = fields[0];
|
|
597
|
+
const mtu = this.safeParseInt(fields[1]);
|
|
598
|
+
if (header.includes('ibytes') && fields.length >= 11) {
|
|
599
|
+
const tail = fields.slice(-7);
|
|
600
|
+
if (tail.length < 7)
|
|
601
|
+
continue;
|
|
602
|
+
const [ipkts, ierrs, ibytes, opkts, oerrs, obytes, collisions] = tail;
|
|
603
|
+
stats.push({
|
|
604
|
+
interface: name,
|
|
605
|
+
mtu,
|
|
606
|
+
rxPackets: this.safeParseInt(ipkts),
|
|
607
|
+
rxErrors: this.safeParseInt(ierrs),
|
|
608
|
+
rxBytes: this.safeParseInt(ibytes),
|
|
609
|
+
txPackets: this.safeParseInt(opkts),
|
|
610
|
+
txErrors: this.safeParseInt(oerrs),
|
|
611
|
+
txBytes: this.safeParseInt(obytes),
|
|
612
|
+
collisions: this.safeParseInt(collisions)
|
|
613
|
+
});
|
|
614
|
+
continue;
|
|
615
|
+
}
|
|
616
|
+
const tail = fields.slice(-5);
|
|
617
|
+
if (tail.length < 5)
|
|
618
|
+
continue;
|
|
619
|
+
const [ipkts, ierrs, opkts, oerrs, collisions] = tail;
|
|
620
|
+
stats.push({
|
|
621
|
+
interface: name,
|
|
622
|
+
mtu,
|
|
623
|
+
rxPackets: this.safeParseInt(ipkts),
|
|
624
|
+
rxErrors: this.safeParseInt(ierrs),
|
|
625
|
+
rxBytes: 0,
|
|
626
|
+
txPackets: this.safeParseInt(opkts),
|
|
627
|
+
txErrors: this.safeParseInt(oerrs),
|
|
628
|
+
txBytes: 0,
|
|
629
|
+
collisions: this.safeParseInt(collisions)
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
return stats;
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* 解析 ps -axo pid=,ppid=,comm=,%cpu=,%mem=,rss=,stat=,user=,args= 输出为进程列表
|
|
636
|
+
*/
|
|
637
|
+
parseProcessList(output) {
|
|
638
|
+
const lines = output.split('\n').filter(line => line.trim());
|
|
639
|
+
const processes = [];
|
|
640
|
+
for (const line of lines) {
|
|
641
|
+
const fields = line.trim().split(/\s+/);
|
|
642
|
+
if (fields.length >= 8) {
|
|
643
|
+
const [pid, ppid, cmd, pcpu, pmem, rss, state, user, ...argsParts] = fields;
|
|
644
|
+
const command = argsParts.length > 0 ? argsParts.join(' ').trim() : cmd;
|
|
645
|
+
processes.push({
|
|
646
|
+
pid: this.safeParseInt(pid),
|
|
647
|
+
ppid: this.safeParseInt(ppid),
|
|
648
|
+
name: cmd,
|
|
649
|
+
comm: cmd,
|
|
650
|
+
command,
|
|
651
|
+
cpuUsage: this.safeParseNumber(pcpu),
|
|
652
|
+
memoryUsage: this.safeParseInt(rss) * 1024,
|
|
653
|
+
memoryPercentage: this.safeParseNumber(pmem),
|
|
654
|
+
state,
|
|
655
|
+
user
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
return processes;
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* 解析 ps 输出的进程详细信息
|
|
663
|
+
*/
|
|
664
|
+
parseProcessInfo(data, pid) {
|
|
665
|
+
const summaryParts = data.summary.trim().split(/\s+/);
|
|
666
|
+
if (summaryParts.length < 7) {
|
|
667
|
+
throw new errors_1.MonitorError(`Process ${pid} not found`, errors_1.ErrorCode.NOT_AVAILABLE, this.platformName, { pid });
|
|
668
|
+
}
|
|
669
|
+
const [pidStr, ppidStr, rssStr, pcpu, pmem, state, user] = summaryParts;
|
|
670
|
+
const command = data.command.trim();
|
|
671
|
+
const lstart = data.start.trim();
|
|
672
|
+
return {
|
|
673
|
+
pid: this.safeParseInt(pidStr) || pid,
|
|
674
|
+
ppid: this.safeParseInt(ppidStr),
|
|
675
|
+
name: command.split(' ')[0] || command,
|
|
676
|
+
command,
|
|
677
|
+
cpuUsage: this.safeParseNumber(pcpu),
|
|
678
|
+
memoryUsage: this.safeParseInt(rssStr) * 1024,
|
|
679
|
+
memoryPercentage: this.safeParseNumber(pmem),
|
|
680
|
+
state,
|
|
681
|
+
user,
|
|
682
|
+
startTime: lstart
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* 解析 uname -a, uptime, sysctl -n vm.loadavg, sw_vers 输出为系统信息
|
|
687
|
+
*/
|
|
688
|
+
parseSystemInfo(uname, uptime, loadavg, osVersion) {
|
|
689
|
+
const unameFields = uname.trim().split(' ');
|
|
690
|
+
const uptimeSeconds = os_1.default.uptime();
|
|
691
|
+
const bootTime = Date.now() - uptimeSeconds * 1000;
|
|
692
|
+
const load = this.parseLoadAverage(loadavg);
|
|
693
|
+
const uptimeMs = uptimeSeconds * 1000;
|
|
694
|
+
return {
|
|
695
|
+
hostname: unameFields[1] || 'Unknown',
|
|
696
|
+
platform: 'darwin',
|
|
697
|
+
release: unameFields[2] || 'Unknown',
|
|
698
|
+
version: osVersion ? osVersion.trim() : unameFields[3] || 'Unknown',
|
|
699
|
+
arch: unameFields[4] || 'Unknown',
|
|
700
|
+
uptime: uptimeMs,
|
|
701
|
+
uptimeSeconds,
|
|
702
|
+
bootTime,
|
|
703
|
+
loadAverage: load
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* 解析 sysctl -n vm.loadavg 输出为系统负载
|
|
708
|
+
*/
|
|
709
|
+
parseLoadAverage(output) {
|
|
710
|
+
const match = output.trim().match(/\{\s*([\d.]+)\s+([\d.]+)\s+([\d.]+)\s*\}/);
|
|
711
|
+
if (match) {
|
|
712
|
+
return {
|
|
713
|
+
load1: this.safeParseNumber(match[1]),
|
|
714
|
+
load5: this.safeParseNumber(match[2]),
|
|
715
|
+
load15: this.safeParseNumber(match[3])
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
throw this.createParseError(output, 'Unable to parse load average');
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* 解析 df -h 输出的大小格式转换
|
|
722
|
+
*/
|
|
723
|
+
convertDfSizeToBytes(sizeStr) {
|
|
724
|
+
// df -h 输出的大小格式转换
|
|
725
|
+
const match = sizeStr.match(/^([\d.]+)([KMGT]?)i?$/);
|
|
726
|
+
if (!match)
|
|
727
|
+
return 0;
|
|
728
|
+
const value = this.safeParseNumber(match[1]);
|
|
729
|
+
const unit = match[2];
|
|
730
|
+
const multipliers = {
|
|
731
|
+
'': 1024, // df 默认单位是 KB
|
|
732
|
+
'K': 1024,
|
|
733
|
+
'M': 1024 * 1024,
|
|
734
|
+
'G': 1024 * 1024 * 1024,
|
|
735
|
+
'T': 1024 * 1024 * 1024 * 1024
|
|
736
|
+
};
|
|
737
|
+
return value * (multipliers[unit] || 1024);
|
|
738
|
+
}
|
|
739
|
+
// 实现抽象方法,获取结构化数据
|
|
740
|
+
/**
|
|
741
|
+
* 获取磁盘使用情况,解析 df -k 输出为磁盘使用情况
|
|
742
|
+
*/
|
|
743
|
+
async getDiskUsage() {
|
|
744
|
+
try {
|
|
745
|
+
const result = await this.executeCommand('df -k');
|
|
746
|
+
return this.parseDiskUsage(result.stdout);
|
|
747
|
+
}
|
|
748
|
+
catch (error) {
|
|
749
|
+
throw this.createCommandError('getDiskUsage', error);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
/**
|
|
753
|
+
* 获取磁盘统计,解析 iostat -d 输出为磁盘统计
|
|
754
|
+
*/
|
|
755
|
+
async getDiskStats() {
|
|
756
|
+
try {
|
|
757
|
+
const result = await this.executeCommand('iostat -d');
|
|
758
|
+
return this.parseDiskStats(result.stdout);
|
|
759
|
+
}
|
|
760
|
+
catch (error) {
|
|
761
|
+
throw this.createCommandError('getDiskStats', error);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
/**
|
|
765
|
+
* 获取挂载点,解析 mount 输出为挂载点
|
|
766
|
+
*/
|
|
767
|
+
async getMounts() {
|
|
768
|
+
try {
|
|
769
|
+
const result = await this.executeCommand('mount');
|
|
770
|
+
return this.parseMounts(result.stdout);
|
|
771
|
+
}
|
|
772
|
+
catch (error) {
|
|
773
|
+
throw this.createCommandError('getMounts', error);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
/**
|
|
777
|
+
* 获取文件系统,解析 diskutil list 输出为文件系统
|
|
778
|
+
*/
|
|
779
|
+
async getFileSystems() {
|
|
780
|
+
try {
|
|
781
|
+
const result = await this.executeCommand('diskutil list');
|
|
782
|
+
return this.parseFileSystems(result.stdout);
|
|
783
|
+
}
|
|
784
|
+
catch (error) {
|
|
785
|
+
throw this.createCommandError('getFileSystems', error);
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
/**
|
|
789
|
+
* 获取网络连接,解析 netstat -an 输出为网络连接
|
|
790
|
+
*/
|
|
791
|
+
async getNetworkConnections() {
|
|
792
|
+
try {
|
|
793
|
+
const result = await this.executeCommand('netstat -an');
|
|
794
|
+
return this.parseNetworkConnections(result.stdout);
|
|
795
|
+
}
|
|
796
|
+
catch (error) {
|
|
797
|
+
throw this.createCommandError('getNetworkConnections', error);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
/**
|
|
801
|
+
* 获取默认网关,解析 route -n get default 输出为默认网关
|
|
802
|
+
*/
|
|
803
|
+
async getDefaultGateway() {
|
|
804
|
+
try {
|
|
805
|
+
const result = await this.executeCommand('route -n get default');
|
|
806
|
+
return this.parseDefaultGateway(result.stdout);
|
|
807
|
+
}
|
|
808
|
+
catch (error) {
|
|
809
|
+
throw this.createCommandError('getDefaultGateway', error);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
/**
|
|
813
|
+
* 获取进程列表,解析 ps -axo pid=,ppid=,comm=,%cpu=,%mem=,rss=,stat=,user=,args= 输出为进程列表
|
|
814
|
+
*/
|
|
815
|
+
async getProcessList() {
|
|
816
|
+
try {
|
|
817
|
+
const result = await this.executeCommand('ps -axo pid=,ppid=,comm=,%cpu=,%mem=,rss=,stat=,user=,args=');
|
|
818
|
+
return this.parseProcessList(result.stdout);
|
|
819
|
+
}
|
|
820
|
+
catch (error) {
|
|
821
|
+
throw this.createCommandError('getProcessList', error);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
/**
|
|
825
|
+
* 杀死进程,解析 kill -${signal} ${pid} 输出为杀死进程
|
|
826
|
+
*/
|
|
827
|
+
async killProcess(pid, signal = 'TERM') {
|
|
828
|
+
try {
|
|
829
|
+
const result = await this.executeCommand(`kill -${signal} ${pid}`);
|
|
830
|
+
return result.exitCode === 0;
|
|
831
|
+
}
|
|
832
|
+
catch (error) {
|
|
833
|
+
return false;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
/**
|
|
837
|
+
* 获取进程打开文件,解析 lsof -p ${pid} +c0 -Fn 输出为进程打开文件
|
|
838
|
+
*/
|
|
839
|
+
async getProcessOpenFiles(pid) {
|
|
840
|
+
try {
|
|
841
|
+
const result = await this.executeCommand(`lsof -p ${pid} +c0 -Fn`);
|
|
842
|
+
return this.parseOpenFiles(result.stdout);
|
|
843
|
+
}
|
|
844
|
+
catch (error) {
|
|
845
|
+
return [];
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
/**
|
|
849
|
+
* 获取进程环境变量,解析 ps eww ${pid} 输出为进程环境变量
|
|
850
|
+
*/
|
|
851
|
+
async getProcessEnvironment(pid) {
|
|
852
|
+
try {
|
|
853
|
+
const result = await this.executeCommand(`ps eww ${pid}`);
|
|
854
|
+
return this.parseEnvironment(result.stdout);
|
|
855
|
+
}
|
|
856
|
+
catch (error) {
|
|
857
|
+
return {};
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
/**
|
|
861
|
+
* 获取系统运行时间
|
|
862
|
+
*/
|
|
863
|
+
async getSystemUptime() {
|
|
864
|
+
try {
|
|
865
|
+
const uptimeSeconds = os_1.default.uptime();
|
|
866
|
+
return {
|
|
867
|
+
uptime: uptimeSeconds,
|
|
868
|
+
bootTime: Date.now() - uptimeSeconds * 1000
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
catch (error) {
|
|
872
|
+
throw this.createCommandError('getSystemUptime', error);
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
/**
|
|
876
|
+
* 获取系统用户,解析 who 输出为系统用户
|
|
877
|
+
*/
|
|
878
|
+
async getSystemUsers() {
|
|
879
|
+
try {
|
|
880
|
+
const result = await this.executeCommand('who');
|
|
881
|
+
return this.parseSystemUsers(result.stdout);
|
|
882
|
+
}
|
|
883
|
+
catch (error) {
|
|
884
|
+
throw this.createCommandError('getSystemUsers', error);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
/**
|
|
888
|
+
* 获取系统服务,解析 launchctl list 输出为系统服务
|
|
889
|
+
*/
|
|
890
|
+
async getSystemServices() {
|
|
891
|
+
try {
|
|
892
|
+
const result = await this.executeCommand('launchctl list');
|
|
893
|
+
return this.parseSystemServices(result.stdout);
|
|
894
|
+
}
|
|
895
|
+
catch (error) {
|
|
896
|
+
throw this.createCommandError('getSystemServices', error);
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
// 私有解析方法,解析命令输出为结构化数据
|
|
900
|
+
parseDiskUsage(output) {
|
|
901
|
+
const lines = output.split('\n').slice(1); // 跳过标题行
|
|
902
|
+
const disks = [];
|
|
903
|
+
for (const line of lines) {
|
|
904
|
+
const fields = line.trim().split(/\s+/);
|
|
905
|
+
if (fields.length >= 9) {
|
|
906
|
+
const usedPercent = fields[4].replace('%', '');
|
|
907
|
+
disks.push({
|
|
908
|
+
device: fields[0],
|
|
909
|
+
total: this.safeParseInt(fields[1]) * 1024, // KB to bytes
|
|
910
|
+
used: this.safeParseInt(fields[2]) * 1024,
|
|
911
|
+
available: this.safeParseInt(fields[3]) * 1024,
|
|
912
|
+
usagePercentage: this.safeParseNumber(usedPercent),
|
|
913
|
+
mountPoint: fields[8]
|
|
914
|
+
});
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
return disks;
|
|
918
|
+
}
|
|
919
|
+
/**
|
|
920
|
+
* 解析 iostat -d 输出为磁盘统计
|
|
921
|
+
*/
|
|
922
|
+
parseDiskStats(output) {
|
|
923
|
+
return this.parseIostatDisks(output).map(item => ({
|
|
924
|
+
device: item.device,
|
|
925
|
+
readBytes: item.bytePerSec,
|
|
926
|
+
writeBytes: 0,
|
|
927
|
+
readCount: item.transfersPerSec,
|
|
928
|
+
writeCount: 0,
|
|
929
|
+
readTime: 0,
|
|
930
|
+
writeTime: 0,
|
|
931
|
+
ioTime: 0,
|
|
932
|
+
readSpeed: item.bytePerSec,
|
|
933
|
+
writeSpeed: 0,
|
|
934
|
+
iops: item.transfersPerSec
|
|
935
|
+
}));
|
|
936
|
+
}
|
|
937
|
+
/**
|
|
938
|
+
* 解析 mount 输出为挂载点
|
|
939
|
+
*/
|
|
940
|
+
parseMounts(output) {
|
|
941
|
+
const lines = output.split('\n').filter(line => line.trim());
|
|
942
|
+
const mounts = [];
|
|
943
|
+
for (const line of lines) {
|
|
944
|
+
const match = line.match(/^(.+?)\s+on\s+(.+?)\s+\((.+?)\)$/);
|
|
945
|
+
if (match) {
|
|
946
|
+
const [, device, mountPoint, options] = match;
|
|
947
|
+
const optionsArray = options.split(',').map(opt => opt.trim());
|
|
948
|
+
mounts.push({
|
|
949
|
+
device: device.trim(),
|
|
950
|
+
mountPoint: mountPoint.trim(),
|
|
951
|
+
filesystem: optionsArray[0] || 'unknown',
|
|
952
|
+
options: optionsArray,
|
|
953
|
+
dump: 0,
|
|
954
|
+
pass: 0
|
|
955
|
+
});
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
return mounts;
|
|
959
|
+
}
|
|
960
|
+
/**
|
|
961
|
+
* 解析 diskutil list 输出为文件系统
|
|
962
|
+
*/
|
|
963
|
+
parseFileSystems(output) {
|
|
964
|
+
const lines = output.split('\n').filter(line => line.trim());
|
|
965
|
+
const filesystems = [];
|
|
966
|
+
for (const line of lines) {
|
|
967
|
+
if (line.includes('GUID_partition_scheme') || line.includes('Apple_HFS') ||
|
|
968
|
+
line.includes('Apple_APFS') || line.includes('Microsoft Basic Data')) {
|
|
969
|
+
const parts = line.trim().split(/\s+/);
|
|
970
|
+
if (parts.length >= 3) {
|
|
971
|
+
filesystems.push({
|
|
972
|
+
name: parts[2] || 'unknown',
|
|
973
|
+
type: parts[1] || 'unknown',
|
|
974
|
+
supported: true
|
|
975
|
+
});
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
return filesystems;
|
|
980
|
+
}
|
|
981
|
+
/**
|
|
982
|
+
* 解析 netstat -an 输出为网络连接
|
|
983
|
+
*/
|
|
984
|
+
parseNetworkConnections(output) {
|
|
985
|
+
const lines = output.split('\n').filter(line => line.trim());
|
|
986
|
+
const connections = [];
|
|
987
|
+
for (const line of lines) {
|
|
988
|
+
const fields = line.trim().split(/\s+/);
|
|
989
|
+
if (fields.length >= 6 && (fields[0].includes('tcp') || fields[0].includes('udp'))) {
|
|
990
|
+
connections.push({
|
|
991
|
+
protocol: fields[0],
|
|
992
|
+
localAddress: fields[3] || '*',
|
|
993
|
+
foreignAddress: fields[4] || '*',
|
|
994
|
+
state: fields[5] || 'unknown'
|
|
995
|
+
});
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
return connections;
|
|
999
|
+
}
|
|
1000
|
+
/**
|
|
1001
|
+
* 解析 route -n get default 输出为默认网关
|
|
1002
|
+
*/
|
|
1003
|
+
parseDefaultGateway(output) {
|
|
1004
|
+
const lines = output.split('\n');
|
|
1005
|
+
let gateway = null;
|
|
1006
|
+
let interfaceName = null;
|
|
1007
|
+
for (const line of lines) {
|
|
1008
|
+
if (line.includes('gateway:')) {
|
|
1009
|
+
const match = line.match(/gateway:\s*(.+)/);
|
|
1010
|
+
if (match)
|
|
1011
|
+
gateway = match[1].trim();
|
|
1012
|
+
}
|
|
1013
|
+
if (line.includes('interface:')) {
|
|
1014
|
+
const match = line.match(/interface:\s*(.+)/);
|
|
1015
|
+
if (match)
|
|
1016
|
+
interfaceName = match[1].trim();
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
return gateway ? { gateway, interface: interfaceName || 'unknown' } : null;
|
|
1020
|
+
}
|
|
1021
|
+
/**
|
|
1022
|
+
* 解析 lsof -p ${pid} +c0 -Fn 输出为进程打开文件
|
|
1023
|
+
*/
|
|
1024
|
+
parseOpenFiles(output) {
|
|
1025
|
+
const lines = output.split('\n');
|
|
1026
|
+
const files = [];
|
|
1027
|
+
for (const line of lines) {
|
|
1028
|
+
if (line.startsWith('n')) {
|
|
1029
|
+
files.push(line.substring(1));
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
return files;
|
|
1033
|
+
}
|
|
1034
|
+
/**
|
|
1035
|
+
* 解析 ps eww ${pid} 输出为进程环境变量
|
|
1036
|
+
*/
|
|
1037
|
+
parseEnvironment(output) {
|
|
1038
|
+
const env = {};
|
|
1039
|
+
if (!output) {
|
|
1040
|
+
return env;
|
|
1041
|
+
}
|
|
1042
|
+
const pattern = /(^|\s)([A-Za-z_][A-Za-z0-9_]*)=([\s\S]*?)(?=(\s+[A-Za-z_][A-Za-z0-9_]*=)|$)/g;
|
|
1043
|
+
let match;
|
|
1044
|
+
while ((match = pattern.exec(output)) !== null) {
|
|
1045
|
+
const key = match[2];
|
|
1046
|
+
if (key === 'PID') {
|
|
1047
|
+
continue;
|
|
1048
|
+
}
|
|
1049
|
+
const value = match[3].replace(/\s+$/u, '');
|
|
1050
|
+
env[key] = value;
|
|
1051
|
+
}
|
|
1052
|
+
return env;
|
|
1053
|
+
}
|
|
1054
|
+
parseIostatDisks(output) {
|
|
1055
|
+
const lines = output.split('\n').map(line => line.trimEnd());
|
|
1056
|
+
const deviceLineIndex = lines.findIndex(line => /disk\w+/i.test(line));
|
|
1057
|
+
if (deviceLineIndex === -1) {
|
|
1058
|
+
return [];
|
|
1059
|
+
}
|
|
1060
|
+
const deviceLine = lines[deviceLineIndex];
|
|
1061
|
+
const deviceNames = (deviceLine.match(/disk[^\s]*/gi) || []).map(name => name.trim());
|
|
1062
|
+
if (deviceNames.length === 0) {
|
|
1063
|
+
return [];
|
|
1064
|
+
}
|
|
1065
|
+
const dataLines = lines.slice(deviceLineIndex + 1).filter(line => line && /[\d.]/.test(line));
|
|
1066
|
+
if (dataLines.length === 0) {
|
|
1067
|
+
return [];
|
|
1068
|
+
}
|
|
1069
|
+
const tokens = dataLines[0].trim().split(/\s+/);
|
|
1070
|
+
const metricsPerDevice = 3;
|
|
1071
|
+
const result = [];
|
|
1072
|
+
deviceNames.forEach((device, index) => {
|
|
1073
|
+
const offset = index * metricsPerDevice;
|
|
1074
|
+
if (tokens.length >= offset + metricsPerDevice) {
|
|
1075
|
+
const kbPerTransfer = this.safeParseNumber(tokens[offset]);
|
|
1076
|
+
const transfersPerSec = this.safeParseNumber(tokens[offset + 1]);
|
|
1077
|
+
const mbPerSec = this.safeParseNumber(tokens[offset + 2]);
|
|
1078
|
+
const bytePerSec = mbPerSec * 1024 * 1024;
|
|
1079
|
+
result.push({
|
|
1080
|
+
device,
|
|
1081
|
+
kbPerTransfer,
|
|
1082
|
+
transfersPerSec,
|
|
1083
|
+
mbPerSec,
|
|
1084
|
+
bytePerSec
|
|
1085
|
+
});
|
|
1086
|
+
}
|
|
1087
|
+
});
|
|
1088
|
+
return result;
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* 解析 uptime 输出为系统运行时间
|
|
1092
|
+
*/
|
|
1093
|
+
parseSystemUptime(output) {
|
|
1094
|
+
const uptimeMatch = output.match(/up\s+(.+?),/);
|
|
1095
|
+
const loadMatch = output.match(/load averages:\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)/);
|
|
1096
|
+
return {
|
|
1097
|
+
uptime: uptimeMatch ? uptimeMatch[1].trim() : 'unknown',
|
|
1098
|
+
loadAverage: loadMatch ? {
|
|
1099
|
+
load1: this.safeParseNumber(loadMatch[1]),
|
|
1100
|
+
load5: this.safeParseNumber(loadMatch[2]),
|
|
1101
|
+
load15: this.safeParseNumber(loadMatch[3])
|
|
1102
|
+
} : { load1: 0, load5: 0, load15: 0 }
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
1105
|
+
/**
|
|
1106
|
+
* 解析 who 输出为系统用户
|
|
1107
|
+
*/
|
|
1108
|
+
parseSystemUsers(output) {
|
|
1109
|
+
const lines = output.split('\n').filter(line => line.trim());
|
|
1110
|
+
const users = [];
|
|
1111
|
+
for (const line of lines) {
|
|
1112
|
+
const fields = line.trim().split(/\s+/);
|
|
1113
|
+
if (fields.length >= 5) {
|
|
1114
|
+
users.push({
|
|
1115
|
+
user: fields[0],
|
|
1116
|
+
terminal: fields[1],
|
|
1117
|
+
loginTime: fields.slice(2, -1).join(' '),
|
|
1118
|
+
from: fields[fields.length - 1]
|
|
1119
|
+
});
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
return users;
|
|
1123
|
+
}
|
|
1124
|
+
/**
|
|
1125
|
+
* 解析 launchctl list 输出为系统服务
|
|
1126
|
+
*/
|
|
1127
|
+
parseSystemServices(output) {
|
|
1128
|
+
const lines = output.split('\n').slice(1); // 跳过标题行
|
|
1129
|
+
const services = [];
|
|
1130
|
+
for (const line of lines) {
|
|
1131
|
+
const fields = line.trim().split(/\s+/);
|
|
1132
|
+
if (fields.length >= 3) {
|
|
1133
|
+
services.push({
|
|
1134
|
+
pid: fields[0] === '-' ? null : this.safeParseInt(fields[0]),
|
|
1135
|
+
status: fields[1],
|
|
1136
|
+
label: fields[2]
|
|
1137
|
+
});
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
return services;
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
exports.MacOSAdapter = MacOSAdapter;
|
|
1144
|
+
//# sourceMappingURL=macos-adapter.js.map
|