@masuidrive/procman 0.1.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.
- package/LICENSE +21 -0
- package/README.md +149 -0
- package/dist/src/cli/commands/clear-log.d.ts +10 -0
- package/dist/src/cli/commands/clear-log.d.ts.map +1 -0
- package/dist/src/cli/commands/clear-log.js +77 -0
- package/dist/src/cli/commands/clear-log.js.map +1 -0
- package/dist/src/cli/commands/exit.d.ts +10 -0
- package/dist/src/cli/commands/exit.d.ts.map +1 -0
- package/dist/src/cli/commands/exit.js +65 -0
- package/dist/src/cli/commands/exit.js.map +1 -0
- package/dist/src/cli/commands/help.d.ts +10 -0
- package/dist/src/cli/commands/help.d.ts.map +1 -0
- package/dist/src/cli/commands/help.js +340 -0
- package/dist/src/cli/commands/help.js.map +1 -0
- package/dist/src/cli/commands/list.d.ts +11 -0
- package/dist/src/cli/commands/list.d.ts.map +1 -0
- package/dist/src/cli/commands/list.js +130 -0
- package/dist/src/cli/commands/list.js.map +1 -0
- package/dist/src/cli/commands/load.d.ts +11 -0
- package/dist/src/cli/commands/load.d.ts.map +1 -0
- package/dist/src/cli/commands/load.js +250 -0
- package/dist/src/cli/commands/load.js.map +1 -0
- package/dist/src/cli/commands/log.d.ts +18 -0
- package/dist/src/cli/commands/log.d.ts.map +1 -0
- package/dist/src/cli/commands/log.js +282 -0
- package/dist/src/cli/commands/log.js.map +1 -0
- package/dist/src/cli/commands/restart.d.ts +11 -0
- package/dist/src/cli/commands/restart.d.ts.map +1 -0
- package/dist/src/cli/commands/restart.js +99 -0
- package/dist/src/cli/commands/restart.js.map +1 -0
- package/dist/src/cli/commands/start.d.ts +11 -0
- package/dist/src/cli/commands/start.d.ts.map +1 -0
- package/dist/src/cli/commands/start.js +105 -0
- package/dist/src/cli/commands/start.js.map +1 -0
- package/dist/src/cli/commands/stop.d.ts +12 -0
- package/dist/src/cli/commands/stop.d.ts.map +1 -0
- package/dist/src/cli/commands/stop.js +105 -0
- package/dist/src/cli/commands/stop.js.map +1 -0
- package/dist/src/cli/index.d.ts +3 -0
- package/dist/src/cli/index.d.ts.map +1 -0
- package/dist/src/cli/index.js +28 -0
- package/dist/src/cli/index.js.map +1 -0
- package/dist/src/cli/parser.d.ts +14 -0
- package/dist/src/cli/parser.d.ts.map +1 -0
- package/dist/src/cli/parser.js +131 -0
- package/dist/src/cli/parser.js.map +1 -0
- package/dist/src/cli/signal-handler.d.ts +51 -0
- package/dist/src/cli/signal-handler.d.ts.map +1 -0
- package/dist/src/cli/signal-handler.js +129 -0
- package/dist/src/cli/signal-handler.js.map +1 -0
- package/dist/src/cli/utils/error-handler.d.ts +39 -0
- package/dist/src/cli/utils/error-handler.d.ts.map +1 -0
- package/dist/src/cli/utils/error-handler.js +200 -0
- package/dist/src/cli/utils/error-handler.js.map +1 -0
- package/dist/src/cli/utils/error-utils.d.ts +16 -0
- package/dist/src/cli/utils/error-utils.d.ts.map +1 -0
- package/dist/src/cli/utils/error-utils.js +48 -0
- package/dist/src/cli/utils/error-utils.js.map +1 -0
- package/dist/src/cli/utils/ipc-client.d.ts +80 -0
- package/dist/src/cli/utils/ipc-client.d.ts.map +1 -0
- package/dist/src/cli/utils/ipc-client.js +275 -0
- package/dist/src/cli/utils/ipc-client.js.map +1 -0
- package/dist/src/config/config-loader.d.ts +74 -0
- package/dist/src/config/config-loader.d.ts.map +1 -0
- package/dist/src/config/config-loader.js +229 -0
- package/dist/src/config/config-loader.js.map +1 -0
- package/dist/src/config/config-normalizer.d.ts +135 -0
- package/dist/src/config/config-normalizer.d.ts.map +1 -0
- package/dist/src/config/config-normalizer.js +309 -0
- package/dist/src/config/config-normalizer.js.map +1 -0
- package/dist/src/config/config-reporter.d.ts +183 -0
- package/dist/src/config/config-reporter.d.ts.map +1 -0
- package/dist/src/config/config-reporter.js +311 -0
- package/dist/src/config/config-reporter.js.map +1 -0
- package/dist/src/config/config-validator.d.ts +163 -0
- package/dist/src/config/config-validator.d.ts.map +1 -0
- package/dist/src/config/config-validator.js +489 -0
- package/dist/src/config/config-validator.js.map +1 -0
- package/dist/src/config/config-watcher.d.ts +161 -0
- package/dist/src/config/config-watcher.d.ts.map +1 -0
- package/dist/src/config/config-watcher.js +245 -0
- package/dist/src/config/config-watcher.js.map +1 -0
- package/dist/src/config/index.d.ts +36 -0
- package/dist/src/config/index.d.ts.map +1 -0
- package/dist/src/config/index.js +41 -0
- package/dist/src/config/index.js.map +1 -0
- package/dist/src/config/secure-config-loader.d.ts +77 -0
- package/dist/src/config/secure-config-loader.d.ts.map +1 -0
- package/dist/src/config/secure-config-loader.js +326 -0
- package/dist/src/config/secure-config-loader.js.map +1 -0
- package/dist/src/daemon/base-socket-connection.d.ts +66 -0
- package/dist/src/daemon/base-socket-connection.d.ts.map +1 -0
- package/dist/src/daemon/base-socket-connection.js +192 -0
- package/dist/src/daemon/base-socket-connection.js.map +1 -0
- package/dist/src/daemon/component-manager.d.ts +180 -0
- package/dist/src/daemon/component-manager.d.ts.map +1 -0
- package/dist/src/daemon/component-manager.js +794 -0
- package/dist/src/daemon/component-manager.js.map +1 -0
- package/dist/src/daemon/crash-recovery.d.ts +71 -0
- package/dist/src/daemon/crash-recovery.d.ts.map +1 -0
- package/dist/src/daemon/crash-recovery.js +282 -0
- package/dist/src/daemon/crash-recovery.js.map +1 -0
- package/dist/src/daemon/daemon-main.d.ts +18 -0
- package/dist/src/daemon/daemon-main.d.ts.map +1 -0
- package/dist/src/daemon/daemon-main.js +160 -0
- package/dist/src/daemon/daemon-main.js.map +1 -0
- package/dist/src/daemon/daemon-state-manager.d.ts +111 -0
- package/dist/src/daemon/daemon-state-manager.d.ts.map +1 -0
- package/dist/src/daemon/daemon-state-manager.js +194 -0
- package/dist/src/daemon/daemon-state-manager.js.map +1 -0
- package/dist/src/daemon/data-directory.d.ts +51 -0
- package/dist/src/daemon/data-directory.d.ts.map +1 -0
- package/dist/src/daemon/data-directory.js +136 -0
- package/dist/src/daemon/data-directory.js.map +1 -0
- package/dist/src/daemon/index.d.ts +20 -0
- package/dist/src/daemon/index.d.ts.map +1 -0
- package/dist/src/daemon/index.js +24 -0
- package/dist/src/daemon/index.js.map +1 -0
- package/dist/src/daemon/ipc-client-base.d.ts +153 -0
- package/dist/src/daemon/ipc-client-base.d.ts.map +1 -0
- package/dist/src/daemon/ipc-client-base.js +476 -0
- package/dist/src/daemon/ipc-client-base.js.map +1 -0
- package/dist/src/daemon/ipc-command-handler.d.ts +107 -0
- package/dist/src/daemon/ipc-command-handler.d.ts.map +1 -0
- package/dist/src/daemon/ipc-command-handler.js +483 -0
- package/dist/src/daemon/ipc-command-handler.js.map +1 -0
- package/dist/src/daemon/ipc-factory.d.ts +92 -0
- package/dist/src/daemon/ipc-factory.d.ts.map +1 -0
- package/dist/src/daemon/ipc-factory.js +210 -0
- package/dist/src/daemon/ipc-factory.js.map +1 -0
- package/dist/src/daemon/ipc-server-base.d.ts +158 -0
- package/dist/src/daemon/ipc-server-base.d.ts.map +1 -0
- package/dist/src/daemon/ipc-server-base.js +491 -0
- package/dist/src/daemon/ipc-server-base.js.map +1 -0
- package/dist/src/daemon/message-protocol.d.ts +132 -0
- package/dist/src/daemon/message-protocol.d.ts.map +1 -0
- package/dist/src/daemon/message-protocol.js +252 -0
- package/dist/src/daemon/message-protocol.js.map +1 -0
- package/dist/src/daemon/named-pipe-client.d.ts +61 -0
- package/dist/src/daemon/named-pipe-client.d.ts.map +1 -0
- package/dist/src/daemon/named-pipe-client.js +221 -0
- package/dist/src/daemon/named-pipe-client.js.map +1 -0
- package/dist/src/daemon/named-pipe-server.d.ts +40 -0
- package/dist/src/daemon/named-pipe-server.d.ts.map +1 -0
- package/dist/src/daemon/named-pipe-server.js +102 -0
- package/dist/src/daemon/named-pipe-server.js.map +1 -0
- package/dist/src/daemon/orphan-detector.d.ts +66 -0
- package/dist/src/daemon/orphan-detector.d.ts.map +1 -0
- package/dist/src/daemon/orphan-detector.js +208 -0
- package/dist/src/daemon/orphan-detector.js.map +1 -0
- package/dist/src/daemon/pid-manager.d.ts +49 -0
- package/dist/src/daemon/pid-manager.d.ts.map +1 -0
- package/dist/src/daemon/pid-manager.js +110 -0
- package/dist/src/daemon/pid-manager.js.map +1 -0
- package/dist/src/daemon/procman-daemon.d.ts +188 -0
- package/dist/src/daemon/procman-daemon.d.ts.map +1 -0
- package/dist/src/daemon/procman-daemon.js +802 -0
- package/dist/src/daemon/procman-daemon.js.map +1 -0
- package/dist/src/daemon/reconnection-system.d.ts +113 -0
- package/dist/src/daemon/reconnection-system.d.ts.map +1 -0
- package/dist/src/daemon/reconnection-system.js +223 -0
- package/dist/src/daemon/reconnection-system.js.map +1 -0
- package/dist/src/daemon/resource-manager.d.ts +204 -0
- package/dist/src/daemon/resource-manager.d.ts.map +1 -0
- package/dist/src/daemon/resource-manager.js +423 -0
- package/dist/src/daemon/resource-manager.js.map +1 -0
- package/dist/src/daemon/signal-handler.d.ts +58 -0
- package/dist/src/daemon/signal-handler.d.ts.map +1 -0
- package/dist/src/daemon/signal-handler.js +142 -0
- package/dist/src/daemon/signal-handler.js.map +1 -0
- package/dist/src/daemon/simple-resource-manager.d.ts +95 -0
- package/dist/src/daemon/simple-resource-manager.d.ts.map +1 -0
- package/dist/src/daemon/simple-resource-manager.js +180 -0
- package/dist/src/daemon/simple-resource-manager.js.map +1 -0
- package/dist/src/daemon/unix-socket-client.d.ts +69 -0
- package/dist/src/daemon/unix-socket-client.d.ts.map +1 -0
- package/dist/src/daemon/unix-socket-client.js +313 -0
- package/dist/src/daemon/unix-socket-client.js.map +1 -0
- package/dist/src/daemon/unix-socket-server.d.ts +61 -0
- package/dist/src/daemon/unix-socket-server.d.ts.map +1 -0
- package/dist/src/daemon/unix-socket-server.js +262 -0
- package/dist/src/daemon/unix-socket-server.js.map +1 -0
- package/dist/src/daemon/zombie-reaper.d.ts +83 -0
- package/dist/src/daemon/zombie-reaper.d.ts.map +1 -0
- package/dist/src/daemon/zombie-reaper.js +278 -0
- package/dist/src/daemon/zombie-reaper.js.map +1 -0
- package/dist/src/process-manager/index.d.ts +13 -0
- package/dist/src/process-manager/index.d.ts.map +1 -0
- package/dist/src/process-manager/index.js +11 -0
- package/dist/src/process-manager/index.js.map +1 -0
- package/dist/src/process-manager/interfaces/index.d.ts +31 -0
- package/dist/src/process-manager/interfaces/index.d.ts.map +1 -0
- package/dist/src/process-manager/interfaces/index.js +14 -0
- package/dist/src/process-manager/interfaces/index.js.map +1 -0
- package/dist/src/process-manager/interfaces/process-group.d.ts +200 -0
- package/dist/src/process-manager/interfaces/process-group.d.ts.map +1 -0
- package/dist/src/process-manager/interfaces/process-group.js +10 -0
- package/dist/src/process-manager/interfaces/process-group.js.map +1 -0
- package/dist/src/process-manager/interfaces/process-lifecycle.d.ts +97 -0
- package/dist/src/process-manager/interfaces/process-lifecycle.d.ts.map +1 -0
- package/dist/src/process-manager/interfaces/process-lifecycle.js +10 -0
- package/dist/src/process-manager/interfaces/process-lifecycle.js.map +1 -0
- package/dist/src/process-manager/interfaces/process-monitor.d.ts +118 -0
- package/dist/src/process-manager/interfaces/process-monitor.d.ts.map +1 -0
- package/dist/src/process-manager/interfaces/process-monitor.js +10 -0
- package/dist/src/process-manager/interfaces/process-monitor.js.map +1 -0
- package/dist/src/process-manager/interfaces/process-persistence.d.ts +125 -0
- package/dist/src/process-manager/interfaces/process-persistence.d.ts.map +1 -0
- package/dist/src/process-manager/interfaces/process-persistence.js +10 -0
- package/dist/src/process-manager/interfaces/process-persistence.js.map +1 -0
- package/dist/src/process-manager/managed-process-info.d.ts +307 -0
- package/dist/src/process-manager/managed-process-info.d.ts.map +1 -0
- package/dist/src/process-manager/managed-process-info.js +650 -0
- package/dist/src/process-manager/managed-process-info.js.map +1 -0
- package/dist/src/process-manager/process-group-manager.d.ts +103 -0
- package/dist/src/process-manager/process-group-manager.d.ts.map +1 -0
- package/dist/src/process-manager/process-group-manager.js +400 -0
- package/dist/src/process-manager/process-group-manager.js.map +1 -0
- package/dist/src/process-manager/process-lifecycle-manager.d.ts +68 -0
- package/dist/src/process-manager/process-lifecycle-manager.d.ts.map +1 -0
- package/dist/src/process-manager/process-lifecycle-manager.js +372 -0
- package/dist/src/process-manager/process-lifecycle-manager.js.map +1 -0
- package/dist/src/process-manager/process-manager.d.ts +296 -0
- package/dist/src/process-manager/process-manager.d.ts.map +1 -0
- package/dist/src/process-manager/process-manager.js +659 -0
- package/dist/src/process-manager/process-manager.js.map +1 -0
- package/dist/src/process-manager/process-monitor.d.ts +95 -0
- package/dist/src/process-manager/process-monitor.d.ts.map +1 -0
- package/dist/src/process-manager/process-monitor.js +357 -0
- package/dist/src/process-manager/process-monitor.js.map +1 -0
- package/dist/src/process-manager/process-persistence.d.ts +68 -0
- package/dist/src/process-manager/process-persistence.d.ts.map +1 -0
- package/dist/src/process-manager/process-persistence.js +364 -0
- package/dist/src/process-manager/process-persistence.js.map +1 -0
- package/dist/src/process-manager/utils/mutex.d.ts +82 -0
- package/dist/src/process-manager/utils/mutex.d.ts.map +1 -0
- package/dist/src/process-manager/utils/mutex.js +172 -0
- package/dist/src/process-manager/utils/mutex.js.map +1 -0
- package/dist/src/services/index.d.ts +8 -0
- package/dist/src/services/index.d.ts.map +1 -0
- package/dist/src/services/index.js +8 -0
- package/dist/src/services/index.js.map +1 -0
- package/dist/src/services/log-manager-config.d.ts +65 -0
- package/dist/src/services/log-manager-config.d.ts.map +1 -0
- package/dist/src/services/log-manager-config.js +145 -0
- package/dist/src/services/log-manager-config.js.map +1 -0
- package/dist/src/services/log-manager-factory.d.ts +134 -0
- package/dist/src/services/log-manager-factory.d.ts.map +1 -0
- package/dist/src/services/log-manager-factory.js +203 -0
- package/dist/src/services/log-manager-factory.js.map +1 -0
- package/dist/src/services/log-manager.d.ts +170 -0
- package/dist/src/services/log-manager.d.ts.map +1 -0
- package/dist/src/services/log-manager.js +1199 -0
- package/dist/src/services/log-manager.js.map +1 -0
- package/dist/src/services/log-path-validator.d.ts +121 -0
- package/dist/src/services/log-path-validator.d.ts.map +1 -0
- package/dist/src/services/log-path-validator.js +302 -0
- package/dist/src/services/log-path-validator.js.map +1 -0
- package/dist/src/services/process-log-integration.d.ts +62 -0
- package/dist/src/services/process-log-integration.d.ts.map +1 -0
- package/dist/src/services/process-log-integration.js +157 -0
- package/dist/src/services/process-log-integration.js.map +1 -0
- package/dist/src/shared/config.d.ts +67 -0
- package/dist/src/shared/config.d.ts.map +1 -0
- package/dist/src/shared/config.js +92 -0
- package/dist/src/shared/config.js.map +1 -0
- package/dist/src/shared/constants-streaming.d.ts +39 -0
- package/dist/src/shared/constants-streaming.d.ts.map +1 -0
- package/dist/src/shared/constants-streaming.js +39 -0
- package/dist/src/shared/constants-streaming.js.map +1 -0
- package/dist/src/shared/constants.d.ts +60 -0
- package/dist/src/shared/constants.d.ts.map +1 -0
- package/dist/src/shared/constants.js +71 -0
- package/dist/src/shared/constants.js.map +1 -0
- package/dist/src/shared/errors.d.ts +70 -0
- package/dist/src/shared/errors.d.ts.map +1 -0
- package/dist/src/shared/errors.js +101 -0
- package/dist/src/shared/errors.js.map +1 -0
- package/dist/src/shared/index.d.ts +14 -0
- package/dist/src/shared/index.d.ts.map +1 -0
- package/dist/src/shared/index.js +22 -0
- package/dist/src/shared/index.js.map +1 -0
- package/dist/src/shared/ipc.d.ts +402 -0
- package/dist/src/shared/ipc.d.ts.map +1 -0
- package/dist/src/shared/ipc.js +85 -0
- package/dist/src/shared/ipc.js.map +1 -0
- package/dist/src/shared/logger.d.ts +38 -0
- package/dist/src/shared/logger.d.ts.map +1 -0
- package/dist/src/shared/logger.js +139 -0
- package/dist/src/shared/logger.js.map +1 -0
- package/dist/src/shared/logs.d.ts +53 -0
- package/dist/src/shared/logs.d.ts.map +1 -0
- package/dist/src/shared/logs.js +26 -0
- package/dist/src/shared/logs.js.map +1 -0
- package/dist/src/shared/process.d.ts +102 -0
- package/dist/src/shared/process.d.ts.map +1 -0
- package/dist/src/shared/process.js +55 -0
- package/dist/src/shared/process.js.map +1 -0
- package/dist/src/shared/types.d.ts +15 -0
- package/dist/src/shared/types.d.ts.map +1 -0
- package/dist/src/shared/types.js +8 -0
- package/dist/src/shared/types.js.map +1 -0
- package/dist/src/types/index.d.ts +19 -0
- package/dist/src/types/index.d.ts.map +1 -0
- package/dist/src/types/index.js +5 -0
- package/dist/src/types/index.js.map +1 -0
- package/dist/src/utils/event-cleanup.d.ts +30 -0
- package/dist/src/utils/event-cleanup.d.ts.map +1 -0
- package/dist/src/utils/event-cleanup.js +32 -0
- package/dist/src/utils/event-cleanup.js.map +1 -0
- package/dist/src/utils/memory/memory-monitor.d.ts +126 -0
- package/dist/src/utils/memory/memory-monitor.d.ts.map +1 -0
- package/dist/src/utils/memory/memory-monitor.js +246 -0
- package/dist/src/utils/memory/memory-monitor.js.map +1 -0
- package/package.json +74 -0
|
@@ -0,0 +1,802 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main daemon class for procman
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates daemon components using specialized managers.
|
|
5
|
+
* Follows Single Responsibility Principle by delegating to:
|
|
6
|
+
* - DaemonStateManager: State management and transitions
|
|
7
|
+
* - ComponentManager: Component lifecycle management
|
|
8
|
+
* - SignalHandler: Process signal handling
|
|
9
|
+
* - DataDirectory & PIDManager: File system management
|
|
10
|
+
*/
|
|
11
|
+
import { EventEmitter } from 'events';
|
|
12
|
+
import { DataDirectory } from './data-directory.js';
|
|
13
|
+
import { PIDManager } from './pid-manager.js';
|
|
14
|
+
import { DaemonStateManager, DaemonState } from './daemon-state-manager.js';
|
|
15
|
+
import { ComponentManager } from './component-manager.js';
|
|
16
|
+
import { SignalHandler } from './signal-handler.js';
|
|
17
|
+
import { MemoryMonitor } from '../utils/memory/memory-monitor.js';
|
|
18
|
+
import path from 'path';
|
|
19
|
+
// Re-export DaemonState for backward compatibility
|
|
20
|
+
export { DaemonState };
|
|
21
|
+
/**
|
|
22
|
+
* Recovery constants
|
|
23
|
+
*/
|
|
24
|
+
const RECOVERY_CONSTANTS = {
|
|
25
|
+
MAX_RECOVERY_ATTEMPTS: 3,
|
|
26
|
+
RECOVERY_DELAY_MS: 5000,
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Main daemon class that coordinates all procman components
|
|
30
|
+
*/
|
|
31
|
+
export class ProcmanDaemon extends EventEmitter {
|
|
32
|
+
dataDirectory;
|
|
33
|
+
pidManager;
|
|
34
|
+
stateManager;
|
|
35
|
+
componentManager;
|
|
36
|
+
signalHandler;
|
|
37
|
+
memoryMonitor;
|
|
38
|
+
currentConfig;
|
|
39
|
+
configFilePath;
|
|
40
|
+
recoveryAttempts = 0;
|
|
41
|
+
maxRecoveryAttempts = RECOVERY_CONSTANTS.MAX_RECOVERY_ATTEMPTS;
|
|
42
|
+
isShuttingDown = false; // Add shutdown flag
|
|
43
|
+
constructor() {
|
|
44
|
+
super();
|
|
45
|
+
this.dataDirectory = new DataDirectory(process.env.PROCMAN_SOCKET_PATH);
|
|
46
|
+
this.pidManager = new PIDManager(this.dataDirectory);
|
|
47
|
+
this.stateManager = new DaemonStateManager();
|
|
48
|
+
this.componentManager = new ComponentManager(this.dataDirectory);
|
|
49
|
+
this.signalHandler = new SignalHandler();
|
|
50
|
+
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
|
|
55
|
+
});
|
|
56
|
+
this.setupEventListeners();
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get current daemon state
|
|
60
|
+
*/
|
|
61
|
+
getState() {
|
|
62
|
+
return this.stateManager.getCurrentState();
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Check if daemon is currently running
|
|
66
|
+
*/
|
|
67
|
+
isRunning() {
|
|
68
|
+
return this.stateManager.isRunning();
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Check if daemon is ready to handle requests
|
|
72
|
+
* Performs comprehensive health checks on all components
|
|
73
|
+
*/
|
|
74
|
+
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
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Get detailed health status of all components
|
|
89
|
+
*/
|
|
90
|
+
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
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Start the daemon
|
|
101
|
+
*/
|
|
102
|
+
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
|
+
}
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Stop the daemon
|
|
248
|
+
*/
|
|
249
|
+
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
|
+
}
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Restart the daemon
|
|
276
|
+
*/
|
|
277
|
+
async restart() {
|
|
278
|
+
if (this.isRunning()) {
|
|
279
|
+
await this.stop();
|
|
280
|
+
}
|
|
281
|
+
await this.start();
|
|
282
|
+
}
|
|
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
|
+
*/
|
|
289
|
+
async shutdown(reason = 'manual', timeoutMs = 30000) {
|
|
290
|
+
if (this.isShuttingDown) {
|
|
291
|
+
console.log('[ProcmanDaemon] Shutdown already in progress, waiting...');
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
this.isShuttingDown = true;
|
|
295
|
+
const shutdownStart = Date.now();
|
|
296
|
+
console.log(`[ProcmanDaemon] Beginning graceful shutdown (reason: ${reason}, timeout: ${timeoutMs}ms)`);
|
|
297
|
+
try {
|
|
298
|
+
// Phase 1: Set shutdown flag and reject new connections (5s timeout)
|
|
299
|
+
await this.executeWithTimeout(async () => {
|
|
300
|
+
console.log('[ProcmanDaemon] Phase 1: Setting shutdown flag and rejecting new connections...');
|
|
301
|
+
this.emit('shutdownStarted', reason);
|
|
302
|
+
const ipcServer = this.getIPCServer();
|
|
303
|
+
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
|
+
this.emit('newConnectionsRejected');
|
|
307
|
+
}
|
|
308
|
+
}, 5000, 'Phase 1: Shutdown initialization');
|
|
309
|
+
// Phase 2: Save shutdown state (5s timeout)
|
|
310
|
+
await this.executeWithTimeout(async () => {
|
|
311
|
+
console.log('[ProcmanDaemon] Phase 2: Saving shutdown state...');
|
|
312
|
+
await this.saveShutdownState(reason);
|
|
313
|
+
}, 5000, 'Phase 2: State preservation');
|
|
314
|
+
// Phase 3: Drain active connections (10s timeout)
|
|
315
|
+
await this.executeWithTimeout(async () => {
|
|
316
|
+
console.log('[ProcmanDaemon] Phase 3: Draining active connections...');
|
|
317
|
+
await this.drainActiveConnections();
|
|
318
|
+
}, 10000, 'Phase 3: Connection draining');
|
|
319
|
+
// Phase 4: Stop processes gracefully (15s timeout)
|
|
320
|
+
await this.executeWithTimeout(async () => {
|
|
321
|
+
console.log('[ProcmanDaemon] Phase 4: Stopping managed processes...');
|
|
322
|
+
await this.stopManagedProcesses();
|
|
323
|
+
}, 15000, 'Phase 4: Process termination');
|
|
324
|
+
// Phase 5: Cleanup resources (5s timeout)
|
|
325
|
+
await this.executeWithTimeout(async () => {
|
|
326
|
+
console.log('[ProcmanDaemon] Phase 5: Final resource cleanup...');
|
|
327
|
+
await this.stop(); // Use existing stop method for final cleanup
|
|
328
|
+
}, 5000, 'Phase 5: Resource cleanup');
|
|
329
|
+
const totalTime = Date.now() - shutdownStart;
|
|
330
|
+
console.log(`[ProcmanDaemon] Graceful shutdown completed successfully in ${totalTime}ms`);
|
|
331
|
+
this.emit('shutdownCompleted', reason, totalTime);
|
|
332
|
+
}
|
|
333
|
+
catch (error) {
|
|
334
|
+
const totalTime = Date.now() - shutdownStart;
|
|
335
|
+
console.error(`[ProcmanDaemon] Graceful shutdown failed after ${totalTime}ms:`, error);
|
|
336
|
+
// Force shutdown if graceful shutdown fails
|
|
337
|
+
console.log('[ProcmanDaemon] Attempting forced shutdown...');
|
|
338
|
+
await this.performEmergencyCleanup();
|
|
339
|
+
this.emit('shutdownFailed', reason, error, totalTime);
|
|
340
|
+
throw error;
|
|
341
|
+
}
|
|
342
|
+
finally {
|
|
343
|
+
this.isShuttingDown = false;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Save shutdown state to disk for recovery/analysis
|
|
348
|
+
*/
|
|
349
|
+
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
|
+
}
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Get count of active IPC connections
|
|
379
|
+
*/
|
|
380
|
+
getActiveConnectionCount() {
|
|
381
|
+
const ipcServer = this.getIPCServer();
|
|
382
|
+
if (!ipcServer) {
|
|
383
|
+
return 0;
|
|
384
|
+
}
|
|
385
|
+
return ipcServer.getConnections().length;
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Drain active IPC connections gracefully
|
|
389
|
+
*/
|
|
390
|
+
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
|
+
}
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Stop all managed processes gracefully
|
|
430
|
+
*/
|
|
431
|
+
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]}`;
|
|
471
|
+
}
|
|
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');
|
|
550
|
+
}
|
|
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
|
+
}
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Load configuration and apply it
|
|
582
|
+
*/
|
|
583
|
+
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;
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* Get current configuration
|
|
619
|
+
*/
|
|
620
|
+
getConfig() {
|
|
621
|
+
return this.currentConfig;
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Get component instances (for backward compatibility)
|
|
625
|
+
*/
|
|
626
|
+
getConfigLoader() {
|
|
627
|
+
return this.componentManager.getComponent('configLoader');
|
|
628
|
+
}
|
|
629
|
+
getProcessManager() {
|
|
630
|
+
return this.componentManager.getComponent('processManager');
|
|
631
|
+
}
|
|
632
|
+
getLogManager() {
|
|
633
|
+
return this.componentManager.getComponent('logManager');
|
|
634
|
+
}
|
|
635
|
+
getIPCServer() {
|
|
636
|
+
return this.componentManager.getComponent('ipcServer');
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Get all process statuses (moved from old implementation)
|
|
640
|
+
*/
|
|
641
|
+
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);
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Setup event listeners for internal managers
|
|
665
|
+
*/
|
|
666
|
+
setupEventListeners() {
|
|
667
|
+
// Forward state manager events
|
|
668
|
+
this.stateManager.on('stateChange', (event) => {
|
|
669
|
+
this.emit('stateChange', event.to);
|
|
670
|
+
});
|
|
671
|
+
this.stateManager.on('enterError', () => {
|
|
672
|
+
// Attempt automatic recovery
|
|
673
|
+
setTimeout(() => {
|
|
674
|
+
this.attemptRecovery().catch((error) => {
|
|
675
|
+
console.error('Automatic recovery failed:', error);
|
|
676
|
+
});
|
|
677
|
+
}, 1000);
|
|
678
|
+
});
|
|
679
|
+
// Forward component manager events
|
|
680
|
+
this.componentManager.on('componentStarted', (componentName) => {
|
|
681
|
+
this.emit('componentStarted', componentName);
|
|
682
|
+
});
|
|
683
|
+
this.componentManager.on('componentStopped', (componentName) => {
|
|
684
|
+
this.emit('componentStopped', componentName);
|
|
685
|
+
});
|
|
686
|
+
this.componentManager.on('componentError', (componentName, error) => {
|
|
687
|
+
console.error(`Component ${componentName} error:`, error);
|
|
688
|
+
this.emit('error', error);
|
|
689
|
+
// Trigger error state if not already in error
|
|
690
|
+
if (!this.stateManager.isInError()) {
|
|
691
|
+
this.stateManager.forceError();
|
|
692
|
+
}
|
|
693
|
+
});
|
|
694
|
+
// Setup signal handler events with enhanced shutdown
|
|
695
|
+
this.signalHandler.on('gracefulShutdown', async (signal) => {
|
|
696
|
+
console.log(`Received ${signal}, shutting down gracefully...`);
|
|
697
|
+
try {
|
|
698
|
+
await this.shutdown('signal');
|
|
699
|
+
process.exit(0);
|
|
700
|
+
}
|
|
701
|
+
catch (error) {
|
|
702
|
+
console.error('Error during graceful shutdown:', error);
|
|
703
|
+
process.exit(1);
|
|
704
|
+
}
|
|
705
|
+
});
|
|
706
|
+
this.signalHandler.on('uncaughtException', async (error) => {
|
|
707
|
+
console.error('Uncaught exception in daemon:', error);
|
|
708
|
+
this.emit('error', error);
|
|
709
|
+
// Force error state and attempt recovery
|
|
710
|
+
if (!this.stateManager.isInError()) {
|
|
711
|
+
this.stateManager.forceError();
|
|
712
|
+
}
|
|
713
|
+
});
|
|
714
|
+
// Setup memory monitor event handlers
|
|
715
|
+
this.memoryMonitor.on('memoryCritical', (usage, threshold) => {
|
|
716
|
+
console.warn(`[ProcmanDaemon] Memory critical threshold exceeded: ${this.formatBytes(usage.rss)} > ${this.formatBytes(threshold)}`);
|
|
717
|
+
});
|
|
718
|
+
this.memoryMonitor.on('memoryWarning', (usage, threshold) => {
|
|
719
|
+
console.warn(`[ProcmanDaemon] Memory warning threshold exceeded: ${this.formatBytes(usage.rss)} > ${this.formatBytes(threshold)}`);
|
|
720
|
+
});
|
|
721
|
+
this.memoryMonitor.on('error', (error) => {
|
|
722
|
+
console.error('[ProcmanDaemon] Memory monitor error:', error);
|
|
723
|
+
this.emit('error', error);
|
|
724
|
+
});
|
|
725
|
+
}
|
|
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);
|
|
778
|
+
}
|
|
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
|
+
}
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* Override EventEmitter methods for type safety
|
|
794
|
+
*/
|
|
795
|
+
emit(event, ...args) {
|
|
796
|
+
return super.emit(event, ...args);
|
|
797
|
+
}
|
|
798
|
+
on(event, listener) {
|
|
799
|
+
return super.on(event, listener);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
//# sourceMappingURL=procman-daemon.js.map
|