about-system 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +235 -0
- package/about-system.js +1430 -0
- package/package.json +57 -0
package/about-system.js
ADDED
|
@@ -0,0 +1,1430 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* =========================================================
|
|
5
|
+
* System Info with Emojis - Node.js Version
|
|
6
|
+
* =========================================================
|
|
7
|
+
* Node.js script to display key system information with emojis
|
|
8
|
+
* The output and order are fully customizable: edit DISPLAY_ORDER
|
|
9
|
+
* to control which info blocks are shown and in what order.
|
|
10
|
+
* Supported blocks: user, hostname, ip, iplocal, city, domain, isp, os,
|
|
11
|
+
* cpu, gpu, disk_used, ram_used, top_process, device, kernel, uptime,
|
|
12
|
+
* shell, pacman, ports, containers, memory_available, swap_used,
|
|
13
|
+
* load_average, users_logged_in, network_interfaces, mount_points,
|
|
14
|
+
* services_running, temperature, battery, screen_resolution.
|
|
15
|
+
* Network info (ip, city, domain, isp) is fetched from ipinfo.io
|
|
16
|
+
* only if needed.
|
|
17
|
+
*
|
|
18
|
+
* Author: Adapted from vtempest bash version (2022-25)
|
|
19
|
+
* Updated: 2025-09-01
|
|
20
|
+
* License: MIT
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const os = require('os');
|
|
24
|
+
const fs = require('fs');
|
|
25
|
+
const path = require('path');
|
|
26
|
+
const { execSync } = require('child_process');
|
|
27
|
+
const https = require('https');
|
|
28
|
+
|
|
29
|
+
// Cache configuration
|
|
30
|
+
const CACHE_FILE = path.join(os.tmpdir(), 'systeminfo-cache.json');
|
|
31
|
+
const SETTINGS_FILE = path.join(os.homedir(), '.config', 'systeminfo-settings.json');
|
|
32
|
+
const CACHE_DURATION = {
|
|
33
|
+
// Cache durations in milliseconds
|
|
34
|
+
ip: 5 * 60 * 1000, // 5 minutes for IP info
|
|
35
|
+
cpu: 24 * 60 * 60 * 1000, // 24 hours for CPU info
|
|
36
|
+
gpu: 24 * 60 * 60 * 1000, // 24 hours for GPU info
|
|
37
|
+
os: 24 * 60 * 60 * 1000, // 24 hours for OS info
|
|
38
|
+
device: 24 * 60 * 60 * 1000, // 24 hours for device info
|
|
39
|
+
kernel: 60 * 60 * 1000, // 1 hour for kernel (can change on updates)
|
|
40
|
+
pacman: 10 * 60 * 1000, // 10 minutes for installed packages
|
|
41
|
+
ports: 30 * 1000, // 30 seconds for ports (changes frequently)
|
|
42
|
+
containers: 30 * 1000, // 30 seconds for containers
|
|
43
|
+
top_process: 5 * 1000, // 5 seconds for top process
|
|
44
|
+
disk_used: 60 * 1000, // 1 minute for disk usage
|
|
45
|
+
ram_used: 10 * 1000, // 10 seconds for RAM usage
|
|
46
|
+
services_running: 30 * 1000, // 30 seconds for services
|
|
47
|
+
temperature: 30 * 1000, // 30 seconds for temperature
|
|
48
|
+
battery: 60 * 1000, // 1 minute for battery
|
|
49
|
+
network_interfaces: 5 * 60 * 1000, // 5 minutes for network interfaces
|
|
50
|
+
mount_points: 10 * 60 * 1000 // 10 minutes for mount points
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// Default settings
|
|
54
|
+
const DEFAULT_SETTINGS = {
|
|
55
|
+
version: "1.0.0",
|
|
56
|
+
display_order: [
|
|
57
|
+
['user', 'hostname', 'os', 'cpu', 'gpu', 'device', 'kernel'],
|
|
58
|
+
['disk_used', 'ram_used', 'top_process', 'uptime', 'temperature', 'battery', 'load_average'],
|
|
59
|
+
['ip', 'iplocal', 'city', 'domain', 'isp'],
|
|
60
|
+
['shell', 'pacman', 'ports', 'containers', 'services_running']
|
|
61
|
+
],
|
|
62
|
+
colors: {
|
|
63
|
+
user: "red",
|
|
64
|
+
hostname: "orange",
|
|
65
|
+
disk_used: "purple",
|
|
66
|
+
ram_used: "yellow",
|
|
67
|
+
top_process: "magenta",
|
|
68
|
+
uptime: "cyan",
|
|
69
|
+
ip: "green",
|
|
70
|
+
iplocal: "yellow",
|
|
71
|
+
city: "green",
|
|
72
|
+
domain: "gray",
|
|
73
|
+
isp: "lightblue",
|
|
74
|
+
os: "blue",
|
|
75
|
+
cpu: "orange",
|
|
76
|
+
gpu: "yellow",
|
|
77
|
+
device: "blue",
|
|
78
|
+
kernel: "green",
|
|
79
|
+
shell: "orange",
|
|
80
|
+
pacman: "cyan",
|
|
81
|
+
ports: "multicolor",
|
|
82
|
+
containers: "green",
|
|
83
|
+
memory_available: "blue",
|
|
84
|
+
swap_used: "purple",
|
|
85
|
+
load_average: "red",
|
|
86
|
+
users_logged_in: "cyan",
|
|
87
|
+
network_interfaces: "yellow",
|
|
88
|
+
mount_points: "gray",
|
|
89
|
+
services_running: "green",
|
|
90
|
+
temperature: "red",
|
|
91
|
+
battery: "green",
|
|
92
|
+
screen_resolution: "blue"
|
|
93
|
+
},
|
|
94
|
+
cache: {
|
|
95
|
+
enabled: true,
|
|
96
|
+
custom_durations: {}
|
|
97
|
+
},
|
|
98
|
+
network: {
|
|
99
|
+
timeout: 5000,
|
|
100
|
+
ipinfo_token: "da2d6cc4baa5d1",
|
|
101
|
+
show_offline_message: true
|
|
102
|
+
},
|
|
103
|
+
display: {
|
|
104
|
+
show_emojis: true,
|
|
105
|
+
compact_mode: false,
|
|
106
|
+
separator: "\n",
|
|
107
|
+
max_width: 120,
|
|
108
|
+
multiline: true,
|
|
109
|
+
group_similar: true
|
|
110
|
+
},
|
|
111
|
+
advanced: {
|
|
112
|
+
debug: false,
|
|
113
|
+
performance_logging: false,
|
|
114
|
+
fallback_commands: true
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// Color codes for terminal output
|
|
119
|
+
const colors = {
|
|
120
|
+
reset: '\x1b[0m',
|
|
121
|
+
red: '\x1b[38;5;196m',
|
|
122
|
+
orange: '\x1b[38;5;208m',
|
|
123
|
+
yellow: '\x1b[38;5;226m',
|
|
124
|
+
green: '\x1b[38;5;46m',
|
|
125
|
+
blue: '\x1b[38;5;39m',
|
|
126
|
+
cyan: '\x1b[38;5;51m',
|
|
127
|
+
purple: '\x1b[38;5;171m',
|
|
128
|
+
magenta: '\x1b[38;5;213m',
|
|
129
|
+
gray: '\x1b[38;5;250m',
|
|
130
|
+
lightblue: '\x1b[38;5;220m'
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// Platform detection constants
|
|
134
|
+
const IS_WINDOWS = os.platform() === 'win32';
|
|
135
|
+
const IS_MAC = os.platform() === 'darwin';
|
|
136
|
+
const IS_LINUX = os.platform() === 'linux';
|
|
137
|
+
|
|
138
|
+
// Cache management functions
|
|
139
|
+
function loadCache() {
|
|
140
|
+
try {
|
|
141
|
+
if (fs.existsSync(CACHE_FILE)) {
|
|
142
|
+
const cacheData = JSON.parse(fs.readFileSync(CACHE_FILE, 'utf8'));
|
|
143
|
+
return cacheData;
|
|
144
|
+
}
|
|
145
|
+
} catch (error) {
|
|
146
|
+
// If cache is corrupted, ignore it
|
|
147
|
+
}
|
|
148
|
+
return {};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function saveCache(cache) {
|
|
152
|
+
try {
|
|
153
|
+
fs.writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2));
|
|
154
|
+
} catch (error) {
|
|
155
|
+
// Silently fail if can't write cache
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function isCacheValid(cacheEntry, key) {
|
|
160
|
+
if (!cacheEntry || !cacheEntry.timestamp) return false;
|
|
161
|
+
const age = Date.now() - cacheEntry.timestamp;
|
|
162
|
+
const maxAge = CACHE_DURATION[key] || 60000; // Default 1 minute
|
|
163
|
+
return age < maxAge;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Cache helper functions
|
|
167
|
+
function getCachedValue(cache, key, settings = null) {
|
|
168
|
+
if (!cache[key]) return null;
|
|
169
|
+
|
|
170
|
+
const cacheEntry = cache[key];
|
|
171
|
+
if (!isCacheValid(cacheEntry, key)) {
|
|
172
|
+
delete cache[key];
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return cacheEntry.value;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function setCachedValue(cache, key, value) {
|
|
180
|
+
cache[key] = {
|
|
181
|
+
value: value,
|
|
182
|
+
timestamp: Date.now()
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Settings save function
|
|
187
|
+
function saveSettings(settings) {
|
|
188
|
+
try {
|
|
189
|
+
const configDir = path.dirname(SETTINGS_FILE);
|
|
190
|
+
if (!fs.existsSync(configDir)) {
|
|
191
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
192
|
+
}
|
|
193
|
+
fs.writeFileSync(SETTINGS_FILE, JSON.stringify(settings, null, 2));
|
|
194
|
+
return true;
|
|
195
|
+
} catch (error) {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function loadSettings() {
|
|
201
|
+
let settings;
|
|
202
|
+
try {
|
|
203
|
+
if (fs.existsSync(SETTINGS_FILE)) {
|
|
204
|
+
settings = JSON.parse(fs.readFileSync(SETTINGS_FILE, 'utf8'));
|
|
205
|
+
// Merge with defaults to ensure all properties exist
|
|
206
|
+
settings = { ...DEFAULT_SETTINGS, ...settings };
|
|
207
|
+
} else {
|
|
208
|
+
settings = DEFAULT_SETTINGS;
|
|
209
|
+
}
|
|
210
|
+
} catch {
|
|
211
|
+
settings = DEFAULT_SETTINGS;
|
|
212
|
+
}
|
|
213
|
+
return settings;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Helper function to execute shell commands safely
|
|
217
|
+
function execCommand(command, options = {}) {
|
|
218
|
+
try {
|
|
219
|
+
const cmd = IS_WINDOWS ? `cmd /c ${command}` : command;
|
|
220
|
+
return execSync(cmd, {
|
|
221
|
+
encoding: 'utf8',
|
|
222
|
+
timeout: 10000,
|
|
223
|
+
stdio: ['pipe', 'pipe', 'ignore'],
|
|
224
|
+
...options
|
|
225
|
+
}).toString().trim();
|
|
226
|
+
} catch (error) {
|
|
227
|
+
return '';
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Helper function to check if command exists
|
|
232
|
+
function commandExists(command) {
|
|
233
|
+
try {
|
|
234
|
+
if (IS_WINDOWS) {
|
|
235
|
+
execSync(`where ${command}`, { stdio: 'ignore' });
|
|
236
|
+
} else {
|
|
237
|
+
execSync(`which ${command}`, { stdio: 'ignore' });
|
|
238
|
+
}
|
|
239
|
+
return true;
|
|
240
|
+
} catch {
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Helper function to fetch IP info from ipinfo.io
|
|
246
|
+
async function fetchIPInfo(settings) {
|
|
247
|
+
return new Promise((resolve) => {
|
|
248
|
+
const token = settings.network.ipinfo_token;
|
|
249
|
+
const timeout = settings.network.timeout;
|
|
250
|
+
const url = `https://ipinfo.io/json${token ? `?token=${token}` : ''}`;
|
|
251
|
+
|
|
252
|
+
const req = https.get(url, (res) => {
|
|
253
|
+
let data = '';
|
|
254
|
+
res.on('data', (chunk) => data += chunk);
|
|
255
|
+
res.on('end', () => {
|
|
256
|
+
try {
|
|
257
|
+
resolve(JSON.parse(data));
|
|
258
|
+
} catch {
|
|
259
|
+
resolve({});
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
req.on('error', () => resolve({}));
|
|
265
|
+
req.setTimeout(timeout, () => {
|
|
266
|
+
req.destroy();
|
|
267
|
+
resolve({});
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// System info functions
|
|
273
|
+
const infoFunctions = {
|
|
274
|
+
user(settings) {
|
|
275
|
+
const color = colors[settings.colors.user] || colors.red;
|
|
276
|
+
const emoji = settings.display.show_emojis ? '👤 ' : '';
|
|
277
|
+
return `${color}${emoji}${os.userInfo().username}`;
|
|
278
|
+
},
|
|
279
|
+
|
|
280
|
+
hostname(settings) {
|
|
281
|
+
const color = colors[settings.colors.hostname] || colors.orange;
|
|
282
|
+
const emoji = settings.display.show_emojis ? '🏠 ' : '';
|
|
283
|
+
return `${color}${emoji}${os.hostname()}`;
|
|
284
|
+
},
|
|
285
|
+
|
|
286
|
+
async ip(settings) {
|
|
287
|
+
const cached = getCachedValue(this.cache, 'ip', settings);
|
|
288
|
+
if (cached) return cached;
|
|
289
|
+
|
|
290
|
+
if (!this.ipInfo) {
|
|
291
|
+
const emoji = settings.display.show_emojis ? '🌎 ' : '';
|
|
292
|
+
const result = settings.network.show_offline_message ?
|
|
293
|
+
`${colors.gray}${emoji}No Network` : '';
|
|
294
|
+
setCachedValue(this.cache, 'ip', result);
|
|
295
|
+
return result;
|
|
296
|
+
}
|
|
297
|
+
const ip = this.ipInfo.ip || 'No IP';
|
|
298
|
+
const color = colors[settings.colors.ip] || colors.green;
|
|
299
|
+
const emoji = settings.display.show_emojis ? '🌎 ' : '';
|
|
300
|
+
const result = `${color}${emoji}${ip}`;
|
|
301
|
+
setCachedValue(this.cache, 'ip', result);
|
|
302
|
+
return result;
|
|
303
|
+
},
|
|
304
|
+
|
|
305
|
+
iplocal(settings) {
|
|
306
|
+
const color = colors[settings.colors.iplocal] || colors.yellow;
|
|
307
|
+
const emoji = settings.display.show_emojis ? '🌐 ' : '';
|
|
308
|
+
|
|
309
|
+
if (IS_LINUX) {
|
|
310
|
+
// Try ifconfig first (like bash script)
|
|
311
|
+
try {
|
|
312
|
+
const ifconfig = execCommand('ifconfig 2>/dev/null');
|
|
313
|
+
const wlanMatch = ifconfig.match(/wlan0[\s\S]*?inet (\d+\.\d+\.\d+\.\d+)/);
|
|
314
|
+
if (wlanMatch) {
|
|
315
|
+
return `${color}${emoji}${wlanMatch[1]}`;
|
|
316
|
+
}
|
|
317
|
+
} catch {}
|
|
318
|
+
|
|
319
|
+
// Fallback to ip command (like bash script)
|
|
320
|
+
try {
|
|
321
|
+
const ipAddr = execCommand('ip addr show 2>/dev/null');
|
|
322
|
+
const addresses = [];
|
|
323
|
+
const matches = ipAddr.matchAll(/inet (\d+\.\d+\.\d+\.\d+)\/\d+/g);
|
|
324
|
+
for (const match of matches) {
|
|
325
|
+
if (!match[1].startsWith('127.')) {
|
|
326
|
+
addresses.push(match[1]);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
if (addresses.length > 0) {
|
|
330
|
+
return `${color}${emoji}${addresses.join(' ')}`;
|
|
331
|
+
}
|
|
332
|
+
} catch {}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Fallback to Node.js method
|
|
336
|
+
const interfaces = os.networkInterfaces();
|
|
337
|
+
const addresses = [];
|
|
338
|
+
|
|
339
|
+
for (const name of Object.keys(interfaces)) {
|
|
340
|
+
for (const interface of interfaces[name]) {
|
|
341
|
+
if (interface.family === 'IPv4' && !interface.internal) {
|
|
342
|
+
addresses.push(interface.address);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (addresses.length === 0) return '';
|
|
348
|
+
return `${color}${emoji}${addresses.join(' ')}`;
|
|
349
|
+
},
|
|
350
|
+
|
|
351
|
+
async city(settings) {
|
|
352
|
+
const cached = getCachedValue(this.cache, 'city', settings);
|
|
353
|
+
if (cached) return cached;
|
|
354
|
+
|
|
355
|
+
if (!this.ipInfo || !this.ipInfo.city) {
|
|
356
|
+
setCachedValue(this.cache, 'city', '');
|
|
357
|
+
return '';
|
|
358
|
+
}
|
|
359
|
+
const color = colors[settings.colors.city] || colors.green;
|
|
360
|
+
const emoji = settings.display.show_emojis ? '📍 ' : '';
|
|
361
|
+
const result = `${color}${emoji}${this.ipInfo.city}`;
|
|
362
|
+
setCachedValue(this.cache, 'city', result);
|
|
363
|
+
return result;
|
|
364
|
+
},
|
|
365
|
+
|
|
366
|
+
async domain(settings) {
|
|
367
|
+
const cached = getCachedValue(this.cache, 'domain', settings);
|
|
368
|
+
if (cached) return cached;
|
|
369
|
+
|
|
370
|
+
if (!this.ipInfo || !this.ipInfo.hostname) {
|
|
371
|
+
setCachedValue(this.cache, 'domain', '');
|
|
372
|
+
return '';
|
|
373
|
+
}
|
|
374
|
+
const color = colors[settings.colors.domain] || colors.gray;
|
|
375
|
+
const emoji = settings.display.show_emojis ? '🔗 ' : '';
|
|
376
|
+
const result = `${color}${emoji}http://${this.ipInfo.hostname}`;
|
|
377
|
+
setCachedValue(this.cache, 'domain', result);
|
|
378
|
+
return result;
|
|
379
|
+
},
|
|
380
|
+
|
|
381
|
+
async isp(settings) {
|
|
382
|
+
const cached = getCachedValue(this.cache, 'isp', settings);
|
|
383
|
+
if (cached) return cached;
|
|
384
|
+
|
|
385
|
+
if (!this.ipInfo || !this.ipInfo.org) {
|
|
386
|
+
setCachedValue(this.cache, 'isp', '');
|
|
387
|
+
return '';
|
|
388
|
+
}
|
|
389
|
+
const isp = this.ipInfo.org.split(' ').slice(1).join(' '); // Remove AS number
|
|
390
|
+
const color = colors[settings.colors.isp] || colors.lightblue;
|
|
391
|
+
const emoji = settings.display.show_emojis ? '👮 ' : '';
|
|
392
|
+
const result = `${color}${emoji}${isp}`;
|
|
393
|
+
setCachedValue(this.cache, 'isp', result);
|
|
394
|
+
return result;
|
|
395
|
+
},
|
|
396
|
+
|
|
397
|
+
os(settings) {
|
|
398
|
+
const cached = getCachedValue(this.cache, 'os', settings);
|
|
399
|
+
if (cached) return cached;
|
|
400
|
+
|
|
401
|
+
const platform = os.platform();
|
|
402
|
+
const release = os.release();
|
|
403
|
+
let osName = '';
|
|
404
|
+
|
|
405
|
+
if (IS_WINDOWS) {
|
|
406
|
+
try {
|
|
407
|
+
const version = execCommand('ver');
|
|
408
|
+
const match = version.match(/Microsoft Windows \[Version ([^\]]+)\]/);
|
|
409
|
+
osName = match ? `Windows ${match[1]}` : `Windows ${release}`;
|
|
410
|
+
} catch {
|
|
411
|
+
osName = `Windows ${release}`;
|
|
412
|
+
}
|
|
413
|
+
} else if (IS_MAC) {
|
|
414
|
+
osName = `macOS ${release}`;
|
|
415
|
+
} else if (IS_LINUX) {
|
|
416
|
+
try {
|
|
417
|
+
const osRelease = fs.readFileSync('/etc/os-release', 'utf8');
|
|
418
|
+
const nameMatch = osRelease.match(/^NAME="([^"]+)"/m);
|
|
419
|
+
const versionMatch = osRelease.match(/^VERSION_ID="([^"]+)"/m);
|
|
420
|
+
osName = nameMatch ? nameMatch[1] : 'Linux';
|
|
421
|
+
if (versionMatch) osName += ` ${versionMatch[1]}`;
|
|
422
|
+
} catch {
|
|
423
|
+
osName = `Linux ${release}`;
|
|
424
|
+
}
|
|
425
|
+
} else {
|
|
426
|
+
osName = `${platform} ${release}`;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const color = colors[settings.colors.os] || colors.blue;
|
|
430
|
+
const emoji = settings.display.show_emojis ? '⚡ ' : '';
|
|
431
|
+
const result = `${color}${emoji}${osName}`;
|
|
432
|
+
setCachedValue(this.cache, 'os', result);
|
|
433
|
+
return result;
|
|
434
|
+
},
|
|
435
|
+
|
|
436
|
+
cpu(settings) {
|
|
437
|
+
const cached = getCachedValue(this.cache, 'cpu', settings);
|
|
438
|
+
if (cached) return cached;
|
|
439
|
+
|
|
440
|
+
let cpuName = '';
|
|
441
|
+
|
|
442
|
+
if (IS_LINUX) {
|
|
443
|
+
// Try lscpu first (like bash script)
|
|
444
|
+
try {
|
|
445
|
+
const lscpu = execCommand('lscpu');
|
|
446
|
+
const modelMatch = lscpu.match(/Model name:\s*([^\n,]+)/);
|
|
447
|
+
if (modelMatch) {
|
|
448
|
+
cpuName = modelMatch[1].trim();
|
|
449
|
+
}
|
|
450
|
+
} catch {}
|
|
451
|
+
|
|
452
|
+
// Fallback to /proc/cpuinfo (like bash script)
|
|
453
|
+
if (!cpuName) {
|
|
454
|
+
try {
|
|
455
|
+
const cpuInfo = fs.readFileSync('/proc/cpuinfo', 'utf8');
|
|
456
|
+
const modelMatch = cpuInfo.match(/model name\s*:\s*([^\n]+)/);
|
|
457
|
+
const hardwareMatch = cpuInfo.match(/Hardware\s*:\s*([^\n]+)/);
|
|
458
|
+
|
|
459
|
+
if (modelMatch) {
|
|
460
|
+
cpuName = modelMatch[1].trim();
|
|
461
|
+
} else if (hardwareMatch) {
|
|
462
|
+
cpuName = hardwareMatch[1].trim();
|
|
463
|
+
}
|
|
464
|
+
} catch {}
|
|
465
|
+
}
|
|
466
|
+
} else {
|
|
467
|
+
// Use Node.js os module for other platforms
|
|
468
|
+
const cpus = os.cpus();
|
|
469
|
+
if (cpus.length > 0) {
|
|
470
|
+
cpuName = cpus[0].model;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
if (!cpuName) {
|
|
475
|
+
setCachedValue(this.cache, 'cpu', '');
|
|
476
|
+
return '';
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
const color = colors[settings.colors.cpu] || colors.orange;
|
|
480
|
+
const emoji = settings.display.show_emojis ? '📈 ' : '';
|
|
481
|
+
const result = `${color}${emoji}${cpuName}`;
|
|
482
|
+
setCachedValue(this.cache, 'cpu', result);
|
|
483
|
+
return result;
|
|
484
|
+
},
|
|
485
|
+
|
|
486
|
+
gpu(settings) {
|
|
487
|
+
const cached = getCachedValue(this.cache, 'gpu', settings);
|
|
488
|
+
if (cached) return cached;
|
|
489
|
+
|
|
490
|
+
if (IS_LINUX) {
|
|
491
|
+
try {
|
|
492
|
+
const lspci = execCommand('lspci');
|
|
493
|
+
// Look for VGA and specific GPU brands like the bash script
|
|
494
|
+
const gpuMatch = lspci.match(/VGA.*?(RTX|GeForce|AMD|Intel|NVIDIA)[^\n]*/i);
|
|
495
|
+
if (gpuMatch) {
|
|
496
|
+
let gpu = gpuMatch[0];
|
|
497
|
+
|
|
498
|
+
// Try to extract clean GPU name from brackets like bash script
|
|
499
|
+
const bracketMatch = gpu.match(/\[([^\]]+)\]/);
|
|
500
|
+
if (bracketMatch) {
|
|
501
|
+
gpu = bracketMatch[1];
|
|
502
|
+
} else {
|
|
503
|
+
// Fallback: clean up the string
|
|
504
|
+
gpu = gpu.replace(/^.*VGA[^:]*:\s*/, '').replace(/\s*\(.*\)$/, '').trim();
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
if (gpu) {
|
|
508
|
+
const color = colors[settings.colors.gpu] || colors.yellow;
|
|
509
|
+
const emoji = settings.display.show_emojis ? '🎮 ' : '';
|
|
510
|
+
const result = `${color}${emoji}${gpu}`;
|
|
511
|
+
setCachedValue(this.cache, 'gpu', result);
|
|
512
|
+
return result;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
} catch {}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
setCachedValue(this.cache, 'gpu', '');
|
|
519
|
+
return '';
|
|
520
|
+
},
|
|
521
|
+
|
|
522
|
+
disk_used(settings) {
|
|
523
|
+
const cached = getCachedValue(this.cache, 'disk_used', settings);
|
|
524
|
+
if (cached) return cached;
|
|
525
|
+
|
|
526
|
+
if (IS_LINUX) {
|
|
527
|
+
try {
|
|
528
|
+
const df = execCommand('df -h');
|
|
529
|
+
let diskUsage = '';
|
|
530
|
+
|
|
531
|
+
// Check for Android storage first
|
|
532
|
+
if (df.includes('/storage/emulated')) {
|
|
533
|
+
const match = df.match(/\s+(\d+%)\s+\/storage\/emulated/);
|
|
534
|
+
diskUsage = match ? match[1] : '';
|
|
535
|
+
} else {
|
|
536
|
+
// Check for root filesystem - look for lines ending with exactly " /"
|
|
537
|
+
const lines = df.split('\n');
|
|
538
|
+
for (const line of lines) {
|
|
539
|
+
if (line.trim().endsWith(' /')) {
|
|
540
|
+
const parts = line.trim().split(/\s+/);
|
|
541
|
+
// Find the percentage column (should contain %)
|
|
542
|
+
const percentIndex = parts.findIndex(part => part.includes('%'));
|
|
543
|
+
if (percentIndex !== -1) {
|
|
544
|
+
diskUsage = parts[percentIndex];
|
|
545
|
+
break;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// Fallback: look for any line with root mount point
|
|
551
|
+
if (!diskUsage) {
|
|
552
|
+
const rootMatch = df.match(/(\d+%)\s+\/\s*$/m);
|
|
553
|
+
diskUsage = rootMatch ? rootMatch[1] : '';
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
if (diskUsage) {
|
|
558
|
+
const color = colors[settings.colors.disk_used] || colors.purple;
|
|
559
|
+
const emoji = settings.display.show_emojis ? '📁 ' : '';
|
|
560
|
+
const result = `${color}${emoji}${diskUsage}`;
|
|
561
|
+
setCachedValue(this.cache, 'disk_used', result);
|
|
562
|
+
return result;
|
|
563
|
+
}
|
|
564
|
+
} catch {}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
setCachedValue(this.cache, 'disk_used', '');
|
|
568
|
+
return '';
|
|
569
|
+
},
|
|
570
|
+
|
|
571
|
+
ram_used(settings) {
|
|
572
|
+
const cached = getCachedValue(this.cache, 'ram_used', settings);
|
|
573
|
+
if (cached) return cached;
|
|
574
|
+
|
|
575
|
+
if (IS_LINUX) {
|
|
576
|
+
// Use /proc/meminfo for more accurate Linux memory info like bash script
|
|
577
|
+
try {
|
|
578
|
+
const meminfo = fs.readFileSync('/proc/meminfo', 'utf8');
|
|
579
|
+
const totalMatch = meminfo.match(/MemTotal:\s+(\d+) kB/);
|
|
580
|
+
const freeMatch = meminfo.match(/MemFree:\s+(\d+) kB/);
|
|
581
|
+
|
|
582
|
+
if (totalMatch && freeMatch) {
|
|
583
|
+
const totalMB = Math.round(parseInt(totalMatch[1]) / 1024);
|
|
584
|
+
const freeMB = Math.round(parseInt(freeMatch[1]) / 1024);
|
|
585
|
+
const usedMB = totalMB - freeMB;
|
|
586
|
+
|
|
587
|
+
const totalGB = Math.round(totalMB / 1024);
|
|
588
|
+
const usedGB = Math.round(usedMB / 1024);
|
|
589
|
+
|
|
590
|
+
const color = colors[settings.colors.ram_used] || colors.yellow;
|
|
591
|
+
const emoji = settings.display.show_emojis ? '💾 ' : '';
|
|
592
|
+
const result = `${color}${emoji}${usedGB}/${totalGB}GB`;
|
|
593
|
+
setCachedValue(this.cache, 'ram_used', result);
|
|
594
|
+
return result;
|
|
595
|
+
}
|
|
596
|
+
} catch {}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// Fallback to Node.js os module
|
|
600
|
+
const totalMem = os.totalmem();
|
|
601
|
+
const freeMem = os.freemem();
|
|
602
|
+
const usedMem = totalMem - freeMem;
|
|
603
|
+
|
|
604
|
+
const totalGB = Math.round(totalMem / (1024 * 1024 * 1024));
|
|
605
|
+
const usedGB = Math.round(usedMem / (1024 * 1024 * 1024));
|
|
606
|
+
|
|
607
|
+
const color = colors[settings.colors.ram_used] || colors.yellow;
|
|
608
|
+
const emoji = settings.display.show_emojis ? '💾 ' : '';
|
|
609
|
+
const result = `${color}${emoji}${usedGB}/${totalGB}GB`;
|
|
610
|
+
setCachedValue(this.cache, 'ram_used', result);
|
|
611
|
+
return result;
|
|
612
|
+
},
|
|
613
|
+
|
|
614
|
+
top_process(settings) {
|
|
615
|
+
const cached = getCachedValue(this.cache, 'top_process', settings);
|
|
616
|
+
if (cached) return cached;
|
|
617
|
+
|
|
618
|
+
if (IS_LINUX) {
|
|
619
|
+
try {
|
|
620
|
+
const ps = execCommand('ps -eo pcpu,comm --sort=-%cpu --no-headers');
|
|
621
|
+
const lines = ps.split('\n');
|
|
622
|
+
if (lines.length > 0) {
|
|
623
|
+
const topProcess = lines[0].trim().replace(/\s+/, ' ').split(' ');
|
|
624
|
+
const cpu = topProcess[0].replace(/\.\d+/, '%');
|
|
625
|
+
const process = topProcess[1].split('/').pop();
|
|
626
|
+
const color = colors[settings.colors.top_process] || colors.magenta;
|
|
627
|
+
const emoji = settings.display.show_emojis ? '🔝 ' : '';
|
|
628
|
+
const result = `${color}${emoji}${cpu} ${process}`;
|
|
629
|
+
setCachedValue(this.cache, 'top_process', result);
|
|
630
|
+
return result;
|
|
631
|
+
}
|
|
632
|
+
} catch {}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
setCachedValue(this.cache, 'top_process', '');
|
|
636
|
+
return '';
|
|
637
|
+
},
|
|
638
|
+
|
|
639
|
+
uptime(settings) {
|
|
640
|
+
const uptimeSeconds = os.uptime();
|
|
641
|
+
const days = Math.floor(uptimeSeconds / 86400);
|
|
642
|
+
const hours = Math.floor((uptimeSeconds % 86400) / 3600);
|
|
643
|
+
const minutes = Math.floor((uptimeSeconds % 3600) / 60);
|
|
644
|
+
|
|
645
|
+
const color = colors[settings.colors.uptime] || colors.cyan;
|
|
646
|
+
const emoji = settings.display.show_emojis ? '⏱️ ' : '';
|
|
647
|
+
return `${color}${emoji}${days}d ${hours}h ${minutes}m`;
|
|
648
|
+
},
|
|
649
|
+
|
|
650
|
+
device(settings) {
|
|
651
|
+
const cached = getCachedValue(this.cache, 'device', settings);
|
|
652
|
+
if (cached) return cached;
|
|
653
|
+
|
|
654
|
+
if (IS_LINUX) {
|
|
655
|
+
try {
|
|
656
|
+
// Check for Android
|
|
657
|
+
if (commandExists('getprop')) {
|
|
658
|
+
const device = execCommand('getprop ro.product.model');
|
|
659
|
+
if (device) {
|
|
660
|
+
const color = colors[settings.colors.device] || colors.blue;
|
|
661
|
+
const emoji = settings.display.show_emojis ? '💻 ' : '';
|
|
662
|
+
const result = `${color}${emoji}${device}`;
|
|
663
|
+
setCachedValue(this.cache, 'device', result);
|
|
664
|
+
return result;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// Check for DMI info
|
|
669
|
+
const dmiPath = '/sys/devices/virtual/dmi/id/product_name';
|
|
670
|
+
if (fs.existsSync(dmiPath)) {
|
|
671
|
+
const device = fs.readFileSync(dmiPath, 'utf8').trim();
|
|
672
|
+
if (device) {
|
|
673
|
+
const color = colors[settings.colors.device] || colors.blue;
|
|
674
|
+
const emoji = settings.display.show_emojis ? '💻 ' : '';
|
|
675
|
+
const result = `${color}${emoji}${device}`;
|
|
676
|
+
setCachedValue(this.cache, 'device', result);
|
|
677
|
+
return result;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
} catch {}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
setCachedValue(this.cache, 'device', '');
|
|
684
|
+
return '';
|
|
685
|
+
},
|
|
686
|
+
|
|
687
|
+
kernel(settings) {
|
|
688
|
+
const cached = getCachedValue(this.cache, 'kernel', settings);
|
|
689
|
+
if (cached) return cached;
|
|
690
|
+
|
|
691
|
+
const kernel = os.release();
|
|
692
|
+
const color = colors[settings.colors.kernel] || colors.green;
|
|
693
|
+
const emoji = settings.display.show_emojis ? '🔧 ' : '';
|
|
694
|
+
const result = `${color}${emoji}${kernel}`;
|
|
695
|
+
setCachedValue(this.cache, 'kernel', result);
|
|
696
|
+
return result;
|
|
697
|
+
},
|
|
698
|
+
|
|
699
|
+
shell(settings) {
|
|
700
|
+
if (IS_LINUX) {
|
|
701
|
+
try {
|
|
702
|
+
const ppid = process.ppid;
|
|
703
|
+
const shell = execCommand(`ps -p ${ppid} -o comm=`).split('/').pop();
|
|
704
|
+
const color = colors[settings.colors.shell] || colors.orange;
|
|
705
|
+
const emoji = settings.display.show_emojis ? '🐚 ' : '';
|
|
706
|
+
return `${color}${emoji}${shell}`;
|
|
707
|
+
} catch {}
|
|
708
|
+
}
|
|
709
|
+
return '';
|
|
710
|
+
},
|
|
711
|
+
|
|
712
|
+
pacman(settings) {
|
|
713
|
+
const cached = getCachedValue(this.cache, 'pacman', settings);
|
|
714
|
+
if (cached) return cached;
|
|
715
|
+
|
|
716
|
+
const commands = ['apt', 'npm', 'pip', 'docker', 'hx', 'nvim', 'bun', 'yay',
|
|
717
|
+
'pacman', 'yum', 'dnf', 'zypper', 'emerge', 'apk', 'snap', 'flatpak'];
|
|
718
|
+
const available = commands.filter(cmd => commandExists(cmd));
|
|
719
|
+
|
|
720
|
+
if (available.length === 0) {
|
|
721
|
+
setCachedValue(this.cache, 'pacman', '');
|
|
722
|
+
return '';
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
const color = colors[settings.colors.pacman] || colors.cyan;
|
|
726
|
+
const emoji = settings.display.show_emojis ? '🚀 ' : '';
|
|
727
|
+
const result = `${color}${emoji}${available.join(' ')}`;
|
|
728
|
+
setCachedValue(this.cache, 'pacman', result);
|
|
729
|
+
return result;
|
|
730
|
+
},
|
|
731
|
+
|
|
732
|
+
ports(settings) {
|
|
733
|
+
const cached = getCachedValue(this.cache, 'ports', settings);
|
|
734
|
+
if (cached) return cached;
|
|
735
|
+
|
|
736
|
+
if (IS_LINUX) {
|
|
737
|
+
try {
|
|
738
|
+
const lsof = execCommand('lsof -nP -iTCP -sTCP:LISTEN');
|
|
739
|
+
const lines = lsof.split('\n').slice(1); // Skip header
|
|
740
|
+
const ports = new Set();
|
|
741
|
+
|
|
742
|
+
lines.forEach(line => {
|
|
743
|
+
const parts = line.split(/\s+/);
|
|
744
|
+
if (parts.length >= 9) {
|
|
745
|
+
const address = parts[8];
|
|
746
|
+
const portMatch = address.match(/:(\d+)$/);
|
|
747
|
+
if (portMatch) {
|
|
748
|
+
const port = portMatch[1];
|
|
749
|
+
const process = parts[0].substring(0, 4);
|
|
750
|
+
ports.add(`${port}${process}`);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
});
|
|
754
|
+
|
|
755
|
+
if (ports.size > 0) {
|
|
756
|
+
const portList = Array.from(ports);
|
|
757
|
+
const emoji = settings.display.show_emojis ? '🔌 ' : '';
|
|
758
|
+
let output = ` ${emoji}`;
|
|
759
|
+
|
|
760
|
+
if (settings.colors.ports === 'multicolor') {
|
|
761
|
+
const colorCodes = [31, 32, 33, 34, 35, 36]; // Red, Green, Yellow, Blue, Magenta, Cyan
|
|
762
|
+
portList.forEach((port, index) => {
|
|
763
|
+
const color = colorCodes[index % colorCodes.length];
|
|
764
|
+
output += `\x1b[${color}m${port}\x1b[0m `;
|
|
765
|
+
});
|
|
766
|
+
} else {
|
|
767
|
+
const color = colors[settings.colors.ports] || colors.cyan;
|
|
768
|
+
output = `${color}${emoji}${portList.join(' ')}`;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
const result = output.trim();
|
|
772
|
+
setCachedValue(this.cache, 'ports', result);
|
|
773
|
+
return result;
|
|
774
|
+
}
|
|
775
|
+
} catch {}
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
setCachedValue(this.cache, 'ports', '');
|
|
779
|
+
return '';
|
|
780
|
+
},
|
|
781
|
+
|
|
782
|
+
containers(settings) {
|
|
783
|
+
const cached = getCachedValue(this.cache, 'containers', settings);
|
|
784
|
+
if (cached) return cached;
|
|
785
|
+
|
|
786
|
+
if (!commandExists('docker')) {
|
|
787
|
+
setCachedValue(this.cache, 'containers', '');
|
|
788
|
+
return '';
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
try {
|
|
792
|
+
const containerCount = execCommand('docker ps -q').split('\n').filter(line => line.trim()).length;
|
|
793
|
+
if (containerCount === 0) {
|
|
794
|
+
setCachedValue(this.cache, 'containers', '');
|
|
795
|
+
return '';
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
const containers = execCommand('docker ps --format "{{.Names}}\t{{.Ports}}"');
|
|
799
|
+
const lines = containers.split('\n').filter(line => line.trim());
|
|
800
|
+
|
|
801
|
+
const emoji = settings.display.show_emojis ? '📦' : '';
|
|
802
|
+
const color = colors[settings.colors.containers] || colors.green;
|
|
803
|
+
let output = ` ${color}${emoji}\x1b[0m`;
|
|
804
|
+
|
|
805
|
+
lines.forEach(line => {
|
|
806
|
+
const [name, ports] = line.split('\t');
|
|
807
|
+
if (name) {
|
|
808
|
+
output += ` ${color}${name}\x1b[0m`;
|
|
809
|
+
|
|
810
|
+
if (ports) {
|
|
811
|
+
const portMatches = ports.match(/->(\d+(-\d+)?)\//g);
|
|
812
|
+
if (portMatches) {
|
|
813
|
+
const uniquePorts = [...new Set(portMatches.map(p => p.replace(/->\d+(-\d+)?\//, '')))];
|
|
814
|
+
uniquePorts.forEach(port => {
|
|
815
|
+
output += ` \x1b[33m${port}\x1b[0m`;
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
setCachedValue(this.cache, 'containers', output);
|
|
823
|
+
return output;
|
|
824
|
+
} catch {}
|
|
825
|
+
|
|
826
|
+
setCachedValue(this.cache, 'containers', '');
|
|
827
|
+
return '';
|
|
828
|
+
},
|
|
829
|
+
|
|
830
|
+
// Additional Linux system info functions
|
|
831
|
+
memory_available(settings) {
|
|
832
|
+
if (!IS_LINUX) return '';
|
|
833
|
+
|
|
834
|
+
try {
|
|
835
|
+
const meminfo = fs.readFileSync('/proc/meminfo', 'utf8');
|
|
836
|
+
const availableMatch = meminfo.match(/MemAvailable:\s+(\d+) kB/);
|
|
837
|
+
if (availableMatch) {
|
|
838
|
+
const availableGB = Math.round(parseInt(availableMatch[1]) / 1024 / 1024);
|
|
839
|
+
const color = colors[settings.colors.memory_available] || colors.blue;
|
|
840
|
+
const emoji = settings.display.show_emojis ? '🧠 ' : '';
|
|
841
|
+
return `${color}${emoji}${availableGB}GB available`;
|
|
842
|
+
}
|
|
843
|
+
} catch {}
|
|
844
|
+
return '';
|
|
845
|
+
},
|
|
846
|
+
|
|
847
|
+
swap_used(settings) {
|
|
848
|
+
if (!IS_LINUX) return '';
|
|
849
|
+
|
|
850
|
+
try {
|
|
851
|
+
const meminfo = fs.readFileSync('/proc/meminfo', 'utf8');
|
|
852
|
+
const swapTotalMatch = meminfo.match(/SwapTotal:\s+(\d+) kB/);
|
|
853
|
+
const swapFreeMatch = meminfo.match(/SwapFree:\s+(\d+) kB/);
|
|
854
|
+
|
|
855
|
+
if (swapTotalMatch && swapFreeMatch) {
|
|
856
|
+
const swapTotal = parseInt(swapTotalMatch[1]);
|
|
857
|
+
const swapFree = parseInt(swapFreeMatch[1]);
|
|
858
|
+
const swapUsed = swapTotal - swapFree;
|
|
859
|
+
|
|
860
|
+
if (swapTotal > 0) {
|
|
861
|
+
const swapUsedPercent = Math.round((swapUsed / swapTotal) * 100);
|
|
862
|
+
const swapUsedMB = Math.round(swapUsed / 1024);
|
|
863
|
+
const color = colors[settings.colors.swap_used] || colors.purple;
|
|
864
|
+
const emoji = settings.display.show_emojis ? '🔄 ' : '';
|
|
865
|
+
return `${color}${emoji}${swapUsedPercent}% (${swapUsedMB}MB) swap`;
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
} catch {}
|
|
869
|
+
return '';
|
|
870
|
+
},
|
|
871
|
+
|
|
872
|
+
load_average(settings) {
|
|
873
|
+
if (!IS_LINUX) return '';
|
|
874
|
+
|
|
875
|
+
try {
|
|
876
|
+
const loadavg = fs.readFileSync('/proc/loadavg', 'utf8');
|
|
877
|
+
const loads = loadavg.split(' ').slice(0, 3);
|
|
878
|
+
const color = colors[settings.colors.load_average] || colors.red;
|
|
879
|
+
const emoji = settings.display.show_emojis ? '⚖️ ' : '';
|
|
880
|
+
return `${color}${emoji}${loads.join(' ')}`;
|
|
881
|
+
} catch {}
|
|
882
|
+
return '';
|
|
883
|
+
},
|
|
884
|
+
|
|
885
|
+
users_logged_in(settings) {
|
|
886
|
+
if (!IS_LINUX) return '';
|
|
887
|
+
|
|
888
|
+
try {
|
|
889
|
+
const who = execCommand('who');
|
|
890
|
+
const users = who.split('\n').filter(line => line.trim()).length;
|
|
891
|
+
if (users > 0) {
|
|
892
|
+
const color = colors[settings.colors.users_logged_in] || colors.cyan;
|
|
893
|
+
const emoji = settings.display.show_emojis ? '👥 ' : '';
|
|
894
|
+
return `${color}${emoji}${users} users`;
|
|
895
|
+
}
|
|
896
|
+
} catch {}
|
|
897
|
+
return '';
|
|
898
|
+
},
|
|
899
|
+
|
|
900
|
+
network_interfaces(settings) {
|
|
901
|
+
const cached = getCachedValue(this.cache, 'network_interfaces', settings);
|
|
902
|
+
if (cached) return cached;
|
|
903
|
+
|
|
904
|
+
if (!IS_LINUX) {
|
|
905
|
+
setCachedValue(this.cache, 'network_interfaces', '');
|
|
906
|
+
return '';
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
try {
|
|
910
|
+
const interfaces = os.networkInterfaces();
|
|
911
|
+
const activeInterfaces = [];
|
|
912
|
+
|
|
913
|
+
for (const [name, addrs] of Object.entries(interfaces)) {
|
|
914
|
+
if (name !== 'lo') { // Skip loopback
|
|
915
|
+
const ipv4Addr = addrs.find(addr => addr.family === 'IPv4' && !addr.internal);
|
|
916
|
+
if (ipv4Addr) {
|
|
917
|
+
activeInterfaces.push(name);
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
if (activeInterfaces.length > 0) {
|
|
923
|
+
const color = colors[settings.colors.network_interfaces] || colors.yellow;
|
|
924
|
+
const emoji = settings.display.show_emojis ? '🌐 ' : '';
|
|
925
|
+
const result = `${color}${emoji}${activeInterfaces.join(' ')}`;
|
|
926
|
+
setCachedValue(this.cache, 'network_interfaces', result);
|
|
927
|
+
return result;
|
|
928
|
+
}
|
|
929
|
+
} catch {}
|
|
930
|
+
|
|
931
|
+
setCachedValue(this.cache, 'network_interfaces', '');
|
|
932
|
+
return '';
|
|
933
|
+
},
|
|
934
|
+
|
|
935
|
+
mount_points(settings) {
|
|
936
|
+
const cached = getCachedValue(this.cache, 'mount_points', settings);
|
|
937
|
+
if (cached) return cached;
|
|
938
|
+
|
|
939
|
+
if (!IS_LINUX) {
|
|
940
|
+
setCachedValue(this.cache, 'mount_points', '');
|
|
941
|
+
return '';
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
try {
|
|
945
|
+
const df = execCommand('df -h');
|
|
946
|
+
const lines = df.split('\n').slice(1); // Skip header
|
|
947
|
+
const mountPoints = [];
|
|
948
|
+
|
|
949
|
+
lines.forEach(line => {
|
|
950
|
+
const parts = line.trim().split(/\s+/);
|
|
951
|
+
if (parts.length >= 6) {
|
|
952
|
+
const mountPoint = parts[5];
|
|
953
|
+
const usage = parts[4];
|
|
954
|
+
if (!mountPoint.startsWith('/dev') && !mountPoint.startsWith('/proc') &&
|
|
955
|
+
!mountPoint.startsWith('/sys') && mountPoint !== '/') {
|
|
956
|
+
mountPoints.push(`${mountPoint}(${usage})`);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
});
|
|
960
|
+
|
|
961
|
+
if (mountPoints.length > 0) {
|
|
962
|
+
const color = colors[settings.colors.mount_points] || colors.gray;
|
|
963
|
+
const emoji = settings.display.show_emojis ? '📂 ' : '';
|
|
964
|
+
const result = `${color}${emoji}${mountPoints.slice(0, 3).join(' ')}`;
|
|
965
|
+
setCachedValue(this.cache, 'mount_points', result);
|
|
966
|
+
return result;
|
|
967
|
+
}
|
|
968
|
+
} catch {}
|
|
969
|
+
|
|
970
|
+
setCachedValue(this.cache, 'mount_points', '');
|
|
971
|
+
return '';
|
|
972
|
+
},
|
|
973
|
+
|
|
974
|
+
services_running(settings) {
|
|
975
|
+
const cached = getCachedValue(this.cache, 'services_running', settings);
|
|
976
|
+
if (cached) return cached;
|
|
977
|
+
|
|
978
|
+
if (!IS_LINUX) {
|
|
979
|
+
setCachedValue(this.cache, 'services_running', '');
|
|
980
|
+
return '';
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
try {
|
|
984
|
+
let serviceCount = 0;
|
|
985
|
+
|
|
986
|
+
// Try systemctl first
|
|
987
|
+
if (commandExists('systemctl')) {
|
|
988
|
+
const services = execCommand('systemctl list-units --type=service --state=running --no-pager');
|
|
989
|
+
serviceCount = services.split('\n').filter(line => line.includes('.service')).length;
|
|
990
|
+
} else if (commandExists('service')) {
|
|
991
|
+
// Fallback for older systems
|
|
992
|
+
const services = execCommand('service --status-all');
|
|
993
|
+
serviceCount = services.split('\n').filter(line => line.includes('+')).length;
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
if (serviceCount > 0) {
|
|
997
|
+
const color = colors[settings.colors.services_running] || colors.green;
|
|
998
|
+
const emoji = settings.display.show_emojis ? '⚙️ ' : '';
|
|
999
|
+
const result = `${color}${emoji}${serviceCount} services`;
|
|
1000
|
+
setCachedValue(this.cache, 'services_running', result);
|
|
1001
|
+
return result;
|
|
1002
|
+
}
|
|
1003
|
+
} catch {}
|
|
1004
|
+
|
|
1005
|
+
setCachedValue(this.cache, 'services_running', '');
|
|
1006
|
+
return '';
|
|
1007
|
+
},
|
|
1008
|
+
|
|
1009
|
+
temperature(settings) {
|
|
1010
|
+
const cached = getCachedValue(this.cache, 'temperature', settings);
|
|
1011
|
+
if (cached) return cached;
|
|
1012
|
+
|
|
1013
|
+
if (!IS_LINUX) {
|
|
1014
|
+
setCachedValue(this.cache, 'temperature', '');
|
|
1015
|
+
return '';
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
try {
|
|
1019
|
+
// Try different temperature sources
|
|
1020
|
+
const tempSources = [
|
|
1021
|
+
'/sys/class/thermal/thermal_zone0/temp',
|
|
1022
|
+
'/sys/class/hwmon/hwmon0/temp1_input',
|
|
1023
|
+
'/sys/class/hwmon/hwmon1/temp1_input'
|
|
1024
|
+
];
|
|
1025
|
+
|
|
1026
|
+
for (const source of tempSources) {
|
|
1027
|
+
if (fs.existsSync(source)) {
|
|
1028
|
+
const temp = fs.readFileSync(source, 'utf8').trim();
|
|
1029
|
+
const tempC = Math.round(parseInt(temp) / 1000);
|
|
1030
|
+
|
|
1031
|
+
if (tempC > 0 && tempC < 150) { // Reasonable temperature range
|
|
1032
|
+
const color = tempC > 70 ? colors.red : tempC > 50 ? colors.yellow : colors.green;
|
|
1033
|
+
const emoji = settings.display.show_emojis ? '🌡️ ' : '';
|
|
1034
|
+
const result = `${color}${emoji}${tempC}°C`;
|
|
1035
|
+
setCachedValue(this.cache, 'temperature', result);
|
|
1036
|
+
return result;
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
} catch {}
|
|
1041
|
+
|
|
1042
|
+
setCachedValue(this.cache, 'temperature', '');
|
|
1043
|
+
return '';
|
|
1044
|
+
},
|
|
1045
|
+
|
|
1046
|
+
battery(settings) {
|
|
1047
|
+
const cached = getCachedValue(this.cache, 'battery', settings);
|
|
1048
|
+
if (cached) return cached;
|
|
1049
|
+
|
|
1050
|
+
if (!IS_LINUX) {
|
|
1051
|
+
setCachedValue(this.cache, 'battery', '');
|
|
1052
|
+
return '';
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
try {
|
|
1056
|
+
const batteryPath = '/sys/class/power_supply/BAT0';
|
|
1057
|
+
const capacityPath = `${batteryPath}/capacity`;
|
|
1058
|
+
const statusPath = `${batteryPath}/status`;
|
|
1059
|
+
|
|
1060
|
+
if (fs.existsSync(capacityPath)) {
|
|
1061
|
+
const capacity = fs.readFileSync(capacityPath, 'utf8').trim();
|
|
1062
|
+
const status = fs.existsSync(statusPath) ?
|
|
1063
|
+
fs.readFileSync(statusPath, 'utf8').trim() : 'Unknown';
|
|
1064
|
+
|
|
1065
|
+
const batteryPercent = parseInt(capacity);
|
|
1066
|
+
const isCharging = status === 'Charging';
|
|
1067
|
+
const color = batteryPercent < 20 ? colors.red :
|
|
1068
|
+
batteryPercent < 50 ? colors.yellow : colors.green;
|
|
1069
|
+
const emoji = settings.display.show_emojis ?
|
|
1070
|
+
(isCharging ? '🔌 ' : '🔋 ') : '';
|
|
1071
|
+
const result = `${color}${emoji}${batteryPercent}%${isCharging ? '+' : ''}`;
|
|
1072
|
+
setCachedValue(this.cache, 'battery', result);
|
|
1073
|
+
return result;
|
|
1074
|
+
}
|
|
1075
|
+
} catch {}
|
|
1076
|
+
|
|
1077
|
+
setCachedValue(this.cache, 'battery', '');
|
|
1078
|
+
return '';
|
|
1079
|
+
},
|
|
1080
|
+
|
|
1081
|
+
screen_resolution(settings) {
|
|
1082
|
+
if (!IS_LINUX) return '';
|
|
1083
|
+
|
|
1084
|
+
try {
|
|
1085
|
+
if (process.env.DISPLAY) {
|
|
1086
|
+
const xrandr = execCommand('xrandr');
|
|
1087
|
+
const resolutionMatch = xrandr.match(/(\d+x\d+)\+\d+\+\d+/);
|
|
1088
|
+
if (resolutionMatch) {
|
|
1089
|
+
const color = colors[settings.colors.screen_resolution] || colors.blue;
|
|
1090
|
+
const emoji = settings.display.show_emojis ? '🖥️ ' : '';
|
|
1091
|
+
return `${color}${emoji}${resolutionMatch[1]}`;
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
} catch {}
|
|
1095
|
+
return '';
|
|
1096
|
+
}
|
|
1097
|
+
};
|
|
1098
|
+
|
|
1099
|
+
// Main function to display system info
|
|
1100
|
+
async function displaySystemInfo() {
|
|
1101
|
+
const settings = loadSettings();
|
|
1102
|
+
const cache = loadCache();
|
|
1103
|
+
const context = { cache, settings };
|
|
1104
|
+
|
|
1105
|
+
// Check if we need IP info and it's not cached
|
|
1106
|
+
const allKeys = settings.display_order.flat();
|
|
1107
|
+
const needIPInfo = allKeys.some(key => ['ip', 'isp', 'domain', 'city'].includes(key));
|
|
1108
|
+
const cachedIPInfo = getCachedValue(cache, 'ipInfo', settings);
|
|
1109
|
+
|
|
1110
|
+
if (needIPInfo && !cachedIPInfo) {
|
|
1111
|
+
context.ipInfo = await fetchIPInfo(settings);
|
|
1112
|
+
setCachedValue(cache, 'ipInfo', context.ipInfo);
|
|
1113
|
+
} else if (needIPInfo && cachedIPInfo) {
|
|
1114
|
+
context.ipInfo = cachedIPInfo;
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
const lines = [];
|
|
1118
|
+
|
|
1119
|
+
for (const group of settings.display_order) {
|
|
1120
|
+
const lineItems = [];
|
|
1121
|
+
|
|
1122
|
+
for (const key of group) {
|
|
1123
|
+
if (infoFunctions[key]) {
|
|
1124
|
+
try {
|
|
1125
|
+
const info = await infoFunctions[key].call(context, settings);
|
|
1126
|
+
if (info && info.trim()) {
|
|
1127
|
+
lineItems.push(info);
|
|
1128
|
+
}
|
|
1129
|
+
} catch (error) {
|
|
1130
|
+
if (settings.advanced.debug) {
|
|
1131
|
+
console.error(`Error getting ${key}:`, error.message);
|
|
1132
|
+
}
|
|
1133
|
+
// Continue on errors unless debug mode
|
|
1134
|
+
}
|
|
1135
|
+
} else if (settings.advanced.debug) {
|
|
1136
|
+
console.error(`Unknown info function: ${key}`);
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
// Only add the line if it has content
|
|
1141
|
+
if (lineItems.length > 0) {
|
|
1142
|
+
lines.push(lineItems.join(' '));
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
// Show offline message if no network and IP info was requested but failed
|
|
1147
|
+
if (needIPInfo && (!context.ipInfo || Object.keys(context.ipInfo).length === 0) &&
|
|
1148
|
+
settings.network.show_offline_message && lines.length === 0) {
|
|
1149
|
+
const emoji = settings.display.show_emojis ? '❌ ' : '';
|
|
1150
|
+
lines.push(`${colors.red}${emoji}No internet connection`);
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
// Save cache after all operations
|
|
1154
|
+
saveCache(cache);
|
|
1155
|
+
|
|
1156
|
+
// Output each line
|
|
1157
|
+
if (lines.length > 0) {
|
|
1158
|
+
lines.forEach(line => {
|
|
1159
|
+
console.log(line + colors.reset);
|
|
1160
|
+
});
|
|
1161
|
+
} else if (settings.advanced.debug) {
|
|
1162
|
+
console.log('No system information could be displayed');
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
// Settings management commands
|
|
1167
|
+
function handleSettingsCommand(args) {
|
|
1168
|
+
const settings = loadSettings();
|
|
1169
|
+
|
|
1170
|
+
if (args.includes('--settings-init')) {
|
|
1171
|
+
if (saveSettings(DEFAULT_SETTINGS)) {
|
|
1172
|
+
console.log('Settings initialized with defaults');
|
|
1173
|
+
} else {
|
|
1174
|
+
console.log('Failed to initialize settings');
|
|
1175
|
+
}
|
|
1176
|
+
return true;
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
if (args.includes('--settings-show')) {
|
|
1180
|
+
console.log('Current settings:');
|
|
1181
|
+
console.log(JSON.stringify(settings, null, 2));
|
|
1182
|
+
return true;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
if (args.includes('--settings-reset')) {
|
|
1186
|
+
if (saveSettings(DEFAULT_SETTINGS)) {
|
|
1187
|
+
console.log('Settings reset to defaults');
|
|
1188
|
+
} else {
|
|
1189
|
+
console.log('Failed to reset settings');
|
|
1190
|
+
}
|
|
1191
|
+
return true;
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
const cacheResetIndex = args.indexOf('--cache-clear');
|
|
1195
|
+
if (cacheResetIndex !== -1) {
|
|
1196
|
+
try {
|
|
1197
|
+
if (fs.existsSync(CACHE_FILE)) {
|
|
1198
|
+
fs.unlinkSync(CACHE_FILE);
|
|
1199
|
+
}
|
|
1200
|
+
console.log('Cache cleared');
|
|
1201
|
+
} catch (error) {
|
|
1202
|
+
console.error('Error clearing cache:', error.message);
|
|
1203
|
+
}
|
|
1204
|
+
return true;
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
const setIndex = args.indexOf('--set');
|
|
1208
|
+
if (setIndex !== -1 && args[setIndex + 1] && args[setIndex + 2]) {
|
|
1209
|
+
const key = args[setIndex + 1];
|
|
1210
|
+
const value = args[setIndex + 2];
|
|
1211
|
+
|
|
1212
|
+
try {
|
|
1213
|
+
// Parse JSON value if it looks like JSON
|
|
1214
|
+
const parsedValue = value.startsWith('{') || value.startsWith('[') ?
|
|
1215
|
+
JSON.parse(value) : value;
|
|
1216
|
+
|
|
1217
|
+
// Set nested property using dot notation
|
|
1218
|
+
const keys = key.split('.');
|
|
1219
|
+
let current = settings;
|
|
1220
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
1221
|
+
if (!current[keys[i]]) current[keys[i]] = {};
|
|
1222
|
+
current = current[keys[i]];
|
|
1223
|
+
}
|
|
1224
|
+
current[keys[keys.length - 1]] = parsedValue;
|
|
1225
|
+
|
|
1226
|
+
if (saveSettings(settings)) {
|
|
1227
|
+
console.log(`Setting ${key} = ${value}`);
|
|
1228
|
+
} else {
|
|
1229
|
+
console.log('Failed to save settings');
|
|
1230
|
+
}
|
|
1231
|
+
} catch (error) {
|
|
1232
|
+
console.error('Error setting value:', error.message);
|
|
1233
|
+
}
|
|
1234
|
+
return true;
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
return false;
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
// Installation function - Cross-platform compatible
|
|
1241
|
+
function installShellGreeting() {
|
|
1242
|
+
const homeDir = os.homedir();
|
|
1243
|
+
|
|
1244
|
+
let configDir, scriptPath;
|
|
1245
|
+
if (IS_WINDOWS) {
|
|
1246
|
+
configDir = path.join(homeDir, 'AppData', 'Local');
|
|
1247
|
+
scriptPath = path.join(configDir, 'systeminfo.js');
|
|
1248
|
+
} else {
|
|
1249
|
+
configDir = path.join(homeDir, '.config');
|
|
1250
|
+
scriptPath = path.join(configDir, 'systeminfo.js');
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
const currentScript = path.resolve(__filename);
|
|
1254
|
+
|
|
1255
|
+
try {
|
|
1256
|
+
// Ensure config directory exists
|
|
1257
|
+
if (!fs.existsSync(configDir)) {
|
|
1258
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
// Copy this script
|
|
1262
|
+
fs.copyFileSync(currentScript, scriptPath);
|
|
1263
|
+
if (!IS_WINDOWS) {
|
|
1264
|
+
fs.chmodSync(scriptPath, '755');
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
if (IS_WINDOWS) {
|
|
1268
|
+
// Windows-specific installation
|
|
1269
|
+
console.log('Windows installation:');
|
|
1270
|
+
console.log('1. Script copied to:', scriptPath);
|
|
1271
|
+
console.log('2. To add to PowerShell profile, run:');
|
|
1272
|
+
console.log(` Add-Content $PROFILE "node '${scriptPath}'"`);
|
|
1273
|
+
console.log('3. To add to Command Prompt, create a batch file in your startup folder');
|
|
1274
|
+
|
|
1275
|
+
const startupBat = path.join(configDir, 'systeminfo-startup.bat');
|
|
1276
|
+
fs.writeFileSync(startupBat, `@echo off\nnode "${scriptPath}"\n`);
|
|
1277
|
+
console.log('4. Batch file created:', startupBat);
|
|
1278
|
+
|
|
1279
|
+
} else {
|
|
1280
|
+
// Unix-like installation
|
|
1281
|
+
|
|
1282
|
+
// Silence default login messages
|
|
1283
|
+
try {
|
|
1284
|
+
const hushLoginPath = path.join(homeDir, '.hushlogin');
|
|
1285
|
+
fs.writeFileSync(hushLoginPath, '');
|
|
1286
|
+
} catch {
|
|
1287
|
+
// Ignore permission errors
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
// Add to bash
|
|
1291
|
+
const bashrcPath = path.join(homeDir, '.bashrc');
|
|
1292
|
+
const bashLine = `node ${scriptPath}`;
|
|
1293
|
+
|
|
1294
|
+
if (fs.existsSync(bashrcPath)) {
|
|
1295
|
+
const bashrc = fs.readFileSync(bashrcPath, 'utf8');
|
|
1296
|
+
if (!bashrc.includes('systeminfo.js')) {
|
|
1297
|
+
fs.appendFileSync(bashrcPath, `\n${bashLine}\n`);
|
|
1298
|
+
}
|
|
1299
|
+
} else {
|
|
1300
|
+
fs.writeFileSync(bashrcPath, `${bashLine}\n`);
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
// Add to zsh (common on Mac)
|
|
1304
|
+
const zshrcPath = path.join(homeDir, '.zshrc');
|
|
1305
|
+
if (fs.existsSync(zshrcPath)) {
|
|
1306
|
+
const zshrc = fs.readFileSync(zshrcPath, 'utf8');
|
|
1307
|
+
if (!zshrc.includes('systeminfo.js')) {
|
|
1308
|
+
fs.appendFileSync(zshrcPath, `\n${bashLine}\n`);
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
// Add to fish if config exists
|
|
1313
|
+
const fishConfigPath = path.join(homeDir, '.config', 'fish', 'config.fish');
|
|
1314
|
+
if (fs.existsSync(fishConfigPath)) {
|
|
1315
|
+
const fishConfig = fs.readFileSync(fishConfigPath, 'utf8');
|
|
1316
|
+
if (!fishConfig.includes('systeminfo.js')) {
|
|
1317
|
+
fs.appendFileSync(fishConfigPath, `\nset -U fish_greeting ""\n${bashLine}\n`);
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
// Add to nushell if config exists
|
|
1322
|
+
const nushellConfigPath = path.join(homeDir, '.config', 'nushell', 'config.nu');
|
|
1323
|
+
if (fs.existsSync(nushellConfigPath)) {
|
|
1324
|
+
const nushellConfig = fs.readFileSync(nushellConfigPath, 'utf8');
|
|
1325
|
+
if (!nushellConfig.includes('systeminfo.js')) {
|
|
1326
|
+
fs.appendFileSync(nushellConfigPath, `\n$env.config.show_banner = false\n${bashLine}\n`);
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
console.log('Shell greeting installation completed!');
|
|
1332
|
+
|
|
1333
|
+
} catch (error) {
|
|
1334
|
+
console.error('Error installing shell greeting:', error.message);
|
|
1335
|
+
process.exit(1);
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
// Main execution
|
|
1340
|
+
async function main() {
|
|
1341
|
+
const args = process.argv.slice(2);
|
|
1342
|
+
|
|
1343
|
+
// Handle settings commands
|
|
1344
|
+
if (handleSettingsCommand(args)) {
|
|
1345
|
+
return;
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
// Check for --install argument
|
|
1349
|
+
if (args.includes('--install')) {
|
|
1350
|
+
installShellGreeting();
|
|
1351
|
+
return;
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
// Check for --help argument
|
|
1355
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
1356
|
+
console.log(`
|
|
1357
|
+
System Info Script - Node.js Version
|
|
1358
|
+
|
|
1359
|
+
Usage:
|
|
1360
|
+
node systeminfo.js [options]
|
|
1361
|
+
|
|
1362
|
+
Options:
|
|
1363
|
+
--help, -h Show this help message
|
|
1364
|
+
--install Install as shell greeting
|
|
1365
|
+
--settings-init Initialize settings file with defaults
|
|
1366
|
+
--settings-show Display current settings
|
|
1367
|
+
--settings-reset Reset settings to defaults
|
|
1368
|
+
--cache-clear Clear the cache file
|
|
1369
|
+
--set <key> <value> Set a configuration value (use dot notation)
|
|
1370
|
+
|
|
1371
|
+
Examples:
|
|
1372
|
+
node systeminfo.js
|
|
1373
|
+
node systeminfo.js --install
|
|
1374
|
+
node systeminfo.js --set display.show_emojis false
|
|
1375
|
+
node systeminfo.js --set colors.user blue
|
|
1376
|
+
node systeminfo.js --set display_order '[["user","hostname"],["disk_used","ram_used"]]'
|
|
1377
|
+
|
|
1378
|
+
Settings file: ${SETTINGS_FILE}
|
|
1379
|
+
Cache file: ${CACHE_FILE}
|
|
1380
|
+
|
|
1381
|
+
Platform: ${IS_WINDOWS ? 'Windows' : IS_MAC ? 'macOS' : IS_LINUX ? 'Linux' : 'Unknown'}
|
|
1382
|
+
|
|
1383
|
+
Available display blocks:
|
|
1384
|
+
Basic: user, hostname, uptime, shell, os, kernel, device
|
|
1385
|
+
Resources: disk_used, ram_used, memory_available, swap_used, top_process
|
|
1386
|
+
Network: ip, iplocal, city, domain, isp, network_interfaces
|
|
1387
|
+
Hardware: cpu, gpu, temperature, battery, screen_resolution
|
|
1388
|
+
System: load_average, users_logged_in, mount_points, services_running
|
|
1389
|
+
Tools: pacman, ports, containers
|
|
1390
|
+
|
|
1391
|
+
Available colors:
|
|
1392
|
+
red, orange, yellow, green, blue, cyan, purple, magenta, gray, lightblue
|
|
1393
|
+
(use "multicolor" for ports to get rainbow effect)
|
|
1394
|
+
|
|
1395
|
+
Display Format:
|
|
1396
|
+
The script organizes information into 4 logical categories:
|
|
1397
|
+
- Line 1: Device Info - user, hostname, OS, CPU, GPU, device model, kernel
|
|
1398
|
+
- Line 2: Dynamic Stats - disk usage, RAM, top process, uptime, temperature, battery, load average
|
|
1399
|
+
- Line 3: Network - IP addresses, location, domain, ISP
|
|
1400
|
+
- Line 4: System Apps - shell, package managers, open ports, containers, running services
|
|
1401
|
+
|
|
1402
|
+
To customize grouping, modify display_order as an array of arrays:
|
|
1403
|
+
--set display_order '[["user","hostname","os"],["disk_used","ram_used"],["ip","city"],["shell","pacman"]]'
|
|
1404
|
+
|
|
1405
|
+
Linux-specific features:
|
|
1406
|
+
- Temperature monitoring from thermal zones
|
|
1407
|
+
- Battery status and charging indication
|
|
1408
|
+
- System load averages
|
|
1409
|
+
- Swap usage monitoring
|
|
1410
|
+
- Active service count
|
|
1411
|
+
- Mount point information
|
|
1412
|
+
- Memory availability from /proc/meminfo
|
|
1413
|
+
- Screen resolution via xrandr
|
|
1414
|
+
`);
|
|
1415
|
+
return;
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
// Display system info
|
|
1419
|
+
await displaySystemInfo();
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
// Run the script
|
|
1423
|
+
if (require.main === module) {
|
|
1424
|
+
main().catch(error => {
|
|
1425
|
+
console.error('Error:', error.message);
|
|
1426
|
+
process.exit(1);
|
|
1427
|
+
});
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
module.exports = { displaySystemInfo, installShellGreeting };
|