about-system 0.0.16 → 0.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,516 @@
1
+ #!/usr/bin/env node
2
+ import os from 'os';
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ import { fileURLToPath } from 'url';
6
+ import { getSystemInfo } from './system-info-api.js';
7
+ import type { SystemInfo, SystemInfoOptions } from './systeminfo-types.js';
8
+
9
+ const __filename = fileURLToPath(import.meta.url);
10
+
11
+ // Settings configuration
12
+ const SETTINGS_FILE = path.join(os.homedir(), '.config', 'systeminfo-settings.json');
13
+ const CACHE_FILE = path.join(os.tmpdir(), 'systeminfo-cache.json');
14
+
15
+ // Default settings
16
+ const DEFAULT_SETTINGS = {
17
+ display_order: [
18
+ ['user', 'hostname', 'os', 'device', 'kernel', 'cpu', 'gpu'],
19
+ ['disk_used', 'ram_used', 'top_process', 'uptime', 'temperature', 'battery', 'load_average'],
20
+ ['ip', 'iplocal', 'city', 'domain', 'isp'],
21
+ ['shell', 'pacman', 'services_running', 'containers'],
22
+ ],
23
+ colors: {
24
+ user: 'red',
25
+ hostname: 'orange',
26
+ disk_used: 'purple',
27
+ ram_used: 'yellow',
28
+ top_process: 'magenta',
29
+ uptime: 'cyan',
30
+ ip: 'green',
31
+ iplocal: 'yellow',
32
+ city: 'green',
33
+ domain: 'gray',
34
+ isp: 'lightblue',
35
+ os: 'gray',
36
+ cpu: 'orange',
37
+ gpu: 'yellow',
38
+ device: 'yellow',
39
+ kernel: 'green',
40
+ shell: 'orange',
41
+ pacman: 'multicolor',
42
+ ports: 'multicolor',
43
+ containers: 'green',
44
+ memory_available: 'blue',
45
+ swap_used: 'purple',
46
+ load_average: 'red',
47
+ users_logged_in: 'cyan',
48
+ network_interfaces: 'yellow',
49
+ mount_points: 'gray',
50
+ services_running: 'green',
51
+ temperature: 'red',
52
+ battery: 'green',
53
+ screen_resolution: 'blue',
54
+ },
55
+ display: {
56
+ show_emojis: true,
57
+ single_line: false,
58
+ line_wrap_length: process?.stdout?.columns || 100,
59
+ },
60
+ network: {
61
+ show_offline_message: true,
62
+ },
63
+ advanced: {
64
+ debug: false,
65
+ },
66
+ };
67
+
68
+ // Color codes
69
+ const colors = {
70
+ reset: '\x1b[0m',
71
+ red: '\x1b[38;5;196m',
72
+ orange: '\x1b[38;5;208m',
73
+ yellow: '\x1b[38;5;226m',
74
+ green: '\x1b[38;5;46m',
75
+ blue: '\x1b[38;5;39m',
76
+ cyan: '\x1b[38;5;51m',
77
+ purple: '\x1b[38;5;171m',
78
+ magenta: '\x1b[38;5;213m',
79
+ gray: '\x1b[38;5;250m',
80
+ lightblue: '\x1b[38;5;220m',
81
+ };
82
+
83
+ // Emoji mappings
84
+ const emojis: Record<string, string> = {
85
+ user: '👤 ',
86
+ hostname: '🏠 ',
87
+ ip: '🌎 ',
88
+ iplocal: '🌐 ',
89
+ city: '📍 ',
90
+ domain: '🔗 ',
91
+ isp: '👮 ',
92
+ os: '⚡ ',
93
+ cpu: '📈 ',
94
+ gpu: '🎮 ',
95
+ device: '💻 ',
96
+ kernel: '🔧 ',
97
+ shell: '🐚 ',
98
+ pacman: '🚀 ',
99
+ disk_used: '📁 ',
100
+ ram_used: '💾 ',
101
+ top_process: '🔝 ',
102
+ uptime: '⏱️ ',
103
+ ports: '🔌 ',
104
+ containers: '📦',
105
+ memory_available: '🧠 ',
106
+ swap_used: '🔄 ',
107
+ load_average: '⚖️ ',
108
+ users_logged_in: '👥 ',
109
+ network_interfaces: '🌐 ',
110
+ mount_points: '📂 ',
111
+ services_running: '⚙️ ',
112
+ temperature: '🌡️ ',
113
+ battery_charging: '🔌 ',
114
+ battery: '🔋 ',
115
+ screen_resolution: '🖥️ ',
116
+ };
117
+
118
+ // Platform detection
119
+ const IS_WINDOWS = os.platform() === 'win32';
120
+
121
+ interface Settings {
122
+ display_order: string[][];
123
+ colors: Record<string, string>;
124
+ display: {
125
+ show_emojis: boolean;
126
+ single_line: boolean;
127
+ line_wrap_length: number;
128
+ };
129
+ network: {
130
+ show_offline_message: boolean;
131
+ };
132
+ advanced: {
133
+ debug: boolean;
134
+ };
135
+ }
136
+
137
+ function loadSettings(): Settings {
138
+ try {
139
+ if (fs.existsSync(SETTINGS_FILE)) {
140
+ const settings = JSON.parse(fs.readFileSync(SETTINGS_FILE, 'utf8'));
141
+ return { ...DEFAULT_SETTINGS, ...settings };
142
+ }
143
+ } catch {}
144
+ return DEFAULT_SETTINGS;
145
+ }
146
+
147
+ function saveSettings(settings: Settings): boolean {
148
+ try {
149
+ const configDir = path.dirname(SETTINGS_FILE);
150
+ if (!fs.existsSync(configDir)) {
151
+ fs.mkdirSync(configDir, { recursive: true });
152
+ }
153
+ fs.writeFileSync(SETTINGS_FILE, JSON.stringify(settings, null, 2));
154
+ return true;
155
+ } catch {
156
+ return false;
157
+ }
158
+ }
159
+
160
+ function formatValue(key: string, value: string, settings: Settings): string {
161
+ if (!value || value.trim() === '') return '';
162
+
163
+ const color = colors[settings.colors[key] as keyof typeof colors] || colors.reset;
164
+ const emoji = settings.display.show_emojis ? emojis[key] || '' : '';
165
+
166
+ // Special handling for battery emoji
167
+ if (key === 'battery' && settings.display.show_emojis) {
168
+ const batteryEmoji = value.includes('+') ? emojis.battery_charging : emojis.battery;
169
+ return `${color}${batteryEmoji}${value}`;
170
+ }
171
+
172
+ // Multicolor handling for ports
173
+ if (key === 'ports' && settings.colors[key] === 'multicolor' && value) {
174
+ const emoji = settings.display.show_emojis ? emojis.ports : '';
175
+ let output = ` ${emoji}`;
176
+ const ports = value.split(' ');
177
+ const colorCodes = [31, 32, 33, 34, 35, 36];
178
+ ports.forEach((port, index) => {
179
+ const colorCode = colorCodes[index % colorCodes.length];
180
+ output += `\x1b[${colorCode}m${port}\x1b[0m `;
181
+ });
182
+ return output.trim();
183
+ }
184
+
185
+ // Multicolor handling for pacman
186
+ if (key === 'pacman' && settings.colors[key] === 'multicolor' && value) {
187
+ const emoji = settings.display.show_emojis ? emojis.pacman : '';
188
+ return `${color}${emoji}${value}`;
189
+ }
190
+
191
+ return `${color}${emoji}${value}`;
192
+ }
193
+
194
+ function removeAnsiCodes(str: string): string {
195
+ return str.replace(/\x1b\[[0-9;]*m/g, '');
196
+ }
197
+
198
+ async function displaySystemInfo(customDisplayOrder: string[][] | null = null): Promise<void> {
199
+ const settings = loadSettings();
200
+ const displayOrder = customDisplayOrder || settings.display_order;
201
+
202
+ // Get system info
203
+ const info = await getSystemInfo();
204
+
205
+ // Single line mode
206
+ if (settings.display.single_line) {
207
+ const allItems: string[] = [];
208
+
209
+ for (const group of displayOrder) {
210
+ for (const key of group) {
211
+ const value = info[key as keyof SystemInfo] as string;
212
+ const formatted = formatValue(key, value, settings);
213
+ if (formatted && formatted.trim()) {
214
+ allItems.push(formatted);
215
+ }
216
+ }
217
+ }
218
+
219
+ if (allItems.length > 0) {
220
+ console.log(allItems.join(' ') + colors.reset);
221
+ }
222
+ return;
223
+ }
224
+
225
+ // Multi-line mode with intelligent wrapping
226
+ const lines: string[] = [];
227
+ let currentLine = '';
228
+ const maxLineLength = settings.display.line_wrap_length;
229
+
230
+ for (const group of displayOrder) {
231
+ for (const key of group) {
232
+ const value = info[key as keyof SystemInfo] as string;
233
+ const formatted = formatValue(key, value, settings);
234
+
235
+ if (formatted && formatted.trim()) {
236
+ const formattedLength = removeAnsiCodes(formatted).length;
237
+ const currentLineLength = removeAnsiCodes(currentLine).length;
238
+
239
+ if (currentLine && currentLineLength + formattedLength + 1 > maxLineLength) {
240
+ lines.push(currentLine);
241
+ currentLine = formatted;
242
+ } else {
243
+ currentLine = currentLine ? `${currentLine} ${formatted}` : formatted;
244
+ }
245
+ }
246
+ }
247
+ }
248
+
249
+ if (currentLine) {
250
+ lines.push(currentLine);
251
+ }
252
+
253
+ // Output
254
+ if (lines.length > 0) {
255
+ lines.forEach((line) => {
256
+ console.log(line + colors.reset);
257
+ });
258
+ } else if (settings.advanced.debug) {
259
+ console.log('No system information could be displayed');
260
+ }
261
+ }
262
+
263
+ function handleSettingsCommand(args: string[]): boolean {
264
+ const settings = loadSettings();
265
+
266
+ if (args.includes('--settings-init')) {
267
+ if (saveSettings(DEFAULT_SETTINGS)) {
268
+ console.log('Settings initialized with defaults');
269
+ } else {
270
+ console.log('Failed to initialize settings');
271
+ }
272
+ return true;
273
+ }
274
+
275
+ if (args.includes('--settings-show')) {
276
+ console.log('Current settings:');
277
+ console.log(JSON.stringify(settings, null, 2));
278
+ return true;
279
+ }
280
+
281
+ if (args.includes('--settings-reset')) {
282
+ if (saveSettings(DEFAULT_SETTINGS)) {
283
+ console.log('Settings reset to defaults');
284
+ } else {
285
+ console.log('Failed to reset settings');
286
+ }
287
+ return true;
288
+ }
289
+
290
+ if (args.includes('--refresh')) {
291
+ try {
292
+ if (fs.existsSync(CACHE_FILE)) {
293
+ fs.unlinkSync(CACHE_FILE);
294
+ console.log('Cache cleared');
295
+ }
296
+ } catch (error) {
297
+ console.error('Error clearing cache:', (error as Error).message);
298
+ }
299
+ return true;
300
+ }
301
+
302
+ const setIndex = args.indexOf('--set');
303
+ if (setIndex !== -1 && args[setIndex + 1] && args[setIndex + 2]) {
304
+ const key = args[setIndex + 1];
305
+ const value = args[setIndex + 2];
306
+
307
+ try {
308
+ const parsedValue = value.startsWith('{') || value.startsWith('[') ? JSON.parse(value) : value;
309
+
310
+ const keys = key.split('.');
311
+ let current: any = settings;
312
+ for (let i = 0; i < keys.length - 1; i++) {
313
+ if (!current[keys[i]]) current[keys[i]] = {};
314
+ current = current[keys[i]];
315
+ }
316
+ current[keys[keys.length - 1]] = parsedValue;
317
+
318
+ if (saveSettings(settings)) {
319
+ console.log(`Setting ${key} = ${value}`);
320
+ } else {
321
+ console.log('Failed to save settings');
322
+ }
323
+ } catch (error) {
324
+ console.error('Error setting value:', (error as Error).message);
325
+ }
326
+ return true;
327
+ }
328
+
329
+ return false;
330
+ }
331
+
332
+ function installShellGreeting(): void {
333
+ const homeDir = os.homedir();
334
+
335
+ let configDir: string, scriptPath: string;
336
+ if (IS_WINDOWS) {
337
+ configDir = path.join(homeDir, 'AppData', 'Local');
338
+ scriptPath = path.join(configDir, 'systeminfo.js');
339
+ } else {
340
+ configDir = path.join(homeDir, '.config');
341
+ scriptPath = path.join(configDir, 'systeminfo.js');
342
+ }
343
+
344
+ const currentScript = path.resolve(__filename);
345
+
346
+ try {
347
+ if (!fs.existsSync(configDir)) {
348
+ fs.mkdirSync(configDir, { recursive: true });
349
+ }
350
+
351
+ fs.copyFileSync(currentScript, scriptPath);
352
+ if (!IS_WINDOWS) {
353
+ fs.chmodSync(scriptPath, '755');
354
+ }
355
+
356
+ if (IS_WINDOWS) {
357
+ console.log('Windows installation:');
358
+ console.log('1. Script copied to:', scriptPath);
359
+ console.log('2. To add to PowerShell profile, run:');
360
+ console.log(` Add-Content $PROFILE "node '${scriptPath}'"`);
361
+ console.log('3. To add to Command Prompt, create a batch file in your startup folder');
362
+
363
+ const startupBat = path.join(configDir, 'systeminfo-startup.bat');
364
+ fs.writeFileSync(startupBat, `@echo off\nnode "${scriptPath}"\n`);
365
+ console.log('4. Batch file created:', startupBat);
366
+ } else {
367
+ try {
368
+ const hushLoginPath = path.join(homeDir, '.hushlogin');
369
+ fs.writeFileSync(hushLoginPath, '');
370
+ } catch {}
371
+
372
+ const bashrcPath = path.join(homeDir, '.bashrc');
373
+ const bashLine = `node ${scriptPath}`;
374
+
375
+ if (fs.existsSync(bashrcPath)) {
376
+ const bashrc = fs.readFileSync(bashrcPath, 'utf8');
377
+ if (!bashrc.includes('systeminfo.js')) {
378
+ fs.appendFileSync(bashrcPath, `\n${bashLine}\n`);
379
+ }
380
+ } else {
381
+ fs.writeFileSync(bashrcPath, `${bashLine}\n`);
382
+ }
383
+
384
+ const zshrcPath = path.join(homeDir, '.zshrc');
385
+ if (fs.existsSync(zshrcPath)) {
386
+ const zshrc = fs.readFileSync(zshrcPath, 'utf8');
387
+ if (!zshrc.includes('systeminfo.js')) {
388
+ fs.appendFileSync(zshrcPath, `\n${bashLine}\n`);
389
+ }
390
+ }
391
+
392
+ const fishConfigPath = path.join(homeDir, '.config', 'fish', 'config.fish');
393
+ if (fs.existsSync(fishConfigPath)) {
394
+ const fishConfig = fs.readFileSync(fishConfigPath, 'utf8');
395
+ if (!fishConfig.includes('systeminfo.js')) {
396
+ fs.appendFileSync(fishConfigPath, `\nset -U fish_greeting ""\n${bashLine}\n`);
397
+ }
398
+ }
399
+
400
+ const nushellConfigPath = path.join(homeDir, '.config', 'nushell', 'config.nu');
401
+ if (fs.existsSync(nushellConfigPath)) {
402
+ const nushellConfig = fs.readFileSync(nushellConfigPath, 'utf8');
403
+ if (!nushellConfig.includes('systeminfo.js')) {
404
+ fs.appendFileSync(nushellConfigPath, `\n$env.config.show_banner = false\n${bashLine}\n`);
405
+ }
406
+ }
407
+ }
408
+
409
+ console.log('Shell greeting installation completed!');
410
+ } catch (error) {
411
+ console.error('Error installing shell greeting:', (error as Error).message);
412
+ process.exit(1);
413
+ }
414
+ }
415
+
416
+ function parseCLIMode(args: string[]): string[] | null {
417
+ for (const arg of args) {
418
+ if (!arg.startsWith('--') && arg.includes(',')) {
419
+ return arg
420
+ .split(',')
421
+ .map((part) => part.trim())
422
+ .filter((part) => part.length > 0);
423
+ }
424
+ }
425
+
426
+ for (const arg of args) {
427
+ if (!arg.startsWith('--') && !arg.includes('=') && arg.length > 0) {
428
+ return [arg.trim()];
429
+ }
430
+ }
431
+
432
+ return null;
433
+ }
434
+
435
+ async function showHelp(): Promise<void> {
436
+ console.log(`
437
+ System Info Script - TypeScript Version
438
+
439
+ Usage:
440
+ about-system [options]
441
+ about-system <part1,part2,...> # CLI mode: show specific parts only
442
+
443
+ Options:
444
+ --help, -h Show this help message
445
+ --install Install as shell greeting
446
+ --settings-init Initialize settings file with defaults
447
+ --settings-show Display current settings
448
+ --settings-reset Reset settings to defaults
449
+ --refresh Clear the cache file
450
+ --set <key> <value> Set a configuration value (use dot notation)
451
+ --json Output as JSON
452
+
453
+ Examples:
454
+ about-system # Show all info (default)
455
+ about-system cpu,os # Show only CPU and OS info
456
+ about-system user,hostname,ip # Show user, hostname, and IP
457
+ about-system disk_used # Show only disk usage
458
+ about-system --install
459
+ about-system --set display.show_emojis false
460
+ about-system --set colors.user blue
461
+ about-system --json
462
+
463
+ Settings file: ${SETTINGS_FILE}
464
+ Cache file: ${CACHE_FILE}
465
+
466
+ Platform: ${IS_WINDOWS ? 'Windows' : os.platform() === 'darwin' ? 'macOS' : os.platform() === 'linux' ? 'Linux' : 'Unknown'}
467
+
468
+ Available display blocks:
469
+ Basic: user, hostname, uptime, shell, os, kernel, device
470
+ Resources: disk_used, ram_used, memory_available, swap_used, top_process
471
+ Network: ip, iplocal, city, domain, isp, network_interfaces
472
+ Hardware: cpu, gpu, temperature, battery, screen_resolution
473
+ System: load_average, users_logged_in, mount_points, services_running
474
+ Tools: pacman, ports, containers
475
+ `);
476
+ }
477
+
478
+ async function main(): Promise<void> {
479
+ const args = process.argv.slice(2);
480
+
481
+ if (handleSettingsCommand(args)) {
482
+ return;
483
+ }
484
+
485
+ if (args.includes('--help') || args.includes('-h')) {
486
+ await showHelp();
487
+ return;
488
+ }
489
+
490
+ if (args.includes('--install')) {
491
+ installShellGreeting();
492
+ return;
493
+ }
494
+
495
+ if (args.includes('--json')) {
496
+ const info = await getSystemInfo();
497
+ console.log(JSON.stringify(info, null, 2));
498
+ return;
499
+ }
500
+
501
+ const cliParts = parseCLIMode(args);
502
+ if (cliParts) {
503
+ const customDisplayOrder = [cliParts];
504
+ await displaySystemInfo(customDisplayOrder);
505
+ return;
506
+ }
507
+
508
+ await displaySystemInfo();
509
+ }
510
+
511
+ main().catch((error) => {
512
+ console.error('Error:', error.message);
513
+ process.exit(1);
514
+ });
515
+
516
+ export { displaySystemInfo, installShellGreeting };
package/src/index.ts ADDED
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @fileoverview Main entry point for about-system package
3
+ *
4
+ * Exports the system information API for programmatic use.
5
+ * For CLI usage, use the 'about-system' command directly.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { getSystemInfo } from 'about-system';
10
+ *
11
+ * const info = await getSystemInfo();
12
+ * console.log(info.cpu);
13
+ * console.log(info.ram_used);
14
+ * ```
15
+ */
16
+
17
+ export { getSystemInfo, loadCache, saveCache } from './system-info-api.js';
18
+ export type { SystemInfo, SystemInfoOptions, Platform, GetSystemInfoFunction, DisplaySystemInfoFunction, PlatformAvailability } from './systeminfo-types.js';