@neat.is/core 0.4.16 → 0.4.17
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-VMLWUK7W.js → chunk-CEDXXMGO.js} +16 -2
- package/dist/chunk-CEDXXMGO.js.map +1 -0
- package/dist/{chunk-XS4CGNRO.js → chunk-LUDSPX5N.js} +293 -71
- package/dist/chunk-LUDSPX5N.js.map +1 -0
- package/dist/cli.cjs +568 -286
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +12 -2
- package/dist/cli.d.ts +12 -2
- package/dist/cli.js +76 -17
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +377 -142
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +2 -2
- package/dist/neatd.cjs +384 -149
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +2 -2
- package/dist/server.cjs +269 -97
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-VMLWUK7W.js.map +0 -1
- package/dist/chunk-XS4CGNRO.js.map +0 -1
package/dist/cli.cjs
CHANGED
|
@@ -58,9 +58,9 @@ function mountBearerAuth(app, opts) {
|
|
|
58
58
|
const suffixes = [...DEFAULT_UNAUTH_SUFFIXES, ...opts.extraUnauthenticatedSuffixes ?? []];
|
|
59
59
|
const publicRead = opts.publicRead === true;
|
|
60
60
|
app.addHook("preHandler", (req, reply, done) => {
|
|
61
|
-
const
|
|
61
|
+
const path51 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
|
|
62
62
|
for (const suffix of suffixes) {
|
|
63
|
-
if (
|
|
63
|
+
if (path51 === suffix || path51.endsWith(suffix)) {
|
|
64
64
|
done();
|
|
65
65
|
return;
|
|
66
66
|
}
|
|
@@ -188,8 +188,8 @@ function reshapeGrpcRequest(req) {
|
|
|
188
188
|
};
|
|
189
189
|
}
|
|
190
190
|
function resolveProtoRoot() {
|
|
191
|
-
const here =
|
|
192
|
-
return
|
|
191
|
+
const here = import_node_path41.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
|
|
192
|
+
return import_node_path41.default.resolve(here, "..", "proto");
|
|
193
193
|
}
|
|
194
194
|
function loadTraceService() {
|
|
195
195
|
const protoRoot = resolveProtoRoot();
|
|
@@ -257,13 +257,13 @@ async function startOtelGrpcReceiver(opts) {
|
|
|
257
257
|
})
|
|
258
258
|
};
|
|
259
259
|
}
|
|
260
|
-
var import_node_url2,
|
|
260
|
+
var import_node_url2, import_node_path41, import_node_crypto2, grpc, protoLoader;
|
|
261
261
|
var init_otel_grpc = __esm({
|
|
262
262
|
"src/otel-grpc.ts"() {
|
|
263
263
|
"use strict";
|
|
264
264
|
init_cjs_shims();
|
|
265
265
|
import_node_url2 = require("url");
|
|
266
|
-
|
|
266
|
+
import_node_path41 = __toESM(require("path"), 1);
|
|
267
267
|
import_node_crypto2 = require("crypto");
|
|
268
268
|
grpc = __toESM(require("@grpc/grpc-js"), 1);
|
|
269
269
|
protoLoader = __toESM(require("@grpc/proto-loader"), 1);
|
|
@@ -373,10 +373,10 @@ function parseOtlpRequest(body) {
|
|
|
373
373
|
return out;
|
|
374
374
|
}
|
|
375
375
|
function loadProtoRoot() {
|
|
376
|
-
const here =
|
|
377
|
-
const protoRoot =
|
|
376
|
+
const here = import_node_path42.default.dirname((0, import_node_url3.fileURLToPath)(importMetaUrl));
|
|
377
|
+
const protoRoot = import_node_path42.default.resolve(here, "..", "proto");
|
|
378
378
|
const root = new import_protobufjs.default.Root();
|
|
379
|
-
root.resolvePath = (_origin, target) =>
|
|
379
|
+
root.resolvePath = (_origin, target) => import_node_path42.default.resolve(protoRoot, target);
|
|
380
380
|
root.loadSync(
|
|
381
381
|
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
|
|
382
382
|
{ keepCase: true }
|
|
@@ -572,12 +572,12 @@ async function buildOtelReceiver(opts) {
|
|
|
572
572
|
};
|
|
573
573
|
return decorated;
|
|
574
574
|
}
|
|
575
|
-
var
|
|
575
|
+
var import_node_path42, import_node_url3, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody;
|
|
576
576
|
var init_otel = __esm({
|
|
577
577
|
"src/otel.ts"() {
|
|
578
578
|
"use strict";
|
|
579
579
|
init_cjs_shims();
|
|
580
|
-
|
|
580
|
+
import_node_path42 = __toESM(require("path"), 1);
|
|
581
581
|
import_node_url3 = require("url");
|
|
582
582
|
import_fastify2 = __toESM(require("fastify"), 1);
|
|
583
583
|
import_protobufjs = __toESM(require("protobufjs"), 1);
|
|
@@ -595,6 +595,7 @@ var init_otel = __esm({
|
|
|
595
595
|
var cli_exports = {};
|
|
596
596
|
__export(cli_exports, {
|
|
597
597
|
CLAUDE_SKILL_CONFIG: () => CLAUDE_SKILL_CONFIG,
|
|
598
|
+
ProjectResolutionError: () => ProjectResolutionError,
|
|
598
599
|
QUERY_VERBS: () => QUERY_VERBS,
|
|
599
600
|
commandPrefix: () => commandPrefix,
|
|
600
601
|
isNpxInvocation: () => isNpxInvocation,
|
|
@@ -602,6 +603,7 @@ __export(cli_exports, {
|
|
|
602
603
|
parseArgs: () => parseArgs,
|
|
603
604
|
printBanner: () => printBanner,
|
|
604
605
|
readPackageVersion: () => readPackageVersion,
|
|
606
|
+
resolveProjectForVerb: () => resolveProjectForVerb,
|
|
605
607
|
runInit: () => runInit,
|
|
606
608
|
runQueryVerb: () => runQueryVerb,
|
|
607
609
|
runSkill: () => runSkill,
|
|
@@ -609,7 +611,7 @@ __export(cli_exports, {
|
|
|
609
611
|
});
|
|
610
612
|
module.exports = __toCommonJS(cli_exports);
|
|
611
613
|
init_cjs_shims();
|
|
612
|
-
var
|
|
614
|
+
var import_node_path50 = __toESM(require("path"), 1);
|
|
613
615
|
var import_node_fs32 = require("fs");
|
|
614
616
|
|
|
615
617
|
// src/banner.ts
|
|
@@ -1175,19 +1177,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
1175
1177
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
1176
1178
|
let best = { path: [start], edges: [] };
|
|
1177
1179
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
1178
|
-
function step(node,
|
|
1179
|
-
if (
|
|
1180
|
-
best = { path: [...
|
|
1180
|
+
function step(node, path51, edges) {
|
|
1181
|
+
if (path51.length > best.path.length) {
|
|
1182
|
+
best = { path: [...path51], edges: [...edges] };
|
|
1181
1183
|
}
|
|
1182
|
-
if (
|
|
1184
|
+
if (path51.length - 1 >= maxDepth) return;
|
|
1183
1185
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
1184
1186
|
for (const [srcId, edge] of incoming) {
|
|
1185
1187
|
if (visited.has(srcId)) continue;
|
|
1186
1188
|
visited.add(srcId);
|
|
1187
|
-
|
|
1189
|
+
path51.push(srcId);
|
|
1188
1190
|
edges.push(edge);
|
|
1189
|
-
step(srcId,
|
|
1190
|
-
|
|
1191
|
+
step(srcId, path51, edges);
|
|
1192
|
+
path51.pop();
|
|
1191
1193
|
edges.pop();
|
|
1192
1194
|
visited.delete(srcId);
|
|
1193
1195
|
}
|
|
@@ -1727,6 +1729,41 @@ function thresholdForEdgeType(edgeType, overrides) {
|
|
|
1727
1729
|
const map = overrides ?? loadStaleThresholdsFromEnv();
|
|
1728
1730
|
return map[edgeType] ?? FALLBACK_STALE_THRESHOLD_MS;
|
|
1729
1731
|
}
|
|
1732
|
+
var DEFAULT_INCIDENT_THRESHOLDS = {
|
|
1733
|
+
threshold: 5,
|
|
1734
|
+
windowMs: 6e4
|
|
1735
|
+
};
|
|
1736
|
+
function loadIncidentThresholdsFromEnv() {
|
|
1737
|
+
const raw = process.env.NEAT_INCIDENT_THRESHOLDS;
|
|
1738
|
+
if (!raw) return DEFAULT_INCIDENT_THRESHOLDS;
|
|
1739
|
+
try {
|
|
1740
|
+
const overrides = JSON.parse(raw);
|
|
1741
|
+
const merged = { ...DEFAULT_INCIDENT_THRESHOLDS };
|
|
1742
|
+
if (typeof overrides.threshold === "number" && Number.isFinite(overrides.threshold) && overrides.threshold >= 1) {
|
|
1743
|
+
merged.threshold = Math.floor(overrides.threshold);
|
|
1744
|
+
}
|
|
1745
|
+
if (typeof overrides.windowMs === "number" && Number.isFinite(overrides.windowMs) && overrides.windowMs >= 0) {
|
|
1746
|
+
merged.windowMs = overrides.windowMs;
|
|
1747
|
+
}
|
|
1748
|
+
return merged;
|
|
1749
|
+
} catch (err) {
|
|
1750
|
+
console.warn(
|
|
1751
|
+
`[neat] NEAT_INCIDENT_THRESHOLDS could not be parsed (${err.message}); using defaults`
|
|
1752
|
+
);
|
|
1753
|
+
return DEFAULT_INCIDENT_THRESHOLDS;
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
function httpResponseStatus(span) {
|
|
1757
|
+
for (const key of ["http.response.status_code", "http.status_code"]) {
|
|
1758
|
+
const v = span.attributes[key];
|
|
1759
|
+
if (typeof v === "number" && Number.isFinite(v)) return v;
|
|
1760
|
+
if (typeof v === "string") {
|
|
1761
|
+
const n = Number(v);
|
|
1762
|
+
if (Number.isFinite(n)) return n;
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
return void 0;
|
|
1766
|
+
}
|
|
1730
1767
|
function nowIso(ctx) {
|
|
1731
1768
|
return new Date(ctx.now ? ctx.now() : Date.now()).toISOString();
|
|
1732
1769
|
}
|
|
@@ -2140,6 +2177,71 @@ function makeErrorSpanWriter(errorsPath) {
|
|
|
2140
2177
|
await import_node_fs4.promises.appendFile(errorsPath, JSON.stringify(ev) + "\n", "utf8");
|
|
2141
2178
|
};
|
|
2142
2179
|
}
|
|
2180
|
+
async function recordFailingResponseIncident(ctx, span, affectedNode, timestamp, statusCode, count, firstTimestamp) {
|
|
2181
|
+
const attrs = sanitizeAttributes(span.attributes);
|
|
2182
|
+
const first = firstTimestamp ?? timestamp;
|
|
2183
|
+
const peer = pickAddress(span);
|
|
2184
|
+
const message = count > 1 ? `${count} consecutive HTTP ${statusCode} responses` + (peer ? ` to ${peer}` : "") : `HTTP ${statusCode} response` + (peer ? ` from ${peer}` : "");
|
|
2185
|
+
const ev = {
|
|
2186
|
+
id: `${span.traceId}:${span.spanId}`,
|
|
2187
|
+
timestamp,
|
|
2188
|
+
service: span.service,
|
|
2189
|
+
traceId: span.traceId,
|
|
2190
|
+
spanId: span.spanId,
|
|
2191
|
+
errorType: "http-failure",
|
|
2192
|
+
errorMessage: message,
|
|
2193
|
+
...Object.keys(attrs).length > 0 ? { attributes: attrs } : {},
|
|
2194
|
+
affectedNode,
|
|
2195
|
+
httpStatusCode: statusCode,
|
|
2196
|
+
incidentCount: count,
|
|
2197
|
+
firstTimestamp: first,
|
|
2198
|
+
lastTimestamp: timestamp
|
|
2199
|
+
};
|
|
2200
|
+
await appendErrorEvent(ctx, ev);
|
|
2201
|
+
}
|
|
2202
|
+
async function advance4xxBurst(ctx, span, affectedNode, ts, nowMs, status2) {
|
|
2203
|
+
const { threshold, windowMs } = loadIncidentThresholdsFromEnv();
|
|
2204
|
+
if (!ctx.burstState) ctx.burstState = /* @__PURE__ */ new Map();
|
|
2205
|
+
const peer = pickAddress(span) ?? span.spanId;
|
|
2206
|
+
const key = `${span.service}->${peer}`;
|
|
2207
|
+
const existing = ctx.burstState.get(key);
|
|
2208
|
+
let state;
|
|
2209
|
+
if (existing && nowMs - existing.lastMs <= windowMs) {
|
|
2210
|
+
existing.count += 1;
|
|
2211
|
+
existing.lastTs = ts;
|
|
2212
|
+
existing.lastMs = nowMs;
|
|
2213
|
+
existing.codes.set(status2, (existing.codes.get(status2) ?? 0) + 1);
|
|
2214
|
+
state = existing;
|
|
2215
|
+
} else {
|
|
2216
|
+
state = {
|
|
2217
|
+
count: 1,
|
|
2218
|
+
firstTs: ts,
|
|
2219
|
+
lastTs: ts,
|
|
2220
|
+
lastMs: nowMs,
|
|
2221
|
+
codes: /* @__PURE__ */ new Map([[status2, 1]])
|
|
2222
|
+
};
|
|
2223
|
+
ctx.burstState.set(key, state);
|
|
2224
|
+
}
|
|
2225
|
+
if (state.count < threshold) return;
|
|
2226
|
+
let dominant = status2;
|
|
2227
|
+
let max = 0;
|
|
2228
|
+
for (const [code, n] of state.codes) {
|
|
2229
|
+
if (n > max) {
|
|
2230
|
+
max = n;
|
|
2231
|
+
dominant = code;
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
await recordFailingResponseIncident(
|
|
2235
|
+
ctx,
|
|
2236
|
+
span,
|
|
2237
|
+
affectedNode,
|
|
2238
|
+
state.lastTs,
|
|
2239
|
+
dominant,
|
|
2240
|
+
state.count,
|
|
2241
|
+
state.firstTs
|
|
2242
|
+
);
|
|
2243
|
+
ctx.burstState.delete(key);
|
|
2244
|
+
}
|
|
2143
2245
|
async function handleSpan(ctx, span) {
|
|
2144
2246
|
const ts = span.startTimeIso ?? nowIso(ctx);
|
|
2145
2247
|
const nowMs = ctx.now ? ctx.now() : Date.now();
|
|
@@ -2238,6 +2340,14 @@ async function handleSpan(ctx, span) {
|
|
|
2238
2340
|
await appendErrorEvent(ctx, ev);
|
|
2239
2341
|
}
|
|
2240
2342
|
}
|
|
2343
|
+
if (span.statusCode !== 2) {
|
|
2344
|
+
const status2 = httpResponseStatus(span);
|
|
2345
|
+
if (status2 !== void 0 && status2 >= 500) {
|
|
2346
|
+
await recordFailingResponseIncident(ctx, span, sourceId, ts, status2, 1);
|
|
2347
|
+
} else if (status2 !== void 0 && status2 >= 400 && spanMintsObservedEdge(span.kind)) {
|
|
2348
|
+
await advance4xxBurst(ctx, span, sourceId, ts, nowMs, status2);
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2241
2351
|
void affectedNode;
|
|
2242
2352
|
if (ctx.onPolicyTrigger) await ctx.onPolicyTrigger(ctx.graph);
|
|
2243
2353
|
}
|
|
@@ -4269,7 +4379,7 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
4269
4379
|
|
|
4270
4380
|
// src/extract/calls/index.ts
|
|
4271
4381
|
init_cjs_shims();
|
|
4272
|
-
var
|
|
4382
|
+
var import_types17 = require("@neat.is/types");
|
|
4273
4383
|
|
|
4274
4384
|
// src/extract/calls/http.ts
|
|
4275
4385
|
init_cjs_shims();
|
|
@@ -4622,15 +4732,75 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
4622
4732
|
return out;
|
|
4623
4733
|
}
|
|
4624
4734
|
|
|
4735
|
+
// src/extract/calls/supabase.ts
|
|
4736
|
+
init_cjs_shims();
|
|
4737
|
+
var import_node_path28 = __toESM(require("path"), 1);
|
|
4738
|
+
var import_types16 = require("@neat.is/types");
|
|
4739
|
+
var SUPABASE_JS_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/supabase-js['"`]/;
|
|
4740
|
+
var SUPABASE_SSR_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/ssr['"`]/;
|
|
4741
|
+
var SUPABASE_CLIENT_RE = /\b(createClient|createServerClient|createBrowserClient)\s*\(\s*(?:['"`]([^'"`]*)['"`])?/g;
|
|
4742
|
+
function hostFromLiteral(literal) {
|
|
4743
|
+
if (!literal) return null;
|
|
4744
|
+
const m = /^https?:\/\/([^/:'"`\s]+)/.exec(literal.trim());
|
|
4745
|
+
if (!m) return null;
|
|
4746
|
+
const host = m[1];
|
|
4747
|
+
if (!/\.supabase\.(co|in)$/i.test(host)) return null;
|
|
4748
|
+
return host;
|
|
4749
|
+
}
|
|
4750
|
+
function readImports2(content) {
|
|
4751
|
+
return {
|
|
4752
|
+
hasSupabaseJs: SUPABASE_JS_IMPORT_RE.test(content),
|
|
4753
|
+
hasSupabaseSsr: SUPABASE_SSR_IMPORT_RE.test(content)
|
|
4754
|
+
};
|
|
4755
|
+
}
|
|
4756
|
+
function constructorMatchesImport(name, ctx) {
|
|
4757
|
+
if (name === "createClient") return ctx.hasSupabaseJs;
|
|
4758
|
+
return ctx.hasSupabaseSsr;
|
|
4759
|
+
}
|
|
4760
|
+
function supabaseEndpointsFromFile(file, serviceDir) {
|
|
4761
|
+
const ctx = readImports2(file.content);
|
|
4762
|
+
if (!ctx.hasSupabaseJs && !ctx.hasSupabaseSsr) return [];
|
|
4763
|
+
const out = [];
|
|
4764
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4765
|
+
SUPABASE_CLIENT_RE.lastIndex = 0;
|
|
4766
|
+
let m;
|
|
4767
|
+
while ((m = SUPABASE_CLIENT_RE.exec(file.content)) !== null) {
|
|
4768
|
+
const ctor = m[1];
|
|
4769
|
+
if (!constructorMatchesImport(ctor, ctx)) continue;
|
|
4770
|
+
const host = hostFromLiteral(m[2]);
|
|
4771
|
+
const name = host ?? "env";
|
|
4772
|
+
if (seen.has(name)) continue;
|
|
4773
|
+
seen.add(name);
|
|
4774
|
+
const line = lineOf(file.content, m[0]);
|
|
4775
|
+
out.push({
|
|
4776
|
+
infraId: (0, import_types16.infraId)("supabase", name),
|
|
4777
|
+
name,
|
|
4778
|
+
kind: "supabase",
|
|
4779
|
+
edgeType: "CALLS",
|
|
4780
|
+
// `createClient(...)` from @supabase/supabase-js (or createServerClient /
|
|
4781
|
+
// createBrowserClient from @supabase/ssr) with the import in scope — a
|
|
4782
|
+
// framework-aware recognizer matched the SDK shape. Verified-call-site
|
|
4783
|
+
// tier (ADR-066), the same grade aws.ts / grpc.ts emit at.
|
|
4784
|
+
confidenceKind: "verified-call-site",
|
|
4785
|
+
evidence: {
|
|
4786
|
+
file: import_node_path28.default.relative(serviceDir, file.path),
|
|
4787
|
+
line,
|
|
4788
|
+
snippet: snippet(file.content, line)
|
|
4789
|
+
}
|
|
4790
|
+
});
|
|
4791
|
+
}
|
|
4792
|
+
return out;
|
|
4793
|
+
}
|
|
4794
|
+
|
|
4625
4795
|
// src/extract/calls/index.ts
|
|
4626
4796
|
function edgeTypeFromEndpoint(ep) {
|
|
4627
4797
|
switch (ep.edgeType) {
|
|
4628
4798
|
case "PUBLISHES_TO":
|
|
4629
|
-
return
|
|
4799
|
+
return import_types17.EdgeType.PUBLISHES_TO;
|
|
4630
4800
|
case "CONSUMES_FROM":
|
|
4631
|
-
return
|
|
4801
|
+
return import_types17.EdgeType.CONSUMES_FROM;
|
|
4632
4802
|
default:
|
|
4633
|
-
return
|
|
4803
|
+
return import_types17.EdgeType.CALLS;
|
|
4634
4804
|
}
|
|
4635
4805
|
}
|
|
4636
4806
|
function isAwsKind(kind) {
|
|
@@ -4650,6 +4820,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
4650
4820
|
endpoints.push(...redisEndpointsFromFile(maskedFile, service.dir));
|
|
4651
4821
|
endpoints.push(...awsEndpointsFromFile(maskedFile, service.dir));
|
|
4652
4822
|
endpoints.push(...grpcEndpointsFromFile(maskedFile, service.dir));
|
|
4823
|
+
endpoints.push(...supabaseEndpointsFromFile(maskedFile, service.dir));
|
|
4653
4824
|
}
|
|
4654
4825
|
if (endpoints.length === 0) continue;
|
|
4655
4826
|
const seenEdges = /* @__PURE__ */ new Set();
|
|
@@ -4657,7 +4828,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
4657
4828
|
if (!graph.hasNode(ep.infraId)) {
|
|
4658
4829
|
const node = {
|
|
4659
4830
|
id: ep.infraId,
|
|
4660
|
-
type:
|
|
4831
|
+
type: import_types17.NodeType.InfraNode,
|
|
4661
4832
|
name: ep.name,
|
|
4662
4833
|
// #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
|
|
4663
4834
|
// aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
|
|
@@ -4669,7 +4840,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
4669
4840
|
nodesAdded++;
|
|
4670
4841
|
}
|
|
4671
4842
|
const edgeType = edgeTypeFromEndpoint(ep);
|
|
4672
|
-
const confidence = (0,
|
|
4843
|
+
const confidence = (0, import_types17.confidenceForExtracted)(ep.confidenceKind);
|
|
4673
4844
|
const relFile = toPosix2(ep.evidence.file);
|
|
4674
4845
|
const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
4675
4846
|
graph,
|
|
@@ -4679,7 +4850,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
4679
4850
|
);
|
|
4680
4851
|
nodesAdded += n;
|
|
4681
4852
|
edgesAdded += e;
|
|
4682
|
-
if (!(0,
|
|
4853
|
+
if (!(0, import_types17.passesExtractedFloor)(confidence)) {
|
|
4683
4854
|
noteExtractedDropped({
|
|
4684
4855
|
source: fileNodeId,
|
|
4685
4856
|
target: ep.infraId,
|
|
@@ -4699,7 +4870,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
4699
4870
|
source: fileNodeId,
|
|
4700
4871
|
target: ep.infraId,
|
|
4701
4872
|
type: edgeType,
|
|
4702
|
-
provenance:
|
|
4873
|
+
provenance: import_types17.Provenance.EXTRACTED,
|
|
4703
4874
|
confidence,
|
|
4704
4875
|
evidence: ep.evidence
|
|
4705
4876
|
};
|
|
@@ -4724,16 +4895,16 @@ init_cjs_shims();
|
|
|
4724
4895
|
|
|
4725
4896
|
// src/extract/infra/docker-compose.ts
|
|
4726
4897
|
init_cjs_shims();
|
|
4727
|
-
var
|
|
4728
|
-
var
|
|
4898
|
+
var import_node_path29 = __toESM(require("path"), 1);
|
|
4899
|
+
var import_types19 = require("@neat.is/types");
|
|
4729
4900
|
|
|
4730
4901
|
// src/extract/infra/shared.ts
|
|
4731
4902
|
init_cjs_shims();
|
|
4732
|
-
var
|
|
4903
|
+
var import_types18 = require("@neat.is/types");
|
|
4733
4904
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
4734
4905
|
return {
|
|
4735
|
-
id: (0,
|
|
4736
|
-
type:
|
|
4906
|
+
id: (0, import_types18.infraId)(kind, name),
|
|
4907
|
+
type: import_types18.NodeType.InfraNode,
|
|
4737
4908
|
name,
|
|
4738
4909
|
provider,
|
|
4739
4910
|
kind,
|
|
@@ -4762,7 +4933,7 @@ function dependsOnList(value) {
|
|
|
4762
4933
|
}
|
|
4763
4934
|
function serviceNameToServiceNode(name, services) {
|
|
4764
4935
|
for (const s of services) {
|
|
4765
|
-
if (s.node.name === name ||
|
|
4936
|
+
if (s.node.name === name || import_node_path29.default.basename(s.dir) === name) return s.node.id;
|
|
4766
4937
|
}
|
|
4767
4938
|
return null;
|
|
4768
4939
|
}
|
|
@@ -4771,7 +4942,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
4771
4942
|
let edgesAdded = 0;
|
|
4772
4943
|
let composePath = null;
|
|
4773
4944
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
4774
|
-
const abs =
|
|
4945
|
+
const abs = import_node_path29.default.join(scanPath, name);
|
|
4775
4946
|
if (await exists(abs)) {
|
|
4776
4947
|
composePath = abs;
|
|
4777
4948
|
break;
|
|
@@ -4784,13 +4955,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
4784
4955
|
} catch (err) {
|
|
4785
4956
|
recordExtractionError(
|
|
4786
4957
|
"infra docker-compose",
|
|
4787
|
-
|
|
4958
|
+
import_node_path29.default.relative(scanPath, composePath),
|
|
4788
4959
|
err
|
|
4789
4960
|
);
|
|
4790
4961
|
return { nodesAdded, edgesAdded };
|
|
4791
4962
|
}
|
|
4792
4963
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
4793
|
-
const evidenceFile =
|
|
4964
|
+
const evidenceFile = import_node_path29.default.relative(scanPath, composePath).split(import_node_path29.default.sep).join("/");
|
|
4794
4965
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
4795
4966
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
4796
4967
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -4812,15 +4983,15 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
4812
4983
|
for (const dep of dependsOnList(svc.depends_on)) {
|
|
4813
4984
|
const targetId = composeNameToNodeId.get(dep);
|
|
4814
4985
|
if (!targetId) continue;
|
|
4815
|
-
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId,
|
|
4986
|
+
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId, import_types19.EdgeType.DEPENDS_ON);
|
|
4816
4987
|
if (graph.hasEdge(edgeId)) continue;
|
|
4817
4988
|
const edge = {
|
|
4818
4989
|
id: edgeId,
|
|
4819
4990
|
source: sourceId,
|
|
4820
4991
|
target: targetId,
|
|
4821
|
-
type:
|
|
4822
|
-
provenance:
|
|
4823
|
-
confidence: (0,
|
|
4992
|
+
type: import_types19.EdgeType.DEPENDS_ON,
|
|
4993
|
+
provenance: import_types19.Provenance.EXTRACTED,
|
|
4994
|
+
confidence: (0, import_types19.confidenceForExtracted)("structural"),
|
|
4824
4995
|
evidence: { file: evidenceFile }
|
|
4825
4996
|
};
|
|
4826
4997
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -4832,9 +5003,9 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
4832
5003
|
|
|
4833
5004
|
// src/extract/infra/dockerfile.ts
|
|
4834
5005
|
init_cjs_shims();
|
|
4835
|
-
var
|
|
5006
|
+
var import_node_path30 = __toESM(require("path"), 1);
|
|
4836
5007
|
var import_node_fs16 = require("fs");
|
|
4837
|
-
var
|
|
5008
|
+
var import_types20 = require("@neat.is/types");
|
|
4838
5009
|
function runtimeImage(content) {
|
|
4839
5010
|
const lines = content.split("\n");
|
|
4840
5011
|
let last = null;
|
|
@@ -4853,7 +5024,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4853
5024
|
let nodesAdded = 0;
|
|
4854
5025
|
let edgesAdded = 0;
|
|
4855
5026
|
for (const service of services) {
|
|
4856
|
-
const dockerfilePath =
|
|
5027
|
+
const dockerfilePath = import_node_path30.default.join(service.dir, "Dockerfile");
|
|
4857
5028
|
if (!await exists(dockerfilePath)) continue;
|
|
4858
5029
|
let content;
|
|
4859
5030
|
try {
|
|
@@ -4861,7 +5032,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4861
5032
|
} catch (err) {
|
|
4862
5033
|
recordExtractionError(
|
|
4863
5034
|
"infra dockerfile",
|
|
4864
|
-
|
|
5035
|
+
import_node_path30.default.relative(scanPath, dockerfilePath),
|
|
4865
5036
|
err
|
|
4866
5037
|
);
|
|
4867
5038
|
continue;
|
|
@@ -4873,7 +5044,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4873
5044
|
graph.addNode(node.id, node);
|
|
4874
5045
|
nodesAdded++;
|
|
4875
5046
|
}
|
|
4876
|
-
const relDockerfile = toPosix2(
|
|
5047
|
+
const relDockerfile = toPosix2(import_node_path30.default.relative(service.dir, dockerfilePath));
|
|
4877
5048
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
4878
5049
|
graph,
|
|
4879
5050
|
service.pkg.name,
|
|
@@ -4882,17 +5053,17 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4882
5053
|
);
|
|
4883
5054
|
nodesAdded += fn;
|
|
4884
5055
|
edgesAdded += fe;
|
|
4885
|
-
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id,
|
|
5056
|
+
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id, import_types20.EdgeType.RUNS_ON);
|
|
4886
5057
|
if (!graph.hasEdge(edgeId)) {
|
|
4887
5058
|
const edge = {
|
|
4888
5059
|
id: edgeId,
|
|
4889
5060
|
source: fileNodeId,
|
|
4890
5061
|
target: node.id,
|
|
4891
|
-
type:
|
|
4892
|
-
provenance:
|
|
4893
|
-
confidence: (0,
|
|
5062
|
+
type: import_types20.EdgeType.RUNS_ON,
|
|
5063
|
+
provenance: import_types20.Provenance.EXTRACTED,
|
|
5064
|
+
confidence: (0, import_types20.confidenceForExtracted)("structural"),
|
|
4894
5065
|
evidence: {
|
|
4895
|
-
file: toPosix2(
|
|
5066
|
+
file: toPosix2(import_node_path30.default.relative(scanPath, dockerfilePath))
|
|
4896
5067
|
}
|
|
4897
5068
|
};
|
|
4898
5069
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -4905,7 +5076,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4905
5076
|
// src/extract/infra/terraform.ts
|
|
4906
5077
|
init_cjs_shims();
|
|
4907
5078
|
var import_node_fs17 = require("fs");
|
|
4908
|
-
var
|
|
5079
|
+
var import_node_path31 = __toESM(require("path"), 1);
|
|
4909
5080
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
4910
5081
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
4911
5082
|
if (depth > max) return [];
|
|
@@ -4914,11 +5085,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
|
|
|
4914
5085
|
for (const entry2 of entries) {
|
|
4915
5086
|
if (entry2.isDirectory()) {
|
|
4916
5087
|
if (IGNORED_DIRS.has(entry2.name) || entry2.name === ".terraform") continue;
|
|
4917
|
-
const child =
|
|
5088
|
+
const child = import_node_path31.default.join(start, entry2.name);
|
|
4918
5089
|
if (await isPythonVenvDir(child)) continue;
|
|
4919
5090
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
4920
5091
|
} else if (entry2.isFile() && entry2.name.endsWith(".tf")) {
|
|
4921
|
-
out.push(
|
|
5092
|
+
out.push(import_node_path31.default.join(start, entry2.name));
|
|
4922
5093
|
}
|
|
4923
5094
|
}
|
|
4924
5095
|
return out;
|
|
@@ -4946,7 +5117,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
4946
5117
|
// src/extract/infra/k8s.ts
|
|
4947
5118
|
init_cjs_shims();
|
|
4948
5119
|
var import_node_fs18 = require("fs");
|
|
4949
|
-
var
|
|
5120
|
+
var import_node_path32 = __toESM(require("path"), 1);
|
|
4950
5121
|
var import_yaml3 = require("yaml");
|
|
4951
5122
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
4952
5123
|
Service: "k8s-service",
|
|
@@ -4964,11 +5135,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
|
4964
5135
|
for (const entry2 of entries) {
|
|
4965
5136
|
if (entry2.isDirectory()) {
|
|
4966
5137
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
4967
|
-
const child =
|
|
5138
|
+
const child = import_node_path32.default.join(start, entry2.name);
|
|
4968
5139
|
if (await isPythonVenvDir(child)) continue;
|
|
4969
5140
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
4970
|
-
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
4971
|
-
out.push(
|
|
5141
|
+
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path32.default.extname(entry2.name))) {
|
|
5142
|
+
out.push(import_node_path32.default.join(start, entry2.name));
|
|
4972
5143
|
}
|
|
4973
5144
|
}
|
|
4974
5145
|
return out;
|
|
@@ -5012,17 +5183,17 @@ async function addInfra(graph, scanPath, services) {
|
|
|
5012
5183
|
}
|
|
5013
5184
|
|
|
5014
5185
|
// src/extract/index.ts
|
|
5015
|
-
var
|
|
5186
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
5016
5187
|
|
|
5017
5188
|
// src/extract/retire.ts
|
|
5018
5189
|
init_cjs_shims();
|
|
5019
5190
|
var import_node_fs19 = require("fs");
|
|
5020
|
-
var
|
|
5021
|
-
var
|
|
5191
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
5192
|
+
var import_types21 = require("@neat.is/types");
|
|
5022
5193
|
function dropOrphanedFileNodes(graph) {
|
|
5023
5194
|
const orphans = [];
|
|
5024
5195
|
graph.forEachNode((id, attrs) => {
|
|
5025
|
-
if (attrs.type !==
|
|
5196
|
+
if (attrs.type !== import_types21.NodeType.FileNode) return;
|
|
5026
5197
|
if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
|
|
5027
5198
|
orphans.push(id);
|
|
5028
5199
|
}
|
|
@@ -5035,7 +5206,7 @@ function retireEdgesByFile(graph, file) {
|
|
|
5035
5206
|
const toDrop = [];
|
|
5036
5207
|
graph.forEachEdge((id, attrs) => {
|
|
5037
5208
|
const edge = attrs;
|
|
5038
|
-
if (edge.provenance !==
|
|
5209
|
+
if (edge.provenance !== import_types21.Provenance.EXTRACTED) return;
|
|
5039
5210
|
if (!edge.evidence?.file) return;
|
|
5040
5211
|
if (edge.evidence.file === normalized) toDrop.push(id);
|
|
5041
5212
|
});
|
|
@@ -5048,14 +5219,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
5048
5219
|
const bases = [scanPath, ...serviceDirs];
|
|
5049
5220
|
graph.forEachEdge((id, attrs) => {
|
|
5050
5221
|
const edge = attrs;
|
|
5051
|
-
if (edge.provenance !==
|
|
5222
|
+
if (edge.provenance !== import_types21.Provenance.EXTRACTED) return;
|
|
5052
5223
|
const evidenceFile = edge.evidence?.file;
|
|
5053
5224
|
if (!evidenceFile) return;
|
|
5054
|
-
if (
|
|
5225
|
+
if (import_node_path33.default.isAbsolute(evidenceFile)) {
|
|
5055
5226
|
if (!(0, import_node_fs19.existsSync)(evidenceFile)) toDrop.push(id);
|
|
5056
5227
|
return;
|
|
5057
5228
|
}
|
|
5058
|
-
const found = bases.some((base) => (0, import_node_fs19.existsSync)(
|
|
5229
|
+
const found = bases.some((base) => (0, import_node_fs19.existsSync)(import_node_path33.default.join(base, evidenceFile)));
|
|
5059
5230
|
if (!found) toDrop.push(id);
|
|
5060
5231
|
});
|
|
5061
5232
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -5095,7 +5266,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
5095
5266
|
}
|
|
5096
5267
|
const droppedEntries = drainDroppedExtracted();
|
|
5097
5268
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
5098
|
-
const rejectedPath =
|
|
5269
|
+
const rejectedPath = import_node_path34.default.join(import_node_path34.default.dirname(opts.errorsPath), "rejected.ndjson");
|
|
5099
5270
|
try {
|
|
5100
5271
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
5101
5272
|
} catch (err) {
|
|
@@ -5129,7 +5300,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
5129
5300
|
|
|
5130
5301
|
// src/divergences.ts
|
|
5131
5302
|
init_cjs_shims();
|
|
5132
|
-
var
|
|
5303
|
+
var import_types22 = require("@neat.is/types");
|
|
5133
5304
|
function bucketKey(source, target, type) {
|
|
5134
5305
|
return `${type}|${source}|${target}`;
|
|
5135
5306
|
}
|
|
@@ -5137,22 +5308,22 @@ function bucketEdges(graph) {
|
|
|
5137
5308
|
const buckets = /* @__PURE__ */ new Map();
|
|
5138
5309
|
graph.forEachEdge((id, attrs) => {
|
|
5139
5310
|
const e = attrs;
|
|
5140
|
-
const parsed = (0,
|
|
5311
|
+
const parsed = (0, import_types22.parseEdgeId)(id);
|
|
5141
5312
|
const provenance = parsed?.provenance ?? e.provenance;
|
|
5142
5313
|
const key = bucketKey(e.source, e.target, e.type);
|
|
5143
5314
|
const cur = buckets.get(key) ?? { source: e.source, target: e.target, type: e.type };
|
|
5144
5315
|
switch (provenance) {
|
|
5145
|
-
case
|
|
5316
|
+
case import_types22.Provenance.EXTRACTED:
|
|
5146
5317
|
cur.extracted = e;
|
|
5147
5318
|
break;
|
|
5148
|
-
case
|
|
5319
|
+
case import_types22.Provenance.OBSERVED:
|
|
5149
5320
|
cur.observed = e;
|
|
5150
5321
|
break;
|
|
5151
|
-
case
|
|
5322
|
+
case import_types22.Provenance.INFERRED:
|
|
5152
5323
|
cur.inferred = e;
|
|
5153
5324
|
break;
|
|
5154
5325
|
default:
|
|
5155
|
-
if (e.provenance ===
|
|
5326
|
+
if (e.provenance === import_types22.Provenance.STALE) cur.stale = e;
|
|
5156
5327
|
}
|
|
5157
5328
|
buckets.set(key, cur);
|
|
5158
5329
|
});
|
|
@@ -5161,7 +5332,7 @@ function bucketEdges(graph) {
|
|
|
5161
5332
|
function nodeIsFrontier(graph, nodeId) {
|
|
5162
5333
|
if (!graph.hasNode(nodeId)) return false;
|
|
5163
5334
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
5164
|
-
return attrs.type ===
|
|
5335
|
+
return attrs.type === import_types22.NodeType.FrontierNode;
|
|
5165
5336
|
}
|
|
5166
5337
|
function clampConfidence(n) {
|
|
5167
5338
|
if (!Number.isFinite(n)) return 0;
|
|
@@ -5182,7 +5353,7 @@ function gradedConfidence(edge) {
|
|
|
5182
5353
|
}
|
|
5183
5354
|
function detectMissingDivergences(graph, bucket) {
|
|
5184
5355
|
const out = [];
|
|
5185
|
-
if (bucket.type ===
|
|
5356
|
+
if (bucket.type === import_types22.EdgeType.CONTAINS) return out;
|
|
5186
5357
|
if (bucket.extracted && !bucket.observed) {
|
|
5187
5358
|
if (!nodeIsFrontier(graph, bucket.target)) {
|
|
5188
5359
|
out.push({
|
|
@@ -5223,7 +5394,7 @@ function declaredHostFor(svc) {
|
|
|
5223
5394
|
function hasExtractedConfiguredBy(graph, svcId) {
|
|
5224
5395
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
5225
5396
|
const e = graph.getEdgeAttributes(edgeId);
|
|
5226
|
-
if (e.type ===
|
|
5397
|
+
if (e.type === import_types22.EdgeType.CONFIGURED_BY && e.provenance === import_types22.Provenance.EXTRACTED) {
|
|
5227
5398
|
return true;
|
|
5228
5399
|
}
|
|
5229
5400
|
}
|
|
@@ -5236,10 +5407,10 @@ function detectHostMismatch(graph, svcId, svc) {
|
|
|
5236
5407
|
const out = [];
|
|
5237
5408
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
5238
5409
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
5239
|
-
if (edge.type !==
|
|
5240
|
-
if (edge.provenance !==
|
|
5410
|
+
if (edge.type !== import_types22.EdgeType.CONNECTS_TO) continue;
|
|
5411
|
+
if (edge.provenance !== import_types22.Provenance.OBSERVED) continue;
|
|
5241
5412
|
const target = graph.getNodeAttributes(edge.target);
|
|
5242
|
-
if (target.type !==
|
|
5413
|
+
if (target.type !== import_types22.NodeType.DatabaseNode) continue;
|
|
5243
5414
|
const observedHost = target.host?.trim();
|
|
5244
5415
|
if (!observedHost) continue;
|
|
5245
5416
|
if (observedHost === declaredHost) continue;
|
|
@@ -5261,10 +5432,10 @@ function detectCompatDivergences(graph, svcId, svc) {
|
|
|
5261
5432
|
const deps = svc.dependencies ?? {};
|
|
5262
5433
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
5263
5434
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
5264
|
-
if (edge.type !==
|
|
5265
|
-
if (edge.provenance !==
|
|
5435
|
+
if (edge.type !== import_types22.EdgeType.CONNECTS_TO) continue;
|
|
5436
|
+
if (edge.provenance !== import_types22.Provenance.OBSERVED) continue;
|
|
5266
5437
|
const target = graph.getNodeAttributes(edge.target);
|
|
5267
|
-
if (target.type !==
|
|
5438
|
+
if (target.type !== import_types22.NodeType.DatabaseNode) continue;
|
|
5268
5439
|
for (const pair of compatPairs()) {
|
|
5269
5440
|
if (pair.engine !== target.engine) continue;
|
|
5270
5441
|
const declared = deps[pair.driver];
|
|
@@ -5325,7 +5496,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
5325
5496
|
}
|
|
5326
5497
|
graph.forEachNode((nodeId, attrs) => {
|
|
5327
5498
|
const n = attrs;
|
|
5328
|
-
if (n.type !==
|
|
5499
|
+
if (n.type !== import_types22.NodeType.ServiceNode) return;
|
|
5329
5500
|
const svc = n;
|
|
5330
5501
|
for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
|
|
5331
5502
|
for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
|
|
@@ -5358,7 +5529,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
5358
5529
|
if (a.source !== b.source) return a.source.localeCompare(b.source);
|
|
5359
5530
|
return a.target.localeCompare(b.target);
|
|
5360
5531
|
});
|
|
5361
|
-
return
|
|
5532
|
+
return import_types22.DivergenceResultSchema.parse({
|
|
5362
5533
|
divergences: filtered,
|
|
5363
5534
|
totalAffected: filtered.length,
|
|
5364
5535
|
computedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -5368,8 +5539,8 @@ function computeDivergences(graph, opts = {}) {
|
|
|
5368
5539
|
// src/persist.ts
|
|
5369
5540
|
init_cjs_shims();
|
|
5370
5541
|
var import_node_fs20 = require("fs");
|
|
5371
|
-
var
|
|
5372
|
-
var
|
|
5542
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
5543
|
+
var import_types23 = require("@neat.is/types");
|
|
5373
5544
|
var SCHEMA_VERSION = 4;
|
|
5374
5545
|
function migrateV1ToV2(payload) {
|
|
5375
5546
|
const nodes = payload.graph.nodes;
|
|
@@ -5391,12 +5562,12 @@ function migrateV2ToV3(payload) {
|
|
|
5391
5562
|
for (const edge of edges) {
|
|
5392
5563
|
const attrs = edge.attributes;
|
|
5393
5564
|
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
5394
|
-
attrs.provenance =
|
|
5565
|
+
attrs.provenance = import_types23.Provenance.OBSERVED;
|
|
5395
5566
|
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
5396
5567
|
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
5397
5568
|
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
5398
5569
|
if (type && source && target) {
|
|
5399
|
-
const newId = (0,
|
|
5570
|
+
const newId = (0, import_types23.observedEdgeId)(source, target, type);
|
|
5400
5571
|
attrs.id = newId;
|
|
5401
5572
|
if (edge.key) edge.key = newId;
|
|
5402
5573
|
}
|
|
@@ -5405,7 +5576,7 @@ function migrateV2ToV3(payload) {
|
|
|
5405
5576
|
return { ...payload, schemaVersion: 3 };
|
|
5406
5577
|
}
|
|
5407
5578
|
async function ensureDir(filePath) {
|
|
5408
|
-
await import_node_fs20.promises.mkdir(
|
|
5579
|
+
await import_node_fs20.promises.mkdir(import_node_path35.default.dirname(filePath), { recursive: true });
|
|
5409
5580
|
}
|
|
5410
5581
|
async function saveGraphToDisk(graph, outPath) {
|
|
5411
5582
|
await ensureDir(outPath);
|
|
@@ -5481,7 +5652,7 @@ function startPersistLoop(graph, outPath, intervalMs = 6e4) {
|
|
|
5481
5652
|
// src/gitignore.ts
|
|
5482
5653
|
init_cjs_shims();
|
|
5483
5654
|
var import_node_fs21 = require("fs");
|
|
5484
|
-
var
|
|
5655
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
5485
5656
|
var NEAT_OUT_LINE = "neat-out/";
|
|
5486
5657
|
var NEAT_HEADER = "# NEAT \u2014 machine-local snapshots and events";
|
|
5487
5658
|
function isNeatOutLine(line) {
|
|
@@ -5489,7 +5660,7 @@ function isNeatOutLine(line) {
|
|
|
5489
5660
|
return trimmed === "neat-out/" || trimmed === "neat-out";
|
|
5490
5661
|
}
|
|
5491
5662
|
async function ensureNeatOutIgnored(projectDir) {
|
|
5492
|
-
const file =
|
|
5663
|
+
const file = import_node_path36.default.join(projectDir, ".gitignore");
|
|
5493
5664
|
let existing = null;
|
|
5494
5665
|
try {
|
|
5495
5666
|
existing = await import_node_fs21.promises.readFile(file, "utf8");
|
|
@@ -5516,7 +5687,7 @@ ${NEAT_OUT_LINE}
|
|
|
5516
5687
|
|
|
5517
5688
|
// src/summary.ts
|
|
5518
5689
|
init_cjs_shims();
|
|
5519
|
-
var
|
|
5690
|
+
var import_types24 = require("@neat.is/types");
|
|
5520
5691
|
function renderOtelEnvBlock() {
|
|
5521
5692
|
return [
|
|
5522
5693
|
"for prod OTel routing, set these in your deploy platform's env:",
|
|
@@ -5526,19 +5697,19 @@ function renderOtelEnvBlock() {
|
|
|
5526
5697
|
}
|
|
5527
5698
|
function findIncompatServices(nodes) {
|
|
5528
5699
|
return nodes.filter(
|
|
5529
|
-
(n) => n.type ===
|
|
5700
|
+
(n) => n.type === import_types24.NodeType.ServiceNode && Array.isArray(n.incompatibilities) && (n.incompatibilities ?? []).length > 0
|
|
5530
5701
|
);
|
|
5531
5702
|
}
|
|
5532
5703
|
function servicesWithoutObserved(nodes, edges) {
|
|
5533
5704
|
const seen = /* @__PURE__ */ new Set();
|
|
5534
5705
|
for (const e of edges) {
|
|
5535
|
-
if (e.provenance ===
|
|
5706
|
+
if (e.provenance === import_types24.Provenance.OBSERVED) {
|
|
5536
5707
|
seen.add(e.source);
|
|
5537
5708
|
seen.add(e.target);
|
|
5538
5709
|
}
|
|
5539
5710
|
}
|
|
5540
5711
|
return nodes.filter(
|
|
5541
|
-
(n) => n.type ===
|
|
5712
|
+
(n) => n.type === import_types24.NodeType.ServiceNode && !seen.has(n.id)
|
|
5542
5713
|
);
|
|
5543
5714
|
}
|
|
5544
5715
|
function formatDivergence(d) {
|
|
@@ -5613,26 +5784,26 @@ function formatIncompat(inc) {
|
|
|
5613
5784
|
// src/watch.ts
|
|
5614
5785
|
init_cjs_shims();
|
|
5615
5786
|
var import_node_fs27 = __toESM(require("fs"), 1);
|
|
5616
|
-
var
|
|
5787
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
5617
5788
|
var import_chokidar = __toESM(require("chokidar"), 1);
|
|
5618
5789
|
|
|
5619
5790
|
// src/api.ts
|
|
5620
5791
|
init_cjs_shims();
|
|
5621
5792
|
var import_fastify = __toESM(require("fastify"), 1);
|
|
5622
5793
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
5623
|
-
var
|
|
5794
|
+
var import_types26 = require("@neat.is/types");
|
|
5624
5795
|
|
|
5625
5796
|
// src/extend/index.ts
|
|
5626
5797
|
init_cjs_shims();
|
|
5627
5798
|
var import_node_fs23 = require("fs");
|
|
5628
|
-
var
|
|
5799
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
5629
5800
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
5630
5801
|
var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
|
|
5631
5802
|
|
|
5632
5803
|
// src/installers/package-manager.ts
|
|
5633
5804
|
init_cjs_shims();
|
|
5634
5805
|
var import_node_fs22 = require("fs");
|
|
5635
|
-
var
|
|
5806
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
5636
5807
|
var import_node_child_process = require("child_process");
|
|
5637
5808
|
var LOCKFILE_PRIORITY = [
|
|
5638
5809
|
{ lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
|
|
@@ -5654,22 +5825,22 @@ async function exists2(p) {
|
|
|
5654
5825
|
}
|
|
5655
5826
|
}
|
|
5656
5827
|
async function detectPackageManager(serviceDir) {
|
|
5657
|
-
let dir =
|
|
5828
|
+
let dir = import_node_path37.default.resolve(serviceDir);
|
|
5658
5829
|
const stops = /* @__PURE__ */ new Set();
|
|
5659
5830
|
for (let i = 0; i < 64; i++) {
|
|
5660
5831
|
if (stops.has(dir)) break;
|
|
5661
5832
|
stops.add(dir);
|
|
5662
5833
|
for (const candidate of LOCKFILE_PRIORITY) {
|
|
5663
|
-
const lockPath =
|
|
5834
|
+
const lockPath = import_node_path37.default.join(dir, candidate.lockfile);
|
|
5664
5835
|
if (await exists2(lockPath)) {
|
|
5665
5836
|
return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
|
|
5666
5837
|
}
|
|
5667
5838
|
}
|
|
5668
|
-
const parent =
|
|
5839
|
+
const parent = import_node_path37.default.dirname(dir);
|
|
5669
5840
|
if (parent === dir) break;
|
|
5670
5841
|
dir = parent;
|
|
5671
5842
|
}
|
|
5672
|
-
return { pm: "npm", cwd:
|
|
5843
|
+
return { pm: "npm", cwd: import_node_path37.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
|
|
5673
5844
|
}
|
|
5674
5845
|
async function runPackageManagerInstall(cmd) {
|
|
5675
5846
|
return new Promise((resolve) => {
|
|
@@ -5718,7 +5889,7 @@ async function fileExists2(p) {
|
|
|
5718
5889
|
}
|
|
5719
5890
|
}
|
|
5720
5891
|
async function readPackageJson(scanPath) {
|
|
5721
|
-
const pkgPath =
|
|
5892
|
+
const pkgPath = import_node_path38.default.join(scanPath, "package.json");
|
|
5722
5893
|
const raw = await import_node_fs23.promises.readFile(pkgPath, "utf8");
|
|
5723
5894
|
return JSON.parse(raw);
|
|
5724
5895
|
}
|
|
@@ -5729,11 +5900,11 @@ async function findHookFiles(scanPath) {
|
|
|
5729
5900
|
).sort();
|
|
5730
5901
|
}
|
|
5731
5902
|
function extendLogPath() {
|
|
5732
|
-
return process.env.NEAT_EXTEND_LOG ??
|
|
5903
|
+
return process.env.NEAT_EXTEND_LOG ?? import_node_path38.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
|
|
5733
5904
|
}
|
|
5734
5905
|
async function appendExtendLog(entry2) {
|
|
5735
5906
|
const logPath = extendLogPath();
|
|
5736
|
-
await import_node_fs23.promises.mkdir(
|
|
5907
|
+
await import_node_fs23.promises.mkdir(import_node_path38.default.dirname(logPath), { recursive: true });
|
|
5737
5908
|
await import_node_fs23.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
|
|
5738
5909
|
}
|
|
5739
5910
|
function splicedContent(fileContent, snippet2) {
|
|
@@ -5792,7 +5963,7 @@ function lookupInstrumentation(library, installedVersion) {
|
|
|
5792
5963
|
}
|
|
5793
5964
|
async function describeProjectInstrumentation(ctx) {
|
|
5794
5965
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
5795
|
-
const envNeat = await fileExists2(
|
|
5966
|
+
const envNeat = await fileExists2(import_node_path38.default.join(ctx.scanPath, ".env.neat"));
|
|
5796
5967
|
const registryInstrPackages = new Set(
|
|
5797
5968
|
(0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
|
|
5798
5969
|
);
|
|
@@ -5814,16 +5985,16 @@ async function applyExtension(ctx, args, options) {
|
|
|
5814
5985
|
);
|
|
5815
5986
|
}
|
|
5816
5987
|
for (const file of hookFiles) {
|
|
5817
|
-
const content = await import_node_fs23.promises.readFile(
|
|
5988
|
+
const content = await import_node_fs23.promises.readFile(import_node_path38.default.join(ctx.scanPath, file), "utf8");
|
|
5818
5989
|
if (content.includes(args.registration_snippet)) {
|
|
5819
5990
|
return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
|
|
5820
5991
|
}
|
|
5821
5992
|
}
|
|
5822
5993
|
const primaryFile = hookFiles[0];
|
|
5823
|
-
const primaryPath =
|
|
5994
|
+
const primaryPath = import_node_path38.default.join(ctx.scanPath, primaryFile);
|
|
5824
5995
|
const filesTouched = [];
|
|
5825
5996
|
const depsAdded = [];
|
|
5826
|
-
const pkgPath =
|
|
5997
|
+
const pkgPath = import_node_path38.default.join(ctx.scanPath, "package.json");
|
|
5827
5998
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
5828
5999
|
if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
|
|
5829
6000
|
pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
|
|
@@ -5869,7 +6040,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
5869
6040
|
};
|
|
5870
6041
|
}
|
|
5871
6042
|
for (const file of hookFiles) {
|
|
5872
|
-
const content = await import_node_fs23.promises.readFile(
|
|
6043
|
+
const content = await import_node_fs23.promises.readFile(import_node_path38.default.join(ctx.scanPath, file), "utf8");
|
|
5873
6044
|
if (content.includes(args.registration_snippet)) {
|
|
5874
6045
|
return {
|
|
5875
6046
|
library: args.library,
|
|
@@ -5891,7 +6062,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
5891
6062
|
depsToAdd.push(`${args.instrumentation_package}@${args.version}`);
|
|
5892
6063
|
filesTouched.push("package.json");
|
|
5893
6064
|
}
|
|
5894
|
-
const hookContent = await import_node_fs23.promises.readFile(
|
|
6065
|
+
const hookContent = await import_node_fs23.promises.readFile(import_node_path38.default.join(ctx.scanPath, primaryFile), "utf8");
|
|
5895
6066
|
const patched = splicedContent(hookContent, args.registration_snippet);
|
|
5896
6067
|
if (patched) {
|
|
5897
6068
|
filesTouched.push(primaryFile);
|
|
@@ -5912,7 +6083,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
5912
6083
|
if (!match) {
|
|
5913
6084
|
return { undone: false, message: "no apply found for library" };
|
|
5914
6085
|
}
|
|
5915
|
-
const pkgPath =
|
|
6086
|
+
const pkgPath = import_node_path38.default.join(ctx.scanPath, "package.json");
|
|
5916
6087
|
if (await fileExists2(pkgPath)) {
|
|
5917
6088
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
5918
6089
|
if (pkg.dependencies?.[match.instrumentation_package]) {
|
|
@@ -5923,7 +6094,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
5923
6094
|
}
|
|
5924
6095
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
5925
6096
|
for (const file of hookFiles) {
|
|
5926
|
-
const filePath =
|
|
6097
|
+
const filePath = import_node_path38.default.join(ctx.scanPath, file);
|
|
5927
6098
|
const content = await import_node_fs23.promises.readFile(filePath, "utf8");
|
|
5928
6099
|
if (content.includes(match.registration_snippet)) {
|
|
5929
6100
|
const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
|
|
@@ -6016,23 +6187,23 @@ function canonicalJson(value) {
|
|
|
6016
6187
|
|
|
6017
6188
|
// src/projects.ts
|
|
6018
6189
|
init_cjs_shims();
|
|
6019
|
-
var
|
|
6190
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
6020
6191
|
function pathsForProject(project, baseDir) {
|
|
6021
6192
|
if (project === DEFAULT_PROJECT) {
|
|
6022
6193
|
return {
|
|
6023
|
-
snapshotPath:
|
|
6024
|
-
errorsPath:
|
|
6025
|
-
staleEventsPath:
|
|
6026
|
-
embeddingsCachePath:
|
|
6027
|
-
policyViolationsPath:
|
|
6194
|
+
snapshotPath: import_node_path39.default.join(baseDir, "graph.json"),
|
|
6195
|
+
errorsPath: import_node_path39.default.join(baseDir, "errors.ndjson"),
|
|
6196
|
+
staleEventsPath: import_node_path39.default.join(baseDir, "stale-events.ndjson"),
|
|
6197
|
+
embeddingsCachePath: import_node_path39.default.join(baseDir, "embeddings.json"),
|
|
6198
|
+
policyViolationsPath: import_node_path39.default.join(baseDir, "policy-violations.ndjson")
|
|
6028
6199
|
};
|
|
6029
6200
|
}
|
|
6030
6201
|
return {
|
|
6031
|
-
snapshotPath:
|
|
6032
|
-
errorsPath:
|
|
6033
|
-
staleEventsPath:
|
|
6034
|
-
embeddingsCachePath:
|
|
6035
|
-
policyViolationsPath:
|
|
6202
|
+
snapshotPath: import_node_path39.default.join(baseDir, `${project}.json`),
|
|
6203
|
+
errorsPath: import_node_path39.default.join(baseDir, `errors.${project}.ndjson`),
|
|
6204
|
+
staleEventsPath: import_node_path39.default.join(baseDir, `stale-events.${project}.ndjson`),
|
|
6205
|
+
embeddingsCachePath: import_node_path39.default.join(baseDir, `embeddings.${project}.json`),
|
|
6206
|
+
policyViolationsPath: import_node_path39.default.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
6036
6207
|
};
|
|
6037
6208
|
}
|
|
6038
6209
|
var Projects = class {
|
|
@@ -6070,23 +6241,23 @@ var Projects = class {
|
|
|
6070
6241
|
init_cjs_shims();
|
|
6071
6242
|
var import_node_fs25 = require("fs");
|
|
6072
6243
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
6073
|
-
var
|
|
6074
|
-
var
|
|
6244
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
6245
|
+
var import_types25 = require("@neat.is/types");
|
|
6075
6246
|
var LOCK_TIMEOUT_MS = 5e3;
|
|
6076
6247
|
var LOCK_RETRY_MS = 50;
|
|
6077
6248
|
function neatHome() {
|
|
6078
6249
|
const override = process.env.NEAT_HOME;
|
|
6079
|
-
if (override && override.length > 0) return
|
|
6080
|
-
return
|
|
6250
|
+
if (override && override.length > 0) return import_node_path40.default.resolve(override);
|
|
6251
|
+
return import_node_path40.default.join(import_node_os3.default.homedir(), ".neat");
|
|
6081
6252
|
}
|
|
6082
6253
|
function registryPath() {
|
|
6083
|
-
return
|
|
6254
|
+
return import_node_path40.default.join(neatHome(), "projects.json");
|
|
6084
6255
|
}
|
|
6085
6256
|
function registryLockPath() {
|
|
6086
|
-
return
|
|
6257
|
+
return import_node_path40.default.join(neatHome(), "projects.json.lock");
|
|
6087
6258
|
}
|
|
6088
6259
|
function daemonPidPath() {
|
|
6089
|
-
return
|
|
6260
|
+
return import_node_path40.default.join(neatHome(), "neatd.pid");
|
|
6090
6261
|
}
|
|
6091
6262
|
function isPidAliveDefault(pid) {
|
|
6092
6263
|
try {
|
|
@@ -6146,7 +6317,7 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
|
|
|
6146
6317
|
}
|
|
6147
6318
|
}
|
|
6148
6319
|
async function normalizeProjectPath(input) {
|
|
6149
|
-
const resolved =
|
|
6320
|
+
const resolved = import_node_path40.default.resolve(input);
|
|
6150
6321
|
try {
|
|
6151
6322
|
return await import_node_fs25.promises.realpath(resolved);
|
|
6152
6323
|
} catch {
|
|
@@ -6154,7 +6325,7 @@ async function normalizeProjectPath(input) {
|
|
|
6154
6325
|
}
|
|
6155
6326
|
}
|
|
6156
6327
|
async function writeAtomically(target, contents) {
|
|
6157
|
-
await import_node_fs25.promises.mkdir(
|
|
6328
|
+
await import_node_fs25.promises.mkdir(import_node_path40.default.dirname(target), { recursive: true });
|
|
6158
6329
|
const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
6159
6330
|
const fd = await import_node_fs25.promises.open(tmp, "w");
|
|
6160
6331
|
try {
|
|
@@ -6167,7 +6338,7 @@ async function writeAtomically(target, contents) {
|
|
|
6167
6338
|
}
|
|
6168
6339
|
async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
|
|
6169
6340
|
const deadline = Date.now() + timeoutMs;
|
|
6170
|
-
await import_node_fs25.promises.mkdir(
|
|
6341
|
+
await import_node_fs25.promises.mkdir(import_node_path40.default.dirname(lockPath), { recursive: true });
|
|
6171
6342
|
let probedHolder = false;
|
|
6172
6343
|
while (true) {
|
|
6173
6344
|
try {
|
|
@@ -6220,10 +6391,10 @@ async function readRegistry() {
|
|
|
6220
6391
|
throw err;
|
|
6221
6392
|
}
|
|
6222
6393
|
const parsed = JSON.parse(raw);
|
|
6223
|
-
return
|
|
6394
|
+
return import_types25.RegistryFileSchema.parse(parsed);
|
|
6224
6395
|
}
|
|
6225
6396
|
async function writeRegistry(reg) {
|
|
6226
|
-
const validated =
|
|
6397
|
+
const validated = import_types25.RegistryFileSchema.parse(reg);
|
|
6227
6398
|
await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
|
|
6228
6399
|
}
|
|
6229
6400
|
var ProjectNameCollisionError = class extends Error {
|
|
@@ -6294,6 +6465,58 @@ async function removeProject(name) {
|
|
|
6294
6465
|
return removed;
|
|
6295
6466
|
});
|
|
6296
6467
|
}
|
|
6468
|
+
var DAY_MS2 = 24 * 60 * 60 * 1e3;
|
|
6469
|
+
var DEFAULT_PRUNE_TTL_MS = 7 * DAY_MS2;
|
|
6470
|
+
function pruneTtlMs() {
|
|
6471
|
+
const raw = process.env.NEAT_REGISTRY_PRUNE_TTL_MS;
|
|
6472
|
+
if (!raw) return DEFAULT_PRUNE_TTL_MS;
|
|
6473
|
+
const n = Number.parseInt(raw, 10);
|
|
6474
|
+
if (Number.isFinite(n) && n >= 0) return n;
|
|
6475
|
+
console.warn(
|
|
6476
|
+
`[neat] NEAT_REGISTRY_PRUNE_TTL_MS could not be parsed (${raw}); using default ${DEFAULT_PRUNE_TTL_MS}ms`
|
|
6477
|
+
);
|
|
6478
|
+
return DEFAULT_PRUNE_TTL_MS;
|
|
6479
|
+
}
|
|
6480
|
+
async function statPathStatus(p) {
|
|
6481
|
+
try {
|
|
6482
|
+
const stat = await import_node_fs25.promises.stat(p);
|
|
6483
|
+
return stat.isDirectory() ? "present" : "unknown";
|
|
6484
|
+
} catch (err) {
|
|
6485
|
+
return err.code === "ENOENT" ? "gone" : "unknown";
|
|
6486
|
+
}
|
|
6487
|
+
}
|
|
6488
|
+
async function pruneRegistry(opts = {}) {
|
|
6489
|
+
const ttlMs = opts.ttlMs ?? pruneTtlMs();
|
|
6490
|
+
const statPath = opts.statPath ?? statPathStatus;
|
|
6491
|
+
const now = opts.now ?? Date.now;
|
|
6492
|
+
return withLock(async () => {
|
|
6493
|
+
const reg = await readRegistry();
|
|
6494
|
+
const removed = [];
|
|
6495
|
+
const kept = [];
|
|
6496
|
+
for (const entry2 of reg.projects) {
|
|
6497
|
+
const status2 = await statPath(entry2.path);
|
|
6498
|
+
if (status2 !== "gone") {
|
|
6499
|
+
kept.push(entry2);
|
|
6500
|
+
continue;
|
|
6501
|
+
}
|
|
6502
|
+
if (ttlMs <= 0) {
|
|
6503
|
+
removed.push(entry2);
|
|
6504
|
+
continue;
|
|
6505
|
+
}
|
|
6506
|
+
const lastSeen = Date.parse(entry2.lastSeenAt ?? entry2.registeredAt);
|
|
6507
|
+
const age = now() - (Number.isFinite(lastSeen) ? lastSeen : 0);
|
|
6508
|
+
if (age > ttlMs) {
|
|
6509
|
+
removed.push(entry2);
|
|
6510
|
+
} else {
|
|
6511
|
+
kept.push(entry2);
|
|
6512
|
+
}
|
|
6513
|
+
}
|
|
6514
|
+
if (removed.length === 0) return [];
|
|
6515
|
+
reg.projects = kept;
|
|
6516
|
+
await writeRegistry(reg);
|
|
6517
|
+
return removed;
|
|
6518
|
+
});
|
|
6519
|
+
}
|
|
6297
6520
|
|
|
6298
6521
|
// src/streaming.ts
|
|
6299
6522
|
init_cjs_shims();
|
|
@@ -6307,6 +6530,7 @@ function handleSse(req, reply, opts) {
|
|
|
6307
6530
|
reply.raw.setHeader("Connection", "keep-alive");
|
|
6308
6531
|
reply.raw.setHeader("X-Accel-Buffering", "no");
|
|
6309
6532
|
reply.raw.flushHeaders?.();
|
|
6533
|
+
reply.raw.write(":open\n\n");
|
|
6310
6534
|
let pending = 0;
|
|
6311
6535
|
let dropped = false;
|
|
6312
6536
|
const closeConnection = () => {
|
|
@@ -6486,11 +6710,11 @@ function registerRoutes(scope, ctx) {
|
|
|
6486
6710
|
const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
6487
6711
|
const parsed = [];
|
|
6488
6712
|
for (const c of candidates) {
|
|
6489
|
-
const r =
|
|
6713
|
+
const r = import_types26.DivergenceTypeSchema.safeParse(c);
|
|
6490
6714
|
if (!r.success) {
|
|
6491
6715
|
return reply.code(400).send({
|
|
6492
6716
|
error: `unknown divergence type "${c}"`,
|
|
6493
|
-
allowed:
|
|
6717
|
+
allowed: import_types26.DivergenceTypeSchema.options
|
|
6494
6718
|
});
|
|
6495
6719
|
}
|
|
6496
6720
|
parsed.push(r.data);
|
|
@@ -6703,7 +6927,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6703
6927
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
6704
6928
|
let violations = await log.readAll();
|
|
6705
6929
|
if (req.query.severity) {
|
|
6706
|
-
const sev =
|
|
6930
|
+
const sev = import_types26.PolicySeveritySchema.safeParse(req.query.severity);
|
|
6707
6931
|
if (!sev.success) {
|
|
6708
6932
|
return reply.code(400).send({
|
|
6709
6933
|
error: "invalid severity",
|
|
@@ -6720,7 +6944,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6720
6944
|
scope.post("/policies/check", async (req, reply) => {
|
|
6721
6945
|
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6722
6946
|
if (!proj) return;
|
|
6723
|
-
const parsed =
|
|
6947
|
+
const parsed = import_types26.PoliciesCheckBodySchema.safeParse(req.body ?? {});
|
|
6724
6948
|
if (!parsed.success) {
|
|
6725
6949
|
return reply.code(400).send({
|
|
6726
6950
|
error: "invalid /policies/check body",
|
|
@@ -6964,7 +7188,7 @@ init_otel_grpc();
|
|
|
6964
7188
|
// src/search.ts
|
|
6965
7189
|
init_cjs_shims();
|
|
6966
7190
|
var import_node_fs26 = require("fs");
|
|
6967
|
-
var
|
|
7191
|
+
var import_node_path43 = __toESM(require("path"), 1);
|
|
6968
7192
|
var import_node_crypto3 = require("crypto");
|
|
6969
7193
|
var DEFAULT_LIMIT = 10;
|
|
6970
7194
|
var NOMIC_DIM = 768;
|
|
@@ -7103,7 +7327,7 @@ async function readCache(cachePath) {
|
|
|
7103
7327
|
}
|
|
7104
7328
|
}
|
|
7105
7329
|
async function writeCache(cachePath, cache) {
|
|
7106
|
-
await import_node_fs26.promises.mkdir(
|
|
7330
|
+
await import_node_fs26.promises.mkdir(import_node_path43.default.dirname(cachePath), { recursive: true });
|
|
7107
7331
|
await import_node_fs26.promises.writeFile(cachePath, JSON.stringify(cache));
|
|
7108
7332
|
}
|
|
7109
7333
|
var VectorIndex = class {
|
|
@@ -7260,8 +7484,8 @@ var ALL_PHASES = [
|
|
|
7260
7484
|
];
|
|
7261
7485
|
function classifyChange(relPath) {
|
|
7262
7486
|
const phases = /* @__PURE__ */ new Set();
|
|
7263
|
-
const base =
|
|
7264
|
-
const segments = relPath.split(
|
|
7487
|
+
const base = import_node_path44.default.basename(relPath).toLowerCase();
|
|
7488
|
+
const segments = relPath.split(import_node_path44.default.sep).map((s) => s.toLowerCase());
|
|
7265
7489
|
if (base === "package.json" || base === "requirements.txt" || base === "pyproject.toml" || base === "setup.py") {
|
|
7266
7490
|
phases.add("services");
|
|
7267
7491
|
phases.add("aliases");
|
|
@@ -7382,9 +7606,9 @@ function countWatchableDirs(scanPath, limit) {
|
|
|
7382
7606
|
for (const e of entries) {
|
|
7383
7607
|
if (count >= limit) return;
|
|
7384
7608
|
if (!e.isDirectory()) continue;
|
|
7385
|
-
if (IGNORED_WATCH_PATHS.some((re) => re.test(
|
|
7609
|
+
if (IGNORED_WATCH_PATHS.some((re) => re.test(import_node_path44.default.join(dir, e.name) + import_node_path44.default.sep))) continue;
|
|
7386
7610
|
count++;
|
|
7387
|
-
if (depth < 2) visit(
|
|
7611
|
+
if (depth < 2) visit(import_node_path44.default.join(dir, e.name), depth + 1);
|
|
7388
7612
|
}
|
|
7389
7613
|
};
|
|
7390
7614
|
visit(scanPath, 0);
|
|
@@ -7402,8 +7626,8 @@ async function startWatch(graph, opts) {
|
|
|
7402
7626
|
const projectName = opts.project ?? DEFAULT_PROJECT;
|
|
7403
7627
|
await loadGraphFromDisk(graph, opts.outPath);
|
|
7404
7628
|
const detachEventBus = attachGraphToEventBus(graph, { project: projectName });
|
|
7405
|
-
const policyFilePath =
|
|
7406
|
-
const policyViolationsPath =
|
|
7629
|
+
const policyFilePath = import_node_path44.default.join(opts.scanPath, "policy.json");
|
|
7630
|
+
const policyViolationsPath = import_node_path44.default.join(import_node_path44.default.dirname(opts.outPath), "policy-violations.ndjson");
|
|
7407
7631
|
let policies = [];
|
|
7408
7632
|
try {
|
|
7409
7633
|
policies = await loadPolicyFile(policyFilePath);
|
|
@@ -7454,7 +7678,7 @@ async function startWatch(graph, opts) {
|
|
|
7454
7678
|
assertBindAuthority(host, auth.authToken);
|
|
7455
7679
|
const port = opts.port ?? 8080;
|
|
7456
7680
|
const otelPort = opts.otelPort ?? 4318;
|
|
7457
|
-
const cachePath = opts.embeddingsCachePath ??
|
|
7681
|
+
const cachePath = opts.embeddingsCachePath ?? import_node_path44.default.join(import_node_path44.default.dirname(opts.outPath), "embeddings.json");
|
|
7458
7682
|
let searchIndex;
|
|
7459
7683
|
try {
|
|
7460
7684
|
searchIndex = await buildSearchIndex(graph, { cachePath });
|
|
@@ -7472,7 +7696,7 @@ async function startWatch(graph, opts) {
|
|
|
7472
7696
|
// Paths are derived from the explicit options the watch caller passes
|
|
7473
7697
|
// — pathsForProject is only used to fill in the embeddings/snapshot
|
|
7474
7698
|
// fields so the registry shape is complete.
|
|
7475
|
-
...pathsForProject(projectName,
|
|
7699
|
+
...pathsForProject(projectName, import_node_path44.default.dirname(opts.outPath)),
|
|
7476
7700
|
snapshotPath: opts.outPath,
|
|
7477
7701
|
errorsPath: opts.errorsPath,
|
|
7478
7702
|
staleEventsPath: opts.staleEventsPath
|
|
@@ -7559,9 +7783,9 @@ async function startWatch(graph, opts) {
|
|
|
7559
7783
|
};
|
|
7560
7784
|
const onPath = (absPath) => {
|
|
7561
7785
|
if (shouldIgnore(absPath)) return;
|
|
7562
|
-
const rel =
|
|
7786
|
+
const rel = import_node_path44.default.relative(opts.scanPath, absPath);
|
|
7563
7787
|
if (!rel || rel.startsWith("..")) return;
|
|
7564
|
-
pendingPaths.add(rel.split(
|
|
7788
|
+
pendingPaths.add(rel.split(import_node_path44.default.sep).join("/"));
|
|
7565
7789
|
const phases = classifyChange(rel);
|
|
7566
7790
|
if (phases.size === 0) {
|
|
7567
7791
|
for (const p of ALL_PHASES) pending.add(p);
|
|
@@ -7616,7 +7840,7 @@ async function startWatch(graph, opts) {
|
|
|
7616
7840
|
// src/deploy/detect.ts
|
|
7617
7841
|
init_cjs_shims();
|
|
7618
7842
|
var import_node_fs28 = require("fs");
|
|
7619
|
-
var
|
|
7843
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
7620
7844
|
var import_node_child_process2 = require("child_process");
|
|
7621
7845
|
var import_node_crypto4 = require("crypto");
|
|
7622
7846
|
function generateToken() {
|
|
@@ -7716,7 +7940,7 @@ async function runDeploy(opts = {}) {
|
|
|
7716
7940
|
const token = generateToken();
|
|
7717
7941
|
switch (substrate) {
|
|
7718
7942
|
case "docker-compose": {
|
|
7719
|
-
const artifactPath =
|
|
7943
|
+
const artifactPath = import_node_path45.default.join(cwd, "docker-compose.neat.yml");
|
|
7720
7944
|
const contents = emitDockerCompose(cwd);
|
|
7721
7945
|
await import_node_fs28.promises.writeFile(artifactPath, contents, "utf8");
|
|
7722
7946
|
return {
|
|
@@ -7724,11 +7948,11 @@ async function runDeploy(opts = {}) {
|
|
|
7724
7948
|
artifactPath,
|
|
7725
7949
|
token,
|
|
7726
7950
|
contents,
|
|
7727
|
-
startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${
|
|
7951
|
+
startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${import_node_path45.default.basename(artifactPath)} up -d`
|
|
7728
7952
|
};
|
|
7729
7953
|
}
|
|
7730
7954
|
case "systemd": {
|
|
7731
|
-
const artifactPath =
|
|
7955
|
+
const artifactPath = import_node_path45.default.join(cwd, "neat.service");
|
|
7732
7956
|
const contents = emitSystemdUnit(cwd);
|
|
7733
7957
|
await import_node_fs28.promises.writeFile(artifactPath, contents, "utf8");
|
|
7734
7958
|
return {
|
|
@@ -7764,7 +7988,7 @@ init_cjs_shims();
|
|
|
7764
7988
|
// src/installers/javascript.ts
|
|
7765
7989
|
init_cjs_shims();
|
|
7766
7990
|
var import_node_fs29 = require("fs");
|
|
7767
|
-
var
|
|
7991
|
+
var import_node_path46 = __toESM(require("path"), 1);
|
|
7768
7992
|
var import_semver2 = __toESM(require("semver"), 1);
|
|
7769
7993
|
|
|
7770
7994
|
// src/installers/templates.ts
|
|
@@ -8311,11 +8535,11 @@ var OTEL_ENV = {
|
|
|
8311
8535
|
value: "http://localhost:4318/projects/<project>/v1/traces"
|
|
8312
8536
|
};
|
|
8313
8537
|
function serviceNodeName(pkg, serviceDir) {
|
|
8314
|
-
return pkg.name ??
|
|
8538
|
+
return pkg.name ?? import_node_path46.default.basename(serviceDir);
|
|
8315
8539
|
}
|
|
8316
8540
|
function projectToken(pkg, serviceDir, project) {
|
|
8317
8541
|
if (project && project.length > 0) return project;
|
|
8318
|
-
return pkg.name ??
|
|
8542
|
+
return pkg.name ?? import_node_path46.default.basename(serviceDir);
|
|
8319
8543
|
}
|
|
8320
8544
|
async function readJsonFile(p) {
|
|
8321
8545
|
try {
|
|
@@ -8328,16 +8552,16 @@ async function readJsonFile(p) {
|
|
|
8328
8552
|
async function detectRuntimeKind(pkgRoot, pkg) {
|
|
8329
8553
|
const deps = allDeps(pkg);
|
|
8330
8554
|
if ("react-native" in deps || "expo" in deps) return "react-native";
|
|
8331
|
-
const appJson = await readJsonFile(
|
|
8555
|
+
const appJson = await readJsonFile(import_node_path46.default.join(pkgRoot, "app.json"));
|
|
8332
8556
|
if (appJson && typeof appJson === "object" && "expo" in appJson) {
|
|
8333
8557
|
return "react-native";
|
|
8334
8558
|
}
|
|
8335
|
-
if (await exists3(
|
|
8559
|
+
if (await exists3(import_node_path46.default.join(pkgRoot, "vite.config.js")) || await exists3(import_node_path46.default.join(pkgRoot, "vite.config.ts")) || await exists3(import_node_path46.default.join(pkgRoot, "vite.config.mjs")) || "vite" in deps) {
|
|
8336
8560
|
return "browser-bundle";
|
|
8337
8561
|
}
|
|
8338
|
-
if (await exists3(
|
|
8339
|
-
if (await exists3(
|
|
8340
|
-
if (await exists3(
|
|
8562
|
+
if (await exists3(import_node_path46.default.join(pkgRoot, "wrangler.toml"))) return "cloudflare-workers";
|
|
8563
|
+
if (await exists3(import_node_path46.default.join(pkgRoot, "bun.lockb"))) return "bun";
|
|
8564
|
+
if (await exists3(import_node_path46.default.join(pkgRoot, "deno.json")) || await exists3(import_node_path46.default.join(pkgRoot, "deno.lock"))) {
|
|
8341
8565
|
return "deno";
|
|
8342
8566
|
}
|
|
8343
8567
|
const engines = pkg.engines ?? {};
|
|
@@ -8346,7 +8570,7 @@ async function detectRuntimeKind(pkgRoot, pkg) {
|
|
|
8346
8570
|
}
|
|
8347
8571
|
async function readPackageJson2(serviceDir) {
|
|
8348
8572
|
try {
|
|
8349
|
-
const raw = await import_node_fs29.promises.readFile(
|
|
8573
|
+
const raw = await import_node_fs29.promises.readFile(import_node_path46.default.join(serviceDir, "package.json"), "utf8");
|
|
8350
8574
|
return JSON.parse(raw);
|
|
8351
8575
|
} catch {
|
|
8352
8576
|
return null;
|
|
@@ -8390,7 +8614,7 @@ function needsVersionUpgrade(installed, expected) {
|
|
|
8390
8614
|
var NEXT_CONFIG_CANDIDATES = ["next.config.js", "next.config.ts", "next.config.mjs"];
|
|
8391
8615
|
async function findNextConfig(serviceDir) {
|
|
8392
8616
|
for (const name of NEXT_CONFIG_CANDIDATES) {
|
|
8393
|
-
const candidate =
|
|
8617
|
+
const candidate = import_node_path46.default.join(serviceDir, name);
|
|
8394
8618
|
if (await exists3(candidate)) return candidate;
|
|
8395
8619
|
}
|
|
8396
8620
|
return null;
|
|
@@ -8417,7 +8641,7 @@ function hasRemixDependency(pkg) {
|
|
|
8417
8641
|
}
|
|
8418
8642
|
async function findRemixEntry(serviceDir) {
|
|
8419
8643
|
for (const rel of REMIX_ENTRY_CANDIDATES) {
|
|
8420
|
-
const candidate =
|
|
8644
|
+
const candidate = import_node_path46.default.join(serviceDir, rel);
|
|
8421
8645
|
if (await exists3(candidate)) return candidate;
|
|
8422
8646
|
}
|
|
8423
8647
|
return null;
|
|
@@ -8429,14 +8653,14 @@ function hasSvelteKitDependency(pkg) {
|
|
|
8429
8653
|
}
|
|
8430
8654
|
async function findSvelteKitHooks(serviceDir) {
|
|
8431
8655
|
for (const rel of SVELTEKIT_HOOKS_CANDIDATES) {
|
|
8432
|
-
const candidate =
|
|
8656
|
+
const candidate = import_node_path46.default.join(serviceDir, rel);
|
|
8433
8657
|
if (await exists3(candidate)) return candidate;
|
|
8434
8658
|
}
|
|
8435
8659
|
return null;
|
|
8436
8660
|
}
|
|
8437
8661
|
async function findSvelteKitConfig(serviceDir) {
|
|
8438
8662
|
for (const rel of SVELTEKIT_CONFIG_CANDIDATES) {
|
|
8439
|
-
const candidate =
|
|
8663
|
+
const candidate = import_node_path46.default.join(serviceDir, rel);
|
|
8440
8664
|
if (await exists3(candidate)) return candidate;
|
|
8441
8665
|
}
|
|
8442
8666
|
return null;
|
|
@@ -8447,7 +8671,7 @@ function hasNuxtDependency(pkg) {
|
|
|
8447
8671
|
}
|
|
8448
8672
|
async function findNuxtConfig(serviceDir) {
|
|
8449
8673
|
for (const name of NUXT_CONFIG_CANDIDATES) {
|
|
8450
|
-
const candidate =
|
|
8674
|
+
const candidate = import_node_path46.default.join(serviceDir, name);
|
|
8451
8675
|
if (await exists3(candidate)) return candidate;
|
|
8452
8676
|
}
|
|
8453
8677
|
return null;
|
|
@@ -8458,7 +8682,7 @@ function hasAstroDependency(pkg) {
|
|
|
8458
8682
|
}
|
|
8459
8683
|
async function findAstroConfig(serviceDir) {
|
|
8460
8684
|
for (const name of ASTRO_CONFIG_CANDIDATES) {
|
|
8461
|
-
const candidate =
|
|
8685
|
+
const candidate = import_node_path46.default.join(serviceDir, name);
|
|
8462
8686
|
if (await exists3(candidate)) return candidate;
|
|
8463
8687
|
}
|
|
8464
8688
|
return null;
|
|
@@ -8472,7 +8696,7 @@ function parseNextMajor(range) {
|
|
|
8472
8696
|
return Number.isFinite(n) ? n : null;
|
|
8473
8697
|
}
|
|
8474
8698
|
async function isTypeScriptProject(serviceDir) {
|
|
8475
|
-
return exists3(
|
|
8699
|
+
return exists3(import_node_path46.default.join(serviceDir, "tsconfig.json"));
|
|
8476
8700
|
}
|
|
8477
8701
|
var INDEX_EXTENSIONS = [".ts", ".tsx", ".js", ".mjs", ".cjs"];
|
|
8478
8702
|
var INDEX_CANDIDATES = INDEX_EXTENSIONS.map((ext) => `index${ext}`);
|
|
@@ -8518,7 +8742,7 @@ function entryFromScript(script) {
|
|
|
8518
8742
|
}
|
|
8519
8743
|
async function resolveEntry(serviceDir, pkg) {
|
|
8520
8744
|
if (typeof pkg.main === "string" && pkg.main.length > 0) {
|
|
8521
|
-
const candidate =
|
|
8745
|
+
const candidate = import_node_path46.default.resolve(serviceDir, pkg.main);
|
|
8522
8746
|
if (await exists3(candidate)) return candidate;
|
|
8523
8747
|
}
|
|
8524
8748
|
if (pkg.bin) {
|
|
@@ -8532,36 +8756,36 @@ async function resolveEntry(serviceDir, pkg) {
|
|
|
8532
8756
|
if (typeof first === "string") binEntry = first;
|
|
8533
8757
|
}
|
|
8534
8758
|
if (binEntry) {
|
|
8535
|
-
const candidate =
|
|
8759
|
+
const candidate = import_node_path46.default.resolve(serviceDir, binEntry);
|
|
8536
8760
|
if (await exists3(candidate)) return candidate;
|
|
8537
8761
|
}
|
|
8538
8762
|
}
|
|
8539
8763
|
const startEntry = entryFromScript(pkg.scripts?.start);
|
|
8540
8764
|
if (startEntry) {
|
|
8541
|
-
const candidate =
|
|
8765
|
+
const candidate = import_node_path46.default.resolve(serviceDir, startEntry);
|
|
8542
8766
|
if (await exists3(candidate)) return candidate;
|
|
8543
8767
|
}
|
|
8544
8768
|
const devEntry = entryFromScript(pkg.scripts?.dev);
|
|
8545
8769
|
if (devEntry) {
|
|
8546
|
-
const candidate =
|
|
8770
|
+
const candidate = import_node_path46.default.resolve(serviceDir, devEntry);
|
|
8547
8771
|
if (await exists3(candidate)) return candidate;
|
|
8548
8772
|
}
|
|
8549
8773
|
for (const rel of SRC_INDEX_CANDIDATES) {
|
|
8550
|
-
const candidate =
|
|
8774
|
+
const candidate = import_node_path46.default.join(serviceDir, rel);
|
|
8551
8775
|
if (await exists3(candidate)) return candidate;
|
|
8552
8776
|
}
|
|
8553
8777
|
for (const rel of SRC_NAMED_CANDIDATES) {
|
|
8554
|
-
const candidate =
|
|
8778
|
+
const candidate = import_node_path46.default.join(serviceDir, rel);
|
|
8555
8779
|
if (await exists3(candidate)) return candidate;
|
|
8556
8780
|
}
|
|
8557
8781
|
for (const name of INDEX_CANDIDATES) {
|
|
8558
|
-
const candidate =
|
|
8782
|
+
const candidate = import_node_path46.default.join(serviceDir, name);
|
|
8559
8783
|
if (await exists3(candidate)) return candidate;
|
|
8560
8784
|
}
|
|
8561
8785
|
return null;
|
|
8562
8786
|
}
|
|
8563
8787
|
function dispatchEntry(entryFile, pkg) {
|
|
8564
|
-
const ext =
|
|
8788
|
+
const ext = import_node_path46.default.extname(entryFile).toLowerCase();
|
|
8565
8789
|
if (ext === ".ts" || ext === ".tsx") return "ts";
|
|
8566
8790
|
if (ext === ".mjs") return "esm";
|
|
8567
8791
|
if (ext === ".cjs") return "cjs";
|
|
@@ -8578,9 +8802,9 @@ function otelInitContents(flavor) {
|
|
|
8578
8802
|
return OTEL_INIT_CJS;
|
|
8579
8803
|
}
|
|
8580
8804
|
function injectionLine(flavor, entryFile, otelInitFile) {
|
|
8581
|
-
let rel =
|
|
8805
|
+
let rel = import_node_path46.default.relative(import_node_path46.default.dirname(entryFile), otelInitFile);
|
|
8582
8806
|
if (!rel.startsWith(".")) rel = `./${rel}`;
|
|
8583
|
-
rel = rel.split(
|
|
8807
|
+
rel = rel.split(import_node_path46.default.sep).join("/");
|
|
8584
8808
|
if (flavor === "cjs") return `require('${rel}')`;
|
|
8585
8809
|
if (flavor === "esm") return `import '${rel}'`;
|
|
8586
8810
|
const tsRel = rel.replace(/\.ts$/, "");
|
|
@@ -8593,23 +8817,23 @@ function lineIsOtelInjection(line) {
|
|
|
8593
8817
|
}
|
|
8594
8818
|
async function detectsSrcLayout(serviceDir) {
|
|
8595
8819
|
const [hasSrcApp, hasSrcPages, hasRootApp, hasRootPages] = await Promise.all([
|
|
8596
|
-
exists3(
|
|
8597
|
-
exists3(
|
|
8598
|
-
exists3(
|
|
8599
|
-
exists3(
|
|
8820
|
+
exists3(import_node_path46.default.join(serviceDir, "src", "app")),
|
|
8821
|
+
exists3(import_node_path46.default.join(serviceDir, "src", "pages")),
|
|
8822
|
+
exists3(import_node_path46.default.join(serviceDir, "app")),
|
|
8823
|
+
exists3(import_node_path46.default.join(serviceDir, "pages"))
|
|
8600
8824
|
]);
|
|
8601
8825
|
return (hasSrcApp || hasSrcPages) && !hasRootApp && !hasRootPages;
|
|
8602
8826
|
}
|
|
8603
8827
|
async function planNext(serviceDir, pkg, manifestPath, nextConfigPath, project) {
|
|
8604
8828
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
8605
8829
|
const srcLayout = await detectsSrcLayout(serviceDir);
|
|
8606
|
-
const baseDir = srcLayout ?
|
|
8607
|
-
const instrumentationFile =
|
|
8608
|
-
const instrumentationNodeFile =
|
|
8830
|
+
const baseDir = srcLayout ? import_node_path46.default.join(serviceDir, "src") : serviceDir;
|
|
8831
|
+
const instrumentationFile = import_node_path46.default.join(baseDir, useTs ? "instrumentation.ts" : "instrumentation.js");
|
|
8832
|
+
const instrumentationNodeFile = import_node_path46.default.join(
|
|
8609
8833
|
baseDir,
|
|
8610
8834
|
useTs ? "instrumentation.node.ts" : "instrumentation.node.js"
|
|
8611
8835
|
);
|
|
8612
|
-
const envNeatFile =
|
|
8836
|
+
const envNeatFile = import_node_path46.default.join(baseDir, ".env.neat");
|
|
8613
8837
|
const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
8614
8838
|
const dependencyEdits = [];
|
|
8615
8839
|
for (const sdk of SDK_PACKAGES) {
|
|
@@ -8714,7 +8938,7 @@ function buildDependencyEdits(pkg, manifestPath) {
|
|
|
8714
8938
|
return edits;
|
|
8715
8939
|
}
|
|
8716
8940
|
async function queueEnvNeat(serviceDir, pkg, project, generatedFiles) {
|
|
8717
|
-
const envNeatFile =
|
|
8941
|
+
const envNeatFile = import_node_path46.default.join(serviceDir, ".env.neat");
|
|
8718
8942
|
if (!await exists3(envNeatFile)) {
|
|
8719
8943
|
generatedFiles.push({
|
|
8720
8944
|
file: envNeatFile,
|
|
@@ -8749,7 +8973,7 @@ function fileImportsOtelHook(raw, specifiers) {
|
|
|
8749
8973
|
}
|
|
8750
8974
|
async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
|
|
8751
8975
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
8752
|
-
const otelServerFile =
|
|
8976
|
+
const otelServerFile = import_node_path46.default.join(
|
|
8753
8977
|
serviceDir,
|
|
8754
8978
|
useTs ? "app/otel.server.ts" : "app/otel.server.js"
|
|
8755
8979
|
);
|
|
@@ -8806,11 +9030,11 @@ async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
|
|
|
8806
9030
|
}
|
|
8807
9031
|
async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project) {
|
|
8808
9032
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
8809
|
-
const otelInitFile =
|
|
9033
|
+
const otelInitFile = import_node_path46.default.join(
|
|
8810
9034
|
serviceDir,
|
|
8811
9035
|
useTs ? "src/otel-init.ts" : "src/otel-init.js"
|
|
8812
9036
|
);
|
|
8813
|
-
const resolvedHooksFile = hooksFile ??
|
|
9037
|
+
const resolvedHooksFile = hooksFile ?? import_node_path46.default.join(serviceDir, useTs ? "src/hooks.server.ts" : "src/hooks.server.js");
|
|
8814
9038
|
const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
|
|
8815
9039
|
const generatedFiles = [];
|
|
8816
9040
|
const entrypointEdits = [];
|
|
@@ -8872,11 +9096,11 @@ async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project)
|
|
|
8872
9096
|
}
|
|
8873
9097
|
async function planNuxt(serviceDir, pkg, manifestPath, project) {
|
|
8874
9098
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
8875
|
-
const otelPluginFile =
|
|
9099
|
+
const otelPluginFile = import_node_path46.default.join(
|
|
8876
9100
|
serviceDir,
|
|
8877
9101
|
useTs ? "server/plugins/otel.ts" : "server/plugins/otel.js"
|
|
8878
9102
|
);
|
|
8879
|
-
const otelInitFile =
|
|
9103
|
+
const otelInitFile = import_node_path46.default.join(
|
|
8880
9104
|
serviceDir,
|
|
8881
9105
|
useTs ? "server/plugins/otel-init.ts" : "server/plugins/otel-init.js"
|
|
8882
9106
|
);
|
|
@@ -8927,19 +9151,19 @@ async function planNuxt(serviceDir, pkg, manifestPath, project) {
|
|
|
8927
9151
|
var ASTRO_MIDDLEWARE_CANDIDATES = ["src/middleware.ts", "src/middleware.js"];
|
|
8928
9152
|
async function findAstroMiddleware(serviceDir) {
|
|
8929
9153
|
for (const rel of ASTRO_MIDDLEWARE_CANDIDATES) {
|
|
8930
|
-
const candidate =
|
|
9154
|
+
const candidate = import_node_path46.default.join(serviceDir, rel);
|
|
8931
9155
|
if (await exists3(candidate)) return candidate;
|
|
8932
9156
|
}
|
|
8933
9157
|
return null;
|
|
8934
9158
|
}
|
|
8935
9159
|
async function planAstro(serviceDir, pkg, manifestPath, project) {
|
|
8936
9160
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
8937
|
-
const otelInitFile =
|
|
9161
|
+
const otelInitFile = import_node_path46.default.join(
|
|
8938
9162
|
serviceDir,
|
|
8939
9163
|
useTs ? "src/otel-init.ts" : "src/otel-init.js"
|
|
8940
9164
|
);
|
|
8941
9165
|
const existingMiddleware = await findAstroMiddleware(serviceDir);
|
|
8942
|
-
const middlewareFile = existingMiddleware ??
|
|
9166
|
+
const middlewareFile = existingMiddleware ?? import_node_path46.default.join(serviceDir, useTs ? "src/middleware.ts" : "src/middleware.js");
|
|
8943
9167
|
const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
|
|
8944
9168
|
const generatedFiles = [];
|
|
8945
9169
|
const entrypointEdits = [];
|
|
@@ -9035,7 +9259,7 @@ async function findFrameworkDispatch(serviceDir, pkg, manifestPath, project) {
|
|
|
9035
9259
|
}
|
|
9036
9260
|
async function plan(serviceDir, opts) {
|
|
9037
9261
|
const pkg = await readPackageJson2(serviceDir);
|
|
9038
|
-
const manifestPath =
|
|
9262
|
+
const manifestPath = import_node_path46.default.join(serviceDir, "package.json");
|
|
9039
9263
|
const project = opts?.project;
|
|
9040
9264
|
const empty = {
|
|
9041
9265
|
language: "javascript",
|
|
@@ -9070,8 +9294,8 @@ async function plan(serviceDir, opts) {
|
|
|
9070
9294
|
return { ...empty, libOnly: true };
|
|
9071
9295
|
}
|
|
9072
9296
|
const flavor = dispatchEntry(entryFile, pkg);
|
|
9073
|
-
const otelInitFile =
|
|
9074
|
-
const envNeatFile =
|
|
9297
|
+
const otelInitFile = import_node_path46.default.join(import_node_path46.default.dirname(entryFile), otelInitFilename(flavor));
|
|
9298
|
+
const envNeatFile = import_node_path46.default.join(serviceDir, ".env.neat");
|
|
9075
9299
|
const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
9076
9300
|
const dependencyEdits = [];
|
|
9077
9301
|
for (const sdk of SDK_PACKAGES) {
|
|
@@ -9140,13 +9364,13 @@ async function plan(serviceDir, opts) {
|
|
|
9140
9364
|
};
|
|
9141
9365
|
}
|
|
9142
9366
|
function isAllowedWritePath(serviceDir, target) {
|
|
9143
|
-
const rel =
|
|
9367
|
+
const rel = import_node_path46.default.relative(serviceDir, target);
|
|
9144
9368
|
if (rel.startsWith("..")) return false;
|
|
9145
|
-
const base =
|
|
9369
|
+
const base = import_node_path46.default.basename(target);
|
|
9146
9370
|
if (base === "package.json") return true;
|
|
9147
9371
|
if (base === ".env.neat") return true;
|
|
9148
9372
|
if (/^otel-init\.(?:js|cjs|mjs|ts)$/.test(base)) return true;
|
|
9149
|
-
const relPosix = rel.split(
|
|
9373
|
+
const relPosix = rel.split(import_node_path46.default.sep).join("/");
|
|
9150
9374
|
if (/^instrumentation(?:\.node)?\.(?:js|cjs|mjs|ts)$/.test(base)) {
|
|
9151
9375
|
if (relPosix === base) return true;
|
|
9152
9376
|
if (relPosix === `src/${base}`) return true;
|
|
@@ -9163,7 +9387,7 @@ function isAllowedWritePath(serviceDir, target) {
|
|
|
9163
9387
|
return false;
|
|
9164
9388
|
}
|
|
9165
9389
|
async function writeAtomic(file, contents) {
|
|
9166
|
-
await import_node_fs29.promises.mkdir(
|
|
9390
|
+
await import_node_fs29.promises.mkdir(import_node_path46.default.dirname(file), { recursive: true });
|
|
9167
9391
|
const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
|
|
9168
9392
|
await import_node_fs29.promises.writeFile(tmp, contents, "utf8");
|
|
9169
9393
|
await import_node_fs29.promises.rename(tmp, file);
|
|
@@ -9347,7 +9571,7 @@ async function rollback(installPlan, originals, createdFiles) {
|
|
|
9347
9571
|
...removed.map((f) => `removed: ${f}`),
|
|
9348
9572
|
""
|
|
9349
9573
|
];
|
|
9350
|
-
const rollbackPath =
|
|
9574
|
+
const rollbackPath = import_node_path46.default.join(installPlan.serviceDir, "neat-rollback.patch");
|
|
9351
9575
|
await import_node_fs29.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
|
|
9352
9576
|
}
|
|
9353
9577
|
function injectInstrumentationHook(raw) {
|
|
@@ -9378,7 +9602,7 @@ var javascriptInstaller = {
|
|
|
9378
9602
|
// src/installers/python.ts
|
|
9379
9603
|
init_cjs_shims();
|
|
9380
9604
|
var import_node_fs30 = require("fs");
|
|
9381
|
-
var
|
|
9605
|
+
var import_node_path47 = __toESM(require("path"), 1);
|
|
9382
9606
|
var SDK_PACKAGES2 = [
|
|
9383
9607
|
{ name: "opentelemetry-distro", version: ">=0.49b0" },
|
|
9384
9608
|
{ name: "opentelemetry-exporter-otlp", version: ">=1.28.0" }
|
|
@@ -9399,7 +9623,7 @@ async function exists4(p) {
|
|
|
9399
9623
|
async function detect2(serviceDir) {
|
|
9400
9624
|
const markers = ["requirements.txt", "pyproject.toml", "setup.py"];
|
|
9401
9625
|
for (const m of markers) {
|
|
9402
|
-
if (await exists4(
|
|
9626
|
+
if (await exists4(import_node_path47.default.join(serviceDir, m))) return true;
|
|
9403
9627
|
}
|
|
9404
9628
|
return false;
|
|
9405
9629
|
}
|
|
@@ -9409,7 +9633,7 @@ function reqPackageName(line) {
|
|
|
9409
9633
|
return head.replace(/[<>=!~].*$/, "").toLowerCase();
|
|
9410
9634
|
}
|
|
9411
9635
|
async function planRequirementsTxtEdits(serviceDir) {
|
|
9412
|
-
const file =
|
|
9636
|
+
const file = import_node_path47.default.join(serviceDir, "requirements.txt");
|
|
9413
9637
|
if (!await exists4(file)) return null;
|
|
9414
9638
|
const raw = await import_node_fs30.promises.readFile(file, "utf8");
|
|
9415
9639
|
const presentNames = new Set(
|
|
@@ -9419,7 +9643,7 @@ async function planRequirementsTxtEdits(serviceDir) {
|
|
|
9419
9643
|
return { manifest: file, missing: [...missing] };
|
|
9420
9644
|
}
|
|
9421
9645
|
async function planProcfileEdits(serviceDir) {
|
|
9422
|
-
const procfile =
|
|
9646
|
+
const procfile = import_node_path47.default.join(serviceDir, "Procfile");
|
|
9423
9647
|
if (!await exists4(procfile)) return [];
|
|
9424
9648
|
const raw = await import_node_fs30.promises.readFile(procfile, "utf8");
|
|
9425
9649
|
const edits = [];
|
|
@@ -9508,7 +9732,7 @@ async function apply2(installPlan) {
|
|
|
9508
9732
|
if (raw === void 0) {
|
|
9509
9733
|
throw new Error(`python installer: cannot read ${file} during apply`);
|
|
9510
9734
|
}
|
|
9511
|
-
const base =
|
|
9735
|
+
const base = import_node_path47.default.basename(file);
|
|
9512
9736
|
if (base === "requirements.txt") {
|
|
9513
9737
|
const edits = installPlan.dependencyEdits.filter((e) => e.file === file);
|
|
9514
9738
|
if (edits.length > 0) {
|
|
@@ -9547,7 +9771,7 @@ async function rollback2(installPlan, originals) {
|
|
|
9547
9771
|
...restored.map((f) => `restored: ${f}`),
|
|
9548
9772
|
""
|
|
9549
9773
|
];
|
|
9550
|
-
const rollbackPath =
|
|
9774
|
+
const rollbackPath = import_node_path47.default.join(installPlan.serviceDir, "neat-rollback.patch");
|
|
9551
9775
|
await import_node_fs30.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
|
|
9552
9776
|
}
|
|
9553
9777
|
var pythonInstaller = {
|
|
@@ -9674,7 +9898,7 @@ init_cjs_shims();
|
|
|
9674
9898
|
var import_node_fs31 = require("fs");
|
|
9675
9899
|
var import_node_http = __toESM(require("http"), 1);
|
|
9676
9900
|
var import_node_net = __toESM(require("net"), 1);
|
|
9677
|
-
var
|
|
9901
|
+
var import_node_path48 = __toESM(require("path"), 1);
|
|
9678
9902
|
var import_node_child_process3 = require("child_process");
|
|
9679
9903
|
var import_node_readline = __toESM(require("readline"), 1);
|
|
9680
9904
|
async function extractAndPersist(opts) {
|
|
@@ -9683,7 +9907,7 @@ async function extractAndPersist(opts) {
|
|
|
9683
9907
|
const graphKey = opts.projectExplicit ? opts.project : DEFAULT_PROJECT;
|
|
9684
9908
|
resetGraph(graphKey);
|
|
9685
9909
|
const graph = getGraph(graphKey);
|
|
9686
|
-
const projectPaths = pathsForProject(graphKey,
|
|
9910
|
+
const projectPaths = pathsForProject(graphKey, import_node_path48.default.join(opts.scanPath, "neat-out"));
|
|
9687
9911
|
const extraction = await extractFromDirectory(graph, opts.scanPath, {
|
|
9688
9912
|
errorsPath: projectPaths.errorsPath
|
|
9689
9913
|
});
|
|
@@ -9737,7 +9961,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
9737
9961
|
console.log(`skipping ${svc.dir}: browser bundle; browser-OTel support lands in a future release.`);
|
|
9738
9962
|
} else if (outcome.outcome === "react-native") {
|
|
9739
9963
|
reactNative++;
|
|
9740
|
-
const svcName =
|
|
9964
|
+
const svcName = import_node_path48.default.basename(svc.dir);
|
|
9741
9965
|
console.log(
|
|
9742
9966
|
`neat: ${svc.dir} detected as React Native / Expo
|
|
9743
9967
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -9748,7 +9972,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
9748
9972
|
);
|
|
9749
9973
|
} else if (outcome.outcome === "bun") {
|
|
9750
9974
|
bun++;
|
|
9751
|
-
const svcName =
|
|
9975
|
+
const svcName = import_node_path48.default.basename(svc.dir);
|
|
9752
9976
|
console.log(
|
|
9753
9977
|
`neat: ${svc.dir} detected as Bun
|
|
9754
9978
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -9759,7 +9983,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
9759
9983
|
);
|
|
9760
9984
|
} else if (outcome.outcome === "deno") {
|
|
9761
9985
|
deno++;
|
|
9762
|
-
const svcName =
|
|
9986
|
+
const svcName = import_node_path48.default.basename(svc.dir);
|
|
9763
9987
|
console.log(
|
|
9764
9988
|
`neat: ${svc.dir} detected as Deno
|
|
9765
9989
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -9770,7 +9994,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
9770
9994
|
);
|
|
9771
9995
|
} else if (outcome.outcome === "cloudflare-workers") {
|
|
9772
9996
|
cloudflareWorkers++;
|
|
9773
|
-
const svcName =
|
|
9997
|
+
const svcName = import_node_path48.default.basename(svc.dir);
|
|
9774
9998
|
console.log(
|
|
9775
9999
|
`neat: ${svc.dir} detected as Cloudflare Workers
|
|
9776
10000
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -9781,7 +10005,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
9781
10005
|
);
|
|
9782
10006
|
} else if (outcome.outcome === "electron") {
|
|
9783
10007
|
electron++;
|
|
9784
|
-
const svcName =
|
|
10008
|
+
const svcName = import_node_path48.default.basename(svc.dir);
|
|
9785
10009
|
console.log(
|
|
9786
10010
|
`neat: ${svc.dir} detected as Electron
|
|
9787
10011
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -9955,10 +10179,10 @@ function formatPortCollisionMessage(port) {
|
|
|
9955
10179
|
];
|
|
9956
10180
|
}
|
|
9957
10181
|
function spawnDaemonDetached() {
|
|
9958
|
-
const here =
|
|
10182
|
+
const here = import_node_path48.default.dirname(new URL(importMetaUrl).pathname);
|
|
9959
10183
|
const candidates = [
|
|
9960
|
-
|
|
9961
|
-
|
|
10184
|
+
import_node_path48.default.join(here, "neatd.cjs"),
|
|
10185
|
+
import_node_path48.default.join(here, "neatd.js")
|
|
9962
10186
|
];
|
|
9963
10187
|
let entry2 = null;
|
|
9964
10188
|
const fsSync = require("fs");
|
|
@@ -10173,11 +10397,11 @@ function printSummary(result, graph, dashboardUrl) {
|
|
|
10173
10397
|
|
|
10174
10398
|
// src/cli-verbs.ts
|
|
10175
10399
|
init_cjs_shims();
|
|
10176
|
-
var
|
|
10400
|
+
var import_node_path49 = __toESM(require("path"), 1);
|
|
10177
10401
|
|
|
10178
10402
|
// src/cli-client.ts
|
|
10179
10403
|
init_cjs_shims();
|
|
10180
|
-
var
|
|
10404
|
+
var import_types27 = require("@neat.is/types");
|
|
10181
10405
|
var HttpError = class extends Error {
|
|
10182
10406
|
constructor(status2, message, responseBody = "") {
|
|
10183
10407
|
super(message);
|
|
@@ -10202,10 +10426,10 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
10202
10426
|
const root = baseUrl.replace(/\/$/, "");
|
|
10203
10427
|
const authHeader = bearerToken && bearerToken.length > 0 ? { authorization: `Bearer ${bearerToken}` } : {};
|
|
10204
10428
|
return {
|
|
10205
|
-
async get(
|
|
10429
|
+
async get(path51) {
|
|
10206
10430
|
let res;
|
|
10207
10431
|
try {
|
|
10208
|
-
res = await fetch(`${root}${
|
|
10432
|
+
res = await fetch(`${root}${path51}`, {
|
|
10209
10433
|
headers: { ...authHeader }
|
|
10210
10434
|
});
|
|
10211
10435
|
} catch (err) {
|
|
@@ -10217,16 +10441,16 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
10217
10441
|
const body = await res.text().catch(() => "");
|
|
10218
10442
|
throw new HttpError(
|
|
10219
10443
|
res.status,
|
|
10220
|
-
`${res.status} ${res.statusText} on GET ${
|
|
10444
|
+
`${res.status} ${res.statusText} on GET ${path51}: ${body}`,
|
|
10221
10445
|
body
|
|
10222
10446
|
);
|
|
10223
10447
|
}
|
|
10224
10448
|
return await res.json();
|
|
10225
10449
|
},
|
|
10226
|
-
async post(
|
|
10450
|
+
async post(path51, body) {
|
|
10227
10451
|
let res;
|
|
10228
10452
|
try {
|
|
10229
|
-
res = await fetch(`${root}${
|
|
10453
|
+
res = await fetch(`${root}${path51}`, {
|
|
10230
10454
|
method: "POST",
|
|
10231
10455
|
headers: { "content-type": "application/json", ...authHeader },
|
|
10232
10456
|
body: JSON.stringify(body)
|
|
@@ -10240,7 +10464,7 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
10240
10464
|
const text = await res.text().catch(() => "");
|
|
10241
10465
|
throw new HttpError(
|
|
10242
10466
|
res.status,
|
|
10243
|
-
`${res.status} ${res.statusText} on POST ${
|
|
10467
|
+
`${res.status} ${res.statusText} on POST ${path51}: ${text}`,
|
|
10244
10468
|
text
|
|
10245
10469
|
);
|
|
10246
10470
|
}
|
|
@@ -10254,12 +10478,12 @@ function projectPath(project, suffix) {
|
|
|
10254
10478
|
}
|
|
10255
10479
|
async function runRootCause(client, input) {
|
|
10256
10480
|
const qs = input.errorId ? `?errorId=${encodeURIComponent(input.errorId)}` : "";
|
|
10257
|
-
const
|
|
10481
|
+
const path51 = projectPath(
|
|
10258
10482
|
input.project,
|
|
10259
10483
|
`/graph/root-cause/${encodeURIComponent(input.errorNode)}${qs}`
|
|
10260
10484
|
);
|
|
10261
10485
|
try {
|
|
10262
|
-
const result = await client.get(
|
|
10486
|
+
const result = await client.get(path51);
|
|
10263
10487
|
const arrowPath = result.traversalPath.join(" \u2190 ");
|
|
10264
10488
|
const provenances = result.edgeProvenances.length ? result.edgeProvenances.join(", ") : "(direct, no edges traversed)";
|
|
10265
10489
|
const summary = `Root cause for ${input.errorNode} is ${result.rootCauseNode}. ` + result.rootCauseReason + (result.fixRecommendation ? ` Recommended fix: ${result.fixRecommendation}.` : "");
|
|
@@ -10285,12 +10509,12 @@ async function runRootCause(client, input) {
|
|
|
10285
10509
|
}
|
|
10286
10510
|
async function runBlastRadius(client, input) {
|
|
10287
10511
|
const qs = input.depth !== void 0 ? `?depth=${input.depth}` : "";
|
|
10288
|
-
const
|
|
10512
|
+
const path51 = projectPath(
|
|
10289
10513
|
input.project,
|
|
10290
10514
|
`/graph/blast-radius/${encodeURIComponent(input.nodeId)}${qs}`
|
|
10291
10515
|
);
|
|
10292
10516
|
try {
|
|
10293
|
-
const result = await client.get(
|
|
10517
|
+
const result = await client.get(path51);
|
|
10294
10518
|
if (result.totalAffected === 0) {
|
|
10295
10519
|
return {
|
|
10296
10520
|
summary: `${result.origin} has no downstream dependencies. Nothing else would break if it failed.`
|
|
@@ -10319,17 +10543,17 @@ async function runBlastRadius(client, input) {
|
|
|
10319
10543
|
}
|
|
10320
10544
|
}
|
|
10321
10545
|
function formatBlastEntry(n) {
|
|
10322
|
-
const tag = n.edgeProvenance ===
|
|
10546
|
+
const tag = n.edgeProvenance === import_types27.Provenance.STALE ? " [STALE \u2014 last seen too long ago]" : "";
|
|
10323
10547
|
return ` \u2022 ${n.nodeId} (distance ${n.distance}, ${n.edgeProvenance})${tag}`;
|
|
10324
10548
|
}
|
|
10325
10549
|
async function runDependencies(client, input) {
|
|
10326
10550
|
const depth = input.depth ?? 3;
|
|
10327
|
-
const
|
|
10551
|
+
const path51 = projectPath(
|
|
10328
10552
|
input.project,
|
|
10329
10553
|
`/graph/dependencies/${encodeURIComponent(input.nodeId)}?depth=${depth}`
|
|
10330
10554
|
);
|
|
10331
10555
|
try {
|
|
10332
|
-
const result = await client.get(
|
|
10556
|
+
const result = await client.get(path51);
|
|
10333
10557
|
if (result.total === 0) {
|
|
10334
10558
|
return {
|
|
10335
10559
|
summary: depth === 1 ? `${input.nodeId} has no direct dependencies in the graph.` : `${input.nodeId} has no dependencies (BFS to depth ${depth}).`
|
|
@@ -10365,9 +10589,9 @@ async function runObservedDependencies(client, input) {
|
|
|
10365
10589
|
const edges = await client.get(
|
|
10366
10590
|
projectPath(input.project, `/graph/edges/${encodeURIComponent(input.nodeId)}`)
|
|
10367
10591
|
);
|
|
10368
|
-
const observed = edges.outbound.filter((e) => e.provenance ===
|
|
10592
|
+
const observed = edges.outbound.filter((e) => e.provenance === import_types27.Provenance.OBSERVED);
|
|
10369
10593
|
if (observed.length === 0) {
|
|
10370
|
-
const hasExtracted = edges.outbound.some((e) => e.provenance ===
|
|
10594
|
+
const hasExtracted = edges.outbound.some((e) => e.provenance === import_types27.Provenance.EXTRACTED);
|
|
10371
10595
|
const note = hasExtracted ? " Static (EXTRACTED) dependencies exist but no runtime traffic has been seen \u2014 is OTel running?" : "";
|
|
10372
10596
|
return { summary: `No OBSERVED dependencies for ${input.nodeId}.${note}` };
|
|
10373
10597
|
}
|
|
@@ -10375,7 +10599,7 @@ async function runObservedDependencies(client, input) {
|
|
|
10375
10599
|
return {
|
|
10376
10600
|
summary: `${input.nodeId} has ${observed.length} runtime dependenc${observed.length === 1 ? "y" : "ies"} confirmed by OTel.`,
|
|
10377
10601
|
block: blockLines.join("\n"),
|
|
10378
|
-
provenance:
|
|
10602
|
+
provenance: import_types27.Provenance.OBSERVED
|
|
10379
10603
|
};
|
|
10380
10604
|
} catch (err) {
|
|
10381
10605
|
if (err instanceof HttpError && err.status === 404) {
|
|
@@ -10410,9 +10634,9 @@ function formatDuration(ms) {
|
|
|
10410
10634
|
return `${Math.round(h / 24)}d`;
|
|
10411
10635
|
}
|
|
10412
10636
|
async function runIncidents(client, input) {
|
|
10413
|
-
const
|
|
10637
|
+
const path51 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
|
|
10414
10638
|
try {
|
|
10415
|
-
const body = await client.get(
|
|
10639
|
+
const body = await client.get(path51);
|
|
10416
10640
|
const events = body.events;
|
|
10417
10641
|
if (events.length === 0) {
|
|
10418
10642
|
return {
|
|
@@ -10429,7 +10653,7 @@ async function runIncidents(client, input) {
|
|
|
10429
10653
|
return {
|
|
10430
10654
|
summary: `${target} has ${body.total} recorded incident${body.total === 1 ? "" : "s"}; showing the ${ordered.length} most recent.`,
|
|
10431
10655
|
block: blockLines.join("\n"),
|
|
10432
|
-
provenance:
|
|
10656
|
+
provenance: import_types27.Provenance.OBSERVED
|
|
10433
10657
|
};
|
|
10434
10658
|
} catch (err) {
|
|
10435
10659
|
if (err instanceof HttpError && err.status === 404) {
|
|
@@ -10538,7 +10762,7 @@ async function runStaleEdges(client, input) {
|
|
|
10538
10762
|
return {
|
|
10539
10763
|
summary: `${events.length} stale-edge transition${events.length === 1 ? "" : "s"} recorded${input.edgeType ? ` for ${input.edgeType}` : ""}.`,
|
|
10540
10764
|
block: blockLines.join("\n"),
|
|
10541
|
-
provenance:
|
|
10765
|
+
provenance: import_types27.Provenance.STALE
|
|
10542
10766
|
};
|
|
10543
10767
|
}
|
|
10544
10768
|
async function runPolicies(client, input) {
|
|
@@ -10702,7 +10926,7 @@ async function resolveProjectEntry(opts) {
|
|
|
10702
10926
|
const cwd = opts.cwd ?? process.cwd();
|
|
10703
10927
|
const resolvedCwd = await normalizeProjectPath(cwd);
|
|
10704
10928
|
for (const entry2 of entries) {
|
|
10705
|
-
if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${
|
|
10929
|
+
if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${import_node_path49.default.sep}`)) {
|
|
10706
10930
|
return entry2;
|
|
10707
10931
|
}
|
|
10708
10932
|
}
|
|
@@ -10852,7 +11076,7 @@ async function runSync(opts) {
|
|
|
10852
11076
|
}
|
|
10853
11077
|
|
|
10854
11078
|
// src/cli.ts
|
|
10855
|
-
var
|
|
11079
|
+
var import_types28 = require("@neat.is/types");
|
|
10856
11080
|
function isNpxInvocation() {
|
|
10857
11081
|
if (process.env.npm_command === "exec") return true;
|
|
10858
11082
|
const execpath = process.env.npm_execpath ?? "";
|
|
@@ -10889,6 +11113,8 @@ function usage() {
|
|
|
10889
11113
|
console.log(" uninstall <name>");
|
|
10890
11114
|
console.log(" Remove a project from the registry. Does not touch");
|
|
10891
11115
|
console.log(" neat-out/, policy.json, or any user file.");
|
|
11116
|
+
console.log(" prune Drop registry entries whose path is gone from disk.");
|
|
11117
|
+
console.log(" Flags: --json emit the removed list as JSON");
|
|
10892
11118
|
console.log(" version Print the installed @neat.is/core version and exit.");
|
|
10893
11119
|
console.log(" Aliases: --version, -v.");
|
|
10894
11120
|
console.log(" skill Install or print the Claude Code MCP drop-in.");
|
|
@@ -10948,6 +11174,7 @@ function usage() {
|
|
|
10948
11174
|
console.log("");
|
|
10949
11175
|
console.log("environment:");
|
|
10950
11176
|
console.log(" NEAT_API_URL base URL for the core REST API (default http://localhost:8080)");
|
|
11177
|
+
console.log(" alias: NEAT_CORE_URL (the name the MCP server reads)");
|
|
10951
11178
|
console.log(" NEAT_PROJECT project name when --project isn't passed");
|
|
10952
11179
|
}
|
|
10953
11180
|
var STRING_FLAGS = [
|
|
@@ -11128,12 +11355,12 @@ async function runInit(opts) {
|
|
|
11128
11355
|
printDiscoveryReport(opts, services);
|
|
11129
11356
|
const sections = opts.noInstall ? [] : await buildPatchSections(services, opts.project);
|
|
11130
11357
|
const patch = renderPatch(sections);
|
|
11131
|
-
const patchPath =
|
|
11358
|
+
const patchPath = import_node_path50.default.join(opts.scanPath, "neat.patch");
|
|
11132
11359
|
if (opts.dryRun) {
|
|
11133
11360
|
await import_node_fs32.promises.writeFile(patchPath, patch, "utf8");
|
|
11134
11361
|
written.push(patchPath);
|
|
11135
11362
|
console.log(`dry-run: patch written to ${patchPath}`);
|
|
11136
|
-
const gitignorePath =
|
|
11363
|
+
const gitignorePath = import_node_path50.default.join(opts.scanPath, ".gitignore");
|
|
11137
11364
|
const gitignoreExists = await import_node_fs32.promises.stat(gitignorePath).then(() => true).catch(() => false);
|
|
11138
11365
|
const verb = gitignoreExists ? "append" : "create";
|
|
11139
11366
|
console.log(`dry-run: would ${verb} ${gitignorePath} (add neat-out/)`);
|
|
@@ -11145,9 +11372,9 @@ async function runInit(opts) {
|
|
|
11145
11372
|
const graph = getGraph(graphKey);
|
|
11146
11373
|
const projectPaths = pathsForProject(
|
|
11147
11374
|
graphKey,
|
|
11148
|
-
|
|
11375
|
+
import_node_path50.default.join(opts.scanPath, "neat-out")
|
|
11149
11376
|
);
|
|
11150
|
-
const errorsPath =
|
|
11377
|
+
const errorsPath = import_node_path50.default.join(import_node_path50.default.dirname(opts.outPath), import_node_path50.default.basename(projectPaths.errorsPath));
|
|
11151
11378
|
const result = await extractFromDirectory(graph, opts.scanPath, { errorsPath });
|
|
11152
11379
|
await saveGraphToDisk(graph, opts.outPath);
|
|
11153
11380
|
written.push(opts.outPath);
|
|
@@ -11259,16 +11486,16 @@ var CLAUDE_SKILL_CONFIG = {
|
|
|
11259
11486
|
command: "npx",
|
|
11260
11487
|
args: ["-y", "@neat.is/mcp"],
|
|
11261
11488
|
env: {
|
|
11262
|
-
|
|
11489
|
+
NEAT_CORE_URL: "http://localhost:8080"
|
|
11263
11490
|
}
|
|
11264
11491
|
}
|
|
11265
11492
|
}
|
|
11266
11493
|
};
|
|
11267
11494
|
function claudeConfigPath() {
|
|
11268
11495
|
const override = process.env.NEAT_CLAUDE_CONFIG;
|
|
11269
|
-
if (override && override.length > 0) return
|
|
11496
|
+
if (override && override.length > 0) return import_node_path50.default.resolve(override);
|
|
11270
11497
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
11271
|
-
return
|
|
11498
|
+
return import_node_path50.default.join(home, ".claude.json");
|
|
11272
11499
|
}
|
|
11273
11500
|
async function runSkill(opts) {
|
|
11274
11501
|
const snippet2 = JSON.stringify(CLAUDE_SKILL_CONFIG, null, 2) + "\n";
|
|
@@ -11292,7 +11519,7 @@ async function runSkill(opts) {
|
|
|
11292
11519
|
...existing,
|
|
11293
11520
|
mcpServers: { ...mcp, neat: CLAUDE_SKILL_CONFIG.mcpServers.neat }
|
|
11294
11521
|
};
|
|
11295
|
-
await import_node_fs32.promises.mkdir(
|
|
11522
|
+
await import_node_fs32.promises.mkdir(import_node_path50.default.dirname(target), { recursive: true });
|
|
11296
11523
|
await import_node_fs32.promises.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
11297
11524
|
console.log(`neat skill: wrote mcpServers.neat to ${target}`);
|
|
11298
11525
|
console.log("restart Claude Code to pick up the new MCP server.");
|
|
@@ -11305,6 +11532,9 @@ async function runSkill(opts) {
|
|
|
11305
11532
|
console.log("");
|
|
11306
11533
|
console.log("Manual install: copy mcpServers.neat from --print-config into ~/.claude.json,");
|
|
11307
11534
|
console.log("then restart Claude Code. See packages/claude-skill/SKILL.md for the tool list.");
|
|
11535
|
+
console.log("");
|
|
11536
|
+
console.log("The MCP server reads NEAT_CORE_URL for the daemon URL \u2014 point it at a");
|
|
11537
|
+
console.log("non-default daemon by editing that value in the generated config.");
|
|
11308
11538
|
return { exitCode: 0 };
|
|
11309
11539
|
}
|
|
11310
11540
|
async function main() {
|
|
@@ -11339,12 +11569,12 @@ async function main() {
|
|
|
11339
11569
|
console.error("neat init: --apply and --dry-run are mutually exclusive");
|
|
11340
11570
|
process.exit(2);
|
|
11341
11571
|
}
|
|
11342
|
-
const scanPath =
|
|
11572
|
+
const scanPath = import_node_path50.default.resolve(target);
|
|
11343
11573
|
const projectExplicit = parsed.project !== null;
|
|
11344
|
-
const projectName = projectExplicit ? project :
|
|
11574
|
+
const projectName = projectExplicit ? project : import_node_path50.default.basename(scanPath);
|
|
11345
11575
|
const projectKey = projectExplicit ? project : DEFAULT_PROJECT;
|
|
11346
|
-
const fallback = pathsForProject(projectKey,
|
|
11347
|
-
const outPath =
|
|
11576
|
+
const fallback = pathsForProject(projectKey, import_node_path50.default.join(scanPath, "neat-out")).snapshotPath;
|
|
11577
|
+
const outPath = import_node_path50.default.resolve(process.env.NEAT_OUT_PATH ?? fallback);
|
|
11348
11578
|
const result = await runInit({
|
|
11349
11579
|
scanPath,
|
|
11350
11580
|
outPath,
|
|
@@ -11365,21 +11595,21 @@ async function main() {
|
|
|
11365
11595
|
usage();
|
|
11366
11596
|
process.exit(2);
|
|
11367
11597
|
}
|
|
11368
|
-
const scanPath =
|
|
11598
|
+
const scanPath = import_node_path50.default.resolve(target);
|
|
11369
11599
|
const stat = await import_node_fs32.promises.stat(scanPath).catch(() => null);
|
|
11370
11600
|
if (!stat || !stat.isDirectory()) {
|
|
11371
11601
|
console.error(`neat watch: ${scanPath} is not a directory`);
|
|
11372
11602
|
process.exit(2);
|
|
11373
11603
|
}
|
|
11374
|
-
const projectPaths = pathsForProject(project,
|
|
11375
|
-
const outPath =
|
|
11376
|
-
const errorsPath =
|
|
11377
|
-
process.env.NEAT_ERRORS_PATH ??
|
|
11604
|
+
const projectPaths = pathsForProject(project, import_node_path50.default.join(scanPath, "neat-out"));
|
|
11605
|
+
const outPath = import_node_path50.default.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
|
|
11606
|
+
const errorsPath = import_node_path50.default.resolve(
|
|
11607
|
+
process.env.NEAT_ERRORS_PATH ?? import_node_path50.default.join(import_node_path50.default.dirname(outPath), import_node_path50.default.basename(projectPaths.errorsPath))
|
|
11378
11608
|
);
|
|
11379
|
-
const staleEventsPath =
|
|
11380
|
-
process.env.NEAT_STALE_EVENTS_PATH ??
|
|
11609
|
+
const staleEventsPath = import_node_path50.default.resolve(
|
|
11610
|
+
process.env.NEAT_STALE_EVENTS_PATH ?? import_node_path50.default.join(import_node_path50.default.dirname(outPath), import_node_path50.default.basename(projectPaths.staleEventsPath))
|
|
11381
11611
|
);
|
|
11382
|
-
const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ?
|
|
11612
|
+
const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? import_node_path50.default.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
|
|
11383
11613
|
const handle = await startWatch(getGraph(project), {
|
|
11384
11614
|
scanPath,
|
|
11385
11615
|
outPath,
|
|
@@ -11472,6 +11702,19 @@ async function main() {
|
|
|
11472
11702
|
console.log("note: neat-out/, policy.json, and other files at the project path were left in place.");
|
|
11473
11703
|
return;
|
|
11474
11704
|
}
|
|
11705
|
+
if (cmd === "prune") {
|
|
11706
|
+
const removed = await pruneRegistry({ ttlMs: 0 });
|
|
11707
|
+
if (parsed.json) {
|
|
11708
|
+
console.log(JSON.stringify(removed.map((p) => ({ name: p.name, path: p.path })), null, 2));
|
|
11709
|
+
return;
|
|
11710
|
+
}
|
|
11711
|
+
if (removed.length === 0) {
|
|
11712
|
+
console.log("nothing to prune \u2014 every registered project path still exists.");
|
|
11713
|
+
return;
|
|
11714
|
+
}
|
|
11715
|
+
console.log(`pruned ${removed.length} project${removed.length === 1 ? "" : "s"}: ${removed.map((p) => p.name).join(", ")}`);
|
|
11716
|
+
return;
|
|
11717
|
+
}
|
|
11475
11718
|
if (cmd === "deploy") {
|
|
11476
11719
|
const artifact = await runDeploy();
|
|
11477
11720
|
const block = renderOtelEnvBlock2(artifact.token);
|
|
@@ -11525,11 +11768,11 @@ async function main() {
|
|
|
11525
11768
|
process.exit(1);
|
|
11526
11769
|
}
|
|
11527
11770
|
async function tryOrchestrator(cmd, parsed) {
|
|
11528
|
-
const scanPath =
|
|
11771
|
+
const scanPath = import_node_path50.default.resolve(cmd);
|
|
11529
11772
|
const stat = await import_node_fs32.promises.stat(scanPath).catch(() => null);
|
|
11530
11773
|
if (!stat || !stat.isDirectory()) return null;
|
|
11531
11774
|
const projectExplicit = parsed.project !== null;
|
|
11532
|
-
const projectName = projectExplicit ? parsed.project :
|
|
11775
|
+
const projectName = projectExplicit ? parsed.project : import_node_path50.default.basename(scanPath);
|
|
11533
11776
|
const result = await runOrchestrator({
|
|
11534
11777
|
scanPath,
|
|
11535
11778
|
project: projectName,
|
|
@@ -11559,12 +11802,44 @@ function resolveProjectFlag(parsed) {
|
|
|
11559
11802
|
if (env && env.length > 0 && env !== DEFAULT_PROJECT) return env;
|
|
11560
11803
|
return void 0;
|
|
11561
11804
|
}
|
|
11805
|
+
var ProjectResolutionError = class extends Error {
|
|
11806
|
+
constructor(message, exitCode = 2) {
|
|
11807
|
+
super(message);
|
|
11808
|
+
this.exitCode = exitCode;
|
|
11809
|
+
this.name = "ProjectResolutionError";
|
|
11810
|
+
}
|
|
11811
|
+
exitCode;
|
|
11812
|
+
};
|
|
11813
|
+
async function resolveProjectForVerb(client, parsed) {
|
|
11814
|
+
const explicit = resolveProjectFlag(parsed);
|
|
11815
|
+
if (explicit) return explicit;
|
|
11816
|
+
const projects = await client.get("/projects");
|
|
11817
|
+
if (projects.some((p) => p.name === DEFAULT_PROJECT)) {
|
|
11818
|
+
return void 0;
|
|
11819
|
+
}
|
|
11820
|
+
if (projects.length === 1) {
|
|
11821
|
+
return projects[0].name;
|
|
11822
|
+
}
|
|
11823
|
+
if (projects.length === 0) {
|
|
11824
|
+
throw new ProjectResolutionError(
|
|
11825
|
+
"No projects are registered with the daemon yet. Run `npx neat.is` in a repo to build a graph first, then re-run this command."
|
|
11826
|
+
);
|
|
11827
|
+
}
|
|
11828
|
+
const names = projects.map((p) => p.name).sort().map((n) => ` ${n}`).join("\n");
|
|
11829
|
+
throw new ProjectResolutionError(
|
|
11830
|
+
`Several projects are registered and none is named "default", so I can't pick one for you.
|
|
11831
|
+
Pass --project <name> to choose:
|
|
11832
|
+
${names}`
|
|
11833
|
+
);
|
|
11834
|
+
}
|
|
11835
|
+
function resolveDaemonUrl() {
|
|
11836
|
+
return process.env.NEAT_API_URL ?? process.env.NEAT_CORE_URL ?? "http://localhost:8080";
|
|
11837
|
+
}
|
|
11562
11838
|
async function runQueryVerb(cmd, parsed) {
|
|
11563
|
-
const baseUrl =
|
|
11839
|
+
const baseUrl = resolveDaemonUrl();
|
|
11564
11840
|
const client = createHttpClient(baseUrl, resolveAuthToken());
|
|
11565
|
-
const project = resolveProjectFlag(parsed);
|
|
11566
11841
|
const positional = parsed.positional;
|
|
11567
|
-
let
|
|
11842
|
+
let makeWork;
|
|
11568
11843
|
switch (cmd) {
|
|
11569
11844
|
case "root-cause": {
|
|
11570
11845
|
const node = positional[0];
|
|
@@ -11572,7 +11847,7 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
11572
11847
|
console.error("neat root-cause: missing <node-id>");
|
|
11573
11848
|
return 2;
|
|
11574
11849
|
}
|
|
11575
|
-
|
|
11850
|
+
makeWork = (project) => runRootCause(client, {
|
|
11576
11851
|
errorNode: node,
|
|
11577
11852
|
...parsed.errorId ? { errorId: parsed.errorId } : {},
|
|
11578
11853
|
...project ? { project } : {}
|
|
@@ -11585,7 +11860,7 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
11585
11860
|
console.error("neat blast-radius: missing <node-id>");
|
|
11586
11861
|
return 2;
|
|
11587
11862
|
}
|
|
11588
|
-
|
|
11863
|
+
makeWork = (project) => runBlastRadius(client, {
|
|
11589
11864
|
nodeId: node,
|
|
11590
11865
|
...parsed.depth !== null ? { depth: parsed.depth } : {},
|
|
11591
11866
|
...project ? { project } : {}
|
|
@@ -11598,7 +11873,7 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
11598
11873
|
console.error("neat dependencies: missing <node-id>");
|
|
11599
11874
|
return 2;
|
|
11600
11875
|
}
|
|
11601
|
-
|
|
11876
|
+
makeWork = (project) => runDependencies(client, {
|
|
11602
11877
|
nodeId: node,
|
|
11603
11878
|
...parsed.depth !== null ? { depth: parsed.depth } : {},
|
|
11604
11879
|
...project ? { project } : {}
|
|
@@ -11611,14 +11886,14 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
11611
11886
|
console.error("neat observed-dependencies: missing <node-id>");
|
|
11612
11887
|
return 2;
|
|
11613
11888
|
}
|
|
11614
|
-
|
|
11889
|
+
makeWork = (project) => runObservedDependencies(client, {
|
|
11615
11890
|
nodeId: node,
|
|
11616
11891
|
...project ? { project } : {}
|
|
11617
11892
|
});
|
|
11618
11893
|
break;
|
|
11619
11894
|
}
|
|
11620
11895
|
case "incidents": {
|
|
11621
|
-
|
|
11896
|
+
makeWork = (project) => runIncidents(client, {
|
|
11622
11897
|
...positional[0] ? { nodeId: positional[0] } : {},
|
|
11623
11898
|
...parsed.limit !== null ? { limit: parsed.limit } : {},
|
|
11624
11899
|
...project ? { project } : {}
|
|
@@ -11631,7 +11906,7 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
11631
11906
|
console.error("neat search: missing <query>");
|
|
11632
11907
|
return 2;
|
|
11633
11908
|
}
|
|
11634
|
-
|
|
11909
|
+
makeWork = (project) => runSearch(client, { query: q, ...project ? { project } : {} });
|
|
11635
11910
|
break;
|
|
11636
11911
|
}
|
|
11637
11912
|
case "diff": {
|
|
@@ -11640,14 +11915,14 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
11640
11915
|
console.error("neat diff: --against <snapshot-path> is required");
|
|
11641
11916
|
return 2;
|
|
11642
11917
|
}
|
|
11643
|
-
|
|
11918
|
+
makeWork = (project) => runDiff(client, {
|
|
11644
11919
|
againstSnapshot: against,
|
|
11645
11920
|
...project ? { project } : {}
|
|
11646
11921
|
});
|
|
11647
11922
|
break;
|
|
11648
11923
|
}
|
|
11649
11924
|
case "stale-edges": {
|
|
11650
|
-
|
|
11925
|
+
makeWork = (project) => runStaleEdges(client, {
|
|
11651
11926
|
...parsed.limit !== null ? { limit: parsed.limit } : {},
|
|
11652
11927
|
...parsed.edgeType ? { edgeType: parsed.edgeType } : {},
|
|
11653
11928
|
...project ? { project } : {}
|
|
@@ -11666,7 +11941,7 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
11666
11941
|
return 2;
|
|
11667
11942
|
}
|
|
11668
11943
|
}
|
|
11669
|
-
|
|
11944
|
+
makeWork = (project) => runPolicies(client, {
|
|
11670
11945
|
...parsed.node ? { nodeId: parsed.node } : {},
|
|
11671
11946
|
...hypothetical ? { hypotheticalAction: hypothetical } : {},
|
|
11672
11947
|
...project ? { project } : {}
|
|
@@ -11679,10 +11954,10 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
11679
11954
|
const parts = parsed.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
11680
11955
|
const out = [];
|
|
11681
11956
|
for (const p of parts) {
|
|
11682
|
-
const r =
|
|
11957
|
+
const r = import_types28.DivergenceTypeSchema.safeParse(p);
|
|
11683
11958
|
if (!r.success) {
|
|
11684
11959
|
console.error(
|
|
11685
|
-
`neat divergences: unknown --type "${p}". allowed: ${
|
|
11960
|
+
`neat divergences: unknown --type "${p}". allowed: ${import_types28.DivergenceTypeSchema.options.join(", ")}`
|
|
11686
11961
|
);
|
|
11687
11962
|
return 2;
|
|
11688
11963
|
}
|
|
@@ -11690,7 +11965,7 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
11690
11965
|
}
|
|
11691
11966
|
typeFilter = out;
|
|
11692
11967
|
}
|
|
11693
|
-
|
|
11968
|
+
makeWork = (project) => runDivergences(client, {
|
|
11694
11969
|
...typeFilter ? { type: typeFilter } : {},
|
|
11695
11970
|
...parsed.minConfidence !== null ? { minConfidence: parsed.minConfidence } : {},
|
|
11696
11971
|
...parsed.node ? { node: parsed.node } : {},
|
|
@@ -11703,16 +11978,21 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
11703
11978
|
return 2;
|
|
11704
11979
|
}
|
|
11705
11980
|
try {
|
|
11706
|
-
const
|
|
11981
|
+
const project = await resolveProjectForVerb(client, parsed);
|
|
11982
|
+
const result = await makeWork(project);
|
|
11707
11983
|
if (parsed.json) process.stdout.write(formatJson(result) + "\n");
|
|
11708
11984
|
else process.stdout.write(formatHuman(result) + "\n");
|
|
11709
11985
|
return 0;
|
|
11710
11986
|
} catch (err) {
|
|
11987
|
+
if (err instanceof ProjectResolutionError) {
|
|
11988
|
+
console.error(`neat ${cmd}: ${err.message}`);
|
|
11989
|
+
return err.exitCode;
|
|
11990
|
+
}
|
|
11711
11991
|
if (err instanceof HttpError) {
|
|
11712
11992
|
const detail = err.responseBody.length > 0 ? err.responseBody : err.message;
|
|
11713
11993
|
console.error(`neat ${cmd}: ${detail.trim()}`);
|
|
11714
11994
|
} else if (err instanceof TransportError) {
|
|
11715
|
-
console.error(`neat ${cmd}: ${err.message}. Is the daemon running? (NEAT_API_URL=${
|
|
11995
|
+
console.error(`neat ${cmd}: ${err.message}. Is the daemon running? (NEAT_API_URL=${resolveDaemonUrl()})`);
|
|
11716
11996
|
} else {
|
|
11717
11997
|
console.error(`neat ${cmd}: ${err.message}`);
|
|
11718
11998
|
}
|
|
@@ -11729,6 +12009,7 @@ if (/[\\/]cli\.(?:cjs|js)$/.test(entry) || entry.endsWith("/cli") || entry.endsW
|
|
|
11729
12009
|
// Annotate the CommonJS export names for ESM import in node:
|
|
11730
12010
|
0 && (module.exports = {
|
|
11731
12011
|
CLAUDE_SKILL_CONFIG,
|
|
12012
|
+
ProjectResolutionError,
|
|
11732
12013
|
QUERY_VERBS,
|
|
11733
12014
|
commandPrefix,
|
|
11734
12015
|
isNpxInvocation,
|
|
@@ -11736,6 +12017,7 @@ if (/[\\/]cli\.(?:cjs|js)$/.test(entry) || entry.endsWith("/cli") || entry.endsW
|
|
|
11736
12017
|
parseArgs,
|
|
11737
12018
|
printBanner,
|
|
11738
12019
|
readPackageVersion,
|
|
12020
|
+
resolveProjectForVerb,
|
|
11739
12021
|
runInit,
|
|
11740
12022
|
runQueryVerb,
|
|
11741
12023
|
runSkill,
|