@epiphytic/claudecodeui 1.1.0 → 1.2.1
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-BGneYLVE.css +32 -0
- package/dist/assets/{index-D0xTNXrF.js → index-sqmQ9jF8.js} +210 -212
- package/dist/index.html +2 -2
- package/dist/sw.js +25 -1
- package/package.json +2 -1
- package/public/api-docs.html +879 -0
- package/public/clear-cache.html +85 -0
- package/public/convert-icons.md +53 -0
- package/public/favicon.png +0 -0
- package/public/favicon.svg +9 -0
- package/public/generate-icons.js +49 -0
- package/public/icons/claude-ai-icon.svg +1 -0
- package/public/icons/codex-white.svg +3 -0
- package/public/icons/codex.svg +3 -0
- package/public/icons/cursor-white.svg +12 -0
- package/public/icons/cursor.svg +1 -0
- package/public/icons/generate-icons.md +19 -0
- package/public/icons/icon-128x128.png +0 -0
- package/public/icons/icon-128x128.svg +12 -0
- package/public/icons/icon-144x144.png +0 -0
- package/public/icons/icon-144x144.svg +12 -0
- package/public/icons/icon-152x152.png +0 -0
- package/public/icons/icon-152x152.svg +12 -0
- package/public/icons/icon-192x192.png +0 -0
- package/public/icons/icon-192x192.svg +12 -0
- package/public/icons/icon-384x384.png +0 -0
- package/public/icons/icon-384x384.svg +12 -0
- package/public/icons/icon-512x512.png +0 -0
- package/public/icons/icon-512x512.svg +12 -0
- package/public/icons/icon-72x72.png +0 -0
- package/public/icons/icon-72x72.svg +12 -0
- package/public/icons/icon-96x96.png +0 -0
- package/public/icons/icon-96x96.svg +12 -0
- package/public/icons/icon-template.svg +12 -0
- package/public/logo-128.png +0 -0
- package/public/logo-256.png +0 -0
- package/public/logo-32.png +0 -0
- package/public/logo-512.png +0 -0
- package/public/logo-64.png +0 -0
- package/public/logo.svg +17 -0
- package/public/manifest.json +61 -0
- package/public/screenshots/cli-selection.png +0 -0
- package/public/screenshots/desktop-main.png +0 -0
- package/public/screenshots/mobile-chat.png +0 -0
- package/public/screenshots/tools-modal.png +0 -0
- package/public/sw.js +131 -0
- package/server/database/db.js +98 -0
- package/server/database/init.sql +13 -1
- package/server/external-session-detector.js +188 -48
- package/server/index.js +210 -7
- package/server/orchestrator/client.js +361 -16
- package/server/orchestrator/index.js +83 -8
- package/server/orchestrator/protocol.js +67 -0
- package/server/projects.js +2 -1
- package/dist/assets/index-DKDK7xNY.css +0 -32
package/server/database/db.js
CHANGED
|
@@ -120,6 +120,27 @@ const runMigrations = () => {
|
|
|
120
120
|
`);
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
// Check if orchestrator_tokens table exists
|
|
124
|
+
const orchestratorTokensTable = db
|
|
125
|
+
.prepare(
|
|
126
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name='orchestrator_tokens'",
|
|
127
|
+
)
|
|
128
|
+
.all();
|
|
129
|
+
if (orchestratorTokensTable.length === 0) {
|
|
130
|
+
console.log("Running migration: Creating orchestrator_tokens table");
|
|
131
|
+
db.exec(`
|
|
132
|
+
CREATE TABLE IF NOT EXISTS orchestrator_tokens (
|
|
133
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
134
|
+
host TEXT NOT NULL UNIQUE,
|
|
135
|
+
token TEXT NOT NULL,
|
|
136
|
+
client_id TEXT,
|
|
137
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
138
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
139
|
+
);
|
|
140
|
+
CREATE INDEX IF NOT EXISTS idx_orchestrator_tokens_host ON orchestrator_tokens(host);
|
|
141
|
+
`);
|
|
142
|
+
}
|
|
143
|
+
|
|
123
144
|
console.log("Database migrations completed successfully");
|
|
124
145
|
} catch (error) {
|
|
125
146
|
console.error("Error running migrations:", error.message);
|
|
@@ -642,6 +663,82 @@ const tmuxSessionsDb = {
|
|
|
642
663
|
},
|
|
643
664
|
};
|
|
644
665
|
|
|
666
|
+
// Orchestrator tokens database operations (for storing tokens received during pending mode)
|
|
667
|
+
const orchestratorTokensDb = {
|
|
668
|
+
/**
|
|
669
|
+
* Get stored orchestrator token for a specific host
|
|
670
|
+
* @param {string} host - The orchestrator host (e.g., "duratii.example.com")
|
|
671
|
+
* @returns {{token: string, client_id: string} | null}
|
|
672
|
+
*/
|
|
673
|
+
getToken: (host) => {
|
|
674
|
+
try {
|
|
675
|
+
const row = db
|
|
676
|
+
.prepare(
|
|
677
|
+
"SELECT token, client_id FROM orchestrator_tokens WHERE host = ?",
|
|
678
|
+
)
|
|
679
|
+
.get(host);
|
|
680
|
+
return row || null;
|
|
681
|
+
} catch (err) {
|
|
682
|
+
throw err;
|
|
683
|
+
}
|
|
684
|
+
},
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* Save or update orchestrator token for a host
|
|
688
|
+
* @param {string} host - The orchestrator host
|
|
689
|
+
* @param {string} token - The full token string
|
|
690
|
+
* @param {string} clientId - The client ID from orchestrator
|
|
691
|
+
*/
|
|
692
|
+
saveToken: (host, token, clientId) => {
|
|
693
|
+
try {
|
|
694
|
+
const stmt = db.prepare(`
|
|
695
|
+
INSERT INTO orchestrator_tokens (host, token, client_id, updated_at)
|
|
696
|
+
VALUES (?, ?, ?, CURRENT_TIMESTAMP)
|
|
697
|
+
ON CONFLICT(host) DO UPDATE SET
|
|
698
|
+
token = excluded.token,
|
|
699
|
+
client_id = excluded.client_id,
|
|
700
|
+
updated_at = CURRENT_TIMESTAMP
|
|
701
|
+
`);
|
|
702
|
+
stmt.run(host, token, clientId);
|
|
703
|
+
return true;
|
|
704
|
+
} catch (err) {
|
|
705
|
+
throw err;
|
|
706
|
+
}
|
|
707
|
+
},
|
|
708
|
+
|
|
709
|
+
/**
|
|
710
|
+
* Delete orchestrator token for a host
|
|
711
|
+
* @param {string} host - The orchestrator host
|
|
712
|
+
* @returns {boolean} True if a token was deleted
|
|
713
|
+
*/
|
|
714
|
+
deleteToken: (host) => {
|
|
715
|
+
try {
|
|
716
|
+
const stmt = db.prepare("DELETE FROM orchestrator_tokens WHERE host = ?");
|
|
717
|
+
const result = stmt.run(host);
|
|
718
|
+
return result.changes > 0;
|
|
719
|
+
} catch (err) {
|
|
720
|
+
throw err;
|
|
721
|
+
}
|
|
722
|
+
},
|
|
723
|
+
|
|
724
|
+
/**
|
|
725
|
+
* Get all stored orchestrator tokens
|
|
726
|
+
* @returns {Array<{id: number, host: string, client_id: string, created_at: string, updated_at: string}>}
|
|
727
|
+
*/
|
|
728
|
+
getAllTokens: () => {
|
|
729
|
+
try {
|
|
730
|
+
const rows = db
|
|
731
|
+
.prepare(
|
|
732
|
+
"SELECT id, host, client_id, created_at, updated_at FROM orchestrator_tokens ORDER BY updated_at DESC",
|
|
733
|
+
)
|
|
734
|
+
.all();
|
|
735
|
+
return rows;
|
|
736
|
+
} catch (err) {
|
|
737
|
+
throw err;
|
|
738
|
+
}
|
|
739
|
+
},
|
|
740
|
+
};
|
|
741
|
+
|
|
645
742
|
// Backward compatibility - keep old names pointing to new system
|
|
646
743
|
const githubTokensDb = {
|
|
647
744
|
createGithubToken: (userId, tokenName, githubToken, description = null) => {
|
|
@@ -675,4 +772,5 @@ export {
|
|
|
675
772
|
credentialsDb,
|
|
676
773
|
githubTokensDb, // Backward compatibility
|
|
677
774
|
tmuxSessionsDb,
|
|
775
|
+
orchestratorTokensDb,
|
|
678
776
|
};
|
package/server/database/init.sql
CHANGED
|
@@ -65,4 +65,16 @@ CREATE TABLE IF NOT EXISTS tmux_sessions (
|
|
|
65
65
|
);
|
|
66
66
|
|
|
67
67
|
CREATE INDEX IF NOT EXISTS idx_tmux_sessions_project ON tmux_sessions(project_path);
|
|
68
|
-
CREATE INDEX IF NOT EXISTS idx_tmux_sessions_name ON tmux_sessions(tmux_session_name);
|
|
68
|
+
CREATE INDEX IF NOT EXISTS idx_tmux_sessions_name ON tmux_sessions(tmux_session_name);
|
|
69
|
+
|
|
70
|
+
-- Orchestrator tokens table for storing tokens received from orchestrator during pending mode
|
|
71
|
+
CREATE TABLE IF NOT EXISTS orchestrator_tokens (
|
|
72
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
73
|
+
host TEXT NOT NULL UNIQUE, -- e.g., "duratii.example.com"
|
|
74
|
+
token TEXT NOT NULL, -- Full token string (ao_xxx_yyy)
|
|
75
|
+
client_id TEXT, -- Client ID assigned by orchestrator
|
|
76
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
77
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
CREATE INDEX IF NOT EXISTS idx_orchestrator_tokens_host ON orchestrator_tokens(host);
|
|
@@ -27,10 +27,16 @@ const currentPid = process.pid;
|
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Detect external Claude processes
|
|
30
|
-
* @returns {Array<{ pid: number, command: string, cwd: string | null }
|
|
30
|
+
* @returns {{ processes: Array<{ pid: number, command: string, cwd: string | null }>, detectionAvailable: boolean, error: string | null }}
|
|
31
31
|
*/
|
|
32
32
|
function detectClaudeProcesses() {
|
|
33
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);
|
|
34
40
|
|
|
35
41
|
if (os.platform() === "win32") {
|
|
36
42
|
// Windows: use wmic or tasklist
|
|
@@ -58,72 +64,165 @@ function detectClaudeProcesses() {
|
|
|
58
64
|
}
|
|
59
65
|
}
|
|
60
66
|
}
|
|
67
|
+
} else if (result.error) {
|
|
68
|
+
detectionAvailable = false;
|
|
69
|
+
error = `wmic not available: ${result.error.message}`;
|
|
61
70
|
}
|
|
62
|
-
} catch {
|
|
63
|
-
|
|
71
|
+
} catch (e) {
|
|
72
|
+
detectionAvailable = false;
|
|
73
|
+
error = `Windows process detection failed: ${e.message}`;
|
|
64
74
|
}
|
|
65
75
|
} else {
|
|
66
76
|
// Unix: use pgrep and ps
|
|
67
77
|
try {
|
|
68
|
-
// First,
|
|
69
|
-
const
|
|
78
|
+
// First, check if pgrep is available
|
|
79
|
+
const pgrepCheck = spawnSync("which", ["pgrep"], {
|
|
70
80
|
encoding: "utf8",
|
|
71
81
|
stdio: "pipe",
|
|
72
82
|
});
|
|
83
|
+
console.log(
|
|
84
|
+
"[ExternalSessionDetector] pgrep available:",
|
|
85
|
+
pgrepCheck.status === 0,
|
|
86
|
+
);
|
|
73
87
|
|
|
74
|
-
if (
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
// Skip our own process and child processes
|
|
81
|
-
if (pid === currentPid) continue;
|
|
82
|
-
|
|
83
|
-
// Get command details
|
|
84
|
-
const psResult = spawnSync("ps", ["-p", String(pid), "-o", "args="], {
|
|
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"], {
|
|
85
93
|
encoding: "utf8",
|
|
86
94
|
stdio: "pipe",
|
|
87
95
|
});
|
|
88
96
|
|
|
89
97
|
if (psResult.status === 0) {
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
);
|
|
106
|
-
if (
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
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 });
|
|
110
122
|
}
|
|
111
123
|
}
|
|
112
|
-
} catch {
|
|
113
|
-
// lsof may not be available
|
|
114
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
|
+
}
|
|
115
202
|
|
|
116
|
-
|
|
203
|
+
processes.push({ pid, command, cwd });
|
|
204
|
+
}
|
|
117
205
|
}
|
|
118
206
|
}
|
|
207
|
+
} else {
|
|
208
|
+
console.log(
|
|
209
|
+
"[ExternalSessionDetector] pgrep found no claude processes (status:",
|
|
210
|
+
pgrepResult.status,
|
|
211
|
+
")",
|
|
212
|
+
);
|
|
119
213
|
}
|
|
120
214
|
}
|
|
121
|
-
} catch {
|
|
122
|
-
|
|
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
|
+
);
|
|
123
222
|
}
|
|
124
223
|
}
|
|
125
224
|
|
|
126
|
-
return processes;
|
|
225
|
+
return { processes, detectionAvailable, error };
|
|
127
226
|
}
|
|
128
227
|
|
|
129
228
|
/**
|
|
@@ -133,18 +232,30 @@ function detectClaudeProcesses() {
|
|
|
133
232
|
*/
|
|
134
233
|
function isExternalClaudeProcess(command) {
|
|
135
234
|
// Skip node processes (SDK internals)
|
|
136
|
-
if (command.startsWith("node "))
|
|
235
|
+
if (command.startsWith("node ")) {
|
|
236
|
+
console.log("[isExternalClaudeProcess] Rejected: starts with 'node '");
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
137
239
|
|
|
138
240
|
// Skip our own server
|
|
139
|
-
if (command.includes("claudecodeui/server"))
|
|
241
|
+
if (command.includes("claudecodeui/server")) {
|
|
242
|
+
console.log(
|
|
243
|
+
"[isExternalClaudeProcess] Rejected: contains 'claudecodeui/server'",
|
|
244
|
+
);
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
140
247
|
|
|
141
248
|
// Look for actual claude CLI invocations
|
|
142
|
-
|
|
249
|
+
const isExternal =
|
|
143
250
|
command.includes("claude ") ||
|
|
144
251
|
command.includes("claude-code") ||
|
|
145
252
|
command.match(/\/claude\s/) ||
|
|
146
|
-
command.endsWith("/claude")
|
|
253
|
+
command.endsWith("/claude");
|
|
254
|
+
|
|
255
|
+
console.log(
|
|
256
|
+
`[isExternalClaudeProcess] "${command.slice(0, 60)}..." => ${isExternal}`,
|
|
147
257
|
);
|
|
258
|
+
return isExternal;
|
|
148
259
|
}
|
|
149
260
|
|
|
150
261
|
/**
|
|
@@ -284,34 +395,63 @@ function processExists(pid) {
|
|
|
284
395
|
/**
|
|
285
396
|
* Main detection function - detect all external Claude sessions
|
|
286
397
|
* @param {string} projectPath - The project directory to check
|
|
287
|
-
* @returns {{ hasExternalSession: boolean, processes: Array, tmuxSessions: Array, lockFile: object }}
|
|
398
|
+
* @returns {{ hasExternalSession: boolean, processes: Array, tmuxSessions: Array, lockFile: object, detectionAvailable: boolean, detectionError: string | null }}
|
|
288
399
|
*/
|
|
289
400
|
function detectExternalClaude(projectPath) {
|
|
401
|
+
console.log("[detectExternalClaude] Called with projectPath:", projectPath);
|
|
402
|
+
|
|
290
403
|
// Check cache
|
|
291
404
|
const cacheKey = projectPath || "__global__";
|
|
292
405
|
const cached = detectionCache.get(cacheKey);
|
|
293
406
|
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
|
|
407
|
+
console.log("[detectExternalClaude] Returning cached result");
|
|
294
408
|
return cached.result;
|
|
295
409
|
}
|
|
410
|
+
console.log("[detectExternalClaude] Cache miss, performing fresh detection");
|
|
296
411
|
|
|
297
412
|
const result = {
|
|
298
413
|
hasExternalSession: false,
|
|
299
414
|
processes: [],
|
|
300
415
|
tmuxSessions: [],
|
|
301
416
|
lockFile: { exists: false, lockFile: null, content: null },
|
|
417
|
+
detectionAvailable: true,
|
|
418
|
+
detectionError: null,
|
|
302
419
|
};
|
|
303
420
|
|
|
304
421
|
// Detect processes
|
|
305
|
-
|
|
422
|
+
const processDetection = detectClaudeProcesses();
|
|
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
|
+
|
|
306
435
|
if (projectPath) {
|
|
307
436
|
// Filter to processes in this project
|
|
437
|
+
const beforeFilter = result.processes.length;
|
|
308
438
|
result.processes = result.processes.filter(
|
|
309
439
|
(p) => !p.cwd || p.cwd.startsWith(projectPath),
|
|
310
440
|
);
|
|
441
|
+
console.log(
|
|
442
|
+
"[detectExternalClaude] Filtered processes for project:",
|
|
443
|
+
beforeFilter,
|
|
444
|
+
"->",
|
|
445
|
+
result.processes.length,
|
|
446
|
+
);
|
|
311
447
|
}
|
|
312
448
|
|
|
313
449
|
// Detect tmux sessions
|
|
314
450
|
result.tmuxSessions = detectClaudeTmuxSessions();
|
|
451
|
+
console.log(
|
|
452
|
+
"[detectExternalClaude] tmux sessions found:",
|
|
453
|
+
result.tmuxSessions.length,
|
|
454
|
+
);
|
|
315
455
|
|
|
316
456
|
// Check lock file
|
|
317
457
|
if (projectPath) {
|