@epiphytic/claudecodeui 1.2.3 → 1.3.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/dist/assets/index-CceDF8mT.css +32 -0
- package/dist/assets/index-Dlv06cpK.js +1245 -0
- package/dist/index.html +2 -2
- package/dist/sw.js +44 -4
- package/package.json +4 -2
- package/public/sw.js +44 -4
- package/server/database/db.js +26 -16
- package/server/database/init.sql +2 -1
- package/server/database.js +861 -0
- package/server/db-indexer.js +401 -0
- package/server/external-session-detector.js +48 -392
- package/server/history-cache.js +354 -0
- package/server/index.js +457 -48
- package/server/logger.js +59 -0
- package/server/maintenance-scheduler.js +172 -0
- package/server/messages-cache.js +485 -0
- package/server/openai-codex.js +110 -102
- package/server/orchestrator/client.js +52 -0
- package/server/process-cache.js +513 -0
- package/server/projects.js +64 -47
- package/server/routes/auth.js +18 -12
- package/server/routes/sessions.js +59 -33
- package/server/session-lock.js +2 -10
- package/server/sessions-cache.js +16 -0
- package/dist/assets/index-BV_fwoPa.css +0 -32
- package/dist/assets/index-CWwPqmRx.js +0 -1245
|
@@ -6,334 +6,35 @@
|
|
|
6
6
|
* running simultaneously on the same project.
|
|
7
7
|
*
|
|
8
8
|
* Detection methods:
|
|
9
|
-
* 1. Process detection via
|
|
10
|
-
* 2. tmux session scanning
|
|
9
|
+
* 1. Process detection via cached process scan (updated every minute)
|
|
10
|
+
* 2. tmux session scanning (from cache)
|
|
11
11
|
* 3. Session lock file detection (.claude/session.lock)
|
|
12
|
+
*
|
|
13
|
+
* Performance:
|
|
14
|
+
* - Uses cached process data from process-cache.js for instant responses
|
|
15
|
+
* - Only lock file checks are done on-demand (fast filesystem operation)
|
|
12
16
|
*/
|
|
13
17
|
|
|
14
|
-
import { spawnSync } from "child_process";
|
|
15
18
|
import fs from "fs";
|
|
16
19
|
import path from "path";
|
|
17
|
-
import
|
|
18
|
-
|
|
19
|
-
// Cache to avoid repeated process scans
|
|
20
|
-
const detectionCache = new Map();
|
|
21
|
-
const CACHE_TTL = 5000; // 5 seconds
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Get the current process ID (for exclusion)
|
|
25
|
-
*/
|
|
26
|
-
const currentPid = process.pid;
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Detect external Claude processes
|
|
30
|
-
* @returns {{ processes: Array<{ pid: number, command: string, cwd: string | null }>, detectionAvailable: boolean, error: string | null }}
|
|
31
|
-
*/
|
|
32
|
-
function detectClaudeProcesses() {
|
|
33
|
-
const processes = [];
|
|
34
|
-
let detectionAvailable = true;
|
|
35
|
-
let error = null;
|
|
36
|
-
|
|
37
|
-
console.log("[ExternalSessionDetector] detectClaudeProcesses() called");
|
|
38
|
-
console.log("[ExternalSessionDetector] Platform:", os.platform());
|
|
39
|
-
console.log("[ExternalSessionDetector] Current PID:", currentPid);
|
|
40
|
-
|
|
41
|
-
if (os.platform() === "win32") {
|
|
42
|
-
// Windows: use wmic or tasklist
|
|
43
|
-
try {
|
|
44
|
-
const result = spawnSync(
|
|
45
|
-
"wmic",
|
|
46
|
-
[
|
|
47
|
-
"process",
|
|
48
|
-
"where",
|
|
49
|
-
"name like '%claude%'",
|
|
50
|
-
"get",
|
|
51
|
-
"processid,commandline",
|
|
52
|
-
],
|
|
53
|
-
{ encoding: "utf8", stdio: "pipe" },
|
|
54
|
-
);
|
|
55
|
-
|
|
56
|
-
if (result.status === 0) {
|
|
57
|
-
const lines = result.stdout.trim().split("\n").slice(1); // Skip header
|
|
58
|
-
for (const line of lines) {
|
|
59
|
-
const match = line.match(/(\d+)\s*$/);
|
|
60
|
-
if (match) {
|
|
61
|
-
const pid = parseInt(match[1], 10);
|
|
62
|
-
if (pid !== currentPid) {
|
|
63
|
-
processes.push({ pid, command: line.trim(), cwd: null });
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
} else if (result.error) {
|
|
68
|
-
detectionAvailable = false;
|
|
69
|
-
error = `wmic not available: ${result.error.message}`;
|
|
70
|
-
}
|
|
71
|
-
} catch (e) {
|
|
72
|
-
detectionAvailable = false;
|
|
73
|
-
error = `Windows process detection failed: ${e.message}`;
|
|
74
|
-
}
|
|
75
|
-
} else {
|
|
76
|
-
// Unix: use pgrep and ps
|
|
77
|
-
try {
|
|
78
|
-
// First, check if pgrep is available
|
|
79
|
-
const pgrepCheck = spawnSync("which", ["pgrep"], {
|
|
80
|
-
encoding: "utf8",
|
|
81
|
-
stdio: "pipe",
|
|
82
|
-
});
|
|
83
|
-
console.log(
|
|
84
|
-
"[ExternalSessionDetector] pgrep available:",
|
|
85
|
-
pgrepCheck.status === 0,
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
if (pgrepCheck.status !== 0) {
|
|
89
|
-
// pgrep not available, try ps aux as fallback
|
|
90
|
-
console.log("[ExternalSessionDetector] Using ps aux fallback");
|
|
91
|
-
try {
|
|
92
|
-
const psResult = spawnSync("ps", ["aux"], {
|
|
93
|
-
encoding: "utf8",
|
|
94
|
-
stdio: "pipe",
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
if (psResult.status === 0) {
|
|
98
|
-
const lines = psResult.stdout.split("\n");
|
|
99
|
-
const claudeLines = lines.filter(
|
|
100
|
-
(line) =>
|
|
101
|
-
line.toLowerCase().includes("claude") &&
|
|
102
|
-
!line.includes(String(currentPid)),
|
|
103
|
-
);
|
|
104
|
-
console.log(
|
|
105
|
-
"[ExternalSessionDetector] ps aux found",
|
|
106
|
-
claudeLines.length,
|
|
107
|
-
'lines containing "claude"',
|
|
108
|
-
);
|
|
109
|
-
|
|
110
|
-
for (const line of claudeLines) {
|
|
111
|
-
const parts = line.trim().split(/\s+/);
|
|
112
|
-
if (parts.length >= 2) {
|
|
113
|
-
const pid = parseInt(parts[1], 10);
|
|
114
|
-
if (!isNaN(pid) && pid !== currentPid) {
|
|
115
|
-
const command = parts.slice(10).join(" ");
|
|
116
|
-
const isExternal = isExternalClaudeProcess(command);
|
|
117
|
-
console.log(
|
|
118
|
-
`[ExternalSessionDetector] PID ${pid}: "${command.slice(0, 60)}..." isExternal=${isExternal}`,
|
|
119
|
-
);
|
|
120
|
-
if (isExternal) {
|
|
121
|
-
processes.push({ pid, command, cwd: null });
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
} else {
|
|
127
|
-
detectionAvailable = false;
|
|
128
|
-
error = "Neither pgrep nor ps aux available";
|
|
129
|
-
console.log("[ExternalSessionDetector] ps aux failed");
|
|
130
|
-
}
|
|
131
|
-
} catch (e) {
|
|
132
|
-
detectionAvailable = false;
|
|
133
|
-
error = `Process detection failed: ${e.message}`;
|
|
134
|
-
console.log("[ExternalSessionDetector] ps aux exception:", e.message);
|
|
135
|
-
}
|
|
136
|
-
} else {
|
|
137
|
-
// pgrep is available, use it
|
|
138
|
-
const pgrepResult = spawnSync("pgrep", ["-f", "claude"], {
|
|
139
|
-
encoding: "utf8",
|
|
140
|
-
stdio: "pipe",
|
|
141
|
-
});
|
|
142
|
-
console.log(
|
|
143
|
-
"[ExternalSessionDetector] pgrep status:",
|
|
144
|
-
pgrepResult.status,
|
|
145
|
-
);
|
|
146
|
-
|
|
147
|
-
if (pgrepResult.status === 0) {
|
|
148
|
-
const pids = pgrepResult.stdout.trim().split("\n").filter(Boolean);
|
|
149
|
-
console.log("[ExternalSessionDetector] pgrep found PIDs:", pids);
|
|
150
|
-
|
|
151
|
-
for (const pidStr of pids) {
|
|
152
|
-
const pid = parseInt(pidStr, 10);
|
|
153
|
-
|
|
154
|
-
// Skip our own process and child processes
|
|
155
|
-
if (pid === currentPid) {
|
|
156
|
-
console.log(
|
|
157
|
-
`[ExternalSessionDetector] Skipping our own PID ${pid}`,
|
|
158
|
-
);
|
|
159
|
-
continue;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Get command details
|
|
163
|
-
const psResult = spawnSync(
|
|
164
|
-
"ps",
|
|
165
|
-
["-p", String(pid), "-o", "args="],
|
|
166
|
-
{
|
|
167
|
-
encoding: "utf8",
|
|
168
|
-
stdio: "pipe",
|
|
169
|
-
},
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
if (psResult.status === 0) {
|
|
173
|
-
const command = psResult.stdout.trim();
|
|
174
|
-
const isExternal = isExternalClaudeProcess(command);
|
|
175
|
-
console.log(
|
|
176
|
-
`[ExternalSessionDetector] PID ${pid}: "${command.slice(0, 80)}..." isExternal=${isExternal}`,
|
|
177
|
-
);
|
|
178
|
-
|
|
179
|
-
// Filter out our own subprocesses (claude-sdk spawned by this app)
|
|
180
|
-
// and only include standalone claude CLI invocations
|
|
181
|
-
if (isExternal) {
|
|
182
|
-
// Try to get working directory via lsof
|
|
183
|
-
let cwd = null;
|
|
184
|
-
try {
|
|
185
|
-
const lsofResult = spawnSync(
|
|
186
|
-
"lsof",
|
|
187
|
-
["-p", String(pid), "-Fn"],
|
|
188
|
-
{
|
|
189
|
-
encoding: "utf8",
|
|
190
|
-
stdio: "pipe",
|
|
191
|
-
},
|
|
192
|
-
);
|
|
193
|
-
if (lsofResult.status === 0) {
|
|
194
|
-
const cwdMatch = lsofResult.stdout.match(/n(\/[^\n]+)/);
|
|
195
|
-
if (cwdMatch) {
|
|
196
|
-
cwd = cwdMatch[1];
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
} catch {
|
|
200
|
-
// lsof may not be available - not critical
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
processes.push({ pid, command, cwd });
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
} else {
|
|
208
|
-
console.log(
|
|
209
|
-
"[ExternalSessionDetector] pgrep found no claude processes (status:",
|
|
210
|
-
pgrepResult.status,
|
|
211
|
-
")",
|
|
212
|
-
);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
} catch (e) {
|
|
216
|
-
detectionAvailable = false;
|
|
217
|
-
error = `Unix process detection failed: ${e.message}`;
|
|
218
|
-
console.log(
|
|
219
|
-
"[ExternalSessionDetector] Unix detection exception:",
|
|
220
|
-
e.message,
|
|
221
|
-
);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
return { processes, detectionAvailable, error };
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Check if a command is an external Claude process (not spawned by us)
|
|
230
|
-
* @param {string} command - The process command line
|
|
231
|
-
* @returns {boolean}
|
|
232
|
-
*/
|
|
233
|
-
function isExternalClaudeProcess(command) {
|
|
234
|
-
// Skip node processes (SDK internals)
|
|
235
|
-
if (command.startsWith("node ")) {
|
|
236
|
-
console.log("[isExternalClaudeProcess] Rejected: starts with 'node '");
|
|
237
|
-
return false;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// Skip our own server
|
|
241
|
-
if (command.includes("claudecodeui/server")) {
|
|
242
|
-
console.log(
|
|
243
|
-
"[isExternalClaudeProcess] Rejected: contains 'claudecodeui/server'",
|
|
244
|
-
);
|
|
245
|
-
return false;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// Look for actual claude CLI invocations
|
|
249
|
-
const isExternal =
|
|
250
|
-
command.includes("claude ") ||
|
|
251
|
-
command.includes("claude-code") ||
|
|
252
|
-
command.match(/\/claude\s/) ||
|
|
253
|
-
command.endsWith("/claude");
|
|
254
|
-
|
|
255
|
-
console.log(
|
|
256
|
-
`[isExternalClaudeProcess] "${command.slice(0, 60)}..." => ${isExternal}`,
|
|
257
|
-
);
|
|
258
|
-
return isExternal;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Detect tmux sessions that might be running Claude
|
|
263
|
-
* @returns {Array<{ sessionName: string, windows: number, attached: boolean }>}
|
|
264
|
-
*/
|
|
265
|
-
function detectClaudeTmuxSessions() {
|
|
266
|
-
const sessions = [];
|
|
20
|
+
import { createLogger } from "./logger.js";
|
|
21
|
+
import { getCachedProcessData, getCacheAge } from "./process-cache.js";
|
|
267
22
|
|
|
268
|
-
|
|
269
|
-
const result = spawnSync(
|
|
270
|
-
"tmux",
|
|
271
|
-
[
|
|
272
|
-
"list-sessions",
|
|
273
|
-
"-F",
|
|
274
|
-
"#{session_name}:#{session_windows}:#{session_attached}",
|
|
275
|
-
],
|
|
276
|
-
{ encoding: "utf8", stdio: "pipe" },
|
|
277
|
-
);
|
|
278
|
-
|
|
279
|
-
if (result.status === 0) {
|
|
280
|
-
const lines = result.stdout.trim().split("\n").filter(Boolean);
|
|
281
|
-
|
|
282
|
-
for (const line of lines) {
|
|
283
|
-
const [sessionName, windows, attached] = line.split(":");
|
|
284
|
-
|
|
285
|
-
// Skip our own sessions
|
|
286
|
-
if (sessionName.startsWith("claudeui-")) continue;
|
|
287
|
-
|
|
288
|
-
// Check if session might be running Claude
|
|
289
|
-
// We can peek at the pane content or just check window titles
|
|
290
|
-
if (mightBeClaudeSession(sessionName)) {
|
|
291
|
-
sessions.push({
|
|
292
|
-
sessionName,
|
|
293
|
-
windows: parseInt(windows, 10),
|
|
294
|
-
attached: attached === "1",
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
} catch {
|
|
300
|
-
// tmux not available or no server running
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
return sessions;
|
|
304
|
-
}
|
|
23
|
+
const log = createLogger("external-session-detector");
|
|
305
24
|
|
|
306
25
|
/**
|
|
307
|
-
*
|
|
308
|
-
* @param {
|
|
26
|
+
* Check if a process exists
|
|
27
|
+
* @param {number} pid - Process ID
|
|
309
28
|
* @returns {boolean}
|
|
310
29
|
*/
|
|
311
|
-
function
|
|
312
|
-
// Check common patterns
|
|
313
|
-
const claudePatterns = ["claude", "ai", "chat", "code"];
|
|
314
|
-
const lowerName = sessionName.toLowerCase();
|
|
315
|
-
|
|
316
|
-
for (const pattern of claudePatterns) {
|
|
317
|
-
if (lowerName.includes(pattern)) return true;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
// Try to peek at the session's current command
|
|
30
|
+
function processExists(pid) {
|
|
321
31
|
try {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
{ encoding: "utf8", stdio: "pipe" },
|
|
326
|
-
);
|
|
327
|
-
|
|
328
|
-
if (result.status === 0) {
|
|
329
|
-
const currentCommand = result.stdout.trim().toLowerCase();
|
|
330
|
-
if (currentCommand.includes("claude")) return true;
|
|
331
|
-
}
|
|
32
|
+
// Sending signal 0 checks if process exists without actually sending a signal
|
|
33
|
+
process.kill(pid, 0);
|
|
34
|
+
return true;
|
|
332
35
|
} catch {
|
|
333
|
-
|
|
36
|
+
return false;
|
|
334
37
|
}
|
|
335
|
-
|
|
336
|
-
return false;
|
|
337
38
|
}
|
|
338
39
|
|
|
339
40
|
/**
|
|
@@ -355,6 +56,7 @@ function checkSessionLockFile(projectPath) {
|
|
|
355
56
|
const isAlive = processExists(lockData.pid);
|
|
356
57
|
if (!isAlive) {
|
|
357
58
|
// Stale lock file, clean it up
|
|
59
|
+
log.debug({ lockFile, pid: lockData.pid }, "Removing stale lock");
|
|
358
60
|
try {
|
|
359
61
|
fs.unlinkSync(lockFile);
|
|
360
62
|
} catch {
|
|
@@ -377,83 +79,43 @@ function checkSessionLockFile(projectPath) {
|
|
|
377
79
|
return { exists: false, lockFile: null, content: null };
|
|
378
80
|
}
|
|
379
81
|
|
|
380
|
-
/**
|
|
381
|
-
* Check if a process exists
|
|
382
|
-
* @param {number} pid - Process ID
|
|
383
|
-
* @returns {boolean}
|
|
384
|
-
*/
|
|
385
|
-
function processExists(pid) {
|
|
386
|
-
try {
|
|
387
|
-
// Sending signal 0 checks if process exists without actually sending a signal
|
|
388
|
-
process.kill(pid, 0);
|
|
389
|
-
return true;
|
|
390
|
-
} catch {
|
|
391
|
-
return false;
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
|
|
395
82
|
/**
|
|
396
83
|
* Main detection function - detect all external Claude sessions
|
|
84
|
+
* Uses cached process data for instant response.
|
|
85
|
+
*
|
|
397
86
|
* @param {string} projectPath - The project directory to check
|
|
398
|
-
* @returns {{ hasExternalSession: boolean, processes: Array, tmuxSessions: Array, lockFile: object, detectionAvailable: boolean, detectionError: string | null }}
|
|
87
|
+
* @returns {{ hasExternalSession: boolean, processes: Array, tmuxSessions: Array, lockFile: object, detectionAvailable: boolean, detectionError: string | null, cacheAge: number | null }}
|
|
399
88
|
*/
|
|
400
89
|
function detectExternalClaude(projectPath) {
|
|
401
|
-
|
|
90
|
+
log.debug({ projectPath }, "Detection requested");
|
|
402
91
|
|
|
403
|
-
//
|
|
404
|
-
const
|
|
405
|
-
const
|
|
406
|
-
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
|
|
407
|
-
console.log("[detectExternalClaude] Returning cached result");
|
|
408
|
-
return cached.result;
|
|
409
|
-
}
|
|
410
|
-
console.log("[detectExternalClaude] Cache miss, performing fresh detection");
|
|
92
|
+
// Get cached process data (instant - no process scanning)
|
|
93
|
+
const cachedData = getCachedProcessData();
|
|
94
|
+
const cacheAge = getCacheAge();
|
|
411
95
|
|
|
412
96
|
const result = {
|
|
413
97
|
hasExternalSession: false,
|
|
414
|
-
processes: [],
|
|
415
|
-
tmuxSessions: [],
|
|
98
|
+
processes: [...cachedData.processes],
|
|
99
|
+
tmuxSessions: [...cachedData.tmuxSessions],
|
|
416
100
|
lockFile: { exists: false, lockFile: null, content: null },
|
|
417
|
-
detectionAvailable:
|
|
418
|
-
detectionError:
|
|
101
|
+
detectionAvailable: cachedData.detectionAvailable,
|
|
102
|
+
detectionError: cachedData.detectionError,
|
|
103
|
+
cacheAge,
|
|
419
104
|
};
|
|
420
105
|
|
|
421
|
-
//
|
|
422
|
-
|
|
423
|
-
result.processes = processDetection.processes;
|
|
424
|
-
result.detectionAvailable = processDetection.detectionAvailable;
|
|
425
|
-
result.detectionError = processDetection.error;
|
|
426
|
-
console.log(
|
|
427
|
-
"[detectExternalClaude] Process detection result:",
|
|
428
|
-
processDetection.processes.length,
|
|
429
|
-
"processes, available:",
|
|
430
|
-
processDetection.detectionAvailable,
|
|
431
|
-
"error:",
|
|
432
|
-
processDetection.error,
|
|
433
|
-
);
|
|
434
|
-
|
|
435
|
-
if (projectPath) {
|
|
436
|
-
// Filter to processes in this project
|
|
106
|
+
// Filter processes to project if path provided
|
|
107
|
+
if (projectPath && result.processes.length > 0) {
|
|
437
108
|
const beforeFilter = result.processes.length;
|
|
438
109
|
result.processes = result.processes.filter(
|
|
439
110
|
(p) => !p.cwd || p.cwd.startsWith(projectPath),
|
|
440
111
|
);
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
"->",
|
|
445
|
-
result.processes.length,
|
|
112
|
+
log.debug(
|
|
113
|
+
{ projectPath, before: beforeFilter, after: result.processes.length },
|
|
114
|
+
"Filtered processes for project",
|
|
446
115
|
);
|
|
447
116
|
}
|
|
448
117
|
|
|
449
|
-
//
|
|
450
|
-
result.tmuxSessions = detectClaudeTmuxSessions();
|
|
451
|
-
console.log(
|
|
452
|
-
"[detectExternalClaude] tmux sessions found:",
|
|
453
|
-
result.tmuxSessions.length,
|
|
454
|
-
);
|
|
455
|
-
|
|
456
|
-
// Check lock file
|
|
118
|
+
// Check lock file (fast filesystem operation)
|
|
457
119
|
if (projectPath) {
|
|
458
120
|
result.lockFile = checkSessionLockFile(projectPath);
|
|
459
121
|
}
|
|
@@ -464,22 +126,20 @@ function detectExternalClaude(projectPath) {
|
|
|
464
126
|
result.tmuxSessions.length > 0 ||
|
|
465
127
|
result.lockFile.exists;
|
|
466
128
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
129
|
+
log.debug(
|
|
130
|
+
{
|
|
131
|
+
hasExternalSession: result.hasExternalSession,
|
|
132
|
+
processCount: result.processes.length,
|
|
133
|
+
tmuxCount: result.tmuxSessions.length,
|
|
134
|
+
hasLockFile: result.lockFile.exists,
|
|
135
|
+
cacheAge,
|
|
136
|
+
},
|
|
137
|
+
"Detection complete",
|
|
138
|
+
);
|
|
472
139
|
|
|
473
140
|
return result;
|
|
474
141
|
}
|
|
475
142
|
|
|
476
|
-
/**
|
|
477
|
-
* Clear the detection cache
|
|
478
|
-
*/
|
|
479
|
-
function clearCache() {
|
|
480
|
-
detectionCache.clear();
|
|
481
|
-
}
|
|
482
|
-
|
|
483
143
|
/**
|
|
484
144
|
* Create a session lock file for this application
|
|
485
145
|
* @param {string} projectPath - The project directory
|
|
@@ -504,12 +164,10 @@ function createSessionLock(projectPath, sessionId) {
|
|
|
504
164
|
};
|
|
505
165
|
|
|
506
166
|
fs.writeFileSync(lockFile, JSON.stringify(lockData, null, 2));
|
|
167
|
+
log.debug({ lockFile, sessionId }, "Created session lock");
|
|
507
168
|
return true;
|
|
508
169
|
} catch (err) {
|
|
509
|
-
|
|
510
|
-
"[ExternalSessionDetector] Failed to create lock file:",
|
|
511
|
-
err.message,
|
|
512
|
-
);
|
|
170
|
+
log.error({ error: err.message, lockFile }, "Failed to create lock file");
|
|
513
171
|
return false;
|
|
514
172
|
}
|
|
515
173
|
}
|
|
@@ -525,6 +183,7 @@ function removeSessionLock(projectPath) {
|
|
|
525
183
|
try {
|
|
526
184
|
if (fs.existsSync(lockFile)) {
|
|
527
185
|
fs.unlinkSync(lockFile);
|
|
186
|
+
log.debug({ lockFile }, "Removed session lock");
|
|
528
187
|
}
|
|
529
188
|
return true;
|
|
530
189
|
} catch {
|
|
@@ -534,10 +193,7 @@ function removeSessionLock(projectPath) {
|
|
|
534
193
|
|
|
535
194
|
export {
|
|
536
195
|
detectExternalClaude,
|
|
537
|
-
detectClaudeProcesses,
|
|
538
|
-
detectClaudeTmuxSessions,
|
|
539
196
|
checkSessionLockFile,
|
|
540
197
|
createSessionLock,
|
|
541
198
|
removeSessionLock,
|
|
542
|
-
clearCache,
|
|
543
199
|
};
|