nexus-prime 7.9.3 → 7.9.5
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/app/widgets/runtime-badge.js +2 -2
- package/dist/dashboard/routes/runtime.js +1 -1
- package/dist/dashboard/server.js +26 -2
- package/dist/engines/code-review-graph-client.d.ts +11 -1
- package/dist/engines/code-review-graph-client.js +54 -6
- package/dist/engines/knowledge-fabric.js +9 -5
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* runtime-badge.js — Push-mode runtime availability badges.
|
|
3
3
|
*
|
|
4
|
-
* Fetches GET /api/
|
|
4
|
+
* Fetches GET /api/invokers and renders one pill per invoker in the
|
|
5
5
|
* sidebar footer showing amber (unavailable) or green (available) status.
|
|
6
6
|
*
|
|
7
7
|
* Mounts into #runtime-badges (created by index.html in the sidebar foot).
|
|
@@ -21,7 +21,7 @@ export async function refresh() {
|
|
|
21
21
|
const container = $('runtime-badges');
|
|
22
22
|
if (!container) return;
|
|
23
23
|
|
|
24
|
-
const runtimes = await api('/api/
|
|
24
|
+
const runtimes = await api('/api/invokers', 8_000);
|
|
25
25
|
if (!Array.isArray(runtimes)) return;
|
|
26
26
|
|
|
27
27
|
container.innerHTML = runtimes.map(r => _badge(r)).join('');
|
|
@@ -215,7 +215,7 @@ export const handleRuntimeRoutes = async (ctx, req, res, url) => {
|
|
|
215
215
|
return true;
|
|
216
216
|
}
|
|
217
217
|
// ── Push-mode dispatch ────────────────────────────────────────────────────
|
|
218
|
-
if (req.method === 'GET' && url.pathname === '/api/
|
|
218
|
+
if (req.method === 'GET' && url.pathname === '/api/invokers') {
|
|
219
219
|
const availability = await invokerRegistry.listAvailability();
|
|
220
220
|
ctx.respondJson(res, availability);
|
|
221
221
|
return true;
|
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.5",
|
|
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",
|