@masuidrive/procman 0.1.2 → 0.2.0

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 (204) hide show
  1. package/dist/src/cli/commands/help.d.ts.map +1 -1
  2. package/dist/src/cli/commands/help.js +3 -1
  3. package/dist/src/cli/commands/help.js.map +1 -1
  4. package/dist/src/cli/index.js +6 -2
  5. package/dist/src/cli/index.js.map +1 -1
  6. package/dist/src/cli/parser.d.ts.map +1 -1
  7. package/dist/src/cli/parser.js +20 -0
  8. package/dist/src/cli/parser.js.map +1 -1
  9. package/dist/src/cli/utils/find-package-json.d.ts +4 -0
  10. package/dist/src/cli/utils/find-package-json.d.ts.map +1 -0
  11. package/dist/src/cli/utils/find-package-json.js +15 -0
  12. package/dist/src/cli/utils/find-package-json.js.map +1 -0
  13. package/dist/src/config/config-loader.d.ts +1 -0
  14. package/dist/src/config/config-loader.d.ts.map +1 -1
  15. package/dist/src/config/config-loader.js +16 -6
  16. package/dist/src/config/config-loader.js.map +1 -1
  17. package/dist/src/config/config-validator.d.ts +18 -49
  18. package/dist/src/config/config-validator.d.ts.map +1 -1
  19. package/dist/src/config/config-validator.js +64 -172
  20. package/dist/src/config/config-validator.js.map +1 -1
  21. package/dist/src/config/path-security-validator.d.ts +39 -0
  22. package/dist/src/config/path-security-validator.d.ts.map +1 -0
  23. package/dist/src/config/path-security-validator.js +134 -0
  24. package/dist/src/config/path-security-validator.js.map +1 -0
  25. package/dist/src/config/secure-config-loader.d.ts.map +1 -1
  26. package/dist/src/config/secure-config-loader.js +10 -2
  27. package/dist/src/config/secure-config-loader.js.map +1 -1
  28. package/dist/src/config/validation-suggestions.d.ts +13 -0
  29. package/dist/src/config/validation-suggestions.d.ts.map +1 -0
  30. package/dist/src/config/validation-suggestions.js +40 -0
  31. package/dist/src/config/validation-suggestions.js.map +1 -0
  32. package/dist/src/config/validation-types.d.ts +38 -0
  33. package/dist/src/config/validation-types.d.ts.map +1 -0
  34. package/dist/src/config/validation-types.js +7 -0
  35. package/dist/src/config/validation-types.js.map +1 -0
  36. package/dist/src/daemon/component-command-wiring.d.ts +19 -0
  37. package/dist/src/daemon/component-command-wiring.d.ts.map +1 -0
  38. package/dist/src/daemon/component-command-wiring.js +27 -0
  39. package/dist/src/daemon/component-command-wiring.js.map +1 -0
  40. package/dist/src/daemon/component-daemon-interface.d.ts +38 -0
  41. package/dist/src/daemon/component-daemon-interface.d.ts.map +1 -0
  42. package/dist/src/daemon/component-daemon-interface.js +72 -0
  43. package/dist/src/daemon/component-daemon-interface.js.map +1 -0
  44. package/dist/src/daemon/component-health.d.ts +38 -0
  45. package/dist/src/daemon/component-health.d.ts.map +1 -0
  46. package/dist/src/daemon/component-health.js +146 -0
  47. package/dist/src/daemon/component-health.js.map +1 -0
  48. package/dist/src/daemon/component-manager-errors.d.ts +20 -0
  49. package/dist/src/daemon/component-manager-errors.d.ts.map +1 -0
  50. package/dist/src/daemon/component-manager-errors.js +30 -0
  51. package/dist/src/daemon/component-manager-errors.js.map +1 -0
  52. package/dist/src/daemon/component-manager-types.d.ts +23 -0
  53. package/dist/src/daemon/component-manager-types.d.ts.map +1 -0
  54. package/dist/src/daemon/component-manager-types.js +5 -0
  55. package/dist/src/daemon/component-manager-types.js.map +1 -0
  56. package/dist/src/daemon/component-manager.d.ts +16 -64
  57. package/dist/src/daemon/component-manager.d.ts.map +1 -1
  58. package/dist/src/daemon/component-manager.js +72 -509
  59. package/dist/src/daemon/component-manager.js.map +1 -1
  60. package/dist/src/daemon/component-registration.d.ts +14 -0
  61. package/dist/src/daemon/component-registration.d.ts.map +1 -0
  62. package/dist/src/daemon/component-registration.js +92 -0
  63. package/dist/src/daemon/component-registration.js.map +1 -0
  64. package/dist/src/daemon/component-streaming.d.ts +36 -0
  65. package/dist/src/daemon/component-streaming.d.ts.map +1 -0
  66. package/dist/src/daemon/component-streaming.js +112 -0
  67. package/dist/src/daemon/component-streaming.js.map +1 -0
  68. package/dist/src/daemon/daemon-lifecycle.d.ts +37 -0
  69. package/dist/src/daemon/daemon-lifecycle.d.ts.map +1 -0
  70. package/dist/src/daemon/daemon-lifecycle.js +362 -0
  71. package/dist/src/daemon/daemon-lifecycle.js.map +1 -0
  72. package/dist/src/daemon/daemon-queries.d.ts +53 -0
  73. package/dist/src/daemon/daemon-queries.d.ts.map +1 -0
  74. package/dist/src/daemon/daemon-queries.js +113 -0
  75. package/dist/src/daemon/daemon-queries.js.map +1 -0
  76. package/dist/src/daemon/daemon-types.d.ts +30 -0
  77. package/dist/src/daemon/daemon-types.d.ts.map +1 -0
  78. package/dist/src/daemon/daemon-types.js +8 -0
  79. package/dist/src/daemon/daemon-types.js.map +1 -0
  80. package/dist/src/daemon/disposable-base.d.ts +49 -0
  81. package/dist/src/daemon/disposable-base.d.ts.map +1 -0
  82. package/dist/src/daemon/disposable-base.js +70 -0
  83. package/dist/src/daemon/disposable-base.js.map +1 -0
  84. package/dist/src/daemon/disposal-guard.d.ts +27 -0
  85. package/dist/src/daemon/disposal-guard.d.ts.map +1 -0
  86. package/dist/src/daemon/disposal-guard.js +58 -0
  87. package/dist/src/daemon/disposal-guard.js.map +1 -0
  88. package/dist/src/daemon/ipc-command-handler.d.ts +8 -34
  89. package/dist/src/daemon/ipc-command-handler.d.ts.map +1 -1
  90. package/dist/src/daemon/ipc-command-handler.js +14 -362
  91. package/dist/src/daemon/ipc-command-handler.js.map +1 -1
  92. package/dist/src/daemon/log-command-handlers.d.ts +19 -0
  93. package/dist/src/daemon/log-command-handlers.d.ts.map +1 -0
  94. package/dist/src/daemon/log-command-handlers.js +114 -0
  95. package/dist/src/daemon/log-command-handlers.js.map +1 -0
  96. package/dist/src/daemon/process-command-handlers.d.ts +32 -0
  97. package/dist/src/daemon/process-command-handlers.d.ts.map +1 -0
  98. package/dist/src/daemon/process-command-handlers.js +223 -0
  99. package/dist/src/daemon/process-command-handlers.js.map +1 -0
  100. package/dist/src/daemon/procman-daemon.d.ts +22 -98
  101. package/dist/src/daemon/procman-daemon.d.ts.map +1 -1
  102. package/dist/src/daemon/procman-daemon.js +107 -604
  103. package/dist/src/daemon/procman-daemon.js.map +1 -1
  104. package/dist/src/daemon/resource-manager.d.ts +6 -124
  105. package/dist/src/daemon/resource-manager.d.ts.map +1 -1
  106. package/dist/src/daemon/resource-manager.js +5 -231
  107. package/dist/src/daemon/resource-manager.js.map +1 -1
  108. package/dist/src/daemon/resource-types.d.ts +23 -0
  109. package/dist/src/daemon/resource-types.d.ts.map +1 -0
  110. package/dist/src/daemon/resource-types.js +7 -0
  111. package/dist/src/daemon/resource-types.js.map +1 -0
  112. package/dist/src/daemon/shutdown-orchestrator.d.ts +32 -0
  113. package/dist/src/daemon/shutdown-orchestrator.d.ts.map +1 -0
  114. package/dist/src/daemon/shutdown-orchestrator.js +123 -0
  115. package/dist/src/daemon/shutdown-orchestrator.js.map +1 -0
  116. package/dist/src/daemon/tracked-resources.d.ts +60 -0
  117. package/dist/src/daemon/tracked-resources.d.ts.map +1 -0
  118. package/dist/src/daemon/tracked-resources.js +124 -0
  119. package/dist/src/daemon/tracked-resources.js.map +1 -0
  120. package/dist/src/process-manager/index.d.ts +5 -1
  121. package/dist/src/process-manager/index.d.ts.map +1 -1
  122. package/dist/src/process-manager/index.js +3 -0
  123. package/dist/src/process-manager/index.js.map +1 -1
  124. package/dist/src/process-manager/interfaces/process-group.d.ts +6 -0
  125. package/dist/src/process-manager/interfaces/process-group.d.ts.map +1 -1
  126. package/dist/src/process-manager/managed-process-events.d.ts +22 -0
  127. package/dist/src/process-manager/managed-process-events.d.ts.map +1 -0
  128. package/dist/src/process-manager/managed-process-events.js +5 -0
  129. package/dist/src/process-manager/managed-process-events.js.map +1 -0
  130. package/dist/src/process-manager/managed-process-info.d.ts +11 -53
  131. package/dist/src/process-manager/managed-process-info.d.ts.map +1 -1
  132. package/dist/src/process-manager/managed-process-info.js +46 -133
  133. package/dist/src/process-manager/managed-process-info.js.map +1 -1
  134. package/dist/src/process-manager/process-batch-operations.d.ts +62 -0
  135. package/dist/src/process-manager/process-batch-operations.d.ts.map +1 -0
  136. package/dist/src/process-manager/process-batch-operations.js +80 -0
  137. package/dist/src/process-manager/process-batch-operations.js.map +1 -0
  138. package/dist/src/process-manager/process-config.d.ts +49 -0
  139. package/dist/src/process-manager/process-config.d.ts.map +1 -0
  140. package/dist/src/process-manager/process-config.js +68 -0
  141. package/dist/src/process-manager/process-config.js.map +1 -0
  142. package/dist/src/process-manager/process-event-forwarding.d.ts +18 -0
  143. package/dist/src/process-manager/process-event-forwarding.d.ts.map +1 -0
  144. package/dist/src/process-manager/process-event-forwarding.js +105 -0
  145. package/dist/src/process-manager/process-event-forwarding.js.map +1 -0
  146. package/dist/src/process-manager/process-group-manager.d.ts.map +1 -1
  147. package/dist/src/process-manager/process-group-manager.js +0 -1
  148. package/dist/src/process-manager/process-group-manager.js.map +1 -1
  149. package/dist/src/process-manager/process-info-queries.d.ts +48 -0
  150. package/dist/src/process-manager/process-info-queries.d.ts.map +1 -0
  151. package/dist/src/process-manager/process-info-queries.js +92 -0
  152. package/dist/src/process-manager/process-info-queries.js.map +1 -0
  153. package/dist/src/process-manager/process-manager.d.ts +5 -174
  154. package/dist/src/process-manager/process-manager.d.ts.map +1 -1
  155. package/dist/src/process-manager/process-manager.js +58 -398
  156. package/dist/src/process-manager/process-manager.js.map +1 -1
  157. package/dist/src/process-manager/process-monitoring.d.ts +80 -0
  158. package/dist/src/process-manager/process-monitoring.d.ts.map +1 -0
  159. package/dist/src/process-manager/process-monitoring.js +142 -0
  160. package/dist/src/process-manager/process-monitoring.js.map +1 -0
  161. package/dist/src/process-manager/process-statistics.d.ts +37 -0
  162. package/dist/src/process-manager/process-statistics.d.ts.map +1 -0
  163. package/dist/src/process-manager/process-statistics.js +24 -0
  164. package/dist/src/process-manager/process-statistics.js.map +1 -0
  165. package/dist/src/process-manager/restart-manager.d.ts +82 -0
  166. package/dist/src/process-manager/restart-manager.d.ts.map +1 -0
  167. package/dist/src/process-manager/restart-manager.js +150 -0
  168. package/dist/src/process-manager/restart-manager.js.map +1 -0
  169. package/dist/src/services/app-logger.d.ts +144 -0
  170. package/dist/src/services/app-logger.d.ts.map +1 -0
  171. package/dist/src/services/app-logger.js +489 -0
  172. package/dist/src/services/app-logger.js.map +1 -0
  173. package/dist/src/services/log-buffer.d.ts +35 -0
  174. package/dist/src/services/log-buffer.d.ts.map +1 -0
  175. package/dist/src/services/log-buffer.js +87 -0
  176. package/dist/src/services/log-buffer.js.map +1 -0
  177. package/dist/src/services/log-event-manager.d.ts +32 -0
  178. package/dist/src/services/log-event-manager.d.ts.map +1 -0
  179. package/dist/src/services/log-event-manager.js +31 -0
  180. package/dist/src/services/log-event-manager.js.map +1 -0
  181. package/dist/src/services/log-file-manager.d.ts +46 -0
  182. package/dist/src/services/log-file-manager.d.ts.map +1 -0
  183. package/dist/src/services/log-file-manager.js +156 -0
  184. package/dist/src/services/log-file-manager.js.map +1 -0
  185. package/dist/src/services/log-level-strategy.d.ts +19 -0
  186. package/dist/src/services/log-level-strategy.d.ts.map +1 -0
  187. package/dist/src/services/log-level-strategy.js +40 -0
  188. package/dist/src/services/log-level-strategy.js.map +1 -0
  189. package/dist/src/services/log-manager-types.d.ts +117 -0
  190. package/dist/src/services/log-manager-types.d.ts.map +1 -0
  191. package/dist/src/services/log-manager-types.js +12 -0
  192. package/dist/src/services/log-manager-types.js.map +1 -0
  193. package/dist/src/services/log-manager.d.ts +8 -35
  194. package/dist/src/services/log-manager.d.ts.map +1 -1
  195. package/dist/src/services/log-manager.js +10 -870
  196. package/dist/src/services/log-manager.js.map +1 -1
  197. package/dist/src/services/log-preprocessor.d.ts +30 -0
  198. package/dist/src/services/log-preprocessor.d.ts.map +1 -0
  199. package/dist/src/services/log-preprocessor.js +65 -0
  200. package/dist/src/services/log-preprocessor.js.map +1 -0
  201. package/dist/src/services/process-log-integration.d.ts +7 -2
  202. package/dist/src/services/process-log-integration.d.ts.map +1 -1
  203. package/dist/src/services/process-log-integration.js.map +1 -1
  204. package/package.json +5 -7
@@ -7,6 +7,12 @@
7
7
  * - ComponentManager: Component lifecycle management
8
8
  * - SignalHandler: Process signal handling
9
9
  * - DataDirectory & PIDManager: File system management
10
+ *
11
+ * Implementation is split across:
12
+ * - daemon-lifecycle.ts: Startup, stop, restart, recovery logic
13
+ * - shutdown-orchestrator.ts: Graceful shutdown phases and connection draining
14
+ * - daemon-queries.ts: Query/status methods and component accessors
15
+ * - daemon-types.ts: Shared DaemonContext interface
10
16
  */
11
17
  import { EventEmitter } from 'events';
12
18
  import { DataDirectory } from './data-directory.js';
@@ -15,16 +21,17 @@ import { DaemonStateManager, DaemonState } from './daemon-state-manager.js';
15
21
  import { ComponentManager } from './component-manager.js';
16
22
  import { SignalHandler } from './signal-handler.js';
17
23
  import { MemoryMonitor } from '../utils/memory/memory-monitor.js';
18
- import path from 'path';
24
+ // Lifecycle functions
25
+ import { performStart, performStop, performAttemptRecovery, performEmergencyCleanup as _performEmergencyCleanup, } from './daemon-lifecycle.js';
26
+ // Shutdown orchestration helpers
27
+ import { saveShutdownState as _saveShutdownState, getActiveConnectionCount as _getActiveConnectionCount, drainActiveConnections as _drainActiveConnections, stopManagedProcesses as _stopManagedProcesses, executeWithTimeout as _executeWithTimeout, } from './shutdown-orchestrator.js';
28
+ // Query functions
29
+ import { queryIsReady, queryHealthStatus, queryLoadConfig, queryAllProcessStatuses, getConfigLoader as _getConfigLoader, getProcessManager as _getProcessManager, getLogManager as _getLogManager, getIPCServer as _getIPCServer, } from './daemon-queries.js';
19
30
  // Re-export DaemonState for backward compatibility
20
31
  export { DaemonState };
21
- /**
22
- * Recovery constants
23
- */
24
- const RECOVERY_CONSTANTS = {
25
- MAX_RECOVERY_ATTEMPTS: 3,
26
- RECOVERY_DELAY_MS: 5000,
27
- };
32
+ const debugLog = process.env.DEBUG_PROCMAN
33
+ ? (...args) => console.error(...args)
34
+ : () => { };
28
35
  /**
29
36
  * Main daemon class that coordinates all procman components
30
37
  */
@@ -38,8 +45,7 @@ export class ProcmanDaemon extends EventEmitter {
38
45
  currentConfig;
39
46
  configFilePath;
40
47
  recoveryAttempts = 0;
41
- maxRecoveryAttempts = RECOVERY_CONSTANTS.MAX_RECOVERY_ATTEMPTS;
42
- isShuttingDown = false; // Add shutdown flag
48
+ isShuttingDown = false;
43
49
  constructor() {
44
50
  super();
45
51
  this.dataDirectory = new DataDirectory(process.env.PROCMAN_SOCKET_PATH);
@@ -48,244 +54,82 @@ export class ProcmanDaemon extends EventEmitter {
48
54
  this.componentManager = new ComponentManager(this.dataDirectory);
49
55
  this.signalHandler = new SignalHandler();
50
56
  this.memoryMonitor = new MemoryMonitor({
51
- intervalMs: 30000, // 30 seconds (PM2 standard)
52
- warningThreshold: 100 * 1024 * 1024, // 100MB
53
- criticalThreshold: 200 * 1024 * 1024, // 200MB
54
- enableLogging: process.env.NODE_ENV !== 'test', // Disable logging in test environment
57
+ intervalMs: 30000,
58
+ warningThreshold: 100 * 1024 * 1024,
59
+ criticalThreshold: 200 * 1024 * 1024,
60
+ enableLogging: process.env.NODE_ENV !== 'test',
55
61
  });
56
62
  this.setupEventListeners();
57
63
  }
58
- /**
59
- * Get current daemon state
60
- */
64
+ /** Build the DaemonContext for delegation to sub-modules */
65
+ getContext() {
66
+ // Use a proxy-like object so mutable fields stay in sync with the class
67
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
68
+ const daemon = this;
69
+ return {
70
+ dataDirectory: this.dataDirectory,
71
+ pidManager: this.pidManager,
72
+ stateManager: this.stateManager,
73
+ componentManager: this.componentManager,
74
+ signalHandler: this.signalHandler,
75
+ memoryMonitor: this.memoryMonitor,
76
+ get currentConfig() {
77
+ return daemon.currentConfig;
78
+ },
79
+ set currentConfig(v) {
80
+ daemon.currentConfig = v;
81
+ },
82
+ get configFilePath() {
83
+ return daemon.configFilePath;
84
+ },
85
+ set configFilePath(v) {
86
+ daemon.configFilePath = v;
87
+ },
88
+ get recoveryAttempts() {
89
+ return daemon.recoveryAttempts;
90
+ },
91
+ set recoveryAttempts(v) {
92
+ daemon.recoveryAttempts = v;
93
+ },
94
+ get isShuttingDown() {
95
+ return daemon.isShuttingDown;
96
+ },
97
+ set isShuttingDown(v) {
98
+ daemon.isShuttingDown = v;
99
+ },
100
+ };
101
+ }
102
+ // ── State queries ──────────────────────────────────────────────────
61
103
  getState() {
62
104
  return this.stateManager.getCurrentState();
63
105
  }
64
- /**
65
- * Check if daemon is currently running
66
- */
67
106
  isRunning() {
68
107
  return this.stateManager.isRunning();
69
108
  }
70
- /**
71
- * Check if daemon is ready to handle requests
72
- * Performs comprehensive health checks on all components
73
- */
74
109
  async isReady() {
75
- if (!this.isRunning()) {
76
- return false;
77
- }
78
- try {
79
- const healthCheck = await this.componentManager.performHealthChecks();
80
- return healthCheck.healthy;
81
- }
82
- catch (error) {
83
- console.error('Health check failed:', error);
84
- return false;
85
- }
110
+ return queryIsReady(this.getContext());
86
111
  }
87
- /**
88
- * Get detailed health status of all components
89
- */
90
112
  async getHealthStatus() {
91
- const healthCheck = await this.componentManager.performHealthChecks();
92
- const memoryHealthInfo = this.memoryMonitor.getHealthInfo();
93
- return {
94
- ready: healthCheck.healthy && this.isRunning(),
95
- components: healthCheck.details,
96
- memory: memoryHealthInfo,
97
- };
113
+ return queryHealthStatus(this.getContext());
98
114
  }
99
- /**
100
- * Start the daemon
101
- */
115
+ // ── Lifecycle ──────────────────────────────────────────────────────
102
116
  async start() {
103
- const startTime = Date.now();
104
- console.error('[DEBUG-PROCMAN-DAEMON] Starting daemon initialization...');
105
- console.error('[DEBUG-PROCMAN-DAEMON] Current state check:', JSON.stringify({
106
- currentState: this.getState(),
107
- canStart: this.stateManager.canStart(),
108
- processId: process.pid,
109
- timestamp: new Date().toISOString(),
110
- }, null, 2));
111
- if (!this.stateManager.canStart()) {
112
- throw new Error(`Cannot start daemon: current state is ${this.getState()}`);
113
- }
114
- console.error('[DEBUG-PROCMAN-DAEMON] Transitioning to STARTING state...');
115
- this.stateManager.transitionTo(DaemonState.STARTING);
116
- try {
117
- console.error('[DEBUG-PROCMAN-DAEMON] Step 1: Performing environment checks...');
118
- // Perform environment checks before any other operations
119
- await this.performEnvironmentChecks();
120
- console.error('[DEBUG-PROCMAN-DAEMON] ✓ Environment checks completed');
121
- console.error('[DEBUG-PROCMAN-DAEMON] Step 2: Ensuring no daemon running...');
122
- // Check for existing daemon
123
- await this.pidManager.ensureNoDaemonRunning();
124
- console.error('[DEBUG-PROCMAN-DAEMON] ✓ Daemon uniqueness verified');
125
- console.error('[DEBUG-PROCMAN-DAEMON] Step 3: Initializing data directory...');
126
- // Initialize data directory
127
- await this.dataDirectory.ensureDataDirectory();
128
- console.error('[DEBUG-PROCMAN-DAEMON] ✓ Data directory ready');
129
- console.error('[DEBUG-PROCMAN-DAEMON] Step 4: Writing PID file...');
130
- // Write PID file
131
- await this.pidManager.writePIDFile();
132
- console.error('[DEBUG-PROCMAN-DAEMON] ✓ PID file written');
133
- console.error('[DEBUG-PROCMAN-DAEMON] Step 5: Setting up signal handlers...');
134
- // Setup signal handlers
135
- this.signalHandler.setupHandlers();
136
- console.error('[DEBUG-PROCMAN-DAEMON] ✓ Signal handlers configured');
137
- console.error('[DEBUG-PROCMAN-DAEMON] Step 6: Initializing all components...');
138
- // Initialize components
139
- await this.componentManager.initializeAll();
140
- console.error('[DEBUG-PROCMAN-DAEMON] ✓ All components initialized');
141
- // Perform final readiness check with retries
142
- console.error('[DEBUG-PROCMAN-DAEMON] Step 7: Performing final readiness verification...');
143
- let readinessAttempts = 0;
144
- const maxReadinessAttempts = 10;
145
- const readinessCheckInterval = 500;
146
- console.error('[DEBUG-PROCMAN-DAEMON] Readiness check parameters:', JSON.stringify({
147
- maxAttempts: maxReadinessAttempts,
148
- intervalMs: readinessCheckInterval,
149
- totalMaxTimeMs: maxReadinessAttempts * readinessCheckInterval,
150
- }, null, 2));
151
- while (readinessAttempts < maxReadinessAttempts) {
152
- const checkStartTime = Date.now();
153
- console.error(`[DEBUG-PROCMAN-DAEMON] Readiness attempt ${readinessAttempts + 1}/${maxReadinessAttempts}...`);
154
- try {
155
- const healthCheck = await this.componentManager.performHealthChecks();
156
- const checkDuration = Date.now() - checkStartTime;
157
- if (healthCheck.healthy) {
158
- console.error('[DEBUG-PROCMAN-DAEMON] ✓ Readiness verification successful!');
159
- console.error('[DEBUG-PROCMAN-DAEMON] Readiness success stats:', JSON.stringify({
160
- attempts: readinessAttempts + 1,
161
- checkDurationMs: checkDuration,
162
- healthCheckDetails: healthCheck.details,
163
- }, null, 2));
164
- console.log(`Daemon readiness verified after ${readinessAttempts + 1} attempts`);
165
- break;
166
- }
167
- else {
168
- console.error('[DEBUG-PROCMAN-DAEMON] ✗ Readiness check failed');
169
- console.error('[DEBUG-PROCMAN-DAEMON] Health check failure details:', JSON.stringify({
170
- attempt: readinessAttempts + 1,
171
- maxAttempts: maxReadinessAttempts,
172
- checkDurationMs: checkDuration,
173
- healthCheckDetails: healthCheck.details,
174
- }, null, 2));
175
- console.log(`Readiness check ${readinessAttempts + 1}/${maxReadinessAttempts} failed:`, healthCheck.details);
176
- readinessAttempts++;
177
- if (readinessAttempts < maxReadinessAttempts) {
178
- console.error(`[DEBUG-PROCMAN-DAEMON] Waiting ${readinessCheckInterval}ms before retry...`);
179
- await new Promise((resolve) => setTimeout(resolve, readinessCheckInterval));
180
- }
181
- }
182
- }
183
- catch (error) {
184
- const checkDuration = Date.now() - checkStartTime;
185
- console.error('[DEBUG-PROCMAN-DAEMON] ✗ Readiness check exception');
186
- console.error('[DEBUG-PROCMAN-DAEMON] Exception details:', JSON.stringify({
187
- attempt: readinessAttempts + 1,
188
- checkDurationMs: checkDuration,
189
- errorMessage: error instanceof Error ? error.message : String(error),
190
- errorStack: error instanceof Error ? error.stack : undefined,
191
- }, null, 2));
192
- console.log(`Readiness check ${readinessAttempts + 1}/${maxReadinessAttempts} failed with error:`, error);
193
- readinessAttempts++;
194
- if (readinessAttempts < maxReadinessAttempts) {
195
- await new Promise((resolve) => setTimeout(resolve, readinessCheckInterval));
196
- }
197
- }
198
- }
199
- if (readinessAttempts >= maxReadinessAttempts) {
200
- console.error('[DEBUG-PROCMAN-DAEMON] ✗ Final readiness verification failed after all attempts');
201
- console.error('[DEBUG-PROCMAN-DAEMON] Readiness failure summary:', JSON.stringify({
202
- totalAttempts: readinessAttempts,
203
- maxAttempts: maxReadinessAttempts,
204
- totalTimeSpentMs: readinessAttempts * readinessCheckInterval,
205
- currentState: this.getState(),
206
- }, null, 2));
207
- throw new Error('Daemon components initialized but failed final readiness verification');
208
- }
209
- // All checks passed - transition to running state
210
- console.error('[DEBUG-PROCMAN-DAEMON] Step 8: Transitioning to RUNNING state...');
211
- this.stateManager.transitionTo(DaemonState.RUNNING);
212
- this.recoveryAttempts = 0; // Reset recovery counter on successful start
213
- // Start memory monitoring
214
- console.error('[DEBUG-PROCMAN-DAEMON] Step 9: Starting memory monitoring...');
215
- this.memoryMonitor.start();
216
- console.error('[DEBUG-PROCMAN-DAEMON] ✓ Memory monitoring started');
217
- const totalStartupTime = Date.now() - startTime;
218
- console.error('[DEBUG-PROCMAN-DAEMON] ✓ DAEMON STARTUP COMPLETED SUCCESSFULLY!');
219
- console.error('[DEBUG-PROCMAN-DAEMON] Final startup stats:', JSON.stringify({
220
- totalStartupTimeMs: totalStartupTime,
221
- currentState: this.getState(),
222
- processId: process.pid,
223
- readinessAttempts: readinessAttempts,
224
- timestamp: new Date().toISOString(),
225
- }, null, 2));
226
- console.log('Daemon started successfully and is ready to handle requests');
227
- }
228
- catch (error) {
229
- const totalStartupTime = Date.now() - startTime;
230
- console.error('[DEBUG-PROCMAN-DAEMON] ✗ DAEMON STARTUP FAILED');
231
- console.error('[DEBUG-PROCMAN-DAEMON] Startup failure stats:', JSON.stringify({
232
- totalStartupTimeMs: totalStartupTime,
233
- currentState: this.getState(),
234
- processId: process.pid,
235
- errorMessage: error instanceof Error ? error.message : String(error),
236
- errorStack: error instanceof Error ? error.stack : undefined,
237
- }, null, 2));
238
- this.stateManager.forceError();
239
- console.error('[DEBUG-PROCMAN-DAEMON] Performing startup cleanup...');
240
- // Cleanup on startup failure with better error handling
241
- await this.performStartupCleanup();
242
- console.error('[DEBUG-PROCMAN-DAEMON] Startup cleanup completed');
243
- throw error;
244
- }
117
+ return performStart(this.getContext());
245
118
  }
246
- /**
247
- * Stop the daemon
248
- */
249
119
  async stop() {
250
- if (!this.stateManager.canStop()) {
251
- throw new Error(`Cannot stop daemon: current state is ${this.getState()}`);
252
- }
253
- this.stateManager.transitionTo(DaemonState.STOPPING);
254
- try {
255
- // Stop memory monitoring first
256
- console.error('[DEBUG-PROCMAN-DAEMON] Stopping memory monitoring...');
257
- this.memoryMonitor.stop();
258
- console.error('[DEBUG-PROCMAN-DAEMON] ✓ Memory monitoring stopped');
259
- // Stop components with proper error handling
260
- await this.componentManager.cleanupAll();
261
- // Remove signal handlers
262
- this.signalHandler.cleanupHandlers();
263
- // Clean up PID file
264
- await this.pidManager.cleanup();
265
- this.stateManager.transitionTo(DaemonState.STOPPED);
266
- }
267
- catch (error) {
268
- this.stateManager.forceError();
269
- // Still try to cleanup critical resources
270
- await this.performEmergencyCleanup();
271
- throw error;
272
- }
120
+ return performStop(this.getContext());
273
121
  }
274
- /**
275
- * Restart the daemon
276
- */
277
122
  async restart() {
278
123
  if (this.isRunning()) {
279
124
  await this.stop();
280
125
  }
281
126
  await this.start();
282
127
  }
283
- /**
284
- * Graceful shutdown with extended capabilities and state preservation
285
- *
286
- * @param reason The reason for shutdown
287
- * @param timeoutMs Maximum time to wait for graceful shutdown (default: 30s)
288
- */
128
+ async attemptRecovery() {
129
+ const emitFn = (event, ...args) => super.emit(event, ...args);
130
+ return performAttemptRecovery(this.getContext(), emitFn, this.loadConfig.bind(this));
131
+ }
132
+ // ── Graceful shutdown ──────────────────────────────────────────────
289
133
  async shutdown(reason = 'manual', timeoutMs = 30000) {
290
134
  if (this.isShuttingDown) {
291
135
  console.log('[ProcmanDaemon] Shutdown already in progress, waiting...');
@@ -295,36 +139,29 @@ export class ProcmanDaemon extends EventEmitter {
295
139
  const shutdownStart = Date.now();
296
140
  console.log(`[ProcmanDaemon] Beginning graceful shutdown (reason: ${reason}, timeout: ${timeoutMs}ms)`);
297
141
  try {
298
- // Phase 1: Set shutdown flag and reject new connections (5s timeout)
299
142
  await this.executeWithTimeout(async () => {
300
143
  console.log('[ProcmanDaemon] Phase 1: Setting shutdown flag and rejecting new connections...');
301
144
  this.emit('shutdownStarted', reason);
302
145
  const ipcServer = this.getIPCServer();
303
146
  if (ipcServer) {
304
- // Reject new connections by stopping the server from accepting
305
- // Note: We don't stop the server yet to allow existing connections to drain
306
147
  this.emit('newConnectionsRejected');
307
148
  }
308
149
  }, 5000, 'Phase 1: Shutdown initialization');
309
- // Phase 2: Save shutdown state (5s timeout)
310
150
  await this.executeWithTimeout(async () => {
311
151
  console.log('[ProcmanDaemon] Phase 2: Saving shutdown state...');
312
152
  await this.saveShutdownState(reason);
313
153
  }, 5000, 'Phase 2: State preservation');
314
- // Phase 3: Drain active connections (10s timeout)
315
154
  await this.executeWithTimeout(async () => {
316
155
  console.log('[ProcmanDaemon] Phase 3: Draining active connections...');
317
156
  await this.drainActiveConnections();
318
157
  }, 10000, 'Phase 3: Connection draining');
319
- // Phase 4: Stop processes gracefully (15s timeout)
320
158
  await this.executeWithTimeout(async () => {
321
159
  console.log('[ProcmanDaemon] Phase 4: Stopping managed processes...');
322
160
  await this.stopManagedProcesses();
323
161
  }, 15000, 'Phase 4: Process termination');
324
- // Phase 5: Cleanup resources (5s timeout)
325
162
  await this.executeWithTimeout(async () => {
326
163
  console.log('[ProcmanDaemon] Phase 5: Final resource cleanup...');
327
- await this.stop(); // Use existing stop method for final cleanup
164
+ await this.stop();
328
165
  }, 5000, 'Phase 5: Resource cleanup');
329
166
  const totalTime = Date.now() - shutdownStart;
330
167
  console.log(`[ProcmanDaemon] Graceful shutdown completed successfully in ${totalTime}ms`);
@@ -332,351 +169,75 @@ export class ProcmanDaemon extends EventEmitter {
332
169
  }
333
170
  catch (error) {
334
171
  const totalTime = Date.now() - shutdownStart;
335
- console.error(`[ProcmanDaemon] Graceful shutdown failed after ${totalTime}ms:`, error);
336
- // Force shutdown if graceful shutdown fails
172
+ debugLog(`[ProcmanDaemon] Graceful shutdown failed after ${totalTime}ms:`, error);
337
173
  console.log('[ProcmanDaemon] Attempting forced shutdown...');
338
174
  await this.performEmergencyCleanup();
339
- this.emit('shutdownFailed', reason, error, totalTime);
175
+ this.emit('shutdownFailed', reason, error instanceof Error ? error : new Error(String(error)), totalTime);
340
176
  throw error;
341
177
  }
342
178
  finally {
343
179
  this.isShuttingDown = false;
344
180
  }
345
181
  }
346
- /**
347
- * Save shutdown state to disk for recovery/analysis
348
- */
182
+ /** Save shutdown state to disk (delegates to shutdown-orchestrator) */
349
183
  async saveShutdownState(reason) {
350
- try {
351
- const processManager = this.getProcessManager();
352
- const state = {
353
- timestamp: new Date().toISOString(),
354
- reason,
355
- processId: process.pid,
356
- memoryUsage: process.memoryUsage(),
357
- processes: processManager ? await this.getAllProcessStatuses() : [],
358
- activeConnections: this.getActiveConnectionCount(),
359
- uptime: process.uptime(),
360
- version: process.version,
361
- platform: process.platform,
362
- arch: process.arch,
363
- };
364
- const statePath = path.join(this.dataDirectory.getDataDir(), 'shutdown-state.json');
365
- const fs = await import('fs');
366
- // Ensure directory exists before writing
367
- const dir = path.dirname(statePath);
368
- await fs.promises.mkdir(dir, { recursive: true });
369
- await fs.promises.writeFile(statePath, JSON.stringify(state, null, 2), 'utf-8');
370
- console.log(`[ProcmanDaemon] Shutdown state saved to: ${statePath}`);
371
- }
372
- catch (error) {
373
- // Non-critical error - log but don't fail shutdown
374
- console.error('[ProcmanDaemon] Failed to save shutdown state:', error);
375
- }
184
+ return _saveShutdownState(this.dataDirectory, reason, this.getProcessManager(), this.getAllProcessStatuses.bind(this), this.getActiveConnectionCount());
376
185
  }
377
- /**
378
- * Get count of active IPC connections
379
- */
186
+ /** Get count of active IPC connections */
380
187
  getActiveConnectionCount() {
381
- const ipcServer = this.getIPCServer();
382
- if (!ipcServer) {
383
- return 0;
384
- }
385
- return ipcServer.getConnections().length;
188
+ return _getActiveConnectionCount(this.getIPCServer());
386
189
  }
387
- /**
388
- * Drain active IPC connections gracefully
389
- */
190
+ /** Drain active IPC connections gracefully */
390
191
  async drainActiveConnections() {
391
- const ipcServer = this.getIPCServer();
392
- if (!ipcServer) {
393
- console.log('[ProcmanDaemon] No IPC server to drain connections from');
394
- return;
395
- }
396
- const connections = ipcServer.getConnections();
397
- if (connections.length === 0) {
398
- console.log('[ProcmanDaemon] No active connections to drain');
399
- return;
400
- }
401
- console.log(`[ProcmanDaemon] Draining ${connections.length} active connections...`);
402
- // Send shutdown notice to all connections
403
- ipcServer.broadcast({
404
- id: `shutdown-notice-${Date.now()}`,
405
- type: 'ping', // Use existing command type for compatibility
406
- payload: {
407
- shutdownNotice: true,
408
- reason: 'graceful-shutdown',
409
- gracePeriodMs: 8000, // Give clients 8 seconds to cleanup
410
- },
411
- timestamp: Date.now(),
412
- });
413
- // Wait for connections to close gracefully
414
- const drainStartTime = Date.now();
415
- const maxDrainTime = 8000; // 8 seconds for clients to disconnect
416
- while (ipcServer.getConnections().length > 0 &&
417
- Date.now() - drainStartTime < maxDrainTime) {
418
- await new Promise((resolve) => setTimeout(resolve, 100)); // Check every 100ms
419
- }
420
- const remainingConnections = ipcServer.getConnections().length;
421
- if (remainingConnections > 0) {
422
- console.warn(`[ProcmanDaemon] ${remainingConnections} connections did not close gracefully, will force close`);
423
- }
424
- else {
425
- console.log('[ProcmanDaemon] All connections drained successfully');
426
- }
192
+ return _drainActiveConnections(this.getIPCServer());
427
193
  }
428
- /**
429
- * Stop all managed processes gracefully
430
- */
194
+ /** Stop all managed processes gracefully */
431
195
  async stopManagedProcesses() {
432
- const processManager = this.getProcessManager();
433
- if (!processManager) {
434
- console.log('[ProcmanDaemon] No process manager to stop processes from');
435
- return;
436
- }
437
- const allProcesses = processManager.getAllProcessInfo();
438
- if (allProcesses.length === 0) {
439
- console.log('[ProcmanDaemon] No managed processes to stop');
440
- return;
441
- }
442
- console.log(`[ProcmanDaemon] Stopping ${allProcesses.length} managed processes gracefully...`);
443
- const processNames = allProcesses.map((p) => p.name);
444
- await processManager.stopProcesses(processNames);
445
- console.log('[ProcmanDaemon] All managed processes stopped');
446
- }
447
- /**
448
- * Execute a function with timeout
449
- */
450
- async executeWithTimeout(fn, timeoutMs, phaseName) {
451
- return Promise.race([
452
- fn(),
453
- new Promise((_, reject) => setTimeout(() => reject(new Error(`${phaseName} timed out after ${timeoutMs}ms`)), timeoutMs)),
454
- ]);
455
- }
456
- /**
457
- * Format bytes to human-readable string
458
- */
459
- formatBytes(bytes) {
460
- const units = ['B', 'KB', 'MB', 'GB'];
461
- let size = Number(bytes);
462
- let unitIndex = 0;
463
- if (!isFinite(size) || size < 0) {
464
- return '0B';
465
- }
466
- while (size >= 1024 && unitIndex < units.length - 1) {
467
- size /= 1024;
468
- unitIndex++;
469
- }
470
- return `${size.toFixed(1)}${units[unitIndex]}`;
196
+ return _stopManagedProcesses(this.getProcessManager());
471
197
  }
472
- /**
473
- * Perform environment checks before daemon startup
474
- */
475
- async performEnvironmentChecks() {
476
- // Check 1: Verify HOME environment variable is set
477
- const homeDir = process.env.HOME || process.env.USERPROFILE;
478
- if (!homeDir) {
479
- // Try to detect home directory using os.homedir()
480
- try {
481
- const os = await import('os');
482
- const detectedHome = os.homedir();
483
- if (!detectedHome) {
484
- throw new Error('HOME environment variable is not set and could not detect home directory');
485
- }
486
- // Set HOME for this process and child processes
487
- process.env.HOME = detectedHome;
488
- console.log(`HOME environment variable was missing, set to: ${detectedHome}`);
489
- }
490
- catch (error) {
491
- throw new Error(`Failed to determine home directory: ${error instanceof Error ? error.message : 'Unknown error'}`);
492
- }
493
- }
494
- // Check 2: Verify data directory path can be resolved
495
- try {
496
- const resolvedPath = this.dataDirectory.resolveDataDir();
497
- if (!resolvedPath || resolvedPath.includes('~')) {
498
- throw new Error(`Failed to expand socket path: ${resolvedPath}. HOME environment variable may not be set.`);
499
- }
500
- }
501
- catch (error) {
502
- throw new Error(`Failed to resolve data directory path: ${error instanceof Error ? error.message : 'Unknown error'}`);
503
- }
504
- // Check 3: Verify socket path can be resolved
505
- try {
506
- const socketPath = await this.dataDirectory.getSocketPath();
507
- if (!socketPath) {
508
- throw new Error('Failed to resolve socket path');
509
- }
510
- // On Unix systems, verify the path doesn't contain unresolved ~ characters
511
- if (process.platform !== 'win32' && socketPath.includes('~')) {
512
- throw new Error(`Socket path contains unresolved tilde: ${socketPath}`);
513
- }
514
- }
515
- catch (error) {
516
- throw new Error(`Failed to resolve socket path: ${error instanceof Error ? error.message : 'Unknown error'}`);
517
- }
518
- // Check 4: Verify we can create the data directory (dry run check)
519
- try {
520
- await this.dataDirectory.validateDataDirectory();
521
- // If validation passes, directory already exists with correct permissions
522
- }
523
- catch {
524
- // Directory doesn't exist or has wrong permissions - that's OK, we'll create it later
525
- // But we should verify we have permission to create it
526
- const path = await import('path');
527
- const fs = await import('fs');
528
- const parentDir = path.dirname(this.dataDirectory.getDataDir());
529
- try {
530
- await fs.promises.access(parentDir, fs.constants.W_OK);
531
- }
532
- catch (accessError) {
533
- throw new Error(`Cannot write to parent directory ${parentDir}: ${accessError instanceof Error ? accessError.message : 'Permission denied'}`);
534
- }
535
- }
536
- // Check 5: Verify basic Node.js runtime environment
537
- if (!process.pid) {
538
- throw new Error('Invalid process environment: PID not available');
539
- }
540
- // Check 6: Verify required modules can be loaded
541
- try {
542
- await import('fs');
543
- await import('path');
544
- await import('os');
545
- }
546
- catch (error) {
547
- throw new Error(`Failed to load required Node.js modules: ${error instanceof Error ? error.message : 'Unknown error'}`);
548
- }
549
- console.log('Environment checks passed successfully');
198
+ /** Perform emergency cleanup when normal stop fails */
199
+ async performEmergencyCleanup() {
200
+ return _performEmergencyCleanup(this.getContext());
550
201
  }
551
- /**
552
- * Attempt error recovery
553
- */
554
- async attemptRecovery() {
555
- if (!this.stateManager.isInError()) {
556
- return true; // Already recovered
557
- }
558
- if (this.recoveryAttempts >= this.maxRecoveryAttempts) {
559
- this.emit('recoveryFailed', new Error('Maximum recovery attempts exceeded'));
560
- return false;
561
- }
562
- this.recoveryAttempts++;
563
- this.emit('recoveryStarted');
564
- try {
565
- this.stateManager.beginRecovery();
566
- // Perform recovery steps
567
- await this.performRecoverySteps();
568
- this.stateManager.completeRecovery();
569
- this.emit('recoveryCompleted');
570
- return true;
571
- }
572
- catch (error) {
573
- this.stateManager.failRecovery();
574
- this.emit('recoveryFailed', error);
575
- // Wait before next attempt
576
- await new Promise((resolve) => setTimeout(resolve, RECOVERY_CONSTANTS.RECOVERY_DELAY_MS));
577
- return false;
578
- }
202
+ /** Execute a function with timeout */
203
+ async executeWithTimeout(fn, timeoutMs, phaseName) {
204
+ return _executeWithTimeout(fn, timeoutMs, phaseName);
579
205
  }
580
- /**
581
- * Load configuration and apply it
582
- */
206
+ // ── Configuration ──────────────────────────────────────────────────
583
207
  async loadConfig(configFilePath) {
584
- const configLoader = this.componentManager.getComponent('configLoader');
585
- const processManager = this.componentManager.getComponent('processManager');
586
- const logManager = this.componentManager.getComponent('logManager');
587
- if (!configLoader) {
588
- throw new Error('ConfigLoader not initialized');
589
- }
590
- if (!processManager) {
591
- throw new Error('ProcessManager not initialized');
592
- }
593
- this.configFilePath = configFilePath;
594
- const config = await configLoader.load(configFilePath);
595
- this.currentConfig = config.apps;
596
- // Stop all existing processes
597
- const allProcesses = processManager.getAllProcessInfo();
598
- if (allProcesses.length > 0) {
599
- const processNames = allProcesses.map((p) => p.name);
600
- await processManager.stopProcesses(processNames);
601
- }
602
- // Configure new processes
603
- for (const app of config.apps) {
604
- processManager.configureProcess(app);
605
- // Setup log manager for this app if log files are configured
606
- if (logManager && (app.log_file || app.out_file || app.error_file)) {
607
- logManager.setupAppLogs(app.name, {
608
- logFile: app.log_file,
609
- outFile: app.out_file,
610
- errorFile: app.error_file,
611
- namespace: app.namespace,
612
- });
613
- }
614
- }
615
- return config.apps;
208
+ return queryLoadConfig(this.getContext(), configFilePath);
616
209
  }
617
- /**
618
- * Get current configuration
619
- */
620
210
  getConfig() {
621
211
  return this.currentConfig;
622
212
  }
623
- /**
624
- * Get component instances (for backward compatibility)
625
- */
213
+ // ── Component accessors ────────────────────────────────────────────
626
214
  getConfigLoader() {
627
- return this.componentManager.getComponent('configLoader');
215
+ return _getConfigLoader(this.getContext());
628
216
  }
629
217
  getProcessManager() {
630
- return this.componentManager.getComponent('processManager');
218
+ return _getProcessManager(this.getContext());
631
219
  }
632
220
  getLogManager() {
633
- return this.componentManager.getComponent('logManager');
221
+ return _getLogManager(this.getContext());
634
222
  }
635
223
  getIPCServer() {
636
- return this.componentManager.getComponent('ipcServer');
224
+ return _getIPCServer(this.getContext());
637
225
  }
638
- /**
639
- * Get all process statuses (moved from old implementation)
640
- */
641
226
  async getAllProcessStatuses() {
642
- const processManager = this.getProcessManager();
643
- if (!processManager) {
644
- return [];
645
- }
646
- const processInfos = processManager.getAllProcessInfo();
647
- const statusPromises = processInfos.map(async (info) => {
648
- // Get process stats from monitor
649
- const stats = await processManager.monitor.getProcessStats(info.name);
650
- return {
651
- name: info.name,
652
- namespace: info.namespace || 'default',
653
- pid: info.pid,
654
- status: info.status,
655
- uptime: info.uptime,
656
- memory: stats?.memory || 0,
657
- cpu: stats?.cpu || 0,
658
- restarts: info.restarts,
659
- };
660
- });
661
- return Promise.all(statusPromises);
227
+ return queryAllProcessStatuses(this.getContext());
662
228
  }
663
- /**
664
- * Setup event listeners for internal managers
665
- */
229
+ // ── Event wiring ───────────────────────────────────────────────────
666
230
  setupEventListeners() {
667
- // Forward state manager events
668
231
  this.stateManager.on('stateChange', (event) => {
669
232
  this.emit('stateChange', event.to);
670
233
  });
671
234
  this.stateManager.on('enterError', () => {
672
- // Attempt automatic recovery
673
235
  setTimeout(() => {
674
236
  this.attemptRecovery().catch((error) => {
675
237
  console.error('Automatic recovery failed:', error);
676
238
  });
677
239
  }, 1000);
678
240
  });
679
- // Forward component manager events
680
241
  this.componentManager.on('componentStarted', (componentName) => {
681
242
  this.emit('componentStarted', componentName);
682
243
  });
@@ -686,12 +247,10 @@ export class ProcmanDaemon extends EventEmitter {
686
247
  this.componentManager.on('componentError', (componentName, error) => {
687
248
  console.error(`Component ${componentName} error:`, error);
688
249
  this.emit('error', error);
689
- // Trigger error state if not already in error
690
250
  if (!this.stateManager.isInError()) {
691
251
  this.stateManager.forceError();
692
252
  }
693
253
  });
694
- // Setup signal handler events with enhanced shutdown
695
254
  this.signalHandler.on('gracefulShutdown', async (signal) => {
696
255
  console.log(`Received ${signal}, shutting down gracefully...`);
697
256
  try {
@@ -706,12 +265,10 @@ export class ProcmanDaemon extends EventEmitter {
706
265
  this.signalHandler.on('uncaughtException', async (error) => {
707
266
  console.error('Uncaught exception in daemon:', error);
708
267
  this.emit('error', error);
709
- // Force error state and attempt recovery
710
268
  if (!this.stateManager.isInError()) {
711
269
  this.stateManager.forceError();
712
270
  }
713
271
  });
714
- // Setup memory monitor event handlers
715
272
  this.memoryMonitor.on('memoryCritical', (usage, threshold) => {
716
273
  console.warn(`[ProcmanDaemon] Memory critical threshold exceeded: ${this.formatBytes(usage.rss)} > ${this.formatBytes(threshold)}`);
717
274
  });
@@ -723,75 +280,21 @@ export class ProcmanDaemon extends EventEmitter {
723
280
  this.emit('error', error);
724
281
  });
725
282
  }
726
- /**
727
- * Perform startup cleanup on failure
728
- */
729
- async performStartupCleanup() {
730
- const cleanupTasks = [];
731
- // Cleanup PID file
732
- cleanupTasks.push(this.pidManager.cleanup().catch((error) => {
733
- console.error('Failed to cleanup PID file during startup failure:', error);
734
- }));
735
- // Cleanup signal handlers
736
- try {
737
- this.signalHandler.cleanupHandlers();
738
- }
739
- catch (error) {
740
- console.error('Failed to cleanup signal handlers during startup failure:', error);
741
- }
742
- // Cleanup any partially initialized components
743
- cleanupTasks.push(this.componentManager.cleanupAll().catch((error) => {
744
- console.error('Failed to cleanup components during startup failure:', error);
745
- }));
746
- // Wait for all cleanup tasks to complete
747
- await Promise.all(cleanupTasks);
748
- }
749
- /**
750
- * Perform emergency cleanup when normal stop fails
751
- */
752
- async performEmergencyCleanup() {
753
- console.log('Performing emergency cleanup...');
754
- // Try to cleanup critical resources without throwing errors
755
- try {
756
- await this.pidManager.cleanup();
757
- }
758
- catch (error) {
759
- console.error('Emergency PID cleanup failed:', error);
760
- }
761
- try {
762
- this.signalHandler.cleanupHandlers();
763
- }
764
- catch (error) {
765
- console.error('Emergency signal handler cleanup failed:', error);
766
- }
767
- }
768
- /**
769
- * Perform recovery steps when in RECOVERING state
770
- */
771
- async performRecoverySteps() {
772
- // Step 1: Try to reinitialize components
773
- try {
774
- await this.componentManager.cleanupAll();
775
- }
776
- catch (error) {
777
- console.log('Component cleanup during recovery failed (expected):', error);
283
+ // ── Utilities ──────────────────────────────────────────────────────
284
+ formatBytes(bytes) {
285
+ const units = ['B', 'KB', 'MB', 'GB'];
286
+ let size = Number(bytes);
287
+ let unitIndex = 0;
288
+ if (!isFinite(size) || size < 0) {
289
+ return '0B';
778
290
  }
779
- // Step 2: Reinitialize components
780
- await this.componentManager.initializeAll();
781
- // Step 3: Reapply current configuration if available
782
- if (this.configFilePath) {
783
- try {
784
- await this.loadConfig(this.configFilePath);
785
- }
786
- catch (error) {
787
- console.error('Failed to reload configuration during recovery:', error);
788
- throw error;
789
- }
291
+ while (size >= 1024 && unitIndex < units.length - 1) {
292
+ size /= 1024;
293
+ unitIndex++;
790
294
  }
295
+ return `${size.toFixed(1)}${units[unitIndex]}`;
791
296
  }
792
- /**
793
- * Override EventEmitter methods for type safety
794
- */
297
+ // ── Type-safe EventEmitter overrides ───────────────────────────────
795
298
  emit(event, ...args) {
796
299
  return super.emit(event, ...args);
797
300
  }