camstack 1.2.136 → 1.2.137

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-C85OOASj.js
23637
- var require_dist_C85OOASj = __commonJS({
23638
- "../system/dist/dist-C85OOASj.js"(exports) {
23636
+ // ../system/dist/dist-BOlMCOeT.js
23637
+ var require_dist_BOlMCOeT = __commonJS({
23638
+ "../system/dist/dist-BOlMCOeT.js"(exports) {
23639
23639
  "use strict";
23640
23640
  var zod = require_zod();
23641
23641
  var EventCategory = /* @__PURE__ */ (function(EventCategory2) {
@@ -30499,6 +30499,16 @@ var require_dist_C85OOASj = __commonJS({
30499
30499
  })) }), { auth: "admin" })
30500
30500
  }
30501
30501
  };
30502
+ function describeCustomActions(catalog) {
30503
+ const out = {};
30504
+ for (const [name, spec] of Object.entries(catalog)) out[name] = {
30505
+ kind: spec.kind,
30506
+ auth: spec.auth,
30507
+ scope: spec.scope,
30508
+ ...spec.caller === "required" ? { caller: "required" } : {}
30509
+ };
30510
+ return out;
30511
+ }
30502
30512
  var CustomModelDescriptorSchema = zod.z.object({
30503
30513
  stepId: zod.z.string(),
30504
30514
  entry: ModelCatalogEntrySchema
@@ -35018,6 +35028,7 @@ var require_dist_C85OOASj = __commonJS({
35018
35028
  "addon-crash-loop",
35019
35029
  "addon-update-available",
35020
35030
  "server-update-available",
35031
+ "wrapper-update-available",
35021
35032
  "alarm-triggered",
35022
35033
  "alarm-armed",
35023
35034
  "alarm-disarmed",
@@ -40692,6 +40703,45 @@ var require_dist_C85OOASj = __commonJS({
40692
40703
  /** One operator-grade sentence: this node runs image X; the contract says Y. */
40693
40704
  message: zod.z.string()
40694
40705
  });
40706
+ var WrapperKindSchema = zod.z.enum([
40707
+ "docker",
40708
+ "electron",
40709
+ "native",
40710
+ "contradictory",
40711
+ "unknown"
40712
+ ]);
40713
+ var WrapperEvidenceSchema = zod.z.object({
40714
+ /** Which probe this row reports. */
40715
+ fact: zod.z.enum([
40716
+ "dockerenv-file",
40717
+ "proc-1-cgroup",
40718
+ "platform",
40719
+ "electron-app-version-env"
40720
+ ]),
40721
+ /**
40722
+ * `observed` — this process read it itself. `declared` — the shell told the
40723
+ * process and nothing here can check it. Only the Electron app version is
40724
+ * ever `declared`, and only because a child cannot see its parent.
40725
+ */
40726
+ mode: zod.z.enum(["observed", "declared"]),
40727
+ /** Did the probe support the fact it names? A `false` row is still evidence. */
40728
+ holds: zod.z.boolean(),
40729
+ /** What was actually read, verbatim enough for an operator to argue with. */
40730
+ detail: zod.z.string()
40731
+ });
40732
+ var WrapperIdentitySchema = zod.z.object({
40733
+ kind: WrapperKindSchema,
40734
+ /** Every probe that ran, in a stable order. Never empty. */
40735
+ evidence: zod.z.array(WrapperEvidenceSchema),
40736
+ /**
40737
+ * The version of the SHELL, when the shell has one it can state: the
40738
+ * Electron app version it declared, or the baked seed closure that
40739
+ * fingerprints a container / app bundle. `null` for `contradictory` and
40740
+ * `unknown` — which version belongs to the shell is precisely what is not
40741
+ * known there.
40742
+ */
40743
+ currentVersion: zod.z.string().nullable()
40744
+ });
40695
40745
  var ServerRollbackInfoSchema = zod.z.object({
40696
40746
  /** The version that failed (or was manually rolled back). */
40697
40747
  fromVersion: zod.z.string(),
@@ -40733,7 +40783,13 @@ var require_dist_C85OOASj = __commonJS({
40733
40783
  * Seed-vs-contract verdict (see {@link ImageContractSchema}). Optional for
40734
40784
  * version skew: an older provider's payload simply omits it.
40735
40785
  */
40736
- imageContract: ImageContractSchema.optional()
40786
+ imageContract: ImageContractSchema.optional(),
40787
+ /**
40788
+ * What this node runs INSIDE (see {@link WrapperIdentitySchema}). Optional
40789
+ * for version skew: an older provider's payload simply omits it, and an
40790
+ * absent wrapper is unknown — never `native`.
40791
+ */
40792
+ wrapper: WrapperIdentitySchema.optional()
40737
40793
  });
40738
40794
  var ServerUpdateCheckResultSchema = zod.z.object({
40739
40795
  packageName: zod.z.string(),
@@ -61098,6 +61154,12 @@ var require_dist_C85OOASj = __commonJS({
61098
61154
  return deriveBatteryPresence;
61099
61155
  }
61100
61156
  });
61157
+ Object.defineProperty(exports, "describeCustomActions", {
61158
+ enumerable: true,
61159
+ get: function() {
61160
+ return describeCustomActions;
61161
+ }
61162
+ });
61101
61163
  Object.defineProperty(exports, "deviceManagerCapability", {
61102
61164
  enumerable: true,
61103
61165
  get: function() {
@@ -61524,7 +61586,7 @@ var require_alerts_addon = __commonJS({
61524
61586
  [Symbol.toStringTag]: { value: "Module" }
61525
61587
  });
61526
61588
  require_chunk_Cek0wNdY();
61527
- var require_dist10 = require_dist_C85OOASj();
61589
+ var require_dist10 = require_dist_BOlMCOeT();
61528
61590
  function selectExpired(alerts, cutoffMs) {
61529
61591
  return alerts.filter((a) => a.createdAt < cutoffMs).sort((a, b) => a.createdAt - b.createdAt).map((a) => a.id);
61530
61592
  }
@@ -62343,7 +62405,7 @@ var require_console_logging = __commonJS({
62343
62405
  [Symbol.toStringTag]: { value: "Module" }
62344
62406
  });
62345
62407
  require_chunk_Cek0wNdY();
62346
- var require_dist10 = require_dist_C85OOASj();
62408
+ var require_dist10 = require_dist_BOlMCOeT();
62347
62409
  var require_formatter = require_formatter_DqAKDlvN();
62348
62410
  var LEVEL_RANK = {
62349
62411
  debug: 0,
@@ -62437,7 +62499,7 @@ var require_core_blocks_addon = __commonJS({
62437
62499
  "use strict";
62438
62500
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
62439
62501
  var require_chunk = require_chunk_Cek0wNdY();
62440
- var require_dist10 = require_dist_C85OOASj();
62502
+ var require_dist10 = require_dist_BOlMCOeT();
62441
62503
  var node_crypto = __require("crypto");
62442
62504
  var node_fs_promises = __require("fs/promises");
62443
62505
  node_fs_promises = require_chunk.__toESM(node_fs_promises);
@@ -63334,11 +63396,11 @@ var require_core_blocks = __commonJS({
63334
63396
  }
63335
63397
  });
63336
63398
 
63337
- // ../system/dist/retired-settings-keys-Dfj9LA-i.js
63338
- var require_retired_settings_keys_Dfj9LA_i = __commonJS({
63339
- "../system/dist/retired-settings-keys-Dfj9LA-i.js"(exports) {
63399
+ // ../system/dist/retired-settings-keys-xEAlYnv7.js
63400
+ var require_retired_settings_keys_xEAlYnv7 = __commonJS({
63401
+ "../system/dist/retired-settings-keys-xEAlYnv7.js"(exports) {
63340
63402
  "use strict";
63341
- var require_dist10 = require_dist_C85OOASj();
63403
+ var require_dist10 = require_dist_BOlMCOeT();
63342
63404
  function settingsStoreIsAuthoritativeHere(env) {
63343
63405
  const raw = (env["CAMSTACK_ROLE"] ?? "").trim().toLowerCase();
63344
63406
  return raw === "" || raw === "hub";
@@ -65573,8 +65635,8 @@ var require_device_manager_addon = __commonJS({
65573
65635
  [Symbol.toStringTag]: { value: "Module" }
65574
65636
  });
65575
65637
  require_chunk_Cek0wNdY();
65576
- var require_dist10 = require_dist_C85OOASj();
65577
- var require_retired_settings_keys = require_retired_settings_keys_Dfj9LA_i();
65638
+ var require_dist10 = require_dist_BOlMCOeT();
65639
+ var require_retired_settings_keys = require_retired_settings_keys_xEAlYnv7();
65578
65640
  var node_crypto = __require("crypto");
65579
65641
  var _camstack_types_node = require_node();
65580
65642
  var JOB_HISTORY = 20;
@@ -71199,7 +71261,7 @@ var require_hub_forwarder = __commonJS({
71199
71261
  [Symbol.toStringTag]: { value: "Module" }
71200
71262
  });
71201
71263
  require_chunk_Cek0wNdY();
71202
- var require_dist10 = require_dist_C85OOASj();
71264
+ var require_dist10 = require_dist_BOlMCOeT();
71203
71265
  var require_formatter = require_formatter_DqAKDlvN();
71204
71266
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
71205
71267
  var HubForwarderDestination = class {
@@ -71336,7 +71398,7 @@ var require_liveness_monitor_addon = __commonJS({
71336
71398
  "use strict";
71337
71399
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
71338
71400
  require_chunk_Cek0wNdY();
71339
- var require_dist10 = require_dist_C85OOASj();
71401
+ var require_dist10 = require_dist_BOlMCOeT();
71340
71402
  var NO_DEVICES = "liveness:no-devices";
71341
71403
  var ALL_OFFLINE = "liveness:all-devices-offline";
71342
71404
  var NO_FOOTAGE_PREFIX = "liveness:no-footage:";
@@ -71532,7 +71594,7 @@ var require_local_auth_addon = __commonJS({
71532
71594
  [Symbol.toStringTag]: { value: "Module" }
71533
71595
  });
71534
71596
  var require_chunk = require_chunk_Cek0wNdY();
71535
- var require_dist10 = require_dist_C85OOASj();
71597
+ var require_dist10 = require_dist_BOlMCOeT();
71536
71598
  var node_crypto = __require("crypto");
71537
71599
  node_crypto = require_chunk.__toESM(node_crypto);
71538
71600
  var crypto$1 = __require("crypto");
@@ -79345,7 +79407,7 @@ var require_loki_logging = __commonJS({
79345
79407
  [Symbol.toStringTag]: { value: "Module" }
79346
79408
  });
79347
79409
  require_chunk_Cek0wNdY();
79348
- var require_dist10 = require_dist_C85OOASj();
79410
+ var require_dist10 = require_dist_BOlMCOeT();
79349
79411
  function sanitizeLabelName(raw) {
79350
79412
  const replaced = raw.replaceAll(/[^a-zA-Z0-9_]/g, "_");
79351
79413
  return /^[a-zA-Z_]/.test(replaced) ? replaced : `_${replaced}`;
@@ -79910,7 +79972,7 @@ var require_native_metrics_addon = __commonJS({
79910
79972
  [Symbol.toStringTag]: { value: "Module" }
79911
79973
  });
79912
79974
  var require_chunk = require_chunk_Cek0wNdY();
79913
- var require_dist10 = require_dist_C85OOASj();
79975
+ var require_dist10 = require_dist_BOlMCOeT();
79914
79976
  var node_fs_promises = __require("fs/promises");
79915
79977
  var node_child_process = __require("child_process");
79916
79978
  var node_util = __require("util");
@@ -82346,13 +82408,6 @@ var require_native_metrics_addon = __commonJS({
82346
82408
  cpuSplits
82347
82409
  });
82348
82410
  }
82349
- /**
82350
- * Ask the addon's forked runner to write a V8 heap snapshot (SIGUSR2 → the
82351
- * runner's diagnostic handler in addon-runner.ts), for deep per-addon memory
82352
- * attribution. Resolves the addon's worker pid from the kernel `$process.list`
82353
- * and signals it; the runner writes `/tmp/heap-<sanitized nodeId>.heapsnapshot`
82354
- * and logs its memoryUsage + heap-space breakdown. '$hub' targets this process.
82355
- */
82356
82411
  /** Raw `ps` scan returning every pid + command + resource stats. */
82357
82412
  async runPs() {
82358
82413
  try {
@@ -82494,7 +82549,7 @@ var require_filesystem_storage_addon = __commonJS({
82494
82549
  [Symbol.toStringTag]: { value: "Module" }
82495
82550
  });
82496
82551
  var require_chunk = require_chunk_Cek0wNdY();
82497
- var require_dist10 = require_dist_C85OOASj();
82552
+ var require_dist10 = require_dist_BOlMCOeT();
82498
82553
  var node_crypto = __require("crypto");
82499
82554
  var node_fs_promises = __require("fs/promises");
82500
82555
  var node_path = __require("path");
@@ -83610,8 +83665,8 @@ var require_sqlite_settings_addon = __commonJS({
83610
83665
  [Symbol.toStringTag]: { value: "Module" }
83611
83666
  });
83612
83667
  var require_chunk = require_chunk_Cek0wNdY();
83613
- var require_dist10 = require_dist_C85OOASj();
83614
- var require_retired_settings_keys = require_retired_settings_keys_Dfj9LA_i();
83668
+ var require_dist10 = require_dist_BOlMCOeT();
83669
+ var require_retired_settings_keys = require_retired_settings_keys_xEAlYnv7();
83615
83670
  var node_crypto = __require("crypto");
83616
83671
  var node_fs = __require("fs");
83617
83672
  var node_module = __require("module");
@@ -86115,7 +86170,7 @@ var require_storage_orchestrator_addon = __commonJS({
86115
86170
  [Symbol.toStringTag]: { value: "Module" }
86116
86171
  });
86117
86172
  var require_chunk = require_chunk_Cek0wNdY();
86118
- var require_dist10 = require_dist_C85OOASj();
86173
+ var require_dist10 = require_dist_BOlMCOeT();
86119
86174
  var require_hub_cap_forward = require_hub_cap_forward_DmHNjbB0();
86120
86175
  var zod = require_zod();
86121
86176
  var node_crypto = __require("crypto");
@@ -89765,7 +89820,7 @@ var require_system_config_addon = __commonJS({
89765
89820
  [Symbol.toStringTag]: { value: "Module" }
89766
89821
  });
89767
89822
  require_chunk_Cek0wNdY();
89768
- var require_dist10 = require_dist_C85OOASj();
89823
+ var require_dist10 = require_dist_BOlMCOeT();
89769
89824
  var SECTION_TITLES = {
89770
89825
  server: "Server",
89771
89826
  auth: "Authentication"
@@ -107826,7 +107881,7 @@ var require_winston_logging = __commonJS({
107826
107881
  [Symbol.toStringTag]: { value: "Module" }
107827
107882
  });
107828
107883
  var require_chunk = require_chunk_Cek0wNdY();
107829
- var require_dist10 = require_dist_C85OOASj();
107884
+ var require_dist10 = require_dist_BOlMCOeT();
107830
107885
  var require_formatter = require_formatter_DqAKDlvN();
107831
107886
  var node_path = __require("path");
107832
107887
  node_path = require_chunk.__toESM(node_path);
@@ -120460,9 +120515,9 @@ var require_dist2 = __commonJS({
120460
120515
  }
120461
120516
  });
120462
120517
 
120463
- // ../system/dist/manifest-system-deps-DvY1bY_n.js
120464
- var require_manifest_system_deps_DvY1bY_n = __commonJS({
120465
- "../system/dist/manifest-system-deps-DvY1bY_n.js"(exports) {
120518
+ // ../system/dist/manifest-system-deps-Bo92lg5u.js
120519
+ var require_manifest_system_deps_Bo92lg5u = __commonJS({
120520
+ "../system/dist/manifest-system-deps-Bo92lg5u.js"(exports) {
120466
120521
  "use strict";
120467
120522
  var require_chunk = require_chunk_Cek0wNdY();
120468
120523
  require_hub_cap_forward_DmHNjbB0();
@@ -120481,6 +120536,7 @@ var require_manifest_system_deps_DvY1bY_n = __commonJS({
120481
120536
  var node_http = __require("http");
120482
120537
  var node_v8 = __require("v8");
120483
120538
  node_v8 = require_chunk.__toESM(node_v8);
120539
+ var node_inspector_promises = __require("inspector/promises");
120484
120540
  var node_perf_hooks = __require("perf_hooks");
120485
120541
  var node_vm = __require("vm");
120486
120542
  node_vm = require_chunk.__toESM(node_vm);
@@ -120720,7 +120776,7 @@ var require_manifest_system_deps_DvY1bY_n = __commonJS({
120720
120776
  var DECISIVE_HEAP_SPACES = ["old_space", "large_object_space"];
120721
120777
  var HEAP_SPACE_REPORT_MIN_MB = 32;
120722
120778
  var BYTES_PER_MB = 1048576;
120723
- function mb(bytes) {
120779
+ function mb$1(bytes) {
120724
120780
  return Math.round(bytes / BYTES_PER_MB);
120725
120781
  }
120726
120782
  function tenthsMb(bytes) {
@@ -120741,13 +120797,13 @@ var require_manifest_system_deps_DvY1bY_n = __commonJS({
120741
120797
  }
120742
120798
  function selectReportedSpaces(spaces, minMb = 32) {
120743
120799
  const decisive = DECISIVE_HEAP_SPACES.map((name) => spaces.find((space) => space.name === name)).filter((space) => space !== void 0);
120744
- const rest = spaces.filter((space) => !DECISIVE_HEAP_SPACES.includes(space.name)).filter((space) => mb(space.usedBytes) >= minMb).toSorted((a, b) => b.usedBytes - a.usedBytes || a.name.localeCompare(b.name));
120800
+ const rest = spaces.filter((space) => !DECISIVE_HEAP_SPACES.includes(space.name)).filter((space) => mb$1(space.usedBytes) >= minMb).toSorted((a, b) => b.usedBytes - a.usedBytes || a.name.localeCompare(b.name));
120745
120801
  return [...decisive, ...rest];
120746
120802
  }
120747
120803
  function formatHeapSpaces(spaces, minMb = 32) {
120748
120804
  const selected = selectReportedSpaces(spaces, minMb);
120749
120805
  if (selected.length === 0) return "";
120750
- return ` ${selected.map((s) => `${heapSpaceField(s.name)}=${mb(s.usedBytes)}MB`).join(" ")}`;
120806
+ return ` ${selected.map((s) => `${heapSpaceField(s.name)}=${mb$1(s.usedBytes)}MB`).join(" ")}`;
120751
120807
  }
120752
120808
  var defaultDeps = {
120753
120809
  readMemory: () => process.memoryUsage(),
@@ -120763,6 +120819,137 @@ var require_manifest_system_deps_DvY1bY_n = __commonJS({
120763
120819
  request.sink.warn(`[mem] node="${request.label}" space report failed: ${error instanceof Error ? error.message : String(error)}`);
120764
120820
  }
120765
120821
  }
120822
+ var ARRAY_BUFFER_CENSUS_THRESHOLD_MB = 512;
120823
+ var ARRAY_BUFFER_CENSUS_SUSTAIN_MS = 6e4;
120824
+ var ARRAY_BUFFER_CENSUS_COOLDOWN_MS = 30 * 6e4;
120825
+ var ARRAY_BUFFER_RELEASE_MIN_DROP_RATIO = 0.5;
120826
+ function createArrayBufferCensusGate(options = {}) {
120827
+ const thresholdMb = options.thresholdMb ?? 512;
120828
+ const sustainMs = options.sustainMs ?? 6e4;
120829
+ const cooldownMs = options.cooldownMs ?? 18e5;
120830
+ let aboveSince = null;
120831
+ let lastTakenAt = Number.NEGATIVE_INFINITY;
120832
+ let previousMb = null;
120833
+ let previousAt = null;
120834
+ return { observe(arrayBuffersMb, at) {
120835
+ const previous = previousMb;
120836
+ const previousHeldForMs = previous !== null && previousAt !== null && aboveSince !== null ? previousAt - aboveSince : null;
120837
+ previousMb = arrayBuffersMb;
120838
+ previousAt = at;
120839
+ if (previous !== null && previousHeldForMs !== null && previousHeldForMs >= sustainMs && previous - arrayBuffersMb >= thresholdMb * 0.5) {
120840
+ aboveSince = null;
120841
+ return {
120842
+ kind: "released",
120843
+ fromMb: previous,
120844
+ toMb: arrayBuffersMb
120845
+ };
120846
+ }
120847
+ if (arrayBuffersMb < thresholdMb) {
120848
+ aboveSince = null;
120849
+ return { kind: "hold" };
120850
+ }
120851
+ aboveSince ??= at;
120852
+ const heldForMs = at - aboveSince;
120853
+ if (heldForMs < sustainMs) return { kind: "hold" };
120854
+ if (at - lastTakenAt < cooldownMs) return { kind: "hold" };
120855
+ lastTakenAt = at;
120856
+ return {
120857
+ kind: "take",
120858
+ reason: `arrayBuffers=${arrayBuffersMb}MB has been >=${thresholdMb}MB for ${heldForMs}ms`
120859
+ };
120860
+ } };
120861
+ }
120862
+ var HISTOGRAM_FN = `function () {
120863
+ var byBucket = new Map(), bySize = new Map()
120864
+ var count = 0, bytes = 0, detached = 0
120865
+ for (var i = 0; i < this.length; i++) {
120866
+ var b = this[i].byteLength
120867
+ count++
120868
+ bytes += b
120869
+ if (b === 0) { detached++; continue }
120870
+ var bucket = 1
120871
+ while (bucket < b) bucket *= 2
120872
+ var e = byBucket.get(bucket); if (!e) { e = [0, 0]; byBucket.set(bucket, e) }
120873
+ e[0]++; e[1] += b
120874
+ var s = bySize.get(b); if (!s) { s = [0, 0]; bySize.set(b, s) }
120875
+ s[0]++; s[1] += b
120876
+ }
120877
+ var buckets = []
120878
+ byBucket.forEach(function (v, k) { buckets.push({ upToBytes: k, count: v[0], bytes: v[1] }) })
120879
+ buckets.sort(function (a, b) { return b.bytes - a.bytes })
120880
+ var topSizes = []
120881
+ bySize.forEach(function (v, k) { topSizes.push({ byteLength: k, count: v[0], bytes: v[1] }) })
120882
+ topSizes.sort(function (a, b) { return b.bytes - a.bytes })
120883
+ return { count: count, bytes: bytes, detached: detached,
120884
+ buckets: buckets.slice(0, 6), topSizes: topSizes.slice(0, 5) }
120885
+ }`;
120886
+ function isFiniteNumber(value) {
120887
+ return typeof value === "number" && Number.isFinite(value);
120888
+ }
120889
+ function isBucket(value) {
120890
+ return typeof value === "object" && value !== null && isFiniteNumber(Reflect.get(value, "upToBytes")) && isFiniteNumber(Reflect.get(value, "count")) && isFiniteNumber(Reflect.get(value, "bytes"));
120891
+ }
120892
+ function isExactSize(value) {
120893
+ return typeof value === "object" && value !== null && isFiniteNumber(Reflect.get(value, "byteLength")) && isFiniteNumber(Reflect.get(value, "count")) && isFiniteNumber(Reflect.get(value, "bytes"));
120894
+ }
120895
+ function isArrayBufferCensusReport(value) {
120896
+ if (typeof value !== "object" || value === null) return false;
120897
+ const buckets = Reflect.get(value, "buckets");
120898
+ const topSizes = Reflect.get(value, "topSizes");
120899
+ return isFiniteNumber(Reflect.get(value, "count")) && isFiniteNumber(Reflect.get(value, "bytes")) && isFiniteNumber(Reflect.get(value, "detached")) && Array.isArray(buckets) && buckets.every(isBucket) && Array.isArray(topSizes) && topSizes.every(isExactSize);
120900
+ }
120901
+ function readObjectId(result, path) {
120902
+ let cursor = result;
120903
+ for (const key of path) {
120904
+ if (typeof cursor !== "object" || cursor === null) throw new Error(`inspector reply has no ${path.join(".")}`);
120905
+ cursor = Reflect.get(cursor, key);
120906
+ }
120907
+ if (typeof cursor !== "string") throw new Error(`inspector reply has no ${path.join(".")}`);
120908
+ return cursor;
120909
+ }
120910
+ function createInspectorArrayBufferCensus() {
120911
+ return async () => {
120912
+ const session = new node_inspector_promises.Session();
120913
+ session.connect();
120914
+ try {
120915
+ const prototypeObjectId = readObjectId(await session.post("Runtime.evaluate", { expression: "ArrayBuffer.prototype" }), ["result", "objectId"]);
120916
+ try {
120917
+ const objectId = readObjectId(await session.post("Runtime.queryObjects", { prototypeObjectId }), ["objects", "objectId"]);
120918
+ try {
120919
+ const evaluated = await session.post("Runtime.callFunctionOn", {
120920
+ objectId,
120921
+ functionDeclaration: HISTOGRAM_FN,
120922
+ returnByValue: true
120923
+ });
120924
+ const value = typeof evaluated === "object" && evaluated !== null ? Reflect.get(Reflect.get(evaluated, "result") ?? {}, "value") : void 0;
120925
+ if (!isArrayBufferCensusReport(value)) throw new Error("inspector returned a histogram of an unexpected shape");
120926
+ return value;
120927
+ } finally {
120928
+ await session.post("Runtime.releaseObject", { objectId });
120929
+ }
120930
+ } finally {
120931
+ await session.post("Runtime.releaseObject", { objectId: prototypeObjectId });
120932
+ }
120933
+ } finally {
120934
+ session.disconnect();
120935
+ }
120936
+ };
120937
+ }
120938
+ var MB$1 = 1048576;
120939
+ function mb(bytes) {
120940
+ return Math.round(bytes / MB$1);
120941
+ }
120942
+ function formatBucket(bucket) {
120943
+ return `<=${bucket.upToBytes >= MB$1 ? `${Math.round(bucket.upToBytes / MB$1)}MB` : bucket.upToBytes >= 1024 ? `${Math.round(bucket.upToBytes / 1024)}KB` : `${bucket.upToBytes}B`}:${bucket.count}x/${mb(bucket.bytes)}MB`;
120944
+ }
120945
+ function formatExact(size) {
120946
+ return `${size.byteLength}Bx${size.count}=${mb(size.bytes)}MB`;
120947
+ }
120948
+ function formatArrayBufferCensus(label, report, context) {
120949
+ const live = mb(report.bytes);
120950
+ const verdict = live < context.rawMb * 0.5 ? "mostly DEAD buffers awaiting a sweep, not a retention" : "LIVE \u2014 something holds them";
120951
+ return `[mem] ${label} arrayBuffer census: live=${live}MB in ${report.count} ArrayBuffers (raw arrayBuffers=${context.rawMb}MB at the trigger \u2192 ${verdict}) top sizes: ${report.topSizes.map(formatExact).join(", ") || "(none)"}; buckets: ${report.buckets.map(formatBucket).join(", ") || "(none)"}; detached=${report.detached} took=${context.tookMs}ms \u2014 cost: ONE forced full GC (the same pause as a reclaim pass), walked in memory, nothing serialised, nothing written; trigger: ${context.reason}`;
120952
+ }
120766
120953
  var HUB_MAIN_HEAP_WATCH_LABEL = "hub-main";
120767
120954
  var HEAP_WATCH_INTERVAL_MS = 6e4;
120768
120955
  var HEAP_WATCH_WARN_RATIO = 0.8;
@@ -120999,9 +121186,36 @@ var require_manifest_system_deps_DvY1bY_n = __commonJS({
120999
121186
  if (previous === "escalated" && mode === "steady") sink.info(`[mem] ${label} heap back to routine \u2014 logging every ${intervalMs}ms`);
121000
121187
  checkRssBudget(sample, at);
121001
121188
  maybeReclaim(sample);
121189
+ judgeArrayBuffers(sample, at);
121002
121190
  } catch {
121003
121191
  }
121004
121192
  };
121193
+ const censusProbe = probes?.arrayBufferCensus;
121194
+ const censusGate = censusProbe === void 0 ? void 0 : createArrayBufferCensusGate(censusProbe);
121195
+ let censusInFlight = false;
121196
+ const judgeArrayBuffers = (sample, at) => {
121197
+ if (censusProbe === void 0 || censusGate === void 0) return;
121198
+ const verdict = censusGate.observe(sample.arrayBuffersMb, at);
121199
+ if (verdict.kind === "released") {
121200
+ sink.warn(`[mem] ${label} arrayBuffers RELEASED ${verdict.fromMb}MB\u2192${verdict.toMb}MB between two probes ${probeIntervalMs}ms apart \u2014 no owner logged this release; whatever held them let go in one instant (a socket that died, a queue that drained, a map that was cleared). Correlate with every line of this process in that window.`);
121201
+ return;
121202
+ }
121203
+ if (verdict.kind !== "take" || censusInFlight) return;
121204
+ censusInFlight = true;
121205
+ const startedAt = now();
121206
+ const rawMb = sample.arrayBuffersMb;
121207
+ censusProbe.census().then((report) => {
121208
+ sink.warn(formatArrayBufferCensus(label, report, {
121209
+ rawMb,
121210
+ tookMs: now() - startedAt,
121211
+ reason: verdict.reason
121212
+ }));
121213
+ }).catch((error) => {
121214
+ sink.warn(`[mem] ${label} arrayBuffer census FAILED after ${now() - startedAt}ms (trigger: ${verdict.reason}) \u2014 ${error instanceof Error ? error.message : String(error)}`);
121215
+ }).finally(() => {
121216
+ censusInFlight = false;
121217
+ });
121218
+ };
121005
121219
  const timer = setInterval(tick, probeIntervalMs);
121006
121220
  timer.unref?.();
121007
121221
  tick();
@@ -125746,7 +125960,8 @@ var require_manifest_system_deps_DvY1bY_n = __commonJS({
125746
125960
  this.registeredHandler({
125747
125961
  childId: msg.childId,
125748
125962
  caps: msg.caps,
125749
- incarnation
125963
+ incarnation,
125964
+ ...msg.customActions !== void 0 ? { customActions: msg.customActions } : {}
125750
125965
  });
125751
125966
  return { ok: true };
125752
125967
  }
@@ -125831,6 +126046,15 @@ var require_manifest_system_deps_DvY1bY_n = __commonJS({
125831
126046
  * subscriptions do), the register carries the real union — even if empty.
125832
126047
  */
125833
126048
  hasDeclaredEventPatterns = false;
126049
+ /**
126050
+ * The custom-action catalogs of every hosted addon, once the runner's init
126051
+ * loop has produced them (`updateCaps(caps, catalogs)` at post-init). `null`
126052
+ * until then, and OMITTED from the register frame while null: the parent
126053
+ * reads absence as "not described yet", and a pre-init register must never
126054
+ * look like "this child declares no actions". Kept on the client so every
126055
+ * later re-register (native-cap change, reconnect) re-carries it unchanged.
126056
+ */
126057
+ latestCustomActions = null;
125834
126058
  /** Events and logs queued while the channel is not yet open. */
125835
126059
  pendingEmits = [];
125836
126060
  /** Handler for parent→child events. Registered via `onEvent`. */
@@ -125970,12 +126194,7 @@ var require_manifest_system_deps_DvY1bY_n = __commonJS({
125970
126194
  }
125971
126195
  if (msg.kind === "set-log-level") this.setLogLevelHandler?.(msg.level);
125972
126196
  });
125973
- const register = {
125974
- kind: "register",
125975
- childId: this.options.childId,
125976
- caps: this.latestCaps,
125977
- ...this.hasDeclaredEventPatterns ? { eventPatterns: this.latestEventPatterns } : {}
125978
- };
126197
+ const register = this.registerFrame(this.latestCaps);
125979
126198
  try {
125980
126199
  await channel.request(register);
125981
126200
  } catch (err) {
@@ -126001,16 +126220,26 @@ var require_manifest_system_deps_DvY1bY_n = __commonJS({
126001
126220
  * it (device-restore can race ahead of the UDS connect). After `start()`,
126002
126221
  * the set is sent immediately.
126003
126222
  */
126004
- async updateCaps(caps) {
126223
+ async updateCaps(caps, customActions) {
126005
126224
  this.latestCaps = caps;
126225
+ if (customActions !== void 0) this.latestCustomActions = customActions;
126006
126226
  if (this.channel === null) return;
126007
- const register = {
126227
+ await this.channel.request(this.registerFrame(caps));
126228
+ }
126229
+ /**
126230
+ * The register frame: caps, plus everything a re-register must atomically
126231
+ * re-carry so no separate re-sync can be missed — the subscription union
126232
+ * (omitted if nothing ever declared, which keeps the fail-open shape) and the
126233
+ * custom-action catalogs (omitted until the init loop produced them).
126234
+ */
126235
+ registerFrame(caps) {
126236
+ return {
126008
126237
  kind: "register",
126009
126238
  childId: this.options.childId,
126010
126239
  caps,
126011
- ...this.hasDeclaredEventPatterns ? { eventPatterns: this.latestEventPatterns } : {}
126240
+ ...this.hasDeclaredEventPatterns ? { eventPatterns: this.latestEventPatterns } : {},
126241
+ ...this.latestCustomActions !== null ? { customActions: this.latestCustomActions } : {}
126012
126242
  };
126013
- await this.channel.request(register);
126014
126243
  }
126015
126244
  /**
126016
126245
  * Fire-and-forget: send a system event to the parent for forwarding to the
@@ -127983,6 +128212,30 @@ var require_manifest_system_deps_DvY1bY_n = __commonJS({
127983
128212
  return AGENT_CAP_FWD_SERVICE;
127984
128213
  }
127985
128214
  });
128215
+ Object.defineProperty(exports, "ARRAY_BUFFER_CENSUS_COOLDOWN_MS", {
128216
+ enumerable: true,
128217
+ get: function() {
128218
+ return ARRAY_BUFFER_CENSUS_COOLDOWN_MS;
128219
+ }
128220
+ });
128221
+ Object.defineProperty(exports, "ARRAY_BUFFER_CENSUS_SUSTAIN_MS", {
128222
+ enumerable: true,
128223
+ get: function() {
128224
+ return ARRAY_BUFFER_CENSUS_SUSTAIN_MS;
128225
+ }
128226
+ });
128227
+ Object.defineProperty(exports, "ARRAY_BUFFER_CENSUS_THRESHOLD_MB", {
128228
+ enumerable: true,
128229
+ get: function() {
128230
+ return ARRAY_BUFFER_CENSUS_THRESHOLD_MB;
128231
+ }
128232
+ });
128233
+ Object.defineProperty(exports, "ARRAY_BUFFER_RELEASE_MIN_DROP_RATIO", {
128234
+ enumerable: true,
128235
+ get: function() {
128236
+ return ARRAY_BUFFER_RELEASE_MIN_DROP_RATIO;
128237
+ }
128238
+ });
127986
128239
  Object.defineProperty(exports, "AddonDepsManager", {
127987
128240
  enumerable: true,
127988
128241
  get: function() {
@@ -128313,6 +128566,12 @@ var require_manifest_system_deps_DvY1bY_n = __commonJS({
128313
128566
  return createAddonService;
128314
128567
  }
128315
128568
  });
128569
+ Object.defineProperty(exports, "createArrayBufferCensusGate", {
128570
+ enumerable: true,
128571
+ get: function() {
128572
+ return createArrayBufferCensusGate;
128573
+ }
128574
+ });
128316
128575
  Object.defineProperty(exports, "createBrokerDeviceManagerApi", {
128317
128576
  enumerable: true,
128318
128577
  get: function() {
@@ -128337,6 +128596,12 @@ var require_manifest_system_deps_DvY1bY_n = __commonJS({
128337
128596
  return createEventPlaneReader;
128338
128597
  }
128339
128598
  });
128599
+ Object.defineProperty(exports, "createInspectorArrayBufferCensus", {
128600
+ enumerable: true,
128601
+ get: function() {
128602
+ return createInspectorArrayBufferCensus;
128603
+ }
128604
+ });
128340
128605
  Object.defineProperty(exports, "createKernelHwAccel", {
128341
128606
  enumerable: true,
128342
128607
  get: function() {
@@ -128451,6 +128716,12 @@ var require_manifest_system_deps_DvY1bY_n = __commonJS({
128451
128716
  return encodeFrame;
128452
128717
  }
128453
128718
  });
128719
+ Object.defineProperty(exports, "formatArrayBufferCensus", {
128720
+ enumerable: true,
128721
+ get: function() {
128722
+ return formatArrayBufferCensus;
128723
+ }
128724
+ });
128454
128725
  Object.defineProperty(exports, "formatEventPlane", {
128455
128726
  enumerable: true,
128456
128727
  get: function() {
@@ -128553,6 +128824,12 @@ var require_manifest_system_deps_DvY1bY_n = __commonJS({
128553
128824
  return ipcParentLink;
128554
128825
  }
128555
128826
  });
128827
+ Object.defineProperty(exports, "isArrayBufferCensusReport", {
128828
+ enumerable: true,
128829
+ get: function() {
128830
+ return isArrayBufferCensusReport;
128831
+ }
128832
+ });
128556
128833
  Object.defineProperty(exports, "localEndpointPath", {
128557
128834
  enumerable: true,
128558
128835
  get: function() {
@@ -128736,10 +129013,11 @@ var require_manifest_system_deps_DvY1bY_n = __commonJS({
128736
129013
  }
128737
129014
  });
128738
129015
 
128739
- // ../system/dist/custom-action-registry-jY0NOZK8.js
128740
- var require_custom_action_registry_jY0NOZK8 = __commonJS({
128741
- "../system/dist/custom-action-registry-jY0NOZK8.js"(exports) {
129016
+ // ../system/dist/custom-action-registry-BDwM5qAK.js
129017
+ var require_custom_action_registry_BDwM5qAK = __commonJS({
129018
+ "../system/dist/custom-action-registry-BDwM5qAK.js"(exports) {
128742
129019
  "use strict";
129020
+ var require_dist10 = require_dist_BOlMCOeT();
128743
129021
  var CustomActionRegistry = class {
128744
129022
  byAddon = /* @__PURE__ */ new Map();
128745
129023
  registerAddon(addonId, catalog, handler) {
@@ -128766,6 +129044,24 @@ var require_custom_action_registry_jY0NOZK8 = __commonJS({
128766
129044
  listAddons() {
128767
129045
  return [...this.byAddon.keys()];
128768
129046
  }
129047
+ /**
129048
+ * Every registered catalog with its schemas stripped, keyed by addon id —
129049
+ * what a runner puts in its register frame so the hub learns the catalogs
129050
+ * from the process that owns them (D444). `hostedAddonIds` are the addons
129051
+ * this runner hosts: each is present in the result, with an EMPTY catalog
129052
+ * when it registered none, so the parent can say "declares no actions" per
129053
+ * addon instead of guessing from an absent key.
129054
+ */
129055
+ describeAll(hostedAddonIds) {
129056
+ const out = {};
129057
+ for (const addonId of hostedAddonIds) out[addonId] = {};
129058
+ for (const [addonId, actions] of this.byAddon) {
129059
+ const catalog = {};
129060
+ for (const [name, entry] of actions) catalog[name] = entry.spec;
129061
+ out[addonId] = require_dist10.describeCustomActions(catalog);
129062
+ }
129063
+ return out;
129064
+ }
128769
129065
  };
128770
129066
  Object.defineProperty(exports, "CustomActionRegistry", {
128771
129067
  enumerable: true,
@@ -132535,7 +132831,7 @@ var require_dist3 = __commonJS({
132535
132831
  "use strict";
132536
132832
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
132537
132833
  var require_chunk = require_chunk_Cek0wNdY();
132538
- var require_dist10 = require_dist_C85OOASj();
132834
+ var require_dist10 = require_dist_BOlMCOeT();
132539
132835
  var require_builtins_alerts_alerts_addon = require_alerts_addon();
132540
132836
  require_alerts();
132541
132837
  var require_formatter = require_formatter_DqAKDlvN();
@@ -132562,9 +132858,9 @@ var require_dist3 = __commonJS({
132562
132858
  var require_builtins_winston_logging_index = require_winston_logging();
132563
132859
  var require_event_loop_stall_monitor = require_event_loop_stall_monitor_DM9OAzy8();
132564
132860
  var require_tls$1 = require_tls_BxQlomxd();
132565
- var require_manifest_system_deps = require_manifest_system_deps_DvY1bY_n();
132861
+ var require_manifest_system_deps = require_manifest_system_deps_Bo92lg5u();
132566
132862
  var require_resource_monitor = require_resource_monitor_CdnzxBLP();
132567
- var require_custom_action_registry = require_custom_action_registry_jY0NOZK8();
132863
+ var require_custom_action_registry = require_custom_action_registry_BDwM5qAK();
132568
132864
  var zod = require_zod();
132569
132865
  var node_crypto = __require("crypto");
132570
132866
  var _camstack_types_node = require_node();
@@ -213246,6 +213542,10 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
213246
213542
  exports.AGENT_CAP_FWD_SERVICE = require_manifest_system_deps.AGENT_CAP_FWD_SERVICE;
213247
213543
  exports.AGENT_READINESS_SERVICE_NAME = AGENT_READINESS_SERVICE_NAME;
213248
213544
  exports.ALL_CAPABILITY_DEFINITIONS = require_dist10.ALL_CAPABILITY_DEFINITIONS;
213545
+ exports.ARRAY_BUFFER_CENSUS_COOLDOWN_MS = require_manifest_system_deps.ARRAY_BUFFER_CENSUS_COOLDOWN_MS;
213546
+ exports.ARRAY_BUFFER_CENSUS_SUSTAIN_MS = require_manifest_system_deps.ARRAY_BUFFER_CENSUS_SUSTAIN_MS;
213547
+ exports.ARRAY_BUFFER_CENSUS_THRESHOLD_MB = require_manifest_system_deps.ARRAY_BUFFER_CENSUS_THRESHOLD_MB;
213548
+ exports.ARRAY_BUFFER_RELEASE_MIN_DROP_RATIO = require_manifest_system_deps.ARRAY_BUFFER_RELEASE_MIN_DROP_RATIO;
213249
213549
  exports.AddonApiFactory = AddonApiFactory;
213250
213550
  exports.AddonDepsManager = require_manifest_system_deps.AddonDepsManager;
213251
213551
  exports.AddonEngineManager = AddonEngineManager;
@@ -213429,6 +213729,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
213429
213729
  exports.createAddonContext = require_manifest_system_deps.createAddonContext;
213430
213730
  exports.createAddonDataPlaneFacility = require_manifest_system_deps.createAddonDataPlaneFacility;
213431
213731
  exports.createAddonService = require_manifest_system_deps.createAddonService;
213732
+ exports.createArrayBufferCensusGate = require_manifest_system_deps.createArrayBufferCensusGate;
213432
213733
  exports.createAuthenticatedFileServer = require_event_loop_stall_monitor.createAuthenticatedFileServer;
213433
213734
  exports.createBroker = createBroker2;
213434
213735
  exports.createBrokerDeviceManagerApi = require_manifest_system_deps.createBrokerDeviceManagerApi;
@@ -213440,6 +213741,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
213440
213741
  exports.createFileDataPlaneHandler = require_event_loop_stall_monitor.createFileDataPlaneHandler;
213441
213742
  exports.createHubCapForwardService = require_hub_cap_forward.createHubCapForwardService;
213442
213743
  exports.createHubService = createHubService;
213744
+ exports.createInspectorArrayBufferCensus = require_manifest_system_deps.createInspectorArrayBufferCensus;
213443
213745
  exports.createKernelHwAccel = require_manifest_system_deps.createKernelHwAccel;
213444
213746
  exports.createLocalTransport = require_manifest_system_deps.createLocalTransport;
213445
213747
  exports.createLoopDelayMeter = require_manifest_system_deps.createLoopDelayMeter;
@@ -213509,6 +213811,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
213509
213811
  return _camstack_types_node.findInPath;
213510
213812
  }
213511
213813
  });
213814
+ exports.formatArrayBufferCensus = require_manifest_system_deps.formatArrayBufferCensus;
213512
213815
  exports.formatEventPlane = require_manifest_system_deps.formatEventPlane;
213513
213816
  exports.formatHeapSpaces = require_manifest_system_deps.formatHeapSpaces;
213514
213817
  exports.formatLogLine = require_formatter.formatLogLine;
@@ -213564,6 +213867,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
213564
213867
  });
213565
213868
  exports.ipcParentLink = require_manifest_system_deps.ipcParentLink;
213566
213869
  exports.isAddonDeploySource = isAddonDeploySource;
213870
+ exports.isArrayBufferCensusReport = require_manifest_system_deps.isArrayBufferCensusReport;
213567
213871
  exports.isArrayOutputSchema = require_dist10.isArrayOutputSchema;
213568
213872
  exports.isClusterSecretMismatchError = isClusterSecretMismatchError;
213569
213873
  exports.isCollectionArrayMethod = require_dist10.isCollectionArrayMethod;
@@ -220368,6 +220672,16 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
220368
220672
  }
220369
220673
  };
220370
220674
  }
220675
+ function describeCustomActions(catalog) {
220676
+ const out = {};
220677
+ for (const [name, spec] of Object.entries(catalog)) out[name] = {
220678
+ kind: spec.kind,
220679
+ auth: spec.auth,
220680
+ scope: spec.scope,
220681
+ ...spec.caller === "required" ? { caller: "required" } : {}
220682
+ };
220683
+ return out;
220684
+ }
220371
220685
  var CustomModelDescriptorSchema = zod.z.object({
220372
220686
  stepId: zod.z.string(),
220373
220687
  entry: ModelCatalogEntrySchema
@@ -225327,6 +225641,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
225327
225641
  "addon-crash-loop",
225328
225642
  "addon-update-available",
225329
225643
  "server-update-available",
225644
+ "wrapper-update-available",
225330
225645
  "alarm-triggered",
225331
225646
  "alarm-armed",
225332
225647
  "alarm-disarmed",
@@ -226184,6 +226499,10 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
226184
226499
  value: "server-update-available",
226185
226500
  label: "Server update available"
226186
226501
  },
226502
+ {
226503
+ value: "wrapper-update-available",
226504
+ label: "Container / app update available"
226505
+ },
226187
226506
  {
226188
226507
  value: "addon-updated",
226189
226508
  label: "Addons updated"
@@ -231656,6 +231975,45 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
231656
231975
  /** One operator-grade sentence: this node runs image X; the contract says Y. */
231657
231976
  message: zod.z.string()
231658
231977
  });
231978
+ var WrapperKindSchema = zod.z.enum([
231979
+ "docker",
231980
+ "electron",
231981
+ "native",
231982
+ "contradictory",
231983
+ "unknown"
231984
+ ]);
231985
+ var WrapperEvidenceSchema = zod.z.object({
231986
+ /** Which probe this row reports. */
231987
+ fact: zod.z.enum([
231988
+ "dockerenv-file",
231989
+ "proc-1-cgroup",
231990
+ "platform",
231991
+ "electron-app-version-env"
231992
+ ]),
231993
+ /**
231994
+ * `observed` — this process read it itself. `declared` — the shell told the
231995
+ * process and nothing here can check it. Only the Electron app version is
231996
+ * ever `declared`, and only because a child cannot see its parent.
231997
+ */
231998
+ mode: zod.z.enum(["observed", "declared"]),
231999
+ /** Did the probe support the fact it names? A `false` row is still evidence. */
232000
+ holds: zod.z.boolean(),
232001
+ /** What was actually read, verbatim enough for an operator to argue with. */
232002
+ detail: zod.z.string()
232003
+ });
232004
+ var WrapperIdentitySchema = zod.z.object({
232005
+ kind: WrapperKindSchema,
232006
+ /** Every probe that ran, in a stable order. Never empty. */
232007
+ evidence: zod.z.array(WrapperEvidenceSchema),
232008
+ /**
232009
+ * The version of the SHELL, when the shell has one it can state: the
232010
+ * Electron app version it declared, or the baked seed closure that
232011
+ * fingerprints a container / app bundle. `null` for `contradictory` and
232012
+ * `unknown` — which version belongs to the shell is precisely what is not
232013
+ * known there.
232014
+ */
232015
+ currentVersion: zod.z.string().nullable()
232016
+ });
231659
232017
  var ServerRollbackInfoSchema = zod.z.object({
231660
232018
  /** The version that failed (or was manually rolled back). */
231661
232019
  fromVersion: zod.z.string(),
@@ -231697,7 +232055,13 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
231697
232055
  * Seed-vs-contract verdict (see {@link ImageContractSchema}). Optional for
231698
232056
  * version skew: an older provider's payload simply omits it.
231699
232057
  */
231700
- imageContract: ImageContractSchema.optional()
232058
+ imageContract: ImageContractSchema.optional(),
232059
+ /**
232060
+ * What this node runs INSIDE (see {@link WrapperIdentitySchema}). Optional
232061
+ * for version skew: an older provider's payload simply omits it, and an
232062
+ * absent wrapper is unknown — never `native`.
232063
+ */
232064
+ wrapper: WrapperIdentitySchema.optional()
231701
232065
  });
231702
232066
  var ServerUpdateCheckResultSchema = zod.z.object({
231703
232067
  packageName: zod.z.string(),
@@ -256648,7 +257012,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
256648
257012
  case "deviceTypes":
256649
257013
  return kind.startsWith("device-") || kind === "detection-blind";
256650
257014
  case "nodeIds":
256651
- return kind.startsWith("node-") || kind === "addon-crash-loop" || kind === "addon-update-available" || kind === "addon-updated" || kind === "server-update-available" || kind === "server-updated";
257015
+ return kind.startsWith("node-") || kind === "addon-crash-loop" || kind === "addon-update-available" || kind === "addon-updated" || kind === "server-update-available" || kind === "wrapper-update-available" || kind === "server-updated";
256652
257016
  case "packageNames":
256653
257017
  return kind.endsWith("update-available") || kind === "addon-updated" || kind === "server-updated";
256654
257018
  }
@@ -259684,6 +260048,9 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
259684
260048
  exports.WidgetMetadataSchema = WidgetMetadataSchema;
259685
260049
  exports.WidgetRemoteSchema = WidgetRemoteSchema;
259686
260050
  exports.WidgetSizeEnum = WidgetSizeEnum;
260051
+ exports.WrapperEvidenceSchema = WrapperEvidenceSchema;
260052
+ exports.WrapperIdentitySchema = WrapperIdentitySchema;
260053
+ exports.WrapperKindSchema = WrapperKindSchema;
259687
260054
  exports.YAMNET_TO_MACRO = YAMNET_TO_MACRO;
259688
260055
  exports.ZoneCrossingDirectionSchema = ZoneCrossingDirectionSchema;
259689
260056
  exports.ZoneCrossingSchema = ZoneCrossingSchema;
@@ -259812,6 +260179,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
259812
260179
  exports.deriveCameraSwitches = deriveCameraSwitches;
259813
260180
  exports.deriveDetailCropRect = deriveDetailCropRect;
259814
260181
  exports.deriveRecordingMode = deriveRecordingMode;
260182
+ exports.describeCustomActions = describeCustomActions;
259815
260183
  exports.describeModelVariant = describeModelVariant;
259816
260184
  exports.detectAccessRole = detectAccessRole;
259817
260185
  exports.detectionPipelineCapability = detectionPipelineCapability;
@@ -260731,6 +261099,7 @@ var require_server_root = __commonJS({
260731
261099
  DEV_UPLOADS_DIRNAME: /* @__PURE__ */ __name(() => DEV_UPLOADS_DIRNAME, "DEV_UPLOADS_DIRNAME"),
260732
261100
  DEV_UPLOADS_KEEP_COUNT: /* @__PURE__ */ __name(() => DEV_UPLOADS_KEEP_COUNT, "DEV_UPLOADS_KEEP_COUNT"),
260733
261101
  DEV_UPLOAD_MANIFEST_FILE: /* @__PURE__ */ __name(() => DEV_UPLOAD_MANIFEST_FILE, "DEV_UPLOAD_MANIFEST_FILE"),
261102
+ ELECTRON_APP_VERSION_ENV: /* @__PURE__ */ __name(() => ELECTRON_APP_VERSION_ENV, "ELECTRON_APP_VERSION_ENV"),
260734
261103
  HOST_EXTERNAL_SPECIFIERS: /* @__PURE__ */ __name(() => HOST_EXTERNAL_SPECIFIERS, "HOST_EXTERNAL_SPECIFIERS"),
260735
261104
  HUB_ROOT_SPEC: /* @__PURE__ */ __name(() => HUB_ROOT_SPEC2, "HUB_ROOT_SPEC"),
260736
261105
  PENDING_ROOT_SWAP_FILE: /* @__PURE__ */ __name(() => PENDING_ROOT_SWAP_FILE, "PENDING_ROOT_SWAP_FILE"),
@@ -260747,6 +261116,7 @@ var require_server_root = __commonJS({
260747
261116
  currentDir: /* @__PURE__ */ __name(() => currentDir, "currentDir"),
260748
261117
  currentEntryPath: /* @__PURE__ */ __name(() => currentEntryPath, "currentEntryPath"),
260749
261118
  detectWorkspaceRoot: /* @__PURE__ */ __name(() => detectWorkspaceRoot, "detectWorkspaceRoot"),
261119
+ detectWrapperIdentity: /* @__PURE__ */ __name(() => detectWrapperIdentity, "detectWrapperIdentity"),
260750
261120
  devChannelEpoch: /* @__PURE__ */ __name(() => devChannelEpoch, "devChannelEpoch"),
260751
261121
  devUploadManifestPath: /* @__PURE__ */ __name(() => devUploadManifestPath, "devUploadManifestPath"),
260752
261122
  devUploadVersionDir: /* @__PURE__ */ __name(() => devUploadVersionDir, "devUploadVersionDir"),
@@ -260764,6 +261134,7 @@ var require_server_root = __commonJS({
260764
261134
  readPendingRootSwap: /* @__PURE__ */ __name(() => readPendingRootSwap, "readPendingRootSwap"),
260765
261135
  readRestartIntentMarker: /* @__PURE__ */ __name(() => readRestartIntentMarker, "readRestartIntentMarker"),
260766
261136
  readServerRootState: /* @__PURE__ */ __name(() => readServerRootState, "readServerRootState"),
261137
+ readWrapperProbes: /* @__PURE__ */ __name(() => readWrapperProbes, "readWrapperProbes"),
260767
261138
  registerActiveRootResolver: /* @__PURE__ */ __name(() => registerActiveRootResolver, "registerActiveRootResolver"),
260768
261139
  restartIntentMarkerPath: /* @__PURE__ */ __name(() => restartIntentMarkerPath, "restartIntentMarkerPath"),
260769
261140
  rootEntryPath: /* @__PURE__ */ __name(() => rootEntryPath2, "rootEntryPath"),
@@ -261443,6 +261814,93 @@ var require_server_root = __commonJS({
261443
261814
  };
261444
261815
  }
261445
261816
  __name(assessImageContract, "assessImageContract");
261817
+ var ELECTRON_APP_VERSION_ENV = "CAMSTACK_AGENT_APP_VERSION";
261818
+ var CONTAINER_CGROUP_MARKERS = [
261819
+ "docker",
261820
+ "containerd",
261821
+ "kubepods",
261822
+ "podman"
261823
+ ];
261824
+ function cgroupNamesAContainer(contents) {
261825
+ const lower = contents.toLowerCase();
261826
+ return CONTAINER_CGROUP_MARKERS.some((marker) => lower.includes(marker));
261827
+ }
261828
+ __name(cgroupNamesAContainer, "cgroupNamesAContainer");
261829
+ function cgroupDetail(contents) {
261830
+ const lines = contents.split("\n").filter((line) => line.trim().length > 0);
261831
+ const named = lines.find((line) => cgroupNamesAContainer(line));
261832
+ return (named ?? lines[0] ?? "").trim().slice(0, 200);
261833
+ }
261834
+ __name(cgroupDetail, "cgroupDetail");
261835
+ function detectWrapperIdentity(inputs) {
261836
+ const { platform, dockerEnvFileExists, proc1Cgroup, electronAppVersion, seedVersion } = inputs;
261837
+ const linux = platform === "linux";
261838
+ const cgroupAnswered = proc1Cgroup !== null;
261839
+ const cgroupSaysContainer = proc1Cgroup !== null && cgroupNamesAContainer(proc1Cgroup);
261840
+ const containerObserved = dockerEnvFileExists || cgroupSaysContainer;
261841
+ const electronDeclared = electronAppVersion !== void 0 && electronAppVersion.length > 0;
261842
+ const containerRuledOut = !containerObserved && (cgroupAnswered || !linux);
261843
+ const evidence = [
261844
+ {
261845
+ fact: "platform",
261846
+ mode: "observed",
261847
+ holds: linux,
261848
+ detail: linux ? `process.platform=${platform} \u2014 a container is possible here` : `process.platform=${platform} \u2014 a linux container cannot run on this platform`
261849
+ },
261850
+ {
261851
+ fact: "dockerenv-file",
261852
+ mode: "observed",
261853
+ holds: dockerEnvFileExists,
261854
+ detail: dockerEnvFileExists ? "/.dockerenv exists" : "/.dockerenv not present"
261855
+ },
261856
+ {
261857
+ fact: "proc-1-cgroup",
261858
+ mode: "observed",
261859
+ holds: cgroupSaysContainer,
261860
+ detail: proc1Cgroup === null ? "/proc/1/cgroup unreadable \u2014 this probe did NOT answer" : cgroupDetail(proc1Cgroup)
261861
+ },
261862
+ {
261863
+ fact: "electron-app-version-env",
261864
+ mode: "declared",
261865
+ holds: electronDeclared,
261866
+ detail: electronDeclared ? `CAMSTACK_AGENT_APP_VERSION=${String(electronAppVersion)}` : "CAMSTACK_AGENT_APP_VERSION not set"
261867
+ }
261868
+ ];
261869
+ if (containerObserved && electronDeclared) {
261870
+ return {
261871
+ kind: "contradictory",
261872
+ evidence,
261873
+ currentVersion: null
261874
+ };
261875
+ }
261876
+ if (containerObserved) {
261877
+ return {
261878
+ kind: "docker",
261879
+ evidence,
261880
+ currentVersion: seedVersion
261881
+ };
261882
+ }
261883
+ if (electronDeclared) {
261884
+ return {
261885
+ kind: "electron",
261886
+ evidence,
261887
+ currentVersion: electronAppVersion ?? null
261888
+ };
261889
+ }
261890
+ if (containerRuledOut) {
261891
+ return {
261892
+ kind: "native",
261893
+ evidence,
261894
+ currentVersion: seedVersion
261895
+ };
261896
+ }
261897
+ return {
261898
+ kind: "unknown",
261899
+ evidence,
261900
+ currentVersion: null
261901
+ };
261902
+ }
261903
+ __name(detectWrapperIdentity, "detectWrapperIdentity");
261446
261904
  var fs4 = __toESM2(__require("fs"));
261447
261905
  var path4 = __toESM2(__require("path"));
261448
261906
  function detectWorkspaceRoot(fromDir) {
@@ -261584,6 +262042,26 @@ var require_server_root = __commonJS({
261584
262042
  var NPM_INSTALL_TIMEOUT_MS = 15 * 6e4;
261585
262043
  var RESTART_REASON_PREFIX = "server-update";
261586
262044
  var STALE_TRANSIENT_MS = 24 * 60 * 60 * 1e3;
262045
+ function readWrapperProbes() {
262046
+ let proc1Cgroup = null;
262047
+ try {
262048
+ proc1Cgroup = fs6.readFileSync("/proc/1/cgroup", "utf-8");
262049
+ } catch {
262050
+ proc1Cgroup = null;
262051
+ }
262052
+ let dockerEnvFileExists = false;
262053
+ try {
262054
+ dockerEnvFileExists = fs6.existsSync("/.dockerenv");
262055
+ } catch {
262056
+ dockerEnvFileExists = false;
262057
+ }
262058
+ return {
262059
+ platform: process.platform,
262060
+ dockerEnvFileExists,
262061
+ proc1Cgroup
262062
+ };
262063
+ }
262064
+ __name(readWrapperProbes, "readWrapperProbes");
261587
262065
  function readPackageVersion(pkgJsonPath) {
261588
262066
  try {
261589
262067
  const raw = JSON.parse(fs6.readFileSync(pkgJsonPath, "utf-8"));
@@ -261610,6 +262088,7 @@ var require_server_root = __commonJS({
261610
262088
  execNpm;
261611
262089
  ensureNativePrebuildsFn;
261612
262090
  env;
262091
+ readWrapperProbesFn;
261613
262092
  now;
261614
262093
  runningPackageJsonPath;
261615
262094
  workspaceProbeDir;
@@ -261637,6 +262116,7 @@ var require_server_root = __commonJS({
261637
262116
  });
261638
262117
  this.ensureNativePrebuildsFn = options.ensureNativePrebuilds ?? (async () => void 0);
261639
262118
  this.env = options.env ?? process.env;
262119
+ this.readWrapperProbesFn = options.readWrapperProbes ?? readWrapperProbes;
261640
262120
  this.now = options.now ?? Date.now;
261641
262121
  this.runningPackageJsonPath = options.runningPackageJsonPath;
261642
262122
  this.workspaceProbeDir = options.workspaceProbeDir;
@@ -261662,6 +262142,20 @@ var require_server_root = __commonJS({
261662
262142
  if (seedDir === void 0 || seedDir.length === 0) return null;
261663
262143
  return readPackageVersion(path6.join(seedDir, "package.json"));
261664
262144
  }
262145
+ /**
262146
+ * What this node runs INSIDE. Observed where it can be (the container
262147
+ * probes), declared only where it cannot (the Electron app version, which a
262148
+ * child process has no other way to learn). A contradiction between the two
262149
+ * is reported as `contradictory`, never resolved — see `wrapper-identity.ts`
262150
+ * and D437.
262151
+ */
262152
+ wrapperIdentity() {
262153
+ return detectWrapperIdentity({
262154
+ ...this.readWrapperProbesFn(),
262155
+ electronAppVersion: this.env[ELECTRON_APP_VERSION_ENV],
262156
+ seedVersion: this.seedVersion()
262157
+ });
262158
+ }
261665
262159
  rootDir() {
261666
262160
  return serverRootDir(this.dataDir);
261667
262161
  }
@@ -261700,7 +262194,11 @@ var require_server_root = __commonJS({
261700
262194
  seedVersion: this.seedVersion(),
261701
262195
  latestVersion,
261702
262196
  runningVersion
261703
- })
262197
+ }),
262198
+ // The other half of the same question. `imageContract` says the shell is
262199
+ // BEHIND; this says WHAT the shell is, which is the only thing that can
262200
+ // turn that verdict into an instruction the operator can act on.
262201
+ wrapper: this.wrapperIdentity()
261704
262202
  };
261705
262203
  }
261706
262204
  // ── Check ─────────────────────────────────────────────────────────────
@@ -393072,6 +393570,7 @@ var require_update_availability_emitter = __commonJS({
393072
393570
  nodeId: scope
393073
393571
  }));
393074
393572
  const head = list[0];
393573
+ const wrapperKind = head.wrapperKind;
393075
393574
  this.eventBus.emit({
393076
393575
  id: `update.available:${target}:${scope}:${packages.map((pkg) => `${pkg.packageName}@${pkg.latestVersion}`).toSorted().join(",")}`,
393077
393576
  timestamp: /* @__PURE__ */ new Date(),
@@ -393084,7 +393583,8 @@ var require_update_availability_emitter = __commonJS({
393084
393583
  latestVersion: head.latestVersion,
393085
393584
  packages,
393086
393585
  nodeId: scope,
393087
- nodeIds: [scope]
393586
+ nodeIds: [scope],
393587
+ ...wrapperKind !== void 0 ? { wrapperKind } : {}
393088
393588
  }
393089
393589
  });
393090
393590
  }
@@ -409851,6 +410351,7 @@ var require_update_check_scheduler = __commonJS({
409851
410351
  continue;
409852
410352
  }
409853
410353
  await this.attempt("node server package", node.id, () => this.targets.checkNodeServerUpdate(node.id, node.isHub));
410354
+ await this.attempt("node wrapper", node.id, () => this.targets.checkWrapperUpdate(node.id, node.isHub));
409854
410355
  if (node.isHub)
409855
410356
  continue;
409856
410357
  await this.attempt("agent addon packages", node.id, () => this.targets.checkAgentAddons(node.id));
@@ -424401,6 +424902,161 @@ var require_ws_request_census = __commonJS({
424401
424902
  }
424402
424903
  });
424403
424904
 
424905
+ // ../../server/backend/dist/api/trpc/ws-slow-consumer-guard.js
424906
+ var require_ws_slow_consumer_guard = __commonJS({
424907
+ "../../server/backend/dist/api/trpc/ws-slow-consumer-guard.js"(exports) {
424908
+ "use strict";
424909
+ Object.defineProperty(exports, "__esModule", { value: true });
424910
+ exports.WsSlowConsumerGuard = exports.WS_SLOW_CONSUMER_POLL_MS = exports.WS_SLOW_CONSUMER_GRACE_MS = exports.WS_SLOW_CONSUMER_MAX_BUFFERED_BYTES = void 0;
424911
+ exports.wsConnectionFacts = wsConnectionFacts;
424912
+ var client_ip_js_1 = require_client_ip();
424913
+ exports.WS_SLOW_CONSUMER_MAX_BUFFERED_BYTES = 16 * 1024 * 1024;
424914
+ exports.WS_SLOW_CONSUMER_GRACE_MS = 3e4;
424915
+ exports.WS_SLOW_CONSUMER_POLL_MS = 5e3;
424916
+ var ABNORMAL_CLOSURE = 1006;
424917
+ var UNIDENTIFIED = "(unidentified)";
424918
+ var WsSlowConsumerGuard = class {
424919
+ connections = /* @__PURE__ */ new Set();
424920
+ bySocket = /* @__PURE__ */ new WeakMap();
424921
+ logger;
424922
+ maxBufferedBytes;
424923
+ graceMs;
424924
+ pollMs;
424925
+ now;
424926
+ timer = null;
424927
+ constructor(options) {
424928
+ this.logger = options.logger;
424929
+ this.maxBufferedBytes = options.maxBufferedBytes ?? exports.WS_SLOW_CONSUMER_MAX_BUFFERED_BYTES;
424930
+ this.graceMs = options.graceMs ?? exports.WS_SLOW_CONSUMER_GRACE_MS;
424931
+ this.pollMs = options.pollMs ?? exports.WS_SLOW_CONSUMER_POLL_MS;
424932
+ this.now = options.now ?? (() => Date.now());
424933
+ }
424934
+ /** Watch one connection until it closes. */
424935
+ attach(socket, facts) {
424936
+ const connection = {
424937
+ socket,
424938
+ facts,
424939
+ openedAt: this.now(),
424940
+ principal: UNIDENTIFIED,
424941
+ overSince: null,
424942
+ peakBufferedBytes: 0,
424943
+ terminated: false
424944
+ };
424945
+ this.connections.add(connection);
424946
+ this.bySocket.set(socket, connection);
424947
+ socket.once("close", (code, reason) => {
424948
+ this.connections.delete(connection);
424949
+ this.logger.info("tRPC WS connection closed", {
424950
+ meta: {
424951
+ ...this.describe(connection),
424952
+ code,
424953
+ reason: reason.length > 0 ? reason.toString("utf8") : "",
424954
+ closedBy: connection.terminated ? "slow-consumer-guard" : code === ABNORMAL_CLOSURE ? "peer gone or keep-alive PING unanswered" : "peer"
424955
+ }
424956
+ });
424957
+ });
424958
+ }
424959
+ /** Name the connection's principal once the WS context resolved it. */
424960
+ identify(socket, principal) {
424961
+ const connection = this.bySocket.get(socket);
424962
+ if (connection !== void 0)
424963
+ connection.principal = principal;
424964
+ }
424965
+ /** Read every queue once. Exposed for the spec; production runs it on {@link start}. */
424966
+ poll() {
424967
+ const at = this.now();
424968
+ for (const connection of this.connections) {
424969
+ try {
424970
+ this.judge(connection, at);
424971
+ } catch (err) {
424972
+ this.logger.warn("tRPC WS slow-consumer guard could not read a socket", {
424973
+ meta: { ...this.describe(connection), error: errorMessage(err) }
424974
+ });
424975
+ }
424976
+ }
424977
+ }
424978
+ start() {
424979
+ if (this.timer !== null)
424980
+ return;
424981
+ this.timer = setInterval(() => this.poll(), this.pollMs);
424982
+ this.timer.unref?.();
424983
+ }
424984
+ stop() {
424985
+ if (this.timer === null)
424986
+ return;
424987
+ clearInterval(this.timer);
424988
+ this.timer = null;
424989
+ }
424990
+ judge(connection, at) {
424991
+ if (connection.terminated)
424992
+ return;
424993
+ const buffered = connection.socket.bufferedAmount;
424994
+ if (buffered > connection.peakBufferedBytes)
424995
+ connection.peakBufferedBytes = buffered;
424996
+ if (buffered <= this.maxBufferedBytes) {
424997
+ if (connection.overSince !== null) {
424998
+ this.logger.info("tRPC WS client drained a backlog that had crossed the bound", {
424999
+ meta: {
425000
+ ...this.describe(connection),
425001
+ overForMs: at - connection.overSince,
425002
+ bufferedBytes: buffered
425003
+ }
425004
+ });
425005
+ connection.overSince = null;
425006
+ connection.peakBufferedBytes = buffered;
425007
+ }
425008
+ return;
425009
+ }
425010
+ connection.overSince ??= at;
425011
+ const overForMs = at - connection.overSince;
425012
+ if (overForMs < this.graceMs)
425013
+ return;
425014
+ connection.terminated = true;
425015
+ this.logger.warn("tRPC WS client disconnected \u2014 slow consumer: it stopped reading and its send queue stayed above the bound for the whole grace period (a backgrounded viewer keeps the socket open with its JS suspended; the queue lived in hub-main old_space)", {
425016
+ meta: {
425017
+ ...this.describe(connection),
425018
+ bufferedBytes: buffered,
425019
+ overForMs,
425020
+ boundBytes: this.maxBufferedBytes,
425021
+ graceMs: this.graceMs
425022
+ }
425023
+ });
425024
+ connection.socket.terminate();
425025
+ }
425026
+ describe(connection) {
425027
+ return {
425028
+ ip: connection.facts.ip ?? UNIDENTIFIED,
425029
+ userAgent: connection.facts.userAgent ?? UNIDENTIFIED,
425030
+ principal: connection.principal,
425031
+ bytesRead: safeCount(connection.facts.bytesRead),
425032
+ bytesWritten: safeCount(connection.facts.bytesWritten),
425033
+ peakBufferedBytes: connection.peakBufferedBytes,
425034
+ connectionAgeMs: this.now() - connection.openedAt
425035
+ };
425036
+ }
425037
+ };
425038
+ exports.WsSlowConsumerGuard = WsSlowConsumerGuard;
425039
+ function safeCount(read) {
425040
+ try {
425041
+ return read();
425042
+ } catch {
425043
+ return null;
425044
+ }
425045
+ }
425046
+ function errorMessage(err) {
425047
+ return err instanceof Error ? err.message : String(err);
425048
+ }
425049
+ function wsConnectionFacts(req) {
425050
+ return {
425051
+ ip: (0, client_ip_js_1.extractClientIp)(req),
425052
+ userAgent: (0, client_ip_js_1.extractUserAgent)(req),
425053
+ bytesRead: () => req.socket.bytesRead,
425054
+ bytesWritten: () => req.socket.bytesWritten
425055
+ };
425056
+ }
425057
+ }
425058
+ });
425059
+
424404
425060
  // ../../server/backend/dist/auth/addon-route-jwt-gate.js
424405
425061
  var require_addon_route_jwt_gate = __commonJS({
424406
425062
  "../../server/backend/dist/auth/addon-route-jwt-gate.js"(exports) {
@@ -425270,6 +425926,58 @@ var require_integration_visibility = __commonJS({
425270
425926
  }
425271
425927
  });
425272
425928
 
425929
+ // ../../server/backend/dist/core/addon/forked-custom-actions.js
425930
+ var require_forked_custom_actions = __commonJS({
425931
+ "../../server/backend/dist/core/addon/forked-custom-actions.js"(exports) {
425932
+ "use strict";
425933
+ Object.defineProperty(exports, "__esModule", { value: true });
425934
+ exports.toForkedCustomActionsSpec = toForkedCustomActionsSpec;
425935
+ exports.childCatalogsFingerprint = childCatalogsFingerprint;
425936
+ exports.summarizeChildCatalogs = summarizeChildCatalogs;
425937
+ var zod_1 = require_zod();
425938
+ function toForkedCustomActionsSpec(descriptor) {
425939
+ const spec = {};
425940
+ for (const [name, action] of Object.entries(descriptor)) {
425941
+ spec[name] = {
425942
+ input: zod_1.z.unknown(),
425943
+ output: zod_1.z.unknown(),
425944
+ kind: action.kind,
425945
+ auth: action.auth,
425946
+ scope: action.scope,
425947
+ ...action.caller === "required" ? { caller: "required" } : {}
425948
+ };
425949
+ }
425950
+ return spec;
425951
+ }
425952
+ function childCatalogsFingerprint(catalogs) {
425953
+ return JSON.stringify(sortKeysDeep(catalogs));
425954
+ }
425955
+ function sortKeysDeep(value) {
425956
+ if (Array.isArray(value))
425957
+ return value.map(sortKeysDeep);
425958
+ if (value === null || typeof value !== "object")
425959
+ return value;
425960
+ const out = {};
425961
+ for (const key of Object.keys(value).sort()) {
425962
+ out[key] = sortKeysDeep(Reflect.get(value, key));
425963
+ }
425964
+ return out;
425965
+ }
425966
+ function summarizeChildCatalogs(catalogs) {
425967
+ const withActions = [];
425968
+ const withoutActions = [];
425969
+ for (const addonId of Object.keys(catalogs).sort()) {
425970
+ const count = Object.keys(catalogs[addonId] ?? {}).length;
425971
+ if (count > 0)
425972
+ withActions.push(`${addonId}:${count}`);
425973
+ else
425974
+ withoutActions.push(addonId);
425975
+ }
425976
+ return { withActions, withoutActions };
425977
+ }
425978
+ }
425979
+ });
425980
+
425273
425981
  // ../../server/backend/dist/core/addon/prune-misplaced-addons.js
425274
425982
  var require_prune_misplaced_addons = __commonJS({
425275
425983
  "../../server/backend/dist/core/addon/prune-misplaced-addons.js"(exports) {
@@ -425364,49 +426072,6 @@ var require_prune_misplaced_addons = __commonJS({
425364
426072
  }
425365
426073
  });
425366
426074
 
425367
- // ../../server/backend/dist/core/addon/require-cache.js
425368
- var require_require_cache = __commonJS({
425369
- "../../server/backend/dist/core/addon/require-cache.js"(exports) {
425370
- "use strict";
425371
- var __importDefault = exports && exports.__importDefault || function(mod) {
425372
- return mod && mod.__esModule ? mod : { "default": mod };
425373
- };
425374
- Object.defineProperty(exports, "__esModule", { value: true });
425375
- exports.purgeRequireCacheUnder = purgeRequireCacheUnder;
425376
- exports.bundleGraphRootFor = bundleGraphRootFor;
425377
- var node_fs_1 = __importDefault(__require("fs"));
425378
- var node_path_1 = __importDefault(__require("path"));
425379
- function purgeRequireCacheUnder(dir) {
425380
- try {
425381
- const cache2 = __require.cache;
425382
- if (cache2 === void 0)
425383
- return;
425384
- const prefixes = candidatePrefixes(dir);
425385
- for (const key of Object.keys(cache2)) {
425386
- if (prefixes.some((prefix) => key.startsWith(prefix)))
425387
- delete cache2[key];
425388
- }
425389
- } catch {
425390
- }
425391
- }
425392
- function bundleGraphRootFor(addonDir, entryPath) {
425393
- const distDir = node_path_1.default.resolve(addonDir, "dist");
425394
- const withSep = distDir.endsWith(node_path_1.default.sep) ? distDir : `${distDir}${node_path_1.default.sep}`;
425395
- return entryPath.startsWith(withSep) ? distDir : node_path_1.default.dirname(entryPath);
425396
- }
425397
- function candidatePrefixes(dir) {
425398
- const withSep = (d) => d.endsWith(node_path_1.default.sep) ? d : `${d}${node_path_1.default.sep}`;
425399
- const given = withSep(dir);
425400
- let real = given;
425401
- try {
425402
- real = withSep(node_fs_1.default.realpathSync(dir));
425403
- } catch {
425404
- }
425405
- return real === given ? [given] : [given, real];
425406
- }
425407
- }
425408
- });
425409
-
425410
426075
  // ../../server/backend/dist/core/addon/runner-convergence.js
425411
426076
  var require_runner_convergence = __commonJS({
425412
426077
  "../../server/backend/dist/core/addon/runner-convergence.js"(exports) {
@@ -425525,7 +426190,6 @@ var require_addon_registry_service = __commonJS({
425525
426190
  var fs = __importStar(__require("fs"));
425526
426191
  var os = __importStar(__require("os"));
425527
426192
  var path = __importStar(__require("path"));
425528
- var node_url_1 = __require("url");
425529
426193
  var system_1 = require_dist3();
425530
426194
  var types_1 = require_dist4();
425531
426195
  var client_1 = require_dist2();
@@ -425535,8 +426199,8 @@ var require_addon_registry_service = __commonJS({
425535
426199
  var device_meta_mirror_js_1 = require_device_meta_mirror();
425536
426200
  var integration_visibility_js_1 = require_integration_visibility();
425537
426201
  var package_dir_utils_1 = require_package_dir_utils();
426202
+ var forked_custom_actions_js_1 = require_forked_custom_actions();
425538
426203
  var prune_misplaced_addons_js_1 = require_prune_misplaced_addons();
425539
- var require_cache_js_1 = require_require_cache();
425540
426204
  var runner_convergence_1 = require_runner_convergence();
425541
426205
  var runner_spawn_fanout_js_1 = require_runner_spawn_fanout();
425542
426206
  function shouldEvictMissingOnDisk(entry, id, onDiskIds) {
@@ -425682,10 +426346,14 @@ var require_addon_registry_service = __commonJS({
425682
426346
  // through this from the `api.addons.custom` tRPC procedure.
425683
426347
  customActionRegistry = new system_1.CustomActionRegistry();
425684
426348
  /**
425685
- * Keeps `purgeRequireCacheUnder` + `import()` atomic per addon directory now
425686
- * that the boot runner plan overlaps (D171). See `runner-spawn-fanout.ts`.
426349
+ * Fingerprint of the custom-action catalogs each UDS child last DESCRIBED
426350
+ * (keyed by child id), and the addon ids registered from that description.
426351
+ * A re-register carrying the same catalogs is a no-op with no line; a child
426352
+ * whose new description drops an addon has that addon's actions withdrawn.
426353
+ * See {@link applyChildCustomActions}.
425687
426354
  */
425688
- moduleLoadSerializer = (0, runner_spawn_fanout_js_1.createKeyedSerializer)();
426355
+ childCatalogFingerprints = /* @__PURE__ */ new Map();
426356
+ childCatalogAddons = /* @__PURE__ */ new Map();
425689
426357
  /**
425690
426358
  * AddonIds whose group-runner disconnect is operator-initiated (update /
425691
426359
  * restart / uninstall). The Moleculer `$node.disconnected` handler skips
@@ -427289,6 +427957,7 @@ var require_addon_registry_service = __commonJS({
427289
427957
  wireCapabilityConsumers() {
427290
427958
  if (!this.capabilityRegistry)
427291
427959
  return;
427960
+ this.moleculer.onChildCustomActions((child) => this.applyChildCustomActions(child));
427292
427961
  this.eventBusService.subscribe({ category: "capability:provider-registered" }, (event) => {
427293
427962
  const rawCapability = event.data["capability"];
427294
427963
  const rawAddonId = event.data["addonId"];
@@ -427852,7 +428521,6 @@ var require_addon_registry_service = __commonJS({
427852
428521
  throw new Error(`Failed to spawn runner "${runnerId}" (${addons.length} addons): ${(0, types_1.errMsg)(err)}`, { cause: err });
427853
428522
  }
427854
428523
  for (const { addonId } of addons) {
427855
- await this.registerForkedAddonCustomActions(addonId, runnerId);
427856
428524
  const entry = this.addonEntries.get(addonId);
427857
428525
  if (entry) {
427858
428526
  entry.initialized = true;
@@ -427933,86 +428601,72 @@ var require_addon_registry_service = __commonJS({
427933
428601
  this.recentGroupRespawns.set(runnerId, Date.now());
427934
428602
  }
427935
428603
  for (const { addonId: memberId } of roster) {
427936
- await this.registerForkedAddonCustomActions(memberId, runnerId);
427937
428604
  const memberEntry = this.addonEntries.get(memberId);
427938
428605
  if (memberEntry)
427939
428606
  memberEntry.initialized = true;
427940
428607
  }
427941
428608
  }
427942
428609
  /**
427943
- * (Re-)register the custom-action catalog for a forked / group-hosted
427944
- * addon against the shared `CustomActionRegistry`.
427945
- *
427946
- * The catalog (zod `input`/`output` specs) is read STATICALLY from the
427947
- * addon module's `customActions` named export — the handler dispatches
427948
- * over UDS via `LocalChildRegistry.callAddonOnChild(addonId,
427949
- * {target:'custom', action, args})` (F3 — replaces the removed per-addon
427950
- * Moleculer `custom.<action>` action), so the only divergence vs an
427951
- * in-process addon is the transport, exactly like cap methods. The hub's
427952
- * `CustomActionRegistry` validates input/output around this dispatch.
427953
- *
427954
- * Why a fresh import: `this.addonLoader`'s `module` namespace is
427955
- * captured once at boot. After a hot-update (`installFromTgz` →
427956
- * `restartAddon`) the on-disk bundle is newer than that cached module,
427957
- * so re-reading the boot-time `module` would register a STALE catalog
427958
- * (or none at all, if the addon was first installed after boot). We
427959
- * re-`import()` the entry with a cache-busting query so Node's ESM
427960
- * loader hands back the current bundle.
427961
- *
427962
- * Idempotent: drops any prior registration first. No-op (with a debug
427963
- * log) when the addon exports no `customActions` — most addons don't.
427964
- */
427965
- async registerForkedAddonCustomActions(addonId, runnerId) {
427966
- this.customActionRegistry.unregisterAddon(addonId);
427967
- const entry = this.addonEntries.get(addonId);
427968
- const addonDir = entry?.addonDir;
427969
- const declarationEntry = entry?.declaration?.entry;
427970
- if (!addonDir || !declarationEntry)
428610
+ * Apply the custom-action catalogs a UDS child DESCRIBED in its register
428611
+ * frame (`RegisteredChild.customActions`, schemas stripped) to the hub-wide
428612
+ * `CustomActionRegistry`, dispatching each action back to that child over
428613
+ * the addon-call plane.
428614
+ *
428615
+ * ## Why the child describes, and the hub never reads the bundle
428616
+ *
428617
+ * Until D444 this was `registerForkedAddonCustomActions`: purge the require
428618
+ * cache, re-import the addon's entry INSIDE hub-main, read its
428619
+ * `customActions` export. Measured 2026-09-10 on the live hub: 9.3 MB of
428620
+ * `addon-pipeline-orchestrator/dist/index.js` evaluated in hub-main by a
428621
+ * sampling heap profile, ten deploys in the process's 23 h life, NONE of
428622
+ * which exported a catalog — and the only line saying so was at `debug`,
428623
+ * which the hub does not write. Every evaluation pins whatever module-scope
428624
+ * state the evicted graph left behind, and the bundle-reload guard existed
428625
+ * only to keep that re-execution survivable (D284). The runner is the
428626
+ * process that owns the addon; it describes its catalog once its init loop
428627
+ * has produced it, in the post-init register frame, and validates the
428628
+ * action's input and output itself (`child-addon-call-dispatch.ts`).
428629
+ *
428630
+ * ## What one description does
428631
+ *
428632
+ * - identical to the child's last description → nothing, silently (a
428633
+ * native-cap change or reconnect re-sends the same frame);
428634
+ * - otherwise every addon in it is (re-)registered — an EMPTY catalog
428635
+ * withdraws that addon's actions — and an addon the previous description
428636
+ * named but this one does not is withdrawn too;
428637
+ * - and ONE `info` line per applied description names what it produced,
428638
+ * including "nothing": a load that produces nothing and says nothing is
428639
+ * the branch D391 forbids.
428640
+ */
428641
+ applyChildCustomActions(child) {
428642
+ const catalogs = child.customActions;
428643
+ if (catalogs === void 0)
427971
428644
  return;
427972
- const entryFile = declarationEntry.replace(/^\.\//, "").replace(/^src\//, "dist/").replace(/\.ts$/, ".js");
427973
- let entryPath = path.resolve(addonDir, entryFile);
427974
- if (!fs.existsSync(entryPath)) {
427975
- const base = entryPath.replace(/\.(js|cjs|mjs)$/, "");
427976
- const alternatives = [
427977
- `${base}.cjs`,
427978
- `${base}.mjs`,
427979
- path.resolve(addonDir, "dist", "index.js"),
427980
- path.resolve(addonDir, "dist", "index.cjs"),
427981
- path.resolve(addonDir, "dist", "index.mjs"),
427982
- path.resolve(addonDir, declarationEntry)
427983
- ];
427984
- entryPath = alternatives.find((p) => fs.existsSync(p)) ?? entryPath;
427985
- }
427986
- if (!fs.existsSync(entryPath))
427987
- return;
427988
- let catalog;
427989
- try {
427990
- const graphRoot = (0, require_cache_js_1.bundleGraphRootFor)(addonDir, entryPath);
427991
- const modUnknown = await this.moduleLoadSerializer.run(graphRoot, async () => {
427992
- (0, require_cache_js_1.purgeRequireCacheUnder)(graphRoot);
427993
- const cacheBustedUrl = `${(0, node_url_1.pathToFileURL)(entryPath).href}?t=${Date.now()}`;
427994
- const nativeImport = new Function("specifier", "return import(specifier)");
427995
- return nativeImport(cacheBustedUrl);
427996
- });
427997
- catalog = modUnknown && typeof modUnknown === "object" ? modUnknown["customActions"] : void 0;
427998
- } catch (err) {
427999
- this.logger.warn("Failed to load custom-action catalog for forked addon", {
428000
- tags: { addonId },
428001
- meta: { error: (0, types_1.errMsg)(err) }
428002
- });
428003
- return;
428004
- }
428005
- if (!catalog || typeof catalog !== "object") {
428006
- this.logger.debug("Forked addon exports no custom actions", {
428007
- tags: { addonId },
428008
- meta: { runnerId }
428009
- });
428645
+ const fingerprint = (0, forked_custom_actions_js_1.childCatalogsFingerprint)(catalogs);
428646
+ if (this.childCatalogFingerprints.get(child.childId) === fingerprint)
428010
428647
  return;
428648
+ this.childCatalogFingerprints.set(child.childId, fingerprint);
428649
+ const described = new Set(Object.keys(catalogs));
428650
+ for (const previous of this.childCatalogAddons.get(child.childId) ?? []) {
428651
+ if (!described.has(previous))
428652
+ this.customActionRegistry.unregisterAddon(previous);
428653
+ }
428654
+ this.childCatalogAddons.set(child.childId, described);
428655
+ for (const [addonId, descriptor] of Object.entries(catalogs)) {
428656
+ if (Object.keys(descriptor).length === 0) {
428657
+ this.customActionRegistry.unregisterAddon(addonId);
428658
+ continue;
428659
+ }
428660
+ this.customActionRegistry.registerAddon(addonId, (0, forked_custom_actions_js_1.toForkedCustomActionsSpec)(descriptor), (action, input, caller) => this.dispatchForkedCustomAction(addonId, action, input, caller));
428011
428661
  }
428012
- this.customActionRegistry.registerAddon(addonId, catalog, (action, input, caller) => this.dispatchForkedCustomAction(addonId, action, input, caller));
428013
- this.logger.info("Runner addon custom actions registered", {
428014
- tags: { addonId },
428015
- meta: { runnerId }
428662
+ const summary = (0, forked_custom_actions_js_1.summarizeChildCatalogs)(catalogs);
428663
+ this.logger.info("UDS child custom-action catalogs applied", {
428664
+ meta: {
428665
+ childId: child.childId,
428666
+ incarnation: child.incarnation,
428667
+ registered: summary.withActions.length > 0 ? summary.withActions.join(",") : "(none)",
428668
+ withoutActions: summary.withoutActions.length > 0 ? summary.withoutActions.join(",") : "(none)"
428669
+ }
428016
428670
  });
428017
428671
  }
428018
428672
  /**
@@ -429982,6 +430636,15 @@ var require_moleculer_service = __commonJS({
429982
430636
  * See {@link ChildManifestGate}.
429983
430637
  */
429984
430638
  childManifestGate = new ChildManifestGate();
430639
+ /**
430640
+ * Receives every child register frame that DESCRIBES its custom-action
430641
+ * catalogs (the post-init one and every re-register after it). Consulted
430642
+ * BEFORE {@link ChildManifestGate}: the gate compares caps, and a post-init
430643
+ * register of a non-device runner carries the same caps as its pre-init one
430644
+ * — skipping it would lose the only frame that carries the catalogs (D444).
430645
+ * The consumer (`AddonRegistryService`) dedupes by catalog fingerprint.
430646
+ */
430647
+ childCustomActionsHandler = null;
429985
430648
  /**
429986
430649
  * Fixed-period agent-readiness snapshot sweep (D8 reconcile) — repairs
429987
430650
  * agent-origin readiness deltas lost while the agent stayed connected.
@@ -430059,6 +430722,14 @@ var require_moleculer_service = __commonJS({
430059
430722
  get childRegistry() {
430060
430723
  return this.localChildRegistry;
430061
430724
  }
430725
+ /**
430726
+ * Register the (single) handler for a child's described custom-action
430727
+ * catalogs. See {@link childCustomActionsHandler}. Called once by
430728
+ * `AddonRegistryService` when it wires its consumers.
430729
+ */
430730
+ onChildCustomActions(handler) {
430731
+ this.childCustomActionsHandler = handler;
430732
+ }
430062
430733
  /** The CapRouteResolver once onModuleInit has completed; null before that. */
430063
430734
  get capRouteResolver() {
430064
430735
  return this.resolver;
@@ -430311,6 +430982,15 @@ var require_moleculer_service = __commonJS({
430311
430982
  registry.onChildRegistered((child) => {
430312
430983
  const hubNodeId = this.brokerSafe.nodeID;
430313
430984
  const childNodeId = `${hubNodeId}/${child.childId}`;
430985
+ if (child.customActions !== void 0) {
430986
+ try {
430987
+ this.childCustomActionsHandler?.(child);
430988
+ } catch (err) {
430989
+ logger.warn("UDS child custom-action catalogs could not be applied", {
430990
+ meta: { nodeId: childNodeId, incarnation: child.incarnation, error: (0, types_1.errMsg)(err) }
430991
+ });
430992
+ }
430993
+ }
430314
430994
  const params = buildChildUdsManifest(childNodeId, child.childId, child.caps);
430315
430995
  if (!this.childManifestGate.shouldApply(child.childId, child.incarnation, params)) {
430316
430996
  if (this.childManifestGate.sampleDue()) {
@@ -432104,6 +432784,62 @@ var require_topology_emitter_service = __commonJS({
432104
432784
  }
432105
432785
  });
432106
432786
 
432787
+ // ../../server/backend/dist/core/updates/wrapper-update-checker.js
432788
+ var require_wrapper_update_checker = __commonJS({
432789
+ "../../server/backend/dist/core/updates/wrapper-update-checker.js"(exports) {
432790
+ "use strict";
432791
+ Object.defineProperty(exports, "__esModule", { value: true });
432792
+ exports.WrapperUpdateChecker = exports.WRAPPER_PACKAGE_NAME = void 0;
432793
+ exports.WRAPPER_PACKAGE_NAME = "camstack-wrapper";
432794
+ var WrapperUpdateChecker = class {
432795
+ emitter;
432796
+ logger;
432797
+ readStatus;
432798
+ constructor(options) {
432799
+ this.emitter = options.emitter;
432800
+ this.logger = options.logger;
432801
+ this.readStatus = options.readStatus;
432802
+ }
432803
+ async check(nodeId, isHub) {
432804
+ const status = await this.readStatus(nodeId, isHub);
432805
+ const wrapper = status.wrapper;
432806
+ const contract = status.imageContract;
432807
+ if (wrapper === void 0 || contract === void 0) {
432808
+ throw new Error(`node ${nodeId} reports no wrapper identity (server-management payload predates it)`);
432809
+ }
432810
+ if (contract.state === "in-sync") {
432811
+ this.emitter.publishSnapshot("wrapper", [], nodeId);
432812
+ return;
432813
+ }
432814
+ const seedVersion = contract.seedVersion;
432815
+ const contractVersion = contract.contractVersion;
432816
+ const behind = contract.state === "behind-patch" || contract.state === "behind-series";
432817
+ if (!behind || seedVersion === null || contractVersion === null) {
432818
+ this.logger.warn("Wrapper check inconclusive \u2014 nothing published", {
432819
+ tags: { nodeId },
432820
+ meta: { state: contract.state, wrapperKind: wrapper.kind, seedVersion, contractVersion }
432821
+ });
432822
+ return;
432823
+ }
432824
+ const candidate = {
432825
+ target: "wrapper",
432826
+ packageName: exports.WRAPPER_PACKAGE_NAME,
432827
+ // The SHELL's versions, not the running code's. `wrapper.currentVersion`
432828
+ // is what the shell says about ITSELF (an Electron app version), which
432829
+ // is not on the same scale as a release; the seed is, and the seed is
432830
+ // what `contractVersion` was compared against.
432831
+ currentVersion: seedVersion,
432832
+ latestVersion: contractVersion,
432833
+ nodeId,
432834
+ wrapperKind: wrapper.kind
432835
+ };
432836
+ this.emitter.publishSnapshot("wrapper", [candidate], nodeId);
432837
+ }
432838
+ };
432839
+ exports.WrapperUpdateChecker = WrapperUpdateChecker;
432840
+ }
432841
+ });
432842
+
432107
432843
  // ../../server/backend/dist/manual-boot.js
432108
432844
  var require_manual_boot = __commonJS({
432109
432845
  "../../server/backend/dist/manual-boot.js"(exports) {
@@ -432183,9 +432919,11 @@ var require_manual_boot = __commonJS({
432183
432919
  var storage_service_1 = require_storage_service();
432184
432920
  var stream_probe_service_1 = require_stream_probe_service();
432185
432921
  var topology_emitter_service_1 = require_topology_emitter_service();
432922
+ var update_availability_emitter_js_1 = require_update_availability_emitter();
432186
432923
  var update_availability_store_js_1 = require_update_availability_store();
432187
432924
  var agent_installed_packages_js_1 = require_agent_installed_packages();
432188
432925
  var update_check_scheduler_js_1 = require_update_check_scheduler();
432926
+ var wrapper_update_checker_js_1 = require_wrapper_update_checker();
432189
432927
  var ServiceContainer = class {
432190
432928
  services = /* @__PURE__ */ new Map();
432191
432929
  register(ctor, instance) {
@@ -432292,6 +433030,20 @@ var require_manual_boot = __commonJS({
432292
433030
  const topologyEmitterService = new topology_emitter_service_1.TopologyEmitterService(eventBusService, agentRegistryService, addonRegistryService, (0, cap_providers_1.createNodeRootPackageLookup)(moleculerService, serverUpdateService));
432293
433031
  const postBootService = new post_boot_service_1.PostBootService(addonRegistryService, eventBusService, loggingService);
432294
433032
  const updateCheckLogger = loggingService.createLogger("UpdateCheck");
433033
+ const wrapperUpdateChecker = new wrapper_update_checker_js_1.WrapperUpdateChecker({
433034
+ logger: updateCheckLogger,
433035
+ emitter: new update_availability_emitter_js_1.UpdateAvailabilityEmitter(eventBusService, { type: "core", id: "wrapper-update-check" }, new update_availability_store_js_1.FileUpdateAvailabilityStore(availabilityDataDir, "wrapper-update", loggingService.createLogger("UpdateAvailability"))),
433036
+ readStatus: async (nodeId, isHub) => {
433037
+ if (isHub)
433038
+ return serverUpdateService.getServerPackageStatus();
433039
+ const proxy = moleculerService.createCapabilityProxy("server-management", nodeId);
433040
+ if (proxy === null) {
433041
+ throw new Error(`server-management unreachable on ${nodeId}`);
433042
+ }
433043
+ const status = await proxy["getServerPackageStatus"]?.({});
433044
+ return types_1.ServerPackageStatusSchema.parse(status);
433045
+ }
433046
+ });
432295
433047
  const updateCheckScheduler = new update_check_scheduler_js_1.UpdateCheckScheduler({
432296
433048
  logger: updateCheckLogger,
432297
433049
  getIntervalSeconds: () => addonPackageService.getAutoUpdateSettings().updateCheckIntervalSeconds,
@@ -432315,7 +433067,8 @@ var require_manual_boot = __commonJS({
432315
433067
  return void 0;
432316
433068
  }
432317
433069
  return proxy["checkServerUpdate"]?.({});
432318
- }
433070
+ },
433071
+ checkWrapperUpdate: (nodeId, isHub) => wrapperUpdateChecker.check(nodeId, isHub)
432319
433072
  }
432320
433073
  });
432321
433074
  addonPackageService.setUpdateCheckRescheduler(() => updateCheckScheduler.reschedule());
@@ -432522,6 +433275,7 @@ var require_main4 = __commonJS({
432522
433275
  var trpc_error_principal_1 = require_trpc_error_principal();
432523
433276
  var trpc_error_device_tags_1 = require_trpc_error_device_tags();
432524
433277
  var ws_request_census_1 = require_ws_request_census();
433278
+ var ws_slow_consumer_guard_js_1 = require_ws_slow_consumer_guard();
432525
433279
  var addon_route_jwt_gate_js_1 = require_addon_route_jwt_gate();
432526
433280
  var addon_route_share_gate_js_1 = require_addon_route_share_gate();
432527
433281
  var session_cookie_js_1 = require_session_cookie();
@@ -432646,7 +433400,17 @@ var require_main4 = __commonJS({
432646
433400
  // per peer and per frame kind. Same lazy registry, same reason.
432647
433401
  socketPlane: (0, system_1.createSocketPlaneReader)({
432648
433402
  registry: () => moleculerForEventPlane?.childRegistry ?? null
432649
- })
433403
+ }),
433404
+ // The ArrayBuffer census, armed by the probe itself. Twice on 2026-09-10
433405
+ // this process's `arrayBuffers` climbed linearly for an hour (to 1.3 GB
433406
+ // and to 5 GB, both LIVE across a forced compaction) and released in one
433407
+ // instant, with no line at either boundary; the measurement that names
433408
+ // the shape existed and was never run because it needed a human awake
433409
+ // during the episode. Now the heartbeat takes it: one forced full GC —
433410
+ // the pause a reclaim pass already costs — walked in memory, nothing
433411
+ // written, on a threshold held for a minute, at most twice an hour.
433412
+ // It is NOT a heap snapshot; see `array-buffer-census.ts`.
433413
+ arrayBufferCensus: { census: (0, system_1.createInspectorArrayBufferCensus)() }
432650
433414
  });
432651
433415
  cleanupOrphanProcesses();
432652
433416
  let spaIndexHtml = null;
@@ -433246,15 +434010,31 @@ var require_main4 = __commonJS({
433246
434010
  concurrencyLimit: 10
433247
434011
  }
433248
434012
  });
434013
+ const wsSlowConsumerGuard = new ws_slow_consumer_guard_js_1.WsSlowConsumerGuard({
434014
+ logger: app.get(logging_service_1.LoggingService).createLogger("tRPC:ws")
434015
+ });
434016
+ wsSlowConsumerGuard.start();
433249
434017
  wss.on("connection", (client, req) => {
433250
434018
  (0, ws_request_census_1.attachWsCensusSession)(httpRequestCensus, client, req, app.get(logging_service_1.LoggingService).createLogger("tRPC:ws"));
434019
+ wsSlowConsumerGuard.attach(client, (0, ws_slow_consumer_guard_js_1.wsConnectionFacts)(req));
433251
434020
  });
433252
434021
  (0, ws_1.applyWSSHandler)({
433253
434022
  wss,
433254
434023
  router: appRouter,
434024
+ // The complement of the bound above, for the SUSPENDED client: a PING is
434025
+ // a text frame the client's JS must answer, and a backgrounded app's JS
434026
+ // does not run — so the socket is terminated ~40 s into the background
434027
+ // instead of accumulating for the night, and the app reconnects on
434028
+ // return (every WS client of this hub is `@trpc/client` >= 11, which
434029
+ // answers PING with PONG; the viewer pins ^11.16). The bound stays: it
434030
+ // covers a client whose JS runs but whose link cannot drain the events.
434031
+ // A termination here closes with 1006; the guard's close line names it.
434032
+ keepAlive: { enabled: true, pingMs: 3e4, pongWaitMs: 1e4 },
433255
434033
  createContext: async (opts) => {
433256
434034
  const wsCtx = await (0, trpc_context_1.createWsTrpcContext)(opts, authService, addonRegistry, shareTokenService, app.get(logging_service_1.LoggingService).createLogger("tRPC:ws"));
433257
- (0, ws_request_census_1.identifyWsCensusSession)(opts.res, (0, trpc_error_principal_1.describeTrpcPrincipal)(wsCtx.user));
434035
+ const principal = (0, trpc_error_principal_1.describeTrpcPrincipal)(wsCtx.user);
434036
+ (0, ws_request_census_1.identifyWsCensusSession)(opts.res, principal);
434037
+ wsSlowConsumerGuard.identify(opts.res, principal);
433258
434038
  return wsCtx;
433259
434039
  },
433260
434040
  onError: ({ path: trpcPath, error, ctx, input }) => {