aegis-bridge 2.3.7 → 2.3.9
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/pipeline.js +5 -0
- package/dist/server.js +8 -0
- package/dist/signal-cleanup-helper.d.ts +48 -0
- package/dist/signal-cleanup-helper.js +118 -0
- package/package.json +1 -1
package/dist/pipeline.js
CHANGED
|
@@ -179,6 +179,11 @@ export class PipelineManager {
|
|
|
179
179
|
setTimeout(() => {
|
|
180
180
|
this.pipelines.delete(pipelineId);
|
|
181
181
|
this.pipelineConfigs.delete(pipelineId); // #219: clean up stored config
|
|
182
|
+
// #578: Stop polling when no pipelines remain
|
|
183
|
+
if (this.pipelines.size === 0 && this.pollInterval) {
|
|
184
|
+
clearInterval(this.pollInterval);
|
|
185
|
+
this.pollInterval = null;
|
|
186
|
+
}
|
|
182
187
|
}, 30_000);
|
|
183
188
|
}
|
|
184
189
|
}
|
package/dist/server.js
CHANGED
|
@@ -34,6 +34,7 @@ import { MetricsCollector } from './metrics.js';
|
|
|
34
34
|
import { registerHookRoutes } from './hooks.js';
|
|
35
35
|
import { registerWsTerminalRoute } from './ws-terminal.js';
|
|
36
36
|
import { SwarmMonitor } from './swarm-monitor.js';
|
|
37
|
+
import { killAllSessions } from './signal-cleanup-helper.js';
|
|
37
38
|
import { execFileSync } from 'node:child_process';
|
|
38
39
|
import { authKeySchema, sendMessageSchema, commandSchema, bashSchema, screenshotSchema, permissionHookSchema, stopHookSchema, batchSessionSchema, pipelineSchema, parseIntSafe, isValidUUID, } from './validation.js';
|
|
39
40
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -1503,6 +1504,13 @@ async function main() {
|
|
|
1503
1504
|
clearInterval(metricsSaveInterval);
|
|
1504
1505
|
clearInterval(ipPruneInterval);
|
|
1505
1506
|
clearInterval(authSweepInterval);
|
|
1507
|
+
// Issue #569: Kill all CC sessions and tmux windows before exit
|
|
1508
|
+
try {
|
|
1509
|
+
await killAllSessions(sessions, tmux);
|
|
1510
|
+
}
|
|
1511
|
+
catch (e) {
|
|
1512
|
+
console.error('Error killing sessions:', e);
|
|
1513
|
+
}
|
|
1506
1514
|
// 3. Destroy channels (awaits Telegram poll loop)
|
|
1507
1515
|
try {
|
|
1508
1516
|
await channels.destroy();
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* signal-cleanup-helper.ts — Signal handler cleanup logic for Issue #569.
|
|
3
|
+
*
|
|
4
|
+
* Provides killAllSessions() and createSignalHandler() for graceful shutdown.
|
|
5
|
+
* Separated from server.ts for testability.
|
|
6
|
+
*/
|
|
7
|
+
import type { SessionManager } from './session.js';
|
|
8
|
+
import type { TmuxManager } from './tmux.js';
|
|
9
|
+
/** Result of killAllSessions operation. */
|
|
10
|
+
export interface KillAllResult {
|
|
11
|
+
/** Number of sessions successfully killed. */
|
|
12
|
+
killed: number;
|
|
13
|
+
/** Number of sessions that failed to kill. */
|
|
14
|
+
errors: number;
|
|
15
|
+
}
|
|
16
|
+
/** Result of killAllSessionsWithTimeout operation. */
|
|
17
|
+
export interface KillAllWithTimeoutResult extends KillAllResult {
|
|
18
|
+
/** Whether any session kill timed out. */
|
|
19
|
+
timedOut: boolean;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Kill all active CC sessions and the tmux session.
|
|
23
|
+
* Best-effort: continues even if individual session kills fail.
|
|
24
|
+
*
|
|
25
|
+
* @param sessions - SessionManager instance
|
|
26
|
+
* @param tmux - TmuxManager instance
|
|
27
|
+
* @returns Number of sessions killed and errors encountered
|
|
28
|
+
*/
|
|
29
|
+
export declare function killAllSessions(sessions: SessionManager, tmux: TmuxManager): Promise<KillAllResult>;
|
|
30
|
+
/**
|
|
31
|
+
* Kill all sessions with per-session timeout protection.
|
|
32
|
+
* If a session kill hangs beyond the timeout, it is skipped.
|
|
33
|
+
*
|
|
34
|
+
* @param sessions - SessionManager instance
|
|
35
|
+
* @param tmux - TmuxManager instance
|
|
36
|
+
* @param perSessionTimeoutMs - Maximum time to wait per session kill (default 5000ms)
|
|
37
|
+
* @returns Result including timeout status
|
|
38
|
+
*/
|
|
39
|
+
export declare function killAllSessionsWithTimeout(sessions: SessionManager, tmux: TmuxManager, perSessionTimeoutMs?: number): Promise<KillAllWithTimeoutResult>;
|
|
40
|
+
/**
|
|
41
|
+
* Create a signal handler that kills all sessions on SIGTERM/SIGINT.
|
|
42
|
+
* Includes reentrance guard to prevent double cleanup on rapid signals.
|
|
43
|
+
*
|
|
44
|
+
* @param sessions - SessionManager instance
|
|
45
|
+
* @param tmux - TmuxManager instance
|
|
46
|
+
* @returns Signal handler function
|
|
47
|
+
*/
|
|
48
|
+
export declare function createSignalHandler(sessions: SessionManager, tmux: TmuxManager): (signal: string) => void;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* signal-cleanup-helper.ts — Signal handler cleanup logic for Issue #569.
|
|
3
|
+
*
|
|
4
|
+
* Provides killAllSessions() and createSignalHandler() for graceful shutdown.
|
|
5
|
+
* Separated from server.ts for testability.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Kill all active CC sessions and the tmux session.
|
|
9
|
+
* Best-effort: continues even if individual session kills fail.
|
|
10
|
+
*
|
|
11
|
+
* @param sessions - SessionManager instance
|
|
12
|
+
* @param tmux - TmuxManager instance
|
|
13
|
+
* @returns Number of sessions killed and errors encountered
|
|
14
|
+
*/
|
|
15
|
+
export async function killAllSessions(sessions, tmux) {
|
|
16
|
+
const allSessions = sessions.listSessions();
|
|
17
|
+
let killed = 0;
|
|
18
|
+
let errors = 0;
|
|
19
|
+
// Kill each session individually (restores settings, cleans up temp files)
|
|
20
|
+
for (const session of allSessions) {
|
|
21
|
+
try {
|
|
22
|
+
await sessions.killSession(session.id);
|
|
23
|
+
killed++;
|
|
24
|
+
}
|
|
25
|
+
catch (e) {
|
|
26
|
+
errors++;
|
|
27
|
+
console.error(`Signal cleanup: failed to kill session ${session.windowName} (${session.id.slice(0, 8)}): ${e.message}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// Final fallback: kill the entire tmux session to ensure nothing is left
|
|
31
|
+
try {
|
|
32
|
+
await tmux.killSession();
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
console.error(`Signal cleanup: failed to kill tmux session: ${e.message}`);
|
|
36
|
+
}
|
|
37
|
+
console.log(`Signal cleanup: killed ${killed} sessions (${errors} errors)`);
|
|
38
|
+
return { killed, errors };
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Kill all sessions with per-session timeout protection.
|
|
42
|
+
* If a session kill hangs beyond the timeout, it is skipped.
|
|
43
|
+
*
|
|
44
|
+
* @param sessions - SessionManager instance
|
|
45
|
+
* @param tmux - TmuxManager instance
|
|
46
|
+
* @param perSessionTimeoutMs - Maximum time to wait per session kill (default 5000ms)
|
|
47
|
+
* @returns Result including timeout status
|
|
48
|
+
*/
|
|
49
|
+
export async function killAllSessionsWithTimeout(sessions, tmux, perSessionTimeoutMs = 5_000) {
|
|
50
|
+
const allSessions = sessions.listSessions();
|
|
51
|
+
let killed = 0;
|
|
52
|
+
let errors = 0;
|
|
53
|
+
let timedOut = false;
|
|
54
|
+
for (const session of allSessions) {
|
|
55
|
+
try {
|
|
56
|
+
await withTimeout(sessions.killSession(session.id), perSessionTimeoutMs, `Session kill timeout for ${session.windowName}`);
|
|
57
|
+
killed++;
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
if (e instanceof TimeoutError) {
|
|
61
|
+
timedOut = true;
|
|
62
|
+
console.error(`Signal cleanup: TIMED OUT killing session ${session.windowName}`);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
console.error(`Signal cleanup: failed to kill session ${session.windowName}: ${e.message}`);
|
|
66
|
+
}
|
|
67
|
+
errors++;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Final fallback: kill entire tmux session
|
|
71
|
+
try {
|
|
72
|
+
await tmux.killSession();
|
|
73
|
+
}
|
|
74
|
+
catch (e) {
|
|
75
|
+
console.error(`Signal cleanup: failed to kill tmux session: ${e.message}`);
|
|
76
|
+
}
|
|
77
|
+
console.log(`Signal cleanup: killed ${killed}/${allSessions.length} sessions (${errors} errors, ${timedOut ? 'some timed out' : 'no timeouts'})`);
|
|
78
|
+
return { killed, errors, timedOut };
|
|
79
|
+
}
|
|
80
|
+
/** Error thrown when an operation exceeds its timeout. */
|
|
81
|
+
class TimeoutError extends Error {
|
|
82
|
+
constructor(message) {
|
|
83
|
+
super(message);
|
|
84
|
+
this.name = 'TimeoutError';
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/** Wrap a promise with a timeout. */
|
|
88
|
+
function withTimeout(promise, ms, message) {
|
|
89
|
+
return new Promise((resolve, reject) => {
|
|
90
|
+
const timer = setTimeout(() => reject(new TimeoutError(message)), ms);
|
|
91
|
+
promise.then((val) => { clearTimeout(timer); resolve(val); }, (err) => { clearTimeout(timer); reject(err); });
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Create a signal handler that kills all sessions on SIGTERM/SIGINT.
|
|
96
|
+
* Includes reentrance guard to prevent double cleanup on rapid signals.
|
|
97
|
+
*
|
|
98
|
+
* @param sessions - SessionManager instance
|
|
99
|
+
* @param tmux - TmuxManager instance
|
|
100
|
+
* @returns Signal handler function
|
|
101
|
+
*/
|
|
102
|
+
export function createSignalHandler(sessions, tmux) {
|
|
103
|
+
let shuttingDown = false;
|
|
104
|
+
return (signal) => {
|
|
105
|
+
if (shuttingDown)
|
|
106
|
+
return;
|
|
107
|
+
shuttingDown = true;
|
|
108
|
+
console.log(`${signal} received — cleaning up ${sessions.listSessions().length} active sessions...`);
|
|
109
|
+
void killAllSessions(sessions, tmux)
|
|
110
|
+
.then((result) => {
|
|
111
|
+
console.log(`${signal} cleanup complete: ${result.killed} sessions killed`);
|
|
112
|
+
})
|
|
113
|
+
.catch((e) => {
|
|
114
|
+
console.error(`${signal} cleanup error:`, e);
|
|
115
|
+
});
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=signal-cleanup-helper.js.map
|