commandmate 0.3.5 → 0.4.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/.next/BUILD_ID +1 -1
- package/.next/app-build-manifest.json +19 -23
- package/.next/app-path-routes-manifest.json +1 -1
- package/.next/build-manifest.json +5 -5
- package/.next/cache/.tsbuildinfo +1 -1
- package/.next/cache/config.json +3 -3
- package/.next/cache/webpack/client-production/0.pack +0 -0
- package/.next/cache/webpack/client-production/1.pack +0 -0
- package/.next/cache/webpack/client-production/2.pack +0 -0
- package/.next/cache/webpack/client-production/index.pack +0 -0
- package/.next/cache/webpack/client-production/index.pack.old +0 -0
- package/.next/cache/webpack/edge-server-production/0.pack +0 -0
- package/.next/cache/webpack/edge-server-production/index.pack +0 -0
- package/.next/cache/webpack/server-production/0.pack +0 -0
- package/.next/cache/webpack/server-production/index.pack +0 -0
- package/.next/next-server.js.nft.json +1 -1
- package/.next/prerender-manifest.json +1 -1
- package/.next/react-loadable-manifest.json +69 -55
- package/.next/required-server-files.json +1 -1
- package/.next/server/app/_not-found/page.js +1 -1
- package/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update-check/route.js +1 -1
- package/.next/server/app/api/repositories/clone/route.js +1 -1
- package/.next/server/app/api/repositories/route.js +8 -8
- package/.next/server/app/api/repositories/scan/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/capture/route.js +1 -2
- package/.next/server/app/api/worktrees/[id]/capture/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/files/[...path]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/route.js +2 -2
- package/.next/server/app/api/worktrees/[id]/search/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/terminal/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/terminal/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/[...path]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/upload/[...path]/route.js +1 -1
- package/.next/server/app/api/worktrees/route.js +1 -1
- package/.next/server/app/login/page.js +1 -1
- package/.next/server/app/login/page.js.nft.json +1 -1
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/page.js +3 -3
- package/.next/server/app/page.js.nft.json +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/proxy/[...path]/route.js +4 -4
- package/.next/server/app/worktrees/[id]/files/[...path]/page.js +1 -1
- package/.next/server/app/worktrees/[id]/files/[...path]/page.js.nft.json +1 -1
- package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/page.js +6 -6
- package/.next/server/app/worktrees/[id]/page.js.nft.json +1 -1
- package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/terminal/page.js +2 -4
- package/.next/server/app/worktrees/[id]/terminal/page.js.nft.json +1 -1
- package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +8 -8
- package/.next/server/chunks/{3294.js → 1628.js} +3 -3
- package/.next/server/chunks/185.js +36 -0
- package/.next/server/chunks/3860.js +1 -1
- package/.next/server/chunks/4893.js +2 -2
- package/.next/server/chunks/4952.js +1 -1
- package/.next/server/chunks/5488.js +6 -6
- package/.next/server/chunks/7425.js +34 -31
- package/.next/server/chunks/7566.js +2 -2
- package/.next/server/chunks/8199.js +1 -0
- package/.next/server/chunks/8585.js +1 -1
- package/.next/server/chunks/8693.js +1 -1
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/middleware-manifest.json +5 -5
- package/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/12-00c528d46a0a0a1d.js +1 -0
- package/.next/static/chunks/{13.feeafc7cc620f8c4.js → 13.b9521543496f4468.js} +1 -1
- package/.next/static/chunks/1334.bfedf44ee9fe2761.js +1 -0
- package/.next/static/chunks/143.eb6b4671490cd223.js +1 -0
- package/.next/static/chunks/{3574.7a94c27e6a496a56.js → 1442.74b5f4de9a4b4e1b.js} +1 -1
- package/.next/static/chunks/2083-b5bed0c77cc53281.js +1 -0
- package/.next/static/chunks/2725.eb2d236c8030711c.js +1 -0
- package/.next/static/chunks/3398-3d40a17387bd554b.js +1 -0
- package/.next/static/chunks/3516.3c576047408cae6b.js +1 -0
- package/.next/static/chunks/3559.422c6ca760b85750.js +1 -0
- package/.next/static/chunks/3956.52c5b9a0071a641d.js +1 -0
- package/.next/static/chunks/4012.32b576a4fa621774.js +1 -0
- package/.next/static/chunks/4212.e7ba1009bc1da62d.js +131 -0
- package/.next/static/chunks/4303.caf91e86105d5e70.js +1 -0
- package/.next/static/chunks/4327.4dcda9b6fab6a385.js +82 -0
- package/.next/static/chunks/4671.d86d21d0dfdace41.js +1 -0
- package/.next/static/chunks/5518.ec88dcb5a27b17fe.js +1 -0
- package/.next/static/chunks/6434.08d262283371d333.js +1 -0
- package/.next/static/chunks/{656.5e2de0173f5a06bd.js → 656.dc26b973d07d9627.js} +5 -5
- package/.next/static/chunks/7119.01777af21b55740c.js +1 -0
- package/.next/static/chunks/7293.fb88bb102af4aa04.js +1 -0
- package/.next/static/chunks/8913-40625650292eb3d0.js +1 -0
- package/.next/static/chunks/8977.fc18b8260cd8bc1f.js +1 -0
- package/.next/static/chunks/9552.d959149efd41e84b.js +1 -0
- package/.next/static/chunks/app/layout-7198a7a49aa21a97.js +1 -0
- package/.next/static/chunks/app/page-7498cf75e69d9227.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/files/[...path]/page-0599f64a8e80d255.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/page-94ad7a1ce1f0c440.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/terminal/page-175b618c047bc992.js +1 -0
- package/.next/static/chunks/d3ac728e.daf595a898e9b720.js +1 -0
- package/.next/static/chunks/webpack-f7111aab807d73b9.js +1 -0
- package/.next/static/css/f7dc01350168df01.css +3 -0
- package/.next/trace +5 -5
- package/README.md +66 -56
- package/dist/server/server.js +5 -0
- package/dist/server/src/lib/auto-yes-manager.js +58 -18
- package/dist/server/src/lib/claude-session.js +9 -3
- package/dist/server/src/lib/cli-session.js +60 -10
- package/dist/server/src/lib/cli-tools/codex.js +7 -7
- package/dist/server/src/lib/cli-tools/gemini.js +3 -0
- package/dist/server/src/lib/cli-tools/opencode-config.js +179 -33
- package/dist/server/src/lib/cli-tools/opencode.js +5 -0
- package/dist/server/src/lib/cli-tools/vibe-local.js +3 -0
- package/dist/server/src/lib/cmate-parser.js +7 -7
- package/dist/server/src/lib/db-migrations.js +18 -1
- package/dist/server/src/lib/errors.js +153 -0
- package/dist/server/src/lib/prompt-answer-sender.js +3 -0
- package/dist/server/src/lib/prompt-detector.js +49 -7
- package/dist/server/src/lib/resource-cleanup.js +257 -0
- package/dist/server/src/lib/schedule-manager.js +269 -83
- package/dist/server/src/lib/tmux-capture-cache.js +221 -0
- package/dist/server/src/lib/tmux.js +41 -20
- package/dist/server/src/types/markdown-editor.js +9 -1
- package/package.json +11 -8
- package/.next/server/chunks/539.js +0 -35
- package/.next/server/chunks/7458.js +0 -1
- package/.next/server/chunks/7808.js +0 -1
- package/.next/static/chunks/1038-3509435b68c0967e.js +0 -1
- package/.next/static/chunks/1098.49268c9fe1b028fa.js +0 -1
- package/.next/static/chunks/2335-98a211e00b94c7ac.js +0 -1
- package/.next/static/chunks/3559.f073f72c4466ce0e.js +0 -1
- package/.next/static/chunks/3843.3fdda732987f7bb8.js +0 -1
- package/.next/static/chunks/4212.52c1bb34fc97d0d0.js +0 -131
- package/.next/static/chunks/4327.157a4c226d919531.js +0 -60
- package/.next/static/chunks/4362.7bd6f0282e49d79b.js +0 -1
- package/.next/static/chunks/4721.40615a5f4f32b5fb.js +0 -1
- package/.next/static/chunks/5112.17318d1c6b28044b.js +0 -1
- package/.next/static/chunks/6406.9653f0d41ab85059.js +0 -1
- package/.next/static/chunks/6792.3c01ac4dda4b5c6d.js +0 -1
- package/.next/static/chunks/8091-d65d2ab6daed23c6.js +0 -1
- package/.next/static/chunks/8125.245a9df052d274fb.js +0 -1
- package/.next/static/chunks/8522.1607e96011c66877.js +0 -1
- package/.next/static/chunks/8841.dadeb1ece8e46004.js +0 -1
- package/.next/static/chunks/8885.f8d9912b40d74811.js +0 -1
- package/.next/static/chunks/9178-88850a7c48deea07.js +0 -1
- package/.next/static/chunks/9552.b7dfb7903ead934b.js +0 -1
- package/.next/static/chunks/app/layout-9110f9a5e41c6bf4.js +0 -1
- package/.next/static/chunks/app/page-9e523a8f415bc707.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/files/[...path]/page-4a3c0861367e0391.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/page-8fb4dc30b58a5681.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/terminal/page-5d85a7e508ce36d3.js +0 -1
- package/.next/static/chunks/d3ac728e.6c9c508274d4d2d5.js +0 -1
- package/.next/static/chunks/webpack-81c97591dd5567ac.js +0 -1
- package/.next/static/css/45b3a41370668314.css +0 -3
- /package/.next/static/chunks/{30d07d85-393352a92199f695.js → 30d07d85.1dc99a921fc18e34.js} +0 -0
- /package/.next/static/{p3hosTZoJ22r35fWwUoLr → dwGMLEU53HOvFOWqiZOT0}/_buildManifest.js +0 -0
- /package/.next/static/{p3hosTZoJ22r35fWwUoLr → dwGMLEU53HOvFOWqiZOT0}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Resource Cleanup Module
|
|
4
|
+
* Issue #404: Long-running server resource leak prevention
|
|
5
|
+
*
|
|
6
|
+
* Section 1: MCP orphaned process cleanup (ppid=1 detection)
|
|
7
|
+
* Section 2: globalThis Map periodic cleanup (orphaned entries)
|
|
8
|
+
* Orchestrator: initResourceCleanup / stopResourceCleanup
|
|
9
|
+
*
|
|
10
|
+
* Security:
|
|
11
|
+
* - execFile() only (no exec()), per Issue #393 convention
|
|
12
|
+
* - PID validation: Number.isInteger(pid) && pid > 0
|
|
13
|
+
* - MCP_PROCESS_PATTERNS boundary match
|
|
14
|
+
* - Container environment detection (skip in Docker)
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.MAX_PS_OUTPUT_BYTES = exports.MCP_PROCESS_PATTERNS = exports.CLEANUP_INTERVAL_MS = void 0;
|
|
18
|
+
exports.cleanupOrphanedMcpProcesses = cleanupOrphanedMcpProcesses;
|
|
19
|
+
exports.cleanupOrphanedMapEntries = cleanupOrphanedMapEntries;
|
|
20
|
+
exports.initResourceCleanup = initResourceCleanup;
|
|
21
|
+
exports.stopResourceCleanup = stopResourceCleanup;
|
|
22
|
+
const child_process_1 = require("child_process");
|
|
23
|
+
const fs_1 = require("fs");
|
|
24
|
+
const auto_yes_manager_1 = require("./auto-yes-manager");
|
|
25
|
+
const schedule_manager_1 = require("./schedule-manager");
|
|
26
|
+
const db_instance_1 = require("./db-instance");
|
|
27
|
+
// =============================================================================
|
|
28
|
+
// Constants
|
|
29
|
+
// =============================================================================
|
|
30
|
+
/** Cleanup interval: 24 hours */
|
|
31
|
+
exports.CLEANUP_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
32
|
+
/** Process name patterns for MCP orphaned process detection */
|
|
33
|
+
exports.MCP_PROCESS_PATTERNS = ['codex mcp-server', 'playwright-mcp'];
|
|
34
|
+
/** Maximum ps output buffer size (1MB) */
|
|
35
|
+
exports.MAX_PS_OUTPUT_BYTES = 1 * 1024 * 1024;
|
|
36
|
+
// =============================================================================
|
|
37
|
+
// Section 1: MCP Orphaned Process Cleanup
|
|
38
|
+
// =============================================================================
|
|
39
|
+
/**
|
|
40
|
+
* Check if running in a container environment.
|
|
41
|
+
* Detects Docker/container by checking /proc/1/cgroup existence.
|
|
42
|
+
*
|
|
43
|
+
* @returns true if in a container environment
|
|
44
|
+
*/
|
|
45
|
+
function isContainerEnvironment() {
|
|
46
|
+
try {
|
|
47
|
+
return (0, fs_1.existsSync)('/proc/1/cgroup');
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Check if a process command matches any MCP process pattern.
|
|
55
|
+
* Uses case-insensitive substring match for reliability.
|
|
56
|
+
*
|
|
57
|
+
* @param command - Process command string from ps output
|
|
58
|
+
* @returns true if command matches an MCP process pattern
|
|
59
|
+
*/
|
|
60
|
+
function matchesMcpPattern(command) {
|
|
61
|
+
const lowerCommand = command.toLowerCase();
|
|
62
|
+
return exports.MCP_PROCESS_PATTERNS.some(pattern => lowerCommand.includes(pattern.toLowerCase()));
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Clean up orphaned MCP processes (ppid=1).
|
|
66
|
+
* Finds processes whose parent is init (ppid=1) and whose command
|
|
67
|
+
* matches MCP_PROCESS_PATTERNS, then sends SIGTERM.
|
|
68
|
+
*
|
|
69
|
+
* Skips execution in container environments to avoid killing init processes.
|
|
70
|
+
*
|
|
71
|
+
* @returns Result with killed/failed PIDs
|
|
72
|
+
*/
|
|
73
|
+
async function cleanupOrphanedMcpProcesses() {
|
|
74
|
+
const result = {
|
|
75
|
+
killedPids: [],
|
|
76
|
+
failedPids: [],
|
|
77
|
+
skipped: false,
|
|
78
|
+
};
|
|
79
|
+
// Skip in container environments
|
|
80
|
+
if (isContainerEnvironment()) {
|
|
81
|
+
result.skipped = true;
|
|
82
|
+
result.reason = 'container';
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
return new Promise((resolve) => {
|
|
86
|
+
(0, child_process_1.execFile)('ps', ['-eo', 'pid,ppid,command'], { maxBuffer: exports.MAX_PS_OUTPUT_BYTES }, (error, stdout) => {
|
|
87
|
+
if (error) {
|
|
88
|
+
console.warn('[resource-cleanup] Failed to list processes:', error);
|
|
89
|
+
resolve(result);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const lines = stdout.split('\n');
|
|
93
|
+
// Skip header line
|
|
94
|
+
for (let i = 1; i < lines.length; i++) {
|
|
95
|
+
const line = lines[i].trim();
|
|
96
|
+
if (!line)
|
|
97
|
+
continue;
|
|
98
|
+
// Parse: PID PPID COMMAND
|
|
99
|
+
const parts = line.split(/\s+/);
|
|
100
|
+
if (parts.length < 3)
|
|
101
|
+
continue;
|
|
102
|
+
const pid = parseInt(parts[0], 10);
|
|
103
|
+
const ppid = parseInt(parts[1], 10);
|
|
104
|
+
const command = parts.slice(2).join(' ');
|
|
105
|
+
// Validate PID
|
|
106
|
+
if (!Number.isInteger(pid) || pid <= 0)
|
|
107
|
+
continue;
|
|
108
|
+
if (!Number.isInteger(ppid))
|
|
109
|
+
continue;
|
|
110
|
+
// Only target orphaned processes (ppid=1) matching MCP patterns
|
|
111
|
+
if (ppid !== 1)
|
|
112
|
+
continue;
|
|
113
|
+
if (!matchesMcpPattern(command))
|
|
114
|
+
continue;
|
|
115
|
+
try {
|
|
116
|
+
process.kill(pid, 'SIGTERM');
|
|
117
|
+
result.killedPids.push(pid);
|
|
118
|
+
console.log(`[resource-cleanup] Killed orphaned MCP process: PID=${pid}, command="${command}"`);
|
|
119
|
+
}
|
|
120
|
+
catch (killError) {
|
|
121
|
+
result.failedPids.push(pid);
|
|
122
|
+
console.warn(`[resource-cleanup] Failed to kill PID=${pid}:`, killError);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (result.killedPids.length > 0) {
|
|
126
|
+
console.log(`[resource-cleanup] Cleaned up ${result.killedPids.length} orphaned MCP process(es)`);
|
|
127
|
+
}
|
|
128
|
+
resolve(result);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
// =============================================================================
|
|
133
|
+
// Section 2: globalThis Map Periodic Cleanup
|
|
134
|
+
// =============================================================================
|
|
135
|
+
/**
|
|
136
|
+
* Get all valid worktree IDs from the database.
|
|
137
|
+
*
|
|
138
|
+
* @returns Set of valid worktree IDs
|
|
139
|
+
*/
|
|
140
|
+
function getDbWorktreeIds() {
|
|
141
|
+
try {
|
|
142
|
+
const db = (0, db_instance_1.getDbInstance)();
|
|
143
|
+
const rows = db.prepare('SELECT id FROM worktrees').all();
|
|
144
|
+
return new Set(rows.map(r => r.id));
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
console.warn('[resource-cleanup] Failed to query worktrees from DB:', error);
|
|
148
|
+
return new Set();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Clean up orphaned entries from globalThis Maps.
|
|
153
|
+
* Compares Map keys against DB worktree IDs and removes entries
|
|
154
|
+
* for worktrees that no longer exist.
|
|
155
|
+
*
|
|
156
|
+
* Uses better-sqlite3's synchronous API, so the DB query and Map
|
|
157
|
+
* mutation happen in the same synchronous block (no race conditions).
|
|
158
|
+
*
|
|
159
|
+
* @returns Result with deleted entry IDs
|
|
160
|
+
*/
|
|
161
|
+
function cleanupOrphanedMapEntries() {
|
|
162
|
+
const result = {
|
|
163
|
+
deletedAutoYesStateIds: [],
|
|
164
|
+
deletedAutoYesPollerIds: [],
|
|
165
|
+
deletedScheduleWorktreeIds: [],
|
|
166
|
+
};
|
|
167
|
+
const validWorktreeIds = getDbWorktreeIds();
|
|
168
|
+
if (validWorktreeIds.size === 0) {
|
|
169
|
+
// If DB query returned nothing, skip cleanup to avoid false positives
|
|
170
|
+
return result;
|
|
171
|
+
}
|
|
172
|
+
// Cleanup autoYesStates
|
|
173
|
+
const autoYesStateIds = (0, auto_yes_manager_1.getAutoYesStateWorktreeIds)();
|
|
174
|
+
for (const worktreeId of autoYesStateIds) {
|
|
175
|
+
if (!validWorktreeIds.has(worktreeId)) {
|
|
176
|
+
(0, auto_yes_manager_1.deleteAutoYesState)(worktreeId);
|
|
177
|
+
result.deletedAutoYesStateIds.push(worktreeId);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// Cleanup autoYesPollerStates
|
|
181
|
+
const autoYesPollerIds = (0, auto_yes_manager_1.getAutoYesPollerWorktreeIds)();
|
|
182
|
+
for (const worktreeId of autoYesPollerIds) {
|
|
183
|
+
if (!validWorktreeIds.has(worktreeId)) {
|
|
184
|
+
(0, auto_yes_manager_1.stopAutoYesPolling)(worktreeId);
|
|
185
|
+
result.deletedAutoYesPollerIds.push(worktreeId);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// Cleanup schedule manager entries (via encapsulated accessor)
|
|
189
|
+
const scheduleWorktreeIds = (0, schedule_manager_1.getScheduleWorktreeIds)();
|
|
190
|
+
for (const worktreeId of scheduleWorktreeIds) {
|
|
191
|
+
if (!validWorktreeIds.has(worktreeId)) {
|
|
192
|
+
(0, schedule_manager_1.stopScheduleForWorktree)(worktreeId);
|
|
193
|
+
result.deletedScheduleWorktreeIds.push(worktreeId);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const totalDeleted = result.deletedAutoYesStateIds.length +
|
|
197
|
+
result.deletedAutoYesPollerIds.length +
|
|
198
|
+
result.deletedScheduleWorktreeIds.length;
|
|
199
|
+
if (totalDeleted > 0) {
|
|
200
|
+
console.log(`[resource-cleanup] Cleaned up ${totalDeleted} orphaned Map entries:`, {
|
|
201
|
+
autoYesStates: result.deletedAutoYesStateIds.length,
|
|
202
|
+
autoYesPollers: result.deletedAutoYesPollerIds.length,
|
|
203
|
+
schedules: result.deletedScheduleWorktreeIds.length,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
return result;
|
|
207
|
+
}
|
|
208
|
+
// =============================================================================
|
|
209
|
+
// Orchestrator
|
|
210
|
+
// =============================================================================
|
|
211
|
+
/**
|
|
212
|
+
* Run the complete cleanup cycle.
|
|
213
|
+
* Executes both MCP process cleanup and Map entry cleanup.
|
|
214
|
+
*/
|
|
215
|
+
async function runCleanupCycle() {
|
|
216
|
+
try {
|
|
217
|
+
await cleanupOrphanedMcpProcesses();
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
console.warn('[resource-cleanup] MCP cleanup error:', error);
|
|
221
|
+
}
|
|
222
|
+
try {
|
|
223
|
+
cleanupOrphanedMapEntries();
|
|
224
|
+
}
|
|
225
|
+
catch (error) {
|
|
226
|
+
console.warn('[resource-cleanup] Map cleanup error:', error);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Initialize resource cleanup.
|
|
231
|
+
* Starts a periodic timer that runs the cleanup cycle every CLEANUP_INTERVAL_MS.
|
|
232
|
+
* Prevents duplicate timers via globalThis.__resourceCleanupTimerId check.
|
|
233
|
+
*/
|
|
234
|
+
function initResourceCleanup() {
|
|
235
|
+
if (globalThis.__resourceCleanupTimerId !== undefined) {
|
|
236
|
+
console.log('[resource-cleanup] Already initialized, skipping');
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
// Run initial cleanup
|
|
240
|
+
void runCleanupCycle();
|
|
241
|
+
// Start periodic timer
|
|
242
|
+
globalThis.__resourceCleanupTimerId = setInterval(() => {
|
|
243
|
+
void runCleanupCycle();
|
|
244
|
+
}, exports.CLEANUP_INTERVAL_MS);
|
|
245
|
+
console.log('[resource-cleanup] Initialized (interval: 24h)');
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Stop resource cleanup.
|
|
249
|
+
* Clears the periodic timer.
|
|
250
|
+
*/
|
|
251
|
+
function stopResourceCleanup() {
|
|
252
|
+
if (globalThis.__resourceCleanupTimerId !== undefined) {
|
|
253
|
+
clearInterval(globalThis.__resourceCleanupTimerId);
|
|
254
|
+
globalThis.__resourceCleanupTimerId = undefined;
|
|
255
|
+
console.log('[resource-cleanup] Stopped');
|
|
256
|
+
}
|
|
257
|
+
}
|