@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.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.2.689] - 2026-10-29
11
+
12
+ ### Added
13
+
14
+ - A node's free memory comes from the metrics provider instead of os.freemem(), which undercounts on macOS
15
+ - device-export gains optional methods to list, expose and unexpose actions and notification targets beside devices.
16
+ - Forget node now works on an online node too: its certificates are revoked, its connection is cut and calls pinned to it fail at once.
17
+ - Generated routers for the pet-fountain capability
18
+ - Agents report their own process memory in the topology (the core process showed 0), and an unreadable memory shows as unknown
19
+
10
20
  ## [1.2.688] - 2026-10-29
11
21
 
12
22
  ### Changed
@@ -240,7 +240,8 @@ function createAgentService(deps) {
240
240
  const { broker } = ctx;
241
241
  const cpus = os.cpus();
242
242
  let cpuPercent = 0;
243
- let memoryPercent = 0;
243
+ let memoryPercent = null;
244
+ let freeMemoryBytes = null;
244
245
  const metrics = deps.getMetricsProvider?.();
245
246
  if (metrics) {
246
247
  // Bounded so $agent.status never hangs (and drops the node from
@@ -249,6 +250,7 @@ function createAgentService(deps) {
249
250
  if (snapshot) {
250
251
  cpuPercent = snapshot.cpu.total;
251
252
  memoryPercent = snapshot.memory.percent;
253
+ freeMemoryBytes = snapshot.memory.availableBytes;
252
254
  }
253
255
  }
254
256
  const mem = process.memoryUsage();
@@ -262,9 +264,12 @@ function createAgentService(deps) {
262
264
  cpuCores: cpus.length,
263
265
  cpuModel: cpus[0]?.model,
264
266
  totalMemoryMB: Math.round(os.totalmem() / 1024 / 1024),
265
- freeMemoryMB: Math.round(os.freemem() / 1024 / 1024),
267
+ freeMemoryMB: freeMemoryBytes === null ? null : Math.round(freeMemoryBytes / 1024 / 1024),
266
268
  cpuPercent,
267
269
  memoryPercent,
270
+ // The agent's own (core) process, so a topology row can attribute
271
+ // the largest process on the node instead of showing 0.
272
+ processRssBytes: process.memoryUsage().rss,
268
273
  uptime: os.uptime(),
269
274
  // The agent *process* itself (distinct from the host `uptime` /
270
275
  // memory above) — surfaced so the UI can show how long THIS agent
@@ -298,7 +303,7 @@ function createAgentService(deps) {
298
303
  handler: async (ctx) => {
299
304
  const { broker } = ctx;
300
305
  let cpuPercent = 0;
301
- let memoryPercent = 0;
306
+ let memoryPercent = null;
302
307
  const metrics = deps.getMetricsProvider?.();
303
308
  if (metrics) {
304
309
  try {
@@ -349,7 +349,9 @@ async function computeTopologyInternal(agentRegistry, addonRegistry, getNodeRoot
349
349
  name: `${node.info.id} (core)`,
350
350
  state: 'running',
351
351
  cpuPercent: node.status?.cpuPercent ?? 0,
352
- memoryRss: 0,
352
+ // The agent's own process RSS from $agent.status; 0 only for an agent
353
+ // that predates the field.
354
+ memoryRss: node.status?.processRssBytes ?? 0,
353
355
  uptimeSeconds: Math.floor((Date.now() - node.connectedSince) / 1000),
354
356
  services: mainProcessServices,
355
357
  };
@@ -414,7 +416,7 @@ async function computeTopologyInternal(agentRegistry, addonRegistry, getNodeRoot
414
416
  isHub: node.isHub,
415
417
  isOnline: node.isOnline !== false,
416
418
  cpuPercent: node.status?.cpuPercent ?? 0,
417
- memoryPercent: node.status?.memoryPercent ?? 0,
419
+ memoryPercent: node.status?.memoryPercent ?? null,
418
420
  uptime: Date.now() - node.connectedSince,
419
421
  // Fix (was hardcoded `new Date().toISOString()` — always "now", a bug
420
422
  // even for online nodes): report the persisted `lastActive` when we have
@@ -456,7 +458,7 @@ async function computeTopologyInternal(agentRegistry, addonRegistry, getNodeRoot
456
458
  isHub: persisted.isHub,
457
459
  isOnline: false,
458
460
  cpuPercent: 0,
459
- memoryPercent: 0,
461
+ memoryPercent: null,
460
462
  uptime: 0,
461
463
  lastSeen: new Date(persisted.lastActive).toISOString(),
462
464
  localIps: [...persisted.localIps],
@@ -69,9 +69,9 @@ function createClusterNodesRouter(agentRegistry) {
69
69
  await agentRegistry.repairNodeAddons(input.nodeId);
70
70
  return { success: true };
71
71
  }),
72
- // Purge one node's persisted offline history. Pairs with
73
- // `pipelineOrchestrator.removeAgentSettings` on the frontend to fully
74
- // forget an offline node (history row + per-node pipeline assignments).
72
+ // Forget a node, online or offline: revoke its certificates, cut it from routing, purge its
73
+ // persisted history. Pairs with `pipelineOrchestrator.removeAgentSettings` on the frontend
74
+ // (per-node pipeline assignments).
75
75
  forgetNode: trpc_middleware_js_1.adminProcedure
76
76
  .input(ForgetNodeInputSchema)
77
77
  .output(ForgetNodeResultSchema)
@@ -2,7 +2,7 @@
2
2
  // AUTO-GENERATED by scripts/generate-cap-mounts.ts — DO NOT EDIT
3
3
  // Re-run: npx tsx scripts/generate-cap-mounts.ts
4
4
  //
5
- // Mounted: 188 Skipped (legacy): 3
5
+ // Mounted: 189 Skipped (legacy): 3
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.LEGACY_SHAPE_SKIP = void 0;
8
8
  exports.mountAllCaps = mountAllCaps;
@@ -231,6 +231,8 @@ function mountAllCaps(services) {
231
231
  ...first,
232
232
  listSupportedDeviceKinds: (0, cap_mount_helpers_js_1.concatCollection)(providers, 'listSupportedDeviceKinds'),
233
233
  listExposedDevices: (0, cap_mount_helpers_js_1.concatCollection)(providers, 'listExposedDevices'),
234
+ listSupportedEntityKinds: (0, cap_mount_helpers_js_1.concatCollection)(providers, 'listSupportedEntityKinds'),
235
+ listExposedEntities: (0, cap_mount_helpers_js_1.concatCollection)(providers, 'listExposedEntities'),
234
236
  };
235
237
  }, remoteCapProxy),
236
238
  deviceExtension: (0, generated_cap_routers_js_1.createCapRouter_deviceExtension)((_ctx) => (0, device_collection_dispatch_js_1.requireDeviceCollection)(reg, 'device-extension'), remoteCapProxy),
@@ -501,6 +503,7 @@ function mountAllCaps(services) {
501
503
  osd: (0, generated_cap_routers_js_1.createCapRouter_osd)((_ctx) => (0, cap_mount_helpers_js_1.requireDeviceScoped)(reg, 'osd'), remoteCapProxy),
502
504
  osdManager: (0, generated_cap_routers_js_1.createCapRouter_osdManager)((_ctx) => reg?.getSingleton('osd-manager') ?? null, remoteCapProxy),
503
505
  petFeeder: (0, generated_cap_routers_js_1.createCapRouter_petFeeder)((_ctx) => (0, cap_mount_helpers_js_1.requireDeviceScoped)(reg, 'pet-feeder'), remoteCapProxy),
506
+ petFountain: (0, generated_cap_routers_js_1.createCapRouter_petFountain)((_ctx) => (0, cap_mount_helpers_js_1.requireDeviceScoped)(reg, 'pet-fountain'), remoteCapProxy),
504
507
  pipelineAnalytics: (0, generated_cap_routers_js_1.createCapRouter_pipelineAnalytics)((_ctx) => (0, cap_mount_helpers_js_1.requireDeviceScoped)(reg, 'pipeline-analytics'), remoteCapProxy),
505
508
  pipelineExecutor: (0, generated_cap_routers_js_1.createCapRouter_pipelineExecutor)((_ctx) => reg?.getSingleton('pipeline-executor') ??
506
509
  null, remoteCapProxy),