camstack 1.2.58 → 1.2.59

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.
@@ -23633,9 +23633,9 @@ var require_zod = __commonJS({
23633
23633
  }
23634
23634
  });
23635
23635
 
23636
- // ../system/dist/dist-CxgTulEG.js
23637
- var require_dist_CxgTulEG = __commonJS({
23638
- "../system/dist/dist-CxgTulEG.js"(exports) {
23636
+ // ../system/dist/dist-C7uZ_fOb.js
23637
+ var require_dist_C7uZ_fOb = __commonJS({
23638
+ "../system/dist/dist-C7uZ_fOb.js"(exports) {
23639
23639
  "use strict";
23640
23640
  var zod = require_zod();
23641
23641
  var EventCategory = /* @__PURE__ */ (function(EventCategory2) {
@@ -32718,6 +32718,38 @@ var require_dist_CxgTulEG = __commonJS({
32718
32718
  /** Wall-clock uptime (seconds). Parsed from `ps etime`. */
32719
32719
  uptimeSec: zod.z.number()
32720
32720
  });
32721
+ var ContainerMemoryPointSchema = zod.z.object({
32722
+ /** Which hierarchy answered, so a reading is never ambiguous. */
32723
+ source: zod.z.enum(["cgroup-v2", "cgroup-v1"]),
32724
+ /** `memory.current` (v2) / `memory.usage_in_bytes` (v1). Always known. */
32725
+ currentBytes: zod.z.number(),
32726
+ /** The cgroup's ceiling. `null` = NO LIMIT — never a sentinel, never zero. */
32727
+ limitBytes: zod.z.number().nullable(),
32728
+ /** Anonymous pages: the closest thing to "what the processes allocated". */
32729
+ anonBytes: zod.z.number().nullable(),
32730
+ /** Page cache. Charged to the cgroup, owned by no process. */
32731
+ fileBytes: zod.z.number().nullable(),
32732
+ /**
32733
+ * Shared memory — and the field that explained the largest single surprise.
32734
+ * The i915 driver backs GPU buffers with shmem, so an inference pool or a
32735
+ * hardware-decode session holding DRM objects is charged HERE and appears
32736
+ * nowhere in a `ps` scan.
32737
+ */
32738
+ shmemBytes: zod.z.number().nullable(),
32739
+ /** Kernel slab charged to this cgroup. `null` on v1, which never publishes it. */
32740
+ slabBytes: zod.z.number().nullable(),
32741
+ /**
32742
+ * Shrinkable i915 GEM object bytes, from debugfs.
32743
+ *
32744
+ * **Host-wide across every DRM client, NOT cgroup-scoped.** It is not a
32745
+ * component of `currentBytes` and must not be subtracted from it; it says
32746
+ * what put the shmem there, where `shmemBytes` only says how much.
32747
+ *
32748
+ * `null` wherever debugfs is not mounted — which is inside every camstack
32749
+ * container today — and on any node with no Intel GPU.
32750
+ */
32751
+ gpuShmemBytes: zod.z.number().nullable()
32752
+ }).extend({ atMs: zod.z.number() });
32721
32753
  var DumpHeapSnapshotInputSchema = zod.z.object({
32722
32754
  /** The addon whose runner should dump a heap snapshot. */
32723
32755
  addonId: zod.z.string()
@@ -32767,6 +32799,21 @@ var require_dist_CxgTulEG = __commonJS({
32767
32799
  /** One entry per function seen in the window, heaviest-first. */
32768
32800
  series: zod.z.array(LoadFunctionSeriesSchema).readonly(),
32769
32801
  /**
32802
+ * The CONTAINER's memory over the same window, oldest-first.
32803
+ *
32804
+ * Sits next to `series` rather than in a method of its own because the whole
32805
+ * question is a subtraction: the per-process rows in `series` sum to one
32806
+ * number and this one is another, and an operator who has to issue two calls
32807
+ * to compare them will compare two different instants. Same reader, same
32808
+ * `sinceMs`, same `bucketMs`, same timestamps.
32809
+ *
32810
+ * **EMPTY means ABSENT, never zero.** A node with no cgroup — a developer
32811
+ * Mac, a bare-metal host, a container with the hierarchy hidden — reports no
32812
+ * points at all. A zero here would be indistinguishable from a healthy
32813
+ * container and is precisely the lie this field exists to avoid.
32814
+ */
32815
+ containerMemory: zod.z.array(ContainerMemoryPointSchema).readonly(),
32816
+ /**
32770
32817
  * Width of one returned bucket, in ms. Equals the sampling cadence when no
32771
32818
  * reduction was needed — so a caller can always say what one point covers
32772
32819
  * without having to know whether it was reduced.
@@ -58639,7 +58686,7 @@ var require_alerts_addon = __commonJS({
58639
58686
  [Symbol.toStringTag]: { value: "Module" }
58640
58687
  });
58641
58688
  require_chunk_Cek0wNdY();
58642
- var require_dist10 = require_dist_CxgTulEG();
58689
+ var require_dist10 = require_dist_C7uZ_fOb();
58643
58690
  function selectExpired(alerts, cutoffMs) {
58644
58691
  return alerts.filter((a) => a.createdAt < cutoffMs).sort((a, b) => a.createdAt - b.createdAt).map((a) => a.id);
58645
58692
  }
@@ -59458,7 +59505,7 @@ var require_console_logging = __commonJS({
59458
59505
  [Symbol.toStringTag]: { value: "Module" }
59459
59506
  });
59460
59507
  require_chunk_Cek0wNdY();
59461
- var require_dist10 = require_dist_CxgTulEG();
59508
+ var require_dist10 = require_dist_C7uZ_fOb();
59462
59509
  var require_formatter = require_formatter_DqAKDlvN();
59463
59510
  var LEVEL_RANK = {
59464
59511
  debug: 0,
@@ -59552,7 +59599,7 @@ var require_core_blocks_addon = __commonJS({
59552
59599
  "use strict";
59553
59600
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
59554
59601
  var require_chunk = require_chunk_Cek0wNdY();
59555
- var require_dist10 = require_dist_CxgTulEG();
59602
+ var require_dist10 = require_dist_C7uZ_fOb();
59556
59603
  var node_crypto = __require("crypto");
59557
59604
  var node_fs_promises = __require("fs/promises");
59558
59605
  node_fs_promises = require_chunk.__toESM(node_fs_promises);
@@ -60449,11 +60496,11 @@ var require_core_blocks = __commonJS({
60449
60496
  }
60450
60497
  });
60451
60498
 
60452
- // ../system/dist/retired-settings-keys-BbjWjol0.js
60453
- var require_retired_settings_keys_BbjWjol0 = __commonJS({
60454
- "../system/dist/retired-settings-keys-BbjWjol0.js"(exports) {
60499
+ // ../system/dist/retired-settings-keys-CBU4-TJO.js
60500
+ var require_retired_settings_keys_CBU4_TJO = __commonJS({
60501
+ "../system/dist/retired-settings-keys-CBU4-TJO.js"(exports) {
60455
60502
  "use strict";
60456
- var require_dist10 = require_dist_CxgTulEG();
60503
+ var require_dist10 = require_dist_C7uZ_fOb();
60457
60504
  function settingsStoreIsAuthoritativeHere(env) {
60458
60505
  const raw = (env["CAMSTACK_ROLE"] ?? "").trim().toLowerCase();
60459
60506
  return raw === "" || raw === "hub";
@@ -62667,8 +62714,8 @@ var require_device_manager_addon = __commonJS({
62667
62714
  [Symbol.toStringTag]: { value: "Module" }
62668
62715
  });
62669
62716
  require_chunk_Cek0wNdY();
62670
- var require_dist10 = require_dist_CxgTulEG();
62671
- var require_retired_settings_keys = require_retired_settings_keys_BbjWjol0();
62717
+ var require_dist10 = require_dist_C7uZ_fOb();
62718
+ var require_retired_settings_keys = require_retired_settings_keys_CBU4_TJO();
62672
62719
  var node_crypto = __require("crypto");
62673
62720
  var _camstack_types_node = require_node();
62674
62721
  var JOB_HISTORY = 20;
@@ -67417,7 +67464,7 @@ var require_hub_forwarder = __commonJS({
67417
67464
  [Symbol.toStringTag]: { value: "Module" }
67418
67465
  });
67419
67466
  require_chunk_Cek0wNdY();
67420
- var require_dist10 = require_dist_CxgTulEG();
67467
+ var require_dist10 = require_dist_C7uZ_fOb();
67421
67468
  var require_formatter = require_formatter_DqAKDlvN();
67422
67469
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
67423
67470
  var HubForwarderDestination = class {
@@ -67554,7 +67601,7 @@ var require_liveness_monitor_addon = __commonJS({
67554
67601
  "use strict";
67555
67602
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
67556
67603
  require_chunk_Cek0wNdY();
67557
- var require_dist10 = require_dist_CxgTulEG();
67604
+ var require_dist10 = require_dist_C7uZ_fOb();
67558
67605
  var NO_DEVICES = "liveness:no-devices";
67559
67606
  var ALL_OFFLINE = "liveness:all-devices-offline";
67560
67607
  var NO_FOOTAGE_PREFIX = "liveness:no-footage:";
@@ -67744,7 +67791,7 @@ var require_local_auth_addon = __commonJS({
67744
67791
  [Symbol.toStringTag]: { value: "Module" }
67745
67792
  });
67746
67793
  var require_chunk = require_chunk_Cek0wNdY();
67747
- var require_dist10 = require_dist_CxgTulEG();
67794
+ var require_dist10 = require_dist_C7uZ_fOb();
67748
67795
  var node_crypto = __require("crypto");
67749
67796
  node_crypto = require_chunk.__toESM(node_crypto);
67750
67797
  var crypto$1 = __require("crypto");
@@ -75557,7 +75604,7 @@ var require_loki_logging = __commonJS({
75557
75604
  [Symbol.toStringTag]: { value: "Module" }
75558
75605
  });
75559
75606
  require_chunk_Cek0wNdY();
75560
- var require_dist10 = require_dist_CxgTulEG();
75607
+ var require_dist10 = require_dist_C7uZ_fOb();
75561
75608
  function sanitizeLabelName(raw) {
75562
75609
  const replaced = raw.replaceAll(/[^a-zA-Z0-9_]/g, "_");
75563
75610
  return /^[a-zA-Z_]/.test(replaced) ? replaced : `_${replaced}`;
@@ -76122,14 +76169,859 @@ var require_native_metrics_addon = __commonJS({
76122
76169
  [Symbol.toStringTag]: { value: "Module" }
76123
76170
  });
76124
76171
  var require_chunk = require_chunk_Cek0wNdY();
76125
- var require_dist10 = require_dist_CxgTulEG();
76172
+ var require_dist10 = require_dist_C7uZ_fOb();
76126
76173
  var node_fs_promises = __require("fs/promises");
76127
76174
  var node_child_process = __require("child_process");
76128
76175
  var node_util = __require("util");
76129
- var node_os = __require("os");
76130
- node_os = require_chunk.__toESM(node_os);
76131
76176
  var node_fs = __require("fs");
76132
76177
  node_fs = require_chunk.__toESM(node_fs);
76178
+ var node_os = __require("os");
76179
+ node_os = require_chunk.__toESM(node_os);
76180
+ var nodeCgroupFileReader = { read(path) {
76181
+ try {
76182
+ return (0, node_fs.readFileSync)(path, "utf8");
76183
+ } catch {
76184
+ return null;
76185
+ }
76186
+ } };
76187
+ var CGROUP_MOUNT_ROOT = "/sys/fs/cgroup";
76188
+ var I915_GEM_OBJECTS_PATH = "/sys/kernel/debug/dri/0/i915_gem_objects";
76189
+ var V1_UNLIMITED_SENTINEL = 9223372036854772e3;
76190
+ function parseCgroupSelf(text) {
76191
+ let v1 = null;
76192
+ for (const rawLine of text.split("\n")) {
76193
+ const line = rawLine.trim();
76194
+ if (line.length === 0) continue;
76195
+ const first = line.indexOf(":");
76196
+ if (first < 0) continue;
76197
+ const second = line.indexOf(":", first + 1);
76198
+ if (second < 0) continue;
76199
+ const controllers = line.slice(first + 1, second);
76200
+ const path = line.slice(second + 1);
76201
+ if (path.length === 0) continue;
76202
+ const normalized = path.startsWith("/") ? path : `/${path}`;
76203
+ if (controllers.length === 0) return {
76204
+ version: "v2",
76205
+ path: normalized
76206
+ };
76207
+ if (v1 === null && controllers.split(",").includes("memory")) v1 = {
76208
+ version: "v1",
76209
+ path: normalized
76210
+ };
76211
+ }
76212
+ return v1;
76213
+ }
76214
+ function parseCgroupStat(text) {
76215
+ const out = /* @__PURE__ */ new Map();
76216
+ for (const rawLine of text.split("\n")) {
76217
+ const line = rawLine.trim();
76218
+ if (line.length === 0) continue;
76219
+ const space = line.indexOf(" ");
76220
+ if (space <= 0) continue;
76221
+ const key = line.slice(0, space);
76222
+ const value = Number(line.slice(space + 1).trim());
76223
+ if (!Number.isFinite(value)) continue;
76224
+ out.set(key, value);
76225
+ }
76226
+ return out;
76227
+ }
76228
+ function parseCgroupScalar(text) {
76229
+ const trimmed = text.trim();
76230
+ if (trimmed.length === 0) return null;
76231
+ if (trimmed === "max") return null;
76232
+ const value = Number(trimmed);
76233
+ if (!Number.isFinite(value) || value < 0) return null;
76234
+ if (value >= V1_UNLIMITED_SENTINEL) return null;
76235
+ return value;
76236
+ }
76237
+ function parseI915GemObjects(text) {
76238
+ const match = text.match(/^\s*\d+\s+shrinkable\b[^,\n]*,\s*(\d+)\s+bytes/m);
76239
+ if (match === null) return null;
76240
+ const bytes = Number(match[1]);
76241
+ return Number.isFinite(bytes) ? bytes : null;
76242
+ }
76243
+ function resolveCgroupMemoryLayout(fs) {
76244
+ const selfText = fs.read("/proc/self/cgroup");
76245
+ if (selfText === null) return null;
76246
+ const entry = parseCgroupSelf(selfText);
76247
+ if (entry === null) return null;
76248
+ const suffix = entry.path === "/" ? "" : entry.path;
76249
+ const candidates = entry.version === "v2" ? [`${CGROUP_MOUNT_ROOT}${suffix}`, CGROUP_MOUNT_ROOT] : [`${CGROUP_MOUNT_ROOT}/memory${suffix}`, `${CGROUP_MOUNT_ROOT}/memory`];
76250
+ const usageFile = entry.version === "v2" ? "memory.current" : "memory.usage_in_bytes";
76251
+ const limitFile = entry.version === "v2" ? "memory.max" : "memory.limit_in_bytes";
76252
+ for (const directory of candidates) {
76253
+ if (fs.read(`${directory}/${usageFile}`) === null) continue;
76254
+ return {
76255
+ source: entry.version === "v2" ? "cgroup-v2" : "cgroup-v1",
76256
+ directory,
76257
+ currentPath: `${directory}/${usageFile}`,
76258
+ limitPath: `${directory}/${limitFile}`,
76259
+ statPath: `${directory}/memory.stat`
76260
+ };
76261
+ }
76262
+ return null;
76263
+ }
76264
+ function statValue(stat, key) {
76265
+ const value = stat.get(key);
76266
+ return value === void 0 ? null : value;
76267
+ }
76268
+ function resolveSlab(stat) {
76269
+ const direct = statValue(stat, "slab");
76270
+ if (direct !== null) return direct;
76271
+ const reclaimable = stat.get("slab_reclaimable");
76272
+ const unreclaimable = stat.get("slab_unreclaimable");
76273
+ if (reclaimable === void 0 || unreclaimable === void 0) return null;
76274
+ return reclaimable + unreclaimable;
76275
+ }
76276
+ function buildContainerMemorySnapshot(input) {
76277
+ const currentBytes = parseCgroupScalar(input.currentText);
76278
+ if (currentBytes === null) return null;
76279
+ const stat = input.statText === null ? /* @__PURE__ */ new Map() : parseCgroupStat(input.statText);
76280
+ const isV2 = input.source === "cgroup-v2";
76281
+ return {
76282
+ source: input.source,
76283
+ currentBytes,
76284
+ limitBytes: input.limitText === null ? null : parseCgroupScalar(input.limitText),
76285
+ anonBytes: statValue(stat, isV2 ? "anon" : "rss"),
76286
+ fileBytes: statValue(stat, isV2 ? "file" : "cache"),
76287
+ shmemBytes: statValue(stat, "shmem"),
76288
+ slabBytes: isV2 ? resolveSlab(stat) : null,
76289
+ gpuShmemBytes: input.gpuShmemBytes
76290
+ };
76291
+ }
76292
+ var ContainerMemoryProbe = class {
76293
+ logger;
76294
+ fs;
76295
+ wantGpu;
76296
+ resolved = false;
76297
+ layout = null;
76298
+ gpuProbed = false;
76299
+ gpuReadable = false;
76300
+ /** `null` until the first sample — so the FIRST outcome is a transition too. */
76301
+ lastReadOk = null;
76302
+ constructor(deps) {
76303
+ this.logger = deps.logger;
76304
+ this.fs = deps.fs ?? nodeCgroupFileReader;
76305
+ this.wantGpu = deps.readGpu ?? true;
76306
+ }
76307
+ /** Did this node resolve a cgroup at all? Absent is a reportable fact. */
76308
+ get available() {
76309
+ return this.layout !== null;
76310
+ }
76311
+ /**
76312
+ * One reading, or `null` when this node has no cgroup or the cgroup stopped
76313
+ * answering. `null` is ABSENT — the caller contributes no point, and no
76314
+ * point is what a chart must show, never a zero.
76315
+ */
76316
+ sample() {
76317
+ const layout = this.resolveOnce();
76318
+ if (layout === null) return null;
76319
+ const currentText = this.fs.read(layout.currentPath);
76320
+ if (currentText === null) {
76321
+ this.noteRead(false, `unreadable: ${layout.currentPath}`);
76322
+ return null;
76323
+ }
76324
+ const snapshot = buildContainerMemorySnapshot({
76325
+ source: layout.source,
76326
+ currentText,
76327
+ limitText: this.fs.read(layout.limitPath),
76328
+ statText: this.fs.read(layout.statPath),
76329
+ gpuShmemBytes: this.readGpuShmem()
76330
+ });
76331
+ if (snapshot === null) {
76332
+ this.noteRead(false, `unparseable: ${layout.currentPath}`);
76333
+ return null;
76334
+ }
76335
+ this.noteRead(true, null);
76336
+ return snapshot;
76337
+ }
76338
+ /** Resolve on first use and never again — a cgroup does not move under a pid. */
76339
+ resolveOnce() {
76340
+ if (this.resolved) return this.layout;
76341
+ this.resolved = true;
76342
+ try {
76343
+ this.layout = resolveCgroupMemoryLayout(this.fs);
76344
+ } catch (err) {
76345
+ this.layout = null;
76346
+ this.logger.warn("cgroup memory resolution failed \u2014 container memory will be ABSENT", { meta: { error: require_dist10.errMsg(err) } });
76347
+ return null;
76348
+ }
76349
+ if (this.layout === null) {
76350
+ this.logger.info("no cgroup on this node \u2014 container memory is ABSENT, not zero, for every sample", { meta: {
76351
+ platform: process.platform,
76352
+ probed: "/proc/self/cgroup"
76353
+ } });
76354
+ return null;
76355
+ }
76356
+ this.logger.info("container memory resolved from the cgroup", { meta: {
76357
+ source: this.layout.source,
76358
+ directory: this.layout.directory
76359
+ } });
76360
+ return this.layout;
76361
+ }
76362
+ /**
76363
+ * The GPU number, probed once.
76364
+ *
76365
+ * It is host-wide and lives in debugfs, so it is absent far more often than
76366
+ * it is present — inside every camstack container today. One `open` decides
76367
+ * it for the life of the process; after that an unreadable file costs
76368
+ * nothing at all, not even a syscall.
76369
+ */
76370
+ readGpuShmem() {
76371
+ if (!this.wantGpu) return null;
76372
+ if (!this.gpuProbed) {
76373
+ this.gpuProbed = true;
76374
+ const text2 = this.fs.read(I915_GEM_OBJECTS_PATH);
76375
+ this.gpuReadable = text2 !== null && parseI915GemObjects(text2) !== null;
76376
+ this.logger.info(this.gpuReadable ? "i915 GEM accounting is readable \u2014 GPU-backed shmem will be reported" : "i915 GEM accounting is NOT readable \u2014 gpuShmemBytes stays UNKNOWN (this is normal: debugfs is not mounted in the container, and a node with no Intel GPU has no such file)", { meta: { path: I915_GEM_OBJECTS_PATH } });
76377
+ if (!this.gpuReadable) return null;
76378
+ return text2 === null ? null : parseI915GemObjects(text2);
76379
+ }
76380
+ if (!this.gpuReadable) return null;
76381
+ const text = this.fs.read(I915_GEM_OBJECTS_PATH);
76382
+ return text === null ? null : parseI915GemObjects(text);
76383
+ }
76384
+ /** Log a read outcome only when it CHANGES. Never once per tick. */
76385
+ noteRead(ok, reason) {
76386
+ if (this.lastReadOk === ok) return;
76387
+ const first = this.lastReadOk === null;
76388
+ this.lastReadOk = ok;
76389
+ if (!ok) {
76390
+ this.logger.warn("cgroup memory stopped answering \u2014 container memory is ABSENT until it returns", { meta: { reason: reason ?? "unknown" } });
76391
+ return;
76392
+ }
76393
+ if (!first) this.logger.info("cgroup memory is answering again");
76394
+ }
76395
+ };
76396
+ var LoadPartition = class {
76397
+ capacity;
76398
+ slots;
76399
+ head = 0;
76400
+ count = 0;
76401
+ rows = 0;
76402
+ /** Newest retained sample's timestamp — the monotonic gate for `push`. */
76403
+ newestAtMs = null;
76404
+ constructor(capacity) {
76405
+ this.capacity = capacity;
76406
+ this.slots = Array.from({ length: capacity });
76407
+ }
76408
+ /**
76409
+ * Accept a sample.
76410
+ *
76411
+ * A sample at or before the newest one already held is REFUSED. The bus drops
76412
+ * a node's own broadcast echo, but a cross-node redelivery or a replayed
76413
+ * subscription must not be able to double a point — and idempotence here is
76414
+ * what lets every reader above treat the series as a set.
76415
+ *
76416
+ * `accepted` is reported separately from `rowDelta` on purpose: a full ring
76417
+ * that evicts a sample of the same size has a delta of zero and is not a
76418
+ * refusal, and conflating the two would silently stop advancing the write
76419
+ * ordinal on a steady-state cluster.
76420
+ */
76421
+ push(sample) {
76422
+ if (this.newestAtMs !== null && sample.atMs <= this.newestAtMs) return {
76423
+ accepted: false,
76424
+ rowDelta: 0
76425
+ };
76426
+ const evicted = this.count === this.capacity ? this.slots[this.head]?.processes.length ?? 0 : 0;
76427
+ this.slots[this.head] = sample;
76428
+ this.head = (this.head + 1) % this.capacity;
76429
+ if (this.count < this.capacity) this.count++;
76430
+ this.newestAtMs = sample.atMs;
76431
+ const rowDelta = sample.processes.length - evicted;
76432
+ this.rows += rowDelta;
76433
+ return {
76434
+ accepted: true,
76435
+ rowDelta
76436
+ };
76437
+ }
76438
+ /** Drop the oldest sample. Returns the rows reclaimed (0 when empty). */
76439
+ dropOldest() {
76440
+ if (this.count === 0) return 0;
76441
+ const index = (this.head - this.count + this.capacity) % this.capacity;
76442
+ const victim = this.slots[index];
76443
+ this.slots[index] = void 0;
76444
+ this.count--;
76445
+ const reclaimed = victim?.processes.length ?? 0;
76446
+ this.rows -= reclaimed;
76447
+ if (this.count === 0) this.newestAtMs = null;
76448
+ return reclaimed;
76449
+ }
76450
+ /** Oldest-first, optionally only what is strictly newer than `sinceMs`. */
76451
+ list(sinceMs) {
76452
+ const out = [];
76453
+ for (let i = 0; i < this.count; i++) {
76454
+ const index = (this.head - this.count + i + this.capacity) % this.capacity;
76455
+ const sample = this.slots[index];
76456
+ if (sample === void 0) continue;
76457
+ if (sinceMs !== void 0 && sample.atMs <= sinceMs) continue;
76458
+ out.push(sample);
76459
+ }
76460
+ return out;
76461
+ }
76462
+ size() {
76463
+ return this.count;
76464
+ }
76465
+ rowCount() {
76466
+ return this.rows;
76467
+ }
76468
+ oldestAtMs() {
76469
+ if (this.count === 0) return null;
76470
+ const index = (this.head - this.count + this.capacity) % this.capacity;
76471
+ return this.slots[index]?.atMs ?? null;
76472
+ }
76473
+ lastWriteAtMs() {
76474
+ return this.newestAtMs;
76475
+ }
76476
+ };
76477
+ var NodeLoadRing = class {
76478
+ partitions = /* @__PURE__ */ new Map();
76479
+ /**
76480
+ * Node id → write ordinal of its last accepted sample. A monotonic counter,
76481
+ * not a clock: partition eviction must follow the order writes actually
76482
+ * happened, and node clocks disagree.
76483
+ */
76484
+ lastWriteSeq = /* @__PURE__ */ new Map();
76485
+ writeSeq = 0;
76486
+ totalRows = 0;
76487
+ samplesPerNode;
76488
+ maxTotalProcessRows;
76489
+ maxNodes;
76490
+ idleEvictionMs;
76491
+ now;
76492
+ constructor(options = {}) {
76493
+ this.samplesPerNode = options.samplesPerNode ?? 180;
76494
+ this.maxTotalProcessRows = options.maxTotalProcessRows ?? 32e3;
76495
+ this.maxNodes = options.maxNodes ?? 16;
76496
+ this.idleEvictionMs = options.idleEvictionMs ?? 36e5;
76497
+ this.now = options.now ?? Date.now;
76498
+ }
76499
+ /**
76500
+ * Retain one snapshot. `processes` is stored by reference — the payload is
76501
+ * already an immutable arrival off the bus, and copying it would double the
76502
+ * measured cost for nothing.
76503
+ *
76504
+ * Returns whether the sample was ACCEPTED — that is, whether it was new
76505
+ * rather than a replay of a timestamp this node has already delivered.
76506
+ *
76507
+ * The return value is not diagnostics. It is the idempotence gate the
76508
+ * DURABLE tier rides on (`load-series-store.ts`): only an accepted sample is
76509
+ * appended to the table, which is what lets that table carry an `INTEGER`
76510
+ * rowid key instead of a composite unique index over two million rows. The
76511
+ * gate is one monotonic comparison in memory; the index it replaces was
76512
+ * measured at ~40 bytes per row.
76513
+ */
76514
+ record(nodeId, atMs, processes) {
76515
+ if (nodeId.length === 0) return false;
76516
+ this.sweepIdle();
76517
+ let partition = this.partitions.get(nodeId);
76518
+ if (partition === void 0) {
76519
+ partition = new LoadPartition(this.samplesPerNode);
76520
+ this.partitions.set(nodeId, partition);
76521
+ }
76522
+ const outcome = partition.push({
76523
+ atMs,
76524
+ processes
76525
+ });
76526
+ if (!outcome.accepted) {
76527
+ if (partition.size() === 0) this.partitions.delete(nodeId);
76528
+ return false;
76529
+ }
76530
+ this.totalRows += outcome.rowDelta;
76531
+ this.lastWriteSeq.set(nodeId, ++this.writeSeq);
76532
+ if (this.partitions.size > this.maxNodes) this.evictPartitions(this.partitions.size - this.maxNodes, nodeId);
76533
+ this.enforceRowBudget();
76534
+ return true;
76535
+ }
76536
+ /**
76537
+ * Read one node's retained series, oldest-first.
76538
+ *
76539
+ * `sinceMs` is EXCLUSIVE: a caller passes the newest timestamp it already
76540
+ * holds and gets back only what it is missing. That is the whole contract
76541
+ * that lets the admin UI seed from here and then continue live without
76542
+ * doubling a point it already drew.
76543
+ *
76544
+ * A node nobody has recorded answers with an empty series, not an error —
76545
+ * unknown is the truth about a node that has not reported.
76546
+ */
76547
+ read(nodeId, sinceMs) {
76548
+ this.sweepIdle();
76549
+ const partition = this.partitions.get(nodeId);
76550
+ if (partition === void 0) return {
76551
+ nodeId,
76552
+ samples: [],
76553
+ retainedSamples: 0,
76554
+ oldestAtMs: null,
76555
+ capacity: this.samplesPerNode
76556
+ };
76557
+ return {
76558
+ nodeId,
76559
+ samples: partition.list(sinceMs),
76560
+ retainedSamples: partition.size(),
76561
+ oldestAtMs: partition.oldestAtMs(),
76562
+ capacity: this.samplesPerNode
76563
+ };
76564
+ }
76565
+ /** Node ids with a live partition. Observability for the fleet bound. */
76566
+ nodeIds() {
76567
+ return [...this.partitions.keys()];
76568
+ }
76569
+ /** Process rows retained across every partition — the number that IS memory. */
76570
+ rowCount() {
76571
+ return this.totalRows;
76572
+ }
76573
+ /** Samples retained across every partition. */
76574
+ sampleCount() {
76575
+ let total = 0;
76576
+ for (const partition of this.partitions.values()) total += partition.size();
76577
+ return total;
76578
+ }
76579
+ /**
76580
+ * Drop partitions whose newest sample is older than the retention window.
76581
+ *
76582
+ * Lazy, on write and on read — never a timer. A timer would be a new
76583
+ * periodic cost in a subsystem whose entire premise is that it adds none,
76584
+ * and a ring that nobody writes to and nobody reads is not growing either.
76585
+ */
76586
+ sweepIdle() {
76587
+ const cutoff = this.now() - this.idleEvictionMs;
76588
+ for (const [nodeId, partition] of [...this.partitions.entries()]) {
76589
+ const lastWrite = partition.lastWriteAtMs();
76590
+ if (lastWrite !== null && lastWrite > cutoff) continue;
76591
+ this.dropPartition(nodeId);
76592
+ }
76593
+ }
76594
+ /**
76595
+ * Bring the fleet back under the row budget by dropping the OLDEST sample of
76596
+ * the HEAVIEST partition, repeatedly.
76597
+ *
76598
+ * Terminates: every iteration removes one sample from a non-empty partition,
76599
+ * and the population of samples is finite and strictly decreasing.
76600
+ */
76601
+ enforceRowBudget() {
76602
+ while (this.totalRows > this.maxTotalProcessRows) {
76603
+ const victim = this.heaviestPartition();
76604
+ if (victim === null) return;
76605
+ const [nodeId, partition] = victim;
76606
+ this.totalRows -= partition.dropOldest();
76607
+ if (partition.size() === 0) this.dropPartition(nodeId);
76608
+ }
76609
+ }
76610
+ heaviestPartition() {
76611
+ let best = null;
76612
+ for (const entry of this.partitions.entries()) {
76613
+ if (entry[1].size() === 0) continue;
76614
+ if (best === null || entry[1].rowCount() > best[1].rowCount()) best = entry;
76615
+ }
76616
+ return best;
76617
+ }
76618
+ /** Drop `count` whole partitions, least-recently-written first. */
76619
+ evictPartitions(count, protectedNodeId) {
76620
+ const order = [...this.partitions.keys()].filter((nodeId) => nodeId !== protectedNodeId).toSorted((a, b) => (this.lastWriteSeq.get(a) ?? 0) - (this.lastWriteSeq.get(b) ?? 0));
76621
+ let remaining = count;
76622
+ for (const nodeId of order) {
76623
+ if (remaining <= 0) return;
76624
+ this.dropPartition(nodeId);
76625
+ remaining -= 1;
76626
+ }
76627
+ }
76628
+ /** Remove a partition and everything that indexes it. */
76629
+ dropPartition(nodeId) {
76630
+ const partition = this.partitions.get(nodeId);
76631
+ if (partition === void 0) return;
76632
+ this.totalRows -= partition.rowCount();
76633
+ this.partitions.delete(nodeId);
76634
+ this.lastWriteSeq.delete(nodeId);
76635
+ }
76636
+ };
76637
+ var CONTAINER_MEMORY_COLLECTION = "metrics:node-memory-samples";
76638
+ var CONTAINER_MEMORY_MAX_TOTAL_ROWS = 2880;
76639
+ var CONTAINER_MEMORY_MAX_ROWS = 5e4;
76640
+ var CONTAINER_MEMORY_COLUMNS = [
76641
+ {
76642
+ name: "id",
76643
+ type: "INTEGER",
76644
+ primaryKey: true,
76645
+ notNull: true
76646
+ },
76647
+ {
76648
+ name: "nodeId",
76649
+ type: "TEXT",
76650
+ notNull: true
76651
+ },
76652
+ /** Shared with the process rows of the same tick — that is the whole point. */
76653
+ {
76654
+ name: "atMs",
76655
+ type: "INTEGER",
76656
+ notNull: true
76657
+ },
76658
+ {
76659
+ name: "source",
76660
+ type: "TEXT",
76661
+ notNull: true
76662
+ },
76663
+ /**
76664
+ * Whole MiB, like `rssMib` on the process row and for the same reason: a
76665
+ * byte-exact residency on a value in the tens of gigabytes is precision no
76666
+ * chart can draw, and it costs bytes on every row forever.
76667
+ */
76668
+ {
76669
+ name: "currentMib",
76670
+ type: "INTEGER",
76671
+ notNull: true
76672
+ },
76673
+ /** `NULL` = NO LIMIT. Never a sentinel. */
76674
+ {
76675
+ name: "limitMib",
76676
+ type: "INTEGER"
76677
+ },
76678
+ /** `NULL` = UNKNOWN on all four. Never zero. */
76679
+ {
76680
+ name: "anonMib",
76681
+ type: "INTEGER"
76682
+ },
76683
+ {
76684
+ name: "fileMib",
76685
+ type: "INTEGER"
76686
+ },
76687
+ {
76688
+ name: "shmemMib",
76689
+ type: "INTEGER"
76690
+ },
76691
+ {
76692
+ name: "slabMib",
76693
+ type: "INTEGER"
76694
+ },
76695
+ /** Host-wide i915 GEM bytes. `NULL` wherever debugfs is not mounted. */
76696
+ {
76697
+ name: "gpuShmemMib",
76698
+ type: "INTEGER"
76699
+ }
76700
+ ];
76701
+ var CONTAINER_MEMORY_INDEXES = [
76702
+ /** The one index, and it serves both the read and the prune — see the
76703
+ * process table's `idx_load_samples_node_at` for the full reasoning. */
76704
+ {
76705
+ name: "idx_node_memory_node_at",
76706
+ columns: ["nodeId", "atMs"]
76707
+ }
76708
+ ];
76709
+ var BYTES_PER_MIB$1 = 1048576;
76710
+ function toMib(value) {
76711
+ return value === null ? null : Math.round(value / BYTES_PER_MIB$1);
76712
+ }
76713
+ function fromMib(value) {
76714
+ return value === null ? null : value * BYTES_PER_MIB$1;
76715
+ }
76716
+ function snapshotToRow(nodeId, atMs, snapshot) {
76717
+ return {
76718
+ nodeId,
76719
+ atMs,
76720
+ source: snapshot.source,
76721
+ currentMib: Math.round(snapshot.currentBytes / BYTES_PER_MIB$1),
76722
+ limitMib: toMib(snapshot.limitBytes),
76723
+ anonMib: toMib(snapshot.anonBytes),
76724
+ fileMib: toMib(snapshot.fileBytes),
76725
+ shmemMib: toMib(snapshot.shmemBytes),
76726
+ slabMib: toMib(snapshot.slabBytes),
76727
+ gpuShmemMib: toMib(snapshot.gpuShmemBytes)
76728
+ };
76729
+ }
76730
+ function rowToPoint(row) {
76731
+ if (row.source !== "cgroup-v2" && row.source !== "cgroup-v1") return null;
76732
+ return {
76733
+ atMs: row.atMs,
76734
+ source: row.source,
76735
+ currentBytes: row.currentMib * BYTES_PER_MIB$1,
76736
+ limitBytes: fromMib(row.limitMib),
76737
+ anonBytes: fromMib(row.anonMib),
76738
+ fileBytes: fromMib(row.fileMib),
76739
+ shmemBytes: fromMib(row.shmemMib),
76740
+ slabBytes: fromMib(row.slabMib),
76741
+ gpuShmemBytes: fromMib(row.gpuShmemMib)
76742
+ };
76743
+ }
76744
+ function recordToMemoryRow(data) {
76745
+ const nodeId = data["nodeId"];
76746
+ const source = data["source"];
76747
+ if (typeof nodeId !== "string" || typeof source !== "string") return null;
76748
+ const atMs = Number(data["atMs"]);
76749
+ const currentMib = Number(data["currentMib"]);
76750
+ if (!Number.isFinite(atMs) || !Number.isFinite(currentMib)) return null;
76751
+ const optional = (raw) => {
76752
+ if (raw === null || raw === void 0) return null;
76753
+ const n = Number(raw);
76754
+ return Number.isFinite(n) ? n : null;
76755
+ };
76756
+ return {
76757
+ nodeId,
76758
+ atMs,
76759
+ source,
76760
+ currentMib,
76761
+ limitMib: optional(data["limitMib"]),
76762
+ anonMib: optional(data["anonMib"]),
76763
+ fileMib: optional(data["fileMib"]),
76764
+ shmemMib: optional(data["shmemMib"]),
76765
+ slabMib: optional(data["slabMib"]),
76766
+ gpuShmemMib: optional(data["gpuShmemMib"])
76767
+ };
76768
+ }
76769
+ var ContainerMemoryRing = class {
76770
+ ring;
76771
+ constructor(options = {}) {
76772
+ this.ring = new NodeLoadRing({
76773
+ samplesPerNode: options.samplesPerNode ?? 180,
76774
+ maxTotalProcessRows: CONTAINER_MEMORY_MAX_TOTAL_ROWS,
76775
+ ...options.now !== void 0 ? { now: options.now } : {}
76776
+ });
76777
+ }
76778
+ /** Retain one reading. `false` = a timestamp this node already delivered. */
76779
+ record(nodeId, atMs, snapshot) {
76780
+ return this.ring.record(nodeId, atMs, [snapshot]);
76781
+ }
76782
+ /**
76783
+ * Read one node's retained readings, oldest-first. `sinceMs` is EXCLUSIVE,
76784
+ * matching every other reader in this subsystem.
76785
+ *
76786
+ * A node nobody has recorded answers EMPTY — which is what "this node has no
76787
+ * cgroup" looks like, and it must stay distinguishable from a zero.
76788
+ */
76789
+ read(nodeId, sinceMs) {
76790
+ const series = this.ring.read(nodeId, sinceMs);
76791
+ const out = [];
76792
+ for (const sample of series.samples) {
76793
+ const snapshot = sample.processes[0];
76794
+ if (snapshot === void 0) continue;
76795
+ out.push({
76796
+ ...snapshot,
76797
+ atMs: sample.atMs
76798
+ });
76799
+ }
76800
+ return out;
76801
+ }
76802
+ };
76803
+ function reduceContainerMemory(points, bucketMs, origin) {
76804
+ if (bucketMs <= 0 || points.length === 0) return points;
76805
+ const buckets = /* @__PURE__ */ new Map();
76806
+ for (const point of points) {
76807
+ const start = origin + Math.floor((point.atMs - origin) / bucketMs) * bucketMs;
76808
+ const held = buckets.get(start);
76809
+ if (held === void 0 || point.currentBytes > held.currentBytes) buckets.set(start, {
76810
+ ...point,
76811
+ atMs: start
76812
+ });
76813
+ }
76814
+ return [...buckets.values()].toSorted((a, b) => a.atMs - b.atMs);
76815
+ }
76816
+ function mergeContainerMemory(cold, hot) {
76817
+ const byAt = /* @__PURE__ */ new Map();
76818
+ for (const point of cold) byAt.set(point.atMs, point);
76819
+ for (const point of hot) byAt.set(point.atMs, point);
76820
+ return [...byAt.values()].toSorted((a, b) => a.atMs - b.atMs);
76821
+ }
76822
+ var ContainerMemoryStore = class {
76823
+ declared = false;
76824
+ store;
76825
+ logger;
76826
+ nowFn;
76827
+ prunePageRows;
76828
+ constructor(deps) {
76829
+ this.store = deps.store;
76830
+ this.logger = deps.logger;
76831
+ this.nowFn = deps.now ?? (() => Date.now());
76832
+ this.prunePageRows = deps.prunePageRows ?? 2e4;
76833
+ }
76834
+ /** Idempotently declare the collection. `false` when the store refused. */
76835
+ async declare() {
76836
+ if (this.declared) return true;
76837
+ try {
76838
+ await this.store.declareCollection.mutate({
76839
+ collection: CONTAINER_MEMORY_COLLECTION,
76840
+ columns: [...CONTAINER_MEMORY_COLUMNS],
76841
+ indexes: CONTAINER_MEMORY_INDEXES.map((i) => ({
76842
+ name: i.name,
76843
+ columns: [...i.columns]
76844
+ }))
76845
+ });
76846
+ this.declared = true;
76847
+ return true;
76848
+ } catch (err) {
76849
+ this.logger.warn("container memory declareCollection failed \u2014 nothing will be retained on disk", { meta: {
76850
+ collection: CONTAINER_MEMORY_COLLECTION,
76851
+ error: require_dist10.errMsg(err)
76852
+ } });
76853
+ return false;
76854
+ }
76855
+ }
76856
+ /** Append ONE reading. */
76857
+ async append(nodeId, atMs, snapshot) {
76858
+ if (!await this.declare()) return 0;
76859
+ const record = { ...snapshotToRow(nodeId, atMs, snapshot) };
76860
+ try {
76861
+ const { inserted } = await this.store.insertMany.mutate({
76862
+ collection: CONTAINER_MEMORY_COLLECTION,
76863
+ records: [{ data: record }]
76864
+ });
76865
+ return inserted;
76866
+ } catch (err) {
76867
+ this.logger.warn("container memory reading not retained \u2014 this interval will be missing", { meta: {
76868
+ nodeId,
76869
+ atMs,
76870
+ error: require_dist10.errMsg(err)
76871
+ } });
76872
+ return 0;
76873
+ }
76874
+ }
76875
+ /** Read one node's cold readings, oldest-first. `sinceMs` is EXCLUSIVE. */
76876
+ async read(nodeId, sinceMs, limitRows) {
76877
+ if (!await this.declare()) return [];
76878
+ try {
76879
+ const records = await this.store.query.query({
76880
+ collection: CONTAINER_MEMORY_COLLECTION,
76881
+ filter: {
76882
+ where: { nodeId },
76883
+ whereBetween: { atMs: [sinceMs + 1, Number.MAX_SAFE_INTEGER] },
76884
+ orderBy: {
76885
+ field: "atMs",
76886
+ direction: "asc"
76887
+ },
76888
+ limit: limitRows
76889
+ }
76890
+ });
76891
+ const points = [];
76892
+ for (const record of records) {
76893
+ const row = recordToMemoryRow(record.data);
76894
+ if (row === null) continue;
76895
+ const point = rowToPoint(row);
76896
+ if (point !== null) points.push(point);
76897
+ }
76898
+ return points;
76899
+ } catch (err) {
76900
+ this.logger.warn("container memory cold read failed \u2014 answering from the hot window only", { meta: {
76901
+ nodeId,
76902
+ sinceMs,
76903
+ error: require_dist10.errMsg(err)
76904
+ } });
76905
+ return [];
76906
+ }
76907
+ }
76908
+ /**
76909
+ * Enforce BOTH bounds, oldest-first, through a bounded page each.
76910
+ *
76911
+ * Not rate-limited here: it is driven by the process store's own prune
76912
+ * cadence, so it runs at most once a minute by construction and a second
76913
+ * timer would be a second thing to get wrong.
76914
+ */
76915
+ async prune(nodeIds, retention) {
76916
+ if (!await this.declare()) return null;
76917
+ const ageCutoff = this.nowFn() - retention.retentionHours * 36e5;
76918
+ let deletedByAge = 0;
76919
+ let rowsExamined = 0;
76920
+ for (const nodeId of nodeIds) {
76921
+ const outcome = await this.deleteOldestPage(nodeId, ageCutoff, this.prunePageRows);
76922
+ deletedByAge += outcome.deleted;
76923
+ rowsExamined += outcome.examined;
76924
+ }
76925
+ const total = await this.count();
76926
+ const excess = total === null ? 0 : total - CONTAINER_MEMORY_MAX_ROWS;
76927
+ let deletedByCap = 0;
76928
+ if (excess > 0) {
76929
+ let remaining = Math.min(excess, this.prunePageRows);
76930
+ for (const nodeId of nodeIds) {
76931
+ if (remaining <= 0) break;
76932
+ const outcome = await this.deleteOldestPage(nodeId, null, remaining);
76933
+ deletedByCap += outcome.deleted;
76934
+ remaining -= outcome.deleted;
76935
+ rowsExamined += outcome.examined;
76936
+ }
76937
+ this.logger.warn("container memory ROW CAP bit \u2014 evicting the oldest readings", { meta: {
76938
+ collection: CONTAINER_MEMORY_COLLECTION,
76939
+ rows: total,
76940
+ cap: CONTAINER_MEMORY_MAX_ROWS,
76941
+ over: excess,
76942
+ deleted: deletedByCap,
76943
+ hint: "lower the retention or the sampling cadence \u2014 the cap is the guarantee, not the intention"
76944
+ } });
76945
+ }
76946
+ return {
76947
+ deletedByAge,
76948
+ deletedByCap,
76949
+ rowsExamined,
76950
+ capBit: excess > 0
76951
+ };
76952
+ }
76953
+ /** Total rows, or `null` when the store could not answer. */
76954
+ async count() {
76955
+ if (!await this.declare()) return null;
76956
+ try {
76957
+ return await this.store.count.query({ collection: CONTAINER_MEMORY_COLLECTION });
76958
+ } catch (err) {
76959
+ this.logger.warn("container memory count failed \u2014 the row cap is not enforced this pass", { meta: { error: require_dist10.errMsg(err) } });
76960
+ return null;
76961
+ }
76962
+ }
76963
+ /**
76964
+ * Delete this node's oldest rows, at most one page's worth.
76965
+ *
76966
+ * `cutoff === null` means "the oldest rows, whatever their age" — the
76967
+ * row-cap path, which is bounded by `limitRows` instead. Either way the page
76968
+ * is READ FIRST, ordered by the one index, and its last `atMs` becomes the
76969
+ * effective cutoff for a single bounded delete. An unbounded `deleteWhere`
76970
+ * would be one statement and one stalling transaction the first time a
76971
+ * retention is lowered.
76972
+ */
76973
+ async deleteOldestPage(nodeId, cutoff, limitRows) {
76974
+ if (limitRows <= 0) return {
76975
+ deleted: 0,
76976
+ examined: 0
76977
+ };
76978
+ try {
76979
+ const page = await this.store.query.query({
76980
+ collection: CONTAINER_MEMORY_COLLECTION,
76981
+ filter: {
76982
+ where: { nodeId },
76983
+ ...cutoff === null ? {} : { whereBetween: { atMs: [0, cutoff] } },
76984
+ orderBy: {
76985
+ field: "atMs",
76986
+ direction: "asc"
76987
+ },
76988
+ limit: Math.min(limitRows, this.prunePageRows)
76989
+ },
76990
+ columns: ["atMs"]
76991
+ });
76992
+ if (page.length === 0) return {
76993
+ deleted: 0,
76994
+ examined: 0
76995
+ };
76996
+ const effectiveCutoff = Number(page.at(-1)?.data["atMs"]);
76997
+ if (!Number.isFinite(effectiveCutoff)) return {
76998
+ deleted: 0,
76999
+ examined: page.length
77000
+ };
77001
+ const { deleted } = await this.store.deleteWhere.mutate({
77002
+ collection: CONTAINER_MEMORY_COLLECTION,
77003
+ filter: {
77004
+ where: { nodeId },
77005
+ whereBetween: { atMs: [0, effectiveCutoff] }
77006
+ }
77007
+ });
77008
+ return {
77009
+ deleted,
77010
+ examined: page.length
77011
+ };
77012
+ } catch (err) {
77013
+ this.logger.warn("container memory prune failed \u2014 the table keeps growing this pass", { meta: {
77014
+ nodeId,
77015
+ cutoff,
77016
+ error: require_dist10.errMsg(err)
77017
+ } });
77018
+ return {
77019
+ deleted: 0,
77020
+ examined: 0
77021
+ };
77022
+ }
77023
+ }
77024
+ };
76133
77025
  var IS_DARWIN = node_os.platform() === "darwin";
76134
77026
  var IS_LINUX = node_os.platform() === "linux";
76135
77027
  var NativeMetricsProvider = class {
@@ -76594,247 +77486,6 @@ var require_native_metrics_addon = __commonJS({
76594
77486
  });
76595
77487
  });
76596
77488
  }
76597
- var LoadPartition = class {
76598
- capacity;
76599
- slots;
76600
- head = 0;
76601
- count = 0;
76602
- rows = 0;
76603
- /** Newest retained sample's timestamp — the monotonic gate for `push`. */
76604
- newestAtMs = null;
76605
- constructor(capacity) {
76606
- this.capacity = capacity;
76607
- this.slots = Array.from({ length: capacity });
76608
- }
76609
- /**
76610
- * Accept a sample.
76611
- *
76612
- * A sample at or before the newest one already held is REFUSED. The bus drops
76613
- * a node's own broadcast echo, but a cross-node redelivery or a replayed
76614
- * subscription must not be able to double a point — and idempotence here is
76615
- * what lets every reader above treat the series as a set.
76616
- *
76617
- * `accepted` is reported separately from `rowDelta` on purpose: a full ring
76618
- * that evicts a sample of the same size has a delta of zero and is not a
76619
- * refusal, and conflating the two would silently stop advancing the write
76620
- * ordinal on a steady-state cluster.
76621
- */
76622
- push(sample) {
76623
- if (this.newestAtMs !== null && sample.atMs <= this.newestAtMs) return {
76624
- accepted: false,
76625
- rowDelta: 0
76626
- };
76627
- const evicted = this.count === this.capacity ? this.slots[this.head]?.processes.length ?? 0 : 0;
76628
- this.slots[this.head] = sample;
76629
- this.head = (this.head + 1) % this.capacity;
76630
- if (this.count < this.capacity) this.count++;
76631
- this.newestAtMs = sample.atMs;
76632
- const rowDelta = sample.processes.length - evicted;
76633
- this.rows += rowDelta;
76634
- return {
76635
- accepted: true,
76636
- rowDelta
76637
- };
76638
- }
76639
- /** Drop the oldest sample. Returns the rows reclaimed (0 when empty). */
76640
- dropOldest() {
76641
- if (this.count === 0) return 0;
76642
- const index = (this.head - this.count + this.capacity) % this.capacity;
76643
- const victim = this.slots[index];
76644
- this.slots[index] = void 0;
76645
- this.count--;
76646
- const reclaimed = victim?.processes.length ?? 0;
76647
- this.rows -= reclaimed;
76648
- if (this.count === 0) this.newestAtMs = null;
76649
- return reclaimed;
76650
- }
76651
- /** Oldest-first, optionally only what is strictly newer than `sinceMs`. */
76652
- list(sinceMs) {
76653
- const out = [];
76654
- for (let i = 0; i < this.count; i++) {
76655
- const index = (this.head - this.count + i + this.capacity) % this.capacity;
76656
- const sample = this.slots[index];
76657
- if (sample === void 0) continue;
76658
- if (sinceMs !== void 0 && sample.atMs <= sinceMs) continue;
76659
- out.push(sample);
76660
- }
76661
- return out;
76662
- }
76663
- size() {
76664
- return this.count;
76665
- }
76666
- rowCount() {
76667
- return this.rows;
76668
- }
76669
- oldestAtMs() {
76670
- if (this.count === 0) return null;
76671
- const index = (this.head - this.count + this.capacity) % this.capacity;
76672
- return this.slots[index]?.atMs ?? null;
76673
- }
76674
- lastWriteAtMs() {
76675
- return this.newestAtMs;
76676
- }
76677
- };
76678
- var NodeLoadRing = class {
76679
- partitions = /* @__PURE__ */ new Map();
76680
- /**
76681
- * Node id → write ordinal of its last accepted sample. A monotonic counter,
76682
- * not a clock: partition eviction must follow the order writes actually
76683
- * happened, and node clocks disagree.
76684
- */
76685
- lastWriteSeq = /* @__PURE__ */ new Map();
76686
- writeSeq = 0;
76687
- totalRows = 0;
76688
- samplesPerNode;
76689
- maxTotalProcessRows;
76690
- maxNodes;
76691
- idleEvictionMs;
76692
- now;
76693
- constructor(options = {}) {
76694
- this.samplesPerNode = options.samplesPerNode ?? 180;
76695
- this.maxTotalProcessRows = options.maxTotalProcessRows ?? 32e3;
76696
- this.maxNodes = options.maxNodes ?? 16;
76697
- this.idleEvictionMs = options.idleEvictionMs ?? 36e5;
76698
- this.now = options.now ?? Date.now;
76699
- }
76700
- /**
76701
- * Retain one snapshot. `processes` is stored by reference — the payload is
76702
- * already an immutable arrival off the bus, and copying it would double the
76703
- * measured cost for nothing.
76704
- *
76705
- * Returns whether the sample was ACCEPTED — that is, whether it was new
76706
- * rather than a replay of a timestamp this node has already delivered.
76707
- *
76708
- * The return value is not diagnostics. It is the idempotence gate the
76709
- * DURABLE tier rides on (`load-series-store.ts`): only an accepted sample is
76710
- * appended to the table, which is what lets that table carry an `INTEGER`
76711
- * rowid key instead of a composite unique index over two million rows. The
76712
- * gate is one monotonic comparison in memory; the index it replaces was
76713
- * measured at ~40 bytes per row.
76714
- */
76715
- record(nodeId, atMs, processes) {
76716
- if (nodeId.length === 0) return false;
76717
- this.sweepIdle();
76718
- let partition = this.partitions.get(nodeId);
76719
- if (partition === void 0) {
76720
- partition = new LoadPartition(this.samplesPerNode);
76721
- this.partitions.set(nodeId, partition);
76722
- }
76723
- const outcome = partition.push({
76724
- atMs,
76725
- processes
76726
- });
76727
- if (!outcome.accepted) {
76728
- if (partition.size() === 0) this.partitions.delete(nodeId);
76729
- return false;
76730
- }
76731
- this.totalRows += outcome.rowDelta;
76732
- this.lastWriteSeq.set(nodeId, ++this.writeSeq);
76733
- if (this.partitions.size > this.maxNodes) this.evictPartitions(this.partitions.size - this.maxNodes, nodeId);
76734
- this.enforceRowBudget();
76735
- return true;
76736
- }
76737
- /**
76738
- * Read one node's retained series, oldest-first.
76739
- *
76740
- * `sinceMs` is EXCLUSIVE: a caller passes the newest timestamp it already
76741
- * holds and gets back only what it is missing. That is the whole contract
76742
- * that lets the admin UI seed from here and then continue live without
76743
- * doubling a point it already drew.
76744
- *
76745
- * A node nobody has recorded answers with an empty series, not an error —
76746
- * unknown is the truth about a node that has not reported.
76747
- */
76748
- read(nodeId, sinceMs) {
76749
- this.sweepIdle();
76750
- const partition = this.partitions.get(nodeId);
76751
- if (partition === void 0) return {
76752
- nodeId,
76753
- samples: [],
76754
- retainedSamples: 0,
76755
- oldestAtMs: null,
76756
- capacity: this.samplesPerNode
76757
- };
76758
- return {
76759
- nodeId,
76760
- samples: partition.list(sinceMs),
76761
- retainedSamples: partition.size(),
76762
- oldestAtMs: partition.oldestAtMs(),
76763
- capacity: this.samplesPerNode
76764
- };
76765
- }
76766
- /** Node ids with a live partition. Observability for the fleet bound. */
76767
- nodeIds() {
76768
- return [...this.partitions.keys()];
76769
- }
76770
- /** Process rows retained across every partition — the number that IS memory. */
76771
- rowCount() {
76772
- return this.totalRows;
76773
- }
76774
- /** Samples retained across every partition. */
76775
- sampleCount() {
76776
- let total = 0;
76777
- for (const partition of this.partitions.values()) total += partition.size();
76778
- return total;
76779
- }
76780
- /**
76781
- * Drop partitions whose newest sample is older than the retention window.
76782
- *
76783
- * Lazy, on write and on read — never a timer. A timer would be a new
76784
- * periodic cost in a subsystem whose entire premise is that it adds none,
76785
- * and a ring that nobody writes to and nobody reads is not growing either.
76786
- */
76787
- sweepIdle() {
76788
- const cutoff = this.now() - this.idleEvictionMs;
76789
- for (const [nodeId, partition] of [...this.partitions.entries()]) {
76790
- const lastWrite = partition.lastWriteAtMs();
76791
- if (lastWrite !== null && lastWrite > cutoff) continue;
76792
- this.dropPartition(nodeId);
76793
- }
76794
- }
76795
- /**
76796
- * Bring the fleet back under the row budget by dropping the OLDEST sample of
76797
- * the HEAVIEST partition, repeatedly.
76798
- *
76799
- * Terminates: every iteration removes one sample from a non-empty partition,
76800
- * and the population of samples is finite and strictly decreasing.
76801
- */
76802
- enforceRowBudget() {
76803
- while (this.totalRows > this.maxTotalProcessRows) {
76804
- const victim = this.heaviestPartition();
76805
- if (victim === null) return;
76806
- const [nodeId, partition] = victim;
76807
- this.totalRows -= partition.dropOldest();
76808
- if (partition.size() === 0) this.dropPartition(nodeId);
76809
- }
76810
- }
76811
- heaviestPartition() {
76812
- let best = null;
76813
- for (const entry of this.partitions.entries()) {
76814
- if (entry[1].size() === 0) continue;
76815
- if (best === null || entry[1].rowCount() > best[1].rowCount()) best = entry;
76816
- }
76817
- return best;
76818
- }
76819
- /** Drop `count` whole partitions, least-recently-written first. */
76820
- evictPartitions(count, protectedNodeId) {
76821
- const order = [...this.partitions.keys()].filter((nodeId) => nodeId !== protectedNodeId).toSorted((a, b) => (this.lastWriteSeq.get(a) ?? 0) - (this.lastWriteSeq.get(b) ?? 0));
76822
- let remaining = count;
76823
- for (const nodeId of order) {
76824
- if (remaining <= 0) return;
76825
- this.dropPartition(nodeId);
76826
- remaining -= 1;
76827
- }
76828
- }
76829
- /** Remove a partition and everything that indexes it. */
76830
- dropPartition(nodeId) {
76831
- const partition = this.partitions.get(nodeId);
76832
- if (partition === void 0) return;
76833
- this.totalRows -= partition.rowCount();
76834
- this.partitions.delete(nodeId);
76835
- this.lastWriteSeq.delete(nodeId);
76836
- }
76837
- };
76838
77489
  var LOAD_SERIES_COLLECTION = "metrics:node-load-samples";
76839
77490
  var DEFAULT_MAX_ROWS = 5e5;
76840
77491
  var LOAD_SERIES_COLUMNS = [
@@ -77545,6 +78196,20 @@ var require_native_metrics_addon = __commonJS({
77545
78196
  * per sample, to reach that very same table.
77546
78197
  */
77547
78198
  loadStore = null;
78199
+ /**
78200
+ * The container's OWN memory, sampled on the process-snapshot tick.
78201
+ *
78202
+ * It rides that tick rather than a timer of its own so a memory reading and
78203
+ * the process rows it must be compared against carry the SAME timestamp —
78204
+ * the whole question is a subtraction, and two clocks make it an estimate.
78205
+ * Constructed in `onInitialize`, where the logger exists; it resolves the
78206
+ * cgroup once and latches.
78207
+ */
78208
+ memoryProbe = null;
78209
+ /** Hot tier for the container reading. Bounds in `container-memory-series.ts`. */
78210
+ memoryRing = new ContainerMemoryRing();
78211
+ /** Cold tier, HUB only — same reasoning as `loadStore`. */
78212
+ memoryStore = null;
77548
78213
  /** The resolved knobs. Re-resolved on every settings write. */
77549
78214
  loadConfig = {
77550
78215
  cadenceSec: 10,
@@ -77617,13 +78282,20 @@ var require_native_metrics_addon = __commonJS({
77617
78282
  dumpHeapSnapshot: (params) => this.dumpHeapSnapshot(params)
77618
78283
  };
77619
78284
  this.applyLoadSeriesConfig();
77620
- if (this.isHub) this.loadStore = new LoadSeriesStore({
77621
- store: this.ctx.api.settingsStore,
77622
- logger: this.ctx.logger.child("LoadSeries")
77623
- });
78285
+ if (this.isHub) {
78286
+ this.loadStore = new LoadSeriesStore({
78287
+ store: this.ctx.api.settingsStore,
78288
+ logger: this.ctx.logger.child("LoadSeries")
78289
+ });
78290
+ this.memoryStore = new ContainerMemoryStore({
78291
+ store: this.ctx.api.settingsStore,
78292
+ logger: this.ctx.logger.child("ContainerMemory")
78293
+ });
78294
+ }
78295
+ this.memoryProbe = new ContainerMemoryProbe({ logger: this.ctx.logger.child("ContainerMemory") });
77624
78296
  this.snapshotTimer = setInterval(() => this.emitResourcesSnapshot(), METRICS_SNAPSHOT_INTERVAL_MS);
77625
78297
  this.startProcessSnapshotTimer();
77626
- this.ctx.addDisposer(this.ctx.eventBus.subscribe({ category: require_dist10.EventCategory.MetricsNodeProcessesSnapshot }, (event) => this.retainSnapshot(event.data.nodeId, event.data.timestamp, event.data.processes)));
78298
+ this.ctx.addDisposer(this.ctx.eventBus.subscribe({ category: require_dist10.EventCategory.MetricsNodeProcessesSnapshot }, (event) => this.retainSnapshot(event.data.nodeId, event.data.timestamp, event.data.processes, event.data.containerMemory)));
77627
78299
  return [{
77628
78300
  capability: require_dist10.metricsProviderCapability,
77629
78301
  provider: composed
@@ -77641,19 +78313,31 @@ var require_native_metrics_addon = __commonJS({
77641
78313
  * The durable append is fire-and-forget: a storage stall must cost a gap in
77642
78314
  * the cold window, never a blocked event-bus handler. Every failure logs.
77643
78315
  */
77644
- retainSnapshot(nodeId, atMs, processes) {
78316
+ retainSnapshot(nodeId, atMs, processes, containerMemory) {
77645
78317
  const retained = processes.map((p) => NativeMetricsAddon2.toRetained(p));
77646
78318
  if (!this.loadRing.record(nodeId, atMs, retained)) return;
77647
78319
  this.observedRowsByNode.set(nodeId, retained.length);
78320
+ if (containerMemory !== null) this.memoryRing.record(nodeId, atMs, containerMemory);
77648
78321
  const store = this.loadStore;
77649
78322
  if (store === null) return;
77650
- store.append(nodeId, atMs, retained).then(() => store.prune([...this.observedRowsByNode.keys()], this.loadConfig)).catch((err) => {
78323
+ store.append(nodeId, atMs, retained).then(() => store.prune([...this.observedRowsByNode.keys()], this.loadConfig)).then(async (outcome) => {
78324
+ if (outcome === null) return;
78325
+ await this.memoryStore?.prune([...this.observedRowsByNode.keys()], this.loadConfig);
78326
+ }).catch((err) => {
77651
78327
  this.ctx.logger.warn("durable load series write failed", { meta: {
77652
78328
  nodeId,
77653
78329
  atMs,
77654
78330
  error: err instanceof Error ? err.message : String(err)
77655
78331
  } });
77656
78332
  });
78333
+ if (containerMemory === null) return;
78334
+ this.memoryStore?.append(nodeId, atMs, containerMemory).catch((err) => {
78335
+ this.ctx.logger.warn("durable container memory write failed", { meta: {
78336
+ nodeId,
78337
+ atMs,
78338
+ error: err instanceof Error ? err.message : String(err)
78339
+ } });
78340
+ });
77657
78341
  }
77658
78342
  /**
77659
78343
  * The ONE reader, over both tiers.
@@ -77682,6 +78366,14 @@ var require_native_metrics_addon = __commonJS({
77682
78366
  return {
77683
78367
  nodeId: params.forNodeId,
77684
78368
  series: merged.series,
78369
+ containerMemory: await this.readContainerMemory({
78370
+ forNodeId: params.forNodeId,
78371
+ sinceMs,
78372
+ ...params.sinceMs !== void 0 ? { exclusiveSinceMs: params.sinceMs } : {},
78373
+ bucketMs: merged.bucketMs,
78374
+ cadenceMs,
78375
+ origin: merged.oldestAtMs
78376
+ }),
77685
78377
  bucketMs: merged.bucketMs,
77686
78378
  retainedSamples: merged.retainedSamples,
77687
78379
  oldestAtMs: merged.oldestAtMs,
@@ -77690,6 +78382,24 @@ var require_native_metrics_addon = __commonJS({
77690
78382
  };
77691
78383
  }
77692
78384
  /**
78385
+ * The container's memory over the same window, on the same x-axis.
78386
+ *
78387
+ * Both tiers, merged and deduped on `atMs` like the process series, then
78388
+ * reduced against the SAME `bucketMs` and the SAME origin — otherwise the
78389
+ * two series in one answer would sit on two different axes and the
78390
+ * subtraction they exist for would be an eyeball.
78391
+ *
78392
+ * An EMPTY answer means the node has no cgroup, or nothing has been retained
78393
+ * for it yet. It never means zero.
78394
+ */
78395
+ async readContainerMemory(params) {
78396
+ const hot = this.memoryRing.read(params.forNodeId, params.exclusiveSinceMs);
78397
+ const store = this.memoryStore;
78398
+ const merged = mergeContainerMemory(store === null ? [] : await store.read(params.forNodeId, params.sinceMs, CONTAINER_MEMORY_MAX_ROWS), hot);
78399
+ if (params.bucketMs <= params.cadenceMs) return merged;
78400
+ return reduceContainerMemory(merged, params.bucketMs, params.origin ?? 0);
78401
+ }
78402
+ /**
77693
78403
  * Re-resolve the knobs and restate the fixed cadence.
77694
78404
  *
77695
78405
  * A REFUSED value (out of 5-60 s, or out of 1-72 h) leaves the previous
@@ -77806,6 +78516,7 @@ var require_native_metrics_addon = __commonJS({
77806
78516
  const timestamp = Date.now();
77807
78517
  try {
77808
78518
  const processes = await this.listNodeProcesses();
78519
+ const containerMemory = this.memoryProbe?.sample() ?? null;
77809
78520
  eventBus.emit(require_dist10.createEvent(require_dist10.EventCategory.MetricsNodeProcessesSnapshot, {
77810
78521
  type: "node",
77811
78522
  id: nodeId,
@@ -77813,7 +78524,8 @@ var require_native_metrics_addon = __commonJS({
77813
78524
  }, {
77814
78525
  nodeId,
77815
78526
  processes,
77816
- timestamp
78527
+ timestamp,
78528
+ containerMemory
77817
78529
  }));
77818
78530
  } catch (err) {
77819
78531
  this.ctx.logger.warn("process snapshot skipped \u2014 this interval will be missing", { meta: {
@@ -78079,14 +78791,14 @@ var require_filesystem_storage_addon = __commonJS({
78079
78791
  [Symbol.toStringTag]: { value: "Module" }
78080
78792
  });
78081
78793
  var require_chunk = require_chunk_Cek0wNdY();
78082
- var require_dist10 = require_dist_CxgTulEG();
78794
+ var require_dist10 = require_dist_C7uZ_fOb();
78083
78795
  var node_crypto = __require("crypto");
78084
78796
  var node_fs_promises = __require("fs/promises");
78085
78797
  var node_path = __require("path");
78086
78798
  node_path = require_chunk.__toESM(node_path);
78087
- var node_os = __require("os");
78088
78799
  var node_fs = __require("fs");
78089
78800
  node_fs = require_chunk.__toESM(node_fs);
78801
+ var node_os = __require("os");
78090
78802
  function isWithinAllowedRoots(candidate, allowedRoots) {
78091
78803
  const norm = (0, node_path.resolve)(candidate);
78092
78804
  return allowedRoots.some((root) => {
@@ -79195,8 +79907,8 @@ var require_sqlite_settings_addon = __commonJS({
79195
79907
  [Symbol.toStringTag]: { value: "Module" }
79196
79908
  });
79197
79909
  var require_chunk = require_chunk_Cek0wNdY();
79198
- var require_dist10 = require_dist_CxgTulEG();
79199
- var require_retired_settings_keys = require_retired_settings_keys_BbjWjol0();
79910
+ var require_dist10 = require_dist_C7uZ_fOb();
79911
+ var require_retired_settings_keys = require_retired_settings_keys_CBU4_TJO();
79200
79912
  var node_crypto = __require("crypto");
79201
79913
  var node_fs = __require("fs");
79202
79914
  var node_module = __require("module");
@@ -81475,7 +82187,7 @@ var require_storage_orchestrator_addon = __commonJS({
81475
82187
  [Symbol.toStringTag]: { value: "Module" }
81476
82188
  });
81477
82189
  var require_chunk = require_chunk_Cek0wNdY();
81478
- var require_dist10 = require_dist_CxgTulEG();
82190
+ var require_dist10 = require_dist_C7uZ_fOb();
81479
82191
  var node_crypto = __require("crypto");
81480
82192
  var node_fs_promises = __require("fs/promises");
81481
82193
  node_fs_promises = require_chunk.__toESM(node_fs_promises);
@@ -83356,7 +84068,7 @@ var require_system_config_addon = __commonJS({
83356
84068
  [Symbol.toStringTag]: { value: "Module" }
83357
84069
  });
83358
84070
  require_chunk_Cek0wNdY();
83359
- var require_dist10 = require_dist_CxgTulEG();
84071
+ var require_dist10 = require_dist_C7uZ_fOb();
83360
84072
  var SECTION_TITLES = {
83361
84073
  server: "Server",
83362
84074
  auth: "Authentication"
@@ -101417,7 +102129,7 @@ var require_winston_logging = __commonJS({
101417
102129
  [Symbol.toStringTag]: { value: "Module" }
101418
102130
  });
101419
102131
  var require_chunk = require_chunk_Cek0wNdY();
101420
- var require_dist10 = require_dist_CxgTulEG();
102132
+ var require_dist10 = require_dist_C7uZ_fOb();
101421
102133
  var require_formatter = require_formatter_DqAKDlvN();
101422
102134
  var node_path = __require("path");
101423
102135
  node_path = require_chunk.__toESM(node_path);
@@ -102447,9 +103159,9 @@ var require_file_data_plane_DO8KbxCe = __commonJS({
102447
103159
  }
102448
103160
  });
102449
103161
 
102450
- // ../system/dist/tls-u8QCJCFE.js
102451
- var require_tls_u8QCJCFE = __commonJS({
102452
- "../system/dist/tls-u8QCJCFE.js"(exports) {
103162
+ // ../system/dist/tls-BxQlomxd.js
103163
+ var require_tls_BxQlomxd = __commonJS({
103164
+ "../system/dist/tls-BxQlomxd.js"(exports) {
102453
103165
  "use strict";
102454
103166
  var require_chunk = require_chunk_Cek0wNdY();
102455
103167
  var node_crypto = __require("crypto");
@@ -102457,9 +103169,9 @@ var require_tls_u8QCJCFE = __commonJS({
102457
103169
  var node_path = __require("path");
102458
103170
  var node_child_process = __require("child_process");
102459
103171
  var node_util = __require("util");
103172
+ var node_fs = __require("fs");
102460
103173
  var node_os = __require("os");
102461
103174
  node_os = require_chunk.__toESM(node_os);
102462
- var node_fs = __require("fs");
102463
103175
  var node_http = __require("http");
102464
103176
  var node_net = __require("net");
102465
103177
  var DEFAULT_HTTP_PORT = 4480;
@@ -113905,12 +114617,12 @@ var require_dist2 = __commonJS({
113905
114617
  }
113906
114618
  });
113907
114619
 
113908
- // ../system/dist/manifest-python-deps-CcDe4S2Z.js
113909
- var require_manifest_python_deps_CcDe4S2Z = __commonJS({
113910
- "../system/dist/manifest-python-deps-CcDe4S2Z.js"(exports) {
114620
+ // ../system/dist/manifest-python-deps-CRTx_j4k.js
114621
+ var require_manifest_python_deps_CRTx_j4k = __commonJS({
114622
+ "../system/dist/manifest-python-deps-CRTx_j4k.js"(exports) {
113911
114623
  "use strict";
113912
114624
  var require_chunk = require_chunk_Cek0wNdY();
113913
- require_dist_CxgTulEG();
114625
+ require_dist_C7uZ_fOb();
113914
114626
  var node_crypto = __require("crypto");
113915
114627
  node_crypto = require_chunk.__toESM(node_crypto);
113916
114628
  var _camstack_types_node = require_node();
@@ -113919,10 +114631,10 @@ var require_manifest_python_deps_CcDe4S2Z = __commonJS({
113919
114631
  node_path = require_chunk.__toESM(node_path);
113920
114632
  var node_child_process = __require("child_process");
113921
114633
  var node_util = __require("util");
113922
- var node_os = __require("os");
113923
- node_os = require_chunk.__toESM(node_os);
113924
114634
  var node_fs = __require("fs");
113925
114635
  node_fs = require_chunk.__toESM(node_fs);
114636
+ var node_os = __require("os");
114637
+ node_os = require_chunk.__toESM(node_os);
113926
114638
  var node_http = __require("http");
113927
114639
  var node_v8 = __require("v8");
113928
114640
  node_v8 = require_chunk.__toESM(node_v8);
@@ -125356,7 +126068,7 @@ var require_dist3 = __commonJS({
125356
126068
  "use strict";
125357
126069
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
125358
126070
  var require_chunk = require_chunk_Cek0wNdY();
125359
- var require_dist10 = require_dist_CxgTulEG();
126071
+ var require_dist10 = require_dist_C7uZ_fOb();
125360
126072
  var require_builtins_alerts_alerts_addon = require_alerts_addon();
125361
126073
  require_alerts();
125362
126074
  var require_formatter = require_formatter_DqAKDlvN();
@@ -125381,8 +126093,8 @@ var require_dist3 = __commonJS({
125381
126093
  require_system_config();
125382
126094
  var require_builtins_winston_logging_index = require_winston_logging();
125383
126095
  var require_file_data_plane = require_file_data_plane_DO8KbxCe();
125384
- var require_tls$1 = require_tls_u8QCJCFE();
125385
- var require_manifest_python_deps = require_manifest_python_deps_CcDe4S2Z();
126096
+ var require_tls$1 = require_tls_BxQlomxd();
126097
+ var require_manifest_python_deps = require_manifest_python_deps_CRTx_j4k();
125386
126098
  var require_resource_monitor = require_resource_monitor_CdnzxBLP();
125387
126099
  var require_custom_action_registry = require_custom_action_registry_jY0NOZK8();
125388
126100
  var zod = require_zod();
@@ -125394,11 +126106,11 @@ var require_dist3 = __commonJS({
125394
126106
  var node_child_process = __require("child_process");
125395
126107
  var node_util = __require("util");
125396
126108
  node_util = require_chunk.__toESM(node_util);
125397
- var node_os = __require("os");
125398
- node_os = require_chunk.__toESM(node_os);
125399
126109
  var node_fs = __require("fs");
125400
126110
  var node_fs$1 = require_chunk.__toESM(node_fs, 1);
125401
126111
  node_fs = require_chunk.__toESM(node_fs);
126112
+ var node_os = __require("os");
126113
+ node_os = require_chunk.__toESM(node_os);
125402
126114
  var node_http = __require("http");
125403
126115
  var node_vm = __require("vm");
125404
126116
  node_vm = require_chunk.__toESM(node_vm);
@@ -215080,6 +215792,38 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
215080
215792
  /** Wall-clock uptime (seconds). Parsed from `ps etime`. */
215081
215793
  uptimeSec: zod.z.number()
215082
215794
  });
215795
+ var ContainerMemoryPointSchema = zod.z.object({
215796
+ /** Which hierarchy answered, so a reading is never ambiguous. */
215797
+ source: zod.z.enum(["cgroup-v2", "cgroup-v1"]),
215798
+ /** `memory.current` (v2) / `memory.usage_in_bytes` (v1). Always known. */
215799
+ currentBytes: zod.z.number(),
215800
+ /** The cgroup's ceiling. `null` = NO LIMIT — never a sentinel, never zero. */
215801
+ limitBytes: zod.z.number().nullable(),
215802
+ /** Anonymous pages: the closest thing to "what the processes allocated". */
215803
+ anonBytes: zod.z.number().nullable(),
215804
+ /** Page cache. Charged to the cgroup, owned by no process. */
215805
+ fileBytes: zod.z.number().nullable(),
215806
+ /**
215807
+ * Shared memory — and the field that explained the largest single surprise.
215808
+ * The i915 driver backs GPU buffers with shmem, so an inference pool or a
215809
+ * hardware-decode session holding DRM objects is charged HERE and appears
215810
+ * nowhere in a `ps` scan.
215811
+ */
215812
+ shmemBytes: zod.z.number().nullable(),
215813
+ /** Kernel slab charged to this cgroup. `null` on v1, which never publishes it. */
215814
+ slabBytes: zod.z.number().nullable(),
215815
+ /**
215816
+ * Shrinkable i915 GEM object bytes, from debugfs.
215817
+ *
215818
+ * **Host-wide across every DRM client, NOT cgroup-scoped.** It is not a
215819
+ * component of `currentBytes` and must not be subtracted from it; it says
215820
+ * what put the shmem there, where `shmemBytes` only says how much.
215821
+ *
215822
+ * `null` wherever debugfs is not mounted — which is inside every camstack
215823
+ * container today — and on any node with no Intel GPU.
215824
+ */
215825
+ gpuShmemBytes: zod.z.number().nullable()
215826
+ }).extend({ atMs: zod.z.number() });
215083
215827
  var DumpHeapSnapshotInputSchema = zod.z.object({
215084
215828
  /** The addon whose runner should dump a heap snapshot. */
215085
215829
  addonId: zod.z.string()
@@ -215129,6 +215873,21 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
215129
215873
  /** One entry per function seen in the window, heaviest-first. */
215130
215874
  series: zod.z.array(LoadFunctionSeriesSchema).readonly(),
215131
215875
  /**
215876
+ * The CONTAINER's memory over the same window, oldest-first.
215877
+ *
215878
+ * Sits next to `series` rather than in a method of its own because the whole
215879
+ * question is a subtraction: the per-process rows in `series` sum to one
215880
+ * number and this one is another, and an operator who has to issue two calls
215881
+ * to compare them will compare two different instants. Same reader, same
215882
+ * `sinceMs`, same `bucketMs`, same timestamps.
215883
+ *
215884
+ * **EMPTY means ABSENT, never zero.** A node with no cgroup — a developer
215885
+ * Mac, a bare-metal host, a container with the hierarchy hidden — reports no
215886
+ * points at all. A zero here would be indistinguishable from a healthy
215887
+ * container and is precisely the lie this field exists to avoid.
215888
+ */
215889
+ containerMemory: zod.z.array(ContainerMemoryPointSchema).readonly(),
215890
+ /**
215132
215891
  * Width of one returned bucket, in ms. Equals the sampling cadence when no
215133
215892
  * reduction was needed — so a caller can always say what one point covers
215134
215893
  * without having to know whether it was reduced.