@neat.is/core 0.4.15 → 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 +923 -590
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +19 -4
- package/dist/cli.d.ts +19 -4
- package/dist/cli.js +291 -186
- 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 +3 -4
- package/dist/chunk-VMLWUK7W.js.map +0 -1
- package/dist/chunk-XS4CGNRO.js.map +0 -1
package/dist/neatd.cjs
CHANGED
|
@@ -57,9 +57,9 @@ function mountBearerAuth(app, opts) {
|
|
|
57
57
|
const suffixes = [...DEFAULT_UNAUTH_SUFFIXES, ...opts.extraUnauthenticatedSuffixes ?? []];
|
|
58
58
|
const publicRead = opts.publicRead === true;
|
|
59
59
|
app.addHook("preHandler", (req2, reply, done) => {
|
|
60
|
-
const
|
|
60
|
+
const path45 = (req2.url.split("?")[0] ?? "").replace(/\/+$/, "");
|
|
61
61
|
for (const suffix of suffixes) {
|
|
62
|
-
if (
|
|
62
|
+
if (path45 === suffix || path45.endsWith(suffix)) {
|
|
63
63
|
done();
|
|
64
64
|
return;
|
|
65
65
|
}
|
|
@@ -187,8 +187,8 @@ function reshapeGrpcRequest(req2) {
|
|
|
187
187
|
};
|
|
188
188
|
}
|
|
189
189
|
function resolveProtoRoot() {
|
|
190
|
-
const here =
|
|
191
|
-
return
|
|
190
|
+
const here = import_node_path39.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
191
|
+
return import_node_path39.default.resolve(here, "..", "proto");
|
|
192
192
|
}
|
|
193
193
|
function loadTraceService() {
|
|
194
194
|
const protoRoot = resolveProtoRoot();
|
|
@@ -256,13 +256,13 @@ async function startOtelGrpcReceiver(opts) {
|
|
|
256
256
|
})
|
|
257
257
|
};
|
|
258
258
|
}
|
|
259
|
-
var import_node_url,
|
|
259
|
+
var import_node_url, import_node_path39, import_node_crypto2, grpc, protoLoader;
|
|
260
260
|
var init_otel_grpc = __esm({
|
|
261
261
|
"src/otel-grpc.ts"() {
|
|
262
262
|
"use strict";
|
|
263
263
|
init_cjs_shims();
|
|
264
264
|
import_node_url = require("url");
|
|
265
|
-
|
|
265
|
+
import_node_path39 = __toESM(require("path"), 1);
|
|
266
266
|
import_node_crypto2 = require("crypto");
|
|
267
267
|
grpc = __toESM(require("@grpc/grpc-js"), 1);
|
|
268
268
|
protoLoader = __toESM(require("@grpc/proto-loader"), 1);
|
|
@@ -372,10 +372,10 @@ function parseOtlpRequest(body) {
|
|
|
372
372
|
return out;
|
|
373
373
|
}
|
|
374
374
|
function loadProtoRoot() {
|
|
375
|
-
const here =
|
|
376
|
-
const protoRoot =
|
|
375
|
+
const here = import_node_path40.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
|
|
376
|
+
const protoRoot = import_node_path40.default.resolve(here, "..", "proto");
|
|
377
377
|
const root = new import_protobufjs.default.Root();
|
|
378
|
-
root.resolvePath = (_origin, target) =>
|
|
378
|
+
root.resolvePath = (_origin, target) => import_node_path40.default.resolve(protoRoot, target);
|
|
379
379
|
root.loadSync(
|
|
380
380
|
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
|
|
381
381
|
{ keepCase: true }
|
|
@@ -571,12 +571,12 @@ async function buildOtelReceiver(opts) {
|
|
|
571
571
|
};
|
|
572
572
|
return decorated;
|
|
573
573
|
}
|
|
574
|
-
var
|
|
574
|
+
var import_node_path40, import_node_url2, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody;
|
|
575
575
|
var init_otel = __esm({
|
|
576
576
|
"src/otel.ts"() {
|
|
577
577
|
"use strict";
|
|
578
578
|
init_cjs_shims();
|
|
579
|
-
|
|
579
|
+
import_node_path40 = __toESM(require("path"), 1);
|
|
580
580
|
import_node_url2 = require("url");
|
|
581
581
|
import_fastify2 = __toESM(require("fastify"), 1);
|
|
582
582
|
import_protobufjs = __toESM(require("protobufjs"), 1);
|
|
@@ -593,13 +593,13 @@ var init_otel = __esm({
|
|
|
593
593
|
// src/neatd.ts
|
|
594
594
|
init_cjs_shims();
|
|
595
595
|
var import_node_fs26 = require("fs");
|
|
596
|
-
var
|
|
596
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
597
597
|
var import_node_module = require("module");
|
|
598
598
|
|
|
599
599
|
// src/daemon.ts
|
|
600
600
|
init_cjs_shims();
|
|
601
601
|
var import_node_fs25 = require("fs");
|
|
602
|
-
var
|
|
602
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
603
603
|
|
|
604
604
|
// src/graph.ts
|
|
605
605
|
init_cjs_shims();
|
|
@@ -1128,19 +1128,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
1128
1128
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
1129
1129
|
let best = { path: [start], edges: [] };
|
|
1130
1130
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
1131
|
-
function step(node,
|
|
1132
|
-
if (
|
|
1133
|
-
best = { path: [...
|
|
1131
|
+
function step(node, path45, edges) {
|
|
1132
|
+
if (path45.length > best.path.length) {
|
|
1133
|
+
best = { path: [...path45], edges: [...edges] };
|
|
1134
1134
|
}
|
|
1135
|
-
if (
|
|
1135
|
+
if (path45.length - 1 >= maxDepth) return;
|
|
1136
1136
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
1137
1137
|
for (const [srcId, edge] of incoming) {
|
|
1138
1138
|
if (visited.has(srcId)) continue;
|
|
1139
1139
|
visited.add(srcId);
|
|
1140
|
-
|
|
1140
|
+
path45.push(srcId);
|
|
1141
1141
|
edges.push(edge);
|
|
1142
|
-
step(srcId,
|
|
1143
|
-
|
|
1142
|
+
step(srcId, path45, edges);
|
|
1143
|
+
path45.pop();
|
|
1144
1144
|
edges.pop();
|
|
1145
1145
|
visited.delete(srcId);
|
|
1146
1146
|
}
|
|
@@ -1658,6 +1658,41 @@ var DEFAULT_STALE_THRESHOLDS = {
|
|
|
1658
1658
|
CONFIGURED_BY: DAY_MS,
|
|
1659
1659
|
RUNS_ON: DAY_MS
|
|
1660
1660
|
};
|
|
1661
|
+
var DEFAULT_INCIDENT_THRESHOLDS = {
|
|
1662
|
+
threshold: 5,
|
|
1663
|
+
windowMs: 6e4
|
|
1664
|
+
};
|
|
1665
|
+
function loadIncidentThresholdsFromEnv() {
|
|
1666
|
+
const raw = process.env.NEAT_INCIDENT_THRESHOLDS;
|
|
1667
|
+
if (!raw) return DEFAULT_INCIDENT_THRESHOLDS;
|
|
1668
|
+
try {
|
|
1669
|
+
const overrides = JSON.parse(raw);
|
|
1670
|
+
const merged = { ...DEFAULT_INCIDENT_THRESHOLDS };
|
|
1671
|
+
if (typeof overrides.threshold === "number" && Number.isFinite(overrides.threshold) && overrides.threshold >= 1) {
|
|
1672
|
+
merged.threshold = Math.floor(overrides.threshold);
|
|
1673
|
+
}
|
|
1674
|
+
if (typeof overrides.windowMs === "number" && Number.isFinite(overrides.windowMs) && overrides.windowMs >= 0) {
|
|
1675
|
+
merged.windowMs = overrides.windowMs;
|
|
1676
|
+
}
|
|
1677
|
+
return merged;
|
|
1678
|
+
} catch (err) {
|
|
1679
|
+
console.warn(
|
|
1680
|
+
`[neat] NEAT_INCIDENT_THRESHOLDS could not be parsed (${err.message}); using defaults`
|
|
1681
|
+
);
|
|
1682
|
+
return DEFAULT_INCIDENT_THRESHOLDS;
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
function httpResponseStatus(span) {
|
|
1686
|
+
for (const key of ["http.response.status_code", "http.status_code"]) {
|
|
1687
|
+
const v = span.attributes[key];
|
|
1688
|
+
if (typeof v === "number" && Number.isFinite(v)) return v;
|
|
1689
|
+
if (typeof v === "string") {
|
|
1690
|
+
const n = Number(v);
|
|
1691
|
+
if (Number.isFinite(n)) return n;
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
return void 0;
|
|
1695
|
+
}
|
|
1661
1696
|
function nowIso(ctx) {
|
|
1662
1697
|
return new Date(ctx.now ? ctx.now() : Date.now()).toISOString();
|
|
1663
1698
|
}
|
|
@@ -2071,6 +2106,71 @@ function makeErrorSpanWriter(errorsPath) {
|
|
|
2071
2106
|
await import_node_fs3.promises.appendFile(errorsPath, JSON.stringify(ev) + "\n", "utf8");
|
|
2072
2107
|
};
|
|
2073
2108
|
}
|
|
2109
|
+
async function recordFailingResponseIncident(ctx, span, affectedNode, timestamp, statusCode, count, firstTimestamp) {
|
|
2110
|
+
const attrs = sanitizeAttributes(span.attributes);
|
|
2111
|
+
const first = firstTimestamp ?? timestamp;
|
|
2112
|
+
const peer = pickAddress(span);
|
|
2113
|
+
const message = count > 1 ? `${count} consecutive HTTP ${statusCode} responses` + (peer ? ` to ${peer}` : "") : `HTTP ${statusCode} response` + (peer ? ` from ${peer}` : "");
|
|
2114
|
+
const ev = {
|
|
2115
|
+
id: `${span.traceId}:${span.spanId}`,
|
|
2116
|
+
timestamp,
|
|
2117
|
+
service: span.service,
|
|
2118
|
+
traceId: span.traceId,
|
|
2119
|
+
spanId: span.spanId,
|
|
2120
|
+
errorType: "http-failure",
|
|
2121
|
+
errorMessage: message,
|
|
2122
|
+
...Object.keys(attrs).length > 0 ? { attributes: attrs } : {},
|
|
2123
|
+
affectedNode,
|
|
2124
|
+
httpStatusCode: statusCode,
|
|
2125
|
+
incidentCount: count,
|
|
2126
|
+
firstTimestamp: first,
|
|
2127
|
+
lastTimestamp: timestamp
|
|
2128
|
+
};
|
|
2129
|
+
await appendErrorEvent(ctx, ev);
|
|
2130
|
+
}
|
|
2131
|
+
async function advance4xxBurst(ctx, span, affectedNode, ts, nowMs, status2) {
|
|
2132
|
+
const { threshold, windowMs } = loadIncidentThresholdsFromEnv();
|
|
2133
|
+
if (!ctx.burstState) ctx.burstState = /* @__PURE__ */ new Map();
|
|
2134
|
+
const peer = pickAddress(span) ?? span.spanId;
|
|
2135
|
+
const key = `${span.service}->${peer}`;
|
|
2136
|
+
const existing = ctx.burstState.get(key);
|
|
2137
|
+
let state;
|
|
2138
|
+
if (existing && nowMs - existing.lastMs <= windowMs) {
|
|
2139
|
+
existing.count += 1;
|
|
2140
|
+
existing.lastTs = ts;
|
|
2141
|
+
existing.lastMs = nowMs;
|
|
2142
|
+
existing.codes.set(status2, (existing.codes.get(status2) ?? 0) + 1);
|
|
2143
|
+
state = existing;
|
|
2144
|
+
} else {
|
|
2145
|
+
state = {
|
|
2146
|
+
count: 1,
|
|
2147
|
+
firstTs: ts,
|
|
2148
|
+
lastTs: ts,
|
|
2149
|
+
lastMs: nowMs,
|
|
2150
|
+
codes: /* @__PURE__ */ new Map([[status2, 1]])
|
|
2151
|
+
};
|
|
2152
|
+
ctx.burstState.set(key, state);
|
|
2153
|
+
}
|
|
2154
|
+
if (state.count < threshold) return;
|
|
2155
|
+
let dominant = status2;
|
|
2156
|
+
let max = 0;
|
|
2157
|
+
for (const [code, n] of state.codes) {
|
|
2158
|
+
if (n > max) {
|
|
2159
|
+
max = n;
|
|
2160
|
+
dominant = code;
|
|
2161
|
+
}
|
|
2162
|
+
}
|
|
2163
|
+
await recordFailingResponseIncident(
|
|
2164
|
+
ctx,
|
|
2165
|
+
span,
|
|
2166
|
+
affectedNode,
|
|
2167
|
+
state.lastTs,
|
|
2168
|
+
dominant,
|
|
2169
|
+
state.count,
|
|
2170
|
+
state.firstTs
|
|
2171
|
+
);
|
|
2172
|
+
ctx.burstState.delete(key);
|
|
2173
|
+
}
|
|
2074
2174
|
async function handleSpan(ctx, span) {
|
|
2075
2175
|
const ts = span.startTimeIso ?? nowIso(ctx);
|
|
2076
2176
|
const nowMs = ctx.now ? ctx.now() : Date.now();
|
|
@@ -2169,6 +2269,14 @@ async function handleSpan(ctx, span) {
|
|
|
2169
2269
|
await appendErrorEvent(ctx, ev);
|
|
2170
2270
|
}
|
|
2171
2271
|
}
|
|
2272
|
+
if (span.statusCode !== 2) {
|
|
2273
|
+
const status2 = httpResponseStatus(span);
|
|
2274
|
+
if (status2 !== void 0 && status2 >= 500) {
|
|
2275
|
+
await recordFailingResponseIncident(ctx, span, sourceId, ts, status2, 1);
|
|
2276
|
+
} else if (status2 !== void 0 && status2 >= 400 && spanMintsObservedEdge(span.kind)) {
|
|
2277
|
+
await advance4xxBurst(ctx, span, sourceId, ts, nowMs, status2);
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2172
2280
|
void affectedNode;
|
|
2173
2281
|
if (ctx.onPolicyTrigger) await ctx.onPolicyTrigger(ctx.graph);
|
|
2174
2282
|
}
|
|
@@ -4115,7 +4223,7 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
4115
4223
|
|
|
4116
4224
|
// src/extract/calls/index.ts
|
|
4117
4225
|
init_cjs_shims();
|
|
4118
|
-
var
|
|
4226
|
+
var import_types17 = require("@neat.is/types");
|
|
4119
4227
|
|
|
4120
4228
|
// src/extract/calls/http.ts
|
|
4121
4229
|
init_cjs_shims();
|
|
@@ -4468,15 +4576,75 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
4468
4576
|
return out;
|
|
4469
4577
|
}
|
|
4470
4578
|
|
|
4579
|
+
// src/extract/calls/supabase.ts
|
|
4580
|
+
init_cjs_shims();
|
|
4581
|
+
var import_node_path27 = __toESM(require("path"), 1);
|
|
4582
|
+
var import_types16 = require("@neat.is/types");
|
|
4583
|
+
var SUPABASE_JS_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/supabase-js['"`]/;
|
|
4584
|
+
var SUPABASE_SSR_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/ssr['"`]/;
|
|
4585
|
+
var SUPABASE_CLIENT_RE = /\b(createClient|createServerClient|createBrowserClient)\s*\(\s*(?:['"`]([^'"`]*)['"`])?/g;
|
|
4586
|
+
function hostFromLiteral(literal) {
|
|
4587
|
+
if (!literal) return null;
|
|
4588
|
+
const m = /^https?:\/\/([^/:'"`\s]+)/.exec(literal.trim());
|
|
4589
|
+
if (!m) return null;
|
|
4590
|
+
const host = m[1];
|
|
4591
|
+
if (!/\.supabase\.(co|in)$/i.test(host)) return null;
|
|
4592
|
+
return host;
|
|
4593
|
+
}
|
|
4594
|
+
function readImports2(content) {
|
|
4595
|
+
return {
|
|
4596
|
+
hasSupabaseJs: SUPABASE_JS_IMPORT_RE.test(content),
|
|
4597
|
+
hasSupabaseSsr: SUPABASE_SSR_IMPORT_RE.test(content)
|
|
4598
|
+
};
|
|
4599
|
+
}
|
|
4600
|
+
function constructorMatchesImport(name, ctx) {
|
|
4601
|
+
if (name === "createClient") return ctx.hasSupabaseJs;
|
|
4602
|
+
return ctx.hasSupabaseSsr;
|
|
4603
|
+
}
|
|
4604
|
+
function supabaseEndpointsFromFile(file, serviceDir) {
|
|
4605
|
+
const ctx = readImports2(file.content);
|
|
4606
|
+
if (!ctx.hasSupabaseJs && !ctx.hasSupabaseSsr) return [];
|
|
4607
|
+
const out = [];
|
|
4608
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4609
|
+
SUPABASE_CLIENT_RE.lastIndex = 0;
|
|
4610
|
+
let m;
|
|
4611
|
+
while ((m = SUPABASE_CLIENT_RE.exec(file.content)) !== null) {
|
|
4612
|
+
const ctor = m[1];
|
|
4613
|
+
if (!constructorMatchesImport(ctor, ctx)) continue;
|
|
4614
|
+
const host = hostFromLiteral(m[2]);
|
|
4615
|
+
const name = host ?? "env";
|
|
4616
|
+
if (seen.has(name)) continue;
|
|
4617
|
+
seen.add(name);
|
|
4618
|
+
const line = lineOf(file.content, m[0]);
|
|
4619
|
+
out.push({
|
|
4620
|
+
infraId: (0, import_types16.infraId)("supabase", name),
|
|
4621
|
+
name,
|
|
4622
|
+
kind: "supabase",
|
|
4623
|
+
edgeType: "CALLS",
|
|
4624
|
+
// `createClient(...)` from @supabase/supabase-js (or createServerClient /
|
|
4625
|
+
// createBrowserClient from @supabase/ssr) with the import in scope — a
|
|
4626
|
+
// framework-aware recognizer matched the SDK shape. Verified-call-site
|
|
4627
|
+
// tier (ADR-066), the same grade aws.ts / grpc.ts emit at.
|
|
4628
|
+
confidenceKind: "verified-call-site",
|
|
4629
|
+
evidence: {
|
|
4630
|
+
file: import_node_path27.default.relative(serviceDir, file.path),
|
|
4631
|
+
line,
|
|
4632
|
+
snippet: snippet(file.content, line)
|
|
4633
|
+
}
|
|
4634
|
+
});
|
|
4635
|
+
}
|
|
4636
|
+
return out;
|
|
4637
|
+
}
|
|
4638
|
+
|
|
4471
4639
|
// src/extract/calls/index.ts
|
|
4472
4640
|
function edgeTypeFromEndpoint(ep) {
|
|
4473
4641
|
switch (ep.edgeType) {
|
|
4474
4642
|
case "PUBLISHES_TO":
|
|
4475
|
-
return
|
|
4643
|
+
return import_types17.EdgeType.PUBLISHES_TO;
|
|
4476
4644
|
case "CONSUMES_FROM":
|
|
4477
|
-
return
|
|
4645
|
+
return import_types17.EdgeType.CONSUMES_FROM;
|
|
4478
4646
|
default:
|
|
4479
|
-
return
|
|
4647
|
+
return import_types17.EdgeType.CALLS;
|
|
4480
4648
|
}
|
|
4481
4649
|
}
|
|
4482
4650
|
function isAwsKind(kind) {
|
|
@@ -4496,6 +4664,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
4496
4664
|
endpoints.push(...redisEndpointsFromFile(maskedFile, service.dir));
|
|
4497
4665
|
endpoints.push(...awsEndpointsFromFile(maskedFile, service.dir));
|
|
4498
4666
|
endpoints.push(...grpcEndpointsFromFile(maskedFile, service.dir));
|
|
4667
|
+
endpoints.push(...supabaseEndpointsFromFile(maskedFile, service.dir));
|
|
4499
4668
|
}
|
|
4500
4669
|
if (endpoints.length === 0) continue;
|
|
4501
4670
|
const seenEdges = /* @__PURE__ */ new Set();
|
|
@@ -4503,7 +4672,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
4503
4672
|
if (!graph.hasNode(ep.infraId)) {
|
|
4504
4673
|
const node = {
|
|
4505
4674
|
id: ep.infraId,
|
|
4506
|
-
type:
|
|
4675
|
+
type: import_types17.NodeType.InfraNode,
|
|
4507
4676
|
name: ep.name,
|
|
4508
4677
|
// #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
|
|
4509
4678
|
// aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
|
|
@@ -4515,7 +4684,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
4515
4684
|
nodesAdded++;
|
|
4516
4685
|
}
|
|
4517
4686
|
const edgeType = edgeTypeFromEndpoint(ep);
|
|
4518
|
-
const confidence = (0,
|
|
4687
|
+
const confidence = (0, import_types17.confidenceForExtracted)(ep.confidenceKind);
|
|
4519
4688
|
const relFile = toPosix2(ep.evidence.file);
|
|
4520
4689
|
const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
4521
4690
|
graph,
|
|
@@ -4525,7 +4694,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
4525
4694
|
);
|
|
4526
4695
|
nodesAdded += n;
|
|
4527
4696
|
edgesAdded += e;
|
|
4528
|
-
if (!(0,
|
|
4697
|
+
if (!(0, import_types17.passesExtractedFloor)(confidence)) {
|
|
4529
4698
|
noteExtractedDropped({
|
|
4530
4699
|
source: fileNodeId,
|
|
4531
4700
|
target: ep.infraId,
|
|
@@ -4545,7 +4714,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
4545
4714
|
source: fileNodeId,
|
|
4546
4715
|
target: ep.infraId,
|
|
4547
4716
|
type: edgeType,
|
|
4548
|
-
provenance:
|
|
4717
|
+
provenance: import_types17.Provenance.EXTRACTED,
|
|
4549
4718
|
confidence,
|
|
4550
4719
|
evidence: ep.evidence
|
|
4551
4720
|
};
|
|
@@ -4570,16 +4739,16 @@ init_cjs_shims();
|
|
|
4570
4739
|
|
|
4571
4740
|
// src/extract/infra/docker-compose.ts
|
|
4572
4741
|
init_cjs_shims();
|
|
4573
|
-
var
|
|
4574
|
-
var
|
|
4742
|
+
var import_node_path28 = __toESM(require("path"), 1);
|
|
4743
|
+
var import_types19 = require("@neat.is/types");
|
|
4575
4744
|
|
|
4576
4745
|
// src/extract/infra/shared.ts
|
|
4577
4746
|
init_cjs_shims();
|
|
4578
|
-
var
|
|
4747
|
+
var import_types18 = require("@neat.is/types");
|
|
4579
4748
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
4580
4749
|
return {
|
|
4581
|
-
id: (0,
|
|
4582
|
-
type:
|
|
4750
|
+
id: (0, import_types18.infraId)(kind, name),
|
|
4751
|
+
type: import_types18.NodeType.InfraNode,
|
|
4583
4752
|
name,
|
|
4584
4753
|
provider,
|
|
4585
4754
|
kind,
|
|
@@ -4608,7 +4777,7 @@ function dependsOnList(value) {
|
|
|
4608
4777
|
}
|
|
4609
4778
|
function serviceNameToServiceNode(name, services) {
|
|
4610
4779
|
for (const s of services) {
|
|
4611
|
-
if (s.node.name === name ||
|
|
4780
|
+
if (s.node.name === name || import_node_path28.default.basename(s.dir) === name) return s.node.id;
|
|
4612
4781
|
}
|
|
4613
4782
|
return null;
|
|
4614
4783
|
}
|
|
@@ -4617,7 +4786,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
4617
4786
|
let edgesAdded = 0;
|
|
4618
4787
|
let composePath = null;
|
|
4619
4788
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
4620
|
-
const abs =
|
|
4789
|
+
const abs = import_node_path28.default.join(scanPath, name);
|
|
4621
4790
|
if (await exists(abs)) {
|
|
4622
4791
|
composePath = abs;
|
|
4623
4792
|
break;
|
|
@@ -4630,13 +4799,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
4630
4799
|
} catch (err) {
|
|
4631
4800
|
recordExtractionError(
|
|
4632
4801
|
"infra docker-compose",
|
|
4633
|
-
|
|
4802
|
+
import_node_path28.default.relative(scanPath, composePath),
|
|
4634
4803
|
err
|
|
4635
4804
|
);
|
|
4636
4805
|
return { nodesAdded, edgesAdded };
|
|
4637
4806
|
}
|
|
4638
4807
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
4639
|
-
const evidenceFile =
|
|
4808
|
+
const evidenceFile = import_node_path28.default.relative(scanPath, composePath).split(import_node_path28.default.sep).join("/");
|
|
4640
4809
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
4641
4810
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
4642
4811
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -4658,15 +4827,15 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
4658
4827
|
for (const dep of dependsOnList(svc.depends_on)) {
|
|
4659
4828
|
const targetId = composeNameToNodeId.get(dep);
|
|
4660
4829
|
if (!targetId) continue;
|
|
4661
|
-
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId,
|
|
4830
|
+
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId, import_types19.EdgeType.DEPENDS_ON);
|
|
4662
4831
|
if (graph.hasEdge(edgeId)) continue;
|
|
4663
4832
|
const edge = {
|
|
4664
4833
|
id: edgeId,
|
|
4665
4834
|
source: sourceId,
|
|
4666
4835
|
target: targetId,
|
|
4667
|
-
type:
|
|
4668
|
-
provenance:
|
|
4669
|
-
confidence: (0,
|
|
4836
|
+
type: import_types19.EdgeType.DEPENDS_ON,
|
|
4837
|
+
provenance: import_types19.Provenance.EXTRACTED,
|
|
4838
|
+
confidence: (0, import_types19.confidenceForExtracted)("structural"),
|
|
4670
4839
|
evidence: { file: evidenceFile }
|
|
4671
4840
|
};
|
|
4672
4841
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -4678,9 +4847,9 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
4678
4847
|
|
|
4679
4848
|
// src/extract/infra/dockerfile.ts
|
|
4680
4849
|
init_cjs_shims();
|
|
4681
|
-
var
|
|
4850
|
+
var import_node_path29 = __toESM(require("path"), 1);
|
|
4682
4851
|
var import_node_fs15 = require("fs");
|
|
4683
|
-
var
|
|
4852
|
+
var import_types20 = require("@neat.is/types");
|
|
4684
4853
|
function runtimeImage(content) {
|
|
4685
4854
|
const lines = content.split("\n");
|
|
4686
4855
|
let last = null;
|
|
@@ -4699,7 +4868,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4699
4868
|
let nodesAdded = 0;
|
|
4700
4869
|
let edgesAdded = 0;
|
|
4701
4870
|
for (const service of services) {
|
|
4702
|
-
const dockerfilePath =
|
|
4871
|
+
const dockerfilePath = import_node_path29.default.join(service.dir, "Dockerfile");
|
|
4703
4872
|
if (!await exists(dockerfilePath)) continue;
|
|
4704
4873
|
let content;
|
|
4705
4874
|
try {
|
|
@@ -4707,7 +4876,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4707
4876
|
} catch (err) {
|
|
4708
4877
|
recordExtractionError(
|
|
4709
4878
|
"infra dockerfile",
|
|
4710
|
-
|
|
4879
|
+
import_node_path29.default.relative(scanPath, dockerfilePath),
|
|
4711
4880
|
err
|
|
4712
4881
|
);
|
|
4713
4882
|
continue;
|
|
@@ -4719,7 +4888,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4719
4888
|
graph.addNode(node.id, node);
|
|
4720
4889
|
nodesAdded++;
|
|
4721
4890
|
}
|
|
4722
|
-
const relDockerfile = toPosix2(
|
|
4891
|
+
const relDockerfile = toPosix2(import_node_path29.default.relative(service.dir, dockerfilePath));
|
|
4723
4892
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
4724
4893
|
graph,
|
|
4725
4894
|
service.pkg.name,
|
|
@@ -4728,17 +4897,17 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4728
4897
|
);
|
|
4729
4898
|
nodesAdded += fn;
|
|
4730
4899
|
edgesAdded += fe;
|
|
4731
|
-
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id,
|
|
4900
|
+
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id, import_types20.EdgeType.RUNS_ON);
|
|
4732
4901
|
if (!graph.hasEdge(edgeId)) {
|
|
4733
4902
|
const edge = {
|
|
4734
4903
|
id: edgeId,
|
|
4735
4904
|
source: fileNodeId,
|
|
4736
4905
|
target: node.id,
|
|
4737
|
-
type:
|
|
4738
|
-
provenance:
|
|
4739
|
-
confidence: (0,
|
|
4906
|
+
type: import_types20.EdgeType.RUNS_ON,
|
|
4907
|
+
provenance: import_types20.Provenance.EXTRACTED,
|
|
4908
|
+
confidence: (0, import_types20.confidenceForExtracted)("structural"),
|
|
4740
4909
|
evidence: {
|
|
4741
|
-
file: toPosix2(
|
|
4910
|
+
file: toPosix2(import_node_path29.default.relative(scanPath, dockerfilePath))
|
|
4742
4911
|
}
|
|
4743
4912
|
};
|
|
4744
4913
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -4751,7 +4920,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4751
4920
|
// src/extract/infra/terraform.ts
|
|
4752
4921
|
init_cjs_shims();
|
|
4753
4922
|
var import_node_fs16 = require("fs");
|
|
4754
|
-
var
|
|
4923
|
+
var import_node_path30 = __toESM(require("path"), 1);
|
|
4755
4924
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
4756
4925
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
4757
4926
|
if (depth > max) return [];
|
|
@@ -4760,11 +4929,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
|
|
|
4760
4929
|
for (const entry2 of entries) {
|
|
4761
4930
|
if (entry2.isDirectory()) {
|
|
4762
4931
|
if (IGNORED_DIRS.has(entry2.name) || entry2.name === ".terraform") continue;
|
|
4763
|
-
const child =
|
|
4932
|
+
const child = import_node_path30.default.join(start, entry2.name);
|
|
4764
4933
|
if (await isPythonVenvDir(child)) continue;
|
|
4765
4934
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
4766
4935
|
} else if (entry2.isFile() && entry2.name.endsWith(".tf")) {
|
|
4767
|
-
out.push(
|
|
4936
|
+
out.push(import_node_path30.default.join(start, entry2.name));
|
|
4768
4937
|
}
|
|
4769
4938
|
}
|
|
4770
4939
|
return out;
|
|
@@ -4792,7 +4961,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
4792
4961
|
// src/extract/infra/k8s.ts
|
|
4793
4962
|
init_cjs_shims();
|
|
4794
4963
|
var import_node_fs17 = require("fs");
|
|
4795
|
-
var
|
|
4964
|
+
var import_node_path31 = __toESM(require("path"), 1);
|
|
4796
4965
|
var import_yaml3 = require("yaml");
|
|
4797
4966
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
4798
4967
|
Service: "k8s-service",
|
|
@@ -4810,11 +4979,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
|
4810
4979
|
for (const entry2 of entries) {
|
|
4811
4980
|
if (entry2.isDirectory()) {
|
|
4812
4981
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
4813
|
-
const child =
|
|
4982
|
+
const child = import_node_path31.default.join(start, entry2.name);
|
|
4814
4983
|
if (await isPythonVenvDir(child)) continue;
|
|
4815
4984
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
4816
|
-
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
4817
|
-
out.push(
|
|
4985
|
+
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path31.default.extname(entry2.name))) {
|
|
4986
|
+
out.push(import_node_path31.default.join(start, entry2.name));
|
|
4818
4987
|
}
|
|
4819
4988
|
}
|
|
4820
4989
|
return out;
|
|
@@ -4858,17 +5027,17 @@ async function addInfra(graph, scanPath, services) {
|
|
|
4858
5027
|
}
|
|
4859
5028
|
|
|
4860
5029
|
// src/extract/index.ts
|
|
4861
|
-
var
|
|
5030
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
4862
5031
|
|
|
4863
5032
|
// src/extract/retire.ts
|
|
4864
5033
|
init_cjs_shims();
|
|
4865
5034
|
var import_node_fs18 = require("fs");
|
|
4866
|
-
var
|
|
4867
|
-
var
|
|
5035
|
+
var import_node_path32 = __toESM(require("path"), 1);
|
|
5036
|
+
var import_types21 = require("@neat.is/types");
|
|
4868
5037
|
function dropOrphanedFileNodes(graph) {
|
|
4869
5038
|
const orphans = [];
|
|
4870
5039
|
graph.forEachNode((id, attrs) => {
|
|
4871
|
-
if (attrs.type !==
|
|
5040
|
+
if (attrs.type !== import_types21.NodeType.FileNode) return;
|
|
4872
5041
|
if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
|
|
4873
5042
|
orphans.push(id);
|
|
4874
5043
|
}
|
|
@@ -4881,14 +5050,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
4881
5050
|
const bases = [scanPath, ...serviceDirs];
|
|
4882
5051
|
graph.forEachEdge((id, attrs) => {
|
|
4883
5052
|
const edge = attrs;
|
|
4884
|
-
if (edge.provenance !==
|
|
5053
|
+
if (edge.provenance !== import_types21.Provenance.EXTRACTED) return;
|
|
4885
5054
|
const evidenceFile = edge.evidence?.file;
|
|
4886
5055
|
if (!evidenceFile) return;
|
|
4887
|
-
if (
|
|
5056
|
+
if (import_node_path32.default.isAbsolute(evidenceFile)) {
|
|
4888
5057
|
if (!(0, import_node_fs18.existsSync)(evidenceFile)) toDrop.push(id);
|
|
4889
5058
|
return;
|
|
4890
5059
|
}
|
|
4891
|
-
const found = bases.some((base) => (0, import_node_fs18.existsSync)(
|
|
5060
|
+
const found = bases.some((base) => (0, import_node_fs18.existsSync)(import_node_path32.default.join(base, evidenceFile)));
|
|
4892
5061
|
if (!found) toDrop.push(id);
|
|
4893
5062
|
});
|
|
4894
5063
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -4928,7 +5097,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
4928
5097
|
}
|
|
4929
5098
|
const droppedEntries = drainDroppedExtracted();
|
|
4930
5099
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
4931
|
-
const rejectedPath =
|
|
5100
|
+
const rejectedPath = import_node_path33.default.join(import_node_path33.default.dirname(opts.errorsPath), "rejected.ndjson");
|
|
4932
5101
|
try {
|
|
4933
5102
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
4934
5103
|
} catch (err) {
|
|
@@ -4963,8 +5132,8 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
4963
5132
|
// src/persist.ts
|
|
4964
5133
|
init_cjs_shims();
|
|
4965
5134
|
var import_node_fs19 = require("fs");
|
|
4966
|
-
var
|
|
4967
|
-
var
|
|
5135
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
5136
|
+
var import_types22 = require("@neat.is/types");
|
|
4968
5137
|
var SCHEMA_VERSION = 4;
|
|
4969
5138
|
function migrateV1ToV2(payload) {
|
|
4970
5139
|
const nodes = payload.graph.nodes;
|
|
@@ -4986,12 +5155,12 @@ function migrateV2ToV3(payload) {
|
|
|
4986
5155
|
for (const edge of edges) {
|
|
4987
5156
|
const attrs = edge.attributes;
|
|
4988
5157
|
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
4989
|
-
attrs.provenance =
|
|
5158
|
+
attrs.provenance = import_types22.Provenance.OBSERVED;
|
|
4990
5159
|
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
4991
5160
|
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
4992
5161
|
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
4993
5162
|
if (type && source && target) {
|
|
4994
|
-
const newId = (0,
|
|
5163
|
+
const newId = (0, import_types22.observedEdgeId)(source, target, type);
|
|
4995
5164
|
attrs.id = newId;
|
|
4996
5165
|
if (edge.key) edge.key = newId;
|
|
4997
5166
|
}
|
|
@@ -5000,7 +5169,7 @@ function migrateV2ToV3(payload) {
|
|
|
5000
5169
|
return { ...payload, schemaVersion: 3 };
|
|
5001
5170
|
}
|
|
5002
5171
|
async function ensureDir(filePath) {
|
|
5003
|
-
await import_node_fs19.promises.mkdir(
|
|
5172
|
+
await import_node_fs19.promises.mkdir(import_node_path34.default.dirname(filePath), { recursive: true });
|
|
5004
5173
|
}
|
|
5005
5174
|
async function saveGraphToDisk(graph, outPath) {
|
|
5006
5175
|
await ensureDir(outPath);
|
|
@@ -5075,23 +5244,23 @@ function startPersistLoop(graph, outPath, intervalMs = 6e4) {
|
|
|
5075
5244
|
|
|
5076
5245
|
// src/projects.ts
|
|
5077
5246
|
init_cjs_shims();
|
|
5078
|
-
var
|
|
5247
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
5079
5248
|
function pathsForProject(project, baseDir) {
|
|
5080
5249
|
if (project === DEFAULT_PROJECT) {
|
|
5081
5250
|
return {
|
|
5082
|
-
snapshotPath:
|
|
5083
|
-
errorsPath:
|
|
5084
|
-
staleEventsPath:
|
|
5085
|
-
embeddingsCachePath:
|
|
5086
|
-
policyViolationsPath:
|
|
5251
|
+
snapshotPath: import_node_path35.default.join(baseDir, "graph.json"),
|
|
5252
|
+
errorsPath: import_node_path35.default.join(baseDir, "errors.ndjson"),
|
|
5253
|
+
staleEventsPath: import_node_path35.default.join(baseDir, "stale-events.ndjson"),
|
|
5254
|
+
embeddingsCachePath: import_node_path35.default.join(baseDir, "embeddings.json"),
|
|
5255
|
+
policyViolationsPath: import_node_path35.default.join(baseDir, "policy-violations.ndjson")
|
|
5087
5256
|
};
|
|
5088
5257
|
}
|
|
5089
5258
|
return {
|
|
5090
|
-
snapshotPath:
|
|
5091
|
-
errorsPath:
|
|
5092
|
-
staleEventsPath:
|
|
5093
|
-
embeddingsCachePath:
|
|
5094
|
-
policyViolationsPath:
|
|
5259
|
+
snapshotPath: import_node_path35.default.join(baseDir, `${project}.json`),
|
|
5260
|
+
errorsPath: import_node_path35.default.join(baseDir, `errors.${project}.ndjson`),
|
|
5261
|
+
staleEventsPath: import_node_path35.default.join(baseDir, `stale-events.${project}.ndjson`),
|
|
5262
|
+
embeddingsCachePath: import_node_path35.default.join(baseDir, `embeddings.${project}.json`),
|
|
5263
|
+
policyViolationsPath: import_node_path35.default.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
5095
5264
|
};
|
|
5096
5265
|
}
|
|
5097
5266
|
var Projects = class {
|
|
@@ -5129,19 +5298,19 @@ var Projects = class {
|
|
|
5129
5298
|
init_cjs_shims();
|
|
5130
5299
|
var import_fastify = __toESM(require("fastify"), 1);
|
|
5131
5300
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
5132
|
-
var
|
|
5301
|
+
var import_types25 = require("@neat.is/types");
|
|
5133
5302
|
|
|
5134
5303
|
// src/extend/index.ts
|
|
5135
5304
|
init_cjs_shims();
|
|
5136
5305
|
var import_node_fs21 = require("fs");
|
|
5137
|
-
var
|
|
5306
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
5138
5307
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
5139
5308
|
var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
|
|
5140
5309
|
|
|
5141
5310
|
// src/installers/package-manager.ts
|
|
5142
5311
|
init_cjs_shims();
|
|
5143
5312
|
var import_node_fs20 = require("fs");
|
|
5144
|
-
var
|
|
5313
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
5145
5314
|
var import_node_child_process = require("child_process");
|
|
5146
5315
|
var LOCKFILE_PRIORITY = [
|
|
5147
5316
|
{ lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
|
|
@@ -5163,22 +5332,22 @@ async function exists2(p) {
|
|
|
5163
5332
|
}
|
|
5164
5333
|
}
|
|
5165
5334
|
async function detectPackageManager(serviceDir) {
|
|
5166
|
-
let dir =
|
|
5335
|
+
let dir = import_node_path36.default.resolve(serviceDir);
|
|
5167
5336
|
const stops = /* @__PURE__ */ new Set();
|
|
5168
5337
|
for (let i = 0; i < 64; i++) {
|
|
5169
5338
|
if (stops.has(dir)) break;
|
|
5170
5339
|
stops.add(dir);
|
|
5171
5340
|
for (const candidate of LOCKFILE_PRIORITY) {
|
|
5172
|
-
const lockPath =
|
|
5341
|
+
const lockPath = import_node_path36.default.join(dir, candidate.lockfile);
|
|
5173
5342
|
if (await exists2(lockPath)) {
|
|
5174
5343
|
return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
|
|
5175
5344
|
}
|
|
5176
5345
|
}
|
|
5177
|
-
const parent =
|
|
5346
|
+
const parent = import_node_path36.default.dirname(dir);
|
|
5178
5347
|
if (parent === dir) break;
|
|
5179
5348
|
dir = parent;
|
|
5180
5349
|
}
|
|
5181
|
-
return { pm: "npm", cwd:
|
|
5350
|
+
return { pm: "npm", cwd: import_node_path36.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
|
|
5182
5351
|
}
|
|
5183
5352
|
async function runPackageManagerInstall(cmd) {
|
|
5184
5353
|
return new Promise((resolve) => {
|
|
@@ -5227,7 +5396,7 @@ async function fileExists2(p) {
|
|
|
5227
5396
|
}
|
|
5228
5397
|
}
|
|
5229
5398
|
async function readPackageJson(scanPath) {
|
|
5230
|
-
const pkgPath =
|
|
5399
|
+
const pkgPath = import_node_path37.default.join(scanPath, "package.json");
|
|
5231
5400
|
const raw = await import_node_fs21.promises.readFile(pkgPath, "utf8");
|
|
5232
5401
|
return JSON.parse(raw);
|
|
5233
5402
|
}
|
|
@@ -5238,11 +5407,11 @@ async function findHookFiles(scanPath) {
|
|
|
5238
5407
|
).sort();
|
|
5239
5408
|
}
|
|
5240
5409
|
function extendLogPath() {
|
|
5241
|
-
return process.env.NEAT_EXTEND_LOG ??
|
|
5410
|
+
return process.env.NEAT_EXTEND_LOG ?? import_node_path37.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
|
|
5242
5411
|
}
|
|
5243
5412
|
async function appendExtendLog(entry2) {
|
|
5244
5413
|
const logPath = extendLogPath();
|
|
5245
|
-
await import_node_fs21.promises.mkdir(
|
|
5414
|
+
await import_node_fs21.promises.mkdir(import_node_path37.default.dirname(logPath), { recursive: true });
|
|
5246
5415
|
await import_node_fs21.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
|
|
5247
5416
|
}
|
|
5248
5417
|
function splicedContent(fileContent, snippet2) {
|
|
@@ -5301,7 +5470,7 @@ function lookupInstrumentation(library, installedVersion) {
|
|
|
5301
5470
|
}
|
|
5302
5471
|
async function describeProjectInstrumentation(ctx) {
|
|
5303
5472
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
5304
|
-
const envNeat = await fileExists2(
|
|
5473
|
+
const envNeat = await fileExists2(import_node_path37.default.join(ctx.scanPath, ".env.neat"));
|
|
5305
5474
|
const registryInstrPackages = new Set(
|
|
5306
5475
|
(0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
|
|
5307
5476
|
);
|
|
@@ -5323,16 +5492,16 @@ async function applyExtension(ctx, args, options) {
|
|
|
5323
5492
|
);
|
|
5324
5493
|
}
|
|
5325
5494
|
for (const file of hookFiles) {
|
|
5326
|
-
const content = await import_node_fs21.promises.readFile(
|
|
5495
|
+
const content = await import_node_fs21.promises.readFile(import_node_path37.default.join(ctx.scanPath, file), "utf8");
|
|
5327
5496
|
if (content.includes(args.registration_snippet)) {
|
|
5328
5497
|
return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
|
|
5329
5498
|
}
|
|
5330
5499
|
}
|
|
5331
5500
|
const primaryFile = hookFiles[0];
|
|
5332
|
-
const primaryPath =
|
|
5501
|
+
const primaryPath = import_node_path37.default.join(ctx.scanPath, primaryFile);
|
|
5333
5502
|
const filesTouched = [];
|
|
5334
5503
|
const depsAdded = [];
|
|
5335
|
-
const pkgPath =
|
|
5504
|
+
const pkgPath = import_node_path37.default.join(ctx.scanPath, "package.json");
|
|
5336
5505
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
5337
5506
|
if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
|
|
5338
5507
|
pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
|
|
@@ -5378,7 +5547,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
5378
5547
|
};
|
|
5379
5548
|
}
|
|
5380
5549
|
for (const file of hookFiles) {
|
|
5381
|
-
const content = await import_node_fs21.promises.readFile(
|
|
5550
|
+
const content = await import_node_fs21.promises.readFile(import_node_path37.default.join(ctx.scanPath, file), "utf8");
|
|
5382
5551
|
if (content.includes(args.registration_snippet)) {
|
|
5383
5552
|
return {
|
|
5384
5553
|
library: args.library,
|
|
@@ -5400,7 +5569,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
5400
5569
|
depsToAdd.push(`${args.instrumentation_package}@${args.version}`);
|
|
5401
5570
|
filesTouched.push("package.json");
|
|
5402
5571
|
}
|
|
5403
|
-
const hookContent = await import_node_fs21.promises.readFile(
|
|
5572
|
+
const hookContent = await import_node_fs21.promises.readFile(import_node_path37.default.join(ctx.scanPath, primaryFile), "utf8");
|
|
5404
5573
|
const patched = splicedContent(hookContent, args.registration_snippet);
|
|
5405
5574
|
if (patched) {
|
|
5406
5575
|
filesTouched.push(primaryFile);
|
|
@@ -5421,7 +5590,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
5421
5590
|
if (!match) {
|
|
5422
5591
|
return { undone: false, message: "no apply found for library" };
|
|
5423
5592
|
}
|
|
5424
|
-
const pkgPath =
|
|
5593
|
+
const pkgPath = import_node_path37.default.join(ctx.scanPath, "package.json");
|
|
5425
5594
|
if (await fileExists2(pkgPath)) {
|
|
5426
5595
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
5427
5596
|
if (pkg.dependencies?.[match.instrumentation_package]) {
|
|
@@ -5432,7 +5601,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
5432
5601
|
}
|
|
5433
5602
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
5434
5603
|
for (const file of hookFiles) {
|
|
5435
|
-
const filePath =
|
|
5604
|
+
const filePath = import_node_path37.default.join(ctx.scanPath, file);
|
|
5436
5605
|
const content = await import_node_fs21.promises.readFile(filePath, "utf8");
|
|
5437
5606
|
if (content.includes(match.registration_snippet)) {
|
|
5438
5607
|
const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
|
|
@@ -5448,7 +5617,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
5448
5617
|
|
|
5449
5618
|
// src/divergences.ts
|
|
5450
5619
|
init_cjs_shims();
|
|
5451
|
-
var
|
|
5620
|
+
var import_types23 = require("@neat.is/types");
|
|
5452
5621
|
function bucketKey(source, target, type) {
|
|
5453
5622
|
return `${type}|${source}|${target}`;
|
|
5454
5623
|
}
|
|
@@ -5456,22 +5625,22 @@ function bucketEdges(graph) {
|
|
|
5456
5625
|
const buckets = /* @__PURE__ */ new Map();
|
|
5457
5626
|
graph.forEachEdge((id, attrs) => {
|
|
5458
5627
|
const e = attrs;
|
|
5459
|
-
const parsed = (0,
|
|
5628
|
+
const parsed = (0, import_types23.parseEdgeId)(id);
|
|
5460
5629
|
const provenance = parsed?.provenance ?? e.provenance;
|
|
5461
5630
|
const key = bucketKey(e.source, e.target, e.type);
|
|
5462
5631
|
const cur = buckets.get(key) ?? { source: e.source, target: e.target, type: e.type };
|
|
5463
5632
|
switch (provenance) {
|
|
5464
|
-
case
|
|
5633
|
+
case import_types23.Provenance.EXTRACTED:
|
|
5465
5634
|
cur.extracted = e;
|
|
5466
5635
|
break;
|
|
5467
|
-
case
|
|
5636
|
+
case import_types23.Provenance.OBSERVED:
|
|
5468
5637
|
cur.observed = e;
|
|
5469
5638
|
break;
|
|
5470
|
-
case
|
|
5639
|
+
case import_types23.Provenance.INFERRED:
|
|
5471
5640
|
cur.inferred = e;
|
|
5472
5641
|
break;
|
|
5473
5642
|
default:
|
|
5474
|
-
if (e.provenance ===
|
|
5643
|
+
if (e.provenance === import_types23.Provenance.STALE) cur.stale = e;
|
|
5475
5644
|
}
|
|
5476
5645
|
buckets.set(key, cur);
|
|
5477
5646
|
});
|
|
@@ -5480,7 +5649,7 @@ function bucketEdges(graph) {
|
|
|
5480
5649
|
function nodeIsFrontier(graph, nodeId) {
|
|
5481
5650
|
if (!graph.hasNode(nodeId)) return false;
|
|
5482
5651
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
5483
|
-
return attrs.type ===
|
|
5652
|
+
return attrs.type === import_types23.NodeType.FrontierNode;
|
|
5484
5653
|
}
|
|
5485
5654
|
function clampConfidence(n) {
|
|
5486
5655
|
if (!Number.isFinite(n)) return 0;
|
|
@@ -5501,7 +5670,7 @@ function gradedConfidence(edge) {
|
|
|
5501
5670
|
}
|
|
5502
5671
|
function detectMissingDivergences(graph, bucket) {
|
|
5503
5672
|
const out = [];
|
|
5504
|
-
if (bucket.type ===
|
|
5673
|
+
if (bucket.type === import_types23.EdgeType.CONTAINS) return out;
|
|
5505
5674
|
if (bucket.extracted && !bucket.observed) {
|
|
5506
5675
|
if (!nodeIsFrontier(graph, bucket.target)) {
|
|
5507
5676
|
out.push({
|
|
@@ -5542,7 +5711,7 @@ function declaredHostFor(svc) {
|
|
|
5542
5711
|
function hasExtractedConfiguredBy(graph, svcId) {
|
|
5543
5712
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
5544
5713
|
const e = graph.getEdgeAttributes(edgeId);
|
|
5545
|
-
if (e.type ===
|
|
5714
|
+
if (e.type === import_types23.EdgeType.CONFIGURED_BY && e.provenance === import_types23.Provenance.EXTRACTED) {
|
|
5546
5715
|
return true;
|
|
5547
5716
|
}
|
|
5548
5717
|
}
|
|
@@ -5555,10 +5724,10 @@ function detectHostMismatch(graph, svcId, svc) {
|
|
|
5555
5724
|
const out = [];
|
|
5556
5725
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
5557
5726
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
5558
|
-
if (edge.type !==
|
|
5559
|
-
if (edge.provenance !==
|
|
5727
|
+
if (edge.type !== import_types23.EdgeType.CONNECTS_TO) continue;
|
|
5728
|
+
if (edge.provenance !== import_types23.Provenance.OBSERVED) continue;
|
|
5560
5729
|
const target = graph.getNodeAttributes(edge.target);
|
|
5561
|
-
if (target.type !==
|
|
5730
|
+
if (target.type !== import_types23.NodeType.DatabaseNode) continue;
|
|
5562
5731
|
const observedHost = target.host?.trim();
|
|
5563
5732
|
if (!observedHost) continue;
|
|
5564
5733
|
if (observedHost === declaredHost) continue;
|
|
@@ -5580,10 +5749,10 @@ function detectCompatDivergences(graph, svcId, svc) {
|
|
|
5580
5749
|
const deps = svc.dependencies ?? {};
|
|
5581
5750
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
5582
5751
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
5583
|
-
if (edge.type !==
|
|
5584
|
-
if (edge.provenance !==
|
|
5752
|
+
if (edge.type !== import_types23.EdgeType.CONNECTS_TO) continue;
|
|
5753
|
+
if (edge.provenance !== import_types23.Provenance.OBSERVED) continue;
|
|
5585
5754
|
const target = graph.getNodeAttributes(edge.target);
|
|
5586
|
-
if (target.type !==
|
|
5755
|
+
if (target.type !== import_types23.NodeType.DatabaseNode) continue;
|
|
5587
5756
|
for (const pair of compatPairs()) {
|
|
5588
5757
|
if (pair.engine !== target.engine) continue;
|
|
5589
5758
|
const declared = deps[pair.driver];
|
|
@@ -5644,7 +5813,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
5644
5813
|
}
|
|
5645
5814
|
graph.forEachNode((nodeId, attrs) => {
|
|
5646
5815
|
const n = attrs;
|
|
5647
|
-
if (n.type !==
|
|
5816
|
+
if (n.type !== import_types23.NodeType.ServiceNode) return;
|
|
5648
5817
|
const svc = n;
|
|
5649
5818
|
for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
|
|
5650
5819
|
for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
|
|
@@ -5677,7 +5846,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
5677
5846
|
if (a.source !== b.source) return a.source.localeCompare(b.source);
|
|
5678
5847
|
return a.target.localeCompare(b.target);
|
|
5679
5848
|
});
|
|
5680
|
-
return
|
|
5849
|
+
return import_types23.DivergenceResultSchema.parse({
|
|
5681
5850
|
divergences: filtered,
|
|
5682
5851
|
totalAffected: filtered.length,
|
|
5683
5852
|
computedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -5765,23 +5934,23 @@ function canonicalJson(value) {
|
|
|
5765
5934
|
init_cjs_shims();
|
|
5766
5935
|
var import_node_fs23 = require("fs");
|
|
5767
5936
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
5768
|
-
var
|
|
5769
|
-
var
|
|
5937
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
5938
|
+
var import_types24 = require("@neat.is/types");
|
|
5770
5939
|
var LOCK_TIMEOUT_MS = 5e3;
|
|
5771
5940
|
var LOCK_RETRY_MS = 50;
|
|
5772
5941
|
function neatHome() {
|
|
5773
5942
|
const override = process.env.NEAT_HOME;
|
|
5774
|
-
if (override && override.length > 0) return
|
|
5775
|
-
return
|
|
5943
|
+
if (override && override.length > 0) return import_node_path38.default.resolve(override);
|
|
5944
|
+
return import_node_path38.default.join(import_node_os3.default.homedir(), ".neat");
|
|
5776
5945
|
}
|
|
5777
5946
|
function registryPath() {
|
|
5778
|
-
return
|
|
5947
|
+
return import_node_path38.default.join(neatHome(), "projects.json");
|
|
5779
5948
|
}
|
|
5780
5949
|
function registryLockPath() {
|
|
5781
|
-
return
|
|
5950
|
+
return import_node_path38.default.join(neatHome(), "projects.json.lock");
|
|
5782
5951
|
}
|
|
5783
5952
|
function daemonPidPath() {
|
|
5784
|
-
return
|
|
5953
|
+
return import_node_path38.default.join(neatHome(), "neatd.pid");
|
|
5785
5954
|
}
|
|
5786
5955
|
function isPidAliveDefault(pid) {
|
|
5787
5956
|
try {
|
|
@@ -5841,7 +6010,7 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
|
|
|
5841
6010
|
}
|
|
5842
6011
|
}
|
|
5843
6012
|
async function writeAtomically(target, contents) {
|
|
5844
|
-
await import_node_fs23.promises.mkdir(
|
|
6013
|
+
await import_node_fs23.promises.mkdir(import_node_path38.default.dirname(target), { recursive: true });
|
|
5845
6014
|
const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
5846
6015
|
const fd = await import_node_fs23.promises.open(tmp, "w");
|
|
5847
6016
|
try {
|
|
@@ -5854,7 +6023,7 @@ async function writeAtomically(target, contents) {
|
|
|
5854
6023
|
}
|
|
5855
6024
|
async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
|
|
5856
6025
|
const deadline = Date.now() + timeoutMs;
|
|
5857
|
-
await import_node_fs23.promises.mkdir(
|
|
6026
|
+
await import_node_fs23.promises.mkdir(import_node_path38.default.dirname(lockPath), { recursive: true });
|
|
5858
6027
|
let probedHolder = false;
|
|
5859
6028
|
while (true) {
|
|
5860
6029
|
try {
|
|
@@ -5907,10 +6076,10 @@ async function readRegistry() {
|
|
|
5907
6076
|
throw err;
|
|
5908
6077
|
}
|
|
5909
6078
|
const parsed = JSON.parse(raw);
|
|
5910
|
-
return
|
|
6079
|
+
return import_types24.RegistryFileSchema.parse(parsed);
|
|
5911
6080
|
}
|
|
5912
6081
|
async function writeRegistry(reg) {
|
|
5913
|
-
const validated =
|
|
6082
|
+
const validated = import_types24.RegistryFileSchema.parse(reg);
|
|
5914
6083
|
await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
|
|
5915
6084
|
}
|
|
5916
6085
|
async function getProject(name) {
|
|
@@ -5940,6 +6109,58 @@ async function touchLastSeen(name, at = (/* @__PURE__ */ new Date()).toISOString
|
|
|
5940
6109
|
await writeRegistry(reg);
|
|
5941
6110
|
});
|
|
5942
6111
|
}
|
|
6112
|
+
var DAY_MS2 = 24 * 60 * 60 * 1e3;
|
|
6113
|
+
var DEFAULT_PRUNE_TTL_MS = 7 * DAY_MS2;
|
|
6114
|
+
function pruneTtlMs() {
|
|
6115
|
+
const raw = process.env.NEAT_REGISTRY_PRUNE_TTL_MS;
|
|
6116
|
+
if (!raw) return DEFAULT_PRUNE_TTL_MS;
|
|
6117
|
+
const n = Number.parseInt(raw, 10);
|
|
6118
|
+
if (Number.isFinite(n) && n >= 0) return n;
|
|
6119
|
+
console.warn(
|
|
6120
|
+
`[neat] NEAT_REGISTRY_PRUNE_TTL_MS could not be parsed (${raw}); using default ${DEFAULT_PRUNE_TTL_MS}ms`
|
|
6121
|
+
);
|
|
6122
|
+
return DEFAULT_PRUNE_TTL_MS;
|
|
6123
|
+
}
|
|
6124
|
+
async function statPathStatus(p) {
|
|
6125
|
+
try {
|
|
6126
|
+
const stat = await import_node_fs23.promises.stat(p);
|
|
6127
|
+
return stat.isDirectory() ? "present" : "unknown";
|
|
6128
|
+
} catch (err) {
|
|
6129
|
+
return err.code === "ENOENT" ? "gone" : "unknown";
|
|
6130
|
+
}
|
|
6131
|
+
}
|
|
6132
|
+
async function pruneRegistry(opts = {}) {
|
|
6133
|
+
const ttlMs = opts.ttlMs ?? pruneTtlMs();
|
|
6134
|
+
const statPath = opts.statPath ?? statPathStatus;
|
|
6135
|
+
const now = opts.now ?? Date.now;
|
|
6136
|
+
return withLock(async () => {
|
|
6137
|
+
const reg = await readRegistry();
|
|
6138
|
+
const removed = [];
|
|
6139
|
+
const kept = [];
|
|
6140
|
+
for (const entry2 of reg.projects) {
|
|
6141
|
+
const status2 = await statPath(entry2.path);
|
|
6142
|
+
if (status2 !== "gone") {
|
|
6143
|
+
kept.push(entry2);
|
|
6144
|
+
continue;
|
|
6145
|
+
}
|
|
6146
|
+
if (ttlMs <= 0) {
|
|
6147
|
+
removed.push(entry2);
|
|
6148
|
+
continue;
|
|
6149
|
+
}
|
|
6150
|
+
const lastSeen = Date.parse(entry2.lastSeenAt ?? entry2.registeredAt);
|
|
6151
|
+
const age = now() - (Number.isFinite(lastSeen) ? lastSeen : 0);
|
|
6152
|
+
if (age > ttlMs) {
|
|
6153
|
+
removed.push(entry2);
|
|
6154
|
+
} else {
|
|
6155
|
+
kept.push(entry2);
|
|
6156
|
+
}
|
|
6157
|
+
}
|
|
6158
|
+
if (removed.length === 0) return [];
|
|
6159
|
+
reg.projects = kept;
|
|
6160
|
+
await writeRegistry(reg);
|
|
6161
|
+
return removed;
|
|
6162
|
+
});
|
|
6163
|
+
}
|
|
5943
6164
|
|
|
5944
6165
|
// src/streaming.ts
|
|
5945
6166
|
init_cjs_shims();
|
|
@@ -5953,6 +6174,7 @@ function handleSse(req2, reply, opts) {
|
|
|
5953
6174
|
reply.raw.setHeader("Connection", "keep-alive");
|
|
5954
6175
|
reply.raw.setHeader("X-Accel-Buffering", "no");
|
|
5955
6176
|
reply.raw.flushHeaders?.();
|
|
6177
|
+
reply.raw.write(":open\n\n");
|
|
5956
6178
|
let pending = 0;
|
|
5957
6179
|
let dropped = false;
|
|
5958
6180
|
const closeConnection = () => {
|
|
@@ -6132,11 +6354,11 @@ function registerRoutes(scope, ctx) {
|
|
|
6132
6354
|
const candidates = req2.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
6133
6355
|
const parsed = [];
|
|
6134
6356
|
for (const c of candidates) {
|
|
6135
|
-
const r =
|
|
6357
|
+
const r = import_types25.DivergenceTypeSchema.safeParse(c);
|
|
6136
6358
|
if (!r.success) {
|
|
6137
6359
|
return reply.code(400).send({
|
|
6138
6360
|
error: `unknown divergence type "${c}"`,
|
|
6139
|
-
allowed:
|
|
6361
|
+
allowed: import_types25.DivergenceTypeSchema.options
|
|
6140
6362
|
});
|
|
6141
6363
|
}
|
|
6142
6364
|
parsed.push(r.data);
|
|
@@ -6349,7 +6571,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6349
6571
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
6350
6572
|
let violations = await log.readAll();
|
|
6351
6573
|
if (req2.query.severity) {
|
|
6352
|
-
const sev =
|
|
6574
|
+
const sev = import_types25.PolicySeveritySchema.safeParse(req2.query.severity);
|
|
6353
6575
|
if (!sev.success) {
|
|
6354
6576
|
return reply.code(400).send({
|
|
6355
6577
|
error: "invalid severity",
|
|
@@ -6366,7 +6588,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6366
6588
|
scope.post("/policies/check", async (req2, reply) => {
|
|
6367
6589
|
const proj = resolveProject(registry, req2, reply, ctx.bootstrap);
|
|
6368
6590
|
if (!proj) return;
|
|
6369
|
-
const parsed =
|
|
6591
|
+
const parsed = import_types25.PoliciesCheckBodySchema.safeParse(req2.body ?? {});
|
|
6370
6592
|
if (!parsed.success) {
|
|
6371
6593
|
return reply.code(400).send({
|
|
6372
6594
|
error: "invalid /policies/check body",
|
|
@@ -6609,7 +6831,7 @@ init_auth();
|
|
|
6609
6831
|
// src/unrouted.ts
|
|
6610
6832
|
init_cjs_shims();
|
|
6611
6833
|
var import_node_fs24 = require("fs");
|
|
6612
|
-
var
|
|
6834
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
6613
6835
|
function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new Date()) {
|
|
6614
6836
|
return {
|
|
6615
6837
|
timestamp: now.toISOString(),
|
|
@@ -6619,12 +6841,12 @@ function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new
|
|
|
6619
6841
|
};
|
|
6620
6842
|
}
|
|
6621
6843
|
async function appendUnroutedSpan(neatHome3, record) {
|
|
6622
|
-
const target =
|
|
6844
|
+
const target = import_node_path41.default.join(neatHome3, "errors.ndjson");
|
|
6623
6845
|
await import_node_fs24.promises.mkdir(neatHome3, { recursive: true });
|
|
6624
6846
|
await import_node_fs24.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
|
|
6625
6847
|
}
|
|
6626
6848
|
function unroutedErrorsPath(neatHome3) {
|
|
6627
|
-
return
|
|
6849
|
+
return import_node_path41.default.join(neatHome3, "errors.ndjson");
|
|
6628
6850
|
}
|
|
6629
6851
|
|
|
6630
6852
|
// src/daemon.ts
|
|
@@ -6639,11 +6861,11 @@ function teardownSlot(slot) {
|
|
|
6639
6861
|
}
|
|
6640
6862
|
}
|
|
6641
6863
|
function neatHomeFor(opts) {
|
|
6642
|
-
if (opts.neatHome && opts.neatHome.length > 0) return
|
|
6864
|
+
if (opts.neatHome && opts.neatHome.length > 0) return import_node_path42.default.resolve(opts.neatHome);
|
|
6643
6865
|
const env = process.env.NEAT_HOME;
|
|
6644
|
-
if (env && env.length > 0) return
|
|
6866
|
+
if (env && env.length > 0) return import_node_path42.default.resolve(env);
|
|
6645
6867
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
6646
|
-
return
|
|
6868
|
+
return import_node_path42.default.join(home, ".neat");
|
|
6647
6869
|
}
|
|
6648
6870
|
function routeSpanToProject(serviceName, projects) {
|
|
6649
6871
|
if (!serviceName) return DEFAULT_PROJECT;
|
|
@@ -6678,7 +6900,7 @@ function isTokenContained(needle, haystack) {
|
|
|
6678
6900
|
return tokens.includes(needle);
|
|
6679
6901
|
}
|
|
6680
6902
|
async function bootstrapProject(entry2) {
|
|
6681
|
-
const paths = pathsForProject(entry2.name,
|
|
6903
|
+
const paths = pathsForProject(entry2.name, import_node_path42.default.join(entry2.path, "neat-out"));
|
|
6682
6904
|
try {
|
|
6683
6905
|
const stat = await import_node_fs25.promises.stat(entry2.path);
|
|
6684
6906
|
if (!stat.isDirectory()) {
|
|
@@ -6761,7 +6983,7 @@ async function startDaemon(opts = {}) {
|
|
|
6761
6983
|
`neatd: registry not found at ${regPath}. Run \`neat init <path>\` to register a project before starting the daemon.`
|
|
6762
6984
|
);
|
|
6763
6985
|
}
|
|
6764
|
-
const pidPath =
|
|
6986
|
+
const pidPath = import_node_path42.default.join(home, "neatd.pid");
|
|
6765
6987
|
await writeAtomically(pidPath, `${process.pid}
|
|
6766
6988
|
`);
|
|
6767
6989
|
const slots = /* @__PURE__ */ new Map();
|
|
@@ -6850,6 +7072,19 @@ async function startDaemon(opts = {}) {
|
|
|
6850
7072
|
}
|
|
6851
7073
|
}
|
|
6852
7074
|
async function loadAll() {
|
|
7075
|
+
try {
|
|
7076
|
+
const pruned = await pruneRegistry();
|
|
7077
|
+
for (const entry2 of pruned) {
|
|
7078
|
+
console.log(
|
|
7079
|
+
`neatd: pruned project "${entry2.name}" \u2014 registered path ${entry2.path} is gone`
|
|
7080
|
+
);
|
|
7081
|
+
slots.delete(entry2.name);
|
|
7082
|
+
bootstrapStatus.delete(entry2.name);
|
|
7083
|
+
bootstrapStartedAt.delete(entry2.name);
|
|
7084
|
+
}
|
|
7085
|
+
} catch (err) {
|
|
7086
|
+
console.warn(`neatd: registry prune skipped \u2014 ${err.message}`);
|
|
7087
|
+
}
|
|
6853
7088
|
const projects = await listProjects();
|
|
6854
7089
|
const seen = /* @__PURE__ */ new Set();
|
|
6855
7090
|
const pending = [];
|
|
@@ -7066,8 +7301,8 @@ async function startDaemon(opts = {}) {
|
|
|
7066
7301
|
let registryWatcher = null;
|
|
7067
7302
|
let reloadTimer = null;
|
|
7068
7303
|
try {
|
|
7069
|
-
const regDir =
|
|
7070
|
-
const regBase =
|
|
7304
|
+
const regDir = import_node_path42.default.dirname(regPath);
|
|
7305
|
+
const regBase = import_node_path42.default.basename(regPath);
|
|
7071
7306
|
registryWatcher = (0, import_node_fs25.watch)(regDir, (_eventType, filename) => {
|
|
7072
7307
|
if (filename !== null && filename !== regBase) return;
|
|
7073
7308
|
if (reloadTimer) clearTimeout(reloadTimer);
|
|
@@ -7130,7 +7365,7 @@ init_auth();
|
|
|
7130
7365
|
init_cjs_shims();
|
|
7131
7366
|
var import_node_child_process2 = require("child_process");
|
|
7132
7367
|
var import_node_net = __toESM(require("net"), 1);
|
|
7133
|
-
var
|
|
7368
|
+
var import_node_path43 = __toESM(require("path"), 1);
|
|
7134
7369
|
var DEFAULT_WEB_PORT = 6328;
|
|
7135
7370
|
async function assertPortFree(port) {
|
|
7136
7371
|
await new Promise((resolve, reject) => {
|
|
@@ -7158,10 +7393,10 @@ function resolveWebPackageDir() {
|
|
|
7158
7393
|
eval("require")
|
|
7159
7394
|
);
|
|
7160
7395
|
const pkgJsonPath = req.resolve("@neat.is/web/package.json");
|
|
7161
|
-
return
|
|
7396
|
+
return import_node_path43.default.dirname(pkgJsonPath);
|
|
7162
7397
|
}
|
|
7163
7398
|
function resolveStandaloneServerEntry(webDir) {
|
|
7164
|
-
return
|
|
7399
|
+
return import_node_path43.default.join(webDir, ".next/standalone/packages/web/server.js");
|
|
7165
7400
|
}
|
|
7166
7401
|
async function spawnWebUI(restPort) {
|
|
7167
7402
|
const portRaw = process.env.NEAT_WEB_PORT;
|
|
@@ -7186,7 +7421,7 @@ async function spawnWebUI(restPort) {
|
|
|
7186
7421
|
NEAT_API_URL: process.env.NEAT_API_URL ?? `http://localhost:${restPort}`
|
|
7187
7422
|
};
|
|
7188
7423
|
const child = (0, import_node_child_process2.spawn)(process.execPath, [serverEntry], {
|
|
7189
|
-
cwd:
|
|
7424
|
+
cwd: import_node_path43.default.dirname(serverEntry),
|
|
7190
7425
|
env,
|
|
7191
7426
|
stdio: ["ignore", "inherit", "inherit"],
|
|
7192
7427
|
detached: false
|
|
@@ -7299,14 +7534,14 @@ function localVersion() {
|
|
|
7299
7534
|
}
|
|
7300
7535
|
function neatHome2() {
|
|
7301
7536
|
if (process.env.NEAT_HOME && process.env.NEAT_HOME.length > 0) {
|
|
7302
|
-
return
|
|
7537
|
+
return import_node_path44.default.resolve(process.env.NEAT_HOME);
|
|
7303
7538
|
}
|
|
7304
7539
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
7305
|
-
return
|
|
7540
|
+
return import_node_path44.default.join(home, ".neat");
|
|
7306
7541
|
}
|
|
7307
7542
|
async function readPid() {
|
|
7308
7543
|
try {
|
|
7309
|
-
const raw = await import_node_fs26.promises.readFile(
|
|
7544
|
+
const raw = await import_node_fs26.promises.readFile(import_node_path44.default.join(neatHome2(), "neatd.pid"), "utf8");
|
|
7310
7545
|
const n = Number.parseInt(raw.trim(), 10);
|
|
7311
7546
|
return Number.isFinite(n) ? n : null;
|
|
7312
7547
|
} catch {
|