@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.
Files changed (200) hide show
  1. package/README.md +156 -0
  2. package/bin/dev.js +16 -0
  3. package/files/README.md +0 -0
  4. package/files/claude/.claude/commands/setup-context.md +3 -0
  5. package/files/monorepos/_archive/README.md +36 -0
  6. package/files/monorepos/_legacy/README.md +36 -0
  7. package/files/monorepos/ai-docs/README.md +33 -0
  8. package/files/monorepos/apps/README.md +24 -0
  9. package/files/monorepos/docs/README.md +40 -0
  10. package/files/monorepos/packages/README.md +25 -0
  11. package/files/monorepos/research/README.md +29 -0
  12. package/files/monorepos/scripts/README.md +24 -0
  13. package/package.json +39 -0
  14. package/src/cli.js +68 -0
  15. package/src/commands/README.md +41 -0
  16. package/src/commands/configure.js +199 -0
  17. package/src/commands/identity.js +1630 -0
  18. package/src/commands/ignore.js +247 -0
  19. package/src/commands/install.js +173 -0
  20. package/src/commands/setup.js +212 -0
  21. package/src/commands/status.js +223 -0
  22. package/src/completion.js +284 -0
  23. package/src/constants.js +45 -0
  24. package/src/ignore/claude-code.txt +10 -0
  25. package/src/ignore/docker.txt +18 -0
  26. package/src/ignore/linux.txt +23 -0
  27. package/src/ignore/macos.txt +36 -0
  28. package/src/ignore/node.txt +55 -0
  29. package/src/ignore/terraform.txt +37 -0
  30. package/src/ignore/vscode.txt +18 -0
  31. package/src/ignore/windows.txt +35 -0
  32. package/src/index.js +0 -0
  33. package/src/installs/README.md +399 -0
  34. package/src/installs/adobe-creative-cloud.js +44 -0
  35. package/src/installs/appcleaner.js +44 -0
  36. package/src/installs/atomicparsley.js +44 -0
  37. package/src/installs/aws-cli.js +44 -0
  38. package/src/installs/balena-etcher.js +44 -0
  39. package/src/installs/bambu-studio.js +44 -0
  40. package/src/installs/bash-completion.js +44 -0
  41. package/src/installs/bash.js +44 -0
  42. package/src/installs/beyond-compare.js +44 -0
  43. package/src/installs/build-essential.js +44 -0
  44. package/src/installs/caffeine.js +44 -0
  45. package/src/installs/camtasia.js +44 -0
  46. package/src/installs/chatgpt.js +44 -0
  47. package/src/installs/chrome-canary.js +44 -0
  48. package/src/installs/chromium.js +44 -0
  49. package/src/installs/claude-code.js +44 -0
  50. package/src/installs/curl.js +44 -0
  51. package/src/installs/cursor.js +44 -0
  52. package/src/installs/dbschema.js +44 -0
  53. package/src/installs/docker.js +44 -0
  54. package/src/installs/drawio.js +44 -0
  55. package/src/installs/elmedia-player.js +44 -0
  56. package/src/installs/ffmpeg.js +44 -0
  57. package/src/installs/gemini-cli.js +44 -0
  58. package/src/installs/git.js +44 -0
  59. package/src/installs/gitego.js +44 -0
  60. package/src/installs/go.js +44 -0
  61. package/src/installs/google-chrome.js +44 -0
  62. package/src/installs/gpg.js +141 -0
  63. package/src/installs/homebrew.js +44 -0
  64. package/src/installs/imageoptim.js +44 -0
  65. package/src/installs/jq.js +44 -0
  66. package/src/installs/keyboard-maestro.js +44 -0
  67. package/src/installs/latex.js +44 -0
  68. package/src/installs/lftp.js +44 -0
  69. package/src/installs/messenger.js +44 -0
  70. package/src/installs/microsoft-office.js +44 -0
  71. package/src/installs/microsoft-teams.js +44 -0
  72. package/src/installs/node.js +44 -0
  73. package/src/installs/nordpass.js +44 -0
  74. package/src/installs/nvm.js +44 -0
  75. package/src/installs/openssh.js +134 -0
  76. package/src/installs/pandoc.js +44 -0
  77. package/src/installs/pinentry.js +44 -0
  78. package/src/installs/pngyu.js +44 -0
  79. package/src/installs/postman.js +44 -0
  80. package/src/installs/safari-tech-preview.js +44 -0
  81. package/src/installs/sfnt2woff.js +44 -0
  82. package/src/installs/shellcheck.js +44 -0
  83. package/src/installs/slack.js +44 -0
  84. package/src/installs/snagit.js +44 -0
  85. package/src/installs/spotify.js +44 -0
  86. package/src/installs/studio-3t.js +44 -0
  87. package/src/installs/sublime-text.js +44 -0
  88. package/src/installs/superwhisper.js +44 -0
  89. package/src/installs/tailscale.js +44 -0
  90. package/src/installs/termius.js +44 -0
  91. package/src/installs/terraform.js +44 -0
  92. package/src/installs/tidal.js +44 -0
  93. package/src/installs/tmux.js +44 -0
  94. package/src/installs/tree.js +44 -0
  95. package/src/installs/vim.js +44 -0
  96. package/src/installs/vlc.js +44 -0
  97. package/src/installs/vscode.js +44 -0
  98. package/src/installs/whatsapp.js +44 -0
  99. package/src/installs/woff2.js +44 -0
  100. package/src/installs/xcode.js +44 -0
  101. package/src/installs/yarn.js +44 -0
  102. package/src/installs/yq.js +44 -0
  103. package/src/installs/yt-dlp.js +44 -0
  104. package/src/installs/zoom.js +44 -0
  105. package/src/scripts/README.md +95 -0
  106. package/src/scripts/afk.js +23 -0
  107. package/src/scripts/backup-all.js +24 -0
  108. package/src/scripts/backup-source.js +24 -0
  109. package/src/scripts/brewd.js +23 -0
  110. package/src/scripts/brewi.js +24 -0
  111. package/src/scripts/brewr.js +24 -0
  112. package/src/scripts/brews.js +24 -0
  113. package/src/scripts/brewu.js +23 -0
  114. package/src/scripts/c.js +23 -0
  115. package/src/scripts/ccurl.js +24 -0
  116. package/src/scripts/certbot-crontab-init.js +24 -0
  117. package/src/scripts/certbot-init.js +25 -0
  118. package/src/scripts/ch.js +23 -0
  119. package/src/scripts/claude-danger.js +23 -0
  120. package/src/scripts/clean-dev.js +24 -0
  121. package/src/scripts/clear-dns-cache.js +23 -0
  122. package/src/scripts/clone.js +25 -0
  123. package/src/scripts/code-all.js +24 -0
  124. package/src/scripts/count-files.js +24 -0
  125. package/src/scripts/count-folders.js +24 -0
  126. package/src/scripts/count.js +24 -0
  127. package/src/scripts/d.js +23 -0
  128. package/src/scripts/datauri.js +24 -0
  129. package/src/scripts/delete-files.js +24 -0
  130. package/src/scripts/docker-clean.js +24 -0
  131. package/src/scripts/dp.js +23 -0
  132. package/src/scripts/e.js +24 -0
  133. package/src/scripts/empty-trash.js +23 -0
  134. package/src/scripts/evm.js +25 -0
  135. package/src/scripts/fetch-github-repos.js +25 -0
  136. package/src/scripts/get-channel.js +24 -0
  137. package/src/scripts/get-course.js +26 -0
  138. package/src/scripts/get-dependencies.js +25 -0
  139. package/src/scripts/get-folder.js +26 -0
  140. package/src/scripts/get-tunes.js +25 -0
  141. package/src/scripts/get-video.js +24 -0
  142. package/src/scripts/git-backup.js +25 -0
  143. package/src/scripts/git-clone.js +25 -0
  144. package/src/scripts/git-pup.js +23 -0
  145. package/src/scripts/git-push.js +24 -0
  146. package/src/scripts/h.js +24 -0
  147. package/src/scripts/hide-desktop-icons.js +23 -0
  148. package/src/scripts/hide-hidden-files.js +23 -0
  149. package/src/scripts/install-dependencies-from.js +25 -0
  150. package/src/scripts/ips.js +26 -0
  151. package/src/scripts/iso.js +24 -0
  152. package/src/scripts/killni.js +23 -0
  153. package/src/scripts/ll.js +24 -0
  154. package/src/scripts/local-ip.js +23 -0
  155. package/src/scripts/m.js +24 -0
  156. package/src/scripts/map.js +24 -0
  157. package/src/scripts/mkd.js +24 -0
  158. package/src/scripts/ncu-update-all.js +24 -0
  159. package/src/scripts/nginx-init.js +28 -0
  160. package/src/scripts/npmi.js +23 -0
  161. package/src/scripts/o.js +24 -0
  162. package/src/scripts/org-by-date.js +24 -0
  163. package/src/scripts/p.js +23 -0
  164. package/src/scripts/packages.js +25 -0
  165. package/src/scripts/path.js +23 -0
  166. package/src/scripts/ports.js +23 -0
  167. package/src/scripts/q.js +23 -0
  168. package/src/scripts/refresh-files.js +26 -0
  169. package/src/scripts/remove-smaller-files.js +24 -0
  170. package/src/scripts/rename-files-with-date.js +25 -0
  171. package/src/scripts/resize-image.js +25 -0
  172. package/src/scripts/rm-safe.js +24 -0
  173. package/src/scripts/s.js +24 -0
  174. package/src/scripts/set-git-public.js +23 -0
  175. package/src/scripts/show-desktop-icons.js +23 -0
  176. package/src/scripts/show-hidden-files.js +23 -0
  177. package/src/scripts/tpa.js +23 -0
  178. package/src/scripts/tpo.js +23 -0
  179. package/src/scripts/u.js +23 -0
  180. package/src/scripts/vpush.js +23 -0
  181. package/src/scripts/y.js +23 -0
  182. package/src/utils/README.md +95 -0
  183. package/src/utils/common/apps.js +143 -0
  184. package/src/utils/common/display.js +157 -0
  185. package/src/utils/common/network.js +185 -0
  186. package/src/utils/common/os.js +202 -0
  187. package/src/utils/common/package-manager.js +301 -0
  188. package/src/utils/common/privileges.js +138 -0
  189. package/src/utils/common/shell.js +195 -0
  190. package/src/utils/macos/apps.js +228 -0
  191. package/src/utils/macos/brew.js +315 -0
  192. package/src/utils/ubuntu/apt.js +301 -0
  193. package/src/utils/ubuntu/desktop.js +292 -0
  194. package/src/utils/ubuntu/snap.js +302 -0
  195. package/src/utils/ubuntu/systemd.js +286 -0
  196. package/src/utils/windows/choco.js +327 -0
  197. package/src/utils/windows/env.js +246 -0
  198. package/src/utils/windows/registry.js +269 -0
  199. package/src/utils/windows/shell.js +240 -0
  200. 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
+ };