@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
package/dist/neatd.cjs
CHANGED
|
@@ -6309,11 +6309,25 @@ function upsertInferredEdge(graph, type, source, target, ts) {
|
|
|
6309
6309
|
};
|
|
6310
6310
|
graph.addEdgeWithKey(id, source, target, edge);
|
|
6311
6311
|
}
|
|
6312
|
+
function emitIncidentEvent(project, ev) {
|
|
6313
|
+
emitNeatEvent({
|
|
6314
|
+
type: "incident",
|
|
6315
|
+
project,
|
|
6316
|
+
payload: {
|
|
6317
|
+
incidentId: ev.id,
|
|
6318
|
+
affectedNode: ev.affectedNode,
|
|
6319
|
+
service: ev.service,
|
|
6320
|
+
incidentKind: (0, import_types8.incidentKindOf)(ev),
|
|
6321
|
+
at: ev.timestamp
|
|
6322
|
+
}
|
|
6323
|
+
});
|
|
6324
|
+
}
|
|
6312
6325
|
async function appendErrorEvent(ctx, ev) {
|
|
6313
6326
|
await import_node_fs7.promises.mkdir(import_node_path8.default.dirname(ctx.errorsPath), { recursive: true });
|
|
6314
6327
|
await import_node_fs7.promises.appendFile(ctx.errorsPath, JSON.stringify(ev) + "\n", "utf8");
|
|
6328
|
+
emitIncidentEvent(ctx.project ?? DEFAULT_PROJECT, ev);
|
|
6315
6329
|
}
|
|
6316
|
-
async function appendConnectorIncident(errorsPath, input) {
|
|
6330
|
+
async function appendConnectorIncident(errorsPath, input, project) {
|
|
6317
6331
|
const ev = {
|
|
6318
6332
|
id: input.id,
|
|
6319
6333
|
timestamp: input.timestamp,
|
|
@@ -6327,6 +6341,7 @@ async function appendConnectorIncident(errorsPath, input) {
|
|
|
6327
6341
|
};
|
|
6328
6342
|
await import_node_fs7.promises.mkdir(import_node_path8.default.dirname(errorsPath), { recursive: true });
|
|
6329
6343
|
await import_node_fs7.promises.appendFile(errorsPath, JSON.stringify(ev) + "\n", "utf8");
|
|
6344
|
+
if (project) emitIncidentEvent(project, ev);
|
|
6330
6345
|
}
|
|
6331
6346
|
function landIncidentCallSite(span, callSite, trusted, graph) {
|
|
6332
6347
|
const relPath = graph ? reconcileObservedRelPath(graph, span.service, callSite.relPath) : callSite.relPath;
|
|
@@ -6420,12 +6435,13 @@ function buildErrorEventForReceiver(span, graph, scanPath) {
|
|
|
6420
6435
|
affectedNode: locus.affectedNode
|
|
6421
6436
|
};
|
|
6422
6437
|
}
|
|
6423
|
-
function makeErrorSpanWriter(errorsPath, graph, scanPath) {
|
|
6438
|
+
function makeErrorSpanWriter(errorsPath, graph, scanPath, project = DEFAULT_PROJECT) {
|
|
6424
6439
|
return async (span) => {
|
|
6425
6440
|
const ev = buildErrorEventForReceiver(span, graph, scanPath);
|
|
6426
6441
|
if (!ev) return;
|
|
6427
6442
|
await import_node_fs7.promises.mkdir(import_node_path8.default.dirname(errorsPath), { recursive: true });
|
|
6428
6443
|
await import_node_fs7.promises.appendFile(errorsPath, JSON.stringify(ev) + "\n", "utf8");
|
|
6444
|
+
emitIncidentEvent(project, ev);
|
|
6429
6445
|
};
|
|
6430
6446
|
}
|
|
6431
6447
|
async function recordFailingResponseIncident(ctx, span, affectedNode, timestamp, statusCode, count, firstTimestamp) {
|
|
@@ -16132,7 +16148,7 @@ var Projects = class {
|
|
|
16132
16148
|
init_cjs_shims();
|
|
16133
16149
|
var import_fastify2 = __toESM(require("fastify"), 1);
|
|
16134
16150
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
16135
|
-
var
|
|
16151
|
+
var import_types98 = require("@neat.is/types");
|
|
16136
16152
|
|
|
16137
16153
|
// src/extend/index.ts
|
|
16138
16154
|
init_cjs_shims();
|
|
@@ -17174,9 +17190,160 @@ function queryLogEntries(opts) {
|
|
|
17174
17190
|
return merged;
|
|
17175
17191
|
}
|
|
17176
17192
|
|
|
17177
|
-
// src/
|
|
17193
|
+
// src/goodybag.ts
|
|
17178
17194
|
init_cjs_shims();
|
|
17179
17195
|
var import_types59 = require("@neat.is/types");
|
|
17196
|
+
var CODE_FILEPATH_ATTR2 = "code.filepath";
|
|
17197
|
+
var CODE_LINENO_ATTR2 = "code.lineno";
|
|
17198
|
+
var BLAST_NEAREST_LIMIT = 5;
|
|
17199
|
+
function grainOf2(graph, nodeId) {
|
|
17200
|
+
if (graph.hasNode(nodeId)) {
|
|
17201
|
+
const t = graph.getNodeAttributes(nodeId).type;
|
|
17202
|
+
if (typeof t === "string" && t.length > 0) {
|
|
17203
|
+
return (t.endsWith("Node") ? t.slice(0, -4) : t).toLowerCase();
|
|
17204
|
+
}
|
|
17205
|
+
}
|
|
17206
|
+
const colon = nodeId.indexOf(":");
|
|
17207
|
+
return colon > 0 ? nodeId.slice(0, colon) : "unknown";
|
|
17208
|
+
}
|
|
17209
|
+
function locusOf(graph, ev) {
|
|
17210
|
+
const file = ev.attributes?.[CODE_FILEPATH_ATTR2];
|
|
17211
|
+
if (typeof file !== "string" || file.length === 0) return null;
|
|
17212
|
+
const rawLine = ev.attributes?.[CODE_LINENO_ATTR2];
|
|
17213
|
+
const line = typeof rawLine === "number" ? rawLine : Number(rawLine);
|
|
17214
|
+
const node = graph.hasNode(ev.affectedNode) ? graph.getNodeAttributes(ev.affectedNode) : void 0;
|
|
17215
|
+
return {
|
|
17216
|
+
file,
|
|
17217
|
+
...Number.isFinite(line) ? { lineStart: line, lineEnd: line } : {},
|
|
17218
|
+
...node?.name ? { symbol: node.name } : {},
|
|
17219
|
+
service: node?.service ?? ev.service,
|
|
17220
|
+
provenance: import_types59.Provenance.OBSERVED
|
|
17221
|
+
};
|
|
17222
|
+
}
|
|
17223
|
+
function locusFromNode(graph, nodeId) {
|
|
17224
|
+
if (!graph.hasNode(nodeId)) return null;
|
|
17225
|
+
const n = graph.getNodeAttributes(nodeId);
|
|
17226
|
+
const file = n.relPath ?? n.path;
|
|
17227
|
+
if (typeof file !== "string" || file.length === 0) return null;
|
|
17228
|
+
const start = n.span?.startLine;
|
|
17229
|
+
const end = n.span?.endLine;
|
|
17230
|
+
return {
|
|
17231
|
+
file,
|
|
17232
|
+
...typeof start === "number" ? { lineStart: start } : {},
|
|
17233
|
+
...typeof end === "number" ? { lineEnd: end } : {},
|
|
17234
|
+
...n.qualname ? { symbol: shortLabel(graph, nodeId) } : {},
|
|
17235
|
+
...n.service ? { service: n.service } : {},
|
|
17236
|
+
provenance: import_types59.Provenance.INFERRED
|
|
17237
|
+
};
|
|
17238
|
+
}
|
|
17239
|
+
function promoteCauseLocus(graph, causeNode, incidents) {
|
|
17240
|
+
const native = incidents.find(
|
|
17241
|
+
(e) => e.affectedNode === causeNode && typeof e.attributes?.[CODE_FILEPATH_ATTR2] === "string"
|
|
17242
|
+
);
|
|
17243
|
+
if (native) {
|
|
17244
|
+
const l = locusOf(graph, native);
|
|
17245
|
+
if (l) return { ...l, symbol: shortLabel(graph, causeNode), provenance: import_types59.Provenance.INFERRED };
|
|
17246
|
+
}
|
|
17247
|
+
return locusFromNode(graph, causeNode);
|
|
17248
|
+
}
|
|
17249
|
+
function divergenceSummary(d) {
|
|
17250
|
+
const column = "column" in d && d.column ? `.${d.column}` : "";
|
|
17251
|
+
const label = "table" in d && d.table ? `${d.table}${column}` : `${d.source} \u2192 ${d.target}`;
|
|
17252
|
+
return label;
|
|
17253
|
+
}
|
|
17254
|
+
function baseName(p) {
|
|
17255
|
+
const parts = p.split(/[\\/]/);
|
|
17256
|
+
return parts[parts.length - 1] || p;
|
|
17257
|
+
}
|
|
17258
|
+
function shortLabel(graph, nodeId) {
|
|
17259
|
+
if (graph.hasNode(nodeId)) {
|
|
17260
|
+
const name = graph.getNodeAttributes(nodeId).name;
|
|
17261
|
+
if (typeof name === "string" && name.length > 0) return name;
|
|
17262
|
+
}
|
|
17263
|
+
const afterHash = nodeId.includes("#") ? nodeId.slice(nodeId.lastIndexOf("#") + 1) : nodeId;
|
|
17264
|
+
return afterHash.includes(":") ? afterHash.slice(afterHash.lastIndexOf(":") + 1) : afterHash;
|
|
17265
|
+
}
|
|
17266
|
+
function renderHeadline(graph, ev, locus, causeNode) {
|
|
17267
|
+
const what = ev.exceptionType ? `raised ${ev.exceptionType}` : ev.errorMessage || "failed";
|
|
17268
|
+
const causeLabel = causeNode && causeNode !== ev.affectedNode ? shortLabel(graph, causeNode) : "";
|
|
17269
|
+
if (locus) {
|
|
17270
|
+
const base = baseName(locus.file);
|
|
17271
|
+
const lines = locus.lineStart != null ? locus.lineEnd && locus.lineEnd !== locus.lineStart ? `LINES ${locus.lineStart}-${locus.lineEnd}` : `LINE ${locus.lineStart}` : "";
|
|
17272
|
+
const symbol = locus.symbol ?? (grainOf2(graph, ev.affectedNode) === "symbol" ? shortLabel(graph, ev.affectedNode) : void 0);
|
|
17273
|
+
const subject = symbol ? `SYMBOL ${symbol}` : `FILE ${base}`;
|
|
17274
|
+
const at = symbol ? `${lines ? `${lines} in ` : ""}${base}` : lines;
|
|
17275
|
+
const where = at ? ` at ${at}` : "";
|
|
17276
|
+
const svc = locus.service ?? ev.service;
|
|
17277
|
+
const cause2 = causeLabel && causeLabel !== symbol ? ` \u2192 root cause ${causeLabel}` : "";
|
|
17278
|
+
return `${subject}${where} (SERVICE ${svc}) ${what} at ${ev.timestamp}${cause2}`;
|
|
17279
|
+
}
|
|
17280
|
+
const cause = causeLabel ? ` \u2192 root cause ${causeLabel}` : "";
|
|
17281
|
+
return `SERVICE ${ev.service} ${what} at ${ev.timestamp}${cause}`;
|
|
17282
|
+
}
|
|
17283
|
+
function buildIncidentCard(graph, errorEvent, incidents, policies) {
|
|
17284
|
+
const affected = errorEvent.affectedNode;
|
|
17285
|
+
let locus = locusOf(graph, errorEvent);
|
|
17286
|
+
const inGraph = graph.hasNode(affected);
|
|
17287
|
+
const rc = inGraph ? getRootCause(graph, affected, errorEvent, incidents) : null;
|
|
17288
|
+
let rootCause = null;
|
|
17289
|
+
if (rc) {
|
|
17290
|
+
const provs = rc.edgeProvenances ?? [];
|
|
17291
|
+
const chain = (rc.traversalPath ?? []).map((node, i) => ({
|
|
17292
|
+
node,
|
|
17293
|
+
grain: grainOf2(graph, node),
|
|
17294
|
+
provenance: provs[i] ?? provs[provs.length - 1] ?? import_types59.Provenance.INFERRED
|
|
17295
|
+
}));
|
|
17296
|
+
rootCause = {
|
|
17297
|
+
node: rc.rootCauseNode,
|
|
17298
|
+
...rc.candidates?.[0]?.classification ? { classification: rc.candidates[0].classification } : {},
|
|
17299
|
+
reason: rc.rootCauseReason,
|
|
17300
|
+
confidence: rc.confidence,
|
|
17301
|
+
fix: rc.fixRecommendation ?? null,
|
|
17302
|
+
chain
|
|
17303
|
+
};
|
|
17304
|
+
}
|
|
17305
|
+
if (locus === null && rootCause) {
|
|
17306
|
+
locus = promoteCauseLocus(graph, rootCause.node, incidents);
|
|
17307
|
+
}
|
|
17308
|
+
const blast = inGraph ? getBlastRadius(graph, affected) : { origin: affected, affectedNodes: [], totalAffected: 0 };
|
|
17309
|
+
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 }));
|
|
17310
|
+
const applicable = inGraph ? selectApplicablePolicies(graph, policies, affected) : [];
|
|
17311
|
+
const policyCards = applicable.map((p) => ({
|
|
17312
|
+
policyName: p.policyName,
|
|
17313
|
+
severity: p.severity,
|
|
17314
|
+
message: p.reason
|
|
17315
|
+
}));
|
|
17316
|
+
const divergences = inGraph ? computeDivergences(graph, { node: affected, incidents }).divergences.map((d) => ({
|
|
17317
|
+
type: d.type,
|
|
17318
|
+
summary: divergenceSummary(d)
|
|
17319
|
+
})) : [];
|
|
17320
|
+
const card = {
|
|
17321
|
+
kind: "incident",
|
|
17322
|
+
id: errorEvent.id,
|
|
17323
|
+
at: errorEvent.timestamp,
|
|
17324
|
+
incidentKind: (0, import_types59.incidentKindOf)(errorEvent),
|
|
17325
|
+
service: errorEvent.service,
|
|
17326
|
+
affectedNode: affected,
|
|
17327
|
+
message: errorEvent.errorMessage,
|
|
17328
|
+
...errorEvent.exceptionType ? { exceptionType: errorEvent.exceptionType } : {},
|
|
17329
|
+
...errorEvent.httpStatusCode !== void 0 ? { httpStatusCode: errorEvent.httpStatusCode } : {},
|
|
17330
|
+
...errorEvent.incidentCount !== void 0 ? { count: errorEvent.incidentCount } : {},
|
|
17331
|
+
...errorEvent.firstTimestamp && errorEvent.lastTimestamp ? { window: { first: errorEvent.firstTimestamp, last: errorEvent.lastTimestamp } } : {},
|
|
17332
|
+
...errorEvent.traceId ? { traceId: errorEvent.traceId } : {},
|
|
17333
|
+
...errorEvent.spanId ? { spanId: errorEvent.spanId } : {},
|
|
17334
|
+
locus,
|
|
17335
|
+
rootCause,
|
|
17336
|
+
...nearest.length > 0 ? { blastRadius: { totalAffected: blast.totalAffected, nearest } } : {},
|
|
17337
|
+
...policyCards.length > 0 ? { policies: policyCards } : {},
|
|
17338
|
+
...divergences.length > 0 ? { divergence: divergences } : {},
|
|
17339
|
+
headline: renderHeadline(graph, errorEvent, locus, rootCause?.node ?? null)
|
|
17340
|
+
};
|
|
17341
|
+
return import_types59.IncidentCardSchema.parse(card);
|
|
17342
|
+
}
|
|
17343
|
+
|
|
17344
|
+
// src/ask.ts
|
|
17345
|
+
init_cjs_shims();
|
|
17346
|
+
var import_types60 = require("@neat.is/types");
|
|
17180
17347
|
var DEFAULT_MAX_NODES = 3;
|
|
17181
17348
|
var MAX_FACTS_PER_SECTION = 6;
|
|
17182
17349
|
var INTENT_RULES = [
|
|
@@ -17404,7 +17571,7 @@ async function resolveEntities(graph, question, searchIndex, maxNodes) {
|
|
|
17404
17571
|
};
|
|
17405
17572
|
graph.forEachNode((id, attrs) => {
|
|
17406
17573
|
const node = attrs;
|
|
17407
|
-
if (node.type ===
|
|
17574
|
+
if (node.type === import_types60.NodeType.FrontierNode) return;
|
|
17408
17575
|
const name = nodeName(node);
|
|
17409
17576
|
const body = idBody(id);
|
|
17410
17577
|
const labelTokens = /* @__PURE__ */ new Set([...tokens(body), ...tokens(name)]);
|
|
@@ -17423,7 +17590,7 @@ async function resolveEntities(graph, question, searchIndex, maxNodes) {
|
|
|
17423
17590
|
const res = await searchIndex.search(question, 10);
|
|
17424
17591
|
if (res.provider !== "substring") {
|
|
17425
17592
|
for (const m of res.matches) {
|
|
17426
|
-
if (m.node.type ===
|
|
17593
|
+
if (m.node.type === import_types60.NodeType.FrontierNode) continue;
|
|
17427
17594
|
const already = best.get(m.node.id);
|
|
17428
17595
|
if (!already && m.score < EMBED_MIN_SCORE) continue;
|
|
17429
17596
|
consider({
|
|
@@ -17461,7 +17628,7 @@ function edgeSignalNote(e) {
|
|
|
17461
17628
|
function buildRootCauseSection(graph, node, incidents, now) {
|
|
17462
17629
|
const result = getRootCause(graph, node, void 0, incidents, { now });
|
|
17463
17630
|
if (!result) return null;
|
|
17464
|
-
const lastProv = result.edgeProvenances[result.edgeProvenances.length - 1] ??
|
|
17631
|
+
const lastProv = result.edgeProvenances[result.edgeProvenances.length - 1] ?? import_types60.Provenance.OBSERVED;
|
|
17465
17632
|
const facts = [
|
|
17466
17633
|
{
|
|
17467
17634
|
text: `Root cause: ${result.rootCauseNode} \u2014 ${result.rootCauseReason}`,
|
|
@@ -17511,7 +17678,7 @@ function buildObservedSection(graph, node) {
|
|
|
17511
17678
|
if (result.observed && result.inboundObservedCount > 0) {
|
|
17512
17679
|
facts.push({
|
|
17513
17680
|
text: `no outbound runtime calls, but OTel observed ${result.inboundObservedCount} inbound call path${result.inboundObservedCount === 1 ? "" : "s"} \u2014 a pure receiver`,
|
|
17514
|
-
provenance:
|
|
17681
|
+
provenance: import_types60.Provenance.OBSERVED
|
|
17515
17682
|
});
|
|
17516
17683
|
} else {
|
|
17517
17684
|
return null;
|
|
@@ -17539,7 +17706,7 @@ function buildIncidentsSection(node, incidents) {
|
|
|
17539
17706
|
const facts = ordered.map((ev) => ({
|
|
17540
17707
|
text: `${ev.timestamp} \u2014 ${ev.service}: ${ev.errorMessage}`,
|
|
17541
17708
|
// ErrorEvents are observation records — OBSERVED by definition.
|
|
17542
|
-
provenance:
|
|
17709
|
+
provenance: import_types60.Provenance.OBSERVED
|
|
17543
17710
|
}));
|
|
17544
17711
|
return { heading: `Recent incidents (OBSERVED) \u2014 ${relevant.length} recorded`, facts };
|
|
17545
17712
|
}
|
|
@@ -17599,7 +17766,7 @@ function buildGlobalIncidentsSection(incidents) {
|
|
|
17599
17766
|
}
|
|
17600
17767
|
const byKey = /* @__PURE__ */ new Map();
|
|
17601
17768
|
for (const ev of incidents) {
|
|
17602
|
-
const key = ev.affectedNode || (0,
|
|
17769
|
+
const key = ev.affectedNode || (0, import_types60.serviceId)(ev.service);
|
|
17603
17770
|
const cur = byKey.get(key);
|
|
17604
17771
|
if (!cur) {
|
|
17605
17772
|
byKey.set(key, { key, count: 1, latest: ev.timestamp, sampleMsg: ev.errorMessage });
|
|
@@ -17614,7 +17781,7 @@ function buildGlobalIncidentsSection(incidents) {
|
|
|
17614
17781
|
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);
|
|
17615
17782
|
const facts = rows.map((r) => ({
|
|
17616
17783
|
text: `${r.key} \u2014 ${r.count} incident${r.count === 1 ? "" : "s"}, latest ${r.latest}: ${r.sampleMsg}`,
|
|
17617
|
-
provenance:
|
|
17784
|
+
provenance: import_types60.Provenance.OBSERVED
|
|
17618
17785
|
}));
|
|
17619
17786
|
return {
|
|
17620
17787
|
heading: `Incidents across the system \u2014 ${incidents.length} recorded`,
|
|
@@ -17627,7 +17794,7 @@ function buildOverviewSections(graph, incidents) {
|
|
|
17627
17794
|
graph.forEachNode((_id, attrs) => {
|
|
17628
17795
|
const node = attrs;
|
|
17629
17796
|
nodeByType.set(node.type, (nodeByType.get(node.type) ?? 0) + 1);
|
|
17630
|
-
if (node.type ===
|
|
17797
|
+
if (node.type === import_types60.NodeType.ServiceNode) services.push(node.id);
|
|
17631
17798
|
});
|
|
17632
17799
|
const edgeByProv = /* @__PURE__ */ new Map();
|
|
17633
17800
|
graph.forEachEdge((_id, attrs) => {
|
|
@@ -17639,10 +17806,10 @@ function buildOverviewSections(graph, incidents) {
|
|
|
17639
17806
|
const shapeFacts = [
|
|
17640
17807
|
{ text: `${graph.order} nodes, ${graph.size} edges` },
|
|
17641
17808
|
{
|
|
17642
|
-
text: `${count(
|
|
17809
|
+
text: `${count(import_types60.NodeType.ServiceNode)} services, ${count(import_types60.NodeType.FileNode)} files, ${count(import_types60.NodeType.SymbolNode)} symbols, ${count(import_types60.NodeType.DatabaseNode)} databases`
|
|
17643
17810
|
}
|
|
17644
17811
|
];
|
|
17645
|
-
for (const p of [
|
|
17812
|
+
for (const p of [import_types60.Provenance.EXTRACTED, import_types60.Provenance.OBSERVED, import_types60.Provenance.INFERRED, import_types60.Provenance.STALE]) {
|
|
17646
17813
|
const n = edgeByProv.get(p) ?? 0;
|
|
17647
17814
|
if (n > 0) shapeFacts.push({ text: `${n} ${p} edge${n === 1 ? "" : "s"}`, provenance: p });
|
|
17648
17815
|
}
|
|
@@ -17659,7 +17826,7 @@ function buildOverviewSections(graph, incidents) {
|
|
|
17659
17826
|
if (incidents && incidents.length > 0) {
|
|
17660
17827
|
const incCount = /* @__PURE__ */ new Map();
|
|
17661
17828
|
for (const ev of incidents) {
|
|
17662
|
-
const key = ev.affectedNode || (0,
|
|
17829
|
+
const key = ev.affectedNode || (0, import_types60.serviceId)(ev.service);
|
|
17663
17830
|
incCount.set(key, (incCount.get(key) ?? 0) + 1);
|
|
17664
17831
|
}
|
|
17665
17832
|
const top = [...incCount.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, MAX_FACTS_PER_SECTION);
|
|
@@ -17667,7 +17834,7 @@ function buildOverviewSections(graph, incidents) {
|
|
|
17667
17834
|
heading: `Nodes with incidents \u2014 ${incidents.length} recorded`,
|
|
17668
17835
|
facts: top.map(([k, n]) => ({
|
|
17669
17836
|
text: `${k} \u2014 ${n} incident${n === 1 ? "" : "s"}`,
|
|
17670
|
-
provenance:
|
|
17837
|
+
provenance: import_types60.Provenance.OBSERVED
|
|
17671
17838
|
}))
|
|
17672
17839
|
});
|
|
17673
17840
|
}
|
|
@@ -17803,7 +17970,7 @@ async function askGraph(graph, question, opts = {}) {
|
|
|
17803
17970
|
const provSet = /* @__PURE__ */ new Set();
|
|
17804
17971
|
for (const s of sections) for (const f of s.facts) if (f.provenance) provSet.add(f.provenance);
|
|
17805
17972
|
const confidence = sections[0]?.facts[0]?.confidence;
|
|
17806
|
-
return
|
|
17973
|
+
return import_types60.AskResultSchema.parse({
|
|
17807
17974
|
question,
|
|
17808
17975
|
intent,
|
|
17809
17976
|
matched,
|
|
@@ -17898,7 +18065,7 @@ init_cjs_shims();
|
|
|
17898
18065
|
var import_node_fs37 = require("fs");
|
|
17899
18066
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
17900
18067
|
var import_node_path72 = __toESM(require("path"), 1);
|
|
17901
|
-
var
|
|
18068
|
+
var import_types61 = require("@neat.is/types");
|
|
17902
18069
|
var LOCK_TIMEOUT_MS = 5e3;
|
|
17903
18070
|
var LOCK_RETRY_MS = 50;
|
|
17904
18071
|
function neatHome() {
|
|
@@ -18100,10 +18267,10 @@ async function readRegistry() {
|
|
|
18100
18267
|
throw err;
|
|
18101
18268
|
}
|
|
18102
18269
|
const parsed = JSON.parse(raw);
|
|
18103
|
-
return
|
|
18270
|
+
return import_types61.RegistryFileSchema.parse(parsed);
|
|
18104
18271
|
}
|
|
18105
18272
|
async function writeRegistry(reg) {
|
|
18106
|
-
const validated =
|
|
18273
|
+
const validated = import_types61.RegistryFileSchema.parse(reg);
|
|
18107
18274
|
await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
|
|
18108
18275
|
}
|
|
18109
18276
|
async function getProject(name) {
|
|
@@ -18454,15 +18621,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
|
|
|
18454
18621
|
|
|
18455
18622
|
// src/connectors/index.ts
|
|
18456
18623
|
init_cjs_shims();
|
|
18457
|
-
var
|
|
18624
|
+
var import_types62 = require("@neat.is/types");
|
|
18458
18625
|
var NO_ENV = "unknown";
|
|
18459
18626
|
function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
18460
18627
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
18461
18628
|
const sites = [];
|
|
18462
18629
|
for (const edgeId of graph.inboundEdges(targetNodeId)) {
|
|
18463
18630
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
18464
|
-
if (edge.provenance !==
|
|
18465
|
-
const parsed = (0,
|
|
18631
|
+
if (edge.provenance !== import_types62.Provenance.EXTRACTED) continue;
|
|
18632
|
+
const parsed = (0, import_types62.parseFileId)(edge.source);
|
|
18466
18633
|
if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
|
|
18467
18634
|
const site = { relPath: edge.evidence.file };
|
|
18468
18635
|
if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
|
|
@@ -18473,7 +18640,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
|
18473
18640
|
function routeCallSiteFor(graph, targetNodeId) {
|
|
18474
18641
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
18475
18642
|
const attrs = graph.getNodeAttributes(targetNodeId);
|
|
18476
|
-
if (attrs.type !==
|
|
18643
|
+
if (attrs.type !== import_types62.NodeType.RouteNode || !attrs.path) return void 0;
|
|
18477
18644
|
const site = { relPath: attrs.path };
|
|
18478
18645
|
if (attrs.line !== void 0) site.line = attrs.line;
|
|
18479
18646
|
return site;
|
|
@@ -18503,7 +18670,7 @@ async function runConnectorPoll(connector, ctx, graph, resolveTarget) {
|
|
|
18503
18670
|
errorMessage: signal.incident.errorMessage,
|
|
18504
18671
|
...signal.incident.attributes ? { attributes: signal.incident.attributes } : {},
|
|
18505
18672
|
affectedNode: resolved.targetNodeId
|
|
18506
|
-
});
|
|
18673
|
+
}, ctx.project);
|
|
18507
18674
|
continue;
|
|
18508
18675
|
}
|
|
18509
18676
|
if (resolved.ensureInfraNode) {
|
|
@@ -19085,23 +19252,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
|
|
|
19085
19252
|
|
|
19086
19253
|
// src/connectors/supabase/resolve.ts
|
|
19087
19254
|
init_cjs_shims();
|
|
19088
|
-
var
|
|
19255
|
+
var import_types64 = require("@neat.is/types");
|
|
19089
19256
|
function createSupabaseResolveTarget(graph, config) {
|
|
19090
19257
|
return (signal, _ctx) => {
|
|
19091
19258
|
if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
|
|
19092
19259
|
return null;
|
|
19093
19260
|
}
|
|
19094
|
-
const subResourceId = (0,
|
|
19261
|
+
const subResourceId = (0, import_types64.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
|
|
19095
19262
|
if (graph.hasNode(subResourceId)) {
|
|
19096
|
-
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType:
|
|
19263
|
+
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types64.EdgeType.CALLS };
|
|
19097
19264
|
}
|
|
19098
|
-
const bareResourceId = (0,
|
|
19265
|
+
const bareResourceId = (0, import_types64.infraId)(signal.targetKind, signal.targetName);
|
|
19099
19266
|
if (graph.hasNode(bareResourceId)) {
|
|
19100
|
-
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType:
|
|
19267
|
+
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types64.EdgeType.CALLS };
|
|
19101
19268
|
}
|
|
19102
|
-
const projectLevelId = (0,
|
|
19269
|
+
const projectLevelId = (0, import_types64.infraId)("supabase", config.nodeRef);
|
|
19103
19270
|
if (graph.hasNode(projectLevelId)) {
|
|
19104
|
-
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType:
|
|
19271
|
+
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types64.EdgeType.CALLS };
|
|
19105
19272
|
}
|
|
19106
19273
|
return null;
|
|
19107
19274
|
};
|
|
@@ -19194,7 +19361,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
|
|
|
19194
19361
|
|
|
19195
19362
|
// src/connectors/railway/index.ts
|
|
19196
19363
|
init_cjs_shims();
|
|
19197
|
-
var
|
|
19364
|
+
var import_types68 = require("@neat.is/types");
|
|
19198
19365
|
|
|
19199
19366
|
// src/connectors/railway/client.ts
|
|
19200
19367
|
init_cjs_shims();
|
|
@@ -19345,7 +19512,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
|
|
|
19345
19512
|
const out = [];
|
|
19346
19513
|
graph.forEachNode((_id, attrs) => {
|
|
19347
19514
|
const node = attrs;
|
|
19348
|
-
if (node.type !==
|
|
19515
|
+
if (node.type !== import_types68.NodeType.RouteNode) return;
|
|
19349
19516
|
const route = attrs;
|
|
19350
19517
|
if (route.service !== serviceName) return;
|
|
19351
19518
|
out.push({
|
|
@@ -19449,12 +19616,12 @@ function createRailwayResolveTarget(config) {
|
|
|
19449
19616
|
const serviceName = config.serviceNameById[config.serviceId];
|
|
19450
19617
|
if (!serviceName) return null;
|
|
19451
19618
|
if (signal.targetKind === ROUTE_TARGET_KIND) {
|
|
19452
|
-
return { targetNodeId: signal.targetName, serviceName, edgeType:
|
|
19619
|
+
return { targetNodeId: signal.targetName, serviceName, edgeType: import_types68.EdgeType.CALLS };
|
|
19453
19620
|
}
|
|
19454
19621
|
if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
|
|
19455
19622
|
const peerName = config.serviceNameById[signal.targetName];
|
|
19456
19623
|
if (!peerName) return null;
|
|
19457
|
-
return { targetNodeId: (0,
|
|
19624
|
+
return { targetNodeId: (0, import_types68.serviceId)(peerName), serviceName, edgeType: import_types68.EdgeType.CONNECTS_TO };
|
|
19458
19625
|
}
|
|
19459
19626
|
return null;
|
|
19460
19627
|
};
|
|
@@ -19642,7 +19809,7 @@ function mapLogEntriesToSignals(entries) {
|
|
|
19642
19809
|
|
|
19643
19810
|
// src/connectors/firebase/resolve.ts
|
|
19644
19811
|
init_cjs_shims();
|
|
19645
|
-
var
|
|
19812
|
+
var import_types69 = require("@neat.is/types");
|
|
19646
19813
|
function neatServiceNameFor(resourceType, resourceName, serviceMap) {
|
|
19647
19814
|
switch (resourceType) {
|
|
19648
19815
|
case "cloud_function":
|
|
@@ -19657,7 +19824,7 @@ function routeEntriesFor(graph, serviceName) {
|
|
|
19657
19824
|
const entries = [];
|
|
19658
19825
|
graph.forEachNode((_id, attrs) => {
|
|
19659
19826
|
const node = attrs;
|
|
19660
|
-
if (node.type !==
|
|
19827
|
+
if (node.type !== import_types69.NodeType.RouteNode) return;
|
|
19661
19828
|
const route = attrs;
|
|
19662
19829
|
if (route.service !== serviceName) return;
|
|
19663
19830
|
entries.push({
|
|
@@ -19689,7 +19856,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
|
|
|
19689
19856
|
return {
|
|
19690
19857
|
targetNodeId: match.routeNodeId,
|
|
19691
19858
|
serviceName,
|
|
19692
|
-
edgeType:
|
|
19859
|
+
edgeType: import_types69.EdgeType.CALLS
|
|
19693
19860
|
};
|
|
19694
19861
|
};
|
|
19695
19862
|
}
|
|
@@ -19716,7 +19883,7 @@ init_cjs_shims();
|
|
|
19716
19883
|
|
|
19717
19884
|
// src/connectors/cloudflare/connector.ts
|
|
19718
19885
|
init_cjs_shims();
|
|
19719
|
-
var
|
|
19886
|
+
var import_types71 = require("@neat.is/types");
|
|
19720
19887
|
|
|
19721
19888
|
// src/connectors/cloudflare/client.ts
|
|
19722
19889
|
init_cjs_shims();
|
|
@@ -19880,7 +20047,7 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
19880
20047
|
graph.forEachNode((id, attrs) => {
|
|
19881
20048
|
if (found) return;
|
|
19882
20049
|
const a = attrs;
|
|
19883
|
-
if (a.type ===
|
|
20050
|
+
if (a.type === import_types71.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
|
|
19884
20051
|
found = id;
|
|
19885
20052
|
}
|
|
19886
20053
|
});
|
|
@@ -19892,7 +20059,7 @@ function findMatchingRouteNode(graph, serviceName, method, path78) {
|
|
|
19892
20059
|
graph.forEachNode((id, attrs) => {
|
|
19893
20060
|
if (found) return;
|
|
19894
20061
|
const a = attrs;
|
|
19895
|
-
if (a.type !==
|
|
20062
|
+
if (a.type !== import_types71.NodeType.RouteNode || a.service !== serviceName) return;
|
|
19896
20063
|
if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
|
|
19897
20064
|
const routeMethod = (a.method ?? "").toUpperCase();
|
|
19898
20065
|
if (routeMethod !== "ALL" && routeMethod !== method) return;
|
|
@@ -19911,11 +20078,11 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
19911
20078
|
};
|
|
19912
20079
|
const mapping = config.workers?.[scriptName];
|
|
19913
20080
|
if (mapping) {
|
|
19914
|
-
const wholeFileId = (0,
|
|
20081
|
+
const wholeFileId = (0, import_types71.fileId)(mapping.service, mapping.entryFile);
|
|
19915
20082
|
return {
|
|
19916
20083
|
targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
|
|
19917
20084
|
serviceName: mapping.service,
|
|
19918
|
-
edgeType:
|
|
20085
|
+
edgeType: import_types71.EdgeType.CALLS
|
|
19919
20086
|
};
|
|
19920
20087
|
}
|
|
19921
20088
|
const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
|
|
@@ -19924,13 +20091,13 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
19924
20091
|
return {
|
|
19925
20092
|
targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
|
|
19926
20093
|
serviceName: fileNode.service,
|
|
19927
|
-
edgeType:
|
|
20094
|
+
edgeType: import_types71.EdgeType.CALLS
|
|
19928
20095
|
};
|
|
19929
20096
|
}
|
|
19930
20097
|
return {
|
|
19931
|
-
targetNodeId: (0,
|
|
20098
|
+
targetNodeId: (0, import_types71.infraId)("cloudflare-worker", scriptName),
|
|
19932
20099
|
serviceName: scriptName,
|
|
19933
|
-
edgeType:
|
|
20100
|
+
edgeType: import_types71.EdgeType.CALLS,
|
|
19934
20101
|
ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
|
|
19935
20102
|
};
|
|
19936
20103
|
};
|
|
@@ -20126,14 +20293,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
|
|
|
20126
20293
|
|
|
20127
20294
|
// src/connectors/neon/resolve.ts
|
|
20128
20295
|
init_cjs_shims();
|
|
20129
|
-
var
|
|
20296
|
+
var import_types75 = require("@neat.is/types");
|
|
20130
20297
|
function createNeonResolveTarget(config) {
|
|
20131
20298
|
return (signal) => {
|
|
20132
20299
|
if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
|
|
20133
20300
|
return {
|
|
20134
|
-
targetNodeId: (0,
|
|
20301
|
+
targetNodeId: (0, import_types75.infraId)("sql-table", signal.targetName),
|
|
20135
20302
|
serviceName: config.serviceName,
|
|
20136
|
-
edgeType:
|
|
20303
|
+
edgeType: import_types75.EdgeType.CALLS,
|
|
20137
20304
|
ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
|
|
20138
20305
|
};
|
|
20139
20306
|
};
|
|
@@ -20314,14 +20481,14 @@ function mapLogEntriesToSignals2(entries) {
|
|
|
20314
20481
|
|
|
20315
20482
|
// src/connectors/cloud-run/resolve.ts
|
|
20316
20483
|
init_cjs_shims();
|
|
20317
|
-
var
|
|
20484
|
+
var import_types79 = require("@neat.is/types");
|
|
20318
20485
|
var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
|
|
20319
20486
|
function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
|
|
20320
20487
|
let found = null;
|
|
20321
20488
|
graph.forEachNode((_id, attrs) => {
|
|
20322
20489
|
if (found) return;
|
|
20323
20490
|
const node = attrs;
|
|
20324
|
-
if (node.type !==
|
|
20491
|
+
if (node.type !== import_types79.NodeType.RouteNode) return;
|
|
20325
20492
|
const route = attrs;
|
|
20326
20493
|
if (route.service !== serviceName || !route.pathTemplate) return;
|
|
20327
20494
|
if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
|
|
@@ -20346,13 +20513,13 @@ function createCloudRunResolveTarget(graph, config) {
|
|
|
20346
20513
|
normalizePathTemplate(path78)
|
|
20347
20514
|
);
|
|
20348
20515
|
if (routeNodeId) {
|
|
20349
|
-
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType:
|
|
20516
|
+
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types79.EdgeType.CALLS };
|
|
20350
20517
|
}
|
|
20351
20518
|
}
|
|
20352
20519
|
return {
|
|
20353
|
-
targetNodeId: (0,
|
|
20520
|
+
targetNodeId: (0, import_types79.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
|
|
20354
20521
|
serviceName: mappedService ?? gcpServiceName,
|
|
20355
|
-
edgeType:
|
|
20522
|
+
edgeType: import_types79.EdgeType.CALLS,
|
|
20356
20523
|
ensureInfraNode: {
|
|
20357
20524
|
kind: CLOUD_RUN_SERVICE_INFRA_KIND,
|
|
20358
20525
|
name: gcpServiceName,
|
|
@@ -20534,14 +20701,14 @@ function mapLogEntriesToSignals3(entries) {
|
|
|
20534
20701
|
|
|
20535
20702
|
// src/connectors/gcp-lb/resolve.ts
|
|
20536
20703
|
init_cjs_shims();
|
|
20537
|
-
var
|
|
20704
|
+
var import_types83 = require("@neat.is/types");
|
|
20538
20705
|
var GCP_LB_BACKEND_INFRA_KIND = "gcp-lb-backend";
|
|
20539
20706
|
function findMatchingRouteNode3(graph, serviceName, method, normalizedPath) {
|
|
20540
20707
|
let found = null;
|
|
20541
20708
|
graph.forEachNode((_id, attrs) => {
|
|
20542
20709
|
if (found) return;
|
|
20543
20710
|
const node = attrs;
|
|
20544
|
-
if (node.type !==
|
|
20711
|
+
if (node.type !== import_types83.NodeType.RouteNode) return;
|
|
20545
20712
|
const route = attrs;
|
|
20546
20713
|
if (route.service !== serviceName || !route.pathTemplate) return;
|
|
20547
20714
|
if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
|
|
@@ -20566,13 +20733,13 @@ function createGcpLbResolveTarget(graph, config) {
|
|
|
20566
20733
|
normalizePathTemplate(path78)
|
|
20567
20734
|
);
|
|
20568
20735
|
if (routeNodeId) {
|
|
20569
|
-
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType:
|
|
20736
|
+
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types83.EdgeType.CALLS };
|
|
20570
20737
|
}
|
|
20571
20738
|
}
|
|
20572
20739
|
return {
|
|
20573
|
-
targetNodeId: (0,
|
|
20740
|
+
targetNodeId: (0, import_types83.infraId)(GCP_LB_BACKEND_INFRA_KIND, backendServiceName),
|
|
20574
20741
|
serviceName: mappedService ?? backendServiceName,
|
|
20575
|
-
edgeType:
|
|
20742
|
+
edgeType: import_types83.EdgeType.CALLS,
|
|
20576
20743
|
ensureInfraNode: {
|
|
20577
20744
|
kind: GCP_LB_BACKEND_INFRA_KIND,
|
|
20578
20745
|
name: backendServiceName,
|
|
@@ -20613,7 +20780,7 @@ function createGcpLbConnector(graph, config = {}) {
|
|
|
20613
20780
|
|
|
20614
20781
|
// src/connectors/render/index.ts
|
|
20615
20782
|
init_cjs_shims();
|
|
20616
|
-
var
|
|
20783
|
+
var import_types86 = require("@neat.is/types");
|
|
20617
20784
|
|
|
20618
20785
|
// src/connectors/render/types.ts
|
|
20619
20786
|
init_cjs_shims();
|
|
@@ -20691,7 +20858,7 @@ function buildRenderRouteIndex(graph, serviceName) {
|
|
|
20691
20858
|
const out = [];
|
|
20692
20859
|
graph.forEachNode((_id, attrs) => {
|
|
20693
20860
|
const node = attrs;
|
|
20694
|
-
if (node.type !==
|
|
20861
|
+
if (node.type !== import_types86.NodeType.RouteNode) return;
|
|
20695
20862
|
const route = attrs;
|
|
20696
20863
|
if (route.service !== serviceName) return;
|
|
20697
20864
|
out.push({
|
|
@@ -20776,7 +20943,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
|
|
|
20776
20943
|
function createRenderResolveTarget(config) {
|
|
20777
20944
|
return (signal) => {
|
|
20778
20945
|
if (signal.targetKind === ROUTE_TARGET_KIND2) {
|
|
20779
|
-
return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType:
|
|
20946
|
+
return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types86.EdgeType.CALLS };
|
|
20780
20947
|
}
|
|
20781
20948
|
return null;
|
|
20782
20949
|
};
|
|
@@ -20914,21 +21081,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
|
|
|
20914
21081
|
|
|
20915
21082
|
// src/connectors/planetscale/resolve.ts
|
|
20916
21083
|
init_cjs_shims();
|
|
20917
|
-
var
|
|
21084
|
+
var import_types90 = require("@neat.is/types");
|
|
20918
21085
|
var PLANETSCALE_DATABASE_KIND = "planetscale-database";
|
|
20919
21086
|
function createPlanetscaleResolveTarget(graph, config) {
|
|
20920
21087
|
const databaseName = `${config.organization}/${config.database}`;
|
|
20921
21088
|
return (signal, _ctx) => {
|
|
20922
21089
|
if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
|
|
20923
|
-
const tableId = (0,
|
|
21090
|
+
const tableId = (0, import_types90.infraId)("sql-table", signal.targetName);
|
|
20924
21091
|
if (graph.hasNode(tableId)) {
|
|
20925
|
-
return { targetNodeId: tableId, serviceName: config.serviceName, edgeType:
|
|
21092
|
+
return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types90.EdgeType.CALLS };
|
|
20926
21093
|
}
|
|
20927
|
-
const providerId = (0,
|
|
21094
|
+
const providerId = (0, import_types90.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
|
|
20928
21095
|
return {
|
|
20929
21096
|
targetNodeId: providerId,
|
|
20930
21097
|
serviceName: config.serviceName,
|
|
20931
|
-
edgeType:
|
|
21098
|
+
edgeType: import_types90.EdgeType.CALLS,
|
|
20932
21099
|
ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
|
|
20933
21100
|
};
|
|
20934
21101
|
};
|
|
@@ -21193,7 +21360,7 @@ function mapBuildsToSignals(builds, serviceName) {
|
|
|
21193
21360
|
|
|
21194
21361
|
// src/connectors/eas/resolve.ts
|
|
21195
21362
|
init_cjs_shims();
|
|
21196
|
-
var
|
|
21363
|
+
var import_types95 = require("@neat.is/types");
|
|
21197
21364
|
var NO_ENV2 = "unknown";
|
|
21198
21365
|
var EAS_JSON_PHASES = /* @__PURE__ */ new Set(["READ_EAS_JSON"]);
|
|
21199
21366
|
var APP_CONFIG_PHASES = /* @__PURE__ */ new Set([
|
|
@@ -21209,8 +21376,8 @@ function configBasenamesForPhase(phase) {
|
|
|
21209
21376
|
function configNodeService(graph, configNodeId) {
|
|
21210
21377
|
for (const edgeId of graph.inboundEdges(configNodeId)) {
|
|
21211
21378
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
21212
|
-
if (edge.type !==
|
|
21213
|
-
const parsed = (0,
|
|
21379
|
+
if (edge.type !== import_types95.EdgeType.CONFIGURED_BY) continue;
|
|
21380
|
+
const parsed = (0, import_types95.parseFileId)(edge.source);
|
|
21214
21381
|
if (parsed) return parsed.service;
|
|
21215
21382
|
}
|
|
21216
21383
|
return null;
|
|
@@ -21221,7 +21388,7 @@ function findConfigNode(graph, basenames, serviceName) {
|
|
|
21221
21388
|
graph.forEachNode((id, attrs) => {
|
|
21222
21389
|
if (scoped) return;
|
|
21223
21390
|
const node = attrs;
|
|
21224
|
-
if (node.type !==
|
|
21391
|
+
if (node.type !== import_types95.NodeType.ConfigNode) return;
|
|
21225
21392
|
if (typeof node.name !== "string" || !basenames.includes(node.name)) return;
|
|
21226
21393
|
if (anyMatch === null) anyMatch = id;
|
|
21227
21394
|
if (configNodeService(graph, id) === serviceName) scoped = id;
|
|
@@ -21238,13 +21405,13 @@ function createEasResolveTarget(graph) {
|
|
|
21238
21405
|
if (basenames.length > 0) {
|
|
21239
21406
|
const configNodeId = findConfigNode(graph, basenames, serviceName);
|
|
21240
21407
|
if (configNodeId) {
|
|
21241
|
-
return { targetNodeId: configNodeId, serviceName, edgeType:
|
|
21408
|
+
return { targetNodeId: configNodeId, serviceName, edgeType: import_types95.EdgeType.CALLS };
|
|
21242
21409
|
}
|
|
21243
21410
|
}
|
|
21244
21411
|
return {
|
|
21245
21412
|
targetNodeId: resolveFusedServiceId(graph, serviceName, NO_ENV2),
|
|
21246
21413
|
serviceName,
|
|
21247
|
-
edgeType:
|
|
21414
|
+
edgeType: import_types95.EdgeType.CALLS
|
|
21248
21415
|
};
|
|
21249
21416
|
};
|
|
21250
21417
|
}
|
|
@@ -21831,6 +21998,7 @@ async function startConnectorPolling(input) {
|
|
|
21831
21998
|
registration.connector,
|
|
21832
21999
|
{
|
|
21833
22000
|
projectDir: input.projectDir,
|
|
22001
|
+
project: input.project,
|
|
21834
22002
|
credentials: registration.credentials,
|
|
21835
22003
|
...input.errorsPath ? { errorsPath: input.errorsPath } : {}
|
|
21836
22004
|
},
|
|
@@ -22005,11 +22173,11 @@ function registerRoutes(scope, ctx) {
|
|
|
22005
22173
|
const candidates = req2.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
22006
22174
|
const parsed = [];
|
|
22007
22175
|
for (const c of candidates) {
|
|
22008
|
-
const r =
|
|
22176
|
+
const r = import_types98.DivergenceTypeSchema.safeParse(c);
|
|
22009
22177
|
if (!r.success) {
|
|
22010
22178
|
return reply.code(400).send({
|
|
22011
22179
|
error: `unknown divergence type "${c}"`,
|
|
22012
|
-
allowed:
|
|
22180
|
+
allowed: import_types98.DivergenceTypeSchema.options
|
|
22013
22181
|
});
|
|
22014
22182
|
}
|
|
22015
22183
|
parsed.push(r.data);
|
|
@@ -22126,6 +22294,7 @@ function registerRoutes(scope, ctx) {
|
|
|
22126
22294
|
reg.connector,
|
|
22127
22295
|
{
|
|
22128
22296
|
projectDir: proj.scanPath ?? "",
|
|
22297
|
+
project: proj.name,
|
|
22129
22298
|
credentials: reg.credentials,
|
|
22130
22299
|
...incidentsPath ? { errorsPath: incidentsPath } : {}
|
|
22131
22300
|
},
|
|
@@ -22189,6 +22358,39 @@ function registerRoutes(scope, ctx) {
|
|
|
22189
22358
|
if (!result) return reply.code(404).send({ error: "no root cause found", id: nodeId });
|
|
22190
22359
|
return result;
|
|
22191
22360
|
});
|
|
22361
|
+
scope.get("/graph/incident-card/:nodeId", async (req2, reply) => {
|
|
22362
|
+
const proj = resolveProject(registry, req2, reply, ctx.bootstrap, ctx.singleProject);
|
|
22363
|
+
if (!proj) return;
|
|
22364
|
+
const { nodeId } = req2.params;
|
|
22365
|
+
if (!proj.graph.hasNode(nodeId)) {
|
|
22366
|
+
return reply.code(404).send({ error: "node not found", id: nodeId });
|
|
22367
|
+
}
|
|
22368
|
+
const epath = errorsPathFor(proj);
|
|
22369
|
+
const incidents = epath ? await readErrorEvents(epath) : [];
|
|
22370
|
+
let errorEvent;
|
|
22371
|
+
if (req2.query.errorId) {
|
|
22372
|
+
errorEvent = incidents.find((e) => e.id === req2.query.errorId);
|
|
22373
|
+
if (!errorEvent) {
|
|
22374
|
+
return reply.code(404).send({ error: "error event not found", id: req2.query.errorId });
|
|
22375
|
+
}
|
|
22376
|
+
} else {
|
|
22377
|
+
const svc = nodeId.replace(/^service:/, "");
|
|
22378
|
+
errorEvent = [...incidents].filter((e) => e.affectedNode === nodeId || e.service === svc).sort((a, b) => (b.timestamp ?? "").localeCompare(a.timestamp ?? ""))[0];
|
|
22379
|
+
if (!errorEvent) {
|
|
22380
|
+
return reply.code(404).send({ error: "no incident found for node", id: nodeId });
|
|
22381
|
+
}
|
|
22382
|
+
}
|
|
22383
|
+
const policyPath = ctx.policyFilePathFor(proj);
|
|
22384
|
+
let policies = [];
|
|
22385
|
+
if (policyPath) {
|
|
22386
|
+
try {
|
|
22387
|
+
policies = await loadPolicyFile(policyPath);
|
|
22388
|
+
} catch {
|
|
22389
|
+
policies = [];
|
|
22390
|
+
}
|
|
22391
|
+
}
|
|
22392
|
+
return buildIncidentCard(proj.graph, errorEvent, incidents, policies);
|
|
22393
|
+
});
|
|
22192
22394
|
scope.get("/graph/blast-radius/:nodeId", async (req2, reply) => {
|
|
22193
22395
|
const proj = resolveProject(registry, req2, reply, ctx.bootstrap, ctx.singleProject);
|
|
22194
22396
|
if (!proj) return;
|
|
@@ -22371,7 +22573,7 @@ function registerRoutes(scope, ctx) {
|
|
|
22371
22573
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
22372
22574
|
let violations = await log.readAll();
|
|
22373
22575
|
if (req2.query.severity) {
|
|
22374
|
-
const sev =
|
|
22576
|
+
const sev = import_types98.PolicySeveritySchema.safeParse(req2.query.severity);
|
|
22375
22577
|
if (!sev.success) {
|
|
22376
22578
|
return reply.code(400).send({
|
|
22377
22579
|
error: "invalid severity",
|
|
@@ -22410,7 +22612,7 @@ function registerRoutes(scope, ctx) {
|
|
|
22410
22612
|
scope.post("/policies/check", async (req2, reply) => {
|
|
22411
22613
|
const proj = resolveProject(registry, req2, reply, ctx.bootstrap, ctx.singleProject);
|
|
22412
22614
|
if (!proj) return;
|
|
22413
|
-
const parsed =
|
|
22615
|
+
const parsed = import_types98.PoliciesCheckBodySchema.safeParse(req2.body ?? {});
|
|
22414
22616
|
if (!parsed.success) {
|
|
22415
22617
|
return reply.code(400).send({
|
|
22416
22618
|
error: "invalid /policies/check body",
|
|
@@ -22751,7 +22953,7 @@ function unroutedErrorsPath(neatHome4) {
|
|
|
22751
22953
|
}
|
|
22752
22954
|
|
|
22753
22955
|
// src/daemon.ts
|
|
22754
|
-
var
|
|
22956
|
+
var import_types99 = require("@neat.is/types");
|
|
22755
22957
|
function daemonJsonPath(scanPath) {
|
|
22756
22958
|
return import_node_path75.default.join(scanPath, "neat-out", "daemon.json");
|
|
22757
22959
|
}
|
|
@@ -22890,7 +23092,7 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
|
|
|
22890
23092
|
if (!serviceName) return true;
|
|
22891
23093
|
if (serviceNameMatchesProject(serviceName, project)) return true;
|
|
22892
23094
|
return graph.someNode(
|
|
22893
|
-
(_id, attrs) => attrs.type ===
|
|
23095
|
+
(_id, attrs) => attrs.type === import_types99.NodeType.ServiceNode && attrs.name === serviceName
|
|
22894
23096
|
);
|
|
22895
23097
|
}
|
|
22896
23098
|
async function bootstrapProject(entry2, connectors = [], neatHome4) {
|
|
@@ -23345,7 +23547,12 @@ async function startDaemon(opts = {}) {
|
|
|
23345
23547
|
onErrorSpanSync: async (span) => {
|
|
23346
23548
|
const slot = await resolveTargetSlot(span.service, span.traceId);
|
|
23347
23549
|
if (!slot) return;
|
|
23348
|
-
await makeErrorSpanWriter(
|
|
23550
|
+
await makeErrorSpanWriter(
|
|
23551
|
+
slot.paths.errorsPath,
|
|
23552
|
+
slot.graph,
|
|
23553
|
+
slot.entry.path,
|
|
23554
|
+
slot.entry.name
|
|
23555
|
+
)(span);
|
|
23349
23556
|
},
|
|
23350
23557
|
// Project-scoped route (issue #367) — the URL already named the
|
|
23351
23558
|
// project. Resolution is a direct slot lookup; service.name resolves
|
|
@@ -23368,7 +23575,12 @@ async function startDaemon(opts = {}) {
|
|
|
23368
23575
|
onProjectErrorSpanSync: async (project, span) => {
|
|
23369
23576
|
const slot = await resolveSlotByName(project, span.service, span.traceId);
|
|
23370
23577
|
if (!slot) return;
|
|
23371
|
-
await makeErrorSpanWriter(
|
|
23578
|
+
await makeErrorSpanWriter(
|
|
23579
|
+
slot.paths.errorsPath,
|
|
23580
|
+
slot.graph,
|
|
23581
|
+
slot.entry.path,
|
|
23582
|
+
slot.entry.name
|
|
23583
|
+
)(span);
|
|
23372
23584
|
},
|
|
23373
23585
|
// #881 — 404 a project-scoped POST for a project this daemon doesn't
|
|
23374
23586
|
// host, rather than accepting it and dropping the batch. `slots` covers
|