@neat.is/core 0.9.21-dev.20260923 → 0.9.21

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
@@ -805,7 +805,7 @@ __export(cli_exports, {
805
805
  });
806
806
  module.exports = __toCommonJS(cli_exports);
807
807
  init_cjs_shims();
808
- var import_node_path97 = __toESM(require("path"), 1);
808
+ var import_node_path98 = __toESM(require("path"), 1);
809
809
  var import_node_os11 = __toESM(require("os"), 1);
810
810
  var import_node_fs59 = require("fs");
811
811
 
@@ -8838,7 +8838,7 @@ function collectPhpSymbolDefs(root) {
8838
8838
  });
8839
8839
  };
8840
8840
  const dot = (s) => s.replace(/\\/g, ".").replace(/::/g, ".").replace(/^\.+/, "");
8841
- const join = (prefix, name) => prefix ? `${prefix}.${name}` : name;
8841
+ const join2 = (prefix, name) => prefix ? `${prefix}.${name}` : name;
8842
8842
  const walkChildren = (container, nsCtx, classCtx) => {
8843
8843
  let ns = nsCtx;
8844
8844
  for (let i = 0; i < container.namedChildCount; i++) {
@@ -8860,7 +8860,7 @@ function collectPhpSymbolDefs(root) {
8860
8860
  switch (node.type) {
8861
8861
  case "function_definition": {
8862
8862
  const name = node.childForFieldName("name")?.text;
8863
- if (name) push("function", join(nsCtx, name), node);
8863
+ if (name) push("function", join2(nsCtx, name), node);
8864
8864
  walkBody(node, nsCtx, void 0);
8865
8865
  return;
8866
8866
  }
@@ -8868,7 +8868,7 @@ function collectPhpSymbolDefs(root) {
8868
8868
  case "trait_declaration":
8869
8869
  case "interface_declaration": {
8870
8870
  const name = node.childForFieldName("name")?.text;
8871
- const full = name ? join(nsCtx, name) : classCtx;
8871
+ const full = name ? join2(nsCtx, name) : classCtx;
8872
8872
  if (name) push("class", full, node);
8873
8873
  walkBody(node, nsCtx, full);
8874
8874
  return;
@@ -8877,7 +8877,7 @@ function collectPhpSymbolDefs(root) {
8877
8877
  const name = node.childForFieldName("name")?.text;
8878
8878
  if (name) {
8879
8879
  const kind = name === "__construct" ? "constructor" : "method";
8880
- push(kind, join(classCtx ?? nsCtx, name), node);
8880
+ push(kind, join2(classCtx ?? nsCtx, name), node);
8881
8881
  }
8882
8882
  return;
8883
8883
  }
@@ -8903,7 +8903,7 @@ function collectCsharpSymbolDefs(root) {
8903
8903
  endLine: node.endPosition.row + 1
8904
8904
  });
8905
8905
  };
8906
- const join = (prefix, name) => prefix ? `${prefix}.${name}` : name;
8906
+ const join2 = (prefix, name) => prefix ? `${prefix}.${name}` : name;
8907
8907
  const walkChildren = (container, nsCtx, classCtx) => {
8908
8908
  let ns = nsCtx;
8909
8909
  for (let i = 0; i < container.namedChildCount; i++) {
@@ -8911,7 +8911,7 @@ function collectCsharpSymbolDefs(root) {
8911
8911
  if (!child) continue;
8912
8912
  if (child.type === "file_scoped_namespace_declaration") {
8913
8913
  const nameNode = child.childForFieldName("name");
8914
- if (nameNode) ns = join(nsCtx, nameNode.text);
8914
+ if (nameNode) ns = join2(nsCtx, nameNode.text);
8915
8915
  continue;
8916
8916
  }
8917
8917
  visit(child, ns, classCtx);
@@ -8925,7 +8925,7 @@ function collectCsharpSymbolDefs(root) {
8925
8925
  switch (node.type) {
8926
8926
  case "namespace_declaration": {
8927
8927
  const nameNode = node.childForFieldName("name");
8928
- const ns = nameNode ? join(nsCtx, nameNode.text) : nsCtx;
8928
+ const ns = nameNode ? join2(nsCtx, nameNode.text) : nsCtx;
8929
8929
  walkBody(node, ns, classCtx);
8930
8930
  return;
8931
8931
  }
@@ -8934,19 +8934,19 @@ function collectCsharpSymbolDefs(root) {
8934
8934
  case "struct_declaration":
8935
8935
  case "record_declaration": {
8936
8936
  const name = node.childForFieldName("name")?.text;
8937
- const full = name ? join(classCtx ?? nsCtx, name) : classCtx;
8937
+ const full = name ? join2(classCtx ?? nsCtx, name) : classCtx;
8938
8938
  if (name) push("class", full, node);
8939
8939
  walkBody(node, nsCtx, full);
8940
8940
  return;
8941
8941
  }
8942
8942
  case "method_declaration": {
8943
8943
  const name = node.childForFieldName("name")?.text;
8944
- if (name) push("method", join(classCtx ?? nsCtx, name), node);
8944
+ if (name) push("method", join2(classCtx ?? nsCtx, name), node);
8945
8945
  return;
8946
8946
  }
8947
8947
  case "constructor_declaration": {
8948
8948
  const name = node.childForFieldName("name")?.text;
8949
- if (name) push("constructor", join(classCtx ?? nsCtx, name), node);
8949
+ if (name) push("constructor", join2(classCtx ?? nsCtx, name), node);
8950
8950
  return;
8951
8951
  }
8952
8952
  }
@@ -8965,7 +8965,7 @@ function collectJavaSymbolDefs(root) {
8965
8965
  endLine: node.endPosition.row + 1
8966
8966
  });
8967
8967
  };
8968
- const join = (prefix, name) => prefix ? `${prefix}.${name}` : name;
8968
+ const join2 = (prefix, name) => prefix ? `${prefix}.${name}` : name;
8969
8969
  let pkg;
8970
8970
  for (let i = 0; i < root.namedChildCount; i++) {
8971
8971
  const child = root.namedChild(i);
@@ -8991,19 +8991,19 @@ function collectJavaSymbolDefs(root) {
8991
8991
  case "enum_declaration":
8992
8992
  case "record_declaration": {
8993
8993
  const name = node.childForFieldName("name")?.text;
8994
- const full = name ? join(classCtx ?? pkg, name) : classCtx;
8994
+ const full = name ? join2(classCtx ?? pkg, name) : classCtx;
8995
8995
  if (name) push("class", full, node);
8996
8996
  walkBody(node, full);
8997
8997
  return;
8998
8998
  }
8999
8999
  case "method_declaration": {
9000
9000
  const name = node.childForFieldName("name")?.text;
9001
- if (name) push("method", join(classCtx ?? pkg, name), node);
9001
+ if (name) push("method", join2(classCtx ?? pkg, name), node);
9002
9002
  return;
9003
9003
  }
9004
9004
  case "constructor_declaration": {
9005
9005
  const name = node.childForFieldName("name")?.text;
9006
- if (name) push("constructor", join(classCtx ?? pkg, name), node);
9006
+ if (name) push("constructor", join2(classCtx ?? pkg, name), node);
9007
9007
  return;
9008
9008
  }
9009
9009
  }
@@ -9022,7 +9022,7 @@ function collectKotlinSymbolDefs(root) {
9022
9022
  endLine: node.endPosition.row + 1
9023
9023
  });
9024
9024
  };
9025
- const join = (prefix, name) => prefix ? `${prefix}.${name}` : name;
9025
+ const join2 = (prefix, name) => prefix ? `${prefix}.${name}` : name;
9026
9026
  const firstChildOfType2 = (node, types) => {
9027
9027
  for (let i = 0; i < node.namedChildCount; i++) {
9028
9028
  const child = node.namedChild(i);
@@ -9051,7 +9051,7 @@ function collectKotlinSymbolDefs(root) {
9051
9051
  case "class_declaration":
9052
9052
  case "object_declaration": {
9053
9053
  const name = nameOf(node, "type_identifier");
9054
- const full = name ? join(classCtx ?? pkg, name) : classCtx;
9054
+ const full = name ? join2(classCtx ?? pkg, name) : classCtx;
9055
9055
  if (name) push("class", full, node);
9056
9056
  const body = bodyOf(node);
9057
9057
  if (body) walkChildren(body, full);
@@ -9059,20 +9059,20 @@ function collectKotlinSymbolDefs(root) {
9059
9059
  }
9060
9060
  case "companion_object": {
9061
9061
  const name = nameOf(node, "type_identifier");
9062
- const full = name ? join(classCtx ?? pkg, name) : classCtx ?? pkg;
9062
+ const full = name ? join2(classCtx ?? pkg, name) : classCtx ?? pkg;
9063
9063
  const body = bodyOf(node);
9064
9064
  if (body) walkChildren(body, full);
9065
9065
  return;
9066
9066
  }
9067
9067
  case "function_declaration": {
9068
9068
  const name = nameOf(node, "simple_identifier");
9069
- if (name) push(classCtx ? "method" : "function", join(classCtx ?? pkg, name), node);
9069
+ if (name) push(classCtx ? "method" : "function", join2(classCtx ?? pkg, name), node);
9070
9070
  return;
9071
9071
  }
9072
9072
  case "secondary_constructor": {
9073
9073
  if (classCtx) {
9074
9074
  const typeName = classCtx.slice(classCtx.lastIndexOf(".") + 1);
9075
- push("constructor", join(classCtx, typeName), node);
9075
+ push("constructor", join2(classCtx, typeName), node);
9076
9076
  }
9077
9077
  return;
9078
9078
  }
@@ -9092,14 +9092,14 @@ function collectRustSymbolDefs(root) {
9092
9092
  endLine: node.endPosition.row + 1
9093
9093
  });
9094
9094
  };
9095
- const join = (prefix, name) => prefix ? `${prefix}::${name}` : name;
9095
+ const join2 = (prefix, name) => prefix ? `${prefix}::${name}` : name;
9096
9096
  const walkTypeBody = (body, typeCtx) => {
9097
9097
  for (let i = 0; i < body.namedChildCount; i++) {
9098
9098
  const child = body.namedChild(i);
9099
9099
  if (!child) continue;
9100
9100
  if (child.type === "function_item" || child.type === "function_signature_item") {
9101
9101
  const name = child.childForFieldName("name")?.text;
9102
- if (name) push("method", join(typeCtx, name), child);
9102
+ if (name) push("method", join2(typeCtx, name), child);
9103
9103
  }
9104
9104
  }
9105
9105
  };
@@ -9113,7 +9113,7 @@ function collectRustSymbolDefs(root) {
9113
9113
  switch (node.type) {
9114
9114
  case "mod_item": {
9115
9115
  const name = node.childForFieldName("name")?.text;
9116
- const full = name ? join(modCtx, name) : modCtx;
9116
+ const full = name ? join2(modCtx, name) : modCtx;
9117
9117
  const body = node.childForFieldName("body");
9118
9118
  if (body) walkChildren(body, full);
9119
9119
  return;
@@ -9121,12 +9121,12 @@ function collectRustSymbolDefs(root) {
9121
9121
  case "struct_item":
9122
9122
  case "enum_item": {
9123
9123
  const name = node.childForFieldName("name")?.text;
9124
- if (name) push("class", join(modCtx, name), node);
9124
+ if (name) push("class", join2(modCtx, name), node);
9125
9125
  return;
9126
9126
  }
9127
9127
  case "trait_item": {
9128
9128
  const name = node.childForFieldName("name")?.text;
9129
- const full = name ? join(modCtx, name) : modCtx;
9129
+ const full = name ? join2(modCtx, name) : modCtx;
9130
9130
  if (name) push("class", full, node);
9131
9131
  const body = node.childForFieldName("body");
9132
9132
  if (body && full) walkTypeBody(body, full);
@@ -9134,14 +9134,14 @@ function collectRustSymbolDefs(root) {
9134
9134
  }
9135
9135
  case "impl_item": {
9136
9136
  const typeName = node.childForFieldName("type")?.text;
9137
- const full = typeName ? join(modCtx, typeName) : modCtx;
9137
+ const full = typeName ? join2(modCtx, typeName) : modCtx;
9138
9138
  const body = node.childForFieldName("body");
9139
9139
  if (body && full) walkTypeBody(body, full);
9140
9140
  return;
9141
9141
  }
9142
9142
  case "function_item": {
9143
9143
  const name = node.childForFieldName("name")?.text;
9144
- if (name) push("function", join(modCtx, name), node);
9144
+ if (name) push("function", join2(modCtx, name), node);
9145
9145
  return;
9146
9146
  }
9147
9147
  }
@@ -9160,7 +9160,7 @@ function collectCppSymbolDefs(root) {
9160
9160
  endLine: node.endPosition.row + 1
9161
9161
  });
9162
9162
  };
9163
- const join = (prefix, name) => prefix ? `${prefix}::${name}` : name;
9163
+ const join2 = (prefix, name) => prefix ? `${prefix}::${name}` : name;
9164
9164
  const DECLARATOR_WRAPPERS = /* @__PURE__ */ new Set([
9165
9165
  "function_declarator",
9166
9166
  "pointer_declarator",
@@ -9186,7 +9186,7 @@ function collectCppSymbolDefs(root) {
9186
9186
  return void 0;
9187
9187
  };
9188
9188
  const classify = (nameText, prefix, enclosingClass) => {
9189
- const qualname = join(prefix, nameText);
9189
+ const qualname = join2(prefix, nameText);
9190
9190
  let className;
9191
9191
  let memberName;
9192
9192
  const idx = nameText.lastIndexOf("::");
@@ -9215,7 +9215,7 @@ function collectCppSymbolDefs(root) {
9215
9215
  switch (node.type) {
9216
9216
  case "namespace_definition": {
9217
9217
  const name = node.childForFieldName("name")?.text;
9218
- const newPrefix = name ? join(prefix, name) : prefix;
9218
+ const newPrefix = name ? join2(prefix, name) : prefix;
9219
9219
  const body = node.childForFieldName("body");
9220
9220
  if (body) walkChildren(body, newPrefix, void 0);
9221
9221
  return;
@@ -9223,7 +9223,7 @@ function collectCppSymbolDefs(root) {
9223
9223
  case "class_specifier":
9224
9224
  case "struct_specifier": {
9225
9225
  const name = node.childForFieldName("name")?.text;
9226
- const full = name ? join(prefix, name) : prefix;
9226
+ const full = name ? join2(prefix, name) : prefix;
9227
9227
  if (name) push("class", full, node);
9228
9228
  const body = node.childForFieldName("body");
9229
9229
  if (body && name) walkChildren(body, full, name);
@@ -17492,14 +17492,14 @@ function formatIncompat(inc) {
17492
17492
  // src/watch.ts
17493
17493
  init_cjs_shims();
17494
17494
  var import_node_fs45 = __toESM(require("fs"), 1);
17495
- var import_node_path82 = __toESM(require("path"), 1);
17495
+ var import_node_path83 = __toESM(require("path"), 1);
17496
17496
  var import_chokidar = __toESM(require("chokidar"), 1);
17497
17497
 
17498
17498
  // src/api.ts
17499
17499
  init_cjs_shims();
17500
17500
  var import_fastify2 = __toESM(require("fastify"), 1);
17501
17501
  var import_cors = __toESM(require("@fastify/cors"), 1);
17502
- var import_types102 = require("@neat.is/types");
17502
+ var import_types103 = require("@neat.is/types");
17503
17503
 
17504
17504
  // src/extend/index.ts
17505
17505
  init_cjs_shims();
@@ -18052,7 +18052,7 @@ var INTENT_RULES = [
18052
18052
  },
18053
18053
  {
18054
18054
  intent: "blast-radius",
18055
- test: /\b(blast|break[\s-]?if|breaks[\s-]?if|impact|downstream|dependents?|redeploy|who\s+(?:uses|calls|depends)|what\s+depends\s+on|affect(?:s|ed)?)\b/
18055
+ test: /\b(blast|break[\s-]?if|breaks[\s-]?if|impact|downstream|dependents?|redeploy|who\s+(?:uses|calls|depends(?:\s+on)?)|what\s+depends\s+on|consumers?\s+of|callers?\s+of|affect(?:s|ed)?)\b/
18056
18056
  },
18057
18057
  {
18058
18058
  intent: "divergence",
@@ -18064,11 +18064,11 @@ var INTENT_RULES = [
18064
18064
  },
18065
18065
  {
18066
18066
  intent: "observed",
18067
- test: /\b(at\s+runtime|in\s+prod(?:uction)?|actually\s+call\w*|really\s+call\w*|observed|runtime\s+traffic)\b/
18067
+ test: /\b(at\s+runtime|in\s+prod(?:uction)?|actually|really\s+call\w*|observed|runtime\s+traffic|slow|latency|p95|timing)\b/
18068
18068
  },
18069
18069
  {
18070
18070
  intent: "dependencies",
18071
- test: /\b(depend\w*|calls?|uses?|imports?|relies\s+on|needs?)\b/
18071
+ test: /\b(depend\w*|calls?|uses?|imports?|relies\s+on|needs?|talks?\s+to|connects?\s+to|hits?|reads?\s+from|writes?\s+to)\b/
18072
18072
  }
18073
18073
  ];
18074
18074
  function classifyIntent(question) {
@@ -18235,7 +18235,25 @@ var INTENT_WORDS = /* @__PURE__ */ new Set([
18235
18235
  "rely",
18236
18236
  "needs",
18237
18237
  "need",
18238
- "sync"
18238
+ "sync",
18239
+ "talk",
18240
+ "talks",
18241
+ "connect",
18242
+ "connects",
18243
+ "hit",
18244
+ "hits",
18245
+ "read",
18246
+ "reads",
18247
+ "write",
18248
+ "writes",
18249
+ "consumer",
18250
+ "consumers",
18251
+ "caller",
18252
+ "callers",
18253
+ "slow",
18254
+ "latency",
18255
+ "p95",
18256
+ "timing"
18239
18257
  ]);
18240
18258
  function tokens(input) {
18241
18259
  const spaced = input.replace(/([a-z0-9])([A-Z])/g, "$1 $2");
@@ -18259,6 +18277,13 @@ function idBody(id) {
18259
18277
  const colon = id.indexOf(":");
18260
18278
  return colon >= 0 ? id.slice(colon + 1) : id;
18261
18279
  }
18280
+ var TYPE_NOUNS = [
18281
+ { nouns: ["database", "db", "table"], type: import_types64.NodeType.DatabaseNode },
18282
+ { nouns: ["service", "api", "backend"], type: import_types64.NodeType.ServiceNode },
18283
+ { nouns: ["route", "endpoint"], type: import_types64.NodeType.RouteNode },
18284
+ { nouns: ["file"], type: import_types64.NodeType.FileNode },
18285
+ { nouns: ["config"], type: import_types64.NodeType.ConfigNode }
18286
+ ];
18262
18287
  var EMBED_MIN_SCORE = 0.35;
18263
18288
  async function resolveEntities(graph, question, searchIndex, maxNodes) {
18264
18289
  const qTokens = queryTokens(question);
@@ -18266,6 +18291,7 @@ async function resolveEntities(graph, question, searchIndex, maxNodes) {
18266
18291
  const best = /* @__PURE__ */ new Map();
18267
18292
  const consider = (cand) => {
18268
18293
  const cur = best.get(cand.nodeId);
18294
+ if (cur?.via === "id" && cand.via === "embedding") return;
18269
18295
  if (!cur || cand.score > cur.score) best.set(cand.nodeId, cand);
18270
18296
  };
18271
18297
  graph.forEachNode((id, attrs) => {
@@ -18275,12 +18301,12 @@ async function resolveEntities(graph, question, searchIndex, maxNodes) {
18275
18301
  const body = idBody(id);
18276
18302
  const labelTokens = /* @__PURE__ */ new Set([...tokens(body), ...tokens(name)]);
18277
18303
  if (labelTokens.size === 0) return;
18278
- let matched = 0;
18279
- for (const t of qTokens) if (labelTokens.has(t)) matched += 1;
18304
+ let matched2 = 0;
18305
+ for (const t of qTokens) if (labelTokens.has(t)) matched2 += 1;
18280
18306
  const named2 = body.length >= 2 && normalized.includes(body.toLowerCase()) || name.length >= 2 && normalized.includes(name.toLowerCase());
18281
- if (matched === 0 && !named2) return;
18282
- const coverage = qTokens.length > 0 ? matched / qTokens.length : 0;
18283
- const via = named2 ? "id" : matched > 0 && tokens(name).some((t) => qTokens.includes(t)) ? "label" : "token";
18307
+ if (matched2 === 0 && !named2) return;
18308
+ const coverage = qTokens.length > 0 ? matched2 / qTokens.length : 0;
18309
+ const via = named2 ? "id" : matched2 > 0 && tokens(name).some((t) => qTokens.includes(t)) ? "label" : "token";
18284
18310
  const score = named2 ? Math.max(0.9, coverage) : Math.min(0.85, 0.3 + 0.7 * coverage);
18285
18311
  consider({ nodeId: id, label: name, via, score: Math.min(1, score) });
18286
18312
  });
@@ -18290,6 +18316,7 @@ async function resolveEntities(graph, question, searchIndex, maxNodes) {
18290
18316
  if (res.provider !== "substring") {
18291
18317
  for (const m of res.matches) {
18292
18318
  if (m.node.type === import_types64.NodeType.FrontierNode) continue;
18319
+ if (!graph.hasNode(m.node.id)) continue;
18293
18320
  const already = best.get(m.node.id);
18294
18321
  if (!already && m.score < EMBED_MIN_SCORE) continue;
18295
18322
  consider({
@@ -18303,10 +18330,31 @@ async function resolveEntities(graph, question, searchIndex, maxNodes) {
18303
18330
  } catch {
18304
18331
  }
18305
18332
  }
18306
- const viaRank = { id: 3, label: 2, token: 1, embedding: 0 };
18307
- return [...best.values()].sort(
18308
- (a, b) => b.score - a.score || viaRank[b.via] - viaRank[a.via] || a.nodeId.localeCompare(b.nodeId)
18333
+ if (best.size === 0) {
18334
+ const words = new Set(tokens(question));
18335
+ const kind = TYPE_NOUNS.find((entry2) => entry2.nouns.some((noun) => words.has(noun)));
18336
+ if (kind) {
18337
+ const ids = graph.filterNodes((_id, attrs) => attrs.type === kind.type).sort();
18338
+ if (ids.length > 1) {
18339
+ return { matched: [], ambiguousType: { noun: kind.nouns.find((noun) => words.has(noun)), ids } };
18340
+ }
18341
+ if (ids.length === 1) {
18342
+ const id = ids[0];
18343
+ return { matched: [{ nodeId: id, label: nodeName(graph.getNodeAttributes(id)), via: "type", score: 0.6 }].slice(0, maxNodes) };
18344
+ }
18345
+ return { matched: [] };
18346
+ }
18347
+ }
18348
+ const viaRank = { id: 4, label: 3, token: 2, type: 1, embedding: 0 };
18349
+ const ranked = [...best.values()].sort(
18350
+ (a, b) => Number(b.via === "id") - Number(a.via === "id") || b.score - a.score || viaRank[b.via] - viaRank[a.via] || a.nodeId.localeCompare(b.nodeId)
18351
+ );
18352
+ const primary = ranked[0];
18353
+ const primaryType = primary && graph.getNodeAttributes(primary.nodeId).type;
18354
+ const matched = ranked.filter(
18355
+ (candidate) => !primary || primary.via !== "id" || primary.score < 0.85 || candidate === primary || graph.getNodeAttributes(candidate.nodeId).type === primaryType || candidate.via !== "embedding" && candidate.score >= primary.score - 0.2
18309
18356
  ).slice(0, maxNodes).map((s) => ({ nodeId: s.nodeId, label: s.label, via: s.via, score: Number(s.score.toFixed(3)) }));
18357
+ return { matched };
18310
18358
  }
18311
18359
  function incidentsForNode(nodeId, incidents) {
18312
18360
  if (!incidents || incidents.length === 0) return [];
@@ -18398,6 +18446,23 @@ function buildBlastSection(graph, node) {
18398
18446
  facts
18399
18447
  };
18400
18448
  }
18449
+ function buildDatabaseCallersSection(graph, node) {
18450
+ const callers = getBlastRadius(graph, node).affectedNodes.filter((item) => {
18451
+ if (graph.getNodeAttributes(item.nodeId).type !== import_types64.NodeType.ServiceNode) return false;
18452
+ return item.path.slice(1, -1).every((id) => {
18453
+ const type = graph.getNodeAttributes(id).type;
18454
+ return type === import_types64.NodeType.FileNode || type === import_types64.NodeType.SymbolNode;
18455
+ });
18456
+ });
18457
+ return {
18458
+ heading: `Services calling ${node} \u2014 ${callers.length}`,
18459
+ facts: callers.length > 0 ? callers.slice(0, MAX_FACTS_PER_SECTION).map((item) => ({
18460
+ text: `${item.nodeId} (distance ${item.distance})`,
18461
+ provenance: item.edgeProvenance,
18462
+ confidence: item.confidence
18463
+ })) : [{ text: `No inbound service callers found for ${node}.` }]
18464
+ };
18465
+ }
18401
18466
  function buildIncidentsSection(node, incidents) {
18402
18467
  const relevant = incidentsForNode(node, incidents);
18403
18468
  if (relevant.length === 0) return null;
@@ -18424,6 +18489,29 @@ function divergenceLine(d) {
18424
18489
  }
18425
18490
  return `[${d.type}] ${d.source} \u2192 ${d.target} \u2014 ${d.reason}`;
18426
18491
  }
18492
+ function runtimeEdgeState(graph) {
18493
+ let observed = false;
18494
+ let stale = false;
18495
+ graph.forEachEdge((_id, attrs) => {
18496
+ if (attrs.provenance === import_types64.Provenance.OBSERVED) observed = true;
18497
+ if (attrs.provenance === import_types64.Provenance.STALE) stale = true;
18498
+ });
18499
+ return { observed, stale };
18500
+ }
18501
+ function pendingRuntimeMessage(graph, divergences) {
18502
+ const count = divergences.filter((d) => d.type === "missing-observed").length;
18503
+ if (count === 0) return void 0;
18504
+ const { observed, stale } = runtimeEdgeState(graph);
18505
+ if (observed) return void 0;
18506
+ const prefix = stale ? "No current runtime observations \u2014 earlier runtime evidence is stale" : "No runtime observed yet";
18507
+ return `${prefix} \u2014 ${count} declared dependenc${count === 1 ? "y is" : "ies are"} waiting to be confirmed. Send traces (or connect a provider) and ask again.`;
18508
+ }
18509
+ function noDivergenceMessage(graph) {
18510
+ if (graph.order === 0) return "Graph is empty \u2014 no declared or observed dependencies to compare.";
18511
+ const { observed, stale } = runtimeEdgeState(graph);
18512
+ if (!observed) return stale ? "No current runtime dependency observations to compare \u2014 earlier runtime evidence is stale." : "No runtime dependency observations to compare with declared code.";
18513
+ return "None \u2014 declared code and observed runtime agree across the graph.";
18514
+ }
18427
18515
  function buildDivergenceSection(graph, node, incidents) {
18428
18516
  const result = computeDivergences(graph, { node, ...incidents ? { incidents } : {} });
18429
18517
  if (result.totalAffected === 0) return null;
@@ -18440,19 +18528,28 @@ function buildDivergenceSection(graph, node, incidents) {
18440
18528
  }
18441
18529
  function buildGlobalDivergenceSection(graph, incidents) {
18442
18530
  const result = computeDivergences(graph, incidents ? { incidents } : {});
18531
+ const pending = pendingRuntimeMessage(graph, result.divergences);
18532
+ const visible = pending ? result.divergences.filter((d) => d.type !== "missing-observed") : result.divergences;
18533
+ if (visible.length === 0 && pending) {
18534
+ return {
18535
+ heading: "Divergences (EXTRACTED vs OBSERVED)",
18536
+ facts: [{ text: pending }]
18537
+ };
18538
+ }
18443
18539
  if (result.totalAffected === 0) {
18444
18540
  return {
18445
18541
  heading: "Divergences (EXTRACTED vs OBSERVED)",
18446
- facts: [{ text: "None \u2014 declared code and observed runtime agree across the graph." }]
18542
+ facts: [{ text: noDivergenceMessage(graph) }]
18447
18543
  };
18448
18544
  }
18449
- const facts = result.divergences.slice(0, MAX_FACTS_PER_SECTION).map((d) => ({
18545
+ const facts = visible.slice(0, MAX_FACTS_PER_SECTION).map((d) => ({
18450
18546
  text: divergenceLine(d),
18451
18547
  confidence: d.confidence
18452
18548
  // Composite by construction (EXTRACTED vs OBSERVED), so no single provenance.
18453
18549
  }));
18550
+ if (pending && facts.length < MAX_FACTS_PER_SECTION) facts.push({ text: pending });
18454
18551
  return {
18455
- heading: `Divergences (EXTRACTED vs OBSERVED) \u2014 ${result.totalAffected}`,
18552
+ heading: `Divergences (EXTRACTED vs OBSERVED) \u2014 ${visible.length}`,
18456
18553
  facts
18457
18554
  };
18458
18555
  }
@@ -18538,13 +18635,12 @@ function buildOverviewSections(graph, incidents) {
18538
18635
  });
18539
18636
  }
18540
18637
  const div = computeDivergences(graph, incidents ? { incidents } : {});
18638
+ const pending = pendingRuntimeMessage(graph, div.divergences);
18639
+ const visibleCount = pending ? div.divergences.filter((d) => d.type !== "missing-observed").length : div.totalAffected;
18640
+ const divergenceText = visibleCount > 0 ? `${visibleCount} divergence${visibleCount === 1 ? "" : "s"} between declared code and observed runtime \u2014 ask "are there any divergences?" for the list.${pending ? ` ${pending}` : ""}` : pending ?? noDivergenceMessage(graph);
18541
18641
  sections.push({
18542
18642
  heading: "Divergences",
18543
- facts: [
18544
- {
18545
- text: div.totalAffected === 0 ? "None \u2014 declared code and observed runtime agree across the graph." : `${div.totalAffected} divergence${div.totalAffected === 1 ? "" : "s"} between declared code and observed runtime \u2014 ask "are there any divergences?" for the list.`
18546
- }
18547
- ]
18643
+ facts: [{ text: divergenceText }]
18548
18644
  });
18549
18645
  return sections;
18550
18646
  }
@@ -18589,6 +18685,8 @@ function summarizeGlobal(intent, sections) {
18589
18685
  const lead = sections[0];
18590
18686
  switch (intent) {
18591
18687
  case "divergence": {
18688
+ const pending = lead?.facts[0]?.text;
18689
+ if (pending?.startsWith("No runtime") || pending?.startsWith("No current runtime") || pending?.startsWith("Graph is empty")) return pending;
18592
18690
  const none = lead?.facts[0]?.text.startsWith("None") ?? true;
18593
18691
  return none ? "No divergences across the graph \u2014 declared code and observed runtime agree." : `${lead.heading} across the graph, highest-confidence first below.`;
18594
18692
  }
@@ -18601,11 +18699,16 @@ function summarizeGlobal(intent, sections) {
18601
18699
  return `System overview across the whole graph: ${sections.length} view${sections.length === 1 ? "" : "s"} below \u2014 shape (nodes/edges by provenance), busiest services, incidents, and divergences.`;
18602
18700
  }
18603
18701
  }
18604
- function summarize(question, intent, matched, primary, sections, scope) {
18702
+ function summarize(question, intent, matched, primary, sections, scope, ambiguousType) {
18605
18703
  if (scope === "global") {
18606
18704
  return summarizeGlobal(intent, sections);
18607
18705
  }
18608
18706
  if (!primary) {
18707
+ if (ambiguousType) {
18708
+ const shown = ambiguousType.ids.slice(0, 10);
18709
+ const more = ambiguousType.ids.length - shown.length;
18710
+ return `"${ambiguousType.noun}" matches ${ambiguousType.ids.length} nodes. Name one by ID: ${shown.join(", ")}${more > 0 ? `, and ${more} more` : ""}.`;
18711
+ }
18609
18712
  return `Nothing in "${question}" resolved to a node in the graph. Name a service, file, route, or table \u2014 e.g. \`ask "what does <service> depend on?"\`. For a graph-wide look, ask for an overview, divergences, or incidents.`;
18610
18713
  }
18611
18714
  const lead = sections[0];
@@ -18628,10 +18731,13 @@ function summarize(question, intent, matched, primary, sections, scope) {
18628
18731
  core = lead ? `${lead.heading} of ${primary}.` : `${primary} has no dependents \u2014 nothing else would break if it failed.`;
18629
18732
  break;
18630
18733
  case "dependencies":
18631
- core = lead ? `${primary}: ${lead.heading.toLowerCase()} listed below.` : `${primary} has no declared dependencies in the graph.`;
18734
+ core = lead?.heading.startsWith("Services calling") ? `${lead.heading}.` : lead ? `${primary}: ${lead.heading.toLowerCase()} listed below.` : `${primary} has no declared dependencies in the graph.`;
18632
18735
  break;
18633
18736
  case "observed":
18634
- core = lead ? `${primary} at runtime: ${lead.facts.length} OBSERVED fact${lead.facts.length === 1 ? "" : "s"}.` : `No runtime traffic OBSERVED for ${primary}.`;
18737
+ {
18738
+ const observed = sections.find((s) => s.heading === "Runtime dependencies (OBSERVED)");
18739
+ core = observed ? `${primary} at runtime: ${observed.facts.length} OBSERVED fact${observed.facts.length === 1 ? "" : "s"}.` : `No runtime traffic OBSERVED for ${primary}.`;
18740
+ }
18635
18741
  break;
18636
18742
  case "incidents":
18637
18743
  core = lead ? `${primary}: ${lead.heading.toLowerCase()}.` : `No incidents recorded against ${primary}.`;
@@ -18648,24 +18754,28 @@ async function askGraph(graph, question, opts = {}) {
18648
18754
  const now = opts.now ?? Date.now();
18649
18755
  const maxNodes = opts.maxNodes ?? DEFAULT_MAX_NODES;
18650
18756
  const intent = classifyIntent(question);
18651
- const matched = await resolveEntities(graph, question, opts.searchIndex, maxNodes);
18757
+ const resolution = await resolveEntities(graph, question, opts.searchIndex, maxNodes);
18758
+ const matched = resolution.matched;
18652
18759
  const primary = matched[0]?.nodeId;
18653
18760
  const sections = [];
18654
18761
  let scope;
18655
18762
  if (primary) {
18656
18763
  scope = "node";
18764
+ const callersQuestion = intent === "dependencies" && graph.getNodeAttributes(primary).type === import_types64.NodeType.DatabaseNode && /\b(?:which|what)\s+services?\s+(?:talks?\s+to|connects?\s+to|uses?|hits?|calls?|reads?\s+from|writes?\s+to)\b/i.test(question);
18765
+ if (callersQuestion) sections.push(buildDatabaseCallersSection(graph, primary));
18657
18766
  for (const kind of SECTION_ORDER[intent]) {
18767
+ if (callersQuestion && kind === "blast") continue;
18658
18768
  const s = buildSection(kind, graph, primary, opts.incidents, now);
18659
18769
  if (s && s.facts.length > 0) sections.push(s);
18660
18770
  }
18661
18771
  } else {
18662
- const global = buildGlobalSections(intent, graph, opts.incidents);
18772
+ const global = resolution.ambiguousType ? null : buildGlobalSections(intent, graph, opts.incidents);
18663
18773
  if (global) {
18664
18774
  scope = "global";
18665
18775
  for (const s of global) if (s.facts.length > 0) sections.push(s);
18666
18776
  }
18667
18777
  }
18668
- const answer = summarize(question, intent, matched, primary, sections, scope);
18778
+ const answer = summarize(question, intent, matched, primary, sections, scope, resolution.ambiguousType);
18669
18779
  const provSet = /* @__PURE__ */ new Set();
18670
18780
  for (const s of sections) for (const f of s.facts) if (f.provenance) provSet.add(f.provenance);
18671
18781
  const confidence = sections[0]?.facts[0]?.confidence;
@@ -20759,9 +20869,9 @@ var RESOURCE_TYPES = [
20759
20869
  function isFirebaseResourceType(value) {
20760
20870
  return RESOURCE_TYPES.includes(value);
20761
20871
  }
20762
- function buildEntriesFilter(sinceIso) {
20872
+ function buildEntriesFilter(sinceIso, resourceTypes = RESOURCE_TYPES) {
20763
20873
  return [
20764
- 'resource.type = ("cloud_function" OR "cloud_run_revision" OR "firebase_domain")',
20874
+ `resource.type = (${resourceTypes.map((t) => `"${t}"`).join(" OR ")})`,
20765
20875
  "httpRequest:*",
20766
20876
  `timestamp >= "${sinceIso}"`
20767
20877
  ].join(" AND ");
@@ -20770,8 +20880,8 @@ var DEFAULT_LOOKBACK_MS = 24 * 60 * 60 * 1e3;
20770
20880
  var ENTRIES_LIST_URL = "https://logging.googleapis.com/v2/entries:list";
20771
20881
  var PAGE_SIZE = 1e3;
20772
20882
  var MAX_PAGES = 20;
20773
- async function fetchHttpRequestLogEntries(creds, sinceIso) {
20774
- const filter = buildEntriesFilter(sinceIso);
20883
+ async function fetchHttpRequestLogEntries(creds, sinceIso, resourceTypes) {
20884
+ const filter = buildEntriesFilter(sinceIso, resourceTypes);
20775
20885
  const out = [];
20776
20886
  let pageToken;
20777
20887
  for (let page = 0; page < MAX_PAGES; page++) {
@@ -20886,7 +20996,38 @@ function mapLogEntriesToSignals(entries) {
20886
20996
 
20887
20997
  // src/connectors/firebase/resolve.ts
20888
20998
  init_cjs_shims();
20999
+ var import_types74 = require("@neat.is/types");
21000
+
21001
+ // src/connectors/infer-service.ts
21002
+ init_cjs_shims();
20889
21003
  var import_types73 = require("@neat.is/types");
21004
+ function normalizeName(name) {
21005
+ return name.toLowerCase().replace(/[^a-z0-9]/g, "");
21006
+ }
21007
+ function inferServiceName(graph, resourceName, method, path98) {
21008
+ const wantedMethod = method.toUpperCase();
21009
+ const wantedPath = normalizePathTemplate(path98);
21010
+ const services = [];
21011
+ const owners = /* @__PURE__ */ new Set();
21012
+ graph.forEachNode((_id, attrs) => {
21013
+ const node = attrs;
21014
+ if (node.type === import_types73.NodeType.ServiceNode && typeof node.name === "string") {
21015
+ services.push(node.name);
21016
+ return;
21017
+ }
21018
+ if (node.type !== import_types73.NodeType.RouteNode) return;
21019
+ const route = attrs;
21020
+ if (!route.pathTemplate || normalizePathTemplate(route.pathTemplate) !== wantedPath) return;
21021
+ const routeMethod = route.method.toUpperCase();
21022
+ if (routeMethod === "ALL" || routeMethod === wantedMethod) owners.add(route.service);
21023
+ });
21024
+ const wanted = normalizeName(resourceName);
21025
+ const byName = services.find((s) => normalizeName(s) === wanted);
21026
+ if (byName) return byName;
21027
+ return owners.size === 1 ? [...owners][0] : null;
21028
+ }
21029
+
21030
+ // src/connectors/firebase/resolve.ts
20890
21031
  function neatServiceNameFor(resourceType, resourceName, serviceMap) {
20891
21032
  switch (resourceType) {
20892
21033
  case "cloud_function":
@@ -20901,7 +21042,7 @@ function routeEntriesFor(graph, serviceName) {
20901
21042
  const entries = [];
20902
21043
  graph.forEachNode((_id, attrs) => {
20903
21044
  const node = attrs;
20904
- if (node.type !== import_types73.NodeType.RouteNode) return;
21045
+ if (node.type !== import_types74.NodeType.RouteNode) return;
20905
21046
  const route = attrs;
20906
21047
  if (route.service !== serviceName) return;
20907
21048
  entries.push({
@@ -20925,32 +21066,38 @@ function createFirebaseResolveTarget(graph, serviceMap) {
20925
21066
  }
20926
21067
  const identity = parseFirebaseTargetName(signal.targetName);
20927
21068
  if (!identity) return null;
20928
- const serviceName = neatServiceNameFor(resourceType, identity.resourceName, serviceMap);
20929
- if (!serviceName) return null;
20930
21069
  const normalizedPath = normalizePathTemplate(identity.path);
21070
+ const serviceName = neatServiceNameFor(resourceType, identity.resourceName, serviceMap) ?? (serviceMap.inferServices ? inferServiceName(graph, identity.resourceName, identity.method, identity.path) : null);
21071
+ if (!serviceName) return null;
20931
21072
  const match = findRoute2(routeEntriesFor(graph, serviceName), identity.method, normalizedPath);
20932
21073
  if (!match) return null;
20933
21074
  return {
20934
21075
  targetNodeId: match.routeNodeId,
20935
21076
  serviceName,
20936
- edgeType: import_types73.EdgeType.CALLS
21077
+ edgeType: import_types74.EdgeType.CALLS
20937
21078
  };
20938
21079
  };
20939
21080
  }
20940
21081
 
20941
21082
  // src/connectors/firebase/index.ts
20942
21083
  var FirebaseConnector = class {
21084
+ constructor(opts = {}) {
21085
+ this.opts = opts;
21086
+ }
21087
+ opts;
20943
21088
  provider = "firebase";
20944
21089
  async poll(ctx) {
20945
21090
  const creds = readFirebaseCredentials(ctx.credentials);
20946
21091
  const sinceIso = ctx.since ?? new Date(Date.now() - DEFAULT_LOOKBACK_MS).toISOString();
20947
- const entries = await fetchHttpRequestLogEntries(creds, sinceIso);
21092
+ const entries = await fetchHttpRequestLogEntries(creds, sinceIso, this.opts.resourceTypes);
20948
21093
  return mapLogEntriesToSignals(entries);
20949
21094
  }
20950
21095
  };
20951
21096
  function createFirebaseConnector(graph, serviceMap) {
20952
21097
  return {
20953
- connector: new FirebaseConnector(),
21098
+ connector: new FirebaseConnector(
21099
+ serviceMap.excludeCloudRun ? { resourceTypes: ["cloud_function", "firebase_domain"] } : {}
21100
+ ),
20954
21101
  resolveTarget: createFirebaseResolveTarget(graph, serviceMap)
20955
21102
  };
20956
21103
  }
@@ -20960,7 +21107,7 @@ init_cjs_shims();
20960
21107
 
20961
21108
  // src/connectors/cloudflare/connector.ts
20962
21109
  init_cjs_shims();
20963
- var import_types75 = require("@neat.is/types");
21110
+ var import_types76 = require("@neat.is/types");
20964
21111
 
20965
21112
  // src/connectors/cloudflare/client.ts
20966
21113
  init_cjs_shims();
@@ -21124,7 +21271,7 @@ function findTaggedWorkerFileNode(graph, workerName) {
21124
21271
  graph.forEachNode((id, attrs) => {
21125
21272
  if (found) return;
21126
21273
  const a = attrs;
21127
- if (a.type === import_types75.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
21274
+ if (a.type === import_types76.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
21128
21275
  found = id;
21129
21276
  }
21130
21277
  });
@@ -21136,7 +21283,7 @@ function findMatchingRouteNode(graph, serviceName, method, path98) {
21136
21283
  graph.forEachNode((id, attrs) => {
21137
21284
  if (found) return;
21138
21285
  const a = attrs;
21139
- if (a.type !== import_types75.NodeType.RouteNode || a.service !== serviceName) return;
21286
+ if (a.type !== import_types76.NodeType.RouteNode || a.service !== serviceName) return;
21140
21287
  if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
21141
21288
  const routeMethod = (a.method ?? "").toUpperCase();
21142
21289
  if (routeMethod !== "ALL" && routeMethod !== method) return;
@@ -21155,11 +21302,11 @@ function createCloudflareResolveTarget(config, graph) {
21155
21302
  };
21156
21303
  const mapping = config.workers?.[scriptName];
21157
21304
  if (mapping) {
21158
- const wholeFileId = (0, import_types75.fileId)(mapping.service, mapping.entryFile);
21305
+ const wholeFileId = (0, import_types76.fileId)(mapping.service, mapping.entryFile);
21159
21306
  return {
21160
21307
  targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
21161
21308
  serviceName: mapping.service,
21162
- edgeType: import_types75.EdgeType.CALLS
21309
+ edgeType: import_types76.EdgeType.CALLS
21163
21310
  };
21164
21311
  }
21165
21312
  const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
@@ -21168,13 +21315,13 @@ function createCloudflareResolveTarget(config, graph) {
21168
21315
  return {
21169
21316
  targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
21170
21317
  serviceName: fileNode.service,
21171
- edgeType: import_types75.EdgeType.CALLS
21318
+ edgeType: import_types76.EdgeType.CALLS
21172
21319
  };
21173
21320
  }
21174
21321
  return {
21175
- targetNodeId: (0, import_types75.infraId)("cloudflare-worker", scriptName),
21322
+ targetNodeId: (0, import_types76.infraId)("cloudflare-worker", scriptName),
21176
21323
  serviceName: scriptName,
21177
- edgeType: import_types75.EdgeType.CALLS,
21324
+ edgeType: import_types76.EdgeType.CALLS,
21178
21325
  ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
21179
21326
  };
21180
21327
  };
@@ -21370,14 +21517,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
21370
21517
 
21371
21518
  // src/connectors/neon/resolve.ts
21372
21519
  init_cjs_shims();
21373
- var import_types79 = require("@neat.is/types");
21520
+ var import_types80 = require("@neat.is/types");
21374
21521
  function createNeonResolveTarget(config) {
21375
21522
  return (signal) => {
21376
21523
  if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
21377
21524
  return {
21378
- targetNodeId: (0, import_types79.infraId)("sql-table", signal.targetName),
21525
+ targetNodeId: (0, import_types80.infraId)("sql-table", signal.targetName),
21379
21526
  serviceName: config.serviceName,
21380
- edgeType: import_types79.EdgeType.CALLS,
21527
+ edgeType: import_types80.EdgeType.CALLS,
21381
21528
  ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
21382
21529
  };
21383
21530
  };
@@ -21558,14 +21705,14 @@ function mapLogEntriesToSignals2(entries) {
21558
21705
 
21559
21706
  // src/connectors/cloud-run/resolve.ts
21560
21707
  init_cjs_shims();
21561
- var import_types83 = require("@neat.is/types");
21708
+ var import_types84 = require("@neat.is/types");
21562
21709
  var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
21563
21710
  function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
21564
21711
  let found = null;
21565
21712
  graph.forEachNode((_id, attrs) => {
21566
21713
  if (found) return;
21567
21714
  const node = attrs;
21568
- if (node.type !== import_types83.NodeType.RouteNode) return;
21715
+ if (node.type !== import_types84.NodeType.RouteNode) return;
21569
21716
  const route = attrs;
21570
21717
  if (route.service !== serviceName || !route.pathTemplate) return;
21571
21718
  if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
@@ -21581,7 +21728,7 @@ function createCloudRunResolveTarget(graph, config) {
21581
21728
  const identity = parseCloudRunTargetName(signal.targetName);
21582
21729
  if (!identity) return null;
21583
21730
  const { serviceName: gcpServiceName, method, path: path98 } = identity;
21584
- const mappedService = config.serviceMap?.[gcpServiceName];
21731
+ const mappedService = config.serviceMap?.[gcpServiceName] ?? (config.inferServices ? inferServiceName(graph, gcpServiceName, method, path98) ?? void 0 : void 0);
21585
21732
  if (mappedService) {
21586
21733
  const routeNodeId = findMatchingRouteNode2(
21587
21734
  graph,
@@ -21590,13 +21737,13 @@ function createCloudRunResolveTarget(graph, config) {
21590
21737
  normalizePathTemplate(path98)
21591
21738
  );
21592
21739
  if (routeNodeId) {
21593
- return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types83.EdgeType.CALLS };
21740
+ return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types84.EdgeType.CALLS };
21594
21741
  }
21595
21742
  }
21596
21743
  return {
21597
- targetNodeId: (0, import_types83.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
21744
+ targetNodeId: (0, import_types84.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
21598
21745
  serviceName: mappedService ?? gcpServiceName,
21599
- edgeType: import_types83.EdgeType.CALLS,
21746
+ edgeType: import_types84.EdgeType.CALLS,
21600
21747
  ensureInfraNode: {
21601
21748
  kind: CLOUD_RUN_SERVICE_INFRA_KIND,
21602
21749
  name: gcpServiceName,
@@ -21778,14 +21925,14 @@ function mapLogEntriesToSignals3(entries) {
21778
21925
 
21779
21926
  // src/connectors/gcp-lb/resolve.ts
21780
21927
  init_cjs_shims();
21781
- var import_types87 = require("@neat.is/types");
21928
+ var import_types88 = require("@neat.is/types");
21782
21929
  var GCP_LB_BACKEND_INFRA_KIND = "gcp-lb-backend";
21783
21930
  function findMatchingRouteNode3(graph, serviceName, method, normalizedPath) {
21784
21931
  let found = null;
21785
21932
  graph.forEachNode((_id, attrs) => {
21786
21933
  if (found) return;
21787
21934
  const node = attrs;
21788
- if (node.type !== import_types87.NodeType.RouteNode) return;
21935
+ if (node.type !== import_types88.NodeType.RouteNode) return;
21789
21936
  const route = attrs;
21790
21937
  if (route.service !== serviceName || !route.pathTemplate) return;
21791
21938
  if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
@@ -21801,7 +21948,7 @@ function createGcpLbResolveTarget(graph, config) {
21801
21948
  const identity = parseGcpLbTargetName(signal.targetName);
21802
21949
  if (!identity) return null;
21803
21950
  const { backendServiceName, method, path: path98 } = identity;
21804
- const mappedService = config.backendServiceMap?.[backendServiceName];
21951
+ const mappedService = config.backendServiceMap?.[backendServiceName] ?? (config.inferServices ? inferServiceName(graph, backendServiceName, method, path98) ?? void 0 : void 0);
21805
21952
  if (mappedService) {
21806
21953
  const routeNodeId = findMatchingRouteNode3(
21807
21954
  graph,
@@ -21810,13 +21957,13 @@ function createGcpLbResolveTarget(graph, config) {
21810
21957
  normalizePathTemplate(path98)
21811
21958
  );
21812
21959
  if (routeNodeId) {
21813
- return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types87.EdgeType.CALLS };
21960
+ return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types88.EdgeType.CALLS };
21814
21961
  }
21815
21962
  }
21816
21963
  return {
21817
- targetNodeId: (0, import_types87.infraId)(GCP_LB_BACKEND_INFRA_KIND, backendServiceName),
21964
+ targetNodeId: (0, import_types88.infraId)(GCP_LB_BACKEND_INFRA_KIND, backendServiceName),
21818
21965
  serviceName: mappedService ?? backendServiceName,
21819
- edgeType: import_types87.EdgeType.CALLS,
21966
+ edgeType: import_types88.EdgeType.CALLS,
21820
21967
  ensureInfraNode: {
21821
21968
  kind: GCP_LB_BACKEND_INFRA_KIND,
21822
21969
  name: backendServiceName,
@@ -21857,7 +22004,7 @@ function createGcpLbConnector(graph, config = {}) {
21857
22004
 
21858
22005
  // src/connectors/render/index.ts
21859
22006
  init_cjs_shims();
21860
- var import_types90 = require("@neat.is/types");
22007
+ var import_types91 = require("@neat.is/types");
21861
22008
 
21862
22009
  // src/connectors/render/types.ts
21863
22010
  init_cjs_shims();
@@ -21935,7 +22082,7 @@ function buildRenderRouteIndex(graph, serviceName) {
21935
22082
  const out = [];
21936
22083
  graph.forEachNode((_id, attrs) => {
21937
22084
  const node = attrs;
21938
- if (node.type !== import_types90.NodeType.RouteNode) return;
22085
+ if (node.type !== import_types91.NodeType.RouteNode) return;
21939
22086
  const route = attrs;
21940
22087
  if (route.service !== serviceName) return;
21941
22088
  out.push({
@@ -22020,7 +22167,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
22020
22167
  function createRenderResolveTarget(config) {
22021
22168
  return (signal) => {
22022
22169
  if (signal.targetKind === ROUTE_TARGET_KIND2) {
22023
- return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types90.EdgeType.CALLS };
22170
+ return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types91.EdgeType.CALLS };
22024
22171
  }
22025
22172
  return null;
22026
22173
  };
@@ -22158,21 +22305,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
22158
22305
 
22159
22306
  // src/connectors/planetscale/resolve.ts
22160
22307
  init_cjs_shims();
22161
- var import_types94 = require("@neat.is/types");
22308
+ var import_types95 = require("@neat.is/types");
22162
22309
  var PLANETSCALE_DATABASE_KIND = "planetscale-database";
22163
22310
  function createPlanetscaleResolveTarget(graph, config) {
22164
22311
  const databaseName = `${config.organization}/${config.database}`;
22165
22312
  return (signal, _ctx) => {
22166
22313
  if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
22167
- const tableId = (0, import_types94.infraId)("sql-table", signal.targetName);
22314
+ const tableId = (0, import_types95.infraId)("sql-table", signal.targetName);
22168
22315
  if (graph.hasNode(tableId)) {
22169
- return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types94.EdgeType.CALLS };
22316
+ return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types95.EdgeType.CALLS };
22170
22317
  }
22171
- const providerId = (0, import_types94.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
22318
+ const providerId = (0, import_types95.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
22172
22319
  return {
22173
22320
  targetNodeId: providerId,
22174
22321
  serviceName: config.serviceName,
22175
- edgeType: import_types94.EdgeType.CALLS,
22322
+ edgeType: import_types95.EdgeType.CALLS,
22176
22323
  ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
22177
22324
  };
22178
22325
  };
@@ -22437,7 +22584,7 @@ function mapBuildsToSignals(builds, serviceName) {
22437
22584
 
22438
22585
  // src/connectors/eas/resolve.ts
22439
22586
  init_cjs_shims();
22440
- var import_types99 = require("@neat.is/types");
22587
+ var import_types100 = require("@neat.is/types");
22441
22588
  var NO_ENV2 = "unknown";
22442
22589
  var EAS_JSON_PHASES = /* @__PURE__ */ new Set(["READ_EAS_JSON"]);
22443
22590
  var APP_CONFIG_PHASES = /* @__PURE__ */ new Set([
@@ -22453,8 +22600,8 @@ function configBasenamesForPhase(phase) {
22453
22600
  function configNodeService(graph, configNodeId) {
22454
22601
  for (const edgeId of graph.inboundEdges(configNodeId)) {
22455
22602
  const edge = graph.getEdgeAttributes(edgeId);
22456
- if (edge.type !== import_types99.EdgeType.CONFIGURED_BY) continue;
22457
- const parsed = (0, import_types99.parseFileId)(edge.source);
22603
+ if (edge.type !== import_types100.EdgeType.CONFIGURED_BY) continue;
22604
+ const parsed = (0, import_types100.parseFileId)(edge.source);
22458
22605
  if (parsed) return parsed.service;
22459
22606
  }
22460
22607
  return null;
@@ -22465,7 +22612,7 @@ function findConfigNode(graph, basenames, serviceName) {
22465
22612
  graph.forEachNode((id, attrs) => {
22466
22613
  if (scoped) return;
22467
22614
  const node = attrs;
22468
- if (node.type !== import_types99.NodeType.ConfigNode) return;
22615
+ if (node.type !== import_types100.NodeType.ConfigNode) return;
22469
22616
  if (typeof node.name !== "string" || !basenames.includes(node.name)) return;
22470
22617
  if (anyMatch === null) anyMatch = id;
22471
22618
  if (configNodeService(graph, id) === serviceName) scoped = id;
@@ -22482,13 +22629,13 @@ function createEasResolveTarget(graph) {
22482
22629
  if (basenames.length > 0) {
22483
22630
  const configNodeId = findConfigNode(graph, basenames, serviceName);
22484
22631
  if (configNodeId) {
22485
- return { targetNodeId: configNodeId, serviceName, edgeType: import_types99.EdgeType.CALLS };
22632
+ return { targetNodeId: configNodeId, serviceName, edgeType: import_types100.EdgeType.CALLS };
22486
22633
  }
22487
22634
  }
22488
22635
  return {
22489
22636
  targetNodeId: resolveFusedServiceId(graph, serviceName, NO_ENV2),
22490
22637
  serviceName,
22491
- edgeType: import_types99.EdgeType.CALLS
22638
+ edgeType: import_types100.EdgeType.CALLS
22492
22639
  };
22493
22640
  };
22494
22641
  }
@@ -23356,11 +23503,11 @@ function registerRoutes(scope, ctx) {
23356
23503
  const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
23357
23504
  const parsed = [];
23358
23505
  for (const c of candidates) {
23359
- const r = import_types102.DivergenceTypeSchema.safeParse(c);
23506
+ const r = import_types103.DivergenceTypeSchema.safeParse(c);
23360
23507
  if (!r.success) {
23361
23508
  return reply.code(400).send({
23362
23509
  error: `unknown divergence type "${c}"`,
23363
- allowed: import_types102.DivergenceTypeSchema.options
23510
+ allowed: import_types103.DivergenceTypeSchema.options
23364
23511
  });
23365
23512
  }
23366
23513
  parsed.push(r.data);
@@ -23756,7 +23903,7 @@ function registerRoutes(scope, ctx) {
23756
23903
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
23757
23904
  let violations = await log.readAll();
23758
23905
  if (req.query.severity) {
23759
- const sev = import_types102.PolicySeveritySchema.safeParse(req.query.severity);
23906
+ const sev = import_types103.PolicySeveritySchema.safeParse(req.query.severity);
23760
23907
  if (!sev.success) {
23761
23908
  return reply.code(400).send({
23762
23909
  error: "invalid severity",
@@ -23795,7 +23942,7 @@ function registerRoutes(scope, ctx) {
23795
23942
  scope.post("/policies/check", async (req, reply) => {
23796
23943
  const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
23797
23944
  if (!proj) return;
23798
- const parsed = import_types102.PoliciesCheckBodySchema.safeParse(req.body ?? {});
23945
+ const parsed = import_types103.PoliciesCheckBodySchema.safeParse(req.body ?? {});
23799
23946
  if (!parsed.success) {
23800
23947
  return reply.code(400).send({
23801
23948
  error: "invalid /policies/check body",
@@ -24115,7 +24262,7 @@ init_auth();
24115
24262
 
24116
24263
  // src/hang-sensor.ts
24117
24264
  init_cjs_shims();
24118
- var import_types103 = require("@neat.is/types");
24265
+ var import_types104 = require("@neat.is/types");
24119
24266
  var RECONCILE_INCIDENT_LIMIT = 500;
24120
24267
  function stageHangSurfaces(graph, incidents) {
24121
24268
  const boundaries = /* @__PURE__ */ new Set();
@@ -24128,7 +24275,7 @@ function stageHangSurfaces(graph, incidents) {
24128
24275
  if (!isBoundaryTimeoutSymptom(ctx)) continue;
24129
24276
  const hop = resolveHangHop(graph, boundary, incidents);
24130
24277
  if (!hop) continue;
24131
- if (stageFrontierEdge(graph, hop.source, hop.target, import_types103.EdgeType.CALLS)) staged++;
24278
+ if (stageFrontierEdge(graph, hop.source, hop.target, import_types104.EdgeType.CALLS)) staged++;
24132
24279
  }
24133
24280
  return staged;
24134
24281
  }
@@ -24495,7 +24642,7 @@ function mapWorkloadsToSignals(deployments, pods, config) {
24495
24642
 
24496
24643
  // src/connectors/kubernetes/resolve.ts
24497
24644
  init_cjs_shims();
24498
- var import_types105 = require("@neat.is/types");
24645
+ var import_types106 = require("@neat.is/types");
24499
24646
  var NO_ENV3 = "unknown";
24500
24647
  function createK8sResolveTarget(graph) {
24501
24648
  return (signal) => {
@@ -24506,7 +24653,7 @@ function createK8sResolveTarget(graph) {
24506
24653
  return {
24507
24654
  targetNodeId: resolveFusedServiceId(graph, serviceName, NO_ENV3),
24508
24655
  serviceName,
24509
- edgeType: import_types105.EdgeType.CALLS
24656
+ edgeType: import_types106.EdgeType.CALLS
24510
24657
  };
24511
24658
  };
24512
24659
  }
@@ -24619,12 +24766,14 @@ function createKubernetesConnector(graph, config, fetchImpl) {
24619
24766
  // src/daemon.ts
24620
24767
  init_cjs_shims();
24621
24768
  var import_node_fs43 = require("fs");
24622
- var import_node_path80 = __toESM(require("path"), 1);
24769
+ var import_node_path81 = __toESM(require("path"), 1);
24623
24770
  var import_node_module = require("module");
24624
24771
  init_otel();
24625
24772
 
24626
24773
  // src/connectors/hosted.ts
24627
24774
  init_cjs_shims();
24775
+ var import_promises2 = require("fs/promises");
24776
+ var import_node_path78 = require("path");
24628
24777
 
24629
24778
  // src/connectors/railway/target-ref.ts
24630
24779
  init_cjs_shims();
@@ -24632,8 +24781,8 @@ init_cjs_shims();
24632
24781
  // src/connectors/hosted-repos.ts
24633
24782
  init_cjs_shims();
24634
24783
  var import_node_os6 = __toESM(require("os"), 1);
24635
- var import_node_path78 = __toESM(require("path"), 1);
24636
- var import_promises2 = require("fs/promises");
24784
+ var import_node_path79 = __toESM(require("path"), 1);
24785
+ var import_promises3 = require("fs/promises");
24637
24786
  var DEFAULT_SYNC_INTERVAL_MS = 5 * 6e4;
24638
24787
  var CLONE_TIMEOUT_MS = 5 * 6e4;
24639
24788
 
@@ -24643,28 +24792,28 @@ init_auth();
24643
24792
  // src/unrouted.ts
24644
24793
  init_cjs_shims();
24645
24794
  var import_node_fs42 = require("fs");
24646
- var import_node_path79 = __toESM(require("path"), 1);
24795
+ var import_node_path80 = __toESM(require("path"), 1);
24647
24796
 
24648
24797
  // src/daemon.ts
24649
- var import_types108 = require("@neat.is/types");
24798
+ var import_types109 = require("@neat.is/types");
24650
24799
  function daemonJsonPath(scanPath) {
24651
- return import_node_path80.default.join(scanPath, "neat-out", "daemon.json");
24800
+ return import_node_path81.default.join(scanPath, "neat-out", "daemon.json");
24652
24801
  }
24653
24802
  function daemonsDiscoveryDir(home) {
24654
24803
  const base = home && home.length > 0 ? home : neatHomeFromEnv();
24655
- return import_node_path80.default.join(base, "daemons");
24804
+ return import_node_path81.default.join(base, "daemons");
24656
24805
  }
24657
24806
  function daemonDiscoveryPath(project, home) {
24658
- return import_node_path80.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
24807
+ return import_node_path81.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
24659
24808
  }
24660
24809
  function sanitizeDiscoveryName(project) {
24661
24810
  return project.replace(/[^A-Za-z0-9._-]/g, "_");
24662
24811
  }
24663
24812
  function neatHomeFromEnv() {
24664
24813
  const env = process.env.NEAT_HOME;
24665
- if (env && env.length > 0) return import_node_path80.default.resolve(env);
24814
+ if (env && env.length > 0) return import_node_path81.default.resolve(env);
24666
24815
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
24667
- return import_node_path80.default.join(home, ".neat");
24816
+ return import_node_path81.default.join(home, ".neat");
24668
24817
  }
24669
24818
  async function readDaemonRecord(scanPath) {
24670
24819
  try {
@@ -24735,7 +24884,7 @@ init_otel_grpc();
24735
24884
  // src/search.ts
24736
24885
  init_cjs_shims();
24737
24886
  var import_node_fs44 = require("fs");
24738
- var import_node_path81 = __toESM(require("path"), 1);
24887
+ var import_node_path82 = __toESM(require("path"), 1);
24739
24888
  var import_node_crypto5 = require("crypto");
24740
24889
  var DEFAULT_LIMIT = 10;
24741
24890
  var NOMIC_DIM = 768;
@@ -24898,7 +25047,7 @@ async function readCache(cachePath) {
24898
25047
  }
24899
25048
  }
24900
25049
  async function writeCache(cachePath, cache) {
24901
- await import_node_fs44.promises.mkdir(import_node_path81.default.dirname(cachePath), { recursive: true });
25050
+ await import_node_fs44.promises.mkdir(import_node_path82.default.dirname(cachePath), { recursive: true });
24902
25051
  await import_node_fs44.promises.writeFile(cachePath, JSON.stringify(cache));
24903
25052
  }
24904
25053
  var VectorIndex = class {
@@ -25092,8 +25241,8 @@ var ALL_PHASES = [
25092
25241
  ];
25093
25242
  function classifyChange(relPath) {
25094
25243
  const phases = /* @__PURE__ */ new Set();
25095
- const base = import_node_path82.default.basename(relPath).toLowerCase();
25096
- const segments = relPath.split(import_node_path82.default.sep).map((s) => s.toLowerCase());
25244
+ const base = import_node_path83.default.basename(relPath).toLowerCase();
25245
+ const segments = relPath.split(import_node_path83.default.sep).map((s) => s.toLowerCase());
25097
25246
  if (base === "package.json" || base === "requirements.txt" || base === "pyproject.toml" || base === "setup.py") {
25098
25247
  phases.add("services");
25099
25248
  phases.add("aliases");
@@ -25233,9 +25382,9 @@ function countWatchableDirs(scanPath, limit) {
25233
25382
  for (const e of entries) {
25234
25383
  if (count >= limit) return;
25235
25384
  if (!e.isDirectory()) continue;
25236
- if (IGNORED_WATCH_PATHS.some((re) => re.test(import_node_path82.default.join(dir, e.name) + import_node_path82.default.sep))) continue;
25385
+ if (IGNORED_WATCH_PATHS.some((re) => re.test(import_node_path83.default.join(dir, e.name) + import_node_path83.default.sep))) continue;
25237
25386
  count++;
25238
- if (depth < 2) visit(import_node_path82.default.join(dir, e.name), depth + 1);
25387
+ if (depth < 2) visit(import_node_path83.default.join(dir, e.name), depth + 1);
25239
25388
  }
25240
25389
  };
25241
25390
  visit(scanPath, 0);
@@ -25253,8 +25402,8 @@ async function startWatch(graph, opts) {
25253
25402
  const projectName = opts.project ?? DEFAULT_PROJECT;
25254
25403
  await loadGraphFromDisk(graph, opts.outPath);
25255
25404
  const detachEventBus = attachGraphToEventBus(graph, { project: projectName });
25256
- const policyFilePath = import_node_path82.default.join(opts.scanPath, "policy.json");
25257
- const policyViolationsPath = import_node_path82.default.join(import_node_path82.default.dirname(opts.outPath), "policy-violations.ndjson");
25405
+ const policyFilePath = import_node_path83.default.join(opts.scanPath, "policy.json");
25406
+ const policyViolationsPath = import_node_path83.default.join(import_node_path83.default.dirname(opts.outPath), "policy-violations.ndjson");
25258
25407
  let policies = [];
25259
25408
  try {
25260
25409
  policies = await loadPolicyFile(policyFilePath);
@@ -25328,7 +25477,7 @@ async function startWatch(graph, opts) {
25328
25477
  assertBindAuthority(host, auth.authToken);
25329
25478
  const port = opts.port ?? 8080;
25330
25479
  const otelPort = opts.otelPort ?? 4318;
25331
- const cachePath = opts.embeddingsCachePath ?? import_node_path82.default.join(import_node_path82.default.dirname(opts.outPath), "embeddings.json");
25480
+ const cachePath = opts.embeddingsCachePath ?? import_node_path83.default.join(import_node_path83.default.dirname(opts.outPath), "embeddings.json");
25332
25481
  let searchIndex;
25333
25482
  try {
25334
25483
  searchIndex = await buildSearchIndex(graph, { cachePath });
@@ -25346,7 +25495,7 @@ async function startWatch(graph, opts) {
25346
25495
  // Paths are derived from the explicit options the watch caller passes
25347
25496
  // — pathsForProject is only used to fill in the embeddings/snapshot
25348
25497
  // fields so the registry shape is complete.
25349
- ...pathsForProject(projectName, import_node_path82.default.dirname(opts.outPath)),
25498
+ ...pathsForProject(projectName, import_node_path83.default.dirname(opts.outPath)),
25350
25499
  snapshotPath: opts.outPath,
25351
25500
  errorsPath: opts.errorsPath,
25352
25501
  staleEventsPath: opts.staleEventsPath
@@ -25464,9 +25613,9 @@ async function startWatch(graph, opts) {
25464
25613
  };
25465
25614
  const onPath = (absPath) => {
25466
25615
  if (shouldIgnore(absPath)) return;
25467
- const rel = import_node_path82.default.relative(opts.scanPath, absPath);
25616
+ const rel = import_node_path83.default.relative(opts.scanPath, absPath);
25468
25617
  if (!rel || rel.startsWith("..")) return;
25469
- pendingPaths.add(rel.split(import_node_path82.default.sep).join("/"));
25618
+ pendingPaths.add(rel.split(import_node_path83.default.sep).join("/"));
25470
25619
  const phases = classifyChange(rel);
25471
25620
  if (phases.size === 0) {
25472
25621
  for (const p of ALL_PHASES) pending.add(p);
@@ -25525,7 +25674,7 @@ async function startWatch(graph, opts) {
25525
25674
  // src/deploy/detect.ts
25526
25675
  init_cjs_shims();
25527
25676
  var import_node_fs46 = require("fs");
25528
- var import_node_path83 = __toESM(require("path"), 1);
25677
+ var import_node_path84 = __toESM(require("path"), 1);
25529
25678
  var import_node_child_process2 = require("child_process");
25530
25679
  var import_node_crypto6 = require("crypto");
25531
25680
  function generateToken() {
@@ -25625,7 +25774,7 @@ async function runDeploy(opts = {}) {
25625
25774
  const token = generateToken();
25626
25775
  switch (substrate) {
25627
25776
  case "docker-compose": {
25628
- const artifactPath = import_node_path83.default.join(cwd, "docker-compose.neat.yml");
25777
+ const artifactPath = import_node_path84.default.join(cwd, "docker-compose.neat.yml");
25629
25778
  const contents = emitDockerCompose(cwd);
25630
25779
  await import_node_fs46.promises.writeFile(artifactPath, contents, "utf8");
25631
25780
  return {
@@ -25633,11 +25782,11 @@ async function runDeploy(opts = {}) {
25633
25782
  artifactPath,
25634
25783
  token,
25635
25784
  contents,
25636
- startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${import_node_path83.default.basename(artifactPath)} up -d`
25785
+ startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${import_node_path84.default.basename(artifactPath)} up -d`
25637
25786
  };
25638
25787
  }
25639
25788
  case "systemd": {
25640
- const artifactPath = import_node_path83.default.join(cwd, "neat.service");
25789
+ const artifactPath = import_node_path84.default.join(cwd, "neat.service");
25641
25790
  const contents = emitSystemdUnit(cwd);
25642
25791
  await import_node_fs46.promises.writeFile(artifactPath, contents, "utf8");
25643
25792
  return {
@@ -25673,7 +25822,7 @@ init_cjs_shims();
25673
25822
  // src/installers/javascript.ts
25674
25823
  init_cjs_shims();
25675
25824
  var import_node_fs47 = require("fs");
25676
- var import_node_path84 = __toESM(require("path"), 1);
25825
+ var import_node_path85 = __toESM(require("path"), 1);
25677
25826
  var import_semver2 = __toESM(require("semver"), 1);
25678
25827
 
25679
25828
  // src/installers/templates.ts
@@ -26343,11 +26492,11 @@ var OTEL_ENV = {
26343
26492
  value: "http://localhost:4318/projects/<project>/v1/traces"
26344
26493
  };
26345
26494
  function serviceNodeName2(pkg, serviceDir) {
26346
- return pkg.name ?? import_node_path84.default.basename(serviceDir);
26495
+ return pkg.name ?? import_node_path85.default.basename(serviceDir);
26347
26496
  }
26348
26497
  function projectToken(pkg, serviceDir, project) {
26349
26498
  if (project && project.length > 0) return project;
26350
- return pkg.name ?? import_node_path84.default.basename(serviceDir);
26499
+ return pkg.name ?? import_node_path85.default.basename(serviceDir);
26351
26500
  }
26352
26501
  async function readJsonFile(p) {
26353
26502
  try {
@@ -26360,16 +26509,16 @@ async function readJsonFile(p) {
26360
26509
  async function detectRuntimeKind(pkgRoot, pkg) {
26361
26510
  const deps = allDeps(pkg);
26362
26511
  if ("react-native" in deps || "expo" in deps) return "react-native";
26363
- const appJson = await readJsonFile(import_node_path84.default.join(pkgRoot, "app.json"));
26512
+ const appJson = await readJsonFile(import_node_path85.default.join(pkgRoot, "app.json"));
26364
26513
  if (appJson && typeof appJson === "object" && "expo" in appJson) {
26365
26514
  return "react-native";
26366
26515
  }
26367
- if (await exists3(import_node_path84.default.join(pkgRoot, "vite.config.js")) || await exists3(import_node_path84.default.join(pkgRoot, "vite.config.ts")) || await exists3(import_node_path84.default.join(pkgRoot, "vite.config.mjs")) || "vite" in deps) {
26516
+ if (await exists3(import_node_path85.default.join(pkgRoot, "vite.config.js")) || await exists3(import_node_path85.default.join(pkgRoot, "vite.config.ts")) || await exists3(import_node_path85.default.join(pkgRoot, "vite.config.mjs")) || "vite" in deps) {
26368
26517
  return "browser-bundle";
26369
26518
  }
26370
- if (await exists3(import_node_path84.default.join(pkgRoot, "wrangler.toml"))) return "cloudflare-workers";
26371
- if (await exists3(import_node_path84.default.join(pkgRoot, "bun.lockb"))) return "bun";
26372
- if (await exists3(import_node_path84.default.join(pkgRoot, "deno.json")) || await exists3(import_node_path84.default.join(pkgRoot, "deno.lock"))) {
26519
+ if (await exists3(import_node_path85.default.join(pkgRoot, "wrangler.toml"))) return "cloudflare-workers";
26520
+ if (await exists3(import_node_path85.default.join(pkgRoot, "bun.lockb"))) return "bun";
26521
+ if (await exists3(import_node_path85.default.join(pkgRoot, "deno.json")) || await exists3(import_node_path85.default.join(pkgRoot, "deno.lock"))) {
26373
26522
  return "deno";
26374
26523
  }
26375
26524
  const engines = pkg.engines ?? {};
@@ -26378,7 +26527,7 @@ async function detectRuntimeKind(pkgRoot, pkg) {
26378
26527
  }
26379
26528
  async function readPackageJson2(serviceDir) {
26380
26529
  try {
26381
- const raw = await import_node_fs47.promises.readFile(import_node_path84.default.join(serviceDir, "package.json"), "utf8");
26530
+ const raw = await import_node_fs47.promises.readFile(import_node_path85.default.join(serviceDir, "package.json"), "utf8");
26382
26531
  return JSON.parse(raw);
26383
26532
  } catch {
26384
26533
  return null;
@@ -26422,7 +26571,7 @@ function needsVersionUpgrade(installed, expected) {
26422
26571
  var NEXT_CONFIG_CANDIDATES = ["next.config.js", "next.config.ts", "next.config.mjs"];
26423
26572
  async function findNextConfig(serviceDir) {
26424
26573
  for (const name of NEXT_CONFIG_CANDIDATES) {
26425
- const candidate = import_node_path84.default.join(serviceDir, name);
26574
+ const candidate = import_node_path85.default.join(serviceDir, name);
26426
26575
  if (await exists3(candidate)) return candidate;
26427
26576
  }
26428
26577
  return null;
@@ -26491,7 +26640,7 @@ function hasRemixDependency(pkg) {
26491
26640
  }
26492
26641
  async function findRemixEntry(serviceDir) {
26493
26642
  for (const rel of REMIX_ENTRY_CANDIDATES) {
26494
- const candidate = import_node_path84.default.join(serviceDir, rel);
26643
+ const candidate = import_node_path85.default.join(serviceDir, rel);
26495
26644
  if (await exists3(candidate)) return candidate;
26496
26645
  }
26497
26646
  return null;
@@ -26503,14 +26652,14 @@ function hasSvelteKitDependency(pkg) {
26503
26652
  }
26504
26653
  async function findSvelteKitHooks(serviceDir) {
26505
26654
  for (const rel of SVELTEKIT_HOOKS_CANDIDATES) {
26506
- const candidate = import_node_path84.default.join(serviceDir, rel);
26655
+ const candidate = import_node_path85.default.join(serviceDir, rel);
26507
26656
  if (await exists3(candidate)) return candidate;
26508
26657
  }
26509
26658
  return null;
26510
26659
  }
26511
26660
  async function findSvelteKitConfig(serviceDir) {
26512
26661
  for (const rel of SVELTEKIT_CONFIG_CANDIDATES) {
26513
- const candidate = import_node_path84.default.join(serviceDir, rel);
26662
+ const candidate = import_node_path85.default.join(serviceDir, rel);
26514
26663
  if (await exists3(candidate)) return candidate;
26515
26664
  }
26516
26665
  return null;
@@ -26521,7 +26670,7 @@ function hasNuxtDependency(pkg) {
26521
26670
  }
26522
26671
  async function findNuxtConfig(serviceDir) {
26523
26672
  for (const name of NUXT_CONFIG_CANDIDATES) {
26524
- const candidate = import_node_path84.default.join(serviceDir, name);
26673
+ const candidate = import_node_path85.default.join(serviceDir, name);
26525
26674
  if (await exists3(candidate)) return candidate;
26526
26675
  }
26527
26676
  return null;
@@ -26532,7 +26681,7 @@ function hasAstroDependency(pkg) {
26532
26681
  }
26533
26682
  async function findAstroConfig(serviceDir) {
26534
26683
  for (const name of ASTRO_CONFIG_CANDIDATES) {
26535
- const candidate = import_node_path84.default.join(serviceDir, name);
26684
+ const candidate = import_node_path85.default.join(serviceDir, name);
26536
26685
  if (await exists3(candidate)) return candidate;
26537
26686
  }
26538
26687
  return null;
@@ -26546,7 +26695,7 @@ function parseNextMajor(range) {
26546
26695
  return Number.isFinite(n) ? n : null;
26547
26696
  }
26548
26697
  async function isTypeScriptProject(serviceDir) {
26549
- return exists3(import_node_path84.default.join(serviceDir, "tsconfig.json"));
26698
+ return exists3(import_node_path85.default.join(serviceDir, "tsconfig.json"));
26550
26699
  }
26551
26700
  var INDEX_EXTENSIONS = [".ts", ".tsx", ".js", ".mjs", ".cjs"];
26552
26701
  var INDEX_CANDIDATES = INDEX_EXTENSIONS.map((ext) => `index${ext}`);
@@ -26595,7 +26744,7 @@ function entryFromScript(script) {
26595
26744
  }
26596
26745
  async function resolveEntry(serviceDir, pkg) {
26597
26746
  if (typeof pkg.main === "string" && pkg.main.length > 0) {
26598
- const candidate = import_node_path84.default.resolve(serviceDir, pkg.main);
26747
+ const candidate = import_node_path85.default.resolve(serviceDir, pkg.main);
26599
26748
  if (await exists3(candidate)) return candidate;
26600
26749
  }
26601
26750
  if (pkg.bin) {
@@ -26609,40 +26758,40 @@ async function resolveEntry(serviceDir, pkg) {
26609
26758
  if (typeof first === "string") binEntry = first;
26610
26759
  }
26611
26760
  if (binEntry) {
26612
- const candidate = import_node_path84.default.resolve(serviceDir, binEntry);
26761
+ const candidate = import_node_path85.default.resolve(serviceDir, binEntry);
26613
26762
  if (await exists3(candidate)) return candidate;
26614
26763
  }
26615
26764
  }
26616
26765
  const startEntry = entryFromScript(pkg.scripts?.start);
26617
26766
  if (startEntry) {
26618
- const candidate = import_node_path84.default.resolve(serviceDir, startEntry);
26767
+ const candidate = import_node_path85.default.resolve(serviceDir, startEntry);
26619
26768
  if (await exists3(candidate)) return candidate;
26620
26769
  }
26621
26770
  const devEntry = entryFromScript(pkg.scripts?.dev);
26622
26771
  if (devEntry) {
26623
- const candidate = import_node_path84.default.resolve(serviceDir, devEntry);
26772
+ const candidate = import_node_path85.default.resolve(serviceDir, devEntry);
26624
26773
  if (await exists3(candidate)) return candidate;
26625
26774
  }
26626
26775
  for (const rel of SRC_INDEX_CANDIDATES) {
26627
- const candidate = import_node_path84.default.join(serviceDir, rel);
26776
+ const candidate = import_node_path85.default.join(serviceDir, rel);
26628
26777
  if (await exists3(candidate)) return candidate;
26629
26778
  }
26630
26779
  for (const rel of SRC_NAMED_CANDIDATES) {
26631
- const candidate = import_node_path84.default.join(serviceDir, rel);
26780
+ const candidate = import_node_path85.default.join(serviceDir, rel);
26632
26781
  if (await exists3(candidate)) return candidate;
26633
26782
  }
26634
26783
  for (const rel of ROOT_NAMED_CANDIDATES) {
26635
- const candidate = import_node_path84.default.join(serviceDir, rel);
26784
+ const candidate = import_node_path85.default.join(serviceDir, rel);
26636
26785
  if (await exists3(candidate)) return candidate;
26637
26786
  }
26638
26787
  for (const name of INDEX_CANDIDATES) {
26639
- const candidate = import_node_path84.default.join(serviceDir, name);
26788
+ const candidate = import_node_path85.default.join(serviceDir, name);
26640
26789
  if (await exists3(candidate)) return candidate;
26641
26790
  }
26642
26791
  return null;
26643
26792
  }
26644
26793
  function dispatchEntry(entryFile, pkg) {
26645
- const ext = import_node_path84.default.extname(entryFile).toLowerCase();
26794
+ const ext = import_node_path85.default.extname(entryFile).toLowerCase();
26646
26795
  if (ext === ".ts" || ext === ".tsx") return pkg.type === "module" ? "ts" : "ts-cjs";
26647
26796
  if (ext === ".mjs") return "esm";
26648
26797
  if (ext === ".cjs") return "cjs";
@@ -26660,9 +26809,9 @@ function otelInitContents(flavor) {
26660
26809
  return OTEL_INIT_CJS;
26661
26810
  }
26662
26811
  function injectionLine(flavor, entryFile, otelInitFile) {
26663
- let rel = import_node_path84.default.relative(import_node_path84.default.dirname(entryFile), otelInitFile);
26812
+ let rel = import_node_path85.default.relative(import_node_path85.default.dirname(entryFile), otelInitFile);
26664
26813
  if (!rel.startsWith(".")) rel = `./${rel}`;
26665
- rel = rel.split(import_node_path84.default.sep).join("/");
26814
+ rel = rel.split(import_node_path85.default.sep).join("/");
26666
26815
  if (flavor === "cjs") return `require('${rel}')`;
26667
26816
  if (flavor === "esm") return `import '${rel}'`;
26668
26817
  const tsRel = rel.replace(/\.ts$/, "");
@@ -26675,27 +26824,27 @@ function lineIsOtelInjection(line) {
26675
26824
  }
26676
26825
  async function detectsSrcLayout(serviceDir) {
26677
26826
  const [hasSrcApp, hasSrcPages, hasRootApp, hasRootPages] = await Promise.all([
26678
- exists3(import_node_path84.default.join(serviceDir, "src", "app")),
26679
- exists3(import_node_path84.default.join(serviceDir, "src", "pages")),
26680
- exists3(import_node_path84.default.join(serviceDir, "app")),
26681
- exists3(import_node_path84.default.join(serviceDir, "pages"))
26827
+ exists3(import_node_path85.default.join(serviceDir, "src", "app")),
26828
+ exists3(import_node_path85.default.join(serviceDir, "src", "pages")),
26829
+ exists3(import_node_path85.default.join(serviceDir, "app")),
26830
+ exists3(import_node_path85.default.join(serviceDir, "pages"))
26682
26831
  ]);
26683
26832
  return (hasSrcApp || hasSrcPages) && !hasRootApp && !hasRootPages;
26684
26833
  }
26685
26834
  async function planNext(serviceDir, pkg, manifestPath, nextConfigPath, project) {
26686
26835
  const useTs = await isTypeScriptProject(serviceDir);
26687
26836
  const srcLayout = await detectsSrcLayout(serviceDir);
26688
- const baseDir = srcLayout ? import_node_path84.default.join(serviceDir, "src") : serviceDir;
26689
- const instrumentationFile = import_node_path84.default.join(baseDir, useTs ? "instrumentation.ts" : "instrumentation.js");
26690
- const instrumentationNodeFile = import_node_path84.default.join(
26837
+ const baseDir = srcLayout ? import_node_path85.default.join(serviceDir, "src") : serviceDir;
26838
+ const instrumentationFile = import_node_path85.default.join(baseDir, useTs ? "instrumentation.ts" : "instrumentation.js");
26839
+ const instrumentationNodeFile = import_node_path85.default.join(
26691
26840
  baseDir,
26692
26841
  useTs ? "instrumentation.node.ts" : "instrumentation.node.js"
26693
26842
  );
26694
- const instrumentationEdgeFile = import_node_path84.default.join(
26843
+ const instrumentationEdgeFile = import_node_path85.default.join(
26695
26844
  baseDir,
26696
26845
  useTs ? "instrumentation.edge.ts" : "instrumentation.edge.js"
26697
26846
  );
26698
- const envNeatFile = import_node_path84.default.join(baseDir, ".env.neat");
26847
+ const envNeatFile = import_node_path85.default.join(baseDir, ".env.neat");
26699
26848
  const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
26700
26849
  const dependencyEdits = [];
26701
26850
  for (const sdk of SDK_PACKAGES) {
@@ -26820,7 +26969,7 @@ function buildDependencyEdits(pkg, manifestPath) {
26820
26969
  return edits;
26821
26970
  }
26822
26971
  async function queueEnvNeat(serviceDir, pkg, project, generatedFiles) {
26823
- const envNeatFile = import_node_path84.default.join(serviceDir, ".env.neat");
26972
+ const envNeatFile = import_node_path85.default.join(serviceDir, ".env.neat");
26824
26973
  if (!await exists3(envNeatFile)) {
26825
26974
  generatedFiles.push({
26826
26975
  file: envNeatFile,
@@ -26855,7 +27004,7 @@ function fileImportsOtelHook(raw, specifiers) {
26855
27004
  }
26856
27005
  async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
26857
27006
  const useTs = await isTypeScriptProject(serviceDir);
26858
- const otelServerFile = import_node_path84.default.join(
27007
+ const otelServerFile = import_node_path85.default.join(
26859
27008
  serviceDir,
26860
27009
  useTs ? "app/otel.server.ts" : "app/otel.server.js"
26861
27010
  );
@@ -26912,11 +27061,11 @@ async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
26912
27061
  }
26913
27062
  async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project) {
26914
27063
  const useTs = await isTypeScriptProject(serviceDir);
26915
- const otelInitFile = import_node_path84.default.join(
27064
+ const otelInitFile = import_node_path85.default.join(
26916
27065
  serviceDir,
26917
27066
  useTs ? "src/otel-init.ts" : "src/otel-init.js"
26918
27067
  );
26919
- const resolvedHooksFile = hooksFile ?? import_node_path84.default.join(serviceDir, useTs ? "src/hooks.server.ts" : "src/hooks.server.js");
27068
+ const resolvedHooksFile = hooksFile ?? import_node_path85.default.join(serviceDir, useTs ? "src/hooks.server.ts" : "src/hooks.server.js");
26920
27069
  const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
26921
27070
  const generatedFiles = [];
26922
27071
  const entrypointEdits = [];
@@ -26978,11 +27127,11 @@ async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project)
26978
27127
  }
26979
27128
  async function planNuxt(serviceDir, pkg, manifestPath, project) {
26980
27129
  const useTs = await isTypeScriptProject(serviceDir);
26981
- const otelPluginFile = import_node_path84.default.join(
27130
+ const otelPluginFile = import_node_path85.default.join(
26982
27131
  serviceDir,
26983
27132
  useTs ? "server/plugins/otel.ts" : "server/plugins/otel.js"
26984
27133
  );
26985
- const otelInitFile = import_node_path84.default.join(
27134
+ const otelInitFile = import_node_path85.default.join(
26986
27135
  serviceDir,
26987
27136
  useTs ? "server/plugins/otel-init.ts" : "server/plugins/otel-init.js"
26988
27137
  );
@@ -27033,19 +27182,19 @@ async function planNuxt(serviceDir, pkg, manifestPath, project) {
27033
27182
  var ASTRO_MIDDLEWARE_CANDIDATES = ["src/middleware.ts", "src/middleware.js"];
27034
27183
  async function findAstroMiddleware(serviceDir) {
27035
27184
  for (const rel of ASTRO_MIDDLEWARE_CANDIDATES) {
27036
- const candidate = import_node_path84.default.join(serviceDir, rel);
27185
+ const candidate = import_node_path85.default.join(serviceDir, rel);
27037
27186
  if (await exists3(candidate)) return candidate;
27038
27187
  }
27039
27188
  return null;
27040
27189
  }
27041
27190
  async function planAstro(serviceDir, pkg, manifestPath, project) {
27042
27191
  const useTs = await isTypeScriptProject(serviceDir);
27043
- const otelInitFile = import_node_path84.default.join(
27192
+ const otelInitFile = import_node_path85.default.join(
27044
27193
  serviceDir,
27045
27194
  useTs ? "src/otel-init.ts" : "src/otel-init.js"
27046
27195
  );
27047
27196
  const existingMiddleware = await findAstroMiddleware(serviceDir);
27048
- const middlewareFile = existingMiddleware ?? import_node_path84.default.join(serviceDir, useTs ? "src/middleware.ts" : "src/middleware.js");
27197
+ const middlewareFile = existingMiddleware ?? import_node_path85.default.join(serviceDir, useTs ? "src/middleware.ts" : "src/middleware.js");
27049
27198
  const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
27050
27199
  const generatedFiles = [];
27051
27200
  const entrypointEdits = [];
@@ -27141,7 +27290,7 @@ async function findFrameworkDispatch(serviceDir, pkg, manifestPath, project) {
27141
27290
  }
27142
27291
  async function plan(serviceDir, opts) {
27143
27292
  const pkg = await readPackageJson2(serviceDir);
27144
- const manifestPath = import_node_path84.default.join(serviceDir, "package.json");
27293
+ const manifestPath = import_node_path85.default.join(serviceDir, "package.json");
27145
27294
  const project = opts?.project;
27146
27295
  const empty = {
27147
27296
  language: "javascript",
@@ -27176,8 +27325,8 @@ async function plan(serviceDir, opts) {
27176
27325
  return { ...empty, libOnly: true };
27177
27326
  }
27178
27327
  const flavor = dispatchEntry(entryFile, pkg);
27179
- const otelInitFile = import_node_path84.default.join(import_node_path84.default.dirname(entryFile), otelInitFilename(flavor));
27180
- const envNeatFile = import_node_path84.default.join(serviceDir, ".env.neat");
27328
+ const otelInitFile = import_node_path85.default.join(import_node_path85.default.dirname(entryFile), otelInitFilename(flavor));
27329
+ const envNeatFile = import_node_path85.default.join(serviceDir, ".env.neat");
27181
27330
  const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
27182
27331
  const dependencyEdits = [];
27183
27332
  for (const sdk of SDK_PACKAGES) {
@@ -27246,13 +27395,13 @@ async function plan(serviceDir, opts) {
27246
27395
  };
27247
27396
  }
27248
27397
  function isAllowedWritePath(serviceDir, target) {
27249
- const rel = import_node_path84.default.relative(serviceDir, target);
27398
+ const rel = import_node_path85.default.relative(serviceDir, target);
27250
27399
  if (rel.startsWith("..")) return false;
27251
- const base = import_node_path84.default.basename(target);
27400
+ const base = import_node_path85.default.basename(target);
27252
27401
  if (base === "package.json") return true;
27253
27402
  if (base === ".env.neat") return true;
27254
27403
  if (/^otel-init\.(?:js|cjs|mjs|ts)$/.test(base)) return true;
27255
- const relPosix = rel.split(import_node_path84.default.sep).join("/");
27404
+ const relPosix = rel.split(import_node_path85.default.sep).join("/");
27256
27405
  if (/^instrumentation(?:\.(?:node|edge))?\.(?:js|cjs|mjs|ts)$/.test(base)) {
27257
27406
  if (relPosix === base) return true;
27258
27407
  if (relPosix === `src/${base}`) return true;
@@ -27269,7 +27418,7 @@ function isAllowedWritePath(serviceDir, target) {
27269
27418
  return false;
27270
27419
  }
27271
27420
  async function writeAtomic(file, contents) {
27272
- await import_node_fs47.promises.mkdir(import_node_path84.default.dirname(file), { recursive: true });
27421
+ await import_node_fs47.promises.mkdir(import_node_path85.default.dirname(file), { recursive: true });
27273
27422
  const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
27274
27423
  await import_node_fs47.promises.writeFile(tmp, contents, "utf8");
27275
27424
  await import_node_fs47.promises.rename(tmp, file);
@@ -27453,7 +27602,7 @@ async function rollback(installPlan, originals, createdFiles) {
27453
27602
  ...removed.map((f) => `removed: ${f}`),
27454
27603
  ""
27455
27604
  ];
27456
- const rollbackPath = import_node_path84.default.join(installPlan.serviceDir, "neat-rollback.patch");
27605
+ const rollbackPath = import_node_path85.default.join(installPlan.serviceDir, "neat-rollback.patch");
27457
27606
  await import_node_fs47.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
27458
27607
  }
27459
27608
  function injectInstrumentationHook(raw) {
@@ -27484,7 +27633,7 @@ var javascriptInstaller = {
27484
27633
  // src/installers/python.ts
27485
27634
  init_cjs_shims();
27486
27635
  var import_node_fs48 = require("fs");
27487
- var import_node_path85 = __toESM(require("path"), 1);
27636
+ var import_node_path86 = __toESM(require("path"), 1);
27488
27637
  var SDK_PACKAGES2 = [
27489
27638
  { name: "opentelemetry-distro", version: ">=0.49b0" },
27490
27639
  { name: "opentelemetry-exporter-otlp", version: ">=1.28.0" }
@@ -27582,7 +27731,7 @@ async function writeFileAtomic(file, contents) {
27582
27731
  await import_node_fs48.promises.rename(tmp, file);
27583
27732
  }
27584
27733
  async function resolvePyEntrypoint(serviceDir) {
27585
- const procfile = import_node_path85.default.join(serviceDir, "Procfile");
27734
+ const procfile = import_node_path86.default.join(serviceDir, "Procfile");
27586
27735
  if (await exists4(procfile)) {
27587
27736
  const raw = await import_node_fs48.promises.readFile(procfile, "utf8");
27588
27737
  for (const line of raw.split(/\r?\n/)) {
@@ -27592,20 +27741,20 @@ async function resolvePyEntrypoint(serviceDir) {
27592
27741
  const asgi = cmd.match(/\b(?:uvicorn|gunicorn|hypercorn|daphne)\s+([\w.]+):/);
27593
27742
  if (asgi) {
27594
27743
  const modPath = asgi[1].replace(/\./g, "/");
27595
- const asFile = import_node_path85.default.join(serviceDir, `${modPath}.py`);
27744
+ const asFile = import_node_path86.default.join(serviceDir, `${modPath}.py`);
27596
27745
  if (await exists4(asFile)) return asFile;
27597
- const asPkg = import_node_path85.default.join(serviceDir, modPath, "__init__.py");
27746
+ const asPkg = import_node_path86.default.join(serviceDir, modPath, "__init__.py");
27598
27747
  if (await exists4(asPkg)) return asPkg;
27599
27748
  }
27600
27749
  const runFile = cmd.match(/\b(?:python3?|fastapi\s+(?:run|dev))\s+([\w./-]+\.py)\b/);
27601
27750
  if (runFile) {
27602
- const p = import_node_path85.default.join(serviceDir, runFile[1]);
27751
+ const p = import_node_path86.default.join(serviceDir, runFile[1]);
27603
27752
  if (await exists4(p)) return p;
27604
27753
  }
27605
27754
  }
27606
27755
  }
27607
27756
  for (const name of ["main.py", "app.py", "asgi.py", "wsgi.py", "manage.py", "server.py"]) {
27608
- const p = import_node_path85.default.join(serviceDir, name);
27757
+ const p = import_node_path86.default.join(serviceDir, name);
27609
27758
  if (await exists4(p)) return p;
27610
27759
  }
27611
27760
  return null;
@@ -27631,7 +27780,7 @@ async function exists4(p) {
27631
27780
  async function detect2(serviceDir) {
27632
27781
  const markers = ["requirements.txt", "pyproject.toml", "setup.py"];
27633
27782
  for (const m of markers) {
27634
- if (await exists4(import_node_path85.default.join(serviceDir, m))) return true;
27783
+ if (await exists4(import_node_path86.default.join(serviceDir, m))) return true;
27635
27784
  }
27636
27785
  return false;
27637
27786
  }
@@ -27641,7 +27790,7 @@ function reqPackageName(line) {
27641
27790
  return head.replace(/[<>=!~].*$/, "").toLowerCase();
27642
27791
  }
27643
27792
  async function planRequirementsTxtEdits(serviceDir) {
27644
- const file = import_node_path85.default.join(serviceDir, "requirements.txt");
27793
+ const file = import_node_path86.default.join(serviceDir, "requirements.txt");
27645
27794
  if (!await exists4(file)) return null;
27646
27795
  const raw = await import_node_fs48.promises.readFile(file, "utf8");
27647
27796
  const presentNames = new Set(
@@ -27651,7 +27800,7 @@ async function planRequirementsTxtEdits(serviceDir) {
27651
27800
  return { manifest: file, missing: [...missing] };
27652
27801
  }
27653
27802
  async function planProcfileEdits(serviceDir) {
27654
- const procfile = import_node_path85.default.join(serviceDir, "Procfile");
27803
+ const procfile = import_node_path86.default.join(serviceDir, "Procfile");
27655
27804
  if (!await exists4(procfile)) return [];
27656
27805
  const raw = await import_node_fs48.promises.readFile(procfile, "utf8");
27657
27806
  const edits = [];
@@ -27689,7 +27838,7 @@ async function plan2(serviceDir) {
27689
27838
  }
27690
27839
  const entrypointEdits = await planProcfileEdits(serviceDir);
27691
27840
  const entryFile = await resolvePyEntrypoint(serviceDir);
27692
- const generatedFiles = entryFile ? [{ file: import_node_path85.default.join(serviceDir, NEAT_OTEL_FILENAME), contents: neatOtelPy() }] : [];
27841
+ const generatedFiles = entryFile ? [{ file: import_node_path86.default.join(serviceDir, NEAT_OTEL_FILENAME), contents: neatOtelPy() }] : [];
27693
27842
  if (dependencyEdits.length === 0 && entrypointEdits.length === 0 && !entryFile) {
27694
27843
  return empty;
27695
27844
  }
@@ -27757,7 +27906,7 @@ async function apply2(installPlan) {
27757
27906
  if (raw === void 0) {
27758
27907
  throw new Error(`python installer: cannot read ${file} during apply`);
27759
27908
  }
27760
- const base = import_node_path85.default.basename(file);
27909
+ const base = import_node_path86.default.basename(file);
27761
27910
  if (base === "requirements.txt") {
27762
27911
  const edits = installPlan.dependencyEdits.filter((e) => e.file === file);
27763
27912
  if (edits.length > 0) {
@@ -27812,7 +27961,7 @@ async function rollback2(installPlan, originals, createdFiles = []) {
27812
27961
  ...removed.map((f) => `removed: ${f}`),
27813
27962
  ""
27814
27963
  ];
27815
- const rollbackPath = import_node_path85.default.join(installPlan.serviceDir, "neat-rollback.patch");
27964
+ const rollbackPath = import_node_path86.default.join(installPlan.serviceDir, "neat-rollback.patch");
27816
27965
  await import_node_fs48.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
27817
27966
  }
27818
27967
  var pythonInstaller = {
@@ -27825,7 +27974,7 @@ var pythonInstaller = {
27825
27974
  // src/installers/go.ts
27826
27975
  init_cjs_shims();
27827
27976
  var import_node_fs49 = require("fs");
27828
- var import_node_path86 = __toESM(require("path"), 1);
27977
+ var import_node_path87 = __toESM(require("path"), 1);
27829
27978
  var GO_DEPS = [
27830
27979
  { name: "go.opentelemetry.io/otel", version: "v1.38.0" },
27831
27980
  { name: "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp", version: "v1.38.0" },
@@ -27835,13 +27984,13 @@ async function exists5(file) {
27835
27984
  return import_node_fs49.promises.stat(file).then(() => true, () => false);
27836
27985
  }
27837
27986
  async function findMain(serviceDir) {
27838
- const root = import_node_path86.default.join(serviceDir, "main.go");
27987
+ const root = import_node_path87.default.join(serviceDir, "main.go");
27839
27988
  if (await exists5(root)) return root;
27840
- const cmd = import_node_path86.default.join(serviceDir, "cmd");
27989
+ const cmd = import_node_path87.default.join(serviceDir, "cmd");
27841
27990
  const entries = await import_node_fs49.promises.readdir(cmd, { withFileTypes: true }).catch(() => []);
27842
27991
  for (const entry2 of entries.sort((a, b) => a.name.localeCompare(b.name))) {
27843
27992
  if (!entry2.isDirectory()) continue;
27844
- const candidate = import_node_path86.default.join(cmd, entry2.name, "main.go");
27993
+ const candidate = import_node_path87.default.join(cmd, entry2.name, "main.go");
27845
27994
  if (await exists5(candidate)) return candidate;
27846
27995
  }
27847
27996
  return null;
@@ -27916,17 +28065,17 @@ func init() {
27916
28065
  `;
27917
28066
  }
27918
28067
  async function detect3(serviceDir) {
27919
- return exists5(import_node_path86.default.join(serviceDir, "go.mod"));
28068
+ return exists5(import_node_path87.default.join(serviceDir, "go.mod"));
27920
28069
  }
27921
28070
  async function plan3(serviceDir) {
27922
- const manifest = import_node_path86.default.join(serviceDir, "go.mod");
28071
+ const manifest = import_node_path87.default.join(serviceDir, "go.mod");
27923
28072
  const raw = await import_node_fs49.promises.readFile(manifest, "utf8");
27924
28073
  const main2 = await findMain(serviceDir);
27925
28074
  const dependencyEdits = GO_DEPS.filter((dep) => !raw.includes(dep.name)).map((dep) => ({ file: manifest, kind: "add", ...dep }));
27926
28075
  if (!main2) return { language: "go", serviceDir, dependencyEdits: [], entrypointEdits: [], envEdits: [], libOnly: true };
27927
28076
  const source = await import_node_fs49.promises.readFile(main2, "utf8");
27928
28077
  const packageName = source.match(/^\s*package\s+(\w+)\s*$/m)?.[1] ?? "main";
27929
- const generated = import_node_path86.default.join(import_node_path86.default.dirname(main2), "neat_otel.go");
28078
+ const generated = import_node_path87.default.join(import_node_path87.default.dirname(main2), "neat_otel.go");
27930
28079
  const generatedFiles = await exists5(generated) ? [] : [{ file: generated, contents: neatOtelGo(packageName) }];
27931
28080
  return { language: "go", serviceDir, dependencyEdits, entrypointEdits: [], envEdits: [], generatedFiles, entryFile: main2 };
27932
28081
  }
@@ -27960,7 +28109,7 @@ var goInstaller = { name: "go", detect: detect3, plan: plan3, apply: apply3 };
27960
28109
  // src/installers/ruby.ts
27961
28110
  init_cjs_shims();
27962
28111
  var import_node_fs50 = require("fs");
27963
- var import_node_path87 = __toESM(require("path"), 1);
28112
+ var import_node_path88 = __toESM(require("path"), 1);
27964
28113
  var RUBY_MARKERS = [
27965
28114
  "Gemfile",
27966
28115
  "Gemfile.lock"
@@ -27971,7 +28120,7 @@ var RUBY_GEMS = [
27971
28120
  { name: "opentelemetry-instrumentation-all", version: "~> 0.62" }
27972
28121
  ];
27973
28122
  var NEAT_OTEL_STAMP2 = "neat-otel-init v1";
27974
- var INITIALIZER_REL = import_node_path87.default.join("config", "initializers", "neat_otel.rb");
28123
+ var INITIALIZER_REL = import_node_path88.default.join("config", "initializers", "neat_otel.rb");
27975
28124
  function neatOtelRb(opts = {}) {
27976
28125
  const service = opts.project ?? "ruby-service";
27977
28126
  const endpoint2 = opts.project ? `http://localhost:4318/projects/${opts.project}/v1/traces` : "http://localhost:4318/v1/traces";
@@ -28060,14 +28209,14 @@ async function exists6(p) {
28060
28209
  }
28061
28210
  async function detect4(serviceDir) {
28062
28211
  for (const marker of RUBY_MARKERS) {
28063
- if (await exists6(import_node_path87.default.join(serviceDir, marker))) return true;
28212
+ if (await exists6(import_node_path88.default.join(serviceDir, marker))) return true;
28064
28213
  }
28065
28214
  return false;
28066
28215
  }
28067
28216
  async function isRailsApp(serviceDir, gemfile) {
28068
28217
  if (gemfile && /^\s*gem\s+['"]rails['"]/m.test(gemfile)) return true;
28069
28218
  for (const marker of ["config/application.rb", "config/environment.rb", "bin/rails"]) {
28070
- if (await exists6(import_node_path87.default.join(serviceDir, marker))) return true;
28219
+ if (await exists6(import_node_path88.default.join(serviceDir, marker))) return true;
28071
28220
  }
28072
28221
  return false;
28073
28222
  }
@@ -28076,7 +28225,7 @@ function gemPresent(gemfile, name) {
28076
28225
  return new RegExp(`^\\s*gem\\s+['"]${escaped}['"]`, "m").test(gemfile);
28077
28226
  }
28078
28227
  async function readGemfile(serviceDir) {
28079
- const file = import_node_path87.default.join(serviceDir, "Gemfile");
28228
+ const file = import_node_path88.default.join(serviceDir, "Gemfile");
28080
28229
  if (!await exists6(file)) return null;
28081
28230
  return { file, body: await import_node_fs50.promises.readFile(file, "utf8") };
28082
28231
  }
@@ -28098,7 +28247,7 @@ async function plan4(serviceDir, opts) {
28098
28247
  }
28099
28248
  }
28100
28249
  const rails = await isRailsApp(serviceDir, gemfile?.body ?? null);
28101
- const initializer = import_node_path87.default.join(serviceDir, INITIALIZER_REL);
28250
+ const initializer = import_node_path88.default.join(serviceDir, INITIALIZER_REL);
28102
28251
  const generatedFiles = [];
28103
28252
  if (rails && !await exists6(initializer)) {
28104
28253
  generatedFiles.push({
@@ -28163,7 +28312,7 @@ async function rollback3(serviceDir, language, originals, created) {
28163
28312
  ...removed.map((f) => `removed: ${f}`),
28164
28313
  ""
28165
28314
  ];
28166
- await import_node_fs50.promises.writeFile(import_node_path87.default.join(serviceDir, "neat-rollback.patch"), body.join("\n"), "utf8");
28315
+ await import_node_fs50.promises.writeFile(import_node_path88.default.join(serviceDir, "neat-rollback.patch"), body.join("\n"), "utf8");
28167
28316
  }
28168
28317
  async function apply4(installPlan) {
28169
28318
  const { serviceDir } = installPlan;
@@ -28184,7 +28333,7 @@ async function apply4(installPlan) {
28184
28333
  try {
28185
28334
  for (const gf of generatedFiles) {
28186
28335
  if (await exists6(gf.file)) continue;
28187
- await import_node_fs50.promises.mkdir(import_node_path87.default.dirname(gf.file), { recursive: true });
28336
+ await import_node_fs50.promises.mkdir(import_node_path88.default.dirname(gf.file), { recursive: true });
28188
28337
  await writeFileAtomic2(gf.file, gf.contents);
28189
28338
  writtenFiles.push(gf.file);
28190
28339
  created.push(gf.file);
@@ -28202,7 +28351,7 @@ async function apply4(installPlan) {
28202
28351
  await rollback3(serviceDir, installPlan.language, originals, created);
28203
28352
  throw err;
28204
28353
  }
28205
- const wroteManifest = writtenFiles.some((f) => import_node_path87.default.basename(f) === "Gemfile");
28354
+ const wroteManifest = writtenFiles.some((f) => import_node_path88.default.basename(f) === "Gemfile");
28206
28355
  return {
28207
28356
  serviceDir,
28208
28357
  outcome: writtenFiles.length > 0 ? "instrumented" : "already-instrumented",
@@ -28215,7 +28364,7 @@ var rubyInstaller = { name: "ruby", detect: detect4, plan: plan4, apply: apply4
28215
28364
  // src/installers/php.ts
28216
28365
  init_cjs_shims();
28217
28366
  var import_node_fs51 = require("fs");
28218
- var import_node_path88 = __toESM(require("path"), 1);
28367
+ var import_node_path89 = __toESM(require("path"), 1);
28219
28368
  var PHP_MARKERS = [
28220
28369
  "composer.json",
28221
28370
  "composer.lock"
@@ -28292,7 +28441,7 @@ async function exists7(p) {
28292
28441
  }
28293
28442
  async function detect5(serviceDir) {
28294
28443
  for (const marker of PHP_MARKERS) {
28295
- if (await exists7(import_node_path88.default.join(serviceDir, marker))) return true;
28444
+ if (await exists7(import_node_path89.default.join(serviceDir, marker))) return true;
28296
28445
  }
28297
28446
  return false;
28298
28447
  }
@@ -28316,13 +28465,13 @@ async function plan5(serviceDir, opts) {
28316
28465
  entrypointEdits: [],
28317
28466
  envEdits: []
28318
28467
  };
28319
- const composerPath = import_node_path88.default.join(serviceDir, "composer.json");
28468
+ const composerPath = import_node_path89.default.join(serviceDir, "composer.json");
28320
28469
  const hasComposer = await exists7(composerPath);
28321
28470
  const dependencyEdits = [];
28322
28471
  if (hasComposer) {
28323
28472
  const body = await import_node_fs51.promises.readFile(composerPath, "utf8");
28324
28473
  const { require: require2, requireDev } = readComposerObject(body);
28325
- const laravel = "laravel/framework" in require2 || "laravel/framework" in requireDev || await exists7(import_node_path88.default.join(serviceDir, "artisan"));
28474
+ const laravel = "laravel/framework" in require2 || "laravel/framework" in requireDev || await exists7(import_node_path89.default.join(serviceDir, "artisan"));
28326
28475
  const wanted = laravel ? PHP_PACKAGES : PHP_PACKAGES.filter((p) => p.name !== LARAVEL_PACKAGE);
28327
28476
  for (const pkg of wanted) {
28328
28477
  if (!(pkg.name in require2)) {
@@ -28330,7 +28479,7 @@ async function plan5(serviceDir, opts) {
28330
28479
  }
28331
28480
  }
28332
28481
  }
28333
- const bootstrap = import_node_path88.default.join(serviceDir, NEAT_OTEL_FILENAME2);
28482
+ const bootstrap = import_node_path89.default.join(serviceDir, NEAT_OTEL_FILENAME2);
28334
28483
  const generatedFiles = [];
28335
28484
  if (hasComposer && !await exists7(bootstrap)) {
28336
28485
  generatedFiles.push({ file: bootstrap, contents: neatOtelPhp({ project: opts?.project }), skipIfExists: true });
@@ -28402,7 +28551,7 @@ async function rollback4(serviceDir, language, originals, created) {
28402
28551
  ...removed.map((f) => `removed: ${f}`),
28403
28552
  ""
28404
28553
  ];
28405
- await import_node_fs51.promises.writeFile(import_node_path88.default.join(serviceDir, "neat-rollback.patch"), body.join("\n"), "utf8");
28554
+ await import_node_fs51.promises.writeFile(import_node_path89.default.join(serviceDir, "neat-rollback.patch"), body.join("\n"), "utf8");
28406
28555
  }
28407
28556
  async function apply5(installPlan) {
28408
28557
  const { serviceDir } = installPlan;
@@ -28423,7 +28572,7 @@ async function apply5(installPlan) {
28423
28572
  try {
28424
28573
  for (const gf of generatedFiles) {
28425
28574
  if (await exists7(gf.file)) continue;
28426
- await import_node_fs51.promises.mkdir(import_node_path88.default.dirname(gf.file), { recursive: true });
28575
+ await import_node_fs51.promises.mkdir(import_node_path89.default.dirname(gf.file), { recursive: true });
28427
28576
  await writeFileAtomic3(gf.file, gf.contents);
28428
28577
  writtenFiles.push(gf.file);
28429
28578
  created.push(gf.file);
@@ -28442,10 +28591,10 @@ async function apply5(installPlan) {
28442
28591
  throw err;
28443
28592
  }
28444
28593
  if (writtenFiles.length > 0) {
28445
- console.warn(`neat: PHP instrumentation staged in ${import_node_path88.default.basename(serviceDir)}, but a system step remains:
28594
+ console.warn(`neat: PHP instrumentation staged in ${import_node_path89.default.basename(serviceDir)}, but a system step remains:
28446
28595
  ${PHP_PECL_CAVEAT}`);
28447
28596
  }
28448
- const wroteManifest = writtenFiles.some((f) => import_node_path88.default.basename(f) === "composer.json");
28597
+ const wroteManifest = writtenFiles.some((f) => import_node_path89.default.basename(f) === "composer.json");
28449
28598
  return {
28450
28599
  serviceDir,
28451
28600
  outcome: writtenFiles.length > 0 ? "instrumented" : "already-instrumented",
@@ -28580,7 +28729,7 @@ init_cjs_shims();
28580
28729
  var import_node_fs52 = require("fs");
28581
28730
  var import_node_http = __toESM(require("http"), 1);
28582
28731
  var import_node_net = __toESM(require("net"), 1);
28583
- var import_node_path89 = __toESM(require("path"), 1);
28732
+ var import_node_path90 = __toESM(require("path"), 1);
28584
28733
  var import_node_url4 = require("url");
28585
28734
  var import_node_child_process3 = require("child_process");
28586
28735
  var import_node_readline = __toESM(require("readline"), 1);
@@ -28590,7 +28739,7 @@ async function extractAndPersist(opts) {
28590
28739
  const graphKey = opts.projectExplicit ? opts.project : DEFAULT_PROJECT;
28591
28740
  resetGraph(graphKey);
28592
28741
  const graph = getGraph(graphKey);
28593
- const projectPaths = pathsForProject(graphKey, import_node_path89.default.join(opts.scanPath, "neat-out"));
28742
+ const projectPaths = pathsForProject(graphKey, import_node_path90.default.join(opts.scanPath, "neat-out"));
28594
28743
  const extraction = await extractFromDirectory(graph, opts.scanPath, {
28595
28744
  errorsPath: projectPaths.errorsPath
28596
28745
  });
@@ -28634,7 +28783,7 @@ async function applyInstallersOver(services, project, options = {}) {
28634
28783
  if (outcome.outcome === "instrumented") {
28635
28784
  instrumented++;
28636
28785
  if (plan6.dependencyEdits.length > 0) {
28637
- const manifest = import_node_path89.default.basename(plan6.dependencyEdits[0].file);
28786
+ const manifest = import_node_path90.default.basename(plan6.dependencyEdits[0].file);
28638
28787
  if (manifest === "package.json") {
28639
28788
  const cmd = await resolveManager(svc.dir);
28640
28789
  const key = `${cmd.pm}:${cmd.cwd}`;
@@ -28648,7 +28797,7 @@ async function applyInstallersOver(services, project, options = {}) {
28648
28797
  libOnly++;
28649
28798
  const appDeps = svc.pkg ? appFrameworkDependencies(svc.pkg) : [];
28650
28799
  if (appDeps.length > 0) {
28651
- const svcName = import_node_path89.default.basename(svc.dir);
28800
+ const svcName = import_node_path90.default.basename(svc.dir);
28652
28801
  const list = appDeps.join(", ");
28653
28802
  console.warn(
28654
28803
  `neat: runtime layer won't engage for ${svcName}: no entry point found.
@@ -28661,7 +28810,7 @@ async function applyInstallersOver(services, project, options = {}) {
28661
28810
  console.log(`skipping ${svc.dir}: browser bundle; browser-OTel support lands in a future release.`);
28662
28811
  } else if (outcome.outcome === "react-native") {
28663
28812
  reactNative++;
28664
- const svcName = import_node_path89.default.basename(svc.dir);
28813
+ const svcName = import_node_path90.default.basename(svc.dir);
28665
28814
  console.log(
28666
28815
  `neat: ${svc.dir} detected as React Native / Expo
28667
28816
  The installer doesn't cover this runtime deterministically.
@@ -28672,7 +28821,7 @@ async function applyInstallersOver(services, project, options = {}) {
28672
28821
  );
28673
28822
  } else if (outcome.outcome === "bun") {
28674
28823
  bun++;
28675
- const svcName = import_node_path89.default.basename(svc.dir);
28824
+ const svcName = import_node_path90.default.basename(svc.dir);
28676
28825
  console.log(
28677
28826
  `neat: ${svc.dir} detected as Bun
28678
28827
  The installer doesn't cover this runtime deterministically.
@@ -28683,7 +28832,7 @@ async function applyInstallersOver(services, project, options = {}) {
28683
28832
  );
28684
28833
  } else if (outcome.outcome === "deno") {
28685
28834
  deno++;
28686
- const svcName = import_node_path89.default.basename(svc.dir);
28835
+ const svcName = import_node_path90.default.basename(svc.dir);
28687
28836
  console.log(
28688
28837
  `neat: ${svc.dir} detected as Deno
28689
28838
  The installer doesn't cover this runtime deterministically.
@@ -28694,7 +28843,7 @@ async function applyInstallersOver(services, project, options = {}) {
28694
28843
  );
28695
28844
  } else if (outcome.outcome === "cloudflare-workers") {
28696
28845
  cloudflareWorkers++;
28697
- const svcName = import_node_path89.default.basename(svc.dir);
28846
+ const svcName = import_node_path90.default.basename(svc.dir);
28698
28847
  console.log(
28699
28848
  `neat: ${svc.dir} detected as Cloudflare Workers
28700
28849
  The installer doesn't cover this runtime deterministically.
@@ -28705,7 +28854,7 @@ async function applyInstallersOver(services, project, options = {}) {
28705
28854
  );
28706
28855
  } else if (outcome.outcome === "electron") {
28707
28856
  electron++;
28708
- const svcName = import_node_path89.default.basename(svc.dir);
28857
+ const svcName = import_node_path90.default.basename(svc.dir);
28709
28858
  console.log(
28710
28859
  `neat: ${svc.dir} detected as Electron
28711
28860
  The installer doesn't cover this runtime deterministically.
@@ -28718,7 +28867,7 @@ async function applyInstallersOver(services, project, options = {}) {
28718
28867
  if (svc.pkg && (outcome.outcome === "instrumented" || outcome.outcome === "already-instrumented")) {
28719
28868
  const gaps = uninstrumentedLibraries(svc.pkg);
28720
28869
  if (gaps.length > 0) {
28721
- const svcName = import_node_path89.default.basename(svc.dir);
28870
+ const svcName = import_node_path90.default.basename(svc.dir);
28722
28871
  const list = gaps.join(", ");
28723
28872
  const subject = gaps.length === 1 ? "this library" : "these libraries";
28724
28873
  const aux = gaps.length === 1 ? "isn't" : "aren't";
@@ -28930,8 +29079,8 @@ async function persistedPortsFor(scanPath) {
28930
29079
  return { rest: record.ports.rest, otlp: record.ports.otlp, web: record.ports.web };
28931
29080
  }
28932
29081
  async function acquireSpawnLock(scanPath) {
28933
- const lockPath = import_node_path89.default.join(scanPath, "neat-out", "daemon.spawn.lock");
28934
- await import_node_fs52.promises.mkdir(import_node_path89.default.dirname(lockPath), { recursive: true });
29082
+ const lockPath = import_node_path90.default.join(scanPath, "neat-out", "daemon.spawn.lock");
29083
+ await import_node_fs52.promises.mkdir(import_node_path90.default.dirname(lockPath), { recursive: true });
28935
29084
  const STALE_LOCK_MS = 6e4;
28936
29085
  try {
28937
29086
  const fd = await import_node_fs52.promises.open(lockPath, "wx");
@@ -28976,13 +29125,13 @@ async function healthIsForProject(restPort, project) {
28976
29125
  return false;
28977
29126
  }
28978
29127
  function daemonLogPath(projectPath3) {
28979
- return import_node_path89.default.join(projectPath3, "neat-out", "daemon.log");
29128
+ return import_node_path90.default.join(projectPath3, "neat-out", "daemon.log");
28980
29129
  }
28981
29130
  function spawnDaemonDetached(spec) {
28982
- const here = import_node_path89.default.dirname((0, import_node_url4.fileURLToPath)(importMetaUrl));
29131
+ const here = import_node_path90.default.dirname((0, import_node_url4.fileURLToPath)(importMetaUrl));
28983
29132
  const candidates = [
28984
- import_node_path89.default.join(here, "neatd.cjs"),
28985
- import_node_path89.default.join(here, "neatd.js")
29133
+ import_node_path90.default.join(here, "neatd.cjs"),
29134
+ import_node_path90.default.join(here, "neatd.js")
28986
29135
  ];
28987
29136
  let entry2 = null;
28988
29137
  const fsSync = require("fs");
@@ -29012,7 +29161,7 @@ function spawnDaemonDetached(spec) {
29012
29161
  let logFd = null;
29013
29162
  if (spec) {
29014
29163
  const logPath = daemonLogPath(spec.projectPath);
29015
- fsSync.mkdirSync(import_node_path89.default.dirname(logPath), { recursive: true });
29164
+ fsSync.mkdirSync(import_node_path90.default.dirname(logPath), { recursive: true });
29016
29165
  logFd = fsSync.openSync(logPath, "a");
29017
29166
  }
29018
29167
  const child = (0, import_node_child_process3.spawn)(process.execPath, [entry2, "start"], {
@@ -29211,7 +29360,7 @@ async function runOrchestrator(opts) {
29211
29360
  result.steps.browser = "skipped";
29212
29361
  }
29213
29362
  const daemonRunning = result.steps.daemon === "spawned" || result.steps.daemon === "already-running";
29214
- const daemonLog = daemonRunning ? import_node_path89.default.relative(opts.scanPath, daemonLogPath(opts.scanPath)) : null;
29363
+ const daemonLog = daemonRunning ? import_node_path90.default.relative(opts.scanPath, daemonLogPath(opts.scanPath)) : null;
29215
29364
  printSummary(result, graph, daemonLog);
29216
29365
  return result;
29217
29366
  }
@@ -29691,18 +29840,18 @@ var import_node_crypto7 = require("crypto");
29691
29840
  init_cjs_shims();
29692
29841
  var import_node_fs53 = require("fs");
29693
29842
  var import_node_os7 = __toESM(require("os"), 1);
29694
- var import_node_path90 = __toESM(require("path"), 1);
29843
+ var import_node_path91 = __toESM(require("path"), 1);
29695
29844
  var PROFILES_CONFIG_VERSION = 1;
29696
29845
  function neatHome3() {
29697
29846
  const override = process.env.NEAT_HOME;
29698
- if (override && override.length > 0) return import_node_path90.default.resolve(override);
29699
- return import_node_path90.default.join(import_node_os7.default.homedir(), ".neat");
29847
+ if (override && override.length > 0) return import_node_path91.default.resolve(override);
29848
+ return import_node_path91.default.join(import_node_os7.default.homedir(), ".neat");
29700
29849
  }
29701
29850
  function profilesConfigPath(home = neatHome3()) {
29702
- return import_node_path90.default.join(home, "profiles.json");
29851
+ return import_node_path91.default.join(home, "profiles.json");
29703
29852
  }
29704
29853
  function profilesConfigLockPath(home = neatHome3()) {
29705
- return import_node_path90.default.join(home, "profiles.json.lock");
29854
+ return import_node_path91.default.join(home, "profiles.json.lock");
29706
29855
  }
29707
29856
  var MODE_MASK_LOOSER_THAN_06002 = 63;
29708
29857
  async function warnIfModeLooserThan06002(file) {
@@ -29820,7 +29969,7 @@ function serialize(config) {
29820
29969
  }
29821
29970
  async function writeConfigAtomic(config, home) {
29822
29971
  const file = profilesConfigPath(home);
29823
- await import_node_fs53.promises.mkdir(import_node_path90.default.dirname(file), { recursive: true });
29972
+ await import_node_fs53.promises.mkdir(import_node_path91.default.dirname(file), { recursive: true });
29824
29973
  const tmp = `${file}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
29825
29974
  const fd = await import_node_fs53.promises.open(tmp, "w", 384);
29826
29975
  try {
@@ -29834,7 +29983,7 @@ async function writeConfigAtomic(config, home) {
29834
29983
  var LOCK_RETRY_MS2 = 50;
29835
29984
  var LOCK_TIMEOUT_MS2 = 5e3;
29836
29985
  async function acquireLock2(lockPath) {
29837
- await import_node_fs53.promises.mkdir(import_node_path90.default.dirname(lockPath), { recursive: true });
29986
+ await import_node_fs53.promises.mkdir(import_node_path91.default.dirname(lockPath), { recursive: true });
29838
29987
  const deadline = Date.now() + LOCK_TIMEOUT_MS2;
29839
29988
  for (; ; ) {
29840
29989
  try {
@@ -30194,11 +30343,11 @@ async function runConnectCommand(rawArgs, deps = {}) {
30194
30343
  // src/doctor-cli.ts
30195
30344
  init_cjs_shims();
30196
30345
  var import_node_fs54 = require("fs");
30197
- var import_node_path91 = __toESM(require("path"), 1);
30346
+ var import_node_path92 = __toESM(require("path"), 1);
30198
30347
 
30199
30348
  // src/cli-client.ts
30200
30349
  init_cjs_shims();
30201
- var import_types109 = require("@neat.is/types");
30350
+ var import_types110 = require("@neat.is/types");
30202
30351
  var HttpError = class extends Error {
30203
30352
  constructor(status2, message, responseBody = "") {
30204
30353
  super(message);
@@ -30340,7 +30489,7 @@ async function runBlastRadius(client, input) {
30340
30489
  }
30341
30490
  }
30342
30491
  function formatBlastEntry(n) {
30343
- const tag = n.edgeProvenance === import_types109.Provenance.STALE ? " [STALE \u2014 last seen too long ago]" : "";
30492
+ const tag = n.edgeProvenance === import_types110.Provenance.STALE ? " [STALE \u2014 last seen too long ago]" : "";
30344
30493
  return ` \u2022 ${n.nodeId} (distance ${n.distance}, ${n.edgeProvenance})${tag}`;
30345
30494
  }
30346
30495
  async function runDependencies(client, input) {
@@ -30397,7 +30546,7 @@ async function runObservedDependencies(client, input) {
30397
30546
  if (result.observed) {
30398
30547
  return {
30399
30548
  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.`,
30400
- provenance: import_types109.Provenance.OBSERVED
30549
+ provenance: import_types110.Provenance.OBSERVED
30401
30550
  };
30402
30551
  }
30403
30552
  const note = result.hasExtractedOutbound ? " Static (EXTRACTED) dependencies exist but no runtime traffic has been seen \u2014 is OTel running?" : "";
@@ -30407,7 +30556,7 @@ async function runObservedDependencies(client, input) {
30407
30556
  return {
30408
30557
  summary: `${input.nodeId} has ${result.dependencies.length} runtime dependenc${result.dependencies.length === 1 ? "y" : "ies"} confirmed by OTel.`,
30409
30558
  block: blockLines.join("\n"),
30410
- provenance: import_types109.Provenance.OBSERVED
30559
+ provenance: import_types110.Provenance.OBSERVED
30411
30560
  };
30412
30561
  } catch (err) {
30413
30562
  if (err instanceof HttpError && err.status === 404) {
@@ -30461,7 +30610,7 @@ async function runIncidents(client, input) {
30461
30610
  return {
30462
30611
  summary: `${target} has ${body.total} recorded incident${body.total === 1 ? "" : "s"}; showing the ${ordered.length} most recent.`,
30463
30612
  block: blockLines.join("\n"),
30464
- provenance: import_types109.Provenance.OBSERVED
30613
+ provenance: import_types110.Provenance.OBSERVED
30465
30614
  };
30466
30615
  } catch (err) {
30467
30616
  if (err instanceof HttpError && err.status === 404) {
@@ -30570,7 +30719,7 @@ async function runStaleEdges(client, input) {
30570
30719
  return {
30571
30720
  summary: `${events.length} stale-edge transition${events.length === 1 ? "" : "s"} recorded${input.edgeType ? ` for ${input.edgeType}` : ""}.`,
30572
30721
  block: blockLines.join("\n"),
30573
- provenance: import_types109.Provenance.STALE
30722
+ provenance: import_types110.Provenance.STALE
30574
30723
  };
30575
30724
  }
30576
30725
  async function runPolicies(client, input) {
@@ -30808,7 +30957,7 @@ function checkNode(nodeVersion) {
30808
30957
  }
30809
30958
  async function neatOutExists(cwd) {
30810
30959
  try {
30811
- const st = await import_node_fs54.promises.stat(import_node_path91.default.join(cwd, "neat-out"));
30960
+ const st = await import_node_fs54.promises.stat(import_node_path92.default.join(cwd, "neat-out"));
30812
30961
  return st.isDirectory();
30813
30962
  } catch {
30814
30963
  return false;
@@ -30927,7 +31076,7 @@ async function runDoctorCommand(argv, deps = {}) {
30927
31076
 
30928
31077
  // src/login-cli.ts
30929
31078
  init_cjs_shims();
30930
- var import_promises3 = __toESM(require("readline/promises"), 1);
31079
+ var import_promises4 = __toESM(require("readline/promises"), 1);
30931
31080
  var PROBE_ATTEMPT_TIMEOUT_MS = 3e4;
30932
31081
  var PROBE_TOTAL_BUDGET_MS = 12e4;
30933
31082
  var PROBE_RETRY_PAUSE_MS = 2e3;
@@ -31180,7 +31329,7 @@ async function runLogoutCommand(argv, deps = {}) {
31180
31329
  }
31181
31330
  async function defaultReadLine(prompt) {
31182
31331
  if (!process.stdin.isTTY) return void 0;
31183
- const rl = import_promises3.default.createInterface({ input: process.stdin, output: process.stdout });
31332
+ const rl = import_promises4.default.createInterface({ input: process.stdin, output: process.stdout });
31184
31333
  try {
31185
31334
  return await rl.question(prompt);
31186
31335
  } finally {
@@ -31223,7 +31372,7 @@ async function defaultReadSecret(prompt) {
31223
31372
 
31224
31373
  // src/welcome.ts
31225
31374
  init_cjs_shims();
31226
- var import_promises4 = __toESM(require("readline/promises"), 1);
31375
+ var import_promises5 = __toESM(require("readline/promises"), 1);
31227
31376
  var WORDMARK = [
31228
31377
  "\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557",
31229
31378
  "\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D",
@@ -31306,7 +31455,7 @@ async function shouldShowWelcome(deps = {}) {
31306
31455
  }
31307
31456
  async function defaultReadLine2(prompt) {
31308
31457
  if (!process.stdin.isTTY) return void 0;
31309
- const rl = import_promises4.default.createInterface({ input: process.stdin, output: process.stdout });
31458
+ const rl = import_promises5.default.createInterface({ input: process.stdin, output: process.stdout });
31310
31459
  try {
31311
31460
  return await rl.question(prompt);
31312
31461
  } finally {
@@ -31316,7 +31465,7 @@ async function defaultReadLine2(prompt) {
31316
31465
 
31317
31466
  // src/hooks-cli.ts
31318
31467
  init_cjs_shims();
31319
- var import_node_path92 = __toESM(require("path"), 1);
31468
+ var import_node_path93 = __toESM(require("path"), 1);
31320
31469
  var import_node_os8 = __toESM(require("os"), 1);
31321
31470
  var import_node_fs55 = require("fs");
31322
31471
  var import_node_url5 = require("url");
@@ -31325,14 +31474,14 @@ var GUIDE_FILENAME = "GRAPH_FIRST.md";
31325
31474
  var GUIDE_INSTALL_NAME = "neat-graph-first.md";
31326
31475
  var HOOK_MATCHER = "Grep|Glob|Bash";
31327
31476
  function moduleDir() {
31328
- return typeof __dirname !== "undefined" ? __dirname : import_node_path92.default.dirname((0, import_node_url5.fileURLToPath)(importMetaUrl));
31477
+ return typeof __dirname !== "undefined" ? __dirname : import_node_path93.default.dirname((0, import_node_url5.fileURLToPath)(importMetaUrl));
31329
31478
  }
31330
31479
  async function readSkillAsset(rel) {
31331
31480
  const here = moduleDir();
31332
31481
  const candidates = [
31333
- import_node_path92.default.resolve(here, "../../claude-skill", rel),
31334
- import_node_path92.default.resolve(here, "../../../claude-skill", rel),
31335
- import_node_path92.default.resolve(here, "../claude-skill", rel)
31482
+ import_node_path93.default.resolve(here, "../../claude-skill", rel),
31483
+ import_node_path93.default.resolve(here, "../../../claude-skill", rel),
31484
+ import_node_path93.default.resolve(here, "../claude-skill", rel)
31336
31485
  ];
31337
31486
  for (const candidate of candidates) {
31338
31487
  try {
@@ -31346,20 +31495,20 @@ async function readSkillAsset(rel) {
31346
31495
  }
31347
31496
  function neatHome4() {
31348
31497
  const override = process.env.NEAT_HOME;
31349
- if (override && override.length > 0) return import_node_path92.default.resolve(override);
31350
- return import_node_path92.default.join(import_node_os8.default.homedir(), ".neat");
31498
+ if (override && override.length > 0) return import_node_path93.default.resolve(override);
31499
+ return import_node_path93.default.join(import_node_os8.default.homedir(), ".neat");
31351
31500
  }
31352
31501
  function claudeSettingsPath() {
31353
31502
  const override = process.env.NEAT_CLAUDE_SETTINGS;
31354
- if (override && override.length > 0) return import_node_path92.default.resolve(override);
31503
+ if (override && override.length > 0) return import_node_path93.default.resolve(override);
31355
31504
  const home = process.env.HOME ?? process.env.USERPROFILE ?? import_node_os8.default.homedir();
31356
- return import_node_path92.default.join(home, ".claude", "settings.json");
31505
+ return import_node_path93.default.join(home, ".claude", "settings.json");
31357
31506
  }
31358
31507
  function installedHookPath() {
31359
- return import_node_path92.default.join(neatHome4(), "hooks", HOOK_FILENAME);
31508
+ return import_node_path93.default.join(neatHome4(), "hooks", HOOK_FILENAME);
31360
31509
  }
31361
31510
  function gateFlagPath() {
31362
- return import_node_path92.default.join(neatHome4(), "hooks", "gate-enabled");
31511
+ return import_node_path93.default.join(neatHome4(), "hooks", "gate-enabled");
31363
31512
  }
31364
31513
  function isNeatSearchEntry(entry2) {
31365
31514
  return (entry2.hooks ?? []).some(
@@ -31392,9 +31541,9 @@ async function runHooks(opts) {
31392
31541
  const hookScript = await readSkillAsset(`hooks/${HOOK_FILENAME}`);
31393
31542
  const guide = await readSkillAsset(GUIDE_FILENAME);
31394
31543
  const scriptPath = installedHookPath();
31395
- await import_node_fs55.promises.mkdir(import_node_path92.default.dirname(scriptPath), { recursive: true });
31544
+ await import_node_fs55.promises.mkdir(import_node_path93.default.dirname(scriptPath), { recursive: true });
31396
31545
  await import_node_fs55.promises.writeFile(scriptPath, hookScript, { mode: 493 });
31397
- const guidePath = import_node_path92.default.join(neatHome4(), GUIDE_INSTALL_NAME);
31546
+ const guidePath = import_node_path93.default.join(neatHome4(), GUIDE_INSTALL_NAME);
31398
31547
  await import_node_fs55.promises.writeFile(guidePath, guide, "utf8");
31399
31548
  const settingsFile = claudeSettingsPath();
31400
31549
  let settings = {};
@@ -31421,11 +31570,11 @@ async function runHooks(opts) {
31421
31570
  ...settings,
31422
31571
  hooks: { ...hooks, PreToolUse: preToolUse }
31423
31572
  };
31424
- await import_node_fs55.promises.mkdir(import_node_path92.default.dirname(settingsFile), { recursive: true });
31573
+ await import_node_fs55.promises.mkdir(import_node_path93.default.dirname(settingsFile), { recursive: true });
31425
31574
  await import_node_fs55.promises.writeFile(settingsFile, JSON.stringify(merged, null, 2) + "\n", "utf8");
31426
31575
  const flag = gateFlagPath();
31427
31576
  if (opts.gate) {
31428
- await import_node_fs55.promises.mkdir(import_node_path92.default.dirname(flag), { recursive: true });
31577
+ await import_node_fs55.promises.mkdir(import_node_path93.default.dirname(flag), { recursive: true });
31429
31578
  await import_node_fs55.promises.writeFile(flag, "1\n", "utf8");
31430
31579
  } else {
31431
31580
  await import_node_fs55.promises.rm(flag, { force: true });
@@ -31518,7 +31667,7 @@ async function runHooksCommand(args) {
31518
31667
 
31519
31668
  // src/claude-cli.ts
31520
31669
  init_cjs_shims();
31521
- var import_node_path93 = __toESM(require("path"), 1);
31670
+ var import_node_path94 = __toESM(require("path"), 1);
31522
31671
  var import_node_fs56 = require("fs");
31523
31672
  var NEAT_SECTION_HEADING = "## neat";
31524
31673
  var NEAT_DIRECTIVE_BODY = `This project has NEAT wired in: a live, fused semantic graph of the system \u2014
@@ -31553,8 +31702,8 @@ ${NEAT_DIRECTIVE_BODY}
31553
31702
  }
31554
31703
  function claudeMdPath() {
31555
31704
  const override = process.env.NEAT_CLAUDE_MD;
31556
- if (override && override.length > 0) return import_node_path93.default.resolve(override);
31557
- return import_node_path93.default.join(process.cwd(), "CLAUDE.md");
31705
+ if (override && override.length > 0) return import_node_path94.default.resolve(override);
31706
+ return import_node_path94.default.join(process.cwd(), "CLAUDE.md");
31558
31707
  }
31559
31708
  function splitAroundSection(raw) {
31560
31709
  const lines = raw.split("\n");
@@ -31593,7 +31742,7 @@ async function runInstall() {
31593
31742
  const raw = await readIfExists2(file) ?? "";
31594
31743
  const { before, after, found } = splitAroundSection(raw);
31595
31744
  const next = compose(before, after);
31596
- await import_node_fs56.promises.mkdir(import_node_path93.default.dirname(file), { recursive: true });
31745
+ await import_node_fs56.promises.mkdir(import_node_path94.default.dirname(file), { recursive: true });
31597
31746
  await import_node_fs56.promises.writeFile(file, next, "utf8");
31598
31747
  const verb = raw.length === 0 ? "created" : found ? "refreshed" : "added";
31599
31748
  console.log(`neat claude: ${verb} the \`${NEAT_SECTION_HEADING}\` section in ${file}`);
@@ -31658,7 +31807,7 @@ async function runClaudeCommand(args) {
31658
31807
 
31659
31808
  // src/codex-cli.ts
31660
31809
  init_cjs_shims();
31661
- var import_node_path94 = __toESM(require("path"), 1);
31810
+ var import_node_path95 = __toESM(require("path"), 1);
31662
31811
  var import_node_os9 = __toESM(require("os"), 1);
31663
31812
  var import_node_fs57 = require("fs");
31664
31813
  var import_node_util = require("util");
@@ -31678,14 +31827,14 @@ var NEAT_GRAPH_FIRST_START = "<!-- neat:graph-first -->";
31678
31827
  var NEAT_GRAPH_FIRST_END = "<!-- /neat:graph-first -->";
31679
31828
  function codexConfigPath() {
31680
31829
  const override = process.env.NEAT_CODEX_CONFIG;
31681
- if (override && override.length > 0) return import_node_path94.default.resolve(override);
31830
+ if (override && override.length > 0) return import_node_path95.default.resolve(override);
31682
31831
  const home = process.env.HOME ?? process.env.USERPROFILE ?? import_node_os9.default.homedir();
31683
- return import_node_path94.default.join(home, ".codex", "config.toml");
31832
+ return import_node_path95.default.join(home, ".codex", "config.toml");
31684
31833
  }
31685
31834
  function agentsFilePath() {
31686
31835
  const override = process.env.NEAT_CODEX_AGENTS;
31687
- if (override && override.length > 0) return import_node_path94.default.resolve(override);
31688
- return import_node_path94.default.join(process.cwd(), "AGENTS.md");
31836
+ if (override && override.length > 0) return import_node_path95.default.resolve(override);
31837
+ return import_node_path95.default.join(process.cwd(), "AGENTS.md");
31689
31838
  }
31690
31839
  function isTableHeader(line) {
31691
31840
  return /^\s*\[\[?[^\]]+\]\]?\s*$/.test(line);
@@ -31868,14 +32017,14 @@ async function runCodex(opts) {
31868
32017
  return { exitCode: 0 };
31869
32018
  }
31870
32019
  if (config.changed) {
31871
- await import_node_fs57.promises.mkdir(import_node_path94.default.dirname(configPath), { recursive: true });
32020
+ await import_node_fs57.promises.mkdir(import_node_path95.default.dirname(configPath), { recursive: true });
31872
32021
  await import_node_fs57.promises.writeFile(configPath, config.text, "utf8");
31873
32022
  console.log(`neat codex: wrote [mcp_servers.neat] to ${configPath}`);
31874
32023
  } else {
31875
32024
  console.log(`neat codex: ${configPath} already has NEAT's MCP server`);
31876
32025
  }
31877
32026
  if (agents.changed) {
31878
- await import_node_fs57.promises.mkdir(import_node_path94.default.dirname(agentsPath), { recursive: true });
32027
+ await import_node_fs57.promises.mkdir(import_node_path95.default.dirname(agentsPath), { recursive: true });
31879
32028
  await import_node_fs57.promises.writeFile(agentsPath, agents.text, "utf8");
31880
32029
  console.log(`neat codex: wrote the graph-first block to ${agentsPath}`);
31881
32030
  } else {
@@ -31933,7 +32082,7 @@ async function runCodexCommand(args) {
31933
32082
 
31934
32083
  // src/editors-cli.ts
31935
32084
  init_cjs_shims();
31936
- var import_node_path95 = __toESM(require("path"), 1);
32085
+ var import_node_path96 = __toESM(require("path"), 1);
31937
32086
  var import_node_os10 = __toESM(require("os"), 1);
31938
32087
  var import_node_fs58 = require("fs");
31939
32088
  var import_node_util2 = require("util");
@@ -31959,17 +32108,17 @@ function homeDir() {
31959
32108
  }
31960
32109
  function xdgConfigDir() {
31961
32110
  const xdg = process.env.XDG_CONFIG_HOME;
31962
- return xdg && xdg.length > 0 ? import_node_path95.default.resolve(xdg) : import_node_path95.default.join(homeDir(), ".config");
32111
+ return xdg && xdg.length > 0 ? import_node_path96.default.resolve(xdg) : import_node_path96.default.join(homeDir(), ".config");
31963
32112
  }
31964
32113
  function envOverride(name) {
31965
32114
  const v = process.env[name];
31966
- return v && v.length > 0 ? import_node_path95.default.resolve(v) : void 0;
32115
+ return v && v.length > 0 ? import_node_path96.default.resolve(v) : void 0;
31967
32116
  }
31968
32117
  var CURSOR_CLIENT = {
31969
32118
  id: "cursor",
31970
32119
  label: "Cursor",
31971
32120
  docsUrl: "https://docs.cursor.com/context/mcp",
31972
- mcpConfigPath: () => envOverride("NEAT_CURSOR_CONFIG") ?? import_node_path95.default.join(homeDir(), ".cursor", "mcp.json"),
32121
+ mcpConfigPath: () => envOverride("NEAT_CURSOR_CONFIG") ?? import_node_path96.default.join(homeDir(), ".cursor", "mcp.json"),
31973
32122
  mcpContainerKey: "mcpServers",
31974
32123
  format: "json",
31975
32124
  // Cursor still reads a single `.cursorrules` at the project root (the modern
@@ -31981,7 +32130,7 @@ var DEVIN_CLIENT = {
31981
32130
  id: "devin",
31982
32131
  label: "Devin Desktop (Cascade)",
31983
32132
  docsUrl: "https://docs.devin.ai/desktop/cascade/mcp",
31984
- mcpConfigPath: () => envOverride("NEAT_DEVIN_CONFIG") ?? import_node_path95.default.join(homeDir(), ".codeium", "windsurf", "mcp_config.json"),
32133
+ mcpConfigPath: () => envOverride("NEAT_DEVIN_CONFIG") ?? import_node_path96.default.join(homeDir(), ".codeium", "windsurf", "mcp_config.json"),
31985
32134
  mcpContainerKey: "mcpServers",
31986
32135
  format: "json",
31987
32136
  rulesFileName: ".windsurfrules"
@@ -31990,7 +32139,7 @@ var GEMINI_CLIENT = {
31990
32139
  id: "gemini",
31991
32140
  label: "Gemini CLI",
31992
32141
  docsUrl: "https://github.com/google-gemini/gemini-cli/blob/main/docs/tools/mcp-server.md",
31993
- mcpConfigPath: () => envOverride("NEAT_GEMINI_CONFIG") ?? import_node_path95.default.join(homeDir(), ".gemini", "settings.json"),
32142
+ mcpConfigPath: () => envOverride("NEAT_GEMINI_CONFIG") ?? import_node_path96.default.join(homeDir(), ".gemini", "settings.json"),
31994
32143
  mcpContainerKey: "mcpServers",
31995
32144
  format: "json",
31996
32145
  rulesFileName: "GEMINI.md"
@@ -31999,7 +32148,7 @@ var QWEN_CLIENT = {
31999
32148
  id: "qwen",
32000
32149
  label: "Qwen Code",
32001
32150
  docsUrl: "https://qwenlm.github.io/qwen-code-docs/en/users/features/mcp/",
32002
- mcpConfigPath: () => envOverride("NEAT_QWEN_CONFIG") ?? import_node_path95.default.join(homeDir(), ".qwen", "settings.json"),
32151
+ mcpConfigPath: () => envOverride("NEAT_QWEN_CONFIG") ?? import_node_path96.default.join(homeDir(), ".qwen", "settings.json"),
32003
32152
  mcpContainerKey: "mcpServers",
32004
32153
  format: "json",
32005
32154
  rulesFileName: "QWEN.md"
@@ -32008,7 +32157,7 @@ var AMAZONQ_CLIENT = {
32008
32157
  id: "amazonq",
32009
32158
  label: "Amazon Q Developer CLI",
32010
32159
  docsUrl: "https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/command-line-mcp-configuration.html",
32011
- mcpConfigPath: () => envOverride("NEAT_AMAZONQ_CONFIG") ?? import_node_path95.default.join(homeDir(), ".aws", "amazonq", "mcp.json"),
32160
+ mcpConfigPath: () => envOverride("NEAT_AMAZONQ_CONFIG") ?? import_node_path96.default.join(homeDir(), ".aws", "amazonq", "mcp.json"),
32012
32161
  mcpContainerKey: "mcpServers",
32013
32162
  format: "json"
32014
32163
  };
@@ -32016,7 +32165,7 @@ var ROOCODE_CLIENT = {
32016
32165
  id: "roocode",
32017
32166
  label: "Roo Code",
32018
32167
  docsUrl: "https://roocodeinc.github.io/Roo-Code/features/mcp/using-mcp-in-roo",
32019
- mcpConfigPath: () => envOverride("NEAT_ROOCODE_CONFIG") ?? import_node_path95.default.join(process.cwd(), ".roo", "mcp.json"),
32168
+ mcpConfigPath: () => envOverride("NEAT_ROOCODE_CONFIG") ?? import_node_path96.default.join(process.cwd(), ".roo", "mcp.json"),
32020
32169
  mcpContainerKey: "mcpServers",
32021
32170
  format: "json"
32022
32171
  };
@@ -32029,9 +32178,9 @@ var ZED_CLIENT = {
32029
32178
  if (override) return override;
32030
32179
  if (process.platform === "win32") {
32031
32180
  const appData = process.env.APPDATA;
32032
- if (appData && appData.length > 0) return import_node_path95.default.join(appData, "Zed", "settings.json");
32181
+ if (appData && appData.length > 0) return import_node_path96.default.join(appData, "Zed", "settings.json");
32033
32182
  }
32034
- return import_node_path95.default.join(homeDir(), ".config", "zed", "settings.json");
32183
+ return import_node_path96.default.join(homeDir(), ".config", "zed", "settings.json");
32035
32184
  },
32036
32185
  mcpContainerKey: "context_servers",
32037
32186
  format: "jsonc",
@@ -32041,7 +32190,7 @@ var OPENCODE_CLIENT = {
32041
32190
  id: "opencode",
32042
32191
  label: "OpenCode",
32043
32192
  docsUrl: "https://opencode.ai/docs/mcp-servers/",
32044
- mcpConfigPath: () => envOverride("NEAT_OPENCODE_CONFIG") ?? import_node_path95.default.join(xdgConfigDir(), "opencode", "opencode.json"),
32193
+ mcpConfigPath: () => envOverride("NEAT_OPENCODE_CONFIG") ?? import_node_path96.default.join(xdgConfigDir(), "opencode", "opencode.json"),
32045
32194
  mcpContainerKey: "mcp",
32046
32195
  format: "json",
32047
32196
  serverEntry: NEAT_OPENCODE_SERVER,
@@ -32051,7 +32200,7 @@ var CRUSH_CLIENT = {
32051
32200
  id: "crush",
32052
32201
  label: "Crush",
32053
32202
  docsUrl: "https://charmbracelet-crush.mintlify.app/configuration/mcp",
32054
- mcpConfigPath: () => envOverride("NEAT_CRUSH_CONFIG") ?? import_node_path95.default.join(xdgConfigDir(), "crush", "crush.json"),
32203
+ mcpConfigPath: () => envOverride("NEAT_CRUSH_CONFIG") ?? import_node_path96.default.join(xdgConfigDir(), "crush", "crush.json"),
32055
32204
  mcpContainerKey: "mcp",
32056
32205
  format: "json",
32057
32206
  serverEntry: NEAT_CRUSH_SERVER,
@@ -32156,7 +32305,7 @@ async function runEditorInstall(client, opts) {
32156
32305
  const mcpPath = client.mcpConfigPath();
32157
32306
  const serverEntry = client.serverEntry ?? NEAT_MCP_SERVER;
32158
32307
  const hasRules = typeof client.rulesFileName === "string";
32159
- const rulesPath = hasRules ? import_node_path95.default.join(opts.projectDir, client.rulesFileName) : "";
32308
+ const rulesPath = hasRules ? import_node_path96.default.join(opts.projectDir, client.rulesFileName) : "";
32160
32309
  const mcp = await planMcp(client, mcpPath);
32161
32310
  if (mcp === null) return { exitCode: 1 };
32162
32311
  let existingRules = "";
@@ -32199,10 +32348,10 @@ async function runEditorInstall(client, opts) {
32199
32348
  );
32200
32349
  return { exitCode: 0 };
32201
32350
  }
32202
- await import_node_fs58.promises.mkdir(import_node_path95.default.dirname(mcpPath), { recursive: true });
32351
+ await import_node_fs58.promises.mkdir(import_node_path96.default.dirname(mcpPath), { recursive: true });
32203
32352
  await import_node_fs58.promises.writeFile(mcpPath, mcp.text, "utf8");
32204
32353
  if (hasRules) {
32205
- await import_node_fs58.promises.mkdir(import_node_path95.default.dirname(rulesPath), { recursive: true });
32354
+ await import_node_fs58.promises.mkdir(import_node_path96.default.dirname(rulesPath), { recursive: true });
32206
32355
  await import_node_fs58.promises.writeFile(rulesPath, newRules, "utf8");
32207
32356
  }
32208
32357
  console.log(`neat ${client.id}: wired NEAT into ${client.label}`);
@@ -32266,12 +32415,12 @@ async function runEditorCommand(clientId, args, projectDir = process.cwd()) {
32266
32415
 
32267
32416
  // src/monitor.ts
32268
32417
  init_cjs_shims();
32269
- var import_types110 = require("@neat.is/types");
32418
+ var import_types111 = require("@neat.is/types");
32270
32419
  var OBSERVED_DEP_EDGE_TYPES = /* @__PURE__ */ new Set([
32271
- import_types110.EdgeType.CALLS,
32272
- import_types110.EdgeType.CONNECTS_TO,
32273
- import_types110.EdgeType.PUBLISHES_TO,
32274
- import_types110.EdgeType.CONSUMES_FROM
32420
+ import_types111.EdgeType.CALLS,
32421
+ import_types111.EdgeType.CONNECTS_TO,
32422
+ import_types111.EdgeType.PUBLISHES_TO,
32423
+ import_types111.EdgeType.CONSUMES_FROM
32275
32424
  ]);
32276
32425
  function divergenceKey(d) {
32277
32426
  const column = "column" in d && d.column ? d.column : "";
@@ -32341,7 +32490,7 @@ function formatDivergenceLine2(d) {
32341
32490
  }
32342
32491
  }
32343
32492
  function formatStaleLine(edgeId) {
32344
- const parsed = (0, import_types110.parseEdgeId)(edgeId);
32493
+ const parsed = (0, import_types111.parseEdgeId)(edgeId);
32345
32494
  if (parsed) {
32346
32495
  return `\u22EF stale ${parsed.source} \u2192 ${parsed.target} (observed edge went quiet)`;
32347
32496
  }
@@ -32354,7 +32503,7 @@ function divergenceJson(d) {
32354
32503
  return JSON.stringify({ kind: "divergence", ...d });
32355
32504
  }
32356
32505
  function staleJson(edgeId) {
32357
- const parsed = (0, import_types110.parseEdgeId)(edgeId);
32506
+ const parsed = (0, import_types111.parseEdgeId)(edgeId);
32358
32507
  return JSON.stringify({
32359
32508
  kind: "stale",
32360
32509
  edgeId,
@@ -32437,7 +32586,7 @@ var MonitorEmitter = class {
32437
32586
  // ignores non-OBSERVED edges and non-dependency edge types (structural
32438
32587
  // ownership), so only real runtime dependencies reach stdout.
32439
32588
  emitObservedEdge(edge) {
32440
- if (edge.provenance !== import_types110.Provenance.OBSERVED) return false;
32589
+ if (edge.provenance !== import_types111.Provenance.OBSERVED) return false;
32441
32590
  if (!OBSERVED_DEP_EDGE_TYPES.has(edge.type)) return false;
32442
32591
  const key = `edge|${edge.id}`;
32443
32592
  if (this.seen.has(key)) return false;
@@ -32618,7 +32767,7 @@ async function runMonitor(opts) {
32618
32767
  case "edge-added": {
32619
32768
  const payload = safeParse(frame.data);
32620
32769
  const edge = payload?.edge;
32621
- if (edge && edge.provenance === import_types110.Provenance.OBSERVED) {
32770
+ if (edge && edge.provenance === import_types111.Provenance.OBSERVED) {
32622
32771
  emitter.emitObservedEdge(edge);
32623
32772
  divergences.schedule();
32624
32773
  }
@@ -32705,7 +32854,7 @@ function sleep(ms, signal) {
32705
32854
 
32706
32855
  // src/cli-verbs.ts
32707
32856
  init_cjs_shims();
32708
- var import_node_path96 = __toESM(require("path"), 1);
32857
+ var import_node_path97 = __toESM(require("path"), 1);
32709
32858
  async function resolveProjectEntry(opts) {
32710
32859
  const entries = await listProjects();
32711
32860
  if (opts.project) {
@@ -32715,7 +32864,7 @@ async function resolveProjectEntry(opts) {
32715
32864
  const cwd = opts.cwd ?? process.cwd();
32716
32865
  const resolvedCwd = await normalizeProjectPath(cwd);
32717
32866
  for (const entry2 of entries) {
32718
- if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${import_node_path96.default.sep}`)) {
32867
+ if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${import_node_path97.default.sep}`)) {
32719
32868
  return entry2;
32720
32869
  }
32721
32870
  }
@@ -32889,7 +33038,7 @@ async function runSync(opts) {
32889
33038
  }
32890
33039
 
32891
33040
  // src/cli.ts
32892
- var import_types111 = require("@neat.is/types");
33041
+ var import_types112 = require("@neat.is/types");
32893
33042
  function usage5() {
32894
33043
  const neat = commandPrefix();
32895
33044
  console.log("Installed via npx? Prefix commands with `npx neat.is`, or install once: `npm i -g neat.is`.");
@@ -33301,12 +33450,12 @@ async function runInit(opts) {
33301
33450
  printDiscoveryReport(opts, services);
33302
33451
  const sections = opts.noInstall ? [] : await buildPatchSections(services, opts.project);
33303
33452
  const patch = renderPatch(sections);
33304
- const patchPath = import_node_path97.default.join(opts.scanPath, "neat.patch");
33453
+ const patchPath = import_node_path98.default.join(opts.scanPath, "neat.patch");
33305
33454
  if (opts.dryRun) {
33306
33455
  await import_node_fs59.promises.writeFile(patchPath, patch, "utf8");
33307
33456
  written.push(patchPath);
33308
33457
  console.log(`dry-run: patch written to ${patchPath}`);
33309
- const gitignorePath = import_node_path97.default.join(opts.scanPath, ".gitignore");
33458
+ const gitignorePath = import_node_path98.default.join(opts.scanPath, ".gitignore");
33310
33459
  const gitignoreExists = await import_node_fs59.promises.stat(gitignorePath).then(() => true).catch(() => false);
33311
33460
  const verb = gitignoreExists ? "append" : "create";
33312
33461
  console.log(`dry-run: would ${verb} ${gitignorePath} (add neat-out/)`);
@@ -33318,9 +33467,9 @@ async function runInit(opts) {
33318
33467
  const graph = getGraph(graphKey);
33319
33468
  const projectPaths = pathsForProject(
33320
33469
  graphKey,
33321
- import_node_path97.default.join(opts.scanPath, "neat-out")
33470
+ import_node_path98.default.join(opts.scanPath, "neat-out")
33322
33471
  );
33323
- const errorsPath = import_node_path97.default.join(import_node_path97.default.dirname(opts.outPath), import_node_path97.default.basename(projectPaths.errorsPath));
33472
+ const errorsPath = import_node_path98.default.join(import_node_path98.default.dirname(opts.outPath), import_node_path98.default.basename(projectPaths.errorsPath));
33324
33473
  const result = await extractFromDirectory(graph, opts.scanPath, { errorsPath });
33325
33474
  await saveGraphToDisk(graph, opts.outPath);
33326
33475
  written.push(opts.outPath);
@@ -33440,9 +33589,9 @@ var CLAUDE_SKILL_CONFIG = {
33440
33589
  };
33441
33590
  function claudeConfigPath() {
33442
33591
  const override = process.env.NEAT_CLAUDE_CONFIG;
33443
- if (override && override.length > 0) return import_node_path97.default.resolve(override);
33592
+ if (override && override.length > 0) return import_node_path98.default.resolve(override);
33444
33593
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
33445
- return import_node_path97.default.join(home, ".claude.json");
33594
+ return import_node_path98.default.join(home, ".claude.json");
33446
33595
  }
33447
33596
  async function runSkill(opts) {
33448
33597
  const snippet2 = JSON.stringify(CLAUDE_SKILL_CONFIG, null, 2) + "\n";
@@ -33466,7 +33615,7 @@ async function runSkill(opts) {
33466
33615
  ...existing,
33467
33616
  mcpServers: { ...mcp, neat: CLAUDE_SKILL_CONFIG.mcpServers.neat }
33468
33617
  };
33469
- await import_node_fs59.promises.mkdir(import_node_path97.default.dirname(target), { recursive: true });
33618
+ await import_node_fs59.promises.mkdir(import_node_path98.default.dirname(target), { recursive: true });
33470
33619
  await import_node_fs59.promises.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
33471
33620
  console.log(`neat skill: wrote mcpServers.neat to ${target}`);
33472
33621
  console.log("restart Claude Code to pick up the new MCP server.");
@@ -33588,12 +33737,12 @@ async function main() {
33588
33737
  console.error("neat init: --apply and --dry-run are mutually exclusive");
33589
33738
  process.exit(2);
33590
33739
  }
33591
- const scanPath = import_node_path97.default.resolve(target);
33740
+ const scanPath = import_node_path98.default.resolve(target);
33592
33741
  const projectExplicit = parsed.project !== null;
33593
- const projectName = projectExplicit ? project : import_node_path97.default.basename(scanPath);
33742
+ const projectName = projectExplicit ? project : import_node_path98.default.basename(scanPath);
33594
33743
  const projectKey = projectExplicit ? project : DEFAULT_PROJECT;
33595
- const fallback = pathsForProject(projectKey, import_node_path97.default.join(scanPath, "neat-out")).snapshotPath;
33596
- const outPath = import_node_path97.default.resolve(process.env.NEAT_OUT_PATH ?? fallback);
33744
+ const fallback = pathsForProject(projectKey, import_node_path98.default.join(scanPath, "neat-out")).snapshotPath;
33745
+ const outPath = import_node_path98.default.resolve(process.env.NEAT_OUT_PATH ?? fallback);
33597
33746
  const result = await runInit({
33598
33747
  scanPath,
33599
33748
  outPath,
@@ -33614,21 +33763,21 @@ async function main() {
33614
33763
  usage5();
33615
33764
  process.exit(2);
33616
33765
  }
33617
- const scanPath = import_node_path97.default.resolve(target);
33766
+ const scanPath = import_node_path98.default.resolve(target);
33618
33767
  const stat = await import_node_fs59.promises.stat(scanPath).catch(() => null);
33619
33768
  if (!stat || !stat.isDirectory()) {
33620
33769
  console.error(`neat watch: ${scanPath} is not a directory`);
33621
33770
  process.exit(2);
33622
33771
  }
33623
- const projectPaths = pathsForProject(project, import_node_path97.default.join(scanPath, "neat-out"));
33624
- const outPath = import_node_path97.default.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
33625
- const errorsPath = import_node_path97.default.resolve(
33626
- process.env.NEAT_ERRORS_PATH ?? import_node_path97.default.join(import_node_path97.default.dirname(outPath), import_node_path97.default.basename(projectPaths.errorsPath))
33772
+ const projectPaths = pathsForProject(project, import_node_path98.default.join(scanPath, "neat-out"));
33773
+ const outPath = import_node_path98.default.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
33774
+ const errorsPath = import_node_path98.default.resolve(
33775
+ process.env.NEAT_ERRORS_PATH ?? import_node_path98.default.join(import_node_path98.default.dirname(outPath), import_node_path98.default.basename(projectPaths.errorsPath))
33627
33776
  );
33628
- const staleEventsPath = import_node_path97.default.resolve(
33629
- process.env.NEAT_STALE_EVENTS_PATH ?? import_node_path97.default.join(import_node_path97.default.dirname(outPath), import_node_path97.default.basename(projectPaths.staleEventsPath))
33777
+ const staleEventsPath = import_node_path98.default.resolve(
33778
+ process.env.NEAT_STALE_EVENTS_PATH ?? import_node_path98.default.join(import_node_path98.default.dirname(outPath), import_node_path98.default.basename(projectPaths.staleEventsPath))
33630
33779
  );
33631
- const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? import_node_path97.default.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
33780
+ const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? import_node_path98.default.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
33632
33781
  const handle = await startWatch(getGraph(project), {
33633
33782
  scanPath,
33634
33783
  outPath,
@@ -33637,7 +33786,7 @@ async function main() {
33637
33786
  project,
33638
33787
  // Resolve NEAT_HOME so a `neat watch` picks up connectors added to
33639
33788
  // ~/.neat/connectors.json (#871). Same resolution the rest of the CLI uses.
33640
- neatHome: process.env.NEAT_HOME ? import_node_path97.default.resolve(process.env.NEAT_HOME) : import_node_path97.default.join(import_node_os11.default.homedir(), ".neat"),
33789
+ neatHome: process.env.NEAT_HOME ? import_node_path98.default.resolve(process.env.NEAT_HOME) : import_node_path98.default.join(import_node_os11.default.homedir(), ".neat"),
33641
33790
  ...embeddingsCachePath ? { embeddingsCachePath } : {},
33642
33791
  host: process.env.HOST ?? "0.0.0.0",
33643
33792
  port: Number(process.env.PORT ?? 8080),
@@ -33819,11 +33968,11 @@ async function main() {
33819
33968
  process.exit(1);
33820
33969
  }
33821
33970
  async function tryOrchestrator(cmd, parsed) {
33822
- const scanPath = import_node_path97.default.resolve(cmd);
33971
+ const scanPath = import_node_path98.default.resolve(cmd);
33823
33972
  const stat = await import_node_fs59.promises.stat(scanPath).catch(() => null);
33824
33973
  if (!stat || !stat.isDirectory()) return null;
33825
33974
  const projectExplicit = parsed.project !== null;
33826
- const projectName = projectExplicit ? parsed.project : import_node_path97.default.basename(scanPath);
33975
+ const projectName = projectExplicit ? parsed.project : import_node_path98.default.basename(scanPath);
33827
33976
  const result = await runOrchestrator({
33828
33977
  scanPath,
33829
33978
  project: projectName,
@@ -34062,10 +34211,10 @@ async function runQueryVerb(cmd, parsed) {
34062
34211
  const parts = parsed.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
34063
34212
  const out = [];
34064
34213
  for (const p of parts) {
34065
- const r = import_types111.DivergenceTypeSchema.safeParse(p);
34214
+ const r = import_types112.DivergenceTypeSchema.safeParse(p);
34066
34215
  if (!r.success) {
34067
34216
  console.error(
34068
- `neat divergences: unknown --type "${p}". allowed: ${import_types111.DivergenceTypeSchema.options.join(", ")}`
34217
+ `neat divergences: unknown --type "${p}". allowed: ${import_types112.DivergenceTypeSchema.options.join(", ")}`
34069
34218
  );
34070
34219
  return 2;
34071
34220
  }