@neat.is/core 0.4.16 → 0.4.18
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-6CO7C4IU.js → chunk-BGPWBRLU.js} +4 -2
- package/dist/{chunk-6CO7C4IU.js.map → chunk-BGPWBRLU.js.map} +1 -1
- package/dist/{chunk-VMLWUK7W.js → chunk-GXWBMJDJ.js} +210 -11
- package/dist/chunk-GXWBMJDJ.js.map +1 -0
- package/dist/{chunk-GHPHVXYM.js → chunk-MTXF77TN.js} +2 -2
- package/dist/{chunk-XS4CGNRO.js → chunk-T3X6XJPU.js} +403 -72
- package/dist/chunk-T3X6XJPU.js.map +1 -0
- package/dist/cli.cjs +964 -364
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +12 -2
- package/dist/cli.d.ts +12 -2
- package/dist/cli.js +331 -86
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +559 -150
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +51 -13
- package/dist/index.d.ts +51 -13
- package/dist/index.js +4 -4
- package/dist/neatd.cjs +726 -193
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +161 -37
- package/dist/neatd.js.map +1 -1
- package/dist/{otel-grpc-QAISVAY5.js → otel-grpc-I67ONCRM.js} +3 -3
- package/dist/server.cjs +271 -97
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +3 -3
- package/package.json +2 -2
- package/dist/chunk-VMLWUK7W.js.map +0 -1
- package/dist/chunk-XS4CGNRO.js.map +0 -1
- /package/dist/{chunk-GHPHVXYM.js.map → chunk-MTXF77TN.js.map} +0 -0
- /package/dist/{otel-grpc-QAISVAY5.js.map → otel-grpc-I67ONCRM.js.map} +0 -0
package/dist/cli.cjs
CHANGED
|
@@ -58,9 +58,9 @@ function mountBearerAuth(app, opts) {
|
|
|
58
58
|
const suffixes = [...DEFAULT_UNAUTH_SUFFIXES, ...opts.extraUnauthenticatedSuffixes ?? []];
|
|
59
59
|
const publicRead = opts.publicRead === true;
|
|
60
60
|
app.addHook("preHandler", (req, reply, done) => {
|
|
61
|
-
const
|
|
61
|
+
const path53 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
|
|
62
62
|
for (const suffix of suffixes) {
|
|
63
|
-
if (
|
|
63
|
+
if (path53 === suffix || path53.endsWith(suffix)) {
|
|
64
64
|
done();
|
|
65
65
|
return;
|
|
66
66
|
}
|
|
@@ -188,8 +188,8 @@ function reshapeGrpcRequest(req) {
|
|
|
188
188
|
};
|
|
189
189
|
}
|
|
190
190
|
function resolveProtoRoot() {
|
|
191
|
-
const here =
|
|
192
|
-
return
|
|
191
|
+
const here = import_node_path41.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
|
|
192
|
+
return import_node_path41.default.resolve(here, "..", "proto");
|
|
193
193
|
}
|
|
194
194
|
function loadTraceService() {
|
|
195
195
|
const protoRoot = resolveProtoRoot();
|
|
@@ -257,13 +257,13 @@ async function startOtelGrpcReceiver(opts) {
|
|
|
257
257
|
})
|
|
258
258
|
};
|
|
259
259
|
}
|
|
260
|
-
var import_node_url2,
|
|
260
|
+
var import_node_url2, import_node_path41, import_node_crypto2, grpc, protoLoader;
|
|
261
261
|
var init_otel_grpc = __esm({
|
|
262
262
|
"src/otel-grpc.ts"() {
|
|
263
263
|
"use strict";
|
|
264
264
|
init_cjs_shims();
|
|
265
265
|
import_node_url2 = require("url");
|
|
266
|
-
|
|
266
|
+
import_node_path41 = __toESM(require("path"), 1);
|
|
267
267
|
import_node_crypto2 = require("crypto");
|
|
268
268
|
grpc = __toESM(require("@grpc/grpc-js"), 1);
|
|
269
269
|
protoLoader = __toESM(require("@grpc/proto-loader"), 1);
|
|
@@ -373,10 +373,10 @@ function parseOtlpRequest(body) {
|
|
|
373
373
|
return out;
|
|
374
374
|
}
|
|
375
375
|
function loadProtoRoot() {
|
|
376
|
-
const here =
|
|
377
|
-
const protoRoot =
|
|
376
|
+
const here = import_node_path42.default.dirname((0, import_node_url3.fileURLToPath)(importMetaUrl));
|
|
377
|
+
const protoRoot = import_node_path42.default.resolve(here, "..", "proto");
|
|
378
378
|
const root = new import_protobufjs.default.Root();
|
|
379
|
-
root.resolvePath = (_origin, target) =>
|
|
379
|
+
root.resolvePath = (_origin, target) => import_node_path42.default.resolve(protoRoot, target);
|
|
380
380
|
root.loadSync(
|
|
381
381
|
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
|
|
382
382
|
{ keepCase: true }
|
|
@@ -474,8 +474,10 @@ async function buildOtelReceiver(opts) {
|
|
|
474
474
|
for (const s of spans) projectQueue.push({ project, span: s });
|
|
475
475
|
projectDrainPromise = projectDrainPromise.then(() => drainProject());
|
|
476
476
|
};
|
|
477
|
+
const offersProjectRouting = opts.onProjectSpan !== void 0;
|
|
477
478
|
const legacyEndpointWarned = /* @__PURE__ */ new Set();
|
|
478
479
|
function warnLegacyEndpoint(serviceName) {
|
|
480
|
+
if (!offersProjectRouting) return;
|
|
479
481
|
if (legacyEndpointWarned.has(serviceName)) return;
|
|
480
482
|
legacyEndpointWarned.add(serviceName);
|
|
481
483
|
console.warn(
|
|
@@ -572,12 +574,12 @@ async function buildOtelReceiver(opts) {
|
|
|
572
574
|
};
|
|
573
575
|
return decorated;
|
|
574
576
|
}
|
|
575
|
-
var
|
|
577
|
+
var import_node_path42, import_node_url3, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody;
|
|
576
578
|
var init_otel = __esm({
|
|
577
579
|
"src/otel.ts"() {
|
|
578
580
|
"use strict";
|
|
579
581
|
init_cjs_shims();
|
|
580
|
-
|
|
582
|
+
import_node_path42 = __toESM(require("path"), 1);
|
|
581
583
|
import_node_url3 = require("url");
|
|
582
584
|
import_fastify2 = __toESM(require("fastify"), 1);
|
|
583
585
|
import_protobufjs = __toESM(require("protobufjs"), 1);
|
|
@@ -595,6 +597,7 @@ var init_otel = __esm({
|
|
|
595
597
|
var cli_exports = {};
|
|
596
598
|
__export(cli_exports, {
|
|
597
599
|
CLAUDE_SKILL_CONFIG: () => CLAUDE_SKILL_CONFIG,
|
|
600
|
+
ProjectResolutionError: () => ProjectResolutionError,
|
|
598
601
|
QUERY_VERBS: () => QUERY_VERBS,
|
|
599
602
|
commandPrefix: () => commandPrefix,
|
|
600
603
|
isNpxInvocation: () => isNpxInvocation,
|
|
@@ -602,6 +605,7 @@ __export(cli_exports, {
|
|
|
602
605
|
parseArgs: () => parseArgs,
|
|
603
606
|
printBanner: () => printBanner,
|
|
604
607
|
readPackageVersion: () => readPackageVersion,
|
|
608
|
+
resolveProjectForVerb: () => resolveProjectForVerb,
|
|
605
609
|
runInit: () => runInit,
|
|
606
610
|
runQueryVerb: () => runQueryVerb,
|
|
607
611
|
runSkill: () => runSkill,
|
|
@@ -609,8 +613,8 @@ __export(cli_exports, {
|
|
|
609
613
|
});
|
|
610
614
|
module.exports = __toCommonJS(cli_exports);
|
|
611
615
|
init_cjs_shims();
|
|
612
|
-
var
|
|
613
|
-
var
|
|
616
|
+
var import_node_path52 = __toESM(require("path"), 1);
|
|
617
|
+
var import_node_fs34 = require("fs");
|
|
614
618
|
|
|
615
619
|
// src/banner.ts
|
|
616
620
|
init_cjs_shims();
|
|
@@ -1175,19 +1179,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
1175
1179
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
1176
1180
|
let best = { path: [start], edges: [] };
|
|
1177
1181
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
1178
|
-
function step(node,
|
|
1179
|
-
if (
|
|
1180
|
-
best = { path: [...
|
|
1182
|
+
function step(node, path53, edges) {
|
|
1183
|
+
if (path53.length > best.path.length) {
|
|
1184
|
+
best = { path: [...path53], edges: [...edges] };
|
|
1181
1185
|
}
|
|
1182
|
-
if (
|
|
1186
|
+
if (path53.length - 1 >= maxDepth) return;
|
|
1183
1187
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
1184
1188
|
for (const [srcId, edge] of incoming) {
|
|
1185
1189
|
if (visited.has(srcId)) continue;
|
|
1186
1190
|
visited.add(srcId);
|
|
1187
|
-
|
|
1191
|
+
path53.push(srcId);
|
|
1188
1192
|
edges.push(edge);
|
|
1189
|
-
step(srcId,
|
|
1190
|
-
|
|
1193
|
+
step(srcId, path53, edges);
|
|
1194
|
+
path53.pop();
|
|
1191
1195
|
edges.pop();
|
|
1192
1196
|
visited.delete(srcId);
|
|
1193
1197
|
}
|
|
@@ -1727,6 +1731,41 @@ function thresholdForEdgeType(edgeType, overrides) {
|
|
|
1727
1731
|
const map = overrides ?? loadStaleThresholdsFromEnv();
|
|
1728
1732
|
return map[edgeType] ?? FALLBACK_STALE_THRESHOLD_MS;
|
|
1729
1733
|
}
|
|
1734
|
+
var DEFAULT_INCIDENT_THRESHOLDS = {
|
|
1735
|
+
threshold: 5,
|
|
1736
|
+
windowMs: 6e4
|
|
1737
|
+
};
|
|
1738
|
+
function loadIncidentThresholdsFromEnv() {
|
|
1739
|
+
const raw = process.env.NEAT_INCIDENT_THRESHOLDS;
|
|
1740
|
+
if (!raw) return DEFAULT_INCIDENT_THRESHOLDS;
|
|
1741
|
+
try {
|
|
1742
|
+
const overrides = JSON.parse(raw);
|
|
1743
|
+
const merged = { ...DEFAULT_INCIDENT_THRESHOLDS };
|
|
1744
|
+
if (typeof overrides.threshold === "number" && Number.isFinite(overrides.threshold) && overrides.threshold >= 1) {
|
|
1745
|
+
merged.threshold = Math.floor(overrides.threshold);
|
|
1746
|
+
}
|
|
1747
|
+
if (typeof overrides.windowMs === "number" && Number.isFinite(overrides.windowMs) && overrides.windowMs >= 0) {
|
|
1748
|
+
merged.windowMs = overrides.windowMs;
|
|
1749
|
+
}
|
|
1750
|
+
return merged;
|
|
1751
|
+
} catch (err) {
|
|
1752
|
+
console.warn(
|
|
1753
|
+
`[neat] NEAT_INCIDENT_THRESHOLDS could not be parsed (${err.message}); using defaults`
|
|
1754
|
+
);
|
|
1755
|
+
return DEFAULT_INCIDENT_THRESHOLDS;
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
function httpResponseStatus(span) {
|
|
1759
|
+
for (const key of ["http.response.status_code", "http.status_code"]) {
|
|
1760
|
+
const v = span.attributes[key];
|
|
1761
|
+
if (typeof v === "number" && Number.isFinite(v)) return v;
|
|
1762
|
+
if (typeof v === "string") {
|
|
1763
|
+
const n = Number(v);
|
|
1764
|
+
if (Number.isFinite(n)) return n;
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
return void 0;
|
|
1768
|
+
}
|
|
1730
1769
|
function nowIso(ctx) {
|
|
1731
1770
|
return new Date(ctx.now ? ctx.now() : Date.now()).toISOString();
|
|
1732
1771
|
}
|
|
@@ -2140,6 +2179,71 @@ function makeErrorSpanWriter(errorsPath) {
|
|
|
2140
2179
|
await import_node_fs4.promises.appendFile(errorsPath, JSON.stringify(ev) + "\n", "utf8");
|
|
2141
2180
|
};
|
|
2142
2181
|
}
|
|
2182
|
+
async function recordFailingResponseIncident(ctx, span, affectedNode, timestamp, statusCode, count, firstTimestamp) {
|
|
2183
|
+
const attrs = sanitizeAttributes(span.attributes);
|
|
2184
|
+
const first = firstTimestamp ?? timestamp;
|
|
2185
|
+
const peer = pickAddress(span);
|
|
2186
|
+
const message = count > 1 ? `${count} consecutive HTTP ${statusCode} responses` + (peer ? ` to ${peer}` : "") : `HTTP ${statusCode} response` + (peer ? ` from ${peer}` : "");
|
|
2187
|
+
const ev = {
|
|
2188
|
+
id: `${span.traceId}:${span.spanId}`,
|
|
2189
|
+
timestamp,
|
|
2190
|
+
service: span.service,
|
|
2191
|
+
traceId: span.traceId,
|
|
2192
|
+
spanId: span.spanId,
|
|
2193
|
+
errorType: "http-failure",
|
|
2194
|
+
errorMessage: message,
|
|
2195
|
+
...Object.keys(attrs).length > 0 ? { attributes: attrs } : {},
|
|
2196
|
+
affectedNode,
|
|
2197
|
+
httpStatusCode: statusCode,
|
|
2198
|
+
incidentCount: count,
|
|
2199
|
+
firstTimestamp: first,
|
|
2200
|
+
lastTimestamp: timestamp
|
|
2201
|
+
};
|
|
2202
|
+
await appendErrorEvent(ctx, ev);
|
|
2203
|
+
}
|
|
2204
|
+
async function advance4xxBurst(ctx, span, affectedNode, ts, nowMs, status2) {
|
|
2205
|
+
const { threshold, windowMs } = loadIncidentThresholdsFromEnv();
|
|
2206
|
+
if (!ctx.burstState) ctx.burstState = /* @__PURE__ */ new Map();
|
|
2207
|
+
const peer = pickAddress(span) ?? span.spanId;
|
|
2208
|
+
const key = `${span.service}->${peer}`;
|
|
2209
|
+
const existing = ctx.burstState.get(key);
|
|
2210
|
+
let state;
|
|
2211
|
+
if (existing && nowMs - existing.lastMs <= windowMs) {
|
|
2212
|
+
existing.count += 1;
|
|
2213
|
+
existing.lastTs = ts;
|
|
2214
|
+
existing.lastMs = nowMs;
|
|
2215
|
+
existing.codes.set(status2, (existing.codes.get(status2) ?? 0) + 1);
|
|
2216
|
+
state = existing;
|
|
2217
|
+
} else {
|
|
2218
|
+
state = {
|
|
2219
|
+
count: 1,
|
|
2220
|
+
firstTs: ts,
|
|
2221
|
+
lastTs: ts,
|
|
2222
|
+
lastMs: nowMs,
|
|
2223
|
+
codes: /* @__PURE__ */ new Map([[status2, 1]])
|
|
2224
|
+
};
|
|
2225
|
+
ctx.burstState.set(key, state);
|
|
2226
|
+
}
|
|
2227
|
+
if (state.count < threshold) return;
|
|
2228
|
+
let dominant = status2;
|
|
2229
|
+
let max = 0;
|
|
2230
|
+
for (const [code, n] of state.codes) {
|
|
2231
|
+
if (n > max) {
|
|
2232
|
+
max = n;
|
|
2233
|
+
dominant = code;
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
await recordFailingResponseIncident(
|
|
2237
|
+
ctx,
|
|
2238
|
+
span,
|
|
2239
|
+
affectedNode,
|
|
2240
|
+
state.lastTs,
|
|
2241
|
+
dominant,
|
|
2242
|
+
state.count,
|
|
2243
|
+
state.firstTs
|
|
2244
|
+
);
|
|
2245
|
+
ctx.burstState.delete(key);
|
|
2246
|
+
}
|
|
2143
2247
|
async function handleSpan(ctx, span) {
|
|
2144
2248
|
const ts = span.startTimeIso ?? nowIso(ctx);
|
|
2145
2249
|
const nowMs = ctx.now ? ctx.now() : Date.now();
|
|
@@ -2238,6 +2342,14 @@ async function handleSpan(ctx, span) {
|
|
|
2238
2342
|
await appendErrorEvent(ctx, ev);
|
|
2239
2343
|
}
|
|
2240
2344
|
}
|
|
2345
|
+
if (span.statusCode !== 2) {
|
|
2346
|
+
const status2 = httpResponseStatus(span);
|
|
2347
|
+
if (status2 !== void 0 && status2 >= 500) {
|
|
2348
|
+
await recordFailingResponseIncident(ctx, span, sourceId, ts, status2, 1);
|
|
2349
|
+
} else if (status2 !== void 0 && status2 >= 400 && spanMintsObservedEdge(span.kind)) {
|
|
2350
|
+
await advance4xxBurst(ctx, span, sourceId, ts, nowMs, status2);
|
|
2351
|
+
}
|
|
2352
|
+
}
|
|
2241
2353
|
void affectedNode;
|
|
2242
2354
|
if (ctx.onPolicyTrigger) await ctx.onPolicyTrigger(ctx.graph);
|
|
2243
2355
|
}
|
|
@@ -4269,7 +4381,7 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
4269
4381
|
|
|
4270
4382
|
// src/extract/calls/index.ts
|
|
4271
4383
|
init_cjs_shims();
|
|
4272
|
-
var
|
|
4384
|
+
var import_types17 = require("@neat.is/types");
|
|
4273
4385
|
|
|
4274
4386
|
// src/extract/calls/http.ts
|
|
4275
4387
|
init_cjs_shims();
|
|
@@ -4622,15 +4734,75 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
4622
4734
|
return out;
|
|
4623
4735
|
}
|
|
4624
4736
|
|
|
4737
|
+
// src/extract/calls/supabase.ts
|
|
4738
|
+
init_cjs_shims();
|
|
4739
|
+
var import_node_path28 = __toESM(require("path"), 1);
|
|
4740
|
+
var import_types16 = require("@neat.is/types");
|
|
4741
|
+
var SUPABASE_JS_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/supabase-js['"`]/;
|
|
4742
|
+
var SUPABASE_SSR_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/ssr['"`]/;
|
|
4743
|
+
var SUPABASE_CLIENT_RE = /\b(createClient|createServerClient|createBrowserClient)\s*\(\s*(?:['"`]([^'"`]*)['"`])?/g;
|
|
4744
|
+
function hostFromLiteral(literal) {
|
|
4745
|
+
if (!literal) return null;
|
|
4746
|
+
const m = /^https?:\/\/([^/:'"`\s]+)/.exec(literal.trim());
|
|
4747
|
+
if (!m) return null;
|
|
4748
|
+
const host = m[1];
|
|
4749
|
+
if (!/\.supabase\.(co|in)$/i.test(host)) return null;
|
|
4750
|
+
return host;
|
|
4751
|
+
}
|
|
4752
|
+
function readImports2(content) {
|
|
4753
|
+
return {
|
|
4754
|
+
hasSupabaseJs: SUPABASE_JS_IMPORT_RE.test(content),
|
|
4755
|
+
hasSupabaseSsr: SUPABASE_SSR_IMPORT_RE.test(content)
|
|
4756
|
+
};
|
|
4757
|
+
}
|
|
4758
|
+
function constructorMatchesImport(name, ctx) {
|
|
4759
|
+
if (name === "createClient") return ctx.hasSupabaseJs;
|
|
4760
|
+
return ctx.hasSupabaseSsr;
|
|
4761
|
+
}
|
|
4762
|
+
function supabaseEndpointsFromFile(file, serviceDir) {
|
|
4763
|
+
const ctx = readImports2(file.content);
|
|
4764
|
+
if (!ctx.hasSupabaseJs && !ctx.hasSupabaseSsr) return [];
|
|
4765
|
+
const out = [];
|
|
4766
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4767
|
+
SUPABASE_CLIENT_RE.lastIndex = 0;
|
|
4768
|
+
let m;
|
|
4769
|
+
while ((m = SUPABASE_CLIENT_RE.exec(file.content)) !== null) {
|
|
4770
|
+
const ctor = m[1];
|
|
4771
|
+
if (!constructorMatchesImport(ctor, ctx)) continue;
|
|
4772
|
+
const host = hostFromLiteral(m[2]);
|
|
4773
|
+
const name = host ?? "env";
|
|
4774
|
+
if (seen.has(name)) continue;
|
|
4775
|
+
seen.add(name);
|
|
4776
|
+
const line = lineOf(file.content, m[0]);
|
|
4777
|
+
out.push({
|
|
4778
|
+
infraId: (0, import_types16.infraId)("supabase", name),
|
|
4779
|
+
name,
|
|
4780
|
+
kind: "supabase",
|
|
4781
|
+
edgeType: "CALLS",
|
|
4782
|
+
// `createClient(...)` from @supabase/supabase-js (or createServerClient /
|
|
4783
|
+
// createBrowserClient from @supabase/ssr) with the import in scope — a
|
|
4784
|
+
// framework-aware recognizer matched the SDK shape. Verified-call-site
|
|
4785
|
+
// tier (ADR-066), the same grade aws.ts / grpc.ts emit at.
|
|
4786
|
+
confidenceKind: "verified-call-site",
|
|
4787
|
+
evidence: {
|
|
4788
|
+
file: import_node_path28.default.relative(serviceDir, file.path),
|
|
4789
|
+
line,
|
|
4790
|
+
snippet: snippet(file.content, line)
|
|
4791
|
+
}
|
|
4792
|
+
});
|
|
4793
|
+
}
|
|
4794
|
+
return out;
|
|
4795
|
+
}
|
|
4796
|
+
|
|
4625
4797
|
// src/extract/calls/index.ts
|
|
4626
4798
|
function edgeTypeFromEndpoint(ep) {
|
|
4627
4799
|
switch (ep.edgeType) {
|
|
4628
4800
|
case "PUBLISHES_TO":
|
|
4629
|
-
return
|
|
4801
|
+
return import_types17.EdgeType.PUBLISHES_TO;
|
|
4630
4802
|
case "CONSUMES_FROM":
|
|
4631
|
-
return
|
|
4803
|
+
return import_types17.EdgeType.CONSUMES_FROM;
|
|
4632
4804
|
default:
|
|
4633
|
-
return
|
|
4805
|
+
return import_types17.EdgeType.CALLS;
|
|
4634
4806
|
}
|
|
4635
4807
|
}
|
|
4636
4808
|
function isAwsKind(kind) {
|
|
@@ -4650,6 +4822,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
4650
4822
|
endpoints.push(...redisEndpointsFromFile(maskedFile, service.dir));
|
|
4651
4823
|
endpoints.push(...awsEndpointsFromFile(maskedFile, service.dir));
|
|
4652
4824
|
endpoints.push(...grpcEndpointsFromFile(maskedFile, service.dir));
|
|
4825
|
+
endpoints.push(...supabaseEndpointsFromFile(maskedFile, service.dir));
|
|
4653
4826
|
}
|
|
4654
4827
|
if (endpoints.length === 0) continue;
|
|
4655
4828
|
const seenEdges = /* @__PURE__ */ new Set();
|
|
@@ -4657,7 +4830,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
4657
4830
|
if (!graph.hasNode(ep.infraId)) {
|
|
4658
4831
|
const node = {
|
|
4659
4832
|
id: ep.infraId,
|
|
4660
|
-
type:
|
|
4833
|
+
type: import_types17.NodeType.InfraNode,
|
|
4661
4834
|
name: ep.name,
|
|
4662
4835
|
// #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
|
|
4663
4836
|
// aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
|
|
@@ -4669,7 +4842,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
4669
4842
|
nodesAdded++;
|
|
4670
4843
|
}
|
|
4671
4844
|
const edgeType = edgeTypeFromEndpoint(ep);
|
|
4672
|
-
const confidence = (0,
|
|
4845
|
+
const confidence = (0, import_types17.confidenceForExtracted)(ep.confidenceKind);
|
|
4673
4846
|
const relFile = toPosix2(ep.evidence.file);
|
|
4674
4847
|
const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
4675
4848
|
graph,
|
|
@@ -4679,7 +4852,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
4679
4852
|
);
|
|
4680
4853
|
nodesAdded += n;
|
|
4681
4854
|
edgesAdded += e;
|
|
4682
|
-
if (!(0,
|
|
4855
|
+
if (!(0, import_types17.passesExtractedFloor)(confidence)) {
|
|
4683
4856
|
noteExtractedDropped({
|
|
4684
4857
|
source: fileNodeId,
|
|
4685
4858
|
target: ep.infraId,
|
|
@@ -4699,7 +4872,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
4699
4872
|
source: fileNodeId,
|
|
4700
4873
|
target: ep.infraId,
|
|
4701
4874
|
type: edgeType,
|
|
4702
|
-
provenance:
|
|
4875
|
+
provenance: import_types17.Provenance.EXTRACTED,
|
|
4703
4876
|
confidence,
|
|
4704
4877
|
evidence: ep.evidence
|
|
4705
4878
|
};
|
|
@@ -4724,16 +4897,16 @@ init_cjs_shims();
|
|
|
4724
4897
|
|
|
4725
4898
|
// src/extract/infra/docker-compose.ts
|
|
4726
4899
|
init_cjs_shims();
|
|
4727
|
-
var
|
|
4728
|
-
var
|
|
4900
|
+
var import_node_path29 = __toESM(require("path"), 1);
|
|
4901
|
+
var import_types19 = require("@neat.is/types");
|
|
4729
4902
|
|
|
4730
4903
|
// src/extract/infra/shared.ts
|
|
4731
4904
|
init_cjs_shims();
|
|
4732
|
-
var
|
|
4905
|
+
var import_types18 = require("@neat.is/types");
|
|
4733
4906
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
4734
4907
|
return {
|
|
4735
|
-
id: (0,
|
|
4736
|
-
type:
|
|
4908
|
+
id: (0, import_types18.infraId)(kind, name),
|
|
4909
|
+
type: import_types18.NodeType.InfraNode,
|
|
4737
4910
|
name,
|
|
4738
4911
|
provider,
|
|
4739
4912
|
kind,
|
|
@@ -4762,7 +4935,7 @@ function dependsOnList(value) {
|
|
|
4762
4935
|
}
|
|
4763
4936
|
function serviceNameToServiceNode(name, services) {
|
|
4764
4937
|
for (const s of services) {
|
|
4765
|
-
if (s.node.name === name ||
|
|
4938
|
+
if (s.node.name === name || import_node_path29.default.basename(s.dir) === name) return s.node.id;
|
|
4766
4939
|
}
|
|
4767
4940
|
return null;
|
|
4768
4941
|
}
|
|
@@ -4771,7 +4944,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
4771
4944
|
let edgesAdded = 0;
|
|
4772
4945
|
let composePath = null;
|
|
4773
4946
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
4774
|
-
const abs =
|
|
4947
|
+
const abs = import_node_path29.default.join(scanPath, name);
|
|
4775
4948
|
if (await exists(abs)) {
|
|
4776
4949
|
composePath = abs;
|
|
4777
4950
|
break;
|
|
@@ -4784,13 +4957,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
4784
4957
|
} catch (err) {
|
|
4785
4958
|
recordExtractionError(
|
|
4786
4959
|
"infra docker-compose",
|
|
4787
|
-
|
|
4960
|
+
import_node_path29.default.relative(scanPath, composePath),
|
|
4788
4961
|
err
|
|
4789
4962
|
);
|
|
4790
4963
|
return { nodesAdded, edgesAdded };
|
|
4791
4964
|
}
|
|
4792
4965
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
4793
|
-
const evidenceFile =
|
|
4966
|
+
const evidenceFile = import_node_path29.default.relative(scanPath, composePath).split(import_node_path29.default.sep).join("/");
|
|
4794
4967
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
4795
4968
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
4796
4969
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -4812,15 +4985,15 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
4812
4985
|
for (const dep of dependsOnList(svc.depends_on)) {
|
|
4813
4986
|
const targetId = composeNameToNodeId.get(dep);
|
|
4814
4987
|
if (!targetId) continue;
|
|
4815
|
-
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId,
|
|
4988
|
+
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId, import_types19.EdgeType.DEPENDS_ON);
|
|
4816
4989
|
if (graph.hasEdge(edgeId)) continue;
|
|
4817
4990
|
const edge = {
|
|
4818
4991
|
id: edgeId,
|
|
4819
4992
|
source: sourceId,
|
|
4820
4993
|
target: targetId,
|
|
4821
|
-
type:
|
|
4822
|
-
provenance:
|
|
4823
|
-
confidence: (0,
|
|
4994
|
+
type: import_types19.EdgeType.DEPENDS_ON,
|
|
4995
|
+
provenance: import_types19.Provenance.EXTRACTED,
|
|
4996
|
+
confidence: (0, import_types19.confidenceForExtracted)("structural"),
|
|
4824
4997
|
evidence: { file: evidenceFile }
|
|
4825
4998
|
};
|
|
4826
4999
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -4832,9 +5005,9 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
4832
5005
|
|
|
4833
5006
|
// src/extract/infra/dockerfile.ts
|
|
4834
5007
|
init_cjs_shims();
|
|
4835
|
-
var
|
|
5008
|
+
var import_node_path30 = __toESM(require("path"), 1);
|
|
4836
5009
|
var import_node_fs16 = require("fs");
|
|
4837
|
-
var
|
|
5010
|
+
var import_types20 = require("@neat.is/types");
|
|
4838
5011
|
function runtimeImage(content) {
|
|
4839
5012
|
const lines = content.split("\n");
|
|
4840
5013
|
let last = null;
|
|
@@ -4853,7 +5026,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4853
5026
|
let nodesAdded = 0;
|
|
4854
5027
|
let edgesAdded = 0;
|
|
4855
5028
|
for (const service of services) {
|
|
4856
|
-
const dockerfilePath =
|
|
5029
|
+
const dockerfilePath = import_node_path30.default.join(service.dir, "Dockerfile");
|
|
4857
5030
|
if (!await exists(dockerfilePath)) continue;
|
|
4858
5031
|
let content;
|
|
4859
5032
|
try {
|
|
@@ -4861,7 +5034,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4861
5034
|
} catch (err) {
|
|
4862
5035
|
recordExtractionError(
|
|
4863
5036
|
"infra dockerfile",
|
|
4864
|
-
|
|
5037
|
+
import_node_path30.default.relative(scanPath, dockerfilePath),
|
|
4865
5038
|
err
|
|
4866
5039
|
);
|
|
4867
5040
|
continue;
|
|
@@ -4873,7 +5046,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4873
5046
|
graph.addNode(node.id, node);
|
|
4874
5047
|
nodesAdded++;
|
|
4875
5048
|
}
|
|
4876
|
-
const relDockerfile = toPosix2(
|
|
5049
|
+
const relDockerfile = toPosix2(import_node_path30.default.relative(service.dir, dockerfilePath));
|
|
4877
5050
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
4878
5051
|
graph,
|
|
4879
5052
|
service.pkg.name,
|
|
@@ -4882,17 +5055,17 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4882
5055
|
);
|
|
4883
5056
|
nodesAdded += fn;
|
|
4884
5057
|
edgesAdded += fe;
|
|
4885
|
-
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id,
|
|
5058
|
+
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id, import_types20.EdgeType.RUNS_ON);
|
|
4886
5059
|
if (!graph.hasEdge(edgeId)) {
|
|
4887
5060
|
const edge = {
|
|
4888
5061
|
id: edgeId,
|
|
4889
5062
|
source: fileNodeId,
|
|
4890
5063
|
target: node.id,
|
|
4891
|
-
type:
|
|
4892
|
-
provenance:
|
|
4893
|
-
confidence: (0,
|
|
5064
|
+
type: import_types20.EdgeType.RUNS_ON,
|
|
5065
|
+
provenance: import_types20.Provenance.EXTRACTED,
|
|
5066
|
+
confidence: (0, import_types20.confidenceForExtracted)("structural"),
|
|
4894
5067
|
evidence: {
|
|
4895
|
-
file: toPosix2(
|
|
5068
|
+
file: toPosix2(import_node_path30.default.relative(scanPath, dockerfilePath))
|
|
4896
5069
|
}
|
|
4897
5070
|
};
|
|
4898
5071
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -4905,7 +5078,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4905
5078
|
// src/extract/infra/terraform.ts
|
|
4906
5079
|
init_cjs_shims();
|
|
4907
5080
|
var import_node_fs17 = require("fs");
|
|
4908
|
-
var
|
|
5081
|
+
var import_node_path31 = __toESM(require("path"), 1);
|
|
4909
5082
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
4910
5083
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
4911
5084
|
if (depth > max) return [];
|
|
@@ -4914,11 +5087,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
|
|
|
4914
5087
|
for (const entry2 of entries) {
|
|
4915
5088
|
if (entry2.isDirectory()) {
|
|
4916
5089
|
if (IGNORED_DIRS.has(entry2.name) || entry2.name === ".terraform") continue;
|
|
4917
|
-
const child =
|
|
5090
|
+
const child = import_node_path31.default.join(start, entry2.name);
|
|
4918
5091
|
if (await isPythonVenvDir(child)) continue;
|
|
4919
5092
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
4920
5093
|
} else if (entry2.isFile() && entry2.name.endsWith(".tf")) {
|
|
4921
|
-
out.push(
|
|
5094
|
+
out.push(import_node_path31.default.join(start, entry2.name));
|
|
4922
5095
|
}
|
|
4923
5096
|
}
|
|
4924
5097
|
return out;
|
|
@@ -4946,7 +5119,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
4946
5119
|
// src/extract/infra/k8s.ts
|
|
4947
5120
|
init_cjs_shims();
|
|
4948
5121
|
var import_node_fs18 = require("fs");
|
|
4949
|
-
var
|
|
5122
|
+
var import_node_path32 = __toESM(require("path"), 1);
|
|
4950
5123
|
var import_yaml3 = require("yaml");
|
|
4951
5124
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
4952
5125
|
Service: "k8s-service",
|
|
@@ -4964,11 +5137,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
|
4964
5137
|
for (const entry2 of entries) {
|
|
4965
5138
|
if (entry2.isDirectory()) {
|
|
4966
5139
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
4967
|
-
const child =
|
|
5140
|
+
const child = import_node_path32.default.join(start, entry2.name);
|
|
4968
5141
|
if (await isPythonVenvDir(child)) continue;
|
|
4969
5142
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
4970
|
-
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
4971
|
-
out.push(
|
|
5143
|
+
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path32.default.extname(entry2.name))) {
|
|
5144
|
+
out.push(import_node_path32.default.join(start, entry2.name));
|
|
4972
5145
|
}
|
|
4973
5146
|
}
|
|
4974
5147
|
return out;
|
|
@@ -5012,17 +5185,17 @@ async function addInfra(graph, scanPath, services) {
|
|
|
5012
5185
|
}
|
|
5013
5186
|
|
|
5014
5187
|
// src/extract/index.ts
|
|
5015
|
-
var
|
|
5188
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
5016
5189
|
|
|
5017
5190
|
// src/extract/retire.ts
|
|
5018
5191
|
init_cjs_shims();
|
|
5019
5192
|
var import_node_fs19 = require("fs");
|
|
5020
|
-
var
|
|
5021
|
-
var
|
|
5193
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
5194
|
+
var import_types21 = require("@neat.is/types");
|
|
5022
5195
|
function dropOrphanedFileNodes(graph) {
|
|
5023
5196
|
const orphans = [];
|
|
5024
5197
|
graph.forEachNode((id, attrs) => {
|
|
5025
|
-
if (attrs.type !==
|
|
5198
|
+
if (attrs.type !== import_types21.NodeType.FileNode) return;
|
|
5026
5199
|
if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
|
|
5027
5200
|
orphans.push(id);
|
|
5028
5201
|
}
|
|
@@ -5035,7 +5208,7 @@ function retireEdgesByFile(graph, file) {
|
|
|
5035
5208
|
const toDrop = [];
|
|
5036
5209
|
graph.forEachEdge((id, attrs) => {
|
|
5037
5210
|
const edge = attrs;
|
|
5038
|
-
if (edge.provenance !==
|
|
5211
|
+
if (edge.provenance !== import_types21.Provenance.EXTRACTED) return;
|
|
5039
5212
|
if (!edge.evidence?.file) return;
|
|
5040
5213
|
if (edge.evidence.file === normalized) toDrop.push(id);
|
|
5041
5214
|
});
|
|
@@ -5048,14 +5221,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
5048
5221
|
const bases = [scanPath, ...serviceDirs];
|
|
5049
5222
|
graph.forEachEdge((id, attrs) => {
|
|
5050
5223
|
const edge = attrs;
|
|
5051
|
-
if (edge.provenance !==
|
|
5224
|
+
if (edge.provenance !== import_types21.Provenance.EXTRACTED) return;
|
|
5052
5225
|
const evidenceFile = edge.evidence?.file;
|
|
5053
5226
|
if (!evidenceFile) return;
|
|
5054
|
-
if (
|
|
5227
|
+
if (import_node_path33.default.isAbsolute(evidenceFile)) {
|
|
5055
5228
|
if (!(0, import_node_fs19.existsSync)(evidenceFile)) toDrop.push(id);
|
|
5056
5229
|
return;
|
|
5057
5230
|
}
|
|
5058
|
-
const found = bases.some((base) => (0, import_node_fs19.existsSync)(
|
|
5231
|
+
const found = bases.some((base) => (0, import_node_fs19.existsSync)(import_node_path33.default.join(base, evidenceFile)));
|
|
5059
5232
|
if (!found) toDrop.push(id);
|
|
5060
5233
|
});
|
|
5061
5234
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -5095,7 +5268,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
5095
5268
|
}
|
|
5096
5269
|
const droppedEntries = drainDroppedExtracted();
|
|
5097
5270
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
5098
|
-
const rejectedPath =
|
|
5271
|
+
const rejectedPath = import_node_path34.default.join(import_node_path34.default.dirname(opts.errorsPath), "rejected.ndjson");
|
|
5099
5272
|
try {
|
|
5100
5273
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
5101
5274
|
} catch (err) {
|
|
@@ -5129,7 +5302,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
5129
5302
|
|
|
5130
5303
|
// src/divergences.ts
|
|
5131
5304
|
init_cjs_shims();
|
|
5132
|
-
var
|
|
5305
|
+
var import_types22 = require("@neat.is/types");
|
|
5133
5306
|
function bucketKey(source, target, type) {
|
|
5134
5307
|
return `${type}|${source}|${target}`;
|
|
5135
5308
|
}
|
|
@@ -5137,22 +5310,22 @@ function bucketEdges(graph) {
|
|
|
5137
5310
|
const buckets = /* @__PURE__ */ new Map();
|
|
5138
5311
|
graph.forEachEdge((id, attrs) => {
|
|
5139
5312
|
const e = attrs;
|
|
5140
|
-
const parsed = (0,
|
|
5313
|
+
const parsed = (0, import_types22.parseEdgeId)(id);
|
|
5141
5314
|
const provenance = parsed?.provenance ?? e.provenance;
|
|
5142
5315
|
const key = bucketKey(e.source, e.target, e.type);
|
|
5143
5316
|
const cur = buckets.get(key) ?? { source: e.source, target: e.target, type: e.type };
|
|
5144
5317
|
switch (provenance) {
|
|
5145
|
-
case
|
|
5318
|
+
case import_types22.Provenance.EXTRACTED:
|
|
5146
5319
|
cur.extracted = e;
|
|
5147
5320
|
break;
|
|
5148
|
-
case
|
|
5321
|
+
case import_types22.Provenance.OBSERVED:
|
|
5149
5322
|
cur.observed = e;
|
|
5150
5323
|
break;
|
|
5151
|
-
case
|
|
5324
|
+
case import_types22.Provenance.INFERRED:
|
|
5152
5325
|
cur.inferred = e;
|
|
5153
5326
|
break;
|
|
5154
5327
|
default:
|
|
5155
|
-
if (e.provenance ===
|
|
5328
|
+
if (e.provenance === import_types22.Provenance.STALE) cur.stale = e;
|
|
5156
5329
|
}
|
|
5157
5330
|
buckets.set(key, cur);
|
|
5158
5331
|
});
|
|
@@ -5161,7 +5334,7 @@ function bucketEdges(graph) {
|
|
|
5161
5334
|
function nodeIsFrontier(graph, nodeId) {
|
|
5162
5335
|
if (!graph.hasNode(nodeId)) return false;
|
|
5163
5336
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
5164
|
-
return attrs.type ===
|
|
5337
|
+
return attrs.type === import_types22.NodeType.FrontierNode;
|
|
5165
5338
|
}
|
|
5166
5339
|
function clampConfidence(n) {
|
|
5167
5340
|
if (!Number.isFinite(n)) return 0;
|
|
@@ -5182,7 +5355,7 @@ function gradedConfidence(edge) {
|
|
|
5182
5355
|
}
|
|
5183
5356
|
function detectMissingDivergences(graph, bucket) {
|
|
5184
5357
|
const out = [];
|
|
5185
|
-
if (bucket.type ===
|
|
5358
|
+
if (bucket.type === import_types22.EdgeType.CONTAINS) return out;
|
|
5186
5359
|
if (bucket.extracted && !bucket.observed) {
|
|
5187
5360
|
if (!nodeIsFrontier(graph, bucket.target)) {
|
|
5188
5361
|
out.push({
|
|
@@ -5223,7 +5396,7 @@ function declaredHostFor(svc) {
|
|
|
5223
5396
|
function hasExtractedConfiguredBy(graph, svcId) {
|
|
5224
5397
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
5225
5398
|
const e = graph.getEdgeAttributes(edgeId);
|
|
5226
|
-
if (e.type ===
|
|
5399
|
+
if (e.type === import_types22.EdgeType.CONFIGURED_BY && e.provenance === import_types22.Provenance.EXTRACTED) {
|
|
5227
5400
|
return true;
|
|
5228
5401
|
}
|
|
5229
5402
|
}
|
|
@@ -5236,10 +5409,10 @@ function detectHostMismatch(graph, svcId, svc) {
|
|
|
5236
5409
|
const out = [];
|
|
5237
5410
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
5238
5411
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
5239
|
-
if (edge.type !==
|
|
5240
|
-
if (edge.provenance !==
|
|
5412
|
+
if (edge.type !== import_types22.EdgeType.CONNECTS_TO) continue;
|
|
5413
|
+
if (edge.provenance !== import_types22.Provenance.OBSERVED) continue;
|
|
5241
5414
|
const target = graph.getNodeAttributes(edge.target);
|
|
5242
|
-
if (target.type !==
|
|
5415
|
+
if (target.type !== import_types22.NodeType.DatabaseNode) continue;
|
|
5243
5416
|
const observedHost = target.host?.trim();
|
|
5244
5417
|
if (!observedHost) continue;
|
|
5245
5418
|
if (observedHost === declaredHost) continue;
|
|
@@ -5261,10 +5434,10 @@ function detectCompatDivergences(graph, svcId, svc) {
|
|
|
5261
5434
|
const deps = svc.dependencies ?? {};
|
|
5262
5435
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
5263
5436
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
5264
|
-
if (edge.type !==
|
|
5265
|
-
if (edge.provenance !==
|
|
5437
|
+
if (edge.type !== import_types22.EdgeType.CONNECTS_TO) continue;
|
|
5438
|
+
if (edge.provenance !== import_types22.Provenance.OBSERVED) continue;
|
|
5266
5439
|
const target = graph.getNodeAttributes(edge.target);
|
|
5267
|
-
if (target.type !==
|
|
5440
|
+
if (target.type !== import_types22.NodeType.DatabaseNode) continue;
|
|
5268
5441
|
for (const pair of compatPairs()) {
|
|
5269
5442
|
if (pair.engine !== target.engine) continue;
|
|
5270
5443
|
const declared = deps[pair.driver];
|
|
@@ -5325,7 +5498,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
5325
5498
|
}
|
|
5326
5499
|
graph.forEachNode((nodeId, attrs) => {
|
|
5327
5500
|
const n = attrs;
|
|
5328
|
-
if (n.type !==
|
|
5501
|
+
if (n.type !== import_types22.NodeType.ServiceNode) return;
|
|
5329
5502
|
const svc = n;
|
|
5330
5503
|
for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
|
|
5331
5504
|
for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
|
|
@@ -5358,7 +5531,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
5358
5531
|
if (a.source !== b.source) return a.source.localeCompare(b.source);
|
|
5359
5532
|
return a.target.localeCompare(b.target);
|
|
5360
5533
|
});
|
|
5361
|
-
return
|
|
5534
|
+
return import_types22.DivergenceResultSchema.parse({
|
|
5362
5535
|
divergences: filtered,
|
|
5363
5536
|
totalAffected: filtered.length,
|
|
5364
5537
|
computedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -5368,8 +5541,8 @@ function computeDivergences(graph, opts = {}) {
|
|
|
5368
5541
|
// src/persist.ts
|
|
5369
5542
|
init_cjs_shims();
|
|
5370
5543
|
var import_node_fs20 = require("fs");
|
|
5371
|
-
var
|
|
5372
|
-
var
|
|
5544
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
5545
|
+
var import_types23 = require("@neat.is/types");
|
|
5373
5546
|
var SCHEMA_VERSION = 4;
|
|
5374
5547
|
function migrateV1ToV2(payload) {
|
|
5375
5548
|
const nodes = payload.graph.nodes;
|
|
@@ -5391,12 +5564,12 @@ function migrateV2ToV3(payload) {
|
|
|
5391
5564
|
for (const edge of edges) {
|
|
5392
5565
|
const attrs = edge.attributes;
|
|
5393
5566
|
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
5394
|
-
attrs.provenance =
|
|
5567
|
+
attrs.provenance = import_types23.Provenance.OBSERVED;
|
|
5395
5568
|
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
5396
5569
|
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
5397
5570
|
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
5398
5571
|
if (type && source && target) {
|
|
5399
|
-
const newId = (0,
|
|
5572
|
+
const newId = (0, import_types23.observedEdgeId)(source, target, type);
|
|
5400
5573
|
attrs.id = newId;
|
|
5401
5574
|
if (edge.key) edge.key = newId;
|
|
5402
5575
|
}
|
|
@@ -5405,7 +5578,7 @@ function migrateV2ToV3(payload) {
|
|
|
5405
5578
|
return { ...payload, schemaVersion: 3 };
|
|
5406
5579
|
}
|
|
5407
5580
|
async function ensureDir(filePath) {
|
|
5408
|
-
await import_node_fs20.promises.mkdir(
|
|
5581
|
+
await import_node_fs20.promises.mkdir(import_node_path35.default.dirname(filePath), { recursive: true });
|
|
5409
5582
|
}
|
|
5410
5583
|
async function saveGraphToDisk(graph, outPath) {
|
|
5411
5584
|
await ensureDir(outPath);
|
|
@@ -5481,7 +5654,7 @@ function startPersistLoop(graph, outPath, intervalMs = 6e4) {
|
|
|
5481
5654
|
// src/gitignore.ts
|
|
5482
5655
|
init_cjs_shims();
|
|
5483
5656
|
var import_node_fs21 = require("fs");
|
|
5484
|
-
var
|
|
5657
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
5485
5658
|
var NEAT_OUT_LINE = "neat-out/";
|
|
5486
5659
|
var NEAT_HEADER = "# NEAT \u2014 machine-local snapshots and events";
|
|
5487
5660
|
function isNeatOutLine(line) {
|
|
@@ -5489,7 +5662,7 @@ function isNeatOutLine(line) {
|
|
|
5489
5662
|
return trimmed === "neat-out/" || trimmed === "neat-out";
|
|
5490
5663
|
}
|
|
5491
5664
|
async function ensureNeatOutIgnored(projectDir) {
|
|
5492
|
-
const file =
|
|
5665
|
+
const file = import_node_path36.default.join(projectDir, ".gitignore");
|
|
5493
5666
|
let existing = null;
|
|
5494
5667
|
try {
|
|
5495
5668
|
existing = await import_node_fs21.promises.readFile(file, "utf8");
|
|
@@ -5516,7 +5689,7 @@ ${NEAT_OUT_LINE}
|
|
|
5516
5689
|
|
|
5517
5690
|
// src/summary.ts
|
|
5518
5691
|
init_cjs_shims();
|
|
5519
|
-
var
|
|
5692
|
+
var import_types24 = require("@neat.is/types");
|
|
5520
5693
|
function renderOtelEnvBlock() {
|
|
5521
5694
|
return [
|
|
5522
5695
|
"for prod OTel routing, set these in your deploy platform's env:",
|
|
@@ -5526,19 +5699,19 @@ function renderOtelEnvBlock() {
|
|
|
5526
5699
|
}
|
|
5527
5700
|
function findIncompatServices(nodes) {
|
|
5528
5701
|
return nodes.filter(
|
|
5529
|
-
(n) => n.type ===
|
|
5702
|
+
(n) => n.type === import_types24.NodeType.ServiceNode && Array.isArray(n.incompatibilities) && (n.incompatibilities ?? []).length > 0
|
|
5530
5703
|
);
|
|
5531
5704
|
}
|
|
5532
5705
|
function servicesWithoutObserved(nodes, edges) {
|
|
5533
5706
|
const seen = /* @__PURE__ */ new Set();
|
|
5534
5707
|
for (const e of edges) {
|
|
5535
|
-
if (e.provenance ===
|
|
5708
|
+
if (e.provenance === import_types24.Provenance.OBSERVED) {
|
|
5536
5709
|
seen.add(e.source);
|
|
5537
5710
|
seen.add(e.target);
|
|
5538
5711
|
}
|
|
5539
5712
|
}
|
|
5540
5713
|
return nodes.filter(
|
|
5541
|
-
(n) => n.type ===
|
|
5714
|
+
(n) => n.type === import_types24.NodeType.ServiceNode && !seen.has(n.id)
|
|
5542
5715
|
);
|
|
5543
5716
|
}
|
|
5544
5717
|
function formatDivergence(d) {
|
|
@@ -5613,26 +5786,26 @@ function formatIncompat(inc) {
|
|
|
5613
5786
|
// src/watch.ts
|
|
5614
5787
|
init_cjs_shims();
|
|
5615
5788
|
var import_node_fs27 = __toESM(require("fs"), 1);
|
|
5616
|
-
var
|
|
5789
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
5617
5790
|
var import_chokidar = __toESM(require("chokidar"), 1);
|
|
5618
5791
|
|
|
5619
5792
|
// src/api.ts
|
|
5620
5793
|
init_cjs_shims();
|
|
5621
5794
|
var import_fastify = __toESM(require("fastify"), 1);
|
|
5622
5795
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
5623
|
-
var
|
|
5796
|
+
var import_types26 = require("@neat.is/types");
|
|
5624
5797
|
|
|
5625
5798
|
// src/extend/index.ts
|
|
5626
5799
|
init_cjs_shims();
|
|
5627
5800
|
var import_node_fs23 = require("fs");
|
|
5628
|
-
var
|
|
5801
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
5629
5802
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
5630
5803
|
var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
|
|
5631
5804
|
|
|
5632
5805
|
// src/installers/package-manager.ts
|
|
5633
5806
|
init_cjs_shims();
|
|
5634
5807
|
var import_node_fs22 = require("fs");
|
|
5635
|
-
var
|
|
5808
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
5636
5809
|
var import_node_child_process = require("child_process");
|
|
5637
5810
|
var LOCKFILE_PRIORITY = [
|
|
5638
5811
|
{ lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
|
|
@@ -5654,22 +5827,22 @@ async function exists2(p) {
|
|
|
5654
5827
|
}
|
|
5655
5828
|
}
|
|
5656
5829
|
async function detectPackageManager(serviceDir) {
|
|
5657
|
-
let dir =
|
|
5830
|
+
let dir = import_node_path37.default.resolve(serviceDir);
|
|
5658
5831
|
const stops = /* @__PURE__ */ new Set();
|
|
5659
5832
|
for (let i = 0; i < 64; i++) {
|
|
5660
5833
|
if (stops.has(dir)) break;
|
|
5661
5834
|
stops.add(dir);
|
|
5662
5835
|
for (const candidate of LOCKFILE_PRIORITY) {
|
|
5663
|
-
const lockPath =
|
|
5836
|
+
const lockPath = import_node_path37.default.join(dir, candidate.lockfile);
|
|
5664
5837
|
if (await exists2(lockPath)) {
|
|
5665
5838
|
return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
|
|
5666
5839
|
}
|
|
5667
5840
|
}
|
|
5668
|
-
const parent =
|
|
5841
|
+
const parent = import_node_path37.default.dirname(dir);
|
|
5669
5842
|
if (parent === dir) break;
|
|
5670
5843
|
dir = parent;
|
|
5671
5844
|
}
|
|
5672
|
-
return { pm: "npm", cwd:
|
|
5845
|
+
return { pm: "npm", cwd: import_node_path37.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
|
|
5673
5846
|
}
|
|
5674
5847
|
async function runPackageManagerInstall(cmd) {
|
|
5675
5848
|
return new Promise((resolve) => {
|
|
@@ -5718,7 +5891,7 @@ async function fileExists2(p) {
|
|
|
5718
5891
|
}
|
|
5719
5892
|
}
|
|
5720
5893
|
async function readPackageJson(scanPath) {
|
|
5721
|
-
const pkgPath =
|
|
5894
|
+
const pkgPath = import_node_path38.default.join(scanPath, "package.json");
|
|
5722
5895
|
const raw = await import_node_fs23.promises.readFile(pkgPath, "utf8");
|
|
5723
5896
|
return JSON.parse(raw);
|
|
5724
5897
|
}
|
|
@@ -5729,11 +5902,11 @@ async function findHookFiles(scanPath) {
|
|
|
5729
5902
|
).sort();
|
|
5730
5903
|
}
|
|
5731
5904
|
function extendLogPath() {
|
|
5732
|
-
return process.env.NEAT_EXTEND_LOG ??
|
|
5905
|
+
return process.env.NEAT_EXTEND_LOG ?? import_node_path38.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
|
|
5733
5906
|
}
|
|
5734
5907
|
async function appendExtendLog(entry2) {
|
|
5735
5908
|
const logPath = extendLogPath();
|
|
5736
|
-
await import_node_fs23.promises.mkdir(
|
|
5909
|
+
await import_node_fs23.promises.mkdir(import_node_path38.default.dirname(logPath), { recursive: true });
|
|
5737
5910
|
await import_node_fs23.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
|
|
5738
5911
|
}
|
|
5739
5912
|
function splicedContent(fileContent, snippet2) {
|
|
@@ -5792,7 +5965,7 @@ function lookupInstrumentation(library, installedVersion) {
|
|
|
5792
5965
|
}
|
|
5793
5966
|
async function describeProjectInstrumentation(ctx) {
|
|
5794
5967
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
5795
|
-
const envNeat = await fileExists2(
|
|
5968
|
+
const envNeat = await fileExists2(import_node_path38.default.join(ctx.scanPath, ".env.neat"));
|
|
5796
5969
|
const registryInstrPackages = new Set(
|
|
5797
5970
|
(0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
|
|
5798
5971
|
);
|
|
@@ -5814,16 +5987,16 @@ async function applyExtension(ctx, args, options) {
|
|
|
5814
5987
|
);
|
|
5815
5988
|
}
|
|
5816
5989
|
for (const file of hookFiles) {
|
|
5817
|
-
const content = await import_node_fs23.promises.readFile(
|
|
5990
|
+
const content = await import_node_fs23.promises.readFile(import_node_path38.default.join(ctx.scanPath, file), "utf8");
|
|
5818
5991
|
if (content.includes(args.registration_snippet)) {
|
|
5819
5992
|
return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
|
|
5820
5993
|
}
|
|
5821
5994
|
}
|
|
5822
5995
|
const primaryFile = hookFiles[0];
|
|
5823
|
-
const primaryPath =
|
|
5996
|
+
const primaryPath = import_node_path38.default.join(ctx.scanPath, primaryFile);
|
|
5824
5997
|
const filesTouched = [];
|
|
5825
5998
|
const depsAdded = [];
|
|
5826
|
-
const pkgPath =
|
|
5999
|
+
const pkgPath = import_node_path38.default.join(ctx.scanPath, "package.json");
|
|
5827
6000
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
5828
6001
|
if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
|
|
5829
6002
|
pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
|
|
@@ -5869,7 +6042,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
5869
6042
|
};
|
|
5870
6043
|
}
|
|
5871
6044
|
for (const file of hookFiles) {
|
|
5872
|
-
const content = await import_node_fs23.promises.readFile(
|
|
6045
|
+
const content = await import_node_fs23.promises.readFile(import_node_path38.default.join(ctx.scanPath, file), "utf8");
|
|
5873
6046
|
if (content.includes(args.registration_snippet)) {
|
|
5874
6047
|
return {
|
|
5875
6048
|
library: args.library,
|
|
@@ -5891,7 +6064,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
5891
6064
|
depsToAdd.push(`${args.instrumentation_package}@${args.version}`);
|
|
5892
6065
|
filesTouched.push("package.json");
|
|
5893
6066
|
}
|
|
5894
|
-
const hookContent = await import_node_fs23.promises.readFile(
|
|
6067
|
+
const hookContent = await import_node_fs23.promises.readFile(import_node_path38.default.join(ctx.scanPath, primaryFile), "utf8");
|
|
5895
6068
|
const patched = splicedContent(hookContent, args.registration_snippet);
|
|
5896
6069
|
if (patched) {
|
|
5897
6070
|
filesTouched.push(primaryFile);
|
|
@@ -5912,7 +6085,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
5912
6085
|
if (!match) {
|
|
5913
6086
|
return { undone: false, message: "no apply found for library" };
|
|
5914
6087
|
}
|
|
5915
|
-
const pkgPath =
|
|
6088
|
+
const pkgPath = import_node_path38.default.join(ctx.scanPath, "package.json");
|
|
5916
6089
|
if (await fileExists2(pkgPath)) {
|
|
5917
6090
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
5918
6091
|
if (pkg.dependencies?.[match.instrumentation_package]) {
|
|
@@ -5923,7 +6096,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
5923
6096
|
}
|
|
5924
6097
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
5925
6098
|
for (const file of hookFiles) {
|
|
5926
|
-
const filePath =
|
|
6099
|
+
const filePath = import_node_path38.default.join(ctx.scanPath, file);
|
|
5927
6100
|
const content = await import_node_fs23.promises.readFile(filePath, "utf8");
|
|
5928
6101
|
if (content.includes(match.registration_snippet)) {
|
|
5929
6102
|
const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
|
|
@@ -6016,23 +6189,23 @@ function canonicalJson(value) {
|
|
|
6016
6189
|
|
|
6017
6190
|
// src/projects.ts
|
|
6018
6191
|
init_cjs_shims();
|
|
6019
|
-
var
|
|
6192
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
6020
6193
|
function pathsForProject(project, baseDir) {
|
|
6021
6194
|
if (project === DEFAULT_PROJECT) {
|
|
6022
6195
|
return {
|
|
6023
|
-
snapshotPath:
|
|
6024
|
-
errorsPath:
|
|
6025
|
-
staleEventsPath:
|
|
6026
|
-
embeddingsCachePath:
|
|
6027
|
-
policyViolationsPath:
|
|
6196
|
+
snapshotPath: import_node_path39.default.join(baseDir, "graph.json"),
|
|
6197
|
+
errorsPath: import_node_path39.default.join(baseDir, "errors.ndjson"),
|
|
6198
|
+
staleEventsPath: import_node_path39.default.join(baseDir, "stale-events.ndjson"),
|
|
6199
|
+
embeddingsCachePath: import_node_path39.default.join(baseDir, "embeddings.json"),
|
|
6200
|
+
policyViolationsPath: import_node_path39.default.join(baseDir, "policy-violations.ndjson")
|
|
6028
6201
|
};
|
|
6029
6202
|
}
|
|
6030
6203
|
return {
|
|
6031
|
-
snapshotPath:
|
|
6032
|
-
errorsPath:
|
|
6033
|
-
staleEventsPath:
|
|
6034
|
-
embeddingsCachePath:
|
|
6035
|
-
policyViolationsPath:
|
|
6204
|
+
snapshotPath: import_node_path39.default.join(baseDir, `${project}.json`),
|
|
6205
|
+
errorsPath: import_node_path39.default.join(baseDir, `errors.${project}.ndjson`),
|
|
6206
|
+
staleEventsPath: import_node_path39.default.join(baseDir, `stale-events.${project}.ndjson`),
|
|
6207
|
+
embeddingsCachePath: import_node_path39.default.join(baseDir, `embeddings.${project}.json`),
|
|
6208
|
+
policyViolationsPath: import_node_path39.default.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
6036
6209
|
};
|
|
6037
6210
|
}
|
|
6038
6211
|
var Projects = class {
|
|
@@ -6070,23 +6243,128 @@ var Projects = class {
|
|
|
6070
6243
|
init_cjs_shims();
|
|
6071
6244
|
var import_node_fs25 = require("fs");
|
|
6072
6245
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
6073
|
-
var
|
|
6074
|
-
var
|
|
6246
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
6247
|
+
var import_types25 = require("@neat.is/types");
|
|
6075
6248
|
var LOCK_TIMEOUT_MS = 5e3;
|
|
6076
6249
|
var LOCK_RETRY_MS = 50;
|
|
6077
6250
|
function neatHome() {
|
|
6078
6251
|
const override = process.env.NEAT_HOME;
|
|
6079
|
-
if (override && override.length > 0) return
|
|
6080
|
-
return
|
|
6252
|
+
if (override && override.length > 0) return import_node_path40.default.resolve(override);
|
|
6253
|
+
return import_node_path40.default.join(import_node_os3.default.homedir(), ".neat");
|
|
6081
6254
|
}
|
|
6082
6255
|
function registryPath() {
|
|
6083
|
-
return
|
|
6256
|
+
return import_node_path40.default.join(neatHome(), "projects.json");
|
|
6084
6257
|
}
|
|
6085
6258
|
function registryLockPath() {
|
|
6086
|
-
return
|
|
6259
|
+
return import_node_path40.default.join(neatHome(), "projects.json.lock");
|
|
6087
6260
|
}
|
|
6088
6261
|
function daemonPidPath() {
|
|
6089
|
-
return
|
|
6262
|
+
return import_node_path40.default.join(neatHome(), "neatd.pid");
|
|
6263
|
+
}
|
|
6264
|
+
function daemonsDir() {
|
|
6265
|
+
return import_node_path40.default.join(neatHome(), "daemons");
|
|
6266
|
+
}
|
|
6267
|
+
function isFiniteInt(v) {
|
|
6268
|
+
return typeof v === "number" && Number.isFinite(v);
|
|
6269
|
+
}
|
|
6270
|
+
function parseDaemonRecord(raw) {
|
|
6271
|
+
let obj;
|
|
6272
|
+
try {
|
|
6273
|
+
obj = JSON.parse(raw);
|
|
6274
|
+
} catch {
|
|
6275
|
+
return void 0;
|
|
6276
|
+
}
|
|
6277
|
+
if (typeof obj !== "object" || obj === null) return void 0;
|
|
6278
|
+
const r = obj;
|
|
6279
|
+
const ports = r.ports;
|
|
6280
|
+
if (typeof r.project !== "string" || typeof r.projectPath !== "string" || !isFiniteInt(r.pid) || r.status !== "running" && r.status !== "stopped" || typeof r.startedAt !== "string" || typeof r.neatVersion !== "string" || !ports || !isFiniteInt(ports.rest) || !isFiniteInt(ports.otlp) || !isFiniteInt(ports.web)) {
|
|
6281
|
+
return void 0;
|
|
6282
|
+
}
|
|
6283
|
+
return {
|
|
6284
|
+
project: r.project,
|
|
6285
|
+
projectPath: r.projectPath,
|
|
6286
|
+
pid: r.pid,
|
|
6287
|
+
status: r.status,
|
|
6288
|
+
ports: { rest: ports.rest, otlp: ports.otlp, web: ports.web },
|
|
6289
|
+
startedAt: r.startedAt,
|
|
6290
|
+
neatVersion: r.neatVersion
|
|
6291
|
+
};
|
|
6292
|
+
}
|
|
6293
|
+
var defaultDiscoveryProbe = { isPidAlive: isPidAliveDefault };
|
|
6294
|
+
async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
6295
|
+
const dir = daemonsDir();
|
|
6296
|
+
let names;
|
|
6297
|
+
try {
|
|
6298
|
+
names = await import_node_fs25.promises.readdir(dir);
|
|
6299
|
+
} catch (err) {
|
|
6300
|
+
if (err.code === "ENOENT") return [];
|
|
6301
|
+
throw err;
|
|
6302
|
+
}
|
|
6303
|
+
const out = [];
|
|
6304
|
+
for (const name of names) {
|
|
6305
|
+
if (!name.endsWith(".json")) continue;
|
|
6306
|
+
const file = import_node_path40.default.join(dir, name);
|
|
6307
|
+
let raw;
|
|
6308
|
+
try {
|
|
6309
|
+
raw = await import_node_fs25.promises.readFile(file, "utf8");
|
|
6310
|
+
} catch {
|
|
6311
|
+
continue;
|
|
6312
|
+
}
|
|
6313
|
+
const record = parseDaemonRecord(raw);
|
|
6314
|
+
if (!record) continue;
|
|
6315
|
+
const live = record.status === "running" && probe.isPidAlive(record.pid);
|
|
6316
|
+
out.push({ record, live, source: file });
|
|
6317
|
+
}
|
|
6318
|
+
out.sort((a, b) => a.record.project.localeCompare(b.record.project));
|
|
6319
|
+
return out;
|
|
6320
|
+
}
|
|
6321
|
+
async function removeDaemonRecord(source) {
|
|
6322
|
+
await import_node_fs25.promises.unlink(source).catch(() => {
|
|
6323
|
+
});
|
|
6324
|
+
}
|
|
6325
|
+
async function listMachineProjects(probe = defaultDiscoveryProbe) {
|
|
6326
|
+
const discovered = await discoverDaemons(probe);
|
|
6327
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
6328
|
+
for (const d of discovered) {
|
|
6329
|
+
const key = await normalizeProjectPath(d.record.projectPath);
|
|
6330
|
+
byPath.set(key, {
|
|
6331
|
+
project: d.record.project,
|
|
6332
|
+
projectPath: d.record.projectPath,
|
|
6333
|
+
state: d.live ? "running" : "stopped",
|
|
6334
|
+
ports: d.record.ports,
|
|
6335
|
+
pid: d.record.pid
|
|
6336
|
+
});
|
|
6337
|
+
}
|
|
6338
|
+
let legacy = [];
|
|
6339
|
+
try {
|
|
6340
|
+
legacy = (await readRegistry()).projects;
|
|
6341
|
+
} catch {
|
|
6342
|
+
legacy = [];
|
|
6343
|
+
}
|
|
6344
|
+
for (const entry2 of legacy) {
|
|
6345
|
+
const key = await normalizeProjectPath(entry2.path);
|
|
6346
|
+
if (byPath.has(key)) continue;
|
|
6347
|
+
byPath.set(key, {
|
|
6348
|
+
project: entry2.name,
|
|
6349
|
+
projectPath: entry2.path,
|
|
6350
|
+
state: "registered",
|
|
6351
|
+
registryStatus: entry2.status
|
|
6352
|
+
});
|
|
6353
|
+
}
|
|
6354
|
+
return [...byPath.values()].sort((a, b) => a.project.localeCompare(b.project));
|
|
6355
|
+
}
|
|
6356
|
+
async function findDaemonByProject(name, probe = defaultDiscoveryProbe) {
|
|
6357
|
+
const discovered = await discoverDaemons(probe);
|
|
6358
|
+
return discovered.find((d) => d.record.project === name);
|
|
6359
|
+
}
|
|
6360
|
+
function signalDaemonStop(pid) {
|
|
6361
|
+
try {
|
|
6362
|
+
process.kill(pid, "SIGTERM");
|
|
6363
|
+
return true;
|
|
6364
|
+
} catch (err) {
|
|
6365
|
+
if (err.code === "ESRCH") return false;
|
|
6366
|
+
throw err;
|
|
6367
|
+
}
|
|
6090
6368
|
}
|
|
6091
6369
|
function isPidAliveDefault(pid) {
|
|
6092
6370
|
try {
|
|
@@ -6146,7 +6424,7 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
|
|
|
6146
6424
|
}
|
|
6147
6425
|
}
|
|
6148
6426
|
async function normalizeProjectPath(input) {
|
|
6149
|
-
const resolved =
|
|
6427
|
+
const resolved = import_node_path40.default.resolve(input);
|
|
6150
6428
|
try {
|
|
6151
6429
|
return await import_node_fs25.promises.realpath(resolved);
|
|
6152
6430
|
} catch {
|
|
@@ -6154,7 +6432,7 @@ async function normalizeProjectPath(input) {
|
|
|
6154
6432
|
}
|
|
6155
6433
|
}
|
|
6156
6434
|
async function writeAtomically(target, contents) {
|
|
6157
|
-
await import_node_fs25.promises.mkdir(
|
|
6435
|
+
await import_node_fs25.promises.mkdir(import_node_path40.default.dirname(target), { recursive: true });
|
|
6158
6436
|
const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
6159
6437
|
const fd = await import_node_fs25.promises.open(tmp, "w");
|
|
6160
6438
|
try {
|
|
@@ -6167,7 +6445,7 @@ async function writeAtomically(target, contents) {
|
|
|
6167
6445
|
}
|
|
6168
6446
|
async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
|
|
6169
6447
|
const deadline = Date.now() + timeoutMs;
|
|
6170
|
-
await import_node_fs25.promises.mkdir(
|
|
6448
|
+
await import_node_fs25.promises.mkdir(import_node_path40.default.dirname(lockPath), { recursive: true });
|
|
6171
6449
|
let probedHolder = false;
|
|
6172
6450
|
while (true) {
|
|
6173
6451
|
try {
|
|
@@ -6220,10 +6498,10 @@ async function readRegistry() {
|
|
|
6220
6498
|
throw err;
|
|
6221
6499
|
}
|
|
6222
6500
|
const parsed = JSON.parse(raw);
|
|
6223
|
-
return
|
|
6501
|
+
return import_types25.RegistryFileSchema.parse(parsed);
|
|
6224
6502
|
}
|
|
6225
6503
|
async function writeRegistry(reg) {
|
|
6226
|
-
const validated =
|
|
6504
|
+
const validated = import_types25.RegistryFileSchema.parse(reg);
|
|
6227
6505
|
await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
|
|
6228
6506
|
}
|
|
6229
6507
|
var ProjectNameCollisionError = class extends Error {
|
|
@@ -6294,6 +6572,58 @@ async function removeProject(name) {
|
|
|
6294
6572
|
return removed;
|
|
6295
6573
|
});
|
|
6296
6574
|
}
|
|
6575
|
+
var DAY_MS2 = 24 * 60 * 60 * 1e3;
|
|
6576
|
+
var DEFAULT_PRUNE_TTL_MS = 7 * DAY_MS2;
|
|
6577
|
+
function pruneTtlMs() {
|
|
6578
|
+
const raw = process.env.NEAT_REGISTRY_PRUNE_TTL_MS;
|
|
6579
|
+
if (!raw) return DEFAULT_PRUNE_TTL_MS;
|
|
6580
|
+
const n = Number.parseInt(raw, 10);
|
|
6581
|
+
if (Number.isFinite(n) && n >= 0) return n;
|
|
6582
|
+
console.warn(
|
|
6583
|
+
`[neat] NEAT_REGISTRY_PRUNE_TTL_MS could not be parsed (${raw}); using default ${DEFAULT_PRUNE_TTL_MS}ms`
|
|
6584
|
+
);
|
|
6585
|
+
return DEFAULT_PRUNE_TTL_MS;
|
|
6586
|
+
}
|
|
6587
|
+
async function statPathStatus(p) {
|
|
6588
|
+
try {
|
|
6589
|
+
const stat = await import_node_fs25.promises.stat(p);
|
|
6590
|
+
return stat.isDirectory() ? "present" : "unknown";
|
|
6591
|
+
} catch (err) {
|
|
6592
|
+
return err.code === "ENOENT" ? "gone" : "unknown";
|
|
6593
|
+
}
|
|
6594
|
+
}
|
|
6595
|
+
async function pruneRegistry(opts = {}) {
|
|
6596
|
+
const ttlMs = opts.ttlMs ?? pruneTtlMs();
|
|
6597
|
+
const statPath = opts.statPath ?? statPathStatus;
|
|
6598
|
+
const now = opts.now ?? Date.now;
|
|
6599
|
+
return withLock(async () => {
|
|
6600
|
+
const reg = await readRegistry();
|
|
6601
|
+
const removed = [];
|
|
6602
|
+
const kept = [];
|
|
6603
|
+
for (const entry2 of reg.projects) {
|
|
6604
|
+
const status2 = await statPath(entry2.path);
|
|
6605
|
+
if (status2 !== "gone") {
|
|
6606
|
+
kept.push(entry2);
|
|
6607
|
+
continue;
|
|
6608
|
+
}
|
|
6609
|
+
if (ttlMs <= 0) {
|
|
6610
|
+
removed.push(entry2);
|
|
6611
|
+
continue;
|
|
6612
|
+
}
|
|
6613
|
+
const lastSeen = Date.parse(entry2.lastSeenAt ?? entry2.registeredAt);
|
|
6614
|
+
const age = now() - (Number.isFinite(lastSeen) ? lastSeen : 0);
|
|
6615
|
+
if (age > ttlMs) {
|
|
6616
|
+
removed.push(entry2);
|
|
6617
|
+
} else {
|
|
6618
|
+
kept.push(entry2);
|
|
6619
|
+
}
|
|
6620
|
+
}
|
|
6621
|
+
if (removed.length === 0) return [];
|
|
6622
|
+
reg.projects = kept;
|
|
6623
|
+
await writeRegistry(reg);
|
|
6624
|
+
return removed;
|
|
6625
|
+
});
|
|
6626
|
+
}
|
|
6297
6627
|
|
|
6298
6628
|
// src/streaming.ts
|
|
6299
6629
|
init_cjs_shims();
|
|
@@ -6307,6 +6637,7 @@ function handleSse(req, reply, opts) {
|
|
|
6307
6637
|
reply.raw.setHeader("Connection", "keep-alive");
|
|
6308
6638
|
reply.raw.setHeader("X-Accel-Buffering", "no");
|
|
6309
6639
|
reply.raw.flushHeaders?.();
|
|
6640
|
+
reply.raw.write(":open\n\n");
|
|
6310
6641
|
let pending = 0;
|
|
6311
6642
|
let dropped = false;
|
|
6312
6643
|
const closeConnection = () => {
|
|
@@ -6486,11 +6817,11 @@ function registerRoutes(scope, ctx) {
|
|
|
6486
6817
|
const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
6487
6818
|
const parsed = [];
|
|
6488
6819
|
for (const c of candidates) {
|
|
6489
|
-
const r =
|
|
6820
|
+
const r = import_types26.DivergenceTypeSchema.safeParse(c);
|
|
6490
6821
|
if (!r.success) {
|
|
6491
6822
|
return reply.code(400).send({
|
|
6492
6823
|
error: `unknown divergence type "${c}"`,
|
|
6493
|
-
allowed:
|
|
6824
|
+
allowed: import_types26.DivergenceTypeSchema.options
|
|
6494
6825
|
});
|
|
6495
6826
|
}
|
|
6496
6827
|
parsed.push(r.data);
|
|
@@ -6703,7 +7034,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6703
7034
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
6704
7035
|
let violations = await log.readAll();
|
|
6705
7036
|
if (req.query.severity) {
|
|
6706
|
-
const sev =
|
|
7037
|
+
const sev = import_types26.PolicySeveritySchema.safeParse(req.query.severity);
|
|
6707
7038
|
if (!sev.success) {
|
|
6708
7039
|
return reply.code(400).send({
|
|
6709
7040
|
error: "invalid severity",
|
|
@@ -6720,7 +7051,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6720
7051
|
scope.post("/policies/check", async (req, reply) => {
|
|
6721
7052
|
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6722
7053
|
if (!proj) return;
|
|
6723
|
-
const parsed =
|
|
7054
|
+
const parsed = import_types26.PoliciesCheckBodySchema.safeParse(req.body ?? {});
|
|
6724
7055
|
if (!parsed.success) {
|
|
6725
7056
|
return reply.code(400).send({
|
|
6726
7057
|
error: "invalid /policies/check body",
|
|
@@ -6964,7 +7295,7 @@ init_otel_grpc();
|
|
|
6964
7295
|
// src/search.ts
|
|
6965
7296
|
init_cjs_shims();
|
|
6966
7297
|
var import_node_fs26 = require("fs");
|
|
6967
|
-
var
|
|
7298
|
+
var import_node_path43 = __toESM(require("path"), 1);
|
|
6968
7299
|
var import_node_crypto3 = require("crypto");
|
|
6969
7300
|
var DEFAULT_LIMIT = 10;
|
|
6970
7301
|
var NOMIC_DIM = 768;
|
|
@@ -7103,7 +7434,7 @@ async function readCache(cachePath) {
|
|
|
7103
7434
|
}
|
|
7104
7435
|
}
|
|
7105
7436
|
async function writeCache(cachePath, cache) {
|
|
7106
|
-
await import_node_fs26.promises.mkdir(
|
|
7437
|
+
await import_node_fs26.promises.mkdir(import_node_path43.default.dirname(cachePath), { recursive: true });
|
|
7107
7438
|
await import_node_fs26.promises.writeFile(cachePath, JSON.stringify(cache));
|
|
7108
7439
|
}
|
|
7109
7440
|
var VectorIndex = class {
|
|
@@ -7260,8 +7591,8 @@ var ALL_PHASES = [
|
|
|
7260
7591
|
];
|
|
7261
7592
|
function classifyChange(relPath) {
|
|
7262
7593
|
const phases = /* @__PURE__ */ new Set();
|
|
7263
|
-
const base =
|
|
7264
|
-
const segments = relPath.split(
|
|
7594
|
+
const base = import_node_path44.default.basename(relPath).toLowerCase();
|
|
7595
|
+
const segments = relPath.split(import_node_path44.default.sep).map((s) => s.toLowerCase());
|
|
7265
7596
|
if (base === "package.json" || base === "requirements.txt" || base === "pyproject.toml" || base === "setup.py") {
|
|
7266
7597
|
phases.add("services");
|
|
7267
7598
|
phases.add("aliases");
|
|
@@ -7382,9 +7713,9 @@ function countWatchableDirs(scanPath, limit) {
|
|
|
7382
7713
|
for (const e of entries) {
|
|
7383
7714
|
if (count >= limit) return;
|
|
7384
7715
|
if (!e.isDirectory()) continue;
|
|
7385
|
-
if (IGNORED_WATCH_PATHS.some((re) => re.test(
|
|
7716
|
+
if (IGNORED_WATCH_PATHS.some((re) => re.test(import_node_path44.default.join(dir, e.name) + import_node_path44.default.sep))) continue;
|
|
7386
7717
|
count++;
|
|
7387
|
-
if (depth < 2) visit(
|
|
7718
|
+
if (depth < 2) visit(import_node_path44.default.join(dir, e.name), depth + 1);
|
|
7388
7719
|
}
|
|
7389
7720
|
};
|
|
7390
7721
|
visit(scanPath, 0);
|
|
@@ -7402,8 +7733,8 @@ async function startWatch(graph, opts) {
|
|
|
7402
7733
|
const projectName = opts.project ?? DEFAULT_PROJECT;
|
|
7403
7734
|
await loadGraphFromDisk(graph, opts.outPath);
|
|
7404
7735
|
const detachEventBus = attachGraphToEventBus(graph, { project: projectName });
|
|
7405
|
-
const policyFilePath =
|
|
7406
|
-
const policyViolationsPath =
|
|
7736
|
+
const policyFilePath = import_node_path44.default.join(opts.scanPath, "policy.json");
|
|
7737
|
+
const policyViolationsPath = import_node_path44.default.join(import_node_path44.default.dirname(opts.outPath), "policy-violations.ndjson");
|
|
7407
7738
|
let policies = [];
|
|
7408
7739
|
try {
|
|
7409
7740
|
policies = await loadPolicyFile(policyFilePath);
|
|
@@ -7454,7 +7785,7 @@ async function startWatch(graph, opts) {
|
|
|
7454
7785
|
assertBindAuthority(host, auth.authToken);
|
|
7455
7786
|
const port = opts.port ?? 8080;
|
|
7456
7787
|
const otelPort = opts.otelPort ?? 4318;
|
|
7457
|
-
const cachePath = opts.embeddingsCachePath ??
|
|
7788
|
+
const cachePath = opts.embeddingsCachePath ?? import_node_path44.default.join(import_node_path44.default.dirname(opts.outPath), "embeddings.json");
|
|
7458
7789
|
let searchIndex;
|
|
7459
7790
|
try {
|
|
7460
7791
|
searchIndex = await buildSearchIndex(graph, { cachePath });
|
|
@@ -7472,7 +7803,7 @@ async function startWatch(graph, opts) {
|
|
|
7472
7803
|
// Paths are derived from the explicit options the watch caller passes
|
|
7473
7804
|
// — pathsForProject is only used to fill in the embeddings/snapshot
|
|
7474
7805
|
// fields so the registry shape is complete.
|
|
7475
|
-
...pathsForProject(projectName,
|
|
7806
|
+
...pathsForProject(projectName, import_node_path44.default.dirname(opts.outPath)),
|
|
7476
7807
|
snapshotPath: opts.outPath,
|
|
7477
7808
|
errorsPath: opts.errorsPath,
|
|
7478
7809
|
staleEventsPath: opts.staleEventsPath
|
|
@@ -7559,9 +7890,9 @@ async function startWatch(graph, opts) {
|
|
|
7559
7890
|
};
|
|
7560
7891
|
const onPath = (absPath) => {
|
|
7561
7892
|
if (shouldIgnore(absPath)) return;
|
|
7562
|
-
const rel =
|
|
7893
|
+
const rel = import_node_path44.default.relative(opts.scanPath, absPath);
|
|
7563
7894
|
if (!rel || rel.startsWith("..")) return;
|
|
7564
|
-
pendingPaths.add(rel.split(
|
|
7895
|
+
pendingPaths.add(rel.split(import_node_path44.default.sep).join("/"));
|
|
7565
7896
|
const phases = classifyChange(rel);
|
|
7566
7897
|
if (phases.size === 0) {
|
|
7567
7898
|
for (const p of ALL_PHASES) pending.add(p);
|
|
@@ -7616,7 +7947,7 @@ async function startWatch(graph, opts) {
|
|
|
7616
7947
|
// src/deploy/detect.ts
|
|
7617
7948
|
init_cjs_shims();
|
|
7618
7949
|
var import_node_fs28 = require("fs");
|
|
7619
|
-
var
|
|
7950
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
7620
7951
|
var import_node_child_process2 = require("child_process");
|
|
7621
7952
|
var import_node_crypto4 = require("crypto");
|
|
7622
7953
|
function generateToken() {
|
|
@@ -7716,7 +8047,7 @@ async function runDeploy(opts = {}) {
|
|
|
7716
8047
|
const token = generateToken();
|
|
7717
8048
|
switch (substrate) {
|
|
7718
8049
|
case "docker-compose": {
|
|
7719
|
-
const artifactPath =
|
|
8050
|
+
const artifactPath = import_node_path45.default.join(cwd, "docker-compose.neat.yml");
|
|
7720
8051
|
const contents = emitDockerCompose(cwd);
|
|
7721
8052
|
await import_node_fs28.promises.writeFile(artifactPath, contents, "utf8");
|
|
7722
8053
|
return {
|
|
@@ -7724,11 +8055,11 @@ async function runDeploy(opts = {}) {
|
|
|
7724
8055
|
artifactPath,
|
|
7725
8056
|
token,
|
|
7726
8057
|
contents,
|
|
7727
|
-
startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${
|
|
8058
|
+
startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${import_node_path45.default.basename(artifactPath)} up -d`
|
|
7728
8059
|
};
|
|
7729
8060
|
}
|
|
7730
8061
|
case "systemd": {
|
|
7731
|
-
const artifactPath =
|
|
8062
|
+
const artifactPath = import_node_path45.default.join(cwd, "neat.service");
|
|
7732
8063
|
const contents = emitSystemdUnit(cwd);
|
|
7733
8064
|
await import_node_fs28.promises.writeFile(artifactPath, contents, "utf8");
|
|
7734
8065
|
return {
|
|
@@ -7764,15 +8095,56 @@ init_cjs_shims();
|
|
|
7764
8095
|
// src/installers/javascript.ts
|
|
7765
8096
|
init_cjs_shims();
|
|
7766
8097
|
var import_node_fs29 = require("fs");
|
|
7767
|
-
var
|
|
8098
|
+
var import_node_path46 = __toESM(require("path"), 1);
|
|
7768
8099
|
var import_semver2 = __toESM(require("semver"), 1);
|
|
7769
8100
|
|
|
7770
8101
|
// src/installers/templates.ts
|
|
7771
8102
|
init_cjs_shims();
|
|
7772
8103
|
var OTEL_INIT_HEADER = "// Generated by `neat init --apply` (ADR-069). OpenTelemetry auto-instrumentation hook.";
|
|
7773
|
-
var OTEL_INIT_STAMP = "// neat-template-version:
|
|
8104
|
+
var OTEL_INIT_STAMP = "// neat-template-version: 6 \u2014 daemon.json endpoint resolution (ADR-096) + layered file-first capture (ADR-090).";
|
|
7774
8105
|
var OTEL_OTLP_HEADERS_JS = "if (process.env.NEAT_OTEL_TOKEN) process.env.OTEL_EXPORTER_OTLP_HEADERS ||= 'Authorization=Bearer ' + process.env.NEAT_OTEL_TOKEN";
|
|
7775
8106
|
var OTEL_OTLP_PROTOCOL_JS = "process.env.OTEL_EXPORTER_OTLP_PROTOCOL ||= 'http/json'";
|
|
8107
|
+
var OTEL_ENDPOINT_RESOLVER_CJS = `;(function () {
|
|
8108
|
+
try {
|
|
8109
|
+
if (process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT || process.env.OTEL_EXPORTER_OTLP_ENDPOINT) return
|
|
8110
|
+
const __neatFs = require('node:fs')
|
|
8111
|
+
const __neatPath = require('node:path')
|
|
8112
|
+
let __neatDir = process.cwd()
|
|
8113
|
+
for (let __i = 0; __i < 8; __i++) {
|
|
8114
|
+
try {
|
|
8115
|
+
const __rec = JSON.parse(__neatFs.readFileSync(__neatPath.join(__neatDir, 'neat-out', 'daemon.json'), 'utf8'))
|
|
8116
|
+
if (__rec && __rec.ports && typeof __rec.ports.otlp === 'number') {
|
|
8117
|
+
process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = 'http://localhost:' + __rec.ports.otlp + '/v1/traces'
|
|
8118
|
+
break
|
|
8119
|
+
}
|
|
8120
|
+
} catch (_e) {}
|
|
8121
|
+
const __parent = __neatPath.dirname(__neatDir)
|
|
8122
|
+
if (__parent === __neatDir) break
|
|
8123
|
+
__neatDir = __parent
|
|
8124
|
+
}
|
|
8125
|
+
} catch (_e) {}
|
|
8126
|
+
process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||= 'http://localhost:4318/v1/traces'
|
|
8127
|
+
})()`;
|
|
8128
|
+
var OTEL_ESM_NODE_IMPORTS = "import { readFileSync as __neatReadFileSync } from 'node:fs'\nimport { join as __neatJoin, dirname as __neatDirname } from 'node:path'";
|
|
8129
|
+
var OTEL_ENDPOINT_RESOLVER_ESM = `;(function () {
|
|
8130
|
+
try {
|
|
8131
|
+
if (process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT || process.env.OTEL_EXPORTER_OTLP_ENDPOINT) return
|
|
8132
|
+
let __neatDir = process.cwd()
|
|
8133
|
+
for (let __i = 0; __i < 8; __i++) {
|
|
8134
|
+
try {
|
|
8135
|
+
const __rec = JSON.parse(__neatReadFileSync(__neatJoin(__neatDir, 'neat-out', 'daemon.json'), 'utf8'))
|
|
8136
|
+
if (__rec && __rec.ports && typeof __rec.ports.otlp === 'number') {
|
|
8137
|
+
process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = 'http://localhost:' + __rec.ports.otlp + '/v1/traces'
|
|
8138
|
+
break
|
|
8139
|
+
}
|
|
8140
|
+
} catch (_e) {}
|
|
8141
|
+
const __parent = __neatDirname(__neatDir)
|
|
8142
|
+
if (__parent === __neatDir) break
|
|
8143
|
+
__neatDir = __parent
|
|
8144
|
+
}
|
|
8145
|
+
} catch (_e) {}
|
|
8146
|
+
process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||= 'http://localhost:4318/v1/traces'
|
|
8147
|
+
})()`;
|
|
7776
8148
|
function neatCaptureSource(ts) {
|
|
7777
8149
|
const spanT = ts ? ": any" : "";
|
|
7778
8150
|
const strOpt = ts ? ": string | undefined" : "";
|
|
@@ -8061,7 +8433,7 @@ try {
|
|
|
8061
8433
|
var OTEL_INIT_CJS = `${OTEL_INIT_HEADER}
|
|
8062
8434
|
${OTEL_INIT_STAMP}
|
|
8063
8435
|
process.env.OTEL_SERVICE_NAME ||= '__SERVICE_NAME__'
|
|
8064
|
-
|
|
8436
|
+
${OTEL_ENDPOINT_RESOLVER_CJS}
|
|
8065
8437
|
${OTEL_OTLP_PROTOCOL_JS}
|
|
8066
8438
|
${OTEL_OTLP_HEADERS_JS}
|
|
8067
8439
|
|
|
@@ -8079,8 +8451,9 @@ ${neatWireCaptureSource(false)}
|
|
|
8079
8451
|
`;
|
|
8080
8452
|
var OTEL_INIT_ESM = `${OTEL_INIT_HEADER}
|
|
8081
8453
|
${OTEL_INIT_STAMP}
|
|
8454
|
+
${OTEL_ESM_NODE_IMPORTS}
|
|
8082
8455
|
process.env.OTEL_SERVICE_NAME ||= '__SERVICE_NAME__'
|
|
8083
|
-
|
|
8456
|
+
${OTEL_ENDPOINT_RESOLVER_ESM}
|
|
8084
8457
|
${OTEL_OTLP_PROTOCOL_JS}
|
|
8085
8458
|
${OTEL_OTLP_HEADERS_JS}
|
|
8086
8459
|
|
|
@@ -8103,8 +8476,9 @@ ${OTEL_INIT_STAMP}
|
|
|
8103
8476
|
// strict user tsconfig would reject; suppressing type-checking here keeps the
|
|
8104
8477
|
// generated file from breaking the host project's \`tsc\` gate (#427) without
|
|
8105
8478
|
// constraining the runtime logic. The file is regenerated, never hand-edited.
|
|
8479
|
+
${OTEL_ESM_NODE_IMPORTS}
|
|
8106
8480
|
process.env.OTEL_SERVICE_NAME ||= '__SERVICE_NAME__'
|
|
8107
|
-
|
|
8481
|
+
${OTEL_ENDPOINT_RESOLVER_ESM}
|
|
8108
8482
|
${OTEL_OTLP_PROTOCOL_JS}
|
|
8109
8483
|
${OTEL_OTLP_HEADERS_JS}
|
|
8110
8484
|
|
|
@@ -8126,11 +8500,14 @@ ${registrations.join("\n")}
|
|
|
8126
8500
|
`;
|
|
8127
8501
|
return template.replace(/__SERVICE_NAME__/g, serviceName).replace(/__PROJECT__/g, projectName).replace(/__INSTRUMENTATION_BLOCK__\n?/g, block);
|
|
8128
8502
|
}
|
|
8129
|
-
function renderEnvNeat(serviceName,
|
|
8503
|
+
function renderEnvNeat(serviceName, _projectName) {
|
|
8130
8504
|
return [
|
|
8131
8505
|
"# Generated by `neat init --apply` (ADR-069).",
|
|
8132
8506
|
`OTEL_SERVICE_NAME=${serviceName}`,
|
|
8133
|
-
|
|
8507
|
+
"# Advisory only \u2014 the generated otel-init resolves the live endpoint from",
|
|
8508
|
+
"# <project>/neat-out/daemon.json (ports.otlp) at boot (ADR-096). This is the",
|
|
8509
|
+
"# canonical default the daemon takes when its first-choice OTLP port is free.",
|
|
8510
|
+
"OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4318/v1/traces",
|
|
8134
8511
|
"OTEL_EXPORTER_OTLP_PROTOCOL=http/json",
|
|
8135
8512
|
"# Set NEAT_OTEL_TOKEN to the daemon's OTLP secret to authenticate exported spans (#410).",
|
|
8136
8513
|
"# NEAT_OTEL_TOKEN=",
|
|
@@ -8153,8 +8530,9 @@ export async function register() {
|
|
|
8153
8530
|
}
|
|
8154
8531
|
`;
|
|
8155
8532
|
var NEXT_INSTRUMENTATION_NODE_TS = `${NEXT_INSTRUMENTATION_HEADER}
|
|
8533
|
+
${OTEL_ESM_NODE_IMPORTS}
|
|
8156
8534
|
process.env.OTEL_SERVICE_NAME ||= '__SERVICE_NAME__'
|
|
8157
|
-
|
|
8535
|
+
${OTEL_ENDPOINT_RESOLVER_ESM}
|
|
8158
8536
|
${OTEL_OTLP_PROTOCOL_JS}
|
|
8159
8537
|
${OTEL_OTLP_HEADERS_JS}
|
|
8160
8538
|
|
|
@@ -8167,7 +8545,7 @@ new NodeSDK({ instrumentations }).start()
|
|
|
8167
8545
|
`;
|
|
8168
8546
|
var NEXT_INSTRUMENTATION_NODE_JS = `${NEXT_INSTRUMENTATION_HEADER}
|
|
8169
8547
|
process.env.OTEL_SERVICE_NAME ||= '__SERVICE_NAME__'
|
|
8170
|
-
|
|
8548
|
+
${OTEL_ENDPOINT_RESOLVER_CJS}
|
|
8171
8549
|
${OTEL_OTLP_PROTOCOL_JS}
|
|
8172
8550
|
${OTEL_OTLP_HEADERS_JS}
|
|
8173
8551
|
|
|
@@ -8186,8 +8564,9 @@ ${registrations.join("\n")}
|
|
|
8186
8564
|
}
|
|
8187
8565
|
var FRAMEWORK_OTEL_INIT_HEADER = "// Generated by `neat init --apply` (ADR-074). OpenTelemetry SDK hook.";
|
|
8188
8566
|
var FRAMEWORK_OTEL_INIT_TS_BODY = `${FRAMEWORK_OTEL_INIT_HEADER}
|
|
8567
|
+
${OTEL_ESM_NODE_IMPORTS}
|
|
8189
8568
|
process.env.OTEL_SERVICE_NAME ||= '__SERVICE_NAME__'
|
|
8190
|
-
|
|
8569
|
+
${OTEL_ENDPOINT_RESOLVER_ESM}
|
|
8191
8570
|
${OTEL_OTLP_PROTOCOL_JS}
|
|
8192
8571
|
${OTEL_OTLP_HEADERS_JS}
|
|
8193
8572
|
|
|
@@ -8202,7 +8581,7 @@ sdk.start()
|
|
|
8202
8581
|
`;
|
|
8203
8582
|
var FRAMEWORK_OTEL_INIT_JS_BODY = `${FRAMEWORK_OTEL_INIT_HEADER}
|
|
8204
8583
|
process.env.OTEL_SERVICE_NAME ||= '__SERVICE_NAME__'
|
|
8205
|
-
|
|
8584
|
+
${OTEL_ENDPOINT_RESOLVER_CJS}
|
|
8206
8585
|
${OTEL_OTLP_PROTOCOL_JS}
|
|
8207
8586
|
${OTEL_OTLP_HEADERS_JS}
|
|
8208
8587
|
|
|
@@ -8311,11 +8690,11 @@ var OTEL_ENV = {
|
|
|
8311
8690
|
value: "http://localhost:4318/projects/<project>/v1/traces"
|
|
8312
8691
|
};
|
|
8313
8692
|
function serviceNodeName(pkg, serviceDir) {
|
|
8314
|
-
return pkg.name ??
|
|
8693
|
+
return pkg.name ?? import_node_path46.default.basename(serviceDir);
|
|
8315
8694
|
}
|
|
8316
8695
|
function projectToken(pkg, serviceDir, project) {
|
|
8317
8696
|
if (project && project.length > 0) return project;
|
|
8318
|
-
return pkg.name ??
|
|
8697
|
+
return pkg.name ?? import_node_path46.default.basename(serviceDir);
|
|
8319
8698
|
}
|
|
8320
8699
|
async function readJsonFile(p) {
|
|
8321
8700
|
try {
|
|
@@ -8328,16 +8707,16 @@ async function readJsonFile(p) {
|
|
|
8328
8707
|
async function detectRuntimeKind(pkgRoot, pkg) {
|
|
8329
8708
|
const deps = allDeps(pkg);
|
|
8330
8709
|
if ("react-native" in deps || "expo" in deps) return "react-native";
|
|
8331
|
-
const appJson = await readJsonFile(
|
|
8710
|
+
const appJson = await readJsonFile(import_node_path46.default.join(pkgRoot, "app.json"));
|
|
8332
8711
|
if (appJson && typeof appJson === "object" && "expo" in appJson) {
|
|
8333
8712
|
return "react-native";
|
|
8334
8713
|
}
|
|
8335
|
-
if (await exists3(
|
|
8714
|
+
if (await exists3(import_node_path46.default.join(pkgRoot, "vite.config.js")) || await exists3(import_node_path46.default.join(pkgRoot, "vite.config.ts")) || await exists3(import_node_path46.default.join(pkgRoot, "vite.config.mjs")) || "vite" in deps) {
|
|
8336
8715
|
return "browser-bundle";
|
|
8337
8716
|
}
|
|
8338
|
-
if (await exists3(
|
|
8339
|
-
if (await exists3(
|
|
8340
|
-
if (await exists3(
|
|
8717
|
+
if (await exists3(import_node_path46.default.join(pkgRoot, "wrangler.toml"))) return "cloudflare-workers";
|
|
8718
|
+
if (await exists3(import_node_path46.default.join(pkgRoot, "bun.lockb"))) return "bun";
|
|
8719
|
+
if (await exists3(import_node_path46.default.join(pkgRoot, "deno.json")) || await exists3(import_node_path46.default.join(pkgRoot, "deno.lock"))) {
|
|
8341
8720
|
return "deno";
|
|
8342
8721
|
}
|
|
8343
8722
|
const engines = pkg.engines ?? {};
|
|
@@ -8346,7 +8725,7 @@ async function detectRuntimeKind(pkgRoot, pkg) {
|
|
|
8346
8725
|
}
|
|
8347
8726
|
async function readPackageJson2(serviceDir) {
|
|
8348
8727
|
try {
|
|
8349
|
-
const raw = await import_node_fs29.promises.readFile(
|
|
8728
|
+
const raw = await import_node_fs29.promises.readFile(import_node_path46.default.join(serviceDir, "package.json"), "utf8");
|
|
8350
8729
|
return JSON.parse(raw);
|
|
8351
8730
|
} catch {
|
|
8352
8731
|
return null;
|
|
@@ -8390,7 +8769,7 @@ function needsVersionUpgrade(installed, expected) {
|
|
|
8390
8769
|
var NEXT_CONFIG_CANDIDATES = ["next.config.js", "next.config.ts", "next.config.mjs"];
|
|
8391
8770
|
async function findNextConfig(serviceDir) {
|
|
8392
8771
|
for (const name of NEXT_CONFIG_CANDIDATES) {
|
|
8393
|
-
const candidate =
|
|
8772
|
+
const candidate = import_node_path46.default.join(serviceDir, name);
|
|
8394
8773
|
if (await exists3(candidate)) return candidate;
|
|
8395
8774
|
}
|
|
8396
8775
|
return null;
|
|
@@ -8417,7 +8796,7 @@ function hasRemixDependency(pkg) {
|
|
|
8417
8796
|
}
|
|
8418
8797
|
async function findRemixEntry(serviceDir) {
|
|
8419
8798
|
for (const rel of REMIX_ENTRY_CANDIDATES) {
|
|
8420
|
-
const candidate =
|
|
8799
|
+
const candidate = import_node_path46.default.join(serviceDir, rel);
|
|
8421
8800
|
if (await exists3(candidate)) return candidate;
|
|
8422
8801
|
}
|
|
8423
8802
|
return null;
|
|
@@ -8429,14 +8808,14 @@ function hasSvelteKitDependency(pkg) {
|
|
|
8429
8808
|
}
|
|
8430
8809
|
async function findSvelteKitHooks(serviceDir) {
|
|
8431
8810
|
for (const rel of SVELTEKIT_HOOKS_CANDIDATES) {
|
|
8432
|
-
const candidate =
|
|
8811
|
+
const candidate = import_node_path46.default.join(serviceDir, rel);
|
|
8433
8812
|
if (await exists3(candidate)) return candidate;
|
|
8434
8813
|
}
|
|
8435
8814
|
return null;
|
|
8436
8815
|
}
|
|
8437
8816
|
async function findSvelteKitConfig(serviceDir) {
|
|
8438
8817
|
for (const rel of SVELTEKIT_CONFIG_CANDIDATES) {
|
|
8439
|
-
const candidate =
|
|
8818
|
+
const candidate = import_node_path46.default.join(serviceDir, rel);
|
|
8440
8819
|
if (await exists3(candidate)) return candidate;
|
|
8441
8820
|
}
|
|
8442
8821
|
return null;
|
|
@@ -8447,7 +8826,7 @@ function hasNuxtDependency(pkg) {
|
|
|
8447
8826
|
}
|
|
8448
8827
|
async function findNuxtConfig(serviceDir) {
|
|
8449
8828
|
for (const name of NUXT_CONFIG_CANDIDATES) {
|
|
8450
|
-
const candidate =
|
|
8829
|
+
const candidate = import_node_path46.default.join(serviceDir, name);
|
|
8451
8830
|
if (await exists3(candidate)) return candidate;
|
|
8452
8831
|
}
|
|
8453
8832
|
return null;
|
|
@@ -8458,7 +8837,7 @@ function hasAstroDependency(pkg) {
|
|
|
8458
8837
|
}
|
|
8459
8838
|
async function findAstroConfig(serviceDir) {
|
|
8460
8839
|
for (const name of ASTRO_CONFIG_CANDIDATES) {
|
|
8461
|
-
const candidate =
|
|
8840
|
+
const candidate = import_node_path46.default.join(serviceDir, name);
|
|
8462
8841
|
if (await exists3(candidate)) return candidate;
|
|
8463
8842
|
}
|
|
8464
8843
|
return null;
|
|
@@ -8472,7 +8851,7 @@ function parseNextMajor(range) {
|
|
|
8472
8851
|
return Number.isFinite(n) ? n : null;
|
|
8473
8852
|
}
|
|
8474
8853
|
async function isTypeScriptProject(serviceDir) {
|
|
8475
|
-
return exists3(
|
|
8854
|
+
return exists3(import_node_path46.default.join(serviceDir, "tsconfig.json"));
|
|
8476
8855
|
}
|
|
8477
8856
|
var INDEX_EXTENSIONS = [".ts", ".tsx", ".js", ".mjs", ".cjs"];
|
|
8478
8857
|
var INDEX_CANDIDATES = INDEX_EXTENSIONS.map((ext) => `index${ext}`);
|
|
@@ -8518,7 +8897,7 @@ function entryFromScript(script) {
|
|
|
8518
8897
|
}
|
|
8519
8898
|
async function resolveEntry(serviceDir, pkg) {
|
|
8520
8899
|
if (typeof pkg.main === "string" && pkg.main.length > 0) {
|
|
8521
|
-
const candidate =
|
|
8900
|
+
const candidate = import_node_path46.default.resolve(serviceDir, pkg.main);
|
|
8522
8901
|
if (await exists3(candidate)) return candidate;
|
|
8523
8902
|
}
|
|
8524
8903
|
if (pkg.bin) {
|
|
@@ -8532,36 +8911,36 @@ async function resolveEntry(serviceDir, pkg) {
|
|
|
8532
8911
|
if (typeof first === "string") binEntry = first;
|
|
8533
8912
|
}
|
|
8534
8913
|
if (binEntry) {
|
|
8535
|
-
const candidate =
|
|
8914
|
+
const candidate = import_node_path46.default.resolve(serviceDir, binEntry);
|
|
8536
8915
|
if (await exists3(candidate)) return candidate;
|
|
8537
8916
|
}
|
|
8538
8917
|
}
|
|
8539
8918
|
const startEntry = entryFromScript(pkg.scripts?.start);
|
|
8540
8919
|
if (startEntry) {
|
|
8541
|
-
const candidate =
|
|
8920
|
+
const candidate = import_node_path46.default.resolve(serviceDir, startEntry);
|
|
8542
8921
|
if (await exists3(candidate)) return candidate;
|
|
8543
8922
|
}
|
|
8544
8923
|
const devEntry = entryFromScript(pkg.scripts?.dev);
|
|
8545
8924
|
if (devEntry) {
|
|
8546
|
-
const candidate =
|
|
8925
|
+
const candidate = import_node_path46.default.resolve(serviceDir, devEntry);
|
|
8547
8926
|
if (await exists3(candidate)) return candidate;
|
|
8548
8927
|
}
|
|
8549
8928
|
for (const rel of SRC_INDEX_CANDIDATES) {
|
|
8550
|
-
const candidate =
|
|
8929
|
+
const candidate = import_node_path46.default.join(serviceDir, rel);
|
|
8551
8930
|
if (await exists3(candidate)) return candidate;
|
|
8552
8931
|
}
|
|
8553
8932
|
for (const rel of SRC_NAMED_CANDIDATES) {
|
|
8554
|
-
const candidate =
|
|
8933
|
+
const candidate = import_node_path46.default.join(serviceDir, rel);
|
|
8555
8934
|
if (await exists3(candidate)) return candidate;
|
|
8556
8935
|
}
|
|
8557
8936
|
for (const name of INDEX_CANDIDATES) {
|
|
8558
|
-
const candidate =
|
|
8937
|
+
const candidate = import_node_path46.default.join(serviceDir, name);
|
|
8559
8938
|
if (await exists3(candidate)) return candidate;
|
|
8560
8939
|
}
|
|
8561
8940
|
return null;
|
|
8562
8941
|
}
|
|
8563
8942
|
function dispatchEntry(entryFile, pkg) {
|
|
8564
|
-
const ext =
|
|
8943
|
+
const ext = import_node_path46.default.extname(entryFile).toLowerCase();
|
|
8565
8944
|
if (ext === ".ts" || ext === ".tsx") return "ts";
|
|
8566
8945
|
if (ext === ".mjs") return "esm";
|
|
8567
8946
|
if (ext === ".cjs") return "cjs";
|
|
@@ -8578,9 +8957,9 @@ function otelInitContents(flavor) {
|
|
|
8578
8957
|
return OTEL_INIT_CJS;
|
|
8579
8958
|
}
|
|
8580
8959
|
function injectionLine(flavor, entryFile, otelInitFile) {
|
|
8581
|
-
let rel =
|
|
8960
|
+
let rel = import_node_path46.default.relative(import_node_path46.default.dirname(entryFile), otelInitFile);
|
|
8582
8961
|
if (!rel.startsWith(".")) rel = `./${rel}`;
|
|
8583
|
-
rel = rel.split(
|
|
8962
|
+
rel = rel.split(import_node_path46.default.sep).join("/");
|
|
8584
8963
|
if (flavor === "cjs") return `require('${rel}')`;
|
|
8585
8964
|
if (flavor === "esm") return `import '${rel}'`;
|
|
8586
8965
|
const tsRel = rel.replace(/\.ts$/, "");
|
|
@@ -8593,23 +8972,23 @@ function lineIsOtelInjection(line) {
|
|
|
8593
8972
|
}
|
|
8594
8973
|
async function detectsSrcLayout(serviceDir) {
|
|
8595
8974
|
const [hasSrcApp, hasSrcPages, hasRootApp, hasRootPages] = await Promise.all([
|
|
8596
|
-
exists3(
|
|
8597
|
-
exists3(
|
|
8598
|
-
exists3(
|
|
8599
|
-
exists3(
|
|
8975
|
+
exists3(import_node_path46.default.join(serviceDir, "src", "app")),
|
|
8976
|
+
exists3(import_node_path46.default.join(serviceDir, "src", "pages")),
|
|
8977
|
+
exists3(import_node_path46.default.join(serviceDir, "app")),
|
|
8978
|
+
exists3(import_node_path46.default.join(serviceDir, "pages"))
|
|
8600
8979
|
]);
|
|
8601
8980
|
return (hasSrcApp || hasSrcPages) && !hasRootApp && !hasRootPages;
|
|
8602
8981
|
}
|
|
8603
8982
|
async function planNext(serviceDir, pkg, manifestPath, nextConfigPath, project) {
|
|
8604
8983
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
8605
8984
|
const srcLayout = await detectsSrcLayout(serviceDir);
|
|
8606
|
-
const baseDir = srcLayout ?
|
|
8607
|
-
const instrumentationFile =
|
|
8608
|
-
const instrumentationNodeFile =
|
|
8985
|
+
const baseDir = srcLayout ? import_node_path46.default.join(serviceDir, "src") : serviceDir;
|
|
8986
|
+
const instrumentationFile = import_node_path46.default.join(baseDir, useTs ? "instrumentation.ts" : "instrumentation.js");
|
|
8987
|
+
const instrumentationNodeFile = import_node_path46.default.join(
|
|
8609
8988
|
baseDir,
|
|
8610
8989
|
useTs ? "instrumentation.node.ts" : "instrumentation.node.js"
|
|
8611
8990
|
);
|
|
8612
|
-
const envNeatFile =
|
|
8991
|
+
const envNeatFile = import_node_path46.default.join(baseDir, ".env.neat");
|
|
8613
8992
|
const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
8614
8993
|
const dependencyEdits = [];
|
|
8615
8994
|
for (const sdk of SDK_PACKAGES) {
|
|
@@ -8714,7 +9093,7 @@ function buildDependencyEdits(pkg, manifestPath) {
|
|
|
8714
9093
|
return edits;
|
|
8715
9094
|
}
|
|
8716
9095
|
async function queueEnvNeat(serviceDir, pkg, project, generatedFiles) {
|
|
8717
|
-
const envNeatFile =
|
|
9096
|
+
const envNeatFile = import_node_path46.default.join(serviceDir, ".env.neat");
|
|
8718
9097
|
if (!await exists3(envNeatFile)) {
|
|
8719
9098
|
generatedFiles.push({
|
|
8720
9099
|
file: envNeatFile,
|
|
@@ -8749,7 +9128,7 @@ function fileImportsOtelHook(raw, specifiers) {
|
|
|
8749
9128
|
}
|
|
8750
9129
|
async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
|
|
8751
9130
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
8752
|
-
const otelServerFile =
|
|
9131
|
+
const otelServerFile = import_node_path46.default.join(
|
|
8753
9132
|
serviceDir,
|
|
8754
9133
|
useTs ? "app/otel.server.ts" : "app/otel.server.js"
|
|
8755
9134
|
);
|
|
@@ -8806,11 +9185,11 @@ async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
|
|
|
8806
9185
|
}
|
|
8807
9186
|
async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project) {
|
|
8808
9187
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
8809
|
-
const otelInitFile =
|
|
9188
|
+
const otelInitFile = import_node_path46.default.join(
|
|
8810
9189
|
serviceDir,
|
|
8811
9190
|
useTs ? "src/otel-init.ts" : "src/otel-init.js"
|
|
8812
9191
|
);
|
|
8813
|
-
const resolvedHooksFile = hooksFile ??
|
|
9192
|
+
const resolvedHooksFile = hooksFile ?? import_node_path46.default.join(serviceDir, useTs ? "src/hooks.server.ts" : "src/hooks.server.js");
|
|
8814
9193
|
const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
|
|
8815
9194
|
const generatedFiles = [];
|
|
8816
9195
|
const entrypointEdits = [];
|
|
@@ -8872,11 +9251,11 @@ async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project)
|
|
|
8872
9251
|
}
|
|
8873
9252
|
async function planNuxt(serviceDir, pkg, manifestPath, project) {
|
|
8874
9253
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
8875
|
-
const otelPluginFile =
|
|
9254
|
+
const otelPluginFile = import_node_path46.default.join(
|
|
8876
9255
|
serviceDir,
|
|
8877
9256
|
useTs ? "server/plugins/otel.ts" : "server/plugins/otel.js"
|
|
8878
9257
|
);
|
|
8879
|
-
const otelInitFile =
|
|
9258
|
+
const otelInitFile = import_node_path46.default.join(
|
|
8880
9259
|
serviceDir,
|
|
8881
9260
|
useTs ? "server/plugins/otel-init.ts" : "server/plugins/otel-init.js"
|
|
8882
9261
|
);
|
|
@@ -8927,19 +9306,19 @@ async function planNuxt(serviceDir, pkg, manifestPath, project) {
|
|
|
8927
9306
|
var ASTRO_MIDDLEWARE_CANDIDATES = ["src/middleware.ts", "src/middleware.js"];
|
|
8928
9307
|
async function findAstroMiddleware(serviceDir) {
|
|
8929
9308
|
for (const rel of ASTRO_MIDDLEWARE_CANDIDATES) {
|
|
8930
|
-
const candidate =
|
|
9309
|
+
const candidate = import_node_path46.default.join(serviceDir, rel);
|
|
8931
9310
|
if (await exists3(candidate)) return candidate;
|
|
8932
9311
|
}
|
|
8933
9312
|
return null;
|
|
8934
9313
|
}
|
|
8935
9314
|
async function planAstro(serviceDir, pkg, manifestPath, project) {
|
|
8936
9315
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
8937
|
-
const otelInitFile =
|
|
9316
|
+
const otelInitFile = import_node_path46.default.join(
|
|
8938
9317
|
serviceDir,
|
|
8939
9318
|
useTs ? "src/otel-init.ts" : "src/otel-init.js"
|
|
8940
9319
|
);
|
|
8941
9320
|
const existingMiddleware = await findAstroMiddleware(serviceDir);
|
|
8942
|
-
const middlewareFile = existingMiddleware ??
|
|
9321
|
+
const middlewareFile = existingMiddleware ?? import_node_path46.default.join(serviceDir, useTs ? "src/middleware.ts" : "src/middleware.js");
|
|
8943
9322
|
const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
|
|
8944
9323
|
const generatedFiles = [];
|
|
8945
9324
|
const entrypointEdits = [];
|
|
@@ -9035,7 +9414,7 @@ async function findFrameworkDispatch(serviceDir, pkg, manifestPath, project) {
|
|
|
9035
9414
|
}
|
|
9036
9415
|
async function plan(serviceDir, opts) {
|
|
9037
9416
|
const pkg = await readPackageJson2(serviceDir);
|
|
9038
|
-
const manifestPath =
|
|
9417
|
+
const manifestPath = import_node_path46.default.join(serviceDir, "package.json");
|
|
9039
9418
|
const project = opts?.project;
|
|
9040
9419
|
const empty = {
|
|
9041
9420
|
language: "javascript",
|
|
@@ -9070,8 +9449,8 @@ async function plan(serviceDir, opts) {
|
|
|
9070
9449
|
return { ...empty, libOnly: true };
|
|
9071
9450
|
}
|
|
9072
9451
|
const flavor = dispatchEntry(entryFile, pkg);
|
|
9073
|
-
const otelInitFile =
|
|
9074
|
-
const envNeatFile =
|
|
9452
|
+
const otelInitFile = import_node_path46.default.join(import_node_path46.default.dirname(entryFile), otelInitFilename(flavor));
|
|
9453
|
+
const envNeatFile = import_node_path46.default.join(serviceDir, ".env.neat");
|
|
9075
9454
|
const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
9076
9455
|
const dependencyEdits = [];
|
|
9077
9456
|
for (const sdk of SDK_PACKAGES) {
|
|
@@ -9140,13 +9519,13 @@ async function plan(serviceDir, opts) {
|
|
|
9140
9519
|
};
|
|
9141
9520
|
}
|
|
9142
9521
|
function isAllowedWritePath(serviceDir, target) {
|
|
9143
|
-
const rel =
|
|
9522
|
+
const rel = import_node_path46.default.relative(serviceDir, target);
|
|
9144
9523
|
if (rel.startsWith("..")) return false;
|
|
9145
|
-
const base =
|
|
9524
|
+
const base = import_node_path46.default.basename(target);
|
|
9146
9525
|
if (base === "package.json") return true;
|
|
9147
9526
|
if (base === ".env.neat") return true;
|
|
9148
9527
|
if (/^otel-init\.(?:js|cjs|mjs|ts)$/.test(base)) return true;
|
|
9149
|
-
const relPosix = rel.split(
|
|
9528
|
+
const relPosix = rel.split(import_node_path46.default.sep).join("/");
|
|
9150
9529
|
if (/^instrumentation(?:\.node)?\.(?:js|cjs|mjs|ts)$/.test(base)) {
|
|
9151
9530
|
if (relPosix === base) return true;
|
|
9152
9531
|
if (relPosix === `src/${base}`) return true;
|
|
@@ -9163,7 +9542,7 @@ function isAllowedWritePath(serviceDir, target) {
|
|
|
9163
9542
|
return false;
|
|
9164
9543
|
}
|
|
9165
9544
|
async function writeAtomic(file, contents) {
|
|
9166
|
-
await import_node_fs29.promises.mkdir(
|
|
9545
|
+
await import_node_fs29.promises.mkdir(import_node_path46.default.dirname(file), { recursive: true });
|
|
9167
9546
|
const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
|
|
9168
9547
|
await import_node_fs29.promises.writeFile(tmp, contents, "utf8");
|
|
9169
9548
|
await import_node_fs29.promises.rename(tmp, file);
|
|
@@ -9347,7 +9726,7 @@ async function rollback(installPlan, originals, createdFiles) {
|
|
|
9347
9726
|
...removed.map((f) => `removed: ${f}`),
|
|
9348
9727
|
""
|
|
9349
9728
|
];
|
|
9350
|
-
const rollbackPath =
|
|
9729
|
+
const rollbackPath = import_node_path46.default.join(installPlan.serviceDir, "neat-rollback.patch");
|
|
9351
9730
|
await import_node_fs29.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
|
|
9352
9731
|
}
|
|
9353
9732
|
function injectInstrumentationHook(raw) {
|
|
@@ -9378,7 +9757,7 @@ var javascriptInstaller = {
|
|
|
9378
9757
|
// src/installers/python.ts
|
|
9379
9758
|
init_cjs_shims();
|
|
9380
9759
|
var import_node_fs30 = require("fs");
|
|
9381
|
-
var
|
|
9760
|
+
var import_node_path47 = __toESM(require("path"), 1);
|
|
9382
9761
|
var SDK_PACKAGES2 = [
|
|
9383
9762
|
{ name: "opentelemetry-distro", version: ">=0.49b0" },
|
|
9384
9763
|
{ name: "opentelemetry-exporter-otlp", version: ">=1.28.0" }
|
|
@@ -9399,7 +9778,7 @@ async function exists4(p) {
|
|
|
9399
9778
|
async function detect2(serviceDir) {
|
|
9400
9779
|
const markers = ["requirements.txt", "pyproject.toml", "setup.py"];
|
|
9401
9780
|
for (const m of markers) {
|
|
9402
|
-
if (await exists4(
|
|
9781
|
+
if (await exists4(import_node_path47.default.join(serviceDir, m))) return true;
|
|
9403
9782
|
}
|
|
9404
9783
|
return false;
|
|
9405
9784
|
}
|
|
@@ -9409,7 +9788,7 @@ function reqPackageName(line) {
|
|
|
9409
9788
|
return head.replace(/[<>=!~].*$/, "").toLowerCase();
|
|
9410
9789
|
}
|
|
9411
9790
|
async function planRequirementsTxtEdits(serviceDir) {
|
|
9412
|
-
const file =
|
|
9791
|
+
const file = import_node_path47.default.join(serviceDir, "requirements.txt");
|
|
9413
9792
|
if (!await exists4(file)) return null;
|
|
9414
9793
|
const raw = await import_node_fs30.promises.readFile(file, "utf8");
|
|
9415
9794
|
const presentNames = new Set(
|
|
@@ -9419,7 +9798,7 @@ async function planRequirementsTxtEdits(serviceDir) {
|
|
|
9419
9798
|
return { manifest: file, missing: [...missing] };
|
|
9420
9799
|
}
|
|
9421
9800
|
async function planProcfileEdits(serviceDir) {
|
|
9422
|
-
const procfile =
|
|
9801
|
+
const procfile = import_node_path47.default.join(serviceDir, "Procfile");
|
|
9423
9802
|
if (!await exists4(procfile)) return [];
|
|
9424
9803
|
const raw = await import_node_fs30.promises.readFile(procfile, "utf8");
|
|
9425
9804
|
const edits = [];
|
|
@@ -9508,7 +9887,7 @@ async function apply2(installPlan) {
|
|
|
9508
9887
|
if (raw === void 0) {
|
|
9509
9888
|
throw new Error(`python installer: cannot read ${file} during apply`);
|
|
9510
9889
|
}
|
|
9511
|
-
const base =
|
|
9890
|
+
const base = import_node_path47.default.basename(file);
|
|
9512
9891
|
if (base === "requirements.txt") {
|
|
9513
9892
|
const edits = installPlan.dependencyEdits.filter((e) => e.file === file);
|
|
9514
9893
|
if (edits.length > 0) {
|
|
@@ -9547,7 +9926,7 @@ async function rollback2(installPlan, originals) {
|
|
|
9547
9926
|
...restored.map((f) => `restored: ${f}`),
|
|
9548
9927
|
""
|
|
9549
9928
|
];
|
|
9550
|
-
const rollbackPath =
|
|
9929
|
+
const rollbackPath = import_node_path47.default.join(installPlan.serviceDir, "neat-rollback.patch");
|
|
9551
9930
|
await import_node_fs30.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
|
|
9552
9931
|
}
|
|
9553
9932
|
var pythonInstaller = {
|
|
@@ -9671,19 +10050,51 @@ function renderPatch(sections) {
|
|
|
9671
10050
|
|
|
9672
10051
|
// src/orchestrator.ts
|
|
9673
10052
|
init_cjs_shims();
|
|
9674
|
-
var
|
|
10053
|
+
var import_node_fs33 = require("fs");
|
|
9675
10054
|
var import_node_http = __toESM(require("http"), 1);
|
|
9676
10055
|
var import_node_net = __toESM(require("net"), 1);
|
|
9677
|
-
var
|
|
10056
|
+
var import_node_path50 = __toESM(require("path"), 1);
|
|
9678
10057
|
var import_node_child_process3 = require("child_process");
|
|
9679
10058
|
var import_node_readline = __toESM(require("readline"), 1);
|
|
10059
|
+
|
|
10060
|
+
// src/daemon.ts
|
|
10061
|
+
init_cjs_shims();
|
|
10062
|
+
var import_node_fs32 = require("fs");
|
|
10063
|
+
var import_node_path49 = __toESM(require("path"), 1);
|
|
10064
|
+
var import_node_module = require("module");
|
|
10065
|
+
init_otel();
|
|
10066
|
+
init_auth();
|
|
10067
|
+
|
|
10068
|
+
// src/unrouted.ts
|
|
10069
|
+
init_cjs_shims();
|
|
10070
|
+
var import_node_fs31 = require("fs");
|
|
10071
|
+
var import_node_path48 = __toESM(require("path"), 1);
|
|
10072
|
+
|
|
10073
|
+
// src/daemon.ts
|
|
10074
|
+
function daemonJsonPath(scanPath) {
|
|
10075
|
+
return import_node_path49.default.join(scanPath, "neat-out", "daemon.json");
|
|
10076
|
+
}
|
|
10077
|
+
async function readDaemonRecord(scanPath) {
|
|
10078
|
+
try {
|
|
10079
|
+
const raw = await import_node_fs32.promises.readFile(daemonJsonPath(scanPath), "utf8");
|
|
10080
|
+
const parsed = JSON.parse(raw);
|
|
10081
|
+
if (typeof parsed.project === "string" && parsed.ports && typeof parsed.ports.rest === "number" && typeof parsed.ports.otlp === "number" && typeof parsed.ports.web === "number") {
|
|
10082
|
+
return parsed;
|
|
10083
|
+
}
|
|
10084
|
+
return null;
|
|
10085
|
+
} catch {
|
|
10086
|
+
return null;
|
|
10087
|
+
}
|
|
10088
|
+
}
|
|
10089
|
+
|
|
10090
|
+
// src/orchestrator.ts
|
|
9680
10091
|
async function extractAndPersist(opts) {
|
|
9681
10092
|
const services = await discoverServices(opts.scanPath);
|
|
9682
10093
|
const languages = [...new Set(services.map((s) => s.node.language))].sort();
|
|
9683
10094
|
const graphKey = opts.projectExplicit ? opts.project : DEFAULT_PROJECT;
|
|
9684
10095
|
resetGraph(graphKey);
|
|
9685
10096
|
const graph = getGraph(graphKey);
|
|
9686
|
-
const projectPaths = pathsForProject(graphKey,
|
|
10097
|
+
const projectPaths = pathsForProject(graphKey, import_node_path50.default.join(opts.scanPath, "neat-out"));
|
|
9687
10098
|
const extraction = await extractFromDirectory(graph, opts.scanPath, {
|
|
9688
10099
|
errorsPath: projectPaths.errorsPath
|
|
9689
10100
|
});
|
|
@@ -9737,7 +10148,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
9737
10148
|
console.log(`skipping ${svc.dir}: browser bundle; browser-OTel support lands in a future release.`);
|
|
9738
10149
|
} else if (outcome.outcome === "react-native") {
|
|
9739
10150
|
reactNative++;
|
|
9740
|
-
const svcName =
|
|
10151
|
+
const svcName = import_node_path50.default.basename(svc.dir);
|
|
9741
10152
|
console.log(
|
|
9742
10153
|
`neat: ${svc.dir} detected as React Native / Expo
|
|
9743
10154
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -9748,7 +10159,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
9748
10159
|
);
|
|
9749
10160
|
} else if (outcome.outcome === "bun") {
|
|
9750
10161
|
bun++;
|
|
9751
|
-
const svcName =
|
|
10162
|
+
const svcName = import_node_path50.default.basename(svc.dir);
|
|
9752
10163
|
console.log(
|
|
9753
10164
|
`neat: ${svc.dir} detected as Bun
|
|
9754
10165
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -9759,7 +10170,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
9759
10170
|
);
|
|
9760
10171
|
} else if (outcome.outcome === "deno") {
|
|
9761
10172
|
deno++;
|
|
9762
|
-
const svcName =
|
|
10173
|
+
const svcName = import_node_path50.default.basename(svc.dir);
|
|
9763
10174
|
console.log(
|
|
9764
10175
|
`neat: ${svc.dir} detected as Deno
|
|
9765
10176
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -9770,7 +10181,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
9770
10181
|
);
|
|
9771
10182
|
} else if (outcome.outcome === "cloudflare-workers") {
|
|
9772
10183
|
cloudflareWorkers++;
|
|
9773
|
-
const svcName =
|
|
10184
|
+
const svcName = import_node_path50.default.basename(svc.dir);
|
|
9774
10185
|
console.log(
|
|
9775
10186
|
`neat: ${svc.dir} detected as Cloudflare Workers
|
|
9776
10187
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -9781,7 +10192,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
9781
10192
|
);
|
|
9782
10193
|
} else if (outcome.outcome === "electron") {
|
|
9783
10194
|
electron++;
|
|
9784
|
-
const svcName =
|
|
10195
|
+
const svcName = import_node_path50.default.basename(svc.dir);
|
|
9785
10196
|
console.log(
|
|
9786
10197
|
`neat: ${svc.dir} detected as Electron
|
|
9787
10198
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -9862,10 +10273,6 @@ async function fetchDaemonHealth(restPort) {
|
|
|
9862
10273
|
});
|
|
9863
10274
|
});
|
|
9864
10275
|
}
|
|
9865
|
-
async function checkDaemonHealth(restPort) {
|
|
9866
|
-
const body = await fetchDaemonHealth(restPort);
|
|
9867
|
-
return body !== null;
|
|
9868
|
-
}
|
|
9869
10276
|
async function probeProjectHealth(restPort, name) {
|
|
9870
10277
|
return new Promise((resolve) => {
|
|
9871
10278
|
const req = import_node_http.default.get(
|
|
@@ -9941,12 +10348,6 @@ async function isPortFree(port) {
|
|
|
9941
10348
|
server.listen(port, "127.0.0.1");
|
|
9942
10349
|
});
|
|
9943
10350
|
}
|
|
9944
|
-
async function probePortsFree() {
|
|
9945
|
-
for (const port of NEAT_PORTS) {
|
|
9946
|
-
if (!await isPortFree(port)) return { free: false, held: port };
|
|
9947
|
-
}
|
|
9948
|
-
return { free: true };
|
|
9949
|
-
}
|
|
9950
10351
|
function formatPortCollisionMessage(port) {
|
|
9951
10352
|
return [
|
|
9952
10353
|
`neat: port ${port} is in use; the NEAT daemon needs it.`,
|
|
@@ -9954,11 +10355,82 @@ function formatPortCollisionMessage(port) {
|
|
|
9954
10355
|
` \`lsof -i :${port}\` to find the holding process.`
|
|
9955
10356
|
];
|
|
9956
10357
|
}
|
|
9957
|
-
|
|
9958
|
-
|
|
10358
|
+
var PORT_ALLOCATION_ATTEMPTS = 8;
|
|
10359
|
+
var PORT_STRIDE = 1;
|
|
10360
|
+
async function tripleFree(ports) {
|
|
10361
|
+
for (const p of [ports.rest, ports.otlp, ports.web]) {
|
|
10362
|
+
if (!await isPortFree(p)) return false;
|
|
10363
|
+
}
|
|
10364
|
+
return true;
|
|
10365
|
+
}
|
|
10366
|
+
async function allocatePorts() {
|
|
10367
|
+
const [baseRest, baseOtlp, baseWeb] = NEAT_PORTS;
|
|
10368
|
+
for (let i = 0; i < PORT_ALLOCATION_ATTEMPTS; i++) {
|
|
10369
|
+
const candidate = {
|
|
10370
|
+
rest: baseRest + i * PORT_STRIDE,
|
|
10371
|
+
otlp: baseOtlp + i * PORT_STRIDE,
|
|
10372
|
+
web: baseWeb + i * PORT_STRIDE
|
|
10373
|
+
};
|
|
10374
|
+
if (await tripleFree(candidate)) return candidate;
|
|
10375
|
+
}
|
|
10376
|
+
return null;
|
|
10377
|
+
}
|
|
10378
|
+
async function persistedPortsFor(scanPath) {
|
|
10379
|
+
const record = await readDaemonRecord(scanPath);
|
|
10380
|
+
if (!record) return null;
|
|
10381
|
+
return { rest: record.ports.rest, otlp: record.ports.otlp, web: record.ports.web };
|
|
10382
|
+
}
|
|
10383
|
+
async function acquireSpawnLock(scanPath) {
|
|
10384
|
+
const lockPath = import_node_path50.default.join(scanPath, "neat-out", "daemon.spawn.lock");
|
|
10385
|
+
await import_node_fs33.promises.mkdir(import_node_path50.default.dirname(lockPath), { recursive: true });
|
|
10386
|
+
const STALE_LOCK_MS = 6e4;
|
|
10387
|
+
try {
|
|
10388
|
+
const fd = await import_node_fs33.promises.open(lockPath, "wx");
|
|
10389
|
+
await fd.writeFile(`${process.pid}
|
|
10390
|
+
`, "utf8");
|
|
10391
|
+
await fd.close();
|
|
10392
|
+
return async () => {
|
|
10393
|
+
await import_node_fs33.promises.unlink(lockPath).catch(() => {
|
|
10394
|
+
});
|
|
10395
|
+
};
|
|
10396
|
+
} catch (err) {
|
|
10397
|
+
if (err.code !== "EEXIST") return null;
|
|
10398
|
+
try {
|
|
10399
|
+
const stat = await import_node_fs33.promises.stat(lockPath);
|
|
10400
|
+
if (Date.now() - stat.mtimeMs > STALE_LOCK_MS) {
|
|
10401
|
+
await import_node_fs33.promises.unlink(lockPath).catch(() => {
|
|
10402
|
+
});
|
|
10403
|
+
return acquireSpawnLock(scanPath);
|
|
10404
|
+
}
|
|
10405
|
+
} catch {
|
|
10406
|
+
return acquireSpawnLock(scanPath);
|
|
10407
|
+
}
|
|
10408
|
+
return null;
|
|
10409
|
+
}
|
|
10410
|
+
}
|
|
10411
|
+
async function waitForPeerDaemon(restPort, project, timeoutMs) {
|
|
10412
|
+
const deadline = Date.now() + timeoutMs;
|
|
10413
|
+
while (Date.now() < deadline) {
|
|
10414
|
+
if (await healthIsForProject(restPort, project)) return true;
|
|
10415
|
+
await new Promise((r) => setTimeout(r, PROBE_INTERVAL_MS));
|
|
10416
|
+
}
|
|
10417
|
+
return healthIsForProject(restPort, project);
|
|
10418
|
+
}
|
|
10419
|
+
async function healthIsForProject(restPort, project) {
|
|
10420
|
+
const body = await fetchDaemonHealth(restPort);
|
|
10421
|
+
if (body === null) return false;
|
|
10422
|
+
const named = body.project;
|
|
10423
|
+
if (typeof named === "string") return named === project;
|
|
10424
|
+
if (Array.isArray(body.projects)) {
|
|
10425
|
+
return body.projects.some((p) => p.name === project);
|
|
10426
|
+
}
|
|
10427
|
+
return false;
|
|
10428
|
+
}
|
|
10429
|
+
function spawnDaemonDetached(spec) {
|
|
10430
|
+
const here = import_node_path50.default.dirname(new URL(importMetaUrl).pathname);
|
|
9959
10431
|
const candidates = [
|
|
9960
|
-
|
|
9961
|
-
|
|
10432
|
+
import_node_path50.default.join(here, "neatd.cjs"),
|
|
10433
|
+
import_node_path50.default.join(here, "neatd.js")
|
|
9962
10434
|
];
|
|
9963
10435
|
let entry2 = null;
|
|
9964
10436
|
const fsSync = require("fs");
|
|
@@ -9978,6 +10450,13 @@ function spawnDaemonDetached() {
|
|
|
9978
10450
|
if (!hasToken && (!env.HOST || env.HOST.length === 0)) {
|
|
9979
10451
|
env.HOST = "127.0.0.1";
|
|
9980
10452
|
}
|
|
10453
|
+
if (spec) {
|
|
10454
|
+
env.NEAT_PROJECT = spec.project;
|
|
10455
|
+
env.NEAT_PROJECT_PATH = spec.projectPath;
|
|
10456
|
+
env.PORT = String(spec.ports.rest);
|
|
10457
|
+
env.OTEL_PORT = String(spec.ports.otlp);
|
|
10458
|
+
env.NEAT_WEB_PORT = String(spec.ports.web);
|
|
10459
|
+
}
|
|
9981
10460
|
const child = (0, import_node_child_process3.spawn)(process.execPath, [entry2, "start"], {
|
|
9982
10461
|
detached: true,
|
|
9983
10462
|
// stderr inherits the orchestrator's fd so the daemon's
|
|
@@ -10016,7 +10495,7 @@ async function runOrchestrator(opts) {
|
|
|
10016
10495
|
browser: "skipped"
|
|
10017
10496
|
}
|
|
10018
10497
|
};
|
|
10019
|
-
const stat = await
|
|
10498
|
+
const stat = await import_node_fs33.promises.stat(opts.scanPath).catch(() => null);
|
|
10020
10499
|
if (!stat || !stat.isDirectory()) {
|
|
10021
10500
|
console.error(`neat: ${opts.scanPath} is not a directory`);
|
|
10022
10501
|
result.exitCode = 2;
|
|
@@ -10097,48 +10576,75 @@ async function runOrchestrator(opts) {
|
|
|
10097
10576
|
result.exitCode = 1;
|
|
10098
10577
|
}
|
|
10099
10578
|
}
|
|
10100
|
-
const restPort = Number(process.env.PORT ?? 8080);
|
|
10101
10579
|
const timeoutMs = opts.daemonReadyTimeoutMs ?? DEFAULT_DAEMON_READY_TIMEOUT_MS;
|
|
10102
|
-
|
|
10580
|
+
const persistedPorts = await persistedPortsFor(opts.scanPath);
|
|
10581
|
+
let allocated = null;
|
|
10582
|
+
if (persistedPorts && await healthIsForProject(persistedPorts.rest, currentProjectName)) {
|
|
10103
10583
|
result.steps.daemon = "already-running";
|
|
10584
|
+
allocated = persistedPorts;
|
|
10104
10585
|
} else {
|
|
10105
|
-
|
|
10106
|
-
|
|
10107
|
-
|
|
10586
|
+
if (persistedPorts && await isPortFree(persistedPorts.rest) && await tripleFree(persistedPorts)) {
|
|
10587
|
+
allocated = persistedPorts;
|
|
10588
|
+
} else {
|
|
10589
|
+
allocated = await allocatePorts();
|
|
10590
|
+
}
|
|
10591
|
+
if (!allocated) {
|
|
10592
|
+
for (const line of formatPortCollisionMessage(NEAT_PORTS[0])) {
|
|
10108
10593
|
console.error(line);
|
|
10109
10594
|
}
|
|
10110
10595
|
result.exitCode = 3;
|
|
10111
10596
|
return result;
|
|
10112
10597
|
}
|
|
10113
|
-
|
|
10114
|
-
|
|
10115
|
-
|
|
10116
|
-
|
|
10117
|
-
|
|
10118
|
-
|
|
10119
|
-
|
|
10120
|
-
|
|
10121
|
-
|
|
10122
|
-
if (!ready.ready) {
|
|
10123
|
-
console.error(`neat: daemon did not become ready within ${timeoutMs}ms`);
|
|
10124
|
-
if (ready.stillBootstrapping.length > 0) {
|
|
10125
|
-
console.error(
|
|
10126
|
-
`neat: still bootstrapping: ${ready.stillBootstrapping.join(", ")}`
|
|
10127
|
-
);
|
|
10598
|
+
const release = await acquireSpawnLock(opts.scanPath);
|
|
10599
|
+
if (!release) {
|
|
10600
|
+
const reused = await waitForPeerDaemon(allocated.rest, currentProjectName, timeoutMs);
|
|
10601
|
+
if (reused) {
|
|
10602
|
+
result.steps.daemon = "already-running";
|
|
10603
|
+
} else {
|
|
10604
|
+
console.error("neat: another `neat` is spawning this project but its daemon did not come up in time");
|
|
10605
|
+
result.exitCode = 1;
|
|
10606
|
+
return result;
|
|
10128
10607
|
}
|
|
10129
|
-
|
|
10130
|
-
|
|
10608
|
+
} else {
|
|
10609
|
+
try {
|
|
10610
|
+
if (await healthIsForProject(allocated.rest, currentProjectName)) {
|
|
10611
|
+
result.steps.daemon = "already-running";
|
|
10612
|
+
} else {
|
|
10613
|
+
spawnDaemonDetached({
|
|
10614
|
+
project: currentProjectName,
|
|
10615
|
+
projectPath: opts.scanPath,
|
|
10616
|
+
ports: allocated
|
|
10617
|
+
});
|
|
10618
|
+
const ready = await waitForDaemonReady(allocated.rest, timeoutMs);
|
|
10619
|
+
result.steps.daemon = ready.ready ? "spawned" : "timed-out";
|
|
10620
|
+
if (!ready.ready) {
|
|
10621
|
+
console.error(`neat: daemon did not become ready within ${timeoutMs}ms`);
|
|
10622
|
+
if (ready.stillBootstrapping.length > 0) {
|
|
10623
|
+
console.error(`neat: still bootstrapping: ${ready.stillBootstrapping.join(", ")}`);
|
|
10624
|
+
}
|
|
10625
|
+
if (ready.brokenProjects.length > 0) {
|
|
10626
|
+
console.error(`neat: broken projects: ${ready.brokenProjects.join(", ")}`);
|
|
10627
|
+
}
|
|
10628
|
+
result.exitCode = 1;
|
|
10629
|
+
return result;
|
|
10630
|
+
}
|
|
10631
|
+
if (ready.brokenProjects.length > 0) {
|
|
10632
|
+
console.warn(
|
|
10633
|
+
`neat: ${ready.brokenProjects.length} project(s) reported broken: ${ready.brokenProjects.join(", ")}`
|
|
10634
|
+
);
|
|
10635
|
+
}
|
|
10636
|
+
}
|
|
10637
|
+
} catch (err) {
|
|
10638
|
+
console.error(`neat: daemon spawn failed \u2014 ${err.message}`);
|
|
10639
|
+
result.exitCode = 1;
|
|
10640
|
+
return result;
|
|
10641
|
+
} finally {
|
|
10642
|
+
await release();
|
|
10131
10643
|
}
|
|
10132
|
-
result.exitCode = 1;
|
|
10133
|
-
return result;
|
|
10134
|
-
}
|
|
10135
|
-
if (ready.brokenProjects.length > 0) {
|
|
10136
|
-
console.warn(
|
|
10137
|
-
`neat: ${ready.brokenProjects.length} project(s) reported broken: ${ready.brokenProjects.join(", ")}`
|
|
10138
|
-
);
|
|
10139
10644
|
}
|
|
10140
10645
|
}
|
|
10141
|
-
const
|
|
10646
|
+
const webPort = allocated?.web ?? NEAT_PORTS[2];
|
|
10647
|
+
const dashboardUrl = opts.dashboardUrl ?? `http://localhost:${webPort}`;
|
|
10142
10648
|
if (opts.noOpen || !process.stdout.isTTY) {
|
|
10143
10649
|
result.steps.browser = "skipped";
|
|
10144
10650
|
} else {
|
|
@@ -10173,11 +10679,11 @@ function printSummary(result, graph, dashboardUrl) {
|
|
|
10173
10679
|
|
|
10174
10680
|
// src/cli-verbs.ts
|
|
10175
10681
|
init_cjs_shims();
|
|
10176
|
-
var
|
|
10682
|
+
var import_node_path51 = __toESM(require("path"), 1);
|
|
10177
10683
|
|
|
10178
10684
|
// src/cli-client.ts
|
|
10179
10685
|
init_cjs_shims();
|
|
10180
|
-
var
|
|
10686
|
+
var import_types27 = require("@neat.is/types");
|
|
10181
10687
|
var HttpError = class extends Error {
|
|
10182
10688
|
constructor(status2, message, responseBody = "") {
|
|
10183
10689
|
super(message);
|
|
@@ -10202,10 +10708,10 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
10202
10708
|
const root = baseUrl.replace(/\/$/, "");
|
|
10203
10709
|
const authHeader = bearerToken && bearerToken.length > 0 ? { authorization: `Bearer ${bearerToken}` } : {};
|
|
10204
10710
|
return {
|
|
10205
|
-
async get(
|
|
10711
|
+
async get(path53) {
|
|
10206
10712
|
let res;
|
|
10207
10713
|
try {
|
|
10208
|
-
res = await fetch(`${root}${
|
|
10714
|
+
res = await fetch(`${root}${path53}`, {
|
|
10209
10715
|
headers: { ...authHeader }
|
|
10210
10716
|
});
|
|
10211
10717
|
} catch (err) {
|
|
@@ -10217,16 +10723,16 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
10217
10723
|
const body = await res.text().catch(() => "");
|
|
10218
10724
|
throw new HttpError(
|
|
10219
10725
|
res.status,
|
|
10220
|
-
`${res.status} ${res.statusText} on GET ${
|
|
10726
|
+
`${res.status} ${res.statusText} on GET ${path53}: ${body}`,
|
|
10221
10727
|
body
|
|
10222
10728
|
);
|
|
10223
10729
|
}
|
|
10224
10730
|
return await res.json();
|
|
10225
10731
|
},
|
|
10226
|
-
async post(
|
|
10732
|
+
async post(path53, body) {
|
|
10227
10733
|
let res;
|
|
10228
10734
|
try {
|
|
10229
|
-
res = await fetch(`${root}${
|
|
10735
|
+
res = await fetch(`${root}${path53}`, {
|
|
10230
10736
|
method: "POST",
|
|
10231
10737
|
headers: { "content-type": "application/json", ...authHeader },
|
|
10232
10738
|
body: JSON.stringify(body)
|
|
@@ -10240,7 +10746,7 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
10240
10746
|
const text = await res.text().catch(() => "");
|
|
10241
10747
|
throw new HttpError(
|
|
10242
10748
|
res.status,
|
|
10243
|
-
`${res.status} ${res.statusText} on POST ${
|
|
10749
|
+
`${res.status} ${res.statusText} on POST ${path53}: ${text}`,
|
|
10244
10750
|
text
|
|
10245
10751
|
);
|
|
10246
10752
|
}
|
|
@@ -10254,12 +10760,12 @@ function projectPath(project, suffix) {
|
|
|
10254
10760
|
}
|
|
10255
10761
|
async function runRootCause(client, input) {
|
|
10256
10762
|
const qs = input.errorId ? `?errorId=${encodeURIComponent(input.errorId)}` : "";
|
|
10257
|
-
const
|
|
10763
|
+
const path53 = projectPath(
|
|
10258
10764
|
input.project,
|
|
10259
10765
|
`/graph/root-cause/${encodeURIComponent(input.errorNode)}${qs}`
|
|
10260
10766
|
);
|
|
10261
10767
|
try {
|
|
10262
|
-
const result = await client.get(
|
|
10768
|
+
const result = await client.get(path53);
|
|
10263
10769
|
const arrowPath = result.traversalPath.join(" \u2190 ");
|
|
10264
10770
|
const provenances = result.edgeProvenances.length ? result.edgeProvenances.join(", ") : "(direct, no edges traversed)";
|
|
10265
10771
|
const summary = `Root cause for ${input.errorNode} is ${result.rootCauseNode}. ` + result.rootCauseReason + (result.fixRecommendation ? ` Recommended fix: ${result.fixRecommendation}.` : "");
|
|
@@ -10285,12 +10791,12 @@ async function runRootCause(client, input) {
|
|
|
10285
10791
|
}
|
|
10286
10792
|
async function runBlastRadius(client, input) {
|
|
10287
10793
|
const qs = input.depth !== void 0 ? `?depth=${input.depth}` : "";
|
|
10288
|
-
const
|
|
10794
|
+
const path53 = projectPath(
|
|
10289
10795
|
input.project,
|
|
10290
10796
|
`/graph/blast-radius/${encodeURIComponent(input.nodeId)}${qs}`
|
|
10291
10797
|
);
|
|
10292
10798
|
try {
|
|
10293
|
-
const result = await client.get(
|
|
10799
|
+
const result = await client.get(path53);
|
|
10294
10800
|
if (result.totalAffected === 0) {
|
|
10295
10801
|
return {
|
|
10296
10802
|
summary: `${result.origin} has no downstream dependencies. Nothing else would break if it failed.`
|
|
@@ -10319,17 +10825,17 @@ async function runBlastRadius(client, input) {
|
|
|
10319
10825
|
}
|
|
10320
10826
|
}
|
|
10321
10827
|
function formatBlastEntry(n) {
|
|
10322
|
-
const tag = n.edgeProvenance ===
|
|
10828
|
+
const tag = n.edgeProvenance === import_types27.Provenance.STALE ? " [STALE \u2014 last seen too long ago]" : "";
|
|
10323
10829
|
return ` \u2022 ${n.nodeId} (distance ${n.distance}, ${n.edgeProvenance})${tag}`;
|
|
10324
10830
|
}
|
|
10325
10831
|
async function runDependencies(client, input) {
|
|
10326
10832
|
const depth = input.depth ?? 3;
|
|
10327
|
-
const
|
|
10833
|
+
const path53 = projectPath(
|
|
10328
10834
|
input.project,
|
|
10329
10835
|
`/graph/dependencies/${encodeURIComponent(input.nodeId)}?depth=${depth}`
|
|
10330
10836
|
);
|
|
10331
10837
|
try {
|
|
10332
|
-
const result = await client.get(
|
|
10838
|
+
const result = await client.get(path53);
|
|
10333
10839
|
if (result.total === 0) {
|
|
10334
10840
|
return {
|
|
10335
10841
|
summary: depth === 1 ? `${input.nodeId} has no direct dependencies in the graph.` : `${input.nodeId} has no dependencies (BFS to depth ${depth}).`
|
|
@@ -10365,9 +10871,9 @@ async function runObservedDependencies(client, input) {
|
|
|
10365
10871
|
const edges = await client.get(
|
|
10366
10872
|
projectPath(input.project, `/graph/edges/${encodeURIComponent(input.nodeId)}`)
|
|
10367
10873
|
);
|
|
10368
|
-
const observed = edges.outbound.filter((e) => e.provenance ===
|
|
10874
|
+
const observed = edges.outbound.filter((e) => e.provenance === import_types27.Provenance.OBSERVED);
|
|
10369
10875
|
if (observed.length === 0) {
|
|
10370
|
-
const hasExtracted = edges.outbound.some((e) => e.provenance ===
|
|
10876
|
+
const hasExtracted = edges.outbound.some((e) => e.provenance === import_types27.Provenance.EXTRACTED);
|
|
10371
10877
|
const note = hasExtracted ? " Static (EXTRACTED) dependencies exist but no runtime traffic has been seen \u2014 is OTel running?" : "";
|
|
10372
10878
|
return { summary: `No OBSERVED dependencies for ${input.nodeId}.${note}` };
|
|
10373
10879
|
}
|
|
@@ -10375,7 +10881,7 @@ async function runObservedDependencies(client, input) {
|
|
|
10375
10881
|
return {
|
|
10376
10882
|
summary: `${input.nodeId} has ${observed.length} runtime dependenc${observed.length === 1 ? "y" : "ies"} confirmed by OTel.`,
|
|
10377
10883
|
block: blockLines.join("\n"),
|
|
10378
|
-
provenance:
|
|
10884
|
+
provenance: import_types27.Provenance.OBSERVED
|
|
10379
10885
|
};
|
|
10380
10886
|
} catch (err) {
|
|
10381
10887
|
if (err instanceof HttpError && err.status === 404) {
|
|
@@ -10410,9 +10916,9 @@ function formatDuration(ms) {
|
|
|
10410
10916
|
return `${Math.round(h / 24)}d`;
|
|
10411
10917
|
}
|
|
10412
10918
|
async function runIncidents(client, input) {
|
|
10413
|
-
const
|
|
10919
|
+
const path53 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
|
|
10414
10920
|
try {
|
|
10415
|
-
const body = await client.get(
|
|
10921
|
+
const body = await client.get(path53);
|
|
10416
10922
|
const events = body.events;
|
|
10417
10923
|
if (events.length === 0) {
|
|
10418
10924
|
return {
|
|
@@ -10429,7 +10935,7 @@ async function runIncidents(client, input) {
|
|
|
10429
10935
|
return {
|
|
10430
10936
|
summary: `${target} has ${body.total} recorded incident${body.total === 1 ? "" : "s"}; showing the ${ordered.length} most recent.`,
|
|
10431
10937
|
block: blockLines.join("\n"),
|
|
10432
|
-
provenance:
|
|
10938
|
+
provenance: import_types27.Provenance.OBSERVED
|
|
10433
10939
|
};
|
|
10434
10940
|
} catch (err) {
|
|
10435
10941
|
if (err instanceof HttpError && err.status === 404) {
|
|
@@ -10538,7 +11044,7 @@ async function runStaleEdges(client, input) {
|
|
|
10538
11044
|
return {
|
|
10539
11045
|
summary: `${events.length} stale-edge transition${events.length === 1 ? "" : "s"} recorded${input.edgeType ? ` for ${input.edgeType}` : ""}.`,
|
|
10540
11046
|
block: blockLines.join("\n"),
|
|
10541
|
-
provenance:
|
|
11047
|
+
provenance: import_types27.Provenance.STALE
|
|
10542
11048
|
};
|
|
10543
11049
|
}
|
|
10544
11050
|
async function runPolicies(client, input) {
|
|
@@ -10702,13 +11208,13 @@ async function resolveProjectEntry(opts) {
|
|
|
10702
11208
|
const cwd = opts.cwd ?? process.cwd();
|
|
10703
11209
|
const resolvedCwd = await normalizeProjectPath(cwd);
|
|
10704
11210
|
for (const entry2 of entries) {
|
|
10705
|
-
if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${
|
|
11211
|
+
if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${import_node_path51.default.sep}`)) {
|
|
10706
11212
|
return entry2;
|
|
10707
11213
|
}
|
|
10708
11214
|
}
|
|
10709
11215
|
return null;
|
|
10710
11216
|
}
|
|
10711
|
-
async function
|
|
11217
|
+
async function checkDaemonHealth(baseUrl) {
|
|
10712
11218
|
try {
|
|
10713
11219
|
const res = await fetch(`${baseUrl.replace(/\/$/, "")}/health`, {
|
|
10714
11220
|
signal: AbortSignal.timeout(1500)
|
|
@@ -10809,7 +11315,7 @@ async function runSync(opts) {
|
|
|
10809
11315
|
}
|
|
10810
11316
|
} else {
|
|
10811
11317
|
const daemonUrl = opts.daemonUrl ?? process.env.NEAT_API_URL ?? "http://localhost:8080";
|
|
10812
|
-
const healthy = await
|
|
11318
|
+
const healthy = await checkDaemonHealth(daemonUrl);
|
|
10813
11319
|
if (healthy) {
|
|
10814
11320
|
try {
|
|
10815
11321
|
await pushSnapshotToRemote({
|
|
@@ -10852,7 +11358,7 @@ async function runSync(opts) {
|
|
|
10852
11358
|
}
|
|
10853
11359
|
|
|
10854
11360
|
// src/cli.ts
|
|
10855
|
-
var
|
|
11361
|
+
var import_types28 = require("@neat.is/types");
|
|
10856
11362
|
function isNpxInvocation() {
|
|
10857
11363
|
if (process.env.npm_command === "exec") return true;
|
|
10858
11364
|
const execpath = process.env.npm_execpath ?? "";
|
|
@@ -10883,12 +11389,18 @@ function usage() {
|
|
|
10883
11389
|
console.log(" watch <path> Start neat-core, watch <path>, re-extract on changes.");
|
|
10884
11390
|
console.log(" PORT (default 8080), OTEL_PORT (4318), HOST (0.0.0.0)");
|
|
10885
11391
|
console.log(" control listeners. NEAT_OTLP_GRPC=true also opens 4317.");
|
|
10886
|
-
console.log(" list
|
|
10887
|
-
console.log("
|
|
10888
|
-
console.log("
|
|
11392
|
+
console.log(" list Report the daemons running on this machine (alias: ps).");
|
|
11393
|
+
console.log(" Reads ~/.neat/daemons/ and folds in any registered");
|
|
11394
|
+
console.log(" project no daemon has self-described yet.");
|
|
11395
|
+
console.log(" ps Alias of list.");
|
|
11396
|
+
console.log(" pause <name> Stop a project's daemon until it is started again.");
|
|
11397
|
+
console.log(" resume <name> Bring a paused project back; for a stopped daemon, re-run");
|
|
11398
|
+
console.log(" `neat <path>` to start it.");
|
|
10889
11399
|
console.log(" uninstall <name>");
|
|
10890
|
-
console.log("
|
|
11400
|
+
console.log(" Stop a project's daemon and retire it. Does not touch");
|
|
10891
11401
|
console.log(" neat-out/, policy.json, or any user file.");
|
|
11402
|
+
console.log(" prune Drop registry entries whose path is gone from disk.");
|
|
11403
|
+
console.log(" Flags: --json emit the removed list as JSON");
|
|
10892
11404
|
console.log(" version Print the installed @neat.is/core version and exit.");
|
|
10893
11405
|
console.log(" Aliases: --version, -v.");
|
|
10894
11406
|
console.log(" skill Install or print the Claude Code MCP drop-in.");
|
|
@@ -10948,6 +11460,7 @@ function usage() {
|
|
|
10948
11460
|
console.log("");
|
|
10949
11461
|
console.log("environment:");
|
|
10950
11462
|
console.log(" NEAT_API_URL base URL for the core REST API (default http://localhost:8080)");
|
|
11463
|
+
console.log(" alias: NEAT_CORE_URL (the name the MCP server reads)");
|
|
10951
11464
|
console.log(" NEAT_PROJECT project name when --project isn't passed");
|
|
10952
11465
|
}
|
|
10953
11466
|
var STRING_FLAGS = [
|
|
@@ -11081,6 +11594,14 @@ function printVersion() {
|
|
|
11081
11594
|
process.stdout.write(`${readPackageVersion()}
|
|
11082
11595
|
`);
|
|
11083
11596
|
}
|
|
11597
|
+
function formatMachineProjectRow(r) {
|
|
11598
|
+
if (r.ports) {
|
|
11599
|
+
const where = r.pid !== void 0 ? ` pid=${r.pid}` : "";
|
|
11600
|
+
return `${r.project} ${r.state} rest=${r.ports.rest} otlp=${r.ports.otlp} web=${r.ports.web} ${r.projectPath}${where}`;
|
|
11601
|
+
}
|
|
11602
|
+
const status2 = r.registryStatus ? ` (${r.registryStatus})` : "";
|
|
11603
|
+
return `${r.project} ${r.state}${status2} ${r.projectPath}`;
|
|
11604
|
+
}
|
|
11084
11605
|
function printDiscoveryReport(opts, services) {
|
|
11085
11606
|
const languages = [...new Set(services.map((s) => s.node.language))].sort();
|
|
11086
11607
|
const mode = opts.dryRun ? "dry-run" : opts.apply ? "apply" : "patch-only";
|
|
@@ -11119,7 +11640,7 @@ async function buildPatchSections(services, project) {
|
|
|
11119
11640
|
}
|
|
11120
11641
|
async function runInit(opts) {
|
|
11121
11642
|
const written = [];
|
|
11122
|
-
const stat = await
|
|
11643
|
+
const stat = await import_node_fs34.promises.stat(opts.scanPath).catch(() => null);
|
|
11123
11644
|
if (!stat || !stat.isDirectory()) {
|
|
11124
11645
|
console.error(`neat init: ${opts.scanPath} is not a directory`);
|
|
11125
11646
|
return { exitCode: 2, writtenFiles: written };
|
|
@@ -11128,13 +11649,13 @@ async function runInit(opts) {
|
|
|
11128
11649
|
printDiscoveryReport(opts, services);
|
|
11129
11650
|
const sections = opts.noInstall ? [] : await buildPatchSections(services, opts.project);
|
|
11130
11651
|
const patch = renderPatch(sections);
|
|
11131
|
-
const patchPath =
|
|
11652
|
+
const patchPath = import_node_path52.default.join(opts.scanPath, "neat.patch");
|
|
11132
11653
|
if (opts.dryRun) {
|
|
11133
|
-
await
|
|
11654
|
+
await import_node_fs34.promises.writeFile(patchPath, patch, "utf8");
|
|
11134
11655
|
written.push(patchPath);
|
|
11135
11656
|
console.log(`dry-run: patch written to ${patchPath}`);
|
|
11136
|
-
const gitignorePath =
|
|
11137
|
-
const gitignoreExists = await
|
|
11657
|
+
const gitignorePath = import_node_path52.default.join(opts.scanPath, ".gitignore");
|
|
11658
|
+
const gitignoreExists = await import_node_fs34.promises.stat(gitignorePath).then(() => true).catch(() => false);
|
|
11138
11659
|
const verb = gitignoreExists ? "append" : "create";
|
|
11139
11660
|
console.log(`dry-run: would ${verb} ${gitignorePath} (add neat-out/)`);
|
|
11140
11661
|
console.log("rerun without --dry-run to register and snapshot.");
|
|
@@ -11145,9 +11666,9 @@ async function runInit(opts) {
|
|
|
11145
11666
|
const graph = getGraph(graphKey);
|
|
11146
11667
|
const projectPaths = pathsForProject(
|
|
11147
11668
|
graphKey,
|
|
11148
|
-
|
|
11669
|
+
import_node_path52.default.join(opts.scanPath, "neat-out")
|
|
11149
11670
|
);
|
|
11150
|
-
const errorsPath =
|
|
11671
|
+
const errorsPath = import_node_path52.default.join(import_node_path52.default.dirname(opts.outPath), import_node_path52.default.basename(projectPaths.errorsPath));
|
|
11151
11672
|
const result = await extractFromDirectory(graph, opts.scanPath, { errorsPath });
|
|
11152
11673
|
await saveGraphToDisk(graph, opts.outPath);
|
|
11153
11674
|
written.push(opts.outPath);
|
|
@@ -11226,7 +11747,7 @@ async function runInit(opts) {
|
|
|
11226
11747
|
console.log("Run `npm install` (or your language equivalent) to refresh lockfiles.");
|
|
11227
11748
|
}
|
|
11228
11749
|
} else {
|
|
11229
|
-
await
|
|
11750
|
+
await import_node_fs34.promises.writeFile(patchPath, patch, "utf8");
|
|
11230
11751
|
written.push(patchPath);
|
|
11231
11752
|
}
|
|
11232
11753
|
}
|
|
@@ -11259,16 +11780,16 @@ var CLAUDE_SKILL_CONFIG = {
|
|
|
11259
11780
|
command: "npx",
|
|
11260
11781
|
args: ["-y", "@neat.is/mcp"],
|
|
11261
11782
|
env: {
|
|
11262
|
-
|
|
11783
|
+
NEAT_CORE_URL: "http://localhost:8080"
|
|
11263
11784
|
}
|
|
11264
11785
|
}
|
|
11265
11786
|
}
|
|
11266
11787
|
};
|
|
11267
11788
|
function claudeConfigPath() {
|
|
11268
11789
|
const override = process.env.NEAT_CLAUDE_CONFIG;
|
|
11269
|
-
if (override && override.length > 0) return
|
|
11790
|
+
if (override && override.length > 0) return import_node_path52.default.resolve(override);
|
|
11270
11791
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
11271
|
-
return
|
|
11792
|
+
return import_node_path52.default.join(home, ".claude.json");
|
|
11272
11793
|
}
|
|
11273
11794
|
async function runSkill(opts) {
|
|
11274
11795
|
const snippet2 = JSON.stringify(CLAUDE_SKILL_CONFIG, null, 2) + "\n";
|
|
@@ -11280,7 +11801,7 @@ async function runSkill(opts) {
|
|
|
11280
11801
|
const target = claudeConfigPath();
|
|
11281
11802
|
let existing = {};
|
|
11282
11803
|
try {
|
|
11283
|
-
existing = JSON.parse(await
|
|
11804
|
+
existing = JSON.parse(await import_node_fs34.promises.readFile(target, "utf8"));
|
|
11284
11805
|
} catch (err) {
|
|
11285
11806
|
if (err.code !== "ENOENT") {
|
|
11286
11807
|
console.error(`neat skill: failed to read ${target} \u2014 ${err.message}`);
|
|
@@ -11292,8 +11813,8 @@ async function runSkill(opts) {
|
|
|
11292
11813
|
...existing,
|
|
11293
11814
|
mcpServers: { ...mcp, neat: CLAUDE_SKILL_CONFIG.mcpServers.neat }
|
|
11294
11815
|
};
|
|
11295
|
-
await
|
|
11296
|
-
await
|
|
11816
|
+
await import_node_fs34.promises.mkdir(import_node_path52.default.dirname(target), { recursive: true });
|
|
11817
|
+
await import_node_fs34.promises.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
11297
11818
|
console.log(`neat skill: wrote mcpServers.neat to ${target}`);
|
|
11298
11819
|
console.log("restart Claude Code to pick up the new MCP server.");
|
|
11299
11820
|
return { exitCode: 0 };
|
|
@@ -11305,6 +11826,9 @@ async function runSkill(opts) {
|
|
|
11305
11826
|
console.log("");
|
|
11306
11827
|
console.log("Manual install: copy mcpServers.neat from --print-config into ~/.claude.json,");
|
|
11307
11828
|
console.log("then restart Claude Code. See packages/claude-skill/SKILL.md for the tool list.");
|
|
11829
|
+
console.log("");
|
|
11830
|
+
console.log("The MCP server reads NEAT_CORE_URL for the daemon URL \u2014 point it at a");
|
|
11831
|
+
console.log("non-default daemon by editing that value in the generated config.");
|
|
11308
11832
|
return { exitCode: 0 };
|
|
11309
11833
|
}
|
|
11310
11834
|
async function main() {
|
|
@@ -11339,12 +11863,12 @@ async function main() {
|
|
|
11339
11863
|
console.error("neat init: --apply and --dry-run are mutually exclusive");
|
|
11340
11864
|
process.exit(2);
|
|
11341
11865
|
}
|
|
11342
|
-
const scanPath =
|
|
11866
|
+
const scanPath = import_node_path52.default.resolve(target);
|
|
11343
11867
|
const projectExplicit = parsed.project !== null;
|
|
11344
|
-
const projectName = projectExplicit ? project :
|
|
11868
|
+
const projectName = projectExplicit ? project : import_node_path52.default.basename(scanPath);
|
|
11345
11869
|
const projectKey = projectExplicit ? project : DEFAULT_PROJECT;
|
|
11346
|
-
const fallback = pathsForProject(projectKey,
|
|
11347
|
-
const outPath =
|
|
11870
|
+
const fallback = pathsForProject(projectKey, import_node_path52.default.join(scanPath, "neat-out")).snapshotPath;
|
|
11871
|
+
const outPath = import_node_path52.default.resolve(process.env.NEAT_OUT_PATH ?? fallback);
|
|
11348
11872
|
const result = await runInit({
|
|
11349
11873
|
scanPath,
|
|
11350
11874
|
outPath,
|
|
@@ -11365,21 +11889,21 @@ async function main() {
|
|
|
11365
11889
|
usage();
|
|
11366
11890
|
process.exit(2);
|
|
11367
11891
|
}
|
|
11368
|
-
const scanPath =
|
|
11369
|
-
const stat = await
|
|
11892
|
+
const scanPath = import_node_path52.default.resolve(target);
|
|
11893
|
+
const stat = await import_node_fs34.promises.stat(scanPath).catch(() => null);
|
|
11370
11894
|
if (!stat || !stat.isDirectory()) {
|
|
11371
11895
|
console.error(`neat watch: ${scanPath} is not a directory`);
|
|
11372
11896
|
process.exit(2);
|
|
11373
11897
|
}
|
|
11374
|
-
const projectPaths = pathsForProject(project,
|
|
11375
|
-
const outPath =
|
|
11376
|
-
const errorsPath =
|
|
11377
|
-
process.env.NEAT_ERRORS_PATH ??
|
|
11898
|
+
const projectPaths = pathsForProject(project, import_node_path52.default.join(scanPath, "neat-out"));
|
|
11899
|
+
const outPath = import_node_path52.default.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
|
|
11900
|
+
const errorsPath = import_node_path52.default.resolve(
|
|
11901
|
+
process.env.NEAT_ERRORS_PATH ?? import_node_path52.default.join(import_node_path52.default.dirname(outPath), import_node_path52.default.basename(projectPaths.errorsPath))
|
|
11378
11902
|
);
|
|
11379
|
-
const staleEventsPath =
|
|
11380
|
-
process.env.NEAT_STALE_EVENTS_PATH ??
|
|
11903
|
+
const staleEventsPath = import_node_path52.default.resolve(
|
|
11904
|
+
process.env.NEAT_STALE_EVENTS_PATH ?? import_node_path52.default.join(import_node_path52.default.dirname(outPath), import_node_path52.default.basename(projectPaths.staleEventsPath))
|
|
11381
11905
|
);
|
|
11382
|
-
const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ?
|
|
11906
|
+
const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? import_node_path52.default.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
|
|
11383
11907
|
const handle = await startWatch(getGraph(project), {
|
|
11384
11908
|
scanPath,
|
|
11385
11909
|
outPath,
|
|
@@ -11406,16 +11930,14 @@ async function main() {
|
|
|
11406
11930
|
process.on("SIGINT", shutdown);
|
|
11407
11931
|
return;
|
|
11408
11932
|
}
|
|
11409
|
-
if (cmd === "list") {
|
|
11410
|
-
const
|
|
11411
|
-
if (
|
|
11412
|
-
console.log("no projects registered. run `neat init <path>` to register one.");
|
|
11933
|
+
if (cmd === "list" || cmd === "ps") {
|
|
11934
|
+
const rows = await listMachineProjects();
|
|
11935
|
+
if (rows.length === 0) {
|
|
11936
|
+
console.log("no daemons running and no projects registered. run `neat init <path>` to register one.");
|
|
11413
11937
|
return;
|
|
11414
11938
|
}
|
|
11415
|
-
for (const
|
|
11416
|
-
|
|
11417
|
-
const langs = p.languages.length > 0 ? p.languages.join(",") : "-";
|
|
11418
|
-
console.log(`${p.name} ${p.status} ${langs} ${p.path} last-seen=${seen}`);
|
|
11939
|
+
for (const r of rows) {
|
|
11940
|
+
console.log(formatMachineProjectRow(r));
|
|
11419
11941
|
}
|
|
11420
11942
|
return;
|
|
11421
11943
|
}
|
|
@@ -11426,6 +11948,15 @@ async function main() {
|
|
|
11426
11948
|
usage();
|
|
11427
11949
|
process.exit(2);
|
|
11428
11950
|
}
|
|
11951
|
+
const daemon = await findDaemonByProject(name);
|
|
11952
|
+
if (daemon) {
|
|
11953
|
+
if (daemon.live && signalDaemonStop(daemon.record.pid)) {
|
|
11954
|
+
console.log(`paused: ${name} (${daemon.record.projectPath}) \u2014 stopped daemon pid ${daemon.record.pid}`);
|
|
11955
|
+
} else {
|
|
11956
|
+
console.log(`paused: ${name} (${daemon.record.projectPath}) \u2014 daemon was not running`);
|
|
11957
|
+
}
|
|
11958
|
+
return;
|
|
11959
|
+
}
|
|
11429
11960
|
try {
|
|
11430
11961
|
const entry2 = await setStatus(name, "paused");
|
|
11431
11962
|
console.log(`paused: ${entry2.name} (${entry2.path})`);
|
|
@@ -11442,6 +11973,15 @@ async function main() {
|
|
|
11442
11973
|
usage();
|
|
11443
11974
|
process.exit(2);
|
|
11444
11975
|
}
|
|
11976
|
+
const daemon = await findDaemonByProject(name);
|
|
11977
|
+
if (daemon) {
|
|
11978
|
+
if (daemon.live) {
|
|
11979
|
+
console.log(`resume: ${name} (${daemon.record.projectPath}) \u2014 daemon already running`);
|
|
11980
|
+
} else {
|
|
11981
|
+
console.log(`resume: ${name} (${daemon.record.projectPath}) \u2014 run \`neat ${daemon.record.projectPath}\` to start its daemon again`);
|
|
11982
|
+
}
|
|
11983
|
+
return;
|
|
11984
|
+
}
|
|
11445
11985
|
try {
|
|
11446
11986
|
const entry2 = await setStatus(name, "active");
|
|
11447
11987
|
console.log(`resumed: ${entry2.name} (${entry2.path})`);
|
|
@@ -11463,15 +12003,36 @@ async function main() {
|
|
|
11463
12003
|
usage();
|
|
11464
12004
|
process.exit(2);
|
|
11465
12005
|
}
|
|
12006
|
+
const daemon = await findDaemonByProject(name);
|
|
11466
12007
|
const removed = await removeProject(name);
|
|
11467
|
-
if (!removed) {
|
|
12008
|
+
if (!daemon && !removed) {
|
|
11468
12009
|
console.error(`neat uninstall: no project named "${name}"`);
|
|
11469
12010
|
process.exit(1);
|
|
11470
12011
|
}
|
|
11471
|
-
|
|
12012
|
+
const projectPath2 = daemon?.record.projectPath ?? removed?.path ?? "(unknown path)";
|
|
12013
|
+
if (daemon) {
|
|
12014
|
+
if (daemon.live && signalDaemonStop(daemon.record.pid)) {
|
|
12015
|
+
console.log(`uninstall: ${name} \u2014 stopped daemon pid ${daemon.record.pid}`);
|
|
12016
|
+
}
|
|
12017
|
+
await removeDaemonRecord(daemon.source);
|
|
12018
|
+
}
|
|
12019
|
+
console.log(`unregistered: ${name} (${projectPath2})`);
|
|
11472
12020
|
console.log("note: neat-out/, policy.json, and other files at the project path were left in place.");
|
|
11473
12021
|
return;
|
|
11474
12022
|
}
|
|
12023
|
+
if (cmd === "prune") {
|
|
12024
|
+
const removed = await pruneRegistry({ ttlMs: 0 });
|
|
12025
|
+
if (parsed.json) {
|
|
12026
|
+
console.log(JSON.stringify(removed.map((p) => ({ name: p.name, path: p.path })), null, 2));
|
|
12027
|
+
return;
|
|
12028
|
+
}
|
|
12029
|
+
if (removed.length === 0) {
|
|
12030
|
+
console.log("nothing to prune \u2014 every registered project path still exists.");
|
|
12031
|
+
return;
|
|
12032
|
+
}
|
|
12033
|
+
console.log(`pruned ${removed.length} project${removed.length === 1 ? "" : "s"}: ${removed.map((p) => p.name).join(", ")}`);
|
|
12034
|
+
return;
|
|
12035
|
+
}
|
|
11475
12036
|
if (cmd === "deploy") {
|
|
11476
12037
|
const artifact = await runDeploy();
|
|
11477
12038
|
const block = renderOtelEnvBlock2(artifact.token);
|
|
@@ -11525,11 +12086,11 @@ async function main() {
|
|
|
11525
12086
|
process.exit(1);
|
|
11526
12087
|
}
|
|
11527
12088
|
async function tryOrchestrator(cmd, parsed) {
|
|
11528
|
-
const scanPath =
|
|
11529
|
-
const stat = await
|
|
12089
|
+
const scanPath = import_node_path52.default.resolve(cmd);
|
|
12090
|
+
const stat = await import_node_fs34.promises.stat(scanPath).catch(() => null);
|
|
11530
12091
|
if (!stat || !stat.isDirectory()) return null;
|
|
11531
12092
|
const projectExplicit = parsed.project !== null;
|
|
11532
|
-
const projectName = projectExplicit ? parsed.project :
|
|
12093
|
+
const projectName = projectExplicit ? parsed.project : import_node_path52.default.basename(scanPath);
|
|
11533
12094
|
const result = await runOrchestrator({
|
|
11534
12095
|
scanPath,
|
|
11535
12096
|
project: projectName,
|
|
@@ -11559,12 +12120,44 @@ function resolveProjectFlag(parsed) {
|
|
|
11559
12120
|
if (env && env.length > 0 && env !== DEFAULT_PROJECT) return env;
|
|
11560
12121
|
return void 0;
|
|
11561
12122
|
}
|
|
12123
|
+
var ProjectResolutionError = class extends Error {
|
|
12124
|
+
constructor(message, exitCode = 2) {
|
|
12125
|
+
super(message);
|
|
12126
|
+
this.exitCode = exitCode;
|
|
12127
|
+
this.name = "ProjectResolutionError";
|
|
12128
|
+
}
|
|
12129
|
+
exitCode;
|
|
12130
|
+
};
|
|
12131
|
+
async function resolveProjectForVerb(client, parsed) {
|
|
12132
|
+
const explicit = resolveProjectFlag(parsed);
|
|
12133
|
+
if (explicit) return explicit;
|
|
12134
|
+
const projects = await client.get("/projects");
|
|
12135
|
+
if (projects.some((p) => p.name === DEFAULT_PROJECT)) {
|
|
12136
|
+
return void 0;
|
|
12137
|
+
}
|
|
12138
|
+
if (projects.length === 1) {
|
|
12139
|
+
return projects[0].name;
|
|
12140
|
+
}
|
|
12141
|
+
if (projects.length === 0) {
|
|
12142
|
+
throw new ProjectResolutionError(
|
|
12143
|
+
"No projects are registered with the daemon yet. Run `npx neat.is` in a repo to build a graph first, then re-run this command."
|
|
12144
|
+
);
|
|
12145
|
+
}
|
|
12146
|
+
const names = projects.map((p) => p.name).sort().map((n) => ` ${n}`).join("\n");
|
|
12147
|
+
throw new ProjectResolutionError(
|
|
12148
|
+
`Several projects are registered and none is named "default", so I can't pick one for you.
|
|
12149
|
+
Pass --project <name> to choose:
|
|
12150
|
+
${names}`
|
|
12151
|
+
);
|
|
12152
|
+
}
|
|
12153
|
+
function resolveDaemonUrl() {
|
|
12154
|
+
return process.env.NEAT_API_URL ?? process.env.NEAT_CORE_URL ?? "http://localhost:8080";
|
|
12155
|
+
}
|
|
11562
12156
|
async function runQueryVerb(cmd, parsed) {
|
|
11563
|
-
const baseUrl =
|
|
12157
|
+
const baseUrl = resolveDaemonUrl();
|
|
11564
12158
|
const client = createHttpClient(baseUrl, resolveAuthToken());
|
|
11565
|
-
const project = resolveProjectFlag(parsed);
|
|
11566
12159
|
const positional = parsed.positional;
|
|
11567
|
-
let
|
|
12160
|
+
let makeWork;
|
|
11568
12161
|
switch (cmd) {
|
|
11569
12162
|
case "root-cause": {
|
|
11570
12163
|
const node = positional[0];
|
|
@@ -11572,7 +12165,7 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
11572
12165
|
console.error("neat root-cause: missing <node-id>");
|
|
11573
12166
|
return 2;
|
|
11574
12167
|
}
|
|
11575
|
-
|
|
12168
|
+
makeWork = (project) => runRootCause(client, {
|
|
11576
12169
|
errorNode: node,
|
|
11577
12170
|
...parsed.errorId ? { errorId: parsed.errorId } : {},
|
|
11578
12171
|
...project ? { project } : {}
|
|
@@ -11585,7 +12178,7 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
11585
12178
|
console.error("neat blast-radius: missing <node-id>");
|
|
11586
12179
|
return 2;
|
|
11587
12180
|
}
|
|
11588
|
-
|
|
12181
|
+
makeWork = (project) => runBlastRadius(client, {
|
|
11589
12182
|
nodeId: node,
|
|
11590
12183
|
...parsed.depth !== null ? { depth: parsed.depth } : {},
|
|
11591
12184
|
...project ? { project } : {}
|
|
@@ -11598,7 +12191,7 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
11598
12191
|
console.error("neat dependencies: missing <node-id>");
|
|
11599
12192
|
return 2;
|
|
11600
12193
|
}
|
|
11601
|
-
|
|
12194
|
+
makeWork = (project) => runDependencies(client, {
|
|
11602
12195
|
nodeId: node,
|
|
11603
12196
|
...parsed.depth !== null ? { depth: parsed.depth } : {},
|
|
11604
12197
|
...project ? { project } : {}
|
|
@@ -11611,14 +12204,14 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
11611
12204
|
console.error("neat observed-dependencies: missing <node-id>");
|
|
11612
12205
|
return 2;
|
|
11613
12206
|
}
|
|
11614
|
-
|
|
12207
|
+
makeWork = (project) => runObservedDependencies(client, {
|
|
11615
12208
|
nodeId: node,
|
|
11616
12209
|
...project ? { project } : {}
|
|
11617
12210
|
});
|
|
11618
12211
|
break;
|
|
11619
12212
|
}
|
|
11620
12213
|
case "incidents": {
|
|
11621
|
-
|
|
12214
|
+
makeWork = (project) => runIncidents(client, {
|
|
11622
12215
|
...positional[0] ? { nodeId: positional[0] } : {},
|
|
11623
12216
|
...parsed.limit !== null ? { limit: parsed.limit } : {},
|
|
11624
12217
|
...project ? { project } : {}
|
|
@@ -11631,7 +12224,7 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
11631
12224
|
console.error("neat search: missing <query>");
|
|
11632
12225
|
return 2;
|
|
11633
12226
|
}
|
|
11634
|
-
|
|
12227
|
+
makeWork = (project) => runSearch(client, { query: q, ...project ? { project } : {} });
|
|
11635
12228
|
break;
|
|
11636
12229
|
}
|
|
11637
12230
|
case "diff": {
|
|
@@ -11640,14 +12233,14 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
11640
12233
|
console.error("neat diff: --against <snapshot-path> is required");
|
|
11641
12234
|
return 2;
|
|
11642
12235
|
}
|
|
11643
|
-
|
|
12236
|
+
makeWork = (project) => runDiff(client, {
|
|
11644
12237
|
againstSnapshot: against,
|
|
11645
12238
|
...project ? { project } : {}
|
|
11646
12239
|
});
|
|
11647
12240
|
break;
|
|
11648
12241
|
}
|
|
11649
12242
|
case "stale-edges": {
|
|
11650
|
-
|
|
12243
|
+
makeWork = (project) => runStaleEdges(client, {
|
|
11651
12244
|
...parsed.limit !== null ? { limit: parsed.limit } : {},
|
|
11652
12245
|
...parsed.edgeType ? { edgeType: parsed.edgeType } : {},
|
|
11653
12246
|
...project ? { project } : {}
|
|
@@ -11666,7 +12259,7 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
11666
12259
|
return 2;
|
|
11667
12260
|
}
|
|
11668
12261
|
}
|
|
11669
|
-
|
|
12262
|
+
makeWork = (project) => runPolicies(client, {
|
|
11670
12263
|
...parsed.node ? { nodeId: parsed.node } : {},
|
|
11671
12264
|
...hypothetical ? { hypotheticalAction: hypothetical } : {},
|
|
11672
12265
|
...project ? { project } : {}
|
|
@@ -11679,10 +12272,10 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
11679
12272
|
const parts = parsed.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
11680
12273
|
const out = [];
|
|
11681
12274
|
for (const p of parts) {
|
|
11682
|
-
const r =
|
|
12275
|
+
const r = import_types28.DivergenceTypeSchema.safeParse(p);
|
|
11683
12276
|
if (!r.success) {
|
|
11684
12277
|
console.error(
|
|
11685
|
-
`neat divergences: unknown --type "${p}". allowed: ${
|
|
12278
|
+
`neat divergences: unknown --type "${p}". allowed: ${import_types28.DivergenceTypeSchema.options.join(", ")}`
|
|
11686
12279
|
);
|
|
11687
12280
|
return 2;
|
|
11688
12281
|
}
|
|
@@ -11690,7 +12283,7 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
11690
12283
|
}
|
|
11691
12284
|
typeFilter = out;
|
|
11692
12285
|
}
|
|
11693
|
-
|
|
12286
|
+
makeWork = (project) => runDivergences(client, {
|
|
11694
12287
|
...typeFilter ? { type: typeFilter } : {},
|
|
11695
12288
|
...parsed.minConfidence !== null ? { minConfidence: parsed.minConfidence } : {},
|
|
11696
12289
|
...parsed.node ? { node: parsed.node } : {},
|
|
@@ -11703,16 +12296,21 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
11703
12296
|
return 2;
|
|
11704
12297
|
}
|
|
11705
12298
|
try {
|
|
11706
|
-
const
|
|
12299
|
+
const project = await resolveProjectForVerb(client, parsed);
|
|
12300
|
+
const result = await makeWork(project);
|
|
11707
12301
|
if (parsed.json) process.stdout.write(formatJson(result) + "\n");
|
|
11708
12302
|
else process.stdout.write(formatHuman(result) + "\n");
|
|
11709
12303
|
return 0;
|
|
11710
12304
|
} catch (err) {
|
|
12305
|
+
if (err instanceof ProjectResolutionError) {
|
|
12306
|
+
console.error(`neat ${cmd}: ${err.message}`);
|
|
12307
|
+
return err.exitCode;
|
|
12308
|
+
}
|
|
11711
12309
|
if (err instanceof HttpError) {
|
|
11712
12310
|
const detail = err.responseBody.length > 0 ? err.responseBody : err.message;
|
|
11713
12311
|
console.error(`neat ${cmd}: ${detail.trim()}`);
|
|
11714
12312
|
} else if (err instanceof TransportError) {
|
|
11715
|
-
console.error(`neat ${cmd}: ${err.message}. Is the daemon running? (NEAT_API_URL=${
|
|
12313
|
+
console.error(`neat ${cmd}: ${err.message}. Is the daemon running? (NEAT_API_URL=${resolveDaemonUrl()})`);
|
|
11716
12314
|
} else {
|
|
11717
12315
|
console.error(`neat ${cmd}: ${err.message}`);
|
|
11718
12316
|
}
|
|
@@ -11729,6 +12327,7 @@ if (/[\\/]cli\.(?:cjs|js)$/.test(entry) || entry.endsWith("/cli") || entry.endsW
|
|
|
11729
12327
|
// Annotate the CommonJS export names for ESM import in node:
|
|
11730
12328
|
0 && (module.exports = {
|
|
11731
12329
|
CLAUDE_SKILL_CONFIG,
|
|
12330
|
+
ProjectResolutionError,
|
|
11732
12331
|
QUERY_VERBS,
|
|
11733
12332
|
commandPrefix,
|
|
11734
12333
|
isNpxInvocation,
|
|
@@ -11736,6 +12335,7 @@ if (/[\\/]cli\.(?:cjs|js)$/.test(entry) || entry.endsWith("/cli") || entry.endsW
|
|
|
11736
12335
|
parseArgs,
|
|
11737
12336
|
printBanner,
|
|
11738
12337
|
readPackageVersion,
|
|
12338
|
+
resolveProjectForVerb,
|
|
11739
12339
|
runInit,
|
|
11740
12340
|
runQueryVerb,
|
|
11741
12341
|
runSkill,
|