@vibgrate/cli 2026.803.3 → 2026.803.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/agent-WYBWUBCC.js +26 -0
- package/dist/{agent-JTA4YWMQ.js.map → agent-WYBWUBCC.js.map} +1 -1
- package/dist/baseline-3HECX4Q6.js +9 -0
- package/dist/{baseline-2V7PKA7Q.js.map → baseline-3HECX4Q6.js.map} +1 -1
- package/dist/{chunk-TNADGCOX.js → chunk-23UPIFRU.js} +8 -8
- package/dist/{chunk-TNADGCOX.js.map → chunk-23UPIFRU.js.map} +1 -1
- package/dist/chunk-2A7XCANX.js +375 -0
- package/dist/chunk-2A7XCANX.js.map +1 -0
- package/dist/{chunk-QMME6TB4.js → chunk-4LGDQBYK.js} +3 -102
- package/dist/chunk-4LGDQBYK.js.map +1 -0
- package/dist/{chunk-L5PUNO3T.js → chunk-4ZMJKR74.js} +451 -22
- package/dist/chunk-4ZMJKR74.js.map +1 -0
- package/dist/{chunk-CPLG4GFB.js → chunk-537JBQVB.js} +4 -4
- package/dist/{chunk-CPLG4GFB.js.map → chunk-537JBQVB.js.map} +1 -1
- package/dist/{chunk-NUZM7UOR.js → chunk-5TS7N7J2.js} +59 -9
- package/dist/chunk-5TS7N7J2.js.map +1 -0
- package/dist/{chunk-XK5LKOBF.js → chunk-5WMFX7TY.js} +4 -4
- package/dist/{chunk-XK5LKOBF.js.map → chunk-5WMFX7TY.js.map} +1 -1
- package/dist/{chunk-M452MRWI.js → chunk-66B4ZQA3.js} +9 -8
- package/dist/chunk-66B4ZQA3.js.map +1 -0
- package/dist/{chunk-WGPS3NZQ.js → chunk-7BU7CWXC.js} +33 -11
- package/dist/chunk-7BU7CWXC.js.map +1 -0
- package/dist/{chunk-3Q3ZXJZP.js → chunk-7TOIESUT.js} +5 -4
- package/dist/chunk-7TOIESUT.js.map +1 -0
- package/dist/{chunk-4ACXDVON.js → chunk-BRUMBQV7.js} +4 -4
- package/dist/{chunk-4ACXDVON.js.map → chunk-BRUMBQV7.js.map} +1 -1
- package/dist/{chunk-M7773OUN.js → chunk-BUCNKYTW.js} +5 -5
- package/dist/{chunk-M7773OUN.js.map → chunk-BUCNKYTW.js.map} +1 -1
- package/dist/{chunk-NSWEJPLV.js → chunk-FAA7RBIG.js} +9 -9
- package/dist/{chunk-NSWEJPLV.js.map → chunk-FAA7RBIG.js.map} +1 -1
- package/dist/chunk-FZ5Y4KEI.js +6 -0
- package/dist/{chunk-7KNFG44U.js.map → chunk-FZ5Y4KEI.js.map} +1 -1
- package/dist/{chunk-UXR7FBE3.js → chunk-HBQC4XQJ.js} +6 -6
- package/dist/{chunk-UXR7FBE3.js.map → chunk-HBQC4XQJ.js.map} +1 -1
- package/dist/{chunk-KYX2YCXT.js → chunk-HVHCXM2E.js} +9 -9
- package/dist/{chunk-KYX2YCXT.js.map → chunk-HVHCXM2E.js.map} +1 -1
- package/dist/{chunk-NJFQCDGA.js → chunk-INIULQEP.js} +3 -3
- package/dist/{chunk-NJFQCDGA.js.map → chunk-INIULQEP.js.map} +1 -1
- package/dist/{chunk-K6Q6HTFD.js → chunk-JZGVUH57.js} +4 -4
- package/dist/{chunk-K6Q6HTFD.js.map → chunk-JZGVUH57.js.map} +1 -1
- package/dist/{chunk-TM5XQY3L.js → chunk-KFK7KCAH.js} +3 -3
- package/dist/{chunk-TM5XQY3L.js.map → chunk-KFK7KCAH.js.map} +1 -1
- package/dist/{chunk-6DFV4WCQ.js → chunk-OVDL76LB.js} +3 -3
- package/dist/{chunk-6DFV4WCQ.js.map → chunk-OVDL76LB.js.map} +1 -1
- package/dist/{chunk-SJFBXCII.js → chunk-QUI3E3TQ.js} +54 -9
- package/dist/chunk-QUI3E3TQ.js.map +1 -0
- package/dist/chunk-RBMKBYGV.js +102 -0
- package/dist/chunk-RBMKBYGV.js.map +1 -0
- package/dist/{chunk-HC44772H.js → chunk-TVRM57CE.js} +5 -373
- package/dist/chunk-TVRM57CE.js.map +1 -0
- package/dist/{chunk-PXM3VZYR.js → chunk-UWVFLVZX.js} +4 -4
- package/dist/{chunk-PXM3VZYR.js.map → chunk-UWVFLVZX.js.map} +1 -1
- package/dist/{chunk-6JP7HGIF.js → chunk-Y4JO6LYP.js} +3 -3
- package/dist/{chunk-6JP7HGIF.js.map → chunk-Y4JO6LYP.js.map} +1 -1
- package/dist/cli.js +489 -337
- package/dist/cli.js.map +1 -1
- package/dist/embed-worker-main.d.ts +2 -0
- package/dist/embed-worker-main.js +64 -0
- package/dist/embed-worker-main.js.map +1 -0
- package/dist/ensure-map-DZ53QXRT.js +31 -0
- package/dist/{ensure-map-TAVYMPEG.js.map → ensure-map-DZ53QXRT.js.map} +1 -1
- package/dist/graph-backend-3A2M22QN.js +13 -0
- package/dist/{graph-backend-WF4LGBGD.js.map → graph-backend-3A2M22QN.js.map} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +16 -14
- package/dist/index.js.map +1 -1
- package/dist/{interactive-6PN237LX.js → interactive-DB7IC3BL.js} +31 -29
- package/dist/interactive-DB7IC3BL.js.map +1 -0
- package/dist/load-6KVGUQMU.js +11 -0
- package/dist/{load-T2FOJUPI.js.map → load-6KVGUQMU.js.map} +1 -1
- package/dist/{run-outcome-AMIKAASL.js → run-outcome-YOYRC32U.js} +7 -6
- package/dist/run-outcome-YOYRC32U.js.map +1 -0
- package/dist/runtime-session-XEP7IPGP.js +22 -0
- package/dist/{runtime-session-QAVOPSSH.js.map → runtime-session-XEP7IPGP.js.map} +1 -1
- package/dist/session-5WKEACIF.js +17 -0
- package/dist/{session-SCHQQTHN.js.map → session-5WKEACIF.js.map} +1 -1
- package/dist/{stream-json-A6K2MONY.js → stream-json-3OJCPBWY.js} +13 -11
- package/dist/stream-json-3OJCPBWY.js.map +1 -0
- package/dist/{vg-mcp-bridge-3TSFOL4V.js → vg-mcp-bridge-PM3C4FQL.js} +12 -10
- package/dist/vg-mcp-bridge-PM3C4FQL.js.map +1 -0
- package/dist/{vgd-B67JCNJP.js → vgd-UTLMMPIZ.js} +12 -10
- package/dist/vgd-UTLMMPIZ.js.map +1 -0
- package/package.json +1 -1
- package/scripts/postinstall.mjs +22 -1
- package/dist/agent-JTA4YWMQ.js +0 -24
- package/dist/baseline-2V7PKA7Q.js +0 -9
- package/dist/chunk-3Q3ZXJZP.js.map +0 -1
- package/dist/chunk-7KNFG44U.js +0 -6
- package/dist/chunk-HC44772H.js.map +0 -1
- package/dist/chunk-L5PUNO3T.js.map +0 -1
- package/dist/chunk-M452MRWI.js.map +0 -1
- package/dist/chunk-NUZM7UOR.js.map +0 -1
- package/dist/chunk-QMME6TB4.js.map +0 -1
- package/dist/chunk-SJFBXCII.js.map +0 -1
- package/dist/chunk-WGPS3NZQ.js.map +0 -1
- package/dist/ensure-map-TAVYMPEG.js +0 -29
- package/dist/graph-backend-WF4LGBGD.js +0 -11
- package/dist/interactive-6PN237LX.js.map +0 -1
- package/dist/load-T2FOJUPI.js +0 -10
- package/dist/run-outcome-AMIKAASL.js.map +0 -1
- package/dist/runtime-session-QAVOPSSH.js +0 -20
- package/dist/session-SCHQQTHN.js +0 -15
- package/dist/stream-json-A6K2MONY.js.map +0 -1
- package/dist/vg-mcp-bridge-3TSFOL4V.js.map +0 -1
- package/dist/vgd-B67JCNJP.js.map +0 -1
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import { listHostSessions, hostSessionPoolSize, acquireHostSession, evictIdleSessions, clearHostSessionPool, dropHostSession, generateOnSession } from './chunk-3LOWJSAF.js';
|
|
2
1
|
import { vgdSocketPath, vgdPidPath } from './chunk-RBIRXQR6.js';
|
|
2
|
+
import { listHostSessions, hostSessionPoolSize, acquireHostSession, evictIdleSessions, clearHostSessionPool, dropHostSession, generateOnSession } from './chunk-3LOWJSAF.js';
|
|
3
3
|
import { resolveOne } from './chunk-J7LKEB2R.js';
|
|
4
|
-
import { impactOf, queryGraph } from './chunk-
|
|
5
|
-
import { loadGraph } from './chunk-
|
|
4
|
+
import { impactOf, queryGraph } from './chunk-TVRM57CE.js';
|
|
5
|
+
import { loadGraph } from './chunk-537JBQVB.js';
|
|
6
6
|
import { repositoryIdFromRoot, globalGraphPathForRef, globalGraphPath } from './chunk-BWPBB45M.js';
|
|
7
7
|
import { activeGraphSlotKey, detectGitRef } from './chunk-BPF3YX2U.js';
|
|
8
8
|
import * as path from 'path';
|
|
9
|
-
import * as
|
|
9
|
+
import * as fs2 from 'fs';
|
|
10
10
|
import * as net from 'net';
|
|
11
|
+
import { spawn } from 'child_process';
|
|
12
|
+
import { fileURLToPath } from 'url';
|
|
11
13
|
|
|
12
14
|
// src/runtime/vgd/protocol.ts
|
|
13
15
|
var VGD_PROTOCOL_VERSION = "vgd/0";
|
|
@@ -56,6 +58,28 @@ function parseRequest(line) {
|
|
|
56
58
|
graphPath: typeof graphPath === "string" && graphPath.trim() ? graphPath.trim() : void 0
|
|
57
59
|
};
|
|
58
60
|
}
|
|
61
|
+
if (op === "embed-status") {
|
|
62
|
+
const repositoryId = raw.repositoryId;
|
|
63
|
+
return {
|
|
64
|
+
op: "embed-status",
|
|
65
|
+
repositoryId: typeof repositoryId === "string" && repositoryId.trim() ? repositoryId.trim() : void 0
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
if (op === "embed-query") {
|
|
69
|
+
const text = raw.text;
|
|
70
|
+
if (typeof text !== "string" || !text.trim()) return { error: "embed-query requires text" };
|
|
71
|
+
return { op: "embed-query", text: text.trim().slice(0, 8e3) };
|
|
72
|
+
}
|
|
73
|
+
if (op === "embed-index") {
|
|
74
|
+
const repositoryId = raw.repositoryId;
|
|
75
|
+
const gitRef = raw.gitRef;
|
|
76
|
+
if (typeof repositoryId !== "string" || !repositoryId.trim()) return { error: "embed-index requires repositoryId" };
|
|
77
|
+
return {
|
|
78
|
+
op: "embed-index",
|
|
79
|
+
repositoryId: repositoryId.trim(),
|
|
80
|
+
gitRef: typeof gitRef === "string" && gitRef.trim() ? gitRef.trim() : void 0
|
|
81
|
+
};
|
|
82
|
+
}
|
|
59
83
|
if (op === "query-graph") {
|
|
60
84
|
const repositoryId = raw.repositoryId;
|
|
61
85
|
const query = raw.query;
|
|
@@ -186,7 +210,9 @@ var ActiveGraphCache = class {
|
|
|
186
210
|
maxPerRepo;
|
|
187
211
|
maxTotal;
|
|
188
212
|
now;
|
|
213
|
+
onEvict;
|
|
189
214
|
constructor(options = {}) {
|
|
215
|
+
this.onEvict = options.onEvict;
|
|
190
216
|
this.idleTimeoutMs = options.idleTimeoutMs ?? DEFAULT_ACTIVE_GRAPH_IDLE_MS;
|
|
191
217
|
this.maxPerRepo = options.maxPerRepo ?? 8;
|
|
192
218
|
this.maxTotal = options.maxTotal ?? 32;
|
|
@@ -276,9 +302,17 @@ var ActiveGraphCache = class {
|
|
|
276
302
|
);
|
|
277
303
|
}
|
|
278
304
|
clear() {
|
|
305
|
+
for (const slot of this.slots.values()) this.notifyEvict(slot);
|
|
279
306
|
this.slots.clear();
|
|
280
307
|
this.currentRef.clear();
|
|
281
308
|
}
|
|
309
|
+
notifyEvict(slot) {
|
|
310
|
+
if (!this.onEvict) return;
|
|
311
|
+
try {
|
|
312
|
+
this.onEvict({ repositoryId: slot.repositoryId, gitRef: slot.gitRef });
|
|
313
|
+
} catch {
|
|
314
|
+
}
|
|
315
|
+
}
|
|
282
316
|
evict(now, forceOverflow) {
|
|
283
317
|
const removed = [];
|
|
284
318
|
const removeKey = (key) => {
|
|
@@ -286,6 +320,7 @@ var ActiveGraphCache = class {
|
|
|
286
320
|
if (!slot) return;
|
|
287
321
|
this.slots.delete(key);
|
|
288
322
|
removed.push(key);
|
|
323
|
+
this.notifyEvict(slot);
|
|
289
324
|
};
|
|
290
325
|
const idle = this.idleCandidates(now, this.idleTimeoutMs);
|
|
291
326
|
idle.sort((a, b) => a.loadedAt - b.loadedAt);
|
|
@@ -343,8 +378,21 @@ var ActiveGraphCache = class {
|
|
|
343
378
|
// src/runtime/vgd/registry.ts
|
|
344
379
|
var WorkspaceRegistry = class {
|
|
345
380
|
byId = /* @__PURE__ */ new Map();
|
|
381
|
+
/**
|
|
382
|
+
* Anything keyed by the same (repositoryId, gitRef) slot as the graph — the
|
|
383
|
+
* semantic index — attaches here. Wiring it through the registry rather than
|
|
384
|
+
* the cache directly means every put/select/evict passes one funnel, so an
|
|
385
|
+
* index can never describe a ref its graph no longer holds.
|
|
386
|
+
*/
|
|
387
|
+
slotListener;
|
|
346
388
|
/** Multi-branch in-memory graphs (shared across registered workspaces). */
|
|
347
|
-
graphs = new ActiveGraphCache(
|
|
389
|
+
graphs = new ActiveGraphCache({
|
|
390
|
+
onEvict: (slot) => this.slotListener?.onGraphEvict(slot.repositoryId, slot.gitRef)
|
|
391
|
+
});
|
|
392
|
+
/** Attach the semantic index (or any slot-scoped resource) to graph lifecycle. */
|
|
393
|
+
setSlotListener(listener) {
|
|
394
|
+
this.slotListener = listener;
|
|
395
|
+
}
|
|
348
396
|
register(root, now = () => /* @__PURE__ */ new Date(), meta = {}) {
|
|
349
397
|
const resolved = path.resolve(root);
|
|
350
398
|
const id = repositoryIdFromRoot(resolved);
|
|
@@ -370,10 +418,13 @@ var WorkspaceRegistry = class {
|
|
|
370
418
|
putGraph(repositoryId, gitRef, graph) {
|
|
371
419
|
this.graphs.put(repositoryId, gitRef, graph);
|
|
372
420
|
this.graphs.select(repositoryId, gitRef);
|
|
421
|
+
this.slotListener?.onGraphPut(repositoryId, gitRef, graph);
|
|
422
|
+
this.slotListener?.onGraphSelect(repositoryId, gitRef);
|
|
373
423
|
}
|
|
374
424
|
/** Switch the current branch slot for a repository (no load). */
|
|
375
425
|
selectGitRef(repositoryId, gitRef) {
|
|
376
426
|
this.graphs.select(repositoryId, gitRef);
|
|
427
|
+
this.slotListener?.onGraphSelect(repositoryId, gitRef);
|
|
377
428
|
}
|
|
378
429
|
/** Register every member of a federation (primary + members). */
|
|
379
430
|
registerFederation(members, now = () => /* @__PURE__ */ new Date()) {
|
|
@@ -430,8 +481,8 @@ var VgdHostBroker = class {
|
|
|
430
481
|
}
|
|
431
482
|
status() {
|
|
432
483
|
const leases = {};
|
|
433
|
-
for (const [
|
|
434
|
-
leases[
|
|
484
|
+
for (const [path4, clients] of this.leases) {
|
|
485
|
+
leases[path4] = clients.size;
|
|
435
486
|
}
|
|
436
487
|
return {
|
|
437
488
|
poolSize: hostSessionPoolSize(),
|
|
@@ -530,6 +581,338 @@ function getVgdHostBroker() {
|
|
|
530
581
|
if (!defaultBroker) defaultBroker = new VgdHostBroker();
|
|
531
582
|
return defaultBroker;
|
|
532
583
|
}
|
|
584
|
+
function nodeText(node) {
|
|
585
|
+
const parts = [node.qualifiedName || node.name, node.kind, node.file, node.doc].filter((p) => typeof p === "string" && p.length > 0);
|
|
586
|
+
return parts.join(" \xB7 ").slice(0, 512);
|
|
587
|
+
}
|
|
588
|
+
var EmbedBroker = class {
|
|
589
|
+
child = null;
|
|
590
|
+
starting = false;
|
|
591
|
+
gaveUp = false;
|
|
592
|
+
crashes = 0;
|
|
593
|
+
reason;
|
|
594
|
+
model;
|
|
595
|
+
nextId = 1;
|
|
596
|
+
buffer = "";
|
|
597
|
+
pending = /* @__PURE__ */ new Map();
|
|
598
|
+
indexes = /* @__PURE__ */ new Map();
|
|
599
|
+
/** Slot keys with a build in flight, so warms are not duplicated. */
|
|
600
|
+
building = /* @__PURE__ */ new Set();
|
|
601
|
+
/** The slot most recently made current — logged on change. */
|
|
602
|
+
currentKey;
|
|
603
|
+
graphProvider;
|
|
604
|
+
log;
|
|
605
|
+
spawnWorker;
|
|
606
|
+
maxCrashes;
|
|
607
|
+
requestTimeoutMs;
|
|
608
|
+
batchSize;
|
|
609
|
+
now;
|
|
610
|
+
constructor(options = {}) {
|
|
611
|
+
this.log = options.log ?? (() => {
|
|
612
|
+
});
|
|
613
|
+
this.spawnWorker = options.spawnWorker ?? defaultSpawnWorker;
|
|
614
|
+
this.maxCrashes = options.maxCrashes ?? 3;
|
|
615
|
+
this.requestTimeoutMs = options.requestTimeoutMs ?? 12e4;
|
|
616
|
+
this.batchSize = options.batchSize ?? 256;
|
|
617
|
+
this.now = options.now ?? (() => Date.now());
|
|
618
|
+
}
|
|
619
|
+
status() {
|
|
620
|
+
return {
|
|
621
|
+
worker: this.gaveUp ? "unavailable" : this.child ? "ready" : this.starting ? "starting" : "stopped",
|
|
622
|
+
pid: this.child?.pid,
|
|
623
|
+
model: this.model,
|
|
624
|
+
crashes: this.crashes,
|
|
625
|
+
reason: this.reason,
|
|
626
|
+
slots: this.listSlots()
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
listSlots(repositoryId) {
|
|
630
|
+
const out = [];
|
|
631
|
+
for (const idx of this.indexes.values()) {
|
|
632
|
+
if (repositoryId && idx.repositoryId !== repositoryId) continue;
|
|
633
|
+
out.push({
|
|
634
|
+
repositoryId: idx.repositoryId,
|
|
635
|
+
gitRef: idx.gitRef,
|
|
636
|
+
state: idx.state,
|
|
637
|
+
vectors: idx.vectors.size,
|
|
638
|
+
nodeCount: idx.nodeCount,
|
|
639
|
+
builtAt: idx.builtAt,
|
|
640
|
+
buildMs: idx.buildMs,
|
|
641
|
+
error: idx.error
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
return out.sort((a, b) => a.repositoryId.localeCompare(b.repositoryId) || a.gitRef.localeCompare(b.gitRef));
|
|
645
|
+
}
|
|
646
|
+
slot(repositoryId, gitRef) {
|
|
647
|
+
return this.indexes.get(activeGraphSlotKey(repositoryId, gitRef));
|
|
648
|
+
}
|
|
649
|
+
/** True once the broker has stopped trying — callers should go lexical. */
|
|
650
|
+
unavailable() {
|
|
651
|
+
return this.gaveUp;
|
|
652
|
+
}
|
|
653
|
+
/**
|
|
654
|
+
* A graph slot was published or replaced. The vectors for that slot no
|
|
655
|
+
* longer describe its graph, so they are dropped and a rebuild is queued.
|
|
656
|
+
* Called from the same funnel that stores the graph, so the two can never
|
|
657
|
+
* disagree about which ref they belong to.
|
|
658
|
+
*/
|
|
659
|
+
onGraphPut(repositoryId, gitRef, graph) {
|
|
660
|
+
const key = activeGraphSlotKey(repositoryId, gitRef);
|
|
661
|
+
const existing = this.indexes.get(key);
|
|
662
|
+
const nodeCount = graph.nodes?.length ?? 0;
|
|
663
|
+
if (existing && existing.state === "ready") {
|
|
664
|
+
this.log(`semantic: ${repositoryId}@${gitRef} graph republished \u2014 index stale (${existing.vectors.size} vectors dropped)`);
|
|
665
|
+
}
|
|
666
|
+
this.indexes.set(key, {
|
|
667
|
+
repositoryId,
|
|
668
|
+
gitRef,
|
|
669
|
+
state: "stale",
|
|
670
|
+
vectors: /* @__PURE__ */ new Map(),
|
|
671
|
+
nodeCount
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Supplies the graph for a slot so a select can warm without the caller
|
|
676
|
+
* threading it through. Set by the daemon to read its own cache.
|
|
677
|
+
*/
|
|
678
|
+
setGraphProvider(provider) {
|
|
679
|
+
this.graphProvider = provider;
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* A slot became current — a branch switch, a new repo, a fresh publish.
|
|
683
|
+
* The matching index is swapped in, and warmed in the background if it is
|
|
684
|
+
* not ready, so the first Ask after a switch is not the one that pays.
|
|
685
|
+
*/
|
|
686
|
+
onGraphSelect(repositoryId, gitRef) {
|
|
687
|
+
const key = activeGraphSlotKey(repositoryId, gitRef);
|
|
688
|
+
if (this.currentKey !== key) {
|
|
689
|
+
this.currentKey = key;
|
|
690
|
+
const idx2 = this.indexes.get(key);
|
|
691
|
+
this.log(
|
|
692
|
+
`semantic: current slot \u2192 ${repositoryId}@${gitRef} \xB7 index ${idx2 ? `${idx2.state} (${idx2.vectors.size} vectors)` : "absent"}`
|
|
693
|
+
);
|
|
694
|
+
}
|
|
695
|
+
if (this.gaveUp) return;
|
|
696
|
+
const idx = this.indexes.get(key);
|
|
697
|
+
if (idx?.state === "ready" || this.building.has(key)) return;
|
|
698
|
+
const graph = this.graphProvider?.(repositoryId, gitRef);
|
|
699
|
+
if (!graph) return;
|
|
700
|
+
void this.ensureIndex(repositoryId, gitRef, graph);
|
|
701
|
+
}
|
|
702
|
+
/** A graph slot was evicted — its index goes with it, never outliving it. */
|
|
703
|
+
onGraphEvict(repositoryId, gitRef) {
|
|
704
|
+
const key = activeGraphSlotKey(repositoryId, gitRef);
|
|
705
|
+
const idx = this.indexes.get(key);
|
|
706
|
+
if (!idx) return;
|
|
707
|
+
this.indexes.delete(key);
|
|
708
|
+
this.log(`semantic: ${repositoryId}@${gitRef} index evicted with its graph slot (${idx.vectors.size} vectors freed)`);
|
|
709
|
+
}
|
|
710
|
+
/** Drop everything (daemon shutdown / registry clear). */
|
|
711
|
+
clear() {
|
|
712
|
+
this.indexes.clear();
|
|
713
|
+
this.building.clear();
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* Ensure the slot has vectors, building them if needed. Resolves when the
|
|
717
|
+
* index is ready or has definitively failed; never throws.
|
|
718
|
+
*/
|
|
719
|
+
async ensureIndex(repositoryId, gitRef, graph) {
|
|
720
|
+
const key = activeGraphSlotKey(repositoryId, gitRef);
|
|
721
|
+
let idx = this.indexes.get(key);
|
|
722
|
+
if (!idx) {
|
|
723
|
+
idx = { repositoryId, gitRef, state: "stale", vectors: /* @__PURE__ */ new Map(), nodeCount: graph.nodes?.length ?? 0 };
|
|
724
|
+
this.indexes.set(key, idx);
|
|
725
|
+
}
|
|
726
|
+
if (idx.state === "ready") return idx;
|
|
727
|
+
if (this.gaveUp) {
|
|
728
|
+
idx.state = "failed";
|
|
729
|
+
idx.error = this.reason ?? "embedding worker unavailable";
|
|
730
|
+
return idx;
|
|
731
|
+
}
|
|
732
|
+
if (this.building.has(key)) return idx;
|
|
733
|
+
this.building.add(key);
|
|
734
|
+
idx.state = "building";
|
|
735
|
+
const nodes = (graph.nodes ?? []).filter((n) => !!n?.id);
|
|
736
|
+
const started = this.now();
|
|
737
|
+
this.log(`semantic: building index for ${repositoryId}@${gitRef} (${nodes.length} nodes)\u2026`);
|
|
738
|
+
try {
|
|
739
|
+
const vectors = /* @__PURE__ */ new Map();
|
|
740
|
+
for (let i = 0; i < nodes.length; i += this.batchSize) {
|
|
741
|
+
const batch = nodes.slice(i, i + this.batchSize);
|
|
742
|
+
const out = await this.embed(batch.map((n) => nodeText(n)));
|
|
743
|
+
batch.forEach((n, j) => {
|
|
744
|
+
const v = out[j];
|
|
745
|
+
if (v) vectors.set(n.id, v);
|
|
746
|
+
});
|
|
747
|
+
const live = this.indexes.get(key);
|
|
748
|
+
if (!live || live !== idx) {
|
|
749
|
+
this.log(`semantic: index build for ${repositoryId}@${gitRef} abandoned \u2014 slot changed underneath it`);
|
|
750
|
+
return idx;
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
idx.vectors = vectors;
|
|
754
|
+
idx.nodeCount = nodes.length;
|
|
755
|
+
idx.state = "ready";
|
|
756
|
+
idx.builtAt = this.now();
|
|
757
|
+
idx.buildMs = idx.builtAt - started;
|
|
758
|
+
idx.error = void 0;
|
|
759
|
+
this.log(`semantic: index ready for ${repositoryId}@${gitRef} \u2014 ${vectors.size} vectors in ${idx.buildMs}ms`);
|
|
760
|
+
} catch (err) {
|
|
761
|
+
idx.state = "failed";
|
|
762
|
+
idx.error = err instanceof Error ? err.message : String(err);
|
|
763
|
+
this.log(`semantic: index build failed for ${repositoryId}@${gitRef} \u2014 ${idx.error}; callers fall back to lexical`);
|
|
764
|
+
} finally {
|
|
765
|
+
this.building.delete(key);
|
|
766
|
+
}
|
|
767
|
+
return idx;
|
|
768
|
+
}
|
|
769
|
+
/** Embed one query string. Returns null when semantic is not available. */
|
|
770
|
+
async embedQuery(text) {
|
|
771
|
+
if (this.gaveUp) return null;
|
|
772
|
+
try {
|
|
773
|
+
const [vector] = await this.embed([text], true);
|
|
774
|
+
return vector ?? null;
|
|
775
|
+
} catch (err) {
|
|
776
|
+
this.log(`semantic: query embedding failed \u2014 ${err instanceof Error ? err.message : String(err)}; using lexical`);
|
|
777
|
+
return null;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
/** Stop the worker (daemon shutdown). Safe to call when already stopped. */
|
|
781
|
+
stop() {
|
|
782
|
+
const child = this.child;
|
|
783
|
+
this.child = null;
|
|
784
|
+
this.starting = false;
|
|
785
|
+
for (const [, p] of this.pending) {
|
|
786
|
+
clearTimeout(p.timer);
|
|
787
|
+
p.reject(new Error("embedding worker stopped"));
|
|
788
|
+
}
|
|
789
|
+
this.pending.clear();
|
|
790
|
+
if (child) {
|
|
791
|
+
try {
|
|
792
|
+
child.kill();
|
|
793
|
+
} catch {
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
// ── worker plumbing ────────────────────────────────────────────────────
|
|
798
|
+
ensureWorker() {
|
|
799
|
+
if (this.child) return this.child;
|
|
800
|
+
this.starting = true;
|
|
801
|
+
const child = this.spawnWorker();
|
|
802
|
+
this.child = child;
|
|
803
|
+
this.starting = false;
|
|
804
|
+
this.buffer = "";
|
|
805
|
+
this.log(`semantic: embedding worker started (pid ${child.pid ?? "?"})`);
|
|
806
|
+
child.stdout?.setEncoding("utf8");
|
|
807
|
+
child.stdout?.on("data", (chunk) => this.onData(chunk));
|
|
808
|
+
child.stderr?.setEncoding("utf8");
|
|
809
|
+
child.stderr?.on("data", (chunk) => {
|
|
810
|
+
const text = String(chunk).trim();
|
|
811
|
+
if (text) this.log(`semantic worker: ${text.slice(0, 500)}`);
|
|
812
|
+
});
|
|
813
|
+
child.on("exit", (code, signal) => this.onExit(code, signal));
|
|
814
|
+
child.on("error", (err) => {
|
|
815
|
+
this.log(`semantic: embedding worker could not start \u2014 ${err.message}`);
|
|
816
|
+
this.onExit(null, null);
|
|
817
|
+
});
|
|
818
|
+
return child;
|
|
819
|
+
}
|
|
820
|
+
onData(chunk) {
|
|
821
|
+
this.buffer += chunk;
|
|
822
|
+
let nl;
|
|
823
|
+
while ((nl = this.buffer.indexOf("\n")) >= 0) {
|
|
824
|
+
const line = this.buffer.slice(0, nl).trim();
|
|
825
|
+
this.buffer = this.buffer.slice(nl + 1);
|
|
826
|
+
if (!line) continue;
|
|
827
|
+
let res;
|
|
828
|
+
try {
|
|
829
|
+
res = JSON.parse(line);
|
|
830
|
+
} catch {
|
|
831
|
+
continue;
|
|
832
|
+
}
|
|
833
|
+
if (typeof res.model === "string") this.model = res.model;
|
|
834
|
+
const id = res.id;
|
|
835
|
+
if (typeof id !== "number") continue;
|
|
836
|
+
const p = this.pending.get(id);
|
|
837
|
+
if (!p) continue;
|
|
838
|
+
this.pending.delete(id);
|
|
839
|
+
clearTimeout(p.timer);
|
|
840
|
+
if (res.ok && Array.isArray(res.vectors)) p.resolve(res.vectors);
|
|
841
|
+
else p.reject(new Error(res.error || "embedding worker returned no vectors"));
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
onExit(code, signal) {
|
|
845
|
+
const wasRunning = !!this.child;
|
|
846
|
+
this.child = null;
|
|
847
|
+
if (!wasRunning) return;
|
|
848
|
+
this.crashes++;
|
|
849
|
+
const how = signal ? `signal ${signal}` : `code ${code ?? "null"}`;
|
|
850
|
+
for (const [, p] of this.pending) {
|
|
851
|
+
clearTimeout(p.timer);
|
|
852
|
+
p.reject(new Error(`embedding worker exited (${how})`));
|
|
853
|
+
}
|
|
854
|
+
this.pending.clear();
|
|
855
|
+
if (this.crashes >= this.maxCrashes) {
|
|
856
|
+
this.gaveUp = true;
|
|
857
|
+
this.reason = `the embedding worker exited (${how}) ${this.crashes} times \u2014 semantic search is off for this daemon; everything falls back to lexical. A native backend that aborts like this is usually a blocked onnxruntime-node postinstall (see \`vg embed --where\`).`;
|
|
858
|
+
this.log(`semantic: ${this.reason}`);
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
861
|
+
this.log(`semantic: embedding worker exited (${how}) \u2014 restarting on next use (${this.crashes}/${this.maxCrashes})`);
|
|
862
|
+
}
|
|
863
|
+
embed(texts, query = false) {
|
|
864
|
+
if (this.gaveUp) return Promise.reject(new Error(this.reason ?? "embedding worker unavailable"));
|
|
865
|
+
if (!texts.length) return Promise.resolve([]);
|
|
866
|
+
let child;
|
|
867
|
+
try {
|
|
868
|
+
child = this.ensureWorker();
|
|
869
|
+
} catch (err) {
|
|
870
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
871
|
+
this.crashes++;
|
|
872
|
+
if (this.crashes >= this.maxCrashes) {
|
|
873
|
+
this.gaveUp = true;
|
|
874
|
+
this.reason = `the embedding worker could not start (${error.message}) \u2014 semantic search is off for this daemon`;
|
|
875
|
+
this.log(`semantic: ${this.reason}`);
|
|
876
|
+
} else {
|
|
877
|
+
this.log(`semantic: embedding worker could not start \u2014 ${error.message} (${this.crashes}/${this.maxCrashes})`);
|
|
878
|
+
}
|
|
879
|
+
return Promise.reject(error);
|
|
880
|
+
}
|
|
881
|
+
const id = this.nextId++;
|
|
882
|
+
return new Promise((resolve2, reject) => {
|
|
883
|
+
const timer = setTimeout(() => {
|
|
884
|
+
this.pending.delete(id);
|
|
885
|
+
reject(new Error(`embedding request timed out after ${this.requestTimeoutMs}ms`));
|
|
886
|
+
}, this.requestTimeoutMs);
|
|
887
|
+
timer.unref?.();
|
|
888
|
+
this.pending.set(id, { resolve: resolve2, reject, timer });
|
|
889
|
+
try {
|
|
890
|
+
child.stdin?.write(JSON.stringify({ id, op: "embed", texts, query }) + "\n");
|
|
891
|
+
} catch (err) {
|
|
892
|
+
this.pending.delete(id);
|
|
893
|
+
clearTimeout(timer);
|
|
894
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
895
|
+
}
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
/** A successful exchange clears the crash streak — used after a good build. */
|
|
899
|
+
noteHealthy() {
|
|
900
|
+
this.crashes = 0;
|
|
901
|
+
}
|
|
902
|
+
};
|
|
903
|
+
function defaultSpawnWorker() {
|
|
904
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
905
|
+
const entry = path.join(here, "embed-worker-main.js");
|
|
906
|
+
if (!fs2.existsSync(entry)) {
|
|
907
|
+
throw new Error(`embedding worker not found at ${entry} \u2014 semantic runs lexically`);
|
|
908
|
+
}
|
|
909
|
+
return spawn(process.execPath, [entry], {
|
|
910
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
911
|
+
// Never inherit loader flags or a debugger port: this child exists to be
|
|
912
|
+
// expendable, and anything that changes how it loads defeats the point.
|
|
913
|
+
env: { ...process.env, NODE_OPTIONS: "" }
|
|
914
|
+
});
|
|
915
|
+
}
|
|
533
916
|
|
|
534
917
|
// src/runtime/vgd/server.ts
|
|
535
918
|
async function startVgdServer(options = {}) {
|
|
@@ -537,18 +920,23 @@ async function startVgdServer(options = {}) {
|
|
|
537
920
|
const pidPath = options.pidPath ?? vgdPidPath();
|
|
538
921
|
const registry = options.registry ?? new WorkspaceRegistry();
|
|
539
922
|
const hostBroker = options.hostBroker ?? getVgdHostBroker();
|
|
923
|
+
const log = options.log ?? (() => {
|
|
924
|
+
});
|
|
925
|
+
const embedBroker = options.embedBroker ?? new EmbedBroker({ log });
|
|
926
|
+
embedBroker.setGraphProvider((repositoryId, gitRef) => registry.graphs.get(repositoryId, gitRef)?.graph);
|
|
927
|
+
registry.setSlotListener(embedBroker);
|
|
540
928
|
const onShutdownRequest = options.onShutdownRequest;
|
|
541
929
|
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
542
930
|
const pid = options.pid ?? process.pid;
|
|
543
931
|
const startedAt = Date.now();
|
|
544
932
|
if (!socketPath.startsWith("\\\\.\\pipe")) {
|
|
545
|
-
|
|
933
|
+
fs2.mkdirSync(path.dirname(socketPath), { recursive: true });
|
|
546
934
|
try {
|
|
547
|
-
|
|
935
|
+
fs2.unlinkSync(socketPath);
|
|
548
936
|
} catch {
|
|
549
937
|
}
|
|
550
938
|
} else {
|
|
551
|
-
|
|
939
|
+
fs2.mkdirSync(path.dirname(pidPath), { recursive: true });
|
|
552
940
|
}
|
|
553
941
|
const server = net.createServer((socket) => {
|
|
554
942
|
let buffer = "";
|
|
@@ -561,7 +949,7 @@ async function startVgdServer(options = {}) {
|
|
|
561
949
|
buffer = buffer.slice(nl + 1);
|
|
562
950
|
if (!line) continue;
|
|
563
951
|
void Promise.resolve(
|
|
564
|
-
handleLine(line, { registry, hostBroker, now, pid, startedAt, socketPath, onShutdownRequest })
|
|
952
|
+
handleLine(line, { registry, hostBroker, embedBroker, now, pid, startedAt, socketPath, onShutdownRequest, log })
|
|
565
953
|
).then(
|
|
566
954
|
(response) => {
|
|
567
955
|
socket.write(JSON.stringify(response) + "\n");
|
|
@@ -578,8 +966,8 @@ async function startVgdServer(options = {}) {
|
|
|
578
966
|
});
|
|
579
967
|
});
|
|
580
968
|
try {
|
|
581
|
-
|
|
582
|
-
|
|
969
|
+
fs2.mkdirSync(path.dirname(pidPath), { recursive: true });
|
|
970
|
+
fs2.writeFileSync(pidPath, `${pid}
|
|
583
971
|
`, "utf8");
|
|
584
972
|
} catch {
|
|
585
973
|
}
|
|
@@ -588,14 +976,15 @@ async function startVgdServer(options = {}) {
|
|
|
588
976
|
registry,
|
|
589
977
|
startedAt,
|
|
590
978
|
async close() {
|
|
979
|
+
embedBroker.stop();
|
|
591
980
|
await new Promise((resolve2) => server.close(() => resolve2()));
|
|
592
981
|
try {
|
|
593
|
-
|
|
982
|
+
fs2.unlinkSync(pidPath);
|
|
594
983
|
} catch {
|
|
595
984
|
}
|
|
596
985
|
if (!socketPath.startsWith("\\\\.\\pipe")) {
|
|
597
986
|
try {
|
|
598
|
-
|
|
987
|
+
fs2.unlinkSync(socketPath);
|
|
599
988
|
} catch {
|
|
600
989
|
}
|
|
601
990
|
}
|
|
@@ -630,6 +1019,37 @@ async function handleLine(line, ctx) {
|
|
|
630
1019
|
setTimeout(() => hook(), 150);
|
|
631
1020
|
return { ok: true, stopping: true };
|
|
632
1021
|
}
|
|
1022
|
+
case "embed-status":
|
|
1023
|
+
return { ok: true, semantic: ctx.embedBroker.status() };
|
|
1024
|
+
case "embed-query": {
|
|
1025
|
+
const vector = await ctx.embedBroker.embedQuery(req.text);
|
|
1026
|
+
if (!vector) {
|
|
1027
|
+
return {
|
|
1028
|
+
ok: false,
|
|
1029
|
+
error: "semantic unavailable \u2014 rank lexically",
|
|
1030
|
+
code: "semantic_unavailable"
|
|
1031
|
+
};
|
|
1032
|
+
}
|
|
1033
|
+
return { ok: true, vector, model: ctx.embedBroker.status().model };
|
|
1034
|
+
}
|
|
1035
|
+
case "embed-index": {
|
|
1036
|
+
const gitRef = req.gitRef ?? ctx.registry.graphs.selectedRef(req.repositoryId);
|
|
1037
|
+
if (!gitRef) return { ok: false, error: "no current gitRef for this repository", code: "no_slot" };
|
|
1038
|
+
const slot = ctx.registry.graphs.get(req.repositoryId, gitRef);
|
|
1039
|
+
if (!slot) {
|
|
1040
|
+
return { ok: false, error: `no graph slot for ${req.repositoryId}@${gitRef} \u2014 publish the map first`, code: "no_slot" };
|
|
1041
|
+
}
|
|
1042
|
+
const idx = await ctx.embedBroker.ensureIndex(req.repositoryId, gitRef, slot.graph);
|
|
1043
|
+
return {
|
|
1044
|
+
ok: true,
|
|
1045
|
+
indexed: true,
|
|
1046
|
+
repositoryId: req.repositoryId,
|
|
1047
|
+
gitRef,
|
|
1048
|
+
state: idx.state,
|
|
1049
|
+
vectors: idx.vectors.size,
|
|
1050
|
+
buildMs: idx.buildMs
|
|
1051
|
+
};
|
|
1052
|
+
}
|
|
633
1053
|
case "host-status":
|
|
634
1054
|
return { ok: true, host: ctx.hostBroker.status() };
|
|
635
1055
|
case "host-load": {
|
|
@@ -670,11 +1090,14 @@ async function handleLine(line, ctx) {
|
|
|
670
1090
|
}
|
|
671
1091
|
case "list":
|
|
672
1092
|
return { ok: true, workspaces: ctx.registry.list() };
|
|
673
|
-
case "register":
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
1093
|
+
case "register": {
|
|
1094
|
+
const known = ctx.registry.size();
|
|
1095
|
+
const workspace = ctx.registry.register(req.root, ctx.now, { label: req.label, role: req.role });
|
|
1096
|
+
if (ctx.registry.size() > known) {
|
|
1097
|
+
ctx.log(`register: new workspace ${workspace.id} (${workspace.root}${workspace.gitRef ? ` @${workspace.gitRef}` : ""})`);
|
|
1098
|
+
}
|
|
1099
|
+
return { ok: true, workspace };
|
|
1100
|
+
}
|
|
678
1101
|
case "unregister":
|
|
679
1102
|
return { ok: true, removed: ctx.registry.unregister(req.root) };
|
|
680
1103
|
case "register-federation": {
|
|
@@ -686,12 +1109,17 @@ async function handleLine(line, ctx) {
|
|
|
686
1109
|
return { ok: true, slots: ctx.registry.graphs.list(req.repositoryId) };
|
|
687
1110
|
}
|
|
688
1111
|
case "select-git-ref": {
|
|
1112
|
+
const before = ctx.registry.graphs.selectedRef(req.repositoryId);
|
|
1113
|
+
if (before !== req.gitRef) {
|
|
1114
|
+
ctx.log(`select-git-ref: ${req.repositoryId} ${before ?? "(none)"} \u2192 ${req.gitRef}`);
|
|
1115
|
+
}
|
|
689
1116
|
ctx.registry.selectGitRef(req.repositoryId, req.gitRef);
|
|
690
1117
|
return { ok: true, selected: true, repositoryId: req.repositoryId, gitRef: req.gitRef };
|
|
691
1118
|
}
|
|
692
1119
|
case "put-graph": {
|
|
693
1120
|
try {
|
|
694
1121
|
const graph = req.graph;
|
|
1122
|
+
ctx.log(`put-graph: ${req.repositoryId}@${req.gitRef} \xB7 ${graph.nodes?.length ?? 0} nodes`);
|
|
695
1123
|
ctx.registry.putGraph(req.repositoryId, req.gitRef, graph);
|
|
696
1124
|
return {
|
|
697
1125
|
ok: true,
|
|
@@ -707,6 +1135,7 @@ async function handleLine(line, ctx) {
|
|
|
707
1135
|
case "load-graph": {
|
|
708
1136
|
const record = ctx.registry.register(req.root, ctx.now, { gitRef: req.gitRef });
|
|
709
1137
|
const gitRef = req.gitRef ?? record.gitRef ?? "HEAD";
|
|
1138
|
+
ctx.log(`load-graph: ${record.id}@${gitRef} from ${req.root}`);
|
|
710
1139
|
const graph = loadGraph(req.root, req.graphPath);
|
|
711
1140
|
if (!graph) {
|
|
712
1141
|
return {
|
|
@@ -885,5 +1314,5 @@ var BranchGraphSession = class {
|
|
|
885
1314
|
};
|
|
886
1315
|
|
|
887
1316
|
export { ActiveGraphCache, BranchGraphSession, DEFAULT_ACTIVE_GRAPH_IDLE_MS, VGD_PROTOCOL_VERSION, WorkspaceRegistry, parseRequest, startVgdServer };
|
|
888
|
-
//# sourceMappingURL=chunk-
|
|
889
|
-
//# sourceMappingURL=chunk-
|
|
1317
|
+
//# sourceMappingURL=chunk-4ZMJKR74.js.map
|
|
1318
|
+
//# sourceMappingURL=chunk-4ZMJKR74.js.map
|