@camstack/server 1.2.688 → 1.2.689

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.
@@ -418,27 +418,17 @@ class AgentRegistryService {
418
418
  * nothing re-announces it, and the operator has no reason to wait for a
419
419
  * timer they cannot see.
420
420
  *
421
- * An ONLINE node is REFUSED: dropping a live node from the routing catalog
422
- * would break every cap call pinned to it, and it re-registers on the next
423
- * heartbeat anyway — "forget" of a live node is not a slow success, it is
424
- * not a thing.
421
+ * An ONLINE node is forgotten too (operator ruling 2026-10-29, D966). The old refusal said a
422
+ * live node re-registers at once; since D857 its leaf is on the CRL, so it cannot. The order
423
+ * matters: (1) revoke (the revoker also closes the node's mutual-TLS sockets, because the CRL is
424
+ * only read at a handshake), (2) `disconnected` in the catalog, which unregisters its services and
425
+ * rejects the cap calls still waiting on it with `RequestRejectedError` - a bare `nodes.delete`
426
+ * would leave both behind, (3) the delete and the rest of the cleanup. The agent finds out on
427
+ * its own: refused at the next handshake, it asks the hub (`agent-membership-watch`) and resets
428
+ * to unpaired after two CA-signed `revoked` answers. `stillListed` is read back at the end, so a
429
+ * node that came straight back (revocation failed) is reported as listed, never as forgotten.
425
430
  */
426
431
  async forgetClusterNode(nodeId) {
427
- if (this.isNodeOnline(nodeId)) {
428
- const message = `Refused: ${nodeId} is ONLINE. Take the node down first — a live node re-registers immediately.`;
429
- this.logger?.warn('forget cluster node refused', {
430
- tags: { nodeId },
431
- meta: { reason: 'node-online' },
432
- });
433
- return {
434
- forgotten: false,
435
- historyRemoved: false,
436
- registryRemoved: false,
437
- stillListed: true,
438
- certificates: { outcome: 'not-attempted' },
439
- message,
440
- };
441
- }
442
432
  // First: a forgotten node whose leaf stays valid walks back in on its next boot (D857).
443
433
  const certificates = await this.revokeForgottenNodeCertificates(nodeId);
444
434
  const historyOutcome = this.historyStore
@@ -446,6 +436,8 @@ class AgentRegistryService {
446
436
  : 'store-unavailable';
447
437
  // The mirror still lists the forgotten node: the next directory re-reads.
448
438
  this.historyMirror?.invalidate();
439
+ if (this.isNodeOnline(nodeId))
440
+ this.disconnectFromNodeCatalog(nodeId);
449
441
  const registryRemoved = this.removeFromNodeCatalog(nodeId);
450
442
  // Its intended set is gone with the row, so the back-fill breaker's memory
451
443
  // of failed deliveries to it is meaningless — drop it too.
@@ -499,6 +491,20 @@ class AgentRegistryService {
499
491
  message,
500
492
  };
501
493
  }
494
+ /**
495
+ * Take a LIVE node out of routing the way Moleculer does when its transport drops: services
496
+ * unregistered, pending requests rejected (typed, not left to time out). Idempotent.
497
+ */
498
+ disconnectFromNodeCatalog(nodeId) {
499
+ const nodes = this.nodeCatalogRegistry()?.nodes;
500
+ if (nodes?.disconnected === undefined) {
501
+ this.logger?.warn('cluster node catalog does not expose disconnected - calls may hang', {
502
+ tags: { nodeId },
503
+ });
504
+ return;
505
+ }
506
+ nodes.disconnected(nodeId, false);
507
+ }
502
508
  /** Is the node currently AVAILABLE in Moleculer's catalog? */
503
509
  isNodeOnline(nodeId) {
504
510
  return this.catalogNodeIds({ onlyAvailable: true }).includes(nodeId);
@@ -1173,7 +1179,8 @@ class AgentRegistryService {
1173
1179
  status: {
1174
1180
  activeCameras: 0,
1175
1181
  cpuPercent: status.cpuPercent ?? 0,
1176
- memoryPercent: status.memoryPercent ?? 0,
1182
+ memoryPercent: typeof status.memoryPercent === 'number' ? status.memoryPercent : null,
1183
+ processRssBytes: typeof status.processRssBytes === 'number' ? status.processRssBytes : null,
1177
1184
  fps: {},
1178
1185
  errors: [],
1179
1186
  },
@@ -1237,7 +1244,7 @@ class AgentRegistryService {
1237
1244
  status: {
1238
1245
  activeCameras: 0,
1239
1246
  cpuPercent: 0,
1240
- memoryPercent: 0,
1247
+ memoryPercent: null,
1241
1248
  fps: {},
1242
1249
  errors: [],
1243
1250
  },
@@ -1254,7 +1261,7 @@ class AgentRegistryService {
1254
1261
  // The cap contract only exposes async snapshots; the cached read is cheap
1255
1262
  // because the addon's background sampler keeps the snapshot warm.
1256
1263
  let cpuPercent = 0;
1257
- let memoryPercent = 0;
1264
+ let memoryPercent = null;
1258
1265
  const registry = this.capabilityService.getRegistry();
1259
1266
  if (registry) {
1260
1267
  const metricsProvider = registry.getSingleton('metrics-provider');
@@ -1283,6 +1290,7 @@ class AgentRegistryService {
1283
1290
  activeCameras: 0,
1284
1291
  cpuPercent,
1285
1292
  memoryPercent,
1293
+ processRssBytes: process.memoryUsage().rss,
1286
1294
  fps: {},
1287
1295
  errors: [],
1288
1296
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/server",
3
- "version": "1.2.688",
3
+ "version": "1.2.689",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -49,10 +49,10 @@
49
49
  ]
50
50
  },
51
51
  "dependencies": {
52
- "@camstack/addon-admin-ui": "1.2.516",
53
- "@camstack/addon-agent-ui": "1.2.291",
52
+ "@camstack/addon-admin-ui": "1.2.517",
53
+ "@camstack/addon-agent-ui": "1.2.292",
54
54
  "@camstack/addon-auth": "1.2.170",
55
- "@camstack/addon-benchmark": "1.2.290",
55
+ "@camstack/addon-benchmark": "1.2.291",
56
56
  "@camstack/addon-decoder-nodeav": "1.2.144",
57
57
  "@camstack/addon-notifiers": "1.2.623",
58
58
  "@camstack/addon-pipeline": "1.2.634",
@@ -60,9 +60,9 @@
60
60
  "@camstack/addon-post-analysis": "1.2.651",
61
61
  "@camstack/develop": "0.1.39",
62
62
  "@camstack/sdk": "1.2.154",
63
- "@camstack/system": "1.2.517",
64
- "@camstack/types": "1.2.443",
65
- "@camstack/ui-library": "1.2.349",
63
+ "@camstack/system": "1.2.518",
64
+ "@camstack/types": "1.2.444",
65
+ "@camstack/ui-library": "1.2.350",
66
66
  "@fastify/compress": "^9.0.0",
67
67
  "@fastify/cookie": "^11.0.2",
68
68
  "@fastify/cors": "^11.2.0",