nightytidy 0.2.6 → 0.2.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nightytidy",
3
- "version": "0.2.6",
3
+ "version": "0.2.7",
4
4
  "description": "Automated overnight codebase improvement through Claude Code",
5
5
  "license": "MIT",
6
6
  "author": "Dorian Spitz",
@@ -12,6 +12,7 @@ import { WebhookDispatcher } from './webhook-dispatcher.js';
12
12
  import { CliBridge } from './cli-bridge.js';
13
13
  import { AgentGit } from './git-integration.js';
14
14
  import { FirebaseAuth } from './firebase-auth.js';
15
+ import { acquireKeepAwake, releaseKeepAwake } from './keep-awake.js';
15
16
 
16
17
  const FIREBASE_WEBHOOK_URL = 'https://webhookingest-24h6taciuq-uc.a.run.app';
17
18
 
@@ -479,7 +480,10 @@ export async function startAgent() {
479
480
 
480
481
  async function processQueue() {
481
482
  const run = runQueue.dequeue();
482
- if (!run) return;
483
+ if (!run) {
484
+ releaseKeepAwake();
485
+ return;
486
+ }
483
487
 
484
488
  const project = projectManager.getProject(run.projectId);
485
489
  if (!project) {
@@ -497,6 +501,7 @@ export async function startAgent() {
497
501
  runOutputBuffer = '';
498
502
  runProgress = { stepList: [], completedCount: 0, failedCount: 0, totalCost: 0, currentStepNum: null };
499
503
 
504
+ acquireKeepAwake();
500
505
  info(`\n━━━ Run started: ${project.name} ━━━`);
501
506
  info(` Steps: [${run.steps.join(', ')}] (${run.steps.length} total)`);
502
507
  info(` Project: ${project.path}`);
@@ -1036,6 +1041,7 @@ export async function startAgent() {
1036
1041
  // Graceful shutdown
1037
1042
  const shutdown = async () => {
1038
1043
  info('Agent shutting down...');
1044
+ releaseKeepAwake();
1039
1045
  saveInterruptedState();
1040
1046
  scheduler.stopAll();
1041
1047
  await wsServer.stop();
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Prevents the OS from sleeping while NightyTidy runs are active.
3
+ *
4
+ * Windows: Uses PowerShell to call SetThreadExecutionState with
5
+ * ES_CONTINUOUS | ES_SYSTEM_REQUIRED (0x80000001). This tells Windows
6
+ * "don't sleep, this process needs the system." The flag is automatically
7
+ * cleared when the process exits or when releaseKeepAwake() is called.
8
+ *
9
+ * macOS/Linux: Uses caffeinate / systemd-inhibit respectively.
10
+ *
11
+ * No admin privileges required on any platform.
12
+ */
13
+ import { execSync, spawn } from 'node:child_process';
14
+ import { debug, warn } from '../logger.js';
15
+
16
+ let keepAwakeProcess = null;
17
+
18
+ export function acquireKeepAwake() {
19
+ if (keepAwakeProcess) return; // already held
20
+
21
+ try {
22
+ if (process.platform === 'win32') {
23
+ // PowerShell script that sets ES_CONTINUOUS | ES_SYSTEM_REQUIRED
24
+ // and then sleeps forever. When we kill this process, the flag clears.
25
+ keepAwakeProcess = spawn('powershell', [
26
+ '-NoProfile', '-WindowStyle', 'Hidden', '-Command',
27
+ `Add-Type -TypeDefinition 'using System; using System.Runtime.InteropServices; public class SleepPreventer { [DllImport("kernel32.dll")] public static extern uint SetThreadExecutionState(uint esFlags); }'; [SleepPreventer]::SetThreadExecutionState(0x80000001); while($true) { Start-Sleep -Seconds 3600 }`,
28
+ ], { stdio: 'ignore', detached: false });
29
+ keepAwakeProcess.unref();
30
+ keepAwakeProcess.on('error', () => { keepAwakeProcess = null; });
31
+ debug('Sleep prevention acquired (Windows SetThreadExecutionState)');
32
+ } else if (process.platform === 'darwin') {
33
+ // macOS: caffeinate prevents sleep, -i = idle sleep, -s = system sleep
34
+ keepAwakeProcess = spawn('caffeinate', ['-is'], { stdio: 'ignore' });
35
+ keepAwakeProcess.unref();
36
+ keepAwakeProcess.on('error', () => { keepAwakeProcess = null; });
37
+ debug('Sleep prevention acquired (macOS caffeinate)');
38
+ } else {
39
+ // Linux: systemd-inhibit (may not exist on all distros)
40
+ keepAwakeProcess = spawn('systemd-inhibit', [
41
+ '--what=idle:sleep', '--who=NightyTidy', '--why=Running codebase improvement',
42
+ 'sleep', 'infinity',
43
+ ], { stdio: 'ignore' });
44
+ keepAwakeProcess.unref();
45
+ keepAwakeProcess.on('error', () => { keepAwakeProcess = null; });
46
+ debug('Sleep prevention acquired (Linux systemd-inhibit)');
47
+ }
48
+ } catch {
49
+ warn('Could not acquire sleep prevention — system may sleep during runs');
50
+ }
51
+ }
52
+
53
+ export function releaseKeepAwake() {
54
+ if (!keepAwakeProcess) return;
55
+ try {
56
+ keepAwakeProcess.kill();
57
+ } catch { /* already dead */ }
58
+ keepAwakeProcess = null;
59
+ debug('Sleep prevention released');
60
+ }