@neat.is/core 0.4.16 → 0.4.18
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-6CO7C4IU.js → chunk-BGPWBRLU.js} +4 -2
- package/dist/{chunk-6CO7C4IU.js.map → chunk-BGPWBRLU.js.map} +1 -1
- package/dist/{chunk-VMLWUK7W.js → chunk-GXWBMJDJ.js} +210 -11
- package/dist/chunk-GXWBMJDJ.js.map +1 -0
- package/dist/{chunk-GHPHVXYM.js → chunk-MTXF77TN.js} +2 -2
- package/dist/{chunk-XS4CGNRO.js → chunk-T3X6XJPU.js} +403 -72
- package/dist/chunk-T3X6XJPU.js.map +1 -0
- package/dist/cli.cjs +964 -364
- 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 +331 -86
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +559 -150
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +51 -13
- package/dist/index.d.ts +51 -13
- package/dist/index.js +4 -4
- package/dist/neatd.cjs +726 -193
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +161 -37
- package/dist/neatd.js.map +1 -1
- package/dist/{otel-grpc-QAISVAY5.js → otel-grpc-I67ONCRM.js} +3 -3
- package/dist/server.cjs +271 -97
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +3 -3
- package/package.json +2 -2
- package/dist/chunk-VMLWUK7W.js.map +0 -1
- package/dist/chunk-XS4CGNRO.js.map +0 -1
- /package/dist/{chunk-GHPHVXYM.js.map → chunk-MTXF77TN.js.map} +0 -0
- /package/dist/{otel-grpc-QAISVAY5.js.map → otel-grpc-I67ONCRM.js.map} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
mountBearerAuth,
|
|
3
3
|
readAuthEnv
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-BGPWBRLU.js";
|
|
5
5
|
|
|
6
6
|
// src/graph.ts
|
|
7
7
|
import GraphDefault from "graphology";
|
|
@@ -454,19 +454,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
454
454
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
455
455
|
let best = { path: [start], edges: [] };
|
|
456
456
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
457
|
-
function step(node,
|
|
458
|
-
if (
|
|
459
|
-
best = { path: [...
|
|
457
|
+
function step(node, path39, edges) {
|
|
458
|
+
if (path39.length > best.path.length) {
|
|
459
|
+
best = { path: [...path39], edges: [...edges] };
|
|
460
460
|
}
|
|
461
|
-
if (
|
|
461
|
+
if (path39.length - 1 >= maxDepth) return;
|
|
462
462
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
463
463
|
for (const [srcId, edge] of incoming) {
|
|
464
464
|
if (visited.has(srcId)) continue;
|
|
465
465
|
visited.add(srcId);
|
|
466
|
-
|
|
466
|
+
path39.push(srcId);
|
|
467
467
|
edges.push(edge);
|
|
468
|
-
step(srcId,
|
|
469
|
-
|
|
468
|
+
step(srcId, path39, edges);
|
|
469
|
+
path39.pop();
|
|
470
470
|
edges.pop();
|
|
471
471
|
visited.delete(srcId);
|
|
472
472
|
}
|
|
@@ -1093,6 +1093,41 @@ function thresholdForEdgeType(edgeType, overrides) {
|
|
|
1093
1093
|
const map = overrides ?? loadStaleThresholdsFromEnv();
|
|
1094
1094
|
return map[edgeType] ?? FALLBACK_STALE_THRESHOLD_MS;
|
|
1095
1095
|
}
|
|
1096
|
+
var DEFAULT_INCIDENT_THRESHOLDS = {
|
|
1097
|
+
threshold: 5,
|
|
1098
|
+
windowMs: 6e4
|
|
1099
|
+
};
|
|
1100
|
+
function loadIncidentThresholdsFromEnv() {
|
|
1101
|
+
const raw = process.env.NEAT_INCIDENT_THRESHOLDS;
|
|
1102
|
+
if (!raw) return DEFAULT_INCIDENT_THRESHOLDS;
|
|
1103
|
+
try {
|
|
1104
|
+
const overrides = JSON.parse(raw);
|
|
1105
|
+
const merged = { ...DEFAULT_INCIDENT_THRESHOLDS };
|
|
1106
|
+
if (typeof overrides.threshold === "number" && Number.isFinite(overrides.threshold) && overrides.threshold >= 1) {
|
|
1107
|
+
merged.threshold = Math.floor(overrides.threshold);
|
|
1108
|
+
}
|
|
1109
|
+
if (typeof overrides.windowMs === "number" && Number.isFinite(overrides.windowMs) && overrides.windowMs >= 0) {
|
|
1110
|
+
merged.windowMs = overrides.windowMs;
|
|
1111
|
+
}
|
|
1112
|
+
return merged;
|
|
1113
|
+
} catch (err) {
|
|
1114
|
+
console.warn(
|
|
1115
|
+
`[neat] NEAT_INCIDENT_THRESHOLDS could not be parsed (${err.message}); using defaults`
|
|
1116
|
+
);
|
|
1117
|
+
return DEFAULT_INCIDENT_THRESHOLDS;
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
function httpResponseStatus(span) {
|
|
1121
|
+
for (const key of ["http.response.status_code", "http.status_code"]) {
|
|
1122
|
+
const v = span.attributes[key];
|
|
1123
|
+
if (typeof v === "number" && Number.isFinite(v)) return v;
|
|
1124
|
+
if (typeof v === "string") {
|
|
1125
|
+
const n = Number(v);
|
|
1126
|
+
if (Number.isFinite(n)) return n;
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
return void 0;
|
|
1130
|
+
}
|
|
1096
1131
|
function nowIso(ctx) {
|
|
1097
1132
|
return new Date(ctx.now ? ctx.now() : Date.now()).toISOString();
|
|
1098
1133
|
}
|
|
@@ -1506,6 +1541,71 @@ function makeErrorSpanWriter(errorsPath) {
|
|
|
1506
1541
|
await fs3.appendFile(errorsPath, JSON.stringify(ev) + "\n", "utf8");
|
|
1507
1542
|
};
|
|
1508
1543
|
}
|
|
1544
|
+
async function recordFailingResponseIncident(ctx, span, affectedNode, timestamp, statusCode, count, firstTimestamp) {
|
|
1545
|
+
const attrs = sanitizeAttributes(span.attributes);
|
|
1546
|
+
const first = firstTimestamp ?? timestamp;
|
|
1547
|
+
const peer = pickAddress(span);
|
|
1548
|
+
const message = count > 1 ? `${count} consecutive HTTP ${statusCode} responses` + (peer ? ` to ${peer}` : "") : `HTTP ${statusCode} response` + (peer ? ` from ${peer}` : "");
|
|
1549
|
+
const ev = {
|
|
1550
|
+
id: `${span.traceId}:${span.spanId}`,
|
|
1551
|
+
timestamp,
|
|
1552
|
+
service: span.service,
|
|
1553
|
+
traceId: span.traceId,
|
|
1554
|
+
spanId: span.spanId,
|
|
1555
|
+
errorType: "http-failure",
|
|
1556
|
+
errorMessage: message,
|
|
1557
|
+
...Object.keys(attrs).length > 0 ? { attributes: attrs } : {},
|
|
1558
|
+
affectedNode,
|
|
1559
|
+
httpStatusCode: statusCode,
|
|
1560
|
+
incidentCount: count,
|
|
1561
|
+
firstTimestamp: first,
|
|
1562
|
+
lastTimestamp: timestamp
|
|
1563
|
+
};
|
|
1564
|
+
await appendErrorEvent(ctx, ev);
|
|
1565
|
+
}
|
|
1566
|
+
async function advance4xxBurst(ctx, span, affectedNode, ts, nowMs, status) {
|
|
1567
|
+
const { threshold, windowMs } = loadIncidentThresholdsFromEnv();
|
|
1568
|
+
if (!ctx.burstState) ctx.burstState = /* @__PURE__ */ new Map();
|
|
1569
|
+
const peer = pickAddress(span) ?? span.spanId;
|
|
1570
|
+
const key = `${span.service}->${peer}`;
|
|
1571
|
+
const existing = ctx.burstState.get(key);
|
|
1572
|
+
let state;
|
|
1573
|
+
if (existing && nowMs - existing.lastMs <= windowMs) {
|
|
1574
|
+
existing.count += 1;
|
|
1575
|
+
existing.lastTs = ts;
|
|
1576
|
+
existing.lastMs = nowMs;
|
|
1577
|
+
existing.codes.set(status, (existing.codes.get(status) ?? 0) + 1);
|
|
1578
|
+
state = existing;
|
|
1579
|
+
} else {
|
|
1580
|
+
state = {
|
|
1581
|
+
count: 1,
|
|
1582
|
+
firstTs: ts,
|
|
1583
|
+
lastTs: ts,
|
|
1584
|
+
lastMs: nowMs,
|
|
1585
|
+
codes: /* @__PURE__ */ new Map([[status, 1]])
|
|
1586
|
+
};
|
|
1587
|
+
ctx.burstState.set(key, state);
|
|
1588
|
+
}
|
|
1589
|
+
if (state.count < threshold) return;
|
|
1590
|
+
let dominant = status;
|
|
1591
|
+
let max = 0;
|
|
1592
|
+
for (const [code, n] of state.codes) {
|
|
1593
|
+
if (n > max) {
|
|
1594
|
+
max = n;
|
|
1595
|
+
dominant = code;
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
await recordFailingResponseIncident(
|
|
1599
|
+
ctx,
|
|
1600
|
+
span,
|
|
1601
|
+
affectedNode,
|
|
1602
|
+
state.lastTs,
|
|
1603
|
+
dominant,
|
|
1604
|
+
state.count,
|
|
1605
|
+
state.firstTs
|
|
1606
|
+
);
|
|
1607
|
+
ctx.burstState.delete(key);
|
|
1608
|
+
}
|
|
1509
1609
|
async function handleSpan(ctx, span) {
|
|
1510
1610
|
const ts = span.startTimeIso ?? nowIso(ctx);
|
|
1511
1611
|
const nowMs = ctx.now ? ctx.now() : Date.now();
|
|
@@ -1604,6 +1704,14 @@ async function handleSpan(ctx, span) {
|
|
|
1604
1704
|
await appendErrorEvent(ctx, ev);
|
|
1605
1705
|
}
|
|
1606
1706
|
}
|
|
1707
|
+
if (span.statusCode !== 2) {
|
|
1708
|
+
const status = httpResponseStatus(span);
|
|
1709
|
+
if (status !== void 0 && status >= 500) {
|
|
1710
|
+
await recordFailingResponseIncident(ctx, span, sourceId, ts, status, 1);
|
|
1711
|
+
} else if (status !== void 0 && status >= 400 && spanMintsObservedEdge(span.kind)) {
|
|
1712
|
+
await advance4xxBurst(ctx, span, sourceId, ts, nowMs, status);
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1607
1715
|
void affectedNode;
|
|
1608
1716
|
if (ctx.onPolicyTrigger) await ctx.onPolicyTrigger(ctx.graph);
|
|
1609
1717
|
}
|
|
@@ -3995,6 +4103,65 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
3995
4103
|
return out;
|
|
3996
4104
|
}
|
|
3997
4105
|
|
|
4106
|
+
// src/extract/calls/supabase.ts
|
|
4107
|
+
import path27 from "path";
|
|
4108
|
+
import { infraId as infraId5 } from "@neat.is/types";
|
|
4109
|
+
var SUPABASE_JS_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/supabase-js['"`]/;
|
|
4110
|
+
var SUPABASE_SSR_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/ssr['"`]/;
|
|
4111
|
+
var SUPABASE_CLIENT_RE = /\b(createClient|createServerClient|createBrowserClient)\s*\(\s*(?:['"`]([^'"`]*)['"`])?/g;
|
|
4112
|
+
function hostFromLiteral(literal) {
|
|
4113
|
+
if (!literal) return null;
|
|
4114
|
+
const m = /^https?:\/\/([^/:'"`\s]+)/.exec(literal.trim());
|
|
4115
|
+
if (!m) return null;
|
|
4116
|
+
const host = m[1];
|
|
4117
|
+
if (!/\.supabase\.(co|in)$/i.test(host)) return null;
|
|
4118
|
+
return host;
|
|
4119
|
+
}
|
|
4120
|
+
function readImports2(content) {
|
|
4121
|
+
return {
|
|
4122
|
+
hasSupabaseJs: SUPABASE_JS_IMPORT_RE.test(content),
|
|
4123
|
+
hasSupabaseSsr: SUPABASE_SSR_IMPORT_RE.test(content)
|
|
4124
|
+
};
|
|
4125
|
+
}
|
|
4126
|
+
function constructorMatchesImport(name, ctx) {
|
|
4127
|
+
if (name === "createClient") return ctx.hasSupabaseJs;
|
|
4128
|
+
return ctx.hasSupabaseSsr;
|
|
4129
|
+
}
|
|
4130
|
+
function supabaseEndpointsFromFile(file, serviceDir) {
|
|
4131
|
+
const ctx = readImports2(file.content);
|
|
4132
|
+
if (!ctx.hasSupabaseJs && !ctx.hasSupabaseSsr) return [];
|
|
4133
|
+
const out = [];
|
|
4134
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4135
|
+
SUPABASE_CLIENT_RE.lastIndex = 0;
|
|
4136
|
+
let m;
|
|
4137
|
+
while ((m = SUPABASE_CLIENT_RE.exec(file.content)) !== null) {
|
|
4138
|
+
const ctor = m[1];
|
|
4139
|
+
if (!constructorMatchesImport(ctor, ctx)) continue;
|
|
4140
|
+
const host = hostFromLiteral(m[2]);
|
|
4141
|
+
const name = host ?? "env";
|
|
4142
|
+
if (seen.has(name)) continue;
|
|
4143
|
+
seen.add(name);
|
|
4144
|
+
const line = lineOf(file.content, m[0]);
|
|
4145
|
+
out.push({
|
|
4146
|
+
infraId: infraId5("supabase", name),
|
|
4147
|
+
name,
|
|
4148
|
+
kind: "supabase",
|
|
4149
|
+
edgeType: "CALLS",
|
|
4150
|
+
// `createClient(...)` from @supabase/supabase-js (or createServerClient /
|
|
4151
|
+
// createBrowserClient from @supabase/ssr) with the import in scope — a
|
|
4152
|
+
// framework-aware recognizer matched the SDK shape. Verified-call-site
|
|
4153
|
+
// tier (ADR-066), the same grade aws.ts / grpc.ts emit at.
|
|
4154
|
+
confidenceKind: "verified-call-site",
|
|
4155
|
+
evidence: {
|
|
4156
|
+
file: path27.relative(serviceDir, file.path),
|
|
4157
|
+
line,
|
|
4158
|
+
snippet: snippet(file.content, line)
|
|
4159
|
+
}
|
|
4160
|
+
});
|
|
4161
|
+
}
|
|
4162
|
+
return out;
|
|
4163
|
+
}
|
|
4164
|
+
|
|
3998
4165
|
// src/extract/calls/index.ts
|
|
3999
4166
|
function edgeTypeFromEndpoint(ep) {
|
|
4000
4167
|
switch (ep.edgeType) {
|
|
@@ -4023,6 +4190,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
4023
4190
|
endpoints.push(...redisEndpointsFromFile(maskedFile, service.dir));
|
|
4024
4191
|
endpoints.push(...awsEndpointsFromFile(maskedFile, service.dir));
|
|
4025
4192
|
endpoints.push(...grpcEndpointsFromFile(maskedFile, service.dir));
|
|
4193
|
+
endpoints.push(...supabaseEndpointsFromFile(maskedFile, service.dir));
|
|
4026
4194
|
}
|
|
4027
4195
|
if (endpoints.length === 0) continue;
|
|
4028
4196
|
const seenEdges = /* @__PURE__ */ new Set();
|
|
@@ -4093,14 +4261,14 @@ async function addCallEdges(graph, services) {
|
|
|
4093
4261
|
}
|
|
4094
4262
|
|
|
4095
4263
|
// src/extract/infra/docker-compose.ts
|
|
4096
|
-
import
|
|
4264
|
+
import path28 from "path";
|
|
4097
4265
|
import { EdgeType as EdgeType10, Provenance as Provenance8, confidenceForExtracted as confidenceForExtracted7 } from "@neat.is/types";
|
|
4098
4266
|
|
|
4099
4267
|
// src/extract/infra/shared.ts
|
|
4100
|
-
import { NodeType as NodeType10, infraId as
|
|
4268
|
+
import { NodeType as NodeType10, infraId as infraId6 } from "@neat.is/types";
|
|
4101
4269
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
4102
4270
|
return {
|
|
4103
|
-
id:
|
|
4271
|
+
id: infraId6(kind, name),
|
|
4104
4272
|
type: NodeType10.InfraNode,
|
|
4105
4273
|
name,
|
|
4106
4274
|
provider,
|
|
@@ -4130,7 +4298,7 @@ function dependsOnList(value) {
|
|
|
4130
4298
|
}
|
|
4131
4299
|
function serviceNameToServiceNode(name, services) {
|
|
4132
4300
|
for (const s of services) {
|
|
4133
|
-
if (s.node.name === name ||
|
|
4301
|
+
if (s.node.name === name || path28.basename(s.dir) === name) return s.node.id;
|
|
4134
4302
|
}
|
|
4135
4303
|
return null;
|
|
4136
4304
|
}
|
|
@@ -4139,7 +4307,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
4139
4307
|
let edgesAdded = 0;
|
|
4140
4308
|
let composePath = null;
|
|
4141
4309
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
4142
|
-
const abs =
|
|
4310
|
+
const abs = path28.join(scanPath, name);
|
|
4143
4311
|
if (await exists(abs)) {
|
|
4144
4312
|
composePath = abs;
|
|
4145
4313
|
break;
|
|
@@ -4152,13 +4320,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
4152
4320
|
} catch (err) {
|
|
4153
4321
|
recordExtractionError(
|
|
4154
4322
|
"infra docker-compose",
|
|
4155
|
-
|
|
4323
|
+
path28.relative(scanPath, composePath),
|
|
4156
4324
|
err
|
|
4157
4325
|
);
|
|
4158
4326
|
return { nodesAdded, edgesAdded };
|
|
4159
4327
|
}
|
|
4160
4328
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
4161
|
-
const evidenceFile =
|
|
4329
|
+
const evidenceFile = path28.relative(scanPath, composePath).split(path28.sep).join("/");
|
|
4162
4330
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
4163
4331
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
4164
4332
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -4199,7 +4367,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
4199
4367
|
}
|
|
4200
4368
|
|
|
4201
4369
|
// src/extract/infra/dockerfile.ts
|
|
4202
|
-
import
|
|
4370
|
+
import path29 from "path";
|
|
4203
4371
|
import { promises as fs15 } from "fs";
|
|
4204
4372
|
import { EdgeType as EdgeType11, Provenance as Provenance9, confidenceForExtracted as confidenceForExtracted8 } from "@neat.is/types";
|
|
4205
4373
|
function runtimeImage(content) {
|
|
@@ -4220,7 +4388,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4220
4388
|
let nodesAdded = 0;
|
|
4221
4389
|
let edgesAdded = 0;
|
|
4222
4390
|
for (const service of services) {
|
|
4223
|
-
const dockerfilePath =
|
|
4391
|
+
const dockerfilePath = path29.join(service.dir, "Dockerfile");
|
|
4224
4392
|
if (!await exists(dockerfilePath)) continue;
|
|
4225
4393
|
let content;
|
|
4226
4394
|
try {
|
|
@@ -4228,7 +4396,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4228
4396
|
} catch (err) {
|
|
4229
4397
|
recordExtractionError(
|
|
4230
4398
|
"infra dockerfile",
|
|
4231
|
-
|
|
4399
|
+
path29.relative(scanPath, dockerfilePath),
|
|
4232
4400
|
err
|
|
4233
4401
|
);
|
|
4234
4402
|
continue;
|
|
@@ -4240,7 +4408,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4240
4408
|
graph.addNode(node.id, node);
|
|
4241
4409
|
nodesAdded++;
|
|
4242
4410
|
}
|
|
4243
|
-
const relDockerfile = toPosix2(
|
|
4411
|
+
const relDockerfile = toPosix2(path29.relative(service.dir, dockerfilePath));
|
|
4244
4412
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
4245
4413
|
graph,
|
|
4246
4414
|
service.pkg.name,
|
|
@@ -4259,7 +4427,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4259
4427
|
provenance: Provenance9.EXTRACTED,
|
|
4260
4428
|
confidence: confidenceForExtracted8("structural"),
|
|
4261
4429
|
evidence: {
|
|
4262
|
-
file: toPosix2(
|
|
4430
|
+
file: toPosix2(path29.relative(scanPath, dockerfilePath))
|
|
4263
4431
|
}
|
|
4264
4432
|
};
|
|
4265
4433
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -4271,7 +4439,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4271
4439
|
|
|
4272
4440
|
// src/extract/infra/terraform.ts
|
|
4273
4441
|
import { promises as fs16 } from "fs";
|
|
4274
|
-
import
|
|
4442
|
+
import path30 from "path";
|
|
4275
4443
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
4276
4444
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
4277
4445
|
if (depth > max) return [];
|
|
@@ -4280,11 +4448,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
|
|
|
4280
4448
|
for (const entry of entries) {
|
|
4281
4449
|
if (entry.isDirectory()) {
|
|
4282
4450
|
if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
|
|
4283
|
-
const child =
|
|
4451
|
+
const child = path30.join(start, entry.name);
|
|
4284
4452
|
if (await isPythonVenvDir(child)) continue;
|
|
4285
4453
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
4286
4454
|
} else if (entry.isFile() && entry.name.endsWith(".tf")) {
|
|
4287
|
-
out.push(
|
|
4455
|
+
out.push(path30.join(start, entry.name));
|
|
4288
4456
|
}
|
|
4289
4457
|
}
|
|
4290
4458
|
return out;
|
|
@@ -4311,7 +4479,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
4311
4479
|
|
|
4312
4480
|
// src/extract/infra/k8s.ts
|
|
4313
4481
|
import { promises as fs17 } from "fs";
|
|
4314
|
-
import
|
|
4482
|
+
import path31 from "path";
|
|
4315
4483
|
import { parseAllDocuments as parseAllDocuments2 } from "yaml";
|
|
4316
4484
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
4317
4485
|
Service: "k8s-service",
|
|
@@ -4329,11 +4497,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
|
4329
4497
|
for (const entry of entries) {
|
|
4330
4498
|
if (entry.isDirectory()) {
|
|
4331
4499
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
4332
|
-
const child =
|
|
4500
|
+
const child = path31.join(start, entry.name);
|
|
4333
4501
|
if (await isPythonVenvDir(child)) continue;
|
|
4334
4502
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
4335
|
-
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
4336
|
-
out.push(
|
|
4503
|
+
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(path31.extname(entry.name))) {
|
|
4504
|
+
out.push(path31.join(start, entry.name));
|
|
4337
4505
|
}
|
|
4338
4506
|
}
|
|
4339
4507
|
return out;
|
|
@@ -4377,11 +4545,11 @@ async function addInfra(graph, scanPath, services) {
|
|
|
4377
4545
|
}
|
|
4378
4546
|
|
|
4379
4547
|
// src/extract/index.ts
|
|
4380
|
-
import
|
|
4548
|
+
import path33 from "path";
|
|
4381
4549
|
|
|
4382
4550
|
// src/extract/retire.ts
|
|
4383
4551
|
import { existsSync as existsSync2 } from "fs";
|
|
4384
|
-
import
|
|
4552
|
+
import path32 from "path";
|
|
4385
4553
|
import { NodeType as NodeType11, Provenance as Provenance10 } from "@neat.is/types";
|
|
4386
4554
|
function dropOrphanedFileNodes(graph) {
|
|
4387
4555
|
const orphans = [];
|
|
@@ -4415,11 +4583,11 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
4415
4583
|
if (edge.provenance !== Provenance10.EXTRACTED) return;
|
|
4416
4584
|
const evidenceFile = edge.evidence?.file;
|
|
4417
4585
|
if (!evidenceFile) return;
|
|
4418
|
-
if (
|
|
4586
|
+
if (path32.isAbsolute(evidenceFile)) {
|
|
4419
4587
|
if (!existsSync2(evidenceFile)) toDrop.push(id);
|
|
4420
4588
|
return;
|
|
4421
4589
|
}
|
|
4422
|
-
const found = bases.some((base) => existsSync2(
|
|
4590
|
+
const found = bases.some((base) => existsSync2(path32.join(base, evidenceFile)));
|
|
4423
4591
|
if (!found) toDrop.push(id);
|
|
4424
4592
|
});
|
|
4425
4593
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -4459,7 +4627,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
4459
4627
|
}
|
|
4460
4628
|
const droppedEntries = drainDroppedExtracted();
|
|
4461
4629
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
4462
|
-
const rejectedPath =
|
|
4630
|
+
const rejectedPath = path33.join(path33.dirname(opts.errorsPath), "rejected.ndjson");
|
|
4463
4631
|
try {
|
|
4464
4632
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
4465
4633
|
} catch (err) {
|
|
@@ -4736,7 +4904,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
4736
4904
|
|
|
4737
4905
|
// src/persist.ts
|
|
4738
4906
|
import { promises as fs18 } from "fs";
|
|
4739
|
-
import
|
|
4907
|
+
import path34 from "path";
|
|
4740
4908
|
import { Provenance as Provenance12, observedEdgeId as observedEdgeId2 } from "@neat.is/types";
|
|
4741
4909
|
var SCHEMA_VERSION = 4;
|
|
4742
4910
|
function migrateV1ToV2(payload) {
|
|
@@ -4773,7 +4941,7 @@ function migrateV2ToV3(payload) {
|
|
|
4773
4941
|
return { ...payload, schemaVersion: 3 };
|
|
4774
4942
|
}
|
|
4775
4943
|
async function ensureDir(filePath) {
|
|
4776
|
-
await fs18.mkdir(
|
|
4944
|
+
await fs18.mkdir(path34.dirname(filePath), { recursive: true });
|
|
4777
4945
|
}
|
|
4778
4946
|
async function saveGraphToDisk(graph, outPath) {
|
|
4779
4947
|
await ensureDir(outPath);
|
|
@@ -4923,23 +5091,23 @@ function canonicalJson(value) {
|
|
|
4923
5091
|
}
|
|
4924
5092
|
|
|
4925
5093
|
// src/projects.ts
|
|
4926
|
-
import
|
|
5094
|
+
import path35 from "path";
|
|
4927
5095
|
function pathsForProject(project, baseDir) {
|
|
4928
5096
|
if (project === DEFAULT_PROJECT) {
|
|
4929
5097
|
return {
|
|
4930
|
-
snapshotPath:
|
|
4931
|
-
errorsPath:
|
|
4932
|
-
staleEventsPath:
|
|
4933
|
-
embeddingsCachePath:
|
|
4934
|
-
policyViolationsPath:
|
|
5098
|
+
snapshotPath: path35.join(baseDir, "graph.json"),
|
|
5099
|
+
errorsPath: path35.join(baseDir, "errors.ndjson"),
|
|
5100
|
+
staleEventsPath: path35.join(baseDir, "stale-events.ndjson"),
|
|
5101
|
+
embeddingsCachePath: path35.join(baseDir, "embeddings.json"),
|
|
5102
|
+
policyViolationsPath: path35.join(baseDir, "policy-violations.ndjson")
|
|
4935
5103
|
};
|
|
4936
5104
|
}
|
|
4937
5105
|
return {
|
|
4938
|
-
snapshotPath:
|
|
4939
|
-
errorsPath:
|
|
4940
|
-
staleEventsPath:
|
|
4941
|
-
embeddingsCachePath:
|
|
4942
|
-
policyViolationsPath:
|
|
5106
|
+
snapshotPath: path35.join(baseDir, `${project}.json`),
|
|
5107
|
+
errorsPath: path35.join(baseDir, `errors.${project}.ndjson`),
|
|
5108
|
+
staleEventsPath: path35.join(baseDir, `stale-events.${project}.ndjson`),
|
|
5109
|
+
embeddingsCachePath: path35.join(baseDir, `embeddings.${project}.json`),
|
|
5110
|
+
policyViolationsPath: path35.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
4943
5111
|
};
|
|
4944
5112
|
}
|
|
4945
5113
|
var Projects = class {
|
|
@@ -4980,7 +5148,7 @@ function parseExtraProjects(raw) {
|
|
|
4980
5148
|
// src/registry.ts
|
|
4981
5149
|
import { promises as fs20 } from "fs";
|
|
4982
5150
|
import os2 from "os";
|
|
4983
|
-
import
|
|
5151
|
+
import path36 from "path";
|
|
4984
5152
|
import {
|
|
4985
5153
|
RegistryFileSchema
|
|
4986
5154
|
} from "@neat.is/types";
|
|
@@ -4988,17 +5156,122 @@ var LOCK_TIMEOUT_MS = 5e3;
|
|
|
4988
5156
|
var LOCK_RETRY_MS = 50;
|
|
4989
5157
|
function neatHome() {
|
|
4990
5158
|
const override = process.env.NEAT_HOME;
|
|
4991
|
-
if (override && override.length > 0) return
|
|
4992
|
-
return
|
|
5159
|
+
if (override && override.length > 0) return path36.resolve(override);
|
|
5160
|
+
return path36.join(os2.homedir(), ".neat");
|
|
4993
5161
|
}
|
|
4994
5162
|
function registryPath() {
|
|
4995
|
-
return
|
|
5163
|
+
return path36.join(neatHome(), "projects.json");
|
|
4996
5164
|
}
|
|
4997
5165
|
function registryLockPath() {
|
|
4998
|
-
return
|
|
5166
|
+
return path36.join(neatHome(), "projects.json.lock");
|
|
4999
5167
|
}
|
|
5000
5168
|
function daemonPidPath() {
|
|
5001
|
-
return
|
|
5169
|
+
return path36.join(neatHome(), "neatd.pid");
|
|
5170
|
+
}
|
|
5171
|
+
function daemonsDir() {
|
|
5172
|
+
return path36.join(neatHome(), "daemons");
|
|
5173
|
+
}
|
|
5174
|
+
function isFiniteInt(v) {
|
|
5175
|
+
return typeof v === "number" && Number.isFinite(v);
|
|
5176
|
+
}
|
|
5177
|
+
function parseDaemonRecord(raw) {
|
|
5178
|
+
let obj;
|
|
5179
|
+
try {
|
|
5180
|
+
obj = JSON.parse(raw);
|
|
5181
|
+
} catch {
|
|
5182
|
+
return void 0;
|
|
5183
|
+
}
|
|
5184
|
+
if (typeof obj !== "object" || obj === null) return void 0;
|
|
5185
|
+
const r = obj;
|
|
5186
|
+
const ports = r.ports;
|
|
5187
|
+
if (typeof r.project !== "string" || typeof r.projectPath !== "string" || !isFiniteInt(r.pid) || r.status !== "running" && r.status !== "stopped" || typeof r.startedAt !== "string" || typeof r.neatVersion !== "string" || !ports || !isFiniteInt(ports.rest) || !isFiniteInt(ports.otlp) || !isFiniteInt(ports.web)) {
|
|
5188
|
+
return void 0;
|
|
5189
|
+
}
|
|
5190
|
+
return {
|
|
5191
|
+
project: r.project,
|
|
5192
|
+
projectPath: r.projectPath,
|
|
5193
|
+
pid: r.pid,
|
|
5194
|
+
status: r.status,
|
|
5195
|
+
ports: { rest: ports.rest, otlp: ports.otlp, web: ports.web },
|
|
5196
|
+
startedAt: r.startedAt,
|
|
5197
|
+
neatVersion: r.neatVersion
|
|
5198
|
+
};
|
|
5199
|
+
}
|
|
5200
|
+
var defaultDiscoveryProbe = { isPidAlive: isPidAliveDefault };
|
|
5201
|
+
async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
5202
|
+
const dir = daemonsDir();
|
|
5203
|
+
let names;
|
|
5204
|
+
try {
|
|
5205
|
+
names = await fs20.readdir(dir);
|
|
5206
|
+
} catch (err) {
|
|
5207
|
+
if (err.code === "ENOENT") return [];
|
|
5208
|
+
throw err;
|
|
5209
|
+
}
|
|
5210
|
+
const out = [];
|
|
5211
|
+
for (const name of names) {
|
|
5212
|
+
if (!name.endsWith(".json")) continue;
|
|
5213
|
+
const file = path36.join(dir, name);
|
|
5214
|
+
let raw;
|
|
5215
|
+
try {
|
|
5216
|
+
raw = await fs20.readFile(file, "utf8");
|
|
5217
|
+
} catch {
|
|
5218
|
+
continue;
|
|
5219
|
+
}
|
|
5220
|
+
const record = parseDaemonRecord(raw);
|
|
5221
|
+
if (!record) continue;
|
|
5222
|
+
const live = record.status === "running" && probe.isPidAlive(record.pid);
|
|
5223
|
+
out.push({ record, live, source: file });
|
|
5224
|
+
}
|
|
5225
|
+
out.sort((a, b) => a.record.project.localeCompare(b.record.project));
|
|
5226
|
+
return out;
|
|
5227
|
+
}
|
|
5228
|
+
async function removeDaemonRecord(source) {
|
|
5229
|
+
await fs20.unlink(source).catch(() => {
|
|
5230
|
+
});
|
|
5231
|
+
}
|
|
5232
|
+
async function listMachineProjects(probe = defaultDiscoveryProbe) {
|
|
5233
|
+
const discovered = await discoverDaemons(probe);
|
|
5234
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
5235
|
+
for (const d of discovered) {
|
|
5236
|
+
const key = await normalizeProjectPath(d.record.projectPath);
|
|
5237
|
+
byPath.set(key, {
|
|
5238
|
+
project: d.record.project,
|
|
5239
|
+
projectPath: d.record.projectPath,
|
|
5240
|
+
state: d.live ? "running" : "stopped",
|
|
5241
|
+
ports: d.record.ports,
|
|
5242
|
+
pid: d.record.pid
|
|
5243
|
+
});
|
|
5244
|
+
}
|
|
5245
|
+
let legacy = [];
|
|
5246
|
+
try {
|
|
5247
|
+
legacy = (await readRegistry()).projects;
|
|
5248
|
+
} catch {
|
|
5249
|
+
legacy = [];
|
|
5250
|
+
}
|
|
5251
|
+
for (const entry of legacy) {
|
|
5252
|
+
const key = await normalizeProjectPath(entry.path);
|
|
5253
|
+
if (byPath.has(key)) continue;
|
|
5254
|
+
byPath.set(key, {
|
|
5255
|
+
project: entry.name,
|
|
5256
|
+
projectPath: entry.path,
|
|
5257
|
+
state: "registered",
|
|
5258
|
+
registryStatus: entry.status
|
|
5259
|
+
});
|
|
5260
|
+
}
|
|
5261
|
+
return [...byPath.values()].sort((a, b) => a.project.localeCompare(b.project));
|
|
5262
|
+
}
|
|
5263
|
+
async function findDaemonByProject(name, probe = defaultDiscoveryProbe) {
|
|
5264
|
+
const discovered = await discoverDaemons(probe);
|
|
5265
|
+
return discovered.find((d) => d.record.project === name);
|
|
5266
|
+
}
|
|
5267
|
+
function signalDaemonStop(pid) {
|
|
5268
|
+
try {
|
|
5269
|
+
process.kill(pid, "SIGTERM");
|
|
5270
|
+
return true;
|
|
5271
|
+
} catch (err) {
|
|
5272
|
+
if (err.code === "ESRCH") return false;
|
|
5273
|
+
throw err;
|
|
5274
|
+
}
|
|
5002
5275
|
}
|
|
5003
5276
|
function isPidAliveDefault(pid) {
|
|
5004
5277
|
try {
|
|
@@ -5058,7 +5331,7 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
|
|
|
5058
5331
|
}
|
|
5059
5332
|
}
|
|
5060
5333
|
async function normalizeProjectPath(input) {
|
|
5061
|
-
const resolved =
|
|
5334
|
+
const resolved = path36.resolve(input);
|
|
5062
5335
|
try {
|
|
5063
5336
|
return await fs20.realpath(resolved);
|
|
5064
5337
|
} catch {
|
|
@@ -5066,7 +5339,7 @@ async function normalizeProjectPath(input) {
|
|
|
5066
5339
|
}
|
|
5067
5340
|
}
|
|
5068
5341
|
async function writeAtomically(target, contents) {
|
|
5069
|
-
await fs20.mkdir(
|
|
5342
|
+
await fs20.mkdir(path36.dirname(target), { recursive: true });
|
|
5070
5343
|
const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
5071
5344
|
const fd = await fs20.open(tmp, "w");
|
|
5072
5345
|
try {
|
|
@@ -5079,7 +5352,7 @@ async function writeAtomically(target, contents) {
|
|
|
5079
5352
|
}
|
|
5080
5353
|
async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
|
|
5081
5354
|
const deadline = Date.now() + timeoutMs;
|
|
5082
|
-
await fs20.mkdir(
|
|
5355
|
+
await fs20.mkdir(path36.dirname(lockPath), { recursive: true });
|
|
5083
5356
|
let probedHolder = false;
|
|
5084
5357
|
while (true) {
|
|
5085
5358
|
try {
|
|
@@ -5215,6 +5488,58 @@ async function removeProject(name) {
|
|
|
5215
5488
|
return removed;
|
|
5216
5489
|
});
|
|
5217
5490
|
}
|
|
5491
|
+
var DAY_MS2 = 24 * 60 * 60 * 1e3;
|
|
5492
|
+
var DEFAULT_PRUNE_TTL_MS = 7 * DAY_MS2;
|
|
5493
|
+
function pruneTtlMs() {
|
|
5494
|
+
const raw = process.env.NEAT_REGISTRY_PRUNE_TTL_MS;
|
|
5495
|
+
if (!raw) return DEFAULT_PRUNE_TTL_MS;
|
|
5496
|
+
const n = Number.parseInt(raw, 10);
|
|
5497
|
+
if (Number.isFinite(n) && n >= 0) return n;
|
|
5498
|
+
console.warn(
|
|
5499
|
+
`[neat] NEAT_REGISTRY_PRUNE_TTL_MS could not be parsed (${raw}); using default ${DEFAULT_PRUNE_TTL_MS}ms`
|
|
5500
|
+
);
|
|
5501
|
+
return DEFAULT_PRUNE_TTL_MS;
|
|
5502
|
+
}
|
|
5503
|
+
async function statPathStatus(p) {
|
|
5504
|
+
try {
|
|
5505
|
+
const stat = await fs20.stat(p);
|
|
5506
|
+
return stat.isDirectory() ? "present" : "unknown";
|
|
5507
|
+
} catch (err) {
|
|
5508
|
+
return err.code === "ENOENT" ? "gone" : "unknown";
|
|
5509
|
+
}
|
|
5510
|
+
}
|
|
5511
|
+
async function pruneRegistry(opts = {}) {
|
|
5512
|
+
const ttlMs = opts.ttlMs ?? pruneTtlMs();
|
|
5513
|
+
const statPath = opts.statPath ?? statPathStatus;
|
|
5514
|
+
const now = opts.now ?? Date.now;
|
|
5515
|
+
return withLock(async () => {
|
|
5516
|
+
const reg = await readRegistry();
|
|
5517
|
+
const removed = [];
|
|
5518
|
+
const kept = [];
|
|
5519
|
+
for (const entry of reg.projects) {
|
|
5520
|
+
const status = await statPath(entry.path);
|
|
5521
|
+
if (status !== "gone") {
|
|
5522
|
+
kept.push(entry);
|
|
5523
|
+
continue;
|
|
5524
|
+
}
|
|
5525
|
+
if (ttlMs <= 0) {
|
|
5526
|
+
removed.push(entry);
|
|
5527
|
+
continue;
|
|
5528
|
+
}
|
|
5529
|
+
const lastSeen = Date.parse(entry.lastSeenAt ?? entry.registeredAt);
|
|
5530
|
+
const age = now() - (Number.isFinite(lastSeen) ? lastSeen : 0);
|
|
5531
|
+
if (age > ttlMs) {
|
|
5532
|
+
removed.push(entry);
|
|
5533
|
+
} else {
|
|
5534
|
+
kept.push(entry);
|
|
5535
|
+
}
|
|
5536
|
+
}
|
|
5537
|
+
if (removed.length === 0) return [];
|
|
5538
|
+
reg.projects = kept;
|
|
5539
|
+
await writeRegistry(reg);
|
|
5540
|
+
return removed;
|
|
5541
|
+
});
|
|
5542
|
+
}
|
|
5218
5543
|
|
|
5219
5544
|
// src/api.ts
|
|
5220
5545
|
import Fastify from "fastify";
|
|
@@ -5223,13 +5548,13 @@ import { DivergenceTypeSchema, PoliciesCheckBodySchema, PolicySeveritySchema } f
|
|
|
5223
5548
|
|
|
5224
5549
|
// src/extend/index.ts
|
|
5225
5550
|
import { promises as fs22 } from "fs";
|
|
5226
|
-
import
|
|
5551
|
+
import path38 from "path";
|
|
5227
5552
|
import os3 from "os";
|
|
5228
5553
|
import { resolve as registryResolve, list as registryList } from "@neat.is/instrumentation-registry";
|
|
5229
5554
|
|
|
5230
5555
|
// src/installers/package-manager.ts
|
|
5231
5556
|
import { promises as fs21 } from "fs";
|
|
5232
|
-
import
|
|
5557
|
+
import path37 from "path";
|
|
5233
5558
|
import { spawn } from "child_process";
|
|
5234
5559
|
var LOCKFILE_PRIORITY = [
|
|
5235
5560
|
{ lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
|
|
@@ -5251,22 +5576,22 @@ async function exists2(p) {
|
|
|
5251
5576
|
}
|
|
5252
5577
|
}
|
|
5253
5578
|
async function detectPackageManager(serviceDir) {
|
|
5254
|
-
let dir =
|
|
5579
|
+
let dir = path37.resolve(serviceDir);
|
|
5255
5580
|
const stops = /* @__PURE__ */ new Set();
|
|
5256
5581
|
for (let i = 0; i < 64; i++) {
|
|
5257
5582
|
if (stops.has(dir)) break;
|
|
5258
5583
|
stops.add(dir);
|
|
5259
5584
|
for (const candidate of LOCKFILE_PRIORITY) {
|
|
5260
|
-
const lockPath =
|
|
5585
|
+
const lockPath = path37.join(dir, candidate.lockfile);
|
|
5261
5586
|
if (await exists2(lockPath)) {
|
|
5262
5587
|
return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
|
|
5263
5588
|
}
|
|
5264
5589
|
}
|
|
5265
|
-
const parent =
|
|
5590
|
+
const parent = path37.dirname(dir);
|
|
5266
5591
|
if (parent === dir) break;
|
|
5267
5592
|
dir = parent;
|
|
5268
5593
|
}
|
|
5269
|
-
return { pm: "npm", cwd:
|
|
5594
|
+
return { pm: "npm", cwd: path37.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
|
|
5270
5595
|
}
|
|
5271
5596
|
async function runPackageManagerInstall(cmd) {
|
|
5272
5597
|
return new Promise((resolve) => {
|
|
@@ -5315,7 +5640,7 @@ async function fileExists2(p) {
|
|
|
5315
5640
|
}
|
|
5316
5641
|
}
|
|
5317
5642
|
async function readPackageJson(scanPath) {
|
|
5318
|
-
const pkgPath =
|
|
5643
|
+
const pkgPath = path38.join(scanPath, "package.json");
|
|
5319
5644
|
const raw = await fs22.readFile(pkgPath, "utf8");
|
|
5320
5645
|
return JSON.parse(raw);
|
|
5321
5646
|
}
|
|
@@ -5326,11 +5651,11 @@ async function findHookFiles(scanPath) {
|
|
|
5326
5651
|
).sort();
|
|
5327
5652
|
}
|
|
5328
5653
|
function extendLogPath() {
|
|
5329
|
-
return process.env.NEAT_EXTEND_LOG ??
|
|
5654
|
+
return process.env.NEAT_EXTEND_LOG ?? path38.join(os3.homedir(), ".neat", "extend-log.ndjson");
|
|
5330
5655
|
}
|
|
5331
5656
|
async function appendExtendLog(entry) {
|
|
5332
5657
|
const logPath = extendLogPath();
|
|
5333
|
-
await fs22.mkdir(
|
|
5658
|
+
await fs22.mkdir(path38.dirname(logPath), { recursive: true });
|
|
5334
5659
|
await fs22.appendFile(logPath, JSON.stringify(entry) + "\n", "utf8");
|
|
5335
5660
|
}
|
|
5336
5661
|
function splicedContent(fileContent, snippet2) {
|
|
@@ -5389,7 +5714,7 @@ function lookupInstrumentation(library, installedVersion) {
|
|
|
5389
5714
|
}
|
|
5390
5715
|
async function describeProjectInstrumentation(ctx) {
|
|
5391
5716
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
5392
|
-
const envNeat = await fileExists2(
|
|
5717
|
+
const envNeat = await fileExists2(path38.join(ctx.scanPath, ".env.neat"));
|
|
5393
5718
|
const registryInstrPackages = new Set(
|
|
5394
5719
|
registryList().map((e) => e.instrumentation_package).filter((p) => !!p)
|
|
5395
5720
|
);
|
|
@@ -5411,16 +5736,16 @@ async function applyExtension(ctx, args, options) {
|
|
|
5411
5736
|
);
|
|
5412
5737
|
}
|
|
5413
5738
|
for (const file of hookFiles) {
|
|
5414
|
-
const content = await fs22.readFile(
|
|
5739
|
+
const content = await fs22.readFile(path38.join(ctx.scanPath, file), "utf8");
|
|
5415
5740
|
if (content.includes(args.registration_snippet)) {
|
|
5416
5741
|
return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
|
|
5417
5742
|
}
|
|
5418
5743
|
}
|
|
5419
5744
|
const primaryFile = hookFiles[0];
|
|
5420
|
-
const primaryPath =
|
|
5745
|
+
const primaryPath = path38.join(ctx.scanPath, primaryFile);
|
|
5421
5746
|
const filesTouched = [];
|
|
5422
5747
|
const depsAdded = [];
|
|
5423
|
-
const pkgPath =
|
|
5748
|
+
const pkgPath = path38.join(ctx.scanPath, "package.json");
|
|
5424
5749
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
5425
5750
|
if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
|
|
5426
5751
|
pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
|
|
@@ -5466,7 +5791,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
5466
5791
|
};
|
|
5467
5792
|
}
|
|
5468
5793
|
for (const file of hookFiles) {
|
|
5469
|
-
const content = await fs22.readFile(
|
|
5794
|
+
const content = await fs22.readFile(path38.join(ctx.scanPath, file), "utf8");
|
|
5470
5795
|
if (content.includes(args.registration_snippet)) {
|
|
5471
5796
|
return {
|
|
5472
5797
|
library: args.library,
|
|
@@ -5488,7 +5813,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
5488
5813
|
depsToAdd.push(`${args.instrumentation_package}@${args.version}`);
|
|
5489
5814
|
filesTouched.push("package.json");
|
|
5490
5815
|
}
|
|
5491
|
-
const hookContent = await fs22.readFile(
|
|
5816
|
+
const hookContent = await fs22.readFile(path38.join(ctx.scanPath, primaryFile), "utf8");
|
|
5492
5817
|
const patched = splicedContent(hookContent, args.registration_snippet);
|
|
5493
5818
|
if (patched) {
|
|
5494
5819
|
filesTouched.push(primaryFile);
|
|
@@ -5509,7 +5834,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
5509
5834
|
if (!match) {
|
|
5510
5835
|
return { undone: false, message: "no apply found for library" };
|
|
5511
5836
|
}
|
|
5512
|
-
const pkgPath =
|
|
5837
|
+
const pkgPath = path38.join(ctx.scanPath, "package.json");
|
|
5513
5838
|
if (await fileExists2(pkgPath)) {
|
|
5514
5839
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
5515
5840
|
if (pkg.dependencies?.[match.instrumentation_package]) {
|
|
@@ -5520,7 +5845,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
5520
5845
|
}
|
|
5521
5846
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
5522
5847
|
for (const file of hookFiles) {
|
|
5523
|
-
const filePath =
|
|
5848
|
+
const filePath = path38.join(ctx.scanPath, file);
|
|
5524
5849
|
const content = await fs22.readFile(filePath, "utf8");
|
|
5525
5850
|
if (content.includes(match.registration_snippet)) {
|
|
5526
5851
|
const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
|
|
@@ -5545,6 +5870,7 @@ function handleSse(req, reply, opts) {
|
|
|
5545
5870
|
reply.raw.setHeader("Connection", "keep-alive");
|
|
5546
5871
|
reply.raw.setHeader("X-Accel-Buffering", "no");
|
|
5547
5872
|
reply.raw.flushHeaders?.();
|
|
5873
|
+
reply.raw.write(":open\n\n");
|
|
5548
5874
|
let pending = 0;
|
|
5549
5875
|
let dropped = false;
|
|
5550
5876
|
const closeConnection = () => {
|
|
@@ -6244,6 +6570,10 @@ export {
|
|
|
6244
6570
|
parseExtraProjects,
|
|
6245
6571
|
registryPath,
|
|
6246
6572
|
registryLockPath,
|
|
6573
|
+
removeDaemonRecord,
|
|
6574
|
+
listMachineProjects,
|
|
6575
|
+
findDaemonByProject,
|
|
6576
|
+
signalDaemonStop,
|
|
6247
6577
|
normalizeProjectPath,
|
|
6248
6578
|
writeAtomically,
|
|
6249
6579
|
readRegistry,
|
|
@@ -6254,6 +6584,7 @@ export {
|
|
|
6254
6584
|
setStatus,
|
|
6255
6585
|
touchLastSeen,
|
|
6256
6586
|
removeProject,
|
|
6587
|
+
pruneRegistry,
|
|
6257
6588
|
buildApi
|
|
6258
6589
|
};
|
|
6259
|
-
//# sourceMappingURL=chunk-
|
|
6590
|
+
//# sourceMappingURL=chunk-T3X6XJPU.js.map
|