@neat.is/core 0.6.0 → 0.6.2-dev.20260722

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 path63 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
65
- if (exactUnauthPaths.has(path63) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path63)) {
64
+ const path64 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
65
+ if (exactUnauthPaths.has(path64) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path64)) {
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_path50.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
198
- return import_node_path50.default.resolve(here, "..", "proto");
197
+ const here = import_node_path51.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
198
+ return import_node_path51.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_path50, import_node_crypto2, grpc, protoLoader;
266
+ var import_node_url2, import_node_path51, 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_path50 = __toESM(require("path"), 1);
272
+ import_node_path51 = __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);
@@ -347,6 +347,14 @@ function normalizeDbSystem(attrs) {
347
347
  if (typeof raw !== "string") return void 0;
348
348
  return raw === "mongoose" ? "mongodb" : raw;
349
349
  }
350
+ function tableFromSqlStatement(sql) {
351
+ if (typeof sql !== "string" || sql.length === 0) return null;
352
+ if (/\bjoin\b/i.test(sql)) return null;
353
+ const froms = sql.match(/\bfrom\b/gi);
354
+ if (froms && froms.length > 1) return null;
355
+ const m = /\b(?:from|into|update)\s+(?:"?[\w$]+"?\s*\.\s*)?"?([a-zA-Z_][\w$]*)"?/i.exec(sql);
356
+ return m ? m[1] : null;
357
+ }
350
358
  function messagingDestinationOf(attrs) {
351
359
  for (const key of ["messaging.destination.name", "messaging.destination"]) {
352
360
  const v = attrs[key];
@@ -367,8 +375,8 @@ function websocketChannelPathOf(attrs) {
367
375
  const v = attrs[key];
368
376
  if (typeof v === "string" && v.length > 0) {
369
377
  const q = v.indexOf("?");
370
- const path63 = q === -1 ? v : v.slice(0, q);
371
- if (path63.length > 0) return path63;
378
+ const path64 = q === -1 ? v : v.slice(0, q);
379
+ if (path64.length > 0) return path64;
372
380
  }
373
381
  }
374
382
  return void 0;
@@ -404,6 +412,7 @@ function parseOtlpRequest(body) {
404
412
  dbSystem: normalizeDbSystem(attrs),
405
413
  dbName: typeof attrs["db.name"] === "string" ? attrs["db.name"] : void 0,
406
414
  dbCollection: typeof attrs["db.collection.name"] === "string" ? attrs["db.collection.name"] : typeof attrs["db.mongodb.collection"] === "string" ? attrs["db.mongodb.collection"] : void 0,
415
+ dbTable: typeof attrs["db.statement"] === "string" ? tableFromSqlStatement(attrs["db.statement"]) ?? void 0 : void 0,
407
416
  messagingSystem: typeof attrs["messaging.system"] === "string" ? attrs["messaging.system"] : void 0,
408
417
  messagingDestination: messagingDestinationOf(attrs),
409
418
  graphqlOperationName: typeof attrs["graphql.operation.name"] === "string" && attrs["graphql.operation.name"].length > 0 ? attrs["graphql.operation.name"] : void 0,
@@ -423,10 +432,10 @@ function parseOtlpRequest(body) {
423
432
  return out;
424
433
  }
425
434
  function loadProtoRoot() {
426
- const here = import_node_path51.default.dirname((0, import_node_url3.fileURLToPath)(importMetaUrl));
427
- const protoRoot = import_node_path51.default.resolve(here, "..", "proto");
435
+ const here = import_node_path52.default.dirname((0, import_node_url3.fileURLToPath)(importMetaUrl));
436
+ const protoRoot = import_node_path52.default.resolve(here, "..", "proto");
428
437
  const root = new import_protobufjs.default.Root();
429
- root.resolvePath = (_origin, target) => import_node_path51.default.resolve(protoRoot, target);
438
+ root.resolvePath = (_origin, target) => import_node_path52.default.resolve(protoRoot, target);
430
439
  root.loadSync(
431
440
  "opentelemetry/proto/collector/trace/v1/trace_service.proto",
432
441
  { keepCase: true }
@@ -654,12 +663,12 @@ async function listenSteppingOtlp(app, requestedPort, host) {
654
663
  }
655
664
  }
656
665
  }
657
- var import_node_path51, import_node_url3, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
666
+ var import_node_path52, import_node_url3, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
658
667
  var init_otel = __esm({
659
668
  "src/otel.ts"() {
660
669
  "use strict";
661
670
  init_cjs_shims();
662
- import_node_path51 = __toESM(require("path"), 1);
671
+ import_node_path52 = __toESM(require("path"), 1);
663
672
  import_node_url3 = require("url");
664
673
  import_fastify2 = __toESM(require("fastify"), 1);
665
674
  import_protobufjs = __toESM(require("protobufjs"), 1);
@@ -696,7 +705,7 @@ __export(cli_exports, {
696
705
  });
697
706
  module.exports = __toCommonJS(cli_exports);
698
707
  init_cjs_shims();
699
- var import_node_path62 = __toESM(require("path"), 1);
708
+ var import_node_path63 = __toESM(require("path"), 1);
700
709
  var import_node_fs41 = require("fs");
701
710
 
702
711
  // src/banner.ts
@@ -1262,19 +1271,19 @@ function confidenceFromMix(edges, now = Date.now()) {
1262
1271
  function longestIncomingWalk(graph, start, maxDepth) {
1263
1272
  let best = { path: [start], edges: [] };
1264
1273
  const visited = /* @__PURE__ */ new Set([start]);
1265
- function step(node, path63, edges) {
1266
- if (path63.length > best.path.length) {
1267
- best = { path: [...path63], edges: [...edges] };
1274
+ function step(node, path64, edges) {
1275
+ if (path64.length > best.path.length) {
1276
+ best = { path: [...path64], edges: [...edges] };
1268
1277
  }
1269
- if (path63.length - 1 >= maxDepth) return;
1278
+ if (path64.length - 1 >= maxDepth) return;
1270
1279
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
1271
1280
  for (const [srcId, edge] of incoming) {
1272
1281
  if (visited.has(srcId)) continue;
1273
1282
  visited.add(srcId);
1274
- path63.push(srcId);
1283
+ path64.push(srcId);
1275
1284
  edges.push(edge);
1276
- step(srcId, path63, edges);
1277
- path63.pop();
1285
+ step(srcId, path64, edges);
1286
+ path64.pop();
1278
1287
  edges.pop();
1279
1288
  visited.delete(srcId);
1280
1289
  }
@@ -1282,11 +1291,11 @@ function longestIncomingWalk(graph, start, maxDepth) {
1282
1291
  step(start, [start], []);
1283
1292
  return best;
1284
1293
  }
1285
- function databaseRootCauseShape(graph, origin, walk3) {
1294
+ function databaseRootCauseShape(graph, origin, walk4) {
1286
1295
  const targetDb = origin;
1287
1296
  const candidatePairs = compatPairs().filter((p) => p.engine === targetDb.engine);
1288
1297
  if (candidatePairs.length === 0) return null;
1289
- for (const id of walk3.path) {
1298
+ for (const id of walk4.path) {
1290
1299
  const owner = resolveOwningService(graph, id);
1291
1300
  if (!owner) continue;
1292
1301
  const { id: serviceId6, svc } = owner;
@@ -1313,8 +1322,8 @@ function databaseRootCauseShape(graph, origin, walk3) {
1313
1322
  }
1314
1323
  return null;
1315
1324
  }
1316
- function serviceRootCauseShape(graph, _origin, walk3) {
1317
- for (const id of walk3.path) {
1325
+ function serviceRootCauseShape(graph, _origin, walk4) {
1326
+ for (const id of walk4.path) {
1318
1327
  const owner = resolveOwningService(graph, id);
1319
1328
  if (!owner) continue;
1320
1329
  const { id: serviceId6, svc } = owner;
@@ -1350,10 +1359,10 @@ function serviceRootCauseShape(graph, _origin, walk3) {
1350
1359
  }
1351
1360
  return null;
1352
1361
  }
1353
- function fileRootCauseShape(graph, origin, walk3) {
1362
+ function fileRootCauseShape(graph, origin, walk4) {
1354
1363
  const owner = resolveOwningService(graph, origin.id);
1355
1364
  if (!owner) return null;
1356
- return serviceRootCauseShape(graph, owner.svc, walk3);
1365
+ return serviceRootCauseShape(graph, owner.svc, walk4);
1357
1366
  }
1358
1367
  var rootCauseShapes = {
1359
1368
  [import_types.NodeType.DatabaseNode]: databaseRootCauseShape,
@@ -1365,16 +1374,16 @@ function getRootCause(graph, errorNodeId, errorEvent, incidents) {
1365
1374
  const origin = graph.getNodeAttributes(errorNodeId);
1366
1375
  const shape = rootCauseShapes[origin.type];
1367
1376
  if (shape) {
1368
- const walk3 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
1369
- const match = shape(graph, origin, walk3);
1377
+ const walk4 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
1378
+ const match = shape(graph, origin, walk4);
1370
1379
  if (match) {
1371
1380
  const reason = errorEvent ? `${match.rootCauseReason} (observed error: ${errorEvent.errorMessage})` : match.rootCauseReason;
1372
1381
  return import_types.RootCauseResultSchema.parse({
1373
1382
  rootCauseNode: match.rootCauseNode,
1374
1383
  rootCauseReason: reason,
1375
- traversalPath: walk3.path,
1376
- edgeProvenances: walk3.edges.map((e) => e.provenance),
1377
- confidence: confidenceFromMix(walk3.edges),
1384
+ traversalPath: walk4.path,
1385
+ edgeProvenances: walk4.edges.map((e) => e.provenance),
1386
+ confidence: confidenceFromMix(walk4.edges),
1378
1387
  fixRecommendation: match.fixRecommendation
1379
1388
  });
1380
1389
  }
@@ -1395,8 +1404,8 @@ function localizeFromIncidents(nodeId, incidents, errorEvent) {
1395
1404
  if (relevant.length === 0) return null;
1396
1405
  const latest = [...relevant].sort((a, b) => b.timestamp.localeCompare(a.timestamp))[0];
1397
1406
  const attrs = latest.attributes ?? {};
1398
- const filepath = typeof attrs["code.filepath"] === "string" ? attrs["code.filepath"] : void 0;
1399
- const lineno = typeof attrs["code.lineno"] === "number" ? attrs["code.lineno"] : void 0;
1407
+ const filepath = codeFilepathOf(attrs);
1408
+ const lineno = codeLinenoOf(attrs);
1400
1409
  const route = typeof attrs["http.route"] === "string" ? attrs["http.route"] : void 0;
1401
1410
  const location = filepath ? `${filepath}${lineno !== void 0 ? `:${lineno}` : ""}` : void 0;
1402
1411
  const sameMode = relevant.filter((ev) => ev.errorMessage === latest.errorMessage);
@@ -1468,26 +1477,26 @@ function dominantFailingCall(graph, serviceId6, visited) {
1468
1477
  return best;
1469
1478
  }
1470
1479
  function followFailingCallChain(graph, originServiceId, maxDepth) {
1471
- const path63 = [originServiceId];
1480
+ const path64 = [originServiceId];
1472
1481
  const edges = [];
1473
1482
  const visited = /* @__PURE__ */ new Set([originServiceId]);
1474
1483
  let current = originServiceId;
1475
1484
  for (let depth = 0; depth < maxDepth; depth++) {
1476
1485
  const hop = dominantFailingCall(graph, current, visited);
1477
1486
  if (!hop) break;
1478
- path63.push(hop.nextService);
1487
+ path64.push(hop.nextService);
1479
1488
  edges.push(hop.edge);
1480
1489
  visited.add(hop.nextService);
1481
1490
  current = hop.nextService;
1482
1491
  }
1483
1492
  if (edges.length === 0) return null;
1484
- return { path: path63, edges, culprit: current };
1493
+ return { path: path64, edges, culprit: current };
1485
1494
  }
1486
1495
  function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1487
1496
  const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
1488
1497
  if (!chain) return null;
1489
1498
  const culprit = chain.culprit;
1490
- const path63 = [...chain.path];
1499
+ const path64 = [...chain.path];
1491
1500
  const edgeProvenances = chain.edges.map((e) => e.provenance);
1492
1501
  const baseConfidence = confidenceFromMix(chain.edges);
1493
1502
  const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
@@ -1495,14 +1504,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1495
1504
  if (loc) {
1496
1505
  let rootCauseNode = culprit;
1497
1506
  if (loc.fileNode) {
1498
- path63.push(loc.fileNode);
1507
+ path64.push(loc.fileNode);
1499
1508
  edgeProvenances.push(import_types.Provenance.OBSERVED);
1500
1509
  rootCauseNode = loc.fileNode;
1501
1510
  }
1502
1511
  return import_types.RootCauseResultSchema.parse({
1503
1512
  rootCauseNode,
1504
1513
  rootCauseReason: loc.rootCauseReason,
1505
- traversalPath: path63,
1514
+ traversalPath: path64,
1506
1515
  edgeProvenances,
1507
1516
  confidence,
1508
1517
  ...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
@@ -1514,7 +1523,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1514
1523
  return import_types.RootCauseResultSchema.parse({
1515
1524
  rootCauseNode: culprit,
1516
1525
  rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
1517
- traversalPath: path63,
1526
+ traversalPath: path64,
1518
1527
  edgeProvenances,
1519
1528
  confidence,
1520
1529
  fixRecommendation: `Inspect ${culpritName}'s failing handler`
@@ -2272,9 +2281,30 @@ function isLoopbackHost(host) {
2272
2281
  const h = host.toLowerCase();
2273
2282
  return h === "localhost" || h === "ip6-localhost" || h === "::1" || h === "[::1]" || /^127(?:\.\d{1,3}){3}$/.test(h);
2274
2283
  }
2284
+ var CODE_FILE_PATH_ATTR = "code.file.path";
2275
2285
  var CODE_FILEPATH_ATTR = "code.filepath";
2286
+ var CODE_LINE_NUMBER_ATTR = "code.line.number";
2276
2287
  var CODE_LINENO_ATTR = "code.lineno";
2288
+ var CODE_FUNCTION_NAME_ATTR = "code.function.name";
2277
2289
  var CODE_FUNCTION_ATTR = "code.function";
2290
+ function codeFilepathOf(attrs) {
2291
+ const stable = attrs[CODE_FILE_PATH_ATTR];
2292
+ if (typeof stable === "string" && stable.length > 0) return stable;
2293
+ const prior = attrs[CODE_FILEPATH_ATTR];
2294
+ return typeof prior === "string" && prior.length > 0 ? prior : void 0;
2295
+ }
2296
+ function codeLinenoOf(attrs) {
2297
+ const stable = attrs[CODE_LINE_NUMBER_ATTR];
2298
+ if (typeof stable === "number" && Number.isFinite(stable)) return stable;
2299
+ const prior = attrs[CODE_LINENO_ATTR];
2300
+ return typeof prior === "number" && Number.isFinite(prior) ? prior : void 0;
2301
+ }
2302
+ function codeFunctionOf(attrs) {
2303
+ const stable = attrs[CODE_FUNCTION_NAME_ATTR];
2304
+ if (typeof stable === "string" && stable.length > 0) return stable;
2305
+ const prior = attrs[CODE_FUNCTION_ATTR];
2306
+ return typeof prior === "string" && prior.length > 0 ? prior : void 0;
2307
+ }
2278
2308
  function toPosix(p) {
2279
2309
  return p.split("\\").join("/");
2280
2310
  }
@@ -2352,10 +2382,9 @@ function resolveDistToSrc(absFilepath, line) {
2352
2382
  }
2353
2383
  }
2354
2384
  function callSiteFromSpan(span, serviceNode, scanPath) {
2355
- const filepath = span.attributes[CODE_FILEPATH_ATTR];
2356
- if (typeof filepath !== "string" || filepath.length === 0) return null;
2357
- const linenoRaw = span.attributes[CODE_LINENO_ATTR];
2358
- let line = typeof linenoRaw === "number" && Number.isFinite(linenoRaw) ? linenoRaw : void 0;
2385
+ const filepath = codeFilepathOf(span.attributes);
2386
+ if (filepath === void 0) return null;
2387
+ let line = codeLinenoOf(span.attributes);
2359
2388
  const abs = toPosix(filepath).replace(/^file:\/\//, "");
2360
2389
  const resolved = resolveDistToSrc(abs, line);
2361
2390
  let effectivePath = filepath;
@@ -2370,8 +2399,7 @@ function callSiteFromSpan(span, serviceNode, scanPath) {
2370
2399
  if (!resolved && abs.endsWith(".js") && relPath.startsWith("dist/") && serviceNode?.name) {
2371
2400
  warnNoSourceMaps(serviceNode.name);
2372
2401
  }
2373
- const fnRaw = span.attributes[CODE_FUNCTION_ATTR];
2374
- const fn = typeof fnRaw === "string" && fnRaw.length > 0 ? fnRaw : void 0;
2402
+ const fn = codeFunctionOf(span.attributes);
2375
2403
  return {
2376
2404
  relPath,
2377
2405
  ...line !== void 0 ? { line } : {},
@@ -2953,6 +2981,18 @@ async function handleSpan(ctx, span) {
2953
2981
  callSiteEvidence
2954
2982
  );
2955
2983
  }
2984
+ if (span.dbTable) {
2985
+ const tableId = ensureInfraNode(ctx.graph, "sql-table", span.dbTable, "self");
2986
+ upsertObservedEdge(
2987
+ ctx.graph,
2988
+ import_types3.EdgeType.CALLS,
2989
+ observedSource(),
2990
+ tableId,
2991
+ ts,
2992
+ isError,
2993
+ callSiteEvidence
2994
+ );
2995
+ }
2956
2996
  }
2957
2997
  } else if (span.messagingSystem && span.messagingDestination && spanMintsMessagingEdge(span.kind)) {
2958
2998
  const targetId = ensureMessagingDestinationNode(
@@ -4132,14 +4172,14 @@ function buildServiceHostIndex(services) {
4132
4172
  }
4133
4173
  async function walkSourceFiles(dir) {
4134
4174
  const out = [];
4135
- async function walk3(current) {
4175
+ async function walk4(current) {
4136
4176
  const entries = await import_node_fs11.promises.readdir(current, { withFileTypes: true }).catch(() => []);
4137
4177
  for (const entry2 of entries) {
4138
4178
  const full = import_node_path11.default.join(current, entry2.name);
4139
4179
  if (entry2.isDirectory()) {
4140
4180
  if (IGNORED_DIRS.has(entry2.name)) continue;
4141
4181
  if (await isPythonVenvDir(full)) continue;
4142
- await walk3(full);
4182
+ await walk4(full);
4143
4183
  } else if (entry2.isFile() && SERVICE_FILE_EXTENSIONS.has(import_node_path11.default.extname(entry2.name)) && // Skip NEAT's own generated `otel-init.*` bootstrap — extracting it
4144
4184
  // would attribute our instrumentation imports to the user's service.
4145
4185
  !isNeatAuthoredSourceFile(entry2.name)) {
@@ -4147,7 +4187,7 @@ async function walkSourceFiles(dir) {
4147
4187
  }
4148
4188
  }
4149
4189
  }
4150
- await walk3(dir);
4190
+ await walk4(dir);
4151
4191
  return out;
4152
4192
  }
4153
4193
  async function loadSourceFiles(dir) {
@@ -5297,20 +5337,20 @@ var import_node_path22 = __toESM(require("path"), 1);
5297
5337
  var import_types10 = require("@neat.is/types");
5298
5338
  async function walkConfigFiles(dir) {
5299
5339
  const out = [];
5300
- async function walk3(current) {
5340
+ async function walk4(current) {
5301
5341
  const entries = await import_node_fs15.promises.readdir(current, { withFileTypes: true });
5302
5342
  for (const entry2 of entries) {
5303
5343
  const full = import_node_path22.default.join(current, entry2.name);
5304
5344
  if (entry2.isDirectory()) {
5305
5345
  if (IGNORED_DIRS.has(entry2.name)) continue;
5306
5346
  if (await isPythonVenvDir(full)) continue;
5307
- await walk3(full);
5347
+ await walk4(full);
5308
5348
  } else if (entry2.isFile() && isConfigFile(entry2.name).match) {
5309
5349
  out.push(full);
5310
5350
  }
5311
5351
  }
5312
5352
  }
5313
- await walk3(dir);
5353
+ await walk4(dir);
5314
5354
  return out;
5315
5355
  }
5316
5356
  async function addConfigNodes(graph, services, scanPath) {
@@ -5363,6 +5403,7 @@ init_cjs_shims();
5363
5403
  var import_node_path23 = __toESM(require("path"), 1);
5364
5404
  var import_tree_sitter2 = __toESM(require("tree-sitter"), 1);
5365
5405
  var import_tree_sitter_javascript2 = __toESM(require("tree-sitter-javascript"), 1);
5406
+ var import_tree_sitter_python2 = __toESM(require("tree-sitter-python"), 1);
5366
5407
  var import_types11 = require("@neat.is/types");
5367
5408
  var PARSE_CHUNK2 = 16384;
5368
5409
  function parseSource2(parser, source) {
@@ -5375,6 +5416,11 @@ function makeJsParser2() {
5375
5416
  p.setLanguage(import_tree_sitter_javascript2.default);
5376
5417
  return p;
5377
5418
  }
5419
+ function makePyParser2() {
5420
+ const p = new import_tree_sitter2.default();
5421
+ p.setLanguage(import_tree_sitter_python2.default);
5422
+ return p;
5423
+ }
5378
5424
  var ROUTER_METHODS = /* @__PURE__ */ new Set([
5379
5425
  "get",
5380
5426
  "post",
@@ -5387,6 +5433,7 @@ var ROUTER_METHODS = /* @__PURE__ */ new Set([
5387
5433
  ]);
5388
5434
  var NEXT_APP_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
5389
5435
  var JS_ROUTE_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx"]);
5436
+ var FASTAPI_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head", "trace"]);
5390
5437
  function canonicalizeTemplate(raw) {
5391
5438
  let p = raw.split("?")[0].split("#")[0];
5392
5439
  if (!p.startsWith("/")) p = "/" + p;
@@ -5614,8 +5661,102 @@ function nextRoutesFromFile(source, relFile, parser) {
5614
5661
  }
5615
5662
  return [];
5616
5663
  }
5664
+ function pyStaticStringText(node) {
5665
+ if (node.type !== "string") return null;
5666
+ for (let i = 0; i < node.namedChildCount; i++) {
5667
+ const child = node.namedChild(i);
5668
+ if (child?.type === "interpolation") return null;
5669
+ if (child?.type === "string_content") return child.text;
5670
+ }
5671
+ return "";
5672
+ }
5673
+ function keywordArrayStrings(argsNode, key) {
5674
+ for (let i = 0; i < argsNode.namedChildCount; i++) {
5675
+ const arg = argsNode.namedChild(i);
5676
+ if (arg?.type !== "keyword_argument") continue;
5677
+ if (arg.childForFieldName("name")?.text !== key) continue;
5678
+ const val = arg.childForFieldName("value");
5679
+ if (!val || val.type !== "list") return [];
5680
+ const out = [];
5681
+ for (let j = 0; j < val.namedChildCount; j++) {
5682
+ const el = val.namedChild(j);
5683
+ if (el?.type === "string") {
5684
+ const s = pyStaticStringText(el);
5685
+ if (s) out.push(s);
5686
+ }
5687
+ }
5688
+ return out;
5689
+ }
5690
+ return [];
5691
+ }
5692
+ function collectApiRouterPrefixes(root) {
5693
+ const prefixes = /* @__PURE__ */ new Map();
5694
+ walk(root, (node) => {
5695
+ if (node.type !== "assignment") return;
5696
+ const right = node.childForFieldName("right");
5697
+ if (!right || right.type !== "call") return;
5698
+ const fn = right.childForFieldName("function");
5699
+ if (!fn) return;
5700
+ const ctor = fn.type === "attribute" ? fn.childForFieldName("attribute")?.text : fn.text;
5701
+ if (ctor !== "APIRouter") return;
5702
+ const left = node.childForFieldName("left");
5703
+ if (!left || left.type !== "identifier") return;
5704
+ const args = right.childForFieldName("arguments");
5705
+ if (!args) return;
5706
+ for (let i = 0; i < args.namedChildCount; i++) {
5707
+ const arg = args.namedChild(i);
5708
+ if (arg?.type !== "keyword_argument") continue;
5709
+ if (arg.childForFieldName("name")?.text !== "prefix") continue;
5710
+ const val = arg.childForFieldName("value");
5711
+ const p = val ? pyStaticStringText(val) : null;
5712
+ if (p !== null) prefixes.set(left.text, p);
5713
+ }
5714
+ });
5715
+ return prefixes;
5716
+ }
5717
+ function fastapiRoutesFromSource(source, parser) {
5718
+ const tree = parseSource2(parser, source);
5719
+ const prefixes = collectApiRouterPrefixes(tree.rootNode);
5720
+ const out = [];
5721
+ walk(tree.rootNode, (node) => {
5722
+ if (node.type !== "decorator") return;
5723
+ const call = node.namedChild(0);
5724
+ if (!call || call.type !== "call") return;
5725
+ const fn = call.childForFieldName("function");
5726
+ if (!fn || fn.type !== "attribute") return;
5727
+ const method = fn.childForFieldName("attribute")?.text?.toLowerCase();
5728
+ if (!method) return;
5729
+ const isVerb = FASTAPI_METHODS.has(method);
5730
+ if (!isVerb && method !== "api_route") return;
5731
+ const args = call.childForFieldName("arguments");
5732
+ const first = args?.namedChild(0);
5733
+ if (!first || first.type !== "string") return;
5734
+ const rawPath = pyStaticStringText(first);
5735
+ if (rawPath === null || !rawPath.startsWith("/")) return;
5736
+ const obj = fn.childForFieldName("object")?.text;
5737
+ const prefix = obj ? prefixes.get(obj) ?? "" : "";
5738
+ const pathTemplate = canonicalizeTemplate(prefix + rawPath);
5739
+ const line = node.startPosition.row + 1;
5740
+ if (isVerb) {
5741
+ out.push({ method: method.toUpperCase(), pathTemplate, line, framework: "fastapi" });
5742
+ return;
5743
+ }
5744
+ const methods = keywordArrayStrings(args, "methods");
5745
+ const list = methods.length > 0 ? methods : ["ALL"];
5746
+ for (const m of list) {
5747
+ out.push({
5748
+ method: m === "ALL" ? "ALL" : m.toUpperCase(),
5749
+ pathTemplate,
5750
+ line,
5751
+ framework: "fastapi"
5752
+ });
5753
+ }
5754
+ });
5755
+ return out;
5756
+ }
5617
5757
  async function addRoutes(graph, services) {
5618
5758
  const jsParser = makeJsParser2();
5759
+ const pyParser = makePyParser2();
5619
5760
  let nodesAdded = 0;
5620
5761
  let edgesAdded = 0;
5621
5762
  for (const service of services) {
@@ -5627,15 +5768,20 @@ async function addRoutes(graph, services) {
5627
5768
  const hasFastify = deps["fastify"] !== void 0;
5628
5769
  const hasHono = deps["hono"] !== void 0;
5629
5770
  const hasNext = deps["next"] !== void 0;
5630
- if (!hasExpress && !hasFastify && !hasHono && !hasNext) continue;
5771
+ const hasFastapi = deps["fastapi"] !== void 0;
5772
+ if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasFastapi) continue;
5631
5773
  const files = await loadSourceFiles(service.dir);
5632
5774
  for (const file of files) {
5633
5775
  if (isTestPath(file.path)) continue;
5634
- if (!JS_ROUTE_EXTENSIONS.has(import_node_path23.default.extname(file.path))) continue;
5776
+ const ext = import_node_path23.default.extname(file.path);
5777
+ const isPy = ext === ".py";
5778
+ if (!JS_ROUTE_EXTENSIONS.has(ext) && !isPy) continue;
5635
5779
  const relFile = toPosix2(import_node_path23.default.relative(service.dir, file.path));
5636
5780
  let routes;
5637
5781
  try {
5638
- if (hasNext && (isNextAppRouteFile(relFile) || isNextPagesApiFile(relFile))) {
5782
+ if (isPy) {
5783
+ routes = hasFastapi ? fastapiRoutesFromSource(file.content, pyParser) : [];
5784
+ } else if (hasNext && (isNextAppRouteFile(relFile) || isNextPagesApiFile(relFile))) {
5639
5785
  routes = nextRoutesFromFile(file.content, relFile, jsParser);
5640
5786
  } else if (hasExpress || hasFastify || hasHono) {
5641
5787
  routes = serverRoutesFromSource(file.content, jsParser, hasExpress, hasFastify, hasHono);
@@ -5731,20 +5877,20 @@ function grpcMethodsFromProto(content, fqPackage) {
5731
5877
  }
5732
5878
  async function walkProtoFiles(dir) {
5733
5879
  const out = [];
5734
- async function walk3(current) {
5880
+ async function walk4(current) {
5735
5881
  const entries = await import_node_fs16.promises.readdir(current, { withFileTypes: true }).catch(() => []);
5736
5882
  for (const entry2 of entries) {
5737
5883
  const full = import_node_path24.default.join(current, entry2.name);
5738
5884
  if (entry2.isDirectory()) {
5739
5885
  if (IGNORED_DIRS.has(entry2.name)) continue;
5740
5886
  if (await isPythonVenvDir(full)) continue;
5741
- await walk3(full);
5887
+ await walk4(full);
5742
5888
  } else if (entry2.isFile() && import_node_path24.default.extname(entry2.name) === PROTO_EXTENSION) {
5743
5889
  out.push(full);
5744
5890
  }
5745
5891
  }
5746
5892
  }
5747
- await walk3(dir);
5893
+ await walk4(dir);
5748
5894
  return out;
5749
5895
  }
5750
5896
  async function addGrpcMethods(graph, services) {
@@ -5812,14 +5958,14 @@ async function addGrpcMethods(graph, services) {
5812
5958
 
5813
5959
  // src/extract/calls/index.ts
5814
5960
  init_cjs_shims();
5815
- var import_types21 = require("@neat.is/types");
5961
+ var import_types22 = require("@neat.is/types");
5816
5962
 
5817
5963
  // src/extract/calls/http.ts
5818
5964
  init_cjs_shims();
5819
5965
  var import_node_path25 = __toESM(require("path"), 1);
5820
5966
  var import_tree_sitter3 = __toESM(require("tree-sitter"), 1);
5821
5967
  var import_tree_sitter_javascript3 = __toESM(require("tree-sitter-javascript"), 1);
5822
- var import_tree_sitter_python2 = __toESM(require("tree-sitter-python"), 1);
5968
+ var import_tree_sitter_python3 = __toESM(require("tree-sitter-python"), 1);
5823
5969
  var import_types13 = require("@neat.is/types");
5824
5970
  var STRING_LITERAL_NODE_TYPES = /* @__PURE__ */ new Set(["string_fragment", "string_content"]);
5825
5971
  var JSX_EXTERNAL_LINK_TAGS = /* @__PURE__ */ new Set(["a", "Link", "NavLink", "ExternalLink", "Anchor"]);
@@ -5874,14 +6020,14 @@ function makeJsParser3() {
5874
6020
  p.setLanguage(import_tree_sitter_javascript3.default);
5875
6021
  return p;
5876
6022
  }
5877
- function makePyParser2() {
6023
+ function makePyParser3() {
5878
6024
  const p = new import_tree_sitter3.default();
5879
- p.setLanguage(import_tree_sitter_python2.default);
6025
+ p.setLanguage(import_tree_sitter_python3.default);
5880
6026
  return p;
5881
6027
  }
5882
6028
  async function addHttpCallEdges(graph, services) {
5883
6029
  const jsParser = makeJsParser3();
5884
- const pyParser = makePyParser2();
6030
+ const pyParser = makePyParser3();
5885
6031
  const { knownHosts, hostToNodeId } = buildServiceHostIndex(services);
5886
6032
  let nodesAdded = 0;
5887
6033
  let edgesAdded = 0;
@@ -6821,15 +6967,130 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
6821
6967
  return out;
6822
6968
  }
6823
6969
 
6970
+ // src/extract/calls/sqlalchemy.ts
6971
+ init_cjs_shims();
6972
+ var import_node_path33 = __toESM(require("path"), 1);
6973
+ var import_tree_sitter5 = __toESM(require("tree-sitter"), 1);
6974
+ var import_tree_sitter_python4 = __toESM(require("tree-sitter-python"), 1);
6975
+ var import_types21 = require("@neat.is/types");
6976
+ var SQLALCHEMY_IMPORT_RE = /(?:from|import)\s+(?:flask_sqlalchemy|sqlalchemy)\b/;
6977
+ var PARSE_CHUNK5 = 16384;
6978
+ function makePyParser4() {
6979
+ const p = new import_tree_sitter5.default();
6980
+ p.setLanguage(import_tree_sitter_python4.default);
6981
+ return p;
6982
+ }
6983
+ function parseSource5(parser, source) {
6984
+ return parser.parse(
6985
+ (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK5)
6986
+ );
6987
+ }
6988
+ function flaskSqlalchemyTableName(className) {
6989
+ return className.replace(/((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))/g, "_$1").toLowerCase().replace(/^_+/, "");
6990
+ }
6991
+ function namedChildren(node) {
6992
+ const out = [];
6993
+ for (let i = 0; i < node.namedChildCount; i++) {
6994
+ const c = node.namedChild(i);
6995
+ if (c) out.push(c);
6996
+ }
6997
+ return out;
6998
+ }
6999
+ function pyStaticStringText2(node) {
7000
+ if (node.type !== "string") return null;
7001
+ for (const child of namedChildren(node)) {
7002
+ if (child.type === "interpolation") return null;
7003
+ if (child.type === "string_content") return child.text;
7004
+ }
7005
+ return "";
7006
+ }
7007
+ function explicitTablename(body) {
7008
+ for (const stmt of namedChildren(body)) {
7009
+ if (stmt.type !== "expression_statement") continue;
7010
+ const assign = stmt.namedChild(0);
7011
+ if (assign?.type !== "assignment") continue;
7012
+ if (assign.childForFieldName("left")?.text !== "__tablename__") continue;
7013
+ const right = assign.childForFieldName("right");
7014
+ if (right?.type === "string") {
7015
+ const s = pyStaticStringText2(right);
7016
+ return s ? { name: s } : "computed";
7017
+ }
7018
+ return "computed";
7019
+ }
7020
+ return null;
7021
+ }
7022
+ function extendsFlaskModel(cls) {
7023
+ const supers = cls.childForFieldName("superclasses");
7024
+ if (!supers) return false;
7025
+ for (const a of namedChildren(supers)) {
7026
+ const t = a.text;
7027
+ if (t === "db.Model" || t === "Model" || t.endsWith(".Model")) return true;
7028
+ }
7029
+ return false;
7030
+ }
7031
+ function walk3(node, visit) {
7032
+ visit(node);
7033
+ for (const c of namedChildren(node)) walk3(c, visit);
7034
+ }
7035
+ function sqlalchemyEndpointsFromFile(file, serviceDir) {
7036
+ if (!SQLALCHEMY_IMPORT_RE.test(file.content)) return [];
7037
+ const tree = parseSource5(makePyParser4(), file.content);
7038
+ const out = [];
7039
+ const seen = /* @__PURE__ */ new Set();
7040
+ const push = (name, line) => {
7041
+ if (seen.has(name)) return;
7042
+ seen.add(name);
7043
+ out.push({
7044
+ infraId: (0, import_types21.infraId)("sql-table", name),
7045
+ name,
7046
+ kind: "sql-table",
7047
+ edgeType: "CALLS",
7048
+ confidenceKind: "verified-call-site",
7049
+ evidence: {
7050
+ file: import_node_path33.default.relative(serviceDir, file.path),
7051
+ line,
7052
+ snippet: snippet(file.content, line)
7053
+ }
7054
+ });
7055
+ };
7056
+ walk3(tree.rootNode, (node) => {
7057
+ if (node.type === "class_definition") {
7058
+ const body = node.childForFieldName("body");
7059
+ const nameNode = node.childForFieldName("name");
7060
+ if (!body || !nameNode) return;
7061
+ const line = node.startPosition.row + 1;
7062
+ const explicit = explicitTablename(body);
7063
+ if (explicit === "computed") return;
7064
+ if (explicit) {
7065
+ push(explicit.name, line);
7066
+ return;
7067
+ }
7068
+ if (extendsFlaskModel(node)) push(flaskSqlalchemyTableName(nameNode.text), line);
7069
+ return;
7070
+ }
7071
+ if (node.type === "call") {
7072
+ const fn = node.childForFieldName("function");
7073
+ const fnText = fn?.text;
7074
+ if (fnText !== "Table" && fnText !== "sa.Table" && fnText !== "sqlalchemy.Table") return;
7075
+ const first = node.childForFieldName("arguments")?.namedChild(0);
7076
+ if (first?.type === "string") {
7077
+ const s = pyStaticStringText2(first);
7078
+ if (s) push(s, node.startPosition.row + 1);
7079
+ }
7080
+ }
7081
+ });
7082
+ return out;
7083
+ }
7084
+
6824
7085
  // src/extract/calls/index.ts
6825
7086
  function edgeTypeFromEndpoint(ep) {
6826
7087
  switch (ep.edgeType) {
6827
7088
  case "PUBLISHES_TO":
6828
- return import_types21.EdgeType.PUBLISHES_TO;
7089
+ return import_types22.EdgeType.PUBLISHES_TO;
6829
7090
  case "CONSUMES_FROM":
6830
- return import_types21.EdgeType.CONSUMES_FROM;
7091
+ return import_types22.EdgeType.CONSUMES_FROM;
6831
7092
  default:
6832
- return import_types21.EdgeType.CALLS;
7093
+ return import_types22.EdgeType.CALLS;
6833
7094
  }
6834
7095
  }
6835
7096
  function isAwsKind(kind) {
@@ -6853,6 +7114,7 @@ async function addExternalEndpointEdges(graph, services) {
6853
7114
  endpoints.push(...grpcEndpointsFromFile(maskedFile, service.dir));
6854
7115
  endpoints.push(...supabaseEndpointsFromFile(maskedFile, service.dir));
6855
7116
  endpoints.push(...mongooseEndpointsFromFile(maskedFile, service.dir));
7117
+ endpoints.push(...sqlalchemyEndpointsFromFile(maskedFile, service.dir));
6856
7118
  }
6857
7119
  endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
6858
7120
  if (endpoints.length === 0) continue;
@@ -6861,7 +7123,7 @@ async function addExternalEndpointEdges(graph, services) {
6861
7123
  if (!graph.hasNode(ep.infraId)) {
6862
7124
  const node = {
6863
7125
  id: ep.infraId,
6864
- type: import_types21.NodeType.InfraNode,
7126
+ type: import_types22.NodeType.InfraNode,
6865
7127
  name: ep.name,
6866
7128
  // #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
6867
7129
  // aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
@@ -6873,7 +7135,7 @@ async function addExternalEndpointEdges(graph, services) {
6873
7135
  nodesAdded++;
6874
7136
  }
6875
7137
  const edgeType = edgeTypeFromEndpoint(ep);
6876
- const confidence = (0, import_types21.confidenceForExtracted)(ep.confidenceKind);
7138
+ const confidence = (0, import_types22.confidenceForExtracted)(ep.confidenceKind);
6877
7139
  const relFile = toPosix2(ep.evidence.file);
6878
7140
  const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
6879
7141
  graph,
@@ -6883,7 +7145,7 @@ async function addExternalEndpointEdges(graph, services) {
6883
7145
  );
6884
7146
  nodesAdded += n;
6885
7147
  edgesAdded += e;
6886
- if (!(0, import_types21.passesExtractedFloor)(confidence)) {
7148
+ if (!(0, import_types22.passesExtractedFloor)(confidence)) {
6887
7149
  noteExtractedDropped({
6888
7150
  source: fileNodeId,
6889
7151
  target: ep.infraId,
@@ -6903,7 +7165,7 @@ async function addExternalEndpointEdges(graph, services) {
6903
7165
  source: fileNodeId,
6904
7166
  target: ep.infraId,
6905
7167
  type: edgeType,
6906
- provenance: import_types21.Provenance.EXTRACTED,
7168
+ provenance: import_types22.Provenance.EXTRACTED,
6907
7169
  confidence,
6908
7170
  evidence: ep.evidence
6909
7171
  };
@@ -6929,16 +7191,16 @@ init_cjs_shims();
6929
7191
 
6930
7192
  // src/extract/infra/docker-compose.ts
6931
7193
  init_cjs_shims();
6932
- var import_node_path33 = __toESM(require("path"), 1);
6933
- var import_types23 = require("@neat.is/types");
7194
+ var import_node_path34 = __toESM(require("path"), 1);
7195
+ var import_types24 = require("@neat.is/types");
6934
7196
 
6935
7197
  // src/extract/infra/shared.ts
6936
7198
  init_cjs_shims();
6937
- var import_types22 = require("@neat.is/types");
7199
+ var import_types23 = require("@neat.is/types");
6938
7200
  function makeInfraNode(kind, name, provider = "self", extras) {
6939
7201
  return {
6940
- id: (0, import_types22.infraId)(kind, name),
6941
- type: import_types22.NodeType.InfraNode,
7202
+ id: (0, import_types23.infraId)(kind, name),
7203
+ type: import_types23.NodeType.InfraNode,
6942
7204
  name,
6943
7205
  provider,
6944
7206
  kind,
@@ -6982,8 +7244,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
6982
7244
  source: anchorId,
6983
7245
  target: node.id,
6984
7246
  type: edgeType,
6985
- provenance: import_types22.Provenance.EXTRACTED,
6986
- confidence: (0, import_types22.confidenceForExtracted)("structural"),
7247
+ provenance: import_types23.Provenance.EXTRACTED,
7248
+ confidence: (0, import_types23.confidenceForExtracted)("structural"),
6987
7249
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
6988
7250
  };
6989
7251
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -7000,7 +7262,7 @@ function dependsOnList(value) {
7000
7262
  }
7001
7263
  function serviceNameToServiceNode(name, services) {
7002
7264
  for (const s of services) {
7003
- if (s.node.name === name || import_node_path33.default.basename(s.dir) === name) return s.node.id;
7265
+ if (s.node.name === name || import_node_path34.default.basename(s.dir) === name) return s.node.id;
7004
7266
  }
7005
7267
  return null;
7006
7268
  }
@@ -7009,7 +7271,7 @@ async function addComposeInfra(graph, scanPath, services) {
7009
7271
  let edgesAdded = 0;
7010
7272
  let composePath = null;
7011
7273
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
7012
- const abs = import_node_path33.default.join(scanPath, name);
7274
+ const abs = import_node_path34.default.join(scanPath, name);
7013
7275
  if (await exists(abs)) {
7014
7276
  composePath = abs;
7015
7277
  break;
@@ -7022,13 +7284,13 @@ async function addComposeInfra(graph, scanPath, services) {
7022
7284
  } catch (err) {
7023
7285
  recordExtractionError(
7024
7286
  "infra docker-compose",
7025
- import_node_path33.default.relative(scanPath, composePath),
7287
+ import_node_path34.default.relative(scanPath, composePath),
7026
7288
  err
7027
7289
  );
7028
7290
  return { nodesAdded, edgesAdded };
7029
7291
  }
7030
7292
  if (!compose?.services) return { nodesAdded, edgesAdded };
7031
- const evidenceFile = import_node_path33.default.relative(scanPath, composePath).split(import_node_path33.default.sep).join("/");
7293
+ const evidenceFile = import_node_path34.default.relative(scanPath, composePath).split(import_node_path34.default.sep).join("/");
7032
7294
  const composeNameToNodeId = /* @__PURE__ */ new Map();
7033
7295
  for (const [composeName, svc] of Object.entries(compose.services)) {
7034
7296
  const matchedServiceId = serviceNameToServiceNode(composeName, services);
@@ -7050,15 +7312,15 @@ async function addComposeInfra(graph, scanPath, services) {
7050
7312
  for (const dep of dependsOnList(svc.depends_on)) {
7051
7313
  const targetId = composeNameToNodeId.get(dep);
7052
7314
  if (!targetId) continue;
7053
- const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId, import_types23.EdgeType.DEPENDS_ON);
7315
+ const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId, import_types24.EdgeType.DEPENDS_ON);
7054
7316
  if (graph.hasEdge(edgeId)) continue;
7055
7317
  const edge = {
7056
7318
  id: edgeId,
7057
7319
  source: sourceId,
7058
7320
  target: targetId,
7059
- type: import_types23.EdgeType.DEPENDS_ON,
7060
- provenance: import_types23.Provenance.EXTRACTED,
7061
- confidence: (0, import_types23.confidenceForExtracted)("structural"),
7321
+ type: import_types24.EdgeType.DEPENDS_ON,
7322
+ provenance: import_types24.Provenance.EXTRACTED,
7323
+ confidence: (0, import_types24.confidenceForExtracted)("structural"),
7062
7324
  evidence: { file: evidenceFile }
7063
7325
  };
7064
7326
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -7070,9 +7332,9 @@ async function addComposeInfra(graph, scanPath, services) {
7070
7332
 
7071
7333
  // src/extract/infra/dockerfile.ts
7072
7334
  init_cjs_shims();
7073
- var import_node_path34 = __toESM(require("path"), 1);
7335
+ var import_node_path35 = __toESM(require("path"), 1);
7074
7336
  var import_node_fs17 = require("fs");
7075
- var import_types24 = require("@neat.is/types");
7337
+ var import_types25 = require("@neat.is/types");
7076
7338
  function readDockerfile(content) {
7077
7339
  let image = null;
7078
7340
  const ports = [];
@@ -7101,7 +7363,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
7101
7363
  let nodesAdded = 0;
7102
7364
  let edgesAdded = 0;
7103
7365
  for (const service of services) {
7104
- const dockerfilePath = import_node_path34.default.join(service.dir, "Dockerfile");
7366
+ const dockerfilePath = import_node_path35.default.join(service.dir, "Dockerfile");
7105
7367
  if (!await exists(dockerfilePath)) continue;
7106
7368
  let content;
7107
7369
  try {
@@ -7109,7 +7371,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
7109
7371
  } catch (err) {
7110
7372
  recordExtractionError(
7111
7373
  "infra dockerfile",
7112
- import_node_path34.default.relative(scanPath, dockerfilePath),
7374
+ import_node_path35.default.relative(scanPath, dockerfilePath),
7113
7375
  err
7114
7376
  );
7115
7377
  continue;
@@ -7121,8 +7383,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
7121
7383
  graph.addNode(node.id, node);
7122
7384
  nodesAdded++;
7123
7385
  }
7124
- const relDockerfile = toPosix2(import_node_path34.default.relative(service.dir, dockerfilePath));
7125
- const evidenceFile = toPosix2(import_node_path34.default.relative(scanPath, dockerfilePath));
7386
+ const relDockerfile = toPosix2(import_node_path35.default.relative(service.dir, dockerfilePath));
7387
+ const evidenceFile = toPosix2(import_node_path35.default.relative(scanPath, dockerfilePath));
7126
7388
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
7127
7389
  graph,
7128
7390
  service.pkg.name,
@@ -7131,15 +7393,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
7131
7393
  );
7132
7394
  nodesAdded += fn;
7133
7395
  edgesAdded += fe;
7134
- const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id, import_types24.EdgeType.RUNS_ON);
7396
+ const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id, import_types25.EdgeType.RUNS_ON);
7135
7397
  if (!graph.hasEdge(edgeId)) {
7136
7398
  const edge = {
7137
7399
  id: edgeId,
7138
7400
  source: fileNodeId,
7139
7401
  target: node.id,
7140
- type: import_types24.EdgeType.RUNS_ON,
7141
- provenance: import_types24.Provenance.EXTRACTED,
7142
- confidence: (0, import_types24.confidenceForExtracted)("structural"),
7402
+ type: import_types25.EdgeType.RUNS_ON,
7403
+ provenance: import_types25.Provenance.EXTRACTED,
7404
+ confidence: (0, import_types25.confidenceForExtracted)("structural"),
7143
7405
  evidence: {
7144
7406
  file: evidenceFile,
7145
7407
  ...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
@@ -7154,15 +7416,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
7154
7416
  graph.addNode(portNode.id, portNode);
7155
7417
  nodesAdded++;
7156
7418
  }
7157
- const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id, import_types24.EdgeType.CONNECTS_TO);
7419
+ const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id, import_types25.EdgeType.CONNECTS_TO);
7158
7420
  if (graph.hasEdge(portEdgeId)) continue;
7159
7421
  const portEdge = {
7160
7422
  id: portEdgeId,
7161
7423
  source: fileNodeId,
7162
7424
  target: portNode.id,
7163
- type: import_types24.EdgeType.CONNECTS_TO,
7164
- provenance: import_types24.Provenance.EXTRACTED,
7165
- confidence: (0, import_types24.confidenceForExtracted)("structural"),
7425
+ type: import_types25.EdgeType.CONNECTS_TO,
7426
+ provenance: import_types25.Provenance.EXTRACTED,
7427
+ confidence: (0, import_types25.confidenceForExtracted)("structural"),
7166
7428
  evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
7167
7429
  };
7168
7430
  graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
@@ -7175,8 +7437,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
7175
7437
  // src/extract/infra/terraform.ts
7176
7438
  init_cjs_shims();
7177
7439
  var import_node_fs18 = require("fs");
7178
- var import_node_path35 = __toESM(require("path"), 1);
7179
- var import_types25 = require("@neat.is/types");
7440
+ var import_node_path36 = __toESM(require("path"), 1);
7441
+ var import_types26 = require("@neat.is/types");
7180
7442
  var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
7181
7443
  var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
7182
7444
  async function walkTfFiles(start, depth = 0, max = 5) {
@@ -7186,11 +7448,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
7186
7448
  for (const entry2 of entries) {
7187
7449
  if (entry2.isDirectory()) {
7188
7450
  if (IGNORED_DIRS.has(entry2.name) || entry2.name === ".terraform") continue;
7189
- const child = import_node_path35.default.join(start, entry2.name);
7451
+ const child = import_node_path36.default.join(start, entry2.name);
7190
7452
  if (await isPythonVenvDir(child)) continue;
7191
7453
  out.push(...await walkTfFiles(child, depth + 1, max));
7192
7454
  } else if (entry2.isFile() && entry2.name.endsWith(".tf")) {
7193
- out.push(import_node_path35.default.join(start, entry2.name));
7455
+ out.push(import_node_path36.default.join(start, entry2.name));
7194
7456
  }
7195
7457
  }
7196
7458
  return out;
@@ -7222,7 +7484,7 @@ async function addTerraformResources(graph, scanPath) {
7222
7484
  const files = await walkTfFiles(scanPath);
7223
7485
  for (const file of files) {
7224
7486
  const content = await import_node_fs18.promises.readFile(file, "utf8");
7225
- const evidenceFile = toPosix2(import_node_path35.default.relative(scanPath, file));
7487
+ const evidenceFile = toPosix2(import_node_path36.default.relative(scanPath, file));
7226
7488
  const resources = [];
7227
7489
  const byKey = /* @__PURE__ */ new Map();
7228
7490
  RESOURCE_RE.lastIndex = 0;
@@ -7257,16 +7519,16 @@ async function addTerraformResources(graph, scanPath) {
7257
7519
  if (!target) continue;
7258
7520
  if (seen.has(target.nodeId)) continue;
7259
7521
  seen.add(target.nodeId);
7260
- const edgeId = (0, import_types4.extractedEdgeId)(resource.nodeId, target.nodeId, import_types25.EdgeType.DEPENDS_ON);
7522
+ const edgeId = (0, import_types4.extractedEdgeId)(resource.nodeId, target.nodeId, import_types26.EdgeType.DEPENDS_ON);
7261
7523
  if (graph.hasEdge(edgeId)) continue;
7262
7524
  const line = lineAt2(content, resource.bodyOffset + ref.index);
7263
7525
  const edge = {
7264
7526
  id: edgeId,
7265
7527
  source: resource.nodeId,
7266
7528
  target: target.nodeId,
7267
- type: import_types25.EdgeType.DEPENDS_ON,
7268
- provenance: import_types25.Provenance.EXTRACTED,
7269
- confidence: (0, import_types25.confidenceForExtracted)("structural"),
7529
+ type: import_types26.EdgeType.DEPENDS_ON,
7530
+ provenance: import_types26.Provenance.EXTRACTED,
7531
+ confidence: (0, import_types26.confidenceForExtracted)("structural"),
7270
7532
  evidence: { file: evidenceFile, line, snippet: key }
7271
7533
  };
7272
7534
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -7280,7 +7542,7 @@ async function addTerraformResources(graph, scanPath) {
7280
7542
  // src/extract/infra/k8s.ts
7281
7543
  init_cjs_shims();
7282
7544
  var import_node_fs19 = require("fs");
7283
- var import_node_path36 = __toESM(require("path"), 1);
7545
+ var import_node_path37 = __toESM(require("path"), 1);
7284
7546
  var import_yaml3 = require("yaml");
7285
7547
  var K8S_KIND_TO_INFRA_KIND = {
7286
7548
  Service: "k8s-service",
@@ -7298,11 +7560,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
7298
7560
  for (const entry2 of entries) {
7299
7561
  if (entry2.isDirectory()) {
7300
7562
  if (IGNORED_DIRS.has(entry2.name)) continue;
7301
- const child = import_node_path36.default.join(start, entry2.name);
7563
+ const child = import_node_path37.default.join(start, entry2.name);
7302
7564
  if (await isPythonVenvDir(child)) continue;
7303
7565
  out.push(...await walkYamlFiles2(child, depth + 1, max));
7304
- } else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path36.default.extname(entry2.name))) {
7305
- out.push(import_node_path36.default.join(start, entry2.name));
7566
+ } else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path37.default.extname(entry2.name))) {
7567
+ out.push(import_node_path37.default.join(start, entry2.name));
7306
7568
  }
7307
7569
  }
7308
7570
  return out;
@@ -7336,13 +7598,13 @@ async function addK8sResources(graph, scanPath) {
7336
7598
  // src/extract/infra/cloudflare.ts
7337
7599
  init_cjs_shims();
7338
7600
  var import_node_fs20 = require("fs");
7339
- var import_node_path37 = __toESM(require("path"), 1);
7601
+ var import_node_path38 = __toESM(require("path"), 1);
7340
7602
  var import_smol_toml2 = require("smol-toml");
7341
- var import_types26 = require("@neat.is/types");
7603
+ var import_types27 = require("@neat.is/types");
7342
7604
  var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
7343
7605
  async function readWranglerConfig(dir) {
7344
7606
  for (const filename of WRANGLER_FILENAMES) {
7345
- const abs = import_node_path37.default.join(dir, filename);
7607
+ const abs = import_node_path38.default.join(dir, filename);
7346
7608
  if (!await exists(abs)) continue;
7347
7609
  const raw = await import_node_fs20.promises.readFile(abs, "utf8");
7348
7610
  const config = filename === "wrangler.toml" ? (0, import_smol_toml2.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -7386,8 +7648,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
7386
7648
  source: anchorId,
7387
7649
  target: node.id,
7388
7650
  type: edgeType,
7389
- provenance: import_types26.Provenance.EXTRACTED,
7390
- confidence: (0, import_types26.confidenceForExtracted)("structural"),
7651
+ provenance: import_types27.Provenance.EXTRACTED,
7652
+ confidence: (0, import_types27.confidenceForExtracted)("structural"),
7391
7653
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
7392
7654
  };
7393
7655
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -7405,11 +7667,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7405
7667
  try {
7406
7668
  read = await readWranglerConfig(service.dir);
7407
7669
  } catch (err) {
7408
- recordExtractionError("infra cloudflare", import_node_path37.default.relative(scanPath, service.dir), err);
7670
+ recordExtractionError("infra cloudflare", import_node_path38.default.relative(scanPath, service.dir), err);
7409
7671
  continue;
7410
7672
  }
7411
7673
  if (!read || !read.config.name) continue;
7412
- const evidenceFile = toPosix2(import_node_path37.default.relative(scanPath, import_node_path37.default.join(service.dir, read.relFile)));
7674
+ const evidenceFile = toPosix2(import_node_path38.default.relative(scanPath, import_node_path38.default.join(service.dir, read.relFile)));
7413
7675
  discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
7414
7676
  }
7415
7677
  for (const worker of discovered) {
@@ -7421,7 +7683,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7421
7683
  }
7422
7684
  let anchorId = service.node.id;
7423
7685
  if (config.main) {
7424
- const entryRelPath = toPosix2(import_node_path37.default.normalize(config.main));
7686
+ const entryRelPath = toPosix2(import_node_path38.default.normalize(config.main));
7425
7687
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
7426
7688
  graph,
7427
7689
  service.pkg.name,
@@ -7448,15 +7710,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7448
7710
  nodesAdded++;
7449
7711
  }
7450
7712
  if (runtimeNode.id !== anchorId) {
7451
- const runsOnId = (0, import_types4.extractedEdgeId)(anchorId, runtimeNode.id, import_types26.EdgeType.RUNS_ON);
7713
+ const runsOnId = (0, import_types4.extractedEdgeId)(anchorId, runtimeNode.id, import_types27.EdgeType.RUNS_ON);
7452
7714
  if (!graph.hasEdge(runsOnId)) {
7453
7715
  const edge = {
7454
7716
  id: runsOnId,
7455
7717
  source: anchorId,
7456
7718
  target: runtimeNode.id,
7457
- type: import_types26.EdgeType.RUNS_ON,
7458
- provenance: import_types26.Provenance.EXTRACTED,
7459
- confidence: (0, import_types26.confidenceForExtracted)("structural"),
7719
+ type: import_types27.EdgeType.RUNS_ON,
7720
+ provenance: import_types27.Provenance.EXTRACTED,
7721
+ confidence: (0, import_types27.confidenceForExtracted)("structural"),
7460
7722
  evidence: {
7461
7723
  file: evidenceFile,
7462
7724
  ...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
@@ -7470,7 +7732,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7470
7732
  const result = addResourceEdge(
7471
7733
  graph,
7472
7734
  anchorId,
7473
- import_types26.EdgeType.CONNECTS_TO,
7735
+ import_types27.EdgeType.CONNECTS_TO,
7474
7736
  "cloudflare-route",
7475
7737
  route,
7476
7738
  evidenceFile,
@@ -7494,7 +7756,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7494
7756
  const result = addResourceEdge(
7495
7757
  graph,
7496
7758
  anchorId,
7497
- import_types26.EdgeType.DEPENDS_ON,
7759
+ import_types27.EdgeType.DEPENDS_ON,
7498
7760
  group.kind,
7499
7761
  name,
7500
7762
  evidenceFile,
@@ -7508,7 +7770,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7508
7770
  const result = addResourceEdge(
7509
7771
  graph,
7510
7772
  anchorId,
7511
- import_types26.EdgeType.DEPENDS_ON,
7773
+ import_types27.EdgeType.DEPENDS_ON,
7512
7774
  "cloudflare-cron",
7513
7775
  cron,
7514
7776
  evidenceFile,
@@ -7521,7 +7783,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7521
7783
  const result = addResourceEdge(
7522
7784
  graph,
7523
7785
  anchorId,
7524
- import_types26.EdgeType.DEPENDS_ON,
7786
+ import_types27.EdgeType.DEPENDS_ON,
7525
7787
  "cloudflare-env-var",
7526
7788
  varName,
7527
7789
  evidenceFile,
@@ -7534,15 +7796,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7534
7796
  if (!svc.service) continue;
7535
7797
  const target = workerIndex.get(svc.service);
7536
7798
  if (target && target.anchorId !== anchorId) {
7537
- const edgeId = (0, import_types4.extractedEdgeId)(anchorId, target.anchorId, import_types26.EdgeType.CALLS);
7799
+ const edgeId = (0, import_types4.extractedEdgeId)(anchorId, target.anchorId, import_types27.EdgeType.CALLS);
7538
7800
  if (!graph.hasEdge(edgeId)) {
7539
7801
  const edge = {
7540
7802
  id: edgeId,
7541
7803
  source: anchorId,
7542
7804
  target: target.anchorId,
7543
- type: import_types26.EdgeType.CALLS,
7544
- provenance: import_types26.Provenance.EXTRACTED,
7545
- confidence: (0, import_types26.confidenceForExtracted)("structural"),
7805
+ type: import_types27.EdgeType.CALLS,
7806
+ provenance: import_types27.Provenance.EXTRACTED,
7807
+ confidence: (0, import_types27.confidenceForExtracted)("structural"),
7546
7808
  evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
7547
7809
  };
7548
7810
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -7553,7 +7815,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7553
7815
  const result = addResourceEdge(
7554
7816
  graph,
7555
7817
  anchorId,
7556
- import_types26.EdgeType.DEPENDS_ON,
7818
+ import_types27.EdgeType.DEPENDS_ON,
7557
7819
  "cloudflare-service-binding",
7558
7820
  svc.service,
7559
7821
  evidenceFile,
@@ -7569,12 +7831,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7569
7831
  // src/extract/infra/vercel.ts
7570
7832
  init_cjs_shims();
7571
7833
  var import_node_fs21 = require("fs");
7572
- var import_node_path38 = __toESM(require("path"), 1);
7573
- var import_types27 = require("@neat.is/types");
7834
+ var import_node_path39 = __toESM(require("path"), 1);
7835
+ var import_types28 = require("@neat.is/types");
7574
7836
  var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
7575
7837
  async function readVercelConfig(dir) {
7576
7838
  for (const filename of VERCEL_CONFIG_FILENAMES) {
7577
- const abs = import_node_path38.default.join(dir, filename);
7839
+ const abs = import_node_path39.default.join(dir, filename);
7578
7840
  if (!await exists(abs)) continue;
7579
7841
  const raw = await import_node_fs21.promises.readFile(abs, "utf8");
7580
7842
  const config = JSON.parse(maskCommentsInSource(raw));
@@ -7583,7 +7845,7 @@ async function readVercelConfig(dir) {
7583
7845
  return null;
7584
7846
  }
7585
7847
  async function readLinkedProjectName(dir) {
7586
- const abs = import_node_path38.default.join(dir, ".vercel", "project.json");
7848
+ const abs = import_node_path39.default.join(dir, ".vercel", "project.json");
7587
7849
  if (!await exists(abs)) return void 0;
7588
7850
  const parsed = JSON.parse(await import_node_fs21.promises.readFile(abs, "utf8"));
7589
7851
  return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
@@ -7601,7 +7863,7 @@ async function addVercelServices(graph, services, scanPath) {
7601
7863
  read = await readVercelConfig(service.dir);
7602
7864
  projectName = await readLinkedProjectName(service.dir);
7603
7865
  } catch (err) {
7604
- recordExtractionError("infra vercel", import_node_path38.default.relative(scanPath, service.dir), err);
7866
+ recordExtractionError("infra vercel", import_node_path39.default.relative(scanPath, service.dir), err);
7605
7867
  continue;
7606
7868
  }
7607
7869
  if (!read && !projectName) continue;
@@ -7617,7 +7879,7 @@ async function addVercelServices(graph, services, scanPath) {
7617
7879
  const anchorId = service.node.id;
7618
7880
  if (!read) continue;
7619
7881
  const { config, relFile, raw } = read;
7620
- const evidenceFile = toPosix2(import_node_path38.default.relative(scanPath, import_node_path38.default.join(service.dir, relFile)));
7882
+ const evidenceFile = toPosix2(import_node_path39.default.relative(scanPath, import_node_path39.default.join(service.dir, relFile)));
7621
7883
  const add = (edgeType, kind, name) => {
7622
7884
  if (!name) return;
7623
7885
  const result = emitPlatformResourceEdge(
@@ -7633,12 +7895,12 @@ async function addVercelServices(graph, services, scanPath) {
7633
7895
  nodesAdded += result.nodesAdded;
7634
7896
  edgesAdded += result.edgesAdded;
7635
7897
  };
7636
- add(import_types27.EdgeType.RUNS_ON, "vercel", "vercel");
7637
- for (const cron of config.crons ?? []) add(import_types27.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
7638
- for (const varName of Object.keys(config.env ?? {})) add(import_types27.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
7639
- for (const varName of Object.keys(config.build?.env ?? {})) add(import_types27.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
7898
+ add(import_types28.EdgeType.RUNS_ON, "vercel", "vercel");
7899
+ for (const cron of config.crons ?? []) add(import_types28.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
7900
+ for (const varName of Object.keys(config.env ?? {})) add(import_types28.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
7901
+ for (const varName of Object.keys(config.build?.env ?? {})) add(import_types28.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
7640
7902
  for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
7641
- add(import_types27.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
7903
+ add(import_types28.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
7642
7904
  }
7643
7905
  }
7644
7906
  return { nodesAdded, edgesAdded };
@@ -7647,13 +7909,13 @@ async function addVercelServices(graph, services, scanPath) {
7647
7909
  // src/extract/infra/railway.ts
7648
7910
  init_cjs_shims();
7649
7911
  var import_node_fs22 = require("fs");
7650
- var import_node_path39 = __toESM(require("path"), 1);
7912
+ var import_node_path40 = __toESM(require("path"), 1);
7651
7913
  var import_smol_toml3 = require("smol-toml");
7652
- var import_types28 = require("@neat.is/types");
7914
+ var import_types29 = require("@neat.is/types");
7653
7915
  var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
7654
7916
  async function readRailwayConfig(dir) {
7655
7917
  for (const filename of RAILWAY_FILENAMES) {
7656
- const abs = import_node_path39.default.join(dir, filename);
7918
+ const abs = import_node_path40.default.join(dir, filename);
7657
7919
  if (!await exists(abs)) continue;
7658
7920
  const raw = await import_node_fs22.promises.readFile(abs, "utf8");
7659
7921
  const config = filename === "railway.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -7669,7 +7931,7 @@ async function addRailwayServices(graph, services, scanPath) {
7669
7931
  try {
7670
7932
  read = await readRailwayConfig(service.dir);
7671
7933
  } catch (err) {
7672
- recordExtractionError("infra railway", import_node_path39.default.relative(scanPath, service.dir), err);
7934
+ recordExtractionError("infra railway", import_node_path40.default.relative(scanPath, service.dir), err);
7673
7935
  continue;
7674
7936
  }
7675
7937
  if (!read) continue;
@@ -7679,7 +7941,7 @@ async function addRailwayServices(graph, services, scanPath) {
7679
7941
  }
7680
7942
  const anchorId = service.node.id;
7681
7943
  const { config, relFile, raw } = read;
7682
- const evidenceFile = toPosix2(import_node_path39.default.relative(scanPath, import_node_path39.default.join(service.dir, relFile)));
7944
+ const evidenceFile = toPosix2(import_node_path40.default.relative(scanPath, import_node_path40.default.join(service.dir, relFile)));
7683
7945
  const add = (edgeType, kind, name) => {
7684
7946
  if (!name) return;
7685
7947
  const result = emitPlatformResourceEdge(
@@ -7695,9 +7957,9 @@ async function addRailwayServices(graph, services, scanPath) {
7695
7957
  nodesAdded += result.nodesAdded;
7696
7958
  edgesAdded += result.edgesAdded;
7697
7959
  };
7698
- add(import_types28.EdgeType.RUNS_ON, "railway", "railway");
7699
- add(import_types28.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
7700
- add(import_types28.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
7960
+ add(import_types29.EdgeType.RUNS_ON, "railway", "railway");
7961
+ add(import_types29.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
7962
+ add(import_types29.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
7701
7963
  }
7702
7964
  return { nodesAdded, edgesAdded };
7703
7965
  }
@@ -7705,12 +7967,12 @@ async function addRailwayServices(graph, services, scanPath) {
7705
7967
  // src/extract/infra/supabase.ts
7706
7968
  init_cjs_shims();
7707
7969
  var import_node_fs23 = require("fs");
7708
- var import_node_path40 = __toESM(require("path"), 1);
7970
+ var import_node_path41 = __toESM(require("path"), 1);
7709
7971
  var import_smol_toml4 = require("smol-toml");
7710
- var import_types29 = require("@neat.is/types");
7972
+ var import_types30 = require("@neat.is/types");
7711
7973
  async function readSupabaseConfig(dir) {
7712
- const relFile = import_node_path40.default.join("supabase", "config.toml");
7713
- const abs = import_node_path40.default.join(dir, relFile);
7974
+ const relFile = import_node_path41.default.join("supabase", "config.toml");
7975
+ const abs = import_node_path41.default.join(dir, relFile);
7714
7976
  if (!await exists(abs)) return null;
7715
7977
  const raw = await import_node_fs23.promises.readFile(abs, "utf8");
7716
7978
  const config = (0, import_smol_toml4.parse)(raw);
@@ -7724,7 +7986,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
7724
7986
  try {
7725
7987
  read = await readSupabaseConfig(service.dir);
7726
7988
  } catch (err) {
7727
- recordExtractionError("infra supabase", import_node_path40.default.relative(scanPath, service.dir), err);
7989
+ recordExtractionError("infra supabase", import_node_path41.default.relative(scanPath, service.dir), err);
7728
7990
  continue;
7729
7991
  }
7730
7992
  if (!read) continue;
@@ -7739,7 +8001,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
7739
8001
  });
7740
8002
  }
7741
8003
  const anchorId = service.node.id;
7742
- const evidenceFile = toPosix2(import_node_path40.default.relative(scanPath, import_node_path40.default.join(service.dir, relFile)));
8004
+ const evidenceFile = toPosix2(import_node_path41.default.relative(scanPath, import_node_path41.default.join(service.dir, relFile)));
7743
8005
  const add = (edgeType, kind, name) => {
7744
8006
  if (!name) return;
7745
8007
  const result = emitPlatformResourceEdge(
@@ -7755,10 +8017,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
7755
8017
  nodesAdded += result.nodesAdded;
7756
8018
  edgesAdded += result.edgesAdded;
7757
8019
  };
7758
- add(import_types29.EdgeType.RUNS_ON, "supabase", "supabase");
7759
- for (const fn of Object.keys(config.functions ?? {})) add(import_types29.EdgeType.DEPENDS_ON, "supabase-function", fn);
7760
- if (config.storage) add(import_types29.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
7761
- if (config.auth) add(import_types29.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
8020
+ add(import_types30.EdgeType.RUNS_ON, "supabase", "supabase");
8021
+ for (const fn of Object.keys(config.functions ?? {})) add(import_types30.EdgeType.DEPENDS_ON, "supabase-function", fn);
8022
+ if (config.storage) add(import_types30.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
8023
+ if (config.auth) add(import_types30.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
7762
8024
  }
7763
8025
  return { nodesAdded, edgesAdded };
7764
8026
  }
@@ -7780,17 +8042,17 @@ async function addInfra(graph, scanPath, services) {
7780
8042
  }
7781
8043
 
7782
8044
  // src/extract/index.ts
7783
- var import_node_path42 = __toESM(require("path"), 1);
8045
+ var import_node_path43 = __toESM(require("path"), 1);
7784
8046
 
7785
8047
  // src/extract/retire.ts
7786
8048
  init_cjs_shims();
7787
8049
  var import_node_fs24 = require("fs");
7788
- var import_node_path41 = __toESM(require("path"), 1);
7789
- var import_types30 = require("@neat.is/types");
8050
+ var import_node_path42 = __toESM(require("path"), 1);
8051
+ var import_types31 = require("@neat.is/types");
7790
8052
  function dropOrphanedFileNodes(graph) {
7791
8053
  const orphans = [];
7792
8054
  graph.forEachNode((id, attrs) => {
7793
- if (attrs.type !== import_types30.NodeType.FileNode) return;
8055
+ if (attrs.type !== import_types31.NodeType.FileNode) return;
7794
8056
  if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
7795
8057
  orphans.push(id);
7796
8058
  }
@@ -7803,7 +8065,7 @@ function retireEdgesByFile(graph, file) {
7803
8065
  const toDrop = [];
7804
8066
  graph.forEachEdge((id, attrs) => {
7805
8067
  const edge = attrs;
7806
- if (edge.provenance !== import_types30.Provenance.EXTRACTED) return;
8068
+ if (edge.provenance !== import_types31.Provenance.EXTRACTED) return;
7807
8069
  if (!edge.evidence?.file) return;
7808
8070
  if (edge.evidence.file === normalized) toDrop.push(id);
7809
8071
  });
@@ -7816,14 +8078,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
7816
8078
  const bases = [scanPath, ...serviceDirs];
7817
8079
  graph.forEachEdge((id, attrs) => {
7818
8080
  const edge = attrs;
7819
- if (edge.provenance !== import_types30.Provenance.EXTRACTED) return;
8081
+ if (edge.provenance !== import_types31.Provenance.EXTRACTED) return;
7820
8082
  const evidenceFile = edge.evidence?.file;
7821
8083
  if (!evidenceFile) return;
7822
- if (import_node_path41.default.isAbsolute(evidenceFile)) {
8084
+ if (import_node_path42.default.isAbsolute(evidenceFile)) {
7823
8085
  if (!(0, import_node_fs24.existsSync)(evidenceFile)) toDrop.push(id);
7824
8086
  return;
7825
8087
  }
7826
- const found = bases.some((base) => (0, import_node_fs24.existsSync)(import_node_path41.default.join(base, evidenceFile)));
8088
+ const found = bases.some((base) => (0, import_node_fs24.existsSync)(import_node_path42.default.join(base, evidenceFile)));
7827
8089
  if (!found) toDrop.push(id);
7828
8090
  });
7829
8091
  for (const id of toDrop) graph.dropEdge(id);
@@ -7865,7 +8127,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
7865
8127
  }
7866
8128
  const droppedEntries = drainDroppedExtracted();
7867
8129
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
7868
- const rejectedPath = import_node_path42.default.join(import_node_path42.default.dirname(opts.errorsPath), "rejected.ndjson");
8130
+ const rejectedPath = import_node_path43.default.join(import_node_path43.default.dirname(opts.errorsPath), "rejected.ndjson");
7869
8131
  try {
7870
8132
  await writeRejectedExtracted(droppedEntries, rejectedPath);
7871
8133
  } catch (err) {
@@ -7899,39 +8161,39 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
7899
8161
 
7900
8162
  // src/divergences.ts
7901
8163
  init_cjs_shims();
7902
- var import_types31 = require("@neat.is/types");
8164
+ var import_types32 = require("@neat.is/types");
7903
8165
  function bucketKey(source, target, type) {
7904
8166
  return `${type}|${source}|${target}`;
7905
8167
  }
7906
8168
  function bucketSourceFor(graph, edge) {
7907
- if (edge.type !== import_types31.EdgeType.CONNECTS_TO) return edge.source;
7908
- const parsed = (0, import_types31.parseFileId)(edge.source);
8169
+ if (edge.type !== import_types32.EdgeType.CONNECTS_TO) return edge.source;
8170
+ const parsed = (0, import_types32.parseFileId)(edge.source);
7909
8171
  if (!parsed || !graph.hasNode(edge.target)) return edge.source;
7910
8172
  const target = graph.getNodeAttributes(edge.target);
7911
- if (target.type !== import_types31.NodeType.DatabaseNode) return edge.source;
7912
- return (0, import_types31.serviceId)(parsed.service);
8173
+ if (target.type !== import_types32.NodeType.DatabaseNode) return edge.source;
8174
+ return (0, import_types32.serviceId)(parsed.service);
7913
8175
  }
7914
8176
  function bucketEdges(graph) {
7915
8177
  const buckets2 = /* @__PURE__ */ new Map();
7916
8178
  graph.forEachEdge((id, attrs) => {
7917
8179
  const e = attrs;
7918
- const parsed = (0, import_types31.parseEdgeId)(id);
8180
+ const parsed = (0, import_types32.parseEdgeId)(id);
7919
8181
  const provenance = parsed?.provenance ?? e.provenance;
7920
8182
  const source = bucketSourceFor(graph, e);
7921
8183
  const key = bucketKey(source, e.target, e.type);
7922
8184
  const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
7923
8185
  switch (provenance) {
7924
- case import_types31.Provenance.EXTRACTED:
8186
+ case import_types32.Provenance.EXTRACTED:
7925
8187
  cur.extracted = e;
7926
8188
  break;
7927
- case import_types31.Provenance.OBSERVED:
8189
+ case import_types32.Provenance.OBSERVED:
7928
8190
  cur.observed = e;
7929
8191
  break;
7930
- case import_types31.Provenance.INFERRED:
8192
+ case import_types32.Provenance.INFERRED:
7931
8193
  cur.inferred = e;
7932
8194
  break;
7933
8195
  default:
7934
- if (e.provenance === import_types31.Provenance.STALE) cur.stale = e;
8196
+ if (e.provenance === import_types32.Provenance.STALE) cur.stale = e;
7935
8197
  }
7936
8198
  buckets2.set(key, cur);
7937
8199
  });
@@ -7940,12 +8202,12 @@ function bucketEdges(graph) {
7940
8202
  function nodeIsFrontier(graph, nodeId) {
7941
8203
  if (!graph.hasNode(nodeId)) return false;
7942
8204
  const attrs = graph.getNodeAttributes(nodeId);
7943
- return attrs.type === import_types31.NodeType.FrontierNode;
8205
+ return attrs.type === import_types32.NodeType.FrontierNode;
7944
8206
  }
7945
8207
  function nodeIsWebsocketChannel(graph, nodeId) {
7946
8208
  if (!graph.hasNode(nodeId)) return false;
7947
8209
  const attrs = graph.getNodeAttributes(nodeId);
7948
- return attrs.type === import_types31.NodeType.WebSocketChannelNode;
8210
+ return attrs.type === import_types32.NodeType.WebSocketChannelNode;
7949
8211
  }
7950
8212
  function clampConfidence(n) {
7951
8213
  if (!Number.isFinite(n)) return 0;
@@ -7965,14 +8227,14 @@ function gradedConfidence(edge) {
7965
8227
  return clampConfidence(confidenceForEdge(edge));
7966
8228
  }
7967
8229
  var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
7968
- import_types31.EdgeType.CALLS,
7969
- import_types31.EdgeType.CONNECTS_TO,
7970
- import_types31.EdgeType.PUBLISHES_TO,
7971
- import_types31.EdgeType.CONSUMES_FROM
8230
+ import_types32.EdgeType.CALLS,
8231
+ import_types32.EdgeType.CONNECTS_TO,
8232
+ import_types32.EdgeType.PUBLISHES_TO,
8233
+ import_types32.EdgeType.CONSUMES_FROM
7972
8234
  ]);
7973
8235
  function detectMissingDivergences(graph, bucket) {
7974
8236
  const out = [];
7975
- if (bucket.type === import_types31.EdgeType.CONTAINS) return out;
8237
+ if (bucket.type === import_types32.EdgeType.CONTAINS) return out;
7976
8238
  if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
7977
8239
  if (!nodeIsFrontier(graph, bucket.target)) {
7978
8240
  out.push({
@@ -8013,7 +8275,7 @@ function declaredHostFor(svc) {
8013
8275
  function hasExtractedConfiguredBy(graph, svcId) {
8014
8276
  for (const edgeId of graph.outboundEdges(svcId)) {
8015
8277
  const e = graph.getEdgeAttributes(edgeId);
8016
- if (e.type === import_types31.EdgeType.CONFIGURED_BY && e.provenance === import_types31.Provenance.EXTRACTED) {
8278
+ if (e.type === import_types32.EdgeType.CONFIGURED_BY && e.provenance === import_types32.Provenance.EXTRACTED) {
8017
8279
  return true;
8018
8280
  }
8019
8281
  }
@@ -8026,10 +8288,10 @@ function detectHostMismatch(graph, svcId, svc) {
8026
8288
  const out = [];
8027
8289
  for (const edgeId of graph.outboundEdges(svcId)) {
8028
8290
  const edge = graph.getEdgeAttributes(edgeId);
8029
- if (edge.type !== import_types31.EdgeType.CONNECTS_TO) continue;
8030
- if (edge.provenance !== import_types31.Provenance.OBSERVED) continue;
8291
+ if (edge.type !== import_types32.EdgeType.CONNECTS_TO) continue;
8292
+ if (edge.provenance !== import_types32.Provenance.OBSERVED) continue;
8031
8293
  const target = graph.getNodeAttributes(edge.target);
8032
- if (target.type !== import_types31.NodeType.DatabaseNode) continue;
8294
+ if (target.type !== import_types32.NodeType.DatabaseNode) continue;
8033
8295
  const observedHost = target.host?.trim();
8034
8296
  if (!observedHost) continue;
8035
8297
  if (observedHost === declaredHost) continue;
@@ -8051,10 +8313,10 @@ function detectCompatDivergences(graph, svcId, svc) {
8051
8313
  const deps = svc.dependencies ?? {};
8052
8314
  for (const edgeId of graph.outboundEdges(svcId)) {
8053
8315
  const edge = graph.getEdgeAttributes(edgeId);
8054
- if (edge.type !== import_types31.EdgeType.CONNECTS_TO) continue;
8055
- if (edge.provenance !== import_types31.Provenance.OBSERVED) continue;
8316
+ if (edge.type !== import_types32.EdgeType.CONNECTS_TO) continue;
8317
+ if (edge.provenance !== import_types32.Provenance.OBSERVED) continue;
8056
8318
  const target = graph.getNodeAttributes(edge.target);
8057
- if (target.type !== import_types31.NodeType.DatabaseNode) continue;
8319
+ if (target.type !== import_types32.NodeType.DatabaseNode) continue;
8058
8320
  for (const pair of compatPairs()) {
8059
8321
  if (pair.engine !== target.engine) continue;
8060
8322
  const declared = deps[pair.driver];
@@ -8113,7 +8375,7 @@ function suppressHostMismatchHalves(all) {
8113
8375
  for (const d of all) {
8114
8376
  if (d.type !== "host-mismatch") continue;
8115
8377
  observedHalf.add(`${d.source}->${d.target}`);
8116
- declaredHalf.add((0, import_types31.databaseId)(d.extractedHost));
8378
+ declaredHalf.add((0, import_types32.databaseId)(d.extractedHost));
8117
8379
  }
8118
8380
  if (observedHalf.size === 0) return all;
8119
8381
  return all.filter((d) => {
@@ -8132,7 +8394,7 @@ function computeDivergences(graph, opts = {}) {
8132
8394
  }
8133
8395
  graph.forEachNode((nodeId, attrs) => {
8134
8396
  const n = attrs;
8135
- if (n.type !== import_types31.NodeType.ServiceNode) return;
8397
+ if (n.type !== import_types32.NodeType.ServiceNode) return;
8136
8398
  const svc = n;
8137
8399
  for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
8138
8400
  for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
@@ -8166,7 +8428,7 @@ function computeDivergences(graph, opts = {}) {
8166
8428
  if (a.source !== b.source) return a.source.localeCompare(b.source);
8167
8429
  return a.target.localeCompare(b.target);
8168
8430
  });
8169
- return import_types31.DivergenceResultSchema.parse({
8431
+ return import_types32.DivergenceResultSchema.parse({
8170
8432
  divergences: filtered,
8171
8433
  totalAffected: filtered.length,
8172
8434
  computedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -8176,8 +8438,8 @@ function computeDivergences(graph, opts = {}) {
8176
8438
  // src/persist.ts
8177
8439
  init_cjs_shims();
8178
8440
  var import_node_fs25 = require("fs");
8179
- var import_node_path43 = __toESM(require("path"), 1);
8180
- var import_types32 = require("@neat.is/types");
8441
+ var import_node_path44 = __toESM(require("path"), 1);
8442
+ var import_types33 = require("@neat.is/types");
8181
8443
  var SCHEMA_VERSION = 4;
8182
8444
  function migrateV1ToV2(payload) {
8183
8445
  const nodes = payload.graph.nodes;
@@ -8199,12 +8461,12 @@ function migrateV2ToV3(payload) {
8199
8461
  for (const edge of edges) {
8200
8462
  const attrs = edge.attributes;
8201
8463
  if (!attrs || attrs.provenance !== "FRONTIER") continue;
8202
- attrs.provenance = import_types32.Provenance.OBSERVED;
8464
+ attrs.provenance = import_types33.Provenance.OBSERVED;
8203
8465
  const type = typeof attrs.type === "string" ? attrs.type : void 0;
8204
8466
  const source = typeof attrs.source === "string" ? attrs.source : void 0;
8205
8467
  const target = typeof attrs.target === "string" ? attrs.target : void 0;
8206
8468
  if (type && source && target) {
8207
- const newId = (0, import_types32.observedEdgeId)(source, target, type);
8469
+ const newId = (0, import_types33.observedEdgeId)(source, target, type);
8208
8470
  attrs.id = newId;
8209
8471
  if (edge.key) edge.key = newId;
8210
8472
  }
@@ -8213,7 +8475,7 @@ function migrateV2ToV3(payload) {
8213
8475
  return { ...payload, schemaVersion: 3 };
8214
8476
  }
8215
8477
  async function ensureDir(filePath) {
8216
- await import_node_fs25.promises.mkdir(import_node_path43.default.dirname(filePath), { recursive: true });
8478
+ await import_node_fs25.promises.mkdir(import_node_path44.default.dirname(filePath), { recursive: true });
8217
8479
  }
8218
8480
  async function saveGraphToDisk(graph, outPath) {
8219
8481
  await ensureDir(outPath);
@@ -8295,7 +8557,7 @@ function startPersistLoop(graph, outPath, opts = {}) {
8295
8557
  // src/gitignore.ts
8296
8558
  init_cjs_shims();
8297
8559
  var import_node_fs26 = require("fs");
8298
- var import_node_path44 = __toESM(require("path"), 1);
8560
+ var import_node_path45 = __toESM(require("path"), 1);
8299
8561
  var NEAT_OUT_LINE = "neat-out/";
8300
8562
  var NEAT_HEADER = "# NEAT \u2014 machine-local snapshots and events";
8301
8563
  function isNeatOutLine(line) {
@@ -8303,7 +8565,7 @@ function isNeatOutLine(line) {
8303
8565
  return trimmed === "neat-out/" || trimmed === "neat-out";
8304
8566
  }
8305
8567
  async function ensureNeatOutIgnored(projectDir) {
8306
- const file = import_node_path44.default.join(projectDir, ".gitignore");
8568
+ const file = import_node_path45.default.join(projectDir, ".gitignore");
8307
8569
  let existing = null;
8308
8570
  try {
8309
8571
  existing = await import_node_fs26.promises.readFile(file, "utf8");
@@ -8330,7 +8592,7 @@ ${NEAT_OUT_LINE}
8330
8592
 
8331
8593
  // src/summary.ts
8332
8594
  init_cjs_shims();
8333
- var import_types33 = require("@neat.is/types");
8595
+ var import_types34 = require("@neat.is/types");
8334
8596
  function renderOtelEnvBlock() {
8335
8597
  return [
8336
8598
  "for prod OTel routing, set these in your deploy platform's env:",
@@ -8340,19 +8602,19 @@ function renderOtelEnvBlock() {
8340
8602
  }
8341
8603
  function findIncompatServices(nodes) {
8342
8604
  return nodes.filter(
8343
- (n) => n.type === import_types33.NodeType.ServiceNode && Array.isArray(n.incompatibilities) && (n.incompatibilities ?? []).length > 0
8605
+ (n) => n.type === import_types34.NodeType.ServiceNode && Array.isArray(n.incompatibilities) && (n.incompatibilities ?? []).length > 0
8344
8606
  );
8345
8607
  }
8346
8608
  function servicesWithoutObserved(nodes, edges) {
8347
8609
  const seen = /* @__PURE__ */ new Set();
8348
8610
  for (const e of edges) {
8349
- if (e.provenance === import_types33.Provenance.OBSERVED) {
8611
+ if (e.provenance === import_types34.Provenance.OBSERVED) {
8350
8612
  seen.add(e.source);
8351
8613
  seen.add(e.target);
8352
8614
  }
8353
8615
  }
8354
8616
  return nodes.filter(
8355
- (n) => n.type === import_types33.NodeType.ServiceNode && !seen.has(n.id)
8617
+ (n) => n.type === import_types34.NodeType.ServiceNode && !seen.has(n.id)
8356
8618
  );
8357
8619
  }
8358
8620
  function formatDivergence(d) {
@@ -8427,26 +8689,26 @@ function formatIncompat(inc) {
8427
8689
  // src/watch.ts
8428
8690
  init_cjs_shims();
8429
8691
  var import_node_fs35 = __toESM(require("fs"), 1);
8430
- var import_node_path55 = __toESM(require("path"), 1);
8692
+ var import_node_path56 = __toESM(require("path"), 1);
8431
8693
  var import_chokidar = __toESM(require("chokidar"), 1);
8432
8694
 
8433
8695
  // src/api.ts
8434
8696
  init_cjs_shims();
8435
8697
  var import_fastify = __toESM(require("fastify"), 1);
8436
8698
  var import_cors = __toESM(require("@fastify/cors"), 1);
8437
- var import_types35 = require("@neat.is/types");
8699
+ var import_types36 = require("@neat.is/types");
8438
8700
 
8439
8701
  // src/extend/index.ts
8440
8702
  init_cjs_shims();
8441
8703
  var import_node_fs28 = require("fs");
8442
- var import_node_path46 = __toESM(require("path"), 1);
8704
+ var import_node_path47 = __toESM(require("path"), 1);
8443
8705
  var import_node_os2 = __toESM(require("os"), 1);
8444
8706
  var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
8445
8707
 
8446
8708
  // src/installers/package-manager.ts
8447
8709
  init_cjs_shims();
8448
8710
  var import_node_fs27 = require("fs");
8449
- var import_node_path45 = __toESM(require("path"), 1);
8711
+ var import_node_path46 = __toESM(require("path"), 1);
8450
8712
  var import_node_child_process = require("child_process");
8451
8713
  var LOCKFILE_PRIORITY = [
8452
8714
  { lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
@@ -8468,22 +8730,22 @@ async function exists2(p) {
8468
8730
  }
8469
8731
  }
8470
8732
  async function detectPackageManager(serviceDir) {
8471
- let dir = import_node_path45.default.resolve(serviceDir);
8733
+ let dir = import_node_path46.default.resolve(serviceDir);
8472
8734
  const stops = /* @__PURE__ */ new Set();
8473
8735
  for (let i = 0; i < 64; i++) {
8474
8736
  if (stops.has(dir)) break;
8475
8737
  stops.add(dir);
8476
8738
  for (const candidate of LOCKFILE_PRIORITY) {
8477
- const lockPath = import_node_path45.default.join(dir, candidate.lockfile);
8739
+ const lockPath = import_node_path46.default.join(dir, candidate.lockfile);
8478
8740
  if (await exists2(lockPath)) {
8479
8741
  return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
8480
8742
  }
8481
8743
  }
8482
- const parent = import_node_path45.default.dirname(dir);
8744
+ const parent = import_node_path46.default.dirname(dir);
8483
8745
  if (parent === dir) break;
8484
8746
  dir = parent;
8485
8747
  }
8486
- return { pm: "npm", cwd: import_node_path45.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
8748
+ return { pm: "npm", cwd: import_node_path46.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
8487
8749
  }
8488
8750
  async function runPackageManagerInstall(cmd) {
8489
8751
  return new Promise((resolve) => {
@@ -8532,7 +8794,7 @@ async function fileExists2(p) {
8532
8794
  }
8533
8795
  }
8534
8796
  async function readPackageJson(scanPath) {
8535
- const pkgPath = import_node_path46.default.join(scanPath, "package.json");
8797
+ const pkgPath = import_node_path47.default.join(scanPath, "package.json");
8536
8798
  const raw = await import_node_fs28.promises.readFile(pkgPath, "utf8");
8537
8799
  return JSON.parse(raw);
8538
8800
  }
@@ -8546,27 +8808,27 @@ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
8546
8808
  ]);
8547
8809
  async function findHookFiles(scanPath) {
8548
8810
  const found = [];
8549
- const walk3 = async (dir) => {
8811
+ const walk4 = async (dir) => {
8550
8812
  const entries = await import_node_fs28.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
8551
8813
  for (const entry2 of entries) {
8552
8814
  if (entry2.isDirectory()) {
8553
8815
  if (entry2.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry2.name)) continue;
8554
- await walk3(import_node_path46.default.join(dir, entry2.name));
8816
+ await walk4(import_node_path47.default.join(dir, entry2.name));
8555
8817
  } else if (entry2.isFile()) {
8556
8818
  if ((entry2.name.startsWith("instrumentation") || entry2.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry2.name)) {
8557
- const rel = import_node_path46.default.relative(scanPath, import_node_path46.default.join(dir, entry2.name));
8558
- found.push(rel.split(import_node_path46.default.sep).join("/"));
8819
+ const rel = import_node_path47.default.relative(scanPath, import_node_path47.default.join(dir, entry2.name));
8820
+ found.push(rel.split(import_node_path47.default.sep).join("/"));
8559
8821
  }
8560
8822
  }
8561
8823
  }
8562
8824
  };
8563
- await walk3(scanPath);
8825
+ await walk4(scanPath);
8564
8826
  return found.sort();
8565
8827
  }
8566
8828
  async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
8567
8829
  let fallback = null;
8568
8830
  for (const file of hookFiles) {
8569
- const content = await import_node_fs28.promises.readFile(import_node_path46.default.join(scanPath, file), "utf8");
8831
+ const content = await import_node_fs28.promises.readFile(import_node_path47.default.join(scanPath, file), "utf8");
8570
8832
  const patched = splicedContent(content, snippet2);
8571
8833
  if (patched !== null) return { file, content, patched };
8572
8834
  if (fallback === null) fallback = { file, content };
@@ -8574,11 +8836,11 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
8574
8836
  return { file: fallback.file, content: fallback.content, patched: null };
8575
8837
  }
8576
8838
  function extendLogPath() {
8577
- return process.env.NEAT_EXTEND_LOG ?? import_node_path46.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
8839
+ return process.env.NEAT_EXTEND_LOG ?? import_node_path47.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
8578
8840
  }
8579
8841
  async function appendExtendLog(entry2) {
8580
8842
  const logPath = extendLogPath();
8581
- await import_node_fs28.promises.mkdir(import_node_path46.default.dirname(logPath), { recursive: true });
8843
+ await import_node_fs28.promises.mkdir(import_node_path47.default.dirname(logPath), { recursive: true });
8582
8844
  await import_node_fs28.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
8583
8845
  }
8584
8846
  function splicedContent(fileContent, snippet2) {
@@ -8637,7 +8899,7 @@ function lookupInstrumentation(library, installedVersion) {
8637
8899
  }
8638
8900
  async function describeProjectInstrumentation(ctx) {
8639
8901
  const hookFiles = await findHookFiles(ctx.scanPath);
8640
- const envNeat = await fileExists2(import_node_path46.default.join(ctx.scanPath, ".env.neat"));
8902
+ const envNeat = await fileExists2(import_node_path47.default.join(ctx.scanPath, ".env.neat"));
8641
8903
  const registryInstrPackages = new Set(
8642
8904
  (0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
8643
8905
  );
@@ -8659,7 +8921,7 @@ async function applyExtension(ctx, args, options) {
8659
8921
  );
8660
8922
  }
8661
8923
  for (const file of hookFiles) {
8662
- const content = await import_node_fs28.promises.readFile(import_node_path46.default.join(ctx.scanPath, file), "utf8");
8924
+ const content = await import_node_fs28.promises.readFile(import_node_path47.default.join(ctx.scanPath, file), "utf8");
8663
8925
  if (content.includes(args.registration_snippet)) {
8664
8926
  return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
8665
8927
  }
@@ -8671,10 +8933,10 @@ async function applyExtension(ctx, args, options) {
8671
8933
  );
8672
8934
  }
8673
8935
  const primaryFile = primary.file;
8674
- const primaryPath = import_node_path46.default.join(ctx.scanPath, primaryFile);
8936
+ const primaryPath = import_node_path47.default.join(ctx.scanPath, primaryFile);
8675
8937
  const filesTouched = [];
8676
8938
  const depsAdded = [];
8677
- const pkgPath = import_node_path46.default.join(ctx.scanPath, "package.json");
8939
+ const pkgPath = import_node_path47.default.join(ctx.scanPath, "package.json");
8678
8940
  const pkg = await readPackageJson(ctx.scanPath);
8679
8941
  if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
8680
8942
  pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
@@ -8713,7 +8975,7 @@ async function dryRunExtension(ctx, args) {
8713
8975
  };
8714
8976
  }
8715
8977
  for (const file of hookFiles) {
8716
- const content = await import_node_fs28.promises.readFile(import_node_path46.default.join(ctx.scanPath, file), "utf8");
8978
+ const content = await import_node_fs28.promises.readFile(import_node_path47.default.join(ctx.scanPath, file), "utf8");
8717
8979
  if (content.includes(args.registration_snippet)) {
8718
8980
  return {
8719
8981
  library: args.library,
@@ -8754,7 +9016,7 @@ async function rollbackExtension(ctx, args) {
8754
9016
  if (!match) {
8755
9017
  return { undone: false, message: "no apply found for library" };
8756
9018
  }
8757
- const pkgPath = import_node_path46.default.join(ctx.scanPath, "package.json");
9019
+ const pkgPath = import_node_path47.default.join(ctx.scanPath, "package.json");
8758
9020
  if (await fileExists2(pkgPath)) {
8759
9021
  const pkg = await readPackageJson(ctx.scanPath);
8760
9022
  if (pkg.dependencies?.[match.instrumentation_package]) {
@@ -8765,7 +9027,7 @@ async function rollbackExtension(ctx, args) {
8765
9027
  }
8766
9028
  const hookFiles = await findHookFiles(ctx.scanPath);
8767
9029
  for (const file of hookFiles) {
8768
- const filePath = import_node_path46.default.join(ctx.scanPath, file);
9030
+ const filePath = import_node_path47.default.join(ctx.scanPath, file);
8769
9031
  const content = await import_node_fs28.promises.readFile(filePath, "utf8");
8770
9032
  if (content.includes(match.registration_snippet)) {
8771
9033
  const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
@@ -8903,23 +9165,23 @@ function canonicalJson(value) {
8903
9165
 
8904
9166
  // src/projects.ts
8905
9167
  init_cjs_shims();
8906
- var import_node_path47 = __toESM(require("path"), 1);
9168
+ var import_node_path48 = __toESM(require("path"), 1);
8907
9169
  function pathsForProject(project, baseDir) {
8908
9170
  if (project === DEFAULT_PROJECT) {
8909
9171
  return {
8910
- snapshotPath: import_node_path47.default.join(baseDir, "graph.json"),
8911
- errorsPath: import_node_path47.default.join(baseDir, "errors.ndjson"),
8912
- staleEventsPath: import_node_path47.default.join(baseDir, "stale-events.ndjson"),
8913
- embeddingsCachePath: import_node_path47.default.join(baseDir, "embeddings.json"),
8914
- policyViolationsPath: import_node_path47.default.join(baseDir, "policy-violations.ndjson")
9172
+ snapshotPath: import_node_path48.default.join(baseDir, "graph.json"),
9173
+ errorsPath: import_node_path48.default.join(baseDir, "errors.ndjson"),
9174
+ staleEventsPath: import_node_path48.default.join(baseDir, "stale-events.ndjson"),
9175
+ embeddingsCachePath: import_node_path48.default.join(baseDir, "embeddings.json"),
9176
+ policyViolationsPath: import_node_path48.default.join(baseDir, "policy-violations.ndjson")
8915
9177
  };
8916
9178
  }
8917
9179
  return {
8918
- snapshotPath: import_node_path47.default.join(baseDir, `${project}.json`),
8919
- errorsPath: import_node_path47.default.join(baseDir, `errors.${project}.ndjson`),
8920
- staleEventsPath: import_node_path47.default.join(baseDir, `stale-events.${project}.ndjson`),
8921
- embeddingsCachePath: import_node_path47.default.join(baseDir, `embeddings.${project}.json`),
8922
- policyViolationsPath: import_node_path47.default.join(baseDir, `policy-violations.${project}.ndjson`)
9180
+ snapshotPath: import_node_path48.default.join(baseDir, `${project}.json`),
9181
+ errorsPath: import_node_path48.default.join(baseDir, `errors.${project}.ndjson`),
9182
+ staleEventsPath: import_node_path48.default.join(baseDir, `stale-events.${project}.ndjson`),
9183
+ embeddingsCachePath: import_node_path48.default.join(baseDir, `embeddings.${project}.json`),
9184
+ policyViolationsPath: import_node_path48.default.join(baseDir, `policy-violations.${project}.ndjson`)
8923
9185
  };
8924
9186
  }
8925
9187
  var Projects = class {
@@ -8957,26 +9219,26 @@ var Projects = class {
8957
9219
  init_cjs_shims();
8958
9220
  var import_node_fs30 = require("fs");
8959
9221
  var import_node_os3 = __toESM(require("os"), 1);
8960
- var import_node_path48 = __toESM(require("path"), 1);
8961
- var import_types34 = require("@neat.is/types");
9222
+ var import_node_path49 = __toESM(require("path"), 1);
9223
+ var import_types35 = require("@neat.is/types");
8962
9224
  var LOCK_TIMEOUT_MS = 5e3;
8963
9225
  var LOCK_RETRY_MS = 50;
8964
9226
  function neatHome() {
8965
9227
  const override = process.env.NEAT_HOME;
8966
- if (override && override.length > 0) return import_node_path48.default.resolve(override);
8967
- return import_node_path48.default.join(import_node_os3.default.homedir(), ".neat");
9228
+ if (override && override.length > 0) return import_node_path49.default.resolve(override);
9229
+ return import_node_path49.default.join(import_node_os3.default.homedir(), ".neat");
8968
9230
  }
8969
9231
  function registryPath() {
8970
- return import_node_path48.default.join(neatHome(), "projects.json");
9232
+ return import_node_path49.default.join(neatHome(), "projects.json");
8971
9233
  }
8972
9234
  function registryLockPath() {
8973
- return import_node_path48.default.join(neatHome(), "projects.json.lock");
9235
+ return import_node_path49.default.join(neatHome(), "projects.json.lock");
8974
9236
  }
8975
9237
  function daemonPidPath() {
8976
- return import_node_path48.default.join(neatHome(), "neatd.pid");
9238
+ return import_node_path49.default.join(neatHome(), "neatd.pid");
8977
9239
  }
8978
9240
  function daemonsDir() {
8979
- return import_node_path48.default.join(neatHome(), "daemons");
9241
+ return import_node_path49.default.join(neatHome(), "daemons");
8980
9242
  }
8981
9243
  function isFiniteInt(v) {
8982
9244
  return typeof v === "number" && Number.isFinite(v);
@@ -9017,7 +9279,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
9017
9279
  const out = [];
9018
9280
  for (const name of names) {
9019
9281
  if (!name.endsWith(".json")) continue;
9020
- const file = import_node_path48.default.join(dir, name);
9282
+ const file = import_node_path49.default.join(dir, name);
9021
9283
  let raw;
9022
9284
  try {
9023
9285
  raw = await import_node_fs30.promises.readFile(file, "utf8");
@@ -9138,7 +9400,7 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
9138
9400
  }
9139
9401
  }
9140
9402
  async function normalizeProjectPath(input) {
9141
- const resolved = import_node_path48.default.resolve(input);
9403
+ const resolved = import_node_path49.default.resolve(input);
9142
9404
  try {
9143
9405
  return await import_node_fs30.promises.realpath(resolved);
9144
9406
  } catch {
@@ -9146,7 +9408,7 @@ async function normalizeProjectPath(input) {
9146
9408
  }
9147
9409
  }
9148
9410
  async function writeAtomically(target, contents) {
9149
- await import_node_fs30.promises.mkdir(import_node_path48.default.dirname(target), { recursive: true });
9411
+ await import_node_fs30.promises.mkdir(import_node_path49.default.dirname(target), { recursive: true });
9150
9412
  const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
9151
9413
  const fd = await import_node_fs30.promises.open(tmp, "w");
9152
9414
  try {
@@ -9159,7 +9421,7 @@ async function writeAtomically(target, contents) {
9159
9421
  }
9160
9422
  async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
9161
9423
  const deadline = Date.now() + timeoutMs;
9162
- await import_node_fs30.promises.mkdir(import_node_path48.default.dirname(lockPath), { recursive: true });
9424
+ await import_node_fs30.promises.mkdir(import_node_path49.default.dirname(lockPath), { recursive: true });
9163
9425
  let probedHolder = false;
9164
9426
  while (true) {
9165
9427
  try {
@@ -9212,10 +9474,10 @@ async function readRegistry() {
9212
9474
  throw err;
9213
9475
  }
9214
9476
  const parsed = JSON.parse(raw);
9215
- return import_types34.RegistryFileSchema.parse(parsed);
9477
+ return import_types35.RegistryFileSchema.parse(parsed);
9216
9478
  }
9217
9479
  async function writeRegistry(reg) {
9218
- const validated = import_types34.RegistryFileSchema.parse(reg);
9480
+ const validated = import_types35.RegistryFileSchema.parse(reg);
9219
9481
  await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
9220
9482
  }
9221
9483
  var ProjectNameCollisionError = class extends Error {
@@ -9401,7 +9663,7 @@ init_auth();
9401
9663
  // src/connectors-config.ts
9402
9664
  init_cjs_shims();
9403
9665
  var import_node_os4 = __toESM(require("os"), 1);
9404
- var import_node_path49 = __toESM(require("path"), 1);
9666
+ var import_node_path50 = __toESM(require("path"), 1);
9405
9667
  var import_node_fs31 = require("fs");
9406
9668
  var CONNECTORS_CONFIG_VERSION = 1;
9407
9669
  var EnvRefUnsetError = class extends Error {
@@ -9416,11 +9678,11 @@ var EnvRefUnsetError = class extends Error {
9416
9678
  };
9417
9679
  function neatHome2() {
9418
9680
  const override = process.env.NEAT_HOME;
9419
- if (override && override.length > 0) return import_node_path49.default.resolve(override);
9420
- return import_node_path49.default.join(import_node_os4.default.homedir(), ".neat");
9681
+ if (override && override.length > 0) return import_node_path50.default.resolve(override);
9682
+ return import_node_path50.default.join(import_node_os4.default.homedir(), ".neat");
9421
9683
  }
9422
9684
  function connectorsConfigPath(home = neatHome2()) {
9423
- return import_node_path49.default.join(home, "connectors.json");
9685
+ return import_node_path50.default.join(home, "connectors.json");
9424
9686
  }
9425
9687
  var MODE_MASK_LOOSER_THAN_0600 = 63;
9426
9688
  async function warnIfModeLooserThan0600(file) {
@@ -9551,7 +9813,7 @@ function connectorMatchesProject(entry2, project) {
9551
9813
  var CONNECTORS_LOCK_TIMEOUT_MS = 5e3;
9552
9814
  var CONNECTORS_LOCK_RETRY_MS = 50;
9553
9815
  function connectorsConfigLockPath(home = neatHome2()) {
9554
- return import_node_path49.default.join(home, "connectors.json.lock");
9816
+ return import_node_path50.default.join(home, "connectors.json.lock");
9555
9817
  }
9556
9818
  function isEnvRef(value) {
9557
9819
  return value.length > 1 && value.startsWith("$");
@@ -9564,7 +9826,7 @@ function redactCredentialRef(ref) {
9564
9826
  return out;
9565
9827
  }
9566
9828
  async function writeConfigAtomically0600(file, contents) {
9567
- await import_node_fs31.promises.mkdir(import_node_path49.default.dirname(file), { recursive: true });
9829
+ await import_node_fs31.promises.mkdir(import_node_path50.default.dirname(file), { recursive: true });
9568
9830
  const tmp = `${file}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
9569
9831
  const fd = await import_node_fs31.promises.open(tmp, "w", 384);
9570
9832
  try {
@@ -9578,7 +9840,7 @@ async function writeConfigAtomically0600(file, contents) {
9578
9840
  }
9579
9841
  async function acquireConnectorsLock(lockPath, timeoutMs = CONNECTORS_LOCK_TIMEOUT_MS) {
9580
9842
  const deadline = Date.now() + timeoutMs;
9581
- await import_node_fs31.promises.mkdir(import_node_path49.default.dirname(lockPath), { recursive: true });
9843
+ await import_node_fs31.promises.mkdir(import_node_path50.default.dirname(lockPath), { recursive: true });
9582
9844
  for (; ; ) {
9583
9845
  try {
9584
9846
  const fd = await import_node_fs31.promises.open(lockPath, "wx");
@@ -9861,11 +10123,11 @@ function registerRoutes(scope, ctx) {
9861
10123
  const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
9862
10124
  const parsed = [];
9863
10125
  for (const c of candidates) {
9864
- const r = import_types35.DivergenceTypeSchema.safeParse(c);
10126
+ const r = import_types36.DivergenceTypeSchema.safeParse(c);
9865
10127
  if (!r.success) {
9866
10128
  return reply.code(400).send({
9867
10129
  error: `unknown divergence type "${c}"`,
9868
- allowed: import_types35.DivergenceTypeSchema.options
10130
+ allowed: import_types36.DivergenceTypeSchema.options
9869
10131
  });
9870
10132
  }
9871
10133
  parsed.push(r.data);
@@ -10136,7 +10398,7 @@ function registerRoutes(scope, ctx) {
10136
10398
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
10137
10399
  let violations = await log.readAll();
10138
10400
  if (req.query.severity) {
10139
- const sev = import_types35.PolicySeveritySchema.safeParse(req.query.severity);
10401
+ const sev = import_types36.PolicySeveritySchema.safeParse(req.query.severity);
10140
10402
  if (!sev.success) {
10141
10403
  return reply.code(400).send({
10142
10404
  error: "invalid severity",
@@ -10175,7 +10437,7 @@ function registerRoutes(scope, ctx) {
10175
10437
  scope.post("/policies/check", async (req, reply) => {
10176
10438
  const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
10177
10439
  if (!proj) return;
10178
- const parsed = import_types35.PoliciesCheckBodySchema.safeParse(req.body ?? {});
10440
+ const parsed = import_types36.PoliciesCheckBodySchema.safeParse(req.body ?? {});
10179
10441
  if (!parsed.success) {
10180
10442
  return reply.code(400).send({
10181
10443
  error: "invalid /policies/check body",
@@ -10477,13 +10739,13 @@ init_otel();
10477
10739
  // src/daemon.ts
10478
10740
  init_cjs_shims();
10479
10741
  var import_node_fs33 = require("fs");
10480
- var import_node_path53 = __toESM(require("path"), 1);
10742
+ var import_node_path54 = __toESM(require("path"), 1);
10481
10743
  var import_node_module = require("module");
10482
10744
  init_otel();
10483
10745
 
10484
10746
  // src/connectors/index.ts
10485
10747
  init_cjs_shims();
10486
- var import_types36 = require("@neat.is/types");
10748
+ var import_types37 = require("@neat.is/types");
10487
10749
 
10488
10750
  // src/connectors/registry.ts
10489
10751
  init_cjs_shims();
@@ -10858,10 +11120,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
10858
11120
  // src/connectors/supabase/map.ts
10859
11121
  var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
10860
11122
  var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
10861
- function targetFromRestPath(path63) {
10862
- const rpcMatch = REST_RPC_PATH_RE.exec(path63);
11123
+ function targetFromRestPath(path64) {
11124
+ const rpcMatch = REST_RPC_PATH_RE.exec(path64);
10863
11125
  if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
10864
- const tableMatch = REST_TABLE_PATH_RE.exec(path63);
11126
+ const tableMatch = REST_TABLE_PATH_RE.exec(path64);
10865
11127
  if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
10866
11128
  return null;
10867
11129
  }
@@ -10970,23 +11232,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
10970
11232
 
10971
11233
  // src/connectors/supabase/resolve.ts
10972
11234
  init_cjs_shims();
10973
- var import_types38 = require("@neat.is/types");
11235
+ var import_types39 = require("@neat.is/types");
10974
11236
  function createSupabaseResolveTarget(graph, config) {
10975
11237
  return (signal, _ctx) => {
10976
11238
  if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
10977
11239
  return null;
10978
11240
  }
10979
- const subResourceId = (0, import_types38.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
11241
+ const subResourceId = (0, import_types39.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
10980
11242
  if (graph.hasNode(subResourceId)) {
10981
- return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types38.EdgeType.CALLS };
11243
+ return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types39.EdgeType.CALLS };
10982
11244
  }
10983
- const bareResourceId = (0, import_types38.infraId)(signal.targetKind, signal.targetName);
11245
+ const bareResourceId = (0, import_types39.infraId)(signal.targetKind, signal.targetName);
10984
11246
  if (graph.hasNode(bareResourceId)) {
10985
- return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types38.EdgeType.CALLS };
11247
+ return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types39.EdgeType.CALLS };
10986
11248
  }
10987
- const projectLevelId = (0, import_types38.infraId)("supabase", config.nodeRef);
11249
+ const projectLevelId = (0, import_types39.infraId)("supabase", config.nodeRef);
10988
11250
  if (graph.hasNode(projectLevelId)) {
10989
- return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types38.EdgeType.CALLS };
11251
+ return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types39.EdgeType.CALLS };
10990
11252
  }
10991
11253
  return null;
10992
11254
  };
@@ -11079,7 +11341,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
11079
11341
 
11080
11342
  // src/connectors/railway/index.ts
11081
11343
  init_cjs_shims();
11082
- var import_types42 = require("@neat.is/types");
11344
+ var import_types43 = require("@neat.is/types");
11083
11345
 
11084
11346
  // src/connectors/railway/client.ts
11085
11347
  init_cjs_shims();
@@ -11205,7 +11467,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
11205
11467
  const out = [];
11206
11468
  graph.forEachNode((_id, attrs) => {
11207
11469
  const node = attrs;
11208
- if (node.type !== import_types42.NodeType.RouteNode) return;
11470
+ if (node.type !== import_types43.NodeType.RouteNode) return;
11209
11471
  const route = attrs;
11210
11472
  if (route.service !== serviceName) return;
11211
11473
  out.push({
@@ -11309,12 +11571,12 @@ function createRailwayResolveTarget(config) {
11309
11571
  const serviceName = config.serviceNameById[config.serviceId];
11310
11572
  if (!serviceName) return null;
11311
11573
  if (signal.targetKind === ROUTE_TARGET_KIND) {
11312
- return { targetNodeId: signal.targetName, serviceName, edgeType: import_types42.EdgeType.CALLS };
11574
+ return { targetNodeId: signal.targetName, serviceName, edgeType: import_types43.EdgeType.CALLS };
11313
11575
  }
11314
11576
  if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
11315
11577
  const peerName = config.serviceNameById[signal.targetName];
11316
11578
  if (!peerName) return null;
11317
- return { targetNodeId: (0, import_types42.serviceId)(peerName), serviceName, edgeType: import_types42.EdgeType.CONNECTS_TO };
11579
+ return { targetNodeId: (0, import_types43.serviceId)(peerName), serviceName, edgeType: import_types43.EdgeType.CONNECTS_TO };
11318
11580
  }
11319
11581
  return null;
11320
11582
  };
@@ -11438,9 +11700,9 @@ function parseFirebaseTargetName(targetName) {
11438
11700
  const secondSep = rest.indexOf(FIELD_SEP);
11439
11701
  if (secondSep === -1) return null;
11440
11702
  const method = rest.slice(0, secondSep);
11441
- const path63 = rest.slice(secondSep + 1);
11442
- if (!resourceName || !method || !path63) return null;
11443
- return { resourceName, method, path: path63 };
11703
+ const path64 = rest.slice(secondSep + 1);
11704
+ if (!resourceName || !method || !path64) return null;
11705
+ return { resourceName, method, path: path64 };
11444
11706
  }
11445
11707
  function resourceNameFor(type, labels) {
11446
11708
  if (!labels) return null;
@@ -11478,14 +11740,14 @@ function mapLogEntryToSignal(entry2) {
11478
11740
  if (!req) return null;
11479
11741
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
11480
11742
  const method = req.requestMethod.toUpperCase();
11481
- const path63 = pathFromRequestUrl(req.requestUrl);
11482
- if (path63 === null) return null;
11743
+ const path64 = pathFromRequestUrl(req.requestUrl);
11744
+ if (path64 === null) return null;
11483
11745
  const timestamp = entry2.timestamp;
11484
11746
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
11485
11747
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
11486
11748
  return {
11487
11749
  targetKind: resourceType,
11488
- targetName: packFirebaseTargetName({ resourceName, method, path: path63 }),
11750
+ targetName: packFirebaseTargetName({ resourceName, method, path: path64 }),
11489
11751
  callCount: 1,
11490
11752
  errorCount: isError ? 1 : 0,
11491
11753
  lastObservedIso: timestamp
@@ -11502,7 +11764,7 @@ function mapLogEntriesToSignals(entries) {
11502
11764
 
11503
11765
  // src/connectors/firebase/resolve.ts
11504
11766
  init_cjs_shims();
11505
- var import_types43 = require("@neat.is/types");
11767
+ var import_types44 = require("@neat.is/types");
11506
11768
  function neatServiceNameFor(resourceType, resourceName, serviceMap) {
11507
11769
  switch (resourceType) {
11508
11770
  case "cloud_function":
@@ -11517,7 +11779,7 @@ function routeEntriesFor(graph, serviceName) {
11517
11779
  const entries = [];
11518
11780
  graph.forEachNode((_id, attrs) => {
11519
11781
  const node = attrs;
11520
- if (node.type !== import_types43.NodeType.RouteNode) return;
11782
+ if (node.type !== import_types44.NodeType.RouteNode) return;
11521
11783
  const route = attrs;
11522
11784
  if (route.service !== serviceName) return;
11523
11785
  entries.push({
@@ -11549,7 +11811,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
11549
11811
  return {
11550
11812
  targetNodeId: match.routeNodeId,
11551
11813
  serviceName,
11552
- edgeType: import_types43.EdgeType.CALLS
11814
+ edgeType: import_types44.EdgeType.CALLS
11553
11815
  };
11554
11816
  };
11555
11817
  }
@@ -11576,7 +11838,7 @@ init_cjs_shims();
11576
11838
 
11577
11839
  // src/connectors/cloudflare/connector.ts
11578
11840
  init_cjs_shims();
11579
- var import_types45 = require("@neat.is/types");
11841
+ var import_types46 = require("@neat.is/types");
11580
11842
 
11581
11843
  // src/connectors/cloudflare/client.ts
11582
11844
  init_cjs_shims();
@@ -11692,7 +11954,7 @@ function mapEventToSignal(event) {
11692
11954
  if (Number.isNaN(observedAt.getTime())) return null;
11693
11955
  const statusCode = metadata?.statusCode;
11694
11956
  const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
11695
- const path63 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
11957
+ const path64 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
11696
11958
  return {
11697
11959
  targetKind: CLOUDFLARE_TARGET_KIND,
11698
11960
  targetName: scriptName,
@@ -11700,7 +11962,7 @@ function mapEventToSignal(event) {
11700
11962
  errorCount: isError ? 1 : 0,
11701
11963
  lastObservedIso: observedAt.toISOString(),
11702
11964
  method,
11703
- ...path63 ? { path: path63 } : {},
11965
+ ...path64 ? { path: path64 } : {},
11704
11966
  ...typeof statusCode === "number" ? { statusCode } : {},
11705
11967
  ...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
11706
11968
  };
@@ -11740,19 +12002,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
11740
12002
  graph.forEachNode((id, attrs) => {
11741
12003
  if (found) return;
11742
12004
  const a = attrs;
11743
- if (a.type === import_types45.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
12005
+ if (a.type === import_types46.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
11744
12006
  found = id;
11745
12007
  }
11746
12008
  });
11747
12009
  return found;
11748
12010
  }
11749
- function findMatchingRouteNode(graph, serviceName, method, path63) {
11750
- const normalizedPath = normalizePathTemplate(path63);
12011
+ function findMatchingRouteNode(graph, serviceName, method, path64) {
12012
+ const normalizedPath = normalizePathTemplate(path64);
11751
12013
  let found = null;
11752
12014
  graph.forEachNode((id, attrs) => {
11753
12015
  if (found) return;
11754
12016
  const a = attrs;
11755
- if (a.type !== import_types45.NodeType.RouteNode || a.service !== serviceName) return;
12017
+ if (a.type !== import_types46.NodeType.RouteNode || a.service !== serviceName) return;
11756
12018
  if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
11757
12019
  const routeMethod = (a.method ?? "").toUpperCase();
11758
12020
  if (routeMethod !== "ALL" && routeMethod !== method) return;
@@ -11764,18 +12026,18 @@ function createCloudflareResolveTarget(config, graph) {
11764
12026
  return (signal) => {
11765
12027
  if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
11766
12028
  const scriptName = signal.targetName;
11767
- const { method, path: path63 } = signal;
12029
+ const { method, path: path64 } = signal;
11768
12030
  const resolveRouteGrain = (serviceName, wholeFileId) => {
11769
- if (!method || !path63) return wholeFileId;
11770
- return findMatchingRouteNode(graph, serviceName, method, path63) ?? wholeFileId;
12031
+ if (!method || !path64) return wholeFileId;
12032
+ return findMatchingRouteNode(graph, serviceName, method, path64) ?? wholeFileId;
11771
12033
  };
11772
12034
  const mapping = config.workers?.[scriptName];
11773
12035
  if (mapping) {
11774
- const wholeFileId = (0, import_types45.fileId)(mapping.service, mapping.entryFile);
12036
+ const wholeFileId = (0, import_types46.fileId)(mapping.service, mapping.entryFile);
11775
12037
  return {
11776
12038
  targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
11777
12039
  serviceName: mapping.service,
11778
- edgeType: import_types45.EdgeType.CALLS
12040
+ edgeType: import_types46.EdgeType.CALLS
11779
12041
  };
11780
12042
  }
11781
12043
  const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
@@ -11784,13 +12046,13 @@ function createCloudflareResolveTarget(config, graph) {
11784
12046
  return {
11785
12047
  targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
11786
12048
  serviceName: fileNode.service,
11787
- edgeType: import_types45.EdgeType.CALLS
12049
+ edgeType: import_types46.EdgeType.CALLS
11788
12050
  };
11789
12051
  }
11790
12052
  return {
11791
- targetNodeId: (0, import_types45.infraId)("cloudflare-worker", scriptName),
12053
+ targetNodeId: (0, import_types46.infraId)("cloudflare-worker", scriptName),
11792
12054
  serviceName: scriptName,
11793
- edgeType: import_types45.EdgeType.CALLS,
12055
+ edgeType: import_types46.EdgeType.CALLS,
11794
12056
  ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
11795
12057
  };
11796
12058
  };
@@ -12249,28 +12511,28 @@ init_auth();
12249
12511
  // src/unrouted.ts
12250
12512
  init_cjs_shims();
12251
12513
  var import_node_fs32 = require("fs");
12252
- var import_node_path52 = __toESM(require("path"), 1);
12514
+ var import_node_path53 = __toESM(require("path"), 1);
12253
12515
 
12254
12516
  // src/daemon.ts
12255
- var import_types48 = require("@neat.is/types");
12517
+ var import_types49 = require("@neat.is/types");
12256
12518
  function daemonJsonPath(scanPath) {
12257
- return import_node_path53.default.join(scanPath, "neat-out", "daemon.json");
12519
+ return import_node_path54.default.join(scanPath, "neat-out", "daemon.json");
12258
12520
  }
12259
12521
  function daemonsDiscoveryDir(home) {
12260
12522
  const base = home && home.length > 0 ? home : neatHomeFromEnv();
12261
- return import_node_path53.default.join(base, "daemons");
12523
+ return import_node_path54.default.join(base, "daemons");
12262
12524
  }
12263
12525
  function daemonDiscoveryPath(project, home) {
12264
- return import_node_path53.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
12526
+ return import_node_path54.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
12265
12527
  }
12266
12528
  function sanitizeDiscoveryName(project) {
12267
12529
  return project.replace(/[^A-Za-z0-9._-]/g, "_");
12268
12530
  }
12269
12531
  function neatHomeFromEnv() {
12270
12532
  const env = process.env.NEAT_HOME;
12271
- if (env && env.length > 0) return import_node_path53.default.resolve(env);
12533
+ if (env && env.length > 0) return import_node_path54.default.resolve(env);
12272
12534
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
12273
- return import_node_path53.default.join(home, ".neat");
12535
+ return import_node_path54.default.join(home, ".neat");
12274
12536
  }
12275
12537
  async function readDaemonRecord(scanPath) {
12276
12538
  try {
@@ -12341,7 +12603,7 @@ init_otel_grpc();
12341
12603
  // src/search.ts
12342
12604
  init_cjs_shims();
12343
12605
  var import_node_fs34 = require("fs");
12344
- var import_node_path54 = __toESM(require("path"), 1);
12606
+ var import_node_path55 = __toESM(require("path"), 1);
12345
12607
  var import_node_crypto4 = require("crypto");
12346
12608
  var DEFAULT_LIMIT = 10;
12347
12609
  var NOMIC_DIM = 768;
@@ -12504,7 +12766,7 @@ async function readCache(cachePath) {
12504
12766
  }
12505
12767
  }
12506
12768
  async function writeCache(cachePath, cache) {
12507
- await import_node_fs34.promises.mkdir(import_node_path54.default.dirname(cachePath), { recursive: true });
12769
+ await import_node_fs34.promises.mkdir(import_node_path55.default.dirname(cachePath), { recursive: true });
12508
12770
  await import_node_fs34.promises.writeFile(cachePath, JSON.stringify(cache));
12509
12771
  }
12510
12772
  var VectorIndex = class {
@@ -12662,8 +12924,8 @@ var ALL_PHASES = [
12662
12924
  ];
12663
12925
  function classifyChange(relPath) {
12664
12926
  const phases = /* @__PURE__ */ new Set();
12665
- const base = import_node_path55.default.basename(relPath).toLowerCase();
12666
- const segments = relPath.split(import_node_path55.default.sep).map((s) => s.toLowerCase());
12927
+ const base = import_node_path56.default.basename(relPath).toLowerCase();
12928
+ const segments = relPath.split(import_node_path56.default.sep).map((s) => s.toLowerCase());
12667
12929
  if (base === "package.json" || base === "requirements.txt" || base === "pyproject.toml" || base === "setup.py") {
12668
12930
  phases.add("services");
12669
12931
  phases.add("aliases");
@@ -12791,9 +13053,9 @@ function countWatchableDirs(scanPath, limit) {
12791
13053
  for (const e of entries) {
12792
13054
  if (count >= limit) return;
12793
13055
  if (!e.isDirectory()) continue;
12794
- if (IGNORED_WATCH_PATHS.some((re) => re.test(import_node_path55.default.join(dir, e.name) + import_node_path55.default.sep))) continue;
13056
+ if (IGNORED_WATCH_PATHS.some((re) => re.test(import_node_path56.default.join(dir, e.name) + import_node_path56.default.sep))) continue;
12795
13057
  count++;
12796
- if (depth < 2) visit(import_node_path55.default.join(dir, e.name), depth + 1);
13058
+ if (depth < 2) visit(import_node_path56.default.join(dir, e.name), depth + 1);
12797
13059
  }
12798
13060
  };
12799
13061
  visit(scanPath, 0);
@@ -12811,8 +13073,8 @@ async function startWatch(graph, opts) {
12811
13073
  const projectName = opts.project ?? DEFAULT_PROJECT;
12812
13074
  await loadGraphFromDisk(graph, opts.outPath);
12813
13075
  const detachEventBus = attachGraphToEventBus(graph, { project: projectName });
12814
- const policyFilePath = import_node_path55.default.join(opts.scanPath, "policy.json");
12815
- const policyViolationsPath = import_node_path55.default.join(import_node_path55.default.dirname(opts.outPath), "policy-violations.ndjson");
13076
+ const policyFilePath = import_node_path56.default.join(opts.scanPath, "policy.json");
13077
+ const policyViolationsPath = import_node_path56.default.join(import_node_path56.default.dirname(opts.outPath), "policy-violations.ndjson");
12816
13078
  let policies = [];
12817
13079
  try {
12818
13080
  policies = await loadPolicyFile(policyFilePath);
@@ -12863,7 +13125,7 @@ async function startWatch(graph, opts) {
12863
13125
  assertBindAuthority(host, auth.authToken);
12864
13126
  const port = opts.port ?? 8080;
12865
13127
  const otelPort = opts.otelPort ?? 4318;
12866
- const cachePath = opts.embeddingsCachePath ?? import_node_path55.default.join(import_node_path55.default.dirname(opts.outPath), "embeddings.json");
13128
+ const cachePath = opts.embeddingsCachePath ?? import_node_path56.default.join(import_node_path56.default.dirname(opts.outPath), "embeddings.json");
12867
13129
  let searchIndex;
12868
13130
  try {
12869
13131
  searchIndex = await buildSearchIndex(graph, { cachePath });
@@ -12881,7 +13143,7 @@ async function startWatch(graph, opts) {
12881
13143
  // Paths are derived from the explicit options the watch caller passes
12882
13144
  // — pathsForProject is only used to fill in the embeddings/snapshot
12883
13145
  // fields so the registry shape is complete.
12884
- ...pathsForProject(projectName, import_node_path55.default.dirname(opts.outPath)),
13146
+ ...pathsForProject(projectName, import_node_path56.default.dirname(opts.outPath)),
12885
13147
  snapshotPath: opts.outPath,
12886
13148
  errorsPath: opts.errorsPath,
12887
13149
  staleEventsPath: opts.staleEventsPath
@@ -12999,9 +13261,9 @@ async function startWatch(graph, opts) {
12999
13261
  };
13000
13262
  const onPath = (absPath) => {
13001
13263
  if (shouldIgnore(absPath)) return;
13002
- const rel = import_node_path55.default.relative(opts.scanPath, absPath);
13264
+ const rel = import_node_path56.default.relative(opts.scanPath, absPath);
13003
13265
  if (!rel || rel.startsWith("..")) return;
13004
- pendingPaths.add(rel.split(import_node_path55.default.sep).join("/"));
13266
+ pendingPaths.add(rel.split(import_node_path56.default.sep).join("/"));
13005
13267
  const phases = classifyChange(rel);
13006
13268
  if (phases.size === 0) {
13007
13269
  for (const p of ALL_PHASES) pending.add(p);
@@ -13058,7 +13320,7 @@ async function startWatch(graph, opts) {
13058
13320
  // src/deploy/detect.ts
13059
13321
  init_cjs_shims();
13060
13322
  var import_node_fs36 = require("fs");
13061
- var import_node_path56 = __toESM(require("path"), 1);
13323
+ var import_node_path57 = __toESM(require("path"), 1);
13062
13324
  var import_node_child_process2 = require("child_process");
13063
13325
  var import_node_crypto5 = require("crypto");
13064
13326
  function generateToken() {
@@ -13158,7 +13420,7 @@ async function runDeploy(opts = {}) {
13158
13420
  const token = generateToken();
13159
13421
  switch (substrate) {
13160
13422
  case "docker-compose": {
13161
- const artifactPath = import_node_path56.default.join(cwd, "docker-compose.neat.yml");
13423
+ const artifactPath = import_node_path57.default.join(cwd, "docker-compose.neat.yml");
13162
13424
  const contents = emitDockerCompose(cwd);
13163
13425
  await import_node_fs36.promises.writeFile(artifactPath, contents, "utf8");
13164
13426
  return {
@@ -13166,11 +13428,11 @@ async function runDeploy(opts = {}) {
13166
13428
  artifactPath,
13167
13429
  token,
13168
13430
  contents,
13169
- startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${import_node_path56.default.basename(artifactPath)} up -d`
13431
+ startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${import_node_path57.default.basename(artifactPath)} up -d`
13170
13432
  };
13171
13433
  }
13172
13434
  case "systemd": {
13173
- const artifactPath = import_node_path56.default.join(cwd, "neat.service");
13435
+ const artifactPath = import_node_path57.default.join(cwd, "neat.service");
13174
13436
  const contents = emitSystemdUnit(cwd);
13175
13437
  await import_node_fs36.promises.writeFile(artifactPath, contents, "utf8");
13176
13438
  return {
@@ -13206,7 +13468,7 @@ init_cjs_shims();
13206
13468
  // src/installers/javascript.ts
13207
13469
  init_cjs_shims();
13208
13470
  var import_node_fs37 = require("fs");
13209
- var import_node_path57 = __toESM(require("path"), 1);
13471
+ var import_node_path58 = __toESM(require("path"), 1);
13210
13472
  var import_semver2 = __toESM(require("semver"), 1);
13211
13473
 
13212
13474
  // src/installers/templates.ts
@@ -13859,11 +14121,11 @@ var OTEL_ENV = {
13859
14121
  value: "http://localhost:4318/projects/<project>/v1/traces"
13860
14122
  };
13861
14123
  function serviceNodeName(pkg, serviceDir) {
13862
- return pkg.name ?? import_node_path57.default.basename(serviceDir);
14124
+ return pkg.name ?? import_node_path58.default.basename(serviceDir);
13863
14125
  }
13864
14126
  function projectToken(pkg, serviceDir, project) {
13865
14127
  if (project && project.length > 0) return project;
13866
- return pkg.name ?? import_node_path57.default.basename(serviceDir);
14128
+ return pkg.name ?? import_node_path58.default.basename(serviceDir);
13867
14129
  }
13868
14130
  async function readJsonFile(p) {
13869
14131
  try {
@@ -13876,16 +14138,16 @@ async function readJsonFile(p) {
13876
14138
  async function detectRuntimeKind(pkgRoot, pkg) {
13877
14139
  const deps = allDeps(pkg);
13878
14140
  if ("react-native" in deps || "expo" in deps) return "react-native";
13879
- const appJson = await readJsonFile(import_node_path57.default.join(pkgRoot, "app.json"));
14141
+ const appJson = await readJsonFile(import_node_path58.default.join(pkgRoot, "app.json"));
13880
14142
  if (appJson && typeof appJson === "object" && "expo" in appJson) {
13881
14143
  return "react-native";
13882
14144
  }
13883
- if (await exists3(import_node_path57.default.join(pkgRoot, "vite.config.js")) || await exists3(import_node_path57.default.join(pkgRoot, "vite.config.ts")) || await exists3(import_node_path57.default.join(pkgRoot, "vite.config.mjs")) || "vite" in deps) {
14145
+ if (await exists3(import_node_path58.default.join(pkgRoot, "vite.config.js")) || await exists3(import_node_path58.default.join(pkgRoot, "vite.config.ts")) || await exists3(import_node_path58.default.join(pkgRoot, "vite.config.mjs")) || "vite" in deps) {
13884
14146
  return "browser-bundle";
13885
14147
  }
13886
- if (await exists3(import_node_path57.default.join(pkgRoot, "wrangler.toml"))) return "cloudflare-workers";
13887
- if (await exists3(import_node_path57.default.join(pkgRoot, "bun.lockb"))) return "bun";
13888
- if (await exists3(import_node_path57.default.join(pkgRoot, "deno.json")) || await exists3(import_node_path57.default.join(pkgRoot, "deno.lock"))) {
14148
+ if (await exists3(import_node_path58.default.join(pkgRoot, "wrangler.toml"))) return "cloudflare-workers";
14149
+ if (await exists3(import_node_path58.default.join(pkgRoot, "bun.lockb"))) return "bun";
14150
+ if (await exists3(import_node_path58.default.join(pkgRoot, "deno.json")) || await exists3(import_node_path58.default.join(pkgRoot, "deno.lock"))) {
13889
14151
  return "deno";
13890
14152
  }
13891
14153
  const engines = pkg.engines ?? {};
@@ -13894,7 +14156,7 @@ async function detectRuntimeKind(pkgRoot, pkg) {
13894
14156
  }
13895
14157
  async function readPackageJson2(serviceDir) {
13896
14158
  try {
13897
- const raw = await import_node_fs37.promises.readFile(import_node_path57.default.join(serviceDir, "package.json"), "utf8");
14159
+ const raw = await import_node_fs37.promises.readFile(import_node_path58.default.join(serviceDir, "package.json"), "utf8");
13898
14160
  return JSON.parse(raw);
13899
14161
  } catch {
13900
14162
  return null;
@@ -13938,7 +14200,7 @@ function needsVersionUpgrade(installed, expected) {
13938
14200
  var NEXT_CONFIG_CANDIDATES = ["next.config.js", "next.config.ts", "next.config.mjs"];
13939
14201
  async function findNextConfig(serviceDir) {
13940
14202
  for (const name of NEXT_CONFIG_CANDIDATES) {
13941
- const candidate = import_node_path57.default.join(serviceDir, name);
14203
+ const candidate = import_node_path58.default.join(serviceDir, name);
13942
14204
  if (await exists3(candidate)) return candidate;
13943
14205
  }
13944
14206
  return null;
@@ -14007,7 +14269,7 @@ function hasRemixDependency(pkg) {
14007
14269
  }
14008
14270
  async function findRemixEntry(serviceDir) {
14009
14271
  for (const rel of REMIX_ENTRY_CANDIDATES) {
14010
- const candidate = import_node_path57.default.join(serviceDir, rel);
14272
+ const candidate = import_node_path58.default.join(serviceDir, rel);
14011
14273
  if (await exists3(candidate)) return candidate;
14012
14274
  }
14013
14275
  return null;
@@ -14019,14 +14281,14 @@ function hasSvelteKitDependency(pkg) {
14019
14281
  }
14020
14282
  async function findSvelteKitHooks(serviceDir) {
14021
14283
  for (const rel of SVELTEKIT_HOOKS_CANDIDATES) {
14022
- const candidate = import_node_path57.default.join(serviceDir, rel);
14284
+ const candidate = import_node_path58.default.join(serviceDir, rel);
14023
14285
  if (await exists3(candidate)) return candidate;
14024
14286
  }
14025
14287
  return null;
14026
14288
  }
14027
14289
  async function findSvelteKitConfig(serviceDir) {
14028
14290
  for (const rel of SVELTEKIT_CONFIG_CANDIDATES) {
14029
- const candidate = import_node_path57.default.join(serviceDir, rel);
14291
+ const candidate = import_node_path58.default.join(serviceDir, rel);
14030
14292
  if (await exists3(candidate)) return candidate;
14031
14293
  }
14032
14294
  return null;
@@ -14037,7 +14299,7 @@ function hasNuxtDependency(pkg) {
14037
14299
  }
14038
14300
  async function findNuxtConfig(serviceDir) {
14039
14301
  for (const name of NUXT_CONFIG_CANDIDATES) {
14040
- const candidate = import_node_path57.default.join(serviceDir, name);
14302
+ const candidate = import_node_path58.default.join(serviceDir, name);
14041
14303
  if (await exists3(candidate)) return candidate;
14042
14304
  }
14043
14305
  return null;
@@ -14048,7 +14310,7 @@ function hasAstroDependency(pkg) {
14048
14310
  }
14049
14311
  async function findAstroConfig(serviceDir) {
14050
14312
  for (const name of ASTRO_CONFIG_CANDIDATES) {
14051
- const candidate = import_node_path57.default.join(serviceDir, name);
14313
+ const candidate = import_node_path58.default.join(serviceDir, name);
14052
14314
  if (await exists3(candidate)) return candidate;
14053
14315
  }
14054
14316
  return null;
@@ -14062,7 +14324,7 @@ function parseNextMajor(range) {
14062
14324
  return Number.isFinite(n) ? n : null;
14063
14325
  }
14064
14326
  async function isTypeScriptProject(serviceDir) {
14065
- return exists3(import_node_path57.default.join(serviceDir, "tsconfig.json"));
14327
+ return exists3(import_node_path58.default.join(serviceDir, "tsconfig.json"));
14066
14328
  }
14067
14329
  var INDEX_EXTENSIONS = [".ts", ".tsx", ".js", ".mjs", ".cjs"];
14068
14330
  var INDEX_CANDIDATES = INDEX_EXTENSIONS.map((ext) => `index${ext}`);
@@ -14111,7 +14373,7 @@ function entryFromScript(script) {
14111
14373
  }
14112
14374
  async function resolveEntry(serviceDir, pkg) {
14113
14375
  if (typeof pkg.main === "string" && pkg.main.length > 0) {
14114
- const candidate = import_node_path57.default.resolve(serviceDir, pkg.main);
14376
+ const candidate = import_node_path58.default.resolve(serviceDir, pkg.main);
14115
14377
  if (await exists3(candidate)) return candidate;
14116
14378
  }
14117
14379
  if (pkg.bin) {
@@ -14125,40 +14387,40 @@ async function resolveEntry(serviceDir, pkg) {
14125
14387
  if (typeof first === "string") binEntry = first;
14126
14388
  }
14127
14389
  if (binEntry) {
14128
- const candidate = import_node_path57.default.resolve(serviceDir, binEntry);
14390
+ const candidate = import_node_path58.default.resolve(serviceDir, binEntry);
14129
14391
  if (await exists3(candidate)) return candidate;
14130
14392
  }
14131
14393
  }
14132
14394
  const startEntry = entryFromScript(pkg.scripts?.start);
14133
14395
  if (startEntry) {
14134
- const candidate = import_node_path57.default.resolve(serviceDir, startEntry);
14396
+ const candidate = import_node_path58.default.resolve(serviceDir, startEntry);
14135
14397
  if (await exists3(candidate)) return candidate;
14136
14398
  }
14137
14399
  const devEntry = entryFromScript(pkg.scripts?.dev);
14138
14400
  if (devEntry) {
14139
- const candidate = import_node_path57.default.resolve(serviceDir, devEntry);
14401
+ const candidate = import_node_path58.default.resolve(serviceDir, devEntry);
14140
14402
  if (await exists3(candidate)) return candidate;
14141
14403
  }
14142
14404
  for (const rel of SRC_INDEX_CANDIDATES) {
14143
- const candidate = import_node_path57.default.join(serviceDir, rel);
14405
+ const candidate = import_node_path58.default.join(serviceDir, rel);
14144
14406
  if (await exists3(candidate)) return candidate;
14145
14407
  }
14146
14408
  for (const rel of SRC_NAMED_CANDIDATES) {
14147
- const candidate = import_node_path57.default.join(serviceDir, rel);
14409
+ const candidate = import_node_path58.default.join(serviceDir, rel);
14148
14410
  if (await exists3(candidate)) return candidate;
14149
14411
  }
14150
14412
  for (const rel of ROOT_NAMED_CANDIDATES) {
14151
- const candidate = import_node_path57.default.join(serviceDir, rel);
14413
+ const candidate = import_node_path58.default.join(serviceDir, rel);
14152
14414
  if (await exists3(candidate)) return candidate;
14153
14415
  }
14154
14416
  for (const name of INDEX_CANDIDATES) {
14155
- const candidate = import_node_path57.default.join(serviceDir, name);
14417
+ const candidate = import_node_path58.default.join(serviceDir, name);
14156
14418
  if (await exists3(candidate)) return candidate;
14157
14419
  }
14158
14420
  return null;
14159
14421
  }
14160
14422
  function dispatchEntry(entryFile, pkg) {
14161
- const ext = import_node_path57.default.extname(entryFile).toLowerCase();
14423
+ const ext = import_node_path58.default.extname(entryFile).toLowerCase();
14162
14424
  if (ext === ".ts" || ext === ".tsx") return "ts";
14163
14425
  if (ext === ".mjs") return "esm";
14164
14426
  if (ext === ".cjs") return "cjs";
@@ -14175,9 +14437,9 @@ function otelInitContents(flavor) {
14175
14437
  return OTEL_INIT_CJS;
14176
14438
  }
14177
14439
  function injectionLine(flavor, entryFile, otelInitFile) {
14178
- let rel = import_node_path57.default.relative(import_node_path57.default.dirname(entryFile), otelInitFile);
14440
+ let rel = import_node_path58.default.relative(import_node_path58.default.dirname(entryFile), otelInitFile);
14179
14441
  if (!rel.startsWith(".")) rel = `./${rel}`;
14180
- rel = rel.split(import_node_path57.default.sep).join("/");
14442
+ rel = rel.split(import_node_path58.default.sep).join("/");
14181
14443
  if (flavor === "cjs") return `require('${rel}')`;
14182
14444
  if (flavor === "esm") return `import '${rel}'`;
14183
14445
  const tsRel = rel.replace(/\.ts$/, "");
@@ -14190,27 +14452,27 @@ function lineIsOtelInjection(line) {
14190
14452
  }
14191
14453
  async function detectsSrcLayout(serviceDir) {
14192
14454
  const [hasSrcApp, hasSrcPages, hasRootApp, hasRootPages] = await Promise.all([
14193
- exists3(import_node_path57.default.join(serviceDir, "src", "app")),
14194
- exists3(import_node_path57.default.join(serviceDir, "src", "pages")),
14195
- exists3(import_node_path57.default.join(serviceDir, "app")),
14196
- exists3(import_node_path57.default.join(serviceDir, "pages"))
14455
+ exists3(import_node_path58.default.join(serviceDir, "src", "app")),
14456
+ exists3(import_node_path58.default.join(serviceDir, "src", "pages")),
14457
+ exists3(import_node_path58.default.join(serviceDir, "app")),
14458
+ exists3(import_node_path58.default.join(serviceDir, "pages"))
14197
14459
  ]);
14198
14460
  return (hasSrcApp || hasSrcPages) && !hasRootApp && !hasRootPages;
14199
14461
  }
14200
14462
  async function planNext(serviceDir, pkg, manifestPath, nextConfigPath, project) {
14201
14463
  const useTs = await isTypeScriptProject(serviceDir);
14202
14464
  const srcLayout = await detectsSrcLayout(serviceDir);
14203
- const baseDir = srcLayout ? import_node_path57.default.join(serviceDir, "src") : serviceDir;
14204
- const instrumentationFile = import_node_path57.default.join(baseDir, useTs ? "instrumentation.ts" : "instrumentation.js");
14205
- const instrumentationNodeFile = import_node_path57.default.join(
14465
+ const baseDir = srcLayout ? import_node_path58.default.join(serviceDir, "src") : serviceDir;
14466
+ const instrumentationFile = import_node_path58.default.join(baseDir, useTs ? "instrumentation.ts" : "instrumentation.js");
14467
+ const instrumentationNodeFile = import_node_path58.default.join(
14206
14468
  baseDir,
14207
14469
  useTs ? "instrumentation.node.ts" : "instrumentation.node.js"
14208
14470
  );
14209
- const instrumentationEdgeFile = import_node_path57.default.join(
14471
+ const instrumentationEdgeFile = import_node_path58.default.join(
14210
14472
  baseDir,
14211
14473
  useTs ? "instrumentation.edge.ts" : "instrumentation.edge.js"
14212
14474
  );
14213
- const envNeatFile = import_node_path57.default.join(baseDir, ".env.neat");
14475
+ const envNeatFile = import_node_path58.default.join(baseDir, ".env.neat");
14214
14476
  const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
14215
14477
  const dependencyEdits = [];
14216
14478
  for (const sdk of SDK_PACKAGES) {
@@ -14335,7 +14597,7 @@ function buildDependencyEdits(pkg, manifestPath) {
14335
14597
  return edits;
14336
14598
  }
14337
14599
  async function queueEnvNeat(serviceDir, pkg, project, generatedFiles) {
14338
- const envNeatFile = import_node_path57.default.join(serviceDir, ".env.neat");
14600
+ const envNeatFile = import_node_path58.default.join(serviceDir, ".env.neat");
14339
14601
  if (!await exists3(envNeatFile)) {
14340
14602
  generatedFiles.push({
14341
14603
  file: envNeatFile,
@@ -14370,7 +14632,7 @@ function fileImportsOtelHook(raw, specifiers) {
14370
14632
  }
14371
14633
  async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
14372
14634
  const useTs = await isTypeScriptProject(serviceDir);
14373
- const otelServerFile = import_node_path57.default.join(
14635
+ const otelServerFile = import_node_path58.default.join(
14374
14636
  serviceDir,
14375
14637
  useTs ? "app/otel.server.ts" : "app/otel.server.js"
14376
14638
  );
@@ -14427,11 +14689,11 @@ async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
14427
14689
  }
14428
14690
  async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project) {
14429
14691
  const useTs = await isTypeScriptProject(serviceDir);
14430
- const otelInitFile = import_node_path57.default.join(
14692
+ const otelInitFile = import_node_path58.default.join(
14431
14693
  serviceDir,
14432
14694
  useTs ? "src/otel-init.ts" : "src/otel-init.js"
14433
14695
  );
14434
- const resolvedHooksFile = hooksFile ?? import_node_path57.default.join(serviceDir, useTs ? "src/hooks.server.ts" : "src/hooks.server.js");
14696
+ const resolvedHooksFile = hooksFile ?? import_node_path58.default.join(serviceDir, useTs ? "src/hooks.server.ts" : "src/hooks.server.js");
14435
14697
  const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
14436
14698
  const generatedFiles = [];
14437
14699
  const entrypointEdits = [];
@@ -14493,11 +14755,11 @@ async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project)
14493
14755
  }
14494
14756
  async function planNuxt(serviceDir, pkg, manifestPath, project) {
14495
14757
  const useTs = await isTypeScriptProject(serviceDir);
14496
- const otelPluginFile = import_node_path57.default.join(
14758
+ const otelPluginFile = import_node_path58.default.join(
14497
14759
  serviceDir,
14498
14760
  useTs ? "server/plugins/otel.ts" : "server/plugins/otel.js"
14499
14761
  );
14500
- const otelInitFile = import_node_path57.default.join(
14762
+ const otelInitFile = import_node_path58.default.join(
14501
14763
  serviceDir,
14502
14764
  useTs ? "server/plugins/otel-init.ts" : "server/plugins/otel-init.js"
14503
14765
  );
@@ -14548,19 +14810,19 @@ async function planNuxt(serviceDir, pkg, manifestPath, project) {
14548
14810
  var ASTRO_MIDDLEWARE_CANDIDATES = ["src/middleware.ts", "src/middleware.js"];
14549
14811
  async function findAstroMiddleware(serviceDir) {
14550
14812
  for (const rel of ASTRO_MIDDLEWARE_CANDIDATES) {
14551
- const candidate = import_node_path57.default.join(serviceDir, rel);
14813
+ const candidate = import_node_path58.default.join(serviceDir, rel);
14552
14814
  if (await exists3(candidate)) return candidate;
14553
14815
  }
14554
14816
  return null;
14555
14817
  }
14556
14818
  async function planAstro(serviceDir, pkg, manifestPath, project) {
14557
14819
  const useTs = await isTypeScriptProject(serviceDir);
14558
- const otelInitFile = import_node_path57.default.join(
14820
+ const otelInitFile = import_node_path58.default.join(
14559
14821
  serviceDir,
14560
14822
  useTs ? "src/otel-init.ts" : "src/otel-init.js"
14561
14823
  );
14562
14824
  const existingMiddleware = await findAstroMiddleware(serviceDir);
14563
- const middlewareFile = existingMiddleware ?? import_node_path57.default.join(serviceDir, useTs ? "src/middleware.ts" : "src/middleware.js");
14825
+ const middlewareFile = existingMiddleware ?? import_node_path58.default.join(serviceDir, useTs ? "src/middleware.ts" : "src/middleware.js");
14564
14826
  const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
14565
14827
  const generatedFiles = [];
14566
14828
  const entrypointEdits = [];
@@ -14656,7 +14918,7 @@ async function findFrameworkDispatch(serviceDir, pkg, manifestPath, project) {
14656
14918
  }
14657
14919
  async function plan(serviceDir, opts) {
14658
14920
  const pkg = await readPackageJson2(serviceDir);
14659
- const manifestPath = import_node_path57.default.join(serviceDir, "package.json");
14921
+ const manifestPath = import_node_path58.default.join(serviceDir, "package.json");
14660
14922
  const project = opts?.project;
14661
14923
  const empty = {
14662
14924
  language: "javascript",
@@ -14691,8 +14953,8 @@ async function plan(serviceDir, opts) {
14691
14953
  return { ...empty, libOnly: true };
14692
14954
  }
14693
14955
  const flavor = dispatchEntry(entryFile, pkg);
14694
- const otelInitFile = import_node_path57.default.join(import_node_path57.default.dirname(entryFile), otelInitFilename(flavor));
14695
- const envNeatFile = import_node_path57.default.join(serviceDir, ".env.neat");
14956
+ const otelInitFile = import_node_path58.default.join(import_node_path58.default.dirname(entryFile), otelInitFilename(flavor));
14957
+ const envNeatFile = import_node_path58.default.join(serviceDir, ".env.neat");
14696
14958
  const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
14697
14959
  const dependencyEdits = [];
14698
14960
  for (const sdk of SDK_PACKAGES) {
@@ -14761,13 +15023,13 @@ async function plan(serviceDir, opts) {
14761
15023
  };
14762
15024
  }
14763
15025
  function isAllowedWritePath(serviceDir, target) {
14764
- const rel = import_node_path57.default.relative(serviceDir, target);
15026
+ const rel = import_node_path58.default.relative(serviceDir, target);
14765
15027
  if (rel.startsWith("..")) return false;
14766
- const base = import_node_path57.default.basename(target);
15028
+ const base = import_node_path58.default.basename(target);
14767
15029
  if (base === "package.json") return true;
14768
15030
  if (base === ".env.neat") return true;
14769
15031
  if (/^otel-init\.(?:js|cjs|mjs|ts)$/.test(base)) return true;
14770
- const relPosix = rel.split(import_node_path57.default.sep).join("/");
15032
+ const relPosix = rel.split(import_node_path58.default.sep).join("/");
14771
15033
  if (/^instrumentation(?:\.(?:node|edge))?\.(?:js|cjs|mjs|ts)$/.test(base)) {
14772
15034
  if (relPosix === base) return true;
14773
15035
  if (relPosix === `src/${base}`) return true;
@@ -14784,7 +15046,7 @@ function isAllowedWritePath(serviceDir, target) {
14784
15046
  return false;
14785
15047
  }
14786
15048
  async function writeAtomic(file, contents) {
14787
- await import_node_fs37.promises.mkdir(import_node_path57.default.dirname(file), { recursive: true });
15049
+ await import_node_fs37.promises.mkdir(import_node_path58.default.dirname(file), { recursive: true });
14788
15050
  const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
14789
15051
  await import_node_fs37.promises.writeFile(tmp, contents, "utf8");
14790
15052
  await import_node_fs37.promises.rename(tmp, file);
@@ -14968,7 +15230,7 @@ async function rollback(installPlan, originals, createdFiles) {
14968
15230
  ...removed.map((f) => `removed: ${f}`),
14969
15231
  ""
14970
15232
  ];
14971
- const rollbackPath = import_node_path57.default.join(installPlan.serviceDir, "neat-rollback.patch");
15233
+ const rollbackPath = import_node_path58.default.join(installPlan.serviceDir, "neat-rollback.patch");
14972
15234
  await import_node_fs37.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
14973
15235
  }
14974
15236
  function injectInstrumentationHook(raw) {
@@ -14999,7 +15261,7 @@ var javascriptInstaller = {
14999
15261
  // src/installers/python.ts
15000
15262
  init_cjs_shims();
15001
15263
  var import_node_fs38 = require("fs");
15002
- var import_node_path58 = __toESM(require("path"), 1);
15264
+ var import_node_path59 = __toESM(require("path"), 1);
15003
15265
  var SDK_PACKAGES2 = [
15004
15266
  { name: "opentelemetry-distro", version: ">=0.49b0" },
15005
15267
  { name: "opentelemetry-exporter-otlp", version: ">=1.28.0" }
@@ -15020,7 +15282,7 @@ async function exists4(p) {
15020
15282
  async function detect2(serviceDir) {
15021
15283
  const markers = ["requirements.txt", "pyproject.toml", "setup.py"];
15022
15284
  for (const m of markers) {
15023
- if (await exists4(import_node_path58.default.join(serviceDir, m))) return true;
15285
+ if (await exists4(import_node_path59.default.join(serviceDir, m))) return true;
15024
15286
  }
15025
15287
  return false;
15026
15288
  }
@@ -15030,7 +15292,7 @@ function reqPackageName(line) {
15030
15292
  return head.replace(/[<>=!~].*$/, "").toLowerCase();
15031
15293
  }
15032
15294
  async function planRequirementsTxtEdits(serviceDir) {
15033
- const file = import_node_path58.default.join(serviceDir, "requirements.txt");
15295
+ const file = import_node_path59.default.join(serviceDir, "requirements.txt");
15034
15296
  if (!await exists4(file)) return null;
15035
15297
  const raw = await import_node_fs38.promises.readFile(file, "utf8");
15036
15298
  const presentNames = new Set(
@@ -15040,7 +15302,7 @@ async function planRequirementsTxtEdits(serviceDir) {
15040
15302
  return { manifest: file, missing: [...missing] };
15041
15303
  }
15042
15304
  async function planProcfileEdits(serviceDir) {
15043
- const procfile = import_node_path58.default.join(serviceDir, "Procfile");
15305
+ const procfile = import_node_path59.default.join(serviceDir, "Procfile");
15044
15306
  if (!await exists4(procfile)) return [];
15045
15307
  const raw = await import_node_fs38.promises.readFile(procfile, "utf8");
15046
15308
  const edits = [];
@@ -15129,7 +15391,7 @@ async function apply2(installPlan) {
15129
15391
  if (raw === void 0) {
15130
15392
  throw new Error(`python installer: cannot read ${file} during apply`);
15131
15393
  }
15132
- const base = import_node_path58.default.basename(file);
15394
+ const base = import_node_path59.default.basename(file);
15133
15395
  if (base === "requirements.txt") {
15134
15396
  const edits = installPlan.dependencyEdits.filter((e) => e.file === file);
15135
15397
  if (edits.length > 0) {
@@ -15168,7 +15430,7 @@ async function rollback2(installPlan, originals) {
15168
15430
  ...restored.map((f) => `restored: ${f}`),
15169
15431
  ""
15170
15432
  ];
15171
- const rollbackPath = import_node_path58.default.join(installPlan.serviceDir, "neat-rollback.patch");
15433
+ const rollbackPath = import_node_path59.default.join(installPlan.serviceDir, "neat-rollback.patch");
15172
15434
  await import_node_fs38.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
15173
15435
  }
15174
15436
  var pythonInstaller = {
@@ -15295,7 +15557,7 @@ init_cjs_shims();
15295
15557
  var import_node_fs39 = require("fs");
15296
15558
  var import_node_http = __toESM(require("http"), 1);
15297
15559
  var import_node_net = __toESM(require("net"), 1);
15298
- var import_node_path59 = __toESM(require("path"), 1);
15560
+ var import_node_path60 = __toESM(require("path"), 1);
15299
15561
  var import_node_url4 = require("url");
15300
15562
  var import_node_child_process3 = require("child_process");
15301
15563
  var import_node_readline = __toESM(require("readline"), 1);
@@ -15305,7 +15567,7 @@ async function extractAndPersist(opts) {
15305
15567
  const graphKey = opts.projectExplicit ? opts.project : DEFAULT_PROJECT;
15306
15568
  resetGraph(graphKey);
15307
15569
  const graph = getGraph(graphKey);
15308
- const projectPaths = pathsForProject(graphKey, import_node_path59.default.join(opts.scanPath, "neat-out"));
15570
+ const projectPaths = pathsForProject(graphKey, import_node_path60.default.join(opts.scanPath, "neat-out"));
15309
15571
  const extraction = await extractFromDirectory(graph, opts.scanPath, {
15310
15572
  errorsPath: projectPaths.errorsPath
15311
15573
  });
@@ -15357,7 +15619,7 @@ async function applyInstallersOver(services, project, options = {}) {
15357
15619
  libOnly++;
15358
15620
  const appDeps = svc.pkg ? appFrameworkDependencies(svc.pkg) : [];
15359
15621
  if (appDeps.length > 0) {
15360
- const svcName = import_node_path59.default.basename(svc.dir);
15622
+ const svcName = import_node_path60.default.basename(svc.dir);
15361
15623
  const list = appDeps.join(", ");
15362
15624
  console.warn(
15363
15625
  `neat: runtime layer won't engage for ${svcName}: no entry point found.
@@ -15370,7 +15632,7 @@ async function applyInstallersOver(services, project, options = {}) {
15370
15632
  console.log(`skipping ${svc.dir}: browser bundle; browser-OTel support lands in a future release.`);
15371
15633
  } else if (outcome.outcome === "react-native") {
15372
15634
  reactNative++;
15373
- const svcName = import_node_path59.default.basename(svc.dir);
15635
+ const svcName = import_node_path60.default.basename(svc.dir);
15374
15636
  console.log(
15375
15637
  `neat: ${svc.dir} detected as React Native / Expo
15376
15638
  The installer doesn't cover this runtime deterministically.
@@ -15381,7 +15643,7 @@ async function applyInstallersOver(services, project, options = {}) {
15381
15643
  );
15382
15644
  } else if (outcome.outcome === "bun") {
15383
15645
  bun++;
15384
- const svcName = import_node_path59.default.basename(svc.dir);
15646
+ const svcName = import_node_path60.default.basename(svc.dir);
15385
15647
  console.log(
15386
15648
  `neat: ${svc.dir} detected as Bun
15387
15649
  The installer doesn't cover this runtime deterministically.
@@ -15392,7 +15654,7 @@ async function applyInstallersOver(services, project, options = {}) {
15392
15654
  );
15393
15655
  } else if (outcome.outcome === "deno") {
15394
15656
  deno++;
15395
- const svcName = import_node_path59.default.basename(svc.dir);
15657
+ const svcName = import_node_path60.default.basename(svc.dir);
15396
15658
  console.log(
15397
15659
  `neat: ${svc.dir} detected as Deno
15398
15660
  The installer doesn't cover this runtime deterministically.
@@ -15403,7 +15665,7 @@ async function applyInstallersOver(services, project, options = {}) {
15403
15665
  );
15404
15666
  } else if (outcome.outcome === "cloudflare-workers") {
15405
15667
  cloudflareWorkers++;
15406
- const svcName = import_node_path59.default.basename(svc.dir);
15668
+ const svcName = import_node_path60.default.basename(svc.dir);
15407
15669
  console.log(
15408
15670
  `neat: ${svc.dir} detected as Cloudflare Workers
15409
15671
  The installer doesn't cover this runtime deterministically.
@@ -15414,7 +15676,7 @@ async function applyInstallersOver(services, project, options = {}) {
15414
15676
  );
15415
15677
  } else if (outcome.outcome === "electron") {
15416
15678
  electron++;
15417
- const svcName = import_node_path59.default.basename(svc.dir);
15679
+ const svcName = import_node_path60.default.basename(svc.dir);
15418
15680
  console.log(
15419
15681
  `neat: ${svc.dir} detected as Electron
15420
15682
  The installer doesn't cover this runtime deterministically.
@@ -15427,7 +15689,7 @@ async function applyInstallersOver(services, project, options = {}) {
15427
15689
  if (svc.pkg && (outcome.outcome === "instrumented" || outcome.outcome === "already-instrumented")) {
15428
15690
  const gaps = uninstrumentedLibraries(svc.pkg);
15429
15691
  if (gaps.length > 0) {
15430
- const svcName = import_node_path59.default.basename(svc.dir);
15692
+ const svcName = import_node_path60.default.basename(svc.dir);
15431
15693
  const list = gaps.join(", ");
15432
15694
  const subject = gaps.length === 1 ? "this library" : "these libraries";
15433
15695
  const aux = gaps.length === 1 ? "isn't" : "aren't";
@@ -15633,8 +15895,8 @@ async function persistedPortsFor(scanPath) {
15633
15895
  return { rest: record.ports.rest, otlp: record.ports.otlp, web: record.ports.web };
15634
15896
  }
15635
15897
  async function acquireSpawnLock(scanPath) {
15636
- const lockPath = import_node_path59.default.join(scanPath, "neat-out", "daemon.spawn.lock");
15637
- await import_node_fs39.promises.mkdir(import_node_path59.default.dirname(lockPath), { recursive: true });
15898
+ const lockPath = import_node_path60.default.join(scanPath, "neat-out", "daemon.spawn.lock");
15899
+ await import_node_fs39.promises.mkdir(import_node_path60.default.dirname(lockPath), { recursive: true });
15638
15900
  const STALE_LOCK_MS = 6e4;
15639
15901
  try {
15640
15902
  const fd = await import_node_fs39.promises.open(lockPath, "wx");
@@ -15679,13 +15941,13 @@ async function healthIsForProject(restPort, project) {
15679
15941
  return false;
15680
15942
  }
15681
15943
  function daemonLogPath(projectPath2) {
15682
- return import_node_path59.default.join(projectPath2, "neat-out", "daemon.log");
15944
+ return import_node_path60.default.join(projectPath2, "neat-out", "daemon.log");
15683
15945
  }
15684
15946
  function spawnDaemonDetached(spec) {
15685
- const here = import_node_path59.default.dirname((0, import_node_url4.fileURLToPath)(importMetaUrl));
15947
+ const here = import_node_path60.default.dirname((0, import_node_url4.fileURLToPath)(importMetaUrl));
15686
15948
  const candidates = [
15687
- import_node_path59.default.join(here, "neatd.cjs"),
15688
- import_node_path59.default.join(here, "neatd.js")
15949
+ import_node_path60.default.join(here, "neatd.cjs"),
15950
+ import_node_path60.default.join(here, "neatd.js")
15689
15951
  ];
15690
15952
  let entry2 = null;
15691
15953
  const fsSync = require("fs");
@@ -15715,7 +15977,7 @@ function spawnDaemonDetached(spec) {
15715
15977
  let logFd = null;
15716
15978
  if (spec) {
15717
15979
  const logPath = daemonLogPath(spec.projectPath);
15718
- fsSync.mkdirSync(import_node_path59.default.dirname(logPath), { recursive: true });
15980
+ fsSync.mkdirSync(import_node_path60.default.dirname(logPath), { recursive: true });
15719
15981
  logFd = fsSync.openSync(logPath, "a");
15720
15982
  }
15721
15983
  const child = (0, import_node_child_process3.spawn)(process.execPath, [entry2, "start"], {
@@ -15914,7 +16176,7 @@ async function runOrchestrator(opts) {
15914
16176
  result.steps.browser = openBrowser(dashboardUrl);
15915
16177
  }
15916
16178
  const daemonRunning = result.steps.daemon === "spawned" || result.steps.daemon === "already-running";
15917
- const daemonLog = daemonRunning ? import_node_path59.default.relative(opts.scanPath, daemonLogPath(opts.scanPath)) : null;
16179
+ const daemonLog = daemonRunning ? import_node_path60.default.relative(opts.scanPath, daemonLogPath(opts.scanPath)) : null;
15918
16180
  printSummary(result, graph, dashboardUrl, daemonLog);
15919
16181
  return result;
15920
16182
  }
@@ -16371,7 +16633,7 @@ async function runConnectorCommand(rawArgs, deps = {}) {
16371
16633
 
16372
16634
  // src/hooks-cli.ts
16373
16635
  init_cjs_shims();
16374
- var import_node_path60 = __toESM(require("path"), 1);
16636
+ var import_node_path61 = __toESM(require("path"), 1);
16375
16637
  var import_node_os5 = __toESM(require("os"), 1);
16376
16638
  var import_node_fs40 = require("fs");
16377
16639
  var import_node_url5 = require("url");
@@ -16380,14 +16642,14 @@ var GUIDE_FILENAME = "GRAPH_FIRST.md";
16380
16642
  var GUIDE_INSTALL_NAME = "neat-graph-first.md";
16381
16643
  var HOOK_MATCHER = "Grep|Glob|Bash";
16382
16644
  function moduleDir() {
16383
- return typeof __dirname !== "undefined" ? __dirname : import_node_path60.default.dirname((0, import_node_url5.fileURLToPath)(importMetaUrl));
16645
+ return typeof __dirname !== "undefined" ? __dirname : import_node_path61.default.dirname((0, import_node_url5.fileURLToPath)(importMetaUrl));
16384
16646
  }
16385
16647
  async function readSkillAsset(rel) {
16386
16648
  const here = moduleDir();
16387
16649
  const candidates = [
16388
- import_node_path60.default.resolve(here, "../../claude-skill", rel),
16389
- import_node_path60.default.resolve(here, "../../../claude-skill", rel),
16390
- import_node_path60.default.resolve(here, "../claude-skill", rel)
16650
+ import_node_path61.default.resolve(here, "../../claude-skill", rel),
16651
+ import_node_path61.default.resolve(here, "../../../claude-skill", rel),
16652
+ import_node_path61.default.resolve(here, "../claude-skill", rel)
16391
16653
  ];
16392
16654
  for (const candidate of candidates) {
16393
16655
  try {
@@ -16401,17 +16663,17 @@ async function readSkillAsset(rel) {
16401
16663
  }
16402
16664
  function neatHome3() {
16403
16665
  const override = process.env.NEAT_HOME;
16404
- if (override && override.length > 0) return import_node_path60.default.resolve(override);
16405
- return import_node_path60.default.join(import_node_os5.default.homedir(), ".neat");
16666
+ if (override && override.length > 0) return import_node_path61.default.resolve(override);
16667
+ return import_node_path61.default.join(import_node_os5.default.homedir(), ".neat");
16406
16668
  }
16407
16669
  function claudeSettingsPath() {
16408
16670
  const override = process.env.NEAT_CLAUDE_SETTINGS;
16409
- if (override && override.length > 0) return import_node_path60.default.resolve(override);
16671
+ if (override && override.length > 0) return import_node_path61.default.resolve(override);
16410
16672
  const home = process.env.HOME ?? process.env.USERPROFILE ?? import_node_os5.default.homedir();
16411
- return import_node_path60.default.join(home, ".claude", "settings.json");
16673
+ return import_node_path61.default.join(home, ".claude", "settings.json");
16412
16674
  }
16413
16675
  function installedHookPath() {
16414
- return import_node_path60.default.join(neatHome3(), "hooks", HOOK_FILENAME);
16676
+ return import_node_path61.default.join(neatHome3(), "hooks", HOOK_FILENAME);
16415
16677
  }
16416
16678
  function isNeatSearchEntry(entry2) {
16417
16679
  return (entry2.hooks ?? []).some(
@@ -16444,9 +16706,9 @@ async function runHooks(opts) {
16444
16706
  const hookScript = await readSkillAsset(`hooks/${HOOK_FILENAME}`);
16445
16707
  const guide = await readSkillAsset(GUIDE_FILENAME);
16446
16708
  const scriptPath = installedHookPath();
16447
- await import_node_fs40.promises.mkdir(import_node_path60.default.dirname(scriptPath), { recursive: true });
16709
+ await import_node_fs40.promises.mkdir(import_node_path61.default.dirname(scriptPath), { recursive: true });
16448
16710
  await import_node_fs40.promises.writeFile(scriptPath, hookScript, { mode: 493 });
16449
- const guidePath = import_node_path60.default.join(neatHome3(), GUIDE_INSTALL_NAME);
16711
+ const guidePath = import_node_path61.default.join(neatHome3(), GUIDE_INSTALL_NAME);
16450
16712
  await import_node_fs40.promises.writeFile(guidePath, guide, "utf8");
16451
16713
  const settingsFile = claudeSettingsPath();
16452
16714
  let settings = {};
@@ -16473,7 +16735,7 @@ async function runHooks(opts) {
16473
16735
  ...settings,
16474
16736
  hooks: { ...hooks, PreToolUse: preToolUse }
16475
16737
  };
16476
- await import_node_fs40.promises.mkdir(import_node_path60.default.dirname(settingsFile), { recursive: true });
16738
+ await import_node_fs40.promises.mkdir(import_node_path61.default.dirname(settingsFile), { recursive: true });
16477
16739
  await import_node_fs40.promises.writeFile(settingsFile, JSON.stringify(merged, null, 2) + "\n", "utf8");
16478
16740
  console.log(`neat hooks: installed the search-nudge hook`);
16479
16741
  console.log(` script: ${scriptPath}`);
@@ -16545,11 +16807,11 @@ async function runHooksCommand(args) {
16545
16807
 
16546
16808
  // src/cli-verbs.ts
16547
16809
  init_cjs_shims();
16548
- var import_node_path61 = __toESM(require("path"), 1);
16810
+ var import_node_path62 = __toESM(require("path"), 1);
16549
16811
 
16550
16812
  // src/cli-client.ts
16551
16813
  init_cjs_shims();
16552
- var import_types49 = require("@neat.is/types");
16814
+ var import_types50 = require("@neat.is/types");
16553
16815
  var HttpError = class extends Error {
16554
16816
  constructor(status2, message, responseBody = "") {
16555
16817
  super(message);
@@ -16574,10 +16836,10 @@ function createHttpClient(baseUrl, bearerToken) {
16574
16836
  const root = baseUrl.replace(/\/$/, "");
16575
16837
  const authHeader = bearerToken && bearerToken.length > 0 ? { authorization: `Bearer ${bearerToken}` } : {};
16576
16838
  return {
16577
- async get(path63) {
16839
+ async get(path64) {
16578
16840
  let res;
16579
16841
  try {
16580
- res = await fetch(`${root}${path63}`, {
16842
+ res = await fetch(`${root}${path64}`, {
16581
16843
  headers: { ...authHeader }
16582
16844
  });
16583
16845
  } catch (err) {
@@ -16589,16 +16851,16 @@ function createHttpClient(baseUrl, bearerToken) {
16589
16851
  const body = await res.text().catch(() => "");
16590
16852
  throw new HttpError(
16591
16853
  res.status,
16592
- `${res.status} ${res.statusText} on GET ${path63}: ${body}`,
16854
+ `${res.status} ${res.statusText} on GET ${path64}: ${body}`,
16593
16855
  body
16594
16856
  );
16595
16857
  }
16596
16858
  return await res.json();
16597
16859
  },
16598
- async post(path63, body) {
16860
+ async post(path64, body) {
16599
16861
  let res;
16600
16862
  try {
16601
- res = await fetch(`${root}${path63}`, {
16863
+ res = await fetch(`${root}${path64}`, {
16602
16864
  method: "POST",
16603
16865
  headers: { "content-type": "application/json", ...authHeader },
16604
16866
  body: JSON.stringify(body)
@@ -16612,7 +16874,7 @@ function createHttpClient(baseUrl, bearerToken) {
16612
16874
  const text = await res.text().catch(() => "");
16613
16875
  throw new HttpError(
16614
16876
  res.status,
16615
- `${res.status} ${res.statusText} on POST ${path63}: ${text}`,
16877
+ `${res.status} ${res.statusText} on POST ${path64}: ${text}`,
16616
16878
  text
16617
16879
  );
16618
16880
  }
@@ -16626,12 +16888,12 @@ function projectPath(project, suffix) {
16626
16888
  }
16627
16889
  async function runRootCause(client, input) {
16628
16890
  const qs = input.errorId ? `?errorId=${encodeURIComponent(input.errorId)}` : "";
16629
- const path63 = projectPath(
16891
+ const path64 = projectPath(
16630
16892
  input.project,
16631
16893
  `/graph/root-cause/${encodeURIComponent(input.errorNode)}${qs}`
16632
16894
  );
16633
16895
  try {
16634
- const result = await client.get(path63);
16896
+ const result = await client.get(path64);
16635
16897
  const arrowPath = result.traversalPath.join(" \u2190 ");
16636
16898
  const provenances = result.edgeProvenances.length ? result.edgeProvenances.join(", ") : "(direct, no edges traversed)";
16637
16899
  const summary = `Root cause for ${input.errorNode} is ${result.rootCauseNode}. ` + result.rootCauseReason + (result.fixRecommendation ? ` Recommended fix: ${result.fixRecommendation}.` : "");
@@ -16657,12 +16919,12 @@ async function runRootCause(client, input) {
16657
16919
  }
16658
16920
  async function runBlastRadius(client, input) {
16659
16921
  const qs = input.depth !== void 0 ? `?depth=${input.depth}` : "";
16660
- const path63 = projectPath(
16922
+ const path64 = projectPath(
16661
16923
  input.project,
16662
16924
  `/graph/blast-radius/${encodeURIComponent(input.nodeId)}${qs}`
16663
16925
  );
16664
16926
  try {
16665
- const result = await client.get(path63);
16927
+ const result = await client.get(path64);
16666
16928
  if (result.totalAffected === 0) {
16667
16929
  return {
16668
16930
  summary: `${result.origin} has no dependents. Nothing else would break if it failed.`
@@ -16691,17 +16953,17 @@ async function runBlastRadius(client, input) {
16691
16953
  }
16692
16954
  }
16693
16955
  function formatBlastEntry(n) {
16694
- const tag = n.edgeProvenance === import_types49.Provenance.STALE ? " [STALE \u2014 last seen too long ago]" : "";
16956
+ const tag = n.edgeProvenance === import_types50.Provenance.STALE ? " [STALE \u2014 last seen too long ago]" : "";
16695
16957
  return ` \u2022 ${n.nodeId} (distance ${n.distance}, ${n.edgeProvenance})${tag}`;
16696
16958
  }
16697
16959
  async function runDependencies(client, input) {
16698
16960
  const depth = input.depth ?? 3;
16699
- const path63 = projectPath(
16961
+ const path64 = projectPath(
16700
16962
  input.project,
16701
16963
  `/graph/dependencies/${encodeURIComponent(input.nodeId)}?depth=${depth}`
16702
16964
  );
16703
16965
  try {
16704
- const result = await client.get(path63);
16966
+ const result = await client.get(path64);
16705
16967
  if (result.total === 0) {
16706
16968
  return {
16707
16969
  summary: depth === 1 ? `${input.nodeId} has no direct dependencies in the graph.` : `${input.nodeId} has no dependencies (BFS to depth ${depth}).`
@@ -16748,7 +17010,7 @@ async function runObservedDependencies(client, input) {
16748
17010
  if (result.observed) {
16749
17011
  return {
16750
17012
  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.`,
16751
- provenance: import_types49.Provenance.OBSERVED
17013
+ provenance: import_types50.Provenance.OBSERVED
16752
17014
  };
16753
17015
  }
16754
17016
  const note = result.hasExtractedOutbound ? " Static (EXTRACTED) dependencies exist but no runtime traffic has been seen \u2014 is OTel running?" : "";
@@ -16758,7 +17020,7 @@ async function runObservedDependencies(client, input) {
16758
17020
  return {
16759
17021
  summary: `${input.nodeId} has ${result.dependencies.length} runtime dependenc${result.dependencies.length === 1 ? "y" : "ies"} confirmed by OTel.`,
16760
17022
  block: blockLines.join("\n"),
16761
- provenance: import_types49.Provenance.OBSERVED
17023
+ provenance: import_types50.Provenance.OBSERVED
16762
17024
  };
16763
17025
  } catch (err) {
16764
17026
  if (err instanceof HttpError && err.status === 404) {
@@ -16793,9 +17055,9 @@ function formatDuration(ms) {
16793
17055
  return `${Math.round(h / 24)}d`;
16794
17056
  }
16795
17057
  async function runIncidents(client, input) {
16796
- const path63 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
17058
+ const path64 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
16797
17059
  try {
16798
- const body = await client.get(path63);
17060
+ const body = await client.get(path64);
16799
17061
  const events = body.events;
16800
17062
  if (events.length === 0) {
16801
17063
  return {
@@ -16812,7 +17074,7 @@ async function runIncidents(client, input) {
16812
17074
  return {
16813
17075
  summary: `${target} has ${body.total} recorded incident${body.total === 1 ? "" : "s"}; showing the ${ordered.length} most recent.`,
16814
17076
  block: blockLines.join("\n"),
16815
- provenance: import_types49.Provenance.OBSERVED
17077
+ provenance: import_types50.Provenance.OBSERVED
16816
17078
  };
16817
17079
  } catch (err) {
16818
17080
  if (err instanceof HttpError && err.status === 404) {
@@ -16921,7 +17183,7 @@ async function runStaleEdges(client, input) {
16921
17183
  return {
16922
17184
  summary: `${events.length} stale-edge transition${events.length === 1 ? "" : "s"} recorded${input.edgeType ? ` for ${input.edgeType}` : ""}.`,
16923
17185
  block: blockLines.join("\n"),
16924
- provenance: import_types49.Provenance.STALE
17186
+ provenance: import_types50.Provenance.STALE
16925
17187
  };
16926
17188
  }
16927
17189
  async function runPolicies(client, input) {
@@ -17085,7 +17347,7 @@ async function resolveProjectEntry(opts) {
17085
17347
  const cwd = opts.cwd ?? process.cwd();
17086
17348
  const resolvedCwd = await normalizeProjectPath(cwd);
17087
17349
  for (const entry2 of entries) {
17088
- if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${import_node_path61.default.sep}`)) {
17350
+ if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${import_node_path62.default.sep}`)) {
17089
17351
  return entry2;
17090
17352
  }
17091
17353
  }
@@ -17238,7 +17500,7 @@ async function runSync(opts) {
17238
17500
  }
17239
17501
 
17240
17502
  // src/cli.ts
17241
- var import_types50 = require("@neat.is/types");
17503
+ var import_types51 = require("@neat.is/types");
17242
17504
  function isNpxInvocation() {
17243
17505
  if (process.env.npm_command === "exec") return true;
17244
17506
  const execpath = process.env.npm_execpath ?? "";
@@ -17549,12 +17811,12 @@ async function runInit(opts) {
17549
17811
  printDiscoveryReport(opts, services);
17550
17812
  const sections = opts.noInstall ? [] : await buildPatchSections(services, opts.project);
17551
17813
  const patch = renderPatch(sections);
17552
- const patchPath = import_node_path62.default.join(opts.scanPath, "neat.patch");
17814
+ const patchPath = import_node_path63.default.join(opts.scanPath, "neat.patch");
17553
17815
  if (opts.dryRun) {
17554
17816
  await import_node_fs41.promises.writeFile(patchPath, patch, "utf8");
17555
17817
  written.push(patchPath);
17556
17818
  console.log(`dry-run: patch written to ${patchPath}`);
17557
- const gitignorePath = import_node_path62.default.join(opts.scanPath, ".gitignore");
17819
+ const gitignorePath = import_node_path63.default.join(opts.scanPath, ".gitignore");
17558
17820
  const gitignoreExists = await import_node_fs41.promises.stat(gitignorePath).then(() => true).catch(() => false);
17559
17821
  const verb = gitignoreExists ? "append" : "create";
17560
17822
  console.log(`dry-run: would ${verb} ${gitignorePath} (add neat-out/)`);
@@ -17566,9 +17828,9 @@ async function runInit(opts) {
17566
17828
  const graph = getGraph(graphKey);
17567
17829
  const projectPaths = pathsForProject(
17568
17830
  graphKey,
17569
- import_node_path62.default.join(opts.scanPath, "neat-out")
17831
+ import_node_path63.default.join(opts.scanPath, "neat-out")
17570
17832
  );
17571
- const errorsPath = import_node_path62.default.join(import_node_path62.default.dirname(opts.outPath), import_node_path62.default.basename(projectPaths.errorsPath));
17833
+ const errorsPath = import_node_path63.default.join(import_node_path63.default.dirname(opts.outPath), import_node_path63.default.basename(projectPaths.errorsPath));
17572
17834
  const result = await extractFromDirectory(graph, opts.scanPath, { errorsPath });
17573
17835
  await saveGraphToDisk(graph, opts.outPath);
17574
17836
  written.push(opts.outPath);
@@ -17687,9 +17949,9 @@ var CLAUDE_SKILL_CONFIG = {
17687
17949
  };
17688
17950
  function claudeConfigPath() {
17689
17951
  const override = process.env.NEAT_CLAUDE_CONFIG;
17690
- if (override && override.length > 0) return import_node_path62.default.resolve(override);
17952
+ if (override && override.length > 0) return import_node_path63.default.resolve(override);
17691
17953
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
17692
- return import_node_path62.default.join(home, ".claude.json");
17954
+ return import_node_path63.default.join(home, ".claude.json");
17693
17955
  }
17694
17956
  async function runSkill(opts) {
17695
17957
  const snippet2 = JSON.stringify(CLAUDE_SKILL_CONFIG, null, 2) + "\n";
@@ -17713,7 +17975,7 @@ async function runSkill(opts) {
17713
17975
  ...existing,
17714
17976
  mcpServers: { ...mcp, neat: CLAUDE_SKILL_CONFIG.mcpServers.neat }
17715
17977
  };
17716
- await import_node_fs41.promises.mkdir(import_node_path62.default.dirname(target), { recursive: true });
17978
+ await import_node_fs41.promises.mkdir(import_node_path63.default.dirname(target), { recursive: true });
17717
17979
  await import_node_fs41.promises.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
17718
17980
  console.log(`neat skill: wrote mcpServers.neat to ${target}`);
17719
17981
  console.log("restart Claude Code to pick up the new MCP server.");
@@ -17779,12 +18041,12 @@ async function main() {
17779
18041
  console.error("neat init: --apply and --dry-run are mutually exclusive");
17780
18042
  process.exit(2);
17781
18043
  }
17782
- const scanPath = import_node_path62.default.resolve(target);
18044
+ const scanPath = import_node_path63.default.resolve(target);
17783
18045
  const projectExplicit = parsed.project !== null;
17784
- const projectName = projectExplicit ? project : import_node_path62.default.basename(scanPath);
18046
+ const projectName = projectExplicit ? project : import_node_path63.default.basename(scanPath);
17785
18047
  const projectKey = projectExplicit ? project : DEFAULT_PROJECT;
17786
- const fallback = pathsForProject(projectKey, import_node_path62.default.join(scanPath, "neat-out")).snapshotPath;
17787
- const outPath = import_node_path62.default.resolve(process.env.NEAT_OUT_PATH ?? fallback);
18048
+ const fallback = pathsForProject(projectKey, import_node_path63.default.join(scanPath, "neat-out")).snapshotPath;
18049
+ const outPath = import_node_path63.default.resolve(process.env.NEAT_OUT_PATH ?? fallback);
17788
18050
  const result = await runInit({
17789
18051
  scanPath,
17790
18052
  outPath,
@@ -17805,21 +18067,21 @@ async function main() {
17805
18067
  usage2();
17806
18068
  process.exit(2);
17807
18069
  }
17808
- const scanPath = import_node_path62.default.resolve(target);
18070
+ const scanPath = import_node_path63.default.resolve(target);
17809
18071
  const stat = await import_node_fs41.promises.stat(scanPath).catch(() => null);
17810
18072
  if (!stat || !stat.isDirectory()) {
17811
18073
  console.error(`neat watch: ${scanPath} is not a directory`);
17812
18074
  process.exit(2);
17813
18075
  }
17814
- const projectPaths = pathsForProject(project, import_node_path62.default.join(scanPath, "neat-out"));
17815
- const outPath = import_node_path62.default.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
17816
- const errorsPath = import_node_path62.default.resolve(
17817
- process.env.NEAT_ERRORS_PATH ?? import_node_path62.default.join(import_node_path62.default.dirname(outPath), import_node_path62.default.basename(projectPaths.errorsPath))
18076
+ const projectPaths = pathsForProject(project, import_node_path63.default.join(scanPath, "neat-out"));
18077
+ const outPath = import_node_path63.default.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
18078
+ const errorsPath = import_node_path63.default.resolve(
18079
+ process.env.NEAT_ERRORS_PATH ?? import_node_path63.default.join(import_node_path63.default.dirname(outPath), import_node_path63.default.basename(projectPaths.errorsPath))
17818
18080
  );
17819
- const staleEventsPath = import_node_path62.default.resolve(
17820
- process.env.NEAT_STALE_EVENTS_PATH ?? import_node_path62.default.join(import_node_path62.default.dirname(outPath), import_node_path62.default.basename(projectPaths.staleEventsPath))
18081
+ const staleEventsPath = import_node_path63.default.resolve(
18082
+ process.env.NEAT_STALE_EVENTS_PATH ?? import_node_path63.default.join(import_node_path63.default.dirname(outPath), import_node_path63.default.basename(projectPaths.staleEventsPath))
17821
18083
  );
17822
- const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? import_node_path62.default.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
18084
+ const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? import_node_path63.default.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
17823
18085
  const handle = await startWatch(getGraph(project), {
17824
18086
  scanPath,
17825
18087
  outPath,
@@ -18002,11 +18264,11 @@ async function main() {
18002
18264
  process.exit(1);
18003
18265
  }
18004
18266
  async function tryOrchestrator(cmd, parsed) {
18005
- const scanPath = import_node_path62.default.resolve(cmd);
18267
+ const scanPath = import_node_path63.default.resolve(cmd);
18006
18268
  const stat = await import_node_fs41.promises.stat(scanPath).catch(() => null);
18007
18269
  if (!stat || !stat.isDirectory()) return null;
18008
18270
  const projectExplicit = parsed.project !== null;
18009
- const projectName = projectExplicit ? parsed.project : import_node_path62.default.basename(scanPath);
18271
+ const projectName = projectExplicit ? parsed.project : import_node_path63.default.basename(scanPath);
18010
18272
  const result = await runOrchestrator({
18011
18273
  scanPath,
18012
18274
  project: projectName,
@@ -18195,10 +18457,10 @@ async function runQueryVerb(cmd, parsed) {
18195
18457
  const parts = parsed.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
18196
18458
  const out = [];
18197
18459
  for (const p of parts) {
18198
- const r = import_types50.DivergenceTypeSchema.safeParse(p);
18460
+ const r = import_types51.DivergenceTypeSchema.safeParse(p);
18199
18461
  if (!r.success) {
18200
18462
  console.error(
18201
- `neat divergences: unknown --type "${p}". allowed: ${import_types50.DivergenceTypeSchema.options.join(", ")}`
18463
+ `neat divergences: unknown --type "${p}". allowed: ${import_types51.DivergenceTypeSchema.options.join(", ")}`
18202
18464
  );
18203
18465
  return 2;
18204
18466
  }