@neat.is/core 0.4.28-dev.20260712 → 0.4.28-dev.20260714

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/server.cjs CHANGED
@@ -59,8 +59,8 @@ function mountBearerAuth(app, opts) {
59
59
  ]);
60
60
  const publicRead = opts.publicRead === true;
61
61
  app.addHook("preHandler", (req, reply, done) => {
62
- const path47 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
63
- if (exactUnauthPaths.has(path47) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path47)) {
62
+ const path51 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
63
+ if (exactUnauthPaths.has(path51) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path51)) {
64
64
  done();
65
65
  return;
66
66
  }
@@ -192,8 +192,8 @@ function reshapeGrpcRequest(req) {
192
192
  };
193
193
  }
194
194
  function resolveProtoRoot() {
195
- const here = import_node_path43.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
196
- return import_node_path43.default.resolve(here, "..", "proto");
195
+ const here = import_node_path47.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
196
+ return import_node_path47.default.resolve(here, "..", "proto");
197
197
  }
198
198
  function loadTraceService() {
199
199
  const protoRoot = resolveProtoRoot();
@@ -261,13 +261,13 @@ async function startOtelGrpcReceiver(opts) {
261
261
  })
262
262
  };
263
263
  }
264
- var import_node_url, import_node_path43, import_node_crypto2, grpc, protoLoader;
264
+ var import_node_url, import_node_path47, import_node_crypto2, grpc, protoLoader;
265
265
  var init_otel_grpc = __esm({
266
266
  "src/otel-grpc.ts"() {
267
267
  "use strict";
268
268
  init_cjs_shims();
269
269
  import_node_url = require("url");
270
- import_node_path43 = __toESM(require("path"), 1);
270
+ import_node_path47 = __toESM(require("path"), 1);
271
271
  import_node_crypto2 = require("crypto");
272
272
  grpc = __toESM(require("@grpc/grpc-js"), 1);
273
273
  protoLoader = __toESM(require("@grpc/proto-loader"), 1);
@@ -360,8 +360,8 @@ function websocketChannelPathOf(attrs) {
360
360
  const v = attrs[key];
361
361
  if (typeof v === "string" && v.length > 0) {
362
362
  const q = v.indexOf("?");
363
- const path47 = q === -1 ? v : v.slice(0, q);
364
- if (path47.length > 0) return path47;
363
+ const path51 = q === -1 ? v : v.slice(0, q);
364
+ if (path51.length > 0) return path51;
365
365
  }
366
366
  }
367
367
  return void 0;
@@ -415,10 +415,10 @@ function parseOtlpRequest(body) {
415
415
  return out;
416
416
  }
417
417
  function loadProtoRoot() {
418
- const here = import_node_path44.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
419
- const protoRoot = import_node_path44.default.resolve(here, "..", "proto");
418
+ const here = import_node_path48.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
419
+ const protoRoot = import_node_path48.default.resolve(here, "..", "proto");
420
420
  const root = new import_protobufjs.default.Root();
421
- root.resolvePath = (_origin, target) => import_node_path44.default.resolve(protoRoot, target);
421
+ root.resolvePath = (_origin, target) => import_node_path48.default.resolve(protoRoot, target);
422
422
  root.loadSync(
423
423
  "opentelemetry/proto/collector/trace/v1/trace_service.proto",
424
424
  { keepCase: true }
@@ -630,12 +630,12 @@ async function buildOtelReceiver(opts) {
630
630
  };
631
631
  return decorated;
632
632
  }
633
- var import_node_path44, import_node_url2, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody;
633
+ var import_node_path48, import_node_url2, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody;
634
634
  var init_otel = __esm({
635
635
  "src/otel.ts"() {
636
636
  "use strict";
637
637
  init_cjs_shims();
638
- import_node_path44 = __toESM(require("path"), 1);
638
+ import_node_path48 = __toESM(require("path"), 1);
639
639
  import_node_url2 = require("url");
640
640
  import_fastify2 = __toESM(require("fastify"), 1);
641
641
  import_protobufjs = __toESM(require("protobufjs"), 1);
@@ -651,7 +651,7 @@ var init_otel = __esm({
651
651
 
652
652
  // src/server.ts
653
653
  init_cjs_shims();
654
- var import_node_path46 = __toESM(require("path"), 1);
654
+ var import_node_path50 = __toESM(require("path"), 1);
655
655
 
656
656
  // src/graph.ts
657
657
  init_cjs_shims();
@@ -675,7 +675,7 @@ function getGraph(project = DEFAULT_PROJECT) {
675
675
  init_cjs_shims();
676
676
  var import_fastify = __toESM(require("fastify"), 1);
677
677
  var import_cors = __toESM(require("@fastify/cors"), 1);
678
- var import_types30 = require("@neat.is/types");
678
+ var import_types33 = require("@neat.is/types");
679
679
 
680
680
  // src/extend/index.ts
681
681
  init_cjs_shims();
@@ -1418,19 +1418,19 @@ function confidenceFromMix(edges, now = Date.now()) {
1418
1418
  function longestIncomingWalk(graph, start, maxDepth) {
1419
1419
  let best = { path: [start], edges: [] };
1420
1420
  const visited = /* @__PURE__ */ new Set([start]);
1421
- function step(node, path47, edges) {
1422
- if (path47.length > best.path.length) {
1423
- best = { path: [...path47], edges: [...edges] };
1421
+ function step(node, path51, edges) {
1422
+ if (path51.length > best.path.length) {
1423
+ best = { path: [...path51], edges: [...edges] };
1424
1424
  }
1425
- if (path47.length - 1 >= maxDepth) return;
1425
+ if (path51.length - 1 >= maxDepth) return;
1426
1426
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
1427
1427
  for (const [srcId, edge] of incoming) {
1428
1428
  if (visited.has(srcId)) continue;
1429
1429
  visited.add(srcId);
1430
- path47.push(srcId);
1430
+ path51.push(srcId);
1431
1431
  edges.push(edge);
1432
- step(srcId, path47, edges);
1433
- path47.pop();
1432
+ step(srcId, path51, edges);
1433
+ path51.pop();
1434
1434
  edges.pop();
1435
1435
  visited.delete(srcId);
1436
1436
  }
@@ -1624,26 +1624,26 @@ function dominantFailingCall(graph, serviceId4, visited) {
1624
1624
  return best;
1625
1625
  }
1626
1626
  function followFailingCallChain(graph, originServiceId, maxDepth) {
1627
- const path47 = [originServiceId];
1627
+ const path51 = [originServiceId];
1628
1628
  const edges = [];
1629
1629
  const visited = /* @__PURE__ */ new Set([originServiceId]);
1630
1630
  let current = originServiceId;
1631
1631
  for (let depth = 0; depth < maxDepth; depth++) {
1632
1632
  const hop = dominantFailingCall(graph, current, visited);
1633
1633
  if (!hop) break;
1634
- path47.push(hop.nextService);
1634
+ path51.push(hop.nextService);
1635
1635
  edges.push(hop.edge);
1636
1636
  visited.add(hop.nextService);
1637
1637
  current = hop.nextService;
1638
1638
  }
1639
1639
  if (edges.length === 0) return null;
1640
- return { path: path47, edges, culprit: current };
1640
+ return { path: path51, edges, culprit: current };
1641
1641
  }
1642
1642
  function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1643
1643
  const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
1644
1644
  if (!chain) return null;
1645
1645
  const culprit = chain.culprit;
1646
- const path47 = [...chain.path];
1646
+ const path51 = [...chain.path];
1647
1647
  const edgeProvenances = chain.edges.map((e) => e.provenance);
1648
1648
  const baseConfidence = confidenceFromMix(chain.edges);
1649
1649
  const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
@@ -1651,14 +1651,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1651
1651
  if (loc) {
1652
1652
  let rootCauseNode = culprit;
1653
1653
  if (loc.fileNode) {
1654
- path47.push(loc.fileNode);
1654
+ path51.push(loc.fileNode);
1655
1655
  edgeProvenances.push(import_types.Provenance.OBSERVED);
1656
1656
  rootCauseNode = loc.fileNode;
1657
1657
  }
1658
1658
  return import_types.RootCauseResultSchema.parse({
1659
1659
  rootCauseNode,
1660
1660
  rootCauseReason: loc.rootCauseReason,
1661
- traversalPath: path47,
1661
+ traversalPath: path51,
1662
1662
  edgeProvenances,
1663
1663
  confidence,
1664
1664
  ...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
@@ -1670,7 +1670,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1670
1670
  return import_types.RootCauseResultSchema.parse({
1671
1671
  rootCauseNode: culprit,
1672
1672
  rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
1673
- traversalPath: path47,
1673
+ traversalPath: path51,
1674
1674
  edgeProvenances,
1675
1675
  confidence,
1676
1676
  fixRecommendation: `Inspect ${culpritName}'s failing handler`
@@ -6966,6 +6966,39 @@ function classifyImage(image) {
6966
6966
  if (last.startsWith("memcached")) return "memcached";
6967
6967
  return "container";
6968
6968
  }
6969
+ function lineContaining(raw, needle) {
6970
+ if (!needle) return void 0;
6971
+ const idx = raw.indexOf(needle);
6972
+ if (idx === -1) return void 0;
6973
+ let line = 1;
6974
+ for (let i = 0; i < idx; i++) if (raw[i] === "\n") line++;
6975
+ return line;
6976
+ }
6977
+ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provider, evidenceFile, line) {
6978
+ let nodesAdded = 0;
6979
+ let edgesAdded = 0;
6980
+ const node = makeInfraNode(kind, name, provider);
6981
+ if (!graph.hasNode(node.id)) {
6982
+ graph.addNode(node.id, node);
6983
+ nodesAdded++;
6984
+ }
6985
+ if (node.id === anchorId) return { nodesAdded, edgesAdded };
6986
+ const edgeId = (0, import_types5.extractedEdgeId)(anchorId, node.id, edgeType);
6987
+ if (!graph.hasEdge(edgeId)) {
6988
+ const edge = {
6989
+ id: edgeId,
6990
+ source: anchorId,
6991
+ target: node.id,
6992
+ type: edgeType,
6993
+ provenance: import_types22.Provenance.EXTRACTED,
6994
+ confidence: (0, import_types22.confidenceForExtracted)("structural"),
6995
+ evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
6996
+ };
6997
+ graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
6998
+ edgesAdded++;
6999
+ }
7000
+ return { nodesAdded, edgesAdded };
7001
+ }
6969
7002
 
6970
7003
  // src/extract/infra/docker-compose.ts
6971
7004
  function dependsOnList(value) {
@@ -7325,7 +7358,7 @@ async function readWranglerConfig(dir) {
7325
7358
  }
7326
7359
  return null;
7327
7360
  }
7328
- function lineContaining(raw, needle) {
7361
+ function lineContaining2(raw, needle) {
7329
7362
  if (!needle) return void 0;
7330
7363
  const idx = raw.indexOf(needle);
7331
7364
  if (idx === -1) return void 0;
@@ -7449,7 +7482,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7449
7482
  "cloudflare-route",
7450
7483
  route,
7451
7484
  evidenceFile,
7452
- lineContaining(raw, route)
7485
+ lineContaining2(raw, route)
7453
7486
  );
7454
7487
  nodesAdded += result.nodesAdded;
7455
7488
  edgesAdded += result.edgesAdded;
@@ -7473,7 +7506,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7473
7506
  group.kind,
7474
7507
  name,
7475
7508
  evidenceFile,
7476
- lineContaining(raw, name)
7509
+ lineContaining2(raw, name)
7477
7510
  );
7478
7511
  nodesAdded += result.nodesAdded;
7479
7512
  edgesAdded += result.edgesAdded;
@@ -7487,7 +7520,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7487
7520
  "cloudflare-cron",
7488
7521
  cron,
7489
7522
  evidenceFile,
7490
- lineContaining(raw, cron)
7523
+ lineContaining2(raw, cron)
7491
7524
  );
7492
7525
  nodesAdded += result.nodesAdded;
7493
7526
  edgesAdded += result.edgesAdded;
@@ -7500,7 +7533,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7500
7533
  "cloudflare-env-var",
7501
7534
  varName,
7502
7535
  evidenceFile,
7503
- lineContaining(raw, varName)
7536
+ lineContaining2(raw, varName)
7504
7537
  );
7505
7538
  nodesAdded += result.nodesAdded;
7506
7539
  edgesAdded += result.edgesAdded;
@@ -7518,7 +7551,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7518
7551
  type: import_types26.EdgeType.CALLS,
7519
7552
  provenance: import_types26.Provenance.EXTRACTED,
7520
7553
  confidence: (0, import_types26.confidenceForExtracted)("structural"),
7521
- evidence: { file: evidenceFile, line: lineContaining(raw, svc.service) }
7554
+ evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
7522
7555
  };
7523
7556
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
7524
7557
  edgesAdded++;
@@ -7532,7 +7565,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7532
7565
  "cloudflare-service-binding",
7533
7566
  svc.service,
7534
7567
  evidenceFile,
7535
- lineContaining(raw, svc.service)
7568
+ lineContaining2(raw, svc.service)
7536
7569
  );
7537
7570
  nodesAdded += result.nodesAdded;
7538
7571
  edgesAdded += result.edgesAdded;
@@ -7541,6 +7574,203 @@ async function addCloudflareWorkers(graph, services, scanPath) {
7541
7574
  return { nodesAdded, edgesAdded };
7542
7575
  }
7543
7576
 
7577
+ // src/extract/infra/vercel.ts
7578
+ init_cjs_shims();
7579
+ var import_node_fs22 = require("fs");
7580
+ var import_node_path38 = __toESM(require("path"), 1);
7581
+ var import_types27 = require("@neat.is/types");
7582
+ var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
7583
+ async function readVercelConfig(dir) {
7584
+ for (const filename of VERCEL_CONFIG_FILENAMES) {
7585
+ const abs = import_node_path38.default.join(dir, filename);
7586
+ if (!await exists2(abs)) continue;
7587
+ const raw = await import_node_fs22.promises.readFile(abs, "utf8");
7588
+ const config = JSON.parse(maskCommentsInSource(raw));
7589
+ return { config, relFile: filename, raw };
7590
+ }
7591
+ return null;
7592
+ }
7593
+ async function readLinkedProjectName(dir) {
7594
+ const abs = import_node_path38.default.join(dir, ".vercel", "project.json");
7595
+ if (!await exists2(abs)) return void 0;
7596
+ const parsed = JSON.parse(await import_node_fs22.promises.readFile(abs, "utf8"));
7597
+ return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
7598
+ }
7599
+ function routeSource(route) {
7600
+ return route.source ?? route.src;
7601
+ }
7602
+ async function addVercelServices(graph, services, scanPath) {
7603
+ let nodesAdded = 0;
7604
+ let edgesAdded = 0;
7605
+ for (const service of services) {
7606
+ let read = null;
7607
+ let projectName;
7608
+ try {
7609
+ read = await readVercelConfig(service.dir);
7610
+ projectName = await readLinkedProjectName(service.dir);
7611
+ } catch (err) {
7612
+ recordExtractionError("infra vercel", import_node_path38.default.relative(scanPath, service.dir), err);
7613
+ continue;
7614
+ }
7615
+ if (!read && !projectName) continue;
7616
+ const serviceNode = graph.getNodeAttributes(service.node.id);
7617
+ if (serviceNode.platform !== "vercel" || projectName && serviceNode.platformName !== projectName) {
7618
+ const updated = {
7619
+ ...serviceNode,
7620
+ platform: "vercel",
7621
+ ...projectName ? { platformName: projectName } : {}
7622
+ };
7623
+ graph.replaceNodeAttributes(service.node.id, updated);
7624
+ }
7625
+ const anchorId = service.node.id;
7626
+ if (!read) continue;
7627
+ const { config, relFile, raw } = read;
7628
+ const evidenceFile = toPosix2(import_node_path38.default.relative(scanPath, import_node_path38.default.join(service.dir, relFile)));
7629
+ const add = (edgeType, kind, name) => {
7630
+ if (!name) return;
7631
+ const result = emitPlatformResourceEdge(
7632
+ graph,
7633
+ anchorId,
7634
+ edgeType,
7635
+ kind,
7636
+ name,
7637
+ "vercel",
7638
+ evidenceFile,
7639
+ lineContaining(raw, name)
7640
+ );
7641
+ nodesAdded += result.nodesAdded;
7642
+ edgesAdded += result.edgesAdded;
7643
+ };
7644
+ add(import_types27.EdgeType.RUNS_ON, "vercel", "vercel");
7645
+ for (const cron of config.crons ?? []) add(import_types27.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
7646
+ for (const varName of Object.keys(config.env ?? {})) add(import_types27.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
7647
+ for (const varName of Object.keys(config.build?.env ?? {})) add(import_types27.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
7648
+ for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
7649
+ add(import_types27.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
7650
+ }
7651
+ }
7652
+ return { nodesAdded, edgesAdded };
7653
+ }
7654
+
7655
+ // src/extract/infra/railway.ts
7656
+ init_cjs_shims();
7657
+ var import_node_fs23 = require("fs");
7658
+ var import_node_path39 = __toESM(require("path"), 1);
7659
+ var import_smol_toml3 = require("smol-toml");
7660
+ var import_types28 = require("@neat.is/types");
7661
+ var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
7662
+ async function readRailwayConfig(dir) {
7663
+ for (const filename of RAILWAY_FILENAMES) {
7664
+ const abs = import_node_path39.default.join(dir, filename);
7665
+ if (!await exists2(abs)) continue;
7666
+ const raw = await import_node_fs23.promises.readFile(abs, "utf8");
7667
+ const config = filename === "railway.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
7668
+ return { config, relFile: filename, raw };
7669
+ }
7670
+ return null;
7671
+ }
7672
+ async function addRailwayServices(graph, services, scanPath) {
7673
+ let nodesAdded = 0;
7674
+ let edgesAdded = 0;
7675
+ for (const service of services) {
7676
+ let read = null;
7677
+ try {
7678
+ read = await readRailwayConfig(service.dir);
7679
+ } catch (err) {
7680
+ recordExtractionError("infra railway", import_node_path39.default.relative(scanPath, service.dir), err);
7681
+ continue;
7682
+ }
7683
+ if (!read) continue;
7684
+ const serviceNode = graph.getNodeAttributes(service.node.id);
7685
+ if (serviceNode.platform !== "railway") {
7686
+ graph.replaceNodeAttributes(service.node.id, { ...serviceNode, platform: "railway" });
7687
+ }
7688
+ const anchorId = service.node.id;
7689
+ const { config, relFile, raw } = read;
7690
+ const evidenceFile = toPosix2(import_node_path39.default.relative(scanPath, import_node_path39.default.join(service.dir, relFile)));
7691
+ const add = (edgeType, kind, name) => {
7692
+ if (!name) return;
7693
+ const result = emitPlatformResourceEdge(
7694
+ graph,
7695
+ anchorId,
7696
+ edgeType,
7697
+ kind,
7698
+ name,
7699
+ "railway",
7700
+ evidenceFile,
7701
+ lineContaining(raw, name)
7702
+ );
7703
+ nodesAdded += result.nodesAdded;
7704
+ edgesAdded += result.edgesAdded;
7705
+ };
7706
+ add(import_types28.EdgeType.RUNS_ON, "railway", "railway");
7707
+ add(import_types28.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
7708
+ add(import_types28.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
7709
+ }
7710
+ return { nodesAdded, edgesAdded };
7711
+ }
7712
+
7713
+ // src/extract/infra/supabase.ts
7714
+ init_cjs_shims();
7715
+ var import_node_fs24 = require("fs");
7716
+ var import_node_path40 = __toESM(require("path"), 1);
7717
+ var import_smol_toml4 = require("smol-toml");
7718
+ var import_types29 = require("@neat.is/types");
7719
+ async function readSupabaseConfig(dir) {
7720
+ const relFile = import_node_path40.default.join("supabase", "config.toml");
7721
+ const abs = import_node_path40.default.join(dir, relFile);
7722
+ if (!await exists2(abs)) return null;
7723
+ const raw = await import_node_fs24.promises.readFile(abs, "utf8");
7724
+ const config = (0, import_smol_toml4.parse)(raw);
7725
+ return { config, relFile, raw };
7726
+ }
7727
+ async function addSupabaseProjects(graph, services, scanPath) {
7728
+ let nodesAdded = 0;
7729
+ let edgesAdded = 0;
7730
+ for (const service of services) {
7731
+ let read = null;
7732
+ try {
7733
+ read = await readSupabaseConfig(service.dir);
7734
+ } catch (err) {
7735
+ recordExtractionError("infra supabase", import_node_path40.default.relative(scanPath, service.dir), err);
7736
+ continue;
7737
+ }
7738
+ if (!read) continue;
7739
+ const { config, relFile, raw } = read;
7740
+ const projectId = typeof config.project_id === "string" ? config.project_id : void 0;
7741
+ const serviceNode = graph.getNodeAttributes(service.node.id);
7742
+ if (serviceNode.platform !== "supabase" || projectId && serviceNode.platformName !== projectId) {
7743
+ graph.replaceNodeAttributes(service.node.id, {
7744
+ ...serviceNode,
7745
+ platform: "supabase",
7746
+ ...projectId ? { platformName: projectId } : {}
7747
+ });
7748
+ }
7749
+ const anchorId = service.node.id;
7750
+ const evidenceFile = toPosix2(import_node_path40.default.relative(scanPath, import_node_path40.default.join(service.dir, relFile)));
7751
+ const add = (edgeType, kind, name) => {
7752
+ if (!name) return;
7753
+ const result = emitPlatformResourceEdge(
7754
+ graph,
7755
+ anchorId,
7756
+ edgeType,
7757
+ kind,
7758
+ name,
7759
+ "supabase",
7760
+ evidenceFile,
7761
+ lineContaining(raw, name)
7762
+ );
7763
+ nodesAdded += result.nodesAdded;
7764
+ edgesAdded += result.edgesAdded;
7765
+ };
7766
+ add(import_types29.EdgeType.RUNS_ON, "supabase", "supabase");
7767
+ for (const fn of Object.keys(config.functions ?? {})) add(import_types29.EdgeType.DEPENDS_ON, "supabase-function", fn);
7768
+ if (config.storage) add(import_types29.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
7769
+ if (config.auth) add(import_types29.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
7770
+ }
7771
+ return { nodesAdded, edgesAdded };
7772
+ }
7773
+
7544
7774
  // src/extract/infra/index.ts
7545
7775
  async function addInfra(graph, scanPath, services) {
7546
7776
  const compose = await addComposeInfra(graph, scanPath, services);
@@ -7548,24 +7778,27 @@ async function addInfra(graph, scanPath, services) {
7548
7778
  const terraform = await addTerraformResources(graph, scanPath);
7549
7779
  const k8s = await addK8sResources(graph, scanPath);
7550
7780
  const cloudflare = await addCloudflareWorkers(graph, services, scanPath);
7781
+ const vercel = await addVercelServices(graph, services, scanPath);
7782
+ const railway = await addRailwayServices(graph, services, scanPath);
7783
+ const supabase = await addSupabaseProjects(graph, services, scanPath);
7551
7784
  return {
7552
- nodesAdded: compose.nodesAdded + dockerfile.nodesAdded + terraform.nodesAdded + k8s.nodesAdded + cloudflare.nodesAdded,
7553
- edgesAdded: compose.edgesAdded + dockerfile.edgesAdded + terraform.edgesAdded + k8s.edgesAdded + cloudflare.edgesAdded
7785
+ nodesAdded: compose.nodesAdded + dockerfile.nodesAdded + terraform.nodesAdded + k8s.nodesAdded + cloudflare.nodesAdded + vercel.nodesAdded + railway.nodesAdded + supabase.nodesAdded,
7786
+ edgesAdded: compose.edgesAdded + dockerfile.edgesAdded + terraform.edgesAdded + k8s.edgesAdded + cloudflare.edgesAdded + vercel.edgesAdded + railway.edgesAdded + supabase.edgesAdded
7554
7787
  };
7555
7788
  }
7556
7789
 
7557
7790
  // src/extract/index.ts
7558
- var import_node_path39 = __toESM(require("path"), 1);
7791
+ var import_node_path42 = __toESM(require("path"), 1);
7559
7792
 
7560
7793
  // src/extract/retire.ts
7561
7794
  init_cjs_shims();
7562
- var import_node_fs22 = require("fs");
7563
- var import_node_path38 = __toESM(require("path"), 1);
7564
- var import_types27 = require("@neat.is/types");
7795
+ var import_node_fs25 = require("fs");
7796
+ var import_node_path41 = __toESM(require("path"), 1);
7797
+ var import_types30 = require("@neat.is/types");
7565
7798
  function dropOrphanedFileNodes(graph) {
7566
7799
  const orphans = [];
7567
7800
  graph.forEachNode((id, attrs) => {
7568
- if (attrs.type !== import_types27.NodeType.FileNode) return;
7801
+ if (attrs.type !== import_types30.NodeType.FileNode) return;
7569
7802
  if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
7570
7803
  orphans.push(id);
7571
7804
  }
@@ -7578,14 +7811,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
7578
7811
  const bases = [scanPath, ...serviceDirs];
7579
7812
  graph.forEachEdge((id, attrs) => {
7580
7813
  const edge = attrs;
7581
- if (edge.provenance !== import_types27.Provenance.EXTRACTED) return;
7814
+ if (edge.provenance !== import_types30.Provenance.EXTRACTED) return;
7582
7815
  const evidenceFile = edge.evidence?.file;
7583
7816
  if (!evidenceFile) return;
7584
- if (import_node_path38.default.isAbsolute(evidenceFile)) {
7585
- if (!(0, import_node_fs22.existsSync)(evidenceFile)) toDrop.push(id);
7817
+ if (import_node_path41.default.isAbsolute(evidenceFile)) {
7818
+ if (!(0, import_node_fs25.existsSync)(evidenceFile)) toDrop.push(id);
7586
7819
  return;
7587
7820
  }
7588
- const found = bases.some((base) => (0, import_node_fs22.existsSync)(import_node_path38.default.join(base, evidenceFile)));
7821
+ const found = bases.some((base) => (0, import_node_fs25.existsSync)(import_node_path41.default.join(base, evidenceFile)));
7589
7822
  if (!found) toDrop.push(id);
7590
7823
  });
7591
7824
  for (const id of toDrop) graph.dropEdge(id);
@@ -7627,7 +7860,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
7627
7860
  }
7628
7861
  const droppedEntries = drainDroppedExtracted();
7629
7862
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
7630
- const rejectedPath = import_node_path39.default.join(import_node_path39.default.dirname(opts.errorsPath), "rejected.ndjson");
7863
+ const rejectedPath = import_node_path42.default.join(import_node_path42.default.dirname(opts.errorsPath), "rejected.ndjson");
7631
7864
  try {
7632
7865
  await writeRejectedExtracted(droppedEntries, rejectedPath);
7633
7866
  } catch (err) {
@@ -7661,7 +7894,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
7661
7894
 
7662
7895
  // src/diff.ts
7663
7896
  init_cjs_shims();
7664
- var import_node_fs23 = require("fs");
7897
+ var import_node_fs26 = require("fs");
7665
7898
  async function loadSnapshotForDiff(target) {
7666
7899
  if (/^https?:\/\//i.test(target)) {
7667
7900
  const res = await fetch(target);
@@ -7670,7 +7903,7 @@ async function loadSnapshotForDiff(target) {
7670
7903
  }
7671
7904
  return await res.json();
7672
7905
  }
7673
- const raw = await import_node_fs23.promises.readFile(target, "utf8");
7906
+ const raw = await import_node_fs26.promises.readFile(target, "utf8");
7674
7907
  return JSON.parse(raw);
7675
7908
  }
7676
7909
  function indexEntries(entries) {
@@ -7738,9 +7971,9 @@ function canonicalJson(value) {
7738
7971
 
7739
7972
  // src/persist.ts
7740
7973
  init_cjs_shims();
7741
- var import_node_fs24 = require("fs");
7742
- var import_node_path40 = __toESM(require("path"), 1);
7743
- var import_types28 = require("@neat.is/types");
7974
+ var import_node_fs27 = require("fs");
7975
+ var import_node_path43 = __toESM(require("path"), 1);
7976
+ var import_types31 = require("@neat.is/types");
7744
7977
  var SCHEMA_VERSION = 4;
7745
7978
  function migrateV1ToV2(payload) {
7746
7979
  const nodes = payload.graph.nodes;
@@ -7762,12 +7995,12 @@ function migrateV2ToV3(payload) {
7762
7995
  for (const edge of edges) {
7763
7996
  const attrs = edge.attributes;
7764
7997
  if (!attrs || attrs.provenance !== "FRONTIER") continue;
7765
- attrs.provenance = import_types28.Provenance.OBSERVED;
7998
+ attrs.provenance = import_types31.Provenance.OBSERVED;
7766
7999
  const type = typeof attrs.type === "string" ? attrs.type : void 0;
7767
8000
  const source = typeof attrs.source === "string" ? attrs.source : void 0;
7768
8001
  const target = typeof attrs.target === "string" ? attrs.target : void 0;
7769
8002
  if (type && source && target) {
7770
- const newId = (0, import_types28.observedEdgeId)(source, target, type);
8003
+ const newId = (0, import_types31.observedEdgeId)(source, target, type);
7771
8004
  attrs.id = newId;
7772
8005
  if (edge.key) edge.key = newId;
7773
8006
  }
@@ -7776,7 +8009,7 @@ function migrateV2ToV3(payload) {
7776
8009
  return { ...payload, schemaVersion: 3 };
7777
8010
  }
7778
8011
  async function ensureDir(filePath) {
7779
- await import_node_fs24.promises.mkdir(import_node_path40.default.dirname(filePath), { recursive: true });
8012
+ await import_node_fs27.promises.mkdir(import_node_path43.default.dirname(filePath), { recursive: true });
7780
8013
  }
7781
8014
  async function saveGraphToDisk(graph, outPath) {
7782
8015
  await ensureDir(outPath);
@@ -7786,13 +8019,13 @@ async function saveGraphToDisk(graph, outPath) {
7786
8019
  graph: graph.export()
7787
8020
  };
7788
8021
  const tmp = `${outPath}.tmp`;
7789
- await import_node_fs24.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
7790
- await import_node_fs24.promises.rename(tmp, outPath);
8022
+ await import_node_fs27.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
8023
+ await import_node_fs27.promises.rename(tmp, outPath);
7791
8024
  }
7792
8025
  async function loadGraphFromDisk(graph, outPath) {
7793
8026
  let raw;
7794
8027
  try {
7795
- raw = await import_node_fs24.promises.readFile(outPath, "utf8");
8028
+ raw = await import_node_fs27.promises.readFile(outPath, "utf8");
7796
8029
  } catch (err) {
7797
8030
  if (err.code === "ENOENT") return;
7798
8031
  throw err;
@@ -7857,23 +8090,23 @@ function startPersistLoop(graph, outPath, opts = {}) {
7857
8090
 
7858
8091
  // src/projects.ts
7859
8092
  init_cjs_shims();
7860
- var import_node_path41 = __toESM(require("path"), 1);
8093
+ var import_node_path44 = __toESM(require("path"), 1);
7861
8094
  function pathsForProject(project, baseDir) {
7862
8095
  if (project === DEFAULT_PROJECT) {
7863
8096
  return {
7864
- snapshotPath: import_node_path41.default.join(baseDir, "graph.json"),
7865
- errorsPath: import_node_path41.default.join(baseDir, "errors.ndjson"),
7866
- staleEventsPath: import_node_path41.default.join(baseDir, "stale-events.ndjson"),
7867
- embeddingsCachePath: import_node_path41.default.join(baseDir, "embeddings.json"),
7868
- policyViolationsPath: import_node_path41.default.join(baseDir, "policy-violations.ndjson")
8097
+ snapshotPath: import_node_path44.default.join(baseDir, "graph.json"),
8098
+ errorsPath: import_node_path44.default.join(baseDir, "errors.ndjson"),
8099
+ staleEventsPath: import_node_path44.default.join(baseDir, "stale-events.ndjson"),
8100
+ embeddingsCachePath: import_node_path44.default.join(baseDir, "embeddings.json"),
8101
+ policyViolationsPath: import_node_path44.default.join(baseDir, "policy-violations.ndjson")
7869
8102
  };
7870
8103
  }
7871
8104
  return {
7872
- snapshotPath: import_node_path41.default.join(baseDir, `${project}.json`),
7873
- errorsPath: import_node_path41.default.join(baseDir, `errors.${project}.ndjson`),
7874
- staleEventsPath: import_node_path41.default.join(baseDir, `stale-events.${project}.ndjson`),
7875
- embeddingsCachePath: import_node_path41.default.join(baseDir, `embeddings.${project}.json`),
7876
- policyViolationsPath: import_node_path41.default.join(baseDir, `policy-violations.${project}.ndjson`)
8105
+ snapshotPath: import_node_path44.default.join(baseDir, `${project}.json`),
8106
+ errorsPath: import_node_path44.default.join(baseDir, `errors.${project}.ndjson`),
8107
+ staleEventsPath: import_node_path44.default.join(baseDir, `stale-events.${project}.ndjson`),
8108
+ embeddingsCachePath: import_node_path44.default.join(baseDir, `embeddings.${project}.json`),
8109
+ policyViolationsPath: import_node_path44.default.join(baseDir, `policy-violations.${project}.ndjson`)
7877
8110
  };
7878
8111
  }
7879
8112
  var Projects = class {
@@ -7913,23 +8146,23 @@ function parseExtraProjects(raw) {
7913
8146
 
7914
8147
  // src/registry.ts
7915
8148
  init_cjs_shims();
7916
- var import_node_fs25 = require("fs");
8149
+ var import_node_fs28 = require("fs");
7917
8150
  var import_node_os3 = __toESM(require("os"), 1);
7918
- var import_node_path42 = __toESM(require("path"), 1);
7919
- var import_types29 = require("@neat.is/types");
8151
+ var import_node_path45 = __toESM(require("path"), 1);
8152
+ var import_types32 = require("@neat.is/types");
7920
8153
  function neatHome() {
7921
8154
  const override = process.env.NEAT_HOME;
7922
- if (override && override.length > 0) return import_node_path42.default.resolve(override);
7923
- return import_node_path42.default.join(import_node_os3.default.homedir(), ".neat");
8155
+ if (override && override.length > 0) return import_node_path45.default.resolve(override);
8156
+ return import_node_path45.default.join(import_node_os3.default.homedir(), ".neat");
7924
8157
  }
7925
8158
  function registryPath() {
7926
- return import_node_path42.default.join(neatHome(), "projects.json");
8159
+ return import_node_path45.default.join(neatHome(), "projects.json");
7927
8160
  }
7928
8161
  async function readRegistry() {
7929
8162
  const file = registryPath();
7930
8163
  let raw;
7931
8164
  try {
7932
- raw = await import_node_fs25.promises.readFile(file, "utf8");
8165
+ raw = await import_node_fs28.promises.readFile(file, "utf8");
7933
8166
  } catch (err) {
7934
8167
  if (err.code === "ENOENT") {
7935
8168
  return { version: 1, projects: [] };
@@ -7937,7 +8170,7 @@ async function readRegistry() {
7937
8170
  throw err;
7938
8171
  }
7939
8172
  const parsed = JSON.parse(raw);
7940
- return import_types29.RegistryFileSchema.parse(parsed);
8173
+ return import_types32.RegistryFileSchema.parse(parsed);
7941
8174
  }
7942
8175
  async function getProject(name) {
7943
8176
  const reg = await readRegistry();
@@ -8008,6 +8241,168 @@ data: ${JSON.stringify(envelope.payload)}
8008
8241
 
8009
8242
  // src/api.ts
8010
8243
  init_auth();
8244
+
8245
+ // src/connectors-config.ts
8246
+ init_cjs_shims();
8247
+ var import_node_os4 = __toESM(require("os"), 1);
8248
+ var import_node_path46 = __toESM(require("path"), 1);
8249
+ var import_node_fs29 = require("fs");
8250
+ var CONNECTORS_CONFIG_VERSION = 1;
8251
+ function neatHome2() {
8252
+ const override = process.env.NEAT_HOME;
8253
+ if (override && override.length > 0) return import_node_path46.default.resolve(override);
8254
+ return import_node_path46.default.join(import_node_os4.default.homedir(), ".neat");
8255
+ }
8256
+ function connectorsConfigPath(home = neatHome2()) {
8257
+ return import_node_path46.default.join(home, "connectors.json");
8258
+ }
8259
+ var MODE_MASK_LOOSER_THAN_0600 = 63;
8260
+ async function warnIfModeLooserThan0600(file) {
8261
+ if (process.platform === "win32") return;
8262
+ try {
8263
+ const stat = await import_node_fs29.promises.stat(file);
8264
+ if ((stat.mode & MODE_MASK_LOOSER_THAN_0600) !== 0) {
8265
+ const mode = (stat.mode & 511).toString(8).padStart(3, "0");
8266
+ console.warn(
8267
+ `[neat] ${file} is mode 0${mode}, looser than the 0600 this file's secrets call for \u2014 run \`chmod 600 ${file}\``
8268
+ );
8269
+ }
8270
+ } catch {
8271
+ }
8272
+ }
8273
+ async function readConnectorsConfig(home = neatHome2()) {
8274
+ const file = connectorsConfigPath(home);
8275
+ let raw;
8276
+ try {
8277
+ raw = await import_node_fs29.promises.readFile(file, "utf8");
8278
+ } catch (err) {
8279
+ if (err.code === "ENOENT") {
8280
+ return { version: CONNECTORS_CONFIG_VERSION, connectors: [] };
8281
+ }
8282
+ throw err;
8283
+ }
8284
+ await warnIfModeLooserThan0600(file);
8285
+ let parsed;
8286
+ try {
8287
+ parsed = JSON.parse(raw);
8288
+ } catch (err) {
8289
+ throw new Error(`${file} is not valid JSON: ${err.message}`);
8290
+ }
8291
+ return validateConfig(parsed, file);
8292
+ }
8293
+ function validateConfig(parsed, file) {
8294
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
8295
+ throw new Error(`${file} must be a JSON object with a "connectors" array`);
8296
+ }
8297
+ const obj = parsed;
8298
+ const version = obj.version === void 0 ? CONNECTORS_CONFIG_VERSION : obj.version;
8299
+ if (typeof version !== "number" || !Number.isInteger(version)) {
8300
+ throw new Error(`${file}: "version" must be an integer`);
8301
+ }
8302
+ const rawConnectors = obj.connectors;
8303
+ if (!Array.isArray(rawConnectors)) {
8304
+ throw new Error(`${file}: "connectors" must be an array`);
8305
+ }
8306
+ const connectors = rawConnectors.map((entry, i) => validateEntry(entry, i, file));
8307
+ return { version, connectors };
8308
+ }
8309
+ function validateEntry(entry, index, file) {
8310
+ const where = `${file}: connectors[${index}]`;
8311
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
8312
+ throw new Error(`${where} must be an object`);
8313
+ }
8314
+ const e = entry;
8315
+ const id = e.id;
8316
+ if (typeof id !== "string" || id.length === 0) {
8317
+ throw new Error(`${where}.id must be a non-empty string`);
8318
+ }
8319
+ const provider = e.provider;
8320
+ if (typeof provider !== "string" || provider.length === 0) {
8321
+ throw new Error(`${where}.provider must be a non-empty string`);
8322
+ }
8323
+ if (e.project !== void 0 && typeof e.project !== "string") {
8324
+ throw new Error(`${where}.project must be a string when present`);
8325
+ }
8326
+ const credential = validateCredentialRef(e.credential, `${where}.credential`);
8327
+ let options;
8328
+ if (e.options !== void 0) {
8329
+ if (typeof e.options !== "object" || e.options === null || Array.isArray(e.options)) {
8330
+ throw new Error(`${where}.options must be an object when present`);
8331
+ }
8332
+ options = e.options;
8333
+ }
8334
+ return {
8335
+ id,
8336
+ provider,
8337
+ ...typeof e.project === "string" ? { project: e.project } : {},
8338
+ credential,
8339
+ ...options ? { options } : {}
8340
+ };
8341
+ }
8342
+ function validateCredentialRef(raw, where) {
8343
+ if (typeof raw === "string") {
8344
+ if (raw.length === 0) throw new Error(`${where} must be a non-empty string`);
8345
+ return raw;
8346
+ }
8347
+ if (typeof raw === "object" && raw !== null && !Array.isArray(raw)) {
8348
+ const fields = raw;
8349
+ const keys = Object.keys(fields);
8350
+ if (keys.length === 0) throw new Error(`${where} object must have at least one field`);
8351
+ for (const key of keys) {
8352
+ const v = fields[key];
8353
+ if (typeof v !== "string" || v.length === 0) {
8354
+ throw new Error(`${where}.${key} must be a non-empty string`);
8355
+ }
8356
+ }
8357
+ return fields;
8358
+ }
8359
+ throw new Error(`${where} must be a string or an object of strings`);
8360
+ }
8361
+ function connectorMatchesProject(entry, project) {
8362
+ return entry.project === void 0 || entry.project === project;
8363
+ }
8364
+ function isEnvRef(value) {
8365
+ return value.length > 1 && value.startsWith("$");
8366
+ }
8367
+ function redactCredentialRef(ref) {
8368
+ const one = (value) => isEnvRef(value) ? value : "****";
8369
+ if (typeof ref === "string") return one(ref);
8370
+ const out = {};
8371
+ for (const [key, value] of Object.entries(ref)) out[key] = one(value);
8372
+ return out;
8373
+ }
8374
+
8375
+ // src/connectors/status.ts
8376
+ init_cjs_shims();
8377
+ var CONNECTOR_STALE_THRESHOLD_MS = 5 * 6e4;
8378
+ var records = /* @__PURE__ */ new Map();
8379
+ function deriveState(rec, now, thresholdMs) {
8380
+ if (rec.lastOutcome === "error") return "error";
8381
+ const okAt = rec.lastOkAt ? Date.parse(rec.lastOkAt) : NaN;
8382
+ if (!Number.isNaN(okAt) && now - okAt > thresholdMs) return "stale";
8383
+ return "healthy";
8384
+ }
8385
+ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_THRESHOLD_MS) {
8386
+ const rec = records.get(id);
8387
+ if (!rec) {
8388
+ return {
8389
+ state: "idle",
8390
+ lastPollAt: null,
8391
+ lastOutcome: null,
8392
+ lastError: null,
8393
+ signalsLastPoll: 0
8394
+ };
8395
+ }
8396
+ return {
8397
+ state: deriveState(rec, now, thresholdMs),
8398
+ lastPollAt: rec.lastPollAt,
8399
+ lastOutcome: rec.lastOutcome,
8400
+ lastError: rec.lastError,
8401
+ signalsLastPoll: rec.signalsLastPoll
8402
+ };
8403
+ }
8404
+
8405
+ // src/api.ts
8011
8406
  function serializeGraph(graph) {
8012
8407
  const nodes = [];
8013
8408
  graph.forEachNode((_id, attrs) => {
@@ -8158,11 +8553,11 @@ function registerRoutes(scope, ctx) {
8158
8553
  const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
8159
8554
  const parsed = [];
8160
8555
  for (const c of candidates) {
8161
- const r = import_types30.DivergenceTypeSchema.safeParse(c);
8556
+ const r = import_types33.DivergenceTypeSchema.safeParse(c);
8162
8557
  if (!r.success) {
8163
8558
  return reply.code(400).send({
8164
8559
  error: `unknown divergence type "${c}"`,
8165
- allowed: import_types30.DivergenceTypeSchema.options
8560
+ allowed: import_types33.DivergenceTypeSchema.options
8166
8561
  });
8167
8562
  }
8168
8563
  parsed.push(r.data);
@@ -8227,6 +8622,26 @@ function registerRoutes(scope, ctx) {
8227
8622
  const sliced = filtered.slice(0, safeLimit);
8228
8623
  return { count: sliced.length, total, logs: sliced };
8229
8624
  });
8625
+ scope.get("/connectors", async (req, reply) => {
8626
+ const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
8627
+ if (!proj) return;
8628
+ let entries;
8629
+ try {
8630
+ entries = (await readConnectorsConfig(ctx.connectorsHome)).connectors;
8631
+ } catch (err) {
8632
+ return reply.code(500).send({
8633
+ error: "connectors.json failed to read",
8634
+ details: err.message
8635
+ });
8636
+ }
8637
+ const connectors = entries.filter((entry) => connectorMatchesProject(entry, proj.name)).map((entry) => ({
8638
+ id: entry.id,
8639
+ provider: entry.provider,
8640
+ credentialRef: redactCredentialRef(entry.credential),
8641
+ status: getConnectorStatus(entry.id)
8642
+ }));
8643
+ return { connectors };
8644
+ });
8230
8645
  const incidentHistoryHandler = async (req, reply) => {
8231
8646
  const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
8232
8647
  if (!proj) return;
@@ -8413,7 +8828,7 @@ function registerRoutes(scope, ctx) {
8413
8828
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
8414
8829
  let violations = await log.readAll();
8415
8830
  if (req.query.severity) {
8416
- const sev = import_types30.PolicySeveritySchema.safeParse(req.query.severity);
8831
+ const sev = import_types33.PolicySeveritySchema.safeParse(req.query.severity);
8417
8832
  if (!sev.success) {
8418
8833
  return reply.code(400).send({
8419
8834
  error: "invalid severity",
@@ -8452,7 +8867,7 @@ function registerRoutes(scope, ctx) {
8452
8867
  scope.post("/policies/check", async (req, reply) => {
8453
8868
  const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
8454
8869
  if (!proj) return;
8455
- const parsed = import_types30.PoliciesCheckBodySchema.safeParse(req.body ?? {});
8870
+ const parsed = import_types33.PoliciesCheckBodySchema.safeParse(req.body ?? {});
8456
8871
  if (!parsed.success) {
8457
8872
  return reply.code(400).send({
8458
8873
  error: "invalid /policies/check body",
@@ -8597,9 +9012,11 @@ async function buildApi(opts) {
8597
9012
  trustProxy,
8598
9013
  publicRead
8599
9014
  });
9015
+ const requiresAuth = authToken !== void 0 && authToken.length > 0 && trustProxy !== true;
8600
9016
  app.get("/api/config", async () => ({
8601
9017
  publicRead: publicRead === true,
8602
- authProxy: trustProxy === true
9018
+ authProxy: trustProxy === true,
9019
+ requiresAuth
8603
9020
  }));
8604
9021
  const startedAt = opts.startedAt ?? Date.now();
8605
9022
  const registry = buildLegacyRegistry(opts);
@@ -8629,7 +9046,8 @@ async function buildApi(opts) {
8629
9046
  staleEventsPathFor,
8630
9047
  policyFilePathFor,
8631
9048
  bootstrap: opts.bootstrap,
8632
- singleProject: opts.singleProject?.name
9049
+ singleProject: opts.singleProject?.name,
9050
+ connectorsHome: opts.connectorsHome
8633
9051
  };
8634
9052
  app.get("/health", async () => {
8635
9053
  const uptimeMs = Date.now() - startedAt;
@@ -8714,8 +9132,8 @@ init_otel_grpc();
8714
9132
 
8715
9133
  // src/search.ts
8716
9134
  init_cjs_shims();
8717
- var import_node_fs26 = require("fs");
8718
- var import_node_path45 = __toESM(require("path"), 1);
9135
+ var import_node_fs30 = require("fs");
9136
+ var import_node_path49 = __toESM(require("path"), 1);
8719
9137
  var import_node_crypto3 = require("crypto");
8720
9138
  var DEFAULT_LIMIT = 10;
8721
9139
  var NOMIC_DIM = 768;
@@ -8869,7 +9287,7 @@ async function pickEmbedder() {
8869
9287
  }
8870
9288
  async function readCache(cachePath) {
8871
9289
  try {
8872
- const raw = await import_node_fs26.promises.readFile(cachePath, "utf8");
9290
+ const raw = await import_node_fs30.promises.readFile(cachePath, "utf8");
8873
9291
  const parsed = JSON.parse(raw);
8874
9292
  if (parsed.version !== 1) return null;
8875
9293
  return parsed;
@@ -8878,8 +9296,8 @@ async function readCache(cachePath) {
8878
9296
  }
8879
9297
  }
8880
9298
  async function writeCache(cachePath, cache) {
8881
- await import_node_fs26.promises.mkdir(import_node_path45.default.dirname(cachePath), { recursive: true });
8882
- await import_node_fs26.promises.writeFile(cachePath, JSON.stringify(cache));
9299
+ await import_node_fs30.promises.mkdir(import_node_path49.default.dirname(cachePath), { recursive: true });
9300
+ await import_node_fs30.promises.writeFile(cachePath, JSON.stringify(cache));
8883
9301
  }
8884
9302
  var VectorIndex = class {
8885
9303
  constructor(embedder, cachePath) {
@@ -9059,14 +9477,14 @@ async function bootProject(registry, name, scanPath, baseDir) {
9059
9477
  async function main() {
9060
9478
  const baseDirEnv = process.env.NEAT_OUT_DIR;
9061
9479
  const legacyOutPath = process.env.NEAT_OUT_PATH;
9062
- const baseDir = baseDirEnv ? import_node_path46.default.resolve(baseDirEnv) : legacyOutPath ? import_node_path46.default.resolve(import_node_path46.default.dirname(legacyOutPath)) : import_node_path46.default.resolve("./neat-out");
9063
- const defaultScanPath = import_node_path46.default.resolve(process.env.NEAT_SCAN_PATH ?? "./demo");
9480
+ const baseDir = baseDirEnv ? import_node_path50.default.resolve(baseDirEnv) : legacyOutPath ? import_node_path50.default.resolve(import_node_path50.default.dirname(legacyOutPath)) : import_node_path50.default.resolve("./neat-out");
9481
+ const defaultScanPath = import_node_path50.default.resolve(process.env.NEAT_SCAN_PATH ?? "./demo");
9064
9482
  const registry = new Projects();
9065
9483
  await bootProject(registry, DEFAULT_PROJECT, defaultScanPath, baseDir);
9066
9484
  for (const name of parseExtraProjects(process.env.NEAT_PROJECTS)) {
9067
9485
  const envKey = `NEAT_PROJECT_SCAN_PATH_${name.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
9068
9486
  const projectScan = process.env[envKey];
9069
- await bootProject(registry, name, projectScan ? import_node_path46.default.resolve(projectScan) : void 0, baseDir);
9487
+ await bootProject(registry, name, projectScan ? import_node_path50.default.resolve(projectScan) : void 0, baseDir);
9070
9488
  }
9071
9489
  const host = process.env.HOST ?? "0.0.0.0";
9072
9490
  const port = Number(process.env.PORT ?? 8080);