add mac software support
Signed-off-by: si458 <simonsmith5521@gmail.com>
si458 committed
May 30, 2026 at 18:24 UTC
6a3f7878f1664f30a01a801f3d8473bef51ae7d5
2 files changed
+108
agents/meshcore.js
+16
@@ -1729,6 +1729,12 @@ function handleServerCommand(data) {
1729
require('linux-info').packages().then(sendSoftwareResponse).catch(function(e) { sendSoftwareResponse({ error: e.toString() }); });
1730
} else { sendSoftwareResponse({ error: "Not supported" }); }
1731
} catch (e) { sendSoftwareResponse({ error: e.toString() }); }
1732
+ } else if (process.platform == 'darwin') {
1733
+ try {
1734
+ if (require('mac-info').apps) {
1735
+ require('mac-info').apps().then(sendSoftwareResponse).catch(function(e) { sendSoftwareResponse({ error: e.toString() }); });
1736
+ } else { sendSoftwareResponse({ error: "Not supported" }); }
1737
+ } catch (e) { sendSoftwareResponse({ error: e.toString() }); }
1738
} else {
1739
sendSoftwareResponse({ success: false, error: "Not supported" });
1740
}
@@ -6052,6 +6058,16 @@ function processConsoleCommand(cmd, args, rights, sessionid) {
6058
} catch (ex) {
6059
sendConsoleText("Module linux-info error: " + ex, sessionid);
6060
}
6061
+ } else if (process.platform == 'darwin') {
6062
+ try {
6063
+ require('mac-info').apps().then(function (apps) {
6064
+ sendConsoleText(JSON.stringify(apps, null, 1), sessionid);
6065
+ }).catch(function(e) {
6066
+ sendConsoleText("Error: " + e, sessionid);
6067
+ });
6068
+ } catch (ex) {
6069
+ sendConsoleText("Module mac-info error: " + ex, sessionid);
6070
+ }
6071
} else {
6072
sendConsoleText("Installed apps not supported on this platform.", sessionid);
6073
}
agents/modules_meshcore/mac-info.js
new
+92
@@ -0,0 +1,92 @@
1
+
2
+var promise = require('promise');
3
+
4
+function apps() {
5
+ var ret = new promise(function (res, rej) { this._res = res; this._rej = rej; });
6
+
7
+ function runSh(script) {
8
+ try {
9
+ var child = require('child_process').execFile('/bin/sh', ['sh']);
10
+ child.stdout.str = '';
11
+ child.stdout.on('data', function (d) { child.stdout.str += d; });
12
+ child.stdin.write(script + '\nexit\n');
13
+ child.waitExit();
14
+ return child.stdout.str.trim();
15
+ } catch (ex) { return ''; }
16
+ }
17
+
18
+ // Single shell process: PlistBuddy handles both XML and binary plists.
19
+ // Outputs tab-separated: name, version, publisher, date, scope — one app per line.
20
+ var script = [
21
+ 'PB=/usr/libexec/PlistBuddy',
22
+ 'process_app() {',
23
+ ' app="$1" scope="$2"',
24
+ ' plist="$app/Contents/Info.plist"',
25
+ ' [ -f "$plist" ] || return',
26
+ ' name=$(basename "$app" .app)',
27
+ ' version=$("$PB" -c "Print :CFBundleShortVersionString" "$plist" 2>/dev/null)',
28
+ ' publisher=$("$PB" -c "Print :NSHumanReadableCopyright" "$plist" 2>/dev/null)',
29
+ ' if [ -z "$publisher" ]; then',
30
+ ' bid=$("$PB" -c "Print :CFBundleIdentifier" "$plist" 2>/dev/null)',
31
+ ' publisher=$(echo "$bid" | awk -F. \'{if(NF>=2){s=$2; print toupper(substr(s,1,1)) substr(s,2)}}\')',
32
+ ' fi',
33
+ ' date=$(stat -f "%Sm" -t "%Y-%m-%d" "$app" 2>/dev/null)',
34
+ ' arch=""',
35
+ ' exe=$("$PB" -c "Print :CFBundleExecutable" "$plist" 2>/dev/null)',
36
+ ' if [ -n "$exe" ]; then',
37
+ ' lipoout=$(lipo -info "$app/Contents/MacOS/$exe" 2>/dev/null)',
38
+ ' if echo "$lipoout" | grep -q "x86_64" && echo "$lipoout" | grep -q "arm64"; then',
39
+ ' arch="universal"',
40
+ ' elif echo "$lipoout" | grep -q "arm64"; then',
41
+ ' arch="arm64"',
42
+ ' elif echo "$lipoout" | grep -q "x86_64"; then',
43
+ ' arch="x86_64"',
44
+ ' fi',
45
+ ' fi',
46
+ ' printf "%s\\t%s\\t%s\\t%s\\t%s\\t%s\\n" \\',
47
+ ' "$(printf "%s" "$name" | tr "\\t\\n" " ")" \\',
48
+ ' "$(printf "%s" "$version" | tr "\\t\\n" " ")" \\',
49
+ ' "$(printf "%s" "$publisher" | tr "\\t\\n" " ")" \\',
50
+ ' "$date" "$arch" "$scope"',
51
+ '}',
52
+ 'for sysdir in /Applications /System/Applications /System/Applications/Utilities; do',
53
+ ' [ -d "$sysdir" ] || continue',
54
+ ' find "$sysdir" -maxdepth 1 -name "*.app" -type d 2>/dev/null | while IFS= read -r app; do',
55
+ ' process_app "$app" system',
56
+ ' done',
57
+ 'done',
58
+ 'for userdir in /Users/*/; do',
59
+ ' user=$(basename "$userdir")',
60
+ ' [ "$user" = Shared ] && continue',
61
+ ' case "$user" in .*) continue;; esac',
62
+ ' appsdir="${userdir}Applications"',
63
+ ' [ -d "$appsdir" ] || continue',
64
+ ' find "$appsdir" -maxdepth 1 -name "*.app" -type d 2>/dev/null | while IFS= read -r app; do',
65
+ ' process_app "$app" user',
66
+ ' done',
67
+ 'done'
68
+ ].join('\n');
69
+
70
+ try {
71
+ var out = runSh(script);
72
+ var results = [];
73
+ if (out) {
74
+ out.split('\n').forEach(function (line) {
75
+ var p = line.split('\t');
76
+ if (p.length < 6 || !p[0]) return;
77
+ results.push({ name: p[0], version: p[1] || '', publisher: p[2] || '', date: p[3] || '', arch: p[4] || '', location: p[5] || 'system' });
78
+ });
79
+ }
80
+ ret._res(results);
81
+ } catch (e) {
82
+ ret._rej(e);
83
+ }
84
+ return (ret);
85
+}
86
+
87
+if (process.platform == 'darwin') {
88
+ module.exports = { apps: apps };
89
+} else {
90
+ var not_supported = function () { throw (process.platform + ' not supported'); };
91
+ module.exports = { apps: not_supported };
92
+}