@pheem49/mint 1.5.0 → 1.5.1

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.
Files changed (78) hide show
  1. package/README.md +27 -1
  2. package/main.js +28 -14
  3. package/mint-cli-logic.js +3 -119
  4. package/mint-cli.js +497 -23
  5. package/models/Shiroko_Model/Shiroko/Shiroko_Core/72d86db84cfa9730b894c241fd24c0db.png +0 -0
  6. package/models/Shiroko_Model/Shiroko/Shiroko_Core/items_pinned_to_model.json +14 -0
  7. package/models/Shiroko_Model/Shiroko/Shiroko_Core//345/221/206/347/214/253.exp3.json +10 -0
  8. package/models/Shiroko_Model/Shiroko/Shiroko_Core//345/221/206/347/214/253/347/234/274/347/217/240/346/221/207/346/231/203.exp3.json +15 -0
  9. package/models/Shiroko_Model/Shiroko/Shiroko_Core//345/233/264/350/243/231.exp3.json +10 -0
  10. package/models/Shiroko_Model/Shiroko/Shiroko_Core//346/213/215/347/205/247.exp3.json +50 -0
  11. package/models/Shiroko_Model/Shiroko/Shiroko_Core//346/213/277/347/254/224.exp3.json +10 -0
  12. package/models/Shiroko_Model/Shiroko/Shiroko_Core//347/202/271/344/270/200/344/270/213.exp3.json +10 -0
  13. package/models/Shiroko_Model/Shiroko/Shiroko_Core//347/214/253/345/222/252/346/273/244/351/225/234.exp3.json +10 -0
  14. package/models/Shiroko_Model/Shiroko/Shiroko_Core//347/234/274/351/225/234.exp3.json +10 -0
  15. package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_00.png +0 -0
  16. package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_01.png +0 -0
  17. package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_02.png +0 -0
  18. package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_03.png +0 -0
  19. package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.cdi3.json +1498 -0
  20. package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.moc3 +0 -0
  21. package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.model3.json +47 -0
  22. package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.physics3.json +6658 -0
  23. package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.vtube.json +1299 -0
  24. package/models/Shiroko_Model/Shiroko//342/232/241/351/253/230/344/272/256/342/232/241/344/275/277/347/224/250/346/225/231/347/250/213/344/270/216/346/263/250/346/204/217/344/272/213/351/241/271.txt +23 -0
  25. package/package.json +26 -1
  26. package/src/AI_Brain/Gemini_API.js +147 -46
  27. package/src/AI_Brain/autonomous_brain.js +2 -1
  28. package/src/AI_Brain/memory_store.js +299 -3
  29. package/src/CLI/chat_router.js +18 -6
  30. package/src/CLI/chat_ui.js +396 -50
  31. package/src/CLI/code_agent.js +203 -14
  32. package/src/CLI/image_input.js +90 -0
  33. package/src/CLI/onboarding.js +72 -15
  34. package/src/CLI/updater.js +6 -4
  35. package/src/System/action_executor.js +59 -10
  36. package/src/System/config_manager.js +31 -1
  37. package/src/System/granular_automation.js +122 -53
  38. package/src/System/proactive_loop.js +19 -3
  39. package/src/System/safety_manager.js +108 -0
  40. package/src/System/sandbox_runner.js +182 -0
  41. package/src/System/system_automation.js +127 -81
  42. package/src/System/system_info.js +70 -0
  43. package/src/System/tool_registry.js +280 -0
  44. package/src/System/window_manager.js +4 -2
  45. package/src/UI/live2d_manager.js +368 -0
  46. package/src/UI/renderer.js +176 -18
  47. package/src/UI/styles.css +452 -31
  48. package/.codex +0 -0
  49. package/docs/assets/Agent_Mint.png +0 -0
  50. package/docs/assets/CLI_Screen.png +0 -0
  51. package/docs/assets/Settings.png +0 -0
  52. package/docs/assets/icon.png +0 -0
  53. package/docs/guide.html +0 -632
  54. package/docs/index.html +0 -133
  55. package/docs/style.css +0 -579
  56. package/index.html +0 -16
  57. package/src/UI/index.html +0 -126
  58. package/tech_news.txt +0 -3
  59. package/test_knowledge.txt +0 -3
  60. package/tests/action_executor_safety.test.js +0 -67
  61. package/tests/agent_orchestrator.test.js +0 -41
  62. package/tests/chat_router.test.js +0 -42
  63. package/tests/code_agent.test.js +0 -69
  64. package/tests/config_manager.test.js +0 -141
  65. package/tests/docker.test.js +0 -46
  66. package/tests/file_operations.test.js +0 -57
  67. package/tests/gmail.test.js +0 -135
  68. package/tests/gmail_auth.test.js +0 -129
  69. package/tests/google_calendar.test.js +0 -113
  70. package/tests/google_tts_urls.test.js +0 -24
  71. package/tests/memory_store.test.js +0 -185
  72. package/tests/notion.test.js +0 -121
  73. package/tests/provider_routing.test.js +0 -83
  74. package/tests/safety_manager.test.js +0 -40
  75. package/tests/spotify.test.js +0 -201
  76. package/tests/system_monitor.test.js +0 -37
  77. package/tests/updater.test.js +0 -32
  78. package/tests/workspace_manager.test.js +0 -56
@@ -0,0 +1,182 @@
1
+ const { execFile, spawnSync } = require('child_process');
2
+ const fs = require('fs');
3
+ const os = require('os');
4
+ const path = require('path');
5
+ const safetyManager = require('./safety_manager');
6
+
7
+ function commandExists(command) {
8
+ const lookup = process.platform === 'win32' ? 'where' : 'which';
9
+ const result = spawnSync(lookup, [command], { encoding: 'utf8', shell: false });
10
+ return result.status === 0;
11
+ }
12
+
13
+ function uniqueExistingRoots(roots) {
14
+ return Array.from(new Set((roots || [])
15
+ .filter(Boolean)
16
+ .map((root) => path.resolve(root))
17
+ .filter((root) => fs.existsSync(root))));
18
+ }
19
+
20
+ function buildBubblewrapArgs(command, options = {}) {
21
+ const cwd = path.resolve(options.cwd || process.cwd());
22
+ const readRoots = uniqueExistingRoots(safetyManager.getAllowedRoots('read'));
23
+ const writeRoots = uniqueExistingRoots(safetyManager.getAllowedRoots('write'));
24
+ const writableSet = new Set(writeRoots);
25
+ const bindRoots = uniqueExistingRoots([cwd, ...readRoots, ...writeRoots]);
26
+
27
+ const args = [
28
+ '--die-with-parent',
29
+ '--proc', '/proc',
30
+ '--dev', '/dev',
31
+ '--tmpfs', '/tmp',
32
+ '--ro-bind', '/usr', '/usr',
33
+ '--ro-bind', '/bin', '/bin',
34
+ '--ro-bind', '/etc', '/etc'
35
+ ];
36
+
37
+ for (const libPath of ['/lib', '/lib64']) {
38
+ if (fs.existsSync(libPath)) args.push('--ro-bind', libPath, libPath);
39
+ }
40
+
41
+ const parentDirs = new Set();
42
+ for (const root of bindRoots) {
43
+ let current = path.dirname(root);
44
+ while (current && current !== path.dirname(current)) {
45
+ parentDirs.add(current);
46
+ current = path.dirname(current);
47
+ }
48
+ }
49
+ for (const dir of Array.from(parentDirs).sort((a, b) => a.length - b.length)) {
50
+ if (!['/usr', '/bin', '/etc', '/lib', '/lib64'].includes(dir)) {
51
+ args.push('--dir', dir);
52
+ }
53
+ }
54
+
55
+ for (const root of bindRoots) {
56
+ const flag = writableSet.has(root) || root === cwd ? '--bind' : '--ro-bind';
57
+ args.push(flag, root, root);
58
+ }
59
+
60
+ args.push('--chdir', cwd, 'bash', '-lc', command);
61
+ return args;
62
+ }
63
+
64
+ function escapeSandboxProfileString(value) {
65
+ return String(value || '').replace(/\\/g, '\\\\').replace(/"/g, '\\"');
66
+ }
67
+
68
+ function buildMacSandboxProfile(options = {}) {
69
+ const cwd = path.resolve(options.cwd || process.cwd());
70
+ const readRoots = uniqueExistingRoots(safetyManager.getAllowedRoots('read'));
71
+ const writeRoots = uniqueExistingRoots(safetyManager.getAllowedRoots('write'));
72
+ const allowedRead = uniqueExistingRoots([
73
+ cwd,
74
+ '/bin',
75
+ '/sbin',
76
+ '/usr',
77
+ '/System',
78
+ '/Library',
79
+ ...readRoots,
80
+ ...writeRoots
81
+ ]);
82
+ const allowedWrite = uniqueExistingRoots([cwd, ...writeRoots]);
83
+
84
+ const readRules = allowedRead.map((root) => ` (subpath "${escapeSandboxProfileString(root)}")`).join('\n');
85
+ const writeRules = allowedWrite.map((root) => ` (subpath "${escapeSandboxProfileString(root)}")`).join('\n');
86
+
87
+ return [
88
+ '(version 1)',
89
+ '(deny default)',
90
+ '(allow process*)',
91
+ '(allow sysctl-read)',
92
+ '(allow signal (target self))',
93
+ '(allow file-read-metadata)',
94
+ '(allow file-read*',
95
+ readRules,
96
+ ')',
97
+ '(allow file-write*',
98
+ writeRules,
99
+ ` (subpath "${escapeSandboxProfileString(os.tmpdir())}")`,
100
+ ')'
101
+ ].join('\n');
102
+ }
103
+
104
+ function getShellInvocation(command) {
105
+ if (process.platform === 'win32') {
106
+ return {
107
+ command: 'powershell.exe',
108
+ args: ['-NoLogo', '-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command', command]
109
+ };
110
+ }
111
+ return {
112
+ command: 'bash',
113
+ args: ['-lc', command]
114
+ };
115
+ }
116
+
117
+ function execFilePromise(command, args, options = {}) {
118
+ return new Promise((resolve, reject) => {
119
+ execFile(command, args, options, (error, stdout, stderr) => {
120
+ if (error) {
121
+ error.stdout = stdout;
122
+ error.stderr = stderr;
123
+ reject(error);
124
+ return;
125
+ }
126
+ resolve({ stdout, stderr });
127
+ });
128
+ });
129
+ }
130
+
131
+ async function runShell(command, options = {}) {
132
+ const policy = safetyManager.getPolicy();
133
+ safetyManager.assertShellCommandAllowed(command);
134
+
135
+ const cwd = path.resolve(options.cwd || process.cwd());
136
+ const execOptions = {
137
+ cwd,
138
+ maxBuffer: options.maxBuffer || 1024 * 1024 * 4,
139
+ env: options.env || process.env
140
+ };
141
+
142
+ if (!policy.enabled || policy.sandboxMode === 'off') {
143
+ const shell = getShellInvocation(command);
144
+ return execFilePromise(shell.command, shell.args, execOptions);
145
+ }
146
+
147
+ const sandboxCommand = policy.sandboxCommand || 'bwrap';
148
+ if (process.platform === 'linux' && commandExists(sandboxCommand)) {
149
+ return execFilePromise(sandboxCommand, buildBubblewrapArgs(command, { cwd }), execOptions);
150
+ }
151
+
152
+ if (process.platform === 'darwin' && commandExists('sandbox-exec')) {
153
+ return execFilePromise('sandbox-exec', ['-p', buildMacSandboxProfile({ cwd }), 'bash', '-lc', command], execOptions);
154
+ }
155
+
156
+ if (policy.sandboxMode === 'enforce') {
157
+ const hint = process.platform === 'darwin'
158
+ ? "macOS sandbox-exec is not available."
159
+ : process.platform === 'win32'
160
+ ? 'Windows sandbox provider is not configured. Use WSL/containers or set sandboxMode to prefer.'
161
+ : `Sandbox command '${sandboxCommand}' is not available.`;
162
+ throw new Error(`Sandbox is enforced but no sandbox provider could run. ${hint}`);
163
+ }
164
+
165
+ safetyManager.appendActionLog({
166
+ source: options.source || 'sandbox_runner',
167
+ action: 'sandbox_fallback',
168
+ sandboxCommand,
169
+ platform: process.platform,
170
+ cwd
171
+ });
172
+ const shell = getShellInvocation(command);
173
+ return execFilePromise(shell.command, shell.args, execOptions);
174
+ }
175
+
176
+ module.exports = {
177
+ runShell,
178
+ buildBubblewrapArgs,
179
+ buildMacSandboxProfile,
180
+ getShellInvocation,
181
+ commandExists
182
+ };
@@ -1,115 +1,161 @@
1
- const { exec } = require('child_process');
1
+ const { execFile } = require('child_process');
2
+ const fs = require('fs');
2
3
 
3
- /**
4
- * Executes a shell command and returns a promise.
5
- */
6
- function execPromise(command) {
4
+ function execPromise(command, args = []) {
7
5
  return new Promise((resolve, reject) => {
8
- exec(command, (error, stdout, stderr) => {
6
+ execFile(command, args, (error, stdout, stderr) => {
9
7
  if (error) {
8
+ error.stderr = stderr;
10
9
  reject(error);
11
10
  return;
12
11
  }
13
- resolve(stdout.trim());
12
+ resolve(String(stdout || '').trim());
14
13
  });
15
14
  });
16
15
  }
17
16
 
18
- /**
19
- * Linux System Automation Logic
20
- */
21
- const SystemAutomation = {
22
- // Volume Control (Using amixer / pulseaudio)
17
+ function unsupported(feature) {
18
+ throw new Error(`${feature} is not supported on ${process.platform} by the current automation provider.`);
19
+ }
20
+
21
+ function clampPercent(percent) {
22
+ const value = Number(percent);
23
+ if (!Number.isFinite(value)) return 50;
24
+ return Math.max(0, Math.min(100, Math.round(value)));
25
+ }
26
+
27
+ const linuxProvider = {
23
28
  async setVolume(percent) {
24
- // Try amixer first (common on many distros)
29
+ const value = clampPercent(percent);
25
30
  try {
26
- await execPromise(`amixer -D pulse sset Master ${percent}%`);
27
- return `Volume set to ${percent}%`;
28
- } catch (e) {
29
- try {
30
- await execPromise(`pactl set-sink-volume @DEFAULT_SINK@ ${percent}%`);
31
- return `Volume set to ${percent}%`;
32
- } catch (err) {
33
- throw new Error("Failed to set volume. amixer or pactl not found.");
34
- }
31
+ await execPromise('amixer', ['-D', 'pulse', 'sset', 'Master', `${value}%`]);
32
+ return `Volume set to ${value}%`;
33
+ } catch (_) {
34
+ await execPromise('pactl', ['set-sink-volume', '@DEFAULT_SINK@', `${value}%`]);
35
+ return `Volume set to ${value}%`;
35
36
  }
36
37
  },
37
-
38
38
  async mute() {
39
39
  try {
40
- await execPromise(`amixer -D pulse sset Master toggle`);
41
- return "Volume toggled (mute/unmute)";
42
- } catch (e) {
43
- await execPromise(`pactl set-sink-mute @DEFAULT_SINK@ toggle`);
44
- return "Volume toggled (mute/unmute)";
40
+ await execPromise('amixer', ['-D', 'pulse', 'sset', 'Master', 'toggle']);
41
+ return 'Volume toggled (mute/unmute)';
42
+ } catch (_) {
43
+ await execPromise('pactl', ['set-sink-mute', '@DEFAULT_SINK@', 'toggle']);
44
+ return 'Volume toggled (mute/unmute)';
45
45
  }
46
46
  },
47
-
48
- // Brightness Control (Using brightnessctl or xbacklight)
49
47
  async setBrightness(percent) {
48
+ const value = clampPercent(percent);
50
49
  try {
51
- // brightnessctl is modern and common on Wayland/X11
52
- await execPromise(`brightnessctl set ${percent}%`);
53
- return `Brightness set to ${percent}%`;
54
- } catch (e) {
55
- try {
56
- await execPromise(`xbacklight -set ${percent}`);
57
- return `Brightness set to ${percent}%`;
58
- } catch (err) {
59
- throw new Error("Failed to set brightness. brightnessctl or xbacklight not found.");
60
- }
50
+ await execPromise('brightnessctl', ['set', `${value}%`]);
51
+ return `Brightness set to ${value}%`;
52
+ } catch (_) {
53
+ await execPromise('xbacklight', ['-set', String(value)]);
54
+ return `Brightness set to ${value}%`;
61
55
  }
62
56
  },
63
-
64
- // Power Management
65
- async sleep() {
66
- return execPromise('systemctl suspend');
57
+ sleep: () => execPromise('systemctl', ['suspend']),
58
+ restart: () => execPromise('systemctl', ['reboot']),
59
+ shutdown: () => execPromise('systemctl', ['poweroff']),
60
+ async minimizeAll() {
61
+ await execPromise('xdotool', ['key', 'Super+d']);
62
+ return 'Minimized all windows';
67
63
  },
64
+ async getSystemInfo() {
65
+ try {
66
+ const osInfo = await execPromise('lsb_release', ['-ds']);
67
+ const kernel = await execPromise('uname', ['-r']);
68
+ const arch = await execPromise('uname', ['-m']);
69
+ return `Operating System: ${osInfo}\nKernel: ${kernel}\nArchitecture: ${arch}`;
70
+ } catch (_) {
71
+ const osRelease = fs.existsSync('/etc/os-release') ? fs.readFileSync('/etc/os-release', 'utf8') : '';
72
+ const prettyName = (osRelease.match(/^PRETTY_NAME="?([^"\n]+)"?/m) || [])[1] || 'Linux';
73
+ const kernel = await execPromise('uname', ['-r']);
74
+ const arch = await execPromise('uname', ['-m']);
75
+ return `Operating System: ${prettyName}\nKernel: ${kernel}\nArchitecture: ${arch}`;
76
+ }
77
+ }
78
+ };
68
79
 
69
- async restart() {
70
- return execPromise('systemctl reboot');
80
+ const macProvider = {
81
+ async setVolume(percent) {
82
+ const value = clampPercent(percent);
83
+ await execPromise('osascript', ['-e', `set volume output volume ${value}`]);
84
+ return `Volume set to ${value}%`;
71
85
  },
72
-
73
- async shutdown() {
74
- return execPromise('systemctl poweroff');
86
+ async mute() {
87
+ await execPromise('osascript', ['-e', 'set volume output muted not (output muted of (get volume settings))']);
88
+ return 'Volume toggled (mute/unmute)';
89
+ },
90
+ setBrightness: () => unsupported('Brightness control'),
91
+ sleep: () => execPromise('osascript', ['-e', 'tell application "System Events" to sleep']),
92
+ restart: () => execPromise('osascript', ['-e', 'tell application "System Events" to restart']),
93
+ shutdown: () => execPromise('osascript', ['-e', 'tell application "System Events" to shut down']),
94
+ async minimizeAll() {
95
+ await execPromise('osascript', ['-e', 'tell application "System Events" to keystroke "h" using {command down, option down}']);
96
+ return 'Hid visible applications';
75
97
  },
98
+ async getSystemInfo() {
99
+ const product = await execPromise('sw_vers', ['-productName']);
100
+ const version = await execPromise('sw_vers', ['-productVersion']);
101
+ const build = await execPromise('sw_vers', ['-buildVersion']);
102
+ const arch = await execPromise('uname', ['-m']);
103
+ return `Operating System: ${product} ${version} (${build})\nArchitecture: ${arch}`;
104
+ }
105
+ };
76
106
 
77
- // Window Management (Minimal implementation using xdotool if available)
107
+ const windowsProvider = {
108
+ setVolume: () => unsupported('Volume control'),
109
+ mute: () => unsupported('Mute control'),
110
+ setBrightness: () => unsupported('Brightness control'),
111
+ sleep: () => execPromise('rundll32.exe', ['powrprof.dll,SetSuspendState', '0,1,0']),
112
+ restart: () => execPromise('shutdown.exe', ['/r', '/t', '0']),
113
+ shutdown: () => execPromise('shutdown.exe', ['/s', '/t', '0']),
78
114
  async minimizeAll() {
79
- try {
80
- await execPromise('xdotool key Super+d');
81
- return "Minimized all windows";
82
- } catch (e) {
83
- throw new Error("xdotool not found. Cannot perform window management.");
84
- }
115
+ await execPromise('powershell.exe', [
116
+ '-NoProfile',
117
+ '-NonInteractive',
118
+ '-Command',
119
+ '$shell = New-Object -ComObject Shell.Application; $shell.MinimizeAll()'
120
+ ]);
121
+ return 'Minimized all windows';
85
122
  },
123
+ async getSystemInfo() {
124
+ const caption = await execPromise('powershell.exe', [
125
+ '-NoProfile',
126
+ '-NonInteractive',
127
+ '-Command',
128
+ '(Get-CimInstance Win32_OperatingSystem).Caption'
129
+ ]);
130
+ const version = await execPromise('cmd.exe', ['/c', 'ver']);
131
+ const arch = process.arch;
132
+ return `Operating System: ${caption}\nVersion: ${version}\nArchitecture: ${arch}`;
133
+ }
134
+ };
86
135
 
87
- // System Information
88
- async getSystemInfo(target = "") {
89
- // If target is empty, return OS info
90
- if (!target) {
91
- try {
92
- // Try lsb_release first
93
- const osInfo = await execPromise('lsb_release -ds');
94
- const kernel = await execPromise('uname -r');
95
- const arch = await execPromise('uname -m');
96
- return `Operating System: ${osInfo}\nKernel: ${kernel}\nArchitecture: ${arch}`;
97
- } catch (e) {
98
- try {
99
- // Fallback to /etc/os-release
100
- const osInfo = await execPromise('grep PRETTY_NAME /etc/os-release | cut -d\'"\' -f2');
101
- const kernel = await execPromise('uname -r');
102
- const arch = await execPromise('uname -m');
103
- return `Operating System: ${osInfo}\nKernel: ${kernel}\nArchitecture: ${arch}`;
104
- } catch (err) {
105
- return "Could not retrieve OS information.";
106
- }
107
- }
108
- }
109
-
110
- // Handle weather or other info if target is provided
111
- // For now, let's just return a placeholder or handle it if needed
136
+ function getProvider(platform = process.platform) {
137
+ if (platform === 'darwin') return macProvider;
138
+ if (platform === 'win32') return windowsProvider;
139
+ return linuxProvider;
140
+ }
141
+
142
+ const SystemAutomation = {
143
+ setVolume: (percent) => getProvider().setVolume(percent),
144
+ mute: () => getProvider().mute(),
145
+ setBrightness: (percent) => getProvider().setBrightness(percent),
146
+ sleep: () => getProvider().sleep(),
147
+ restart: () => getProvider().restart(),
148
+ shutdown: () => getProvider().shutdown(),
149
+ minimizeAll: () => getProvider().minimizeAll(),
150
+ async getSystemInfo(target = '') {
151
+ if (!target) return getProvider().getSystemInfo();
112
152
  return `System info for ${target} is not yet implemented.`;
153
+ },
154
+ _providers: {
155
+ linux: linuxProvider,
156
+ darwin: macProvider,
157
+ win32: windowsProvider,
158
+ getProvider
113
159
  }
114
160
  };
115
161
 
@@ -1,4 +1,70 @@
1
1
  const os = require('os');
2
+ const fs = require('fs');
3
+
4
+ function readFirstExisting(paths) {
5
+ for (const filePath of paths) {
6
+ try {
7
+ const value = fs.readFileSync(filePath, 'utf8').trim();
8
+ if (value && value !== 'None' && value !== 'To be filled by O.E.M.') {
9
+ return value;
10
+ }
11
+ } catch (_) {}
12
+ }
13
+ return '';
14
+ }
15
+
16
+ function getLinuxDistro() {
17
+ try {
18
+ const content = fs.readFileSync('/etc/os-release', 'utf8');
19
+ const values = {};
20
+ content.split('\n').forEach(line => {
21
+ const match = line.match(/^([A-Z_]+)=(.*)$/);
22
+ if (match) {
23
+ values[match[1]] = match[2].replace(/^"|"$/g, '');
24
+ }
25
+ });
26
+ return values.PRETTY_NAME || values.NAME || '';
27
+ } catch (_) {
28
+ return '';
29
+ }
30
+ }
31
+
32
+ function getMachineModel() {
33
+ if (os.platform() !== 'linux') {
34
+ return {
35
+ vendor: '',
36
+ product: os.hostname(),
37
+ version: '',
38
+ board: '',
39
+ display: os.hostname()
40
+ };
41
+ }
42
+
43
+ const vendor = readFirstExisting([
44
+ '/sys/devices/virtual/dmi/id/sys_vendor',
45
+ '/sys/class/dmi/id/sys_vendor'
46
+ ]);
47
+ const product = readFirstExisting([
48
+ '/sys/devices/virtual/dmi/id/product_name',
49
+ '/sys/class/dmi/id/product_name'
50
+ ]);
51
+ const version = readFirstExisting([
52
+ '/sys/devices/virtual/dmi/id/product_version',
53
+ '/sys/class/dmi/id/product_version'
54
+ ]);
55
+ const board = readFirstExisting([
56
+ '/sys/devices/virtual/dmi/id/board_name',
57
+ '/sys/class/dmi/id/board_name'
58
+ ]);
59
+
60
+ return {
61
+ vendor,
62
+ product,
63
+ version,
64
+ board,
65
+ display: [vendor, product, version].filter(Boolean).join(' ') || board || os.hostname()
66
+ };
67
+ }
2
68
 
3
69
  /**
4
70
  * ดึงข้อมูล RAM, CPU, เวลาปัจจุบัน
@@ -16,9 +82,12 @@ function getSystemInfo() {
16
82
  const cpuModel = os.cpus()[0]?.model || 'Unknown CPU';
17
83
  const cpuCores = os.cpus().length;
18
84
  const platform = os.platform();
85
+ const distro = platform === 'linux' ? getLinuxDistro() : '';
19
86
  const hostname = os.hostname();
87
+ const machine = getMachineModel();
20
88
 
21
89
  return {
90
+ machine,
22
91
  ram: {
23
92
  total: (totalRAM / 1024 / 1024 / 1024).toFixed(2) + ' GB',
24
93
  used: (usedRAM / 1024 / 1024 / 1024).toFixed(2) + ' GB',
@@ -32,6 +101,7 @@ function getSystemInfo() {
32
101
  time: timeStr,
33
102
  date: dateStr,
34
103
  platform,
104
+ distro,
35
105
  hostname
36
106
  };
37
107
  }