@neat.is/core 0.10.6-dev.20261007 → 0.10.6-dev.20261009
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-VL7ANDPI.js → chunk-ECBRGTB2.js} +10 -8
- package/dist/{chunk-VL7ANDPI.js.map → chunk-ECBRGTB2.js.map} +1 -1
- package/dist/{chunk-KTJK2PAH.js → chunk-H2BAPN33.js} +311 -611
- package/dist/chunk-H2BAPN33.js.map +1 -0
- package/dist/chunk-MR64ICXX.js +19 -0
- package/dist/chunk-MR64ICXX.js.map +1 -0
- package/dist/{chunk-EFBWQIKG.js → chunk-QPG2QXCW.js} +11 -27
- package/dist/chunk-QPG2QXCW.js.map +1 -0
- package/dist/chunk-RGYC5U2Q.js +511 -0
- package/dist/chunk-RGYC5U2Q.js.map +1 -0
- package/dist/{chunk-LXQCL4MP.js → chunk-TZXSWSHX.js} +2 -2
- package/dist/{chunk-BC53SCT7.js → chunk-VFZ6CXLI.js} +20 -6
- package/dist/chunk-VFZ6CXLI.js.map +1 -0
- package/dist/cli.cjs +409 -77
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +2 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +251 -77
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +165 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -99
- package/dist/index.d.ts +3 -99
- package/dist/index.js +18 -15
- package/dist/neatd.cjs +165 -17
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +7 -4
- package/dist/neatd.js.map +1 -1
- package/dist/otel-grpc-CCCI772O.js +11 -0
- package/dist/registry.cjs +570 -0
- package/dist/registry.cjs.map +1 -0
- package/dist/registry.d.cts +220 -0
- package/dist/registry.d.ts +220 -0
- package/dist/registry.js +62 -0
- package/dist/registry.js.map +1 -0
- package/dist/server.cjs +185 -23
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +10 -6
- package/dist/server.js.map +1 -1
- package/package.json +8 -3
- package/dist/chunk-BC53SCT7.js.map +0 -1
- package/dist/chunk-EFBWQIKG.js.map +0 -1
- package/dist/chunk-KTJK2PAH.js.map +0 -1
- package/dist/otel-grpc-B7ABHLV7.js +0 -10
- /package/dist/{chunk-LXQCL4MP.js.map → chunk-TZXSWSHX.js.map} +0 -0
- /package/dist/{otel-grpc-B7ABHLV7.js.map → otel-grpc-CCCI772O.js.map} +0 -0
package/dist/cli.cjs
CHANGED
|
@@ -6724,7 +6724,29 @@ function ensureFrontierNode(graph, host, ts) {
|
|
|
6724
6724
|
graph.addNode(id, node);
|
|
6725
6725
|
return id;
|
|
6726
6726
|
}
|
|
6727
|
-
|
|
6727
|
+
var EDGE_ERROR_MESSAGE_MAX = 512;
|
|
6728
|
+
function buildEdgeErrorExemplar(detail, ts) {
|
|
6729
|
+
return {
|
|
6730
|
+
...detail.exceptionType !== void 0 ? { exceptionType: detail.exceptionType } : {},
|
|
6731
|
+
...detail.message !== void 0 ? { message: detail.message.slice(0, EDGE_ERROR_MESSAGE_MAX) } : {},
|
|
6732
|
+
...detail.httpStatusCode !== void 0 ? { httpStatusCode: detail.httpStatusCode } : {},
|
|
6733
|
+
at: ts
|
|
6734
|
+
};
|
|
6735
|
+
}
|
|
6736
|
+
function edgeErrorDetailFromSpan(span) {
|
|
6737
|
+
const exceptionType = span.exception?.type;
|
|
6738
|
+
const message = span.exception?.message;
|
|
6739
|
+
const httpStatusCode = httpResponseStatus(span);
|
|
6740
|
+
if (exceptionType === void 0 && message === void 0 && httpStatusCode === void 0) {
|
|
6741
|
+
return void 0;
|
|
6742
|
+
}
|
|
6743
|
+
return {
|
|
6744
|
+
...exceptionType !== void 0 ? { exceptionType } : {},
|
|
6745
|
+
...message !== void 0 ? { message } : {},
|
|
6746
|
+
...httpStatusCode !== void 0 ? { httpStatusCode } : {}
|
|
6747
|
+
};
|
|
6748
|
+
}
|
|
6749
|
+
function upsertObservedEdge(graph, type, source, target, ts, isError = false, evidence, durationMs, errorDetail) {
|
|
6728
6750
|
if (!graph.hasNode(source) || !graph.hasNode(target)) return null;
|
|
6729
6751
|
const grain = source.startsWith("file:") || source.startsWith("symbol:") ? "file" : "service";
|
|
6730
6752
|
const id = makeObservedEdgeId(type, source, target);
|
|
@@ -6734,13 +6756,15 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false, ev
|
|
|
6734
6756
|
const newErrorCount = (existing.signal?.errorCount ?? 0) + (isError ? 1 : 0);
|
|
6735
6757
|
const latencyHist2 = durationMs !== void 0 ? recordLatency({ ...existing.signal?.latencyHist ?? {} }, durationMs) : existing.signal?.latencyHist;
|
|
6736
6758
|
const latencyMs2 = latencyPercentiles(latencyHist2) ?? existing.signal?.latencyMs;
|
|
6759
|
+
const lastError2 = isError && errorDetail ? buildEdgeErrorExemplar(errorDetail, ts) : existing.signal?.lastError;
|
|
6737
6760
|
const newSignal = {
|
|
6738
6761
|
spanCount: newSpanCount,
|
|
6739
6762
|
errorCount: newErrorCount,
|
|
6740
6763
|
lastObservedAgeMs: 0,
|
|
6741
6764
|
...latencyHist2 ? { latencyHist: latencyHist2 } : {},
|
|
6742
6765
|
...latencyMs2 ? { latencyMs: latencyMs2 } : {},
|
|
6743
|
-
...existing.signal?.anomalous !== void 0 ? { anomalous: existing.signal.anomalous } : {}
|
|
6766
|
+
...existing.signal?.anomalous !== void 0 ? { anomalous: existing.signal.anomalous } : {},
|
|
6767
|
+
...lastError2 ? { lastError: lastError2 } : {}
|
|
6744
6768
|
};
|
|
6745
6769
|
const updated = {
|
|
6746
6770
|
...existing,
|
|
@@ -6757,12 +6781,14 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false, ev
|
|
|
6757
6781
|
}
|
|
6758
6782
|
const latencyHist = durationMs !== void 0 ? recordLatency({}, durationMs) : void 0;
|
|
6759
6783
|
const latencyMs = latencyHist ? latencyPercentiles(latencyHist) : void 0;
|
|
6784
|
+
const lastError = isError && errorDetail ? buildEdgeErrorExemplar(errorDetail, ts) : void 0;
|
|
6760
6785
|
const signal = {
|
|
6761
6786
|
spanCount: 1,
|
|
6762
6787
|
errorCount: isError ? 1 : 0,
|
|
6763
6788
|
lastObservedAgeMs: 0,
|
|
6764
6789
|
...latencyHist ? { latencyHist } : {},
|
|
6765
|
-
...latencyMs ? { latencyMs } : {}
|
|
6790
|
+
...latencyMs ? { latencyMs } : {},
|
|
6791
|
+
...lastError ? { lastError } : {}
|
|
6766
6792
|
};
|
|
6767
6793
|
const edge = {
|
|
6768
6794
|
id,
|
|
@@ -7103,6 +7129,7 @@ async function handleSpan(ctx, span) {
|
|
|
7103
7129
|
const sourceId = ensureServiceNode(ctx.graph, span.service, env);
|
|
7104
7130
|
const isError = spanRecordsError(span);
|
|
7105
7131
|
const durationMs = span.durationNanos > 0n && !spanIsStreaming(span) ? Number(span.durationNanos) / 1e6 : void 0;
|
|
7132
|
+
const errorDetail = isError ? edgeErrorDetailFromSpan(span) : void 0;
|
|
7106
7133
|
const sourceServiceNode = ctx.graph.getNodeAttributes(sourceId);
|
|
7107
7134
|
const callSite = callSiteFromSpan(span, sourceServiceNode, ctx.scanPath);
|
|
7108
7135
|
cacheSpanService(span, nowMs, callSite);
|
|
@@ -7142,7 +7169,8 @@ async function handleSpan(ctx, span) {
|
|
|
7142
7169
|
ts,
|
|
7143
7170
|
isError,
|
|
7144
7171
|
callSiteEvidence,
|
|
7145
|
-
durationMs
|
|
7172
|
+
durationMs,
|
|
7173
|
+
errorDetail
|
|
7146
7174
|
);
|
|
7147
7175
|
if (result) affectedNode = targetId;
|
|
7148
7176
|
if (span.dbSystem === "mongodb" && span.dbCollection) {
|
|
@@ -7155,7 +7183,8 @@ async function handleSpan(ctx, span) {
|
|
|
7155
7183
|
ts,
|
|
7156
7184
|
isError,
|
|
7157
7185
|
callSiteEvidence,
|
|
7158
|
-
durationMs
|
|
7186
|
+
durationMs,
|
|
7187
|
+
errorDetail
|
|
7159
7188
|
);
|
|
7160
7189
|
}
|
|
7161
7190
|
if (span.dbTable) {
|
|
@@ -7168,7 +7197,8 @@ async function handleSpan(ctx, span) {
|
|
|
7168
7197
|
ts,
|
|
7169
7198
|
isError,
|
|
7170
7199
|
callSiteEvidence,
|
|
7171
|
-
durationMs
|
|
7200
|
+
durationMs,
|
|
7201
|
+
errorDetail
|
|
7172
7202
|
);
|
|
7173
7203
|
mergeObservedColumns(ctx.graph, tableId, span.dbColumns);
|
|
7174
7204
|
}
|
|
@@ -7188,7 +7218,8 @@ async function handleSpan(ctx, span) {
|
|
|
7188
7218
|
ts,
|
|
7189
7219
|
isError,
|
|
7190
7220
|
callSiteEvidence,
|
|
7191
|
-
durationMs
|
|
7221
|
+
durationMs,
|
|
7222
|
+
errorDetail
|
|
7192
7223
|
);
|
|
7193
7224
|
if (result) affectedNode = targetId;
|
|
7194
7225
|
} else if (span.graphqlOperationName && span.graphqlOperationType && spanServesGraphqlOperation(span.kind)) {
|
|
@@ -7206,7 +7237,8 @@ async function handleSpan(ctx, span) {
|
|
|
7206
7237
|
ts,
|
|
7207
7238
|
isError,
|
|
7208
7239
|
callSiteEvidence,
|
|
7209
|
-
durationMs
|
|
7240
|
+
durationMs,
|
|
7241
|
+
errorDetail
|
|
7210
7242
|
);
|
|
7211
7243
|
if (result) affectedNode = targetId;
|
|
7212
7244
|
} else if (span.rpcSystem === "grpc" && span.rpcService && span.rpcMethod && spanServesGrpcMethod(span.kind)) {
|
|
@@ -7219,7 +7251,8 @@ async function handleSpan(ctx, span) {
|
|
|
7219
7251
|
ts,
|
|
7220
7252
|
isError,
|
|
7221
7253
|
callSiteEvidence,
|
|
7222
|
-
durationMs
|
|
7254
|
+
durationMs,
|
|
7255
|
+
errorDetail
|
|
7223
7256
|
);
|
|
7224
7257
|
if (result) affectedNode = targetId;
|
|
7225
7258
|
} else if (span.websocketChannel && spanServesWebsocketChannel(span.kind)) {
|
|
@@ -7236,7 +7269,8 @@ async function handleSpan(ctx, span) {
|
|
|
7236
7269
|
ts,
|
|
7237
7270
|
isError,
|
|
7238
7271
|
callSiteEvidence,
|
|
7239
|
-
durationMs
|
|
7272
|
+
durationMs,
|
|
7273
|
+
errorDetail
|
|
7240
7274
|
);
|
|
7241
7275
|
if (result) affectedNode = targetId;
|
|
7242
7276
|
} else {
|
|
@@ -7253,7 +7287,8 @@ async function handleSpan(ctx, span) {
|
|
|
7253
7287
|
ts,
|
|
7254
7288
|
isError,
|
|
7255
7289
|
callSiteEvidence,
|
|
7256
|
-
durationMs
|
|
7290
|
+
durationMs,
|
|
7291
|
+
errorDetail
|
|
7257
7292
|
);
|
|
7258
7293
|
affectedNode = targetId;
|
|
7259
7294
|
resolvedViaAddress = true;
|
|
@@ -7267,7 +7302,8 @@ async function handleSpan(ctx, span) {
|
|
|
7267
7302
|
ts,
|
|
7268
7303
|
isError,
|
|
7269
7304
|
callSiteEvidence,
|
|
7270
|
-
durationMs
|
|
7305
|
+
durationMs,
|
|
7306
|
+
errorDetail
|
|
7271
7307
|
);
|
|
7272
7308
|
affectedNode = frontierNodeId;
|
|
7273
7309
|
resolvedViaAddress = true;
|
|
@@ -7294,7 +7330,8 @@ async function handleSpan(ctx, span) {
|
|
|
7294
7330
|
ts,
|
|
7295
7331
|
isError,
|
|
7296
7332
|
fallbackEvidence,
|
|
7297
|
-
durationMs
|
|
7333
|
+
durationMs,
|
|
7334
|
+
errorDetail
|
|
7298
7335
|
);
|
|
7299
7336
|
}
|
|
7300
7337
|
}
|
|
@@ -7309,7 +7346,7 @@ async function handleSpan(ctx, span) {
|
|
|
7309
7346
|
);
|
|
7310
7347
|
if (routeNodeId) {
|
|
7311
7348
|
const routeSvc = ctx.graph.getNodeAttributes(routeNodeId).service;
|
|
7312
|
-
upsertObservedEdge(ctx.graph, import_types9.EdgeType.CONTAINS, (0, import_types9.serviceId)(routeSvc), routeNodeId, ts, isError, void 0, durationMs);
|
|
7349
|
+
upsertObservedEdge(ctx.graph, import_types9.EdgeType.CONTAINS, (0, import_types9.serviceId)(routeSvc), routeNodeId, ts, isError, void 0, durationMs, errorDetail);
|
|
7313
7350
|
}
|
|
7314
7351
|
}
|
|
7315
7352
|
if (span.statusCode === 2) {
|
|
@@ -17226,6 +17263,11 @@ function detectObservedFailingEdge(graph, bucket) {
|
|
|
17226
17263
|
spanCount,
|
|
17227
17264
|
errorCount,
|
|
17228
17265
|
errorRate: clampConfidence(errorRate),
|
|
17266
|
+
// The bounded last-error exemplar rides the finding as evidence (ADR-236):
|
|
17267
|
+
// the error rate says HOW MUCH is failing, the exemplar says WHAT failed —
|
|
17268
|
+
// a timeout vs a deadlock vs a connection-refused, which `errorCount`
|
|
17269
|
+
// alone cannot name. Present only when the observed edge carries one.
|
|
17270
|
+
...signal.lastError ? { lastError: signal.lastError } : {},
|
|
17229
17271
|
confidence: OBSERVED_FAILING_CONFIDENCE,
|
|
17230
17272
|
reason: `Code declares ${bucket.source} \u2192 ${bucket.target} (${bucket.type}) and production observes it, but ${errorCount}/${spanCount} observed calls fail (${pct}% error rate) \u2014 the declared dependency is predominantly failing. The declared intent (EXTRACTED) and the observed behaviour (OBSERVED) diverge.`,
|
|
17231
17273
|
recommendation: "Treat this as a broken declared dependency, not a coverage gap: production runs the call the code declares and most calls error. Check the dependency\u2019s health, recent deploys, and the observed error responses on this edge."
|
|
@@ -17726,11 +17768,20 @@ ${NEAT_OUT_LINE}
|
|
|
17726
17768
|
// src/summary.ts
|
|
17727
17769
|
init_cjs_shims();
|
|
17728
17770
|
var import_types62 = require("@neat.is/types");
|
|
17771
|
+
|
|
17772
|
+
// src/otlp-headers.ts
|
|
17773
|
+
init_cjs_shims();
|
|
17774
|
+
var OTLP_BEARER_PREFIX = "Authorization=Bearer%20";
|
|
17775
|
+
function otlpBearerHeader(token) {
|
|
17776
|
+
return OTLP_BEARER_PREFIX + encodeURIComponent(token);
|
|
17777
|
+
}
|
|
17778
|
+
|
|
17779
|
+
// src/summary.ts
|
|
17729
17780
|
function renderOtelEnvBlock() {
|
|
17730
17781
|
return [
|
|
17731
17782
|
"for prod OTel routing, set these in your deploy platform's env:",
|
|
17732
17783
|
" OTEL_EXPORTER_OTLP_ENDPOINT=https://<your-neat-host>:4318",
|
|
17733
|
-
|
|
17784
|
+
` OTEL_EXPORTER_OTLP_HEADERS=${OTLP_BEARER_PREFIX}<NEAT_AUTH_TOKEN>`
|
|
17734
17785
|
].join("\n");
|
|
17735
17786
|
}
|
|
17736
17787
|
function findIncompatServices(nodes) {
|
|
@@ -18314,10 +18365,11 @@ function locusOf(graph, ev) {
|
|
|
18314
18365
|
const rawLine = ev.attributes?.[CODE_LINENO_ATTR2];
|
|
18315
18366
|
const line = typeof rawLine === "number" ? rawLine : Number(rawLine);
|
|
18316
18367
|
const node = graph.hasNode(ev.affectedNode) ? graph.getNodeAttributes(ev.affectedNode) : void 0;
|
|
18368
|
+
const symbol = node?.type === "SymbolNode" ? node.qualname : void 0;
|
|
18317
18369
|
return {
|
|
18318
18370
|
file,
|
|
18319
18371
|
...Number.isFinite(line) ? { lineStart: line, lineEnd: line } : {},
|
|
18320
|
-
...
|
|
18372
|
+
...symbol ? { symbol } : {},
|
|
18321
18373
|
service: node?.service ?? ev.service,
|
|
18322
18374
|
provenance: import_types63.Provenance.OBSERVED
|
|
18323
18375
|
};
|
|
@@ -18413,7 +18465,8 @@ function buildIncidentCard(graph, errorEvent, incidents, policies) {
|
|
|
18413
18465
|
const policyCards = applicable.map((p) => ({
|
|
18414
18466
|
policyName: p.policyName,
|
|
18415
18467
|
severity: p.severity,
|
|
18416
|
-
message: p.reason
|
|
18468
|
+
message: p.reason,
|
|
18469
|
+
onViolation: p.onViolation
|
|
18417
18470
|
}));
|
|
18418
18471
|
const divergences = inGraph ? computeDivergences(graph, { node: affected, incidents }).divergences.map((d) => ({
|
|
18419
18472
|
type: d.type,
|
|
@@ -18440,8 +18493,111 @@ function buildIncidentCard(graph, errorEvent, incidents, policies) {
|
|
|
18440
18493
|
...divergences.length > 0 ? { divergence: divergences } : {},
|
|
18441
18494
|
headline: renderHeadline(graph, errorEvent, locus, rootCause?.node ?? null)
|
|
18442
18495
|
};
|
|
18496
|
+
card.grade = gradeIncidentCard(card);
|
|
18443
18497
|
return import_types63.IncidentCardSchema.parse(card);
|
|
18444
18498
|
}
|
|
18499
|
+
var GRADE_PRIORS_VERSION = "sniper-architecture-priors-2026-10-08";
|
|
18500
|
+
var GRADE_WEIGHTS = {
|
|
18501
|
+
evidence: 3,
|
|
18502
|
+
locus: 2,
|
|
18503
|
+
tests: 2,
|
|
18504
|
+
reach: 1.5,
|
|
18505
|
+
kind: 1.5,
|
|
18506
|
+
chain: 1,
|
|
18507
|
+
recur: 1,
|
|
18508
|
+
div: 1
|
|
18509
|
+
};
|
|
18510
|
+
var PROVENANCE_WEIGHT = {
|
|
18511
|
+
OBSERVED: 1,
|
|
18512
|
+
EXTRACTED: 0.8,
|
|
18513
|
+
INFERRED: 0.4,
|
|
18514
|
+
FRONTIER: 0.3,
|
|
18515
|
+
STALE: 0.2
|
|
18516
|
+
};
|
|
18517
|
+
var REACH_NODES_MAX = 50;
|
|
18518
|
+
var TIMEOUT = /timeout|timed out|deadline/i;
|
|
18519
|
+
var round2 = (n) => Math.round(n * 1e4) / 1e4;
|
|
18520
|
+
function factor(name, value, evidence, reason) {
|
|
18521
|
+
return {
|
|
18522
|
+
value: value === null ? null : round2(value),
|
|
18523
|
+
weight: GRADE_WEIGHTS[name],
|
|
18524
|
+
evidence,
|
|
18525
|
+
...reason ? { reason } : {}
|
|
18526
|
+
};
|
|
18527
|
+
}
|
|
18528
|
+
function gradeIncidentCard(card) {
|
|
18529
|
+
const rc = card.rootCause;
|
|
18530
|
+
const chain = rc?.chain ?? [];
|
|
18531
|
+
const gates = {
|
|
18532
|
+
policyNotBlock: {
|
|
18533
|
+
passed: !(card.policies ?? []).some((p) => p.onViolation === "block"),
|
|
18534
|
+
evidence: ["policies[].onViolation"]
|
|
18535
|
+
},
|
|
18536
|
+
notSymptomOnly: {
|
|
18537
|
+
passed: rc?.classification !== "symptom-only",
|
|
18538
|
+
evidence: ["rootCause.classification"]
|
|
18539
|
+
},
|
|
18540
|
+
locusResolves: { passed: card.locus !== null, evidence: ["locus"] }
|
|
18541
|
+
};
|
|
18542
|
+
const gamma = Object.values(gates).every((g) => g.passed) ? 1 : 0;
|
|
18543
|
+
const carrying = chain.some((h) => h.carriesSignal === true) ? chain.filter((h) => h.carriesSignal === true) : chain;
|
|
18544
|
+
const hopWeights = carrying.map((h) => PROVENANCE_WEIGHT[h.provenance]);
|
|
18545
|
+
const evidence = hopWeights.length === 0 ? factor("evidence", null, ["rootCause.chain[].provenance"], "no root-cause chain on the card") : hopWeights.some((w) => w === void 0) ? factor("evidence", null, ["rootCause.chain[].provenance"], "a chain hop has a provenance with no weight") : factor("evidence", Math.min(...hopWeights), ["rootCause.chain[].provenance"]);
|
|
18546
|
+
const locus = card.locus ? factor("locus", card.locus.symbol ? 1 : 0.6, ["locus.symbol", "locus.file"]) : factor("locus", null, ["locus"], "no locus on the card (the locus gate fails)");
|
|
18547
|
+
const tests = factor(
|
|
18548
|
+
"tests",
|
|
18549
|
+
null,
|
|
18550
|
+
[],
|
|
18551
|
+
"the card carries no test-file classification or test-to-symbol edges"
|
|
18552
|
+
);
|
|
18553
|
+
const reach = card.blastRadius ? factor(
|
|
18554
|
+
"reach",
|
|
18555
|
+
Math.max(0, 1 - card.blastRadius.totalAffected / REACH_NODES_MAX),
|
|
18556
|
+
["blastRadius.totalAffected"],
|
|
18557
|
+
`nodes in the blast radius against a prior bound of ${REACH_NODES_MAX}`
|
|
18558
|
+
) : factor("reach", null, ["blastRadius"], "no blast radius on the card");
|
|
18559
|
+
const timeout = TIMEOUT.test(`${card.exceptionType ?? ""} ${card.message}`);
|
|
18560
|
+
const kindValue = timeout ? 0.3 : card.incidentKind === "exception" ? 1 : card.incidentKind === "5xx" || card.incidentKind === "status-error" ? 0.7 : card.incidentKind === "connector" ? 0.6 : 0.4;
|
|
18561
|
+
const kind = factor("kind", kindValue, ["incidentKind", "exceptionType", "message"]);
|
|
18562
|
+
const chainFactor = rc ? factor(
|
|
18563
|
+
"chain",
|
|
18564
|
+
1 / (1 + 0.25 * (Math.max(1, chain.length - 1) - 1)),
|
|
18565
|
+
["rootCause.chain"]
|
|
18566
|
+
) : factor("chain", null, ["rootCause"], "no root cause on the card");
|
|
18567
|
+
const occurrences = card.count ?? 1;
|
|
18568
|
+
const recurValue = Math.min(1, Math.log(1 + occurrences) / Math.log(11));
|
|
18569
|
+
const recur = factor(
|
|
18570
|
+
"recur",
|
|
18571
|
+
recurValue,
|
|
18572
|
+
["count"],
|
|
18573
|
+
card.count === void 0 ? "no coalesced count; one recorded occurrence" : void 0
|
|
18574
|
+
);
|
|
18575
|
+
const div = factor("div", (card.divergence ?? []).length > 0 ? 1 : 0.7, ["divergence"]);
|
|
18576
|
+
const factors = { evidence, locus, tests, reach, kind, chain: chainFactor, recur, div };
|
|
18577
|
+
let weighted = 0;
|
|
18578
|
+
let total = 0;
|
|
18579
|
+
for (const f of Object.values(factors)) {
|
|
18580
|
+
if (f.value === null) continue;
|
|
18581
|
+
weighted += f.weight * f.value;
|
|
18582
|
+
total += f.weight;
|
|
18583
|
+
}
|
|
18584
|
+
const C = round2(weighted / total);
|
|
18585
|
+
const G = round2(gamma * C);
|
|
18586
|
+
const band = gamma === 0 || G < 0.5 ? "out" : G >= 0.75 ? "full" : "diagnose-only";
|
|
18587
|
+
return {
|
|
18588
|
+
G,
|
|
18589
|
+
gamma,
|
|
18590
|
+
gates,
|
|
18591
|
+
C,
|
|
18592
|
+
factors,
|
|
18593
|
+
band,
|
|
18594
|
+
urgency: {
|
|
18595
|
+
value: null,
|
|
18596
|
+
reason: "f_fresh needs the time of the last OBSERVED signal at the node, which the card does not carry"
|
|
18597
|
+
},
|
|
18598
|
+
priorsVersion: GRADE_PRIORS_VERSION
|
|
18599
|
+
};
|
|
18600
|
+
}
|
|
18445
18601
|
|
|
18446
18602
|
// src/ask.ts
|
|
18447
18603
|
init_cjs_shims();
|
|
@@ -20327,7 +20483,7 @@ async function computePrVerdict(req, ctx, deps, deadline) {
|
|
|
20327
20483
|
const found = observedBreakFrom(node, getObservedDependencies(ctx.liveGraph, node.id));
|
|
20328
20484
|
if (found) observedBreaks.push(found);
|
|
20329
20485
|
}
|
|
20330
|
-
const { marker, body } = renderVerdict({
|
|
20486
|
+
const { marker, body: rendered } = renderVerdict({
|
|
20331
20487
|
graph: head,
|
|
20332
20488
|
delta,
|
|
20333
20489
|
changedFiles,
|
|
@@ -20335,6 +20491,7 @@ async function computePrVerdict(req, ctx, deps, deadline) {
|
|
|
20335
20491
|
observedBreaks,
|
|
20336
20492
|
tone: req.tone ?? "loud"
|
|
20337
20493
|
});
|
|
20494
|
+
const body = rendered.startsWith(marker) ? rendered.slice(marker.length).replace(/^(?:\r?\n)+/, "") : rendered;
|
|
20338
20495
|
return {
|
|
20339
20496
|
marker,
|
|
20340
20497
|
body,
|
|
@@ -26147,13 +26304,25 @@ async function makeTransformersEmbedder() {
|
|
|
26147
26304
|
}
|
|
26148
26305
|
};
|
|
26149
26306
|
}
|
|
26150
|
-
async function pickEmbedder() {
|
|
26151
|
-
|
|
26152
|
-
|
|
26153
|
-
|
|
26307
|
+
async function pickEmbedder(provider) {
|
|
26308
|
+
if (provider !== "transformers") {
|
|
26309
|
+
const host = ollamaHost();
|
|
26310
|
+
if (host && await ollamaReachable(host)) {
|
|
26311
|
+
return makeOllamaEmbedder(host);
|
|
26312
|
+
}
|
|
26154
26313
|
}
|
|
26314
|
+
if (provider === "ollama") return null;
|
|
26155
26315
|
return makeTransformersEmbedder();
|
|
26156
26316
|
}
|
|
26317
|
+
function searchProviderFromEnv(env = process.env, warn2 = console.warn) {
|
|
26318
|
+
const value = env.NEAT_SEARCH_PROVIDER;
|
|
26319
|
+
if (!value) return void 0;
|
|
26320
|
+
if (value === "substring" || value === "ollama" || value === "transformers") return value;
|
|
26321
|
+
warn2(
|
|
26322
|
+
`semantic_search: NEAT_SEARCH_PROVIDER="${value}" is not substring, ollama, or transformers; using automatic selection`
|
|
26323
|
+
);
|
|
26324
|
+
return void 0;
|
|
26325
|
+
}
|
|
26157
26326
|
async function readCache(cachePath) {
|
|
26158
26327
|
try {
|
|
26159
26328
|
const raw = await import_node_fs49.promises.readFile(cachePath, "utf8");
|
|
@@ -26317,7 +26486,8 @@ async function buildSearchIndex(graph, options = {}) {
|
|
|
26317
26486
|
if (options.embedder) {
|
|
26318
26487
|
embedder = options.embedder;
|
|
26319
26488
|
} else if (options.forceProvider !== "substring") {
|
|
26320
|
-
const
|
|
26489
|
+
const forcedProvider = options.forceProvider;
|
|
26490
|
+
const factory = options.embedderFactory ?? (() => pickEmbedder(forcedProvider));
|
|
26321
26491
|
embedder = await resolveEmbedderBounded(
|
|
26322
26492
|
factory,
|
|
26323
26493
|
options.initTimeoutMs ?? searchInitTimeoutMs()
|
|
@@ -26596,9 +26766,10 @@ async function startWatch(graph, opts) {
|
|
|
26596
26766
|
const port = opts.port ?? 8080;
|
|
26597
26767
|
const otelPort = opts.otelPort ?? 4318;
|
|
26598
26768
|
const cachePath = opts.embeddingsCachePath ?? import_node_path86.default.join(import_node_path86.default.dirname(opts.outPath), "embeddings.json");
|
|
26769
|
+
const forceProvider = searchProviderFromEnv();
|
|
26599
26770
|
let searchIndex;
|
|
26600
26771
|
try {
|
|
26601
|
-
searchIndex = await buildSearchIndex(graph, { cachePath });
|
|
26772
|
+
searchIndex = await buildSearchIndex(graph, { cachePath, forceProvider });
|
|
26602
26773
|
console.log(`semantic_search: ${searchIndex.provider} provider`);
|
|
26603
26774
|
} catch (err) {
|
|
26604
26775
|
console.warn(
|
|
@@ -26882,7 +27053,7 @@ function emitDockerRunSnippet() {
|
|
|
26882
27053
|
function renderOtelEnvBlock2(token, host = "<host>") {
|
|
26883
27054
|
return [
|
|
26884
27055
|
`OTEL_EXPORTER_OTLP_ENDPOINT=https://${host}:4318`,
|
|
26885
|
-
`OTEL_EXPORTER_OTLP_HEADERS
|
|
27056
|
+
`OTEL_EXPORTER_OTLP_HEADERS=${otlpBearerHeader(token)}`,
|
|
26886
27057
|
"OTEL_SERVICE_NAME=<service>"
|
|
26887
27058
|
].join("\n");
|
|
26888
27059
|
}
|
|
@@ -26947,7 +27118,7 @@ var import_semver2 = __toESM(require("semver"), 1);
|
|
|
26947
27118
|
init_cjs_shims();
|
|
26948
27119
|
var OTEL_INIT_HEADER = "// Generated by `neat init --apply` (ADR-069). OpenTelemetry auto-instrumentation hook.";
|
|
26949
27120
|
var OTEL_INIT_STAMP = "// neat-template-version: 8 \u2014 CommonJS TypeScript uses a synchronous bootstrap (#910); OTel init degrades to no-OBSERVED when dependencies are absent (#820); daemon.json endpoint resolution (ADR-096) + layered file-first capture (ADR-090).";
|
|
26950
|
-
var OTEL_OTLP_HEADERS_JS = "if (process.env.NEAT_OTEL_TOKEN) process.env.OTEL_EXPORTER_OTLP_HEADERS ||= 'Authorization=Bearer
|
|
27121
|
+
var OTEL_OTLP_HEADERS_JS = "if (process.env.NEAT_OTEL_TOKEN) process.env.OTEL_EXPORTER_OTLP_HEADERS ||= 'Authorization=Bearer%20' + encodeURIComponent(process.env.NEAT_OTEL_TOKEN)";
|
|
26951
27122
|
var OTEL_OTLP_PROTOCOL_JS = "process.env.OTEL_EXPORTER_OTLP_PROTOCOL ||= 'http/json'";
|
|
26952
27123
|
var OTEL_ENDPOINT_RESOLVER_CJS = `;(function () {
|
|
26953
27124
|
try {
|
|
@@ -27392,7 +27563,31 @@ ${registrations.join("\n")}
|
|
|
27392
27563
|
`;
|
|
27393
27564
|
return template.replace(/__SERVICE_NAME__/g, serviceName).replace(/__PROJECT__/g, projectName).replace(/__INSTRUMENTATION_BLOCK__\n?/g, block);
|
|
27394
27565
|
}
|
|
27395
|
-
function renderEnvNeat(serviceName, projectName) {
|
|
27566
|
+
function renderEnvNeat(serviceName, projectName, nodeOptions, extraInstrumentations = []) {
|
|
27567
|
+
if (nodeOptions) {
|
|
27568
|
+
return [
|
|
27569
|
+
"# Generated by `neat init --apply` / `neat <path>` (ADR-069, ADR-232).",
|
|
27570
|
+
"# NEAT attaches its OpenTelemetry bootstrap at process start; no source is edited.",
|
|
27571
|
+
"# NODE_OPTIONS is read once when the process starts, so load this file before",
|
|
27572
|
+
"# your start command:",
|
|
27573
|
+
"# set -a; . ./.env.neat; set +a; <your start command>",
|
|
27574
|
+
"# or copy these settings into your process manager / platform environment.",
|
|
27575
|
+
`OTEL_SERVICE_NAME=${serviceName}`,
|
|
27576
|
+
"# The preload sends spans to this project's daemon, read from",
|
|
27577
|
+
"# <project>/neat-out/daemon.json; NEAT_PROJECT names the route when no record",
|
|
27578
|
+
"# is found. Set OTEL_EXPORTER_OTLP_ENDPOINT only to send spans elsewhere.",
|
|
27579
|
+
`NEAT_PROJECT=${projectName}`,
|
|
27580
|
+
"OTEL_EXPORTER_OTLP_PROTOCOL=http/json",
|
|
27581
|
+
"# Set NEAT_OTEL_TOKEN to the daemon's OTLP secret to authenticate exported spans (#410).",
|
|
27582
|
+
"# NEAT_OTEL_TOKEN=",
|
|
27583
|
+
...extraInstrumentations.length > 0 ? [
|
|
27584
|
+
"# Instrumentations outside the auto bundle, loaded by the preload (<package>#<export>).",
|
|
27585
|
+
`NEAT_OTEL_INSTRUMENTATIONS=${extraInstrumentations.join(",")}`
|
|
27586
|
+
] : [],
|
|
27587
|
+
`NODE_OPTIONS="${nodeOptions}"`,
|
|
27588
|
+
""
|
|
27589
|
+
].join("\n");
|
|
27590
|
+
}
|
|
27396
27591
|
return [
|
|
27397
27592
|
"# Generated by `neat init --apply` (ADR-069).",
|
|
27398
27593
|
`OTEL_SERVICE_NAME=${serviceName}`,
|
|
@@ -27588,14 +27783,16 @@ function detectNonBundledInstrumentations(pkg) {
|
|
|
27588
27783
|
out.push({
|
|
27589
27784
|
pkg: "@prisma/instrumentation",
|
|
27590
27785
|
version: prismaInstrVersion,
|
|
27591
|
-
registration: "instrumentations.push(new (require('@prisma/instrumentation').PrismaInstrumentation)())"
|
|
27786
|
+
registration: "instrumentations.push(new (require('@prisma/instrumentation').PrismaInstrumentation)())",
|
|
27787
|
+
exportName: "PrismaInstrumentation"
|
|
27592
27788
|
});
|
|
27593
27789
|
}
|
|
27594
27790
|
if (getMajor(deps["@nestjs/core"]) >= 11) {
|
|
27595
27791
|
out.push({
|
|
27596
27792
|
pkg: "@opentelemetry/instrumentation-nestjs-core",
|
|
27597
27793
|
version: "^0.67.0",
|
|
27598
|
-
registration: "instrumentations.push(new (require('@opentelemetry/instrumentation-nestjs-core').NestInstrumentation)())"
|
|
27794
|
+
registration: "instrumentations.push(new (require('@opentelemetry/instrumentation-nestjs-core').NestInstrumentation)())",
|
|
27795
|
+
exportName: "NestInstrumentation"
|
|
27599
27796
|
});
|
|
27600
27797
|
}
|
|
27601
27798
|
return out;
|
|
@@ -28406,6 +28603,49 @@ async function findFrameworkDispatch(serviceDir, pkg, manifestPath, project) {
|
|
|
28406
28603
|
}
|
|
28407
28604
|
return null;
|
|
28408
28605
|
}
|
|
28606
|
+
var ATTACH_PACKAGE_NAME = "@neat.is/otel-node";
|
|
28607
|
+
function attachPackageRange(coreVersion = readPackageVersion()) {
|
|
28608
|
+
return import_semver2.default.valid(coreVersion) ? `^${coreVersion}` : "latest";
|
|
28609
|
+
}
|
|
28610
|
+
async function planAttachment(serviceDir, pkg, manifestPath, project) {
|
|
28611
|
+
const svcName = serviceNodeName2(pkg, serviceDir);
|
|
28612
|
+
const projectName = projectToken(pkg, serviceDir, project);
|
|
28613
|
+
const envNeatFile = import_node_path88.default.join(serviceDir, ".env.neat");
|
|
28614
|
+
const flag = pkg.type === "module" ? "--import" : "--require";
|
|
28615
|
+
const nodeOptions = `${flag} @neat.is/otel-node/register`;
|
|
28616
|
+
const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
28617
|
+
const dependencyEdits = [];
|
|
28618
|
+
if (!(ATTACH_PACKAGE_NAME in existingDeps)) {
|
|
28619
|
+
dependencyEdits.push({ file: manifestPath, kind: "add", name: ATTACH_PACKAGE_NAME, version: attachPackageRange() });
|
|
28620
|
+
}
|
|
28621
|
+
const nonBundled = detectNonBundledInstrumentations(pkg);
|
|
28622
|
+
for (const inst of nonBundled) {
|
|
28623
|
+
if (inst.pkg in existingDeps) {
|
|
28624
|
+
if (needsVersionUpgrade(existingDeps[inst.pkg], inst.version)) {
|
|
28625
|
+
dependencyEdits.push({ file: manifestPath, kind: "upgrade", name: inst.pkg, version: inst.version, fromVersion: existingDeps[inst.pkg] });
|
|
28626
|
+
}
|
|
28627
|
+
continue;
|
|
28628
|
+
}
|
|
28629
|
+
dependencyEdits.push({ file: manifestPath, kind: "add", name: inst.pkg, version: inst.version });
|
|
28630
|
+
}
|
|
28631
|
+
const extraInstrumentations = nonBundled.map((i) => `${i.pkg}#${i.exportName}`);
|
|
28632
|
+
const generatedFiles = [];
|
|
28633
|
+
if (!await exists3(envNeatFile)) {
|
|
28634
|
+
generatedFiles.push({
|
|
28635
|
+
file: envNeatFile,
|
|
28636
|
+
contents: renderEnvNeat(svcName, projectName, nodeOptions, extraInstrumentations),
|
|
28637
|
+
skipIfExists: true
|
|
28638
|
+
});
|
|
28639
|
+
}
|
|
28640
|
+
return {
|
|
28641
|
+
language: "javascript",
|
|
28642
|
+
serviceDir,
|
|
28643
|
+
dependencyEdits,
|
|
28644
|
+
entrypointEdits: [],
|
|
28645
|
+
envEdits: [OTEL_ENV],
|
|
28646
|
+
generatedFiles
|
|
28647
|
+
};
|
|
28648
|
+
}
|
|
28409
28649
|
async function plan(serviceDir, opts) {
|
|
28410
28650
|
const pkg = await readPackageJson2(serviceDir);
|
|
28411
28651
|
const manifestPath = import_node_path88.default.join(serviceDir, "package.json");
|
|
@@ -28431,14 +28671,17 @@ async function plan(serviceDir, opts) {
|
|
|
28431
28671
|
if (!entryFile) {
|
|
28432
28672
|
return { ...empty, libOnly: true };
|
|
28433
28673
|
}
|
|
28674
|
+
const runtimeKind = await detectRuntimeKind(serviceDir, pkg);
|
|
28675
|
+
if (runtimeKind !== "node") {
|
|
28676
|
+
return { ...empty, runtimeKind };
|
|
28677
|
+
}
|
|
28678
|
+
}
|
|
28679
|
+
if (!opts?.sourceEdit) {
|
|
28680
|
+
return planAttachment(serviceDir, pkg, manifestPath, project);
|
|
28434
28681
|
}
|
|
28435
28682
|
if (frameworkDispatch) {
|
|
28436
28683
|
return frameworkDispatch();
|
|
28437
28684
|
}
|
|
28438
|
-
const runtimeKind = await detectRuntimeKind(serviceDir, pkg);
|
|
28439
|
-
if (runtimeKind !== "node") {
|
|
28440
|
-
return { ...empty, runtimeKind };
|
|
28441
|
-
}
|
|
28442
28685
|
if (!entryFile) {
|
|
28443
28686
|
return { ...empty, libOnly: true };
|
|
28444
28687
|
}
|
|
@@ -28756,6 +28999,7 @@ var SDK_PACKAGES2 = [
|
|
|
28756
28999
|
{ name: "opentelemetry-distro", version: ">=0.49b0" },
|
|
28757
29000
|
{ name: "opentelemetry-exporter-otlp", version: ">=1.28.0" }
|
|
28758
29001
|
];
|
|
29002
|
+
var NEAT_OTEL_PACKAGE = { name: "neat-otel", version: ">=0.1.0" };
|
|
28759
29003
|
var OTEL_ENV2 = {
|
|
28760
29004
|
file: null,
|
|
28761
29005
|
key: "OTEL_EXPORTER_OTLP_ENDPOINT",
|
|
@@ -28907,34 +29151,37 @@ function reqPackageName(line) {
|
|
|
28907
29151
|
const head = stripped.split(/[\s;]/)[0] ?? "";
|
|
28908
29152
|
return head.replace(/[<>=!~].*$/, "").toLowerCase();
|
|
28909
29153
|
}
|
|
28910
|
-
async function planRequirementsTxtEdits(serviceDir) {
|
|
29154
|
+
async function planRequirementsTxtEdits(serviceDir, packages = SDK_PACKAGES2) {
|
|
28911
29155
|
const file = import_node_path89.default.join(serviceDir, "requirements.txt");
|
|
28912
29156
|
if (!await exists4(file)) return null;
|
|
28913
29157
|
const raw = await import_node_fs53.promises.readFile(file, "utf8");
|
|
28914
29158
|
const presentNames = new Set(
|
|
28915
29159
|
raw.split(/\r?\n/).map(reqPackageName).filter((n) => n.length > 0)
|
|
28916
29160
|
);
|
|
28917
|
-
const missing =
|
|
29161
|
+
const missing = packages.filter((p) => !presentNames.has(p.name.toLowerCase()));
|
|
28918
29162
|
return { manifest: file, missing: [...missing] };
|
|
28919
29163
|
}
|
|
28920
|
-
|
|
29164
|
+
var PY_LAUNCHER_PYTHON = /^python3?\b/;
|
|
29165
|
+
var PY_LAUNCHERS_BROAD = /^(python3?|uvicorn|gunicorn|hypercorn|daphne|granian|celery|flask|fastapi)\b/;
|
|
29166
|
+
async function planProcfileEdits(serviceDir, broad = false) {
|
|
28921
29167
|
const procfile = import_node_path89.default.join(serviceDir, "Procfile");
|
|
28922
29168
|
if (!await exists4(procfile)) return [];
|
|
28923
29169
|
const raw = await import_node_fs53.promises.readFile(procfile, "utf8");
|
|
29170
|
+
const launcher = broad ? PY_LAUNCHERS_BROAD : PY_LAUNCHER_PYTHON;
|
|
28924
29171
|
const edits = [];
|
|
28925
29172
|
for (const line of raw.split(/\r?\n/)) {
|
|
28926
29173
|
if (line.length === 0) continue;
|
|
28927
29174
|
const m = line.match(/^([a-zA-Z0-9_-]+):\s*(.+)$/);
|
|
28928
29175
|
if (!m) continue;
|
|
28929
29176
|
const cmd = m[2];
|
|
28930
|
-
if (
|
|
29177
|
+
if (!launcher.test(cmd)) continue;
|
|
28931
29178
|
if (cmd.startsWith("opentelemetry-instrument ")) continue;
|
|
28932
29179
|
const after = `${m[1]}: opentelemetry-instrument ${cmd}`;
|
|
28933
29180
|
edits.push({ file: procfile, before: line, after });
|
|
28934
29181
|
}
|
|
28935
29182
|
return edits;
|
|
28936
29183
|
}
|
|
28937
|
-
async function plan2(serviceDir) {
|
|
29184
|
+
async function plan2(serviceDir, opts) {
|
|
28938
29185
|
const empty = {
|
|
28939
29186
|
language: "python",
|
|
28940
29187
|
serviceDir,
|
|
@@ -28942,6 +29189,25 @@ async function plan2(serviceDir) {
|
|
|
28942
29189
|
entrypointEdits: [],
|
|
28943
29190
|
envEdits: []
|
|
28944
29191
|
};
|
|
29192
|
+
if (!opts?.sourceEdit) {
|
|
29193
|
+
const entrypointEdits2 = await planProcfileEdits(serviceDir, true);
|
|
29194
|
+
const reqs2 = await planRequirementsTxtEdits(serviceDir, [NEAT_OTEL_PACKAGE, ...SDK_PACKAGES2]);
|
|
29195
|
+
const dependencyEdits2 = reqs2 ? reqs2.missing.map((sdk) => ({
|
|
29196
|
+
file: reqs2.manifest,
|
|
29197
|
+
kind: "add",
|
|
29198
|
+
name: sdk.name,
|
|
29199
|
+
version: sdk.version
|
|
29200
|
+
})) : [];
|
|
29201
|
+
if (dependencyEdits2.length === 0 && entrypointEdits2.length === 0) return empty;
|
|
29202
|
+
return {
|
|
29203
|
+
language: "python",
|
|
29204
|
+
serviceDir,
|
|
29205
|
+
dependencyEdits: dependencyEdits2,
|
|
29206
|
+
entrypointEdits: entrypointEdits2,
|
|
29207
|
+
envEdits: [OTEL_ENV2]
|
|
29208
|
+
};
|
|
29209
|
+
}
|
|
29210
|
+
const entrypointEdits = await planProcfileEdits(serviceDir);
|
|
28945
29211
|
const dependencyEdits = [];
|
|
28946
29212
|
const reqs = await planRequirementsTxtEdits(serviceDir);
|
|
28947
29213
|
if (reqs) {
|
|
@@ -28954,7 +29220,6 @@ async function plan2(serviceDir) {
|
|
|
28954
29220
|
});
|
|
28955
29221
|
}
|
|
28956
29222
|
}
|
|
28957
|
-
const entrypointEdits = await planProcfileEdits(serviceDir);
|
|
28958
29223
|
const entryFile = await resolvePyEntrypoint(serviceDir);
|
|
28959
29224
|
const generatedFiles = entryFile ? [{ file: import_node_path89.default.join(serviceDir, NEAT_OTEL_FILENAME), contents: neatOtelPy() }] : [];
|
|
28960
29225
|
if (dependencyEdits.length === 0 && entrypointEdits.length === 0 && !entryFile) {
|
|
@@ -29910,7 +30175,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
29910
30175
|
for (const svc of services) {
|
|
29911
30176
|
const installer = await pickInstaller(svc.dir);
|
|
29912
30177
|
if (!installer) continue;
|
|
29913
|
-
const plan6 = await installer.plan(svc.dir, { project });
|
|
30178
|
+
const plan6 = await installer.plan(svc.dir, { project, sourceEdit: options.sourceEdit });
|
|
29914
30179
|
if (isEmptyPlan(plan6) && !plan6.libOnly && plan6.runtimeKind === void 0) {
|
|
29915
30180
|
already++;
|
|
29916
30181
|
continue;
|
|
@@ -30484,8 +30749,12 @@ async function runOrchestrator(opts) {
|
|
|
30484
30749
|
result.steps.apply.skipped = true;
|
|
30485
30750
|
console.log("skipped instrumentation (--no-instrument)");
|
|
30486
30751
|
} else {
|
|
30487
|
-
const tally = await applyInstallersOver(services, opts.project);
|
|
30488
|
-
result.steps.apply = {
|
|
30752
|
+
const tally = await applyInstallersOver(services, opts.project, { sourceEdit: opts.sourceEdit });
|
|
30753
|
+
result.steps.apply = {
|
|
30754
|
+
...tally,
|
|
30755
|
+
skipped: false,
|
|
30756
|
+
...!opts.sourceEdit && tally.instrumented > 0 ? { attached: true } : {}
|
|
30757
|
+
};
|
|
30489
30758
|
console.log(
|
|
30490
30759
|
`instrumented ${tally.instrumented}, already ${tally.alreadyInstrumented}, lib-only ${tally.libOnly}`
|
|
30491
30760
|
);
|
|
@@ -30598,6 +30867,12 @@ function printSummary(result, graph, daemonLog) {
|
|
|
30598
30867
|
for (const i of failedInstalls) {
|
|
30599
30868
|
console.log(` run \`${i.pm} install\` in ${i.cwd}`);
|
|
30600
30869
|
}
|
|
30870
|
+
} else if (daemonLog !== null && result.steps.apply.attached === true) {
|
|
30871
|
+
console.log("next: start your app with NEAT attached \u2014 OBSERVED edges fill in as it runs,");
|
|
30872
|
+
console.log(" and divergences surface where code and runtime disagree.");
|
|
30873
|
+
console.log(" Node: in the service directory, `set -a; . ./.env.neat; set +a`, then your");
|
|
30874
|
+
console.log(" usual start command (or put its NODE_OPTIONS in your process manager).");
|
|
30875
|
+
console.log(" Python: run your start command under `opentelemetry-instrument`.");
|
|
30601
30876
|
} else if (daemonLog !== null) {
|
|
30602
30877
|
console.log(
|
|
30603
30878
|
"next: run your app or your test suite \u2014 OBSERVED edges fill in as it executes,"
|
|
@@ -31772,7 +32047,7 @@ async function runSsoLogin(opts, deps) {
|
|
|
31772
32047
|
out("");
|
|
31773
32048
|
out("To fill the OBSERVED layer, instrument your app to send OpenTelemetry to the hosted daemon:");
|
|
31774
32049
|
out(` OTEL_EXPORTER_OTLP_ENDPOINT=${cred.ingestEndpoint}`);
|
|
31775
|
-
out(` OTEL_EXPORTER_OTLP_HEADERS
|
|
32050
|
+
out(` OTEL_EXPORTER_OTLP_HEADERS=${otlpBearerHeader(cred.otelToken)}`);
|
|
31776
32051
|
}
|
|
31777
32052
|
await runPostLoginConnect({
|
|
31778
32053
|
cpUrl: opts.cpUrl,
|
|
@@ -33013,6 +33288,12 @@ init_cjs_shims();
|
|
|
33013
33288
|
var import_node_fs61 = require("fs");
|
|
33014
33289
|
var import_node_path98 = __toESM(require("path"), 1);
|
|
33015
33290
|
var import_promises5 = __toESM(require("readline/promises"), 1);
|
|
33291
|
+
|
|
33292
|
+
// src/agent-directive.generated.ts
|
|
33293
|
+
init_cjs_shims();
|
|
33294
|
+
var AGENT_DIRECTIVE = '## Query NEAT before searching files\n\nThis project has a live NEAT graph: code, data and infrastructure declarations\nfused with runtime traffic, incidents and supported provider telemetry. It can\nshow what exists, what actually ran, where a failure began, and what may break\nwhen a node changes. The graph is deterministic: the agent supplies the model;\nNEAT resolves named nodes and traverses recorded evidence rather than asking an\nLLM to infer architecture from file names.\n\nEvery claim carries provenance and confidence. `EXTRACTED` comes from recognized\nsource or configuration, `OBSERVED` from spans or supported provider signals,\n`INFERRED` from a bridged relationship, and `STALE` marks an observed edge that\nwent quiet. Missing runtime evidence does not prove a path never runs.\n\n**For questions about this system\'s behavior, structure, data dependencies,\nfailures, or change impact, call the MCP `ask` tool (`mcp__neat__ask`) before\nRead/Grep/Glob/Bash.** You do not need an exact node id. `ask` resolves names\nand routes to the relevant graph traversal, returning provenance-tagged facts.\nWhen MCP is unavailable, use the same door through the CLI:\n\n```\nnpx neat.is ask "why is checkout failing?"\nnpx neat.is ask "what breaks if I change the orders table?"\nnpx neat.is ask "what does the payments service depend on at runtime?"\n```\n\nFor a failure, ask first, then call `get_incident_card` on the named service,\nfile or symbol. The card combines the incident, likely cause, blast radius,\npolicies and divergence into a work order. Use `expand` one hop at a time and\n`relate` to test whether the suspected cause and symptom share a signal. Check\nprovenance and confidence before acting.\n\nBefore a change, use `get_blast_radius` and `check_policies` to see dependents\nand advisory architectural rules. Use `get_divergences` to compare declared\nintent with observed behavior, `get_graph_diff` to compare a saved snapshot\nwith the live graph, and `get_recent_stale_edges` when traffic goes quiet.\n`get_dependencies` maps a node\'s outgoing graph; `get_observed_dependencies`\nshows only seen runtime/provider calls. `semantic_search` finds node labels,\nnot arbitrary source text.\n\nRead source when the graph does not model what you need: comments, arbitrary\nstring literals, config details, unsupported syntax, or a repository that has\nnot been extracted or connected. If a graph answer is empty, check the daemon\nwith `npx neat.is list` or restart it with `npx neat.is up`; wire MCP with\n`npx neat.is skill --apply`.\n';
|
|
33295
|
+
|
|
33296
|
+
// src/welcome.ts
|
|
33016
33297
|
var WORDMARK = [
|
|
33017
33298
|
"\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",
|
|
33018
33299
|
"\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",
|
|
@@ -33021,21 +33302,27 @@ var WORDMARK = [
|
|
|
33021
33302
|
"\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 ",
|
|
33022
33303
|
"\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D "
|
|
33023
33304
|
];
|
|
33024
|
-
|
|
33025
|
-
|
|
33026
|
-
|
|
33027
|
-
|
|
33028
|
-
|
|
33029
|
-
|
|
33030
|
-
|
|
33031
|
-
|
|
33032
|
-
|
|
33033
|
-
|
|
33305
|
+
function renderAgentSetupPrompt(project, restPort) {
|
|
33306
|
+
const endpoint2 = restPort === void 0 ? "The daemon endpoint is unavailable; run `npx neat.is up` to start it." : `The local daemon is serving it at http://127.0.0.1:${restPort}.`;
|
|
33307
|
+
return `NEAT is set up for project "${project}". ${endpoint2}
|
|
33308
|
+
|
|
33309
|
+
NEAT models this software system as one graph: code, data, infrastructure,
|
|
33310
|
+
runtime traffic, incidents, and supported provider telemetry fused where evidence
|
|
33311
|
+
permits. Use it to see what exists, what actually ran, where a failure began, and
|
|
33312
|
+
what a change could affect. Missing observations do not prove a path never runs.
|
|
33313
|
+
|
|
33314
|
+
1. Wire NEAT's MCP server into this agent session:
|
|
33034
33315
|
npx neat.is skill --apply
|
|
33035
|
-
|
|
33316
|
+
For Codex: npx neat.is codex --apply
|
|
33317
|
+
For Cursor or Gemini, use the corresponding cursor or gemini subcommand.
|
|
33318
|
+
2. Verify the MCP \`ask\` tool answers:
|
|
33319
|
+
ask "what services are in this project and what do they talk to?"
|
|
33320
|
+
If the daemon is unreachable, run \`npx neat.is up\`.
|
|
33036
33321
|
|
|
33037
|
-
|
|
33038
|
-
|
|
33322
|
+
${AGENT_DIRECTIVE}
|
|
33323
|
+
As the app or tests run, traces fill the OBSERVED layer. Daemon log:
|
|
33324
|
+
neat-out/daemon.log. Reopen this door with \`npx neat.is welcome\`.`;
|
|
33325
|
+
}
|
|
33039
33326
|
function printHeader(out) {
|
|
33040
33327
|
for (const line of WORDMARK) out(line);
|
|
33041
33328
|
out("");
|
|
@@ -33072,15 +33359,38 @@ async function runFrontDoor(deps, out) {
|
|
|
33072
33359
|
const cwd = deps.cwd ?? process.cwd();
|
|
33073
33360
|
const flagGiven = deps.instrumentFlagGiven ?? false;
|
|
33074
33361
|
const readRegistry2 = deps.readRegistry ?? (() => readRegistry());
|
|
33362
|
+
const readDaemon = deps.readDaemon ?? (async (project) => {
|
|
33363
|
+
const daemon = await findDaemonByProject(project);
|
|
33364
|
+
return daemon?.live ? { projectPath: daemon.record.projectPath, restPort: daemon.record.ports.rest } : void 0;
|
|
33365
|
+
});
|
|
33075
33366
|
printHeader(out);
|
|
33076
33367
|
out("Welcome to NEAT. Let's get you a graph of this system.");
|
|
33077
33368
|
out("");
|
|
33078
33369
|
const keyReader = deps.readKey !== void 0 ? { read: deps.readKey, done: () => {
|
|
33079
33370
|
} } : createKeyReader();
|
|
33080
33371
|
const picked = keyReader !== null ? await selectOption(out, keyReader, deps.moveCursorUp ?? defaultMoveCursorUp) : await selectByNumber(out, readLine);
|
|
33081
|
-
if (picked === void 0)
|
|
33372
|
+
if (picked === void 0)
|
|
33373
|
+
return runSelfHosted(
|
|
33374
|
+
out,
|
|
33375
|
+
readLine,
|
|
33376
|
+
orchestrator,
|
|
33377
|
+
cwd,
|
|
33378
|
+
readRegistry2,
|
|
33379
|
+
readDaemon,
|
|
33380
|
+
flagGiven,
|
|
33381
|
+
deps.dryRun === true
|
|
33382
|
+
);
|
|
33082
33383
|
if (picked === 0) return login(["--browser"]);
|
|
33083
|
-
return runSelfHosted(
|
|
33384
|
+
return runSelfHosted(
|
|
33385
|
+
out,
|
|
33386
|
+
readLine,
|
|
33387
|
+
orchestrator,
|
|
33388
|
+
cwd,
|
|
33389
|
+
readRegistry2,
|
|
33390
|
+
readDaemon,
|
|
33391
|
+
flagGiven,
|
|
33392
|
+
deps.dryRun === true
|
|
33393
|
+
);
|
|
33084
33394
|
}
|
|
33085
33395
|
var MENU_OPTIONS = [
|
|
33086
33396
|
"Log me into Hosted Neat",
|
|
@@ -33183,17 +33493,7 @@ function decodeKey(buf) {
|
|
|
33183
33493
|
function defaultMoveCursorUp(rows) {
|
|
33184
33494
|
if (rows > 0) process.stdout.write(`\x1B[${rows}A`);
|
|
33185
33495
|
}
|
|
33186
|
-
async function runSelfHosted(out, readLine, orchestrator, cwd, readRegistry2, instrumentFlagGiven) {
|
|
33187
|
-
const wantsPrompt = (await readLine("Print a copy-paste setup prompt for your coding agent? [Y/n]: "))?.trim().toLowerCase();
|
|
33188
|
-
if (wantsPrompt !== "n" && wantsPrompt !== "no") {
|
|
33189
|
-
out("");
|
|
33190
|
-
out("\u2500\u2500\u2500 copy the prompt below into your coding agent \u2500\u2500\u2500");
|
|
33191
|
-
out("");
|
|
33192
|
-
out(AGENT_SETUP_PROMPT);
|
|
33193
|
-
out("");
|
|
33194
|
-
out("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
33195
|
-
out("");
|
|
33196
|
-
}
|
|
33496
|
+
async function runSelfHosted(out, readLine, orchestrator, cwd, readRegistry2, readDaemon, instrumentFlagGiven, dryRun) {
|
|
33197
33497
|
const project = await resolveProjectName(out, readLine, cwd, readRegistry2);
|
|
33198
33498
|
const declined = instrumentFlagGiven ? void 0 : await askToInstrument(out, readLine);
|
|
33199
33499
|
out("Building your local graph now\u2026");
|
|
@@ -33203,7 +33503,22 @@ async function runSelfHosted(out, readLine, orchestrator, cwd, readRegistry2, in
|
|
|
33203
33503
|
...project !== void 0 ? { project } : {},
|
|
33204
33504
|
...declined === void 0 ? {} : declined ? { noInstrument: true } : { yes: true }
|
|
33205
33505
|
};
|
|
33206
|
-
|
|
33506
|
+
const code = await orchestrator(cwd, overrides);
|
|
33507
|
+
if (code !== 0 || dryRun) return code;
|
|
33508
|
+
const wantsPrompt = (await readLine("Print a copy-paste setup prompt for your coding agent? [Y/n]: "))?.trim().toLowerCase();
|
|
33509
|
+
if (wantsPrompt !== "n" && wantsPrompt !== "no") {
|
|
33510
|
+
const projectName = project ?? import_node_path98.default.basename(import_node_path98.default.resolve(cwd));
|
|
33511
|
+
const daemon = await readDaemon(projectName).catch(() => void 0);
|
|
33512
|
+
const port = daemon && import_node_path98.default.resolve(daemon.projectPath) === import_node_path98.default.resolve(cwd) ? daemon.restPort : void 0;
|
|
33513
|
+
out("");
|
|
33514
|
+
out("\u2500\u2500\u2500 copy the directive below into your coding agent \u2500\u2500\u2500");
|
|
33515
|
+
out("");
|
|
33516
|
+
out(renderAgentSetupPrompt(projectName, port));
|
|
33517
|
+
out("");
|
|
33518
|
+
out("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
33519
|
+
out("");
|
|
33520
|
+
}
|
|
33521
|
+
return code;
|
|
33207
33522
|
}
|
|
33208
33523
|
async function askToInstrument(out, readLine) {
|
|
33209
33524
|
out("Instrument the services for OpenTelemetry now?");
|
|
@@ -33213,7 +33528,7 @@ async function askToInstrument(out, readLine) {
|
|
|
33213
33528
|
if (declined) {
|
|
33214
33529
|
out("");
|
|
33215
33530
|
out("Skipping instrumentation \u2014 the graph will hold the declared side only.");
|
|
33216
|
-
out("Run `neat init . --apply` when you want the runtime half.");
|
|
33531
|
+
out("Run `npx neat.is init . --apply` when you want the runtime half.");
|
|
33217
33532
|
}
|
|
33218
33533
|
out("");
|
|
33219
33534
|
return declined;
|
|
@@ -34377,7 +34692,8 @@ function formatIncidentLine(card) {
|
|
|
34377
34692
|
const cls = rc.classification ? `${rc.classification} ` : "";
|
|
34378
34693
|
tag = ` \xB7 ${cls}${rc.confidence.toFixed(2)}${provs ? ` [${provs}]` : ""}`;
|
|
34379
34694
|
}
|
|
34380
|
-
|
|
34695
|
+
const grade = card.grade ? ` \xB7 grade ${card.grade.G.toFixed(2)} ${card.grade.band}` : "";
|
|
34696
|
+
return `\u2716 incident [${card.incidentKind}] ${card.headline}${tag}${grade}`;
|
|
34381
34697
|
}
|
|
34382
34698
|
function incidentJson(card) {
|
|
34383
34699
|
return JSON.stringify(card);
|
|
@@ -34781,7 +35097,7 @@ async function runSync(opts) {
|
|
|
34781
35097
|
snapshotPath = target;
|
|
34782
35098
|
}
|
|
34783
35099
|
const skipApply = opts.dryRun || opts.noInstrument;
|
|
34784
|
-
const applyTally = skipApply ? { instrumented: 0, alreadyInstrumented: 0, libOnly: 0, browserBundle: 0, reactNative: 0 } : await applyInstallersOver(persisted.services, entry2.name);
|
|
35100
|
+
const applyTally = skipApply ? { instrumented: 0, alreadyInstrumented: 0, libOnly: 0, browserBundle: 0, reactNative: 0 } : await applyInstallersOver(persisted.services, entry2.name, { sourceEdit: opts.sourceEdit });
|
|
34785
35101
|
const warnings = [];
|
|
34786
35102
|
let daemonState = "skipped";
|
|
34787
35103
|
let exitCode = 0;
|
|
@@ -34894,6 +35210,8 @@ function usage5() {
|
|
|
34894
35210
|
console.log(" --apply run the SDK install patch in place");
|
|
34895
35211
|
console.log(" --dry-run write only neat.patch; do not register or snapshot");
|
|
34896
35212
|
console.log(" --no-install skip SDK install planning entirely");
|
|
35213
|
+
console.log(" --source-edit inject the OTel init into entry points instead of");
|
|
35214
|
+
console.log(" attaching it at process start (ADR-232)");
|
|
34897
35215
|
console.log(" watch <path> Start neat-core, watch <path>, re-extract on changes.");
|
|
34898
35216
|
console.log(" PORT (default 8080), OTEL_PORT (4318), HOST (0.0.0.0)");
|
|
34899
35217
|
console.log(" control listeners. NEAT_OTLP_GRPC=true also opens 4317.");
|
|
@@ -35001,6 +35319,7 @@ function usage5() {
|
|
|
35001
35319
|
console.log(" --token <token> bearer token for --to (or $NEAT_REMOTE_TOKEN)");
|
|
35002
35320
|
console.log(" --dry-run run extraction in-memory; do not write");
|
|
35003
35321
|
console.log(" --no-instrument skip the SDK install apply step");
|
|
35322
|
+
console.log(" --source-edit inject into entry points instead of attaching");
|
|
35004
35323
|
console.log(" --json emit the delta summary as JSON");
|
|
35005
35324
|
console.log(" connector Configure OBSERVED connectors \u2014 pull (supabase, railway,");
|
|
35006
35325
|
console.log(" firebase, cloudflare, neon, cloud-run) and push (vercel,");
|
|
@@ -35099,6 +35418,7 @@ function parseArgs(rest) {
|
|
|
35099
35418
|
dryRun: false,
|
|
35100
35419
|
noInstall: false,
|
|
35101
35420
|
noInstrument: false,
|
|
35421
|
+
sourceEdit: false,
|
|
35102
35422
|
open: false,
|
|
35103
35423
|
noOpen: false,
|
|
35104
35424
|
yes: false,
|
|
@@ -35137,6 +35457,10 @@ function parseArgs(rest) {
|
|
|
35137
35457
|
out.noInstrument = true;
|
|
35138
35458
|
continue;
|
|
35139
35459
|
}
|
|
35460
|
+
if (arg === "--source-edit") {
|
|
35461
|
+
out.sourceEdit = true;
|
|
35462
|
+
continue;
|
|
35463
|
+
}
|
|
35140
35464
|
if (arg === "--open") {
|
|
35141
35465
|
out.open = true;
|
|
35142
35466
|
continue;
|
|
@@ -35262,12 +35586,12 @@ function printDiscoveryReport(opts, services) {
|
|
|
35262
35586
|
}
|
|
35263
35587
|
console.log("");
|
|
35264
35588
|
}
|
|
35265
|
-
async function buildPatchSections(services, project) {
|
|
35589
|
+
async function buildPatchSections(services, project, sourceEdit) {
|
|
35266
35590
|
const sections = [];
|
|
35267
35591
|
for (const svc of services) {
|
|
35268
35592
|
const installer = await pickInstaller(svc.dir);
|
|
35269
35593
|
if (!installer) continue;
|
|
35270
|
-
const plan6 = await installer.plan(svc.dir, { project });
|
|
35594
|
+
const plan6 = await installer.plan(svc.dir, { project, sourceEdit });
|
|
35271
35595
|
if (isEmptyPlan(plan6) && !plan6.libOnly && plan6.runtimeKind === void 0) continue;
|
|
35272
35596
|
sections.push({ installer: installer.name, plan: plan6 });
|
|
35273
35597
|
}
|
|
@@ -35282,7 +35606,7 @@ async function runInit(opts) {
|
|
|
35282
35606
|
}
|
|
35283
35607
|
const services = await discoverServices(opts.scanPath);
|
|
35284
35608
|
printDiscoveryReport(opts, services);
|
|
35285
|
-
const sections = opts.noInstall ? [] : await buildPatchSections(services, opts.project);
|
|
35609
|
+
const sections = opts.noInstall ? [] : await buildPatchSections(services, opts.project, opts.sourceEdit);
|
|
35286
35610
|
const patch = renderPatch(sections);
|
|
35287
35611
|
const patchPath = import_node_path104.default.join(opts.scanPath, "neat.patch");
|
|
35288
35612
|
if (opts.dryRun) {
|
|
@@ -35586,6 +35910,7 @@ async function main() {
|
|
|
35586
35910
|
apply: apply6,
|
|
35587
35911
|
dryRun,
|
|
35588
35912
|
noInstall,
|
|
35913
|
+
sourceEdit: parsed.sourceEdit,
|
|
35589
35914
|
verbose: parsed.verbose
|
|
35590
35915
|
});
|
|
35591
35916
|
if (result.exitCode !== 0) process.exit(result.exitCode);
|
|
@@ -35778,6 +36103,7 @@ async function main() {
|
|
|
35778
36103
|
...parsed.token ? { token: parsed.token } : {},
|
|
35779
36104
|
dryRun: parsed.dryRun,
|
|
35780
36105
|
noInstrument: parsed.noInstrument,
|
|
36106
|
+
sourceEdit: parsed.sourceEdit,
|
|
35781
36107
|
json: parsed.json
|
|
35782
36108
|
});
|
|
35783
36109
|
if (result.exitCode !== 0) process.exit(result.exitCode);
|
|
@@ -35813,6 +36139,7 @@ async function tryOrchestrator(cmd, parsed, overrides = {}) {
|
|
|
35813
36139
|
project: projectName,
|
|
35814
36140
|
projectExplicit,
|
|
35815
36141
|
noInstrument: parsed.noInstrument,
|
|
36142
|
+
sourceEdit: parsed.sourceEdit,
|
|
35816
36143
|
open: parsed.open,
|
|
35817
36144
|
noOpen: parsed.noOpen,
|
|
35818
36145
|
yes: parsed.yes,
|
|
@@ -35825,6 +36152,10 @@ async function runWelcomeFlow(parsed) {
|
|
|
35825
36152
|
cwd,
|
|
35826
36153
|
{
|
|
35827
36154
|
...parsed,
|
|
36155
|
+
// ADR-232 — the front-door / local-menu run never source-edits, even if
|
|
36156
|
+
// `--source-edit` was on the command line. Source-edit stays an explicit
|
|
36157
|
+
// choice on a deliberately-typed `neat <path> --source-edit`.
|
|
36158
|
+
sourceEdit: false,
|
|
35828
36159
|
...opts?.project !== void 0 ? { project: opts.project } : {},
|
|
35829
36160
|
...opts?.noInstrument !== void 0 ? { noInstrument: opts.noInstrument } : {},
|
|
35830
36161
|
...opts?.yes !== void 0 ? { yes: opts.yes } : {}
|
|
@@ -35840,7 +36171,8 @@ async function runWelcomeFlow(parsed) {
|
|
|
35840
36171
|
// door's wordmark already opened this run, so the orchestrator's banner is skipped.
|
|
35841
36172
|
orchestrator: runLocal,
|
|
35842
36173
|
// An explicit flag is an answer; don't ask again.
|
|
35843
|
-
instrumentFlagGiven: parsed.noInstrument || parsed.dryRun
|
|
36174
|
+
instrumentFlagGiven: parsed.noInstrument || parsed.dryRun,
|
|
36175
|
+
dryRun: parsed.dryRun
|
|
35844
36176
|
});
|
|
35845
36177
|
}
|
|
35846
36178
|
var QUERY_VERBS = /* @__PURE__ */ new Set([
|