@neat.is/core 0.9.2-dev.20260821 → 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/index.cjs
CHANGED
|
@@ -1392,7 +1392,7 @@ function databaseRootCauseShape(graph, origin, walk10) {
|
|
|
1392
1392
|
for (const id of walk10.path) {
|
|
1393
1393
|
const owner = resolveOwningService(graph, id);
|
|
1394
1394
|
if (!owner) continue;
|
|
1395
|
-
const { id:
|
|
1395
|
+
const { id: serviceId16, svc } = owner;
|
|
1396
1396
|
const deps = svc.dependencies ?? {};
|
|
1397
1397
|
for (const pair of candidatePairs) {
|
|
1398
1398
|
const declared = deps[pair.driver];
|
|
@@ -1405,7 +1405,7 @@ function databaseRootCauseShape(graph, origin, walk10) {
|
|
|
1405
1405
|
);
|
|
1406
1406
|
if (!result.compatible) {
|
|
1407
1407
|
return {
|
|
1408
|
-
rootCauseNode:
|
|
1408
|
+
rootCauseNode: serviceId16,
|
|
1409
1409
|
rootCauseReason: result.reason ?? "incompatible driver",
|
|
1410
1410
|
...result.minDriverVersion ? {
|
|
1411
1411
|
fixRecommendation: `Upgrade ${svc.name} ${pair.driver} driver to >= ${result.minDriverVersion}`
|
|
@@ -1420,7 +1420,7 @@ function serviceRootCauseShape(graph, _origin, walk10) {
|
|
|
1420
1420
|
for (const id of walk10.path) {
|
|
1421
1421
|
const owner = resolveOwningService(graph, id);
|
|
1422
1422
|
if (!owner) continue;
|
|
1423
|
-
const { id:
|
|
1423
|
+
const { id: serviceId16, svc } = owner;
|
|
1424
1424
|
const deps = svc.dependencies ?? {};
|
|
1425
1425
|
const serviceNodeEngine = svc.nodeEngine;
|
|
1426
1426
|
for (const constraint of nodeEngineConstraints()) {
|
|
@@ -1429,7 +1429,7 @@ function serviceRootCauseShape(graph, _origin, walk10) {
|
|
|
1429
1429
|
const result = checkNodeEngineConstraint(constraint, declared, serviceNodeEngine);
|
|
1430
1430
|
if (!result.compatible && result.reason) {
|
|
1431
1431
|
return {
|
|
1432
|
-
rootCauseNode:
|
|
1432
|
+
rootCauseNode: serviceId16,
|
|
1433
1433
|
rootCauseReason: result.reason,
|
|
1434
1434
|
...result.requiredNodeVersion ? {
|
|
1435
1435
|
fixRecommendation: `Bump ${svc.name}'s engines.node to >= ${result.requiredNodeVersion}`
|
|
@@ -1444,7 +1444,7 @@ function serviceRootCauseShape(graph, _origin, walk10) {
|
|
|
1444
1444
|
const result = checkPackageConflict(conflict, declared, requiredDeclared);
|
|
1445
1445
|
if (!result.compatible && result.reason) {
|
|
1446
1446
|
return {
|
|
1447
|
-
rootCauseNode:
|
|
1447
|
+
rootCauseNode: serviceId16,
|
|
1448
1448
|
rootCauseReason: result.reason,
|
|
1449
1449
|
fixRecommendation: `Upgrade ${svc.name}'s ${conflict.requires.name} to >= ${conflict.requires.minVersion}`
|
|
1450
1450
|
};
|
|
@@ -1549,9 +1549,9 @@ function rootCauseFromIncidents(nodeId, incidents, errorEvent) {
|
|
|
1549
1549
|
function isFailingCallEdge(e) {
|
|
1550
1550
|
return e.type === import_types.EdgeType.CALLS && (e.signal?.errorCount ?? 0) > 0;
|
|
1551
1551
|
}
|
|
1552
|
-
function callSourcesForService(graph,
|
|
1553
|
-
const ids = [
|
|
1554
|
-
for (const edgeId of graph.outboundEdges(
|
|
1552
|
+
function callSourcesForService(graph, serviceId16) {
|
|
1553
|
+
const ids = [serviceId16];
|
|
1554
|
+
for (const edgeId of graph.outboundEdges(serviceId16)) {
|
|
1555
1555
|
const e = graph.getEdgeAttributes(edgeId);
|
|
1556
1556
|
if (e.type !== import_types.EdgeType.CONTAINS) continue;
|
|
1557
1557
|
const tgt = graph.getNodeAttributes(e.target);
|
|
@@ -1575,9 +1575,9 @@ function failingCallDominates(e, id, curEdge, curId) {
|
|
|
1575
1575
|
}
|
|
1576
1576
|
return id < curId;
|
|
1577
1577
|
}
|
|
1578
|
-
function dominantFailingCall(graph,
|
|
1578
|
+
function dominantFailingCall(graph, serviceId16, visited) {
|
|
1579
1579
|
let best = null;
|
|
1580
|
-
for (const src of callSourcesForService(graph,
|
|
1580
|
+
for (const src of callSourcesForService(graph, serviceId16)) {
|
|
1581
1581
|
for (const edgeId of graph.outboundEdges(src)) {
|
|
1582
1582
|
const e = graph.getEdgeAttributes(edgeId);
|
|
1583
1583
|
if (!isFailingCallEdge(e)) continue;
|
|
@@ -1616,9 +1616,9 @@ function staleCallDominates(e, id, curEdge, curId) {
|
|
|
1616
1616
|
if (ev !== cv) return ev > cv;
|
|
1617
1617
|
return id < curId;
|
|
1618
1618
|
}
|
|
1619
|
-
function dominantStaleCall(graph,
|
|
1619
|
+
function dominantStaleCall(graph, serviceId16, visited) {
|
|
1620
1620
|
const bestByCallee = /* @__PURE__ */ new Map();
|
|
1621
|
-
for (const src of callSourcesForService(graph,
|
|
1621
|
+
for (const src of callSourcesForService(graph, serviceId16)) {
|
|
1622
1622
|
for (const edgeId of graph.outboundEdges(src)) {
|
|
1623
1623
|
const e = graph.getEdgeAttributes(edgeId);
|
|
1624
1624
|
if (e.type !== import_types.EdgeType.CALLS) continue;
|
|
@@ -4817,11 +4817,11 @@ function actixAttributeRoute(attrItem, out) {
|
|
|
4817
4817
|
if (!nameNode) return;
|
|
4818
4818
|
const macro = nameNode.type === "identifier" ? nameNode.text : nameNode.type === "scoped_identifier" ? nameNode.childForFieldName("name")?.text ?? null : null;
|
|
4819
4819
|
if (!macro) return;
|
|
4820
|
-
const
|
|
4821
|
-
if (!
|
|
4820
|
+
const tokens2 = attr.childForFieldName("arguments");
|
|
4821
|
+
if (!tokens2 || tokens2.type !== "token_tree") return;
|
|
4822
4822
|
const strings = [];
|
|
4823
|
-
for (let i = 0; i <
|
|
4824
|
-
const s = rustStringContent(
|
|
4823
|
+
for (let i = 0; i < tokens2.namedChildCount; i++) {
|
|
4824
|
+
const s = rustStringContent(tokens2.namedChild(i));
|
|
4825
4825
|
if (s !== null) strings.push(s);
|
|
4826
4826
|
}
|
|
4827
4827
|
const pathStr = strings[0];
|
|
@@ -6715,29 +6715,29 @@ function promoteFrontierNodes(graph, opts = {}) {
|
|
|
6715
6715
|
toPromote.push({ frontierId: id, serviceId: target });
|
|
6716
6716
|
});
|
|
6717
6717
|
let promoted = 0;
|
|
6718
|
-
for (const { frontierId: frontierId2, serviceId:
|
|
6718
|
+
for (const { frontierId: frontierId2, serviceId: serviceId16 } of toPromote) {
|
|
6719
6719
|
if (opts.policies && opts.policies.length > 0 && opts.policyCtx) {
|
|
6720
6720
|
const gate = canPromoteFrontier(graph, frontierId2, opts.policies, opts.policyCtx);
|
|
6721
6721
|
if (!gate.allowed) {
|
|
6722
6722
|
continue;
|
|
6723
6723
|
}
|
|
6724
6724
|
}
|
|
6725
|
-
rewireFrontierEdges(graph, frontierId2,
|
|
6725
|
+
rewireFrontierEdges(graph, frontierId2, serviceId16);
|
|
6726
6726
|
graph.dropNode(frontierId2);
|
|
6727
6727
|
promoted++;
|
|
6728
6728
|
}
|
|
6729
6729
|
return promoted;
|
|
6730
6730
|
}
|
|
6731
|
-
function rewireFrontierEdges(graph, frontierId2,
|
|
6731
|
+
function rewireFrontierEdges(graph, frontierId2, serviceId16) {
|
|
6732
6732
|
const inbound = [...graph.inboundEdges(frontierId2)];
|
|
6733
6733
|
const outbound = [...graph.outboundEdges(frontierId2)];
|
|
6734
6734
|
for (const edgeId of inbound) {
|
|
6735
6735
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
6736
|
-
rebuildEdge(graph, edge, edge.source,
|
|
6736
|
+
rebuildEdge(graph, edge, edge.source, serviceId16, edgeId);
|
|
6737
6737
|
}
|
|
6738
6738
|
for (const edgeId of outbound) {
|
|
6739
6739
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
6740
|
-
rebuildEdge(graph, edge,
|
|
6740
|
+
rebuildEdge(graph, edge, serviceId16, edge.target, edgeId);
|
|
6741
6741
|
}
|
|
6742
6742
|
}
|
|
6743
6743
|
function rebuildEdge(graph, edge, newSource, newTarget, oldEdgeId) {
|
|
@@ -7852,9 +7852,9 @@ var K8S_KINDS_WITH_HOSTNAMES = /* @__PURE__ */ new Set([
|
|
|
7852
7852
|
"StatefulSet",
|
|
7853
7853
|
"DaemonSet"
|
|
7854
7854
|
]);
|
|
7855
|
-
function addAliases(graph,
|
|
7856
|
-
if (!graph.hasNode(
|
|
7857
|
-
const node = graph.getNodeAttributes(
|
|
7855
|
+
function addAliases(graph, serviceId16, candidates) {
|
|
7856
|
+
if (!graph.hasNode(serviceId16)) return;
|
|
7857
|
+
const node = graph.getNodeAttributes(serviceId16);
|
|
7858
7858
|
if (node.type !== import_types19.NodeType.ServiceNode) return;
|
|
7859
7859
|
const set = new Set(node.aliases ?? []);
|
|
7860
7860
|
for (const c of candidates) {
|
|
@@ -7864,7 +7864,7 @@ function addAliases(graph, serviceId15, candidates) {
|
|
|
7864
7864
|
}
|
|
7865
7865
|
if (set.size === 0) return;
|
|
7866
7866
|
const updated = { ...node, aliases: [...set].sort() };
|
|
7867
|
-
graph.replaceNodeAttributes(
|
|
7867
|
+
graph.replaceNodeAttributes(serviceId16, updated);
|
|
7868
7868
|
}
|
|
7869
7869
|
function indexServicesByName(services) {
|
|
7870
7870
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -7897,12 +7897,12 @@ async function collectComposeAliases(graph, scanPath, serviceIndex) {
|
|
|
7897
7897
|
}
|
|
7898
7898
|
if (!compose?.services) return;
|
|
7899
7899
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
7900
|
-
const
|
|
7901
|
-
if (!
|
|
7900
|
+
const serviceId16 = serviceIndex.get(composeName);
|
|
7901
|
+
if (!serviceId16) continue;
|
|
7902
7902
|
const aliases = /* @__PURE__ */ new Set([composeName]);
|
|
7903
7903
|
if (svc.container_name) aliases.add(svc.container_name);
|
|
7904
7904
|
if (svc.hostname) aliases.add(svc.hostname);
|
|
7905
|
-
addAliases(graph,
|
|
7905
|
+
addAliases(graph, serviceId16, aliases);
|
|
7906
7906
|
}
|
|
7907
7907
|
}
|
|
7908
7908
|
var LABEL_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -15839,7 +15839,7 @@ function startPersistLoop(graph, outPath, opts = {}) {
|
|
|
15839
15839
|
init_cjs_shims();
|
|
15840
15840
|
var import_fastify2 = __toESM(require("fastify"), 1);
|
|
15841
15841
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
15842
|
-
var
|
|
15842
|
+
var import_types93 = require("@neat.is/types");
|
|
15843
15843
|
|
|
15844
15844
|
// src/extend/index.ts
|
|
15845
15845
|
init_cjs_shims();
|
|
@@ -16562,6 +16562,639 @@ function queryLogEntries(opts) {
|
|
|
16562
16562
|
return merged;
|
|
16563
16563
|
}
|
|
16564
16564
|
|
|
16565
|
+
// src/ask.ts
|
|
16566
|
+
init_cjs_shims();
|
|
16567
|
+
var import_types59 = require("@neat.is/types");
|
|
16568
|
+
var DEFAULT_MAX_NODES = 3;
|
|
16569
|
+
var MAX_FACTS_PER_SECTION = 6;
|
|
16570
|
+
var INTENT_RULES = [
|
|
16571
|
+
{
|
|
16572
|
+
intent: "root-cause",
|
|
16573
|
+
test: /\b(why|root[\s-]?cause|failing|fail(?:s|ed)?|breaking|broke(?:n)?|crash(?:ing|ed)?|throw(?:ing|s)?|culprit|5\d\d)\b/
|
|
16574
|
+
},
|
|
16575
|
+
{
|
|
16576
|
+
intent: "blast-radius",
|
|
16577
|
+
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/
|
|
16578
|
+
},
|
|
16579
|
+
{
|
|
16580
|
+
intent: "divergence",
|
|
16581
|
+
test: /\b(diverg\w*|weird|mismatch|drift|out\s+of\s+sync|inconsistent|disagree\w*|declared\s+vs|anything\s+wrong)\b/
|
|
16582
|
+
},
|
|
16583
|
+
{
|
|
16584
|
+
intent: "incidents",
|
|
16585
|
+
test: /\b(incidents?|recent\s+(?:errors?|failures?)|error\s+history|failure\s+history)\b/
|
|
16586
|
+
},
|
|
16587
|
+
{
|
|
16588
|
+
intent: "observed",
|
|
16589
|
+
test: /\b(at\s+runtime|in\s+prod(?:uction)?|actually\s+call\w*|really\s+call\w*|observed|runtime\s+traffic)\b/
|
|
16590
|
+
},
|
|
16591
|
+
{
|
|
16592
|
+
intent: "dependencies",
|
|
16593
|
+
test: /\b(depend\w*|calls?|uses?|imports?|relies\s+on|needs?)\b/
|
|
16594
|
+
}
|
|
16595
|
+
];
|
|
16596
|
+
function classifyIntent(question) {
|
|
16597
|
+
const q = question.toLowerCase();
|
|
16598
|
+
for (const rule of INTENT_RULES) {
|
|
16599
|
+
if (rule.test.test(q)) return rule.intent;
|
|
16600
|
+
}
|
|
16601
|
+
return "overview";
|
|
16602
|
+
}
|
|
16603
|
+
var STOPWORDS = /* @__PURE__ */ new Set([
|
|
16604
|
+
"the",
|
|
16605
|
+
"a",
|
|
16606
|
+
"an",
|
|
16607
|
+
"is",
|
|
16608
|
+
"are",
|
|
16609
|
+
"was",
|
|
16610
|
+
"were",
|
|
16611
|
+
"be",
|
|
16612
|
+
"been",
|
|
16613
|
+
"being",
|
|
16614
|
+
"of",
|
|
16615
|
+
"to",
|
|
16616
|
+
"in",
|
|
16617
|
+
"on",
|
|
16618
|
+
"at",
|
|
16619
|
+
"by",
|
|
16620
|
+
"for",
|
|
16621
|
+
"with",
|
|
16622
|
+
"and",
|
|
16623
|
+
"or",
|
|
16624
|
+
"but",
|
|
16625
|
+
"if",
|
|
16626
|
+
"as",
|
|
16627
|
+
"this",
|
|
16628
|
+
"that",
|
|
16629
|
+
"these",
|
|
16630
|
+
"those",
|
|
16631
|
+
"it",
|
|
16632
|
+
"its",
|
|
16633
|
+
"do",
|
|
16634
|
+
"does",
|
|
16635
|
+
"did",
|
|
16636
|
+
"has",
|
|
16637
|
+
"have",
|
|
16638
|
+
"had",
|
|
16639
|
+
"what",
|
|
16640
|
+
"which",
|
|
16641
|
+
"who",
|
|
16642
|
+
"whom",
|
|
16643
|
+
"how",
|
|
16644
|
+
"when",
|
|
16645
|
+
"where",
|
|
16646
|
+
"my",
|
|
16647
|
+
"me",
|
|
16648
|
+
"i",
|
|
16649
|
+
"we",
|
|
16650
|
+
"you",
|
|
16651
|
+
"they",
|
|
16652
|
+
"them",
|
|
16653
|
+
"from",
|
|
16654
|
+
"into",
|
|
16655
|
+
"about",
|
|
16656
|
+
"would",
|
|
16657
|
+
"will",
|
|
16658
|
+
"can",
|
|
16659
|
+
"could",
|
|
16660
|
+
"should",
|
|
16661
|
+
"anything",
|
|
16662
|
+
"everything",
|
|
16663
|
+
"something",
|
|
16664
|
+
"all",
|
|
16665
|
+
"any",
|
|
16666
|
+
"some",
|
|
16667
|
+
"no",
|
|
16668
|
+
"not",
|
|
16669
|
+
"so",
|
|
16670
|
+
"up",
|
|
16671
|
+
"out",
|
|
16672
|
+
"over",
|
|
16673
|
+
"get",
|
|
16674
|
+
"got",
|
|
16675
|
+
"show",
|
|
16676
|
+
"tell",
|
|
16677
|
+
"explain",
|
|
16678
|
+
"find",
|
|
16679
|
+
"give",
|
|
16680
|
+
"happen",
|
|
16681
|
+
"happens",
|
|
16682
|
+
"happening",
|
|
16683
|
+
"here",
|
|
16684
|
+
"there",
|
|
16685
|
+
"system",
|
|
16686
|
+
"code",
|
|
16687
|
+
"service",
|
|
16688
|
+
"node",
|
|
16689
|
+
"graph"
|
|
16690
|
+
]);
|
|
16691
|
+
var INTENT_WORDS = /* @__PURE__ */ new Set([
|
|
16692
|
+
"why",
|
|
16693
|
+
"root",
|
|
16694
|
+
"cause",
|
|
16695
|
+
"failing",
|
|
16696
|
+
"fail",
|
|
16697
|
+
"fails",
|
|
16698
|
+
"failed",
|
|
16699
|
+
"breaking",
|
|
16700
|
+
"broke",
|
|
16701
|
+
"broken",
|
|
16702
|
+
"crash",
|
|
16703
|
+
"crashing",
|
|
16704
|
+
"crashed",
|
|
16705
|
+
"throw",
|
|
16706
|
+
"throwing",
|
|
16707
|
+
"throws",
|
|
16708
|
+
"culprit",
|
|
16709
|
+
"blast",
|
|
16710
|
+
"radius",
|
|
16711
|
+
"break",
|
|
16712
|
+
"breaks",
|
|
16713
|
+
"impact",
|
|
16714
|
+
"downstream",
|
|
16715
|
+
"dependent",
|
|
16716
|
+
"dependents",
|
|
16717
|
+
"redeploy",
|
|
16718
|
+
"affect",
|
|
16719
|
+
"affects",
|
|
16720
|
+
"affected",
|
|
16721
|
+
"diverge",
|
|
16722
|
+
"divergence",
|
|
16723
|
+
"divergences",
|
|
16724
|
+
"weird",
|
|
16725
|
+
"mismatch",
|
|
16726
|
+
"drift",
|
|
16727
|
+
"inconsistent",
|
|
16728
|
+
"disagree",
|
|
16729
|
+
"declared",
|
|
16730
|
+
"observed",
|
|
16731
|
+
"incident",
|
|
16732
|
+
"incidents",
|
|
16733
|
+
"recent",
|
|
16734
|
+
"errors",
|
|
16735
|
+
"error",
|
|
16736
|
+
"failures",
|
|
16737
|
+
"failure",
|
|
16738
|
+
"history",
|
|
16739
|
+
"runtime",
|
|
16740
|
+
"production",
|
|
16741
|
+
"prod",
|
|
16742
|
+
"traffic",
|
|
16743
|
+
"actually",
|
|
16744
|
+
"really",
|
|
16745
|
+
"depend",
|
|
16746
|
+
"depends",
|
|
16747
|
+
"dependency",
|
|
16748
|
+
"dependencies",
|
|
16749
|
+
"call",
|
|
16750
|
+
"calls",
|
|
16751
|
+
"use",
|
|
16752
|
+
"uses",
|
|
16753
|
+
"using",
|
|
16754
|
+
"import",
|
|
16755
|
+
"imports",
|
|
16756
|
+
"relies",
|
|
16757
|
+
"rely",
|
|
16758
|
+
"needs",
|
|
16759
|
+
"need",
|
|
16760
|
+
"sync"
|
|
16761
|
+
]);
|
|
16762
|
+
function tokens(input) {
|
|
16763
|
+
const spaced = input.replace(/([a-z0-9])([A-Z])/g, "$1 $2");
|
|
16764
|
+
return (spaced.toLowerCase().match(/[a-z0-9]+/g) ?? []).filter((t) => t.length >= 2);
|
|
16765
|
+
}
|
|
16766
|
+
function queryTokens(question) {
|
|
16767
|
+
const seen = /* @__PURE__ */ new Set();
|
|
16768
|
+
const out = [];
|
|
16769
|
+
for (const t of tokens(question)) {
|
|
16770
|
+
if (STOPWORDS.has(t) || INTENT_WORDS.has(t)) continue;
|
|
16771
|
+
if (seen.has(t)) continue;
|
|
16772
|
+
seen.add(t);
|
|
16773
|
+
out.push(t);
|
|
16774
|
+
}
|
|
16775
|
+
return out;
|
|
16776
|
+
}
|
|
16777
|
+
function nodeName(node) {
|
|
16778
|
+
return node.name ?? node.id;
|
|
16779
|
+
}
|
|
16780
|
+
function idBody(id) {
|
|
16781
|
+
const colon = id.indexOf(":");
|
|
16782
|
+
return colon >= 0 ? id.slice(colon + 1) : id;
|
|
16783
|
+
}
|
|
16784
|
+
var EMBED_MIN_SCORE = 0.35;
|
|
16785
|
+
async function resolveEntities(graph, question, searchIndex, maxNodes) {
|
|
16786
|
+
const qTokens = queryTokens(question);
|
|
16787
|
+
const normalized = question.toLowerCase();
|
|
16788
|
+
const best = /* @__PURE__ */ new Map();
|
|
16789
|
+
const consider = (cand) => {
|
|
16790
|
+
const cur = best.get(cand.nodeId);
|
|
16791
|
+
if (!cur || cand.score > cur.score) best.set(cand.nodeId, cand);
|
|
16792
|
+
};
|
|
16793
|
+
graph.forEachNode((id, attrs) => {
|
|
16794
|
+
const node = attrs;
|
|
16795
|
+
if (node.type === import_types59.NodeType.FrontierNode) return;
|
|
16796
|
+
const name = nodeName(node);
|
|
16797
|
+
const body = idBody(id);
|
|
16798
|
+
const labelTokens = /* @__PURE__ */ new Set([...tokens(body), ...tokens(name)]);
|
|
16799
|
+
if (labelTokens.size === 0) return;
|
|
16800
|
+
let matched = 0;
|
|
16801
|
+
for (const t of qTokens) if (labelTokens.has(t)) matched += 1;
|
|
16802
|
+
const named = body.length >= 2 && normalized.includes(body.toLowerCase()) || name.length >= 2 && normalized.includes(name.toLowerCase());
|
|
16803
|
+
if (matched === 0 && !named) return;
|
|
16804
|
+
const coverage = qTokens.length > 0 ? matched / qTokens.length : 0;
|
|
16805
|
+
const via = named ? "id" : matched > 0 && tokens(name).some((t) => qTokens.includes(t)) ? "label" : "token";
|
|
16806
|
+
const score = named ? Math.max(0.9, coverage) : Math.min(0.85, 0.3 + 0.7 * coverage);
|
|
16807
|
+
consider({ nodeId: id, label: name, via, score: Math.min(1, score) });
|
|
16808
|
+
});
|
|
16809
|
+
if (searchIndex) {
|
|
16810
|
+
try {
|
|
16811
|
+
const res = await searchIndex.search(question, 10);
|
|
16812
|
+
if (res.provider !== "substring") {
|
|
16813
|
+
for (const m of res.matches) {
|
|
16814
|
+
if (m.node.type === import_types59.NodeType.FrontierNode) continue;
|
|
16815
|
+
const already = best.get(m.node.id);
|
|
16816
|
+
if (!already && m.score < EMBED_MIN_SCORE) continue;
|
|
16817
|
+
consider({
|
|
16818
|
+
nodeId: m.node.id,
|
|
16819
|
+
label: nodeName(m.node),
|
|
16820
|
+
via: "embedding",
|
|
16821
|
+
score: Math.max(0, Math.min(1, m.score))
|
|
16822
|
+
});
|
|
16823
|
+
}
|
|
16824
|
+
}
|
|
16825
|
+
} catch {
|
|
16826
|
+
}
|
|
16827
|
+
}
|
|
16828
|
+
const viaRank = { id: 3, label: 2, token: 1, embedding: 0 };
|
|
16829
|
+
return [...best.values()].sort(
|
|
16830
|
+
(a, b) => b.score - a.score || viaRank[b.via] - viaRank[a.via] || a.nodeId.localeCompare(b.nodeId)
|
|
16831
|
+
).slice(0, maxNodes).map((s) => ({ nodeId: s.nodeId, label: s.label, via: s.via, score: Number(s.score.toFixed(3)) }));
|
|
16832
|
+
}
|
|
16833
|
+
function incidentsForNode(nodeId, incidents) {
|
|
16834
|
+
if (!incidents || incidents.length === 0) return [];
|
|
16835
|
+
const svc = nodeId.replace(/^service:/, "");
|
|
16836
|
+
return incidents.filter((e) => e.affectedNode === nodeId || e.service === svc);
|
|
16837
|
+
}
|
|
16838
|
+
function edgeSignalNote(e) {
|
|
16839
|
+
const bits = [];
|
|
16840
|
+
if (e.signal) {
|
|
16841
|
+
bits.push(`${e.signal.spanCount} spans`);
|
|
16842
|
+
if (e.signal.errorCount > 0) bits.push(`${e.signal.errorCount} errors`);
|
|
16843
|
+
if (e.signal.latencyMs?.p95 !== void 0) bits.push(`p95 ${Math.round(e.signal.latencyMs.p95)}ms`);
|
|
16844
|
+
} else if (e.callCount !== void 0) {
|
|
16845
|
+
bits.push(`${e.callCount} calls`);
|
|
16846
|
+
}
|
|
16847
|
+
return bits.length ? ` (${bits.join(", ")})` : "";
|
|
16848
|
+
}
|
|
16849
|
+
function buildRootCauseSection(graph, node, incidents, now) {
|
|
16850
|
+
const result = getRootCause(graph, node, void 0, incidents, { now });
|
|
16851
|
+
if (!result) return null;
|
|
16852
|
+
const lastProv = result.edgeProvenances[result.edgeProvenances.length - 1] ?? import_types59.Provenance.OBSERVED;
|
|
16853
|
+
const facts = [
|
|
16854
|
+
{
|
|
16855
|
+
text: `Root cause: ${result.rootCauseNode} \u2014 ${result.rootCauseReason}`,
|
|
16856
|
+
provenance: lastProv,
|
|
16857
|
+
confidence: result.confidence
|
|
16858
|
+
}
|
|
16859
|
+
];
|
|
16860
|
+
const candidates = result.candidates ?? [];
|
|
16861
|
+
for (const c of candidates.slice(0, 3)) {
|
|
16862
|
+
facts.push({
|
|
16863
|
+
text: `Candidate ${c.node} \u2014 ${c.classification}: ${c.reason}`,
|
|
16864
|
+
...c.provenance ? { provenance: c.provenance } : {},
|
|
16865
|
+
confidence: c.confidence
|
|
16866
|
+
});
|
|
16867
|
+
}
|
|
16868
|
+
if (result.traversalPath.length > 1) {
|
|
16869
|
+
facts.push({ text: `Traversal: ${result.traversalPath.join(" \u2190 ")}` });
|
|
16870
|
+
}
|
|
16871
|
+
if (result.fixRecommendation) {
|
|
16872
|
+
facts.push({ text: `Recommended fix: ${result.fixRecommendation}` });
|
|
16873
|
+
}
|
|
16874
|
+
return { heading: "Root cause (navigation)", facts: facts.slice(0, MAX_FACTS_PER_SECTION) };
|
|
16875
|
+
}
|
|
16876
|
+
function buildDependenciesSection(graph, node) {
|
|
16877
|
+
const result = getTransitiveDependencies(graph, node, 2);
|
|
16878
|
+
if (result.total === 0) return null;
|
|
16879
|
+
const facts = result.dependencies.slice(0, MAX_FACTS_PER_SECTION).map((d) => ({
|
|
16880
|
+
text: `${d.nodeId} \u2014 ${d.edgeType} (distance ${d.distance})`,
|
|
16881
|
+
// Per-fact provenance: the transitive walk mixes EXTRACTED and OBSERVED
|
|
16882
|
+
// edges, so the tag is on the fact, not asserted for the whole section.
|
|
16883
|
+
provenance: d.provenance
|
|
16884
|
+
}));
|
|
16885
|
+
return { heading: "Dependencies", facts };
|
|
16886
|
+
}
|
|
16887
|
+
function buildObservedSection(graph, node) {
|
|
16888
|
+
const result = getObservedDependencies(graph, node);
|
|
16889
|
+
const facts = [];
|
|
16890
|
+
for (const e of result.dependencies.slice(0, MAX_FACTS_PER_SECTION)) {
|
|
16891
|
+
const via = e.source !== node ? ` via ${e.source}` : "";
|
|
16892
|
+
facts.push({
|
|
16893
|
+
text: `calls ${e.target}${via}${edgeSignalNote(e)}`,
|
|
16894
|
+
provenance: e.provenance,
|
|
16895
|
+
confidence: confidenceForEdge(e)
|
|
16896
|
+
});
|
|
16897
|
+
}
|
|
16898
|
+
if (facts.length === 0) {
|
|
16899
|
+
if (result.observed && result.inboundObservedCount > 0) {
|
|
16900
|
+
facts.push({
|
|
16901
|
+
text: `no outbound runtime calls, but OTel observed ${result.inboundObservedCount} inbound call path${result.inboundObservedCount === 1 ? "" : "s"} \u2014 a pure receiver`,
|
|
16902
|
+
provenance: import_types59.Provenance.OBSERVED
|
|
16903
|
+
});
|
|
16904
|
+
} else {
|
|
16905
|
+
return null;
|
|
16906
|
+
}
|
|
16907
|
+
}
|
|
16908
|
+
return { heading: "Runtime dependencies (OBSERVED)", facts };
|
|
16909
|
+
}
|
|
16910
|
+
function buildBlastSection(graph, node) {
|
|
16911
|
+
const result = getBlastRadius(graph, node);
|
|
16912
|
+
if (result.totalAffected === 0) return null;
|
|
16913
|
+
const facts = result.affectedNodes.slice(0, MAX_FACTS_PER_SECTION).map((n) => ({
|
|
16914
|
+
text: `${n.nodeId} (distance ${n.distance})`,
|
|
16915
|
+
provenance: n.edgeProvenance,
|
|
16916
|
+
confidence: n.confidence
|
|
16917
|
+
}));
|
|
16918
|
+
return {
|
|
16919
|
+
heading: `Blast radius \u2014 ${result.totalAffected} dependent${result.totalAffected === 1 ? "" : "s"}`,
|
|
16920
|
+
facts
|
|
16921
|
+
};
|
|
16922
|
+
}
|
|
16923
|
+
function buildIncidentsSection(node, incidents) {
|
|
16924
|
+
const relevant = incidentsForNode(node, incidents);
|
|
16925
|
+
if (relevant.length === 0) return null;
|
|
16926
|
+
const ordered = [...relevant].sort((a, b) => b.timestamp.localeCompare(a.timestamp)).slice(0, 4);
|
|
16927
|
+
const facts = ordered.map((ev) => ({
|
|
16928
|
+
text: `${ev.timestamp} \u2014 ${ev.service}: ${ev.errorMessage}`,
|
|
16929
|
+
// ErrorEvents are observation records — OBSERVED by definition.
|
|
16930
|
+
provenance: import_types59.Provenance.OBSERVED
|
|
16931
|
+
}));
|
|
16932
|
+
return { heading: `Recent incidents (OBSERVED) \u2014 ${relevant.length} recorded`, facts };
|
|
16933
|
+
}
|
|
16934
|
+
function divergenceLine(d) {
|
|
16935
|
+
if ((d.type === "missing-observed" || d.type === "missing-extracted") && d.column) {
|
|
16936
|
+
return `[${d.type}] ${d.table ?? d.source} column ${d.column} \u2014 ${d.reason}`;
|
|
16937
|
+
}
|
|
16938
|
+
return `[${d.type}] ${d.source} \u2192 ${d.target} \u2014 ${d.reason}`;
|
|
16939
|
+
}
|
|
16940
|
+
function buildDivergenceSection(graph, node) {
|
|
16941
|
+
const result = computeDivergences(graph, { node });
|
|
16942
|
+
if (result.totalAffected === 0) return null;
|
|
16943
|
+
const facts = result.divergences.slice(0, MAX_FACTS_PER_SECTION).map((d) => ({
|
|
16944
|
+
text: divergenceLine(d),
|
|
16945
|
+
confidence: d.confidence
|
|
16946
|
+
// A divergence is composite by construction (EXTRACTED vs OBSERVED), so it
|
|
16947
|
+
// carries no single provenance — the fact leaves it unset.
|
|
16948
|
+
}));
|
|
16949
|
+
return {
|
|
16950
|
+
heading: `Divergences (EXTRACTED vs OBSERVED) \u2014 ${result.totalAffected}`,
|
|
16951
|
+
facts
|
|
16952
|
+
};
|
|
16953
|
+
}
|
|
16954
|
+
function buildGlobalDivergenceSection(graph) {
|
|
16955
|
+
const result = computeDivergences(graph);
|
|
16956
|
+
if (result.totalAffected === 0) {
|
|
16957
|
+
return {
|
|
16958
|
+
heading: "Divergences (EXTRACTED vs OBSERVED)",
|
|
16959
|
+
facts: [{ text: "None \u2014 declared code and observed runtime agree across the graph." }]
|
|
16960
|
+
};
|
|
16961
|
+
}
|
|
16962
|
+
const facts = result.divergences.slice(0, MAX_FACTS_PER_SECTION).map((d) => ({
|
|
16963
|
+
text: divergenceLine(d),
|
|
16964
|
+
confidence: d.confidence
|
|
16965
|
+
// Composite by construction (EXTRACTED vs OBSERVED), so no single provenance.
|
|
16966
|
+
}));
|
|
16967
|
+
return {
|
|
16968
|
+
heading: `Divergences (EXTRACTED vs OBSERVED) \u2014 ${result.totalAffected}`,
|
|
16969
|
+
facts
|
|
16970
|
+
};
|
|
16971
|
+
}
|
|
16972
|
+
function buildGlobalIncidentsSection(incidents) {
|
|
16973
|
+
if (!incidents || incidents.length === 0) {
|
|
16974
|
+
return {
|
|
16975
|
+
heading: "Incidents across the system",
|
|
16976
|
+
facts: [{ text: "None recorded \u2014 the OBSERVED incident store is empty." }]
|
|
16977
|
+
};
|
|
16978
|
+
}
|
|
16979
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
16980
|
+
for (const ev of incidents) {
|
|
16981
|
+
const key = ev.affectedNode || (0, import_types59.serviceId)(ev.service);
|
|
16982
|
+
const cur = byKey.get(key);
|
|
16983
|
+
if (!cur) {
|
|
16984
|
+
byKey.set(key, { key, count: 1, latest: ev.timestamp, sampleMsg: ev.errorMessage });
|
|
16985
|
+
} else {
|
|
16986
|
+
cur.count += 1;
|
|
16987
|
+
if (ev.timestamp > cur.latest) {
|
|
16988
|
+
cur.latest = ev.timestamp;
|
|
16989
|
+
cur.sampleMsg = ev.errorMessage;
|
|
16990
|
+
}
|
|
16991
|
+
}
|
|
16992
|
+
}
|
|
16993
|
+
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);
|
|
16994
|
+
const facts = rows.map((r) => ({
|
|
16995
|
+
text: `${r.key} \u2014 ${r.count} incident${r.count === 1 ? "" : "s"}, latest ${r.latest}: ${r.sampleMsg}`,
|
|
16996
|
+
provenance: import_types59.Provenance.OBSERVED
|
|
16997
|
+
}));
|
|
16998
|
+
return {
|
|
16999
|
+
heading: `Incidents across the system \u2014 ${incidents.length} recorded`,
|
|
17000
|
+
facts
|
|
17001
|
+
};
|
|
17002
|
+
}
|
|
17003
|
+
function buildOverviewSections(graph, incidents) {
|
|
17004
|
+
const nodeByType = /* @__PURE__ */ new Map();
|
|
17005
|
+
const services = [];
|
|
17006
|
+
graph.forEachNode((_id, attrs) => {
|
|
17007
|
+
const node = attrs;
|
|
17008
|
+
nodeByType.set(node.type, (nodeByType.get(node.type) ?? 0) + 1);
|
|
17009
|
+
if (node.type === import_types59.NodeType.ServiceNode) services.push(node.id);
|
|
17010
|
+
});
|
|
17011
|
+
const edgeByProv = /* @__PURE__ */ new Map();
|
|
17012
|
+
graph.forEachEdge((_id, attrs) => {
|
|
17013
|
+
const p = attrs.provenance;
|
|
17014
|
+
edgeByProv.set(p, (edgeByProv.get(p) ?? 0) + 1);
|
|
17015
|
+
});
|
|
17016
|
+
const sections = [];
|
|
17017
|
+
const count = (t) => nodeByType.get(t) ?? 0;
|
|
17018
|
+
const shapeFacts = [
|
|
17019
|
+
{ text: `${graph.order} nodes, ${graph.size} edges` },
|
|
17020
|
+
{
|
|
17021
|
+
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`
|
|
17022
|
+
}
|
|
17023
|
+
];
|
|
17024
|
+
for (const p of [import_types59.Provenance.EXTRACTED, import_types59.Provenance.OBSERVED, import_types59.Provenance.INFERRED, import_types59.Provenance.STALE]) {
|
|
17025
|
+
const n = edgeByProv.get(p) ?? 0;
|
|
17026
|
+
if (n > 0) shapeFacts.push({ text: `${n} ${p} edge${n === 1 ? "" : "s"}`, provenance: p });
|
|
17027
|
+
}
|
|
17028
|
+
sections.push({ heading: "System shape", facts: shapeFacts });
|
|
17029
|
+
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);
|
|
17030
|
+
if (depCounts.length > 0) {
|
|
17031
|
+
sections.push({
|
|
17032
|
+
heading: "Busiest services (by direct dependencies)",
|
|
17033
|
+
facts: depCounts.map((r) => ({
|
|
17034
|
+
text: `${r.s} \u2014 ${r.n} direct dependenc${r.n === 1 ? "y" : "ies"}`
|
|
17035
|
+
}))
|
|
17036
|
+
});
|
|
17037
|
+
}
|
|
17038
|
+
if (incidents && incidents.length > 0) {
|
|
17039
|
+
const incCount = /* @__PURE__ */ new Map();
|
|
17040
|
+
for (const ev of incidents) {
|
|
17041
|
+
const key = ev.affectedNode || (0, import_types59.serviceId)(ev.service);
|
|
17042
|
+
incCount.set(key, (incCount.get(key) ?? 0) + 1);
|
|
17043
|
+
}
|
|
17044
|
+
const top = [...incCount.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, MAX_FACTS_PER_SECTION);
|
|
17045
|
+
sections.push({
|
|
17046
|
+
heading: `Nodes with incidents \u2014 ${incidents.length} recorded`,
|
|
17047
|
+
facts: top.map(([k, n]) => ({
|
|
17048
|
+
text: `${k} \u2014 ${n} incident${n === 1 ? "" : "s"}`,
|
|
17049
|
+
provenance: import_types59.Provenance.OBSERVED
|
|
17050
|
+
}))
|
|
17051
|
+
});
|
|
17052
|
+
}
|
|
17053
|
+
const div = computeDivergences(graph);
|
|
17054
|
+
sections.push({
|
|
17055
|
+
heading: "Divergences",
|
|
17056
|
+
facts: [
|
|
17057
|
+
{
|
|
17058
|
+
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.`
|
|
17059
|
+
}
|
|
17060
|
+
]
|
|
17061
|
+
});
|
|
17062
|
+
return sections;
|
|
17063
|
+
}
|
|
17064
|
+
function buildGlobalSections(intent, graph, incidents) {
|
|
17065
|
+
switch (intent) {
|
|
17066
|
+
case "divergence":
|
|
17067
|
+
return [buildGlobalDivergenceSection(graph)];
|
|
17068
|
+
case "incidents":
|
|
17069
|
+
return [buildGlobalIncidentsSection(incidents)];
|
|
17070
|
+
case "overview":
|
|
17071
|
+
return buildOverviewSections(graph, incidents);
|
|
17072
|
+
default:
|
|
17073
|
+
return null;
|
|
17074
|
+
}
|
|
17075
|
+
}
|
|
17076
|
+
var SECTION_ORDER = {
|
|
17077
|
+
"root-cause": ["root-cause", "incidents", "blast", "observed", "divergence"],
|
|
17078
|
+
"blast-radius": ["blast", "dependencies", "observed", "incidents"],
|
|
17079
|
+
dependencies: ["dependencies", "observed", "blast"],
|
|
17080
|
+
observed: ["observed", "dependencies", "incidents"],
|
|
17081
|
+
incidents: ["incidents", "root-cause", "observed"],
|
|
17082
|
+
divergence: ["divergence", "observed", "dependencies", "incidents"],
|
|
17083
|
+
overview: ["dependencies", "observed", "blast", "incidents", "divergence"]
|
|
17084
|
+
};
|
|
17085
|
+
function buildSection(kind, graph, node, incidents, now) {
|
|
17086
|
+
switch (kind) {
|
|
17087
|
+
case "root-cause":
|
|
17088
|
+
return buildRootCauseSection(graph, node, incidents, now);
|
|
17089
|
+
case "dependencies":
|
|
17090
|
+
return buildDependenciesSection(graph, node);
|
|
17091
|
+
case "observed":
|
|
17092
|
+
return buildObservedSection(graph, node);
|
|
17093
|
+
case "blast":
|
|
17094
|
+
return buildBlastSection(graph, node);
|
|
17095
|
+
case "incidents":
|
|
17096
|
+
return buildIncidentsSection(node, incidents);
|
|
17097
|
+
case "divergence":
|
|
17098
|
+
return buildDivergenceSection(graph, node);
|
|
17099
|
+
}
|
|
17100
|
+
}
|
|
17101
|
+
function summarizeGlobal(intent, sections) {
|
|
17102
|
+
const lead = sections[0];
|
|
17103
|
+
switch (intent) {
|
|
17104
|
+
case "divergence": {
|
|
17105
|
+
const none = lead?.facts[0]?.text.startsWith("None") ?? true;
|
|
17106
|
+
return none ? "No divergences across the graph \u2014 declared code and observed runtime agree." : `${lead.heading} across the graph, highest-confidence first below.`;
|
|
17107
|
+
}
|
|
17108
|
+
case "incidents": {
|
|
17109
|
+
const none = lead?.facts[0]?.text.startsWith("None") ?? true;
|
|
17110
|
+
return none ? "No incidents recorded across the system \u2014 the OBSERVED incident store is empty." : `${lead.heading}, aggregated by node below.`;
|
|
17111
|
+
}
|
|
17112
|
+
case "overview":
|
|
17113
|
+
default:
|
|
17114
|
+
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.`;
|
|
17115
|
+
}
|
|
17116
|
+
}
|
|
17117
|
+
function summarize(question, intent, matched, primary, sections, scope) {
|
|
17118
|
+
if (scope === "global") {
|
|
17119
|
+
return summarizeGlobal(intent, sections);
|
|
17120
|
+
}
|
|
17121
|
+
if (!primary) {
|
|
17122
|
+
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.`;
|
|
17123
|
+
}
|
|
17124
|
+
const lead = sections[0];
|
|
17125
|
+
const others = matched.slice(1);
|
|
17126
|
+
const alsoNote = others.length ? ` Also matched: ${others.map((m) => m.nodeId).join(", ")}.` : "";
|
|
17127
|
+
let core;
|
|
17128
|
+
switch (intent) {
|
|
17129
|
+
case "root-cause": {
|
|
17130
|
+
const rc = sections.find((s) => s.heading === "Root cause (navigation)");
|
|
17131
|
+
if (rc) {
|
|
17132
|
+
core = rc.facts[0]?.text ?? "";
|
|
17133
|
+
} else if (lead) {
|
|
17134
|
+
core = `No root cause surfaced for ${primary} \u2014 it may be healthy, or the failure isn't recorded. Nearest context: ${lead.heading.toLowerCase()}.`;
|
|
17135
|
+
} else {
|
|
17136
|
+
core = `No root cause surfaced for ${primary} \u2014 it may be healthy.`;
|
|
17137
|
+
}
|
|
17138
|
+
break;
|
|
17139
|
+
}
|
|
17140
|
+
case "blast-radius":
|
|
17141
|
+
core = lead ? `${lead.heading} of ${primary}.` : `${primary} has no dependents \u2014 nothing else would break if it failed.`;
|
|
17142
|
+
break;
|
|
17143
|
+
case "dependencies":
|
|
17144
|
+
core = lead ? `${primary}: ${lead.heading.toLowerCase()} listed below.` : `${primary} has no declared dependencies in the graph.`;
|
|
17145
|
+
break;
|
|
17146
|
+
case "observed":
|
|
17147
|
+
core = lead ? `${primary} at runtime: ${lead.facts.length} OBSERVED fact${lead.facts.length === 1 ? "" : "s"}.` : `No runtime traffic OBSERVED for ${primary}.`;
|
|
17148
|
+
break;
|
|
17149
|
+
case "incidents":
|
|
17150
|
+
core = lead ? `${primary}: ${lead.heading.toLowerCase()}.` : `No incidents recorded against ${primary}.`;
|
|
17151
|
+
break;
|
|
17152
|
+
case "divergence":
|
|
17153
|
+
core = lead ? `${lead.heading} involving ${primary}.` : `No divergences involve ${primary} \u2014 declared and observed agree here.`;
|
|
17154
|
+
break;
|
|
17155
|
+
default:
|
|
17156
|
+
core = `${primary}: ${sections.length} view${sections.length === 1 ? "" : "s"} of its fused local context below.`;
|
|
17157
|
+
}
|
|
17158
|
+
return `${core}${alsoNote}`;
|
|
17159
|
+
}
|
|
17160
|
+
async function askGraph(graph, question, opts = {}) {
|
|
17161
|
+
const now = opts.now ?? Date.now();
|
|
17162
|
+
const maxNodes = opts.maxNodes ?? DEFAULT_MAX_NODES;
|
|
17163
|
+
const intent = classifyIntent(question);
|
|
17164
|
+
const matched = await resolveEntities(graph, question, opts.searchIndex, maxNodes);
|
|
17165
|
+
const primary = matched[0]?.nodeId;
|
|
17166
|
+
const sections = [];
|
|
17167
|
+
let scope;
|
|
17168
|
+
if (primary) {
|
|
17169
|
+
scope = "node";
|
|
17170
|
+
for (const kind of SECTION_ORDER[intent]) {
|
|
17171
|
+
const s = buildSection(kind, graph, primary, opts.incidents, now);
|
|
17172
|
+
if (s && s.facts.length > 0) sections.push(s);
|
|
17173
|
+
}
|
|
17174
|
+
} else {
|
|
17175
|
+
const global = buildGlobalSections(intent, graph, opts.incidents);
|
|
17176
|
+
if (global) {
|
|
17177
|
+
scope = "global";
|
|
17178
|
+
for (const s of global) if (s.facts.length > 0) sections.push(s);
|
|
17179
|
+
}
|
|
17180
|
+
}
|
|
17181
|
+
const answer = summarize(question, intent, matched, primary, sections, scope);
|
|
17182
|
+
const provSet = /* @__PURE__ */ new Set();
|
|
17183
|
+
for (const s of sections) for (const f of s.facts) if (f.provenance) provSet.add(f.provenance);
|
|
17184
|
+
const confidence = sections[0]?.facts[0]?.confidence;
|
|
17185
|
+
return import_types59.AskResultSchema.parse({
|
|
17186
|
+
question,
|
|
17187
|
+
intent,
|
|
17188
|
+
matched,
|
|
17189
|
+
...primary ? { primaryNode: primary } : {},
|
|
17190
|
+
...scope ? { scope } : {},
|
|
17191
|
+
sections,
|
|
17192
|
+
answer,
|
|
17193
|
+
...confidence !== void 0 ? { confidence } : {},
|
|
17194
|
+
provenance: [...provSet]
|
|
17195
|
+
});
|
|
17196
|
+
}
|
|
17197
|
+
|
|
16565
17198
|
// src/diff.ts
|
|
16566
17199
|
init_cjs_shims();
|
|
16567
17200
|
var import_node_fs36 = require("fs");
|
|
@@ -16696,7 +17329,7 @@ init_cjs_shims();
|
|
|
16696
17329
|
var import_node_fs37 = require("fs");
|
|
16697
17330
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
16698
17331
|
var import_node_path72 = __toESM(require("path"), 1);
|
|
16699
|
-
var
|
|
17332
|
+
var import_types60 = require("@neat.is/types");
|
|
16700
17333
|
var LOCK_TIMEOUT_MS = 5e3;
|
|
16701
17334
|
var LOCK_RETRY_MS = 50;
|
|
16702
17335
|
function neatHome() {
|
|
@@ -16906,10 +17539,10 @@ async function readRegistry() {
|
|
|
16906
17539
|
throw err;
|
|
16907
17540
|
}
|
|
16908
17541
|
const parsed = JSON.parse(raw);
|
|
16909
|
-
return
|
|
17542
|
+
return import_types60.RegistryFileSchema.parse(parsed);
|
|
16910
17543
|
}
|
|
16911
17544
|
async function writeRegistry(reg) {
|
|
16912
|
-
const validated =
|
|
17545
|
+
const validated = import_types60.RegistryFileSchema.parse(reg);
|
|
16913
17546
|
await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
|
|
16914
17547
|
}
|
|
16915
17548
|
var ProjectNameCollisionError = class extends Error {
|
|
@@ -17310,15 +17943,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
|
|
|
17310
17943
|
|
|
17311
17944
|
// src/connectors/index.ts
|
|
17312
17945
|
init_cjs_shims();
|
|
17313
|
-
var
|
|
17946
|
+
var import_types61 = require("@neat.is/types");
|
|
17314
17947
|
var NO_ENV = "unknown";
|
|
17315
17948
|
function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
17316
17949
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
17317
17950
|
const sites = [];
|
|
17318
17951
|
for (const edgeId of graph.inboundEdges(targetNodeId)) {
|
|
17319
17952
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
17320
|
-
if (edge.provenance !==
|
|
17321
|
-
const parsed = (0,
|
|
17953
|
+
if (edge.provenance !== import_types61.Provenance.EXTRACTED) continue;
|
|
17954
|
+
const parsed = (0, import_types61.parseFileId)(edge.source);
|
|
17322
17955
|
if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
|
|
17323
17956
|
const site = { relPath: edge.evidence.file };
|
|
17324
17957
|
if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
|
|
@@ -17329,7 +17962,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
|
17329
17962
|
function routeCallSiteFor(graph, targetNodeId) {
|
|
17330
17963
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
17331
17964
|
const attrs = graph.getNodeAttributes(targetNodeId);
|
|
17332
|
-
if (attrs.type !==
|
|
17965
|
+
if (attrs.type !== import_types61.NodeType.RouteNode || !attrs.path) return void 0;
|
|
17333
17966
|
const site = { relPath: attrs.path };
|
|
17334
17967
|
if (attrs.line !== void 0) site.line = attrs.line;
|
|
17335
17968
|
return site;
|
|
@@ -17941,23 +18574,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
|
|
|
17941
18574
|
|
|
17942
18575
|
// src/connectors/supabase/resolve.ts
|
|
17943
18576
|
init_cjs_shims();
|
|
17944
|
-
var
|
|
18577
|
+
var import_types63 = require("@neat.is/types");
|
|
17945
18578
|
function createSupabaseResolveTarget(graph, config) {
|
|
17946
18579
|
return (signal, _ctx) => {
|
|
17947
18580
|
if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
|
|
17948
18581
|
return null;
|
|
17949
18582
|
}
|
|
17950
|
-
const subResourceId = (0,
|
|
18583
|
+
const subResourceId = (0, import_types63.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
|
|
17951
18584
|
if (graph.hasNode(subResourceId)) {
|
|
17952
|
-
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType:
|
|
18585
|
+
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types63.EdgeType.CALLS };
|
|
17953
18586
|
}
|
|
17954
|
-
const bareResourceId = (0,
|
|
18587
|
+
const bareResourceId = (0, import_types63.infraId)(signal.targetKind, signal.targetName);
|
|
17955
18588
|
if (graph.hasNode(bareResourceId)) {
|
|
17956
|
-
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType:
|
|
18589
|
+
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types63.EdgeType.CALLS };
|
|
17957
18590
|
}
|
|
17958
|
-
const projectLevelId = (0,
|
|
18591
|
+
const projectLevelId = (0, import_types63.infraId)("supabase", config.nodeRef);
|
|
17959
18592
|
if (graph.hasNode(projectLevelId)) {
|
|
17960
|
-
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType:
|
|
18593
|
+
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types63.EdgeType.CALLS };
|
|
17961
18594
|
}
|
|
17962
18595
|
return null;
|
|
17963
18596
|
};
|
|
@@ -18050,7 +18683,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
|
|
|
18050
18683
|
|
|
18051
18684
|
// src/connectors/railway/index.ts
|
|
18052
18685
|
init_cjs_shims();
|
|
18053
|
-
var
|
|
18686
|
+
var import_types67 = require("@neat.is/types");
|
|
18054
18687
|
|
|
18055
18688
|
// src/connectors/railway/client.ts
|
|
18056
18689
|
init_cjs_shims();
|
|
@@ -18201,7 +18834,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
|
|
|
18201
18834
|
const out = [];
|
|
18202
18835
|
graph.forEachNode((_id, attrs) => {
|
|
18203
18836
|
const node = attrs;
|
|
18204
|
-
if (node.type !==
|
|
18837
|
+
if (node.type !== import_types67.NodeType.RouteNode) return;
|
|
18205
18838
|
const route = attrs;
|
|
18206
18839
|
if (route.service !== serviceName) return;
|
|
18207
18840
|
out.push({
|
|
@@ -18305,12 +18938,12 @@ function createRailwayResolveTarget(config) {
|
|
|
18305
18938
|
const serviceName = config.serviceNameById[config.serviceId];
|
|
18306
18939
|
if (!serviceName) return null;
|
|
18307
18940
|
if (signal.targetKind === ROUTE_TARGET_KIND) {
|
|
18308
|
-
return { targetNodeId: signal.targetName, serviceName, edgeType:
|
|
18941
|
+
return { targetNodeId: signal.targetName, serviceName, edgeType: import_types67.EdgeType.CALLS };
|
|
18309
18942
|
}
|
|
18310
18943
|
if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
|
|
18311
18944
|
const peerName = config.serviceNameById[signal.targetName];
|
|
18312
18945
|
if (!peerName) return null;
|
|
18313
|
-
return { targetNodeId: (0,
|
|
18946
|
+
return { targetNodeId: (0, import_types67.serviceId)(peerName), serviceName, edgeType: import_types67.EdgeType.CONNECTS_TO };
|
|
18314
18947
|
}
|
|
18315
18948
|
return null;
|
|
18316
18949
|
};
|
|
@@ -18498,7 +19131,7 @@ function mapLogEntriesToSignals(entries) {
|
|
|
18498
19131
|
|
|
18499
19132
|
// src/connectors/firebase/resolve.ts
|
|
18500
19133
|
init_cjs_shims();
|
|
18501
|
-
var
|
|
19134
|
+
var import_types68 = require("@neat.is/types");
|
|
18502
19135
|
function neatServiceNameFor(resourceType, resourceName, serviceMap) {
|
|
18503
19136
|
switch (resourceType) {
|
|
18504
19137
|
case "cloud_function":
|
|
@@ -18513,7 +19146,7 @@ function routeEntriesFor(graph, serviceName) {
|
|
|
18513
19146
|
const entries = [];
|
|
18514
19147
|
graph.forEachNode((_id, attrs) => {
|
|
18515
19148
|
const node = attrs;
|
|
18516
|
-
if (node.type !==
|
|
19149
|
+
if (node.type !== import_types68.NodeType.RouteNode) return;
|
|
18517
19150
|
const route = attrs;
|
|
18518
19151
|
if (route.service !== serviceName) return;
|
|
18519
19152
|
entries.push({
|
|
@@ -18545,7 +19178,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
|
|
|
18545
19178
|
return {
|
|
18546
19179
|
targetNodeId: match.routeNodeId,
|
|
18547
19180
|
serviceName,
|
|
18548
|
-
edgeType:
|
|
19181
|
+
edgeType: import_types68.EdgeType.CALLS
|
|
18549
19182
|
};
|
|
18550
19183
|
};
|
|
18551
19184
|
}
|
|
@@ -18572,7 +19205,7 @@ init_cjs_shims();
|
|
|
18572
19205
|
|
|
18573
19206
|
// src/connectors/cloudflare/connector.ts
|
|
18574
19207
|
init_cjs_shims();
|
|
18575
|
-
var
|
|
19208
|
+
var import_types70 = require("@neat.is/types");
|
|
18576
19209
|
|
|
18577
19210
|
// src/connectors/cloudflare/client.ts
|
|
18578
19211
|
init_cjs_shims();
|
|
@@ -18736,7 +19369,7 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
18736
19369
|
graph.forEachNode((id, attrs) => {
|
|
18737
19370
|
if (found) return;
|
|
18738
19371
|
const a = attrs;
|
|
18739
|
-
if (a.type ===
|
|
19372
|
+
if (a.type === import_types70.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
|
|
18740
19373
|
found = id;
|
|
18741
19374
|
}
|
|
18742
19375
|
});
|
|
@@ -18748,7 +19381,7 @@ function findMatchingRouteNode(graph, serviceName, method, path76) {
|
|
|
18748
19381
|
graph.forEachNode((id, attrs) => {
|
|
18749
19382
|
if (found) return;
|
|
18750
19383
|
const a = attrs;
|
|
18751
|
-
if (a.type !==
|
|
19384
|
+
if (a.type !== import_types70.NodeType.RouteNode || a.service !== serviceName) return;
|
|
18752
19385
|
if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
|
|
18753
19386
|
const routeMethod = (a.method ?? "").toUpperCase();
|
|
18754
19387
|
if (routeMethod !== "ALL" && routeMethod !== method) return;
|
|
@@ -18767,11 +19400,11 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
18767
19400
|
};
|
|
18768
19401
|
const mapping = config.workers?.[scriptName];
|
|
18769
19402
|
if (mapping) {
|
|
18770
|
-
const wholeFileId = (0,
|
|
19403
|
+
const wholeFileId = (0, import_types70.fileId)(mapping.service, mapping.entryFile);
|
|
18771
19404
|
return {
|
|
18772
19405
|
targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
|
|
18773
19406
|
serviceName: mapping.service,
|
|
18774
|
-
edgeType:
|
|
19407
|
+
edgeType: import_types70.EdgeType.CALLS
|
|
18775
19408
|
};
|
|
18776
19409
|
}
|
|
18777
19410
|
const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
|
|
@@ -18780,13 +19413,13 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
18780
19413
|
return {
|
|
18781
19414
|
targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
|
|
18782
19415
|
serviceName: fileNode.service,
|
|
18783
|
-
edgeType:
|
|
19416
|
+
edgeType: import_types70.EdgeType.CALLS
|
|
18784
19417
|
};
|
|
18785
19418
|
}
|
|
18786
19419
|
return {
|
|
18787
|
-
targetNodeId: (0,
|
|
19420
|
+
targetNodeId: (0, import_types70.infraId)("cloudflare-worker", scriptName),
|
|
18788
19421
|
serviceName: scriptName,
|
|
18789
|
-
edgeType:
|
|
19422
|
+
edgeType: import_types70.EdgeType.CALLS,
|
|
18790
19423
|
ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
|
|
18791
19424
|
};
|
|
18792
19425
|
};
|
|
@@ -18982,14 +19615,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
|
|
|
18982
19615
|
|
|
18983
19616
|
// src/connectors/neon/resolve.ts
|
|
18984
19617
|
init_cjs_shims();
|
|
18985
|
-
var
|
|
19618
|
+
var import_types74 = require("@neat.is/types");
|
|
18986
19619
|
function createNeonResolveTarget(config) {
|
|
18987
19620
|
return (signal) => {
|
|
18988
19621
|
if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
|
|
18989
19622
|
return {
|
|
18990
|
-
targetNodeId: (0,
|
|
19623
|
+
targetNodeId: (0, import_types74.infraId)("sql-table", signal.targetName),
|
|
18991
19624
|
serviceName: config.serviceName,
|
|
18992
|
-
edgeType:
|
|
19625
|
+
edgeType: import_types74.EdgeType.CALLS,
|
|
18993
19626
|
ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
|
|
18994
19627
|
};
|
|
18995
19628
|
};
|
|
@@ -19170,14 +19803,14 @@ function mapLogEntriesToSignals2(entries) {
|
|
|
19170
19803
|
|
|
19171
19804
|
// src/connectors/cloud-run/resolve.ts
|
|
19172
19805
|
init_cjs_shims();
|
|
19173
|
-
var
|
|
19806
|
+
var import_types78 = require("@neat.is/types");
|
|
19174
19807
|
var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
|
|
19175
19808
|
function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
|
|
19176
19809
|
let found = null;
|
|
19177
19810
|
graph.forEachNode((_id, attrs) => {
|
|
19178
19811
|
if (found) return;
|
|
19179
19812
|
const node = attrs;
|
|
19180
|
-
if (node.type !==
|
|
19813
|
+
if (node.type !== import_types78.NodeType.RouteNode) return;
|
|
19181
19814
|
const route = attrs;
|
|
19182
19815
|
if (route.service !== serviceName || !route.pathTemplate) return;
|
|
19183
19816
|
if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
|
|
@@ -19202,13 +19835,13 @@ function createCloudRunResolveTarget(graph, config) {
|
|
|
19202
19835
|
normalizePathTemplate(path76)
|
|
19203
19836
|
);
|
|
19204
19837
|
if (routeNodeId) {
|
|
19205
|
-
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType:
|
|
19838
|
+
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types78.EdgeType.CALLS };
|
|
19206
19839
|
}
|
|
19207
19840
|
}
|
|
19208
19841
|
return {
|
|
19209
|
-
targetNodeId: (0,
|
|
19842
|
+
targetNodeId: (0, import_types78.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
|
|
19210
19843
|
serviceName: mappedService ?? gcpServiceName,
|
|
19211
|
-
edgeType:
|
|
19844
|
+
edgeType: import_types78.EdgeType.CALLS,
|
|
19212
19845
|
ensureInfraNode: {
|
|
19213
19846
|
kind: CLOUD_RUN_SERVICE_INFRA_KIND,
|
|
19214
19847
|
name: gcpServiceName,
|
|
@@ -19249,7 +19882,7 @@ function createCloudRunConnector(graph, config = {}) {
|
|
|
19249
19882
|
|
|
19250
19883
|
// src/connectors/render/index.ts
|
|
19251
19884
|
init_cjs_shims();
|
|
19252
|
-
var
|
|
19885
|
+
var import_types81 = require("@neat.is/types");
|
|
19253
19886
|
|
|
19254
19887
|
// src/connectors/render/types.ts
|
|
19255
19888
|
init_cjs_shims();
|
|
@@ -19327,7 +19960,7 @@ function buildRenderRouteIndex(graph, serviceName) {
|
|
|
19327
19960
|
const out = [];
|
|
19328
19961
|
graph.forEachNode((_id, attrs) => {
|
|
19329
19962
|
const node = attrs;
|
|
19330
|
-
if (node.type !==
|
|
19963
|
+
if (node.type !== import_types81.NodeType.RouteNode) return;
|
|
19331
19964
|
const route = attrs;
|
|
19332
19965
|
if (route.service !== serviceName) return;
|
|
19333
19966
|
out.push({
|
|
@@ -19412,7 +20045,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
|
|
|
19412
20045
|
function createRenderResolveTarget(config) {
|
|
19413
20046
|
return (signal) => {
|
|
19414
20047
|
if (signal.targetKind === ROUTE_TARGET_KIND2) {
|
|
19415
|
-
return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType:
|
|
20048
|
+
return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types81.EdgeType.CALLS };
|
|
19416
20049
|
}
|
|
19417
20050
|
return null;
|
|
19418
20051
|
};
|
|
@@ -19550,21 +20183,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
|
|
|
19550
20183
|
|
|
19551
20184
|
// src/connectors/planetscale/resolve.ts
|
|
19552
20185
|
init_cjs_shims();
|
|
19553
|
-
var
|
|
20186
|
+
var import_types85 = require("@neat.is/types");
|
|
19554
20187
|
var PLANETSCALE_DATABASE_KIND = "planetscale-database";
|
|
19555
20188
|
function createPlanetscaleResolveTarget(graph, config) {
|
|
19556
20189
|
const databaseName = `${config.organization}/${config.database}`;
|
|
19557
20190
|
return (signal, _ctx) => {
|
|
19558
20191
|
if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
|
|
19559
|
-
const tableId = (0,
|
|
20192
|
+
const tableId = (0, import_types85.infraId)("sql-table", signal.targetName);
|
|
19560
20193
|
if (graph.hasNode(tableId)) {
|
|
19561
|
-
return { targetNodeId: tableId, serviceName: config.serviceName, edgeType:
|
|
20194
|
+
return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types85.EdgeType.CALLS };
|
|
19562
20195
|
}
|
|
19563
|
-
const providerId = (0,
|
|
20196
|
+
const providerId = (0, import_types85.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
|
|
19564
20197
|
return {
|
|
19565
20198
|
targetNodeId: providerId,
|
|
19566
20199
|
serviceName: config.serviceName,
|
|
19567
|
-
edgeType:
|
|
20200
|
+
edgeType: import_types85.EdgeType.CALLS,
|
|
19568
20201
|
ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
|
|
19569
20202
|
};
|
|
19570
20203
|
};
|
|
@@ -19829,7 +20462,7 @@ function mapBuildsToSignals(builds, serviceName) {
|
|
|
19829
20462
|
|
|
19830
20463
|
// src/connectors/eas/resolve.ts
|
|
19831
20464
|
init_cjs_shims();
|
|
19832
|
-
var
|
|
20465
|
+
var import_types90 = require("@neat.is/types");
|
|
19833
20466
|
var NO_ENV2 = "unknown";
|
|
19834
20467
|
var EAS_JSON_PHASES = /* @__PURE__ */ new Set(["READ_EAS_JSON"]);
|
|
19835
20468
|
var APP_CONFIG_PHASES = /* @__PURE__ */ new Set([
|
|
@@ -19845,8 +20478,8 @@ function configBasenamesForPhase(phase) {
|
|
|
19845
20478
|
function configNodeService(graph, configNodeId) {
|
|
19846
20479
|
for (const edgeId of graph.inboundEdges(configNodeId)) {
|
|
19847
20480
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
19848
|
-
if (edge.type !==
|
|
19849
|
-
const parsed = (0,
|
|
20481
|
+
if (edge.type !== import_types90.EdgeType.CONFIGURED_BY) continue;
|
|
20482
|
+
const parsed = (0, import_types90.parseFileId)(edge.source);
|
|
19850
20483
|
if (parsed) return parsed.service;
|
|
19851
20484
|
}
|
|
19852
20485
|
return null;
|
|
@@ -19857,7 +20490,7 @@ function findConfigNode(graph, basenames, serviceName) {
|
|
|
19857
20490
|
graph.forEachNode((id, attrs) => {
|
|
19858
20491
|
if (scoped) return;
|
|
19859
20492
|
const node = attrs;
|
|
19860
|
-
if (node.type !==
|
|
20493
|
+
if (node.type !== import_types90.NodeType.ConfigNode) return;
|
|
19861
20494
|
if (typeof node.name !== "string" || !basenames.includes(node.name)) return;
|
|
19862
20495
|
if (anyMatch === null) anyMatch = id;
|
|
19863
20496
|
if (configNodeService(graph, id) === serviceName) scoped = id;
|
|
@@ -19874,13 +20507,13 @@ function createEasResolveTarget(graph) {
|
|
|
19874
20507
|
if (basenames.length > 0) {
|
|
19875
20508
|
const configNodeId = findConfigNode(graph, basenames, serviceName);
|
|
19876
20509
|
if (configNodeId) {
|
|
19877
|
-
return { targetNodeId: configNodeId, serviceName, edgeType:
|
|
20510
|
+
return { targetNodeId: configNodeId, serviceName, edgeType: import_types90.EdgeType.CALLS };
|
|
19878
20511
|
}
|
|
19879
20512
|
}
|
|
19880
20513
|
return {
|
|
19881
20514
|
targetNodeId: resolveFusedServiceId(graph, serviceName, NO_ENV2),
|
|
19882
20515
|
serviceName,
|
|
19883
|
-
edgeType:
|
|
20516
|
+
edgeType: import_types90.EdgeType.CALLS
|
|
19884
20517
|
};
|
|
19885
20518
|
};
|
|
19886
20519
|
}
|
|
@@ -20605,11 +21238,11 @@ function registerRoutes(scope, ctx) {
|
|
|
20605
21238
|
const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
20606
21239
|
const parsed = [];
|
|
20607
21240
|
for (const c of candidates) {
|
|
20608
|
-
const r =
|
|
21241
|
+
const r = import_types93.DivergenceTypeSchema.safeParse(c);
|
|
20609
21242
|
if (!r.success) {
|
|
20610
21243
|
return reply.code(400).send({
|
|
20611
21244
|
error: `unknown divergence type "${c}"`,
|
|
20612
|
-
allowed:
|
|
21245
|
+
allowed: import_types93.DivergenceTypeSchema.options
|
|
20613
21246
|
});
|
|
20614
21247
|
}
|
|
20615
21248
|
parsed.push(r.data);
|
|
@@ -20851,6 +21484,18 @@ function registerRoutes(scope, ctx) {
|
|
|
20851
21484
|
matches: matches.slice(0, safeLimit)
|
|
20852
21485
|
};
|
|
20853
21486
|
});
|
|
21487
|
+
scope.get("/graph/ask", async (req, reply) => {
|
|
21488
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
21489
|
+
if (!proj) return;
|
|
21490
|
+
const question = (req.query.q ?? "").trim();
|
|
21491
|
+
if (!question) return reply.code(400).send({ error: "query parameter `q` is required" });
|
|
21492
|
+
const epath = errorsPathFor(proj);
|
|
21493
|
+
const incidents = epath ? await readErrorEvents(epath) : [];
|
|
21494
|
+
return askGraph(proj.graph, question, {
|
|
21495
|
+
...proj.searchIndex ? { searchIndex: proj.searchIndex } : {},
|
|
21496
|
+
incidents
|
|
21497
|
+
});
|
|
21498
|
+
});
|
|
20854
21499
|
scope.get(
|
|
20855
21500
|
"/graph/diff",
|
|
20856
21501
|
async (req, reply) => {
|
|
@@ -20951,7 +21596,7 @@ function registerRoutes(scope, ctx) {
|
|
|
20951
21596
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
20952
21597
|
let violations = await log.readAll();
|
|
20953
21598
|
if (req.query.severity) {
|
|
20954
|
-
const sev =
|
|
21599
|
+
const sev = import_types93.PolicySeveritySchema.safeParse(req.query.severity);
|
|
20955
21600
|
if (!sev.success) {
|
|
20956
21601
|
return reply.code(400).send({
|
|
20957
21602
|
error: "invalid severity",
|
|
@@ -20990,7 +21635,7 @@ function registerRoutes(scope, ctx) {
|
|
|
20990
21635
|
scope.post("/policies/check", async (req, reply) => {
|
|
20991
21636
|
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
20992
21637
|
if (!proj) return;
|
|
20993
|
-
const parsed =
|
|
21638
|
+
const parsed = import_types93.PoliciesCheckBodySchema.safeParse(req.body ?? {});
|
|
20994
21639
|
if (!parsed.success) {
|
|
20995
21640
|
return reply.code(400).send({
|
|
20996
21641
|
error: "invalid /policies/check body",
|
|
@@ -21339,7 +21984,7 @@ function unroutedErrorsPath(neatHome3) {
|
|
|
21339
21984
|
}
|
|
21340
21985
|
|
|
21341
21986
|
// src/daemon.ts
|
|
21342
|
-
var
|
|
21987
|
+
var import_types94 = require("@neat.is/types");
|
|
21343
21988
|
function daemonJsonPath(scanPath) {
|
|
21344
21989
|
return import_node_path75.default.join(scanPath, "neat-out", "daemon.json");
|
|
21345
21990
|
}
|
|
@@ -21451,8 +22096,8 @@ function isTokenContained(needle, haystack) {
|
|
|
21451
22096
|
const n = needle.toLowerCase();
|
|
21452
22097
|
const h = haystack.toLowerCase();
|
|
21453
22098
|
if (!h.includes(n)) return false;
|
|
21454
|
-
const
|
|
21455
|
-
return
|
|
22099
|
+
const tokens2 = h.split(/[-_]/);
|
|
22100
|
+
return tokens2.includes(n);
|
|
21456
22101
|
}
|
|
21457
22102
|
function serviceNameMatchesProject(serviceName, project) {
|
|
21458
22103
|
if (serviceName.toLowerCase() === project.toLowerCase()) return true;
|
|
@@ -21464,7 +22109,7 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
|
|
|
21464
22109
|
if (!serviceName) return true;
|
|
21465
22110
|
if (serviceNameMatchesProject(serviceName, project)) return true;
|
|
21466
22111
|
return graph.someNode(
|
|
21467
|
-
(_id, attrs) => attrs.type ===
|
|
22112
|
+
(_id, attrs) => attrs.type === import_types94.NodeType.ServiceNode && attrs.name === serviceName
|
|
21468
22113
|
);
|
|
21469
22114
|
}
|
|
21470
22115
|
async function bootstrapProject(entry, connectors = [], neatHome3) {
|