@neat.is/core 0.10.6-dev.20261007 → 0.10.6-dev.20261009
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-VL7ANDPI.js → chunk-ECBRGTB2.js} +10 -8
- package/dist/{chunk-VL7ANDPI.js.map → chunk-ECBRGTB2.js.map} +1 -1
- package/dist/{chunk-KTJK2PAH.js → chunk-H2BAPN33.js} +311 -611
- package/dist/chunk-H2BAPN33.js.map +1 -0
- package/dist/chunk-MR64ICXX.js +19 -0
- package/dist/chunk-MR64ICXX.js.map +1 -0
- package/dist/{chunk-EFBWQIKG.js → chunk-QPG2QXCW.js} +11 -27
- package/dist/chunk-QPG2QXCW.js.map +1 -0
- package/dist/chunk-RGYC5U2Q.js +511 -0
- package/dist/chunk-RGYC5U2Q.js.map +1 -0
- package/dist/{chunk-LXQCL4MP.js → chunk-TZXSWSHX.js} +2 -2
- package/dist/{chunk-BC53SCT7.js → chunk-VFZ6CXLI.js} +20 -6
- package/dist/chunk-VFZ6CXLI.js.map +1 -0
- package/dist/cli.cjs +409 -77
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +2 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +251 -77
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +165 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -99
- package/dist/index.d.ts +3 -99
- package/dist/index.js +18 -15
- package/dist/neatd.cjs +165 -17
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +7 -4
- package/dist/neatd.js.map +1 -1
- package/dist/otel-grpc-CCCI772O.js +11 -0
- package/dist/registry.cjs +570 -0
- package/dist/registry.cjs.map +1 -0
- package/dist/registry.d.cts +220 -0
- package/dist/registry.d.ts +220 -0
- package/dist/registry.js +62 -0
- package/dist/registry.js.map +1 -0
- package/dist/server.cjs +185 -23
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +10 -6
- package/dist/server.js.map +1 -1
- package/package.json +8 -3
- package/dist/chunk-BC53SCT7.js.map +0 -1
- package/dist/chunk-EFBWQIKG.js.map +0 -1
- package/dist/chunk-KTJK2PAH.js.map +0 -1
- package/dist/otel-grpc-B7ABHLV7.js +0 -10
- /package/dist/{chunk-LXQCL4MP.js.map → chunk-TZXSWSHX.js.map} +0 -0
- /package/dist/{otel-grpc-B7ABHLV7.js.map → otel-grpc-CCCI772O.js.map} +0 -0
|
@@ -1,10 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
findDaemonByProject,
|
|
3
|
+
getProject,
|
|
4
|
+
listProjects
|
|
5
|
+
} from "./chunk-RGYC5U2Q.js";
|
|
1
6
|
import {
|
|
2
7
|
BEARER_DELEGATED,
|
|
3
8
|
columnsFromSqlStatement,
|
|
4
9
|
mountBearerAuth,
|
|
5
10
|
readAuthEnv,
|
|
6
11
|
tableFromSqlStatement
|
|
7
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-QPG2QXCW.js";
|
|
8
13
|
|
|
9
14
|
// src/graph.ts
|
|
10
15
|
import GraphDefault from "graphology";
|
|
@@ -2036,8 +2041,8 @@ function chiRoutesFromSource(source, parser) {
|
|
|
2036
2041
|
chiWalk(tree.rootNode, "", out);
|
|
2037
2042
|
return out;
|
|
2038
2043
|
}
|
|
2039
|
-
function stripChiRegex(
|
|
2040
|
-
return
|
|
2044
|
+
function stripChiRegex(path76) {
|
|
2045
|
+
return path76.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
|
|
2041
2046
|
}
|
|
2042
2047
|
function chiWalk(node, prefix, out) {
|
|
2043
2048
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
@@ -2709,9 +2714,9 @@ function rubyRocketRoute(args) {
|
|
|
2709
2714
|
if (!pair || pair.type !== "pair") continue;
|
|
2710
2715
|
const k = pair.childForFieldName("key");
|
|
2711
2716
|
if (k?.type !== "string") continue;
|
|
2712
|
-
const
|
|
2713
|
-
if (
|
|
2714
|
-
return { path:
|
|
2717
|
+
const path76 = rubyLiteral(k);
|
|
2718
|
+
if (path76 === null) continue;
|
|
2719
|
+
return { path: path76, target: rubyLiteral(pair.childForFieldName("value")) };
|
|
2715
2720
|
}
|
|
2716
2721
|
return null;
|
|
2717
2722
|
}
|
|
@@ -4658,7 +4663,29 @@ function ensureFrontierNode(graph, host, ts) {
|
|
|
4658
4663
|
graph.addNode(id, node);
|
|
4659
4664
|
return id;
|
|
4660
4665
|
}
|
|
4661
|
-
|
|
4666
|
+
var EDGE_ERROR_MESSAGE_MAX = 512;
|
|
4667
|
+
function buildEdgeErrorExemplar(detail, ts) {
|
|
4668
|
+
return {
|
|
4669
|
+
...detail.exceptionType !== void 0 ? { exceptionType: detail.exceptionType } : {},
|
|
4670
|
+
...detail.message !== void 0 ? { message: detail.message.slice(0, EDGE_ERROR_MESSAGE_MAX) } : {},
|
|
4671
|
+
...detail.httpStatusCode !== void 0 ? { httpStatusCode: detail.httpStatusCode } : {},
|
|
4672
|
+
at: ts
|
|
4673
|
+
};
|
|
4674
|
+
}
|
|
4675
|
+
function edgeErrorDetailFromSpan(span) {
|
|
4676
|
+
const exceptionType = span.exception?.type;
|
|
4677
|
+
const message = span.exception?.message;
|
|
4678
|
+
const httpStatusCode = httpResponseStatus(span);
|
|
4679
|
+
if (exceptionType === void 0 && message === void 0 && httpStatusCode === void 0) {
|
|
4680
|
+
return void 0;
|
|
4681
|
+
}
|
|
4682
|
+
return {
|
|
4683
|
+
...exceptionType !== void 0 ? { exceptionType } : {},
|
|
4684
|
+
...message !== void 0 ? { message } : {},
|
|
4685
|
+
...httpStatusCode !== void 0 ? { httpStatusCode } : {}
|
|
4686
|
+
};
|
|
4687
|
+
}
|
|
4688
|
+
function upsertObservedEdge(graph, type, source, target, ts, isError = false, evidence, durationMs, errorDetail) {
|
|
4662
4689
|
if (!graph.hasNode(source) || !graph.hasNode(target)) return null;
|
|
4663
4690
|
const grain = source.startsWith("file:") || source.startsWith("symbol:") ? "file" : "service";
|
|
4664
4691
|
const id = makeObservedEdgeId(type, source, target);
|
|
@@ -4668,13 +4695,15 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false, ev
|
|
|
4668
4695
|
const newErrorCount = (existing.signal?.errorCount ?? 0) + (isError ? 1 : 0);
|
|
4669
4696
|
const latencyHist2 = durationMs !== void 0 ? recordLatency({ ...existing.signal?.latencyHist ?? {} }, durationMs) : existing.signal?.latencyHist;
|
|
4670
4697
|
const latencyMs2 = latencyPercentiles(latencyHist2) ?? existing.signal?.latencyMs;
|
|
4698
|
+
const lastError2 = isError && errorDetail ? buildEdgeErrorExemplar(errorDetail, ts) : existing.signal?.lastError;
|
|
4671
4699
|
const newSignal = {
|
|
4672
4700
|
spanCount: newSpanCount,
|
|
4673
4701
|
errorCount: newErrorCount,
|
|
4674
4702
|
lastObservedAgeMs: 0,
|
|
4675
4703
|
...latencyHist2 ? { latencyHist: latencyHist2 } : {},
|
|
4676
4704
|
...latencyMs2 ? { latencyMs: latencyMs2 } : {},
|
|
4677
|
-
...existing.signal?.anomalous !== void 0 ? { anomalous: existing.signal.anomalous } : {}
|
|
4705
|
+
...existing.signal?.anomalous !== void 0 ? { anomalous: existing.signal.anomalous } : {},
|
|
4706
|
+
...lastError2 ? { lastError: lastError2 } : {}
|
|
4678
4707
|
};
|
|
4679
4708
|
const updated = {
|
|
4680
4709
|
...existing,
|
|
@@ -4691,12 +4720,14 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false, ev
|
|
|
4691
4720
|
}
|
|
4692
4721
|
const latencyHist = durationMs !== void 0 ? recordLatency({}, durationMs) : void 0;
|
|
4693
4722
|
const latencyMs = latencyHist ? latencyPercentiles(latencyHist) : void 0;
|
|
4723
|
+
const lastError = isError && errorDetail ? buildEdgeErrorExemplar(errorDetail, ts) : void 0;
|
|
4694
4724
|
const signal = {
|
|
4695
4725
|
spanCount: 1,
|
|
4696
4726
|
errorCount: isError ? 1 : 0,
|
|
4697
4727
|
lastObservedAgeMs: 0,
|
|
4698
4728
|
...latencyHist ? { latencyHist } : {},
|
|
4699
|
-
...latencyMs ? { latencyMs } : {}
|
|
4729
|
+
...latencyMs ? { latencyMs } : {},
|
|
4730
|
+
...lastError ? { lastError } : {}
|
|
4700
4731
|
};
|
|
4701
4732
|
const edge = {
|
|
4702
4733
|
id,
|
|
@@ -5037,6 +5068,7 @@ async function handleSpan(ctx, span) {
|
|
|
5037
5068
|
const sourceId = ensureServiceNode(ctx.graph, span.service, env);
|
|
5038
5069
|
const isError = spanRecordsError(span);
|
|
5039
5070
|
const durationMs = span.durationNanos > 0n && !spanIsStreaming(span) ? Number(span.durationNanos) / 1e6 : void 0;
|
|
5071
|
+
const errorDetail = isError ? edgeErrorDetailFromSpan(span) : void 0;
|
|
5040
5072
|
const sourceServiceNode = ctx.graph.getNodeAttributes(sourceId);
|
|
5041
5073
|
const callSite = callSiteFromSpan(span, sourceServiceNode, ctx.scanPath);
|
|
5042
5074
|
cacheSpanService(span, nowMs, callSite);
|
|
@@ -5076,7 +5108,8 @@ async function handleSpan(ctx, span) {
|
|
|
5076
5108
|
ts,
|
|
5077
5109
|
isError,
|
|
5078
5110
|
callSiteEvidence,
|
|
5079
|
-
durationMs
|
|
5111
|
+
durationMs,
|
|
5112
|
+
errorDetail
|
|
5080
5113
|
);
|
|
5081
5114
|
if (result) affectedNode = targetId;
|
|
5082
5115
|
if (span.dbSystem === "mongodb" && span.dbCollection) {
|
|
@@ -5089,7 +5122,8 @@ async function handleSpan(ctx, span) {
|
|
|
5089
5122
|
ts,
|
|
5090
5123
|
isError,
|
|
5091
5124
|
callSiteEvidence,
|
|
5092
|
-
durationMs
|
|
5125
|
+
durationMs,
|
|
5126
|
+
errorDetail
|
|
5093
5127
|
);
|
|
5094
5128
|
}
|
|
5095
5129
|
if (span.dbTable) {
|
|
@@ -5102,7 +5136,8 @@ async function handleSpan(ctx, span) {
|
|
|
5102
5136
|
ts,
|
|
5103
5137
|
isError,
|
|
5104
5138
|
callSiteEvidence,
|
|
5105
|
-
durationMs
|
|
5139
|
+
durationMs,
|
|
5140
|
+
errorDetail
|
|
5106
5141
|
);
|
|
5107
5142
|
mergeObservedColumns(ctx.graph, tableId, span.dbColumns);
|
|
5108
5143
|
}
|
|
@@ -5122,7 +5157,8 @@ async function handleSpan(ctx, span) {
|
|
|
5122
5157
|
ts,
|
|
5123
5158
|
isError,
|
|
5124
5159
|
callSiteEvidence,
|
|
5125
|
-
durationMs
|
|
5160
|
+
durationMs,
|
|
5161
|
+
errorDetail
|
|
5126
5162
|
);
|
|
5127
5163
|
if (result) affectedNode = targetId;
|
|
5128
5164
|
} else if (span.graphqlOperationName && span.graphqlOperationType && spanServesGraphqlOperation(span.kind)) {
|
|
@@ -5140,7 +5176,8 @@ async function handleSpan(ctx, span) {
|
|
|
5140
5176
|
ts,
|
|
5141
5177
|
isError,
|
|
5142
5178
|
callSiteEvidence,
|
|
5143
|
-
durationMs
|
|
5179
|
+
durationMs,
|
|
5180
|
+
errorDetail
|
|
5144
5181
|
);
|
|
5145
5182
|
if (result) affectedNode = targetId;
|
|
5146
5183
|
} else if (span.rpcSystem === "grpc" && span.rpcService && span.rpcMethod && spanServesGrpcMethod(span.kind)) {
|
|
@@ -5153,7 +5190,8 @@ async function handleSpan(ctx, span) {
|
|
|
5153
5190
|
ts,
|
|
5154
5191
|
isError,
|
|
5155
5192
|
callSiteEvidence,
|
|
5156
|
-
durationMs
|
|
5193
|
+
durationMs,
|
|
5194
|
+
errorDetail
|
|
5157
5195
|
);
|
|
5158
5196
|
if (result) affectedNode = targetId;
|
|
5159
5197
|
} else if (span.websocketChannel && spanServesWebsocketChannel(span.kind)) {
|
|
@@ -5170,7 +5208,8 @@ async function handleSpan(ctx, span) {
|
|
|
5170
5208
|
ts,
|
|
5171
5209
|
isError,
|
|
5172
5210
|
callSiteEvidence,
|
|
5173
|
-
durationMs
|
|
5211
|
+
durationMs,
|
|
5212
|
+
errorDetail
|
|
5174
5213
|
);
|
|
5175
5214
|
if (result) affectedNode = targetId;
|
|
5176
5215
|
} else {
|
|
@@ -5187,7 +5226,8 @@ async function handleSpan(ctx, span) {
|
|
|
5187
5226
|
ts,
|
|
5188
5227
|
isError,
|
|
5189
5228
|
callSiteEvidence,
|
|
5190
|
-
durationMs
|
|
5229
|
+
durationMs,
|
|
5230
|
+
errorDetail
|
|
5191
5231
|
);
|
|
5192
5232
|
affectedNode = targetId;
|
|
5193
5233
|
resolvedViaAddress = true;
|
|
@@ -5201,7 +5241,8 @@ async function handleSpan(ctx, span) {
|
|
|
5201
5241
|
ts,
|
|
5202
5242
|
isError,
|
|
5203
5243
|
callSiteEvidence,
|
|
5204
|
-
durationMs
|
|
5244
|
+
durationMs,
|
|
5245
|
+
errorDetail
|
|
5205
5246
|
);
|
|
5206
5247
|
affectedNode = frontierNodeId;
|
|
5207
5248
|
resolvedViaAddress = true;
|
|
@@ -5228,7 +5269,8 @@ async function handleSpan(ctx, span) {
|
|
|
5228
5269
|
ts,
|
|
5229
5270
|
isError,
|
|
5230
5271
|
fallbackEvidence,
|
|
5231
|
-
durationMs
|
|
5272
|
+
durationMs,
|
|
5273
|
+
errorDetail
|
|
5232
5274
|
);
|
|
5233
5275
|
}
|
|
5234
5276
|
}
|
|
@@ -5243,7 +5285,7 @@ async function handleSpan(ctx, span) {
|
|
|
5243
5285
|
);
|
|
5244
5286
|
if (routeNodeId) {
|
|
5245
5287
|
const routeSvc = ctx.graph.getNodeAttributes(routeNodeId).service;
|
|
5246
|
-
upsertObservedEdge(ctx.graph, EdgeType5.CONTAINS, serviceId(routeSvc), routeNodeId, ts, isError, void 0, durationMs);
|
|
5288
|
+
upsertObservedEdge(ctx.graph, EdgeType5.CONTAINS, serviceId(routeSvc), routeNodeId, ts, isError, void 0, durationMs, errorDetail);
|
|
5247
5289
|
}
|
|
5248
5290
|
}
|
|
5249
5291
|
if (span.statusCode === 2) {
|
|
@@ -5726,19 +5768,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
5726
5768
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
5727
5769
|
let best = { path: [start], edges: [] };
|
|
5728
5770
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
5729
|
-
function step(node,
|
|
5730
|
-
if (
|
|
5731
|
-
best = { path: [...
|
|
5771
|
+
function step(node, path76, edges) {
|
|
5772
|
+
if (path76.length > best.path.length) {
|
|
5773
|
+
best = { path: [...path76], edges: [...edges] };
|
|
5732
5774
|
}
|
|
5733
|
-
if (
|
|
5775
|
+
if (path76.length - 1 >= maxDepth) return;
|
|
5734
5776
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
5735
5777
|
for (const [srcId, edge] of incoming) {
|
|
5736
5778
|
if (visited.has(srcId)) continue;
|
|
5737
5779
|
visited.add(srcId);
|
|
5738
|
-
|
|
5780
|
+
path76.push(srcId);
|
|
5739
5781
|
edges.push(edge);
|
|
5740
|
-
step(srcId,
|
|
5741
|
-
|
|
5782
|
+
step(srcId, path76, edges);
|
|
5783
|
+
path76.pop();
|
|
5742
5784
|
edges.pop();
|
|
5743
5785
|
visited.delete(srcId);
|
|
5744
5786
|
}
|
|
@@ -5953,20 +5995,20 @@ function dominantFailingCall(graph, serviceId17, visited) {
|
|
|
5953
5995
|
return best;
|
|
5954
5996
|
}
|
|
5955
5997
|
function followFailingCallChain(graph, originServiceId, maxDepth) {
|
|
5956
|
-
const
|
|
5998
|
+
const path76 = [originServiceId];
|
|
5957
5999
|
const edges = [];
|
|
5958
6000
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
5959
6001
|
let current = originServiceId;
|
|
5960
6002
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
5961
6003
|
const hop = dominantFailingCall(graph, current, visited);
|
|
5962
6004
|
if (!hop) break;
|
|
5963
|
-
|
|
6005
|
+
path76.push(hop.nextService);
|
|
5964
6006
|
edges.push(hop.edge);
|
|
5965
6007
|
visited.add(hop.nextService);
|
|
5966
6008
|
current = hop.nextService;
|
|
5967
6009
|
}
|
|
5968
6010
|
if (edges.length === 0) return null;
|
|
5969
|
-
return { path:
|
|
6011
|
+
return { path: path76, edges, culprit: current };
|
|
5970
6012
|
}
|
|
5971
6013
|
function isStaleCallEdge(e) {
|
|
5972
6014
|
return e.type === EdgeType6.CALLS && e.provenance === Provenance6.STALE;
|
|
@@ -6002,26 +6044,26 @@ function dominantStaleCall(graph, serviceId17, visited) {
|
|
|
6002
6044
|
return best;
|
|
6003
6045
|
}
|
|
6004
6046
|
function followStaleCallChain(graph, originServiceId, maxDepth) {
|
|
6005
|
-
const
|
|
6047
|
+
const path76 = [originServiceId];
|
|
6006
6048
|
const edges = [];
|
|
6007
6049
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
6008
6050
|
let current = originServiceId;
|
|
6009
6051
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
6010
6052
|
const hop = dominantStaleCall(graph, current, visited);
|
|
6011
6053
|
if (!hop) break;
|
|
6012
|
-
|
|
6054
|
+
path76.push(hop.nextService);
|
|
6013
6055
|
edges.push(hop.edge);
|
|
6014
6056
|
visited.add(hop.nextService);
|
|
6015
6057
|
current = hop.nextService;
|
|
6016
6058
|
}
|
|
6017
6059
|
if (edges.length === 0) return null;
|
|
6018
|
-
return { path:
|
|
6060
|
+
return { path: path76, edges, culprit: current };
|
|
6019
6061
|
}
|
|
6020
6062
|
function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
6021
6063
|
const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
|
|
6022
6064
|
if (!chain) return null;
|
|
6023
6065
|
const culprit = chain.culprit;
|
|
6024
|
-
const
|
|
6066
|
+
const path76 = [...chain.path];
|
|
6025
6067
|
const edgeProvenances = chain.edges.map((e) => e.provenance);
|
|
6026
6068
|
const baseConfidence = confidenceFromMix(chain.edges);
|
|
6027
6069
|
const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
|
|
@@ -6029,14 +6071,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
6029
6071
|
if (loc) {
|
|
6030
6072
|
let rootCauseNode = culprit;
|
|
6031
6073
|
if (loc.fileNode) {
|
|
6032
|
-
|
|
6074
|
+
path76.push(loc.fileNode);
|
|
6033
6075
|
edgeProvenances.push(Provenance6.OBSERVED);
|
|
6034
6076
|
rootCauseNode = loc.fileNode;
|
|
6035
6077
|
}
|
|
6036
6078
|
return RootCauseResultSchema.parse({
|
|
6037
6079
|
rootCauseNode,
|
|
6038
6080
|
rootCauseReason: loc.rootCauseReason,
|
|
6039
|
-
traversalPath:
|
|
6081
|
+
traversalPath: path76,
|
|
6040
6082
|
edgeProvenances,
|
|
6041
6083
|
confidence,
|
|
6042
6084
|
...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
|
|
@@ -6048,7 +6090,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
6048
6090
|
return RootCauseResultSchema.parse({
|
|
6049
6091
|
rootCauseNode: culprit,
|
|
6050
6092
|
rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
|
|
6051
|
-
traversalPath:
|
|
6093
|
+
traversalPath: path76,
|
|
6052
6094
|
edgeProvenances,
|
|
6053
6095
|
confidence,
|
|
6054
6096
|
fixRecommendation: `Inspect ${culpritName}'s failing handler`
|
|
@@ -6701,10 +6743,10 @@ function enrichWithNavigation(graph, errorNodeId, tagged, incidents, now) {
|
|
|
6701
6743
|
traversalPath = staleChain.path;
|
|
6702
6744
|
edgeProvenances = staleChain.edges.map((e) => e.provenance);
|
|
6703
6745
|
} else if (top.node !== seedNode) {
|
|
6704
|
-
const
|
|
6705
|
-
if (
|
|
6706
|
-
traversalPath =
|
|
6707
|
-
edgeProvenances =
|
|
6746
|
+
const path76 = findPath(graph, errorNodeId, top.node, "up", ROOT_CAUSE_MAX_DEPTH);
|
|
6747
|
+
if (path76) {
|
|
6748
|
+
traversalPath = path76.nodes;
|
|
6749
|
+
edgeProvenances = path76.edges.map((e) => e.provenance);
|
|
6708
6750
|
} else {
|
|
6709
6751
|
traversalPath = [errorNodeId, top.node];
|
|
6710
6752
|
edgeProvenances = [top.provenance ?? Provenance6.OBSERVED];
|
|
@@ -16320,6 +16362,11 @@ function detectObservedFailingEdge(graph, bucket) {
|
|
|
16320
16362
|
spanCount,
|
|
16321
16363
|
errorCount,
|
|
16322
16364
|
errorRate: clampConfidence(errorRate),
|
|
16365
|
+
// The bounded last-error exemplar rides the finding as evidence (ADR-236):
|
|
16366
|
+
// the error rate says HOW MUCH is failing, the exemplar says WHAT failed —
|
|
16367
|
+
// a timeout vs a deadlock vs a connection-refused, which `errorCount`
|
|
16368
|
+
// alone cannot name. Present only when the observed edge carries one.
|
|
16369
|
+
...signal.lastError ? { lastError: signal.lastError } : {},
|
|
16323
16370
|
confidence: OBSERVED_FAILING_CONFIDENCE,
|
|
16324
16371
|
reason: `Code declares ${bucket.source} \u2192 ${bucket.target} (${bucket.type}) and production observes it, but ${errorCount}/${spanCount} observed calls fail (${pct}% error rate) \u2014 the declared dependency is predominantly failing. The declared intent (EXTRACTED) and the observed behaviour (OBSERVED) diverge.`,
|
|
16325
16372
|
recommendation: "Treat this as a broken declared dependency, not a coverage gap: production runs the call the code declares and most calls error. Check the dependency\u2019s health, recent deploys, and the observed error responses on this edge."
|
|
@@ -16911,440 +16958,6 @@ function parseExtraProjects(raw) {
|
|
|
16911
16958
|
return raw.split(",").map((p) => p.trim()).filter((p) => p.length > 0 && p !== DEFAULT_PROJECT);
|
|
16912
16959
|
}
|
|
16913
16960
|
|
|
16914
|
-
// src/registry.ts
|
|
16915
|
-
import { promises as fs36 } from "fs";
|
|
16916
|
-
import os2 from "os";
|
|
16917
|
-
import path71 from "path";
|
|
16918
|
-
import {
|
|
16919
|
-
RegistryFileSchema
|
|
16920
|
-
} from "@neat.is/types";
|
|
16921
|
-
var LOCK_TIMEOUT_MS = 5e3;
|
|
16922
|
-
var LOCK_RETRY_MS = 50;
|
|
16923
|
-
function neatHome() {
|
|
16924
|
-
const override = process.env.NEAT_HOME;
|
|
16925
|
-
if (override && override.length > 0) return path71.resolve(override);
|
|
16926
|
-
return path71.join(os2.homedir(), ".neat");
|
|
16927
|
-
}
|
|
16928
|
-
function registryPath() {
|
|
16929
|
-
return path71.join(neatHome(), "projects.json");
|
|
16930
|
-
}
|
|
16931
|
-
function registryLockPath() {
|
|
16932
|
-
return path71.join(neatHome(), "projects.json.lock");
|
|
16933
|
-
}
|
|
16934
|
-
function daemonPidPath() {
|
|
16935
|
-
return path71.join(neatHome(), "neatd.pid");
|
|
16936
|
-
}
|
|
16937
|
-
function daemonsDir() {
|
|
16938
|
-
return path71.join(neatHome(), "daemons");
|
|
16939
|
-
}
|
|
16940
|
-
function isFiniteInt(v) {
|
|
16941
|
-
return typeof v === "number" && Number.isFinite(v);
|
|
16942
|
-
}
|
|
16943
|
-
function parseDaemonRecord(raw) {
|
|
16944
|
-
let obj;
|
|
16945
|
-
try {
|
|
16946
|
-
obj = JSON.parse(raw);
|
|
16947
|
-
} catch {
|
|
16948
|
-
return void 0;
|
|
16949
|
-
}
|
|
16950
|
-
if (typeof obj !== "object" || obj === null) return void 0;
|
|
16951
|
-
const r = obj;
|
|
16952
|
-
const ports = r.ports;
|
|
16953
|
-
if (typeof r.project !== "string" || typeof r.projectPath !== "string" || !isFiniteInt(r.pid) || r.status !== "running" && r.status !== "stopped" || typeof r.startedAt !== "string" || typeof r.neatVersion !== "string" || !ports || !isFiniteInt(ports.rest) || !isFiniteInt(ports.otlp) || !isFiniteInt(ports.web)) {
|
|
16954
|
-
return void 0;
|
|
16955
|
-
}
|
|
16956
|
-
return {
|
|
16957
|
-
project: r.project,
|
|
16958
|
-
projectPath: r.projectPath,
|
|
16959
|
-
pid: r.pid,
|
|
16960
|
-
status: r.status,
|
|
16961
|
-
ports: { rest: ports.rest, otlp: ports.otlp, web: ports.web },
|
|
16962
|
-
startedAt: r.startedAt,
|
|
16963
|
-
neatVersion: r.neatVersion
|
|
16964
|
-
};
|
|
16965
|
-
}
|
|
16966
|
-
var defaultDiscoveryProbe = { isPidAlive: isPidAliveDefault };
|
|
16967
|
-
async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
16968
|
-
const dir = daemonsDir();
|
|
16969
|
-
let names;
|
|
16970
|
-
try {
|
|
16971
|
-
names = await fs36.readdir(dir);
|
|
16972
|
-
} catch (err) {
|
|
16973
|
-
if (err.code === "ENOENT") return [];
|
|
16974
|
-
throw err;
|
|
16975
|
-
}
|
|
16976
|
-
const out = [];
|
|
16977
|
-
for (const name of names) {
|
|
16978
|
-
if (!name.endsWith(".json")) continue;
|
|
16979
|
-
const file = path71.join(dir, name);
|
|
16980
|
-
let raw;
|
|
16981
|
-
try {
|
|
16982
|
-
raw = await fs36.readFile(file, "utf8");
|
|
16983
|
-
} catch {
|
|
16984
|
-
continue;
|
|
16985
|
-
}
|
|
16986
|
-
const record = parseDaemonRecord(raw);
|
|
16987
|
-
if (!record) continue;
|
|
16988
|
-
const live = record.status === "running" && probe.isPidAlive(record.pid);
|
|
16989
|
-
out.push({ record, live, source: file });
|
|
16990
|
-
}
|
|
16991
|
-
out.sort((a, b) => a.record.project.localeCompare(b.record.project));
|
|
16992
|
-
return out;
|
|
16993
|
-
}
|
|
16994
|
-
async function removeDaemonRecord(source) {
|
|
16995
|
-
await fs36.unlink(source).catch(() => {
|
|
16996
|
-
});
|
|
16997
|
-
}
|
|
16998
|
-
async function listMachineProjects(probe = defaultDiscoveryProbe) {
|
|
16999
|
-
const discovered = await discoverDaemons(probe);
|
|
17000
|
-
const byPath = /* @__PURE__ */ new Map();
|
|
17001
|
-
for (const d of discovered) {
|
|
17002
|
-
const key = await normalizeProjectPath(d.record.projectPath);
|
|
17003
|
-
byPath.set(key, {
|
|
17004
|
-
project: d.record.project,
|
|
17005
|
-
projectPath: d.record.projectPath,
|
|
17006
|
-
state: d.live ? "running" : "stopped",
|
|
17007
|
-
ports: d.record.ports,
|
|
17008
|
-
pid: d.record.pid
|
|
17009
|
-
});
|
|
17010
|
-
}
|
|
17011
|
-
let legacy = [];
|
|
17012
|
-
try {
|
|
17013
|
-
legacy = (await readRegistry()).projects;
|
|
17014
|
-
} catch {
|
|
17015
|
-
legacy = [];
|
|
17016
|
-
}
|
|
17017
|
-
for (const entry of legacy) {
|
|
17018
|
-
const key = await normalizeProjectPath(entry.path);
|
|
17019
|
-
if (byPath.has(key)) continue;
|
|
17020
|
-
byPath.set(key, {
|
|
17021
|
-
project: entry.name,
|
|
17022
|
-
projectPath: entry.path,
|
|
17023
|
-
state: "registered",
|
|
17024
|
-
registryStatus: entry.status
|
|
17025
|
-
});
|
|
17026
|
-
}
|
|
17027
|
-
return [...byPath.values()].sort((a, b) => a.project.localeCompare(b.project));
|
|
17028
|
-
}
|
|
17029
|
-
async function findDaemonByProject(name, probe = defaultDiscoveryProbe) {
|
|
17030
|
-
const discovered = await discoverDaemons(probe);
|
|
17031
|
-
return discovered.find((d) => d.record.project === name);
|
|
17032
|
-
}
|
|
17033
|
-
function signalDaemonStop(pid) {
|
|
17034
|
-
try {
|
|
17035
|
-
process.kill(pid, "SIGTERM");
|
|
17036
|
-
return true;
|
|
17037
|
-
} catch (err) {
|
|
17038
|
-
if (err.code === "ESRCH") return false;
|
|
17039
|
-
throw err;
|
|
17040
|
-
}
|
|
17041
|
-
}
|
|
17042
|
-
function isPidAliveDefault(pid) {
|
|
17043
|
-
try {
|
|
17044
|
-
process.kill(pid, 0);
|
|
17045
|
-
return true;
|
|
17046
|
-
} catch (err) {
|
|
17047
|
-
return err.code === "EPERM";
|
|
17048
|
-
}
|
|
17049
|
-
}
|
|
17050
|
-
async function readPidFile(file) {
|
|
17051
|
-
try {
|
|
17052
|
-
const raw = await fs36.readFile(file, "utf8");
|
|
17053
|
-
const pid = Number.parseInt(raw.trim(), 10);
|
|
17054
|
-
return Number.isInteger(pid) && pid > 0 ? pid : void 0;
|
|
17055
|
-
} catch {
|
|
17056
|
-
return void 0;
|
|
17057
|
-
}
|
|
17058
|
-
}
|
|
17059
|
-
var defaultLockHolderProbe = {
|
|
17060
|
-
isPidAlive: isPidAliveDefault,
|
|
17061
|
-
daemonPidFromFile: () => readPidFile(daemonPidPath()),
|
|
17062
|
-
async daemonResponds() {
|
|
17063
|
-
const base = process.env.NEAT_API_URL ?? "http://localhost:8080";
|
|
17064
|
-
try {
|
|
17065
|
-
await fetch(`${base}/health`, { signal: AbortSignal.timeout(750) });
|
|
17066
|
-
return true;
|
|
17067
|
-
} catch {
|
|
17068
|
-
return false;
|
|
17069
|
-
}
|
|
17070
|
-
}
|
|
17071
|
-
};
|
|
17072
|
-
async function readLockPid(lockPath) {
|
|
17073
|
-
return readPidFile(lockPath);
|
|
17074
|
-
}
|
|
17075
|
-
async function classifyLockHolder(lockPath, probe = defaultLockHolderProbe) {
|
|
17076
|
-
const lockPid = await readLockPid(lockPath);
|
|
17077
|
-
const daemonPid = await probe.daemonPidFromFile();
|
|
17078
|
-
if (lockPid !== void 0 && lockPid !== process.pid && probe.isPidAlive(lockPid)) {
|
|
17079
|
-
return { kind: lockPid === daemonPid ? "daemon" : "command", pid: lockPid };
|
|
17080
|
-
}
|
|
17081
|
-
if (daemonPid !== void 0 && daemonPid !== process.pid && probe.isPidAlive(daemonPid) && // Guards against a stale pidfile whose PID got reused.
|
|
17082
|
-
await probe.daemonResponds()) {
|
|
17083
|
-
return { kind: "daemon", pid: daemonPid };
|
|
17084
|
-
}
|
|
17085
|
-
return { kind: "stale" };
|
|
17086
|
-
}
|
|
17087
|
-
function lockHolderMessage(holder, lockPath, timeoutMs) {
|
|
17088
|
-
switch (holder.kind) {
|
|
17089
|
-
case "daemon":
|
|
17090
|
-
return `The neat daemon (pid ${holder.pid}) is holding the registry lock. Register this project through the daemon, or stop neatd and re-run \`neat init\`.`;
|
|
17091
|
-
case "command":
|
|
17092
|
-
return `Another neat command (pid ${holder.pid}) is holding the registry lock. Wait for it to finish, or check \`ps\` if you're not sure what's running.`;
|
|
17093
|
-
case "stale":
|
|
17094
|
-
return `timed out after ${timeoutMs}ms waiting for ${lockPath}. Another neat process is holding the lock; if no such process exists, remove the file by hand.`;
|
|
17095
|
-
}
|
|
17096
|
-
}
|
|
17097
|
-
async function normalizeProjectPath(input) {
|
|
17098
|
-
const resolved = path71.resolve(input);
|
|
17099
|
-
try {
|
|
17100
|
-
return await fs36.realpath(resolved);
|
|
17101
|
-
} catch {
|
|
17102
|
-
return resolved;
|
|
17103
|
-
}
|
|
17104
|
-
}
|
|
17105
|
-
async function writeAtomically(target, contents) {
|
|
17106
|
-
await fs36.mkdir(path71.dirname(target), { recursive: true });
|
|
17107
|
-
const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
17108
|
-
const fd = await fs36.open(tmp, "w");
|
|
17109
|
-
try {
|
|
17110
|
-
await fd.writeFile(contents, "utf8");
|
|
17111
|
-
await fd.sync();
|
|
17112
|
-
} finally {
|
|
17113
|
-
await fd.close();
|
|
17114
|
-
}
|
|
17115
|
-
await fs36.rename(tmp, target);
|
|
17116
|
-
}
|
|
17117
|
-
async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
|
|
17118
|
-
const deadline = Date.now() + timeoutMs;
|
|
17119
|
-
await fs36.mkdir(path71.dirname(lockPath), { recursive: true });
|
|
17120
|
-
let probedHolder = false;
|
|
17121
|
-
while (true) {
|
|
17122
|
-
try {
|
|
17123
|
-
const fd = await fs36.open(lockPath, "wx");
|
|
17124
|
-
try {
|
|
17125
|
-
await fd.writeFile(`${process.pid}
|
|
17126
|
-
`, "utf8");
|
|
17127
|
-
} finally {
|
|
17128
|
-
await fd.close();
|
|
17129
|
-
}
|
|
17130
|
-
return;
|
|
17131
|
-
} catch (err) {
|
|
17132
|
-
const code = err.code;
|
|
17133
|
-
if (code !== "EEXIST") throw err;
|
|
17134
|
-
if (!probedHolder) {
|
|
17135
|
-
probedHolder = true;
|
|
17136
|
-
const holder = await classifyLockHolder(lockPath, probe);
|
|
17137
|
-
if (holder.kind === "daemon") {
|
|
17138
|
-
throw new RegistryLockError(lockHolderMessage(holder, lockPath, timeoutMs));
|
|
17139
|
-
}
|
|
17140
|
-
if (holder.kind === "stale" && await isReclaimableLock(lockPath)) {
|
|
17141
|
-
await fs36.unlink(lockPath).catch(() => {
|
|
17142
|
-
});
|
|
17143
|
-
continue;
|
|
17144
|
-
}
|
|
17145
|
-
}
|
|
17146
|
-
if (Date.now() >= deadline) {
|
|
17147
|
-
const holder = await classifyLockHolder(lockPath, probe);
|
|
17148
|
-
throw new RegistryLockError(lockHolderMessage(holder, lockPath, timeoutMs));
|
|
17149
|
-
}
|
|
17150
|
-
await new Promise((r) => setTimeout(r, LOCK_RETRY_MS));
|
|
17151
|
-
}
|
|
17152
|
-
}
|
|
17153
|
-
}
|
|
17154
|
-
var LOCK_UNSTAMPED_GRACE_MS = 5e3;
|
|
17155
|
-
async function isReclaimableLock(lockPath) {
|
|
17156
|
-
const pid = await readLockPid(lockPath);
|
|
17157
|
-
if (pid !== void 0) return true;
|
|
17158
|
-
try {
|
|
17159
|
-
const stat = await fs36.stat(lockPath);
|
|
17160
|
-
return Date.now() - stat.mtimeMs > LOCK_UNSTAMPED_GRACE_MS;
|
|
17161
|
-
} catch {
|
|
17162
|
-
return false;
|
|
17163
|
-
}
|
|
17164
|
-
}
|
|
17165
|
-
async function releaseLock(lockPath) {
|
|
17166
|
-
await fs36.unlink(lockPath).catch(() => {
|
|
17167
|
-
});
|
|
17168
|
-
}
|
|
17169
|
-
async function withLock(fn) {
|
|
17170
|
-
const lock = registryLockPath();
|
|
17171
|
-
await acquireLock(lock);
|
|
17172
|
-
try {
|
|
17173
|
-
return await fn();
|
|
17174
|
-
} finally {
|
|
17175
|
-
await releaseLock(lock);
|
|
17176
|
-
}
|
|
17177
|
-
}
|
|
17178
|
-
async function readRegistry() {
|
|
17179
|
-
const file = registryPath();
|
|
17180
|
-
let raw;
|
|
17181
|
-
try {
|
|
17182
|
-
raw = await fs36.readFile(file, "utf8");
|
|
17183
|
-
} catch (err) {
|
|
17184
|
-
if (err.code === "ENOENT") {
|
|
17185
|
-
return { version: 1, projects: [] };
|
|
17186
|
-
}
|
|
17187
|
-
throw err;
|
|
17188
|
-
}
|
|
17189
|
-
const parsed = JSON.parse(raw);
|
|
17190
|
-
return RegistryFileSchema.parse(parsed);
|
|
17191
|
-
}
|
|
17192
|
-
async function writeRegistry(reg) {
|
|
17193
|
-
const validated = RegistryFileSchema.parse(reg);
|
|
17194
|
-
await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
|
|
17195
|
-
}
|
|
17196
|
-
var RegistryError = class extends Error {
|
|
17197
|
-
constructor(message) {
|
|
17198
|
-
super(message);
|
|
17199
|
-
this.name = "RegistryError";
|
|
17200
|
-
}
|
|
17201
|
-
};
|
|
17202
|
-
var RegistryLockError = class extends RegistryError {
|
|
17203
|
-
constructor(message) {
|
|
17204
|
-
super(message);
|
|
17205
|
-
this.name = "RegistryLockError";
|
|
17206
|
-
}
|
|
17207
|
-
};
|
|
17208
|
-
var ProjectNameCollisionError = class extends RegistryError {
|
|
17209
|
-
projectName;
|
|
17210
|
-
constructor(name) {
|
|
17211
|
-
super(`a project named "${name}" is already registered`);
|
|
17212
|
-
this.name = "ProjectNameCollisionError";
|
|
17213
|
-
this.projectName = name;
|
|
17214
|
-
}
|
|
17215
|
-
};
|
|
17216
|
-
async function addProject(opts) {
|
|
17217
|
-
const resolvedPath = await normalizeProjectPath(opts.path);
|
|
17218
|
-
return withLock(async () => {
|
|
17219
|
-
const reg = await readRegistry();
|
|
17220
|
-
const byName = reg.projects.find((p) => p.name === opts.name);
|
|
17221
|
-
const byPath = reg.projects.find((p) => p.path === resolvedPath);
|
|
17222
|
-
if (byName && byName.path !== resolvedPath) {
|
|
17223
|
-
throw new ProjectNameCollisionError(opts.name);
|
|
17224
|
-
}
|
|
17225
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
17226
|
-
if (byName && byName.path === resolvedPath) {
|
|
17227
|
-
byName.lastSeenAt = now;
|
|
17228
|
-
if (opts.languages) byName.languages = opts.languages;
|
|
17229
|
-
if (opts.status) byName.status = opts.status;
|
|
17230
|
-
await writeRegistry(reg);
|
|
17231
|
-
return byName;
|
|
17232
|
-
}
|
|
17233
|
-
if (byPath && byPath.name !== opts.name) {
|
|
17234
|
-
throw new ProjectNameCollisionError(byPath.name);
|
|
17235
|
-
}
|
|
17236
|
-
const entry = {
|
|
17237
|
-
name: opts.name,
|
|
17238
|
-
path: resolvedPath,
|
|
17239
|
-
registeredAt: now,
|
|
17240
|
-
languages: opts.languages ?? [],
|
|
17241
|
-
status: opts.status ?? "active"
|
|
17242
|
-
};
|
|
17243
|
-
reg.projects.push(entry);
|
|
17244
|
-
await writeRegistry(reg);
|
|
17245
|
-
return entry;
|
|
17246
|
-
});
|
|
17247
|
-
}
|
|
17248
|
-
async function getProject(name) {
|
|
17249
|
-
const reg = await readRegistry();
|
|
17250
|
-
return reg.projects.find((p) => p.name === name);
|
|
17251
|
-
}
|
|
17252
|
-
async function findProjectByPath(dir) {
|
|
17253
|
-
const here = await normalizeProjectPath(dir);
|
|
17254
|
-
const { projects } = await readRegistry();
|
|
17255
|
-
let best;
|
|
17256
|
-
for (const entry of projects) {
|
|
17257
|
-
const inside = here === entry.path || here.startsWith(entry.path + path71.sep);
|
|
17258
|
-
if (inside && (!best || entry.path.length > best.path.length)) best = entry;
|
|
17259
|
-
}
|
|
17260
|
-
return best;
|
|
17261
|
-
}
|
|
17262
|
-
async function listProjects() {
|
|
17263
|
-
const reg = await readRegistry();
|
|
17264
|
-
return reg.projects;
|
|
17265
|
-
}
|
|
17266
|
-
async function setStatus(name, status) {
|
|
17267
|
-
return withLock(async () => {
|
|
17268
|
-
const reg = await readRegistry();
|
|
17269
|
-
const entry = reg.projects.find((p) => p.name === name);
|
|
17270
|
-
if (!entry) throw new RegistryError(`no project named "${name}"`);
|
|
17271
|
-
entry.status = status;
|
|
17272
|
-
await writeRegistry(reg);
|
|
17273
|
-
return entry;
|
|
17274
|
-
});
|
|
17275
|
-
}
|
|
17276
|
-
async function touchLastSeen(name, at = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
17277
|
-
await withLock(async () => {
|
|
17278
|
-
const reg = await readRegistry();
|
|
17279
|
-
const entry = reg.projects.find((p) => p.name === name);
|
|
17280
|
-
if (!entry) return;
|
|
17281
|
-
entry.lastSeenAt = at;
|
|
17282
|
-
await writeRegistry(reg);
|
|
17283
|
-
});
|
|
17284
|
-
}
|
|
17285
|
-
async function removeProject(name) {
|
|
17286
|
-
return withLock(async () => {
|
|
17287
|
-
const reg = await readRegistry();
|
|
17288
|
-
const idx = reg.projects.findIndex((p) => p.name === name);
|
|
17289
|
-
if (idx < 0) return void 0;
|
|
17290
|
-
const [removed] = reg.projects.splice(idx, 1);
|
|
17291
|
-
await writeRegistry(reg);
|
|
17292
|
-
return removed;
|
|
17293
|
-
});
|
|
17294
|
-
}
|
|
17295
|
-
var DAY_MS2 = 24 * 60 * 60 * 1e3;
|
|
17296
|
-
var DEFAULT_PRUNE_TTL_MS = 7 * DAY_MS2;
|
|
17297
|
-
function pruneTtlMs() {
|
|
17298
|
-
const raw = process.env.NEAT_REGISTRY_PRUNE_TTL_MS;
|
|
17299
|
-
if (!raw) return DEFAULT_PRUNE_TTL_MS;
|
|
17300
|
-
const n = Number.parseInt(raw, 10);
|
|
17301
|
-
if (Number.isFinite(n) && n >= 0) return n;
|
|
17302
|
-
console.warn(
|
|
17303
|
-
`[neat] NEAT_REGISTRY_PRUNE_TTL_MS could not be parsed (${raw}); using default ${DEFAULT_PRUNE_TTL_MS}ms`
|
|
17304
|
-
);
|
|
17305
|
-
return DEFAULT_PRUNE_TTL_MS;
|
|
17306
|
-
}
|
|
17307
|
-
async function statPathStatus(p) {
|
|
17308
|
-
try {
|
|
17309
|
-
const stat = await fs36.stat(p);
|
|
17310
|
-
return stat.isDirectory() ? "present" : "unknown";
|
|
17311
|
-
} catch (err) {
|
|
17312
|
-
return err.code === "ENOENT" ? "gone" : "unknown";
|
|
17313
|
-
}
|
|
17314
|
-
}
|
|
17315
|
-
async function pruneRegistry(opts = {}) {
|
|
17316
|
-
const ttlMs = opts.ttlMs ?? pruneTtlMs();
|
|
17317
|
-
const statPath = opts.statPath ?? statPathStatus;
|
|
17318
|
-
const now = opts.now ?? Date.now;
|
|
17319
|
-
return withLock(async () => {
|
|
17320
|
-
const reg = await readRegistry();
|
|
17321
|
-
const removed = [];
|
|
17322
|
-
const kept = [];
|
|
17323
|
-
for (const entry of reg.projects) {
|
|
17324
|
-
const status = await statPath(entry.path);
|
|
17325
|
-
if (status !== "gone") {
|
|
17326
|
-
kept.push(entry);
|
|
17327
|
-
continue;
|
|
17328
|
-
}
|
|
17329
|
-
if (ttlMs <= 0) {
|
|
17330
|
-
removed.push(entry);
|
|
17331
|
-
continue;
|
|
17332
|
-
}
|
|
17333
|
-
const lastSeen = Date.parse(entry.lastSeenAt ?? entry.registeredAt);
|
|
17334
|
-
const age = now() - (Number.isFinite(lastSeen) ? lastSeen : 0);
|
|
17335
|
-
if (age > ttlMs) {
|
|
17336
|
-
removed.push(entry);
|
|
17337
|
-
} else {
|
|
17338
|
-
kept.push(entry);
|
|
17339
|
-
}
|
|
17340
|
-
}
|
|
17341
|
-
if (removed.length === 0) return [];
|
|
17342
|
-
reg.projects = kept;
|
|
17343
|
-
await writeRegistry(reg);
|
|
17344
|
-
return removed;
|
|
17345
|
-
});
|
|
17346
|
-
}
|
|
17347
|
-
|
|
17348
16961
|
// src/api.ts
|
|
17349
16962
|
import Fastify from "fastify";
|
|
17350
16963
|
import { timingSafeEqual } from "crypto";
|
|
@@ -17352,14 +16965,14 @@ import cors from "@fastify/cors";
|
|
|
17352
16965
|
import { DivergenceTypeSchema, PoliciesCheckBodySchema, PolicySeveritySchema } from "@neat.is/types";
|
|
17353
16966
|
|
|
17354
16967
|
// src/extend/index.ts
|
|
17355
|
-
import { promises as
|
|
17356
|
-
import
|
|
17357
|
-
import
|
|
16968
|
+
import { promises as fs37 } from "fs";
|
|
16969
|
+
import path72 from "path";
|
|
16970
|
+
import os2 from "os";
|
|
17358
16971
|
import { resolve as registryResolve, list as registryList } from "@neat.is/instrumentation-registry";
|
|
17359
16972
|
|
|
17360
16973
|
// src/installers/package-manager.ts
|
|
17361
|
-
import { promises as
|
|
17362
|
-
import
|
|
16974
|
+
import { promises as fs36 } from "fs";
|
|
16975
|
+
import path71 from "path";
|
|
17363
16976
|
import { spawn } from "child_process";
|
|
17364
16977
|
var LOCKFILE_PRIORITY = [
|
|
17365
16978
|
{ lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
|
|
@@ -17374,29 +16987,29 @@ var LOCKFILE_PRIORITY = [
|
|
|
17374
16987
|
var NPM_FALLBACK_ARGS = ["install", "--no-audit", "--no-fund", "--prefer-offline"];
|
|
17375
16988
|
async function exists2(p) {
|
|
17376
16989
|
try {
|
|
17377
|
-
await
|
|
16990
|
+
await fs36.access(p);
|
|
17378
16991
|
return true;
|
|
17379
16992
|
} catch {
|
|
17380
16993
|
return false;
|
|
17381
16994
|
}
|
|
17382
16995
|
}
|
|
17383
16996
|
async function detectPackageManager(serviceDir) {
|
|
17384
|
-
let dir =
|
|
16997
|
+
let dir = path71.resolve(serviceDir);
|
|
17385
16998
|
const stops = /* @__PURE__ */ new Set();
|
|
17386
16999
|
for (let i = 0; i < 64; i++) {
|
|
17387
17000
|
if (stops.has(dir)) break;
|
|
17388
17001
|
stops.add(dir);
|
|
17389
17002
|
for (const candidate of LOCKFILE_PRIORITY) {
|
|
17390
|
-
const lockPath =
|
|
17003
|
+
const lockPath = path71.join(dir, candidate.lockfile);
|
|
17391
17004
|
if (await exists2(lockPath)) {
|
|
17392
17005
|
return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
|
|
17393
17006
|
}
|
|
17394
17007
|
}
|
|
17395
|
-
const parent =
|
|
17008
|
+
const parent = path71.dirname(dir);
|
|
17396
17009
|
if (parent === dir) break;
|
|
17397
17010
|
dir = parent;
|
|
17398
17011
|
}
|
|
17399
|
-
return { pm: "npm", cwd:
|
|
17012
|
+
return { pm: "npm", cwd: path71.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
|
|
17400
17013
|
}
|
|
17401
17014
|
async function runPackageManagerInstall(cmd) {
|
|
17402
17015
|
return new Promise((resolve) => {
|
|
@@ -17438,15 +17051,15 @@ ${err.message}`
|
|
|
17438
17051
|
// src/extend/index.ts
|
|
17439
17052
|
async function fileExists2(p) {
|
|
17440
17053
|
try {
|
|
17441
|
-
await
|
|
17054
|
+
await fs37.access(p);
|
|
17442
17055
|
return true;
|
|
17443
17056
|
} catch {
|
|
17444
17057
|
return false;
|
|
17445
17058
|
}
|
|
17446
17059
|
}
|
|
17447
17060
|
async function readPackageJson(scanPath) {
|
|
17448
|
-
const pkgPath =
|
|
17449
|
-
const raw = await
|
|
17061
|
+
const pkgPath = path72.join(scanPath, "package.json");
|
|
17062
|
+
const raw = await fs37.readFile(pkgPath, "utf8");
|
|
17450
17063
|
return JSON.parse(raw);
|
|
17451
17064
|
}
|
|
17452
17065
|
var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
@@ -17460,15 +17073,15 @@ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
17460
17073
|
async function findHookFiles(scanPath) {
|
|
17461
17074
|
const found = [];
|
|
17462
17075
|
const walk11 = async (dir) => {
|
|
17463
|
-
const entries = await
|
|
17076
|
+
const entries = await fs37.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
17464
17077
|
for (const entry of entries) {
|
|
17465
17078
|
if (entry.isDirectory()) {
|
|
17466
17079
|
if (entry.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry.name)) continue;
|
|
17467
|
-
await walk11(
|
|
17080
|
+
await walk11(path72.join(dir, entry.name));
|
|
17468
17081
|
} else if (entry.isFile()) {
|
|
17469
17082
|
if ((entry.name.startsWith("instrumentation") || entry.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry.name)) {
|
|
17470
|
-
const rel =
|
|
17471
|
-
found.push(rel.split(
|
|
17083
|
+
const rel = path72.relative(scanPath, path72.join(dir, entry.name));
|
|
17084
|
+
found.push(rel.split(path72.sep).join("/"));
|
|
17472
17085
|
}
|
|
17473
17086
|
}
|
|
17474
17087
|
}
|
|
@@ -17479,7 +17092,7 @@ async function findHookFiles(scanPath) {
|
|
|
17479
17092
|
async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
17480
17093
|
let fallback = null;
|
|
17481
17094
|
for (const file of hookFiles) {
|
|
17482
|
-
const content = await
|
|
17095
|
+
const content = await fs37.readFile(path72.join(scanPath, file), "utf8");
|
|
17483
17096
|
const patched = splicedContent(content, snippet2);
|
|
17484
17097
|
if (patched !== null) return { file, content, patched };
|
|
17485
17098
|
if (fallback === null) fallback = { file, content };
|
|
@@ -17487,12 +17100,12 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
|
17487
17100
|
return { file: fallback.file, content: fallback.content, patched: null };
|
|
17488
17101
|
}
|
|
17489
17102
|
function extendLogPath() {
|
|
17490
|
-
return process.env.NEAT_EXTEND_LOG ??
|
|
17103
|
+
return process.env.NEAT_EXTEND_LOG ?? path72.join(os2.homedir(), ".neat", "extend-log.ndjson");
|
|
17491
17104
|
}
|
|
17492
17105
|
async function appendExtendLog(entry) {
|
|
17493
17106
|
const logPath = extendLogPath();
|
|
17494
|
-
await
|
|
17495
|
-
await
|
|
17107
|
+
await fs37.mkdir(path72.dirname(logPath), { recursive: true });
|
|
17108
|
+
await fs37.appendFile(logPath, JSON.stringify(entry) + "\n", "utf8");
|
|
17496
17109
|
}
|
|
17497
17110
|
function splicedContent(fileContent, snippet2) {
|
|
17498
17111
|
if (fileContent.includes("__INSTRUMENTATION_BLOCK__")) {
|
|
@@ -17550,7 +17163,7 @@ function lookupInstrumentation(library, installedVersion) {
|
|
|
17550
17163
|
}
|
|
17551
17164
|
async function describeProjectInstrumentation(ctx) {
|
|
17552
17165
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
17553
|
-
const envNeat = await fileExists2(
|
|
17166
|
+
const envNeat = await fileExists2(path72.join(ctx.scanPath, ".env.neat"));
|
|
17554
17167
|
const registryInstrPackages = new Set(
|
|
17555
17168
|
registryList().map((e) => e.instrumentation_package).filter((p) => !!p)
|
|
17556
17169
|
);
|
|
@@ -17572,7 +17185,7 @@ async function applyExtension(ctx, args, options) {
|
|
|
17572
17185
|
);
|
|
17573
17186
|
}
|
|
17574
17187
|
for (const file of hookFiles) {
|
|
17575
|
-
const content = await
|
|
17188
|
+
const content = await fs37.readFile(path72.join(ctx.scanPath, file), "utf8");
|
|
17576
17189
|
if (content.includes(args.registration_snippet)) {
|
|
17577
17190
|
return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
|
|
17578
17191
|
}
|
|
@@ -17584,18 +17197,18 @@ async function applyExtension(ctx, args, options) {
|
|
|
17584
17197
|
);
|
|
17585
17198
|
}
|
|
17586
17199
|
const primaryFile = primary.file;
|
|
17587
|
-
const primaryPath =
|
|
17200
|
+
const primaryPath = path72.join(ctx.scanPath, primaryFile);
|
|
17588
17201
|
const filesTouched = [];
|
|
17589
17202
|
const depsAdded = [];
|
|
17590
|
-
const pkgPath =
|
|
17203
|
+
const pkgPath = path72.join(ctx.scanPath, "package.json");
|
|
17591
17204
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
17592
17205
|
if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
|
|
17593
17206
|
pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
|
|
17594
|
-
await
|
|
17207
|
+
await fs37.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
17595
17208
|
filesTouched.push("package.json");
|
|
17596
17209
|
depsAdded.push(`${args.instrumentation_package}@${args.version}`);
|
|
17597
17210
|
}
|
|
17598
|
-
await
|
|
17211
|
+
await fs37.writeFile(primaryPath, primary.patched, "utf8");
|
|
17599
17212
|
filesTouched.push(primaryFile);
|
|
17600
17213
|
const cmd = await detectPackageManager(ctx.scanPath);
|
|
17601
17214
|
const installer = options?.runInstall ?? runPackageManagerInstall;
|
|
@@ -17626,7 +17239,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
17626
17239
|
};
|
|
17627
17240
|
}
|
|
17628
17241
|
for (const file of hookFiles) {
|
|
17629
|
-
const content = await
|
|
17242
|
+
const content = await fs37.readFile(path72.join(ctx.scanPath, file), "utf8");
|
|
17630
17243
|
if (content.includes(args.registration_snippet)) {
|
|
17631
17244
|
return {
|
|
17632
17245
|
library: args.library,
|
|
@@ -17661,28 +17274,28 @@ async function rollbackExtension(ctx, args) {
|
|
|
17661
17274
|
if (!await fileExists2(logPath)) {
|
|
17662
17275
|
return { undone: false, message: "no apply found for library" };
|
|
17663
17276
|
}
|
|
17664
|
-
const raw = await
|
|
17277
|
+
const raw = await fs37.readFile(logPath, "utf8");
|
|
17665
17278
|
const entries = raw.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
|
|
17666
17279
|
const match = [...entries].reverse().find((e) => e.project === ctx.project && e.library === args.library);
|
|
17667
17280
|
if (!match) {
|
|
17668
17281
|
return { undone: false, message: "no apply found for library" };
|
|
17669
17282
|
}
|
|
17670
|
-
const pkgPath =
|
|
17283
|
+
const pkgPath = path72.join(ctx.scanPath, "package.json");
|
|
17671
17284
|
if (await fileExists2(pkgPath)) {
|
|
17672
17285
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
17673
17286
|
if (pkg.dependencies?.[match.instrumentation_package]) {
|
|
17674
17287
|
const { [match.instrumentation_package]: _removed, ...rest } = pkg.dependencies;
|
|
17675
17288
|
pkg.dependencies = rest;
|
|
17676
|
-
await
|
|
17289
|
+
await fs37.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
17677
17290
|
}
|
|
17678
17291
|
}
|
|
17679
17292
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
17680
17293
|
for (const file of hookFiles) {
|
|
17681
|
-
const filePath =
|
|
17682
|
-
const content = await
|
|
17294
|
+
const filePath = path72.join(ctx.scanPath, file);
|
|
17295
|
+
const content = await fs37.readFile(filePath, "utf8");
|
|
17683
17296
|
if (content.includes(match.registration_snippet)) {
|
|
17684
17297
|
const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
|
|
17685
|
-
await
|
|
17298
|
+
await fs37.writeFile(filePath, filtered, "utf8");
|
|
17686
17299
|
break;
|
|
17687
17300
|
}
|
|
17688
17301
|
}
|
|
@@ -17761,10 +17374,11 @@ function locusOf(graph, ev) {
|
|
|
17761
17374
|
const rawLine = ev.attributes?.[CODE_LINENO_ATTR2];
|
|
17762
17375
|
const line = typeof rawLine === "number" ? rawLine : Number(rawLine);
|
|
17763
17376
|
const node = graph.hasNode(ev.affectedNode) ? graph.getNodeAttributes(ev.affectedNode) : void 0;
|
|
17377
|
+
const symbol = node?.type === "SymbolNode" ? node.qualname : void 0;
|
|
17764
17378
|
return {
|
|
17765
17379
|
file,
|
|
17766
17380
|
...Number.isFinite(line) ? { lineStart: line, lineEnd: line } : {},
|
|
17767
|
-
...
|
|
17381
|
+
...symbol ? { symbol } : {},
|
|
17768
17382
|
service: node?.service ?? ev.service,
|
|
17769
17383
|
provenance: Provenance26.OBSERVED
|
|
17770
17384
|
};
|
|
@@ -17860,7 +17474,8 @@ function buildIncidentCard(graph, errorEvent, incidents, policies) {
|
|
|
17860
17474
|
const policyCards = applicable.map((p) => ({
|
|
17861
17475
|
policyName: p.policyName,
|
|
17862
17476
|
severity: p.severity,
|
|
17863
|
-
message: p.reason
|
|
17477
|
+
message: p.reason,
|
|
17478
|
+
onViolation: p.onViolation
|
|
17864
17479
|
}));
|
|
17865
17480
|
const divergences = inGraph ? computeDivergences(graph, { node: affected, incidents }).divergences.map((d) => ({
|
|
17866
17481
|
type: d.type,
|
|
@@ -17887,8 +17502,111 @@ function buildIncidentCard(graph, errorEvent, incidents, policies) {
|
|
|
17887
17502
|
...divergences.length > 0 ? { divergence: divergences } : {},
|
|
17888
17503
|
headline: renderHeadline(graph, errorEvent, locus, rootCause?.node ?? null)
|
|
17889
17504
|
};
|
|
17505
|
+
card.grade = gradeIncidentCard(card);
|
|
17890
17506
|
return IncidentCardSchema.parse(card);
|
|
17891
17507
|
}
|
|
17508
|
+
var GRADE_PRIORS_VERSION = "sniper-architecture-priors-2026-10-08";
|
|
17509
|
+
var GRADE_WEIGHTS = {
|
|
17510
|
+
evidence: 3,
|
|
17511
|
+
locus: 2,
|
|
17512
|
+
tests: 2,
|
|
17513
|
+
reach: 1.5,
|
|
17514
|
+
kind: 1.5,
|
|
17515
|
+
chain: 1,
|
|
17516
|
+
recur: 1,
|
|
17517
|
+
div: 1
|
|
17518
|
+
};
|
|
17519
|
+
var PROVENANCE_WEIGHT = {
|
|
17520
|
+
OBSERVED: 1,
|
|
17521
|
+
EXTRACTED: 0.8,
|
|
17522
|
+
INFERRED: 0.4,
|
|
17523
|
+
FRONTIER: 0.3,
|
|
17524
|
+
STALE: 0.2
|
|
17525
|
+
};
|
|
17526
|
+
var REACH_NODES_MAX = 50;
|
|
17527
|
+
var TIMEOUT = /timeout|timed out|deadline/i;
|
|
17528
|
+
var round2 = (n) => Math.round(n * 1e4) / 1e4;
|
|
17529
|
+
function factor(name, value, evidence, reason) {
|
|
17530
|
+
return {
|
|
17531
|
+
value: value === null ? null : round2(value),
|
|
17532
|
+
weight: GRADE_WEIGHTS[name],
|
|
17533
|
+
evidence,
|
|
17534
|
+
...reason ? { reason } : {}
|
|
17535
|
+
};
|
|
17536
|
+
}
|
|
17537
|
+
function gradeIncidentCard(card) {
|
|
17538
|
+
const rc = card.rootCause;
|
|
17539
|
+
const chain = rc?.chain ?? [];
|
|
17540
|
+
const gates = {
|
|
17541
|
+
policyNotBlock: {
|
|
17542
|
+
passed: !(card.policies ?? []).some((p) => p.onViolation === "block"),
|
|
17543
|
+
evidence: ["policies[].onViolation"]
|
|
17544
|
+
},
|
|
17545
|
+
notSymptomOnly: {
|
|
17546
|
+
passed: rc?.classification !== "symptom-only",
|
|
17547
|
+
evidence: ["rootCause.classification"]
|
|
17548
|
+
},
|
|
17549
|
+
locusResolves: { passed: card.locus !== null, evidence: ["locus"] }
|
|
17550
|
+
};
|
|
17551
|
+
const gamma = Object.values(gates).every((g) => g.passed) ? 1 : 0;
|
|
17552
|
+
const carrying = chain.some((h) => h.carriesSignal === true) ? chain.filter((h) => h.carriesSignal === true) : chain;
|
|
17553
|
+
const hopWeights = carrying.map((h) => PROVENANCE_WEIGHT[h.provenance]);
|
|
17554
|
+
const evidence = hopWeights.length === 0 ? factor("evidence", null, ["rootCause.chain[].provenance"], "no root-cause chain on the card") : hopWeights.some((w) => w === void 0) ? factor("evidence", null, ["rootCause.chain[].provenance"], "a chain hop has a provenance with no weight") : factor("evidence", Math.min(...hopWeights), ["rootCause.chain[].provenance"]);
|
|
17555
|
+
const locus = card.locus ? factor("locus", card.locus.symbol ? 1 : 0.6, ["locus.symbol", "locus.file"]) : factor("locus", null, ["locus"], "no locus on the card (the locus gate fails)");
|
|
17556
|
+
const tests = factor(
|
|
17557
|
+
"tests",
|
|
17558
|
+
null,
|
|
17559
|
+
[],
|
|
17560
|
+
"the card carries no test-file classification or test-to-symbol edges"
|
|
17561
|
+
);
|
|
17562
|
+
const reach = card.blastRadius ? factor(
|
|
17563
|
+
"reach",
|
|
17564
|
+
Math.max(0, 1 - card.blastRadius.totalAffected / REACH_NODES_MAX),
|
|
17565
|
+
["blastRadius.totalAffected"],
|
|
17566
|
+
`nodes in the blast radius against a prior bound of ${REACH_NODES_MAX}`
|
|
17567
|
+
) : factor("reach", null, ["blastRadius"], "no blast radius on the card");
|
|
17568
|
+
const timeout = TIMEOUT.test(`${card.exceptionType ?? ""} ${card.message}`);
|
|
17569
|
+
const kindValue = timeout ? 0.3 : card.incidentKind === "exception" ? 1 : card.incidentKind === "5xx" || card.incidentKind === "status-error" ? 0.7 : card.incidentKind === "connector" ? 0.6 : 0.4;
|
|
17570
|
+
const kind = factor("kind", kindValue, ["incidentKind", "exceptionType", "message"]);
|
|
17571
|
+
const chainFactor = rc ? factor(
|
|
17572
|
+
"chain",
|
|
17573
|
+
1 / (1 + 0.25 * (Math.max(1, chain.length - 1) - 1)),
|
|
17574
|
+
["rootCause.chain"]
|
|
17575
|
+
) : factor("chain", null, ["rootCause"], "no root cause on the card");
|
|
17576
|
+
const occurrences = card.count ?? 1;
|
|
17577
|
+
const recurValue = Math.min(1, Math.log(1 + occurrences) / Math.log(11));
|
|
17578
|
+
const recur = factor(
|
|
17579
|
+
"recur",
|
|
17580
|
+
recurValue,
|
|
17581
|
+
["count"],
|
|
17582
|
+
card.count === void 0 ? "no coalesced count; one recorded occurrence" : void 0
|
|
17583
|
+
);
|
|
17584
|
+
const div = factor("div", (card.divergence ?? []).length > 0 ? 1 : 0.7, ["divergence"]);
|
|
17585
|
+
const factors = { evidence, locus, tests, reach, kind, chain: chainFactor, recur, div };
|
|
17586
|
+
let weighted = 0;
|
|
17587
|
+
let total = 0;
|
|
17588
|
+
for (const f of Object.values(factors)) {
|
|
17589
|
+
if (f.value === null) continue;
|
|
17590
|
+
weighted += f.weight * f.value;
|
|
17591
|
+
total += f.weight;
|
|
17592
|
+
}
|
|
17593
|
+
const C = round2(weighted / total);
|
|
17594
|
+
const G = round2(gamma * C);
|
|
17595
|
+
const band = gamma === 0 || G < 0.5 ? "out" : G >= 0.75 ? "full" : "diagnose-only";
|
|
17596
|
+
return {
|
|
17597
|
+
G,
|
|
17598
|
+
gamma,
|
|
17599
|
+
gates,
|
|
17600
|
+
C,
|
|
17601
|
+
factors,
|
|
17602
|
+
band,
|
|
17603
|
+
urgency: {
|
|
17604
|
+
value: null,
|
|
17605
|
+
reason: "f_fresh needs the time of the last OBSERVED signal at the node, which the card does not carry"
|
|
17606
|
+
},
|
|
17607
|
+
priorsVersion: GRADE_PRIORS_VERSION
|
|
17608
|
+
};
|
|
17609
|
+
}
|
|
17892
17610
|
|
|
17893
17611
|
// src/ask.ts
|
|
17894
17612
|
import { AskResultSchema, NodeType as NodeType32, Provenance as Provenance27, serviceId as serviceId15 } from "@neat.is/types";
|
|
@@ -18777,9 +18495,9 @@ data: ${JSON.stringify(envelope.payload)}
|
|
|
18777
18495
|
|
|
18778
18496
|
// src/pr-verdict.ts
|
|
18779
18497
|
import { createHash } from "crypto";
|
|
18780
|
-
import { promises as
|
|
18781
|
-
import
|
|
18782
|
-
import
|
|
18498
|
+
import { promises as fs38 } from "fs";
|
|
18499
|
+
import os4 from "os";
|
|
18500
|
+
import path74 from "path";
|
|
18783
18501
|
|
|
18784
18502
|
// ../action/src/graph.mjs
|
|
18785
18503
|
import { readFileSync as readFileSync3 } from "fs";
|
|
@@ -19054,8 +18772,8 @@ function renderVerdict({
|
|
|
19054
18772
|
import { PrVerdictBodySchema } from "@neat.is/types";
|
|
19055
18773
|
|
|
19056
18774
|
// src/connectors/hosted-repos.ts
|
|
19057
|
-
import
|
|
19058
|
-
import
|
|
18775
|
+
import os3 from "os";
|
|
18776
|
+
import path73 from "path";
|
|
19059
18777
|
import { mkdtemp, rm } from "fs/promises";
|
|
19060
18778
|
var CP_REQUEST_TIMEOUT_MS = 1e4;
|
|
19061
18779
|
var DEFAULT_SYNC_INTERVAL_MS = 5 * 6e4;
|
|
@@ -19084,7 +18802,7 @@ async function cpPostStatus(deps, owner, name, body) {
|
|
|
19084
18802
|
if (!res.ok && res.status !== 404) throw new Error(`control plane status POST \u2192 HTTP ${res.status}`);
|
|
19085
18803
|
}
|
|
19086
18804
|
var defaultCloneRepo = async (cloneUrl, ref, destDir) => {
|
|
19087
|
-
const [{ default: git }, httpMod,
|
|
18805
|
+
const [{ default: git }, httpMod, fs40] = await Promise.all([
|
|
19088
18806
|
import("isomorphic-git"),
|
|
19089
18807
|
import("isomorphic-git/http/node"),
|
|
19090
18808
|
import("fs")
|
|
@@ -19095,7 +18813,7 @@ var defaultCloneRepo = async (cloneUrl, ref, destDir) => {
|
|
|
19095
18813
|
const username = parsed.password ? parsed.username : "x-access-token";
|
|
19096
18814
|
const cleanUrl = `${parsed.protocol}//${parsed.host}${parsed.pathname}`;
|
|
19097
18815
|
const clone = git.clone({
|
|
19098
|
-
fs:
|
|
18816
|
+
fs: fs40,
|
|
19099
18817
|
http,
|
|
19100
18818
|
dir: destDir,
|
|
19101
18819
|
url: cleanUrl,
|
|
@@ -19116,7 +18834,7 @@ var defaultCloneRepo = async (cloneUrl, ref, destDir) => {
|
|
|
19116
18834
|
}
|
|
19117
18835
|
};
|
|
19118
18836
|
var defaultCloneCommit = async (cloneUrl, sha, destDir) => {
|
|
19119
|
-
const [{ default: git }, httpMod,
|
|
18837
|
+
const [{ default: git }, httpMod, fs40] = await Promise.all([
|
|
19120
18838
|
import("isomorphic-git"),
|
|
19121
18839
|
import("isomorphic-git/http/node"),
|
|
19122
18840
|
import("fs")
|
|
@@ -19127,10 +18845,10 @@ var defaultCloneCommit = async (cloneUrl, sha, destDir) => {
|
|
|
19127
18845
|
const username = parsed.password ? parsed.username : "x-access-token";
|
|
19128
18846
|
const cleanUrl = `${parsed.protocol}//${parsed.host}${parsed.pathname}`;
|
|
19129
18847
|
const clone = (async () => {
|
|
19130
|
-
await git.init({ fs:
|
|
19131
|
-
await git.addRemote({ fs:
|
|
18848
|
+
await git.init({ fs: fs40, dir: destDir });
|
|
18849
|
+
await git.addRemote({ fs: fs40, dir: destDir, remote: "origin", url: cleanUrl });
|
|
19132
18850
|
await git.fetch({
|
|
19133
|
-
fs:
|
|
18851
|
+
fs: fs40,
|
|
19134
18852
|
http,
|
|
19135
18853
|
dir: destDir,
|
|
19136
18854
|
remote: "origin",
|
|
@@ -19142,7 +18860,7 @@ var defaultCloneCommit = async (cloneUrl, sha, destDir) => {
|
|
|
19142
18860
|
tags: false,
|
|
19143
18861
|
...password ? { onAuth: () => ({ username, password }) } : {}
|
|
19144
18862
|
});
|
|
19145
|
-
await git.checkout({ fs:
|
|
18863
|
+
await git.checkout({ fs: fs40, dir: destDir, ref: sha, force: true });
|
|
19146
18864
|
})();
|
|
19147
18865
|
clone.catch(() => {
|
|
19148
18866
|
});
|
|
@@ -19167,10 +18885,10 @@ async function syncOneRepo(r, input) {
|
|
|
19167
18885
|
const cloneRepo = input.cloneRepo ?? defaultCloneRepo;
|
|
19168
18886
|
const extract = input.extract ?? extractFromDirectory;
|
|
19169
18887
|
const label = `${r.owner}/${r.name}`;
|
|
19170
|
-
const tmpRoot = input.tmpRoot ??
|
|
18888
|
+
const tmpRoot = input.tmpRoot ?? os3.tmpdir();
|
|
19171
18889
|
let dir;
|
|
19172
18890
|
try {
|
|
19173
|
-
dir = await mkdtemp(
|
|
18891
|
+
dir = await mkdtemp(path73.join(tmpRoot, "neat-repo-"));
|
|
19174
18892
|
await cpPostStatus(deps, r.owner, r.name, { syncStatus: "syncing", detail: "cloning" }).catch(() => {
|
|
19175
18893
|
});
|
|
19176
18894
|
await cloneRepo(r.cloneUrl, r.defaultBranch, dir);
|
|
@@ -19339,9 +19057,9 @@ async function computePrVerdict(req, ctx, deps, deadline) {
|
|
|
19339
19057
|
const startedAt = now();
|
|
19340
19058
|
const cloneCommit = deps.cloneCommit ?? defaultCloneCommit;
|
|
19341
19059
|
const extract = deps.extract ?? extractFromDirectory;
|
|
19342
|
-
const root = await
|
|
19343
|
-
const baseDir =
|
|
19344
|
-
const headDir =
|
|
19060
|
+
const root = await fs38.mkdtemp(path74.join(deps.tmpRoot ?? os4.tmpdir(), "neat-pr-"));
|
|
19061
|
+
const baseDir = path74.join(root, "base");
|
|
19062
|
+
const headDir = path74.join(root, "head");
|
|
19345
19063
|
const stage = async (name, run) => {
|
|
19346
19064
|
const left = deadline - now();
|
|
19347
19065
|
if (left <= 0) throw new PrVerdictError(`verdict ran out of time before ${name}`, 504, name);
|
|
@@ -19364,8 +19082,8 @@ async function computePrVerdict(req, ctx, deps, deadline) {
|
|
|
19364
19082
|
};
|
|
19365
19083
|
const extractOpts = { source: `${req.owner}/${req.name}`, announce: false };
|
|
19366
19084
|
try {
|
|
19367
|
-
await
|
|
19368
|
-
await
|
|
19085
|
+
await fs38.mkdir(baseDir);
|
|
19086
|
+
await fs38.mkdir(headDir);
|
|
19369
19087
|
await stage("clone-base", () => cloneCommit(req.cloneUrl, req.baseSha, baseDir));
|
|
19370
19088
|
await stage("clone-head", () => cloneCommit(req.cloneUrl, req.headSha, headDir));
|
|
19371
19089
|
const baseGraph = makeGraph();
|
|
@@ -19387,7 +19105,7 @@ async function computePrVerdict(req, ctx, deps, deadline) {
|
|
|
19387
19105
|
const found = observedBreakFrom(node, getObservedDependencies(ctx.liveGraph, node.id));
|
|
19388
19106
|
if (found) observedBreaks.push(found);
|
|
19389
19107
|
}
|
|
19390
|
-
const { marker, body } = renderVerdict({
|
|
19108
|
+
const { marker, body: rendered } = renderVerdict({
|
|
19391
19109
|
graph: head,
|
|
19392
19110
|
delta,
|
|
19393
19111
|
changedFiles,
|
|
@@ -19395,6 +19113,7 @@ async function computePrVerdict(req, ctx, deps, deadline) {
|
|
|
19395
19113
|
observedBreaks,
|
|
19396
19114
|
tone: req.tone ?? "loud"
|
|
19397
19115
|
});
|
|
19116
|
+
const body = rendered.startsWith(marker) ? rendered.slice(marker.length).replace(/^(?:\r?\n)+/, "") : rendered;
|
|
19398
19117
|
return {
|
|
19399
19118
|
marker,
|
|
19400
19119
|
body,
|
|
@@ -19406,7 +19125,7 @@ async function computePrVerdict(req, ctx, deps, deadline) {
|
|
|
19406
19125
|
durationMs: now() - startedAt
|
|
19407
19126
|
};
|
|
19408
19127
|
} finally {
|
|
19409
|
-
await
|
|
19128
|
+
await fs38.rm(root, { recursive: true, force: true }).catch(() => {
|
|
19410
19129
|
});
|
|
19411
19130
|
}
|
|
19412
19131
|
}
|
|
@@ -19414,13 +19133,13 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", "neat-out"]);
|
|
|
19414
19133
|
async function hashTree(dir) {
|
|
19415
19134
|
const out = /* @__PURE__ */ new Map();
|
|
19416
19135
|
const walk11 = async (rel) => {
|
|
19417
|
-
const entries = await
|
|
19136
|
+
const entries = await fs38.readdir(path74.join(dir, rel), { withFileTypes: true }).catch(() => []);
|
|
19418
19137
|
for (const entry of entries) {
|
|
19419
19138
|
const child = rel ? `${rel}/${entry.name}` : entry.name;
|
|
19420
19139
|
if (entry.isDirectory()) {
|
|
19421
19140
|
if (!SKIP_DIRS.has(entry.name)) await walk11(child);
|
|
19422
19141
|
} else if (entry.isFile()) {
|
|
19423
|
-
const bytes = await
|
|
19142
|
+
const bytes = await fs38.readFile(path74.join(dir, child)).catch(() => null);
|
|
19424
19143
|
if (bytes) out.set(child, createHash("sha1").update(bytes).digest("hex"));
|
|
19425
19144
|
}
|
|
19426
19145
|
}
|
|
@@ -19437,9 +19156,9 @@ async function changedPathsBetween(baseDir, headDir) {
|
|
|
19437
19156
|
}
|
|
19438
19157
|
|
|
19439
19158
|
// src/connectors-config.ts
|
|
19440
|
-
import
|
|
19441
|
-
import
|
|
19442
|
-
import { promises as
|
|
19159
|
+
import os5 from "os";
|
|
19160
|
+
import path75 from "path";
|
|
19161
|
+
import { promises as fs39 } from "fs";
|
|
19443
19162
|
var CONNECTORS_CONFIG_VERSION = 1;
|
|
19444
19163
|
var EnvRefUnsetError = class extends Error {
|
|
19445
19164
|
ref;
|
|
@@ -19451,19 +19170,19 @@ var EnvRefUnsetError = class extends Error {
|
|
|
19451
19170
|
this.varName = varName;
|
|
19452
19171
|
}
|
|
19453
19172
|
};
|
|
19454
|
-
function
|
|
19173
|
+
function neatHome() {
|
|
19455
19174
|
const override = process.env.NEAT_HOME;
|
|
19456
|
-
if (override && override.length > 0) return
|
|
19457
|
-
return
|
|
19175
|
+
if (override && override.length > 0) return path75.resolve(override);
|
|
19176
|
+
return path75.join(os5.homedir(), ".neat");
|
|
19458
19177
|
}
|
|
19459
|
-
function connectorsConfigPath(home =
|
|
19460
|
-
return
|
|
19178
|
+
function connectorsConfigPath(home = neatHome()) {
|
|
19179
|
+
return path75.join(home, "connectors.json");
|
|
19461
19180
|
}
|
|
19462
19181
|
var MODE_MASK_LOOSER_THAN_0600 = 63;
|
|
19463
19182
|
async function warnIfModeLooserThan0600(file) {
|
|
19464
19183
|
if (process.platform === "win32") return;
|
|
19465
19184
|
try {
|
|
19466
|
-
const stat = await
|
|
19185
|
+
const stat = await fs39.stat(file);
|
|
19467
19186
|
if ((stat.mode & MODE_MASK_LOOSER_THAN_0600) !== 0) {
|
|
19468
19187
|
const mode = (stat.mode & 511).toString(8).padStart(3, "0");
|
|
19469
19188
|
console.warn(
|
|
@@ -19473,11 +19192,11 @@ async function warnIfModeLooserThan0600(file) {
|
|
|
19473
19192
|
} catch {
|
|
19474
19193
|
}
|
|
19475
19194
|
}
|
|
19476
|
-
async function readConnectorsConfig(home =
|
|
19195
|
+
async function readConnectorsConfig(home = neatHome()) {
|
|
19477
19196
|
const file = connectorsConfigPath(home);
|
|
19478
19197
|
let raw;
|
|
19479
19198
|
try {
|
|
19480
|
-
raw = await
|
|
19199
|
+
raw = await fs39.readFile(file, "utf8");
|
|
19481
19200
|
} catch (err) {
|
|
19482
19201
|
if (err.code === "ENOENT") {
|
|
19483
19202
|
return { version: CONNECTORS_CONFIG_VERSION, connectors: [] };
|
|
@@ -19587,8 +19306,8 @@ function connectorMatchesProject(entry, project) {
|
|
|
19587
19306
|
}
|
|
19588
19307
|
var CONNECTORS_LOCK_TIMEOUT_MS = 5e3;
|
|
19589
19308
|
var CONNECTORS_LOCK_RETRY_MS = 50;
|
|
19590
|
-
function connectorsConfigLockPath(home =
|
|
19591
|
-
return
|
|
19309
|
+
function connectorsConfigLockPath(home = neatHome()) {
|
|
19310
|
+
return path75.join(home, "connectors.json.lock");
|
|
19592
19311
|
}
|
|
19593
19312
|
function isEnvRef(value) {
|
|
19594
19313
|
return value.length > 1 && value.startsWith("$");
|
|
@@ -19601,9 +19320,9 @@ function redactCredentialRef(ref) {
|
|
|
19601
19320
|
return out;
|
|
19602
19321
|
}
|
|
19603
19322
|
async function writeConfigAtomically0600(file, contents) {
|
|
19604
|
-
await
|
|
19323
|
+
await fs39.mkdir(path75.dirname(file), { recursive: true });
|
|
19605
19324
|
const tmp = `${file}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
19606
|
-
const fd = await
|
|
19325
|
+
const fd = await fs39.open(tmp, "w", 384);
|
|
19607
19326
|
try {
|
|
19608
19327
|
await fd.writeFile(contents, "utf8");
|
|
19609
19328
|
await fd.chmod(384);
|
|
@@ -19611,14 +19330,14 @@ async function writeConfigAtomically0600(file, contents) {
|
|
|
19611
19330
|
} finally {
|
|
19612
19331
|
await fd.close();
|
|
19613
19332
|
}
|
|
19614
|
-
await
|
|
19333
|
+
await fs39.rename(tmp, file);
|
|
19615
19334
|
}
|
|
19616
19335
|
async function acquireConnectorsLock(lockPath, timeoutMs = CONNECTORS_LOCK_TIMEOUT_MS) {
|
|
19617
19336
|
const deadline = Date.now() + timeoutMs;
|
|
19618
|
-
await
|
|
19337
|
+
await fs39.mkdir(path75.dirname(lockPath), { recursive: true });
|
|
19619
19338
|
for (; ; ) {
|
|
19620
19339
|
try {
|
|
19621
|
-
const fd = await
|
|
19340
|
+
const fd = await fs39.open(lockPath, "wx");
|
|
19622
19341
|
try {
|
|
19623
19342
|
await fd.writeFile(`${process.pid}
|
|
19624
19343
|
`, "utf8");
|
|
@@ -19638,7 +19357,7 @@ async function acquireConnectorsLock(lockPath, timeoutMs = CONNECTORS_LOCK_TIMEO
|
|
|
19638
19357
|
}
|
|
19639
19358
|
}
|
|
19640
19359
|
async function releaseConnectorsLock(lockPath) {
|
|
19641
|
-
await
|
|
19360
|
+
await fs39.unlink(lockPath).catch(() => {
|
|
19642
19361
|
});
|
|
19643
19362
|
}
|
|
19644
19363
|
async function withConnectorsLock(home, fn) {
|
|
@@ -19657,7 +19376,7 @@ function serializeConfig(config) {
|
|
|
19657
19376
|
2
|
|
19658
19377
|
) + "\n";
|
|
19659
19378
|
}
|
|
19660
|
-
async function upsertConnectorEntry(entry, home =
|
|
19379
|
+
async function upsertConnectorEntry(entry, home = neatHome()) {
|
|
19661
19380
|
const file = connectorsConfigPath(home);
|
|
19662
19381
|
return withConnectorsLock(home, async () => {
|
|
19663
19382
|
const config = await readConnectorsConfig(home);
|
|
@@ -19670,7 +19389,7 @@ async function upsertConnectorEntry(entry, home = neatHome2()) {
|
|
|
19670
19389
|
return { replaced };
|
|
19671
19390
|
});
|
|
19672
19391
|
}
|
|
19673
|
-
async function removeConnectorEntry(id, home =
|
|
19392
|
+
async function removeConnectorEntry(id, home = neatHome()) {
|
|
19674
19393
|
const file = connectorsConfigPath(home);
|
|
19675
19394
|
return withConnectorsLock(home, async () => {
|
|
19676
19395
|
const config = await readConnectorsConfig(home);
|
|
@@ -20378,10 +20097,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
|
|
|
20378
20097
|
// src/connectors/supabase/map.ts
|
|
20379
20098
|
var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
|
|
20380
20099
|
var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
|
|
20381
|
-
function targetFromRestPath(
|
|
20382
|
-
const rpcMatch = REST_RPC_PATH_RE.exec(
|
|
20100
|
+
function targetFromRestPath(path76) {
|
|
20101
|
+
const rpcMatch = REST_RPC_PATH_RE.exec(path76);
|
|
20383
20102
|
if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
|
|
20384
|
-
const tableMatch = REST_TABLE_PATH_RE.exec(
|
|
20103
|
+
const tableMatch = REST_TABLE_PATH_RE.exec(path76);
|
|
20385
20104
|
if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
|
|
20386
20105
|
return null;
|
|
20387
20106
|
}
|
|
@@ -20976,9 +20695,9 @@ function parseFirebaseTargetName(targetName) {
|
|
|
20976
20695
|
const secondSep = rest.indexOf(FIELD_SEP);
|
|
20977
20696
|
if (secondSep === -1) return null;
|
|
20978
20697
|
const method = rest.slice(0, secondSep);
|
|
20979
|
-
const
|
|
20980
|
-
if (!resourceName || !method || !
|
|
20981
|
-
return { resourceName, method, path:
|
|
20698
|
+
const path76 = rest.slice(secondSep + 1);
|
|
20699
|
+
if (!resourceName || !method || !path76) return null;
|
|
20700
|
+
return { resourceName, method, path: path76 };
|
|
20982
20701
|
}
|
|
20983
20702
|
function resourceNameFor(type, labels) {
|
|
20984
20703
|
if (!labels) return null;
|
|
@@ -21016,14 +20735,14 @@ function mapLogEntryToSignal(entry) {
|
|
|
21016
20735
|
if (!req) return null;
|
|
21017
20736
|
if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
|
|
21018
20737
|
const method = req.requestMethod.toUpperCase();
|
|
21019
|
-
const
|
|
21020
|
-
if (
|
|
20738
|
+
const path76 = pathFromRequestUrl(req.requestUrl);
|
|
20739
|
+
if (path76 === null) return null;
|
|
21021
20740
|
const timestamp = entry.timestamp;
|
|
21022
20741
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
21023
20742
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
|
|
21024
20743
|
return {
|
|
21025
20744
|
targetKind: resourceType,
|
|
21026
|
-
targetName: packFirebaseTargetName({ resourceName, method, path:
|
|
20745
|
+
targetName: packFirebaseTargetName({ resourceName, method, path: path76 }),
|
|
21027
20746
|
callCount: 1,
|
|
21028
20747
|
errorCount: isError ? 1 : 0,
|
|
21029
20748
|
lastObservedIso: timestamp
|
|
@@ -21046,9 +20765,9 @@ import { NodeType as NodeType35 } from "@neat.is/types";
|
|
|
21046
20765
|
function normalizeName(name) {
|
|
21047
20766
|
return name.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
21048
20767
|
}
|
|
21049
|
-
function inferServiceName(graph, resourceName, method,
|
|
20768
|
+
function inferServiceName(graph, resourceName, method, path76) {
|
|
21050
20769
|
const wantedMethod = method.toUpperCase();
|
|
21051
|
-
const wantedPath = normalizePathTemplate(
|
|
20770
|
+
const wantedPath = normalizePathTemplate(path76);
|
|
21052
20771
|
const services = [];
|
|
21053
20772
|
const owners = /* @__PURE__ */ new Set();
|
|
21054
20773
|
graph.forEachNode((_id, attrs) => {
|
|
@@ -21256,7 +20975,7 @@ function mapEventToSignal(event) {
|
|
|
21256
20975
|
if (Number.isNaN(observedAt.getTime())) return null;
|
|
21257
20976
|
const statusCode = metadata?.statusCode;
|
|
21258
20977
|
const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
|
|
21259
|
-
const
|
|
20978
|
+
const path76 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
|
|
21260
20979
|
return {
|
|
21261
20980
|
targetKind: CLOUDFLARE_TARGET_KIND,
|
|
21262
20981
|
targetName: scriptName,
|
|
@@ -21264,7 +20983,7 @@ function mapEventToSignal(event) {
|
|
|
21264
20983
|
errorCount: isError ? 1 : 0,
|
|
21265
20984
|
lastObservedIso: observedAt.toISOString(),
|
|
21266
20985
|
method,
|
|
21267
|
-
...
|
|
20986
|
+
...path76 ? { path: path76 } : {},
|
|
21268
20987
|
...typeof statusCode === "number" ? { statusCode } : {},
|
|
21269
20988
|
...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
|
|
21270
20989
|
};
|
|
@@ -21310,8 +21029,8 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
21310
21029
|
});
|
|
21311
21030
|
return found;
|
|
21312
21031
|
}
|
|
21313
|
-
function findMatchingRouteNode(graph, serviceName, method,
|
|
21314
|
-
const normalizedPath = normalizePathTemplate(
|
|
21032
|
+
function findMatchingRouteNode(graph, serviceName, method, path76) {
|
|
21033
|
+
const normalizedPath = normalizePathTemplate(path76);
|
|
21315
21034
|
let found = null;
|
|
21316
21035
|
graph.forEachNode((id, attrs) => {
|
|
21317
21036
|
if (found) return;
|
|
@@ -21328,10 +21047,10 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
21328
21047
|
return (signal) => {
|
|
21329
21048
|
if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
|
|
21330
21049
|
const scriptName = signal.targetName;
|
|
21331
|
-
const { method, path:
|
|
21050
|
+
const { method, path: path76 } = signal;
|
|
21332
21051
|
const resolveRouteGrain = (serviceName, wholeFileId) => {
|
|
21333
|
-
if (!method || !
|
|
21334
|
-
return findMatchingRouteNode(graph, serviceName, method,
|
|
21052
|
+
if (!method || !path76) return wholeFileId;
|
|
21053
|
+
return findMatchingRouteNode(graph, serviceName, method, path76) ?? wholeFileId;
|
|
21335
21054
|
};
|
|
21336
21055
|
const mapping = config.workers?.[scriptName];
|
|
21337
21056
|
if (mapping) {
|
|
@@ -21661,9 +21380,9 @@ function parseCloudRunTargetName(targetName) {
|
|
|
21661
21380
|
const secondSep = rest.indexOf(FIELD_SEP2);
|
|
21662
21381
|
if (secondSep === -1) return null;
|
|
21663
21382
|
const method = rest.slice(0, secondSep);
|
|
21664
|
-
const
|
|
21665
|
-
if (!serviceName || !method || !
|
|
21666
|
-
return { serviceName, method, path:
|
|
21383
|
+
const path76 = rest.slice(secondSep + 1);
|
|
21384
|
+
if (!serviceName || !method || !path76) return null;
|
|
21385
|
+
return { serviceName, method, path: path76 };
|
|
21667
21386
|
}
|
|
21668
21387
|
|
|
21669
21388
|
// src/connectors/cloud-run/map.ts
|
|
@@ -21692,14 +21411,14 @@ function mapLogEntryToSignal2(entry) {
|
|
|
21692
21411
|
if (!req) return null;
|
|
21693
21412
|
if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
|
|
21694
21413
|
const method = req.requestMethod.toUpperCase();
|
|
21695
|
-
const
|
|
21696
|
-
if (
|
|
21414
|
+
const path76 = pathFromRequestUrl2(req.requestUrl);
|
|
21415
|
+
if (path76 === null) return null;
|
|
21697
21416
|
const timestamp = entry.timestamp;
|
|
21698
21417
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
21699
21418
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD4;
|
|
21700
21419
|
return {
|
|
21701
21420
|
targetKind: CLOUD_RUN_TARGET_KIND,
|
|
21702
|
-
targetName: packCloudRunTargetName({ serviceName, method, path:
|
|
21421
|
+
targetName: packCloudRunTargetName({ serviceName, method, path: path76 }),
|
|
21703
21422
|
callCount: 1,
|
|
21704
21423
|
errorCount: isError ? 1 : 0,
|
|
21705
21424
|
lastObservedIso: timestamp
|
|
@@ -21737,14 +21456,14 @@ function createCloudRunResolveTarget(graph, config) {
|
|
|
21737
21456
|
if (signal.targetKind !== CLOUD_RUN_TARGET_KIND) return null;
|
|
21738
21457
|
const identity = parseCloudRunTargetName(signal.targetName);
|
|
21739
21458
|
if (!identity) return null;
|
|
21740
|
-
const { serviceName: gcpServiceName, method, path:
|
|
21741
|
-
const mappedService = config.serviceMap?.[gcpServiceName] ?? (config.inferServices ? inferServiceName(graph, gcpServiceName, method,
|
|
21459
|
+
const { serviceName: gcpServiceName, method, path: path76 } = identity;
|
|
21460
|
+
const mappedService = config.serviceMap?.[gcpServiceName] ?? (config.inferServices ? inferServiceName(graph, gcpServiceName, method, path76) ?? void 0 : void 0);
|
|
21742
21461
|
if (mappedService) {
|
|
21743
21462
|
const routeNodeId = findMatchingRouteNode2(
|
|
21744
21463
|
graph,
|
|
21745
21464
|
mappedService,
|
|
21746
21465
|
method,
|
|
21747
|
-
normalizePathTemplate(
|
|
21466
|
+
normalizePathTemplate(path76)
|
|
21748
21467
|
);
|
|
21749
21468
|
if (routeNodeId) {
|
|
21750
21469
|
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: EdgeType32.CALLS };
|
|
@@ -21872,9 +21591,9 @@ function parseGcpLbTargetName(targetName) {
|
|
|
21872
21591
|
const secondSep = rest.indexOf(FIELD_SEP3);
|
|
21873
21592
|
if (secondSep === -1) return null;
|
|
21874
21593
|
const method = rest.slice(0, secondSep);
|
|
21875
|
-
const
|
|
21876
|
-
if (!backendServiceName || !method || !
|
|
21877
|
-
return { backendServiceName, method, path:
|
|
21594
|
+
const path76 = rest.slice(secondSep + 1);
|
|
21595
|
+
if (!backendServiceName || !method || !path76) return null;
|
|
21596
|
+
return { backendServiceName, method, path: path76 };
|
|
21878
21597
|
}
|
|
21879
21598
|
|
|
21880
21599
|
// src/connectors/gcp-lb/map.ts
|
|
@@ -21903,14 +21622,14 @@ function mapLogEntryToSignal3(entry) {
|
|
|
21903
21622
|
if (!req) return null;
|
|
21904
21623
|
if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
|
|
21905
21624
|
const method = req.requestMethod.toUpperCase();
|
|
21906
|
-
const
|
|
21907
|
-
if (
|
|
21625
|
+
const path76 = pathFromRequestUrl3(req.requestUrl);
|
|
21626
|
+
if (path76 === null) return null;
|
|
21908
21627
|
const timestamp = entry.timestamp;
|
|
21909
21628
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
21910
21629
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD5;
|
|
21911
21630
|
return {
|
|
21912
21631
|
targetKind: GCP_LB_TARGET_KIND,
|
|
21913
|
-
targetName: packGcpLbTargetName({ backendServiceName, method, path:
|
|
21632
|
+
targetName: packGcpLbTargetName({ backendServiceName, method, path: path76 }),
|
|
21914
21633
|
callCount: 1,
|
|
21915
21634
|
errorCount: isError ? 1 : 0,
|
|
21916
21635
|
lastObservedIso: timestamp
|
|
@@ -21948,14 +21667,14 @@ function createGcpLbResolveTarget(graph, config) {
|
|
|
21948
21667
|
if (signal.targetKind !== GCP_LB_TARGET_KIND) return null;
|
|
21949
21668
|
const identity = parseGcpLbTargetName(signal.targetName);
|
|
21950
21669
|
if (!identity) return null;
|
|
21951
|
-
const { backendServiceName, method, path:
|
|
21952
|
-
const mappedService = config.backendServiceMap?.[backendServiceName] ?? (config.inferServices ? inferServiceName(graph, backendServiceName, method,
|
|
21670
|
+
const { backendServiceName, method, path: path76 } = identity;
|
|
21671
|
+
const mappedService = config.backendServiceMap?.[backendServiceName] ?? (config.inferServices ? inferServiceName(graph, backendServiceName, method, path76) ?? void 0 : void 0);
|
|
21953
21672
|
if (mappedService) {
|
|
21954
21673
|
const routeNodeId = findMatchingRouteNode3(
|
|
21955
21674
|
graph,
|
|
21956
21675
|
mappedService,
|
|
21957
21676
|
method,
|
|
21958
|
-
normalizePathTemplate(
|
|
21677
|
+
normalizePathTemplate(path76)
|
|
21959
21678
|
);
|
|
21960
21679
|
if (routeNodeId) {
|
|
21961
21680
|
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: EdgeType33.CALLS };
|
|
@@ -24416,25 +24135,6 @@ export {
|
|
|
24416
24135
|
pathsForProject,
|
|
24417
24136
|
Projects,
|
|
24418
24137
|
parseExtraProjects,
|
|
24419
|
-
registryPath,
|
|
24420
|
-
registryLockPath,
|
|
24421
|
-
removeDaemonRecord,
|
|
24422
|
-
listMachineProjects,
|
|
24423
|
-
findDaemonByProject,
|
|
24424
|
-
signalDaemonStop,
|
|
24425
|
-
normalizeProjectPath,
|
|
24426
|
-
writeAtomically,
|
|
24427
|
-
readRegistry,
|
|
24428
|
-
RegistryError,
|
|
24429
|
-
ProjectNameCollisionError,
|
|
24430
|
-
addProject,
|
|
24431
|
-
getProject,
|
|
24432
|
-
findProjectByPath,
|
|
24433
|
-
listProjects,
|
|
24434
|
-
setStatus,
|
|
24435
|
-
touchLastSeen,
|
|
24436
|
-
removeProject,
|
|
24437
|
-
pruneRegistry,
|
|
24438
24138
|
maybeStartRepoSync,
|
|
24439
24139
|
readConnectorsConfig,
|
|
24440
24140
|
resolveCredential,
|
|
@@ -24457,4 +24157,4 @@ export {
|
|
|
24457
24157
|
deprovisionConnector,
|
|
24458
24158
|
buildApi
|
|
24459
24159
|
};
|
|
24460
|
-
//# sourceMappingURL=chunk-
|
|
24160
|
+
//# sourceMappingURL=chunk-H2BAPN33.js.map
|