@askexenow/exe-os 0.9.68 → 0.9.69
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/bin/cc-doctor.js +293 -30
- package/dist/bin/exe-healthcheck.js +293 -30
- package/dist/lib/exe-daemon.js +1183 -860
- package/dist/mcp/server.js +728 -530
- package/package.json +1 -1
package/dist/bin/cc-doctor.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/bin/exe-healthcheck.ts
|
|
4
|
-
import { existsSync, readFileSync, readdirSync } from "fs";
|
|
5
|
-
import
|
|
4
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3, readdirSync } from "fs";
|
|
5
|
+
import path3 from "path";
|
|
6
6
|
import { execSync } from "child_process";
|
|
7
|
-
import { fileURLToPath as
|
|
7
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
8
8
|
|
|
9
9
|
// src/lib/is-main.ts
|
|
10
10
|
import { realpathSync } from "fs";
|
|
@@ -21,31 +21,239 @@ function isMainModule(importMetaUrl) {
|
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
// src/lib/config.ts
|
|
25
|
+
import { readFile, writeFile } from "fs/promises";
|
|
26
|
+
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
27
|
+
import path from "path";
|
|
28
|
+
import os from "os";
|
|
29
|
+
|
|
30
|
+
// src/lib/secure-files.ts
|
|
31
|
+
import { chmodSync, existsSync, mkdirSync } from "fs";
|
|
32
|
+
import { chmod, mkdir } from "fs/promises";
|
|
33
|
+
|
|
34
|
+
// src/lib/config.ts
|
|
35
|
+
function resolveDataDir() {
|
|
36
|
+
if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
|
|
37
|
+
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
38
|
+
const newDir = path.join(os.homedir(), ".exe-os");
|
|
39
|
+
const legacyDir = path.join(os.homedir(), ".exe-mem");
|
|
40
|
+
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
41
|
+
try {
|
|
42
|
+
renameSync(legacyDir, newDir);
|
|
43
|
+
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
44
|
+
`);
|
|
45
|
+
} catch {
|
|
46
|
+
return legacyDir;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return newDir;
|
|
50
|
+
}
|
|
51
|
+
var EXE_AI_DIR = resolveDataDir();
|
|
52
|
+
var DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
53
|
+
var MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
54
|
+
var CONFIG_PATH = path.join(EXE_AI_DIR, "config.json");
|
|
55
|
+
var LEGACY_LANCE_PATH = path.join(EXE_AI_DIR, "local.lance");
|
|
56
|
+
var CURRENT_CONFIG_VERSION = 1;
|
|
57
|
+
var DEFAULT_CONFIG = {
|
|
58
|
+
config_version: CURRENT_CONFIG_VERSION,
|
|
59
|
+
dbPath: DB_PATH,
|
|
60
|
+
modelFile: "jina-embeddings-v5-small-q4_k_m.gguf",
|
|
61
|
+
embeddingDim: 1024,
|
|
62
|
+
batchSize: 20,
|
|
63
|
+
flushIntervalMs: 1e4,
|
|
64
|
+
autoIngestion: true,
|
|
65
|
+
autoRetrieval: true,
|
|
66
|
+
searchMode: "hybrid",
|
|
67
|
+
hookSearchMode: "hybrid",
|
|
68
|
+
fileGrepEnabled: true,
|
|
69
|
+
splashEffect: true,
|
|
70
|
+
consolidationEnabled: true,
|
|
71
|
+
consolidationIntervalMs: 6 * 60 * 60 * 1e3,
|
|
72
|
+
consolidationModel: "claude-haiku-4-5-20251001",
|
|
73
|
+
consolidationMaxCallsPerRun: 20,
|
|
74
|
+
selfQueryRouter: true,
|
|
75
|
+
selfQueryModel: "claude-haiku-4-5-20251001",
|
|
76
|
+
rerankerEnabled: true,
|
|
77
|
+
scalingRoadmap: {
|
|
78
|
+
rerankerAutoTrigger: {
|
|
79
|
+
enabled: true,
|
|
80
|
+
broadQueryMinCardinality: 5e4,
|
|
81
|
+
fetchTopK: 200,
|
|
82
|
+
returnTopK: 20
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
graphRagEnabled: true,
|
|
86
|
+
wikiEnabled: false,
|
|
87
|
+
wikiUrl: "",
|
|
88
|
+
wikiApiKey: "",
|
|
89
|
+
wikiSyncIntervalMs: 30 * 60 * 1e3,
|
|
90
|
+
wikiWorkspaceMapping: {},
|
|
91
|
+
wikiAutoUpdate: true,
|
|
92
|
+
wikiAutoUpdateThreshold: 0.5,
|
|
93
|
+
wikiAutoUpdateCreateNew: true,
|
|
94
|
+
skillLearning: true,
|
|
95
|
+
skillThreshold: 3,
|
|
96
|
+
skillModel: "claude-haiku-4-5-20251001",
|
|
97
|
+
exeHeartbeat: {
|
|
98
|
+
enabled: true,
|
|
99
|
+
intervalSeconds: 60,
|
|
100
|
+
staleInProgressThresholdHours: 2
|
|
101
|
+
},
|
|
102
|
+
sessionLifecycle: {
|
|
103
|
+
idleKillEnabled: true,
|
|
104
|
+
idleKillTicksRequired: 3,
|
|
105
|
+
idleKillIntercomAckWindowMs: 1e4,
|
|
106
|
+
maxAutoInstances: 10
|
|
107
|
+
},
|
|
108
|
+
autoUpdate: {
|
|
109
|
+
checkOnBoot: true,
|
|
110
|
+
autoInstall: false,
|
|
111
|
+
checkIntervalMs: 24 * 60 * 60 * 1e3
|
|
112
|
+
},
|
|
113
|
+
orchestration: {
|
|
114
|
+
phase: "phase_1_coo",
|
|
115
|
+
phaseSetBy: "default"
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// src/lib/mcp-transport-health.ts
|
|
120
|
+
import { appendFileSync, closeSync, existsSync as existsSync3, mkdirSync as mkdirSync2, openSync, readFileSync as readFileSync2, readSync, statSync, writeFileSync } from "fs";
|
|
121
|
+
import path2 from "path";
|
|
122
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
123
|
+
var MCP_HTTP_EVENTS_PATH = path2.join(EXE_AI_DIR, "mcp-http-events.jsonl");
|
|
124
|
+
var MCP_MONITOR_DIR = path2.join(EXE_AI_DIR, "monitor");
|
|
125
|
+
var MCP_TRANSPORT_SUMMARY_PATH = path2.join(MCP_MONITOR_DIR, "mcp-transport-summary.json");
|
|
126
|
+
var DEFAULT_EVENT_READ_BYTES = 256 * 1024;
|
|
127
|
+
var DEFAULT_EVENT_MAX_BYTES = 1024 * 1024;
|
|
128
|
+
var EVENT_READ_BYTES = parsePositiveInt(process.env.EXE_MCP_HTTP_EVENT_READ_BYTES, DEFAULT_EVENT_READ_BYTES);
|
|
129
|
+
var EVENT_MAX_BYTES = parsePositiveInt(process.env.EXE_MCP_HTTP_EVENT_MAX_BYTES, DEFAULT_EVENT_MAX_BYTES);
|
|
130
|
+
function parsePositiveInt(value, fallback) {
|
|
131
|
+
if (!value) return fallback;
|
|
132
|
+
const parsed = Number.parseInt(value, 10);
|
|
133
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
134
|
+
}
|
|
135
|
+
function readTailText(filePath, maxBytes) {
|
|
136
|
+
const stats = statSync(filePath);
|
|
137
|
+
if (stats.size <= maxBytes) return readFileSync2(filePath, "utf8");
|
|
138
|
+
const fd = openSync(filePath, "r");
|
|
139
|
+
try {
|
|
140
|
+
const buffer = Buffer.allocUnsafe(maxBytes);
|
|
141
|
+
readSync(fd, buffer, 0, maxBytes, stats.size - maxBytes);
|
|
142
|
+
const text = buffer.toString("utf8");
|
|
143
|
+
const firstNewline = text.indexOf("\n");
|
|
144
|
+
return firstNewline >= 0 ? text.slice(firstNewline + 1) : text;
|
|
145
|
+
} finally {
|
|
146
|
+
closeSync(fd);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function readPackageVersion() {
|
|
150
|
+
let dir = path2.dirname(fileURLToPath2(import.meta.url));
|
|
151
|
+
const { root } = path2.parse(dir);
|
|
152
|
+
while (dir !== root) {
|
|
153
|
+
const pkgPath = path2.join(dir, "package.json");
|
|
154
|
+
if (existsSync3(pkgPath)) {
|
|
155
|
+
try {
|
|
156
|
+
const parsed = JSON.parse(readFileSync2(pkgPath, "utf8"));
|
|
157
|
+
return parsed.version ?? "unknown";
|
|
158
|
+
} catch {
|
|
159
|
+
return "unknown";
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
dir = path2.dirname(dir);
|
|
163
|
+
}
|
|
164
|
+
return "unknown";
|
|
165
|
+
}
|
|
166
|
+
function parseMcpHttpEventLines(raw, limit = 200) {
|
|
167
|
+
const lines = raw.trim().split("\n").filter(Boolean).slice(-limit);
|
|
168
|
+
const events = [];
|
|
169
|
+
for (const line of lines) {
|
|
170
|
+
try {
|
|
171
|
+
events.push(JSON.parse(line));
|
|
172
|
+
} catch {
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return events;
|
|
176
|
+
}
|
|
177
|
+
function readMcpHttpEvents(limit = 200) {
|
|
178
|
+
if (!existsSync3(MCP_HTTP_EVENTS_PATH)) return [];
|
|
179
|
+
const raw = readTailText(MCP_HTTP_EVENTS_PATH, EVENT_READ_BYTES);
|
|
180
|
+
return parseMcpHttpEventLines(raw, limit);
|
|
181
|
+
}
|
|
182
|
+
function summarizeMcpTransport(events = readMcpHttpEvents()) {
|
|
183
|
+
const lastServerStartIndex = events.map((event) => event.message).lastIndexOf("server_started");
|
|
184
|
+
const scopedEvents = lastServerStartIndex >= 0 ? events.slice(lastServerStartIndex) : events;
|
|
185
|
+
const activeSessionEvent = [...scopedEvents].reverse().find((event) => typeof event.activeSessions === "number");
|
|
186
|
+
const successfulToolCalls = scopedEvents.filter((event) => event.message === "tool_call_ok");
|
|
187
|
+
const lastSuccessfulToolCall = successfulToolCalls.at(-1) ?? null;
|
|
188
|
+
const summary = {
|
|
189
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
190
|
+
version: readPackageVersion(),
|
|
191
|
+
scope: "local",
|
|
192
|
+
privacy: "metadata_only",
|
|
193
|
+
eventLogPath: MCP_HTTP_EVENTS_PATH,
|
|
194
|
+
activeSessions: activeSessionEvent?.activeSessions ?? null,
|
|
195
|
+
sessionsCreated: scopedEvents.filter((event) => event.message === "session_initialized").length,
|
|
196
|
+
sessionsClosed: scopedEvents.filter((event) => event.message === "session_closed").length,
|
|
197
|
+
missingSessionErrors: scopedEvents.filter((event) => event.message?.includes("missing MCP session")).length,
|
|
198
|
+
staleSessionErrors: scopedEvents.filter((event) => event.message?.includes("stale or unknown")).length,
|
|
199
|
+
transportErrors: scopedEvents.filter((event) => event.level === "warn" || event.level === "error").length,
|
|
200
|
+
lastSuccessfulHandshake: scopedEvents.filter((event) => event.message === "session_initialized").at(-1)?.timestamp ?? null,
|
|
201
|
+
lastSuccessfulToolCall: lastSuccessfulToolCall?.timestamp ?? null,
|
|
202
|
+
lastSuccessfulToolCallByRuntime: {},
|
|
203
|
+
lastSuccessfulToolCallByAgent: {},
|
|
204
|
+
lastSuccessfulToolCallByFamily: {},
|
|
205
|
+
activeSessionsByRuntime: {},
|
|
206
|
+
recentWarnings: scopedEvents.filter((event) => event.level === "warn" || event.level === "error").slice(-10),
|
|
207
|
+
remediation: [
|
|
208
|
+
"If MCP tools fail with missing/stale session, reconnect the MCP client (/mcp or restart the runtime).",
|
|
209
|
+
"Do not access the database directly; MCP is the only data interface.",
|
|
210
|
+
"Check get_daemon_health or exe-healthcheck for daemon/MCP endpoint status before retrying work."
|
|
211
|
+
]
|
|
212
|
+
};
|
|
213
|
+
const createdBySession = /* @__PURE__ */ new Map();
|
|
214
|
+
for (const event of scopedEvents) {
|
|
215
|
+
if (event.message === "session_initialized" && event.sessionId) createdBySession.set(event.sessionId, event);
|
|
216
|
+
if ((event.message === "session_closed" || event.message === "session_expired") && event.sessionId) {
|
|
217
|
+
createdBySession.delete(event.sessionId);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
for (const event of createdBySession.values()) {
|
|
221
|
+
const runtime = event.runtime ?? "Unknown";
|
|
222
|
+
summary.activeSessionsByRuntime[runtime] = (summary.activeSessionsByRuntime[runtime] ?? 0) + 1;
|
|
223
|
+
}
|
|
224
|
+
for (const event of successfulToolCalls) {
|
|
225
|
+
if (event.runtime) summary.lastSuccessfulToolCallByRuntime[event.runtime] = event.timestamp ?? "";
|
|
226
|
+
if (event.agentId) summary.lastSuccessfulToolCallByAgent[event.agentId] = event.timestamp ?? "";
|
|
227
|
+
if (event.toolFamily) summary.lastSuccessfulToolCallByFamily[event.toolFamily] = event.timestamp ?? "";
|
|
228
|
+
}
|
|
229
|
+
return summary;
|
|
230
|
+
}
|
|
231
|
+
|
|
24
232
|
// src/bin/exe-healthcheck.ts
|
|
25
233
|
function findPackageRoot() {
|
|
26
|
-
let dir =
|
|
27
|
-
const { root } =
|
|
234
|
+
let dir = path3.dirname(fileURLToPath3(import.meta.url));
|
|
235
|
+
const { root } = path3.parse(dir);
|
|
28
236
|
while (dir !== root) {
|
|
29
|
-
if (
|
|
30
|
-
dir =
|
|
237
|
+
if (existsSync4(path3.join(dir, "package.json"))) return dir;
|
|
238
|
+
dir = path3.dirname(dir);
|
|
31
239
|
}
|
|
32
240
|
throw new Error("Cannot find package root");
|
|
33
241
|
}
|
|
34
242
|
function checkBuildIntegrity(pkgRoot) {
|
|
35
243
|
const results = [];
|
|
36
|
-
const tsupConfig =
|
|
37
|
-
if (!
|
|
244
|
+
const tsupConfig = path3.join(pkgRoot, "tsup.config.ts");
|
|
245
|
+
if (!existsSync4(tsupConfig)) {
|
|
38
246
|
return [{ name: "build/tsup-config", pass: false, detail: "tsup.config.ts not found" }];
|
|
39
247
|
}
|
|
40
|
-
const configContent =
|
|
248
|
+
const configContent = readFileSync3(tsupConfig, "utf-8");
|
|
41
249
|
const entryMatches = configContent.matchAll(/"([^"]+)":\s*"src\//g);
|
|
42
250
|
const missing = [];
|
|
43
251
|
let total = 0;
|
|
44
252
|
for (const match of entryMatches) {
|
|
45
253
|
const outputKey = match[1];
|
|
46
|
-
const expectedPath =
|
|
254
|
+
const expectedPath = path3.join(pkgRoot, "dist", `${outputKey}.js`);
|
|
47
255
|
total++;
|
|
48
|
-
if (!
|
|
256
|
+
if (!existsSync4(expectedPath)) {
|
|
49
257
|
missing.push(`dist/${outputKey}.js`);
|
|
50
258
|
}
|
|
51
259
|
}
|
|
@@ -67,8 +275,8 @@ function checkBuildIntegrity(pkgRoot) {
|
|
|
67
275
|
}
|
|
68
276
|
function checkEmbedPipeline(pkgRoot) {
|
|
69
277
|
const results = [];
|
|
70
|
-
const daemonPath =
|
|
71
|
-
if (!
|
|
278
|
+
const daemonPath = path3.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
279
|
+
if (!existsSync4(daemonPath)) {
|
|
72
280
|
results.push({
|
|
73
281
|
name: "exed/daemon-exists",
|
|
74
282
|
pass: false,
|
|
@@ -79,17 +287,17 @@ function checkEmbedPipeline(pkgRoot) {
|
|
|
79
287
|
results.push({ name: "exed/daemon-exists", pass: true, detail: "dist/lib/exe-daemon.js exists" });
|
|
80
288
|
const entryDirs = ["dist/hooks", "dist/bin", "dist/mcp"];
|
|
81
289
|
for (const dir of entryDirs) {
|
|
82
|
-
const fullDir =
|
|
83
|
-
if (!
|
|
290
|
+
const fullDir = path3.join(pkgRoot, dir);
|
|
291
|
+
if (!existsSync4(fullDir)) continue;
|
|
84
292
|
let walkDir = fullDir;
|
|
85
|
-
const { root } =
|
|
293
|
+
const { root } = path3.parse(walkDir);
|
|
86
294
|
let foundRoot = null;
|
|
87
295
|
while (walkDir !== root) {
|
|
88
|
-
if (
|
|
296
|
+
if (existsSync4(path3.join(walkDir, "package.json"))) {
|
|
89
297
|
foundRoot = walkDir;
|
|
90
298
|
break;
|
|
91
299
|
}
|
|
92
|
-
walkDir =
|
|
300
|
+
walkDir = path3.dirname(walkDir);
|
|
93
301
|
}
|
|
94
302
|
if (!foundRoot) {
|
|
95
303
|
results.push({
|
|
@@ -99,8 +307,8 @@ function checkEmbedPipeline(pkgRoot) {
|
|
|
99
307
|
});
|
|
100
308
|
continue;
|
|
101
309
|
}
|
|
102
|
-
const resolvedDaemon =
|
|
103
|
-
const reachable =
|
|
310
|
+
const resolvedDaemon = path3.join(foundRoot, "dist", "lib", "exe-daemon.js");
|
|
311
|
+
const reachable = existsSync4(resolvedDaemon);
|
|
104
312
|
results.push({
|
|
105
313
|
name: `exed/reachable-from-${dir}`,
|
|
106
314
|
pass: reachable,
|
|
@@ -111,8 +319,8 @@ function checkEmbedPipeline(pkgRoot) {
|
|
|
111
319
|
}
|
|
112
320
|
function checkTaskSystem(pkgRoot) {
|
|
113
321
|
const results = [];
|
|
114
|
-
const scannerPath =
|
|
115
|
-
if (!
|
|
322
|
+
const scannerPath = path3.join(pkgRoot, "dist", "bin", "scan-tasks.js");
|
|
323
|
+
if (!existsSync4(scannerPath)) {
|
|
116
324
|
results.push({ name: "tasks/scanner", pass: false, detail: "scan-tasks.js not found" });
|
|
117
325
|
return results;
|
|
118
326
|
}
|
|
@@ -134,8 +342,8 @@ function checkTaskSystem(pkgRoot) {
|
|
|
134
342
|
}
|
|
135
343
|
function checkWorkerSpawning(pkgRoot) {
|
|
136
344
|
const results = [];
|
|
137
|
-
const workerPath =
|
|
138
|
-
if (!
|
|
345
|
+
const workerPath = path3.join(pkgRoot, "dist", "hooks", "ingest-worker.js");
|
|
346
|
+
if (!existsSync4(workerPath)) {
|
|
139
347
|
results.push({ name: "workers/ingest-worker", pass: false, detail: "ingest-worker.js not found" });
|
|
140
348
|
return results;
|
|
141
349
|
}
|
|
@@ -149,14 +357,14 @@ function checkWorkerSpawning(pkgRoot) {
|
|
|
149
357
|
detail: `Parse error: ${err instanceof Error ? err.message.slice(0, 200) : String(err)}`
|
|
150
358
|
});
|
|
151
359
|
}
|
|
152
|
-
const hooksDir =
|
|
153
|
-
if (
|
|
360
|
+
const hooksDir = path3.join(pkgRoot, "dist", "hooks");
|
|
361
|
+
if (existsSync4(hooksDir)) {
|
|
154
362
|
const hookFiles = readdirSync(hooksDir).filter((f) => f.endsWith(".js") && !f.endsWith(".js.map"));
|
|
155
363
|
let hooksPassed = 0;
|
|
156
364
|
const hooksFailed = [];
|
|
157
365
|
for (const hook of hookFiles) {
|
|
158
366
|
try {
|
|
159
|
-
execSync(`node --check "${
|
|
367
|
+
execSync(`node --check "${path3.join(hooksDir, hook)}" 2>&1`, { timeout: 1e4, encoding: "utf-8" });
|
|
160
368
|
hooksPassed++;
|
|
161
369
|
} catch {
|
|
162
370
|
hooksFailed.push(hook);
|
|
@@ -178,6 +386,60 @@ function checkWorkerSpawning(pkgRoot) {
|
|
|
178
386
|
}
|
|
179
387
|
return results;
|
|
180
388
|
}
|
|
389
|
+
function checkMcpTransport() {
|
|
390
|
+
const results = [];
|
|
391
|
+
const pidPath = path3.join(EXE_AI_DIR, "exed.pid");
|
|
392
|
+
const tokenPath = path3.join(EXE_AI_DIR, "exed.token");
|
|
393
|
+
let daemonAlive = false;
|
|
394
|
+
if (existsSync4(pidPath)) {
|
|
395
|
+
try {
|
|
396
|
+
const pid = parseInt(readFileSync3(pidPath, "utf8").trim(), 10);
|
|
397
|
+
process.kill(pid, 0);
|
|
398
|
+
daemonAlive = true;
|
|
399
|
+
results.push({ name: "mcp/daemon-process", pass: true, detail: `exed process alive (pid ${pid})` });
|
|
400
|
+
} catch {
|
|
401
|
+
results.push({ name: "mcp/daemon-process", pass: false, detail: "exed.pid exists but process is not alive; restart exe-os/exed" });
|
|
402
|
+
}
|
|
403
|
+
} else {
|
|
404
|
+
results.push({ name: "mcp/daemon-process", pass: false, detail: "No exed.pid found; daemon is not running" });
|
|
405
|
+
}
|
|
406
|
+
if (daemonAlive && existsSync4(tokenPath)) {
|
|
407
|
+
try {
|
|
408
|
+
const token = readFileSync3(tokenPath, "utf8").trim();
|
|
409
|
+
const response = execSync(
|
|
410
|
+
`curl -sS -i -m 2 -H "Authorization: Bearer ${token}" -H 'Accept: application/json, text/event-stream' http://127.0.0.1:48739/mcp`,
|
|
411
|
+
{ encoding: "utf8", timeout: 3e3 }
|
|
412
|
+
);
|
|
413
|
+
const jsonRpcError = response.includes("Content-Type: application/json") && response.includes("missing MCP session");
|
|
414
|
+
results.push({
|
|
415
|
+
name: "mcp/http-endpoint",
|
|
416
|
+
pass: jsonRpcError,
|
|
417
|
+
detail: jsonRpcError ? "MCP HTTP endpoint reachable; missing-session probe returns JSON-RPC (expected)" : "MCP HTTP endpoint reachable but did not return expected JSON-RPC missing-session response"
|
|
418
|
+
});
|
|
419
|
+
} catch (err) {
|
|
420
|
+
results.push({
|
|
421
|
+
name: "mcp/http-endpoint",
|
|
422
|
+
pass: false,
|
|
423
|
+
detail: `MCP HTTP endpoint not reachable: ${err instanceof Error ? err.message.slice(0, 180) : String(err)}`
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
} else if (daemonAlive) {
|
|
427
|
+
results.push({ name: "mcp/http-endpoint", pass: false, detail: "Daemon is alive but exed.token is missing; cannot probe MCP HTTP" });
|
|
428
|
+
}
|
|
429
|
+
const events = readMcpHttpEvents(200);
|
|
430
|
+
const summary = summarizeMcpTransport(events);
|
|
431
|
+
results.push({
|
|
432
|
+
name: "mcp/local-event-log",
|
|
433
|
+
pass: true,
|
|
434
|
+
detail: `${events.length} recent event(s); activeSessions=${summary.activeSessions ?? "unknown"}; lastHandshake=${summary.lastSuccessfulHandshake ?? "none"}; lastTool=${summary.lastSuccessfulToolCall ?? "none"}`
|
|
435
|
+
});
|
|
436
|
+
results.push({
|
|
437
|
+
name: "mcp/monitor-summary",
|
|
438
|
+
pass: true,
|
|
439
|
+
detail: `Privacy-safe local monitor summary: ${path3.join(EXE_AI_DIR, "monitor", "mcp-transport-summary.json")}`
|
|
440
|
+
});
|
|
441
|
+
return results;
|
|
442
|
+
}
|
|
181
443
|
function checkClaudeCodeInstall() {
|
|
182
444
|
const results = [];
|
|
183
445
|
const execPath = process.env.CLAUDE_CODE_EXECPATH ?? "";
|
|
@@ -232,14 +494,14 @@ function checkClaudeCodeInstall() {
|
|
|
232
494
|
detail: "Failed to check claude binary path"
|
|
233
495
|
});
|
|
234
496
|
}
|
|
235
|
-
const versionsDir =
|
|
497
|
+
const versionsDir = path3.join(
|
|
236
498
|
process.env.HOME ?? process.env.USERPROFILE ?? "",
|
|
237
499
|
".local",
|
|
238
500
|
"share",
|
|
239
501
|
"claude",
|
|
240
502
|
"versions"
|
|
241
503
|
);
|
|
242
|
-
if (
|
|
504
|
+
if (existsSync4(versionsDir)) {
|
|
243
505
|
try {
|
|
244
506
|
const entries = readdirSync(versionsDir);
|
|
245
507
|
if (entries.length > 0) {
|
|
@@ -315,6 +577,7 @@ function runHealthCheck() {
|
|
|
315
577
|
...checkEmbedPipeline(pkgRoot),
|
|
316
578
|
...checkTaskSystem(pkgRoot),
|
|
317
579
|
...checkWorkerSpawning(pkgRoot),
|
|
580
|
+
...checkMcpTransport(),
|
|
318
581
|
...checkClaudeCodeInstall()
|
|
319
582
|
];
|
|
320
583
|
const passed = results.filter((r) => r.pass).length;
|