nexus-prime 7.9.3 → 7.9.4
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/dashboard/server.js
CHANGED
|
@@ -81,6 +81,20 @@ function stringifyDashboardJson(data) {
|
|
|
81
81
|
return value;
|
|
82
82
|
}, DASHBOARD_PRETTY_JSON ? 2 : 0);
|
|
83
83
|
}
|
|
84
|
+
function resolveDashboardAppFile(relativePath) {
|
|
85
|
+
const roots = [
|
|
86
|
+
path.join(__dirname, 'app'),
|
|
87
|
+
path.resolve(__dirname, '..', '..', 'src', 'dashboard', 'app'),
|
|
88
|
+
];
|
|
89
|
+
for (const root of roots) {
|
|
90
|
+
const resolved = path.resolve(root, relativePath);
|
|
91
|
+
if (resolved !== root && !resolved.startsWith(root + path.sep))
|
|
92
|
+
continue;
|
|
93
|
+
if (fs.existsSync(resolved))
|
|
94
|
+
return resolved;
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
84
98
|
export class DashboardServer {
|
|
85
99
|
server;
|
|
86
100
|
runtimeProvider;
|
|
@@ -485,7 +499,12 @@ export class DashboardServer {
|
|
|
485
499
|
'X-Content-Type-Options': 'nosniff',
|
|
486
500
|
'X-Frame-Options': 'DENY',
|
|
487
501
|
};
|
|
488
|
-
const htmlPath =
|
|
502
|
+
const htmlPath = resolveDashboardAppFile('index.html');
|
|
503
|
+
if (!htmlPath) {
|
|
504
|
+
res.writeHead(500);
|
|
505
|
+
res.end('Error loading LATTICE dashboard HTML: missing app/index.html');
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
489
508
|
fs.readFile(htmlPath, 'utf8', (err, data) => {
|
|
490
509
|
if (err) {
|
|
491
510
|
res.writeHead(500);
|
|
@@ -514,7 +533,12 @@ export class DashboardServer {
|
|
|
514
533
|
res.end('Bad request');
|
|
515
534
|
return;
|
|
516
535
|
}
|
|
517
|
-
const filePath =
|
|
536
|
+
const filePath = resolveDashboardAppFile(rel);
|
|
537
|
+
if (!filePath) {
|
|
538
|
+
res.writeHead(404);
|
|
539
|
+
res.end('Not found');
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
518
542
|
const ext = path.extname(filePath).toLowerCase();
|
|
519
543
|
const ct = EXT_TYPES[ext] ?? 'application/octet-stream';
|
|
520
544
|
fs.readFile(filePath, (err, data) => {
|
|
@@ -4,21 +4,30 @@
|
|
|
4
4
|
* Connects via Stdio transport to the configured cr-graph binary.
|
|
5
5
|
* Binary discovery order:
|
|
6
6
|
* 1. NEXUS_CRG_BIN env var
|
|
7
|
-
* 2.
|
|
7
|
+
* 2. <repo>/.mcp.json code-review-graph entry
|
|
8
|
+
* 3. ~/.nexus-prime/cr-graph.json { "bin": "/path/to/binary", "args": [...] }
|
|
8
9
|
*
|
|
9
10
|
* Failure mode: when the binary is not found or the process fails to start,
|
|
10
11
|
* the client returns `available = false` and all methods return empty results.
|
|
11
12
|
* The orchestrator falls back to the n-gram index. No regression.
|
|
12
13
|
*/
|
|
14
|
+
interface CrGraphConfig {
|
|
15
|
+
bin: string;
|
|
16
|
+
args?: string[];
|
|
17
|
+
cwd?: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function discoverCrGraphConfig(repoRoot?: string): CrGraphConfig | null;
|
|
13
20
|
export declare class CodeReviewGraphClient {
|
|
14
21
|
private client;
|
|
15
22
|
private transport;
|
|
16
23
|
private _available;
|
|
17
24
|
private _initialized;
|
|
25
|
+
private initPromise;
|
|
18
26
|
private repoRoot;
|
|
19
27
|
constructor(repoRoot: string);
|
|
20
28
|
get available(): boolean;
|
|
21
29
|
init(): Promise<void>;
|
|
30
|
+
private initialize;
|
|
22
31
|
/**
|
|
23
32
|
* Search for file nodes relevant to a task description.
|
|
24
33
|
* Returns absolute file paths, clamped to `limit`.
|
|
@@ -44,3 +53,4 @@ export declare class CodeReviewGraphClient {
|
|
|
44
53
|
* orchestrator falls back to the n-gram index if unavailable.
|
|
45
54
|
*/
|
|
46
55
|
export declare function ensureCrGraphBuilt(repoRoot: string): Promise<void>;
|
|
56
|
+
export {};
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* Connects via Stdio transport to the configured cr-graph binary.
|
|
5
5
|
* Binary discovery order:
|
|
6
6
|
* 1. NEXUS_CRG_BIN env var
|
|
7
|
-
* 2.
|
|
7
|
+
* 2. <repo>/.mcp.json code-review-graph entry
|
|
8
|
+
* 3. ~/.nexus-prime/cr-graph.json { "bin": "/path/to/binary", "args": [...] }
|
|
8
9
|
*
|
|
9
10
|
* Failure mode: when the binary is not found or the process fails to start,
|
|
10
11
|
* the client returns `available = false` and all methods return empty results.
|
|
@@ -18,13 +19,18 @@ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'
|
|
|
18
19
|
import { nexusEventBus } from './event-bus.js';
|
|
19
20
|
const MIN_CRG_CANDIDATES = 5;
|
|
20
21
|
const REQUEST_TIMEOUT_MS = 4_000;
|
|
21
|
-
|
|
22
|
+
const INIT_TIMEOUT_MS = 4_000;
|
|
23
|
+
export function discoverCrGraphConfig(repoRoot) {
|
|
22
24
|
// 1. Env var
|
|
23
25
|
const envBin = process.env['NEXUS_CRG_BIN'];
|
|
24
26
|
if (envBin) {
|
|
25
27
|
return { bin: envBin, args: [] };
|
|
26
28
|
}
|
|
27
|
-
// 2.
|
|
29
|
+
// 2. Repo-local MCP config.
|
|
30
|
+
const repoMcpConfig = repoRoot ? readMcpCrGraphConfig(path.join(repoRoot, '.mcp.json'), repoRoot) : null;
|
|
31
|
+
if (repoMcpConfig)
|
|
32
|
+
return repoMcpConfig;
|
|
33
|
+
// 3. ~/.nexus-prime/cr-graph.json
|
|
28
34
|
try {
|
|
29
35
|
const configPath = path.join(os.homedir(), '.nexus-prime', 'cr-graph.json');
|
|
30
36
|
const raw = fs.readFileSync(configPath, 'utf8');
|
|
@@ -42,11 +48,37 @@ function discoverCrGraphConfig() {
|
|
|
42
48
|
}
|
|
43
49
|
return null;
|
|
44
50
|
}
|
|
51
|
+
function readMcpCrGraphConfig(configPath, repoRoot) {
|
|
52
|
+
try {
|
|
53
|
+
const raw = fs.readFileSync(configPath, 'utf8');
|
|
54
|
+
const parsed = JSON.parse(raw);
|
|
55
|
+
const servers = parsed['mcpServers'];
|
|
56
|
+
if (!servers || typeof servers !== 'object' || Array.isArray(servers))
|
|
57
|
+
return null;
|
|
58
|
+
const server = servers['code-review-graph'];
|
|
59
|
+
if (!server || typeof server !== 'object' || Array.isArray(server))
|
|
60
|
+
return null;
|
|
61
|
+
const record = server;
|
|
62
|
+
if (typeof record['command'] !== 'string' || !record['command'])
|
|
63
|
+
return null;
|
|
64
|
+
return {
|
|
65
|
+
bin: record['command'],
|
|
66
|
+
args: Array.isArray(record['args'])
|
|
67
|
+
? record['args'].filter((arg) => typeof arg === 'string')
|
|
68
|
+
: [],
|
|
69
|
+
cwd: repoRoot,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
45
76
|
export class CodeReviewGraphClient {
|
|
46
77
|
client = null;
|
|
47
78
|
transport = null;
|
|
48
79
|
_available = false;
|
|
49
80
|
_initialized = false;
|
|
81
|
+
initPromise = null;
|
|
50
82
|
repoRoot;
|
|
51
83
|
constructor(repoRoot) {
|
|
52
84
|
this.repoRoot = repoRoot;
|
|
@@ -55,10 +87,18 @@ export class CodeReviewGraphClient {
|
|
|
55
87
|
return this._available;
|
|
56
88
|
}
|
|
57
89
|
async init() {
|
|
90
|
+
if (this.initPromise)
|
|
91
|
+
return this.initPromise;
|
|
58
92
|
if (this._initialized)
|
|
59
93
|
return;
|
|
94
|
+
this.initPromise = this.initialize().finally(() => {
|
|
95
|
+
this.initPromise = null;
|
|
96
|
+
});
|
|
97
|
+
return this.initPromise;
|
|
98
|
+
}
|
|
99
|
+
async initialize() {
|
|
60
100
|
this._initialized = true;
|
|
61
|
-
const config = discoverCrGraphConfig();
|
|
101
|
+
const config = discoverCrGraphConfig(this.repoRoot);
|
|
62
102
|
if (!config)
|
|
63
103
|
return;
|
|
64
104
|
try {
|
|
@@ -69,11 +109,18 @@ export class CodeReviewGraphClient {
|
|
|
69
109
|
stderr: 'ignore',
|
|
70
110
|
});
|
|
71
111
|
this.client = new Client({ name: 'nexus-prime', version: '1.0.0' }, { capabilities: {} });
|
|
72
|
-
await
|
|
112
|
+
await Promise.race([
|
|
113
|
+
this.client.connect(this.transport),
|
|
114
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('cr-graph init timeout')), INIT_TIMEOUT_MS)),
|
|
115
|
+
]);
|
|
73
116
|
this._available = true;
|
|
74
117
|
}
|
|
75
118
|
catch {
|
|
76
119
|
// binary not found or failed to start — degrade gracefully
|
|
120
|
+
try {
|
|
121
|
+
await this.transport?.close();
|
|
122
|
+
}
|
|
123
|
+
catch { /* best-effort */ }
|
|
77
124
|
this.client = null;
|
|
78
125
|
this.transport = null;
|
|
79
126
|
this._available = false;
|
|
@@ -85,6 +132,7 @@ export class CodeReviewGraphClient {
|
|
|
85
132
|
* Returns empty array when unavailable or on error.
|
|
86
133
|
*/
|
|
87
134
|
async semanticSearchNodes(query, limit = 20) {
|
|
135
|
+
await this.init();
|
|
88
136
|
if (!this._available || !this.client)
|
|
89
137
|
return [];
|
|
90
138
|
try {
|
|
@@ -148,7 +196,7 @@ export async function ensureCrGraphBuilt(repoRoot) {
|
|
|
148
196
|
}
|
|
149
197
|
}
|
|
150
198
|
catch { /* stamp missing or corrupt — proceed */ }
|
|
151
|
-
const config = discoverCrGraphConfig();
|
|
199
|
+
const config = discoverCrGraphConfig(repoRoot);
|
|
152
200
|
if (!config)
|
|
153
201
|
return; // binary not configured — nothing to do
|
|
154
202
|
const transport = new StdioClientTransport({
|
|
@@ -41,7 +41,7 @@ export class KnowledgeFabricEngine {
|
|
|
41
41
|
reused: true,
|
|
42
42
|
},
|
|
43
43
|
};
|
|
44
|
-
this.persistBundle(bundle);
|
|
44
|
+
await this.persistBundle(bundle);
|
|
45
45
|
return bundle;
|
|
46
46
|
}
|
|
47
47
|
const bundle = await this.composeStage(input, stage, cacheKey);
|
|
@@ -53,7 +53,7 @@ export class KnowledgeFabricEngine {
|
|
|
53
53
|
reused: false,
|
|
54
54
|
},
|
|
55
55
|
});
|
|
56
|
-
this.persistBundle(bundle);
|
|
56
|
+
await this.persistBundle(bundle);
|
|
57
57
|
return bundle;
|
|
58
58
|
}
|
|
59
59
|
async composeStage(input, stage, cacheKey) {
|
|
@@ -299,7 +299,7 @@ export class KnowledgeFabricEngine {
|
|
|
299
299
|
trace: snapshot?.modelTierTrace ?? [],
|
|
300
300
|
};
|
|
301
301
|
}
|
|
302
|
-
persistBundle(bundle) {
|
|
302
|
+
async persistBundle(bundle) {
|
|
303
303
|
const snapshot = {
|
|
304
304
|
runtimeId: bundle.runtimeId,
|
|
305
305
|
sessionId: bundle.sessionId,
|
|
@@ -325,8 +325,12 @@ export class KnowledgeFabricEngine {
|
|
|
325
325
|
};
|
|
326
326
|
// Update in-memory cache immediately so getSessionSnapshot has zero disk latency.
|
|
327
327
|
this.snapshotCache.set(bundle.runtimeId, snapshot);
|
|
328
|
-
|
|
329
|
-
|
|
328
|
+
try {
|
|
329
|
+
await this.persistBundleAsync(bundle.runtimeId, bundle.sessionId, snapshot);
|
|
330
|
+
}
|
|
331
|
+
catch {
|
|
332
|
+
// Snapshot persistence is best effort; the in-memory cache remains authoritative in-process.
|
|
333
|
+
}
|
|
330
334
|
}
|
|
331
335
|
async persistBundleAsync(runtimeId, sessionId, snapshot) {
|
|
332
336
|
const runtimeDir = path.join(this.stateDir, runtimeId);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexus-prime",
|
|
3
|
-
"version": "7.9.
|
|
3
|
+
"version": "7.9.4",
|
|
4
4
|
"description": "Local-first MCP control plane for coding agents with bootstrap-orchestrate execution, memory fabric, token budgeting, and worktree-backed swarms",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|