@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.js
CHANGED
|
@@ -8,16 +8,15 @@ import {
|
|
|
8
8
|
resolveNeatVersion,
|
|
9
9
|
startK8sSubstratePolling,
|
|
10
10
|
writeDaemonRecord
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-ECBRGTB2.js";
|
|
12
12
|
import {
|
|
13
|
-
buildSearchIndex
|
|
14
|
-
|
|
13
|
+
buildSearchIndex,
|
|
14
|
+
searchProviderFromEnv
|
|
15
|
+
} from "./chunk-VFZ6CXLI.js";
|
|
15
16
|
import {
|
|
16
17
|
DEFAULT_PROJECT,
|
|
17
18
|
PolicyViolationsLog,
|
|
18
|
-
ProjectNameCollisionError,
|
|
19
19
|
Projects,
|
|
20
|
-
RegistryError,
|
|
21
20
|
SCHEMA_VERSION,
|
|
22
21
|
addCallEdges,
|
|
23
22
|
addConfigNodes,
|
|
@@ -25,7 +24,6 @@ import {
|
|
|
25
24
|
addFiles,
|
|
26
25
|
addImports,
|
|
27
26
|
addInfra,
|
|
28
|
-
addProject,
|
|
29
27
|
addRoutes,
|
|
30
28
|
addServiceAliases,
|
|
31
29
|
addServiceNodes,
|
|
@@ -42,59 +40,66 @@ import {
|
|
|
42
40
|
ensureCompatLoaded,
|
|
43
41
|
evaluateAllPolicies,
|
|
44
42
|
extractFromDirectory,
|
|
45
|
-
findDaemonByProject,
|
|
46
|
-
findProjectByPath,
|
|
47
43
|
formatExtractionBanner,
|
|
48
44
|
formatPrecisionFloorBanner,
|
|
49
45
|
formatSkippedBanner,
|
|
50
46
|
getGraph,
|
|
51
|
-
getProject,
|
|
52
47
|
getProviderFieldSchema,
|
|
53
48
|
isEnvRef,
|
|
54
49
|
isPushProvider,
|
|
55
50
|
isStrictExtractionEnabled,
|
|
56
51
|
knownProviderNames,
|
|
57
|
-
listMachineProjects,
|
|
58
|
-
listProjects,
|
|
59
52
|
loadGraphFromDisk,
|
|
60
53
|
loadPolicyFile,
|
|
61
54
|
makeErrorSpanWriter,
|
|
62
55
|
makeSpanHandler,
|
|
63
|
-
normalizeProjectPath,
|
|
64
56
|
pathsForProject,
|
|
65
57
|
promoteFrontierNodes,
|
|
66
58
|
provisionConnector,
|
|
67
|
-
pruneRegistry,
|
|
68
59
|
readConnectorsConfig,
|
|
69
60
|
readErrorEvents,
|
|
70
|
-
readRegistry,
|
|
71
61
|
removeConnectorEntry,
|
|
72
|
-
removeDaemonRecord,
|
|
73
|
-
removeProject,
|
|
74
62
|
resetGraph,
|
|
75
63
|
retireEdgesByFile,
|
|
76
64
|
runPackageManagerInstall,
|
|
77
65
|
saveGraphToDisk,
|
|
78
|
-
setStatus,
|
|
79
|
-
signalDaemonStop,
|
|
80
66
|
startConnectorPolling,
|
|
81
67
|
startPersistLoop,
|
|
82
68
|
startStalenessLoop,
|
|
83
69
|
upsertConnectorEntry,
|
|
84
70
|
validateConnectorEntry
|
|
85
|
-
} from "./chunk-
|
|
71
|
+
} from "./chunk-H2BAPN33.js";
|
|
72
|
+
import {
|
|
73
|
+
ProjectNameCollisionError,
|
|
74
|
+
RegistryError,
|
|
75
|
+
addProject,
|
|
76
|
+
findDaemonByProject,
|
|
77
|
+
findProjectByPath,
|
|
78
|
+
getProject,
|
|
79
|
+
listMachineProjects,
|
|
80
|
+
listProjects,
|
|
81
|
+
normalizeProjectPath,
|
|
82
|
+
pruneRegistry,
|
|
83
|
+
readRegistry,
|
|
84
|
+
removeDaemonRecord,
|
|
85
|
+
removeProject,
|
|
86
|
+
setStatus,
|
|
87
|
+
signalDaemonStop
|
|
88
|
+
} from "./chunk-RGYC5U2Q.js";
|
|
86
89
|
import {
|
|
87
90
|
startOtelGrpcReceiver
|
|
88
|
-
} from "./chunk-
|
|
91
|
+
} from "./chunk-TZXSWSHX.js";
|
|
89
92
|
import {
|
|
90
|
-
__dirname,
|
|
91
|
-
__require,
|
|
92
93
|
assertBindAuthority,
|
|
93
94
|
buildOtelReceiver,
|
|
94
95
|
isLoopbackHost,
|
|
95
96
|
listenSteppingOtlp,
|
|
96
97
|
readAuthEnv
|
|
97
|
-
} from "./chunk-
|
|
98
|
+
} from "./chunk-QPG2QXCW.js";
|
|
99
|
+
import {
|
|
100
|
+
__dirname,
|
|
101
|
+
__require
|
|
102
|
+
} from "./chunk-MR64ICXX.js";
|
|
98
103
|
|
|
99
104
|
// src/cli.ts
|
|
100
105
|
import path21 from "path";
|
|
@@ -278,11 +283,19 @@ ${NEAT_OUT_LINE}
|
|
|
278
283
|
|
|
279
284
|
// src/summary.ts
|
|
280
285
|
import { EdgeType, NodeType, Provenance as Provenance2 } from "@neat.is/types";
|
|
286
|
+
|
|
287
|
+
// src/otlp-headers.ts
|
|
288
|
+
var OTLP_BEARER_PREFIX = "Authorization=Bearer%20";
|
|
289
|
+
function otlpBearerHeader(token) {
|
|
290
|
+
return OTLP_BEARER_PREFIX + encodeURIComponent(token);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// src/summary.ts
|
|
281
294
|
function renderOtelEnvBlock() {
|
|
282
295
|
return [
|
|
283
296
|
"for prod OTel routing, set these in your deploy platform's env:",
|
|
284
297
|
" OTEL_EXPORTER_OTLP_ENDPOINT=https://<your-neat-host>:4318",
|
|
285
|
-
|
|
298
|
+
` OTEL_EXPORTER_OTLP_HEADERS=${OTLP_BEARER_PREFIX}<NEAT_AUTH_TOKEN>`
|
|
286
299
|
].join("\n");
|
|
287
300
|
}
|
|
288
301
|
function findIncompatServices(nodes) {
|
|
@@ -698,9 +711,10 @@ async function startWatch(graph, opts) {
|
|
|
698
711
|
const port = opts.port ?? 8080;
|
|
699
712
|
const otelPort = opts.otelPort ?? 4318;
|
|
700
713
|
const cachePath = opts.embeddingsCachePath ?? path3.join(path3.dirname(opts.outPath), "embeddings.json");
|
|
714
|
+
const forceProvider = searchProviderFromEnv();
|
|
701
715
|
let searchIndex;
|
|
702
716
|
try {
|
|
703
|
-
searchIndex = await buildSearchIndex(graph, { cachePath });
|
|
717
|
+
searchIndex = await buildSearchIndex(graph, { cachePath, forceProvider });
|
|
704
718
|
console.log(`semantic_search: ${searchIndex.provider} provider`);
|
|
705
719
|
} catch (err) {
|
|
706
720
|
console.warn(
|
|
@@ -983,7 +997,7 @@ function emitDockerRunSnippet() {
|
|
|
983
997
|
function renderOtelEnvBlock2(token, host = "<host>") {
|
|
984
998
|
return [
|
|
985
999
|
`OTEL_EXPORTER_OTLP_ENDPOINT=https://${host}:4318`,
|
|
986
|
-
`OTEL_EXPORTER_OTLP_HEADERS
|
|
1000
|
+
`OTEL_EXPORTER_OTLP_HEADERS=${otlpBearerHeader(token)}`,
|
|
987
1001
|
"OTEL_SERVICE_NAME=<service>"
|
|
988
1002
|
].join("\n");
|
|
989
1003
|
}
|
|
@@ -1043,7 +1057,7 @@ import semver from "semver";
|
|
|
1043
1057
|
// src/installers/templates.ts
|
|
1044
1058
|
var OTEL_INIT_HEADER = "// Generated by `neat init --apply` (ADR-069). OpenTelemetry auto-instrumentation hook.";
|
|
1045
1059
|
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).";
|
|
1046
|
-
var OTEL_OTLP_HEADERS_JS = "if (process.env.NEAT_OTEL_TOKEN) process.env.OTEL_EXPORTER_OTLP_HEADERS ||= 'Authorization=Bearer
|
|
1060
|
+
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)";
|
|
1047
1061
|
var OTEL_OTLP_PROTOCOL_JS = "process.env.OTEL_EXPORTER_OTLP_PROTOCOL ||= 'http/json'";
|
|
1048
1062
|
var OTEL_ENDPOINT_RESOLVER_CJS = `;(function () {
|
|
1049
1063
|
try {
|
|
@@ -1488,7 +1502,31 @@ ${registrations.join("\n")}
|
|
|
1488
1502
|
`;
|
|
1489
1503
|
return template.replace(/__SERVICE_NAME__/g, serviceName).replace(/__PROJECT__/g, projectName).replace(/__INSTRUMENTATION_BLOCK__\n?/g, block);
|
|
1490
1504
|
}
|
|
1491
|
-
function renderEnvNeat(serviceName, projectName) {
|
|
1505
|
+
function renderEnvNeat(serviceName, projectName, nodeOptions, extraInstrumentations = []) {
|
|
1506
|
+
if (nodeOptions) {
|
|
1507
|
+
return [
|
|
1508
|
+
"# Generated by `neat init --apply` / `neat <path>` (ADR-069, ADR-232).",
|
|
1509
|
+
"# NEAT attaches its OpenTelemetry bootstrap at process start; no source is edited.",
|
|
1510
|
+
"# NODE_OPTIONS is read once when the process starts, so load this file before",
|
|
1511
|
+
"# your start command:",
|
|
1512
|
+
"# set -a; . ./.env.neat; set +a; <your start command>",
|
|
1513
|
+
"# or copy these settings into your process manager / platform environment.",
|
|
1514
|
+
`OTEL_SERVICE_NAME=${serviceName}`,
|
|
1515
|
+
"# The preload sends spans to this project's daemon, read from",
|
|
1516
|
+
"# <project>/neat-out/daemon.json; NEAT_PROJECT names the route when no record",
|
|
1517
|
+
"# is found. Set OTEL_EXPORTER_OTLP_ENDPOINT only to send spans elsewhere.",
|
|
1518
|
+
`NEAT_PROJECT=${projectName}`,
|
|
1519
|
+
"OTEL_EXPORTER_OTLP_PROTOCOL=http/json",
|
|
1520
|
+
"# Set NEAT_OTEL_TOKEN to the daemon's OTLP secret to authenticate exported spans (#410).",
|
|
1521
|
+
"# NEAT_OTEL_TOKEN=",
|
|
1522
|
+
...extraInstrumentations.length > 0 ? [
|
|
1523
|
+
"# Instrumentations outside the auto bundle, loaded by the preload (<package>#<export>).",
|
|
1524
|
+
`NEAT_OTEL_INSTRUMENTATIONS=${extraInstrumentations.join(",")}`
|
|
1525
|
+
] : [],
|
|
1526
|
+
`NODE_OPTIONS="${nodeOptions}"`,
|
|
1527
|
+
""
|
|
1528
|
+
].join("\n");
|
|
1529
|
+
}
|
|
1492
1530
|
return [
|
|
1493
1531
|
"# Generated by `neat init --apply` (ADR-069).",
|
|
1494
1532
|
`OTEL_SERVICE_NAME=${serviceName}`,
|
|
@@ -1684,14 +1722,16 @@ function detectNonBundledInstrumentations(pkg) {
|
|
|
1684
1722
|
out.push({
|
|
1685
1723
|
pkg: "@prisma/instrumentation",
|
|
1686
1724
|
version: prismaInstrVersion,
|
|
1687
|
-
registration: "instrumentations.push(new (require('@prisma/instrumentation').PrismaInstrumentation)())"
|
|
1725
|
+
registration: "instrumentations.push(new (require('@prisma/instrumentation').PrismaInstrumentation)())",
|
|
1726
|
+
exportName: "PrismaInstrumentation"
|
|
1688
1727
|
});
|
|
1689
1728
|
}
|
|
1690
1729
|
if (getMajor(deps["@nestjs/core"]) >= 11) {
|
|
1691
1730
|
out.push({
|
|
1692
1731
|
pkg: "@opentelemetry/instrumentation-nestjs-core",
|
|
1693
1732
|
version: "^0.67.0",
|
|
1694
|
-
registration: "instrumentations.push(new (require('@opentelemetry/instrumentation-nestjs-core').NestInstrumentation)())"
|
|
1733
|
+
registration: "instrumentations.push(new (require('@opentelemetry/instrumentation-nestjs-core').NestInstrumentation)())",
|
|
1734
|
+
exportName: "NestInstrumentation"
|
|
1695
1735
|
});
|
|
1696
1736
|
}
|
|
1697
1737
|
return out;
|
|
@@ -2502,6 +2542,49 @@ async function findFrameworkDispatch(serviceDir, pkg, manifestPath, project) {
|
|
|
2502
2542
|
}
|
|
2503
2543
|
return null;
|
|
2504
2544
|
}
|
|
2545
|
+
var ATTACH_PACKAGE_NAME = "@neat.is/otel-node";
|
|
2546
|
+
function attachPackageRange(coreVersion = readPackageVersion()) {
|
|
2547
|
+
return semver.valid(coreVersion) ? `^${coreVersion}` : "latest";
|
|
2548
|
+
}
|
|
2549
|
+
async function planAttachment(serviceDir, pkg, manifestPath, project) {
|
|
2550
|
+
const svcName = serviceNodeName(pkg, serviceDir);
|
|
2551
|
+
const projectName = projectToken(pkg, serviceDir, project);
|
|
2552
|
+
const envNeatFile = path5.join(serviceDir, ".env.neat");
|
|
2553
|
+
const flag = pkg.type === "module" ? "--import" : "--require";
|
|
2554
|
+
const nodeOptions = `${flag} @neat.is/otel-node/register`;
|
|
2555
|
+
const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
2556
|
+
const dependencyEdits = [];
|
|
2557
|
+
if (!(ATTACH_PACKAGE_NAME in existingDeps)) {
|
|
2558
|
+
dependencyEdits.push({ file: manifestPath, kind: "add", name: ATTACH_PACKAGE_NAME, version: attachPackageRange() });
|
|
2559
|
+
}
|
|
2560
|
+
const nonBundled = detectNonBundledInstrumentations(pkg);
|
|
2561
|
+
for (const inst of nonBundled) {
|
|
2562
|
+
if (inst.pkg in existingDeps) {
|
|
2563
|
+
if (needsVersionUpgrade(existingDeps[inst.pkg], inst.version)) {
|
|
2564
|
+
dependencyEdits.push({ file: manifestPath, kind: "upgrade", name: inst.pkg, version: inst.version, fromVersion: existingDeps[inst.pkg] });
|
|
2565
|
+
}
|
|
2566
|
+
continue;
|
|
2567
|
+
}
|
|
2568
|
+
dependencyEdits.push({ file: manifestPath, kind: "add", name: inst.pkg, version: inst.version });
|
|
2569
|
+
}
|
|
2570
|
+
const extraInstrumentations = nonBundled.map((i) => `${i.pkg}#${i.exportName}`);
|
|
2571
|
+
const generatedFiles = [];
|
|
2572
|
+
if (!await exists(envNeatFile)) {
|
|
2573
|
+
generatedFiles.push({
|
|
2574
|
+
file: envNeatFile,
|
|
2575
|
+
contents: renderEnvNeat(svcName, projectName, nodeOptions, extraInstrumentations),
|
|
2576
|
+
skipIfExists: true
|
|
2577
|
+
});
|
|
2578
|
+
}
|
|
2579
|
+
return {
|
|
2580
|
+
language: "javascript",
|
|
2581
|
+
serviceDir,
|
|
2582
|
+
dependencyEdits,
|
|
2583
|
+
entrypointEdits: [],
|
|
2584
|
+
envEdits: [OTEL_ENV],
|
|
2585
|
+
generatedFiles
|
|
2586
|
+
};
|
|
2587
|
+
}
|
|
2505
2588
|
async function plan(serviceDir, opts) {
|
|
2506
2589
|
const pkg = await readPackageJson(serviceDir);
|
|
2507
2590
|
const manifestPath = path5.join(serviceDir, "package.json");
|
|
@@ -2527,14 +2610,17 @@ async function plan(serviceDir, opts) {
|
|
|
2527
2610
|
if (!entryFile) {
|
|
2528
2611
|
return { ...empty, libOnly: true };
|
|
2529
2612
|
}
|
|
2613
|
+
const runtimeKind = await detectRuntimeKind(serviceDir, pkg);
|
|
2614
|
+
if (runtimeKind !== "node") {
|
|
2615
|
+
return { ...empty, runtimeKind };
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
if (!opts?.sourceEdit) {
|
|
2619
|
+
return planAttachment(serviceDir, pkg, manifestPath, project);
|
|
2530
2620
|
}
|
|
2531
2621
|
if (frameworkDispatch) {
|
|
2532
2622
|
return frameworkDispatch();
|
|
2533
2623
|
}
|
|
2534
|
-
const runtimeKind = await detectRuntimeKind(serviceDir, pkg);
|
|
2535
|
-
if (runtimeKind !== "node") {
|
|
2536
|
-
return { ...empty, runtimeKind };
|
|
2537
|
-
}
|
|
2538
2624
|
if (!entryFile) {
|
|
2539
2625
|
return { ...empty, libOnly: true };
|
|
2540
2626
|
}
|
|
@@ -2851,6 +2937,7 @@ var SDK_PACKAGES2 = [
|
|
|
2851
2937
|
{ name: "opentelemetry-distro", version: ">=0.49b0" },
|
|
2852
2938
|
{ name: "opentelemetry-exporter-otlp", version: ">=1.28.0" }
|
|
2853
2939
|
];
|
|
2940
|
+
var NEAT_OTEL_PACKAGE = { name: "neat-otel", version: ">=0.1.0" };
|
|
2854
2941
|
var OTEL_ENV2 = {
|
|
2855
2942
|
file: null,
|
|
2856
2943
|
key: "OTEL_EXPORTER_OTLP_ENDPOINT",
|
|
@@ -3002,34 +3089,37 @@ function reqPackageName(line) {
|
|
|
3002
3089
|
const head = stripped.split(/[\s;]/)[0] ?? "";
|
|
3003
3090
|
return head.replace(/[<>=!~].*$/, "").toLowerCase();
|
|
3004
3091
|
}
|
|
3005
|
-
async function planRequirementsTxtEdits(serviceDir) {
|
|
3092
|
+
async function planRequirementsTxtEdits(serviceDir, packages = SDK_PACKAGES2) {
|
|
3006
3093
|
const file = path6.join(serviceDir, "requirements.txt");
|
|
3007
3094
|
if (!await exists2(file)) return null;
|
|
3008
3095
|
const raw = await fs5.readFile(file, "utf8");
|
|
3009
3096
|
const presentNames = new Set(
|
|
3010
3097
|
raw.split(/\r?\n/).map(reqPackageName).filter((n) => n.length > 0)
|
|
3011
3098
|
);
|
|
3012
|
-
const missing =
|
|
3099
|
+
const missing = packages.filter((p) => !presentNames.has(p.name.toLowerCase()));
|
|
3013
3100
|
return { manifest: file, missing: [...missing] };
|
|
3014
3101
|
}
|
|
3015
|
-
|
|
3102
|
+
var PY_LAUNCHER_PYTHON = /^python3?\b/;
|
|
3103
|
+
var PY_LAUNCHERS_BROAD = /^(python3?|uvicorn|gunicorn|hypercorn|daphne|granian|celery|flask|fastapi)\b/;
|
|
3104
|
+
async function planProcfileEdits(serviceDir, broad = false) {
|
|
3016
3105
|
const procfile = path6.join(serviceDir, "Procfile");
|
|
3017
3106
|
if (!await exists2(procfile)) return [];
|
|
3018
3107
|
const raw = await fs5.readFile(procfile, "utf8");
|
|
3108
|
+
const launcher = broad ? PY_LAUNCHERS_BROAD : PY_LAUNCHER_PYTHON;
|
|
3019
3109
|
const edits = [];
|
|
3020
3110
|
for (const line of raw.split(/\r?\n/)) {
|
|
3021
3111
|
if (line.length === 0) continue;
|
|
3022
3112
|
const m = line.match(/^([a-zA-Z0-9_-]+):\s*(.+)$/);
|
|
3023
3113
|
if (!m) continue;
|
|
3024
3114
|
const cmd = m[2];
|
|
3025
|
-
if (
|
|
3115
|
+
if (!launcher.test(cmd)) continue;
|
|
3026
3116
|
if (cmd.startsWith("opentelemetry-instrument ")) continue;
|
|
3027
3117
|
const after = `${m[1]}: opentelemetry-instrument ${cmd}`;
|
|
3028
3118
|
edits.push({ file: procfile, before: line, after });
|
|
3029
3119
|
}
|
|
3030
3120
|
return edits;
|
|
3031
3121
|
}
|
|
3032
|
-
async function plan2(serviceDir) {
|
|
3122
|
+
async function plan2(serviceDir, opts) {
|
|
3033
3123
|
const empty = {
|
|
3034
3124
|
language: "python",
|
|
3035
3125
|
serviceDir,
|
|
@@ -3037,6 +3127,25 @@ async function plan2(serviceDir) {
|
|
|
3037
3127
|
entrypointEdits: [],
|
|
3038
3128
|
envEdits: []
|
|
3039
3129
|
};
|
|
3130
|
+
if (!opts?.sourceEdit) {
|
|
3131
|
+
const entrypointEdits2 = await planProcfileEdits(serviceDir, true);
|
|
3132
|
+
const reqs2 = await planRequirementsTxtEdits(serviceDir, [NEAT_OTEL_PACKAGE, ...SDK_PACKAGES2]);
|
|
3133
|
+
const dependencyEdits2 = reqs2 ? reqs2.missing.map((sdk) => ({
|
|
3134
|
+
file: reqs2.manifest,
|
|
3135
|
+
kind: "add",
|
|
3136
|
+
name: sdk.name,
|
|
3137
|
+
version: sdk.version
|
|
3138
|
+
})) : [];
|
|
3139
|
+
if (dependencyEdits2.length === 0 && entrypointEdits2.length === 0) return empty;
|
|
3140
|
+
return {
|
|
3141
|
+
language: "python",
|
|
3142
|
+
serviceDir,
|
|
3143
|
+
dependencyEdits: dependencyEdits2,
|
|
3144
|
+
entrypointEdits: entrypointEdits2,
|
|
3145
|
+
envEdits: [OTEL_ENV2]
|
|
3146
|
+
};
|
|
3147
|
+
}
|
|
3148
|
+
const entrypointEdits = await planProcfileEdits(serviceDir);
|
|
3040
3149
|
const dependencyEdits = [];
|
|
3041
3150
|
const reqs = await planRequirementsTxtEdits(serviceDir);
|
|
3042
3151
|
if (reqs) {
|
|
@@ -3049,7 +3158,6 @@ async function plan2(serviceDir) {
|
|
|
3049
3158
|
});
|
|
3050
3159
|
}
|
|
3051
3160
|
}
|
|
3052
|
-
const entrypointEdits = await planProcfileEdits(serviceDir);
|
|
3053
3161
|
const entryFile = await resolvePyEntrypoint(serviceDir);
|
|
3054
3162
|
const generatedFiles = entryFile ? [{ file: path6.join(serviceDir, NEAT_OTEL_FILENAME), contents: neatOtelPy() }] : [];
|
|
3055
3163
|
if (dependencyEdits.length === 0 && entrypointEdits.length === 0 && !entryFile) {
|
|
@@ -4000,7 +4108,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
4000
4108
|
for (const svc of services) {
|
|
4001
4109
|
const installer = await pickInstaller(svc.dir);
|
|
4002
4110
|
if (!installer) continue;
|
|
4003
|
-
const plan6 = await installer.plan(svc.dir, { project });
|
|
4111
|
+
const plan6 = await installer.plan(svc.dir, { project, sourceEdit: options.sourceEdit });
|
|
4004
4112
|
if (isEmptyPlan(plan6) && !plan6.libOnly && plan6.runtimeKind === void 0) {
|
|
4005
4113
|
already++;
|
|
4006
4114
|
continue;
|
|
@@ -4574,8 +4682,12 @@ async function runOrchestrator(opts) {
|
|
|
4574
4682
|
result.steps.apply.skipped = true;
|
|
4575
4683
|
console.log("skipped instrumentation (--no-instrument)");
|
|
4576
4684
|
} else {
|
|
4577
|
-
const tally = await applyInstallersOver(services, opts.project);
|
|
4578
|
-
result.steps.apply = {
|
|
4685
|
+
const tally = await applyInstallersOver(services, opts.project, { sourceEdit: opts.sourceEdit });
|
|
4686
|
+
result.steps.apply = {
|
|
4687
|
+
...tally,
|
|
4688
|
+
skipped: false,
|
|
4689
|
+
...!opts.sourceEdit && tally.instrumented > 0 ? { attached: true } : {}
|
|
4690
|
+
};
|
|
4579
4691
|
console.log(
|
|
4580
4692
|
`instrumented ${tally.instrumented}, already ${tally.alreadyInstrumented}, lib-only ${tally.libOnly}`
|
|
4581
4693
|
);
|
|
@@ -4688,6 +4800,12 @@ function printSummary(result, graph, daemonLog) {
|
|
|
4688
4800
|
for (const i of failedInstalls) {
|
|
4689
4801
|
console.log(` run \`${i.pm} install\` in ${i.cwd}`);
|
|
4690
4802
|
}
|
|
4803
|
+
} else if (daemonLog !== null && result.steps.apply.attached === true) {
|
|
4804
|
+
console.log("next: start your app with NEAT attached \u2014 OBSERVED edges fill in as it runs,");
|
|
4805
|
+
console.log(" and divergences surface where code and runtime disagree.");
|
|
4806
|
+
console.log(" Node: in the service directory, `set -a; . ./.env.neat; set +a`, then your");
|
|
4807
|
+
console.log(" usual start command (or put its NODE_OPTIONS in your process manager).");
|
|
4808
|
+
console.log(" Python: run your start command under `opentelemetry-instrument`.");
|
|
4691
4809
|
} else if (daemonLog !== null) {
|
|
4692
4810
|
console.log(
|
|
4693
4811
|
"next: run your app or your test suite \u2014 OBSERVED edges fill in as it executes,"
|
|
@@ -5856,7 +5974,7 @@ async function runSsoLogin(opts, deps) {
|
|
|
5856
5974
|
out("");
|
|
5857
5975
|
out("To fill the OBSERVED layer, instrument your app to send OpenTelemetry to the hosted daemon:");
|
|
5858
5976
|
out(` OTEL_EXPORTER_OTLP_ENDPOINT=${cred.ingestEndpoint}`);
|
|
5859
|
-
out(` OTEL_EXPORTER_OTLP_HEADERS
|
|
5977
|
+
out(` OTEL_EXPORTER_OTLP_HEADERS=${otlpBearerHeader(cred.otelToken)}`);
|
|
5860
5978
|
}
|
|
5861
5979
|
await runPostLoginConnect({
|
|
5862
5980
|
cpUrl: opts.cpUrl,
|
|
@@ -7092,6 +7210,11 @@ async function defaultReadSecret(prompt) {
|
|
|
7092
7210
|
import { promises as fs13 } from "fs";
|
|
7093
7211
|
import path15 from "path";
|
|
7094
7212
|
import readline4 from "readline/promises";
|
|
7213
|
+
|
|
7214
|
+
// src/agent-directive.generated.ts
|
|
7215
|
+
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';
|
|
7216
|
+
|
|
7217
|
+
// src/welcome.ts
|
|
7095
7218
|
var WORDMARK = [
|
|
7096
7219
|
"\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",
|
|
7097
7220
|
"\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",
|
|
@@ -7100,21 +7223,27 @@ var WORDMARK = [
|
|
|
7100
7223
|
"\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 ",
|
|
7101
7224
|
"\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 "
|
|
7102
7225
|
];
|
|
7103
|
-
|
|
7104
|
-
|
|
7105
|
-
|
|
7226
|
+
function renderAgentSetupPrompt(project, restPort) {
|
|
7227
|
+
const endpoint = 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}.`;
|
|
7228
|
+
return `NEAT is set up for project "${project}". ${endpoint}
|
|
7106
7229
|
|
|
7107
|
-
|
|
7108
|
-
|
|
7109
|
-
|
|
7110
|
-
|
|
7111
|
-
|
|
7112
|
-
|
|
7230
|
+
NEAT models this software system as one graph: code, data, infrastructure,
|
|
7231
|
+
runtime traffic, incidents, and supported provider telemetry fused where evidence
|
|
7232
|
+
permits. Use it to see what exists, what actually ran, where a failure began, and
|
|
7233
|
+
what a change could affect. Missing observations do not prove a path never runs.
|
|
7234
|
+
|
|
7235
|
+
1. Wire NEAT's MCP server into this agent session:
|
|
7113
7236
|
npx neat.is skill --apply
|
|
7114
|
-
|
|
7237
|
+
For Codex: npx neat.is codex --apply
|
|
7238
|
+
For Cursor or Gemini, use the corresponding cursor or gemini subcommand.
|
|
7239
|
+
2. Verify the MCP \`ask\` tool answers:
|
|
7240
|
+
ask "what services are in this project and what do they talk to?"
|
|
7241
|
+
If the daemon is unreachable, run \`npx neat.is up\`.
|
|
7115
7242
|
|
|
7116
|
-
|
|
7117
|
-
|
|
7243
|
+
${AGENT_DIRECTIVE}
|
|
7244
|
+
As the app or tests run, traces fill the OBSERVED layer. Daemon log:
|
|
7245
|
+
neat-out/daemon.log. Reopen this door with \`npx neat.is welcome\`.`;
|
|
7246
|
+
}
|
|
7118
7247
|
function printHeader(out) {
|
|
7119
7248
|
for (const line of WORDMARK) out(line);
|
|
7120
7249
|
out("");
|
|
@@ -7151,15 +7280,38 @@ async function runFrontDoor(deps, out) {
|
|
|
7151
7280
|
const cwd = deps.cwd ?? process.cwd();
|
|
7152
7281
|
const flagGiven = deps.instrumentFlagGiven ?? false;
|
|
7153
7282
|
const readRegistry2 = deps.readRegistry ?? (() => readRegistry());
|
|
7283
|
+
const readDaemon = deps.readDaemon ?? (async (project) => {
|
|
7284
|
+
const daemon = await findDaemonByProject(project);
|
|
7285
|
+
return daemon?.live ? { projectPath: daemon.record.projectPath, restPort: daemon.record.ports.rest } : void 0;
|
|
7286
|
+
});
|
|
7154
7287
|
printHeader(out);
|
|
7155
7288
|
out("Welcome to NEAT. Let's get you a graph of this system.");
|
|
7156
7289
|
out("");
|
|
7157
7290
|
const keyReader = deps.readKey !== void 0 ? { read: deps.readKey, done: () => {
|
|
7158
7291
|
} } : createKeyReader();
|
|
7159
7292
|
const picked = keyReader !== null ? await selectOption(out, keyReader, deps.moveCursorUp ?? defaultMoveCursorUp) : await selectByNumber(out, readLine);
|
|
7160
|
-
if (picked === void 0)
|
|
7293
|
+
if (picked === void 0)
|
|
7294
|
+
return runSelfHosted(
|
|
7295
|
+
out,
|
|
7296
|
+
readLine,
|
|
7297
|
+
orchestrator,
|
|
7298
|
+
cwd,
|
|
7299
|
+
readRegistry2,
|
|
7300
|
+
readDaemon,
|
|
7301
|
+
flagGiven,
|
|
7302
|
+
deps.dryRun === true
|
|
7303
|
+
);
|
|
7161
7304
|
if (picked === 0) return login(["--browser"]);
|
|
7162
|
-
return runSelfHosted(
|
|
7305
|
+
return runSelfHosted(
|
|
7306
|
+
out,
|
|
7307
|
+
readLine,
|
|
7308
|
+
orchestrator,
|
|
7309
|
+
cwd,
|
|
7310
|
+
readRegistry2,
|
|
7311
|
+
readDaemon,
|
|
7312
|
+
flagGiven,
|
|
7313
|
+
deps.dryRun === true
|
|
7314
|
+
);
|
|
7163
7315
|
}
|
|
7164
7316
|
var MENU_OPTIONS = [
|
|
7165
7317
|
"Log me into Hosted Neat",
|
|
@@ -7262,17 +7414,7 @@ function decodeKey(buf) {
|
|
|
7262
7414
|
function defaultMoveCursorUp(rows) {
|
|
7263
7415
|
if (rows > 0) process.stdout.write(`\x1B[${rows}A`);
|
|
7264
7416
|
}
|
|
7265
|
-
async function runSelfHosted(out, readLine, orchestrator, cwd, readRegistry2, instrumentFlagGiven) {
|
|
7266
|
-
const wantsPrompt = (await readLine("Print a copy-paste setup prompt for your coding agent? [Y/n]: "))?.trim().toLowerCase();
|
|
7267
|
-
if (wantsPrompt !== "n" && wantsPrompt !== "no") {
|
|
7268
|
-
out("");
|
|
7269
|
-
out("\u2500\u2500\u2500 copy the prompt below into your coding agent \u2500\u2500\u2500");
|
|
7270
|
-
out("");
|
|
7271
|
-
out(AGENT_SETUP_PROMPT);
|
|
7272
|
-
out("");
|
|
7273
|
-
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");
|
|
7274
|
-
out("");
|
|
7275
|
-
}
|
|
7417
|
+
async function runSelfHosted(out, readLine, orchestrator, cwd, readRegistry2, readDaemon, instrumentFlagGiven, dryRun) {
|
|
7276
7418
|
const project = await resolveProjectName(out, readLine, cwd, readRegistry2);
|
|
7277
7419
|
const declined = instrumentFlagGiven ? void 0 : await askToInstrument(out, readLine);
|
|
7278
7420
|
out("Building your local graph now\u2026");
|
|
@@ -7282,7 +7424,22 @@ async function runSelfHosted(out, readLine, orchestrator, cwd, readRegistry2, in
|
|
|
7282
7424
|
...project !== void 0 ? { project } : {},
|
|
7283
7425
|
...declined === void 0 ? {} : declined ? { noInstrument: true } : { yes: true }
|
|
7284
7426
|
};
|
|
7285
|
-
|
|
7427
|
+
const code = await orchestrator(cwd, overrides);
|
|
7428
|
+
if (code !== 0 || dryRun) return code;
|
|
7429
|
+
const wantsPrompt = (await readLine("Print a copy-paste setup prompt for your coding agent? [Y/n]: "))?.trim().toLowerCase();
|
|
7430
|
+
if (wantsPrompt !== "n" && wantsPrompt !== "no") {
|
|
7431
|
+
const projectName = project ?? path15.basename(path15.resolve(cwd));
|
|
7432
|
+
const daemon = await readDaemon(projectName).catch(() => void 0);
|
|
7433
|
+
const port = daemon && path15.resolve(daemon.projectPath) === path15.resolve(cwd) ? daemon.restPort : void 0;
|
|
7434
|
+
out("");
|
|
7435
|
+
out("\u2500\u2500\u2500 copy the directive below into your coding agent \u2500\u2500\u2500");
|
|
7436
|
+
out("");
|
|
7437
|
+
out(renderAgentSetupPrompt(projectName, port));
|
|
7438
|
+
out("");
|
|
7439
|
+
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");
|
|
7440
|
+
out("");
|
|
7441
|
+
}
|
|
7442
|
+
return code;
|
|
7286
7443
|
}
|
|
7287
7444
|
async function askToInstrument(out, readLine) {
|
|
7288
7445
|
out("Instrument the services for OpenTelemetry now?");
|
|
@@ -7292,7 +7449,7 @@ async function askToInstrument(out, readLine) {
|
|
|
7292
7449
|
if (declined) {
|
|
7293
7450
|
out("");
|
|
7294
7451
|
out("Skipping instrumentation \u2014 the graph will hold the declared side only.");
|
|
7295
|
-
out("Run `neat init . --apply` when you want the runtime half.");
|
|
7452
|
+
out("Run `npx neat.is init . --apply` when you want the runtime half.");
|
|
7296
7453
|
}
|
|
7297
7454
|
out("");
|
|
7298
7455
|
return declined;
|
|
@@ -8451,7 +8608,8 @@ function formatIncidentLine(card) {
|
|
|
8451
8608
|
const cls = rc.classification ? `${rc.classification} ` : "";
|
|
8452
8609
|
tag = ` \xB7 ${cls}${rc.confidence.toFixed(2)}${provs ? ` [${provs}]` : ""}`;
|
|
8453
8610
|
}
|
|
8454
|
-
|
|
8611
|
+
const grade = card.grade ? ` \xB7 grade ${card.grade.G.toFixed(2)} ${card.grade.band}` : "";
|
|
8612
|
+
return `\u2716 incident [${card.incidentKind}] ${card.headline}${tag}${grade}`;
|
|
8455
8613
|
}
|
|
8456
8614
|
function incidentJson(card) {
|
|
8457
8615
|
return JSON.stringify(card);
|
|
@@ -8854,7 +9012,7 @@ async function runSync(opts) {
|
|
|
8854
9012
|
snapshotPath = target;
|
|
8855
9013
|
}
|
|
8856
9014
|
const skipApply = opts.dryRun || opts.noInstrument;
|
|
8857
|
-
const applyTally = skipApply ? { instrumented: 0, alreadyInstrumented: 0, libOnly: 0, browserBundle: 0, reactNative: 0 } : await applyInstallersOver(persisted.services, entry2.name);
|
|
9015
|
+
const applyTally = skipApply ? { instrumented: 0, alreadyInstrumented: 0, libOnly: 0, browserBundle: 0, reactNative: 0 } : await applyInstallersOver(persisted.services, entry2.name, { sourceEdit: opts.sourceEdit });
|
|
8858
9016
|
const warnings = [];
|
|
8859
9017
|
let daemonState = "skipped";
|
|
8860
9018
|
let exitCode = 0;
|
|
@@ -8966,6 +9124,8 @@ function usage5() {
|
|
|
8966
9124
|
console.log(" --apply run the SDK install patch in place");
|
|
8967
9125
|
console.log(" --dry-run write only neat.patch; do not register or snapshot");
|
|
8968
9126
|
console.log(" --no-install skip SDK install planning entirely");
|
|
9127
|
+
console.log(" --source-edit inject the OTel init into entry points instead of");
|
|
9128
|
+
console.log(" attaching it at process start (ADR-232)");
|
|
8969
9129
|
console.log(" watch <path> Start neat-core, watch <path>, re-extract on changes.");
|
|
8970
9130
|
console.log(" PORT (default 8080), OTEL_PORT (4318), HOST (0.0.0.0)");
|
|
8971
9131
|
console.log(" control listeners. NEAT_OTLP_GRPC=true also opens 4317.");
|
|
@@ -9073,6 +9233,7 @@ function usage5() {
|
|
|
9073
9233
|
console.log(" --token <token> bearer token for --to (or $NEAT_REMOTE_TOKEN)");
|
|
9074
9234
|
console.log(" --dry-run run extraction in-memory; do not write");
|
|
9075
9235
|
console.log(" --no-instrument skip the SDK install apply step");
|
|
9236
|
+
console.log(" --source-edit inject into entry points instead of attaching");
|
|
9076
9237
|
console.log(" --json emit the delta summary as JSON");
|
|
9077
9238
|
console.log(" connector Configure OBSERVED connectors \u2014 pull (supabase, railway,");
|
|
9078
9239
|
console.log(" firebase, cloudflare, neon, cloud-run) and push (vercel,");
|
|
@@ -9171,6 +9332,7 @@ function parseArgs(rest) {
|
|
|
9171
9332
|
dryRun: false,
|
|
9172
9333
|
noInstall: false,
|
|
9173
9334
|
noInstrument: false,
|
|
9335
|
+
sourceEdit: false,
|
|
9174
9336
|
open: false,
|
|
9175
9337
|
noOpen: false,
|
|
9176
9338
|
yes: false,
|
|
@@ -9209,6 +9371,10 @@ function parseArgs(rest) {
|
|
|
9209
9371
|
out.noInstrument = true;
|
|
9210
9372
|
continue;
|
|
9211
9373
|
}
|
|
9374
|
+
if (arg === "--source-edit") {
|
|
9375
|
+
out.sourceEdit = true;
|
|
9376
|
+
continue;
|
|
9377
|
+
}
|
|
9212
9378
|
if (arg === "--open") {
|
|
9213
9379
|
out.open = true;
|
|
9214
9380
|
continue;
|
|
@@ -9334,12 +9500,12 @@ function printDiscoveryReport(opts, services) {
|
|
|
9334
9500
|
}
|
|
9335
9501
|
console.log("");
|
|
9336
9502
|
}
|
|
9337
|
-
async function buildPatchSections(services, project) {
|
|
9503
|
+
async function buildPatchSections(services, project, sourceEdit) {
|
|
9338
9504
|
const sections = [];
|
|
9339
9505
|
for (const svc of services) {
|
|
9340
9506
|
const installer = await pickInstaller(svc.dir);
|
|
9341
9507
|
if (!installer) continue;
|
|
9342
|
-
const plan6 = await installer.plan(svc.dir, { project });
|
|
9508
|
+
const plan6 = await installer.plan(svc.dir, { project, sourceEdit });
|
|
9343
9509
|
if (isEmptyPlan(plan6) && !plan6.libOnly && plan6.runtimeKind === void 0) continue;
|
|
9344
9510
|
sections.push({ installer: installer.name, plan: plan6 });
|
|
9345
9511
|
}
|
|
@@ -9354,7 +9520,7 @@ async function runInit(opts) {
|
|
|
9354
9520
|
}
|
|
9355
9521
|
const services = await discoverServices(opts.scanPath);
|
|
9356
9522
|
printDiscoveryReport(opts, services);
|
|
9357
|
-
const sections = opts.noInstall ? [] : await buildPatchSections(services, opts.project);
|
|
9523
|
+
const sections = opts.noInstall ? [] : await buildPatchSections(services, opts.project, opts.sourceEdit);
|
|
9358
9524
|
const patch = renderPatch(sections);
|
|
9359
9525
|
const patchPath = path21.join(opts.scanPath, "neat.patch");
|
|
9360
9526
|
if (opts.dryRun) {
|
|
@@ -9658,6 +9824,7 @@ async function main() {
|
|
|
9658
9824
|
apply: apply6,
|
|
9659
9825
|
dryRun,
|
|
9660
9826
|
noInstall,
|
|
9827
|
+
sourceEdit: parsed.sourceEdit,
|
|
9661
9828
|
verbose: parsed.verbose
|
|
9662
9829
|
});
|
|
9663
9830
|
if (result.exitCode !== 0) process.exit(result.exitCode);
|
|
@@ -9850,6 +10017,7 @@ async function main() {
|
|
|
9850
10017
|
...parsed.token ? { token: parsed.token } : {},
|
|
9851
10018
|
dryRun: parsed.dryRun,
|
|
9852
10019
|
noInstrument: parsed.noInstrument,
|
|
10020
|
+
sourceEdit: parsed.sourceEdit,
|
|
9853
10021
|
json: parsed.json
|
|
9854
10022
|
});
|
|
9855
10023
|
if (result.exitCode !== 0) process.exit(result.exitCode);
|
|
@@ -9885,6 +10053,7 @@ async function tryOrchestrator(cmd, parsed, overrides = {}) {
|
|
|
9885
10053
|
project: projectName,
|
|
9886
10054
|
projectExplicit,
|
|
9887
10055
|
noInstrument: parsed.noInstrument,
|
|
10056
|
+
sourceEdit: parsed.sourceEdit,
|
|
9888
10057
|
open: parsed.open,
|
|
9889
10058
|
noOpen: parsed.noOpen,
|
|
9890
10059
|
yes: parsed.yes,
|
|
@@ -9897,6 +10066,10 @@ async function runWelcomeFlow(parsed) {
|
|
|
9897
10066
|
cwd,
|
|
9898
10067
|
{
|
|
9899
10068
|
...parsed,
|
|
10069
|
+
// ADR-232 — the front-door / local-menu run never source-edits, even if
|
|
10070
|
+
// `--source-edit` was on the command line. Source-edit stays an explicit
|
|
10071
|
+
// choice on a deliberately-typed `neat <path> --source-edit`.
|
|
10072
|
+
sourceEdit: false,
|
|
9900
10073
|
...opts?.project !== void 0 ? { project: opts.project } : {},
|
|
9901
10074
|
...opts?.noInstrument !== void 0 ? { noInstrument: opts.noInstrument } : {},
|
|
9902
10075
|
...opts?.yes !== void 0 ? { yes: opts.yes } : {}
|
|
@@ -9912,7 +10085,8 @@ async function runWelcomeFlow(parsed) {
|
|
|
9912
10085
|
// door's wordmark already opened this run, so the orchestrator's banner is skipped.
|
|
9913
10086
|
orchestrator: runLocal,
|
|
9914
10087
|
// An explicit flag is an answer; don't ask again.
|
|
9915
|
-
instrumentFlagGiven: parsed.noInstrument || parsed.dryRun
|
|
10088
|
+
instrumentFlagGiven: parsed.noInstrument || parsed.dryRun,
|
|
10089
|
+
dryRun: parsed.dryRun
|
|
9916
10090
|
});
|
|
9917
10091
|
}
|
|
9918
10092
|
var QUERY_VERBS = /* @__PURE__ */ new Set([
|