@neat.is/core 0.4.4 → 0.4.6
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-YJOA7BBF.js → chunk-3QCRUEQD.js} +2 -2
- package/dist/{chunk-ZVNP3ZDH.js → chunk-HVF4S7J3.js} +6 -3
- package/dist/chunk-HVF4S7J3.js.map +1 -0
- package/dist/{chunk-J4YBTD24.js → chunk-NON5AXOR.js} +38 -4
- package/dist/chunk-NON5AXOR.js.map +1 -0
- package/dist/{chunk-G3YGPWJL.js → chunk-RBWL4HRB.js} +13 -2
- package/dist/chunk-RBWL4HRB.js.map +1 -0
- package/dist/cli.cjs +358 -105
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +335 -97
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +49 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -4
- package/dist/neatd.cjs +49 -1
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +3 -3
- package/dist/{otel-grpc-FIERFRGD.js → otel-grpc-VSPMP3OS.js} +3 -3
- package/dist/server.cjs +15 -1
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +3 -3
- package/package.json +2 -2
- package/dist/chunk-G3YGPWJL.js.map +0 -1
- package/dist/chunk-J4YBTD24.js.map +0 -1
- package/dist/chunk-ZVNP3ZDH.js.map +0 -1
- /package/dist/{chunk-YJOA7BBF.js.map → chunk-3QCRUEQD.js.map} +0 -0
- /package/dist/{otel-grpc-FIERFRGD.js.map → otel-grpc-VSPMP3OS.js.map} +0 -0
package/dist/cli.cjs
CHANGED
|
@@ -49,9 +49,9 @@ function mountBearerAuth(app, opts) {
|
|
|
49
49
|
const suffixes = [...DEFAULT_UNAUTH_SUFFIXES, ...opts.extraUnauthenticatedSuffixes ?? []];
|
|
50
50
|
const publicRead = opts.publicRead === true;
|
|
51
51
|
app.addHook("preHandler", (req, reply, done) => {
|
|
52
|
-
const
|
|
52
|
+
const path46 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
|
|
53
53
|
for (const suffix of suffixes) {
|
|
54
|
-
if (
|
|
54
|
+
if (path46 === suffix || path46.endsWith(suffix)) {
|
|
55
55
|
done();
|
|
56
56
|
return;
|
|
57
57
|
}
|
|
@@ -301,12 +301,15 @@ function parseOtlpRequest(body) {
|
|
|
301
301
|
const out = [];
|
|
302
302
|
for (const rs of body.resourceSpans ?? []) {
|
|
303
303
|
const resourceAttrs = attrsToRecord(rs.resource?.attributes);
|
|
304
|
-
const
|
|
304
|
+
const rawServiceName = resourceAttrs["service.name"];
|
|
305
|
+
const resourceServiceNamePresent = typeof rawServiceName === "string" && rawServiceName.length > 0;
|
|
306
|
+
const service = resourceServiceNamePresent ? rawServiceName : "unidentified";
|
|
305
307
|
for (const ss of rs.scopeSpans ?? []) {
|
|
306
308
|
for (const span of ss.spans ?? []) {
|
|
307
309
|
const attrs = attrsToRecord(span.attributes);
|
|
308
310
|
const parsed = {
|
|
309
311
|
service,
|
|
312
|
+
resourceServiceNamePresent,
|
|
310
313
|
traceId: span.traceId ?? "",
|
|
311
314
|
spanId: span.spanId ?? "",
|
|
312
315
|
parentSpanId: span.parentSpanId || void 0,
|
|
@@ -559,8 +562,8 @@ __export(cli_exports, {
|
|
|
559
562
|
});
|
|
560
563
|
module.exports = __toCommonJS(cli_exports);
|
|
561
564
|
init_cjs_shims();
|
|
562
|
-
var
|
|
563
|
-
var
|
|
565
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
566
|
+
var import_node_fs29 = require("fs");
|
|
564
567
|
var import_node_url3 = require("url");
|
|
565
568
|
|
|
566
569
|
// src/graph.ts
|
|
@@ -1071,19 +1074,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
1071
1074
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
1072
1075
|
let best = { path: [start], edges: [] };
|
|
1073
1076
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
1074
|
-
function step(node,
|
|
1075
|
-
if (
|
|
1076
|
-
best = { path: [...
|
|
1077
|
+
function step(node, path46, edges) {
|
|
1078
|
+
if (path46.length > best.path.length) {
|
|
1079
|
+
best = { path: [...path46], edges: [...edges] };
|
|
1077
1080
|
}
|
|
1078
|
-
if (
|
|
1081
|
+
if (path46.length - 1 >= maxDepth) return;
|
|
1079
1082
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
1080
1083
|
for (const [srcId, edge] of incoming) {
|
|
1081
1084
|
if (visited.has(srcId)) continue;
|
|
1082
1085
|
visited.add(srcId);
|
|
1083
|
-
|
|
1086
|
+
path46.push(srcId);
|
|
1084
1087
|
edges.push(edge);
|
|
1085
|
-
step(srcId,
|
|
1086
|
-
|
|
1088
|
+
step(srcId, path46, edges);
|
|
1089
|
+
path46.pop();
|
|
1087
1090
|
edges.pop();
|
|
1088
1091
|
visited.delete(srcId);
|
|
1089
1092
|
}
|
|
@@ -1620,6 +1623,14 @@ function thresholdForEdgeType(edgeType, overrides) {
|
|
|
1620
1623
|
function nowIso(ctx) {
|
|
1621
1624
|
return new Date(ctx.now ? ctx.now() : Date.now()).toISOString();
|
|
1622
1625
|
}
|
|
1626
|
+
var unidentifiedWarnedProjects = /* @__PURE__ */ new Set();
|
|
1627
|
+
function warnUnidentifiedSpan(project) {
|
|
1628
|
+
if (unidentifiedWarnedProjects.has(project)) return;
|
|
1629
|
+
unidentifiedWarnedProjects.add(project);
|
|
1630
|
+
console.warn(
|
|
1631
|
+
`[neatd] span lacked service.name; routed to 'unidentified' in project ${project}; check your OTel SDK config.`
|
|
1632
|
+
);
|
|
1633
|
+
}
|
|
1623
1634
|
function pickAttr(span, ...keys) {
|
|
1624
1635
|
for (const k of keys) {
|
|
1625
1636
|
const v = span.attributes[k];
|
|
@@ -1874,6 +1885,9 @@ async function handleSpan(ctx, span) {
|
|
|
1874
1885
|
const ts = span.startTimeIso ?? nowIso(ctx);
|
|
1875
1886
|
const nowMs = ctx.now ? ctx.now() : Date.now();
|
|
1876
1887
|
const env = span.env ?? "unknown";
|
|
1888
|
+
if (span.resourceServiceNamePresent === false) {
|
|
1889
|
+
warnUnidentifiedSpan(ctx.project ?? DEFAULT_PROJECT);
|
|
1890
|
+
}
|
|
1877
1891
|
const sourceId = ensureServiceNode(ctx.graph, span.service, env);
|
|
1878
1892
|
const isError = span.statusCode === 2;
|
|
1879
1893
|
cacheSpanService(span, nowMs);
|
|
@@ -6559,22 +6573,40 @@ var OTEL_INIT_CJS = `${OTEL_INIT_HEADER}
|
|
|
6559
6573
|
process.env.OTEL_SERVICE_NAME ||= '__SERVICE_NAME__'
|
|
6560
6574
|
process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||= 'http://localhost:4318/projects/__PROJECT__/v1/traces'
|
|
6561
6575
|
|
|
6562
|
-
require('@opentelemetry/
|
|
6576
|
+
const { NodeSDK } = require('@opentelemetry/sdk-node')
|
|
6577
|
+
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node')
|
|
6578
|
+
|
|
6579
|
+
const instrumentations = [getNodeAutoInstrumentations()]
|
|
6580
|
+
__INSTRUMENTATION_BLOCK__
|
|
6581
|
+
new NodeSDK({ instrumentations }).start()
|
|
6563
6582
|
`;
|
|
6564
6583
|
var OTEL_INIT_ESM = `${OTEL_INIT_HEADER}
|
|
6565
6584
|
process.env.OTEL_SERVICE_NAME ||= '__SERVICE_NAME__'
|
|
6566
6585
|
process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||= 'http://localhost:4318/projects/__PROJECT__/v1/traces'
|
|
6567
6586
|
|
|
6568
|
-
|
|
6587
|
+
import { NodeSDK } from '@opentelemetry/sdk-node'
|
|
6588
|
+
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'
|
|
6589
|
+
|
|
6590
|
+
const instrumentations = [getNodeAutoInstrumentations()]
|
|
6591
|
+
__INSTRUMENTATION_BLOCK__
|
|
6592
|
+
new NodeSDK({ instrumentations }).start()
|
|
6569
6593
|
`;
|
|
6570
6594
|
var OTEL_INIT_TS = `${OTEL_INIT_HEADER}
|
|
6571
6595
|
process.env.OTEL_SERVICE_NAME ||= '__SERVICE_NAME__'
|
|
6572
6596
|
process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||= 'http://localhost:4318/projects/__PROJECT__/v1/traces'
|
|
6573
6597
|
|
|
6574
|
-
|
|
6598
|
+
import { NodeSDK } from '@opentelemetry/sdk-node'
|
|
6599
|
+
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'
|
|
6600
|
+
|
|
6601
|
+
const instrumentations = [getNodeAutoInstrumentations()]
|
|
6602
|
+
__INSTRUMENTATION_BLOCK__
|
|
6603
|
+
new NodeSDK({ instrumentations }).start()
|
|
6575
6604
|
`;
|
|
6576
|
-
function renderNodeOtelInit(template, serviceName, projectName) {
|
|
6577
|
-
|
|
6605
|
+
function renderNodeOtelInit(template, serviceName, projectName, registrations = []) {
|
|
6606
|
+
const block = registrations.length === 0 ? "" : `
|
|
6607
|
+
${registrations.join("\n")}
|
|
6608
|
+
`;
|
|
6609
|
+
return template.replace(/__SERVICE_NAME__/g, serviceName).replace(/__PROJECT__/g, projectName).replace(/__INSTRUMENTATION_BLOCK__\n?/g, block);
|
|
6578
6610
|
}
|
|
6579
6611
|
function renderEnvNeat(serviceName, projectName) {
|
|
6580
6612
|
return [
|
|
@@ -6606,7 +6638,9 @@ process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||= 'http://localhost:4318/projec
|
|
|
6606
6638
|
import { NodeSDK } from '@opentelemetry/sdk-node'
|
|
6607
6639
|
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'
|
|
6608
6640
|
|
|
6609
|
-
|
|
6641
|
+
const instrumentations = [getNodeAutoInstrumentations()]
|
|
6642
|
+
__INSTRUMENTATION_BLOCK__
|
|
6643
|
+
new NodeSDK({ instrumentations }).start()
|
|
6610
6644
|
`;
|
|
6611
6645
|
var NEXT_INSTRUMENTATION_NODE_JS = `${NEXT_INSTRUMENTATION_HEADER}
|
|
6612
6646
|
process.env.OTEL_SERVICE_NAME ||= '__SERVICE_NAME__'
|
|
@@ -6615,10 +6649,15 @@ process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||= 'http://localhost:4318/projec
|
|
|
6615
6649
|
const { NodeSDK } = require('@opentelemetry/sdk-node')
|
|
6616
6650
|
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node')
|
|
6617
6651
|
|
|
6618
|
-
|
|
6652
|
+
const instrumentations = [getNodeAutoInstrumentations()]
|
|
6653
|
+
__INSTRUMENTATION_BLOCK__
|
|
6654
|
+
new NodeSDK({ instrumentations }).start()
|
|
6619
6655
|
`;
|
|
6620
|
-
function renderNextInstrumentationNode(template, serviceName, projectName) {
|
|
6621
|
-
|
|
6656
|
+
function renderNextInstrumentationNode(template, serviceName, projectName, registrations = []) {
|
|
6657
|
+
const block = registrations.length === 0 ? "" : `
|
|
6658
|
+
${registrations.join("\n")}
|
|
6659
|
+
`;
|
|
6660
|
+
return template.replace(/__SERVICE_NAME__/g, serviceName).replace(/__PROJECT__/g, projectName).replace(/__INSTRUMENTATION_BLOCK__\n?/g, block);
|
|
6622
6661
|
}
|
|
6623
6662
|
var FRAMEWORK_OTEL_INIT_HEADER = "// Generated by `neat init --apply` (ADR-074). OpenTelemetry SDK hook.";
|
|
6624
6663
|
var FRAMEWORK_OTEL_INIT_TS_BODY = `${FRAMEWORK_OTEL_INIT_HEADER}
|
|
@@ -6714,6 +6753,25 @@ var SDK_PACKAGES = [
|
|
|
6714
6753
|
{ name: "@opentelemetry/sdk-node", version: "^0.57.0" },
|
|
6715
6754
|
{ name: "@opentelemetry/auto-instrumentations-node", version: "^0.55.0" }
|
|
6716
6755
|
];
|
|
6756
|
+
function getMajor(versionRange) {
|
|
6757
|
+
if (!versionRange) return 0;
|
|
6758
|
+
const match = versionRange.match(/(\d+)/);
|
|
6759
|
+
return match ? parseInt(match[1], 10) : 0;
|
|
6760
|
+
}
|
|
6761
|
+
function detectNonBundledInstrumentations(pkg) {
|
|
6762
|
+
const deps = allDeps(pkg);
|
|
6763
|
+
const out = [];
|
|
6764
|
+
if ("@prisma/client" in deps) {
|
|
6765
|
+
const prismaMajor = getMajor(deps["@prisma/client"]);
|
|
6766
|
+
const prismaInstrVersion = prismaMajor >= 6 ? "^6.0.0" : "^5.0.0";
|
|
6767
|
+
out.push({
|
|
6768
|
+
pkg: "@prisma/instrumentation",
|
|
6769
|
+
version: prismaInstrVersion,
|
|
6770
|
+
registration: "instrumentations.push(new (require('@prisma/instrumentation').PrismaInstrumentation)())"
|
|
6771
|
+
});
|
|
6772
|
+
}
|
|
6773
|
+
return out;
|
|
6774
|
+
}
|
|
6717
6775
|
var OTEL_ENV = {
|
|
6718
6776
|
// ADR-069 §4 — endpoint moves into the per-package .env.neat (written
|
|
6719
6777
|
// by the apply phase). The envEdits surface stays for the dry-run
|
|
@@ -7004,8 +7062,19 @@ async function planNext(serviceDir, pkg, manifestPath, nextConfigPath, project)
|
|
|
7004
7062
|
version: sdk.version
|
|
7005
7063
|
});
|
|
7006
7064
|
}
|
|
7065
|
+
const nonBundled = detectNonBundledInstrumentations(pkg);
|
|
7066
|
+
for (const inst of nonBundled) {
|
|
7067
|
+
if (inst.pkg in existingDeps) continue;
|
|
7068
|
+
dependencyEdits.push({
|
|
7069
|
+
file: manifestPath,
|
|
7070
|
+
kind: "add",
|
|
7071
|
+
name: inst.pkg,
|
|
7072
|
+
version: inst.version
|
|
7073
|
+
});
|
|
7074
|
+
}
|
|
7007
7075
|
const svcName = serviceNodeName(pkg, serviceDir);
|
|
7008
7076
|
const projectName = projectToken(pkg, serviceDir, project);
|
|
7077
|
+
const registrations = nonBundled.map((i) => i.registration);
|
|
7009
7078
|
const generatedFiles = [];
|
|
7010
7079
|
if (!await exists2(instrumentationFile)) {
|
|
7011
7080
|
generatedFiles.push({
|
|
@@ -7020,7 +7089,8 @@ async function planNext(serviceDir, pkg, manifestPath, nextConfigPath, project)
|
|
|
7020
7089
|
contents: renderNextInstrumentationNode(
|
|
7021
7090
|
useTs ? NEXT_INSTRUMENTATION_NODE_TS : NEXT_INSTRUMENTATION_NODE_JS,
|
|
7022
7091
|
svcName,
|
|
7023
|
-
projectName
|
|
7092
|
+
projectName,
|
|
7093
|
+
registrations
|
|
7024
7094
|
),
|
|
7025
7095
|
skipIfExists: true
|
|
7026
7096
|
});
|
|
@@ -7370,55 +7440,73 @@ async function planAstro(serviceDir, pkg, manifestPath, project) {
|
|
|
7370
7440
|
framework: "astro"
|
|
7371
7441
|
};
|
|
7372
7442
|
}
|
|
7373
|
-
async function
|
|
7374
|
-
const pkg = await readPackageJson(serviceDir);
|
|
7375
|
-
const manifestPath = import_node_path40.default.join(serviceDir, "package.json");
|
|
7376
|
-
const project = opts?.project;
|
|
7377
|
-
const empty = {
|
|
7378
|
-
language: "javascript",
|
|
7379
|
-
serviceDir,
|
|
7380
|
-
dependencyEdits: [],
|
|
7381
|
-
entrypointEdits: [],
|
|
7382
|
-
envEdits: [],
|
|
7383
|
-
generatedFiles: []
|
|
7384
|
-
};
|
|
7385
|
-
if (!pkg) return empty;
|
|
7443
|
+
async function findFrameworkDispatch(serviceDir, pkg, manifestPath, project) {
|
|
7386
7444
|
if (hasNextDependency(pkg)) {
|
|
7387
7445
|
const nextConfig = await findNextConfig(serviceDir);
|
|
7388
7446
|
if (nextConfig) {
|
|
7389
|
-
return planNext(serviceDir, pkg, manifestPath, nextConfig, project);
|
|
7447
|
+
return () => planNext(serviceDir, pkg, manifestPath, nextConfig, project);
|
|
7390
7448
|
}
|
|
7391
7449
|
}
|
|
7392
7450
|
if (hasRemixDependency(pkg)) {
|
|
7393
7451
|
const remixEntry = await findRemixEntry(serviceDir);
|
|
7394
7452
|
if (remixEntry) {
|
|
7395
|
-
return planRemix(serviceDir, pkg, manifestPath, remixEntry, project);
|
|
7453
|
+
return () => planRemix(serviceDir, pkg, manifestPath, remixEntry, project);
|
|
7396
7454
|
}
|
|
7397
7455
|
}
|
|
7398
7456
|
if (hasSvelteKitDependency(pkg)) {
|
|
7399
7457
|
const hooks = await findSvelteKitHooks(serviceDir);
|
|
7400
7458
|
const config = await findSvelteKitConfig(serviceDir);
|
|
7401
7459
|
if (hooks || config) {
|
|
7402
|
-
return planSvelteKit(serviceDir, pkg, manifestPath, hooks, project);
|
|
7460
|
+
return () => planSvelteKit(serviceDir, pkg, manifestPath, hooks, project);
|
|
7403
7461
|
}
|
|
7404
7462
|
}
|
|
7405
7463
|
if (hasNuxtDependency(pkg)) {
|
|
7406
7464
|
const nuxtConfig = await findNuxtConfig(serviceDir);
|
|
7407
7465
|
if (nuxtConfig) {
|
|
7408
|
-
return planNuxt(serviceDir, pkg, manifestPath, project);
|
|
7466
|
+
return () => planNuxt(serviceDir, pkg, manifestPath, project);
|
|
7409
7467
|
}
|
|
7410
7468
|
}
|
|
7411
7469
|
if (hasAstroDependency(pkg)) {
|
|
7412
7470
|
const astroConfig = await findAstroConfig(serviceDir);
|
|
7413
7471
|
if (astroConfig) {
|
|
7414
|
-
return planAstro(serviceDir, pkg, manifestPath, project);
|
|
7472
|
+
return () => planAstro(serviceDir, pkg, manifestPath, project);
|
|
7473
|
+
}
|
|
7474
|
+
}
|
|
7475
|
+
return null;
|
|
7476
|
+
}
|
|
7477
|
+
async function plan(serviceDir, opts) {
|
|
7478
|
+
const pkg = await readPackageJson(serviceDir);
|
|
7479
|
+
const manifestPath = import_node_path40.default.join(serviceDir, "package.json");
|
|
7480
|
+
const project = opts?.project;
|
|
7481
|
+
const empty = {
|
|
7482
|
+
language: "javascript",
|
|
7483
|
+
serviceDir,
|
|
7484
|
+
dependencyEdits: [],
|
|
7485
|
+
entrypointEdits: [],
|
|
7486
|
+
envEdits: [],
|
|
7487
|
+
generatedFiles: []
|
|
7488
|
+
};
|
|
7489
|
+
if (!pkg) return empty;
|
|
7490
|
+
const frameworkDispatch = await findFrameworkDispatch(
|
|
7491
|
+
serviceDir,
|
|
7492
|
+
pkg,
|
|
7493
|
+
manifestPath,
|
|
7494
|
+
project
|
|
7495
|
+
);
|
|
7496
|
+
let entryFile = null;
|
|
7497
|
+
if (!frameworkDispatch) {
|
|
7498
|
+
entryFile = await resolveEntry(serviceDir, pkg);
|
|
7499
|
+
if (!entryFile) {
|
|
7500
|
+
return { ...empty, libOnly: true };
|
|
7415
7501
|
}
|
|
7416
7502
|
}
|
|
7503
|
+
if (frameworkDispatch) {
|
|
7504
|
+
return frameworkDispatch();
|
|
7505
|
+
}
|
|
7417
7506
|
const runtimeKind = await detectRuntimeKind(serviceDir, pkg);
|
|
7418
7507
|
if (runtimeKind !== "node") {
|
|
7419
7508
|
return { ...empty, runtimeKind };
|
|
7420
7509
|
}
|
|
7421
|
-
const entryFile = await resolveEntry(serviceDir, pkg);
|
|
7422
7510
|
if (!entryFile) {
|
|
7423
7511
|
return { ...empty, libOnly: true };
|
|
7424
7512
|
}
|
|
@@ -7436,6 +7524,16 @@ async function plan(serviceDir, opts) {
|
|
|
7436
7524
|
version: sdk.version
|
|
7437
7525
|
});
|
|
7438
7526
|
}
|
|
7527
|
+
const nonBundled = detectNonBundledInstrumentations(pkg);
|
|
7528
|
+
for (const inst of nonBundled) {
|
|
7529
|
+
if (inst.pkg in existingDeps) continue;
|
|
7530
|
+
dependencyEdits.push({
|
|
7531
|
+
file: manifestPath,
|
|
7532
|
+
kind: "add",
|
|
7533
|
+
name: inst.pkg,
|
|
7534
|
+
version: inst.version
|
|
7535
|
+
});
|
|
7536
|
+
}
|
|
7439
7537
|
const entrypointEdits = [];
|
|
7440
7538
|
try {
|
|
7441
7539
|
const raw = await import_node_fs25.promises.readFile(entryFile, "utf8");
|
|
@@ -7454,11 +7552,17 @@ async function plan(serviceDir, opts) {
|
|
|
7454
7552
|
}
|
|
7455
7553
|
const svcName = serviceNodeName(pkg, serviceDir);
|
|
7456
7554
|
const projectName = projectToken(pkg, serviceDir, project);
|
|
7555
|
+
const registrations = nonBundled.map((i) => i.registration);
|
|
7457
7556
|
const generatedFiles = [];
|
|
7458
7557
|
if (!await exists2(otelInitFile)) {
|
|
7459
7558
|
generatedFiles.push({
|
|
7460
7559
|
file: otelInitFile,
|
|
7461
|
-
contents: renderNodeOtelInit(
|
|
7560
|
+
contents: renderNodeOtelInit(
|
|
7561
|
+
otelInitContents(flavor),
|
|
7562
|
+
svcName,
|
|
7563
|
+
projectName,
|
|
7564
|
+
registrations
|
|
7565
|
+
),
|
|
7462
7566
|
skipIfExists: true
|
|
7463
7567
|
});
|
|
7464
7568
|
}
|
|
@@ -7991,18 +8095,100 @@ function renderPatch(sections) {
|
|
|
7991
8095
|
|
|
7992
8096
|
// src/orchestrator.ts
|
|
7993
8097
|
init_cjs_shims();
|
|
7994
|
-
var
|
|
8098
|
+
var import_node_fs28 = require("fs");
|
|
7995
8099
|
var import_node_http = __toESM(require("http"), 1);
|
|
8100
|
+
var import_node_net = __toESM(require("net"), 1);
|
|
8101
|
+
var import_node_path43 = __toESM(require("path"), 1);
|
|
8102
|
+
var import_node_child_process3 = require("child_process");
|
|
8103
|
+
var import_node_readline = __toESM(require("readline"), 1);
|
|
8104
|
+
|
|
8105
|
+
// src/installers/package-manager.ts
|
|
8106
|
+
init_cjs_shims();
|
|
8107
|
+
var import_node_fs27 = require("fs");
|
|
7996
8108
|
var import_node_path42 = __toESM(require("path"), 1);
|
|
7997
8109
|
var import_node_child_process2 = require("child_process");
|
|
7998
|
-
var
|
|
8110
|
+
var LOCKFILE_PRIORITY = [
|
|
8111
|
+
{ lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
|
|
8112
|
+
{ lockfile: "pnpm-lock.yaml", pm: "pnpm", args: ["install", "--no-summary"] },
|
|
8113
|
+
{ lockfile: "yarn.lock", pm: "yarn", args: ["install", "--silent"] },
|
|
8114
|
+
{
|
|
8115
|
+
lockfile: "package-lock.json",
|
|
8116
|
+
pm: "npm",
|
|
8117
|
+
args: ["install", "--no-audit", "--no-fund", "--prefer-offline"]
|
|
8118
|
+
}
|
|
8119
|
+
];
|
|
8120
|
+
var NPM_FALLBACK_ARGS = ["install", "--no-audit", "--no-fund", "--prefer-offline"];
|
|
8121
|
+
async function exists4(p) {
|
|
8122
|
+
try {
|
|
8123
|
+
await import_node_fs27.promises.access(p);
|
|
8124
|
+
return true;
|
|
8125
|
+
} catch {
|
|
8126
|
+
return false;
|
|
8127
|
+
}
|
|
8128
|
+
}
|
|
8129
|
+
async function detectPackageManager(serviceDir) {
|
|
8130
|
+
let dir = import_node_path42.default.resolve(serviceDir);
|
|
8131
|
+
const stops = /* @__PURE__ */ new Set();
|
|
8132
|
+
for (let i = 0; i < 64; i++) {
|
|
8133
|
+
if (stops.has(dir)) break;
|
|
8134
|
+
stops.add(dir);
|
|
8135
|
+
for (const candidate of LOCKFILE_PRIORITY) {
|
|
8136
|
+
const lockPath = import_node_path42.default.join(dir, candidate.lockfile);
|
|
8137
|
+
if (await exists4(lockPath)) {
|
|
8138
|
+
return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
|
|
8139
|
+
}
|
|
8140
|
+
}
|
|
8141
|
+
const parent = import_node_path42.default.dirname(dir);
|
|
8142
|
+
if (parent === dir) break;
|
|
8143
|
+
dir = parent;
|
|
8144
|
+
}
|
|
8145
|
+
return { pm: "npm", cwd: import_node_path42.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
|
|
8146
|
+
}
|
|
8147
|
+
async function runPackageManagerInstall(cmd) {
|
|
8148
|
+
return new Promise((resolve) => {
|
|
8149
|
+
const child = (0, import_node_child_process2.spawn)(cmd.pm, cmd.args, {
|
|
8150
|
+
cwd: cmd.cwd,
|
|
8151
|
+
// Inherit PATH + HOME so the user's installed managers resolve.
|
|
8152
|
+
env: process.env,
|
|
8153
|
+
// `false` keeps the parent in control of cleanup if the orchestrator
|
|
8154
|
+
// exits before install finishes. Cross-platform-safe.
|
|
8155
|
+
shell: false,
|
|
8156
|
+
stdio: ["ignore", "ignore", "pipe"]
|
|
8157
|
+
});
|
|
8158
|
+
let stderr = "";
|
|
8159
|
+
child.stderr?.on("data", (chunk) => {
|
|
8160
|
+
stderr += chunk.toString("utf8");
|
|
8161
|
+
});
|
|
8162
|
+
child.on("error", (err) => {
|
|
8163
|
+
resolve({
|
|
8164
|
+
pm: cmd.pm,
|
|
8165
|
+
cwd: cmd.cwd,
|
|
8166
|
+
args: cmd.args,
|
|
8167
|
+
exitCode: 127,
|
|
8168
|
+
stderr: stderr + `
|
|
8169
|
+
${err.message}`
|
|
8170
|
+
});
|
|
8171
|
+
});
|
|
8172
|
+
child.on("close", (code) => {
|
|
8173
|
+
resolve({
|
|
8174
|
+
pm: cmd.pm,
|
|
8175
|
+
cwd: cmd.cwd,
|
|
8176
|
+
args: cmd.args,
|
|
8177
|
+
exitCode: code ?? 1,
|
|
8178
|
+
stderr: stderr.trim()
|
|
8179
|
+
});
|
|
8180
|
+
});
|
|
8181
|
+
});
|
|
8182
|
+
}
|
|
8183
|
+
|
|
8184
|
+
// src/orchestrator.ts
|
|
7999
8185
|
async function extractAndPersist(opts) {
|
|
8000
8186
|
const services = await discoverServices(opts.scanPath);
|
|
8001
8187
|
const languages = [...new Set(services.map((s) => s.node.language))].sort();
|
|
8002
8188
|
const graphKey = opts.projectExplicit ? opts.project : DEFAULT_PROJECT;
|
|
8003
8189
|
resetGraph(graphKey);
|
|
8004
8190
|
const graph = getGraph(graphKey);
|
|
8005
|
-
const projectPaths = pathsForProject(graphKey,
|
|
8191
|
+
const projectPaths = pathsForProject(graphKey, import_node_path43.default.join(opts.scanPath, "neat-out"));
|
|
8006
8192
|
const extraction = await extractFromDirectory(graph, opts.scanPath, {
|
|
8007
8193
|
errorsPath: projectPaths.errorsPath
|
|
8008
8194
|
});
|
|
@@ -8020,12 +8206,15 @@ async function extractAndPersist(opts) {
|
|
|
8020
8206
|
errorsPath: projectPaths.errorsPath
|
|
8021
8207
|
};
|
|
8022
8208
|
}
|
|
8023
|
-
async function applyInstallersOver(services, project) {
|
|
8209
|
+
async function applyInstallersOver(services, project, options = {}) {
|
|
8210
|
+
const resolveManager = options.resolveManager ?? detectPackageManager;
|
|
8211
|
+
const runInstall = options.runInstall ?? runPackageManagerInstall;
|
|
8024
8212
|
let instrumented = 0;
|
|
8025
8213
|
let already = 0;
|
|
8026
8214
|
let libOnly = 0;
|
|
8027
8215
|
let browserBundle = 0;
|
|
8028
8216
|
let reactNative = 0;
|
|
8217
|
+
const installPlans = /* @__PURE__ */ new Map();
|
|
8029
8218
|
for (const svc of services) {
|
|
8030
8219
|
const installer = await pickInstaller(svc.dir);
|
|
8031
8220
|
if (!installer) continue;
|
|
@@ -8035,8 +8224,14 @@ async function applyInstallersOver(services, project) {
|
|
|
8035
8224
|
continue;
|
|
8036
8225
|
}
|
|
8037
8226
|
const outcome = await installer.apply(plan3);
|
|
8038
|
-
if (outcome.outcome === "instrumented")
|
|
8039
|
-
|
|
8227
|
+
if (outcome.outcome === "instrumented") {
|
|
8228
|
+
instrumented++;
|
|
8229
|
+
if (plan3.dependencyEdits.length > 0) {
|
|
8230
|
+
const cmd = await resolveManager(svc.dir);
|
|
8231
|
+
const key = `${cmd.pm}:${cmd.cwd}`;
|
|
8232
|
+
if (!installPlans.has(key)) installPlans.set(key, cmd);
|
|
8233
|
+
}
|
|
8234
|
+
} else if (outcome.outcome === "already-instrumented") already++;
|
|
8040
8235
|
else if (outcome.outcome === "lib-only") libOnly++;
|
|
8041
8236
|
else if (outcome.outcome === "browser-bundle") {
|
|
8042
8237
|
browserBundle++;
|
|
@@ -8046,7 +8241,30 @@ async function applyInstallersOver(services, project) {
|
|
|
8046
8241
|
console.log(`skipping ${svc.dir}: React Native target; browser-OTel support lands in a future release.`);
|
|
8047
8242
|
}
|
|
8048
8243
|
}
|
|
8049
|
-
|
|
8244
|
+
const packageManagerInstalls = [];
|
|
8245
|
+
for (const cmd of installPlans.values()) {
|
|
8246
|
+
console.log(`running \`${cmd.pm} ${cmd.args.join(" ")}\` in ${cmd.cwd}`);
|
|
8247
|
+
const result = await runInstall(cmd);
|
|
8248
|
+
packageManagerInstalls.push(result);
|
|
8249
|
+
if (result.exitCode !== 0) {
|
|
8250
|
+
console.error(
|
|
8251
|
+
`neat: ${cmd.pm} install failed in ${cmd.cwd} (exit ${result.exitCode}); run it manually to surface the error.`
|
|
8252
|
+
);
|
|
8253
|
+
if (result.stderr.length > 0) {
|
|
8254
|
+
for (const line of result.stderr.split(/\r?\n/).slice(0, 20)) {
|
|
8255
|
+
console.error(` ${line}`);
|
|
8256
|
+
}
|
|
8257
|
+
}
|
|
8258
|
+
}
|
|
8259
|
+
}
|
|
8260
|
+
return {
|
|
8261
|
+
instrumented,
|
|
8262
|
+
alreadyInstrumented: already,
|
|
8263
|
+
libOnly,
|
|
8264
|
+
browserBundle,
|
|
8265
|
+
reactNative,
|
|
8266
|
+
packageManagerInstalls
|
|
8267
|
+
};
|
|
8050
8268
|
}
|
|
8051
8269
|
async function promptYesNo(question) {
|
|
8052
8270
|
const rl = import_node_readline.default.createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -8159,11 +8377,33 @@ async function waitForDaemonReady(restPort, timeoutMs) {
|
|
|
8159
8377
|
stillBootstrapping: projects.filter((p) => p.status === "bootstrapping").map((p) => p.name)
|
|
8160
8378
|
};
|
|
8161
8379
|
}
|
|
8380
|
+
var NEAT_PORTS = [8080, 4318, 6328];
|
|
8381
|
+
async function isPortFree(port) {
|
|
8382
|
+
return new Promise((resolve) => {
|
|
8383
|
+
const server = import_node_net.default.createServer();
|
|
8384
|
+
server.once("error", () => resolve(false));
|
|
8385
|
+
server.once("listening", () => server.close(() => resolve(true)));
|
|
8386
|
+
server.listen(port, "127.0.0.1");
|
|
8387
|
+
});
|
|
8388
|
+
}
|
|
8389
|
+
async function probePortsFree() {
|
|
8390
|
+
for (const port of NEAT_PORTS) {
|
|
8391
|
+
if (!await isPortFree(port)) return { free: false, held: port };
|
|
8392
|
+
}
|
|
8393
|
+
return { free: true };
|
|
8394
|
+
}
|
|
8395
|
+
function formatPortCollisionMessage(port) {
|
|
8396
|
+
return [
|
|
8397
|
+
`neat: port ${port} is in use; the NEAT daemon needs it.`,
|
|
8398
|
+
` run \`neatd stop\` to release the previous daemon, or`,
|
|
8399
|
+
` \`lsof -i :${port}\` to find the holding process.`
|
|
8400
|
+
];
|
|
8401
|
+
}
|
|
8162
8402
|
function spawnDaemonDetached() {
|
|
8163
|
-
const here =
|
|
8403
|
+
const here = import_node_path43.default.dirname(new URL(importMetaUrl).pathname);
|
|
8164
8404
|
const candidates = [
|
|
8165
|
-
|
|
8166
|
-
|
|
8405
|
+
import_node_path43.default.join(here, "neatd.cjs"),
|
|
8406
|
+
import_node_path43.default.join(here, "neatd.js")
|
|
8167
8407
|
];
|
|
8168
8408
|
let entry2 = null;
|
|
8169
8409
|
const fsSync = require("fs");
|
|
@@ -8183,7 +8423,7 @@ function spawnDaemonDetached() {
|
|
|
8183
8423
|
if (!hasToken && (!env.HOST || env.HOST.length === 0)) {
|
|
8184
8424
|
env.HOST = "127.0.0.1";
|
|
8185
8425
|
}
|
|
8186
|
-
const child = (0,
|
|
8426
|
+
const child = (0, import_node_child_process3.spawn)(process.execPath, [entry2, "start"], {
|
|
8187
8427
|
detached: true,
|
|
8188
8428
|
// stderr inherits the orchestrator's fd so the daemon's
|
|
8189
8429
|
// `BindAuthorityError` message lands in front of the operator instead
|
|
@@ -8200,7 +8440,7 @@ function openBrowser(url) {
|
|
|
8200
8440
|
const cmd = platform === "darwin" ? "open" : platform === "win32" ? "cmd" : "xdg-open";
|
|
8201
8441
|
const args = platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
8202
8442
|
try {
|
|
8203
|
-
const child = (0,
|
|
8443
|
+
const child = (0, import_node_child_process3.spawn)(cmd, args, { detached: true, stdio: "ignore" });
|
|
8204
8444
|
child.on("error", () => {
|
|
8205
8445
|
});
|
|
8206
8446
|
child.unref();
|
|
@@ -8221,7 +8461,7 @@ async function runOrchestrator(opts) {
|
|
|
8221
8461
|
browser: "skipped"
|
|
8222
8462
|
}
|
|
8223
8463
|
};
|
|
8224
|
-
const stat = await
|
|
8464
|
+
const stat = await import_node_fs28.promises.stat(opts.scanPath).catch(() => null);
|
|
8225
8465
|
if (!stat || !stat.isDirectory()) {
|
|
8226
8466
|
console.error(`neat: ${opts.scanPath} is not a directory`);
|
|
8227
8467
|
result.exitCode = 2;
|
|
@@ -8289,12 +8529,24 @@ async function runOrchestrator(opts) {
|
|
|
8289
8529
|
console.log(
|
|
8290
8530
|
`instrumented ${tally.instrumented}, already ${tally.alreadyInstrumented}, lib-only ${tally.libOnly}`
|
|
8291
8531
|
);
|
|
8532
|
+
const failedInstalls = tally.packageManagerInstalls.filter((i) => i.exitCode !== 0);
|
|
8533
|
+
if (failedInstalls.length > 0) {
|
|
8534
|
+
result.exitCode = 1;
|
|
8535
|
+
}
|
|
8292
8536
|
}
|
|
8293
8537
|
const restPort = Number(process.env.PORT ?? 8080);
|
|
8294
8538
|
const timeoutMs = opts.daemonReadyTimeoutMs ?? DEFAULT_DAEMON_READY_TIMEOUT_MS;
|
|
8295
8539
|
if (await checkDaemonHealth(restPort)) {
|
|
8296
8540
|
result.steps.daemon = "already-running";
|
|
8297
8541
|
} else {
|
|
8542
|
+
const probe = await probePortsFree();
|
|
8543
|
+
if (!probe.free) {
|
|
8544
|
+
for (const line of formatPortCollisionMessage(probe.held)) {
|
|
8545
|
+
console.error(line);
|
|
8546
|
+
}
|
|
8547
|
+
result.exitCode = 3;
|
|
8548
|
+
return result;
|
|
8549
|
+
}
|
|
8298
8550
|
try {
|
|
8299
8551
|
spawnDaemonDetached();
|
|
8300
8552
|
} catch (err) {
|
|
@@ -8352,7 +8604,7 @@ function printSummary(result, graph, dashboardUrl) {
|
|
|
8352
8604
|
|
|
8353
8605
|
// src/cli-verbs.ts
|
|
8354
8606
|
init_cjs_shims();
|
|
8355
|
-
var
|
|
8607
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
8356
8608
|
|
|
8357
8609
|
// src/cli-client.ts
|
|
8358
8610
|
init_cjs_shims();
|
|
@@ -8377,10 +8629,10 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
8377
8629
|
const root = baseUrl.replace(/\/$/, "");
|
|
8378
8630
|
const authHeader = bearerToken && bearerToken.length > 0 ? { authorization: `Bearer ${bearerToken}` } : {};
|
|
8379
8631
|
return {
|
|
8380
|
-
async get(
|
|
8632
|
+
async get(path46) {
|
|
8381
8633
|
let res;
|
|
8382
8634
|
try {
|
|
8383
|
-
res = await fetch(`${root}${
|
|
8635
|
+
res = await fetch(`${root}${path46}`, {
|
|
8384
8636
|
headers: { ...authHeader }
|
|
8385
8637
|
});
|
|
8386
8638
|
} catch (err) {
|
|
@@ -8392,16 +8644,16 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
8392
8644
|
const body = await res.text().catch(() => "");
|
|
8393
8645
|
throw new HttpError(
|
|
8394
8646
|
res.status,
|
|
8395
|
-
`${res.status} ${res.statusText} on GET ${
|
|
8647
|
+
`${res.status} ${res.statusText} on GET ${path46}: ${body}`,
|
|
8396
8648
|
body
|
|
8397
8649
|
);
|
|
8398
8650
|
}
|
|
8399
8651
|
return await res.json();
|
|
8400
8652
|
},
|
|
8401
|
-
async post(
|
|
8653
|
+
async post(path46, body) {
|
|
8402
8654
|
let res;
|
|
8403
8655
|
try {
|
|
8404
|
-
res = await fetch(`${root}${
|
|
8656
|
+
res = await fetch(`${root}${path46}`, {
|
|
8405
8657
|
method: "POST",
|
|
8406
8658
|
headers: { "content-type": "application/json", ...authHeader },
|
|
8407
8659
|
body: JSON.stringify(body)
|
|
@@ -8415,7 +8667,7 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
8415
8667
|
const text = await res.text().catch(() => "");
|
|
8416
8668
|
throw new HttpError(
|
|
8417
8669
|
res.status,
|
|
8418
|
-
`${res.status} ${res.statusText} on POST ${
|
|
8670
|
+
`${res.status} ${res.statusText} on POST ${path46}: ${text}`,
|
|
8419
8671
|
text
|
|
8420
8672
|
);
|
|
8421
8673
|
}
|
|
@@ -8429,12 +8681,12 @@ function projectPath(project, suffix) {
|
|
|
8429
8681
|
}
|
|
8430
8682
|
async function runRootCause(client, input) {
|
|
8431
8683
|
const qs = input.errorId ? `?errorId=${encodeURIComponent(input.errorId)}` : "";
|
|
8432
|
-
const
|
|
8684
|
+
const path46 = projectPath(
|
|
8433
8685
|
input.project,
|
|
8434
8686
|
`/graph/root-cause/${encodeURIComponent(input.errorNode)}${qs}`
|
|
8435
8687
|
);
|
|
8436
8688
|
try {
|
|
8437
|
-
const result = await client.get(
|
|
8689
|
+
const result = await client.get(path46);
|
|
8438
8690
|
const arrowPath = result.traversalPath.join(" \u2190 ");
|
|
8439
8691
|
const provenances = result.edgeProvenances.length ? result.edgeProvenances.join(", ") : "(direct, no edges traversed)";
|
|
8440
8692
|
const summary = `Root cause for ${input.errorNode} is ${result.rootCauseNode}. ` + result.rootCauseReason + (result.fixRecommendation ? ` Recommended fix: ${result.fixRecommendation}.` : "");
|
|
@@ -8460,12 +8712,12 @@ async function runRootCause(client, input) {
|
|
|
8460
8712
|
}
|
|
8461
8713
|
async function runBlastRadius(client, input) {
|
|
8462
8714
|
const qs = input.depth !== void 0 ? `?depth=${input.depth}` : "";
|
|
8463
|
-
const
|
|
8715
|
+
const path46 = projectPath(
|
|
8464
8716
|
input.project,
|
|
8465
8717
|
`/graph/blast-radius/${encodeURIComponent(input.nodeId)}${qs}`
|
|
8466
8718
|
);
|
|
8467
8719
|
try {
|
|
8468
|
-
const result = await client.get(
|
|
8720
|
+
const result = await client.get(path46);
|
|
8469
8721
|
if (result.totalAffected === 0) {
|
|
8470
8722
|
return {
|
|
8471
8723
|
summary: `${result.origin} has no downstream dependencies. Nothing else would break if it failed.`
|
|
@@ -8499,12 +8751,12 @@ function formatBlastEntry(n) {
|
|
|
8499
8751
|
}
|
|
8500
8752
|
async function runDependencies(client, input) {
|
|
8501
8753
|
const depth = input.depth ?? 3;
|
|
8502
|
-
const
|
|
8754
|
+
const path46 = projectPath(
|
|
8503
8755
|
input.project,
|
|
8504
8756
|
`/graph/dependencies/${encodeURIComponent(input.nodeId)}?depth=${depth}`
|
|
8505
8757
|
);
|
|
8506
8758
|
try {
|
|
8507
|
-
const result = await client.get(
|
|
8759
|
+
const result = await client.get(path46);
|
|
8508
8760
|
if (result.total === 0) {
|
|
8509
8761
|
return {
|
|
8510
8762
|
summary: depth === 1 ? `${input.nodeId} has no direct dependencies in the graph.` : `${input.nodeId} has no dependencies (BFS to depth ${depth}).`
|
|
@@ -8585,9 +8837,9 @@ function formatDuration(ms) {
|
|
|
8585
8837
|
return `${Math.round(h / 24)}d`;
|
|
8586
8838
|
}
|
|
8587
8839
|
async function runIncidents(client, input) {
|
|
8588
|
-
const
|
|
8840
|
+
const path46 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
|
|
8589
8841
|
try {
|
|
8590
|
-
const body = await client.get(
|
|
8842
|
+
const body = await client.get(path46);
|
|
8591
8843
|
const events = body.events;
|
|
8592
8844
|
if (events.length === 0) {
|
|
8593
8845
|
return {
|
|
@@ -8877,7 +9129,7 @@ async function resolveProjectEntry(opts) {
|
|
|
8877
9129
|
const cwd = opts.cwd ?? process.cwd();
|
|
8878
9130
|
const resolvedCwd = await normalizeProjectPath(cwd);
|
|
8879
9131
|
for (const entry2 of entries) {
|
|
8880
|
-
if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${
|
|
9132
|
+
if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${import_node_path44.default.sep}`)) {
|
|
8881
9133
|
return entry2;
|
|
8882
9134
|
}
|
|
8883
9135
|
}
|
|
@@ -9102,7 +9354,8 @@ function usage() {
|
|
|
9102
9354
|
console.log(" 0 success");
|
|
9103
9355
|
console.log(" 1 server error (4xx/5xx body printed to stderr)");
|
|
9104
9356
|
console.log(" 2 misuse (missing args, bad flags) \u2014 handled before any network call");
|
|
9105
|
-
console.log(" 3 daemon
|
|
9357
|
+
console.log(" 3 environmental \u2014 daemon unreachable, or one of ports 8080 / 4318 / 6328");
|
|
9358
|
+
console.log(" is held by another process when `neat <path>` tries to spawn the daemon");
|
|
9106
9359
|
console.log("");
|
|
9107
9360
|
console.log("environment:");
|
|
9108
9361
|
console.log(" NEAT_API_URL base URL for the core REST API (default http://localhost:8080)");
|
|
@@ -9236,14 +9489,14 @@ function assignFlag(out, field, value) {
|
|
|
9236
9489
|
out[field] = value;
|
|
9237
9490
|
}
|
|
9238
9491
|
function readPackageVersion() {
|
|
9239
|
-
const here = typeof __dirname !== "undefined" ? __dirname :
|
|
9492
|
+
const here = typeof __dirname !== "undefined" ? __dirname : import_node_path45.default.dirname((0, import_node_url3.fileURLToPath)(importMetaUrl));
|
|
9240
9493
|
const candidates = [
|
|
9241
|
-
|
|
9242
|
-
|
|
9494
|
+
import_node_path45.default.resolve(here, "../package.json"),
|
|
9495
|
+
import_node_path45.default.resolve(here, "../../package.json")
|
|
9243
9496
|
];
|
|
9244
9497
|
for (const candidate of candidates) {
|
|
9245
9498
|
try {
|
|
9246
|
-
const raw = (0,
|
|
9499
|
+
const raw = (0, import_node_fs29.readFileSync)(candidate, "utf8");
|
|
9247
9500
|
const parsed = JSON.parse(raw);
|
|
9248
9501
|
if (parsed.name === "@neat.is/core" && typeof parsed.version === "string") {
|
|
9249
9502
|
return parsed.version;
|
|
@@ -9307,7 +9560,7 @@ async function buildPatchSections(services, project) {
|
|
|
9307
9560
|
}
|
|
9308
9561
|
async function runInit(opts) {
|
|
9309
9562
|
const written = [];
|
|
9310
|
-
const stat = await
|
|
9563
|
+
const stat = await import_node_fs29.promises.stat(opts.scanPath).catch(() => null);
|
|
9311
9564
|
if (!stat || !stat.isDirectory()) {
|
|
9312
9565
|
console.error(`neat init: ${opts.scanPath} is not a directory`);
|
|
9313
9566
|
return { exitCode: 2, writtenFiles: written };
|
|
@@ -9316,13 +9569,13 @@ async function runInit(opts) {
|
|
|
9316
9569
|
printDiscoveryReport(opts, services);
|
|
9317
9570
|
const sections = opts.noInstall ? [] : await buildPatchSections(services, opts.project);
|
|
9318
9571
|
const patch = renderPatch(sections);
|
|
9319
|
-
const patchPath =
|
|
9572
|
+
const patchPath = import_node_path45.default.join(opts.scanPath, "neat.patch");
|
|
9320
9573
|
if (opts.dryRun) {
|
|
9321
|
-
await
|
|
9574
|
+
await import_node_fs29.promises.writeFile(patchPath, patch, "utf8");
|
|
9322
9575
|
written.push(patchPath);
|
|
9323
9576
|
console.log(`dry-run: patch written to ${patchPath}`);
|
|
9324
|
-
const gitignorePath =
|
|
9325
|
-
const gitignoreExists = await
|
|
9577
|
+
const gitignorePath = import_node_path45.default.join(opts.scanPath, ".gitignore");
|
|
9578
|
+
const gitignoreExists = await import_node_fs29.promises.stat(gitignorePath).then(() => true).catch(() => false);
|
|
9326
9579
|
const verb = gitignoreExists ? "append" : "create";
|
|
9327
9580
|
console.log(`dry-run: would ${verb} ${gitignorePath} (add neat-out/)`);
|
|
9328
9581
|
console.log("rerun without --dry-run to register and snapshot.");
|
|
@@ -9333,9 +9586,9 @@ async function runInit(opts) {
|
|
|
9333
9586
|
const graph = getGraph(graphKey);
|
|
9334
9587
|
const projectPaths = pathsForProject(
|
|
9335
9588
|
graphKey,
|
|
9336
|
-
|
|
9589
|
+
import_node_path45.default.join(opts.scanPath, "neat-out")
|
|
9337
9590
|
);
|
|
9338
|
-
const errorsPath =
|
|
9591
|
+
const errorsPath = import_node_path45.default.join(import_node_path45.default.dirname(opts.outPath), import_node_path45.default.basename(projectPaths.errorsPath));
|
|
9339
9592
|
const result = await extractFromDirectory(graph, opts.scanPath, { errorsPath });
|
|
9340
9593
|
await saveGraphToDisk(graph, opts.outPath);
|
|
9341
9594
|
written.push(opts.outPath);
|
|
@@ -9414,7 +9667,7 @@ async function runInit(opts) {
|
|
|
9414
9667
|
console.log("Run `npm install` (or your language equivalent) to refresh lockfiles.");
|
|
9415
9668
|
}
|
|
9416
9669
|
} else {
|
|
9417
|
-
await
|
|
9670
|
+
await import_node_fs29.promises.writeFile(patchPath, patch, "utf8");
|
|
9418
9671
|
written.push(patchPath);
|
|
9419
9672
|
}
|
|
9420
9673
|
}
|
|
@@ -9454,9 +9707,9 @@ var CLAUDE_SKILL_CONFIG = {
|
|
|
9454
9707
|
};
|
|
9455
9708
|
function claudeConfigPath() {
|
|
9456
9709
|
const override = process.env.NEAT_CLAUDE_CONFIG;
|
|
9457
|
-
if (override && override.length > 0) return
|
|
9710
|
+
if (override && override.length > 0) return import_node_path45.default.resolve(override);
|
|
9458
9711
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
9459
|
-
return
|
|
9712
|
+
return import_node_path45.default.join(home, ".claude.json");
|
|
9460
9713
|
}
|
|
9461
9714
|
async function runSkill(opts) {
|
|
9462
9715
|
const snippet2 = JSON.stringify(CLAUDE_SKILL_CONFIG, null, 2) + "\n";
|
|
@@ -9468,7 +9721,7 @@ async function runSkill(opts) {
|
|
|
9468
9721
|
const target = claudeConfigPath();
|
|
9469
9722
|
let existing = {};
|
|
9470
9723
|
try {
|
|
9471
|
-
existing = JSON.parse(await
|
|
9724
|
+
existing = JSON.parse(await import_node_fs29.promises.readFile(target, "utf8"));
|
|
9472
9725
|
} catch (err) {
|
|
9473
9726
|
if (err.code !== "ENOENT") {
|
|
9474
9727
|
console.error(`neat skill: failed to read ${target} \u2014 ${err.message}`);
|
|
@@ -9480,8 +9733,8 @@ async function runSkill(opts) {
|
|
|
9480
9733
|
...existing,
|
|
9481
9734
|
mcpServers: { ...mcp, neat: CLAUDE_SKILL_CONFIG.mcpServers.neat }
|
|
9482
9735
|
};
|
|
9483
|
-
await
|
|
9484
|
-
await
|
|
9736
|
+
await import_node_fs29.promises.mkdir(import_node_path45.default.dirname(target), { recursive: true });
|
|
9737
|
+
await import_node_fs29.promises.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
9485
9738
|
console.log(`neat skill: wrote mcpServers.neat to ${target}`);
|
|
9486
9739
|
console.log("restart Claude Code to pick up the new MCP server.");
|
|
9487
9740
|
return { exitCode: 0 };
|
|
@@ -9519,12 +9772,12 @@ async function main() {
|
|
|
9519
9772
|
console.error("neat init: --apply and --dry-run are mutually exclusive");
|
|
9520
9773
|
process.exit(2);
|
|
9521
9774
|
}
|
|
9522
|
-
const scanPath =
|
|
9775
|
+
const scanPath = import_node_path45.default.resolve(target);
|
|
9523
9776
|
const projectExplicit = parsed.project !== null;
|
|
9524
|
-
const projectName = projectExplicit ? project :
|
|
9777
|
+
const projectName = projectExplicit ? project : import_node_path45.default.basename(scanPath);
|
|
9525
9778
|
const projectKey = projectExplicit ? project : DEFAULT_PROJECT;
|
|
9526
|
-
const fallback = pathsForProject(projectKey,
|
|
9527
|
-
const outPath =
|
|
9779
|
+
const fallback = pathsForProject(projectKey, import_node_path45.default.join(scanPath, "neat-out")).snapshotPath;
|
|
9780
|
+
const outPath = import_node_path45.default.resolve(process.env.NEAT_OUT_PATH ?? fallback);
|
|
9528
9781
|
const result = await runInit({
|
|
9529
9782
|
scanPath,
|
|
9530
9783
|
outPath,
|
|
@@ -9545,21 +9798,21 @@ async function main() {
|
|
|
9545
9798
|
usage();
|
|
9546
9799
|
process.exit(2);
|
|
9547
9800
|
}
|
|
9548
|
-
const scanPath =
|
|
9549
|
-
const stat = await
|
|
9801
|
+
const scanPath = import_node_path45.default.resolve(target);
|
|
9802
|
+
const stat = await import_node_fs29.promises.stat(scanPath).catch(() => null);
|
|
9550
9803
|
if (!stat || !stat.isDirectory()) {
|
|
9551
9804
|
console.error(`neat watch: ${scanPath} is not a directory`);
|
|
9552
9805
|
process.exit(2);
|
|
9553
9806
|
}
|
|
9554
|
-
const projectPaths = pathsForProject(project,
|
|
9555
|
-
const outPath =
|
|
9556
|
-
const errorsPath =
|
|
9557
|
-
process.env.NEAT_ERRORS_PATH ??
|
|
9807
|
+
const projectPaths = pathsForProject(project, import_node_path45.default.join(scanPath, "neat-out"));
|
|
9808
|
+
const outPath = import_node_path45.default.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
|
|
9809
|
+
const errorsPath = import_node_path45.default.resolve(
|
|
9810
|
+
process.env.NEAT_ERRORS_PATH ?? import_node_path45.default.join(import_node_path45.default.dirname(outPath), import_node_path45.default.basename(projectPaths.errorsPath))
|
|
9558
9811
|
);
|
|
9559
|
-
const staleEventsPath =
|
|
9560
|
-
process.env.NEAT_STALE_EVENTS_PATH ??
|
|
9812
|
+
const staleEventsPath = import_node_path45.default.resolve(
|
|
9813
|
+
process.env.NEAT_STALE_EVENTS_PATH ?? import_node_path45.default.join(import_node_path45.default.dirname(outPath), import_node_path45.default.basename(projectPaths.staleEventsPath))
|
|
9561
9814
|
);
|
|
9562
|
-
const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ?
|
|
9815
|
+
const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? import_node_path45.default.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
|
|
9563
9816
|
const handle = await startWatch(getGraph(project), {
|
|
9564
9817
|
scanPath,
|
|
9565
9818
|
outPath,
|
|
@@ -9705,11 +9958,11 @@ async function main() {
|
|
|
9705
9958
|
process.exit(1);
|
|
9706
9959
|
}
|
|
9707
9960
|
async function tryOrchestrator(cmd, parsed) {
|
|
9708
|
-
const scanPath =
|
|
9709
|
-
const stat = await
|
|
9961
|
+
const scanPath = import_node_path45.default.resolve(cmd);
|
|
9962
|
+
const stat = await import_node_fs29.promises.stat(scanPath).catch(() => null);
|
|
9710
9963
|
if (!stat || !stat.isDirectory()) return null;
|
|
9711
9964
|
const projectExplicit = parsed.project !== null;
|
|
9712
|
-
const projectName = projectExplicit ? parsed.project :
|
|
9965
|
+
const projectName = projectExplicit ? parsed.project : import_node_path45.default.basename(scanPath);
|
|
9713
9966
|
const result = await runOrchestrator({
|
|
9714
9967
|
scanPath,
|
|
9715
9968
|
project: projectName,
|