@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,302 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Snap Package Manager Utilities
|
|
5
|
+
*
|
|
6
|
+
* Ubuntu-specific utilities for interacting with Snap packages.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const shell = require('../common/shell');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Checks if snapd is installed and running
|
|
13
|
+
* @returns {boolean}
|
|
14
|
+
*/
|
|
15
|
+
function isInstalled() {
|
|
16
|
+
return shell.commandExists('snap');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Installs a snap package
|
|
21
|
+
* @param {string} snapName - The snap name to install
|
|
22
|
+
* @param {Object} [options] - Installation options
|
|
23
|
+
* @param {boolean} [options.classic=false] - Use classic confinement
|
|
24
|
+
* @param {string} [options.channel] - The channel to install from (stable, edge, etc.)
|
|
25
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
26
|
+
*/
|
|
27
|
+
async function install(snapName, options = {}) {
|
|
28
|
+
if (!isInstalled()) {
|
|
29
|
+
return {
|
|
30
|
+
success: false,
|
|
31
|
+
output: 'snap is not available'
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let command = `sudo snap install ${snapName}`;
|
|
36
|
+
|
|
37
|
+
if (options.classic) {
|
|
38
|
+
command += ' --classic';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (options.channel) {
|
|
42
|
+
command += ` --channel=${options.channel}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const result = await shell.exec(command);
|
|
46
|
+
return {
|
|
47
|
+
success: result.code === 0,
|
|
48
|
+
output: result.stdout || result.stderr
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Removes an installed snap
|
|
54
|
+
* @param {string} snapName - The snap name to remove
|
|
55
|
+
* @param {Object} [options] - Removal options
|
|
56
|
+
* @param {boolean} [options.purge=false] - Remove all data
|
|
57
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
58
|
+
*/
|
|
59
|
+
async function remove(snapName, options = {}) {
|
|
60
|
+
if (!isInstalled()) {
|
|
61
|
+
return {
|
|
62
|
+
success: false,
|
|
63
|
+
output: 'snap is not available'
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
let command = `sudo snap remove ${snapName}`;
|
|
68
|
+
|
|
69
|
+
if (options.purge) {
|
|
70
|
+
command += ' --purge';
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const result = await shell.exec(command);
|
|
74
|
+
return {
|
|
75
|
+
success: result.code === 0,
|
|
76
|
+
output: result.stdout || result.stderr
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Checks if a specific snap is installed
|
|
82
|
+
* @param {string} snapName - The snap name to check
|
|
83
|
+
* @returns {Promise<boolean>}
|
|
84
|
+
*/
|
|
85
|
+
async function isSnapInstalled(snapName) {
|
|
86
|
+
if (!isInstalled()) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const result = await shell.exec(`snap list ${snapName} 2>/dev/null`);
|
|
91
|
+
return result.code === 0;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Returns the installed version of a snap
|
|
96
|
+
* @param {string} snapName - The snap name
|
|
97
|
+
* @returns {Promise<string|null>}
|
|
98
|
+
*/
|
|
99
|
+
async function getSnapVersion(snapName) {
|
|
100
|
+
if (!isInstalled()) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const result = await shell.exec(`snap list ${snapName} 2>/dev/null`);
|
|
105
|
+
if (result.code !== 0) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Output format:
|
|
110
|
+
// Name Version Rev Tracking Publisher Notes
|
|
111
|
+
// code 1.85.0 151 latest/stable vscode classic
|
|
112
|
+
const lines = result.stdout.split('\n').filter(Boolean);
|
|
113
|
+
if (lines.length >= 2) {
|
|
114
|
+
const parts = lines[1].split(/\s+/);
|
|
115
|
+
if (parts.length >= 2) {
|
|
116
|
+
return parts[1]; // Version is the second column
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Updates a snap to the latest version
|
|
125
|
+
* @param {string} [snapName] - The snap to refresh (all if omitted)
|
|
126
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
127
|
+
*/
|
|
128
|
+
async function refresh(snapName) {
|
|
129
|
+
if (!isInstalled()) {
|
|
130
|
+
return {
|
|
131
|
+
success: false,
|
|
132
|
+
output: 'snap is not available'
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const command = snapName ? `sudo snap refresh ${snapName}` : 'sudo snap refresh';
|
|
137
|
+
const result = await shell.exec(command);
|
|
138
|
+
return {
|
|
139
|
+
success: result.code === 0,
|
|
140
|
+
output: result.stdout || result.stderr
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Lists all installed snaps
|
|
146
|
+
* @returns {Promise<Array<{ name: string, version: string, rev: string, tracking: string }>>}
|
|
147
|
+
*/
|
|
148
|
+
async function list() {
|
|
149
|
+
if (!isInstalled()) {
|
|
150
|
+
return [];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const result = await shell.exec('snap list');
|
|
154
|
+
if (result.code !== 0) {
|
|
155
|
+
return [];
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const lines = result.stdout.split('\n').filter(Boolean);
|
|
159
|
+
// Skip header line
|
|
160
|
+
if (lines.length <= 1) {
|
|
161
|
+
return [];
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return lines.slice(1).map((line) => {
|
|
165
|
+
const parts = line.split(/\s+/);
|
|
166
|
+
return {
|
|
167
|
+
name: parts[0] || '',
|
|
168
|
+
version: parts[1] || '',
|
|
169
|
+
rev: parts[2] || '',
|
|
170
|
+
tracking: parts[3] || ''
|
|
171
|
+
};
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Searches for snaps in the store
|
|
177
|
+
* @param {string} query - The search query
|
|
178
|
+
* @returns {Promise<Array<{ name: string, version: string, publisher: string, summary: string }>>}
|
|
179
|
+
*/
|
|
180
|
+
async function search(query) {
|
|
181
|
+
if (!isInstalled()) {
|
|
182
|
+
return [];
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const result = await shell.exec(`snap find "${query}"`);
|
|
186
|
+
if (result.code !== 0) {
|
|
187
|
+
return [];
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const lines = result.stdout.split('\n').filter(Boolean);
|
|
191
|
+
// Skip header line
|
|
192
|
+
if (lines.length <= 1) {
|
|
193
|
+
return [];
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return lines.slice(1).map((line) => {
|
|
197
|
+
const parts = line.split(/\s+/);
|
|
198
|
+
return {
|
|
199
|
+
name: parts[0] || '',
|
|
200
|
+
version: parts[1] || '',
|
|
201
|
+
publisher: parts[2] || '',
|
|
202
|
+
summary: parts.slice(3).join(' ') || ''
|
|
203
|
+
};
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Gets detailed information about a snap
|
|
209
|
+
* @param {string} snapName - The snap name
|
|
210
|
+
* @returns {Promise<string|null>}
|
|
211
|
+
*/
|
|
212
|
+
async function info(snapName) {
|
|
213
|
+
if (!isInstalled()) {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const result = await shell.exec(`snap info ${snapName}`);
|
|
218
|
+
if (result.code === 0) {
|
|
219
|
+
return result.stdout;
|
|
220
|
+
}
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Enables a disabled snap
|
|
226
|
+
* @param {string} snapName - The snap name
|
|
227
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
228
|
+
*/
|
|
229
|
+
async function enable(snapName) {
|
|
230
|
+
if (!isInstalled()) {
|
|
231
|
+
return {
|
|
232
|
+
success: false,
|
|
233
|
+
output: 'snap is not available'
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const result = await shell.exec(`sudo snap enable ${snapName}`);
|
|
238
|
+
return {
|
|
239
|
+
success: result.code === 0,
|
|
240
|
+
output: result.stdout || result.stderr
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Disables a snap without removing it
|
|
246
|
+
* @param {string} snapName - The snap name
|
|
247
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
248
|
+
*/
|
|
249
|
+
async function disable(snapName) {
|
|
250
|
+
if (!isInstalled()) {
|
|
251
|
+
return {
|
|
252
|
+
success: false,
|
|
253
|
+
output: 'snap is not available'
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const result = await shell.exec(`sudo snap disable ${snapName}`);
|
|
258
|
+
return {
|
|
259
|
+
success: result.code === 0,
|
|
260
|
+
output: result.stdout || result.stderr
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Connects a snap interface
|
|
266
|
+
* @param {string} plug - The plug (e.g., 'snap-name:plug-name')
|
|
267
|
+
* @param {string} [slot] - The slot to connect to
|
|
268
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
269
|
+
*/
|
|
270
|
+
async function connect(plug, slot) {
|
|
271
|
+
if (!isInstalled()) {
|
|
272
|
+
return {
|
|
273
|
+
success: false,
|
|
274
|
+
output: 'snap is not available'
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const command = slot
|
|
279
|
+
? `sudo snap connect ${plug} ${slot}`
|
|
280
|
+
: `sudo snap connect ${plug}`;
|
|
281
|
+
|
|
282
|
+
const result = await shell.exec(command);
|
|
283
|
+
return {
|
|
284
|
+
success: result.code === 0,
|
|
285
|
+
output: result.stdout || result.stderr
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
module.exports = {
|
|
290
|
+
isInstalled,
|
|
291
|
+
install,
|
|
292
|
+
remove,
|
|
293
|
+
isSnapInstalled,
|
|
294
|
+
getSnapVersion,
|
|
295
|
+
refresh,
|
|
296
|
+
list,
|
|
297
|
+
search,
|
|
298
|
+
info,
|
|
299
|
+
enable,
|
|
300
|
+
disable,
|
|
301
|
+
connect
|
|
302
|
+
};
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Systemd Service Management Utilities
|
|
5
|
+
*
|
|
6
|
+
* Linux-specific utilities for managing systemd services.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const shell = require('../common/shell');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Checks if systemd is the init system
|
|
13
|
+
* @returns {boolean}
|
|
14
|
+
*/
|
|
15
|
+
function isSystemdAvailable() {
|
|
16
|
+
// Check if systemctl exists and systemd is running
|
|
17
|
+
if (!shell.commandExists('systemctl')) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Verify systemd is actually running (not just installed)
|
|
22
|
+
const result = shell.execSync('systemctl is-system-running 2>/dev/null');
|
|
23
|
+
// Valid responses include: running, degraded, maintenance, initializing
|
|
24
|
+
return result !== '' && !result.includes('offline');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Checks if a systemd service is active (running)
|
|
29
|
+
* @param {string} service - The service name
|
|
30
|
+
* @returns {Promise<boolean>}
|
|
31
|
+
*/
|
|
32
|
+
async function isServiceRunning(service) {
|
|
33
|
+
if (!isSystemdAvailable()) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const result = await shell.exec(`systemctl is-active ${service}`);
|
|
38
|
+
return result.stdout.trim() === 'active';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Checks if a service is enabled (starts on boot)
|
|
43
|
+
* @param {string} service - The service name
|
|
44
|
+
* @returns {Promise<boolean>}
|
|
45
|
+
*/
|
|
46
|
+
async function isServiceEnabled(service) {
|
|
47
|
+
if (!isSystemdAvailable()) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const result = await shell.exec(`systemctl is-enabled ${service}`);
|
|
52
|
+
return result.stdout.trim() === 'enabled';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Starts a systemd service
|
|
57
|
+
* @param {string} service - The service name
|
|
58
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
59
|
+
*/
|
|
60
|
+
async function startService(service) {
|
|
61
|
+
if (!isSystemdAvailable()) {
|
|
62
|
+
return {
|
|
63
|
+
success: false,
|
|
64
|
+
output: 'systemd is not available'
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const result = await shell.exec(`sudo systemctl start ${service}`);
|
|
69
|
+
return {
|
|
70
|
+
success: result.code === 0,
|
|
71
|
+
output: result.stdout || result.stderr
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Stops a systemd service
|
|
77
|
+
* @param {string} service - The service name
|
|
78
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
79
|
+
*/
|
|
80
|
+
async function stopService(service) {
|
|
81
|
+
if (!isSystemdAvailable()) {
|
|
82
|
+
return {
|
|
83
|
+
success: false,
|
|
84
|
+
output: 'systemd is not available'
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const result = await shell.exec(`sudo systemctl stop ${service}`);
|
|
89
|
+
return {
|
|
90
|
+
success: result.code === 0,
|
|
91
|
+
output: result.stdout || result.stderr
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Restarts a systemd service
|
|
97
|
+
* @param {string} service - The service name
|
|
98
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
99
|
+
*/
|
|
100
|
+
async function restartService(service) {
|
|
101
|
+
if (!isSystemdAvailable()) {
|
|
102
|
+
return {
|
|
103
|
+
success: false,
|
|
104
|
+
output: 'systemd is not available'
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const result = await shell.exec(`sudo systemctl restart ${service}`);
|
|
109
|
+
return {
|
|
110
|
+
success: result.code === 0,
|
|
111
|
+
output: result.stdout || result.stderr
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Reloads a systemd service configuration
|
|
117
|
+
* @param {string} service - The service name
|
|
118
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
119
|
+
*/
|
|
120
|
+
async function reloadService(service) {
|
|
121
|
+
if (!isSystemdAvailable()) {
|
|
122
|
+
return {
|
|
123
|
+
success: false,
|
|
124
|
+
output: 'systemd is not available'
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const result = await shell.exec(`sudo systemctl reload ${service}`);
|
|
129
|
+
return {
|
|
130
|
+
success: result.code === 0,
|
|
131
|
+
output: result.stdout || result.stderr
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Enables a service to start on boot
|
|
137
|
+
* @param {string} service - The service name
|
|
138
|
+
* @param {Object} [options] - Options
|
|
139
|
+
* @param {boolean} [options.now=false] - Also start the service immediately
|
|
140
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
141
|
+
*/
|
|
142
|
+
async function enableService(service, options = {}) {
|
|
143
|
+
if (!isSystemdAvailable()) {
|
|
144
|
+
return {
|
|
145
|
+
success: false,
|
|
146
|
+
output: 'systemd is not available'
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const nowFlag = options.now ? ' --now' : '';
|
|
151
|
+
const result = await shell.exec(`sudo systemctl enable${nowFlag} ${service}`);
|
|
152
|
+
return {
|
|
153
|
+
success: result.code === 0,
|
|
154
|
+
output: result.stdout || result.stderr
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Disables a service from starting on boot
|
|
160
|
+
* @param {string} service - The service name
|
|
161
|
+
* @param {Object} [options] - Options
|
|
162
|
+
* @param {boolean} [options.now=false] - Also stop the service immediately
|
|
163
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
164
|
+
*/
|
|
165
|
+
async function disableService(service, options = {}) {
|
|
166
|
+
if (!isSystemdAvailable()) {
|
|
167
|
+
return {
|
|
168
|
+
success: false,
|
|
169
|
+
output: 'systemd is not available'
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const nowFlag = options.now ? ' --now' : '';
|
|
174
|
+
const result = await shell.exec(`sudo systemctl disable${nowFlag} ${service}`);
|
|
175
|
+
return {
|
|
176
|
+
success: result.code === 0,
|
|
177
|
+
output: result.stdout || result.stderr
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Gets the status of a service
|
|
183
|
+
* @param {string} service - The service name
|
|
184
|
+
* @returns {Promise<string|null>}
|
|
185
|
+
*/
|
|
186
|
+
async function getServiceStatus(service) {
|
|
187
|
+
if (!isSystemdAvailable()) {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const result = await shell.exec(`systemctl status ${service}`);
|
|
192
|
+
return result.stdout || result.stderr;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Lists all running services
|
|
197
|
+
* @returns {Promise<string[]>}
|
|
198
|
+
*/
|
|
199
|
+
async function listRunningServices() {
|
|
200
|
+
if (!isSystemdAvailable()) {
|
|
201
|
+
return [];
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const result = await shell.exec('systemctl list-units --type=service --state=running --no-pager --plain');
|
|
205
|
+
if (result.code !== 0) {
|
|
206
|
+
return [];
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return result.stdout
|
|
210
|
+
.split('\n')
|
|
211
|
+
.filter(Boolean)
|
|
212
|
+
.slice(0, -1) // Remove summary line
|
|
213
|
+
.map((line) => {
|
|
214
|
+
const parts = line.trim().split(/\s+/);
|
|
215
|
+
return parts[0] ? parts[0].replace('.service', '') : '';
|
|
216
|
+
})
|
|
217
|
+
.filter(Boolean);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Reloads the systemd daemon (after unit file changes)
|
|
222
|
+
* @returns {Promise<{ success: boolean, output: string }>}
|
|
223
|
+
*/
|
|
224
|
+
async function daemonReload() {
|
|
225
|
+
if (!isSystemdAvailable()) {
|
|
226
|
+
return {
|
|
227
|
+
success: false,
|
|
228
|
+
output: 'systemd is not available'
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const result = await shell.exec('sudo systemctl daemon-reload');
|
|
233
|
+
return {
|
|
234
|
+
success: result.code === 0,
|
|
235
|
+
output: result.stdout || result.stderr
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Checks if a service exists
|
|
241
|
+
* @param {string} service - The service name
|
|
242
|
+
* @returns {Promise<boolean>}
|
|
243
|
+
*/
|
|
244
|
+
async function serviceExists(service) {
|
|
245
|
+
if (!isSystemdAvailable()) {
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const result = await shell.exec(`systemctl cat ${service} 2>/dev/null`);
|
|
250
|
+
return result.code === 0;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Gets the logs for a service
|
|
255
|
+
* @param {string} service - The service name
|
|
256
|
+
* @param {Object} [options] - Options
|
|
257
|
+
* @param {number} [options.lines=50] - Number of lines to retrieve
|
|
258
|
+
* @param {boolean} [options.follow=false] - Follow the logs (not recommended for programmatic use)
|
|
259
|
+
* @returns {Promise<string|null>}
|
|
260
|
+
*/
|
|
261
|
+
async function getServiceLogs(service, options = {}) {
|
|
262
|
+
if (!shell.commandExists('journalctl')) {
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const lines = options.lines || 50;
|
|
267
|
+
const result = await shell.exec(`journalctl -u ${service} -n ${lines} --no-pager`);
|
|
268
|
+
return result.code === 0 ? result.stdout : null;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
module.exports = {
|
|
272
|
+
isSystemdAvailable,
|
|
273
|
+
isServiceRunning,
|
|
274
|
+
isServiceEnabled,
|
|
275
|
+
startService,
|
|
276
|
+
stopService,
|
|
277
|
+
restartService,
|
|
278
|
+
reloadService,
|
|
279
|
+
enableService,
|
|
280
|
+
disableService,
|
|
281
|
+
getServiceStatus,
|
|
282
|
+
listRunningServices,
|
|
283
|
+
daemonReload,
|
|
284
|
+
serviceExists,
|
|
285
|
+
getServiceLogs
|
|
286
|
+
};
|