@neat.is/core 0.2.9 → 0.3.0
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-IRPH6KL4.js → chunk-B7UUGIXB.js} +3 -1
- package/dist/chunk-B7UUGIXB.js.map +1 -0
- package/dist/chunk-DGUM43GV.js +11 -0
- package/dist/{chunk-BVF7MVR5.js → chunk-I5KODOJV.js} +2 -2
- package/dist/{chunk-5KX7EI4F.js → chunk-NVCEZXL7.js} +331 -20
- package/dist/chunk-NVCEZXL7.js.map +1 -0
- package/dist/{chunk-I5IMCXRO.js → chunk-QYUB3FQL.js} +2 -2
- package/dist/cli.cjs +443 -42
- 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 +111 -13
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +323 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -12
- package/dist/index.d.ts +4 -12
- package/dist/index.js +5 -4
- package/dist/neatd.cjs +106 -5
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +112 -6
- package/dist/neatd.js.map +1 -1
- package/dist/{otel-grpc-B4XBSI4W.js → otel-grpc-USFL3OI3.js} +3 -2
- package/dist/otel-grpc-USFL3OI3.js.map +1 -0
- package/dist/server.cjs +431 -125
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +4 -3
- package/dist/server.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-5KX7EI4F.js.map +0 -1
- package/dist/chunk-IRPH6KL4.js.map +0 -1
- /package/dist/{otel-grpc-B4XBSI4W.js.map → chunk-DGUM43GV.js.map} +0 -0
- /package/dist/{chunk-BVF7MVR5.js.map → chunk-I5KODOJV.js.map} +0 -0
- /package/dist/{chunk-I5IMCXRO.js.map → chunk-QYUB3FQL.js.map} +0 -0
package/dist/cli.d.cts
CHANGED
|
@@ -27,6 +27,8 @@ interface ParsedArgs {
|
|
|
27
27
|
against: string | null;
|
|
28
28
|
errorId: string | null;
|
|
29
29
|
hypotheticalAction: string | null;
|
|
30
|
+
type: string | null;
|
|
31
|
+
minConfidence: number | null;
|
|
30
32
|
positional: string[];
|
|
31
33
|
}
|
|
32
34
|
declare function parseArgs(rest: string[]): ParsedArgs;
|
package/dist/cli.d.ts
CHANGED
|
@@ -27,6 +27,8 @@ interface ParsedArgs {
|
|
|
27
27
|
against: string | null;
|
|
28
28
|
errorId: string | null;
|
|
29
29
|
hypotheticalAction: string | null;
|
|
30
|
+
type: string | null;
|
|
31
|
+
minConfidence: number | null;
|
|
30
32
|
positional: string[];
|
|
31
33
|
}
|
|
32
34
|
declare function parseArgs(rest: string[]): ParsedArgs;
|
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
} from "./chunk-XOOCA5T7.js";
|
|
5
5
|
import {
|
|
6
6
|
buildApi
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-NVCEZXL7.js";
|
|
8
8
|
import {
|
|
9
9
|
DEFAULT_PROJECT,
|
|
10
10
|
PolicyViolationsLog,
|
|
@@ -37,11 +37,12 @@ import {
|
|
|
37
37
|
setStatus,
|
|
38
38
|
startPersistLoop,
|
|
39
39
|
startStalenessLoop
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-B7UUGIXB.js";
|
|
41
41
|
import {
|
|
42
42
|
buildOtelReceiver,
|
|
43
43
|
startOtelGrpcReceiver
|
|
44
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-QYUB3FQL.js";
|
|
45
|
+
import "./chunk-DGUM43GV.js";
|
|
45
46
|
|
|
46
47
|
// src/cli.ts
|
|
47
48
|
import path4 from "path";
|
|
@@ -753,6 +754,9 @@ function renderPatch(sections) {
|
|
|
753
754
|
return lines.join("\n");
|
|
754
755
|
}
|
|
755
756
|
|
|
757
|
+
// src/cli.ts
|
|
758
|
+
import { DivergenceTypeSchema } from "@neat.is/types";
|
|
759
|
+
|
|
756
760
|
// src/cli-client.ts
|
|
757
761
|
import { Provenance as Provenance2 } from "@neat.is/types";
|
|
758
762
|
var HttpError = class extends Error {
|
|
@@ -826,7 +830,7 @@ async function runRootCause(client, input) {
|
|
|
826
830
|
const qs = input.errorId ? `?errorId=${encodeURIComponent(input.errorId)}` : "";
|
|
827
831
|
const path5 = projectPath(
|
|
828
832
|
input.project,
|
|
829
|
-
`/
|
|
833
|
+
`/graph/root-cause/${encodeURIComponent(input.errorNode)}${qs}`
|
|
830
834
|
);
|
|
831
835
|
try {
|
|
832
836
|
const result = await client.get(path5);
|
|
@@ -857,7 +861,7 @@ async function runBlastRadius(client, input) {
|
|
|
857
861
|
const qs = input.depth !== void 0 ? `?depth=${input.depth}` : "";
|
|
858
862
|
const path5 = projectPath(
|
|
859
863
|
input.project,
|
|
860
|
-
`/
|
|
864
|
+
`/graph/blast-radius/${encodeURIComponent(input.nodeId)}${qs}`
|
|
861
865
|
);
|
|
862
866
|
try {
|
|
863
867
|
const result = await client.get(path5);
|
|
@@ -896,7 +900,7 @@ async function runDependencies(client, input) {
|
|
|
896
900
|
const depth = input.depth ?? 3;
|
|
897
901
|
const path5 = projectPath(
|
|
898
902
|
input.project,
|
|
899
|
-
`/graph/
|
|
903
|
+
`/graph/dependencies/${encodeURIComponent(input.nodeId)}?depth=${depth}`
|
|
900
904
|
);
|
|
901
905
|
try {
|
|
902
906
|
const result = await client.get(path5);
|
|
@@ -982,7 +986,8 @@ function formatDuration(ms) {
|
|
|
982
986
|
async function runIncidents(client, input) {
|
|
983
987
|
const path5 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
|
|
984
988
|
try {
|
|
985
|
-
const
|
|
989
|
+
const body = await client.get(path5);
|
|
990
|
+
const events = body.events;
|
|
986
991
|
if (events.length === 0) {
|
|
987
992
|
return {
|
|
988
993
|
summary: input.nodeId ? `No incidents recorded against ${input.nodeId}.` : "No incidents recorded."
|
|
@@ -996,7 +1001,7 @@ async function runIncidents(client, input) {
|
|
|
996
1001
|
}
|
|
997
1002
|
const target = input.nodeId ?? "the project";
|
|
998
1003
|
return {
|
|
999
|
-
summary: `${target} has ${
|
|
1004
|
+
summary: `${target} has ${body.total} recorded incident${body.total === 1 ? "" : "s"}; showing the ${ordered.length} most recent.`,
|
|
1000
1005
|
block: blockLines.join("\n"),
|
|
1001
1006
|
provenance: Provenance2.OBSERVED
|
|
1002
1007
|
};
|
|
@@ -1092,9 +1097,10 @@ async function runStaleEdges(client, input) {
|
|
|
1092
1097
|
if (input.limit !== void 0) params.set("limit", String(input.limit));
|
|
1093
1098
|
if (input.edgeType) params.set("edgeType", input.edgeType);
|
|
1094
1099
|
const qs = params.size > 0 ? `?${params.toString()}` : "";
|
|
1095
|
-
const
|
|
1096
|
-
projectPath(input.project, `/
|
|
1100
|
+
const body = await client.get(
|
|
1101
|
+
projectPath(input.project, `/stale-events${qs}`)
|
|
1097
1102
|
);
|
|
1103
|
+
const events = body.events;
|
|
1098
1104
|
if (events.length === 0) {
|
|
1099
1105
|
return {
|
|
1100
1106
|
summary: input.edgeType ? `No stale ${input.edgeType} edges recorded.` : "No stale-edge transitions recorded yet."
|
|
@@ -1128,9 +1134,10 @@ async function runPolicies(client, input) {
|
|
|
1128
1134
|
const params = new URLSearchParams();
|
|
1129
1135
|
if (input.policyId) params.set("policyId", input.policyId);
|
|
1130
1136
|
const qs = params.size > 0 ? `?${params.toString()}` : "";
|
|
1131
|
-
|
|
1137
|
+
const body = await client.get(
|
|
1132
1138
|
projectPath(input.project, `/policies/violations${qs}`)
|
|
1133
1139
|
);
|
|
1140
|
+
violations = body.violations;
|
|
1134
1141
|
allowed = violations.every((v) => v.onViolation !== "block");
|
|
1135
1142
|
}
|
|
1136
1143
|
if (input.nodeId) {
|
|
@@ -1169,6 +1176,54 @@ async function runPolicies(client, input) {
|
|
|
1169
1176
|
provenance: severities.join(" ")
|
|
1170
1177
|
};
|
|
1171
1178
|
}
|
|
1179
|
+
function formatDivergenceLine(d) {
|
|
1180
|
+
switch (d.type) {
|
|
1181
|
+
case "missing-observed":
|
|
1182
|
+
case "missing-extracted":
|
|
1183
|
+
return ` \u2022 [${d.type}] ${d.source} \u2192 ${d.target} (${d.edgeType}) \u2014 confidence ${d.confidence.toFixed(2)}`;
|
|
1184
|
+
case "version-mismatch":
|
|
1185
|
+
return ` \u2022 [${d.type}] ${d.source} \u2192 ${d.target} \u2014 declared ${d.extractedVersion}, observed engine ${d.observedVersion} (${d.compatibility})`;
|
|
1186
|
+
case "host-mismatch":
|
|
1187
|
+
return ` \u2022 [${d.type}] ${d.source} \u2192 ${d.target} \u2014 declared host ${d.extractedHost}, observed host ${d.observedHost}`;
|
|
1188
|
+
case "compat-violation":
|
|
1189
|
+
return ` \u2022 [${d.type}] ${d.source} \u2192 ${d.target} \u2014 ${d.rule.kind}${d.rule.package ? ` (${d.rule.package})` : ""}`;
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
async function runDivergences(client, input) {
|
|
1193
|
+
const params = new URLSearchParams();
|
|
1194
|
+
if (input.type && input.type.length > 0) params.set("type", input.type.join(","));
|
|
1195
|
+
if (input.minConfidence !== void 0) {
|
|
1196
|
+
params.set("minConfidence", String(input.minConfidence));
|
|
1197
|
+
}
|
|
1198
|
+
if (input.node) params.set("node", input.node);
|
|
1199
|
+
const qs = params.size > 0 ? `?${params.toString()}` : "";
|
|
1200
|
+
const result = await client.get(
|
|
1201
|
+
projectPath(input.project, `/graph/divergences${qs}`)
|
|
1202
|
+
);
|
|
1203
|
+
if (result.totalAffected === 0) {
|
|
1204
|
+
return {
|
|
1205
|
+
summary: "No divergences found between the declared (EXTRACTED) and observed (OBSERVED) views of the graph."
|
|
1206
|
+
};
|
|
1207
|
+
}
|
|
1208
|
+
const headline = result.divergences[0];
|
|
1209
|
+
const summary = `Found ${result.totalAffected} divergence${result.totalAffected === 1 ? "" : "s"} between code and production. Highest-confidence: ${headline.type} on ${headline.source} \u2192 ${headline.target}. ${headline.reason}`;
|
|
1210
|
+
const blockLines = [];
|
|
1211
|
+
for (const d of result.divergences) {
|
|
1212
|
+
blockLines.push(formatDivergenceLine(d));
|
|
1213
|
+
blockLines.push(` reason: ${d.reason}`);
|
|
1214
|
+
blockLines.push(` recommendation: ${d.recommendation}`);
|
|
1215
|
+
}
|
|
1216
|
+
const maxConfidence = result.divergences.reduce(
|
|
1217
|
+
(m, d) => Math.max(m, d.confidence),
|
|
1218
|
+
0
|
|
1219
|
+
);
|
|
1220
|
+
return {
|
|
1221
|
+
summary,
|
|
1222
|
+
block: blockLines.join("\n"),
|
|
1223
|
+
confidence: maxConfidence,
|
|
1224
|
+
provenance: "composite (EXTRACTED + OBSERVED)"
|
|
1225
|
+
};
|
|
1226
|
+
}
|
|
1172
1227
|
function formatFooter(confidence, provenance) {
|
|
1173
1228
|
const c = confidence === void 0 ? "n/a" : confidence.toFixed(2);
|
|
1174
1229
|
const p = provenance === void 0 ? "n/a" : Array.isArray(provenance) ? [...new Set(provenance)].join(", ") : provenance;
|
|
@@ -1247,6 +1302,9 @@ function usage() {
|
|
|
1247
1302
|
console.log(" policies Current policy violations.");
|
|
1248
1303
|
console.log(" Flags: --node <id>, --hypothetical-action <json>");
|
|
1249
1304
|
console.log(" example: neat policies --node service:<name> --json");
|
|
1305
|
+
console.log(" divergences Where code (EXTRACTED) and production (OBSERVED) disagree.");
|
|
1306
|
+
console.log(" Flags: --type <list>, --min-confidence <0..1>, --node <id>");
|
|
1307
|
+
console.log(" example: neat divergences --min-confidence 0.7");
|
|
1250
1308
|
console.log("");
|
|
1251
1309
|
console.log("flags:");
|
|
1252
1310
|
console.log(' --project <name> Name the project this command targets. Default: "default".');
|
|
@@ -1271,7 +1329,9 @@ var STRING_FLAGS = [
|
|
|
1271
1329
|
["--since", "since"],
|
|
1272
1330
|
["--against", "against"],
|
|
1273
1331
|
["--error-id", "errorId"],
|
|
1274
|
-
["--hypothetical-action", "hypotheticalAction"]
|
|
1332
|
+
["--hypothetical-action", "hypotheticalAction"],
|
|
1333
|
+
["--type", "type"],
|
|
1334
|
+
["--min-confidence", "minConfidence"]
|
|
1275
1335
|
];
|
|
1276
1336
|
function parseArgs(rest) {
|
|
1277
1337
|
const positional = [];
|
|
@@ -1290,6 +1350,8 @@ function parseArgs(rest) {
|
|
|
1290
1350
|
against: null,
|
|
1291
1351
|
errorId: null,
|
|
1292
1352
|
hypotheticalAction: null,
|
|
1353
|
+
type: null,
|
|
1354
|
+
minConfidence: null,
|
|
1293
1355
|
positional: []
|
|
1294
1356
|
};
|
|
1295
1357
|
for (let i = 0; i < rest.length; i++) {
|
|
@@ -1349,6 +1411,15 @@ function assignFlag(out, field, value) {
|
|
|
1349
1411
|
out[field] = n;
|
|
1350
1412
|
return;
|
|
1351
1413
|
}
|
|
1414
|
+
if (field === "minConfidence") {
|
|
1415
|
+
const n = Number(value);
|
|
1416
|
+
if (!Number.isFinite(n) || n < 0 || n > 1) {
|
|
1417
|
+
console.error("neat: --min-confidence must be a number in [0, 1]");
|
|
1418
|
+
process.exit(2);
|
|
1419
|
+
}
|
|
1420
|
+
out.minConfidence = n;
|
|
1421
|
+
return;
|
|
1422
|
+
}
|
|
1352
1423
|
;
|
|
1353
1424
|
out[field] = value;
|
|
1354
1425
|
}
|
|
@@ -1732,7 +1803,9 @@ var QUERY_VERBS = /* @__PURE__ */ new Set([
|
|
|
1732
1803
|
"search",
|
|
1733
1804
|
"diff",
|
|
1734
1805
|
"stale-edges",
|
|
1735
|
-
"policies"
|
|
1806
|
+
"policies",
|
|
1807
|
+
// Tenth verb (ADR-060) — amends ADR-050's locked allowlist of nine.
|
|
1808
|
+
"divergences"
|
|
1736
1809
|
]);
|
|
1737
1810
|
function resolveProjectFlag(parsed) {
|
|
1738
1811
|
if (parsed.project) return parsed.project;
|
|
@@ -1854,6 +1927,31 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
1854
1927
|
});
|
|
1855
1928
|
break;
|
|
1856
1929
|
}
|
|
1930
|
+
case "divergences": {
|
|
1931
|
+
let typeFilter;
|
|
1932
|
+
if (parsed.type) {
|
|
1933
|
+
const parts = parsed.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
1934
|
+
const out = [];
|
|
1935
|
+
for (const p of parts) {
|
|
1936
|
+
const r = DivergenceTypeSchema.safeParse(p);
|
|
1937
|
+
if (!r.success) {
|
|
1938
|
+
console.error(
|
|
1939
|
+
`neat divergences: unknown --type "${p}". allowed: ${DivergenceTypeSchema.options.join(", ")}`
|
|
1940
|
+
);
|
|
1941
|
+
return 2;
|
|
1942
|
+
}
|
|
1943
|
+
out.push(r.data);
|
|
1944
|
+
}
|
|
1945
|
+
typeFilter = out;
|
|
1946
|
+
}
|
|
1947
|
+
work = runDivergences(client, {
|
|
1948
|
+
...typeFilter ? { type: typeFilter } : {},
|
|
1949
|
+
...parsed.minConfidence !== null ? { minConfidence: parsed.minConfidence } : {},
|
|
1950
|
+
...parsed.node ? { node: parsed.node } : {},
|
|
1951
|
+
...project ? { project } : {}
|
|
1952
|
+
});
|
|
1953
|
+
break;
|
|
1954
|
+
}
|
|
1857
1955
|
default:
|
|
1858
1956
|
console.error(`neat: unknown query verb "${cmd}"`);
|
|
1859
1957
|
return 2;
|