@superdangerous/app-framework 4.9.0

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.
Files changed (239) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +652 -0
  3. package/dist/api/logsRouter.d.ts +20 -0
  4. package/dist/api/logsRouter.d.ts.map +1 -0
  5. package/dist/api/logsRouter.js +515 -0
  6. package/dist/api/logsRouter.js.map +1 -0
  7. package/dist/cli/dev-server.d.ts +7 -0
  8. package/dist/cli/dev-server.d.ts.map +1 -0
  9. package/dist/cli/dev-server.js +640 -0
  10. package/dist/cli/dev-server.js.map +1 -0
  11. package/dist/cli/index.d.ts +7 -0
  12. package/dist/cli/index.d.ts.map +1 -0
  13. package/dist/cli/index.js +26 -0
  14. package/dist/cli/index.js.map +1 -0
  15. package/dist/core/StandardServer.d.ts +129 -0
  16. package/dist/core/StandardServer.d.ts.map +1 -0
  17. package/dist/core/StandardServer.js +453 -0
  18. package/dist/core/StandardServer.js.map +1 -0
  19. package/dist/core/apiResponse.d.ts +69 -0
  20. package/dist/core/apiResponse.d.ts.map +1 -0
  21. package/dist/core/apiResponse.js +127 -0
  22. package/dist/core/apiResponse.js.map +1 -0
  23. package/dist/core/healthCheck.d.ts +160 -0
  24. package/dist/core/healthCheck.d.ts.map +1 -0
  25. package/dist/core/healthCheck.js +398 -0
  26. package/dist/core/healthCheck.js.map +1 -0
  27. package/dist/core/index.d.ts +40 -0
  28. package/dist/core/index.d.ts.map +1 -0
  29. package/dist/core/index.js +40 -0
  30. package/dist/core/index.js.map +1 -0
  31. package/dist/core/logger.d.ts +117 -0
  32. package/dist/core/logger.d.ts.map +1 -0
  33. package/dist/core/logger.js +826 -0
  34. package/dist/core/logger.js.map +1 -0
  35. package/dist/core/portUtils.d.ts +71 -0
  36. package/dist/core/portUtils.d.ts.map +1 -0
  37. package/dist/core/portUtils.js +240 -0
  38. package/dist/core/portUtils.js.map +1 -0
  39. package/dist/core/storageService.d.ts +119 -0
  40. package/dist/core/storageService.d.ts.map +1 -0
  41. package/dist/core/storageService.js +405 -0
  42. package/dist/core/storageService.js.map +1 -0
  43. package/dist/desktop/bundler.d.ts +40 -0
  44. package/dist/desktop/bundler.d.ts.map +1 -0
  45. package/dist/desktop/bundler.js +176 -0
  46. package/dist/desktop/bundler.js.map +1 -0
  47. package/dist/desktop/index.d.ts +25 -0
  48. package/dist/desktop/index.d.ts.map +1 -0
  49. package/dist/desktop/index.js +15 -0
  50. package/dist/desktop/index.js.map +1 -0
  51. package/dist/desktop/native-modules.d.ts +66 -0
  52. package/dist/desktop/native-modules.d.ts.map +1 -0
  53. package/dist/desktop/native-modules.js +200 -0
  54. package/dist/desktop/native-modules.js.map +1 -0
  55. package/dist/index.d.ts +29 -0
  56. package/dist/index.d.ts.map +1 -0
  57. package/dist/index.js +39 -0
  58. package/dist/index.js.map +1 -0
  59. package/dist/logging/LogCategories.d.ts +87 -0
  60. package/dist/logging/LogCategories.d.ts.map +1 -0
  61. package/dist/logging/LogCategories.js +205 -0
  62. package/dist/logging/LogCategories.js.map +1 -0
  63. package/dist/middleware/aiErrorHandler.d.ts +31 -0
  64. package/dist/middleware/aiErrorHandler.d.ts.map +1 -0
  65. package/dist/middleware/aiErrorHandler.js +181 -0
  66. package/dist/middleware/aiErrorHandler.js.map +1 -0
  67. package/dist/middleware/auth.d.ts +101 -0
  68. package/dist/middleware/auth.d.ts.map +1 -0
  69. package/dist/middleware/auth.js +230 -0
  70. package/dist/middleware/auth.js.map +1 -0
  71. package/dist/middleware/cors.d.ts +56 -0
  72. package/dist/middleware/cors.d.ts.map +1 -0
  73. package/dist/middleware/cors.js +123 -0
  74. package/dist/middleware/cors.js.map +1 -0
  75. package/dist/middleware/errorHandler.d.ts +13 -0
  76. package/dist/middleware/errorHandler.d.ts.map +1 -0
  77. package/dist/middleware/errorHandler.js +85 -0
  78. package/dist/middleware/errorHandler.js.map +1 -0
  79. package/dist/middleware/fileUpload.d.ts +62 -0
  80. package/dist/middleware/fileUpload.d.ts.map +1 -0
  81. package/dist/middleware/fileUpload.js +175 -0
  82. package/dist/middleware/fileUpload.js.map +1 -0
  83. package/dist/middleware/health.d.ts +48 -0
  84. package/dist/middleware/health.d.ts.map +1 -0
  85. package/dist/middleware/health.js +143 -0
  86. package/dist/middleware/health.js.map +1 -0
  87. package/dist/middleware/index.d.ts +20 -0
  88. package/dist/middleware/index.d.ts.map +1 -0
  89. package/dist/middleware/index.js +18 -0
  90. package/dist/middleware/index.js.map +1 -0
  91. package/dist/middleware/openapi.d.ts +64 -0
  92. package/dist/middleware/openapi.d.ts.map +1 -0
  93. package/dist/middleware/openapi.js +258 -0
  94. package/dist/middleware/openapi.js.map +1 -0
  95. package/dist/middleware/requestLogging.d.ts +22 -0
  96. package/dist/middleware/requestLogging.d.ts.map +1 -0
  97. package/dist/middleware/requestLogging.js +61 -0
  98. package/dist/middleware/requestLogging.js.map +1 -0
  99. package/dist/middleware/session.d.ts +84 -0
  100. package/dist/middleware/session.d.ts.map +1 -0
  101. package/dist/middleware/session.js +189 -0
  102. package/dist/middleware/session.js.map +1 -0
  103. package/dist/middleware/validation.d.ts +1337 -0
  104. package/dist/middleware/validation.d.ts.map +1 -0
  105. package/dist/middleware/validation.js +483 -0
  106. package/dist/middleware/validation.js.map +1 -0
  107. package/dist/services/aiService.d.ts +180 -0
  108. package/dist/services/aiService.d.ts.map +1 -0
  109. package/dist/services/aiService.js +547 -0
  110. package/dist/services/aiService.js.map +1 -0
  111. package/dist/services/conversationStorage.d.ts +38 -0
  112. package/dist/services/conversationStorage.d.ts.map +1 -0
  113. package/dist/services/conversationStorage.js +158 -0
  114. package/dist/services/conversationStorage.js.map +1 -0
  115. package/dist/services/crossPlatformBuffer.d.ts +84 -0
  116. package/dist/services/crossPlatformBuffer.d.ts.map +1 -0
  117. package/dist/services/crossPlatformBuffer.js +246 -0
  118. package/dist/services/crossPlatformBuffer.js.map +1 -0
  119. package/dist/services/index.d.ts +17 -0
  120. package/dist/services/index.d.ts.map +1 -0
  121. package/dist/services/index.js +18 -0
  122. package/dist/services/index.js.map +1 -0
  123. package/dist/services/networkService.d.ts +81 -0
  124. package/dist/services/networkService.d.ts.map +1 -0
  125. package/dist/services/networkService.js +268 -0
  126. package/dist/services/networkService.js.map +1 -0
  127. package/dist/services/queueService.d.ts +112 -0
  128. package/dist/services/queueService.d.ts.map +1 -0
  129. package/dist/services/queueService.js +338 -0
  130. package/dist/services/queueService.js.map +1 -0
  131. package/dist/services/settingsService.d.ts +135 -0
  132. package/dist/services/settingsService.d.ts.map +1 -0
  133. package/dist/services/settingsService.js +425 -0
  134. package/dist/services/settingsService.js.map +1 -0
  135. package/dist/services/systemMonitor.d.ts +208 -0
  136. package/dist/services/systemMonitor.d.ts.map +1 -0
  137. package/dist/services/systemMonitor.js +693 -0
  138. package/dist/services/systemMonitor.js.map +1 -0
  139. package/dist/services/updateService.d.ts +78 -0
  140. package/dist/services/updateService.d.ts.map +1 -0
  141. package/dist/services/updateService.js +252 -0
  142. package/dist/services/updateService.js.map +1 -0
  143. package/dist/services/websocketEvents.d.ts +372 -0
  144. package/dist/services/websocketEvents.d.ts.map +1 -0
  145. package/dist/services/websocketEvents.js +338 -0
  146. package/dist/services/websocketEvents.js.map +1 -0
  147. package/dist/services/websocketServer.d.ts +80 -0
  148. package/dist/services/websocketServer.d.ts.map +1 -0
  149. package/dist/services/websocketServer.js +299 -0
  150. package/dist/services/websocketServer.js.map +1 -0
  151. package/dist/settings/SettingsSchema.d.ts +151 -0
  152. package/dist/settings/SettingsSchema.d.ts.map +1 -0
  153. package/dist/settings/SettingsSchema.js +424 -0
  154. package/dist/settings/SettingsSchema.js.map +1 -0
  155. package/dist/testing/TestServer.d.ts +69 -0
  156. package/dist/testing/TestServer.d.ts.map +1 -0
  157. package/dist/testing/TestServer.js +250 -0
  158. package/dist/testing/TestServer.js.map +1 -0
  159. package/dist/types/index.d.ts +137 -0
  160. package/dist/types/index.d.ts.map +1 -0
  161. package/dist/types/index.js +5 -0
  162. package/dist/types/index.js.map +1 -0
  163. package/dist/utils/appPaths.d.ts +74 -0
  164. package/dist/utils/appPaths.d.ts.map +1 -0
  165. package/dist/utils/appPaths.js +162 -0
  166. package/dist/utils/appPaths.js.map +1 -0
  167. package/dist/utils/fs-utils.d.ts +50 -0
  168. package/dist/utils/fs-utils.d.ts.map +1 -0
  169. package/dist/utils/fs-utils.js +114 -0
  170. package/dist/utils/fs-utils.js.map +1 -0
  171. package/dist/utils/index.d.ts +12 -0
  172. package/dist/utils/index.d.ts.map +1 -0
  173. package/dist/utils/index.js +10 -0
  174. package/dist/utils/index.js.map +1 -0
  175. package/dist/utils/standardConfig.d.ts +61 -0
  176. package/dist/utils/standardConfig.d.ts.map +1 -0
  177. package/dist/utils/standardConfig.js +109 -0
  178. package/dist/utils/standardConfig.js.map +1 -0
  179. package/dist/utils/startupBanner.d.ts +34 -0
  180. package/dist/utils/startupBanner.d.ts.map +1 -0
  181. package/dist/utils/startupBanner.js +169 -0
  182. package/dist/utils/startupBanner.js.map +1 -0
  183. package/dist/utils/startupLogger.d.ts +45 -0
  184. package/dist/utils/startupLogger.d.ts.map +1 -0
  185. package/dist/utils/startupLogger.js +200 -0
  186. package/dist/utils/startupLogger.js.map +1 -0
  187. package/package.json +151 -0
  188. package/src/api/logsRouter.ts +600 -0
  189. package/src/cli/dev-server.ts +803 -0
  190. package/src/cli/index.ts +31 -0
  191. package/src/core/StandardServer.ts +587 -0
  192. package/src/core/apiResponse.ts +202 -0
  193. package/src/core/healthCheck.ts +565 -0
  194. package/src/core/index.ts +80 -0
  195. package/src/core/logger.ts +1092 -0
  196. package/src/core/portUtils.ts +319 -0
  197. package/src/core/storageService.ts +595 -0
  198. package/src/desktop/bundler.ts +271 -0
  199. package/src/desktop/index.ts +18 -0
  200. package/src/desktop/native-modules.ts +289 -0
  201. package/src/index.ts +142 -0
  202. package/src/logging/LogCategories.ts +302 -0
  203. package/src/middleware/aiErrorHandler.ts +278 -0
  204. package/src/middleware/auth.ts +329 -0
  205. package/src/middleware/cors.ts +187 -0
  206. package/src/middleware/errorHandler.ts +103 -0
  207. package/src/middleware/fileUpload.ts +252 -0
  208. package/src/middleware/health.ts +206 -0
  209. package/src/middleware/index.ts +71 -0
  210. package/src/middleware/openapi.ts +305 -0
  211. package/src/middleware/requestLogging.ts +92 -0
  212. package/src/middleware/session.ts +238 -0
  213. package/src/middleware/validation.ts +603 -0
  214. package/src/services/aiService.ts +789 -0
  215. package/src/services/conversationStorage.ts +232 -0
  216. package/src/services/crossPlatformBuffer.ts +341 -0
  217. package/src/services/index.ts +47 -0
  218. package/src/services/networkService.ts +351 -0
  219. package/src/services/queueService.ts +446 -0
  220. package/src/services/settingsService.ts +549 -0
  221. package/src/services/systemMonitor.ts +936 -0
  222. package/src/services/updateService.ts +334 -0
  223. package/src/services/websocketEvents.ts +409 -0
  224. package/src/services/websocketServer.ts +394 -0
  225. package/src/settings/SettingsSchema.ts +664 -0
  226. package/src/testing/TestServer.ts +312 -0
  227. package/src/types/index.ts +154 -0
  228. package/src/utils/appPaths.ts +196 -0
  229. package/src/utils/fs-utils.ts +130 -0
  230. package/src/utils/index.ts +15 -0
  231. package/src/utils/standardConfig.ts +178 -0
  232. package/src/utils/startupBanner.ts +287 -0
  233. package/src/utils/startupLogger.ts +268 -0
  234. package/ui/dist/index.d.mts +1221 -0
  235. package/ui/dist/index.d.ts +1221 -0
  236. package/ui/dist/index.js +73 -0
  237. package/ui/dist/index.js.map +1 -0
  238. package/ui/dist/index.mjs +73 -0
  239. package/ui/dist/index.mjs.map +1 -0
@@ -0,0 +1,936 @@
1
+ /**
2
+ * Enhanced System Monitor Service
3
+ * Provides comprehensive system health and performance metrics
4
+ */
5
+
6
+ import os from "os";
7
+ import fs from "fs";
8
+ import { promisify } from "util";
9
+ import { exec } from "child_process";
10
+ import { createLogger } from "../core/index.js";
11
+
12
+ const execAsync = promisify(exec);
13
+ const logger = createLogger("SystemMonitor");
14
+
15
+ export interface CPUInfo {
16
+ usage: number;
17
+ temperature?: number;
18
+ cores: number;
19
+ model: string;
20
+ speed: number;
21
+ loadAverage: number[];
22
+ }
23
+
24
+ export interface MemoryInfo {
25
+ total: number;
26
+ used: number;
27
+ free: number;
28
+ percentage: number;
29
+ available?: number;
30
+ active?: number;
31
+ inactive?: number;
32
+ buffers?: number;
33
+ cached?: number;
34
+ swap?: {
35
+ total: number;
36
+ used: number;
37
+ free: number;
38
+ percentage?: number;
39
+ };
40
+ process?: {
41
+ rss: number; // Resident Set Size
42
+ heapTotal: number; // V8 heap total
43
+ heapUsed: number; // V8 heap used
44
+ external: number; // C++ objects bound to JS
45
+ arrayBuffers: number;
46
+ };
47
+ breakdown?: {
48
+ apps: number; // Memory used by applications
49
+ pageCache: number; // Page cache
50
+ buffers: number; // Buffers
51
+ slab: number; // Kernel slab
52
+ kernelStack: number;
53
+ pageTables: number;
54
+ vmallocUsed: number;
55
+ };
56
+ }
57
+
58
+ export interface DiskInfo {
59
+ total: number;
60
+ used: number;
61
+ free: number;
62
+ percentage: number;
63
+ io?: {
64
+ readBytes: number;
65
+ writeBytes: number;
66
+ readOps: number;
67
+ writeOps: number;
68
+ };
69
+ }
70
+
71
+ export interface NetworkInterfaceInfo {
72
+ name: string;
73
+ addresses: string[];
74
+ mac: string;
75
+ speed?: number;
76
+ stats?: {
77
+ rxBytes: number;
78
+ txBytes: number;
79
+ rxPackets: number;
80
+ txPackets: number;
81
+ rxErrors: number;
82
+ txErrors: number;
83
+ };
84
+ }
85
+
86
+ export interface SystemInfo {
87
+ platform: string;
88
+ arch: string;
89
+ version: string;
90
+ kernel: string;
91
+ hostname: string;
92
+ uptime: number;
93
+ bootTime: Date;
94
+ }
95
+
96
+ export interface ProcessInfo {
97
+ pid: number;
98
+ name: string;
99
+ cpu: number;
100
+ memory: number;
101
+ uptime: number;
102
+ }
103
+
104
+ export interface SystemHealth {
105
+ cpu: CPUInfo;
106
+ memory: MemoryInfo;
107
+ disk: DiskInfo;
108
+ system: SystemInfo;
109
+ network: {
110
+ interfaces: NetworkInterfaceInfo[];
111
+ connections: number;
112
+ };
113
+ processes?: ProcessInfo[];
114
+ timestamp: Date;
115
+ }
116
+
117
+ class SystemMonitor {
118
+ private cpuUsageHistory: number[] = [];
119
+ private lastCPUInfo: any = null;
120
+ private diskIOCache: Map<string, any> = new Map();
121
+
122
+ /**
123
+ * Get comprehensive system health metrics
124
+ */
125
+ async getSystemHealth(): Promise<SystemHealth> {
126
+ const [cpu, memory, disk, system, network] = await Promise.all([
127
+ this.getCPUInfo(),
128
+ this.getMemoryInfo(),
129
+ this.getDiskInfo(),
130
+ this.getSystemInfo(),
131
+ this.getNetworkInfo(),
132
+ ]);
133
+
134
+ return {
135
+ cpu,
136
+ memory,
137
+ disk,
138
+ system,
139
+ network,
140
+ timestamp: new Date(),
141
+ };
142
+ }
143
+
144
+ /**
145
+ * Get CPU information including temperature
146
+ */
147
+ async getCPUInfo(): Promise<CPUInfo> {
148
+ const cpus = os.cpus();
149
+ const model = cpus[0]?.model || "Unknown";
150
+ const cores = cpus.length;
151
+ const speed = cpus[0]?.speed || 0;
152
+ const loadAverage = os.loadavg();
153
+
154
+ // Calculate CPU usage
155
+ const usage = await this.calculateCPUUsage();
156
+
157
+ // Try to get CPU temperature
158
+ const temperature = await this.getCPUTemperature();
159
+
160
+ return {
161
+ usage,
162
+ temperature,
163
+ cores,
164
+ model,
165
+ speed,
166
+ loadAverage,
167
+ };
168
+ }
169
+
170
+ /**
171
+ * Calculate CPU usage percentage
172
+ */
173
+ private async calculateCPUUsage(): Promise<number> {
174
+ const cpus = os.cpus();
175
+
176
+ let totalIdle = 0;
177
+ let totalTick = 0;
178
+
179
+ cpus.forEach((cpu) => {
180
+ for (const type in cpu.times) {
181
+ totalTick += cpu.times[type as keyof typeof cpu.times];
182
+ }
183
+ totalIdle += cpu.times.idle;
184
+ });
185
+
186
+ if (this.lastCPUInfo) {
187
+ const idleDiff = totalIdle - this.lastCPUInfo.idle;
188
+ const totalDiff = totalTick - this.lastCPUInfo.total;
189
+ const usage = Math.round(100 - (100 * idleDiff) / totalDiff);
190
+
191
+ this.cpuUsageHistory.push(usage);
192
+ if (this.cpuUsageHistory.length > 60) {
193
+ this.cpuUsageHistory.shift();
194
+ }
195
+
196
+ this.lastCPUInfo = { idle: totalIdle, total: totalTick };
197
+ return usage;
198
+ }
199
+
200
+ this.lastCPUInfo = { idle: totalIdle, total: totalTick };
201
+ return 0;
202
+ }
203
+
204
+ /**
205
+ * Get CPU temperature (platform-specific)
206
+ */
207
+ private async getCPUTemperature(): Promise<number | undefined> {
208
+ const platform = process.platform;
209
+
210
+ try {
211
+ if (platform === "darwin") {
212
+ // macOS - try using osx-temperature-sensor
213
+ const { stdout } = await execAsync(
214
+ 'sysctl -n machdep.xcpm.cpu_thermal_level 2>/dev/null || echo ""',
215
+ );
216
+ if (stdout.trim()) {
217
+ return parseFloat(stdout.trim());
218
+ }
219
+ } else if (platform === "linux") {
220
+ // Linux - read from thermal zone
221
+ const thermalZone = "/sys/class/thermal/thermal_zone0/temp";
222
+ if (fs.existsSync(thermalZone)) {
223
+ const temp = fs.readFileSync(thermalZone, "utf8");
224
+ return parseInt(temp) / 1000; // Convert from millidegrees
225
+ }
226
+ } else if (platform === "win32") {
227
+ // Windows - use wmic
228
+ const { stdout } = await execAsync(
229
+ "wmic /namespace:\\\\root\\wmi PATH MSAcpi_ThermalZoneTemperature get CurrentTemperature /value",
230
+ );
231
+ const match = stdout.match(/CurrentTemperature=(\d+)/);
232
+ if (match) {
233
+ // Convert from tenths of Kelvin to Celsius
234
+ return (parseInt(match[1]) - 2732) / 10;
235
+ }
236
+ }
237
+ } catch (_error) {
238
+ logger.debug("Could not get CPU temperature:", _error);
239
+ }
240
+
241
+ return undefined;
242
+ }
243
+
244
+ /**
245
+ * Get memory information including swap and detailed breakdown
246
+ */
247
+ async getMemoryInfo(): Promise<MemoryInfo> {
248
+ const totalMem = os.totalmem();
249
+ const freeMem = os.freemem();
250
+ const usedMem = totalMem - freeMem;
251
+ const percentage = Math.round((usedMem / totalMem) * 100);
252
+
253
+ const memInfo: MemoryInfo = {
254
+ total: totalMem,
255
+ used: usedMem,
256
+ free: freeMem,
257
+ percentage,
258
+ };
259
+
260
+ // Add process memory usage
261
+ const memUsage = process.memoryUsage();
262
+ memInfo.process = {
263
+ rss: memUsage.rss,
264
+ heapTotal: memUsage.heapTotal,
265
+ heapUsed: memUsage.heapUsed,
266
+ external: memUsage.external,
267
+ arrayBuffers: memUsage.arrayBuffers || 0,
268
+ };
269
+
270
+ // Try to get detailed memory information
271
+ const detailed = await this.getDetailedMemoryInfo();
272
+ if (detailed) {
273
+ Object.assign(memInfo, detailed);
274
+ }
275
+
276
+ // Try to get swap information
277
+ const swap = await this.getSwapInfo();
278
+ if (swap) {
279
+ memInfo.swap = {
280
+ ...swap,
281
+ percentage:
282
+ swap.total > 0 ? Math.round((swap.used / swap.total) * 100) : 0,
283
+ };
284
+ }
285
+
286
+ return memInfo;
287
+ }
288
+
289
+ /**
290
+ * Get detailed memory breakdown (Linux only)
291
+ */
292
+ private async getDetailedMemoryInfo(): Promise<
293
+ Partial<MemoryInfo> | undefined
294
+ > {
295
+ const platform = process.platform;
296
+
297
+ if (platform !== "linux") {
298
+ return undefined;
299
+ }
300
+
301
+ try {
302
+ const { stdout } = await execAsync("cat /proc/meminfo");
303
+ const lines = stdout.split("\n");
304
+ const meminfo: Record<string, number> = {};
305
+
306
+ for (const line of lines) {
307
+ const match = line.match(/^(\w+):\s+(\d+)\s+kB/);
308
+ if (match) {
309
+ meminfo[match[1]] = parseInt(match[2]) * 1024; // Convert to bytes
310
+ }
311
+ }
312
+
313
+ const result: Partial<MemoryInfo> = {};
314
+
315
+ // Available memory
316
+ if (meminfo.MemAvailable) {
317
+ result.available = meminfo.MemAvailable;
318
+ }
319
+
320
+ // Active/Inactive
321
+ if (meminfo.Active) result.active = meminfo.Active;
322
+ if (meminfo.Inactive) result.inactive = meminfo.Inactive;
323
+
324
+ // Buffers/Cached
325
+ if (meminfo.Buffers) result.buffers = meminfo.Buffers;
326
+ if (meminfo.Cached) result.cached = meminfo.Cached;
327
+
328
+ // Detailed breakdown
329
+ if (meminfo.Slab || meminfo.KernelStack || meminfo.PageTables) {
330
+ result.breakdown = {
331
+ apps:
332
+ meminfo.MemTotal -
333
+ meminfo.MemFree -
334
+ meminfo.Buffers -
335
+ meminfo.Cached || 0,
336
+ pageCache: meminfo.Cached || 0,
337
+ buffers: meminfo.Buffers || 0,
338
+ slab: meminfo.Slab || 0,
339
+ kernelStack: meminfo.KernelStack || 0,
340
+ pageTables: meminfo.PageTables || 0,
341
+ vmallocUsed: meminfo.VmallocUsed || 0,
342
+ };
343
+ }
344
+
345
+ return result;
346
+ } catch (_error) {
347
+ logger.debug("Could not get detailed memory info:", _error);
348
+ return undefined;
349
+ }
350
+ }
351
+
352
+ /**
353
+ * Get swap memory information
354
+ */
355
+ private async getSwapInfo(): Promise<
356
+ { total: number; used: number; free: number } | undefined
357
+ > {
358
+ const platform = process.platform;
359
+
360
+ try {
361
+ if (platform === "linux") {
362
+ const { stdout } = await execAsync("free -b | grep Swap");
363
+ const parts = stdout.trim().split(/\s+/);
364
+ if (parts.length >= 3) {
365
+ return {
366
+ total: parseInt(parts[1]),
367
+ used: parseInt(parts[2]),
368
+ free: parseInt(parts[3]),
369
+ };
370
+ }
371
+ } else if (platform === "darwin") {
372
+ const { stdout } = await execAsync("sysctl vm.swapusage");
373
+ const match = stdout.match(
374
+ /total = ([\d.]+)M.*used = ([\d.]+)M.*free = ([\d.]+)M/,
375
+ );
376
+ if (match) {
377
+ return {
378
+ total: parseFloat(match[1]) * 1024 * 1024,
379
+ used: parseFloat(match[2]) * 1024 * 1024,
380
+ free: parseFloat(match[3]) * 1024 * 1024,
381
+ };
382
+ }
383
+ }
384
+ } catch (_error) {
385
+ logger.debug("Could not get swap information:", _error);
386
+ }
387
+
388
+ return undefined;
389
+ }
390
+
391
+ /**
392
+ * Get disk information including I/O stats
393
+ */
394
+ async getDiskInfo(): Promise<DiskInfo> {
395
+ const platform = process.platform;
396
+ let diskInfo: DiskInfo = {
397
+ total: 0,
398
+ used: 0,
399
+ free: 0,
400
+ percentage: 0,
401
+ };
402
+
403
+ try {
404
+ if (platform === "darwin" || platform === "linux") {
405
+ const { stdout } = await execAsync("df -k / | tail -1");
406
+ const parts = stdout.trim().split(/\s+/);
407
+
408
+ if (parts.length >= 4) {
409
+ const total = parseInt(parts[1]) * 1024;
410
+ const used = parseInt(parts[2]) * 1024;
411
+ const free = parseInt(parts[3]) * 1024;
412
+ const percentage = Math.round((used / total) * 100);
413
+
414
+ diskInfo = { total, used, free, percentage };
415
+ }
416
+ } else if (platform === "win32") {
417
+ const { stdout } = await execAsync(
418
+ "wmic logicaldisk get size,freespace /value",
419
+ );
420
+ const lines = stdout.trim().split("\n");
421
+ let total = 0,
422
+ free = 0;
423
+
424
+ lines.forEach((line) => {
425
+ if (line.startsWith("FreeSpace=")) {
426
+ const value = line.split("=")[1];
427
+ if (value) free += parseInt(value);
428
+ } else if (line.startsWith("Size=")) {
429
+ const value = line.split("=")[1];
430
+ if (value) total += parseInt(value);
431
+ }
432
+ });
433
+
434
+ const used = total - free;
435
+ const percentage = total > 0 ? Math.round((used / total) * 100) : 0;
436
+
437
+ diskInfo = { total, used, free, percentage };
438
+ }
439
+
440
+ // Get I/O stats
441
+ const io = await this.getDiskIOStats();
442
+ if (io) {
443
+ diskInfo.io = io;
444
+ }
445
+ } catch (_error) {
446
+ logger.debug("Could not get disk information:", _error);
447
+ }
448
+
449
+ return diskInfo;
450
+ }
451
+
452
+ /**
453
+ * Get disk I/O statistics
454
+ */
455
+ private async getDiskIOStats(): Promise<any> {
456
+ const platform = process.platform;
457
+
458
+ try {
459
+ if (platform === "linux") {
460
+ const { stdout } = await execAsync(
461
+ 'cat /proc/diskstats | grep -E "sda |nvme0n1 " | head -1',
462
+ );
463
+ const parts = stdout.trim().split(/\s+/);
464
+
465
+ if (parts.length >= 10) {
466
+ const current = {
467
+ readOps: parseInt(parts[3]),
468
+ readBytes: parseInt(parts[5]) * 512,
469
+ writeOps: parseInt(parts[7]),
470
+ writeBytes: parseInt(parts[9]) * 512,
471
+ };
472
+
473
+ // Calculate rate if we have previous data
474
+ const cacheKey = "diskio";
475
+ const previous = this.diskIOCache.get(cacheKey);
476
+
477
+ if (previous) {
478
+ const timeDiff = Date.now() - previous.timestamp;
479
+ const rateFactor = 1000 / timeDiff; // Convert to per second
480
+
481
+ const io = {
482
+ readBytes: Math.round(
483
+ (current.readBytes - previous.readBytes) * rateFactor,
484
+ ),
485
+ writeBytes: Math.round(
486
+ (current.writeBytes - previous.writeBytes) * rateFactor,
487
+ ),
488
+ readOps: Math.round(
489
+ (current.readOps - previous.readOps) * rateFactor,
490
+ ),
491
+ writeOps: Math.round(
492
+ (current.writeOps - previous.writeOps) * rateFactor,
493
+ ),
494
+ };
495
+
496
+ this.diskIOCache.set(cacheKey, {
497
+ ...current,
498
+ timestamp: Date.now(),
499
+ });
500
+ return io;
501
+ }
502
+
503
+ this.diskIOCache.set(cacheKey, { ...current, timestamp: Date.now() });
504
+ }
505
+ } else if (platform === "darwin") {
506
+ const { stdout } = await execAsync("iostat -d -w 1 -c 2 | tail -1");
507
+ const parts = stdout.trim().split(/\s+/);
508
+
509
+ if (parts.length >= 3) {
510
+ return {
511
+ readBytes: parseFloat(parts[0]) * 1024,
512
+ writeBytes: parseFloat(parts[1]) * 1024,
513
+ readOps: 0,
514
+ writeOps: 0,
515
+ };
516
+ }
517
+ }
518
+ } catch (_error) {
519
+ logger.debug("Could not get disk I/O statistics:", _error);
520
+ }
521
+
522
+ return undefined;
523
+ }
524
+
525
+ /**
526
+ * Get system information
527
+ */
528
+ async getSystemInfo(): Promise<SystemInfo> {
529
+ const platform = process.platform;
530
+ const arch = process.arch;
531
+ const version = os.release();
532
+ const hostname = os.hostname();
533
+ const uptime = os.uptime();
534
+ const bootTime = new Date(Date.now() - uptime * 1000);
535
+
536
+ let kernel = version;
537
+
538
+ // Try to get more detailed kernel info
539
+ try {
540
+ if (platform === "linux" || platform === "darwin") {
541
+ const { stdout } = await execAsync("uname -r");
542
+ kernel = stdout.trim();
543
+ }
544
+ } catch {
545
+ // Use default
546
+ }
547
+
548
+ return {
549
+ platform,
550
+ arch,
551
+ version,
552
+ kernel,
553
+ hostname,
554
+ uptime,
555
+ bootTime,
556
+ };
557
+ }
558
+
559
+ /**
560
+ * Get network information
561
+ */
562
+ async getNetworkInfo(): Promise<{
563
+ interfaces: NetworkInterfaceInfo[];
564
+ connections: number;
565
+ }> {
566
+ const interfaces = await this.getNetworkInterfaces();
567
+ const connections = await this.getNetworkConnections();
568
+
569
+ return {
570
+ interfaces,
571
+ connections,
572
+ };
573
+ }
574
+
575
+ /**
576
+ * Get detailed network interface information
577
+ */
578
+ private async getNetworkInterfaces(): Promise<NetworkInterfaceInfo[]> {
579
+ const interfaces = os.networkInterfaces();
580
+ const result: NetworkInterfaceInfo[] = [];
581
+
582
+ for (const [name, addresses] of Object.entries(interfaces)) {
583
+ if (!addresses) continue;
584
+
585
+ const ipv4Addresses = addresses
586
+ .filter((addr) => addr.family === "IPv4")
587
+ .map((addr) => addr.address);
588
+
589
+ if (ipv4Addresses.length > 0) {
590
+ const interfaceInfo: NetworkInterfaceInfo = {
591
+ name,
592
+ addresses: ipv4Addresses,
593
+ mac: addresses[0]?.mac || "00:00:00:00:00:00",
594
+ };
595
+
596
+ // Try to get interface statistics
597
+ const stats = await this.getInterfaceStats(name);
598
+ if (stats) {
599
+ interfaceInfo.stats = stats;
600
+ }
601
+
602
+ result.push(interfaceInfo);
603
+ }
604
+ }
605
+
606
+ return result;
607
+ }
608
+
609
+ /**
610
+ * Get network interface statistics
611
+ */
612
+ private async getInterfaceStats(interfaceName: string): Promise<any> {
613
+ const platform = process.platform;
614
+
615
+ try {
616
+ if (platform === "linux") {
617
+ const statsFile = `/sys/class/net/${interfaceName}/statistics`;
618
+ if (fs.existsSync(statsFile)) {
619
+ const readFile = (file: string) => {
620
+ try {
621
+ return parseInt(
622
+ fs.readFileSync(`${statsFile}/${file}`, "utf8").trim(),
623
+ );
624
+ } catch {
625
+ return 0;
626
+ }
627
+ };
628
+
629
+ return {
630
+ rxBytes: readFile("rx_bytes"),
631
+ txBytes: readFile("tx_bytes"),
632
+ rxPackets: readFile("rx_packets"),
633
+ txPackets: readFile("tx_packets"),
634
+ rxErrors: readFile("rx_errors"),
635
+ txErrors: readFile("tx_errors"),
636
+ };
637
+ }
638
+ } else if (platform === "darwin") {
639
+ const { stdout } = await execAsync(
640
+ `netstat -ibn | grep -A 1 "^${interfaceName}"`,
641
+ );
642
+ const lines = stdout.trim().split("\n");
643
+
644
+ if (lines.length >= 2) {
645
+ const parts = lines[1].trim().split(/\s+/);
646
+ if (parts.length >= 10) {
647
+ return {
648
+ rxPackets: parseInt(parts[4]),
649
+ rxErrors: parseInt(parts[5]),
650
+ rxBytes: parseInt(parts[6]),
651
+ txPackets: parseInt(parts[7]),
652
+ txErrors: parseInt(parts[8]),
653
+ txBytes: parseInt(parts[9]),
654
+ };
655
+ }
656
+ }
657
+ }
658
+ } catch (_error) {
659
+ logger.debug(
660
+ `Could not get stats for interface ${interfaceName}:`,
661
+ _error,
662
+ );
663
+ }
664
+
665
+ return undefined;
666
+ }
667
+
668
+ /**
669
+ * Get detailed network statistics with bandwidth usage
670
+ */
671
+ async getNetworkStatistics(): Promise<{
672
+ interfaces: NetworkInterfaceInfo[];
673
+ totalBytesReceived: number;
674
+ totalBytesSent: number;
675
+ totalPacketsReceived: number;
676
+ totalPacketsSent: number;
677
+ connections: number;
678
+ bandwidth?: {
679
+ download: number; // bytes/sec
680
+ upload: number; // bytes/sec
681
+ };
682
+ }> {
683
+ const interfaces = await this.getNetworkInterfaces();
684
+ let totalBytesReceived = 0;
685
+ let totalBytesSent = 0;
686
+ let totalPacketsReceived = 0;
687
+ let totalPacketsSent = 0;
688
+
689
+ // Sum up statistics from all interfaces
690
+ for (const iface of interfaces) {
691
+ if (iface.stats) {
692
+ totalBytesReceived += iface.stats.rxBytes || 0;
693
+ totalBytesSent += iface.stats.txBytes || 0;
694
+ totalPacketsReceived += iface.stats.rxPackets || 0;
695
+ totalPacketsSent += iface.stats.txPackets || 0;
696
+ }
697
+ }
698
+
699
+ const connections = await this.getNetworkConnections();
700
+
701
+ // Calculate bandwidth if we have previous measurements
702
+ const bandwidth = await this.calculateBandwidth(
703
+ totalBytesReceived,
704
+ totalBytesSent,
705
+ );
706
+
707
+ return {
708
+ interfaces,
709
+ totalBytesReceived,
710
+ totalBytesSent,
711
+ totalPacketsReceived,
712
+ totalPacketsSent,
713
+ connections,
714
+ bandwidth,
715
+ };
716
+ }
717
+
718
+ /**
719
+ * Calculate bandwidth based on byte counters
720
+ */
721
+ private lastNetworkMeasurement?: {
722
+ timestamp: number;
723
+ bytesReceived: number;
724
+ bytesSent: number;
725
+ };
726
+
727
+ private async calculateBandwidth(
728
+ currentBytesReceived: number,
729
+ currentBytesSent: number,
730
+ ): Promise<{ download: number; upload: number } | undefined> {
731
+ const now = Date.now();
732
+
733
+ if (this.lastNetworkMeasurement) {
734
+ const timeDiff = (now - this.lastNetworkMeasurement.timestamp) / 1000; // seconds
735
+
736
+ if (timeDiff > 0) {
737
+ const download =
738
+ (currentBytesReceived - this.lastNetworkMeasurement.bytesReceived) /
739
+ timeDiff;
740
+ const upload =
741
+ (currentBytesSent - this.lastNetworkMeasurement.bytesSent) / timeDiff;
742
+
743
+ this.lastNetworkMeasurement = {
744
+ timestamp: now,
745
+ bytesReceived: currentBytesReceived,
746
+ bytesSent: currentBytesSent,
747
+ };
748
+
749
+ return {
750
+ download: Math.max(0, download),
751
+ upload: Math.max(0, upload),
752
+ };
753
+ }
754
+ } else {
755
+ // First measurement
756
+ this.lastNetworkMeasurement = {
757
+ timestamp: now,
758
+ bytesReceived: currentBytesReceived,
759
+ bytesSent: currentBytesSent,
760
+ };
761
+ }
762
+
763
+ return undefined;
764
+ }
765
+
766
+ /**
767
+ * Get active network connections by state
768
+ */
769
+ async getNetworkConnectionsByState(): Promise<{
770
+ established: number;
771
+ listening: number;
772
+ timeWait: number;
773
+ closeWait: number;
774
+ total: number;
775
+ }> {
776
+ const platform = process.platform;
777
+
778
+ try {
779
+ let established = 0;
780
+ let listening = 0;
781
+ let timeWait = 0;
782
+ let closeWait = 0;
783
+
784
+ if (platform === "linux") {
785
+ const { stdout } = await execAsync("ss -tan");
786
+ const lines = stdout.split("\n").slice(1); // Skip header
787
+
788
+ for (const line of lines) {
789
+ if (line.includes("ESTAB")) established++;
790
+ else if (line.includes("LISTEN")) listening++;
791
+ else if (line.includes("TIME-WAIT")) timeWait++;
792
+ else if (line.includes("CLOSE-WAIT")) closeWait++;
793
+ }
794
+ } else if (platform === "darwin" || platform === "win32") {
795
+ const { stdout } = await execAsync("netstat -an");
796
+ const lines = stdout.split("\n");
797
+
798
+ for (const line of lines) {
799
+ if (line.includes("ESTABLISHED")) established++;
800
+ else if (line.includes("LISTEN")) listening++;
801
+ else if (line.includes("TIME_WAIT")) timeWait++;
802
+ else if (line.includes("CLOSE_WAIT")) closeWait++;
803
+ }
804
+ }
805
+
806
+ return {
807
+ established,
808
+ listening,
809
+ timeWait,
810
+ closeWait,
811
+ total: established + listening + timeWait + closeWait,
812
+ };
813
+ } catch (_error) {
814
+ logger.debug("Could not get network connections by state:", _error);
815
+ return {
816
+ established: 0,
817
+ listening: 0,
818
+ timeWait: 0,
819
+ closeWait: 0,
820
+ total: 0,
821
+ };
822
+ }
823
+ }
824
+
825
+ /**
826
+ * Get number of network connections
827
+ */
828
+ private async getNetworkConnections(): Promise<number> {
829
+ try {
830
+ const platform = process.platform;
831
+ let command = "";
832
+
833
+ if (platform === "linux") {
834
+ command = "ss -tun | tail -n +2 | wc -l";
835
+ } else if (platform === "darwin") {
836
+ command = "netstat -an | grep ESTABLISHED | wc -l";
837
+ } else if (platform === "win32") {
838
+ command = 'netstat -an | find /c "ESTABLISHED"';
839
+ }
840
+
841
+ if (command) {
842
+ const { stdout } = await execAsync(command);
843
+ return parseInt(stdout.trim()) || 0;
844
+ }
845
+ } catch (_error) {
846
+ logger.debug("Could not get network connections:", _error);
847
+ }
848
+
849
+ return 0;
850
+ }
851
+
852
+ /**
853
+ * Get top processes by CPU or memory usage
854
+ */
855
+ async getTopProcesses(
856
+ sortBy: "cpu" | "memory" = "cpu",
857
+ limit: number = 10,
858
+ ): Promise<ProcessInfo[]> {
859
+ const platform = process.platform;
860
+ const processes: ProcessInfo[] = [];
861
+
862
+ try {
863
+ if (platform === "linux" || platform === "darwin") {
864
+ const sortFlag = sortBy === "cpu" ? "-pcpu" : "-pmem";
865
+ const { stdout } = await execAsync(
866
+ `ps aux --sort=${sortFlag} | head -${limit + 1} | tail -${limit}`,
867
+ );
868
+ const lines = stdout.trim().split("\n");
869
+
870
+ for (const line of lines) {
871
+ const parts = line.trim().split(/\s+/);
872
+ if (parts.length >= 11) {
873
+ processes.push({
874
+ pid: parseInt(parts[1]),
875
+ cpu: parseFloat(parts[2]),
876
+ memory: parseFloat(parts[3]),
877
+ name: parts[10],
878
+ uptime: 0, // Would need additional parsing
879
+ });
880
+ }
881
+ }
882
+ } else if (platform === "win32") {
883
+ await execAsync(
884
+ "wmic process get ProcessId,Name,PageFileUsage,UserModeTime /format:csv",
885
+ );
886
+ // Parse Windows output
887
+ // Implementation would be more complex
888
+ }
889
+ } catch (_error) {
890
+ logger.debug("Could not get process list:", _error);
891
+ }
892
+
893
+ return processes;
894
+ }
895
+
896
+ /**
897
+ * Get CPU usage history
898
+ */
899
+ getCPUHistory(): number[] {
900
+ return [...this.cpuUsageHistory];
901
+ }
902
+
903
+ /**
904
+ * Monitor system health continuously
905
+ */
906
+ async startMonitoring(
907
+ interval: number = 5000,
908
+ callback?: (health: SystemHealth) => void,
909
+ ): Promise<NodeJS.Timeout> {
910
+ // Initial reading
911
+ await this.getSystemHealth();
912
+
913
+ return setInterval(async () => {
914
+ try {
915
+ const health = await this.getSystemHealth();
916
+ if (callback) {
917
+ callback(health);
918
+ }
919
+ } catch (_error) {
920
+ logger.error("Error monitoring system health:", _error);
921
+ }
922
+ }, interval);
923
+ }
924
+ }
925
+
926
+ // Singleton instance
927
+ let systemMonitor: SystemMonitor | null = null;
928
+
929
+ export function getSystemMonitor(): SystemMonitor {
930
+ if (!systemMonitor) {
931
+ systemMonitor = new SystemMonitor();
932
+ }
933
+ return systemMonitor;
934
+ }
935
+
936
+ export default SystemMonitor;