@neat.is/core 0.9.8-dev.20260829 → 0.9.9
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-KZBFP7L6.js → chunk-B6KJG5KN.js} +219 -13
- package/dist/chunk-B6KJG5KN.js.map +1 -0
- package/dist/{chunk-JSUPWMH3.js → chunk-QETJIA2Q.js} +14 -4
- package/dist/{chunk-JSUPWMH3.js.map → chunk-QETJIA2Q.js.map} +1 -1
- package/dist/cli.cjs +343 -98
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +46 -3
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +294 -82
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -2
- package/dist/neatd.cjs +294 -82
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +2 -2
- package/dist/server.cjs +280 -80
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-KZBFP7L6.js.map +0 -1
|
@@ -931,6 +931,7 @@ import {
|
|
|
931
931
|
frontierId,
|
|
932
932
|
graphqlOperationId,
|
|
933
933
|
grpcMethodId,
|
|
934
|
+
incidentKindOf,
|
|
934
935
|
inferredEdgeId,
|
|
935
936
|
infraId,
|
|
936
937
|
observedEdgeId,
|
|
@@ -4580,11 +4581,25 @@ function upsertInferredEdge(graph, type, source, target, ts) {
|
|
|
4580
4581
|
};
|
|
4581
4582
|
graph.addEdgeWithKey(id, source, target, edge);
|
|
4582
4583
|
}
|
|
4584
|
+
function emitIncidentEvent(project, ev) {
|
|
4585
|
+
emitNeatEvent({
|
|
4586
|
+
type: "incident",
|
|
4587
|
+
project,
|
|
4588
|
+
payload: {
|
|
4589
|
+
incidentId: ev.id,
|
|
4590
|
+
affectedNode: ev.affectedNode,
|
|
4591
|
+
service: ev.service,
|
|
4592
|
+
incidentKind: incidentKindOf(ev),
|
|
4593
|
+
at: ev.timestamp
|
|
4594
|
+
}
|
|
4595
|
+
});
|
|
4596
|
+
}
|
|
4583
4597
|
async function appendErrorEvent(ctx, ev) {
|
|
4584
4598
|
await fs7.mkdir(path8.dirname(ctx.errorsPath), { recursive: true });
|
|
4585
4599
|
await fs7.appendFile(ctx.errorsPath, JSON.stringify(ev) + "\n", "utf8");
|
|
4600
|
+
emitIncidentEvent(ctx.project ?? DEFAULT_PROJECT, ev);
|
|
4586
4601
|
}
|
|
4587
|
-
async function appendConnectorIncident(errorsPath, input) {
|
|
4602
|
+
async function appendConnectorIncident(errorsPath, input, project) {
|
|
4588
4603
|
const ev = {
|
|
4589
4604
|
id: input.id,
|
|
4590
4605
|
timestamp: input.timestamp,
|
|
@@ -4598,6 +4613,7 @@ async function appendConnectorIncident(errorsPath, input) {
|
|
|
4598
4613
|
};
|
|
4599
4614
|
await fs7.mkdir(path8.dirname(errorsPath), { recursive: true });
|
|
4600
4615
|
await fs7.appendFile(errorsPath, JSON.stringify(ev) + "\n", "utf8");
|
|
4616
|
+
if (project) emitIncidentEvent(project, ev);
|
|
4601
4617
|
}
|
|
4602
4618
|
function landIncidentCallSite(span, callSite, trusted, graph) {
|
|
4603
4619
|
const relPath = graph ? reconcileObservedRelPath(graph, span.service, callSite.relPath) : callSite.relPath;
|
|
@@ -4691,12 +4707,13 @@ function buildErrorEventForReceiver(span, graph, scanPath) {
|
|
|
4691
4707
|
affectedNode: locus.affectedNode
|
|
4692
4708
|
};
|
|
4693
4709
|
}
|
|
4694
|
-
function makeErrorSpanWriter(errorsPath, graph, scanPath) {
|
|
4710
|
+
function makeErrorSpanWriter(errorsPath, graph, scanPath, project = DEFAULT_PROJECT) {
|
|
4695
4711
|
return async (span) => {
|
|
4696
4712
|
const ev = buildErrorEventForReceiver(span, graph, scanPath);
|
|
4697
4713
|
if (!ev) return;
|
|
4698
4714
|
await fs7.mkdir(path8.dirname(errorsPath), { recursive: true });
|
|
4699
4715
|
await fs7.appendFile(errorsPath, JSON.stringify(ev) + "\n", "utf8");
|
|
4716
|
+
emitIncidentEvent(project, ev);
|
|
4700
4717
|
};
|
|
4701
4718
|
}
|
|
4702
4719
|
async function recordFailingResponseIncident(ctx, span, affectedNode, timestamp, statusCode, count, firstTimestamp) {
|
|
@@ -16931,8 +16948,162 @@ function queryLogEntries(opts) {
|
|
|
16931
16948
|
return merged;
|
|
16932
16949
|
}
|
|
16933
16950
|
|
|
16951
|
+
// src/goodybag.ts
|
|
16952
|
+
import {
|
|
16953
|
+
IncidentCardSchema,
|
|
16954
|
+
Provenance as Provenance26,
|
|
16955
|
+
incidentKindOf as incidentKindOf2
|
|
16956
|
+
} from "@neat.is/types";
|
|
16957
|
+
var CODE_FILEPATH_ATTR2 = "code.filepath";
|
|
16958
|
+
var CODE_LINENO_ATTR2 = "code.lineno";
|
|
16959
|
+
var BLAST_NEAREST_LIMIT = 5;
|
|
16960
|
+
function grainOf2(graph, nodeId) {
|
|
16961
|
+
if (graph.hasNode(nodeId)) {
|
|
16962
|
+
const t = graph.getNodeAttributes(nodeId).type;
|
|
16963
|
+
if (typeof t === "string" && t.length > 0) {
|
|
16964
|
+
return (t.endsWith("Node") ? t.slice(0, -4) : t).toLowerCase();
|
|
16965
|
+
}
|
|
16966
|
+
}
|
|
16967
|
+
const colon = nodeId.indexOf(":");
|
|
16968
|
+
return colon > 0 ? nodeId.slice(0, colon) : "unknown";
|
|
16969
|
+
}
|
|
16970
|
+
function locusOf(graph, ev) {
|
|
16971
|
+
const file = ev.attributes?.[CODE_FILEPATH_ATTR2];
|
|
16972
|
+
if (typeof file !== "string" || file.length === 0) return null;
|
|
16973
|
+
const rawLine = ev.attributes?.[CODE_LINENO_ATTR2];
|
|
16974
|
+
const line = typeof rawLine === "number" ? rawLine : Number(rawLine);
|
|
16975
|
+
const node = graph.hasNode(ev.affectedNode) ? graph.getNodeAttributes(ev.affectedNode) : void 0;
|
|
16976
|
+
return {
|
|
16977
|
+
file,
|
|
16978
|
+
...Number.isFinite(line) ? { lineStart: line, lineEnd: line } : {},
|
|
16979
|
+
...node?.name ? { symbol: node.name } : {},
|
|
16980
|
+
service: node?.service ?? ev.service,
|
|
16981
|
+
provenance: Provenance26.OBSERVED
|
|
16982
|
+
};
|
|
16983
|
+
}
|
|
16984
|
+
function locusFromNode(graph, nodeId) {
|
|
16985
|
+
if (!graph.hasNode(nodeId)) return null;
|
|
16986
|
+
const n = graph.getNodeAttributes(nodeId);
|
|
16987
|
+
const file = n.relPath ?? n.path;
|
|
16988
|
+
if (typeof file !== "string" || file.length === 0) return null;
|
|
16989
|
+
const start = n.span?.startLine;
|
|
16990
|
+
const end = n.span?.endLine;
|
|
16991
|
+
return {
|
|
16992
|
+
file,
|
|
16993
|
+
...typeof start === "number" ? { lineStart: start } : {},
|
|
16994
|
+
...typeof end === "number" ? { lineEnd: end } : {},
|
|
16995
|
+
...n.qualname ? { symbol: shortLabel(graph, nodeId) } : {},
|
|
16996
|
+
...n.service ? { service: n.service } : {},
|
|
16997
|
+
provenance: Provenance26.INFERRED
|
|
16998
|
+
};
|
|
16999
|
+
}
|
|
17000
|
+
function promoteCauseLocus(graph, causeNode, incidents) {
|
|
17001
|
+
const native = incidents.find(
|
|
17002
|
+
(e) => e.affectedNode === causeNode && typeof e.attributes?.[CODE_FILEPATH_ATTR2] === "string"
|
|
17003
|
+
);
|
|
17004
|
+
if (native) {
|
|
17005
|
+
const l = locusOf(graph, native);
|
|
17006
|
+
if (l) return { ...l, symbol: shortLabel(graph, causeNode), provenance: Provenance26.INFERRED };
|
|
17007
|
+
}
|
|
17008
|
+
return locusFromNode(graph, causeNode);
|
|
17009
|
+
}
|
|
17010
|
+
function divergenceSummary(d) {
|
|
17011
|
+
const column = "column" in d && d.column ? `.${d.column}` : "";
|
|
17012
|
+
const label = "table" in d && d.table ? `${d.table}${column}` : `${d.source} \u2192 ${d.target}`;
|
|
17013
|
+
return label;
|
|
17014
|
+
}
|
|
17015
|
+
function baseName(p) {
|
|
17016
|
+
const parts = p.split(/[\\/]/);
|
|
17017
|
+
return parts[parts.length - 1] || p;
|
|
17018
|
+
}
|
|
17019
|
+
function shortLabel(graph, nodeId) {
|
|
17020
|
+
if (graph.hasNode(nodeId)) {
|
|
17021
|
+
const name = graph.getNodeAttributes(nodeId).name;
|
|
17022
|
+
if (typeof name === "string" && name.length > 0) return name;
|
|
17023
|
+
}
|
|
17024
|
+
const afterHash = nodeId.includes("#") ? nodeId.slice(nodeId.lastIndexOf("#") + 1) : nodeId;
|
|
17025
|
+
return afterHash.includes(":") ? afterHash.slice(afterHash.lastIndexOf(":") + 1) : afterHash;
|
|
17026
|
+
}
|
|
17027
|
+
function renderHeadline(graph, ev, locus, causeNode) {
|
|
17028
|
+
const what = ev.exceptionType ? `raised ${ev.exceptionType}` : ev.errorMessage || "failed";
|
|
17029
|
+
const causeLabel = causeNode && causeNode !== ev.affectedNode ? shortLabel(graph, causeNode) : "";
|
|
17030
|
+
if (locus) {
|
|
17031
|
+
const base = baseName(locus.file);
|
|
17032
|
+
const lines = locus.lineStart != null ? locus.lineEnd && locus.lineEnd !== locus.lineStart ? `LINES ${locus.lineStart}-${locus.lineEnd}` : `LINE ${locus.lineStart}` : "";
|
|
17033
|
+
const symbol = locus.symbol ?? (grainOf2(graph, ev.affectedNode) === "symbol" ? shortLabel(graph, ev.affectedNode) : void 0);
|
|
17034
|
+
const subject = symbol ? `SYMBOL ${symbol}` : `FILE ${base}`;
|
|
17035
|
+
const at = symbol ? `${lines ? `${lines} in ` : ""}${base}` : lines;
|
|
17036
|
+
const where = at ? ` at ${at}` : "";
|
|
17037
|
+
const svc = locus.service ?? ev.service;
|
|
17038
|
+
const cause2 = causeLabel && causeLabel !== symbol ? ` \u2192 root cause ${causeLabel}` : "";
|
|
17039
|
+
return `${subject}${where} (SERVICE ${svc}) ${what} at ${ev.timestamp}${cause2}`;
|
|
17040
|
+
}
|
|
17041
|
+
const cause = causeLabel ? ` \u2192 root cause ${causeLabel}` : "";
|
|
17042
|
+
return `SERVICE ${ev.service} ${what} at ${ev.timestamp}${cause}`;
|
|
17043
|
+
}
|
|
17044
|
+
function buildIncidentCard(graph, errorEvent, incidents, policies) {
|
|
17045
|
+
const affected = errorEvent.affectedNode;
|
|
17046
|
+
let locus = locusOf(graph, errorEvent);
|
|
17047
|
+
const inGraph = graph.hasNode(affected);
|
|
17048
|
+
const rc = inGraph ? getRootCause(graph, affected, errorEvent, incidents) : null;
|
|
17049
|
+
let rootCause = null;
|
|
17050
|
+
if (rc) {
|
|
17051
|
+
const provs = rc.edgeProvenances ?? [];
|
|
17052
|
+
const chain = (rc.traversalPath ?? []).map((node, i) => ({
|
|
17053
|
+
node,
|
|
17054
|
+
grain: grainOf2(graph, node),
|
|
17055
|
+
provenance: provs[i] ?? provs[provs.length - 1] ?? Provenance26.INFERRED
|
|
17056
|
+
}));
|
|
17057
|
+
rootCause = {
|
|
17058
|
+
node: rc.rootCauseNode,
|
|
17059
|
+
...rc.candidates?.[0]?.classification ? { classification: rc.candidates[0].classification } : {},
|
|
17060
|
+
reason: rc.rootCauseReason,
|
|
17061
|
+
confidence: rc.confidence,
|
|
17062
|
+
fix: rc.fixRecommendation ?? null,
|
|
17063
|
+
chain
|
|
17064
|
+
};
|
|
17065
|
+
}
|
|
17066
|
+
if (locus === null && rootCause) {
|
|
17067
|
+
locus = promoteCauseLocus(graph, rootCause.node, incidents);
|
|
17068
|
+
}
|
|
17069
|
+
const blast = inGraph ? getBlastRadius(graph, affected) : { origin: affected, affectedNodes: [], totalAffected: 0 };
|
|
17070
|
+
const nearest = [...blast.affectedNodes].sort((a, b) => a.distance - b.distance).slice(0, BLAST_NEAREST_LIMIT).map((n) => ({ node: n.nodeId, distance: n.distance, provenance: n.edgeProvenance }));
|
|
17071
|
+
const applicable = inGraph ? selectApplicablePolicies(graph, policies, affected) : [];
|
|
17072
|
+
const policyCards = applicable.map((p) => ({
|
|
17073
|
+
policyName: p.policyName,
|
|
17074
|
+
severity: p.severity,
|
|
17075
|
+
message: p.reason
|
|
17076
|
+
}));
|
|
17077
|
+
const divergences = inGraph ? computeDivergences(graph, { node: affected, incidents }).divergences.map((d) => ({
|
|
17078
|
+
type: d.type,
|
|
17079
|
+
summary: divergenceSummary(d)
|
|
17080
|
+
})) : [];
|
|
17081
|
+
const card = {
|
|
17082
|
+
kind: "incident",
|
|
17083
|
+
id: errorEvent.id,
|
|
17084
|
+
at: errorEvent.timestamp,
|
|
17085
|
+
incidentKind: incidentKindOf2(errorEvent),
|
|
17086
|
+
service: errorEvent.service,
|
|
17087
|
+
affectedNode: affected,
|
|
17088
|
+
message: errorEvent.errorMessage,
|
|
17089
|
+
...errorEvent.exceptionType ? { exceptionType: errorEvent.exceptionType } : {},
|
|
17090
|
+
...errorEvent.httpStatusCode !== void 0 ? { httpStatusCode: errorEvent.httpStatusCode } : {},
|
|
17091
|
+
...errorEvent.incidentCount !== void 0 ? { count: errorEvent.incidentCount } : {},
|
|
17092
|
+
...errorEvent.firstTimestamp && errorEvent.lastTimestamp ? { window: { first: errorEvent.firstTimestamp, last: errorEvent.lastTimestamp } } : {},
|
|
17093
|
+
...errorEvent.traceId ? { traceId: errorEvent.traceId } : {},
|
|
17094
|
+
...errorEvent.spanId ? { spanId: errorEvent.spanId } : {},
|
|
17095
|
+
locus,
|
|
17096
|
+
rootCause,
|
|
17097
|
+
...nearest.length > 0 ? { blastRadius: { totalAffected: blast.totalAffected, nearest } } : {},
|
|
17098
|
+
...policyCards.length > 0 ? { policies: policyCards } : {},
|
|
17099
|
+
...divergences.length > 0 ? { divergence: divergences } : {},
|
|
17100
|
+
headline: renderHeadline(graph, errorEvent, locus, rootCause?.node ?? null)
|
|
17101
|
+
};
|
|
17102
|
+
return IncidentCardSchema.parse(card);
|
|
17103
|
+
}
|
|
17104
|
+
|
|
16934
17105
|
// src/ask.ts
|
|
16935
|
-
import { AskResultSchema, NodeType as NodeType32, Provenance as
|
|
17106
|
+
import { AskResultSchema, NodeType as NodeType32, Provenance as Provenance27, serviceId as serviceId14 } from "@neat.is/types";
|
|
16936
17107
|
var DEFAULT_MAX_NODES = 3;
|
|
16937
17108
|
var MAX_FACTS_PER_SECTION = 6;
|
|
16938
17109
|
var INTENT_RULES = [
|
|
@@ -17217,7 +17388,7 @@ function edgeSignalNote(e) {
|
|
|
17217
17388
|
function buildRootCauseSection(graph, node, incidents, now) {
|
|
17218
17389
|
const result = getRootCause(graph, node, void 0, incidents, { now });
|
|
17219
17390
|
if (!result) return null;
|
|
17220
|
-
const lastProv = result.edgeProvenances[result.edgeProvenances.length - 1] ??
|
|
17391
|
+
const lastProv = result.edgeProvenances[result.edgeProvenances.length - 1] ?? Provenance27.OBSERVED;
|
|
17221
17392
|
const facts = [
|
|
17222
17393
|
{
|
|
17223
17394
|
text: `Root cause: ${result.rootCauseNode} \u2014 ${result.rootCauseReason}`,
|
|
@@ -17267,7 +17438,7 @@ function buildObservedSection(graph, node) {
|
|
|
17267
17438
|
if (result.observed && result.inboundObservedCount > 0) {
|
|
17268
17439
|
facts.push({
|
|
17269
17440
|
text: `no outbound runtime calls, but OTel observed ${result.inboundObservedCount} inbound call path${result.inboundObservedCount === 1 ? "" : "s"} \u2014 a pure receiver`,
|
|
17270
|
-
provenance:
|
|
17441
|
+
provenance: Provenance27.OBSERVED
|
|
17271
17442
|
});
|
|
17272
17443
|
} else {
|
|
17273
17444
|
return null;
|
|
@@ -17295,7 +17466,7 @@ function buildIncidentsSection(node, incidents) {
|
|
|
17295
17466
|
const facts = ordered.map((ev) => ({
|
|
17296
17467
|
text: `${ev.timestamp} \u2014 ${ev.service}: ${ev.errorMessage}`,
|
|
17297
17468
|
// ErrorEvents are observation records — OBSERVED by definition.
|
|
17298
|
-
provenance:
|
|
17469
|
+
provenance: Provenance27.OBSERVED
|
|
17299
17470
|
}));
|
|
17300
17471
|
return { heading: `Recent incidents (OBSERVED) \u2014 ${relevant.length} recorded`, facts };
|
|
17301
17472
|
}
|
|
@@ -17370,7 +17541,7 @@ function buildGlobalIncidentsSection(incidents) {
|
|
|
17370
17541
|
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);
|
|
17371
17542
|
const facts = rows.map((r) => ({
|
|
17372
17543
|
text: `${r.key} \u2014 ${r.count} incident${r.count === 1 ? "" : "s"}, latest ${r.latest}: ${r.sampleMsg}`,
|
|
17373
|
-
provenance:
|
|
17544
|
+
provenance: Provenance27.OBSERVED
|
|
17374
17545
|
}));
|
|
17375
17546
|
return {
|
|
17376
17547
|
heading: `Incidents across the system \u2014 ${incidents.length} recorded`,
|
|
@@ -17398,7 +17569,7 @@ function buildOverviewSections(graph, incidents) {
|
|
|
17398
17569
|
text: `${count(NodeType32.ServiceNode)} services, ${count(NodeType32.FileNode)} files, ${count(NodeType32.SymbolNode)} symbols, ${count(NodeType32.DatabaseNode)} databases`
|
|
17399
17570
|
}
|
|
17400
17571
|
];
|
|
17401
|
-
for (const p of [
|
|
17572
|
+
for (const p of [Provenance27.EXTRACTED, Provenance27.OBSERVED, Provenance27.INFERRED, Provenance27.STALE]) {
|
|
17402
17573
|
const n = edgeByProv.get(p) ?? 0;
|
|
17403
17574
|
if (n > 0) shapeFacts.push({ text: `${n} ${p} edge${n === 1 ? "" : "s"}`, provenance: p });
|
|
17404
17575
|
}
|
|
@@ -17423,7 +17594,7 @@ function buildOverviewSections(graph, incidents) {
|
|
|
17423
17594
|
heading: `Nodes with incidents \u2014 ${incidents.length} recorded`,
|
|
17424
17595
|
facts: top.map(([k, n]) => ({
|
|
17425
17596
|
text: `${k} \u2014 ${n} incident${n === 1 ? "" : "s"}`,
|
|
17426
|
-
provenance:
|
|
17597
|
+
provenance: Provenance27.OBSERVED
|
|
17427
17598
|
}))
|
|
17428
17599
|
});
|
|
17429
17600
|
}
|
|
@@ -17954,14 +18125,14 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
|
|
|
17954
18125
|
}
|
|
17955
18126
|
|
|
17956
18127
|
// src/connectors/index.ts
|
|
17957
|
-
import { NodeType as NodeType33, parseFileId as parseFileId2, Provenance as
|
|
18128
|
+
import { NodeType as NodeType33, parseFileId as parseFileId2, Provenance as Provenance28 } from "@neat.is/types";
|
|
17958
18129
|
var NO_ENV = "unknown";
|
|
17959
18130
|
function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
17960
18131
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
17961
18132
|
const sites = [];
|
|
17962
18133
|
for (const edgeId of graph.inboundEdges(targetNodeId)) {
|
|
17963
18134
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
17964
|
-
if (edge.provenance !==
|
|
18135
|
+
if (edge.provenance !== Provenance28.EXTRACTED) continue;
|
|
17965
18136
|
const parsed = parseFileId2(edge.source);
|
|
17966
18137
|
if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
|
|
17967
18138
|
const site = { relPath: edge.evidence.file };
|
|
@@ -18003,7 +18174,7 @@ async function runConnectorPoll(connector, ctx, graph, resolveTarget) {
|
|
|
18003
18174
|
errorMessage: signal.incident.errorMessage,
|
|
18004
18175
|
...signal.incident.attributes ? { attributes: signal.incident.attributes } : {},
|
|
18005
18176
|
affectedNode: resolved.targetNodeId
|
|
18006
|
-
});
|
|
18177
|
+
}, ctx.project);
|
|
18007
18178
|
continue;
|
|
18008
18179
|
}
|
|
18009
18180
|
if (resolved.ensureInfraNode) {
|
|
@@ -21277,6 +21448,7 @@ async function startConnectorPolling(input) {
|
|
|
21277
21448
|
registration.connector,
|
|
21278
21449
|
{
|
|
21279
21450
|
projectDir: input.projectDir,
|
|
21451
|
+
project: input.project,
|
|
21280
21452
|
credentials: registration.credentials,
|
|
21281
21453
|
...input.errorsPath ? { errorsPath: input.errorsPath } : {}
|
|
21282
21454
|
},
|
|
@@ -21620,6 +21792,7 @@ function registerRoutes(scope, ctx) {
|
|
|
21620
21792
|
reg.connector,
|
|
21621
21793
|
{
|
|
21622
21794
|
projectDir: proj.scanPath ?? "",
|
|
21795
|
+
project: proj.name,
|
|
21623
21796
|
credentials: reg.credentials,
|
|
21624
21797
|
...incidentsPath ? { errorsPath: incidentsPath } : {}
|
|
21625
21798
|
},
|
|
@@ -21683,6 +21856,39 @@ function registerRoutes(scope, ctx) {
|
|
|
21683
21856
|
if (!result) return reply.code(404).send({ error: "no root cause found", id: nodeId });
|
|
21684
21857
|
return result;
|
|
21685
21858
|
});
|
|
21859
|
+
scope.get("/graph/incident-card/:nodeId", async (req, reply) => {
|
|
21860
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
21861
|
+
if (!proj) return;
|
|
21862
|
+
const { nodeId } = req.params;
|
|
21863
|
+
if (!proj.graph.hasNode(nodeId)) {
|
|
21864
|
+
return reply.code(404).send({ error: "node not found", id: nodeId });
|
|
21865
|
+
}
|
|
21866
|
+
const epath = errorsPathFor(proj);
|
|
21867
|
+
const incidents = epath ? await readErrorEvents(epath) : [];
|
|
21868
|
+
let errorEvent;
|
|
21869
|
+
if (req.query.errorId) {
|
|
21870
|
+
errorEvent = incidents.find((e) => e.id === req.query.errorId);
|
|
21871
|
+
if (!errorEvent) {
|
|
21872
|
+
return reply.code(404).send({ error: "error event not found", id: req.query.errorId });
|
|
21873
|
+
}
|
|
21874
|
+
} else {
|
|
21875
|
+
const svc = nodeId.replace(/^service:/, "");
|
|
21876
|
+
errorEvent = [...incidents].filter((e) => e.affectedNode === nodeId || e.service === svc).sort((a, b) => (b.timestamp ?? "").localeCompare(a.timestamp ?? ""))[0];
|
|
21877
|
+
if (!errorEvent) {
|
|
21878
|
+
return reply.code(404).send({ error: "no incident found for node", id: nodeId });
|
|
21879
|
+
}
|
|
21880
|
+
}
|
|
21881
|
+
const policyPath = ctx.policyFilePathFor(proj);
|
|
21882
|
+
let policies = [];
|
|
21883
|
+
if (policyPath) {
|
|
21884
|
+
try {
|
|
21885
|
+
policies = await loadPolicyFile(policyPath);
|
|
21886
|
+
} catch {
|
|
21887
|
+
policies = [];
|
|
21888
|
+
}
|
|
21889
|
+
}
|
|
21890
|
+
return buildIncidentCard(proj.graph, errorEvent, incidents, policies);
|
|
21891
|
+
});
|
|
21686
21892
|
scope.get("/graph/blast-radius/:nodeId", async (req, reply) => {
|
|
21687
21893
|
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
21688
21894
|
if (!proj) return;
|
|
@@ -22304,4 +22510,4 @@ export {
|
|
|
22304
22510
|
deprovisionConnector,
|
|
22305
22511
|
buildApi
|
|
22306
22512
|
};
|
|
22307
|
-
//# sourceMappingURL=chunk-
|
|
22513
|
+
//# sourceMappingURL=chunk-B6KJG5KN.js.map
|