@neat.is/core 0.2.6 → 0.2.8
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-T2U4U256.js → chunk-FTKDVKBC.js} +76 -13
- package/dist/chunk-FTKDVKBC.js.map +1 -0
- package/dist/{chunk-6SFEITLJ.js → chunk-GAYTAGEH.js} +298 -13
- package/dist/chunk-GAYTAGEH.js.map +1 -0
- package/dist/{chunk-6JT6L2OV.js → chunk-HWO746IM.js} +8 -10
- package/dist/{chunk-6JT6L2OV.js.map → chunk-HWO746IM.js.map} +1 -1
- package/dist/cli.cjs +1129 -249
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +22 -1
- package/dist/cli.d.ts +22 -1
- package/dist/cli.js +757 -39
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +303 -197
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +16 -18
- package/dist/neatd.cjs +32 -2
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +2 -3
- package/dist/neatd.js.map +1 -1
- package/dist/server.cjs +190 -49
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +2 -2
- package/package.json +5 -3
- package/dist/chunk-6SFEITLJ.js.map +0 -1
- package/dist/chunk-T2U4U256.js.map +0 -1
- package/dist/chunk-WX55TLUT.js +0 -184
- package/dist/chunk-WX55TLUT.js.map +0 -1
package/dist/neatd.js
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
startDaemon
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-HWO746IM.js";
|
|
5
5
|
import {
|
|
6
6
|
listProjects,
|
|
7
7
|
registryPath
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-6SFEITLJ.js";
|
|
8
|
+
} from "./chunk-GAYTAGEH.js";
|
|
10
9
|
|
|
11
10
|
// src/neatd.ts
|
|
12
11
|
import { promises as fs } from "fs";
|
package/dist/neatd.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/neatd.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * `neatd` — distribution-layer daemon CLI (ADR-049).\n *\n * Subcommands:\n * neatd start [--foreground] boot the daemon and watch the registry\n * neatd stop signal the running daemon to shut down\n * neatd reload signal the running daemon to re-read the registry\n * neatd status print PID + per-project last-seen timestamps\n *\n * MVP runs in foreground only. Backgrounding is the supervisor's job\n * (launchd / systemd / nohup) — `neatd start` blocks until SIGINT/SIGTERM.\n */\n\nimport { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport { startDaemon } from './daemon.js'\nimport { listProjects, registryPath } from './registry.js'\n\nfunction neatHome(): string {\n if (process.env.NEAT_HOME && process.env.NEAT_HOME.length > 0) {\n return path.resolve(process.env.NEAT_HOME)\n }\n const home = process.env.HOME ?? process.env.USERPROFILE ?? ''\n return path.join(home, '.neat')\n}\n\nasync function readPid(): Promise<number | null> {\n try {\n const raw = await fs.readFile(path.join(neatHome(), 'neatd.pid'), 'utf8')\n const n = Number.parseInt(raw.trim(), 10)\n return Number.isFinite(n) ? n : null\n } catch {\n return null\n }\n}\n\nfunction usage(): void {\n console.log('usage: neatd <start|stop|reload|status> [--foreground]')\n}\n\nasync function cmdStart(): Promise<void> {\n const handle = await startDaemon()\n console.log(`neatd: started, PID ${process.pid}, ${handle.slots.size} project(s)`)\n console.log(`neatd: registry at ${registryPath()}`)\n console.log('neatd: SIGHUP reloads, SIGTERM/SIGINT stops')\n\n let stopping = false\n const shutdown = (signal: NodeJS.Signals): void => {\n if (stopping) return\n stopping = true\n console.log(`neatd: ${signal} received, stopping…`)\n void handle\n .stop()\n .catch((err) => console.error(`neatd: shutdown error — ${(err as Error).message}`))\n .finally(() => process.exit(0))\n }\n process.on('SIGTERM', shutdown)\n process.on('SIGINT', shutdown)\n\n // Block forever — supervisors keep us in the foreground.\n await new Promise<void>(() => {})\n}\n\nasync function cmdStop(): Promise<void> {\n const pid = await readPid()\n if (pid === null) {\n console.error('neatd: no running daemon found (no PID file)')\n process.exit(1)\n }\n try {\n process.kill(pid, 'SIGTERM')\n console.log(`neatd: SIGTERM sent to PID ${pid}`)\n } catch (err) {\n console.error(`neatd: failed to signal PID ${pid} — ${(err as Error).message}`)\n process.exit(1)\n }\n}\n\nasync function cmdReload(): Promise<void> {\n const pid = await readPid()\n if (pid === null) {\n console.error('neatd: no running daemon found (no PID file)')\n process.exit(1)\n }\n try {\n process.kill(pid, 'SIGHUP')\n console.log(`neatd: SIGHUP sent to PID ${pid}`)\n } catch (err) {\n console.error(`neatd: failed to signal PID ${pid} — ${(err as Error).message}`)\n process.exit(1)\n }\n}\n\nasync function cmdStatus(): Promise<void> {\n const pid = await readPid()\n console.log(`pid: ${pid ?? '(not running)'}`)\n console.log(`registry: ${registryPath()}`)\n const projects = await listProjects().catch(() => [])\n if (projects.length === 0) {\n console.log('projects: (none)')\n return\n }\n console.log('projects:')\n for (const p of projects) {\n const seen = p.lastSeenAt ?? 'never'\n console.log(` ${p.name}\\t${p.status}\\t${p.path}\\tlast-seen=${seen}`)\n }\n}\n\nasync function main(): Promise<void> {\n const cmd = process.argv[2]\n if (!cmd || cmd === '-h' || cmd === '--help') {\n usage()\n process.exit(cmd ? 0 : 2)\n }\n\n if (cmd === 'start') return cmdStart()\n if (cmd === 'stop') return cmdStop()\n if (cmd === 'reload') return cmdReload()\n if (cmd === 'status') return cmdStatus()\n\n console.error(`neatd: unknown command \"${cmd}\"`)\n usage()\n process.exit(1)\n}\n\nconst entry = process.argv[1] ?? ''\nif (/[\\\\/]neatd\\.(?:cjs|js)$/.test(entry) || entry.endsWith('/neatd')) {\n main().catch((err) => {\n console.error(err)\n process.exit(1)\n })\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/neatd.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * `neatd` — distribution-layer daemon CLI (ADR-049).\n *\n * Subcommands:\n * neatd start [--foreground] boot the daemon and watch the registry\n * neatd stop signal the running daemon to shut down\n * neatd reload signal the running daemon to re-read the registry\n * neatd status print PID + per-project last-seen timestamps\n *\n * MVP runs in foreground only. Backgrounding is the supervisor's job\n * (launchd / systemd / nohup) — `neatd start` blocks until SIGINT/SIGTERM.\n */\n\nimport { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport { startDaemon } from './daemon.js'\nimport { listProjects, registryPath } from './registry.js'\n\nfunction neatHome(): string {\n if (process.env.NEAT_HOME && process.env.NEAT_HOME.length > 0) {\n return path.resolve(process.env.NEAT_HOME)\n }\n const home = process.env.HOME ?? process.env.USERPROFILE ?? ''\n return path.join(home, '.neat')\n}\n\nasync function readPid(): Promise<number | null> {\n try {\n const raw = await fs.readFile(path.join(neatHome(), 'neatd.pid'), 'utf8')\n const n = Number.parseInt(raw.trim(), 10)\n return Number.isFinite(n) ? n : null\n } catch {\n return null\n }\n}\n\nfunction usage(): void {\n console.log('usage: neatd <start|stop|reload|status> [--foreground]')\n}\n\nasync function cmdStart(): Promise<void> {\n const handle = await startDaemon()\n console.log(`neatd: started, PID ${process.pid}, ${handle.slots.size} project(s)`)\n console.log(`neatd: registry at ${registryPath()}`)\n console.log('neatd: SIGHUP reloads, SIGTERM/SIGINT stops')\n\n let stopping = false\n const shutdown = (signal: NodeJS.Signals): void => {\n if (stopping) return\n stopping = true\n console.log(`neatd: ${signal} received, stopping…`)\n void handle\n .stop()\n .catch((err) => console.error(`neatd: shutdown error — ${(err as Error).message}`))\n .finally(() => process.exit(0))\n }\n process.on('SIGTERM', shutdown)\n process.on('SIGINT', shutdown)\n\n // Block forever — supervisors keep us in the foreground.\n await new Promise<void>(() => {})\n}\n\nasync function cmdStop(): Promise<void> {\n const pid = await readPid()\n if (pid === null) {\n console.error('neatd: no running daemon found (no PID file)')\n process.exit(1)\n }\n try {\n process.kill(pid, 'SIGTERM')\n console.log(`neatd: SIGTERM sent to PID ${pid}`)\n } catch (err) {\n console.error(`neatd: failed to signal PID ${pid} — ${(err as Error).message}`)\n process.exit(1)\n }\n}\n\nasync function cmdReload(): Promise<void> {\n const pid = await readPid()\n if (pid === null) {\n console.error('neatd: no running daemon found (no PID file)')\n process.exit(1)\n }\n try {\n process.kill(pid, 'SIGHUP')\n console.log(`neatd: SIGHUP sent to PID ${pid}`)\n } catch (err) {\n console.error(`neatd: failed to signal PID ${pid} — ${(err as Error).message}`)\n process.exit(1)\n }\n}\n\nasync function cmdStatus(): Promise<void> {\n const pid = await readPid()\n console.log(`pid: ${pid ?? '(not running)'}`)\n console.log(`registry: ${registryPath()}`)\n const projects = await listProjects().catch(() => [])\n if (projects.length === 0) {\n console.log('projects: (none)')\n return\n }\n console.log('projects:')\n for (const p of projects) {\n const seen = p.lastSeenAt ?? 'never'\n console.log(` ${p.name}\\t${p.status}\\t${p.path}\\tlast-seen=${seen}`)\n }\n}\n\nasync function main(): Promise<void> {\n const cmd = process.argv[2]\n if (!cmd || cmd === '-h' || cmd === '--help') {\n usage()\n process.exit(cmd ? 0 : 2)\n }\n\n if (cmd === 'start') return cmdStart()\n if (cmd === 'stop') return cmdStop()\n if (cmd === 'reload') return cmdReload()\n if (cmd === 'status') return cmdStatus()\n\n console.error(`neatd: unknown command \"${cmd}\"`)\n usage()\n process.exit(1)\n}\n\nconst entry = process.argv[1] ?? ''\nif (/[\\\\/]neatd\\.(?:cjs|js)$/.test(entry) || entry.endsWith('/neatd')) {\n main().catch((err) => {\n console.error(err)\n process.exit(1)\n })\n}\n"],"mappings":"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","names":[]}
|
package/dist/server.cjs
CHANGED
|
@@ -99,8 +99,8 @@ function reshapeGrpcRequest(req) {
|
|
|
99
99
|
};
|
|
100
100
|
}
|
|
101
101
|
function resolveProtoRoot() {
|
|
102
|
-
const here =
|
|
103
|
-
return
|
|
102
|
+
const here = import_node_path30.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
103
|
+
return import_node_path30.default.resolve(here, "..", "proto");
|
|
104
104
|
}
|
|
105
105
|
function loadTraceService() {
|
|
106
106
|
const protoRoot = resolveProtoRoot();
|
|
@@ -155,13 +155,13 @@ async function startOtelGrpcReceiver(opts) {
|
|
|
155
155
|
})
|
|
156
156
|
};
|
|
157
157
|
}
|
|
158
|
-
var import_node_url,
|
|
158
|
+
var import_node_url, import_node_path30, grpc, protoLoader;
|
|
159
159
|
var init_otel_grpc = __esm({
|
|
160
160
|
"src/otel-grpc.ts"() {
|
|
161
161
|
"use strict";
|
|
162
162
|
init_cjs_shims();
|
|
163
163
|
import_node_url = require("url");
|
|
164
|
-
|
|
164
|
+
import_node_path30 = __toESM(require("path"), 1);
|
|
165
165
|
grpc = __toESM(require("@grpc/grpc-js"), 1);
|
|
166
166
|
protoLoader = __toESM(require("@grpc/proto-loader"), 1);
|
|
167
167
|
init_otel();
|
|
@@ -258,10 +258,10 @@ function parseOtlpRequest(body) {
|
|
|
258
258
|
}
|
|
259
259
|
function loadProtobufDecoder() {
|
|
260
260
|
if (exportTraceServiceRequestType) return exportTraceServiceRequestType;
|
|
261
|
-
const here =
|
|
262
|
-
const protoRoot =
|
|
261
|
+
const here = import_node_path31.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
|
|
262
|
+
const protoRoot = import_node_path31.default.resolve(here, "..", "proto");
|
|
263
263
|
const root = new import_protobufjs.default.Root();
|
|
264
|
-
root.resolvePath = (_origin, target) =>
|
|
264
|
+
root.resolvePath = (_origin, target) => import_node_path31.default.resolve(protoRoot, target);
|
|
265
265
|
root.loadSync(
|
|
266
266
|
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
|
|
267
267
|
{ keepCase: true }
|
|
@@ -353,12 +353,12 @@ async function buildOtelReceiver(opts) {
|
|
|
353
353
|
};
|
|
354
354
|
return decorated;
|
|
355
355
|
}
|
|
356
|
-
var
|
|
356
|
+
var import_node_path31, import_node_url2, import_fastify2, import_protobufjs, exportTraceServiceRequestType;
|
|
357
357
|
var init_otel = __esm({
|
|
358
358
|
"src/otel.ts"() {
|
|
359
359
|
"use strict";
|
|
360
360
|
init_cjs_shims();
|
|
361
|
-
|
|
361
|
+
import_node_path31 = __toESM(require("path"), 1);
|
|
362
362
|
import_node_url2 = require("url");
|
|
363
363
|
import_fastify2 = __toESM(require("fastify"), 1);
|
|
364
364
|
import_protobufjs = __toESM(require("protobufjs"), 1);
|
|
@@ -368,7 +368,7 @@ var init_otel = __esm({
|
|
|
368
368
|
|
|
369
369
|
// src/server.ts
|
|
370
370
|
init_cjs_shims();
|
|
371
|
-
var
|
|
371
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
372
372
|
|
|
373
373
|
// src/graph.ts
|
|
374
374
|
init_cjs_shims();
|
|
@@ -392,7 +392,7 @@ function getGraph(project = DEFAULT_PROJECT) {
|
|
|
392
392
|
init_cjs_shims();
|
|
393
393
|
var import_fastify = __toESM(require("fastify"), 1);
|
|
394
394
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
395
|
-
var
|
|
395
|
+
var import_types19 = require("@neat.is/types");
|
|
396
396
|
|
|
397
397
|
// src/policy.ts
|
|
398
398
|
init_cjs_shims();
|
|
@@ -713,6 +713,18 @@ function deprecatedApis() {
|
|
|
713
713
|
return currentMatrix().deprecatedApis ?? [];
|
|
714
714
|
}
|
|
715
715
|
|
|
716
|
+
// src/events.ts
|
|
717
|
+
init_cjs_shims();
|
|
718
|
+
var import_node_events = require("events");
|
|
719
|
+
var EVENT_BUS_CHANNEL = "event";
|
|
720
|
+
var NeatEventBus = class extends import_node_events.EventEmitter {
|
|
721
|
+
};
|
|
722
|
+
var eventBus = new NeatEventBus();
|
|
723
|
+
eventBus.setMaxListeners(0);
|
|
724
|
+
function emitNeatEvent(envelope) {
|
|
725
|
+
eventBus.emit(EVENT_BUS_CHANNEL, envelope);
|
|
726
|
+
}
|
|
727
|
+
|
|
716
728
|
// src/traverse.ts
|
|
717
729
|
init_cjs_shims();
|
|
718
730
|
var import_types = require("@neat.is/types");
|
|
@@ -800,19 +812,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
800
812
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
801
813
|
let best = { path: [start], edges: [] };
|
|
802
814
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
803
|
-
function step(node,
|
|
804
|
-
if (
|
|
805
|
-
best = { path: [...
|
|
815
|
+
function step(node, path34, edges) {
|
|
816
|
+
if (path34.length > best.path.length) {
|
|
817
|
+
best = { path: [...path34], edges: [...edges] };
|
|
806
818
|
}
|
|
807
|
-
if (
|
|
819
|
+
if (path34.length - 1 >= maxDepth) return;
|
|
808
820
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
809
821
|
for (const [srcId, edge] of incoming) {
|
|
810
822
|
if (visited.has(srcId)) continue;
|
|
811
823
|
visited.add(srcId);
|
|
812
|
-
|
|
824
|
+
path34.push(srcId);
|
|
813
825
|
edges.push(edge);
|
|
814
|
-
step(srcId,
|
|
815
|
-
|
|
826
|
+
step(srcId, path34, edges);
|
|
827
|
+
path34.pop();
|
|
816
828
|
edges.pop();
|
|
817
829
|
visited.delete(srcId);
|
|
818
830
|
}
|
|
@@ -1276,9 +1288,11 @@ async function loadPolicyFile(policyPath) {
|
|
|
1276
1288
|
}
|
|
1277
1289
|
var PolicyViolationsLog = class {
|
|
1278
1290
|
path;
|
|
1291
|
+
project;
|
|
1279
1292
|
seen = null;
|
|
1280
|
-
constructor(logPath) {
|
|
1293
|
+
constructor(logPath, project = DEFAULT_PROJECT) {
|
|
1281
1294
|
this.path = logPath;
|
|
1295
|
+
this.project = project;
|
|
1282
1296
|
}
|
|
1283
1297
|
async append(v) {
|
|
1284
1298
|
if (!this.seen) await this.hydrate();
|
|
@@ -1286,6 +1300,11 @@ var PolicyViolationsLog = class {
|
|
|
1286
1300
|
this.seen.add(v.id);
|
|
1287
1301
|
await import_node_fs2.promises.mkdir(import_node_path2.default.dirname(this.path), { recursive: true });
|
|
1288
1302
|
await import_node_fs2.promises.appendFile(this.path, JSON.stringify(v) + "\n", "utf8");
|
|
1303
|
+
emitNeatEvent({
|
|
1304
|
+
type: "policy-violation",
|
|
1305
|
+
project: this.project,
|
|
1306
|
+
payload: { violation: v }
|
|
1307
|
+
});
|
|
1289
1308
|
return true;
|
|
1290
1309
|
}
|
|
1291
1310
|
async readAll() {
|
|
@@ -1741,6 +1760,7 @@ async function markStaleEdges(graph, options = {}) {
|
|
|
1741
1760
|
const thresholds = options.thresholds ?? loadStaleThresholdsFromEnv();
|
|
1742
1761
|
const now = options.now ?? Date.now();
|
|
1743
1762
|
const events = [];
|
|
1763
|
+
const project = options.project ?? DEFAULT_PROJECT;
|
|
1744
1764
|
graph.forEachEdge((id, attrs) => {
|
|
1745
1765
|
const e = attrs;
|
|
1746
1766
|
if (e.provenance !== import_types3.Provenance.OBSERVED) return;
|
|
@@ -1760,6 +1780,15 @@ async function markStaleEdges(graph, options = {}) {
|
|
|
1760
1780
|
lastObserved: e.lastObserved,
|
|
1761
1781
|
transitionedAt: new Date(now).toISOString()
|
|
1762
1782
|
});
|
|
1783
|
+
emitNeatEvent({
|
|
1784
|
+
type: "stale-transition",
|
|
1785
|
+
project,
|
|
1786
|
+
payload: {
|
|
1787
|
+
edgeId: id,
|
|
1788
|
+
from: import_types3.Provenance.OBSERVED,
|
|
1789
|
+
to: import_types3.Provenance.STALE
|
|
1790
|
+
}
|
|
1791
|
+
});
|
|
1763
1792
|
}
|
|
1764
1793
|
});
|
|
1765
1794
|
if (options.staleEventsPath && events.length > 0) {
|
|
@@ -1790,7 +1819,8 @@ function startStalenessLoop(graph, options = {}) {
|
|
|
1790
1819
|
try {
|
|
1791
1820
|
await markStaleEdges(graph, {
|
|
1792
1821
|
thresholds: options.thresholds,
|
|
1793
|
-
staleEventsPath: options.staleEventsPath
|
|
1822
|
+
staleEventsPath: options.staleEventsPath,
|
|
1823
|
+
project: options.project
|
|
1794
1824
|
});
|
|
1795
1825
|
if (options.onPolicyTrigger) await options.onPolicyTrigger(graph);
|
|
1796
1826
|
} catch (err) {
|
|
@@ -3020,7 +3050,15 @@ async function addHttpCallEdges(graph, services) {
|
|
|
3020
3050
|
const seenTargets = /* @__PURE__ */ new Map();
|
|
3021
3051
|
for (const file of files) {
|
|
3022
3052
|
const parser = import_node_path18.default.extname(file.path) === ".py" ? pyParser : jsParser;
|
|
3023
|
-
|
|
3053
|
+
let targets;
|
|
3054
|
+
try {
|
|
3055
|
+
targets = callsFromSource(file.content, parser, knownHosts);
|
|
3056
|
+
} catch (err) {
|
|
3057
|
+
console.warn(
|
|
3058
|
+
`[neat] http call extraction skipped ${file.path}: ${err.message}`
|
|
3059
|
+
);
|
|
3060
|
+
continue;
|
|
3061
|
+
}
|
|
3024
3062
|
for (const t of targets) {
|
|
3025
3063
|
const targetId = hostToNodeId.get(t);
|
|
3026
3064
|
if (!targetId || targetId === service.node.id) continue;
|
|
@@ -3549,11 +3587,22 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
3549
3587
|
const phase5 = await addInfra(graph, scanPath, services);
|
|
3550
3588
|
const frontiersPromoted = promoteFrontierNodes(graph);
|
|
3551
3589
|
if (opts.onPolicyTrigger) await opts.onPolicyTrigger(graph);
|
|
3552
|
-
|
|
3590
|
+
const result = {
|
|
3553
3591
|
nodesAdded: phase1Nodes + phase2.nodesAdded + phase3.nodesAdded + phase4.nodesAdded + phase5.nodesAdded,
|
|
3554
3592
|
edgesAdded: phase2.edgesAdded + phase3.edgesAdded + phase4.edgesAdded + phase5.edgesAdded,
|
|
3555
3593
|
frontiersPromoted
|
|
3556
3594
|
};
|
|
3595
|
+
emitNeatEvent({
|
|
3596
|
+
type: "extraction-complete",
|
|
3597
|
+
project: opts.project ?? DEFAULT_PROJECT,
|
|
3598
|
+
payload: {
|
|
3599
|
+
project: opts.project ?? DEFAULT_PROJECT,
|
|
3600
|
+
fileCount: services.length,
|
|
3601
|
+
nodesAdded: result.nodesAdded,
|
|
3602
|
+
edgesAdded: result.edgesAdded
|
|
3603
|
+
}
|
|
3604
|
+
});
|
|
3605
|
+
return result;
|
|
3557
3606
|
}
|
|
3558
3607
|
|
|
3559
3608
|
// src/diff.ts
|
|
@@ -3689,6 +3738,94 @@ function parseExtraProjects(raw) {
|
|
|
3689
3738
|
return raw.split(",").map((p) => p.trim()).filter((p) => p.length > 0 && p !== DEFAULT_PROJECT);
|
|
3690
3739
|
}
|
|
3691
3740
|
|
|
3741
|
+
// src/registry.ts
|
|
3742
|
+
init_cjs_shims();
|
|
3743
|
+
var import_node_fs16 = require("fs");
|
|
3744
|
+
var import_node_os2 = __toESM(require("os"), 1);
|
|
3745
|
+
var import_node_path28 = __toESM(require("path"), 1);
|
|
3746
|
+
var import_types18 = require("@neat.is/types");
|
|
3747
|
+
function neatHome() {
|
|
3748
|
+
const override = process.env.NEAT_HOME;
|
|
3749
|
+
if (override && override.length > 0) return import_node_path28.default.resolve(override);
|
|
3750
|
+
return import_node_path28.default.join(import_node_os2.default.homedir(), ".neat");
|
|
3751
|
+
}
|
|
3752
|
+
function registryPath() {
|
|
3753
|
+
return import_node_path28.default.join(neatHome(), "projects.json");
|
|
3754
|
+
}
|
|
3755
|
+
async function readRegistry() {
|
|
3756
|
+
const file = registryPath();
|
|
3757
|
+
let raw;
|
|
3758
|
+
try {
|
|
3759
|
+
raw = await import_node_fs16.promises.readFile(file, "utf8");
|
|
3760
|
+
} catch (err) {
|
|
3761
|
+
if (err.code === "ENOENT") {
|
|
3762
|
+
return { version: 1, projects: [] };
|
|
3763
|
+
}
|
|
3764
|
+
throw err;
|
|
3765
|
+
}
|
|
3766
|
+
const parsed = JSON.parse(raw);
|
|
3767
|
+
return import_types18.RegistryFileSchema.parse(parsed);
|
|
3768
|
+
}
|
|
3769
|
+
async function listProjects() {
|
|
3770
|
+
const reg = await readRegistry();
|
|
3771
|
+
return reg.projects;
|
|
3772
|
+
}
|
|
3773
|
+
|
|
3774
|
+
// src/streaming.ts
|
|
3775
|
+
init_cjs_shims();
|
|
3776
|
+
var SSE_HEARTBEAT_MS = 3e4;
|
|
3777
|
+
var SSE_BACKPRESSURE_CAP = 1e3;
|
|
3778
|
+
function handleSse(req, reply, opts) {
|
|
3779
|
+
const heartbeatMs = opts.heartbeatMs ?? SSE_HEARTBEAT_MS;
|
|
3780
|
+
const backpressureCap = opts.backpressureCap ?? SSE_BACKPRESSURE_CAP;
|
|
3781
|
+
reply.raw.setHeader("Content-Type", "text/event-stream");
|
|
3782
|
+
reply.raw.setHeader("Cache-Control", "no-cache, no-transform");
|
|
3783
|
+
reply.raw.setHeader("Connection", "keep-alive");
|
|
3784
|
+
reply.raw.setHeader("X-Accel-Buffering", "no");
|
|
3785
|
+
reply.raw.flushHeaders?.();
|
|
3786
|
+
let pending = 0;
|
|
3787
|
+
let dropped = false;
|
|
3788
|
+
const closeConnection = () => {
|
|
3789
|
+
if (dropped) return;
|
|
3790
|
+
dropped = true;
|
|
3791
|
+
eventBus.off(EVENT_BUS_CHANNEL, listener);
|
|
3792
|
+
clearInterval(heartbeat);
|
|
3793
|
+
if (!reply.raw.writableEnded) reply.raw.end();
|
|
3794
|
+
};
|
|
3795
|
+
const writeFrame = (frame) => {
|
|
3796
|
+
if (dropped) return;
|
|
3797
|
+
if (pending >= backpressureCap) {
|
|
3798
|
+
const errFrame = `event: error
|
|
3799
|
+
data: ${JSON.stringify({ reason: "backpressure" })}
|
|
3800
|
+
|
|
3801
|
+
`;
|
|
3802
|
+
reply.raw.write(errFrame);
|
|
3803
|
+
closeConnection();
|
|
3804
|
+
return;
|
|
3805
|
+
}
|
|
3806
|
+
pending++;
|
|
3807
|
+
reply.raw.write(frame, () => {
|
|
3808
|
+
pending = Math.max(0, pending - 1);
|
|
3809
|
+
});
|
|
3810
|
+
};
|
|
3811
|
+
const listener = (envelope) => {
|
|
3812
|
+
if (envelope.project !== opts.project) return;
|
|
3813
|
+
writeFrame(`event: ${envelope.type}
|
|
3814
|
+
data: ${JSON.stringify(envelope.payload)}
|
|
3815
|
+
|
|
3816
|
+
`);
|
|
3817
|
+
};
|
|
3818
|
+
eventBus.on(EVENT_BUS_CHANNEL, listener);
|
|
3819
|
+
const heartbeat = setInterval(() => {
|
|
3820
|
+
if (dropped) return;
|
|
3821
|
+
reply.raw.write(":heartbeat\n\n");
|
|
3822
|
+
}, heartbeatMs);
|
|
3823
|
+
if (typeof heartbeat.unref === "function") heartbeat.unref();
|
|
3824
|
+
req.raw.on("close", closeConnection);
|
|
3825
|
+
reply.raw.on("close", closeConnection);
|
|
3826
|
+
reply.raw.on("error", closeConnection);
|
|
3827
|
+
}
|
|
3828
|
+
|
|
3692
3829
|
// src/api.ts
|
|
3693
3830
|
function serializeGraph(graph) {
|
|
3694
3831
|
const nodes = [];
|
|
@@ -3737,6 +3874,11 @@ function buildLegacyRegistry(opts) {
|
|
|
3737
3874
|
}
|
|
3738
3875
|
function registerRoutes(scope, ctx) {
|
|
3739
3876
|
const { registry, startedAt, errorsPathFor, staleEventsPathFor } = ctx;
|
|
3877
|
+
scope.get("/events", (req, reply) => {
|
|
3878
|
+
const proj = resolveProject(registry, req, reply);
|
|
3879
|
+
if (!proj) return;
|
|
3880
|
+
handleSse(req, reply, { project: proj.name });
|
|
3881
|
+
});
|
|
3740
3882
|
scope.get("/health", async (req, reply) => {
|
|
3741
3883
|
const proj = resolveProject(registry, req, reply);
|
|
3742
3884
|
if (!proj) return;
|
|
@@ -3947,7 +4089,7 @@ function registerRoutes(scope, ctx) {
|
|
|
3947
4089
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
3948
4090
|
let violations = await log.readAll();
|
|
3949
4091
|
if (req.query.severity) {
|
|
3950
|
-
const sev =
|
|
4092
|
+
const sev = import_types19.PolicySeveritySchema.safeParse(req.query.severity);
|
|
3951
4093
|
if (!sev.success) {
|
|
3952
4094
|
return reply.code(400).send({
|
|
3953
4095
|
error: "invalid severity",
|
|
@@ -3964,7 +4106,7 @@ function registerRoutes(scope, ctx) {
|
|
|
3964
4106
|
scope.post("/policies/check", async (req, reply) => {
|
|
3965
4107
|
const proj = resolveProject(registry, req, reply);
|
|
3966
4108
|
if (!proj) return;
|
|
3967
|
-
const parsed =
|
|
4109
|
+
const parsed = import_types19.PoliciesCheckBodySchema.safeParse(req.body ?? {});
|
|
3968
4110
|
if (!parsed.success) {
|
|
3969
4111
|
return reply.code(400).send({
|
|
3970
4112
|
error: "invalid /policies/check body",
|
|
@@ -4028,17 +4170,16 @@ async function buildApi(opts) {
|
|
|
4028
4170
|
staleEventsPathFor,
|
|
4029
4171
|
policyFilePathFor
|
|
4030
4172
|
};
|
|
4031
|
-
app.get("/projects", async () =>
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
}));
|
|
4173
|
+
app.get("/projects", async (_req, reply) => {
|
|
4174
|
+
try {
|
|
4175
|
+
return await listProjects();
|
|
4176
|
+
} catch (err) {
|
|
4177
|
+
return reply.code(500).send({
|
|
4178
|
+
error: "failed to read project registry",
|
|
4179
|
+
details: err.message
|
|
4180
|
+
});
|
|
4181
|
+
}
|
|
4182
|
+
});
|
|
4042
4183
|
registerRoutes(app, routeCtx);
|
|
4043
4184
|
await app.register(
|
|
4044
4185
|
async (scope) => {
|
|
@@ -4051,8 +4192,8 @@ async function buildApi(opts) {
|
|
|
4051
4192
|
|
|
4052
4193
|
// src/persist.ts
|
|
4053
4194
|
init_cjs_shims();
|
|
4054
|
-
var
|
|
4055
|
-
var
|
|
4195
|
+
var import_node_fs17 = require("fs");
|
|
4196
|
+
var import_node_path29 = __toESM(require("path"), 1);
|
|
4056
4197
|
var SCHEMA_VERSION = 2;
|
|
4057
4198
|
function migrateV1ToV2(payload) {
|
|
4058
4199
|
const nodes = payload.graph.nodes;
|
|
@@ -4066,7 +4207,7 @@ function migrateV1ToV2(payload) {
|
|
|
4066
4207
|
return { ...payload, schemaVersion: 2 };
|
|
4067
4208
|
}
|
|
4068
4209
|
async function ensureDir(filePath) {
|
|
4069
|
-
await
|
|
4210
|
+
await import_node_fs17.promises.mkdir(import_node_path29.default.dirname(filePath), { recursive: true });
|
|
4070
4211
|
}
|
|
4071
4212
|
async function saveGraphToDisk(graph, outPath) {
|
|
4072
4213
|
await ensureDir(outPath);
|
|
@@ -4076,13 +4217,13 @@ async function saveGraphToDisk(graph, outPath) {
|
|
|
4076
4217
|
graph: graph.export()
|
|
4077
4218
|
};
|
|
4078
4219
|
const tmp = `${outPath}.tmp`;
|
|
4079
|
-
await
|
|
4080
|
-
await
|
|
4220
|
+
await import_node_fs17.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
|
|
4221
|
+
await import_node_fs17.promises.rename(tmp, outPath);
|
|
4081
4222
|
}
|
|
4082
4223
|
async function loadGraphFromDisk(graph, outPath) {
|
|
4083
4224
|
let raw;
|
|
4084
4225
|
try {
|
|
4085
|
-
raw = await
|
|
4226
|
+
raw = await import_node_fs17.promises.readFile(outPath, "utf8");
|
|
4086
4227
|
} catch (err) {
|
|
4087
4228
|
if (err.code === "ENOENT") return;
|
|
4088
4229
|
throw err;
|
|
@@ -4139,8 +4280,8 @@ init_otel_grpc();
|
|
|
4139
4280
|
|
|
4140
4281
|
// src/search.ts
|
|
4141
4282
|
init_cjs_shims();
|
|
4142
|
-
var
|
|
4143
|
-
var
|
|
4283
|
+
var import_node_fs18 = require("fs");
|
|
4284
|
+
var import_node_path32 = __toESM(require("path"), 1);
|
|
4144
4285
|
var import_node_crypto = require("crypto");
|
|
4145
4286
|
var DEFAULT_LIMIT = 10;
|
|
4146
4287
|
var NOMIC_DIM = 768;
|
|
@@ -4270,7 +4411,7 @@ async function pickEmbedder() {
|
|
|
4270
4411
|
}
|
|
4271
4412
|
async function readCache(cachePath) {
|
|
4272
4413
|
try {
|
|
4273
|
-
const raw = await
|
|
4414
|
+
const raw = await import_node_fs18.promises.readFile(cachePath, "utf8");
|
|
4274
4415
|
const parsed = JSON.parse(raw);
|
|
4275
4416
|
if (parsed.version !== 1) return null;
|
|
4276
4417
|
return parsed;
|
|
@@ -4279,8 +4420,8 @@ async function readCache(cachePath) {
|
|
|
4279
4420
|
}
|
|
4280
4421
|
}
|
|
4281
4422
|
async function writeCache(cachePath, cache) {
|
|
4282
|
-
await
|
|
4283
|
-
await
|
|
4423
|
+
await import_node_fs18.promises.mkdir(import_node_path32.default.dirname(cachePath), { recursive: true });
|
|
4424
|
+
await import_node_fs18.promises.writeFile(cachePath, JSON.stringify(cache));
|
|
4284
4425
|
}
|
|
4285
4426
|
var VectorIndex = class {
|
|
4286
4427
|
constructor(embedder, cachePath) {
|
|
@@ -4459,14 +4600,14 @@ async function bootProject(registry, name, scanPath, baseDir) {
|
|
|
4459
4600
|
async function main() {
|
|
4460
4601
|
const baseDirEnv = process.env.NEAT_OUT_DIR;
|
|
4461
4602
|
const legacyOutPath = process.env.NEAT_OUT_PATH;
|
|
4462
|
-
const baseDir = baseDirEnv ?
|
|
4463
|
-
const defaultScanPath =
|
|
4603
|
+
const baseDir = baseDirEnv ? import_node_path33.default.resolve(baseDirEnv) : legacyOutPath ? import_node_path33.default.resolve(import_node_path33.default.dirname(legacyOutPath)) : import_node_path33.default.resolve("./neat-out");
|
|
4604
|
+
const defaultScanPath = import_node_path33.default.resolve(process.env.NEAT_SCAN_PATH ?? "./demo");
|
|
4464
4605
|
const registry = new Projects();
|
|
4465
4606
|
await bootProject(registry, DEFAULT_PROJECT, defaultScanPath, baseDir);
|
|
4466
4607
|
for (const name of parseExtraProjects(process.env.NEAT_PROJECTS)) {
|
|
4467
4608
|
const envKey = `NEAT_PROJECT_SCAN_PATH_${name.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
|
|
4468
4609
|
const projectScan = process.env[envKey];
|
|
4469
|
-
await bootProject(registry, name, projectScan ?
|
|
4610
|
+
await bootProject(registry, name, projectScan ? import_node_path33.default.resolve(projectScan) : void 0, baseDir);
|
|
4470
4611
|
}
|
|
4471
4612
|
const host = process.env.HOST ?? "0.0.0.0";
|
|
4472
4613
|
const port = Number(process.env.PORT ?? 8080);
|