@fredlackey/devutils 0.0.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.
- package/README.md +156 -0
- package/bin/dev.js +16 -0
- package/files/README.md +0 -0
- package/files/claude/.claude/commands/setup-context.md +3 -0
- package/files/monorepos/_archive/README.md +36 -0
- package/files/monorepos/_legacy/README.md +36 -0
- package/files/monorepos/ai-docs/README.md +33 -0
- package/files/monorepos/apps/README.md +24 -0
- package/files/monorepos/docs/README.md +40 -0
- package/files/monorepos/packages/README.md +25 -0
- package/files/monorepos/research/README.md +29 -0
- package/files/monorepos/scripts/README.md +24 -0
- package/package.json +39 -0
- package/src/cli.js +68 -0
- package/src/commands/README.md +41 -0
- package/src/commands/configure.js +199 -0
- package/src/commands/identity.js +1630 -0
- package/src/commands/ignore.js +247 -0
- package/src/commands/install.js +173 -0
- package/src/commands/setup.js +212 -0
- package/src/commands/status.js +223 -0
- package/src/completion.js +284 -0
- package/src/constants.js +45 -0
- package/src/ignore/claude-code.txt +10 -0
- package/src/ignore/docker.txt +18 -0
- package/src/ignore/linux.txt +23 -0
- package/src/ignore/macos.txt +36 -0
- package/src/ignore/node.txt +55 -0
- package/src/ignore/terraform.txt +37 -0
- package/src/ignore/vscode.txt +18 -0
- package/src/ignore/windows.txt +35 -0
- package/src/index.js +0 -0
- package/src/installs/README.md +399 -0
- package/src/installs/adobe-creative-cloud.js +44 -0
- package/src/installs/appcleaner.js +44 -0
- package/src/installs/atomicparsley.js +44 -0
- package/src/installs/aws-cli.js +44 -0
- package/src/installs/balena-etcher.js +44 -0
- package/src/installs/bambu-studio.js +44 -0
- package/src/installs/bash-completion.js +44 -0
- package/src/installs/bash.js +44 -0
- package/src/installs/beyond-compare.js +44 -0
- package/src/installs/build-essential.js +44 -0
- package/src/installs/caffeine.js +44 -0
- package/src/installs/camtasia.js +44 -0
- package/src/installs/chatgpt.js +44 -0
- package/src/installs/chrome-canary.js +44 -0
- package/src/installs/chromium.js +44 -0
- package/src/installs/claude-code.js +44 -0
- package/src/installs/curl.js +44 -0
- package/src/installs/cursor.js +44 -0
- package/src/installs/dbschema.js +44 -0
- package/src/installs/docker.js +44 -0
- package/src/installs/drawio.js +44 -0
- package/src/installs/elmedia-player.js +44 -0
- package/src/installs/ffmpeg.js +44 -0
- package/src/installs/gemini-cli.js +44 -0
- package/src/installs/git.js +44 -0
- package/src/installs/gitego.js +44 -0
- package/src/installs/go.js +44 -0
- package/src/installs/google-chrome.js +44 -0
- package/src/installs/gpg.js +141 -0
- package/src/installs/homebrew.js +44 -0
- package/src/installs/imageoptim.js +44 -0
- package/src/installs/jq.js +44 -0
- package/src/installs/keyboard-maestro.js +44 -0
- package/src/installs/latex.js +44 -0
- package/src/installs/lftp.js +44 -0
- package/src/installs/messenger.js +44 -0
- package/src/installs/microsoft-office.js +44 -0
- package/src/installs/microsoft-teams.js +44 -0
- package/src/installs/node.js +44 -0
- package/src/installs/nordpass.js +44 -0
- package/src/installs/nvm.js +44 -0
- package/src/installs/openssh.js +134 -0
- package/src/installs/pandoc.js +44 -0
- package/src/installs/pinentry.js +44 -0
- package/src/installs/pngyu.js +44 -0
- package/src/installs/postman.js +44 -0
- package/src/installs/safari-tech-preview.js +44 -0
- package/src/installs/sfnt2woff.js +44 -0
- package/src/installs/shellcheck.js +44 -0
- package/src/installs/slack.js +44 -0
- package/src/installs/snagit.js +44 -0
- package/src/installs/spotify.js +44 -0
- package/src/installs/studio-3t.js +44 -0
- package/src/installs/sublime-text.js +44 -0
- package/src/installs/superwhisper.js +44 -0
- package/src/installs/tailscale.js +44 -0
- package/src/installs/termius.js +44 -0
- package/src/installs/terraform.js +44 -0
- package/src/installs/tidal.js +44 -0
- package/src/installs/tmux.js +44 -0
- package/src/installs/tree.js +44 -0
- package/src/installs/vim.js +44 -0
- package/src/installs/vlc.js +44 -0
- package/src/installs/vscode.js +44 -0
- package/src/installs/whatsapp.js +44 -0
- package/src/installs/woff2.js +44 -0
- package/src/installs/xcode.js +44 -0
- package/src/installs/yarn.js +44 -0
- package/src/installs/yq.js +44 -0
- package/src/installs/yt-dlp.js +44 -0
- package/src/installs/zoom.js +44 -0
- package/src/scripts/README.md +95 -0
- package/src/scripts/afk.js +23 -0
- package/src/scripts/backup-all.js +24 -0
- package/src/scripts/backup-source.js +24 -0
- package/src/scripts/brewd.js +23 -0
- package/src/scripts/brewi.js +24 -0
- package/src/scripts/brewr.js +24 -0
- package/src/scripts/brews.js +24 -0
- package/src/scripts/brewu.js +23 -0
- package/src/scripts/c.js +23 -0
- package/src/scripts/ccurl.js +24 -0
- package/src/scripts/certbot-crontab-init.js +24 -0
- package/src/scripts/certbot-init.js +25 -0
- package/src/scripts/ch.js +23 -0
- package/src/scripts/claude-danger.js +23 -0
- package/src/scripts/clean-dev.js +24 -0
- package/src/scripts/clear-dns-cache.js +23 -0
- package/src/scripts/clone.js +25 -0
- package/src/scripts/code-all.js +24 -0
- package/src/scripts/count-files.js +24 -0
- package/src/scripts/count-folders.js +24 -0
- package/src/scripts/count.js +24 -0
- package/src/scripts/d.js +23 -0
- package/src/scripts/datauri.js +24 -0
- package/src/scripts/delete-files.js +24 -0
- package/src/scripts/docker-clean.js +24 -0
- package/src/scripts/dp.js +23 -0
- package/src/scripts/e.js +24 -0
- package/src/scripts/empty-trash.js +23 -0
- package/src/scripts/evm.js +25 -0
- package/src/scripts/fetch-github-repos.js +25 -0
- package/src/scripts/get-channel.js +24 -0
- package/src/scripts/get-course.js +26 -0
- package/src/scripts/get-dependencies.js +25 -0
- package/src/scripts/get-folder.js +26 -0
- package/src/scripts/get-tunes.js +25 -0
- package/src/scripts/get-video.js +24 -0
- package/src/scripts/git-backup.js +25 -0
- package/src/scripts/git-clone.js +25 -0
- package/src/scripts/git-pup.js +23 -0
- package/src/scripts/git-push.js +24 -0
- package/src/scripts/h.js +24 -0
- package/src/scripts/hide-desktop-icons.js +23 -0
- package/src/scripts/hide-hidden-files.js +23 -0
- package/src/scripts/install-dependencies-from.js +25 -0
- package/src/scripts/ips.js +26 -0
- package/src/scripts/iso.js +24 -0
- package/src/scripts/killni.js +23 -0
- package/src/scripts/ll.js +24 -0
- package/src/scripts/local-ip.js +23 -0
- package/src/scripts/m.js +24 -0
- package/src/scripts/map.js +24 -0
- package/src/scripts/mkd.js +24 -0
- package/src/scripts/ncu-update-all.js +24 -0
- package/src/scripts/nginx-init.js +28 -0
- package/src/scripts/npmi.js +23 -0
- package/src/scripts/o.js +24 -0
- package/src/scripts/org-by-date.js +24 -0
- package/src/scripts/p.js +23 -0
- package/src/scripts/packages.js +25 -0
- package/src/scripts/path.js +23 -0
- package/src/scripts/ports.js +23 -0
- package/src/scripts/q.js +23 -0
- package/src/scripts/refresh-files.js +26 -0
- package/src/scripts/remove-smaller-files.js +24 -0
- package/src/scripts/rename-files-with-date.js +25 -0
- package/src/scripts/resize-image.js +25 -0
- package/src/scripts/rm-safe.js +24 -0
- package/src/scripts/s.js +24 -0
- package/src/scripts/set-git-public.js +23 -0
- package/src/scripts/show-desktop-icons.js +23 -0
- package/src/scripts/show-hidden-files.js +23 -0
- package/src/scripts/tpa.js +23 -0
- package/src/scripts/tpo.js +23 -0
- package/src/scripts/u.js +23 -0
- package/src/scripts/vpush.js +23 -0
- package/src/scripts/y.js +23 -0
- package/src/utils/README.md +95 -0
- package/src/utils/common/apps.js +143 -0
- package/src/utils/common/display.js +157 -0
- package/src/utils/common/network.js +185 -0
- package/src/utils/common/os.js +202 -0
- package/src/utils/common/package-manager.js +301 -0
- package/src/utils/common/privileges.js +138 -0
- package/src/utils/common/shell.js +195 -0
- package/src/utils/macos/apps.js +228 -0
- package/src/utils/macos/brew.js +315 -0
- package/src/utils/ubuntu/apt.js +301 -0
- package/src/utils/ubuntu/desktop.js +292 -0
- package/src/utils/ubuntu/snap.js +302 -0
- package/src/utils/ubuntu/systemd.js +286 -0
- package/src/utils/windows/choco.js +327 -0
- package/src/utils/windows/env.js +246 -0
- package/src/utils/windows/registry.js +269 -0
- package/src/utils/windows/shell.js +240 -0
- package/src/utils/windows/winget.js +378 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* macOS Application Detection Utilities
|
|
5
|
+
*
|
|
6
|
+
* macOS-specific utilities for detecting installed GUI applications.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const os = require('os');
|
|
12
|
+
const shell = require('../common/shell');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Standard application directories on macOS
|
|
16
|
+
*/
|
|
17
|
+
const APP_DIRECTORIES = [
|
|
18
|
+
'/Applications',
|
|
19
|
+
path.join(os.homedir(), 'Applications')
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Checks if an app exists in /Applications or ~/Applications
|
|
24
|
+
* @param {string} appName - The application name (with or without .app extension)
|
|
25
|
+
* @returns {boolean}
|
|
26
|
+
*/
|
|
27
|
+
function isAppInstalled(appName) {
|
|
28
|
+
const bundlePath = getAppBundlePath(appName);
|
|
29
|
+
return bundlePath !== null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Returns the full path to an .app bundle
|
|
34
|
+
* @param {string} appName - The application name (with or without .app extension)
|
|
35
|
+
* @returns {string|null}
|
|
36
|
+
*/
|
|
37
|
+
function getAppBundlePath(appName) {
|
|
38
|
+
// Normalize app name - add .app if not present
|
|
39
|
+
const normalizedName = appName.endsWith('.app') ? appName : `${appName}.app`;
|
|
40
|
+
|
|
41
|
+
// Also try common variations
|
|
42
|
+
const variations = [
|
|
43
|
+
normalizedName,
|
|
44
|
+
// Handle cases like "Visual Studio Code" vs "Visual Studio Code.app"
|
|
45
|
+
appName.replace(/\.app$/, '') + '.app'
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
// Special case mappings for common apps
|
|
49
|
+
const appMappings = {
|
|
50
|
+
'vscode': 'Visual Studio Code.app',
|
|
51
|
+
'code': 'Visual Studio Code.app',
|
|
52
|
+
'visual-studio-code': 'Visual Studio Code.app',
|
|
53
|
+
'chrome': 'Google Chrome.app',
|
|
54
|
+
'google-chrome': 'Google Chrome.app',
|
|
55
|
+
'firefox': 'Firefox.app',
|
|
56
|
+
'slack': 'Slack.app',
|
|
57
|
+
'spotify': 'Spotify.app',
|
|
58
|
+
'iterm': 'iTerm.app',
|
|
59
|
+
'iterm2': 'iTerm.app'
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const lowercaseName = appName.toLowerCase().replace(/\.app$/, '');
|
|
63
|
+
if (appMappings[lowercaseName]) {
|
|
64
|
+
variations.push(appMappings[lowercaseName]);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
for (const appDir of APP_DIRECTORIES) {
|
|
68
|
+
for (const variant of variations) {
|
|
69
|
+
const fullPath = path.join(appDir, variant);
|
|
70
|
+
if (fs.existsSync(fullPath)) {
|
|
71
|
+
return fullPath;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Reads version from an app's Info.plist
|
|
81
|
+
* @param {string} appName - The application name
|
|
82
|
+
* @returns {string|null}
|
|
83
|
+
*/
|
|
84
|
+
function getAppVersion(appName) {
|
|
85
|
+
const bundlePath = getAppBundlePath(appName);
|
|
86
|
+
if (!bundlePath) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const infoPlistPath = path.join(bundlePath, 'Contents', 'Info.plist');
|
|
91
|
+
if (!fs.existsSync(infoPlistPath)) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
// Use PlistBuddy to read the version (more reliable than parsing XML)
|
|
97
|
+
const result = shell.execSync(
|
|
98
|
+
`/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "${infoPlistPath}"`
|
|
99
|
+
);
|
|
100
|
+
if (result) {
|
|
101
|
+
return result.trim();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Fallback: try CFBundleVersion
|
|
105
|
+
const bundleVersion = shell.execSync(
|
|
106
|
+
`/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "${infoPlistPath}"`
|
|
107
|
+
);
|
|
108
|
+
if (bundleVersion) {
|
|
109
|
+
return bundleVersion.trim();
|
|
110
|
+
}
|
|
111
|
+
} catch {
|
|
112
|
+
// Fallback: try to parse the plist directly
|
|
113
|
+
try {
|
|
114
|
+
const plistContent = fs.readFileSync(infoPlistPath, 'utf8');
|
|
115
|
+
const versionMatch = plistContent.match(
|
|
116
|
+
/<key>CFBundleShortVersionString<\/key>\s*<string>([^<]+)<\/string>/
|
|
117
|
+
);
|
|
118
|
+
if (versionMatch) {
|
|
119
|
+
return versionMatch[1];
|
|
120
|
+
}
|
|
121
|
+
} catch {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Returns a list of installed GUI applications
|
|
131
|
+
* @returns {string[]}
|
|
132
|
+
*/
|
|
133
|
+
function listInstalledApps() {
|
|
134
|
+
const apps = [];
|
|
135
|
+
|
|
136
|
+
for (const appDir of APP_DIRECTORIES) {
|
|
137
|
+
if (!fs.existsSync(appDir)) {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
const entries = fs.readdirSync(appDir);
|
|
143
|
+
for (const entry of entries) {
|
|
144
|
+
if (entry.endsWith('.app')) {
|
|
145
|
+
apps.push(entry.replace(/\.app$/, ''));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
} catch {
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return apps.sort();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Gets the bundle identifier for an application
|
|
158
|
+
* @param {string} appName - The application name
|
|
159
|
+
* @returns {string|null}
|
|
160
|
+
*/
|
|
161
|
+
function getBundleIdentifier(appName) {
|
|
162
|
+
const bundlePath = getAppBundlePath(appName);
|
|
163
|
+
if (!bundlePath) {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const infoPlistPath = path.join(bundlePath, 'Contents', 'Info.plist');
|
|
168
|
+
if (!fs.existsSync(infoPlistPath)) {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
const result = shell.execSync(
|
|
174
|
+
`/usr/libexec/PlistBuddy -c "Print CFBundleIdentifier" "${infoPlistPath}"`
|
|
175
|
+
);
|
|
176
|
+
return result ? result.trim() : null;
|
|
177
|
+
} catch {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Opens an application
|
|
184
|
+
* @param {string} appName - The application name
|
|
185
|
+
* @returns {Promise<boolean>}
|
|
186
|
+
*/
|
|
187
|
+
async function openApp(appName) {
|
|
188
|
+
const bundlePath = getAppBundlePath(appName);
|
|
189
|
+
if (!bundlePath) {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const result = await shell.exec(`open "${bundlePath}"`);
|
|
194
|
+
return result.code === 0;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Checks if an application is currently running
|
|
199
|
+
* @param {string} appName - The application name
|
|
200
|
+
* @returns {Promise<boolean>}
|
|
201
|
+
*/
|
|
202
|
+
async function isAppRunning(appName) {
|
|
203
|
+
const result = await shell.exec(`pgrep -x "${appName}"`);
|
|
204
|
+
return result.code === 0;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Quits an application gracefully
|
|
209
|
+
* @param {string} appName - The application name
|
|
210
|
+
* @returns {Promise<boolean>}
|
|
211
|
+
*/
|
|
212
|
+
async function quitApp(appName) {
|
|
213
|
+
const result = await shell.exec(
|
|
214
|
+
`osascript -e 'tell application "${appName}" to quit'`
|
|
215
|
+
);
|
|
216
|
+
return result.code === 0;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
module.exports = {
|
|
220
|
+
isAppInstalled,
|
|
221
|
+
getAppBundlePath,
|
|
222
|
+
getAppVersion,
|
|
223
|
+
listInstalledApps,
|
|
224
|
+
getBundleIdentifier,
|
|
225
|
+
openApp,
|
|
226
|
+
isAppRunning,
|
|
227
|
+
quitApp
|
|
228
|
+
};
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Homebrew Package Manager Utilities
|
|
5
|
+
*
|
|
6
|
+
* macOS-specific utilities for interacting with Homebrew.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const shell = require('../common/shell');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Checks if Homebrew is installed and functional
|
|
13
|
+
* @returns {boolean}
|
|
14
|
+
*/
|
|
15
|
+
function isInstalled() {
|
|
16
|
+
return shell.commandExists('brew');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Returns the installed Homebrew version
|
|
21
|
+
* @returns {Promise<string|null>}
|
|
22
|
+
*/
|
|
23
|
+
async function getVersion() {
|
|
24
|
+
if (!isInstalled()) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const result = await shell.exec('brew --version');
|
|
29
|
+
if (result.code === 0) {
|
|
30
|
+
// Output: "Homebrew 4.1.0"
|
|
31
|
+
const match = result.stdout.match(/Homebrew\s+(\d+\.\d+\.?\d*)/);
|
|
32
|
+
return match ? match[1] : null;
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Installs a Homebrew formula (CLI tool)
|
|
39
|
+
* @param {string} formula - The formula name to install
|
|
40
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
41
|
+
*/
|
|
42
|
+
async function install(formula) {
|
|
43
|
+
if (!isInstalled()) {
|
|
44
|
+
return {
|
|
45
|
+
success: false,
|
|
46
|
+
output: 'Homebrew is not installed'
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const result = await shell.exec(`brew install ${formula}`);
|
|
51
|
+
return {
|
|
52
|
+
success: result.code === 0,
|
|
53
|
+
output: result.stdout || result.stderr
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Installs a Homebrew cask (GUI application)
|
|
59
|
+
* @param {string} cask - The cask name to install
|
|
60
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
61
|
+
*/
|
|
62
|
+
async function installCask(cask) {
|
|
63
|
+
if (!isInstalled()) {
|
|
64
|
+
return {
|
|
65
|
+
success: false,
|
|
66
|
+
output: 'Homebrew is not installed'
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const result = await shell.exec(`brew install --cask ${cask}`);
|
|
71
|
+
return {
|
|
72
|
+
success: result.code === 0,
|
|
73
|
+
output: result.stdout || result.stderr
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Removes a Homebrew formula
|
|
79
|
+
* @param {string} formula - The formula name to remove
|
|
80
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
81
|
+
*/
|
|
82
|
+
async function uninstall(formula) {
|
|
83
|
+
if (!isInstalled()) {
|
|
84
|
+
return {
|
|
85
|
+
success: false,
|
|
86
|
+
output: 'Homebrew is not installed'
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const result = await shell.exec(`brew uninstall ${formula}`);
|
|
91
|
+
return {
|
|
92
|
+
success: result.code === 0,
|
|
93
|
+
output: result.stdout || result.stderr
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Removes a Homebrew cask
|
|
99
|
+
* @param {string} cask - The cask name to remove
|
|
100
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
101
|
+
*/
|
|
102
|
+
async function uninstallCask(cask) {
|
|
103
|
+
if (!isInstalled()) {
|
|
104
|
+
return {
|
|
105
|
+
success: false,
|
|
106
|
+
output: 'Homebrew is not installed'
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const result = await shell.exec(`brew uninstall --cask ${cask}`);
|
|
111
|
+
return {
|
|
112
|
+
success: result.code === 0,
|
|
113
|
+
output: result.stdout || result.stderr
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Checks if a specific formula is installed
|
|
119
|
+
* @param {string} formula - The formula name to check
|
|
120
|
+
* @returns {Promise<boolean>}
|
|
121
|
+
*/
|
|
122
|
+
async function isFormulaInstalled(formula) {
|
|
123
|
+
if (!isInstalled()) {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const result = await shell.exec(`brew list --formula ${formula}`);
|
|
128
|
+
return result.code === 0;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Checks if a specific cask is installed
|
|
133
|
+
* @param {string} cask - The cask name to check
|
|
134
|
+
* @returns {Promise<boolean>}
|
|
135
|
+
*/
|
|
136
|
+
async function isCaskInstalled(cask) {
|
|
137
|
+
if (!isInstalled()) {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const result = await shell.exec(`brew list --cask ${cask}`);
|
|
142
|
+
return result.code === 0;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Updates Homebrew itself and package lists
|
|
147
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
148
|
+
*/
|
|
149
|
+
async function update() {
|
|
150
|
+
if (!isInstalled()) {
|
|
151
|
+
return {
|
|
152
|
+
success: false,
|
|
153
|
+
output: 'Homebrew is not installed'
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const result = await shell.exec('brew update');
|
|
158
|
+
return {
|
|
159
|
+
success: result.code === 0,
|
|
160
|
+
output: result.stdout || result.stderr
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Upgrades a specific formula or all outdated formulas
|
|
166
|
+
* @param {string} [formula] - The formula to upgrade (all if omitted)
|
|
167
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
168
|
+
*/
|
|
169
|
+
async function upgrade(formula) {
|
|
170
|
+
if (!isInstalled()) {
|
|
171
|
+
return {
|
|
172
|
+
success: false,
|
|
173
|
+
output: 'Homebrew is not installed'
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const command = formula ? `brew upgrade ${formula}` : 'brew upgrade';
|
|
178
|
+
const result = await shell.exec(command);
|
|
179
|
+
return {
|
|
180
|
+
success: result.code === 0,
|
|
181
|
+
output: result.stdout || result.stderr
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Adds a third-party Homebrew tap
|
|
187
|
+
* @param {string} repository - The tap repository (e.g., 'homebrew/cask-fonts')
|
|
188
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
189
|
+
*/
|
|
190
|
+
async function tap(repository) {
|
|
191
|
+
if (!isInstalled()) {
|
|
192
|
+
return {
|
|
193
|
+
success: false,
|
|
194
|
+
output: 'Homebrew is not installed'
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const result = await shell.exec(`brew tap ${repository}`);
|
|
199
|
+
return {
|
|
200
|
+
success: result.code === 0,
|
|
201
|
+
output: result.stdout || result.stderr
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Searches for formulas/casks matching query
|
|
207
|
+
* @param {string} query - The search query
|
|
208
|
+
* @returns {Promise<{ formulas: string[], casks: string[] }>}
|
|
209
|
+
*/
|
|
210
|
+
async function search(query) {
|
|
211
|
+
if (!isInstalled()) {
|
|
212
|
+
return { formulas: [], casks: [] };
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const result = await shell.exec(`brew search ${query}`);
|
|
216
|
+
if (result.code !== 0) {
|
|
217
|
+
return { formulas: [], casks: [] };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const lines = result.stdout.split('\n').filter(Boolean);
|
|
221
|
+
const formulas = [];
|
|
222
|
+
const casks = [];
|
|
223
|
+
let inCasks = false;
|
|
224
|
+
|
|
225
|
+
for (const line of lines) {
|
|
226
|
+
if (line.includes('==> Formulae')) {
|
|
227
|
+
inCasks = false;
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
if (line.includes('==> Casks')) {
|
|
231
|
+
inCasks = true;
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
if (line.startsWith('==>')) {
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Each line may contain multiple space-separated package names
|
|
239
|
+
const packages = line.trim().split(/\s+/).filter(Boolean);
|
|
240
|
+
if (inCasks) {
|
|
241
|
+
casks.push(...packages);
|
|
242
|
+
} else {
|
|
243
|
+
formulas.push(...packages);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return { formulas, casks };
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Gets information about a formula or cask
|
|
252
|
+
* @param {string} name - The formula or cask name
|
|
253
|
+
* @returns {Promise<string|null>}
|
|
254
|
+
*/
|
|
255
|
+
async function info(name) {
|
|
256
|
+
if (!isInstalled()) {
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const result = await shell.exec(`brew info ${name}`);
|
|
261
|
+
if (result.code === 0) {
|
|
262
|
+
return result.stdout;
|
|
263
|
+
}
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Lists all installed formulas
|
|
269
|
+
* @returns {Promise<string[]>}
|
|
270
|
+
*/
|
|
271
|
+
async function listFormulas() {
|
|
272
|
+
if (!isInstalled()) {
|
|
273
|
+
return [];
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const result = await shell.exec('brew list --formula');
|
|
277
|
+
if (result.code === 0) {
|
|
278
|
+
return result.stdout.split('\n').filter(Boolean);
|
|
279
|
+
}
|
|
280
|
+
return [];
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Lists all installed casks
|
|
285
|
+
* @returns {Promise<string[]>}
|
|
286
|
+
*/
|
|
287
|
+
async function listCasks() {
|
|
288
|
+
if (!isInstalled()) {
|
|
289
|
+
return [];
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const result = await shell.exec('brew list --cask');
|
|
293
|
+
if (result.code === 0) {
|
|
294
|
+
return result.stdout.split('\n').filter(Boolean);
|
|
295
|
+
}
|
|
296
|
+
return [];
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
module.exports = {
|
|
300
|
+
isInstalled,
|
|
301
|
+
getVersion,
|
|
302
|
+
install,
|
|
303
|
+
installCask,
|
|
304
|
+
uninstall,
|
|
305
|
+
uninstallCask,
|
|
306
|
+
isFormulaInstalled,
|
|
307
|
+
isCaskInstalled,
|
|
308
|
+
update,
|
|
309
|
+
upgrade,
|
|
310
|
+
tap,
|
|
311
|
+
search,
|
|
312
|
+
info,
|
|
313
|
+
listFormulas,
|
|
314
|
+
listCasks
|
|
315
|
+
};
|