about-system 0.0.41 → 0.0.46
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 +11 -1
- package/dist/about-system-cli.js +1 -1
- package/dist/cpu-geekbench.json +1 -0
- package/dist/gpu-geekbench.json +1 -0
- package/dist/index.js +1 -1
- package/dist/system-info-api-CChFyamn.js +2396 -0
- package/dist/system-info-api-CChFyamn.js.map +1 -0
- package/dist/system-info-api.d.ts +42 -18
- package/dist/system-info-api.js +1 -1
- package/package.json +3 -3
- package/src/info/hardware.ts +33 -9
- package/src/info/memory.ts +183 -53
- package/src/info/network.ts +6 -6
- package/src/info/platform.ts +14 -1
- package/src/info/process.ts +20 -4
- package/src/info/settings.ts +7 -2
- package/src/info/software.ts +15 -3
- package/src/info/system-status.ts +67 -3
- package/src/system-info-api.ts +3 -0
- package/src/systeminfo-types.d.ts +16 -4
- package/dist/system-info-api-DCP1ZJVR.js +0 -2237
- package/dist/system-info-api-DCP1ZJVR.js.map +0 -1
|
@@ -15,7 +15,7 @@ declare function battery(context: InfoContext): string;
|
|
|
15
15
|
* Uses fuzzy matching to find similar CPU models
|
|
16
16
|
* @param context - Info context with cache
|
|
17
17
|
* @returns Benchmark info with score and rank, or empty string
|
|
18
|
-
* @example "
|
|
18
|
+
* @example "37.9k #1", "20.5k #68"
|
|
19
19
|
*/
|
|
20
20
|
declare function bench(context: InfoContext): string;
|
|
21
21
|
|
|
@@ -102,9 +102,23 @@ declare function cpu_bench_info(context: InfoContext): string;
|
|
|
102
102
|
*/
|
|
103
103
|
declare function device(context: InfoContext): string;
|
|
104
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Gets disk size (used/total) for each real disk
|
|
107
|
+
* Uses `df -k` on Linux/macOS, filtering out pseudo filesystems, system/boot
|
|
108
|
+
* partitions (e.g. /boot, /System/Volumes/*), and anything under 10GB, since
|
|
109
|
+
* those aren't disks a user would care about. When exactly one real disk is
|
|
110
|
+
* found (the common case) the result omits the mount point; when multiple
|
|
111
|
+
* real disks are found (e.g. a data drive or external volume), each is
|
|
112
|
+
* labeled by its mount point.
|
|
113
|
+
* @param context - Info context with cache
|
|
114
|
+
* @returns Disk size as "used/total GB" or "mount(used/total GB) ..." or empty string
|
|
115
|
+
* @example "256/512GB", "/(100/500GB) /Volumes/Backup(900/2000GB)"
|
|
116
|
+
*/
|
|
117
|
+
declare function disk_size(context: InfoContext): string;
|
|
118
|
+
|
|
105
119
|
/**
|
|
106
120
|
* Gets root filesystem disk usage percentage
|
|
107
|
-
* Uses df command on Linux/Android
|
|
121
|
+
* Uses df command on Linux/Android/macOS
|
|
108
122
|
* @param context - Info context with cache
|
|
109
123
|
* @returns Percentage string or empty string
|
|
110
124
|
* @example "45%", "78%"
|
|
@@ -139,7 +153,7 @@ declare function gpu(context: InfoContext): string;
|
|
|
139
153
|
* Uses fuzzy matching to find similar GPU models
|
|
140
154
|
* @param context - Info context with cache
|
|
141
155
|
* @returns Benchmark info with score and rank, or empty string
|
|
142
|
-
* @example "
|
|
156
|
+
* @example "37.9k #1", "20.5k #68"
|
|
143
157
|
*/
|
|
144
158
|
declare function gpu_bench(context: InfoContext): string;
|
|
145
159
|
|
|
@@ -188,6 +202,7 @@ export declare const infoFunctions: {
|
|
|
188
202
|
gpu_bench: typeof gpu_bench;
|
|
189
203
|
gpu_bench_info: typeof gpu_bench_info;
|
|
190
204
|
disk_used: typeof disk_used;
|
|
205
|
+
disk_size: typeof disk_size;
|
|
191
206
|
ram_used: typeof ram_used;
|
|
192
207
|
top_process: typeof top_process;
|
|
193
208
|
uptime: typeof uptime;
|
|
@@ -264,7 +279,8 @@ declare function kernel(context: InfoContext): string;
|
|
|
264
279
|
|
|
265
280
|
/**
|
|
266
281
|
* Gets system load averages
|
|
267
|
-
* Reads 1, 5, and 15 minute load averages from /proc/loadavg (Linux
|
|
282
|
+
* Reads 1, 5, and 15 minute load averages from /proc/loadavg (Linux) or
|
|
283
|
+
* os.loadavg() (macOS)
|
|
268
284
|
* @returns Space-separated load averages (1m 5m 15m) or empty string
|
|
269
285
|
* @example "0.52 0.58 0.59", "2.10 1.95 1.88"
|
|
270
286
|
*/
|
|
@@ -277,7 +293,8 @@ export declare function loadCache(): Cache_2;
|
|
|
277
293
|
|
|
278
294
|
/**
|
|
279
295
|
* Gets available memory in gigabytes
|
|
280
|
-
* Reads MemAvailable from /proc/meminfo (Linux
|
|
296
|
+
* Reads MemAvailable from /proc/meminfo (Linux) or derives it from
|
|
297
|
+
* vm_stat's free+inactive pages (macOS)
|
|
281
298
|
* @returns Available memory with "GB available" suffix or empty string
|
|
282
299
|
* @example "12GB available", "4GB available"
|
|
283
300
|
*/
|
|
@@ -285,20 +302,23 @@ declare function memory_available(): string;
|
|
|
285
302
|
|
|
286
303
|
/**
|
|
287
304
|
* Gets mounted filesystem information
|
|
288
|
-
* Lists non-system mount points with usage from df (Linux
|
|
289
|
-
*
|
|
305
|
+
* Lists non-system mount points with usage from df (Linux/macOS)
|
|
306
|
+
* On Linux, excludes /, /dev, /proc, /sys and internal /System/Volumes mounts.
|
|
307
|
+
* On macOS, APFS internal volumes (Data, VM, Preboot, Update, ...) all live
|
|
308
|
+
* under /System/Volumes and are not user-relevant, so only /Volumes (external
|
|
309
|
+
* disks, network shares, disk images) are reported.
|
|
290
310
|
* @param context - Info context with cache
|
|
291
311
|
* @returns Space-separated mount points with usage or empty string
|
|
292
|
-
* @example "/home(45%) /mnt/data(78%)", "/
|
|
312
|
+
* @example "/home(45%) /mnt/data(78%)", "/Volumes/Backup(78%)"
|
|
293
313
|
*/
|
|
294
314
|
declare function mount_points(context: InfoContext): string;
|
|
295
315
|
|
|
296
316
|
/**
|
|
297
317
|
* Gets active network interface names
|
|
298
|
-
* Lists non-loopback interfaces with IPv4 addresses (Linux only)
|
|
318
|
+
* Lists non-loopback interfaces with IPv4 addresses (Linux/macOS only)
|
|
299
319
|
* @param context - Info context with cache
|
|
300
320
|
* @returns Space-separated interface names or empty string
|
|
301
|
-
* @example "eth0 wlan0", "
|
|
321
|
+
* @example "eth0 wlan0", "en0 en1"
|
|
302
322
|
*/
|
|
303
323
|
declare function network_interfaces(context: InfoContext): string;
|
|
304
324
|
|
|
@@ -322,7 +342,7 @@ declare function packages(context: InfoContext): string;
|
|
|
322
342
|
|
|
323
343
|
/**
|
|
324
344
|
* Gets open TCP ports with service names
|
|
325
|
-
* Uses lsof to find listening TCP ports (Linux only)
|
|
345
|
+
* Uses lsof to find listening TCP ports (Linux/macOS only)
|
|
326
346
|
* @param context - Info context with cache
|
|
327
347
|
* @returns Space-separated port+process pairs or empty string
|
|
328
348
|
* @example "80http 443http 22ssh", "3000node 5432post"
|
|
@@ -344,8 +364,8 @@ declare function ram_used(context: InfoContext): string;
|
|
|
344
364
|
export declare function saveCache(cache: Cache_2): void;
|
|
345
365
|
|
|
346
366
|
/**
|
|
347
|
-
* Gets screen resolution
|
|
348
|
-
* Uses xrandr
|
|
367
|
+
* Gets screen resolution
|
|
368
|
+
* Uses xrandr on Linux (X11/Xorg only) or system_profiler on macOS
|
|
349
369
|
* @returns Resolution in WIDTHxHEIGHT format or empty string
|
|
350
370
|
* @example "1920x1080", "2560x1440", "3840x2160"
|
|
351
371
|
*/
|
|
@@ -362,7 +382,8 @@ declare function services_running(context: InfoContext): string;
|
|
|
362
382
|
|
|
363
383
|
/**
|
|
364
384
|
* Gets the current shell name
|
|
365
|
-
* Uses
|
|
385
|
+
* Uses the user's login shell (macOS/Linux), falls back to ps for the
|
|
386
|
+
* parent process shell
|
|
366
387
|
* @returns Shell name or empty string on Windows
|
|
367
388
|
* @example "bash", "zsh", "fish", "nu"
|
|
368
389
|
*/
|
|
@@ -370,7 +391,8 @@ declare function shell(context: InfoContext): string;
|
|
|
370
391
|
|
|
371
392
|
/**
|
|
372
393
|
* Gets swap memory usage
|
|
373
|
-
* Calculates swap usage from /proc/meminfo (Linux
|
|
394
|
+
* Calculates swap usage from /proc/meminfo (Linux) or `sysctl vm.swapusage`
|
|
395
|
+
* (macOS)
|
|
374
396
|
* @returns Swap usage as "percentage (size MB) swap" or empty string
|
|
375
397
|
* @example "15% (512MB) swap", "0% (0MB) swap"
|
|
376
398
|
*/
|
|
@@ -378,7 +400,9 @@ declare function swap_used(): string;
|
|
|
378
400
|
|
|
379
401
|
/**
|
|
380
402
|
* Gets system temperature in Celsius
|
|
381
|
-
* Reads from thermal zone or hwmon sensors (Linux
|
|
403
|
+
* Reads from thermal zone or hwmon sensors (Linux), or from the
|
|
404
|
+
* `osx-cpu-temp` CLI (macOS, if installed via `brew install osx-cpu-temp`) -
|
|
405
|
+
* macOS has no unprivileged API for this, so nothing is reported without it
|
|
382
406
|
* @param context - Info context with cache
|
|
383
407
|
* @returns Temperature with °C suffix or empty string
|
|
384
408
|
* @example "45°C", "62°C"
|
|
@@ -387,7 +411,7 @@ declare function temperature(context: InfoContext): string;
|
|
|
387
411
|
|
|
388
412
|
/**
|
|
389
413
|
* Gets the highest CPU-consuming process
|
|
390
|
-
* Uses ps command to find top process by CPU usage (Linux only)
|
|
414
|
+
* Uses ps command to find top process by CPU usage (Linux/macOS only)
|
|
391
415
|
* @param context - Info context with cache
|
|
392
416
|
* @returns Process info as "percentage processname" or empty string
|
|
393
417
|
* @example "8% firefox", "15% chrome", "3% systemd"
|
|
@@ -409,7 +433,7 @@ declare function user(): string;
|
|
|
409
433
|
|
|
410
434
|
/**
|
|
411
435
|
* Gets number of logged in users
|
|
412
|
-
* Uses who command to count active user sessions (Linux only)
|
|
436
|
+
* Uses who command to count active user sessions (Linux/macOS only)
|
|
413
437
|
* @returns User count with "users" suffix or empty string
|
|
414
438
|
* @example "3 users", "1 users"
|
|
415
439
|
*/
|
package/dist/system-info-api.js
CHANGED
|
@@ -2,7 +2,7 @@ import "os";
|
|
|
2
2
|
import "fs";
|
|
3
3
|
import "https";
|
|
4
4
|
import "path";
|
|
5
|
-
import { g as m, i as r, l as p, s as n } from "./system-info-api-
|
|
5
|
+
import { g as m, i as r, l as p, s as n } from "./system-info-api-CChFyamn.js";
|
|
6
6
|
export {
|
|
7
7
|
m as getSystemInfo,
|
|
8
8
|
r as infoFunctions,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "about-system",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.46",
|
|
4
4
|
"description": "A Node.js script to display key system information with emojis. Cross-platform support for Windows, macOS, and Linux with customizable output and caching.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -56,9 +56,9 @@
|
|
|
56
56
|
"license": "rights.institute/prosper",
|
|
57
57
|
"repository": {
|
|
58
58
|
"type": "git",
|
|
59
|
-
"url": "https://github.com/OpenSourceAGI/
|
|
59
|
+
"url": "https://github.com/OpenSourceAGI/dev-tools-starter-agent/tree/master/packages/about-system-info"
|
|
60
60
|
},
|
|
61
|
-
"homepage": "https://github.com/OpenSourceAGI/
|
|
61
|
+
"homepage": "https://github.com/OpenSourceAGI/dev-tools-starter-agent/tree/master/packages/about-system-info",
|
|
62
62
|
"files": [
|
|
63
63
|
"dist/**/*",
|
|
64
64
|
"src/**/*.ts",
|
package/src/info/hardware.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import os from "os";
|
|
7
7
|
import fs from "fs";
|
|
8
8
|
import type { InfoContext } from "../types/internal-types";
|
|
9
|
-
import { IS_WINDOWS, IS_LINUX } from "../utils/platform";
|
|
9
|
+
import { IS_WINDOWS, IS_MAC, IS_LINUX } from "../utils/platform";
|
|
10
10
|
import { execCommand } from "../utils/command";
|
|
11
11
|
import { getCachedValue, setCachedValue } from "../cache/cache";
|
|
12
12
|
import Fuse from "fuse.js";
|
|
@@ -133,6 +133,19 @@ export function gpu(context: InfoContext): string {
|
|
|
133
133
|
setCachedValue(context.cache, "gpu", result);
|
|
134
134
|
return result;
|
|
135
135
|
}
|
|
136
|
+
} else if (IS_MAC) {
|
|
137
|
+
try {
|
|
138
|
+
const profile = execCommand("system_profiler SPDisplaysDataType");
|
|
139
|
+
const gpus = Array.from(profile.matchAll(/Chipset Model:\s*(.+)/g)).map(
|
|
140
|
+
(match) => match[1].trim()
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
if (gpus.length > 0) {
|
|
144
|
+
const result = gpus.join(", ");
|
|
145
|
+
setCachedValue(context.cache, "gpu", result);
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
148
|
+
} catch {}
|
|
136
149
|
} else if (IS_LINUX) {
|
|
137
150
|
try {
|
|
138
151
|
const lspci = execCommand("lspci");
|
|
@@ -173,12 +186,23 @@ export function gpu(context: InfoContext): string {
|
|
|
173
186
|
}
|
|
174
187
|
|
|
175
188
|
/**
|
|
176
|
-
* Gets screen resolution
|
|
177
|
-
* Uses xrandr
|
|
189
|
+
* Gets screen resolution
|
|
190
|
+
* Uses xrandr on Linux (X11/Xorg only) or system_profiler on macOS
|
|
178
191
|
* @returns Resolution in WIDTHxHEIGHT format or empty string
|
|
179
192
|
* @example "1920x1080", "2560x1440", "3840x2160"
|
|
180
193
|
*/
|
|
181
194
|
export function screen_resolution(): string {
|
|
195
|
+
if (IS_MAC) {
|
|
196
|
+
try {
|
|
197
|
+
const profile = execCommand("system_profiler SPDisplaysDataType");
|
|
198
|
+
const resolutionMatch = profile.match(/Resolution:\s*(\d+) x (\d+)/);
|
|
199
|
+
if (resolutionMatch) {
|
|
200
|
+
return `${resolutionMatch[1]}x${resolutionMatch[2]}`;
|
|
201
|
+
}
|
|
202
|
+
} catch {}
|
|
203
|
+
return "";
|
|
204
|
+
}
|
|
205
|
+
|
|
182
206
|
if (!IS_LINUX) return "";
|
|
183
207
|
|
|
184
208
|
try {
|
|
@@ -198,7 +222,7 @@ export function screen_resolution(): string {
|
|
|
198
222
|
* Uses fuzzy matching to find similar CPU models
|
|
199
223
|
* @param context - Info context with cache
|
|
200
224
|
* @returns Benchmark info with score and rank, or empty string
|
|
201
|
-
* @example "
|
|
225
|
+
* @example "37.9k #1", "20.5k #68"
|
|
202
226
|
*/
|
|
203
227
|
export function bench(context: InfoContext): string {
|
|
204
228
|
const cached = getCachedValue(context.cache, "bench");
|
|
@@ -212,7 +236,7 @@ export function bench(context: InfoContext): string {
|
|
|
212
236
|
|
|
213
237
|
try {
|
|
214
238
|
let benchmarkPath: string;
|
|
215
|
-
const benchmarkFile = "cpu-geekbench
|
|
239
|
+
const benchmarkFile = "cpu-geekbench.json";
|
|
216
240
|
|
|
217
241
|
// Try multiple resolution strategies
|
|
218
242
|
if (fs.existsSync(benchmarkFile)) {
|
|
@@ -252,7 +276,7 @@ export function bench(context: InfoContext): string {
|
|
|
252
276
|
const match = results[0].item;
|
|
253
277
|
const score = Math.round(match.score / 100) / 10;
|
|
254
278
|
const rank = match.rank;
|
|
255
|
-
const result =
|
|
279
|
+
const result = `${score}k #${rank}`;
|
|
256
280
|
setCachedValue(context.cache, "bench", result);
|
|
257
281
|
return result;
|
|
258
282
|
}
|
|
@@ -283,7 +307,7 @@ export function cpu_bench_info(context: InfoContext): string {
|
|
|
283
307
|
|
|
284
308
|
try {
|
|
285
309
|
let benchmarkPath: string;
|
|
286
|
-
const benchmarkFile = "cpu-geekbench
|
|
310
|
+
const benchmarkFile = "cpu-geekbench.json";
|
|
287
311
|
|
|
288
312
|
// Try multiple resolution strategies
|
|
289
313
|
if (fs.existsSync(benchmarkFile)) {
|
|
@@ -357,7 +381,7 @@ export function cpu_bench_info(context: InfoContext): string {
|
|
|
357
381
|
* Uses fuzzy matching to find similar GPU models
|
|
358
382
|
* @param context - Info context with cache
|
|
359
383
|
* @returns Benchmark info with score and rank, or empty string
|
|
360
|
-
* @example "
|
|
384
|
+
* @example "37.9k #1", "20.5k #68"
|
|
361
385
|
*/
|
|
362
386
|
export function gpu_bench(context: InfoContext): string {
|
|
363
387
|
const cached = getCachedValue(context.cache, "gpu_bench");
|
|
@@ -411,7 +435,7 @@ export function gpu_bench(context: InfoContext): string {
|
|
|
411
435
|
const match = results[0].item;
|
|
412
436
|
const score = Math.round(match.score / 100) / 10;
|
|
413
437
|
const rank = match.rank;
|
|
414
|
-
const result =
|
|
438
|
+
const result = `${score}k #${rank}`;
|
|
415
439
|
setCachedValue(context.cache, "gpu_bench", result);
|
|
416
440
|
return result;
|
|
417
441
|
}
|
package/src/info/memory.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import os from "os";
|
|
7
7
|
import fs from "fs";
|
|
8
8
|
import type { InfoContext } from "../types/internal-types";
|
|
9
|
-
import { IS_LINUX } from "../utils/platform";
|
|
9
|
+
import { IS_LINUX, IS_MAC } from "../utils/platform";
|
|
10
10
|
import { execCommand } from "../utils/command";
|
|
11
11
|
import { getCachedValue, setCachedValue } from "../cache/cache";
|
|
12
12
|
|
|
@@ -56,56 +56,101 @@ export function ram_used(context: InfoContext): string {
|
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
58
|
* Gets available memory in gigabytes
|
|
59
|
-
* Reads MemAvailable from /proc/meminfo (Linux
|
|
59
|
+
* Reads MemAvailable from /proc/meminfo (Linux) or derives it from
|
|
60
|
+
* vm_stat's free+inactive pages (macOS)
|
|
60
61
|
* @returns Available memory with "GB available" suffix or empty string
|
|
61
62
|
* @example "12GB available", "4GB available"
|
|
62
63
|
*/
|
|
63
64
|
export function memory_available(): string {
|
|
64
|
-
if (
|
|
65
|
+
if (IS_LINUX) {
|
|
66
|
+
try {
|
|
67
|
+
const meminfo = fs.readFileSync("/proc/meminfo", "utf8");
|
|
68
|
+
const availableMatch = meminfo.match(/MemAvailable:\s+(\d+) kB/);
|
|
69
|
+
if (availableMatch) {
|
|
70
|
+
const availableGB = Math.round(parseInt(availableMatch[1]) / 1024 / 1024);
|
|
71
|
+
return `${availableGB}GB available`;
|
|
72
|
+
}
|
|
73
|
+
} catch {}
|
|
74
|
+
return "";
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (IS_MAC) {
|
|
78
|
+
try {
|
|
79
|
+
const vmStat = execCommand("vm_stat");
|
|
80
|
+
const pageSizeMatch = vmStat.match(/page size of (\d+) bytes/);
|
|
81
|
+
const freeMatch = vmStat.match(/Pages free:\s+(\d+)\./);
|
|
82
|
+
const inactiveMatch = vmStat.match(/Pages inactive:\s+(\d+)\./);
|
|
83
|
+
|
|
84
|
+
if (pageSizeMatch && freeMatch && inactiveMatch) {
|
|
85
|
+
const pageSize = parseInt(pageSizeMatch[1]);
|
|
86
|
+
const availablePages = parseInt(freeMatch[1]) + parseInt(inactiveMatch[1]);
|
|
87
|
+
const availableGB = Math.round((availablePages * pageSize) / (1024 * 1024 * 1024));
|
|
88
|
+
return `${availableGB}GB available`;
|
|
89
|
+
}
|
|
90
|
+
} catch {}
|
|
91
|
+
return "";
|
|
92
|
+
}
|
|
65
93
|
|
|
66
|
-
try {
|
|
67
|
-
const meminfo = fs.readFileSync("/proc/meminfo", "utf8");
|
|
68
|
-
const availableMatch = meminfo.match(/MemAvailable:\s+(\d+) kB/);
|
|
69
|
-
if (availableMatch) {
|
|
70
|
-
const availableGB = Math.round(parseInt(availableMatch[1]) / 1024 / 1024);
|
|
71
|
-
return `${availableGB}GB available`;
|
|
72
|
-
}
|
|
73
|
-
} catch {}
|
|
74
94
|
return "";
|
|
75
95
|
}
|
|
76
96
|
|
|
77
97
|
/**
|
|
78
98
|
* Gets swap memory usage
|
|
79
|
-
* Calculates swap usage from /proc/meminfo (Linux
|
|
99
|
+
* Calculates swap usage from /proc/meminfo (Linux) or `sysctl vm.swapusage`
|
|
100
|
+
* (macOS)
|
|
80
101
|
* @returns Swap usage as "percentage (size MB) swap" or empty string
|
|
81
102
|
* @example "15% (512MB) swap", "0% (0MB) swap"
|
|
82
103
|
*/
|
|
83
104
|
export function swap_used(): string {
|
|
84
|
-
if (
|
|
105
|
+
if (IS_LINUX) {
|
|
106
|
+
try {
|
|
107
|
+
const meminfo = fs.readFileSync("/proc/meminfo", "utf8");
|
|
108
|
+
const swapTotalMatch = meminfo.match(/SwapTotal:\s+(\d+) kB/);
|
|
109
|
+
const swapFreeMatch = meminfo.match(/SwapFree:\s+(\d+) kB/);
|
|
85
110
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (swapTotal > 0) {
|
|
97
|
-
const swapUsedPercent = Math.round((swapUsed / swapTotal) * 100);
|
|
98
|
-
const swapUsedMB = Math.round(swapUsed / 1024);
|
|
99
|
-
return `${swapUsedPercent}% (${swapUsedMB}MB) swap`;
|
|
111
|
+
if (swapTotalMatch && swapFreeMatch) {
|
|
112
|
+
const swapTotal = parseInt(swapTotalMatch[1]);
|
|
113
|
+
const swapFree = parseInt(swapFreeMatch[1]);
|
|
114
|
+
const swapUsed = swapTotal - swapFree;
|
|
115
|
+
|
|
116
|
+
if (swapTotal > 0) {
|
|
117
|
+
const swapUsedPercent = Math.round((swapUsed / swapTotal) * 100);
|
|
118
|
+
const swapUsedMB = Math.round(swapUsed / 1024);
|
|
119
|
+
return `${swapUsedPercent}% (${swapUsedMB}MB) swap`;
|
|
120
|
+
}
|
|
100
121
|
}
|
|
101
|
-
}
|
|
102
|
-
|
|
122
|
+
} catch {}
|
|
123
|
+
return "";
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (IS_MAC) {
|
|
127
|
+
try {
|
|
128
|
+
const swapusage = execCommand("sysctl -n vm.swapusage");
|
|
129
|
+
const match = swapusage.match(
|
|
130
|
+
/total\s*=\s*([\d.]+)M\s+used\s*=\s*([\d.]+)M\s+free\s*=\s*([\d.]+)M/
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
if (match) {
|
|
134
|
+
const swapTotal = parseFloat(match[1]);
|
|
135
|
+
const swapUsedMB = parseFloat(match[2]);
|
|
136
|
+
|
|
137
|
+
if (swapTotal > 0) {
|
|
138
|
+
const swapUsedPercent = Math.round((swapUsedMB / swapTotal) * 100);
|
|
139
|
+
return `${swapUsedPercent}% (${Math.round(swapUsedMB)}MB) swap`;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return "0% (0MB) swap";
|
|
143
|
+
}
|
|
144
|
+
} catch {}
|
|
145
|
+
return "";
|
|
146
|
+
}
|
|
147
|
+
|
|
103
148
|
return "";
|
|
104
149
|
}
|
|
105
150
|
|
|
106
151
|
/**
|
|
107
152
|
* Gets root filesystem disk usage percentage
|
|
108
|
-
* Uses df command on Linux/Android
|
|
153
|
+
* Uses df command on Linux/Android/macOS
|
|
109
154
|
* @param context - Info context with cache
|
|
110
155
|
* @returns Percentage string or empty string
|
|
111
156
|
* @example "45%", "78%"
|
|
@@ -114,7 +159,7 @@ export function disk_used(context: InfoContext): string {
|
|
|
114
159
|
const cached = getCachedValue(context.cache, "disk_used");
|
|
115
160
|
if (cached !== null) return cached;
|
|
116
161
|
|
|
117
|
-
if (IS_LINUX) {
|
|
162
|
+
if (IS_LINUX || IS_MAC) {
|
|
118
163
|
try {
|
|
119
164
|
const df = execCommand("df -h");
|
|
120
165
|
let diskUsage = "";
|
|
@@ -151,35 +196,108 @@ export function disk_used(context: InfoContext): string {
|
|
|
151
196
|
}
|
|
152
197
|
|
|
153
198
|
/**
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
* @returns Space-separated load averages (1m 5m 15m) or empty string
|
|
157
|
-
* @example "0.52 0.58 0.59", "2.10 1.95 1.88"
|
|
199
|
+
* Minimum total size (in KB) for a partition to be considered a real disk
|
|
200
|
+
* rather than a small system/boot partition
|
|
158
201
|
*/
|
|
159
|
-
|
|
160
|
-
|
|
202
|
+
const MIN_REAL_DISK_KB = 10 * 1024 * 1024; // 10GB
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Gets disk size (used/total) for each real disk
|
|
206
|
+
* Uses `df -k` on Linux/macOS, filtering out pseudo filesystems, system/boot
|
|
207
|
+
* partitions (e.g. /boot, /System/Volumes/*), and anything under 10GB, since
|
|
208
|
+
* those aren't disks a user would care about. When exactly one real disk is
|
|
209
|
+
* found (the common case) the result omits the mount point; when multiple
|
|
210
|
+
* real disks are found (e.g. a data drive or external volume), each is
|
|
211
|
+
* labeled by its mount point.
|
|
212
|
+
* @param context - Info context with cache
|
|
213
|
+
* @returns Disk size as "used/total GB" or "mount(used/total GB) ..." or empty string
|
|
214
|
+
* @example "256/512GB", "/(100/500GB) /Volumes/Backup(900/2000GB)"
|
|
215
|
+
*/
|
|
216
|
+
export function disk_size(context: InfoContext): string {
|
|
217
|
+
const cached = getCachedValue(context.cache, "disk_size");
|
|
218
|
+
if (cached !== null) return cached;
|
|
219
|
+
|
|
220
|
+
if (!IS_LINUX && !IS_MAC) {
|
|
221
|
+
setCachedValue(context.cache, "disk_size", "");
|
|
222
|
+
return "";
|
|
223
|
+
}
|
|
161
224
|
|
|
162
225
|
try {
|
|
163
|
-
const
|
|
164
|
-
const
|
|
165
|
-
|
|
226
|
+
const df = execCommand("df -k");
|
|
227
|
+
const lines = df.trim().split("\n").slice(1);
|
|
228
|
+
const disks: { mount: string; usedGB: number; totalGB: number }[] = [];
|
|
229
|
+
|
|
230
|
+
for (const line of lines) {
|
|
231
|
+
const parts = line.trim().split(/\s+/);
|
|
232
|
+
if (parts.length < 6) continue;
|
|
233
|
+
|
|
234
|
+
const filesystem = parts[0];
|
|
235
|
+
const totalKB = parseInt(parts[1]);
|
|
236
|
+
const usedKB = parseInt(parts[2]);
|
|
237
|
+
const mount = parts[parts.length - 1];
|
|
238
|
+
|
|
239
|
+
if (isNaN(totalKB) || isNaN(usedKB) || totalKB < MIN_REAL_DISK_KB) continue;
|
|
240
|
+
|
|
241
|
+
if (IS_MAC) {
|
|
242
|
+
// Only the boot disk ("/") and externally mounted real disks count.
|
|
243
|
+
// APFS internal system volumes (Data, VM, Preboot, Update, ...) all
|
|
244
|
+
// live under /System/Volumes and aren't separate physical disks.
|
|
245
|
+
if (mount !== "/" && !mount.startsWith("/Volumes/")) continue;
|
|
246
|
+
} else {
|
|
247
|
+
if (
|
|
248
|
+
filesystem.startsWith("devtmpfs") ||
|
|
249
|
+
filesystem.startsWith("tmpfs") ||
|
|
250
|
+
filesystem === "overlay" ||
|
|
251
|
+
mount.startsWith("/boot") ||
|
|
252
|
+
mount.startsWith("/dev") ||
|
|
253
|
+
mount.startsWith("/proc") ||
|
|
254
|
+
mount.startsWith("/sys") ||
|
|
255
|
+
mount.startsWith("/run")
|
|
256
|
+
) {
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
disks.push({
|
|
262
|
+
mount,
|
|
263
|
+
usedGB: Math.round(usedKB / (1024 * 1024)),
|
|
264
|
+
totalGB: Math.round(totalKB / (1024 * 1024)),
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
let result = "";
|
|
269
|
+
if (disks.length === 1) {
|
|
270
|
+
result = `${disks[0].usedGB}/${disks[0].totalGB}GB`;
|
|
271
|
+
} else if (disks.length > 1) {
|
|
272
|
+
result = disks
|
|
273
|
+
.map((d) => `${d.mount}(${d.usedGB}/${d.totalGB}GB)`)
|
|
274
|
+
.join(" ");
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
setCachedValue(context.cache, "disk_size", result);
|
|
278
|
+
return result;
|
|
166
279
|
} catch {}
|
|
280
|
+
|
|
281
|
+
setCachedValue(context.cache, "disk_size", "");
|
|
167
282
|
return "";
|
|
168
283
|
}
|
|
169
284
|
|
|
170
285
|
/**
|
|
171
286
|
* Gets mounted filesystem information
|
|
172
|
-
* Lists non-system mount points with usage from df (Linux
|
|
173
|
-
*
|
|
287
|
+
* Lists non-system mount points with usage from df (Linux/macOS)
|
|
288
|
+
* On Linux, excludes /, /dev, /proc, /sys and internal /System/Volumes mounts.
|
|
289
|
+
* On macOS, APFS internal volumes (Data, VM, Preboot, Update, ...) all live
|
|
290
|
+
* under /System/Volumes and are not user-relevant, so only /Volumes (external
|
|
291
|
+
* disks, network shares, disk images) are reported.
|
|
174
292
|
* @param context - Info context with cache
|
|
175
293
|
* @returns Space-separated mount points with usage or empty string
|
|
176
|
-
* @example "/home(45%) /mnt/data(78%)", "/
|
|
294
|
+
* @example "/home(45%) /mnt/data(78%)", "/Volumes/Backup(78%)"
|
|
177
295
|
*/
|
|
178
296
|
export function mount_points(context: InfoContext): string {
|
|
179
297
|
const cached = getCachedValue(context.cache, "mount_points");
|
|
180
298
|
if (cached !== null) return cached;
|
|
181
299
|
|
|
182
|
-
if (!IS_LINUX) {
|
|
300
|
+
if (!IS_LINUX && !IS_MAC) {
|
|
183
301
|
setCachedValue(context.cache, "mount_points", "");
|
|
184
302
|
return "";
|
|
185
303
|
}
|
|
@@ -191,17 +309,29 @@ export function mount_points(context: InfoContext): string {
|
|
|
191
309
|
|
|
192
310
|
lines.forEach((line) => {
|
|
193
311
|
const parts = line.trim().split(/\s+/);
|
|
194
|
-
if (parts.length
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
312
|
+
if (parts.length < 2) return;
|
|
313
|
+
|
|
314
|
+
const mountPoint = parts[parts.length - 1];
|
|
315
|
+
const usage = parts.find((part) => /^\d+%$/.test(part)) || "";
|
|
316
|
+
|
|
317
|
+
if (!usage) return;
|
|
318
|
+
|
|
319
|
+
if (IS_MAC) {
|
|
320
|
+
// Skip the "Macintosh HD" self-symlink and any other symlink back to /
|
|
321
|
+
if (mountPoint.startsWith("/Volumes/") && mountPoint !== "/") {
|
|
203
322
|
mountPoints.push(`${mountPoint}(${usage})`);
|
|
204
323
|
}
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (
|
|
328
|
+
!mountPoint.startsWith("/dev") &&
|
|
329
|
+
!mountPoint.startsWith("/proc") &&
|
|
330
|
+
!mountPoint.startsWith("/sys") &&
|
|
331
|
+
!mountPoint.startsWith("/System/Volumes") &&
|
|
332
|
+
mountPoint !== "/"
|
|
333
|
+
) {
|
|
334
|
+
mountPoints.push(`${mountPoint}(${usage})`);
|
|
205
335
|
}
|
|
206
336
|
});
|
|
207
337
|
|
package/src/info/network.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import os from "os";
|
|
7
7
|
import type { InfoContext } from "../types/internal-types";
|
|
8
|
-
import { IS_LINUX } from "../utils/platform";
|
|
8
|
+
import { IS_LINUX, IS_MAC } from "../utils/platform";
|
|
9
9
|
import { execCommand } from "../utils/command";
|
|
10
10
|
import { getCachedValue, setCachedValue } from "../cache/cache";
|
|
11
11
|
|
|
@@ -135,16 +135,16 @@ export async function isp(context: InfoContext): Promise<string> {
|
|
|
135
135
|
|
|
136
136
|
/**
|
|
137
137
|
* Gets active network interface names
|
|
138
|
-
* Lists non-loopback interfaces with IPv4 addresses (Linux only)
|
|
138
|
+
* Lists non-loopback interfaces with IPv4 addresses (Linux/macOS only)
|
|
139
139
|
* @param context - Info context with cache
|
|
140
140
|
* @returns Space-separated interface names or empty string
|
|
141
|
-
* @example "eth0 wlan0", "
|
|
141
|
+
* @example "eth0 wlan0", "en0 en1"
|
|
142
142
|
*/
|
|
143
143
|
export function network_interfaces(context: InfoContext): string {
|
|
144
144
|
const cached = getCachedValue(context.cache, "network_interfaces");
|
|
145
145
|
if (cached !== null) return cached;
|
|
146
146
|
|
|
147
|
-
if (!IS_LINUX) {
|
|
147
|
+
if (!IS_LINUX && !IS_MAC) {
|
|
148
148
|
setCachedValue(context.cache, "network_interfaces", "");
|
|
149
149
|
return "";
|
|
150
150
|
}
|
|
@@ -174,7 +174,7 @@ export function network_interfaces(context: InfoContext): string {
|
|
|
174
174
|
}
|
|
175
175
|
/**
|
|
176
176
|
* Gets open TCP ports with service names
|
|
177
|
-
* Uses lsof to find listening TCP ports (Linux only)
|
|
177
|
+
* Uses lsof to find listening TCP ports (Linux/macOS only)
|
|
178
178
|
* @param context - Info context with cache
|
|
179
179
|
* @returns Space-separated port+process pairs or empty string
|
|
180
180
|
* @example "80http 443http 22ssh", "3000node 5432post"
|
|
@@ -183,7 +183,7 @@ export function ports(context: InfoContext): string {
|
|
|
183
183
|
const cached = getCachedValue(context.cache, "ports");
|
|
184
184
|
if (cached !== null) return cached;
|
|
185
185
|
|
|
186
|
-
if (IS_LINUX) {
|
|
186
|
+
if (IS_LINUX || IS_MAC) {
|
|
187
187
|
try {
|
|
188
188
|
const lsof = execCommand("lsof -nP -iTCP -sTCP:LISTEN");
|
|
189
189
|
const lines = lsof.split("\n").slice(1);
|