@factiii/stack 0.1.2 → 0.1.6

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 (33) hide show
  1. package/bin/factiii +13 -0
  2. package/dist/cli/pr-check.d.ts +24 -0
  3. package/dist/cli/pr-check.d.ts.map +1 -0
  4. package/dist/cli/pr-check.js +153 -0
  5. package/dist/cli/pr-check.js.map +1 -0
  6. package/dist/plugins/addons/server-mode/index.d.ts.map +1 -1
  7. package/dist/plugins/addons/server-mode/index.js +3 -0
  8. package/dist/plugins/addons/server-mode/index.js.map +1 -1
  9. package/dist/plugins/addons/server-mode/scanfix/mac.d.ts +20 -3
  10. package/dist/plugins/addons/server-mode/scanfix/mac.d.ts.map +1 -1
  11. package/dist/plugins/addons/server-mode/scanfix/mac.js +291 -176
  12. package/dist/plugins/addons/server-mode/scanfix/mac.js.map +1 -1
  13. package/dist/plugins/addons/server-mode/scanfix/tart.d.ts +19 -0
  14. package/dist/plugins/addons/server-mode/scanfix/tart.d.ts.map +1 -0
  15. package/dist/plugins/addons/server-mode/scanfix/tart.js +350 -0
  16. package/dist/plugins/addons/server-mode/scanfix/tart.js.map +1 -0
  17. package/dist/plugins/pipelines/factiii/pr-check.d.ts +35 -0
  18. package/dist/plugins/pipelines/factiii/pr-check.d.ts.map +1 -0
  19. package/dist/plugins/pipelines/factiii/pr-check.js +202 -0
  20. package/dist/plugins/pipelines/factiii/pr-check.js.map +1 -0
  21. package/dist/plugins/pipelines/factiii/utils/workflows.d.ts.map +1 -1
  22. package/dist/plugins/pipelines/factiii/utils/workflows.js +1 -0
  23. package/dist/plugins/pipelines/factiii/utils/workflows.js.map +1 -1
  24. package/dist/plugins/pipelines/factiii/workflows/factiii-cicd-staging.yml +8 -3
  25. package/dist/plugins/pipelines/factiii/workflows/factiii-pr-check.yml +103 -0
  26. package/dist/plugins/servers/mac/staging.d.ts.map +1 -1
  27. package/dist/plugins/servers/mac/staging.js +304 -52
  28. package/dist/plugins/servers/mac/staging.js.map +1 -1
  29. package/dist/utils/github-status.d.ts +39 -0
  30. package/dist/utils/github-status.d.ts.map +1 -0
  31. package/dist/utils/github-status.js +172 -0
  32. package/dist/utils/github-status.js.map +1 -0
  33. package/package.json +3 -3
@@ -5,13 +5,30 @@
5
5
  * Fixes for configuring Mac as a deployment server HOST (no Docker/dev tools).
6
6
  * Focus: what makes a Mac reliable as a server.
7
7
  *
8
+ * Power Management (pmset):
8
9
  * - Disable sleep (system, disk, display)
9
10
  * - Auto-restart on power loss
10
- * - Don't turn off when inactive (disablesleep)
11
- * - Disable screensaver
11
+ * - Fully disable sleep (disablesleep)
12
+ * - Wake on LAN (womp)
13
+ * - Disable hibernate
14
+ * - Disable standby
15
+ * - Disable Power Nap
16
+ * - Disable proximity wake
17
+ * - Keep awake during SSH (ttyskeepawake)
18
+ *
19
+ * System Services:
20
+ * - Disable screensaver (-currentHost)
12
21
  * - Enable SSH (Remote Login)
13
22
  * - Disable App Nap
14
- * - Auto-login on boot (optional)
23
+ * - Disable Spotlight indexing
24
+ * - Disable Time Machine
25
+ * - Disable auto-update restart
26
+ * - Disable Bluetooth (manual)
27
+ * - Auto-login on boot (manual)
28
+ *
29
+ * Network/Security:
30
+ * - Enable NTP (network time)
31
+ * - Disable file sharing (manual)
15
32
  */
16
33
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
17
34
  if (k2 === undefined) k2 = k;
@@ -50,14 +67,39 @@ Object.defineProperty(exports, "__esModule", { value: true });
50
67
  exports.macFixes = void 0;
51
68
  const child_process_1 = require("child_process");
52
69
  const fs = __importStar(require("fs"));
70
+ function macFixPair(def) {
71
+ return ['staging', 'prod'].map(stage => ({
72
+ id: def.idBase + '-' + stage,
73
+ stage,
74
+ os: 'mac',
75
+ severity: def.severity,
76
+ description: def.description,
77
+ scan: def.scan,
78
+ fix: def.fix,
79
+ manualFix: def.manualFix,
80
+ }));
81
+ }
82
+ // ============================================================
83
+ // Helper: check a pmset value
84
+ // ============================================================
85
+ function pmsetValueIs(key, expected) {
86
+ try {
87
+ const result = (0, child_process_1.execSync)('pmset -g 2>/dev/null | grep -i ' + key + ' || true', {
88
+ encoding: 'utf8',
89
+ stdio: ['pipe', 'pipe', 'pipe'],
90
+ });
91
+ return result.trim().includes(expected);
92
+ }
93
+ catch {
94
+ return false;
95
+ }
96
+ }
53
97
  exports.macFixes = [
54
98
  // ============================================================
55
- // STAGING FIXES
99
+ // POWER MANAGEMENT (pmset)
56
100
  // ============================================================
57
- {
58
- id: 'macos-sleep-enabled-staging',
59
- stage: 'staging',
60
- os: 'mac',
101
+ ...macFixPair({
102
+ idBase: 'macos-sleep-enabled',
61
103
  severity: 'warning',
62
104
  description: 'macOS sleep is enabled (server may go offline)',
63
105
  scan: async (_config, _rootDir) => {
@@ -66,11 +108,10 @@ exports.macFixes = [
66
108
  encoding: 'utf8',
67
109
  stdio: ['pipe', 'pipe', 'pipe'],
68
110
  });
69
- // Check if sleep is not 0
70
111
  return !result.includes('sleep 0');
71
112
  }
72
113
  catch {
73
- return false; // Can't determine, assume OK
114
+ return false;
74
115
  }
75
116
  },
76
117
  fix: async (_config, _rootDir) => {
@@ -84,139 +125,113 @@ exports.macFixes = [
84
125
  }
85
126
  },
86
127
  manualFix: 'Run: sudo pmset -a sleep 0 disksleep 0 displaysleep 0',
87
- },
88
- {
89
- id: 'macos-screensaver-enabled-staging',
90
- stage: 'staging',
91
- os: 'mac',
92
- severity: 'info',
93
- description: 'macOS screensaver is enabled',
128
+ }),
129
+ ...macFixPair({
130
+ idBase: 'macos-autorestart-disabled',
131
+ severity: 'critical',
132
+ description: 'Auto-restart on power loss is disabled (server may stay off after outage)',
94
133
  scan: async (_config, _rootDir) => {
95
134
  try {
96
- const result = (0, child_process_1.execSync)('defaults read com.apple.screensaver idleTime 2>/dev/null || echo "300"', {
135
+ const result = (0, child_process_1.execSync)('pmset -g 2>/dev/null | grep -i autorestart || true', {
97
136
  encoding: 'utf8',
137
+ stdio: ['pipe', 'pipe', 'pipe'],
98
138
  });
99
- const idleTime = parseInt(result.trim(), 10);
100
- return idleTime > 0;
139
+ return !result.trim().includes('1');
101
140
  }
102
141
  catch {
103
- return false;
142
+ return true;
104
143
  }
105
144
  },
106
145
  fix: async (_config, _rootDir) => {
107
146
  try {
108
- console.log(' Disabling macOS screensaver...');
109
- (0, child_process_1.execSync)('defaults write com.apple.screensaver idleTime 0', { stdio: 'inherit' });
147
+ console.log(' Enabling auto-restart on power loss...');
148
+ (0, child_process_1.execSync)('sudo pmset -a autorestart 1', { stdio: 'inherit' });
110
149
  return true;
111
150
  }
112
151
  catch {
113
152
  return false;
114
153
  }
115
154
  },
116
- manualFix: 'Run: defaults write com.apple.screensaver idleTime 0',
117
- },
118
- {
119
- id: 'macos-ssh-disabled-staging',
120
- stage: 'staging',
121
- os: 'mac',
122
- severity: 'critical',
123
- description: 'macOS Remote Login (SSH) is disabled',
155
+ manualFix: 'Run: sudo pmset -a autorestart 1',
156
+ }),
157
+ ...macFixPair({
158
+ idBase: 'macos-disablesleep-disabled',
159
+ severity: 'warning',
160
+ description: 'System sleep is not fully disabled (disablesleep=0 allows Sleep menu)',
124
161
  scan: async (_config, _rootDir) => {
125
162
  try {
126
- const result = (0, child_process_1.execSync)('sudo systemsetup -getremotelogin 2>/dev/null', {
163
+ const result = (0, child_process_1.execSync)('pmset -g custom 2>/dev/null | grep -i disablesleep || true', {
127
164
  encoding: 'utf8',
165
+ stdio: ['pipe', 'pipe', 'pipe'],
128
166
  });
129
- return result.toLowerCase().includes('off');
167
+ return !result.trim().includes('1');
130
168
  }
131
169
  catch {
132
- return false;
170
+ return true;
133
171
  }
134
172
  },
135
173
  fix: async (_config, _rootDir) => {
136
174
  try {
137
- console.log(' Enabling macOS Remote Login (SSH)...');
138
- (0, child_process_1.execSync)('sudo systemsetup -setremotelogin on', { stdio: 'inherit' });
175
+ console.log(' Disabling system sleep (server mode)...');
176
+ (0, child_process_1.execSync)('sudo pmset -a disablesleep 1', { stdio: 'inherit' });
139
177
  return true;
140
178
  }
141
179
  catch {
142
180
  return false;
143
181
  }
144
182
  },
145
- manualFix: 'Run: sudo systemsetup -setremotelogin on',
146
- },
147
- {
148
- id: 'macos-app-nap-enabled-staging',
149
- stage: 'staging',
150
- os: 'mac',
151
- severity: 'info',
152
- description: 'macOS App Nap may pause background processes',
183
+ manualFix: 'Run: sudo pmset -a disablesleep 1',
184
+ }),
185
+ ...macFixPair({
186
+ idBase: 'macos-wake-on-lan',
187
+ severity: 'warning',
188
+ description: 'Wake on LAN is disabled (cannot remotely wake server via magic packet)',
153
189
  scan: async (_config, _rootDir) => {
154
- try {
155
- const result = (0, child_process_1.execSync)('defaults read NSGlobalDomain NSAppSleepDisabled 2>/dev/null || echo "0"', {
156
- encoding: 'utf8',
157
- });
158
- return result.trim() !== '1';
159
- }
160
- catch {
161
- return true; // If can't read, assume App Nap is enabled
162
- }
190
+ return !pmsetValueIs('womp', '1');
163
191
  },
164
192
  fix: async (_config, _rootDir) => {
165
193
  try {
166
- console.log(' Disabling macOS App Nap...');
167
- (0, child_process_1.execSync)('defaults write NSGlobalDomain NSAppSleepDisabled -bool YES', { stdio: 'inherit' });
194
+ console.log(' Enabling Wake on LAN...');
195
+ (0, child_process_1.execSync)('sudo pmset -a womp 1', { stdio: 'inherit' });
168
196
  return true;
169
197
  }
170
198
  catch {
171
199
  return false;
172
200
  }
173
201
  },
174
- manualFix: 'Run: defaults write NSGlobalDomain NSAppSleepDisabled -bool YES',
175
- },
176
- {
177
- id: 'macos-autorestart-disabled-staging',
178
- stage: 'staging',
179
- os: 'mac',
202
+ manualFix: 'Run: sudo pmset -a womp 1 (requires Ethernet and compatible hardware)',
203
+ }),
204
+ ...macFixPair({
205
+ idBase: 'macos-hibernate-enabled',
180
206
  severity: 'warning',
181
- description: 'Auto-restart on power loss is disabled (server may stay off after outage)',
207
+ description: 'Hibernate mode is enabled (server writes RAM to disk and powers off)',
182
208
  scan: async (_config, _rootDir) => {
183
- try {
184
- const result = (0, child_process_1.execSync)('pmset -g 2>/dev/null | grep -i autorestart || true', {
185
- encoding: 'utf8',
186
- stdio: ['pipe', 'pipe', 'pipe'],
187
- });
188
- // Problem if autorestart is 0 or line doesn't contain 1
189
- return !result.trim().includes('1');
190
- }
191
- catch {
192
- return true; // Assume needs fix if we can't read
193
- }
209
+ return !pmsetValueIs('hibernatemode', '0');
194
210
  },
195
211
  fix: async (_config, _rootDir) => {
196
212
  try {
197
- console.log(' Enabling auto-restart on power loss...');
198
- (0, child_process_1.execSync)('sudo pmset -a autorestart 1', { stdio: 'inherit' });
213
+ console.log(' Disabling hibernate mode...');
214
+ (0, child_process_1.execSync)('sudo pmset -a hibernatemode 0', { stdio: 'inherit' });
199
215
  return true;
200
216
  }
201
217
  catch {
202
218
  return false;
203
219
  }
204
220
  },
205
- manualFix: 'Run: sudo pmset -a autorestart 1',
206
- },
207
- {
208
- id: 'macos-disablesleep-disabled-staging',
209
- stage: 'staging',
210
- os: 'mac',
211
- severity: 'info',
212
- description: 'System sleep is not fully disabled (disablesleep=0 allows Sleep menu)',
221
+ manualFix: 'Run: sudo pmset -a hibernatemode 0',
222
+ }),
223
+ ...macFixPair({
224
+ idBase: 'macos-standby-enabled',
225
+ severity: 'warning',
226
+ description: 'Standby mode is enabled (server enters deep sleep after extended idle)',
213
227
  scan: async (_config, _rootDir) => {
214
228
  try {
215
- const result = (0, child_process_1.execSync)('pmset -g custom 2>/dev/null | grep -i disablesleep || true', {
229
+ // Use 'standby ' with trailing space to avoid matching standbydelayhigh/standbydelaylow
230
+ const result = (0, child_process_1.execSync)('pmset -g 2>/dev/null | grep -E "^\\s*standby\\s+" || true', {
216
231
  encoding: 'utf8',
217
232
  stdio: ['pipe', 'pipe', 'pipe'],
218
233
  });
219
- return !result.trim().includes('1');
234
+ return !result.trim().includes('0');
220
235
  }
221
236
  catch {
222
237
  return true;
@@ -224,77 +239,78 @@ exports.macFixes = [
224
239
  },
225
240
  fix: async (_config, _rootDir) => {
226
241
  try {
227
- console.log(' Disabling system sleep (server mode)...');
228
- (0, child_process_1.execSync)('sudo pmset -a disablesleep 1', { stdio: 'inherit' });
242
+ console.log(' Disabling standby mode...');
243
+ (0, child_process_1.execSync)('sudo pmset -a standby 0', { stdio: 'inherit' });
229
244
  return true;
230
245
  }
231
246
  catch {
232
247
  return false;
233
248
  }
234
249
  },
235
- manualFix: 'Run: sudo pmset -a disablesleep 1',
236
- },
237
- {
238
- id: 'macos-autologin-disabled-staging',
239
- stage: 'staging',
240
- os: 'mac',
250
+ manualFix: 'Run: sudo pmset -a standby 0',
251
+ }),
252
+ ...macFixPair({
253
+ idBase: 'macos-powernap-enabled',
241
254
  severity: 'info',
242
- description: 'Auto-login on boot is not configured (may require manual login after power loss)',
255
+ description: 'Power Nap is enabled (wastes CPU for iCloud/Mail checks on server)',
243
256
  scan: async (_config, _rootDir) => {
257
+ return !pmsetValueIs('powernap', '0');
258
+ },
259
+ fix: async (_config, _rootDir) => {
244
260
  try {
245
- const plist = '/Library/Preferences/com.apple.loginwindow.plist';
246
- if (!fs.existsSync(plist))
247
- return true;
248
- const result = (0, child_process_1.execSync)(`defaults read ${plist} autoLoginUser 2>/dev/null || echo ""`, {
249
- encoding: 'utf8',
250
- });
251
- return !result.trim();
261
+ console.log(' Disabling Power Nap...');
262
+ (0, child_process_1.execSync)('sudo pmset -a powernap 0', { stdio: 'inherit' });
263
+ return true;
252
264
  }
253
265
  catch {
254
- return true;
266
+ return false;
255
267
  }
256
268
  },
257
- fix: null,
258
- manualFix: 'Set auto-login: System Settings > Users & Groups > Login Options > Automatic login > select user. ' +
259
- 'Or: sudo defaults write /Library/Preferences/com.apple.loginwindow autoLoginUser -string "admin" (then reboot)',
260
- },
261
- // ============================================================
262
- // PROD FIXES (same as staging)
263
- // ============================================================
264
- {
265
- id: 'macos-sleep-enabled-prod',
266
- stage: 'prod',
267
- os: 'mac',
268
- severity: 'warning',
269
- description: 'macOS sleep is enabled (server may go offline)',
269
+ manualFix: 'Run: sudo pmset -a powernap 0',
270
+ }),
271
+ ...macFixPair({
272
+ idBase: 'macos-proximitywake-enabled',
273
+ severity: 'info',
274
+ description: 'Proximity wake is enabled (nearby Apple devices can wake the Mac)',
270
275
  scan: async (_config, _rootDir) => {
276
+ return !pmsetValueIs('proximitywake', '0');
277
+ },
278
+ fix: async (_config, _rootDir) => {
271
279
  try {
272
- const result = (0, child_process_1.execSync)('pmset -g | grep -E "^\\s*sleep\\s+"', {
273
- encoding: 'utf8',
274
- stdio: ['pipe', 'pipe', 'pipe'],
275
- });
276
- return !result.includes('sleep 0');
280
+ console.log(' Disabling proximity wake...');
281
+ (0, child_process_1.execSync)('sudo pmset -a proximitywake 0', { stdio: 'inherit' });
282
+ return true;
277
283
  }
278
284
  catch {
279
285
  return false;
280
286
  }
281
287
  },
288
+ manualFix: 'Run: sudo pmset -a proximitywake 0',
289
+ }),
290
+ ...macFixPair({
291
+ idBase: 'macos-ttyskeepawake-disabled',
292
+ severity: 'warning',
293
+ description: 'TTY keep-awake is disabled (Mac may sleep during active SSH sessions)',
294
+ scan: async (_config, _rootDir) => {
295
+ return !pmsetValueIs('ttyskeepawake', '1');
296
+ },
282
297
  fix: async (_config, _rootDir) => {
283
298
  try {
284
- console.log(' Disabling macOS sleep...');
285
- (0, child_process_1.execSync)('sudo pmset -a sleep 0 disksleep 0 displaysleep 0', { stdio: 'inherit' });
299
+ console.log(' Enabling TTY keep-awake...');
300
+ (0, child_process_1.execSync)('sudo pmset -a ttyskeepawake 1', { stdio: 'inherit' });
286
301
  return true;
287
302
  }
288
303
  catch {
289
304
  return false;
290
305
  }
291
306
  },
292
- manualFix: 'Run: sudo pmset -a sleep 0 disksleep 0 displaysleep 0',
293
- },
294
- {
295
- id: 'macos-ssh-disabled-prod',
296
- stage: 'prod',
297
- os: 'mac',
307
+ manualFix: 'Run: sudo pmset -a ttyskeepawake 1',
308
+ }),
309
+ // ============================================================
310
+ // SYSTEM SERVICES
311
+ // ============================================================
312
+ ...macFixPair({
313
+ idBase: 'macos-ssh-disabled',
298
314
  severity: 'critical',
299
315
  description: 'macOS Remote Login (SSH) is disabled',
300
316
  scan: async (_config, _rootDir) => {
@@ -319,19 +335,18 @@ exports.macFixes = [
319
335
  }
320
336
  },
321
337
  manualFix: 'Run: sudo systemsetup -setremotelogin on',
322
- },
323
- {
324
- id: 'macos-screensaver-enabled-prod',
325
- stage: 'prod',
326
- os: 'mac',
338
+ }),
339
+ ...macFixPair({
340
+ idBase: 'macos-screensaver-enabled',
327
341
  severity: 'info',
328
- description: 'macOS screensaver is enabled',
342
+ description: 'macOS screensaver is enabled (wasted GPU cycles on headless host)',
329
343
  scan: async (_config, _rootDir) => {
330
344
  try {
331
- const result = (0, child_process_1.execSync)('defaults read com.apple.screensaver idleTime 2>/dev/null || echo "300"', {
345
+ const result = (0, child_process_1.execSync)('defaults -currentHost read com.apple.screensaver idleTime 2>/dev/null || echo "300"', {
332
346
  encoding: 'utf8',
333
347
  });
334
- return parseInt(result.trim(), 10) > 0;
348
+ const idleTime = parseInt(result.trim(), 10);
349
+ return idleTime > 0;
335
350
  }
336
351
  catch {
337
352
  return false;
@@ -339,19 +354,18 @@ exports.macFixes = [
339
354
  },
340
355
  fix: async (_config, _rootDir) => {
341
356
  try {
342
- (0, child_process_1.execSync)('defaults write com.apple.screensaver idleTime 0', { stdio: 'inherit' });
357
+ console.log(' Disabling macOS screensaver...');
358
+ (0, child_process_1.execSync)('defaults -currentHost write com.apple.screensaver idleTime 0', { stdio: 'inherit' });
343
359
  return true;
344
360
  }
345
361
  catch {
346
362
  return false;
347
363
  }
348
364
  },
349
- manualFix: 'Run: defaults write com.apple.screensaver idleTime 0',
350
- },
351
- {
352
- id: 'macos-app-nap-enabled-prod',
353
- stage: 'prod',
354
- os: 'mac',
365
+ manualFix: 'Run: defaults -currentHost write com.apple.screensaver idleTime 0',
366
+ }),
367
+ ...macFixPair({
368
+ idBase: 'macos-app-nap-enabled',
355
369
  severity: 'info',
356
370
  description: 'macOS App Nap may pause background processes',
357
371
  scan: async (_config, _rootDir) => {
@@ -367,6 +381,7 @@ exports.macFixes = [
367
381
  },
368
382
  fix: async (_config, _rootDir) => {
369
383
  try {
384
+ console.log(' Disabling macOS App Nap...');
370
385
  (0, child_process_1.execSync)('defaults write NSGlobalDomain NSAppSleepDisabled -bool YES', { stdio: 'inherit' });
371
386
  return true;
372
387
  }
@@ -375,77 +390,117 @@ exports.macFixes = [
375
390
  }
376
391
  },
377
392
  manualFix: 'Run: defaults write NSGlobalDomain NSAppSleepDisabled -bool YES',
378
- },
379
- {
380
- id: 'macos-autorestart-disabled-prod',
381
- stage: 'prod',
382
- os: 'mac',
383
- severity: 'warning',
384
- description: 'Auto-restart on power loss is disabled',
393
+ }),
394
+ ...macFixPair({
395
+ idBase: 'macos-spotlight-enabled',
396
+ severity: 'info',
397
+ description: 'Spotlight indexing is enabled (burns CPU/disk on headless server)',
385
398
  scan: async (_config, _rootDir) => {
386
399
  try {
387
- const result = (0, child_process_1.execSync)('pmset -g 2>/dev/null | grep -i autorestart || true', {
400
+ const result = (0, child_process_1.execSync)('mdutil -s / 2>/dev/null', {
388
401
  encoding: 'utf8',
389
- stdio: ['pipe', 'pipe', 'pipe'],
390
402
  });
391
- return !result.trim().includes('1');
403
+ return result.toLowerCase().includes('indexing enabled');
392
404
  }
393
405
  catch {
394
- return true;
406
+ return false;
395
407
  }
396
408
  },
397
409
  fix: async (_config, _rootDir) => {
398
410
  try {
399
- (0, child_process_1.execSync)('sudo pmset -a autorestart 1', { stdio: 'inherit' });
411
+ console.log(' Disabling Spotlight indexing...');
412
+ (0, child_process_1.execSync)('sudo mdutil -a -i off', { stdio: 'inherit' });
400
413
  return true;
401
414
  }
402
415
  catch {
403
416
  return false;
404
417
  }
405
418
  },
406
- manualFix: 'Run: sudo pmset -a autorestart 1',
407
- },
408
- {
409
- id: 'macos-disablesleep-disabled-prod',
410
- stage: 'prod',
411
- os: 'mac',
419
+ manualFix: 'Run: sudo mdutil -a -i off',
420
+ }),
421
+ ...macFixPair({
422
+ idBase: 'macos-timemachine-enabled',
412
423
  severity: 'info',
413
- description: 'System sleep is not fully disabled',
424
+ description: 'Time Machine is enabled (causes periodic heavy disk I/O)',
414
425
  scan: async (_config, _rootDir) => {
415
426
  try {
416
- const result = (0, child_process_1.execSync)('pmset -g custom 2>/dev/null | grep -i disablesleep || true', {
427
+ const result = (0, child_process_1.execSync)('defaults read /Library/Preferences/com.apple.TimeMachine AutoBackup 2>/dev/null || echo "0"', {
417
428
  encoding: 'utf8',
418
- stdio: ['pipe', 'pipe', 'pipe'],
419
429
  });
420
- return !result.trim().includes('1');
430
+ return result.trim() === '1';
421
431
  }
422
432
  catch {
433
+ return false;
434
+ }
435
+ },
436
+ fix: async (_config, _rootDir) => {
437
+ try {
438
+ console.log(' Disabling Time Machine...');
439
+ (0, child_process_1.execSync)('sudo tmutil disable', { stdio: 'inherit' });
423
440
  return true;
424
441
  }
442
+ catch {
443
+ return false;
444
+ }
445
+ },
446
+ manualFix: 'Run: sudo tmutil disable',
447
+ }),
448
+ ...macFixPair({
449
+ idBase: 'macos-autoupdate-restart',
450
+ severity: 'critical',
451
+ description: 'macOS auto-update may reboot the server without warning',
452
+ scan: async (_config, _rootDir) => {
453
+ try {
454
+ const result = (0, child_process_1.execSync)('defaults read /Library/Preferences/com.apple.SoftwareUpdate AutomaticallyInstallMacOSUpdates 2>/dev/null || echo "0"', {
455
+ encoding: 'utf8',
456
+ });
457
+ return result.trim() === '1';
458
+ }
459
+ catch {
460
+ return false;
461
+ }
425
462
  },
426
463
  fix: async (_config, _rootDir) => {
427
464
  try {
428
- (0, child_process_1.execSync)('sudo pmset -a disablesleep 1', { stdio: 'inherit' });
465
+ console.log(' Disabling automatic macOS update installs...');
466
+ (0, child_process_1.execSync)('sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate AutomaticallyInstallMacOSUpdates -bool false', { stdio: 'inherit' });
429
467
  return true;
430
468
  }
431
469
  catch {
432
470
  return false;
433
471
  }
434
472
  },
435
- manualFix: 'Run: sudo pmset -a disablesleep 1',
436
- },
437
- {
438
- id: 'macos-autologin-disabled-prod',
439
- stage: 'prod',
440
- os: 'mac',
473
+ manualFix: 'Run: sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate AutomaticallyInstallMacOSUpdates -bool false',
474
+ }),
475
+ ...macFixPair({
476
+ idBase: 'macos-bluetooth-enabled',
477
+ severity: 'info',
478
+ description: 'Bluetooth is enabled (unnecessary attack surface on headless server)',
479
+ scan: async (_config, _rootDir) => {
480
+ try {
481
+ const result = (0, child_process_1.execSync)('defaults read /Library/Preferences/com.apple.Bluetooth ControllerPowerState 2>/dev/null || echo "1"', {
482
+ encoding: 'utf8',
483
+ });
484
+ return result.trim() !== '0';
485
+ }
486
+ catch {
487
+ return false;
488
+ }
489
+ },
490
+ fix: null,
491
+ manualFix: 'Disable Bluetooth (only if no BT keyboard/mouse): System Settings > Bluetooth > Turn Off. ' +
492
+ 'Or: sudo defaults write /Library/Preferences/com.apple.Bluetooth ControllerPowerState -int 0 && sudo killall -HUP bluetoothd',
493
+ }),
494
+ ...macFixPair({
495
+ idBase: 'macos-autologin-disabled',
441
496
  severity: 'info',
442
- description: 'Auto-login on boot is not configured',
497
+ description: 'Auto-login on boot is not configured (may require manual login after power loss)',
443
498
  scan: async (_config, _rootDir) => {
444
499
  try {
445
500
  const plist = '/Library/Preferences/com.apple.loginwindow.plist';
446
501
  if (!fs.existsSync(plist))
447
502
  return true;
448
- const result = (0, child_process_1.execSync)(`defaults read ${plist} autoLoginUser 2>/dev/null || echo ""`, {
503
+ const result = (0, child_process_1.execSync)('defaults read ' + plist + ' autoLoginUser 2>/dev/null || echo ""', {
449
504
  encoding: 'utf8',
450
505
  });
451
506
  return !result.trim();
@@ -455,7 +510,67 @@ exports.macFixes = [
455
510
  }
456
511
  },
457
512
  fix: null,
458
- manualFix: 'System Settings > Users & Groups > Login Options > Automatic login. Or: sudo defaults write /Library/Preferences/com.apple.loginwindow autoLoginUser -string "admin"',
459
- },
513
+ manualFix: 'Set auto-login: System Settings > Users & Groups > Login Options > Automatic login > select user. ' +
514
+ 'Or: sudo defaults write /Library/Preferences/com.apple.loginwindow autoLoginUser -string "admin" (then reboot)',
515
+ }),
516
+ // ============================================================
517
+ // NETWORK / SECURITY
518
+ // ============================================================
519
+ ...macFixPair({
520
+ idBase: 'macos-ntp-disabled',
521
+ severity: 'warning',
522
+ description: 'Network time (NTP) is disabled (accurate time needed for TLS, logs, cron)',
523
+ scan: async (_config, _rootDir) => {
524
+ try {
525
+ const result = (0, child_process_1.execSync)('sudo systemsetup -getusingnetworktime 2>/dev/null', {
526
+ encoding: 'utf8',
527
+ });
528
+ return result.toLowerCase().includes('off');
529
+ }
530
+ catch {
531
+ return false;
532
+ }
533
+ },
534
+ fix: async (_config, _rootDir) => {
535
+ try {
536
+ console.log(' Enabling network time (NTP)...');
537
+ (0, child_process_1.execSync)('sudo systemsetup -setusingnetworktime on', { stdio: 'inherit' });
538
+ return true;
539
+ }
540
+ catch {
541
+ return false;
542
+ }
543
+ },
544
+ manualFix: 'Run: sudo systemsetup -setusingnetworktime on',
545
+ }),
546
+ ...macFixPair({
547
+ idBase: 'macos-file-sharing-enabled',
548
+ severity: 'info',
549
+ description: 'File sharing (AFP/SMB) is enabled (unnecessary attack surface on server)',
550
+ scan: async (_config, _rootDir) => {
551
+ try {
552
+ // Check if smbd is running (SMB file sharing)
553
+ (0, child_process_1.execSync)('launchctl list 2>/dev/null | grep com.apple.smbd', {
554
+ stdio: ['pipe', 'pipe', 'pipe'],
555
+ });
556
+ return true;
557
+ }
558
+ catch {
559
+ // smbd not running, check AFP
560
+ try {
561
+ const result = (0, child_process_1.execSync)('defaults read /Library/Preferences/com.apple.AppleFileServer guestAccess 2>/dev/null || echo "0"', {
562
+ encoding: 'utf8',
563
+ });
564
+ return result.trim() === '1';
565
+ }
566
+ catch {
567
+ return false;
568
+ }
569
+ }
570
+ },
571
+ fix: null,
572
+ manualFix: 'Disable file sharing: System Settings > General > Sharing > File Sharing > Off. ' +
573
+ 'Only disable if file sharing is not intentionally used.',
574
+ }),
460
575
  ];
461
576
  //# sourceMappingURL=mac.js.map