@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
package/dist/server.cjs
CHANGED
|
@@ -472,8 +472,10 @@ async function buildOtelReceiver(opts) {
|
|
|
472
472
|
for (const s of spans) projectQueue.push({ project, span: s });
|
|
473
473
|
projectDrainPromise = projectDrainPromise.then(() => drainProject());
|
|
474
474
|
};
|
|
475
|
+
const offersProjectRouting = opts.onProjectSpan !== void 0;
|
|
475
476
|
const legacyEndpointWarned = /* @__PURE__ */ new Set();
|
|
476
477
|
function warnLegacyEndpoint(serviceName) {
|
|
478
|
+
if (!offersProjectRouting) return;
|
|
477
479
|
if (legacyEndpointWarned.has(serviceName)) return;
|
|
478
480
|
legacyEndpointWarned.add(serviceName);
|
|
479
481
|
console.warn(
|
|
@@ -5849,7 +5851,9 @@ async function loadGraphFromDisk(graph, outPath) {
|
|
|
5849
5851
|
graph.clear();
|
|
5850
5852
|
graph.import(payload.graph);
|
|
5851
5853
|
}
|
|
5852
|
-
function startPersistLoop(graph, outPath,
|
|
5854
|
+
function startPersistLoop(graph, outPath, opts = {}) {
|
|
5855
|
+
const intervalMs = opts.intervalMs ?? 6e4;
|
|
5856
|
+
const exitOnSignal = opts.exitOnSignal ?? true;
|
|
5853
5857
|
let stopped = false;
|
|
5854
5858
|
const tick = async () => {
|
|
5855
5859
|
if (stopped) return;
|
|
@@ -5862,7 +5866,7 @@ function startPersistLoop(graph, outPath, intervalMs = 6e4) {
|
|
|
5862
5866
|
const interval = setInterval(() => {
|
|
5863
5867
|
void tick();
|
|
5864
5868
|
}, intervalMs);
|
|
5865
|
-
const onSignal = (signal) => {
|
|
5869
|
+
const onSignal = exitOnSignal ? (signal) => {
|
|
5866
5870
|
void (async () => {
|
|
5867
5871
|
try {
|
|
5868
5872
|
await saveGraphToDisk(graph, outPath);
|
|
@@ -5872,14 +5876,18 @@ function startPersistLoop(graph, outPath, intervalMs = 6e4) {
|
|
|
5872
5876
|
process.exit(0);
|
|
5873
5877
|
}
|
|
5874
5878
|
})();
|
|
5875
|
-
};
|
|
5876
|
-
|
|
5877
|
-
|
|
5879
|
+
} : null;
|
|
5880
|
+
if (onSignal) {
|
|
5881
|
+
process.on("SIGTERM", onSignal);
|
|
5882
|
+
process.on("SIGINT", onSignal);
|
|
5883
|
+
}
|
|
5878
5884
|
return () => {
|
|
5879
5885
|
stopped = true;
|
|
5880
5886
|
clearInterval(interval);
|
|
5881
|
-
|
|
5882
|
-
|
|
5887
|
+
if (onSignal) {
|
|
5888
|
+
process.off("SIGTERM", onSignal);
|
|
5889
|
+
process.off("SIGINT", onSignal);
|
|
5890
|
+
}
|
|
5883
5891
|
};
|
|
5884
5892
|
}
|
|
5885
5893
|
|
|
@@ -6047,12 +6055,16 @@ function serializeGraph(graph) {
|
|
|
6047
6055
|
});
|
|
6048
6056
|
return { nodes, edges };
|
|
6049
6057
|
}
|
|
6050
|
-
function projectFromReq(req) {
|
|
6058
|
+
function projectFromReq(req, singleProject) {
|
|
6051
6059
|
const params = req.params;
|
|
6052
|
-
|
|
6060
|
+
const named = params.project;
|
|
6061
|
+
if (singleProject) {
|
|
6062
|
+
return named === void 0 || named === DEFAULT_PROJECT ? singleProject : named;
|
|
6063
|
+
}
|
|
6064
|
+
return named ?? DEFAULT_PROJECT;
|
|
6053
6065
|
}
|
|
6054
|
-
function resolveProject(registry, req, reply, bootstrap) {
|
|
6055
|
-
const name = projectFromReq(req);
|
|
6066
|
+
function resolveProject(registry, req, reply, bootstrap, singleProject) {
|
|
6067
|
+
const name = projectFromReq(req, singleProject);
|
|
6056
6068
|
const ctx = registry.get(name);
|
|
6057
6069
|
if (!ctx) {
|
|
6058
6070
|
const phase = bootstrap?.status(name);
|
|
@@ -6093,13 +6105,13 @@ function buildLegacyRegistry(opts) {
|
|
|
6093
6105
|
function registerRoutes(scope, ctx) {
|
|
6094
6106
|
const { registry, startedAt, errorsPathFor, staleEventsPathFor } = ctx;
|
|
6095
6107
|
scope.get("/events", (req, reply) => {
|
|
6096
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6108
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6097
6109
|
if (!proj) return;
|
|
6098
6110
|
handleSse(req, reply, { project: proj.name });
|
|
6099
6111
|
});
|
|
6100
6112
|
if (ctx.scope === "project") {
|
|
6101
6113
|
scope.get("/health", async (req, reply) => {
|
|
6102
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6114
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6103
6115
|
if (!proj) return;
|
|
6104
6116
|
const uptimeMs = Date.now() - startedAt;
|
|
6105
6117
|
return {
|
|
@@ -6117,14 +6129,14 @@ function registerRoutes(scope, ctx) {
|
|
|
6117
6129
|
});
|
|
6118
6130
|
}
|
|
6119
6131
|
scope.get("/graph", async (req, reply) => {
|
|
6120
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6132
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6121
6133
|
if (!proj) return;
|
|
6122
6134
|
return serializeGraph(proj.graph);
|
|
6123
6135
|
});
|
|
6124
6136
|
scope.get(
|
|
6125
6137
|
"/graph/node/:id",
|
|
6126
6138
|
async (req, reply) => {
|
|
6127
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6139
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6128
6140
|
if (!proj) return;
|
|
6129
6141
|
const { id } = req.params;
|
|
6130
6142
|
if (!proj.graph.hasNode(id)) {
|
|
@@ -6136,7 +6148,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6136
6148
|
scope.get(
|
|
6137
6149
|
"/graph/edges/:id",
|
|
6138
6150
|
async (req, reply) => {
|
|
6139
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6151
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6140
6152
|
if (!proj) return;
|
|
6141
6153
|
const { id } = req.params;
|
|
6142
6154
|
if (!proj.graph.hasNode(id)) {
|
|
@@ -6148,7 +6160,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6148
6160
|
}
|
|
6149
6161
|
);
|
|
6150
6162
|
scope.get("/graph/dependencies/:nodeId", async (req, reply) => {
|
|
6151
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6163
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6152
6164
|
if (!proj) return;
|
|
6153
6165
|
const { nodeId } = req.params;
|
|
6154
6166
|
if (!proj.graph.hasNode(nodeId)) {
|
|
@@ -6163,7 +6175,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6163
6175
|
return getTransitiveDependencies(proj.graph, nodeId, depth);
|
|
6164
6176
|
});
|
|
6165
6177
|
scope.get("/graph/divergences", async (req, reply) => {
|
|
6166
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6178
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6167
6179
|
if (!proj) return;
|
|
6168
6180
|
let typeFilter;
|
|
6169
6181
|
if (req.query.type) {
|
|
@@ -6198,7 +6210,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6198
6210
|
});
|
|
6199
6211
|
});
|
|
6200
6212
|
scope.get("/incidents", async (req, reply) => {
|
|
6201
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6213
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6202
6214
|
if (!proj) return;
|
|
6203
6215
|
const epath = errorsPathFor(proj);
|
|
6204
6216
|
if (!epath) return { count: 0, total: 0, events: [] };
|
|
@@ -6210,7 +6222,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6210
6222
|
return { count: sliced.length, total, events: sliced };
|
|
6211
6223
|
});
|
|
6212
6224
|
scope.get("/stale-events", async (req, reply) => {
|
|
6213
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6225
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6214
6226
|
if (!proj) return;
|
|
6215
6227
|
const spath = staleEventsPathFor(proj);
|
|
6216
6228
|
if (!spath) return { count: 0, total: 0, events: [] };
|
|
@@ -6225,7 +6237,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6225
6237
|
scope.get(
|
|
6226
6238
|
"/incidents/:nodeId",
|
|
6227
6239
|
async (req, reply) => {
|
|
6228
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6240
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6229
6241
|
if (!proj) return;
|
|
6230
6242
|
const { nodeId } = req.params;
|
|
6231
6243
|
if (!proj.graph.hasNode(nodeId)) {
|
|
@@ -6241,7 +6253,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6241
6253
|
}
|
|
6242
6254
|
);
|
|
6243
6255
|
scope.get("/graph/root-cause/:nodeId", async (req, reply) => {
|
|
6244
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6256
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6245
6257
|
if (!proj) return;
|
|
6246
6258
|
const { nodeId } = req.params;
|
|
6247
6259
|
if (!proj.graph.hasNode(nodeId)) {
|
|
@@ -6261,7 +6273,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6261
6273
|
return result;
|
|
6262
6274
|
});
|
|
6263
6275
|
scope.get("/graph/blast-radius/:nodeId", async (req, reply) => {
|
|
6264
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6276
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6265
6277
|
if (!proj) return;
|
|
6266
6278
|
const { nodeId } = req.params;
|
|
6267
6279
|
if (!proj.graph.hasNode(nodeId)) {
|
|
@@ -6274,7 +6286,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6274
6286
|
return getBlastRadius(proj.graph, nodeId, depth);
|
|
6275
6287
|
});
|
|
6276
6288
|
scope.get("/search", async (req, reply) => {
|
|
6277
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6289
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6278
6290
|
if (!proj) return;
|
|
6279
6291
|
const raw = (req.query.q ?? "").trim();
|
|
6280
6292
|
if (!raw) return reply.code(400).send({ error: "query parameter `q` is required" });
|
|
@@ -6305,7 +6317,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6305
6317
|
scope.get(
|
|
6306
6318
|
"/graph/diff",
|
|
6307
6319
|
async (req, reply) => {
|
|
6308
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6320
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6309
6321
|
if (!proj) return;
|
|
6310
6322
|
const against = req.query.against;
|
|
6311
6323
|
if (!against) {
|
|
@@ -6320,7 +6332,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6320
6332
|
}
|
|
6321
6333
|
);
|
|
6322
6334
|
scope.post("/snapshot", async (req, reply) => {
|
|
6323
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6335
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6324
6336
|
if (!proj) return;
|
|
6325
6337
|
const body = req.body;
|
|
6326
6338
|
if (!body || typeof body !== "object" || !body.snapshot) {
|
|
@@ -6349,7 +6361,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6349
6361
|
}
|
|
6350
6362
|
});
|
|
6351
6363
|
scope.post("/graph/scan", async (req, reply) => {
|
|
6352
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6364
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6353
6365
|
if (!proj) return;
|
|
6354
6366
|
if (!proj.scanPath) {
|
|
6355
6367
|
return reply.code(409).send({ error: "scan path not configured for this project", project: proj.name });
|
|
@@ -6365,7 +6377,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6365
6377
|
};
|
|
6366
6378
|
});
|
|
6367
6379
|
scope.get("/policies", async (req, reply) => {
|
|
6368
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6380
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6369
6381
|
if (!proj) return;
|
|
6370
6382
|
const policyPath = ctx.policyFilePathFor(proj);
|
|
6371
6383
|
if (!policyPath) {
|
|
@@ -6382,7 +6394,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6382
6394
|
}
|
|
6383
6395
|
});
|
|
6384
6396
|
scope.get("/policies/violations", async (req, reply) => {
|
|
6385
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6397
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6386
6398
|
if (!proj) return;
|
|
6387
6399
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
6388
6400
|
let violations = await log.readAll();
|
|
@@ -6402,7 +6414,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6402
6414
|
return { violations };
|
|
6403
6415
|
});
|
|
6404
6416
|
scope.post("/policies/check", async (req, reply) => {
|
|
6405
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6417
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6406
6418
|
if (!proj) return;
|
|
6407
6419
|
const parsed = import_types25.PoliciesCheckBodySchema.safeParse(req.body ?? {});
|
|
6408
6420
|
if (!parsed.success) {
|
|
@@ -6438,7 +6450,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6438
6450
|
};
|
|
6439
6451
|
});
|
|
6440
6452
|
scope.get("/extend/list-uninstrumented", async (req, reply) => {
|
|
6441
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6453
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6442
6454
|
if (!proj) return;
|
|
6443
6455
|
if (!proj.scanPath) {
|
|
6444
6456
|
return reply.code(409).send({ error: "scan path not configured for this project", project: proj.name });
|
|
@@ -6451,7 +6463,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6451
6463
|
}
|
|
6452
6464
|
});
|
|
6453
6465
|
scope.get("/extend/lookup", async (req, reply) => {
|
|
6454
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6466
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6455
6467
|
if (!proj) return;
|
|
6456
6468
|
const { library, version } = req.query;
|
|
6457
6469
|
if (!library) {
|
|
@@ -6464,7 +6476,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6464
6476
|
return result;
|
|
6465
6477
|
});
|
|
6466
6478
|
scope.get("/extend/describe", async (req, reply) => {
|
|
6467
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6479
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6468
6480
|
if (!proj) return;
|
|
6469
6481
|
if (!proj.scanPath) {
|
|
6470
6482
|
return reply.code(409).send({ error: "scan path not configured for this project", project: proj.name });
|
|
@@ -6477,7 +6489,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6477
6489
|
}
|
|
6478
6490
|
});
|
|
6479
6491
|
scope.post("/extend/apply", async (req, reply) => {
|
|
6480
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6492
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6481
6493
|
if (!proj) return;
|
|
6482
6494
|
if (!proj.scanPath) {
|
|
6483
6495
|
return reply.code(409).send({ error: "scan path not configured for this project", project: proj.name });
|
|
@@ -6497,7 +6509,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6497
6509
|
}
|
|
6498
6510
|
});
|
|
6499
6511
|
scope.post("/extend/dry-run", async (req, reply) => {
|
|
6500
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6512
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6501
6513
|
if (!proj) return;
|
|
6502
6514
|
if (!proj.scanPath) {
|
|
6503
6515
|
return reply.code(409).send({ error: "scan path not configured for this project", project: proj.name });
|
|
@@ -6517,7 +6529,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6517
6529
|
}
|
|
6518
6530
|
});
|
|
6519
6531
|
scope.post("/extend/rollback", async (req, reply) => {
|
|
6520
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6532
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6521
6533
|
if (!proj) return;
|
|
6522
6534
|
if (!proj.scanPath) {
|
|
6523
6535
|
return reply.code(409).send({ error: "scan path not configured for this project", project: proj.name });
|
|
@@ -6580,7 +6592,8 @@ async function buildApi(opts) {
|
|
|
6580
6592
|
errorsPathFor,
|
|
6581
6593
|
staleEventsPathFor,
|
|
6582
6594
|
policyFilePathFor,
|
|
6583
|
-
bootstrap: opts.bootstrap
|
|
6595
|
+
bootstrap: opts.bootstrap,
|
|
6596
|
+
singleProject: opts.singleProject?.name
|
|
6584
6597
|
};
|
|
6585
6598
|
app.get("/health", async () => {
|
|
6586
6599
|
const uptimeMs = Date.now() - startedAt;
|
|
@@ -6603,10 +6616,29 @@ async function buildApi(opts) {
|
|
|
6603
6616
|
return {
|
|
6604
6617
|
ok: true,
|
|
6605
6618
|
uptimeMs,
|
|
6619
|
+
// ADR-096 §7 — a per-project daemon carries its project at the top level
|
|
6620
|
+
// so a spawn can confirm a daemon found on a reused port is serving THIS
|
|
6621
|
+
// project before reusing it. The legacy multi-project daemon omits it and
|
|
6622
|
+
// is matched against the `projects` array instead.
|
|
6623
|
+
...opts.singleProject ? { project: opts.singleProject.name } : {},
|
|
6606
6624
|
projects
|
|
6607
6625
|
};
|
|
6608
6626
|
});
|
|
6609
6627
|
app.get("/projects", async (_req, reply) => {
|
|
6628
|
+
if (opts.singleProject) {
|
|
6629
|
+
const phase = opts.bootstrap?.status(opts.singleProject.name);
|
|
6630
|
+
const entry = {
|
|
6631
|
+
name: opts.singleProject.name,
|
|
6632
|
+
path: opts.singleProject.path,
|
|
6633
|
+
registeredAt: new Date(startedAt).toISOString(),
|
|
6634
|
+
languages: [],
|
|
6635
|
+
// The daemon is serving this project, so it's active unless its
|
|
6636
|
+
// bootstrap broke. ('bootstrapping' still reads as active — the project
|
|
6637
|
+
// is the one to land on; its graph route answers 503 until ready.)
|
|
6638
|
+
status: phase === "broken" ? "broken" : "active"
|
|
6639
|
+
};
|
|
6640
|
+
return [entry];
|
|
6641
|
+
}
|
|
6610
6642
|
try {
|
|
6611
6643
|
return await listProjects();
|
|
6612
6644
|
} catch (err) {
|