about-system 0.0.5 → 0.0.9

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 CHANGED
@@ -101,7 +101,7 @@ npx about-system
101
101
 
102
102
  The script uses a JSON settings file located at:
103
103
  - **Linux/macOS**: `~/.config/systeminfo-settings.json`
104
- - **Windows**: `%APPDATA%\Local\systeminfo-settings.json`
104
+ - **Windows**: `%APPDATA%\systeminfo-settings.json`
105
105
 
106
106
  #### Settings Commands
107
107
 
package/package.json CHANGED
@@ -1,22 +1,31 @@
1
1
  {
2
2
  "name": "about-system",
3
- "version": "0.0.5",
3
+ "version": "0.0.9",
4
4
  "description": "A Node.js script to display key system information with emojis. Cross-platform support for Windows, macOS, and Linux with customizable output and caching.",
5
- "main": "about-system.js",
5
+ "main": "src/about-system.js",
6
6
  "bin": {
7
- "about-system": "./about-system.js"
7
+ "about-system": "./src/about-system.js",
8
+ "test-speed": "./src/test-speed-cloudflare.js"
9
+ },
10
+ "type": "module",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./src/systeminfo-types.d.ts",
14
+ "import": "./src/about-system.js"
15
+ }
8
16
  },
9
17
  "scripts": {
10
18
  "ship": "npx standard-version --release-as patch; npm publish",
11
- "start": "node about-system.js",
12
- "install-greeting": "node about-system.js --install",
13
- "show-settings": "node about-system.js --settings-show",
14
- "reset-settings": "node about-system.js --settings-reset",
15
- "clear-cache": "node about-system.js --cache-clear",
16
- "help": "node about-system.js --help"
19
+ "start": "node src/about-system.js",
20
+ "test-speed": "node src/test-speed-cloudflare.js",
21
+ "install-greeting": "node src/about-system.js --install",
22
+ "show-settings": "node src/about-system.js --settings-show",
23
+ "reset-settings": "node src/about-system.js --settings-reset",
24
+ "clear-cache": "node src/about-system.js --cache-clear",
25
+ "help": "node src/about-system.js --help"
17
26
  },
18
27
  "keywords": [
19
- "about-system",
28
+ "src/about-system",
20
29
  "system",
21
30
  "information",
22
31
  "cli",
@@ -38,7 +47,7 @@
38
47
  },
39
48
  "homepage": "https://github.com/vtempest/server-shell-setup#readme",
40
49
  "files": [
41
- "about-system.js",
50
+ "src/about-system.js",
42
51
  "README.md",
43
52
  "package.json"
44
53
  ],
@@ -51,5 +60,8 @@
51
60
  "preferGlobal": true,
52
61
  "directories": {
53
62
  "bin": "."
63
+ },
64
+ "dependencies": {
65
+ "ora": "^8.2.0"
54
66
  }
55
67
  }
@@ -15,16 +15,15 @@
15
15
  * Network info (ip, city, domain, isp) is fetched from ipinfo.io
16
16
  * only if needed.
17
17
  *
18
- * Author: vtempest
19
- * Updated: 2025-09-01
20
- * License: MIT
18
+ * @author: vtempest
19
+ * @license: MIT
21
20
  */
22
21
 
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');
22
+ import os from 'os';
23
+ import fs from 'fs';
24
+ import path from 'path';
25
+ import { execSync } from 'child_process';
26
+ import https from 'https';
28
27
 
29
28
  // Cache configuration
30
29
  const CACHE_FILE = path.join(os.tmpdir(), 'systeminfo-cache.json');
@@ -337,9 +336,9 @@ const infoFunctions = {
337
336
  const addresses = [];
338
337
 
339
338
  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);
339
+ for (const device of interfaces[name]) {
340
+ if (device.family === 'IPv4' && !device.internal) {
341
+ addresses.push(device.address);
343
342
  }
344
343
  }
345
344
  }
@@ -439,7 +438,26 @@ const infoFunctions = {
439
438
 
440
439
  let cpuName = '';
441
440
 
442
- if (IS_LINUX) {
441
+ if (IS_WINDOWS) {
442
+ // Windows-specific CPU detection using WMIC
443
+ try {
444
+ const wmic = execCommand('wmic cpu get name /format:list');
445
+ const nameMatch = wmic.match(/Name=(.+)/);
446
+ if (nameMatch) {
447
+ cpuName = nameMatch[1].trim();
448
+ }
449
+ } catch {}
450
+
451
+ // Fallback to PowerShell if WMIC fails
452
+ if (!cpuName) {
453
+ try {
454
+ const ps = execCommand('powershell.exe -Command "Get-WmiObject -Class Win32_Processor | Select-Object -ExpandProperty Name"');
455
+ if (ps.trim()) {
456
+ cpuName = ps.trim();
457
+ }
458
+ } catch {}
459
+ }
460
+ } else if (IS_LINUX) {
443
461
  // Try lscpu first (like bash script)
444
462
  try {
445
463
  const lscpu = execCommand('lscpu');
@@ -467,7 +485,7 @@ const infoFunctions = {
467
485
  // Use Node.js os module for other platforms
468
486
  const cpus = os.cpus();
469
487
  if (cpus.length > 0) {
470
- cpuName = cpus[0].model;
488
+ cpuName = cpus[0].model.trim().replace(/[\r\n]+/g, ' ');
471
489
  }
472
490
  }
473
491
 
@@ -477,7 +495,7 @@ const infoFunctions = {
477
495
  }
478
496
 
479
497
  //remove "with ..." from cpuName
480
- cpuName = cpuName.replace(/with .*/, '');
498
+ cpuName = cpuName.trim().replace(/with .*/, '');
481
499
 
482
500
  const color = colors[settings.colors.cpu] || colors.orange;
483
501
  const emoji = settings.display.show_emojis ? '📈 ' : '';
@@ -490,7 +508,36 @@ const infoFunctions = {
490
508
  const cached = getCachedValue(this.cache, 'gpu', settings);
491
509
  if (cached) return cached;
492
510
 
493
- if (IS_LINUX) {
511
+ if (IS_WINDOWS) {
512
+ try {
513
+ // Get GPU information using WMIC
514
+ const wmic = execCommand('wmic path win32_VideoController get name /format:list');
515
+ const nameMatch = wmic.match(/Name=(.+)/);
516
+ if (nameMatch) {
517
+ const gpu = nameMatch[1].trim();
518
+ if (gpu && gpu !== '' && !gpu.includes('Microsoft Basic')) {
519
+ const color = colors[settings.colors.gpu] || colors.yellow;
520
+ const emoji = settings.display.show_emojis ? '🎮 ' : '';
521
+ const result = `${color}${emoji}${gpu}`;
522
+ setCachedValue(this.cache, 'gpu', result);
523
+ return result;
524
+ }
525
+ }
526
+ } catch {}
527
+
528
+ // Fallback using PowerShell
529
+ try {
530
+ const ps = execCommand('powershell.exe -Command "Get-WmiObject -Class Win32_VideoController | Where-Object {$_.Name -notlike \'*Microsoft Basic*\'} | Select-Object -First 1 -ExpandProperty Name"');
531
+ if (ps.trim()) {
532
+ const gpu = ps.trim();
533
+ const color = colors[settings.colors.gpu] || colors.yellow;
534
+ const emoji = settings.display.show_emojis ? '🎮 ' : '';
535
+ const result = `${color}${emoji}${gpu}`;
536
+ setCachedValue(this.cache, 'gpu', result);
537
+ return result;
538
+ }
539
+ } catch {}
540
+ } else if (IS_LINUX) {
494
541
  try {
495
542
  const lspci = execCommand('lspci');
496
543
  // Look for VGA and specific GPU brands like the bash script
@@ -526,7 +573,40 @@ const infoFunctions = {
526
573
  const cached = getCachedValue(this.cache, 'disk_used', settings);
527
574
  if (cached) return cached;
528
575
 
529
- if (IS_LINUX) {
576
+ if (IS_WINDOWS) {
577
+ try {
578
+ // Get disk usage for C: drive using PowerShell
579
+ 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"');
580
+ if (ps.trim()) {
581
+ const percentFree = parseInt(ps.trim());
582
+ const percentUsed = 100 - percentFree;
583
+ const color = colors[settings.colors.disk_used] || colors.purple;
584
+ const emoji = settings.display.show_emojis ? '📁 ' : '';
585
+ const result = `${color}${emoji}${percentUsed}%`;
586
+ setCachedValue(this.cache, 'disk_used', result);
587
+ return result;
588
+ }
589
+ } catch {}
590
+
591
+ // Fallback using WMIC
592
+ try {
593
+ const wmic = execCommand('wmic logicaldisk where "DeviceID=\'C:\'" get Size,FreeSpace /format:list');
594
+ const sizeMatch = wmic.match(/Size=(\d+)/);
595
+ const freeMatch = wmic.match(/FreeSpace=(\d+)/);
596
+
597
+ if (sizeMatch && freeMatch) {
598
+ const size = parseInt(sizeMatch[1]);
599
+ const free = parseInt(freeMatch[1]);
600
+ const used = size - free;
601
+ const percentUsed = Math.round((used / size) * 100);
602
+ const color = colors[settings.colors.disk_used] || colors.purple;
603
+ const emoji = settings.display.show_emojis ? '📁 ' : '';
604
+ const result = `${color}${emoji}${percentUsed}%`;
605
+ setCachedValue(this.cache, 'disk_used', result);
606
+ return result;
607
+ }
608
+ } catch {}
609
+ } else if (IS_LINUX) {
530
610
  try {
531
611
  const df = execCommand('df -h');
532
612
  let diskUsage = '';
@@ -618,7 +698,44 @@ const infoFunctions = {
618
698
  const cached = getCachedValue(this.cache, 'top_process', settings);
619
699
  if (cached) return cached;
620
700
 
621
- if (IS_LINUX) {
701
+ if (IS_WINDOWS) {
702
+ try {
703
+ // Get top process using PowerShell performance counters
704
+ const ps = execCommand('powershell.exe -Command "Get-Counter \'\\Process(*)\\% Processor Time\' | Select-Object -ExpandProperty CounterSamples | Where-Object {$_.InstanceName -notin @(\'_Total\', \'Idle\')} | Sort-Object CookedValue -Descending | Select-Object -First 1 | Select-Object InstanceName, @{Name=\'CPU%\'; Expression={[math]::Round($_.CookedValue, 1)}}"');
705
+ const lines = ps.split('\n').filter(line => line.trim());
706
+ if (lines.length > 0) {
707
+ const parts = lines[0].trim().split(/\s+/);
708
+ if (parts.length >= 2) {
709
+ const processName = parts[0];
710
+ const cpuPercent = parts[1];
711
+ const color = colors[settings.colors.top_process] || colors.magenta;
712
+ const emoji = settings.display.show_emojis ? '🔝 ' : '';
713
+ const result = `${color}${emoji}${cpuPercent}% ${processName}`;
714
+ setCachedValue(this.cache, 'top_process', result);
715
+ return result;
716
+ }
717
+ }
718
+ } catch {}
719
+
720
+ // Fallback using tasklist
721
+ try {
722
+ const tasklist = execCommand('tasklist /fo csv | findstr /v "Image Name" | sort /r /+5');
723
+ const lines = tasklist.split('\n').filter(line => line.trim());
724
+ if (lines.length > 0) {
725
+ const line = lines[0];
726
+ const parts = line.split(',');
727
+ if (parts.length >= 5) {
728
+ const processName = parts[0].replace(/"/g, '').split('.')[0];
729
+ const memoryUsage = parts[4].replace(/"/g, '').replace(/[,\s]/g, '');
730
+ const color = colors[settings.colors.top_process] || colors.magenta;
731
+ const emoji = settings.display.show_emojis ? '🔝 ' : '';
732
+ const result = `${color}${emoji}${memoryUsage}KB ${processName}`;
733
+ setCachedValue(this.cache, 'top_process', result);
734
+ return result;
735
+ }
736
+ }
737
+ } catch {}
738
+ } else if (IS_LINUX) {
622
739
  try {
623
740
  const ps = execCommand('ps -eo pcpu,comm --sort=-%cpu --no-headers');
624
741
  const lines = ps.split('\n');
@@ -654,7 +771,36 @@ const infoFunctions = {
654
771
  const cached = getCachedValue(this.cache, 'device', settings);
655
772
  if (cached) return cached;
656
773
 
657
- if (IS_LINUX) {
774
+ if (IS_WINDOWS) {
775
+ try {
776
+ // Get computer model using WMIC
777
+ const wmic = execCommand('wmic csproduct get name /format:list');
778
+ const nameMatch = wmic.match(/Name=(.+)/);
779
+ if (nameMatch) {
780
+ const device = nameMatch[1].trim();
781
+ if (device && device !== '') {
782
+ const color = colors[settings.colors.device] || colors.blue;
783
+ const emoji = settings.display.show_emojis ? '💻 ' : '';
784
+ const result = `${color}${emoji}${device}`;
785
+ setCachedValue(this.cache, 'device', result);
786
+ return result;
787
+ }
788
+ }
789
+ } catch {}
790
+
791
+ // Fallback using PowerShell
792
+ try {
793
+ const ps = execCommand('powershell.exe -Command "Get-WmiObject -Class Win32_ComputerSystem | Select-Object -ExpandProperty Model"');
794
+ if (ps.trim()) {
795
+ const device = ps.trim();
796
+ const color = colors[settings.colors.device] || colors.blue;
797
+ const emoji = settings.display.show_emojis ? '💻 ' : '';
798
+ const result = `${color}${emoji}${device}`;
799
+ setCachedValue(this.cache, 'device', result);
800
+ return result;
801
+ }
802
+ } catch {}
803
+ } else if (IS_LINUX) {
658
804
  try {
659
805
  // Check for Android
660
806
  if (commandExists('getprop')) {
@@ -1240,17 +1386,15 @@ function handleSettingsCommand(args) {
1240
1386
  return true;
1241
1387
  }
1242
1388
 
1243
- const cacheResetIndex = args.indexOf('--cache-clear');
1389
+ const cacheResetIndex = args.indexOf('--refresh');
1244
1390
  if (cacheResetIndex !== -1) {
1245
1391
  try {
1246
1392
  if (fs.existsSync(CACHE_FILE)) {
1247
1393
  fs.unlinkSync(CACHE_FILE);
1248
1394
  }
1249
- console.log('Cache cleared');
1250
1395
  } catch (error) {
1251
1396
  console.error('Error clearing cache:', error.message);
1252
1397
  }
1253
- return true;
1254
1398
  }
1255
1399
 
1256
1400
  const setIndex = args.indexOf('--set');
@@ -1430,7 +1574,7 @@ Options:
1430
1574
  --settings-init Initialize settings file with defaults
1431
1575
  --settings-show Display current settings
1432
1576
  --settings-reset Reset settings to defaults
1433
- --cache-clear Clear the cache file
1577
+ --refresh Clear the cache file
1434
1578
  --set <key> <value> Set a configuration value (use dot notation)
1435
1579
 
1436
1580
  Examples:
@@ -1490,11 +1634,11 @@ Linux-specific features:
1490
1634
  }
1491
1635
 
1492
1636
  // Run the script
1493
- if (require.main === module) {
1637
+ // if (import.meta.url === `file://${process.argv[1]}`) {
1494
1638
  main().catch(error => {
1495
1639
  console.error('Error:', error.message);
1496
1640
  process.exit(1);
1497
1641
  });
1498
- }
1642
+ // }
1499
1643
 
1500
- module.exports = { displaySystemInfo, installShellGreeting };
1644
+ export { displaySystemInfo, installShellGreeting };
@@ -0,0 +1,51 @@
1
+ import ora from 'ora';
2
+ import { spawn, execSync } from 'child_process';
3
+
4
+
5
+ /**
6
+ * Tests the download speed in MB/s using Cloudflare's speed test.
7
+ * Optionally displays a spinner message during the test.
8
+ * Their official package is @cloudflare/speedtest and it shows errors and too much output.
9
+ * This script uses a process to hide the errors and give speed in MB/s via CLI or import.
10
+ * @param {boolean} [showMessage=true] - Whether to show explanation message while testing.
11
+ * @returns {Promise<string>} The measured download speed in MB/s as a string (e.g., "12.3").
12
+ *
13
+ * @example
14
+ * import { testDownloadSpeed } from './test-speed-cloudflare.js';
15
+ * const downloadSpeed = await testDownloadSpeed(false);
16
+ * console.log(downloadSpeed); // e.g., "12.3"
17
+ */
18
+ export async function testDownloadSpeed(showMessage = true) {
19
+ var spinner;
20
+ if (showMessage)
21
+ spinner = ora('Testing download speed in MB/s using Cloudflare, runs for 1 minute').start();
22
+
23
+ // globally install @cloudflare/speedtest
24
+ await execSync('npm i -g @cloudflare/speedtest');
25
+
26
+ const result = await new Promise((resolve) => {
27
+ const child = spawn('node', [
28
+ '-e',
29
+ "import SpeedTest from '@cloudflare/speedtest'; new SpeedTest({iterations: 1}).onFinish = r => console.log((r.getDownloadBandwidth() / 8388608).toFixed(1))"
30
+ ]);
31
+
32
+ let output = '';
33
+ child.stdout.on('data', (data) => {
34
+ output += data.toString();
35
+ });
36
+ // Extract last number from output
37
+ child.on('close', () => {
38
+ resolve(output.trim().split('\n').pop().match(/(\d+(\.\d+)?)/)?.[0]);
39
+ });
40
+
41
+ });
42
+
43
+ if (showMessage) spinner.succeed(result + ' MB/s');
44
+ return result;
45
+ }
46
+
47
+ // try{
48
+ // run if file is executed directly
49
+ // if (process?.env.npm_lifecycle_event?.length > 0 || import.meta.url === `file://${process.argv[1]}` || import.meta.url === process.argv[1] || import.meta.url === `file://${process.cwd()}/${process.argv[1]}`)
50
+ testDownloadSpeed();
51
+ // } catch { }