@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/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 path71 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
65
- if (exactUnauthPaths.has(path71) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path71)) {
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 = import_node_path39.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
198
- return import_node_path39.default.resolve(here, "..", "proto");
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, import_node_path39, import_node_crypto2, grpc, protoLoader;
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
- import_node_path39 = __toESM(require("path"), 1);
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 path71 = q === -1 ? v : v.slice(0, q);
419
- if (path71.length > 0) return path71;
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 = import_node_path40.default.dirname((0, import_node_url3.fileURLToPath)(importMetaUrl));
479
- const protoRoot = import_node_path40.default.resolve(here, "..", "proto");
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) => import_node_path40.default.resolve(protoRoot, 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 import_node_path40, import_node_url3, import_fastify, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
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
- import_node_path40 = __toESM(require("path"), 1);
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 import_node_path70 = __toESM(require("path"), 1);
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, path71, edges) {
1325
- if (path71.length > best.path.length) {
1326
- best = { path: [...path71], edges: [...edges] };
1325
+ function step(node, path72, edges) {
1326
+ if (path72.length > best.path.length) {
1327
+ best = { path: [...path72], edges: [...edges] };
1327
1328
  }
1328
- if (path71.length - 1 >= maxDepth) return;
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
- path71.push(srcId);
1334
+ path72.push(srcId);
1334
1335
  edges.push(edge);
1335
- step(srcId, path71, edges);
1336
- path71.pop();
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 path71 = [originServiceId];
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
- path71.push(hop.nextService);
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: path71, edges, culprit: current };
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 path71 = [...chain.path];
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
- path71.push(loc.fileNode);
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: path71,
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: path71,
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 import_types29 = require("@neat.is/types");
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 import_node_path41 = __toESM(require("path"), 1);
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 import_types28 = require("@neat.is/types");
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 (import_node_path41.default.extname(file.path) !== ".go") return [];
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, import_types28.infraId)("sql-table", table),
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(import_node_path41.default.relative(serviceDir, file.path)), line, snippet: snippet(file.content, line) }
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 import_types29.EdgeType.PUBLISHES_TO;
8608
+ return import_types30.EdgeType.PUBLISHES_TO;
8480
8609
  case "CONSUMES_FROM":
8481
- return import_types29.EdgeType.CONSUMES_FROM;
8610
+ return import_types30.EdgeType.CONSUMES_FROM;
8482
8611
  default:
8483
- return import_types29.EdgeType.CALLS;
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: import_types29.NodeType.InfraNode,
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 === import_types29.NodeType.InfraNode) {
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
- import_types29.Provenance.EXTRACTED,
8544
- (0, import_types29.confidenceForExtracted)(ep.confidenceKind)
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, import_types29.confidenceForExtracted)(ep.confidenceKind);
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, import_types29.passesExtractedFloor)(confidence)) {
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: import_types29.Provenance.EXTRACTED,
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 import_node_path42 = __toESM(require("path"), 1);
8607
- var import_types31 = require("@neat.is/types");
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 import_types30 = require("@neat.is/types");
8741
+ var import_types31 = require("@neat.is/types");
8612
8742
  function makeInfraNode(kind, name, provider = "self", extras) {
8613
8743
  return {
8614
- id: (0, import_types30.infraId)(kind, name),
8615
- type: import_types30.NodeType.InfraNode,
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: import_types30.Provenance.EXTRACTED,
8660
- confidence: (0, import_types30.confidenceForExtracted)("structural"),
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 || import_node_path42.default.basename(s.dir) === name) return s.node.id;
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 = import_node_path42.default.join(scanPath, name);
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
- import_node_path42.default.relative(scanPath, composePath),
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 = import_node_path42.default.relative(scanPath, composePath).split(import_node_path42.default.sep).join("/");
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, import_types31.EdgeType.DEPENDS_ON);
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: import_types31.EdgeType.DEPENDS_ON,
8734
- provenance: import_types31.Provenance.EXTRACTED,
8735
- confidence: (0, import_types31.confidenceForExtracted)("structural"),
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 import_node_path43 = __toESM(require("path"), 1);
8877
+ var import_node_path44 = __toESM(require("path"), 1);
8748
8878
  var import_node_fs18 = require("fs");
8749
- var import_types32 = require("@neat.is/types");
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 = import_node_path43.default.join(service.dir, "Dockerfile");
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
- import_node_path43.default.relative(scanPath, dockerfilePath),
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(import_node_path43.default.relative(service.dir, dockerfilePath));
8799
- const evidenceFile = toPosix(import_node_path43.default.relative(scanPath, dockerfilePath));
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, import_types32.EdgeType.RUNS_ON);
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: import_types32.EdgeType.RUNS_ON,
8815
- provenance: import_types32.Provenance.EXTRACTED,
8816
- confidence: (0, import_types32.confidenceForExtracted)("structural"),
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, import_types32.EdgeType.CONNECTS_TO);
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: import_types32.EdgeType.CONNECTS_TO,
8838
- provenance: import_types32.Provenance.EXTRACTED,
8839
- confidence: (0, import_types32.confidenceForExtracted)("structural"),
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 import_node_path44 = __toESM(require("path"), 1);
8853
- var import_types33 = require("@neat.is/types");
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 = import_node_path44.default.join(start, entry2.name);
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(import_node_path44.default.join(start, entry2.name));
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(import_node_path44.default.relative(scanPath, file));
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, import_types33.EdgeType.DEPENDS_ON);
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: import_types33.EdgeType.DEPENDS_ON,
8942
- provenance: import_types33.Provenance.EXTRACTED,
8943
- confidence: (0, import_types33.confidenceForExtracted)("structural"),
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 import_node_path45 = __toESM(require("path"), 1);
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 = import_node_path45.default.join(start, entry2.name);
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(import_node_path45.default.extname(entry2.name))) {
8979
- out.push(import_node_path45.default.join(start, entry2.name));
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 import_node_path46 = __toESM(require("path"), 1);
9143
+ var import_node_path47 = __toESM(require("path"), 1);
9014
9144
  var import_smol_toml2 = require("smol-toml");
9015
- var import_types34 = require("@neat.is/types");
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 = import_node_path46.default.join(dir, filename);
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: import_types34.Provenance.EXTRACTED,
9064
- confidence: (0, import_types34.confidenceForExtracted)("structural"),
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", import_node_path46.default.relative(scanPath, service.dir), err);
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(import_node_path46.default.relative(scanPath, import_node_path46.default.join(service.dir, read.relFile)));
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(import_node_path46.default.normalize(config.main));
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, import_types34.EdgeType.RUNS_ON);
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: import_types34.EdgeType.RUNS_ON,
9132
- provenance: import_types34.Provenance.EXTRACTED,
9133
- confidence: (0, import_types34.confidenceForExtracted)("structural"),
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
- import_types34.EdgeType.CONNECTS_TO,
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
- import_types34.EdgeType.DEPENDS_ON,
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
- import_types34.EdgeType.DEPENDS_ON,
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
- import_types34.EdgeType.DEPENDS_ON,
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, import_types34.EdgeType.CALLS);
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: import_types34.EdgeType.CALLS,
9218
- provenance: import_types34.Provenance.EXTRACTED,
9219
- confidence: (0, import_types34.confidenceForExtracted)("structural"),
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
- import_types34.EdgeType.DEPENDS_ON,
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 import_node_path47 = __toESM(require("path"), 1);
9247
- var import_types35 = require("@neat.is/types");
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 = import_node_path47.default.join(dir, filename);
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 = import_node_path47.default.join(dir, ".vercel", "project.json");
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", import_node_path47.default.relative(scanPath, service.dir), err);
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(import_node_path47.default.relative(scanPath, import_node_path47.default.join(service.dir, relFile)));
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(import_types35.EdgeType.RUNS_ON, "vercel", "vercel");
9311
- for (const cron of config.crons ?? []) add(import_types35.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
9312
- for (const varName of Object.keys(config.env ?? {})) add(import_types35.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
9313
- for (const varName of Object.keys(config.build?.env ?? {})) add(import_types35.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
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(import_types35.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
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 import_node_path48 = __toESM(require("path"), 1);
9454
+ var import_node_path49 = __toESM(require("path"), 1);
9325
9455
  var import_smol_toml3 = require("smol-toml");
9326
- var import_types36 = require("@neat.is/types");
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 = import_node_path48.default.join(dir, filename);
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", import_node_path48.default.relative(scanPath, service.dir), err);
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(import_node_path48.default.relative(scanPath, import_node_path48.default.join(service.dir, relFile)));
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(import_types36.EdgeType.RUNS_ON, "railway", "railway");
9373
- add(import_types36.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
9374
- add(import_types36.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
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 import_node_path49 = __toESM(require("path"), 1);
9512
+ var import_node_path50 = __toESM(require("path"), 1);
9383
9513
  var import_smol_toml4 = require("smol-toml");
9384
- var import_types37 = require("@neat.is/types");
9514
+ var import_types38 = require("@neat.is/types");
9385
9515
  async function readSupabaseConfig(dir) {
9386
- const relFile = import_node_path49.default.join("supabase", "config.toml");
9387
- const abs = import_node_path49.default.join(dir, relFile);
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", import_node_path49.default.relative(scanPath, service.dir), err);
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(import_node_path49.default.relative(scanPath, import_node_path49.default.join(service.dir, relFile)));
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(import_types37.EdgeType.RUNS_ON, "supabase", "supabase");
9433
- for (const fn of Object.keys(config.functions ?? {})) add(import_types37.EdgeType.DEPENDS_ON, "supabase-function", fn);
9434
- if (config.storage) add(import_types37.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
9435
- if (config.auth) add(import_types37.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
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 import_node_path51 = __toESM(require("path"), 1);
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 import_node_path50 = __toESM(require("path"), 1);
9463
- var import_types38 = require("@neat.is/types");
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 !== import_types38.NodeType.FileNode) return;
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 !== import_types38.Provenance.EXTRACTED) return;
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 !== import_types38.Provenance.EXTRACTED) return;
9623
+ if (edge.provenance !== import_types39.Provenance.EXTRACTED) return;
9494
9624
  const evidenceFile = edge.evidence?.file;
9495
9625
  if (!evidenceFile) return;
9496
- if (import_node_path50.default.isAbsolute(evidenceFile)) {
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)(import_node_path50.default.join(base, evidenceFile)));
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 = import_node_path51.default.join(import_node_path51.default.dirname(opts.errorsPath), "rejected.ndjson");
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 import_types39 = require("@neat.is/types");
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 !== import_types39.EdgeType.CONNECTS_TO) return edge.source;
9593
- const parsed = (0, import_types39.parseFileId)(edge.source);
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 !== import_types39.NodeType.DatabaseNode) return edge.source;
9597
- return (0, import_types39.serviceId)(parsed.service);
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, import_types39.parseEdgeId)(id);
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 import_types39.Provenance.EXTRACTED:
9739
+ case import_types40.Provenance.EXTRACTED:
9610
9740
  cur.extracted = e;
9611
9741
  break;
9612
- case import_types39.Provenance.OBSERVED:
9742
+ case import_types40.Provenance.OBSERVED:
9613
9743
  cur.observed = e;
9614
9744
  break;
9615
- case import_types39.Provenance.INFERRED:
9745
+ case import_types40.Provenance.INFERRED:
9616
9746
  cur.inferred = e;
9617
9747
  break;
9618
9748
  default:
9619
- if (e.provenance === import_types39.Provenance.STALE) cur.stale = e;
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 === import_types39.NodeType.FrontierNode;
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 === import_types39.NodeType.WebSocketChannelNode;
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 === import_types39.NodeType.SymbolNode;
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
- import_types39.EdgeType.CALLS,
9659
- import_types39.EdgeType.CONNECTS_TO,
9660
- import_types39.EdgeType.PUBLISHES_TO,
9661
- import_types39.EdgeType.CONSUMES_FROM
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 === import_types39.EdgeType.CONTAINS) return out;
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 === import_types39.EdgeType.CONFIGURED_BY && e.provenance === import_types39.Provenance.EXTRACTED) {
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 !== import_types39.EdgeType.CONNECTS_TO) continue;
9721
- if (edge.provenance !== import_types39.Provenance.OBSERVED) continue;
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 !== import_types39.NodeType.DatabaseNode) continue;
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 !== import_types39.EdgeType.CONNECTS_TO) continue;
9746
- if (edge.provenance !== import_types39.Provenance.OBSERVED) continue;
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 !== import_types39.NodeType.DatabaseNode) continue;
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, import_types39.databaseId)(d.extractedHost));
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 === import_types39.NodeType.ServiceNode) {
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 === import_types39.NodeType.InfraNode && n.kind === "sql-table") {
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 import_types39.DivergenceResultSchema.parse({
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 import_node_path52 = __toESM(require("path"), 1);
9917
- var import_types40 = require("@neat.is/types");
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 !== import_types40.NodeType.InfraNode) continue;
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 = import_types40.Provenance.OBSERVED;
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, import_types40.observedEdgeId)(source, target, type);
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(import_node_path52.default.dirname(filePath), { recursive: true });
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 import_node_path53 = __toESM(require("path"), 1);
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 = import_node_path53.default.join(projectDir, ".gitignore");
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 import_types41 = require("@neat.is/types");
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 === import_types41.NodeType.ServiceNode && Array.isArray(n.incompatibilities) && (n.incompatibilities ?? []).length > 0
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 === import_types41.Provenance.OBSERVED) {
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 === import_types41.NodeType.ServiceNode && !seen.has(n.id)
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 import_node_path62 = __toESM(require("path"), 1);
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 import_types59 = require("@neat.is/types");
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 import_node_path55 = __toESM(require("path"), 1);
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 import_node_path54 = __toESM(require("path"), 1);
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 = import_node_path54.default.resolve(serviceDir);
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 = import_node_path54.default.join(dir, candidate.lockfile);
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 = import_node_path54.default.dirname(dir);
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: import_node_path54.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
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 = import_node_path55.default.join(scanPath, "package.json");
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(import_node_path55.default.join(dir, entry2.name));
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 = import_node_path55.default.relative(scanPath, import_node_path55.default.join(dir, entry2.name));
10316
- found.push(rel.split(import_node_path55.default.sep).join("/"));
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(import_node_path55.default.join(scanPath, file), "utf8");
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 ?? import_node_path55.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
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(import_node_path55.default.dirname(logPath), { recursive: true });
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(import_node_path55.default.join(ctx.scanPath, ".env.neat"));
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(import_node_path55.default.join(ctx.scanPath, file), "utf8");
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 = import_node_path55.default.join(ctx.scanPath, primaryFile);
10562
+ const primaryPath = import_node_path56.default.join(ctx.scanPath, primaryFile);
10433
10563
  const filesTouched = [];
10434
10564
  const depsAdded = [];
10435
- const pkgPath = import_node_path55.default.join(ctx.scanPath, "package.json");
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(import_node_path55.default.join(ctx.scanPath, file), "utf8");
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 = import_node_path55.default.join(ctx.scanPath, "package.json");
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 = import_node_path55.default.join(ctx.scanPath, file);
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 import_node_path56 = __toESM(require("path"), 1);
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: import_node_path56.default.join(baseDir, "graph.json"),
10669
- errorsPath: import_node_path56.default.join(baseDir, "errors.ndjson"),
10670
- staleEventsPath: import_node_path56.default.join(baseDir, "stale-events.ndjson"),
10671
- embeddingsCachePath: import_node_path56.default.join(baseDir, "embeddings.json"),
10672
- policyViolationsPath: import_node_path56.default.join(baseDir, "policy-violations.ndjson")
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: import_node_path56.default.join(baseDir, `${project}.json`),
10677
- errorsPath: import_node_path56.default.join(baseDir, `errors.${project}.ndjson`),
10678
- staleEventsPath: import_node_path56.default.join(baseDir, `stale-events.${project}.ndjson`),
10679
- embeddingsCachePath: import_node_path56.default.join(baseDir, `embeddings.${project}.json`),
10680
- policyViolationsPath: import_node_path56.default.join(baseDir, `policy-violations.${project}.ndjson`)
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 import_node_path57 = __toESM(require("path"), 1);
10719
- var import_types42 = require("@neat.is/types");
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 import_node_path57.default.resolve(override);
10725
- return import_node_path57.default.join(import_node_os3.default.homedir(), ".neat");
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 import_node_path57.default.join(neatHome(), "projects.json");
10858
+ return import_node_path58.default.join(neatHome(), "projects.json");
10729
10859
  }
10730
10860
  function registryLockPath() {
10731
- return import_node_path57.default.join(neatHome(), "projects.json.lock");
10861
+ return import_node_path58.default.join(neatHome(), "projects.json.lock");
10732
10862
  }
10733
10863
  function daemonPidPath() {
10734
- return import_node_path57.default.join(neatHome(), "neatd.pid");
10864
+ return import_node_path58.default.join(neatHome(), "neatd.pid");
10735
10865
  }
10736
10866
  function daemonsDir() {
10737
- return import_node_path57.default.join(neatHome(), "daemons");
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 = import_node_path57.default.join(dir, name);
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 = import_node_path57.default.resolve(input);
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(import_node_path57.default.dirname(target), { recursive: true });
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(import_node_path57.default.dirname(lockPath), { recursive: true });
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 import_types42.RegistryFileSchema.parse(parsed);
11103
+ return import_types43.RegistryFileSchema.parse(parsed);
10974
11104
  }
10975
11105
  async function writeRegistry(reg) {
10976
- const validated = import_types42.RegistryFileSchema.parse(reg);
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 import_node_path58 = __toESM(require("path"), 1);
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 import_node_path58.default.resolve(override);
11178
- return import_node_path58.default.join(import_node_os4.default.homedir(), ".neat");
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 import_node_path58.default.join(home, "connectors.json");
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 import_node_path58.default.join(home, "connectors.json.lock");
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(import_node_path58.default.dirname(file), { recursive: true });
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(import_node_path58.default.dirname(lockPath), { recursive: true });
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 import_types43 = require("@neat.is/types");
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 !== import_types43.Provenance.EXTRACTED) continue;
11497
- const parsed = (0, import_types43.parseFileId)(edge.source);
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 !== import_types43.NodeType.RouteNode || !attrs.path) return void 0;
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(path71) {
11986
- const rpcMatch = REST_RPC_PATH_RE.exec(path71);
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(path71);
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 import_types45 = require("@neat.is/types");
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, import_types45.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
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: import_types45.EdgeType.CALLS };
12237
+ return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types46.EdgeType.CALLS };
12108
12238
  }
12109
- const bareResourceId = (0, import_types45.infraId)(signal.targetKind, signal.targetName);
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: import_types45.EdgeType.CALLS };
12241
+ return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types46.EdgeType.CALLS };
12112
12242
  }
12113
- const projectLevelId = (0, import_types45.infraId)("supabase", config.nodeRef);
12243
+ const projectLevelId = (0, import_types46.infraId)("supabase", config.nodeRef);
12114
12244
  if (graph.hasNode(projectLevelId)) {
12115
- return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types45.EdgeType.CALLS };
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 import_types49 = require("@neat.is/types");
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 !== import_types49.NodeType.RouteNode) return;
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: import_types49.EdgeType.CALLS };
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, import_types49.serviceId)(peerName), serviceName, edgeType: import_types49.EdgeType.CONNECTS_TO };
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 path71 = rest.slice(secondSep + 1);
12593
- if (!resourceName || !method || !path71) return null;
12594
- return { resourceName, method, path: path71 };
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 path71 = pathFromRequestUrl(req.requestUrl);
12633
- if (path71 === null) return null;
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: path71 }),
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 import_types50 = require("@neat.is/types");
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 !== import_types50.NodeType.RouteNode) return;
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: import_types50.EdgeType.CALLS
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 import_types52 = require("@neat.is/types");
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 path71 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
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
- ...path71 ? { path: path71 } : {},
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 === import_types52.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
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, path71) {
12901
- const normalizedPath = normalizePathTemplate(path71);
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 !== import_types52.NodeType.RouteNode || a.service !== serviceName) return;
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: path71 } = signal;
13048
+ const { method, path: path72 } = signal;
12919
13049
  const resolveRouteGrain = (serviceName, wholeFileId) => {
12920
- if (!method || !path71) return wholeFileId;
12921
- return findMatchingRouteNode(graph, serviceName, method, path71) ?? wholeFileId;
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, import_types52.fileId)(mapping.service, mapping.entryFile);
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: import_types52.EdgeType.CALLS
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: import_types52.EdgeType.CALLS
13068
+ edgeType: import_types53.EdgeType.CALLS
12939
13069
  };
12940
13070
  }
12941
13071
  return {
12942
- targetNodeId: (0, import_types52.infraId)("cloudflare-worker", scriptName),
13072
+ targetNodeId: (0, import_types53.infraId)("cloudflare-worker", scriptName),
12943
13073
  serviceName: scriptName,
12944
- edgeType: import_types52.EdgeType.CALLS,
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 import_types56 = require("@neat.is/types");
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, import_types56.infraId)("sql-table", signal.targetName),
13275
+ targetNodeId: (0, import_types57.infraId)("sql-table", signal.targetName),
13146
13276
  serviceName: config.serviceName,
13147
- edgeType: import_types56.EdgeType.CALLS,
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 = import_types59.DivergenceTypeSchema.safeParse(c);
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: import_types59.DivergenceTypeSchema.options
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 = import_types59.PolicySeveritySchema.safeParse(req.query.severity);
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 = import_types59.PoliciesCheckBodySchema.safeParse(req.body ?? {});
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 import_node_path60 = __toESM(require("path"), 1);
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 import_node_path59 = __toESM(require("path"), 1);
14602
+ var import_node_path60 = __toESM(require("path"), 1);
14473
14603
 
14474
14604
  // src/daemon.ts
14475
- var import_types60 = require("@neat.is/types");
14605
+ var import_types61 = require("@neat.is/types");
14476
14606
  function daemonJsonPath(scanPath) {
14477
- return import_node_path60.default.join(scanPath, "neat-out", "daemon.json");
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 import_node_path60.default.join(base, "daemons");
14611
+ return import_node_path61.default.join(base, "daemons");
14482
14612
  }
14483
14613
  function daemonDiscoveryPath(project, home) {
14484
- return import_node_path60.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
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 import_node_path60.default.resolve(env);
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 import_node_path60.default.join(home, ".neat");
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 import_node_path61 = __toESM(require("path"), 1);
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(import_node_path61.default.dirname(cachePath), { recursive: true });
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 = import_node_path62.default.basename(relPath).toLowerCase();
14886
- const segments = relPath.split(import_node_path62.default.sep).map((s) => s.toLowerCase());
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(import_node_path62.default.join(dir, e.name) + import_node_path62.default.sep))) continue;
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(import_node_path62.default.join(dir, e.name), depth + 1);
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 = import_node_path62.default.join(opts.scanPath, "policy.json");
15035
- const policyViolationsPath = import_node_path62.default.join(import_node_path62.default.dirname(opts.outPath), "policy-violations.ndjson");
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 ?? import_node_path62.default.join(import_node_path62.default.dirname(opts.outPath), "embeddings.json");
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, import_node_path62.default.dirname(opts.outPath)),
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 = import_node_path62.default.relative(opts.scanPath, absPath);
15361
+ const rel = import_node_path63.default.relative(opts.scanPath, absPath);
15232
15362
  if (!rel || rel.startsWith("..")) return;
15233
- pendingPaths.add(rel.split(import_node_path62.default.sep).join("/"));
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 import_node_path63 = __toESM(require("path"), 1);
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 = import_node_path63.default.join(cwd, "docker-compose.neat.yml");
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 ${import_node_path63.default.basename(artifactPath)} up -d`
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 = import_node_path63.default.join(cwd, "neat.service");
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 import_node_path64 = __toESM(require("path"), 1);
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 ?? import_node_path64.default.basename(serviceDir);
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 ?? import_node_path64.default.basename(serviceDir);
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(import_node_path64.default.join(pkgRoot, "app.json"));
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(import_node_path64.default.join(pkgRoot, "vite.config.js")) || await exists3(import_node_path64.default.join(pkgRoot, "vite.config.ts")) || await exists3(import_node_path64.default.join(pkgRoot, "vite.config.mjs")) || "vite" in deps) {
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(import_node_path64.default.join(pkgRoot, "wrangler.toml"))) return "cloudflare-workers";
16131
- if (await exists3(import_node_path64.default.join(pkgRoot, "bun.lockb"))) return "bun";
16132
- if (await exists3(import_node_path64.default.join(pkgRoot, "deno.json")) || await exists3(import_node_path64.default.join(pkgRoot, "deno.lock"))) {
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(import_node_path64.default.join(serviceDir, "package.json"), "utf8");
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 = import_node_path64.default.join(serviceDir, name);
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 = import_node_path64.default.join(serviceDir, rel);
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 = import_node_path64.default.join(serviceDir, rel);
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 = import_node_path64.default.join(serviceDir, rel);
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 = import_node_path64.default.join(serviceDir, name);
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 = import_node_path64.default.join(serviceDir, name);
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(import_node_path64.default.join(serviceDir, "tsconfig.json"));
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 = import_node_path64.default.resolve(serviceDir, pkg.main);
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 = import_node_path64.default.resolve(serviceDir, binEntry);
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 = import_node_path64.default.resolve(serviceDir, startEntry);
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 = import_node_path64.default.resolve(serviceDir, devEntry);
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 = import_node_path64.default.join(serviceDir, rel);
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 = import_node_path64.default.join(serviceDir, rel);
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 = import_node_path64.default.join(serviceDir, rel);
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 = import_node_path64.default.join(serviceDir, name);
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 = import_node_path64.default.extname(entryFile).toLowerCase();
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 = import_node_path64.default.relative(import_node_path64.default.dirname(entryFile), otelInitFile);
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(import_node_path64.default.sep).join("/");
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(import_node_path64.default.join(serviceDir, "src", "app")),
16439
- exists3(import_node_path64.default.join(serviceDir, "src", "pages")),
16440
- exists3(import_node_path64.default.join(serviceDir, "app")),
16441
- exists3(import_node_path64.default.join(serviceDir, "pages"))
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 ? import_node_path64.default.join(serviceDir, "src") : serviceDir;
16449
- const instrumentationFile = import_node_path64.default.join(baseDir, useTs ? "instrumentation.ts" : "instrumentation.js");
16450
- const instrumentationNodeFile = import_node_path64.default.join(
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 = import_node_path64.default.join(
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 = import_node_path64.default.join(baseDir, ".env.neat");
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 = import_node_path64.default.join(serviceDir, ".env.neat");
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 = import_node_path64.default.join(
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 = import_node_path64.default.join(
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 ?? import_node_path64.default.join(serviceDir, useTs ? "src/hooks.server.ts" : "src/hooks.server.js");
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 = import_node_path64.default.join(
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 = import_node_path64.default.join(
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 = import_node_path64.default.join(serviceDir, rel);
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 = import_node_path64.default.join(
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 ?? import_node_path64.default.join(serviceDir, useTs ? "src/middleware.ts" : "src/middleware.js");
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 = import_node_path64.default.join(serviceDir, "package.json");
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 = import_node_path64.default.join(import_node_path64.default.dirname(entryFile), otelInitFilename(flavor));
16940
- const envNeatFile = import_node_path64.default.join(serviceDir, ".env.neat");
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 = import_node_path64.default.relative(serviceDir, target);
17139
+ const rel = import_node_path65.default.relative(serviceDir, target);
17010
17140
  if (rel.startsWith("..")) return false;
17011
- const base = import_node_path64.default.basename(target);
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(import_node_path64.default.sep).join("/");
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(import_node_path64.default.dirname(file), { recursive: true });
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 = import_node_path64.default.join(installPlan.serviceDir, "neat-rollback.patch");
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 import_node_path65 = __toESM(require("path"), 1);
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 = import_node_path65.default.join(serviceDir, "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 = import_node_path65.default.join(serviceDir, `${modPath}.py`);
17485
+ const asFile = import_node_path66.default.join(serviceDir, `${modPath}.py`);
17356
17486
  if (await exists4(asFile)) return asFile;
17357
- const asPkg = import_node_path65.default.join(serviceDir, modPath, "__init__.py");
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 = import_node_path65.default.join(serviceDir, runFile[1]);
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 = import_node_path65.default.join(serviceDir, name);
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(import_node_path65.default.join(serviceDir, m))) return true;
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 = import_node_path65.default.join(serviceDir, "requirements.txt");
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 = import_node_path65.default.join(serviceDir, "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: import_node_path65.default.join(serviceDir, NEAT_OTEL_FILENAME), contents: neatOtelPy() }] : [];
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 = import_node_path65.default.basename(file);
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 = import_node_path65.default.join(installPlan.serviceDir, "neat-rollback.patch");
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 import_node_path66 = __toESM(require("path"), 1);
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 = import_node_path66.default.join(serviceDir, "main.go");
17728
+ const root = import_node_path67.default.join(serviceDir, "main.go");
17599
17729
  if (await exists5(root)) return root;
17600
- const cmd = import_node_path66.default.join(serviceDir, "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 = import_node_path66.default.join(cmd, entry2.name, "main.go");
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(import_node_path66.default.join(serviceDir, "go.mod"));
17809
+ return exists5(import_node_path67.default.join(serviceDir, "go.mod"));
17680
17810
  }
17681
17811
  async function plan3(serviceDir) {
17682
- const manifest = import_node_path66.default.join(serviceDir, "go.mod");
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 = import_node_path66.default.join(import_node_path66.default.dirname(main2), "neat_otel.go");
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 import_node_path67 = __toESM(require("path"), 1);
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, import_node_path67.default.join(opts.scanPath, "neat-out"));
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 = import_node_path67.default.basename(svc.dir);
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 = import_node_path67.default.basename(svc.dir);
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 = import_node_path67.default.basename(svc.dir);
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 = import_node_path67.default.basename(svc.dir);
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 = import_node_path67.default.basename(svc.dir);
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 = import_node_path67.default.basename(svc.dir);
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 = import_node_path67.default.basename(svc.dir);
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 = import_node_path67.default.join(scanPath, "neat-out", "daemon.spawn.lock");
18170
- await import_node_fs41.promises.mkdir(import_node_path67.default.dirname(lockPath), { recursive: true });
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(projectPath2) {
18215
- return import_node_path67.default.join(projectPath2, "neat-out", "daemon.log");
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 = import_node_path67.default.dirname((0, import_node_url4.fileURLToPath)(importMetaUrl));
18348
+ const here = import_node_path68.default.dirname((0, import_node_url4.fileURLToPath)(importMetaUrl));
18219
18349
  const candidates = [
18220
- import_node_path67.default.join(here, "neatd.cjs"),
18221
- import_node_path67.default.join(here, "neatd.js")
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(import_node_path67.default.dirname(logPath), { recursive: true });
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 ? import_node_path67.default.relative(opts.scanPath, daemonLogPath(opts.scanPath)) : null;
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 import_node_path68 = __toESM(require("path"), 1);
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 : import_node_path68.default.dirname((0, import_node_url5.fileURLToPath)(importMetaUrl));
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
- import_node_path68.default.resolve(here, "../../claude-skill", rel),
18924
- import_node_path68.default.resolve(here, "../../../claude-skill", rel),
18925
- import_node_path68.default.resolve(here, "../claude-skill", rel)
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 import_node_path68.default.resolve(override);
18940
- return import_node_path68.default.join(import_node_os5.default.homedir(), ".neat");
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 import_node_path68.default.resolve(override);
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 import_node_path68.default.join(home, ".claude", "settings.json");
19076
+ return import_node_path69.default.join(home, ".claude", "settings.json");
18947
19077
  }
18948
19078
  function installedHookPath() {
18949
- return import_node_path68.default.join(neatHome3(), "hooks", HOOK_FILENAME);
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(import_node_path68.default.dirname(scriptPath), { recursive: true });
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 = import_node_path68.default.join(neatHome3(), GUIDE_INSTALL_NAME);
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(import_node_path68.default.dirname(settingsFile), { recursive: true });
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/cli-verbs.ts
19211
+ // src/monitor.ts
19082
19212
  init_cjs_shims();
19083
- var import_node_path69 = __toESM(require("path"), 1);
19213
+ var import_types63 = require("@neat.is/types");
19084
19214
 
19085
19215
  // src/cli-client.ts
19086
19216
  init_cjs_shims();
19087
- var import_types61 = require("@neat.is/types");
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(path71) {
19242
+ async get(path72) {
19113
19243
  let res;
19114
19244
  try {
19115
- res = await fetch(`${root}${path71}`, {
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 ${path71}: ${body}`,
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(path71, body) {
19263
+ async post(path72, body) {
19134
19264
  let res;
19135
19265
  try {
19136
- res = await fetch(`${root}${path71}`, {
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 ${path71}: ${text}`,
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 path71 = projectPath(
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(path71);
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 path71 = projectPath(
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(path71);
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 === import_types61.Provenance.STALE ? " [STALE \u2014 last seen too long ago]" : "";
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 path71 = projectPath(
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(path71);
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: import_types61.Provenance.OBSERVED
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: import_types61.Provenance.OBSERVED
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 path71 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
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(path71);
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: import_types61.Provenance.OBSERVED
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: import_types61.Provenance.STALE
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}${import_node_path69.default.sep}`)) {
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 import_types62 = require("@neat.is/types");
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 = import_node_path70.default.join(opts.scanPath, "neat.patch");
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 = import_node_path70.default.join(opts.scanPath, ".gitignore");
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
- import_node_path70.default.join(opts.scanPath, "neat-out")
20549
+ import_node_path71.default.join(opts.scanPath, "neat-out")
20108
20550
  );
20109
- const errorsPath = import_node_path70.default.join(import_node_path70.default.dirname(opts.outPath), import_node_path70.default.basename(projectPaths.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 import_node_path70.default.resolve(override);
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 import_node_path70.default.join(home, ".claude.json");
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(import_node_path70.default.dirname(target), { recursive: true });
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 = import_node_path70.default.resolve(target);
20762
+ const scanPath = import_node_path71.default.resolve(target);
20321
20763
  const projectExplicit = parsed.project !== null;
20322
- const projectName = projectExplicit ? project : import_node_path70.default.basename(scanPath);
20764
+ const projectName = projectExplicit ? project : import_node_path71.default.basename(scanPath);
20323
20765
  const projectKey = projectExplicit ? project : DEFAULT_PROJECT;
20324
- const fallback = pathsForProject(projectKey, import_node_path70.default.join(scanPath, "neat-out")).snapshotPath;
20325
- const outPath = import_node_path70.default.resolve(process.env.NEAT_OUT_PATH ?? fallback);
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 = import_node_path70.default.resolve(target);
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, import_node_path70.default.join(scanPath, "neat-out"));
20353
- const outPath = import_node_path70.default.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
20354
- const errorsPath = import_node_path70.default.resolve(
20355
- process.env.NEAT_ERRORS_PATH ?? import_node_path70.default.join(import_node_path70.default.dirname(outPath), import_node_path70.default.basename(projectPaths.errorsPath))
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 = import_node_path70.default.resolve(
20358
- process.env.NEAT_STALE_EVENTS_PATH ?? import_node_path70.default.join(import_node_path70.default.dirname(outPath), import_node_path70.default.basename(projectPaths.staleEventsPath))
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 ? import_node_path70.default.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
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 ? import_node_path70.default.resolve(process.env.NEAT_HOME) : import_node_path70.default.join(import_node_os6.default.homedir(), ".neat"),
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 projectPath2 = daemon?.record.projectPath ?? removed?.path ?? "(unknown path)";
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} (${projectPath2})`);
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 = import_node_path70.default.resolve(cmd);
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 : import_node_path70.default.basename(scanPath);
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 = import_types62.DivergenceTypeSchema.safeParse(p);
21186
+ const r = import_types64.DivergenceTypeSchema.safeParse(p);
20740
21187
  if (!r.success) {
20741
21188
  console.error(
20742
- `neat divergences: unknown --type "${p}". allowed: ${import_types62.DivergenceTypeSchema.options.join(", ")}`
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