about-system 0.0.17 → 0.0.19
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 +390 -89
- package/dist/about-system-cli.d.ts +5 -0
- package/dist/about-system-cli.js +236 -0
- package/dist/about-system-cli.js.map +1 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/system-info-api-Bc0iSNdN.js +837 -0
- package/dist/system-info-api-Bc0iSNdN.js.map +1 -0
- package/dist/system-info-api.d.ts +378 -0
- package/dist/system-info-api.js +12 -0
- package/dist/system-info-api.js.map +1 -0
- package/package.json +36 -16
- package/src/about-system-cli.ts +419 -0
- package/src/cache/cache-config.ts +68 -0
- package/src/cache/cache.ts +95 -0
- package/src/index.ts +18 -0
- package/src/info/hardware.ts +173 -0
- package/src/info/memory.ts +215 -0
- package/src/info/network.ts +213 -0
- package/src/info/platform.ts +145 -0
- package/src/info/process.ts +72 -0
- package/src/info/settings.ts +209 -0
- package/src/info/software.ts +192 -0
- package/src/info/system-status.ts +152 -0
- package/src/system-info-api.ts +271 -0
- package/src/systeminfo-types.d.ts +457 -0
- package/src/types/internal-types.ts +21 -0
- package/src/utils/command.ts +47 -0
- package/src/utils/network.ts +58 -0
- package/src/utils/platform.ts +13 -0
- package/src/about-system.js +0 -1642
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* System status and health information functions
|
|
3
|
+
* @module info/system-status
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import fs from "fs";
|
|
7
|
+
import os from "os";
|
|
8
|
+
import type { InfoContext } from "../types/internal-types";
|
|
9
|
+
import { IS_LINUX } from "../utils/platform";
|
|
10
|
+
import { execCommand, commandExists } from "../utils/command";
|
|
11
|
+
import { getCachedValue, setCachedValue } from "../cache/cache";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Gets system load averages
|
|
15
|
+
* Reads 1, 5, and 15 minute load averages from /proc/loadavg (Linux only)
|
|
16
|
+
* @returns Space-separated load averages (1m 5m 15m) or empty string
|
|
17
|
+
* @example "0.52 0.58 0.59", "2.10 1.95 1.88"
|
|
18
|
+
*/
|
|
19
|
+
export function load_average(context: InfoContext): string {
|
|
20
|
+
if (!IS_LINUX) return "";
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
const loadavg = fs.readFileSync("/proc/loadavg", "utf8");
|
|
24
|
+
const loads = loadavg.split(" ").slice(0, 3);
|
|
25
|
+
return loads.join(" ");
|
|
26
|
+
} catch {}
|
|
27
|
+
return "";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Gets battery charge level and charging status
|
|
32
|
+
* Reads from /sys/class/power_supply/BAT0 (Linux laptops only)
|
|
33
|
+
* @param context - Info context with cache
|
|
34
|
+
* @returns Battery percentage with optional + for charging, or empty string
|
|
35
|
+
* @example "85%", "42%+", "100%"
|
|
36
|
+
*/
|
|
37
|
+
export function battery(context: InfoContext): string {
|
|
38
|
+
const cached = getCachedValue(context.cache, "battery");
|
|
39
|
+
if (cached !== null) return cached;
|
|
40
|
+
|
|
41
|
+
if (!IS_LINUX) {
|
|
42
|
+
setCachedValue(context.cache, "battery", "");
|
|
43
|
+
return "";
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
const batteryPath = "/sys/class/power_supply/BAT0";
|
|
48
|
+
const capacityPath = `${batteryPath}/capacity`;
|
|
49
|
+
const statusPath = `${batteryPath}/status`;
|
|
50
|
+
|
|
51
|
+
if (fs.existsSync(capacityPath)) {
|
|
52
|
+
const capacity = fs.readFileSync(capacityPath, "utf8").trim();
|
|
53
|
+
const status = fs.existsSync(statusPath)
|
|
54
|
+
? fs.readFileSync(statusPath, "utf8").trim()
|
|
55
|
+
: "Unknown";
|
|
56
|
+
|
|
57
|
+
const batteryPercent = parseInt(capacity);
|
|
58
|
+
const isCharging = status === "Charging";
|
|
59
|
+
const result = `${batteryPercent}%${isCharging ? "+" : ""}`;
|
|
60
|
+
setCachedValue(context.cache, "battery", result);
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
} catch {}
|
|
64
|
+
|
|
65
|
+
setCachedValue(context.cache, "battery", "");
|
|
66
|
+
return "";
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Gets system temperature in Celsius
|
|
71
|
+
* Reads from thermal zone or hwmon sensors (Linux only)
|
|
72
|
+
* @param context - Info context with cache
|
|
73
|
+
* @returns Temperature with °C suffix or empty string
|
|
74
|
+
* @example "45°C", "62°C"
|
|
75
|
+
*/
|
|
76
|
+
export function temperature(context: InfoContext): string {
|
|
77
|
+
const cached = getCachedValue(context.cache, "temperature");
|
|
78
|
+
if (cached !== null) return cached;
|
|
79
|
+
|
|
80
|
+
if (!IS_LINUX) {
|
|
81
|
+
setCachedValue(context.cache, "temperature", "");
|
|
82
|
+
return "";
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
const tempSources = [
|
|
87
|
+
"/sys/class/thermal/thermal_zone0/temp",
|
|
88
|
+
"/sys/class/hwmon/hwmon0/temp1_input",
|
|
89
|
+
"/sys/class/hwmon/hwmon1/temp1_input",
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
for (const source of tempSources) {
|
|
93
|
+
if (fs.existsSync(source)) {
|
|
94
|
+
const temp = fs.readFileSync(source, "utf8").trim();
|
|
95
|
+
const tempC = Math.round(parseInt(temp) / 1000);
|
|
96
|
+
|
|
97
|
+
if (tempC > 0 && tempC < 150) {
|
|
98
|
+
const result = `${tempC}°C`;
|
|
99
|
+
setCachedValue(context.cache, "temperature", result);
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
} catch {}
|
|
105
|
+
|
|
106
|
+
setCachedValue(context.cache, "temperature", "");
|
|
107
|
+
return "";
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Gets count of running system services
|
|
112
|
+
* Uses systemctl or service command (Linux only)
|
|
113
|
+
* @param context - Info context with cache
|
|
114
|
+
* @returns Service count with "services" suffix or empty string
|
|
115
|
+
* @example "125 services", "89 services"
|
|
116
|
+
*/
|
|
117
|
+
export function services_running(context: InfoContext): string {
|
|
118
|
+
const cached = getCachedValue(context.cache, "services_running");
|
|
119
|
+
if (cached !== null) return cached;
|
|
120
|
+
|
|
121
|
+
if (!IS_LINUX) {
|
|
122
|
+
setCachedValue(context.cache, "services_running", "");
|
|
123
|
+
return "";
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
let serviceCount = 0;
|
|
128
|
+
|
|
129
|
+
if (commandExists("systemctl")) {
|
|
130
|
+
const services = execCommand(
|
|
131
|
+
"systemctl list-units --type=service --state=running --no-pager"
|
|
132
|
+
);
|
|
133
|
+
serviceCount = services
|
|
134
|
+
.split("\n")
|
|
135
|
+
.filter((line) => line.includes(".service")).length;
|
|
136
|
+
} else if (commandExists("service")) {
|
|
137
|
+
const services = execCommand("service --status-all");
|
|
138
|
+
serviceCount = services
|
|
139
|
+
.split("\n")
|
|
140
|
+
.filter((line) => line.includes("+")).length;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (serviceCount > 0) {
|
|
144
|
+
const result = `${serviceCount} services`;
|
|
145
|
+
setCachedValue(context.cache, "services_running", result);
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
148
|
+
} catch {}
|
|
149
|
+
|
|
150
|
+
setCachedValue(context.cache, "services_running", "");
|
|
151
|
+
return "";
|
|
152
|
+
}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview System Information API
|
|
3
|
+
*
|
|
4
|
+
* A comprehensive cross-platform system information collection API.
|
|
5
|
+
* Provides clean JSON data without formatting, suitable for programmatic use.
|
|
6
|
+
*
|
|
7
|
+
* @module system-info-api
|
|
8
|
+
* @author vtempest
|
|
9
|
+
* @license rights.institute/prosper
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import os from "os";
|
|
13
|
+
import fs from "fs";
|
|
14
|
+
import https from "https";
|
|
15
|
+
import path from "path";
|
|
16
|
+
import type { SystemInfo, SystemInfoOptions } from "./systeminfo-types";
|
|
17
|
+
import { CACHE_FILE } from "./info/settings"; // Using CACHE_FILE from settings to ensure consistency
|
|
18
|
+
|
|
19
|
+
// Import info functions from modules
|
|
20
|
+
import { user, hostname, os_info, kernel, device } from "./info/platform";
|
|
21
|
+
import { cpu, gpu, screen_resolution } from "./info/hardware";
|
|
22
|
+
import {
|
|
23
|
+
disk_used,
|
|
24
|
+
ram_used,
|
|
25
|
+
memory_available,
|
|
26
|
+
swap_used,
|
|
27
|
+
mount_points,
|
|
28
|
+
} from "./info/memory";
|
|
29
|
+
import { top_process, uptime, users_logged_in } from "./info/process";
|
|
30
|
+
import {
|
|
31
|
+
ip,
|
|
32
|
+
iplocal,
|
|
33
|
+
city,
|
|
34
|
+
domain,
|
|
35
|
+
isp,
|
|
36
|
+
network_interfaces,
|
|
37
|
+
ports,
|
|
38
|
+
} from "./info/network";
|
|
39
|
+
import {
|
|
40
|
+
services_running,
|
|
41
|
+
temperature,
|
|
42
|
+
battery,
|
|
43
|
+
load_average,
|
|
44
|
+
} from "./info/system-status";
|
|
45
|
+
import { shell, packages, containers } from "./info/software";
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Cache duration configuration for different system information types
|
|
49
|
+
* Values are in milliseconds
|
|
50
|
+
*/
|
|
51
|
+
const CACHE_DURATION = {
|
|
52
|
+
ip: 5 * 60 * 1000,
|
|
53
|
+
cpu: 24 * 60 * 60 * 1000,
|
|
54
|
+
gpu: 24 * 60 * 60 * 1000,
|
|
55
|
+
os: 24 * 60 * 60 * 1000,
|
|
56
|
+
device: 24 * 60 * 60 * 1000,
|
|
57
|
+
kernel: 60 * 60 * 1000,
|
|
58
|
+
pacman: 10 * 60 * 1000,
|
|
59
|
+
ports: 5 * 60 * 1000,
|
|
60
|
+
containers: 5 * 60 * 1000,
|
|
61
|
+
top_process: 5 * 1000,
|
|
62
|
+
disk_used: 60 * 1000,
|
|
63
|
+
ram_used: 10 * 1000,
|
|
64
|
+
services_running: 5 * 60 * 1000,
|
|
65
|
+
temperature: 30 * 1000,
|
|
66
|
+
battery: 60 * 1000,
|
|
67
|
+
network_interfaces: 5 * 60 * 1000,
|
|
68
|
+
mount_points: 10 * 60 * 1000,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Platform detection constants
|
|
73
|
+
*/
|
|
74
|
+
const IS_WINDOWS = os.platform() === "win32";
|
|
75
|
+
const IS_MAC = os.platform() === "darwin";
|
|
76
|
+
const IS_LINUX = os.platform() === "linux";
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Default IPInfo.io API token for geolocation
|
|
80
|
+
*/
|
|
81
|
+
const DEFAULT_IPINFO_TOKEN = "da2d6cc4baa5d1";
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Default network request timeout in milliseconds
|
|
85
|
+
*/
|
|
86
|
+
const DEFAULT_NETWORK_TIMEOUT = 5000;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Represents a cached value with timestamp
|
|
90
|
+
*/
|
|
91
|
+
interface CacheEntry {
|
|
92
|
+
value: any;
|
|
93
|
+
timestamp: number;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Cache storage structure
|
|
98
|
+
*/
|
|
99
|
+
interface Cache {
|
|
100
|
+
[key: string]: CacheEntry;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* IP information from ipinfo.io API
|
|
105
|
+
*/
|
|
106
|
+
interface IPInfo {
|
|
107
|
+
ip?: string;
|
|
108
|
+
city?: string;
|
|
109
|
+
hostname?: string;
|
|
110
|
+
org?: string;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Context object passed to info collection functions
|
|
115
|
+
*/
|
|
116
|
+
interface InfoContext {
|
|
117
|
+
cache: Cache;
|
|
118
|
+
ipInfo?: IPInfo;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Loads cache from disk
|
|
123
|
+
*/
|
|
124
|
+
function loadCache(): Cache {
|
|
125
|
+
try {
|
|
126
|
+
if (fs.existsSync(CACHE_FILE)) {
|
|
127
|
+
return JSON.parse(fs.readFileSync(CACHE_FILE, "utf8"));
|
|
128
|
+
}
|
|
129
|
+
} catch (error) {
|
|
130
|
+
// Corrupted cache - return empty object
|
|
131
|
+
}
|
|
132
|
+
return {};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Saves cache to disk
|
|
137
|
+
*/
|
|
138
|
+
function saveCache(cache: Cache): void {
|
|
139
|
+
try {
|
|
140
|
+
const cacheDir = path.dirname(CACHE_FILE);
|
|
141
|
+
if (!fs.existsSync(cacheDir)) {
|
|
142
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
143
|
+
}
|
|
144
|
+
fs.writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2));
|
|
145
|
+
} catch (error) {
|
|
146
|
+
// Silently fail if can't write cache
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Fetches IP geolocation information from ipinfo.io API
|
|
152
|
+
*/
|
|
153
|
+
async function fetchIPInfo(
|
|
154
|
+
token: string = DEFAULT_IPINFO_TOKEN,
|
|
155
|
+
timeout: number = DEFAULT_NETWORK_TIMEOUT
|
|
156
|
+
): Promise<IPInfo> {
|
|
157
|
+
return new Promise((resolve) => {
|
|
158
|
+
const url = `https://ipinfo.io/json${token ? `?token=${token}` : ""}`;
|
|
159
|
+
|
|
160
|
+
const req = https.get(url, (res) => {
|
|
161
|
+
let data = "";
|
|
162
|
+
res.on("data", (chunk) => (data += chunk));
|
|
163
|
+
res.on("end", () => {
|
|
164
|
+
try {
|
|
165
|
+
resolve(JSON.parse(data));
|
|
166
|
+
} catch {
|
|
167
|
+
resolve({});
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
req.on("error", () => resolve({}));
|
|
173
|
+
req.setTimeout(timeout, () => {
|
|
174
|
+
req.destroy();
|
|
175
|
+
resolve({});
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* System information collection functions map
|
|
182
|
+
*/
|
|
183
|
+
export const infoFunctions = {
|
|
184
|
+
user,
|
|
185
|
+
hostname,
|
|
186
|
+
ip,
|
|
187
|
+
iplocal,
|
|
188
|
+
city,
|
|
189
|
+
domain,
|
|
190
|
+
isp,
|
|
191
|
+
os: os_info, // Mapped from os_info
|
|
192
|
+
cpu,
|
|
193
|
+
gpu,
|
|
194
|
+
disk_used,
|
|
195
|
+
ram_used,
|
|
196
|
+
top_process,
|
|
197
|
+
uptime,
|
|
198
|
+
device,
|
|
199
|
+
kernel,
|
|
200
|
+
shell,
|
|
201
|
+
pacman: packages, // Mapped from packages
|
|
202
|
+
ports,
|
|
203
|
+
containers,
|
|
204
|
+
memory_available,
|
|
205
|
+
swap_used,
|
|
206
|
+
load_average,
|
|
207
|
+
users_logged_in,
|
|
208
|
+
network_interfaces,
|
|
209
|
+
mount_points,
|
|
210
|
+
services_running,
|
|
211
|
+
temperature,
|
|
212
|
+
battery,
|
|
213
|
+
screen_resolution,
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Get all system information as a clean JSON object
|
|
218
|
+
*/
|
|
219
|
+
export async function getSystemInfo(
|
|
220
|
+
options: SystemInfoOptions = {}
|
|
221
|
+
): Promise<SystemInfo> {
|
|
222
|
+
const cache = loadCache();
|
|
223
|
+
const context: InfoContext = { cache };
|
|
224
|
+
|
|
225
|
+
// Check if we need IP info
|
|
226
|
+
// Some functions need IP info, so we fetch it once if needed by any function
|
|
227
|
+
// For now we always try to fetch it if not cached, as ip/city/etc depend on it
|
|
228
|
+
// Optimization: check if we actually need to run those functions based on options if provided
|
|
229
|
+
// But current implementation fetches it always if missing from cache
|
|
230
|
+
|
|
231
|
+
const cachedIPInfo = cache["ipInfo"]?.value;
|
|
232
|
+
if (cachedIPInfo) {
|
|
233
|
+
context.ipInfo = cachedIPInfo;
|
|
234
|
+
} else {
|
|
235
|
+
context.ipInfo = await fetchIPInfo();
|
|
236
|
+
// Cache IP info itself
|
|
237
|
+
cache["ipInfo"] = {
|
|
238
|
+
value: context.ipInfo,
|
|
239
|
+
timestamp: Date.now(),
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Collect all system information
|
|
244
|
+
const info: Partial<SystemInfo> = {
|
|
245
|
+
timestamp: new Date().toISOString(),
|
|
246
|
+
platform: IS_WINDOWS
|
|
247
|
+
? "windows"
|
|
248
|
+
: IS_MAC
|
|
249
|
+
? "macos"
|
|
250
|
+
: IS_LINUX
|
|
251
|
+
? "linux"
|
|
252
|
+
: "unknown",
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
// Call all info functions
|
|
256
|
+
for (const [key, fn] of Object.entries(infoFunctions)) {
|
|
257
|
+
try {
|
|
258
|
+
// most functions are sync but some are async (ip related)
|
|
259
|
+
const value = await fn(context);
|
|
260
|
+
info[key as keyof SystemInfo] = value as any;
|
|
261
|
+
} catch (error) {
|
|
262
|
+
info[key as keyof SystemInfo] = "" as any;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
saveCache(cache);
|
|
267
|
+
|
|
268
|
+
return info as SystemInfo;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export { loadCache, saveCache };
|