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.
Files changed (161) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-build-manifest.json +19 -23
  3. package/.next/app-path-routes-manifest.json +1 -1
  4. package/.next/build-manifest.json +5 -5
  5. package/.next/cache/.tsbuildinfo +1 -1
  6. package/.next/cache/config.json +3 -3
  7. package/.next/cache/webpack/client-production/0.pack +0 -0
  8. package/.next/cache/webpack/client-production/1.pack +0 -0
  9. package/.next/cache/webpack/client-production/2.pack +0 -0
  10. package/.next/cache/webpack/client-production/index.pack +0 -0
  11. package/.next/cache/webpack/client-production/index.pack.old +0 -0
  12. package/.next/cache/webpack/edge-server-production/0.pack +0 -0
  13. package/.next/cache/webpack/edge-server-production/index.pack +0 -0
  14. package/.next/cache/webpack/server-production/0.pack +0 -0
  15. package/.next/cache/webpack/server-production/index.pack +0 -0
  16. package/.next/next-server.js.nft.json +1 -1
  17. package/.next/prerender-manifest.json +1 -1
  18. package/.next/react-loadable-manifest.json +69 -55
  19. package/.next/required-server-files.json +1 -1
  20. package/.next/server/app/_not-found/page.js +1 -1
  21. package/.next/server/app/_not-found/page.js.nft.json +1 -1
  22. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  23. package/.next/server/app/api/app/update-check/route.js +1 -1
  24. package/.next/server/app/api/repositories/clone/route.js +1 -1
  25. package/.next/server/app/api/repositories/route.js +8 -8
  26. package/.next/server/app/api/repositories/scan/route.js +1 -1
  27. package/.next/server/app/api/worktrees/[id]/capture/route.js +1 -2
  28. package/.next/server/app/api/worktrees/[id]/capture/route.js.nft.json +1 -1
  29. package/.next/server/app/api/worktrees/[id]/current-output/route.js +1 -1
  30. package/.next/server/app/api/worktrees/[id]/files/[...path]/route.js +1 -1
  31. package/.next/server/app/api/worktrees/[id]/prompt-response/route.js +1 -1
  32. package/.next/server/app/api/worktrees/[id]/respond/route.js +1 -1
  33. package/.next/server/app/api/worktrees/[id]/route.js +1 -1
  34. package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route.js +1 -1
  35. package/.next/server/app/api/worktrees/[id]/schedules/route.js +2 -2
  36. package/.next/server/app/api/worktrees/[id]/search/route.js +1 -1
  37. package/.next/server/app/api/worktrees/[id]/terminal/route.js +1 -1
  38. package/.next/server/app/api/worktrees/[id]/terminal/route.js.nft.json +1 -1
  39. package/.next/server/app/api/worktrees/[id]/tree/[...path]/route.js +1 -1
  40. package/.next/server/app/api/worktrees/[id]/upload/[...path]/route.js +1 -1
  41. package/.next/server/app/api/worktrees/route.js +1 -1
  42. package/.next/server/app/login/page.js +1 -1
  43. package/.next/server/app/login/page.js.nft.json +1 -1
  44. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  45. package/.next/server/app/page.js +3 -3
  46. package/.next/server/app/page.js.nft.json +1 -1
  47. package/.next/server/app/page_client-reference-manifest.js +1 -1
  48. package/.next/server/app/proxy/[...path]/route.js +4 -4
  49. package/.next/server/app/worktrees/[id]/files/[...path]/page.js +1 -1
  50. package/.next/server/app/worktrees/[id]/files/[...path]/page.js.nft.json +1 -1
  51. package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
  52. package/.next/server/app/worktrees/[id]/page.js +6 -6
  53. package/.next/server/app/worktrees/[id]/page.js.nft.json +1 -1
  54. package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
  55. package/.next/server/app/worktrees/[id]/terminal/page.js +2 -4
  56. package/.next/server/app/worktrees/[id]/terminal/page.js.nft.json +1 -1
  57. package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
  58. package/.next/server/app-paths-manifest.json +8 -8
  59. package/.next/server/chunks/{3294.js → 1628.js} +3 -3
  60. package/.next/server/chunks/185.js +36 -0
  61. package/.next/server/chunks/3860.js +1 -1
  62. package/.next/server/chunks/4893.js +2 -2
  63. package/.next/server/chunks/4952.js +1 -1
  64. package/.next/server/chunks/5488.js +6 -6
  65. package/.next/server/chunks/7425.js +34 -31
  66. package/.next/server/chunks/7566.js +2 -2
  67. package/.next/server/chunks/8199.js +1 -0
  68. package/.next/server/chunks/8585.js +1 -1
  69. package/.next/server/chunks/8693.js +1 -1
  70. package/.next/server/middleware-build-manifest.js +1 -1
  71. package/.next/server/middleware-manifest.json +5 -5
  72. package/.next/server/middleware-react-loadable-manifest.js +1 -1
  73. package/.next/server/pages/500.html +1 -1
  74. package/.next/server/server-reference-manifest.json +1 -1
  75. package/.next/static/chunks/12-00c528d46a0a0a1d.js +1 -0
  76. package/.next/static/chunks/{13.feeafc7cc620f8c4.js → 13.b9521543496f4468.js} +1 -1
  77. package/.next/static/chunks/1334.bfedf44ee9fe2761.js +1 -0
  78. package/.next/static/chunks/143.eb6b4671490cd223.js +1 -0
  79. package/.next/static/chunks/{3574.7a94c27e6a496a56.js → 1442.74b5f4de9a4b4e1b.js} +1 -1
  80. package/.next/static/chunks/2083-b5bed0c77cc53281.js +1 -0
  81. package/.next/static/chunks/2725.eb2d236c8030711c.js +1 -0
  82. package/.next/static/chunks/3398-3d40a17387bd554b.js +1 -0
  83. package/.next/static/chunks/3516.3c576047408cae6b.js +1 -0
  84. package/.next/static/chunks/3559.422c6ca760b85750.js +1 -0
  85. package/.next/static/chunks/3956.52c5b9a0071a641d.js +1 -0
  86. package/.next/static/chunks/4012.32b576a4fa621774.js +1 -0
  87. package/.next/static/chunks/4212.e7ba1009bc1da62d.js +131 -0
  88. package/.next/static/chunks/4303.caf91e86105d5e70.js +1 -0
  89. package/.next/static/chunks/4327.4dcda9b6fab6a385.js +82 -0
  90. package/.next/static/chunks/4671.d86d21d0dfdace41.js +1 -0
  91. package/.next/static/chunks/5518.ec88dcb5a27b17fe.js +1 -0
  92. package/.next/static/chunks/6434.08d262283371d333.js +1 -0
  93. package/.next/static/chunks/{656.5e2de0173f5a06bd.js → 656.dc26b973d07d9627.js} +5 -5
  94. package/.next/static/chunks/7119.01777af21b55740c.js +1 -0
  95. package/.next/static/chunks/7293.fb88bb102af4aa04.js +1 -0
  96. package/.next/static/chunks/8913-40625650292eb3d0.js +1 -0
  97. package/.next/static/chunks/8977.fc18b8260cd8bc1f.js +1 -0
  98. package/.next/static/chunks/9552.d959149efd41e84b.js +1 -0
  99. package/.next/static/chunks/app/layout-7198a7a49aa21a97.js +1 -0
  100. package/.next/static/chunks/app/page-7498cf75e69d9227.js +1 -0
  101. package/.next/static/chunks/app/worktrees/[id]/files/[...path]/page-0599f64a8e80d255.js +1 -0
  102. package/.next/static/chunks/app/worktrees/[id]/page-94ad7a1ce1f0c440.js +1 -0
  103. package/.next/static/chunks/app/worktrees/[id]/terminal/page-175b618c047bc992.js +1 -0
  104. package/.next/static/chunks/d3ac728e.daf595a898e9b720.js +1 -0
  105. package/.next/static/chunks/webpack-f7111aab807d73b9.js +1 -0
  106. package/.next/static/css/f7dc01350168df01.css +3 -0
  107. package/.next/trace +5 -5
  108. package/README.md +66 -56
  109. package/dist/server/server.js +5 -0
  110. package/dist/server/src/lib/auto-yes-manager.js +58 -18
  111. package/dist/server/src/lib/claude-session.js +9 -3
  112. package/dist/server/src/lib/cli-session.js +60 -10
  113. package/dist/server/src/lib/cli-tools/codex.js +7 -7
  114. package/dist/server/src/lib/cli-tools/gemini.js +3 -0
  115. package/dist/server/src/lib/cli-tools/opencode-config.js +179 -33
  116. package/dist/server/src/lib/cli-tools/opencode.js +5 -0
  117. package/dist/server/src/lib/cli-tools/vibe-local.js +3 -0
  118. package/dist/server/src/lib/cmate-parser.js +7 -7
  119. package/dist/server/src/lib/db-migrations.js +18 -1
  120. package/dist/server/src/lib/errors.js +153 -0
  121. package/dist/server/src/lib/prompt-answer-sender.js +3 -0
  122. package/dist/server/src/lib/prompt-detector.js +49 -7
  123. package/dist/server/src/lib/resource-cleanup.js +257 -0
  124. package/dist/server/src/lib/schedule-manager.js +269 -83
  125. package/dist/server/src/lib/tmux-capture-cache.js +221 -0
  126. package/dist/server/src/lib/tmux.js +41 -20
  127. package/dist/server/src/types/markdown-editor.js +9 -1
  128. package/package.json +11 -8
  129. package/.next/server/chunks/539.js +0 -35
  130. package/.next/server/chunks/7458.js +0 -1
  131. package/.next/server/chunks/7808.js +0 -1
  132. package/.next/static/chunks/1038-3509435b68c0967e.js +0 -1
  133. package/.next/static/chunks/1098.49268c9fe1b028fa.js +0 -1
  134. package/.next/static/chunks/2335-98a211e00b94c7ac.js +0 -1
  135. package/.next/static/chunks/3559.f073f72c4466ce0e.js +0 -1
  136. package/.next/static/chunks/3843.3fdda732987f7bb8.js +0 -1
  137. package/.next/static/chunks/4212.52c1bb34fc97d0d0.js +0 -131
  138. package/.next/static/chunks/4327.157a4c226d919531.js +0 -60
  139. package/.next/static/chunks/4362.7bd6f0282e49d79b.js +0 -1
  140. package/.next/static/chunks/4721.40615a5f4f32b5fb.js +0 -1
  141. package/.next/static/chunks/5112.17318d1c6b28044b.js +0 -1
  142. package/.next/static/chunks/6406.9653f0d41ab85059.js +0 -1
  143. package/.next/static/chunks/6792.3c01ac4dda4b5c6d.js +0 -1
  144. package/.next/static/chunks/8091-d65d2ab6daed23c6.js +0 -1
  145. package/.next/static/chunks/8125.245a9df052d274fb.js +0 -1
  146. package/.next/static/chunks/8522.1607e96011c66877.js +0 -1
  147. package/.next/static/chunks/8841.dadeb1ece8e46004.js +0 -1
  148. package/.next/static/chunks/8885.f8d9912b40d74811.js +0 -1
  149. package/.next/static/chunks/9178-88850a7c48deea07.js +0 -1
  150. package/.next/static/chunks/9552.b7dfb7903ead934b.js +0 -1
  151. package/.next/static/chunks/app/layout-9110f9a5e41c6bf4.js +0 -1
  152. package/.next/static/chunks/app/page-9e523a8f415bc707.js +0 -1
  153. package/.next/static/chunks/app/worktrees/[id]/files/[...path]/page-4a3c0861367e0391.js +0 -1
  154. package/.next/static/chunks/app/worktrees/[id]/page-8fb4dc30b58a5681.js +0 -1
  155. package/.next/static/chunks/app/worktrees/[id]/terminal/page-5d85a7e508ce36d3.js +0 -1
  156. package/.next/static/chunks/d3ac728e.6c9c508274d4d2d5.js +0 -1
  157. package/.next/static/chunks/webpack-81c97591dd5567ac.js +0 -1
  158. package/.next/static/css/45b3a41370668314.css +0 -3
  159. /package/.next/static/chunks/{30d07d85-393352a92199f695.js → 30d07d85.1dc99a921fc18e34.js} +0 -0
  160. /package/.next/static/{p3hosTZoJ22r35fWwUoLr → dwGMLEU53HOvFOWqiZOT0}/_buildManifest.js +0 -0
  161. /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
+ }