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.
@@ -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 };