@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
|
+
* APT Package Manager Utilities
|
|
5
|
+
*
|
|
6
|
+
* Ubuntu/Debian-specific utilities for interacting with APT.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const shell = require('../common/shell');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Checks if apt is available
|
|
13
|
+
* @returns {boolean}
|
|
14
|
+
*/
|
|
15
|
+
function isInstalled() {
|
|
16
|
+
return shell.commandExists('apt-get');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Installs a package via apt-get
|
|
21
|
+
* @param {string} packageName - The package name to install
|
|
22
|
+
* @param {Object} [options] - Installation options
|
|
23
|
+
* @param {boolean} [options.autoConfirm=true] - Automatically confirm installation
|
|
24
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
25
|
+
*/
|
|
26
|
+
async function install(packageName, options = {}) {
|
|
27
|
+
if (!isInstalled()) {
|
|
28
|
+
return {
|
|
29
|
+
success: false,
|
|
30
|
+
output: 'apt-get is not available'
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const autoConfirm = options.autoConfirm !== false ? '-y' : '';
|
|
35
|
+
const result = await shell.exec(`sudo apt-get install ${autoConfirm} ${packageName}`);
|
|
36
|
+
return {
|
|
37
|
+
success: result.code === 0,
|
|
38
|
+
output: result.stdout || result.stderr
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Removes an installed package
|
|
44
|
+
* @param {string} packageName - The package name to remove
|
|
45
|
+
* @param {Object} [options] - Removal options
|
|
46
|
+
* @param {boolean} [options.purge=false] - Also remove configuration files
|
|
47
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
48
|
+
*/
|
|
49
|
+
async function remove(packageName, options = {}) {
|
|
50
|
+
if (!isInstalled()) {
|
|
51
|
+
return {
|
|
52
|
+
success: false,
|
|
53
|
+
output: 'apt-get is not available'
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const command = options.purge ? 'purge' : 'remove';
|
|
58
|
+
const result = await shell.exec(`sudo apt-get ${command} -y ${packageName}`);
|
|
59
|
+
return {
|
|
60
|
+
success: result.code === 0,
|
|
61
|
+
output: result.stdout || result.stderr
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Runs apt-get update to refresh package lists
|
|
67
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
68
|
+
*/
|
|
69
|
+
async function update() {
|
|
70
|
+
if (!isInstalled()) {
|
|
71
|
+
return {
|
|
72
|
+
success: false,
|
|
73
|
+
output: 'apt-get is not available'
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const result = await shell.exec('sudo apt-get update');
|
|
78
|
+
return {
|
|
79
|
+
success: result.code === 0,
|
|
80
|
+
output: result.stdout || result.stderr
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Upgrades a specific package or all packages
|
|
86
|
+
* @param {string} [packageName] - The package to upgrade (all if omitted)
|
|
87
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
88
|
+
*/
|
|
89
|
+
async function upgrade(packageName) {
|
|
90
|
+
if (!isInstalled()) {
|
|
91
|
+
return {
|
|
92
|
+
success: false,
|
|
93
|
+
output: 'apt-get is not available'
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const command = packageName
|
|
98
|
+
? `sudo apt-get install -y --only-upgrade ${packageName}`
|
|
99
|
+
: 'sudo apt-get upgrade -y';
|
|
100
|
+
|
|
101
|
+
const result = await shell.exec(command);
|
|
102
|
+
return {
|
|
103
|
+
success: result.code === 0,
|
|
104
|
+
output: result.stdout || result.stderr
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Queries dpkg to check if a package is installed
|
|
110
|
+
* @param {string} packageName - The package name to check
|
|
111
|
+
* @returns {Promise<boolean>}
|
|
112
|
+
*/
|
|
113
|
+
async function isPackageInstalled(packageName) {
|
|
114
|
+
const result = await shell.exec(`dpkg -l ${packageName} 2>/dev/null | grep -q "^ii"`);
|
|
115
|
+
return result.code === 0;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Returns the installed version of a package
|
|
120
|
+
* @param {string} packageName - The package name
|
|
121
|
+
* @returns {Promise<string|null>}
|
|
122
|
+
*/
|
|
123
|
+
async function getPackageVersion(packageName) {
|
|
124
|
+
const result = await shell.exec(
|
|
125
|
+
`dpkg -l ${packageName} 2>/dev/null | grep "^ii" | awk '{print $3}'`
|
|
126
|
+
);
|
|
127
|
+
if (result.code === 0 && result.stdout.trim()) {
|
|
128
|
+
return result.stdout.trim();
|
|
129
|
+
}
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Adds an APT repository to sources
|
|
135
|
+
* @param {string} repo - The repository specification
|
|
136
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
137
|
+
*/
|
|
138
|
+
async function addRepository(repo) {
|
|
139
|
+
if (!shell.commandExists('add-apt-repository')) {
|
|
140
|
+
// Try to install software-properties-common first
|
|
141
|
+
const installResult = await shell.exec('sudo apt-get install -y software-properties-common');
|
|
142
|
+
if (installResult.code !== 0) {
|
|
143
|
+
return {
|
|
144
|
+
success: false,
|
|
145
|
+
output: 'add-apt-repository is not available and could not be installed'
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const result = await shell.exec(`sudo add-apt-repository -y "${repo}"`);
|
|
151
|
+
return {
|
|
152
|
+
success: result.code === 0,
|
|
153
|
+
output: result.stdout || result.stderr
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Removes an APT repository
|
|
159
|
+
* @param {string} repo - The repository specification
|
|
160
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
161
|
+
*/
|
|
162
|
+
async function removeRepository(repo) {
|
|
163
|
+
if (!shell.commandExists('add-apt-repository')) {
|
|
164
|
+
return {
|
|
165
|
+
success: false,
|
|
166
|
+
output: 'add-apt-repository is not available'
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const result = await shell.exec(`sudo add-apt-repository --remove -y "${repo}"`);
|
|
171
|
+
return {
|
|
172
|
+
success: result.code === 0,
|
|
173
|
+
output: result.stdout || result.stderr
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Imports a GPG key for package verification from a URL
|
|
179
|
+
* @param {string} keyUrl - The URL of the GPG key
|
|
180
|
+
* @param {string} [keyringPath] - Optional path for the keyring file
|
|
181
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
182
|
+
*/
|
|
183
|
+
async function addKey(keyUrl, keyringPath) {
|
|
184
|
+
// Modern method using signed-by (recommended for Ubuntu 22.04+)
|
|
185
|
+
if (keyringPath) {
|
|
186
|
+
const result = await shell.exec(
|
|
187
|
+
`curl -fsSL "${keyUrl}" | sudo gpg --dearmor -o "${keyringPath}"`
|
|
188
|
+
);
|
|
189
|
+
return {
|
|
190
|
+
success: result.code === 0,
|
|
191
|
+
output: result.stdout || result.stderr
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Legacy method using apt-key (deprecated but still works)
|
|
196
|
+
const result = await shell.exec(
|
|
197
|
+
`curl -fsSL "${keyUrl}" | sudo apt-key add -`
|
|
198
|
+
);
|
|
199
|
+
return {
|
|
200
|
+
success: result.code === 0,
|
|
201
|
+
output: result.stdout || result.stderr
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Imports a GPG key from a keyserver
|
|
207
|
+
* @param {string} keyId - The GPG key ID
|
|
208
|
+
* @param {string} [keyserver='keyserver.ubuntu.com'] - The keyserver to use
|
|
209
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
210
|
+
*/
|
|
211
|
+
async function addKeyFromKeyserver(keyId, keyserver = 'keyserver.ubuntu.com') {
|
|
212
|
+
const result = await shell.exec(
|
|
213
|
+
`sudo apt-key adv --keyserver ${keyserver} --recv-keys ${keyId}`
|
|
214
|
+
);
|
|
215
|
+
return {
|
|
216
|
+
success: result.code === 0,
|
|
217
|
+
output: result.stdout || result.stderr
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Searches apt cache for packages
|
|
223
|
+
* @param {string} query - The search query
|
|
224
|
+
* @returns {Promise<string[]>}
|
|
225
|
+
*/
|
|
226
|
+
async function search(query) {
|
|
227
|
+
const result = await shell.exec(`apt-cache search "${query}"`);
|
|
228
|
+
if (result.code !== 0) {
|
|
229
|
+
return [];
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return result.stdout
|
|
233
|
+
.split('\n')
|
|
234
|
+
.filter(Boolean)
|
|
235
|
+
.map((line) => {
|
|
236
|
+
// Format: "package-name - description"
|
|
237
|
+
const match = line.match(/^(\S+)\s+-\s+/);
|
|
238
|
+
return match ? match[1] : line.split(' ')[0];
|
|
239
|
+
})
|
|
240
|
+
.filter(Boolean);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Gets detailed information about a package
|
|
245
|
+
* @param {string} packageName - The package name
|
|
246
|
+
* @returns {Promise<string|null>}
|
|
247
|
+
*/
|
|
248
|
+
async function info(packageName) {
|
|
249
|
+
const result = await shell.exec(`apt-cache show ${packageName}`);
|
|
250
|
+
if (result.code === 0) {
|
|
251
|
+
return result.stdout;
|
|
252
|
+
}
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Lists all installed packages
|
|
258
|
+
* @returns {Promise<string[]>}
|
|
259
|
+
*/
|
|
260
|
+
async function listInstalled() {
|
|
261
|
+
const result = await shell.exec('dpkg --get-selections | grep -v deinstall');
|
|
262
|
+
if (result.code !== 0) {
|
|
263
|
+
return [];
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return result.stdout
|
|
267
|
+
.split('\n')
|
|
268
|
+
.filter(Boolean)
|
|
269
|
+
.map((line) => line.split('\t')[0])
|
|
270
|
+
.filter(Boolean);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Cleans up apt cache
|
|
275
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
276
|
+
*/
|
|
277
|
+
async function clean() {
|
|
278
|
+
const result = await shell.exec('sudo apt-get clean && sudo apt-get autoremove -y');
|
|
279
|
+
return {
|
|
280
|
+
success: result.code === 0,
|
|
281
|
+
output: result.stdout || result.stderr
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
module.exports = {
|
|
286
|
+
isInstalled,
|
|
287
|
+
install,
|
|
288
|
+
remove,
|
|
289
|
+
update,
|
|
290
|
+
upgrade,
|
|
291
|
+
isPackageInstalled,
|
|
292
|
+
getPackageVersion,
|
|
293
|
+
addRepository,
|
|
294
|
+
removeRepository,
|
|
295
|
+
addKey,
|
|
296
|
+
addKeyFromKeyserver,
|
|
297
|
+
search,
|
|
298
|
+
info,
|
|
299
|
+
listInstalled,
|
|
300
|
+
clean
|
|
301
|
+
};
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Desktop Environment Detection Utilities
|
|
5
|
+
*
|
|
6
|
+
* Linux-specific utilities for detecting desktop environments and display servers.
|
|
7
|
+
* These functions address a Linux-specific concern: Linux has multiple display
|
|
8
|
+
* servers (X11, Wayland) and desktop environments (GNOME, KDE, XFCE, etc.).
|
|
9
|
+
* macOS always uses Aqua/Quartz, and Windows always uses the Windows Desktop.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const shell = require('../common/shell');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Checks if a desktop environment is installed and running
|
|
17
|
+
* @returns {boolean}
|
|
18
|
+
*/
|
|
19
|
+
function hasDesktop() {
|
|
20
|
+
// Check if any display server is available
|
|
21
|
+
if (isX11() || isWayland()) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Check for common desktop environment indicators
|
|
26
|
+
if (process.env.XDG_CURRENT_DESKTOP) {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (process.env.DESKTOP_SESSION) {
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Check if any desktop environment package is installed
|
|
35
|
+
const desktopPackages = [
|
|
36
|
+
'ubuntu-desktop',
|
|
37
|
+
'kubuntu-desktop',
|
|
38
|
+
'xubuntu-desktop',
|
|
39
|
+
'lubuntu-desktop',
|
|
40
|
+
'gnome-shell',
|
|
41
|
+
'plasma-desktop',
|
|
42
|
+
'xfce4',
|
|
43
|
+
'lxde',
|
|
44
|
+
'mate-desktop'
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
for (const pkg of desktopPackages) {
|
|
48
|
+
const result = shell.execSync(`dpkg -l ${pkg} 2>/dev/null | grep -q "^ii"`);
|
|
49
|
+
if (result !== '') {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Returns the desktop environment name
|
|
59
|
+
* @returns {string|null} - Desktop name (gnome, kde, xfce, etc.) or null
|
|
60
|
+
*/
|
|
61
|
+
function getDesktopEnvironment() {
|
|
62
|
+
// XDG_CURRENT_DESKTOP is the most reliable indicator
|
|
63
|
+
if (process.env.XDG_CURRENT_DESKTOP) {
|
|
64
|
+
const desktop = process.env.XDG_CURRENT_DESKTOP.toLowerCase();
|
|
65
|
+
|
|
66
|
+
// Normalize common values
|
|
67
|
+
if (desktop.includes('gnome')) return 'gnome';
|
|
68
|
+
if (desktop.includes('kde') || desktop.includes('plasma')) return 'kde';
|
|
69
|
+
if (desktop.includes('xfce')) return 'xfce';
|
|
70
|
+
if (desktop.includes('lxde')) return 'lxde';
|
|
71
|
+
if (desktop.includes('lxqt')) return 'lxqt';
|
|
72
|
+
if (desktop.includes('mate')) return 'mate';
|
|
73
|
+
if (desktop.includes('cinnamon')) return 'cinnamon';
|
|
74
|
+
if (desktop.includes('unity')) return 'unity';
|
|
75
|
+
if (desktop.includes('budgie')) return 'budgie';
|
|
76
|
+
if (desktop.includes('pantheon')) return 'pantheon';
|
|
77
|
+
|
|
78
|
+
return desktop.split(':')[0]; // Take first if multiple (e.g., "ubuntu:GNOME")
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Check DESKTOP_SESSION as fallback
|
|
82
|
+
if (process.env.DESKTOP_SESSION) {
|
|
83
|
+
const session = process.env.DESKTOP_SESSION.toLowerCase();
|
|
84
|
+
|
|
85
|
+
if (session.includes('gnome')) return 'gnome';
|
|
86
|
+
if (session.includes('kde') || session.includes('plasma')) return 'kde';
|
|
87
|
+
if (session.includes('xfce')) return 'xfce';
|
|
88
|
+
if (session.includes('lxde')) return 'lxde';
|
|
89
|
+
if (session.includes('mate')) return 'mate';
|
|
90
|
+
|
|
91
|
+
return session;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Check GNOME_DESKTOP_SESSION_ID (older GNOME)
|
|
95
|
+
if (process.env.GNOME_DESKTOP_SESSION_ID) {
|
|
96
|
+
return 'gnome';
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Check KDE_FULL_SESSION
|
|
100
|
+
if (process.env.KDE_FULL_SESSION) {
|
|
101
|
+
return 'kde';
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Returns the display server type
|
|
109
|
+
* @returns {'x11'|'wayland'|null}
|
|
110
|
+
*/
|
|
111
|
+
function getDisplayServer() {
|
|
112
|
+
if (isWayland()) {
|
|
113
|
+
return 'wayland';
|
|
114
|
+
}
|
|
115
|
+
if (isX11()) {
|
|
116
|
+
return 'x11';
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Checks if running under X11
|
|
123
|
+
* @returns {boolean}
|
|
124
|
+
*/
|
|
125
|
+
function isX11() {
|
|
126
|
+
// Check XDG_SESSION_TYPE first (most reliable on modern systems)
|
|
127
|
+
if (process.env.XDG_SESSION_TYPE === 'x11') {
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Check DISPLAY environment variable
|
|
132
|
+
if (process.env.DISPLAY) {
|
|
133
|
+
// Make sure we're not in Wayland with XWayland
|
|
134
|
+
if (process.env.WAYLAND_DISPLAY) {
|
|
135
|
+
return false; // We're in Wayland, DISPLAY is for XWayland compatibility
|
|
136
|
+
}
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Checks if running under Wayland
|
|
145
|
+
* @returns {boolean}
|
|
146
|
+
*/
|
|
147
|
+
function isWayland() {
|
|
148
|
+
// Check XDG_SESSION_TYPE first
|
|
149
|
+
if (process.env.XDG_SESSION_TYPE === 'wayland') {
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Check WAYLAND_DISPLAY
|
|
154
|
+
if (process.env.WAYLAND_DISPLAY) {
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Returns the DISPLAY or WAYLAND_DISPLAY environment value
|
|
163
|
+
* @returns {string|null}
|
|
164
|
+
*/
|
|
165
|
+
function getDisplayVariable() {
|
|
166
|
+
if (process.env.WAYLAND_DISPLAY) {
|
|
167
|
+
return process.env.WAYLAND_DISPLAY;
|
|
168
|
+
}
|
|
169
|
+
if (process.env.DISPLAY) {
|
|
170
|
+
return process.env.DISPLAY;
|
|
171
|
+
}
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Gets the XDG session type
|
|
177
|
+
* @returns {string|null}
|
|
178
|
+
*/
|
|
179
|
+
function getSessionType() {
|
|
180
|
+
return process.env.XDG_SESSION_TYPE || null;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Gets the current session ID
|
|
185
|
+
* @returns {string|null}
|
|
186
|
+
*/
|
|
187
|
+
function getSessionId() {
|
|
188
|
+
return process.env.XDG_SESSION_ID || null;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Checks if running in a virtual terminal (TTY) without GUI
|
|
193
|
+
* @returns {boolean}
|
|
194
|
+
*/
|
|
195
|
+
function isTTY() {
|
|
196
|
+
// XDG_SESSION_TYPE is 'tty' for virtual terminals
|
|
197
|
+
if (process.env.XDG_SESSION_TYPE === 'tty') {
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Check if TERM indicates a TTY
|
|
202
|
+
const term = process.env.TERM || '';
|
|
203
|
+
if (term === 'linux' || term.startsWith('vt')) {
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Gets the screen resolution (requires xrandr or similar)
|
|
212
|
+
* @returns {Promise<{ width: number, height: number }|null>}
|
|
213
|
+
*/
|
|
214
|
+
async function getScreenResolution() {
|
|
215
|
+
if (isX11()) {
|
|
216
|
+
const result = await shell.exec('xrandr 2>/dev/null | grep "\\*" | head -1');
|
|
217
|
+
if (result.code === 0 && result.stdout) {
|
|
218
|
+
const match = result.stdout.match(/(\d+)x(\d+)/);
|
|
219
|
+
if (match) {
|
|
220
|
+
return {
|
|
221
|
+
width: parseInt(match[1], 10),
|
|
222
|
+
height: parseInt(match[2], 10)
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (isWayland()) {
|
|
229
|
+
// Try wlr-randr for wlroots-based compositors
|
|
230
|
+
const result = await shell.exec('wlr-randr 2>/dev/null | grep "current" | head -1');
|
|
231
|
+
if (result.code === 0 && result.stdout) {
|
|
232
|
+
const match = result.stdout.match(/(\d+)x(\d+)/);
|
|
233
|
+
if (match) {
|
|
234
|
+
return {
|
|
235
|
+
width: parseInt(match[1], 10),
|
|
236
|
+
height: parseInt(match[2], 10)
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Gets the GNOME Shell version (if running GNOME)
|
|
247
|
+
* @returns {Promise<string|null>}
|
|
248
|
+
*/
|
|
249
|
+
async function getGnomeVersion() {
|
|
250
|
+
if (getDesktopEnvironment() !== 'gnome') {
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const result = await shell.exec('gnome-shell --version 2>/dev/null');
|
|
255
|
+
if (result.code === 0) {
|
|
256
|
+
const match = result.stdout.match(/GNOME Shell (\d+\.\d+\.?\d*)/);
|
|
257
|
+
return match ? match[1] : null;
|
|
258
|
+
}
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Gets the KDE Plasma version (if running KDE)
|
|
264
|
+
* @returns {Promise<string|null>}
|
|
265
|
+
*/
|
|
266
|
+
async function getKdeVersion() {
|
|
267
|
+
if (getDesktopEnvironment() !== 'kde') {
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const result = await shell.exec('plasmashell --version 2>/dev/null');
|
|
272
|
+
if (result.code === 0) {
|
|
273
|
+
const match = result.stdout.match(/plasmashell (\d+\.\d+\.?\d*)/);
|
|
274
|
+
return match ? match[1] : null;
|
|
275
|
+
}
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
module.exports = {
|
|
280
|
+
hasDesktop,
|
|
281
|
+
getDesktopEnvironment,
|
|
282
|
+
getDisplayServer,
|
|
283
|
+
isX11,
|
|
284
|
+
isWayland,
|
|
285
|
+
getDisplayVariable,
|
|
286
|
+
getSessionType,
|
|
287
|
+
getSessionId,
|
|
288
|
+
isTTY,
|
|
289
|
+
getScreenResolution,
|
|
290
|
+
getGnomeVersion,
|
|
291
|
+
getKdeVersion
|
|
292
|
+
};
|