@neat.is/core 0.4.17 → 0.4.19
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-GHPHVXYM.js → chunk-MTXF77TN.js} +2 -2
- package/dist/{chunk-LUDSPX5N.js → chunk-Q5EDVWKZ.js} +178 -39
- package/dist/{chunk-LUDSPX5N.js.map → chunk-Q5EDVWKZ.js.map} +1 -1
- package/dist/{chunk-CEDXXMGO.js → chunk-ZV7GHZ2D.js} +208 -24
- package/dist/chunk-ZV7GHZ2D.js.map +1 -0
- package/dist/cli.cjs +520 -172
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +256 -70
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +260 -58
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +52 -14
- package/dist/index.d.ts +52 -14
- package/dist/index.js +4 -4
- package/dist/neatd.cjs +429 -96
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +168 -37
- package/dist/neatd.js.map +1 -1
- package/dist/{otel-grpc-QAISVAY5.js → otel-grpc-I67ONCRM.js} +3 -3
- package/dist/server.cjs +69 -37
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +3 -3
- package/package.json +2 -2
- package/dist/chunk-CEDXXMGO.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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
mountBearerAuth,
|
|
3
3
|
readAuthEnv
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-BGPWBRLU.js";
|
|
5
5
|
|
|
6
6
|
// src/graph.ts
|
|
7
7
|
import GraphDefault from "graphology";
|
|
@@ -4980,7 +4980,9 @@ async function loadGraphFromDisk(graph, outPath) {
|
|
|
4980
4980
|
graph.clear();
|
|
4981
4981
|
graph.import(payload.graph);
|
|
4982
4982
|
}
|
|
4983
|
-
function startPersistLoop(graph, outPath,
|
|
4983
|
+
function startPersistLoop(graph, outPath, opts = {}) {
|
|
4984
|
+
const intervalMs = opts.intervalMs ?? 6e4;
|
|
4985
|
+
const exitOnSignal = opts.exitOnSignal ?? true;
|
|
4984
4986
|
let stopped = false;
|
|
4985
4987
|
const tick = async () => {
|
|
4986
4988
|
if (stopped) return;
|
|
@@ -4993,7 +4995,7 @@ function startPersistLoop(graph, outPath, intervalMs = 6e4) {
|
|
|
4993
4995
|
const interval = setInterval(() => {
|
|
4994
4996
|
void tick();
|
|
4995
4997
|
}, intervalMs);
|
|
4996
|
-
const onSignal = (signal) => {
|
|
4998
|
+
const onSignal = exitOnSignal ? (signal) => {
|
|
4997
4999
|
void (async () => {
|
|
4998
5000
|
try {
|
|
4999
5001
|
await saveGraphToDisk(graph, outPath);
|
|
@@ -5003,14 +5005,18 @@ function startPersistLoop(graph, outPath, intervalMs = 6e4) {
|
|
|
5003
5005
|
process.exit(0);
|
|
5004
5006
|
}
|
|
5005
5007
|
})();
|
|
5006
|
-
};
|
|
5007
|
-
|
|
5008
|
-
|
|
5008
|
+
} : null;
|
|
5009
|
+
if (onSignal) {
|
|
5010
|
+
process.on("SIGTERM", onSignal);
|
|
5011
|
+
process.on("SIGINT", onSignal);
|
|
5012
|
+
}
|
|
5009
5013
|
return () => {
|
|
5010
5014
|
stopped = true;
|
|
5011
5015
|
clearInterval(interval);
|
|
5012
|
-
|
|
5013
|
-
|
|
5016
|
+
if (onSignal) {
|
|
5017
|
+
process.off("SIGTERM", onSignal);
|
|
5018
|
+
process.off("SIGINT", onSignal);
|
|
5019
|
+
}
|
|
5014
5020
|
};
|
|
5015
5021
|
}
|
|
5016
5022
|
|
|
@@ -5168,6 +5174,111 @@ function registryLockPath() {
|
|
|
5168
5174
|
function daemonPidPath() {
|
|
5169
5175
|
return path36.join(neatHome(), "neatd.pid");
|
|
5170
5176
|
}
|
|
5177
|
+
function daemonsDir() {
|
|
5178
|
+
return path36.join(neatHome(), "daemons");
|
|
5179
|
+
}
|
|
5180
|
+
function isFiniteInt(v) {
|
|
5181
|
+
return typeof v === "number" && Number.isFinite(v);
|
|
5182
|
+
}
|
|
5183
|
+
function parseDaemonRecord(raw) {
|
|
5184
|
+
let obj;
|
|
5185
|
+
try {
|
|
5186
|
+
obj = JSON.parse(raw);
|
|
5187
|
+
} catch {
|
|
5188
|
+
return void 0;
|
|
5189
|
+
}
|
|
5190
|
+
if (typeof obj !== "object" || obj === null) return void 0;
|
|
5191
|
+
const r = obj;
|
|
5192
|
+
const ports = r.ports;
|
|
5193
|
+
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)) {
|
|
5194
|
+
return void 0;
|
|
5195
|
+
}
|
|
5196
|
+
return {
|
|
5197
|
+
project: r.project,
|
|
5198
|
+
projectPath: r.projectPath,
|
|
5199
|
+
pid: r.pid,
|
|
5200
|
+
status: r.status,
|
|
5201
|
+
ports: { rest: ports.rest, otlp: ports.otlp, web: ports.web },
|
|
5202
|
+
startedAt: r.startedAt,
|
|
5203
|
+
neatVersion: r.neatVersion
|
|
5204
|
+
};
|
|
5205
|
+
}
|
|
5206
|
+
var defaultDiscoveryProbe = { isPidAlive: isPidAliveDefault };
|
|
5207
|
+
async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
5208
|
+
const dir = daemonsDir();
|
|
5209
|
+
let names;
|
|
5210
|
+
try {
|
|
5211
|
+
names = await fs20.readdir(dir);
|
|
5212
|
+
} catch (err) {
|
|
5213
|
+
if (err.code === "ENOENT") return [];
|
|
5214
|
+
throw err;
|
|
5215
|
+
}
|
|
5216
|
+
const out = [];
|
|
5217
|
+
for (const name of names) {
|
|
5218
|
+
if (!name.endsWith(".json")) continue;
|
|
5219
|
+
const file = path36.join(dir, name);
|
|
5220
|
+
let raw;
|
|
5221
|
+
try {
|
|
5222
|
+
raw = await fs20.readFile(file, "utf8");
|
|
5223
|
+
} catch {
|
|
5224
|
+
continue;
|
|
5225
|
+
}
|
|
5226
|
+
const record = parseDaemonRecord(raw);
|
|
5227
|
+
if (!record) continue;
|
|
5228
|
+
const live = record.status === "running" && probe.isPidAlive(record.pid);
|
|
5229
|
+
out.push({ record, live, source: file });
|
|
5230
|
+
}
|
|
5231
|
+
out.sort((a, b) => a.record.project.localeCompare(b.record.project));
|
|
5232
|
+
return out;
|
|
5233
|
+
}
|
|
5234
|
+
async function removeDaemonRecord(source) {
|
|
5235
|
+
await fs20.unlink(source).catch(() => {
|
|
5236
|
+
});
|
|
5237
|
+
}
|
|
5238
|
+
async function listMachineProjects(probe = defaultDiscoveryProbe) {
|
|
5239
|
+
const discovered = await discoverDaemons(probe);
|
|
5240
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
5241
|
+
for (const d of discovered) {
|
|
5242
|
+
const key = await normalizeProjectPath(d.record.projectPath);
|
|
5243
|
+
byPath.set(key, {
|
|
5244
|
+
project: d.record.project,
|
|
5245
|
+
projectPath: d.record.projectPath,
|
|
5246
|
+
state: d.live ? "running" : "stopped",
|
|
5247
|
+
ports: d.record.ports,
|
|
5248
|
+
pid: d.record.pid
|
|
5249
|
+
});
|
|
5250
|
+
}
|
|
5251
|
+
let legacy = [];
|
|
5252
|
+
try {
|
|
5253
|
+
legacy = (await readRegistry()).projects;
|
|
5254
|
+
} catch {
|
|
5255
|
+
legacy = [];
|
|
5256
|
+
}
|
|
5257
|
+
for (const entry of legacy) {
|
|
5258
|
+
const key = await normalizeProjectPath(entry.path);
|
|
5259
|
+
if (byPath.has(key)) continue;
|
|
5260
|
+
byPath.set(key, {
|
|
5261
|
+
project: entry.name,
|
|
5262
|
+
projectPath: entry.path,
|
|
5263
|
+
state: "registered",
|
|
5264
|
+
registryStatus: entry.status
|
|
5265
|
+
});
|
|
5266
|
+
}
|
|
5267
|
+
return [...byPath.values()].sort((a, b) => a.project.localeCompare(b.project));
|
|
5268
|
+
}
|
|
5269
|
+
async function findDaemonByProject(name, probe = defaultDiscoveryProbe) {
|
|
5270
|
+
const discovered = await discoverDaemons(probe);
|
|
5271
|
+
return discovered.find((d) => d.record.project === name);
|
|
5272
|
+
}
|
|
5273
|
+
function signalDaemonStop(pid) {
|
|
5274
|
+
try {
|
|
5275
|
+
process.kill(pid, "SIGTERM");
|
|
5276
|
+
return true;
|
|
5277
|
+
} catch (err) {
|
|
5278
|
+
if (err.code === "ESRCH") return false;
|
|
5279
|
+
throw err;
|
|
5280
|
+
}
|
|
5281
|
+
}
|
|
5171
5282
|
function isPidAliveDefault(pid) {
|
|
5172
5283
|
try {
|
|
5173
5284
|
process.kill(pid, 0);
|
|
@@ -5821,12 +5932,16 @@ function serializeGraph(graph) {
|
|
|
5821
5932
|
});
|
|
5822
5933
|
return { nodes, edges };
|
|
5823
5934
|
}
|
|
5824
|
-
function projectFromReq(req) {
|
|
5935
|
+
function projectFromReq(req, singleProject) {
|
|
5825
5936
|
const params = req.params;
|
|
5826
|
-
|
|
5937
|
+
const named = params.project;
|
|
5938
|
+
if (singleProject) {
|
|
5939
|
+
return named === void 0 || named === DEFAULT_PROJECT ? singleProject : named;
|
|
5940
|
+
}
|
|
5941
|
+
return named ?? DEFAULT_PROJECT;
|
|
5827
5942
|
}
|
|
5828
|
-
function resolveProject(registry, req, reply, bootstrap) {
|
|
5829
|
-
const name = projectFromReq(req);
|
|
5943
|
+
function resolveProject(registry, req, reply, bootstrap, singleProject) {
|
|
5944
|
+
const name = projectFromReq(req, singleProject);
|
|
5830
5945
|
const ctx = registry.get(name);
|
|
5831
5946
|
if (!ctx) {
|
|
5832
5947
|
const phase = bootstrap?.status(name);
|
|
@@ -5867,13 +5982,13 @@ function buildLegacyRegistry(opts) {
|
|
|
5867
5982
|
function registerRoutes(scope, ctx) {
|
|
5868
5983
|
const { registry, startedAt, errorsPathFor, staleEventsPathFor } = ctx;
|
|
5869
5984
|
scope.get("/events", (req, reply) => {
|
|
5870
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
5985
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
5871
5986
|
if (!proj) return;
|
|
5872
5987
|
handleSse(req, reply, { project: proj.name });
|
|
5873
5988
|
});
|
|
5874
5989
|
if (ctx.scope === "project") {
|
|
5875
5990
|
scope.get("/health", async (req, reply) => {
|
|
5876
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
5991
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
5877
5992
|
if (!proj) return;
|
|
5878
5993
|
const uptimeMs = Date.now() - startedAt;
|
|
5879
5994
|
return {
|
|
@@ -5891,14 +6006,14 @@ function registerRoutes(scope, ctx) {
|
|
|
5891
6006
|
});
|
|
5892
6007
|
}
|
|
5893
6008
|
scope.get("/graph", async (req, reply) => {
|
|
5894
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6009
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
5895
6010
|
if (!proj) return;
|
|
5896
6011
|
return serializeGraph(proj.graph);
|
|
5897
6012
|
});
|
|
5898
6013
|
scope.get(
|
|
5899
6014
|
"/graph/node/:id",
|
|
5900
6015
|
async (req, reply) => {
|
|
5901
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6016
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
5902
6017
|
if (!proj) return;
|
|
5903
6018
|
const { id } = req.params;
|
|
5904
6019
|
if (!proj.graph.hasNode(id)) {
|
|
@@ -5910,7 +6025,7 @@ function registerRoutes(scope, ctx) {
|
|
|
5910
6025
|
scope.get(
|
|
5911
6026
|
"/graph/edges/:id",
|
|
5912
6027
|
async (req, reply) => {
|
|
5913
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6028
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
5914
6029
|
if (!proj) return;
|
|
5915
6030
|
const { id } = req.params;
|
|
5916
6031
|
if (!proj.graph.hasNode(id)) {
|
|
@@ -5922,7 +6037,7 @@ function registerRoutes(scope, ctx) {
|
|
|
5922
6037
|
}
|
|
5923
6038
|
);
|
|
5924
6039
|
scope.get("/graph/dependencies/:nodeId", async (req, reply) => {
|
|
5925
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6040
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
5926
6041
|
if (!proj) return;
|
|
5927
6042
|
const { nodeId } = req.params;
|
|
5928
6043
|
if (!proj.graph.hasNode(nodeId)) {
|
|
@@ -5937,7 +6052,7 @@ function registerRoutes(scope, ctx) {
|
|
|
5937
6052
|
return getTransitiveDependencies(proj.graph, nodeId, depth);
|
|
5938
6053
|
});
|
|
5939
6054
|
scope.get("/graph/divergences", async (req, reply) => {
|
|
5940
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6055
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
5941
6056
|
if (!proj) return;
|
|
5942
6057
|
let typeFilter;
|
|
5943
6058
|
if (req.query.type) {
|
|
@@ -5972,7 +6087,7 @@ function registerRoutes(scope, ctx) {
|
|
|
5972
6087
|
});
|
|
5973
6088
|
});
|
|
5974
6089
|
scope.get("/incidents", async (req, reply) => {
|
|
5975
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6090
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
5976
6091
|
if (!proj) return;
|
|
5977
6092
|
const epath = errorsPathFor(proj);
|
|
5978
6093
|
if (!epath) return { count: 0, total: 0, events: [] };
|
|
@@ -5984,7 +6099,7 @@ function registerRoutes(scope, ctx) {
|
|
|
5984
6099
|
return { count: sliced.length, total, events: sliced };
|
|
5985
6100
|
});
|
|
5986
6101
|
scope.get("/stale-events", async (req, reply) => {
|
|
5987
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6102
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
5988
6103
|
if (!proj) return;
|
|
5989
6104
|
const spath = staleEventsPathFor(proj);
|
|
5990
6105
|
if (!spath) return { count: 0, total: 0, events: [] };
|
|
@@ -5999,7 +6114,7 @@ function registerRoutes(scope, ctx) {
|
|
|
5999
6114
|
scope.get(
|
|
6000
6115
|
"/incidents/:nodeId",
|
|
6001
6116
|
async (req, reply) => {
|
|
6002
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6117
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6003
6118
|
if (!proj) return;
|
|
6004
6119
|
const { nodeId } = req.params;
|
|
6005
6120
|
if (!proj.graph.hasNode(nodeId)) {
|
|
@@ -6015,7 +6130,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6015
6130
|
}
|
|
6016
6131
|
);
|
|
6017
6132
|
scope.get("/graph/root-cause/:nodeId", async (req, reply) => {
|
|
6018
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6133
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6019
6134
|
if (!proj) return;
|
|
6020
6135
|
const { nodeId } = req.params;
|
|
6021
6136
|
if (!proj.graph.hasNode(nodeId)) {
|
|
@@ -6035,7 +6150,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6035
6150
|
return result;
|
|
6036
6151
|
});
|
|
6037
6152
|
scope.get("/graph/blast-radius/:nodeId", async (req, reply) => {
|
|
6038
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6153
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6039
6154
|
if (!proj) return;
|
|
6040
6155
|
const { nodeId } = req.params;
|
|
6041
6156
|
if (!proj.graph.hasNode(nodeId)) {
|
|
@@ -6048,7 +6163,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6048
6163
|
return getBlastRadius(proj.graph, nodeId, depth);
|
|
6049
6164
|
});
|
|
6050
6165
|
scope.get("/search", async (req, reply) => {
|
|
6051
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6166
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6052
6167
|
if (!proj) return;
|
|
6053
6168
|
const raw = (req.query.q ?? "").trim();
|
|
6054
6169
|
if (!raw) return reply.code(400).send({ error: "query parameter `q` is required" });
|
|
@@ -6079,7 +6194,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6079
6194
|
scope.get(
|
|
6080
6195
|
"/graph/diff",
|
|
6081
6196
|
async (req, reply) => {
|
|
6082
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6197
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6083
6198
|
if (!proj) return;
|
|
6084
6199
|
const against = req.query.against;
|
|
6085
6200
|
if (!against) {
|
|
@@ -6094,7 +6209,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6094
6209
|
}
|
|
6095
6210
|
);
|
|
6096
6211
|
scope.post("/snapshot", async (req, reply) => {
|
|
6097
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6212
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6098
6213
|
if (!proj) return;
|
|
6099
6214
|
const body = req.body;
|
|
6100
6215
|
if (!body || typeof body !== "object" || !body.snapshot) {
|
|
@@ -6123,7 +6238,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6123
6238
|
}
|
|
6124
6239
|
});
|
|
6125
6240
|
scope.post("/graph/scan", async (req, reply) => {
|
|
6126
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6241
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6127
6242
|
if (!proj) return;
|
|
6128
6243
|
if (!proj.scanPath) {
|
|
6129
6244
|
return reply.code(409).send({ error: "scan path not configured for this project", project: proj.name });
|
|
@@ -6139,7 +6254,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6139
6254
|
};
|
|
6140
6255
|
});
|
|
6141
6256
|
scope.get("/policies", async (req, reply) => {
|
|
6142
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6257
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6143
6258
|
if (!proj) return;
|
|
6144
6259
|
const policyPath = ctx.policyFilePathFor(proj);
|
|
6145
6260
|
if (!policyPath) {
|
|
@@ -6156,7 +6271,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6156
6271
|
}
|
|
6157
6272
|
});
|
|
6158
6273
|
scope.get("/policies/violations", async (req, reply) => {
|
|
6159
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6274
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6160
6275
|
if (!proj) return;
|
|
6161
6276
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
6162
6277
|
let violations = await log.readAll();
|
|
@@ -6176,7 +6291,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6176
6291
|
return { violations };
|
|
6177
6292
|
});
|
|
6178
6293
|
scope.post("/policies/check", async (req, reply) => {
|
|
6179
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6294
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6180
6295
|
if (!proj) return;
|
|
6181
6296
|
const parsed = PoliciesCheckBodySchema.safeParse(req.body ?? {});
|
|
6182
6297
|
if (!parsed.success) {
|
|
@@ -6212,7 +6327,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6212
6327
|
};
|
|
6213
6328
|
});
|
|
6214
6329
|
scope.get("/extend/list-uninstrumented", async (req, reply) => {
|
|
6215
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6330
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6216
6331
|
if (!proj) return;
|
|
6217
6332
|
if (!proj.scanPath) {
|
|
6218
6333
|
return reply.code(409).send({ error: "scan path not configured for this project", project: proj.name });
|
|
@@ -6225,7 +6340,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6225
6340
|
}
|
|
6226
6341
|
});
|
|
6227
6342
|
scope.get("/extend/lookup", async (req, reply) => {
|
|
6228
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6343
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6229
6344
|
if (!proj) return;
|
|
6230
6345
|
const { library, version } = req.query;
|
|
6231
6346
|
if (!library) {
|
|
@@ -6238,7 +6353,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6238
6353
|
return result;
|
|
6239
6354
|
});
|
|
6240
6355
|
scope.get("/extend/describe", async (req, reply) => {
|
|
6241
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6356
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6242
6357
|
if (!proj) return;
|
|
6243
6358
|
if (!proj.scanPath) {
|
|
6244
6359
|
return reply.code(409).send({ error: "scan path not configured for this project", project: proj.name });
|
|
@@ -6251,7 +6366,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6251
6366
|
}
|
|
6252
6367
|
});
|
|
6253
6368
|
scope.post("/extend/apply", async (req, reply) => {
|
|
6254
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6369
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6255
6370
|
if (!proj) return;
|
|
6256
6371
|
if (!proj.scanPath) {
|
|
6257
6372
|
return reply.code(409).send({ error: "scan path not configured for this project", project: proj.name });
|
|
@@ -6271,7 +6386,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6271
6386
|
}
|
|
6272
6387
|
});
|
|
6273
6388
|
scope.post("/extend/dry-run", async (req, reply) => {
|
|
6274
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6389
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6275
6390
|
if (!proj) return;
|
|
6276
6391
|
if (!proj.scanPath) {
|
|
6277
6392
|
return reply.code(409).send({ error: "scan path not configured for this project", project: proj.name });
|
|
@@ -6291,7 +6406,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6291
6406
|
}
|
|
6292
6407
|
});
|
|
6293
6408
|
scope.post("/extend/rollback", async (req, reply) => {
|
|
6294
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6409
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6295
6410
|
if (!proj) return;
|
|
6296
6411
|
if (!proj.scanPath) {
|
|
6297
6412
|
return reply.code(409).send({ error: "scan path not configured for this project", project: proj.name });
|
|
@@ -6354,7 +6469,8 @@ async function buildApi(opts) {
|
|
|
6354
6469
|
errorsPathFor,
|
|
6355
6470
|
staleEventsPathFor,
|
|
6356
6471
|
policyFilePathFor,
|
|
6357
|
-
bootstrap: opts.bootstrap
|
|
6472
|
+
bootstrap: opts.bootstrap,
|
|
6473
|
+
singleProject: opts.singleProject?.name
|
|
6358
6474
|
};
|
|
6359
6475
|
app.get("/health", async () => {
|
|
6360
6476
|
const uptimeMs = Date.now() - startedAt;
|
|
@@ -6377,10 +6493,29 @@ async function buildApi(opts) {
|
|
|
6377
6493
|
return {
|
|
6378
6494
|
ok: true,
|
|
6379
6495
|
uptimeMs,
|
|
6496
|
+
// ADR-096 §7 — a per-project daemon carries its project at the top level
|
|
6497
|
+
// so a spawn can confirm a daemon found on a reused port is serving THIS
|
|
6498
|
+
// project before reusing it. The legacy multi-project daemon omits it and
|
|
6499
|
+
// is matched against the `projects` array instead.
|
|
6500
|
+
...opts.singleProject ? { project: opts.singleProject.name } : {},
|
|
6380
6501
|
projects
|
|
6381
6502
|
};
|
|
6382
6503
|
});
|
|
6383
6504
|
app.get("/projects", async (_req, reply) => {
|
|
6505
|
+
if (opts.singleProject) {
|
|
6506
|
+
const phase = opts.bootstrap?.status(opts.singleProject.name);
|
|
6507
|
+
const entry = {
|
|
6508
|
+
name: opts.singleProject.name,
|
|
6509
|
+
path: opts.singleProject.path,
|
|
6510
|
+
registeredAt: new Date(startedAt).toISOString(),
|
|
6511
|
+
languages: [],
|
|
6512
|
+
// The daemon is serving this project, so it's active unless its
|
|
6513
|
+
// bootstrap broke. ('bootstrapping' still reads as active — the project
|
|
6514
|
+
// is the one to land on; its graph route answers 503 until ready.)
|
|
6515
|
+
status: phase === "broken" ? "broken" : "active"
|
|
6516
|
+
};
|
|
6517
|
+
return [entry];
|
|
6518
|
+
}
|
|
6384
6519
|
try {
|
|
6385
6520
|
return await listProjects();
|
|
6386
6521
|
} catch (err) {
|
|
@@ -6465,6 +6600,10 @@ export {
|
|
|
6465
6600
|
parseExtraProjects,
|
|
6466
6601
|
registryPath,
|
|
6467
6602
|
registryLockPath,
|
|
6603
|
+
removeDaemonRecord,
|
|
6604
|
+
listMachineProjects,
|
|
6605
|
+
findDaemonByProject,
|
|
6606
|
+
signalDaemonStop,
|
|
6468
6607
|
normalizeProjectPath,
|
|
6469
6608
|
writeAtomically,
|
|
6470
6609
|
readRegistry,
|
|
@@ -6478,4 +6617,4 @@ export {
|
|
|
6478
6617
|
pruneRegistry,
|
|
6479
6618
|
buildApi
|
|
6480
6619
|
};
|
|
6481
|
-
//# sourceMappingURL=chunk-
|
|
6620
|
+
//# sourceMappingURL=chunk-Q5EDVWKZ.js.map
|