@vibgrate/cli 2026.722.1 → 2026.727.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/DOCS.md +418 -196
- package/README.md +34 -7
- package/dist/baseline-K7YBFKZI.js +7 -0
- package/dist/{baseline-J44M7MWP.js.map → baseline-K7YBFKZI.js.map} +1 -1
- package/dist/chunk-3BVSDMHV.js +2496 -0
- package/dist/chunk-3BVSDMHV.js.map +1 -0
- package/dist/{chunk-PY3DNX5H.js → chunk-3Q7QBWFW.js} +13 -2
- package/dist/chunk-3Q7QBWFW.js.map +1 -0
- package/dist/chunk-4CQ3MGNN.js +136 -0
- package/dist/chunk-4CQ3MGNN.js.map +1 -0
- package/dist/chunk-5I4VSBWQ.js +764 -0
- package/dist/chunk-5I4VSBWQ.js.map +1 -0
- package/dist/chunk-5Q54GEHM.js +29 -0
- package/dist/chunk-5Q54GEHM.js.map +1 -0
- package/dist/{chunk-XKXKWYJF.js → chunk-65MEOA4E.js} +21 -18
- package/dist/chunk-65MEOA4E.js.map +1 -0
- package/dist/{chunk-WNIIKCNF.js → chunk-6IOQJGFT.js} +29 -238
- package/dist/chunk-6IOQJGFT.js.map +1 -0
- package/dist/{chunk-CS37OBE3.js → chunk-7JHPQ6WF.js} +791 -783
- package/dist/chunk-7JHPQ6WF.js.map +1 -0
- package/dist/chunk-BPF3YX2U.js +64 -0
- package/dist/chunk-BPF3YX2U.js.map +1 -0
- package/dist/chunk-BWPBB45M.js +88 -0
- package/dist/chunk-BWPBB45M.js.map +1 -0
- package/dist/chunk-C4CKTAZP.js +1695 -0
- package/dist/chunk-C4CKTAZP.js.map +1 -0
- package/dist/chunk-EDAAG7BL.js +612 -0
- package/dist/chunk-EDAAG7BL.js.map +1 -0
- package/dist/chunk-EDBV33B7.js +57 -0
- package/dist/chunk-EDBV33B7.js.map +1 -0
- package/dist/chunk-HABUT7HW.js +233 -0
- package/dist/chunk-HABUT7HW.js.map +1 -0
- package/dist/{chunk-EXQV26MF.js → chunk-HYZVHJ3Q.js} +3 -3
- package/dist/{chunk-EXQV26MF.js.map → chunk-HYZVHJ3Q.js.map} +1 -1
- package/dist/chunk-IS7VTH2Q.js +627 -0
- package/dist/chunk-IS7VTH2Q.js.map +1 -0
- package/dist/chunk-IZDH67DX.js +289 -0
- package/dist/chunk-IZDH67DX.js.map +1 -0
- package/dist/chunk-J7LKEB2R.js +51 -0
- package/dist/chunk-J7LKEB2R.js.map +1 -0
- package/dist/chunk-JIBOJTGS.js +313 -0
- package/dist/chunk-JIBOJTGS.js.map +1 -0
- package/dist/chunk-JZYGEZTU.js +82 -0
- package/dist/chunk-JZYGEZTU.js.map +1 -0
- package/dist/chunk-K7REHDQS.js +776 -0
- package/dist/chunk-K7REHDQS.js.map +1 -0
- package/dist/chunk-KHJO4D5N.js +108 -0
- package/dist/chunk-KHJO4D5N.js.map +1 -0
- package/dist/chunk-NDMDSPFX.js +69 -0
- package/dist/chunk-NDMDSPFX.js.map +1 -0
- package/dist/chunk-OOIG3RUM.js +276 -0
- package/dist/chunk-OOIG3RUM.js.map +1 -0
- package/dist/{chunk-6CXTPC74.js → chunk-OXQNQW5J.js} +27 -6
- package/dist/chunk-OXQNQW5J.js.map +1 -0
- package/dist/chunk-PMYWZRRS.js +62 -0
- package/dist/chunk-PMYWZRRS.js.map +1 -0
- package/dist/chunk-PXHNTC6D.js +382 -0
- package/dist/chunk-PXHNTC6D.js.map +1 -0
- package/dist/{chunk-VXT6LUGP.js → chunk-S6CYDZEW.js} +1395 -1975
- package/dist/chunk-S6CYDZEW.js.map +1 -0
- package/dist/chunk-SZLF66FJ.js +843 -0
- package/dist/chunk-SZLF66FJ.js.map +1 -0
- package/dist/chunk-VXGV6NTD.js +476 -0
- package/dist/chunk-VXGV6NTD.js.map +1 -0
- package/dist/{chunk-JSNHZCIV.js → chunk-WLETRGAC.js} +16 -9
- package/dist/chunk-WLETRGAC.js.map +1 -0
- package/dist/{chunk-2EVQKZTO.js → chunk-YDAT7LD2.js} +190 -13
- package/dist/chunk-YDAT7LD2.js.map +1 -0
- package/dist/cli.js +3451 -509
- package/dist/cli.js.map +1 -1
- package/dist/execution-env-3BN7Z737.js +3 -0
- package/dist/execution-env-3BN7Z737.js.map +1 -0
- package/dist/federation-EPZDUWCK.js +6 -0
- package/dist/federation-EPZDUWCK.js.map +1 -0
- package/dist/git-ref-HPRLUFFW.js +3 -0
- package/dist/git-ref-HPRLUFFW.js.map +1 -0
- package/dist/graph-backend-ZMSYOZMW.js +10 -0
- package/dist/graph-backend-ZMSYOZMW.js.map +1 -0
- package/dist/index.d.ts +432 -126
- package/dist/index.js +14 -7
- package/dist/index.js.map +1 -1
- package/dist/{interactive-736PBC7J.js → interactive-ICL7D7LO.js} +185 -365
- package/dist/interactive-ICL7D7LO.js.map +1 -0
- package/dist/llm-host-2RAYQSDQ.js +8 -0
- package/dist/llm-host-2RAYQSDQ.js.map +1 -0
- package/dist/load-OHVKDX4R.js +7 -0
- package/dist/load-OHVKDX4R.js.map +1 -0
- package/dist/local-runtime-OJFLX2HI.js +4 -0
- package/dist/local-runtime-OJFLX2HI.js.map +1 -0
- package/dist/model-execution-profile-MQ2QCP24.js +3 -0
- package/dist/model-execution-profile-MQ2QCP24.js.map +1 -0
- package/dist/model-orchestrator-FFTZI3U2.js +8 -0
- package/dist/model-orchestrator-FFTZI3U2.js.map +1 -0
- package/dist/parse-worker.d.ts +1 -1
- package/dist/parse-worker.js +1 -1
- package/dist/paths-MCH75O3G.js +5 -0
- package/dist/paths-MCH75O3G.js.map +1 -0
- package/dist/resolve-gguf-4UPIGPEE.js +7 -0
- package/dist/resolve-gguf-4UPIGPEE.js.map +1 -0
- package/dist/runtime-session-IDU5BDUJ.js +17 -0
- package/dist/runtime-session-IDU5BDUJ.js.map +1 -0
- package/dist/session-L2OSDRPI.js +10 -0
- package/dist/{session-POJUDFPH.js.map → session-L2OSDRPI.js.map} +1 -1
- package/dist/{stream-json-Q2JGWPHB.js → stream-json-F74W5FCK.js} +16 -6
- package/dist/stream-json-F74W5FCK.js.map +1 -0
- package/dist/{types-Jl6RJ175.d.ts → types-ZMg1mSuu.d.ts} +3 -1
- package/dist/vgd-EGBQD7BW.js +16 -0
- package/dist/vgd-EGBQD7BW.js.map +1 -0
- package/package.json +9 -6
- package/dist/baseline-J44M7MWP.js +0 -7
- package/dist/chunk-2EVQKZTO.js.map +0 -1
- package/dist/chunk-6CXTPC74.js.map +0 -1
- package/dist/chunk-CS37OBE3.js.map +0 -1
- package/dist/chunk-GGJZA3Q6.js +0 -961
- package/dist/chunk-GGJZA3Q6.js.map +0 -1
- package/dist/chunk-JBXNQCGE.js +0 -484
- package/dist/chunk-JBXNQCGE.js.map +0 -1
- package/dist/chunk-JSNHZCIV.js.map +0 -1
- package/dist/chunk-PY3DNX5H.js.map +0 -1
- package/dist/chunk-VXT6LUGP.js.map +0 -1
- package/dist/chunk-WNIIKCNF.js.map +0 -1
- package/dist/chunk-XKXKWYJF.js.map +0 -1
- package/dist/interactive-736PBC7J.js.map +0 -1
- package/dist/session-POJUDFPH.js +0 -6
- package/dist/stream-json-Q2JGWPHB.js.map +0 -1
|
@@ -0,0 +1,843 @@
|
|
|
1
|
+
import { vgdSocketPath, vgdPidPath } from './chunk-PMYWZRRS.js';
|
|
2
|
+
import { listHostSessions, hostSessionPoolSize, acquireHostSession, evictIdleSessions, clearHostSessionPool, dropHostSession, generateOnSession } from './chunk-K7REHDQS.js';
|
|
3
|
+
import { resolveOne } from './chunk-J7LKEB2R.js';
|
|
4
|
+
import { impactOf, queryGraph } from './chunk-6IOQJGFT.js';
|
|
5
|
+
import { loadGraph } from './chunk-HABUT7HW.js';
|
|
6
|
+
import { parseGraph } from './chunk-5Q54GEHM.js';
|
|
7
|
+
import { repositoryIdFromRoot, globalGraphPathForRef, globalGraphPath } from './chunk-BWPBB45M.js';
|
|
8
|
+
import { activeGraphSlotKey, detectGitRef } from './chunk-BPF3YX2U.js';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
import * as fs from 'fs';
|
|
11
|
+
import * as net from 'net';
|
|
12
|
+
|
|
13
|
+
// src/runtime/vgd/protocol.ts
|
|
14
|
+
var VGD_PROTOCOL_VERSION = "vgd/0";
|
|
15
|
+
function parseRequest(line) {
|
|
16
|
+
let raw;
|
|
17
|
+
try {
|
|
18
|
+
raw = JSON.parse(line);
|
|
19
|
+
} catch {
|
|
20
|
+
return { error: "invalid JSON" };
|
|
21
|
+
}
|
|
22
|
+
if (!raw || typeof raw !== "object") return { error: "request must be an object" };
|
|
23
|
+
const op = raw.op;
|
|
24
|
+
if (op === "ping" || op === "status" || op === "list") return { op };
|
|
25
|
+
if (op === "list-graph-slots") {
|
|
26
|
+
const repositoryId = raw.repositoryId;
|
|
27
|
+
return {
|
|
28
|
+
op: "list-graph-slots",
|
|
29
|
+
repositoryId: typeof repositoryId === "string" && repositoryId.trim() ? repositoryId.trim() : void 0
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
if (op === "select-git-ref") {
|
|
33
|
+
const repositoryId = raw.repositoryId;
|
|
34
|
+
const gitRef = raw.gitRef;
|
|
35
|
+
if (typeof repositoryId !== "string" || !repositoryId.trim()) return { error: "select-git-ref requires repositoryId" };
|
|
36
|
+
if (typeof gitRef !== "string" || !gitRef.trim()) return { error: "select-git-ref requires gitRef" };
|
|
37
|
+
return { op: "select-git-ref", repositoryId: repositoryId.trim(), gitRef: gitRef.trim() };
|
|
38
|
+
}
|
|
39
|
+
if (op === "put-graph") {
|
|
40
|
+
const repositoryId = raw.repositoryId;
|
|
41
|
+
const gitRef = raw.gitRef;
|
|
42
|
+
const graph = raw.graph;
|
|
43
|
+
if (typeof repositoryId !== "string" || !repositoryId.trim()) return { error: "put-graph requires repositoryId" };
|
|
44
|
+
if (typeof gitRef !== "string" || !gitRef.trim()) return { error: "put-graph requires gitRef" };
|
|
45
|
+
if (!graph || typeof graph !== "object") return { error: "put-graph requires graph object" };
|
|
46
|
+
return { op: "put-graph", repositoryId: repositoryId.trim(), gitRef: gitRef.trim(), graph };
|
|
47
|
+
}
|
|
48
|
+
if (op === "query-graph") {
|
|
49
|
+
const repositoryId = raw.repositoryId;
|
|
50
|
+
const query = raw.query;
|
|
51
|
+
const limit = raw.limit;
|
|
52
|
+
const gitRef = raw.gitRef;
|
|
53
|
+
if (typeof repositoryId !== "string" || !repositoryId.trim()) return { error: "query-graph requires repositoryId" };
|
|
54
|
+
if (typeof query !== "string" || !query.trim()) return { error: "query-graph requires query" };
|
|
55
|
+
return {
|
|
56
|
+
op: "query-graph",
|
|
57
|
+
repositoryId: repositoryId.trim(),
|
|
58
|
+
query: query.trim(),
|
|
59
|
+
limit: typeof limit === "number" && limit > 0 ? Math.min(Math.floor(limit), 50) : void 0,
|
|
60
|
+
gitRef: typeof gitRef === "string" && gitRef.trim() ? gitRef.trim() : void 0
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
if (op === "impact-of") {
|
|
64
|
+
const repositoryId = raw.repositoryId;
|
|
65
|
+
const symbol = raw.symbol;
|
|
66
|
+
const depth = raw.depth;
|
|
67
|
+
const gitRef = raw.gitRef;
|
|
68
|
+
if (typeof repositoryId !== "string" || !repositoryId.trim()) return { error: "impact-of requires repositoryId" };
|
|
69
|
+
if (typeof symbol !== "string" || !symbol.trim()) return { error: "impact-of requires symbol" };
|
|
70
|
+
return {
|
|
71
|
+
op: "impact-of",
|
|
72
|
+
repositoryId: repositoryId.trim(),
|
|
73
|
+
symbol: symbol.trim(),
|
|
74
|
+
depth: typeof depth === "number" && depth > 0 ? Math.min(Math.floor(depth), 12) : void 0,
|
|
75
|
+
gitRef: typeof gitRef === "string" && gitRef.trim() ? gitRef.trim() : void 0
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
if (op === "graph-summary") {
|
|
79
|
+
const repositoryId = raw.repositoryId;
|
|
80
|
+
const gitRef = raw.gitRef;
|
|
81
|
+
if (typeof repositoryId !== "string" || !repositoryId.trim()) return { error: "graph-summary requires repositoryId" };
|
|
82
|
+
return {
|
|
83
|
+
op: "graph-summary",
|
|
84
|
+
repositoryId: repositoryId.trim(),
|
|
85
|
+
gitRef: typeof gitRef === "string" && gitRef.trim() ? gitRef.trim() : void 0
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (op === "register" || op === "unregister") {
|
|
89
|
+
const root = raw.root;
|
|
90
|
+
if (typeof root !== "string" || !root.trim()) return { error: `${op} requires a non-empty root` };
|
|
91
|
+
const label = raw.label;
|
|
92
|
+
const role = raw.role;
|
|
93
|
+
if (op === "register") {
|
|
94
|
+
return {
|
|
95
|
+
op: "register",
|
|
96
|
+
root: root.trim(),
|
|
97
|
+
label: typeof label === "string" ? label : void 0,
|
|
98
|
+
role: role === "primary" || role === "member" ? role : void 0
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
return { op: "unregister", root: root.trim() };
|
|
102
|
+
}
|
|
103
|
+
if (op === "register-federation") {
|
|
104
|
+
const primaryRoot = raw.primaryRoot;
|
|
105
|
+
const members = raw.members;
|
|
106
|
+
if (typeof primaryRoot !== "string" || !primaryRoot.trim()) return { error: "register-federation requires primaryRoot" };
|
|
107
|
+
if (!Array.isArray(members) || members.length === 0) return { error: "register-federation requires members[]" };
|
|
108
|
+
const parsed = [];
|
|
109
|
+
for (const m of members) {
|
|
110
|
+
if (!m || typeof m !== "object") continue;
|
|
111
|
+
const root = m.root;
|
|
112
|
+
if (typeof root !== "string" || !root.trim()) continue;
|
|
113
|
+
const label = m.label;
|
|
114
|
+
const role = m.role;
|
|
115
|
+
parsed.push({
|
|
116
|
+
root: root.trim(),
|
|
117
|
+
label: typeof label === "string" ? label : void 0,
|
|
118
|
+
role: role === "primary" || role === "member" ? role : void 0
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
if (!parsed.length) return { error: "register-federation members must include at least one root" };
|
|
122
|
+
return { op: "register-federation", primaryRoot: primaryRoot.trim(), members: parsed };
|
|
123
|
+
}
|
|
124
|
+
if (op === "host-status") return { op: "host-status" };
|
|
125
|
+
if (op === "host-load") {
|
|
126
|
+
const modelPath = raw.modelPath;
|
|
127
|
+
if (typeof modelPath !== "string" || !modelPath.trim()) return { error: "host-load requires modelPath" };
|
|
128
|
+
return { op: "host-load", modelPath: modelPath.trim() };
|
|
129
|
+
}
|
|
130
|
+
if (op === "host-unload") {
|
|
131
|
+
const modelPath = raw.modelPath;
|
|
132
|
+
return {
|
|
133
|
+
op: "host-unload",
|
|
134
|
+
modelPath: typeof modelPath === "string" && modelPath.trim() ? modelPath.trim() : void 0
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
if (op === "host-generate") {
|
|
138
|
+
const modelPath = raw.modelPath;
|
|
139
|
+
const messages = raw.messages;
|
|
140
|
+
if (typeof modelPath !== "string" || !modelPath.trim()) return { error: "host-generate requires modelPath" };
|
|
141
|
+
if (!Array.isArray(messages) || messages.length === 0) return { error: "host-generate requires messages[]" };
|
|
142
|
+
const parsed = [];
|
|
143
|
+
for (const m of messages) {
|
|
144
|
+
if (!m || typeof m !== "object") continue;
|
|
145
|
+
const role = m.role;
|
|
146
|
+
const content = m.content;
|
|
147
|
+
if (typeof role !== "string" || typeof content !== "string") continue;
|
|
148
|
+
parsed.push({ role, content });
|
|
149
|
+
}
|
|
150
|
+
if (!parsed.length) return { error: "host-generate messages must include role+content" };
|
|
151
|
+
const grammar = raw.grammar;
|
|
152
|
+
const requireGrammar = raw.requireGrammar;
|
|
153
|
+
const maxTokens = raw.maxTokens;
|
|
154
|
+
const temperature = raw.temperature;
|
|
155
|
+
return {
|
|
156
|
+
op: "host-generate",
|
|
157
|
+
modelPath: modelPath.trim(),
|
|
158
|
+
messages: parsed,
|
|
159
|
+
grammar: typeof grammar === "string" ? grammar : void 0,
|
|
160
|
+
requireGrammar: requireGrammar === true,
|
|
161
|
+
maxTokens: typeof maxTokens === "number" ? maxTokens : void 0,
|
|
162
|
+
temperature: typeof temperature === "number" ? temperature : void 0
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
return { error: `unknown op: ${String(op)}` };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// src/runtime/active-graph-cache.ts
|
|
169
|
+
var DEFAULT_ACTIVE_GRAPH_IDLE_MS = 20 * 60 * 1e3;
|
|
170
|
+
var ActiveGraphCache = class {
|
|
171
|
+
slots = /* @__PURE__ */ new Map();
|
|
172
|
+
/** repositoryId → currently selected gitRef */
|
|
173
|
+
currentRef = /* @__PURE__ */ new Map();
|
|
174
|
+
idleTimeoutMs;
|
|
175
|
+
maxPerRepo;
|
|
176
|
+
maxTotal;
|
|
177
|
+
now;
|
|
178
|
+
constructor(options = {}) {
|
|
179
|
+
this.idleTimeoutMs = options.idleTimeoutMs ?? DEFAULT_ACTIVE_GRAPH_IDLE_MS;
|
|
180
|
+
this.maxPerRepo = options.maxPerRepo ?? 8;
|
|
181
|
+
this.maxTotal = options.maxTotal ?? 32;
|
|
182
|
+
this.now = options.now ?? (() => Date.now());
|
|
183
|
+
}
|
|
184
|
+
size() {
|
|
185
|
+
return this.slots.size;
|
|
186
|
+
}
|
|
187
|
+
get(repositoryId, gitRef) {
|
|
188
|
+
const slot = this.slots.get(activeGraphSlotKey(repositoryId, gitRef));
|
|
189
|
+
if (slot) {
|
|
190
|
+
slot.lastAccessAt = this.now();
|
|
191
|
+
}
|
|
192
|
+
return slot;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Insert or replace a graph for (repositoryId, gitRef). Touches access time.
|
|
196
|
+
* Runs eviction if over cap.
|
|
197
|
+
*/
|
|
198
|
+
put(repositoryId, gitRef, graph) {
|
|
199
|
+
const key = activeGraphSlotKey(repositoryId, gitRef);
|
|
200
|
+
const t = this.now();
|
|
201
|
+
const existing = this.slots.get(key);
|
|
202
|
+
const slot = {
|
|
203
|
+
repositoryId,
|
|
204
|
+
gitRef,
|
|
205
|
+
graph,
|
|
206
|
+
loadedAt: existing?.loadedAt ?? t,
|
|
207
|
+
lastAccessAt: t,
|
|
208
|
+
nodeCount: graph.nodes?.length ?? 0
|
|
209
|
+
};
|
|
210
|
+
this.slots.set(key, slot);
|
|
211
|
+
if (!this.currentRef.has(repositoryId)) {
|
|
212
|
+
this.currentRef.set(repositoryId, gitRef);
|
|
213
|
+
}
|
|
214
|
+
this.evictIfNeeded();
|
|
215
|
+
return slot;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Mark gitRef as the current branch for this repository and touch it.
|
|
219
|
+
* Returns the slot if present.
|
|
220
|
+
*/
|
|
221
|
+
select(repositoryId, gitRef) {
|
|
222
|
+
this.currentRef.set(repositoryId, gitRef);
|
|
223
|
+
return this.get(repositoryId, gitRef);
|
|
224
|
+
}
|
|
225
|
+
/** Currently selected gitRef for a repository, if any. */
|
|
226
|
+
selectedRef(repositoryId) {
|
|
227
|
+
return this.currentRef.get(repositoryId);
|
|
228
|
+
}
|
|
229
|
+
current(repositoryId) {
|
|
230
|
+
const ref = this.currentRef.get(repositoryId);
|
|
231
|
+
if (!ref) return void 0;
|
|
232
|
+
return this.get(repositoryId, ref);
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Drop idle slots past the timeout (LILO among idle).
|
|
236
|
+
* Returns keys of removed slots.
|
|
237
|
+
*/
|
|
238
|
+
evictIdle(now = this.now()) {
|
|
239
|
+
return this.evict(now, false);
|
|
240
|
+
}
|
|
241
|
+
/** Force eviction of idle slots when over caps (timeout may be ignored for overflow). */
|
|
242
|
+
evictIfNeeded() {
|
|
243
|
+
return this.evict(this.now(), true);
|
|
244
|
+
}
|
|
245
|
+
list(repositoryId) {
|
|
246
|
+
const t = this.now();
|
|
247
|
+
const out = [];
|
|
248
|
+
for (const slot of this.slots.values()) {
|
|
249
|
+
if (repositoryId && slot.repositoryId !== repositoryId) continue;
|
|
250
|
+
const current = this.currentRef.get(slot.repositoryId) === slot.gitRef;
|
|
251
|
+
const idleMs = t - slot.lastAccessAt;
|
|
252
|
+
out.push({
|
|
253
|
+
repositoryId: slot.repositoryId,
|
|
254
|
+
gitRef: slot.gitRef,
|
|
255
|
+
loadedAt: slot.loadedAt,
|
|
256
|
+
lastAccessAt: slot.lastAccessAt,
|
|
257
|
+
nodeCount: slot.nodeCount,
|
|
258
|
+
current,
|
|
259
|
+
idleMs,
|
|
260
|
+
evictable: !current && idleMs >= this.idleTimeoutMs
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
return out.sort(
|
|
264
|
+
(a, b) => a.repositoryId.localeCompare(b.repositoryId) || a.gitRef.localeCompare(b.gitRef)
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
clear() {
|
|
268
|
+
this.slots.clear();
|
|
269
|
+
this.currentRef.clear();
|
|
270
|
+
}
|
|
271
|
+
evict(now, forceOverflow) {
|
|
272
|
+
const removed = [];
|
|
273
|
+
const removeKey = (key) => {
|
|
274
|
+
const slot = this.slots.get(key);
|
|
275
|
+
if (!slot) return;
|
|
276
|
+
this.slots.delete(key);
|
|
277
|
+
removed.push(key);
|
|
278
|
+
};
|
|
279
|
+
const idle = this.idleCandidates(now, this.idleTimeoutMs);
|
|
280
|
+
idle.sort((a, b) => a.loadedAt - b.loadedAt);
|
|
281
|
+
for (const slot of idle) {
|
|
282
|
+
removeKey(activeGraphSlotKey(slot.repositoryId, slot.gitRef));
|
|
283
|
+
}
|
|
284
|
+
if (forceOverflow) {
|
|
285
|
+
while (this.slots.size > this.maxTotal) {
|
|
286
|
+
const victim = this.pickLiloVictim(
|
|
287
|
+
now,
|
|
288
|
+
/* requireIdleTimeout */
|
|
289
|
+
false
|
|
290
|
+
);
|
|
291
|
+
if (!victim) break;
|
|
292
|
+
removeKey(activeGraphSlotKey(victim.repositoryId, victim.gitRef));
|
|
293
|
+
}
|
|
294
|
+
const byRepo = /* @__PURE__ */ new Map();
|
|
295
|
+
for (const s of this.slots.values()) {
|
|
296
|
+
const list = byRepo.get(s.repositoryId) ?? [];
|
|
297
|
+
list.push(s);
|
|
298
|
+
byRepo.set(s.repositoryId, list);
|
|
299
|
+
}
|
|
300
|
+
for (const [repoId, list] of byRepo) {
|
|
301
|
+
if (list.length <= this.maxPerRepo) continue;
|
|
302
|
+
const nonCurrent = list.filter((s) => this.currentRef.get(repoId) !== s.gitRef).sort((a, b) => a.loadedAt - b.loadedAt);
|
|
303
|
+
let excess = list.length - this.maxPerRepo;
|
|
304
|
+
for (const s of nonCurrent) {
|
|
305
|
+
if (excess <= 0) break;
|
|
306
|
+
removeKey(activeGraphSlotKey(s.repositoryId, s.gitRef));
|
|
307
|
+
excess--;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return removed;
|
|
312
|
+
}
|
|
313
|
+
idleCandidates(now, timeoutMs) {
|
|
314
|
+
const out = [];
|
|
315
|
+
for (const slot of this.slots.values()) {
|
|
316
|
+
if (this.currentRef.get(slot.repositoryId) === slot.gitRef) continue;
|
|
317
|
+
if (now - slot.lastAccessAt >= timeoutMs) out.push(slot);
|
|
318
|
+
}
|
|
319
|
+
return out;
|
|
320
|
+
}
|
|
321
|
+
pickLiloVictim(now, requireIdleTimeout) {
|
|
322
|
+
let best = null;
|
|
323
|
+
for (const slot of this.slots.values()) {
|
|
324
|
+
if (this.currentRef.get(slot.repositoryId) === slot.gitRef) continue;
|
|
325
|
+
if (requireIdleTimeout && now - slot.lastAccessAt < this.idleTimeoutMs) continue;
|
|
326
|
+
if (!best || slot.loadedAt < best.loadedAt) best = slot;
|
|
327
|
+
}
|
|
328
|
+
return best;
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
// src/runtime/vgd/registry.ts
|
|
333
|
+
var WorkspaceRegistry = class {
|
|
334
|
+
byId = /* @__PURE__ */ new Map();
|
|
335
|
+
/** Multi-branch in-memory graphs (shared across registered workspaces). */
|
|
336
|
+
graphs = new ActiveGraphCache();
|
|
337
|
+
register(root, now = () => /* @__PURE__ */ new Date(), meta = {}) {
|
|
338
|
+
const resolved = path.resolve(root);
|
|
339
|
+
const id = repositoryIdFromRoot(resolved);
|
|
340
|
+
const git = meta.gitRef ? { ref: meta.gitRef, kind: "branch" } : detectGitRef(resolved);
|
|
341
|
+
const graphPath = git.kind !== "none" && git.ref ? globalGraphPathForRef(resolved, git.ref) : globalGraphPath(resolved);
|
|
342
|
+
const record = {
|
|
343
|
+
id,
|
|
344
|
+
root: resolved,
|
|
345
|
+
graphPath,
|
|
346
|
+
registeredAt: now().toISOString(),
|
|
347
|
+
label: meta.label,
|
|
348
|
+
role: meta.role,
|
|
349
|
+
gitRef: git.ref || void 0
|
|
350
|
+
};
|
|
351
|
+
this.byId.set(id, record);
|
|
352
|
+
if (git.ref) this.graphs.select(id, git.ref);
|
|
353
|
+
return record;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Activate or insert an in-memory graph for a workspace branch.
|
|
357
|
+
* Call after load/build; runs LILO idle eviction as needed.
|
|
358
|
+
*/
|
|
359
|
+
putGraph(repositoryId, gitRef, graph) {
|
|
360
|
+
this.graphs.put(repositoryId, gitRef, graph);
|
|
361
|
+
this.graphs.select(repositoryId, gitRef);
|
|
362
|
+
}
|
|
363
|
+
/** Switch the current branch slot for a repository (no load). */
|
|
364
|
+
selectGitRef(repositoryId, gitRef) {
|
|
365
|
+
this.graphs.select(repositoryId, gitRef);
|
|
366
|
+
}
|
|
367
|
+
/** Register every member of a federation (primary + members). */
|
|
368
|
+
registerFederation(members, now = () => /* @__PURE__ */ new Date()) {
|
|
369
|
+
return members.map((m) => this.register(m.root, now, { label: m.label, role: m.role }));
|
|
370
|
+
}
|
|
371
|
+
unregister(root) {
|
|
372
|
+
const id = repositoryIdFromRoot(path.resolve(root));
|
|
373
|
+
return this.byId.delete(id);
|
|
374
|
+
}
|
|
375
|
+
list() {
|
|
376
|
+
return [...this.byId.values()].sort((a, b) => a.root.localeCompare(b.root));
|
|
377
|
+
}
|
|
378
|
+
get(root) {
|
|
379
|
+
return this.byId.get(repositoryIdFromRoot(path.resolve(root)));
|
|
380
|
+
}
|
|
381
|
+
/** Lookup by stable repository id. */
|
|
382
|
+
getById(repositoryId) {
|
|
383
|
+
return this.byId.get(repositoryId);
|
|
384
|
+
}
|
|
385
|
+
size() {
|
|
386
|
+
return this.byId.size;
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Resolve an ActiveGraph for query/impact: prefer the named gitRef, else the
|
|
390
|
+
* selected current slot, else any warm slot for the repository.
|
|
391
|
+
*/
|
|
392
|
+
resolveGraph(repositoryId, gitRef) {
|
|
393
|
+
if (gitRef) {
|
|
394
|
+
const slot = this.graphs.get(repositoryId, gitRef);
|
|
395
|
+
if (slot) return { graph: slot.graph, gitRef: slot.gitRef };
|
|
396
|
+
}
|
|
397
|
+
const current = this.graphs.current(repositoryId);
|
|
398
|
+
if (current) return { graph: current.graph, gitRef: current.gitRef };
|
|
399
|
+
const any = this.graphs.list(repositoryId).slice().sort((a, b) => a.loadedAt - b.loadedAt)[0];
|
|
400
|
+
if (any) {
|
|
401
|
+
const slot = this.graphs.get(repositoryId, any.gitRef);
|
|
402
|
+
if (slot) return { graph: slot.graph, gitRef: slot.gitRef };
|
|
403
|
+
}
|
|
404
|
+
return null;
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
// src/runtime/vgd/host-broker.ts
|
|
409
|
+
var VgdHostBroker = class {
|
|
410
|
+
binding = null;
|
|
411
|
+
loaded = /* @__PURE__ */ new Set();
|
|
412
|
+
/** modelPath → set of client ids holding a lease. */
|
|
413
|
+
leases = /* @__PURE__ */ new Map();
|
|
414
|
+
setBinding(lib) {
|
|
415
|
+
this.binding = lib;
|
|
416
|
+
}
|
|
417
|
+
hasBinding() {
|
|
418
|
+
return this.binding != null;
|
|
419
|
+
}
|
|
420
|
+
status() {
|
|
421
|
+
const leases = {};
|
|
422
|
+
for (const [path3, clients] of this.leases) {
|
|
423
|
+
leases[path3] = clients.size;
|
|
424
|
+
}
|
|
425
|
+
return {
|
|
426
|
+
poolSize: hostSessionPoolSize(),
|
|
427
|
+
loadedModels: [...this.loaded],
|
|
428
|
+
bindingReady: this.binding != null,
|
|
429
|
+
leases,
|
|
430
|
+
sessions: listHostSessions()
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Load weights and optionally register a client lease (P4 multi-client).
|
|
435
|
+
* When clientId is omitted, behaves like a one-shot load (no lease bookkeeping).
|
|
436
|
+
*/
|
|
437
|
+
async load(modelPath, clientId) {
|
|
438
|
+
if (!this.binding) {
|
|
439
|
+
return { ok: false, error: "host binding not configured \u2014 start vgd with llama runtime or attach binding" };
|
|
440
|
+
}
|
|
441
|
+
try {
|
|
442
|
+
await acquireHostSession(this.binding, modelPath);
|
|
443
|
+
this.loaded.add(modelPath);
|
|
444
|
+
if (clientId) {
|
|
445
|
+
let set = this.leases.get(modelPath);
|
|
446
|
+
if (!set) {
|
|
447
|
+
set = /* @__PURE__ */ new Set();
|
|
448
|
+
this.leases.set(modelPath, set);
|
|
449
|
+
}
|
|
450
|
+
set.add(clientId);
|
|
451
|
+
}
|
|
452
|
+
evictIdleSessions(4);
|
|
453
|
+
return { ok: true, modelPath, leases: this.leases.get(modelPath)?.size ?? 0 };
|
|
454
|
+
} catch (e) {
|
|
455
|
+
return { ok: false, error: e instanceof Error ? e.message : String(e) };
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Release a client lease. When the last lease is released, drop the warm
|
|
460
|
+
* session for that path. Without clientId, clears all leases for the path
|
|
461
|
+
* (or entire pool if modelPath omitted).
|
|
462
|
+
*/
|
|
463
|
+
async unload(modelPath, clientId) {
|
|
464
|
+
if (!modelPath) {
|
|
465
|
+
const n = hostSessionPoolSize();
|
|
466
|
+
clearHostSessionPool();
|
|
467
|
+
this.loaded.clear();
|
|
468
|
+
this.leases.clear();
|
|
469
|
+
return { ok: true, cleared: n, remainingLeases: 0 };
|
|
470
|
+
}
|
|
471
|
+
if (clientId) {
|
|
472
|
+
const set = this.leases.get(modelPath);
|
|
473
|
+
if (set) {
|
|
474
|
+
set.delete(clientId);
|
|
475
|
+
if (set.size === 0) this.leases.delete(modelPath);
|
|
476
|
+
}
|
|
477
|
+
const remaining = this.leases.get(modelPath)?.size ?? 0;
|
|
478
|
+
if (remaining === 0) {
|
|
479
|
+
const dropped2 = dropHostSession(modelPath) ? 1 : 0;
|
|
480
|
+
this.loaded.delete(modelPath);
|
|
481
|
+
return { ok: true, cleared: dropped2, remainingLeases: 0 };
|
|
482
|
+
}
|
|
483
|
+
return { ok: true, cleared: 0, remainingLeases: remaining };
|
|
484
|
+
}
|
|
485
|
+
this.leases.delete(modelPath);
|
|
486
|
+
const dropped = dropHostSession(modelPath) ? 1 : 0;
|
|
487
|
+
this.loaded.delete(modelPath);
|
|
488
|
+
return { ok: true, cleared: dropped, remainingLeases: 0 };
|
|
489
|
+
}
|
|
490
|
+
/** How many clients hold a lease on modelPath. */
|
|
491
|
+
leaseCount(modelPath) {
|
|
492
|
+
return this.leases.get(modelPath)?.size ?? 0;
|
|
493
|
+
}
|
|
494
|
+
async generate(modelPath, messages, opts) {
|
|
495
|
+
if (!this.binding) {
|
|
496
|
+
return { ok: false, error: "host binding not configured" };
|
|
497
|
+
}
|
|
498
|
+
try {
|
|
499
|
+
const session = await acquireHostSession(this.binding, modelPath);
|
|
500
|
+
this.loaded.add(modelPath);
|
|
501
|
+
const result = await generateOnSession(session, messages, {
|
|
502
|
+
grammar: opts?.grammar,
|
|
503
|
+
requireGrammar: opts?.requireGrammar,
|
|
504
|
+
identifierTrie: opts?.identifierTrie,
|
|
505
|
+
annotateUnknownIdentifiers: opts?.annotateUnknownIdentifiers,
|
|
506
|
+
draftCandidates: opts?.draftCandidates,
|
|
507
|
+
maxTokens: opts?.maxTokens,
|
|
508
|
+
temperature: opts?.temperature,
|
|
509
|
+
promptSegments: opts?.promptSegments
|
|
510
|
+
});
|
|
511
|
+
return { ok: true, result };
|
|
512
|
+
} catch (e) {
|
|
513
|
+
return { ok: false, error: e instanceof Error ? e.message : String(e) };
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
var defaultBroker = null;
|
|
518
|
+
function getVgdHostBroker() {
|
|
519
|
+
if (!defaultBroker) defaultBroker = new VgdHostBroker();
|
|
520
|
+
return defaultBroker;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// src/runtime/vgd/server.ts
|
|
524
|
+
async function startVgdServer(options = {}) {
|
|
525
|
+
const socketPath = options.socketPath ?? vgdSocketPath();
|
|
526
|
+
const pidPath = options.pidPath ?? vgdPidPath();
|
|
527
|
+
const registry = options.registry ?? new WorkspaceRegistry();
|
|
528
|
+
const hostBroker = options.hostBroker ?? getVgdHostBroker();
|
|
529
|
+
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
530
|
+
const pid = options.pid ?? process.pid;
|
|
531
|
+
const startedAt = Date.now();
|
|
532
|
+
if (!socketPath.startsWith("\\\\.\\pipe")) {
|
|
533
|
+
fs.mkdirSync(path.dirname(socketPath), { recursive: true });
|
|
534
|
+
try {
|
|
535
|
+
fs.unlinkSync(socketPath);
|
|
536
|
+
} catch {
|
|
537
|
+
}
|
|
538
|
+
} else {
|
|
539
|
+
fs.mkdirSync(path.dirname(pidPath), { recursive: true });
|
|
540
|
+
}
|
|
541
|
+
const server = net.createServer((socket) => {
|
|
542
|
+
let buffer = "";
|
|
543
|
+
socket.setEncoding("utf8");
|
|
544
|
+
socket.on("data", (chunk) => {
|
|
545
|
+
buffer += chunk;
|
|
546
|
+
let nl;
|
|
547
|
+
while ((nl = buffer.indexOf("\n")) >= 0) {
|
|
548
|
+
const line = buffer.slice(0, nl).trim();
|
|
549
|
+
buffer = buffer.slice(nl + 1);
|
|
550
|
+
if (!line) continue;
|
|
551
|
+
void Promise.resolve(handleLine(line, { registry, hostBroker, now, pid, startedAt, socketPath })).then(
|
|
552
|
+
(response) => {
|
|
553
|
+
socket.write(JSON.stringify(response) + "\n");
|
|
554
|
+
}
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
});
|
|
558
|
+
});
|
|
559
|
+
await new Promise((resolve2, reject) => {
|
|
560
|
+
server.once("error", reject);
|
|
561
|
+
server.listen(socketPath, () => {
|
|
562
|
+
server.off("error", reject);
|
|
563
|
+
resolve2();
|
|
564
|
+
});
|
|
565
|
+
});
|
|
566
|
+
try {
|
|
567
|
+
fs.mkdirSync(path.dirname(pidPath), { recursive: true });
|
|
568
|
+
fs.writeFileSync(pidPath, `${pid}
|
|
569
|
+
`, "utf8");
|
|
570
|
+
} catch {
|
|
571
|
+
}
|
|
572
|
+
return {
|
|
573
|
+
socketPath,
|
|
574
|
+
registry,
|
|
575
|
+
startedAt,
|
|
576
|
+
async close() {
|
|
577
|
+
await new Promise((resolve2) => server.close(() => resolve2()));
|
|
578
|
+
try {
|
|
579
|
+
fs.unlinkSync(pidPath);
|
|
580
|
+
} catch {
|
|
581
|
+
}
|
|
582
|
+
if (!socketPath.startsWith("\\\\.\\pipe")) {
|
|
583
|
+
try {
|
|
584
|
+
fs.unlinkSync(socketPath);
|
|
585
|
+
} catch {
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
async function handleLine(line, ctx) {
|
|
592
|
+
const req = parseRequest(line);
|
|
593
|
+
if ("error" in req) return { ok: false, error: req.error, code: "bad_request" };
|
|
594
|
+
switch (req.op) {
|
|
595
|
+
case "ping":
|
|
596
|
+
return { ok: true, pong: true, version: VGD_PROTOCOL_VERSION };
|
|
597
|
+
case "status":
|
|
598
|
+
return {
|
|
599
|
+
ok: true,
|
|
600
|
+
pid: ctx.pid,
|
|
601
|
+
uptimeMs: Date.now() - ctx.startedAt,
|
|
602
|
+
workspaces: ctx.registry.size(),
|
|
603
|
+
graphSlots: ctx.registry.graphs.size(),
|
|
604
|
+
version: VGD_PROTOCOL_VERSION,
|
|
605
|
+
socketPath: ctx.socketPath
|
|
606
|
+
};
|
|
607
|
+
case "host-status":
|
|
608
|
+
return { ok: true, host: ctx.hostBroker.status() };
|
|
609
|
+
case "host-load": {
|
|
610
|
+
const r = await ctx.hostBroker.load(req.modelPath);
|
|
611
|
+
if (!r.ok) return { ok: false, error: r.error, code: "host_error" };
|
|
612
|
+
return { ok: true, hostLoaded: true, modelPath: r.modelPath };
|
|
613
|
+
}
|
|
614
|
+
case "host-unload": {
|
|
615
|
+
const r = await ctx.hostBroker.unload(req.modelPath);
|
|
616
|
+
return { ok: true, hostUnloaded: true, cleared: r.cleared };
|
|
617
|
+
}
|
|
618
|
+
case "host-generate": {
|
|
619
|
+
const r = await ctx.hostBroker.generate(
|
|
620
|
+
req.modelPath,
|
|
621
|
+
req.messages.map((m) => ({
|
|
622
|
+
role: m.role === "system" || m.role === "assistant" ? m.role : "user",
|
|
623
|
+
content: m.content
|
|
624
|
+
})),
|
|
625
|
+
{
|
|
626
|
+
grammar: req.grammar,
|
|
627
|
+
requireGrammar: req.requireGrammar,
|
|
628
|
+
maxTokens: req.maxTokens,
|
|
629
|
+
temperature: req.temperature
|
|
630
|
+
}
|
|
631
|
+
);
|
|
632
|
+
if (!r.ok) return { ok: false, error: r.error, code: "host_error" };
|
|
633
|
+
return {
|
|
634
|
+
ok: true,
|
|
635
|
+
hostGenerated: true,
|
|
636
|
+
text: r.result.text,
|
|
637
|
+
model: r.result.model,
|
|
638
|
+
constrained: r.result.constrained,
|
|
639
|
+
grammarApplied: r.result.grammarApplied,
|
|
640
|
+
draftAcceptedChars: r.result.draftAcceptedChars,
|
|
641
|
+
latencyMs: r.result.latencyMs,
|
|
642
|
+
unknownIdentifiers: r.result.unknownIdentifiers
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
case "list":
|
|
646
|
+
return { ok: true, workspaces: ctx.registry.list() };
|
|
647
|
+
case "register":
|
|
648
|
+
return {
|
|
649
|
+
ok: true,
|
|
650
|
+
workspace: ctx.registry.register(req.root, ctx.now, { label: req.label, role: req.role })
|
|
651
|
+
};
|
|
652
|
+
case "unregister":
|
|
653
|
+
return { ok: true, removed: ctx.registry.unregister(req.root) };
|
|
654
|
+
case "register-federation": {
|
|
655
|
+
const workspaces = ctx.registry.registerFederation(req.members, ctx.now);
|
|
656
|
+
return { ok: true, workspaces, federation: true };
|
|
657
|
+
}
|
|
658
|
+
case "list-graph-slots": {
|
|
659
|
+
ctx.registry.graphs.evictIdle();
|
|
660
|
+
return { ok: true, slots: ctx.registry.graphs.list(req.repositoryId) };
|
|
661
|
+
}
|
|
662
|
+
case "select-git-ref": {
|
|
663
|
+
ctx.registry.selectGitRef(req.repositoryId, req.gitRef);
|
|
664
|
+
return { ok: true, selected: true, repositoryId: req.repositoryId, gitRef: req.gitRef };
|
|
665
|
+
}
|
|
666
|
+
case "put-graph": {
|
|
667
|
+
try {
|
|
668
|
+
const graph = parseGraph(JSON.stringify(req.graph));
|
|
669
|
+
ctx.registry.putGraph(req.repositoryId, req.gitRef, graph);
|
|
670
|
+
return {
|
|
671
|
+
ok: true,
|
|
672
|
+
stored: true,
|
|
673
|
+
repositoryId: req.repositoryId,
|
|
674
|
+
gitRef: req.gitRef,
|
|
675
|
+
nodeCount: graph.nodes?.length ?? 0
|
|
676
|
+
};
|
|
677
|
+
} catch (e) {
|
|
678
|
+
return { ok: false, error: e instanceof Error ? e.message : "invalid graph", code: "bad_graph" };
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
case "query-graph": {
|
|
682
|
+
const resolved = ctx.registry.resolveGraph(req.repositoryId, req.gitRef);
|
|
683
|
+
if (!resolved) {
|
|
684
|
+
return { ok: false, error: "no ActiveGraph loaded for repository \u2014 put-graph first", code: "no_graph" };
|
|
685
|
+
}
|
|
686
|
+
const result = queryGraph(resolved.graph, req.query, { limit: req.limit ?? 12, budget: 2e3 });
|
|
687
|
+
return {
|
|
688
|
+
ok: true,
|
|
689
|
+
query: req.query,
|
|
690
|
+
repositoryId: req.repositoryId,
|
|
691
|
+
gitRef: resolved.gitRef,
|
|
692
|
+
matches: result.matches.slice(0, req.limit ?? 12).map((m) => ({
|
|
693
|
+
id: m.node.id,
|
|
694
|
+
qualifiedName: m.node.qualifiedName,
|
|
695
|
+
kind: m.node.kind,
|
|
696
|
+
file: m.node.file,
|
|
697
|
+
line: m.node.span?.start ?? 0,
|
|
698
|
+
score: m.score,
|
|
699
|
+
why: m.why
|
|
700
|
+
})),
|
|
701
|
+
tokensEstimate: result.tokensEstimate
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
case "impact-of": {
|
|
705
|
+
const resolved = ctx.registry.resolveGraph(req.repositoryId, req.gitRef);
|
|
706
|
+
if (!resolved) {
|
|
707
|
+
return { ok: false, error: "no ActiveGraph loaded for repository \u2014 put-graph first", code: "no_graph" };
|
|
708
|
+
}
|
|
709
|
+
const node = resolveSymbol(resolved.graph, req.symbol);
|
|
710
|
+
if (!node) {
|
|
711
|
+
return { ok: false, error: `symbol not found: ${req.symbol}`, code: "not_found" };
|
|
712
|
+
}
|
|
713
|
+
const impact = impactOf(resolved.graph, node.id, { depth: req.depth ?? 4 });
|
|
714
|
+
return {
|
|
715
|
+
ok: true,
|
|
716
|
+
repositoryId: req.repositoryId,
|
|
717
|
+
gitRef: resolved.gitRef,
|
|
718
|
+
symbol: req.symbol,
|
|
719
|
+
root: impact.root,
|
|
720
|
+
depth: impact.depth,
|
|
721
|
+
affected: impact.affected.slice(0, 40).map((a) => ({
|
|
722
|
+
id: a.id,
|
|
723
|
+
name: a.name,
|
|
724
|
+
kind: a.kind,
|
|
725
|
+
file: a.file,
|
|
726
|
+
line: a.line,
|
|
727
|
+
depth: a.depth,
|
|
728
|
+
confidence: a.confidence
|
|
729
|
+
})),
|
|
730
|
+
direct: impact.direct,
|
|
731
|
+
transitive: impact.transitive
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
case "graph-summary": {
|
|
735
|
+
const resolved = ctx.registry.resolveGraph(req.repositoryId, req.gitRef);
|
|
736
|
+
if (!resolved) {
|
|
737
|
+
return { ok: false, error: "no ActiveGraph loaded for repository \u2014 put-graph first", code: "no_graph" };
|
|
738
|
+
}
|
|
739
|
+
const g = resolved.graph;
|
|
740
|
+
return {
|
|
741
|
+
ok: true,
|
|
742
|
+
repositoryId: req.repositoryId,
|
|
743
|
+
gitRef: resolved.gitRef,
|
|
744
|
+
summary: {
|
|
745
|
+
nodeCount: g.nodes?.length ?? 0,
|
|
746
|
+
edgeCount: g.edges?.length ?? 0,
|
|
747
|
+
languages: g.meta?.languages ?? [],
|
|
748
|
+
corpusHash: g.provenance?.corpusHash ?? null,
|
|
749
|
+
root: g.meta?.root ?? null
|
|
750
|
+
}
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
function resolveSymbol(graph, symbol) {
|
|
756
|
+
if (graph.nodes.some((n) => n.id === symbol)) return { id: symbol };
|
|
757
|
+
const hit = resolveOne(graph, symbol);
|
|
758
|
+
if (hit.node?.id) return { id: hit.node.id };
|
|
759
|
+
if (hit.candidates.length === 1 && hit.candidates[0]?.id) return { id: hit.candidates[0].id };
|
|
760
|
+
const q = symbol.toLowerCase();
|
|
761
|
+
const byQn = graph.nodes.find((n) => n.qualifiedName?.toLowerCase() === q || n.name?.toLowerCase() === q);
|
|
762
|
+
return byQn ? { id: byQn.id } : null;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// src/runtime/branch-graph-session.ts
|
|
766
|
+
var BranchGraphSession = class {
|
|
767
|
+
root;
|
|
768
|
+
repositoryId;
|
|
769
|
+
cache;
|
|
770
|
+
graphPath;
|
|
771
|
+
detectGit;
|
|
772
|
+
load;
|
|
773
|
+
gitRun;
|
|
774
|
+
lastRef = "";
|
|
775
|
+
constructor(options) {
|
|
776
|
+
this.root = options.root;
|
|
777
|
+
this.repositoryId = repositoryIdFromRoot(options.root);
|
|
778
|
+
this.cache = options.cache ?? new ActiveGraphCache();
|
|
779
|
+
this.graphPath = options.graphPath;
|
|
780
|
+
this.detectGit = options.detectGit ?? detectGitRef;
|
|
781
|
+
this.load = options.load ?? loadGraph;
|
|
782
|
+
this.gitRun = options.gitRun;
|
|
783
|
+
}
|
|
784
|
+
/**
|
|
785
|
+
* Resolve HEAD, activate the matching cache slot (or load from disk), and
|
|
786
|
+
* run idle eviction. Returns null graph when no map exists for this ref.
|
|
787
|
+
*/
|
|
788
|
+
ensureCurrent() {
|
|
789
|
+
this.cache.evictIdle();
|
|
790
|
+
const info = this.gitRun ? this.detectGit(this.root, this.gitRun) : this.detectGit(this.root);
|
|
791
|
+
const gitRef = info.ref || "unknown";
|
|
792
|
+
const switched = this.lastRef !== "" && this.lastRef !== gitRef;
|
|
793
|
+
this.lastRef = gitRef;
|
|
794
|
+
if (this.graphPath) {
|
|
795
|
+
const graph2 = this.load(this.root, this.graphPath);
|
|
796
|
+
if (graph2) {
|
|
797
|
+
this.cache.put(this.repositoryId, gitRef, graph2);
|
|
798
|
+
this.cache.select(this.repositoryId, gitRef);
|
|
799
|
+
}
|
|
800
|
+
return {
|
|
801
|
+
graph: graph2,
|
|
802
|
+
gitRef,
|
|
803
|
+
kind: info.kind,
|
|
804
|
+
switched,
|
|
805
|
+
fromCache: false,
|
|
806
|
+
repositoryId: this.repositoryId
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
const warm = this.cache.get(this.repositoryId, gitRef);
|
|
810
|
+
if (warm) {
|
|
811
|
+
this.cache.select(this.repositoryId, gitRef);
|
|
812
|
+
return {
|
|
813
|
+
graph: warm.graph,
|
|
814
|
+
gitRef,
|
|
815
|
+
kind: info.kind,
|
|
816
|
+
switched,
|
|
817
|
+
fromCache: true,
|
|
818
|
+
repositoryId: this.repositoryId
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
const graph = this.load(this.root);
|
|
822
|
+
if (graph) {
|
|
823
|
+
this.cache.put(this.repositoryId, gitRef, graph);
|
|
824
|
+
this.cache.select(this.repositoryId, gitRef);
|
|
825
|
+
}
|
|
826
|
+
return {
|
|
827
|
+
graph,
|
|
828
|
+
gitRef,
|
|
829
|
+
kind: info.kind,
|
|
830
|
+
switched,
|
|
831
|
+
fromCache: false,
|
|
832
|
+
repositoryId: this.repositoryId
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
/** Diagnostics for daemon status / tests. */
|
|
836
|
+
listSlots() {
|
|
837
|
+
return this.cache.list(this.repositoryId);
|
|
838
|
+
}
|
|
839
|
+
};
|
|
840
|
+
|
|
841
|
+
export { ActiveGraphCache, BranchGraphSession, DEFAULT_ACTIVE_GRAPH_IDLE_MS, VGD_PROTOCOL_VERSION, WorkspaceRegistry, parseRequest, startVgdServer };
|
|
842
|
+
//# sourceMappingURL=chunk-SZLF66FJ.js.map
|
|
843
|
+
//# sourceMappingURL=chunk-SZLF66FJ.js.map
|