@neat.is/core 0.9.2-dev.20260822 → 0.9.2
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/{chunk-RQQUI3NQ.js → chunk-HD5X5TWY.js} +690 -46
- package/dist/chunk-HD5X5TWY.js.map +1 -0
- package/dist/{chunk-L4SZIIER.js → chunk-TMHCS4ZY.js} +2 -2
- package/dist/cli.cjs +1148 -279
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +333 -110
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +733 -88
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/neatd.cjs +733 -88
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +2 -2
- package/dist/server.cjs +732 -87
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-RQQUI3NQ.js.map +0 -1
- /package/dist/{chunk-L4SZIIER.js.map → chunk-TMHCS4ZY.js.map} +0 -0
package/dist/neatd.cjs
CHANGED
|
@@ -1355,7 +1355,7 @@ function databaseRootCauseShape(graph, origin, walk10) {
|
|
|
1355
1355
|
for (const id of walk10.path) {
|
|
1356
1356
|
const owner = resolveOwningService(graph, id);
|
|
1357
1357
|
if (!owner) continue;
|
|
1358
|
-
const { id:
|
|
1358
|
+
const { id: serviceId16, svc } = owner;
|
|
1359
1359
|
const deps = svc.dependencies ?? {};
|
|
1360
1360
|
for (const pair of candidatePairs) {
|
|
1361
1361
|
const declared = deps[pair.driver];
|
|
@@ -1368,7 +1368,7 @@ function databaseRootCauseShape(graph, origin, walk10) {
|
|
|
1368
1368
|
);
|
|
1369
1369
|
if (!result.compatible) {
|
|
1370
1370
|
return {
|
|
1371
|
-
rootCauseNode:
|
|
1371
|
+
rootCauseNode: serviceId16,
|
|
1372
1372
|
rootCauseReason: result.reason ?? "incompatible driver",
|
|
1373
1373
|
...result.minDriverVersion ? {
|
|
1374
1374
|
fixRecommendation: `Upgrade ${svc.name} ${pair.driver} driver to >= ${result.minDriverVersion}`
|
|
@@ -1383,7 +1383,7 @@ function serviceRootCauseShape(graph, _origin, walk10) {
|
|
|
1383
1383
|
for (const id of walk10.path) {
|
|
1384
1384
|
const owner = resolveOwningService(graph, id);
|
|
1385
1385
|
if (!owner) continue;
|
|
1386
|
-
const { id:
|
|
1386
|
+
const { id: serviceId16, svc } = owner;
|
|
1387
1387
|
const deps = svc.dependencies ?? {};
|
|
1388
1388
|
const serviceNodeEngine = svc.nodeEngine;
|
|
1389
1389
|
for (const constraint of nodeEngineConstraints()) {
|
|
@@ -1392,7 +1392,7 @@ function serviceRootCauseShape(graph, _origin, walk10) {
|
|
|
1392
1392
|
const result = checkNodeEngineConstraint(constraint, declared, serviceNodeEngine);
|
|
1393
1393
|
if (!result.compatible && result.reason) {
|
|
1394
1394
|
return {
|
|
1395
|
-
rootCauseNode:
|
|
1395
|
+
rootCauseNode: serviceId16,
|
|
1396
1396
|
rootCauseReason: result.reason,
|
|
1397
1397
|
...result.requiredNodeVersion ? {
|
|
1398
1398
|
fixRecommendation: `Bump ${svc.name}'s engines.node to >= ${result.requiredNodeVersion}`
|
|
@@ -1407,7 +1407,7 @@ function serviceRootCauseShape(graph, _origin, walk10) {
|
|
|
1407
1407
|
const result = checkPackageConflict(conflict, declared, requiredDeclared);
|
|
1408
1408
|
if (!result.compatible && result.reason) {
|
|
1409
1409
|
return {
|
|
1410
|
-
rootCauseNode:
|
|
1410
|
+
rootCauseNode: serviceId16,
|
|
1411
1411
|
rootCauseReason: result.reason,
|
|
1412
1412
|
fixRecommendation: `Upgrade ${svc.name}'s ${conflict.requires.name} to >= ${conflict.requires.minVersion}`
|
|
1413
1413
|
};
|
|
@@ -1512,9 +1512,9 @@ function rootCauseFromIncidents(nodeId, incidents, errorEvent) {
|
|
|
1512
1512
|
function isFailingCallEdge(e) {
|
|
1513
1513
|
return e.type === import_types.EdgeType.CALLS && (e.signal?.errorCount ?? 0) > 0;
|
|
1514
1514
|
}
|
|
1515
|
-
function callSourcesForService(graph,
|
|
1516
|
-
const ids = [
|
|
1517
|
-
for (const edgeId of graph.outboundEdges(
|
|
1515
|
+
function callSourcesForService(graph, serviceId16) {
|
|
1516
|
+
const ids = [serviceId16];
|
|
1517
|
+
for (const edgeId of graph.outboundEdges(serviceId16)) {
|
|
1518
1518
|
const e = graph.getEdgeAttributes(edgeId);
|
|
1519
1519
|
if (e.type !== import_types.EdgeType.CONTAINS) continue;
|
|
1520
1520
|
const tgt = graph.getNodeAttributes(e.target);
|
|
@@ -1538,9 +1538,9 @@ function failingCallDominates(e, id, curEdge, curId) {
|
|
|
1538
1538
|
}
|
|
1539
1539
|
return id < curId;
|
|
1540
1540
|
}
|
|
1541
|
-
function dominantFailingCall(graph,
|
|
1541
|
+
function dominantFailingCall(graph, serviceId16, visited) {
|
|
1542
1542
|
let best = null;
|
|
1543
|
-
for (const src of callSourcesForService(graph,
|
|
1543
|
+
for (const src of callSourcesForService(graph, serviceId16)) {
|
|
1544
1544
|
for (const edgeId of graph.outboundEdges(src)) {
|
|
1545
1545
|
const e = graph.getEdgeAttributes(edgeId);
|
|
1546
1546
|
if (!isFailingCallEdge(e)) continue;
|
|
@@ -1579,9 +1579,9 @@ function staleCallDominates(e, id, curEdge, curId) {
|
|
|
1579
1579
|
if (ev !== cv) return ev > cv;
|
|
1580
1580
|
return id < curId;
|
|
1581
1581
|
}
|
|
1582
|
-
function dominantStaleCall(graph,
|
|
1582
|
+
function dominantStaleCall(graph, serviceId16, visited) {
|
|
1583
1583
|
const bestByCallee = /* @__PURE__ */ new Map();
|
|
1584
|
-
for (const src of callSourcesForService(graph,
|
|
1584
|
+
for (const src of callSourcesForService(graph, serviceId16)) {
|
|
1585
1585
|
for (const edgeId of graph.outboundEdges(src)) {
|
|
1586
1586
|
const e = graph.getEdgeAttributes(edgeId);
|
|
1587
1587
|
if (e.type !== import_types.EdgeType.CALLS) continue;
|
|
@@ -4780,11 +4780,11 @@ function actixAttributeRoute(attrItem, out) {
|
|
|
4780
4780
|
if (!nameNode) return;
|
|
4781
4781
|
const macro = nameNode.type === "identifier" ? nameNode.text : nameNode.type === "scoped_identifier" ? nameNode.childForFieldName("name")?.text ?? null : null;
|
|
4782
4782
|
if (!macro) return;
|
|
4783
|
-
const
|
|
4784
|
-
if (!
|
|
4783
|
+
const tokens2 = attr.childForFieldName("arguments");
|
|
4784
|
+
if (!tokens2 || tokens2.type !== "token_tree") return;
|
|
4785
4785
|
const strings = [];
|
|
4786
|
-
for (let i = 0; i <
|
|
4787
|
-
const s = rustStringContent(
|
|
4786
|
+
for (let i = 0; i < tokens2.namedChildCount; i++) {
|
|
4787
|
+
const s = rustStringContent(tokens2.namedChild(i));
|
|
4788
4788
|
if (s !== null) strings.push(s);
|
|
4789
4789
|
}
|
|
4790
4790
|
const pathStr = strings[0];
|
|
@@ -6678,29 +6678,29 @@ function promoteFrontierNodes(graph, opts = {}) {
|
|
|
6678
6678
|
toPromote.push({ frontierId: id, serviceId: target });
|
|
6679
6679
|
});
|
|
6680
6680
|
let promoted = 0;
|
|
6681
|
-
for (const { frontierId: frontierId2, serviceId:
|
|
6681
|
+
for (const { frontierId: frontierId2, serviceId: serviceId16 } of toPromote) {
|
|
6682
6682
|
if (opts.policies && opts.policies.length > 0 && opts.policyCtx) {
|
|
6683
6683
|
const gate = canPromoteFrontier(graph, frontierId2, opts.policies, opts.policyCtx);
|
|
6684
6684
|
if (!gate.allowed) {
|
|
6685
6685
|
continue;
|
|
6686
6686
|
}
|
|
6687
6687
|
}
|
|
6688
|
-
rewireFrontierEdges(graph, frontierId2,
|
|
6688
|
+
rewireFrontierEdges(graph, frontierId2, serviceId16);
|
|
6689
6689
|
graph.dropNode(frontierId2);
|
|
6690
6690
|
promoted++;
|
|
6691
6691
|
}
|
|
6692
6692
|
return promoted;
|
|
6693
6693
|
}
|
|
6694
|
-
function rewireFrontierEdges(graph, frontierId2,
|
|
6694
|
+
function rewireFrontierEdges(graph, frontierId2, serviceId16) {
|
|
6695
6695
|
const inbound = [...graph.inboundEdges(frontierId2)];
|
|
6696
6696
|
const outbound = [...graph.outboundEdges(frontierId2)];
|
|
6697
6697
|
for (const edgeId of inbound) {
|
|
6698
6698
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
6699
|
-
rebuildEdge(graph, edge, edge.source,
|
|
6699
|
+
rebuildEdge(graph, edge, edge.source, serviceId16, edgeId);
|
|
6700
6700
|
}
|
|
6701
6701
|
for (const edgeId of outbound) {
|
|
6702
6702
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
6703
|
-
rebuildEdge(graph, edge,
|
|
6703
|
+
rebuildEdge(graph, edge, serviceId16, edge.target, edgeId);
|
|
6704
6704
|
}
|
|
6705
6705
|
}
|
|
6706
6706
|
function rebuildEdge(graph, edge, newSource, newTarget, oldEdgeId) {
|
|
@@ -7812,9 +7812,9 @@ var K8S_KINDS_WITH_HOSTNAMES = /* @__PURE__ */ new Set([
|
|
|
7812
7812
|
"StatefulSet",
|
|
7813
7813
|
"DaemonSet"
|
|
7814
7814
|
]);
|
|
7815
|
-
function addAliases(graph,
|
|
7816
|
-
if (!graph.hasNode(
|
|
7817
|
-
const node = graph.getNodeAttributes(
|
|
7815
|
+
function addAliases(graph, serviceId16, candidates) {
|
|
7816
|
+
if (!graph.hasNode(serviceId16)) return;
|
|
7817
|
+
const node = graph.getNodeAttributes(serviceId16);
|
|
7818
7818
|
if (node.type !== import_types19.NodeType.ServiceNode) return;
|
|
7819
7819
|
const set = new Set(node.aliases ?? []);
|
|
7820
7820
|
for (const c of candidates) {
|
|
@@ -7824,7 +7824,7 @@ function addAliases(graph, serviceId15, candidates) {
|
|
|
7824
7824
|
}
|
|
7825
7825
|
if (set.size === 0) return;
|
|
7826
7826
|
const updated = { ...node, aliases: [...set].sort() };
|
|
7827
|
-
graph.replaceNodeAttributes(
|
|
7827
|
+
graph.replaceNodeAttributes(serviceId16, updated);
|
|
7828
7828
|
}
|
|
7829
7829
|
function indexServicesByName(services) {
|
|
7830
7830
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -7857,12 +7857,12 @@ async function collectComposeAliases(graph, scanPath, serviceIndex) {
|
|
|
7857
7857
|
}
|
|
7858
7858
|
if (!compose?.services) return;
|
|
7859
7859
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
7860
|
-
const
|
|
7861
|
-
if (!
|
|
7860
|
+
const serviceId16 = serviceIndex.get(composeName);
|
|
7861
|
+
if (!serviceId16) continue;
|
|
7862
7862
|
const aliases = /* @__PURE__ */ new Set([composeName]);
|
|
7863
7863
|
if (svc.container_name) aliases.add(svc.container_name);
|
|
7864
7864
|
if (svc.hostname) aliases.add(svc.hostname);
|
|
7865
|
-
addAliases(graph,
|
|
7865
|
+
addAliases(graph, serviceId16, aliases);
|
|
7866
7866
|
}
|
|
7867
7867
|
}
|
|
7868
7868
|
var LABEL_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -15851,7 +15851,7 @@ var Projects = class {
|
|
|
15851
15851
|
init_cjs_shims();
|
|
15852
15852
|
var import_fastify2 = __toESM(require("fastify"), 1);
|
|
15853
15853
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
15854
|
-
var
|
|
15854
|
+
var import_types93 = require("@neat.is/types");
|
|
15855
15855
|
|
|
15856
15856
|
// src/extend/index.ts
|
|
15857
15857
|
init_cjs_shims();
|
|
@@ -16574,6 +16574,639 @@ function queryLogEntries(opts) {
|
|
|
16574
16574
|
return merged;
|
|
16575
16575
|
}
|
|
16576
16576
|
|
|
16577
|
+
// src/ask.ts
|
|
16578
|
+
init_cjs_shims();
|
|
16579
|
+
var import_types59 = require("@neat.is/types");
|
|
16580
|
+
var DEFAULT_MAX_NODES = 3;
|
|
16581
|
+
var MAX_FACTS_PER_SECTION = 6;
|
|
16582
|
+
var INTENT_RULES = [
|
|
16583
|
+
{
|
|
16584
|
+
intent: "root-cause",
|
|
16585
|
+
test: /\b(why|root[\s-]?cause|failing|fail(?:s|ed)?|breaking|broke(?:n)?|crash(?:ing|ed)?|throw(?:ing|s)?|culprit|5\d\d)\b/
|
|
16586
|
+
},
|
|
16587
|
+
{
|
|
16588
|
+
intent: "blast-radius",
|
|
16589
|
+
test: /\b(blast|break[\s-]?if|breaks[\s-]?if|impact|downstream|dependents?|redeploy|who\s+(?:uses|calls|depends)|what\s+depends\s+on|affect(?:s|ed)?)\b/
|
|
16590
|
+
},
|
|
16591
|
+
{
|
|
16592
|
+
intent: "divergence",
|
|
16593
|
+
test: /\b(diverg\w*|weird|mismatch|drift|out\s+of\s+sync|inconsistent|disagree\w*|declared\s+vs|anything\s+wrong)\b/
|
|
16594
|
+
},
|
|
16595
|
+
{
|
|
16596
|
+
intent: "incidents",
|
|
16597
|
+
test: /\b(incidents?|recent\s+(?:errors?|failures?)|error\s+history|failure\s+history)\b/
|
|
16598
|
+
},
|
|
16599
|
+
{
|
|
16600
|
+
intent: "observed",
|
|
16601
|
+
test: /\b(at\s+runtime|in\s+prod(?:uction)?|actually\s+call\w*|really\s+call\w*|observed|runtime\s+traffic)\b/
|
|
16602
|
+
},
|
|
16603
|
+
{
|
|
16604
|
+
intent: "dependencies",
|
|
16605
|
+
test: /\b(depend\w*|calls?|uses?|imports?|relies\s+on|needs?)\b/
|
|
16606
|
+
}
|
|
16607
|
+
];
|
|
16608
|
+
function classifyIntent(question) {
|
|
16609
|
+
const q = question.toLowerCase();
|
|
16610
|
+
for (const rule of INTENT_RULES) {
|
|
16611
|
+
if (rule.test.test(q)) return rule.intent;
|
|
16612
|
+
}
|
|
16613
|
+
return "overview";
|
|
16614
|
+
}
|
|
16615
|
+
var STOPWORDS = /* @__PURE__ */ new Set([
|
|
16616
|
+
"the",
|
|
16617
|
+
"a",
|
|
16618
|
+
"an",
|
|
16619
|
+
"is",
|
|
16620
|
+
"are",
|
|
16621
|
+
"was",
|
|
16622
|
+
"were",
|
|
16623
|
+
"be",
|
|
16624
|
+
"been",
|
|
16625
|
+
"being",
|
|
16626
|
+
"of",
|
|
16627
|
+
"to",
|
|
16628
|
+
"in",
|
|
16629
|
+
"on",
|
|
16630
|
+
"at",
|
|
16631
|
+
"by",
|
|
16632
|
+
"for",
|
|
16633
|
+
"with",
|
|
16634
|
+
"and",
|
|
16635
|
+
"or",
|
|
16636
|
+
"but",
|
|
16637
|
+
"if",
|
|
16638
|
+
"as",
|
|
16639
|
+
"this",
|
|
16640
|
+
"that",
|
|
16641
|
+
"these",
|
|
16642
|
+
"those",
|
|
16643
|
+
"it",
|
|
16644
|
+
"its",
|
|
16645
|
+
"do",
|
|
16646
|
+
"does",
|
|
16647
|
+
"did",
|
|
16648
|
+
"has",
|
|
16649
|
+
"have",
|
|
16650
|
+
"had",
|
|
16651
|
+
"what",
|
|
16652
|
+
"which",
|
|
16653
|
+
"who",
|
|
16654
|
+
"whom",
|
|
16655
|
+
"how",
|
|
16656
|
+
"when",
|
|
16657
|
+
"where",
|
|
16658
|
+
"my",
|
|
16659
|
+
"me",
|
|
16660
|
+
"i",
|
|
16661
|
+
"we",
|
|
16662
|
+
"you",
|
|
16663
|
+
"they",
|
|
16664
|
+
"them",
|
|
16665
|
+
"from",
|
|
16666
|
+
"into",
|
|
16667
|
+
"about",
|
|
16668
|
+
"would",
|
|
16669
|
+
"will",
|
|
16670
|
+
"can",
|
|
16671
|
+
"could",
|
|
16672
|
+
"should",
|
|
16673
|
+
"anything",
|
|
16674
|
+
"everything",
|
|
16675
|
+
"something",
|
|
16676
|
+
"all",
|
|
16677
|
+
"any",
|
|
16678
|
+
"some",
|
|
16679
|
+
"no",
|
|
16680
|
+
"not",
|
|
16681
|
+
"so",
|
|
16682
|
+
"up",
|
|
16683
|
+
"out",
|
|
16684
|
+
"over",
|
|
16685
|
+
"get",
|
|
16686
|
+
"got",
|
|
16687
|
+
"show",
|
|
16688
|
+
"tell",
|
|
16689
|
+
"explain",
|
|
16690
|
+
"find",
|
|
16691
|
+
"give",
|
|
16692
|
+
"happen",
|
|
16693
|
+
"happens",
|
|
16694
|
+
"happening",
|
|
16695
|
+
"here",
|
|
16696
|
+
"there",
|
|
16697
|
+
"system",
|
|
16698
|
+
"code",
|
|
16699
|
+
"service",
|
|
16700
|
+
"node",
|
|
16701
|
+
"graph"
|
|
16702
|
+
]);
|
|
16703
|
+
var INTENT_WORDS = /* @__PURE__ */ new Set([
|
|
16704
|
+
"why",
|
|
16705
|
+
"root",
|
|
16706
|
+
"cause",
|
|
16707
|
+
"failing",
|
|
16708
|
+
"fail",
|
|
16709
|
+
"fails",
|
|
16710
|
+
"failed",
|
|
16711
|
+
"breaking",
|
|
16712
|
+
"broke",
|
|
16713
|
+
"broken",
|
|
16714
|
+
"crash",
|
|
16715
|
+
"crashing",
|
|
16716
|
+
"crashed",
|
|
16717
|
+
"throw",
|
|
16718
|
+
"throwing",
|
|
16719
|
+
"throws",
|
|
16720
|
+
"culprit",
|
|
16721
|
+
"blast",
|
|
16722
|
+
"radius",
|
|
16723
|
+
"break",
|
|
16724
|
+
"breaks",
|
|
16725
|
+
"impact",
|
|
16726
|
+
"downstream",
|
|
16727
|
+
"dependent",
|
|
16728
|
+
"dependents",
|
|
16729
|
+
"redeploy",
|
|
16730
|
+
"affect",
|
|
16731
|
+
"affects",
|
|
16732
|
+
"affected",
|
|
16733
|
+
"diverge",
|
|
16734
|
+
"divergence",
|
|
16735
|
+
"divergences",
|
|
16736
|
+
"weird",
|
|
16737
|
+
"mismatch",
|
|
16738
|
+
"drift",
|
|
16739
|
+
"inconsistent",
|
|
16740
|
+
"disagree",
|
|
16741
|
+
"declared",
|
|
16742
|
+
"observed",
|
|
16743
|
+
"incident",
|
|
16744
|
+
"incidents",
|
|
16745
|
+
"recent",
|
|
16746
|
+
"errors",
|
|
16747
|
+
"error",
|
|
16748
|
+
"failures",
|
|
16749
|
+
"failure",
|
|
16750
|
+
"history",
|
|
16751
|
+
"runtime",
|
|
16752
|
+
"production",
|
|
16753
|
+
"prod",
|
|
16754
|
+
"traffic",
|
|
16755
|
+
"actually",
|
|
16756
|
+
"really",
|
|
16757
|
+
"depend",
|
|
16758
|
+
"depends",
|
|
16759
|
+
"dependency",
|
|
16760
|
+
"dependencies",
|
|
16761
|
+
"call",
|
|
16762
|
+
"calls",
|
|
16763
|
+
"use",
|
|
16764
|
+
"uses",
|
|
16765
|
+
"using",
|
|
16766
|
+
"import",
|
|
16767
|
+
"imports",
|
|
16768
|
+
"relies",
|
|
16769
|
+
"rely",
|
|
16770
|
+
"needs",
|
|
16771
|
+
"need",
|
|
16772
|
+
"sync"
|
|
16773
|
+
]);
|
|
16774
|
+
function tokens(input) {
|
|
16775
|
+
const spaced = input.replace(/([a-z0-9])([A-Z])/g, "$1 $2");
|
|
16776
|
+
return (spaced.toLowerCase().match(/[a-z0-9]+/g) ?? []).filter((t) => t.length >= 2);
|
|
16777
|
+
}
|
|
16778
|
+
function queryTokens(question) {
|
|
16779
|
+
const seen = /* @__PURE__ */ new Set();
|
|
16780
|
+
const out = [];
|
|
16781
|
+
for (const t of tokens(question)) {
|
|
16782
|
+
if (STOPWORDS.has(t) || INTENT_WORDS.has(t)) continue;
|
|
16783
|
+
if (seen.has(t)) continue;
|
|
16784
|
+
seen.add(t);
|
|
16785
|
+
out.push(t);
|
|
16786
|
+
}
|
|
16787
|
+
return out;
|
|
16788
|
+
}
|
|
16789
|
+
function nodeName(node) {
|
|
16790
|
+
return node.name ?? node.id;
|
|
16791
|
+
}
|
|
16792
|
+
function idBody(id) {
|
|
16793
|
+
const colon = id.indexOf(":");
|
|
16794
|
+
return colon >= 0 ? id.slice(colon + 1) : id;
|
|
16795
|
+
}
|
|
16796
|
+
var EMBED_MIN_SCORE = 0.35;
|
|
16797
|
+
async function resolveEntities(graph, question, searchIndex, maxNodes) {
|
|
16798
|
+
const qTokens = queryTokens(question);
|
|
16799
|
+
const normalized = question.toLowerCase();
|
|
16800
|
+
const best = /* @__PURE__ */ new Map();
|
|
16801
|
+
const consider = (cand) => {
|
|
16802
|
+
const cur = best.get(cand.nodeId);
|
|
16803
|
+
if (!cur || cand.score > cur.score) best.set(cand.nodeId, cand);
|
|
16804
|
+
};
|
|
16805
|
+
graph.forEachNode((id, attrs) => {
|
|
16806
|
+
const node = attrs;
|
|
16807
|
+
if (node.type === import_types59.NodeType.FrontierNode) return;
|
|
16808
|
+
const name = nodeName(node);
|
|
16809
|
+
const body = idBody(id);
|
|
16810
|
+
const labelTokens = /* @__PURE__ */ new Set([...tokens(body), ...tokens(name)]);
|
|
16811
|
+
if (labelTokens.size === 0) return;
|
|
16812
|
+
let matched = 0;
|
|
16813
|
+
for (const t of qTokens) if (labelTokens.has(t)) matched += 1;
|
|
16814
|
+
const named = body.length >= 2 && normalized.includes(body.toLowerCase()) || name.length >= 2 && normalized.includes(name.toLowerCase());
|
|
16815
|
+
if (matched === 0 && !named) return;
|
|
16816
|
+
const coverage = qTokens.length > 0 ? matched / qTokens.length : 0;
|
|
16817
|
+
const via = named ? "id" : matched > 0 && tokens(name).some((t) => qTokens.includes(t)) ? "label" : "token";
|
|
16818
|
+
const score = named ? Math.max(0.9, coverage) : Math.min(0.85, 0.3 + 0.7 * coverage);
|
|
16819
|
+
consider({ nodeId: id, label: name, via, score: Math.min(1, score) });
|
|
16820
|
+
});
|
|
16821
|
+
if (searchIndex) {
|
|
16822
|
+
try {
|
|
16823
|
+
const res = await searchIndex.search(question, 10);
|
|
16824
|
+
if (res.provider !== "substring") {
|
|
16825
|
+
for (const m of res.matches) {
|
|
16826
|
+
if (m.node.type === import_types59.NodeType.FrontierNode) continue;
|
|
16827
|
+
const already = best.get(m.node.id);
|
|
16828
|
+
if (!already && m.score < EMBED_MIN_SCORE) continue;
|
|
16829
|
+
consider({
|
|
16830
|
+
nodeId: m.node.id,
|
|
16831
|
+
label: nodeName(m.node),
|
|
16832
|
+
via: "embedding",
|
|
16833
|
+
score: Math.max(0, Math.min(1, m.score))
|
|
16834
|
+
});
|
|
16835
|
+
}
|
|
16836
|
+
}
|
|
16837
|
+
} catch {
|
|
16838
|
+
}
|
|
16839
|
+
}
|
|
16840
|
+
const viaRank = { id: 3, label: 2, token: 1, embedding: 0 };
|
|
16841
|
+
return [...best.values()].sort(
|
|
16842
|
+
(a, b) => b.score - a.score || viaRank[b.via] - viaRank[a.via] || a.nodeId.localeCompare(b.nodeId)
|
|
16843
|
+
).slice(0, maxNodes).map((s) => ({ nodeId: s.nodeId, label: s.label, via: s.via, score: Number(s.score.toFixed(3)) }));
|
|
16844
|
+
}
|
|
16845
|
+
function incidentsForNode(nodeId, incidents) {
|
|
16846
|
+
if (!incidents || incidents.length === 0) return [];
|
|
16847
|
+
const svc = nodeId.replace(/^service:/, "");
|
|
16848
|
+
return incidents.filter((e) => e.affectedNode === nodeId || e.service === svc);
|
|
16849
|
+
}
|
|
16850
|
+
function edgeSignalNote(e) {
|
|
16851
|
+
const bits = [];
|
|
16852
|
+
if (e.signal) {
|
|
16853
|
+
bits.push(`${e.signal.spanCount} spans`);
|
|
16854
|
+
if (e.signal.errorCount > 0) bits.push(`${e.signal.errorCount} errors`);
|
|
16855
|
+
if (e.signal.latencyMs?.p95 !== void 0) bits.push(`p95 ${Math.round(e.signal.latencyMs.p95)}ms`);
|
|
16856
|
+
} else if (e.callCount !== void 0) {
|
|
16857
|
+
bits.push(`${e.callCount} calls`);
|
|
16858
|
+
}
|
|
16859
|
+
return bits.length ? ` (${bits.join(", ")})` : "";
|
|
16860
|
+
}
|
|
16861
|
+
function buildRootCauseSection(graph, node, incidents, now) {
|
|
16862
|
+
const result = getRootCause(graph, node, void 0, incidents, { now });
|
|
16863
|
+
if (!result) return null;
|
|
16864
|
+
const lastProv = result.edgeProvenances[result.edgeProvenances.length - 1] ?? import_types59.Provenance.OBSERVED;
|
|
16865
|
+
const facts = [
|
|
16866
|
+
{
|
|
16867
|
+
text: `Root cause: ${result.rootCauseNode} \u2014 ${result.rootCauseReason}`,
|
|
16868
|
+
provenance: lastProv,
|
|
16869
|
+
confidence: result.confidence
|
|
16870
|
+
}
|
|
16871
|
+
];
|
|
16872
|
+
const candidates = result.candidates ?? [];
|
|
16873
|
+
for (const c of candidates.slice(0, 3)) {
|
|
16874
|
+
facts.push({
|
|
16875
|
+
text: `Candidate ${c.node} \u2014 ${c.classification}: ${c.reason}`,
|
|
16876
|
+
...c.provenance ? { provenance: c.provenance } : {},
|
|
16877
|
+
confidence: c.confidence
|
|
16878
|
+
});
|
|
16879
|
+
}
|
|
16880
|
+
if (result.traversalPath.length > 1) {
|
|
16881
|
+
facts.push({ text: `Traversal: ${result.traversalPath.join(" \u2190 ")}` });
|
|
16882
|
+
}
|
|
16883
|
+
if (result.fixRecommendation) {
|
|
16884
|
+
facts.push({ text: `Recommended fix: ${result.fixRecommendation}` });
|
|
16885
|
+
}
|
|
16886
|
+
return { heading: "Root cause (navigation)", facts: facts.slice(0, MAX_FACTS_PER_SECTION) };
|
|
16887
|
+
}
|
|
16888
|
+
function buildDependenciesSection(graph, node) {
|
|
16889
|
+
const result = getTransitiveDependencies(graph, node, 2);
|
|
16890
|
+
if (result.total === 0) return null;
|
|
16891
|
+
const facts = result.dependencies.slice(0, MAX_FACTS_PER_SECTION).map((d) => ({
|
|
16892
|
+
text: `${d.nodeId} \u2014 ${d.edgeType} (distance ${d.distance})`,
|
|
16893
|
+
// Per-fact provenance: the transitive walk mixes EXTRACTED and OBSERVED
|
|
16894
|
+
// edges, so the tag is on the fact, not asserted for the whole section.
|
|
16895
|
+
provenance: d.provenance
|
|
16896
|
+
}));
|
|
16897
|
+
return { heading: "Dependencies", facts };
|
|
16898
|
+
}
|
|
16899
|
+
function buildObservedSection(graph, node) {
|
|
16900
|
+
const result = getObservedDependencies(graph, node);
|
|
16901
|
+
const facts = [];
|
|
16902
|
+
for (const e of result.dependencies.slice(0, MAX_FACTS_PER_SECTION)) {
|
|
16903
|
+
const via = e.source !== node ? ` via ${e.source}` : "";
|
|
16904
|
+
facts.push({
|
|
16905
|
+
text: `calls ${e.target}${via}${edgeSignalNote(e)}`,
|
|
16906
|
+
provenance: e.provenance,
|
|
16907
|
+
confidence: confidenceForEdge(e)
|
|
16908
|
+
});
|
|
16909
|
+
}
|
|
16910
|
+
if (facts.length === 0) {
|
|
16911
|
+
if (result.observed && result.inboundObservedCount > 0) {
|
|
16912
|
+
facts.push({
|
|
16913
|
+
text: `no outbound runtime calls, but OTel observed ${result.inboundObservedCount} inbound call path${result.inboundObservedCount === 1 ? "" : "s"} \u2014 a pure receiver`,
|
|
16914
|
+
provenance: import_types59.Provenance.OBSERVED
|
|
16915
|
+
});
|
|
16916
|
+
} else {
|
|
16917
|
+
return null;
|
|
16918
|
+
}
|
|
16919
|
+
}
|
|
16920
|
+
return { heading: "Runtime dependencies (OBSERVED)", facts };
|
|
16921
|
+
}
|
|
16922
|
+
function buildBlastSection(graph, node) {
|
|
16923
|
+
const result = getBlastRadius(graph, node);
|
|
16924
|
+
if (result.totalAffected === 0) return null;
|
|
16925
|
+
const facts = result.affectedNodes.slice(0, MAX_FACTS_PER_SECTION).map((n) => ({
|
|
16926
|
+
text: `${n.nodeId} (distance ${n.distance})`,
|
|
16927
|
+
provenance: n.edgeProvenance,
|
|
16928
|
+
confidence: n.confidence
|
|
16929
|
+
}));
|
|
16930
|
+
return {
|
|
16931
|
+
heading: `Blast radius \u2014 ${result.totalAffected} dependent${result.totalAffected === 1 ? "" : "s"}`,
|
|
16932
|
+
facts
|
|
16933
|
+
};
|
|
16934
|
+
}
|
|
16935
|
+
function buildIncidentsSection(node, incidents) {
|
|
16936
|
+
const relevant = incidentsForNode(node, incidents);
|
|
16937
|
+
if (relevant.length === 0) return null;
|
|
16938
|
+
const ordered = [...relevant].sort((a, b) => b.timestamp.localeCompare(a.timestamp)).slice(0, 4);
|
|
16939
|
+
const facts = ordered.map((ev) => ({
|
|
16940
|
+
text: `${ev.timestamp} \u2014 ${ev.service}: ${ev.errorMessage}`,
|
|
16941
|
+
// ErrorEvents are observation records — OBSERVED by definition.
|
|
16942
|
+
provenance: import_types59.Provenance.OBSERVED
|
|
16943
|
+
}));
|
|
16944
|
+
return { heading: `Recent incidents (OBSERVED) \u2014 ${relevant.length} recorded`, facts };
|
|
16945
|
+
}
|
|
16946
|
+
function divergenceLine(d) {
|
|
16947
|
+
if ((d.type === "missing-observed" || d.type === "missing-extracted") && d.column) {
|
|
16948
|
+
return `[${d.type}] ${d.table ?? d.source} column ${d.column} \u2014 ${d.reason}`;
|
|
16949
|
+
}
|
|
16950
|
+
return `[${d.type}] ${d.source} \u2192 ${d.target} \u2014 ${d.reason}`;
|
|
16951
|
+
}
|
|
16952
|
+
function buildDivergenceSection(graph, node) {
|
|
16953
|
+
const result = computeDivergences(graph, { node });
|
|
16954
|
+
if (result.totalAffected === 0) return null;
|
|
16955
|
+
const facts = result.divergences.slice(0, MAX_FACTS_PER_SECTION).map((d) => ({
|
|
16956
|
+
text: divergenceLine(d),
|
|
16957
|
+
confidence: d.confidence
|
|
16958
|
+
// A divergence is composite by construction (EXTRACTED vs OBSERVED), so it
|
|
16959
|
+
// carries no single provenance — the fact leaves it unset.
|
|
16960
|
+
}));
|
|
16961
|
+
return {
|
|
16962
|
+
heading: `Divergences (EXTRACTED vs OBSERVED) \u2014 ${result.totalAffected}`,
|
|
16963
|
+
facts
|
|
16964
|
+
};
|
|
16965
|
+
}
|
|
16966
|
+
function buildGlobalDivergenceSection(graph) {
|
|
16967
|
+
const result = computeDivergences(graph);
|
|
16968
|
+
if (result.totalAffected === 0) {
|
|
16969
|
+
return {
|
|
16970
|
+
heading: "Divergences (EXTRACTED vs OBSERVED)",
|
|
16971
|
+
facts: [{ text: "None \u2014 declared code and observed runtime agree across the graph." }]
|
|
16972
|
+
};
|
|
16973
|
+
}
|
|
16974
|
+
const facts = result.divergences.slice(0, MAX_FACTS_PER_SECTION).map((d) => ({
|
|
16975
|
+
text: divergenceLine(d),
|
|
16976
|
+
confidence: d.confidence
|
|
16977
|
+
// Composite by construction (EXTRACTED vs OBSERVED), so no single provenance.
|
|
16978
|
+
}));
|
|
16979
|
+
return {
|
|
16980
|
+
heading: `Divergences (EXTRACTED vs OBSERVED) \u2014 ${result.totalAffected}`,
|
|
16981
|
+
facts
|
|
16982
|
+
};
|
|
16983
|
+
}
|
|
16984
|
+
function buildGlobalIncidentsSection(incidents) {
|
|
16985
|
+
if (!incidents || incidents.length === 0) {
|
|
16986
|
+
return {
|
|
16987
|
+
heading: "Incidents across the system",
|
|
16988
|
+
facts: [{ text: "None recorded \u2014 the OBSERVED incident store is empty." }]
|
|
16989
|
+
};
|
|
16990
|
+
}
|
|
16991
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
16992
|
+
for (const ev of incidents) {
|
|
16993
|
+
const key = ev.affectedNode || (0, import_types59.serviceId)(ev.service);
|
|
16994
|
+
const cur = byKey.get(key);
|
|
16995
|
+
if (!cur) {
|
|
16996
|
+
byKey.set(key, { key, count: 1, latest: ev.timestamp, sampleMsg: ev.errorMessage });
|
|
16997
|
+
} else {
|
|
16998
|
+
cur.count += 1;
|
|
16999
|
+
if (ev.timestamp > cur.latest) {
|
|
17000
|
+
cur.latest = ev.timestamp;
|
|
17001
|
+
cur.sampleMsg = ev.errorMessage;
|
|
17002
|
+
}
|
|
17003
|
+
}
|
|
17004
|
+
}
|
|
17005
|
+
const rows = [...byKey.values()].sort((a, b) => b.count - a.count || b.latest.localeCompare(a.latest) || a.key.localeCompare(b.key)).slice(0, MAX_FACTS_PER_SECTION);
|
|
17006
|
+
const facts = rows.map((r) => ({
|
|
17007
|
+
text: `${r.key} \u2014 ${r.count} incident${r.count === 1 ? "" : "s"}, latest ${r.latest}: ${r.sampleMsg}`,
|
|
17008
|
+
provenance: import_types59.Provenance.OBSERVED
|
|
17009
|
+
}));
|
|
17010
|
+
return {
|
|
17011
|
+
heading: `Incidents across the system \u2014 ${incidents.length} recorded`,
|
|
17012
|
+
facts
|
|
17013
|
+
};
|
|
17014
|
+
}
|
|
17015
|
+
function buildOverviewSections(graph, incidents) {
|
|
17016
|
+
const nodeByType = /* @__PURE__ */ new Map();
|
|
17017
|
+
const services = [];
|
|
17018
|
+
graph.forEachNode((_id, attrs) => {
|
|
17019
|
+
const node = attrs;
|
|
17020
|
+
nodeByType.set(node.type, (nodeByType.get(node.type) ?? 0) + 1);
|
|
17021
|
+
if (node.type === import_types59.NodeType.ServiceNode) services.push(node.id);
|
|
17022
|
+
});
|
|
17023
|
+
const edgeByProv = /* @__PURE__ */ new Map();
|
|
17024
|
+
graph.forEachEdge((_id, attrs) => {
|
|
17025
|
+
const p = attrs.provenance;
|
|
17026
|
+
edgeByProv.set(p, (edgeByProv.get(p) ?? 0) + 1);
|
|
17027
|
+
});
|
|
17028
|
+
const sections = [];
|
|
17029
|
+
const count = (t) => nodeByType.get(t) ?? 0;
|
|
17030
|
+
const shapeFacts = [
|
|
17031
|
+
{ text: `${graph.order} nodes, ${graph.size} edges` },
|
|
17032
|
+
{
|
|
17033
|
+
text: `${count(import_types59.NodeType.ServiceNode)} services, ${count(import_types59.NodeType.FileNode)} files, ${count(import_types59.NodeType.SymbolNode)} symbols, ${count(import_types59.NodeType.DatabaseNode)} databases`
|
|
17034
|
+
}
|
|
17035
|
+
];
|
|
17036
|
+
for (const p of [import_types59.Provenance.EXTRACTED, import_types59.Provenance.OBSERVED, import_types59.Provenance.INFERRED, import_types59.Provenance.STALE]) {
|
|
17037
|
+
const n = edgeByProv.get(p) ?? 0;
|
|
17038
|
+
if (n > 0) shapeFacts.push({ text: `${n} ${p} edge${n === 1 ? "" : "s"}`, provenance: p });
|
|
17039
|
+
}
|
|
17040
|
+
sections.push({ heading: "System shape", facts: shapeFacts });
|
|
17041
|
+
const depCounts = services.map((s) => ({ s, n: getTransitiveDependencies(graph, s, 1).total })).filter((r) => r.n > 0).sort((a, b) => b.n - a.n || a.s.localeCompare(b.s)).slice(0, MAX_FACTS_PER_SECTION);
|
|
17042
|
+
if (depCounts.length > 0) {
|
|
17043
|
+
sections.push({
|
|
17044
|
+
heading: "Busiest services (by direct dependencies)",
|
|
17045
|
+
facts: depCounts.map((r) => ({
|
|
17046
|
+
text: `${r.s} \u2014 ${r.n} direct dependenc${r.n === 1 ? "y" : "ies"}`
|
|
17047
|
+
}))
|
|
17048
|
+
});
|
|
17049
|
+
}
|
|
17050
|
+
if (incidents && incidents.length > 0) {
|
|
17051
|
+
const incCount = /* @__PURE__ */ new Map();
|
|
17052
|
+
for (const ev of incidents) {
|
|
17053
|
+
const key = ev.affectedNode || (0, import_types59.serviceId)(ev.service);
|
|
17054
|
+
incCount.set(key, (incCount.get(key) ?? 0) + 1);
|
|
17055
|
+
}
|
|
17056
|
+
const top = [...incCount.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, MAX_FACTS_PER_SECTION);
|
|
17057
|
+
sections.push({
|
|
17058
|
+
heading: `Nodes with incidents \u2014 ${incidents.length} recorded`,
|
|
17059
|
+
facts: top.map(([k, n]) => ({
|
|
17060
|
+
text: `${k} \u2014 ${n} incident${n === 1 ? "" : "s"}`,
|
|
17061
|
+
provenance: import_types59.Provenance.OBSERVED
|
|
17062
|
+
}))
|
|
17063
|
+
});
|
|
17064
|
+
}
|
|
17065
|
+
const div = computeDivergences(graph);
|
|
17066
|
+
sections.push({
|
|
17067
|
+
heading: "Divergences",
|
|
17068
|
+
facts: [
|
|
17069
|
+
{
|
|
17070
|
+
text: div.totalAffected === 0 ? "None \u2014 declared code and observed runtime agree across the graph." : `${div.totalAffected} divergence${div.totalAffected === 1 ? "" : "s"} between declared code and observed runtime \u2014 ask "are there any divergences?" for the list.`
|
|
17071
|
+
}
|
|
17072
|
+
]
|
|
17073
|
+
});
|
|
17074
|
+
return sections;
|
|
17075
|
+
}
|
|
17076
|
+
function buildGlobalSections(intent, graph, incidents) {
|
|
17077
|
+
switch (intent) {
|
|
17078
|
+
case "divergence":
|
|
17079
|
+
return [buildGlobalDivergenceSection(graph)];
|
|
17080
|
+
case "incidents":
|
|
17081
|
+
return [buildGlobalIncidentsSection(incidents)];
|
|
17082
|
+
case "overview":
|
|
17083
|
+
return buildOverviewSections(graph, incidents);
|
|
17084
|
+
default:
|
|
17085
|
+
return null;
|
|
17086
|
+
}
|
|
17087
|
+
}
|
|
17088
|
+
var SECTION_ORDER = {
|
|
17089
|
+
"root-cause": ["root-cause", "incidents", "blast", "observed", "divergence"],
|
|
17090
|
+
"blast-radius": ["blast", "dependencies", "observed", "incidents"],
|
|
17091
|
+
dependencies: ["dependencies", "observed", "blast"],
|
|
17092
|
+
observed: ["observed", "dependencies", "incidents"],
|
|
17093
|
+
incidents: ["incidents", "root-cause", "observed"],
|
|
17094
|
+
divergence: ["divergence", "observed", "dependencies", "incidents"],
|
|
17095
|
+
overview: ["dependencies", "observed", "blast", "incidents", "divergence"]
|
|
17096
|
+
};
|
|
17097
|
+
function buildSection(kind, graph, node, incidents, now) {
|
|
17098
|
+
switch (kind) {
|
|
17099
|
+
case "root-cause":
|
|
17100
|
+
return buildRootCauseSection(graph, node, incidents, now);
|
|
17101
|
+
case "dependencies":
|
|
17102
|
+
return buildDependenciesSection(graph, node);
|
|
17103
|
+
case "observed":
|
|
17104
|
+
return buildObservedSection(graph, node);
|
|
17105
|
+
case "blast":
|
|
17106
|
+
return buildBlastSection(graph, node);
|
|
17107
|
+
case "incidents":
|
|
17108
|
+
return buildIncidentsSection(node, incidents);
|
|
17109
|
+
case "divergence":
|
|
17110
|
+
return buildDivergenceSection(graph, node);
|
|
17111
|
+
}
|
|
17112
|
+
}
|
|
17113
|
+
function summarizeGlobal(intent, sections) {
|
|
17114
|
+
const lead = sections[0];
|
|
17115
|
+
switch (intent) {
|
|
17116
|
+
case "divergence": {
|
|
17117
|
+
const none = lead?.facts[0]?.text.startsWith("None") ?? true;
|
|
17118
|
+
return none ? "No divergences across the graph \u2014 declared code and observed runtime agree." : `${lead.heading} across the graph, highest-confidence first below.`;
|
|
17119
|
+
}
|
|
17120
|
+
case "incidents": {
|
|
17121
|
+
const none = lead?.facts[0]?.text.startsWith("None") ?? true;
|
|
17122
|
+
return none ? "No incidents recorded across the system \u2014 the OBSERVED incident store is empty." : `${lead.heading}, aggregated by node below.`;
|
|
17123
|
+
}
|
|
17124
|
+
case "overview":
|
|
17125
|
+
default:
|
|
17126
|
+
return `System overview across the whole graph: ${sections.length} view${sections.length === 1 ? "" : "s"} below \u2014 shape (nodes/edges by provenance), busiest services, incidents, and divergences.`;
|
|
17127
|
+
}
|
|
17128
|
+
}
|
|
17129
|
+
function summarize(question, intent, matched, primary, sections, scope) {
|
|
17130
|
+
if (scope === "global") {
|
|
17131
|
+
return summarizeGlobal(intent, sections);
|
|
17132
|
+
}
|
|
17133
|
+
if (!primary) {
|
|
17134
|
+
return `Nothing in "${question}" resolved to a node in the graph. Name a service, file, route, or table \u2014 e.g. \`ask "what does <service> depend on?"\`. For a graph-wide look, ask for an overview, divergences, or incidents.`;
|
|
17135
|
+
}
|
|
17136
|
+
const lead = sections[0];
|
|
17137
|
+
const others = matched.slice(1);
|
|
17138
|
+
const alsoNote = others.length ? ` Also matched: ${others.map((m) => m.nodeId).join(", ")}.` : "";
|
|
17139
|
+
let core;
|
|
17140
|
+
switch (intent) {
|
|
17141
|
+
case "root-cause": {
|
|
17142
|
+
const rc = sections.find((s) => s.heading === "Root cause (navigation)");
|
|
17143
|
+
if (rc) {
|
|
17144
|
+
core = rc.facts[0]?.text ?? "";
|
|
17145
|
+
} else if (lead) {
|
|
17146
|
+
core = `No root cause surfaced for ${primary} \u2014 it may be healthy, or the failure isn't recorded. Nearest context: ${lead.heading.toLowerCase()}.`;
|
|
17147
|
+
} else {
|
|
17148
|
+
core = `No root cause surfaced for ${primary} \u2014 it may be healthy.`;
|
|
17149
|
+
}
|
|
17150
|
+
break;
|
|
17151
|
+
}
|
|
17152
|
+
case "blast-radius":
|
|
17153
|
+
core = lead ? `${lead.heading} of ${primary}.` : `${primary} has no dependents \u2014 nothing else would break if it failed.`;
|
|
17154
|
+
break;
|
|
17155
|
+
case "dependencies":
|
|
17156
|
+
core = lead ? `${primary}: ${lead.heading.toLowerCase()} listed below.` : `${primary} has no declared dependencies in the graph.`;
|
|
17157
|
+
break;
|
|
17158
|
+
case "observed":
|
|
17159
|
+
core = lead ? `${primary} at runtime: ${lead.facts.length} OBSERVED fact${lead.facts.length === 1 ? "" : "s"}.` : `No runtime traffic OBSERVED for ${primary}.`;
|
|
17160
|
+
break;
|
|
17161
|
+
case "incidents":
|
|
17162
|
+
core = lead ? `${primary}: ${lead.heading.toLowerCase()}.` : `No incidents recorded against ${primary}.`;
|
|
17163
|
+
break;
|
|
17164
|
+
case "divergence":
|
|
17165
|
+
core = lead ? `${lead.heading} involving ${primary}.` : `No divergences involve ${primary} \u2014 declared and observed agree here.`;
|
|
17166
|
+
break;
|
|
17167
|
+
default:
|
|
17168
|
+
core = `${primary}: ${sections.length} view${sections.length === 1 ? "" : "s"} of its fused local context below.`;
|
|
17169
|
+
}
|
|
17170
|
+
return `${core}${alsoNote}`;
|
|
17171
|
+
}
|
|
17172
|
+
async function askGraph(graph, question, opts = {}) {
|
|
17173
|
+
const now = opts.now ?? Date.now();
|
|
17174
|
+
const maxNodes = opts.maxNodes ?? DEFAULT_MAX_NODES;
|
|
17175
|
+
const intent = classifyIntent(question);
|
|
17176
|
+
const matched = await resolveEntities(graph, question, opts.searchIndex, maxNodes);
|
|
17177
|
+
const primary = matched[0]?.nodeId;
|
|
17178
|
+
const sections = [];
|
|
17179
|
+
let scope;
|
|
17180
|
+
if (primary) {
|
|
17181
|
+
scope = "node";
|
|
17182
|
+
for (const kind of SECTION_ORDER[intent]) {
|
|
17183
|
+
const s = buildSection(kind, graph, primary, opts.incidents, now);
|
|
17184
|
+
if (s && s.facts.length > 0) sections.push(s);
|
|
17185
|
+
}
|
|
17186
|
+
} else {
|
|
17187
|
+
const global = buildGlobalSections(intent, graph, opts.incidents);
|
|
17188
|
+
if (global) {
|
|
17189
|
+
scope = "global";
|
|
17190
|
+
for (const s of global) if (s.facts.length > 0) sections.push(s);
|
|
17191
|
+
}
|
|
17192
|
+
}
|
|
17193
|
+
const answer = summarize(question, intent, matched, primary, sections, scope);
|
|
17194
|
+
const provSet = /* @__PURE__ */ new Set();
|
|
17195
|
+
for (const s of sections) for (const f of s.facts) if (f.provenance) provSet.add(f.provenance);
|
|
17196
|
+
const confidence = sections[0]?.facts[0]?.confidence;
|
|
17197
|
+
return import_types59.AskResultSchema.parse({
|
|
17198
|
+
question,
|
|
17199
|
+
intent,
|
|
17200
|
+
matched,
|
|
17201
|
+
...primary ? { primaryNode: primary } : {},
|
|
17202
|
+
...scope ? { scope } : {},
|
|
17203
|
+
sections,
|
|
17204
|
+
answer,
|
|
17205
|
+
...confidence !== void 0 ? { confidence } : {},
|
|
17206
|
+
provenance: [...provSet]
|
|
17207
|
+
});
|
|
17208
|
+
}
|
|
17209
|
+
|
|
16577
17210
|
// src/diff.ts
|
|
16578
17211
|
init_cjs_shims();
|
|
16579
17212
|
var import_node_fs36 = require("fs");
|
|
@@ -16656,7 +17289,7 @@ init_cjs_shims();
|
|
|
16656
17289
|
var import_node_fs37 = require("fs");
|
|
16657
17290
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
16658
17291
|
var import_node_path72 = __toESM(require("path"), 1);
|
|
16659
|
-
var
|
|
17292
|
+
var import_types60 = require("@neat.is/types");
|
|
16660
17293
|
var LOCK_TIMEOUT_MS = 5e3;
|
|
16661
17294
|
var LOCK_RETRY_MS = 50;
|
|
16662
17295
|
function neatHome() {
|
|
@@ -16858,10 +17491,10 @@ async function readRegistry() {
|
|
|
16858
17491
|
throw err;
|
|
16859
17492
|
}
|
|
16860
17493
|
const parsed = JSON.parse(raw);
|
|
16861
|
-
return
|
|
17494
|
+
return import_types60.RegistryFileSchema.parse(parsed);
|
|
16862
17495
|
}
|
|
16863
17496
|
async function writeRegistry(reg) {
|
|
16864
|
-
const validated =
|
|
17497
|
+
const validated = import_types60.RegistryFileSchema.parse(reg);
|
|
16865
17498
|
await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
|
|
16866
17499
|
}
|
|
16867
17500
|
async function getProject(name) {
|
|
@@ -17212,15 +17845,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
|
|
|
17212
17845
|
|
|
17213
17846
|
// src/connectors/index.ts
|
|
17214
17847
|
init_cjs_shims();
|
|
17215
|
-
var
|
|
17848
|
+
var import_types61 = require("@neat.is/types");
|
|
17216
17849
|
var NO_ENV = "unknown";
|
|
17217
17850
|
function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
17218
17851
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
17219
17852
|
const sites = [];
|
|
17220
17853
|
for (const edgeId of graph.inboundEdges(targetNodeId)) {
|
|
17221
17854
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
17222
|
-
if (edge.provenance !==
|
|
17223
|
-
const parsed = (0,
|
|
17855
|
+
if (edge.provenance !== import_types61.Provenance.EXTRACTED) continue;
|
|
17856
|
+
const parsed = (0, import_types61.parseFileId)(edge.source);
|
|
17224
17857
|
if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
|
|
17225
17858
|
const site = { relPath: edge.evidence.file };
|
|
17226
17859
|
if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
|
|
@@ -17231,7 +17864,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
|
17231
17864
|
function routeCallSiteFor(graph, targetNodeId) {
|
|
17232
17865
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
17233
17866
|
const attrs = graph.getNodeAttributes(targetNodeId);
|
|
17234
|
-
if (attrs.type !==
|
|
17867
|
+
if (attrs.type !== import_types61.NodeType.RouteNode || !attrs.path) return void 0;
|
|
17235
17868
|
const site = { relPath: attrs.path };
|
|
17236
17869
|
if (attrs.line !== void 0) site.line = attrs.line;
|
|
17237
17870
|
return site;
|
|
@@ -17843,23 +18476,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
|
|
|
17843
18476
|
|
|
17844
18477
|
// src/connectors/supabase/resolve.ts
|
|
17845
18478
|
init_cjs_shims();
|
|
17846
|
-
var
|
|
18479
|
+
var import_types63 = require("@neat.is/types");
|
|
17847
18480
|
function createSupabaseResolveTarget(graph, config) {
|
|
17848
18481
|
return (signal, _ctx) => {
|
|
17849
18482
|
if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
|
|
17850
18483
|
return null;
|
|
17851
18484
|
}
|
|
17852
|
-
const subResourceId = (0,
|
|
18485
|
+
const subResourceId = (0, import_types63.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
|
|
17853
18486
|
if (graph.hasNode(subResourceId)) {
|
|
17854
|
-
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType:
|
|
18487
|
+
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types63.EdgeType.CALLS };
|
|
17855
18488
|
}
|
|
17856
|
-
const bareResourceId = (0,
|
|
18489
|
+
const bareResourceId = (0, import_types63.infraId)(signal.targetKind, signal.targetName);
|
|
17857
18490
|
if (graph.hasNode(bareResourceId)) {
|
|
17858
|
-
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType:
|
|
18491
|
+
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types63.EdgeType.CALLS };
|
|
17859
18492
|
}
|
|
17860
|
-
const projectLevelId = (0,
|
|
18493
|
+
const projectLevelId = (0, import_types63.infraId)("supabase", config.nodeRef);
|
|
17861
18494
|
if (graph.hasNode(projectLevelId)) {
|
|
17862
|
-
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType:
|
|
18495
|
+
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types63.EdgeType.CALLS };
|
|
17863
18496
|
}
|
|
17864
18497
|
return null;
|
|
17865
18498
|
};
|
|
@@ -17952,7 +18585,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
|
|
|
17952
18585
|
|
|
17953
18586
|
// src/connectors/railway/index.ts
|
|
17954
18587
|
init_cjs_shims();
|
|
17955
|
-
var
|
|
18588
|
+
var import_types67 = require("@neat.is/types");
|
|
17956
18589
|
|
|
17957
18590
|
// src/connectors/railway/client.ts
|
|
17958
18591
|
init_cjs_shims();
|
|
@@ -18103,7 +18736,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
|
|
|
18103
18736
|
const out = [];
|
|
18104
18737
|
graph.forEachNode((_id, attrs) => {
|
|
18105
18738
|
const node = attrs;
|
|
18106
|
-
if (node.type !==
|
|
18739
|
+
if (node.type !== import_types67.NodeType.RouteNode) return;
|
|
18107
18740
|
const route = attrs;
|
|
18108
18741
|
if (route.service !== serviceName) return;
|
|
18109
18742
|
out.push({
|
|
@@ -18207,12 +18840,12 @@ function createRailwayResolveTarget(config) {
|
|
|
18207
18840
|
const serviceName = config.serviceNameById[config.serviceId];
|
|
18208
18841
|
if (!serviceName) return null;
|
|
18209
18842
|
if (signal.targetKind === ROUTE_TARGET_KIND) {
|
|
18210
|
-
return { targetNodeId: signal.targetName, serviceName, edgeType:
|
|
18843
|
+
return { targetNodeId: signal.targetName, serviceName, edgeType: import_types67.EdgeType.CALLS };
|
|
18211
18844
|
}
|
|
18212
18845
|
if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
|
|
18213
18846
|
const peerName = config.serviceNameById[signal.targetName];
|
|
18214
18847
|
if (!peerName) return null;
|
|
18215
|
-
return { targetNodeId: (0,
|
|
18848
|
+
return { targetNodeId: (0, import_types67.serviceId)(peerName), serviceName, edgeType: import_types67.EdgeType.CONNECTS_TO };
|
|
18216
18849
|
}
|
|
18217
18850
|
return null;
|
|
18218
18851
|
};
|
|
@@ -18400,7 +19033,7 @@ function mapLogEntriesToSignals(entries) {
|
|
|
18400
19033
|
|
|
18401
19034
|
// src/connectors/firebase/resolve.ts
|
|
18402
19035
|
init_cjs_shims();
|
|
18403
|
-
var
|
|
19036
|
+
var import_types68 = require("@neat.is/types");
|
|
18404
19037
|
function neatServiceNameFor(resourceType, resourceName, serviceMap) {
|
|
18405
19038
|
switch (resourceType) {
|
|
18406
19039
|
case "cloud_function":
|
|
@@ -18415,7 +19048,7 @@ function routeEntriesFor(graph, serviceName) {
|
|
|
18415
19048
|
const entries = [];
|
|
18416
19049
|
graph.forEachNode((_id, attrs) => {
|
|
18417
19050
|
const node = attrs;
|
|
18418
|
-
if (node.type !==
|
|
19051
|
+
if (node.type !== import_types68.NodeType.RouteNode) return;
|
|
18419
19052
|
const route = attrs;
|
|
18420
19053
|
if (route.service !== serviceName) return;
|
|
18421
19054
|
entries.push({
|
|
@@ -18447,7 +19080,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
|
|
|
18447
19080
|
return {
|
|
18448
19081
|
targetNodeId: match.routeNodeId,
|
|
18449
19082
|
serviceName,
|
|
18450
|
-
edgeType:
|
|
19083
|
+
edgeType: import_types68.EdgeType.CALLS
|
|
18451
19084
|
};
|
|
18452
19085
|
};
|
|
18453
19086
|
}
|
|
@@ -18474,7 +19107,7 @@ init_cjs_shims();
|
|
|
18474
19107
|
|
|
18475
19108
|
// src/connectors/cloudflare/connector.ts
|
|
18476
19109
|
init_cjs_shims();
|
|
18477
|
-
var
|
|
19110
|
+
var import_types70 = require("@neat.is/types");
|
|
18478
19111
|
|
|
18479
19112
|
// src/connectors/cloudflare/client.ts
|
|
18480
19113
|
init_cjs_shims();
|
|
@@ -18638,7 +19271,7 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
18638
19271
|
graph.forEachNode((id, attrs) => {
|
|
18639
19272
|
if (found) return;
|
|
18640
19273
|
const a = attrs;
|
|
18641
|
-
if (a.type ===
|
|
19274
|
+
if (a.type === import_types70.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
|
|
18642
19275
|
found = id;
|
|
18643
19276
|
}
|
|
18644
19277
|
});
|
|
@@ -18650,7 +19283,7 @@ function findMatchingRouteNode(graph, serviceName, method, path78) {
|
|
|
18650
19283
|
graph.forEachNode((id, attrs) => {
|
|
18651
19284
|
if (found) return;
|
|
18652
19285
|
const a = attrs;
|
|
18653
|
-
if (a.type !==
|
|
19286
|
+
if (a.type !== import_types70.NodeType.RouteNode || a.service !== serviceName) return;
|
|
18654
19287
|
if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
|
|
18655
19288
|
const routeMethod = (a.method ?? "").toUpperCase();
|
|
18656
19289
|
if (routeMethod !== "ALL" && routeMethod !== method) return;
|
|
@@ -18669,11 +19302,11 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
18669
19302
|
};
|
|
18670
19303
|
const mapping = config.workers?.[scriptName];
|
|
18671
19304
|
if (mapping) {
|
|
18672
|
-
const wholeFileId = (0,
|
|
19305
|
+
const wholeFileId = (0, import_types70.fileId)(mapping.service, mapping.entryFile);
|
|
18673
19306
|
return {
|
|
18674
19307
|
targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
|
|
18675
19308
|
serviceName: mapping.service,
|
|
18676
|
-
edgeType:
|
|
19309
|
+
edgeType: import_types70.EdgeType.CALLS
|
|
18677
19310
|
};
|
|
18678
19311
|
}
|
|
18679
19312
|
const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
|
|
@@ -18682,13 +19315,13 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
18682
19315
|
return {
|
|
18683
19316
|
targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
|
|
18684
19317
|
serviceName: fileNode.service,
|
|
18685
|
-
edgeType:
|
|
19318
|
+
edgeType: import_types70.EdgeType.CALLS
|
|
18686
19319
|
};
|
|
18687
19320
|
}
|
|
18688
19321
|
return {
|
|
18689
|
-
targetNodeId: (0,
|
|
19322
|
+
targetNodeId: (0, import_types70.infraId)("cloudflare-worker", scriptName),
|
|
18690
19323
|
serviceName: scriptName,
|
|
18691
|
-
edgeType:
|
|
19324
|
+
edgeType: import_types70.EdgeType.CALLS,
|
|
18692
19325
|
ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
|
|
18693
19326
|
};
|
|
18694
19327
|
};
|
|
@@ -18884,14 +19517,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
|
|
|
18884
19517
|
|
|
18885
19518
|
// src/connectors/neon/resolve.ts
|
|
18886
19519
|
init_cjs_shims();
|
|
18887
|
-
var
|
|
19520
|
+
var import_types74 = require("@neat.is/types");
|
|
18888
19521
|
function createNeonResolveTarget(config) {
|
|
18889
19522
|
return (signal) => {
|
|
18890
19523
|
if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
|
|
18891
19524
|
return {
|
|
18892
|
-
targetNodeId: (0,
|
|
19525
|
+
targetNodeId: (0, import_types74.infraId)("sql-table", signal.targetName),
|
|
18893
19526
|
serviceName: config.serviceName,
|
|
18894
|
-
edgeType:
|
|
19527
|
+
edgeType: import_types74.EdgeType.CALLS,
|
|
18895
19528
|
ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
|
|
18896
19529
|
};
|
|
18897
19530
|
};
|
|
@@ -19072,14 +19705,14 @@ function mapLogEntriesToSignals2(entries) {
|
|
|
19072
19705
|
|
|
19073
19706
|
// src/connectors/cloud-run/resolve.ts
|
|
19074
19707
|
init_cjs_shims();
|
|
19075
|
-
var
|
|
19708
|
+
var import_types78 = require("@neat.is/types");
|
|
19076
19709
|
var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
|
|
19077
19710
|
function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
|
|
19078
19711
|
let found = null;
|
|
19079
19712
|
graph.forEachNode((_id, attrs) => {
|
|
19080
19713
|
if (found) return;
|
|
19081
19714
|
const node = attrs;
|
|
19082
|
-
if (node.type !==
|
|
19715
|
+
if (node.type !== import_types78.NodeType.RouteNode) return;
|
|
19083
19716
|
const route = attrs;
|
|
19084
19717
|
if (route.service !== serviceName || !route.pathTemplate) return;
|
|
19085
19718
|
if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
|
|
@@ -19104,13 +19737,13 @@ function createCloudRunResolveTarget(graph, config) {
|
|
|
19104
19737
|
normalizePathTemplate(path78)
|
|
19105
19738
|
);
|
|
19106
19739
|
if (routeNodeId) {
|
|
19107
|
-
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType:
|
|
19740
|
+
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types78.EdgeType.CALLS };
|
|
19108
19741
|
}
|
|
19109
19742
|
}
|
|
19110
19743
|
return {
|
|
19111
|
-
targetNodeId: (0,
|
|
19744
|
+
targetNodeId: (0, import_types78.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
|
|
19112
19745
|
serviceName: mappedService ?? gcpServiceName,
|
|
19113
|
-
edgeType:
|
|
19746
|
+
edgeType: import_types78.EdgeType.CALLS,
|
|
19114
19747
|
ensureInfraNode: {
|
|
19115
19748
|
kind: CLOUD_RUN_SERVICE_INFRA_KIND,
|
|
19116
19749
|
name: gcpServiceName,
|
|
@@ -19151,7 +19784,7 @@ function createCloudRunConnector(graph, config = {}) {
|
|
|
19151
19784
|
|
|
19152
19785
|
// src/connectors/render/index.ts
|
|
19153
19786
|
init_cjs_shims();
|
|
19154
|
-
var
|
|
19787
|
+
var import_types81 = require("@neat.is/types");
|
|
19155
19788
|
|
|
19156
19789
|
// src/connectors/render/types.ts
|
|
19157
19790
|
init_cjs_shims();
|
|
@@ -19229,7 +19862,7 @@ function buildRenderRouteIndex(graph, serviceName) {
|
|
|
19229
19862
|
const out = [];
|
|
19230
19863
|
graph.forEachNode((_id, attrs) => {
|
|
19231
19864
|
const node = attrs;
|
|
19232
|
-
if (node.type !==
|
|
19865
|
+
if (node.type !== import_types81.NodeType.RouteNode) return;
|
|
19233
19866
|
const route = attrs;
|
|
19234
19867
|
if (route.service !== serviceName) return;
|
|
19235
19868
|
out.push({
|
|
@@ -19314,7 +19947,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
|
|
|
19314
19947
|
function createRenderResolveTarget(config) {
|
|
19315
19948
|
return (signal) => {
|
|
19316
19949
|
if (signal.targetKind === ROUTE_TARGET_KIND2) {
|
|
19317
|
-
return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType:
|
|
19950
|
+
return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types81.EdgeType.CALLS };
|
|
19318
19951
|
}
|
|
19319
19952
|
return null;
|
|
19320
19953
|
};
|
|
@@ -19452,21 +20085,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
|
|
|
19452
20085
|
|
|
19453
20086
|
// src/connectors/planetscale/resolve.ts
|
|
19454
20087
|
init_cjs_shims();
|
|
19455
|
-
var
|
|
20088
|
+
var import_types85 = require("@neat.is/types");
|
|
19456
20089
|
var PLANETSCALE_DATABASE_KIND = "planetscale-database";
|
|
19457
20090
|
function createPlanetscaleResolveTarget(graph, config) {
|
|
19458
20091
|
const databaseName = `${config.organization}/${config.database}`;
|
|
19459
20092
|
return (signal, _ctx) => {
|
|
19460
20093
|
if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
|
|
19461
|
-
const tableId = (0,
|
|
20094
|
+
const tableId = (0, import_types85.infraId)("sql-table", signal.targetName);
|
|
19462
20095
|
if (graph.hasNode(tableId)) {
|
|
19463
|
-
return { targetNodeId: tableId, serviceName: config.serviceName, edgeType:
|
|
20096
|
+
return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types85.EdgeType.CALLS };
|
|
19464
20097
|
}
|
|
19465
|
-
const providerId = (0,
|
|
20098
|
+
const providerId = (0, import_types85.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
|
|
19466
20099
|
return {
|
|
19467
20100
|
targetNodeId: providerId,
|
|
19468
20101
|
serviceName: config.serviceName,
|
|
19469
|
-
edgeType:
|
|
20102
|
+
edgeType: import_types85.EdgeType.CALLS,
|
|
19470
20103
|
ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
|
|
19471
20104
|
};
|
|
19472
20105
|
};
|
|
@@ -19731,7 +20364,7 @@ function mapBuildsToSignals(builds, serviceName) {
|
|
|
19731
20364
|
|
|
19732
20365
|
// src/connectors/eas/resolve.ts
|
|
19733
20366
|
init_cjs_shims();
|
|
19734
|
-
var
|
|
20367
|
+
var import_types90 = require("@neat.is/types");
|
|
19735
20368
|
var NO_ENV2 = "unknown";
|
|
19736
20369
|
var EAS_JSON_PHASES = /* @__PURE__ */ new Set(["READ_EAS_JSON"]);
|
|
19737
20370
|
var APP_CONFIG_PHASES = /* @__PURE__ */ new Set([
|
|
@@ -19747,8 +20380,8 @@ function configBasenamesForPhase(phase) {
|
|
|
19747
20380
|
function configNodeService(graph, configNodeId) {
|
|
19748
20381
|
for (const edgeId of graph.inboundEdges(configNodeId)) {
|
|
19749
20382
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
19750
|
-
if (edge.type !==
|
|
19751
|
-
const parsed = (0,
|
|
20383
|
+
if (edge.type !== import_types90.EdgeType.CONFIGURED_BY) continue;
|
|
20384
|
+
const parsed = (0, import_types90.parseFileId)(edge.source);
|
|
19752
20385
|
if (parsed) return parsed.service;
|
|
19753
20386
|
}
|
|
19754
20387
|
return null;
|
|
@@ -19759,7 +20392,7 @@ function findConfigNode(graph, basenames, serviceName) {
|
|
|
19759
20392
|
graph.forEachNode((id, attrs) => {
|
|
19760
20393
|
if (scoped) return;
|
|
19761
20394
|
const node = attrs;
|
|
19762
|
-
if (node.type !==
|
|
20395
|
+
if (node.type !== import_types90.NodeType.ConfigNode) return;
|
|
19763
20396
|
if (typeof node.name !== "string" || !basenames.includes(node.name)) return;
|
|
19764
20397
|
if (anyMatch === null) anyMatch = id;
|
|
19765
20398
|
if (configNodeService(graph, id) === serviceName) scoped = id;
|
|
@@ -19776,13 +20409,13 @@ function createEasResolveTarget(graph) {
|
|
|
19776
20409
|
if (basenames.length > 0) {
|
|
19777
20410
|
const configNodeId = findConfigNode(graph, basenames, serviceName);
|
|
19778
20411
|
if (configNodeId) {
|
|
19779
|
-
return { targetNodeId: configNodeId, serviceName, edgeType:
|
|
20412
|
+
return { targetNodeId: configNodeId, serviceName, edgeType: import_types90.EdgeType.CALLS };
|
|
19780
20413
|
}
|
|
19781
20414
|
}
|
|
19782
20415
|
return {
|
|
19783
20416
|
targetNodeId: resolveFusedServiceId(graph, serviceName, NO_ENV2),
|
|
19784
20417
|
serviceName,
|
|
19785
|
-
edgeType:
|
|
20418
|
+
edgeType: import_types90.EdgeType.CALLS
|
|
19786
20419
|
};
|
|
19787
20420
|
};
|
|
19788
20421
|
}
|
|
@@ -20507,11 +21140,11 @@ function registerRoutes(scope, ctx) {
|
|
|
20507
21140
|
const candidates = req2.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
20508
21141
|
const parsed = [];
|
|
20509
21142
|
for (const c of candidates) {
|
|
20510
|
-
const r =
|
|
21143
|
+
const r = import_types93.DivergenceTypeSchema.safeParse(c);
|
|
20511
21144
|
if (!r.success) {
|
|
20512
21145
|
return reply.code(400).send({
|
|
20513
21146
|
error: `unknown divergence type "${c}"`,
|
|
20514
|
-
allowed:
|
|
21147
|
+
allowed: import_types93.DivergenceTypeSchema.options
|
|
20515
21148
|
});
|
|
20516
21149
|
}
|
|
20517
21150
|
parsed.push(r.data);
|
|
@@ -20753,6 +21386,18 @@ function registerRoutes(scope, ctx) {
|
|
|
20753
21386
|
matches: matches.slice(0, safeLimit)
|
|
20754
21387
|
};
|
|
20755
21388
|
});
|
|
21389
|
+
scope.get("/graph/ask", async (req2, reply) => {
|
|
21390
|
+
const proj = resolveProject(registry, req2, reply, ctx.bootstrap, ctx.singleProject);
|
|
21391
|
+
if (!proj) return;
|
|
21392
|
+
const question = (req2.query.q ?? "").trim();
|
|
21393
|
+
if (!question) return reply.code(400).send({ error: "query parameter `q` is required" });
|
|
21394
|
+
const epath = errorsPathFor(proj);
|
|
21395
|
+
const incidents = epath ? await readErrorEvents(epath) : [];
|
|
21396
|
+
return askGraph(proj.graph, question, {
|
|
21397
|
+
...proj.searchIndex ? { searchIndex: proj.searchIndex } : {},
|
|
21398
|
+
incidents
|
|
21399
|
+
});
|
|
21400
|
+
});
|
|
20756
21401
|
scope.get(
|
|
20757
21402
|
"/graph/diff",
|
|
20758
21403
|
async (req2, reply) => {
|
|
@@ -20853,7 +21498,7 @@ function registerRoutes(scope, ctx) {
|
|
|
20853
21498
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
20854
21499
|
let violations = await log.readAll();
|
|
20855
21500
|
if (req2.query.severity) {
|
|
20856
|
-
const sev =
|
|
21501
|
+
const sev = import_types93.PolicySeveritySchema.safeParse(req2.query.severity);
|
|
20857
21502
|
if (!sev.success) {
|
|
20858
21503
|
return reply.code(400).send({
|
|
20859
21504
|
error: "invalid severity",
|
|
@@ -20892,7 +21537,7 @@ function registerRoutes(scope, ctx) {
|
|
|
20892
21537
|
scope.post("/policies/check", async (req2, reply) => {
|
|
20893
21538
|
const proj = resolveProject(registry, req2, reply, ctx.bootstrap, ctx.singleProject);
|
|
20894
21539
|
if (!proj) return;
|
|
20895
|
-
const parsed =
|
|
21540
|
+
const parsed = import_types93.PoliciesCheckBodySchema.safeParse(req2.body ?? {});
|
|
20896
21541
|
if (!parsed.success) {
|
|
20897
21542
|
return reply.code(400).send({
|
|
20898
21543
|
error: "invalid /policies/check body",
|
|
@@ -21233,7 +21878,7 @@ function unroutedErrorsPath(neatHome4) {
|
|
|
21233
21878
|
}
|
|
21234
21879
|
|
|
21235
21880
|
// src/daemon.ts
|
|
21236
|
-
var
|
|
21881
|
+
var import_types94 = require("@neat.is/types");
|
|
21237
21882
|
function daemonJsonPath(scanPath) {
|
|
21238
21883
|
return import_node_path75.default.join(scanPath, "neat-out", "daemon.json");
|
|
21239
21884
|
}
|
|
@@ -21359,8 +22004,8 @@ function isTokenContained(needle, haystack) {
|
|
|
21359
22004
|
const n = needle.toLowerCase();
|
|
21360
22005
|
const h = haystack.toLowerCase();
|
|
21361
22006
|
if (!h.includes(n)) return false;
|
|
21362
|
-
const
|
|
21363
|
-
return
|
|
22007
|
+
const tokens2 = h.split(/[-_]/);
|
|
22008
|
+
return tokens2.includes(n);
|
|
21364
22009
|
}
|
|
21365
22010
|
function serviceNameMatchesProject(serviceName, project) {
|
|
21366
22011
|
if (serviceName.toLowerCase() === project.toLowerCase()) return true;
|
|
@@ -21372,7 +22017,7 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
|
|
|
21372
22017
|
if (!serviceName) return true;
|
|
21373
22018
|
if (serviceNameMatchesProject(serviceName, project)) return true;
|
|
21374
22019
|
return graph.someNode(
|
|
21375
|
-
(_id, attrs) => attrs.type ===
|
|
22020
|
+
(_id, attrs) => attrs.type === import_types94.NodeType.ServiceNode && attrs.name === serviceName
|
|
21376
22021
|
);
|
|
21377
22022
|
}
|
|
21378
22023
|
async function bootstrapProject(entry2, connectors = [], neatHome4) {
|