@neat.is/core 0.2.10 → 0.3.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/neatd.js CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  startDaemon
4
- } from "./chunk-5BQWBQJR.js";
4
+ } from "./chunk-I5KODOJV.js";
5
5
  import {
6
6
  listProjects,
7
7
  registryPath
8
- } from "./chunk-JMLZOKCS.js";
8
+ } from "./chunk-B7UUGIXB.js";
9
9
  import {
10
10
  __require
11
11
  } from "./chunk-DGUM43GV.js";
package/dist/server.cjs CHANGED
@@ -4126,6 +4126,10 @@ async function readRegistry() {
4126
4126
  const parsed = JSON.parse(raw);
4127
4127
  return import_types19.RegistryFileSchema.parse(parsed);
4128
4128
  }
4129
+ async function getProject(name) {
4130
+ const reg = await readRegistry();
4131
+ return reg.projects.find((p) => p.name === name);
4132
+ }
4129
4133
  async function listProjects() {
4130
4134
  const reg = await readRegistry();
4131
4135
  return reg.projects;
@@ -4242,9 +4246,15 @@ function registerRoutes(scope, ctx) {
4242
4246
  scope.get("/health", async (req, reply) => {
4243
4247
  const proj = resolveProject(registry, req, reply);
4244
4248
  if (!proj) return;
4249
+ const uptimeMs = Date.now() - startedAt;
4245
4250
  return {
4246
- uptime: Math.floor((Date.now() - startedAt) / 1e3),
4251
+ ok: true,
4247
4252
  project: proj.name,
4253
+ uptimeMs,
4254
+ // Legacy fields kept additively. The web shell's StatusBar reads
4255
+ // nodeCount / edgeCount; ADR-061's HealthResponseSchema validates
4256
+ // the canonical triple and lets the extras pass through.
4257
+ uptime: Math.floor(uptimeMs / 1e3),
4248
4258
  nodeCount: proj.graph.order,
4249
4259
  edgeCount: proj.graph.size,
4250
4260
  lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
@@ -4264,7 +4274,7 @@ function registerRoutes(scope, ctx) {
4264
4274
  if (!proj.graph.hasNode(id)) {
4265
4275
  return reply.code(404).send({ error: "node not found", id });
4266
4276
  }
4267
- return proj.graph.getNodeAttributes(id);
4277
+ return { node: proj.graph.getNodeAttributes(id) };
4268
4278
  }
4269
4279
  );
4270
4280
  scope.get(
@@ -4281,12 +4291,12 @@ function registerRoutes(scope, ctx) {
4281
4291
  return { inbound, outbound };
4282
4292
  }
4283
4293
  );
4284
- scope.get("/graph/node/:id/dependencies", async (req, reply) => {
4294
+ scope.get("/graph/dependencies/:nodeId", async (req, reply) => {
4285
4295
  const proj = resolveProject(registry, req, reply);
4286
4296
  if (!proj) return;
4287
- const { id } = req.params;
4288
- if (!proj.graph.hasNode(id)) {
4289
- return reply.code(404).send({ error: "node not found", id });
4297
+ const { nodeId } = req.params;
4298
+ if (!proj.graph.hasNode(nodeId)) {
4299
+ return reply.code(404).send({ error: "node not found", id: nodeId });
4290
4300
  }
4291
4301
  const depth = req.query.depth ? Number(req.query.depth) : TRANSITIVE_DEPENDENCIES_DEFAULT_DEPTH;
4292
4302
  if (!Number.isFinite(depth) || depth < 1 || depth > TRANSITIVE_DEPENDENCIES_MAX_DEPTH) {
@@ -4294,7 +4304,7 @@ function registerRoutes(scope, ctx) {
4294
4304
  error: `depth must be an integer in [1, ${TRANSITIVE_DEPENDENCIES_MAX_DEPTH}]`
4295
4305
  });
4296
4306
  }
4297
- return getTransitiveDependencies(proj.graph, id, depth);
4307
+ return getTransitiveDependencies(proj.graph, nodeId, depth);
4298
4308
  });
4299
4309
  scope.get("/graph/divergences", async (req, reply) => {
4300
4310
  const proj = resolveProject(registry, req, reply);
@@ -4335,19 +4345,26 @@ function registerRoutes(scope, ctx) {
4335
4345
  const proj = resolveProject(registry, req, reply);
4336
4346
  if (!proj) return;
4337
4347
  const epath = errorsPathFor(proj);
4338
- if (!epath) return [];
4339
- return readErrorEvents(epath);
4348
+ if (!epath) return { count: 0, total: 0, events: [] };
4349
+ const events = await readErrorEvents(epath);
4350
+ const total = events.length;
4351
+ const limit = req.query.limit ? Number(req.query.limit) : 50;
4352
+ const safeLimit = Number.isFinite(limit) && limit > 0 ? Math.min(limit, 200) : 50;
4353
+ const sliced = events.slice(0, safeLimit);
4354
+ return { count: sliced.length, total, events: sliced };
4340
4355
  });
4341
- scope.get("/incidents/stale", async (req, reply) => {
4356
+ scope.get("/stale-events", async (req, reply) => {
4342
4357
  const proj = resolveProject(registry, req, reply);
4343
4358
  if (!proj) return;
4344
4359
  const spath = staleEventsPathFor(proj);
4345
- if (!spath) return [];
4360
+ if (!spath) return { count: 0, total: 0, events: [] };
4346
4361
  const events = await readStaleEvents(spath);
4347
4362
  const filtered = req.query.edgeType ? events.filter((e) => e.edgeType === req.query.edgeType) : events;
4348
4363
  const ordered = [...filtered].reverse();
4364
+ const total = ordered.length;
4349
4365
  const limit = req.query.limit ? Number(req.query.limit) : 50;
4350
- return ordered.slice(0, Number.isFinite(limit) && limit > 0 ? limit : 50);
4366
+ const sliced = ordered.slice(0, Number.isFinite(limit) && limit > 0 ? limit : 50);
4367
+ return { count: sliced.length, total, events: sliced };
4351
4368
  });
4352
4369
  scope.get(
4353
4370
  "/incidents/:nodeId",
@@ -4359,14 +4376,15 @@ function registerRoutes(scope, ctx) {
4359
4376
  return reply.code(404).send({ error: "node not found", id: nodeId });
4360
4377
  }
4361
4378
  const epath = errorsPathFor(proj);
4362
- if (!epath) return [];
4379
+ if (!epath) return { count: 0, total: 0, events: [] };
4363
4380
  const events = await readErrorEvents(epath);
4364
- return events.filter(
4381
+ const filtered = events.filter(
4365
4382
  (e) => e.affectedNode === nodeId || e.service === nodeId.replace(/^service:/, "")
4366
4383
  );
4384
+ return { count: filtered.length, total: filtered.length, events: filtered };
4367
4385
  }
4368
4386
  );
4369
- scope.get("/traverse/root-cause/:nodeId", async (req, reply) => {
4387
+ scope.get("/graph/root-cause/:nodeId", async (req, reply) => {
4370
4388
  const proj = resolveProject(registry, req, reply);
4371
4389
  if (!proj) return;
4372
4390
  const { nodeId } = req.params;
@@ -4386,7 +4404,7 @@ function registerRoutes(scope, ctx) {
4386
4404
  if (!result) return reply.code(404).send({ error: "no root cause found", id: nodeId });
4387
4405
  return result;
4388
4406
  });
4389
- scope.get("/traverse/blast-radius/:nodeId", async (req, reply) => {
4407
+ scope.get("/graph/blast-radius/:nodeId", async (req, reply) => {
4390
4408
  const proj = resolveProject(registry, req, reply);
4391
4409
  if (!proj) return;
4392
4410
  const { nodeId } = req.params;
@@ -4496,7 +4514,7 @@ function registerRoutes(scope, ctx) {
4496
4514
  if (req.query.policyId) {
4497
4515
  violations = violations.filter((v) => v.policyId === req.query.policyId);
4498
4516
  }
4499
- return violations;
4517
+ return { violations };
4500
4518
  });
4501
4519
  scope.post("/policies/check", async (req, reply) => {
4502
4520
  const proj = resolveProject(registry, req, reply);
@@ -4575,6 +4593,20 @@ async function buildApi(opts) {
4575
4593
  });
4576
4594
  }
4577
4595
  });
4596
+ app.get("/projects/:project", async (req, reply) => {
4597
+ try {
4598
+ const entry = await getProject(req.params.project);
4599
+ if (!entry) {
4600
+ return reply.code(404).send({ error: "project not found", project: req.params.project });
4601
+ }
4602
+ return { project: entry };
4603
+ } catch (err) {
4604
+ return reply.code(500).send({
4605
+ error: "failed to read project registry",
4606
+ details: err.message
4607
+ });
4608
+ }
4609
+ });
4578
4610
  registerRoutes(app, routeCtx);
4579
4611
  await app.register(
4580
4612
  async (scope) => {