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