@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.
- package/dist/src/cli/commands/help.d.ts.map +1 -1
- package/dist/src/cli/commands/help.js +3 -1
- package/dist/src/cli/commands/help.js.map +1 -1
- package/dist/src/cli/index.js +6 -2
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/cli/parser.d.ts.map +1 -1
- package/dist/src/cli/parser.js +20 -0
- package/dist/src/cli/parser.js.map +1 -1
- package/dist/src/cli/utils/find-package-json.d.ts +4 -0
- package/dist/src/cli/utils/find-package-json.d.ts.map +1 -0
- package/dist/src/cli/utils/find-package-json.js +15 -0
- package/dist/src/cli/utils/find-package-json.js.map +1 -0
- package/dist/src/config/config-loader.d.ts +1 -0
- package/dist/src/config/config-loader.d.ts.map +1 -1
- package/dist/src/config/config-loader.js +16 -6
- package/dist/src/config/config-loader.js.map +1 -1
- package/dist/src/config/config-validator.d.ts +18 -49
- package/dist/src/config/config-validator.d.ts.map +1 -1
- package/dist/src/config/config-validator.js +64 -172
- package/dist/src/config/config-validator.js.map +1 -1
- package/dist/src/config/path-security-validator.d.ts +39 -0
- package/dist/src/config/path-security-validator.d.ts.map +1 -0
- package/dist/src/config/path-security-validator.js +134 -0
- package/dist/src/config/path-security-validator.js.map +1 -0
- package/dist/src/config/secure-config-loader.d.ts.map +1 -1
- package/dist/src/config/secure-config-loader.js +10 -2
- package/dist/src/config/secure-config-loader.js.map +1 -1
- package/dist/src/config/validation-suggestions.d.ts +13 -0
- package/dist/src/config/validation-suggestions.d.ts.map +1 -0
- package/dist/src/config/validation-suggestions.js +40 -0
- package/dist/src/config/validation-suggestions.js.map +1 -0
- package/dist/src/config/validation-types.d.ts +38 -0
- package/dist/src/config/validation-types.d.ts.map +1 -0
- package/dist/src/config/validation-types.js +7 -0
- package/dist/src/config/validation-types.js.map +1 -0
- package/dist/src/daemon/component-command-wiring.d.ts +19 -0
- package/dist/src/daemon/component-command-wiring.d.ts.map +1 -0
- package/dist/src/daemon/component-command-wiring.js +27 -0
- package/dist/src/daemon/component-command-wiring.js.map +1 -0
- package/dist/src/daemon/component-daemon-interface.d.ts +38 -0
- package/dist/src/daemon/component-daemon-interface.d.ts.map +1 -0
- package/dist/src/daemon/component-daemon-interface.js +72 -0
- package/dist/src/daemon/component-daemon-interface.js.map +1 -0
- package/dist/src/daemon/component-health.d.ts +38 -0
- package/dist/src/daemon/component-health.d.ts.map +1 -0
- package/dist/src/daemon/component-health.js +146 -0
- package/dist/src/daemon/component-health.js.map +1 -0
- package/dist/src/daemon/component-manager-errors.d.ts +20 -0
- package/dist/src/daemon/component-manager-errors.d.ts.map +1 -0
- package/dist/src/daemon/component-manager-errors.js +30 -0
- package/dist/src/daemon/component-manager-errors.js.map +1 -0
- package/dist/src/daemon/component-manager-types.d.ts +23 -0
- package/dist/src/daemon/component-manager-types.d.ts.map +1 -0
- package/dist/src/daemon/component-manager-types.js +5 -0
- package/dist/src/daemon/component-manager-types.js.map +1 -0
- package/dist/src/daemon/component-manager.d.ts +16 -64
- package/dist/src/daemon/component-manager.d.ts.map +1 -1
- package/dist/src/daemon/component-manager.js +72 -509
- package/dist/src/daemon/component-manager.js.map +1 -1
- package/dist/src/daemon/component-registration.d.ts +14 -0
- package/dist/src/daemon/component-registration.d.ts.map +1 -0
- package/dist/src/daemon/component-registration.js +92 -0
- package/dist/src/daemon/component-registration.js.map +1 -0
- package/dist/src/daemon/component-streaming.d.ts +36 -0
- package/dist/src/daemon/component-streaming.d.ts.map +1 -0
- package/dist/src/daemon/component-streaming.js +112 -0
- package/dist/src/daemon/component-streaming.js.map +1 -0
- package/dist/src/daemon/daemon-lifecycle.d.ts +37 -0
- package/dist/src/daemon/daemon-lifecycle.d.ts.map +1 -0
- package/dist/src/daemon/daemon-lifecycle.js +362 -0
- package/dist/src/daemon/daemon-lifecycle.js.map +1 -0
- package/dist/src/daemon/daemon-queries.d.ts +53 -0
- package/dist/src/daemon/daemon-queries.d.ts.map +1 -0
- package/dist/src/daemon/daemon-queries.js +113 -0
- package/dist/src/daemon/daemon-queries.js.map +1 -0
- package/dist/src/daemon/daemon-types.d.ts +30 -0
- package/dist/src/daemon/daemon-types.d.ts.map +1 -0
- package/dist/src/daemon/daemon-types.js +8 -0
- package/dist/src/daemon/daemon-types.js.map +1 -0
- package/dist/src/daemon/disposable-base.d.ts +49 -0
- package/dist/src/daemon/disposable-base.d.ts.map +1 -0
- package/dist/src/daemon/disposable-base.js +70 -0
- package/dist/src/daemon/disposable-base.js.map +1 -0
- package/dist/src/daemon/disposal-guard.d.ts +27 -0
- package/dist/src/daemon/disposal-guard.d.ts.map +1 -0
- package/dist/src/daemon/disposal-guard.js +58 -0
- package/dist/src/daemon/disposal-guard.js.map +1 -0
- package/dist/src/daemon/ipc-command-handler.d.ts +8 -34
- package/dist/src/daemon/ipc-command-handler.d.ts.map +1 -1
- package/dist/src/daemon/ipc-command-handler.js +14 -362
- package/dist/src/daemon/ipc-command-handler.js.map +1 -1
- package/dist/src/daemon/log-command-handlers.d.ts +19 -0
- package/dist/src/daemon/log-command-handlers.d.ts.map +1 -0
- package/dist/src/daemon/log-command-handlers.js +114 -0
- package/dist/src/daemon/log-command-handlers.js.map +1 -0
- package/dist/src/daemon/process-command-handlers.d.ts +32 -0
- package/dist/src/daemon/process-command-handlers.d.ts.map +1 -0
- package/dist/src/daemon/process-command-handlers.js +223 -0
- package/dist/src/daemon/process-command-handlers.js.map +1 -0
- package/dist/src/daemon/procman-daemon.d.ts +22 -98
- package/dist/src/daemon/procman-daemon.d.ts.map +1 -1
- package/dist/src/daemon/procman-daemon.js +107 -604
- package/dist/src/daemon/procman-daemon.js.map +1 -1
- package/dist/src/daemon/resource-manager.d.ts +6 -124
- package/dist/src/daemon/resource-manager.d.ts.map +1 -1
- package/dist/src/daemon/resource-manager.js +5 -231
- package/dist/src/daemon/resource-manager.js.map +1 -1
- package/dist/src/daemon/resource-types.d.ts +23 -0
- package/dist/src/daemon/resource-types.d.ts.map +1 -0
- package/dist/src/daemon/resource-types.js +7 -0
- package/dist/src/daemon/resource-types.js.map +1 -0
- package/dist/src/daemon/shutdown-orchestrator.d.ts +32 -0
- package/dist/src/daemon/shutdown-orchestrator.d.ts.map +1 -0
- package/dist/src/daemon/shutdown-orchestrator.js +123 -0
- package/dist/src/daemon/shutdown-orchestrator.js.map +1 -0
- package/dist/src/daemon/tracked-resources.d.ts +60 -0
- package/dist/src/daemon/tracked-resources.d.ts.map +1 -0
- package/dist/src/daemon/tracked-resources.js +124 -0
- package/dist/src/daemon/tracked-resources.js.map +1 -0
- package/dist/src/process-manager/index.d.ts +5 -1
- package/dist/src/process-manager/index.d.ts.map +1 -1
- package/dist/src/process-manager/index.js +3 -0
- package/dist/src/process-manager/index.js.map +1 -1
- package/dist/src/process-manager/interfaces/process-group.d.ts +6 -0
- package/dist/src/process-manager/interfaces/process-group.d.ts.map +1 -1
- package/dist/src/process-manager/managed-process-events.d.ts +22 -0
- package/dist/src/process-manager/managed-process-events.d.ts.map +1 -0
- package/dist/src/process-manager/managed-process-events.js +5 -0
- package/dist/src/process-manager/managed-process-events.js.map +1 -0
- package/dist/src/process-manager/managed-process-info.d.ts +11 -53
- package/dist/src/process-manager/managed-process-info.d.ts.map +1 -1
- package/dist/src/process-manager/managed-process-info.js +46 -133
- package/dist/src/process-manager/managed-process-info.js.map +1 -1
- package/dist/src/process-manager/process-batch-operations.d.ts +62 -0
- package/dist/src/process-manager/process-batch-operations.d.ts.map +1 -0
- package/dist/src/process-manager/process-batch-operations.js +80 -0
- package/dist/src/process-manager/process-batch-operations.js.map +1 -0
- package/dist/src/process-manager/process-config.d.ts +49 -0
- package/dist/src/process-manager/process-config.d.ts.map +1 -0
- package/dist/src/process-manager/process-config.js +68 -0
- package/dist/src/process-manager/process-config.js.map +1 -0
- package/dist/src/process-manager/process-event-forwarding.d.ts +18 -0
- package/dist/src/process-manager/process-event-forwarding.d.ts.map +1 -0
- package/dist/src/process-manager/process-event-forwarding.js +105 -0
- package/dist/src/process-manager/process-event-forwarding.js.map +1 -0
- package/dist/src/process-manager/process-group-manager.d.ts.map +1 -1
- package/dist/src/process-manager/process-group-manager.js +0 -1
- package/dist/src/process-manager/process-group-manager.js.map +1 -1
- package/dist/src/process-manager/process-info-queries.d.ts +48 -0
- package/dist/src/process-manager/process-info-queries.d.ts.map +1 -0
- package/dist/src/process-manager/process-info-queries.js +92 -0
- package/dist/src/process-manager/process-info-queries.js.map +1 -0
- package/dist/src/process-manager/process-manager.d.ts +5 -174
- package/dist/src/process-manager/process-manager.d.ts.map +1 -1
- package/dist/src/process-manager/process-manager.js +58 -398
- package/dist/src/process-manager/process-manager.js.map +1 -1
- package/dist/src/process-manager/process-monitoring.d.ts +80 -0
- package/dist/src/process-manager/process-monitoring.d.ts.map +1 -0
- package/dist/src/process-manager/process-monitoring.js +142 -0
- package/dist/src/process-manager/process-monitoring.js.map +1 -0
- package/dist/src/process-manager/process-statistics.d.ts +37 -0
- package/dist/src/process-manager/process-statistics.d.ts.map +1 -0
- package/dist/src/process-manager/process-statistics.js +24 -0
- package/dist/src/process-manager/process-statistics.js.map +1 -0
- package/dist/src/process-manager/restart-manager.d.ts +82 -0
- package/dist/src/process-manager/restart-manager.d.ts.map +1 -0
- package/dist/src/process-manager/restart-manager.js +150 -0
- package/dist/src/process-manager/restart-manager.js.map +1 -0
- package/dist/src/services/app-logger.d.ts +144 -0
- package/dist/src/services/app-logger.d.ts.map +1 -0
- package/dist/src/services/app-logger.js +489 -0
- package/dist/src/services/app-logger.js.map +1 -0
- package/dist/src/services/log-buffer.d.ts +35 -0
- package/dist/src/services/log-buffer.d.ts.map +1 -0
- package/dist/src/services/log-buffer.js +87 -0
- package/dist/src/services/log-buffer.js.map +1 -0
- package/dist/src/services/log-event-manager.d.ts +32 -0
- package/dist/src/services/log-event-manager.d.ts.map +1 -0
- package/dist/src/services/log-event-manager.js +31 -0
- package/dist/src/services/log-event-manager.js.map +1 -0
- package/dist/src/services/log-file-manager.d.ts +46 -0
- package/dist/src/services/log-file-manager.d.ts.map +1 -0
- package/dist/src/services/log-file-manager.js +156 -0
- package/dist/src/services/log-file-manager.js.map +1 -0
- package/dist/src/services/log-level-strategy.d.ts +19 -0
- package/dist/src/services/log-level-strategy.d.ts.map +1 -0
- package/dist/src/services/log-level-strategy.js +40 -0
- package/dist/src/services/log-level-strategy.js.map +1 -0
- package/dist/src/services/log-manager-types.d.ts +117 -0
- package/dist/src/services/log-manager-types.d.ts.map +1 -0
- package/dist/src/services/log-manager-types.js +12 -0
- package/dist/src/services/log-manager-types.js.map +1 -0
- package/dist/src/services/log-manager.d.ts +8 -35
- package/dist/src/services/log-manager.d.ts.map +1 -1
- package/dist/src/services/log-manager.js +10 -870
- package/dist/src/services/log-manager.js.map +1 -1
- package/dist/src/services/log-preprocessor.d.ts +30 -0
- package/dist/src/services/log-preprocessor.d.ts.map +1 -0
- package/dist/src/services/log-preprocessor.js +65 -0
- package/dist/src/services/log-preprocessor.js.map +1 -0
- package/dist/src/services/process-log-integration.d.ts +7 -2
- package/dist/src/services/process-log-integration.d.ts.map +1 -1
- package/dist/src/services/process-log-integration.js.map +1 -1
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
52
|
-
warningThreshold: 100 * 1024 * 1024,
|
|
53
|
-
criticalThreshold: 200 * 1024 * 1024,
|
|
54
|
-
enableLogging: process.env.NODE_ENV !== 'test',
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
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();
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
215
|
+
return _getConfigLoader(this.getContext());
|
|
628
216
|
}
|
|
629
217
|
getProcessManager() {
|
|
630
|
-
return this.
|
|
218
|
+
return _getProcessManager(this.getContext());
|
|
631
219
|
}
|
|
632
220
|
getLogManager() {
|
|
633
|
-
return this.
|
|
221
|
+
return _getLogManager(this.getContext());
|
|
634
222
|
}
|
|
635
223
|
getIPCServer() {
|
|
636
|
-
return this.
|
|
224
|
+
return _getIPCServer(this.getContext());
|
|
637
225
|
}
|
|
638
|
-
/**
|
|
639
|
-
* Get all process statuses (moved from old implementation)
|
|
640
|
-
*/
|
|
641
226
|
async getAllProcessStatuses() {
|
|
642
|
-
|
|
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
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
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
|
-
|
|
780
|
-
|
|
781
|
-
|
|
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
|
}
|