@neat.is/core 0.7.1 → 0.7.3
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-MDBE23Y3.js → chunk-3SBEY67S.js} +2 -2
- package/dist/{chunk-RR4LWQQB.js → chunk-BY6ZP5WA.js} +267 -139
- package/dist/chunk-BY6ZP5WA.js.map +1 -0
- package/dist/cli.cjs +945 -466
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +2 -1
- package/dist/cli.d.ts +2 -1
- package/dist/cli.js +356 -8
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +425 -296
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/neatd.cjs +434 -305
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +2 -2
- package/dist/server.cjs +357 -228
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-RR4LWQQB.js.map +0 -1
- /package/dist/{chunk-MDBE23Y3.js.map → chunk-3SBEY67S.js.map} +0 -0
package/dist/cli.cjs
CHANGED
|
@@ -61,8 +61,8 @@ function mountBearerAuth(app, opts) {
|
|
|
61
61
|
]);
|
|
62
62
|
const publicRead = opts.publicRead === true;
|
|
63
63
|
app.addHook("preHandler", (req, reply, done) => {
|
|
64
|
-
const
|
|
65
|
-
if (exactUnauthPaths.has(
|
|
64
|
+
const path72 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
|
|
65
|
+
if (exactUnauthPaths.has(path72) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path72)) {
|
|
66
66
|
done();
|
|
67
67
|
return;
|
|
68
68
|
}
|
|
@@ -194,8 +194,8 @@ function reshapeGrpcRequest(req) {
|
|
|
194
194
|
};
|
|
195
195
|
}
|
|
196
196
|
function resolveProtoRoot() {
|
|
197
|
-
const here =
|
|
198
|
-
return
|
|
197
|
+
const here = import_node_path40.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
|
|
198
|
+
return import_node_path40.default.resolve(here, "..", "proto");
|
|
199
199
|
}
|
|
200
200
|
function loadTraceService() {
|
|
201
201
|
const protoRoot = resolveProtoRoot();
|
|
@@ -263,13 +263,13 @@ async function startOtelGrpcReceiver(opts) {
|
|
|
263
263
|
})
|
|
264
264
|
};
|
|
265
265
|
}
|
|
266
|
-
var import_node_url2,
|
|
266
|
+
var import_node_url2, import_node_path40, import_node_crypto2, grpc, protoLoader;
|
|
267
267
|
var init_otel_grpc = __esm({
|
|
268
268
|
"src/otel-grpc.ts"() {
|
|
269
269
|
"use strict";
|
|
270
270
|
init_cjs_shims();
|
|
271
271
|
import_node_url2 = require("url");
|
|
272
|
-
|
|
272
|
+
import_node_path40 = __toESM(require("path"), 1);
|
|
273
273
|
import_node_crypto2 = require("crypto");
|
|
274
274
|
grpc = __toESM(require("@grpc/grpc-js"), 1);
|
|
275
275
|
protoLoader = __toESM(require("@grpc/proto-loader"), 1);
|
|
@@ -415,8 +415,8 @@ function websocketChannelPathOf(attrs) {
|
|
|
415
415
|
const v = attrs[key];
|
|
416
416
|
if (typeof v === "string" && v.length > 0) {
|
|
417
417
|
const q = v.indexOf("?");
|
|
418
|
-
const
|
|
419
|
-
if (
|
|
418
|
+
const path72 = q === -1 ? v : v.slice(0, q);
|
|
419
|
+
if (path72.length > 0) return path72;
|
|
420
420
|
}
|
|
421
421
|
}
|
|
422
422
|
return void 0;
|
|
@@ -475,10 +475,10 @@ function parseOtlpRequest(body) {
|
|
|
475
475
|
return out;
|
|
476
476
|
}
|
|
477
477
|
function loadProtoRoot() {
|
|
478
|
-
const here =
|
|
479
|
-
const protoRoot =
|
|
478
|
+
const here = import_node_path41.default.dirname((0, import_node_url3.fileURLToPath)(importMetaUrl));
|
|
479
|
+
const protoRoot = import_node_path41.default.resolve(here, "..", "proto");
|
|
480
480
|
const root = new import_protobufjs.default.Root();
|
|
481
|
-
root.resolvePath = (_origin, target) =>
|
|
481
|
+
root.resolvePath = (_origin, target) => import_node_path41.default.resolve(protoRoot, target);
|
|
482
482
|
root.loadSync(
|
|
483
483
|
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
|
|
484
484
|
{ keepCase: true }
|
|
@@ -709,12 +709,12 @@ async function listenSteppingOtlp(app, requestedPort, host) {
|
|
|
709
709
|
}
|
|
710
710
|
}
|
|
711
711
|
}
|
|
712
|
-
var
|
|
712
|
+
var import_node_path41, import_node_url3, import_fastify, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
|
|
713
713
|
var init_otel = __esm({
|
|
714
714
|
"src/otel.ts"() {
|
|
715
715
|
"use strict";
|
|
716
716
|
init_cjs_shims();
|
|
717
|
-
|
|
717
|
+
import_node_path41 = __toESM(require("path"), 1);
|
|
718
718
|
import_node_url3 = require("url");
|
|
719
719
|
import_fastify = __toESM(require("fastify"), 1);
|
|
720
720
|
import_protobufjs = __toESM(require("protobufjs"), 1);
|
|
@@ -745,13 +745,14 @@ __export(cli_exports, {
|
|
|
745
745
|
resolveDaemonUrl: () => resolveDaemonUrl,
|
|
746
746
|
resolveProjectForVerb: () => resolveProjectForVerb,
|
|
747
747
|
runInit: () => runInit,
|
|
748
|
+
runMonitorVerb: () => runMonitorVerb,
|
|
748
749
|
runQueryVerb: () => runQueryVerb,
|
|
749
750
|
runSkill: () => runSkill,
|
|
750
751
|
usage: () => usage2
|
|
751
752
|
});
|
|
752
753
|
module.exports = __toCommonJS(cli_exports);
|
|
753
754
|
init_cjs_shims();
|
|
754
|
-
var
|
|
755
|
+
var import_node_path71 = __toESM(require("path"), 1);
|
|
755
756
|
var import_node_os6 = __toESM(require("os"), 1);
|
|
756
757
|
var import_node_fs43 = require("fs");
|
|
757
758
|
|
|
@@ -1321,19 +1322,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
1321
1322
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
1322
1323
|
let best = { path: [start], edges: [] };
|
|
1323
1324
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
1324
|
-
function step(node,
|
|
1325
|
-
if (
|
|
1326
|
-
best = { path: [...
|
|
1325
|
+
function step(node, path72, edges) {
|
|
1326
|
+
if (path72.length > best.path.length) {
|
|
1327
|
+
best = { path: [...path72], edges: [...edges] };
|
|
1327
1328
|
}
|
|
1328
|
-
if (
|
|
1329
|
+
if (path72.length - 1 >= maxDepth) return;
|
|
1329
1330
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
1330
1331
|
for (const [srcId, edge] of incoming) {
|
|
1331
1332
|
if (visited.has(srcId)) continue;
|
|
1332
1333
|
visited.add(srcId);
|
|
1333
|
-
|
|
1334
|
+
path72.push(srcId);
|
|
1334
1335
|
edges.push(edge);
|
|
1335
|
-
step(srcId,
|
|
1336
|
-
|
|
1336
|
+
step(srcId, path72, edges);
|
|
1337
|
+
path72.pop();
|
|
1337
1338
|
edges.pop();
|
|
1338
1339
|
visited.delete(srcId);
|
|
1339
1340
|
}
|
|
@@ -1540,26 +1541,26 @@ function dominantFailingCall(graph, serviceId7, visited) {
|
|
|
1540
1541
|
return best;
|
|
1541
1542
|
}
|
|
1542
1543
|
function followFailingCallChain(graph, originServiceId, maxDepth) {
|
|
1543
|
-
const
|
|
1544
|
+
const path72 = [originServiceId];
|
|
1544
1545
|
const edges = [];
|
|
1545
1546
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
1546
1547
|
let current = originServiceId;
|
|
1547
1548
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1548
1549
|
const hop = dominantFailingCall(graph, current, visited);
|
|
1549
1550
|
if (!hop) break;
|
|
1550
|
-
|
|
1551
|
+
path72.push(hop.nextService);
|
|
1551
1552
|
edges.push(hop.edge);
|
|
1552
1553
|
visited.add(hop.nextService);
|
|
1553
1554
|
current = hop.nextService;
|
|
1554
1555
|
}
|
|
1555
1556
|
if (edges.length === 0) return null;
|
|
1556
|
-
return { path:
|
|
1557
|
+
return { path: path72, edges, culprit: current };
|
|
1557
1558
|
}
|
|
1558
1559
|
function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
1559
1560
|
const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
|
|
1560
1561
|
if (!chain) return null;
|
|
1561
1562
|
const culprit = chain.culprit;
|
|
1562
|
-
const
|
|
1563
|
+
const path72 = [...chain.path];
|
|
1563
1564
|
const edgeProvenances = chain.edges.map((e) => e.provenance);
|
|
1564
1565
|
const baseConfidence = confidenceFromMix(chain.edges);
|
|
1565
1566
|
const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
|
|
@@ -1567,14 +1568,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1567
1568
|
if (loc) {
|
|
1568
1569
|
let rootCauseNode = culprit;
|
|
1569
1570
|
if (loc.fileNode) {
|
|
1570
|
-
|
|
1571
|
+
path72.push(loc.fileNode);
|
|
1571
1572
|
edgeProvenances.push(import_types.Provenance.OBSERVED);
|
|
1572
1573
|
rootCauseNode = loc.fileNode;
|
|
1573
1574
|
}
|
|
1574
1575
|
return import_types.RootCauseResultSchema.parse({
|
|
1575
1576
|
rootCauseNode,
|
|
1576
1577
|
rootCauseReason: loc.rootCauseReason,
|
|
1577
|
-
traversalPath:
|
|
1578
|
+
traversalPath: path72,
|
|
1578
1579
|
edgeProvenances,
|
|
1579
1580
|
confidence,
|
|
1580
1581
|
...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
|
|
@@ -1586,7 +1587,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1586
1587
|
return import_types.RootCauseResultSchema.parse({
|
|
1587
1588
|
rootCauseNode: culprit,
|
|
1588
1589
|
rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
|
|
1589
|
-
traversalPath:
|
|
1590
|
+
traversalPath: path72,
|
|
1590
1591
|
edgeProvenances,
|
|
1591
1592
|
confidence,
|
|
1592
1593
|
fixRecommendation: `Inspect ${culpritName}'s failing handler`
|
|
@@ -6968,7 +6969,7 @@ async function addGrpcMethods(graph, services) {
|
|
|
6968
6969
|
|
|
6969
6970
|
// src/extract/calls/index.ts
|
|
6970
6971
|
init_cjs_shims();
|
|
6971
|
-
var
|
|
6972
|
+
var import_types30 = require("@neat.is/types");
|
|
6972
6973
|
|
|
6973
6974
|
// src/extract/calls/http.ts
|
|
6974
6975
|
init_cjs_shims();
|
|
@@ -8424,12 +8425,140 @@ function drizzleEndpointsFromFile(file, serviceDir) {
|
|
|
8424
8425
|
return out;
|
|
8425
8426
|
}
|
|
8426
8427
|
|
|
8428
|
+
// src/extract/calls/prisma.ts
|
|
8429
|
+
init_cjs_shims();
|
|
8430
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
8431
|
+
var import_types28 = require("@neat.is/types");
|
|
8432
|
+
var SCALAR_TYPES = /* @__PURE__ */ new Set([
|
|
8433
|
+
"Int",
|
|
8434
|
+
"String",
|
|
8435
|
+
"Boolean",
|
|
8436
|
+
"DateTime",
|
|
8437
|
+
"Float",
|
|
8438
|
+
"Decimal",
|
|
8439
|
+
"BigInt",
|
|
8440
|
+
"Bytes",
|
|
8441
|
+
"Json"
|
|
8442
|
+
]);
|
|
8443
|
+
function stripLineComment(line) {
|
|
8444
|
+
let inStr = false;
|
|
8445
|
+
for (let i = 0; i < line.length; i++) {
|
|
8446
|
+
const ch = line[i];
|
|
8447
|
+
if (ch === '"' && line[i - 1] !== "\\") inStr = !inStr;
|
|
8448
|
+
else if (!inStr && ch === "/" && line[i + 1] === "/") return line.slice(0, i);
|
|
8449
|
+
}
|
|
8450
|
+
return line;
|
|
8451
|
+
}
|
|
8452
|
+
function netBraces(line) {
|
|
8453
|
+
const s = stripLineComment(line).replace(/"(\\.|[^"\\])*"/g, "");
|
|
8454
|
+
let n = 0;
|
|
8455
|
+
for (const ch of s) {
|
|
8456
|
+
if (ch === "{") n++;
|
|
8457
|
+
else if (ch === "}") n--;
|
|
8458
|
+
}
|
|
8459
|
+
return n;
|
|
8460
|
+
}
|
|
8461
|
+
function prismaColumnsFromSchema(file, serviceDir) {
|
|
8462
|
+
const content = file.content;
|
|
8463
|
+
if (!/\bmodel\s+[A-Za-z_]\w*\s*\{/.test(content)) return [];
|
|
8464
|
+
const lines = content.split("\n");
|
|
8465
|
+
const modelNames = /* @__PURE__ */ new Set();
|
|
8466
|
+
const enumNames = /* @__PURE__ */ new Set();
|
|
8467
|
+
for (const line of lines) {
|
|
8468
|
+
const m = line.match(/^\s*model\s+([A-Za-z_]\w*)\s*\{/);
|
|
8469
|
+
if (m) modelNames.add(m[1]);
|
|
8470
|
+
const e = line.match(/^\s*enum\s+([A-Za-z_]\w*)\s*\{/);
|
|
8471
|
+
if (e) enumNames.add(e[1]);
|
|
8472
|
+
}
|
|
8473
|
+
const out = [];
|
|
8474
|
+
const seenTable = /* @__PURE__ */ new Set();
|
|
8475
|
+
const finalize = (b) => {
|
|
8476
|
+
if (seenTable.has(b.tableName)) return;
|
|
8477
|
+
seenTable.add(b.tableName);
|
|
8478
|
+
out.push({
|
|
8479
|
+
infraId: (0, import_types28.infraId)("sql-table", b.tableName),
|
|
8480
|
+
name: b.tableName,
|
|
8481
|
+
kind: "sql-table",
|
|
8482
|
+
edgeType: "CALLS",
|
|
8483
|
+
confidenceKind: "structural",
|
|
8484
|
+
columns: b.columns,
|
|
8485
|
+
evidence: {
|
|
8486
|
+
file: import_node_path39.default.relative(serviceDir, file.path),
|
|
8487
|
+
line: b.startLine,
|
|
8488
|
+
snippet: snippet(content, b.startLine)
|
|
8489
|
+
}
|
|
8490
|
+
});
|
|
8491
|
+
};
|
|
8492
|
+
let current = null;
|
|
8493
|
+
let depth = 0;
|
|
8494
|
+
for (let i = 0; i < lines.length; i++) {
|
|
8495
|
+
const raw = lines[i];
|
|
8496
|
+
const lineNo = i + 1;
|
|
8497
|
+
if (current === null) {
|
|
8498
|
+
const header = raw.match(/^\s*(model|enum|datasource|generator|type)\s+([A-Za-z_]\w*)\b/);
|
|
8499
|
+
if (header && raw.includes("{")) {
|
|
8500
|
+
current = {
|
|
8501
|
+
kind: header[1],
|
|
8502
|
+
tableName: header[2],
|
|
8503
|
+
startLine: lineNo,
|
|
8504
|
+
columns: [],
|
|
8505
|
+
seenCol: /* @__PURE__ */ new Set()
|
|
8506
|
+
};
|
|
8507
|
+
depth = netBraces(raw);
|
|
8508
|
+
if (depth <= 0) {
|
|
8509
|
+
if (current.kind === "model") finalize(current);
|
|
8510
|
+
current = null;
|
|
8511
|
+
}
|
|
8512
|
+
}
|
|
8513
|
+
continue;
|
|
8514
|
+
}
|
|
8515
|
+
depth += netBraces(raw);
|
|
8516
|
+
if (depth <= 0) {
|
|
8517
|
+
if (current.kind === "model") finalize(current);
|
|
8518
|
+
current = null;
|
|
8519
|
+
continue;
|
|
8520
|
+
}
|
|
8521
|
+
if (current.kind !== "model") continue;
|
|
8522
|
+
const trimmed = stripLineComment(raw).trim();
|
|
8523
|
+
if (!trimmed) continue;
|
|
8524
|
+
if (trimmed.startsWith("@@")) {
|
|
8525
|
+
const map = trimmed.match(/@@map\(\s*"([^"]+)"\s*\)/);
|
|
8526
|
+
if (map) current.tableName = map[1];
|
|
8527
|
+
continue;
|
|
8528
|
+
}
|
|
8529
|
+
const fm = trimmed.match(/^([A-Za-z_]\w*)\s+([A-Za-z_]\w*)/);
|
|
8530
|
+
if (!fm) continue;
|
|
8531
|
+
const fieldName = fm[1];
|
|
8532
|
+
const baseType = fm[2];
|
|
8533
|
+
if (/@relation\b/.test(trimmed) || modelNames.has(baseType)) continue;
|
|
8534
|
+
if (!enumNames.has(baseType) && !SCALAR_TYPES.has(baseType)) continue;
|
|
8535
|
+
const mapM = trimmed.match(/(?<!@)@map\(\s*"([^"]+)"\s*\)/);
|
|
8536
|
+
const columnName = mapM ? mapM[1] : fieldName;
|
|
8537
|
+
if (!current.seenCol.has(columnName)) {
|
|
8538
|
+
current.seenCol.add(columnName);
|
|
8539
|
+
current.columns.push(columnName);
|
|
8540
|
+
}
|
|
8541
|
+
}
|
|
8542
|
+
if (current && current.kind === "model") finalize(current);
|
|
8543
|
+
return out;
|
|
8544
|
+
}
|
|
8545
|
+
async function prismaColumnEndpoints(serviceDir) {
|
|
8546
|
+
const schemaPath = await findFirst(serviceDir, [
|
|
8547
|
+
import_node_path39.default.join("prisma", "schema.prisma"),
|
|
8548
|
+
"schema.prisma"
|
|
8549
|
+
]);
|
|
8550
|
+
if (!schemaPath) return [];
|
|
8551
|
+
const content = await readIfExists(schemaPath);
|
|
8552
|
+
if (!content) return [];
|
|
8553
|
+
return prismaColumnsFromSchema({ path: schemaPath, content }, serviceDir);
|
|
8554
|
+
}
|
|
8555
|
+
|
|
8427
8556
|
// src/extract/calls/go.ts
|
|
8428
8557
|
init_cjs_shims();
|
|
8429
|
-
var
|
|
8558
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
8430
8559
|
var import_tree_sitter10 = __toESM(require("tree-sitter"), 1);
|
|
8431
8560
|
var import_tree_sitter_go3 = __toESM(require("tree-sitter-go"), 1);
|
|
8432
|
-
var
|
|
8561
|
+
var import_types29 = require("@neat.is/types");
|
|
8433
8562
|
init_otel();
|
|
8434
8563
|
var SQL_METHODS = /* @__PURE__ */ new Set(["Exec", "ExecContext", "Query", "QueryContext", "QueryRow", "QueryRowContext"]);
|
|
8435
8564
|
var PARSE_CHUNK8 = 16384;
|
|
@@ -8441,7 +8570,7 @@ function walk5(node, visit) {
|
|
|
8441
8570
|
}
|
|
8442
8571
|
}
|
|
8443
8572
|
function goSqlEndpointsFromFile(file, serviceDir) {
|
|
8444
|
-
if (
|
|
8573
|
+
if (import_node_path42.default.extname(file.path) !== ".go") return [];
|
|
8445
8574
|
const parser = new import_tree_sitter10.default();
|
|
8446
8575
|
parser.setLanguage(import_tree_sitter_go3.default);
|
|
8447
8576
|
const tree = parser.parse(
|
|
@@ -8461,12 +8590,12 @@ function goSqlEndpointsFromFile(file, serviceDir) {
|
|
|
8461
8590
|
if (!table) return;
|
|
8462
8591
|
const line = node.startPosition.row + 1;
|
|
8463
8592
|
out.push({
|
|
8464
|
-
infraId: (0,
|
|
8593
|
+
infraId: (0, import_types29.infraId)("sql-table", table),
|
|
8465
8594
|
name: table,
|
|
8466
8595
|
kind: "sql-table",
|
|
8467
8596
|
edgeType: "CALLS",
|
|
8468
8597
|
confidenceKind: "verified-call-site",
|
|
8469
|
-
evidence: { file: toPosix(
|
|
8598
|
+
evidence: { file: toPosix(import_node_path42.default.relative(serviceDir, file.path)), line, snippet: snippet(file.content, line) }
|
|
8470
8599
|
});
|
|
8471
8600
|
});
|
|
8472
8601
|
return out;
|
|
@@ -8476,11 +8605,11 @@ function goSqlEndpointsFromFile(file, serviceDir) {
|
|
|
8476
8605
|
function edgeTypeFromEndpoint(ep) {
|
|
8477
8606
|
switch (ep.edgeType) {
|
|
8478
8607
|
case "PUBLISHES_TO":
|
|
8479
|
-
return
|
|
8608
|
+
return import_types30.EdgeType.PUBLISHES_TO;
|
|
8480
8609
|
case "CONSUMES_FROM":
|
|
8481
|
-
return
|
|
8610
|
+
return import_types30.EdgeType.CONSUMES_FROM;
|
|
8482
8611
|
default:
|
|
8483
|
-
return
|
|
8612
|
+
return import_types30.EdgeType.CALLS;
|
|
8484
8613
|
}
|
|
8485
8614
|
}
|
|
8486
8615
|
function isAwsKind(kind) {
|
|
@@ -8515,13 +8644,14 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
8515
8644
|
}
|
|
8516
8645
|
endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
|
|
8517
8646
|
endpoints.push(...pythonOrmCrossFileEndpoints(maskedFiles, service.dir));
|
|
8647
|
+
endpoints.push(...await prismaColumnEndpoints(service.dir));
|
|
8518
8648
|
if (endpoints.length === 0) continue;
|
|
8519
8649
|
const seenEdges = /* @__PURE__ */ new Set();
|
|
8520
8650
|
for (const ep of endpoints) {
|
|
8521
8651
|
if (!graph.hasNode(ep.infraId)) {
|
|
8522
8652
|
const node = {
|
|
8523
8653
|
id: ep.infraId,
|
|
8524
|
-
type:
|
|
8654
|
+
type: import_types30.NodeType.InfraNode,
|
|
8525
8655
|
name: ep.name,
|
|
8526
8656
|
// #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
|
|
8527
8657
|
// aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
|
|
@@ -8534,20 +8664,20 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
8534
8664
|
}
|
|
8535
8665
|
if (ep.columns && ep.columns.length > 0) {
|
|
8536
8666
|
const node = graph.getNodeAttributes(ep.infraId);
|
|
8537
|
-
if (node.type ===
|
|
8667
|
+
if (node.type === import_types30.NodeType.InfraNode) {
|
|
8538
8668
|
graph.replaceNodeAttributes(ep.infraId, {
|
|
8539
8669
|
...node,
|
|
8540
8670
|
columns: foldColumns(
|
|
8541
8671
|
node.columns,
|
|
8542
8672
|
ep.columns,
|
|
8543
|
-
|
|
8544
|
-
(0,
|
|
8673
|
+
import_types30.Provenance.EXTRACTED,
|
|
8674
|
+
(0, import_types30.confidenceForExtracted)(ep.confidenceKind)
|
|
8545
8675
|
)
|
|
8546
8676
|
});
|
|
8547
8677
|
}
|
|
8548
8678
|
}
|
|
8549
8679
|
const edgeType = edgeTypeFromEndpoint(ep);
|
|
8550
|
-
const confidence = (0,
|
|
8680
|
+
const confidence = (0, import_types30.confidenceForExtracted)(ep.confidenceKind);
|
|
8551
8681
|
const relFile = toPosix(ep.evidence.file);
|
|
8552
8682
|
const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
8553
8683
|
graph,
|
|
@@ -8557,7 +8687,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
8557
8687
|
);
|
|
8558
8688
|
nodesAdded += n;
|
|
8559
8689
|
edgesAdded += e;
|
|
8560
|
-
if (!(0,
|
|
8690
|
+
if (!(0, import_types30.passesExtractedFloor)(confidence)) {
|
|
8561
8691
|
noteExtractedDropped({
|
|
8562
8692
|
source: fileNodeId,
|
|
8563
8693
|
target: ep.infraId,
|
|
@@ -8577,7 +8707,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
8577
8707
|
source: fileNodeId,
|
|
8578
8708
|
target: ep.infraId,
|
|
8579
8709
|
type: edgeType,
|
|
8580
|
-
provenance:
|
|
8710
|
+
provenance: import_types30.Provenance.EXTRACTED,
|
|
8581
8711
|
confidence,
|
|
8582
8712
|
evidence: ep.evidence
|
|
8583
8713
|
};
|
|
@@ -8603,16 +8733,16 @@ init_cjs_shims();
|
|
|
8603
8733
|
|
|
8604
8734
|
// src/extract/infra/docker-compose.ts
|
|
8605
8735
|
init_cjs_shims();
|
|
8606
|
-
var
|
|
8607
|
-
var
|
|
8736
|
+
var import_node_path43 = __toESM(require("path"), 1);
|
|
8737
|
+
var import_types32 = require("@neat.is/types");
|
|
8608
8738
|
|
|
8609
8739
|
// src/extract/infra/shared.ts
|
|
8610
8740
|
init_cjs_shims();
|
|
8611
|
-
var
|
|
8741
|
+
var import_types31 = require("@neat.is/types");
|
|
8612
8742
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
8613
8743
|
return {
|
|
8614
|
-
id: (0,
|
|
8615
|
-
type:
|
|
8744
|
+
id: (0, import_types31.infraId)(kind, name),
|
|
8745
|
+
type: import_types31.NodeType.InfraNode,
|
|
8616
8746
|
name,
|
|
8617
8747
|
provider,
|
|
8618
8748
|
kind,
|
|
@@ -8656,8 +8786,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
|
|
|
8656
8786
|
source: anchorId,
|
|
8657
8787
|
target: node.id,
|
|
8658
8788
|
type: edgeType,
|
|
8659
|
-
provenance:
|
|
8660
|
-
confidence: (0,
|
|
8789
|
+
provenance: import_types31.Provenance.EXTRACTED,
|
|
8790
|
+
confidence: (0, import_types31.confidenceForExtracted)("structural"),
|
|
8661
8791
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
8662
8792
|
};
|
|
8663
8793
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -8674,7 +8804,7 @@ function dependsOnList(value) {
|
|
|
8674
8804
|
}
|
|
8675
8805
|
function serviceNameToServiceNode(name, services) {
|
|
8676
8806
|
for (const s of services) {
|
|
8677
|
-
if (s.node.name === name ||
|
|
8807
|
+
if (s.node.name === name || import_node_path43.default.basename(s.dir) === name) return s.node.id;
|
|
8678
8808
|
}
|
|
8679
8809
|
return null;
|
|
8680
8810
|
}
|
|
@@ -8683,7 +8813,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
8683
8813
|
let edgesAdded = 0;
|
|
8684
8814
|
let composePath = null;
|
|
8685
8815
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
8686
|
-
const abs =
|
|
8816
|
+
const abs = import_node_path43.default.join(scanPath, name);
|
|
8687
8817
|
if (await exists(abs)) {
|
|
8688
8818
|
composePath = abs;
|
|
8689
8819
|
break;
|
|
@@ -8696,13 +8826,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
8696
8826
|
} catch (err) {
|
|
8697
8827
|
recordExtractionError(
|
|
8698
8828
|
"infra docker-compose",
|
|
8699
|
-
|
|
8829
|
+
import_node_path43.default.relative(scanPath, composePath),
|
|
8700
8830
|
err
|
|
8701
8831
|
);
|
|
8702
8832
|
return { nodesAdded, edgesAdded };
|
|
8703
8833
|
}
|
|
8704
8834
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
8705
|
-
const evidenceFile =
|
|
8835
|
+
const evidenceFile = import_node_path43.default.relative(scanPath, composePath).split(import_node_path43.default.sep).join("/");
|
|
8706
8836
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
8707
8837
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
8708
8838
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -8724,15 +8854,15 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
8724
8854
|
for (const dep of dependsOnList(svc.depends_on)) {
|
|
8725
8855
|
const targetId = composeNameToNodeId.get(dep);
|
|
8726
8856
|
if (!targetId) continue;
|
|
8727
|
-
const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId,
|
|
8857
|
+
const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types32.EdgeType.DEPENDS_ON);
|
|
8728
8858
|
if (graph.hasEdge(edgeId)) continue;
|
|
8729
8859
|
const edge = {
|
|
8730
8860
|
id: edgeId,
|
|
8731
8861
|
source: sourceId,
|
|
8732
8862
|
target: targetId,
|
|
8733
|
-
type:
|
|
8734
|
-
provenance:
|
|
8735
|
-
confidence: (0,
|
|
8863
|
+
type: import_types32.EdgeType.DEPENDS_ON,
|
|
8864
|
+
provenance: import_types32.Provenance.EXTRACTED,
|
|
8865
|
+
confidence: (0, import_types32.confidenceForExtracted)("structural"),
|
|
8736
8866
|
evidence: { file: evidenceFile }
|
|
8737
8867
|
};
|
|
8738
8868
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -8744,9 +8874,9 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
8744
8874
|
|
|
8745
8875
|
// src/extract/infra/dockerfile.ts
|
|
8746
8876
|
init_cjs_shims();
|
|
8747
|
-
var
|
|
8877
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
8748
8878
|
var import_node_fs18 = require("fs");
|
|
8749
|
-
var
|
|
8879
|
+
var import_types33 = require("@neat.is/types");
|
|
8750
8880
|
function readDockerfile(content) {
|
|
8751
8881
|
let image = null;
|
|
8752
8882
|
const ports = [];
|
|
@@ -8775,7 +8905,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
8775
8905
|
let nodesAdded = 0;
|
|
8776
8906
|
let edgesAdded = 0;
|
|
8777
8907
|
for (const service of services) {
|
|
8778
|
-
const dockerfilePath =
|
|
8908
|
+
const dockerfilePath = import_node_path44.default.join(service.dir, "Dockerfile");
|
|
8779
8909
|
if (!await exists(dockerfilePath)) continue;
|
|
8780
8910
|
let content;
|
|
8781
8911
|
try {
|
|
@@ -8783,7 +8913,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
8783
8913
|
} catch (err) {
|
|
8784
8914
|
recordExtractionError(
|
|
8785
8915
|
"infra dockerfile",
|
|
8786
|
-
|
|
8916
|
+
import_node_path44.default.relative(scanPath, dockerfilePath),
|
|
8787
8917
|
err
|
|
8788
8918
|
);
|
|
8789
8919
|
continue;
|
|
@@ -8795,8 +8925,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
8795
8925
|
graph.addNode(node.id, node);
|
|
8796
8926
|
nodesAdded++;
|
|
8797
8927
|
}
|
|
8798
|
-
const relDockerfile = toPosix(
|
|
8799
|
-
const evidenceFile = toPosix(
|
|
8928
|
+
const relDockerfile = toPosix(import_node_path44.default.relative(service.dir, dockerfilePath));
|
|
8929
|
+
const evidenceFile = toPosix(import_node_path44.default.relative(scanPath, dockerfilePath));
|
|
8800
8930
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
8801
8931
|
graph,
|
|
8802
8932
|
service.pkg.name,
|
|
@@ -8805,15 +8935,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
8805
8935
|
);
|
|
8806
8936
|
nodesAdded += fn;
|
|
8807
8937
|
edgesAdded += fe;
|
|
8808
|
-
const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id,
|
|
8938
|
+
const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types33.EdgeType.RUNS_ON);
|
|
8809
8939
|
if (!graph.hasEdge(edgeId)) {
|
|
8810
8940
|
const edge = {
|
|
8811
8941
|
id: edgeId,
|
|
8812
8942
|
source: fileNodeId,
|
|
8813
8943
|
target: node.id,
|
|
8814
|
-
type:
|
|
8815
|
-
provenance:
|
|
8816
|
-
confidence: (0,
|
|
8944
|
+
type: import_types33.EdgeType.RUNS_ON,
|
|
8945
|
+
provenance: import_types33.Provenance.EXTRACTED,
|
|
8946
|
+
confidence: (0, import_types33.confidenceForExtracted)("structural"),
|
|
8817
8947
|
evidence: {
|
|
8818
8948
|
file: evidenceFile,
|
|
8819
8949
|
...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
|
|
@@ -8828,15 +8958,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
8828
8958
|
graph.addNode(portNode.id, portNode);
|
|
8829
8959
|
nodesAdded++;
|
|
8830
8960
|
}
|
|
8831
|
-
const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id,
|
|
8961
|
+
const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types33.EdgeType.CONNECTS_TO);
|
|
8832
8962
|
if (graph.hasEdge(portEdgeId)) continue;
|
|
8833
8963
|
const portEdge = {
|
|
8834
8964
|
id: portEdgeId,
|
|
8835
8965
|
source: fileNodeId,
|
|
8836
8966
|
target: portNode.id,
|
|
8837
|
-
type:
|
|
8838
|
-
provenance:
|
|
8839
|
-
confidence: (0,
|
|
8967
|
+
type: import_types33.EdgeType.CONNECTS_TO,
|
|
8968
|
+
provenance: import_types33.Provenance.EXTRACTED,
|
|
8969
|
+
confidence: (0, import_types33.confidenceForExtracted)("structural"),
|
|
8840
8970
|
evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
|
|
8841
8971
|
};
|
|
8842
8972
|
graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
|
|
@@ -8849,8 +8979,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
8849
8979
|
// src/extract/infra/terraform.ts
|
|
8850
8980
|
init_cjs_shims();
|
|
8851
8981
|
var import_node_fs19 = require("fs");
|
|
8852
|
-
var
|
|
8853
|
-
var
|
|
8982
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
8983
|
+
var import_types34 = require("@neat.is/types");
|
|
8854
8984
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
8855
8985
|
var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
|
|
8856
8986
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
@@ -8860,11 +8990,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
|
|
|
8860
8990
|
for (const entry2 of entries) {
|
|
8861
8991
|
if (entry2.isDirectory()) {
|
|
8862
8992
|
if (IGNORED_DIRS.has(entry2.name) || entry2.name === ".terraform") continue;
|
|
8863
|
-
const child =
|
|
8993
|
+
const child = import_node_path45.default.join(start, entry2.name);
|
|
8864
8994
|
if (await isPythonVenvDir(child)) continue;
|
|
8865
8995
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
8866
8996
|
} else if (entry2.isFile() && entry2.name.endsWith(".tf")) {
|
|
8867
|
-
out.push(
|
|
8997
|
+
out.push(import_node_path45.default.join(start, entry2.name));
|
|
8868
8998
|
}
|
|
8869
8999
|
}
|
|
8870
9000
|
return out;
|
|
@@ -8896,7 +9026,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
8896
9026
|
const files = await walkTfFiles(scanPath);
|
|
8897
9027
|
for (const file of files) {
|
|
8898
9028
|
const content = await import_node_fs19.promises.readFile(file, "utf8");
|
|
8899
|
-
const evidenceFile = toPosix(
|
|
9029
|
+
const evidenceFile = toPosix(import_node_path45.default.relative(scanPath, file));
|
|
8900
9030
|
const resources = [];
|
|
8901
9031
|
const byKey = /* @__PURE__ */ new Map();
|
|
8902
9032
|
RESOURCE_RE.lastIndex = 0;
|
|
@@ -8931,16 +9061,16 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
8931
9061
|
if (!target) continue;
|
|
8932
9062
|
if (seen.has(target.nodeId)) continue;
|
|
8933
9063
|
seen.add(target.nodeId);
|
|
8934
|
-
const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId,
|
|
9064
|
+
const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types34.EdgeType.DEPENDS_ON);
|
|
8935
9065
|
if (graph.hasEdge(edgeId)) continue;
|
|
8936
9066
|
const line = lineAt2(content, resource.bodyOffset + ref.index);
|
|
8937
9067
|
const edge = {
|
|
8938
9068
|
id: edgeId,
|
|
8939
9069
|
source: resource.nodeId,
|
|
8940
9070
|
target: target.nodeId,
|
|
8941
|
-
type:
|
|
8942
|
-
provenance:
|
|
8943
|
-
confidence: (0,
|
|
9071
|
+
type: import_types34.EdgeType.DEPENDS_ON,
|
|
9072
|
+
provenance: import_types34.Provenance.EXTRACTED,
|
|
9073
|
+
confidence: (0, import_types34.confidenceForExtracted)("structural"),
|
|
8944
9074
|
evidence: { file: evidenceFile, line, snippet: key }
|
|
8945
9075
|
};
|
|
8946
9076
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -8954,7 +9084,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
8954
9084
|
// src/extract/infra/k8s.ts
|
|
8955
9085
|
init_cjs_shims();
|
|
8956
9086
|
var import_node_fs20 = require("fs");
|
|
8957
|
-
var
|
|
9087
|
+
var import_node_path46 = __toESM(require("path"), 1);
|
|
8958
9088
|
var import_yaml3 = require("yaml");
|
|
8959
9089
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
8960
9090
|
Service: "k8s-service",
|
|
@@ -8972,11 +9102,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
|
8972
9102
|
for (const entry2 of entries) {
|
|
8973
9103
|
if (entry2.isDirectory()) {
|
|
8974
9104
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
8975
|
-
const child =
|
|
9105
|
+
const child = import_node_path46.default.join(start, entry2.name);
|
|
8976
9106
|
if (await isPythonVenvDir(child)) continue;
|
|
8977
9107
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
8978
|
-
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
8979
|
-
out.push(
|
|
9108
|
+
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path46.default.extname(entry2.name))) {
|
|
9109
|
+
out.push(import_node_path46.default.join(start, entry2.name));
|
|
8980
9110
|
}
|
|
8981
9111
|
}
|
|
8982
9112
|
return out;
|
|
@@ -9010,13 +9140,13 @@ async function addK8sResources(graph, scanPath) {
|
|
|
9010
9140
|
// src/extract/infra/cloudflare.ts
|
|
9011
9141
|
init_cjs_shims();
|
|
9012
9142
|
var import_node_fs21 = require("fs");
|
|
9013
|
-
var
|
|
9143
|
+
var import_node_path47 = __toESM(require("path"), 1);
|
|
9014
9144
|
var import_smol_toml2 = require("smol-toml");
|
|
9015
|
-
var
|
|
9145
|
+
var import_types35 = require("@neat.is/types");
|
|
9016
9146
|
var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
|
|
9017
9147
|
async function readWranglerConfig(dir) {
|
|
9018
9148
|
for (const filename of WRANGLER_FILENAMES) {
|
|
9019
|
-
const abs =
|
|
9149
|
+
const abs = import_node_path47.default.join(dir, filename);
|
|
9020
9150
|
if (!await exists(abs)) continue;
|
|
9021
9151
|
const raw = await import_node_fs21.promises.readFile(abs, "utf8");
|
|
9022
9152
|
const config = filename === "wrangler.toml" ? (0, import_smol_toml2.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -9060,8 +9190,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
|
|
|
9060
9190
|
source: anchorId,
|
|
9061
9191
|
target: node.id,
|
|
9062
9192
|
type: edgeType,
|
|
9063
|
-
provenance:
|
|
9064
|
-
confidence: (0,
|
|
9193
|
+
provenance: import_types35.Provenance.EXTRACTED,
|
|
9194
|
+
confidence: (0, import_types35.confidenceForExtracted)("structural"),
|
|
9065
9195
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
9066
9196
|
};
|
|
9067
9197
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -9079,11 +9209,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
9079
9209
|
try {
|
|
9080
9210
|
read = await readWranglerConfig(service.dir);
|
|
9081
9211
|
} catch (err) {
|
|
9082
|
-
recordExtractionError("infra cloudflare",
|
|
9212
|
+
recordExtractionError("infra cloudflare", import_node_path47.default.relative(scanPath, service.dir), err);
|
|
9083
9213
|
continue;
|
|
9084
9214
|
}
|
|
9085
9215
|
if (!read || !read.config.name) continue;
|
|
9086
|
-
const evidenceFile = toPosix(
|
|
9216
|
+
const evidenceFile = toPosix(import_node_path47.default.relative(scanPath, import_node_path47.default.join(service.dir, read.relFile)));
|
|
9087
9217
|
discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
|
|
9088
9218
|
}
|
|
9089
9219
|
for (const worker of discovered) {
|
|
@@ -9095,7 +9225,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
9095
9225
|
}
|
|
9096
9226
|
let anchorId = service.node.id;
|
|
9097
9227
|
if (config.main) {
|
|
9098
|
-
const entryRelPath = toPosix(
|
|
9228
|
+
const entryRelPath = toPosix(import_node_path47.default.normalize(config.main));
|
|
9099
9229
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
9100
9230
|
graph,
|
|
9101
9231
|
service.pkg.name,
|
|
@@ -9122,15 +9252,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
9122
9252
|
nodesAdded++;
|
|
9123
9253
|
}
|
|
9124
9254
|
if (runtimeNode.id !== anchorId) {
|
|
9125
|
-
const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id,
|
|
9255
|
+
const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types35.EdgeType.RUNS_ON);
|
|
9126
9256
|
if (!graph.hasEdge(runsOnId)) {
|
|
9127
9257
|
const edge = {
|
|
9128
9258
|
id: runsOnId,
|
|
9129
9259
|
source: anchorId,
|
|
9130
9260
|
target: runtimeNode.id,
|
|
9131
|
-
type:
|
|
9132
|
-
provenance:
|
|
9133
|
-
confidence: (0,
|
|
9261
|
+
type: import_types35.EdgeType.RUNS_ON,
|
|
9262
|
+
provenance: import_types35.Provenance.EXTRACTED,
|
|
9263
|
+
confidence: (0, import_types35.confidenceForExtracted)("structural"),
|
|
9134
9264
|
evidence: {
|
|
9135
9265
|
file: evidenceFile,
|
|
9136
9266
|
...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
|
|
@@ -9144,7 +9274,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
9144
9274
|
const result = addResourceEdge(
|
|
9145
9275
|
graph,
|
|
9146
9276
|
anchorId,
|
|
9147
|
-
|
|
9277
|
+
import_types35.EdgeType.CONNECTS_TO,
|
|
9148
9278
|
"cloudflare-route",
|
|
9149
9279
|
route,
|
|
9150
9280
|
evidenceFile,
|
|
@@ -9168,7 +9298,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
9168
9298
|
const result = addResourceEdge(
|
|
9169
9299
|
graph,
|
|
9170
9300
|
anchorId,
|
|
9171
|
-
|
|
9301
|
+
import_types35.EdgeType.DEPENDS_ON,
|
|
9172
9302
|
group.kind,
|
|
9173
9303
|
name,
|
|
9174
9304
|
evidenceFile,
|
|
@@ -9182,7 +9312,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
9182
9312
|
const result = addResourceEdge(
|
|
9183
9313
|
graph,
|
|
9184
9314
|
anchorId,
|
|
9185
|
-
|
|
9315
|
+
import_types35.EdgeType.DEPENDS_ON,
|
|
9186
9316
|
"cloudflare-cron",
|
|
9187
9317
|
cron,
|
|
9188
9318
|
evidenceFile,
|
|
@@ -9195,7 +9325,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
9195
9325
|
const result = addResourceEdge(
|
|
9196
9326
|
graph,
|
|
9197
9327
|
anchorId,
|
|
9198
|
-
|
|
9328
|
+
import_types35.EdgeType.DEPENDS_ON,
|
|
9199
9329
|
"cloudflare-env-var",
|
|
9200
9330
|
varName,
|
|
9201
9331
|
evidenceFile,
|
|
@@ -9208,15 +9338,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
9208
9338
|
if (!svc.service) continue;
|
|
9209
9339
|
const target = workerIndex.get(svc.service);
|
|
9210
9340
|
if (target && target.anchorId !== anchorId) {
|
|
9211
|
-
const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId,
|
|
9341
|
+
const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types35.EdgeType.CALLS);
|
|
9212
9342
|
if (!graph.hasEdge(edgeId)) {
|
|
9213
9343
|
const edge = {
|
|
9214
9344
|
id: edgeId,
|
|
9215
9345
|
source: anchorId,
|
|
9216
9346
|
target: target.anchorId,
|
|
9217
|
-
type:
|
|
9218
|
-
provenance:
|
|
9219
|
-
confidence: (0,
|
|
9347
|
+
type: import_types35.EdgeType.CALLS,
|
|
9348
|
+
provenance: import_types35.Provenance.EXTRACTED,
|
|
9349
|
+
confidence: (0, import_types35.confidenceForExtracted)("structural"),
|
|
9220
9350
|
evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
|
|
9221
9351
|
};
|
|
9222
9352
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -9227,7 +9357,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
9227
9357
|
const result = addResourceEdge(
|
|
9228
9358
|
graph,
|
|
9229
9359
|
anchorId,
|
|
9230
|
-
|
|
9360
|
+
import_types35.EdgeType.DEPENDS_ON,
|
|
9231
9361
|
"cloudflare-service-binding",
|
|
9232
9362
|
svc.service,
|
|
9233
9363
|
evidenceFile,
|
|
@@ -9243,12 +9373,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
9243
9373
|
// src/extract/infra/vercel.ts
|
|
9244
9374
|
init_cjs_shims();
|
|
9245
9375
|
var import_node_fs22 = require("fs");
|
|
9246
|
-
var
|
|
9247
|
-
var
|
|
9376
|
+
var import_node_path48 = __toESM(require("path"), 1);
|
|
9377
|
+
var import_types36 = require("@neat.is/types");
|
|
9248
9378
|
var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
|
|
9249
9379
|
async function readVercelConfig(dir) {
|
|
9250
9380
|
for (const filename of VERCEL_CONFIG_FILENAMES) {
|
|
9251
|
-
const abs =
|
|
9381
|
+
const abs = import_node_path48.default.join(dir, filename);
|
|
9252
9382
|
if (!await exists(abs)) continue;
|
|
9253
9383
|
const raw = await import_node_fs22.promises.readFile(abs, "utf8");
|
|
9254
9384
|
const config = JSON.parse(maskCommentsInSource(raw));
|
|
@@ -9257,7 +9387,7 @@ async function readVercelConfig(dir) {
|
|
|
9257
9387
|
return null;
|
|
9258
9388
|
}
|
|
9259
9389
|
async function readLinkedProjectName(dir) {
|
|
9260
|
-
const abs =
|
|
9390
|
+
const abs = import_node_path48.default.join(dir, ".vercel", "project.json");
|
|
9261
9391
|
if (!await exists(abs)) return void 0;
|
|
9262
9392
|
const parsed = JSON.parse(await import_node_fs22.promises.readFile(abs, "utf8"));
|
|
9263
9393
|
return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
|
|
@@ -9275,7 +9405,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
9275
9405
|
read = await readVercelConfig(service.dir);
|
|
9276
9406
|
projectName = await readLinkedProjectName(service.dir);
|
|
9277
9407
|
} catch (err) {
|
|
9278
|
-
recordExtractionError("infra vercel",
|
|
9408
|
+
recordExtractionError("infra vercel", import_node_path48.default.relative(scanPath, service.dir), err);
|
|
9279
9409
|
continue;
|
|
9280
9410
|
}
|
|
9281
9411
|
if (!read && !projectName) continue;
|
|
@@ -9291,7 +9421,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
9291
9421
|
const anchorId = service.node.id;
|
|
9292
9422
|
if (!read) continue;
|
|
9293
9423
|
const { config, relFile, raw } = read;
|
|
9294
|
-
const evidenceFile = toPosix(
|
|
9424
|
+
const evidenceFile = toPosix(import_node_path48.default.relative(scanPath, import_node_path48.default.join(service.dir, relFile)));
|
|
9295
9425
|
const add = (edgeType, kind, name) => {
|
|
9296
9426
|
if (!name) return;
|
|
9297
9427
|
const result = emitPlatformResourceEdge(
|
|
@@ -9307,12 +9437,12 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
9307
9437
|
nodesAdded += result.nodesAdded;
|
|
9308
9438
|
edgesAdded += result.edgesAdded;
|
|
9309
9439
|
};
|
|
9310
|
-
add(
|
|
9311
|
-
for (const cron of config.crons ?? []) add(
|
|
9312
|
-
for (const varName of Object.keys(config.env ?? {})) add(
|
|
9313
|
-
for (const varName of Object.keys(config.build?.env ?? {})) add(
|
|
9440
|
+
add(import_types36.EdgeType.RUNS_ON, "vercel", "vercel");
|
|
9441
|
+
for (const cron of config.crons ?? []) add(import_types36.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
|
|
9442
|
+
for (const varName of Object.keys(config.env ?? {})) add(import_types36.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
9443
|
+
for (const varName of Object.keys(config.build?.env ?? {})) add(import_types36.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
9314
9444
|
for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
|
|
9315
|
-
add(
|
|
9445
|
+
add(import_types36.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
|
|
9316
9446
|
}
|
|
9317
9447
|
}
|
|
9318
9448
|
return { nodesAdded, edgesAdded };
|
|
@@ -9321,13 +9451,13 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
9321
9451
|
// src/extract/infra/railway.ts
|
|
9322
9452
|
init_cjs_shims();
|
|
9323
9453
|
var import_node_fs23 = require("fs");
|
|
9324
|
-
var
|
|
9454
|
+
var import_node_path49 = __toESM(require("path"), 1);
|
|
9325
9455
|
var import_smol_toml3 = require("smol-toml");
|
|
9326
|
-
var
|
|
9456
|
+
var import_types37 = require("@neat.is/types");
|
|
9327
9457
|
var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
|
|
9328
9458
|
async function readRailwayConfig(dir) {
|
|
9329
9459
|
for (const filename of RAILWAY_FILENAMES) {
|
|
9330
|
-
const abs =
|
|
9460
|
+
const abs = import_node_path49.default.join(dir, filename);
|
|
9331
9461
|
if (!await exists(abs)) continue;
|
|
9332
9462
|
const raw = await import_node_fs23.promises.readFile(abs, "utf8");
|
|
9333
9463
|
const config = filename === "railway.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -9343,7 +9473,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
9343
9473
|
try {
|
|
9344
9474
|
read = await readRailwayConfig(service.dir);
|
|
9345
9475
|
} catch (err) {
|
|
9346
|
-
recordExtractionError("infra railway",
|
|
9476
|
+
recordExtractionError("infra railway", import_node_path49.default.relative(scanPath, service.dir), err);
|
|
9347
9477
|
continue;
|
|
9348
9478
|
}
|
|
9349
9479
|
if (!read) continue;
|
|
@@ -9353,7 +9483,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
9353
9483
|
}
|
|
9354
9484
|
const anchorId = service.node.id;
|
|
9355
9485
|
const { config, relFile, raw } = read;
|
|
9356
|
-
const evidenceFile = toPosix(
|
|
9486
|
+
const evidenceFile = toPosix(import_node_path49.default.relative(scanPath, import_node_path49.default.join(service.dir, relFile)));
|
|
9357
9487
|
const add = (edgeType, kind, name) => {
|
|
9358
9488
|
if (!name) return;
|
|
9359
9489
|
const result = emitPlatformResourceEdge(
|
|
@@ -9369,9 +9499,9 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
9369
9499
|
nodesAdded += result.nodesAdded;
|
|
9370
9500
|
edgesAdded += result.edgesAdded;
|
|
9371
9501
|
};
|
|
9372
|
-
add(
|
|
9373
|
-
add(
|
|
9374
|
-
add(
|
|
9502
|
+
add(import_types37.EdgeType.RUNS_ON, "railway", "railway");
|
|
9503
|
+
add(import_types37.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
|
|
9504
|
+
add(import_types37.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
|
|
9375
9505
|
}
|
|
9376
9506
|
return { nodesAdded, edgesAdded };
|
|
9377
9507
|
}
|
|
@@ -9379,12 +9509,12 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
9379
9509
|
// src/extract/infra/supabase.ts
|
|
9380
9510
|
init_cjs_shims();
|
|
9381
9511
|
var import_node_fs24 = require("fs");
|
|
9382
|
-
var
|
|
9512
|
+
var import_node_path50 = __toESM(require("path"), 1);
|
|
9383
9513
|
var import_smol_toml4 = require("smol-toml");
|
|
9384
|
-
var
|
|
9514
|
+
var import_types38 = require("@neat.is/types");
|
|
9385
9515
|
async function readSupabaseConfig(dir) {
|
|
9386
|
-
const relFile =
|
|
9387
|
-
const abs =
|
|
9516
|
+
const relFile = import_node_path50.default.join("supabase", "config.toml");
|
|
9517
|
+
const abs = import_node_path50.default.join(dir, relFile);
|
|
9388
9518
|
if (!await exists(abs)) return null;
|
|
9389
9519
|
const raw = await import_node_fs24.promises.readFile(abs, "utf8");
|
|
9390
9520
|
const config = (0, import_smol_toml4.parse)(raw);
|
|
@@ -9398,7 +9528,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
9398
9528
|
try {
|
|
9399
9529
|
read = await readSupabaseConfig(service.dir);
|
|
9400
9530
|
} catch (err) {
|
|
9401
|
-
recordExtractionError("infra supabase",
|
|
9531
|
+
recordExtractionError("infra supabase", import_node_path50.default.relative(scanPath, service.dir), err);
|
|
9402
9532
|
continue;
|
|
9403
9533
|
}
|
|
9404
9534
|
if (!read) continue;
|
|
@@ -9413,7 +9543,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
9413
9543
|
});
|
|
9414
9544
|
}
|
|
9415
9545
|
const anchorId = service.node.id;
|
|
9416
|
-
const evidenceFile = toPosix(
|
|
9546
|
+
const evidenceFile = toPosix(import_node_path50.default.relative(scanPath, import_node_path50.default.join(service.dir, relFile)));
|
|
9417
9547
|
const add = (edgeType, kind, name) => {
|
|
9418
9548
|
if (!name) return;
|
|
9419
9549
|
const result = emitPlatformResourceEdge(
|
|
@@ -9429,10 +9559,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
9429
9559
|
nodesAdded += result.nodesAdded;
|
|
9430
9560
|
edgesAdded += result.edgesAdded;
|
|
9431
9561
|
};
|
|
9432
|
-
add(
|
|
9433
|
-
for (const fn of Object.keys(config.functions ?? {})) add(
|
|
9434
|
-
if (config.storage) add(
|
|
9435
|
-
if (config.auth) add(
|
|
9562
|
+
add(import_types38.EdgeType.RUNS_ON, "supabase", "supabase");
|
|
9563
|
+
for (const fn of Object.keys(config.functions ?? {})) add(import_types38.EdgeType.DEPENDS_ON, "supabase-function", fn);
|
|
9564
|
+
if (config.storage) add(import_types38.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
|
|
9565
|
+
if (config.auth) add(import_types38.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
|
|
9436
9566
|
}
|
|
9437
9567
|
return { nodesAdded, edgesAdded };
|
|
9438
9568
|
}
|
|
@@ -9454,17 +9584,17 @@ async function addInfra(graph, scanPath, services) {
|
|
|
9454
9584
|
}
|
|
9455
9585
|
|
|
9456
9586
|
// src/extract/index.ts
|
|
9457
|
-
var
|
|
9587
|
+
var import_node_path52 = __toESM(require("path"), 1);
|
|
9458
9588
|
|
|
9459
9589
|
// src/extract/retire.ts
|
|
9460
9590
|
init_cjs_shims();
|
|
9461
9591
|
var import_node_fs25 = require("fs");
|
|
9462
|
-
var
|
|
9463
|
-
var
|
|
9592
|
+
var import_node_path51 = __toESM(require("path"), 1);
|
|
9593
|
+
var import_types39 = require("@neat.is/types");
|
|
9464
9594
|
function dropOrphanedFileNodes(graph) {
|
|
9465
9595
|
const orphans = [];
|
|
9466
9596
|
graph.forEachNode((id, attrs) => {
|
|
9467
|
-
if (attrs.type !==
|
|
9597
|
+
if (attrs.type !== import_types39.NodeType.FileNode) return;
|
|
9468
9598
|
if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
|
|
9469
9599
|
orphans.push(id);
|
|
9470
9600
|
}
|
|
@@ -9477,7 +9607,7 @@ function retireEdgesByFile(graph, file) {
|
|
|
9477
9607
|
const toDrop = [];
|
|
9478
9608
|
graph.forEachEdge((id, attrs) => {
|
|
9479
9609
|
const edge = attrs;
|
|
9480
|
-
if (edge.provenance !==
|
|
9610
|
+
if (edge.provenance !== import_types39.Provenance.EXTRACTED) return;
|
|
9481
9611
|
if (!edge.evidence?.file) return;
|
|
9482
9612
|
if (edge.evidence.file === normalized) toDrop.push(id);
|
|
9483
9613
|
});
|
|
@@ -9490,14 +9620,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
9490
9620
|
const bases = [scanPath, ...serviceDirs];
|
|
9491
9621
|
graph.forEachEdge((id, attrs) => {
|
|
9492
9622
|
const edge = attrs;
|
|
9493
|
-
if (edge.provenance !==
|
|
9623
|
+
if (edge.provenance !== import_types39.Provenance.EXTRACTED) return;
|
|
9494
9624
|
const evidenceFile = edge.evidence?.file;
|
|
9495
9625
|
if (!evidenceFile) return;
|
|
9496
|
-
if (
|
|
9626
|
+
if (import_node_path51.default.isAbsolute(evidenceFile)) {
|
|
9497
9627
|
if (!(0, import_node_fs25.existsSync)(evidenceFile)) toDrop.push(id);
|
|
9498
9628
|
return;
|
|
9499
9629
|
}
|
|
9500
|
-
const found = bases.some((base) => (0, import_node_fs25.existsSync)(
|
|
9630
|
+
const found = bases.some((base) => (0, import_node_fs25.existsSync)(import_node_path51.default.join(base, evidenceFile)));
|
|
9501
9631
|
if (!found) toDrop.push(id);
|
|
9502
9632
|
});
|
|
9503
9633
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -9550,7 +9680,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
9550
9680
|
}
|
|
9551
9681
|
const droppedEntries = drainDroppedExtracted();
|
|
9552
9682
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
9553
|
-
const rejectedPath =
|
|
9683
|
+
const rejectedPath = import_node_path52.default.join(import_node_path52.default.dirname(opts.errorsPath), "rejected.ndjson");
|
|
9554
9684
|
try {
|
|
9555
9685
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
9556
9686
|
} catch (err) {
|
|
@@ -9584,39 +9714,39 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
9584
9714
|
|
|
9585
9715
|
// src/divergences.ts
|
|
9586
9716
|
init_cjs_shims();
|
|
9587
|
-
var
|
|
9717
|
+
var import_types40 = require("@neat.is/types");
|
|
9588
9718
|
function bucketKey(source, target, type) {
|
|
9589
9719
|
return `${type}|${source}|${target}`;
|
|
9590
9720
|
}
|
|
9591
9721
|
function bucketSourceFor(graph, edge) {
|
|
9592
|
-
if (edge.type !==
|
|
9593
|
-
const parsed = (0,
|
|
9722
|
+
if (edge.type !== import_types40.EdgeType.CONNECTS_TO) return edge.source;
|
|
9723
|
+
const parsed = (0, import_types40.parseFileId)(edge.source);
|
|
9594
9724
|
if (!parsed || !graph.hasNode(edge.target)) return edge.source;
|
|
9595
9725
|
const target = graph.getNodeAttributes(edge.target);
|
|
9596
|
-
if (target.type !==
|
|
9597
|
-
return (0,
|
|
9726
|
+
if (target.type !== import_types40.NodeType.DatabaseNode) return edge.source;
|
|
9727
|
+
return (0, import_types40.serviceId)(parsed.service);
|
|
9598
9728
|
}
|
|
9599
9729
|
function bucketEdges(graph) {
|
|
9600
9730
|
const buckets2 = /* @__PURE__ */ new Map();
|
|
9601
9731
|
graph.forEachEdge((id, attrs) => {
|
|
9602
9732
|
const e = attrs;
|
|
9603
|
-
const parsed = (0,
|
|
9733
|
+
const parsed = (0, import_types40.parseEdgeId)(id);
|
|
9604
9734
|
const provenance = parsed?.provenance ?? e.provenance;
|
|
9605
9735
|
const source = bucketSourceFor(graph, e);
|
|
9606
9736
|
const key = bucketKey(source, e.target, e.type);
|
|
9607
9737
|
const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
|
|
9608
9738
|
switch (provenance) {
|
|
9609
|
-
case
|
|
9739
|
+
case import_types40.Provenance.EXTRACTED:
|
|
9610
9740
|
cur.extracted = e;
|
|
9611
9741
|
break;
|
|
9612
|
-
case
|
|
9742
|
+
case import_types40.Provenance.OBSERVED:
|
|
9613
9743
|
cur.observed = e;
|
|
9614
9744
|
break;
|
|
9615
|
-
case
|
|
9745
|
+
case import_types40.Provenance.INFERRED:
|
|
9616
9746
|
cur.inferred = e;
|
|
9617
9747
|
break;
|
|
9618
9748
|
default:
|
|
9619
|
-
if (e.provenance ===
|
|
9749
|
+
if (e.provenance === import_types40.Provenance.STALE) cur.stale = e;
|
|
9620
9750
|
}
|
|
9621
9751
|
buckets2.set(key, cur);
|
|
9622
9752
|
});
|
|
@@ -9625,17 +9755,17 @@ function bucketEdges(graph) {
|
|
|
9625
9755
|
function nodeIsFrontier(graph, nodeId) {
|
|
9626
9756
|
if (!graph.hasNode(nodeId)) return false;
|
|
9627
9757
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
9628
|
-
return attrs.type ===
|
|
9758
|
+
return attrs.type === import_types40.NodeType.FrontierNode;
|
|
9629
9759
|
}
|
|
9630
9760
|
function nodeIsWebsocketChannel(graph, nodeId) {
|
|
9631
9761
|
if (!graph.hasNode(nodeId)) return false;
|
|
9632
9762
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
9633
|
-
return attrs.type ===
|
|
9763
|
+
return attrs.type === import_types40.NodeType.WebSocketChannelNode;
|
|
9634
9764
|
}
|
|
9635
9765
|
function nodeIsSymbol(graph, nodeId) {
|
|
9636
9766
|
if (!graph.hasNode(nodeId)) return false;
|
|
9637
9767
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
9638
|
-
return attrs.type ===
|
|
9768
|
+
return attrs.type === import_types40.NodeType.SymbolNode;
|
|
9639
9769
|
}
|
|
9640
9770
|
function clampConfidence(n) {
|
|
9641
9771
|
if (!Number.isFinite(n)) return 0;
|
|
@@ -9655,14 +9785,14 @@ function gradedConfidence(edge) {
|
|
|
9655
9785
|
return clampConfidence(confidenceForEdge(edge));
|
|
9656
9786
|
}
|
|
9657
9787
|
var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
|
|
9658
|
-
|
|
9659
|
-
|
|
9660
|
-
|
|
9661
|
-
|
|
9788
|
+
import_types40.EdgeType.CALLS,
|
|
9789
|
+
import_types40.EdgeType.CONNECTS_TO,
|
|
9790
|
+
import_types40.EdgeType.PUBLISHES_TO,
|
|
9791
|
+
import_types40.EdgeType.CONSUMES_FROM
|
|
9662
9792
|
]);
|
|
9663
9793
|
function detectMissingDivergences(graph, bucket) {
|
|
9664
9794
|
const out = [];
|
|
9665
|
-
if (bucket.type ===
|
|
9795
|
+
if (bucket.type === import_types40.EdgeType.CONTAINS) return out;
|
|
9666
9796
|
if (nodeIsSymbol(graph, bucket.source) || nodeIsSymbol(graph, bucket.target)) return out;
|
|
9667
9797
|
if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
|
|
9668
9798
|
if (!nodeIsFrontier(graph, bucket.target)) {
|
|
@@ -9704,7 +9834,7 @@ function declaredHostFor(svc) {
|
|
|
9704
9834
|
function hasExtractedConfiguredBy(graph, svcId) {
|
|
9705
9835
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
9706
9836
|
const e = graph.getEdgeAttributes(edgeId);
|
|
9707
|
-
if (e.type ===
|
|
9837
|
+
if (e.type === import_types40.EdgeType.CONFIGURED_BY && e.provenance === import_types40.Provenance.EXTRACTED) {
|
|
9708
9838
|
return true;
|
|
9709
9839
|
}
|
|
9710
9840
|
}
|
|
@@ -9717,10 +9847,10 @@ function detectHostMismatch(graph, svcId, svc) {
|
|
|
9717
9847
|
const out = [];
|
|
9718
9848
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
9719
9849
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
9720
|
-
if (edge.type !==
|
|
9721
|
-
if (edge.provenance !==
|
|
9850
|
+
if (edge.type !== import_types40.EdgeType.CONNECTS_TO) continue;
|
|
9851
|
+
if (edge.provenance !== import_types40.Provenance.OBSERVED) continue;
|
|
9722
9852
|
const target = graph.getNodeAttributes(edge.target);
|
|
9723
|
-
if (target.type !==
|
|
9853
|
+
if (target.type !== import_types40.NodeType.DatabaseNode) continue;
|
|
9724
9854
|
const observedHost = target.host?.trim();
|
|
9725
9855
|
if (!observedHost) continue;
|
|
9726
9856
|
if (observedHost === declaredHost) continue;
|
|
@@ -9742,10 +9872,10 @@ function detectCompatDivergences(graph, svcId, svc) {
|
|
|
9742
9872
|
const deps = svc.dependencies ?? {};
|
|
9743
9873
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
9744
9874
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
9745
|
-
if (edge.type !==
|
|
9746
|
-
if (edge.provenance !==
|
|
9875
|
+
if (edge.type !== import_types40.EdgeType.CONNECTS_TO) continue;
|
|
9876
|
+
if (edge.provenance !== import_types40.Provenance.OBSERVED) continue;
|
|
9747
9877
|
const target = graph.getNodeAttributes(edge.target);
|
|
9748
|
-
if (target.type !==
|
|
9878
|
+
if (target.type !== import_types40.NodeType.DatabaseNode) continue;
|
|
9749
9879
|
for (const pair of compatPairs()) {
|
|
9750
9880
|
if (pair.engine !== target.engine) continue;
|
|
9751
9881
|
const declared = deps[pair.driver];
|
|
@@ -9842,7 +9972,7 @@ function suppressHostMismatchHalves(all) {
|
|
|
9842
9972
|
for (const d of all) {
|
|
9843
9973
|
if (d.type !== "host-mismatch") continue;
|
|
9844
9974
|
observedHalf.add(`${d.source}->${d.target}`);
|
|
9845
|
-
declaredHalf.add((0,
|
|
9975
|
+
declaredHalf.add((0, import_types40.databaseId)(d.extractedHost));
|
|
9846
9976
|
}
|
|
9847
9977
|
if (observedHalf.size === 0) return all;
|
|
9848
9978
|
return all.filter((d) => {
|
|
@@ -9861,13 +9991,13 @@ function computeDivergences(graph, opts = {}) {
|
|
|
9861
9991
|
}
|
|
9862
9992
|
graph.forEachNode((nodeId, attrs) => {
|
|
9863
9993
|
const n = attrs;
|
|
9864
|
-
if (n.type ===
|
|
9994
|
+
if (n.type === import_types40.NodeType.ServiceNode) {
|
|
9865
9995
|
const svc = n;
|
|
9866
9996
|
for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
|
|
9867
9997
|
for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
|
|
9868
9998
|
return;
|
|
9869
9999
|
}
|
|
9870
|
-
if (n.type ===
|
|
10000
|
+
if (n.type === import_types40.NodeType.InfraNode && n.kind === "sql-table") {
|
|
9871
10001
|
for (const d of detectColumnDrift(n)) all.push(d);
|
|
9872
10002
|
}
|
|
9873
10003
|
});
|
|
@@ -9903,7 +10033,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
9903
10033
|
const bc = "column" in b && b.column ? b.column : "";
|
|
9904
10034
|
return ac.localeCompare(bc);
|
|
9905
10035
|
});
|
|
9906
|
-
return
|
|
10036
|
+
return import_types40.DivergenceResultSchema.parse({
|
|
9907
10037
|
divergences: filtered,
|
|
9908
10038
|
totalAffected: filtered.length,
|
|
9909
10039
|
computedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -9913,8 +10043,8 @@ function computeDivergences(graph, opts = {}) {
|
|
|
9913
10043
|
// src/persist.ts
|
|
9914
10044
|
init_cjs_shims();
|
|
9915
10045
|
var import_node_fs26 = require("fs");
|
|
9916
|
-
var
|
|
9917
|
-
var
|
|
10046
|
+
var import_node_path53 = __toESM(require("path"), 1);
|
|
10047
|
+
var import_types41 = require("@neat.is/types");
|
|
9918
10048
|
var SCHEMA_VERSION = 6;
|
|
9919
10049
|
function migrateV1ToV2(payload) {
|
|
9920
10050
|
const nodes = payload.graph.nodes;
|
|
@@ -9938,7 +10068,7 @@ function migrateV5ToV6(payload) {
|
|
|
9938
10068
|
if (Array.isArray(nodes)) {
|
|
9939
10069
|
for (const node of nodes) {
|
|
9940
10070
|
const attrs = node.attributes;
|
|
9941
|
-
if (!attrs || attrs.type !==
|
|
10071
|
+
if (!attrs || attrs.type !== import_types41.NodeType.InfraNode) continue;
|
|
9942
10072
|
if (attrs.kind !== "sql-table" && attrs.kind !== "supabase-table") continue;
|
|
9943
10073
|
if (!Array.isArray(attrs.columns)) attrs.columns = [];
|
|
9944
10074
|
}
|
|
@@ -9951,12 +10081,12 @@ function migrateV2ToV3(payload) {
|
|
|
9951
10081
|
for (const edge of edges) {
|
|
9952
10082
|
const attrs = edge.attributes;
|
|
9953
10083
|
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
9954
|
-
attrs.provenance =
|
|
10084
|
+
attrs.provenance = import_types41.Provenance.OBSERVED;
|
|
9955
10085
|
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
9956
10086
|
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
9957
10087
|
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
9958
10088
|
if (type && source && target) {
|
|
9959
|
-
const newId = (0,
|
|
10089
|
+
const newId = (0, import_types41.observedEdgeId)(source, target, type);
|
|
9960
10090
|
attrs.id = newId;
|
|
9961
10091
|
if (edge.key) edge.key = newId;
|
|
9962
10092
|
}
|
|
@@ -9965,7 +10095,7 @@ function migrateV2ToV3(payload) {
|
|
|
9965
10095
|
return { ...payload, schemaVersion: 3 };
|
|
9966
10096
|
}
|
|
9967
10097
|
async function ensureDir(filePath) {
|
|
9968
|
-
await import_node_fs26.promises.mkdir(
|
|
10098
|
+
await import_node_fs26.promises.mkdir(import_node_path53.default.dirname(filePath), { recursive: true });
|
|
9969
10099
|
}
|
|
9970
10100
|
async function saveGraphToDisk(graph, outPath) {
|
|
9971
10101
|
await ensureDir(outPath);
|
|
@@ -10053,7 +10183,7 @@ function startPersistLoop(graph, outPath, opts = {}) {
|
|
|
10053
10183
|
// src/gitignore.ts
|
|
10054
10184
|
init_cjs_shims();
|
|
10055
10185
|
var import_node_fs27 = require("fs");
|
|
10056
|
-
var
|
|
10186
|
+
var import_node_path54 = __toESM(require("path"), 1);
|
|
10057
10187
|
var NEAT_OUT_LINE = "neat-out/";
|
|
10058
10188
|
var NEAT_HEADER = "# NEAT \u2014 machine-local snapshots and events";
|
|
10059
10189
|
function isNeatOutLine(line) {
|
|
@@ -10061,7 +10191,7 @@ function isNeatOutLine(line) {
|
|
|
10061
10191
|
return trimmed === "neat-out/" || trimmed === "neat-out";
|
|
10062
10192
|
}
|
|
10063
10193
|
async function ensureNeatOutIgnored(projectDir) {
|
|
10064
|
-
const file =
|
|
10194
|
+
const file = import_node_path54.default.join(projectDir, ".gitignore");
|
|
10065
10195
|
let existing = null;
|
|
10066
10196
|
try {
|
|
10067
10197
|
existing = await import_node_fs27.promises.readFile(file, "utf8");
|
|
@@ -10088,7 +10218,7 @@ ${NEAT_OUT_LINE}
|
|
|
10088
10218
|
|
|
10089
10219
|
// src/summary.ts
|
|
10090
10220
|
init_cjs_shims();
|
|
10091
|
-
var
|
|
10221
|
+
var import_types42 = require("@neat.is/types");
|
|
10092
10222
|
function renderOtelEnvBlock() {
|
|
10093
10223
|
return [
|
|
10094
10224
|
"for prod OTel routing, set these in your deploy platform's env:",
|
|
@@ -10098,19 +10228,19 @@ function renderOtelEnvBlock() {
|
|
|
10098
10228
|
}
|
|
10099
10229
|
function findIncompatServices(nodes) {
|
|
10100
10230
|
return nodes.filter(
|
|
10101
|
-
(n) => n.type ===
|
|
10231
|
+
(n) => n.type === import_types42.NodeType.ServiceNode && Array.isArray(n.incompatibilities) && (n.incompatibilities ?? []).length > 0
|
|
10102
10232
|
);
|
|
10103
10233
|
}
|
|
10104
10234
|
function servicesWithoutObserved(nodes, edges) {
|
|
10105
10235
|
const seen = /* @__PURE__ */ new Set();
|
|
10106
10236
|
for (const e of edges) {
|
|
10107
|
-
if (e.provenance ===
|
|
10237
|
+
if (e.provenance === import_types42.Provenance.OBSERVED) {
|
|
10108
10238
|
seen.add(e.source);
|
|
10109
10239
|
seen.add(e.target);
|
|
10110
10240
|
}
|
|
10111
10241
|
}
|
|
10112
10242
|
return nodes.filter(
|
|
10113
|
-
(n) => n.type ===
|
|
10243
|
+
(n) => n.type === import_types42.NodeType.ServiceNode && !seen.has(n.id)
|
|
10114
10244
|
);
|
|
10115
10245
|
}
|
|
10116
10246
|
function formatDivergence(d) {
|
|
@@ -10185,26 +10315,26 @@ function formatIncompat(inc) {
|
|
|
10185
10315
|
// src/watch.ts
|
|
10186
10316
|
init_cjs_shims();
|
|
10187
10317
|
var import_node_fs36 = __toESM(require("fs"), 1);
|
|
10188
|
-
var
|
|
10318
|
+
var import_node_path63 = __toESM(require("path"), 1);
|
|
10189
10319
|
var import_chokidar = __toESM(require("chokidar"), 1);
|
|
10190
10320
|
|
|
10191
10321
|
// src/api.ts
|
|
10192
10322
|
init_cjs_shims();
|
|
10193
10323
|
var import_fastify2 = __toESM(require("fastify"), 1);
|
|
10194
10324
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
10195
|
-
var
|
|
10325
|
+
var import_types60 = require("@neat.is/types");
|
|
10196
10326
|
|
|
10197
10327
|
// src/extend/index.ts
|
|
10198
10328
|
init_cjs_shims();
|
|
10199
10329
|
var import_node_fs29 = require("fs");
|
|
10200
|
-
var
|
|
10330
|
+
var import_node_path56 = __toESM(require("path"), 1);
|
|
10201
10331
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
10202
10332
|
var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
|
|
10203
10333
|
|
|
10204
10334
|
// src/installers/package-manager.ts
|
|
10205
10335
|
init_cjs_shims();
|
|
10206
10336
|
var import_node_fs28 = require("fs");
|
|
10207
|
-
var
|
|
10337
|
+
var import_node_path55 = __toESM(require("path"), 1);
|
|
10208
10338
|
var import_node_child_process = require("child_process");
|
|
10209
10339
|
var LOCKFILE_PRIORITY = [
|
|
10210
10340
|
{ lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
|
|
@@ -10226,22 +10356,22 @@ async function exists2(p) {
|
|
|
10226
10356
|
}
|
|
10227
10357
|
}
|
|
10228
10358
|
async function detectPackageManager(serviceDir) {
|
|
10229
|
-
let dir =
|
|
10359
|
+
let dir = import_node_path55.default.resolve(serviceDir);
|
|
10230
10360
|
const stops = /* @__PURE__ */ new Set();
|
|
10231
10361
|
for (let i = 0; i < 64; i++) {
|
|
10232
10362
|
if (stops.has(dir)) break;
|
|
10233
10363
|
stops.add(dir);
|
|
10234
10364
|
for (const candidate of LOCKFILE_PRIORITY) {
|
|
10235
|
-
const lockPath =
|
|
10365
|
+
const lockPath = import_node_path55.default.join(dir, candidate.lockfile);
|
|
10236
10366
|
if (await exists2(lockPath)) {
|
|
10237
10367
|
return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
|
|
10238
10368
|
}
|
|
10239
10369
|
}
|
|
10240
|
-
const parent =
|
|
10370
|
+
const parent = import_node_path55.default.dirname(dir);
|
|
10241
10371
|
if (parent === dir) break;
|
|
10242
10372
|
dir = parent;
|
|
10243
10373
|
}
|
|
10244
|
-
return { pm: "npm", cwd:
|
|
10374
|
+
return { pm: "npm", cwd: import_node_path55.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
|
|
10245
10375
|
}
|
|
10246
10376
|
async function runPackageManagerInstall(cmd) {
|
|
10247
10377
|
return new Promise((resolve) => {
|
|
@@ -10290,7 +10420,7 @@ async function fileExists2(p) {
|
|
|
10290
10420
|
}
|
|
10291
10421
|
}
|
|
10292
10422
|
async function readPackageJson(scanPath) {
|
|
10293
|
-
const pkgPath =
|
|
10423
|
+
const pkgPath = import_node_path56.default.join(scanPath, "package.json");
|
|
10294
10424
|
const raw = await import_node_fs29.promises.readFile(pkgPath, "utf8");
|
|
10295
10425
|
return JSON.parse(raw);
|
|
10296
10426
|
}
|
|
@@ -10309,11 +10439,11 @@ async function findHookFiles(scanPath) {
|
|
|
10309
10439
|
for (const entry2 of entries) {
|
|
10310
10440
|
if (entry2.isDirectory()) {
|
|
10311
10441
|
if (entry2.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry2.name)) continue;
|
|
10312
|
-
await walk6(
|
|
10442
|
+
await walk6(import_node_path56.default.join(dir, entry2.name));
|
|
10313
10443
|
} else if (entry2.isFile()) {
|
|
10314
10444
|
if ((entry2.name.startsWith("instrumentation") || entry2.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry2.name)) {
|
|
10315
|
-
const rel =
|
|
10316
|
-
found.push(rel.split(
|
|
10445
|
+
const rel = import_node_path56.default.relative(scanPath, import_node_path56.default.join(dir, entry2.name));
|
|
10446
|
+
found.push(rel.split(import_node_path56.default.sep).join("/"));
|
|
10317
10447
|
}
|
|
10318
10448
|
}
|
|
10319
10449
|
}
|
|
@@ -10324,7 +10454,7 @@ async function findHookFiles(scanPath) {
|
|
|
10324
10454
|
async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
10325
10455
|
let fallback = null;
|
|
10326
10456
|
for (const file of hookFiles) {
|
|
10327
|
-
const content = await import_node_fs29.promises.readFile(
|
|
10457
|
+
const content = await import_node_fs29.promises.readFile(import_node_path56.default.join(scanPath, file), "utf8");
|
|
10328
10458
|
const patched = splicedContent(content, snippet2);
|
|
10329
10459
|
if (patched !== null) return { file, content, patched };
|
|
10330
10460
|
if (fallback === null) fallback = { file, content };
|
|
@@ -10332,11 +10462,11 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
|
10332
10462
|
return { file: fallback.file, content: fallback.content, patched: null };
|
|
10333
10463
|
}
|
|
10334
10464
|
function extendLogPath() {
|
|
10335
|
-
return process.env.NEAT_EXTEND_LOG ??
|
|
10465
|
+
return process.env.NEAT_EXTEND_LOG ?? import_node_path56.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
|
|
10336
10466
|
}
|
|
10337
10467
|
async function appendExtendLog(entry2) {
|
|
10338
10468
|
const logPath = extendLogPath();
|
|
10339
|
-
await import_node_fs29.promises.mkdir(
|
|
10469
|
+
await import_node_fs29.promises.mkdir(import_node_path56.default.dirname(logPath), { recursive: true });
|
|
10340
10470
|
await import_node_fs29.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
|
|
10341
10471
|
}
|
|
10342
10472
|
function splicedContent(fileContent, snippet2) {
|
|
@@ -10395,7 +10525,7 @@ function lookupInstrumentation(library, installedVersion) {
|
|
|
10395
10525
|
}
|
|
10396
10526
|
async function describeProjectInstrumentation(ctx) {
|
|
10397
10527
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
10398
|
-
const envNeat = await fileExists2(
|
|
10528
|
+
const envNeat = await fileExists2(import_node_path56.default.join(ctx.scanPath, ".env.neat"));
|
|
10399
10529
|
const registryInstrPackages = new Set(
|
|
10400
10530
|
(0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
|
|
10401
10531
|
);
|
|
@@ -10417,7 +10547,7 @@ async function applyExtension(ctx, args, options) {
|
|
|
10417
10547
|
);
|
|
10418
10548
|
}
|
|
10419
10549
|
for (const file of hookFiles) {
|
|
10420
|
-
const content = await import_node_fs29.promises.readFile(
|
|
10550
|
+
const content = await import_node_fs29.promises.readFile(import_node_path56.default.join(ctx.scanPath, file), "utf8");
|
|
10421
10551
|
if (content.includes(args.registration_snippet)) {
|
|
10422
10552
|
return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
|
|
10423
10553
|
}
|
|
@@ -10429,10 +10559,10 @@ async function applyExtension(ctx, args, options) {
|
|
|
10429
10559
|
);
|
|
10430
10560
|
}
|
|
10431
10561
|
const primaryFile = primary.file;
|
|
10432
|
-
const primaryPath =
|
|
10562
|
+
const primaryPath = import_node_path56.default.join(ctx.scanPath, primaryFile);
|
|
10433
10563
|
const filesTouched = [];
|
|
10434
10564
|
const depsAdded = [];
|
|
10435
|
-
const pkgPath =
|
|
10565
|
+
const pkgPath = import_node_path56.default.join(ctx.scanPath, "package.json");
|
|
10436
10566
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
10437
10567
|
if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
|
|
10438
10568
|
pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
|
|
@@ -10471,7 +10601,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
10471
10601
|
};
|
|
10472
10602
|
}
|
|
10473
10603
|
for (const file of hookFiles) {
|
|
10474
|
-
const content = await import_node_fs29.promises.readFile(
|
|
10604
|
+
const content = await import_node_fs29.promises.readFile(import_node_path56.default.join(ctx.scanPath, file), "utf8");
|
|
10475
10605
|
if (content.includes(args.registration_snippet)) {
|
|
10476
10606
|
return {
|
|
10477
10607
|
library: args.library,
|
|
@@ -10512,7 +10642,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
10512
10642
|
if (!match) {
|
|
10513
10643
|
return { undone: false, message: "no apply found for library" };
|
|
10514
10644
|
}
|
|
10515
|
-
const pkgPath =
|
|
10645
|
+
const pkgPath = import_node_path56.default.join(ctx.scanPath, "package.json");
|
|
10516
10646
|
if (await fileExists2(pkgPath)) {
|
|
10517
10647
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
10518
10648
|
if (pkg.dependencies?.[match.instrumentation_package]) {
|
|
@@ -10523,7 +10653,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
10523
10653
|
}
|
|
10524
10654
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
10525
10655
|
for (const file of hookFiles) {
|
|
10526
|
-
const filePath =
|
|
10656
|
+
const filePath = import_node_path56.default.join(ctx.scanPath, file);
|
|
10527
10657
|
const content = await import_node_fs29.promises.readFile(filePath, "utf8");
|
|
10528
10658
|
if (content.includes(match.registration_snippet)) {
|
|
10529
10659
|
const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
|
|
@@ -10661,23 +10791,23 @@ function canonicalJson(value) {
|
|
|
10661
10791
|
|
|
10662
10792
|
// src/projects.ts
|
|
10663
10793
|
init_cjs_shims();
|
|
10664
|
-
var
|
|
10794
|
+
var import_node_path57 = __toESM(require("path"), 1);
|
|
10665
10795
|
function pathsForProject(project, baseDir) {
|
|
10666
10796
|
if (project === DEFAULT_PROJECT) {
|
|
10667
10797
|
return {
|
|
10668
|
-
snapshotPath:
|
|
10669
|
-
errorsPath:
|
|
10670
|
-
staleEventsPath:
|
|
10671
|
-
embeddingsCachePath:
|
|
10672
|
-
policyViolationsPath:
|
|
10798
|
+
snapshotPath: import_node_path57.default.join(baseDir, "graph.json"),
|
|
10799
|
+
errorsPath: import_node_path57.default.join(baseDir, "errors.ndjson"),
|
|
10800
|
+
staleEventsPath: import_node_path57.default.join(baseDir, "stale-events.ndjson"),
|
|
10801
|
+
embeddingsCachePath: import_node_path57.default.join(baseDir, "embeddings.json"),
|
|
10802
|
+
policyViolationsPath: import_node_path57.default.join(baseDir, "policy-violations.ndjson")
|
|
10673
10803
|
};
|
|
10674
10804
|
}
|
|
10675
10805
|
return {
|
|
10676
|
-
snapshotPath:
|
|
10677
|
-
errorsPath:
|
|
10678
|
-
staleEventsPath:
|
|
10679
|
-
embeddingsCachePath:
|
|
10680
|
-
policyViolationsPath:
|
|
10806
|
+
snapshotPath: import_node_path57.default.join(baseDir, `${project}.json`),
|
|
10807
|
+
errorsPath: import_node_path57.default.join(baseDir, `errors.${project}.ndjson`),
|
|
10808
|
+
staleEventsPath: import_node_path57.default.join(baseDir, `stale-events.${project}.ndjson`),
|
|
10809
|
+
embeddingsCachePath: import_node_path57.default.join(baseDir, `embeddings.${project}.json`),
|
|
10810
|
+
policyViolationsPath: import_node_path57.default.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
10681
10811
|
};
|
|
10682
10812
|
}
|
|
10683
10813
|
var Projects = class {
|
|
@@ -10715,26 +10845,26 @@ var Projects = class {
|
|
|
10715
10845
|
init_cjs_shims();
|
|
10716
10846
|
var import_node_fs31 = require("fs");
|
|
10717
10847
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
10718
|
-
var
|
|
10719
|
-
var
|
|
10848
|
+
var import_node_path58 = __toESM(require("path"), 1);
|
|
10849
|
+
var import_types43 = require("@neat.is/types");
|
|
10720
10850
|
var LOCK_TIMEOUT_MS = 5e3;
|
|
10721
10851
|
var LOCK_RETRY_MS = 50;
|
|
10722
10852
|
function neatHome() {
|
|
10723
10853
|
const override = process.env.NEAT_HOME;
|
|
10724
|
-
if (override && override.length > 0) return
|
|
10725
|
-
return
|
|
10854
|
+
if (override && override.length > 0) return import_node_path58.default.resolve(override);
|
|
10855
|
+
return import_node_path58.default.join(import_node_os3.default.homedir(), ".neat");
|
|
10726
10856
|
}
|
|
10727
10857
|
function registryPath() {
|
|
10728
|
-
return
|
|
10858
|
+
return import_node_path58.default.join(neatHome(), "projects.json");
|
|
10729
10859
|
}
|
|
10730
10860
|
function registryLockPath() {
|
|
10731
|
-
return
|
|
10861
|
+
return import_node_path58.default.join(neatHome(), "projects.json.lock");
|
|
10732
10862
|
}
|
|
10733
10863
|
function daemonPidPath() {
|
|
10734
|
-
return
|
|
10864
|
+
return import_node_path58.default.join(neatHome(), "neatd.pid");
|
|
10735
10865
|
}
|
|
10736
10866
|
function daemonsDir() {
|
|
10737
|
-
return
|
|
10867
|
+
return import_node_path58.default.join(neatHome(), "daemons");
|
|
10738
10868
|
}
|
|
10739
10869
|
function isFiniteInt(v) {
|
|
10740
10870
|
return typeof v === "number" && Number.isFinite(v);
|
|
@@ -10775,7 +10905,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
|
10775
10905
|
const out = [];
|
|
10776
10906
|
for (const name of names) {
|
|
10777
10907
|
if (!name.endsWith(".json")) continue;
|
|
10778
|
-
const file =
|
|
10908
|
+
const file = import_node_path58.default.join(dir, name);
|
|
10779
10909
|
let raw;
|
|
10780
10910
|
try {
|
|
10781
10911
|
raw = await import_node_fs31.promises.readFile(file, "utf8");
|
|
@@ -10896,7 +11026,7 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
|
|
|
10896
11026
|
}
|
|
10897
11027
|
}
|
|
10898
11028
|
async function normalizeProjectPath(input) {
|
|
10899
|
-
const resolved =
|
|
11029
|
+
const resolved = import_node_path58.default.resolve(input);
|
|
10900
11030
|
try {
|
|
10901
11031
|
return await import_node_fs31.promises.realpath(resolved);
|
|
10902
11032
|
} catch {
|
|
@@ -10904,7 +11034,7 @@ async function normalizeProjectPath(input) {
|
|
|
10904
11034
|
}
|
|
10905
11035
|
}
|
|
10906
11036
|
async function writeAtomically(target, contents) {
|
|
10907
|
-
await import_node_fs31.promises.mkdir(
|
|
11037
|
+
await import_node_fs31.promises.mkdir(import_node_path58.default.dirname(target), { recursive: true });
|
|
10908
11038
|
const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
10909
11039
|
const fd = await import_node_fs31.promises.open(tmp, "w");
|
|
10910
11040
|
try {
|
|
@@ -10917,7 +11047,7 @@ async function writeAtomically(target, contents) {
|
|
|
10917
11047
|
}
|
|
10918
11048
|
async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
|
|
10919
11049
|
const deadline = Date.now() + timeoutMs;
|
|
10920
|
-
await import_node_fs31.promises.mkdir(
|
|
11050
|
+
await import_node_fs31.promises.mkdir(import_node_path58.default.dirname(lockPath), { recursive: true });
|
|
10921
11051
|
let probedHolder = false;
|
|
10922
11052
|
while (true) {
|
|
10923
11053
|
try {
|
|
@@ -10970,10 +11100,10 @@ async function readRegistry() {
|
|
|
10970
11100
|
throw err;
|
|
10971
11101
|
}
|
|
10972
11102
|
const parsed = JSON.parse(raw);
|
|
10973
|
-
return
|
|
11103
|
+
return import_types43.RegistryFileSchema.parse(parsed);
|
|
10974
11104
|
}
|
|
10975
11105
|
async function writeRegistry(reg) {
|
|
10976
|
-
const validated =
|
|
11106
|
+
const validated = import_types43.RegistryFileSchema.parse(reg);
|
|
10977
11107
|
await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
|
|
10978
11108
|
}
|
|
10979
11109
|
var ProjectNameCollisionError = class extends Error {
|
|
@@ -11159,7 +11289,7 @@ init_auth();
|
|
|
11159
11289
|
// src/connectors-config.ts
|
|
11160
11290
|
init_cjs_shims();
|
|
11161
11291
|
var import_node_os4 = __toESM(require("os"), 1);
|
|
11162
|
-
var
|
|
11292
|
+
var import_node_path59 = __toESM(require("path"), 1);
|
|
11163
11293
|
var import_node_fs32 = require("fs");
|
|
11164
11294
|
var CONNECTORS_CONFIG_VERSION = 1;
|
|
11165
11295
|
var EnvRefUnsetError = class extends Error {
|
|
@@ -11174,11 +11304,11 @@ var EnvRefUnsetError = class extends Error {
|
|
|
11174
11304
|
};
|
|
11175
11305
|
function neatHome2() {
|
|
11176
11306
|
const override = process.env.NEAT_HOME;
|
|
11177
|
-
if (override && override.length > 0) return
|
|
11178
|
-
return
|
|
11307
|
+
if (override && override.length > 0) return import_node_path59.default.resolve(override);
|
|
11308
|
+
return import_node_path59.default.join(import_node_os4.default.homedir(), ".neat");
|
|
11179
11309
|
}
|
|
11180
11310
|
function connectorsConfigPath(home = neatHome2()) {
|
|
11181
|
-
return
|
|
11311
|
+
return import_node_path59.default.join(home, "connectors.json");
|
|
11182
11312
|
}
|
|
11183
11313
|
var MODE_MASK_LOOSER_THAN_0600 = 63;
|
|
11184
11314
|
async function warnIfModeLooserThan0600(file) {
|
|
@@ -11309,7 +11439,7 @@ function connectorMatchesProject(entry2, project) {
|
|
|
11309
11439
|
var CONNECTORS_LOCK_TIMEOUT_MS = 5e3;
|
|
11310
11440
|
var CONNECTORS_LOCK_RETRY_MS = 50;
|
|
11311
11441
|
function connectorsConfigLockPath(home = neatHome2()) {
|
|
11312
|
-
return
|
|
11442
|
+
return import_node_path59.default.join(home, "connectors.json.lock");
|
|
11313
11443
|
}
|
|
11314
11444
|
function isEnvRef(value) {
|
|
11315
11445
|
return value.length > 1 && value.startsWith("$");
|
|
@@ -11322,7 +11452,7 @@ function redactCredentialRef(ref) {
|
|
|
11322
11452
|
return out;
|
|
11323
11453
|
}
|
|
11324
11454
|
async function writeConfigAtomically0600(file, contents) {
|
|
11325
|
-
await import_node_fs32.promises.mkdir(
|
|
11455
|
+
await import_node_fs32.promises.mkdir(import_node_path59.default.dirname(file), { recursive: true });
|
|
11326
11456
|
const tmp = `${file}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
11327
11457
|
const fd = await import_node_fs32.promises.open(tmp, "w", 384);
|
|
11328
11458
|
try {
|
|
@@ -11336,7 +11466,7 @@ async function writeConfigAtomically0600(file, contents) {
|
|
|
11336
11466
|
}
|
|
11337
11467
|
async function acquireConnectorsLock(lockPath, timeoutMs = CONNECTORS_LOCK_TIMEOUT_MS) {
|
|
11338
11468
|
const deadline = Date.now() + timeoutMs;
|
|
11339
|
-
await import_node_fs32.promises.mkdir(
|
|
11469
|
+
await import_node_fs32.promises.mkdir(import_node_path59.default.dirname(lockPath), { recursive: true });
|
|
11340
11470
|
for (; ; ) {
|
|
11341
11471
|
try {
|
|
11342
11472
|
const fd = await import_node_fs32.promises.open(lockPath, "wx");
|
|
@@ -11486,15 +11616,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
|
|
|
11486
11616
|
|
|
11487
11617
|
// src/connectors/index.ts
|
|
11488
11618
|
init_cjs_shims();
|
|
11489
|
-
var
|
|
11619
|
+
var import_types44 = require("@neat.is/types");
|
|
11490
11620
|
var NO_ENV = "unknown";
|
|
11491
11621
|
function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
11492
11622
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
11493
11623
|
const sites = [];
|
|
11494
11624
|
for (const edgeId of graph.inboundEdges(targetNodeId)) {
|
|
11495
11625
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
11496
|
-
if (edge.provenance !==
|
|
11497
|
-
const parsed = (0,
|
|
11626
|
+
if (edge.provenance !== import_types44.Provenance.EXTRACTED) continue;
|
|
11627
|
+
const parsed = (0, import_types44.parseFileId)(edge.source);
|
|
11498
11628
|
if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
|
|
11499
11629
|
const site = { relPath: edge.evidence.file };
|
|
11500
11630
|
if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
|
|
@@ -11505,7 +11635,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
|
11505
11635
|
function routeCallSiteFor(graph, targetNodeId) {
|
|
11506
11636
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
11507
11637
|
const attrs = graph.getNodeAttributes(targetNodeId);
|
|
11508
|
-
if (attrs.type !==
|
|
11638
|
+
if (attrs.type !== import_types44.NodeType.RouteNode || !attrs.path) return void 0;
|
|
11509
11639
|
const site = { relPath: attrs.path };
|
|
11510
11640
|
if (attrs.line !== void 0) site.line = attrs.line;
|
|
11511
11641
|
return site;
|
|
@@ -11982,10 +12112,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
|
|
|
11982
12112
|
// src/connectors/supabase/map.ts
|
|
11983
12113
|
var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
|
|
11984
12114
|
var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
|
|
11985
|
-
function targetFromRestPath(
|
|
11986
|
-
const rpcMatch = REST_RPC_PATH_RE.exec(
|
|
12115
|
+
function targetFromRestPath(path72) {
|
|
12116
|
+
const rpcMatch = REST_RPC_PATH_RE.exec(path72);
|
|
11987
12117
|
if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
|
|
11988
|
-
const tableMatch = REST_TABLE_PATH_RE.exec(
|
|
12118
|
+
const tableMatch = REST_TABLE_PATH_RE.exec(path72);
|
|
11989
12119
|
if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
|
|
11990
12120
|
return null;
|
|
11991
12121
|
}
|
|
@@ -12096,23 +12226,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
|
|
|
12096
12226
|
|
|
12097
12227
|
// src/connectors/supabase/resolve.ts
|
|
12098
12228
|
init_cjs_shims();
|
|
12099
|
-
var
|
|
12229
|
+
var import_types46 = require("@neat.is/types");
|
|
12100
12230
|
function createSupabaseResolveTarget(graph, config) {
|
|
12101
12231
|
return (signal, _ctx) => {
|
|
12102
12232
|
if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
|
|
12103
12233
|
return null;
|
|
12104
12234
|
}
|
|
12105
|
-
const subResourceId = (0,
|
|
12235
|
+
const subResourceId = (0, import_types46.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
|
|
12106
12236
|
if (graph.hasNode(subResourceId)) {
|
|
12107
|
-
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType:
|
|
12237
|
+
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types46.EdgeType.CALLS };
|
|
12108
12238
|
}
|
|
12109
|
-
const bareResourceId = (0,
|
|
12239
|
+
const bareResourceId = (0, import_types46.infraId)(signal.targetKind, signal.targetName);
|
|
12110
12240
|
if (graph.hasNode(bareResourceId)) {
|
|
12111
|
-
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType:
|
|
12241
|
+
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types46.EdgeType.CALLS };
|
|
12112
12242
|
}
|
|
12113
|
-
const projectLevelId = (0,
|
|
12243
|
+
const projectLevelId = (0, import_types46.infraId)("supabase", config.nodeRef);
|
|
12114
12244
|
if (graph.hasNode(projectLevelId)) {
|
|
12115
|
-
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType:
|
|
12245
|
+
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types46.EdgeType.CALLS };
|
|
12116
12246
|
}
|
|
12117
12247
|
return null;
|
|
12118
12248
|
};
|
|
@@ -12205,7 +12335,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
|
|
|
12205
12335
|
|
|
12206
12336
|
// src/connectors/railway/index.ts
|
|
12207
12337
|
init_cjs_shims();
|
|
12208
|
-
var
|
|
12338
|
+
var import_types50 = require("@neat.is/types");
|
|
12209
12339
|
|
|
12210
12340
|
// src/connectors/railway/client.ts
|
|
12211
12341
|
init_cjs_shims();
|
|
@@ -12356,7 +12486,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
|
|
|
12356
12486
|
const out = [];
|
|
12357
12487
|
graph.forEachNode((_id, attrs) => {
|
|
12358
12488
|
const node = attrs;
|
|
12359
|
-
if (node.type !==
|
|
12489
|
+
if (node.type !== import_types50.NodeType.RouteNode) return;
|
|
12360
12490
|
const route = attrs;
|
|
12361
12491
|
if (route.service !== serviceName) return;
|
|
12362
12492
|
out.push({
|
|
@@ -12460,12 +12590,12 @@ function createRailwayResolveTarget(config) {
|
|
|
12460
12590
|
const serviceName = config.serviceNameById[config.serviceId];
|
|
12461
12591
|
if (!serviceName) return null;
|
|
12462
12592
|
if (signal.targetKind === ROUTE_TARGET_KIND) {
|
|
12463
|
-
return { targetNodeId: signal.targetName, serviceName, edgeType:
|
|
12593
|
+
return { targetNodeId: signal.targetName, serviceName, edgeType: import_types50.EdgeType.CALLS };
|
|
12464
12594
|
}
|
|
12465
12595
|
if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
|
|
12466
12596
|
const peerName = config.serviceNameById[signal.targetName];
|
|
12467
12597
|
if (!peerName) return null;
|
|
12468
|
-
return { targetNodeId: (0,
|
|
12598
|
+
return { targetNodeId: (0, import_types50.serviceId)(peerName), serviceName, edgeType: import_types50.EdgeType.CONNECTS_TO };
|
|
12469
12599
|
}
|
|
12470
12600
|
return null;
|
|
12471
12601
|
};
|
|
@@ -12589,9 +12719,9 @@ function parseFirebaseTargetName(targetName) {
|
|
|
12589
12719
|
const secondSep = rest.indexOf(FIELD_SEP);
|
|
12590
12720
|
if (secondSep === -1) return null;
|
|
12591
12721
|
const method = rest.slice(0, secondSep);
|
|
12592
|
-
const
|
|
12593
|
-
if (!resourceName || !method || !
|
|
12594
|
-
return { resourceName, method, path:
|
|
12722
|
+
const path72 = rest.slice(secondSep + 1);
|
|
12723
|
+
if (!resourceName || !method || !path72) return null;
|
|
12724
|
+
return { resourceName, method, path: path72 };
|
|
12595
12725
|
}
|
|
12596
12726
|
function resourceNameFor(type, labels) {
|
|
12597
12727
|
if (!labels) return null;
|
|
@@ -12629,14 +12759,14 @@ function mapLogEntryToSignal(entry2) {
|
|
|
12629
12759
|
if (!req) return null;
|
|
12630
12760
|
if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
|
|
12631
12761
|
const method = req.requestMethod.toUpperCase();
|
|
12632
|
-
const
|
|
12633
|
-
if (
|
|
12762
|
+
const path72 = pathFromRequestUrl(req.requestUrl);
|
|
12763
|
+
if (path72 === null) return null;
|
|
12634
12764
|
const timestamp = entry2.timestamp;
|
|
12635
12765
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
12636
12766
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
|
|
12637
12767
|
return {
|
|
12638
12768
|
targetKind: resourceType,
|
|
12639
|
-
targetName: packFirebaseTargetName({ resourceName, method, path:
|
|
12769
|
+
targetName: packFirebaseTargetName({ resourceName, method, path: path72 }),
|
|
12640
12770
|
callCount: 1,
|
|
12641
12771
|
errorCount: isError ? 1 : 0,
|
|
12642
12772
|
lastObservedIso: timestamp
|
|
@@ -12653,7 +12783,7 @@ function mapLogEntriesToSignals(entries) {
|
|
|
12653
12783
|
|
|
12654
12784
|
// src/connectors/firebase/resolve.ts
|
|
12655
12785
|
init_cjs_shims();
|
|
12656
|
-
var
|
|
12786
|
+
var import_types51 = require("@neat.is/types");
|
|
12657
12787
|
function neatServiceNameFor(resourceType, resourceName, serviceMap) {
|
|
12658
12788
|
switch (resourceType) {
|
|
12659
12789
|
case "cloud_function":
|
|
@@ -12668,7 +12798,7 @@ function routeEntriesFor(graph, serviceName) {
|
|
|
12668
12798
|
const entries = [];
|
|
12669
12799
|
graph.forEachNode((_id, attrs) => {
|
|
12670
12800
|
const node = attrs;
|
|
12671
|
-
if (node.type !==
|
|
12801
|
+
if (node.type !== import_types51.NodeType.RouteNode) return;
|
|
12672
12802
|
const route = attrs;
|
|
12673
12803
|
if (route.service !== serviceName) return;
|
|
12674
12804
|
entries.push({
|
|
@@ -12700,7 +12830,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
|
|
|
12700
12830
|
return {
|
|
12701
12831
|
targetNodeId: match.routeNodeId,
|
|
12702
12832
|
serviceName,
|
|
12703
|
-
edgeType:
|
|
12833
|
+
edgeType: import_types51.EdgeType.CALLS
|
|
12704
12834
|
};
|
|
12705
12835
|
};
|
|
12706
12836
|
}
|
|
@@ -12727,7 +12857,7 @@ init_cjs_shims();
|
|
|
12727
12857
|
|
|
12728
12858
|
// src/connectors/cloudflare/connector.ts
|
|
12729
12859
|
init_cjs_shims();
|
|
12730
|
-
var
|
|
12860
|
+
var import_types53 = require("@neat.is/types");
|
|
12731
12861
|
|
|
12732
12862
|
// src/connectors/cloudflare/client.ts
|
|
12733
12863
|
init_cjs_shims();
|
|
@@ -12843,7 +12973,7 @@ function mapEventToSignal(event) {
|
|
|
12843
12973
|
if (Number.isNaN(observedAt.getTime())) return null;
|
|
12844
12974
|
const statusCode = metadata?.statusCode;
|
|
12845
12975
|
const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
|
|
12846
|
-
const
|
|
12976
|
+
const path72 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
|
|
12847
12977
|
return {
|
|
12848
12978
|
targetKind: CLOUDFLARE_TARGET_KIND,
|
|
12849
12979
|
targetName: scriptName,
|
|
@@ -12851,7 +12981,7 @@ function mapEventToSignal(event) {
|
|
|
12851
12981
|
errorCount: isError ? 1 : 0,
|
|
12852
12982
|
lastObservedIso: observedAt.toISOString(),
|
|
12853
12983
|
method,
|
|
12854
|
-
...
|
|
12984
|
+
...path72 ? { path: path72 } : {},
|
|
12855
12985
|
...typeof statusCode === "number" ? { statusCode } : {},
|
|
12856
12986
|
...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
|
|
12857
12987
|
};
|
|
@@ -12891,19 +13021,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
12891
13021
|
graph.forEachNode((id, attrs) => {
|
|
12892
13022
|
if (found) return;
|
|
12893
13023
|
const a = attrs;
|
|
12894
|
-
if (a.type ===
|
|
13024
|
+
if (a.type === import_types53.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
|
|
12895
13025
|
found = id;
|
|
12896
13026
|
}
|
|
12897
13027
|
});
|
|
12898
13028
|
return found;
|
|
12899
13029
|
}
|
|
12900
|
-
function findMatchingRouteNode(graph, serviceName, method,
|
|
12901
|
-
const normalizedPath = normalizePathTemplate(
|
|
13030
|
+
function findMatchingRouteNode(graph, serviceName, method, path72) {
|
|
13031
|
+
const normalizedPath = normalizePathTemplate(path72);
|
|
12902
13032
|
let found = null;
|
|
12903
13033
|
graph.forEachNode((id, attrs) => {
|
|
12904
13034
|
if (found) return;
|
|
12905
13035
|
const a = attrs;
|
|
12906
|
-
if (a.type !==
|
|
13036
|
+
if (a.type !== import_types53.NodeType.RouteNode || a.service !== serviceName) return;
|
|
12907
13037
|
if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
|
|
12908
13038
|
const routeMethod = (a.method ?? "").toUpperCase();
|
|
12909
13039
|
if (routeMethod !== "ALL" && routeMethod !== method) return;
|
|
@@ -12915,18 +13045,18 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
12915
13045
|
return (signal) => {
|
|
12916
13046
|
if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
|
|
12917
13047
|
const scriptName = signal.targetName;
|
|
12918
|
-
const { method, path:
|
|
13048
|
+
const { method, path: path72 } = signal;
|
|
12919
13049
|
const resolveRouteGrain = (serviceName, wholeFileId) => {
|
|
12920
|
-
if (!method || !
|
|
12921
|
-
return findMatchingRouteNode(graph, serviceName, method,
|
|
13050
|
+
if (!method || !path72) return wholeFileId;
|
|
13051
|
+
return findMatchingRouteNode(graph, serviceName, method, path72) ?? wholeFileId;
|
|
12922
13052
|
};
|
|
12923
13053
|
const mapping = config.workers?.[scriptName];
|
|
12924
13054
|
if (mapping) {
|
|
12925
|
-
const wholeFileId = (0,
|
|
13055
|
+
const wholeFileId = (0, import_types53.fileId)(mapping.service, mapping.entryFile);
|
|
12926
13056
|
return {
|
|
12927
13057
|
targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
|
|
12928
13058
|
serviceName: mapping.service,
|
|
12929
|
-
edgeType:
|
|
13059
|
+
edgeType: import_types53.EdgeType.CALLS
|
|
12930
13060
|
};
|
|
12931
13061
|
}
|
|
12932
13062
|
const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
|
|
@@ -12935,13 +13065,13 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
12935
13065
|
return {
|
|
12936
13066
|
targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
|
|
12937
13067
|
serviceName: fileNode.service,
|
|
12938
|
-
edgeType:
|
|
13068
|
+
edgeType: import_types53.EdgeType.CALLS
|
|
12939
13069
|
};
|
|
12940
13070
|
}
|
|
12941
13071
|
return {
|
|
12942
|
-
targetNodeId: (0,
|
|
13072
|
+
targetNodeId: (0, import_types53.infraId)("cloudflare-worker", scriptName),
|
|
12943
13073
|
serviceName: scriptName,
|
|
12944
|
-
edgeType:
|
|
13074
|
+
edgeType: import_types53.EdgeType.CALLS,
|
|
12945
13075
|
ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
|
|
12946
13076
|
};
|
|
12947
13077
|
};
|
|
@@ -13137,14 +13267,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
|
|
|
13137
13267
|
|
|
13138
13268
|
// src/connectors/neon/resolve.ts
|
|
13139
13269
|
init_cjs_shims();
|
|
13140
|
-
var
|
|
13270
|
+
var import_types57 = require("@neat.is/types");
|
|
13141
13271
|
function createNeonResolveTarget(config) {
|
|
13142
13272
|
return (signal) => {
|
|
13143
13273
|
if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
|
|
13144
13274
|
return {
|
|
13145
|
-
targetNodeId: (0,
|
|
13275
|
+
targetNodeId: (0, import_types57.infraId)("sql-table", signal.targetName),
|
|
13146
13276
|
serviceName: config.serviceName,
|
|
13147
|
-
edgeType:
|
|
13277
|
+
edgeType: import_types57.EdgeType.CALLS,
|
|
13148
13278
|
ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
|
|
13149
13279
|
};
|
|
13150
13280
|
};
|
|
@@ -13787,11 +13917,11 @@ function registerRoutes(scope, ctx) {
|
|
|
13787
13917
|
const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
13788
13918
|
const parsed = [];
|
|
13789
13919
|
for (const c of candidates) {
|
|
13790
|
-
const r =
|
|
13920
|
+
const r = import_types60.DivergenceTypeSchema.safeParse(c);
|
|
13791
13921
|
if (!r.success) {
|
|
13792
13922
|
return reply.code(400).send({
|
|
13793
13923
|
error: `unknown divergence type "${c}"`,
|
|
13794
|
-
allowed:
|
|
13924
|
+
allowed: import_types60.DivergenceTypeSchema.options
|
|
13795
13925
|
});
|
|
13796
13926
|
}
|
|
13797
13927
|
parsed.push(r.data);
|
|
@@ -14100,7 +14230,7 @@ function registerRoutes(scope, ctx) {
|
|
|
14100
14230
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
14101
14231
|
let violations = await log.readAll();
|
|
14102
14232
|
if (req.query.severity) {
|
|
14103
|
-
const sev =
|
|
14233
|
+
const sev = import_types60.PolicySeveritySchema.safeParse(req.query.severity);
|
|
14104
14234
|
if (!sev.success) {
|
|
14105
14235
|
return reply.code(400).send({
|
|
14106
14236
|
error: "invalid severity",
|
|
@@ -14139,7 +14269,7 @@ function registerRoutes(scope, ctx) {
|
|
|
14139
14269
|
scope.post("/policies/check", async (req, reply) => {
|
|
14140
14270
|
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
14141
14271
|
if (!proj) return;
|
|
14142
|
-
const parsed =
|
|
14272
|
+
const parsed = import_types60.PoliciesCheckBodySchema.safeParse(req.body ?? {});
|
|
14143
14273
|
if (!parsed.success) {
|
|
14144
14274
|
return reply.code(400).send({
|
|
14145
14275
|
error: "invalid /policies/check body",
|
|
@@ -14461,7 +14591,7 @@ init_otel();
|
|
|
14461
14591
|
// src/daemon.ts
|
|
14462
14592
|
init_cjs_shims();
|
|
14463
14593
|
var import_node_fs34 = require("fs");
|
|
14464
|
-
var
|
|
14594
|
+
var import_node_path61 = __toESM(require("path"), 1);
|
|
14465
14595
|
var import_node_module = require("module");
|
|
14466
14596
|
init_otel();
|
|
14467
14597
|
init_auth();
|
|
@@ -14469,28 +14599,28 @@ init_auth();
|
|
|
14469
14599
|
// src/unrouted.ts
|
|
14470
14600
|
init_cjs_shims();
|
|
14471
14601
|
var import_node_fs33 = require("fs");
|
|
14472
|
-
var
|
|
14602
|
+
var import_node_path60 = __toESM(require("path"), 1);
|
|
14473
14603
|
|
|
14474
14604
|
// src/daemon.ts
|
|
14475
|
-
var
|
|
14605
|
+
var import_types61 = require("@neat.is/types");
|
|
14476
14606
|
function daemonJsonPath(scanPath) {
|
|
14477
|
-
return
|
|
14607
|
+
return import_node_path61.default.join(scanPath, "neat-out", "daemon.json");
|
|
14478
14608
|
}
|
|
14479
14609
|
function daemonsDiscoveryDir(home) {
|
|
14480
14610
|
const base = home && home.length > 0 ? home : neatHomeFromEnv();
|
|
14481
|
-
return
|
|
14611
|
+
return import_node_path61.default.join(base, "daemons");
|
|
14482
14612
|
}
|
|
14483
14613
|
function daemonDiscoveryPath(project, home) {
|
|
14484
|
-
return
|
|
14614
|
+
return import_node_path61.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
|
|
14485
14615
|
}
|
|
14486
14616
|
function sanitizeDiscoveryName(project) {
|
|
14487
14617
|
return project.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
14488
14618
|
}
|
|
14489
14619
|
function neatHomeFromEnv() {
|
|
14490
14620
|
const env = process.env.NEAT_HOME;
|
|
14491
|
-
if (env && env.length > 0) return
|
|
14621
|
+
if (env && env.length > 0) return import_node_path61.default.resolve(env);
|
|
14492
14622
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
14493
|
-
return
|
|
14623
|
+
return import_node_path61.default.join(home, ".neat");
|
|
14494
14624
|
}
|
|
14495
14625
|
async function readDaemonRecord(scanPath) {
|
|
14496
14626
|
try {
|
|
@@ -14561,7 +14691,7 @@ init_otel_grpc();
|
|
|
14561
14691
|
// src/search.ts
|
|
14562
14692
|
init_cjs_shims();
|
|
14563
14693
|
var import_node_fs35 = require("fs");
|
|
14564
|
-
var
|
|
14694
|
+
var import_node_path62 = __toESM(require("path"), 1);
|
|
14565
14695
|
var import_node_crypto4 = require("crypto");
|
|
14566
14696
|
var DEFAULT_LIMIT = 10;
|
|
14567
14697
|
var NOMIC_DIM = 768;
|
|
@@ -14724,7 +14854,7 @@ async function readCache(cachePath) {
|
|
|
14724
14854
|
}
|
|
14725
14855
|
}
|
|
14726
14856
|
async function writeCache(cachePath, cache) {
|
|
14727
|
-
await import_node_fs35.promises.mkdir(
|
|
14857
|
+
await import_node_fs35.promises.mkdir(import_node_path62.default.dirname(cachePath), { recursive: true });
|
|
14728
14858
|
await import_node_fs35.promises.writeFile(cachePath, JSON.stringify(cache));
|
|
14729
14859
|
}
|
|
14730
14860
|
var VectorIndex = class {
|
|
@@ -14882,8 +15012,8 @@ var ALL_PHASES = [
|
|
|
14882
15012
|
];
|
|
14883
15013
|
function classifyChange(relPath) {
|
|
14884
15014
|
const phases = /* @__PURE__ */ new Set();
|
|
14885
|
-
const base =
|
|
14886
|
-
const segments = relPath.split(
|
|
15015
|
+
const base = import_node_path63.default.basename(relPath).toLowerCase();
|
|
15016
|
+
const segments = relPath.split(import_node_path63.default.sep).map((s) => s.toLowerCase());
|
|
14887
15017
|
if (base === "package.json" || base === "requirements.txt" || base === "pyproject.toml" || base === "setup.py") {
|
|
14888
15018
|
phases.add("services");
|
|
14889
15019
|
phases.add("aliases");
|
|
@@ -15011,9 +15141,9 @@ function countWatchableDirs(scanPath, limit) {
|
|
|
15011
15141
|
for (const e of entries) {
|
|
15012
15142
|
if (count >= limit) return;
|
|
15013
15143
|
if (!e.isDirectory()) continue;
|
|
15014
|
-
if (IGNORED_WATCH_PATHS.some((re) => re.test(
|
|
15144
|
+
if (IGNORED_WATCH_PATHS.some((re) => re.test(import_node_path63.default.join(dir, e.name) + import_node_path63.default.sep))) continue;
|
|
15015
15145
|
count++;
|
|
15016
|
-
if (depth < 2) visit(
|
|
15146
|
+
if (depth < 2) visit(import_node_path63.default.join(dir, e.name), depth + 1);
|
|
15017
15147
|
}
|
|
15018
15148
|
};
|
|
15019
15149
|
visit(scanPath, 0);
|
|
@@ -15031,8 +15161,8 @@ async function startWatch(graph, opts) {
|
|
|
15031
15161
|
const projectName = opts.project ?? DEFAULT_PROJECT;
|
|
15032
15162
|
await loadGraphFromDisk(graph, opts.outPath);
|
|
15033
15163
|
const detachEventBus = attachGraphToEventBus(graph, { project: projectName });
|
|
15034
|
-
const policyFilePath =
|
|
15035
|
-
const policyViolationsPath =
|
|
15164
|
+
const policyFilePath = import_node_path63.default.join(opts.scanPath, "policy.json");
|
|
15165
|
+
const policyViolationsPath = import_node_path63.default.join(import_node_path63.default.dirname(opts.outPath), "policy-violations.ndjson");
|
|
15036
15166
|
let policies = [];
|
|
15037
15167
|
try {
|
|
15038
15168
|
policies = await loadPolicyFile(policyFilePath);
|
|
@@ -15092,7 +15222,7 @@ async function startWatch(graph, opts) {
|
|
|
15092
15222
|
assertBindAuthority(host, auth.authToken);
|
|
15093
15223
|
const port = opts.port ?? 8080;
|
|
15094
15224
|
const otelPort = opts.otelPort ?? 4318;
|
|
15095
|
-
const cachePath = opts.embeddingsCachePath ??
|
|
15225
|
+
const cachePath = opts.embeddingsCachePath ?? import_node_path63.default.join(import_node_path63.default.dirname(opts.outPath), "embeddings.json");
|
|
15096
15226
|
let searchIndex;
|
|
15097
15227
|
try {
|
|
15098
15228
|
searchIndex = await buildSearchIndex(graph, { cachePath });
|
|
@@ -15110,7 +15240,7 @@ async function startWatch(graph, opts) {
|
|
|
15110
15240
|
// Paths are derived from the explicit options the watch caller passes
|
|
15111
15241
|
// — pathsForProject is only used to fill in the embeddings/snapshot
|
|
15112
15242
|
// fields so the registry shape is complete.
|
|
15113
|
-
...pathsForProject(projectName,
|
|
15243
|
+
...pathsForProject(projectName, import_node_path63.default.dirname(opts.outPath)),
|
|
15114
15244
|
snapshotPath: opts.outPath,
|
|
15115
15245
|
errorsPath: opts.errorsPath,
|
|
15116
15246
|
staleEventsPath: opts.staleEventsPath
|
|
@@ -15228,9 +15358,9 @@ async function startWatch(graph, opts) {
|
|
|
15228
15358
|
};
|
|
15229
15359
|
const onPath = (absPath) => {
|
|
15230
15360
|
if (shouldIgnore(absPath)) return;
|
|
15231
|
-
const rel =
|
|
15361
|
+
const rel = import_node_path63.default.relative(opts.scanPath, absPath);
|
|
15232
15362
|
if (!rel || rel.startsWith("..")) return;
|
|
15233
|
-
pendingPaths.add(rel.split(
|
|
15363
|
+
pendingPaths.add(rel.split(import_node_path63.default.sep).join("/"));
|
|
15234
15364
|
const phases = classifyChange(rel);
|
|
15235
15365
|
if (phases.size === 0) {
|
|
15236
15366
|
for (const p of ALL_PHASES) pending.add(p);
|
|
@@ -15288,7 +15418,7 @@ async function startWatch(graph, opts) {
|
|
|
15288
15418
|
// src/deploy/detect.ts
|
|
15289
15419
|
init_cjs_shims();
|
|
15290
15420
|
var import_node_fs37 = require("fs");
|
|
15291
|
-
var
|
|
15421
|
+
var import_node_path64 = __toESM(require("path"), 1);
|
|
15292
15422
|
var import_node_child_process2 = require("child_process");
|
|
15293
15423
|
var import_node_crypto5 = require("crypto");
|
|
15294
15424
|
function generateToken() {
|
|
@@ -15388,7 +15518,7 @@ async function runDeploy(opts = {}) {
|
|
|
15388
15518
|
const token = generateToken();
|
|
15389
15519
|
switch (substrate) {
|
|
15390
15520
|
case "docker-compose": {
|
|
15391
|
-
const artifactPath =
|
|
15521
|
+
const artifactPath = import_node_path64.default.join(cwd, "docker-compose.neat.yml");
|
|
15392
15522
|
const contents = emitDockerCompose(cwd);
|
|
15393
15523
|
await import_node_fs37.promises.writeFile(artifactPath, contents, "utf8");
|
|
15394
15524
|
return {
|
|
@@ -15396,11 +15526,11 @@ async function runDeploy(opts = {}) {
|
|
|
15396
15526
|
artifactPath,
|
|
15397
15527
|
token,
|
|
15398
15528
|
contents,
|
|
15399
|
-
startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${
|
|
15529
|
+
startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${import_node_path64.default.basename(artifactPath)} up -d`
|
|
15400
15530
|
};
|
|
15401
15531
|
}
|
|
15402
15532
|
case "systemd": {
|
|
15403
|
-
const artifactPath =
|
|
15533
|
+
const artifactPath = import_node_path64.default.join(cwd, "neat.service");
|
|
15404
15534
|
const contents = emitSystemdUnit(cwd);
|
|
15405
15535
|
await import_node_fs37.promises.writeFile(artifactPath, contents, "utf8");
|
|
15406
15536
|
return {
|
|
@@ -15436,7 +15566,7 @@ init_cjs_shims();
|
|
|
15436
15566
|
// src/installers/javascript.ts
|
|
15437
15567
|
init_cjs_shims();
|
|
15438
15568
|
var import_node_fs38 = require("fs");
|
|
15439
|
-
var
|
|
15569
|
+
var import_node_path65 = __toESM(require("path"), 1);
|
|
15440
15570
|
var import_semver2 = __toESM(require("semver"), 1);
|
|
15441
15571
|
|
|
15442
15572
|
// src/installers/templates.ts
|
|
@@ -16103,11 +16233,11 @@ var OTEL_ENV = {
|
|
|
16103
16233
|
value: "http://localhost:4318/projects/<project>/v1/traces"
|
|
16104
16234
|
};
|
|
16105
16235
|
function serviceNodeName(pkg, serviceDir) {
|
|
16106
|
-
return pkg.name ??
|
|
16236
|
+
return pkg.name ?? import_node_path65.default.basename(serviceDir);
|
|
16107
16237
|
}
|
|
16108
16238
|
function projectToken(pkg, serviceDir, project) {
|
|
16109
16239
|
if (project && project.length > 0) return project;
|
|
16110
|
-
return pkg.name ??
|
|
16240
|
+
return pkg.name ?? import_node_path65.default.basename(serviceDir);
|
|
16111
16241
|
}
|
|
16112
16242
|
async function readJsonFile(p) {
|
|
16113
16243
|
try {
|
|
@@ -16120,16 +16250,16 @@ async function readJsonFile(p) {
|
|
|
16120
16250
|
async function detectRuntimeKind(pkgRoot, pkg) {
|
|
16121
16251
|
const deps = allDeps(pkg);
|
|
16122
16252
|
if ("react-native" in deps || "expo" in deps) return "react-native";
|
|
16123
|
-
const appJson = await readJsonFile(
|
|
16253
|
+
const appJson = await readJsonFile(import_node_path65.default.join(pkgRoot, "app.json"));
|
|
16124
16254
|
if (appJson && typeof appJson === "object" && "expo" in appJson) {
|
|
16125
16255
|
return "react-native";
|
|
16126
16256
|
}
|
|
16127
|
-
if (await exists3(
|
|
16257
|
+
if (await exists3(import_node_path65.default.join(pkgRoot, "vite.config.js")) || await exists3(import_node_path65.default.join(pkgRoot, "vite.config.ts")) || await exists3(import_node_path65.default.join(pkgRoot, "vite.config.mjs")) || "vite" in deps) {
|
|
16128
16258
|
return "browser-bundle";
|
|
16129
16259
|
}
|
|
16130
|
-
if (await exists3(
|
|
16131
|
-
if (await exists3(
|
|
16132
|
-
if (await exists3(
|
|
16260
|
+
if (await exists3(import_node_path65.default.join(pkgRoot, "wrangler.toml"))) return "cloudflare-workers";
|
|
16261
|
+
if (await exists3(import_node_path65.default.join(pkgRoot, "bun.lockb"))) return "bun";
|
|
16262
|
+
if (await exists3(import_node_path65.default.join(pkgRoot, "deno.json")) || await exists3(import_node_path65.default.join(pkgRoot, "deno.lock"))) {
|
|
16133
16263
|
return "deno";
|
|
16134
16264
|
}
|
|
16135
16265
|
const engines = pkg.engines ?? {};
|
|
@@ -16138,7 +16268,7 @@ async function detectRuntimeKind(pkgRoot, pkg) {
|
|
|
16138
16268
|
}
|
|
16139
16269
|
async function readPackageJson2(serviceDir) {
|
|
16140
16270
|
try {
|
|
16141
|
-
const raw = await import_node_fs38.promises.readFile(
|
|
16271
|
+
const raw = await import_node_fs38.promises.readFile(import_node_path65.default.join(serviceDir, "package.json"), "utf8");
|
|
16142
16272
|
return JSON.parse(raw);
|
|
16143
16273
|
} catch {
|
|
16144
16274
|
return null;
|
|
@@ -16182,7 +16312,7 @@ function needsVersionUpgrade(installed, expected) {
|
|
|
16182
16312
|
var NEXT_CONFIG_CANDIDATES = ["next.config.js", "next.config.ts", "next.config.mjs"];
|
|
16183
16313
|
async function findNextConfig(serviceDir) {
|
|
16184
16314
|
for (const name of NEXT_CONFIG_CANDIDATES) {
|
|
16185
|
-
const candidate =
|
|
16315
|
+
const candidate = import_node_path65.default.join(serviceDir, name);
|
|
16186
16316
|
if (await exists3(candidate)) return candidate;
|
|
16187
16317
|
}
|
|
16188
16318
|
return null;
|
|
@@ -16251,7 +16381,7 @@ function hasRemixDependency(pkg) {
|
|
|
16251
16381
|
}
|
|
16252
16382
|
async function findRemixEntry(serviceDir) {
|
|
16253
16383
|
for (const rel of REMIX_ENTRY_CANDIDATES) {
|
|
16254
|
-
const candidate =
|
|
16384
|
+
const candidate = import_node_path65.default.join(serviceDir, rel);
|
|
16255
16385
|
if (await exists3(candidate)) return candidate;
|
|
16256
16386
|
}
|
|
16257
16387
|
return null;
|
|
@@ -16263,14 +16393,14 @@ function hasSvelteKitDependency(pkg) {
|
|
|
16263
16393
|
}
|
|
16264
16394
|
async function findSvelteKitHooks(serviceDir) {
|
|
16265
16395
|
for (const rel of SVELTEKIT_HOOKS_CANDIDATES) {
|
|
16266
|
-
const candidate =
|
|
16396
|
+
const candidate = import_node_path65.default.join(serviceDir, rel);
|
|
16267
16397
|
if (await exists3(candidate)) return candidate;
|
|
16268
16398
|
}
|
|
16269
16399
|
return null;
|
|
16270
16400
|
}
|
|
16271
16401
|
async function findSvelteKitConfig(serviceDir) {
|
|
16272
16402
|
for (const rel of SVELTEKIT_CONFIG_CANDIDATES) {
|
|
16273
|
-
const candidate =
|
|
16403
|
+
const candidate = import_node_path65.default.join(serviceDir, rel);
|
|
16274
16404
|
if (await exists3(candidate)) return candidate;
|
|
16275
16405
|
}
|
|
16276
16406
|
return null;
|
|
@@ -16281,7 +16411,7 @@ function hasNuxtDependency(pkg) {
|
|
|
16281
16411
|
}
|
|
16282
16412
|
async function findNuxtConfig(serviceDir) {
|
|
16283
16413
|
for (const name of NUXT_CONFIG_CANDIDATES) {
|
|
16284
|
-
const candidate =
|
|
16414
|
+
const candidate = import_node_path65.default.join(serviceDir, name);
|
|
16285
16415
|
if (await exists3(candidate)) return candidate;
|
|
16286
16416
|
}
|
|
16287
16417
|
return null;
|
|
@@ -16292,7 +16422,7 @@ function hasAstroDependency(pkg) {
|
|
|
16292
16422
|
}
|
|
16293
16423
|
async function findAstroConfig(serviceDir) {
|
|
16294
16424
|
for (const name of ASTRO_CONFIG_CANDIDATES) {
|
|
16295
|
-
const candidate =
|
|
16425
|
+
const candidate = import_node_path65.default.join(serviceDir, name);
|
|
16296
16426
|
if (await exists3(candidate)) return candidate;
|
|
16297
16427
|
}
|
|
16298
16428
|
return null;
|
|
@@ -16306,7 +16436,7 @@ function parseNextMajor(range) {
|
|
|
16306
16436
|
return Number.isFinite(n) ? n : null;
|
|
16307
16437
|
}
|
|
16308
16438
|
async function isTypeScriptProject(serviceDir) {
|
|
16309
|
-
return exists3(
|
|
16439
|
+
return exists3(import_node_path65.default.join(serviceDir, "tsconfig.json"));
|
|
16310
16440
|
}
|
|
16311
16441
|
var INDEX_EXTENSIONS = [".ts", ".tsx", ".js", ".mjs", ".cjs"];
|
|
16312
16442
|
var INDEX_CANDIDATES = INDEX_EXTENSIONS.map((ext) => `index${ext}`);
|
|
@@ -16355,7 +16485,7 @@ function entryFromScript(script) {
|
|
|
16355
16485
|
}
|
|
16356
16486
|
async function resolveEntry(serviceDir, pkg) {
|
|
16357
16487
|
if (typeof pkg.main === "string" && pkg.main.length > 0) {
|
|
16358
|
-
const candidate =
|
|
16488
|
+
const candidate = import_node_path65.default.resolve(serviceDir, pkg.main);
|
|
16359
16489
|
if (await exists3(candidate)) return candidate;
|
|
16360
16490
|
}
|
|
16361
16491
|
if (pkg.bin) {
|
|
@@ -16369,40 +16499,40 @@ async function resolveEntry(serviceDir, pkg) {
|
|
|
16369
16499
|
if (typeof first === "string") binEntry = first;
|
|
16370
16500
|
}
|
|
16371
16501
|
if (binEntry) {
|
|
16372
|
-
const candidate =
|
|
16502
|
+
const candidate = import_node_path65.default.resolve(serviceDir, binEntry);
|
|
16373
16503
|
if (await exists3(candidate)) return candidate;
|
|
16374
16504
|
}
|
|
16375
16505
|
}
|
|
16376
16506
|
const startEntry = entryFromScript(pkg.scripts?.start);
|
|
16377
16507
|
if (startEntry) {
|
|
16378
|
-
const candidate =
|
|
16508
|
+
const candidate = import_node_path65.default.resolve(serviceDir, startEntry);
|
|
16379
16509
|
if (await exists3(candidate)) return candidate;
|
|
16380
16510
|
}
|
|
16381
16511
|
const devEntry = entryFromScript(pkg.scripts?.dev);
|
|
16382
16512
|
if (devEntry) {
|
|
16383
|
-
const candidate =
|
|
16513
|
+
const candidate = import_node_path65.default.resolve(serviceDir, devEntry);
|
|
16384
16514
|
if (await exists3(candidate)) return candidate;
|
|
16385
16515
|
}
|
|
16386
16516
|
for (const rel of SRC_INDEX_CANDIDATES) {
|
|
16387
|
-
const candidate =
|
|
16517
|
+
const candidate = import_node_path65.default.join(serviceDir, rel);
|
|
16388
16518
|
if (await exists3(candidate)) return candidate;
|
|
16389
16519
|
}
|
|
16390
16520
|
for (const rel of SRC_NAMED_CANDIDATES) {
|
|
16391
|
-
const candidate =
|
|
16521
|
+
const candidate = import_node_path65.default.join(serviceDir, rel);
|
|
16392
16522
|
if (await exists3(candidate)) return candidate;
|
|
16393
16523
|
}
|
|
16394
16524
|
for (const rel of ROOT_NAMED_CANDIDATES) {
|
|
16395
|
-
const candidate =
|
|
16525
|
+
const candidate = import_node_path65.default.join(serviceDir, rel);
|
|
16396
16526
|
if (await exists3(candidate)) return candidate;
|
|
16397
16527
|
}
|
|
16398
16528
|
for (const name of INDEX_CANDIDATES) {
|
|
16399
|
-
const candidate =
|
|
16529
|
+
const candidate = import_node_path65.default.join(serviceDir, name);
|
|
16400
16530
|
if (await exists3(candidate)) return candidate;
|
|
16401
16531
|
}
|
|
16402
16532
|
return null;
|
|
16403
16533
|
}
|
|
16404
16534
|
function dispatchEntry(entryFile, pkg) {
|
|
16405
|
-
const ext =
|
|
16535
|
+
const ext = import_node_path65.default.extname(entryFile).toLowerCase();
|
|
16406
16536
|
if (ext === ".ts" || ext === ".tsx") return pkg.type === "module" ? "ts" : "ts-cjs";
|
|
16407
16537
|
if (ext === ".mjs") return "esm";
|
|
16408
16538
|
if (ext === ".cjs") return "cjs";
|
|
@@ -16420,9 +16550,9 @@ function otelInitContents(flavor) {
|
|
|
16420
16550
|
return OTEL_INIT_CJS;
|
|
16421
16551
|
}
|
|
16422
16552
|
function injectionLine(flavor, entryFile, otelInitFile) {
|
|
16423
|
-
let rel =
|
|
16553
|
+
let rel = import_node_path65.default.relative(import_node_path65.default.dirname(entryFile), otelInitFile);
|
|
16424
16554
|
if (!rel.startsWith(".")) rel = `./${rel}`;
|
|
16425
|
-
rel = rel.split(
|
|
16555
|
+
rel = rel.split(import_node_path65.default.sep).join("/");
|
|
16426
16556
|
if (flavor === "cjs") return `require('${rel}')`;
|
|
16427
16557
|
if (flavor === "esm") return `import '${rel}'`;
|
|
16428
16558
|
const tsRel = rel.replace(/\.ts$/, "");
|
|
@@ -16435,27 +16565,27 @@ function lineIsOtelInjection(line) {
|
|
|
16435
16565
|
}
|
|
16436
16566
|
async function detectsSrcLayout(serviceDir) {
|
|
16437
16567
|
const [hasSrcApp, hasSrcPages, hasRootApp, hasRootPages] = await Promise.all([
|
|
16438
|
-
exists3(
|
|
16439
|
-
exists3(
|
|
16440
|
-
exists3(
|
|
16441
|
-
exists3(
|
|
16568
|
+
exists3(import_node_path65.default.join(serviceDir, "src", "app")),
|
|
16569
|
+
exists3(import_node_path65.default.join(serviceDir, "src", "pages")),
|
|
16570
|
+
exists3(import_node_path65.default.join(serviceDir, "app")),
|
|
16571
|
+
exists3(import_node_path65.default.join(serviceDir, "pages"))
|
|
16442
16572
|
]);
|
|
16443
16573
|
return (hasSrcApp || hasSrcPages) && !hasRootApp && !hasRootPages;
|
|
16444
16574
|
}
|
|
16445
16575
|
async function planNext(serviceDir, pkg, manifestPath, nextConfigPath, project) {
|
|
16446
16576
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
16447
16577
|
const srcLayout = await detectsSrcLayout(serviceDir);
|
|
16448
|
-
const baseDir = srcLayout ?
|
|
16449
|
-
const instrumentationFile =
|
|
16450
|
-
const instrumentationNodeFile =
|
|
16578
|
+
const baseDir = srcLayout ? import_node_path65.default.join(serviceDir, "src") : serviceDir;
|
|
16579
|
+
const instrumentationFile = import_node_path65.default.join(baseDir, useTs ? "instrumentation.ts" : "instrumentation.js");
|
|
16580
|
+
const instrumentationNodeFile = import_node_path65.default.join(
|
|
16451
16581
|
baseDir,
|
|
16452
16582
|
useTs ? "instrumentation.node.ts" : "instrumentation.node.js"
|
|
16453
16583
|
);
|
|
16454
|
-
const instrumentationEdgeFile =
|
|
16584
|
+
const instrumentationEdgeFile = import_node_path65.default.join(
|
|
16455
16585
|
baseDir,
|
|
16456
16586
|
useTs ? "instrumentation.edge.ts" : "instrumentation.edge.js"
|
|
16457
16587
|
);
|
|
16458
|
-
const envNeatFile =
|
|
16588
|
+
const envNeatFile = import_node_path65.default.join(baseDir, ".env.neat");
|
|
16459
16589
|
const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
16460
16590
|
const dependencyEdits = [];
|
|
16461
16591
|
for (const sdk of SDK_PACKAGES) {
|
|
@@ -16580,7 +16710,7 @@ function buildDependencyEdits(pkg, manifestPath) {
|
|
|
16580
16710
|
return edits;
|
|
16581
16711
|
}
|
|
16582
16712
|
async function queueEnvNeat(serviceDir, pkg, project, generatedFiles) {
|
|
16583
|
-
const envNeatFile =
|
|
16713
|
+
const envNeatFile = import_node_path65.default.join(serviceDir, ".env.neat");
|
|
16584
16714
|
if (!await exists3(envNeatFile)) {
|
|
16585
16715
|
generatedFiles.push({
|
|
16586
16716
|
file: envNeatFile,
|
|
@@ -16615,7 +16745,7 @@ function fileImportsOtelHook(raw, specifiers) {
|
|
|
16615
16745
|
}
|
|
16616
16746
|
async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
|
|
16617
16747
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
16618
|
-
const otelServerFile =
|
|
16748
|
+
const otelServerFile = import_node_path65.default.join(
|
|
16619
16749
|
serviceDir,
|
|
16620
16750
|
useTs ? "app/otel.server.ts" : "app/otel.server.js"
|
|
16621
16751
|
);
|
|
@@ -16672,11 +16802,11 @@ async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
|
|
|
16672
16802
|
}
|
|
16673
16803
|
async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project) {
|
|
16674
16804
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
16675
|
-
const otelInitFile =
|
|
16805
|
+
const otelInitFile = import_node_path65.default.join(
|
|
16676
16806
|
serviceDir,
|
|
16677
16807
|
useTs ? "src/otel-init.ts" : "src/otel-init.js"
|
|
16678
16808
|
);
|
|
16679
|
-
const resolvedHooksFile = hooksFile ??
|
|
16809
|
+
const resolvedHooksFile = hooksFile ?? import_node_path65.default.join(serviceDir, useTs ? "src/hooks.server.ts" : "src/hooks.server.js");
|
|
16680
16810
|
const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
|
|
16681
16811
|
const generatedFiles = [];
|
|
16682
16812
|
const entrypointEdits = [];
|
|
@@ -16738,11 +16868,11 @@ async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project)
|
|
|
16738
16868
|
}
|
|
16739
16869
|
async function planNuxt(serviceDir, pkg, manifestPath, project) {
|
|
16740
16870
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
16741
|
-
const otelPluginFile =
|
|
16871
|
+
const otelPluginFile = import_node_path65.default.join(
|
|
16742
16872
|
serviceDir,
|
|
16743
16873
|
useTs ? "server/plugins/otel.ts" : "server/plugins/otel.js"
|
|
16744
16874
|
);
|
|
16745
|
-
const otelInitFile =
|
|
16875
|
+
const otelInitFile = import_node_path65.default.join(
|
|
16746
16876
|
serviceDir,
|
|
16747
16877
|
useTs ? "server/plugins/otel-init.ts" : "server/plugins/otel-init.js"
|
|
16748
16878
|
);
|
|
@@ -16793,19 +16923,19 @@ async function planNuxt(serviceDir, pkg, manifestPath, project) {
|
|
|
16793
16923
|
var ASTRO_MIDDLEWARE_CANDIDATES = ["src/middleware.ts", "src/middleware.js"];
|
|
16794
16924
|
async function findAstroMiddleware(serviceDir) {
|
|
16795
16925
|
for (const rel of ASTRO_MIDDLEWARE_CANDIDATES) {
|
|
16796
|
-
const candidate =
|
|
16926
|
+
const candidate = import_node_path65.default.join(serviceDir, rel);
|
|
16797
16927
|
if (await exists3(candidate)) return candidate;
|
|
16798
16928
|
}
|
|
16799
16929
|
return null;
|
|
16800
16930
|
}
|
|
16801
16931
|
async function planAstro(serviceDir, pkg, manifestPath, project) {
|
|
16802
16932
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
16803
|
-
const otelInitFile =
|
|
16933
|
+
const otelInitFile = import_node_path65.default.join(
|
|
16804
16934
|
serviceDir,
|
|
16805
16935
|
useTs ? "src/otel-init.ts" : "src/otel-init.js"
|
|
16806
16936
|
);
|
|
16807
16937
|
const existingMiddleware = await findAstroMiddleware(serviceDir);
|
|
16808
|
-
const middlewareFile = existingMiddleware ??
|
|
16938
|
+
const middlewareFile = existingMiddleware ?? import_node_path65.default.join(serviceDir, useTs ? "src/middleware.ts" : "src/middleware.js");
|
|
16809
16939
|
const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
|
|
16810
16940
|
const generatedFiles = [];
|
|
16811
16941
|
const entrypointEdits = [];
|
|
@@ -16901,7 +17031,7 @@ async function findFrameworkDispatch(serviceDir, pkg, manifestPath, project) {
|
|
|
16901
17031
|
}
|
|
16902
17032
|
async function plan(serviceDir, opts) {
|
|
16903
17033
|
const pkg = await readPackageJson2(serviceDir);
|
|
16904
|
-
const manifestPath =
|
|
17034
|
+
const manifestPath = import_node_path65.default.join(serviceDir, "package.json");
|
|
16905
17035
|
const project = opts?.project;
|
|
16906
17036
|
const empty = {
|
|
16907
17037
|
language: "javascript",
|
|
@@ -16936,8 +17066,8 @@ async function plan(serviceDir, opts) {
|
|
|
16936
17066
|
return { ...empty, libOnly: true };
|
|
16937
17067
|
}
|
|
16938
17068
|
const flavor = dispatchEntry(entryFile, pkg);
|
|
16939
|
-
const otelInitFile =
|
|
16940
|
-
const envNeatFile =
|
|
17069
|
+
const otelInitFile = import_node_path65.default.join(import_node_path65.default.dirname(entryFile), otelInitFilename(flavor));
|
|
17070
|
+
const envNeatFile = import_node_path65.default.join(serviceDir, ".env.neat");
|
|
16941
17071
|
const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
16942
17072
|
const dependencyEdits = [];
|
|
16943
17073
|
for (const sdk of SDK_PACKAGES) {
|
|
@@ -17006,13 +17136,13 @@ async function plan(serviceDir, opts) {
|
|
|
17006
17136
|
};
|
|
17007
17137
|
}
|
|
17008
17138
|
function isAllowedWritePath(serviceDir, target) {
|
|
17009
|
-
const rel =
|
|
17139
|
+
const rel = import_node_path65.default.relative(serviceDir, target);
|
|
17010
17140
|
if (rel.startsWith("..")) return false;
|
|
17011
|
-
const base =
|
|
17141
|
+
const base = import_node_path65.default.basename(target);
|
|
17012
17142
|
if (base === "package.json") return true;
|
|
17013
17143
|
if (base === ".env.neat") return true;
|
|
17014
17144
|
if (/^otel-init\.(?:js|cjs|mjs|ts)$/.test(base)) return true;
|
|
17015
|
-
const relPosix = rel.split(
|
|
17145
|
+
const relPosix = rel.split(import_node_path65.default.sep).join("/");
|
|
17016
17146
|
if (/^instrumentation(?:\.(?:node|edge))?\.(?:js|cjs|mjs|ts)$/.test(base)) {
|
|
17017
17147
|
if (relPosix === base) return true;
|
|
17018
17148
|
if (relPosix === `src/${base}`) return true;
|
|
@@ -17029,7 +17159,7 @@ function isAllowedWritePath(serviceDir, target) {
|
|
|
17029
17159
|
return false;
|
|
17030
17160
|
}
|
|
17031
17161
|
async function writeAtomic(file, contents) {
|
|
17032
|
-
await import_node_fs38.promises.mkdir(
|
|
17162
|
+
await import_node_fs38.promises.mkdir(import_node_path65.default.dirname(file), { recursive: true });
|
|
17033
17163
|
const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
|
|
17034
17164
|
await import_node_fs38.promises.writeFile(tmp, contents, "utf8");
|
|
17035
17165
|
await import_node_fs38.promises.rename(tmp, file);
|
|
@@ -17213,7 +17343,7 @@ async function rollback(installPlan, originals, createdFiles) {
|
|
|
17213
17343
|
...removed.map((f) => `removed: ${f}`),
|
|
17214
17344
|
""
|
|
17215
17345
|
];
|
|
17216
|
-
const rollbackPath =
|
|
17346
|
+
const rollbackPath = import_node_path65.default.join(installPlan.serviceDir, "neat-rollback.patch");
|
|
17217
17347
|
await import_node_fs38.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
|
|
17218
17348
|
}
|
|
17219
17349
|
function injectInstrumentationHook(raw) {
|
|
@@ -17244,7 +17374,7 @@ var javascriptInstaller = {
|
|
|
17244
17374
|
// src/installers/python.ts
|
|
17245
17375
|
init_cjs_shims();
|
|
17246
17376
|
var import_node_fs39 = require("fs");
|
|
17247
|
-
var
|
|
17377
|
+
var import_node_path66 = __toESM(require("path"), 1);
|
|
17248
17378
|
var SDK_PACKAGES2 = [
|
|
17249
17379
|
{ name: "opentelemetry-distro", version: ">=0.49b0" },
|
|
17250
17380
|
{ name: "opentelemetry-exporter-otlp", version: ">=1.28.0" }
|
|
@@ -17342,7 +17472,7 @@ async function writeFileAtomic(file, contents) {
|
|
|
17342
17472
|
await import_node_fs39.promises.rename(tmp, file);
|
|
17343
17473
|
}
|
|
17344
17474
|
async function resolvePyEntrypoint(serviceDir) {
|
|
17345
|
-
const procfile =
|
|
17475
|
+
const procfile = import_node_path66.default.join(serviceDir, "Procfile");
|
|
17346
17476
|
if (await exists4(procfile)) {
|
|
17347
17477
|
const raw = await import_node_fs39.promises.readFile(procfile, "utf8");
|
|
17348
17478
|
for (const line of raw.split(/\r?\n/)) {
|
|
@@ -17352,20 +17482,20 @@ async function resolvePyEntrypoint(serviceDir) {
|
|
|
17352
17482
|
const asgi = cmd.match(/\b(?:uvicorn|gunicorn|hypercorn|daphne)\s+([\w.]+):/);
|
|
17353
17483
|
if (asgi) {
|
|
17354
17484
|
const modPath = asgi[1].replace(/\./g, "/");
|
|
17355
|
-
const asFile =
|
|
17485
|
+
const asFile = import_node_path66.default.join(serviceDir, `${modPath}.py`);
|
|
17356
17486
|
if (await exists4(asFile)) return asFile;
|
|
17357
|
-
const asPkg =
|
|
17487
|
+
const asPkg = import_node_path66.default.join(serviceDir, modPath, "__init__.py");
|
|
17358
17488
|
if (await exists4(asPkg)) return asPkg;
|
|
17359
17489
|
}
|
|
17360
17490
|
const runFile = cmd.match(/\b(?:python3?|fastapi\s+(?:run|dev))\s+([\w./-]+\.py)\b/);
|
|
17361
17491
|
if (runFile) {
|
|
17362
|
-
const p =
|
|
17492
|
+
const p = import_node_path66.default.join(serviceDir, runFile[1]);
|
|
17363
17493
|
if (await exists4(p)) return p;
|
|
17364
17494
|
}
|
|
17365
17495
|
}
|
|
17366
17496
|
}
|
|
17367
17497
|
for (const name of ["main.py", "app.py", "asgi.py", "wsgi.py", "manage.py", "server.py"]) {
|
|
17368
|
-
const p =
|
|
17498
|
+
const p = import_node_path66.default.join(serviceDir, name);
|
|
17369
17499
|
if (await exists4(p)) return p;
|
|
17370
17500
|
}
|
|
17371
17501
|
return null;
|
|
@@ -17391,7 +17521,7 @@ async function exists4(p) {
|
|
|
17391
17521
|
async function detect2(serviceDir) {
|
|
17392
17522
|
const markers = ["requirements.txt", "pyproject.toml", "setup.py"];
|
|
17393
17523
|
for (const m of markers) {
|
|
17394
|
-
if (await exists4(
|
|
17524
|
+
if (await exists4(import_node_path66.default.join(serviceDir, m))) return true;
|
|
17395
17525
|
}
|
|
17396
17526
|
return false;
|
|
17397
17527
|
}
|
|
@@ -17401,7 +17531,7 @@ function reqPackageName(line) {
|
|
|
17401
17531
|
return head.replace(/[<>=!~].*$/, "").toLowerCase();
|
|
17402
17532
|
}
|
|
17403
17533
|
async function planRequirementsTxtEdits(serviceDir) {
|
|
17404
|
-
const file =
|
|
17534
|
+
const file = import_node_path66.default.join(serviceDir, "requirements.txt");
|
|
17405
17535
|
if (!await exists4(file)) return null;
|
|
17406
17536
|
const raw = await import_node_fs39.promises.readFile(file, "utf8");
|
|
17407
17537
|
const presentNames = new Set(
|
|
@@ -17411,7 +17541,7 @@ async function planRequirementsTxtEdits(serviceDir) {
|
|
|
17411
17541
|
return { manifest: file, missing: [...missing] };
|
|
17412
17542
|
}
|
|
17413
17543
|
async function planProcfileEdits(serviceDir) {
|
|
17414
|
-
const procfile =
|
|
17544
|
+
const procfile = import_node_path66.default.join(serviceDir, "Procfile");
|
|
17415
17545
|
if (!await exists4(procfile)) return [];
|
|
17416
17546
|
const raw = await import_node_fs39.promises.readFile(procfile, "utf8");
|
|
17417
17547
|
const edits = [];
|
|
@@ -17449,7 +17579,7 @@ async function plan2(serviceDir) {
|
|
|
17449
17579
|
}
|
|
17450
17580
|
const entrypointEdits = await planProcfileEdits(serviceDir);
|
|
17451
17581
|
const entryFile = await resolvePyEntrypoint(serviceDir);
|
|
17452
|
-
const generatedFiles = entryFile ? [{ file:
|
|
17582
|
+
const generatedFiles = entryFile ? [{ file: import_node_path66.default.join(serviceDir, NEAT_OTEL_FILENAME), contents: neatOtelPy() }] : [];
|
|
17453
17583
|
if (dependencyEdits.length === 0 && entrypointEdits.length === 0 && !entryFile) {
|
|
17454
17584
|
return empty;
|
|
17455
17585
|
}
|
|
@@ -17517,7 +17647,7 @@ async function apply2(installPlan) {
|
|
|
17517
17647
|
if (raw === void 0) {
|
|
17518
17648
|
throw new Error(`python installer: cannot read ${file} during apply`);
|
|
17519
17649
|
}
|
|
17520
|
-
const base =
|
|
17650
|
+
const base = import_node_path66.default.basename(file);
|
|
17521
17651
|
if (base === "requirements.txt") {
|
|
17522
17652
|
const edits = installPlan.dependencyEdits.filter((e) => e.file === file);
|
|
17523
17653
|
if (edits.length > 0) {
|
|
@@ -17572,7 +17702,7 @@ async function rollback2(installPlan, originals, createdFiles = []) {
|
|
|
17572
17702
|
...removed.map((f) => `removed: ${f}`),
|
|
17573
17703
|
""
|
|
17574
17704
|
];
|
|
17575
|
-
const rollbackPath =
|
|
17705
|
+
const rollbackPath = import_node_path66.default.join(installPlan.serviceDir, "neat-rollback.patch");
|
|
17576
17706
|
await import_node_fs39.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
|
|
17577
17707
|
}
|
|
17578
17708
|
var pythonInstaller = {
|
|
@@ -17585,7 +17715,7 @@ var pythonInstaller = {
|
|
|
17585
17715
|
// src/installers/go.ts
|
|
17586
17716
|
init_cjs_shims();
|
|
17587
17717
|
var import_node_fs40 = require("fs");
|
|
17588
|
-
var
|
|
17718
|
+
var import_node_path67 = __toESM(require("path"), 1);
|
|
17589
17719
|
var GO_DEPS = [
|
|
17590
17720
|
{ name: "go.opentelemetry.io/otel", version: "v1.38.0" },
|
|
17591
17721
|
{ name: "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp", version: "v1.38.0" },
|
|
@@ -17595,13 +17725,13 @@ async function exists5(file) {
|
|
|
17595
17725
|
return import_node_fs40.promises.stat(file).then(() => true, () => false);
|
|
17596
17726
|
}
|
|
17597
17727
|
async function findMain(serviceDir) {
|
|
17598
|
-
const root =
|
|
17728
|
+
const root = import_node_path67.default.join(serviceDir, "main.go");
|
|
17599
17729
|
if (await exists5(root)) return root;
|
|
17600
|
-
const cmd =
|
|
17730
|
+
const cmd = import_node_path67.default.join(serviceDir, "cmd");
|
|
17601
17731
|
const entries = await import_node_fs40.promises.readdir(cmd, { withFileTypes: true }).catch(() => []);
|
|
17602
17732
|
for (const entry2 of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
17603
17733
|
if (!entry2.isDirectory()) continue;
|
|
17604
|
-
const candidate =
|
|
17734
|
+
const candidate = import_node_path67.default.join(cmd, entry2.name, "main.go");
|
|
17605
17735
|
if (await exists5(candidate)) return candidate;
|
|
17606
17736
|
}
|
|
17607
17737
|
return null;
|
|
@@ -17676,17 +17806,17 @@ func init() {
|
|
|
17676
17806
|
`;
|
|
17677
17807
|
}
|
|
17678
17808
|
async function detect3(serviceDir) {
|
|
17679
|
-
return exists5(
|
|
17809
|
+
return exists5(import_node_path67.default.join(serviceDir, "go.mod"));
|
|
17680
17810
|
}
|
|
17681
17811
|
async function plan3(serviceDir) {
|
|
17682
|
-
const manifest =
|
|
17812
|
+
const manifest = import_node_path67.default.join(serviceDir, "go.mod");
|
|
17683
17813
|
const raw = await import_node_fs40.promises.readFile(manifest, "utf8");
|
|
17684
17814
|
const main2 = await findMain(serviceDir);
|
|
17685
17815
|
const dependencyEdits = GO_DEPS.filter((dep) => !raw.includes(dep.name)).map((dep) => ({ file: manifest, kind: "add", ...dep }));
|
|
17686
17816
|
if (!main2) return { language: "go", serviceDir, dependencyEdits: [], entrypointEdits: [], envEdits: [], libOnly: true };
|
|
17687
17817
|
const source = await import_node_fs40.promises.readFile(main2, "utf8");
|
|
17688
17818
|
const packageName = source.match(/^\s*package\s+(\w+)\s*$/m)?.[1] ?? "main";
|
|
17689
|
-
const generated =
|
|
17819
|
+
const generated = import_node_path67.default.join(import_node_path67.default.dirname(main2), "neat_otel.go");
|
|
17690
17820
|
const generatedFiles = await exists5(generated) ? [] : [{ file: generated, contents: neatOtelGo(packageName) }];
|
|
17691
17821
|
return { language: "go", serviceDir, dependencyEdits, entrypointEdits: [], envEdits: [], generatedFiles, entryFile: main2 };
|
|
17692
17822
|
}
|
|
@@ -17828,7 +17958,7 @@ init_cjs_shims();
|
|
|
17828
17958
|
var import_node_fs41 = require("fs");
|
|
17829
17959
|
var import_node_http = __toESM(require("http"), 1);
|
|
17830
17960
|
var import_node_net = __toESM(require("net"), 1);
|
|
17831
|
-
var
|
|
17961
|
+
var import_node_path68 = __toESM(require("path"), 1);
|
|
17832
17962
|
var import_node_url4 = require("url");
|
|
17833
17963
|
var import_node_child_process3 = require("child_process");
|
|
17834
17964
|
var import_node_readline = __toESM(require("readline"), 1);
|
|
@@ -17838,7 +17968,7 @@ async function extractAndPersist(opts) {
|
|
|
17838
17968
|
const graphKey = opts.projectExplicit ? opts.project : DEFAULT_PROJECT;
|
|
17839
17969
|
resetGraph(graphKey);
|
|
17840
17970
|
const graph = getGraph(graphKey);
|
|
17841
|
-
const projectPaths = pathsForProject(graphKey,
|
|
17971
|
+
const projectPaths = pathsForProject(graphKey, import_node_path68.default.join(opts.scanPath, "neat-out"));
|
|
17842
17972
|
const extraction = await extractFromDirectory(graph, opts.scanPath, {
|
|
17843
17973
|
errorsPath: projectPaths.errorsPath
|
|
17844
17974
|
});
|
|
@@ -17890,7 +18020,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
17890
18020
|
libOnly++;
|
|
17891
18021
|
const appDeps = svc.pkg ? appFrameworkDependencies(svc.pkg) : [];
|
|
17892
18022
|
if (appDeps.length > 0) {
|
|
17893
|
-
const svcName =
|
|
18023
|
+
const svcName = import_node_path68.default.basename(svc.dir);
|
|
17894
18024
|
const list = appDeps.join(", ");
|
|
17895
18025
|
console.warn(
|
|
17896
18026
|
`neat: runtime layer won't engage for ${svcName}: no entry point found.
|
|
@@ -17903,7 +18033,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
17903
18033
|
console.log(`skipping ${svc.dir}: browser bundle; browser-OTel support lands in a future release.`);
|
|
17904
18034
|
} else if (outcome.outcome === "react-native") {
|
|
17905
18035
|
reactNative++;
|
|
17906
|
-
const svcName =
|
|
18036
|
+
const svcName = import_node_path68.default.basename(svc.dir);
|
|
17907
18037
|
console.log(
|
|
17908
18038
|
`neat: ${svc.dir} detected as React Native / Expo
|
|
17909
18039
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -17914,7 +18044,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
17914
18044
|
);
|
|
17915
18045
|
} else if (outcome.outcome === "bun") {
|
|
17916
18046
|
bun++;
|
|
17917
|
-
const svcName =
|
|
18047
|
+
const svcName = import_node_path68.default.basename(svc.dir);
|
|
17918
18048
|
console.log(
|
|
17919
18049
|
`neat: ${svc.dir} detected as Bun
|
|
17920
18050
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -17925,7 +18055,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
17925
18055
|
);
|
|
17926
18056
|
} else if (outcome.outcome === "deno") {
|
|
17927
18057
|
deno++;
|
|
17928
|
-
const svcName =
|
|
18058
|
+
const svcName = import_node_path68.default.basename(svc.dir);
|
|
17929
18059
|
console.log(
|
|
17930
18060
|
`neat: ${svc.dir} detected as Deno
|
|
17931
18061
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -17936,7 +18066,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
17936
18066
|
);
|
|
17937
18067
|
} else if (outcome.outcome === "cloudflare-workers") {
|
|
17938
18068
|
cloudflareWorkers++;
|
|
17939
|
-
const svcName =
|
|
18069
|
+
const svcName = import_node_path68.default.basename(svc.dir);
|
|
17940
18070
|
console.log(
|
|
17941
18071
|
`neat: ${svc.dir} detected as Cloudflare Workers
|
|
17942
18072
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -17947,7 +18077,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
17947
18077
|
);
|
|
17948
18078
|
} else if (outcome.outcome === "electron") {
|
|
17949
18079
|
electron++;
|
|
17950
|
-
const svcName =
|
|
18080
|
+
const svcName = import_node_path68.default.basename(svc.dir);
|
|
17951
18081
|
console.log(
|
|
17952
18082
|
`neat: ${svc.dir} detected as Electron
|
|
17953
18083
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -17960,7 +18090,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
17960
18090
|
if (svc.pkg && (outcome.outcome === "instrumented" || outcome.outcome === "already-instrumented")) {
|
|
17961
18091
|
const gaps = uninstrumentedLibraries(svc.pkg);
|
|
17962
18092
|
if (gaps.length > 0) {
|
|
17963
|
-
const svcName =
|
|
18093
|
+
const svcName = import_node_path68.default.basename(svc.dir);
|
|
17964
18094
|
const list = gaps.join(", ");
|
|
17965
18095
|
const subject = gaps.length === 1 ? "this library" : "these libraries";
|
|
17966
18096
|
const aux = gaps.length === 1 ? "isn't" : "aren't";
|
|
@@ -18166,8 +18296,8 @@ async function persistedPortsFor(scanPath) {
|
|
|
18166
18296
|
return { rest: record.ports.rest, otlp: record.ports.otlp, web: record.ports.web };
|
|
18167
18297
|
}
|
|
18168
18298
|
async function acquireSpawnLock(scanPath) {
|
|
18169
|
-
const lockPath =
|
|
18170
|
-
await import_node_fs41.promises.mkdir(
|
|
18299
|
+
const lockPath = import_node_path68.default.join(scanPath, "neat-out", "daemon.spawn.lock");
|
|
18300
|
+
await import_node_fs41.promises.mkdir(import_node_path68.default.dirname(lockPath), { recursive: true });
|
|
18171
18301
|
const STALE_LOCK_MS = 6e4;
|
|
18172
18302
|
try {
|
|
18173
18303
|
const fd = await import_node_fs41.promises.open(lockPath, "wx");
|
|
@@ -18211,14 +18341,14 @@ async function healthIsForProject(restPort, project) {
|
|
|
18211
18341
|
}
|
|
18212
18342
|
return false;
|
|
18213
18343
|
}
|
|
18214
|
-
function daemonLogPath(
|
|
18215
|
-
return
|
|
18344
|
+
function daemonLogPath(projectPath3) {
|
|
18345
|
+
return import_node_path68.default.join(projectPath3, "neat-out", "daemon.log");
|
|
18216
18346
|
}
|
|
18217
18347
|
function spawnDaemonDetached(spec) {
|
|
18218
|
-
const here =
|
|
18348
|
+
const here = import_node_path68.default.dirname((0, import_node_url4.fileURLToPath)(importMetaUrl));
|
|
18219
18349
|
const candidates = [
|
|
18220
|
-
|
|
18221
|
-
|
|
18350
|
+
import_node_path68.default.join(here, "neatd.cjs"),
|
|
18351
|
+
import_node_path68.default.join(here, "neatd.js")
|
|
18222
18352
|
];
|
|
18223
18353
|
let entry2 = null;
|
|
18224
18354
|
const fsSync = require("fs");
|
|
@@ -18248,7 +18378,7 @@ function spawnDaemonDetached(spec) {
|
|
|
18248
18378
|
let logFd = null;
|
|
18249
18379
|
if (spec) {
|
|
18250
18380
|
const logPath = daemonLogPath(spec.projectPath);
|
|
18251
|
-
fsSync.mkdirSync(
|
|
18381
|
+
fsSync.mkdirSync(import_node_path68.default.dirname(logPath), { recursive: true });
|
|
18252
18382
|
logFd = fsSync.openSync(logPath, "a");
|
|
18253
18383
|
}
|
|
18254
18384
|
const child = (0, import_node_child_process3.spawn)(process.execPath, [entry2, "start"], {
|
|
@@ -18447,7 +18577,7 @@ async function runOrchestrator(opts) {
|
|
|
18447
18577
|
result.steps.browser = openBrowser(dashboardUrl);
|
|
18448
18578
|
}
|
|
18449
18579
|
const daemonRunning = result.steps.daemon === "spawned" || result.steps.daemon === "already-running";
|
|
18450
|
-
const daemonLog = daemonRunning ?
|
|
18580
|
+
const daemonLog = daemonRunning ? import_node_path68.default.relative(opts.scanPath, daemonLogPath(opts.scanPath)) : null;
|
|
18451
18581
|
printSummary(result, graph, dashboardUrl, daemonLog);
|
|
18452
18582
|
return result;
|
|
18453
18583
|
}
|
|
@@ -18906,7 +19036,7 @@ async function runConnectorCommand(rawArgs, deps = {}) {
|
|
|
18906
19036
|
|
|
18907
19037
|
// src/hooks-cli.ts
|
|
18908
19038
|
init_cjs_shims();
|
|
18909
|
-
var
|
|
19039
|
+
var import_node_path69 = __toESM(require("path"), 1);
|
|
18910
19040
|
var import_node_os5 = __toESM(require("os"), 1);
|
|
18911
19041
|
var import_node_fs42 = require("fs");
|
|
18912
19042
|
var import_node_url5 = require("url");
|
|
@@ -18915,14 +19045,14 @@ var GUIDE_FILENAME = "GRAPH_FIRST.md";
|
|
|
18915
19045
|
var GUIDE_INSTALL_NAME = "neat-graph-first.md";
|
|
18916
19046
|
var HOOK_MATCHER = "Grep|Glob|Bash";
|
|
18917
19047
|
function moduleDir() {
|
|
18918
|
-
return typeof __dirname !== "undefined" ? __dirname :
|
|
19048
|
+
return typeof __dirname !== "undefined" ? __dirname : import_node_path69.default.dirname((0, import_node_url5.fileURLToPath)(importMetaUrl));
|
|
18919
19049
|
}
|
|
18920
19050
|
async function readSkillAsset(rel) {
|
|
18921
19051
|
const here = moduleDir();
|
|
18922
19052
|
const candidates = [
|
|
18923
|
-
|
|
18924
|
-
|
|
18925
|
-
|
|
19053
|
+
import_node_path69.default.resolve(here, "../../claude-skill", rel),
|
|
19054
|
+
import_node_path69.default.resolve(here, "../../../claude-skill", rel),
|
|
19055
|
+
import_node_path69.default.resolve(here, "../claude-skill", rel)
|
|
18926
19056
|
];
|
|
18927
19057
|
for (const candidate of candidates) {
|
|
18928
19058
|
try {
|
|
@@ -18936,17 +19066,17 @@ async function readSkillAsset(rel) {
|
|
|
18936
19066
|
}
|
|
18937
19067
|
function neatHome3() {
|
|
18938
19068
|
const override = process.env.NEAT_HOME;
|
|
18939
|
-
if (override && override.length > 0) return
|
|
18940
|
-
return
|
|
19069
|
+
if (override && override.length > 0) return import_node_path69.default.resolve(override);
|
|
19070
|
+
return import_node_path69.default.join(import_node_os5.default.homedir(), ".neat");
|
|
18941
19071
|
}
|
|
18942
19072
|
function claudeSettingsPath() {
|
|
18943
19073
|
const override = process.env.NEAT_CLAUDE_SETTINGS;
|
|
18944
|
-
if (override && override.length > 0) return
|
|
19074
|
+
if (override && override.length > 0) return import_node_path69.default.resolve(override);
|
|
18945
19075
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? import_node_os5.default.homedir();
|
|
18946
|
-
return
|
|
19076
|
+
return import_node_path69.default.join(home, ".claude", "settings.json");
|
|
18947
19077
|
}
|
|
18948
19078
|
function installedHookPath() {
|
|
18949
|
-
return
|
|
19079
|
+
return import_node_path69.default.join(neatHome3(), "hooks", HOOK_FILENAME);
|
|
18950
19080
|
}
|
|
18951
19081
|
function isNeatSearchEntry(entry2) {
|
|
18952
19082
|
return (entry2.hooks ?? []).some(
|
|
@@ -18979,9 +19109,9 @@ async function runHooks(opts) {
|
|
|
18979
19109
|
const hookScript = await readSkillAsset(`hooks/${HOOK_FILENAME}`);
|
|
18980
19110
|
const guide = await readSkillAsset(GUIDE_FILENAME);
|
|
18981
19111
|
const scriptPath = installedHookPath();
|
|
18982
|
-
await import_node_fs42.promises.mkdir(
|
|
19112
|
+
await import_node_fs42.promises.mkdir(import_node_path69.default.dirname(scriptPath), { recursive: true });
|
|
18983
19113
|
await import_node_fs42.promises.writeFile(scriptPath, hookScript, { mode: 493 });
|
|
18984
|
-
const guidePath =
|
|
19114
|
+
const guidePath = import_node_path69.default.join(neatHome3(), GUIDE_INSTALL_NAME);
|
|
18985
19115
|
await import_node_fs42.promises.writeFile(guidePath, guide, "utf8");
|
|
18986
19116
|
const settingsFile = claudeSettingsPath();
|
|
18987
19117
|
let settings = {};
|
|
@@ -19008,7 +19138,7 @@ async function runHooks(opts) {
|
|
|
19008
19138
|
...settings,
|
|
19009
19139
|
hooks: { ...hooks, PreToolUse: preToolUse }
|
|
19010
19140
|
};
|
|
19011
|
-
await import_node_fs42.promises.mkdir(
|
|
19141
|
+
await import_node_fs42.promises.mkdir(import_node_path69.default.dirname(settingsFile), { recursive: true });
|
|
19012
19142
|
await import_node_fs42.promises.writeFile(settingsFile, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
19013
19143
|
console.log(`neat hooks: installed the search-nudge hook`);
|
|
19014
19144
|
console.log(` script: ${scriptPath}`);
|
|
@@ -19078,13 +19208,13 @@ async function runHooksCommand(args) {
|
|
|
19078
19208
|
}
|
|
19079
19209
|
}
|
|
19080
19210
|
|
|
19081
|
-
// src/
|
|
19211
|
+
// src/monitor.ts
|
|
19082
19212
|
init_cjs_shims();
|
|
19083
|
-
var
|
|
19213
|
+
var import_types63 = require("@neat.is/types");
|
|
19084
19214
|
|
|
19085
19215
|
// src/cli-client.ts
|
|
19086
19216
|
init_cjs_shims();
|
|
19087
|
-
var
|
|
19217
|
+
var import_types62 = require("@neat.is/types");
|
|
19088
19218
|
var HttpError = class extends Error {
|
|
19089
19219
|
constructor(status2, message, responseBody = "") {
|
|
19090
19220
|
super(message);
|
|
@@ -19109,10 +19239,10 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
19109
19239
|
const root = baseUrl.replace(/\/$/, "");
|
|
19110
19240
|
const authHeader = bearerToken && bearerToken.length > 0 ? { authorization: `Bearer ${bearerToken}` } : {};
|
|
19111
19241
|
return {
|
|
19112
|
-
async get(
|
|
19242
|
+
async get(path72) {
|
|
19113
19243
|
let res;
|
|
19114
19244
|
try {
|
|
19115
|
-
res = await fetch(`${root}${
|
|
19245
|
+
res = await fetch(`${root}${path72}`, {
|
|
19116
19246
|
headers: { ...authHeader }
|
|
19117
19247
|
});
|
|
19118
19248
|
} catch (err) {
|
|
@@ -19124,16 +19254,16 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
19124
19254
|
const body = await res.text().catch(() => "");
|
|
19125
19255
|
throw new HttpError(
|
|
19126
19256
|
res.status,
|
|
19127
|
-
`${res.status} ${res.statusText} on GET ${
|
|
19257
|
+
`${res.status} ${res.statusText} on GET ${path72}: ${body}`,
|
|
19128
19258
|
body
|
|
19129
19259
|
);
|
|
19130
19260
|
}
|
|
19131
19261
|
return await res.json();
|
|
19132
19262
|
},
|
|
19133
|
-
async post(
|
|
19263
|
+
async post(path72, body) {
|
|
19134
19264
|
let res;
|
|
19135
19265
|
try {
|
|
19136
|
-
res = await fetch(`${root}${
|
|
19266
|
+
res = await fetch(`${root}${path72}`, {
|
|
19137
19267
|
method: "POST",
|
|
19138
19268
|
headers: { "content-type": "application/json", ...authHeader },
|
|
19139
19269
|
body: JSON.stringify(body)
|
|
@@ -19147,7 +19277,7 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
19147
19277
|
const text = await res.text().catch(() => "");
|
|
19148
19278
|
throw new HttpError(
|
|
19149
19279
|
res.status,
|
|
19150
|
-
`${res.status} ${res.statusText} on POST ${
|
|
19280
|
+
`${res.status} ${res.statusText} on POST ${path72}: ${text}`,
|
|
19151
19281
|
text
|
|
19152
19282
|
);
|
|
19153
19283
|
}
|
|
@@ -19161,12 +19291,12 @@ function projectPath(project, suffix) {
|
|
|
19161
19291
|
}
|
|
19162
19292
|
async function runRootCause(client, input) {
|
|
19163
19293
|
const qs = input.errorId ? `?errorId=${encodeURIComponent(input.errorId)}` : "";
|
|
19164
|
-
const
|
|
19294
|
+
const path72 = projectPath(
|
|
19165
19295
|
input.project,
|
|
19166
19296
|
`/graph/root-cause/${encodeURIComponent(input.errorNode)}${qs}`
|
|
19167
19297
|
);
|
|
19168
19298
|
try {
|
|
19169
|
-
const result = await client.get(
|
|
19299
|
+
const result = await client.get(path72);
|
|
19170
19300
|
const arrowPath = result.traversalPath.join(" \u2190 ");
|
|
19171
19301
|
const provenances = result.edgeProvenances.length ? result.edgeProvenances.join(", ") : "(direct, no edges traversed)";
|
|
19172
19302
|
const summary = `Root cause for ${input.errorNode} is ${result.rootCauseNode}. ` + result.rootCauseReason + (result.fixRecommendation ? ` Recommended fix: ${result.fixRecommendation}.` : "");
|
|
@@ -19192,12 +19322,12 @@ async function runRootCause(client, input) {
|
|
|
19192
19322
|
}
|
|
19193
19323
|
async function runBlastRadius(client, input) {
|
|
19194
19324
|
const qs = input.depth !== void 0 ? `?depth=${input.depth}` : "";
|
|
19195
|
-
const
|
|
19325
|
+
const path72 = projectPath(
|
|
19196
19326
|
input.project,
|
|
19197
19327
|
`/graph/blast-radius/${encodeURIComponent(input.nodeId)}${qs}`
|
|
19198
19328
|
);
|
|
19199
19329
|
try {
|
|
19200
|
-
const result = await client.get(
|
|
19330
|
+
const result = await client.get(path72);
|
|
19201
19331
|
if (result.totalAffected === 0) {
|
|
19202
19332
|
return {
|
|
19203
19333
|
summary: `${result.origin} has no dependents. Nothing else would break if it failed.`
|
|
@@ -19226,17 +19356,17 @@ async function runBlastRadius(client, input) {
|
|
|
19226
19356
|
}
|
|
19227
19357
|
}
|
|
19228
19358
|
function formatBlastEntry(n) {
|
|
19229
|
-
const tag = n.edgeProvenance ===
|
|
19359
|
+
const tag = n.edgeProvenance === import_types62.Provenance.STALE ? " [STALE \u2014 last seen too long ago]" : "";
|
|
19230
19360
|
return ` \u2022 ${n.nodeId} (distance ${n.distance}, ${n.edgeProvenance})${tag}`;
|
|
19231
19361
|
}
|
|
19232
19362
|
async function runDependencies(client, input) {
|
|
19233
19363
|
const depth = input.depth ?? 3;
|
|
19234
|
-
const
|
|
19364
|
+
const path72 = projectPath(
|
|
19235
19365
|
input.project,
|
|
19236
19366
|
`/graph/dependencies/${encodeURIComponent(input.nodeId)}?depth=${depth}`
|
|
19237
19367
|
);
|
|
19238
19368
|
try {
|
|
19239
|
-
const result = await client.get(
|
|
19369
|
+
const result = await client.get(path72);
|
|
19240
19370
|
if (result.total === 0) {
|
|
19241
19371
|
return {
|
|
19242
19372
|
summary: depth === 1 ? `${input.nodeId} has no direct dependencies in the graph.` : `${input.nodeId} has no dependencies (BFS to depth ${depth}).`
|
|
@@ -19283,7 +19413,7 @@ async function runObservedDependencies(client, input) {
|
|
|
19283
19413
|
if (result.observed) {
|
|
19284
19414
|
return {
|
|
19285
19415
|
summary: `${input.nodeId} makes no outbound runtime calls, but OTel has observed it receiving traffic on ${result.inboundObservedCount} inbound call path${result.inboundObservedCount === 1 ? "" : "s"} \u2014 it's a pure receiver.`,
|
|
19286
|
-
provenance:
|
|
19416
|
+
provenance: import_types62.Provenance.OBSERVED
|
|
19287
19417
|
};
|
|
19288
19418
|
}
|
|
19289
19419
|
const note = result.hasExtractedOutbound ? " Static (EXTRACTED) dependencies exist but no runtime traffic has been seen \u2014 is OTel running?" : "";
|
|
@@ -19293,7 +19423,7 @@ async function runObservedDependencies(client, input) {
|
|
|
19293
19423
|
return {
|
|
19294
19424
|
summary: `${input.nodeId} has ${result.dependencies.length} runtime dependenc${result.dependencies.length === 1 ? "y" : "ies"} confirmed by OTel.`,
|
|
19295
19425
|
block: blockLines.join("\n"),
|
|
19296
|
-
provenance:
|
|
19426
|
+
provenance: import_types62.Provenance.OBSERVED
|
|
19297
19427
|
};
|
|
19298
19428
|
} catch (err) {
|
|
19299
19429
|
if (err instanceof HttpError && err.status === 404) {
|
|
@@ -19328,9 +19458,9 @@ function formatDuration(ms) {
|
|
|
19328
19458
|
return `${Math.round(h / 24)}d`;
|
|
19329
19459
|
}
|
|
19330
19460
|
async function runIncidents(client, input) {
|
|
19331
|
-
const
|
|
19461
|
+
const path72 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
|
|
19332
19462
|
try {
|
|
19333
|
-
const body = await client.get(
|
|
19463
|
+
const body = await client.get(path72);
|
|
19334
19464
|
const events = body.events;
|
|
19335
19465
|
if (events.length === 0) {
|
|
19336
19466
|
return {
|
|
@@ -19347,7 +19477,7 @@ async function runIncidents(client, input) {
|
|
|
19347
19477
|
return {
|
|
19348
19478
|
summary: `${target} has ${body.total} recorded incident${body.total === 1 ? "" : "s"}; showing the ${ordered.length} most recent.`,
|
|
19349
19479
|
block: blockLines.join("\n"),
|
|
19350
|
-
provenance:
|
|
19480
|
+
provenance: import_types62.Provenance.OBSERVED
|
|
19351
19481
|
};
|
|
19352
19482
|
} catch (err) {
|
|
19353
19483
|
if (err instanceof HttpError && err.status === 404) {
|
|
@@ -19456,7 +19586,7 @@ async function runStaleEdges(client, input) {
|
|
|
19456
19586
|
return {
|
|
19457
19587
|
summary: `${events.length} stale-edge transition${events.length === 1 ? "" : "s"} recorded${input.edgeType ? ` for ${input.edgeType}` : ""}.`,
|
|
19458
19588
|
block: blockLines.join("\n"),
|
|
19459
|
-
provenance:
|
|
19589
|
+
provenance: import_types62.Provenance.STALE
|
|
19460
19590
|
};
|
|
19461
19591
|
}
|
|
19462
19592
|
async function runPolicies(client, input) {
|
|
@@ -19613,7 +19743,311 @@ async function pushSnapshotToRemote(input) {
|
|
|
19613
19743
|
);
|
|
19614
19744
|
}
|
|
19615
19745
|
|
|
19746
|
+
// src/monitor.ts
|
|
19747
|
+
var OBSERVED_DEP_EDGE_TYPES = /* @__PURE__ */ new Set([
|
|
19748
|
+
import_types63.EdgeType.CALLS,
|
|
19749
|
+
import_types63.EdgeType.CONNECTS_TO,
|
|
19750
|
+
import_types63.EdgeType.PUBLISHES_TO,
|
|
19751
|
+
import_types63.EdgeType.CONSUMES_FROM
|
|
19752
|
+
]);
|
|
19753
|
+
function divergenceKey(d) {
|
|
19754
|
+
const column = "column" in d && d.column ? d.column : "";
|
|
19755
|
+
const edgeType = "edgeType" in d && d.edgeType ? d.edgeType : "";
|
|
19756
|
+
let extra = "";
|
|
19757
|
+
switch (d.type) {
|
|
19758
|
+
case "version-mismatch":
|
|
19759
|
+
extra = `${d.extractedVersion}->${d.observedVersion}`;
|
|
19760
|
+
break;
|
|
19761
|
+
case "host-mismatch":
|
|
19762
|
+
extra = `${d.extractedHost}->${d.observedHost}`;
|
|
19763
|
+
break;
|
|
19764
|
+
case "compat-violation":
|
|
19765
|
+
extra = `${d.rule.kind}:${d.rule.package ?? ""}`;
|
|
19766
|
+
break;
|
|
19767
|
+
default:
|
|
19768
|
+
extra = "";
|
|
19769
|
+
}
|
|
19770
|
+
return `div|${d.type}|${d.source}|${d.target}|${edgeType}|${column}|${extra}`;
|
|
19771
|
+
}
|
|
19772
|
+
function tableLabel(d) {
|
|
19773
|
+
return "table" in d && d.table ? d.table : d.source;
|
|
19774
|
+
}
|
|
19775
|
+
function formatDivergenceLine2(d) {
|
|
19776
|
+
switch (d.type) {
|
|
19777
|
+
case "missing-observed":
|
|
19778
|
+
if (d.column) {
|
|
19779
|
+
return `\u26A0 divergence [missing-observed] ${tableLabel(d)}.${d.column} declared, never observed in production`;
|
|
19780
|
+
}
|
|
19781
|
+
return `\u26A0 divergence [missing-observed] ${d.source} \u2192 ${d.target} declared, never observed in production`;
|
|
19782
|
+
case "missing-extracted":
|
|
19783
|
+
if (d.column) {
|
|
19784
|
+
return `\u26A0 divergence [missing-extracted] production writes ${tableLabel(d)}.${d.column} \u2014 not declared in code`;
|
|
19785
|
+
}
|
|
19786
|
+
return `\u26A0 divergence [missing-extracted] production ${d.source} \u2192 ${d.target} \u2014 not declared in code`;
|
|
19787
|
+
case "version-mismatch":
|
|
19788
|
+
return `\u26A0 divergence [version-mismatch] ${d.source} \u2192 ${d.target} declared ${d.extractedVersion}, observed ${d.observedVersion} (${d.compatibility})`;
|
|
19789
|
+
case "host-mismatch":
|
|
19790
|
+
return `\u26A0 divergence [host-mismatch] ${d.source} \u2192 ${d.target} declared host ${d.extractedHost}, observed host ${d.observedHost}`;
|
|
19791
|
+
case "compat-violation":
|
|
19792
|
+
return `\u26A0 divergence [compat-violation] ${d.source} \u2192 ${d.target} \u2014 ${d.rule.kind}${d.rule.package ? ` (${d.rule.package})` : ""}`;
|
|
19793
|
+
}
|
|
19794
|
+
}
|
|
19795
|
+
function formatStaleLine(edgeId) {
|
|
19796
|
+
const parsed = (0, import_types63.parseEdgeId)(edgeId);
|
|
19797
|
+
if (parsed) {
|
|
19798
|
+
return `\u22EF stale ${parsed.source} \u2192 ${parsed.target} (observed edge went quiet)`;
|
|
19799
|
+
}
|
|
19800
|
+
return `\u22EF stale ${edgeId} (observed edge went quiet)`;
|
|
19801
|
+
}
|
|
19802
|
+
function formatObservedEdgeLine(edge) {
|
|
19803
|
+
return `+ observed ${edge.source} \u2192 ${edge.target} (new runtime dependency)`;
|
|
19804
|
+
}
|
|
19805
|
+
function divergenceJson(d) {
|
|
19806
|
+
return JSON.stringify({ kind: "divergence", ...d });
|
|
19807
|
+
}
|
|
19808
|
+
function staleJson(edgeId) {
|
|
19809
|
+
const parsed = (0, import_types63.parseEdgeId)(edgeId);
|
|
19810
|
+
return JSON.stringify({
|
|
19811
|
+
kind: "stale",
|
|
19812
|
+
edgeId,
|
|
19813
|
+
...parsed ? { source: parsed.source, target: parsed.target, edgeType: parsed.type } : {}
|
|
19814
|
+
});
|
|
19815
|
+
}
|
|
19816
|
+
function observedEdgeJson(edge) {
|
|
19817
|
+
return JSON.stringify({
|
|
19818
|
+
kind: "observed",
|
|
19819
|
+
id: edge.id,
|
|
19820
|
+
source: edge.source,
|
|
19821
|
+
target: edge.target,
|
|
19822
|
+
edgeType: edge.type,
|
|
19823
|
+
provenance: edge.provenance
|
|
19824
|
+
});
|
|
19825
|
+
}
|
|
19826
|
+
var MonitorEmitter = class {
|
|
19827
|
+
constructor(opts) {
|
|
19828
|
+
this.opts = opts;
|
|
19829
|
+
}
|
|
19830
|
+
opts;
|
|
19831
|
+
seen = /* @__PURE__ */ new Set();
|
|
19832
|
+
out(line) {
|
|
19833
|
+
this.opts.write(line + "\n");
|
|
19834
|
+
}
|
|
19835
|
+
// Emit every not-yet-seen divergence in a fresh result. Returns the count
|
|
19836
|
+
// newly emitted (0 → nothing printed, the silent path). Idempotent across
|
|
19837
|
+
// re-reads: the seen-set keys off divergenceKey.
|
|
19838
|
+
emitDivergences(result) {
|
|
19839
|
+
let emitted = 0;
|
|
19840
|
+
for (const d of result.divergences) {
|
|
19841
|
+
const key = divergenceKey(d);
|
|
19842
|
+
if (this.seen.has(key)) continue;
|
|
19843
|
+
this.seen.add(key);
|
|
19844
|
+
this.out(this.opts.json ? divergenceJson(d) : formatDivergenceLine2(d));
|
|
19845
|
+
emitted++;
|
|
19846
|
+
}
|
|
19847
|
+
return emitted;
|
|
19848
|
+
}
|
|
19849
|
+
// Emit a stale-transition once, keyed on the edge id.
|
|
19850
|
+
emitStale(edgeId) {
|
|
19851
|
+
const key = `stale|${edgeId}`;
|
|
19852
|
+
if (this.seen.has(key)) return false;
|
|
19853
|
+
this.seen.add(key);
|
|
19854
|
+
this.out(this.opts.json ? staleJson(edgeId) : formatStaleLine(edgeId));
|
|
19855
|
+
return true;
|
|
19856
|
+
}
|
|
19857
|
+
// Emit a new OBSERVED runtime dependency once, keyed on the edge id. Silently
|
|
19858
|
+
// ignores non-OBSERVED edges and non-dependency edge types (structural
|
|
19859
|
+
// ownership), so only real runtime dependencies reach stdout.
|
|
19860
|
+
emitObservedEdge(edge) {
|
|
19861
|
+
if (edge.provenance !== import_types63.Provenance.OBSERVED) return false;
|
|
19862
|
+
if (!OBSERVED_DEP_EDGE_TYPES.has(edge.type)) return false;
|
|
19863
|
+
const key = `edge|${edge.id}`;
|
|
19864
|
+
if (this.seen.has(key)) return false;
|
|
19865
|
+
this.seen.add(key);
|
|
19866
|
+
this.out(this.opts.json ? observedEdgeJson(edge) : formatObservedEdgeLine(edge));
|
|
19867
|
+
return true;
|
|
19868
|
+
}
|
|
19869
|
+
};
|
|
19870
|
+
function parseFrame(raw) {
|
|
19871
|
+
let event = "message";
|
|
19872
|
+
const dataLines = [];
|
|
19873
|
+
for (const line of raw.split("\n")) {
|
|
19874
|
+
if (line.length === 0 || line.startsWith(":")) continue;
|
|
19875
|
+
if (line.startsWith("event:")) {
|
|
19876
|
+
event = line.slice("event:".length).trim();
|
|
19877
|
+
} else if (line.startsWith("data:")) {
|
|
19878
|
+
dataLines.push(line.slice("data:".length).replace(/^ /, ""));
|
|
19879
|
+
}
|
|
19880
|
+
}
|
|
19881
|
+
if (dataLines.length === 0) return null;
|
|
19882
|
+
return { event, data: dataLines.join("\n") };
|
|
19883
|
+
}
|
|
19884
|
+
async function drainSse(body, onFrame) {
|
|
19885
|
+
const reader = body.getReader();
|
|
19886
|
+
const decoder = new TextDecoder();
|
|
19887
|
+
let buf = "";
|
|
19888
|
+
try {
|
|
19889
|
+
for (; ; ) {
|
|
19890
|
+
const { done, value } = await reader.read();
|
|
19891
|
+
if (done) break;
|
|
19892
|
+
buf += decoder.decode(value, { stream: true });
|
|
19893
|
+
buf = buf.replace(/\r\n/g, "\n");
|
|
19894
|
+
let idx;
|
|
19895
|
+
while ((idx = buf.indexOf("\n\n")) !== -1) {
|
|
19896
|
+
const rawFrame = buf.slice(0, idx);
|
|
19897
|
+
buf = buf.slice(idx + 2);
|
|
19898
|
+
const frame = parseFrame(rawFrame);
|
|
19899
|
+
if (frame) onFrame(frame);
|
|
19900
|
+
}
|
|
19901
|
+
}
|
|
19902
|
+
} finally {
|
|
19903
|
+
try {
|
|
19904
|
+
reader.releaseLock();
|
|
19905
|
+
} catch {
|
|
19906
|
+
}
|
|
19907
|
+
}
|
|
19908
|
+
}
|
|
19909
|
+
function safeParse(data) {
|
|
19910
|
+
try {
|
|
19911
|
+
return JSON.parse(data);
|
|
19912
|
+
} catch {
|
|
19913
|
+
return null;
|
|
19914
|
+
}
|
|
19915
|
+
}
|
|
19916
|
+
function projectPath2(project, suffix) {
|
|
19917
|
+
if (!project) return suffix;
|
|
19918
|
+
return `/projects/${encodeURIComponent(project)}${suffix}`;
|
|
19919
|
+
}
|
|
19920
|
+
function backoffDelay(attempt, capMs) {
|
|
19921
|
+
return Math.min(capMs, 500 * 2 ** Math.min(attempt, 6));
|
|
19922
|
+
}
|
|
19923
|
+
async function runMonitor(opts) {
|
|
19924
|
+
const write = opts.write ?? ((line) => process.stdout.write(line));
|
|
19925
|
+
const debounceMs = opts.debounceMs ?? 400;
|
|
19926
|
+
const backoffCapMs = opts.backoffCapMs ?? 1e4;
|
|
19927
|
+
const maxReconnects = opts.maxReconnects ?? Number.POSITIVE_INFINITY;
|
|
19928
|
+
const client = createHttpClient(opts.baseUrl, opts.authToken);
|
|
19929
|
+
const emitter = new MonitorEmitter({ json: opts.json, write });
|
|
19930
|
+
const divergencesPath = projectPath2(opts.project, "/graph/divergences");
|
|
19931
|
+
const eventsUrl = `${opts.baseUrl.replace(/\/$/, "")}${projectPath2(opts.project, "/events")}`;
|
|
19932
|
+
let readTimer = null;
|
|
19933
|
+
let reading = false;
|
|
19934
|
+
let readPending = false;
|
|
19935
|
+
const doRead = async () => {
|
|
19936
|
+
if (reading) {
|
|
19937
|
+
readPending = true;
|
|
19938
|
+
return;
|
|
19939
|
+
}
|
|
19940
|
+
reading = true;
|
|
19941
|
+
try {
|
|
19942
|
+
const result = await client.get(divergencesPath);
|
|
19943
|
+
emitter.emitDivergences(result);
|
|
19944
|
+
} catch {
|
|
19945
|
+
} finally {
|
|
19946
|
+
reading = false;
|
|
19947
|
+
if (readPending) {
|
|
19948
|
+
readPending = false;
|
|
19949
|
+
scheduleRead();
|
|
19950
|
+
}
|
|
19951
|
+
}
|
|
19952
|
+
};
|
|
19953
|
+
const scheduleRead = () => {
|
|
19954
|
+
if (readTimer) clearTimeout(readTimer);
|
|
19955
|
+
readTimer = setTimeout(() => {
|
|
19956
|
+
readTimer = null;
|
|
19957
|
+
void doRead();
|
|
19958
|
+
}, debounceMs);
|
|
19959
|
+
if (typeof readTimer.unref === "function") readTimer.unref();
|
|
19960
|
+
};
|
|
19961
|
+
const onFrame = (frame) => {
|
|
19962
|
+
switch (frame.event) {
|
|
19963
|
+
case "extraction-complete":
|
|
19964
|
+
scheduleRead();
|
|
19965
|
+
break;
|
|
19966
|
+
case "stale-transition": {
|
|
19967
|
+
const payload = safeParse(frame.data);
|
|
19968
|
+
const edgeId = payload && typeof payload.edgeId === "string" ? payload.edgeId : void 0;
|
|
19969
|
+
if (edgeId) emitter.emitStale(edgeId);
|
|
19970
|
+
scheduleRead();
|
|
19971
|
+
break;
|
|
19972
|
+
}
|
|
19973
|
+
case "edge-added": {
|
|
19974
|
+
const payload = safeParse(frame.data);
|
|
19975
|
+
const edge = payload?.edge;
|
|
19976
|
+
if (edge && edge.provenance === import_types63.Provenance.OBSERVED) {
|
|
19977
|
+
emitter.emitObservedEdge(edge);
|
|
19978
|
+
scheduleRead();
|
|
19979
|
+
}
|
|
19980
|
+
break;
|
|
19981
|
+
}
|
|
19982
|
+
default:
|
|
19983
|
+
break;
|
|
19984
|
+
}
|
|
19985
|
+
};
|
|
19986
|
+
const headers = { accept: "text/event-stream" };
|
|
19987
|
+
if (opts.authToken && opts.authToken.length > 0) {
|
|
19988
|
+
headers.authorization = `Bearer ${opts.authToken}`;
|
|
19989
|
+
}
|
|
19990
|
+
let connectedOnce = false;
|
|
19991
|
+
let firstConnect = true;
|
|
19992
|
+
for (let attempt = 0; ; attempt++) {
|
|
19993
|
+
if (opts.signal?.aborted) break;
|
|
19994
|
+
let res;
|
|
19995
|
+
try {
|
|
19996
|
+
res = await fetch(eventsUrl, { headers, signal: opts.signal });
|
|
19997
|
+
} catch (err) {
|
|
19998
|
+
if (err.name === "AbortError") break;
|
|
19999
|
+
if (!connectedOnce || attempt >= maxReconnects) break;
|
|
20000
|
+
await sleep(backoffDelay(attempt, backoffCapMs), opts.signal);
|
|
20001
|
+
continue;
|
|
20002
|
+
}
|
|
20003
|
+
if (!res.ok || !res.body) {
|
|
20004
|
+
await res.body?.cancel().catch(() => {
|
|
20005
|
+
});
|
|
20006
|
+
if (!connectedOnce || attempt >= maxReconnects) break;
|
|
20007
|
+
await sleep(backoffDelay(attempt, backoffCapMs), opts.signal);
|
|
20008
|
+
continue;
|
|
20009
|
+
}
|
|
20010
|
+
connectedOnce = true;
|
|
20011
|
+
attempt = 0;
|
|
20012
|
+
if (firstConnect) {
|
|
20013
|
+
firstConnect = false;
|
|
20014
|
+
await doRead();
|
|
20015
|
+
} else {
|
|
20016
|
+
scheduleRead();
|
|
20017
|
+
}
|
|
20018
|
+
try {
|
|
20019
|
+
await drainSse(res.body, onFrame);
|
|
20020
|
+
} catch {
|
|
20021
|
+
}
|
|
20022
|
+
if (opts.signal?.aborted) break;
|
|
20023
|
+
if (attempt >= maxReconnects) break;
|
|
20024
|
+
await sleep(backoffDelay(attempt, backoffCapMs), opts.signal);
|
|
20025
|
+
}
|
|
20026
|
+
if (readTimer) clearTimeout(readTimer);
|
|
20027
|
+
return 0;
|
|
20028
|
+
}
|
|
20029
|
+
function sleep(ms, signal) {
|
|
20030
|
+
return new Promise((resolve) => {
|
|
20031
|
+
if (signal?.aborted) {
|
|
20032
|
+
resolve();
|
|
20033
|
+
return;
|
|
20034
|
+
}
|
|
20035
|
+
const timer = setTimeout(() => {
|
|
20036
|
+
signal?.removeEventListener("abort", onAbort);
|
|
20037
|
+
resolve();
|
|
20038
|
+
}, ms);
|
|
20039
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
20040
|
+
const onAbort = () => {
|
|
20041
|
+
clearTimeout(timer);
|
|
20042
|
+
resolve();
|
|
20043
|
+
};
|
|
20044
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
20045
|
+
});
|
|
20046
|
+
}
|
|
20047
|
+
|
|
19616
20048
|
// src/cli-verbs.ts
|
|
20049
|
+
init_cjs_shims();
|
|
20050
|
+
var import_node_path70 = __toESM(require("path"), 1);
|
|
19617
20051
|
async function resolveProjectEntry(opts) {
|
|
19618
20052
|
const entries = await listProjects();
|
|
19619
20053
|
if (opts.project) {
|
|
@@ -19623,7 +20057,7 @@ async function resolveProjectEntry(opts) {
|
|
|
19623
20057
|
const cwd = opts.cwd ?? process.cwd();
|
|
19624
20058
|
const resolvedCwd = await normalizeProjectPath(cwd);
|
|
19625
20059
|
for (const entry2 of entries) {
|
|
19626
|
-
if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${
|
|
20060
|
+
if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${import_node_path70.default.sep}`)) {
|
|
19627
20061
|
return entry2;
|
|
19628
20062
|
}
|
|
19629
20063
|
}
|
|
@@ -19776,7 +20210,7 @@ async function runSync(opts) {
|
|
|
19776
20210
|
}
|
|
19777
20211
|
|
|
19778
20212
|
// src/cli.ts
|
|
19779
|
-
var
|
|
20213
|
+
var import_types64 = require("@neat.is/types");
|
|
19780
20214
|
function isNpxInvocation() {
|
|
19781
20215
|
if (process.env.npm_command === "exec") return true;
|
|
19782
20216
|
const execpath = process.env.npm_execpath ?? "";
|
|
@@ -19807,6 +20241,14 @@ function usage2() {
|
|
|
19807
20241
|
console.log(" watch <path> Start neat-core, watch <path>, re-extract on changes.");
|
|
19808
20242
|
console.log(" PORT (default 8080), OTEL_PORT (4318), HOST (0.0.0.0)");
|
|
19809
20243
|
console.log(" control listeners. NEAT_OTLP_GRPC=true also opens 4317.");
|
|
20244
|
+
console.log(" monitor Stream live graph facts to stdout \u2014 one line per new");
|
|
20245
|
+
console.log(" fact \u2014 as the daemon learns them: fresh divergences,");
|
|
20246
|
+
console.log(" integrations that just went stale, and new observed");
|
|
20247
|
+
console.log(" runtime dependencies. Silent when nothing is new; exits");
|
|
20248
|
+
console.log(" clean with no output when no daemon is reachable.");
|
|
20249
|
+
console.log(" Flags:");
|
|
20250
|
+
console.log(" --project <name> watch a registered project by name");
|
|
20251
|
+
console.log(" --json emit one JSON object per line");
|
|
19810
20252
|
console.log(" list Report the daemons running on this machine (alias: ps).");
|
|
19811
20253
|
console.log(" Reads ~/.neat/daemons/ and folds in any registered");
|
|
19812
20254
|
console.log(" project no daemon has self-described yet.");
|
|
@@ -20087,12 +20529,12 @@ async function runInit(opts) {
|
|
|
20087
20529
|
printDiscoveryReport(opts, services);
|
|
20088
20530
|
const sections = opts.noInstall ? [] : await buildPatchSections(services, opts.project);
|
|
20089
20531
|
const patch = renderPatch(sections);
|
|
20090
|
-
const patchPath =
|
|
20532
|
+
const patchPath = import_node_path71.default.join(opts.scanPath, "neat.patch");
|
|
20091
20533
|
if (opts.dryRun) {
|
|
20092
20534
|
await import_node_fs43.promises.writeFile(patchPath, patch, "utf8");
|
|
20093
20535
|
written.push(patchPath);
|
|
20094
20536
|
console.log(`dry-run: patch written to ${patchPath}`);
|
|
20095
|
-
const gitignorePath =
|
|
20537
|
+
const gitignorePath = import_node_path71.default.join(opts.scanPath, ".gitignore");
|
|
20096
20538
|
const gitignoreExists = await import_node_fs43.promises.stat(gitignorePath).then(() => true).catch(() => false);
|
|
20097
20539
|
const verb = gitignoreExists ? "append" : "create";
|
|
20098
20540
|
console.log(`dry-run: would ${verb} ${gitignorePath} (add neat-out/)`);
|
|
@@ -20104,9 +20546,9 @@ async function runInit(opts) {
|
|
|
20104
20546
|
const graph = getGraph(graphKey);
|
|
20105
20547
|
const projectPaths = pathsForProject(
|
|
20106
20548
|
graphKey,
|
|
20107
|
-
|
|
20549
|
+
import_node_path71.default.join(opts.scanPath, "neat-out")
|
|
20108
20550
|
);
|
|
20109
|
-
const errorsPath =
|
|
20551
|
+
const errorsPath = import_node_path71.default.join(import_node_path71.default.dirname(opts.outPath), import_node_path71.default.basename(projectPaths.errorsPath));
|
|
20110
20552
|
const result = await extractFromDirectory(graph, opts.scanPath, { errorsPath });
|
|
20111
20553
|
await saveGraphToDisk(graph, opts.outPath);
|
|
20112
20554
|
written.push(opts.outPath);
|
|
@@ -20225,9 +20667,9 @@ var CLAUDE_SKILL_CONFIG = {
|
|
|
20225
20667
|
};
|
|
20226
20668
|
function claudeConfigPath() {
|
|
20227
20669
|
const override = process.env.NEAT_CLAUDE_CONFIG;
|
|
20228
|
-
if (override && override.length > 0) return
|
|
20670
|
+
if (override && override.length > 0) return import_node_path71.default.resolve(override);
|
|
20229
20671
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
20230
|
-
return
|
|
20672
|
+
return import_node_path71.default.join(home, ".claude.json");
|
|
20231
20673
|
}
|
|
20232
20674
|
async function runSkill(opts) {
|
|
20233
20675
|
const snippet2 = JSON.stringify(CLAUDE_SKILL_CONFIG, null, 2) + "\n";
|
|
@@ -20251,7 +20693,7 @@ async function runSkill(opts) {
|
|
|
20251
20693
|
...existing,
|
|
20252
20694
|
mcpServers: { ...mcp, neat: CLAUDE_SKILL_CONFIG.mcpServers.neat }
|
|
20253
20695
|
};
|
|
20254
|
-
await import_node_fs43.promises.mkdir(
|
|
20696
|
+
await import_node_fs43.promises.mkdir(import_node_path71.default.dirname(target), { recursive: true });
|
|
20255
20697
|
await import_node_fs43.promises.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
20256
20698
|
console.log(`neat skill: wrote mcpServers.neat to ${target}`);
|
|
20257
20699
|
console.log("restart Claude Code to pick up the new MCP server.");
|
|
@@ -20317,12 +20759,12 @@ async function main() {
|
|
|
20317
20759
|
console.error("neat init: --apply and --dry-run are mutually exclusive");
|
|
20318
20760
|
process.exit(2);
|
|
20319
20761
|
}
|
|
20320
|
-
const scanPath =
|
|
20762
|
+
const scanPath = import_node_path71.default.resolve(target);
|
|
20321
20763
|
const projectExplicit = parsed.project !== null;
|
|
20322
|
-
const projectName = projectExplicit ? project :
|
|
20764
|
+
const projectName = projectExplicit ? project : import_node_path71.default.basename(scanPath);
|
|
20323
20765
|
const projectKey = projectExplicit ? project : DEFAULT_PROJECT;
|
|
20324
|
-
const fallback = pathsForProject(projectKey,
|
|
20325
|
-
const outPath =
|
|
20766
|
+
const fallback = pathsForProject(projectKey, import_node_path71.default.join(scanPath, "neat-out")).snapshotPath;
|
|
20767
|
+
const outPath = import_node_path71.default.resolve(process.env.NEAT_OUT_PATH ?? fallback);
|
|
20326
20768
|
const result = await runInit({
|
|
20327
20769
|
scanPath,
|
|
20328
20770
|
outPath,
|
|
@@ -20343,21 +20785,21 @@ async function main() {
|
|
|
20343
20785
|
usage2();
|
|
20344
20786
|
process.exit(2);
|
|
20345
20787
|
}
|
|
20346
|
-
const scanPath =
|
|
20788
|
+
const scanPath = import_node_path71.default.resolve(target);
|
|
20347
20789
|
const stat = await import_node_fs43.promises.stat(scanPath).catch(() => null);
|
|
20348
20790
|
if (!stat || !stat.isDirectory()) {
|
|
20349
20791
|
console.error(`neat watch: ${scanPath} is not a directory`);
|
|
20350
20792
|
process.exit(2);
|
|
20351
20793
|
}
|
|
20352
|
-
const projectPaths = pathsForProject(project,
|
|
20353
|
-
const outPath =
|
|
20354
|
-
const errorsPath =
|
|
20355
|
-
process.env.NEAT_ERRORS_PATH ??
|
|
20794
|
+
const projectPaths = pathsForProject(project, import_node_path71.default.join(scanPath, "neat-out"));
|
|
20795
|
+
const outPath = import_node_path71.default.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
|
|
20796
|
+
const errorsPath = import_node_path71.default.resolve(
|
|
20797
|
+
process.env.NEAT_ERRORS_PATH ?? import_node_path71.default.join(import_node_path71.default.dirname(outPath), import_node_path71.default.basename(projectPaths.errorsPath))
|
|
20356
20798
|
);
|
|
20357
|
-
const staleEventsPath =
|
|
20358
|
-
process.env.NEAT_STALE_EVENTS_PATH ??
|
|
20799
|
+
const staleEventsPath = import_node_path71.default.resolve(
|
|
20800
|
+
process.env.NEAT_STALE_EVENTS_PATH ?? import_node_path71.default.join(import_node_path71.default.dirname(outPath), import_node_path71.default.basename(projectPaths.staleEventsPath))
|
|
20359
20801
|
);
|
|
20360
|
-
const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ?
|
|
20802
|
+
const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? import_node_path71.default.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
|
|
20361
20803
|
const handle = await startWatch(getGraph(project), {
|
|
20362
20804
|
scanPath,
|
|
20363
20805
|
outPath,
|
|
@@ -20366,7 +20808,7 @@ async function main() {
|
|
|
20366
20808
|
project,
|
|
20367
20809
|
// Resolve NEAT_HOME so a `neat watch` picks up connectors added to
|
|
20368
20810
|
// ~/.neat/connectors.json (#871). Same resolution the rest of the CLI uses.
|
|
20369
|
-
neatHome: process.env.NEAT_HOME ?
|
|
20811
|
+
neatHome: process.env.NEAT_HOME ? import_node_path71.default.resolve(process.env.NEAT_HOME) : import_node_path71.default.join(import_node_os6.default.homedir(), ".neat"),
|
|
20370
20812
|
...embeddingsCachePath ? { embeddingsCachePath } : {},
|
|
20371
20813
|
host: process.env.HOST ?? "0.0.0.0",
|
|
20372
20814
|
port: Number(process.env.PORT ?? 8080),
|
|
@@ -20466,14 +20908,14 @@ async function main() {
|
|
|
20466
20908
|
console.error(`neat uninstall: no project named "${name}"`);
|
|
20467
20909
|
process.exit(1);
|
|
20468
20910
|
}
|
|
20469
|
-
const
|
|
20911
|
+
const projectPath3 = daemon?.record.projectPath ?? removed?.path ?? "(unknown path)";
|
|
20470
20912
|
if (daemon) {
|
|
20471
20913
|
if (daemon.live && signalDaemonStop(daemon.record.pid)) {
|
|
20472
20914
|
console.log(`uninstall: ${name} \u2014 stopped daemon pid ${daemon.record.pid}`);
|
|
20473
20915
|
}
|
|
20474
20916
|
await removeDaemonRecord(daemon.source);
|
|
20475
20917
|
}
|
|
20476
|
-
console.log(`unregistered: ${name} (${
|
|
20918
|
+
console.log(`unregistered: ${name} (${projectPath3})`);
|
|
20477
20919
|
console.log("note: neat-out/, policy.json, and other files at the project path were left in place.");
|
|
20478
20920
|
return;
|
|
20479
20921
|
}
|
|
@@ -20528,6 +20970,11 @@ async function main() {
|
|
|
20528
20970
|
if (result.exitCode !== 0) process.exit(result.exitCode);
|
|
20529
20971
|
return;
|
|
20530
20972
|
}
|
|
20973
|
+
if (cmd === "monitor") {
|
|
20974
|
+
const code = await runMonitorVerb(parsed);
|
|
20975
|
+
if (code !== 0) process.exit(code);
|
|
20976
|
+
return;
|
|
20977
|
+
}
|
|
20531
20978
|
if (QUERY_VERBS.has(cmd)) {
|
|
20532
20979
|
const code = await runQueryVerb(cmd, parsed);
|
|
20533
20980
|
if (code !== 0) process.exit(code);
|
|
@@ -20543,11 +20990,11 @@ async function main() {
|
|
|
20543
20990
|
process.exit(1);
|
|
20544
20991
|
}
|
|
20545
20992
|
async function tryOrchestrator(cmd, parsed) {
|
|
20546
|
-
const scanPath =
|
|
20993
|
+
const scanPath = import_node_path71.default.resolve(cmd);
|
|
20547
20994
|
const stat = await import_node_fs43.promises.stat(scanPath).catch(() => null);
|
|
20548
20995
|
if (!stat || !stat.isDirectory()) return null;
|
|
20549
20996
|
const projectExplicit = parsed.project !== null;
|
|
20550
|
-
const projectName = projectExplicit ? parsed.project :
|
|
20997
|
+
const projectName = projectExplicit ? parsed.project : import_node_path71.default.basename(scanPath);
|
|
20551
20998
|
const result = await runOrchestrator({
|
|
20552
20999
|
scanPath,
|
|
20553
21000
|
project: projectName,
|
|
@@ -20736,10 +21183,10 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
20736
21183
|
const parts = parsed.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
20737
21184
|
const out = [];
|
|
20738
21185
|
for (const p of parts) {
|
|
20739
|
-
const r =
|
|
21186
|
+
const r = import_types64.DivergenceTypeSchema.safeParse(p);
|
|
20740
21187
|
if (!r.success) {
|
|
20741
21188
|
console.error(
|
|
20742
|
-
`neat divergences: unknown --type "${p}". allowed: ${
|
|
21189
|
+
`neat divergences: unknown --type "${p}". allowed: ${import_types64.DivergenceTypeSchema.options.join(", ")}`
|
|
20743
21190
|
);
|
|
20744
21191
|
return 2;
|
|
20745
21192
|
}
|
|
@@ -20781,6 +21228,37 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
20781
21228
|
return exitCodeForError(err);
|
|
20782
21229
|
}
|
|
20783
21230
|
}
|
|
21231
|
+
async function runMonitorVerb(parsed) {
|
|
21232
|
+
const requestedProject = resolveProjectFlag(parsed);
|
|
21233
|
+
const baseUrl = await resolveDaemonUrl(requestedProject);
|
|
21234
|
+
const token = resolveAuthToken();
|
|
21235
|
+
const client = createHttpClient(baseUrl, token);
|
|
21236
|
+
let project;
|
|
21237
|
+
try {
|
|
21238
|
+
project = await resolveProjectForVerb(client, parsed);
|
|
21239
|
+
} catch (err) {
|
|
21240
|
+
if (err instanceof ProjectResolutionError) {
|
|
21241
|
+
console.error(`neat monitor: ${err.message}`);
|
|
21242
|
+
}
|
|
21243
|
+
return 0;
|
|
21244
|
+
}
|
|
21245
|
+
const controller = new AbortController();
|
|
21246
|
+
const onSignal = () => controller.abort();
|
|
21247
|
+
process.once("SIGINT", onSignal);
|
|
21248
|
+
process.once("SIGTERM", onSignal);
|
|
21249
|
+
try {
|
|
21250
|
+
return await runMonitor({
|
|
21251
|
+
baseUrl,
|
|
21252
|
+
project,
|
|
21253
|
+
json: parsed.json,
|
|
21254
|
+
authToken: token,
|
|
21255
|
+
signal: controller.signal
|
|
21256
|
+
});
|
|
21257
|
+
} finally {
|
|
21258
|
+
process.off("SIGINT", onSignal);
|
|
21259
|
+
process.off("SIGTERM", onSignal);
|
|
21260
|
+
}
|
|
21261
|
+
}
|
|
20784
21262
|
var entry = process.argv[1] ?? "";
|
|
20785
21263
|
if (/[\\/](?:cli\.(?:cjs|js)|cli|neat|neat\.is)$/.test(entry)) {
|
|
20786
21264
|
main().catch((err) => {
|
|
@@ -20802,6 +21280,7 @@ if (/[\\/](?:cli\.(?:cjs|js)|cli|neat|neat\.is)$/.test(entry)) {
|
|
|
20802
21280
|
resolveDaemonUrl,
|
|
20803
21281
|
resolveProjectForVerb,
|
|
20804
21282
|
runInit,
|
|
21283
|
+
runMonitorVerb,
|
|
20805
21284
|
runQueryVerb,
|
|
20806
21285
|
runSkill,
|
|
20807
21286
|
usage
|