@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,269 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Windows Registry Access Utilities
|
|
5
|
+
*
|
|
6
|
+
* Windows-specific utilities for reading the Windows registry.
|
|
7
|
+
* Used for detecting installed applications and their properties.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const shell = require('../common/shell');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Common registry paths for installed applications
|
|
14
|
+
*/
|
|
15
|
+
const UNINSTALL_PATHS = [
|
|
16
|
+
'HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall',
|
|
17
|
+
'HKLM\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall',
|
|
18
|
+
'HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall'
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Checks Windows registry uninstall keys for an application
|
|
23
|
+
* @param {string} appName - The application name to check
|
|
24
|
+
* @returns {Promise<boolean>}
|
|
25
|
+
*/
|
|
26
|
+
async function isAppInstalled(appName) {
|
|
27
|
+
const appInfo = await findAppInRegistry(appName);
|
|
28
|
+
return appInfo !== null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Retrieves installation path from registry
|
|
33
|
+
* @param {string} appName - The application name
|
|
34
|
+
* @returns {Promise<string|null>}
|
|
35
|
+
*/
|
|
36
|
+
async function getInstallPath(appName) {
|
|
37
|
+
const appInfo = await findAppInRegistry(appName);
|
|
38
|
+
return appInfo ? appInfo.installLocation : null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Retrieves version from registry
|
|
43
|
+
* @param {string} appName - The application name
|
|
44
|
+
* @returns {Promise<string|null>}
|
|
45
|
+
*/
|
|
46
|
+
async function getAppVersion(appName) {
|
|
47
|
+
const appInfo = await findAppInRegistry(appName);
|
|
48
|
+
return appInfo ? appInfo.version : null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Finds an application in the registry and returns its info
|
|
53
|
+
* @param {string} appName - The application name to find
|
|
54
|
+
* @returns {Promise<{ displayName: string, version: string, installLocation: string, publisher: string }|null>}
|
|
55
|
+
*/
|
|
56
|
+
async function findAppInRegistry(appName) {
|
|
57
|
+
const normalizedName = appName.toLowerCase();
|
|
58
|
+
|
|
59
|
+
for (const basePath of UNINSTALL_PATHS) {
|
|
60
|
+
// List all subkeys
|
|
61
|
+
const listResult = await shell.exec(
|
|
62
|
+
`reg query "${basePath}" 2>nul`
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
if (listResult.code !== 0) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const subkeys = listResult.stdout
|
|
70
|
+
.split('\n')
|
|
71
|
+
.filter((line) => line.trim().startsWith(basePath))
|
|
72
|
+
.map((line) => line.trim());
|
|
73
|
+
|
|
74
|
+
for (const subkey of subkeys) {
|
|
75
|
+
const displayName = await getValue(subkey, 'DisplayName');
|
|
76
|
+
if (!displayName) continue;
|
|
77
|
+
|
|
78
|
+
// Check if this is the app we're looking for
|
|
79
|
+
if (displayName.toLowerCase().includes(normalizedName)) {
|
|
80
|
+
return {
|
|
81
|
+
displayName,
|
|
82
|
+
version: await getValue(subkey, 'DisplayVersion'),
|
|
83
|
+
installLocation: await getValue(subkey, 'InstallLocation'),
|
|
84
|
+
publisher: await getValue(subkey, 'Publisher')
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Checks if a registry key exists
|
|
95
|
+
* @param {string} keyPath - The full registry key path
|
|
96
|
+
* @returns {Promise<boolean>}
|
|
97
|
+
*/
|
|
98
|
+
async function keyExists(keyPath) {
|
|
99
|
+
const result = await shell.exec(`reg query "${keyPath}" 2>nul`);
|
|
100
|
+
return result.code === 0;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Reads a value from the registry
|
|
105
|
+
* @param {string} keyPath - The full registry key path
|
|
106
|
+
* @param {string} valueName - The value name to read
|
|
107
|
+
* @returns {Promise<string|null>}
|
|
108
|
+
*/
|
|
109
|
+
async function getValue(keyPath, valueName) {
|
|
110
|
+
const result = await shell.exec(
|
|
111
|
+
`reg query "${keyPath}" /v "${valueName}" 2>nul`
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
if (result.code !== 0) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Parse the output
|
|
119
|
+
// Format: " ValueName REG_SZ Value"
|
|
120
|
+
const lines = result.stdout.split('\n');
|
|
121
|
+
for (const line of lines) {
|
|
122
|
+
if (line.includes(valueName)) {
|
|
123
|
+
// Split by registry type (REG_SZ, REG_DWORD, etc.)
|
|
124
|
+
const match = line.match(/REG_\w+\s+(.+)$/);
|
|
125
|
+
if (match) {
|
|
126
|
+
return match[1].trim();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Lists all values in a registry key
|
|
136
|
+
* @param {string} keyPath - The full registry key path
|
|
137
|
+
* @returns {Promise<Array<{ name: string, type: string, value: string }>>}
|
|
138
|
+
*/
|
|
139
|
+
async function listValues(keyPath) {
|
|
140
|
+
const result = await shell.exec(`reg query "${keyPath}" 2>nul`);
|
|
141
|
+
|
|
142
|
+
if (result.code !== 0) {
|
|
143
|
+
return [];
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const values = [];
|
|
147
|
+
const lines = result.stdout.split('\n');
|
|
148
|
+
|
|
149
|
+
for (const line of lines) {
|
|
150
|
+
// Skip empty lines and the key path line
|
|
151
|
+
if (!line.trim() || line.trim().startsWith('HK')) {
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Parse " Name REG_TYPE Value"
|
|
156
|
+
const match = line.match(/^\s+(\S+)\s+(REG_\w+)\s+(.*)$/);
|
|
157
|
+
if (match) {
|
|
158
|
+
values.push({
|
|
159
|
+
name: match[1],
|
|
160
|
+
type: match[2],
|
|
161
|
+
value: match[3].trim()
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return values;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Lists all subkeys of a registry key
|
|
171
|
+
* @param {string} keyPath - The full registry key path
|
|
172
|
+
* @returns {Promise<string[]>}
|
|
173
|
+
*/
|
|
174
|
+
async function listSubkeys(keyPath) {
|
|
175
|
+
const result = await shell.exec(`reg query "${keyPath}" 2>nul`);
|
|
176
|
+
|
|
177
|
+
if (result.code !== 0) {
|
|
178
|
+
return [];
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return result.stdout
|
|
182
|
+
.split('\n')
|
|
183
|
+
.filter((line) => line.trim().startsWith('HK'))
|
|
184
|
+
.map((line) => line.trim())
|
|
185
|
+
.filter((line) => line !== keyPath);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Gets all installed applications from the registry
|
|
190
|
+
* @returns {Promise<Array<{ name: string, version: string, publisher: string, installLocation: string }>>}
|
|
191
|
+
*/
|
|
192
|
+
async function listInstalledApps() {
|
|
193
|
+
const apps = [];
|
|
194
|
+
|
|
195
|
+
for (const basePath of UNINSTALL_PATHS) {
|
|
196
|
+
const subkeys = await listSubkeys(basePath);
|
|
197
|
+
|
|
198
|
+
for (const subkey of subkeys) {
|
|
199
|
+
const displayName = await getValue(subkey, 'DisplayName');
|
|
200
|
+
if (!displayName) continue;
|
|
201
|
+
|
|
202
|
+
// Skip system components and updates
|
|
203
|
+
const systemComponent = await getValue(subkey, 'SystemComponent');
|
|
204
|
+
if (systemComponent === '1') continue;
|
|
205
|
+
|
|
206
|
+
apps.push({
|
|
207
|
+
name: displayName,
|
|
208
|
+
version: await getValue(subkey, 'DisplayVersion') || '',
|
|
209
|
+
publisher: await getValue(subkey, 'Publisher') || '',
|
|
210
|
+
installLocation: await getValue(subkey, 'InstallLocation') || ''
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Remove duplicates by name
|
|
216
|
+
const seen = new Set();
|
|
217
|
+
return apps.filter((app) => {
|
|
218
|
+
if (seen.has(app.name)) {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
seen.add(app.name);
|
|
222
|
+
return true;
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Gets the uninstall command for an application
|
|
228
|
+
* @param {string} appName - The application name
|
|
229
|
+
* @returns {Promise<string|null>}
|
|
230
|
+
*/
|
|
231
|
+
async function getUninstallCommand(appName) {
|
|
232
|
+
const normalizedName = appName.toLowerCase();
|
|
233
|
+
|
|
234
|
+
for (const basePath of UNINSTALL_PATHS) {
|
|
235
|
+
const subkeys = await listSubkeys(basePath);
|
|
236
|
+
|
|
237
|
+
for (const subkey of subkeys) {
|
|
238
|
+
const displayName = await getValue(subkey, 'DisplayName');
|
|
239
|
+
if (!displayName) continue;
|
|
240
|
+
|
|
241
|
+
if (displayName.toLowerCase().includes(normalizedName)) {
|
|
242
|
+
// Try QuietUninstallString first (silent uninstall)
|
|
243
|
+
const quietUninstall = await getValue(subkey, 'QuietUninstallString');
|
|
244
|
+
if (quietUninstall) {
|
|
245
|
+
return quietUninstall;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Fall back to UninstallString
|
|
249
|
+
const uninstall = await getValue(subkey, 'UninstallString');
|
|
250
|
+
return uninstall;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
module.exports = {
|
|
259
|
+
isAppInstalled,
|
|
260
|
+
getInstallPath,
|
|
261
|
+
getAppVersion,
|
|
262
|
+
findAppInRegistry,
|
|
263
|
+
keyExists,
|
|
264
|
+
getValue,
|
|
265
|
+
listValues,
|
|
266
|
+
listSubkeys,
|
|
267
|
+
listInstalledApps,
|
|
268
|
+
getUninstallCommand
|
|
269
|
+
};
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Windows Shell Environment Detection Utilities
|
|
5
|
+
*
|
|
6
|
+
* Windows-specific utilities for detecting the shell environment.
|
|
7
|
+
* These functions address a Windows-specific concern: CMD and PowerShell have
|
|
8
|
+
* fundamentally different syntax and capabilities. Unix shells (bash, zsh, sh)
|
|
9
|
+
* are interchangeable for command execution purposes.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const shell = require('../common/shell');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Checks if running inside PowerShell
|
|
16
|
+
* @returns {boolean}
|
|
17
|
+
*/
|
|
18
|
+
function isPowerShell() {
|
|
19
|
+
// Check for PowerShell-specific environment variables
|
|
20
|
+
if (process.env.PSModulePath) {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Check if the parent process is PowerShell
|
|
25
|
+
if (process.env.SHELL && process.env.SHELL.includes('pwsh')) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Check ComSpec for PowerShell
|
|
30
|
+
const comspec = process.env.ComSpec || '';
|
|
31
|
+
if (comspec.toLowerCase().includes('powershell')) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Checks if running inside CMD
|
|
40
|
+
* @returns {boolean}
|
|
41
|
+
*/
|
|
42
|
+
function isCmd() {
|
|
43
|
+
// If we're in PowerShell, we're not in CMD
|
|
44
|
+
if (isPowerShell()) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Check ComSpec for cmd.exe
|
|
49
|
+
const comspec = process.env.ComSpec || '';
|
|
50
|
+
if (comspec.toLowerCase().includes('cmd.exe')) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Check PROMPT environment variable (CMD-specific)
|
|
55
|
+
if (process.env.PROMPT) {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Checks if running inside Windows Terminal
|
|
64
|
+
* @returns {boolean}
|
|
65
|
+
*/
|
|
66
|
+
function isWindowsTerminal() {
|
|
67
|
+
// Windows Terminal sets WT_SESSION
|
|
68
|
+
return !!process.env.WT_SESSION;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Returns PowerShell version (5.x for Windows PowerShell, 7.x for PowerShell Core)
|
|
73
|
+
* @returns {Promise<string|null>}
|
|
74
|
+
*/
|
|
75
|
+
async function getPowerShellVersion() {
|
|
76
|
+
// Try pwsh first (PowerShell Core / 7.x)
|
|
77
|
+
if (shell.commandExists('pwsh')) {
|
|
78
|
+
const result = await shell.exec('pwsh -NoProfile -Command "$PSVersionTable.PSVersion.ToString()"');
|
|
79
|
+
if (result.code === 0 && result.stdout.trim()) {
|
|
80
|
+
return result.stdout.trim();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Try powershell (Windows PowerShell / 5.x)
|
|
85
|
+
if (shell.commandExists('powershell')) {
|
|
86
|
+
const result = await shell.exec('powershell -NoProfile -Command "$PSVersionTable.PSVersion.ToString()"');
|
|
87
|
+
if (result.code === 0 && result.stdout.trim()) {
|
|
88
|
+
return result.stdout.trim();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Returns the current shell name
|
|
97
|
+
* @returns {'powershell'|'pwsh'|'cmd'|'unknown'}
|
|
98
|
+
*/
|
|
99
|
+
function getShellName() {
|
|
100
|
+
if (shell.commandExists('pwsh') && isPowerShell()) {
|
|
101
|
+
// Check if it's specifically pwsh (Core) vs powershell (Windows)
|
|
102
|
+
const psModulePath = process.env.PSModulePath || '';
|
|
103
|
+
if (psModulePath.includes('PowerShell\\7') || psModulePath.includes('powershell/7')) {
|
|
104
|
+
return 'pwsh';
|
|
105
|
+
}
|
|
106
|
+
return 'powershell';
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (isPowerShell()) {
|
|
110
|
+
return 'powershell';
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (isCmd()) {
|
|
114
|
+
return 'cmd';
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return 'unknown';
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Checks if PowerShell Core (pwsh 7.x) is available
|
|
122
|
+
* @returns {boolean}
|
|
123
|
+
*/
|
|
124
|
+
function hasPowerShellCore() {
|
|
125
|
+
return shell.commandExists('pwsh');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Checks if Windows PowerShell (5.x) is available
|
|
130
|
+
* @returns {boolean}
|
|
131
|
+
*/
|
|
132
|
+
function hasWindowsPowerShell() {
|
|
133
|
+
return shell.commandExists('powershell');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Gets the path to the preferred PowerShell executable
|
|
138
|
+
* @returns {string|null}
|
|
139
|
+
*/
|
|
140
|
+
function getPowerShellPath() {
|
|
141
|
+
// Prefer PowerShell Core if available
|
|
142
|
+
const pwshPath = shell.which('pwsh');
|
|
143
|
+
if (pwshPath) {
|
|
144
|
+
return pwshPath;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Fall back to Windows PowerShell
|
|
148
|
+
const powershellPath = shell.which('powershell');
|
|
149
|
+
return powershellPath;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Executes a PowerShell command
|
|
154
|
+
* @param {string} command - The PowerShell command to execute
|
|
155
|
+
* @param {Object} [options] - Execution options
|
|
156
|
+
* @param {boolean} [options.noProfile=true] - Don't load the PowerShell profile
|
|
157
|
+
* @param {boolean} [options.usePwsh=true] - Prefer pwsh over powershell
|
|
158
|
+
* @returns {Promise<{ success: boolean, stdout: string, stderr: string }>}
|
|
159
|
+
*/
|
|
160
|
+
async function execPowerShell(command, options = {}) {
|
|
161
|
+
const noProfile = options.noProfile !== false ? '-NoProfile' : '';
|
|
162
|
+
const usePwsh = options.usePwsh !== false && hasPowerShellCore();
|
|
163
|
+
|
|
164
|
+
const ps = usePwsh ? 'pwsh' : 'powershell';
|
|
165
|
+
|
|
166
|
+
// Escape double quotes in the command
|
|
167
|
+
const escapedCommand = command.replace(/"/g, '\\"');
|
|
168
|
+
|
|
169
|
+
const result = await shell.exec(`${ps} ${noProfile} -Command "${escapedCommand}"`);
|
|
170
|
+
return {
|
|
171
|
+
success: result.code === 0,
|
|
172
|
+
stdout: result.stdout,
|
|
173
|
+
stderr: result.stderr
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Gets the Windows build number
|
|
179
|
+
* @returns {Promise<number|null>}
|
|
180
|
+
*/
|
|
181
|
+
async function getWindowsBuild() {
|
|
182
|
+
const result = await shell.exec('powershell -NoProfile -Command "[System.Environment]::OSVersion.Version.Build"');
|
|
183
|
+
if (result.code === 0 && result.stdout.trim()) {
|
|
184
|
+
const build = parseInt(result.stdout.trim(), 10);
|
|
185
|
+
return isNaN(build) ? null : build;
|
|
186
|
+
}
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Gets the Windows version name (e.g., "Windows 11", "Windows 10")
|
|
192
|
+
* @returns {Promise<string|null>}
|
|
193
|
+
*/
|
|
194
|
+
async function getWindowsVersion() {
|
|
195
|
+
const result = await shell.exec('powershell -NoProfile -Command "(Get-WmiObject Win32_OperatingSystem).Caption"');
|
|
196
|
+
if (result.code === 0 && result.stdout.trim()) {
|
|
197
|
+
return result.stdout.trim();
|
|
198
|
+
}
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Checks if the current console supports ANSI colors
|
|
204
|
+
* @returns {boolean}
|
|
205
|
+
*/
|
|
206
|
+
function supportsAnsiColors() {
|
|
207
|
+
// Windows Terminal always supports ANSI
|
|
208
|
+
if (isWindowsTerminal()) {
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Check for ANSICON (adds ANSI support to CMD)
|
|
213
|
+
if (process.env.ANSICON) {
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Check for ConEmu
|
|
218
|
+
if (process.env.ConEmuANSI === 'ON') {
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Modern Windows 10+ CMD supports ANSI if virtual terminal processing is enabled
|
|
223
|
+
// This is hard to detect without trying, so we assume modern Windows supports it
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
module.exports = {
|
|
228
|
+
isPowerShell,
|
|
229
|
+
isCmd,
|
|
230
|
+
isWindowsTerminal,
|
|
231
|
+
getPowerShellVersion,
|
|
232
|
+
getShellName,
|
|
233
|
+
hasPowerShellCore,
|
|
234
|
+
hasWindowsPowerShell,
|
|
235
|
+
getPowerShellPath,
|
|
236
|
+
execPowerShell,
|
|
237
|
+
getWindowsBuild,
|
|
238
|
+
getWindowsVersion,
|
|
239
|
+
supportsAnsiColors
|
|
240
|
+
};
|