@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,301 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Package Manager Abstraction Utilities
|
|
5
|
+
*
|
|
6
|
+
* Cross-platform abstraction layer for package managers.
|
|
7
|
+
* Provides a unified interface for installing packages across different platforms.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const osUtils = require('./os');
|
|
11
|
+
const shell = require('./shell');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Returns list of available package managers on current system
|
|
15
|
+
* @returns {Promise<string[]>}
|
|
16
|
+
*/
|
|
17
|
+
async function getAvailable() {
|
|
18
|
+
const available = [];
|
|
19
|
+
const platform = osUtils.detect();
|
|
20
|
+
|
|
21
|
+
switch (platform.type) {
|
|
22
|
+
case 'macos':
|
|
23
|
+
if (shell.commandExists('brew')) {
|
|
24
|
+
available.push('brew');
|
|
25
|
+
}
|
|
26
|
+
break;
|
|
27
|
+
|
|
28
|
+
case 'ubuntu':
|
|
29
|
+
case 'debian':
|
|
30
|
+
case 'raspbian':
|
|
31
|
+
if (shell.commandExists('apt-get')) {
|
|
32
|
+
available.push('apt');
|
|
33
|
+
}
|
|
34
|
+
if (shell.commandExists('snap')) {
|
|
35
|
+
available.push('snap');
|
|
36
|
+
}
|
|
37
|
+
break;
|
|
38
|
+
|
|
39
|
+
case 'amazon_linux':
|
|
40
|
+
case 'rhel':
|
|
41
|
+
case 'fedora':
|
|
42
|
+
if (shell.commandExists('dnf')) {
|
|
43
|
+
available.push('dnf');
|
|
44
|
+
}
|
|
45
|
+
if (shell.commandExists('yum')) {
|
|
46
|
+
available.push('yum');
|
|
47
|
+
}
|
|
48
|
+
break;
|
|
49
|
+
|
|
50
|
+
case 'windows':
|
|
51
|
+
if (shell.commandExists('winget')) {
|
|
52
|
+
available.push('winget');
|
|
53
|
+
}
|
|
54
|
+
if (shell.commandExists('choco')) {
|
|
55
|
+
available.push('choco');
|
|
56
|
+
}
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Universal package managers
|
|
61
|
+
if (shell.commandExists('npm')) {
|
|
62
|
+
available.push('npm');
|
|
63
|
+
}
|
|
64
|
+
if (shell.commandExists('pip') || shell.commandExists('pip3')) {
|
|
65
|
+
available.push('pip');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return available;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Returns the preferred/recommended package manager for current platform
|
|
73
|
+
* @returns {string|null}
|
|
74
|
+
*/
|
|
75
|
+
function getPreferred() {
|
|
76
|
+
const platform = osUtils.detect();
|
|
77
|
+
|
|
78
|
+
switch (platform.type) {
|
|
79
|
+
case 'macos':
|
|
80
|
+
return 'brew';
|
|
81
|
+
case 'ubuntu':
|
|
82
|
+
case 'debian':
|
|
83
|
+
case 'raspbian':
|
|
84
|
+
return 'apt';
|
|
85
|
+
case 'amazon_linux':
|
|
86
|
+
case 'rhel':
|
|
87
|
+
case 'fedora':
|
|
88
|
+
return platform.packageManager; // dnf or yum
|
|
89
|
+
case 'windows':
|
|
90
|
+
return shell.commandExists('winget') ? 'winget' : 'choco';
|
|
91
|
+
default:
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Installs a package using the best available package manager
|
|
98
|
+
* @param {string} packageName - The package to install
|
|
99
|
+
* @param {Object} [options] - Installation options
|
|
100
|
+
* @param {string} [options.manager] - Force a specific package manager
|
|
101
|
+
* @param {boolean} [options.global] - Install globally (for npm/pip)
|
|
102
|
+
* @param {boolean} [options.cask] - Install as cask (macOS only)
|
|
103
|
+
* @param {boolean} [options.classic] - Use classic confinement (snap only)
|
|
104
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
105
|
+
*/
|
|
106
|
+
async function install(packageName, options = {}) {
|
|
107
|
+
const manager = options.manager || getPreferred();
|
|
108
|
+
|
|
109
|
+
if (!manager) {
|
|
110
|
+
return {
|
|
111
|
+
success: false,
|
|
112
|
+
output: 'No package manager available'
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
let command;
|
|
117
|
+
|
|
118
|
+
switch (manager) {
|
|
119
|
+
case 'brew':
|
|
120
|
+
if (options.cask) {
|
|
121
|
+
command = `brew install --cask ${packageName}`;
|
|
122
|
+
} else {
|
|
123
|
+
command = `brew install ${packageName}`;
|
|
124
|
+
}
|
|
125
|
+
break;
|
|
126
|
+
|
|
127
|
+
case 'apt':
|
|
128
|
+
command = `sudo apt-get install -y ${packageName}`;
|
|
129
|
+
break;
|
|
130
|
+
|
|
131
|
+
case 'snap':
|
|
132
|
+
if (options.classic) {
|
|
133
|
+
command = `sudo snap install ${packageName} --classic`;
|
|
134
|
+
} else {
|
|
135
|
+
command = `sudo snap install ${packageName}`;
|
|
136
|
+
}
|
|
137
|
+
break;
|
|
138
|
+
|
|
139
|
+
case 'dnf':
|
|
140
|
+
command = `sudo dnf install -y ${packageName}`;
|
|
141
|
+
break;
|
|
142
|
+
|
|
143
|
+
case 'yum':
|
|
144
|
+
command = `sudo yum install -y ${packageName}`;
|
|
145
|
+
break;
|
|
146
|
+
|
|
147
|
+
case 'winget':
|
|
148
|
+
command = `winget install --accept-package-agreements --accept-source-agreements ${packageName}`;
|
|
149
|
+
break;
|
|
150
|
+
|
|
151
|
+
case 'choco':
|
|
152
|
+
command = `choco install -y ${packageName}`;
|
|
153
|
+
break;
|
|
154
|
+
|
|
155
|
+
case 'npm':
|
|
156
|
+
if (options.global) {
|
|
157
|
+
command = `npm install -g ${packageName}`;
|
|
158
|
+
} else {
|
|
159
|
+
command = `npm install ${packageName}`;
|
|
160
|
+
}
|
|
161
|
+
break;
|
|
162
|
+
|
|
163
|
+
case 'pip':
|
|
164
|
+
const pip = shell.commandExists('pip3') ? 'pip3' : 'pip';
|
|
165
|
+
if (options.global) {
|
|
166
|
+
command = `${pip} install ${packageName}`;
|
|
167
|
+
} else {
|
|
168
|
+
command = `${pip} install --user ${packageName}`;
|
|
169
|
+
}
|
|
170
|
+
break;
|
|
171
|
+
|
|
172
|
+
default:
|
|
173
|
+
return {
|
|
174
|
+
success: false,
|
|
175
|
+
output: `Unknown package manager: ${manager}`
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const result = await shell.exec(command);
|
|
180
|
+
return {
|
|
181
|
+
success: result.code === 0,
|
|
182
|
+
output: result.stdout || result.stderr
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Uninstalls a package
|
|
188
|
+
* @param {string} packageName - The package to uninstall
|
|
189
|
+
* @param {Object} [options] - Uninstallation options
|
|
190
|
+
* @param {string} [options.manager] - Force a specific package manager
|
|
191
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
192
|
+
*/
|
|
193
|
+
async function uninstall(packageName, options = {}) {
|
|
194
|
+
const manager = options.manager || getPreferred();
|
|
195
|
+
|
|
196
|
+
if (!manager) {
|
|
197
|
+
return {
|
|
198
|
+
success: false,
|
|
199
|
+
output: 'No package manager available'
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
let command;
|
|
204
|
+
|
|
205
|
+
switch (manager) {
|
|
206
|
+
case 'brew':
|
|
207
|
+
command = `brew uninstall ${packageName}`;
|
|
208
|
+
break;
|
|
209
|
+
|
|
210
|
+
case 'apt':
|
|
211
|
+
command = `sudo apt-get remove -y ${packageName}`;
|
|
212
|
+
break;
|
|
213
|
+
|
|
214
|
+
case 'snap':
|
|
215
|
+
command = `sudo snap remove ${packageName}`;
|
|
216
|
+
break;
|
|
217
|
+
|
|
218
|
+
case 'dnf':
|
|
219
|
+
command = `sudo dnf remove -y ${packageName}`;
|
|
220
|
+
break;
|
|
221
|
+
|
|
222
|
+
case 'yum':
|
|
223
|
+
command = `sudo yum remove -y ${packageName}`;
|
|
224
|
+
break;
|
|
225
|
+
|
|
226
|
+
case 'winget':
|
|
227
|
+
command = `winget uninstall ${packageName}`;
|
|
228
|
+
break;
|
|
229
|
+
|
|
230
|
+
case 'choco':
|
|
231
|
+
command = `choco uninstall -y ${packageName}`;
|
|
232
|
+
break;
|
|
233
|
+
|
|
234
|
+
default:
|
|
235
|
+
return {
|
|
236
|
+
success: false,
|
|
237
|
+
output: `Unknown package manager: ${manager}`
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const result = await shell.exec(command);
|
|
242
|
+
return {
|
|
243
|
+
success: result.code === 0,
|
|
244
|
+
output: result.stdout || result.stderr
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Updates package lists
|
|
250
|
+
* @param {string} [manager] - Specific package manager to update
|
|
251
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
252
|
+
*/
|
|
253
|
+
async function update(manager) {
|
|
254
|
+
const pm = manager || getPreferred();
|
|
255
|
+
|
|
256
|
+
let command;
|
|
257
|
+
|
|
258
|
+
switch (pm) {
|
|
259
|
+
case 'brew':
|
|
260
|
+
command = 'brew update';
|
|
261
|
+
break;
|
|
262
|
+
|
|
263
|
+
case 'apt':
|
|
264
|
+
command = 'sudo apt-get update';
|
|
265
|
+
break;
|
|
266
|
+
|
|
267
|
+
case 'dnf':
|
|
268
|
+
command = 'sudo dnf check-update';
|
|
269
|
+
break;
|
|
270
|
+
|
|
271
|
+
case 'yum':
|
|
272
|
+
command = 'sudo yum check-update';
|
|
273
|
+
break;
|
|
274
|
+
|
|
275
|
+
case 'choco':
|
|
276
|
+
command = 'choco upgrade chocolatey -y';
|
|
277
|
+
break;
|
|
278
|
+
|
|
279
|
+
default:
|
|
280
|
+
return {
|
|
281
|
+
success: false,
|
|
282
|
+
output: `Update not supported for: ${pm}`
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const result = await shell.exec(command);
|
|
287
|
+
// dnf/yum check-update returns 100 if updates are available, not an error
|
|
288
|
+
const success = result.code === 0 || (pm === 'dnf' || pm === 'yum') && result.code === 100;
|
|
289
|
+
return {
|
|
290
|
+
success,
|
|
291
|
+
output: result.stdout || result.stderr
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
module.exports = {
|
|
296
|
+
getAvailable,
|
|
297
|
+
getPreferred,
|
|
298
|
+
install,
|
|
299
|
+
uninstall,
|
|
300
|
+
update
|
|
301
|
+
};
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Permission and Elevation Checking Utilities
|
|
5
|
+
*
|
|
6
|
+
* Platform-agnostic utilities for checking elevated privileges.
|
|
7
|
+
* Handles root on Unix and Administrator on Windows internally.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const os = require('os');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Checks if running with elevated privileges
|
|
14
|
+
* On Unix: checks if running as root (uid 0)
|
|
15
|
+
* On Windows: checks if running as Administrator
|
|
16
|
+
* @returns {boolean}
|
|
17
|
+
*/
|
|
18
|
+
function isElevated() {
|
|
19
|
+
if (process.platform === 'win32') {
|
|
20
|
+
return isWindowsAdmin();
|
|
21
|
+
}
|
|
22
|
+
return isUnixRoot();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Checks if running as root on Unix systems
|
|
27
|
+
* @returns {boolean}
|
|
28
|
+
*/
|
|
29
|
+
function isUnixRoot() {
|
|
30
|
+
if (process.platform === 'win32') {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
return process.getuid && process.getuid() === 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Checks if running as Administrator on Windows
|
|
38
|
+
* Uses a heuristic since Node.js doesn't have direct API for this
|
|
39
|
+
* @returns {boolean}
|
|
40
|
+
*/
|
|
41
|
+
function isWindowsAdmin() {
|
|
42
|
+
if (process.platform !== 'win32') {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Method 1: Try to read a system-protected location
|
|
47
|
+
const fs = require('fs');
|
|
48
|
+
try {
|
|
49
|
+
// Try to list the Windows system config directory
|
|
50
|
+
// Only administrators can access this
|
|
51
|
+
fs.readdirSync('C:\\Windows\\System32\\config');
|
|
52
|
+
return true;
|
|
53
|
+
} catch {
|
|
54
|
+
// Not admin or access denied
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Method 2: Check for common admin environment indicators
|
|
58
|
+
// This is less reliable but can be a fallback
|
|
59
|
+
if (process.env.ADMIN_MODE === 'true') {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Determines if a given operation type requires elevation
|
|
68
|
+
* @param {string} operation - The operation type
|
|
69
|
+
* @returns {boolean}
|
|
70
|
+
*/
|
|
71
|
+
function requiresElevation(operation) {
|
|
72
|
+
const elevatedOperations = [
|
|
73
|
+
'install_system_package',
|
|
74
|
+
'modify_system_config',
|
|
75
|
+
'start_system_service',
|
|
76
|
+
'modify_hosts_file',
|
|
77
|
+
'install_global_npm',
|
|
78
|
+
'modify_registry',
|
|
79
|
+
'add_apt_repository',
|
|
80
|
+
'enable_systemd_service'
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
return elevatedOperations.includes(operation);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Gets the username of the current user
|
|
88
|
+
* @returns {string}
|
|
89
|
+
*/
|
|
90
|
+
function getCurrentUser() {
|
|
91
|
+
return os.userInfo().username;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Checks if the current user can write to a directory
|
|
96
|
+
* @param {string} dirPath - The directory path to check
|
|
97
|
+
* @returns {boolean}
|
|
98
|
+
*/
|
|
99
|
+
function canWriteToDirectory(dirPath) {
|
|
100
|
+
const fs = require('fs');
|
|
101
|
+
const path = require('path');
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
// Try to create a temporary file
|
|
105
|
+
const testFile = path.join(dirPath, `.write-test-${Date.now()}`);
|
|
106
|
+
fs.writeFileSync(testFile, '');
|
|
107
|
+
fs.unlinkSync(testFile);
|
|
108
|
+
return true;
|
|
109
|
+
} catch {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Checks if the current user can execute a file
|
|
116
|
+
* @param {string} filePath - The file path to check
|
|
117
|
+
* @returns {boolean}
|
|
118
|
+
*/
|
|
119
|
+
function canExecute(filePath) {
|
|
120
|
+
const fs = require('fs');
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
fs.accessSync(filePath, fs.constants.X_OK);
|
|
124
|
+
return true;
|
|
125
|
+
} catch {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
module.exports = {
|
|
131
|
+
isElevated,
|
|
132
|
+
isUnixRoot,
|
|
133
|
+
isWindowsAdmin,
|
|
134
|
+
requiresElevation,
|
|
135
|
+
getCurrentUser,
|
|
136
|
+
canWriteToDirectory,
|
|
137
|
+
canExecute
|
|
138
|
+
};
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shell Command Execution Utilities
|
|
5
|
+
*
|
|
6
|
+
* Platform-agnostic utilities for executing shell commands. Uses Node.js
|
|
7
|
+
* child_process APIs that work identically on all platforms. Note that while
|
|
8
|
+
* these utilities are cross-platform, the commands passed to them may be
|
|
9
|
+
* platform-specific.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const { exec: cpExec, execSync: cpExecSync, spawn } = require('child_process');
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Executes a shell command asynchronously
|
|
18
|
+
* @param {string} command - The command to execute
|
|
19
|
+
* @param {Object} [options] - Options for child_process.exec
|
|
20
|
+
* @param {string} [options.cwd] - Working directory
|
|
21
|
+
* @param {Object} [options.env] - Environment variables
|
|
22
|
+
* @param {number} [options.timeout] - Timeout in milliseconds
|
|
23
|
+
* @param {string} [options.encoding] - Output encoding (default: 'utf8')
|
|
24
|
+
* @returns {Promise<{ stdout: string, stderr: string, code: number }>}
|
|
25
|
+
*/
|
|
26
|
+
async function exec(command, options = {}) {
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
const opts = {
|
|
29
|
+
encoding: options.encoding || 'utf8',
|
|
30
|
+
cwd: options.cwd,
|
|
31
|
+
env: options.env || process.env,
|
|
32
|
+
timeout: options.timeout,
|
|
33
|
+
maxBuffer: options.maxBuffer || 10 * 1024 * 1024 // 10MB default
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
cpExec(command, opts, (error, stdout, stderr) => {
|
|
37
|
+
if (error) {
|
|
38
|
+
resolve({
|
|
39
|
+
stdout: stdout || '',
|
|
40
|
+
stderr: stderr || error.message,
|
|
41
|
+
code: error.code || 1
|
|
42
|
+
});
|
|
43
|
+
} else {
|
|
44
|
+
resolve({
|
|
45
|
+
stdout: stdout || '',
|
|
46
|
+
stderr: stderr || '',
|
|
47
|
+
code: 0
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Executes a shell command synchronously
|
|
56
|
+
* @param {string} command - The command to execute
|
|
57
|
+
* @param {Object} [options] - Options for child_process.execSync
|
|
58
|
+
* @param {string} [options.cwd] - Working directory
|
|
59
|
+
* @param {Object} [options.env] - Environment variables
|
|
60
|
+
* @param {number} [options.timeout] - Timeout in milliseconds
|
|
61
|
+
* @param {string} [options.encoding] - Output encoding (default: 'utf8')
|
|
62
|
+
* @returns {string} - stdout as string, or empty string on error
|
|
63
|
+
*/
|
|
64
|
+
function execSync(command, options = {}) {
|
|
65
|
+
try {
|
|
66
|
+
const opts = {
|
|
67
|
+
encoding: options.encoding || 'utf8',
|
|
68
|
+
cwd: options.cwd,
|
|
69
|
+
env: options.env || process.env,
|
|
70
|
+
timeout: options.timeout,
|
|
71
|
+
maxBuffer: options.maxBuffer || 10 * 1024 * 1024, // 10MB default
|
|
72
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
return cpExecSync(command, opts).toString().trim();
|
|
76
|
+
} catch (err) {
|
|
77
|
+
return '';
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Locates an executable in PATH (pure Node.js implementation)
|
|
83
|
+
* @param {string} executable - The executable name to find
|
|
84
|
+
* @returns {string|null} - Full path to executable, or null if not found
|
|
85
|
+
*/
|
|
86
|
+
function which(executable) {
|
|
87
|
+
const isWindows = process.platform === 'win32';
|
|
88
|
+
const pathSeparator = isWindows ? ';' : ':';
|
|
89
|
+
const pathEnv = process.env.PATH || '';
|
|
90
|
+
const paths = pathEnv.split(pathSeparator);
|
|
91
|
+
|
|
92
|
+
// On Windows, also check PATHEXT for executable extensions
|
|
93
|
+
const pathExt = isWindows
|
|
94
|
+
? (process.env.PATHEXT || '.COM;.EXE;.BAT;.CMD').split(';')
|
|
95
|
+
: [''];
|
|
96
|
+
|
|
97
|
+
for (const dir of paths) {
|
|
98
|
+
if (!dir) continue;
|
|
99
|
+
|
|
100
|
+
for (const ext of pathExt) {
|
|
101
|
+
const fullPath = path.join(dir, executable + ext);
|
|
102
|
+
try {
|
|
103
|
+
const stat = fs.statSync(fullPath);
|
|
104
|
+
if (stat.isFile()) {
|
|
105
|
+
// On Unix, check if file is executable
|
|
106
|
+
if (!isWindows) {
|
|
107
|
+
try {
|
|
108
|
+
fs.accessSync(fullPath, fs.constants.X_OK);
|
|
109
|
+
return fullPath;
|
|
110
|
+
} catch {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return fullPath;
|
|
115
|
+
}
|
|
116
|
+
} catch {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Checks if a command is available in PATH
|
|
127
|
+
* @param {string} command - The command to check
|
|
128
|
+
* @returns {boolean}
|
|
129
|
+
*/
|
|
130
|
+
function commandExists(command) {
|
|
131
|
+
return which(command) !== null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Spawns a process with streaming output
|
|
136
|
+
* @param {string} command - The command to run
|
|
137
|
+
* @param {string[]} [args] - Arguments array
|
|
138
|
+
* @param {Object} [options] - Spawn options
|
|
139
|
+
* @returns {Promise<{ stdout: string, stderr: string, code: number }>}
|
|
140
|
+
*/
|
|
141
|
+
async function spawnAsync(command, args = [], options = {}) {
|
|
142
|
+
return new Promise((resolve, reject) => {
|
|
143
|
+
const opts = {
|
|
144
|
+
cwd: options.cwd,
|
|
145
|
+
env: options.env || process.env,
|
|
146
|
+
shell: options.shell !== false
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const child = spawn(command, args, opts);
|
|
150
|
+
let stdout = '';
|
|
151
|
+
let stderr = '';
|
|
152
|
+
|
|
153
|
+
if (child.stdout) {
|
|
154
|
+
child.stdout.on('data', (data) => {
|
|
155
|
+
stdout += data.toString();
|
|
156
|
+
if (options.onStdout) {
|
|
157
|
+
options.onStdout(data.toString());
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (child.stderr) {
|
|
163
|
+
child.stderr.on('data', (data) => {
|
|
164
|
+
stderr += data.toString();
|
|
165
|
+
if (options.onStderr) {
|
|
166
|
+
options.onStderr(data.toString());
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
child.on('close', (code) => {
|
|
172
|
+
resolve({
|
|
173
|
+
stdout,
|
|
174
|
+
stderr,
|
|
175
|
+
code: code || 0
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
child.on('error', (err) => {
|
|
180
|
+
resolve({
|
|
181
|
+
stdout,
|
|
182
|
+
stderr: stderr || err.message,
|
|
183
|
+
code: 1
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
module.exports = {
|
|
190
|
+
exec,
|
|
191
|
+
execSync,
|
|
192
|
+
which,
|
|
193
|
+
commandExists,
|
|
194
|
+
spawnAsync
|
|
195
|
+
};
|