@traisetech/autopilot 0.1.3

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.
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Process management utilities
3
+ * Built by Praise Masunga (PraiseTechzw)
4
+ */
5
+
6
+ const fs = require('fs-extra');
7
+ const path = require('path');
8
+ const logger = require('./logger');
9
+ const { ensureConfigDir } = require('./paths');
10
+
11
+ /**
12
+ * Get path to PID file for a repository or global
13
+ * @param {string} [repoPath] - Repository path
14
+ * @returns {string} PID file path
15
+ */
16
+ const getPidPath = (repoPath) => {
17
+ if (repoPath) {
18
+ return path.join(repoPath, '.autopilot.pid');
19
+ }
20
+ // Fallback to global pid if needed, though mostly used per-repo
21
+ return path.join(require('os').homedir(), '.autopilot', 'autopilot.pid');
22
+ };
23
+
24
+ /**
25
+ * Save current process PID to file
26
+ * @param {string} repoPath - Repository path
27
+ */
28
+ const savePid = async (repoPath) => {
29
+ try {
30
+ const pidPath = getPidPath(repoPath);
31
+ await fs.writeFile(pidPath, process.pid.toString());
32
+ } catch (error) {
33
+ logger.error(`Failed to save PID file: ${error.message}`);
34
+ }
35
+ };
36
+
37
+ /**
38
+ * Remove PID file
39
+ * @param {string} repoPath - Repository path
40
+ */
41
+ const removePid = async (repoPath) => {
42
+ try {
43
+ const pidPath = getPidPath(repoPath);
44
+ if (await fs.pathExists(pidPath)) {
45
+ await fs.remove(pidPath);
46
+ }
47
+ } catch (error) {
48
+ // Ignore errors during cleanup
49
+ }
50
+ };
51
+
52
+ /**
53
+ * Check if a process is running
54
+ * @param {number} pid - Process ID
55
+ * @returns {boolean} True if running
56
+ */
57
+ const isProcessRunning = (pid) => {
58
+ try {
59
+ process.kill(pid, 0);
60
+ return true;
61
+ } catch (e) {
62
+ return false;
63
+ }
64
+ };
65
+
66
+ /**
67
+ * Read PID from file and check if running
68
+ * @param {string} repoPath - Repository path
69
+ * @returns {Promise<number|null>} PID if running, null otherwise
70
+ */
71
+ const getRunningPid = async (repoPath) => {
72
+ try {
73
+ const pidPath = getPidPath(repoPath);
74
+ if (await fs.pathExists(pidPath)) {
75
+ const pid = parseInt(await fs.readFile(pidPath, 'utf-8'), 10);
76
+ if (isProcessRunning(pid)) {
77
+ return pid;
78
+ }
79
+ // Stale PID file
80
+ await removePid(repoPath);
81
+ }
82
+ return null;
83
+ } catch (error) {
84
+ return null;
85
+ }
86
+ };
87
+
88
+ /**
89
+ * Register process signal handlers for graceful shutdown
90
+ * @param {Function} cleanupFn - Async cleanup function to run on exit
91
+ */
92
+ const registerProcessHandlers = (cleanupFn) => {
93
+ let cleaningUp = false;
94
+
95
+ const handleSignal = async (signal) => {
96
+ if (cleaningUp) return;
97
+ cleaningUp = true;
98
+
99
+ logger.info(`Received ${signal}, shutting down...`);
100
+
101
+ try {
102
+ if (cleanupFn) {
103
+ await cleanupFn();
104
+ }
105
+ } catch (error) {
106
+ logger.error(`Error during cleanup: ${error.message}`);
107
+ } finally {
108
+ process.exit(0);
109
+ }
110
+ };
111
+
112
+ process.on('SIGINT', () => handleSignal('SIGINT'));
113
+ process.on('SIGTERM', () => handleSignal('SIGTERM'));
114
+
115
+ // Handle uncaught errors to try to cleanup if possible
116
+ process.on('uncaughtException', async (error) => {
117
+ logger.error(`Uncaught Exception: ${error.message}`);
118
+ logger.error(error.stack);
119
+ if (!cleaningUp) {
120
+ cleaningUp = true;
121
+ try {
122
+ if (cleanupFn) await cleanupFn();
123
+ } catch (e) {
124
+ // Ignore
125
+ }
126
+ process.exit(1);
127
+ }
128
+ });
129
+
130
+ process.on('unhandledRejection', async (reason) => {
131
+ logger.error(`Unhandled Rejection: ${reason}`);
132
+ });
133
+ };
134
+
135
+ module.exports = {
136
+ savePid,
137
+ removePid,
138
+ getRunningPid,
139
+ isProcessRunning,
140
+ registerProcessHandlers
141
+ };