@neat.is/core 0.3.3 → 0.3.4
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-VABXPLDT.js → chunk-33ZZ2ZID.js} +181 -66
- package/dist/chunk-33ZZ2ZID.js.map +1 -0
- package/dist/{chunk-ICFH326H.js → chunk-T3A2GK3X.js} +2 -2
- package/dist/cli.cjs +215 -124
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +3 -1
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +160 -74
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +2 -2
- package/dist/neatd.cjs +167 -81
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +2 -2
- package/dist/server.cjs +156 -70
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-VABXPLDT.js.map +0 -1
- /package/dist/{chunk-ICFH326H.js.map → chunk-T3A2GK3X.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -100,8 +100,8 @@ function reshapeGrpcRequest(req) {
|
|
|
100
100
|
};
|
|
101
101
|
}
|
|
102
102
|
function resolveProtoRoot() {
|
|
103
|
-
const here =
|
|
104
|
-
return
|
|
103
|
+
const here = import_node_path34.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
104
|
+
return import_node_path34.default.resolve(here, "..", "proto");
|
|
105
105
|
}
|
|
106
106
|
function loadTraceService() {
|
|
107
107
|
const protoRoot = resolveProtoRoot();
|
|
@@ -156,13 +156,13 @@ async function startOtelGrpcReceiver(opts) {
|
|
|
156
156
|
})
|
|
157
157
|
};
|
|
158
158
|
}
|
|
159
|
-
var import_node_url,
|
|
159
|
+
var import_node_url, import_node_path34, grpc, protoLoader;
|
|
160
160
|
var init_otel_grpc = __esm({
|
|
161
161
|
"src/otel-grpc.ts"() {
|
|
162
162
|
"use strict";
|
|
163
163
|
init_cjs_shims();
|
|
164
164
|
import_node_url = require("url");
|
|
165
|
-
|
|
165
|
+
import_node_path34 = __toESM(require("path"), 1);
|
|
166
166
|
grpc = __toESM(require("@grpc/grpc-js"), 1);
|
|
167
167
|
protoLoader = __toESM(require("@grpc/proto-loader"), 1);
|
|
168
168
|
init_otel();
|
|
@@ -259,10 +259,10 @@ function parseOtlpRequest(body) {
|
|
|
259
259
|
}
|
|
260
260
|
function loadProtobufDecoder() {
|
|
261
261
|
if (exportTraceServiceRequestType) return exportTraceServiceRequestType;
|
|
262
|
-
const here =
|
|
263
|
-
const protoRoot =
|
|
262
|
+
const here = import_node_path35.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
|
|
263
|
+
const protoRoot = import_node_path35.default.resolve(here, "..", "proto");
|
|
264
264
|
const root = new import_protobufjs.default.Root();
|
|
265
|
-
root.resolvePath = (_origin, target) =>
|
|
265
|
+
root.resolvePath = (_origin, target) => import_node_path35.default.resolve(protoRoot, target);
|
|
266
266
|
root.loadSync(
|
|
267
267
|
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
|
|
268
268
|
{ keepCase: true }
|
|
@@ -362,12 +362,12 @@ function logSpanHandler(span) {
|
|
|
362
362
|
`otel: ${span.service} ${span.name} parent=${parent} status=${status2}${db}`
|
|
363
363
|
);
|
|
364
364
|
}
|
|
365
|
-
var
|
|
365
|
+
var import_node_path35, import_node_url2, import_fastify2, import_protobufjs, exportTraceServiceRequestType;
|
|
366
366
|
var init_otel = __esm({
|
|
367
367
|
"src/otel.ts"() {
|
|
368
368
|
"use strict";
|
|
369
369
|
init_cjs_shims();
|
|
370
|
-
|
|
370
|
+
import_node_path35 = __toESM(require("path"), 1);
|
|
371
371
|
import_node_url2 = require("url");
|
|
372
372
|
import_fastify2 = __toESM(require("fastify"), 1);
|
|
373
373
|
import_protobufjs = __toESM(require("protobufjs"), 1);
|
|
@@ -876,19 +876,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
876
876
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
877
877
|
let best = { path: [start], edges: [] };
|
|
878
878
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
879
|
-
function step(node,
|
|
880
|
-
if (
|
|
881
|
-
best = { path: [...
|
|
879
|
+
function step(node, path37, edges) {
|
|
880
|
+
if (path37.length > best.path.length) {
|
|
881
|
+
best = { path: [...path37], edges: [...edges] };
|
|
882
882
|
}
|
|
883
|
-
if (
|
|
883
|
+
if (path37.length - 1 >= maxDepth) return;
|
|
884
884
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
885
885
|
for (const [srcId, edge] of incoming) {
|
|
886
886
|
if (visited.has(srcId)) continue;
|
|
887
887
|
visited.add(srcId);
|
|
888
|
-
|
|
888
|
+
path37.push(srcId);
|
|
889
889
|
edges.push(edge);
|
|
890
|
-
step(srcId,
|
|
891
|
-
|
|
890
|
+
step(srcId, path37, edges);
|
|
891
|
+
path37.pop();
|
|
892
892
|
edges.pop();
|
|
893
893
|
visited.delete(srcId);
|
|
894
894
|
}
|
|
@@ -1577,35 +1577,37 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false) {
|
|
|
1577
1577
|
const existing = graph.getEdgeAttributes(id);
|
|
1578
1578
|
const newSpanCount = (existing.signal?.spanCount ?? existing.callCount ?? 0) + 1;
|
|
1579
1579
|
const newErrorCount = (existing.signal?.errorCount ?? 0) + (isError ? 1 : 0);
|
|
1580
|
+
const newSignal = {
|
|
1581
|
+
spanCount: newSpanCount,
|
|
1582
|
+
errorCount: newErrorCount,
|
|
1583
|
+
lastObservedAgeMs: 0
|
|
1584
|
+
};
|
|
1580
1585
|
const updated = {
|
|
1581
1586
|
...existing,
|
|
1582
1587
|
provenance: import_types3.Provenance.OBSERVED,
|
|
1583
1588
|
lastObserved: ts,
|
|
1584
1589
|
callCount: newSpanCount,
|
|
1585
|
-
signal:
|
|
1586
|
-
|
|
1587
|
-
errorCount: newErrorCount,
|
|
1588
|
-
lastObservedAgeMs: 0
|
|
1589
|
-
},
|
|
1590
|
-
confidence: 1
|
|
1590
|
+
signal: newSignal,
|
|
1591
|
+
confidence: (0, import_types3.confidenceForObservedSignal)(newSignal)
|
|
1591
1592
|
};
|
|
1592
1593
|
graph.replaceEdgeAttributes(id, updated);
|
|
1593
1594
|
return { edge: updated, created: false };
|
|
1594
1595
|
}
|
|
1596
|
+
const signal = {
|
|
1597
|
+
spanCount: 1,
|
|
1598
|
+
errorCount: isError ? 1 : 0,
|
|
1599
|
+
lastObservedAgeMs: 0
|
|
1600
|
+
};
|
|
1595
1601
|
const edge = {
|
|
1596
1602
|
id,
|
|
1597
1603
|
source,
|
|
1598
1604
|
target,
|
|
1599
1605
|
type,
|
|
1600
1606
|
provenance: import_types3.Provenance.OBSERVED,
|
|
1601
|
-
confidence:
|
|
1607
|
+
confidence: (0, import_types3.confidenceForObservedSignal)(signal),
|
|
1602
1608
|
lastObserved: ts,
|
|
1603
1609
|
callCount: 1,
|
|
1604
|
-
signal
|
|
1605
|
-
spanCount: 1,
|
|
1606
|
-
errorCount: isError ? 1 : 0,
|
|
1607
|
-
lastObservedAgeMs: 0
|
|
1608
|
-
}
|
|
1610
|
+
signal
|
|
1609
1611
|
};
|
|
1610
1612
|
graph.addEdgeWithKey(id, source, target, edge);
|
|
1611
1613
|
return { edge, created: true };
|
|
@@ -2232,6 +2234,23 @@ async function writeExtractionErrors(errors, errorsPath) {
|
|
|
2232
2234
|
const lines = errors.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
2233
2235
|
await import_node_fs7.promises.appendFile(errorsPath, lines, "utf8");
|
|
2234
2236
|
}
|
|
2237
|
+
var droppedSink = [];
|
|
2238
|
+
function noteExtractedDropped(edge) {
|
|
2239
|
+
droppedSink.push(edge);
|
|
2240
|
+
}
|
|
2241
|
+
function drainDroppedExtracted() {
|
|
2242
|
+
return droppedSink.splice(0, droppedSink.length);
|
|
2243
|
+
}
|
|
2244
|
+
function isRejectedLogEnabled() {
|
|
2245
|
+
const raw = process.env.NEAT_EXTRACTED_REJECTED_LOG;
|
|
2246
|
+
return raw === "1" || raw === "true";
|
|
2247
|
+
}
|
|
2248
|
+
async function writeRejectedExtracted(drops, rejectedPath) {
|
|
2249
|
+
if (drops.length === 0) return;
|
|
2250
|
+
await import_node_fs7.promises.mkdir(import_node_path7.default.dirname(rejectedPath), { recursive: true });
|
|
2251
|
+
const lines = drops.map((d) => JSON.stringify({ ...d, ts: (/* @__PURE__ */ new Date()).toISOString() })).join("\n") + "\n";
|
|
2252
|
+
await import_node_fs7.promises.appendFile(rejectedPath, lines, "utf8");
|
|
2253
|
+
}
|
|
2235
2254
|
|
|
2236
2255
|
// src/extract/services.ts
|
|
2237
2256
|
var DEFAULT_SCAN_DEPTH = 5;
|
|
@@ -3170,6 +3189,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
3170
3189
|
target: dbNode.id,
|
|
3171
3190
|
type: import_types7.EdgeType.CONNECTS_TO,
|
|
3172
3191
|
provenance: import_types7.Provenance.EXTRACTED,
|
|
3192
|
+
confidence: (0, import_types7.confidenceForExtracted)("structural"),
|
|
3173
3193
|
// ADR-032 / #140 — every EXTRACTED edge carries evidence.file.
|
|
3174
3194
|
// Ghost-edge cleanup keys retirement on this; the conditional
|
|
3175
3195
|
// sourceFile spread that used to live here was a v0.1.x leftover.
|
|
@@ -3244,6 +3264,7 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
3244
3264
|
target: node.id,
|
|
3245
3265
|
type: import_types8.EdgeType.CONFIGURED_BY,
|
|
3246
3266
|
provenance: import_types8.Provenance.EXTRACTED,
|
|
3267
|
+
confidence: (0, import_types8.confidenceForExtracted)("structural"),
|
|
3247
3268
|
evidence: { file: relPath.split(import_node_path18.default.sep).join("/") }
|
|
3248
3269
|
};
|
|
3249
3270
|
if (!graph.hasEdge(edge.id)) {
|
|
@@ -3398,17 +3419,32 @@ async function addHttpCallEdges(graph, services) {
|
|
|
3398
3419
|
for (const [targetId, evidenceFile] of seenTargets) {
|
|
3399
3420
|
const fileContent = files.find((f) => f.path === evidenceFile.file)?.content ?? "";
|
|
3400
3421
|
const line = lineOf(fileContent, `//${evidenceFile.host}`);
|
|
3422
|
+
const confidence = (0, import_types9.confidenceForExtracted)("hostname-shape-match");
|
|
3423
|
+
const ev = {
|
|
3424
|
+
file: import_node_path20.default.relative(service.dir, evidenceFile.file),
|
|
3425
|
+
line,
|
|
3426
|
+
snippet: snippet(fileContent, line)
|
|
3427
|
+
};
|
|
3428
|
+
const edgeId = (0, import_types4.extractedEdgeId)(service.node.id, targetId, import_types9.EdgeType.CALLS);
|
|
3429
|
+
if (!(0, import_types9.passesExtractedFloor)(confidence)) {
|
|
3430
|
+
noteExtractedDropped({
|
|
3431
|
+
source: service.node.id,
|
|
3432
|
+
target: targetId,
|
|
3433
|
+
type: import_types9.EdgeType.CALLS,
|
|
3434
|
+
confidence,
|
|
3435
|
+
confidenceKind: "hostname-shape-match",
|
|
3436
|
+
evidence: ev
|
|
3437
|
+
});
|
|
3438
|
+
continue;
|
|
3439
|
+
}
|
|
3401
3440
|
const edge = {
|
|
3402
|
-
id:
|
|
3441
|
+
id: edgeId,
|
|
3403
3442
|
source: service.node.id,
|
|
3404
3443
|
target: targetId,
|
|
3405
3444
|
type: import_types9.EdgeType.CALLS,
|
|
3406
3445
|
provenance: import_types9.Provenance.EXTRACTED,
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
line,
|
|
3410
|
-
snippet: snippet(fileContent, line)
|
|
3411
|
-
}
|
|
3446
|
+
confidence,
|
|
3447
|
+
evidence: ev
|
|
3412
3448
|
};
|
|
3413
3449
|
if (!graph.hasEdge(edge.id)) {
|
|
3414
3450
|
graph.addEdgeWithKey(edge.id, edge.source, edge.target, edge);
|
|
@@ -3447,6 +3483,10 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
3447
3483
|
name: topic,
|
|
3448
3484
|
kind: "kafka-topic",
|
|
3449
3485
|
edgeType,
|
|
3486
|
+
// `producer.send({topic: 'x'})` / `consumer.subscribe({topic: 'x'})` —
|
|
3487
|
+
// framework-aware (kafkajs / node-rdkafka shape). Verified-call-site
|
|
3488
|
+
// tier (ADR-066).
|
|
3489
|
+
confidenceKind: "verified-call-site",
|
|
3450
3490
|
evidence: {
|
|
3451
3491
|
file: import_node_path21.default.relative(serviceDir, file.path),
|
|
3452
3492
|
line,
|
|
@@ -3479,6 +3519,10 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
3479
3519
|
name: host,
|
|
3480
3520
|
kind: "redis",
|
|
3481
3521
|
edgeType: "CALLS",
|
|
3522
|
+
// `redis://host` URL literal — the scheme is structural support, but no
|
|
3523
|
+
// call expression is verified to wire it through. URL-with-structural-
|
|
3524
|
+
// support tier (ADR-066).
|
|
3525
|
+
confidenceKind: "url-with-structural-support",
|
|
3482
3526
|
evidence: {
|
|
3483
3527
|
file: import_node_path22.default.relative(serviceDir, file.path),
|
|
3484
3528
|
line,
|
|
@@ -3520,6 +3564,10 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
3520
3564
|
name,
|
|
3521
3565
|
kind,
|
|
3522
3566
|
edgeType: "CALLS",
|
|
3567
|
+
// SDK marker (S3Client, GetCommand, etc.) plus a Bucket/TableName
|
|
3568
|
+
// literal — framework-aware recognizer, verified-call-site tier
|
|
3569
|
+
// (ADR-066).
|
|
3570
|
+
confidenceKind: "verified-call-site",
|
|
3523
3571
|
evidence: {
|
|
3524
3572
|
file: import_node_path23.default.relative(serviceDir, file.path),
|
|
3525
3573
|
line,
|
|
@@ -3599,6 +3647,10 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
3599
3647
|
name,
|
|
3600
3648
|
kind,
|
|
3601
3649
|
edgeType: "CALLS",
|
|
3650
|
+
// `new <Name>Client(...)` with @aws-sdk/* or @grpc/grpc-js import
|
|
3651
|
+
// context — import-aware classification per #238. Verified-call-site
|
|
3652
|
+
// tier (ADR-066).
|
|
3653
|
+
confidenceKind: "verified-call-site",
|
|
3602
3654
|
evidence: {
|
|
3603
3655
|
file: import_node_path24.default.relative(serviceDir, file.path),
|
|
3604
3656
|
line,
|
|
@@ -3659,6 +3711,18 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
3659
3711
|
const edgeId = (0, import_types4.extractedEdgeId)(service.node.id, ep.infraId, edgeType);
|
|
3660
3712
|
if (seenEdges.has(edgeId)) continue;
|
|
3661
3713
|
seenEdges.add(edgeId);
|
|
3714
|
+
const confidence = (0, import_types14.confidenceForExtracted)(ep.confidenceKind);
|
|
3715
|
+
if (!(0, import_types14.passesExtractedFloor)(confidence)) {
|
|
3716
|
+
noteExtractedDropped({
|
|
3717
|
+
source: service.node.id,
|
|
3718
|
+
target: ep.infraId,
|
|
3719
|
+
type: edgeType,
|
|
3720
|
+
confidence,
|
|
3721
|
+
confidenceKind: ep.confidenceKind,
|
|
3722
|
+
evidence: ep.evidence
|
|
3723
|
+
});
|
|
3724
|
+
continue;
|
|
3725
|
+
}
|
|
3662
3726
|
if (!graph.hasEdge(edgeId)) {
|
|
3663
3727
|
const edge = {
|
|
3664
3728
|
id: edgeId,
|
|
@@ -3666,6 +3730,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
3666
3730
|
target: ep.infraId,
|
|
3667
3731
|
type: edgeType,
|
|
3668
3732
|
provenance: import_types14.Provenance.EXTRACTED,
|
|
3733
|
+
confidence,
|
|
3669
3734
|
evidence: ep.evidence
|
|
3670
3735
|
};
|
|
3671
3736
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -3785,6 +3850,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
3785
3850
|
target: targetId,
|
|
3786
3851
|
type: import_types16.EdgeType.DEPENDS_ON,
|
|
3787
3852
|
provenance: import_types16.Provenance.EXTRACTED,
|
|
3853
|
+
confidence: (0, import_types16.confidenceForExtracted)("structural"),
|
|
3788
3854
|
evidence: { file: evidenceFile }
|
|
3789
3855
|
};
|
|
3790
3856
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -3845,6 +3911,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
3845
3911
|
target: node.id,
|
|
3846
3912
|
type: import_types17.EdgeType.RUNS_ON,
|
|
3847
3913
|
provenance: import_types17.Provenance.EXTRACTED,
|
|
3914
|
+
confidence: (0, import_types17.confidenceForExtracted)("structural"),
|
|
3848
3915
|
evidence: {
|
|
3849
3916
|
file: import_node_path26.default.relative(scanPath, dockerfilePath).split(import_node_path26.default.sep).join("/")
|
|
3850
3917
|
}
|
|
@@ -3961,6 +4028,9 @@ async function addInfra(graph, scanPath, services) {
|
|
|
3961
4028
|
};
|
|
3962
4029
|
}
|
|
3963
4030
|
|
|
4031
|
+
// src/extract/index.ts
|
|
4032
|
+
var import_node_path30 = __toESM(require("path"), 1);
|
|
4033
|
+
|
|
3964
4034
|
// src/extract/retire.ts
|
|
3965
4035
|
init_cjs_shims();
|
|
3966
4036
|
var import_node_fs17 = require("fs");
|
|
@@ -4013,13 +4083,26 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
4013
4083
|
);
|
|
4014
4084
|
}
|
|
4015
4085
|
}
|
|
4086
|
+
const droppedEntries = drainDroppedExtracted();
|
|
4087
|
+
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
4088
|
+
const rejectedPath = import_node_path30.default.join(import_node_path30.default.dirname(opts.errorsPath), "rejected.ndjson");
|
|
4089
|
+
try {
|
|
4090
|
+
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
4091
|
+
} catch (err) {
|
|
4092
|
+
console.warn(
|
|
4093
|
+
`[neat] failed to write rejected extracted edges to ${rejectedPath}: ${err.message}`
|
|
4094
|
+
);
|
|
4095
|
+
}
|
|
4096
|
+
}
|
|
4016
4097
|
const result = {
|
|
4017
4098
|
nodesAdded: phase1Nodes + phase2.nodesAdded + phase3.nodesAdded + phase4.nodesAdded + phase5.nodesAdded,
|
|
4018
4099
|
edgesAdded: phase2.edgesAdded + phase3.edgesAdded + phase4.edgesAdded + phase5.edgesAdded,
|
|
4019
4100
|
frontiersPromoted,
|
|
4020
4101
|
extractionErrors: errorEntries.length,
|
|
4021
4102
|
errorEntries,
|
|
4022
|
-
ghostsRetired
|
|
4103
|
+
ghostsRetired,
|
|
4104
|
+
extractedDropped: droppedEntries.length,
|
|
4105
|
+
droppedEntries
|
|
4023
4106
|
};
|
|
4024
4107
|
emitNeatEvent({
|
|
4025
4108
|
type: "extraction-complete",
|
|
@@ -4037,7 +4120,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
4037
4120
|
// src/persist.ts
|
|
4038
4121
|
init_cjs_shims();
|
|
4039
4122
|
var import_node_fs18 = require("fs");
|
|
4040
|
-
var
|
|
4123
|
+
var import_node_path31 = __toESM(require("path"), 1);
|
|
4041
4124
|
var SCHEMA_VERSION = 2;
|
|
4042
4125
|
function migrateV1ToV2(payload) {
|
|
4043
4126
|
const nodes = payload.graph.nodes;
|
|
@@ -4051,7 +4134,7 @@ function migrateV1ToV2(payload) {
|
|
|
4051
4134
|
return { ...payload, schemaVersion: 2 };
|
|
4052
4135
|
}
|
|
4053
4136
|
async function ensureDir(filePath) {
|
|
4054
|
-
await import_node_fs18.promises.mkdir(
|
|
4137
|
+
await import_node_fs18.promises.mkdir(import_node_path31.default.dirname(filePath), { recursive: true });
|
|
4055
4138
|
}
|
|
4056
4139
|
async function saveGraphToDisk(graph, outPath) {
|
|
4057
4140
|
await ensureDir(outPath);
|
|
@@ -4163,14 +4246,6 @@ function nodeIsFrontier(graph, nodeId) {
|
|
|
4163
4246
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
4164
4247
|
return attrs.type === import_types19.NodeType.FrontierNode;
|
|
4165
4248
|
}
|
|
4166
|
-
function hasAnyObservedFromSource(graph, sourceId) {
|
|
4167
|
-
if (!graph.hasNode(sourceId)) return false;
|
|
4168
|
-
for (const edgeId of graph.outboundEdges(sourceId)) {
|
|
4169
|
-
const e = graph.getEdgeAttributes(edgeId);
|
|
4170
|
-
if (e.provenance === import_types19.Provenance.OBSERVED) return true;
|
|
4171
|
-
}
|
|
4172
|
-
return false;
|
|
4173
|
-
}
|
|
4174
4249
|
function clampConfidence(n) {
|
|
4175
4250
|
if (!Number.isFinite(n)) return 0;
|
|
4176
4251
|
return Math.max(0, Math.min(1, n));
|
|
@@ -4184,32 +4259,34 @@ function reasonForMissingExtracted(source, target, type) {
|
|
|
4184
4259
|
var RECOMMENDATION_MISSING_OBSERVED = "Verify the code path is exercised in production; check feature flags or conditional branches that might gate the call.";
|
|
4185
4260
|
var RECOMMENDATION_MISSING_EXTRACTED = "Likely dynamic dispatch, reflection, or a coverage gap in tree-sitter extraction. Consider an `aliases` entry on the source service or file an extractor issue.";
|
|
4186
4261
|
var RECOMMENDATION_HOST_MISMATCH = "Check environment-specific config overrides \u2014 the runtime host differs from what static configuration declares.";
|
|
4262
|
+
function gradedConfidence(edge) {
|
|
4263
|
+
if (typeof edge.confidence === "number") return clampConfidence(edge.confidence);
|
|
4264
|
+
return clampConfidence(confidenceForEdge(edge));
|
|
4265
|
+
}
|
|
4187
4266
|
function detectMissingDivergences(graph, bucket) {
|
|
4188
4267
|
const out = [];
|
|
4189
4268
|
if (bucket.extracted && !bucket.observed) {
|
|
4190
4269
|
if (!nodeIsFrontier(graph, bucket.target)) {
|
|
4191
|
-
const sourceHasTraffic = hasAnyObservedFromSource(graph, bucket.source);
|
|
4192
4270
|
out.push({
|
|
4193
4271
|
type: "missing-observed",
|
|
4194
4272
|
source: bucket.source,
|
|
4195
4273
|
target: bucket.target,
|
|
4196
4274
|
edgeType: bucket.type,
|
|
4197
4275
|
extracted: bucket.extracted,
|
|
4198
|
-
confidence:
|
|
4276
|
+
confidence: gradedConfidence(bucket.extracted),
|
|
4199
4277
|
reason: reasonForMissingObserved(bucket.source, bucket.target, bucket.type),
|
|
4200
4278
|
recommendation: RECOMMENDATION_MISSING_OBSERVED
|
|
4201
4279
|
});
|
|
4202
4280
|
}
|
|
4203
4281
|
}
|
|
4204
4282
|
if (bucket.observed && !bucket.extracted) {
|
|
4205
|
-
const cascaded = clampConfidence(confidenceForEdge(bucket.observed));
|
|
4206
4283
|
out.push({
|
|
4207
4284
|
type: "missing-extracted",
|
|
4208
4285
|
source: bucket.source,
|
|
4209
4286
|
target: bucket.target,
|
|
4210
4287
|
edgeType: bucket.type,
|
|
4211
4288
|
observed: bucket.observed,
|
|
4212
|
-
confidence:
|
|
4289
|
+
confidence: gradedConfidence(bucket.observed),
|
|
4213
4290
|
reason: reasonForMissingExtracted(bucket.source, bucket.target, bucket.type),
|
|
4214
4291
|
recommendation: RECOMMENDATION_MISSING_EXTRACTED
|
|
4215
4292
|
});
|
|
@@ -4348,8 +4425,17 @@ function computeDivergences(graph, opts = {}) {
|
|
|
4348
4425
|
const target = opts.node;
|
|
4349
4426
|
filtered = filtered.filter((d) => involvesNode(d, target));
|
|
4350
4427
|
}
|
|
4428
|
+
const TYPE_LEADERSHIP = {
|
|
4429
|
+
"missing-extracted": 0,
|
|
4430
|
+
"missing-observed": 1,
|
|
4431
|
+
"version-mismatch": 2,
|
|
4432
|
+
"host-mismatch": 3,
|
|
4433
|
+
"compat-violation": 4
|
|
4434
|
+
};
|
|
4351
4435
|
filtered.sort((a, b) => {
|
|
4352
4436
|
if (b.confidence !== a.confidence) return b.confidence - a.confidence;
|
|
4437
|
+
const lead = TYPE_LEADERSHIP[a.type] - TYPE_LEADERSHIP[b.type];
|
|
4438
|
+
if (lead !== 0) return lead;
|
|
4353
4439
|
if (a.type !== b.type) return a.type.localeCompare(b.type);
|
|
4354
4440
|
if (a.source !== b.source) return a.source.localeCompare(b.source);
|
|
4355
4441
|
return a.target.localeCompare(b.target);
|
|
@@ -4440,23 +4526,23 @@ function canonicalJson(value) {
|
|
|
4440
4526
|
|
|
4441
4527
|
// src/projects.ts
|
|
4442
4528
|
init_cjs_shims();
|
|
4443
|
-
var
|
|
4529
|
+
var import_node_path32 = __toESM(require("path"), 1);
|
|
4444
4530
|
function pathsForProject(project, baseDir) {
|
|
4445
4531
|
if (project === DEFAULT_PROJECT) {
|
|
4446
4532
|
return {
|
|
4447
|
-
snapshotPath:
|
|
4448
|
-
errorsPath:
|
|
4449
|
-
staleEventsPath:
|
|
4450
|
-
embeddingsCachePath:
|
|
4451
|
-
policyViolationsPath:
|
|
4533
|
+
snapshotPath: import_node_path32.default.join(baseDir, "graph.json"),
|
|
4534
|
+
errorsPath: import_node_path32.default.join(baseDir, "errors.ndjson"),
|
|
4535
|
+
staleEventsPath: import_node_path32.default.join(baseDir, "stale-events.ndjson"),
|
|
4536
|
+
embeddingsCachePath: import_node_path32.default.join(baseDir, "embeddings.json"),
|
|
4537
|
+
policyViolationsPath: import_node_path32.default.join(baseDir, "policy-violations.ndjson")
|
|
4452
4538
|
};
|
|
4453
4539
|
}
|
|
4454
4540
|
return {
|
|
4455
|
-
snapshotPath:
|
|
4456
|
-
errorsPath:
|
|
4457
|
-
staleEventsPath:
|
|
4458
|
-
embeddingsCachePath:
|
|
4459
|
-
policyViolationsPath:
|
|
4541
|
+
snapshotPath: import_node_path32.default.join(baseDir, `${project}.json`),
|
|
4542
|
+
errorsPath: import_node_path32.default.join(baseDir, `errors.${project}.ndjson`),
|
|
4543
|
+
staleEventsPath: import_node_path32.default.join(baseDir, `stale-events.${project}.ndjson`),
|
|
4544
|
+
embeddingsCachePath: import_node_path32.default.join(baseDir, `embeddings.${project}.json`),
|
|
4545
|
+
policyViolationsPath: import_node_path32.default.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
4460
4546
|
};
|
|
4461
4547
|
}
|
|
4462
4548
|
var Projects = class {
|
|
@@ -4494,23 +4580,23 @@ var Projects = class {
|
|
|
4494
4580
|
init_cjs_shims();
|
|
4495
4581
|
var import_node_fs20 = require("fs");
|
|
4496
4582
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
4497
|
-
var
|
|
4583
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
4498
4584
|
var import_types20 = require("@neat.is/types");
|
|
4499
4585
|
var LOCK_TIMEOUT_MS = 5e3;
|
|
4500
4586
|
var LOCK_RETRY_MS = 50;
|
|
4501
4587
|
function neatHome() {
|
|
4502
4588
|
const override = process.env.NEAT_HOME;
|
|
4503
|
-
if (override && override.length > 0) return
|
|
4504
|
-
return
|
|
4589
|
+
if (override && override.length > 0) return import_node_path33.default.resolve(override);
|
|
4590
|
+
return import_node_path33.default.join(import_node_os2.default.homedir(), ".neat");
|
|
4505
4591
|
}
|
|
4506
4592
|
function registryPath() {
|
|
4507
|
-
return
|
|
4593
|
+
return import_node_path33.default.join(neatHome(), "projects.json");
|
|
4508
4594
|
}
|
|
4509
4595
|
function registryLockPath() {
|
|
4510
|
-
return
|
|
4596
|
+
return import_node_path33.default.join(neatHome(), "projects.json.lock");
|
|
4511
4597
|
}
|
|
4512
4598
|
async function normalizeProjectPath(input) {
|
|
4513
|
-
const resolved =
|
|
4599
|
+
const resolved = import_node_path33.default.resolve(input);
|
|
4514
4600
|
try {
|
|
4515
4601
|
return await import_node_fs20.promises.realpath(resolved);
|
|
4516
4602
|
} catch {
|
|
@@ -4518,7 +4604,7 @@ async function normalizeProjectPath(input) {
|
|
|
4518
4604
|
}
|
|
4519
4605
|
}
|
|
4520
4606
|
async function writeAtomically(target, contents) {
|
|
4521
|
-
await import_node_fs20.promises.mkdir(
|
|
4607
|
+
await import_node_fs20.promises.mkdir(import_node_path33.default.dirname(target), { recursive: true });
|
|
4522
4608
|
const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
4523
4609
|
const fd = await import_node_fs20.promises.open(tmp, "w");
|
|
4524
4610
|
try {
|
|
@@ -4531,7 +4617,7 @@ async function writeAtomically(target, contents) {
|
|
|
4531
4617
|
}
|
|
4532
4618
|
async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS) {
|
|
4533
4619
|
const deadline = Date.now() + timeoutMs;
|
|
4534
|
-
await import_node_fs20.promises.mkdir(
|
|
4620
|
+
await import_node_fs20.promises.mkdir(import_node_path33.default.dirname(lockPath), { recursive: true });
|
|
4535
4621
|
while (true) {
|
|
4536
4622
|
try {
|
|
4537
4623
|
const fd = await import_node_fs20.promises.open(lockPath, "wx");
|
|
@@ -5147,14 +5233,14 @@ init_otel_grpc();
|
|
|
5147
5233
|
// src/daemon.ts
|
|
5148
5234
|
init_cjs_shims();
|
|
5149
5235
|
var import_node_fs21 = require("fs");
|
|
5150
|
-
var
|
|
5236
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
5151
5237
|
init_otel();
|
|
5152
5238
|
function neatHomeFor(opts) {
|
|
5153
|
-
if (opts.neatHome && opts.neatHome.length > 0) return
|
|
5239
|
+
if (opts.neatHome && opts.neatHome.length > 0) return import_node_path36.default.resolve(opts.neatHome);
|
|
5154
5240
|
const env = process.env.NEAT_HOME;
|
|
5155
|
-
if (env && env.length > 0) return
|
|
5241
|
+
if (env && env.length > 0) return import_node_path36.default.resolve(env);
|
|
5156
5242
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
5157
|
-
return
|
|
5243
|
+
return import_node_path36.default.join(home, ".neat");
|
|
5158
5244
|
}
|
|
5159
5245
|
function routeSpanToProject(serviceName, projects) {
|
|
5160
5246
|
if (!serviceName) return DEFAULT_PROJECT;
|
|
@@ -5167,7 +5253,7 @@ function routeSpanToProject(serviceName, projects) {
|
|
|
5167
5253
|
return DEFAULT_PROJECT;
|
|
5168
5254
|
}
|
|
5169
5255
|
async function bootstrapProject(entry) {
|
|
5170
|
-
const paths = pathsForProject(entry.name,
|
|
5256
|
+
const paths = pathsForProject(entry.name, import_node_path36.default.join(entry.path, "neat-out"));
|
|
5171
5257
|
try {
|
|
5172
5258
|
const stat = await import_node_fs21.promises.stat(entry.path);
|
|
5173
5259
|
if (!stat.isDirectory()) {
|
|
@@ -5240,7 +5326,7 @@ async function startDaemon(opts = {}) {
|
|
|
5240
5326
|
`neatd: registry not found at ${regPath}. Run \`neat init <path>\` to register a project before starting the daemon.`
|
|
5241
5327
|
);
|
|
5242
5328
|
}
|
|
5243
|
-
const pidPath =
|
|
5329
|
+
const pidPath = import_node_path36.default.join(home, "neatd.pid");
|
|
5244
5330
|
await writeAtomically(pidPath, `${process.pid}
|
|
5245
5331
|
`);
|
|
5246
5332
|
const slots = /* @__PURE__ */ new Map();
|