@camstack/system 1.2.154 → 1.2.156

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.
Files changed (67) hide show
  1. package/dist/addon-runner.js +1 -1
  2. package/dist/addon-runner.mjs +1 -1
  3. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  6. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  7. package/dist/builtins/alerts/alerts.addon.js +1 -1
  8. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  9. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  10. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  11. package/dist/builtins/console-logging/index.js +1 -1
  12. package/dist/builtins/console-logging/index.mjs +1 -1
  13. package/dist/builtins/core-blocks/core-blocks.addon.js +1 -1
  14. package/dist/builtins/core-blocks/core-blocks.addon.mjs +1 -1
  15. package/dist/builtins/device-manager/device-manager.addon.js +2 -2
  16. package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
  17. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  18. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  19. package/dist/builtins/hub-forwarder/index.js +1 -1
  20. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  21. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  22. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  23. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  24. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  25. package/dist/builtins/local-network/local-network.addon.js +1 -1
  26. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  27. package/dist/builtins/loki-logging/index.js +1 -1
  28. package/dist/builtins/loki-logging/index.mjs +1 -1
  29. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  30. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  31. package/dist/builtins/platform-probe/index.js +1 -1
  32. package/dist/builtins/platform-probe/index.mjs +1 -1
  33. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  34. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  35. package/dist/builtins/snapshot/index.js +1 -1
  36. package/dist/builtins/snapshot/index.mjs +1 -1
  37. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  38. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  39. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
  40. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
  41. package/dist/builtins/storage-orchestrator/storage-migration-coordinator.d.ts +101 -1
  42. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.d.ts +24 -0
  43. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +348 -39
  44. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +348 -39
  45. package/dist/builtins/storage-orchestrator/storage-orchestrator.service.d.ts +37 -1
  46. package/dist/builtins/system-config/system-config.addon.js +1 -1
  47. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  48. package/dist/builtins/winston-logging/index.js +1 -1
  49. package/dist/builtins/winston-logging/index.mjs +1 -1
  50. package/dist/{dist-BPlfW-CG.js → dist-CDgIzo82.js} +169 -12
  51. package/dist/{dist-D_NyUV6Y.mjs → dist-LvKus-mT.mjs} +169 -12
  52. package/dist/index.d.ts +2 -0
  53. package/dist/index.js +14 -2
  54. package/dist/index.mjs +3 -3
  55. package/dist/kernel/heap-watch.d.ts +8 -0
  56. package/dist/kernel/socket-plane-report.d.ts +99 -0
  57. package/dist/kernel/transport/frame-codec.d.ts +25 -0
  58. package/dist/kernel/transport/index.d.ts +2 -0
  59. package/dist/kernel/transport/local-child-registry.d.ts +13 -0
  60. package/dist/kernel/transport/local-transport.d.ts +11 -0
  61. package/dist/kernel/transport/socket-channel.d.ts +23 -0
  62. package/dist/kernel/transport/socket-traffic.d.ts +118 -0
  63. package/dist/{manifest-python-deps-BILJm2mI.mjs → manifest-python-deps-CD14Pv0-.mjs} +360 -7
  64. package/dist/{manifest-python-deps-COeSr7el.js → manifest-python-deps-DVODn-qc.js} +431 -6
  65. package/dist/{retired-settings-keys-DBY6ebwV.js → retired-settings-keys-BfAzWvPC.js} +1 -1
  66. package/dist/{retired-settings-keys-Bq1Tb84I.mjs → retired-settings-keys-CV4QVdXj.mjs} +1 -1
  67. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-Cek0wNdY.js");
2
- require("./dist-BPlfW-CG.js");
2
+ require("./dist-CDgIzo82.js");
3
3
  let node_crypto = require("node:crypto");
4
4
  node_crypto = require_chunk.__toESM(node_crypto);
5
5
  let _camstack_types_node = require("@camstack/types/node");
@@ -154,6 +154,282 @@ function formatEventPlane(window) {
154
154
  return ` fanoutMode=uds:${window.udsMode}/cross-node:${window.crossNodeMode} children=${window.childrenConnected} childrenUndeclared=${window.childrenUndeclared} fanoutSent=${window.fanoutSent} fanoutSuppressed=${window.fanoutSuppressed} crossNodeDelivered=${window.crossNodeDelivered} crossNodeSuppressed=${window.crossNodeSuppressed}` + (top.length === 0 ? "" : ` fanoutTop=${top}`);
155
155
  }
156
156
  //#endregion
157
+ //#region src/kernel/transport/socket-traffic.ts
158
+ function createSocketDirectionCounters() {
159
+ return {
160
+ reqMessages: 0,
161
+ reqBytes: 0,
162
+ resMessages: 0,
163
+ resBytes: 0,
164
+ evtMessages: 0,
165
+ evtBytes: 0
166
+ };
167
+ }
168
+ /**
169
+ * The hot-path write. Two additions and at most two string comparisons against
170
+ * interned literals — no allocation, no branch on anything the caller has to
171
+ * compute, and nothing that can throw.
172
+ *
173
+ * Measured on Node 24.17, 20 M iterations, three runs: **2.37–2.41 ns/call**
174
+ * against an empty loop. Its neighbours on the same path, measured in the same
175
+ * run: `encodeFrame` on a 0.33 KB body 1948–1975 ns, `FrameDecoder.push`
176
+ * 1156–1166 ns, `process.memoryUsage()` 920–945 ns. The counter is **0.12 % of
177
+ * `encodeFrame`** — and `encodeFrame` is followed by a syscall.
178
+ *
179
+ * Together with the decoder's per-frame size write (0.06–0.13 ns, see
180
+ * `frame-codec.ts`) the whole instrument adds **~2.5 ns per frame**: at 44 000
181
+ * frames/s that is 107–111 µs/s, i.e. **0.011 % of one core**. An instrument
182
+ * that changes what it measures is not an instrument, and this one cannot.
183
+ */
184
+ function recordSocketFrame(counters, kind, bytes) {
185
+ if (kind === "evt") {
186
+ counters.evtMessages += 1;
187
+ counters.evtBytes += bytes;
188
+ return;
189
+ }
190
+ if (kind === "req") {
191
+ counters.reqMessages += 1;
192
+ counters.reqBytes += bytes;
193
+ return;
194
+ }
195
+ counters.resMessages += 1;
196
+ counters.resBytes += bytes;
197
+ }
198
+ /** Copy out. Called once per heartbeat line, never on the frame path. */
199
+ function sampleSocketDirection(counters) {
200
+ return {
201
+ reqMessages: counters.reqMessages,
202
+ reqBytes: counters.reqBytes,
203
+ resMessages: counters.resMessages,
204
+ resBytes: counters.resBytes,
205
+ evtMessages: counters.evtMessages,
206
+ evtBytes: counters.evtBytes
207
+ };
208
+ }
209
+ /** A direction that has seen nothing. */
210
+ var EMPTY_SOCKET_DIRECTION = {
211
+ reqMessages: 0,
212
+ reqBytes: 0,
213
+ resMessages: 0,
214
+ resBytes: 0,
215
+ evtMessages: 0,
216
+ evtBytes: 0
217
+ };
218
+ /** Total messages across the three kinds. */
219
+ function socketDirectionMessages(sample) {
220
+ return sample.reqMessages + sample.resMessages + sample.evtMessages;
221
+ }
222
+ /** Total on-the-wire bytes across the three kinds. */
223
+ function socketDirectionBytes(sample) {
224
+ return sample.reqBytes + sample.resBytes + sample.evtBytes;
225
+ }
226
+ //#endregion
227
+ //#region src/kernel/socket-plane-report.ts
228
+ /**
229
+ * socket-plane-report — which PEER, exchanging what KIND of message, accounts
230
+ * for hub-main's ~44 000 socket syscalls per second.
231
+ *
232
+ * ## Why this exists, and why it is not the fan-out report
233
+ *
234
+ * `event-plane-report.ts` surfaced the counters the cluster had always kept and
235
+ * never read. It answered its own question and, in answering it, sharpened this
236
+ * one: measured 2026-08-29, **the event plane is 4 % of hub-main's socket
237
+ * traffic**. The remaining 96 % — ~22 000 reads/s and ~22 500 writes/s at a
238
+ * 0.33 KB mean, on the process that mediates every cross-runner call — had no
239
+ * instrument at all, and no instrument was obtainable:
240
+ *
241
+ * - `/proc/<pid>/io` gives ONE `rchar`/`wchar` pair for the whole process.
242
+ * There is no per-socket breakdown anywhere in `/proc`.
243
+ * - The strongest attribution available was a correlation — 78–82 % of
244
+ * hub-main's outbound bytes moving with `hub/pipeline-analytics`, in both
245
+ * measured windows — computed from that runner's own `rchar`, which also
246
+ * counts page-cache file reads on a runner that owns a vector store. A
247
+ * correlation this stable is worth one counter to settle; it is not itself
248
+ * an answer.
249
+ * - Three separate inferences about this traffic were closed early on
250
+ * 2026-08-29 and were wrong. The point of this module is that the next pass
251
+ * is evidence.
252
+ *
253
+ * ## What it reports, and why each field is load-bearing
254
+ *
255
+ * Per kind, per direction, cluster-wide, plus the top peers:
256
+ *
257
+ * - **`evt`** scales with the number of RUNNERS, not with anything an operator
258
+ * asked for — an idle `hub/ml` still reads 82 messages/s. If `evt` dominates,
259
+ * the cost is the plane's own shape and the fix is the fan-out gate.
260
+ * - **`req`** scales with work. If `req` dominates, some caller is chatty and
261
+ * the fix is that caller.
262
+ * - **`res`** is where a large payload can hide (a GOP crosses hub-main as a
263
+ * msgpack `Uint8Array` inside a `res`). **This is the falsifier**: the
264
+ * "message-RATE, not media" conclusion of
265
+ * `docs/design/2026-08-29-hub-main-memory-what-crosses-it.md` §4 rests on a
266
+ * 0.33/0.40 KB mean, and `res` bytes dominating at a large mean breaks it and
267
+ * re-opens the D9/D18 question. Bytes are reported beside messages on every
268
+ * kind precisely so the mean can be read off the line.
269
+ *
270
+ * ## Deltas, never totals
271
+ *
272
+ * The channel counters are cumulative for the life of the channel. A cumulative
273
+ * number on a heartbeat answers "how much since boot", which nobody asked; the
274
+ * question is a rate. So the meter is read ONCE PER LINE and reports the window
275
+ * since the previous read — the same discipline the loop-delay histogram and
276
+ * the fan-out counters already follow, for the same reason.
277
+ *
278
+ * A peer whose runner RESPAWNED reconnects under the same id with a fresh
279
+ * channel and counters that went backwards. Clamping at 0 reports that window
280
+ * as quiet rather than negative: its traffic is genuinely unknown, and a
281
+ * negative number on a rate reads as a broken meter.
282
+ *
283
+ * ## What this is NOT
284
+ *
285
+ * It is not a bound, a budget or a throttle. Nothing here changes a default,
286
+ * drops a frame or tunes a socket. It counts what already crosses, so that the
287
+ * thing that eventually gets fixed is chosen from evidence.
288
+ */
289
+ /**
290
+ * Build a reader over the child registry.
291
+ *
292
+ * Returns `undefined` until the registry exists — the heartbeat starts as the
293
+ * FIRST statement of `bootstrap()` and `LocalChildRegistry` is built hundreds
294
+ * of lines later. Printing zeros in that window would report a silent plane
295
+ * where there is no plane at all.
296
+ *
297
+ * Wrapped: a registry mid-teardown may throw, and a diagnostic that can crash
298
+ * the process it watches is worse than none.
299
+ */
300
+ function createSocketPlaneReader(sources) {
301
+ return () => {
302
+ try {
303
+ const registry = sources.registry();
304
+ if (registry === null) return void 0;
305
+ const listed = registry.listChildren();
306
+ const peers = [];
307
+ for (const child of listed) {
308
+ const traffic = registry.getChildSocketTraffic(child.childId);
309
+ if (traffic === null) continue;
310
+ peers.push({
311
+ peerId: child.childId,
312
+ tx: traffic.tx,
313
+ rx: traffic.rx
314
+ });
315
+ }
316
+ return {
317
+ peers,
318
+ peersConnected: listed.length
319
+ };
320
+ } catch {
321
+ return;
322
+ }
323
+ };
324
+ }
325
+ /**
326
+ * How many peers get named on the line.
327
+ *
328
+ * Five, matching `EVENT_PLANE_TOP_N` for the same reason: the measured
329
+ * distribution is not flat — one peer accounted for 78–82 % of outbound bytes
330
+ * and the runner-up for ~6 % — so five names the dominant peer, its challenger
331
+ * and enough of the tail to see the shape change. Naming all ~38 would put a
332
+ * kilobyte-long line in the hub's shared log every minute, which is how a log
333
+ * stops being read.
334
+ */
335
+ var SOCKET_PLANE_TOP_N = 5;
336
+ /** A process that has never read the plane. */
337
+ var EMPTY_SOCKET_PLANE_BASELINE = { peers: /* @__PURE__ */ new Map() };
338
+ function diffDirection(current, before) {
339
+ const previous = before ?? EMPTY_SOCKET_DIRECTION;
340
+ return {
341
+ reqMessages: Math.max(0, current.reqMessages - previous.reqMessages),
342
+ reqBytes: Math.max(0, current.reqBytes - previous.reqBytes),
343
+ resMessages: Math.max(0, current.resMessages - previous.resMessages),
344
+ resBytes: Math.max(0, current.resBytes - previous.resBytes),
345
+ evtMessages: Math.max(0, current.evtMessages - previous.evtMessages),
346
+ evtBytes: Math.max(0, current.evtBytes - previous.evtBytes)
347
+ };
348
+ }
349
+ function addDirection(into, add) {
350
+ return {
351
+ reqMessages: into.reqMessages + add.reqMessages,
352
+ reqBytes: into.reqBytes + add.reqBytes,
353
+ resMessages: into.resMessages + add.resMessages,
354
+ resBytes: into.resBytes + add.resBytes,
355
+ evtMessages: into.evtMessages + add.evtMessages,
356
+ evtBytes: into.evtBytes + add.evtBytes
357
+ };
358
+ }
359
+ /** Total messages a peer moved in the window, both directions. */
360
+ function peerMessages(delta) {
361
+ return socketDirectionMessages(delta.tx) + socketDirectionMessages(delta.rx);
362
+ }
363
+ /** Subtract the previous cumulative reading from the current one. */
364
+ function diffSocketPlane(baseline, current, topN = 5) {
365
+ const deltas = [];
366
+ let tx = EMPTY_SOCKET_DIRECTION;
367
+ let rx = EMPTY_SOCKET_DIRECTION;
368
+ for (const sample of current.peers) {
369
+ const before = baseline.peers.get(sample.peerId);
370
+ const delta = {
371
+ peerId: sample.peerId,
372
+ tx: diffDirection(sample.tx, before?.tx),
373
+ rx: diffDirection(sample.rx, before?.rx)
374
+ };
375
+ tx = addDirection(tx, delta.tx);
376
+ rx = addDirection(rx, delta.rx);
377
+ if (peerMessages(delta) > 0) deltas.push(delta);
378
+ }
379
+ return {
380
+ peersConnected: current.peersConnected,
381
+ peersMeasured: current.peers.length,
382
+ tx,
383
+ rx,
384
+ top: deltas.toSorted((a, b) => peerMessages(b) - peerMessages(a) || a.peerId.localeCompare(b.peerId)).slice(0, topN)
385
+ };
386
+ }
387
+ function createSocketPlaneMeter(reader, topN = 5) {
388
+ let baseline = EMPTY_SOCKET_PLANE_BASELINE;
389
+ return { read: () => {
390
+ const current = reader();
391
+ if (current === void 0) return void 0;
392
+ const window = diffSocketPlane(baseline, current, topN);
393
+ baseline = { peers: new Map(current.peers.map((p) => [p.peerId, p])) };
394
+ return window;
395
+ } };
396
+ }
397
+ /** Bytes → whole kB. The plane moves 180–330 MB/min; raw bytes would put
398
+ * nine-digit numbers on a line that has to stay readable. */
399
+ function kb(bytes) {
400
+ return Math.round(bytes / 1024);
401
+ }
402
+ /** `req:<msgs>/<kB>,res:<msgs>/<kB>,evt:<msgs>/<kB>` — every kind, always,
403
+ * including at zero: a kind that went quiet is a reading, not a blank. */
404
+ function formatDirection(sample) {
405
+ return `req:${sample.reqMessages}/${kb(sample.reqBytes)}kB,res:${sample.resMessages}/${kb(sample.resBytes)}kB,evt:${sample.evtMessages}/${kb(sample.evtBytes)}kB`;
406
+ }
407
+ /** `<peer>:tx=<req>/<res>/<evt>:rx=<req>/<res>/<evt>:kB=<tx>/<rx>` — the whole
408
+ * peer×kind cross for one peer, which is the question in one token. */
409
+ function formatPeer(delta) {
410
+ return `${delta.peerId}:tx=${delta.tx.reqMessages}/${delta.tx.resMessages}/${delta.tx.evtMessages}:rx=${delta.rx.reqMessages}/${delta.rx.resMessages}/${delta.rx.evtMessages}:kB=${kb(socketDirectionBytes(delta.tx))}/${kb(socketDirectionBytes(delta.rx))}`;
411
+ }
412
+ /**
413
+ * The suffix appended to a `[mem]` line — leading space included, empty when
414
+ * this process has no local socket plane.
415
+ *
416
+ * Only the processes that OWN a child registry have one (hub-main, and an
417
+ * agent's main), so these fields never appear on the ~38 runner lines that
418
+ * share the same log stream. On the ones that do, every field is printed on
419
+ * every line including at zero — `socketPeers` because measured-below-connected
420
+ * is the only way to see an unmeasured channel, and the per-kind splits because
421
+ * "the plane went quiet for a minute" is a reading.
422
+ *
423
+ * `socketTop` is the one conditional field — a ranked list of nothing is noise —
424
+ * and it is a single comma-separated value so the field COUNT does not change
425
+ * from line to line.
426
+ */
427
+ function formatSocketPlane(window) {
428
+ if (window === void 0) return "";
429
+ const top = window.top.map(formatPeer).join(",");
430
+ return ` socketPeers=${window.peersMeasured}/${window.peersConnected} socketTx=${formatDirection(window.tx)} socketRx=${formatDirection(window.rx)}` + (top.length === 0 ? "" : ` socketTop=${top}`);
431
+ }
432
+ //#endregion
157
433
  //#region src/kernel/heap-spaces.ts
158
434
  /**
159
435
  * heap-spaces — WHERE in the V8 heap the bytes are, on the ordinary heartbeat.
@@ -935,6 +1211,7 @@ function format(label, s, loop, budgetMb, suffix) {
935
1211
  function startHeapWatch(label = HUB_MAIN_HEAP_WATCH_LABEL, sink = consoleSink, intervalMs = HEAP_WATCH_INTERVAL_MS, reclaimOptions, loopDelay = createLoopDelayMeter(), execArgv = process.execArgv, announceCeilingOrigin = true, rssBudget, probes) {
936
1212
  const readSpaces = probes?.readSpaces ?? readHeapSpaces;
937
1213
  const eventPlane = probes?.eventPlane === void 0 ? void 0 : createEventPlaneMeter(probes.eventPlane);
1214
+ const socketPlane = probes?.socketPlane === void 0 ? void 0 : createSocketPlaneMeter(probes.socketPlane);
938
1215
  const readMemory = reclaimOptions?.readMemory ?? (() => process.memoryUsage());
939
1216
  const now = reclaimOptions?.now ?? (() => Date.now());
940
1217
  const triggerMb = reclaimOptions?.triggerMb ?? 1024;
@@ -972,7 +1249,7 @@ function startHeapWatch(label = HUB_MAIN_HEAP_WATCH_LABEL, sink = consoleSink, i
972
1249
  return;
973
1250
  }
974
1251
  const after = read();
975
- sink.info(`[mem] reclaim ${label} stranded=${strandedMb(sample)}MB rss=${sample.rssMb}MB→${after.rssMb}MB freed=${sample.rssMb - after.rssMb}MB arrayBuffers=${sample.arrayBuffersMb}MB→${after.arrayBuffersMb}MB took=${now() - startedAt}ms`);
1252
+ sink.info(`[mem] reclaim ${label} stranded=${strandedMb(sample)}MB rss=${sample.rssMb}MB→${after.rssMb}MB freed=${sample.rssMb - after.rssMb}MB arrayBuffers=${sample.arrayBuffersMb}MB→${after.arrayBuffersMb}MB took=${now() - startedAt}ms heapUsed=${sample.heapUsedMb}MB→${after.heapUsedMb}MB`);
976
1253
  if (passesAtFloor >= 6 && !steadyStateAnnounced) {
977
1254
  steadyStateAnnounced = true;
978
1255
  const nextFloorMs = reclaimIntervalMs(passesAtFloor, minIntervalMs, steadyStateIntervalMs);
@@ -1039,7 +1316,7 @@ function startHeapWatch(label = HUB_MAIN_HEAP_WATCH_LABEL, sink = consoleSink, i
1039
1316
  const due = at - lastLoggedAt >= intervalMs;
1040
1317
  if (mode === "escalated" || due) {
1041
1318
  lastLoggedAt = at;
1042
- const line = format(label, sample, loopDelay?.read(), budgetMb, `${formatHeapSpaces(readSpaces())}${formatEventPlane(eventPlane?.read())}`);
1319
+ const line = format(label, sample, loopDelay?.read(), budgetMb, `${formatHeapSpaces(readSpaces())}${formatEventPlane(eventPlane?.read())}` + formatSocketPlane(socketPlane?.read()));
1043
1320
  if (sample.nearLimit) sink.warn(`${line} — APPROACHING HEAP LIMIT`);
1044
1321
  else if (mode === "escalated") sink.warn(`${line} — heap elevated, sampling every ${probeIntervalMs}ms`);
1045
1322
  else sink.info(line);
@@ -4910,6 +5187,33 @@ var FrameDecoder = class {
4910
5187
  chunks = [];
4911
5188
  /** Sum of `chunks[i].byteLength` — tracked so length checks cost nothing. */
4912
5189
  buffered = 0;
5190
+ /** Backing store for {@link lastFrameBytes}; see that getter. */
5191
+ sizes = [];
5192
+ /**
5193
+ * On-the-wire byte length (4-byte prefix included) of each frame returned by
5194
+ * the most recent {@link push}, index-aligned with the returned array.
5195
+ *
5196
+ * It exists because the frames come back DECODED, and a decoded value has no
5197
+ * memory of how many bytes it cost — so a caller counting received bytes per
5198
+ * message kind (`SocketChannel`, feeding the `[mem]` line's per-peer socket
5199
+ * attribution) could otherwise only charge a whole `data` chunk, which may
5200
+ * hold several frames of different kinds, or half of one.
5201
+ *
5202
+ * **Only the first `frames.length` entries are meaningful**, and the array is
5203
+ * never truncated. That is deliberate and it was measured on Node 24.17:
5204
+ * `sizes.length = 0` + `push` costs **35.1–35.9 ns** per call — V8 shrinks the
5205
+ * backing store and the next call re-grows it — which is 3 % of
5206
+ * `FrameDecoder.push` itself and 15× the counter it feeds. Writing in place
5207
+ * costs **0.06–0.13 ns**. On a path that runs ~22 000 times a second, an
5208
+ * instrument may not be the most expensive thing on the line it measures.
5209
+ *
5210
+ * Read it immediately after the `push` that produced it — it is a window onto
5211
+ * the decoder, not a value. Safe because `push` is only ever driven by a
5212
+ * socket `data` event, which Node never delivers re-entrantly.
5213
+ */
5214
+ get lastFrameBytes() {
5215
+ return this.sizes;
5216
+ }
4913
5217
  push(chunk) {
4914
5218
  if (chunk.byteLength > 0) {
4915
5219
  this.chunks.push(chunk);
@@ -4927,6 +5231,7 @@ var FrameDecoder = class {
4927
5231
  body = Buffer.allocUnsafeSlow(len);
4928
5232
  frame.copy(body, 0, HEADER_BYTES);
4929
5233
  } else body = frame.subarray(HEADER_BYTES);
5234
+ this.sizes[frames.length] = total;
4930
5235
  frames.push(decode(body));
4931
5236
  }
4932
5237
  return frames;
@@ -5011,6 +5316,11 @@ var SocketChannel = class {
5011
5316
  closeHandler = () => {};
5012
5317
  closed = false;
5013
5318
  closeFired = false;
5319
+ /** Frames written to the peer, cumulative, split by kind. See
5320
+ * {@link readTraffic} and `transport/socket-traffic.ts`. */
5321
+ txCounters = createSocketDirectionCounters();
5322
+ /** Frames read from the peer, cumulative, split by kind. */
5323
+ rxCounters = createSocketDirectionCounters();
5014
5324
  constructor(socket) {
5015
5325
  this.socket = socket;
5016
5326
  socket.on("data", (chunk) => this.onData(chunk));
@@ -5077,14 +5387,41 @@ var SocketChannel = class {
5077
5387
  this.closed = true;
5078
5388
  this.socket.destroy();
5079
5389
  }
5390
+ /**
5391
+ * This channel's cumulative traffic, both directions, split by frame kind.
5392
+ *
5393
+ * Cumulative for the life of the channel: the reporting layer subtracts the
5394
+ * previous reading, because the question is a RATE ("which peer receives the
5395
+ * 22 500 writes/s") and not a total since boot.
5396
+ */
5397
+ readTraffic() {
5398
+ return {
5399
+ tx: sampleSocketDirection(this.txCounters),
5400
+ rx: sampleSocketDirection(this.rxCounters)
5401
+ };
5402
+ }
5080
5403
  send(frame) {
5081
5404
  if (this.closed) return;
5082
- this.socket.write(encodeFrame(frame));
5405
+ const encoded = encodeFrame(frame);
5406
+ recordSocketFrame(this.txCounters, frame.k, encoded.byteLength);
5407
+ this.socket.write(encoded);
5083
5408
  }
5084
5409
  onData(chunk) {
5085
- for (const f of this.decoder.push(chunk)) this.handleFrame(f);
5410
+ const frames = this.decoder.push(chunk);
5411
+ const sizes = this.decoder.lastFrameBytes;
5412
+ for (let i = 0; i < frames.length; i += 1) this.handleFrame(frames[i], sizes[i] ?? 0);
5086
5413
  }
5087
- async handleFrame(frame) {
5414
+ /**
5415
+ * `wireBytes` is the frame's on-the-wire length, counted HERE rather than in
5416
+ * `onData` on purpose: a peer that sends a frame this decoder can parse but
5417
+ * whose shape is not a `Frame` throws on `frame.k`, and inside this async
5418
+ * method that stays the rejected promise it has always been. Reading `.k`
5419
+ * one frame earlier, synchronously in the socket's `data` handler, would
5420
+ * turn the same malformed frame into an uncaught exception on the transport
5421
+ * — a diagnostic that can crash the process it watches.
5422
+ */
5423
+ async handleFrame(frame, wireBytes) {
5424
+ recordSocketFrame(this.rxCounters, frame.k, wireBytes);
5088
5425
  if (frame.k === "req") {
5089
5426
  try {
5090
5427
  const result = await this.requestHandler(frame.body);
@@ -5958,6 +6295,22 @@ var LocalChildRegistry = class {
5958
6295
  };
5959
6296
  }
5960
6297
  /**
6298
+ * This child's socket traffic — bytes and messages, both directions, split by
6299
+ * frame kind — or `null` when the child is gone or its channel keeps no
6300
+ * counters (an in-process or test transport).
6301
+ *
6302
+ * The event counters above cover ONE kind of frame and, once surfaced on
6303
+ * 2026-08-29, accounted for 4% of hub-main's ~44 000 socket syscalls/s. This
6304
+ * is the same question asked of every frame the channel moves, and it is the
6305
+ * only per-peer attribution that exists: `/proc` reports one `rchar`/`wchar`
6306
+ * pair for the whole process and has no per-socket breakdown.
6307
+ */
6308
+ getChildSocketTraffic(childId) {
6309
+ const entry = this.children.get(childId);
6310
+ if (entry === void 0) return null;
6311
+ return entry.channel.readTraffic?.() ?? null;
6312
+ }
6313
+ /**
5961
6314
  * The regime the counters above were produced under, read once from
5962
6315
  * `CAMSTACK_UDS_EVENT_FANOUT` at construction.
5963
6316
  *
@@ -9211,6 +9564,18 @@ Object.defineProperty(exports, "DeviceRegistry", {
9211
9564
  return DeviceRegistry;
9212
9565
  }
9213
9566
  });
9567
+ Object.defineProperty(exports, "EMPTY_SOCKET_DIRECTION", {
9568
+ enumerable: true,
9569
+ get: function() {
9570
+ return EMPTY_SOCKET_DIRECTION;
9571
+ }
9572
+ });
9573
+ Object.defineProperty(exports, "EMPTY_SOCKET_PLANE_BASELINE", {
9574
+ enumerable: true,
9575
+ get: function() {
9576
+ return EMPTY_SOCKET_PLANE_BASELINE;
9577
+ }
9578
+ });
9214
9579
  Object.defineProperty(exports, "EVENT_PLANE_TOP_N", {
9215
9580
  enumerable: true,
9216
9581
  get: function() {
@@ -9343,6 +9708,12 @@ Object.defineProperty(exports, "RUNNER_RSS_BUDGET_ENV", {
9343
9708
  return RUNNER_RSS_BUDGET_ENV;
9344
9709
  }
9345
9710
  });
9711
+ Object.defineProperty(exports, "SOCKET_PLANE_TOP_N", {
9712
+ enumerable: true,
9713
+ get: function() {
9714
+ return SOCKET_PLANE_TOP_N;
9715
+ }
9716
+ });
9346
9717
  Object.defineProperty(exports, "SocketChannel", {
9347
9718
  enumerable: true,
9348
9719
  get: function() {
@@ -9541,6 +9912,24 @@ Object.defineProperty(exports, "createParentUnownedCallHandler", {
9541
9912
  return createParentUnownedCallHandler;
9542
9913
  }
9543
9914
  });
9915
+ Object.defineProperty(exports, "createSocketDirectionCounters", {
9916
+ enumerable: true,
9917
+ get: function() {
9918
+ return createSocketDirectionCounters;
9919
+ }
9920
+ });
9921
+ Object.defineProperty(exports, "createSocketPlaneMeter", {
9922
+ enumerable: true,
9923
+ get: function() {
9924
+ return createSocketPlaneMeter;
9925
+ }
9926
+ });
9927
+ Object.defineProperty(exports, "createSocketPlaneReader", {
9928
+ enumerable: true,
9929
+ get: function() {
9930
+ return createSocketPlaneReader;
9931
+ }
9932
+ });
9544
9933
  Object.defineProperty(exports, "createUdsAddonContext", {
9545
9934
  enumerable: true,
9546
9935
  get: function() {
@@ -9595,6 +9984,12 @@ Object.defineProperty(exports, "diffEventPlane", {
9595
9984
  return diffEventPlane;
9596
9985
  }
9597
9986
  });
9987
+ Object.defineProperty(exports, "diffSocketPlane", {
9988
+ enumerable: true,
9989
+ get: function() {
9990
+ return diffSocketPlane;
9991
+ }
9992
+ });
9598
9993
  Object.defineProperty(exports, "emitHeapDiagnosticReport", {
9599
9994
  enumerable: true,
9600
9995
  get: function() {
@@ -9619,6 +10014,12 @@ Object.defineProperty(exports, "formatHeapSpaces", {
9619
10014
  return formatHeapSpaces;
9620
10015
  }
9621
10016
  });
10017
+ Object.defineProperty(exports, "formatSocketPlane", {
10018
+ enumerable: true,
10019
+ get: function() {
10020
+ return formatSocketPlane;
10021
+ }
10022
+ });
9622
10023
  Object.defineProperty(exports, "getBrokerEventBus", {
9623
10024
  enumerable: true,
9624
10025
  get: function() {
@@ -9757,6 +10158,12 @@ Object.defineProperty(exports, "reclaimIntervalMs", {
9757
10158
  return reclaimIntervalMs;
9758
10159
  }
9759
10160
  });
10161
+ Object.defineProperty(exports, "recordSocketFrame", {
10162
+ enumerable: true,
10163
+ get: function() {
10164
+ return recordSocketFrame;
10165
+ }
10166
+ });
9760
10167
  Object.defineProperty(exports, "registerEventBusService", {
9761
10168
  enumerable: true,
9762
10169
  get: function() {
@@ -9787,6 +10194,12 @@ Object.defineProperty(exports, "runNpm", {
9787
10194
  return runNpm;
9788
10195
  }
9789
10196
  });
10197
+ Object.defineProperty(exports, "sampleSocketDirection", {
10198
+ enumerable: true,
10199
+ get: function() {
10200
+ return sampleSocketDirection;
10201
+ }
10202
+ });
9790
10203
  Object.defineProperty(exports, "selectReportedSpaces", {
9791
10204
  enumerable: true,
9792
10205
  get: function() {
@@ -9823,6 +10236,18 @@ Object.defineProperty(exports, "shouldReclaim", {
9823
10236
  return shouldReclaim;
9824
10237
  }
9825
10238
  });
10239
+ Object.defineProperty(exports, "socketDirectionBytes", {
10240
+ enumerable: true,
10241
+ get: function() {
10242
+ return socketDirectionBytes;
10243
+ }
10244
+ });
10245
+ Object.defineProperty(exports, "socketDirectionMessages", {
10246
+ enumerable: true,
10247
+ get: function() {
10248
+ return socketDirectionMessages;
10249
+ }
10250
+ });
9826
10251
  Object.defineProperty(exports, "startHeapWatch", {
9827
10252
  enumerable: true,
9828
10253
  get: function() {
@@ -1,4 +1,4 @@
1
- const require_dist = require("./dist-BPlfW-CG.js");
1
+ const require_dist = require("./dist-CDgIzo82.js");
2
2
  //#region src/builtins/sqlite-storage/retired-settings-keys.ts
3
3
  /**
4
4
  * Is THIS node the one whose settings store is the cluster's authority?
@@ -1,4 +1,4 @@
1
- import { zt as asJsonObject } from "./dist-D_NyUV6Y.mjs";
1
+ import { zt as asJsonObject } from "./dist-LvKus-mT.mjs";
2
2
  //#region src/builtins/sqlite-storage/retired-settings-keys.ts
3
3
  /**
4
4
  * Is THIS node the one whose settings store is the cluster's authority?
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/system",
3
- "version": "1.2.154",
3
+ "version": "1.2.156",
4
4
  "description": "Core addon for CamStack — builtins, pipeline, process management, auth, logging, events",
5
5
  "keywords": [
6
6
  "camstack",