@neat.is/core 0.3.7 → 0.4.0
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-75IBA4UL.js → chunk-4V23KYOP.js} +98 -2
- package/dist/chunk-4V23KYOP.js.map +1 -0
- package/dist/{chunk-CY67YKNO.js → chunk-IXIFJKMM.js} +16 -2
- package/dist/chunk-IXIFJKMM.js.map +1 -0
- package/dist/{chunk-EDHOCFOG.js → chunk-N6RPINEJ.js} +16 -5
- package/dist/chunk-N6RPINEJ.js.map +1 -0
- package/dist/{chunk-ZU2RQRCN.js → chunk-UPW4CMOH.js} +368 -270
- package/dist/chunk-UPW4CMOH.js.map +1 -0
- package/dist/cli.cjs +2009 -421
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +7 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.js +1607 -192
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +246 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +4 -4
- package/dist/neatd.cjs +259 -29
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +14 -4
- package/dist/neatd.js.map +1 -1
- package/dist/{otel-grpc-PM4SWPZE.js → otel-grpc-GGZHR7VM.js} +3 -3
- package/dist/server.cjs +387 -161
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +26 -7
- package/dist/server.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-75IBA4UL.js.map +0 -1
- package/dist/chunk-CY67YKNO.js.map +0 -1
- package/dist/chunk-EDHOCFOG.js.map +0 -1
- package/dist/chunk-ZU2RQRCN.js.map +0 -1
- /package/dist/{otel-grpc-PM4SWPZE.js.map → otel-grpc-GGZHR7VM.js.map} +0 -0
package/dist/server.cjs
CHANGED
|
@@ -39,6 +39,91 @@ var init_cjs_shims = __esm({
|
|
|
39
39
|
}
|
|
40
40
|
});
|
|
41
41
|
|
|
42
|
+
// src/auth.ts
|
|
43
|
+
function isLoopbackHost(host) {
|
|
44
|
+
if (!host) return false;
|
|
45
|
+
return LOOPBACK_HOSTS.has(host);
|
|
46
|
+
}
|
|
47
|
+
function assertBindAuthority(host, token) {
|
|
48
|
+
if (token && token.length > 0) return;
|
|
49
|
+
if (isLoopbackHost(host)) return;
|
|
50
|
+
throw new BindAuthorityError(host);
|
|
51
|
+
}
|
|
52
|
+
function mountBearerAuth(app, opts) {
|
|
53
|
+
if (!opts.token || opts.token.length === 0) return;
|
|
54
|
+
if (opts.trustProxy) return;
|
|
55
|
+
const expected = Buffer.from(opts.token, "utf8");
|
|
56
|
+
const suffixes = [...DEFAULT_UNAUTH_SUFFIXES, ...opts.extraUnauthenticatedSuffixes ?? []];
|
|
57
|
+
const publicRead = opts.publicRead === true;
|
|
58
|
+
app.addHook("preHandler", (req, reply, done) => {
|
|
59
|
+
const path38 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
|
|
60
|
+
for (const suffix of suffixes) {
|
|
61
|
+
if (path38 === suffix || path38.endsWith(suffix)) {
|
|
62
|
+
done();
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (publicRead && PUBLIC_READ_METHODS.has(req.method)) {
|
|
67
|
+
done();
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const header = req.headers.authorization;
|
|
71
|
+
if (typeof header !== "string" || !header.startsWith("Bearer ")) {
|
|
72
|
+
void reply.code(401).send({ error: "unauthorized" });
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const provided = Buffer.from(header.slice("Bearer ".length).trim(), "utf8");
|
|
76
|
+
if (provided.length !== expected.length || !(0, import_node_crypto.timingSafeEqual)(provided, expected)) {
|
|
77
|
+
void reply.code(401).send({ error: "unauthorized" });
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
done();
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
function parseBoolEnv(v) {
|
|
84
|
+
if (!v) return false;
|
|
85
|
+
return v === "true" || v === "1";
|
|
86
|
+
}
|
|
87
|
+
function readAuthEnv(env = process.env) {
|
|
88
|
+
const t = env.NEAT_AUTH_TOKEN;
|
|
89
|
+
const ot = env.NEAT_OTEL_TOKEN;
|
|
90
|
+
return {
|
|
91
|
+
authToken: t && t.length > 0 ? t : void 0,
|
|
92
|
+
otelToken: ot && ot.length > 0 ? ot : t && t.length > 0 ? t : void 0,
|
|
93
|
+
trustProxy: env.NEAT_AUTH_PROXY === "true",
|
|
94
|
+
publicRead: parseBoolEnv(env.NEAT_PUBLIC_READ)
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
var import_node_crypto, LOOPBACK_HOSTS, BindAuthorityError, PUBLIC_READ_METHODS, DEFAULT_UNAUTH_SUFFIXES;
|
|
98
|
+
var init_auth = __esm({
|
|
99
|
+
"src/auth.ts"() {
|
|
100
|
+
"use strict";
|
|
101
|
+
init_cjs_shims();
|
|
102
|
+
import_node_crypto = require("crypto");
|
|
103
|
+
LOOPBACK_HOSTS = /* @__PURE__ */ new Set([
|
|
104
|
+
"127.0.0.1",
|
|
105
|
+
"localhost",
|
|
106
|
+
"::1",
|
|
107
|
+
"::ffff:127.0.0.1"
|
|
108
|
+
]);
|
|
109
|
+
BindAuthorityError = class extends Error {
|
|
110
|
+
constructor(host) {
|
|
111
|
+
super(
|
|
112
|
+
`NEAT refuses to bind on a public interface without \`NEAT_AUTH_TOKEN\` set (host="${host}"). Set the token or bind to loopback only.`
|
|
113
|
+
);
|
|
114
|
+
this.name = "BindAuthorityError";
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
PUBLIC_READ_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "OPTIONS"]);
|
|
118
|
+
DEFAULT_UNAUTH_SUFFIXES = [
|
|
119
|
+
"/health",
|
|
120
|
+
"/healthz",
|
|
121
|
+
"/readyz",
|
|
122
|
+
"/api/config"
|
|
123
|
+
];
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
42
127
|
// src/otel-grpc.ts
|
|
43
128
|
var otel_grpc_exports = {};
|
|
44
129
|
__export(otel_grpc_exports, {
|
|
@@ -121,8 +206,21 @@ function loadTraceService() {
|
|
|
121
206
|
async function startOtelGrpcReceiver(opts) {
|
|
122
207
|
const server = new grpc.Server();
|
|
123
208
|
const service = loadTraceService();
|
|
209
|
+
const requiresAuth = !opts.trustProxy && !!opts.authToken && opts.authToken.length > 0;
|
|
210
|
+
const expectedHeader = requiresAuth ? `Bearer ${opts.authToken}` : "";
|
|
124
211
|
server.addService(service, {
|
|
125
212
|
Export: (call, callback) => {
|
|
213
|
+
if (requiresAuth) {
|
|
214
|
+
const meta = call.metadata.get("authorization");
|
|
215
|
+
const got = meta.length > 0 ? String(meta[0]) : "";
|
|
216
|
+
const a = Buffer.from(got, "utf8");
|
|
217
|
+
const b = Buffer.from(expectedHeader, "utf8");
|
|
218
|
+
const ok = a.length === b.length && (0, import_node_crypto2.timingSafeEqual)(a, b);
|
|
219
|
+
if (!ok) {
|
|
220
|
+
callback({ code: grpc.status.UNAUTHENTICATED, message: "unauthorized" });
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
126
224
|
void (async () => {
|
|
127
225
|
try {
|
|
128
226
|
const reshaped = reshapeGrpcRequest(call.request ?? {});
|
|
@@ -155,13 +253,14 @@ async function startOtelGrpcReceiver(opts) {
|
|
|
155
253
|
})
|
|
156
254
|
};
|
|
157
255
|
}
|
|
158
|
-
var import_node_url, import_node_path34, grpc, protoLoader;
|
|
256
|
+
var import_node_url, import_node_path34, import_node_crypto2, grpc, protoLoader;
|
|
159
257
|
var init_otel_grpc = __esm({
|
|
160
258
|
"src/otel-grpc.ts"() {
|
|
161
259
|
"use strict";
|
|
162
260
|
init_cjs_shims();
|
|
163
261
|
import_node_url = require("url");
|
|
164
262
|
import_node_path34 = __toESM(require("path"), 1);
|
|
263
|
+
import_node_crypto2 = require("crypto");
|
|
165
264
|
grpc = __toESM(require("@grpc/grpc-js"), 1);
|
|
166
265
|
protoLoader = __toESM(require("@grpc/proto-loader"), 1);
|
|
167
266
|
init_otel();
|
|
@@ -224,6 +323,15 @@ function isoFromUnixNano(nanos) {
|
|
|
224
323
|
return void 0;
|
|
225
324
|
}
|
|
226
325
|
}
|
|
326
|
+
function pickEnv(spanAttrs, resourceAttrs) {
|
|
327
|
+
for (const attrs of [spanAttrs, resourceAttrs]) {
|
|
328
|
+
for (const key of [ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT]) {
|
|
329
|
+
const v = attrs[key];
|
|
330
|
+
if (typeof v === "string" && v.length > 0) return v;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return ENV_FALLBACK;
|
|
334
|
+
}
|
|
227
335
|
function parseOtlpRequest(body) {
|
|
228
336
|
const out = [];
|
|
229
337
|
for (const rs of body.resourceSpans ?? []) {
|
|
@@ -243,6 +351,7 @@ function parseOtlpRequest(body) {
|
|
|
243
351
|
endTimeUnixNano: span.endTimeUnixNano ?? "0",
|
|
244
352
|
startTimeIso: isoFromUnixNano(span.startTimeUnixNano),
|
|
245
353
|
durationNanos: durationNanos(span.startTimeUnixNano, span.endTimeUnixNano),
|
|
354
|
+
env: pickEnv(attrs, resourceAttrs),
|
|
246
355
|
attributes: attrs,
|
|
247
356
|
dbSystem: typeof attrs["db.system"] === "string" ? attrs["db.system"] : void 0,
|
|
248
357
|
dbName: typeof attrs["db.name"] === "string" ? attrs["db.name"] : void 0,
|
|
@@ -302,6 +411,7 @@ async function buildOtelReceiver(opts) {
|
|
|
302
411
|
logger: false,
|
|
303
412
|
bodyLimit: opts.bodyLimit ?? 16 * 1024 * 1024
|
|
304
413
|
});
|
|
414
|
+
mountBearerAuth(app, { token: opts.authToken, trustProxy: opts.trustProxy });
|
|
305
415
|
const queue = [];
|
|
306
416
|
let draining = false;
|
|
307
417
|
let drainPromise = Promise.resolve();
|
|
@@ -380,7 +490,7 @@ async function buildOtelReceiver(opts) {
|
|
|
380
490
|
};
|
|
381
491
|
return decorated;
|
|
382
492
|
}
|
|
383
|
-
var import_node_path35, import_node_url2, import_fastify2, import_protobufjs, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody;
|
|
493
|
+
var import_node_path35, import_node_url2, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody;
|
|
384
494
|
var init_otel = __esm({
|
|
385
495
|
"src/otel.ts"() {
|
|
386
496
|
"use strict";
|
|
@@ -389,6 +499,10 @@ var init_otel = __esm({
|
|
|
389
499
|
import_node_url2 = require("url");
|
|
390
500
|
import_fastify2 = __toESM(require("fastify"), 1);
|
|
391
501
|
import_protobufjs = __toESM(require("protobufjs"), 1);
|
|
502
|
+
init_auth();
|
|
503
|
+
ENV_ATTR_CANONICAL = "deployment.environment.name";
|
|
504
|
+
ENV_ATTR_COMPAT = "deployment.environment";
|
|
505
|
+
ENV_FALLBACK = "unknown";
|
|
392
506
|
exportTraceServiceRequestType = null;
|
|
393
507
|
exportTraceServiceResponseType = null;
|
|
394
508
|
cachedProtobufResponseBody = null;
|
|
@@ -421,7 +535,7 @@ function getGraph(project = DEFAULT_PROJECT) {
|
|
|
421
535
|
init_cjs_shims();
|
|
422
536
|
var import_fastify = __toESM(require("fastify"), 1);
|
|
423
537
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
424
|
-
var
|
|
538
|
+
var import_types22 = require("@neat.is/types");
|
|
425
539
|
|
|
426
540
|
// src/divergences.ts
|
|
427
541
|
init_cjs_shims();
|
|
@@ -1678,52 +1792,64 @@ function cacheSpanService(span, now) {
|
|
|
1678
1792
|
if (!span.traceId || !span.spanId) return;
|
|
1679
1793
|
const key = parentSpanKey(span.traceId, span.spanId);
|
|
1680
1794
|
parentSpanCache.delete(key);
|
|
1681
|
-
parentSpanCache.set(key, {
|
|
1795
|
+
parentSpanCache.set(key, {
|
|
1796
|
+
service: span.service,
|
|
1797
|
+
env: span.env ?? "unknown",
|
|
1798
|
+
expiresAt: now + PARENT_SPAN_CACHE_TTL_MS
|
|
1799
|
+
});
|
|
1682
1800
|
while (parentSpanCache.size > PARENT_SPAN_CACHE_SIZE) {
|
|
1683
1801
|
const oldest = parentSpanCache.keys().next().value;
|
|
1684
1802
|
if (!oldest) break;
|
|
1685
1803
|
parentSpanCache.delete(oldest);
|
|
1686
1804
|
}
|
|
1687
1805
|
}
|
|
1688
|
-
function
|
|
1806
|
+
function lookupParentSpan(traceId, parentSpanId, now) {
|
|
1689
1807
|
const entry = parentSpanCache.get(parentSpanKey(traceId, parentSpanId));
|
|
1690
1808
|
if (!entry) return null;
|
|
1691
1809
|
if (entry.expiresAt <= now) {
|
|
1692
1810
|
parentSpanCache.delete(parentSpanKey(traceId, parentSpanId));
|
|
1693
1811
|
return null;
|
|
1694
1812
|
}
|
|
1695
|
-
return entry.service;
|
|
1813
|
+
return { service: entry.service, env: entry.env };
|
|
1696
1814
|
}
|
|
1697
|
-
function resolveServiceId(graph, host) {
|
|
1698
|
-
const
|
|
1699
|
-
if (graph.hasNode(
|
|
1700
|
-
|
|
1815
|
+
function resolveServiceId(graph, host, env) {
|
|
1816
|
+
const envTagged = (0, import_types4.serviceId)(host, env);
|
|
1817
|
+
if (graph.hasNode(envTagged)) return envTagged;
|
|
1818
|
+
const envLess = (0, import_types4.serviceId)(host);
|
|
1819
|
+
if (envLess !== envTagged && graph.hasNode(envLess)) return envLess;
|
|
1820
|
+
let sameEnv = null;
|
|
1821
|
+
let envLessMatch = null;
|
|
1822
|
+
let anyMatch = null;
|
|
1701
1823
|
graph.forEachNode((id, attrs) => {
|
|
1702
|
-
if (
|
|
1824
|
+
if (sameEnv) return;
|
|
1703
1825
|
const a = attrs;
|
|
1704
1826
|
if (a.type !== import_types4.NodeType.ServiceNode) return;
|
|
1705
|
-
|
|
1706
|
-
|
|
1827
|
+
const matchesByName = a.name === host;
|
|
1828
|
+
const matchesByAlias = a.aliases ? a.aliases.includes(host) : false;
|
|
1829
|
+
if (!matchesByName && !matchesByAlias) return;
|
|
1830
|
+
const nodeEnv = a.env ?? "unknown";
|
|
1831
|
+
if (nodeEnv === env) {
|
|
1832
|
+
sameEnv = id;
|
|
1707
1833
|
return;
|
|
1708
1834
|
}
|
|
1709
|
-
if (
|
|
1710
|
-
|
|
1711
|
-
}
|
|
1835
|
+
if (nodeEnv === "unknown" && !envLessMatch) envLessMatch = id;
|
|
1836
|
+
else if (!anyMatch) anyMatch = id;
|
|
1712
1837
|
});
|
|
1713
|
-
return
|
|
1838
|
+
return sameEnv ?? envLessMatch ?? anyMatch;
|
|
1714
1839
|
}
|
|
1715
1840
|
function frontierIdFor(host) {
|
|
1716
1841
|
return (0, import_types4.frontierId)(host);
|
|
1717
1842
|
}
|
|
1718
|
-
function ensureServiceNode(graph, serviceName) {
|
|
1719
|
-
const id = (0, import_types4.serviceId)(serviceName);
|
|
1843
|
+
function ensureServiceNode(graph, serviceName, env) {
|
|
1844
|
+
const id = (0, import_types4.serviceId)(serviceName, env);
|
|
1720
1845
|
if (graph.hasNode(id)) return id;
|
|
1721
1846
|
const node = {
|
|
1722
1847
|
id,
|
|
1723
1848
|
type: import_types4.NodeType.ServiceNode,
|
|
1724
1849
|
name: serviceName,
|
|
1725
1850
|
language: "unknown",
|
|
1726
|
-
discoveredVia: "otel"
|
|
1851
|
+
discoveredVia: "otel",
|
|
1852
|
+
...env !== "unknown" ? { env } : {}
|
|
1727
1853
|
};
|
|
1728
1854
|
graph.addNode(id, node);
|
|
1729
1855
|
return id;
|
|
@@ -1858,7 +1984,8 @@ function sanitizeAttributes(attrs) {
|
|
|
1858
1984
|
async function handleSpan(ctx, span) {
|
|
1859
1985
|
const ts = span.startTimeIso ?? nowIso(ctx);
|
|
1860
1986
|
const nowMs = ctx.now ? ctx.now() : Date.now();
|
|
1861
|
-
const
|
|
1987
|
+
const env = span.env ?? "unknown";
|
|
1988
|
+
const sourceId = ensureServiceNode(ctx.graph, span.service, env);
|
|
1862
1989
|
const isError = span.statusCode === 2;
|
|
1863
1990
|
cacheSpanService(span, nowMs);
|
|
1864
1991
|
let affectedNode = sourceId;
|
|
@@ -1881,7 +2008,7 @@ async function handleSpan(ctx, span) {
|
|
|
1881
2008
|
const host = pickAddress(span);
|
|
1882
2009
|
let resolvedViaAddress = false;
|
|
1883
2010
|
if (host && host !== span.service) {
|
|
1884
|
-
const targetId = resolveServiceId(ctx.graph, host);
|
|
2011
|
+
const targetId = resolveServiceId(ctx.graph, host, env);
|
|
1885
2012
|
if (targetId && targetId !== sourceId) {
|
|
1886
2013
|
upsertObservedEdge(
|
|
1887
2014
|
ctx.graph,
|
|
@@ -1908,9 +2035,9 @@ async function handleSpan(ctx, span) {
|
|
|
1908
2035
|
}
|
|
1909
2036
|
}
|
|
1910
2037
|
if (!resolvedViaAddress && span.parentSpanId) {
|
|
1911
|
-
const
|
|
1912
|
-
if (
|
|
1913
|
-
const parentId = ensureServiceNode(ctx.graph,
|
|
2038
|
+
const parent = lookupParentSpan(span.traceId, span.parentSpanId, nowMs);
|
|
2039
|
+
if (parent && parent.service !== span.service) {
|
|
2040
|
+
const parentId = ensureServiceNode(ctx.graph, parent.service, parent.env);
|
|
1914
2041
|
upsertObservedEdge(
|
|
1915
2042
|
ctx.graph,
|
|
1916
2043
|
import_types4.EdgeType.CALLS,
|
|
@@ -2106,6 +2233,28 @@ async function readErrorEvents(errorsPath) {
|
|
|
2106
2233
|
throw err;
|
|
2107
2234
|
}
|
|
2108
2235
|
}
|
|
2236
|
+
function mergeSnapshot(graph, snapshot) {
|
|
2237
|
+
const exported = snapshot.graph;
|
|
2238
|
+
let nodesAdded = 0;
|
|
2239
|
+
let edgesAdded = 0;
|
|
2240
|
+
for (const node of exported.nodes ?? []) {
|
|
2241
|
+
if (graph.hasNode(node.key)) continue;
|
|
2242
|
+
if (!node.attributes) continue;
|
|
2243
|
+
graph.addNode(node.key, node.attributes);
|
|
2244
|
+
nodesAdded++;
|
|
2245
|
+
}
|
|
2246
|
+
for (const edge of exported.edges ?? []) {
|
|
2247
|
+
const attrs = edge.attributes;
|
|
2248
|
+
if (!attrs) continue;
|
|
2249
|
+
const id = edge.key ?? attrs.id;
|
|
2250
|
+
if (!id) continue;
|
|
2251
|
+
if (graph.hasEdge(id)) continue;
|
|
2252
|
+
if (!graph.hasNode(edge.source) || !graph.hasNode(edge.target)) continue;
|
|
2253
|
+
graph.addEdgeWithKey(id, edge.source, edge.target, attrs);
|
|
2254
|
+
edgesAdded++;
|
|
2255
|
+
}
|
|
2256
|
+
return { nodesAdded, edgesAdded };
|
|
2257
|
+
}
|
|
2109
2258
|
|
|
2110
2259
|
// src/extract/services.ts
|
|
2111
2260
|
init_cjs_shims();
|
|
@@ -2502,6 +2651,18 @@ async function expandWorkspaceGlobs(scanPath, globs) {
|
|
|
2502
2651
|
}
|
|
2503
2652
|
return [...found];
|
|
2504
2653
|
}
|
|
2654
|
+
function detectJsFramework(pkg) {
|
|
2655
|
+
const deps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
2656
|
+
if (deps["next"] !== void 0) return "next";
|
|
2657
|
+
if (deps["remix"] !== void 0) return "remix";
|
|
2658
|
+
for (const k of Object.keys(deps)) {
|
|
2659
|
+
if (k.startsWith("@remix-run/")) return "remix";
|
|
2660
|
+
}
|
|
2661
|
+
if (deps["@sveltejs/kit"] !== void 0) return "sveltekit";
|
|
2662
|
+
if (deps["nuxt"] !== void 0) return "nuxt";
|
|
2663
|
+
if (deps["astro"] !== void 0) return "astro";
|
|
2664
|
+
return void 0;
|
|
2665
|
+
}
|
|
2505
2666
|
async function discoverNodeService(scanPath, dir) {
|
|
2506
2667
|
const pkgPath = import_node_path8.default.join(dir, "package.json");
|
|
2507
2668
|
if (!await exists(pkgPath)) return null;
|
|
@@ -2513,6 +2674,7 @@ async function discoverNodeService(scanPath, dir) {
|
|
|
2513
2674
|
return null;
|
|
2514
2675
|
}
|
|
2515
2676
|
if (!pkg.name) return null;
|
|
2677
|
+
const framework = detectJsFramework(pkg);
|
|
2516
2678
|
const node = {
|
|
2517
2679
|
id: (0, import_types6.serviceId)(pkg.name),
|
|
2518
2680
|
type: import_types6.NodeType.ServiceNode,
|
|
@@ -2521,7 +2683,8 @@ async function discoverNodeService(scanPath, dir) {
|
|
|
2521
2683
|
version: pkg.version,
|
|
2522
2684
|
dependencies: pkg.dependencies ?? {},
|
|
2523
2685
|
repoPath: import_node_path8.default.relative(scanPath, dir),
|
|
2524
|
-
...pkg.engines?.node ? { nodeEngine: pkg.engines.node } : {}
|
|
2686
|
+
...pkg.engines?.node ? { nodeEngine: pkg.engines.node } : {},
|
|
2687
|
+
...framework ? { framework } : {}
|
|
2525
2688
|
};
|
|
2526
2689
|
return { pkg, dir, node };
|
|
2527
2690
|
}
|
|
@@ -4376,25 +4539,138 @@ function canonicalJson(value) {
|
|
|
4376
4539
|
});
|
|
4377
4540
|
}
|
|
4378
4541
|
|
|
4379
|
-
// src/
|
|
4542
|
+
// src/persist.ts
|
|
4380
4543
|
init_cjs_shims();
|
|
4544
|
+
var import_node_fs19 = require("fs");
|
|
4381
4545
|
var import_node_path31 = __toESM(require("path"), 1);
|
|
4546
|
+
var import_types20 = require("@neat.is/types");
|
|
4547
|
+
var SCHEMA_VERSION = 4;
|
|
4548
|
+
function migrateV1ToV2(payload) {
|
|
4549
|
+
const nodes = payload.graph.nodes;
|
|
4550
|
+
if (Array.isArray(nodes)) {
|
|
4551
|
+
for (const node of nodes) {
|
|
4552
|
+
if (node.attributes && "pgDriverVersion" in node.attributes) {
|
|
4553
|
+
delete node.attributes.pgDriverVersion;
|
|
4554
|
+
}
|
|
4555
|
+
}
|
|
4556
|
+
}
|
|
4557
|
+
return { ...payload, schemaVersion: 2 };
|
|
4558
|
+
}
|
|
4559
|
+
function migrateV3ToV4(payload) {
|
|
4560
|
+
return { ...payload, schemaVersion: 4 };
|
|
4561
|
+
}
|
|
4562
|
+
function migrateV2ToV3(payload) {
|
|
4563
|
+
const edges = payload.graph.edges;
|
|
4564
|
+
if (Array.isArray(edges)) {
|
|
4565
|
+
for (const edge of edges) {
|
|
4566
|
+
const attrs = edge.attributes;
|
|
4567
|
+
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
4568
|
+
attrs.provenance = import_types20.Provenance.OBSERVED;
|
|
4569
|
+
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
4570
|
+
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
4571
|
+
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
4572
|
+
if (type && source && target) {
|
|
4573
|
+
const newId = (0, import_types20.observedEdgeId)(source, target, type);
|
|
4574
|
+
attrs.id = newId;
|
|
4575
|
+
if (edge.key) edge.key = newId;
|
|
4576
|
+
}
|
|
4577
|
+
}
|
|
4578
|
+
}
|
|
4579
|
+
return { ...payload, schemaVersion: 3 };
|
|
4580
|
+
}
|
|
4581
|
+
async function ensureDir(filePath) {
|
|
4582
|
+
await import_node_fs19.promises.mkdir(import_node_path31.default.dirname(filePath), { recursive: true });
|
|
4583
|
+
}
|
|
4584
|
+
async function saveGraphToDisk(graph, outPath) {
|
|
4585
|
+
await ensureDir(outPath);
|
|
4586
|
+
const payload = {
|
|
4587
|
+
schemaVersion: SCHEMA_VERSION,
|
|
4588
|
+
exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4589
|
+
graph: graph.export()
|
|
4590
|
+
};
|
|
4591
|
+
const tmp = `${outPath}.tmp`;
|
|
4592
|
+
await import_node_fs19.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
|
|
4593
|
+
await import_node_fs19.promises.rename(tmp, outPath);
|
|
4594
|
+
}
|
|
4595
|
+
async function loadGraphFromDisk(graph, outPath) {
|
|
4596
|
+
let raw;
|
|
4597
|
+
try {
|
|
4598
|
+
raw = await import_node_fs19.promises.readFile(outPath, "utf8");
|
|
4599
|
+
} catch (err) {
|
|
4600
|
+
if (err.code === "ENOENT") return;
|
|
4601
|
+
throw err;
|
|
4602
|
+
}
|
|
4603
|
+
let payload = JSON.parse(raw);
|
|
4604
|
+
if (payload.schemaVersion === 1) {
|
|
4605
|
+
payload = migrateV1ToV2(payload);
|
|
4606
|
+
}
|
|
4607
|
+
if (payload.schemaVersion === 2) {
|
|
4608
|
+
payload = migrateV2ToV3(payload);
|
|
4609
|
+
}
|
|
4610
|
+
if (payload.schemaVersion === 3) {
|
|
4611
|
+
payload = migrateV3ToV4(payload);
|
|
4612
|
+
}
|
|
4613
|
+
if (payload.schemaVersion !== SCHEMA_VERSION) {
|
|
4614
|
+
throw new Error(
|
|
4615
|
+
`persist: unsupported snapshot schemaVersion ${payload.schemaVersion} (expected ${SCHEMA_VERSION})`
|
|
4616
|
+
);
|
|
4617
|
+
}
|
|
4618
|
+
graph.clear();
|
|
4619
|
+
graph.import(payload.graph);
|
|
4620
|
+
}
|
|
4621
|
+
function startPersistLoop(graph, outPath, intervalMs = 6e4) {
|
|
4622
|
+
let stopped = false;
|
|
4623
|
+
const tick = async () => {
|
|
4624
|
+
if (stopped) return;
|
|
4625
|
+
try {
|
|
4626
|
+
await saveGraphToDisk(graph, outPath);
|
|
4627
|
+
} catch (err) {
|
|
4628
|
+
console.error("persist: periodic save failed", err);
|
|
4629
|
+
}
|
|
4630
|
+
};
|
|
4631
|
+
const interval = setInterval(() => {
|
|
4632
|
+
void tick();
|
|
4633
|
+
}, intervalMs);
|
|
4634
|
+
const onSignal = (signal) => {
|
|
4635
|
+
void (async () => {
|
|
4636
|
+
try {
|
|
4637
|
+
await saveGraphToDisk(graph, outPath);
|
|
4638
|
+
} catch (err) {
|
|
4639
|
+
console.error(`persist: ${signal} save failed`, err);
|
|
4640
|
+
} finally {
|
|
4641
|
+
process.exit(0);
|
|
4642
|
+
}
|
|
4643
|
+
})();
|
|
4644
|
+
};
|
|
4645
|
+
process.on("SIGTERM", onSignal);
|
|
4646
|
+
process.on("SIGINT", onSignal);
|
|
4647
|
+
return () => {
|
|
4648
|
+
stopped = true;
|
|
4649
|
+
clearInterval(interval);
|
|
4650
|
+
process.off("SIGTERM", onSignal);
|
|
4651
|
+
process.off("SIGINT", onSignal);
|
|
4652
|
+
};
|
|
4653
|
+
}
|
|
4654
|
+
|
|
4655
|
+
// src/projects.ts
|
|
4656
|
+
init_cjs_shims();
|
|
4657
|
+
var import_node_path32 = __toESM(require("path"), 1);
|
|
4382
4658
|
function pathsForProject(project, baseDir) {
|
|
4383
4659
|
if (project === DEFAULT_PROJECT) {
|
|
4384
4660
|
return {
|
|
4385
|
-
snapshotPath:
|
|
4386
|
-
errorsPath:
|
|
4387
|
-
staleEventsPath:
|
|
4388
|
-
embeddingsCachePath:
|
|
4389
|
-
policyViolationsPath:
|
|
4661
|
+
snapshotPath: import_node_path32.default.join(baseDir, "graph.json"),
|
|
4662
|
+
errorsPath: import_node_path32.default.join(baseDir, "errors.ndjson"),
|
|
4663
|
+
staleEventsPath: import_node_path32.default.join(baseDir, "stale-events.ndjson"),
|
|
4664
|
+
embeddingsCachePath: import_node_path32.default.join(baseDir, "embeddings.json"),
|
|
4665
|
+
policyViolationsPath: import_node_path32.default.join(baseDir, "policy-violations.ndjson")
|
|
4390
4666
|
};
|
|
4391
4667
|
}
|
|
4392
4668
|
return {
|
|
4393
|
-
snapshotPath:
|
|
4394
|
-
errorsPath:
|
|
4395
|
-
staleEventsPath:
|
|
4396
|
-
embeddingsCachePath:
|
|
4397
|
-
policyViolationsPath:
|
|
4669
|
+
snapshotPath: import_node_path32.default.join(baseDir, `${project}.json`),
|
|
4670
|
+
errorsPath: import_node_path32.default.join(baseDir, `errors.${project}.ndjson`),
|
|
4671
|
+
staleEventsPath: import_node_path32.default.join(baseDir, `stale-events.${project}.ndjson`),
|
|
4672
|
+
embeddingsCachePath: import_node_path32.default.join(baseDir, `embeddings.${project}.json`),
|
|
4673
|
+
policyViolationsPath: import_node_path32.default.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
4398
4674
|
};
|
|
4399
4675
|
}
|
|
4400
4676
|
var Projects = class {
|
|
@@ -4434,23 +4710,23 @@ function parseExtraProjects(raw) {
|
|
|
4434
4710
|
|
|
4435
4711
|
// src/registry.ts
|
|
4436
4712
|
init_cjs_shims();
|
|
4437
|
-
var
|
|
4713
|
+
var import_node_fs20 = require("fs");
|
|
4438
4714
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
4439
|
-
var
|
|
4440
|
-
var
|
|
4715
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
4716
|
+
var import_types21 = require("@neat.is/types");
|
|
4441
4717
|
function neatHome() {
|
|
4442
4718
|
const override = process.env.NEAT_HOME;
|
|
4443
|
-
if (override && override.length > 0) return
|
|
4444
|
-
return
|
|
4719
|
+
if (override && override.length > 0) return import_node_path33.default.resolve(override);
|
|
4720
|
+
return import_node_path33.default.join(import_node_os2.default.homedir(), ".neat");
|
|
4445
4721
|
}
|
|
4446
4722
|
function registryPath() {
|
|
4447
|
-
return
|
|
4723
|
+
return import_node_path33.default.join(neatHome(), "projects.json");
|
|
4448
4724
|
}
|
|
4449
4725
|
async function readRegistry() {
|
|
4450
4726
|
const file = registryPath();
|
|
4451
4727
|
let raw;
|
|
4452
4728
|
try {
|
|
4453
|
-
raw = await
|
|
4729
|
+
raw = await import_node_fs20.promises.readFile(file, "utf8");
|
|
4454
4730
|
} catch (err) {
|
|
4455
4731
|
if (err.code === "ENOENT") {
|
|
4456
4732
|
return { version: 1, projects: [] };
|
|
@@ -4458,7 +4734,7 @@ async function readRegistry() {
|
|
|
4458
4734
|
throw err;
|
|
4459
4735
|
}
|
|
4460
4736
|
const parsed = JSON.parse(raw);
|
|
4461
|
-
return
|
|
4737
|
+
return import_types21.RegistryFileSchema.parse(parsed);
|
|
4462
4738
|
}
|
|
4463
4739
|
async function getProject(name) {
|
|
4464
4740
|
const reg = await readRegistry();
|
|
@@ -4525,6 +4801,7 @@ data: ${JSON.stringify(envelope.payload)}
|
|
|
4525
4801
|
}
|
|
4526
4802
|
|
|
4527
4803
|
// src/api.ts
|
|
4804
|
+
init_auth();
|
|
4528
4805
|
function serializeGraph(graph) {
|
|
4529
4806
|
const nodes = [];
|
|
4530
4807
|
graph.forEachNode((_id, attrs) => {
|
|
@@ -4648,11 +4925,11 @@ function registerRoutes(scope, ctx) {
|
|
|
4648
4925
|
const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
4649
4926
|
const parsed = [];
|
|
4650
4927
|
for (const c of candidates) {
|
|
4651
|
-
const r =
|
|
4928
|
+
const r = import_types22.DivergenceTypeSchema.safeParse(c);
|
|
4652
4929
|
if (!r.success) {
|
|
4653
4930
|
return reply.code(400).send({
|
|
4654
4931
|
error: `unknown divergence type "${c}"`,
|
|
4655
|
-
allowed:
|
|
4932
|
+
allowed: import_types22.DivergenceTypeSchema.options
|
|
4656
4933
|
});
|
|
4657
4934
|
}
|
|
4658
4935
|
parsed.push(r.data);
|
|
@@ -4797,6 +5074,35 @@ function registerRoutes(scope, ctx) {
|
|
|
4797
5074
|
}
|
|
4798
5075
|
}
|
|
4799
5076
|
);
|
|
5077
|
+
scope.post("/snapshot", async (req, reply) => {
|
|
5078
|
+
const proj = resolveProject(registry, req, reply);
|
|
5079
|
+
if (!proj) return;
|
|
5080
|
+
const body = req.body;
|
|
5081
|
+
if (!body || typeof body !== "object" || !body.snapshot) {
|
|
5082
|
+
return reply.code(400).send({ error: "request body must be { snapshot: <persisted-graph> }" });
|
|
5083
|
+
}
|
|
5084
|
+
const snap = body.snapshot;
|
|
5085
|
+
if (typeof snap.schemaVersion !== "number" || snap.schemaVersion !== SCHEMA_VERSION) {
|
|
5086
|
+
return reply.code(400).send({
|
|
5087
|
+
error: `unsupported snapshot schemaVersion ${snap.schemaVersion} (expected ${SCHEMA_VERSION})`
|
|
5088
|
+
});
|
|
5089
|
+
}
|
|
5090
|
+
try {
|
|
5091
|
+
const result = mergeSnapshot(proj.graph, snap);
|
|
5092
|
+
return {
|
|
5093
|
+
project: proj.name,
|
|
5094
|
+
nodesAdded: result.nodesAdded,
|
|
5095
|
+
edgesAdded: result.edgesAdded,
|
|
5096
|
+
nodeCount: proj.graph.order,
|
|
5097
|
+
edgeCount: proj.graph.size
|
|
5098
|
+
};
|
|
5099
|
+
} catch (err) {
|
|
5100
|
+
return reply.code(400).send({
|
|
5101
|
+
error: "snapshot merge failed",
|
|
5102
|
+
details: err.message
|
|
5103
|
+
});
|
|
5104
|
+
}
|
|
5105
|
+
});
|
|
4800
5106
|
scope.post("/graph/scan", async (req, reply) => {
|
|
4801
5107
|
const proj = resolveProject(registry, req, reply);
|
|
4802
5108
|
if (!proj) return;
|
|
@@ -4836,7 +5142,7 @@ function registerRoutes(scope, ctx) {
|
|
|
4836
5142
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
4837
5143
|
let violations = await log.readAll();
|
|
4838
5144
|
if (req.query.severity) {
|
|
4839
|
-
const sev =
|
|
5145
|
+
const sev = import_types22.PolicySeveritySchema.safeParse(req.query.severity);
|
|
4840
5146
|
if (!sev.success) {
|
|
4841
5147
|
return reply.code(400).send({
|
|
4842
5148
|
error: "invalid severity",
|
|
@@ -4853,7 +5159,7 @@ function registerRoutes(scope, ctx) {
|
|
|
4853
5159
|
scope.post("/policies/check", async (req, reply) => {
|
|
4854
5160
|
const proj = resolveProject(registry, req, reply);
|
|
4855
5161
|
if (!proj) return;
|
|
4856
|
-
const parsed =
|
|
5162
|
+
const parsed = import_types22.PoliciesCheckBodySchema.safeParse(req.body ?? {});
|
|
4857
5163
|
if (!parsed.success) {
|
|
4858
5164
|
return reply.code(400).send({
|
|
4859
5165
|
error: "invalid /policies/check body",
|
|
@@ -4890,6 +5196,15 @@ function registerRoutes(scope, ctx) {
|
|
|
4890
5196
|
async function buildApi(opts) {
|
|
4891
5197
|
const app = (0, import_fastify.default)({ logger: false });
|
|
4892
5198
|
await app.register(import_cors.default, { origin: true });
|
|
5199
|
+
mountBearerAuth(app, {
|
|
5200
|
+
token: opts.authToken,
|
|
5201
|
+
trustProxy: opts.trustProxy,
|
|
5202
|
+
publicRead: opts.publicRead
|
|
5203
|
+
});
|
|
5204
|
+
app.get("/api/config", async () => ({
|
|
5205
|
+
publicRead: opts.publicRead === true,
|
|
5206
|
+
authProxy: opts.trustProxy === true
|
|
5207
|
+
}));
|
|
4893
5208
|
const startedAt = opts.startedAt ?? Date.now();
|
|
4894
5209
|
const registry = buildLegacyRegistry(opts);
|
|
4895
5210
|
const legacyErrorsExplicit = !opts.projects && opts.errorsPath !== void 0;
|
|
@@ -4951,113 +5266,6 @@ async function buildApi(opts) {
|
|
|
4951
5266
|
return app;
|
|
4952
5267
|
}
|
|
4953
5268
|
|
|
4954
|
-
// src/persist.ts
|
|
4955
|
-
init_cjs_shims();
|
|
4956
|
-
var import_node_fs20 = require("fs");
|
|
4957
|
-
var import_node_path33 = __toESM(require("path"), 1);
|
|
4958
|
-
var import_types22 = require("@neat.is/types");
|
|
4959
|
-
var SCHEMA_VERSION = 3;
|
|
4960
|
-
function migrateV1ToV2(payload) {
|
|
4961
|
-
const nodes = payload.graph.nodes;
|
|
4962
|
-
if (Array.isArray(nodes)) {
|
|
4963
|
-
for (const node of nodes) {
|
|
4964
|
-
if (node.attributes && "pgDriverVersion" in node.attributes) {
|
|
4965
|
-
delete node.attributes.pgDriverVersion;
|
|
4966
|
-
}
|
|
4967
|
-
}
|
|
4968
|
-
}
|
|
4969
|
-
return { ...payload, schemaVersion: 2 };
|
|
4970
|
-
}
|
|
4971
|
-
function migrateV2ToV3(payload) {
|
|
4972
|
-
const edges = payload.graph.edges;
|
|
4973
|
-
if (Array.isArray(edges)) {
|
|
4974
|
-
for (const edge of edges) {
|
|
4975
|
-
const attrs = edge.attributes;
|
|
4976
|
-
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
4977
|
-
attrs.provenance = import_types22.Provenance.OBSERVED;
|
|
4978
|
-
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
4979
|
-
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
4980
|
-
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
4981
|
-
if (type && source && target) {
|
|
4982
|
-
const newId = (0, import_types22.observedEdgeId)(source, target, type);
|
|
4983
|
-
attrs.id = newId;
|
|
4984
|
-
if (edge.key) edge.key = newId;
|
|
4985
|
-
}
|
|
4986
|
-
}
|
|
4987
|
-
}
|
|
4988
|
-
return { ...payload, schemaVersion: 3 };
|
|
4989
|
-
}
|
|
4990
|
-
async function ensureDir(filePath) {
|
|
4991
|
-
await import_node_fs20.promises.mkdir(import_node_path33.default.dirname(filePath), { recursive: true });
|
|
4992
|
-
}
|
|
4993
|
-
async function saveGraphToDisk(graph, outPath) {
|
|
4994
|
-
await ensureDir(outPath);
|
|
4995
|
-
const payload = {
|
|
4996
|
-
schemaVersion: SCHEMA_VERSION,
|
|
4997
|
-
exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4998
|
-
graph: graph.export()
|
|
4999
|
-
};
|
|
5000
|
-
const tmp = `${outPath}.tmp`;
|
|
5001
|
-
await import_node_fs20.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
|
|
5002
|
-
await import_node_fs20.promises.rename(tmp, outPath);
|
|
5003
|
-
}
|
|
5004
|
-
async function loadGraphFromDisk(graph, outPath) {
|
|
5005
|
-
let raw;
|
|
5006
|
-
try {
|
|
5007
|
-
raw = await import_node_fs20.promises.readFile(outPath, "utf8");
|
|
5008
|
-
} catch (err) {
|
|
5009
|
-
if (err.code === "ENOENT") return;
|
|
5010
|
-
throw err;
|
|
5011
|
-
}
|
|
5012
|
-
let payload = JSON.parse(raw);
|
|
5013
|
-
if (payload.schemaVersion === 1) {
|
|
5014
|
-
payload = migrateV1ToV2(payload);
|
|
5015
|
-
}
|
|
5016
|
-
if (payload.schemaVersion === 2) {
|
|
5017
|
-
payload = migrateV2ToV3(payload);
|
|
5018
|
-
}
|
|
5019
|
-
if (payload.schemaVersion !== SCHEMA_VERSION) {
|
|
5020
|
-
throw new Error(
|
|
5021
|
-
`persist: unsupported snapshot schemaVersion ${payload.schemaVersion} (expected ${SCHEMA_VERSION})`
|
|
5022
|
-
);
|
|
5023
|
-
}
|
|
5024
|
-
graph.clear();
|
|
5025
|
-
graph.import(payload.graph);
|
|
5026
|
-
}
|
|
5027
|
-
function startPersistLoop(graph, outPath, intervalMs = 6e4) {
|
|
5028
|
-
let stopped = false;
|
|
5029
|
-
const tick = async () => {
|
|
5030
|
-
if (stopped) return;
|
|
5031
|
-
try {
|
|
5032
|
-
await saveGraphToDisk(graph, outPath);
|
|
5033
|
-
} catch (err) {
|
|
5034
|
-
console.error("persist: periodic save failed", err);
|
|
5035
|
-
}
|
|
5036
|
-
};
|
|
5037
|
-
const interval = setInterval(() => {
|
|
5038
|
-
void tick();
|
|
5039
|
-
}, intervalMs);
|
|
5040
|
-
const onSignal = (signal) => {
|
|
5041
|
-
void (async () => {
|
|
5042
|
-
try {
|
|
5043
|
-
await saveGraphToDisk(graph, outPath);
|
|
5044
|
-
} catch (err) {
|
|
5045
|
-
console.error(`persist: ${signal} save failed`, err);
|
|
5046
|
-
} finally {
|
|
5047
|
-
process.exit(0);
|
|
5048
|
-
}
|
|
5049
|
-
})();
|
|
5050
|
-
};
|
|
5051
|
-
process.on("SIGTERM", onSignal);
|
|
5052
|
-
process.on("SIGINT", onSignal);
|
|
5053
|
-
return () => {
|
|
5054
|
-
stopped = true;
|
|
5055
|
-
clearInterval(interval);
|
|
5056
|
-
process.off("SIGTERM", onSignal);
|
|
5057
|
-
process.off("SIGINT", onSignal);
|
|
5058
|
-
};
|
|
5059
|
-
}
|
|
5060
|
-
|
|
5061
5269
|
// src/server.ts
|
|
5062
5270
|
init_otel();
|
|
5063
5271
|
init_otel_grpc();
|
|
@@ -5066,7 +5274,7 @@ init_otel_grpc();
|
|
|
5066
5274
|
init_cjs_shims();
|
|
5067
5275
|
var import_node_fs21 = require("fs");
|
|
5068
5276
|
var import_node_path36 = __toESM(require("path"), 1);
|
|
5069
|
-
var
|
|
5277
|
+
var import_node_crypto3 = require("crypto");
|
|
5070
5278
|
var DEFAULT_LIMIT = 10;
|
|
5071
5279
|
var NOMIC_DIM = 768;
|
|
5072
5280
|
var MINI_LM_DIM = 384;
|
|
@@ -5106,7 +5314,7 @@ function embedText(node) {
|
|
|
5106
5314
|
return parts.join(" ");
|
|
5107
5315
|
}
|
|
5108
5316
|
function attrsHash(node) {
|
|
5109
|
-
return (0,
|
|
5317
|
+
return (0, import_node_crypto3.createHash)("sha1").update(embedText(node)).digest("hex").slice(0, 16);
|
|
5110
5318
|
}
|
|
5111
5319
|
function cosine(a, b) {
|
|
5112
5320
|
if (a.length !== b.length) return 0;
|
|
@@ -5350,6 +5558,7 @@ async function buildSearchIndex(graph, options = {}) {
|
|
|
5350
5558
|
}
|
|
5351
5559
|
|
|
5352
5560
|
// src/server.ts
|
|
5561
|
+
init_auth();
|
|
5353
5562
|
async function bootProject(registry, name, scanPath, baseDir) {
|
|
5354
5563
|
const paths = pathsForProject(name, baseDir);
|
|
5355
5564
|
const graph = getGraph(name);
|
|
@@ -5396,7 +5605,14 @@ async function main() {
|
|
|
5396
5605
|
const host = process.env.HOST ?? "0.0.0.0";
|
|
5397
5606
|
const port = Number(process.env.PORT ?? 8080);
|
|
5398
5607
|
const otelPort = Number(process.env.OTEL_PORT ?? 4318);
|
|
5399
|
-
const
|
|
5608
|
+
const auth = readAuthEnv();
|
|
5609
|
+
assertBindAuthority(host, auth.authToken);
|
|
5610
|
+
const app = await buildApi({
|
|
5611
|
+
projects: registry,
|
|
5612
|
+
authToken: auth.authToken,
|
|
5613
|
+
trustProxy: auth.trustProxy,
|
|
5614
|
+
publicRead: auth.publicRead
|
|
5615
|
+
});
|
|
5400
5616
|
await app.listen({ port, host });
|
|
5401
5617
|
console.log(`neat-core listening on http://${host}:${port}`);
|
|
5402
5618
|
console.log(` base dir: ${baseDir}`);
|
|
@@ -5407,12 +5623,22 @@ async function main() {
|
|
|
5407
5623
|
graph: defaultCtx.graph,
|
|
5408
5624
|
errorsPath: defaultCtx.paths.errorsPath
|
|
5409
5625
|
});
|
|
5410
|
-
const otelApp = await buildOtelReceiver({
|
|
5626
|
+
const otelApp = await buildOtelReceiver({
|
|
5627
|
+
onSpan,
|
|
5628
|
+
authToken: auth.otelToken,
|
|
5629
|
+
trustProxy: auth.trustProxy
|
|
5630
|
+
});
|
|
5411
5631
|
await otelApp.listen({ port: otelPort, host });
|
|
5412
5632
|
console.log(`neat-core OTLP receiver on http://${host}:${otelPort}/v1/traces`);
|
|
5413
5633
|
if (process.env.NEAT_OTLP_GRPC === "true") {
|
|
5414
5634
|
const grpcPort = Number(process.env.NEAT_OTLP_GRPC_PORT ?? 4317);
|
|
5415
|
-
const grpcReceiver = await startOtelGrpcReceiver({
|
|
5635
|
+
const grpcReceiver = await startOtelGrpcReceiver({
|
|
5636
|
+
onSpan,
|
|
5637
|
+
host,
|
|
5638
|
+
port: grpcPort,
|
|
5639
|
+
authToken: auth.otelToken,
|
|
5640
|
+
trustProxy: auth.trustProxy
|
|
5641
|
+
});
|
|
5416
5642
|
console.log(`neat-core OTLP/gRPC receiver on ${grpcReceiver.address}`);
|
|
5417
5643
|
}
|
|
5418
5644
|
}
|