nexus-agents 2.28.0 → 2.29.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/README.md +11 -10
- package/dist/chunk-5VZLXMO7.js +838 -0
- package/dist/chunk-5VZLXMO7.js.map +1 -0
- package/dist/chunk-633WH2ML.js +127 -0
- package/dist/chunk-633WH2ML.js.map +1 -0
- package/dist/chunk-7F6HYUIY.js +327 -0
- package/dist/chunk-7F6HYUIY.js.map +1 -0
- package/dist/chunk-CLYZ7FWP.js +30 -0
- package/dist/chunk-CLYZ7FWP.js.map +1 -0
- package/dist/{chunk-QZEAD6AG.js → chunk-DAMRMAM2.js} +19526 -35943
- package/dist/chunk-DAMRMAM2.js.map +1 -0
- package/dist/{chunk-YSDUVCCZ.js → chunk-HH5LVGEE.js} +6 -6
- package/dist/{chunk-E7EX2KQJ.js → chunk-HWDBNDUX.js} +2 -2
- package/dist/chunk-I6YDS23R.js +354 -0
- package/dist/chunk-I6YDS23R.js.map +1 -0
- package/dist/{chunk-L2SHSW4T.js → chunk-IMWYKX4H.js} +4377 -4190
- package/dist/chunk-IMWYKX4H.js.map +1 -0
- package/dist/chunk-KGDG6PWZ.js +61 -0
- package/dist/chunk-KGDG6PWZ.js.map +1 -0
- package/dist/chunk-POBO4G2P.js +3700 -0
- package/dist/chunk-POBO4G2P.js.map +1 -0
- package/dist/chunk-S3BKWNST.js +13475 -0
- package/dist/chunk-S3BKWNST.js.map +1 -0
- package/dist/chunk-T7PU3NPQ.js +42 -0
- package/dist/chunk-T7PU3NPQ.js.map +1 -0
- package/dist/{chunk-UGNLR4NZ.js → chunk-WSK4VSXP.js} +2 -2
- package/dist/{chunk-LKSTILEE.js → chunk-ZBZJHXRT.js} +181 -1120
- package/dist/chunk-ZBZJHXRT.js.map +1 -0
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +1400 -649
- package/dist/cli.js.map +1 -1
- package/dist/composite-router-YPRWVTRB.js +17 -0
- package/dist/consensus-vote-DBE6RNZG.js +23 -0
- package/dist/{dist-H5XNXVAV.js → dist-7PQR2BQB.js} +1006 -954
- package/dist/dist-7PQR2BQB.js.map +1 -0
- package/dist/{doctor-deep-BDE2PHVX.js → doctor-deep-AWE7SRU6.js} +4 -3
- package/dist/expert-config-FHNBQRX2.js +22 -0
- package/dist/expert-config-FHNBQRX2.js.map +1 -0
- package/dist/factory-O5C7ZBZO.js +13 -0
- package/dist/factory-O5C7ZBZO.js.map +1 -0
- package/dist/factory-PCHGQ3ZG.js +16 -0
- package/dist/factory-PCHGQ3ZG.js.map +1 -0
- package/dist/index.d.ts +1253 -681
- package/dist/index.js +490 -313
- package/dist/index.js.map +1 -1
- package/dist/learning-persistence-WMWZJZ35.js +16 -0
- package/dist/learning-persistence-WMWZJZ35.js.map +1 -0
- package/dist/mcp-config-AUZQPUBY.js +12 -0
- package/dist/mcp-config-AUZQPUBY.js.map +1 -0
- package/dist/{model-capabilities-types-CSWO27YN.d.ts → model-capabilities-types-B57GZryc.d.ts} +1 -1
- package/dist/routing-memory-QY3XMU2R.js +13 -0
- package/dist/routing-memory-QY3XMU2R.js.map +1 -0
- package/dist/session-memory-3MBCE5KS.js +22 -0
- package/dist/session-memory-3MBCE5KS.js.map +1 -0
- package/dist/{setup-command-SS7LMN7Y.js → setup-command-IQ4MD3FT.js} +8 -5
- package/dist/setup-command-IQ4MD3FT.js.map +1 -0
- package/dist/setup-config-5YUPLDXF.js +10 -0
- package/dist/setup-config-5YUPLDXF.js.map +1 -0
- package/dist/weather-report-CC2C4KAX.js +15 -0
- package/dist/weather-report-CC2C4KAX.js.map +1 -0
- package/package.json +16 -18
- package/dist/chunk-L2SHSW4T.js.map +0 -1
- package/dist/chunk-LKSTILEE.js.map +0 -1
- package/dist/chunk-QZEAD6AG.js.map +0 -1
- package/dist/dist-H5XNXVAV.js.map +0 -1
- package/dist/setup-config-DSMOOLVW.js +0 -9
- /package/dist/{chunk-YSDUVCCZ.js.map → chunk-HH5LVGEE.js.map} +0 -0
- /package/dist/{chunk-E7EX2KQJ.js.map → chunk-HWDBNDUX.js.map} +0 -0
- /package/dist/{chunk-UGNLR4NZ.js.map → chunk-WSK4VSXP.js.map} +0 -0
- /package/dist/{doctor-deep-BDE2PHVX.js.map → composite-router-YPRWVTRB.js.map} +0 -0
- /package/dist/{setup-command-SS7LMN7Y.js.map → consensus-vote-DBE6RNZG.js.map} +0 -0
- /package/dist/{setup-config-DSMOOLVW.js.map → doctor-deep-AWE7SRU6.js.map} +0 -0
|
@@ -0,0 +1,838 @@
|
|
|
1
|
+
import {
|
|
2
|
+
capitalize
|
|
3
|
+
} from "./chunk-633WH2ML.js";
|
|
4
|
+
import {
|
|
5
|
+
createAllAdapters
|
|
6
|
+
} from "./chunk-ZBZJHXRT.js";
|
|
7
|
+
import {
|
|
8
|
+
DEFAULT_CAPABILITIES,
|
|
9
|
+
DEFAULT_MODEL_CAPABILITIES,
|
|
10
|
+
colors,
|
|
11
|
+
createLogger,
|
|
12
|
+
err,
|
|
13
|
+
getErrorMessage,
|
|
14
|
+
getTimeProvider,
|
|
15
|
+
ok,
|
|
16
|
+
symbols,
|
|
17
|
+
writeLine
|
|
18
|
+
} from "./chunk-IMWYKX4H.js";
|
|
19
|
+
import {
|
|
20
|
+
LEARNING_DIR,
|
|
21
|
+
OUTCOMES_FILE,
|
|
22
|
+
RULES_FILE,
|
|
23
|
+
isPersistenceEnabled
|
|
24
|
+
} from "./chunk-CLYZ7FWP.js";
|
|
25
|
+
|
|
26
|
+
// src/version.ts
|
|
27
|
+
var VERSION = true ? "2.29.0" : "dev";
|
|
28
|
+
|
|
29
|
+
// src/cli/setup-data-dir.ts
|
|
30
|
+
import { mkdirSync, existsSync as existsSync2 } from "fs";
|
|
31
|
+
import { homedir as homedir2 } from "os";
|
|
32
|
+
import { join as join2 } from "path";
|
|
33
|
+
|
|
34
|
+
// src/cli/doctor.ts
|
|
35
|
+
import { existsSync, readFileSync, accessSync, constants as fsConstants } from "fs";
|
|
36
|
+
import { homedir } from "os";
|
|
37
|
+
import { join } from "path";
|
|
38
|
+
|
|
39
|
+
// src/mcp/server.ts
|
|
40
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
41
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
42
|
+
|
|
43
|
+
// src/mcp/task-store.ts
|
|
44
|
+
import { InMemoryTaskStore } from "@modelcontextprotocol/sdk/experimental/tasks";
|
|
45
|
+
var MAX_TASK_TTL_MS = 6e5;
|
|
46
|
+
var DEFAULT_TASK_TTL_MS = 3e5;
|
|
47
|
+
var MAX_TASK_CAPACITY = 50;
|
|
48
|
+
var CLEANUP_INTERVAL_MS = 6e4;
|
|
49
|
+
var logger = createLogger({ component: "task-store" });
|
|
50
|
+
var singletonStore;
|
|
51
|
+
var cleanupTimer;
|
|
52
|
+
function getTaskStore() {
|
|
53
|
+
if (singletonStore === void 0) {
|
|
54
|
+
singletonStore = new InMemoryTaskStore();
|
|
55
|
+
logger.info("Task store created", { maxCapacity: MAX_TASK_CAPACITY });
|
|
56
|
+
cleanupTimer = setInterval(() => {
|
|
57
|
+
evictExcessTasks();
|
|
58
|
+
}, CLEANUP_INTERVAL_MS);
|
|
59
|
+
if (typeof cleanupTimer === "object" && "unref" in cleanupTimer) {
|
|
60
|
+
cleanupTimer.unref();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return singletonStore;
|
|
64
|
+
}
|
|
65
|
+
function clampTaskTtl(requestedTtl) {
|
|
66
|
+
if (requestedTtl === void 0 || requestedTtl === null) {
|
|
67
|
+
return DEFAULT_TASK_TTL_MS;
|
|
68
|
+
}
|
|
69
|
+
if (requestedTtl > MAX_TASK_TTL_MS) {
|
|
70
|
+
logger.warn("Task TTL clamped to maximum", {
|
|
71
|
+
requested: requestedTtl,
|
|
72
|
+
max: MAX_TASK_TTL_MS
|
|
73
|
+
});
|
|
74
|
+
return MAX_TASK_TTL_MS;
|
|
75
|
+
}
|
|
76
|
+
return requestedTtl;
|
|
77
|
+
}
|
|
78
|
+
function evictExcessTasks() {
|
|
79
|
+
if (singletonStore === void 0) return;
|
|
80
|
+
const allTasks = singletonStore.getAllTasks();
|
|
81
|
+
if (allTasks.length <= MAX_TASK_CAPACITY) return;
|
|
82
|
+
const sorted = [...allTasks].sort(
|
|
83
|
+
(a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()
|
|
84
|
+
);
|
|
85
|
+
const evictCount = sorted.length - MAX_TASK_CAPACITY;
|
|
86
|
+
for (let i = 0; i < evictCount; i++) {
|
|
87
|
+
const task = sorted[i];
|
|
88
|
+
if (task === void 0) continue;
|
|
89
|
+
singletonStore.updateTaskStatus(task.taskId, "cancelled", "Evicted: capacity exceeded").catch((err2) => {
|
|
90
|
+
logger.debug("Failed to evict task", {
|
|
91
|
+
taskId: task.taskId,
|
|
92
|
+
error: String(err2)
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
logger.info("Evicted excess tasks", { evicted: evictCount, total: sorted.length });
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// src/mcp/server.ts
|
|
100
|
+
var DEFAULT_SERVER_NAME = "nexus-agents";
|
|
101
|
+
function createServerError(code, message, error) {
|
|
102
|
+
const serverError = { code, message };
|
|
103
|
+
if (error instanceof Error) {
|
|
104
|
+
serverError.cause = error;
|
|
105
|
+
}
|
|
106
|
+
return serverError;
|
|
107
|
+
}
|
|
108
|
+
function createServer(config) {
|
|
109
|
+
const serverName = config?.name ?? DEFAULT_SERVER_NAME;
|
|
110
|
+
const serverVersion = config?.version ?? VERSION;
|
|
111
|
+
const logger2 = config?.logger ?? createLogger({ component: "mcp-server" });
|
|
112
|
+
try {
|
|
113
|
+
initDataDirectories();
|
|
114
|
+
logger2.info("Creating MCP server", {
|
|
115
|
+
name: serverName,
|
|
116
|
+
version: serverVersion
|
|
117
|
+
});
|
|
118
|
+
const server = new McpServer(
|
|
119
|
+
{
|
|
120
|
+
name: serverName,
|
|
121
|
+
version: serverVersion
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
capabilities: {
|
|
125
|
+
logging: {},
|
|
126
|
+
prompts: {},
|
|
127
|
+
resources: {},
|
|
128
|
+
tasks: {}
|
|
129
|
+
},
|
|
130
|
+
taskStore: getTaskStore()
|
|
131
|
+
}
|
|
132
|
+
);
|
|
133
|
+
logger2.debug("MCP server created successfully");
|
|
134
|
+
return ok({ server, logger: logger2 });
|
|
135
|
+
} catch (error) {
|
|
136
|
+
const errorMessage = getErrorMessage(error);
|
|
137
|
+
logger2.error("Failed to create MCP server", error instanceof Error ? error : void 0);
|
|
138
|
+
return err(
|
|
139
|
+
createServerError(
|
|
140
|
+
"SERVER_CREATION_FAILED",
|
|
141
|
+
`Failed to create MCP server: ${errorMessage}`,
|
|
142
|
+
error
|
|
143
|
+
)
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
async function connectTransport(server, transport, logger2) {
|
|
148
|
+
const log = logger2 ?? createLogger({ component: "mcp-server" });
|
|
149
|
+
try {
|
|
150
|
+
log.info("Connecting server to transport");
|
|
151
|
+
await server.connect(transport);
|
|
152
|
+
log.debug("Server connected to transport successfully");
|
|
153
|
+
return ok(void 0);
|
|
154
|
+
} catch (error) {
|
|
155
|
+
const errorMessage = getErrorMessage(error);
|
|
156
|
+
log.error("Failed to connect to transport", error instanceof Error ? error : void 0);
|
|
157
|
+
return err(
|
|
158
|
+
createServerError(
|
|
159
|
+
"SERVER_START_FAILED",
|
|
160
|
+
`Failed to connect to transport: ${errorMessage}`,
|
|
161
|
+
error
|
|
162
|
+
)
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
async function startStdioServer(config) {
|
|
167
|
+
const serverResult = createServer(config);
|
|
168
|
+
if (!serverResult.ok) {
|
|
169
|
+
return serverResult;
|
|
170
|
+
}
|
|
171
|
+
const { server, logger: logger2 } = serverResult.value;
|
|
172
|
+
try {
|
|
173
|
+
logger2.info("Starting stdio transport");
|
|
174
|
+
const transport = new StdioServerTransport();
|
|
175
|
+
const connectResult = await connectTransport(server, transport, logger2);
|
|
176
|
+
if (!connectResult.ok) {
|
|
177
|
+
return connectResult;
|
|
178
|
+
}
|
|
179
|
+
logger2.info("MCP server running with stdio transport");
|
|
180
|
+
return ok({ server, logger: logger2 });
|
|
181
|
+
} catch (error) {
|
|
182
|
+
const errorMessage = getErrorMessage(error);
|
|
183
|
+
logger2.error("Failed to start stdio server", error instanceof Error ? error : void 0);
|
|
184
|
+
return err(
|
|
185
|
+
createServerError(
|
|
186
|
+
"SERVER_START_FAILED",
|
|
187
|
+
`Failed to start stdio server: ${errorMessage}`,
|
|
188
|
+
error
|
|
189
|
+
)
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
async function closeServer(server, logger2) {
|
|
194
|
+
const log = logger2 ?? createLogger({ component: "mcp-server" });
|
|
195
|
+
try {
|
|
196
|
+
log.info("Closing MCP server");
|
|
197
|
+
await server.close();
|
|
198
|
+
log.info("MCP server closed successfully");
|
|
199
|
+
return ok(void 0);
|
|
200
|
+
} catch (error) {
|
|
201
|
+
const errorMessage = getErrorMessage(error);
|
|
202
|
+
log.error("Failed to close server", error instanceof Error ? error : void 0);
|
|
203
|
+
return err(
|
|
204
|
+
createServerError("SERVER_STOP_FAILED", `Failed to close server: ${errorMessage}`, error)
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// src/cli/doctor-formatting.ts
|
|
210
|
+
var REQUIRED_NODE_MAJOR = 22;
|
|
211
|
+
function formatStatus(healthy, warn = false) {
|
|
212
|
+
if (healthy) return `${colors.green}${symbols.check}${colors.reset}`;
|
|
213
|
+
if (warn) return `${colors.yellow}${symbols.warn}${colors.reset}`;
|
|
214
|
+
return `${colors.red}${symbols.cross}${colors.reset}`;
|
|
215
|
+
}
|
|
216
|
+
function formatVersionStatus(status) {
|
|
217
|
+
switch (status) {
|
|
218
|
+
case "supported":
|
|
219
|
+
return `${colors.green}supported${colors.reset}`;
|
|
220
|
+
case "outdated":
|
|
221
|
+
return `${colors.yellow}outdated${colors.reset}`;
|
|
222
|
+
case "unsupported":
|
|
223
|
+
case "breaking":
|
|
224
|
+
return `${colors.red}${status}${colors.reset}`;
|
|
225
|
+
default:
|
|
226
|
+
return status;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
function formatCapacity(capacity) {
|
|
230
|
+
if (capacity === void 0) return "Unknown";
|
|
231
|
+
const remaining = 100 - capacity.utilizationPercent;
|
|
232
|
+
const remainingStr = String(remaining);
|
|
233
|
+
if (remaining > 80) return `${colors.green}${remainingStr}% remaining${colors.reset}`;
|
|
234
|
+
if (remaining > 20) return `${colors.yellow}${remainingStr}% remaining${colors.reset}`;
|
|
235
|
+
return `${colors.red}${remainingStr}% remaining${colors.reset}`;
|
|
236
|
+
}
|
|
237
|
+
function printInstalledCliDetails(cli) {
|
|
238
|
+
writeLine(` Version: ${cli.version} (${formatVersionStatus(cli.versionStatus)})`);
|
|
239
|
+
const authText = cli.authenticated ? `${colors.green}${cli.authMethod ?? "Authenticated"}${colors.reset}` : `${colors.red}Not authenticated${colors.reset}`;
|
|
240
|
+
writeLine(` Auth: ${authText}`);
|
|
241
|
+
if (cli.capacity !== void 0) {
|
|
242
|
+
writeLine(` Capacity: ${formatCapacity(cli.capacity)}`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
function printCliResult(cli) {
|
|
246
|
+
const status = cli.installed && cli.authenticated;
|
|
247
|
+
const warn = cli.installed && (!cli.authenticated || cli.versionStatus === "outdated");
|
|
248
|
+
writeLine(
|
|
249
|
+
`${formatStatus(status, warn)} ${colors.bold}${capitalize(cli.name)} CLI${colors.reset}`
|
|
250
|
+
);
|
|
251
|
+
if (cli.installed) {
|
|
252
|
+
printInstalledCliDetails(cli);
|
|
253
|
+
} else {
|
|
254
|
+
const errorText = cli.error ?? "Not installed";
|
|
255
|
+
writeLine(` ${colors.red}Error: ${errorText}${colors.reset}`);
|
|
256
|
+
}
|
|
257
|
+
if (cli.fix !== void 0 && cli.fix !== "") {
|
|
258
|
+
writeLine(` ${colors.dim}Fix: ${cli.fix}${colors.reset}`);
|
|
259
|
+
}
|
|
260
|
+
writeLine("");
|
|
261
|
+
}
|
|
262
|
+
function printCapabilities(clis) {
|
|
263
|
+
const installedClis = clis.filter((c) => c.installed);
|
|
264
|
+
if (installedClis.length === 0) {
|
|
265
|
+
writeLine(`${formatStatus(false)} No CLIs installed`);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
const caps = DEFAULT_CAPABILITIES;
|
|
269
|
+
const bestReasoning = installedClis.reduce(
|
|
270
|
+
(best, c) => caps[c.name].reasoning > caps[best.name].reasoning ? c : best
|
|
271
|
+
);
|
|
272
|
+
const bestContext = installedClis.reduce(
|
|
273
|
+
(best, c) => caps[c.name].contextWindow > caps[best.name].contextWindow ? c : best
|
|
274
|
+
);
|
|
275
|
+
const bestSpeed = installedClis.reduce(
|
|
276
|
+
(best, c) => caps[c.name].speed > caps[best.name].speed ? c : best
|
|
277
|
+
);
|
|
278
|
+
const contextTokensK = (caps[bestContext.name].contextWindow / 1e3).toFixed(0);
|
|
279
|
+
writeLine(
|
|
280
|
+
`${formatStatus(true)} Complex reasoning: ${colors.bold}${capitalize(bestReasoning.name)}${colors.reset}`
|
|
281
|
+
);
|
|
282
|
+
writeLine(
|
|
283
|
+
`${formatStatus(true)} Large context: ${colors.bold}${capitalize(bestContext.name)}${colors.reset} (${contextTokensK}K tokens)`
|
|
284
|
+
);
|
|
285
|
+
writeLine(
|
|
286
|
+
`${formatStatus(true)} Fast execution: ${colors.bold}${capitalize(bestSpeed.name)}${colors.reset}`
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
function printNodeVersionCheck(check) {
|
|
290
|
+
const versionText = check.supported ? `${colors.green}${check.version}${colors.reset}` : `${colors.yellow}${check.version}${colors.reset}`;
|
|
291
|
+
writeLine(`${formatStatus(check.supported, !check.supported)} Node.js version: ${versionText}`);
|
|
292
|
+
if (!check.supported) {
|
|
293
|
+
writeLine(
|
|
294
|
+
` ${colors.dim}Warning: Node.js ${String(REQUIRED_NODE_MAJOR)}.x LTS required${colors.reset}`
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
function printApiKeysCheck(keys) {
|
|
299
|
+
const configuredCount = keys.filter((k) => k.configured).length;
|
|
300
|
+
const configuredNames = keys.filter((k) => k.configured).map((k) => k.name);
|
|
301
|
+
const hasAny = configuredCount > 0;
|
|
302
|
+
writeLine(
|
|
303
|
+
`${formatStatus(hasAny, !hasAny)} API keys configured: ${String(configuredCount)} of ${String(keys.length)}`
|
|
304
|
+
);
|
|
305
|
+
if (hasAny) {
|
|
306
|
+
writeLine(` ${colors.dim}Keys: ${configuredNames.join(", ")}${colors.reset}`);
|
|
307
|
+
} else {
|
|
308
|
+
writeLine(
|
|
309
|
+
` ${colors.dim}Set ANTHROPIC_API_KEY, OPENAI_API_KEY, or GOOGLE_AI_API_KEY${colors.reset}`
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
function printConfigFileCheck(check) {
|
|
314
|
+
if (check.found && check.path !== null) {
|
|
315
|
+
writeLine(`${formatStatus(true)} Configuration loaded: ${check.path}`);
|
|
316
|
+
} else {
|
|
317
|
+
writeLine(`${formatStatus(false, true)} Configuration file: Not found`);
|
|
318
|
+
writeLine(` ${colors.dim}Run: nexus-agents config init${colors.reset}`);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
function printRegistryAdvisory(advisory) {
|
|
322
|
+
const allAvailable = advisory.unavailableModels === 0;
|
|
323
|
+
const countText = `${String(advisory.availableModels)} of ${String(advisory.totalModels)}`;
|
|
324
|
+
writeLine(`${formatStatus(allAvailable, !allAvailable)} Models available: ${countText}`);
|
|
325
|
+
if (advisory.unavailableModels > 0) {
|
|
326
|
+
const missing = advisory.models.filter((m) => !m.available);
|
|
327
|
+
for (const m of missing) {
|
|
328
|
+
writeLine(` ${colors.dim}${m.displayName} \u2014 ${m.reason}${colors.reset}`);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
const ageText = `${String(advisory.registryAgeDays)} days old`;
|
|
332
|
+
if (advisory.registryStale) {
|
|
333
|
+
writeLine(
|
|
334
|
+
`${colors.yellow}${symbols.warn}${colors.reset} Model registry is ${ageText} \u2014 may have stale model data`
|
|
335
|
+
);
|
|
336
|
+
writeLine(` ${colors.dim}Run: npx tsx scripts/probe-models.ts${colors.reset}`);
|
|
337
|
+
} else {
|
|
338
|
+
writeLine(`${formatStatus(true)} Model registry: ${ageText}`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
function printLearningPersistence(check) {
|
|
342
|
+
if (!check.enabled) {
|
|
343
|
+
writeLine(`${formatStatus(true)} Learning persistence: ${colors.dim}Disabled${colors.reset}`);
|
|
344
|
+
writeLine(` ${colors.dim}Set NEXUS_PERSIST_LEARNING=true to enable${colors.reset}`);
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
const healthy = check.dirExists && check.dirWritable && check.error === null;
|
|
348
|
+
writeLine(`${formatStatus(healthy, !healthy)} Learning persistence: Enabled`);
|
|
349
|
+
if (check.error !== null) {
|
|
350
|
+
writeLine(` ${colors.red}Error: ${check.error}${colors.reset}`);
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
const dirStatus = check.dirExists ? check.dirWritable ? `${colors.green}writable${colors.reset}` : `${colors.red}not writable${colors.reset}` : `${colors.yellow}not created yet${colors.reset}`;
|
|
354
|
+
writeLine(` Data directory: ${dirStatus}`);
|
|
355
|
+
writeLine(` Outcomes: ${String(check.outcomeCount)} recorded`);
|
|
356
|
+
writeLine(` Distilled rules: ${String(check.ruleCount)} active`);
|
|
357
|
+
if (check.rulesLastSaved !== null) {
|
|
358
|
+
writeLine(` Rules last saved: ${check.rulesLastSaved}`);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
function printSqliteCheck(check) {
|
|
362
|
+
if (check.available) {
|
|
363
|
+
writeLine(
|
|
364
|
+
`${formatStatus(true)} SQLite (better-sqlite3): ${colors.green}Available${colors.reset}`
|
|
365
|
+
);
|
|
366
|
+
} else {
|
|
367
|
+
writeLine(
|
|
368
|
+
`${formatStatus(false, true)} SQLite (better-sqlite3): ${colors.yellow}Not available${colors.reset}`
|
|
369
|
+
);
|
|
370
|
+
writeLine(
|
|
371
|
+
` ${colors.dim}Memory backends (agentic, adaptive, typed) require it${colors.reset}`
|
|
372
|
+
);
|
|
373
|
+
writeLine(` ${colors.dim}Fix: npm install -g better-sqlite3${colors.reset}`);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
function printDataDirectory(check) {
|
|
377
|
+
if (check.rootExists) {
|
|
378
|
+
const existCount = check.subdirectories.filter((d) => d.exists).length;
|
|
379
|
+
const totalCount = check.subdirectories.length;
|
|
380
|
+
const allExist = existCount === totalCount;
|
|
381
|
+
const allWritable = check.subdirectories.every((d) => !d.exists || d.writable);
|
|
382
|
+
const healthy = allExist && allWritable;
|
|
383
|
+
writeLine(
|
|
384
|
+
`${formatStatus(healthy, !healthy)} Data directory: ${check.rootPath} (${String(existCount)}/${String(totalCount)} subdirs)`
|
|
385
|
+
);
|
|
386
|
+
if (!allExist) {
|
|
387
|
+
const missing = check.subdirectories.filter((d) => !d.exists);
|
|
388
|
+
for (const dir of missing) {
|
|
389
|
+
writeLine(` ${colors.dim}Missing: ${dir.name}/${colors.reset}`);
|
|
390
|
+
}
|
|
391
|
+
writeLine(` ${colors.dim}Fix: nexus-agents setup${colors.reset}`);
|
|
392
|
+
}
|
|
393
|
+
if (!allWritable) {
|
|
394
|
+
const readonly_ = check.subdirectories.filter((d) => d.exists && !d.writable);
|
|
395
|
+
for (const dir of readonly_) {
|
|
396
|
+
writeLine(` ${colors.yellow}Not writable: ${dir.name}/${colors.reset}`);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
} else {
|
|
400
|
+
writeLine(
|
|
401
|
+
`${formatStatus(false, true)} Data directory: ${colors.yellow}Not created${colors.reset}`
|
|
402
|
+
);
|
|
403
|
+
writeLine(` ${colors.dim}Run: nexus-agents setup${colors.reset}`);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
function printDoctorSummary(result) {
|
|
407
|
+
const unhealthyCount = result.clis.filter((c) => !c.installed || !c.authenticated).length;
|
|
408
|
+
const nodeIssue = result.nodeVersion.supported ? 0 : 1;
|
|
409
|
+
const totalIssues = unhealthyCount + nodeIssue + (result.mcpServerReady ? 0 : 1);
|
|
410
|
+
const summary = result.allHealthy ? `${colors.green}${colors.bold}Status: Ready${colors.reset}` : `${colors.yellow}${colors.bold}Summary: ${String(totalIssues)} issue(s) found${colors.reset}`;
|
|
411
|
+
writeLine(summary);
|
|
412
|
+
writeLine("");
|
|
413
|
+
}
|
|
414
|
+
function printDoctorResults(result) {
|
|
415
|
+
writeLine("");
|
|
416
|
+
writeLine(`${colors.bold}Nexus Agents Doctor${colors.reset}`);
|
|
417
|
+
writeLine("===================");
|
|
418
|
+
writeLine("");
|
|
419
|
+
writeLine(`${colors.cyan}Checking environment...${colors.reset}`);
|
|
420
|
+
writeLine("");
|
|
421
|
+
printNodeVersionCheck(result.nodeVersion);
|
|
422
|
+
printApiKeysCheck(result.apiKeys);
|
|
423
|
+
printConfigFileCheck(result.configFile);
|
|
424
|
+
writeLine("");
|
|
425
|
+
writeLine(`${colors.cyan}Checking CLI installations...${colors.reset}`);
|
|
426
|
+
writeLine("");
|
|
427
|
+
for (const cli of result.clis) {
|
|
428
|
+
printCliResult(cli);
|
|
429
|
+
}
|
|
430
|
+
writeLine(`${colors.cyan}Checking MCP configuration...${colors.reset}`);
|
|
431
|
+
writeLine("");
|
|
432
|
+
writeLine(
|
|
433
|
+
`${formatStatus(result.mcpServerReady)} MCP Server mode: ${result.mcpServerReady ? "Ready" : "Not ready"}`
|
|
434
|
+
);
|
|
435
|
+
writeLine(
|
|
436
|
+
`${formatStatus(result.mcpClientReady)} MCP Client mode: ${result.mcpClientReady ? "Ready (Codex mcp-server)" : "Not ready (Codex not installed)"}`
|
|
437
|
+
);
|
|
438
|
+
writeLine("");
|
|
439
|
+
writeLine(`${colors.cyan}Checking capabilities...${colors.reset}`);
|
|
440
|
+
writeLine("");
|
|
441
|
+
printCapabilities(result.clis);
|
|
442
|
+
writeLine("");
|
|
443
|
+
writeLine(`${colors.cyan}Checking model registry...${colors.reset}`);
|
|
444
|
+
writeLine("");
|
|
445
|
+
printRegistryAdvisory(result.registryAdvisory);
|
|
446
|
+
writeLine("");
|
|
447
|
+
writeLine(`${colors.cyan}Checking learning subsystem...${colors.reset}`);
|
|
448
|
+
writeLine("");
|
|
449
|
+
printLearningPersistence(result.learningPersistence);
|
|
450
|
+
writeLine("");
|
|
451
|
+
writeLine(`${colors.cyan}Checking data storage...${colors.reset}`);
|
|
452
|
+
writeLine("");
|
|
453
|
+
printSqliteCheck(result.sqliteCheck);
|
|
454
|
+
printDataDirectory(result.dataDirectory);
|
|
455
|
+
writeLine("");
|
|
456
|
+
printDoctorSummary(result);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// src/cli/doctor.ts
|
|
460
|
+
var REQUIRED_NODE_MAJOR2 = 22;
|
|
461
|
+
var API_KEY_VARS = ["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "GOOGLE_AI_API_KEY"];
|
|
462
|
+
var CONFIG_FILE_PATHS = ["./nexus-agents.yaml", "./nexus-agents.yml"];
|
|
463
|
+
var DATA_SUBDIRECTORIES = [
|
|
464
|
+
"memory",
|
|
465
|
+
"memory/beliefs",
|
|
466
|
+
"learning",
|
|
467
|
+
"sessions",
|
|
468
|
+
"audit",
|
|
469
|
+
"voting",
|
|
470
|
+
"auth",
|
|
471
|
+
"research",
|
|
472
|
+
"checkpoints"
|
|
473
|
+
];
|
|
474
|
+
function getFixCommand(name, issue) {
|
|
475
|
+
const commands = {
|
|
476
|
+
claude: {
|
|
477
|
+
install: "npm install -g @anthropic-ai/claude-code",
|
|
478
|
+
upgrade: "npm update -g @anthropic-ai/claude-code",
|
|
479
|
+
auth: "claude auth login"
|
|
480
|
+
},
|
|
481
|
+
gemini: {
|
|
482
|
+
install: "npm install -g @google/gemini-cli",
|
|
483
|
+
upgrade: "npm update -g @google/gemini-cli",
|
|
484
|
+
auth: "gemini auth login"
|
|
485
|
+
},
|
|
486
|
+
codex: {
|
|
487
|
+
install: "npm install -g @openai/codex",
|
|
488
|
+
upgrade: "npm update -g @openai/codex",
|
|
489
|
+
auth: "codex auth login"
|
|
490
|
+
},
|
|
491
|
+
opencode: {
|
|
492
|
+
install: "npm install -g opencode-ai",
|
|
493
|
+
upgrade: "npm update -g opencode-ai",
|
|
494
|
+
auth: "opencode auth login"
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
return commands[name][issue] ?? "";
|
|
498
|
+
}
|
|
499
|
+
function createNotFoundResult(name, errorMsg) {
|
|
500
|
+
return {
|
|
501
|
+
name,
|
|
502
|
+
installed: false,
|
|
503
|
+
version: "N/A",
|
|
504
|
+
versionStatus: "unsupported",
|
|
505
|
+
authenticated: false,
|
|
506
|
+
error: errorMsg,
|
|
507
|
+
fix: getFixCommand(name, "install")
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
function detectAuthMethod(name) {
|
|
511
|
+
const authMethods = {
|
|
512
|
+
claude: "CLI auth",
|
|
513
|
+
gemini: "ADC/CLI auth",
|
|
514
|
+
codex: "CLI auth",
|
|
515
|
+
opencode: "CLI auth"
|
|
516
|
+
};
|
|
517
|
+
return authMethods[name];
|
|
518
|
+
}
|
|
519
|
+
function createHealthyResult(name, health, capacity) {
|
|
520
|
+
const authenticated = health.healthy;
|
|
521
|
+
const result = {
|
|
522
|
+
name,
|
|
523
|
+
installed: true,
|
|
524
|
+
version: health.version,
|
|
525
|
+
versionStatus: health.versionStatus,
|
|
526
|
+
authenticated,
|
|
527
|
+
...authenticated && { authMethod: detectAuthMethod(name) },
|
|
528
|
+
...capacity !== void 0 && { capacity }
|
|
529
|
+
};
|
|
530
|
+
if (health.message !== void 0 && health.message !== "") {
|
|
531
|
+
return { ...result, error: health.message };
|
|
532
|
+
}
|
|
533
|
+
if (!authenticated) {
|
|
534
|
+
return { ...result, fix: getFixCommand(name, "auth") };
|
|
535
|
+
}
|
|
536
|
+
if (health.versionStatus === "outdated") {
|
|
537
|
+
return { ...result, fix: getFixCommand(name, "upgrade") };
|
|
538
|
+
}
|
|
539
|
+
return result;
|
|
540
|
+
}
|
|
541
|
+
async function checkCli(name) {
|
|
542
|
+
const adapters = createAllAdapters();
|
|
543
|
+
const adapter = adapters.get(name);
|
|
544
|
+
if (!adapter) {
|
|
545
|
+
return createNotFoundResult(name, "Adapter not available");
|
|
546
|
+
}
|
|
547
|
+
try {
|
|
548
|
+
const health = await adapter.healthCheck();
|
|
549
|
+
let capacity;
|
|
550
|
+
try {
|
|
551
|
+
capacity = await adapter.getCapacity();
|
|
552
|
+
} catch (capErr) {
|
|
553
|
+
void capErr;
|
|
554
|
+
}
|
|
555
|
+
return createHealthyResult(name, health, capacity);
|
|
556
|
+
} catch (error) {
|
|
557
|
+
const message = getErrorMessage(error);
|
|
558
|
+
const isNotFound = message.includes("ENOENT") || message.includes("not found");
|
|
559
|
+
return createNotFoundResult(name, isNotFound ? "Not found in PATH" : message);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
function checkNodeVersion() {
|
|
563
|
+
const version = process.version;
|
|
564
|
+
const major = Number(version.slice(1).split(".")[0]);
|
|
565
|
+
return {
|
|
566
|
+
version,
|
|
567
|
+
major,
|
|
568
|
+
supported: major >= REQUIRED_NODE_MAJOR2
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
function checkApiKeys() {
|
|
572
|
+
return API_KEY_VARS.map((name) => ({
|
|
573
|
+
name,
|
|
574
|
+
configured: typeof process.env[name] === "string" && process.env[name] !== ""
|
|
575
|
+
}));
|
|
576
|
+
}
|
|
577
|
+
function checkConfigFile() {
|
|
578
|
+
for (const configPath of CONFIG_FILE_PATHS) {
|
|
579
|
+
if (existsSync(configPath)) {
|
|
580
|
+
return { found: true, path: configPath };
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
return { found: false, path: null };
|
|
584
|
+
}
|
|
585
|
+
function checkMcpServerReady() {
|
|
586
|
+
try {
|
|
587
|
+
const result = createServer({ name: "nexus-agents-doctor-check" });
|
|
588
|
+
return result.ok;
|
|
589
|
+
} catch {
|
|
590
|
+
return false;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
function buildRegistryAdvisory(cliResults) {
|
|
594
|
+
const installedClis = new Set(cliResults.filter((c) => c.installed).map((c) => c.name));
|
|
595
|
+
const models = DEFAULT_MODEL_CAPABILITIES.models.filter((m) => m.cliName !== void 0).map((m) => {
|
|
596
|
+
const cliName = m.cliName ?? "";
|
|
597
|
+
const available = cliName.length > 0 && installedClis.has(cliName);
|
|
598
|
+
const reason = available ? `${cliName} CLI is installed` : `${cliName} CLI is not installed`;
|
|
599
|
+
return { modelId: m.id, displayName: m.displayName, cliName, available, reason };
|
|
600
|
+
});
|
|
601
|
+
const STALE_THRESHOLD_DAYS = 30;
|
|
602
|
+
const updatedAt = new Date(DEFAULT_MODEL_CAPABILITIES.updatedAt);
|
|
603
|
+
const nowMs = getTimeProvider().now();
|
|
604
|
+
const ageDays = Math.floor((nowMs - updatedAt.getTime()) / (1e3 * 60 * 60 * 24));
|
|
605
|
+
return {
|
|
606
|
+
totalModels: models.length,
|
|
607
|
+
availableModels: models.filter((m) => m.available).length,
|
|
608
|
+
unavailableModels: models.filter((m) => !m.available).length,
|
|
609
|
+
models,
|
|
610
|
+
registryAgeDays: ageDays,
|
|
611
|
+
registryStale: ageDays > STALE_THRESHOLD_DAYS
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
function countJsonlLines(filePath) {
|
|
615
|
+
if (!existsSync(filePath)) return 0;
|
|
616
|
+
return readFileSync(filePath, "utf-8").split("\n").filter((l) => l.trim().length > 0).length;
|
|
617
|
+
}
|
|
618
|
+
function readRulesMetadata(filePath) {
|
|
619
|
+
if (!existsSync(filePath)) return { count: 0, savedAt: null };
|
|
620
|
+
try {
|
|
621
|
+
const raw = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
622
|
+
const rules = raw["rules"];
|
|
623
|
+
const saved = raw["savedAt"];
|
|
624
|
+
return {
|
|
625
|
+
count: Array.isArray(rules) ? rules.length : 0,
|
|
626
|
+
savedAt: typeof saved === "string" ? saved : null
|
|
627
|
+
};
|
|
628
|
+
} catch {
|
|
629
|
+
return { count: 0, savedAt: null };
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
function checkDirAccess(dir) {
|
|
633
|
+
const exists = existsSync(dir);
|
|
634
|
+
if (!exists) return { exists: false, writable: false };
|
|
635
|
+
try {
|
|
636
|
+
accessSync(dir, fsConstants.W_OK);
|
|
637
|
+
return { exists: true, writable: true };
|
|
638
|
+
} catch {
|
|
639
|
+
return { exists: true, writable: false };
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
var DISABLED_CHECK = {
|
|
643
|
+
enabled: false,
|
|
644
|
+
dirExists: false,
|
|
645
|
+
dirWritable: false,
|
|
646
|
+
outcomeCount: 0,
|
|
647
|
+
ruleCount: 0,
|
|
648
|
+
rulesLastSaved: null,
|
|
649
|
+
error: null
|
|
650
|
+
};
|
|
651
|
+
function checkLearningPersistence() {
|
|
652
|
+
if (!isPersistenceEnabled()) return DISABLED_CHECK;
|
|
653
|
+
try {
|
|
654
|
+
const { exists: dirExists, writable: dirWritable } = checkDirAccess(LEARNING_DIR);
|
|
655
|
+
const outcomeCount = countJsonlLines(OUTCOMES_FILE);
|
|
656
|
+
const { count: ruleCount, savedAt: rulesLastSaved } = readRulesMetadata(RULES_FILE);
|
|
657
|
+
return {
|
|
658
|
+
enabled: true,
|
|
659
|
+
dirExists,
|
|
660
|
+
dirWritable,
|
|
661
|
+
outcomeCount,
|
|
662
|
+
ruleCount,
|
|
663
|
+
rulesLastSaved,
|
|
664
|
+
error: null
|
|
665
|
+
};
|
|
666
|
+
} catch (error) {
|
|
667
|
+
return {
|
|
668
|
+
enabled: true,
|
|
669
|
+
dirExists: false,
|
|
670
|
+
dirWritable: false,
|
|
671
|
+
outcomeCount: 0,
|
|
672
|
+
ruleCount: 0,
|
|
673
|
+
rulesLastSaved: null,
|
|
674
|
+
error: getErrorMessage(error)
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
async function checkSqlite() {
|
|
679
|
+
try {
|
|
680
|
+
await import("better-sqlite3");
|
|
681
|
+
return { available: true, error: null };
|
|
682
|
+
} catch (error) {
|
|
683
|
+
const msg = getErrorMessage(error);
|
|
684
|
+
const isNotFound = msg.includes("Cannot find") || msg.includes("MODULE_NOT_FOUND");
|
|
685
|
+
return {
|
|
686
|
+
available: false,
|
|
687
|
+
error: isNotFound ? "better-sqlite3 not installed \u2014 5 memory backends unavailable" : `better-sqlite3 load error: ${msg}`
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
function checkDataDirectory() {
|
|
692
|
+
const rootPath = join(homedir(), ".nexus-agents");
|
|
693
|
+
const rootExists = existsSync(rootPath);
|
|
694
|
+
const subdirectories = DATA_SUBDIRECTORIES.map((name) => {
|
|
695
|
+
const fullPath = join(rootPath, name);
|
|
696
|
+
const exists = existsSync(fullPath);
|
|
697
|
+
return { name, path: fullPath, exists, writable: exists && isWritable(fullPath) };
|
|
698
|
+
});
|
|
699
|
+
return { rootExists, rootPath, subdirectories };
|
|
700
|
+
}
|
|
701
|
+
function isWritable(dirPath) {
|
|
702
|
+
try {
|
|
703
|
+
accessSync(dirPath, fsConstants.W_OK);
|
|
704
|
+
return true;
|
|
705
|
+
} catch {
|
|
706
|
+
return false;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
async function runDoctor() {
|
|
710
|
+
const clis = await Promise.all([
|
|
711
|
+
checkCli("claude"),
|
|
712
|
+
checkCli("gemini"),
|
|
713
|
+
checkCli("codex"),
|
|
714
|
+
checkCli("opencode")
|
|
715
|
+
]);
|
|
716
|
+
const nodeVersion = checkNodeVersion();
|
|
717
|
+
const apiKeys = checkApiKeys();
|
|
718
|
+
const configFile = checkConfigFile();
|
|
719
|
+
const mcpServerReady = checkMcpServerReady();
|
|
720
|
+
const codexCheck = clis.find((c) => c.name === "codex");
|
|
721
|
+
const mcpClientReady = codexCheck?.installed ?? false;
|
|
722
|
+
const registryAdvisory = buildRegistryAdvisory(clis);
|
|
723
|
+
const learningPersistence = checkLearningPersistence();
|
|
724
|
+
const sqliteCheck = await checkSqlite();
|
|
725
|
+
const dataDirectory = checkDataDirectory();
|
|
726
|
+
const hasAuthMethod = apiKeys.some((k) => k.configured) || clis.some((c) => c.installed && c.authenticated);
|
|
727
|
+
const allHealthy = nodeVersion.supported && hasAuthMethod && mcpServerReady && clis.every((c) => c.installed && c.authenticated && c.versionStatus !== "unsupported");
|
|
728
|
+
return {
|
|
729
|
+
clis,
|
|
730
|
+
nodeVersion,
|
|
731
|
+
apiKeys,
|
|
732
|
+
configFile,
|
|
733
|
+
mcpServerReady,
|
|
734
|
+
mcpClientReady,
|
|
735
|
+
registryAdvisory,
|
|
736
|
+
learningPersistence,
|
|
737
|
+
sqliteCheck,
|
|
738
|
+
dataDirectory,
|
|
739
|
+
allHealthy,
|
|
740
|
+
timestamp: new Date(getTimeProvider().now())
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
async function doctorCommand(options = {}) {
|
|
744
|
+
const result = await runDoctor();
|
|
745
|
+
printDoctorResults(result);
|
|
746
|
+
if (options.fix === true) {
|
|
747
|
+
await runDoctorFix(result);
|
|
748
|
+
}
|
|
749
|
+
return result.allHealthy ? 0 : 1;
|
|
750
|
+
}
|
|
751
|
+
async function runDoctorFix(result) {
|
|
752
|
+
const writeLine2 = (text) => {
|
|
753
|
+
process.stdout.write(text + "\n");
|
|
754
|
+
};
|
|
755
|
+
writeLine2("");
|
|
756
|
+
writeLine2("\x1B[1mAuto-fix\x1B[0m");
|
|
757
|
+
writeLine2("\u2500".repeat(40));
|
|
758
|
+
let fixCount = 0;
|
|
759
|
+
if (!result.dataDirectory.rootExists || result.dataDirectory.subdirectories.some((d) => !d.exists || !d.writable)) {
|
|
760
|
+
const { runSetup } = await import("./setup-command-IQ4MD3FT.js");
|
|
761
|
+
const setupResult = runSetup({
|
|
762
|
+
skipMcp: true,
|
|
763
|
+
skipRules: true,
|
|
764
|
+
skipHooks: true,
|
|
765
|
+
skipConfig: true,
|
|
766
|
+
skipOpencode: true
|
|
767
|
+
});
|
|
768
|
+
if (setupResult.success) {
|
|
769
|
+
writeLine2("\u2713 Created missing data directories");
|
|
770
|
+
fixCount++;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
if (!result.configFile.found) {
|
|
774
|
+
const { runConfigInitSync } = await import("./setup-config-5YUPLDXF.js");
|
|
775
|
+
const configResult = runConfigInitSync(process.cwd(), false, false);
|
|
776
|
+
if (configResult.success && configResult.created) {
|
|
777
|
+
writeLine2(`\u2713 Generated config: ${configResult.path}`);
|
|
778
|
+
fixCount++;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
if (!result.sqliteCheck.available) {
|
|
782
|
+
writeLine2("");
|
|
783
|
+
writeLine2("\u26A0 better-sqlite3 not installed (manual step required):");
|
|
784
|
+
writeLine2(" npm install -g better-sqlite3");
|
|
785
|
+
}
|
|
786
|
+
if (fixCount > 0) {
|
|
787
|
+
writeLine2("");
|
|
788
|
+
writeLine2(
|
|
789
|
+
`\x1B[32m${String(fixCount)} issue(s) fixed.\x1B[0m Re-run \x1B[1mnexus-agents doctor\x1B[0m to verify.`
|
|
790
|
+
);
|
|
791
|
+
} else {
|
|
792
|
+
writeLine2("No auto-fixable issues found.");
|
|
793
|
+
}
|
|
794
|
+
writeLine2("");
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// src/cli/setup-data-dir.ts
|
|
798
|
+
var NEXUS_DATA_DIR = join2(homedir2(), ".nexus-agents");
|
|
799
|
+
var RESTRICTED_DIRS = /* @__PURE__ */ new Set(["auth"]);
|
|
800
|
+
function initDataDirectories(dryRun = false) {
|
|
801
|
+
const created = [];
|
|
802
|
+
const alreadyExisted = [];
|
|
803
|
+
try {
|
|
804
|
+
ensureDir(NEXUS_DATA_DIR, dryRun, created, alreadyExisted);
|
|
805
|
+
for (const subdir of DATA_SUBDIRECTORIES) {
|
|
806
|
+
const mode = RESTRICTED_DIRS.has(subdir) ? 448 : void 0;
|
|
807
|
+
ensureDir(join2(NEXUS_DATA_DIR, subdir), dryRun, created, alreadyExisted, mode);
|
|
808
|
+
}
|
|
809
|
+
return { success: true, rootPath: NEXUS_DATA_DIR, created, alreadyExisted, error: null };
|
|
810
|
+
} catch (error) {
|
|
811
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
812
|
+
return { success: false, rootPath: NEXUS_DATA_DIR, created, alreadyExisted, error: msg };
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
function ensureDir(dirPath, dryRun, created, alreadyExisted, mode) {
|
|
816
|
+
if (existsSync2(dirPath)) {
|
|
817
|
+
alreadyExisted.push(dirPath);
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
if (!dryRun) {
|
|
821
|
+
mkdirSync(dirPath, { recursive: true, ...mode !== void 0 ? { mode } : {} });
|
|
822
|
+
}
|
|
823
|
+
created.push(dirPath);
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
export {
|
|
827
|
+
VERSION,
|
|
828
|
+
DEFAULT_TASK_TTL_MS,
|
|
829
|
+
clampTaskTtl,
|
|
830
|
+
runDoctor,
|
|
831
|
+
doctorCommand,
|
|
832
|
+
initDataDirectories,
|
|
833
|
+
createServer,
|
|
834
|
+
connectTransport,
|
|
835
|
+
startStdioServer,
|
|
836
|
+
closeServer
|
|
837
|
+
};
|
|
838
|
+
//# sourceMappingURL=chunk-5VZLXMO7.js.map
|