@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.
- package/CHANGELOG.md +17 -0
- package/LICENSE +22 -0
- package/README.md +210 -0
- package/bin/autopilot.js +48 -0
- package/docs/ARCHITECTURE.md +534 -0
- package/docs/CONFIGURATION.md +82 -0
- package/docs/CONTRIBUTING.md +47 -0
- package/docs/DESIGN_DELIVERY.md +441 -0
- package/docs/DESIGN_SUMMARY.md +61 -0
- package/docs/EXTENDING.md +69 -0
- package/docs/SAFETY-FEATURES.md +56 -0
- package/docs/START_HERE.md +41 -0
- package/docs/TROUBLESHOOTING.md +40 -0
- package/package.json +59 -0
- package/src/commands/doctor.js +121 -0
- package/src/commands/init.js +92 -0
- package/src/commands/start.js +41 -0
- package/src/commands/status.js +56 -0
- package/src/commands/stop.js +50 -0
- package/src/config/defaults.js +34 -0
- package/src/config/ignore.js +37 -0
- package/src/config/loader.js +47 -0
- package/src/core/commit.js +116 -0
- package/src/core/git.js +154 -0
- package/src/core/safety.js +38 -0
- package/src/core/watcher.js +309 -0
- package/src/index.js +50 -0
- package/src/utils/banner.js +6 -0
- package/src/utils/logger.js +49 -0
- package/src/utils/paths.js +59 -0
- package/src/utils/process.js +141 -0
|
@@ -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
|
+
};
|