@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,327 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Chocolatey Package Manager Utilities
|
|
5
|
+
*
|
|
6
|
+
* Windows-specific utilities for interacting with Chocolatey.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const shell = require('../common/shell');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Checks if Chocolatey is installed
|
|
13
|
+
* @returns {boolean}
|
|
14
|
+
*/
|
|
15
|
+
function isInstalled() {
|
|
16
|
+
return shell.commandExists('choco');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Returns the installed Chocolatey 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('choco --version');
|
|
29
|
+
if (result.code === 0) {
|
|
30
|
+
return result.stdout.trim();
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Installs a Chocolatey package
|
|
37
|
+
* @param {string} packageName - The package name to install
|
|
38
|
+
* @param {Object} [options] - Installation options
|
|
39
|
+
* @param {boolean} [options.force=false] - Force reinstall if already installed
|
|
40
|
+
* @param {string} [options.version] - Specific version to install
|
|
41
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
42
|
+
*/
|
|
43
|
+
async function install(packageName, options = {}) {
|
|
44
|
+
if (!isInstalled()) {
|
|
45
|
+
return {
|
|
46
|
+
success: false,
|
|
47
|
+
output: 'Chocolatey is not installed'
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let command = `choco install ${packageName} -y`;
|
|
52
|
+
|
|
53
|
+
if (options.force) {
|
|
54
|
+
command += ' --force';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (options.version) {
|
|
58
|
+
command += ` --version=${options.version}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const result = await shell.exec(command);
|
|
62
|
+
return {
|
|
63
|
+
success: result.code === 0,
|
|
64
|
+
output: result.stdout || result.stderr
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Removes a Chocolatey package
|
|
70
|
+
* @param {string} packageName - The package name to remove
|
|
71
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
72
|
+
*/
|
|
73
|
+
async function uninstall(packageName) {
|
|
74
|
+
if (!isInstalled()) {
|
|
75
|
+
return {
|
|
76
|
+
success: false,
|
|
77
|
+
output: 'Chocolatey is not installed'
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const result = await shell.exec(`choco uninstall ${packageName} -y`);
|
|
82
|
+
return {
|
|
83
|
+
success: result.code === 0,
|
|
84
|
+
output: result.stdout || result.stderr
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Checks if a package is installed via Chocolatey
|
|
90
|
+
* @param {string} packageName - The package name to check
|
|
91
|
+
* @returns {Promise<boolean>}
|
|
92
|
+
*/
|
|
93
|
+
async function isPackageInstalled(packageName) {
|
|
94
|
+
if (!isInstalled()) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const result = await shell.exec(`choco list --local-only --exact ${packageName}`);
|
|
99
|
+
// Output contains the package name if installed
|
|
100
|
+
return result.code === 0 && result.stdout.toLowerCase().includes(packageName.toLowerCase());
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Returns the installed version of a package
|
|
105
|
+
* @param {string} packageName - The package name
|
|
106
|
+
* @returns {Promise<string|null>}
|
|
107
|
+
*/
|
|
108
|
+
async function getPackageVersion(packageName) {
|
|
109
|
+
if (!isInstalled()) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const result = await shell.exec(`choco list --local-only --exact ${packageName}`);
|
|
114
|
+
if (result.code !== 0) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Output format: "packageName version"
|
|
119
|
+
const lines = result.stdout.split('\n').filter(Boolean);
|
|
120
|
+
for (const line of lines) {
|
|
121
|
+
const parts = line.trim().split(/\s+/);
|
|
122
|
+
if (parts[0] && parts[0].toLowerCase() === packageName.toLowerCase()) {
|
|
123
|
+
return parts[1] || null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Upgrades a package or all packages
|
|
132
|
+
* @param {string} [packageName] - The package to upgrade (all if omitted)
|
|
133
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
134
|
+
*/
|
|
135
|
+
async function upgrade(packageName) {
|
|
136
|
+
if (!isInstalled()) {
|
|
137
|
+
return {
|
|
138
|
+
success: false,
|
|
139
|
+
output: 'Chocolatey is not installed'
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const target = packageName || 'all';
|
|
144
|
+
const result = await shell.exec(`choco upgrade ${target} -y`);
|
|
145
|
+
return {
|
|
146
|
+
success: result.code === 0,
|
|
147
|
+
output: result.stdout || result.stderr
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Searches the Chocolatey repository
|
|
153
|
+
* @param {string} query - The search query
|
|
154
|
+
* @returns {Promise<Array<{ name: string, version: string }>>}
|
|
155
|
+
*/
|
|
156
|
+
async function search(query) {
|
|
157
|
+
if (!isInstalled()) {
|
|
158
|
+
return [];
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const result = await shell.exec(`choco search "${query}"`);
|
|
162
|
+
if (result.code !== 0) {
|
|
163
|
+
return [];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const packages = [];
|
|
167
|
+
const lines = result.stdout.split('\n').filter(Boolean);
|
|
168
|
+
|
|
169
|
+
for (const line of lines) {
|
|
170
|
+
// Skip header and summary lines
|
|
171
|
+
if (line.includes('packages found') || line.includes('Chocolatey')) {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const parts = line.trim().split(/\s+/);
|
|
176
|
+
if (parts.length >= 2 && !parts[0].includes('=')) {
|
|
177
|
+
packages.push({
|
|
178
|
+
name: parts[0],
|
|
179
|
+
version: parts[1]
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return packages;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Gets information about a package
|
|
189
|
+
* @param {string} packageName - The package name
|
|
190
|
+
* @returns {Promise<string|null>}
|
|
191
|
+
*/
|
|
192
|
+
async function info(packageName) {
|
|
193
|
+
if (!isInstalled()) {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const result = await shell.exec(`choco info ${packageName}`);
|
|
198
|
+
if (result.code === 0) {
|
|
199
|
+
return result.stdout;
|
|
200
|
+
}
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Lists all locally installed packages
|
|
206
|
+
* @returns {Promise<Array<{ name: string, version: string }>>}
|
|
207
|
+
*/
|
|
208
|
+
async function listInstalled() {
|
|
209
|
+
if (!isInstalled()) {
|
|
210
|
+
return [];
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const result = await shell.exec('choco list --local-only');
|
|
214
|
+
if (result.code !== 0) {
|
|
215
|
+
return [];
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const packages = [];
|
|
219
|
+
const lines = result.stdout.split('\n').filter(Boolean);
|
|
220
|
+
|
|
221
|
+
for (const line of lines) {
|
|
222
|
+
// Skip summary line
|
|
223
|
+
if (line.includes('packages installed')) {
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const parts = line.trim().split(/\s+/);
|
|
228
|
+
if (parts.length >= 2) {
|
|
229
|
+
packages.push({
|
|
230
|
+
name: parts[0],
|
|
231
|
+
version: parts[1]
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return packages;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Lists outdated packages
|
|
241
|
+
* @returns {Promise<Array<{ name: string, currentVersion: string, availableVersion: string }>>}
|
|
242
|
+
*/
|
|
243
|
+
async function listOutdated() {
|
|
244
|
+
if (!isInstalled()) {
|
|
245
|
+
return [];
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const result = await shell.exec('choco outdated');
|
|
249
|
+
if (result.code !== 0) {
|
|
250
|
+
return [];
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const packages = [];
|
|
254
|
+
const lines = result.stdout.split('\n').filter(Boolean);
|
|
255
|
+
|
|
256
|
+
for (const line of lines) {
|
|
257
|
+
// Lines with outdated packages have format: "name|currentVer|availableVer|pinned"
|
|
258
|
+
if (line.includes('|')) {
|
|
259
|
+
const parts = line.split('|');
|
|
260
|
+
if (parts.length >= 3) {
|
|
261
|
+
packages.push({
|
|
262
|
+
name: parts[0].trim(),
|
|
263
|
+
currentVersion: parts[1].trim(),
|
|
264
|
+
availableVersion: parts[2].trim()
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return packages;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Pins a package to prevent upgrades
|
|
275
|
+
* @param {string} packageName - The package name
|
|
276
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
277
|
+
*/
|
|
278
|
+
async function pin(packageName) {
|
|
279
|
+
if (!isInstalled()) {
|
|
280
|
+
return {
|
|
281
|
+
success: false,
|
|
282
|
+
output: 'Chocolatey is not installed'
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const result = await shell.exec(`choco pin add -n="${packageName}"`);
|
|
287
|
+
return {
|
|
288
|
+
success: result.code === 0,
|
|
289
|
+
output: result.stdout || result.stderr
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Unpins a package to allow upgrades
|
|
295
|
+
* @param {string} packageName - The package name
|
|
296
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
297
|
+
*/
|
|
298
|
+
async function unpin(packageName) {
|
|
299
|
+
if (!isInstalled()) {
|
|
300
|
+
return {
|
|
301
|
+
success: false,
|
|
302
|
+
output: 'Chocolatey is not installed'
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const result = await shell.exec(`choco pin remove -n="${packageName}"`);
|
|
307
|
+
return {
|
|
308
|
+
success: result.code === 0,
|
|
309
|
+
output: result.stdout || result.stderr
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
module.exports = {
|
|
314
|
+
isInstalled,
|
|
315
|
+
getVersion,
|
|
316
|
+
install,
|
|
317
|
+
uninstall,
|
|
318
|
+
isPackageInstalled,
|
|
319
|
+
getPackageVersion,
|
|
320
|
+
upgrade,
|
|
321
|
+
search,
|
|
322
|
+
info,
|
|
323
|
+
listInstalled,
|
|
324
|
+
listOutdated,
|
|
325
|
+
pin,
|
|
326
|
+
unpin
|
|
327
|
+
};
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Windows Environment Variables Utilities
|
|
5
|
+
*
|
|
6
|
+
* Windows-specific utilities for managing environment variables.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const shell = require('../common/shell');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Returns the system PATH as an array
|
|
14
|
+
* @returns {string[]}
|
|
15
|
+
*/
|
|
16
|
+
function getPath() {
|
|
17
|
+
const pathEnv = process.env.PATH || process.env.Path || '';
|
|
18
|
+
return pathEnv.split(';').filter(Boolean);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Adds a directory to the user PATH
|
|
23
|
+
* Note: Changes take effect in new processes, not the current one
|
|
24
|
+
* @param {string} directory - The directory to add to PATH
|
|
25
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
26
|
+
*/
|
|
27
|
+
async function addToPath(directory) {
|
|
28
|
+
// Normalize the path
|
|
29
|
+
const normalizedDir = path.resolve(directory);
|
|
30
|
+
|
|
31
|
+
// Check if already in PATH
|
|
32
|
+
const currentPath = getPath();
|
|
33
|
+
if (currentPath.some((p) => path.resolve(p).toLowerCase() === normalizedDir.toLowerCase())) {
|
|
34
|
+
return {
|
|
35
|
+
success: true,
|
|
36
|
+
output: 'Directory is already in PATH'
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Use PowerShell to modify user PATH
|
|
41
|
+
const command = `
|
|
42
|
+
$currentPath = [Environment]::GetEnvironmentVariable('Path', 'User')
|
|
43
|
+
$newPath = "$currentPath;${normalizedDir}"
|
|
44
|
+
[Environment]::SetEnvironmentVariable('Path', $newPath, 'User')
|
|
45
|
+
`.replace(/\n/g, ' ');
|
|
46
|
+
|
|
47
|
+
const result = await shell.exec(`powershell -NoProfile -Command "${command}"`);
|
|
48
|
+
return {
|
|
49
|
+
success: result.code === 0,
|
|
50
|
+
output: result.code === 0
|
|
51
|
+
? `Added ${normalizedDir} to user PATH. Restart your terminal for changes to take effect.`
|
|
52
|
+
: result.stderr
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Removes a directory from the user PATH
|
|
58
|
+
* @param {string} directory - The directory to remove from PATH
|
|
59
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
60
|
+
*/
|
|
61
|
+
async function removeFromPath(directory) {
|
|
62
|
+
const normalizedDir = path.resolve(directory);
|
|
63
|
+
|
|
64
|
+
// Use PowerShell to modify user PATH
|
|
65
|
+
const command = `
|
|
66
|
+
$currentPath = [Environment]::GetEnvironmentVariable('Path', 'User')
|
|
67
|
+
$paths = $currentPath -split ';' | Where-Object { $_.Trim() -ne '' -and [System.IO.Path]::GetFullPath($_).ToLower() -ne '${normalizedDir.toLowerCase().replace(/\\/g, '\\\\')}' }
|
|
68
|
+
$newPath = $paths -join ';'
|
|
69
|
+
[Environment]::SetEnvironmentVariable('Path', $newPath, 'User')
|
|
70
|
+
`.replace(/\n/g, ' ');
|
|
71
|
+
|
|
72
|
+
const result = await shell.exec(`powershell -NoProfile -Command "${command}"`);
|
|
73
|
+
return {
|
|
74
|
+
success: result.code === 0,
|
|
75
|
+
output: result.code === 0
|
|
76
|
+
? `Removed ${normalizedDir} from user PATH. Restart your terminal for changes to take effect.`
|
|
77
|
+
: result.stderr
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Gets an environment variable value
|
|
83
|
+
* @param {string} varName - The variable name
|
|
84
|
+
* @param {'User'|'Machine'|'Process'} [scope='Process'] - The scope to check
|
|
85
|
+
* @returns {Promise<string|null>}
|
|
86
|
+
*/
|
|
87
|
+
async function get(varName, scope = 'Process') {
|
|
88
|
+
if (scope === 'Process') {
|
|
89
|
+
return process.env[varName] || null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const command = `[Environment]::GetEnvironmentVariable('${varName}', '${scope}')`;
|
|
93
|
+
const result = await shell.exec(`powershell -NoProfile -Command "${command}"`);
|
|
94
|
+
|
|
95
|
+
if (result.code === 0 && result.stdout.trim()) {
|
|
96
|
+
return result.stdout.trim();
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Sets a user environment variable
|
|
103
|
+
* @param {string} varName - The variable name
|
|
104
|
+
* @param {string} value - The value to set
|
|
105
|
+
* @param {'User'|'Machine'} [scope='User'] - The scope (Machine requires admin)
|
|
106
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
107
|
+
*/
|
|
108
|
+
async function set(varName, value, scope = 'User') {
|
|
109
|
+
const command = `[Environment]::SetEnvironmentVariable('${varName}', '${value}', '${scope}')`;
|
|
110
|
+
const result = await shell.exec(`powershell -NoProfile -Command "${command}"`);
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
success: result.code === 0,
|
|
114
|
+
output: result.code === 0
|
|
115
|
+
? `Set ${varName}=${value} in ${scope} scope. Restart your terminal for changes to take effect.`
|
|
116
|
+
: result.stderr
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Removes a user environment variable
|
|
122
|
+
* @param {string} varName - The variable name
|
|
123
|
+
* @param {'User'|'Machine'} [scope='User'] - The scope
|
|
124
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
125
|
+
*/
|
|
126
|
+
async function remove(varName, scope = 'User') {
|
|
127
|
+
const command = `[Environment]::SetEnvironmentVariable('${varName}', $null, '${scope}')`;
|
|
128
|
+
const result = await shell.exec(`powershell -NoProfile -Command "${command}"`);
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
success: result.code === 0,
|
|
132
|
+
output: result.code === 0
|
|
133
|
+
? `Removed ${varName} from ${scope} scope. Restart your terminal for changes to take effect.`
|
|
134
|
+
: result.stderr
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Gets all user environment variables
|
|
140
|
+
* @returns {Promise<Object<string, string>>}
|
|
141
|
+
*/
|
|
142
|
+
async function getUserVariables() {
|
|
143
|
+
const command = '[Environment]::GetEnvironmentVariables("User") | ConvertTo-Json';
|
|
144
|
+
const result = await shell.exec(`powershell -NoProfile -Command "${command}"`);
|
|
145
|
+
|
|
146
|
+
if (result.code === 0 && result.stdout.trim()) {
|
|
147
|
+
try {
|
|
148
|
+
return JSON.parse(result.stdout);
|
|
149
|
+
} catch {
|
|
150
|
+
return {};
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return {};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Gets all machine (system) environment variables
|
|
158
|
+
* @returns {Promise<Object<string, string>>}
|
|
159
|
+
*/
|
|
160
|
+
async function getMachineVariables() {
|
|
161
|
+
const command = '[Environment]::GetEnvironmentVariables("Machine") | ConvertTo-Json';
|
|
162
|
+
const result = await shell.exec(`powershell -NoProfile -Command "${command}"`);
|
|
163
|
+
|
|
164
|
+
if (result.code === 0 && result.stdout.trim()) {
|
|
165
|
+
try {
|
|
166
|
+
return JSON.parse(result.stdout);
|
|
167
|
+
} catch {
|
|
168
|
+
return {};
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return {};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Expands environment variables in a string
|
|
176
|
+
* @param {string} str - The string containing variables like %VAR%
|
|
177
|
+
* @returns {string}
|
|
178
|
+
*/
|
|
179
|
+
function expand(str) {
|
|
180
|
+
return str.replace(/%([^%]+)%/g, (_, varName) => {
|
|
181
|
+
return process.env[varName] || `%${varName}%`;
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Broadcasts a WM_SETTINGCHANGE message to notify other applications
|
|
187
|
+
* of environment variable changes
|
|
188
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
189
|
+
*/
|
|
190
|
+
async function broadcastChange() {
|
|
191
|
+
const command = `
|
|
192
|
+
Add-Type -TypeDefinition @"
|
|
193
|
+
using System;
|
|
194
|
+
using System.Runtime.InteropServices;
|
|
195
|
+
public class Win32 {
|
|
196
|
+
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
|
197
|
+
public static extern IntPtr SendMessageTimeout(
|
|
198
|
+
IntPtr hWnd, uint Msg, UIntPtr wParam, string lParam,
|
|
199
|
+
uint fuFlags, uint uTimeout, out UIntPtr lpdwResult);
|
|
200
|
+
}
|
|
201
|
+
"@
|
|
202
|
+
$HWND_BROADCAST = [IntPtr]0xffff
|
|
203
|
+
$WM_SETTINGCHANGE = 0x1a
|
|
204
|
+
$result = [UIntPtr]::Zero
|
|
205
|
+
[Win32]::SendMessageTimeout($HWND_BROADCAST, $WM_SETTINGCHANGE, [UIntPtr]::Zero, "Environment", 2, 5000, [ref]$result)
|
|
206
|
+
`.replace(/\n/g, ' ');
|
|
207
|
+
|
|
208
|
+
const result = await shell.exec(`powershell -NoProfile -Command "${command}"`);
|
|
209
|
+
return {
|
|
210
|
+
success: result.code === 0,
|
|
211
|
+
output: result.code === 0
|
|
212
|
+
? 'Broadcasted environment change notification'
|
|
213
|
+
: result.stderr
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Refreshes the current process's environment from the registry
|
|
219
|
+
* Note: This only affects the current Node.js process
|
|
220
|
+
* @returns {Promise<void>}
|
|
221
|
+
*/
|
|
222
|
+
async function refresh() {
|
|
223
|
+
// Get updated PATH from both User and Machine scopes
|
|
224
|
+
const userPath = await get('Path', 'User');
|
|
225
|
+
const machinePath = await get('Path', 'Machine');
|
|
226
|
+
|
|
227
|
+
if (userPath || machinePath) {
|
|
228
|
+
const combined = [machinePath, userPath].filter(Boolean).join(';');
|
|
229
|
+
process.env.PATH = combined;
|
|
230
|
+
process.env.Path = combined;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
module.exports = {
|
|
235
|
+
getPath,
|
|
236
|
+
addToPath,
|
|
237
|
+
removeFromPath,
|
|
238
|
+
get,
|
|
239
|
+
set,
|
|
240
|
+
remove,
|
|
241
|
+
getUserVariables,
|
|
242
|
+
getMachineVariables,
|
|
243
|
+
expand,
|
|
244
|
+
broadcastChange,
|
|
245
|
+
refresh
|
|
246
|
+
};
|