@vibgrate/cli 2026.817.2 → 2026.818.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/dist/activity-MF2GNYAI.js +5 -0
- package/dist/activity-MF2GNYAI.js.map +1 -0
- package/dist/agent-CUK24AWF.js +30 -0
- package/dist/{agent-UQC7QN77.js.map → agent-CUK24AWF.js.map} +1 -1
- package/dist/{approvals-ASPJQUZK.js → approvals-YFOU6GVB.js} +5 -5
- package/dist/{approvals-ASPJQUZK.js.map → approvals-YFOU6GVB.js.map} +1 -1
- package/dist/attach-6S3XS7VO.js +9 -0
- package/dist/attach-6S3XS7VO.js.map +1 -0
- package/dist/baseline-OBZJRCCP.js +9 -0
- package/dist/{baseline-FIV6PR2M.js.map → baseline-OBZJRCCP.js.map} +1 -1
- package/dist/{chunk-E2FAQREZ.js → chunk-43ESHUM5.js} +174 -401
- package/dist/chunk-43ESHUM5.js.map +1 -0
- package/dist/{chunk-K6DXETMC.js → chunk-4QAMUBAC.js} +13 -12
- package/dist/chunk-4QAMUBAC.js.map +1 -0
- package/dist/{chunk-6K3XK7C5.js → chunk-6ABMV74F.js} +6 -3
- package/dist/chunk-6ABMV74F.js.map +1 -0
- package/dist/chunk-6BEBJCK4.js +467 -0
- package/dist/chunk-6BEBJCK4.js.map +1 -0
- package/dist/{chunk-KJT2R6WW.js → chunk-6LY3JH6S.js} +3 -3
- package/dist/{chunk-KJT2R6WW.js.map → chunk-6LY3JH6S.js.map} +1 -1
- package/dist/chunk-6WRG7VDO.js +118 -0
- package/dist/chunk-6WRG7VDO.js.map +1 -0
- package/dist/chunk-DBX3DL27.js +26 -0
- package/dist/chunk-DBX3DL27.js.map +1 -0
- package/dist/{chunk-Q3M6K3HY.js → chunk-DH6EDSQP.js} +3 -3
- package/dist/{chunk-Q3M6K3HY.js.map → chunk-DH6EDSQP.js.map} +1 -1
- package/dist/{chunk-5OL2TSHK.js → chunk-DM6MJ7IF.js} +4 -4
- package/dist/{chunk-5OL2TSHK.js.map → chunk-DM6MJ7IF.js.map} +1 -1
- package/dist/{chunk-Q4G2BWE3.js → chunk-DUCEKH7Q.js} +12 -6
- package/dist/chunk-DUCEKH7Q.js.map +1 -0
- package/dist/{chunk-EXV4QSAD.js → chunk-E7RVA2ZI.js} +3 -3
- package/dist/{chunk-EXV4QSAD.js.map → chunk-E7RVA2ZI.js.map} +1 -1
- package/dist/{chunk-C3LMWZKF.js → chunk-FFYO7G3T.js} +4 -4
- package/dist/{chunk-C3LMWZKF.js.map → chunk-FFYO7G3T.js.map} +1 -1
- package/dist/chunk-G7ZFSH5J.js +132 -0
- package/dist/chunk-G7ZFSH5J.js.map +1 -0
- package/dist/{chunk-M7IO3DXH.js → chunk-GKZU6MII.js} +165 -31
- package/dist/chunk-GKZU6MII.js.map +1 -0
- package/dist/chunk-H5Q4JUY4.js +47 -0
- package/dist/chunk-H5Q4JUY4.js.map +1 -0
- package/dist/{chunk-LHKQGVRF.js → chunk-H6RV2OAX.js} +31 -10
- package/dist/chunk-H6RV2OAX.js.map +1 -0
- package/dist/{chunk-2FEB5KJS.js → chunk-I3PKYYRP.js} +5 -4
- package/dist/chunk-I3PKYYRP.js.map +1 -0
- package/dist/{chunk-XLXJC7H3.js → chunk-J3A3O4IA.js} +5 -118
- package/dist/chunk-J3A3O4IA.js.map +1 -0
- package/dist/{chunk-O2DL2Q55.js → chunk-J7CVFTLG.js} +5 -5
- package/dist/{chunk-O2DL2Q55.js.map → chunk-J7CVFTLG.js.map} +1 -1
- package/dist/{chunk-3NJUOIZQ.js → chunk-LHQYYBCH.js} +5 -5
- package/dist/{chunk-3NJUOIZQ.js.map → chunk-LHQYYBCH.js.map} +1 -1
- package/dist/{chunk-GOGMS3ST.js → chunk-MHJBIGWV.js} +752 -51
- package/dist/chunk-MHJBIGWV.js.map +1 -0
- package/dist/chunk-NDNNOAS6.js +16 -0
- package/dist/chunk-NDNNOAS6.js.map +1 -0
- package/dist/{chunk-RBIRXQR6.js → chunk-NO4BMW7M.js} +4 -14
- package/dist/chunk-NO4BMW7M.js.map +1 -0
- package/dist/{chunk-DVW3SVCW.js → chunk-OS4YFOFJ.js} +29 -9
- package/dist/chunk-OS4YFOFJ.js.map +1 -0
- package/dist/{chunk-3H3SVJUY.js → chunk-OVUZWR67.js} +107 -542
- package/dist/chunk-OVUZWR67.js.map +1 -0
- package/dist/chunk-POOL355F.js +237 -0
- package/dist/chunk-POOL355F.js.map +1 -0
- package/dist/{chunk-WVMOGIPC.js → chunk-Q7TWHJJ6.js} +3 -3
- package/dist/{chunk-WVMOGIPC.js.map → chunk-Q7TWHJJ6.js.map} +1 -1
- package/dist/{chunk-3FMRFECC.js → chunk-SBHK4ECM.js} +3 -3
- package/dist/{chunk-3FMRFECC.js.map → chunk-SBHK4ECM.js.map} +1 -1
- package/dist/chunk-SUD54UGN.js +6 -0
- package/dist/{chunk-PRG2ULPK.js.map → chunk-SUD54UGN.js.map} +1 -1
- package/dist/{chunk-WAKP7SK7.js → chunk-V2UGRUTL.js} +225 -48
- package/dist/chunk-V2UGRUTL.js.map +1 -0
- package/dist/{chunk-AXZZ5CCG.js → chunk-WVTGF2MP.js} +38 -19
- package/dist/chunk-WVTGF2MP.js.map +1 -0
- package/dist/{chunk-TVDMNIUT.js → chunk-X4DECFUG.js} +4 -4
- package/dist/{chunk-TVDMNIUT.js.map → chunk-X4DECFUG.js.map} +1 -1
- package/dist/{chunk-OZ3IFXRD.js → chunk-YENM3HT7.js} +5 -4
- package/dist/chunk-YENM3HT7.js.map +1 -0
- package/dist/{chunk-KNGAZDGF.js → chunk-ZWFMJUXC.js} +151 -42
- package/dist/chunk-ZWFMJUXC.js.map +1 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.js +576 -454
- package/dist/cli.js.map +1 -1
- package/dist/daemon-4Q5QBBAF.js +31 -0
- package/dist/{daemon-FPTRHO5N.js.map → daemon-4Q5QBBAF.js.map} +1 -1
- package/dist/embed-worker-main.js +3 -3
- package/dist/ensure-map-B2PS5ALS.js +40 -0
- package/dist/{ensure-map-342G2IV3.js.map → ensure-map-B2PS5ALS.js.map} +1 -1
- package/dist/graph-backend-EEXMD6XY.js +16 -0
- package/dist/{graph-backend-RNWQBT3P.js.map → graph-backend-EEXMD6XY.js.map} +1 -1
- package/dist/index.d.ts +606 -4
- package/dist/index.js +24 -17
- package/dist/index.js.map +1 -1
- package/dist/{interactive-SR7TMF6M.js → interactive-KEPGE5HD.js} +40 -33
- package/dist/interactive-KEPGE5HD.js.map +1 -0
- package/dist/load-H5HAME25.js +11 -0
- package/dist/{load-AYFECIQB.js.map → load-H5HAME25.js.map} +1 -1
- package/dist/parse-worker.js +3 -2
- package/dist/parse-worker.js.map +1 -1
- package/dist/{run-outcome-YQQJG3LW.js → run-outcome-V3L4GAN4.js} +5 -5
- package/dist/{run-outcome-YQQJG3LW.js.map → run-outcome-V3L4GAN4.js.map} +1 -1
- package/dist/runtime-session-M2FRGTDS.js +27 -0
- package/dist/{runtime-session-CNJ4DAV5.js.map → runtime-session-M2FRGTDS.js.map} +1 -1
- package/dist/{session-H34HUV2Y.js → session-77YXFUC7.js} +8 -8
- package/dist/{session-H34HUV2Y.js.map → session-77YXFUC7.js.map} +1 -1
- package/dist/slot-subscription-25GOARW5.js +95 -0
- package/dist/slot-subscription-25GOARW5.js.map +1 -0
- package/dist/{stream-json-55YDNAFX.js → stream-json-S6E3W2US.js} +18 -15
- package/dist/stream-json-S6E3W2US.js.map +1 -0
- package/dist/version-VRZT5G4T.js +4 -0
- package/dist/{version-WSE3QOPK.js.map → version-VRZT5G4T.js.map} +1 -1
- package/dist/{vg-mcp-bridge-2CNLHNEL.js → vg-mcp-bridge-HCPJWJKJ.js} +12 -11
- package/dist/vg-mcp-bridge-HCPJWJKJ.js.map +1 -0
- package/dist/{vgd-URUZQORN.js → vgd-R2PNIDEP.js} +15 -10
- package/dist/vgd-R2PNIDEP.js.map +1 -0
- package/package.json +1 -1
- package/dist/agent-UQC7QN77.js +0 -27
- package/dist/baseline-FIV6PR2M.js +0 -9
- package/dist/chunk-2FEB5KJS.js.map +0 -1
- package/dist/chunk-3H3SVJUY.js.map +0 -1
- package/dist/chunk-6K3XK7C5.js.map +0 -1
- package/dist/chunk-AXZZ5CCG.js.map +0 -1
- package/dist/chunk-DVW3SVCW.js.map +0 -1
- package/dist/chunk-E2FAQREZ.js.map +0 -1
- package/dist/chunk-GOGMS3ST.js.map +0 -1
- package/dist/chunk-K6DXETMC.js.map +0 -1
- package/dist/chunk-KNGAZDGF.js.map +0 -1
- package/dist/chunk-LHKQGVRF.js.map +0 -1
- package/dist/chunk-M7IO3DXH.js.map +0 -1
- package/dist/chunk-OZ3IFXRD.js.map +0 -1
- package/dist/chunk-PRG2ULPK.js +0 -6
- package/dist/chunk-Q4G2BWE3.js.map +0 -1
- package/dist/chunk-RBIRXQR6.js.map +0 -1
- package/dist/chunk-WAKP7SK7.js.map +0 -1
- package/dist/chunk-XLXJC7H3.js.map +0 -1
- package/dist/daemon-FPTRHO5N.js +0 -26
- package/dist/ensure-map-342G2IV3.js +0 -33
- package/dist/graph-backend-RNWQBT3P.js +0 -15
- package/dist/interactive-SR7TMF6M.js.map +0 -1
- package/dist/load-AYFECIQB.js +0 -11
- package/dist/runtime-session-CNJ4DAV5.js +0 -22
- package/dist/stream-json-55YDNAFX.js.map +0 -1
- package/dist/version-WSE3QOPK.js +0 -4
- package/dist/vg-mcp-bridge-2CNLHNEL.js.map +0 -1
- package/dist/vgd-URUZQORN.js.map +0 -1
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import { listHostSessions, hostSessionPoolSize, acquireHostSession, evictIdleSessions, clearHostSessionPool, dropHostSession, generateOnSession } from './chunk-3LOWJSAF.js';
|
|
2
|
-
import {
|
|
2
|
+
import { isRelevantChange } from './chunk-DBX3DL27.js';
|
|
3
|
+
import { isSkippedDirName, SKIP_FILES } from './chunk-POOL355F.js';
|
|
4
|
+
import { inventory } from './chunk-6BEBJCK4.js';
|
|
3
5
|
import { resolveOne } from './chunk-J7LKEB2R.js';
|
|
4
|
-
import { impactOf, queryGraph } from './chunk-
|
|
5
|
-
import { loadGraph } from './chunk-
|
|
6
|
-
import {
|
|
6
|
+
import { impactOf, queryGraph, queryGraphSemantic } from './chunk-DUCEKH7Q.js';
|
|
7
|
+
import { loadGraph } from './chunk-SBHK4ECM.js';
|
|
8
|
+
import { vgdSocketPath, vgdPidPath } from './chunk-NDNNOAS6.js';
|
|
9
|
+
import { embedTargets, readCachedVectors, cosine } from './chunk-WVTGF2MP.js';
|
|
10
|
+
import { cacheDir } from './chunk-MVPRPSXU.js';
|
|
11
|
+
import { stableStringify } from './chunk-O3LABX6I.js';
|
|
12
|
+
import { VERSION } from './chunk-SUD54UGN.js';
|
|
7
13
|
import { repositoryIdFromRoot, globalGraphPathForRef, globalGraphPath } from './chunk-BWPBB45M.js';
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
10
|
-
import * as
|
|
14
|
+
import { hashString, canonicalize, hashBytes } from './chunk-JFGYT6BI.js';
|
|
15
|
+
import { activeGraphSlotKey, detectGitRef, clearDetectGitRefCache } from './chunk-BPF3YX2U.js';
|
|
16
|
+
import * as path3 from 'path';
|
|
17
|
+
import * as fs4 from 'fs';
|
|
11
18
|
import * as net from 'net';
|
|
12
19
|
import { spawn } from 'child_process';
|
|
13
20
|
import { fileURLToPath } from 'url';
|
|
@@ -81,6 +88,37 @@ function parseRequest(line) {
|
|
|
81
88
|
gitRef: typeof gitRef === "string" && gitRef.trim() ? gitRef.trim() : void 0
|
|
82
89
|
};
|
|
83
90
|
}
|
|
91
|
+
if (op === "embed-rank") {
|
|
92
|
+
const repositoryId = raw.repositoryId;
|
|
93
|
+
const gitRef = raw.gitRef;
|
|
94
|
+
const text = raw.text;
|
|
95
|
+
const limit = raw.limit;
|
|
96
|
+
if (typeof repositoryId !== "string" || !repositoryId.trim()) return { error: "embed-rank requires repositoryId" };
|
|
97
|
+
if (typeof text !== "string" || !text.trim()) return { error: "embed-rank requires text" };
|
|
98
|
+
return {
|
|
99
|
+
op: "embed-rank",
|
|
100
|
+
repositoryId: repositoryId.trim(),
|
|
101
|
+
gitRef: typeof gitRef === "string" && gitRef.trim() ? gitRef.trim() : void 0,
|
|
102
|
+
// Same bound as embed-query: untrusted socket input, one worker.
|
|
103
|
+
text: text.trim().slice(0, 8e3),
|
|
104
|
+
// Only the ORDER of this list reaches the caller's rank fusion, and a
|
|
105
|
+
// candidate past a few hundred can never surface in a 12-row answer —
|
|
106
|
+
// so a generous cap costs nothing and a huge one buys nothing.
|
|
107
|
+
limit: typeof limit === "number" && limit > 0 ? Math.min(Math.floor(limit), 1e3) : void 0
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
if (op === "watch-slots") {
|
|
111
|
+
const repositoryId = raw.repositoryId;
|
|
112
|
+
return {
|
|
113
|
+
op: "watch-slots",
|
|
114
|
+
repositoryId: typeof repositoryId === "string" && repositoryId.trim() ? repositoryId.trim() : void 0
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
if (op === "dep-context") {
|
|
118
|
+
const repositoryId = raw.repositoryId;
|
|
119
|
+
if (typeof repositoryId !== "string" || !repositoryId.trim()) return { error: "dep-context requires repositoryId" };
|
|
120
|
+
return { op: "dep-context", repositoryId: repositoryId.trim() };
|
|
121
|
+
}
|
|
84
122
|
if (op === "query-graph") {
|
|
85
123
|
const repositoryId = raw.repositoryId;
|
|
86
124
|
const query = raw.query;
|
|
@@ -93,7 +131,13 @@ function parseRequest(line) {
|
|
|
93
131
|
repositoryId: repositoryId.trim(),
|
|
94
132
|
query: query.trim(),
|
|
95
133
|
limit: typeof limit === "number" && limit > 0 ? Math.min(Math.floor(limit), 50) : void 0,
|
|
96
|
-
gitRef: typeof gitRef === "string" && gitRef.trim() ? gitRef.trim() : void 0
|
|
134
|
+
gitRef: typeof gitRef === "string" && gitRef.trim() ? gitRef.trim() : void 0,
|
|
135
|
+
// Opt-in, and left off the parsed request entirely when not asked: a
|
|
136
|
+
// client that asks for semantic gets the fused ranking when the slot's
|
|
137
|
+
// index is ready, and the lexical answer when it is not. Ranking and
|
|
138
|
+
// fusion both happen here, so the caller needs neither the graph nor the
|
|
139
|
+
// embedder — which is the whole reason a panel can afford to ask.
|
|
140
|
+
semantic: raw.semantic === true ? true : void 0
|
|
97
141
|
};
|
|
98
142
|
}
|
|
99
143
|
if (op === "impact-of") {
|
|
@@ -386,6 +430,8 @@ var WorkspaceRegistry = class {
|
|
|
386
430
|
* index can never describe a ref its graph no longer holds.
|
|
387
431
|
*/
|
|
388
432
|
slotListener;
|
|
433
|
+
/** Told after a graph is stored, so the daemon can push to subscribers. */
|
|
434
|
+
publishListener;
|
|
389
435
|
/** Multi-branch in-memory graphs (shared across registered workspaces). */
|
|
390
436
|
graphs = new ActiveGraphCache({
|
|
391
437
|
onEvict: (slot) => this.slotListener?.onGraphEvict(slot.repositoryId, slot.gitRef)
|
|
@@ -394,8 +440,17 @@ var WorkspaceRegistry = class {
|
|
|
394
440
|
setSlotListener(listener) {
|
|
395
441
|
this.slotListener = listener;
|
|
396
442
|
}
|
|
443
|
+
/**
|
|
444
|
+
* Attach the push channel. Separate from {@link GraphSlotListener} on
|
|
445
|
+
* purpose: that one keeps *slot-scoped resources* in step and must run
|
|
446
|
+
* before anyone can query, while this one notifies *other processes* and
|
|
447
|
+
* must run after the store is complete.
|
|
448
|
+
*/
|
|
449
|
+
setPublishListener(listener) {
|
|
450
|
+
this.publishListener = listener;
|
|
451
|
+
}
|
|
397
452
|
register(root, now = () => /* @__PURE__ */ new Date(), meta = {}) {
|
|
398
|
-
const resolved =
|
|
453
|
+
const resolved = path3.resolve(root);
|
|
399
454
|
const id = repositoryIdFromRoot(resolved);
|
|
400
455
|
const git = meta.gitRef ? { ref: meta.gitRef, kind: "branch" } : detectGitRef(resolved);
|
|
401
456
|
const graphPath = git.kind !== "none" && git.ref ? globalGraphPathForRef(resolved, git.ref) : globalGraphPath(resolved);
|
|
@@ -409,7 +464,7 @@ var WorkspaceRegistry = class {
|
|
|
409
464
|
gitRef: git.ref || void 0
|
|
410
465
|
};
|
|
411
466
|
this.byId.set(id, record);
|
|
412
|
-
if (git.ref) this.
|
|
467
|
+
if (git.ref) this.selectGitRef(id, git.ref);
|
|
413
468
|
return record;
|
|
414
469
|
}
|
|
415
470
|
/**
|
|
@@ -421,6 +476,7 @@ var WorkspaceRegistry = class {
|
|
|
421
476
|
this.graphs.select(repositoryId, gitRef);
|
|
422
477
|
this.slotListener?.onGraphPut(repositoryId, gitRef, graph);
|
|
423
478
|
this.slotListener?.onGraphSelect(repositoryId, gitRef);
|
|
479
|
+
this.publishListener?.(repositoryId, gitRef, graph);
|
|
424
480
|
}
|
|
425
481
|
/** Switch the current branch slot for a repository (no load). */
|
|
426
482
|
selectGitRef(repositoryId, gitRef) {
|
|
@@ -432,14 +488,14 @@ var WorkspaceRegistry = class {
|
|
|
432
488
|
return members.map((m) => this.register(m.root, now, { label: m.label, role: m.role }));
|
|
433
489
|
}
|
|
434
490
|
unregister(root) {
|
|
435
|
-
const id = repositoryIdFromRoot(
|
|
491
|
+
const id = repositoryIdFromRoot(path3.resolve(root));
|
|
436
492
|
return this.byId.delete(id);
|
|
437
493
|
}
|
|
438
494
|
list() {
|
|
439
495
|
return [...this.byId.values()].sort((a, b) => a.root.localeCompare(b.root));
|
|
440
496
|
}
|
|
441
497
|
get(root) {
|
|
442
|
-
return this.byId.get(repositoryIdFromRoot(
|
|
498
|
+
return this.byId.get(repositoryIdFromRoot(path3.resolve(root)));
|
|
443
499
|
}
|
|
444
500
|
/** Lookup by stable repository id. */
|
|
445
501
|
getById(repositoryId) {
|
|
@@ -482,8 +538,8 @@ var VgdHostBroker = class {
|
|
|
482
538
|
}
|
|
483
539
|
status() {
|
|
484
540
|
const leases = {};
|
|
485
|
-
for (const [
|
|
486
|
-
leases[
|
|
541
|
+
for (const [path6, clients] of this.leases) {
|
|
542
|
+
leases[path6] = clients.size;
|
|
487
543
|
}
|
|
488
544
|
return {
|
|
489
545
|
poolSize: hostSessionPoolSize(),
|
|
@@ -582,10 +638,6 @@ function getVgdHostBroker() {
|
|
|
582
638
|
if (!defaultBroker) defaultBroker = new VgdHostBroker();
|
|
583
639
|
return defaultBroker;
|
|
584
640
|
}
|
|
585
|
-
function nodeText(node) {
|
|
586
|
-
const parts = [node.qualifiedName || node.name, node.kind, node.file, node.doc].filter((p) => typeof p === "string" && p.length > 0);
|
|
587
|
-
return parts.join(" \xB7 ").slice(0, 512);
|
|
588
|
-
}
|
|
589
641
|
var EmbedBroker = class {
|
|
590
642
|
child = null;
|
|
591
643
|
starting = false;
|
|
@@ -602,6 +654,7 @@ var EmbedBroker = class {
|
|
|
602
654
|
/** The slot most recently made current — logged on change. */
|
|
603
655
|
currentKey;
|
|
604
656
|
graphProvider;
|
|
657
|
+
rootProvider;
|
|
605
658
|
log;
|
|
606
659
|
spawnWorker;
|
|
607
660
|
maxCrashes;
|
|
@@ -637,6 +690,7 @@ var EmbedBroker = class {
|
|
|
637
690
|
state: idx.state,
|
|
638
691
|
vectors: idx.vectors.size,
|
|
639
692
|
nodeCount: idx.nodeCount,
|
|
693
|
+
pending: idx.pending,
|
|
640
694
|
builtAt: idx.builtAt,
|
|
641
695
|
buildMs: idx.buildMs,
|
|
642
696
|
error: idx.error
|
|
@@ -679,6 +733,15 @@ var EmbedBroker = class {
|
|
|
679
733
|
setGraphProvider(provider) {
|
|
680
734
|
this.graphProvider = provider;
|
|
681
735
|
}
|
|
736
|
+
/**
|
|
737
|
+
* Supplies the absolute repository root for a slot, so the broker can read
|
|
738
|
+
* that repo's on-disk vector cache. It cannot come from the graph:
|
|
739
|
+
* `meta.root` is deliberately relative (`.`) so snapshots stay portable
|
|
740
|
+
* across machines. The registry holds the real path.
|
|
741
|
+
*/
|
|
742
|
+
setRootProvider(provider) {
|
|
743
|
+
this.rootProvider = provider;
|
|
744
|
+
}
|
|
682
745
|
/**
|
|
683
746
|
* A slot became current — a branch switch, a new repo, a fresh publish.
|
|
684
747
|
* The matching index is swapped in, and warmed in the background if it is
|
|
@@ -733,17 +796,35 @@ var EmbedBroker = class {
|
|
|
733
796
|
if (this.building.has(key)) return idx;
|
|
734
797
|
this.building.add(key);
|
|
735
798
|
idx.state = "building";
|
|
736
|
-
const nodes = (graph.nodes ?? []).filter((n) => !!n?.id);
|
|
737
799
|
const started = this.now();
|
|
738
|
-
this.
|
|
800
|
+
const seeded = this.seedFromDisk(repositoryId, graph);
|
|
801
|
+
const targets = seeded?.pending ?? embedTargets(graph);
|
|
802
|
+
const vectors = seeded ? seeded.vectors : /* @__PURE__ */ new Map();
|
|
803
|
+
const nodeCount = seeded ? seeded.vectors.size + seeded.pending.length : targets.length;
|
|
804
|
+
idx.nodeCount = nodeCount;
|
|
805
|
+
if (seeded && seeded.vectors.size) {
|
|
806
|
+
this.log(
|
|
807
|
+
`semantic: seeded ${repositoryId}@${gitRef} from the on-disk index \u2014 ${seeded.vectors.size} vectors, ${seeded.pending.length} pending`
|
|
808
|
+
);
|
|
809
|
+
}
|
|
810
|
+
if (seeded?.writerBusy && targets.length) {
|
|
811
|
+
idx.vectors = vectors;
|
|
812
|
+
idx.pending = targets.length;
|
|
813
|
+
idx.state = vectors.size ? "stale" : "absent";
|
|
814
|
+
this.building.delete(key);
|
|
815
|
+
this.log(
|
|
816
|
+
`semantic: ${repositoryId}@${gitRef} left at ${targets.length} pending \u2014 another process is writing the on-disk index; re-warming when it reports in`
|
|
817
|
+
);
|
|
818
|
+
return idx;
|
|
819
|
+
}
|
|
820
|
+
this.log(`semantic: building index for ${repositoryId}@${gitRef} (${targets.length} of ${nodeCount} nodes)\u2026`);
|
|
739
821
|
try {
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
const
|
|
743
|
-
|
|
744
|
-
batch.forEach((n, j) => {
|
|
822
|
+
for (let i = 0; i < targets.length; i += this.batchSize) {
|
|
823
|
+
const batch = targets.slice(i, i + this.batchSize);
|
|
824
|
+
const out = await this.embed(batch.map((t) => t.text));
|
|
825
|
+
batch.forEach((t, j) => {
|
|
745
826
|
const v = out[j];
|
|
746
|
-
if (v) vectors.set(
|
|
827
|
+
if (v) vectors.set(t.id, v);
|
|
747
828
|
});
|
|
748
829
|
const live = this.indexes.get(key);
|
|
749
830
|
if (!live || live !== idx) {
|
|
@@ -752,13 +833,15 @@ var EmbedBroker = class {
|
|
|
752
833
|
}
|
|
753
834
|
}
|
|
754
835
|
idx.vectors = vectors;
|
|
755
|
-
idx.
|
|
836
|
+
idx.pending = void 0;
|
|
756
837
|
idx.state = "ready";
|
|
757
838
|
idx.builtAt = this.now();
|
|
758
839
|
idx.buildMs = idx.builtAt - started;
|
|
759
840
|
idx.error = void 0;
|
|
760
841
|
this.log(`semantic: index ready for ${repositoryId}@${gitRef} \u2014 ${vectors.size} vectors in ${idx.buildMs}ms`);
|
|
761
842
|
} catch (err) {
|
|
843
|
+
idx.vectors = vectors;
|
|
844
|
+
idx.pending = targets.length;
|
|
762
845
|
idx.state = "failed";
|
|
763
846
|
idx.error = err instanceof Error ? err.message : String(err);
|
|
764
847
|
this.log(`semantic: index build failed for ${repositoryId}@${gitRef} \u2014 ${idx.error}; callers fall back to lexical`);
|
|
@@ -767,6 +850,51 @@ var EmbedBroker = class {
|
|
|
767
850
|
}
|
|
768
851
|
return idx;
|
|
769
852
|
}
|
|
853
|
+
/**
|
|
854
|
+
* Read this slot's vectors out of the repo's on-disk cache. Needs the repo
|
|
855
|
+
* root from the registry; a repository the daemon has no root for (a
|
|
856
|
+
* synthetic publish in a test) simply builds from scratch.
|
|
857
|
+
*/
|
|
858
|
+
seedFromDisk(repositoryId, graph) {
|
|
859
|
+
const root = this.rootProvider?.(repositoryId);
|
|
860
|
+
if (!root) return null;
|
|
861
|
+
try {
|
|
862
|
+
const read = readCachedVectors(graph, root);
|
|
863
|
+
if (!this.model) this.model = read.model;
|
|
864
|
+
return { vectors: read.vectors, pending: read.pending, writerBusy: read.writerBusy };
|
|
865
|
+
} catch (err) {
|
|
866
|
+
this.log(`semantic: could not read the on-disk index \u2014 ${err instanceof Error ? err.message : String(err)}; embedding from scratch`);
|
|
867
|
+
return null;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
/**
|
|
871
|
+
* Rank a slot's nodes against one question, inside the daemon.
|
|
872
|
+
*
|
|
873
|
+
* This is the op that makes the resident index worth holding. The
|
|
874
|
+
* alternative — shipping the vectors to the caller so it can rank locally —
|
|
875
|
+
* moves tens of megabytes over a socket per ask; ranking here moves one
|
|
876
|
+
* question in and a few hundred (id, score) pairs out.
|
|
877
|
+
*
|
|
878
|
+
* Only the *order* of this list reaches the caller's rank fusion, so
|
|
879
|
+
* truncating at `limit` is not an approximation of the answer: a candidate
|
|
880
|
+
* ranked past a few hundred cannot reach a 12-row result under RRF.
|
|
881
|
+
*
|
|
882
|
+
* Returns null whenever semantic is unavailable — a caller that gets null
|
|
883
|
+
* runs its own in-process pass, exactly as it does with no daemon at all.
|
|
884
|
+
*/
|
|
885
|
+
async rank(repositoryId, gitRef, text, limit = 200) {
|
|
886
|
+
const idx = this.slot(repositoryId, gitRef);
|
|
887
|
+
if (!idx || idx.vectors.size === 0) return null;
|
|
888
|
+
const queryVec = await this.embedQuery(text);
|
|
889
|
+
if (!queryVec) return null;
|
|
890
|
+
const scored = [];
|
|
891
|
+
for (const [id, vec] of idx.vectors) {
|
|
892
|
+
const score = cosine(queryVec, vec);
|
|
893
|
+
if (score > 0) scored.push({ id, score });
|
|
894
|
+
}
|
|
895
|
+
scored.sort((a, b) => b.score - a.score || a.id.localeCompare(b.id));
|
|
896
|
+
return { ranked: scored.slice(0, limit), state: idx.state, vectors: idx.vectors.size };
|
|
897
|
+
}
|
|
770
898
|
/** Embed one query string. Returns null when semantic is not available. */
|
|
771
899
|
async embedQuery(text) {
|
|
772
900
|
if (this.gaveUp) return null;
|
|
@@ -880,13 +1008,13 @@ var EmbedBroker = class {
|
|
|
880
1008
|
return Promise.reject(error);
|
|
881
1009
|
}
|
|
882
1010
|
const id = this.nextId++;
|
|
883
|
-
return new Promise((
|
|
1011
|
+
return new Promise((resolve3, reject) => {
|
|
884
1012
|
const timer = setTimeout(() => {
|
|
885
1013
|
this.pending.delete(id);
|
|
886
1014
|
reject(new Error(`embedding request timed out after ${this.requestTimeoutMs}ms`));
|
|
887
1015
|
}, this.requestTimeoutMs);
|
|
888
1016
|
timer.unref?.();
|
|
889
|
-
this.pending.set(id, { resolve:
|
|
1017
|
+
this.pending.set(id, { resolve: resolve3, reject, timer });
|
|
890
1018
|
try {
|
|
891
1019
|
child.stdin?.write(JSON.stringify({ id, op: "embed", texts, query }) + "\n");
|
|
892
1020
|
} catch (err) {
|
|
@@ -902,9 +1030,9 @@ var EmbedBroker = class {
|
|
|
902
1030
|
}
|
|
903
1031
|
};
|
|
904
1032
|
function defaultSpawnWorker() {
|
|
905
|
-
const here =
|
|
906
|
-
const entry =
|
|
907
|
-
if (!
|
|
1033
|
+
const here = path3.dirname(fileURLToPath(import.meta.url));
|
|
1034
|
+
const entry = path3.join(here, "embed-worker-main.js");
|
|
1035
|
+
if (!fs4.existsSync(entry)) {
|
|
908
1036
|
throw new Error(`embedding worker not found at ${entry} \u2014 semantic runs lexically`);
|
|
909
1037
|
}
|
|
910
1038
|
return spawn(process.execPath, [entry], {
|
|
@@ -915,6 +1043,446 @@ function defaultSpawnWorker() {
|
|
|
915
1043
|
});
|
|
916
1044
|
}
|
|
917
1045
|
|
|
1046
|
+
// src/lsp/manifest-positions.ts
|
|
1047
|
+
function manifestKind(filePath) {
|
|
1048
|
+
const base = (filePath.split(/[\\/]/).pop() ?? "").toLowerCase();
|
|
1049
|
+
if (base === "package.json") return "package.json";
|
|
1050
|
+
if (base === "go.mod") return "go.mod";
|
|
1051
|
+
if (base === "requirements.txt" || /^requirements.*\.txt$/.test(base)) return "requirements.txt";
|
|
1052
|
+
if (base === "pyproject.toml") return "pyproject.toml";
|
|
1053
|
+
if (base === "cargo.toml") return "Cargo.toml";
|
|
1054
|
+
if (base === "pom.xml") return "pom.xml";
|
|
1055
|
+
if (base === "build.gradle" || base === "build.gradle.kts") return "build.gradle";
|
|
1056
|
+
if (base === "gemfile") return "Gemfile";
|
|
1057
|
+
if (base === "composer.json") return "composer.json";
|
|
1058
|
+
if (base === "pubspec.yaml") return "pubspec.yaml";
|
|
1059
|
+
if (base === "mix.exs") return "mix.exs";
|
|
1060
|
+
if (base.endsWith(".csproj") || base.endsWith(".fsproj") || base.endsWith(".vbproj")) return "csproj";
|
|
1061
|
+
if (base === "dockerfile" || base.startsWith("dockerfile.")) return "Dockerfile";
|
|
1062
|
+
return "unknown";
|
|
1063
|
+
}
|
|
1064
|
+
function isManifest(filePath) {
|
|
1065
|
+
return manifestKind(filePath) !== "unknown";
|
|
1066
|
+
}
|
|
1067
|
+
function esc(s) {
|
|
1068
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1069
|
+
}
|
|
1070
|
+
function findPackageLine(text, pkg, kind) {
|
|
1071
|
+
const lines = text.split(/\r?\n/);
|
|
1072
|
+
const p = esc(pkg);
|
|
1073
|
+
const patterns = (() => {
|
|
1074
|
+
switch (kind) {
|
|
1075
|
+
case "package.json":
|
|
1076
|
+
case "composer.json":
|
|
1077
|
+
return [new RegExp(`^\\s*"${p}"\\s*:`)];
|
|
1078
|
+
case "go.mod":
|
|
1079
|
+
return [new RegExp(`^\\s*(?:require\\s+)?${p}\\s+v?\\d`)];
|
|
1080
|
+
case "requirements.txt":
|
|
1081
|
+
return [new RegExp(`^\\s*${p}\\s*(?:\\[[^\\]]*\\])?\\s*(?:[=<>!~]|$)`, "i")];
|
|
1082
|
+
case "pyproject.toml":
|
|
1083
|
+
case "Cargo.toml":
|
|
1084
|
+
return [
|
|
1085
|
+
new RegExp(`^\\s*${p}\\s*=`),
|
|
1086
|
+
new RegExp(`^\\s*"${p}\\s*(?:[=<>!~\\[]|")`)
|
|
1087
|
+
];
|
|
1088
|
+
case "pom.xml":
|
|
1089
|
+
return [new RegExp(`<artifactId>\\s*${p}\\s*</artifactId>`)];
|
|
1090
|
+
case "build.gradle":
|
|
1091
|
+
return [new RegExp(`["']${p}(?:["':])`)];
|
|
1092
|
+
case "Gemfile":
|
|
1093
|
+
return [new RegExp(`^\\s*gem\\s+["']${p}["']`)];
|
|
1094
|
+
case "pubspec.yaml":
|
|
1095
|
+
return [new RegExp(`^\\s*${p}\\s*:`)];
|
|
1096
|
+
case "mix.exs":
|
|
1097
|
+
return [new RegExp(`\\{\\s*:${p}\\s*,`)];
|
|
1098
|
+
case "csproj":
|
|
1099
|
+
return [new RegExp(`Include\\s*=\\s*"${p}"`, "i")];
|
|
1100
|
+
case "Dockerfile":
|
|
1101
|
+
return [new RegExp(`^\\s*FROM\\s+.*${p}`, "i")];
|
|
1102
|
+
default:
|
|
1103
|
+
return [];
|
|
1104
|
+
}
|
|
1105
|
+
})();
|
|
1106
|
+
for (const re of patterns) {
|
|
1107
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1108
|
+
if (re.test(lines[i])) return i;
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
return -1;
|
|
1112
|
+
}
|
|
1113
|
+
function findRuntimeLine(text, kind) {
|
|
1114
|
+
const lines = text.split(/\r?\n/);
|
|
1115
|
+
const patterns = (() => {
|
|
1116
|
+
switch (kind) {
|
|
1117
|
+
case "package.json":
|
|
1118
|
+
return [/^\s*"node"\s*:/, /^\s*"engines"\s*:/];
|
|
1119
|
+
case "go.mod":
|
|
1120
|
+
return [/^\s*go\s+\d/];
|
|
1121
|
+
case "pyproject.toml":
|
|
1122
|
+
return [/^\s*(?:requires-python|python)\s*=/];
|
|
1123
|
+
case "pom.xml":
|
|
1124
|
+
return [/<(?:java\.version|maven\.compiler\.(?:source|target|release))>/];
|
|
1125
|
+
case "build.gradle":
|
|
1126
|
+
return [/(?:sourceCompatibility|targetCompatibility|JavaVersion)/];
|
|
1127
|
+
case "csproj":
|
|
1128
|
+
return [/<TargetFrameworks?>/i];
|
|
1129
|
+
case "Dockerfile":
|
|
1130
|
+
return [/^\s*FROM\s+/i];
|
|
1131
|
+
default:
|
|
1132
|
+
return [];
|
|
1133
|
+
}
|
|
1134
|
+
})();
|
|
1135
|
+
for (const re of patterns) {
|
|
1136
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1137
|
+
if (re.test(lines[i])) return i;
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
return -1;
|
|
1141
|
+
}
|
|
1142
|
+
function endOfLine(text, line) {
|
|
1143
|
+
const lines = text.split(/\r?\n/);
|
|
1144
|
+
return (lines[line] ?? "").length;
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
// src/lsp/scan-cache.ts
|
|
1148
|
+
var CACHE_VERSION = "vg-lsp-scan-cache/2";
|
|
1149
|
+
function cachePath(root) {
|
|
1150
|
+
return path3.join(cacheDir(root), "lsp-scan.json");
|
|
1151
|
+
}
|
|
1152
|
+
function manifestHash(root) {
|
|
1153
|
+
const files = [];
|
|
1154
|
+
const walk = (dir) => {
|
|
1155
|
+
let entries;
|
|
1156
|
+
try {
|
|
1157
|
+
entries = fs4.readdirSync(dir, { withFileTypes: true });
|
|
1158
|
+
} catch {
|
|
1159
|
+
return;
|
|
1160
|
+
}
|
|
1161
|
+
for (const entry of entries) {
|
|
1162
|
+
const abs = path3.join(dir, entry.name);
|
|
1163
|
+
if (entry.isDirectory()) {
|
|
1164
|
+
if (isSkippedDirName(entry.name)) continue;
|
|
1165
|
+
walk(abs);
|
|
1166
|
+
continue;
|
|
1167
|
+
}
|
|
1168
|
+
if (!entry.isFile()) continue;
|
|
1169
|
+
const base = entry.name.toLowerCase();
|
|
1170
|
+
if (!isManifest(abs) && !SKIP_FILES.has(base)) continue;
|
|
1171
|
+
try {
|
|
1172
|
+
const rel = path3.relative(root, abs).split(path3.sep).join("/");
|
|
1173
|
+
files.push({ rel, hash: hashBytes(fs4.readFileSync(abs)) });
|
|
1174
|
+
} catch {
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
};
|
|
1178
|
+
walk(root);
|
|
1179
|
+
files.sort((a, b) => a.rel < b.rel ? -1 : a.rel > b.rel ? 1 : 0);
|
|
1180
|
+
return hashString(canonicalize(files));
|
|
1181
|
+
}
|
|
1182
|
+
function isDependencyFile(root, filePath) {
|
|
1183
|
+
const rel = path3.relative(path3.resolve(root), path3.resolve(filePath));
|
|
1184
|
+
if (rel === "" || rel.startsWith("..") || path3.isAbsolute(rel)) return false;
|
|
1185
|
+
const parts = rel.split(path3.sep);
|
|
1186
|
+
if (parts.slice(0, -1).some((dir) => isSkippedDirName(dir))) return false;
|
|
1187
|
+
const base = parts[parts.length - 1].toLowerCase();
|
|
1188
|
+
return isManifest(filePath) || SKIP_FILES.has(base);
|
|
1189
|
+
}
|
|
1190
|
+
function loadScanCache(root, key) {
|
|
1191
|
+
try {
|
|
1192
|
+
const raw = JSON.parse(fs4.readFileSync(cachePath(root), "utf8"));
|
|
1193
|
+
if (raw.version === CACHE_VERSION && raw.manifestHash === key.manifestHash && raw.toolVersion === key.toolVersion && raw.offline === key.offline && raw.artifact) {
|
|
1194
|
+
return { manifestHash: raw.manifestHash, artifact: raw.artifact };
|
|
1195
|
+
}
|
|
1196
|
+
} catch {
|
|
1197
|
+
}
|
|
1198
|
+
return null;
|
|
1199
|
+
}
|
|
1200
|
+
function writeScanCache(root, key, artifact) {
|
|
1201
|
+
try {
|
|
1202
|
+
fs4.mkdirSync(cacheDir(root), { recursive: true });
|
|
1203
|
+
fs4.writeFileSync(
|
|
1204
|
+
cachePath(root),
|
|
1205
|
+
stableStringify(
|
|
1206
|
+
{ version: CACHE_VERSION, manifestHash: key.manifestHash, toolVersion: key.toolVersion, offline: key.offline, artifact },
|
|
1207
|
+
0
|
|
1208
|
+
)
|
|
1209
|
+
);
|
|
1210
|
+
} catch {
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
// src/runtime/vgd/freshness.ts
|
|
1215
|
+
var DEFAULT_SETTLE_MS = 600;
|
|
1216
|
+
var DEFAULT_MIN_REBUILD_GAP_MS = 5e3;
|
|
1217
|
+
var DEFAULT_REBUILD_TIMEOUT_MS = 10 * 6e4;
|
|
1218
|
+
var FreshnessSupervisor = class {
|
|
1219
|
+
watched = /* @__PURE__ */ new Map();
|
|
1220
|
+
/** Told when a manifest or lockfile is written — the shared dependency cache. */
|
|
1221
|
+
manifestListener;
|
|
1222
|
+
log;
|
|
1223
|
+
reload;
|
|
1224
|
+
select;
|
|
1225
|
+
settleMs;
|
|
1226
|
+
minRebuildGapMs;
|
|
1227
|
+
rebuildTimeoutMs;
|
|
1228
|
+
watchImpl;
|
|
1229
|
+
rebuildImpl;
|
|
1230
|
+
detectRef;
|
|
1231
|
+
now;
|
|
1232
|
+
constructor(options) {
|
|
1233
|
+
this.log = options.log ?? (() => {
|
|
1234
|
+
});
|
|
1235
|
+
this.reload = options.reload;
|
|
1236
|
+
this.select = options.select;
|
|
1237
|
+
this.settleMs = options.settleMs ?? DEFAULT_SETTLE_MS;
|
|
1238
|
+
this.minRebuildGapMs = options.minRebuildGapMs ?? DEFAULT_MIN_REBUILD_GAP_MS;
|
|
1239
|
+
this.rebuildTimeoutMs = options.rebuildTimeoutMs ?? DEFAULT_REBUILD_TIMEOUT_MS;
|
|
1240
|
+
this.watchImpl = options.watch ?? defaultWatch;
|
|
1241
|
+
this.rebuildImpl = options.rebuild ?? ((root) => spawnRebuild(root, this.rebuildTimeoutMs));
|
|
1242
|
+
this.detectRef = options.detectRef ?? defaultDetectRef;
|
|
1243
|
+
this.now = options.now ?? (() => Date.now());
|
|
1244
|
+
}
|
|
1245
|
+
/**
|
|
1246
|
+
* Attach something that must forget derived state when a manifest or
|
|
1247
|
+
* lockfile changes. The watcher is already looking at exactly these files,
|
|
1248
|
+
* so a cache hung off this has no staleness window — unlike a TTL.
|
|
1249
|
+
*/
|
|
1250
|
+
setManifestListener(listener) {
|
|
1251
|
+
this.manifestListener = listener;
|
|
1252
|
+
}
|
|
1253
|
+
list() {
|
|
1254
|
+
return [...this.watched.values()].map((w) => ({
|
|
1255
|
+
repositoryId: w.repositoryId,
|
|
1256
|
+
root: w.root,
|
|
1257
|
+
gitRef: w.gitRef,
|
|
1258
|
+
watching: !!w.source,
|
|
1259
|
+
pending: w.pending.size,
|
|
1260
|
+
building: w.building,
|
|
1261
|
+
lastRebuildAt: w.lastRebuildAt || void 0
|
|
1262
|
+
})).sort((a, b) => a.repositoryId.localeCompare(b.repositoryId));
|
|
1263
|
+
}
|
|
1264
|
+
/**
|
|
1265
|
+
* Start watching a repository. Idempotent: re-registering the same root only
|
|
1266
|
+
* updates the ref it believes is current.
|
|
1267
|
+
*/
|
|
1268
|
+
start(repositoryId, root, gitRef) {
|
|
1269
|
+
const existing = this.watched.get(repositoryId);
|
|
1270
|
+
if (existing) {
|
|
1271
|
+
existing.gitRef = gitRef;
|
|
1272
|
+
return;
|
|
1273
|
+
}
|
|
1274
|
+
const entry = {
|
|
1275
|
+
repositoryId,
|
|
1276
|
+
root,
|
|
1277
|
+
gitRef,
|
|
1278
|
+
source: null,
|
|
1279
|
+
head: null,
|
|
1280
|
+
timer: null,
|
|
1281
|
+
pending: /* @__PURE__ */ new Set(),
|
|
1282
|
+
building: false,
|
|
1283
|
+
again: false,
|
|
1284
|
+
lastRebuildAt: 0
|
|
1285
|
+
};
|
|
1286
|
+
this.watched.set(repositoryId, entry);
|
|
1287
|
+
entry.source = this.watchImpl(root, (filename) => {
|
|
1288
|
+
if (!isRelevantChange(filename)) return;
|
|
1289
|
+
entry.pending.add(filename);
|
|
1290
|
+
const manifest = isDependencyFile(root, path3.join(root, filename));
|
|
1291
|
+
if (manifest) this.manifestListener?.(entry.repositoryId);
|
|
1292
|
+
this.schedule(entry, manifest ? "manifest" : "source");
|
|
1293
|
+
});
|
|
1294
|
+
const gitDir = path3.join(root, ".git");
|
|
1295
|
+
if (fs4.existsSync(gitDir)) {
|
|
1296
|
+
entry.head = this.watchImpl(gitDir, (filename) => {
|
|
1297
|
+
if (path3.basename(filename) !== "HEAD") return;
|
|
1298
|
+
this.onHeadChanged(entry);
|
|
1299
|
+
});
|
|
1300
|
+
}
|
|
1301
|
+
this.log(
|
|
1302
|
+
`freshness: watching ${repositoryId} @${gitRef} (${entry.source ? "source" : "no source watcher"}${entry.head ? " + HEAD" : ""})`
|
|
1303
|
+
);
|
|
1304
|
+
}
|
|
1305
|
+
/** Stop watching one repository — its slot is gone or was unregistered. */
|
|
1306
|
+
stop(repositoryId) {
|
|
1307
|
+
const entry = this.watched.get(repositoryId);
|
|
1308
|
+
if (!entry) return;
|
|
1309
|
+
this.watched.delete(repositoryId);
|
|
1310
|
+
if (entry.timer) clearTimeout(entry.timer);
|
|
1311
|
+
entry.source?.close();
|
|
1312
|
+
entry.head?.close();
|
|
1313
|
+
this.log(`freshness: stopped watching ${repositoryId}`);
|
|
1314
|
+
}
|
|
1315
|
+
/** Daemon shutdown. */
|
|
1316
|
+
stopAll() {
|
|
1317
|
+
for (const id of [...this.watched.keys()]) this.stop(id);
|
|
1318
|
+
}
|
|
1319
|
+
/**
|
|
1320
|
+
* A branch switch: re-select first, and rebuild only when that ref has no
|
|
1321
|
+
* usable snapshot. Checking out a branch you were on ten minutes ago should
|
|
1322
|
+
* be instant, not a full rebuild.
|
|
1323
|
+
*/
|
|
1324
|
+
onHeadChanged(entry) {
|
|
1325
|
+
const ref = this.detectRef(entry.root);
|
|
1326
|
+
if (!ref || ref === entry.gitRef) return;
|
|
1327
|
+
const from = entry.gitRef;
|
|
1328
|
+
entry.gitRef = ref;
|
|
1329
|
+
entry.pending.clear();
|
|
1330
|
+
this.log(`freshness: ${entry.repositoryId} branch ${from} \u2192 ${ref}`);
|
|
1331
|
+
this.select(entry.repositoryId, ref);
|
|
1332
|
+
void this.reload(entry.repositoryId, entry.root, ref).then((nodes) => {
|
|
1333
|
+
if (nodes != null) {
|
|
1334
|
+
this.log(`freshness: ${entry.repositoryId}@${ref} slot warm from disk \u2014 ${nodes} nodes`);
|
|
1335
|
+
} else {
|
|
1336
|
+
this.log(`freshness: ${entry.repositoryId}@${ref} has no snapshot yet \u2014 rebuilding`);
|
|
1337
|
+
this.schedule(entry, "branch");
|
|
1338
|
+
}
|
|
1339
|
+
});
|
|
1340
|
+
}
|
|
1341
|
+
schedule(entry, reason) {
|
|
1342
|
+
if (entry.building) {
|
|
1343
|
+
entry.again = true;
|
|
1344
|
+
return;
|
|
1345
|
+
}
|
|
1346
|
+
if (entry.timer) clearTimeout(entry.timer);
|
|
1347
|
+
const sinceLast = this.now() - entry.lastRebuildAt;
|
|
1348
|
+
const wait = Math.max(this.settleMs, entry.lastRebuildAt ? this.minRebuildGapMs - sinceLast : 0);
|
|
1349
|
+
entry.timer = setTimeout(() => {
|
|
1350
|
+
entry.timer = null;
|
|
1351
|
+
void this.rebuild(entry, reason);
|
|
1352
|
+
}, wait);
|
|
1353
|
+
entry.timer.unref?.();
|
|
1354
|
+
}
|
|
1355
|
+
async rebuild(entry, reason) {
|
|
1356
|
+
if (entry.building) {
|
|
1357
|
+
entry.again = true;
|
|
1358
|
+
return;
|
|
1359
|
+
}
|
|
1360
|
+
entry.building = true;
|
|
1361
|
+
const changed = entry.pending.size;
|
|
1362
|
+
const names = [...entry.pending].sort().slice(0, 3).join(", ");
|
|
1363
|
+
entry.pending.clear();
|
|
1364
|
+
const started = this.now();
|
|
1365
|
+
this.log(
|
|
1366
|
+
`freshness: rebuilding ${entry.repositoryId}@${entry.gitRef} (${reason}${changed ? `, ${changed} file(s): ${names}` : ""})\u2026`
|
|
1367
|
+
);
|
|
1368
|
+
try {
|
|
1369
|
+
const result = await this.rebuildImpl(entry.root);
|
|
1370
|
+
entry.lastRebuildAt = this.now();
|
|
1371
|
+
if (!result.ok) {
|
|
1372
|
+
this.log(`freshness: rebuild failed for ${entry.repositoryId} \u2014 ${result.error ?? "unknown"}; slot keeps its previous map`);
|
|
1373
|
+
return;
|
|
1374
|
+
}
|
|
1375
|
+
const nodes = await this.reload(entry.repositoryId, entry.root, entry.gitRef);
|
|
1376
|
+
this.log(
|
|
1377
|
+
nodes != null ? `freshness: ${entry.repositoryId}@${entry.gitRef} reloaded \u2014 ${nodes} nodes in ${this.now() - started}ms` : `freshness: ${entry.repositoryId}@${entry.gitRef} rebuilt but no snapshot to reload`
|
|
1378
|
+
);
|
|
1379
|
+
} catch (err) {
|
|
1380
|
+
this.log(`freshness: rebuild errored for ${entry.repositoryId} \u2014 ${err instanceof Error ? err.message : String(err)}`);
|
|
1381
|
+
} finally {
|
|
1382
|
+
entry.building = false;
|
|
1383
|
+
if (entry.again) {
|
|
1384
|
+
entry.again = false;
|
|
1385
|
+
this.schedule(entry, "source");
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
};
|
|
1390
|
+
function defaultDetectRef(root) {
|
|
1391
|
+
clearDetectGitRefCache(root);
|
|
1392
|
+
return detectGitRef(root).ref;
|
|
1393
|
+
}
|
|
1394
|
+
function defaultWatch(dir, onChange) {
|
|
1395
|
+
try {
|
|
1396
|
+
const w = fs4.watch(dir, { recursive: true }, (_event, filename) => {
|
|
1397
|
+
if (filename) onChange(String(filename));
|
|
1398
|
+
});
|
|
1399
|
+
w.unref?.();
|
|
1400
|
+
return w;
|
|
1401
|
+
} catch {
|
|
1402
|
+
return null;
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
function spawnRebuild(root, timeoutMs) {
|
|
1406
|
+
return new Promise((resolve3) => {
|
|
1407
|
+
let child;
|
|
1408
|
+
try {
|
|
1409
|
+
child = spawn(process.execPath, [process.argv[1] ?? "", "build", "-C", root, "--json", "--no-daemon", "--no-warm"], {
|
|
1410
|
+
stdio: "ignore",
|
|
1411
|
+
env: { ...process.env, NODE_OPTIONS: "" }
|
|
1412
|
+
});
|
|
1413
|
+
} catch (e) {
|
|
1414
|
+
resolve3({ ok: false, error: e instanceof Error ? e.message : String(e) });
|
|
1415
|
+
return;
|
|
1416
|
+
}
|
|
1417
|
+
const timer = setTimeout(() => {
|
|
1418
|
+
try {
|
|
1419
|
+
child.kill();
|
|
1420
|
+
} catch {
|
|
1421
|
+
}
|
|
1422
|
+
resolve3({ ok: false, error: `rebuild exceeded ${Math.round(timeoutMs / 1e3)}s` });
|
|
1423
|
+
}, timeoutMs);
|
|
1424
|
+
timer.unref?.();
|
|
1425
|
+
child.on("error", (err) => {
|
|
1426
|
+
clearTimeout(timer);
|
|
1427
|
+
resolve3({ ok: false, error: err.message });
|
|
1428
|
+
});
|
|
1429
|
+
child.on("exit", (code) => {
|
|
1430
|
+
clearTimeout(timer);
|
|
1431
|
+
resolve3(code === 0 ? { ok: true } : { ok: false, error: `vg build exited ${code ?? "null"}` });
|
|
1432
|
+
});
|
|
1433
|
+
});
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
// src/runtime/vgd/dep-cache.ts
|
|
1437
|
+
var DepContextCache = class {
|
|
1438
|
+
entries = /* @__PURE__ */ new Map();
|
|
1439
|
+
log;
|
|
1440
|
+
now;
|
|
1441
|
+
computeHash;
|
|
1442
|
+
computeInventory;
|
|
1443
|
+
constructor(options = {}) {
|
|
1444
|
+
this.log = options.log ?? (() => {
|
|
1445
|
+
});
|
|
1446
|
+
this.now = options.now ?? (() => Date.now());
|
|
1447
|
+
this.computeHash = options.computeHash ?? manifestHash;
|
|
1448
|
+
this.computeInventory = options.computeInventory ?? inventory;
|
|
1449
|
+
}
|
|
1450
|
+
/** Compute or reuse the context for a repository root. Never throws. */
|
|
1451
|
+
get(repositoryId, root) {
|
|
1452
|
+
const hit = this.entries.get(repositoryId);
|
|
1453
|
+
if (hit) return hit;
|
|
1454
|
+
const started = this.now();
|
|
1455
|
+
try {
|
|
1456
|
+
const context = {
|
|
1457
|
+
manifestHash: this.computeHash(root),
|
|
1458
|
+
inventory: this.computeInventory(root),
|
|
1459
|
+
builtAt: this.now()
|
|
1460
|
+
};
|
|
1461
|
+
this.entries.set(repositoryId, context);
|
|
1462
|
+
this.log(
|
|
1463
|
+
`deps: context for ${repositoryId} built in ${context.builtAt - started}ms \u2014 ${context.inventory.records.length} dependency(ies)`
|
|
1464
|
+
);
|
|
1465
|
+
return context;
|
|
1466
|
+
} catch (err) {
|
|
1467
|
+
this.log(`deps: could not build context for ${repositoryId} \u2014 ${err instanceof Error ? err.message : String(err)}`);
|
|
1468
|
+
return null;
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
/**
|
|
1472
|
+
* Forget a repository's context. Called from the freshness watcher when a
|
|
1473
|
+
* manifest or lockfile is written, so the next reader recomputes.
|
|
1474
|
+
*/
|
|
1475
|
+
invalidate(repositoryId) {
|
|
1476
|
+
if (this.entries.delete(repositoryId)) this.log(`deps: context for ${repositoryId} invalidated by a manifest change`);
|
|
1477
|
+
}
|
|
1478
|
+
clear() {
|
|
1479
|
+
this.entries.clear();
|
|
1480
|
+
}
|
|
1481
|
+
size() {
|
|
1482
|
+
return this.entries.size;
|
|
1483
|
+
}
|
|
1484
|
+
};
|
|
1485
|
+
|
|
918
1486
|
// src/runtime/vgd/server.ts
|
|
919
1487
|
async function startVgdServer(options = {}) {
|
|
920
1488
|
const socketPath = options.socketPath ?? vgdSocketPath();
|
|
@@ -925,23 +1493,60 @@ async function startVgdServer(options = {}) {
|
|
|
925
1493
|
});
|
|
926
1494
|
const embedBroker = options.embedBroker ?? new EmbedBroker({ log });
|
|
927
1495
|
embedBroker.setGraphProvider((repositoryId, gitRef) => registry.graphs.get(repositoryId, gitRef)?.graph);
|
|
1496
|
+
const freshness = options.freshness ?? new FreshnessSupervisor({
|
|
1497
|
+
log,
|
|
1498
|
+
reload: (repositoryId, root, gitRef) => {
|
|
1499
|
+
const graph = loadGraph(root);
|
|
1500
|
+
if (!graph) return Promise.resolve(null);
|
|
1501
|
+
registry.putGraph(repositoryId, gitRef, graph);
|
|
1502
|
+
return Promise.resolve(graph.nodes?.length ?? 0);
|
|
1503
|
+
},
|
|
1504
|
+
select: (repositoryId, gitRef) => registry.selectGitRef(repositoryId, gitRef)
|
|
1505
|
+
});
|
|
1506
|
+
const watchEnabled = options.watch !== false;
|
|
1507
|
+
const depCache = options.depCache ?? new DepContextCache({ log });
|
|
1508
|
+
freshness.setManifestListener((repositoryId) => depCache.invalidate(repositoryId));
|
|
1509
|
+
embedBroker.setRootProvider((repositoryId) => registry.getById(repositoryId)?.root);
|
|
928
1510
|
registry.setSlotListener(embedBroker);
|
|
929
1511
|
const onShutdownRequest = options.onShutdownRequest;
|
|
930
1512
|
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
931
1513
|
const pid = options.pid ?? process.pid;
|
|
932
1514
|
const startedAt = Date.now();
|
|
933
1515
|
if (!socketPath.startsWith("\\\\.\\pipe")) {
|
|
934
|
-
|
|
1516
|
+
fs4.mkdirSync(path3.dirname(socketPath), { recursive: true });
|
|
935
1517
|
try {
|
|
936
|
-
|
|
1518
|
+
fs4.unlinkSync(socketPath);
|
|
937
1519
|
} catch {
|
|
938
1520
|
}
|
|
939
1521
|
} else {
|
|
940
|
-
|
|
941
|
-
}
|
|
1522
|
+
fs4.mkdirSync(path3.dirname(pidPath), { recursive: true });
|
|
1523
|
+
}
|
|
1524
|
+
const subscribers = /* @__PURE__ */ new Map();
|
|
1525
|
+
const broadcastSlot = (repositoryId, gitRef, graph) => {
|
|
1526
|
+
if (!subscribers.size) return;
|
|
1527
|
+
const frame = JSON.stringify({
|
|
1528
|
+
ok: true,
|
|
1529
|
+
event: "slot-changed",
|
|
1530
|
+
repositoryId,
|
|
1531
|
+
gitRef,
|
|
1532
|
+
nodeCount: graph.nodes?.length ?? 0,
|
|
1533
|
+
corpusHash: graph.provenance?.corpusHash ?? null
|
|
1534
|
+
}) + "\n";
|
|
1535
|
+
for (const [socket, filter] of subscribers) {
|
|
1536
|
+
if (filter.repositoryId && filter.repositoryId !== repositoryId) continue;
|
|
1537
|
+
try {
|
|
1538
|
+
socket.write(frame);
|
|
1539
|
+
} catch {
|
|
1540
|
+
subscribers.delete(socket);
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
};
|
|
1544
|
+
registry.setPublishListener(broadcastSlot);
|
|
942
1545
|
const server = net.createServer((socket) => {
|
|
943
1546
|
let buffer = "";
|
|
944
1547
|
socket.setEncoding("utf8");
|
|
1548
|
+
socket.on("close", () => subscribers.delete(socket));
|
|
1549
|
+
socket.on("error", () => subscribers.delete(socket));
|
|
945
1550
|
socket.on("data", (chunk) => {
|
|
946
1551
|
buffer += chunk;
|
|
947
1552
|
let nl;
|
|
@@ -950,7 +1555,23 @@ async function startVgdServer(options = {}) {
|
|
|
950
1555
|
buffer = buffer.slice(nl + 1);
|
|
951
1556
|
if (!line) continue;
|
|
952
1557
|
void Promise.resolve(
|
|
953
|
-
handleLine(line, {
|
|
1558
|
+
handleLine(line, {
|
|
1559
|
+
registry,
|
|
1560
|
+
hostBroker,
|
|
1561
|
+
embedBroker,
|
|
1562
|
+
freshness: watchEnabled ? freshness : void 0,
|
|
1563
|
+
depCache,
|
|
1564
|
+
subscribe: (repositoryId) => {
|
|
1565
|
+
subscribers.set(socket, { repositoryId });
|
|
1566
|
+
log(`watch-slots: subscriber attached${repositoryId ? ` for ${repositoryId}` : " (all repositories)"}`);
|
|
1567
|
+
},
|
|
1568
|
+
now,
|
|
1569
|
+
pid,
|
|
1570
|
+
startedAt,
|
|
1571
|
+
socketPath,
|
|
1572
|
+
onShutdownRequest,
|
|
1573
|
+
log
|
|
1574
|
+
})
|
|
954
1575
|
).then(
|
|
955
1576
|
(response) => {
|
|
956
1577
|
socket.write(JSON.stringify(response) + "\n");
|
|
@@ -959,16 +1580,16 @@ async function startVgdServer(options = {}) {
|
|
|
959
1580
|
}
|
|
960
1581
|
});
|
|
961
1582
|
});
|
|
962
|
-
await new Promise((
|
|
1583
|
+
await new Promise((resolve3, reject) => {
|
|
963
1584
|
server.once("error", reject);
|
|
964
1585
|
server.listen(socketPath, () => {
|
|
965
1586
|
server.off("error", reject);
|
|
966
|
-
|
|
1587
|
+
resolve3();
|
|
967
1588
|
});
|
|
968
1589
|
});
|
|
969
1590
|
try {
|
|
970
|
-
|
|
971
|
-
|
|
1591
|
+
fs4.mkdirSync(path3.dirname(pidPath), { recursive: true });
|
|
1592
|
+
fs4.writeFileSync(pidPath, `${pid}
|
|
972
1593
|
`, "utf8");
|
|
973
1594
|
} catch {
|
|
974
1595
|
}
|
|
@@ -978,14 +1599,17 @@ async function startVgdServer(options = {}) {
|
|
|
978
1599
|
startedAt,
|
|
979
1600
|
async close() {
|
|
980
1601
|
embedBroker.stop();
|
|
981
|
-
|
|
1602
|
+
freshness.stopAll();
|
|
1603
|
+
depCache.clear();
|
|
1604
|
+
subscribers.clear();
|
|
1605
|
+
await new Promise((resolve3) => server.close(() => resolve3()));
|
|
982
1606
|
try {
|
|
983
|
-
|
|
1607
|
+
fs4.unlinkSync(pidPath);
|
|
984
1608
|
} catch {
|
|
985
1609
|
}
|
|
986
1610
|
if (!socketPath.startsWith("\\\\.\\pipe")) {
|
|
987
1611
|
try {
|
|
988
|
-
|
|
1612
|
+
fs4.unlinkSync(socketPath);
|
|
989
1613
|
} catch {
|
|
990
1614
|
}
|
|
991
1615
|
}
|
|
@@ -1007,7 +1631,8 @@ async function handleLine(line, ctx) {
|
|
|
1007
1631
|
graphSlots: ctx.registry.graphs.size(),
|
|
1008
1632
|
version: VGD_PROTOCOL_VERSION,
|
|
1009
1633
|
socketPath: ctx.socketPath,
|
|
1010
|
-
cliVersion: VERSION
|
|
1634
|
+
cliVersion: VERSION,
|
|
1635
|
+
freshness: ctx.freshness?.list() ?? []
|
|
1011
1636
|
};
|
|
1012
1637
|
case "shutdown": {
|
|
1013
1638
|
if (!ctx.onShutdownRequest) {
|
|
@@ -1052,6 +1677,59 @@ async function handleLine(line, ctx) {
|
|
|
1052
1677
|
buildMs: idx.buildMs
|
|
1053
1678
|
};
|
|
1054
1679
|
}
|
|
1680
|
+
case "embed-rank": {
|
|
1681
|
+
const gitRef = req.gitRef ?? ctx.registry.graphs.selectedRef(req.repositoryId);
|
|
1682
|
+
if (!gitRef) return { ok: false, error: "no current gitRef for this repository", code: "no_slot" };
|
|
1683
|
+
const slot = ctx.registry.graphs.get(req.repositoryId, gitRef);
|
|
1684
|
+
if (!slot) {
|
|
1685
|
+
return { ok: false, error: `no graph slot for ${req.repositoryId}@${gitRef} \u2014 publish the map first`, code: "no_slot" };
|
|
1686
|
+
}
|
|
1687
|
+
await ctx.embedBroker.ensureIndex(req.repositoryId, gitRef, slot.graph);
|
|
1688
|
+
const started = Date.now();
|
|
1689
|
+
const result = await ctx.embedBroker.rank(req.repositoryId, gitRef, req.text, req.limit);
|
|
1690
|
+
if (!result) {
|
|
1691
|
+
return { ok: false, error: "semantic unavailable \u2014 rank lexically", code: "semantic_unavailable" };
|
|
1692
|
+
}
|
|
1693
|
+
return {
|
|
1694
|
+
ok: true,
|
|
1695
|
+
ranked: result.ranked,
|
|
1696
|
+
repositoryId: req.repositoryId,
|
|
1697
|
+
gitRef,
|
|
1698
|
+
state: result.state,
|
|
1699
|
+
vectors: result.vectors,
|
|
1700
|
+
model: ctx.embedBroker.status().model,
|
|
1701
|
+
rankMs: Date.now() - started
|
|
1702
|
+
};
|
|
1703
|
+
}
|
|
1704
|
+
case "watch-slots": {
|
|
1705
|
+
if (!ctx.subscribe) {
|
|
1706
|
+
return { ok: false, error: "this listener does not support subscriptions", code: "unsupported" };
|
|
1707
|
+
}
|
|
1708
|
+
ctx.subscribe(req.repositoryId);
|
|
1709
|
+
return { ok: true, watching: true, repositoryId: req.repositoryId };
|
|
1710
|
+
}
|
|
1711
|
+
case "dep-context": {
|
|
1712
|
+
const record = ctx.registry.getById(req.repositoryId);
|
|
1713
|
+
if (!record) {
|
|
1714
|
+
return { ok: false, error: `unknown repository ${req.repositoryId} \u2014 register it first`, code: "not_found" };
|
|
1715
|
+
}
|
|
1716
|
+
const context = ctx.depCache.get(req.repositoryId, record.root);
|
|
1717
|
+
if (!context) {
|
|
1718
|
+
return { ok: false, error: "could not read this repository\u2019s manifests", code: "no_deps" };
|
|
1719
|
+
}
|
|
1720
|
+
return {
|
|
1721
|
+
ok: true,
|
|
1722
|
+
repositoryId: req.repositoryId,
|
|
1723
|
+
manifestHash: context.manifestHash,
|
|
1724
|
+
dependencies: context.inventory.records.map((r) => ({
|
|
1725
|
+
name: r.name,
|
|
1726
|
+
ecosystem: r.ecosystem,
|
|
1727
|
+
declared: r.declared,
|
|
1728
|
+
installed: r.installed
|
|
1729
|
+
})),
|
|
1730
|
+
builtAt: context.builtAt
|
|
1731
|
+
};
|
|
1732
|
+
}
|
|
1055
1733
|
case "host-status":
|
|
1056
1734
|
return { ok: true, host: ctx.hostBroker.status() };
|
|
1057
1735
|
case "host-load": {
|
|
@@ -1100,8 +1778,11 @@ async function handleLine(line, ctx) {
|
|
|
1100
1778
|
}
|
|
1101
1779
|
return { ok: true, workspace };
|
|
1102
1780
|
}
|
|
1103
|
-
case "unregister":
|
|
1781
|
+
case "unregister": {
|
|
1782
|
+
const record = ctx.registry.get(req.root);
|
|
1783
|
+
if (record) ctx.freshness?.stop(record.id);
|
|
1104
1784
|
return { ok: true, removed: ctx.registry.unregister(req.root) };
|
|
1785
|
+
}
|
|
1105
1786
|
case "register-federation": {
|
|
1106
1787
|
const workspaces = ctx.registry.registerFederation(req.members, ctx.now);
|
|
1107
1788
|
return { ok: true, workspaces, federation: true };
|
|
@@ -1123,6 +1804,7 @@ async function handleLine(line, ctx) {
|
|
|
1123
1804
|
const graph = req.graph;
|
|
1124
1805
|
ctx.log(`put-graph: ${req.repositoryId}@${req.gitRef} \xB7 ${graph.nodes?.length ?? 0} nodes`);
|
|
1125
1806
|
ctx.registry.putGraph(req.repositoryId, req.gitRef, graph);
|
|
1807
|
+
startWatching(ctx, req.repositoryId, req.gitRef);
|
|
1126
1808
|
return {
|
|
1127
1809
|
ok: true,
|
|
1128
1810
|
stored: true,
|
|
@@ -1147,6 +1829,7 @@ async function handleLine(line, ctx) {
|
|
|
1147
1829
|
};
|
|
1148
1830
|
}
|
|
1149
1831
|
ctx.registry.putGraph(record.id, gitRef, graph);
|
|
1832
|
+
startWatching(ctx, record.id, gitRef);
|
|
1150
1833
|
return {
|
|
1151
1834
|
ok: true,
|
|
1152
1835
|
stored: true,
|
|
@@ -1160,13 +1843,27 @@ async function handleLine(line, ctx) {
|
|
|
1160
1843
|
if (!resolved) {
|
|
1161
1844
|
return { ok: false, error: "no ActiveGraph loaded for repository \u2014 put-graph first", code: "no_graph" };
|
|
1162
1845
|
}
|
|
1163
|
-
const
|
|
1846
|
+
const limit = req.limit ?? 12;
|
|
1847
|
+
let mode = "lexical";
|
|
1848
|
+
let result = queryGraph(resolved.graph, req.query, { limit, budget: 2e3 });
|
|
1849
|
+
if (req.semantic) {
|
|
1850
|
+
const ranking = await ctx.embedBroker.rank(req.repositoryId, resolved.gitRef, req.query);
|
|
1851
|
+
if (ranking && ranking.ranked.length) {
|
|
1852
|
+
result = await queryGraphSemantic(resolved.graph, req.query, {
|
|
1853
|
+
limit,
|
|
1854
|
+
budget: 2e3,
|
|
1855
|
+
semanticRanked: ranking.ranked
|
|
1856
|
+
});
|
|
1857
|
+
mode = `semantic (vgd${ctx.embedBroker.status().model ? `, ${ctx.embedBroker.status().model}` : ""})`;
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1164
1860
|
return {
|
|
1165
1861
|
ok: true,
|
|
1166
1862
|
query: req.query,
|
|
1167
1863
|
repositoryId: req.repositoryId,
|
|
1168
1864
|
gitRef: resolved.gitRef,
|
|
1169
|
-
|
|
1865
|
+
mode,
|
|
1866
|
+
matches: result.matches.slice(0, limit).map((m) => ({
|
|
1170
1867
|
id: m.node.id,
|
|
1171
1868
|
qualifiedName: m.node.qualifiedName,
|
|
1172
1869
|
kind: m.node.kind,
|
|
@@ -1229,6 +1926,10 @@ async function handleLine(line, ctx) {
|
|
|
1229
1926
|
}
|
|
1230
1927
|
}
|
|
1231
1928
|
}
|
|
1929
|
+
function startWatching(ctx, repositoryId, gitRef) {
|
|
1930
|
+
const record = ctx.registry.getById(repositoryId);
|
|
1931
|
+
if (record) ctx.freshness?.start(repositoryId, record.root, gitRef);
|
|
1932
|
+
}
|
|
1232
1933
|
function resolveSymbol(graph, symbol) {
|
|
1233
1934
|
if (graph.nodes.some((n) => n.id === symbol)) return { id: symbol };
|
|
1234
1935
|
const hit = resolveOne(graph, symbol);
|
|
@@ -1315,6 +2016,6 @@ var BranchGraphSession = class {
|
|
|
1315
2016
|
}
|
|
1316
2017
|
};
|
|
1317
2018
|
|
|
1318
|
-
export { ActiveGraphCache, BranchGraphSession, DEFAULT_ACTIVE_GRAPH_IDLE_MS, VGD_PROTOCOL_VERSION, WorkspaceRegistry, parseRequest, startVgdServer };
|
|
1319
|
-
//# sourceMappingURL=chunk-
|
|
1320
|
-
//# sourceMappingURL=chunk-
|
|
2019
|
+
export { ActiveGraphCache, BranchGraphSession, DEFAULT_ACTIVE_GRAPH_IDLE_MS, VGD_PROTOCOL_VERSION, WorkspaceRegistry, endOfLine, findPackageLine, findRuntimeLine, isDependencyFile, isManifest, loadScanCache, manifestHash, manifestKind, parseRequest, startVgdServer, writeScanCache };
|
|
2020
|
+
//# sourceMappingURL=chunk-MHJBIGWV.js.map
|
|
2021
|
+
//# sourceMappingURL=chunk-MHJBIGWV.js.map
|